Chinaunix首页 | 论坛 | 博客
  • 博客访问: 34658
  • 博文数量: 14
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 13
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-17 14:40
文章分类
文章存档

2014年(14)

我的朋友

分类: LINUX

2014-09-15 14:50:05

三大控制器已经分析一个啦,下面接着第一篇分析,这篇分析下usb主机控制器设备与驱动的注册:
//代码路径Devices-msm8x60.c (arch\arm\mach-msm)
//主机控制器平台设备的注册
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_IRQ  0x00000400
static u64 dma_mask = 0xffffffffULL;
#ifdef CONFIG_USB_EHCI_MSM_72K
static struct resource resources_hsusb_host[] = {
 {
  .start = 0x12500000,
  .end = 0x12500000 + SZ_1K - 1,
  .flags = IORESOURCE_MEM,
 },
 {
  .start = USB1_HS_IRQ,//132
  .end = USB1_HS_IRQ,
  .flags = IORESOURCE_IRQ,
 },
};
/*主机控制器平台设备*/
struct platform_device   msm_device_hsusb_host = {
 .name = "msm_hsusb_host",
 .id  = 0,
 .num_resources= ARRAY_SIZE(resources_hsusb_host),
 .resource   = resources_hsusb_host,
 .dev    = {
  .dma_mask   = &dma_mask,
  .coherent_dma_mask = 0xffffffffULL,
 },
};
/*要增加的主机控制器的平台设备数组*/
static struct platform_device  *msm_host_devices[] = {
 &msm_device_hsusb_host,
};
/*向系统中注册主机控制器*/
int msm_add_host(unsigned int host, struct msm_usb_host_platform_data  *plat)
{
 struct platform_device *pdev;
 pdev = msm_host_devices[host];
 if (!pdev)
  return -ENODEV;
 pdev->dev.platform_data = plat;
 return platform_device_register(pdev);//向设备驱动模型中注册平台设备
}
#endif

//代码路径Board-msm8x60.c (arch\arm\mach-msm)
//主机控制器的平台数据
static struct msm_usb_host_platform_data   msm_usb_host_pdata = {
 .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_45NM),//集成的PHY,用的45纳米工艺
 .power_budget = 390,//主机最大提供390mA的电流
};
static void __init msm8x60_init(struct msm_board_data *board_data)
{
 msm_add_host(0, &msm_usb_host_pdata);//增加第一个主机控制器
}
//代码路径Ehci-msm72k.c (drivers\usb\host) 
//主机控制器平台驱动的注册
static struct platform_driver ehci_msm_driver = {
 .probe = ehci_msm_probe,//平台驱动的探测函数
 .remove = __exit_p(ehci_msm_remove),
 .driver = {
   .name = "msm_hsusb_host",
      .pm = &ehci_msm_dev_pm_ops, },
};

//代码路径Ehci-hcd.c (drivers\usb\host)
/*平台驱动数组*/
static struct platform_driver *plat_drivers[]  = {
 ...........
#if defined(CONFIG_USB_EHCI_MSM_72K) || defined(CONFIG_USB_EHCI_MSM)
 &ehci_msm_driver,
#endif
 ............
};
/* Keep track of which host controller drivers are loaded */
#define USB_UHCI_LOADED  0
#define USB_OHCI_LOADED  1
#define USB_EHCI_LOADED  2
unsigned long usb_hcds_loaded;
EXPORT_SYMBOL_GPL(usb_hcds_loaded);
static int __init ehci_hcd_init(void)
{
 int i, retval = 0;
 if (usb_disabled())//查看系统是否支持usb?
  return -ENODEV;
 printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);//打印log
 set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);//设置跟踪EHCI主机控制器的bit位
 /*测试是否有UHCI和OHCI控制器驱动已经加载,如果有则给出警告*/
 if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
  printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
    " before uhci_hcd and ohci_hcd, not after\n");
 for (i = 0; i < ARRAY_SIZE(plat_drivers); i++) {
  retval = platform_driver_register(plat_drivers[i]);//向系统中注册平台驱动
  ...................
 }
}
module_init(ehci_hcd_init);

