LINUX下USB1.1设备学习小记(4)_uhci(2)

 
LINUX下USB1.1设备学习小记(4)_uhci(2)
 
来源: ChinaUnix博客  日期: 2009.03.14 16:44 (共有条评论) 我要评论
 
好~ 现在万事俱备,只欠uhci硬件的注册了
现在谈一下uhci硬件的组成,uhci的硬件分为两个大的部分,主机控制器和根集线器,如下图



当提交uhci硬件的注册到pci总线后,经过一轮匹配,终于找到了uhci,进入到uhci_pci_driver->probe这个函数下

usb_hcd_pci_probe在/drivers/usb/host/uhci-hcd.c中
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct hc_driver *driver;
struct usb_hcd *hcd;
int retval;
//判断内核参数是否启用USB
if (usb_disabled())
  return -ENODEV;
//判断匹配表是否存在,该表为uhci_pci_driver->id_table中的内容
if (!id)
  return -EINVAL;
//取得匹配表中的私有结构,为uhci_driver
driver = (struct hc_driver *)id->driver_data;
//私有结构不存在则返回错误
if (!driver)
  return -EINVAL;
//使能pci设备
if (pci_enable_device(dev)  0)
  return -ENODEV;
//设置电源为正常状态
dev->current_state = PCI_D0;
//检测是否有中断号
if (!dev->irq)
{
  dev_err(&dev->dev,"Found HC with no IRQ. Check BIOS/PCI %s setup!/n",pci_name(dev));
  retval = -ENODEV;
  goto err1;
}
//创建usb_hcd结构
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
//检测创建是否成功
if (!hcd)
{
  retval = -ENOMEM;
  goto err1;
}
//检测是否需要映射I/O内存空间,EHCI和OHCI这两个主机控制器有自己的内存空间,访问的时候需要映射到计算机的内存空间中才能对设备进行操作,而UHCI没有自己的内存空间,对UHCI的操作是对IO端口的访问,所以UHCI需要分配IO端口空间
if (driver->flags & HCD_MEMORY)
{
  /* EHCI, OHCI */
  //这两个设备需要映射I/O内存空间
  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 err2;
  }
  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 err3;
  }
}
else
{
  /* UHCI */
  //UHCI需要映射I/O端口
  int region;
  for (region = 0; region  PCI_ROM_RESOURCE; region++)
  {
   //获取PCI I/O区域region号资源标记并判断是否为IO接口资源
   if (!(pci_resource_flags(dev, region) & IORESOURCE_IO))
    continue;
   //获取PCI I/O区域refion的首地址
   hcd->rsrc_start = pci_resource_start(dev, region);
   //计算资源长度
   hcd->rsrc_len = pci_resource_len(dev, region);
   //申请映射I/O端口
   if (request_region(hcd->rsrc_start, hcd->rsrc_len,driver->description))
    break;
  }
  //没有IO接口资源则返回出错
  if (region == PCI_ROM_RESOURCE)
  {
   dev_dbg(&dev->dev, "no i/o regions available/n");
   retval = -EBUSY;
   goto err1;
  }
}
//开始竞争总线
pci_set_master(dev);
//PCI层初始化完毕,进入Host Controller的初始化
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
  goto err4;
return retval;
}
现在看看usb_create_hcd,这个函数创建每个主机控制器必备的usb_hcd结构
usb_create_hcd在/drivers/usb/core/hcd.c中

struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
  struct device *dev, char *bus_name)
{
struct usb_hcd *hcd;
//申请空间,hcd_priv_size为动态的附加结构长度, 附加在usb_hcd的尾部
// 这段空间用于主机控制器的私有结构,uhci的私有结构为uhci_hcd,用于描述uhci的属性
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
if (!hcd)
{
  dev_dbg (dev, "hcd alloc failed/n");
  return NULL;
}
//连接usb_hcd到pci_device
dev_set_drvdata(dev, hcd);
//初始化引用计数器
kref_init(&hcd->kref);
//初始化usb总线
usb_bus_init(&hcd->self);
//连接pci_device到usb_hcd的usb_bus上
hcd->self.controller = dev;
//设置usb_bus名称
hcd->self.bus_name = bus_name;
//设置是否使用dma
hcd->self.uses_dma = (dev->
dma_mask != NULL);
//hcd->rh_timer的注释为drives root-hub polling
//字面意思是一个用于记时执行某函数的结构,例如5ms后执行指定函数
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
//连接hc_driver到usb_hcd上
hcd->driver = driver;
//设置设备名称
hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller";
return hcd;
}
分配并初始化好的usb_hcd结构如下,其中bus_name和uses_dma因为我还没看pci,所以这两个值不清楚,不过在uhci中dma是肯定会用到的


为什么会有个白框呢?还记得hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
这句么,这个白框就是所申请的hcd_priv_size大小的空间,里面是什么我们还不知道,所以是一片白的,等用到的时候我再把它画出来
hcd准备好之后,就要进入更深一个层次的初始化了, usb_add_hcd负责这部分的工作

usb_add_hcd在/drivers/usb/core/hcd.c中

int usb_add_hcd(struct usb_hcd *hcd,
  unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
dev_info(hcd->self.controller, "%s/n", hcd->product_desc);
//设置默认的批准标志,为无线设备设0,否则设1
hcd->authorized_default = hcd->wireless? 0 : 1;
//设置主机控制器的状态为硬件可用
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/* HC is in reset state, but accessible. Now do the one-time init,
  * bottom up so that hcds can customize the root hubs before khubd
  * starts talking to them. (Note, bus id is assigned early too.)
  */
  //初始化缓冲池
if ((retval = hcd_buffer_create(hcd)) != 0)
{
  dev_dbg(hcd->self.controller, "pool alloc failed/n");
  return retval;
}
//注册USB host controller
if ((retval = usb_register_bus(&hcd->self))  0)
  goto err_register_bus;
//分配一个设备描述符用于根设备
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;
}
//设置最高速度
rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : USB_SPEED_FULL;
//连接根集线设备到usb_bus上
hcd->self.root_hub = rhdev;
/* wakeup flag init defaults to "everything works" for root hubs,
  * but drivers can override it in reset() if needed, along with
  * recording the overall controller's system wakeup capability.
  */
  //打开ROOT HUB的WAKEUP
device_init_wakeup(&rhdev->dev, 1);
/* "reset" is misnamed; its role is now one-time init. the controller
  * should already have been reset (and boot firmware kicked off etc).
  */
  //复位设备
