使用libusb之前你的linux系统必须装有usb文件系统,这里还介绍了使用hiddev设备文件来访问设备,目的在于不仅可以比较出usb的易用性,还提供了一个转化成libusb驱动的案例。
3.1 find 设备
任何驱动第一步首先是寻找到要操作的设备,我们先来看看HID驱动是怎样寻找到设备的。我们假设寻找设备的函数Device_Find(注:代码只是为了方便解说,不保证代码的健全)
1. /* 我们简单看一下使用hid驱动寻找设备的实现,然后在看一下libusb是如何寻找设备的 */ 2. int Device_Find() 3. { 4. char dir_str[100]; /* 这个变量我们用来保存设备文件的目录路径 */ 5. char hiddev[100]; /* 这个变量用来保存设备文件的全路径 */ 6. DIR dir; 7. 8. /* 申请的字符串数组清空,这个编程习惯要养成 */ 9. memset (dir_str, 0 , sizeof(dir_str)); 10. memset (hiddev, 0 , sizeof(hiddev)); 11. 12. /* hiddev 的设备描述符不在/dev/usb/hid下面,就在/dev/usb 下面 13. 这里我们使用opendir函数来检验目录的有效性 14. 打开目录返回的值保存在变量dir里,dir前面有声明 15. */ 16. dir=opendir("/dev/usb/hid"); 17. if(dir){ 18. /* 程序运行到这里,说明存在 /dev/usb/hid 路径的目录 */ 19. sprintf(dir_str,"/dev/usb/hid/"); 20. closedir(dir); 21. }else{ 22. /* 如果不存在hid目录,那么设备文件就在/dev/usb下 */ 23. sprintf(dir_str,"/dev/usb/"); 24. } 25. 26. /* DEVICE_MINOR 是指设备数,HID一般是16个 */ 27. for(i = 0; i < DEVICE_MINOR; i++) { 28. /* 获得全路径的设备文件名,一般hid设备文件名是hiddev0 到 hiddev16 */ 29. sprintf(hiddev, "%shiddev%d", dir_str,i); 30. 31. /* 打开设备文件,获得文件句柄 */ 32. fd = open(hiddev, O_RDWR); 33. if(fd > 0) { 34. 35. /* 操作设备获得设备信息 */ 36. ioctl(fd, HIDIOCGDEVINFO, &info); 37. 38. /* VENDOR_ID 和 PRODUCT_ID 是标识usb设备厂家和产品ID, 39. 驱动都需要这两个参数来寻找设备,到此我们寻找到了设备 */ 40. if(info.vendor== VENDOR_ID && info.product== PRODUCT_ID) { 41. /* 这里添加设备的初始化代码 */ 42. 43. 44. device_num++; /* 找到的设备数 */ 45. } 46. close(fd); 47. } 48. } 49. return device_num; /* 返回寻找的设备数量 */ 50. }
我们再来看libusb是如何来寻找和初始化设备
1. int Device_Find() 2. { 3. struct usb_bus *busses; 4. 5. int device_num = 0; 6. 7. device_num = 0; /* 记录设备数量 */ 8. 9. usb_init(); /* 初始化 */ 10. usb_find_busses(); /* 寻找系统上的usb总线 */ 11. usb_find_devices(); /* 寻找usb总线上的usb设备 */ 12. 13. /* 获得系统总线链表的句柄 */ 14. busses = usb_get_busses(); 15. 16. struct usb_bus *bus; 17. /* 遍历总线 */ 18. for (bus = busses; bus; bus = bus->next) { 19. struct usb_device *dev; 20. /* 遍历总线上的设备 */ 21. for (dev = bus->devices; dev; dev = dev->next) { 22. 23. /* 寻找到相关设备, */ 24. if(dev->descriptor.idVendor==VENDOR_ID&& dev->descriptor.idProduct == PRODUCT_ID){ 25. /* 这里添加设备的初始化代码 */ 26. 27. device_num++; /* 找到的设备数 */ 28. } 29. } 30. } 31. return device_num; /* 返回设备数量 */ 32. }
注:在新版本的libusb中,usb_get_busses就可以不用了 ,这个函数是返回系统上的usb总线链表句柄
这里我们直接用usb_busses变量,这个变量在usb.h中被定义为外部变量
所以可以直接写成这样:
1. struct usb_bus *bus; 2. for (bus = usb_busses; bus; bus = bus->next) { 3. struct usb_device *dev; 4. for (dev = bus->devices; dev; dev = dev->next) { 5. /* 这里添加设备的初始化代码 */ 6. } 7. }
3.2 打开设备
假设我们定义的打开设备的函数名是device_open,
1. /* 使用hid驱动打开设备 */ 2. int Device_Open() 3. { 4. int handle; 5. /* 传统HID驱动调用,通过open打开设备文件就可 */ 6. handle = open(“hiddev0”, O_RDONLY); 7. } 8. 9. /* 使用libusb打开驱动 */ 10. 11. int Device_Open() 12. { 13. /* LIBUSB 驱动打开设备,这里写的是伪代码,不保证代码有用 */ 14. struct usb_device* udev; 15. usb_dev_handle* device_handle; 16. 17. 18. /* 当找到设备后,通过usb_open打开设备,这里的函数就相当open 函数 */ 19. device_handle = usb_open(udev); 20. }
3.3 读写设备和操作设备
假设我们的设备使用控制传输方式,至于批处理传输和中断传输限于篇幅这里不介绍
我们这里定义三个函数,Device_Write, Device_Read, Device_Report
Device_Report 功能发送接收函数
Device_Write 功能写数据
Device_Read 功能读数据
Device_Write和Device_Read调用Device_Report发送写的信息和读的信息,开发者根据发送的命令协议来设计,我们这里只简单实现发送数据的函数。
假设我们要给设备发送72字节的数据,头8个字节是报告头,是我们定义的和设备相关的规则,后64位是数据。
HID驱动的实现(这里只是用代码来有助理解,代码是伪代码)
1. int Device_Report(int fd, unsigned char *buffer72) 2. { 3. int ret; /* 保存ioctl函数的返回值 */ 4. int index; 5. unsigned char send_data[72]; /* 发送的数据 */ 6. unsigned char recv_data[72]; /* 接收的数据 */ 7. struct hiddev_usage_ref uref; /* hid驱动定义的数据包 */ 8. struct hiddev_report_info rinfo; /* hid驱动定义的 9. 10. memset(send_data, 0, sizeof(send_data)); 11. memset(recv_data, 0, sizeof(recv_data)); 12. 13. memcpy(send_data, buffer72, 72); 14. /* 这在发送数据之前必须调用的,初始化设备 */ 15. ret = ioctl(fd, HIDIOCINITREPORT, 0); 16. if( ret !=0) { 17. return NOT_OPENED_DEVICE;/* NOT_OPENED_DEVICE 属于自己定义宏 */ 18. } 19. /* HID设备每次传输一个字节的数据包 */ 20. for(index = 0; index < 72; index++) { 21. /* 设置发送数据的状态 */ 22. uref.report_type = HID_REPORT_TYPE_FEATURE; 23. uref.report_id = HID_REPORT_ID_FIRST; 24. uref.usage_index = index; 25. uref.field_index = 0; 26. uref.value = send_data[index]; 27. ioctl(fd, HIDIOCGUCODE, &uref); 28. ret=ioctl(fd, HIDIOCSUSAGE, &uref); 29. if(ret != 0 ){ 30. return UNKNOWN_ERROR; 31. } 32. } 33. /* 发送数据 */ 34. rinfo.report_type = HID_REPORT_TYPE_FEATURE; 35. rinfo.report_id = HID_REPORT_ID_FIRST; 36. rinfo.num_fields = 1; 37. ret=ioctl(fd, HIDIOCSREPORT, &rinfo); /* 发送数据 */ 38. if(ret != 0) { 39. return WRITE_REPORT; 40. } 41. 42. /* 接受数据 */ 43. ret = ioctl(fd, HIDIOCINITREPORT, 0); 44. for(index = 0; index < 72; index++) { 45. uref.report_type = HID_REPORT_TYPE_FEATURE; 46. uref.report_id = HID_REPORT_ID_FIRST; 47. uref.usage_index = index; 48. uref.field_index = 0; 49. ioctl(fd, HIDIOCGUCODE, &uref); 50. ret = ioctl(fd, HIDIOCGUSAGE, &uref); 51. if(ret != 0 ) { 52. return UNKNOWN_ERROR; 53. } 54. recv_data[index] = uref.value; 55. } 56. 57. memcpy(buffer72, recv_data, 72); 58. 59. return SUCCESS; 60. }
libusb驱动的实现
1. int Device_Report(int fd, unsigned char *buffer72) 2. { 3. /* 定义设备句柄 */ 4. usb_dev_handle* Device_handle; 5. 6. /* save the data of send and receive */ 7. unsigned char send_data[72]; 8. unsigned char recv_data[72]; 9. 10. int send_len; 11. int recv_len; 12. 13. /* 数据置空 */ 14. memset(send_data, 0 , sizeof(send_data)); 15. memset(recv_data, 0 , sizeof(recv_data)); 16. 17. /* 这里的g_list是全局的数据变量,里面可以存储相关设备的所需信息, 18. 当然我们 也可以从函数形参中传输进来,设备的信息在打开设备时初始化 19. ,我们将在后面的总结中详细描述一下 */ 20. Device_handle = (usb_dev_handle*)(g_list[fd].device_handle); 21. if (Device_handle == NULL) { 22. return NOT_OPENED_DEVICE; 23. } 24. 25. /* 这个函数前面已经说过,在操作设备前是必须调用的, 0是指用默认的设备 */ 26. usb_claim_interface(Device_handle, 0); 27. 28. /* 发送数据,所用到的宏定义在usb.h可以找到,我列出来大家看一下 29. #define USB_ENDPOINT_OUT 0x00 30. #define USB_TYPE_CLASS (0x01 << 5) 31. #define USB_RECIP_INTERFACE 0x01 32. 33. #define HID_REPORT_SET 0x09 */ 34. send_len = usb_control_msg(Device_handle, 35. USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 36. HID_REPORT_SET, 37. 0x300, 38. 0, 39. send_data, 72, USB_TIMEOUT); 40. 41. /* 发送数据有错误 */ 42. if (send_len < 0) { 43. return WRITE_REPORT; 44. } 45. 46. if (send_len != 72) { 47. return send_len; 48. } 49. 50. /* 接受数据 51. #define USB_ENDPOINT_IN 0x80 52. #define USB_TYPE_CLASS (0x01 << 5) 53. #define USB_RECIP_INTERFACE 0x01 54. #define HID_REPORT_GET 0x01 55. */ 56. recv_len = usb_control_msg(Device_handle, 57. USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 58. HID_REPORT_GET, 59. 0x300, 60. 0, 61. recv_data, 72, USB_TIMEOUT); 62. 63. if (recv_len < 0) { 64. printf("failed to retrieve report from USB device!/n"); 65. return READ_REPORT; 66. } 67. 68. if (recv_len != 72) { 69. return recv_len; 70. } 71. 72. 73. /* 和usb_claim_interface对应 */ 74. usb_release_interface(RY2_handle, 0); 75. memcpy(buffer72, recv_data, 72); 76. 77. return SUCCESS; 78. }
3.4 关闭设备
假设我们定义的关闭设备的函数名是Device_Close()
1. /* 使用hid驱动关闭设备 */ 2. int Device_Close() 3. { 4. int handle; 5. 6. handle = open(“hiddev0”, O_RDONLY); 7. /* 传统HID驱动调用,通过close()设备文件就可 */ 8. 9. close( handle ); 10. } 11. 12. /* 使用libusb关闭驱动 */ 13. int Device_Close() 14. { 15. /* LIBUSB 驱动打开设备,这里写的是伪代码,不保证代码有用 */ 16. struct usb_device* udev; 17. usb_dev_handle* device_handle; 18. 19. device_handle = usb_open(udev); 20. 21. /* libusb库使用usb_close关闭程序 */ 22. usb_close(device_handle); 23. }
libusb 的驱动框架
前面我们看了些主要的libusb函数的使用,这里我们把前面的内容归纳下:
一般的驱动应该都包含如下接口:
Device_Find(); /* 寻找设备接口 */
Device_Open(); /* 打开设备接口 */
Device_Write(); /* 写设备接口 */
Device_Read(); /* 读设备接口 */
Device_Close(); /* 关闭设备接口 */
具体代码如下:
1. #include 2. /* usb.h这个头文件是要包括的,里面包含了必须要用到的数据结构 */ 3. 4. /* 我们将一个设备的属性用一个结构体来概括 */ 5. typedef struct 6. { 7. struct usb_device* udev; 8. usb_dev_handle* device_handle; 9. /* 这里可以添加设备的其他属性,这里只列出每个设备要用到的属性 */ 10. } device_descript; 11. 12. /* 用来设置传输数据的时间延迟 */ 13. #define USB_TIMEOUT 10000 14. 15. /* 厂家ID 和产品 ID */ 16. #define VENDOR_ID 0xffff 17. #define PRODUCT_ID 0xffff 18. 19. /* 这里定义数组来保存设备的相关属性,DEVICE_MINOR可以设置能够同 20. 时操作的设备数量,用全局变量的目的在于方便保存属性 */ 21. #define DEVICE_MINOR 16 22. int g_num; 23. device_descript g_list[ DEVICE_MINOR ]; 24. 25. /* 我们写个设备先找到设备,并把相关信息保存在 g_list 中 */ 26. int Device_Find() 27. { 28. struct usb_bus *bus; 29. struct usb_device *dev; 30. 31. g_num = 0; 32. usb_find_busses(); 33. usb_find_devices(); 34. 35. /* 寻找设备 */ 36. for (bus = usb_busses; bus; bus = bus->next) { 37. for (dev = bus->devices; dev; dev = dev->next) { 38. if(dev->descriptor.idVendor==VENDOR_ID&& dev->descriptor.idProduct == PRODUCT_ID) { 39. /* 保存设备信息 */ 40. if (g_num < DEVICE_MINOR) { 41. g_list[g_num].udev = dev; 42. g_num ++; 43. } 44. } 45. } 46. } 47. return g_num; 48. } 49. /* 找到设备后,我们根据信息打开设备 */ 50. int Device_Open() 51. { 52. /* 根据情况打开你所需要操作的设备,这里我们仅列出伪代码 */ 53. if(g_list[g_num].udev != NULL) { 54. g_list[g_num].device_handle = usb_open(g_list[g_num].udev); 55. } 56. } 57. /* 下面就是操作设备的函数了,我们就不列出来拉,大家可以参考上面的介绍 */ 58. int DeviceWite(int handle) 59. { 60. /* 填写相关代码,具体查看设备协议*/ 61. } 62. int DeviceOpen(int handle) 63. { 64. /* 填写相关代码,具体查看设备协议 */ 65. } 66. /* 最后不要忘记关闭设备 */ 67. void Device_close(int handle) 68. { 69. /* 调用usb_close */ 70. }
小结
到此,使用libusb进行驱动开发介绍完了,通过对库所提供的API的使用可以体会到libusb的易用性。