misc设备驱动模型及实例解析

1、misc设备驱动模型

      本节我们来看一下misc设备驱动模型的有关内容,首先是看看它的设备结构体,定义在include/linux/miscdevice.h中:

  1. struct miscdevice  {  
  2.     int minor;                  //次设备号,若为 MISC_DYNAMIC_MINOR 自动分配  
  3.     const char *name;           //设备名  
  4.     const struct file_operations *fops; //设备文件操作结构体  
  5.     struct list_head list;          //misc_list链表头  
  6.     struct device *parent;  
  7.     struct device *this_device;  
  8.     const char *nodename;  
  9.     mode_t mode;  
  10. };  
      结构体中的部分成员我们是一目了然的,主要是来看看有疑惑的几点:

1、为什么只有次设备号呢?一个设备不是有主、次设备号吗?

      其实,我想大家应该能够想到了,此时没有明确指定,那就说明应该是使用默认值。

2、主设备号的默认值是多少呢?难道所有注册为misc的设备都有相同的主设备号?怎么区分各个设备呢?

      这个主设备号是10.的确,所有注册为misc的设备都有相同的主设备号:10.在使用过程中我们主要是通过次设备号来区分各个设备。这一点不难理解,内核将所有注册为misc的设备都归为一大类。

3、结构体中的list_head结构体类型的list成员的作用是什么呢?

      内核自己会维护一个misc_list链表,所有注册为misc的设备都必须挂在这个链表上,这个list就是该链表的链表头。

4、结构体中的两个device结构体类型指针作用是什么呢?

      作用就是创建设备文件,稍候就可以看到了!

5、我们如何定义自己的misc类型的设备呢?

      可如下定义:

  1. static struct miscdevice misc = {  
  2.     .minor = MISC_DYNAMIC_MINOR,  
  3.     .name = DEVICE_NAME,  
  4.     .fops = &dev_fops,  
  5. };  
       其中的设备文件操作结构体和字符设备类似,这里就不再细讲。

6、定义了自己的misc设备,那么我们如何向内核注册/注销设备呢?

     使用如下两个函数:

  1. int misc_register(struct miscdevice * misc);    //在加载模块时会自动创建设备文件,是主设备号为10的字符设备  
  2. int misc_deregister(struct miscdevice *misc);   //在卸载模块时会自动删除设备文件  
      好了,至此,整个设备驱动的流程就完了,接下来深入了解一下misc设备模型的工作原理。

       首先看看misc初始化函数:

  1. static int __init misc_init(void)  
  2. {  
  3.     int err;  
  4.   
  5. #ifdef CONFIG_PROC_FS  
  6.     /*如果使用proc文件系统,则创建misc项*/  
  7.     proc_create("misc", 0, NULL, &misc_proc_fops);  
  8. #endif  
  9.     /*在/sys/class/目录下创建一个名为misc的类*/  
  10.     misc_class = class_create(THIS_MODULE, "misc");  
  11.     err = PTR_ERR(misc_class);  
  12.     if (IS_ERR(misc_class))  
  13.         goto fail_remove;  
  14.   
  15.     err = -EIO;  
  16.     /*咦,怎么misc设备驱动调用字符驱动的注册函数呢?设备的主设备号为MISC_MAJOR,为10*/  
  17.     if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))  
  18.         goto fail_printk;  
  19.     misc_class->devnode = misc_devnode;  
  20.     return 0;  
  21.   
  22. fail_printk:  
  23.     printk("unable to get major %d for misc devices\n", MISC_MAJOR);  
  24.     class_destroy(misc_class);  
  25. fail_remove:  
  26.     remove_proc_entry("misc", NULL);  
  27.     return err;  
  28. }  
  29. /*向内核注册misc子系统*/    
  30. subsys_initcall(misc_init);   
        接下来看看misc设备驱动的注册与注销函数:

