前一段时间弄了
2
个礼拜的
OTG
驱动调试,感觉精神疲惫啊。主要原因还是自己对
OTG
功能不了解造成的。现在终于完成但是对实质原理还有些模糊。所以
自己重新总结一下。因为自己是菜鸟,所以用菜鸟的白话方式分析。高手滤过吧。
所谓
OTG
功能
就是具备该功能的设备即可当主设备
(host)
去轮询别人,也可以当从设备
(device)
去被别人轮~~(双性人?)。正所谓所有的产品和功能都是因为需求
存在的,举个最简单的需求,原来
MP3
想传送一个歌曲都得通过电脑。现在只要两个
MP3
链接,其中一个
MP3
有
OTG
功能作为主设备(相当于电脑主机),然后另外一个是从设备就可以
实现数据的传送了。
那么话说回来,具有
OTG
功能的设备如何确定自己是主还是从设备那。原来原来
USB
接口上有
4
个管脚,
OTG
功能有
5
个。原来
4
个分别是电
D+ D-
地。
现在增加了一个
ID
。这个
ID
线就决定了自己做主设备还是从设备。如果
ID
线是高则自己是从设备,反之是主设备。
下面开始分析代码。
向平时一样定义platform_device资源等信息。
定义platform_device结构
static
struct
platform_device __maybe_unused dr_otg_device =
{
.
name =
"fsl-usb2-otg"
,
//设备的名称 日后匹配用
.
id =
-
1,
//只有一个这样的设备
.
dev =
{
.
release =
dr_otg_release,
.
dma_mask
=
&
dr_otg_dmamask,
.
coherent_dma_mask
=
0xffffffff,
}
,
.
resource =
otg_resources,
//设备的资源 看下面
.
num_resources =
ARRAY_SIZE(
otg_resources)
,
}
;
定义platform_device下的struct
resource设备资源结构
static
struct
resource
otg_resources[
]
=
{
[
0]
=
{
.
start =
(
u32)
(
USB_OTGREGS_BASE)
,
//描述设备实体在cpu总线上的线
性起始物理地址
.
end =
(
u32)
(
USB_OTGREGS_BASE +
0x1ff)
,
//描述设备实体在cpu总线上的线性结尾物理地址
.
flags =
IORESOURCE_MEM,
}
,
[
1]
=
{
.
start =
MXC_INT_USB_OTG,
//中断号
.
flags =
IORESOURCE_IRQ,
}
,
}
;
定义平台设备私
有数据,以后驱动要使用
static
struct
fsl_usb2_platform_data __maybe_unused dr_utmi_config =
{
.
name =
"DR"
,
.
platform_init =
usbotg_init,
.
platform_uninit =
usbotg_uninit,
.
phy_mode
=
FSL_USB2_PHY_UTMI_WIDE,
.
power_budget =
500,
/* via RT9706 */
.
gpio_usb_active =
gpio_usbotg_utmi_active,
.
gpio_usb_inactive =
gpio_usbotg_utmi_inactive,
.
transceiver =
"utmi"
,
.
wake_up_enable
=
_wake_up_enable,
}
;
#
define
PDATA (
&
dr_utmi_config)
定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
static
inline
void
dr_register_otg(
void
)
{
PDATA-
>
operating_mode
=
FSL_USB2_DR_OTG;
//将模式更改(上面定义的时候定义的是FSL_USB2_PHY_UTMI_WIDE,不知道为什么开始不定义这个,可能是为了兼
容)
dr_otg_device.
dev.
platform_data =
PDATA;
//该设备的私有数据赋值,就
是上面定义的dr_utmi_config
if
(
platform_device_register(
&
dr_otg_device)
)
printk(
KERN_ERR "usb:
can't register otg device/n"
)
;
else
printk(
KERN_INFO
"usb: DR OTG registered/n"
)
;
}
上面几个过程主要是完成了设备的注册。这个过程是:
1.
定
义platform_device结构。
2.
定义
platform_device下的struct resource设备资源结构
3.
定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4.
调用platform_device_register将
platform_device结构
注册上面4个过程调用结束后,设备的信息就被注册到系统中,等待驱动的使用
下
面分析驱动和设备的链接过程
定义platform_driver结构
struct
platform_driver fsl_otg_driver =
{
.
probe =
fsl_otg_probe,
//定义处理函数,该函数在设备名字匹配到后调用,也就是发现该驱动
对应的设备在系统中注册过。
.
remove
=
fsl_otg_remove,
.
driver =
{
.
name =
"fsl-usb2-otg"
,
//通过该名字匹配开始注册进系统的设备
.
owner =
THIS_MODULE,
}
,
}
;
将platform_driver结构注册进系统,系统通
过注册名字匹配该设备是否已经在系统中,如果在调用注册的probe =
fsl_otg_probe函数
static
int
__init fsl_usb_otg_init(
void
)
{
printk(
KERN_INFO DRIVER_DESC " loaded, %s/n"
,
DRIVER_VERSION)
;
return
platform_driver_register(
&
fsl_otg_driver)
;
}
调用
fsl_otg_probe
函数,函数参数
platform_device
*pdev
,就是我们上面注册进系统
的
platform_device
结构,现在由系统赋值调用
fsl_otg_probe
static
int
__init fsl_otg_probe(
struct
platform_device *
pdev)
{
int
status;
struct
fsl_usb2_platform_data *
pdata;
DBG(
"pdev=0x%p/n"
,
pdev)
;
if
(
!
pdev)
return
-
ENODEV;
/*
判断是否有设备自己的数据,就是检查我们上面定义的
3
的过程*/
if
(
!
pdev-
>
dev.
platform_data)
return
-
ENOMEM;
pdata =
pdev-
>
dev.
platform_data;
/* configure the OTG */
status =
fsl_otg_conf(
pdev)
;
if
(
status)
{
printk(
KERN_INFO
"Couldn't init OTG module/n"
)
;
return
-
status;
}
/* start OTG */
status =
usb_otg_start(
pdev)
;
if
(
register_chrdev(
FSL_OTG_MAJOR,
FSL_OTG_NAME,
&
otg_fops)
)
{
printk(
KERN_WARNING FSL_OTG_NAME
": unable to register FSL OTG
device/n"
)
;
return
-
EIO;
}
create_proc_file(
)
;
return
status;
}
上面函数中调用了
fsl_otg_conf
,我们来看看他干了什么。
static
int
fsl_otg_conf(
struct
platform_device *
pdev)
{
int
status;
struct
fsl_otg *
fsl_otg_tc;
struct
fsl_usb2_platform_data *
pdata;
pdata =
pdev-
>
dev.
platform_data;
DBG(
)
;
/**************************************************************/
struct fsl_otg {
struct
otg_transceiver otg;
struct otg_fsm fsm;
struct usb_dr_mmap
*dr_mem_map;
struct delayed_work otg_event;
/*used for usb host */
struct
work_struct work_wq;
u8 host_working;
int irq;
};
/**************************************************************/
if
(
fsl_otg_dev)
return
0;
/* allocate space to fsl otg device */
fsl_otg_tc =
kzalloc(
sizeof
(
struct
fsl_otg)
,
GFP_KERNEL)
;
if
(
!
fsl_otg_tc)
return
-
ENODEV;
INIT_DELAYED_WORK(
&
fsl_otg_tc-
>
otg_event,
fsl_otg_event)
;
INIT_LIST_HEAD(
&
active_timers)
;
status =
fsl_otg_init_timers(
&
fsl_otg_tc-
>
fsm)
;
if
(
status)
{
printk(
KERN_INFO "Couldn't init OTG timers/n"
)
;
fsl_otg_uninit_timers(
)
;
kfree(
fsl_otg_tc)
;
return
status;
}
spin_lock_init(
&
fsl_otg_tc-
>
fsm.
lock)
;
/* Set OTG state machine operations */
/**************************************************************/
static struct otg_fsm_ops fsl_otg_ops = {
.chrg_vbus
= fsl_otg_chrg_vbus,
.drv_vbus = fsl_otg_drv_vbus,
.loc_conn =
fsl_otg_loc_conn,
.loc_sof = fsl_otg_loc_sof,
.start_pulse =
fsl_otg_start_pulse,
.add_timer = fsl_otg_add_timer,
.del_timer =
fsl_otg_del_timer,
.start_host = fsl_otg_start_host,
.start_gadget =
fsl_otg_start_gadget,
};
/**************************************************************/
fsl_otg_tc-
>
fsm.
ops =
&
fsl_otg_ops;
/* initialize the otg structure */
fsl_otg_tc-
>
otg.
label =
DRIVER_DESC;
fsl_otg_tc-
>
otg.
set_host =
fsl_otg_set_host;
fsl_otg_tc-
>
otg.
set_peripheral =
fsl_otg_set_peripheral;
fsl_otg_tc-
>
otg.
set_power
=
fsl_otg_set_power;
fsl_otg_tc-
>
otg.
start_hnp
=
fsl_otg_start_hnp;
fsl_otg_tc-
>
otg.
start_srp
=
fsl_otg_start_srp;
fsl_otg_dev =
fsl_otg_tc;
/* Store the otg transceiver */
/***************************************************************/
int otg_set_transceiver(struct otg_transceiver *x)
{
if
(xceiv && x)
return -EBUSY;
xceiv = x;
return 0;
}
该函数就是将struct
otg_transceiver结构副给一个全局变量保存,供以后使用,以后会通过调用下面函数得到该结构
struct otg_transceiver *otg_get_transceiver(void)
{
if
(xceiv)
get_device(xceiv->dev);
return xceiv;
}
/***************************************************************/
status
=
otg_set_transceiver(
&
fsl_otg_tc-
>
otg)
;
if
(
status)
{
printk(
KERN_WARNING ": unable to register OTG transceiver./n"
)
;
return
status;
}
return
0;
}
int
usb_otg_start(
struct
platform_device *
pdev)
{
struct
fsl_otg *
p_otg;
/*获得otg_transceiver结构
*/
struct
otg_transceiver *
otg_trans =
otg_get_transceiver(
)
;
struct
otg_fsm *
fsm;
volatile
unsigned
long
*
p;
int
status;
struct
resource *
res;
u32 temp;
/*获得设备的私有数据*/
struct
fsl_usb2_platform_data *
pdata =
pdev-
>
dev.
platform_data;
/*
使用container_of宏定义可以通过结构中一个变量的指针获得该结构首地址
*/
p_otg
=
container_of(
otg_trans,
struct
fsl_otg,
otg)
;
fsm =
&
p_otg-
>
fsm;
/* Initialize the state machine
structure with default values */
SET_OTG_STATE(
otg_trans,
OTG_STATE_UNDEFINED)
;
fsm-
>
transceiver =
&
p_otg-
>
otg;
/* We don't require predefined MEM/IRQ
resource index */
/*获得设备的资源,是在设备注册时结构体里面的内容*/
res =
platform_get_resource(
pdev,
IORESOURCE_MEM,
0)
;
if
(
!
res)
return
-
ENXIO;
/* We don't request_mem_region here to
enable resource sharing
* with host/device */
/*通过资源中
获得的物理地址映射一个可以被驱动访问的虚拟地址指针*/
usb_dr_regs =
ioremap(
res-
>
start,
sizeof
(
struct
usb_dr_mmap)
)
;
/*将该指针保存到p_otg
-
>
dr_mem_map中
*/
p_otg-
>
dr_mem_map =
(
struct
usb_dr_mmap *
)
usb_dr_regs;
pdata-
>
regs
=
(
void
*
)
usb_dr_regs;
/* request irq */
/*获得设备注册时候的中断并注册,在
OTG ID发生变化时触发中断,然后调用注册的中断例程函数,函数后面分析*/
p_otg-
>
irq =
platform_get_irq(
pdev,
0)
;
status
=
request_irq(
p_otg-
>
irq,
fsl_otg_isr,
IRQF_SHARED,
driver_name,
p_otg)
;
if
(
status)
{
dev_dbg(
p_otg-
>
otg.
dev,
"can't get IRQ %d, error %d/n"
,
p_otg-
>
irq,
status)
;
iounmap(
p_otg-
>
dr_mem_map)
;
kfree(
p_otg)
;
return
status;
}
if
(
pdata-
>
platform_init &
&
pdata-
>
platform_init(
pdev)
!
=
0)
return
-
EINVAL;
/* Export DR controller resources */
/**************************************************/
int otg_set_resources(struct resource *resources)
{
otg_resources
= resources;
return 0;
}
和otg_set_transceiver功能类似将设备资源保存到一个全局变量中
/**************************************************/
otg_set_resources(
pdev-
>
resource)
;
/*开始配置USB寄存器*/
/* stop the controller */
temp =
readl(
&
p_otg-
>
dr_mem_map-
>
usbcmd)
;
temp
&
=
~
USB_CMD_RUN_STOP;
writel(
temp,
&
p_otg-
>
dr_mem_map-
>
usbcmd)
;
/* reset the controller */
temp
=
readl(
&
p_otg-
>
dr_mem_map-
>
usbcmd)
;
temp
|
=
USB_CMD_CTRL_RESET;
writel(
temp,
&
p_otg-
>
dr_mem_map-
>
usbcmd)
;
/*
wait reset completed */
while
(
readl(
&
p_otg-
>
dr_mem_map-
>
usbcmd)
&
USB_CMD_CTRL_RESET)
;
/* configure the VBUSHS as IDLE(both
host and device) */
temp =
USB_MODE_STREAM_DISABLE |
(
pdata-
>
es ?
USB_MODE_ES :
0)
;
writel(
temp,
&
p_otg-
>
dr_mem_map-
>
usbmode)
;
/*
configure PHY interface */
temp =
readl(
&
p_otg-
>
dr_mem_map-
>
portsc)
;
temp &
=
~
(
PORTSC_PHY_TYPE_SEL |
PORTSC_PTW)
;
switch
(
pdata-
>
phy_mode)
{
case
FSL_USB2_PHY_ULPI:
temp |
=
PORTSC_PTS_ULPI;
break
;
case
FSL_USB2_PHY_UTMI_WIDE:
temp |
=
PORTSC_PTW_16BIT;
/* fall through */
case
FSL_USB2_PHY_UTMI:
temp |
=
PORTSC_PTS_UTMI;
/* fall through */
default
:
break
;
}
writel(
temp,
&
p_otg-
>
dr_mem_map-
>
portsc)
;
if
(
pdata-
>
have_sysif_regs)
{
/* configure control enable IO output,
big endian register */
p =
(
volatile
unsigned
long
*
)
(
&
p_otg-
>
dr_mem_map-
>
control)
;
temp =
*
p;
temp
|
=
USB_CTRL_IOENB;
*
p =
temp;
}
/* disable all interrupt and clear all OTGSC status
*/
temp =
readl(
&
p_otg-
>
dr_mem_map-
>
otgsc)
;
temp &
=
~
OTGSC_INTERRUPT_ENABLE_BITS_MASK;
temp |
=
OTGSC_INTERRUPT_STATUS_BITS_MASK |
OTGSC_CTRL_VBUS_DISCHARGE;
writel(
temp,
&
p_otg-
>
dr_mem_map-
>
otgsc)
;
/*
* The identification (id)
input is FALSE when a Mini-A plug is inserted
* in the devices
Mini-AB receptacle. Otherwise, this input is TRUE.
* Also:
record initial state of ID pin
*/
if
(
le32_to_cpu(
p_otg-
>
dr_mem_map-
>
otgsc)
&
OTGSC_STS_USB_ID)
{
p_otg-
>
otg.
state =
OTG_STATE_UNDEFINED;
p_otg-
>
fsm.
id =
1;
}
else
{
p_otg-
>
otg.
state =
OTG_STATE_A_IDLE;
p_otg-
>
fsm.
id =
0;
}
DBG(
"initial ID pin=%d/n"
,
p_otg-
>
fsm.
id)
;
/*
enable OTG ID pin interrupt */
temp =
readl(
&
p_otg-
>
dr_mem_map-
>
otgsc)
;
temp |
=
OTGSC_INTR_USB_ID_EN;
temp
&
=
~
(
OTGSC_CTRL_VBUS_DISCHARGE |
OTGSC_INTR_1MS_TIMER_EN)
;
writel(
temp,
&
p_otg-
>
dr_mem_map-
>
otgsc)
;
return
0;
}
下面分析下 中断例程函数
该函数就是判断ID的高低,也就是自己做主设备还是从设备
irqreturn_t fsl_otg_isr(
int
irq,
void
*
dev_id)
{
struct
otg_fsm *
fsm =
&
(
(
struct
fsl_otg *
)
dev_id)
-
>
fsm;
struct
otg_transceiver *
otg =
&
(
(
struct
fsl_otg *
)
dev_id)
-
>
otg;
u32 otg_int_src,
otg_sc;
/*
获得ID的变化信息*/
otg_sc =
le32_to_cpu(
usb_dr_regs-
>
otgsc)
;
otg_int_src =
otg_sc &
OTGSC_INTSTS_MASK &
(
otg_sc
>
>
8)
;
/* Only clear otg interrupts */
usb_dr_regs-
>
otgsc |
=
cpu_to_le32(
otg_sc &
OTGSC_INTSTS_MASK)
;
/*FIXME: ID change not generate when init to 0 */
fsm-
>
id =
(
otg_sc &
OTGSC_STS_USB_ID)
?
1 :
0;
otg-
>
default_a =
(
fsm-
>
id =
=
0)
;
/* process OTG interrupts */
if
(
otg_int_src)
{
if
(
otg_int_src
&
OTGSC_INTSTS_USB_ID)
{
fsm-
>
id =
(
otg_sc &
OTGSC_STS_USB_ID)
?
1 :
0;
otg-
>
default_a =
(
fsm-
>
id
=
=
0)
;
/* clear conn information */
if
(
fsm-
>
id)
fsm-
>
b_conn =
0;
else
fsm-
>
a_conn =
0;
if
(
otg-
>
host)
otg-
>
host-
>
is_b_host =
fsm-
>
id;
if
(
otg-
>
gadget)
otg-
>
gadget-
>
is_a_peripheral =
!
fsm-
>
id;
VDBG(
"ID int (ID is %d)/n"
,
fsm-
>
id)
;
if
(
fsm-
>
id)
{
/* switch to gadget *///从设备
/*schedule_delayed_work函数先停止主设备后打开从设备
*/
/***************************************************/
schedule_delayed_work(
&
(
(
struct
fsl_otg *
)
dev_id)
-
>
otg_event,
100)
;
函数就
是延迟100秒调用otg_event,就是下面函数。
static void fsl_otg_event(struct work_struct *work)
{
struct
fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
struct
otg_fsm *fsm = &og->fsm;
if (fsm->id) { /* switch to gadget */
fsl_otg_start_host(fsm,
0);
otg_drv_vbus(fsm, 0);
fsl_otg_start_gadget(fsm, 1);
}
}
/***************************************************/
schedule_delayed_work(
&
(
(
struct
fsl_otg *
)
dev_id)
-
>
otg_event,
100)
;
}
else
{
/*
switch to host *///主设备
cancel_delayed_work(
&
(
(
struct
fsl_otg *
)
dev_id)
-
>
otg_event)
;
fsl_otg_start_gadget(
fsm,
0)
;//停止从设备
otg_drv_vbus(
fsm,
1)
;
fsl_otg_start_host(
fsm,
1)
;//打开主
}
return
IRQ_HANDLED;
}
}
return
IRQ_NONE;
}
int
fsl_otg_start_host(
struct
otg_fsm *
fsm,
int
on)
{
struct
otg_transceiver *
xceiv =
fsm-
>
transceiver;
struct
device *
dev;
struct
fsl_otg *
otg_dev =
container_of(
xceiv,
struct
fsl_otg,
otg)
;
struct
platform_driver *
host_pdrv;
struct
platform_device *
host_pdev;
u32 retval =
0;
/*判
断是否有主设备的驱动注册进系统*/
if
(
!
xceiv-
>
host)
return
-
ENODEV;
dev =
xceiv-
>
host-
>
controller;
/*找到主设备驱动的platform_driver结构,为下面的停止和恢复函数调用做准备
*/
host_pdrv
=
container_of(
(
dev-
>
driver)
,
struct
platform_driver,
driver)
;
host_pdev
=
to_platform_device(
dev)
;
/* Update a_vbus_vld state as
a_vbus_vld int is disabled
* in device mode
*/
fsm-
>
a_vbus_vld =
(
le32_to_cpu(
usb_dr_regs-
>
otgsc)
&
OTGSC_STS_A_VBUS_VALID)
?
1 :
0;
if
(
on)
{
/* start fsl usb host controller */
if
(
otg_dev-
>
host_working)
goto
end;
else
{
otg_reset_controller(
)
;
VDBG(
"host on....../n"
)
;
if
(
host_pdrv-
>
resume)
{
retval
=
host_pdrv-
>
resume(
host_pdev)
;
if
(
fsm-
>
id)
{
/* default-b */
fsl_otg_drv_vbus(
1)
;
/* Workaround: b_host can't driver
* vbus, but PP in PORTSC needs to
* be 1 for
host to work.
* So we set drv_vbus bit in
* transceiver to 0 thru ULPI. */
#
if
defined(
CONFIG_ISP1504_MXC)
write_ulpi(
0x0c,
0x20)
;
#
endif
}
}
otg_dev-
>
host_working =
1;
}
}
else
{
/* stop fsl usb host controller */
if
(
!
otg_dev-
>
host_working)
goto
end;
else
{
VDBG(
"host off....../n"
)
;
if
(
host_pdrv-
>
suspend)
{
retval
=
host_pdrv-
>
suspend(
host_pdev,
otg_suspend_state)
;
if
(
fsm-
>
id)
/* default-b */
fsl_otg_drv_vbus(
0)
;
}
otg_dev-
>
host_working =
0;
}
}
end:
return
retval;
}
可以看到最后设备是使用还是停止调用的函数
分别是
host_pdrv
-
>
suspend
host_pdrv
-
>
resume
而上面两个指针的函数赋值是在主设备驱动中完成的。
int
fsl_otg_start_gadget(
struct
otg_fsm *
fsm,
int
on)
{
struct
otg_transceiver *
xceiv =
fsm-
>
transceiver;
struct
device *
dev;
struct
platform_driver *
gadget_pdrv;
struct
platform_device *
gadget_pdev;
/*判断是否有从设备驱动注册*/
if
(
!
xceiv-
>
gadget |
|
!
xceiv-
>
gadget-
>
dev.
parent)
return
-
ENODEV;
VDBG(
"gadget %s /n"
,
on ?
"on"
:
"off"
)
;
dev =
xceiv-
>
gadget-
>
dev.
parent;
/*找到从设备驱动的platform_driver结构首地址,为
下面调用其提供的功能函数做准备
*/
gadget_pdrv =
container_of(
(
dev-
>
driver)
,
struct
platform_driver,
driver)
;
gadget_pdev =
to_platform_device(
dev)
;
if
(
on)
gadget_pdrv-
>
resume(
gadget_pdev)
;
else
gadget_pdrv-
>
suspend(
gadget_pdev,
otg_suspend_state)
;
return
0;
}
和上面主设备一样
到底是从设备停止还是恢复是调用
gadget_pdrv
-
>
resume
(
gadget_pdev
)
;
gadget_pdrv
-
>
suspend
(
gadget_pdev
,
otg_suspend_state
)
;
上面两个函数的指针就是在从设备驱动注册时链接的。
上面部分就是 OTG功能的 OTG驱动部分。 OTG功能还要有做主设备使用的主设备驱动和做从设备的从设备驱动。
从上面代码分析我们归纳出流程:
分两个大部分:
一 设备的注册 其中包括
1.
定义platform_device结构。
2.
定义platform_device下的struct
resource设备资源结构
3.
定义
platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4.
调用platform_device_register将platform_device结构
二 OTG驱动的注册 其中包括
1.struct
platform_driver fsl_otg_driver 结构的注册
2.匹配到有设备存在时调用的PORE函数,对设备进行初始化设置和功能函数的绑定
3.完成中断函数的绑定和中断例程的注册。
经过上面的处理后,只要OTG ID的变化就会触发中断,调用中断例程函数,决定是调用主设备还是从设备驱动。
而主设备和从设备驱动和OTG调用的链接是分别在主从设备驱动中完成的。后面我们介绍主从设备驱动中会介绍到。
在文章的最后想起来这次调OTG遇见的问题,分享给大家希望大家有帮助。我调试OTG时,开始将OTG编译到内核中。(Y)。结果插入U盘没有反
应。后来发现原来我加入内核后,主设备驱动的先OTG设备驱动被执行,造成主设备函数和OTG功能的链接出现问题。(应该是OTG先初始化
然后从和主设备驱动链接。)后来我使用模块方式编译OTG功能。按照先载入OTG后载入从和主设备。(insmod方式),结果OTG就可以使用了。
后来通过降低主设备的优先级方式,把OTG编译进内核,然后因为主设备优先级低所以最后被调用。 也就是在主设备注册那使用
late_initcall(ehci_hcd_init);
代替//module_init(ehci_hcd_init);。这样主设备的优先级就低于设备驱动的优先级就在驱动加载完加载了。
但是总感觉这样不是很合理的方式,如果有朋友有更好的办法请指教。