分类: LINUX
2009-03-13 14:49:54
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;
};
/* 使用了5+1个端点 */
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数据