linux 下usb通信程序,linux设备驱动之USB数据传输分析

三:传输过程的实现说到传输过程,我们必须要从URB开始说起,这个结构的就好比是网络子系统中的skb,好比是I/O中的bio.USB系统的信息传输就是打成URB结构,然后再过行传送的.URB的全称叫USB request block.下面从它的接口说起.3.1:URB的相关接口1:URB的创建URB的创建是由usb_alloc_urb()完成的.这个函数会完成URB内存的分配和基本成员的初始化工作....
摘要由CSDN通过智能技术生成

三:传输过程的实现

说到传输过程,我们必须要从URB开始说起,这个结构的就好比是网络子系统中的skb,好比是I/O中的bio.USB系统的信息传输就是打成URB结构,然后再过行传送的.

URB的全称叫USB request block.下面从它的接口说起.

3.1:URB的相关接口

1:URB的创建

URB的创建是由usb_alloc_urb()完成的.这个函数会完成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) {

err("alloc_urb: kmalloc failed");

return NULL;

}

usb_init_urb(urb);

return urb;

}

这个函数有两个参数,一个是iso_packets.仅仅用于ISO传输.表示ISO数据包个数,如果用于其它类型的传输,此参数为0.另一个是mem_flags.是分配内存的参数.

Usb_init_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);

}

}

由此可以看到,它的初始化只是初始化了引用计数和ahchor_list链表.这个链表在URB被锁定的时候会用到.

2:URB的初始化

USB2.0 spec中定义了四种传输,为别为ISO,INTER,BULK,CONTORL.linux kernel为INTER,BULK,CONTORL的URB初始化提供了一些API,ISO的传输只能够手动去初始化.这些API如下:

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)

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_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)

分别用来填充CONTORL,BULK,INT类型的URB.

观察他们的函数原型,发现有很多相的的参数.先对这些参数做一下解释:

Urb:是要初始化的urb

Dev:表示消息要被发送到的USB设备

Pipe:表示消息被发送到的端点

transfer_buffer:表示发送数据的缓冲区

length:就是transfer_buffer所表示的缓冲区大小

context:完成处理函数的上下文

complete_fn:传输完了之后要调用的函数.

usb_fill_control_urb()的setup_packet:即将被发送到端点的设备数据包

usb_fill_int_urb()中的interval:这个urb应该被调度的间隔.

函数的实际都是差不多的.以usb_fill_control_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;

}

如上所示,只是将函数的参数赋值给了URB相关的成员而已.

另外,关于ISO的URB初始化虽然没有可以调用的API,但它的初始化也很简单,对应就是填充几个成员而已.

另外,对于pipe的参数.有一系列辅助的宏.如下示:

/* Create various pipes... */

#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)

这个宏都是根据usb2.0 spec的规范来设计的.

3:提交URB

提交urb的接口是usb_submit_urb().代码如下:

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)

{

int             xfertype, max;

struct usb_device       *dev;

struct usb_host_endpoint    *ep;

int             is_out;

if (!urb || urb->hcpriv || !urb->complete)

return -EINVAL;

dev = urb->dev;

if ((!dev) || (dev->state < USB_STATE_DEFAULT))

return -ENODEV;

/* For now, get the endpoint from the pipe.  Eventually drivers

* will be required to set urb->ep directly and we will eliminate

* urb->pipe.

*/

//取得要传输的端口.对端地址是由方向+dev address+port number组成的

ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)

[usb_pipeendpoint(urb->pipe)];

if (!ep)

return -ENOENT;

urb->ep = ep;

urb->status = -EINPROGRESS;

urb->actual_length = 0;

/* Lots of sanity checks, so HCDs can rely on clean data

* and don't need to duplicate tests

*/

//取得ep的传输类型

xfertype = usb_endpoint_type(&ep->desc);

//如果是控制传输.端点0默认是控制传输

