Note: 本文主要列举几个usb设备驱动实例
一、“USB LED”驱动模块
在前面的实验室中,开发了一个功能齐全的USB HID设备的固件,该设备能够通过使用HID报告发送和接收数据。现在,将开发一个Linux USB主机驱动程序来控制USB设备。驱动将发送USB命令切换PIC32MX470开发板的LED1,LED2和LED3; 它将通过sysfs条目从Linux用户空间接收命令,然后将其传输到PIC32MX HID设备。命令值可以是0x01、0x02、0x03。HID设备在接收到report中的0x01命令值时打开LED1,在接收到report中的0x02命令值时打开LED2,在接收到report中的0x03命令值时打开LED3。
USB HID设备由PIC32MX开发板实现,开发板链接:https://www.microchip.com/DevelopmentTools/ProductDetails/dm320103#additional-summary,
工程代码获取:
链接:https://pan.baidu.com/s/13SdRewnSWavIv3yCGEIsmQ?pwd=mkuh
提取码:mkuh
你必须阻止 hid-generic 驱动程序获得我们的自定义驱动程序的控制,所以你需要把包括我们的驱动程序的USB_VENDOR_ID和USB_DEVICE_ID添加到列表hid_ignore_list[]。打开内核源码/drivers/hid文件夹下的hidquirks.c文件,并将下一行代码(粗体)添加到列表的末尾:
static const struct hid_device_id hid_ignore_list[] = { ... #if IS_ENABLED(CONFIG_MOUSE_SYNAPTICS_USB) { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_STICK) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_COMP_TP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, { HID_USB_DEVICE(0x04d8, 0x003f) }, { } };
1.1 “USB LED”驱动模块的代码描述
下面将描述驱动程序的主要代码部分:
1. 包含的函数头文件:
#include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h>
2. 创建ID Table支持热插拔。Vendor ID和Product ID值必须与PIC32MX USB HID设备中使用的值匹配。
#define USBLED_VENDOR_ID 0x04D8 #define USBLED_PRODUCT_ID 0x003F
/* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { { USB_DEVICE(USBLED_VENDOR_ID, USBLED_PRODUCT_ID) }, { } }; MODULE_DEVICE_TABLE(usb, id_table);
3.创建一个私有结构来存储驱动程序的数据。
struct usb_led { struct usb_device *udev; u8 led_number; };
4. 请参阅下面probe()例程的摘录,包含驱动程序的主要代码行和注释。
static int led_probe(struct usb_interface *interface, const struct usb_device_id *id) { /* Get the usb_device structure from the usb_interface one */ struct usb_device *udev = interface_to_usbdev(interface); struct usb_led *dev = NULL; int retval = -ENOMEM;
dev_info(&interface->dev, "led_probe() function is called.\n");
/* Allocate our private data structure */ dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
/* store the usb device in our data structure */ dev->udev = usb_get_dev(udev);
/* Attach the USB device data to the USB interface */ usb_set_intfdata(interface, dev);
/* create a led sysfs entry to interact with the user space */ device_create_file(&interface->dev, &dev_attr_led);
return 0; }
5. 编写led_store()函数。每当你的用户空间应用程序写入usb设备下的led sysfs条目(/sys/bus/usb/devices/1-1.3:1.0/led)时,驱动程序的led_store()函数就会被调用。通过使用usb_get_intfdata()函数恢复与USB设备关联的usb_led结构。写入led sysfs条目的命令存储在val变量中。最后,您将使用usb_bulk_msg()函数通过USB发送命令值。
内核提供了两个usb_bulk_msg()和usb_control_msg()帮助函数,使得传输简单的批量和控制消息成为可能,而不必创建urb结构、初始化它、提交它并等待它的完成处理程序。这些函数是同步的,会使代码休眠。你不能从中断上下文或者自旋锁中调用它们。
int usb_bulk_msg(struct usb_device * usb_dev, unsigned int pipe, void * data, int len, int * actual_length, int timeout);
下面是usb_bulk_msg()参数的简述:
- usb_dev:指向要发送消息的usb设备的指针
- pipe:端点“pipe”,将消息发送到它。
- data:要发送数据的指针
- len:要发送的数据的字节长度
- actual_length:指向一个位置的指针,该位置用于放置以字节为单位传输的实际长度
- timeout:在消息超时前等待消息完成的时间(以毫秒为单位)
下面是led_store()例程的摘录:
static ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct usb_led *led = usb_get_intfdata(intf); u8 val;
/* transform char array to u8 value */ kstrtou8(buf, 10, &val); led->led_number = val;
/* Toggle led */ usb_bulk_msg(led->udev, usb_sndctrlpipe(led->udev, 1), &led->led_number, 1, NULL, 0);
return count; } static DEVICE_ATTR_RW(led);
6. 添加一个usb_driver结构,它将被注册到USB核心:
static struct usb_driver led_driver = { .name = "usbled", .probe = led_probe, .disconnect = led_disconnect, .id_table = id_table, };
7. 用USB总线注册你的驱动:
module_usb_driver(led_driver);
8. 构建模块并将其加载到目标处理器。
1.2 "USB LED" 驱动源码(usb_led.c)
#include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h> #define USBLED_VENDOR_ID 0x04D8 #define USBLED_PRODUCT_ID 0x003F /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { { USB_DEVICE(USBLED_VENDOR_ID, USBLED_PRODUCT_ID) }, { } }; MODULE_DEVICE_TABLE(usb, id_table); struct usb_led { struct usb_device *udev; u8 led_number; }; static ssize_t led_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct usb_led *led = usb_get_intfdata(intf); return sprintf(buf, "%d\n", led->led_number); } static ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct usb_led *led = usb_get_intfdata(intf); u8 val; int error, retval; dev_info(&intf->dev, "led_store() function is called.\n"); /* transform char array to u8 value */ error = kstrtou8(buf, 10, &val); if (error) return error; led->led_number = val; if (val == 1 || val == 2 || val == 3) dev_info(&led->udev->dev, "led = %d\n", led->led_number); else { dev_info(&led->udev->dev, "unknown led %d\n", led->led_number); retval = -EINVAL; return retval; } /* Toggle led */ retval = usb_bulk_msg(led->udev, usb_sndctrlpipe(led->udev, 1), &led->led_number, 1, NULL, 0); if (retval) { retval = -EFAULT; return retval; } return count; } static DEVICE_ATTR_RW(led); static int led_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); struct usb_led *dev = NULL; int retval = -ENOMEM; dev_info(&interface->dev, "led_probe() function is called.\n"); dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL); if (!dev) { dev_err(&interface->dev, "out of memory\n"); retval = -ENOMEM; goto error; } dev->udev = usb_get_dev(udev); usb_set_intfdata(interface, dev); retval = device_create_file(&interface->dev, &dev_attr_led); if (retval) goto error_create_file; return 0; error_create_file: usb_put_dev(udev); usb_set_intfdata(interface, NULL); error: kfree(dev); return retval; } static void led_disconnect(struct usb_interface *interface) { struct usb_led *dev; dev = usb_get_intfdata(interface); device_remove_file(&interface->dev, &dev_attr_led); usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); kfree(dev); dev_info(&interface->dev, "USB LED now disconnected\n"); } static struct usb_driver led_driver = { .name = "usbled", .probe = led_probe, .disconnect = led_disconnect, .id_table = id_table, }; module_usb_driver(led_driver); MODULE_DESCRIPTION("This is a synchronous led usb controlled module");
MODULE_LICENSE("GPL");
1.3 usb_led.ko使用示例
/* Keep the PIC32MX470 board powered off */
root@raspberrypi:/home# insmod usb_led.ko /* load the module */ usb_led: loading out-of-tree module taints kernel. usbcore: registered new interface driver usbled
/* power now the PIC32MX Curiosity board */ root@raspberrypi:/home# usb 1-1.3: new full-speed USB device number 5 using dwc_otg usb 1-1.3: New USB device found, idVendor=04d8, idProduct=003f, bcdDevice= 1.00 usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 1-1.3: Product: LED_USB HID Demo usb 1-1.3: Manufacturer: Microchip Technology Inc. usbled 1-1.3:1.0: led_probe() function is called.
/* check the new created USB device */ root@raspberrypi:/home# cd /sys/bus/usb/devices/1-1.3:1.0 root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# ls authorized bInterfaceProtocol ep_01 power bAlternateSetting bInterfaceSubClass ep_81 subsystem bInterfaceClass bNumEndpoints led supports_autosuspend bInterfaceNumber driver modalias uevent /* Read the configurations of the USB device */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# cat bNumEndpoints 02 root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0/ep_01# cat direction out root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0/ep_81# cat direction in root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# cat bAlternateSetting 0 root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# cat bInterfaceClass 03 root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# cat bNumEndpoints 02
/* Switch on the LED1 of the PIC32MX Curiosity board */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 1 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: led = 1
/* Switch on the LED2 of the PIC32MX Curiosity board */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 2 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: led = 2
/* Switch on the LED3 of the PIC32MX Curiosity board */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 3 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: led = 3
/* read the led status */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# cat led 3
root@raspberrypi:/home# rmmod usb_led.ko /* remove the module */ usbcore: deregistering interface driver usbled usbled 1-1.3:1.0: USB LED now disconnected
二、“USB LED and Switch”驱动模块
在这个新的实验中,您将增加先前驱动程序的功能。除了控制连接到USB设备的三个led外,Linux主机驱动程序还将从USB HID设备接收一个Pushbutton (PIC32MX470 Curiosity Development Board上的S1开关)状态。驱动程序将发送一个值为0x00的命令到USB设备,然后HID设备将回复一个report,其中第一个字节是S1按钮的状态(“0x00”按下,“0x01”未按下)。在这个驱动程序中,与前一个驱动程序不同,主机和设备之间的通信是通过使用USB请求块(urbs)异步完成的。
2.1 “USB LED and Switch”驱动模块的代码描述
驱动程序的主要代码部分如下:
1. 包含的函数头文件:
#include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h>
2. 创建ID Table支持热插拔。Vendor ID和Product ID值必须与PIC32MX USB HID设备中使用的值匹配。
#define USBLED_VENDOR_ID 0x04D8 #define USBLED_PRODUCT_ID 0x003F /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { { USB_DEVICE(USBLED_VENDOR_ID, USBLED_PRODUCT_ID) }, { } }; MODULE_DEVICE_TABLE(usb, id_table);
3.创建一个私有结构来存储驱动程序的数据。
struct usb_led { struct usb_device *udev; struct usb_interface *intf;struct urb *interrupt_out_urb; struct urb *interrupt_in_urb; struct usb_endpoint_descriptor *interrupt_out_endpoint; struct usb_endpoint_descriptor *interrupt_in_endpoint; u8 irq_data; u8 led_number; u8 ibuffer; int interrupt_out_interval; int ep_in; int ep_out; };
4. 请参阅下面probe()例程的摘录,包含驱动程序的主要代码行和注释。
static int led_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); /* Get the current altsetting of the USB interface */ struct usb_host_interface *altsetting = intf->cur_altsetting; struct usb_endpoint_descriptor *endpoint; struct usb_led *dev = NULL; int ep; int ep_in, ep_out; int size; /* * Find the last interrupt out endpoint descriptor * to check its number and its size * Just for teaching purposes */ usb_find_last_int_out_endpoint(altsetting, &endpoint); /* get the endpoint's number */ ep = usb_endpoint_num(endpoint); /* value from 0 to 15, it is 1 */ size = usb_endpoint_maxp(endpoint); /* Validate endpoint and size */ if (size <= 0) { dev_info(&intf->dev, "invalid size (%d)", size); return -ENODEV; } dev_info(&intf->dev, "endpoint size is (%d)", size); dev_info(&intf->dev, "endpoint number is (%d)", ep); /* Get the two addresses (IN and OUT) of the Endpoint 1 */ ep_in = altsetting->endpoint[0].desc.bEndpointAddress; ep_out = altsetting->endpoint[1].desc.bEndpointAddress; /* Allocate our private data structure */ dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL); /* Store values in the data structure */ dev->ep_in = ep_in; dev->ep_out = ep_out; dev->udev = usb_get_dev(udev); dev->intf = intf; /* allocate the int_out_urb structure */ dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); /* initialize the int_out_urb */ usb_fill_int_urb(dev->interrupt_out_urb, dev->udev, usb_sndintpipe(dev->udev, ep_out), (void *)&dev->irq_data, 1, led_urb_out_callback, dev, 1); /* allocate the int_in_urb structure */ dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_in_urb) goto error_out; /* initialize the int_in_urb */ usb_fill_int_urb(dev->interrupt_in_urb, dev->udev, usb_rcvintpipe(dev->udev, ep_in), (void *)&dev->ibuffer, 1, led_urb_in_callback, dev, 1); /* Attach the device data to the interface */ usb_set_intfdata(intf, dev); /* create the led sysfs entry to interact with the user space */ device_create_file(&intf->dev, &dev_attr_led); /* Submit the interrrupt IN URB */ usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); return 0; }
5. 编写led_store()函数。每当你的用户空间应用程序写入usb设备下的led sysfs条目(/sys/bus/usb/devices/1-1.3:1.0/led)时,驱动程序的led_store()函数就会被调用。通过使用usb_get_intfdata()函数恢复与USB设备关联的usb_led结构。写入led sysfs条目的命令存储在irq_data变量中。最后,您将使用usb_submit_urb()函数通过USB发送命令值。
下面是led_store()例程的摘录:
static ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct usb_led *led = usb_get_intfdata(intf); u8 val; /* transform char array to u8 value */ kstrtou8(buf, 10, &val); led->irq_data = val; /* send the data out */ retval = usb_submit_urb(led->interrupt_out_urb, GFP_KERNEL); return count; } static DEVICE_ATTR_RW(led);
6. 创建OUT和IN URB的完成回调。中断OUT完成回调仅仅检查URB状态并返回。中断IN完成回调检查URB状态,然后读取ibuffer以了解从PIC32MX板的S1开关接收到的状态,最后重新提交中断IN URB。
static void led_urb_out_callback(struct urb *urb) { struct usb_led *dev; dev = urb->context; /* sync/async unlink faults aren't errors */ if (urb->status) { if (!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) dev_err(&dev->udev->dev, "%s - nonzero write status received: %d\n", __func__, urb->status);} } static void led_urb_in_callback(struct urb *urb) { int retval; struct usb_led *dev; dev = urb->context; if (urb->status) { if (!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) dev_err(&dev->udev->dev, "%s - nonzero write status received: %d\n", __func__, urb->status); } if (dev->ibuffer == 0x00) pr_info ("switch is ON.\n"); else if (dev->ibuffer == 0x01) pr_info ("switch is OFF.\n"); else pr_info ("bad value received\n"); usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); }
7. 添加一个struct usb_driver结构,它将被注册到USB核心:
static struct usb_driver led_driver = { .name = "usbled", .probe = led_probe, .disconnect = led_disconnect, .id_table = id_table, };
7. 用USB总线注册你的驱动:
module_usb_driver(led_driver);
8. 构建模块并将其加载到目标处理器。
2.2 “USB LED and Switch”驱动程序源代码(usb_urb_int_ledc)
#include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h> #define USBLED_VENDOR_ID 0x04D8 #define USBLED_PRODUCT_ID 0x003F static void led_urb_out_callback(struct urb *urb); static void led_urb_in_callback(struct urb *urb); /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { { USB_DEVICE(USBLED_VENDOR_ID, USBLED_PRODUCT_ID) }, { } }; MODULE_DEVICE_TABLE(usb, id_table); struct usb_led { struct usb_device *udev; struct usb_interface *intf; struct urb *interrupt_out_urb; struct urb *interrupt_in_urb; struct usb_endpoint_descriptor *interrupt_out_endpoint; struct usb_endpoint_descriptor *interrupt_in_endpoint; u8 irq_data; u8 led_number; u8 ibuffer; int interrupt_out_interval; int ep_in; int ep_out; }; static ssize_t led_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct usb_led *led = usb_get_intfdata(intf); \ \ return sprintf(buf, "%d\n", led->led_number); } static ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { /* interface: related set of endpoints which present a single feature or function to the host */ struct usb_interface *intf = to_usb_interface(dev); struct usb_led *led = usb_get_intfdata(intf); u8 val; int error, retval; dev_info(&intf->dev, "led_store() function is called.\n"); /* transform char array to u8 value */ error = kstrtou8(buf, 10, &val); if (error) return error; led->led_number = val; led->irq_data = val; if (val == 0) dev_info(&led->udev->dev, "read status\n"); else if (val == 1 || val == 2 || val == 3) dev_info(&led->udev->dev, "led = %d\n", led->led_number); else { dev_info(&led->udev->dev, "unknown value %d\n", val); retval = -EINVAL; return retval; } /* send the data out */ retval = usb_submit_urb(led->interrupt_out_urb, GFP_KERNEL); if (retval) { dev_err(&led->udev->dev, "Couldn't submit interrupt_out_urb %d\n", retval); return retval; } return count; } static DEVICE_ATTR_RW(led); static void led_urb_out_callback(struct urb *urb) { struct usb_led *dev; dev = urb->context; dev_info(&dev->udev->dev, "led_urb_out_callback() function is called.\n"); /* sync/async unlink faults aren't errors */ if (urb->status) { if (!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) dev_err(&dev->udev->dev, "%s - nonzero write status received: %d\n", __func__, urb->status); } } static void led_urb_in_callback(struct urb *urb) { int retval; struct usb_led *dev; dev = urb->context; dev_info(&dev->udev->dev, "led_urb_in_callback() function is called.\n"); if (urb->status) { if (!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) dev_err(&dev->udev->dev, "%s - nonzero write status received: %d\n", __func__, urb->status); } if (dev->ibuffer == 0x00) pr_info ("switch is ON.\n"); else if (dev->ibuffer == 0x01) pr_info ("switch is OFF.\n"); else pr_info ("bad value received\n"); retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); if (retval) dev_err(&dev->udev->dev, "Couldn't submit interrupt_in_urb %d\n", retval); } static int led_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *altsetting = intf->cur_altsetting; struct usb_endpoint_descriptor *endpoint; struct usb_led *dev = NULL; int ep; int ep_in, ep_out; int retval, size, res; retval = 0; dev_info(&intf->dev, "led_probe() function is called.\n"); res = usb_find_last_int_out_endpoint(altsetting, &endpoint); if (res) { dev_info(&intf->dev, "no endpoint found"); return res; } ep = usb_endpoint_num(endpoint); /* value from 0 to 15, it is 1 */ size = usb_endpoint_maxp(endpoint); /* Validate endpoint and size */ if (size <= 0) { dev_info(&intf->dev, "invalid size (%d)", size); return -ENODEV; } dev_info(&intf->dev, "endpoint size is (%d)", size); dev_info(&intf->dev, "endpoint number is (%d)", ep); ep_in = altsetting->endpoint[0].desc.bEndpointAddress; ep_out = altsetting->endpoint[1].desc.bEndpointAddress; dev_info(&intf->dev, "endpoint in address is (%d)", ep_in); dev_info(&intf->dev, "endpoint out address is (%d)", ep_out); dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL); if (!dev) return -ENOMEM; dev->ep_in = ep_in; dev->ep_out = ep_out; dev->udev = usb_get_dev(udev); dev->intf = intf; /* allocate int_out_urb structure */ dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_out_urb) goto error_out; /* initialize int_out_urb */ usb_fill_int_urb(dev->interrupt_out_urb, dev->udev, usb_sndintpipe(dev->udev, ep_out), (void *)&dev->irq_data, 1, led_urb_out_callback, dev, 1); /* allocate int_in_urb structure */ dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_in_urb) goto error_out; /* initialize int_in_urb */ usb_fill_int_urb(dev->interrupt_in_urb, dev->udev, usb_rcvintpipe(dev->udev, ep_in), (void *)&dev->ibuffer, 1, led_urb_in_callback, dev, 1); usb_set_intfdata(intf, dev); retval = device_create_file(&intf->dev, &dev_attr_led); if (retval) goto error_create_file; retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); if (retval) { dev_err(&dev->udev->dev, "Couldn't submit interrupt_in_urb %d\n", retval); device_remove_file(&intf->dev, &dev_attr_led); goto error_create_file; } dev_info(&dev->udev->dev,"int_in_urb submitted\n"); return 0; error_create_file: usb_free_urb(dev->interrupt_out_urb); usb_free_urb(dev->interrupt_in_urb); usb_put_dev(udev); usb_set_intfdata(intf, NULL); error_out: kfree(dev); return retval; } static void led_disconnect(struct usb_interface *interface) { struct usb_led *dev; dev = usb_get_intfdata(interface); device_remove_file(&interface->dev, &dev_attr_led); usb_free_urb(dev->interrupt_out_urb); usb_free_urb(dev->interrupt_in_urb); usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); kfree(dev); dev_info(&interface->dev, "USB LED now disconnected\n"); } static struct usb_driver led_driver = { .name = "usbled", .probe = led_probe, .disconnect = led_disconnect, .id_table = id_table, }; module_usb_driver(led_driver); MODULE_DESCRIPTION("This is a led/switch usb controlled module with irq in/out endpoints");
MODULE_LICENSE("GPL");
2.3 usb_urb_int_led.ko使用示例
/* Keep the PIC32MX470 board powered off */
root@raspberrypi:/home# insmod usb_urb_int_led.ko /* load the module */ usb_urb_int_led: loading out-of-tree module taints kernel. usbcore: registered new interface driver usbled /* power now the PIC32MX Curiosity board */ root@raspberrypi:/home# usb 1-1.3: new full-speed USB device number 4 using dwc_otg usb 1-1.3: New USB device found, idVendor=04d8, idProduct=003f, bcdDevice= 1.00 usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 1-1.3: Product: LED_USB HID Demo usb 1-1.3: Manufacturer: Microchip Technology Inc. usbled 1-1.3:1.0: led_probe() function is called. usbled 1-1.3:1.0: endpoint size is (64) usbled 1-1.3:1.0: endpoint number is (1) usbled 1-1.3:1.0: endpoint in address is (129)usbled 1-1.3:1.0: endpoint out address is (1) usb 1-1.3: int_in_urb submitted /* Go to the new created USB device */ root@raspberrypi:/home# cd /sys/bus/usb/devices/1-1.3:1.0 /* Switch on the LED1 of the PIC32MX Curiosity board */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 1 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: led = 1 usb 1-1.3: led_urb_out_callback() function is called. /* Switch on the LED2 of the PIC32MX Curiosity board */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 2 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: led = 2 usb 1-1.3: led_urb_out_callback() function is called. /* Switch on the LED3 of the PIC32MX Curiosity board */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 3 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: led = 3 usb 1-1.3: led_urb_out_callback() function is called. /* Keep pressed the S1 switch of PIC32MX Curiosity board and get SW status*/ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 0 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: read status usb 1-1.3: led_urb_out_callback() function is called. usb 1-1.3: led_urb_in_callback() function is called. switch is ON. /* Release the S1 switch of PIC32MX Curiosity board and get SW status */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# echo 0 > led usbled 1-1.3:1.0: led_store() function is called. usb 1-1.3: read status usb 1-1.3: led_urb_out_callback() function is called. usb 1-1.3: led_urb_in_callback() function is called. switch is OFF. root@raspberrypi:/home# rmmod usb_urb_int_led.ko /* remove the module */ usbcore: deregistering interface driver usbled usb 1-1.3: led_urb_in_callback() function is called. switch is OFF. usb 1-1.3: Couldn't submit interrupt_in_urb -1 usbled 1-1.3:1.0: USB LED now disconnected
三、"I2C to USB Multidisplay LED"驱动模块
在本实验中,您将编写一个Linux USB驱动程序,该驱动程序由用户空间控制,使用I2C Tools for Linux; 要执行此任务,您必须在已创建的USB驱动程序中创建一个新的I2C适配器。
驱动程序模型是递归的。在下面的内容中,你可以看到集成了USB转I2C转换器的PCI board,控制I2C设备所需的所有驱动程序。以下是创建这个递归驱动程序模型的主要步骤:
- 首先,您必须开发一个PCI设备驱动程序,它将创建一个USB适配器(PCI设备驱动程序是USB适配器驱动程序的父驱动程序)。
- 其次,你必须开发一个USB设备驱动程序,通过USB核心将USB数据发送到USB适配器驱动程序;这个USB设备驱动程序还将创建一个I2C适配器驱动程序(USB设备驱动程序是I2C适配器驱动程序的父驱动程序)。
- 最后,您将创建一个I2C设备驱动程序,它将通过I2C核心将数据发送到I2C适配器驱动程序,并将创建一个struct file_operations结构来定义驱动程序的函数,这些函数在Linux用户空间读取和写入字符设备时被调用。
这个递归模型将在本实验的驱动程序中得到简化,您只需要执行前面提到的三个步骤中的第二步。在这个驱动程序中,主机和设备之间的通信是通过使用中断OUT URB异步完成的。
本实验中,还需要一个LTC3206开发板,LTC3206 DC749A - Demo Board (http://www.analog.com/en/designcenter/evaluation-hardware-and-software/evaluation-boards-kits/dc749a.html)。将LTC3206开发板通过i2c总线连接到PIC32MX470开发板上。
3.1 “I2C to USB Multidisplay LED”驱动模块的代码描述
驱动程序的主要代码部分如下:
1. 包含的函数头文件:
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/i2c.h>
2. 创建ID Table支持热插拔。Vendor ID和Product ID值必须与PIC32MX USB HID设备中使用的值匹配。
#define USBLED_VENDOR_ID 0x04D8 #define USBLED_PRODUCT_ID 0x003F /* table of devices that work with this driver */ static const struct usb_device_id id_table[] = { { USB_DEVICE(USBLED_VENDOR_ID, USBLED_PRODUCT_ID) }, { } }; MODULE_DEVICE_TABLE(usb, id_table);
3.创建一个私有结构来存储驱动程序的数据。
struct i2c_ltc3206 { u8 obuffer[LTC3206_OUTBUF_LEN]; /* USB write buffer */ /* I2C/SMBus data buffer */ u8 user_data_buffer[LTC3206_I2C_DATA_LEN]; /* LEN is 3 bytes */ int ep_out; /* out endpoint */ struct usb_device *usb_dev; /* the usb device for this device */ struct usb_interface *interface;/* the interface for this device */ struct i2c_adapter adapter; /* i2c related things */ /* wq to wait for an ongoing write */ wait_queue_head_t usb_urb_completion_wait; bool ongoing_usb_ll_op; /* all is in progress */ struct urb *interrupt_out_urb; /* interrupt out URB */ };
4. 请参阅下面probe()例程的摘录,包含驱动程序的主要代码行和注释。
static int ltc3206_probe(struct usb_interface *interface, const struct usb_device_id *id) { /* Get the current altsetting of the USB interface */ struct usb_host_interface *hostif = interface->cur_altsetting; struct i2c_ltc3206 *dev; /* the data structure */ /* allocate data memory for our USB device and initialize it */ kzalloc(sizeof(*dev), GFP_KERNEL); /* get interrupt ep_out address */ dev->ep_out = hostif->endpoint[1].desc.bEndpointAddress; dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; /* declare dynamically a wait queue */ init_waitqueue_head(&dev->usb_urb_completion_wait); /* save our data pointer in this USB interface device */ usb_set_intfdata(interface, dev); /* setup I2C adapter description */ dev->adapter.owner = THIS_MODULE; dev->adapter.class = I2C_CLASS_HWMON; dev->adapter.algo = <c3206_usb_algorithm; i2c_set_adapdata(&dev->adapter, dev); /* Attach the I2C adapter to the USB interface */ dev->adapter.dev.parent = &dev->interface->dev; /* initialize the I2C device */ ltc3206_init(dev); /* and finally attach the adapter to the I2C layer */ i2c_add_adapter(&dev->adapter); return 0; }
5. 编写ltc3206_init()函数。在这个函数中,您将分配和初始化中断OUT URB,它用于主机和设备之间的通信。请看下面ltc3206_init()例程的摘录:
static int ltc3206_init(struct i2c_ltc3206 *dev) { /* allocate int_out_urb structure */ interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); /* initialize int_out_urb structure */ usb_fill_int_urb(dev->interrupt_out_urb, dev->usb_dev, usb_sndintpipe(dev->usb_dev, dev->ep_out), (void *)&dev->obuffer, LTC3206_OUTBUF_LEN, ltc3206_usb_cmpl_cbk, dev, 1); return 0; }
6. 创建一个表示I2C传输方法的i2c_algorithm结构体。你将在这个结构中初始化两个变量:
- master_xfer: 向msgs数组定义的给定i2c适配器发出一组i2c事务,其中num个消息可通过adap指定的适配器传输。
- functionality: 根据I2C_FUNC_*标志,返回此algorithm/adapter对支持的标志。
static const struct i2c_algorithm ltc3206_usb_algorithm = { .master_xfer = ltc3206_usb_i2c_xfer, .functionality = ltc3206_usb_func, };
7. 编写ltc3206_usb_i2c_xfer()函数。每次从Linux用户空间向I2C适配器写入数据时,都会调用此函数。该函数将调用ltc32016_i2c_write(),将从Linux用户空间接收到的I2C数据存储在obuffer[ ] char数组中,然后ltc32016_i2c_write()将调用ltc3206_ll_cmd(),将中断OUT URB提交给USB设备并等待URB的完成。
static int ltc3206_usb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { /* get the private data structure */ struct i2c_ltc3206 *dev = i2c_get_adapdata(adap); struct i2c_msg *pmsg; int ret, count; pr_info("number of i2c msgs is = %d\n", num); for (count = 0; count < num; count++) { pmsg = &msgs[count]; ret = ltc3206_i2c_write(dev, pmsg); if (ret < 0) goto abort; } /* if all the messages were transferred ok, return "num" */ ret = num; abort: return ret; } static int ltc3206_i2c_write(struct i2c_ltc3206 *dev, struct i2c_msg *pmsg) { u8 ucXferLen; int rv; u8 *pSrc, *pDst; /* I2C write lenght */ ucXferLen = (u8)pmsg->len; pSrc = &pmsg->buf[0]; pDst = &dev->obuffer[0]; memcpy(pDst, pSrc, ucXferLen); pr_info("oubuffer[0] = %d\n", dev->obuffer[0]); pr_info("oubuffer[1] = %d\n", dev->obuffer[1]); pr_info("oubuffer[2] = %d\n", dev->obuffer[2]); rv = ltc3206_ll_cmd(dev); if (rv < 0) return -EFAULT; return 0; } static int ltc3206_ll_cmd(struct i2c_ltc3206 *dev) { int rv; /* tell everybody to leave the URB alone * we are going to write to the LTC3206 */ dev->ongoing_usb_ll_op = 1; /* doing USB communication */ /* submit the interrupt out ep packet */ if (usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL)) { dev_err(&dev->interface->dev, "ltc3206(ll): usb_submit_urb intr out failed\n"); dev->ongoing_usb_ll_op = 0; return -EIO; } /* wait for its completion, the USB URB callback will signal it */ rv = wait_event_interruptible(dev->usb_urb_completion_wait, (!dev->ongoing_usb_ll_op)); if (rv < 0) { dev_err(&dev->interface->dev, "ltc3206(ll): wait interrupted\n"); goto ll_exit_clear_flag; } return 0; ll_exit_clear_flag: dev->ongoing_usb_ll_op = 0; return rv; }
8. 创建中断OUT URB的完成回调。完成回调检查URB状态,如果存在错误状态,则重新提交URB。如果传输成功,回调将唤醒休眠进程并返回。
static void ltc3206_usb_cmpl_cbk(struct urb *urb) { struct i2c_ltc3206 *dev = urb->context; int status = urb->status; int retval; switch (status) { case 0: /* success */ break; case -ECONNRESET: /* unlink */ case -ENOENT: case -ESHUTDOWN: return; /* -EPIPE: should clear the halt */ default: /* error */ goto resubmit; } /* * wake up the waiting function * modify the flag indicating the ll status */ dev->ongoing_usb_ll_op = 0; /* communication is OK */ wake_up_interruptible(&dev->usb_urb_completion_wait); return; resubmit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) { dev_err(&dev->interface->dev, "ltc3206(irq): can't resubmit intrerrupt urb, retval %d\n", retval); } }
9. 添加一个usb_driver结构,它将被注册到USB核心:
static struct usb_driver ltc3206_driver = { .name = DRIVER_NAME, .probe = ltc3206_probe, .disconnect = ltc3206_disconnect, .id_table = ltc3206_table, };
10. 用USB总线注册你的驱动程序:
module_usb_driver(ltc3206_driver);
11. 构建模块并将其加载到目标处理器。
3.2 "I2C to USB Multidisplay LED"驱动源码(usb_ltc3206.c)
#include <linux/module.h> #include <linux/slab.h> #include <linux/usb.h> #include <linux/i2c.h> /* i2cset -y 4 0x1b 0x00 0xf0 0x00 i -> this is a full I2C block write blue and toggle the leds i2cset -y 4 0x1b 0xf0 0x00 0x00 i -> red full i2cset -y 4 0x1b 0x10 0x00 0x00 i -> red low i2cset -y 4 0x1b 0x00 0x0f 0x00 i -> green full i2cset -y 4 0x1b 0x00 0x0f 0x0f i -> sub and green full i2cset -y 4 0x1b 0x00 0x00 0xf0 i -> main full */ #define DRIVER_NAME "usb-ltc3206" #define USB_VENDOR_ID_LTC3206 0x04d8 #define USB_DEVICE_ID_LTC3206 0x003f #define LTC3206_OUTBUF_LEN 3 /* USB write packet length */ #define LTC3206_I2C_DATA_LEN 3 /* Structure to hold all of our device specific stuff */ struct i2c_ltc3206 { u8 obuffer[LTC3206_OUTBUF_LEN]; /* USB write buffer */ /* I2C/SMBus data buffer */ u8 user_data_buffer[LTC3206_I2C_DATA_LEN]; int ep_out; /* out endpoint */ struct usb_device *usb_dev; /* the usb device for this device */ struct usb_interface *interface;/* the interface for this device */ struct i2c_adapter adapter; /* i2c related things */ /* wq to wait for an ongoing write */ wait_queue_head_t usb_urb_completion_wait; bool ongoing_usb_ll_op; /* all is in progress */ struct urb *interrupt_out_urb; }; /* * Return list of I2C supported functionality */ static u32 ltc3206_usb_func(struct i2c_adapter *a) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; } /* usb out urb callback function */ static void ltc3206_usb_cmpl_cbk(struct urb *urb) { struct i2c_ltc3206 *dev = urb->context; int status = urb->status; int retval; switch (status) { case 0: /* success */ break; case -ECONNRESET: /* unlink */ case -ENOENT: case -ESHUTDOWN: return; /* -EPIPE: should clear the halt */ default: /* error */ goto resubmit; } /* * wake up the waiting function * modify the flag indicating the ll status */ dev->ongoing_usb_ll_op = 0; /* communication is OK */ wake_up_interruptible(&dev->usb_urb_completion_wait); return; resubmit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) { dev_err(&dev->interface->dev, "ltc3206(irq): can't resubmit intrerrupt urb, retval %d\n", retval); } } static int ltc3206_ll_cmd(struct i2c_ltc3206 *dev) { int rv; /* * tell everybody to leave the URB alone * we are going to write to the LTC3206 */ dev->ongoing_usb_ll_op = 1; /* doing USB communication */ /* submit the interrupt out ep packet */ if (usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL)) { dev_err(&dev->interface->dev, "ltc3206(ll): usb_submit_urb intr out failed\n"); dev->ongoing_usb_ll_op = 0; return -EIO; } /* wait for its completion, the USB URB callback will signal it */ rv = wait_event_interruptible(dev->usb_urb_completion_wait, (!dev->ongoing_usb_ll_op)); if (rv < 0) { dev_err(&dev->interface->dev, "ltc3206(ll): wait interrupted\n"); goto ll_exit_clear_flag; } return 0; ll_exit_clear_flag: dev->ongoing_usb_ll_op = 0; return rv; } static int ltc3206_init(struct i2c_ltc3206 *dev) { int ret; /* initialize the LTC3206 */ dev_info(&dev->interface->dev, "LTC3206 at USB bus %03d address %03d -- ltc3206_init()\n", dev->usb_dev->bus->busnum, dev->usb_dev->devnum); dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_out_urb){ ret = -ENODEV; goto init_error; } usb_fill_int_urb(dev->interrupt_out_urb, dev->usb_dev, usb_sndintpipe(dev->usb_dev, dev->ep_out), (void *)&dev->obuffer, LTC3206_OUTBUF_LEN, ltc3206_usb_cmpl_cbk, dev, 1); ret = 0; goto init_no_error; init_error: dev_err(&dev->interface->dev, "ltc3206_init: Error = %d\n", ret); return ret; init_no_error: dev_info(&dev->interface->dev, "ltc3206_init: Success\n"); return ret; } static int ltc3206_i2c_write(struct i2c_ltc3206 *dev, struct i2c_msg *pmsg) { u8 ucXferLen; int rv; u8 *pSrc, *pDst; if (pmsg->len > LTC3206_I2C_DATA_LEN) { pr_info ("problem with the lenght\n"); return -EINVAL; } /* I2C write lenght */ ucXferLen = (u8)pmsg->len; pSrc = &pmsg->buf[0]; pDst = &dev->obuffer[0]; memcpy(pDst, pSrc, ucXferLen); pr_info("oubuffer[0] = %d\n", dev->obuffer[0]); pr_info("oubuffer[1] = %d\n", dev->obuffer[1]); pr_info("oubuffer[2] = %d\n", dev->obuffer[2]); rv = ltc3206_ll_cmd(dev); if (rv < 0) return -EFAULT; return 0; } /* device layer */ static int ltc3206_usb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct i2c_ltc3206 *dev = i2c_get_adapdata(adap); struct i2c_msg *pmsg; int ret, count; pr_info("number of i2c msgs is = %d\n", num); for (count = 0; count < num; count++) { pmsg = &msgs[count]; ret = ltc3206_i2c_write(dev, pmsg); if (ret < 0) goto abort; } /* if all the messages were transferred ok, return "num" */ ret = num; abort: return ret; } static const struct i2c_algorithm ltc3206_usb_algorithm = { .master_xfer = ltc3206_usb_i2c_xfer, .functionality = ltc3206_usb_func, }; static const struct usb_device_id ltc3206_table[] = { { USB_DEVICE(USB_VENDOR_ID_LTC3206, USB_DEVICE_ID_LTC3206) }, { } }; MODULE_DEVICE_TABLE(usb, ltc3206_table); static void ltc3206_free(struct i2c_ltc3206 *dev) { usb_put_dev(dev->usb_dev); usb_set_intfdata(dev->interface, NULL); kfree(dev); } static int ltc3206_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_host_interface *hostif = interface->cur_altsetting; struct i2c_ltc3206 *dev; int ret; dev_info(&interface->dev, "ltc3206_probe() function is called.\n"); /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { pr_info("i2c-ltc3206(probe): no memory for device state\n"); ret = -ENOMEM; goto error; } /* get ep_out */ dev->ep_out = hostif->endpoint[1].desc.bEndpointAddress; dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; init_waitqueue_head(&dev->usb_urb_completion_wait); /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); /* setup i2c adapter description */ dev->adapter.owner = THIS_MODULE; dev->adapter.class = I2C_CLASS_HWMON; dev->adapter.algo = <c3206_usb_algorithm; i2c_set_adapdata(&dev->adapter, dev); snprintf(dev->adapter.name, sizeof(dev->adapter.name), DRIVER_NAME " at bus %03d device %03d", dev->usb_dev->bus->busnum, dev->usb_dev->devnum); dev->adapter.dev.parent = &dev->interface->dev; /* initialize ltc3206 i2c device */ ret = ltc3206_init(dev); if (ret < 0) { dev_err(&interface->dev, "failed to initialize adapter\n"); goto error_init; } /* and finally attach to i2c layer */ ret = i2c_add_adapter(&dev->adapter); if (ret < 0) { dev_info(&interface->dev, "failed to add I2C adapter\n"); goto error_i2c; } dev_info(&dev->interface->dev, "ltc3206_probe() -> chip connected -> Success\n"); return 0; error_init: usb_free_urb(dev->interrupt_out_urb); error_i2c: usb_set_intfdata(interface, NULL); ltc3206_free(dev); error: return ret; } static void ltc3206_disconnect(struct usb_interface *interface) { struct i2c_ltc3206 *dev = usb_get_intfdata(interface); i2c_del_adapter(&dev->adapter); usb_kill_urb(dev->interrupt_out_urb); usb_free_urb(dev->interrupt_out_urb); usb_set_intfdata(interface, NULL); ltc3206_free(dev); pr_info("i2c-ltc3206(disconnect) -> chip disconnected"); } static struct usb_driver ltc3206_driver = { .name = DRIVER_NAME, .probe = ltc3206_probe, .disconnect = ltc3206_disconnect, .id_table = ltc3206_table, }; module_usb_driver(ltc3206_driver); MODULE_DESCRIPTION("This is a usb controlled i2c ltc3206 device"); MODULE_LICENSE("GPL");
3.3 usb_ltc3206.ko使用示例
/* Keep the PIC32MX470 board powered off */ /* check the i2c adapters of the Raspberry Pi 4 Model B board */ root@raspberrypi:/home# i2cdetect -l i2c-1 i2c bcm2835 (i2c@7e804000) I2C adapter root@raspberrypi:/home# insmod usb_ltc3206.ko /* load the module */ usb_ltc3206: loading out-of-tree module taints kernel. usbcore: registered new interface driver usb-ltc3206 /* power now the PIC32MX Curiosity board */ root@raspberrypi:/home# usb 1-1.3: new full-speed USB device number 4 using dwc_otg usb 1-1.3: New USB device found, idVendor=04d8, idProduct=003f, bcdDevice= 1.00 usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 1-1.3: Product: USB to I2C demo usb 1-1.3: Manufacturer: Microchip Technology Inc. usb-ltc3206 1-1.3:1.0: ltc3206_probe() function is called. usb-ltc3206 1-1.3:1.0: LTC3206 at USB bus 001 address 004 -- ltc3206_init() usb-ltc3206 1-1.3:1.0: ltc3206_init: Success usb-ltc3206 1-1.3:1.0: ltc3206_probe() -> chip connected -> Success /* check again the i2c adapters of the Raspberry Pi 4 Model B board, find the new one */ root@raspberrypi:/home# i2cdetect -l i2c-1 i2c bcm2835 (i2c@7e804000) I2C adapter标签:led,usb,urb,struct,dev,Linux,驱动,USB From: https://www.cnblogs.com/wanglouxiaozi/p/17031041.html
i2c-11 i2c usb-ltc3206 at bus 001 device 004 I2C adapter root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# ls authorized bInterfaceProtocol ep_01 power bAlternateSetting bInterfaceSubClass ep_81 subsystem bInterfaceClass bNumEndpoints i2c-4 supports_autosuspend bInterfaceNumber driver modalias uevent /* * verify the communication between the host and device * these commands toggle the three leds of the PIC32MX board and * set maximum brightness of the LTC3206 LED BLUE */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# i2cset -y 11 0x1b 0x00 0xf0 0x00 i number of i2c msgs is = 1 oubuffer[0] = 0 oubuffer[1] = 240 oubuffer[2] = 0 /* set maximum brightness of the LTC3206 LED RED */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# i2cset -y 11 0x1b 0xf0 0x00 0x00 i /* decrease brightness of the LTC3206 LED RED */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# i2cset -y 11 0x1b 0x10 0x00 0x00 i /* set maximum brightness of the LTC3206 LED GREEN */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# i2cset -y 11 0x1b 0x00 0x0f 0x00 i /* set maximum brightness of the LTC3206 LED GREEN and SUB display */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# i2cset -y 11 0x1b 0x00 0x0f 0x0f i /* set maximum brightness of the LTC3206 MAIN display */ root@raspberrypi:/sys/bus/usb/devices/1-1.3:1.0# i2cset -y 11 0x1b 0x00 0x00 0xf0 i root@raspberrypi:/home# rmmod usb_ltc3206.ko /* remove the module */ usbcore: deregistering interface driver usb-ltc3206 /* Power off the PIC32MX Curiosity board */ root@raspberrypi:/home# i2c-ltc3206(disconnect) -> chip disconnected usb 1-1.3: USB disconnect, device number 4