USB驱动函数总结
2016-11-18 20:46
344 查看
pipe 管道
管道是USB设备通信的通道,内核中提供了创建管道的宏,从宏中我们可以分析出,管道是一个 int 型的变量,由设备号、端点地址、端点类型组合而成。usb_[snd|rcv][ctrl|int|bulk|isoc]pipe(dev, endpoint) 例: struct usb_device *dev = interface_to_usbdev(intf); struct usb_endpoint_descriptor *endpoint; int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
#define usb_sndctrlpipe(dev,endpoint) \ ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint)) #define usb_rcvctrlpipe(dev,endpoint) \ ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) #define usb_sndisocpipe(dev,endpoint) \ ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint)) #define usb_rcvisocpipe(dev,endpoint) \ ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) #define usb_sndbulkpipe(dev,endpoint) \ ((PIPE_BULK << 30) | __create_pipe(dev, endpoint)) #define usb_rcvbulkpipe(dev,endpoint) \ ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN) #define usb_sndintpipe(dev,endpoint) \ ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint)) #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); }
URB
分配URB
usb_alloc_urb(int iso_packets, gfp_t mem_flags)
填充URB
控制传输
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->dev = dev; urb->pipe = pipe; urb->setup_packet = setup_packet; urb->transfer_buffer = transfer_buffer; urb->transfer_buffer_length = buffer_length; urb->complete = complete_fn; urb->context = context; }
中断传输
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; }
批量传输
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) { urb->dev = dev; urb->pipe = pipe; urb->transfer_buffer = transfer_buffer; urb->transfer_buffer_length = buffer_length; urb->complete = complete_fn; urb->context = context; }
等时传输
不幸的是,等时urb 没有和中断、控制、批量urb 类似的初始化函数,因此它们在提交到USB核心之前,需要在驱动程序中手动的初始化。例如:urb->dev = dev; urb->context = uvd; urb->pipe = usb_rcvisocpipe(dev,uvd->video_endp-1); urb->interval = 1; urb->transfer_flags = URB_IOS_ASAP; urb->transfer_buffer = can->sts_buf[i]; urb_complete = konicawc_isoc_irq; urb->number_of_packets = FRAMES_PRE_DESC; urb->transfer_buffer_lenth = FRAMES_PRE_DESC; for (j=0; j < FRAMES_PRE_DESC; j++){ urb->ios_frame_desc[j].offset = j; urb->ios_frame_desc[j].length = 1; }
同步提交URB
有时候 USB 驱动程序只是要发送或者接收一些简单的 USB 数据,而不是想创建一个 struct urb ,初始化它,然后等待该 urb 接收函数运行这些麻烦事都走一遍。内核提供了同步提交 urb 的接口。控制传输
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout // 超时时间,以jiffies为单位 )
如果函数调用成功,返回值为 0 ,如果返回一个负数,表示发生一个错误。如果成功 actual_lenth 参数包含从该消息发送或者接收的字节数。
举例:
/* usb_get_descriptor */ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (type << 8) + index, 0, buf, size, USB_CTRL_GET_TIMEOUT);
控制传输,通过 pipe 可以看出传输方向是设备到主机,和默认端点0通信,请求类型是请求描述符,具体的类型和索引。传输完成时,描述符就被存放在 buf 所指向的缓冲区中了。
usb_ctrl_msg 分析
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout) { struct usb_ctrlrequest *dr; int ret; dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); if (!dr) return -ENOMEM; // 填充 setup packet dr->bRequestType = requesttype; dr->bRequest = request; dr->wValue = cpu_to_le16(value); dr->wIndex = cpu_to_le16(index); dr->wLength = cpu_to_le16(size); /* dbg("usb_control_msg"); */ ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout); kfree(dr); return ret; }
static int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, struct usb_ctrlrequest *cmd, void *data, int len, int timeout) { struct urb *urb; int retv; int length; urb = usb_alloc_urb(0, GFP_NOIO); if (!urb) return -ENOMEM; usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data, len, usb_api_blocking_completion, NULL); retv = usb_start_wait_urb(urb, timeout, &length); if (retv < 0) return retv; else return length; }
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->dev = dev; urb->pipe = pipe; urb->setup_packet = setup_packet; urb->transfer_buffer = transfer_buffer; urb->transfer_buffer_length = buffer_length; urb->complete = complete_fn; urb->context = context; }
struct api_context { struct completion done; int status; }; static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) { struct api_context ctx; unsigned long expire; int retval; // 完成量 init_completion(&ctx.done); urb->context = &ctx; urb->actual_length = 0; retval = usb_submit_urb(urb, GFP_NOIO); if (unlikely(retval)) goto out; expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; // 等待完成,返回值为0表示超时 if (!wait_for_completion_timeout(&ctx.done, expire)) { usb_kill_urb(urb); retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status); dev_dbg(&urb->dev->dev, "%s timed out on ep%d%s len=%u/%u\n", current->comm, usb_endpoint_num(&urb->ep->desc), usb_urb_dir_in(urb) ? "in" : "out", urb->actual_length, urb->transfer_buffer_length); } else retval = ctx.status; out: if (actual_length) *actual_length = urb->actual_length; usb_free_urb(urb); return retval; }
static void usb_api_blocking_completion(struct urb *urb) { struct api_context *ctx = urb->context; ctx->status = urb->status; complete(&ctx->done); }
不难分析,内核帮助我们分配设置了 urb ,并且提供了一个统一的完成函数,在提交 urb 时有一点需要注意,内核初始化了一个完成量,并且内核在提交 urb 之后在 wait_for_completion_timeout ,等待的过程中线程自然就休眠了,何时完成呢?在统一的完成函数中 complete(&ctx->done) 。所以称这种方式为同步提交 urb 。
中断、批量传输
int usb_interrupt_msg(...) { return usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout); } int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int *actual_length, int timeout )
批量和控制传输的参数简单一点,举例:
/* 进行阻塞的批量读取,从设备获取数据 */ 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 (!retval){ if (copy_to_user(buffer, dev->bulk_in_buffer, count)) retval = -EFAULT; else retval = count; }
批量传输,传输方向设备到主机,和端点bulk_in_endpoint通信。
usb_bulk_msg分析
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) { struct urb *urb; struct usb_host_endpoint *ep; // 在 usb_device 的 ep_in[] 和 ep_out[] 数组中找到管道对应的断点 ep = (usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out) [usb_pipeendpoint(pipe)]; if (!ep || len < 0) return -EINVAL; // 分配 urb urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; // 如果是中断端点 if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); usb_fill_int_urb(urb, usb_dev, pipe, data, len, usb_api_blocking_completion, NULL, ep->desc.bInterval); } else // 如果是 bulk 端点 usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, usb_api_blocking_completion, NULL); return usb_start_wait_urb(urb, timeout, actual_length); }
异步提交urb
异步提交 urb 需要我们自己去创建、设置、提交urb,并且提供一个完成函数。举例:
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_device *dev = interface_to_usbdev(intf); struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; int pipe; interface = intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; //endpoint = &intf->cur_altsetting->endpoint[0].desc pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); len = endpoint->wMaxPacketSize; //分配缓冲区,dma usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys); //分配urb um_urb = usb_alloc_urb(0, GFP_KERNEL); //填充urb usb_fill_int_urb(um_urb, dev, pipe, usb_buf, len, usb_mouse_irq, NULL, endpoint->bInterval); um_urb->transfer_dma = usb_buf_phys; um_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //使用dma传输 /* 使用URB */ usb_submit_urb(um_urb, GFP_KERNEL); return 0; }
static void usb_mouse_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(um_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0); input_sync(um_dev); } if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1))) { /* 右键发生了变化 */ input_event(um_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0); input_sync(um_dev); } if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2))) { /* 中键发生了变化 */ input_event(um_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0); input_sync(um_dev); } pre_val = usb_buf[0]; /* 重新提交urb */ usb_submit_urb(um_urb, GFP_KERNEL); }
销毁URB
usb_free_urb(struct urb * urb)
取消URB
应该调用 usb_fill_urb 或 usb_unlink_urb 函数来终止一个已经被提交到USB核心的urb。如果调用 usb_kill_urb ,urb 的生命周期将终止,通常是当设备从系统中断开时,在断开回调函数中调用此函数。
对于某些驱动程序而言,应该使用 usb_ublink_urb 函数来告诉USB核心终止一个urb,该函数并不等到urb完全被终止之后才返回回调函数。这对于在中断处理例程中或者持有一个自旋锁时终止一个urb是很有用,因为等待一个urb完全被终止需要USB核心具有使调用进程休眠的能力。该函数需要被要求终止的urb中的URB_ASYNC_UNLINK标志值被设置才能正确地工作。
相关文章推荐
- windows wdf 驱动开发总结(1)--usb驱动
- windows wdf 驱动开发总结(3)-usb驱动
- Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现
- VxWorks下USB驱动总结1
- VxWorks下USB驱动总结1
- Windows USB功能驱动开发总结
- Windows USB功能驱动开发总结
- Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现
- 字符设备驱动函数解析(学习总结)
- 相关USB驱动总结
- 开发wince下的usb音频设备驱动总结
- USB学习总结1—s3c6410的USB驱动修改
- usb驱动的基本结构和函数简介
- 开发wince下的usb音频设备驱动总结
- wince下的USB驱动要点总结
- windows wdf 驱动开发总结(2)--usb驱动
- usb驱动的基本结构和函数简介
- usb驱动的基本结构和函数简介
- Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现
- usb驱动框架(usb-skeleton)分析总结