if (xfertype == USB_ENDPOINT_XFER_CONTROL) {

//控制传输的urb如果没有setup_packet是非法的

struct usb_ctrlrequest *setup =

(struct usb_ctrlrequest *) urb->setup_packet;

if (!setup)

return -ENOEXEC;

//判断是否是out方向的传输

is_out = !(setup->bRequestType & USB_DIR_IN) ||

!setup->wLength;

} else {

//如果不是控制传输,在端点描述符的bEndportAddress的bit7 包含有端点的传输方向

is_out = usb_endpoint_dir_out(&ep->desc);

}

/* Cache the direction for later use */

//根据传输方向.置urb->transfer_flags的方向位

urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |

(is_out ? URB_DIR_OUT : URB_DIR_IN);

//根据usb2.0 spec.除控制传输外的其它传输只有在config状态的时候才能进行

if (xfertype != USB_ENDPOINT_XFER_CONTROL &&

dev->state < USB_STATE_CONFIGURED)

return -ENODEV;

//传送/接收的最大字节.如果这个最大巧若拙字节还要小于0,那就是非法的

max = le16_to_cpu(ep->desc.wMaxPacketSize);

if (max <= 0) {

dev_dbg(&dev->dev,

"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",

usb_endpoint_num(&ep->desc), is_out ? "out" : "in",

__FUNCTION__, max);

return -EMSGSIZE;

}

/* periodic transfers limit size per frame/uframe,

* but drivers only control those sizes for ISO.

* while we're checking, initialize return status.

*/

//如果是实时传输

if (xfertype == USB_ENDPOINT_XFER_ISOC) {

int n, len;

/* "high bandwidth" mode, 1-3 packets/uframe? */

//如果是高速传输.则要修正它的MAX值

//高速传输时, 一个微帧内可以修输多个数据.bit 11~bit12用来表示一个微帧内

//传输包的个数.

//在USB1.1中是不支持HIGH的

if (dev->speed == USB_SPEED_HIGH) {

int mult = 1 + ((max >> 11) & 0x03);

max &= 0x07ff;

max *= mult;

}

//实现传输的数据包数目不能小于等于0

if (urb->number_of_packets <= 0)

return -EINVAL;

//urb->number_of_packets: 实时数据包个数.每个实时数据包对应urb->iso_frame_desc[]中的一项

for (n = 0; n < urb->number_of_packets; n++) {

len = urb->iso_frame_desc[n].length;

if (len < 0 || len > max)

return -EMSGSIZE;

urb->iso_frame_desc[n].status = -EXDEV;

urb->iso_frame_desc[n].actual_length = 0;

}

}

/* the I/O buffer must be mapped/unmapped, except when length=0 */

//如果要传输的缓存区大小小于0.非法

if (urb->transfer_buffer_length < 0)

return -EMSGSIZE;

#ifdef DEBUG

/* stuff that drivers shouldn't do, but which shouldn't

* cause problems in HCDs if they get it wrong.

*/

{

unsigned int    orig_flags = urb->transfer_flags;

unsigned int    allowed;

/* enforce simple/standard policy */

allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |

URB_NO_INTERRUPT | URB_DIR_MASK | URB_FREE_BUFFER);

switch (xfertype) {

case USB_ENDPOINT_XFER_BULK:

if (is_out)

allowed |= URB_ZERO_PACKET;

/* FALLTHROUGH */

case USB_ENDPOINT_XFER_CONTROL:

allowed |= URB_NO_FSBR; /* only affects UHCI */

/* FALLTHROUGH */

default:            /* all non-iso endpoints */

if (!is_out)

allowed |= URB_SHORT_NOT_OK;

break;

case USB_ENDPOINT_XFER_ISOC:

allowed |= URB_ISO_ASAP;

break;

}

urb->transfer_flags &= allowed;

/* fail if submitter gave bogus flags */

if (urb->transfer_flags != orig_flags) {

err("BOGUS urb flags, %x --> %x",

orig_flags, urb->transfer_flags);

return -EINVAL;

}

}

#endif

