自娱自乐2之Linux gadget驱动1(linux-3.2.36的composite)

上期说要用之前的模板写udc驱动。事实我已经做了,等待测试。当我要测试时,我发现还是要用gadget驱动去调试udc驱动。虽然有现成的,但是你如果搞不懂的话,出问题你根本不知道在哪。所以调试udc驱动之前我们要看gadget驱动。

我看了linux-2.6.10没有composite的东西。还是直接操作struct usb_gadget_driver。那已经是过去式了。现在我用的linux-3.2.36是composite。首先我们就看composite有哪些东西,接着通过实例分析怎么用。

 

struct usb_function {

       const char                     *name;//

       struct usb_gadget_strings       **strings;//字符串描述附

    //这个struct usb_descriptor_header包涵了接口描述符和端点描述符

    //在zero中

    /*

static structusb_descriptor_header *fs_source_sink_descs[] = {

         (structusb_descriptor_header *) &source_sink_intf,

         (structusb_descriptor_header *) &fs_sink_desc,

         (structusb_descriptor_header *) &fs_source_desc,

          NULL,

};

    */

       struct usb_descriptor_header  **descriptors;//全速

       struct usb_descriptor_header  **hs_descriptors;//高速

       struct usb_descriptor_header  **ss_descriptors;//超速

 

       struct usb_configuration *config;//下面看到这个,usb_add_function会调用

 

    //绑定可用的源,我看sourecesink就是gedget->ep_list获取可用的端点、并获取端//点的wMaxPacketSize、bEndpointAddress。还有高速和超速描述符赋值

       int                 (*bind)(structusb_configuration *,

                                   structusb_function *);

       void               (*unbind)(structusb_configuration *,

                                   structusb_function *);

       //配置接口,sourecesink就是通过gadget的速度来选择对应的structusb_desctiptor_header

       //就是上面的三个


      //set_alt:重配置altsettings

       int                 (*set_alt)(structusb_function *,

                                   unsignedinterface, unsigned alt);

       //get_alt:返回当前活动 altsettings

int                 (*get_alt)(structusb_function *,

                                   unsignedinterface);

    //表示这个function应该disable,原因可能是主机复位、重配置、断开

       void               (*disable)(structusb_function *);

    //setup用于接口特性控制请求

       int                 (*setup)(structusb_function *,

                                   const structusb_ctrlrequest *);

    //suspend和resume不用解释吧

       void               (*suspend)(structusb_function *);

       void               (*resume)(structusb_function *);

 

       //对应主机的GETSTATUS请求

       int                 (*get_status)(structusb_function *);

    //接受到SETFEATURE的USB_INTRF_FUNC_SUSPEND时的回调函数

       int                 (*func_suspend)(structusb_function *,

                                          u8suspend_opt);

 

       struct list_head              list;//下面

       DECLARE_BITMAP(endpoints, 32);

};

 

//把function加入到config中

//就是这句list_add_tail(&function->list,&config->functions);

//然后执行bind(如果有的话),速度标志位赋值

int usb_add_function(struct usb_configuration *config, struct usb_function *function);

 

//这俩个会调用udc中的pull_up,可以多次调用。

//如果deactivate调用三次,那么avtivate要调用三次才能把D+拉高

int usb_function_deactivate(struct usb_function *);

int usb_function_activate(struct usb_function *);

 

/*

int usb_interface_id(structusb_configuration *config,

                struct usb_function *function)

{

        unsigned id =config->next_interface_id;

 

        if (id < MAX_CONFIG_INTERFACES) {

                config->interface[id] =function;

                config->next_interface_id =id + 1;

                return id;

        }

        return -ENODEV;

}

简单,不解释

*/

intusb_interface_id(struct usb_configuration *, struct usb_function *);

 

/*

这就是上面的通过gadget的速度来选择对应的structusb_desctiptor_header

的函数

*/

int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,

                     struct usb_ep *_ep);

 

//usb最多255个接口,linux定义是16个

#define   MAX_CONFIG_INTERFACES           16    /* arbitrary;max 255 */

 

//相当于配置

struct usb_configuration {

       const char                     *label;//相当于name

       struct usb_gadget_strings       **strings;//字符串描述符

       const struct usb_descriptor_header**descriptors;//先前的所有function描述表

 

        //和bind相反 ,bind在哪,下面看到

       void               (*unbind)(structusb_configuration *);

    //代表非标准控制请求,sourecesink实现的就是它

       int                 (*setup)(structusb_configuration *,

                                   const structusb_ctrlrequest *);

 

    //配置描述符的东西,spec说的很详细    

       u8                  bConfigurationValue;

       u8                  iConfiguration;

       u8                  bmAttributes;

       u8                  bMaxPower;

 

    //对应设备

       struct usb_composite_dev      *cdev;

 

       struct list_head       list;  //和上面的结构体的list一样

       struct list_head       functions;       //上面已看到使用

       u8                  next_interface_id;   //下一个接口id,上面的usb_interface_id有用

    // usb_add_function会设置它们

       unsigned         superspeed:1;

       unsigned         highspeed:1;

