设备驱动中的gadget(kernel-4.7)

Linux USB Gadget Driver功能

为了与主机端驱动设备的USB Device Driver概念进行区别,将在外围器件中运行的驱动程序称为USB Gadget Driver。其中,Host端驱动设备的驱动程序是master或者client driver,设备端gadget driver是slave或者function driver。

Gadget Driver和USB Host端驱动程序类似,都是使用请求队列来对I/O包进行缓冲,这些请求可以被提交和取消。它们的结构、消息和常量的定义也和USB技术规范第九章的内容一致。同时也是通过bind和unbind将driver与device建立关系。

Linux USB Gadget Driver核心数据结构

USB_Gadget对象


/**
 * struct usb_gadget - represents a usb slave device
 * @work: (internal use) Workqueue to be used for sysfs_notify()
 * @udc: struct usb_udc pointer for this gadget
 * @ops: Function pointers used to access hardware-specific operations.
 * @ep0: Endpoint zero, used when reading or writing responses to
 *  driver setup() requests
 * @ep_list: List of other endpoints supported by the device.
 * @speed: Speed of current connection to USB host.
 * @max_speed: Maximal speed the UDC can handle.  UDC must support this
 *      and all slower speeds.
 * @state: the state we are now (attached, suspended, configured, etc)
 * @name: Identifies the controller hardware type.  Used in diagnostics
 *  and sometimes configuration.
 * @dev: Driver model state for this abstract device.
 * @out_epnum: last used out ep number
 * @in_epnum: last used in ep number
 * @otg_caps: OTG capabilities of this gadget.
 * @sg_supported: true if we can handle scatter-gather
 * @is_otg: True if the USB device port uses a Mini-AB jack, so that the
 *  gadget driver must provide a USB OTG descriptor.
 * @is_a_peripheral: False unless is_otg, the "A" end of a USB cable
 *  is in the Mini-AB jack, and HNP has been used to switch roles
 *  so that the "A" device currently acts as A-Peripheral, not A-Host.
 * @a_hnp_support: OTG device feature flag, indicating that the A-Host
 *  supports HNP at this port.
 * @a_alt_hnp_support: OTG device feature flag, indicating that the A-Host
 *  only supports HNP on a different root port.
 * @b_hnp_enable: OTG device feature flag, indicating that the A-Host
 *  enabled HNP support.
 * @hnp_polling_support: OTG device feature flag, indicating if the OTG device
 *  in peripheral mode can support HNP polling.
 * @host_request_flag: OTG device feature flag, indicating if A-Peripheral
 *  or B-Peripheral wants to take host role.
 * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
 *  MaxPacketSize.
 * @is_selfpowered: if the gadget is self-powered.
 * @deactivated: True if gadget is deactivated - in deactivated state it cannot
 *  be connected.
 * @connected: True if gadget is connected.
 *
 * Gadgets have a mostly-portable "gadget driver" implementing device
 * functions, handling all usb configurations and interfaces.  Gadget
 * drivers talk to hardware-specific code indirectly, through ops vectors.
 * That insulates the gadget driver from hardware details, and packages
 * the hardware endpoints through generic i/o queues.  The "usb_gadget"
 * and "usb_ep" interfaces provide that insulation from the hardware.
 *
 * Except for the driver data, all fields in this structure are
 * read-only to the gadget driver.  That driver data is part of the
 * "driver model" infrastructure in 2.6 (and later) kernels, and for
 * earlier systems is grouped in a similar structure that's not known
 * to the rest of the kernel.
 *
 * Values of the three OTG device feature flags are updated before the
 * setup() call corresponding to USB_REQ_SET_CONFIGURATION, and before
 * driver suspend() calls.  They are valid only when is_otg, and when the
 * device is acting as a B-Peripheral (so is_a_peripheral is false).
 */
struct usb_gadget {
    struct work_struct      work;
    struct usb_udc          *udc;
    /* readonly to gadget driver */
    const struct usb_gadget_ops *ops; //Gadget设备操作函数集
    struct usb_ep           *ep0;//控制端点,只对setup包响应
    struct list_head        ep_list;/* of usb_ep *///将设备的所有端点连成链表,ep0不在其中
    enum usb_device_speed       speed; //高速、全速和低速
    enum usb_device_speed       max_speed;
    enum usb_device_state       state;
    const char          *name; //器件名称
    struct device           dev; //内核设备模型使用
    unsigned            out_epnum;
    unsigned            in_epnum;
    struct usb_otg_caps     *otg_caps;

    unsigned            sg_supported:1;
    unsigned            is_otg:1;
    unsigned            is_a_peripheral:1;
    unsigned            b_hnp_enable:1;
    unsigned            a_hnp_support:1;
    unsigned            a_alt_hnp_support:1;
    unsigned            hnp_polling_support:1;
    unsigned            host_request_flag:1;
    unsigned            quirk_ep_out_aligned_size:1;
    unsigned            quirk_altset_not_supp:1;
    unsigned            quirk_stall_not_supp:1;
    unsigned            quirk_zlp_not_supp:1;
    unsigned            is_selfpowered:1;
    unsigned            deactivated:1;
    unsigned            connected:1;
};

Gadget器件操作函数集
操作UDC硬件的API,但操作端点的函数由端点操作函数集完成

/* the rest of the api to the controller hardware: device operations,
 * which don't involve endpoints (or i/o).
 */
struct usb_gadget_ops {
    int (*get_frame)(struct usb_gadget *);
    int (*wakeup)(struct usb_gadget *);
    int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
    int (*vbus_session) (struct usb_gadget *, int is_active);
    int (*vbus_draw) (struct usb_gadget *, unsigned mA);
    int (*pullup) (struct usb_gadget *, int is_on);
    int (*ioctl)(struct usb_gadget *,
                unsigned code, unsigned long param);
    void    (*get_config_params)(struct usb_dcd_config_params *);
    int (*udc_start)(struct usb_gadget *,
            struct usb_gadget_driver *);
    int (*udc_stop)(struct usb_gadget *);
    struct usb_ep *(*match_ep)(struct usb_gadget *,
            struct usb_endpoint_descriptor *,
            struct usb_ss_ep_comp_descriptor *);
};

USB Gadget driver对象


/*-------------------------------------------------------------------------*/

/**
 * struct usb_gadget_driver - driver for usb 'slave' devices
 * @function: String describing the gadget's function
 * @max_speed: Highest speed the driver handles.
 * @setup: Invoked for ep0 control requests that aren't handled by
 *  the hardware level driver. Most calls must be handled by
 *  the gadget driver, including descriptor and configuration
 *  management.  The 16 bit members of the setup data are in
 *  USB byte order. Called in_interrupt; this may not sleep.  Driver
 *  queues a response to ep0, or returns negative to stall.
 * @disconnect: Invoked after all transfers have been stopped,
 *  when the host is disconnected.  May be called in_interrupt; this
 *  may not sleep.  Some devices can't detect disconnect, so this might
 *  not be called except as part of controller shutdown.
 * @bind: the driver's bind callback
 * @unbind: Invoked when the driver is unbound from a gadget,
 *  usually from rmmod (after a disconnect is reported).
 *  Called in a context that permits sleeping.
 * @suspend: Invoked on USB suspend.  May be called in_interrupt.
 * @resume: Invoked on USB resume.  May be called in_interrupt.
 * @reset: Invoked on USB bus reset. It is mandatory for all gadget drivers
 *  and should be called in_interrupt.
 * @driver: Driver model state for this driver.
 * @udc_name: A name of UDC this driver should be bound to. If udc_name is NULL,
 *  this driver will be bound to any available UDC.
 * @pending: UDC core private data used for deferred probe of this driver.
 * @match_existing_only: If udc is not found, return an error and don't add this
 *      gadget driver to list of pending driver
 *
 * Devices are disabled till a gadget driver successfully bind()s, which
 * means the driver will handle setup() requests needed to enumerate (and
 * meet "chapter 9" requirements) then do some useful work.
 *
 * If gadget->is_otg is true, the gadget driver must provide an OTG
 * descriptor during enumeration, or else fail the bind() call.  In such
 * cases, no USB traffic may flow until both bind() returns without
 * having called usb_gadget_disconnect(), and the USB host stack has
 * initialized.
 *
 * Drivers use hardware-specific knowledge to configure the usb hardware.
 * endpoint addressing is only one of several hardware characteristics that
 * are in descriptors the ep0 implementation returns from setup() calls.
 *
 * Except for ep0 implementation, most driver code shouldn't need change to
 * run on top of different usb controllers.  It'll use endpoints set up by
 * that ep0 implementation.
 *
 * The usb controller driver handles a few standard usb requests.  Those
 * include set_address, and feature flags for devices, interfaces, and
 * endpoints (the get_status, set_feature, and clear_feature requests).
 *
 * Accordingly, the driver's setup() callback must always implement all
 * get_descriptor requests, returning at least a device descriptor and
 * a configuration descriptor.  Drivers must make sure the endpoint
 * descriptors match any hardware constraints. Some hardware also constrains
 * other descriptors. (The pxa250 allows only configurations 1, 2, or 3).
 *
 * The driver's setup() callback must also implement set_configuration,
 * and should also implement set_interface, get_configuration, and
 * get_interface.  Setting a configuration (or interface) is where
 * endpoints should be activated or (config 0) shut down.
 *
 * (Note that only the default control endpoint is supported.  Neither
 * hosts nor devices generally support control traffic except to ep0.)
 *
 * Most devices will ignore USB suspend/resume operations, and so will
 * not provide those callbacks.  However, some may need to change modes
 * when the host is not longer directing those activities.  For example,
 * local controls (buttons, dials, etc) may need to be re-enabled since
 * the (remote) host can't do that any longer; or an error state might
 * be cleared, to make the device behave identically whether or not
 * power is maintained.
 */
struct usb_gadget_driver {
    char            *function; //驱动名称
    enum usb_device_speed   max_speed; //USB设备速度类型
    int         (*bind)(struct usb_gadget *gadget,
                    struct usb_gadget_driver *driver); //将驱动和设备绑定,一般在驱动注册时调用
    void            (*unbind)(struct usb_gadget *);//卸载驱动时调用,rmmod时调用
    int         (*setup)(struct usb_gadget *,
                    const struct usb_ctrlrequest *); //处理ep0的控制请求,在中断中调用,不能睡眠
    void            (*disconnect)(struct usb_gadget *); //可能在中断中调用不能睡眠
    void            (*suspend)(struct usb_gadget *); //电源管理模式相关,设备挂起
    void            (*resume)(struct usb_gadget *); //电源管理模式相关,设备恢复
    void            (*reset)(struct usb_gadget *);

    /* FIXME support safe rmmod */
    struct device_driver    driver; //内核设备管理使用

    char            *udc_name;
    struct list_head    pending;
    unsigned                match_existing_only:1;
};

描述一个I/O请求


struct usb_ep;

/**
 * struct usb_request - describes one i/o request
 * @buf: Buffer used for data.  Always provide this; some controllers
 *  only use PIO, or don't use DMA for some endpoints.
 * @dma: DMA address corresponding to 'buf'.  If you don't set this
 *  field, and the usb controller needs one, it is responsible
 *  for mapping and unmapping the buffer.
 * @sg: a scatterlist for SG-capable controllers.
 * @num_sgs: number of SG entries
 * @num_mapped_sgs: number of SG entries mapped to DMA (internal)
 * @length: Length of that data
 * @stream_id: The stream id, when USB3.0 bulk streams are being used
 * @no_interrupt: If true, hints that no completion irq is needed.
 *  Helpful sometimes with deep request queues that are handled
 *  directly by DMA controllers.
 * @zero: If true, when writing data, makes the last packet be "short"
 *     by adding a zero length packet as needed;
 * @short_not_ok: When reading data, makes short packets be
 *     treated as errors (queue stops advancing till cleanup).
 * @complete: Function called when request completes, so this request and
 *  its buffer may be re-used.  The function will always be called with
 *  interrupts disabled, and it must not sleep.
 *  Reads terminate with a short packet, or when the buffer fills,
 *  whichever comes first.  When writes terminate, some data bytes
 *  will usually still be in flight (often in a hardware fifo).
 *  Errors (for reads or writes) stop the queue from advancing
 *  until the completion function returns, so that any transfers
 *  invalidated by the error may first be dequeued.
 * @context: For use by the completion callback
 * @list: For use by the gadget driver.
 * @status: Reports completion code, zero or a negative errno.
 *  Normally, faults block the transfer queue from advancing until
 *  the completion callback returns.
 *  Code "-ESHUTDOWN" indicates completion caused by device disconnect,
 *  or when the driver disabled the endpoint.
 * @actual: Reports bytes transferred to/from the buffer.  For reads (OUT
 *  transfers) this may be less than the requested length.  If the
 *  short_not_ok flag is set, short reads are treated as errors
 *  even when status otherwise indicates successful completion.
 *  Note that for writes (IN transfers) some data bytes may still
 *  reside in a device-side FIFO when the request is reported as
 *  complete.
 *
 * These are allocated/freed through the endpoint they're used with.  The
 * hardware's driver can add extra per-request data to the memory it returns,
 * which often avoids separate memory allocations (potential failures),
 * later when the request is queued.
 *
 * Request flags affect request handling, such as whether a zero length
 * packet is written (the "zero" flag), whether a short read should be
 * treated as an error (blocking request queue advance, the "short_not_ok"
 * flag), or hinting that an interrupt is not required (the "no_interrupt"
 * flag, for use with deep request queues).
 *
 * Bulk endpoints can use any size buffers, and can also be used for interrupt
 * transfers. interrupt-only endpoints can be much less functional.
 *
 * NOTE:  this is analogous to 'struct urb' on the host side, except that
 * it's thinner and promotes more pre-allocation.
 */

struct usb_request {
    void            *buf;  //数据缓存区
    unsigned        length; //数据长度
    dma_addr_t      dma;  //与buf关联的DMA地址,DMA传输时使用

    struct scatterlist  *sg;
    unsigned        num_sgs;
    unsigned        num_mapped_sgs;

    unsigned        stream_id:16;
    unsigned        no_interrupt:1; //当为true时,表示没有完成函数,则通过中断通知传输完成,这个由DMA控制器直接控制
    unsigned        zero:1; //当输出的最后一个数据包不够长度是是否填充0
    unsigned        short_not_ok:1; //当接收的数据不够指定长度时,是否报错

    void            (*complete)(struct usb_ep *ep,
                    struct usb_request *req); //请求完成函数
    void            *context;  //被completion回调函数使用
    struct list_head    list;  //被Gadget Driver使用,插入队列

    int         status;  //返回完成结果,0表示成功
    unsigned        actual;  //实际传输的数据长度
};

端点


/**
 * struct usb_ep - device side representation of USB endpoint
 * @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk"
 * @ops: Function pointers used to access hardware-specific operations.
 * @ep_list:the gadget's ep_list holds all of its endpoints
 * @caps:The structure describing types and directions supported by endoint.
 * @maxpacket:The maximum packet size used on this endpoint.  The initial
 *  value can sometimes be reduced (hardware allowing), according to
 *      the endpoint descriptor used to configure the endpoint.
 * @maxpacket_limit:The maximum packet size value which can be handled by this
 *  endpoint. It's set once by UDC driver when endpoint is initialized, and
 *  should not be changed. Should not be confused with maxpacket.
 * @max_streams: The maximum number of streams supported
 *  by this EP (0 - 16, actual number is 2^n)
 * @mult: multiplier, 'mult' value for SS Isoc EPs
 * @maxburst: the maximum number of bursts supported by this EP (for usb3)
 * @driver_data:for use by the gadget driver.
 * @address: used to identify the endpoint when finding descriptor that
 *  matches connection speed
 * @desc: endpoint descriptor.  This pointer is set before the endpoint is
 *  enabled and remains valid until the endpoint is disabled.
 * @comp_desc: In case of SuperSpeed support, this is the endpoint companion
 *  descriptor that is used to configure the endpoint
 *
 * the bus controller driver lists all the general purpose endpoints in
 * gadget->ep_list.  the control endpoint (gadget->ep0) is not in that list,
 * and is accessed only in response to a driver setup() callback.
 */

struct usb_ep {
    void            *driver_data;   //端点私有数据
    const char      *name;  //端点名称
    const struct usb_ep_ops *ops; //端点操作函数集
    struct list_head    ep_list;  //Gadget设备建立所有端点的链表
    struct usb_ep_caps  caps;
    bool            claimed;
    bool            enabled;
    unsigned        maxpacket:16; //这个端点使用的最大包长度
    unsigned        maxpacket_limit:16;
    unsigned        max_streams:16;
    unsigned        mult:2;
    unsigned        maxburst:5;
    u8          address;
    const struct usb_endpoint_descriptor    *desc;
    const struct usb_ss_ep_comp_descriptor  *comp_desc;
};

端点操作函数集



struct usb_gadget;
struct usb_gadget_driver;
struct usb_udc;

/* the rest of the api to the controller hardware: device operations,
 * which don't involve endpoints (or i/o).
 */
struct usb_gadget_ops {
    int (*get_frame)(struct usb_gadget *);
    int (*wakeup)(struct usb_gadget *);
    int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
    int (*vbus_session) (struct usb_gadget *, int is_active);
    int (*vbus_draw) (struct usb_gadget *, unsigned mA);
    int (*pullup) (struct usb_gadget *, int is_on);
    int (*ioctl)(struct usb_gadget *,
                unsigned code, unsigned long param);
    void    (*get_config_params)(struct usb_dcd_config_params *);
    int (*udc_start)(struct usb_gadget *,
            struct usb_gadget_driver *);
    int (*udc_stop)(struct usb_gadget *);
    struct usb_ep *(*match_ep)(struct usb_gadget *,
            struct usb_endpoint_descriptor *,
            struct usb_ss_ep_comp_descriptor *);
};

字符串结构


/*-------------------------------------------------------------------------*/

/* utility to simplify dealing with string descriptors */

/**
 * struct usb_string - wraps a C string and its USB id
 * @id:the (nonzero) ID for this string
 * @s:the string, in UTF-8 encoding
 *
 * If you're using usb_gadget_get_string(), use this to wrap a string
 * together with its ID.
 */
struct usb_string {
    u8          id;
    const char      *s;
};


/**
 * struct usb_gadget_strings - a set of USB strings in a given language
 * @language:identifies the strings' language (0x0409 for en-us)
 * @strings:array of strings with their ids
 *
 * If you're using usb_gadget_get_string(), use this to wrap all the
 * strings for a given language.
 */
struct usb_gadget_strings {
    u16         language;   /* 0x0409 for en-us */
    struct usb_string   *strings;
};

UDC驱动程序

UDC层主要数据结构,以S3C2410为例,在drivers/usb/gadget/s3c2410_udc.cs3c2410_udc.h文件中。
下面的结构基本上每个UDC驱动程序都会实现,但具体实现的细节又不太相同。但万变不离其宗,宗就是上面介绍的基本gadget驱动数据结构,基本上UDC驱动程序自己实现的数据结构都是都这些基本数据结构的二次封装。
设备结构


struct s3c2410_udc {
    spinlock_t          lock;

    struct s3c2410_ep       ep[S3C2410_ENDPOINTS];
    int             address;
    struct usb_gadget       gadget;
    struct usb_gadget_driver    *driver;
    struct s3c2410_request      fifo_req;
    u8              fifo_buf[EP_FIFO_SIZE];
    u16             devstatus;

    u32             port_status;
    int             ep0state;

    unsigned            got_irq : 1;

    unsigned            req_std : 1;
    unsigned            req_config : 1;
    unsigned            req_pending : 1;
    u8              vbus;
    struct dentry           *regs_info;
};

程序中对这个结构的初始化:


/*---------------------------------------------------------------------------*/
static struct s3c2410_udc memory = {
    .gadget = {
        .ops        = &s3c2410_ops,
        .ep0        = &memory.ep[0].ep,
        .name       = gadget_name,
        .dev = {
            .init_name  = "gadget",
        },
    },

