OTG即可工作在host模式又可以工作在device模式,模式之间通过HNP协议来进行转换。“USB接口上有4个管脚,OTG功能有5个。原来4个分别是电 D+ D- 地。 现在增加了一个ID。这个ID线就决定了自己做主设备还是从设备。如果ID线是高则自己是从设备,反之是主设备。”
s5pv210使用的是s3c_udc_otg.c驱动。
arch/arm/plat-s5p/dev-hsdevice.c
定义platform_device结构
struct platform_device s3c_device_usbgadget = {
.name = "s3c-usbgadget",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_usbgadget_resource),
.resource = s3c_usbgadget_resource,
};
定义platform_device下的struct resource设备资源结构
static struct resource s3c_usbgadget_resource[] = {
[0] = {
//描述设备实体在cpu总线上的线性起始物理地址
.start = S3C_PA_OTG,
//描述设备实体在cpu总线上的线性结尾物理地址
.end = S3C_PA_OTG + S3C_SZ_OTG - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_OTG,//中断号
.end = IRQ_OTG,
.flags = IORESOURCE_IRQ,
}
};
void __init s5p_otg_set_platdata(struct s5p_otg_platdata *pd)
{
struct s5p_otg_platdata *npd;
npd = s3c_set_platdata(pd, sizeof(struct s5p_otg_platdata),
&s3c_device_usbgadget);
if (!npd->phy_init)
npd->phy_init = s5p_usb_phy_init;
if (!npd->phy_exit)
npd->phy_exit = s5p_usb_phy_exit;
}
arch/arm/mach-s5pv210/mach-mini210.c
/* USB DEVICE */
static struct s5p_otg_platdata mini210_otg_pdata;
static void __init mini210_otg_init(void)
{
struct s5p_otg_platdata *pdata = &mini210_otg_pdata;
s5p_otg_set_platdata(pdata);
}
drivers/usb/gadget/s3c_udc_otg.c
static struct platform_driver s3c_udc_driver = {
.probe = s3c_udc_probe,
.remove = s3c_udc_remove,
.suspend = s3c_udc_suspend,
.resume = s3c_udc_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-usbgadget", //通过该名字匹配开始注册进系统的设备
},
};
//定义处理函数,在设备名字匹配到后调用,发现该驱动对应的设备在系统中注册过。
static int s3c_udc_probe(struct platform_device *pdev)
{
struct s3c_udc *dev = &memory;
int retval;
printk("<<<<< %s >>>>>\n", __func__);
DEBUG("%s: %p\n", __func__, pdev);
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform data defined\n");
return -EINVAL;
}
spin_lock_init(&dev->lock);
dev->dev = pdev;
device_initialize(&dev->gadget.dev);
dev->gadget.dev.parent = &pdev->dev;
dev->gadget.is_dualspeed = 1; /* Hack only*/
dev->gadget.is_otg = 0;
dev->gadget.is_a_peripheral = 0;
dev->gadget.b_hnp_enable = 0;
dev->gadget.a_hnp_support = 0;
dev->gadget.a_alt_hnp_support = 0;
the_controller = dev;
platform_set_drvdata(pdev, dev);
dev_set_name(&dev->gadget.dev, "gadget");
otg_clock = clk_get(&pdev->dev, "otg");
if (otg_clock == NULL) {
printk("failed to find otg clock source\n");
return -ENOENT;
}
udc_reinit(dev);
retval = request_irq(IRQ_EINT8, s5p_eint8_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "s5pv210-ein8", dev);
if (retval != 0)
printk("s5pv210-ein8: can't get irq %i, err %d\n", IRQ_EINT8, retval);
/* irq setup after old hardware state is cleaned up */
retval =
request_irq(IRQ_OTG, s3c_udc_irq, 0, driver_name, dev);
if (retval != 0) {
printk("%s: can't get irq %i, err %d\n", driver_name,
IRQ_OTG, retval);
return -EBUSY;
}
wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
disable_irq(IRQ_OTG);
create_proc_files();
return retval;
}
//将platform_driver结构注册进系统
//系统通过注册名字匹配该设备是否已经在系统中
//如果在调用注册的probe = s3c_udc_probe函数
static int __init udc_init(void)
{
int ret;
printk("<<<<< %s >>>>>\n", __func__);
ret = platform_driver_register(&s3c_udc_driver);
if (!ret)
printk(KERN_INFO "%s : %s\n"
"%s : version %s %s\n",
driver_name, DRIVER_DESC,
driver_name, DRIVER_VERSION, OTG_DMA_MODE ?
"(DMA Mode)" : "(Slave Mode)");
return ret;
}
http://blog.csdn.net/fanqipin/article/details/8450694
说的比较好,但对于USB新手来说,真的是看得云里雾里的。otg controller? gadget device controller? Device controller? gadget driver?
不知道有没从上电枚举开始,硬件驱动都讲的?
他讲的还是有点模糊。
host端枚举过程如下:
a. 当hub检测到hub状态寄存器发生变化时,去唤醒hub的守护进程,去查看具体是那 一个port引起的;
b. 如果发现有设备插入,等待100ms使设备稳定后,root hub会向插入设备的port发送reset请求,reset成功后usb设备处于default状态;
c. 向新插入的设备发送设置地址请求,用来设置设备地址;
d. 获取usb设备的设备描述符;
e.根据usb设备的配置个数,获取对应usb设备配置描述符;
f. 选择e获取的配置描述符,选择合适 的usb配置,对usb设备进行配置;
“usb设备侧对host的请求都是通过中断进行触发的”?这个usb设备侧?这个host?
他下面就分析s3c-hsotg.c了,但我这边210开发板用到的是s3c_udc_otg.c。对于函数及数据结构,没有给出文件目录就直接代码分析了,东一块西一块的,对于我们新手真的只能看着他给的代码看,而无法自己跟着他的思路找代码。
反正我看完,就只知道他分析了,也分析了代码,但一点暂时记忆都没有,不知道他到底分析的是什么流程,数据是怎样的走向。重复看了几遍之后还是一脸的茫然。
自己再看看吧。
210开发板运行Linux系统接上OTG线,没有任何反应。dmesg下也没有任何新加的打印信息。
怎么找到s3c_udc_otg.c,先不说了。
加打印,dmesg | grep “s3c_udc_probe”搜索到了,说明s3c_udc_probe运行了。查看drivers/usb/gadget/s3c_udc_otg.c,s3c_udc_probe设置在s3c_udc_driver里。s3c_udc_driver是名为“s3c-usbgadget”的平台驱动,那就还有“s3c-usbgadget”平台设备。
linux源码下搜索,找到arch/arm/plat-s5p/dev-hsdevice.c中s3c_device_usbgadget平台设备。但这个文件很简单,没有注册平台设备。但看到s5p_otg_set_platdata()调用。搜索此函数的调用。找到arch/arm/mach-s5pv210/mach-mini210.c。在mini210_otg_init()中调用设置了。mini210_otg_init()调用自mini210_machine_init()。
mini210_machine_init()调用的地方。
MACHINE_START(MINI210, "MINI210")
/* Maintainer: Kukjin Kim <[email protected]> */
.boot_params = S5P_PA_SDRAM + 0x100,
.fixup = mini210_fixup,
.init_irq = s5pv210_init_irq,
.map_io = mini210_map_io,
.init_machine = mini210_machine_init,
#ifdef CONFIG_S5P_HIGH_RES_TIMERS
.timer = &s5p_systimer,
#else
.timer = &s5p_timer,
#endif
MACHINE_END
init_machine,那肯定是会运行了,s3c_device_usbgadget平台设备是注册上去了。为验证,s5p_otg_set_platdata()中加打印,dmesg | grep "s5p_otg_set_platdata",确实。
在s3c_udc_probe中:
static int s3c_udc_probe(struct platform_device *pdev)
{
struct s3c_udc *dev = &memory;
int retval;
DEBUG("%s: %p\n", __func__, pdev);
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform data defined\n");
return -EINVAL;
}
spin_lock_init(&dev->lock);
dev->dev = pdev;
device_initialize(&dev->gadget.dev);
dev->gadget.dev.parent = &pdev->dev;
dev->gadget.is_dualspeed = 1; /* Hack only*/
dev->gadget.is_otg = 0;
dev->gadget.is_a_peripheral = 0;
dev->gadget.b_hnp_enable = 0;
dev->gadget.a_hnp_support = 0;
dev->gadget.a_alt_hnp_support = 0;
the_controller = dev;
platform_set_drvdata(pdev, dev);
dev_set_name(&dev->gadget.dev, "gadget");
otg_clock = clk_get(&pdev->dev, "otg");
if (otg_clock == NULL) {
pr_err("failed to find otg clock source\n");
return -ENOENT;
}
udc_reinit(dev);
/* irq setup after old hardware state is cleaned up */
retval =
request_irq(IRQ_OTG, s3c_udc_irq, 0, driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n", driver_name,
IRQ_OTG, retval);
return -EBUSY;
}
wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
disable_irq(IRQ_OTG);
create_proc_files();
return retval;
}
request_irq申请了IRQ_OTG,但却disable了。搜索enable的地方。就两个地方:
1)usb_gadget_probe_driver()
2)s3c_udc_resume()
s3c_udc_resume是电源管理部分(休眠)的,那就是usb_gadget_probe_driver()。
struct usb_gadget_driver {
char *function;
enum usb_device_speed speed;
void (*unbind)(struct usb_gadget *);
int (*setup)(struct usb_gadget *,
const struct usb_ctrlrequest *);
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *);
/* FIXME support safe rmmod */
struct device_driver 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;
unsigned needs_serial:1;
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 *);
};
继续找,http://blog.csdn.net/yaozhenguo2006?viewmode=contents,《Linux USB Gadget--软件结构》、《Linux USB Gadget--各环节的整合》、《Linux USB Gadget--设备枚举》三篇文章还不错。
Webcam使用的却是usb_composite_driver?