我们在上一篇文章中聊的字符设备驱动的写法,参见linux驱动三种写法之——软硬件信息未分离(以字符设备驱动程序为例)
混杂设备的说明
如标题,内核提供了一种简化字符设备驱动的写法,是将主设备号固定为10,然后可以设置内核自动分配次设备号来区分设备个体,本质上还是字符设备,就是将上述链接中的字符设备驱动框架中的【设备号处理工作】和【字符设备对象的处理工作】打包在【混杂设备对象的处理工作】中了。
字符设备驱动框架 与 混杂设备驱动框架 对比图
看一下【字符设备驱动框架】和【混杂设备驱动框架】的差异就明白了。接下来我们来看看具体混杂设备对象是如何实现的。
描述混杂设备对象结构体 struct miscdevice
struct miscdevice {
int minor; //混杂设备对应的次设备号,注意哦:主设备号为 10
const char *name; // 将来的设备文件名,并且设备文件是自动创建
const struct file_operations *fops; //提供的硬件操作接口对象
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
混杂设备对象的配套函数misc_register() 与 misc_deregister()
- 混杂设备注册函数misc_register(),其定义如下
int misc_register(struct miscdevice *misc);
函数说明:向内核注册一个混杂设备对象, 内核会帮你自动创建一个名称为 name 的设备文件并且帮你分配一个次设备号
- 混杂设备卸载函数misc_deregister(),其定义如下
int misc_deregister(struct miscdevice *misc);
函数说明:从内核卸载一个混杂设备对象, 内核会帮你删除创建的设备文件内核会帮你释放申请的次设备号
将上述链接中的linux字符设备驱动实例改写成linux混杂设备驱动程序,如下
/*编写混杂设备驱动,利用ioctl随意开关上述四个LED等*/
//头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
//定义描述led灯硬件的数据结构
struct led_resource{
int gpio;
char *name;
};
//定义初始化4个led灯的硬件信息
struct led_resource led_info[] = {
{PAD_GPIO_C+12, "LED1"},
{PAD_GPIO_C+7, "LED2"},
{PAD_GPIO_C+11, "LED3"},
{PAD_GPIO_B+26, "LED4"}
};
#define LED_ON 0X100001
#define LED_OFF 0X100002
//调用关系:应用程序ioctl -> 软中断 -> 内核sys_ioctl ->驱动led_ioctl
//例子:int index=1;ioctl(fd, LED_ON, &index);//开第1个灯
//参数关系:
// fd<---->file 亲戚关系
// cmd=LED_ON或者cmd=LED_OFF
// arg=(unsigned long)&index
long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
//分配内核缓冲区
int kindex;
copy_from_user(&kindex, (int *)arg, sizeof(kindex));
switch(cmd){
case LED_ON:
gpio_set_value(led_info[kindex-1].gpio, 0);
printk("%s:开第%d个灯\n",__func__, kindex);
break;
case LED_OFF:
gpio_set_value(led_info[kindex - 1].gpio, 1);
printk("%s:关第%d个灯\n",__func__, kindex);
break;
default:
printk("无效命令!\n");
return -1;
}
return 0;
}
//定义led灯的硬件操作接口
struct file_operations led_fops = {
.unlocked_ioctl = led_ioctl,
.owner = THIS_MODULE
};
//定义初始化led的混杂设备对象
struct miscdevice led_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &led_fops,
.name = "vaccine_misc"
};
//入口函数
int led_drv_init(void){
//申请gpio资源
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 1);
}
//向内核注册一个混杂设备对象,内核会自动创建一个名称为name的设备文件并且会自动分配一个次设备号
misc_register(&led_misc);
return 0;
}
//出口函数
void led_drv_exit(void){
//释放gpio资源
int i;
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_set_value(led_info[i].gpio, 1);
gpio_free(led_info[i].gpio);
}
//从内核卸载一个混杂设备对象,内核会删除创建的设备文件
//且内核会释放掉申请的次设备号
misc_deregister(&led_misc);
}
//各种修饰和GPL规则
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
应用程序不变