OTG驱动分析(二)

上回介绍了OTG功能的 OTG部分驱动,本片分析OTG功能的从设备部分驱动。从设备的注册过程和OTG的一样,首先注册设备。

流程是:

1.定义platform_device结构。 
2.定义platform_device下的struct resource设备资源结构 
3.定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据) 
4.调用platform_device_register将platform_device结构
注册上面4个过程调用结束后,设备的信息就被注册到系统中,等待驱动的使用 下面是按照上面顺序贴出代码

定义platform_device结构


static struct platform_device __maybe_unused dr_udc_device = {
    .name = "fsl-usb2-udc",
    .id = -1,
    .dev = {
        .release = dr_udc_release,
        .dma_mask = &dr_udc_dmamask,
        .coherent_dma_mask = 0xffffffff,
    },
    .resource = otg_resources,
    .num_resources = ARRAY_SIZE(otg_resources),
};

我们可以看到resource和OTG的resource一样,使用的都是OTG那部分资源

定义platform_device下的struct resource设备资源结构


static struct resource otg_resources[] = {
    [0] = {
     .start = (u32)(USB_OTGREGS_BASE),
     .end = (u32)(USB_OTGREGS_BASE + 0x1ff),
     .flags = IORESOURCE_MEM,
     },
    [1] = {
     .start = MXC_INT_USB_OTG,
     .flags = IORESOURCE_IRQ,
     },
};

定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)


static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {
    .name = "DR",
    .platform_init = usbotg_init,
    .platform_uninit = usbotg_uninit,
    .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
    .power_budget = 500,        /* via RT9706 */
    .gpio_usb_active = gpio_usbotg_utmi_active,
    .gpio_usb_inactive = gpio_usbotg_utmi_inactive,
    .transceiver = "utmi",
    .wake_up_enable = _wake_up_enable,
};
/*将设备注册进系统*/
static inline void dr_register_udc(void)
{
    PDATA->operating_mode = DR_UDC_MODE; //在OTG功能设备注册的时候这里的模式是“FSL_USB2_DR_OTG”,时还不知道为什么不开始就直接把模式写成这个而是后该,现在知道了 因为主从和OTG都在用一个资源,所以谁用就的把模式该了。

/*#define PDATA (&dr_utmi_config)PDATA指的就是上面的结构 */
    dr_udc_device.dev.platform_data = PDATA;

if (platform_device_register(&dr_udc_device))
        printk(KERN_ERR "usb: can‘t register DR gadget\n");
    else
        printk(KERN_INFO "usb: DR gadget (%s) registered\n",
         PDATA->transceiver);
}

上面就完成了设备的注册。

下面看看驱动和设备的链接。


static struct platform_driver udc_driver = {
    .remove = __exit_p(fsl_udc_remove),
    /* these suspend and resume are not usb suspend and resume */
    .suspend = fsl_udc_suspend,  //切换到主设备后的处理过程
    .resume = fsl_udc_resume,  //切换到从设备的处理过程
    .probe = fsl_udc_probe,
    .driver = {
        .name = driver_name, //就是fsl-usb2-udc,用于匹配设备的
        .owner = THIS_MODULE,
    },
}

将驱动注册进系统,如果发现了设备就调用Probe函数,因为我们上面注册了设备所以必然调用Probe


static int __init udc_init(void)
{
    printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION);
    return platform_driver_register(&udc_driver);//驱动注册进系统
}

发现设备后调用PROBE函数
static int __init fsl_udc_probe(struct platform_device *pdev)
{
    struct resource *res;
    struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
    int ret = -ENODEV;
    unsigned int i;
    u32 dccparams, portsc;

if (strcmp(pdev->name, driver_name)) {
        VDBG("Wrong device\n");
        return -ENODEV;
    }
/********************************************************/

static struct fsl_udc *udc_controller;

全局变量定义

/*******************************************************/
    udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
    if (udc_controller == NULL) {
        ERR("malloc udc failed\n");
        return -ENOMEM;
    }
    udc_controller->pdata = pdata; //私有变量赋值

#ifdef CONFIG_USB_OTG
    /* Memory and interrupt resources will be passed from OTG */
    udc_controller->transceiver = otg_get_transceiver();//在OTG功能中已经通过otg_set_transceiver 设置了transceiver结构,这里面就可以GET

if (!udc_controller->transceiver) {
        printk(KERN_ERR "Can‘t find OTG driver!\n");
        ret = -ENODEV;
        goto err1a;
    }

res = otg_get_resources(); //获得资源
    if (!res) {
        DBG("resource not registered!\n");
        return -ENODEV;
    }
#else
    if ((pdev->dev.parent) &&
        (to_platform_device(pdev->dev.parent)->resource)) {
        pdev->resource =
            to_platform_device(pdev->dev.parent)->resource;
        pdev->num_resources =
            to_platform_device(pdev->dev.parent)->num_resources;
    }

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        ret = -ENXIO;
        goto err1a;
    }

