linux usb 鼠标
谨以此文纪念过往的岁月
4.2.1 usb_endpoint_is_int_in
判断端点是否为中断in类型
static inline int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
{
return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
}
static inline int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
{
return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==USB_ENDPOINT_XFER_INT);
}
端点描述符中bmAttributes的低两位用于标示端点传输类型,分为四种:
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0 --控制传输
#define USB_ENDPOINT_XFER_ISOC 1 --等时传输
#define USB_ENDPOINT_XFER_BULK 2 --块传输
#define USB_ENDPOINT_XFER_INT 3 --中断传输
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{
return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}
对于bEndpointAddress的最高位用于标示传输方向,以host定位,in只device to host ,而out指host to device。而低四位用于标示设备地址。所以每一个hub上最多
挂31个器件。
同时对于其他端点类型的检测类似,如检测端点是否为块in类型:usb_endpoint_is_bulk_in。
4.2.2 usb_rcvintpipe
设置端点信息,其实pipe是一个int类型的数据。urb所发送的特定目标struct usb_device的端点信息。
#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
static inline unsigned int __create_pipe(struct usb_device *dev,unsigned int endpoint)
{
return (dev->devnum << 8) | (endpoint << 15);
}
pipe由四个部分组成,最高2位用于标示PIPE类型,第15到23的bit位用于标示endpoint->bEndpointaddress信息,而第8到第15位用于标示设备的number号,第7bit位
用于标示数据流方向。
个人以为这里的endpoint极容易引起误解,如果写成endpointaddress会较好理解,这样在看到该API时直接就知道传入参数是什么。
创建各种管道:
#define usb_sndctrlpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个控制OUT端点
#define usb_rcvctrlpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个控制IN端点
#define usb_sndisocpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个等时OUT端点
#define usb_rcvisocpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个等时IN端点
#define usb_sndbulkpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个块OUT端点
#define usb_rcvbulkpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个块IN端点
#define usb_sndintpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个中断OUT端点
#define usb_rcvintpipe(dev,endpoint) --把指定usb设备的指定端口设置为一个中断IN端点
pipe的含义就很好理解了,其就是将指定usb设备的指定端口设置为某一类型的端口,即是将端点的信息保存。
4.2.3 usb_maxpacket
取得端点所支持的最大数据数
static inline __u16 usb_maxpacket(struct usb_device *udev, int pipe, int is_out)
{
struct usb_host_endpoint *ep;
unsigned epnum = usb_pipeendpoint(pipe); --获取设备number (pipe>>15)&&0x0F
if (is_out) {
WARN_ON(usb_pipein(pipe));
ep = udev->ep_out[epnum]; --获取端点
} else {
WARN_ON(usb_pipeout(pipe));
ep = udev->ep_in[epnum];
}
if (!ep)
return 0;
/* NOTE: only 0x07ff bits are for packet size... */
return le16_to_cpu(ep->desc.wMaxPacketSize); --获取端点信息
}
4.2.4 usb_buffer_alloc
从字面含义去理解开辟一段缓冲区。参数很好理解,一般的mem_flags为GFP_KERNEL,不过在中断上下文中不能阻塞,设置为GFP_ATOMIC
void *usb_buffer_alloc(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma)
{
if (!dev || !dev->bus)
return NULL;
return hcd_buffer_alloc(dev->bus, size, mem_flags, dma);
}
void *hcd_buffer_alloc(struct usb_bus *bus,size_t size,gfp_t mem_flags,dma_addr_t *dma)
{
struct usb_hcd *hcd = bus_to_hcd(bus);
int i;
/* some USB hosts just use PIO */
if (!bus->controller->dma_mask &&!(hcd->driver->flags & HCD_LOCAL_MEM)) {
*dma = ~(dma_addr_t) 0;
return kmalloc(size, mem_flags);
}
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i]) --当size在HCD_BUFFER_POOLS之内时,直接在pool中分配
return dma_pool_alloc(hcd->pool [i], mem_flags, dma);
}
return dma_alloc_coherent(hcd->self.controller, size, dma, 0); --分配DMA空间
}
4.2.5 usb_alloc_urb
在usb系统中urb很重要,其内核通过urb与usb设备通信。这个类型一个联络官一样,将驱动和设备连接。
urb是一个很巨大的结构体,不过对于驱动工程师而言,需要理解下面几个成员:
struct usb_device *dev; urb所发送的目标usb device的指针。
unsigned int pipe; 这个就不多说了,这个就是所发送特定目标的特定端点信息。至于pipe怎么设置在上面讲过。
unsigned int transfer_flags; 传输标识符
URB_SHORT_NOT_OK --对任何可能发生的对IN端点的读取都应该是err
URB_ISO_ASAP --如果urb是等时的,这个在以后等时urb时再看
URB_NO_TRANSFER_DMA_MAP --当urb包含一个即将传输DMA缓冲区时应该设置该位,usb core使用transfer_dma指向的缓冲区,而不是transfer_buffer指向地址
URB_NO_SETUP_DMA_MAP --类似于上面。usb core使用setup_dma变量指向的缓冲区,而非setup_packet.s
URB_NO_FSBR
URB_ZERO_PACKET
URB_NO_INTERRUPT
void *transfer_buffer 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区的指针。该缓冲区必须要使用kmalloc来创建,而非在栈中或静态内存中。
dma_addr_t transfer_dma 用于以DMA方式传输数据到usb设备的缓冲区。
int transfer_buffer_length 上述两个参数指向的缓冲区的大小
unsigned char *setup_packet 指向控制urb的设置数据包的指针
dma_addr_t setup_dma
usb_complete_t complete 结束处理函数指针,当urb完全传输或发生错误时调用。
void *context 类似于一个urb的私有数据
int actual_length urb结束后,urb所发送或接收的实际长度。
int status urb结束或被调用时的状态
int start_frame 设置或返回初始的帧数量。
int interval urb被轮询的时间间隔
上面几个成员比较重要。
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
urb = kmalloc(sizeof(struct urb) +iso_packets * sizeof(struct usb_iso_packet_descriptor),mem_flags);
if (!urb) {
printk(KERN_ERR "alloc_urb: kmalloc failed\n");
return NULL;
}
usb_init_urb(urb);
return urb;
}
void usb_init_urb(struct urb *urb)
{
if (urb) {
memset(urb, 0, sizeof(*urb));
kref_init(&urb->kref);
INIT_LIST_HEAD(&urb->anchor_list);
}
}
4.2.6 usb_fill_int_urb
在urb申请后,需要对urb的参数进行赋值。对于中断,控制,以及批量urb都有对urb的初始化函数。
以usb_fill_int_urb为例:
static inline 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,int interval)
{
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = transfer_buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = complete_fn;
urb->context = context;
if (dev->speed == USB_SPEED_HIGH)
urb->interval = 1 << (interval - 1);
else
urb->interval = interval;
urb->start_frame = -1;
}
对于控制urb和批量urb都有类似的初始化函数
static inline 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)
static inline 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的部分成员进行赋值。
4.2.7 usb_submit_urb
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
参数 urb 就是前面的urb
mem_flags 有三种值可以使用
GFP_ATOMIC
调用者在一个urb结束处理函数中,中断处理函数,底半部。tasklet或定时器的回调函数中。
调用者正持有一个自旋锁或读写锁
其实所白了这个标识符的设置其实是避免睡眠。
GFP_NOIO
驱动程序处于块IO路径中应使用该值
GFP_KERNEL
在上述情况之外使用
五.总结
对于linux的usb设备的驱动,一般是针对设备的某一接口做的驱动。在其中主要是urb的申请和提交。
阅读(889) | 评论(0) | 转发(0) |