usb_control_msg

来源:互联网 发布:手绘板绘图软件 编辑:程序博客网 时间:2024/05/16 09:16
调用实例
static int set_port_feature(struct usb_device *hdev, int port1, int feature)
{
    return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
        USB_REQ_SET_FEATURE, USB_RT_PORT, feature,
        port1, NULL, 01000);
}
 
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;
 
    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;
}
EXPORT_SYMBOL_GPL(usb_control_msg);
 
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;
}
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;
}
EXPORT_SYMBOL_GPL(usb_alloc_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 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;
    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;
}
 
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;
usb
    if (!urb || urb->hcpriv || !urb->complete)
        return -EINVAL;
    dev = urb->dev;
    if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
        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.
     */

    ep = usb_pipe_endpoint(dev, urb->pipe);
通过pipe和device对象,得到pipe对应的device中的endpoint
static inline struct usb_host_endpoint *
usb_pipe_endpoint(struct usb_device *dev, unsigned int pipe)
{
    struct usb_host_endpoint **eps;
    eps = usb_pipein(pipe) ? dev->ep_in : dev->ep_out;//
    return eps[usb_pipeendpoint(pipe)];
}
在2.0规范Table 9-13. Standard Endpoint Descriptor中,描述了设备中endpoint的地址,bEndpointAddress的Bit 3...0: 表示The endpoint number,一个设备最多只能支持15个IN和15个OUT endpoint,0端点比较特殊,即支持IN又支持OUT,所以一个USB设备IN和OUT的端点数都是16个
struct usb_device {
    ...... 
    struct usb_host_endpoint *ep_in[16];
    struct usb_host_endpoint *ep_out[16];
    ......
}
表示设备或接口设置中包含的endpoint
struct usb_host_endpoint {
    struct usb_endpoint_descriptor        desc;
    struct usb_ss_ep_comp_descriptor    ss_ep_comp;
    struct list_head        urb_list;
    void                *hcpriv;
    struct ep_device        *ep_dev;    /* For sysfs info */
 
    unsigned char *extra;   /* Extra descriptors */
    int extralen;
    int enabled;
};
    if (!ep)
        return -ENOENT;
 
    urb->ep = ep;    //将URB要发送到的endpoint捆绑到URB对象中的endpoint指针
    urb->status = -EINPROGRESS;
    urb->actual_length = 0;    //传输还没开始,实际传输的数据长度是0
 
    /* Lots of sanity checks, so HCDs can rely on clean data
     * and don't need to duplicate tests
     */    //
给URB绑定了ep后,要检查ep类型
    xfertype = usb_endpoint_type(&ep->desc);
    if (xfertype == USB_ENDPOINT_XFER_CONTROL) {    //是否是控制传输?是:通过传进的参数构造的setup_packet判断端点方向;否:通过ep描述符判断
        struct usb_ctrlrequest *setup =    
                (struct usb_ctrlrequest *) urb->setup_packet;    //setup_packet是usb_control_msg传进的参数封装形成dr,格式同usb_ctrlrequest,被强制转换成usb_ctrlrequest类型
        if (!setup)
            return -ENOEXEC;
        is_out = !(setup->bRequestType & USB_DIR_IN) ||
                !setup->wLength;
    } else {
        is_out = usb_endpoint_dir_out(&ep->desc);    //如果不是控制传输,则没有setup_packet,则根据ep的描述符判断ep类型
    }
 
    /* Clear the internal flags and cache the direction for later use */
    urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
            URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
            URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
            URB_DMA_SG_COMBINED);    //把如上的所有位清0
    urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);  
 
    if (xfertype != USB_ENDPOINT_XFER_CONTROL &&    //在设备被配置前,只能发送控制传输URB,其它传输类型必须在设备配置完成后才能发送URB
            dev->state < USB_STATE_CONFIGURED)
        return -ENODEV;
 
    max = usb_endpoint_maxp(&ep->desc);    //每个transaction的大小
    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",
            __func__, 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;
 
        /* SuperSpeed isoc endpoints have up to 16 bursts of up to
         * 3 packets each
         */
/* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */
struct usb_ss_ep_comp_descriptor {
    __u8  bLength;
    __u8  bDescriptorType;
 
    __u8  bMaxBurst;    
//(ep burst支持的最大packets数,0~15==>1~16个packets)Table 9-22. SuperSpeed Endpoint Companion Descriptor
    __u8  bmAttributes;
    __le16 wBytesPerInterval;
} __attribute__ ((packed));
                                                