if (!request_mem_region(res->start, resource_size(res),
                driver_name)) {
        ERR("request mem region for %s failed \n", pdev->name);
        ret = -EBUSY;
        goto err1a;
    }
#endif
/*将物理地址映射为驱动可以访问的虚拟地址*/
    dr_regs = ioremap(res->start, resource_size(res));
    if (!dr_regs) {
        ret = -ENOMEM;
        goto err1;
    }
    pdata->regs = (void *)dr_regs; //私有数据接收映射地址
    /*
     * do platform specific init: check the clock, grab/config pins, etc.
     */

/*调用私有数据的初始化函数,关于初始化函数下面分析*/
    if (pdata->platform_init && pdata->platform_init(pdev)) {
        ret = -ENODEV;
        goto err2a;
    }

if (pdata->have_sysif_regs)
        usb_sys_regs = (struct usb_sys_interface *)
                ((u32)dr_regs + USB_DR_SYS_OFFSET);

/* Read Device Controller Capability Parameters register */
    dccparams = fsl_readl(&dr_regs->dccparams);
    if (!(dccparams & DCCPARAMS_DC)) {
        ERR("This SOC doesn‘t support device role\n");
        ret = -ENODEV;
        goto err2;
    }
    /* Get max device endpoints */
    /* DEN is bidirectional ep number, max_ep doubles the number */
    udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;

#ifdef CONFIG_USB_OTG
    res++;
    udc_controller->irq = res->start;
#else
    udc_controller->irq = platform_get_irq(pdev, 0);
#endif
    if (!udc_controller->irq) {
        ret = -ENODEV;
        goto err2;
    }
   /*注册中断,该中断和OTG的中断共享一个*/
    ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
            driver_name, udc_controller);
    if (ret != 0) {
        ERR("cannot request irq %d err %d \n",
                udc_controller->irq, ret);
        goto err2;
    }

/* Initialize the udc structure including QH member and other member */

/*对一些资源进行空间开辟等初始化操作*/
    if (struct_udc_setup(udc_controller, pdev)) {
        ERR("Can‘t initialize udc data structure\n");
        ret = -ENOMEM;
        goto err3;
    }

if (!udc_controller->transceiver) {
        /* initialize usb hw reg except for regs for EP,
         * leave usbintr reg untouched */
        dr_controller_setup(udc_controller);
    }

/* Setup gadget structure */
    udc_controller->gadget.ops = &fsl_gadget_ops;
    udc_controller->gadget.is_dualspeed = 1;
    udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
    INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
    udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
    udc_controller->gadget.name = driver_name;

/* Setup gadget.dev and register with kernel */
    dev_set_name(&udc_controller->gadget.dev, "gadget");
    udc_controller->gadget.dev.release = fsl_udc_release;
    udc_controller->gadget.dev.parent = &pdev->dev;
    ret = device_register(&udc_controller->gadget.dev);
    if (ret < 0)
        goto err3;

if (udc_controller->transceiver) {
        udc_controller->gadget.is_otg = 1;
        /* now didn‘t support lpm in OTG mode*/
        device_set_wakeup_capable(&pdev->dev, 0);
    }

/* setup QH and epctrl for ep0 */
    ep0_setup(udc_controller);

/* setup udc->eps[] for ep0 */
    struct_ep_setup(udc_controller, 0, "ep0", 0);
    /* for ep0: the desc defined here;
     * for other eps, gadget layer called ep_enable with defined desc
     */
    udc_controller->eps[0].desc = &fsl_ep0_desc;
    udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD;

