
高级字符驱动.pdf
15页一、ioctl函数大部分设备除了读写能力,还可进行超出简单的数据传输之外的操作,所以设备驱动也必须具备进行各种硬件控制操作的能力.这些操作常常通过 ioctl 方法来支持,它有和用户空间版本不同的原型:int(*ioctl)(struct inode*inode,struct file*filp,unsigned int cmd,unsigned long arg);需要注意的是:不管可选的参数 arg 是否由用户给定为一个整数或一个指针,它都以一个 unsigned long 的形式传递如果调用程序不传递arg 参数,被驱动收到的 arg 值是未定义的因为在 arg 参数上的类型检查被关闭了,所以若一个非法参数传递给 ioctl,编译器是无法报警的,且任何关联的错误难以查找.二、定位设备(llseek函数)llseek是修改文件中的当前读写位置的系统调用,内核中的缺省的实现进行移位通过修改 filp-f_pos,这是文件中的当前读写位置对于 lseek 系统调用要正确工作,读和写方法必须通过更新它们收到的偏移量来配合如果设备是不允许移位的,你不能只制止声明 llseek 操作,因为缺省的方法允许移位。
应当在你的 open 方法中,通过调用 nonseekable_open 通知内核你的设备不支持 llseek:三、实例代码*author:Jaguar.Yuan*date:2010-8-7*describion:加入锁机制的高级字符设备驱动,同时实现对llseek以及ioctl的测试,并通过使用module_param()实现对关键数据的自定义输入filename:scull2.c*/#include#include#include#include#include#include#include#include#include#include#include scull01.h#define SCULL2_SIZE 0 x1000/*定义全局内存最大 4K字节*/#define MEM_CLEAR 0X1/*清 0 全局内存*/#define SCULL2_MAJOR 0/*定义为动态分配主设备号*/static int scull2_major=SCULL2_MAJOR;int scull2_minor=0;int scull2_nr_devs=SCULL_NR_DEVS;/设 备 数 量int scull2_quantum=SCULL_QUANTUM;int scull2_qset=SCULL_QSET;名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 15 页 -/*scull2结 构 体 定 义*/struct scull2_qset void*data;struct scull2_qset*next;struct scull2_dev struct scull2_qset*data;/*指向 quantum set*/int quantum;/*当前quantum大小*/int qset;/*当 前 结 构 中 数 组 大 小*/unsigned long size;/*存 储 数 据 量 大 小*/unsigned int access_key;/*被sculluid 和 scullpriv调用*/struct semaphore sem;/*信号量声明*/struct cdev cdev;/*字符设备结构体 */;/*struct semaphore spinlock_t lock;/自旋锁类型 unsigned int count;/信号量计数 struct list_head wait_list;/双向链表结构等待队列,即当需要等待信号量时,调用进程把自己加入到等待队列中,然后进入睡眠状态.;*/struct scull2_dev*scull2_devices;/设备结构体指针/清空 scull2设备内容;必须调用与信号设备。
int scull2_trim(struct scull2_dev*dev)struct scull2_qset*next,*dptr;/声明两个指针指向下一个和前一个字符设备结构int qset=dev-qset;/dev不为 null int i;/采用循环结构清空结构体中原有数据内容for(dptr=dev-data;dptr;dptr=next)if(dptr-data)for(i=0;i datai);/依次对结构体中每个段清空 kfree(dptr-data);/最后清除设备信息 dptr-data=NULL;next=dptr-next;/指向设备中下一个字符设备 kfree(dptr);/释放当前设备/运用默认数据,初始化结构体中内容dev-size=0;dev-quantum=scull2_quantum;dev-qset=scull2_qset;dev-data=NULL;return 0;#ifdef SCULL_DEBUG/提供调试用的相关接口,仅需要在进行调试时被执行内容相关函数/对于/proc 文件系统,该函数的功能是读取入口,提供相关调试信息名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 15 页 -int scull2_read_procmem(char*buf,char*start,off_t offset,int count,int*eof,void*data)int i,j,len=0;int limit=count-80;/设备打印长度不超过80/scull2_nr_devs为设备数量for(i=0;iscull2_nr_devs&lendata;if(down_interruptible(&d-sem)return-ERESTARTSYS;len+=sprintf(buf+len,nDevice%i:qset%i,q%i,sz%lin,i,d-qset,d-quantum,d-size);for(;qs&len next)len+=sprintf(buf+len,item at%p,qset at%pn,qs,qs-data);if(qs-data&!qs-next)for(j=0;jqset;j+)if(qs-dataj)len+=sprintf(buf+len,%4i:%8pn,j,qs-dataj;up(&scull2_devicesi.sem);*eof=1;return len;/通 过 以 上 函 数,实 现 了 文 件 队 列(seq_file)的 同 时 存 在,而 以 前 的read_procmem函数也同样可以执行/下面是进行顺序迭代,根据设备的设备号进行简单的迭代static void*scull2_seq_start(struct seq_file*s,loff_t*pos)if(*pos=scull2_nr_devs)return NULL;/如果偏移位大于设备总数,返回空。
return scull2_devices+*pos;static void*scull2_seq_next(struct seq_file*s,void*v,loff_t*pos)(*pos)+;if(*pos=scull2_nr_devs)return NULL;return scull2_devices+*pos;static void scull2_seq_stop(struct seq_file*s,void*v)/什么事也不用做 /显示结构中相关信息static int scull2_seq_show(struct seq_file*s,void*v)struct scull2_dev*dev=(struct scull2_dev*)v;struct scull2_qset*d;int i;if(down_interruptible(&dev-sem)return-ERESTRATSYS;seq_printf(s,nDevice%i:qest%i,q%i,名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 15 页 -sz%lin,(int)(dev-scull2_devices),dev-qset,dev-quantum,dev-size);for(d=dev-data;d;d=d-next)seq_printf(s,item at%p,qset at%pn,d,d-data);if(d-data&!d-next)for(i=0;iqset;i+)if(d-datai)seq_printf(s,%4i:%8pn,i,d-datai);up(&dev-sem);return 0;/构建队列执行对应的结构体static struct seq_operations scull2_seq_ops=.start=scull2_seq_start,.next=sucll2_seq_next,.stop=scull2_seq_stop,.show=scull2_seq_show,;/现在可以执行在/proc文件,我们只需要open,建立执行队列static int scull2_proc_open(struct inode*inode,struct file*file)return seq_open(file,&scull2_seq_ops);/为/proc 建立相关文件结构static struct file_operations sucll_proc_ops=.owner=THIS_MODULE,.open=scull2_proc_open,.read=seq_read,.llseek=seq_lseek,.release=seq_release,;/建立/proc 执行的 create 及 remove对应函数static void scull2_create_proc(void)struct proc_dir_entry*entry;/create_proc_read_entry(名字,default mode,parent dir,调用函数,client data)create_proc_read_entry(scullmem,0,NULL,scull2_read_procmem,NULL);entry=create_proc_entry(scullseq,0,NULL);if(entry)entry-proc_fops=&scull2_proc_ops;static void scull2_remove_proc(void)/*no problem if it was not registered*/remove_proc_entry(scullmem,NULL/*parent dir*/);remove_proc_entry(scullseq,NULL);#endif/SCULL_DEBUG结束/文件打开函数int scull2_open(struct inode*inode,struct file*filp)struct scull2_dev*dev;/设备相关信息dev=container_of(inode-i_cdev,struct scull2_dev,cdev);/为其它方法 for other methods filp-private_data=dev;/修改设备打开时为只写now trim to 0 the length of the device if open was 名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 15 页 -write-only if(filp-f_flags&O_ACCMODE)&O_WRONLY)if(down_interruptible(&dev-sem)return-ERESTARTSYS;scull2_trim(dev);/忽略 errors up(&dev-sem);return 0;/文件释放函数int 。












