分类: LINUX
2013-11-29 17:07:57
原文地址:USB控制器的初始化和枚举 作者:windguy
我们用UHCI类型的控制器来分析控制器的初始化,过程是这样的:
PCI设备枚举------> UHCI控制器初始化------------>登记usb_bus---------->登记root hub
| | |
| | |
| | |
/ / /
中断处理函数 添加到USB 总线链表 取得配置信息
1。USB控制器是连接在PCI总线上的,是一个PCI设备,所以在PCI总线初始化过程中也会受到枚举。
UHCI的代码在DRIVER/USB/UHCI.C文件中;UCHI作为pci_driver 的结构被搜索出来。
probe函数为:
static int __devinit uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int i;
/* Search for the IO base address.. */
for (i = 0; i < 6; i++) {
unsigned int io_addr = pci_resource_start(dev, i);
unsigned int io_size = pci_resource_len(dev, i);
/* IO address? */
if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))
continue;
return alloc_uhci(dev, io_addr, io_size);
}
return -ENODEV;
}
2。USB控制器作为PCI设备在PCI总线层次上的初始化已经完成了,下面就是USB总线控制器本身的初始化,即USB总线的初始化了。
* 分配UHCI所连接的USB总线,并注册它!
* 探测ROOT HUB的端口数,其IO地址空间的前16个地址用于总线控制器本身,其余的用于ROOT HUB;
ROOT HUB的每个端口(PORT)占用两个地址,端口在2 和8之间,每个端口的BIT7总是1。
* 分配ROOT HUB,也是一个USB DEVICE;
* 注册UHCI的中断请求函数;
* 为ROOT HUB 动态的分配一个设备号;
* ROOT HUB的总线控制器的连接是固定的,不需要通过另一个HUB的报告,所以直接就开始其枚举过程。
static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size)
{
struct uhci *uhci;
int retval;
char buf[8], *bufp = buf;
int i, port;
struct usb_bus *bus;
dma_addr_t dma_handle;
uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
if (!uhci) {
err("couldn't allocate uhci structure");
retval = -ENOMEM;
goto err_alloc_uhci;
}
uhci->dev = dev;
uhci->irq = dev->irq;
uhci->io_addr = io_addr;
uhci->io_size = io_size;
pci_set_drvdata(dev, uhci);
//分配UHCI所连接的USB总线,并注册它!
bus = usb_alloc_bus(&uhci_device_operations);
if (!bus) {
err("unable to allocate bus");
goto err_alloc_bus;
}
uhci->bus = bus;
bus->bus_name = dev->slot_name;
bus->hcpriv = uhci;
usb_register_bus(uhci->bus);
//探测ROOT HUB的端口数,其IO地址空间的前16个地址用于总线控制器本身,其余的用于ROOT HUB;
//ROOT HUB的每个端口(PORT)占用两个地址,端口在2 和8之间,每个端口的BIT7总是1。
/* Initialize the root hub */
/* UHCI specs says devices must have 2 ports, but goes on to say */
/* they may have more but give no way to determine how many they */
/* have. However, according to the UHCI spec, Bit 7 is always set */
/* to 1. So we try to use this to our advantage */
for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) {
unsigned int portstatus;
portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
if (!(portstatus & 0x0080))
break;
}
if (debug)
info("detected %d ports", port);
/* This is experimental so anything less than 2 or greater than 8 is */
/* something weird and we'll ignore it */
if (port < 2 || port > 8) {
info("port count misdetected? forcing to 2 ports");
port = 2;
}
uhci->rh.numports = port;
//分配ROOT HUB,也是一个USB DEVICE;
uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus);
if (!uhci->rh.dev) {
err("unable to allocate root hub");
goto err_alloc_root_hub;
}
start_hc(uhci);
//注册UHCI的中断请求函数;
if (request_irq(dev->irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci))
goto err_request_irq;
/* disable legacy emulation */
pci_write_config_word(uhci->dev, USBLEGSUP, USBLEGSUP_DEFAULT);
//为ROOT HUB 动态的分配一个设备号;
usb_connect(uhci->rh.dev);
//ROOT HUB的总线控制器的连接是固定的,不需要通过另一个HUB的报告,所以直接就开始其枚举过程。
//枚举过程要做的事情如下:1.分配地址;2.读入usb_device_descriptor; 3.读入配置描述符结构;4.选择或者改变设备的配置;
if (usb_new_device(uhci->rh.dev) != 0) {
err("unable to start root hub");
retval = -ENOMEM;
goto err_start_root_hub;
}
return 0;
}
3.枚举ROOT HUB 并做相关的工作
* 取得descriptor 结构
* 查找处理这个设备的驱动
* 留给用户空间程序加载模块;
int usb_new_device(struct usb_device *dev)
{
err = usb_set_address(dev);
wait_ms(10); /* Let the SET_ADDRESS settle */
//取得descriptor 结构
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
err = usb_get_device_descriptor(dev);
err = usb_get_configuration(dev);
/* now that the basic setup is over, add a /proc/bus/usb entry */
usbdevfs_add_device(dev);
//查找处理这个设备的驱动
/* find drivers willing to handle this device */
usb_find_drivers(dev);
//留给用户空间程序加载模块;
/* userspace may load modules and/or configure further */
call_policy ("add", dev);
return 0;
}
4.取得设备的配置信息
* 读入配置描述结构
* 分析配置
int usb_get_configuration(struct usb_device *dev)
{
desc = (struct usb_config_descriptor *)buffer;
for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
/* We grab the first 8 bytes so we know how long the whole */
/* configuration is */
//读入配置描述结构
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
/* Get the full buffer */
length = le16_to_cpu(desc->wTotalLength);
bigbuffer = kmalloc(length, GFP_KERNEL);
/* Now that we know the length, get the whole thing */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
dev->rawdescriptors[cfgno] = bigbuffer;
//分析配置
result = usb_parse_configuration(&dev->config[cfgno], bigbuffer);
}
}
5.分析设备的配置信息
* 找到其中一种类型的描述符,则我们对该描述进行处理
* 分析usb_interface;
int usb_parse_configuration(struct usb_config_descriptor *config, char *buffer)
{
struct usb_descriptor_header *header;
memcpy(config, buffer, USB_DT_CONFIG_SIZE);
le16_to_cpus(&config->wTotalLength);
size = config->wTotalLength;
config->interface = (struct usb_interface *)
kmalloc(config->bNumInterfaces *
sizeof(struct usb_interface), GFP_KERNEL);
memset(config->interface, 0,
config->bNumInterfaces * sizeof(struct usb_interface));
for (i = 0; i < config->bNumInterfaces; i++) {
int numskipped, len;
char *begin;
/* Skip over the rest of the Class Specific or Vendor */
/* Specific descriptors */
begin = buffer;
numskipped = 0;
//找到其中一种类型的描述符,则我们对该描述进行处理;
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
/* If we find another "proper" descriptor then we're done */
if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
(header->bDescriptorType == USB_DT_INTERFACE) ||
(header->bDescriptorType == USB_DT_CONFIG) ||
(header->bDescriptorType == USB_DT_DEVICE))
break;
dbg("skipping descriptor 0x%X", header->bDescriptorType);
numskipped++;
buffer += header->bLength;
size -= header->bLength;
}
if (numskipped)
dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
//分析usb_interface;
retval = usb_parse_interface(config->interface + i, buffer, size);
if (retval < 0)
return retval;
buffer += retval;
size -= retval;
}
return size;
}
6.分析usb_interface
* 分析endpoint 描述符结构
* 为usb_interface_descriptor 分配空间;
static int usb_parse_interface(struct usb_interface *interface, unsigned char *buffer, int size)
{
int i, len, numskipped, retval, parsed = 0;
struct usb_descriptor_header *header;
struct usb_interface_descriptor *ifp;
unsigned char *begin;
interface->act_altsetting = 0;
interface->num_altsetting = 0;
interface->max_altsetting = USB_ALTSETTINGALLOC;
interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
if (!interface->altsetting) {
err("couldn't kmalloc interface->altsetting");
return -1;
}
while (size > 0) {
if (interface->num_altsetting >= interface->max_altsetting) {
void *ptr;
int oldmas;
oldmas = interface->max_altsetting;
interface->max_altsetting += USB_ALTSETTINGALLOC;
if (interface->max_altsetting > USB_MAXALTSETTING) {
warn("too many alternate settings (max %d)",
USB_MAXALTSETTING);
return -1;
}
ptr = interface->altsetting;
interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
if (!interface->altsetting) {
err("couldn't kmalloc interface->altsetting");
interface->altsetting = ptr;
return -1;
}
memcpy(interface->altsetting, ptr, sizeof(struct usb_interface_descriptor) * oldmas);
kfree(ptr);
}
ifp = interface->altsetting + interface->num_altsetting;
interface->num_altsetting++;
memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);
/* Skip over the interface */
buffer += ifp->bLength;
parsed += ifp->bLength;
size -= ifp->bLength;
begin = buffer;
numskipped = 0;
//跳过非标准的interface ,endpoint ,etc;
/* Skip over any interface, class or vendor descriptors */
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
if (header->bLength < 2) {
err("invalid descriptor length of %d", header->bLength);
return -1;
}
/* If we find another "proper" descriptor then we're done */
if ((header->bDescriptorType == USB_DT_INTERFACE) ||
(header->bDescriptorType == USB_DT_ENDPOINT) ||
(header->bDescriptorType == USB_DT_CONFIG) ||
(header->bDescriptorType == USB_DT_DEVICE))
break;
numskipped++;
buffer += header->bLength;
parsed += header->bLength;
size -= header->bLength;
}
if (numskipped)
dbg("skipped %d class/vendor specific interface descriptors", numskipped);
ifp->endpoint = (struct usb_endpoint_descriptor *)
kmalloc(ifp->bNumEndpoints *
sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
//分析endpoint 描述符结构,把每一个endpoint 分配空间,并且读出配置信息!
for (i = 0; i < ifp->bNumEndpoints; i++) {
header = (struct usb_descriptor_header *)buffer;
retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size);
if (retval < 0)
return retval;
buffer += retval;
parsed += retval;
size -= retval;
}
//检查一下是否buffer中还有其他的INTERFACE结构,否则就返回了;
/* We check to see if it's an alternate to this one */
ifp = (struct usb_interface_descriptor *)buffer;
if (size < USB_DT_INTERFACE_SIZE ||ifp->bDescriptorType != USB_DT_INTERFACE ||!ifp->bAlternateSetting)
return parsed;
}
return parsed;
}
7。总线驱动的安装(启动)
* 通过call_policy, 构筑一个命令行"/sbin/hotplug usb"及必要的环境信息和创建一个内核线程,让该线程升级为进程,调用工具软件/sbin/hotplug装入USB HUB的驱动模块。
* 通过usb_init()安装,在driver/usb/usb.c文件中
8。装载集中器的驱动过程及khubd
在driver/usb/hub.c文件中,
*注册HUB的驱动,struct usb_driver hub_driver;
*启动一个内核线程,用于探测HUB的状态改变(插拔设备);
int usb_hub_init(void)
{
int pid;
if (usb_register(&hub_driver) < 0) {
err("Unable to register USB hub driver");
return -1;
}
pid = kernel_thread(usb_hub_thread, NULL,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
if (pid >= 0) {
khubd_pid = pid;
return 0;
}
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
err("failed to start usb_hub_thread");
return -1;
}
在函数hub_probe()->usb_hub_configure()注册HUB的complete_handler函数,在complete_handler中把将该HUB加入hub_event_list链表,该链表提供给usb_hub_thread()->usb_hub_events()处理。
通过usb_hub_events()探测HUB的端口状态改变,即可知道USB DEVICE的插入和拔除 。则可进行一系列的操作了。。。。
9。问题
(1) 注册HUB的驱动,struct usb_driver hub_driver(driver/usb/hub.c)但是没有file_operation 成员,在hub_probe中也没有注册相关的可被文件操作支持的其他设备类型;那么HUB的读、写操作是如何实现的呢?
(2) 在usb_hub_events()(driver/usb/hub.c)处理中,hub_event_list链表被删除了,第一次提交URB触发hub_irq完成添加,那么后来又是么添加进去的?