    /* control endpoint */
    .ep[0] = {
        .num        = 0,
        .ep = {
            .name       = ep0name,
            .ops        = &s3c2410_ep_ops,
            .maxpacket  = EP0_FIFO_SIZE,
            .caps       = USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL,
                        USB_EP_CAPS_DIR_ALL),
        },
        .dev        = &memory,
    },

    /* first group of endpoints */
    .ep[1] = {
        .num        = 1,
        .ep = {
            .name       = "ep1-bulk",
            .ops        = &s3c2410_ep_ops,
            .maxpacket  = EP_FIFO_SIZE,
            .caps       = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
                        USB_EP_CAPS_DIR_ALL),
        },
        .dev        = &memory,
        .fifo_size  = EP_FIFO_SIZE,
        .bEndpointAddress = 1,
        .bmAttributes   = USB_ENDPOINT_XFER_BULK,
    },
    .ep[2] = {
        .num        = 2,
        .ep = {
            .name       = "ep2-bulk",
            .ops        = &s3c2410_ep_ops,
            .maxpacket  = EP_FIFO_SIZE,
            .caps       = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
                        USB_EP_CAPS_DIR_ALL),
        },
        .dev        = &memory,
        .fifo_size  = EP_FIFO_SIZE,
        .bEndpointAddress = 2,
        .bmAttributes   = USB_ENDPOINT_XFER_BULK,
    },
    .ep[3] = {
        .num        = 3,
        .ep = {
            .name       = "ep3-bulk",
            .ops        = &s3c2410_ep_ops,
            .maxpacket  = EP_FIFO_SIZE,
            .caps       = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
                        USB_EP_CAPS_DIR_ALL),
        },
        .dev        = &memory,
        .fifo_size  = EP_FIFO_SIZE,
        .bEndpointAddress = 3,
        .bmAttributes   = USB_ENDPOINT_XFER_BULK,
    },
    .ep[4] = {
        .num        = 4,
        .ep = {
            .name       = "ep4-bulk",
            .ops        = &s3c2410_ep_ops,
            .maxpacket  = EP_FIFO_SIZE,
            .caps       = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
                        USB_EP_CAPS_DIR_ALL),
        },
        .dev        = &memory,
        .fifo_size  = EP_FIFO_SIZE,
        .bEndpointAddress = 4,
        .bmAttributes   = USB_ENDPOINT_XFER_BULK,
    }

};

不同的UDC,自定义的数据结构不同。但一般都有这样一个数据结构来表示UDC设备,对usb_gadget设备对象进行封装,并包含设备的所有端点。

端点结构


struct s3c2410_ep {
    struct list_head        queue;
    unsigned long           last_io;    /* jiffies timestamp */
    struct usb_gadget       *gadget;
    struct s3c2410_udc      *dev;
    struct usb_ep           ep; //封装的struct usb_ep结构
    u8              num;

    unsigned short          fifo_size;
    u8              bEndpointAddress;
    u8              bmAttributes;

    unsigned            halted : 1;
    unsigned            already_seen : 1;
    unsigned            setup_stage : 1;
};

usb_ep结构进行封装,并有一个queue队列来对该端口上的request进行排队。

Request结构


struct s3c2410_request {
    struct list_head        queue;      /* ep's requests */
    struct usb_request      req;
};

usb_request进行封装,queue变量进行队列排队。

UDC驱动是作为platform driver向platform子系统注册的,因此UDC驱动首先就需要实现struct platform_driver结构中的函数成员:


static struct platform_driver udc_driver_24x0 = {
    .driver     = {
        .name   = "s3c24x0-usbgadget",
    },
    .probe      = s3c2410_udc_probe,      //驱动和设备绑定
    .remove     = s3c2410_udc_remove,     //支持热插拔的设备移除
    .suspend    = s3c2410_udc_suspend,     //电源管理相关,挂起设备
    .resume     = s3c2410_udc_resume,        //电源管理相关,恢复设备
    .id_table   = s3c_udc_ids,        //驱动和设备匹配信息
};

其中以s3c2410_udc_probes3c2410_udc_remove函数最为重要,s3c2410_udc_probe函数实现驱动和设备的匹配绑定,并分配资源;而s3c2410_udc_remove函数实现资源的释放。


/*
 *  probe - binds to the platform device
 */
static int s3c2410_udc_probe(struct platform_device *pdev)
{
    struct s3c2410_udc *udc = &memory;  //s3c2410的UDC设备,在其中对usb_gadget设备对象和端点等进行了初始化
    struct device *dev = &pdev->dev;
    int retval;
    int irq;

    dev_dbg(dev, "%s()\n", __func__);

    usb_bus_clock = clk_get(NULL, "usb-bus-gadget");    //获取总线时钟并使能
    if (IS_ERR(usb_bus_clock)) {
        dev_err(dev, "failed to get usb bus clock source\n");
        return PTR_ERR(usb_bus_clock);
    }

    clk_prepare_enable(usb_bus_clock);

    udc_clock = clk_get(NULL, "usb-device");   //获取设备时钟并使能
    if (IS_ERR(udc_clock)) {
        dev_err(dev, "failed to get udc clock source\n");
        return PTR_ERR(udc_clock);
    }

    clk_prepare_enable(udc_clock);

    mdelay(10);

    dev_dbg(dev, "got and enabled clocks\n");

    if (strncmp(pdev->name, "s3c2440", 7) == 0) {
        dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
        memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
        memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
        memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
        memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
    }

    spin_lock_init(&udc->lock);  //获取设备时钟并使能
    udc_info = dev_get_platdata(&pdev->dev);

    rsrc_start = S3C2410_PA_USBDEV;  //s3c2410 UDC端口起始地址
    rsrc_len   = S3C24XX_SZ_USBDEV;  //s3c2410端口地址长度

    if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))  //申请端口资源
        return -EBUSY;

    base_addr = ioremap(rsrc_start, rsrc_len);    //端口映射
    if (!base_addr) {
        retval = -ENOMEM;
        goto err_mem;
    }

    the_controller = udc;
    platform_set_drvdata(pdev, udc); //驱动和设备绑定,在platform_device结构中保存udc设备对象

    /*重新初始化设备*/
    s3c2410_udc_disable(udc);
    s3c2410_udc_reinit(udc);

    /* irq setup after old hardware state is cleaned up */ 
    /*申请中断,并绑定中断函数,中断函数是UDC功能驱动的入口函数*/
    retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
                 0, gadget_name, udc);

    if (retval != 0) {
        dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
        retval = -EBUSY;
        goto err_map;
    }

    dev_dbg(dev, "got irq %i\n", IRQ_USBD);

    if (udc_info && udc_info->vbus_pin > 0) {
        retval = gpio_request(udc_info->vbus_pin, "udc vbus");
        if (retval < 0) {
            dev_err(dev, "cannot claim vbus pin\n");
            goto err_int;
        }

        irq = gpio_to_irq(udc_info->vbus_pin);
        if (irq < 0) {
            dev_err(dev, "no irq for gpio vbus pin\n");
            retval = irq;
            goto err_gpio_claim;
        }

        retval = request_irq(irq, s3c2410_udc_vbus_irq,
                     IRQF_TRIGGER_RISING
                     | IRQF_TRIGGER_FALLING | IRQF_SHARED,
                     gadget_name, udc);

        if (retval != 0) {
            dev_err(dev, "can't get vbus irq %d, err %d\n",
                irq, retval);
            retval = -EBUSY;
            goto err_gpio_claim;
        }

        dev_dbg(dev, "got irq %i\n", irq);
    } else {
        udc->vbus = 1;
    }

    if (udc_info && !udc_info->udc_command &&
        gpio_is_valid(udc_info->pullup_pin)) {

        retval = gpio_request_one(udc_info->pullup_pin,
                udc_info->vbus_pin_inverted ?
                GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
                "udc pullup");
        if (retval)
            goto err_vbus_irq;
    }

    retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
    if (retval)
        goto err_add_udc;

    if (s3c2410_udc_debugfs_root) {    //创建虚拟文件debugfs
        udc->regs_info = debugfs_create_file("registers", S_IRUGO,
                s3c2410_udc_debugfs_root,
                udc, &s3c2410_udc_debugfs_fops);
        if (!udc->regs_info)
            dev_warn(dev, "debugfs file creation failed\n");
    }

    dev_dbg(dev, "probe ok\n");

    return 0;

err_add_udc:
    if (udc_info && !udc_info->udc_command &&
            gpio_is_valid(udc_info->pullup_pin))
        gpio_free(udc_info->pullup_pin);
err_vbus_irq:
    if (udc_info && udc_info->vbus_pin > 0)
        free_irq(gpio_to_irq(udc_info->vbus_pin), udc);
err_gpio_claim:
    if (udc_info && udc_info->vbus_pin > 0)
        gpio_free(udc_info->vbus_pin);
err_int:
    free_irq(IRQ_USBD, udc);
err_map:
    iounmap(base_addr);
err_mem:
    release_mem_region(rsrc_start, rsrc_len);

    return retval;
}

s3c2410_udc_probe函数可以看出,probe函数主要完成的就是将platform_device设备对象和UDC设备对象建立关系,UDC设备和驱动的一些初始化工作,并申请驱动所需的资源,若端口区间、中断号等,并将中断函数和中断号绑定。这个中断处理函数非常重要,对这个设备的所有操作都将从中断函数入口


/*
 *  s3c2410_udc_remove
 */
static int s3c2410_udc_remove(struct platform_device *pdev)
{
    struct s3c2410_udc *udc = platform_get_drvdata(pdev); //获取UDC设备对象,在probe函数中绑定的
    unsigned int irq;

    dev_dbg(&pdev->dev, "%s()\n", __func__);

    if (udc->driver)  //设备的驱动usb_gadget_driver对象,说明设备正在使用
        return -EBUSY;

    usb_del_gadget_udc(&udc->gadget);
    debugfs_remove(udc->regs_info); //移除debugfs文件系统中建立的文件

    if (udc_info && !udc_info->udc_command &&
        gpio_is_valid(udc_info->pullup_pin))
        gpio_free(udc_info->pullup_pin);

    if (udc_info && udc_info->vbus_pin > 0) {
        irq = gpio_to_irq(udc_info->vbus_pin);
        free_irq(irq, udc);
    }

    free_irq(IRQ_USBD, udc);  //释放中断

    iounmap(base_addr);  /*释放端口资源*/
    release_mem_region(rsrc_start, rsrc_len);

    /*释放时钟*/
    if (!IS_ERR(udc_clock) && udc_clock != NULL) {
        clk_disable_unprepare(udc_clock);
        clk_put(udc_clock);
        udc_clock = NULL;
    }

    if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
        clk_disable_unprepare(usb_bus_clock);
        clk_put(usb_bus_clock);
        usb_bus_clock = NULL;
    }

    dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
    return 0;
}

