?? sa1100.c
字號:
dbg_rx (1, "urb: %p urb->buffer: %p actual_length: %d rcv_packetSize: %d", urb, urb->buffer, urb->actual_length, urb->endpoint->rcv_packetSize); if (!(dma_rx_curpos = pci_map_single (NULL, (void *) urb->buffer + urb->actual_length, urb->endpoint->rcv_packetSize, PCI_DMA_FROMDEVICE))) { restore_flags (flags); dbg_rx (0, "pci_map_single failed"); return; } SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok); // enable DMA only if packet to big for FIFO (twenty bytes) if (ep1_endpoint->rcv_packetSize > 20) { dma_rx_active = _sa1100_bi_dma_queue_buffer_irq (dmachn_rx, dma_rx_curpos, urb->endpoint->rcv_packetSize); _sa1100_bi_dma_run (dmachn_rx, dma_rx_active); } restore_flags (flags); }}/* ep1 public functions ************************************************************************ *//** * ep1_init - initialize the endpoint * */void ep1_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint, int chn){ if (endpoint) { int ok; ep1_device = device; ep1_endpoint = endpoint; dmachn_rx = chn; dbg_rx (1, "ep1_endpoint %p", ep1_endpoint); //usbd_fill_rcv(device, endpoint, 5); SET_AND_TEST (*(UDCOMP) = ep1_endpoint->rcv_packetSize - 1, udc (UDCOMP) != ep1_endpoint->rcv_packetSize - 1, ok); if (!ok) { dbg_rx (0, "FAILED setting pktsize %d", *(UDCOMP)); } // setup the dma engine operating parameters dma_is_active = 0; dma_enabled = 1; if (!ep1_endpoint->rcv_urb) { ep1_endpoint->rcv_urb = first_urb_detached (&ep1_endpoint->rdy); dbg_rx (1, "SETTING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb); } else { dbg_rx (1, "CHECKING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb); } ep1_start_dma (ep1_endpoint->rcv_urb); }}/** * ep1_reset - reset the endpoint * * Return non-zero if error. */void ep1_reset (void){ int ok; dbg_rx (1, "CHECKING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb); ep1_stop_dma (ep1_endpoint->rcv_urb); if (ep1_endpoint->rcv_urb) { ep1_endpoint->rcv_urb->actual_length = 0; } ep1_start_dma (ep1_endpoint->rcv_urb); udc_reset_ep (1); SET_AND_TEST (*(UDCOMP) = ep1_endpoint->rcv_packetSize - 1, udc (UDCOMP) != ep1_endpoint->rcv_packetSize - 1, ok); if (!ok) { dbg_rx (0, "FAILED setting pktsize %d", *(UDCOMP)); }}/** * ep1_disable - disable endpoint for use * */void ep1_disable (){ dbg_rx (1, "dma_is_active: %d dma_enabled: %d", dma_is_active, dma_enabled); if (dma_is_active) { ep1_stop_dma (ep1_endpoint->rcv_urb); } if (!dma_enabled) { dbg_rx (1, "!dma_enabled: %x %x %x", dmachn_rx, dma_enabled, dma_is_active); return; } { unsigned long flags; save_flags_cli (flags); if (dma_is_active) { restore_flags (flags); dbg_rx (1, "dma_is_active: %x %x %x", dmachn_rx, dma_enabled, dma_is_active); ep1_stop_dma (ep1_endpoint->rcv_urb); usbd_dealloc_urb (ep1_endpoint->rcv_urb); dbg_rx(1, "CLEARING rcv_urb %p", ep1_endpoint->rcv_urb); ep1_endpoint->rcv_urb = NULL; } dma_enabled = 0; restore_flags (flags); } usbd_flush_rcv (ep1_endpoint); ep1_endpoint = NULL;}/* * Endpoint 1 interrupts will be directed here. * * status : the contents of the UDC Status/Interrupt Register. */void ep1_int_hndlr (unsigned int status){ dma_addr_t dma_address; unsigned int udccs1; int ok; unsigned int len = 0; unsigned int urb_bad = 0; rx_interrupts++; if (!((udccs1 = *(UDCCS1)) & UDCCS1_RPC)) { SET_AND_TEST (*(UDCCS1) = UDCCS1_SST, _udc (UDCCS1) & UDCCS1_SST, ok); SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok); return; } //dbg_rx(4, "udccs1: %02x", udccs1); dma_is_active = 0; // DMA or non-DMA // XXX - we should be able to collapse these together if (ep1_endpoint->rcv_packetSize <= 20) { // non-DMA version if (!(udccs1 & (UDCCS1_RPE))) { // get residual data from fifo if (ep1_endpoint->rcv_urb) { unsigned char *dmabuf; dmabuf = ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length; // XXX Note that it is apparantly impossible to do this with 100% // reliability. Only copy data in if/while length is less than packet size for (; (len < (unsigned) ep1_endpoint->rcv_packetSize) && *(UDCCS1) & UDCCS1_RNE; len++) { *dmabuf++ = *(UDCDR); //udelay(1); } // record an error if the FIFO is not empty udc_ep1_errors += ((urb_bad = *(UDCCS1) & UDCCS1_RNE)) ? 1 : 0; } else { dbg_rx (0, "NO RCV URB"); } } else { udc_rpe_errors++; } } // retrieve dma_address, note that this is not reliable else if (!_sa1100_bi_dma_stop_get_current_irq (dmachn_rx, &dma_address, dma_rx_active)) { // DMA version // stop dma engine, if we can get the current address, unmap, get residual data, and give to bottom half pci_unmap_single (NULL, dma_rx_curpos, ep1_endpoint->rcv_packetSize, PCI_DMA_FROMDEVICE); if (!(udccs1 & (UDCCS1_RPE))) { // get residual data from fifo if (ep1_endpoint->rcv_urb) { unsigned char *dmabuf; len = dma_address - dma_rx_curpos; dmabuf = ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length + len; /* WARNING - it is impossible empty the FIFO with 100% accuracy. * * We only copy data in if/while length is less than packet size * * There are two problems. First fetching the dma address can fail. In this case len will * be too large and we do not attempt to read any data. * * Second, reads sometimes fail to update the FIFO read address resulting in too much * data being retrieved. Slightly delaying between each read helps to reduce this problem * at the expense of increasing latency. * * This (and the similiar problem with the TX FIFO) are the reason that it is NOT SAFE * to operate the SA1100 without a CRC across all bulk transfers. The normal USB CRC which * the hardware uses to protect each packet being sent is not sufficent. The data is being * damaged getting it into and out of the FIFO(s). */ for (; (len < (unsigned) ep1_endpoint->rcv_packetSize) && *(UDCCS1) & UDCCS1_RNE; len++) { *dmabuf++ = *(UDCDR); udelay (0); } // record an error if the FIFO is not empty udc_ep1_errors += ((urb_bad = *(UDCCS1) & UDCCS1_RNE)) ? 1 : 0; } else { dbg_rx (0, "NO RCV URB"); } } else { udc_rpe_errors++; } } else { //dbg_rx(0, "nothing to unmap"); } // let the upper levels handle the urb usbd_rcv_complete_irq (ep1_endpoint, len, urb_bad); // clear SST if necessary if (udccs1 & UDCCS1_SST) { SET_AND_TEST (*(UDCCS1) = UDCCS1_SST, _udc (UDCCS1) & UDCCS1_SST, ok); if (!ok) { dbg_rx (0, "could not clear SST: %02x", *(UDCCS1)); } } // start DMA and reset RPC SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok); if (!ok) { dbg_rx (0, "could not clear RPC: %02x", *(UDCCS1)); } if (dma_enabled && ep1_endpoint->rcv_urb) { dma_is_active = 1; // start DMA if packetsize to big for FIFO (twenty bytes) if ((dma_rx_curpos = pci_map_single (NULL, (void *) ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length, ep1_endpoint->rcv_packetSize, PCI_DMA_FROMDEVICE)) && (ep1_endpoint->rcv_packetSize > 20)) { dma_rx_active = _sa1100_bi_dma_queue_buffer_irq (dmachn_rx, dma_rx_curpos, ep1_endpoint->rcv_packetSize); _sa1100_bi_dma_run (dmachn_rx, dma_rx_active); } }}/* ep2 dma engine functions ******************************************************************** */static int dmachn_tx; // tx dma channelstatic int dma_tx_active;dma_addr_t dma_tx_curpos;static struct usb_endpoint_instance *ep2_endpoint;/* * * ep2_reset_dma - * @restart: * * Reset the DMA engine parameters. */static void ep2_reset_dma (void){ unsigned long flags; local_irq_save (flags); // stop current dma _sa1100_bi_dma_flush_all_irq (dmachn_tx); udelay (10); // XXX ep2_endpoint->tx_urb = NULL; if (dma_tx_curpos) { pci_unmap_single (NULL, dma_tx_curpos, ep2_endpoint->last, PCI_DMA_TODEVICE); } dma_tx_curpos = (dma_addr_t) NULL; ep2_endpoint->tx_urb = NULL; ep2_endpoint->sent = 0; usbd_flush_tx (ep2_endpoint); local_irq_restore (flags);}/* ep2 public functions ************************************************************************ *//** * ep2_enable - * * Initialize the dma variables. Called once. * */int ep2_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint, int chn){ dbgENTER (dbgflg_usbdbi_tx, 1); dmachn_tx = chn; ep2_disable (); //ep2_device = device; { unsigned long flags; local_irq_save (flags); ep2_endpoint = endpoint; dma_tx_curpos = 0; ep2_endpoint->sent = 0; ep2_endpoint->tx_urb = NULL; local_irq_restore (flags); } return 0;}/* * Initialise the DMA system to use the given chn for receiving * packets over endpoint 2. * * chn : the dma channel to use. * returns : 0 if ok, -1 if channel couldn't be used. */void ep2_disable (void){ unsigned long flags; dbgENTER (dbgflg_usbdbi_tx, 1); local_irq_save (flags); if (ep2_endpoint) { ep2_reset_dma (); ep2_endpoint = NULL; } local_irq_restore (flags);}/* * reset any txpkts, it will be up to the client driver to free up the send * queue. */void ep2_reset (void){ dbgENTER (dbgflg_usbdbi_tx, 1); ep2_reset_dma (); udc_reset_ep (2);}/* Interrupt service routines ****************************************************************** *//* * * Interrupt handler - called with interrupts disabled. */void ep2_int_hndlr (unsigned int status, int in_interrupt){ unsigned int udccs2; int ok; int restart = 0; udccs2 = *(UDCCS2); // do not do anything if FST is set if (udccs2 & UDCCS2_FST) { dbg_tx (0, "FST set! (%sint)", in_interrupt ? "" : "not-"); udc_ep2_fst++; return; } else if (udccs2 & UDCCS2_SST) { dbg_tx (0, "SST set, stalling! (%sint)", in_interrupt ? "" : "not-"); udc_stall_ep (2); return; } // if in interrupt we have to stop any current activity if (in_interrupt) { tx_interrupts++; if (!((udccs2 = *(UDCCS2)) & UDCCS2_TPC)) { return; } _sa1100_bi_dma_flush_all_irq (dmachn_tx); // if requred reset stall and restart if (udccs2 & (UDCCS2_TPE | UDCCS2_TUR | UDCCS2_SST)) { if (udccs2 & UDCCS2_SST) { SET_AND_TEST (*(UDCCS2) = UDCCS2_SST, _udc (UDCCS2) & UDCCS2_SST, ok); if (!ok) { dbg_tx (0, "Waiting too long to set SST %02x", *(UDCCS2)); } } udc_ep2_errors++; restart = 1; if (udccs2 & UDCCS2_TPE) { udc_ep2_tpe++; } //QQQ was: if (udccs2 & UDCCS2_TUR | UDCCS2_SST) if (udccs2 & UDCCS2_TUR) { udc_ep2_tur++; } if (udccs2 & UDCCS2_SST) { udc_ep2_sst++; } } // if we have an active buffer, umap the buffer and update position if previous send was successful if (dma_tx_curpos) { pci_unmap_single (NULL, dma_tx_curpos, ep2_endpoint->last, PCI_DMA_TODEVICE); dma_tx_curpos = (dma_addr_t) NULL; } } usbd_tx_complete_irq (ep2_endpoint, restart); // start dma if we have something to send if (ep2_endpoint->tx_urb && ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent) > 0)) { int ok1, ok2; //int count; ep2_endpoint->last = MIN (ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent, ep2_endpoint->tx_urb->endpoint->tx_packetSize); if (! (dma_tx_curpos = pci_map_single (NULL, ep2_endpoint->tx_urb->buffer + ep2_endpoint->sent, ep2_endpoint->last, PCI_DMA_TODEVICE))) { dbg_tx (0, "pci_map_single failed"); } /* The order of the following is critical.... * Reseting the TPC clears the FIFO, so the DMA engine cannot be started until after * TPC is set. But this opens a window where the USB UCD can fail because it is * waiting for the DMA engine to fill the FIFO and it receives an IN packet from * the host. The best we can do is to minimize the window. [Note that this effect * is most noticeable on slow, 133Mhz, processors.] * * 1. Set IMP and all DMA registers, but do not start DMA yet * 2. reset TPC * 3. start DMA */ SET_AND_TEST (*(UDCIMP) = ep2_endpoint->last - 1, _udc (UDCIMP) != (ep2_endpoint->last - 1), ok1); if (ep2_endpoint->last > 16) { dma_tx_active = _sa1100_bi_dma_queue_buffer_irq (dmachn_tx, dma_tx_curpos, ep2_endpoint->last); //for (count = 8; count--; *(UDCDR) = '\0'); SET_AND_TEST (*(UDCCS2) = UDCCS2_TPC, _udc (UDCCS2) & UDCCS2_TPC, ok2); _sa1100_bi_dma_run (dmachn_tx, dma_tx_active); if (!ok1 || !ok2) { dbg_tx (0, "Waiting too long to set UDCIMP %02x or TPC: %02x", *(UDCIMP), *(UDCCS2)); } dbg_tx (4, "queued %d bytes using DMA", ep2_endpoint->last); } else { // Entire packet is small enough to fit in FIFO, stuff it // in directly and forget DMA. unsigned char *dmabuf = (unsigned char *) dma_tx_curpos; int count = ep2_endpoint->last; SET_AND_TEST (*(UDCCS2) = UDCCS2_TPC, _udc (UDCCS2) & UDCCS2_TPC, ok2); for (; count--; *(UDCDR) = *dmabuf++); dbg_tx (4, "queued %d bytes directly to FIFO", ep2_endpoint->last); } }}/* Clock Tick Debug support ****************************************************************** */#define RETRYTIME 10#if defined(CONFIG_USBD_STALL_TIMEOUT) // If an URB waits more than...#define USBD_STALL_TIMEOUT_SECONDS CONFIG_USBD_STALL_TIMEOUT#else#define USBD_STALL_TIMEOUT_SECONDS 0 // Stall watchdog hobbled#endif#if defined(CONFIG_USBD_STALL_DISCONNECT_DURATION)#define USBD_STALL_DISCONNECT_DURATION CONFIG_USBD_STALL_DISCONNECT_DURATION#else#define USBD_STALL_DISCONNECT_DURATION 2 // seconds#endifchar hexdigit (int hex){ hex &= 0xf; return (hex < 0xa) ? ('0' + hex) : ('a' + hex - 0xa);}int hex2buf (char *buf, char *str, int num){ char *cp = buf; while (*str) { *cp++ = *str++; } *cp++ = hexdigit (num >> 4); *cp++ = hexdigit (num & 0xf); *cp++ = ' '; return (cp - buf);}extern int send_length;extern int send_todo;extern int sent_last;static void show_info (void){ char buf[100]; char *cp; volatile short int gpdr;/* if (udc_interrupts_last == udc_interrupts) { udc_irq_enable(); }*/ if (udc_interrupts != udc_interrupts_last) { printk (KERN_DEBUG "--------------\n"); } // do some work memset (buf, 0, sizeof (buf)); cp = buf; cp += hex2buf (cp, "CCR:", *(UDCCR)); cp += hex2buf (cp, "CSR:", *(UDCSR)); cp += hex2buf (cp, "CAR:", *(UDCAR)); cp += hex2buf (cp, "CS0:", *(UDCCS0)); cp += hex2buf (cp, "CS1:", *(UDCCS1)); cp += hex2buf (cp, "CS2:", *(UDCCS2)); cp += hex2buf (cp, "IMP:", *(UDCIMP)); cp += hex2buf (cp, "OMP:", *(UDCOMP)); cp += hex2buf (cp, "WC:", *(UDCWC)); cp += hex2buf (cp, "SR:", *(UDCSR)); gpdr = GPDR; printk (KERN_DEBUG "[%u] %s int:%2d ep0:%d rx:%d tx:%d sus:%d res:%d er:%d re:%d e1:%d e2:%d tpe:%d tur:%d sst:%d fst:%d tck:%d fix:%d\n", udc_interrupts, buf, udc_interrupts - udc_interrupts_last, ep0_interrupts - ep0_interrupts_last, rx_interrupts - rx_interrupts_last, tx_interrupts - tx_interrupts_last, sus_interrupts, res_interrupts, udc_address_errors, udc_rpe_errors, udc_ep1_errors, udc_ep2_errors, udc_ep2_tpe, udc_ep2_tur, udc_ep2_sst, udc_ep2_fst, udc_ticks, udc_fixed); udc_interrupts_last = udc_interrupts; ep0_interrupts_last = ep0_interrupts; rx_interrupts_last = rx_interrupts; tx_interrupts_last = tx_interrupts;}void udc_regs (void){ if (_udc (UDCAR) != usb_address) { printk (KERN_DEBUG "ADDRESS ERROR DETECTED\n"); udc_address_errors++; } *(UDCAR) = usb_address; show_info ();}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -