RK3568平台(USB篇)USB设备驱动

一.USB子系统

在介绍设备端驱动前,我们先来看看 Linux USB子系统。这里的子系统是相对于整个Linux kernel 来说的,而非单一设备。从整体概括了USB主机端和设备端的通信框架。

Linux kernel 中早已集成了较为完善的USB协议栈,由于其规模庞大,包含多个类别的设备驱动,所以Linux系统中的USB协议栈也被称为USB子系统。

1.1 主机端

主机端,简化抽象三层:

  • 各种类设备驱动:mass sotrage, CDC, HID等

  • USB 设备驱动:USB 核心处理

  • 主机控制器驱动:不同的USB主机控制器(OHCI/EHCI/UHCI),抽象为HDC。

1.2 设备端

设备端,也抽象为三层:

  • 设备功能驱动:mass sotage , CDC, HID 等,对应主机端的类设备驱动

  • Gadget 设备驱动:中间层,向下直接和UDC通信,建立链接;向上提供通用接口,屏蔽USB请求以及传输细节。

  • 设备控制器驱动:UDC驱动,直接处理USB设备控制器。

USB驱动整体框架

 二.USB描述符

USB设备描述符(usb_device_descriptor)
     USB配置描述符(usb_config_descriptor)
          USB接口描述符(usb_interface_descriptor)
                USB端点描述符(usb_endpoint_descriptor)

一个USB设备描述符中可以有多个配置描述符,即USB设备可以有多种配置;一个配置描述符中可以有多个接口描述符,即USB设备可以支持多种功能(接口);一个接口描述符中可以有多个端点描述符。

设备描述符:

/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
	__u8  bLength; //该结构体大小
	__u8  bDescriptorType; //描述符类型 (此处应为0x01,即设备描述符)
 
	__le16 bcdUSB; //usb版本号 200 -> USB2.0
	__u8  bDeviceClass; //设备类 
	__u8  bDeviceSubClass; //设备类子类
	__u8  bDeviceProtocol; //设备协议,以上三点都是USB官方定义
	__u8  bMaxPacketSize0; //端点0最大包大小 (只能为8,16,32,64)
	__le16 idVendor; //厂家id
	__le16 idProduct; //产品id
	__le16 bcdDevice; //设备出厂编号
	__u8  iManufacturer; //描述厂商信息的字符串描述符的索引值
	__u8  iProduct; //描述产品信息的字串描述符的索引值
	__u8  iSerialNumber; //描述设备序列号信息的字串描述符的索引值 
	__u8  bNumConfigurations; //可能的配置描述符的数目
} __attribute__ ((packed));

配置描述符:

struct usb_config_descriptor {
	__u8  bLength; //该结构体大小
	__u8  bDescriptorType;//描述符类型(本结构体中固定为0x02)  
 
	__le16 wTotalLength; //该配置下,信息的总长度(包括配置,接口,端点和设备类及厂商定义的描述符)
	__u8  bNumInterfaces; //接口的个数
	__u8  bConfigurationValue; //Set_Configuration命令所需要的参数值,用来选定此配置
	__u8  iConfiguration; //描述该配置的字符串描述的索引值 
	__u8  bmAttributes;//供电模式的选择  
	__u8  bMaxPower;//设备从总线提取的最大电流
} __attribute__ ((packed));

 接口描述符:

配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为“功能”更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。

struct usb_interface_descriptor {
	__u8  bLength;		//该结构体大小
	__u8  bDescriptorType;//接口描述符的类型编号(0x04)
 
	__u8  bInterfaceNumber;  //该接口的编号  
	__u8  bAlternateSetting; //备用的接口描述符编号  
	__u8  bNumEndpoints; //该接口使用的端点数,不包括端点0  
	__u8  bInterfaceClass; //接口类
	__u8  bInterfaceSubClass; //子类
	__u8  bInterfaceProtocol; //协议
	__u8  iInterface;//描述此接口的字串描述表的索引值  
} __attribute__ ((packed));

端点描述符:

端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。

除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。

/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
	__u8  bLength;        //端点描述符字节数大小(7个字节)
	__u8  bDescriptorType;//端点描述符类型编号(0x05) 
 
	__u8  bEndpointAddress; //此描述表所描述的端点的地址、方向 : 
                            // bit3~bit0:端点号,bit6~bit4:保留,
                            // bit7:方向,如果是控制端点则忽略,0-输出端点(主机到设备)1-输入端点(设备到主机)
	__u8  bmAttributes; // 端点特性,bit1~bit0 表示传输类型,其他位保留
                        // 00-控制传输  01-实时传输   10-批量传输 11-中断传输
	__le16 wMaxPacketSize;  //端点收、发的最大包大小
	__u8  bInterval; // 中断传输模式中主机查询端点的时间间隔。
                     // 对于实时传输的端点此域必需为1,表示周期为1ms。对于中断传输的端点此域值的范围为1ms到255ms
 
	/* NOTE:  these two are _only_ in audio endpoints. */
	/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
	__u8  bRefresh;
	__u8  bSynchAddress;
} __attribute__ ((packed));

三.USB总线驱动如何识别设备

当USB设备连接到集线器,集线器状态将发生相应的变化,并将状态变化信息传递给USB主机。USB主机通过根集线器向USB设备发送命令,获取USB设备的各种信息,包含USB设备传输类型、ID号、Product、USB速度等信息

USB匹配识别的框架:

 四.USB设备驱动