       unsigned         fullspeed:1;

       struct usb_function *interface[MAX_CONFIG_INTERFACES];

};

 

   //和上面的usb_add_function差不多,bind对应上面的unbind

intusb_add_config(struct usb_composite_dev *cdev,

                struct usb_configuration*config,

                int (*bind)(structusb_configuration *))

 

//相当于usb_gadget_driver

struct usb_composite_driver {

       const char                            *name;//

       const char                            *iProduct;//产品

       const char                            *iManufacturer;//厂商信息

       const struct usb_device_descriptor *dev;//下面

       struct usb_gadget_strings              **strings;//字符串描述符

       enum usb_device_speed               max_speed;//最高速度

       unsigned         needs_serial:1;//是否需要iSerialNumber

 

       //下面不解释

       int                 (*unbind)(structusb_composite_dev *);

 

       void               (*disconnect)(structusb_composite_dev *);

 

       /* global suspend hooks */

       void               (*suspend)(structusb_composite_dev *);

       void               (*resume)(structusb_composite_dev *);

};

 

//先根据driver给composite_driver赋值,再

//usb_gadget_probe_driver(&composite_driver, composite_bind);

/*

composite_driver是个全局的变量

static struct usb_gadget_driver composite_driver = {

#ifdefCONFIG_USB_GADGET_SUPERSPEED

        .speed          = USB_SPEED_SUPER,

#else

        .speed          = USB_SPEED_HIGH,

#endif

 

        .unbind         = composite_unbind,

 

        .setup          = composite_setup,

        .disconnect     = composite_disconnect,

 

        .suspend        = composite_suspend,

        .resume         = composite_resume,

 

        .driver = {

                .owner          = THIS_MODULE,

        },

};

composite就是通过它和gadget的接口联系的

*/

extern int usb_composite_probe(struct usb_composite_driver *driver,

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

extern void usb_composite_unregister(struct usb_composite_driver *driver);

//继续控制传输

extern void usb_composite_setup_continue(struct usb_composite_dev*cdev);

 

//相当于设备

struct usb_composite_dev {

       struct usb_gadget           *gadget;//只读,抽象的表示gadget的usb外设控制器

       struct usb_request          *req;//用于控制答复

       unsigned                bufsiz;//req的预分配大小

 

       struct usb_configuration *config;//当前活动的config

 

       /* private: */

       /* internals */

       unsigned int                  suspended:1;

       struct usb_device_descriptor  desc;//设备描述符

       struct list_head              configs;//配置链表

       struct usb_composite_driver  *driver;//上面的

       u8                         next_string_id;

    //下面表示对应的字符串的id

       u8                         manufacturer_override;

       u8                         product_override;

       u8                         serial_override;

 

       /*deactivations是int usb_function_deactivate(struct usb_function *);

         int usb_function_activate(struct usb_function *);计数用的

      */

 

       unsigned                deactivations;

 

       /* the composite driver won't completethe control transfer's

        *data/status stages till delayed_status is zero.//这段英文自己理解

        */

/*

若果设备

USB_REQ_SET_INTERFACE结果返回USB_GADGET_DELAYED_STATUS,计数加1

usb_composite_setup_continue计数减1,到0继续执行控制请求。

*/

       int                        delayed_status;

 

       /* protects deactivations anddelayed_status counts*/

       spinlock_t                     lock;

};

 

/*

int usb_string_id(struct usb_composite_dev *cdev)

{

        if (cdev->next_string_id < 254) {

                /* string id 0 is reserved byUSB spec for list of

                 * supported languages */

                /* 255 reserved as well? --mina86 */

                cdev->next_string_id++;

                return cdev->next_string_id;

        }

        return -ENODEV;

}

简单

看zero

       id = usb_string_id(cdev);

       if (id < 0)

              return id;

       strings_dev[STRING_MANUFACTURER_IDX].id =id;

       device_desc.iManufacturer = id;

 

       id = usb_string_id(cdev);

       if (id < 0)

              return id;

       strings_dev[STRING_PRODUCT_IDX].id = id;

       device_desc.iProduct = id;

 

       id = usb_string_id(cdev);

       if (id < 0)

              return id;

       strings_dev[STRING_SERIAL_IDX].id = id;

       device_desc.iSerialNumber = id;

就是给上面的字符串分配id

*/

extern int usb_string_id(struct usb_composite_dev *c);

//下面两个是批量分配

extern intusb_string_ids_tab(struct usb_composite_dev *c,

                           struct usb_string *str);

extern intusb_string_ids_n(struct usb_composite_dev *c, unsigned n);

这几天在群里忙着和别人讨论问题,一直没在弄了。今天简单看了一下zero.c

比较让我惊奇的是,有个

/* If the optional "autoresume" mode isenabled, it provides good

 * functionalcoverage for the "USBCV" test harness from USB-IF.

 */

unsigned autoresume = DEFAULT_AUTORESUME;

module_param(autoresume, uint, S_IRUGO);

MODULE_PARM_DESC(autoresume, "gadget_transfer, or seconds before remotewakeup");

