相关概念
-
misc 的意思是混合、杂项的,因此 MISC 驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动。 MISC 驱动其实就是最简单的字符设备驱动。
-
misc驱动通常嵌套在 platform 总线驱动中,实现复杂的驱动
-
所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号, MISC 设备驱动就用于解决此问题。 MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设备驱动可以简化字符设备驱动的编写。
-
misc驱动编写的核心就是初始化 miscdevice 这个结构体,定义在文件 include/linux/miscdevice.h 中。向 Linux 注册一个 miscdevice 设备。
struct miscdevice
{
int minor; /* 子设备号 需要用户填写*/
const char *name; /* 设备名字 需要用户填写*/
const struct file_operations *fops; /* 设备操作集 需要用户填写*/
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
定义一个 MISC 设备(miscdevice 类型)以后我们需要设置 minor、 name 和 fops 这三个成员变量。 minor 表示子设备号, MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号, Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在
include/linux/miscdevice.h 文件中,如下所示:
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
......
#define MISC_DYNAMIC_MINOR 255
我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。
若指定为255表示由内核自动分配次设备号。
name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件。
fops 就是字符设备的操作集合, MISC 设备驱动最终是需要使用用户提供的 fops 操作集合。
- 当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下:
int misc_register(struct miscdevice * misc);
函数参数和返回值含义如下:
misc:要注册的 MISC 设备。
返回值: 负数,失败; 0,成功。
以前我们需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中我们会使用
如下几个函数完成设备创建过程:
alloc_chrdev_region(); /* 申请设备号 */
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */
现在我们可以直接使用 misc_register 一个函数来完成这些步骤。
当我们卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:
cdev_del(); /* 删除 cdev */
unregister_chrdev_region(); /* 注销设备号 */
device_destroy(); /* 删除设备 */
class_destroy(); /* 删除类 */
现在我们只需要一个 misc_deregister 函数即可完成完成这些步骤。
MISC驱动代码
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/string.h>
#include<linux/jiffies.h>
#include<linux/irq.h>
#include<asm/mach/map.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/interrupt.h>
#include<linux/fcntl.h>
#include<linux/poll.h>
#include<linux/ide.h>
#include<linux/platform_device.h>
#include<linux/miscdevice.h>
#define MISCBEEP_NAME "miscbeep"
#define MISCBEEP_MINOR 144
#define BEEPON 1
#define BEEPOFF 0
static int miscbeep_probe(struct platform_device *dev);
static int miscbeep_remove(struct platform_device *dev);
static int beep_open(struct inode *inode, struct file *filp);
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt);
static int beep_release(struct inode *inode, struct file *filp);
// 自定义设备结构体
struct miscbeep_dev
{
int beep_gpio;
struct device_node *nd;
};
struct miscbeep_dev miscbeep;
struct file_operations miscbeep_fops =
{
.owner = THIS_MODULE,
.open = beep_open,
.write = beep_write,
.release = beep_release,
};
// misc 设备结构体,提供 minor,name,fops 即可
static struct miscdevice beep_misc =
{
.minor = MISCBEEP_MINOR,
// /dev目录下就会生成同名文件,表示当前设备
.name = MISCBEEP_NAME,
.fops = &miscbeep_fops,
};
static struct of_device_id beep_of_match[] =
{
{
.compatible = "gpio-beeper"
// 要和设备树里对应节点的
// compatible 一致
},
{
//sentinel
}
};
static struct platform_driver miscbeep_drv =
{
.driver =
{
// 有设备树的情况下就不会用name值进行匹配
.name = "imx6ul-beeper",
.of_match_table = beep_of_match,
},
.probe = miscbeep_probe,
.remove = miscbeep_remove,
};
static int beep_open(struct inode *inode, struct file *filp)
{
filp->private_data = &miscbeep; /* 设置私有数据 */
return 0;
}
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char beepstat;
struct miscbeep_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
beepstat = databuf[0]; /* 获取状态值 */
if(beepstat == BEEPON) {
gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */
} else if(beepstat == BEEPOFF) {
gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */
}
return 0;
}
static int beep_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int miscbeep_probe(struct platform_device *dev)
{
int ret;
//1.初始化beeper相关的gpio
miscbeep.nd = dev->dev.of_node;
// 获取指定gpio的gpio编号,注意第二个参数一定要和
// 设备树中的属性名一致
miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpios", 0);
if(miscbeep.beep_gpio < 0)
{
ret = -1;
printk("%s(%d):fail\n", __FILE__, __LINE__);
goto fail_findgpio;
}
ret = gpio_request(miscbeep.beep_gpio, "give_it_a_name");
if(ret < 0)
{
printk("%s(%d):fail\n", __FILE__, __LINE__);
goto failgpioreq;
}
ret = gpio_direction_output(miscbeep.beep_gpio, 1);
if(ret < 0)
{
printk("%s(%d):fail\n", __FILE__, __LINE__);
goto fail_setdir;
}
//2 misc 驱动注册
ret = misc_register(&beep_misc);
if(ret < 0)
{
printk("%s(%d):fail\n", __FILE__, __LINE__);
goto fail_setdir;
}
return 0;
fail_setdir:
gpio_free(miscbeep.beep_gpio);
failgpioreq:
fail_findgpio:
return ret;
}
static int miscbeep_remove(struct platform_device *dev)
{
gpio_set_value(miscbeep.beep_gpio, 1);
gpio_free(miscbeep.beep_gpio);
misc_deregister(&beep_misc);
return 0;
}
// driver 入口函数
static int __init miscbeep_init(void)
{
return platform_driver_register(&miscbeep_drv);
}
static void __exit miscbeep_exit(void)
{
platform_driver_unregister(&miscbeep_drv);
}
module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");
设备树对应节点代码
gpiobeep {
compatible = "gpio-beeper" ;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpiobeep>;
beep-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
status = "okay";
};
测试APP代码
其实和前面led的是一样的
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
// ./ledAPP filename 0/1
// 0 -> turn off led
// 1 -> turn on led
#define LED_OFF 0
#define LED_ON 1
int main(int argc, char **argv)
{
int ret = 0;
int fd = 0;
char *filename;
unsigned char databuf[1];
if(argc != 3)
{
printf("Error usage!\r\n");// user room
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0)
{
printf("Can't open file \"%s\"\r\n", filename);
return -1;
}
databuf[0] = atoi(argv[2]);
ret = write(fd, databuf, sizeof(databuf));
if(ret < 0)
{
printf("Led control failed.\r\n");
ret = close(fd);
if(ret < 0)
{
printf("Close file \"%s\" error!\r\n", filename);
return -1;
}
return -1;
}
ret = close(fd);
if(ret < 0)
{
printf("Close file \"%s\" error!\r\n", filename);
return -1;
}
else
{
;
}
return 0;
}
315

被折叠的 条评论
为什么被折叠?