/* setup the udc->eps[] for non-control endpoints and link
     * to gadget.ep_list */
    for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) {
        char name[14];

sprintf(name, "ep%dout", i);
        struct_ep_setup(udc_controller, i * 2, name, 1);
        sprintf(name, "ep%din", i);
        struct_ep_setup(udc_controller, i * 2 + 1, name, 1);
    }

/* use dma_pool for TD management */
    udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev,
            sizeof(struct ep_td_struct),
            DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
    if (udc_controller->td_pool == NULL) {
        ret = -ENOMEM;
        goto err4;
    }
    if (g_iram_size) {
        for (i = 0; i < IRAM_PPH_NTD; i++) {
            udc_controller->iram_buffer[i] =
             USB_IRAM_BASE_ADDR + i * g_iram_size;
            udc_controller->iram_buffer_v[i] =
             IO_ADDRESS(udc_controller->iram_buffer[i]);
        }
    }
#ifdef POSTPONE_FREE_LAST_DTD
    last_free_td = NULL;
#endif

/* disable all INTR */
    fsl_writel(0, &dr_regs->usbintr);

dr_wake_up_enable(udc_controller, false);
    udc_controller->stopped = 1;

portsc = fsl_readl(&dr_regs->portsc1);
    portsc |= PORTSCX_PHY_LOW_POWER_SPD;
    fsl_writel(portsc, &dr_regs->portsc1);

if (udc_controller->pdata->usb_clock_for_pm)
        udc_controller->pdata->usb_clock_for_pm(false);

create_proc_file();
    return 0;

err4:
    device_unregister(&udc_controller->gadget.dev);
err3:
    free_irq(udc_controller->irq, udc_controller);
err2:
    if (pdata->platform_uninit)
        pdata->platform_uninit(pdata);
err2a:
    iounmap((u8 __iomem *)dr_regs);
err1:
    if (!udc_controller->transceiver)
        release_mem_region(res->start, resource_size(res));
err1a:
    kfree(udc_controller);
    udc_controller = NULL;
    return ret;
}

当设备使用主设备时 DEVICE的处理


static int udc_suspend(struct fsl_udc *udc)
{
    u32 mode, usbcmd;

/* open clock for register access */
    if (udc_controller->pdata->usb_clock_for_pm)
        udc_controller->pdata->usb_clock_for_pm(true);

mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK;
    usbcmd = fsl_readl(&dr_regs->usbcmd);

pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped);

/*
     * If the controller is already stopped, then this must be a
     * PM suspend. Remember this fact, so that we will leave the
     * controller stopped at PM resume time.
     */
    if (udc->stopped) {
        pr_debug("gadget already stopped, leaving early\n");
        udc->already_stopped = 1;
        goto out;
    }

if (mode != USB_MODE_CTRL_MODE_DEVICE) {
        pr_debug("gadget not in device mode, leaving early\n");
        goto out;
    }

udc->stopped = 1;
    /* if the suspend is not for switch to host in otg mode */
    if ((!(udc->gadget.is_otg)) ||
            (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
        dr_wake_up_enable(udc, true);
        dr_phy_low_power_mode(udc, true);
    }

/* stop the controller */
    usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP;
    fsl_writel(usbcmd, &dr_regs->usbcmd);

printk(KERN_INFO "USB Gadget suspended\n");
out:
    if (udc_controller->pdata->usb_clock_for_pm)
        udc_controller->pdata->usb_clock_for_pm(false);
    return 0;
}

当切换到从设备时调用


static int fsl_udc_resume(struct platform_device *pdev)
{
    pr_debug("%s(): stopped %d already_stopped %d\n", __func__,
         udc_controller->stopped, udc_controller->already_stopped);

/*
     * If the controller was stopped at suspend time, then
     * don‘t resume it now.
     */
    if (udc_controller->already_stopped) {
        udc_controller->already_stopped = 0;
        pr_debug("gadget was already stopped, leaving early\n");
        return 0;
    }

/* Enable DR irq reg and set controller Run */
    if (udc_controller->stopped) {
        dr_wake_up_enable(udc_controller, false);
        dr_phy_low_power_mode(udc_controller, false);
        mdelay(1);

dr_controller_setup(udc_controller);
        dr_controller_run(udc_controller);
    }
    udc_controller->usb_state = USB_STATE_ATTACHED;
    udc_controller->ep0_dir = 0;

printk(KERN_INFO "USB Gadget resumed\n");
    return 0;
}