从英文看是为USB-IF测试软件USBCV(可以下载到)做的,这就不管了。

我之前看过hub有个自动挂起的东西。它的主要就是一定时间(默认2s)不用就自动suspend。

这里先假设是2秒吧

在f_sourcesink.c sourcesink_add

       /* support autoresume for remote wakeup testing */

       if (autoresume)

                sourcesink_driver.bmAttributes|= USB_CONFIG_ATT_WAKEUP;

 

       /* support OTG systems */

       if (gadget_is_otg(cdev->gadget)) {

                sourcesink_driver.descriptors =otg_desc;

                sourcesink_driver.bmAttributes|= USB_CONFIG_ATT_WAKEUP;

       }

下面的otg是几乎每个gadget驱动都这样写。我们不管otg

上面说的很清楚支持autoresume为了远程唤醒功能测试

static void zero_autoresume(unsignedlong _c)

{

       struct usb_composite_dev       *cdev = (void *)_c;

       struct usb_gadget               *g= cdev->gadget;

 

       /* unconfigured devices can't issue wakeups */

       if (!cdev->config)

                return;

 

       /* Normally the host would be woken up for something

        * more significant than just a timer firing; likely

        * because of some direct user request.

        */

       if (g->speed != USB_SPEED_UNKNOWN) {

                int status =usb_gadget_wakeup(g);//这个会调用udc中的wakeup

                INFO(cdev, "%s -->%d\n", __func__, status);

       }

}

 

static void zero_suspend(structusb_composite_dev *cdev)

{

       if (cdev->gadget->speed == USB_SPEED_UNKNOWN)

                return;

 

       if (autoresume) {

               mod_timer(&autoresume_timer, jiffies + (HZ * autoresume));

                DBG(cdev, "suspend, wakeupin %d seconds\n", autoresume);

       } else

                DBG(cdev, "%s\n",__func__);

}

 

static void zero_resume(structusb_composite_dev *cdev)

{

       DBG(cdev, "%s\n", __func__);

       del_timer(&autoresume_timer);

}

这个zero_suspend和zero_resume一般都在udc的中断处理中调用

有autoresume也就是定时到时自动wakeup。

没这个的话,这两个函数就等于什么都没干。

 

主机端set feature会有个叫remote_wake的值是0x0001,与配置描述符对应的是

sourcesink_driver.bmAttributes|= USB_CONFIG_ATT_WAKEUP,前面是主机的请求,后面是表示设备有这个能力。

 

我看了net2272有个IRQSTAT1寄存器,如果有请求改变,再判断是否是suspend请求

是调用dev->driver->suspend不是调用dev->driver_resume。这两个不对应上面的。

是composite_suspend和composite_resume。在它们里面调用上面的。

 

在看s3c2440的,它的suspend比较变态,每当总线无活动多于 3ms 将置位此位(指的是suspend中断标志位)。因此,如果 MCU 不在第一个挂起中断后停止时钟,它将会持续每 3ms 中断一次,直到 USB 总线无活动。默认是禁止此中断。

 

我们看第一句话,可以看出s3c2440的suspend中断和主机无关,真的叫自动化。

那个zero的resume大概就是已经唤醒了就不要在执行后面的自动化了,所以del timer。

把这个suspend和resume提出来是因为我要写的东西不会包涵它们。

 

我这几天写文章就像挤牙膏啊,再说一点。gadget_chips.h

这个里面有些东西要定义一下,

static inline int usb_gadget_controller_number(struct usb_gadget *gadget);

获取控制器号

static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
是否支持altsettings,你会看到它里面放的是不支持的,可以想到一般的控制器是支持altsettings的。

说一下第一个

我们如果要写个自己的udc,我的是s3c2440,大家知道s3c2440用的是s3c2410。现在让它独立

#define gadget_is_s3c2410(g)            (!strcmp("s3c2410_udc", (g)->name))
#define gadget_is_s3c2440(g)            (!strcmp("s3c2440_udc", (g)->name))
首先定义这个,"s3c2440_udc"要和它的udc驱动一样的。

在usb_gadget_controller_number()里面加上

        else if (gadget_is_s3c2410(gadget))
                return 0x12;
        else if (gadget_is_s3c2440(gadget))
                return 0x12;

其实我还是用了一样的数。

看zero怎么用这个
    gcnum = usb_gadget_controller_number(gadget);
    if (gcnum >= 0)
        device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
    else {
        /* gadget gadget_transfer is so simple (for now, no altsettings) that
         * it SHOULD NOT have problems with bulk-capable hardware.
         * so just warn about unrcognized controllers -- don't panic.
         *
         * things like configuration and altsetting numbering
         * can need hardware-specific attention though.
         */
        pr_warning("%s: controller '%s' not recognized\n",
            longname, gadget->name);
        device_desc.bcdDevice = cpu_to_le16(0x9999);
    }

就获得之后转为小端做为设备bcd。就是设备描述符里的。没获得就是0x9999。


觉得一个文章写得太长了,所以想另开一篇,这篇就完了吧!

你可能感兴趣的:(自娱自乐2之Linux gadget驱动1(linux-3.2.36的composite))