Usb的设备是非常复杂的,它由许多不同的逻辑单元组成,这些逻辑单元之间的关系可以简单地描述如下:
l 设备(usb_device)通常具有一个或者更多的配置(usb_host_config)
l 配置通常具有一个或者更多的接口(usb_interface)
l 接口通常具有一个或者更多的设置
l 设置没有或者具有一个以上的端点(usb_host_endpoint)
各种机构体详细描述见include/linux/usb.H
我们编写设备驱动程序的最实质的一步就是模块的初始化,
Usb驱动模块初始化:
module_init (usb_***_init);
module_exit (usb_***_exit);
usb_***_init注册usb驱动程序
usb_register(&usbdriver)
usb_***_exit卸载驱动程序。
usb_deregister(&usbdriver)
其中的usb_driver机构体包含了如下主要内容:
static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton", //驱动程序的名字
.id_table = skel_table, //驱动程序所支持的设备列表
.probe = skel_probe, //探测函数
.disconnect = skel_disconnect, //断开函数
};
详细的结构:
struct usb_driver {
struct module *owner;
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);
const struct usb_device_id *id_table;
struct device_driver driver;
};
驱动所支持的设备列表可以通过多种方法定义,LDD(3rd)上的实例使用的是这个宏来获得usb_device_id结构体:
USB_DEVICE(vendor,product)
它是根据厂家id,和设备id来匹配驱动程序。
探测函数probe:
当一个设备被安装而usb核心认为该驱动程序应该处理时,探测函数被调用,探测函数首先检查传递给它的设备信息,确定驱动程序是否真的适合该设备。
当驱动程序因为某种原因不应控制设备时,端口函数被调用,它可以做一些清理的工作。
驱动程序通常要探测设备的端点地址,类型和缓冲区大小。
在探测函数中一般都会调用usb_register_dev(interface,&usb_class)函数来把设备注册到usb核心。只要该函数被调用,就要确保设备和驱动程序都处于可以处理用户访问设备的要求的恰当状态。
其中的两个参数结构体如下:
usb_interface结构体描述USB的接口,其中的重要字段有:
struct usb_host_interface *altsetting: 一个接口结构体数组,包含了所有可 能用于该接口的可选设置。
unsigned num_altsetting; altsetting指针所指的可选设置的数量。
unsigned usb_host_interface *cur_altsetting:指向altsetting数组内部的指针, 表示该接口的当前活动设置。
int minor: 包含USB核心分配给该接口的次设备号。
usb_interface结构体中的其它字段在usb驱动程序中不需要考虑。
usb_class_driver结构体包含如下内容:
char *name; sysfs用来描述设备的名字。
struct file_operations *fops;
mode_t mode; 为该驱动程序创建的devfs文件的访问模式。
int minor_base; 这是为该驱动程序指派的次设备号范围的开始值。
usb_class_driver实例:
static struct usb_class_driver skel_class = {
.name = "usb/skel%d",
.fops = &skel_fops,
.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
.minor_base = USB_SKEL_MINOR_BASE,
};
fops结构体实例:
static struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
.open = skel_open,
.release = skel_release,
};
在porbe函数中注册的是设备,而在驱动程序模块初始化中注册的是驱动程序,这一点要分清楚
:register_usb_dev(usb_interface,&usb_class_driver);把usb设备注册到usb核心
:usb_register(&usb_driver); 将驱动程序注册到usb子系统中
回过头来我们就要写file_operations中的函数了,简单的read函数实例如下:
static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;
dev = (struct usb_skel *)file->private_data;
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&count, HZ*10);
/* if the read was successful, copy the data to userspace */
if (!retval) {
if (copy_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
return retval;
}
Linux 内核中的usb代码通过一个称为urb(usb请求块)的东西和所有的usb设备通信。Urb被用来以异步的方式往或从特定的USB端点发送或接收数据。Usb驱动程序可能会为单个端点分配许多urb,也可能对许多不同的端点重用单个urb。一个urb典型的生命周期如下:
1,由USB设备驱动程序创建
2,分配给一个特定usb设备的特定端点
3,由USB设备驱动程序递交到usb核心
4,由usb核心递交到特定设备的特定USB主控制器驱动程序
5,由usb主控制器驱动程序处理,它从设备进行usb传送
6,当urb结束后,usb主控制器驱动程序通知usb设备驱动程序。
struct urb中usb设备驱动程序关心的字段有:
struct usb_device *dev;//urb所发送的目标struct usb_device指针。
unsigned int pipe ; //urb所要发送的特定目标struct usb_device的端点信息。
unsigned int usb_sndctrlpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb设备的指定端点设置为一个控制OUT端点。
unsigned int usb_rcvctrlpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb设备的指定端点设置为一个控制IN端点。
unsigned int usb_sndbulkpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb设备的指定端点设置为一个批量OUT端点。
unsigned int usb_rcvbulkpipe(struct usb_device *dev,unsigned int endpoint);//把指定的usb设备的指定端点设置为一个批量IN端点。
还有中断int,等时isoc的out in 端点类似设置。
unsigned int transfer_flags;//URB_SHORT_NOT_OK;URB_ISO_ASAP;....
void *transfer_buffer;//指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区的指针。
dma_addr_t transfer_dma;//用于以DMA方式传输数据到usb设备的缓冲区。
int transfer_buffer_length;//缓冲区大小
Unsigned char *setup_packet;//指向控制urb的设置数据包的指针,它在传输缓冲区的数据之前被传送。该变量只对控制urb有效。
Dma_addr_t setup_dma;//控制urb拥有设置数据包的DMA缓冲区。
Usb_complete_t complete;//指向一个结束处理例程的指针。
usb_complete_t 的类型定义为:
Typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
Void *contex ;//指向一个可以被usb驱动程序设置的数据块。
Int actuall_length;//当urb结束后,该变量被设置成发送或接收的数据的实际长度。
Int status;//urb 的当前状态
其它略。。
创建和销毁URB:必须使用动态创建usb_alloc_urb。
Struct urb *usb_alloc_urb ( int iso_packet, int mem_falgs);
Iso_packets,是该urb应该包含的等时数据包的数量,如果不打算创建等时数据包,这个设置为0,
Mem_flags,(同kmalloc 的flags)
销毁urb:
void usb_free_urb(struct urb* urb);
各urb的初始化函数:
中断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,
void *context, int interval);
参数:
Struct urb *urb :需要初始化的urb指针。
Struct usb_device *dev:该urb所发送的目标usb设备。
Pipe:该urb所发送的目标的特定端点。
Transfer_buffer:由kmalloc创建的数据缓冲区指针。
Complete:urb结束之后调用的结束处理例程的指针。
Context:指向一个数据块,该块被添加到urb结构中,以便进行结束例程后面的查询。
Interval:urb应该被调度的间隔。
批量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,
void *context);
控制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, void *context);
等时urb必须手工初始化。
提交urb:
一旦urb被USB驱动程序正确的创建和初始化后,就可以提交到usb核心以发送到usb设备,这是通过usb_submit_urb函数来实现的。
int usb_submit_urb(struct urb *urb , int mem_flags);
urb参数指向即将被发送到设备的urb的指针。
mem_flags:只有三个有效的值可以使用:GFP_ATOMIC, GFP_NOIO, GFP_KERNEL.
主要数据结构:
Struct usb_driver: 描述USB驱动程序的结构体
Struct usb_device_id: 描述该驱动程序支持的usb设备类型的结构体
Struct usb_device: 控制整个usb设备的结构体
Struct usb_interface: 所有的usb驱动程序都用它来和USB核心通信
Struct usb_class_driver: 描述了想要使用usb主设备号和用户空间程序进行通信 的USB驱动程序的机构体。
Struct urb: 描述一个usb数据传输的结构体
/*
* struct usb_device - kernel's representation of a USB device
*
* FIXME: Write the kerneldoc!
*
* Usbcore drivers should not set usbdev->state directly. Instead use
* usb_set_device_state().
*/
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 */
struct semaphore serialize;
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 */
int have_langid; /* whether string_langid is valid yet */
int string_langid; /* language ID for strings */
char *product;
char *manufacturer;
char *serial; /* static strings from the device */
struct list_head filelist;
struct class_device *class_dev;
struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
/*
* 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];
};