uhci_hub_control在/drivers/usb/host/uhci-hcd.c中
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); int status, lstatus, retval = 0, len = 0; unsigned int port = wIndex - 1; unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; u16 wPortChange, wPortStatus; unsigned long flags; if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) return -ETIMEDOUT; spin_lock_irqsave(&uhci->lock, flags); switch (typeReq) { //获取集线器状态 case GetHubStatus: *(__le32 *)buf = cpu_to_le32(0); OK(4); /* hub power */ //获取端口状态 case GetPortStatus: //要求的端口号大于实际集线器有的端口数 if (port >= uhci->rh_numports) goto err; uhci_check_ports(uhci); //获得对应的端口地址的寄存器数据 status = inw(port_addr); /* Intel controllers report the OverCurrent bit active on. * VIA controllers report it active off, so we'll adjust the * bit value. (It's not standardized in the UHCI spec.) */ //检测是否为VIA的产品 if (to_pci_dev(hcd->self.controller)->vendor == PCI_VENDOR_ID_VIA) status ^= USBPORTSC_OC; /* UHCI doesn't support C_RESET (always false) */ wPortChange = lstatus = 0; //检测连接状态改变位 if (status & USBPORTSC_CSC) wPortChange |= USB_PORT_STAT_C_CONNECTION; //检测端口有效状态改变位 if (status & USBPORTSC_PEC) wPortChange |= USB_PORT_STAT_C_ENABLE; //检测过电保护改变位 if ((status & USBPORTSC_OCC) && !ignore_oc) wPortChange |= USB_PORT_STAT_C_OVERCURRENT; //检测端口悬挂位 if (test_bit(port, &uhci->port_c_suspend)) { wPortChange |= USB_PORT_STAT_C_SUSPEND; lstatus |= 1; } //检测端口恢复位 if (test_bit(port, &uhci->resuming_ports)) lstatus |= 4; /* UHCI has no power switching (always on) */ //设置端口供电状态为正常 wPortStatus = USB_PORT_STAT_POWER; //检测当前连接状态位 if (status & USBPORTSC_CCS) wPortStatus |= USB_PORT_STAT_CONNECTION; //检测端口有效位 if (status & USBPORTSC_PE) { wPortStatus |= USB_PORT_STAT_ENABLE; //检测端口挂起/恢复位 if (status & SUSPEND_BITS) wPortStatus |= USB_PORT_STAT_SUSPEND; } //检测电源超载位 if (status & USBPORTSC_OC) wPortStatus |= USB_PORT_STAT_OVERCURRENT; //检测端口复位位 if (status & USBPORTSC_PR) wPortStatus |= USB_PORT_STAT_RESET; //检测端口连接的设备是否为低速设备 if (status & USBPORTSC_LSDA) wPortStatus |= USB_PORT_STAT_LOW_SPEED; if (wPortChange) dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",wIndex, status, lstatus); //将状态信息复制到buf的前2字节 *(__le16 *)buf = cpu_to_le16(wPortStatus); //将状态改变信息复制到buf的后2字节 *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange); OK(4); case SetHubFeature: /* We don't implement these */ case ClearHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: OK(0); default: goto err; } break; case SetPortFeature: if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_SUSPEND: SET_RH_PORTSTAT(USBPORTSC_SUSP); OK(0); case USB_PORT_FEAT_RESET: //端口发送复位信号 SET_RH_PORTSTAT(USBPORTSC_PR); /* Reset terminates Resume signalling */ //停止发送恢复信号 uhci_finish_suspend(uhci, port, port_addr); /* USB v2.0 7.1.7.5 */ uhci->ports_timeout = jiffies + msecs_to_jiffies(50); OK(0); case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ OK(0); default: goto err; } break; case ClearPortFeature: //检测端口号是否大于集线器的端口数 if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PE); /* Disable terminates Resume signalling */ uhci_finish_suspend(uhci, port, port_addr); OK(0); case USB_PORT_FEAT_C_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: if (!(inw(port_addr) & USBPORTSC_SUSP)) { /* Make certain the port isn't suspended */ uhci_finish_suspend(uhci, port, port_addr); } else if (!test_and_set_bit(port,&uhci->resuming_ports)) { SET_RH_PORTSTAT(USBPORTSC_RD); /* The controller won't allow RD to be set * if the port is disabled. When this happens * just skip the Resume signalling. */ if (!(inw(port_addr) & USBPORTSC_RD)) uhci_finish_suspend(uhci, port, port_addr); else /* USB v2.0 7.1.7.7 */ uhci->ports_timeout = jiffies + msecs_to_jiffies(20); } OK(0); case USB_PORT_FEAT_C_SUSPEND: clear_bit(port, &uhci->port_c_suspend); OK(0); case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ goto err; case USB_PORT_FEAT_C_CONNECTION: CLR_RH_PORTSTAT(USBPORTSC_CSC); OK(0); case USB_PORT_FEAT_C_OVER_CURRENT: CLR_RH_PORTSTAT(USBPORTSC_OCC); OK(0); case USB_PORT_FEAT_C_RESET: /* this driver won't report these */ OK(0); default: goto err; } break; case GetHubDescriptor: len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); memcpy(buf, root_hub_hub_des, len); if (len > 2) buf[2] = uhci->rh_numports; OK(len); default: err: retval = -EPIPE; } spin_unlock_irqrestore(&uhci->lock, flags); return retval; }
|
这里又是一个大的switch,这次我们的case是GetHubDescriptor,得到集线器描述符root_hub_hub_des,它的结构如下
static __u8 root_hub_hub_des[] =
{
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */
0x0a, /* __u16 wHubCharacteristics; */
0x00, /* (per-port OC, no power switching) */
0x01, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */
0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
};
好~ 获取集线器描述符的任务就完成了,现在到usb_get_status
usb_get_status在/drivers/usb/core/messgae.c中
int usb_get_status(struct usb_device *dev, int type, int target, void *data) { int ret; u16 *status = kmalloc(sizeof(*status), GFP_KERNEL); if (!status) return -ENOMEM; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status, sizeof(*status), USB_CTRL_GET_TIMEOUT); *(u16 *)data = *status; kfree(status); return ret; }
|
又是usb_control_msg,我们又来到了rh_call_control中,这次的case为DeviceRequest | USB_REQ_GET_STATUS,去了一些关于电源的参数(我也不清楚这些电源参数是干什么的,就不分析了)
hub_hub_status取得集线器的状态
hub_hub_status在/drivers/usb/core/hub.c中
static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change) { int ret; mutex_lock(&hub->status_mutex); //取得集线器状态 ret = get_hub_status(hub->hdev, &hub->status->hub); if (ret < 0) dev_err (hub->intfdev,"%s failed (err = %d)\n", __func__, ret); else { *status = le16_to_cpu(hub->status->hub.wHubStatus); *change = le16_to_cpu(hub->status->hub.wHubChange); ret = 0; } mutex_unlock(&hub->status_mutex); return ret; }
|
get_hub_status在/drivers/usb/core/hub.c中
static int get_hub_status(struct usb_device *hdev, struct usb_hub_status *data) { int i, status = -ETIMEDOUT; for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) { status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, data, sizeof(*data), USB_STS_TIMEOUT); } return status; }
|
usb_control_msg又见面了,穿过rh_call_control,再临uhci_hub_control,这次的case为GetHubStatus, *(__le32 *)buf = cpu_to_le32(0); 赏了全为0的数据..... 白跑一趟啊
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
#define usb_rcvintpipe(dev,endpoint) \
((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
usb_rcvintpipe是一个宏,它的作用是生成一个中断管道,注意看,是中断
然后把得到的管道赋到了urb上,还有就是这里的complete为hub_irq
接着到hub_power_on
hub_power_on在/drivers/usb/core/hub.c中
static void hub_power_on(struct usb_hub *hub) { int port1; unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); /* Enable power on each port. Some hubs have reserved values * of LPSM (> 2) in their descriptors, even though they are * USB 2.0 hubs. Some hubs do not implement port-power switching * but only emulate it. In all cases, the ports won't work * unless we send these messages to the hub. */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) dev_dbg(hub->intfdev, "enabling power on all ports\n"); else dev_dbg(hub->intfdev, "trying to enable port power on " "non-switchable hub\n"); //历遍端口 for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) //设置端口的灵巧电源分配 set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); /* Wait at least 100 msec for power to become stable */ msleep(max(pgood_delay, (unsigned) 100)); }
|
set_port_feature在/drivers/usb/core/hub.c中
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, 0, 1000); }
|
usb_control_msg.......... 自觉来到rh_call_control中并跳到uhci_hub_control吧,这次的case为SetPortFeature,什么特性呢? USB_PORT_FEAT_POWER特性
/* UHCI has no power switching */
噢~ 太好了,啥也不用做,返回~
到hub_activate
hub_activate在/drivers/usb/core/hub.c中
static void hub_activate(struct usb_hub *hub) { int status; hub->quiescing = 0; hub->activating = 1; //发送中断urb status = usb_submit_urb(hub->urb, GFP_NOIO); if (status < 0) dev_err(hub->intfdev, "activate --> %d\n", status);
if (hub->has_indicators && blinkenlights) schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); /* scan all ports ASAP */ kick_khubd(hub); }
|
发送中断urb,来到rh_queue_status中,咦? rh_queue_status在哪?看rh_urb_enqueue,在rh_call_control的上面,为什么到rh_queue_status中了呢?还记得前面提到的管道么,这次给urb的是中断类型的管道,所以到了rh_queue_status中
rh_queue_status在/drivers/usb/core/hcd.c中
static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) { int retval; unsigned long flags; int 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; } //连接urb到端点 retval = usb_hcd_link_urb_to_ep(hcd, urb); if (retval) goto done; //连接urb到主机控制器驱动上 hcd->status_urb = 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) mod_timer(&hcd->rh_timer, jiffies); retval = 0; done: spin_unlock_irqrestore (&hcd_root_hub_lock, flags); return retval; }
|
执行完之后到了kick_khubd,关于kick_khubd现在我保密,在之后的文中解谜 = 3=
现在hub_driver的probe终于执行完了........
usb_generic_driver的probe也终于执行完了..............
usb_new_device也终于执行完了
register_root_hub也终于执行完了
跳回到usb_add_hcd中,现在只剩下最后一个函数usb_hcd_poll_rh_status了
usb_hcd_poll_rh_status是主机控制器驱动中重要的函数之一,这个函数负责轮询根集线器的端口,有设备了嘛? 有设备了嘛? 有设备了嘛?........... 直到关闭系统~
现在就来看看usb_hcd_poll_rh_status
usb_hcd_poll_rh_status在/drivers/usb/core/hcd.c中
void usb_hcd_poll_rh_status(struct usb_hcd *hcd) { struct urb *urb; int length; unsigned long flags; char buffer[4]; /* Any root hubs with > 31 ports? */ //检测主机控制器驱动是否已经注册 if (unlikely(!hcd->rh_registered)) return; if (!hcd->uses_new_polling && !hcd->status_urb) return; //进行集线器设备状态检测 length = hcd->driver->hub_status_data(hcd, buffer); //端口有设备 if (length > 0) { /* try to complete the status urb */ spin_lock_irqsave(&hcd_root_hub_lock, flags); urb = hcd->status_urb; //检测urb是否存在 if (urb) { hcd->poll_pending = 0; //清除hcd的状态urb hcd->status_urb = NULL; //置实际传输长度为1 urb->actual_length = length; //拷贝端口状态描述组到urb中 memcpy(urb->transfer_buffer, buffer, length); //卸载urb与节点的连接 usb_hcd_unlink_urb_from_ep(hcd, urb); spin_unlock(&hcd_root_hub_lock); //返回urb给驱动程序 usb_hcd_giveback_urb(hcd, urb, 0); spin_lock(&hcd_root_hub_lock); } else { length = 0; hcd->poll_pending = 1; } spin_unlock_irqrestore(&hcd_root_hub_lock, flags); } /* The USB 2.0 spec says 256 ms. This is close enough and won't * exceed that limit if HZ is 100. The math is more clunky than * maybe expected, this is to make sure that all timers for USB devices * fire at the same time to give the CPU a break inbetween */ //每间隔HZ/4就执行一次hcd->rh_timer中指定的函数 if (hcd->uses_new_polling ? hcd->poll_rh :(length == 0 && hcd->status_urb != NULL)) mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); }
|
hcd->driver->hub_status_data检测集线器的端口状态,uhci中对应的函数为uhci_hub_status_data
uhci_hub_status_data在/drivers/usb/host/uhci-hub.c中
static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { //取得uhci_hcd结构 struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; int status = 0; spin_lock_irqsave(&uhci->lock, flags); //调度uhci中的帧队列 uhci_scan_schedule(uhci); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) goto done; //检测端口 uhci_check_ports(uhci); //获得端口状态 status = get_hub_status_data(uhci, buf); //检测根集线器的状态 switch (uhci->rh_state) { case UHCI_RH_SUSPENDING: case UHCI_RH_SUSPENDED: /* if port change, ask to be resumed */ if (status) //唤醒根集线器 usb_hcd_resume_root_hub(hcd); break; case UHCI_RH_AUTO_STOPPED: /* if port change, auto start */ if (status) //唤醒主机控制器 wakeup_rh(uhci); break; //uhci的状态为运行 case UHCI_RH_RUNNING: /* are any devices attached? */ //检测是否有连接的设备 if (!any_ports_active(uhci)) { //改变uhci的状态为运行但无设备 uhci->rh_state = UHCI_RH_RUNNING_NODEVS; //改变uhci的自动停止时间 uhci->auto_stop_time = jiffies + HZ; } break; //uhci的状态为运行但无设备 case UHCI_RH_RUNNING_NODEVS: /* auto-stop if nothing connected for 1 second */ //检测是否有连接的设备 if (any_ports_active(uhci)) //改变uhci的状态为运行 uhci->rh_state = UHCI_RH_RUNNING; //检测jiffies是否大于uhci->auto_stop_time else if (time_after_eq(jiffies, uhci->auto_stop_time)) //悬挂主机控制器 suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); break; default: break; } done: spin_unlock_irqrestore(&uhci->lock, flags); return status; }
|
uhci_scan_schedule为扫描帧队列中的任务,这个函数的内容先保密 = 3=
uhci_check_ports为检测端口的复位状态
uhci_check_ports在/drivers/usb/host/uhci-hub.c中
static void uhci_check_ports(struct uhci_hcd *uhci) { unsigned int port; unsigned long port_addr; int status; //历遍根集线器的所有端口 for (port = 0; port < uhci->rh_numports; ++port) { //获取端口的地址 port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; //读取端口状态 status = inw(port_addr); //判断端口是否为重置状态 if (unlikely(status & USBPORTSC_PR)) { //判断jiffies是否大于uhci->ports_timeout //也就是判断端口的复位信号是否足够长了 if (time_after_eq(jiffies, uhci->ports_timeout)) { //清除端口的复位状态,停止发送复位信号 CLR_RH_PORTSTAT(USBPORTSC_PR); //延迟10微妙 udelay(10); /* HP's server management chip requires * a longer delay. */ //测试是否为惠普产品,惠普产品需要更长的延迟 if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) wait_for_HP(port_addr); /* If the port was enabled before, turning * reset on caused a port enable change. * Turning reset off causes a port connect * status change. Clear these changes. */ //清除连接状态改变位和端点使能改变位 CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC); //设置端点使能位 SET_RH_PORTSTAT(USBPORTSC_PE); } } //判断端口是否为探测恢复状态 if (unlikely(status & USBPORTSC_RD)) { //检测uhci的恢复位上对应端口的状态 if (!test_bit(port, &uhci->resuming_ports)) { /* Port received a wakeup request */ //设置对应端口的恢复位 set_bit(port, &uhci->resuming_ports); //为恢复延迟设定再检测时间 uhci->ports_timeout = jiffies + msecs_to_jiffies(20); /* Make sure we see the port again * after the resuming period is over. */ mod_timer(&uhci_to_hcd(uhci)->rh_timer,uhci->ports_timeout); } else if (time_after_eq(jiffies,uhci->ports_timeout)) { uhci_finish_suspend(uhci, port, port_addr); } } } }
|
get_hub_status_data负责检测端口的连接状态
get_hub_status_data在/drivers/usb/host/uhci-hub.c
中
static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) { int port; //检测RWC的三个状态改变位 int mask = RWC_BITS; /* Some boards (both VIA and Intel apparently) report bogus * overcurrent indications, causing massive log spam unless * we completely ignore them. This doesn't seem to be a problem * with the chipset so much as with the way it is connected on * the motherboard; if the overcurrent input is left to float * then it may constantly register false positives. */ if (ignore_oc) mask &= ~USBPORTSC_OCC; *buf = 0; //历遍根集线器上的端口 for (port = 0; port < uhci->rh_numports; ++port) { //检测端口的状态改变位,或者端口悬挂位 if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & mask) || test_bit(port, &uhci->port_c_suspend)) //记录端口号 *buf |= (1 << (port + 1)); } return !!*buf; }
|
最后的返回值有两个!!,是转换数值为0或者1,例如!(!10)=!(0)=1,!(!0)=!(1)=0,使得返回值只有0或者1
返回到usb_hcd_poll_rh_status 中,后面的代码先保密 = 3=
最后这句mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4))是按HZ/4的时间间隔执行hcd->rh_timer中指定的函数,在uhci中为rh_timer_func,而rh_timer_func很简单
rh_timer_func在/drivers/usb/core/hcd.c中
static void rh_timer_func (unsigned long _hcd) { usb_hcd_poll_rh_status((struct usb_hcd *) _hcd); }
|
只是简单的调用了usb_hcd_poll_rh_status而已~, usb_hcd_poll_rh_status是什么?
前面.......前面....... 刚分析完啊 Orz
这样,每间隔HZ/4就会执行usb_hcd_poll_rh_status,形成轮询
这个rh_timer是在usb_create_hcd中注册的,大家可以回头看看 = 3=
uhci的注册就算完成了,真是漫长啊 T^T 我眼泪都快流不出来了
注册好的主机数据结构图如下