第16章 USB主机、设备与Gadget驱动之USB UDC与Gadget驱动(三)

16.5 USB OTG驱动

    OTG是On-The-Go的缩写,是近年来发展的技术,2001年12月18日由USB Implementers Forum公布,主要应用于各种不同的设备或移动设备之间的联接,进行数据交换。

    特别是Pad、移动电话、消费类设备。改变如数码相机、摄像机、打印机等设备之间多种不同制式连机器,多大7种制式的存储卡数据交换的不便。2014年左右开始在市场普及。

    USB OTG标准在完全兼容USB 2.0标准的基础上,USB OTG允许设备既可作为主机,也可作为外设操作,OTG新增主机通令协议(HNP)和对话请求协议(SRP)

    在OTG中,初始主机设备称为A设备,外设称为B设备。可用电缆的连接方式来决定初始角色。两用设备使用新型Mini-AB插座,从而使Mini-A插头、Mini-B插头和Mini-AB插座增添了第5个引脚,以用于识别不同的电缆端点。Mini-A插头中的ID引脚接地,Mini-B插头中的ID引脚浮空。当OTG设备检测到接地的ID引脚时,表示默认的是A设备(主机),而检测到ID引脚浮空的设备则认为是B设备(外设)。系统一旦连接后,OTG的角色还可以更换,以采用新的HNP协议。而SRP允许B设备请求A设备打开VBUS电源并启动一次对话。一次OTG对话可通过A设备提供VBUS电源的时间来确定。

    自Linux 2.6.9,OTG相关源代码已经被包含在内核中,新增的主要内容包括:

linux/usb/gadget.h

/**
 * struct usb_gadget - represents a usb slave device
 * @work: (internal use) Workqueue to be used for sysfs_notify()
 * @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
 * @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.
 * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
 *      MaxPacketSize.
 * @xfer_isr_count: UI (transfer complete) interrupts count
 * @usb_core_id: Identifies the usb core controlled by this usb_gadget.
 *               Used in case of more then one core operates concurrently.
 * @bam2bam_func_enabled; Indicates function using bam2bam is enabled or not.
 * @extra_buf_alloc: Extra allocation size for AXI prefetch so that out of
 * boundary access is protected.
 * @interrupt_num: Interrupt number for the underlying platform device.
 *
 * 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;
        /* readonly to gadget driver */
        const struct usb_gadget_ops     *ops;
        struct usb_ep                   *ep0;
        struct list_head                ep_list;        /* of usb_ep */
        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;

        unsigned                        sg_supported:1;
        unsigned                        is_otg:1;     /* C语言中的位域结构体,is_otg占用1位,其值只能是0和1 */
        unsigned                        is_a_peripheral:1;
        unsigned                        b_hnp_enable:1;
        unsigned                        a_hnp_support:1;
        unsigned                        a_alt_hnp_support:1;

        unsigned                        quirk_ep_out_aligned_size:1;
        bool                            remote_wakeup;
        u32                             xfer_isr_count;
        u8                              usb_core_id;
        bool                            l1_supported;
        bool                            bam2bam_func_enabled;
        u32                             extra_buf_alloc;
        int                             interrupt_num;

};


/**
 * usb_gadget_vbus_connect - Notify controller that VBUS is powered
 * @gadget:The device which now has VBUS power.
 * Context: can sleep
 *
 * This call is used by a driver for an external transceiver (or GPIO)
 * that detects a VBUS power session starting.  Common responses include
 * resuming the controller, activating the D+ (or D-) pullup to let the
 * host detect that a USB device is attached, and starting to draw power
 * (8mA or possibly more, especially after SET_CONFIGURATION).
 *
 * Returns zero on success, else negative errno.
 */
static inline int usb_gadget_vbus_connect(struct usb_gadget *gadget)
{
        if (!gadget->ops->vbus_session)
                return -EOPNOTSUPP;
        return gadget->ops->vbus_session(gadget, 1);
}

/**
 * usb_gadget_vbus_draw - constrain controller's VBUS power usage
 * @gadget:The device whose VBUS usage is being described
 * @mA:How much current to draw, in milliAmperes.  This should be twice
 *      the value listed in the configuration descriptor bMaxPower field.
 *
 * This call is used by gadget drivers during SET_CONFIGURATION calls,
 * reporting how much power the device may consume.  For example, this
 * could affect how quickly batteries are recharged.
 *
 * Returns zero on success, else negative errno.
 */
static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
{
        if (!gadget->ops->vbus_draw)
                return -EOPNOTSUPP;
        return gadget->ops->vbus_draw(gadget, mA);
}

/**
 * usb_gadget_vbus_disconnect - notify controller about VBUS session end
 * @gadget:the device whose VBUS supply is being described
 * Context: can sleep
 *
 * This call is used by a driver for an external transceiver (or GPIO)
 * that detects a VBUS power session ending.  Common responses include
 * reversing everything done in usb_gadget_vbus_connect().
 *
 * Returns zero on success, else negative errno.
 */
static inline int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
{
        if (!gadget->ops->vbus_session)
                return -EOPNOTSUPP;
        return gadget->ops->vbus_session(gadget, 0);
}


/**
 * usb_gadget_connect - software-controlled connect to USB host
 * @gadget:the peripheral being connected
 *
 * Enables the D+ (or potentially D-) pullup.  The host will start
 * enumerating this gadget when the pullup is active and a VBUS session
 * is active (the link is powered).  This pullup is always enabled unless
 * usb_gadget_disconnect() has been used to disable it.
 *
 * Returns zero on success, else negative errno.
 */
static inline int usb_gadget_connect(struct usb_gadget *gadget)
{
        if (!gadget->ops->pullup)
                return -EOPNOTSUPP;
        return gadget->ops->pullup(gadget, 1); 
}

/**
 * usb_gadget_disconnect - software-controlled disconnect from USB host
 * @gadget:the peripheral being disconnected
 *
 * Disables the D+ (or potentially D-) pullup, which the host may see
 * as a disconnect (when a VBUS session is active).  Not all systems
 * support software pullup controls.
 *
 * This routine may be used during the gadget driver bind() call to prevent
 * the peripheral from ever being visible to the USB host, unless later
 * usb_gadget_connect() is called.  For example, user mode components may
 * need to be activated before the system can talk to hosts.
 *
 * Returns zero on success, else negative errno.
 */
static inline int usb_gadget_disconnect(struct usb_gadget *gadget)
{
        if (!gadget->ops->pullup)
                return -EOPNOTSUPP;
        return gadget->ops->pullup(gadget, 0);
}

/**
 * usb_gadget_wakeup - tries to wake up the host connected to this gadget
 * @gadget: controller used to wake up the host
 *
 * Returns zero on success, else negative error code if the hardware
 * doesn't support such attempts, or its support has not been enabled
 * by the usb host.  Drivers must return device descriptors that report
 * their ability to support this, or hosts won't enable it.
 *
 * This may also try to use SRP to wake the host and start enumeration,
 * even if OTG isn't otherwise in use.  OTG devices may also start
 * remote wakeup even when hosts don't explicitly enable it.
 */
static inline int usb_gadget_wakeup(struct usb_gadget *gadget)
{
        if (!gadget->ops->wakeup)
                return -EOPNOTSUPP;
        return gadget->ops->wakeup(gadget);
}

(2)Gadget驱动端添加的OTG相关属性和函数

    如果gadget->is_otg属性为真,则增加一个OTG描述符;通过printk()、LED等方式报告HNP(主机通令协议)可用;当挂起开始时,通过用户界面报告HNP切换开始。

(3)主机侧添加的OTG相关属性和函数

在USB核心中新增了关于OTG设备枚举的信息:

linux/usb.h

struct usb_bus {
        struct device *controller;      /* host/master side hardware */
        int busnum;                     /* Bus number (in order of reg) */
        const char *bus_name;           /* stable id (PCI slot_name etc) */
        u8 uses_dma;                    /* Does the host controller use DMA? */
        u8 uses_pio_for_control;        /*
                                         * Does the host controller use PIO
                                         * for control transfers?
                                         */
        u8 otg_port;                    /* 0, or number of OTG/HNP port */
        unsigned is_b_host:1;           /* true during some HNP roleswitches */
        unsigned b_hnp_enable:1;        /* OTG: did A-Host enable HNP? */