注册函数:

  1. int misc_register(struct miscdevice * misc)  
  2. {  
  3.     struct miscdevice *c;  
  4.     dev_t dev;  
  5.     int err = 0;  
  6.   
  7.     /*内核初始化一个链表头*/  
  8.     INIT_LIST_HEAD(&misc->list);  
  9.   
  10.     mutex_lock(&misc_mtx);  
  11.     /*遍历已经注册的misc,如果和当前准备注册的相同(依据次设备号来判断),就返回设备忙*/  
  12.     list_for_each_entry(c, &misc_list, list) {  
  13.         if (c->minor == misc->minor) {  
  14.             mutex_unlock(&misc_mtx);  
  15.             return -EBUSY;  
  16.         }  
  17.     }  
  18.   
  19.     /*动态分配设备的次设备号*/  
  20.     if (misc->minor == MISC_DYNAMIC_MINOR) {  
  21.         int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);  
  22.         if (i >= DYNAMIC_MINORS) {  
  23.             mutex_unlock(&misc_mtx);  
  24.             return -EBUSY;  
  25.         }  
  26.         misc->minor = DYNAMIC_MINORS - i - 1;  
  27.         set_bit(i, misc_minors);  
  28.     }  
  29.   
  30.     /*使用固定的主设备号,动态分配的次设备号构造设备号*/  
  31.     dev = MKDEV(MISC_MAJOR, misc->minor);  
  32.   
  33.     /*创建设备文件,这里就是使用miscdevice结构体中两个device类型指针的地方, 
  34.       当然,这是和linux设备驱动模型相关的*/  
  35.     misc->this_device = device_create(misc_class, misc->parent, dev,  
  36.                       misc, "%s", misc->name);  
  37.     if (IS_ERR(misc->this_device)) {  
  38.         int i = DYNAMIC_MINORS - misc->minor - 1;  
  39.         if (i < DYNAMIC_MINORS && i >= 0)  
  40.             clear_bit(i, misc_minors);  
  41.         err = PTR_ERR(misc->this_device);  
  42.         goto out;  
  43.     }  
  44.   
  45.     /* 
  46.      * Add it to the front, so that later devices can "override" 
  47.      * earlier defaults 
  48.      */  
  49.      /*到这一步也就注册成功了,将新注册的misc设备加入到内核维护的misc_list链表中*/  
  50.     list_add(&misc->list, &misc_list);  
  51.  out:  
  52.     mutex_unlock(&misc_mtx);  
  53.     return err;  
  54. }  
注销函数:
  1. int misc_deregister(struct miscdevice *misc)  
  2. {  
  3.     int i = DYNAMIC_MINORS - misc->minor - 1;  
  4.   
  5.     if (WARN_ON(list_empty(&misc->list)))  
  6.         return -EINVAL;  
  7.   
  8.     mutex_lock(&misc_mtx);  
  9.     /*删除链表节点*/  
  10.     list_del(&misc->list);  
  11.     /*销毁设备文件*/  
  12.     device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));  
  13.     if (i < DYNAMIC_MINORS && i >= 0)  
  14.         clear_bit(i, misc_minors);  
  15.     mutex_unlock(&misc_mtx);  
  16.     return 0;  
  17. }  
       到这里,差不多misc设备驱动模型就差不多了。

2、misc设备驱动实例

      这里贴一个简单的misc设备驱动程序,方便大家对照上面的理论部分进行分析,此驱动程序是友善TQ6410开发板的LED驱动程序,可以看看:

#include <linux/miscdevice.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-bank-m.h>



#define DEVICE_NAME "tq6410-leds"


/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_GPIO_ON	1
#define IOCTL_GPIO_OFF	0

/* 用来指定LED所用的GPIO引脚 */
static unsigned long gpio_table [] =
{
	S3C64XX_GPM(0),
	S3C64XX_GPM(1),
};

