usb控制器与usb设备之间是通过urb结构来传递数据,urb是usb通信基础。
驱动在使用urb之前要先通过usb_alloc_urb来创建struct urb结构,并通过usb_fill_xx_urb来初始化创建的urb,然后通过usb_submit_urb把urb提交给主控制器,由控制器进行实际发送,发送完给控制器把urb所有权交还给驱动,并可通过回调函数获取urb发送状况。
在申请和初始化urb时, 对于不同usb接口标准(ohci,uhci,ehci),是没有区别的. 由于各usb接口标准在其硬件和软件功能分工上存在差异,如uhci在硬件实现逻辑上比较简单,而软件驱动实现比较复杂,ohci硬件逻辑比较复杂,软件实现相对简单,所以 对于不同标准类型的usb_submit_urb低层实现是不同的,其实现是与接口标准相关联的。本文研究对象是在嵌入式系统中应用得比较广泛的ohci接口标准,通过分析ohci的usb_submit_urb来掌握ohci控制器与驱动的任务分工,ohci对于urb处理机制,cpu是samsung的s3c6410.
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_UNAUTHENTICATED))
return -ENODEV;
ep = usb_pipe_endpoint(dev, urb->pipe);
if (!ep)
return -ENOENT;
urb->ep = ep;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
xfertype = usb_endpoint_type(&ep->desc);
if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
struct usb_ctrlrequest *setup =
(struct usb_ctrlrequest *) urb->setup_packet;
if (!setup)
return -ENOEXEC;
is_out = !(setup->bRequestType & USB_DIR_IN) ||
!setup->wLength;
} else {
is_out = usb_endpoint_dir_out(&ep->desc);
}
/* 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);
urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);
if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
dev->state < USB_STATE_CONFIGURED)
return -ENODEV;
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",
__func__, max);
return -EMSGSIZE;
}
if (xfertype == USB_ENDPOINT_XFER_ISOC) {
int n, len;
if (dev->speed == USB_SPEED_SUPER) {
int burst = 1 + ep->ss_ep_comp.bMaxBurst;
int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
max *= burst;
max *= mult;
}
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
max &= 0x07ff;
max *= mult;
}
if (urb->number_of_packets <= 0)
return -EINVAL;
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;
}
}
if (urb->transfer_buffer_length > INT_MAX)
return -EMSGSIZE;
switch (xfertype) {
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
switch (dev->speed) {
case USB_SPEED_WIRELESS:
if (urb->interval < 6)
return -EINVAL;
break;
default:
if (urb->interval <= 0)
return -EINVAL;
break;
}
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 */
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);
}
1-5行 进行了一些基本逻辑判断,urb为提交给hc的数据结构,当然不能为空,而hcpriv是在hcd中初始化的,这里应该为空,complete为回调函数,urb结束就必须运行的,所以必须定义;
7-9行 usb_dev结构中存放着IN,OUT类型的endpoint指针数组,通过urb的pipe来获取endpoint的方向和端口,来确定要接收urb的endpoint.
15-26行 对于控制类型的urb除要发送正常数据外,还得包含request请求包,并通过endpoint desc来确定端口的方向。
29-33行 清除并设置flags标准。
39-46行 获取端口最大发送数据长度。
47-72行 对于高速和超速的等时传输一帧或微帧可传2~3次transaction,从端口的desc属性里获取其最长发送长度后对等时传输数据长度进行判断,并初始化。
75-125行 对于interrupt,isochronous传输类型还得根据usb协议确定访问endpoint list间隔,对于高速,端点desc里的wmaxpacketsize中的bit11,1用来确定可以额外几次transaction次数,如果00表示没有额外传输,01表示有1次,10表示2次,11保留。
127行 调用usb_hcd_submit_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);
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
usbmon_urb_submit(&hcd->self, urb);
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;
}
6-9行 对urb结构中一些计数器进行初始化。
11-20行 根据urb是传送给root hub还是HC选择不同运行路经,如果是传向root hub则运行rh_urb_enqueue,否则运行与控制器相关的urb_enqueue.
22-31行,对urb提交结果进行判断,如果提交失败,则清除相应urb及里面一些参数。
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;
}
a. 中断类型hub root endpoint
hub是一种承上启下的usb设备,与控制器相连的叫root hub,root hub除了本身的endpoint0之外还有一个interrupt类型的endpoint,该端口用于检测root hub端口设备插入或拔出。
static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
{
int retval;
unsigned long flags;
unsigned len = 1 + (urb->dev->maxchild / 8);
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/4) + 1) * (HZ/4));
/* If a status change has already occurred, report it ASAP */
else if (HCD_POLL_PENDING(hcd))
mod_timer(&hcd->rh_timer, jiffies);
retval = 0;
done:
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
return retval;
}
中断类型的端口用于监控 root hub上是否有设备插入,root hub通过两种方式来监控是否有设备插入,一种是通过中断方式,使能相应中断,当状态发生变化时产生中断,然后运行中断服务程序获取取状态寄存器信息;另一种方式是:当提交root hub中的interrupt endpoint上的urb时,便会启动用于查询port状态的定时器rh_timer,当定时时间到后就会运行定时器处理函数rh_timer_func。
14行把当前urb添加到endpoint对应urb列表中。
20-25行,设置定时器rh_timer的定时时间,当uses_new_polling为0时,设置定时超时时间为250ms,否则设置当前时间为超时时间,直接运行定时器function函数rh_timer_func。
不管是通过中断方式还是通过主动提交root hub状态查询urb方式 ,最终都是通过root hub中interrupt的urb来返回port状态,它通过调用usb_hcd_poll_rh_status来实现。
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
struct urb *urb;
int length;
unsigned long flags;
char buffer[6]; /* Any root hubs with > 31 ports? */
if (unlikely(!hcd->rh_pollable))
return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
length = hcd->driver->hub_status_data(hcd, buffer);
if (length > 0) {
spin_lock_irqsave(&hcd_root_hub_lock, flags);
urb = hcd->status_urb;
if (urb) {
clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
hcd->status_urb = NULL;
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock(&hcd_root_hub_lock);
usb_hcd_giveback_urb(hcd, urb, 0);
spin_lock(&hcd_root_hub_lock);
} else {
length = 0;
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
}
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
}
if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) : (length == 0 && hcd->status_urb != NULL))
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
13行 通过调用控制器驱动hub_status_data(ohci_s3c2410_hub_status_data)函数来获取状态信息,状态信息通过buffer返回,如成功获取root hub状态信息,由调用urb回调函数hub_irq,然后根据urb返回结果设置hub中event_bits位,如果相应端口产生变化则置event_bits为1,并通过kick_khubd唤醒usb的守护进程hub_thread.
34、35行根据uses_new_polling和hub状态来设置轮训查询定时时间,本文采用ohci型控制器,在ohci初始化过程中设置了uses_new_pollling为1,且root处于running态,所以设置定时轮询时间为250ms。
static int ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);
struct s3c2410_hcd_port *port;
int orig;
int portno;
orig = ohci_hub_status_data(hcd, buf);
if (info == NULL)
return orig;
port = &info->port[0];
for (portno = 0; portno < 2; port++, portno++) {
if (port->oc_changed == 1 &&
port->flags & S3C_HCDFLG_USED) {
dev_dbg(hcd->self.controller,
"oc change on port %d\n", portno);
if (orig < 1)
orig = 1;
buf[0] |= 1<<(portno+1);
}
}
return orig;
}
s3c6410usb控制器属于ohci接口标准,所以通过ohc_hub_status_data来读取hub信息。
static int ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
int any_connected = 0;
int rhsc_status;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
if (!HCD_HW_ACCESSIBLE(hcd))
goto done;
if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
buf [0] = changed = 1;
else
buf [0] = 0;
if (ohci->num_ports > 7) {
buf [1] = 0;
length++;
}
ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus);
rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC;
for (i = 0; i < ohci->num_ports; i++) {
u32 status = roothub_portstatus (ohci, i);
any_connected |= (status & RH_PS_CCS);
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
}
}
if (ohci_root_hub_state_changes(ohci, changed,any_connected, rhsc_status))
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
else
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
done:
spin_unlock_irqrestore (&ohci->lock, flags);
return changed ? length : 0;
}
13行中roothub_status()用来读取roothub寄存器进而的status标志;
static inline u32 roothub_status (struct ohci_hcd *hc)
{
return ohci_readl (hc, &hc->regs->roothub.status);
}
roothub.status中主要包含:localpowerstatus, overcurrentidicator, deviceremotewakeupenable, localpowerstatuschange, overcurrentindicatorchange, clearremotewakeupenable.这里主要关注localpowerstatuschange和overcurrentindicatorchange,前者由于ohci root hub不支持local power status feature,所以读出来永远为“0”,后者表示过流状态是否发生变化。
13-20行通过roothub_status来获取root hub本身状态没有发生变化,设置并设初始化用来表示各个端口状态的变量buf,buf[0]的bit0用来表示root hub自身状态,剩下的7位用来表示port1-port6状态,如果root hub下的接的设备数量num_ports大于7,则还需要buf[1]来表示端口状态;
25-37行用来读取root hub下各port的状态,如果发生变化,则设置相应位。
回到usb_hcd_poll_rh_status的14行,如果返回长度大于0,则设置urb的真实长度,并调用回调函数hub_irq。
只要root hub上有设备插入或拔出,通过中断和轮询机制,usb系统就能知道,并运行相应服务程序。
b. 控制类型root hub endpoint
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;
unsigned len = 0;
int status;
u8 patch_wakeup = 0;
u8 patch_protocol = 0;
might_sleep();
spin_lock_irq(&hcd_root_hub_lock);
status = usb_hcd_link_urb_to_ep(hcd, urb);
spin_unlock_irq(&hcd_root_hub_lock);
if (status)
return status;
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 */
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:
switch (hcd->speed) {
case HCD_USB3:
bufp = usb3_rh_dev_descriptor;
break;
case HCD_USB2:
bufp = usb2_rh_dev_descriptor;
break;
case HCD_USB11:
bufp = usb11_rh_dev_descriptor;
break;
default:
goto error;
}
len = 18;
if (hcd->has_tt)
patch_protocol = 1;
break;
case USB_DT_CONFIG << 8:
switch (hcd->speed) {
case HCD_USB3:
bufp = ss_rh_config_descriptor;
len = sizeof ss_rh_config_descriptor;
break;
case HCD_USB2:
bufp = hs_rh_config_descriptor;
len = sizeof hs_rh_config_descriptor;
break;
case HCD_USB11:
bufp = fs_rh_config_descriptor;
len = sizeof fs_rh_config_descriptor;
break;
default:
goto error;
}
if (device_can_wakeup(&hcd->self.root_hub->dev))
patch_wakeup = 1;
break;
case USB_DT_STRING << 8:
if ((wValue & 0xff) < 4)
urb->actual_length = rh_string(wValue & 0xff,hcd, ubuf, wLength);
else /* unsupported IDs --> "protocol stall" */
goto error;
break;
default:
goto error;
}
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
tbuf [0] = 0;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
// wValue == urb->dev->devaddr
dev_dbg (hcd->self.controller, "root hub device address %d\n",
wValue);
break;
/* ENDPOINT REQUESTS */
case EndpointRequest | USB_REQ_GET_STATUS:
// ENDPOINT_HALT flag
tbuf [0] = 0;
tbuf [1] = 0;
len = 2;
/* FALLTHROUGH */
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
case EndpointOutRequest | USB_REQ_SET_FEATURE:
dev_dbg (hcd->self.controller, "no endpoint features yet\n");
break;
default:
/* non-generic request */
switch (typeReq) {
case GetHubStatus:
case GetPortStatus:
len = 4;
break;
case GetHubDescriptor:
len = sizeof (struct usb_hub_descriptor);
break;
}
status = hcd->driver->hub_control (hcd,typeReq, wValue, wIndex, tbuf, wLength);
break;
error:
/* "protocol stall" on error */
status = -EPIPE;
}
if (status)
len = 0;
if (len) {
if (urb->transfer_buffer_length < len)
len = urb->transfer_buffer_length;
urb->actual_length = len;
memcpy (ubuf, bufp, len);
/* report whether RH hardware supports remote wakeup */
if (patch_wakeup && len > offsetof (struct usb_config_descriptor, bmAttributes))
((struct usb_config_descriptor *)ubuf)->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
/* report whether RH hardware has an integrated TT */
if (patch_protocol && len > offsetof(struct usb_device_descriptor,bDeviceProtocol))
((struct usb_device_descriptor *) ubuf)->bDeviceProtocol = 1;
}
spin_lock_irq(&hcd_root_hub_lock);
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock(&hcd_root_hub_lock);
usb_hcd_giveback_urb(hcd, urb, status);
spin_lock(&hcd_root_hub_lock);
spin_unlock_irq(&hcd_root_hub_lock);
return 0;
}
该函数比较长,函数主要作用就是根据usb协议,设置不同request tbuf内容及长度,最后调用控制器驱动中的hub_control。
static int ohci_s3c2410_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
{
struct s3c2410_hcd_info *info = to_s3c2410_info(hcd);
struct usb_hub_descriptor *desc;
int ret = -EINVAL;
u32 *data = (u32 *)buf;
if (info == NULL) {
ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
goto out;
}
switch (typeReq) {
case SetPortFeature:
if (wValue == USB_PORT_FEAT_POWER) {
dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");
s3c2410_usb_set_power(info, wIndex, 1);
goto out;
}
break;
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_C_OVER_CURRENT:
dev_dbg(hcd->self.controller,"ClearPortFeature: C_OVER_CURRENT\n");
if (valid_port(wIndex)) {
info->port[wIndex-1].oc_changed = 0;
info->port[wIndex-1].oc_status = 0;
}
goto out;
case USB_PORT_FEAT_OVER_CURRENT:
dev_dbg(hcd->self.controller, "ClearPortFeature: OVER_CURRENT\n");
if (valid_port(wIndex))
info->port[wIndex-1].oc_status = 0;
goto out;
case USB_PORT_FEAT_POWER:
dev_dbg(hcd->self.controller, "ClearPortFeature: POWER\n");
if (valid_port(wIndex)) {
s3c2410_usb_set_power(info, wIndex, 0);
return 0;
}
}
break;
}
ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
if (ret)
goto out;
switch (typeReq) {
case GetHubDescriptor:
desc = (struct usb_hub_descriptor *)buf;
if (info->power_control == NULL)
return ret;
dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n",desc->wHubCharacteristics);
/* remove the old configurations for power-switching, and
* over-current protection, and insert our new configuration
*/
desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
desc->wHubCharacteristics |= cpu_to_le16(0x0001);
if (info->enable_oc) {
desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);
desc->wHubCharacteristics |= cpu_to_le16(0x0008 |0x0001);
}
dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", desc->wHubCharacteristics);
return ret;
case GetPortStatus:
if (valid_port(wIndex)) {
if (info->port[wIndex-1].oc_changed)
*data |= cpu_to_le32(RH_PS_OCIC);
if (info->port[wIndex-1].oc_status)
*data |= cpu_to_le32(RH_PS_POCI);
}
}
out:
return ret;
}
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 = 0;
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);
#ifndef OHCI_VERBOSE_DEBUG
if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
#endif
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:
#ifdef CONFIG_USB_OTG
if (hcd->self.otg_port == (wIndex + 1)
&& hcd->self.b_hnp_enable)
ohci->start_hnp(ohci);
else
#endif
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;
}
ohci_hub_control函数对ClearHubFeature,ClearPortFeature,GetHubDescriptor,GetHubStatus,GetPortStatus,SetHubFeature,SetPortFeature请求给出来默认的操作方式。
17-28行处理ClearHubFeature请求,至于是清除什么feature由request中的wvalue决定,其支持的类型在usb 协议table11-17 Hub Class Feature Selectorss都有定义,对于清除C_HUB_OVER_CURRENT,可以通过操作ohci协议中定义的HcRhStatus Register来完成。
85-119中设备hub或port的feature也可以通过设置ohci中的HcRhStatus Register和HcRhPortStatus Register完成 。