1.驱动程序中的若干重要函数ioctl,register_chrdev,request_irq
(1)Ioctl方法
Ioctl方法主要用于对设备进行读写之外的其他控制,比如配置设备、进入或退出某种操作模式,这些操作一般都无法通过read/write文件操作来完成,无法通过write操作控制,这就是ioctl操作的功能。
用户空间的ioctl函数的原型为:
int ioctl(inf fd,int cmd,…)
其中的…代表可变数目的参数表,实际中是一个可选参数,一般定义为:
int ioctl(inf fd,int cmd,char *argp)
驱动程序中定义的ioctl 方法原型为:
int (*ioctl) (struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
inode 和 file两个指针对应应用程序传递的文件描述符fd(即在用户态下,调用ioctl时,就是把fd映射到内核态的前两个参数),cmd不会被修改地传递给 驱动程序,可选的参数arg则无论用户应用程序使用的是指针还是其他类型值,都以unsigned long的形式传递给驱动。
(2)驱动程序的注册,这个工作是和驱动模块获 得主设备号时初始化一同进行的。你可以使用头文件 linux/fs.h中的函数register_chrdev来实现。
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
其中unsigned int major是你申请的主设备号, const char *name是设备文件名,将要在文件/proc/devices中出现。 struct file_operations *fops是指向你的驱动模块的 file_operations表的指针即用来登记驱动程序实际执行操作的函数的指针。负的返回值意味着注册失败。 注册时并不需要提供从设备号。内核本身并不在意从设备号。如何申请一个没有被使用的主设备号?最简单的方法是查看文件 Documentation/devices.txt从中挑选一个没有被使用的。这不是 一劳永逸的方法因为你无法得知该主设备号在将来会被占用。最终的方法是让内核为你动 态分配一个。如果你向函数register_chrdev传递为0的主设备号,那么 返回的就是动态分配的主设备号(如ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);)。副作用就是既然你无法得知主设备号,你就无法预先建立一个设备文件。 有多种解决方法。第一种方法是新注册的驱动模块会输出自己新分配到的主设备号,所以我们可以手工建立需要的设备文件。第二种是利用文件 /proc/devices新注册的驱动模块的入口,要么手工建立设备文件, 要么编一个脚本去自动读取该文件并且生成设备文件。第三种是在我们的模块中,当注册成功时,使用mknod调用建立设备文件并且调用 rm 删除该设备文件在驱动模块调用函数(cleanup_module(void){unregister_chrdev(major,’ DEVICE_NAME’)}第一参数表示主设备号,第二个参数表示设备名)前。
一种问题要注意的是,在操作驱动程序时,系统处于内核态,此进要输出打印信息要用printk()函数,而在用户态时,要输出打印信息时,要用printf()函数。
编译驱动程序时,$gcc –O2 –DMODULE –D__KERNEL__ -c(test.o可加可不加) test.c其中各个参数表示:本机gcc,二级优化,编译成模块,声明为要加载到内核的模块。如果设备驱动程序有多个文件,把每个文件按上面的命令行编 译后,ld –r file1.o file2.o –o modulename进行链接。
编译完成后,要加载到内核中去,$insmod –f test.o如果安装成功在/proc/devices中可以看到设备test,并可以看到它的主设备号。
要卸载的话,$rmmod test
要创建设备文件,mknod /dev/test c major minor
用mknod命令来创建一个设备文件:mknod device_driver c 120 0, device_driver为设备文件名,c指的是字符设备,120是主设备号,0为次设备号。device_driver这个名字与注册函数中使用的字 符串要一致!即register_chrdev(0, DEVICE_NAME, &s3c2410_fops); 中的 DEVICE_NAME要与device_driver一致。
要查看驱动程序的主设备号,$ cat /proc/devices对应,就可以看到主设备号,从设备号一般设置为0。
(3)中断申请函数int request_irq(unsigned int irq,void (*handle)(int ,void*,struct pt_regs*),unsigned int long flags,const char *device, void *dev_id)各个参数表示:要申请的中断号,申请的中断服务程序入口,中断标识(SA_INTERRUPT快速中断(区别于ARM中的快速中断),0 为正常中断,或共享中断),中断产生的设备名(哪一设备要用到这个中断, 用于在/proc/interrupts中显示的中断的拥有者), 用于标识产生中断的设备号。如果登记成功会返回0,这时在/proc/interrupts文件中可以看到你请求的中断。
(4) devfs_register函数
其原型为:
devfs_register(devfs_handle_t dir, const char *name, unsigned int flags,
unsigned int major, unsigned int minor,
umode_t mode, void *ops, void *info)
其中的参数说明如下:
Dir 新创建的设备文件的父目录,一般设为null, 表示父目录为/dev
Name 设备名称,如想包含子目录,可以直接在名字中包含’/’
Flags Devfs标志的位掩码。
Major 主设备号 如果在flags参数中指定为DEVFS_FL_AUTO_DEVNUM,则主次设备号就无用了。
Minor 次设备号,
Mode 设备的访问模式
Ops 设备的文件操作数据结构指针
Info filp->private_data的默认值。
驱动中一些重要函数
最新推荐文章于 2023-11-06 17:53:19 发布
驱动中一些重要函数