可以看出,remove函数基本上是probe函数的逆操作,将probe函数中申请的资源释放掉。

UDC驱动程序还需要为上层实现usb_gadget_register_driverusb_gadget_unregister_driver两个gadget driver注册接口,这两个函数将实现gadget driverudc driver绑定。

中断函数
中断处理函数是UDC驱动层的核心函数,由于UDC是从设备,主机端是控制端,所有的操作都是主机端发起的,所以中断处理函数是UDC驱动层的核心函数。


/*
 *  s3c2410_udc_irq - interrupt handler
 */
static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
{
    struct s3c2410_udc *dev = _dev;  //UDC设备对象
    int usb_status;
    int usbd_status;
    int pwr_reg;
    int ep0csr;
    int i;
    u32 idx, idx2;
    unsigned long flags;

    spin_lock_irqsave(&dev->lock, flags);

    /* Driver connected ? */
    if (!dev->driver) {      //还没有和驱动绑定,清除中断
        /* Clear interrupts */
        udc_write(udc_read(S3C2410_UDC_USB_INT_REG),
                S3C2410_UDC_USB_INT_REG);
        udc_write(udc_read(S3C2410_UDC_EP_INT_REG),
                S3C2410_UDC_EP_INT_REG);
    }

    /* Save index */
    idx = udc_read(S3C2410_UDC_INDEX_REG);  //这是哪个端点产生中断的索引号

    /* Read status registers */
    usb_status = udc_read(S3C2410_UDC_USB_INT_REG);  //UDC设备状态
    usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); //UDC产生中断的端点状态
    pwr_reg = udc_read(S3C2410_UDC_PWR_REG);

    udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
    ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

    dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",
        usb_status, usbd_status, pwr_reg, ep0csr);

    /*
     * Now, handle interrupts. There's two types :
     * - Reset, Resume, Suspend coming -> usb_int_reg
     * - EP -> ep_int_reg  
     *  开始中断的实际处理,这里的中断只有两种类型:
     *  UDC设备中断: 重置、挂起和恢复 
     *  端点中断
     */

    /* RESET */ 
     /* UDC设备RESET操作处理 */
    if (usb_status & S3C2410_UDC_USBINT_RESET) {
        /* two kind of reset :
         * - reset start -> pwr reg = 8
         * - reset end   -> pwr reg = 0
         **/
        dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",
            ep0csr, pwr_reg);

        dev->gadget.speed = USB_SPEED_UNKNOWN;
        udc_write(0x00, S3C2410_UDC_INDEX_REG);
        udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,
                S3C2410_UDC_MAXP_REG);
        dev->address = 0;

        dev->ep0state = EP0_IDLE;
        dev->gadget.speed = USB_SPEED_FULL;

        /* clear interrupt */
        udc_write(S3C2410_UDC_USBINT_RESET,
                S3C2410_UDC_USB_INT_REG);

        udc_write(idx, S3C2410_UDC_INDEX_REG);
        spin_unlock_irqrestore(&dev->lock, flags);
        return IRQ_HANDLED;
    }

    /* RESUME */ 
      /* UDC设备RESUME操作处理 */
    if (usb_status & S3C2410_UDC_USBINT_RESUME) {
        dprintk(DEBUG_NORMAL, "USB resume\n");

        /* clear interrupt */
        udc_write(S3C2410_UDC_USBINT_RESUME,
                S3C2410_UDC_USB_INT_REG);

        if (dev->gadget.speed != USB_SPEED_UNKNOWN
                && dev->driver
                && dev->driver->resume)
            dev->driver->resume(&dev->gadget);
    }

    /* SUSPEND */
    if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
        dprintk(DEBUG_NORMAL, "USB suspend\n");

        /* clear interrupt */
        udc_write(S3C2410_UDC_USBINT_SUSPEND,
                S3C2410_UDC_USB_INT_REG);

        if (dev->gadget.speed != USB_SPEED_UNKNOWN
                && dev->driver
                && dev->driver->suspend)
            dev->driver->suspend(&dev->gadget);

        dev->ep0state = EP0_IDLE;
    }

    /* EP */
    /* control traffic */ 
    /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
     * generate an interrupt
     */ 
     /* 下面就是端点中断得处理 */
    /* 首先是控制端点(端点0)的中断处理*/
    if (usbd_status & S3C2410_UDC_INT_EP0) {
        dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
        /* Clear the interrupt bit by setting it to 1 */
        udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
        s3c2410_udc_handle_ep0(dev);
    }

    /* endpoint data transfers */ 
      /* 其他端点,就是数据传输的处理*/
    for (i = 1; i < S3C2410_ENDPOINTS; i++) {  //遍历所有端点,找出中断的端点
        u32 tmp = 1 << i;
        if (usbd_status & tmp) {
            dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);

            /* Clear the interrupt bit by setting it to 1 */
            udc_write(tmp, S3C2410_UDC_EP_INT_REG);
            s3c2410_udc_handle_ep(&dev->ep[i]);
        }
    }

    /* what else causes this interrupt? a receive! who is it? */
    if (!usb_status && !usbd_status && !pwr_reg && !ep0csr) {
        for (i = 1; i < S3C2410_ENDPOINTS; i++) {
            idx2 = udc_read(S3C2410_UDC_INDEX_REG);
            udc_write(i, S3C2410_UDC_INDEX_REG);

            if (udc_read(S3C2410_UDC_OUT_CSR1_REG) & 0x1)
                s3c2410_udc_handle_ep(&dev->ep[i]);

            /* restore index */
            udc_write(idx2, S3C2410_UDC_INDEX_REG);
        }
    }

    dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);

    /* Restore old index */
    udc_write(idx, S3C2410_UDC_INDEX_REG);

    spin_unlock_irqrestore(&dev->lock, flags);

    return IRQ_HANDLED;
}

端点操作函数
端点操作函数是UDC驱动的基础,因为大部分的动作其实都是和端点相关的,如数据传输等。
首先来看中断函数中涉及的两个函数,一个是端点0的处理函数,一个是其他端点的处理函数。


static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
{
    u32         ep0csr;
    struct s3c2410_ep   *ep = &dev->ep[0];
    struct s3c2410_request  *req;
    struct usb_ctrlrequest  crq;

    if (list_empty(&ep->queue))
        req = NULL;
    else
        req = list_entry(ep->queue.next, struct s3c2410_request, queue);

    /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
     * S3C2410_UDC_EP0_CSR_REG when index is zero */

    udc_write(0, S3C2410_UDC_INDEX_REG);
    ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

    dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n",
        ep0csr, ep0states[dev->ep0state]);

    /* clear stall status */
    if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
        s3c2410_udc_nuke(dev, ep, -EPIPE);
        dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
        s3c2410_udc_clear_ep0_sst(base_addr);
        dev->ep0state = EP0_IDLE;
        return;
    }

    /* clear setup end */
    if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
        dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
        s3c2410_udc_nuke(dev, ep, 0);
        s3c2410_udc_clear_ep0_se(base_addr);
        dev->ep0state = EP0_IDLE;
    }

    switch (dev->ep0state) {
    case EP0_IDLE:
        s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
        break;

    case EP0_IN_DATA_PHASE:         /* GET_DESCRIPTOR etc */
        dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
        if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req)
            s3c2410_udc_write_fifo(ep, req);
        break;

    case EP0_OUT_DATA_PHASE:        /* SET_DESCRIPTOR etc */
        dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
        if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req)
            s3c2410_udc_read_fifo(ep, req);
        break;

    case EP0_END_XFER:
        dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
        dev->ep0state = EP0_IDLE;
        break;

    case EP0_STALL:
        dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
        dev->ep0state = EP0_IDLE;
        break;
    }
}

其他端点的处理函数,主要是数据发送和接收


/*
 *  handle_ep - Manage I/O endpoints
 */

static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
{
    struct s3c2410_request  *req;
    int         is_in = ep->bEndpointAddress & USB_DIR_IN;
    u32         ep_csr1;
    u32         idx;

    if (likely(!list_empty(&ep->queue)))
        req = list_entry(ep->queue.next,
                struct s3c2410_request, queue);
    else
        req = NULL;

    idx = ep->bEndpointAddress & 0x7F;

    if (is_in) {
        udc_write(idx, S3C2410_UDC_INDEX_REG);
        ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
        dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",
            idx, ep_csr1, req ? 1 : 0);

        if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
            dprintk(DEBUG_VERBOSE, "st\n");
            udc_write(idx, S3C2410_UDC_INDEX_REG);
            udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL,
                    S3C2410_UDC_IN_CSR1_REG);
            return;
        }

        if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
            s3c2410_udc_write_fifo(ep, req);
    } else {
        udc_write(idx, S3C2410_UDC_INDEX_REG);
        ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
        dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);

        if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
            udc_write(idx, S3C2410_UDC_INDEX_REG);
            udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL,
                    S3C2410_UDC_OUT_CSR1_REG);
            return;
        }

        if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
            s3c2410_udc_read_fifo(ep, req);
    }
}

端点操作函数集:端点的基本操作函数


static const struct usb_ep_ops s3c2410_ep_ops = {
    .enable     = s3c2410_udc_ep_enable,  //端点使能
    .disable    = s3c2410_udc_ep_disable,   //关闭端点

    .alloc_request  = s3c2410_udc_alloc_request,   //分配一个请求
    .free_request   = s3c2410_udc_free_request,    //释放请求

    .queue      = s3c2410_udc_queue,   //向端点提交一个请求
    .dequeue    = s3c2410_udc_dequeue,  //从端点请求队列中删除一个请求

    .set_halt   = s3c2410_udc_set_halt,
};

主要分析queue这个函数,因为上层主要和这个函数打交道,接收或发送数据都需要对应的端点队列提交请求


/*
 *  s3c2410_udc_queue
 */
