Chinaunix首页 | 论坛 | 博客
  • 博客访问: 646970
  • 博文数量: 156
  • 博客积分: 4833
  • 博客等级: 上校
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-21 19:36
文章分类

全部博文(156)

文章存档

2016年(2)

2013年(1)

2012年(13)

2011年(30)

2010年(46)

2009年(29)

2008年(23)

2007年(12)

分类: LINUX

2009-03-13 14:49:54

at91udc.c ->USB Peripheral Controller Drivers

implement the gadget API, and are the only layers that talk directly to hardware. Different controller hardware will need different drivers, which may also need board-specific customization. These provide a software "gadget" device, visible in sysfs. You can think of that device as being the virtual hardware to which the higher level drivers are written. Other operating systems use other names for this component, perhaps labeling the API plus its libraries as a "Platform Independent Layer" and its implementation as a "Platform Specific Layer".


//每一个endpoint 对应一个该结构

struct at91_ep {

struct usb_ep ep;

struct list_head queue;

struct at91_udc *udc;

void __iomem *creg;


unsigned maxpacket:16;

u8 int_mask;

unsigned is_pingpong:1;


unsigned stopped:1;

unsigned is_in:1;

unsigned is_iso:1;

unsigned fifo_bank:1;


const struct usb_endpoint_descriptor

*desc;

};



struct at91_udc {

struct usb_gadget gadget;

struct at91_ep ep[NUM_ENDPOINTS];

struct usb_gadget_driver *driver;

unsigned vbus:1;

unsigned enabled:1;

unsigned clocked:1;

unsigned suspended:1;

unsigned req_pending:1;

unsigned wait_for_addr_ack:1;

unsigned wait_for_config_ack:1;

unsigned selfpowered:1;

u8 addr;

struct at91_udc_data board;

struct clk *iclk, *fclk;gadget

struct platform_device *pdev;

struct proc_dir_entry *pde;

int udp_irq;

};



/* 使用了51个端点 */


static struct at91_udc controller = {

.gadget = {

.ops = &at91_udc_ops,

.ep0 = &controller.ep[0].ep,

.name = driver_name,

.dev = {

.bus_id = "gadget",

.release = nop_release,

}

},

.ep[0] = {

.ep = {

.name = ep0name,

.ops = &at91_ep_ops,

},

.udc = &controller,

.maxpacket = 8,

.creg = (void __iomem *)(AT91_VA_BASE_UDP

+ AT91_UDP_CSR(0)),

.int_mask = 1 << 0,

},

.ep[1] = {

.ep = {

.name = "ep1",

.ops = &at91_ep_ops,

},

.udc = &controller,

.is_pingpong = 1,

.maxpacket = 64,

.creg = (void __iomem *)(AT91_VA_BASE_UDP

+ AT91_UDP_CSR(1)),

.int_mask = 1 << 1,

},

.ep[2] = {

.ep = {

.name = "ep2",

.ops = &at91_ep_ops,

},

.udc = &controller,

.is_pingpong = 1,

.maxpacket = 64,

.creg = (void __iomem *)(AT91_VA_BASE_UDP

+ AT91_UDP_CSR(2)),

.int_mask = 1 << 2,

},

.ep[3] = {

.ep = {

/* could actually do bulk too */

.name = "ep3-int",

.ops = &at91_ep_ops,

},

.udc = &controller,

.maxpacket = 8,

.creg = (void __iomem *)(AT91_VA_BASE_UDP

+ AT91_UDP_CSR(3)),

.int_mask = 1 << 3,

},

.ep[4] = {

.ep = {

.name = "ep4",

.ops = &at91_ep_ops,

},

.udc = &controller,

.is_pingpong = 1,

.maxpacket = 256,

.creg = (void __iomem *)(AT91_VA_BASE_UDP

+ AT91_UDP_CSR(4)),

.int_mask = 1 << 4,

},

.ep[5] = {

.ep = {

.name = "ep5",

.ops = &at91_ep_ops,

},

.udc = &controller,

.is_pingpong = 1,

.maxpacket = 256,

.creg = (void __iomem *)(AT91_VA_BASE_UDP

+ AT91_UDP_CSR(5)),

.int_mask = 1 << 5,

},

};



1)注册驱动->at91udc_probe


at91udc_probe()

{

. . . .

udc = &controller;

udc->gadget.dev.parent = dev;

//in fact the data is /arch/arm/mach-at91rm9200/ static struct at91_udc_data udc_data;

udc->board = *(struct at91_udc_data *) dev->platform_data;

udc->pdev = pdev;

//初始化每个端点

udc_reinit(udc);

udc->enabled = 0;


. . .

}


udc_reinit()

{

INIT_LIST_HEAD(&udc->gadget.ep_list);

INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);


for (i = 0; i < NUM_ENDPOINTS; i++) {

struct at91_ep *ep = &udc->ep[i];


if (i != 0)//添加端点到gadget列表中

list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);

//清空描述符

ep->desc = NULL;

ep->stopped = 0;

ep->fifo_bank = 0;

ep->ep.maxpacket = ep->maxpacket;

// initialiser une queue par endpoint

//每一个端点一个队列 usb队列

INIT_LIST_HEAD(&ep->queue);

}

}





static irqreturn_t at91_udc_irq (int irq, void *_udc, struct pt_regs *r)

{

struct at91_udc *udc = _udc;

u32 rescans = 5;


while (rescans--) {

u32 status;


status = at91_udp_read(AT91_UDP_ISR)

& at91_udp_read(AT91_UDP_IMR);

if (!status)

break;


/* USB reset irq: not maskable */

if (status & AT91_UDP_ENDBUSRES) {

at91_udp_write(AT91_UDP_IDR, ~MINIMUS_INTERRUPTUS);

at91_udp_write(AT91_UDP_IER, MINIMUS_INTERRUPTUS);

/* Atmel code clears this irq twice */

at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES);

at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES);

VDBG("end bus reset\n");

udc->addr = 0;

stop_activity(udc);


/* enable ep0 */

at91_udp_write(AT91_UDP_CSR(0),

AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);

 udc->gadget.speed = USB_SPEED_FULL;

udc->suspended = 0;

at91_udp_write(AT91_UDP_IER, AT91_UDP_EP(0));


/*

* NOTE: this driver keeps clocks off unless the

* USB host is present. That saves power, but for

* boards that don't support VBUS detection, both

* clocks need to be active most of the time.

*/


/* host initiated suspend (3+ms bus idle) */

} else if (status & AT91_UDP_RXSUSP) {

at91_udp_write(AT91_UDP_IDR, AT91_UDP_RXSUSP);

at91_udp_write(AT91_UDP_IER, AT91_UDP_RXRSM);

at91_udp_write(AT91_UDP_ICR, AT91_UDP_RXSUSP);

// VDBG("bus suspend\n");

if (udc->suspended)

continue;

udc->suspended = 1;


/*

* NOTE: when suspending a VBUS-powered device, the

* gadget driver should switch into slow clock mode

* and then into standby to avoid drawing more than

* 500uA power (2500uA for some high-power configs).

*/

if (udc->driver && udc->driver->suspend)

udc->driver->suspend(&udc->gadget);


/* host initiated resume */

} else if (status & AT91_UDP_RXRSM) {

at91_udp_write(AT91_UDP_IDR, AT91_UDP_RXRSM);

at91_udp_write(AT91_UDP_IER, AT91_UDP_RXSUSP);

at91_udp_write(AT91_UDP_ICR, AT91_UDP_RXRSM);

// VDBG("bus resume\n");

if (!udc->suspended)

continue;

udc->suspended = 0;


/*

* NOTE: for a VBUS-powered device, the gadget driver

* would normally want to switch out of slow clock

* mode into normal mode.

*/

if (udc->driver && udc->driver->resume)

udc->driver->resume(&udc->gadget);


/* endpoint IRQs are cleared by handling them */

} else {

int i;

unsigned mask = 1;

struct at91_ep *ep = &udc->ep[1];


//端点0特殊处理

if (status & mask)

handle_ep0(udc);

//其它端点处理

for (i = 1; i < NUM_ENDPOINTS; i++) {

mask <<= 1;

if (status & mask)

handle_ep(ep);

ep++;

}

}

}


return IRQ_HANDLED;

}




static int handle_ep(struct at91_ep *ep)

{

struct at91_request *req;

u32 __iomem *creg = ep->creg;

u32 csr = __raw_readl(creg);

//判断该endpoint是否有悬挂的需求

if (!list_empty(&ep->queue))

req = list_entry(ep->queue.next,

struct at91_request, queue);

else

req = NULL;

//IN请求

if (ep->is_in) {

if (csr & (AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)) {

csr |= CLR_FX;

csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP);

__raw_writel(csr, creg);

}

if (req)

return write_fifo(ep, req);


} else { //OUT请求

if (csr & AT91_UDP_STALLSENT) {

/* STALLSENT bit == ISOERR */

if (ep->is_iso && req)

req->req.status = -EILSEQ;

csr |= CLR_FX;

csr &= ~(SET_FX | AT91_UDP_STALLSENT);

__raw_writel(csr, creg);

csr = __raw_readl(creg);

}

if (req && (csr & RX_DATA_READY))

return read_fifo(ep, req);

}

return 0;

}



usb_gadget_register_driver


/usb/配置

interrupt(at91_udc_irq)

-->handle_ep0

-->handle_setup

-->udc->driver->setup(&udc->gadget, &pkt.r);

(usb_gadget_register_driver提供,填充发送队列)

// endpoint 操作,每一个端点对应一个,结构相同。

static struct usb_ep_ops at91_ep_ops = {

.enable = at91_ep_enable,

.disable = at91_ep_disable,

.alloc_request = at91_ep_alloc_request,

.free_request = at91_ep_free_request,

.alloc_buffer = at91_ep_alloc_buffer,

.free_buffer = at91_ep_free_buffer,

.queue = at91_ep_queue,

.dequeue = at91_ep_dequeue,

.set_halt = at91_ep_set_halt,

// there's only imprecise fifo status reporting

};


################################################

该操作由usb gadget抽象层封装后调用

usb_ep_alloc_request-->alloc_request

usb_ep_enable--> at91_ep_enable



gadget驱动的bind函数通过 usb_ep_autoconfig 获取可用的端口(端口命名有规范)

-->ep_matches

/*

* - ep1, ep2, ... address is fixed, not direction or type

* - ep1in, ep2out, ... address and direction are fixed, not type

* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction

* - ep1in-bulk, ep2out-iso, ... all three are fixed

* - ep-* ... no functionality restrictions

*

*/



USB Peripheral Controller Drivers提供可用的端点,中断驱动,底层函数供gadget调用,两者通过 ep->queue通讯USB数据


阅读(1097) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~