if (hcd->driver->reset && (retval = hcd->driver->reset(hcd))  0)
{
  dev_err(hcd->self.controller, "can't setup/n");
  goto err_hcd_driver_setup;
}
/* 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 (hcd->driver->irq)
{
  /* IRQF_DISABLED doesn't work as advertised when used together
   * with IRQF_SHARED. As usb_hcd_irq() will always disable
   * interrupts we can remove it here.
   */
  irqflags &= ~IRQF_DISABLED;
  //设置中断描述串
  snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
    hcd->driver->description, hcd->self.busnum);
  //安装中断处理函数
  if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,hcd->irq_descr, hcd)) != 0)
  {
   dev_err(hcd->self.controller,"request interrupt %d failed/n", irqnum);
   goto err_request_irq;
  }
  
  hcd->irq = irqnum;
  dev_info(hcd->self.controller, "irq %d, %s 0x%08llx/n", irqnum,
    (hcd->driver->flags & HCD_MEMORY) ?"io mem" : "io base",
     (unsigned long long)hcd->rsrc_start);
}
else
{
  hcd->irq = -1;
  if (hcd->rsrc_start)
   dev_info(hcd->self.controller, "%s 0x%08llx/n",
     (hcd->driver->flags & HCD_MEMORY) ?
     "io mem" : "io base",
     (unsigned long long)hcd->rsrc_start);
}
//启动设备
if ((retval = hcd->driver->start(hcd))  0)
{
  dev_err(hcd->self.controller, "startup error %d/n", retval);
  goto err_hcd_driver_start;
}
/* 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)
  //查询设备状态
  usb_hcd_poll_rh_status(hcd);
return retval;
}
首先是hcd_buffer_create,这个函数用于初始化主机控制器的dma内存池,为以后的dma内存分配做准备

hcd_buffer_create在/drivers/usb/core/buffer.c中

int hcd_buffer_create(struct usb_hcd *hcd)
{
char name[16];
int i, size;
if (!hcd->self.
controller->dma_mask && !(hcd->driver->flags & HCD_LOCAL_MEM))
  return 0;
//创建4个不同大小的缓冲池,为不同大小的dma请求分配合适的dma内存
for (i = 0; i  HCD_BUFFER_POOLS; i++)
{
  size = pool_max
;
  if (!size)
   continue;
  snprintf(name, sizeof name, "buffer-%d", size);
  //创建DMA内存池,此时还没有真正分配空间
  hcd->pool
= dma_pool_create(name, hcd->self.controller,size, size, 0);
  if (!hcd->pool
)
  {
   hcd_buffer_destroy(hcd);
   return -ENOMEM;
  }
}
return 0;
}
usb_register_bus用于注册uhci自己的总线,管理连接到uhci的所有设备
static int usb_register_bus(struct usb_bus *bus)
{
int result = -E2BIG;
int busnum;
mutex_lock(&usb_bus_list_lock);
//在usbmap位图中寻找未占用的位
busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
//大于限制则返回错误
if (busnum >= USB_MAXBUS)
{
  printk (KERN_ERR "%s: too many buses/n", usbcore_name);
  goto error_find_busnum;
}
//置相应位为已经使用
set_bit (busnum, busmap.busmap);
//设置uhci总线的地址号
bus->busnum = busnum;
//创建一个usb_host%目录,用于表达该设备为主机控制器
bus->dev = device_create_drvdata(usb_host_class, bus->controller,
      MKDEV(0, 0), bus,"usb_host%d", busnum);
result = PTR_ERR(bus->dev);
if (IS_ERR(bus->dev))
  goto error_create_class_dev;
/* Add it to the local list of buses */
//添加到usb_bus_list的链表中
list_add (&bus->bus_list, &usb_bus_list);
mutex_unlock(&usb_bus_list_lock);
//新型的总线通知机制,我还没研究,放着等我弄懂先吧
usb_notify_add_bus(bus);
dev_info (bus->controller, "new USB bus registered, assigned bus "
    "number %d/n", bus->busnum);
return 0;
}
这个函数执行完后的结构图如下

Click here to open new window
CTRL+Mouse wheel to zoom in/out


然后到usb_alloc_dev,这个函数负责分配一个usb设备数据结构,在这里,这个usb数据结构用于描述主机控制器的根集线器,这个根集线器也是usb设备的一种

usb_alloc_dev在/drivers/usb/core/usb.c中
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;
//分配usb_device结构
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
  return NULL;
//增加主机控制器的设备计数器
if (!usb_get_hcd(bus_to_hcd(bus)))
{
  kfree(dev);
  return NULL;
}
//初始化设备
device_initialize(&dev->dev);
//设置所属总线类型,这里是重点,为什么匹配的时候不是匹配pci总线或者其它总线,因为在这里设置了这个设备属于usb总线
dev->dev.bus = &usb_bus_type;
//设置设备自身类型,这里也是重点,这里设置了设备的类型,是设备还是接口,对于usb驱动来说分两类,设备驱动和接口驱动, usb_generic_driver为usb设备驱动,hub为usb接口驱动
dev->dev.type = &usb_device_type;
//设置属性组,也就是目录下的描述文件
dev->dev.groups = usb_device_groups;
//设置是否使用dma
dev->dev.dma_mask = bus->controller->dma_mask;
set_dev_node(&dev->dev, dev_to_node(bus->controller));
//设置设备状态为连接
dev->state = USB_STATE_ATTACHED;
atomic_set(&dev->urbnum, 0)
INIT_LIST_HEAD(&dev->ep0.urb_list);
//初始化端点0
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);
//设置URB发送允许位
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 bus_id isn't
  * as stable: bus->busnum changes easily from modprobe order,
  * cardbus or pci hotplugging, and so on.
  */
  //检测是否为根集线器,分配不同的名字