static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
        gfp_t gfp_flags)
{
    struct s3c2410_request  *req = to_s3c2410_req(_req);
    struct s3c2410_ep   *ep = to_s3c2410_ep(_ep);
    struct s3c2410_udc  *dev;
    u32         ep_csr = 0;
    int         fifo_count = 0;
    unsigned long       flags;

    if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
        dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
        return -EINVAL;
    }

    dev = ep->dev;
    if (unlikely(!dev->driver
            || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
        return -ESHUTDOWN;
    }

    local_irq_save(flags);

    if (unlikely(!_req || !_req->complete
            || !_req->buf || !list_empty(&req->queue))) {
        if (!_req)
            dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
        else {
            dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
                __func__, !_req->complete, !_req->buf,
                !list_empty(&req->queue));
        }

        local_irq_restore(flags);
        return -EINVAL;
    }

    _req->status = -EINPROGRESS;
    _req->actual = 0;

    dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n",
         __func__, ep->bEndpointAddress, _req->length);

    if (ep->bEndpointAddress) {
        udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);

        ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
                ? S3C2410_UDC_IN_CSR1_REG
                : S3C2410_UDC_OUT_CSR1_REG);
        fifo_count = s3c2410_udc_fifo_count_out();
    } else {
        udc_write(0, S3C2410_UDC_INDEX_REG);
        ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
        fifo_count = s3c2410_udc_fifo_count_out();
    }

    /* kickstart this i/o queue? */
    if (list_empty(&ep->queue) && !ep->halted) {
        if (ep->bEndpointAddress == 0 /* ep0 */) {
            switch (dev->ep0state) {
            case EP0_IN_DATA_PHASE:
                if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY)
                        && s3c2410_udc_write_fifo(ep,
                            req)) {
                    dev->ep0state = EP0_IDLE;
                    req = NULL;
                }
                break;

            case EP0_OUT_DATA_PHASE:
                if ((!_req->length)
                    || ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
                        && s3c2410_udc_read_fifo(ep,
                            req))) {
                    dev->ep0state = EP0_IDLE;
                    req = NULL;
                }
                break;

            default:
                local_irq_restore(flags);
                return -EL2HLT;
            }
        } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
                && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))
                && s3c2410_udc_write_fifo(ep, req)) {
            req = NULL;
        } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
                && fifo_count
                && s3c2410_udc_read_fifo(ep, req)) {
            req = NULL;
        }
    }

    /* pio or dma irq handler advances the queue. */
    if (likely(req))
        list_add_tail(&req->queue, &ep->queue);

    local_irq_restore(flags);

    dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
    return 0;
}

UDC驱动中还有一个重要函数,在数据传输或接收完成,即一个请求完成之后,会调用这个请求的完成函数来通知上层驱动。


/*------------------------- I/O ----------------------------------*/

/*
 *  s3c2410_udc_done
 */
static void s3c2410_udc_done(struct s3c2410_ep *ep,
        struct s3c2410_request *req, int status)
{
    unsigned halted = ep->halted;

    list_del_init(&req->queue);

    if (likely(req->req.status == -EINPROGRESS))
        req->req.status = status;
    else
        status = req->req.status;

    ep->halted = 1;
    usb_gadget_giveback_request(&ep->ep, &req->req);
    ep->halted = halted;
}

UDC设备驱动层的源码就分析得差不多了,其他很多函数都是操作寄存器,与UDC设备密切相关,但总的来说完成的功能都是一致的。可以发现,在UDC设备驱动层主要需要做以下几个工作:
1. 对usb_gadgetusb_epusb_request三个标准数据结构进行封装,根据自己UDC的一些设备特性,设计对应的自己的数据结构;
2. 实现platform_driver数据结构中的函数,将UDC设备驱动向platform系统进行注册;
3. 实现usb_gadget_ops函数集,这些函数主要是操作UDC设备的一些特性(针对设备);
4. 实现usb_ep_ops函数集,这些函数主要是操作端点的功能,如请求分配和提交等(针对端点);
5. 实现UDC设备的中断处理函数,这个函数基本上就是UDC设备驱动的核心;

Gadget设备层

这一层是可选的,介于UDC驱动层和Gadget功能层之间。主要源码在composite.c和composite.h文件中,设备层其实和硬件无关,主要实现一些通用性的代码,减少gadget功能层的代码重复工作。Gadget设备层其中承上启下的作用,联系Gadget功能层和UDC驱动层。

将composite源码独立出来,还为复合设备的实现提供了一个通用性的框架。复合设备是指在一个配置描述符中支持多个功能,或者支持多个配置的设备中,每个配置都有一个不同的功能。如一个设备同时支持网络和存储,一个设备同时支持键盘和鼠标功能等。
Gadget设备层的主要数据结构:
usb_function 结构体


/**
 * struct usb_function - describes one function of a configuration
 * @name: For diagnostics, identifies the function.
 * @strings: tables of strings, keyed by identifiers assigned during bind()
 *  and by language IDs provided in control requests
 * @fs_descriptors: Table of full (or low) speed descriptors, using interface and
 *  string identifiers assigned during @bind().  If this pointer is null,
 *  the function will not be available at full speed (or at low speed).
 * @hs_descriptors: Table of high speed descriptors, using interface and
 *  string identifiers assigned during @bind().  If this pointer is null,
 *  the function will not be available at high speed.
 * @ss_descriptors: Table of super speed descriptors, using interface and
 *  string identifiers assigned during @bind(). If this
 *  pointer is null after initiation, the function will not
 *  be available at super speed.
 * @ssp_descriptors: Table of super speed plus descriptors, using
 *  interface and string identifiers assigned during @bind(). If
 *  this pointer is null after initiation, the function will not
 *  be available at super speed plus.
 * @config: assigned when @usb_add_function() is called; this is the
 *  configuration with which this function is associated.
 * @os_desc_table: Table of (interface id, os descriptors) pairs. The function
 *  can expose more than one interface. If an interface is a member of
 *  an IAD, only the first interface of IAD has its entry in the table.
 * @os_desc_n: Number of entries in os_desc_table
 * @bind: Before the gadget can register, all of its functions bind() to the
 *  available resources including string and interface identifiers used
 *  in interface or class descriptors; endpoints; I/O buffers; and so on.
 * @unbind: Reverses @bind; called as a side effect of unregistering the
 *  driver which added this function.
 * @free_func: free the struct usb_function.
 * @mod: (internal) points to the module that created this structure.
 * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
 *  initialize usb_ep.driver data at this time (when it is used).
 *  Note that setting an interface to its current altsetting resets
 *  interface state, and that all interfaces have a disabled state.
 * @get_alt: Returns the active altsetting.  If this is not provided,
 *  then only altsetting zero is supported.
 * @disable: (REQUIRED) Indicates the function should be disabled.  Reasons
 *  include host resetting or reconfiguring the gadget, and disconnection.
 * @setup: Used for interface-specific control requests.
 * @req_match: Tests if a given class request can be handled by this function.
 * @suspend: Notifies functions when the host stops sending USB traffic.
 * @resume: Notifies functions when the host restarts USB traffic.
 * @get_status: Returns function status as a reply to
 *  GetStatus() request when the recipient is Interface.
 * @func_suspend: callback to be called when
 *  SetFeature(FUNCTION_SUSPEND) is reseived
 *
 * A single USB function uses one or more interfaces, and should in most
 * cases support operation at both full and high speeds.  Each function is
 * associated by @usb_add_function() with a one configuration; that function
 * causes @bind() to be called so resources can be allocated as part of
 * setting up a gadget driver.  Those resources include endpoints, which
 * should be allocated using @usb_ep_autoconfig().
 *
 * To support dual speed operation, a function driver provides descriptors
 * for both high and full speed operation.  Except in rare cases that don't
 * involve bulk endpoints, each speed needs different endpoint descriptors.
 *
 * Function drivers choose their own strategies for managing instance data.
 * The simplest strategy just declares it "static', which means the function
 * can only be activated once.  If the function needs to be exposed in more
 * than one configuration at a given speed, it needs to support multiple
 * usb_function structures (one for each configuration).
 *
 * A more complex strategy might encapsulate a @usb_function structure inside
 * a driver-specific instance structure to allows multiple activations.  An
 * example of multiple activations might be a CDC ACM function that supports
 * two or more distinct instances within the same configuration, providing
 * several independent logical data links to a USB host.
 */

struct usb_function {
    const char          *name;  //功能名称
    struct usb_gadget_strings   **strings;  //string数组,通过bind中分配的id访问
    struct usb_descriptor_header    **fs_descriptors;//全速的描述符表,用于bind中分配的接口描述符和string描述符
    struct usb_descriptor_header    **hs_descriptors;//高速描述符表
    struct usb_descriptor_header    **ss_descriptors;
    struct usb_descriptor_header    **ssp_descriptors;

    struct usb_configuration    *config; //调用usb_add_function()函数赋值,是这个功能关联的配置描述符

    struct usb_os_desc_table    *os_desc_table;
    unsigned            os_desc_n;

    /* REVISIT:  bind() functions can be marked __init, which
     * makes trouble for section mismatch analysis.  See if
     * we can't restructure things to avoid mismatching.
     * Related:  unbind() may kfree() but bind() won't...
     */

    /* configuration management:  bind/unbind */
    int         (*bind)(struct usb_configuration *,
                    struct usb_function *);
    void            (*unbind)(struct usb_configuration *,
                    struct usb_function *);
    void            (*free_func)(struct usb_function *f);
    struct module       *mod;

    /* runtime state management */
    int         (*set_alt)(struct usb_function *,
                    unsigned interface, unsigned alt); /*重新配置altsetting,*/
    int         (*get_alt)(struct usb_function *,
                    unsigned interface);    /*获取当前altsetting*/
    void            (*disable)(struct usb_function *);   /*关闭功能*/
    int         (*setup)(struct usb_function *,
                    const struct usb_ctrlrequest *);   /*接口相关的控制处理*/
    bool            (*req_match)(struct usb_function *,
                    const struct usb_ctrlrequest *);
    void            (*suspend)(struct usb_function *);    /*电源管理相关的挂起功能*/
    void            (*resume)(struct usb_function *);  /*电源管理相关的恢复功能*/

    /* USB 3.0 additions */
    int         (*get_status)(struct usb_function *);
    int         (*func_suspend)(struct usb_function *,
                        u8 suspend_opt);
    /* private: */
    /* internals */
    struct list_head        list;
    DECLARE_BITMAP(endpoints, 32);
    const struct usb_function_instance *fi;

