天天看點

裝置驅動中的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.c

s3c2410_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_probe

s3c2410_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_driver

usb_gadget_unregister_driver

兩個

gadget drive

r注冊接口,這兩個函數将實作

gadget driver

udc 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_gadget

usb_ep

usb_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功能的代碼非常複雜。

未完待續。。。。

繼續閱讀