天天看点

USB core(一) - rh_queue_status与rh_call_controlrh_call_controlrh_queue_status

rh_call_control

为了说明rh_call_control,先来看看是如何通过usb_control_msg,接着usb_submit_urb,然后rh_urb_enqueue,最终在调用rh_call_control的过程。

hub信息获取函数

get_hub_descriptor(struct usb_device *hdev, void *data)
    usb_control_msg(hdev, usb_rcvctrlpipe(hdev, ),
                        USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
                        dtype << , , data, size,
                        USB_CTRL_GET_TIMEOUT);

clear_hub_feature(struct usb_device *hdev, int feature)
    usb_control_msg(hdev, usb_sndctrlpipe(hdev, ),
                USB_REQ_CLEAR_FEATURE, usb_control_msg, feature, , NULL, , );

usb_clear_port_feature(struct usb_device *hdev, int port1, int feature)
    usb_control_msg(hdev, usb_sndctrlpipe(hdev, ),
                USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
                NULL, , );

set_port_feature(struct usb_device *hdev, int port1, int feature)
    usb_control_msg(hdev, usb_sndctrlpipe(hdev, ),
                USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
                NULL, , );
           

以上是hub的一些控制传输,基本都通过usb_control_msg来获取信息,面向两个对象USB_RT_HUB和USB_RT_PORT,来看看usb_control_msg

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(, 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 < )
                return retv;
        else
                return length;
}

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

        ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
        kfree(dr);
        return ret;
}
           

usb_control_msg将8个字节的request信息填写到结构体struct usb_ctrlrequest

usb_internal_control_msg将这个usb_ctrlrequest与data赋值给urb,最终通过usb_start_wait_urb提交urb,在usb_start_wait_urb中调用了usb_submit_urb

usb_submit_urb

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
    usb_hcd_submit_urb(urb, mem_flags);
        if (is_root_hub(urb->dev)) {
                    status = rh_urb_enqueue(hcd, urb);
            } else {
                status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
        }
           

由于这里提交urb的是hub,所以会执行rh_urb_enqueue

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的数据传输,一般分为两部分,int传输和control传输,这个int传输是虚构的int端点的传输,就是起一个rh_timer,然后调用usb_hcd_poll_rh_status不停的polling roothub的status,int传输放到后面再讲

这里说明control传输。

rh_call_control

static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{
    struct usb_ctrlrequest *cmd;
        u16             typeReq, wValue, wIndex, wLength;

    cmd = (struct usb_ctrlrequest *) urb->setup_packet;
        typeReq  = (cmd->bRequestType << ) | cmd->bRequest;
        wValue   = le16_to_cpu (cmd->wValue);
        wIndex   = le16_to_cpu (cmd->wIndex);
        wLength  = le16_to_cpu (cmd->wLength);

    switch (typeReq) {
    case DeviceRequest | USB_REQ_GET_STATUS: ...
    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: ...
    case DeviceOutRequest | USB_REQ_SET_FEATURE:
    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
    ...
    case EndpointRequest | USB_REQ_GET_STATUS: ...
    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: ...
    case EndpointOutRequest | USB_REQ_SET_FEATURE: ...
    ...
    /* CLASS REQUESTS (and errors) */
    default:
        status = hcd->driver->hub_control (hcd,
                        typeReq, wValue, wIndex,
                        tbuf, wLength);
}
           

如果是standard request则rh_call_control根据不同的case对所需的情况赋值,而如果是class specific的request,则会调用底层提供的hub_control函数来获取hub的信息。

当底层调用hub_control函数的时候,之前传递的USB_RT_HUB和USB_RT_PORT就派上用场了,

ohci_hub_control

#define USB_RT_HUB  (USB_TYPE_CLASS | USB_RECIP_DEVICE) //0x20
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) //0x23

//上面的是requestType,左移8位和request相与之后得到下面
#define ClearHubFeature         (0x2000 | USB_REQ_CLEAR_FEATURE)
#define ClearPortFeature        (0x2300 | USB_REQ_CLEAR_FEATURE)
#define GetHubDescriptor        (0xa000 | USB_REQ_GET_DESCRIPTOR)
#define GetHubStatus            (0xa000 | USB_REQ_GET_STATUS)
#define GetPortStatus           (0xa300 | USB_REQ_GET_STATUS)
#define SetHubFeature           (0x2000 | USB_REQ_SET_FEATURE)
#define SetPortFeature          (0x2300 | USB_REQ_SET_FEATURE)

