misc注册方式实现platform驱动
解析设备树信息
设备树编辑和解析方式在我另外一篇基于gpiod API的platform总线多个led驱动开发
misc设备简介
所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。
随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号, MISC 设备驱动就用于解决此问题。 MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用
MISC 设备驱动可以简化字符设备驱动的编写。
- 注册结构体的定义在文件 include/linux/miscdevice.h
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 APOLLO_MOUSE_MINOR 7 /* unused */
#define PC110PAD_MINOR 9 /* unused */
/*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */
#define WATCHDOG_MINOR 130 /* Watchdog timer */
#define TEMP_MINOR 131 /* Temperature Sensor */
#define APM_MINOR_DEV 134
#define RTC_MINOR 135
/*#define EFI_RTC_MINOR 136 was EFI Time services */
#define VHCI_MINOR 137
#define SUN_OPENPROM_MINOR 139
#define DMAPI_MINOR 140 /* unused */
#define NVRAM_MINOR 144
#define SBUS_FLASH_MINOR 152
.....
注册卸载函数
//注册函数
extern int misc_register(struct miscdevice *misc);
//卸载函数
extern void misc_deregister(struct miscdevice *misc);
注册函数可以替代之前所做的事情,如下
alloc_chrdev_region(); /* 申请设备号 */
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */
卸载函数同理
注册函数和misc_open函数详解
查看注册函数的解释
意思是在系统调用open时候,file->pricate_data会指向该结构体,即struct miscdevice结构体。
该操作的所在位置。
- 查看misc_open函数的操作,
简单讲解这个操作的含义,最终结果 文件指针,file指针的私有数据区存放,自己定义misc结构体指针。 - 重点来了,在misc结构体中除了前三个变量需要自己定义,后面几个也需要关注,其中的
struct device *this_device;
该结构体指向misc设备的设备指针,当然它也有私有数据区,就可以利用该区进行私有数据保存,因为misc设备时全局变量。
数据流向:
- 在probe中初始化相关IO状态后,保存私有数据地址
- misc注册成功后,给misc设备的this_device成员保存该私有数据地址priv_data
- 在自定义 open 或者 在其他接口函数中 取出file-private_data ,数据类型为miscdevice
- 取出该设备中的私有数据 priv_data = drv_get_drvdata(miscdevice->this_device),就可以使用该私有变量中的数据
基于misc设备注册的代码
probe部分
//1.初始化io状态
rv = paser_dt_init_led(pdev);
if(rv < 0)
{
return rv;
}
priv = platform_get_drvdata(pdev); //获取私有数据
//2.注册misc设备
rv = misc_register(&led0_miscdev);
if(rv < 0)
{
dev_err(&pdev->dev,"misc led0 device register failure.\n");
return -EFAULT;
}
dev_set_drvdata(led0_miscdev.this_device, priv); //将私有数据保存至misc设备的私有数据区
//第二个设备注册
rv = misc_register(&led1_miscdev);
if(rv < 0)
{
dev_err(&pdev->dev,"misc led1 device register failure.\n");
return -EFAULT;
}
dev_set_drvdata(led1_miscdev.this_device, priv);
printk("misc gpio leds driver probe okay.\n");
remove部分
for(i=0; i<priv->num_leds; i++)
{
gpiod_set_value(priv->leds[i].led_gpiod, OFF);
devm_gpiod_put(&pdev->dev, priv->leds[i].led_gpiod); //释放gpiod
}
//注销misc设备
misc_deregister(&led0_miscdev);
misc_deregister(&led1_miscdev);
printk("misc gpio leds driver remove.\n");
fops接口
ioctl接口
miscdev = filp->private_data; //open misc设备时候,miscdevice放到该私有数据处
priv = dev_get_drvdata(miscdev->this_device);
switch (cmd) {
case LED_OFF:/* variable case */
if(priv->num_leds <= arg)
{
printk("led%ld doesn't exist\n", arg);
return -ENOTTY;
}
gpiod_set_value(priv->leds[arg].led_gpiod, OFF);
break;
case LED_ON:
if(priv->num_leds <= arg)
{
printk("led%ld doesn't exist\n", arg);
return -ENOTTY;
}
gpiod_set_value(priv->leds[arg].led_gpiod, ON);
break;
default:
printk("driver don't support ioctl command=%d\n", cmd);
print_led_help();
}
其他函数操作看我的另一篇博客 基于gpiod API的platform总线多个led驱动开发