有了网站源代码,机场建设集团网站,安装了wordpress程序,工业企业在线平台一、驱动类型 USB 驱动开发主要分为两种#xff1a;主机侧的驱动程序和设备侧的驱动程序。一般我们编写的都是主机侧的USB驱动程序。 主机侧驱动程序用于控制插入到主机中的 USB 设备#xff0c;而设备侧驱动程序则负责控制 USB 设备如何与主机通信。由于设备侧驱动程序通常与…一、驱动类型 USB 驱动开发主要分为两种主机侧的驱动程序和设备侧的驱动程序。一般我们编写的都是主机侧的USB驱动程序。 主机侧驱动程序用于控制插入到主机中的 USB 设备而设备侧驱动程序则负责控制 USB 设备如何与主机通信。由于设备侧驱动程序通常与设备功能紧密相关因此常常被称为 USB gadget 驱动程序。USB gadget 驱动程序的作用是定义如何通过 USB 协议栈与主机端进行通信确保设备能够正确响应主机的请求。 在 USB 系统中主机侧和设备侧有各自不同的控制器。主机侧使用的是主机控制器Host Controller它负责在主机和设备之间建立数据传输连接管理设备的连接和断开。设备侧则使用 USB 设备控制器UDC它用于控制设备如何响应主机的请求和进行数据传输。主机控制器和设备控制器分别在各自的系统中充当着至关重要的角色确保 USB 设备和主机之间的有效通信。 这两种驱动程序通过操作系统中的 USB 子系统进行协同工作提供了广泛的设备支持从键盘、鼠标等简单外设到复杂的存储设备、音频设备等多种类型的 USB 设备。
二、USB传输介质-URB USB通信和IIC类似都要先构建数据包然后使用对应的API函数进行传输。URB就是USB传输的介质。URBUSB请求块是Linux内核中用于管理USB数据传输的结构体。在Linux中USB数据传输的核心就是通过URB来进行的它相当于I2C中的数据包封装承担着数据传输的“容器”角色。URB用于描述一次USB传输的请求包括传输方向、数据长度、目标端点等信息。
1. 根据数据传输的方式和协议类型URB有不同的类型。
1控制传输URB用于管理设备控制请求如设备的初始化、配置等。使用usb_fill_control_urb()来填充控制传输的URB。
2批量传输URB用于较大数据量的传输通常用于数据的读取和写入。使用usb_fill_bulk_urb()来填充批量传输URB。
3等时传输URB用于对实时性要求较高的传输如音频、视频流等。使用usb_fill_int_urb()来填充等时传输URB。
4中断传输URB用于短数据的周期性传输如键盘、鼠标等设备。使用usb_fill_int_urb()来填充中断传输URB。
2. 结构体如下所示。
struct urb {struct list_head urb_list; // 用于管理URB队列的链表unsigned int pipe; // 传输通道即端点void *context; // 用户定义的上下文用于回调函数中传递信息unsigned char *transfer_buffer; // 数据缓冲区指针指向传输数据的内存dma_addr_t transfer_dma; // 用于DMA传输的物理地址unsigned int transfer_flags; // 标志指示URB的某些属性例如同步、异步等unsigned int status; // 传输状态成功或失败unsigned int actual_length; // 实际传输的字节数unsigned int number_of_packets; // 分包传输时的数据包数unsigned int timeout; // 传输超时时间毫秒unsigned int start_frame; // 传输开始的帧号unsigned int interval; // 传输间隔时间仅对中断传输有效unsigned char *setup_packet; // 指向控制传输的请求包如果是控制传输时struct urb *next; // 下一个URB供链表使用unsigned int transfer_buffer_length; // 缓冲区的长度即传输数据的最大字节数struct usb_device *dev; // USB设备指针指向传输目标设备void (*complete)(struct urb *urb); // 传输完成后的回调函数struct mutex *lock; // 用于同步URB访问的互斥锁unsigned int pipe_flags; // 端点标志描述端点的类型和特性
};3. 相关API。
1构建URB对象。 返回一个指向分配的URB对象的指针。如果分配失败返回 NULL。
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
/*
iso_packets表示如果URB是用于等时传输这个参数指定URB包含的数据包数量。如果不是等时传输此参数应为0。
mem_flags内存分配标志通常使用 GFP_KERNEL 来分配内存。
*/
2释放一个URB对象。
void usb_free_urb(struct urb *urb);3填充控制传输URB。
void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,unsigned int pipe, unsigned char *setup_packet,void *transfer_buffer, int buffer_length,usb_complete_t complete_fn, void *context);
/*urb要填充的URB对象。dev目标USB设备。pipeUSB管道端点可以通过usb_sndctrlpipe()和usb_rcvctrlpipe()等函数获取。setup_packet指向控制请求的setup包指针包含请求的控制信息如请求码、值、索引等。transfer_buffer指向传输数据缓冲区的指针。如果是控制传输的输入数据数据会存储在这 里输出数据也通过此缓冲区发送。buffer_length传输缓冲区的长度。complete_fn传输完成后的回调函数。context用于回调函数的用户定义的上下文数据。可以在回调函数中使用。
*/4填充批量传输URB。
void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,unsigned int pipe, void *transfer_buffer,int buffer_length, usb_complete_t complete_fn,void *context);
/*urb要填充的URB对象。dev目标USB设备。pipeUSB管道端点可以通过usb_sndbulkpipe()和usb_rcvbulkpipe()等函数获取。transfer_buffer指向传输数据缓冲区的指针。buffer_length传输缓冲区的长度。complete_fn传输完成后的回调函数。context用于回调函数的用户定义的上下文数据。
*/ 5填充中断传输URB。
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,unsigned int pipe, void *transfer_buffer,int buffer_length, usb_complete_t complete_fn,void *context, unsigned int interval);
/*urb要填充的URB对象。dev目标USB设备。pipeUSB管道端点可以通过usb_sndintpipe()和usb_rcvintpipe()等函数获取。transfer_buffer指向传输数据缓冲区的指针。buffer_length传输缓冲区的长度。complete_fn传输完成后的回调函数。context用于回调函数的用户定义的上下文数据。interval传输间隔单位为帧适用于中断传输。
*/ 6提交URB进行传输。 如果提交成功返回 0如果失败返回一个负数错误码。
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
/*urb要提交的URB对象。mem_flags内存分配标志通常使用 GFP_KERNEL。
*/
7取消一个URB的传输。
void usb_kill_urb(struct urb *urb);三、USB主机侧驱动开发键盘驱动
1. 操作流程。
1编写USB驱动框架。
2完善probe函数。
3在退出函数中释放掉内存以及相关注销。 2. 编写USB驱动框架。
①在函数入口函数中注册usb驱动在出口函数中注销usb驱动。
②填充usb驱动信息例如名称、probe函数等。
#include linux/module.h
#include linux/usb.h
#include linux/init.h
//设备连接时执行
static int my_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) {return 0;
}//设备断开时执行
static void my_usb_disconnect(struct usb_interface *intf) {}static const struct usb_device_id my_usb_id_table[] {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD) },{ }, // 空结构体表示数组结束
};static struct usb_driver my_usb_driver {.name my_usb,.probe my_usb_probe,.disconnect my_usb_disconnect,.id_table my_usb_id_table,
};static int my_usb_init(void) {int ret usb_register(my_usb_driver);return 0;
}static void my_usb_exit(void) {usb_deregister(my_usb_driver);
}module_init(my_usb_init);
module_exit(my_usb_exit);
MODULE_LICENSE(GPL);
3. 完善probe函数。
①由于键盘属于输入设备则在probe函数中先为输入子系统开辟空间并填充信息。
②设置键盘的按键事件以及键值。
③注册输入子系统到驱动。
④构建URB对象开辟空间并填充中断传输URB函数中的参数。
⑤提交URB对象用于数据传输。
⑥在填充URB的回调函数中上报键盘按键事件。
⑦在退出函数中释放内存。
#include linux/init.h
#include linux/module.h
#include linux/usb.h
#include linux/usb/input.h
#include linux/hid.h
#include linux/slab.hstruct input_dev *myusb_inputdev NULL; // 输入设备
struct urb *myusb_urb NULL; // USB请求块URB
unsigned char *myusb_buf NULL; // 数据缓冲区
unsigned char myusb_buf_old[8]; // 数据缓冲区int myusb_size 0; // 缓冲区大小
dma_addr_t myusb_dma; // DMA地址// 键盘的键码表包含标准键盘的按键映射
static const unsigned char usb_keyboardcode[256] {0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,3, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2,4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,65, 66, 67, 68, 87, 88, 99, 70, 119, 110, 102, 104, 111, 107, 109, 106,105, 108, 103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,72, 73, 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190,191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135, 136, 113,115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94, 95, 0, 0, 0,122, 123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115,114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140
};//回调函数执行的条件是 URB传输完成无论是成功还是失败。
static void myusb_func(struct urb *urb) //填充中断URB的回调函数
{//上报特殊按键for (i 0; i 8; i) {input_report_key(myusb_inputdev, usb_keyboardcode[i 224], (myusb_buf[0] i) 1);}//上报普通按键for (i 2; i 8; i) {if (myusb_buf_old[i] 3 memscan(myusb_buf 2, myusb_buf_old[i], 6) myusb_buf 8) {if (usb_keyboardcode[myusb_buf_old[i]]) {input_report_key(myusb_inputdev, usb_keyboardcode[myusb_buf_old[i]], 0);}}if (myusb_buf[i] 3 memscan(myusb_buf_old 2, myusb_buf[i], 6) myusb_buf_old 8) {if (usb_keyboardcode[myusb_buf[i]]) {input_report_key(myusb_inputdev, usb_keyboardcode[myusb_buf[i]], 1);}}}input_sync(myusb_inputdev); //同步事件即上报完毕。memcpy(myusb_buf_old, myusb_buf, 8); //myusb_buf复制给myusb_buf_oldusb_submit_urb(myusb_urb , GFP_ATOMIC); //提交URB
}// 设备连接时执行
static int my_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) {int i;int ret;struct usb_device *myusb_dev interface_to_usbdev(intf); // 获取USB设备指针struct usb_endpoint_descriptor *endpoint;// 1. 为输入设备分配内存myusb_inputdev input_allocate_device();if (!myusb_inputdev) {pr_err(Failed to allocate input device\n);return -ENOMEM;}myusb_inputdev-name myusb_input; // 设置设备名称// 2. 设置事件类型按键事件和重复事件set_bit(EV_KEY, myusb_inputdev-evbit);set_bit(EV_REP, myusb_inputdev-evbit);for (i 0; i 255; i) { // 设置按键位图set_bit(usb_keyboardcode[i], myusb_inputdev-keybit);}clear_bit(0, myusb_inputdev-keybit); // 清除无效的按键位// 3. 注册输入设备ret input_register_device(myusb_inputdev);if (ret) {input_free_device(myusb_inputdev);return ret;}
// 4. URB分配内存myusb_urb usb_alloc_urb(0, GFP_KERNEL); // 分配一个URBISO数据包数为0内存分配标志为GFP_KERNEL// 获取端点描述符并获取端点最大数据包大小endpoint intf-cur_altsetting-endpoint[0].desc;myusb_size endpoint-wMaxPacketSize;// 分配一致性内存缓冲区用于数据传输myusb_buf usb_alloc_coherent(myusb_dev, myusb_size, GFP_ATOMIC, myusb_dma);// 获取接收中断管道unsigned int pipe usb_rcvintpipe(myusb_dev, endpoint-bEndpointAddress);// 填充URBusb_fill_int_urb(myusb_urb, myusb_dev, pipe, myusb_buf, myusb_size, myusb_func, 0, endpoint-bInterval);// 5. 提交URB进行数据传输ret usb_submit_urb(myusb_urb, GFP_KERNEL);mykbd_urb-transfer_dma mykbd_dma; // 设置URB的DMA地址mykbd_urb-transfer_flags | URB_NO_TRANSFER_DMA_MAP; // 设置URB的标志不使用DMA映射return 0;
}// 设备断开时执行
void myusb_disconnect(struct usb_interface *intf) {usb_kill_urb(myusb_urb); // 取消URB传输usb_free_urb(myusb_urb); // 释放URBusb_free_coherent(myusb_dev, myusb_size, myusb_buf, myusb_dma); // 释放一致性内存input_unregister_device(myusb_inputdev); // 注销输入设备input_free_device(myusb_inputdev); // 释放输入设备
} 问题1上述代码中按键数组中的数据代表什么呢 答如下图所示不同的键值对应不同的按键数组中的0元素就是保留位也就是未分配的值。 问题2URB回调函数中的代码是什么意思 答是用于上报键盘上各个按键事件。USB每次传输都是8个字节的数据对于键盘来说这8个字节的数据分别对应下面的按键类型。 如下面两张图所示 这张图是USB键盘按下后读取的8个字节的值。 下面这张图是读取的8个字节的值的解析。 4.验证
1 取消开发板上之前默认的USB驱动键盘。将内核源码重新配置编译到开发板。 2将自己写的驱动的ko文件上传至开发板并将USB键盘连接开发板。
3使用命令exec 0/dev/ttyl按下按键如果没有反应则证明此时没有键盘驱动了。由于按键现在用不了所以重新启动开发板即可。
4安装ko驱动模块然后重新使用命令exec 0/dev/ttyl这时按下按键就会有响应如下所示 五、USB设备侧驱动开发
31.Gadget驱动把开发板模拟成USB网卡_哔哩哔哩_bilibili