上期说要用之前的模板写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。
觉得一个文章写得太长了,所以想另开一篇,这篇就完了吧!