📄 net2280.c
字号:
/* basic endpoint init */
for (tmp = 0; tmp < 7; tmp++) {
struct net2280_ep *ep = &dev->ep [tmp];
ep->ep.name = ep_name [tmp];
ep->dev = dev;
ep->num = tmp;
if (tmp > 0 && tmp <= 4) {
ep->fifo_size = 1024;
if (init_dma)
ep->dma = &dev->dma [tmp - 1];
} else
ep->fifo_size = 64;
ep->regs = &dev->epregs [tmp];
ep_reset (dev->regs, ep);
}
dev->ep [0].ep.maxpacket = 64;
dev->ep [5].ep.maxpacket = 64;
dev->ep [6].ep.maxpacket = 64;
dev->gadget.ep0 = &dev->ep [0].ep;
dev->ep [0].stopped = 0;
INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
/* we want to prevent lowlevel/insecure access from the USB host,
* but erratum 0119 means this enable bit is ignored
*/
for (tmp = 0; tmp < 5; tmp++)
writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg);
}
static void ep0_start (struct net2280 *dev)
{
writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE)
| (1 << CLEAR_NAK_OUT_PACKETS)
| (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
, &dev->epregs [0].ep_rsp);
/*
* hardware optionally handles a bunch of standard requests
* that the API hides from drivers anyway. have it do so.
* endpoint status/features are handled in software, to
* help pass tests for some dubious behavior.
*/
writel ( (1 << SET_TEST_MODE)
| (1 << SET_ADDRESS)
| (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP)
| (1 << GET_DEVICE_STATUS)
| (1 << GET_INTERFACE_STATUS)
, &dev->usb->stdrsp);
writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE)
| (1 << SELF_POWERED_USB_DEVICE)
| (1 << REMOTE_WAKEUP_SUPPORT)
| (dev->softconnect << USB_DETECT_ENABLE)
| (1 << SELF_POWERED_STATUS)
, &dev->usb->usbctl);
/* enable irqs so we can see ep0 and general operation */
writel ( (1 << SETUP_PACKET_INTERRUPT_ENABLE)
| (1 << ENDPOINT_0_INTERRUPT_ENABLE)
, &dev->regs->pciirqenb0);
writel ( (1 << PCI_INTERRUPT_ENABLE)
| (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE)
| (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE)
| (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE)
| (1 << VBUS_INTERRUPT_ENABLE)
| (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE)
| (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE)
, &dev->regs->pciirqenb1);
/* don't leave any writes posted */
(void) readl (&dev->usb->usbctl);
}
/* when a driver is successfully registered, it will receive
* control requests including set_configuration(), which enables
* non-control requests. then usb traffic follows until a
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
int usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
struct net2280 *dev = the_controller;
int retval;
unsigned i;
/* insist on high speed support from the driver, since
* (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
* "must not be used in normal operation"
*/
if (!driver
|| driver->speed != USB_SPEED_HIGH
|| !driver->bind
|| !driver->unbind
|| !driver->setup)
return -EINVAL;
if (!dev)
return -ENODEV;
if (dev->driver)
return -EBUSY;
for (i = 0; i < 7; i++)
dev->ep [i].irqs = 0;
/* hook up the driver ... */
dev->softconnect = 1;
driver->driver.bus = NULL;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
retval = driver->bind (&dev->gadget);
if (retval) {
DEBUG (dev, "bind to driver %s --> %d\n",
driver->driver.name, retval);
dev->driver = NULL;
dev->gadget.dev.driver = NULL;
return retval;
}
device_create_file (&dev->pdev->dev, &dev_attr_function);
device_create_file (&dev->pdev->dev, &dev_attr_queues);
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
*/
net2280_led_active (dev, 1);
ep0_start (dev);
DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n",
driver->driver.name,
readl (&dev->usb->usbctl),
readl (&dev->usb->stdrsp));
/* pci writes may still be posted */
return 0;
}
EXPORT_SYMBOL (usb_gadget_register_driver);
static void
stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
{
int i;
/* don't disconnect if it's not connected */
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
driver = NULL;
/* stop hardware; prevent new request submissions;
* and kill any outstanding requests.
*/
usb_reset (dev);
for (i = 0; i < 7; i++)
nuke (&dev->ep [i]);
/* report disconnect; the driver is already quiesced */
if (driver) {
spin_unlock (&dev->lock);
driver->disconnect (&dev->gadget);
spin_lock (&dev->lock);
}
usb_reinit (dev);
}
int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
{
struct net2280 *dev = the_controller;
unsigned long flags;
if (!dev)
return -ENODEV;
if (!driver || driver != dev->driver)
return -EINVAL;
spin_lock_irqsave (&dev->lock, flags);
stop_activity (dev, driver);
spin_unlock_irqrestore (&dev->lock, flags);
net2280_pullup (&dev->gadget, 0);
driver->unbind (&dev->gadget);
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
net2280_led_active (dev, 0);
device_remove_file (&dev->pdev->dev, &dev_attr_function);
device_remove_file (&dev->pdev->dev, &dev_attr_queues);
DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
return 0;
}
EXPORT_SYMBOL (usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
* also works for dma-capable endpoints, in pio mode or just
* to manually advance the queue after short OUT transfers.
*/
static void handle_ep_small (struct net2280_ep *ep)
{
struct net2280_request *req;
u32 t;
/* 0 error, 1 mid-data, 2 done */
int mode = 1;
if (!list_empty (&ep->queue))
req = list_entry (ep->queue.next,
struct net2280_request, queue);
else
req = NULL;
/* ack all, and handle what we care about */
t = readl (&ep->regs->ep_stat);
ep->irqs++;
#if 0
VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n",
ep->ep.name, t, req ? &req->req : 0);
#endif
writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat);
/* for ep0, monitor token irqs to catch data stage length errors
* and to synchronize on status.
*
* also, to defer reporting of protocol stalls ... here's where
* data or status first appears, handling stalls here should never
* cause trouble on the host side..
*
* control requests could be slightly faster without token synch for
* status, but status can jam up that way.
*/
if (unlikely (ep->num == 0)) {
if (ep->is_in) {
/* status; stop NAKing */
if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) {
if (ep->dev->protocol_stall) {
ep->stopped = 1;
set_halt (ep);
}
if (!req)
allow_status (ep);
mode = 2;
/* reply to extra IN data tokens with a zlp */
} else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
if (ep->dev->protocol_stall) {
ep->stopped = 1;
set_halt (ep);
mode = 2;
} else if (!req && ep->stopped)
write_fifo (ep, NULL);
}
} else {
/* status; stop NAKing */
if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
if (ep->dev->protocol_stall) {
ep->stopped = 1;
set_halt (ep);
}
mode = 2;
/* an extra OUT token is an error */
} else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT))
&& req
&& req->req.actual == req->req.length)
|| !req) {
ep->dev->protocol_stall = 1;
set_halt (ep);
ep->stopped = 1;
if (req)
done (ep, req, -EOVERFLOW);
req = NULL;
}
}
}
if (unlikely (!req))
return;
/* manual DMA queue advance after short OUT */
if (likely (ep->dma != 0)) {
if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
u32 count;
int stopped = ep->stopped;
/* TRANSFERRED works around OUT_DONE erratum 0112.
* we expect (N <= maxpacket) bytes; host wrote M.
* iff (M < N) we won't ever see a DMA interrupt.
*/
ep->stopped = 1;
for (count = 0; ; t = readl (&ep->regs->ep_stat)) {
/* any preceding dma transfers must finish.
* dma handles (M >= N), may empty the queue
*/
scan_dma_completions (ep);
if (unlikely (list_empty (&ep->queue)
|| ep->out_overflow)) {
req = NULL;
break;
}
req = list_entry (ep->queue.next,
struct net2280_request, queue);
/* here either (M < N), a "real" short rx;
* or (M == N) and the queue didn't empty
*/
if (likely (t & (1 << FIFO_EMPTY))) {
count = readl (&ep->dma->dmacount);
count &= DMA_BYTE_COUNT_MASK;
if (readl (&ep->dma->dmadesc)
!= req->td_dma)
req = NULL;
break;
}
udelay(1);
}
/* stop DMA, leave ep NAKing */
writel ((1 << DMA_ABORT), &ep->dma->dmastat);
spin_stop_dma (ep->dma);
if (likely (req)) {
req->td->dmacount = 0;
t = readl (&ep->regs->ep_avail);
dma_done (ep, req, count, t);
}
/* also flush to prevent erratum 0106 trouble */
if (unlikely (ep->out_overflow
|| (ep->dev->chiprev == 0x0100
&& ep->dev->gadget.speed
== USB_SPEED_FULL))) {
out_flush (ep);
ep->out_overflow = 0;
}
/* (re)start dma if needed, stop NAKing */
ep->stopped = stopped;
if (!list_empty (&ep->queue))
restart_dma (ep);
} else
DEBUG (ep->dev, "%s dma ep_stat %08x ??\n",
ep->ep.name, t);
return;
/* data packet(s) received (in the fifo, OUT) */
} else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) {
if (read_fifo (ep, req) && ep->num != 0)
mode = 2;
/* data packet(s) transmitted (IN) */
} else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) {
unsigned len;
len = req->req.length - req->req.actual;
if (len > ep->ep.maxpacket)
len = ep->ep.maxpacket;
req->req.actual += len;
/* if we wrote it all, we're usually done */
if (req->req.actual == req->req.length) {
if (ep->num == 0) {
/* wait for control status */
if (mode != 2)
req = NULL;
} else if (!req->req.zero || len != ep->ep.maxpacket)
mode = 2;
}
/* there was nothing to do ... */
} else if (mode == 1)
return;
/* done */
if (mode == 2) {
/* stream endpoints often resubmit/unlink in completion */
done (ep, req, 0);
/* maybe advance queue to next request */
if (ep->num == 0) {
/* NOTE: net2280 could let gadget driver start the
* status stage later. since not all controllers let
* them control that, the api doesn't (yet) allow it.
*/
if (!ep->stopped)
allow_status (ep);
req = NULL;
} else {
if (!list_empty (&ep->queue) && !ep->stopped)
req = list_entry (ep->queue.next,
struct net2280_request, queue);
else
req = NULL;
if (req && !ep->is_in)
stop_out_naking (ep);
}
}
/* is there a buffer for the next packet?
* for best streaming performance, make sure there is one.
*/
if (req && !ep->stopped) {
/* load IN fifo with next packet (may be zlp) */
if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
write_fifo (ep, &req->req);
}
}
static struct net2280_ep *
get_ep_by_addr (struct net2280 *dev, u16 wIndex)
{
struct net2280_ep *ep;
if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
return &dev->ep [0];
list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) {
u8 bEndpointAddress;
if (!ep->desc)
continue;
bEndpointAddress = ep->desc->bEndpointAddress;
if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
continue;
if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
return ep;
}
return NULL;
}
static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
{
struct net2280_ep *ep;
u32 num, scratch;
/* most of these don't need individual acks */
stat &= ~(1 << INTA_ASSERTED);
if (!stat)
return;
// DEBUG (dev, "irqstat0 %04x\n", stat);
/* starting a control request? */
if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) {
union {
u32 raw [2];
struct usb_ctrlrequest r;
} u;
int tmp = 0;
struct net2280_request *req;
if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED))
dev->gadget.speed = USB_SPEED_HIGH;
else
dev->gadget.speed = USB_SPEED_FULL;
net2280_led_speed (dev, dev->gadget.speed);
DEBUG (dev, "%s speed\n",
(dev->gadget.speed == USB_SPEED_HIGH)
? "high" : "full");
}
ep = &dev->ep [0];
ep->irqs++;
/* make sure any leftover request state is cleared */
stat &= ~(1 << ENDPOINT_0_INTERRUPT);
while (!list_empty (&ep->queue)) {
req = list_entry (ep->queue.next,
struct net2280_request, queue);
done (ep, req, (req->req.actual == req->req.length)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -