zoukankan      html  css  js  c++  java
  • 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 ");
        else
            printk(KERN_INFO "usb: DR gadget (%s) registered ",
             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) ", 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 ");
            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 ");
            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! ");
            ret = -ENODEV;
            goto err1a;
        }

        res = otg_get_resources(); //获得资源
        if (!res) {
            DBG("resource not registered! ");
            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 ", 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 ");
            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 ",
                    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 ");
            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 (= 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 (= 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 ", __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 ");
            udc->already_stopped = 1;
            goto out;
        }

        if (mode != USB_MODE_CTRL_MODE_DEVICE) {
            pr_debug("gadget not in device mode, leaving early ");
            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 ");
    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 ", __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 ");
            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 ");
        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两个函数。

  • 相关阅读:
    Qt之qInstallMessageHandler(重定向至文件)
    linux下MySQL安装及设置
    Apache2 同源策略解决方案
    BSD和云 – 不可错过的BSD聚会
    Nginx转发地址解决跨域问题
    Nginx下css的链接问题
    nginx 基本操作
    Azure 媒体服务可将优质内容传输至 Apple TV
    支付宝接口
    drf过滤组件
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5129729.html
Copyright © 2011-2022 走看看