        unsigned no_stop_on_short:1;    /*
                                         * Quirk: some controllers don't stop
                                         * the ep queue on a short transfer
                                         * with the URB_SHORT_NOT_OK flag set.
                                         */
        unsigned no_sg_constraint:1;    /* no sg constraint */
        unsigned sg_tablesize;          /* 0 or largest number of sg list entries */


        int devnum_next;                /* Next open device number in
                                         * round-robin allocation */


        struct usb_devmap devmap;       /* device address allocation map */
        struct usb_device *root_hub;    /* Root hub */
        struct usb_bus *hs_companion;   /* Companion EHCI bus, if any */
        struct list_head bus_list;      /* list of busses */

        struct mutex usb_address0_mutex; /* unaddressed device mutex */

        int bandwidth_allocated;        /* on this bus: how much of the time
                                         * reserved for periodic (intr/iso)
                                         * requests is used, on average?
                                         * Units: microseconds/frame.
                                         * Limits: Full/low speed reserve 90%,
                                         * while high speed reserves 80%.
                                         */
        int bandwidth_int_reqs;         /* number of Interrupt requests */
        int bandwidth_isoc_reqs;        /* number of Isoc. requests */

        unsigned resuming_ports;        /* bit array: resuming root-hub ports */


#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
        struct mon_bus *mon_bus;        /* non-null when associated */
        int monitored;                  /* non-zero when monitored */
#endif
        unsigned skip_resume:1;         /* All USB devices are brought into full
                                         * power state after system resume. It
                                         * is desirable for some buses to keep
                                         * their devices in suspend state even
                                         * after system resume. The devices
                                         * are resumed later when a remote
                                         * wakeup is detected or an interface
                                         * driver starts I/O.
                                         */
};

为了实现HNP需要的挂起/恢复,新增如下通用接口:

int usb_suspend_device(struct usb_device *dev, u32 state);
int usb_resume_device(struct usb_device *dev);

(4)新增OTG功能切换和协议的描述结构体usb_otg

linux/usb/otg.h

struct usb_otg {
        u8                      default_a;
        struct usb_phy          *phy;
        struct usb_bus          *host;
        struct usb_gadget       *gadget;

        /* bind/unbind the host controller */
        int     (*set_host)(struct usb_otg *otg, struct usb_bus *host);
        /* bind/unbind the peripheral controller */
        int     (*set_peripheral)(struct usb_otg *otg, truct usb_gadget *gadget);

        /* effective for A-peripheral, ignored for B devices */
        int     (*set_vbus)(struct usb_otg *otg, bool enabled);

        /* for B devices only:  start session with A-Host */
        int     (*start_srp)(struct usb_otg *otg);

        /* start or continue HNP role switch */
        int     (*start_hnp)(struct usb_otg *otg);
};

备注:

usb_otg的代码一般在USB的phy端实现。

16.6 总结

    USB驱动分为USB主机驱动和USB设备驱动,如果系统的USB主机控制器符合OHCI(Open Host Controller Interface)等标准,那主机驱动的绝大部分工作都可以沿用通用的代码。

    对于一个USB设备,USB设备至少具备两重身份:首先USB设备是“USB”的,其次USB设备是“自己”的。USB设备是“USB”的,指USB设备挂接在USB总线上,其必须完成usb_driver的初始化和注册;USB设备是“自己”的,意味着本身可能是一个字符设备、tty设备、网络设备等,在USB设备驱动中也必须实现符合相应框架的代码。

    USB设备驱动的自身设备驱动部分的读写等操作流程有其特殊性,以URB(USB请求块)来贯穿始终,一个URB的生命周期通常包含创建、初始化、提交和被USB核心及USB主机传递、完成后回调函数被调用的过程,在URB被驱动提交后,也可以被取消。

    在UDC(USB设备控制器)和Gadget Function(UDC驱动之上的)侧,UDC关心底层的硬件操作,而Function(File Storage)驱动则只是利用通用的API,并通过usb_request与底层UDC驱动交互。



猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80600337