常见嵌入式驱动程序(嵌入式设备驱动程序基础笔记第24期)
在Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)、USB核心驱动(USBD)和不同种类的USB设备类驱动。
相关概念1. USB是主从结构
- 所有的USB传输,都是从USB主机这方发起;USB设备没有"主动"通知USB主机的能力。
- 例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动地等得PC机来读。
2. USB的传输类型:
- a. 控制传输:可靠,时间有保证,比如:USB设备的识别过程
- b. 批量传输: 可靠, 时间没有保证, 比如:U盘
- c. 中断传输:可靠,实时,比如:USB鼠标
- d. 实时传输:不可靠,实时,比如:USB摄像头
3. USB传输的对象:端点(endpoint)
我们说"读U盘"、"写U盘",可以细化为:把数据写到U盘的端点1,从U盘的端点2里读出数据
除了端点0外,每一个端点只支持一个方向的数据传输
端点0用于控制传输,既能输出也能输入
4. 每一个端点都有传输类型,传输方向
5. 术语里、程序里说的输入(IN)、输出(OUT) "都是" 基于USB主机的立场说的。
比如鼠标的数据是从鼠标传到PC机, 对应的端点称为"输入端点"
6,USB分类
- UHCI: intel, 低速(1.5Mbps)/全速(12Mbps)
- OHCI: microsoft 低速/全速
- EHCI: 高速(480Mbps)
1. 识别USB设备
- 1.1 分配地址
- 1.2 告诉USB设备(set address)
- 1.3 发出命令获取描述符
描述符的信息可以在include\linux\usb\Ch9.h看到
2. 查找并安装对应的设备驱动程序
3. 提供USB读写函数
USB驱动程序概述
USB驱动程序框架:
app:
-------------------------------------------
USB设备驱动程序 // 知道数据含义
内核 --------------------------------------
USB总线驱动程序 // 1. 识别, 2. 找到匹配的设备驱动, 3. 提供USB读写函数 (它不知道数据含义)
-------------------------------------------
USB主机控制器
UHCI OHCI EHCI
硬件 -----------
USB设备
更多有关usb子系统的介绍,参考下面这篇文章(非常详细的介绍了usb子系统)
https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch13.html
相关结构体1,usb_device_id
struct usb_device_id {
/* which fields to match against? */
__u16 match_flags;
/* Used for product specific matches; range is inclusive */
__u16 idVendor; //厂家ID
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches */
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches */
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* not matched against */
kernel_ulong_t driver_info;
};
2,usb_driver
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
void (*pre_reset) (struct usb_interface *intf);
void (*post_reset) (struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
};
3,usb_interface
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting; /* the currently
* active alternate setting */
unsigned num_altsetting; /* number of alternate settings */
int minor; /* minor number this interface is
* bound to */
enum usb_interface_condition condition; /* state of binding */
unsigned is_active:1; /* the interface is not suspended */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
struct device dev; /* interface specific device info */
struct device *usb_dev; /* pointer to the usb class's device, if any */
int pm_usage_cnt; /* usage counter for autosuspend */
};
4,usb_host_interface
struct usb_host_interface {
struct usb_interface_descriptor desc;
/* array of desc.bNumEndpoint endpoints associated with this
* interface setting. these will be in no particular order.
*/
struct usb_host_endpoint *endpoint;
char *string; /* iInterface string, if present */
unsigned char *extra; /* Extra descriptors */
int extralen;
};
5,usb_interface_descriptor
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));
6,usb_endpoint_descriptor
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;
__u8 bmAttributes;
__le16 wMaxPacketSize;
__u8 bInterval;
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
7,usb_device
struct usb_device {
int devnum; /* Address on USB bus */
char devpath [16]; /* Use in messages: /port/port/... */
enum usb_device_state state; /* configured, not attached, etc */
enum usb_device_speed speed; /* high/full/low (or error) */
struct usb_tt *tt; /* low/full speed dev, highspeed hub */
int ttport; /* device port on that tt hub */
unsigned int toggle[2]; /* one bit for each endpoint
* ([0] = IN, [1] = OUT) */
struct usb_device *parent; /* our hub, unless we're the root */
struct usb_bus *bus; /* Bus we're part of */
struct usb_host_endpoint ep0;
struct device dev; /* Generic device interface */
struct usb_device_descriptor descriptor;/* Descriptor */
struct usb_host_config *config; /* All of the configs */
struct usb_host_config *actconfig;/* the active configuration */
struct usb_host_endpoint *ep_in[16];
struct usb_host_endpoint *ep_out[16];
char **rawdescriptors; /* Raw descriptors for each config */
unsigned short bus_mA; /* Current available from the bus */
u8 portnum; /* Parent port number (origin 1) */
u8 level; /* Number of USB hub ancestors */
unsigned discon_suspended:1; /* Disconnected while suspended */
unsigned have_langid:1; /* whether string_langid is valid */
int string_langid; /* language ID for strings */
/* static strings from the device */
char *product; /* iProduct string, if present */
char *manufacturer; /* iManufacturer string, if present */
char *serial; /* iSerialNumber string, if present */
struct list_head filelist;
#ifdef CONFIG_USB_DEVICE_CLASS
struct device *usb_classdev;
#endif
#ifdef CONFIG_USB_DEVICEFS
struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
#endif
/*
* Child devices - these can be either new devices
* (if this is a hub device), or different instances
* of this same device.
*
* Each instance needs its own set of data structures.
*/
int maxchild; /* Number of ports if hub */
struct usb_device *children[USB_MAXCHILDREN];
int pm_usage_cnt; /* usage counter for autosuspend */
u32 quirks; /* quirks of the whole device */
#ifdef CONFIG_PM
struct delayed_work autosuspend; /* for delayed autosuspends */
struct mutex pm_mutex; /* protects PM operations */
unsigned long last_busy; /* time of last use */
int autosuspend_delay; /* in jiffies */
unsigned auto_pm:1; /* autosuspend/resume in progress */
unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */
unsigned autosuspend_disabled:1; /* autosuspend and autoresume */
unsigned autoresume_disabled:1; /* disabled by the user */
#endif
};
8,usb_host_config
struct usb_host_config {
struct usb_config_descriptor desc;
char *string; /* iConfiguration string, if present */
/* the interfaces associated with this configuration,
* stored in no particular order */
struct usb_interface *interface[USB_MAXINTERFACES];
/* Interface information available even when this is not the
* active configuration */
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
unsigned char *extra; /* Extra descriptors */
int extralen;
};
9,usb_bus
struct usb_bus {
struct device *controller; /* host/master side hardware */
int busnum; /* Bus number (in order of reg) */
char *bus_name; /* stable id (PCI slot_name etc) */
u8 uses_dma; /* Does the host controller use DMA? */
u8 otg_port; /* 0, or number of OTG/HNP port */
unsigned is_b_host:1; /* true during some HNP roleswitches */
unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */
int devnum_next; /* Next open device number in
* round-robin allocation */
struct usb_devmap devmap; /* device address allocation map */
struct usb_device *root_hub; /* Root hub */
struct list_head bus_list; /* list of busses */
int bandwidth_allocated; /* on this bus: how much of the time
* reserved for periodic (intr/iso)
* requests is used, on average?
* Units: microseconds/frame.
* Limits: Full/low speed reserve 90%,
* while high speed reserves 80%.
*/
int bandwidth_int_reqs; /* number of Interrupt requests */
int bandwidth_isoc_reqs; /* number of Isoc. requests */
#ifdef CONFIG_USB_DEVICEFS
struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */
#endif
struct class_device *class_dev; /* class device for this bus */
#if defined(CONFIG_USB_MON)
struct mon_bus *mon_bus; /* non-null when associated */
int monitored; /* non-zero when monitored */
#endif
};
上述结构体之间的关系,如下图:
USB设备(usb_device)
包括配置(Configurations)、接口(Interfaces)和端点(endpoint),以及USB驱动程序如何绑定到USB接口
1,端点(endpoint):USB通信最基本的形式叫做端点。
端点分类:Control,lsochronous,Interrupt和bulk
(1)控制传输模式(Control)
控制传输模式支持双向传输,用来处理从usb主机端口到usb设备端口的数据传输,用于控制指令,设备状态查询以及确认命令。
(2)等时传输方式(lsochronous)
等时传输是一种周期性的连续性的意向传输模式,通常用于对时间有着密切关系的信息的传输,对准确性要求不高,但对时间要求极为敏感的设备,如视频,音频的传输。
(3)中断传输模式(Interrupt)
中断传输模式用于非周期性的,自然发生的,数据量小的传输,数据传输的方向是从设备到主机。如usb键盘和鼠标
(4)批量传输模式(bulk)
批量传输模式是一种单向的,用于大量数据传输的模式,该方式用来传输正确无误的数据。通常打印机,扫描仪,数码相机以这种方式与主机连接
备注:usb_host_endpoint 结构体里的 usb_endpoint_descriptor 结构体 就包含了端点的描述:
(usb_endpoint_descriptor )
- bEndpointAddress 端点的地址
- bmAttributes 端点类型
- wMaxPacketSize 端点一次可以处理的最大字节大小
- bInterval 如果端点是中断类型的端点,则此值中断请求之间的时间间隔
2,接口(Interfaces)
USB端点被捆绑到接口中,USB接口只能处理一种类型的USB逻辑连接,例如鼠标、键盘或音频流。一些USB设备有多个接口,例如USB扬声器可能由两个接口组成:一个用于按钮的USB键盘和一个USB音频流。因为USB接口代表基本功能,每个USB驱动程序控制一个接口;因此,以扬声器为例,Linux需要为一个硬件设备提供两个不同的驱动程序。
在内核中用 struct usb_interface 结构描述USB接口。这个结构是USB核心传递给USB驱动程序的,也是USB驱动程序负责控制的。该结构中的重要元素包括:
- struct usb_host_interface *altsetting 包含可为此接口选择的所有备用设置的接口结构数组
- unsigned num_altsetting altsetting 指针指向的备用设置数
- struct usb_host_interface *cur_altsetting 指向数组altsetting的指针,表示此接口的当前活动设置
- int minor 如果绑定到此接口的USB驱动程序使用USB主设备号,则此变量包含USB核心分配给接口的次设备号
3,配置(Configurations)
USB接口本身被捆绑到配置中。USB设备可以有多种配置,并且可以在它们之间切换以更改设备的状态。例如,一些允许下载固件的设备包含多个配置来实现这一点。单个配置只能在一个时间点启用。Linux不能很好地处理多个配置的USB设备,但是它们很少。
Linux使用struct usb_host_config 结构描述USB配置,并使用struct usb_device 结构描述整个USB设备。
USB设备驱动程序通常不需要读取或写入这些结构中的任何值,因此这里不详细定义它们。但可以在内核源代码树的include/linux/usb.h文件中找到它们的描述。
总结上述,USB设备非常复杂,由许多不同的逻辑单元组成。这些单元之间的关系可以简单地描述如下:- 1,设备(usb_device)通常有一个或多个配置(Configurations)。
- 2,配置通常有一个或多个接口(Interfaces)。
- 3,接口通常有一个或多个设置(Setting )。
- 4,接口具有零个或多个端点(Endpoint)。
1,第一个USB设备是根集线器(root hub)。这是USB控制器,通常包含在PCI设备中。控制器之所以这样命名是因为它控制连接到它的整个USB总线。控制器是PCI总线和USB总线之间的桥梁,也是该总线上的第一个USB设备。所有根集线器都由USB核心分配一个唯一的编号。
2,Linux内核中的USB设备使用urb(USB请求块)与所有USB设备通信。这个请求块是用struct urb结构描述的,可以在include/linux/usb.h文件中找到。
3,urb用于以异步方式向特定USB设备上的特定USB端点发送数据或从该端点接收数据。USB设备驱动程序可以为单个端点分配多个urb,或者可以根据驱动程序的需要为多个不同端点重用单个urb。设备中的每个端点都可以处理一个urb队列,以便在队列为空之前将多个urb发送到同一端点。
4,urb典型生命周期如下:
- 由USB设备驱动程序创建。
- 分配给特定USB设备的特定端点。
- 通过USB设备驱动程序提交到USB核心。
- 由USB核心提交到指定设备的特定USB主机控制器驱动程序。
- 由USB主机控制器驱动程序处理,该驱动程序将USB传输到设备。
- 当urb完成时,USB主机控制器驱动程序通知USB设备驱动程序。
5,编写USB设备驱动程序的方法类似于pci驱动程序:驱动程序向USB子系统注册其驱动程序对象,然后使用供应商和设备标识符来判断其硬件是否已安装。
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com