if (unlikely(!parent))
{
  //设置设备的层次为0
  dev->devpath[0] = '0';
  //设置父设备,这是根集线器,没有父设备,所以为NULL
  dev->dev.parent = bus->controller;
  sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
  root_hub = 1;
}
else
{
  /* match any labeling on the hubs; it's one-based */
  if (parent->devpath[0] == '0')
   snprintf(dev->devpath, sizeof dev->devpath,"%d", port1);
  else
   snprintf(dev->devpath, sizeof dev->devpath,"%s.%d", parent->devpath, port1);
  dev->dev.parent = &parent->dev;
  sprintf(&dev->dev.bus_id[0], "%d-%s",bus->busnum, dev->devpath);
  /* hub driver sets up TT records */
}
//设置usb设备的节点号
dev->portnum = port1;
//设置usb设备的usb总线
dev->bus = bus;
//设置上层hub
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
//这部分为电源管理机制,我们不关心这部分内容
#ifdef CONFIG_PM
mutex_init(&dev->pm_mutex);
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
dev->connect_time = jiffies;
dev->active_duration = -jiffies;
#endif
//检测是否为根集线器,这里为根集线器
if (root_hub) /* Root hub always ok [and always wired] */
  //设置设备的批准标志为1
  dev->authorized = 1;
else
{
  dev->authorized = usb_hcd->authorized_default;
  dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
}

return dev;
}
执行完后的结构图如下
Click here to open new window
CTRL+Mouse wheel to zoom in/out

现在到hcd->driver->reset,这个函数为uhci_init

uhci_init在/drivers/usb/host/uhci-hcd.c中

static int uhci_init(struct usb_hcd *hcd)
{
//这里解开白框的秘密了,原来是一个uhci_hcd结构,如果大家仔细的话,其实这个结构很早就出现了,在uhci_driver中,hcd_priv_size = sizeof(struct uhci_hcd), hcd_priv_size的值正是uhci_hcd的大小
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int port;
//取得IO端口资源区长度
unsigned io_size = (unsigned) hcd->rsrc_len;
//取得IO端口资源区起始地址
uhci->io_addr = (unsigned long) hcd->rsrc_start;
/* The UHCI spec says devices must have 2 ports, and goes on to say
  * they may have more but gives no way to determine how many there
  * are. However according to the UHCI spec, Bit 7 of the port
  * status and control register is always set to 1. So we try to
  * use this to our advantage. Another common failure mode when
  * a nonexistent register is addressed is to return all ones, so
  * we test for that also.
  */
  //判断根集线器有几个端口
for (port = 0; port  (io_size - USBPORTSC1) / 2; port++)
{
  unsigned int portstatus;
  //inw - 读IO端口
  portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2));
  //第7位不为1或者全为1则说明端口寄存器,判断完毕
  if (!(portstatus & 0x0080) || portstatus == 0xffff)
   break;
}
if (debug)
  dev_info(uhci_dev(uhci), "detected %d ports/n", port);
/* Anything greater than 7 is weird so we'll ignore it. */
//多于7个端口则强制设置为2个
if (port > UHCI_RH_MAXCHILD)
{
  dev_info(uhci_dev(uhci), "port count misdetected? ""forcing to 2 ports/n");
  port = 2;
}
//记录端口数
uhci->rh_numports = port;
/* Kick BIOS off this hardware and reset if the controller
  * isn't already safely quiescent.
  */
  //判断UHCI是否需要重置
check_and_reset_hc(uhci);
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值