/*

* Force periodic transfer intervals to be legal values that are

* a power of two (so HCDs don't need to).

*

* FIXME want bus->{intr,iso}_sched_horizon values here.  Each HC

* supports different values... this uses EHCI/UHCI defaults (and

* EHCI can use smaller non-default values).

*/

//关于实时传输和中断传输的interval处理

switch (xfertype) {

case USB_ENDPOINT_XFER_ISOC:

case USB_ENDPOINT_XFER_INT:

/* too small? */

//interval不能小于或等于0

if (urb->interval <= 0)

return -EINVAL;

/* too big? */

switch (dev->speed) {

case USB_SPEED_HIGH:    /* units are microframes */

/* NOTE usb handles 2^15 */

if (urb->interval > (1024 * 8))

urb->interval = 1024 * 8;

max = 1024 * 8;

break;

case USB_SPEED_FULL:    /* units are frames/msec */

case USB_SPEED_LOW:

if (xfertype == USB_ENDPOINT_XFER_INT) {

if (urb->interval > 255)

return -EINVAL;

/* NOTE ohci only handles up to 32 */

max = 128;

} else {

if (urb->interval > 1024)

urb->interval = 1024;

/* NOTE usb and ohci handle up to 2^15 */

max = 1024;

}

break;

default:

return -EINVAL;

}

/* Round down to a power of 2, no more than max */

urb->interval = min(max, 1 << ilog2(urb->interval));

}

return usb_hcd_submit_urb(urb, mem_flags);

}

这段代码虽然很长,但逻辑很清楚.对照代码中的注释理解应该是没有问题的.在这里要注意,UHCI是属于USB1.1的,它不支持HIGH传输.

对URB进行一系列处理之后,就会将urb丢给hcd进行处理了.usb_hcd_submit_urb()代码如下:

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)

{

int         status;

//从usb_bus的地址取得usb_hcd的地址

struct usb_hcd      *hcd = bus_to_hcd(urb->dev->bus);

/* increment urb's reference count as part of giving it to the HCD

* (which will control it).  HCD guarantees that it either returns

* an error or calls giveback(), but not both.

*/

//增加有关的引用计数,usbmon*系列的函数是编译选择的.忽略

usb_get_urb(urb);

atomic_inc(&urb->use_count);

atomic_inc(&urb->dev->urbnum);

usbmon_urb_submit(&hcd->self, urb);

/* NOTE requirements on root-hub callers (usbfs and the hub

* driver, for now):  URBs' urb->transfer_buffer must be

* valid and usb_buffer_{sync,unmap}() not be needed, since

* they could clobber root hub response data.  Also, control

* URBs must be submitted in process context with interrupts

* enabled.

*/

//对传输的缓存区进行DMA映射

status = map_urb_for_dma(hcd, urb, mem_flags);

//出现错误,返回

if (unlikely(status)) {

usbmon_urb_submit_error(&hcd->self, urb, status);

goto error;

}

//如果是root hub

if (is_root_hub(urb->dev))

status = rh_urb_enqueue(hcd, urb);

else

//如果是一般的设备

status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);

if (unlikely(status)) {

usbmon_urb_submit_error(&hcd->self, urb, status);

unmap_urb_for_dma(hcd, urb);

error:

urb->hcpriv = NULL;

INIT_LIST_HEAD(&urb->urb_list);

atomic_dec(&urb->use_count);

atomic_dec(&urb->dev->urbnum);

if (urb->reject)

wake_up(&usb_kill_urb_queue);

usb_put_urb(urb);

}

return status;

}

在这里函数里要注意到,urb->transfer_buffer是一个虚拟地址,用于UHCI的时候,必须要将其映射物理地址,以供设备使用.这也就是map_urb_for_dma()要完成的工作.map_urb_for_dma()函数比较简单,这里就不做详细分析.

可能有人会有这样的疑惑,对于root hub的情况,为什么不用对传输缓存区进行DMA映射呢?

在后面的处理中我们可以看到,其实对于root hub ,它不需要进行实际的物理传输,linux按照spec上的规定,将它静态放置在内存中,在进行相关操作的时候,只要直接copy过去就可以了.

