PCI检测到USB主机控制器后会进行驱动的匹配
usb_hcd_pci_probe函数进行设备与驱动的匹配,并执行初始化动作
创建USB_HCD结构
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
if (!hcd) {
retval = -ENOMEM;
goto disable_pci;
}
if (driver->flags & HCD_MEMORY) {
/* EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);
hcd->rsrc_len = pci_resource_len(dev, 0);
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
driver->description)) {
dev_dbg(&dev->dev, "controller already in use\n");
retval = -EBUSY;
goto clear_companion;
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
dev_dbg(&dev->dev, "error mapping memory\n");
retval = -EFAULT;
goto release_mem_region;
}
} else {
/* UHCI */
UHCI的IO资源是在IO地址空间中的
int region;
for (region = 0; region < PCI_ROM_RESOURCE; region++) {
if (!(pci_resource_flags(dev, region) &
IORESOURCE_IO))
continue;
hcd->rsrc_start = pci_resource_start(dev, region);
hcd->rsrc_len = pci_resource_len(dev, region);
if (request_region(hcd->rsrc_start, hcd->rsrc_len,
driver->description))
break;
}
if (region == PCI_ROM_RESOURCE) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
goto clear_companion;
}
}
使主机控制器有成为总线主的能力,否则无法使用DMA功能
pci_set_master(dev);
向core层添加主机控制器
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
usb_add_hcd函数:
初始化hcd->pool,按照全局数组pool_max中的size分配的DMA pool。
if ((retval = hcd_buffer_create(hcd)) != 0) {
dev_dbg(hcd->self.controller, "pool alloc failed\n");
return retval;
}
每个主机控制器代表一条USB总线,注册该条USB总线。
if ((retval = usb_register_bus(&hcd->self)) < 0)
goto err_register_bus;
root hub处理
if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
dev_err(hcd->self.controller, "unable to allocate root hub\n");
retval = -ENOMEM;
goto err_allocate_root_hub;
}
hcd->self.root_hub = rhdev;
switch (hcd->speed) {
case HCD_USB11:
rhdev->speed = USB_SPEED_FULL;
break;
case HCD_USB2:
rhdev->speed = USB_SPEED_HIGH;
break;
case HCD_USB3:
rhdev->speed = USB_SPEED_SUPER;
break;
default:
retval = -EINVAL;
goto err_set_rh_speed;
}
reset主机控制器
if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
dev_err(hcd->self.controller, "can't setup\n");
goto err_hcd_driver_setup;
}
hcd->rh_pollable = 1;
走的是uhci_pci_init函数:
uhci->io_addr = (unsigned long) hcd->rsrc_start;
计算root hub的端口数,UHCI文档上是2个
uhci->rh_numports = uhci_count_ports(hcd);
/* 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.)
*/
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA)
uhci->oc_low = 1;
/* HP's server management chip requires a longer port reset delay. */
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
uhci->wait_for_hp = 1;
/* Set up pointers to PCI-specific functions */
uhci->reset_hc = uhci_pci_reset_hc;
uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
uhci->configure_hc = uhci_pci_configure_hc;
uhci->resume_detect_interrupts_are_broken =
uhci_pci_resume_detect_interrupts_are_broken;
uhci->global_suspend_mode_is_broken =
uhci_pci_global_suspend_mode_is_broken;
/* Kick BIOS off this hardware and reset if the controller
* isn't already safely quiescent.
*/
check_and_reset_hc(uhci);
结束reset
/* NOTE: root hub and controller capabilities may not be the same */
if (device_can_wakeup(hcd->self.controller)
&& device_can_wakeup(&hcd->self.root_hub->dev))
dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
/* enable irqs just before we start the controller */
if (usb_hcd_is_primary_hcd(hcd)) {
retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
if (retval)
goto err_request_irq;
}
调用start函数,后面分析。
hcd->state = HC_STATE_RUNNING;
retval = hcd->driver->start(hcd);
if (retval < 0) {
dev_err(hcd->self.controller, "startup error %d\n", retval);
goto err_hcd_driver_start;
}
注册root hub
/* starting here, usbcore will pay attention to this root hub */
rhdev->bus_mA = min(500u, hcd->power_budget);
if ((retval = register_root_hub(hcd)) != 0)
goto err_register_root_hub;
retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
if (retval < 0) {
printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n",
retval);
goto error_create_attr_group;
}
if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
usb_hcd_poll_rh_status(hcd);
return retval;
usb_add_hcd结束
usb_hcd_pci_probe结束。
分配root hub的时候使用了usb_alloc_dev函数,分析如下:
struct usb_device *usb_alloc_dev(struct usb_device *parent,
struct usb_bus *bus, unsigned port1)
{
struct usb_device *dev;
struct usb_hcd *usb_hcd = container_of(bus, struct usb_hcd, self);
unsigned root_hub = 0;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
if (!usb_get_hcd(bus_to_hcd(bus))) {
kfree(dev);
return NULL;
}
/* Root hubs aren't true devices, so don't allocate HCD resources */
if (usb_hcd->driver->alloc_dev && parent &&
!usb_hcd->driver->alloc_dev(usb_hcd, dev)) {
usb_put_hcd(bus_to_hcd(bus));
kfree(dev);
return NULL;
}
device_initialize(&dev->dev);
设置要挂在哪个bus_type上,设备驱动模型方面的代码
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
dev->dev.dma_mask = bus->controller->dma_mask;
set_dev_node(&dev->dev, dev_to_node(bus->controller));
设备ATTACHED状态
dev->state = USB_STATE_ATTACHED;
atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list);
ep0,默认控制端点,首先设置它的maxpacket为默认大小,后面得到设备描述符后再更新
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
/* ep0 maxpacket comes later, from device descriptor */
usb_enable_endpoint(dev, &dev->ep0, false);
=========================================================
该函数如下:
void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep,
bool reset_ep)
{
int epnum = usb_endpoint_num(&ep->desc);为0
int is_out = usb_endpoint_dir_out(&ep->desc);为1
int is_control = usb_endpoint_xfer_control(&ep->desc);为1
if (reset_ep)
usb_hcd_reset_endpoint(dev, ep);
is_control为1,也就是会执行后面这几个条件语句
if (is_out || is_control)
dev->ep_out[epnum] = ep;
if (!is_out || is_control)
dev->ep_in[epnum] = ep;
ep->enabled = 1;
}
===========================================================
dev->can_submit = 1;
/* Save readable and stable topology id, distinguishing devices
* by location for diagnostics, tools, driver model, etc. The
* string is a path along hub ports, from the root. Each device's
* dev->devpath will be stable until USB is re-cabled, and hubs
* are often labeled with these port numbers. The name isn't
* as stable: bus->busnum changes easily from modprobe order,
* cardbus or pci hotplugging, and so on.
*/
if (unlikely(!parent)) {
如果设备是root hub 则名字是usb1,usb2。。。。devpath[0]是0
dev->devpath[0] = '0';
dev->route = 0;
dev->dev.parent = bus->controller;
dev_set_name(&dev->dev, "usb%d", bus->busnum);
root_hub = 1;
} else {
对于root hub下的设备.它的名称为:总线号+”-”+端口号.例如,第一条usb总线上的root hub的第一个端口上的设备叫”1-0”.第二个端口上的设备名称为”1-1”
对于父结点不是root hub的设备.它的名称为: 总线号+”-”+端口路径. 例如.在第一条usb总线上的root hub的第一个端口上的hub上.第一个端口上的设备名称叫做: 1-0.1 ,第二个端口上的设备名称叫做1-0.2
依次往下推……
如果你到/sys中查看usb设备的话,看到的名称跟这里分析的会不一样.这是因为,对bus_id的处理还没完呢!后面还有相关的处理.等代码分析到了的时候再看. *^_^*.
/* match any labeling on the hubs; it's one-based */
if (parent->devpath[0] == '0') {
snprintf(dev->devpath, sizeof dev->devpath,
"%d", port1);
/* Root ports are not counted in route string */
dev->route = 0;
} else {
snprintf(dev->devpath, sizeof dev->devpath,
"%s.%d", parent->devpath, port1);
/* Route string assumes hubs have less than 16 ports */
if (port1 < 15)
dev->route = parent->route +
(port1 << ((parent->level - 1)*4));
else
dev->route = parent->route +
(15 << ((parent->level - 1)*4));
}
dev->dev.parent = &parent->dev;
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
/* hub driver sets up TT records */
}
dev->portnum = port1;
dev->bus = bus;
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
#ifdef CONFIG_PM
pm_runtime_set_autosuspend_delay(&dev->dev,
usb_autosuspend_delay * 1000);
dev->connect_time = jiffies;
dev->active_duration = -jiffies;
#endif
if (root_hub) /* Root hub always ok [and always wired] */
dev->authorized = 1;
else {
dev->authorized = usb_hcd->authorized_default;
dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
}
return dev;
}
static int uhci_start(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i;
struct dentry __maybe_unused *dentry;
hcd->uses_new_polling = 1;
spin_lock_init(&uhci->lock);
setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,
(unsigned long) uhci);
INIT_LIST_HEAD(&uhci->idle_qh_list);
init_waitqueue_head(&uhci->waitqh);
#ifdef UHCI_DEBUG_OPS
dentry = debugfs_create_file(hcd->self.bus_name,
S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
uhci, &uhci_debug_operations);
if (!dentry) {
dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
return -ENOMEM;
}
uhci->dentry = dentry;
#endif
分配DMA内存区,1024个frame
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
UHCI_NUMFRAMES * sizeof(*uhci->frame),
&uhci->frame_dma_handle, 0);
if (!uhci->frame) {
dev_err(uhci_dev(uhci), "unable to allocate "
"consistent memory for frame list\n");
goto err_alloc_frame;
}
memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame));
uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),
GFP_KERNEL);
if (!uhci->frame_cpu) {
dev_err(uhci_dev(uhci), "unable to allocate "
"memory for frame pointers\n");
goto err_alloc_frame_cpu;
}
TD池 用于以后TD的分配
uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
sizeof(struct uhci_td), 16, 0);
if (!uhci->td_pool) {
dev_err(uhci_dev(uhci), "unable to create td dma_pool\n");
goto err_create_td_pool;
}
=============================================================================================
QH池 用于以后QH的分配
QH结构中硬件直接使用的是以下两个字段(UHCI文档定义)
/* Hardware fields */
__hc32 link; /* Next QH in the schedule */ 指向下一个QH
__hc32 element; /* Queue element (TD) pointer */这个QH中的TD元素
uhci->qh_pool = dma_pool_create("uhci_qh", uhci_dev(uhci),
sizeof(struct uhci_qh), 16, 0);
if (!uhci->qh_pool) {
dev_err(uhci_dev(uhci), "unable to create qh dma_pool\n");
goto err_create_qh_pool;
}
============================================================================================
=============================================================================================
分配TD结构,分配后td->dma_handle为DMA使用的物理地址
TD结构中硬件直接使用的是以下四个字段(UHCI文档定义)
/* Hardware fields */
__hc32 link; 指向QH或TD,同QH的link
__hc32 status;
__hc32 token;
token有32个bit:
bit31到bit21表示Maximum Length,即这次传输的最大允许字节.
bit20是保留位
bit19表示Data Toggle
bit18到bit15表示Endpoint的地址,即我们曾经说的端点号
bit14到bit8表示设备地址
bit7到bit0表示PID,即Packet ID
有一套宏专门用来操作token的:
208 /*
209 * for TD <info>: (a.k.a. Token)
210 */
211 #define td_token(td) le32_to_cpu((td)->token)
212 #define TD_TOKEN_DEVADDR_SHIFT 8
213 #define TD_TOKEN_TOGGLE_SHIFT 19
214 #define TD_TOKEN_TOGGLE (1 << 19)
215 #define TD_TOKEN_EXPLEN_SHIFT 21
216 #define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */
217 #define TD_TOKEN_PID_MASK 0xFF
218
219 #define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << /
220 TD_TOKEN_EXPLEN_SHIFT)
221
222 #define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + /
223 1) & TD_TOKEN_EXPLEN_MASK)
224 #define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
225 #define uhci_endpoint(token) (((token) >> 15) & 0xf)
226 #define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
227 #define uhci_devep(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7ff)
228 #define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
229 #define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
230 #define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
__hc32 buffer;
uhci->term_td = uhci_alloc_td(uhci);
if (!uhci->term_td) {
dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n");
goto err_alloc_term_td;
}
================================================================================================
分配QH,skelqh有11个元素
for (i = 0; i < UHCI_NUM_SKELQH; i++) {
uhci->skelqh[i] = uhci_alloc_qh(uhci, NULL, NULL);
if (!uhci->skelqh[i]) {
dev_err(uhci_dev(uhci), "unable to allocate QH\n");
goto err_alloc_skelqh;
}
}
====================================================================================
/*
* 8 Interrupt queues; link all higher int queues to int1 = async
*/
uhci->skelqh数组的2到8项的后续指针都指向了skelqh[9].skelqh[9]指向了UHCI_PTR_TERM.(skel_async_qh为skelqh[9])
uhci->skelqh[2]~ uhci->skelqh[9].代表8个时间间隔的调度队列.
依次被称为 int128,int64,int32,int16,int8,int4,int2,int1.
即对于int128,即每隔128ms调度一次.int1.即每隔1ms调度一次,
for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
uhci->skelqh[i]->link = LINK_TO_QH(uhci, uhci->skel_async_qh);
skel_async_qh的link无效
uhci->skel_async_qh->link = UHCI_PTR_TERM(uhci);
skel_term_qh为skelqh[10](最后一个数组元素,一共11个元素),它的link指向它自己。
uhci->skel_term_qh->link = LINK_TO_QH(uhci, uhci->skel_term_qh);
关于LINK_TO_QH(qh)
#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle))
76 #define UHCI_PTR_BITS __constant_cpu_to_le32(0x000F)
77 #define UHCI_PTR_TERM __constant_cpu_to_le32(0x0001)
78 #define UHCI_PTR_QH __constant_cpu_to_le32(0x0002) 设置link指针中的bit1
79 #define UHCI_PTR_DEPTH __constant_cpu_to_le32(0x0004)
80 #define UHCI_PTR_BREADTH __constant_cpu_to_le32(0x0000)
__le32 link 是一个指针,这个指针指向下一个QH,
它包含着下一个QH或者下一个TD的地址.
一共32个bits,其中只有bit31到bit4这些位是用来记录地址的,QH,TD都是16字节对齐的,所以低4位可以做以下用途:
bit3和bit2是保留位,
bit1则用来表示该指针指向的是一个QH还是一个TD.
bit1如果为1,表示本指针指向的是一个QH,
如果为0,表示本指针指向的是一个TD
bit0表示本QH是否是最后一个QH,为1表示本QH是最后一个QH,所以当bit0为1时,link指针其他位都是无效的,所以用UHCI_PTR_TERM来设置一个link指针无效。
======================================================================================
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci, uhci->term_td, 0, uhci_explen(0) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
=======================================================================================
uhci_fill_td函数如下:
static inline void uhci_fill_td(struct uhci_hcd *uhci, struct uhci_td *td,
u32 status, u32 token, u32 buffer)
{
td->status = cpu_to_hc32(uhci, status);
td->token = cpu_to_hc32(uhci, token);
td->buffer = cpu_to_hc32(uhci, buffer);
}
按照上面的TD token操作宏 解释如下:
uhci_explen(0)表示将bit31到bit21的Maximum Length设置为0.
(0x7f << TD_TOKEN_DEVADDR_SHIFT)表示将bit14到bit8的设备地址设置为0x7f
USB_PID_IN表示将bit7到bit0的PID设置为0x69,该值是文档中定义的表示PID IN。
=======================================================================================
将link指针无效
uhci->term_td->link = UHCI_PTR_TERM(uhci);
skelqh数组的9和10元素的element都指向term_td
uhci->skel_async_qh->element = uhci->skel_term_qh->element =
LINK_TO_TD(uhci, uhci->term_td);
/*
* Fill the frame list: make all entries point to the proper
* interrupt queue.
*/
将skelqh数组根据周期连接到frame上
for (i = 0; i < UHCI_NUMFRAMES; i++) {
/* Only place we don't use the frame list routines */
uhci->frame[i] = uhci_frame_skel_link(uhci, i);
}
===============================================================================
uhci_frame_skel_link该函数有点复杂:
static __hc32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
{
int skelnum;
/*
* The interrupt queues will be interleaved as evenly as possible.
* There's not much to be done about period-1 interrupts; they have
* to occur in every frame. But we can schedule period-2 interrupts
* in odd-numbered frames, period-4 interrupts in frames congruent
* to 2 (mod 4), and so on. This way each frame only has two
* interrupt QHs, which will help spread out bandwidth utilization.
*
* ffs (Find First bit Set) does exactly what we need:
* 1,3,5,... => ffs = 0 => use period-2 QH = skelqh[8],
* 2,6,10,... => ffs = 1 => use period-4 QH = skelqh[7], etc.
* ffs >= 7 => not on any high-period queue, so use
* period-1 QH = skelqh[9].
* Add in UHCI_NUMFRAMES to insure at least one bit is set.
*/
skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
if (skelnum <= 1)
skelnum = 9;
return LINK_TO_QH(uhci, uhci->skelqh[skelnum]);
}
__ffs函数功能如下:
给它一个输入参数,它找到这个输入的参数的第一个为1的位
输入数字:0-1023
1,3,5,7,9等奇数: 那么返回0,skelnum为8,也就是连接到skelqh[8]上,这是一个2ms周期的QH。
2,6,10,14,18,22,26等 1 7 7 4
4,12,20,28,36等
===================================================================================
目前frame里保存的是对应周期的qh的地址(就是skelqh数组里面的那几个元素),
而每个QH的link都指向skelqh[9],skelqh[9]的link是UHCI_PTR_TERM,element指向uhci->term_td。
也就是以后只要将TD连接到对应周期的skelqh数组元素上,那么就会被处理。
/*
* Some architectures require a full mb() to enforce completion of
* the memory writes above before the I/O transfers in configure_hc().
*/
mb();
设置frame地址什么的,最后配置
configure_hc(uhci);
uhci->is_initialized = 1;
spin_lock_irq(&uhci->lock);
驱动root hub
start_rh(uhci);
spin_unlock_irq(&uhci->lock);
return 0;
主机控制器的初始化完成