    unsigned int        bind_deactivated:1;
};

usb_configuration 结构体


/**
 * struct usb_configuration - represents one gadget configuration
 * @label: For diagnostics, describes the configuration.
 * @strings: Tables of strings, keyed by identifiers assigned during @bind()
 *  and by language IDs provided in control requests.
 * @descriptors: Table of descriptors preceding all function descriptors.
 *  Examples include OTG and vendor-specific descriptors.
 * @unbind: Reverses @bind; called as a side effect of unregistering the
 *  driver which added this configuration.
 * @setup: Used to delegate control requests that aren't handled by standard
 *  device infrastructure or directed at a specific interface.
 * @bConfigurationValue: Copied into configuration descriptor.
 * @iConfiguration: Copied into configuration descriptor.
 * @bmAttributes: Copied into configuration descriptor.
 * @MaxPower: Power consumtion in mA. Used to compute bMaxPower in the
 *  configuration descriptor after considering the bus speed.
 * @cdev: assigned by @usb_add_config() before calling @bind(); this is
 *  the device associated with this configuration.
 *
 * Configurations are building blocks for gadget drivers structured around
 * function drivers.  Simple USB gadgets require only one function and one
 * configuration, and handle dual-speed hardware by always providing the same
 * functionality.  Slightly more complex gadgets may have more than one
 * single-function configuration at a given speed; or have configurations
 * that only work at one speed.
 *
 * Composite devices are, by definition, ones with configurations which
 * include more than one function.
 *
 * The lifecycle of a usb_configuration includes allocation, initialization
 * of the fields described above, and calling @usb_add_config() to set up
 * internal data and bind it to a specific device.  The configuration's
 * @bind() method is then used to initialize all the functions and then
 * call @usb_add_function() for them.
 *
 * Those functions would normally be independent of each other, but that's
 * not mandatory.  CDC WMC devices are an example where functions often
 * depend on other functions, with some functions subsidiary to others.
 * Such interdependency may be managed in any way, so long as all of the
 * descriptors complete by the time the composite driver returns from
 * its bind() routine.
 * usb_configuration 表示一个Gadget配置
 */
struct usb_configuration {
    const char          *label; //配置名称
    struct usb_gadget_strings   **strings;  //字符串表
    const struct usb_descriptor_header **descriptors; //功能描述符表

    /* REVISIT:  bind() functions can be marked __init, which
     * makes trouble for section mismatch analysis.  See if
     * we can't restructure things to avoid mismatching...
     */

    /* configuration management: unbind/setup */ 
    void            (*unbind)(struct usb_configuration *);
    int         (*setup)(struct usb_configuration *,
                    const struct usb_ctrlrequest *);

    /* fields in the config descriptor */
       /*用来赋值配置描述符*/
    u8          bConfigurationValue;
    u8          iConfiguration;
    u8          bmAttributes;
    u16         MaxPower;

    struct usb_composite_dev    *cdev;  /*和composite设备关联,在usb_add_config函数中设置*/

    /* private: */
    /* internals */
    struct list_head    list;
    struct list_head    functions;  //功能链表
    u8          next_interface_id;
    unsigned        superspeed:1;
    unsigned        highspeed:1;
    unsigned        fullspeed:1;
    unsigned        superspeed_plus:1;
    struct usb_function *interface[MAX_CONFIG_INTERFACES];
};

usb_composite_driver 结构体


/**
 * struct usb_composite_driver - groups configurations into a gadget
 * @name: For diagnostics, identifies the driver.
 * @dev: Template descriptor for the device, including default device
 *  identifiers.
 * @strings: tables of strings, keyed by identifiers assigned during @bind
 *  and language IDs provided in control requests. Note: The first entries
 *  are predefined. The first entry that may be used is
 *  USB_GADGET_FIRST_AVAIL_IDX
 * @max_speed: Highest speed the driver supports.
 * @needs_serial: set to 1 if the gadget needs userspace to provide
 *  a serial number.  If one is not provided, warning will be printed.
 * @bind: (REQUIRED) Used to allocate resources that are shared across the
 *  whole device, such as string IDs, and add its configurations using
 *  @usb_add_config(). This may fail by returning a negative errno
 *  value; it should return zero on successful initialization.
 * @unbind: Reverses @bind; called as a side effect of unregistering
 *  this driver.
 * @disconnect: optional driver disconnect method
 * @suspend: Notifies when the host stops sending USB traffic,
 *  after function notifications
 * @resume: Notifies configuration when the host restarts USB traffic,
 *  before function notifications
 * @gadget_driver: Gadget driver controlling this driver
 *
 * Devices default to reporting self powered operation.  Devices which rely
 * on bus powered operation should report this in their @bind method.
 *
 * Before returning from @bind, various fields in the template descriptor
 * may be overridden.  These include the idVendor/idProduct/bcdDevice values
 * normally to bind the appropriate host side driver, and the three strings
 * (iManufacturer, iProduct, iSerialNumber) normally used to provide user
 * meaningful device identifiers.  (The strings will not be defined unless
 * they are defined in @dev and @strings.)  The correct ep0 maxpacket size
 * is also reported, as defined by the underlying controller driver.
 */
struct usb_composite_driver {
    const char              *name;  //驱动名称
    const struct usb_device_descriptor  *dev; //设备描述符
    struct usb_gadget_strings       **strings;  //字符串表
    enum usb_device_speed           max_speed;
    unsigned        needs_serial:1;

    int         (*bind)(struct usb_composite_dev *cdev);
    int         (*unbind)(struct usb_composite_dev *);

    void            (*disconnect)(struct usb_composite_dev *);

    /* global suspend hooks */
    void            (*suspend)(struct usb_composite_dev *);
    void            (*resume)(struct usb_composite_dev *);
    struct usb_gadget_driver        gadget_driver;
};

usb_composite_dev 结构体


/**
 * struct usb_composite_device - represents one composite usb gadget
 * @gadget: read-only, abstracts the gadget's usb peripheral controller
 * @req: used for control responses; buffer is pre-allocated
 * @os_desc_req: used for OS descriptors responses; buffer is pre-allocated
 * @config: the currently active configuration
 * @qw_sign: qwSignature part of the OS string
 * @b_vendor_code: bMS_VendorCode part of the OS string
 * @use_os_string: false by default, interested gadgets set it
 * @os_desc_config: the configuration to be used with OS descriptors
 * @setup_pending: true when setup request is queued but not completed
 * @os_desc_pending: true when os_desc request is queued but not completed
 *
 * One of these devices is allocated and initialized before the
 * associated device driver's bind() is called.
 *
 * OPEN ISSUE:  it appears that some WUSB devices will need to be
 * built by combining a normal (wired) gadget with a wireless one.
 * This revision of the gadget framework should probably try to make
 * sure doing that won't hurt too much.
 *
 * One notion for how to handle Wireless USB devices involves:
 * (a) a second gadget here, discovery mechanism TBD, but likely
 *     needing separate "register/unregister WUSB gadget" calls;
 * (b) updates to usb_gadget to include flags "is it wireless",
 *     "is it wired", plus (presumably in a wrapper structure)
 *     bandgroup and PHY info;
 * (c) presumably a wireless_ep wrapping a usb_ep, and reporting
 *     wireless-specific parameters like maxburst and maxsequence;
 * (d) configurations that are specific to wireless links;
 * (e) function drivers that understand wireless configs and will
 *     support wireless for (additional) function instances;
 * (f) a function to support association setup (like CBAF), not
 *     necessarily requiring a wireless adapter;
 * (g) composite device setup that can create one or more wireless
 *     configs, including appropriate association setup support;
 * (h) more, TBD.
 */
struct usb_composite_dev {
    struct usb_gadget       *gadget;  //关联的gadget
    struct usb_request      *req;  //用于控制响应,提前分配的
    struct usb_request      *os_desc_req;

    struct usb_configuration    *config;  //当前配置

    /* OS String is a custom (yet popular) extension to the USB standard. */
    u8              qw_sign[OS_STRING_QW_SIGN_LEN];
    u8              b_vendor_code;
    struct usb_configuration    *os_desc_config;
    unsigned int            use_os_string:1;

    /* private: */
    /* internals */
    unsigned int            suspended:1;
    struct usb_device_descriptor    desc;
    struct list_head        configs;  //配置链表
    struct list_head        gstrings;
    struct usb_composite_driver *driver;
    u8              next_string_id;
    char                *def_manufacturer;

    /* the gadget driver won't enable the data pullup
     * while the deactivation count is nonzero.
     */
    unsigned            deactivations;

    /* the composite driver won't complete the control transfer's
     * data/status stages till delayed_status is zero.
     */
    int             delayed_status;

    /* protects deactivations and delayed_status counts*/
    spinlock_t          lock;

    unsigned            setup_pending:1;
    unsigned            os_desc_pending:1;
};

其中重要的函数:
composite_bind 函数



static int composite_bind(struct usb_gadget *gadget,
        struct usb_gadget_driver *gdriver)
{
    struct usb_composite_dev    *cdev;
    struct usb_composite_driver *composite = to_cdriver(gdriver);
    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);
    INIT_LIST_HEAD(&cdev->gstrings);

    status = composite_dev_prepare(composite, cdev);
    if (status)
        goto fail;

    /* composite gadget needs to assign strings for whole device (like
     * serial number), register function drivers, potentially update
     * power state and consumption, etc
     */
    status = composite->bind(cdev);
    if (status < 0)
        goto fail;

    if (cdev->use_os_string) {
        status = composite_os_desc_req_prepare(cdev, gadget->ep0);
        if (status)
            goto fail;
    }

    update_unchanged_dev_desc(&cdev->desc, composite->dev);

    /* has userspace failed to provide a serial number? */
    if (composite->needs_serial && !cdev->desc.iSerialNumber)
        WARNING(cdev, "userspace failed to provide iSerialNumber\n");

    INFO(cdev, "%s ready\n", composite->name);
    return 0;

fail:
    __composite_unbind(gadget, false);
    return status;
}

composite_bind函数主要完成UDC驱动层gadget设备和设备层composite设备关系的建立,分配控制端点0的请求数据结构,设置设备描述符,并调用功能层的bind函数分配功能层所需要的资源等工作。

