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两个函数。