Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181535
  • 博文数量: 108
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 65
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-16 11:38
文章分类
文章存档

2016年(2)

2015年(44)

2014年(62)

分类: LINUX

2015-11-22 20:20:57

原文地址:linux usb hub驱动 作者:steven_miao

linux usb hub驱动

谨以此文纪念过往的岁月

一.   前言

OHCI (Open Host Controller Interface),是康柏、微软、国家半导体等公司共同制定的一个USB主机接口规范,它提供一个更抽象的接口来完成USB数据传输工作。

OHCI规范中,最重要的几个概念是端点(EndPoint - ED)、传输描述符(Transport Descriptor - TD)、主机控制器通信区(HCCA)。其中ED负责确定传输类型(控制传输、批量传输、同步传输和中断传输)。TD确定传输参数。HCCA用于确定数据 传输是否完毕。

本文针对s3c6410usb host的驱动来讲述usb host的驱动。

二.usb host驱动

话说s3c6410的硬件支持ohci,其配置时也是采用的是ohci-hcd,其编译时为ohci-hcd.c文件。

static int __init ohci_hcd_mod_init(void)

{

    platform_driver_register (&ohci_hcd_s3c2410_driver);

}

其实真正注册的是ohci_hcd_s3c2410_driver这个驱动。那我们来看一下这个结构体的具体值。

static struct platform_driver ohci_hcd_s3c2410_driver = {

    .probe      = ohci_hcd_s3c2410_drv_probe,

    .remove    =  ohci_hcd_s3c2410_drv_remove,

    .shutdown   = usb_hcd_platform_shutdown,

    .driver     = {

        .owner  = THIS_MODULE,

        .name   = "s3c2410-ohci",

    },

};

那我们一一来看上述的每一个函数的实现。

2.1 hcd 探测

函数很简单其实现功能的是usb_hcd_s3c2410_probe函数。

static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)

{

    return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev);

}

ohci_s3c2410_hc_driver提供了对于ohci的操作集。对于这些函数在后面的学习中去看,在此不加扩展。我们将下面的函数剔除枝叶留其主干。

static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,

                                     struct platform_device *dev)

{

    struct usb_hcd *hcd = NULL;

    int retval;

#if !defined(CONFIG_ARCH_2410)

        usb_host_clk_en();          --使能clk

#endif

    s3c2410_usb_set_power(dev->dev.platform_data, 1, 1); 

    s3c2410_usb_set_power(dev->dev.platform_data, 2, 1);

    hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");  --创建一个hcd

    hcd->rsrc_start = dev->resource[0].start; --获取物理地址

    hcd->rsrc_len   = dev->resource[0].end - dev->resource[0].start + 1;

 request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name);

    clk = clk_get(&dev->dev, "usb-host");

    s3c2410_start_hc(dev, hcd);

    hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);

    ohci_hcd_init(hcd_to_ohci(hcd));

    retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);

    return 0;

}

对于usb的电源管理,我们暂时不看,不看不代表不重要,电源管理是很重要的。

那依次来看上面的函数。usb_create_hcd创建和初始化一个hcd结构体。

参数说明:

Driver :将使用该hcdhc驱动

dev    :该hc的设备实体,将存储在hcd->self.controller中。

bus_name: 存储在hcd->self.bus_name的值

struct usb_hcd * usb_create_hcd (const struct hc_driver *driver,

        struct device *dev, const char *bus_name)

{

    struct usb_hcd *hcd;

    hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);

    if (!hcd) {

       return NULL;

    }

    dev_set_drvdata(dev, hcd);  --hcd存储到dev的私用数据。

    kref_init(&hcd->kref);   --初始化hcd的引用计数

 

    usb_bus_init(&hcd->self);  --初始化usb_bus 结构体

    hcd->self.controller = dev;--hcdcontroller实体

    hcd->self.bus_name = bus_name; --总线名称

    hcd->self.uses_dma = (dev->dma_mask != NULL); --使用使用dma的旗标

 --如何理解该定时器,众所周知usb采用轮询的办法来实现对从设备的读取数据,那有一点就会很疑惑就是那所谓中断传输是怎么一回事,从字面意思去理解应该是从设备如果有数据发送,则产生一个中断通知usb host,这是与usb协议相互违背的。那这是怎么一回事呢,其实这就是timer的作用了。

    init_timer(&hcd->rh_timer); 

    hcd->rh_timer.function = rh_timer_func;

    hcd->rh_timer.data = (unsigned long) hcd;

    hcd->driver = driver;

    hcd->product_desc = (driver->product_desc) ? driver->product_desc :

            "USB Host Controller";

    return hcd;

}

上面就是初始化一个hcd的实体。

s3c2410_start_hc启动hc。这里有一个很奇怪的结构体就是struct s3c2410_hcd_info,在s3c6410中并没有看到该结构体的赋值。也许有人对此很困惑,该结构体做什么用的。那我们来看该结构体的真正面目。

struct s3c2410_hcd_info {

    struct usb_hcd      *hcd;  --保存该hcd_info所属的hcd

    struct s3c2410_hcd_port port[2]; --两个端口。

 

    void        (*power_control)(int port, int to); --电源控制

    void        (*enable_oc)(struct s3c2410_hcd_info *, int on);

    void        (*report_oc)(struct s3c2410_hcd_info *, int ports);

};

usb-host.txt中对其功能进行了说明,就是一对函数,使能过流检测和控制端口电源状态。

power_control:使能或禁止端口电源

enable_oc    :使能或禁止端口过流检测

report_oc    :当端口存在过流,则会调用该函数。

static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd)

{

    struct s3c2410_hcd_info *info = dev->dev.platform_data;

clk_enable(clk);

    if (info != NULL) {  --s3c6410中该info为空。

        info->hcd   = hcd;

        info->report_oc = s3c2410_hcd_oc;

        if (info->enable_oc != NULL) {

            (info->enable_oc)(info, 1);

        }

    }

}

初始化ohci_hcd

static void ohci_hcd_init (struct ohci_hcd *ohci)

{

    ohci->next_statechange = jiffies;

    spin_lock_init (&ohci->lock);

    INIT_LIST_HEAD (&ohci->pending);

}

初始化并注册usb_hcd

完成通用hcd的初始化和注册,在这里同时完成中断的申请和注册。

int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags)

{

    int retval;

    struct usb_device *rhdev;

 

    hcd->authorized_default = hcd->wireless? 0 : 1;  --判断是否为无线

    set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); --设置HW_ACCESSIBLE旗标

    if ((retval = hcd_buffer_create(hcd)) != 0) { --开辟hcd的缓冲区

            return retval;

    }

    if ((retval = usb_register_bus(&hcd->self)) < 0)

        goto err_register_bus;

    if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {

            retval = -ENOMEM;

        goto err_allocate_root_hub;

    }

    rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :USB_SPEED_FULL; --指定根hubspeed

    hcd->self.root_hub = rhdev;

 

    device_init_wakeup(&rhdev->dev, 1);

 

    if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {--NULL

        goto err_hcd_driver_setup;

    }

    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");

    if (hcd->driver->irq) {   --中断处理

        if (irqflags & IRQF_SHARED)

            irqflags &= ~IRQF_DISABLED;

        snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",

                hcd->driver->description, hcd->self.busnum);

        request_irq(irqnum, &usb_hcd_irq, irqflags,hcd->irq_descr, hcd);--申请中断线

        }

        hcd->irq = irqnum;

    } else {

        hcd->irq = -1

    }

 

    hcd->driver->start(hcd);   --调用start ohci_s3c2410_start

    rhdev->bus_mA = min(500u, hcd->power_budget);

    register_root_hub(hcd))    --注册root hub

       

    retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);

    if (retval < 0) {

        goto error_create_attr_group;

    }

    if (hcd->uses_new_polling && hcd->poll_rh)

        usb_hcd_poll_rh_status(hcd);

    return retval;

}