上面的两个函数就是在上一篇OTG 一中介绍的

gadget_pdrv->resume(gadget_pdev);
gadget_pdrv->suspend(gadget_pdev, otg_suspend_state);

这两处就是只想的这个函数。而把前面的函数和上面这两个指针链接的地方就是在OTG中的

fsl_otg_start_gadget函数。

从上面我们可以看到以下几点:

1.OTG功能的从设备使用的资源和私有数据与OTG设备的一致,(主设备也是一致)

2.从设备主要为OTG功能提供fsl_udc_resume和udc_suspend两个函数。

时间: 2024-11-14 13:45:39

OTG驱动分析(二)的相关文章

OTG驱动分析(一)

前一段时间弄了2个礼拜的OTG驱动调试,感觉精神疲惫啊.主要原因还是自己对OTG功能不了解造成的. 如今最终完毕可是对实质原理还有些模糊.所以自己又一次总结一下. 由于自己是菜鸟,所以用菜鸟的白话方式分析. 高手滤过吧. 所谓OTG功能就是具备该功能的设备就可以当主设备(host)去轮询别人,也能够当从设备(device)去被别人轮--(双性人?). 正所谓全部的产品和功能都是由于需求存在的,举个最简单的需求.原来MP3想传送一个歌曲都得通过电脑.如今仅仅要两个MP3链接,当中一个MP3有OTG

tiny4412 串口驱动分析二 --- printk的实现

作者:彭东林 邮箱:[email protected] 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 u-boot:U-Boot 2010.12 Linux内核版本:linux-3.0.31 Android版本:android-4.1.2 源码:kernel/printk.c asmlinkage int printk(const char *fmt, ...) { va_list args; int r;

块设备驱动框架分析(二)

参考:块设备驱动之一  块设备驱动之二  块设备驱动之三 总结上一篇的块设备驱动的步骤: 1. 分配gendisk: alloc_disk static struct gendisk * ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */2. 设置2.1 分配/设置队列: // 它提供读写能力static struct request_queue  * ramblock_queue = blk_init_queue(do_ramblock_r

RK30SDK开发板驱动分析(二):DDR频率配置

在内核配置界界面,我们可以很容易的配置DDR的频率,300M OR 600M, so easy! 那么它是如何起作用的呢? 回想 RK30SDK开发板驱动分析(一) 末尾提到MACHINE_START是系统开始的地方,位于board-rk30-box.c ,注意里面有个函数rk30_map_io MACHINE_START(RK30, "RK30board") .boot_params = PLAT_PHYS_OFFSET + 0x800, .fixup = rk30_fixup, .

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

Android Binder分析二:Natvie Service的注冊

这一章我们通过MediaPlayerService的注冊来说明怎样在Native层通过binder向ServiceManager注冊一个service,以及client怎样通过binder向ServiceManager获得一个service,并调用这个Service的方法. Native Service的注冊 这里以MediaPlayerService举例来说明怎样在Native层注冊Service,首先来看main_mediaservice.cpp的main方法: int main(int a

【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

[linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9000驱动分析(四):net_device结构体 [linux驱动分析]之dm9000驱动分析(五):另外几个重要的结构体 [linux驱动分析]之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 [linux驱动分析]之dm9000驱动分析(七):dm9000的卸

Android 驱动(二) IIC简介

一. I2C简介 I2C(Inter-Integrated Circuit)总线是一种由 Philips 公司开发的两线式串行总线,用于连接微控制器及其外围设备.I2C 总线最主要的优点就是简单性和有效性,简单体现在接线简单,只有两根线数据线(SCL)和时钟线(SDA),而且 控制简单.所以一些封装较小的器件多使用I2C总线,常见的使用I2C总线的设备有EEPROM.RTC及一些传感器.这里我们介绍下基于linux的I2C设备驱动的编写. I2C设备驱动的编写有多种方式 一种是直接操作CPU的I

S3C6410 LCD驱动分析(转)

一. 理论分析1. 几个概念:FIMC :    Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD:     Fully Interactive Mobile Display (完全交互式移动显示设备)2. 设置VCLK在VIDCON0中bit[3:2]-->Select the Video Clock source =00 --> HCLK=133MHZbit[13:6] --> CLKVAL_F = 13  (这个值是在驱动中计算出来的