USB 总线驱动程序,在接入 USB 设备时,会帮我们构造一个新的 usb_dev 注册到“usb_bus_type”里去。这部分是内核做好的。我们要做的是,构造一个 usb_driver 结构体,注册到“usb_bus_type”中去。在“usb_driver”结构体中有“id_table”表示他能支持哪些设备,当 USB 设备能匹配 id_table 中某一个设备时,就会调用“usb_driver”结构体中的“.probe”(自已确定在 probe 中做的事情)等函数,如当拔掉USB 设备时,就会调用其中的“.disconnect”函数。

简单的USB驱动程序示例:

/* drivers\hid\usbhid\usbmouse.c */

static struct input_dev *uk_dev;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;

static struct usb_device_id usbmouse_as_key_id_table [] = {	//id_table支持的设备
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE) },
	//{USB_DEVICE(0x1234,0x5678)},	直接指定厂家ID和设备ID
	{ }	/* Terminating entry */
};

static void usbmouse_as_key_irq(struct urb *urb){
	static unsigned char pre_val;
	/* USB鼠标数据含义
	 * data[0]: bit0-左键, 1-按下, 0-松开
	 *          bit1-右键, 1-按下, 0-松开
	 *          bit2-中键, 1-按下, 0-松开 
     */
	if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0))){
		/* 左键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0);
		input_sync(uk_dev);
	}
	if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1))){
		/* 右键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0);
		input_sync(uk_dev);
	}
	if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2))){
		/* 中键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0);
		input_sync(uk_dev);
	}	
	pre_val = usb_buf[0];

	/* 重新提交urb */
	usb_submit_urb(uk_urb, GFP_KERNEL);
}
static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id){
	struct usb_device *dev = interface_to_usbdev(intf);	
	//通过USB接口得到usb_device结构体,里面有描述符
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;
	int pipe;
	
	interface = intf->cur_altsetting;
	endpoint = &interface->endpoint[0].desc;

	/* a. 分配一个input_dev */
	uk_dev = input_allocate_device();		
	/* b. 设置 */
	/* b.1 能产生哪类事件 */
	set_bit(EV_KEY, uk_dev->evbit);		//按键类
	set_bit(EV_REP, uk_dev->evbit);		//重复类
	/* b.2 能产生哪些事件 */
	set_bit(KEY_L, uk_dev->keybit);
	set_bit(KEY_S, uk_dev->keybit);
	set_bit(KEY_ENTER, uk_dev->keybit);	
	/* c. 注册 */
	input_register_device(uk_dev);
	
	/* d. 硬件相关操作 */
	/* 数据传输3要素: 源,目的,长度 */
	/* 源: USB设备的某个端点 */
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
	/* 长度: */
	len = endpoint->wMaxPacketSize;		//端点描述符最大包大小
	/* 目的: */
	usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);	//分配缓冲区

	/* 使用"3要素" */
	/* 分配usb request block */
	uk_urb = usb_alloc_urb(0, GFP_KERNEL);	
	/* 使用"3要素设置urb" */
	usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
	uk_urb->transfer_dma = usb_buf_phys;
	uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	/* 使用URB */
	usb_submit_urb(uk_urb, GFP_KERNEL);	
	return 0;
}

static void usbmouse_as_key_disconnect(struct usb_interface *intf){
	struct usb_device *dev = interface_to_usbdev(intf);
	usb_kill_urb(uk_urb);
	usb_free_urb(uk_urb);
	usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
	input_unregister_device(uk_dev);
	input_free_device(uk_dev);
}
/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
	.name		= "usbmouse_as_key_",
	.probe		= usbmouse_as_key_probe,
	.disconnect	= usbmouse_as_key_disconnect,
	.id_table	= usbmouse_as_key_id_table,
};

static int usbmouse_as_key_init(void){
	usb_register(&usbmouse_as_key_driver);	// 2. 注册 usb_driver
	return 0;
}

static void usbmouse_as_key_exit(void){
	usb_deregister(&usbmouse_as_key_driver);	//	卸载usb_driver
}

module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);
MODULE_LICENSE("GPL");

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对于RK3568芯片,USB驱动的安装可以按照以下步骤进行: 1. 下载USB驱动程序:首先,你需要在RK3568芯片的官方网站或相关厂商的支持网站上下载适用于RK3568USB驱动程序。确保下载的驱动程序与你的操作系统版本相匹配。 2. 解压驱动程序:下载完驱动程序后,将其解压缩到一个临时文件夹中。 3. 连接设备:将RK3568设备通过USB数据线连接到计算机上,并确保设备处于正常的工作状态。 4. 安装驱动程序:打开设备管理器(在Windows系统中,可以通过右键点击“我的电脑”或“此电脑”,选择“管理”,然后在左侧导航栏中找到“设备管理器”),找到与RK3568设备相关的项(可能标记为未知设备或其他类别),右键点击它,选择“更新驱动程序软件”。 5. 手动安装驱动程序:在驱动程序更新对话框中,选择“浏览计算机以查找驱动程序软件”,然后浏览到你之前解压缩的驱动程序文件夹,选择合适的驱动程序文件进行安装。 6. 完成安装:按照提示完成驱动程序的安装过程。安装完成后,设备管理器中的RK3568设备应该不再显示为未知设备,并且可以正常使用USB功能。 请注意,以上步骤仅供参考,具体的安装过程可能因驱动程序版本、操作系统版本等因素而略有不同。建议在安装驱动程序之前,先参考相关文档或与设备厂商进行沟通,以确保正确安装驱动程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式_笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值