composite_unbind 函数


static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
{
    struct usb_composite_dev    *cdev = get_gadget_data(gadget);

    /* composite_disconnect() must already have been called
     * by the underlying peripheral controller driver!
     * so there's no i/o concurrency that could affect the
     * state protected by cdev->lock.
     */
    WARN_ON(cdev->config);

    while (!list_empty(&cdev->configs)) {
        struct usb_configuration    *c;
        c = list_first_entry(&cdev->configs,
                struct usb_configuration, list);
        remove_config(cdev, c);
    }
    if (cdev->driver->unbind && unbind_driver)
        cdev->driver->unbind(cdev);

    composite_dev_cleanup(cdev);

    kfree(cdev->def_manufacturer);
    kfree(cdev);
    set_gadget_data(gadget, NULL);
}


static void composite_unbind(struct usb_gadget *gadget)
{
    __composite_unbind(gadget, true);
}

Unbind函数完成bind函数中分配的资源,并遍历所有配置和各个配置下的功能,调用其对应得unbind函数来释放资源。

在配置中增加一个功能,每个配置初始化后都必须至少有一个功能。
usb_add_function 函数


/**
 * usb_add_function() - add a function to a configuration
 * @config: the configuration
 * @function: the function being added
 * Context: single threaded during gadget setup
 *
 * After initialization, each configuration must have one or more
 * functions added to it.  Adding a function involves calling its @bind()
 * method to allocate resources such as interface and string identifiers
 * and endpoints.
 *
 * This function returns the value of the function's bind(), which is
 * zero for success else a negative errno value.
 */
int usb_add_function(struct usb_configuration *config,
        struct usb_function *function)
{
    int value = -EINVAL;

    DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
            function->name, function,
            config->label, config);

    if (!function->set_alt || !function->disable)
        goto done;

    function->config = config;
    list_add_tail(&function->list, &config->functions);

    if (function->bind_deactivated) {
        value = usb_function_deactivate(function);
        if (value)
            goto done;
    }

    /* REVISIT *require* function->bind? */
    if (function->bind) {
        value = function->bind(config, function);
        if (value < 0) {
            list_del(&function->list);
            function->config = NULL;
        }
    } else
        value = 0;

    /* We allow configurations that don't work at both speeds.
     * If we run into a lowspeed Linux system, treat it the same
     * as full speed ... it's the function drivers that will need
     * to avoid bulk and ISO transfers.
     */
    if (!config->fullspeed && function->fs_descriptors)
        config->fullspeed = true;
    if (!config->highspeed && function->hs_descriptors)
        config->highspeed = true;
    if (!config->superspeed && function->ss_descriptors)
        config->superspeed = true;
    if (!config->superspeed_plus && function->ssp_descriptors)
        config->superspeed_plus = true;

done:
    if (value)
        DBG(config->cdev, "adding '%s'/%p --> %d\n",
                function->name, function, value);
    return value;
}

为设备增加一个配置,这个实现原理和在配置下增加一个功能类似
usb_add_config 函数:


/**
 * usb_add_config() - add a configuration to a device.
 * @cdev: wraps the USB gadget
 * @config: the configuration, with bConfigurationValue assigned
 * @bind: the configuration's bind function
 * Context: single threaded during gadget setup
 *
 * One of the main tasks of a composite @bind() routine is to
 * add each of the configurations it supports, using this routine.
 *
 * This function returns the value of the configuration's @bind(), which
 * is zero for success else a negative errno value.  Binding configurations
 * assigns global resources including string IDs, and per-configuration
 * resources such as interface IDs and endpoints.
 */
int usb_add_config(struct usb_composite_dev *cdev,
        struct usb_configuration *config,
        int (*bind)(struct usb_configuration *))
{
    int             status = -EINVAL;

    if (!bind)
        goto done;

    DBG(cdev, "adding config #%u '%s'/%p\n",
            config->bConfigurationValue,
            config->label, config);

    status = usb_add_config_only(cdev, config);
    if (status)
        goto done;

    status = bind(config);
    if (status < 0) {
        while (!list_empty(&config->functions)) {
            struct usb_function     *f;

            f = list_first_entry(&config->functions,
                    struct usb_function, list);
            list_del(&f->list);
            if (f->unbind) {
                DBG(cdev, "unbind function '%s'/%p\n",
                    f->name, f);
                f->unbind(config, f);
                /* may free memory for "f" */
            }
        }
        list_del(&config->list);
        config->cdev = NULL;
    } else {
        unsigned    i;

        DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n",
            config->bConfigurationValue, config,
            config->superspeed_plus ? " superplus" : "",
            config->superspeed ? " super" : "",
            config->highspeed ? " high" : "",
            config->fullspeed
                ? (gadget_is_dualspeed(cdev->gadget)
                    ? " full"
                    : " full/low")
                : "");

        for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
            struct usb_function *f = config->interface[i];

            if (!f)
                continue;
            DBG(cdev, "  interface %d = %s/%p\n",
                i, f->name, f);
        }
    }

    /* set_alt(), or next bind(), sets up ep->claimed as needed */
    usb_ep_autoconfig_reset(cdev->gadget);

done:
    if (status)
        DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
                config->bConfigurationValue, status);
    return status;
}

设置一个配置
set_config 函数


static int set_config(struct usb_composite_dev *cdev,
        const struct usb_ctrlrequest *ctrl, unsigned number)
{
    struct usb_gadget   *gadget = cdev->gadget;
    struct usb_configuration *c = NULL;
    int         result = -EINVAL;
    unsigned        power = gadget_is_otg(gadget) ? 8 : 100;
    int         tmp;

    if (number) {
        list_for_each_entry(c, &cdev->configs, list) {
            if (c->bConfigurationValue == number) {
                /*
                 * We disable the FDs of the previous
                 * configuration only if the new configuration
                 * is a valid one
                 */
                if (cdev->config)
                    reset_config(cdev);
                result = 0;
                break;
            }
        }
        if (result < 0)
            goto done;
    } else { /* Zero configuration value - need to reset the config */
        if (cdev->config)
            reset_config(cdev);
        result = 0;
    }

    INFO(cdev, "%s config #%d: %s\n",
         usb_speed_string(gadget->speed),
         number, c ? c->label : "unconfigured");

    if (!c)
        goto done;

    usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
    cdev->config = c;

    /* Initialize all interfaces by setting them to altsetting zero. */
    for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
        struct usb_function *f = c->interface[tmp];
        struct usb_descriptor_header **descriptors;

        if (!f)
            break;

        /*
         * Record which endpoints are used by the function. This is used
         * to dispatch control requests targeted at that endpoint to the
         * function's setup callback instead of the current
         * configuration's setup callback.
         */
        descriptors = function_descriptors(f, gadget->speed);

        for (; *descriptors; ++descriptors) {
            struct usb_endpoint_descriptor *ep;
            int addr;

            if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
                continue;

            ep = (struct usb_endpoint_descriptor *)*descriptors;
            addr = ((ep->bEndpointAddress & 0x80) >> 3)
                 |  (ep->bEndpointAddress & 0x0f);
            set_bit(addr, f->endpoints);
        }

        result = f->set_alt(f, tmp, 0);
        if (result < 0) {
            DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
                    tmp, f->name, f, result);

            reset_config(cdev);
            goto done;
        }

        if (result == USB_GADGET_DELAYED_STATUS) {
            DBG(cdev,
             "%s: interface %d (%s) requested delayed status\n",
                    __func__, tmp, f->name);
            cdev->delayed_status++;
            DBG(cdev, "delayed_status count %d\n",
                    cdev->delayed_status);
        }
    }

    /* when we return, be sure our power usage is valid */
    power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
done:
    usb_gadget_vbus_draw(gadget, power);
    if (result >= 0 && cdev->delayed_status)
        result = USB_GADGET_DELAYED_STATUS;
    return result;
}

setup函数完成了ep0所需要处理的而底层不能处理的功能,由于这一个层实现了这个函数,所以功能层就不需要关注于USB协议的这些事务性处理,而重点放在需要实现的功能上
composite_setup 函数:


/*
 * The setup() callback implements all the ep0 functionality that's
 * not handled lower down, in hardware or the hardware driver(like
 * device and endpoint feature flags, and their status).  It's all
 * housekeeping for the gadget function we're implementing.  Most of
 * the work is in config and function specific setup.
 */
