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标志值被设置才能正确地工作。