浅谈linux驱动2字符设备驱动程序.docx
- 文档编号:8885860
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:9
- 大小:310.76KB
浅谈linux驱动2字符设备驱动程序.docx
《浅谈linux驱动2字符设备驱动程序.docx》由会员分享,可在线阅读,更多相关《浅谈linux驱动2字符设备驱动程序.docx(9页珍藏版)》请在冰豆网上搜索。
浅谈linux驱动2字符设备驱动程序
浅谈linux驱动
(2)-字符设备驱动程序2012年02月19日12:
44:
04
大概分以下五个知识点:
1.设备号
2.创建设备文件
3.设备注册
4.重要数据结构
5.设备操作
首先我们谈谈设备号,其实设备号分主设备号和次设备号,主设备号用来区别不同的设备,次设备号用来区别同一类设备不同型号设备(打个比方,cs8900和dm9000都是网卡设备,那么他们的主设备号就是一样的,而次设备号的存在就是为了区分哪个是cs8900哪个是dm9000)。
那怎么查看设备号呢,用ls-l命令,现在我们看图说话
接下来我们输入ls-l命令
这里就可以一目了然了,前面的是主设备号,后面的就是次设备号。
那么有朋友问了,设备号用来干嘛的?
这里我画个图
我们这里的字符设备文件和字符设备驱动都对应的一个号,当这个号相同的时候,字符设备文件就和字符设备驱动关联上了。
这里用的是一个设备号来建立这个联系。
接下来我们看看在内核当中是如何表述设备号的,其实在linux内核当中,这个主次设备号是有规律的,主次设备号加在一起,实质就是unsignedint32位整数,它用dev_t来描述。
其中高12位是主设备号,低20位是次设备号。
一般情况下,我们不会说出现上千个设备的,所有主设备号是够用的,2的12次方是多少?
4096,也就是说最多支持4096种类型的设备。
我们是怎样从dev_t分离出主设备号和次设备号的呢?
用MAJOR(dev_tdev)这个宏分离出主设备号,用MINOR(dev_tdev)这个宏分离出次设备号。
linux内核如何给设备分配设备号呢?
分两种:
静态申请,动态分配。
如何静态申请呢?
首先我们要知道设备号存放在哪里?
我们来看下图
然后打开这个文挡
上面描述可能还有点问题,总之你选用的设备号,是前面没有出现过的才行。
然后使用register_chrdev_region这个函数去注册去申请设备号。
intregister_chrdev_region(dev_from,unsignedcount,constchar*name)
from代表你希望使用的设备号是多少?
比如我选个500,总之前面没出现过就行。
count表示希望申请使用设备号的数目,比如我们申请了5个,那么系统就会给你留5个号出来,打个比方,5000,5001,5002,5003,5004.
name是设备名,比如serial。
接下来我们谈谈动态分配,我们常常会担心自己选的设备号前面已经用过了,担心设备号选错引起不必要的麻烦,那么我们现在就采取动态分配设备号的方式,让内核来分配设备号,内核一定比我们清楚哪些设备号是空闲的.我们用alloc_chrdev_region这个函数来分配设备号。
这里有个需要注意的,这里动态分配设备号,一定是要在驱动安装之后才能执行的。
动态分配intalloc_chrdev_region(dev_t*dev,unsignedbaseminor,unsignedcount,constchar*name),意思就是请求内核分配count个设备号,且次设备号从baseminor开始。
(dev是主设备号,baseminor是起始次设备号,count是分配的设备号数目,name是设备名)。
设备号是相当宝贵的,不用的时候请注销掉,voidunregister_chrdev_region(dev_tfrom,unsignedcount)
有了设备号之后我们要创建设备文件了,创建设备文件的办法有两种:
1.mknod,命令手工创建
2.自动创建
mknod用法,mknodfilenametypemajorminor,这里filename表示设备名,type表示设备类型(比如clock,char啊),major是主设备号,minor是次设备号。
打个比方
在linux字符驱动程序设计中,有三种非常重要的数据结构:
structfile(表示打开的文件,系统每个打开的文件内核都有一个关联的structfile,内核打开文件的时候创建,在文件关闭之后是自动释放)
structinode(记录文件物理信息,比如文件的位置,比如一个设备文件的设备号是多少,一个文件可以有多个file结构,但只有一个inode)
structfile_operations(一个函数指针的集合,定义能在设备上进行的操作,结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作,保存为null)打个比方,我们应用程序需要通过驱动程序来对硬件进行操作,比如我们应用程序发出一个read的调用,那么我们驱动程序凭什么对你发出的read做出反应?
那么请看下面的一张图
还是拿read来做例子,应用程序发出read,全凭这张图里函数的转换,驱动程序做出反应,调用memoryread。
我这里拿了个led的例子供大家参考对照理解
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineLED_MAX_LENGTH8/*maximumcharswrittentoprocfile*/
staticinlinevoidled_toggle(void)
{
unsignedcharval=get_auxio();
unsignedcharon,off;
if(val&AUXIO_LED){
on=0;
off=AUXIO_LED;
}else{
on=AUXIO_LED;
off=0;
}
set_auxio(on,off);
}
staticstructtimer_listled_blink_timer;
staticvoidled_blink(unsignedlongtimeout)
{
led_toggle();
/*reschedule*/
if(!
timeout){/*blinkaccordingtoload*/
led_blink_timer.expires=jiffies+
((1+(avenrun[0]>>FSHIFT))*HZ);
led_blink_timer.data=0;
}else{/*blinkatuserspecifiedinterval*/
led_blink_timer.expires=jiffies+(timeout*HZ);
led_blink_timer.data=timeout;
}
add_timer(&led_blink_timer);
}
staticintled_proc_show(structseq_file*m,void*v)
{
if(get_auxio()&AUXIO_LED)
seq_puts(m,"on\n");
else
seq_puts(m,"off\n");
return0;
}
staticintled_proc_open(structinode*inode,structfile*file)
{
returnsingle_open(file,led_proc_show,NULL);
}
staticssize_tled_proc_write(structfile*file,constchar__user*buffer,
size_tcount,loff_t*ppos)
{
char*buf=NULL;
if(count>LED_MAX_LENGTH)
count=LED_MAX_LENGTH;
buf=kmalloc(sizeof(char)*(count+1),GFP_KERNEL);
if(!
buf)
return-ENOMEM;
if(copy_from_user(buf,buffer,count)){
kfree(buf);
return-EFAULT;
}
buf[count]='\0';
/*workaround\nwhenecho'ingintoproc*/
if(buf[count-1]=='\n')
buf[count-1]='\0';
/*beforewechangeanythingwewanttostopanyrunningtimers,
*otherwisecallssuchasonwillhavenopersistenteffect
*/
del_timer_sync(&led_blink_timer);
if(!
strcmp(buf,"on")){
auxio_set_led(AUXIO_LED_ON);
}elseif(!
strcmp(buf,"toggle")){
led_toggle();
}elseif((*buf>'0')&&(*buf<='9')){
led_blink(simple_strtoul(buf,NULL,10));
}elseif(!
strcmp(buf,"load")){
led_blink(0);
}else{
auxio_set_led(AUXIO_LED_OFF);
}
kfree(buf);
returncount;
}
staticconststructfile_operationsled_proc_fops={
.owner=THIS_MODULE,
.open=led_proc_open,
.read=seq_read,
.llseek=seq_lseek,
.release=single_release,
.write=led_proc_write,
};
staticstructproc_dir_entry*led;
#defineLED_VERSION"0.1"
staticint__initled_init(void)
{
init_timer(&led_blink_timer);
led_blink_timer.function=led_blink;
led=proc_create("led",0,NULL,&led_proc_fops);
if(!
led)
return-ENOMEM;
led->owner=THIS_MODULE;
printk(KERN_INFO
"led:
version%s,LarsKotthoff
LED_VERSION);
return0;
}
staticvoid__exitled_exit(void)
{
remove_proc_entry("led",NULL);
del_timer_sync(&led_blink_timer);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("LarsKotthoff
MODULE_DESCRIPTION("ProvidescontrolofthefrontLEDonSPARCsystems.");
MODULE_LICENSE("GPL");
MODULE_VERSION(LED_VERSION);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 浅谈 linux 驱动 字符 设备 驱动程序