int
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
    struct usb_composite_dev    *cdev = get_gadget_data(gadget);
    struct usb_request      *req = cdev->req;
    int             value = -EOPNOTSUPP;
    int             status = 0;
    u16             w_index = le16_to_cpu(ctrl->wIndex);
    u8              intf = w_index & 0xFF;
    u16             w_value = le16_to_cpu(ctrl->wValue);
    u16             w_length = le16_to_cpu(ctrl->wLength);
    struct usb_function     *f = NULL;
    u8              endp;

    /* partial re-init of the response message; the function or the
     * gadget might need to intercept e.g. a control-OUT completion
     * when we delegate to it.
     */
    req->zero = 0;
    req->context = cdev;
    req->complete = composite_setup_complete;
    req->length = 0;
    gadget->ep0->driver_data = cdev;

    /*
     * Don't let non-standard requests match any of the cases below
     * by accident.
     */
    if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
        goto unknown;

    switch (ctrl->bRequest) {

    /* we handle all standard USB descriptors */
    case USB_REQ_GET_DESCRIPTOR:
        if (ctrl->bRequestType != USB_DIR_IN)
            goto unknown;
        switch (w_value >> 8) {

        case USB_DT_DEVICE:
            cdev->desc.bNumConfigurations =
                count_configs(cdev, USB_DT_DEVICE);
            cdev->desc.bMaxPacketSize0 =
                cdev->gadget->ep0->maxpacket;
            if (gadget_is_superspeed(gadget)) {
                if (gadget->speed >= USB_SPEED_SUPER) {
                    cdev->desc.bcdUSB = cpu_to_le16(0x0310);
                    cdev->desc.bMaxPacketSize0 = 9;
                } else {
                    cdev->desc.bcdUSB = cpu_to_le16(0x0210);
                }
            } else {
                cdev->desc.bcdUSB = cpu_to_le16(0x0200);
            }

            value = min(w_length, (u16) sizeof cdev->desc);
            memcpy(req->buf, &cdev->desc, value);
            break;
        case USB_DT_DEVICE_QUALIFIER:
            if (!gadget_is_dualspeed(gadget) ||
                gadget->speed >= USB_SPEED_SUPER)
                break;
            device_qual(cdev);
            value = min_t(int, w_length,
                sizeof(struct usb_qualifier_descriptor));
            break;
        case USB_DT_OTHER_SPEED_CONFIG:
            if (!gadget_is_dualspeed(gadget) ||
                gadget->speed >= USB_SPEED_SUPER)
                break;
            /* FALLTHROUGH */
        case USB_DT_CONFIG:
            value = config_desc(cdev, w_value);
            if (value >= 0)
                value = min(w_length, (u16) value);
            break;
        case USB_DT_STRING:
            value = get_string(cdev, req->buf,
                    w_index, w_value & 0xff);
            if (value >= 0)
                value = min(w_length, (u16) value);
            break;
        case USB_DT_BOS:
            if (gadget_is_superspeed(gadget)) {
                value = bos_desc(cdev);
                value = min(w_length, (u16) value);
            }
            break;
        case USB_DT_OTG:
            if (gadget_is_otg(gadget)) {
                struct usb_configuration *config;
                int otg_desc_len = 0;

                if (cdev->config)
                    config = cdev->config;
                else
                    config = list_first_entry(
                            &cdev->configs,
                        struct usb_configuration, list);
                if (!config)
                    goto done;

                if (gadget->otg_caps &&
                    (gadget->otg_caps->otg_rev >= 0x0200))
                    otg_desc_len += sizeof(
                        struct usb_otg20_descriptor);
                else
                    otg_desc_len += sizeof(
                        struct usb_otg_descriptor);

                value = min_t(int, w_length, otg_desc_len);
                memcpy(req->buf, config->descriptors[0], value);
            }
            break;
        }
        break;

    /* any number of configs can work */
    case USB_REQ_SET_CONFIGURATION:
        if (ctrl->bRequestType != 0)
            goto unknown;
        if (gadget_is_otg(gadget)) {
            if (gadget->a_hnp_support)
                DBG(cdev, "HNP available\n");
            else if (gadget->a_alt_hnp_support)
                DBG(cdev, "HNP on another port\n");
            else
                VDBG(cdev, "HNP inactive\n");
        }
        spin_lock(&cdev->lock);
        value = set_config(cdev, ctrl, w_value);
        spin_unlock(&cdev->lock);
        break;
    case USB_REQ_GET_CONFIGURATION:
        if (ctrl->bRequestType != USB_DIR_IN)
            goto unknown;
        if (cdev->config)
            *(u8 *)req->buf = cdev->config->bConfigurationValue;
        else
            *(u8 *)req->buf = 0;
        value = min(w_length, (u16) 1);
        break;

    /* function drivers must handle get/set altsetting; if there's
     * no get() method, we know only altsetting zero works.
     */
    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;
    case USB_REQ_GET_STATUS:
        if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&
                        (w_index == OTG_STS_SELECTOR)) {
            if (ctrl->bRequestType != (USB_DIR_IN |
                            USB_RECIP_DEVICE))
                goto unknown;
            *((u8 *)req->buf) = gadget->host_request_flag;
            value = 1;
            break;
        }

        /*
         * USB 3.0 additions:
         * Function driver should handle get_status request. If such cb
         * wasn't supplied we respond with default value = 0
         * Note: function driver should supply such cb only for the
         * first interface of the function
         */
        if (!gadget_is_superspeed(gadget))
            goto unknown;
        if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
            goto unknown;
        value = 2;  /* This is the length of the get_status reply */
        put_unaligned_le16(0, req->buf);
        if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
            break;
        f = cdev->config->interface[intf];
        if (!f)
            break;
        status = f->get_status ? f->get_status(f) : 0;
        if (status < 0)
            break;
        put_unaligned_le16(status & 0x0000ffff, req->buf);
        break;
    /*
     * Function drivers should handle SetFeature/ClearFeature
     * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
     * only for the first interface of the function
     */
    case USB_REQ_CLEAR_FEATURE:
    case USB_REQ_SET_FEATURE:
        if (!gadget_is_superspeed(gadget))
            goto unknown;
        if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
            goto unknown;
        switch (w_value) {
        case USB_INTRF_FUNC_SUSPEND:
            if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
                break;
            f = cdev->config->interface[intf];
            if (!f)
                break;
            value = 0;
            if (f->func_suspend)
                value = f->func_suspend(f, w_index >> 8);
            if (value < 0) {
                ERROR(cdev,
                      "func_suspend() returned error %d\n",
                      value);
                value = 0;
            }
            break;
        }
        break;
    default:
unknown:
        /*
         * OS descriptors handling
         */
        if (cdev->use_os_string && cdev->os_desc_config &&
            (ctrl->bRequestType & USB_TYPE_VENDOR) &&
            ctrl->bRequest == cdev->b_vendor_code) {
            struct usb_request      *req;
            struct usb_configuration    *os_desc_cfg;
            u8              *buf;
            int             interface;
            int             count = 0;

            req = cdev->os_desc_req;
            req->context = cdev;
            req->complete = composite_setup_complete;
            buf = req->buf;
            os_desc_cfg = cdev->os_desc_config;
            memset(buf, 0, w_length);
            buf[5] = 0x01;
            switch (ctrl->bRequestType & USB_RECIP_MASK) {
            case USB_RECIP_DEVICE:
                if (w_index != 0x4 || (w_value >> 8))
                    break;
                buf[6] = w_index;
                if (w_length == 0x10) {
                    /* Number of ext compat interfaces */
                    count = count_ext_compat(os_desc_cfg);
                    buf[8] = count;
                    count *= 24; /* 24 B/ext compat desc */
                    count += 16; /* header */
                    put_unaligned_le32(count, buf);
                    value = w_length;
                } else {
                    /* "extended compatibility ID"s */
                    count = count_ext_compat(os_desc_cfg);
                    buf[8] = count;
                    count *= 24; /* 24 B/ext compat desc */
                    count += 16; /* header */
                    put_unaligned_le32(count, buf);
                    buf += 16;
                    fill_ext_compat(os_desc_cfg, buf);
                    value = w_length;
                }
                break;
            case USB_RECIP_INTERFACE:
                if (w_index != 0x5 || (w_value >> 8))
                    break;
                interface = w_value & 0xFF;
                buf[6] = w_index;
                if (w_length == 0x0A) {
                    count = count_ext_prop(os_desc_cfg,
                        interface);
                    put_unaligned_le16(count, buf + 8);
                    count = len_ext_prop(os_desc_cfg,
                        interface);
                    put_unaligned_le32(count, buf);

                    value = w_length;
                } else {
                    count = count_ext_prop(os_desc_cfg,
                        interface);
                    put_unaligned_le16(count, buf + 8);
                    count = len_ext_prop(os_desc_cfg,
                        interface);
                    put_unaligned_le32(count, buf);
                    buf += 10;
                    value = fill_ext_prop(os_desc_cfg,
                                  interface, buf);
                    if (value < 0)
                        return value;

                    value = w_length;
                }
                break;
            }

            if (value >= 0) {
                req->length = value;
                req->context = cdev;
                req->zero = value < w_length;
                value = composite_ep0_queue(cdev, req,
                                GFP_ATOMIC);
                if (value < 0) {
                    DBG(cdev, "ep_queue --> %d\n", value);
                    req->status = 0;
                    composite_setup_complete(gadget->ep0,
                                 req);
                }
            }
            return value;
        }

        VDBG(cdev,
            "non-core control req%02x.%02x v%04x i%04x l%d\n",
            ctrl->bRequestType, ctrl->bRequest,
            w_value, w_index, w_length);

        /* functions always handle their interfaces and endpoints...
         * punt other recipients (other, WUSB, ...) to the current
         * configuration code.
         *
         * REVISIT it could make sense to let the composite device
         * take such requests too, if that's ever needed:  to work
         * in config 0, etc.
         */
        if (cdev->config) {
            list_for_each_entry(f, &cdev->config->functions, list)
                if (f->req_match && f->req_match(f, ctrl))
                    goto try_fun_setup;
            f = NULL;
        }

        switch (ctrl->bRequestType & USB_RECIP_MASK) {
        case USB_RECIP_INTERFACE:
            if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
                break;
            f = cdev->config->interface[intf];
            break;

        case USB_RECIP_ENDPOINT:
            endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
            list_for_each_entry(f, &cdev->config->functions, list) {
                if (test_bit(endp, f->endpoints))
                    break;
            }
            if (&f->list == &cdev->config->functions)
                f = NULL;
            break;
        }
try_fun_setup:
        if (f && f->setup)
            value = f->setup(f, ctrl);
        else {
            struct usb_configuration    *c;

            c = cdev->config;
            if (!c)
                goto done;

            /* try current config's setup */
            if (c->setup) {
                value = c->setup(c, ctrl);
                goto done;
            }

            /* try the only function in the current config */
            if (!list_is_singular(&c->functions))
                goto done;
            f = list_first_entry(&c->functions, struct usb_function,
                         list);
            if (f->setup)
                value = f->setup(f, ctrl);
        }

        goto done;
    }

    /* respond with data transfer before status phase? */
    if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
        req->length = value;
        req->context = cdev;
        req->zero = value < w_length;
        value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
        if (value < 0) {
            DBG(cdev, "ep_queue --> %d\n", value);
            req->status = 0;
            composite_setup_complete(gadget->ep0, req);
        }
    } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
        WARN(cdev,
            "%s: Delayed status not supported for w_length != 0",
            __func__);
    }

done:
    /* device either stalls (value < 0) or reports success */
    return value;
}

Gadget 功能层

Gadget功能层完成USB设备的具体功能,其表现的形式各不相同,如键盘、鼠标、存储和网卡等等。功能层不仅涉及到Gadget驱动相关的内容,还涉及到其功能相关的内核子系统。如存储还涉及到内核存储子系统,网卡还涉及到网络驱动子系统。因此,Gadget功能的代码非常复杂。

未完待续。。。。

你可能感兴趣的:(kernel,linux设备驱动,usb设备驱动)