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

发布时间:2024-09-18  

1、misc设备驱动模型


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



struct miscdevice  {

int minor;    //次设备号,若为 MISC_DYNAMIC_MINOR 自动分配

const char *name; //设备名

const struct file_operations *fops; //设备文件操作结构体

struct list_head list; //misc_list链表头

struct device *parent;

struct device *this_device;

const char *nodename;

mode_t mode;

};

      结构体中的部分成员我们是一目了然的,主要是来看看有疑惑的几点:

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


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


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


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


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


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


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


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


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


      可如下定义:



static struct miscdevice misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &dev_fops,

};

       其中的设备文件操作结构体和字符设备类似,这里就不再细讲。

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


     使用如下两个函数:



int misc_register(struct miscdevice * misc); //在加载模块时会自动创建设备文件,是主设备号为10的字符设备

int misc_deregister(struct miscdevice *misc); //在卸载模块时会自动删除设备文件

      好了,至此,整个设备驱动的流程就完了,接下来深入了解一下misc设备模型的工作原理。

       首先看看misc初始化函数:



static int __init misc_init(void)

{

int err;


#ifdef CONFIG_PROC_FS

/*如果使用proc文件系统,则创建misc项*/

proc_create('misc', 0, NULL, &misc_proc_fops);

#endif

/*在/sys/class/目录下创建一个名为misc的类*/

misc_class = class_create(THIS_MODULE, 'misc');

err = PTR_ERR(misc_class);

if (IS_ERR(misc_class))

goto fail_remove;


err = -EIO;

/*咦,怎么misc设备驱动调用字符驱动的注册函数呢?设备的主设备号为MISC_MAJOR,为10*/

if (register_chrdev(MISC_MAJOR,'misc',&misc_fops))

goto fail_printk;

misc_class->devnode = misc_devnode;

return 0;


fail_printk:

printk('unable to get major %d for misc devicesn', MISC_MAJOR);

class_destroy(misc_class);

fail_remove:

remove_proc_entry('misc', NULL);

return err;

}

/*向内核注册misc子系统*/  

subsys_initcall(misc_init); 

        接下来看看misc设备驱动的注册与注销函数:

注册函数:



int misc_register(struct miscdevice * misc)

{

struct miscdevice *c;

dev_t dev;

int err = 0;


/*内核初始化一个链表头*/

INIT_LIST_HEAD(&misc->list);


mutex_lock(&misc_mtx);

/*遍历已经注册的misc,如果和当前准备注册的相同(依据次设备号来判断),就返回设备忙*/

list_for_each_entry(c, &misc_list, list) {

if (c->minor == misc->minor) {

mutex_unlock(&misc_mtx);

return -EBUSY;

}

}


/*动态分配设备的次设备号*/

if (misc->minor == MISC_DYNAMIC_MINOR) {

int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);

if (i >= DYNAMIC_MINORS) {

mutex_unlock(&misc_mtx);

return -EBUSY;

}

misc->minor = DYNAMIC_MINORS - i - 1;

set_bit(i, misc_minors);

}


/*使用固定的主设备号,动态分配的次设备号构造设备号*/

dev = MKDEV(MISC_MAJOR, misc->minor);


/*创建设备文件,这里就是使用miscdevice结构体中两个device类型指针的地方,

  当然,这是和linux设备驱动模型相关的*/

misc->this_device = device_create(misc_class, misc->parent, dev,

  misc, '%s', misc->name);

if (IS_ERR(misc->this_device)) {

int i = DYNAMIC_MINORS - misc->minor - 1;

if (i < DYNAMIC_MINORS && i >= 0)

clear_bit(i, misc_minors);

err = PTR_ERR(misc->this_device);

goto out;

}


/*

* Add it to the front, so that later devices can 'override'

* earlier defaults

*/

/*到这一步也就注册成功了,将新注册的misc设备加入到内核维护的misc_list链表中*/

list_add(&misc->list, &misc_list);

 out:

mutex_unlock(&misc_mtx);

return err;

}

注销函数:


int misc_deregister(struct miscdevice *misc)

{

int i = DYNAMIC_MINORS - misc->minor - 1;


if (WARN_ON(list_empty(&misc->list)))

return -EINVAL;


mutex_lock(&misc_mtx);

/*删除链表节点*/

list_del(&misc->list);

/*销毁设备文件*/

device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));

if (i < DYNAMIC_MINORS && i >= 0)

clear_bit(i, misc_minors);

mutex_unlock(&misc_mtx);

return 0;

}

       到这里,差不多misc设备驱动模型就差不多了。

2、misc设备驱动实例


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



#include

#include

#include

//#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include


#include

#include

#include


#include

#include

#include


#define DEVICE_NAME 'leds'


static long sbc2440_leds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

switch(cmd) {

unsigned tmp;

case 0:

case 1:

if (arg > 4) {

return -EINVAL;

}

tmp = readl(S3C64XX_GPKDAT);

tmp &= ~(1 << (4 + arg));

tmp |= ( (!cmd) << (4 + arg) );

writel(tmp, S3C64XX_GPKDAT);

return 0;

default:

return -EINVAL;

}

}


static struct file_operations dev_fops = {

.owner = THIS_MODULE,

.unlocked_ioctl = sbc2440_leds_ioctl,

};


static struct miscdevice misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &dev_fops,

};


static int __init dev_init(void)

{

int ret;


{

unsigned tmp;

tmp = readl(S3C64XX_GPKCON);

tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16);

writel(tmp, S3C64XX_GPKCON);

tmp = readl(S3C64XX_GPKDAT);

tmp |= (0xF << 4);

writel(tmp, S3C64XX_GPKDAT);

}


ret = misc_register(&misc);


printk (DEVICE_NAME'tinitializedn');


return ret;

}


static void __exit dev_exit(void)

{

misc_deregister(&misc);

}


module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE('GPL');

MODULE_AUTHOR('FriendlyARM Inc.');


      好了,今天就到此结束,呵呵!


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>