先谈谈如何写linux驱动:
- 在驱动模块初始化函数中调用register_chrdev(),将驱动向系统注册为一个字符设备,伪装成一个文件,上层的应用可以通过访问这个文件(字符设备),来操作驱动模块。
- 驱动模块注册为字符设备后,还需要用户在命令行中敲mknod命令来创建一个对应的字符文件,上层应用就是用open, close, read, write该文件这样的方式来访问驱动模块。
- 装了udev的系统,可以让系统自动为您创建该字符文件:首先调用cdev_init()和cdev_add()来创建字符设备,然后通过调用device_create()在/dev/目录下创建对应的字符文件,
具体方法可以参考sdk8remote代码中Dib07x0RegisterDev()函数。另,需要在etc/udev/rules.d/目录下放相应的rules文件
接着谈谈usb驱动的写法,以sdk8remote项目代码为例:
- module_init(Dib07x0ModuleInit)
驱动模块初始化入口
- Dib07x0ModuleInit():模块初始化函数
调用usb_register()注册usb接口驱动
- usb_register():usb驱动接口注册函数
1. 可以在Dib07x0ModuleInit()里调用,这样insmod之后,驱动接口即被注册(设备id被注册),有相应设备插入则probe会被调用(此种做法参考LinuxKernelSdioMx28)
2. 也可以在usb设备初始化时调用,这样设备插入时,probe不会被调用,只有在usb设备初始化时,usb_register()被调用,系统才会重新检测设备id,并调用probe。(此种做法好处是,模块初始化不涉及何种设备,具有更好的通用性。参考LinuxKernelSdioMx53)
- static struct usb_driver Dib07x0UsbDriver
是usb接口驱动的结构体包括probe()函数等,如下
static struct usb_driver Dib07x0UsbDriver =
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15)
.owner = THIS_MODULE,
#endif
.name = "dib07x0",
.probe = Dib07x0UsbProbe,
.disconnect = Dib07x0UsbDisconnect,
.suspend = Dib07x0UsbSuspend,
.resume = Dib07x0UsbResume,
.id_table = Dib07x0UsbTable,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
.supports_autosuspend = 1,
#endif
};
其中.id_table很重要,它里面定义了此usb驱动模块关心的usb设备id号,只有插入的usb设备的id号和这里面定义的id对应上,系统才会调用.probe函数。
- Dib07x0UsbProbe()
系统发现设备后调用的函数,可以在这个函数里做接口和端点的设置和判断,然后调用Dib07x0RegisterDev()分别创建不同端点的字符设备。
- Dib07x0RegisterDev()
调用cdev_init()和cdev_add(),将驱动模块向系统注册为字符设备,并将操作该设备的接口函数file_operations也一起注册了。
由于一般usb设备都有多个接口和端点,而每一个端点对应一个字符设备(如control端点和data端点分别对应两个字符设备),因此注册字符设备的操作必须在probe函数里调用,
即只有在系统检测到硬件设备并发现端点时一个一个地去注册字符设备。注销字符设备,则在设备移除函数.disconnect()里调用(参考sdk8remote代码)
- struct file_operations
包含如下最基本的文件操作函数,
struct file_operations fops =
{
.ioctl = DibBridgeTargetModuleIoctl, //控制命令或数据传输
.open = DibBridgeTargetModuleOpen,
.read = DibBridgeTargetModuleReadData, //数据传输
.write = DibBridgeTargetModuleWriteData
.release = DibBridgeTargetModuleRelease,
};
- Dib07x0UsbDisconnect()
usb设备断开时系统会调用此函数来处理
- Dib07x0UsbSuspend()
系统休眠时,会调用此函数来处理
- Dib07x0UsbResume()
系统恢复时,会调用此函数来处理
- .supports_autosuspend
设为1时,表示此设备(或驱动)支持usb的选择性挂起(selective suspend or autosuspend):选择性挂起是指在系统不休眠的情况下,系统主动停止不工作的外设,
比如插在某一个usb口上的usb dongle等,这时系统和其他usb端口还是正常工作的,主要是为了省电。
关于USB读写操作
- 主机对USB设备的读写操作需要借助URB(Usb Request Block):usb_alloc_urb,usb_fill_bulk_urb,usb_submit_urb等。这是异步操作,URB完成之后,会回调一个completing function来做结束,在回调函数里去判断urb返回的状态。系统会负责user space和kernel space之间的数据交换。
- 还有另外一个不需要创建URB的选择,即usb_control_msg和usb_bulk_msg,这两个函数是同步函数,因此适合小批量数据传输,不适合做大批量数据传输或者在中断函数里使用。其中,usb_control_msg是顾名思义是专为contorl EP使用的,usb_bulk_msg是给其它EP用的。如果函数运行成功(读操作),需要调用copy_to_user()将读取到的数据传送到user space。
- 在驱动模块初始化函数中调用register_chrdev(),将驱动向系统注册为一个字符设备,伪装成一个文件,上层的应用可以通过访问这个文件(字符设备),来操作驱动模块。
- 驱动模块注册为字符设备后,还需要用户在命令行中敲mknod命令来创建一个对应的字符文件,上层应用就是用open, close, read, write该文件这样的方式来访问驱动模块。
- 装了udev的系统,可以让系统自动为您创建该字符文件:首先调用cdev_init()和cdev_add()来创建字符设备,然后通过调用device_create()在/dev/目录下创建对应的字符文件,
具体方法可以参考sdk8remote代码中Dib07x0RegisterDev()函数。另,需要在etc/udev/rules.d/目录下放相应的rules文件
接着谈谈usb驱动的写法,以sdk8remote项目代码为例:
- module_init(Dib07x0ModuleInit)
驱动模块初始化入口
- Dib07x0ModuleInit():模块初始化函数
调用usb_register()注册usb接口驱动
- usb_register():usb驱动接口注册函数
1. 可以在Dib07x0ModuleInit()里调用,这样insmod之后,驱动接口即被注册(设备id被注册),有相应设备插入则probe会被调用(此种做法参考LinuxKernelSdioMx28)
2. 也可以在usb设备初始化时调用,这样设备插入时,probe不会被调用,只有在usb设备初始化时,usb_register()被调用,系统才会重新检测设备id,并调用probe。(此种做法好处是,模块初始化不涉及何种设备,具有更好的通用性。参考LinuxKernelSdioMx53)
- static struct usb_driver Dib07x0UsbDriver
是usb接口驱动的结构体包括probe()函数等,如下
static struct usb_driver Dib07x0UsbDriver =
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15)
.owner = THIS_MODULE,
#endif
.name = "dib07x0",
.probe = Dib07x0UsbProbe,
.disconnect = Dib07x0UsbDisconnect,
.suspend = Dib07x0UsbSuspend,
.resume = Dib07x0UsbResume,
.id_table = Dib07x0UsbTable,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
.supports_autosuspend = 1,
#endif
};
其中.id_table很重要,它里面定义了此usb驱动模块关心的usb设备id号,只有插入的usb设备的id号和这里面定义的id对应上,系统才会调用.probe函数。
- Dib07x0UsbProbe()
系统发现设备后调用的函数,可以在这个函数里做接口和端点的设置和判断,然后调用Dib07x0RegisterDev()分别创建不同端点的字符设备。
- Dib07x0RegisterDev()
调用cdev_init()和cdev_add(),将驱动模块向系统注册为字符设备,并将操作该设备的接口函数file_operations也一起注册了。
由于一般usb设备都有多个接口和端点,而每一个端点对应一个字符设备(如control端点和data端点分别对应两个字符设备),因此注册字符设备的操作必须在probe函数里调用,
即只有在系统检测到硬件设备并发现端点时一个一个地去注册字符设备。注销字符设备,则在设备移除函数.disconnect()里调用(参考sdk8remote代码)
- struct file_operations
包含如下最基本的文件操作函数,
struct file_operations fops =
{
.ioctl = DibBridgeTargetModuleIoctl, //控制命令或数据传输
.open = DibBridgeTargetModuleOpen,
.read = DibBridgeTargetModuleReadData, //数据传输
.write = DibBridgeTargetModuleWriteData
.release = DibBridgeTargetModuleRelease,
};
- Dib07x0UsbDisconnect()
usb设备断开时系统会调用此函数来处理
- Dib07x0UsbSuspend()
系统休眠时,会调用此函数来处理
- Dib07x0UsbResume()
系统恢复时,会调用此函数来处理
- .supports_autosuspend
设为1时,表示此设备(或驱动)支持usb的选择性挂起(selective suspend or autosuspend):选择性挂起是指在系统不休眠的情况下,系统主动停止不工作的外设,
比如插在某一个usb口上的usb dongle等,这时系统和其他usb端口还是正常工作的,主要是为了省电。
关于USB读写操作
- 主机对USB设备的读写操作需要借助URB(Usb Request Block):usb_alloc_urb,usb_fill_bulk_urb,usb_submit_urb等。这是异步操作,URB完成之后,会回调一个completing function来做结束,在回调函数里去判断urb返回的状态。系统会负责user space和kernel space之间的数据交换。
- 还有另外一个不需要创建URB的选择,即usb_control_msg和usb_bulk_msg,这两个函数是同步函数,因此适合小批量数据传输,不适合做大批量数据传输或者在中断函数里使用。其中,usb_control_msg是顾名思义是专为contorl EP使用的,usb_bulk_msg是给其它EP用的。如果函数运行成功(读操作),需要调用copy_to_user()将读取到的数据传送到user space。