其次,要注意,这个函数不能用于中断上下文,因为该函数是同步的,会引起睡眠.

在这里,我们看到,流程最终转入到了下面的代码片段中:

//如果是root hub

if (is_root_hub(urb->dev))

status = rh_urb_enqueue(hcd, urb);

else

//如果是一般的设备

status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);

下面,就分情况来剖析,各种传输到底是怎么完成的.

3.2:控制传输过程

1:root hub的控制传输

在前面看到,对于root hub的情况,流程会转入rh_urb_enqueue().代码如下:

static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)

{

//如果是中断传输的端点

if (usb_endpoint_xfer_int(&urb->ep->desc))

return rh_queue_status (hcd, urb);

//如果是控制传输的端点

if (usb_endpoint_xfer_control(&urb->ep->desc))

return rh_call_control (hcd, urb);

return -EINVAL;

}

对应是控制传输的时,流程转入了rh_call_control()中:

static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)

{

struct usb_ctrlrequest *cmd;

u16     typeReq, wValue, wIndex, wLength;

u8      *ubuf = urb->transfer_buffer;

u8      tbuf [sizeof (struct usb_hub_descriptor)]

__attribute__((aligned(4)));

const u8    *bufp = tbuf;

int     len = 0;

int     patch_wakeup = 0;

int     status;

int     n;

might_sleep();

spin_lock_irq(&hcd_root_hub_lock);

//将urb加到ep的urb传输链表

status = usb_hcd_link_urb_to_ep(hcd, urb);

spin_unlock_irq(&hcd_root_hub_lock);

if (status)

return status;

//将urb的私有域指向了hcd

urb->hcpriv = hcd;  /* Indicate it's queued */

cmd = (struct usb_ctrlrequest *) urb->setup_packet;

typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;

wValue   = le16_to_cpu (cmd->wValue);

wIndex   = le16_to_cpu (cmd->wIndex);

wLength  = le16_to_cpu (cmd->wLength);

if (wLength > urb->transfer_buffer_length)

goto error;

urb->actual_length = 0;

switch (typeReq) {

/* DEVICE REQUESTS */

/* The root hub's remote wakeup enable bit is implemented using

* driver model wakeup flags.  If this system supports wakeup

* through USB, userspace may change the default "allow wakeup"

* policy through sysfs or these calls.

*

* Most root hubs support wakeup from downstream devices, for

* runtime power management (disabling USB clocks and reducing

* VBUS power usage).  However, not all of them do so; silicon,

* board, and BIOS bugs here are not uncommon, so these can't

* be treated quite like external hubs.

*

* Likewise, not all root hubs will pass wakeup events upstream,

* to wake up the whole system.  So don't assume root hub and

* controller capabilities are identical.

*/

case DeviceRequest | USB_REQ_GET_STATUS:

tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)

<< USB_DEVICE_REMOTE_WAKEUP)

| (1 << USB_DEVICE_SELF_POWERED);

tbuf [1] = 0;

len = 2;

break;

case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:

if (wValue == USB_DEVICE_REMOTE_WAKEUP)

device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);

else

goto error;

break;

case DeviceOutRequest | USB_REQ_SET_FEATURE:

if (device_can_wakeup(&hcd->self.root_hub->dev)

&& wValue == USB_DEVICE_REMOTE_WAKEUP)

device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);

else

goto error;

break;

case DeviceRequest | USB_REQ_GET_CONFIGURATION:

tbuf [0] = 1;

len = 1;

/* FALLTHROUGH */

case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:

break;

case DeviceRequest | USB_REQ_GET_DESCRIPTOR:

switch (wValue & 0xff00) {

case USB_DT_DEVICE << 8:

if (hcd->driver->flags & HCD_USB2)

bufp = usb2_rh_dev_descriptor;

else if (hcd->driver->flags & HCD_USB11)

bufp = usb11_rh_dev_descriptor;

else

goto error;

len = 18;

break;

case USB_DT_CONFIG << 8:

if (hcd->driver->fl

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值