usb gadget驱动分析

gadget就是指一些比较杂的小设备,gadget类的一定是作为usb device用。

前面一篇提到usb作为device的情况,注册完成后,导出usb_gadget_probe_driver函数后就完了。

这个函数就是为gadget驱动准备的,这就要从gadget/android.c开始分析,先从它的init函数看起:

static int __init init(void)
{
    struct android_dev *dev;
    int err;
。。。。。。
    android_class = class_create(THIS_MODULE, "android_usb");
    if (IS_ERR(android_class))
        return PTR_ERR(android_class);

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;

    dev->functions = supported_functions;
    INIT_LIST_HEAD(&dev->enabled_functions);
    INIT_WORK(&dev->work, android_work);

    err = android_create_device(dev);                                                                                                                          
    if (err) {
        class_destroy(android_class);
        kfree(dev);
        return err;
    }

    _android_dev = dev;

    /* Override composite driver functions */
    composite_driver.setup = android_setup;
    composite_driver.disconnect = android_disconnect;

    return usb_composite_probe(&android_usb_driver, android_bind);
}

supported_functions代表了gadget驱动支持的类型,如下:

static struct android_usb_function *supported_functions[] = {                                                                                                  
    &adb_function,
    &acm_function,
    &mtp_function,
    &ptp_function,
    &rndis_function,
    &mass_storage_function,
    &accessory_function,
    NULL
};

他们分别代表adb设备、acm(串口设备)、mtp(ptp的扩展)、ptp(图片传输设备)、rndis(网络设备)、U盘设备、accessory(其他一些小附件)。

不管上述何种设备,都是把它们模拟成一个usb从设备来看待,所以必须要有端点0作为控制传输、还要有其它非零端点用来传输数据。

刚好A10 CPU的usb otg 口有一个控制传输端点,4个批量传输端点和一个中断传输端点,可以符合上述的要求。

所以android.c、composite.c 的作用就是依据USB协议模拟出端点0、设备描述符等一个usb设备枚举过程所要求的功能;至于这个usb设备具体做什么,那就是由接口描述符和端点的功能决定,所以f_mtp.c、f_accessory.c、f_mass_storage.c、f_adb.c、f_acm.c等文件所做的事情的就是实现具体接口和这个接口下的端点的具体功能。

android_usb_driver代表了一个设备描述符所要具备的信息和响应的动作:

static struct usb_composite_driver android_usb_driver = {                                                                                                      
    .name       = "android_usb",
    .dev        = &device_desc,
    .strings    = dev_strings,
    .unbind     = android_usb_unbind,
};

device_desc类型为usb_device_descriptor,定义如下:

static struct usb_device_descriptor device_desc = {                                                                                                            
    .bLength              = sizeof(device_desc),
    .bDescriptorType      = USB_DT_DEVICE,
    .bcdUSB               = __constant_cpu_to_le16(0x0200),
    .bDeviceClass         = USB_CLASS_PER_INTERFACE,
    .idVendor             = __constant_cpu_to_le16(VENDOR_ID),
    .idProduct            = __constant_cpu_to_le16(PRODUCT_ID),
    .bcdDevice            = __constant_cpu_to_le16(0xffff),
    .bNumConfigurations   = 1, 
};

这就是一个设备描述符的信息。

接着usb_composite_probe注册一个usb_composite_driver到composite.c 中,如果这一步能注册成功,那么gadget驱动就已经准备好了,随时可以相应主机的请求:

int usb_composite_probe(struct usb_composite_driver *driver,
                   int (*bind)(struct usb_composite_dev *cdev))
{
    if (!driver || !driver->dev || !bind || composite)
        return -EINVAL;

    if (!driver->name)
        driver->name = "composite";
    if (!driver->iProduct)
        driver->iProduct = driver->name;
    composite_driver.function =  (char *) driver->name;
    composite_driver.driver.name = driver->name;
    composite = driver;
    composite_gadget_bind = bind;

    return usb_gadget_probe_driver(&composite_driver, composite_bind);
}

composite_driver代表一个gadget驱动:

static struct usb_gadget_driver composite_driver = {
    .speed      = USB_SPEED_HIGH,

    .unbind     = composite_unbind,

    .setup      = composite_setup,                                                                                                                             
    .disconnect = composite_disconnect,

    .suspend    = composite_suspend,
    .resume     = composite_resume,

    .driver = {
        .owner      = THIS_MODULE,
    },
};

简单的将driver和bind的值保存下来后调用。

usb_gadget_probe_driver函数就是在具体平台中定义的。前面一篇文章说过,这是导出来给gadget驱动用的,它是在usb/sun4i_usb/udc/sw_udc.c中:

int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
                            int (*bind)(struct usb_gadget *))
{
。。。。。。
    if (!bind || !driver->setup || driver->speed < USB_SPEED_FULL) {
        DMSG_PANIC("ERR: Invalid driver: bind %p setup %p speed %d\n",
                   bind, driver->setup, driver->speed);
        return -EINVAL;                                                                                                                                        
    }   
。。。。。。
    /* Bind the driver */
    if ((retval = device_add(&udc->gadget.dev)) != 0) {
        DMSG_PANIC("ERR: Error in device_add() : %d\n",retval);
        goto register_error;
    }

    DMSG_INFO_UDC("[%s]: binding gadget driver '%s'\n", gadget_name, driver->driver.name);

    if ((retval = bind (&udc->gadget)) != 0) {
        DMSG_PANIC("ERR: Error in bind() : %d\n",retval);
        device_del(&udc->gadget.dev);
        goto register_error;
    }

这里才用device_add将设备添加到设备层, 接着回调bind方法:

static int composite_bind(struct usb_gadget *gadget)
{
    struct usb_composite_dev    *cdev;
    int             status = -ENOMEM;

    cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
    if (!cdev)
        return status;

    spin_lock_init(&cdev->lock);
    cdev->gadget = gadget;
    set_gadget_data(gadget, cdev);
    INIT_LIST_HEAD(&cdev->configs);

    /* preallocate control response and buffer */
    cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
    if (!cdev->req)
        goto fail;
    cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
    if (!cdev->req->buf)
        goto fail;
    cdev->req->complete = composite_setup_complete;
    gadget->ep0->driver_data = cdev;
。。。。。。
    if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
        usb_gadget_set_selfpowered(gadget);

    /* interface and string IDs start at zero via kzalloc.
     * we force endpoints to start unassigned; few controller
     * drivers will zero ep->driver_data.
     */
    usb_ep_autoconfig_reset(cdev->gadget);

    /* composite gadget needs to assign strings for whole device (like
     * serial number), register function drivers, potentially update
     * power state and consumption, etc
     */
    status = composite_gadget_bind(cdev);
。。。。。。
    /* standardized runtime overrides for device ID data */
    if (idVendor)
        cdev->desc.idVendor = cpu_to_le16(idVendor);
    if (idProduct)
        cdev->desc.idProduct = cpu_to_le16(idProduct);                                                                                                         
    if (bcdDevice)
        cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);

    /* string overrides */
    if (iManufacturer || !cdev->desc.iManufacturer) {
        if (!iManufacturer && !composite->iManufacturer &&
            !*composite_manufacturer)
            snprintf(composite_manufacturer,
                 sizeof composite_manufacturer,
                 "%s %s with %s",
                 init_utsname()->sysname,
                 init_utsname()->release,
                 gadget->name);

        cdev->manufacturer_override =
            override_id(cdev, &cdev->desc.iManufacturer);
    }

    if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
        cdev->product_override =
            override_id(cdev, &cdev->desc.iProduct);

    if (iSerialNumber)
        cdev->serial_override =
            override_id(cdev, &cdev->desc.iSerialNumber);
。。。。。。
}
首先认识usb_gadget这个结构体,这是具体平台定义的gadget资源,在sun4i_usb/udc/sw_udc.c中:

static struct sw_udc sw_udc = {
    .gadget = {
        .ops        = &sw_udc_ops,
        .ep0        = &sw_udc.ep[0].ep,
        .name       = gadget_name,
        .dev = {
            .init_name  = "gadget",
        },
    },

    /* control endpoint */
    .ep[0] = {
        .num            = 0,
        .ep = {
            .name       = ep0name,
            .ops        = &sw_udc_ep_ops,                                                                                                                      
            .maxpacket  = EP0_FIFO_SIZE,
        },
        .dev            = &sw_udc,
    },

    /* first group of endpoints */
    .ep[1] = {
        .num            = 1,
        .ep = {
            .name       = "ep1-bulk",
            .ops        = &sw_udc_ep_ops,
            .maxpacket  = SW_UDC_EP_FIFO_SIZE,
        },
        .dev                = &sw_udc,
        .fifo_size          = (SW_UDC_EP_FIFO_SIZE * (SW_UDC_FIFO_NUM + 1)),
        .bEndpointAddress   = 1,
        .bmAttributes       = USB_ENDPOINT_XFER_BULK,
    },
。。。。。。
.ep[5] = {
        .num            = 5,
        .ep = {
            .name       = "ep5-int",
            .ops        = &sw_udc_ep_ops,
            .maxpacket  = SW_UDC_EP_FIFO_SIZE,
        },
        .dev                = &sw_udc,
        .fifo_size          = (SW_UDC_EP_FIFO_SIZE * (SW_UDC_FIFO_NUM + 1)),
        .bEndpointAddress   = 5,
        .bmAttributes       = USB_ENDPOINT_XFER_INT,
    },
};
一个端点0,4个bulk和一个int传输全部定义在这里,他们的ops指针都指向同一个sw_udc_ep_ops:

static const struct usb_ep_ops sw_udc_ep_ops = {                                                                                                               
    .enable         = sw_udc_ep_enable,
    .disable        = sw_udc_ep_disable,

    .alloc_request  = sw_udc_alloc_request,
    .free_request   = sw_udc_free_request,

    .queue          = sw_udc_queue,
    .dequeue        = sw_udc_dequeue,

    .set_halt       = sw_udc_set_halt,
};

usb_ep_alloc_request是为端点0分配空间对象,这样才能使用。composite_gadget_bind指针指向了android.c的bind函数:

static int android_bind(struct usb_composite_dev *cdev)
{
    struct android_dev *dev = _android_dev;
    struct usb_gadget   *gadget = cdev->gadget;
    int         gcnum, id, ret;

    usb_gadget_disconnect(gadget);

    ret = android_init_functions(dev->functions, cdev);
。。。。。。
    usb_gadget_set_selfpowered(gadget);
    dev->cdev = cdev;

    return 0;
}

主要是做初始化工作以及为pid、vid赋值等。

好像到这里就完成了,都是注册并填充了一些数据、方法等,那何时才相应主机的请求呢?有中断到来的时候。

在sun4i_usb/udc/sw_udc.c中,中断到来就表示主机有请求了,会调用注册的中断函数sw_udc_irq:

static irqreturn_t sw_udc_irq(int dummy, void *_dev)
{
。。。。。。
    if (tx_irq & USBC_INTTx_FLAG_EP0) {
         DMSG_DBG_UDC("USB ep0 irq\n");

        /* Clear the interrupt bit by setting it to 1 */
        USBC_INT_ClearEpPending(g_sw_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, 0);

        if(dev->gadget.speed == USB_SPEED_UNKNOWN){
            if(USBC_Dev_QueryTransferMode(g_sw_udc_io.usb_bsp_hdle) == USBC_TS_MODE_HS){
                dev->gadget.speed = USB_SPEED_HIGH;

            }else{
                dev->gadget.speed= USB_SPEED_FULL;
            }
        }

        sw_udc_handle_ep0(dev);
    }
。。。。。。
    /* tx endpoint data transfers */
    for (i = 1; i < SW_UDC_ENDPOINTS; i++) {
        u32 tmp = 1 << i;

        if (tx_irq & tmp) {
            DMSG_DBG_UDC("USB tx ep%d irq\n", i);

            /* Clear the interrupt bit by setting it to 1 */
            USBC_INT_ClearEpPending(g_sw_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, i);

            sw_udc_handle_ep(&dev->ep[i]);
        }
    }

    /* rx endpoint data transfers */
    for (i = 1; i < SW_UDC_ENDPOINTS; i++) {
        u32 tmp = 1 << i;

        if (rx_irq & tmp) {
            DMSG_DBG_UDC("USB rx ep%d irq\n", i);

            /* Clear the interrupt bit by setting it to 1 */
            USBC_INT_ClearEpPending(g_sw_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX, i);

            sw_udc_handle_ep(&dev->ep[i]);
        }
    }
。。。。。。
}

主机肯定首先要和ep0通讯的。sw_udc_handle_ep0把请求类型做标记保存下来,最终调用的是dev->driver->setup(&dev->gadget, crq),也就是android.c中的android_setup函数:

static int
android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c)
{
。。。。。。
    list_for_each_entry(f, &dev->enabled_functions, enabled_list) {                                                                                            
        if (f->ctrlrequest) {
            value = f->ctrlrequest(f, cdev, c);
            if (value >= 0)
                break;
        }
    }
。。。。。。
    /* Special case the accessory function.
     * It needs to handle control requests before it is enabled.
     */
    if (value < 0)
        value = acc_ctrlrequest(cdev, c);

    if (value < 0)
        value = composite_setup(gadget, c);

    spin_lock_irqsave(&cdev->lock, flags);
    if (!dev->connected) {
        dev->connected = 1;
        schedule_work(&dev->work);
    }
    else if (c->bRequest == USB_REQ_SET_CONFIGURATION && cdev->config) {
        schedule_work(&dev->work);
    }
    spin_unlock_irqrestore(&cdev->lock, flags);

    return value;
}

enabled_list链表的数据,是上层控制的,上层使用到那个功能才会添加到链表中去,anroid系统常使用到的是mass_storage,adb两个。ctrlrequest方法可以不定义,看具体设备而定。

如果acc_ctrlrequest方法无法处理,最终调用composite_setup来处理,它会相应主机的获取描述符,设置地址等请求。比如获取或者设置接口描述符也在这里完成:

    case USB_REQ_SET_INTERFACE:
        if (ctrl->bRequestType != USB_RECIP_INTERFACE)
            goto unknown;
        if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
            break;
        f = cdev->config->interface[intf];
        if (!f)
            break;
        if (w_value && !f->set_alt)
            break;
        value = f->set_alt(f, w_index, w_value);
        if (value == USB_GADGET_DELAYED_STATUS) {
            DBG(cdev,
             "%s: interface %d (%s) requested delayed status\n",
                    __func__, intf, f->name);
            cdev->delayed_status++;
            DBG(cdev, "delayed_status count %d\n",
                    cdev->delayed_status);
        }
        break;
    case USB_REQ_GET_INTERFACE:
        if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
            goto unknown;
        if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
            break;
        f = cdev->config->interface[intf];
        if (!f)
            break;
        /* lots of interfaces only need altsetting zero... */
        value = f->get_alt ? f->get_alt(f, w_index) : 0;
        if (value < 0)
            break;
        *((u8 *)req->buf) = value;
        value = min(w_length, (u16) 1);
        break;








  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux USB gadget驱动编写有如下几个步骤: 1. 确定USB gadget功能:首先需要确定所需实现的USB gadget功能,例如以USB设备的形式提供存储、网络、音频等服务。这样可以决定需要实现的USB gadget驱动类型和功能。 2. 编写USB驱动框架:基于Linux内核框架,编写USB gadget驱动的基本框架。这包括注册USB gadget驱动和常用的函数接口等。 3. 实现USB gadget子系统:根据所需的功能,实现USB gadget子系统的模块,如存储、网络或音频子系统等。这些子系统需要封装底层的USB通信协议和数据传输,供应用程序调用。 4. 配置USB gadget驱动:根据具体需求,在系统配置文件中进行必要的配置,以启用和配置USB gadget驱动。这包括配置端点、描述符和功能等。 5. 移植和编译:将驱动程序编译成内核模块,然后将其移植到目标设备上。对于嵌入式设备,可能需要修改硬件相关的代码,以适应硬件平台。 6. 测试和调试:编写测试用例,对USB gadget驱动进行测试和调试,确保其正常工作。这包括对设备和主机之间的数据传输进行验证,以及处理异常情况和错误处理。 总之,编写Linux USB gadget驱动需要明确所需实现的功能、基于内核框架编写驱动框架、实现USB gadget子系统、配置以及移植和编译。最后进行测试和调试,确保驱动程序的正常运行。通过以上步骤,可以实现各种USB设备功能的驱动。 ### 回答2: Linux USB gadget驱动是用于实现USB设备的功能的驱动程序。它使得Linux设备可以作为一个USB设备与其他主机进行通信。在编写Linux USB gadget驱动时,需要完成以下几个步骤。 首先,需要确定设备的功能和属性。USB设备可以有多种功能,如储存设备、键盘、鼠标等。根据设备的类型和规格,确定设备的操作和数据传输方式。 其次,在驱动程序中定义设备的USB描述符。USB描述符包括设备描述符、接口描述符和端点描述符等,它们是USB协议的一部分,用于描述设备的属性和功能。 然后,在驱动程序中实现设备的相关功能。根据设备的类型和规格,编写相应的功能代码。例如,如果设备是一个键盘,就需要实现按键事件的处理逻辑;如果设备是一个储存设备,就需要实现读写数据的逻辑。 最后,编译和加载驱动程序。使用Linux内核提供的工具链,将驱动程序编译为可执行文件,并将其加载到Linux内核中运行。加载驱动程序后,系统即可识别设备,并根据驱动程序中定义的功能和属性来处理设备的操作和数据传输。 总之,编写Linux USB gadget驱动需要确定设备的功能和属性、定义USB描述符、实现设备的相关功能,最后编译和加载驱动程序。通过这些步骤,我们可以在Linux系统中实现USB设备的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值