//平台驱动注册过程,如果匹配到设备,会调用驱动的probe函数
static int __devinit ehci_msm_probe(struct platform_device *pdev)
{
 struct usb_hcd *hcd;
 struct resource *res;
 struct msm_usb_host_platform_data  *pdata;
 struct msmusb_hcd *mhcd;
 hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));//创建一个主机控制器
 hcd->irq = platform_get_irq(pdev, 0);//获取平台设备的中断资源132
 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 hcd->rsrc_start = res->start;//获得io内存的起始地址
 hcd->rsrc_len = resource_size(res);//获得io内存的长度
 //获得msm的ehci主机控制器的结构体
 mhcd = hcd_to_mhcd(hcd);//(struct msmusb_hcd *) (hcd->hcd_priv);
 /*初始化struct msmusb_hcd结构体的各个成员*/
 spin_lock_init(&mhcd->lock);
 mhcd->in_lpm = 0;//是否处于低功耗模式?
 mhcd->running = 0;//是否正在运行?
 device_init_wakeup(&pdev->dev, 1);
 pdata = pdev->dev.platform_data;//取出平台设备数据
 hcd->power_budget = pdata->power_budget;//取出主机控制器能提供的最大电流390
 mhcd->pdata = pdata;//保存平台数据到该设备中
 INIT_WORK(&mhcd->lpm_exit_work, usb_lpm_exit_w);//初始化退出低功耗的work
 wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));//初始化该控制器的wake lock
 /*初始化ehci的时钟,并初始化*/
 pdata->ebi1_clk = clk_get(&pdev->dev, "core_clk");
 clk_set_rate(pdata->ebi1_clk, INT_MAX);
 retval = msm_xusb_init_host(pdev, mhcd);//初始化host控制器
 pm_runtime_enable(&pdev->dev);
 return retval;
}
1、创建通用主机控制器结构
/*********************************************************************************************************************/
//在分析前看下这些结构体的关系
struct usb_hcd {
 struct usb_bus  self;  /* hcd is-a bus */
 struct kref      kref;  /* reference counter */
 const char   *product_desc; /* product/vendor string */
 int           speed;      /* Speed for this roothub. May be different from  hcd->driver->flags & HCD_MASK */
 char       irq_descr[24]; /* driver + bus # */
 struct timer_list rh_timer;     /* drives root-hub polling */
 struct urb   *status_urb; /* the current status urb */
#ifdef CONFIG_USB_SUSPEND
 struct work_struct wakeup_work; /* for remote wakeup */
#endif
 /* hardware info/state */
 const struct hc_driver *driver; /* hw-specific hooks */
 unsigned long   flags;
#define HCD_FLAG_HW_ACCESSIBLE           0 /* at full power */
#define HCD_FLAG_SAW_IRQ               1
#define HCD_FLAG_POLL_RH               2 /* poll for rh status? */
#define HCD_FLAG_POLL_PENDING           3 /* status has changed? */
#define HCD_FLAG_WAKEUP_PENDING     4 /* root hub is resuming? */
#define HCD_FLAG_RH_RUNNING               5 /* root hub is running? */
#define HCD_FLAG_DEAD                6 /* controller has died? */
/* The flags can be tested using these macros; they are likely to be slightly faster than test_bit(). */
#define HCD_HW_ACCESSIBLE(hcd)         ((hcd)->flags & (1U << HCD_FLAG_HW_ACCESSIBLE))
#define HCD_SAW_IRQ(hcd)             ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ))
#define HCD_POLL_RH(hcd)             ((hcd)->flags & (1U << HCD_FLAG_POLL_RH))
#define HCD_POLL_PENDING(hcd)         ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING))
#define HCD_WAKEUP_PENDING(hcd)   ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING))
#define HCD_RH_RUNNING(hcd)             ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING))
#define HCD_DEAD(hcd)              ((hcd)->flags & (1U << HCD_FLAG_DEAD))
 /* Flags that get set only during HCD registration or removal. */
 unsigned  rh_registered:1;/* is root hub registered? */
 unsigned  rh_pollable:1; /* may we poll the root hub? */
 unsigned  msix_enabled:1; /* driver has MSI-X enabled? */
 /* The next flag is a stopgap, to be removed when all the HCDs
  * support the new root-hub polling mechanism. */
 unsigned  uses_new_polling:1;
 unsigned  wireless:1; /* Wireless USB HCD */
 unsigned  authorized_default:1;
 unsigned  has_tt:1; /* Integrated TT in root hub */
 int    irq;  /* irq allocated */
 void __iomem *regs;  /* device memory/io */
 u64       rsrc_start; /* memory/io resource start */
 u64       rsrc_len; /* memory/io resource length */
 unsigned  power_budget;/* in mA, 0 = no limit */
 struct mutex  *bandwidth_mutex;
 struct usb_hcd  *shared_hcd;
 struct usb_hcd  *primary_hcd;
#define   HCD_BUFFER_POOLS   4
 struct dma_pool  *pool[HCD_BUFFER_POOLS];
 int   state;
# define __ACTIVE  0x01
# define __SUSPEND  0x04
# define __TRANSIENT  0x80
# define HC_STATE_HALT 0
# define HC_STATE_RUNNING     (__ACTIVE)
# define HC_STATE_QUIESCING     (__SUSPEND|__TRANSIENT|__ACTIVE)
# define HC_STATE_RESUMING     (__SUSPEND|__TRANSIENT)
# define HC_STATE_SUSPENDED  (__SUSPEND)
#define HC_IS_RUNNING(state)         ((state) & __ACTIVE)
#define HC_IS_SUSPENDED(state)    ((state) & __SUSPEND)
 unsigned long hcd_priv[0]//预留空间存放下面的结构体(1)
   __attribute__ ((aligned(sizeof(unsigned long))));
};
struct msmusb_hcd {//(1)
 struct ehci_hcd ehci;//指向下面的结构体(2)
 struct clk *alt_core_clk;
 struct clk *iface_clk;
 unsigned in_lpm;
 struct work_struct lpm_exit_work;
 spinlock_t lock;
 struct wake_lock wlock;
 unsigned int clk_enabled;
 struct msm_usb_host_platform_data *pdata;
 unsigned running;
 struct otg_transceiver *xceiv;
 struct work_struct otg_work;
 unsigned flags;
 struct msm_otg_ops otg_ops;
};
/* one per controller */
struct ehci_hcd {//(2)
 /* glue to PCI and HCD framework */
 struct ehci_caps __iomem *caps;//对应ehci spec上的寄存器
 struct ehci_regs __iomem *regs;
 struct ehci_dbg_port __iomem *debug;
 __u32   hcs_params; /* cached register copy */
 spinlock_t  lock;
 /* async schedule support */
 struct ehci_qh  *async;
 struct ehci_qh  *dummy; /* For AMD quirk use */
 struct ehci_qh  *reclaim;
 struct ehci_qh  *qh_scan_next;
 unsigned  scanning : 1;
 /* periodic schedule support */
#define DEFAULT_I_TDPS  1024  /* some HCs can do less */
 unsigned  periodic_size;
 __hc32   *periodic;      /* hw periodic table */
 dma_addr_t  periodic_dma;
 unsigned  i_thresh;      /* uframes HC might cache */
 union ehci_shadow *pshadow;  /* mirror hw periodic table */
 int    next_uframe;  /* scan periodic, start here */
 unsigned  periodic_sched;  /* periodic activity count */
 /* list of itds & sitds completed while clock_frame was still active */
 struct list_head cached_itd_list;
 struct list_head cached_sitd_list;
 unsigned   clock_frame;
 /* per root hub port */
 unsigned long  reset_done [EHCI_MAX_ROOT_PORTS];
 /* bit vectors (one bit per port) */
 unsigned long  bus_suspended;  /* which ports were already suspended at the start of a bus suspend */
 unsigned long  companion_ports; /* which ports are  dedicated to the companion controller */
 unsigned long  owned_ports;  /* which ports are  owned by the companion during a bus suspend */
 unsigned long  port_c_suspend;  /* which ports have  the change-suspend feature turned on */
 unsigned long  suspended_ports; /* which ports are  suspended */
 /* per-HC memory pools (could be per-bus, but ...) */
 struct dma_pool  *qh_pool; /* qh per active urb */
 struct dma_pool  *qtd_pool; /* one or more per qh */
 struct dma_pool  *itd_pool; /* itd per iso urb */
 struct dma_pool  *sitd_pool; /* sitd per split iso urb */
 struct timer_list iaa_watchdog;
 struct timer_list   watchdog;
 unsigned long  actions;
 unsigned      periodic_stamp;
 unsigned      random_frame;
 unsigned long     next_statechange;
 ktime_t       last_periodic_enable;
 u32           command;
 void (*start_hnp)(struct ehci_hcd *ehci);
 /* SILICON QUIRKS */
 unsigned  no_selective_suspend:1;
 unsigned  has_fsl_port_bug:1; /* FreeScale */
 unsigned  big_endian_mmio:1;
 unsigned  big_endian_desc:1;
 unsigned  big_endian_capbase:1;
 unsigned  has_amcc_usb23:1;
 unsigned  need_io_watchdog:1;
 unsigned  broken_periodic:1;
 unsigned  amd_pll_fix:1;
 unsigned  fs_i_thresh:1;        /* Intel iso scheduling */
 unsigned  use_dummy_qh:1;        /* AMD Frame List table quirk*/
 unsigned  has_synopsys_hc_bug:1; /* Synopsys HC */
 /* required for usb32 quirk */
 #define OHCI_CTRL_HCFS           (3 << 6)
 #define OHCI_USB_OPER            (2 << 6)
 #define OHCI_USB_SUSPEND         (3 << 6)
 #define OHCI_HCCTRL_OFFSET       0x4
 #define OHCI_HCCTRL_LEN          0x4
 __hc32   *ohci_hcctrl_reg;
 unsigned  has_hostpc:1;
 unsigned  has_lpm:1;   /* support link power management */
 unsigned  has_ppcd:1;  /* support per-port change bits */
 u8    sbrn;   /* packed release number */
 ...............
 /* OTG controllers and transceivers need software interaction*/
 struct otg_transceiver *transceiver;//连接OTG核心控制器的桥梁
};
/*************************************************************************************************************/
//hc driver驱动结构体
struct hc_driver {
 const char *description; /* "ehci-hcd" etc */
 const char *product_desc; /* product/vendor string */
 size_t  hcd_priv_size; /* size of private data */
 /* irq handler */
 irqreturn_t (*irq) (struct usb_hcd *hcd);
 int flags;
#define HCD_MEMORY   0x0001 /* HC regs use memory (else I/O) */
#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */
#define HCD_SHARED   0x0004 /* Two (or more) usb_hcds share HW */
#define HCD_USB11   0x0010 /* USB 1.1 */
#define HCD_USB2   0x0020 /* USB 2.0 */
#define HCD_USB3   0x0040 /* USB 3.0 */
#define HCD_MASK   0x0070
 /* called to init HCD and root hub */
 int (*reset) (struct usb_hcd *hcd);
 int (*start) (struct usb_hcd *hcd);
 /* NOTE:  these suspend/resume calls relate to the HC as  a whole, not just the root hub; they're for PCI bus glue.*/
 /* called after suspending the hub, before entering D3 etc */
 int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);
 /* called after entering D0 (etc), before resuming the hub */
 int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
 /* cleanly make HCD stop writing memory and doing I/O */
 void (*stop) (struct usb_hcd *hcd);
 void (*shutdown) (struct usb_hcd *hcd); /* shutdown HCD */
 /* return current frame number */
 int (*get_frame_number) (struct usb_hcd *hcd);
 /* manage i/o requests, device state */
 int (*urb_enqueue)(struct usb_hcd *hcd,  struct urb *urb, gfp_t mem_flags);
 int (*urb_dequeue)(struct usb_hcd *hcd,  struct urb *urb, int status);
 /* hw synch, freeing endpoint resources that urb_dequeue can't */
 void(*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
 /* (optional) these hooks allow an HCD to override the default DMA mapping and unmapping routines.  In general, they shouldn't be
  * necessary unless the host controller has special DMA requirements, such as alignment contraints.  If these are not specified, the
  * general usb_hcd_(un)?map_urb_for_dma functions will be used instead (and it may be a good idea to call these functions in your HCD implementation) */
 int     (*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb,   gfp_t mem_flags);
 void    (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb);
 /* (optional) reset any endpoint state such as sequence number and current window */
 void (*endpoint_reset)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
 /* root hub support */
 int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
 int (*hub_control) (struct usb_hcd *hcd,u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength);
 int (*bus_suspend)(struct usb_hcd *);
 int (*bus_resume)(struct usb_hcd *);
 int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
 /* force handover of high-speed port to full-speed companion */
 void(*relinquish_port)(struct usb_hcd *, int);
 /* has a port been handed over to a companion? */
 int (*port_handed_over)(struct usb_hcd *, int);
 /* CLEAR_TT_BUFFER completion callback */
 void(*clear_tt_buffer_complete)(struct usb_hcd *, struct usb_host_endpoint *);
 /* xHCI specific functions  Called by usb_alloc_dev to alloc HC device structures */
 int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
 /* Called by usb_disconnect to free HC device structures */
 void (*free_dev)(struct usb_hcd *, struct usb_device *);
 /* Change a group of bulk endpoints to support multiple stream IDs */
 int (*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags);
 /* Reverts a group of bulk endpoints back to not using stream IDs.  Can fail if we run out of memory.*/
 int (*free_streams)(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags);
 /* Bandwidth computation functions */
 /* Note that add_endpoint() can only be called once per endpoint before check_bandwidth() or reset_bandwidth() must be called.
  * drop_endpoint() can only be called once per endpoint also. A call to xhci_drop_endpoint() followed by a call to
  * xhci_add_endpoint() will add the endpoint to the schedule with possibly new parameters denoted by a different endpoint descriptor
  * in usb_host_endpoint.  A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is not allowed. */
 
 /* Allocate endpoint resources and add them to a new schedule */
 int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
 
 /* Drop an endpoint from a new schedule */
 int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
 /* Check that a new hardware configuration, set using endpoint_enable and endpoint_disable, does not exceed bus
  * bandwidth.  This must be called before any set configuration or set interface requests are sent to the device.*/
 int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
 /* Reset the device schedule to the last known good schedule, which was set from a previous successful call to
  * check_bandwidth().  This reverts any add_endpoint() and drop_endpoint() calls since that last successful call.
  * Used for when a check_bandwidth() call fails due to resource or bandwidth constraints. */
 void(*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
 /* Returns the hardware-chosen device address */
 int (*address_device)(struct usb_hcd *, struct usb_device *udev);
 /* Notifies the HCD after a hub descriptor is fetched. Will block. */
 int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags);
 int (*reset_device)(struct usb_hcd *, struct usb_device *);
    /* Notifies the HCD after a device is connected and its address is set */
 int (*update_device)(struct usb_hcd *, struct usb_device *);
};
//下面重点看下控制器的分配函数usb_create_hcd()
static struct hc_driver msm_hc_driver = {
 .description  = hcd_name,//static const char hcd_name [] = "ehci_hcd";
 .product_desc   = "Qualcomm On-Chip EHCI Host Controller",
 .hcd_priv_size   = sizeof(struct msmusb_hcd),//实际的控制器结构体分配的大小
 /*generic hardware linkage */
 .irq   = ehci_msm_irq,
 .flags   = HCD_USB2, //#define HCD_USB2 0x0020
 .reset   = ehci_msm_reset,
 .start   = ehci_msm_run,
 .stop     = ehci_stop,
 .shutdown = ehci_shutdown,
 /* managing i/o requests and associated device resources */
 .urb_enqueue  = ehci_urb_enqueue,
 .urb_dequeue  = ehci_urb_dequeue,
 .endpoint_disable = ehci_endpoint_disable,
 /* scheduling support */
 .get_frame_number = ehci_get_frame,
 /* root hub support*/
 .hub_status_data = ehci_hub_status_data,
 .hub_control   = ehci_hub_control,
 .bus_suspend   = ehci_msm_bus_suspend,
 .bus_resume   = ehci_msm_bus_resume,
 .relinquish_port = ehci_relinquish_port,
 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
//下面开始分析该函数
hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name)
{
 return usb_create_shared_hcd(driver, dev, bus_name, NULL);
}
//下面是该函数的具体实现
struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name, struct usb_hcd *primary_hcd)
{
 struct usb_hcd *hcd;
 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);//分配内存,该内存有两部分,hcd+hcd_priv_size
 if (primary_hcd == NULL) {
  hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),GFP_KERNEL);//为互斥体分配内存
  mutex_init(hcd->bandwidth_mutex);//初始化hcd的互斥体
  dev_set_drvdata(dev, hcd);//把该hcd设置为dev的私有数据
 }
 ...........
 kref_init(&hcd->kref);//初始化引用
 /*初始化usb总线*/
 usb_bus_init(&hcd->self);
 hcd->self.controller = dev;
 hcd->self.bus_name = bus_name;
 hcd->self.uses_dma = (dev->dma_mask != NULL);
 /*初始化hcd用的timer*/
 init_timer(&hcd->rh_timer);
 hcd->rh_timer.function = rh_timer_func;//root hub轮询时用的hub
 hcd->rh_timer.data = (unsigned long) hcd;//该timer的function参数