那一一来看上面的函数,学习内核就要有打破砂锅问到底的精神,唯有知道那背后的种种风光,才能领略那种种风采。闲话不说,继续!

记住下面结构体中flag的值。那就看这几个宏定义是什么意思。

#define HCD_MEMORY  0x0001         --hc的寄存器使用memory映射

#define HCD_LOCAL_MEM   0x0002      --hc使用local memory

#define HCD_USB11   0x0010      --usb1.1

#define HCD_USB2    0x0020      --usb2.0

static const struct hc_driver ohci_s3c2410_hc_driver=

{

    .flags =        HCD_USB11 | HCD_MEMORY,

}

hcd分配缓冲池,当hc需要使用DMA内存分配器。

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;

--#define HCD_BUFFER_POOLS 4

   我们查看pool_max其实是一个全局数组。如果需要开辟的缓冲区更大的话,直接采用分配page的函数。

static const size_t pool_max [HCD_BUFFER_POOLS] = {

    32,128,512,PAGE_SIZE / 2

};

    for (i = 0; i < HCD_BUFFER_POOLS; i++) {

        size = pool_max[i];

        if (!size)

            continue;

        snprintf(name, sizeof name, "buffer-%d", size);

hcd->pool[i] = dma_pool_create(name, hcd->self.controller,size, size, 0);

        if (!hcd->pool [i]) {

            hcd_buffer_destroy(hcd);

            return -ENOMEM;

        }

    }

    return 0;

}

dma_pool_create创建一个DMA(生成一个dma_pool,并没有分配相应空间,真正分配物理内存将在dma_pool_alloc()总实现)

下面的函数是usb_bus注册,对于该函数也许很难理解。不过参照网上http://www.sudu.cn/info/html/edu/20080425/301909.html的说明,估计会好理解很多。

每个主机控制器拥有一个USB系统,称为一个USB总线。USBD支持多个主机控制器,即多个USB总线。当每增加一个主机控制器时,会给他分配一个usb_bus结构。USBD动态安装和卸载主机驱动。主机驱动安装时,他的初始化函数一方面完成主机控制器硬件的设置和初始化工作,另一方面调用usb_alloc_bususb_register_bus来将自己注册到USBD中去,供USB子系统访问。

static int usb_register_bus(struct usb_bus *bus)

{

    int result = -E2BIG;

    int busnum;

 

    mutex_lock(&usb_bus_list_lock);

    busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);

--busmap来存储主机驱动,一个bit位代表一个主机驱动

    if (busnum >= USB_MAXBUS) {

            return result;

    }

    set_bit (busnum, busmap.busmap);

    bus->busnum = busnum;

    bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0),bus, "usb_host%d", busnum);

  --usb_host类下创建一个usb_host设备。

    list_add (&bus->bus_list, &usb_bus_list);

    mutex_unlock(&usb_bus_list_lock);

    usb_notify_add_bus(bus);

    return 0;

}

那大家也许会好奇该bus有什么作用。那下面就看usb_bus的真实面目。要知道爱一个人,不仅仅是爱她的外表还有内在,真正的她。

struct usb_bus {

    struct device *controller;  --主机的硬件

    int busnum;                  --bus number

    const char *bus_name;          --bus name

    u8 uses_dma;                     --主机控制器是否使用DMA

    u8 otg_port;                     --otg的端口

    unsigned is_b_host:1;      

    unsigned b_hnp_enable:1;

    int devnum_next;              --在循环开辟中下一个打开的设备

    struct usb_devmap devmap;   --设备地址分配匹配。

    struct usb_device *root_hub;    --hub

    struct list_head bus_list;  --buses 链表

 

    int bandwidth_allocated;    --在这个总线上,每个周期多少时间被保留使用。单位:微秒/帧。全速和低速设备保留90%,而高速保留80%

    int bandwidth_int_reqs;     --中断请求个数

    int bandwidth_isoc_reqs;    --等时请求个数

 

#ifdef CONFIG_USB_DEVICEFS