/* 用来指定GPIO引脚的功能:输出 */
static unsigned int gpio_cfg_table [] =
{
	S3C64XX_GPM_OUTPUT(0),
	S3C64XX_GPM_OUTPUT(1),
};


#ifdef CONFIG_TQ6410_DEBUG_LEDS
static void tq6410_debug_leds(unsigned int cmd,unsigned long arg)
{
	s3c_gpio_setpin(gpio_table[arg], cmd);
}
static void toggle_led(unsigned int cmd,unsigned long arg)
{
	int loop=0;
	printk("%s : led %ld toggle now: \n",__func__,arg);
	for(; loop<11; loop++)
	{	cmd = loop%2;
		printk("leds %d %s \n",arg+1,(cmd)?"on":"off");
		tq6410_debug_leds(cmd,arg);
		mdelay(1000);
	}

}
#endif
static int tq6410_gpio_open(struct inode *inode, struct file *file)
{
	int i;
	printk(KERN_INFO " leds opened\n");
	for (i = 0; i < sizeof(gpio_table)/sizeof(unsigned long); i++)
	{
		s3c_gpio_cfgpin(gpio_table[i], gpio_cfg_table[i]);
		s3c_gpio_setpin(gpio_table[i], 0);
	}
#ifdef CONFIG_TQ6410_DEBUG_LEDS
	for (i = 0; i < sizeof(gpio_table)/sizeof(unsigned long); i++)
	{
		toggle_led(1,i);
	}
#endif
	return 0;

}

static long tq6410_gpio_ioctl(
	struct file *file, 
	unsigned int cmd, 
	unsigned long arg)
{
	arg -= 1;
	if (arg > sizeof(gpio_table)/sizeof(unsigned long))
	{
		return -EINVAL;
	}

	switch(cmd)
	{
	case IOCTL_GPIO_ON:
		// 设置指定引脚的输出电平为1
		s3c_gpio_setpin(gpio_table[arg], 1);
		return 0;

	case IOCTL_GPIO_OFF:
		// 设置指定引脚的输出电平为0
		s3c_gpio_setpin(gpio_table[arg], 0);
		return 0;

	default:
		return -EINVAL;
	}
}
/*驱动接口设置*/
static struct file_operations dev_fops = 
{
	.owner		= THIS_MODULE,
	.unlocked_ioctl	= tq6410_gpio_ioctl,
	.open		= tq6410_gpio_open,
};
/*设备结构的设置*/
static struct miscdevice misc = 
{
	.minor		= MISC_DYNAMIC_MINOR,
	.name		= DEVICE_NAME,
	.fops		= &dev_fops,
};
/*初始化设备,配置对应的IO,以及注册设备*/
static int __init tq6410_leds_init(void)
{
	int ret;

	int i;
	
	for (i = 0; i < sizeof(gpio_table)/sizeof(unsigned long); i++)
	{
		s3c_gpio_cfgpin(gpio_table[i], gpio_cfg_table[i]);
		s3c_gpio_setpin(gpio_table[i], 0);
		s3c_gpio_setpull(gpio_table[i], S3C_GPIO_PULL_NONE);
	}

	ret = misc_register(&misc);

	printk(KERN_INFO "TQ6410 LEDs driver successfully probed\n");

	return ret;
}
/*注销设备*/
static void __exit tq6410_leds_exit(void)
{
	misc_deregister(&misc);
}

module_init(tq6410_leds_init);
module_exit(tq6410_leds_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.embedsky.net");
MODULE_DESCRIPTION("TQ6410 LEDS Driver");


3编译和加载和查看

make

[root@EmbedSky xuyuefei]# insmod xyf_leds.ko

[root@EmbedSky /]# cat /proc/devices   
Character devices:
.....

 10 misc

[root@EmbedSky xuyuefei]# ls /sys/class/misc/       看到xyf_leds......




以上转自:http://blog.csdn.net/chenlong12580/article/details/7339127

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值