//最大字节数可以按以下方式计算:wMaxPacketSize * (bMaxBurst +1) * (Mult + 1)
        if (dev->speed == USB_SPEED_SUPER) {
            int     burst = 1 + ep->ss_ep_comp.bMaxBurst;    //
终结点伴随描述符的 bMaxBurst 字段。此值表示单个突发事务中可以存在的 wMaxPacketSize 的盘的数目。一个突发事务内最多可有 16 个盘(由 0 到 15 指示)
            int     mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);   
 //终结点伴随描述符的 Isochronous.Mult 字段。在 SuperSpeed 常时等量传输中,其他事务(与高速设备非常类似)指的是突发事务。Mult 值指示终结点支持的最大突发事务数。一个服务时间间隔内最多可有三个突发事务(由 0 到 2 指示)
            max *= burst;
            max *= mult;
        }
 //参考
http://technet.microsoft.com/zh-cn/library/hh406225(v=vs.85).aspx
        /* "high bandwidth" mode, 1-3 packets/uframe? */
        if (dev->speed == USB_SPEED_HIGH) {
            int    mult = 1 + ((max >> 11& 0x03);
    // High-Speed, High Bandwidth Endpoints 每微帧最多支持三个high-speed transactions,取支持的high-speed transactions数
            max &= 0x07ff;    //低11位表示每个high-speed transaction的最大数据量(byte)
            max *= mult;
        }
10..0 Maximum size of data payload in bytes
12..11 Number of transactions per microframe
15..13 Reserved,must be set to zero
 
        if (urb->number_of_packets <= 0)
            return -EINVAL;
        for (n = 0; n < urb->number_of_packets; n++) {
    //number_of_packets表示一个URB里有过少个等时传输,每个等时传输也称作一个packet,用struct usb_iso_packet_descriptor iso_frame_desc[]表示。
            len = urb->iso_frame_desc[n].length;
            if (len < 0 || len > max)
    //如果哪个等时传输中没有数据,如果预算传输的长度比计算出来的max还大,则返回,并将实际传输的数据长度置0.
                return -EMSGSIZE;
            urb->iso_frame_desc[n].status = -EXDEV;
    //-EXDEV表示这次等时传输仅仅部分完成
            urb->iso_frame_desc[n].actual_length = 0;
        }
    }
 
    /* the I/O buffer must be mapped/unmapped, except when length=0 */
    if (urb->transfer_buffer_length > INT_MAX)
    //没有看懂???
        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    allowed;
    static int pipetypes[4= {
        PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
    };
 
    /* Check that the pipe's type matches the endpoint's type */
    if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
        dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
            usb_pipetype(urb->pipe), pipetypes[xfertype]);
 
    /* Check against a simple/standard policy */
    allowed = (URB_NO_TRANSFER_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;
    }
    allowed &= urb->transfer_flags;
 
    /* warn if submitter gave bogus flags */
    if (allowed != urb->transfer_flags)
        dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
            urb->transfer_flags, allowed);
    }
#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).
     */

    switch (xfertype) {
    //是关于interval的,所以只涉及ISO和INT
    case USB_ENDPOINT_XFER_ISOC:
    case USB_ENDPOINT_XFER_INT:
        /* too small? */
        switch (dev->speed) {
        case USB_SPEED_WIRELESS:
            if (urb->interval < 6)
                return -EINVAL;
            break;
        default:
            if (urb->interval <= 0)
                return -EINVAL;
            break;
        }
        /* too big? */
        switch (dev->speed) {
        case USB_SPEED_SUPER:    /* units are 125us */
            /* Handle up to 2^(16-1) microframes */
            if (urb->interval > (1 << 15))
                return -EINVAL;
            max = 1 << 15;
            break;
        case USB_SPEED_WIRELESS:
            if (urb->interval > 16)
                return -EINVAL;
            break;
        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;
        }
        if (dev->speed != USB_SPEED_WIRELESS) {
            /* 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);
}
EXPORT_SYMBOL_GPL(usb_submit_urb);

/*-------------------------------------------------------------------------*/

/* may be called in any context with a valid urb->dev usecount
 * caller surrenders "ownership" of urb
 * expects usb_submit_urb() to have sanity checked and conditioned all
 * inputs in the urb
 */

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
    int            status;
    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.
     */

    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.
     */


    if (is_root_hub(urb->dev)) {
        status = rh_urb_enqueue(hcd, urb);
    } else {
        status = map_urb_for_dma(hcd, urb, mem_flags);
        if (likely(status == 0)) {
            status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
            if (unlikely(status))
                unmap_urb_for_dma(hcd, urb);
        }
    }

    if (unlikely(status)) {
        usbmon_urb_submit_error(&hcd->self, urb, status);
        urb->hcpriv = NULL;
        INIT_LIST_HEAD(&urb->urb_list);
        atomic_dec(&urb->use_count);
        atomic_dec(&urb->dev->urbnum);
        if (atomic_read(&urb->reject))
            wake_up(&usb_kill_urb_queue);
        usb_put_urb(urb);
    }
    return status;
}


原创粉丝点击