static int ohci_hub_control (
    struct usb_hcd  *hcd,
    u16     typeReq,
    u16     wValue,
    u16     wIndex,
    char        *buf,
    u16     wLength
) {
    struct ohci_hcd *ohci = hcd_to_ohci (hcd);
    int     ports = ohci->num_ports;
    u32     temp;
    int     retval = ;

    if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
        return -ESHUTDOWN;

    switch (typeReq) {
    case ClearHubFeature:
        switch (wValue) {
        case C_HUB_OVER_CURRENT:
            ohci_writel (ohci, RH_HS_OCIC,
                    &ohci->regs->roothub.status);
        case C_HUB_LOCAL_POWER:
            break;
        default:
            goto error;
        }
        break;
    case ClearPortFeature:
        if (!wIndex || wIndex > ports)
            goto error;
        wIndex--;

        switch (wValue) {
        case USB_PORT_FEAT_ENABLE:
            temp = RH_PS_CCS;
            break;
        case USB_PORT_FEAT_C_ENABLE:
            temp = RH_PS_PESC;
            break;
        case USB_PORT_FEAT_SUSPEND:
            temp = RH_PS_POCI;
            break;
        case USB_PORT_FEAT_C_SUSPEND:
            temp = RH_PS_PSSC;
            break;
        case USB_PORT_FEAT_POWER:
            temp = RH_PS_LSDA;
            break;
        case USB_PORT_FEAT_C_CONNECTION:
            temp = RH_PS_CSC;
            break;
        case USB_PORT_FEAT_C_OVER_CURRENT:
            temp = RH_PS_OCIC;
            break;
        case USB_PORT_FEAT_C_RESET:
            temp = RH_PS_PRSC;
            break;
        default:
            goto error;
        }
        ohci_writel (ohci, temp,
                &ohci->regs->roothub.portstatus [wIndex]);
        // ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);
        break;
    case GetHubDescriptor:
        ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
        break;
    case GetHubStatus:
        temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
        put_unaligned_le32(temp, buf);
        break;
    case GetPortStatus:
        if (!wIndex || wIndex > ports)
            goto error;
        wIndex--;
        temp = roothub_portstatus (ohci, wIndex);
        put_unaligned_le32(temp, buf);

        dbg_port (ohci, "GetStatus", wIndex, temp);
        break;
    case SetHubFeature:
        switch (wValue) {
        case C_HUB_OVER_CURRENT:
            // FIXME:  this can be cleared, yes?
        case C_HUB_LOCAL_POWER:
            break;
        default:
            goto error;
        }
        break;
    case SetPortFeature:
        if (!wIndex || wIndex > ports)
            goto error;
        wIndex--;
        switch (wValue) {
        case USB_PORT_FEAT_SUSPEND:
            ohci_writel (ohci, RH_PS_PSS,
                &ohci->regs->roothub.portstatus [wIndex]);
            break;
        case USB_PORT_FEAT_POWER:
            ohci_writel (ohci, RH_PS_PPS,
                &ohci->regs->roothub.portstatus [wIndex]);
            break;
        case USB_PORT_FEAT_RESET:
            retval = root_port_reset (ohci, wIndex);
            break;
        default:
            goto error;
        }
        break;

    default:
error:
        /* "protocol stall" on error */
        retval = -EPIPE;
    }
    return retval;
}
           

rh_queue_status

static void rh_timer_func (unsigned long _hcd)
{
        usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
}

static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
{
        int             retval;    
        unsigned long   flags;     
        unsigned        len =  + (urb->dev->maxchild / ); 

        spin_lock_irqsave (&hcd_root_hub_lock, flags);
        if (hcd->status_urb || urb->transfer_buffer_length < len) {
                dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
                retval = -EINVAL;  
                goto done;         
        }

        retval = usb_hcd_link_urb_to_ep(hcd, urb); 
        if (retval)                
                goto done;         

        hcd->status_urb = urb;     
        urb->hcpriv = hcd;      /* indicate it's queued */
        if (!hcd->uses_new_polling)
                mod_timer(&hcd->rh_timer, (jiffies/(HZ/) + ) * (HZ/));

        /* If a status change has already occurred, report it ASAP */
        else if (HCD_POLL_PENDING(hcd))     
                mod_timer(&hcd->rh_timer, jiffies); 
        retval = ;
 done: 
        spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
        return retval;             
}

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;
}
           

int端点是虚拟的中断端点,driver通过他不断去polling hub的状态,usb_hcd_poll_rh_status就是这个polling调用的函数。