2023年12月20日发(作者:)
前一段时间弄了
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_);
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( "/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( "/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驱动的注册 其中包括
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);。这样主设备的优先级
就低于设备驱动的优先级就在驱动加载完加载了。 但是总感觉这样不是很合理的方式,如果有朋友有更好的办法请指教。
上回介绍了OTG功能的 OTG部分驱动,本片分析OTG功能的从设备部分驱动。从设备的注册过程和OTG的一样,首先注册设备。
流程是:
1. 定义platform_device结构。
2. 定义platform_device下的struct resource设备资源结构
3. 定义 platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4. 调用platform_device_register将platform_device结构
注 册上面4个过程调用结束后,设备的信息就被注册到系统中,等待驱动的使用
下面是按照上面顺序贴出代码
定义platform_device结构
static struct platform_device __maybe_unused dr_udc_device = {
. name = "fsl-usb2-udc" ,
. id = - 1,
. dev = {
. release = dr_udc_release,
. dma_mask = & dr_udc_dmamask,
. coherent_dma_mask = 0xffffffff,
} ,
. resource = otg_resources,
. num_resources = ARRAY_SIZE( otg_resources) ,
} ;
我们可以看到resource和OTG的resource一样,使用的都是OTG那 部分资源
定义platform_device下的struct resource设备资源结构
static struct resource otg_resources[ ] = {
[ 0] = {
. start = ( u32) ( USB_OTGREGS_BASE) ,
. end = ( u32) ( USB_OTGREGS_BASE + 0x1ff) ,
. flags = IORESOURCE_MEM,
} ,
[ 1] = {
. start = MXC_INT_USB_OTG,
. flags = IORESOURCE_IRQ,
} ,
} ;
定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
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,
} ;
/*将设备注册 进系统*/
static inline void dr_register_udc( void )
{
PDATA- > operating_mode = DR_UDC_MODE; //在OTG功能设备注册的时候这里的模式是“FSL_USB2_DR_OTG ”, 时还不知道为什么不开始就直接把模式写成这个而是后该,现在知道了 因为主从和OTG都在用一个资源,所以谁用就的把模式该了。
/*#define PDATA (&dr_utmi_config)PDATA指的就是上面的结构 */
dr_udc_device. dev. platform_data = PDATA;
if ( platform_device_register( & dr_udc_device) )
printk( KERN_ERR "usb: can't register DR gadget/n" ) ;
else
printk( KERN_INFO "usb: DR gadget (%s) registered/n" ,
PDATA- > transceiver) ;
}
上面就完成了设备的注册。
下面看看驱动和设备的链接。
static struct platform_driver udc_driver = {
. remove = __exit_p( fsl_udc_remove) ,
/* these suspend and resume are not usb suspend and resume * /
. suspend = fsl_udc_suspend, //切换到主设备后的处理过程
. resume = fsl_udc_resume, //切换到从设备的处理过程
. probe = fsl_udc_probe,
. driver = {
. name = driver_name, //就是fsl-usb2-udc,用于匹配设备的
. owner = THIS_MODULE,
},
}
将驱动注册进系统,如果发现了设备就调用Probe函数,因为我们上面注册了设备所以必然调用Probe
static int __init udc_init( void )
{
printk( KERN_INFO "%s (%s)/n" , driver_desc, DRIVER_VERSION) ;
return platform_driver_register( & udc_driver) ;//驱动注册进系统
}
发现设备后调用PROBE函数
static int __init fsl_udc_probe( struct platform_device * pdev)
{
struct resource * res;
struct fsl_usb2_platform_data * pdata = pdev- > dev.
platform_data;
int ret = - ENODEV;
unsigned int i;
u32 dccparams, portsc;
if ( strcmp ( pdev- > name, driver_name) ) {
VDBG( "Wrong device/n" ) ;
return - ENODEV;
}
/********************************************************/
static struct fsl_udc *udc_controller;
全局变量定义
/*******************************************************/
udc_controller = kzalloc( sizeof ( struct fsl_udc) ,
GFP_KERNEL) ;
if ( udc_controller = = NULL ) {
ERR( "malloc udc failed/n" ) ;
return - ENOMEM;
}
udc_controller- > pdata = pdata; //私有变量赋值
# ifdef CONFIG_USB_OTG
/* Memory and interrupt resources will be passed from OTG */
udc_controller- > transceiver = otg_get_transceiver( ) ;//在OTG功能中已经通过otg_set_transceiver 设置了transceiver结构,这里面就可以GET
if ( ! udc_controller- > transceiver) {
printk( KERN_ERR "Can't find OTG driver!/n" ) ;
ret = - ENODEV;
goto err1a;
}
res = otg_get_resources( ) ; //获得资源
if ( ! res) {
DBG( "resource not registered!/n" ) ;
return - ENODEV;
}
# else
if ( ( pdev- > dev. parent) & &
( to_platform_device( pdev- > dev. parent) - > resource) )
{
pdev- > resource =
to_platform_device( pdev- > dev. parent) - > resource;
pdev- > num_resources =
to_platform_device( pdev- > dev. parent) - >
num_resources;
}
res = platform_get_resource( pdev, IORESOURCE_MEM, 0) ;
if ( ! res) {
ret = - ENXIO;
goto err1a;
}
if ( ! request_mem_region( res- > start, resource_size( res) ,
driver_name) ) {
ERR( "request mem region for %s failed /n" , pdev- >
name) ;
ret = - EBUSY;
goto err1a;
}
# endif
/*将物理地址映射为驱动可以访问的虚拟地址*/
dr_regs = ioremap( res- > start, resource_size( res) ) ;
if ( ! dr_regs) {
ret = - ENOMEM;
goto err1;
}
pdata- > regs = ( void * ) dr_regs; //私有数据接收映射地址
/*
* do platform specific init: check the clock, grab/config
pins, etc.
*/
/*调用私有数据的初始化函数,关于初始化函数下面分析*/
if ( pdata- > platform_init & & pdata- > platform_init( pdev) )
{
ret = - ENODEV;
goto err2a;
}
if ( pdata- > have_sysif_regs)
usb_sys_regs = ( struct usb_sys_interface * )
( ( u32) dr_regs + USB_DR_SYS_OFFSET) ;
/* Read Device Controller Capability Parameters register */
dccparams = fsl_readl( & dr_regs- > dccparams) ;
if ( ! ( dccparams & DCCPARAMS_DC) ) {
ERR( "This SOC doesn't support device role/n" ) ;
ret = - ENODEV;
goto err2;
}
/* Get max device endpoints */
/* DEN is bidirectional ep number, max_ep doubles the number */
udc_controller- > max_ep = ( dccparams & DCCPARAMS_DEN_MASK) *
2;
# ifdef CONFIG_USB_OTG
res+ + ;
udc_controller- > irq = res- > start;
# else
udc_controller- > irq = platform_get_irq( pdev, 0) ;
# endif
if ( ! udc_controller- > irq) {
ret = - ENODEV;
goto err2;
}
/*注册中断,该中断和OTG的中断共享一个*/
ret = request_irq( udc_controller- > irq, fsl_udc_irq,
IRQF_SHARED,
driver_name, udc_controller) ;
if ( ret ! = 0) {
ERR( "cannot request irq %d err %d /n" ,
udc_controller- > irq, ret) ;
goto err2;
}
/* Initialize the udc structure including QH member and other
member */
/*对一些资源进行空间开辟等初始化操作*/
if ( struct_udc_setup( udc_controller, pdev) ) {
ERR( "Can't initialize udc data structure/n" ) ;
ret = - ENOMEM;
goto err3;
}
if ( ! udc_controller- > transceiver) {
/* initialize usb hw reg except for regs for EP,
* leave usbintr reg untouched */
dr_controller_setup( udc_controller) ;
}
/* Setup gadget structure */
udc_controller- > gadget. ops = & fsl_gadget_ops;
udc_controller- > gadget. is_dualspeed = 1;
udc_controller- > gadget. ep0 = & udc_controller- > eps[ 0] .
ep;
INIT_LIST_HEAD( & udc_controller- > gadget. ep_list) ;
udc_controller- > gadget. speed = USB_SPEED_UNKNOWN;
udc_controller- > gadget. name = driver_name;
/* Setup and register with kernel */
dev_set_name( & udc_controller- > gadget. dev, "gadget" ) ;
udc_controller- > gadget. dev. release = fsl_udc_release;
udc_controller- > gadget. dev. parent = & pdev- > dev;
ret = device_register( & udc_controller- > gadget. dev) ;
if ( ret < 0)
goto err3;
if ( udc_controller- > transceiver) {
udc_controller- > gadget. is_otg = 1;
/* now didn't support lpm in OTG mode*/
device_set_wakeup_capable( & pdev- > dev, 0) ;
}
/* setup QH and epctrl for ep0 */
ep0_setup( udc_controller) ;
/* setup udc->eps[] for ep0 */
struct_ep_setup( udc_controller, 0, "ep0" , 0) ;
/* for ep0: the desc defined here;
* for other eps, gadget layer called ep_enable with defined
desc
*/
udc_controller- > eps[ 0] . desc = & fsl_ep0_desc;
udc_controller- > eps[ 0] . ep. maxpacket =
USB_MAX_CTRL_PAYLOAD;
/* setup the udc->eps[] for non-control endpoints and link
* to _list */
for ( i = 1; i < ( int ) ( udc_controller- > max_ep / 2) ; i+
+ ) {
char name[ 14] ;
sprintf ( name, "ep%dout" , i) ;
struct_ep_setup( udc_controller, i * 2, name, 1) ;
sprintf ( name, "ep%din" , i) ;
struct_ep_setup( udc_controller, i * 2 + 1, name, 1) ;
}
/* use dma_pool for TD management */
udc_controller- > td_pool = dma_pool_create( "udc_td" , & pdev- > dev,
sizeof ( struct ep_td_struct) ,
DTD_ALIGNMENT, UDC_DMA_BOUNDARY) ;
if ( udc_controller- > td_pool = = NULL ) {
ret = - ENOMEM;
goto err4;
}
if ( g_iram_size) {
for ( i = 0; i < IRAM_PPH_NTD; i+ + ) {
udc_controller- > iram_buffer[ i] =
USB_IRAM_BASE_ADDR + i * g_iram_size;
udc_controller- > iram_buffer_v[ i] =
IO_ADDRESS( udc_controller- > iram_buffer[ i] ) ;
}
}
# ifdef POSTPONE_FREE_LAST_DTD
last_free_td = NULL ;
# endif
/* disable all INTR */
fsl_writel( 0, & dr_regs- > usbintr) ;
dr_wake_up_enable( udc_controller, false ) ;
udc_controller- > stopped = 1;
portsc = fsl_readl( & dr_regs- > portsc1) ;
portsc | = PORTSCX_PHY_LOW_POWER_SPD;
fsl_writel( portsc, & dr_regs- > portsc1) ;
if ( udc_controller- > pdata- > usb_clock_for_pm)
udc_controller- > pdata- > usb_clock_for_pm( false ) ;
create_proc_file( ) ;
return 0;
err4:
device_unregister( & udc_controller- > gadget. dev) ;
err3:
free_irq( udc_controller- > irq, udc_controller) ;
err2:
if ( pdata- > platform_uninit)
pdata- > platform_uninit( pdata) ;
err2a:
iounmap( ( u8 __iomem * ) dr_regs) ;
err1:
if ( ! udc_controller- > transceiver)
release_mem_region( res- > start, resource_size( res) ) ;
err1a:
kfree( udc_controller) ;
udc_controller = NULL ;
return ret;
}
当设备使用主设备时 DEVICE的处理
static int udc_suspend( struct fsl_udc * udc)
{
u32 mode, usbcmd;
/* open clock for register access */
if ( udc_controller- > pdata- > usb_clock_for_pm)
udc_controller- > pdata- > usb_clock_for_pm( true ) ;
mode = fsl_readl( & dr_regs- > usbmode) &
USB_MODE_CTRL_MODE_MASK;
usbcmd = fsl_readl( & dr_regs- > usbcmd) ;
pr_debug( "%s(): mode 0x%x stopped %d/n" , __func__ , mode,
udc- > stopped) ;
/*
* If the controller is already stopped, then this must be a
* PM suspend. Remember this fact, so that we will leave the
* controller stopped at PM resume time.
*/
if ( udc- > stopped) {
pr_debug( "gadget already stopped, leaving early/n" ) ;
udc- > already_stopped = 1;
goto out;
}
if ( mode ! = USB_MODE_CTRL_MODE_DEVICE) {
pr_debug( "gadget not in device mode, leaving early/n" ) ;
goto out;
}
udc- > stopped = 1;
/* if the suspend is not for switch to host in otg mode */
if ( ( ! ( udc- > gadget. is_otg) ) | |
( fsl_readl( & dr_regs- > otgsc) & OTGSC_STS_USB_ID) )
{
dr_wake_up_enable( udc, true ) ;
dr_phy_low_power_mode( udc, true ) ;
}
/* stop the controller */
usbcmd = fsl_readl( & dr_regs- > usbcmd) & ~ USB_CMD_RUN_STOP;
fsl_writel( usbcmd, & dr_regs- > usbcmd) ;
printk( KERN_INFO "USB Gadget suspended/n" ) ;
out:
if ( udc_controller- > pdata- > usb_clock_for_pm)
udc_controller- > pdata- > usb_clock_for_pm( false ) ;
return 0;
}
当切换到从设备时调用
static int fsl_udc_resume( struct platform_device * pdev)
{
pr_debug( "%s(): stopped %d already_stopped %d/n" , __func__ ,
udc_controller- > stopped, udc_controller- >
already_stopped) ;
/*
* If the controller was stopped at suspend time, then
* don't resume it now.
*/
if ( udc_controller- > already_stopped) {
udc_controller- > already_stopped = 0;
pr_debug( "gadget was already stopped, leaving early/n" ) ;
return 0;
}
/* Enable DR irq reg and set controller Run */
if ( udc_controller- > stopped) {
dr_wake_up_enable( udc_controller, false ) ;
dr_phy_low_power_mode( udc_controller, false ) ;
mdelay( 1) ;
dr_controller_setup( udc_controller) ;
dr_controller_run( udc_controller) ;
}
udc_controller- > usb_state = USB_STATE_ATTACHED;
udc_controller- > ep0_dir = 0;
printk( KERN_INFO "USB Gadget resumed/n" ) ;
return 0;
}
上面的两个函数就是在上一篇OTG 一中介绍的
gadget_pdrv- > resume ( gadget_pdev ) ;
gadget_pdrv - > suspend ( gadget_pdev , otg_suspend_state ) ;
这两处就是只想的这个函数。而把 前面的函数和上面这两个指针链接的地方就是在OTG中的
fsl_otg_start_gadget 函数。
从 上面我们可以看到以下几点:
功能的从设备使用的资源和私有数据与OTG设备的一致,(主设备也是一致)
2. 从设备主要为OTG功能提供fsl_udc_resume和udc_suspend两个函数。


发布评论