    struct dentry *usbfs_dentry;    --bususbfs目录入口

#endif

    struct device *dev;             --bus设备

};

可以同过usb_register_bus来注册一个usb host总线,当然也可以通过usb_deregister_bus来注销一个usb host的总线。

 

注册bus过后来为usb host分配一个usb_device.记住传入的参数parentNULL,因为该usb hub为根hub

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;

    }

    device_initialize(&dev->dev);

    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));

    dev->state = USB_STATE_ATTACHED;  --hub的状态为以连接

    atomic_set(&dev->urbnum, 0);

 

    INIT_LIST_HEAD(&dev->ep0.urb_list);

    dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;

    dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;

    usb_enable_endpoint(dev, &dev->ep0, true);

    dev->can_submit = 1;

 

    if (unlikely(!parent)) {

        dev->devpath[0] = '0';

 

        dev->dev.parent = bus->controller;

        dev_set_name(&dev->dev, "usb%d", bus->busnum);

        root_hub = 1;

    } else {

        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;

        dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);

    }

 

    dev->portnum = port1;

    dev->bus = bus;

    dev->parent = parent;

    INIT_LIST_HEAD(&dev->filelist);

    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;

}

到此还有两个函数没有看ohci_s3c2410_startregister_root_hub。那还是继续下去,总会有一天会看到路的尽头。

ohci_s3c2410_start这个函数很假,其实他自己没有做什么。

static int ohci_s3c2410_start (struct usb_hcd *hcd)

{

    struct ohci_hcd *ohci = hcd_to_ohci (hcd);

    int ret;

 

    if ((ret = ohci_init(ohci)) < 0)

        return ret;

 

    if ((ret = ohci_run (ohci)) < 0) {

        err ("can't start %s", hcd->self.bus_name);

        ohci_stop (hcd);

        return ret;

    }

    return 0;

}

完成硬件OHCI的初始化和运行。关于OHCI的硬件初始化,以后再专门来看。现在主要看root hub的软件注册。

注册root hub,将root hub注册到usb子系统中。通过usb_new_device来注册一个usb device,同时为root hub分配地址,通常是1

static int register_root_hub(struct usb_hcd *hcd)

{

    struct device *parent_dev = hcd->self.controller;

    struct usb_device *usb_dev = hcd->self.root_hub;

    const int devnum = 1;

    int retval;

 

    usb_dev->devnum = devnum;

    usb_dev->bus->devnum_next = devnum + 1;

    memset (&usb_dev->bus->devmap.devicemap, 0,

            sizeof usb_dev->bus->devmap.devicemap);

    set_bit (devnum, usb_dev->bus->devmap.devicemap);

    usb_set_device_state(usb_dev, USB_STATE_ADDRESS);

 

    mutex_lock(&usb_bus_list_lock);

 

    usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);

    retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);

    if (retval != sizeof usb_dev->descriptor) {

        mutex_unlock(&usb_bus_list_lock);

        return (retval < 0) ? retval : -EMSGSIZE;

    }

 

    retval = usb_new_device (usb_dev); 

    if (retval) {

    }

    mutex_unlock(&usb_bus_list_lock);

 

    if (retval == 0) {

        spin_lock_irq (&hcd_root_hub_lock);

        hcd->rh_registered = 1;

        spin_unlock_irq (&hcd_root_hub_lock);

        if (hcd->state == HC_STATE_HALT)

            usb_hc_died (hcd);  /* This time clean up */

    }

    return retval;

}

到此usb_hcd_s3c2410_probe就算是看完了,不过还是很粗糙。继续!

.总结

在该次学习中主要是了解一个usb主机控制器的加载。记住不是所有的hub都是root hub,对于usb子系统而言,只有唯一的root hubhcd host controller driver其意为主机控制器驱动。对于普通的hub的而言,其仅仅是root hub下挂载的一个设备。不过对于hub的驱动和普通的usb设备驱动是不同的。在linux中会为每一个hub创建一个usb_bus

阅读(569) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册