#ifdef CONFIG_USB_SUSPEND
 INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
 hcd->driver = driver;//把driver挂到hcd上
 hcd->speed = driver->flags & HCD_MASK;//usb2.0
 hcd->product_desc = (driver->product_desc) ? driver->product_desc :"USB Host Controller";//为产品描述符赋值
 return hcd;
}
2、初始化EHCI主机控制器
static int msm_xusb_init_host(struct platform_device *pdev,  struct msmusb_hcd *mhcd)
{
 int ret = 0;
 struct msm_otg *otg;
 struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 struct msm_usb_host_platform_data *pdata = mhcd->pdata;
 switch (PHY_TYPE(pdata->phy_info)) {
   case USB_PHY_INTEGRATED:
    ...................
    INIT_WORK(&mhcd->otg_work, msm_hsusb_otg_work);
    mhcd->xceiv = otg_get_transceiver();//从otg控制器中获得收发器,这时可以使用otg核心资源
    otg = container_of(mhcd->xceiv, struct msm_otg, otg);
    hcd->regs = otg->regs;//从otg中取出io寄存器的操作地址
    otg->start_host = msm_hsusb_start_host;//挂载启动主机控制器回调函数
    ehci->start_hnp = ehci_msm_start_hnp;
    ret = otg_set_host(mhcd->xceiv, &hcd->self);//设置otg收发器的host成员
    break;
   default:
    pr_err("phy type is bad\n");
 }
 return ret;
}
//----------------------------------------------------------------------------
2.1 设置收发器的host成员
otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
    otg->set_host(otg, host);
      msm_otg_set_host;
   static int msm_otg_set_host(struct otg_transceiver *xceiv, struct usb_bus *host)
   {
    struct msm_otg *dev = container_of(xceiv, struct msm_otg, otg);
    dev->usbdev_nb.notifier_call = usbdev_notify;//挂载通知链函数
    usb_register_notify(&dev->usbdev_nb);//注册通知链
    dev->otg.host = host;//把主控制器总线挂到otg设备控制器上
    pr_info("host driver registered w/ tranceiver\n");
    ............
    return 0;
   }

