失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记

嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记

时间:2022-01-04 11:12:35

相关推荐

嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记

Linux USB设备驱动

二十、Linux USB设备驱动20.1 USB简介20.1.1 USB2.0总线拓扑20.1.2 USB总线枚举和设备布局20.1.3 USB数据传输20.1.4 USB设备类别20.1.5 USB描述符20.2 Linux USB 子系统20.3 编写Linux USB设备驱动程序20.3.1 注册设备驱动20.3.2 Linux 主机端数据类型20.3.3 USB请求块 20.4 USB LED模块20.4 USB LED驱动代码20.5 USB LED 和开关模块20.5.1 代码 20.6 连接到USB多显LED的I2C模块20.6.1 简介20.6.2 代码

二十、Linux USB设备驱动

20.1 USB简介

USB(通用串行总线)最早的USB总线速率包括低速(1.5Mbps)、高速(480Mbps),USB 3.0规范出现后,速率达到4.8Gbps。USB最大优点是它支持动态连接和移除,一种称”即插即用“的接口。

20.1.1 USB2.0总线拓扑

20.1.2 USB总线枚举和设备布局

USB主机控制轮询总线,其中所有十五均由USB主机发起。

端点、描述符。

20.1.3 USB数据传输

一旦枚举完成,主机和设备就可以自由地进行通信。

四种不同类型的传输:控制传输;批量数据传输;中断数据传输;等时数据传输;

20.1.4 USB设备类别

常见的有HID、打印机、成像设备、大容量存储设备和通信设备。

20.1.5 USB描述符

设备描述符

配置描述符

接口描述符

端点描述符

20.2 Linux USB 子系统

Linux USB是一类特定API实现,用于支持USB外设和主机控制器。

Linux USB API支持对控制消息和批量消息的同步调用。

20.3 编写Linux USB设备驱动程序

20.3.1 注册设备驱动

注册USB设备驱动程序,usb_driver结构体定义在,如下:

/linux/driver/misc/usbsevseg.c

20.3.2 Linux 主机端数据类型

USB设备驱动程序实际上绑定到接口,而不是绑定到设备。

20.3.3 USB请求块

20.4 USB LED模块

USB HID设备固件,该设备能够使用HID报告来发送和接收数据。

20.4 USB LED驱动代码

#include <linux/slab.h>#include <linux/module.h>#include <linux/usb.h>//创建ID表示支持热插拔#define USBLED_VENDOR_ID0x04D8#define USBLED_PRODUCT_ID0x003F/* 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,};//将驱动注册到USB总线module_usb_driver(led_driver);MODULE_LICENSE("GPL");MODULE_AUTHOR(" ");MODULE_DESCRIPTION("This is a synchronous led usb controlled module");

20.5 USB LED 和开关模块

20.5.1 代码

#include <linux/slab.h>#include <linux/module.h>#include <linux/usb.h>#define USBLED_VENDOR_ID0x04D8#define USBLED_PRODUCT_ID0x003Fstatic 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");elsepr_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_LICENSE("GPL");MODULE_AUTHOR(" ");MODULE_DESCRIPTION("This is a led/switch usb controlled module with irq in/out endpoints");

20.6 连接到USB多显LED的I2C模块

20.6.1 简介

使用芯片LTC3206 I2C 多显LED控制器。

20.6.2 代码

#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 ledsi2cset -y 4 0x1b 0xf0 0x00 0x00 i -> red full i2cset -y 4 0x1b 0x10 0x00 0x00 i -> red lowi2cset -y 4 0x1b 0x00 0x0f 0x00 i -> green fulli2cset -y 4 0x1b 0x00 0x0f 0x0f i -> sub and green fulli2cset -y 4 0x1b 0x00 0x00 0xf0 i -> main full */#define DRIVER_NAME"usb-ltc3206"#define USB_VENDOR_ID_LTC32060x04d8#define USB_DEVICE_ID_LTC32060x003f#define LTC3206_OUTBUF_LEN3/* USB write packet length */#define LTC3206_I2C_DATA_LEN3/* 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;}//分配并初始化用于主机和设备之间通信的中断输出URBstatic 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 = &ltc3206_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_AUTHOR(" ");MODULE_DESCRIPTION("This is a usb controlled i2c ltc3206 device");MODULE_LICENSE("GPL");

感谢阅读,祝君成功!

-by aiziyou

如果觉得《嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。