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調用的函數。