//下面看下上面用到的通知链处理函数,主要目的是驱动otg控制器的状态机
static int usbdev_notify(struct notifier_block *self, unsigned long action, void *device)
{
 enum usb_otg_state state;
 struct msm_otg *dev = container_of(self, struct msm_otg, usbdev_nb);
 struct usb_device *udev = device;
 int work = 1;
 unsigned long flags;
 ....................
 spin_lock_irqsave(&dev->lock, flags);
 state = dev->otg.state;
 spin_unlock_irqrestore(&dev->lock, flags);
 switch (state) {
 case OTG_STATE_A_WAIT_BCON:
  if (action == USB_DEVICE_ADD) {
   pr_debug("B_CONN set\n");
   set_bit(B_CONN, &dev->inputs);//设置位域
   .............
  }
  break;
 case OTG_STATE_A_HOST:
  if (action == USB_DEVICE_REMOVE) {
   pr_debug("B_CONN clear\n");
   clear_bit(B_CONN, &dev->inputs);//清除位域
   set_aca_bmaxpower(dev, 0);
  }
  break;
 default:
  work = 0;
  break;
 }
do_work:
 if (work) {
  wake_lock(&dev->wlock);
  queue_work(dev->wq, &dev->sm_work);//调度msm_otg_sm_work,驱动状态机
 }
out:
 return NOTIFY_OK;
}
上面就是主机控制器设备与驱动的注册.下一篇接着分析设备控制器及其驱动的注册。当分析完三大控制器后,看下该控制器三大控制器是如何配合完成android usb子系统的功能。
阅读(1310) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~