www.pudn.com > Linux2410_device.rar > sl11.c
/* * sl11_bi/udc.c * * Copyright (c) 2000, 2001, 2002 Lineo * * By: * Stuart Lynne, * Tom Rushworth , * Bruce Balden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /*****************************************************************************/ #include #include #include "../usbd-export.h" #include "../usbd-build.h" #include "../usbd-module.h" MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); MODULE_DESCRIPTION ("USB Device SL11 Bus Interface"); USBD_MODULE_INFO ("sl11_bi 0.1-alpha"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../usbd.h" #include "../usbd-func.h" #include "../usbd-bus.h" #include "../usbd-inline.h" #include "usbd-bi.h" #include "sl11.h" #include "sl11-info.h" #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* * ep_address - endpoint buffer addresses * * 0-3 epNa * 4-7 epNb */ static unsigned char ep_address[UDC_MAX_ENDPOINTS * 2] = { EP_S_BUF, EP_S_BUF, EP_S_BUF, EP_S_BUF, EP_S_BUF, EP_S_BUF, EP_S_BUF, EP_S_BUF }; /* * ep_register - endpoint register addresses * * 0-3 epNa * 4-7 epNb */ static unsigned char ep_register[UDC_MAX_ENDPOINTS * 2] = { EP0_Control, EP1_Control, EP2_Control, EP3_Control, EP0_Control + 8, EP1_Control + 8, EP2_Control + 8, EP3_Control + 8 }; static struct usb_device_instance *udc_device; // required for the interrupt handler /* * ep_sequence - current sequence number for each endpoint */ static unsigned char ep_sequence[UDC_MAX_ENDPOINTS] = { 0, 0, 0, 0 }; /* * ep_next - next ping pong buffer */ static unsigned char ep_next[UDC_MAX_ENDPOINTS] = { 0, 0, 0, 0 }; /* * ep_int_mask - interrupt status register endpoint bit masks */ static unsigned char ep_int_mask[UDC_MAX_ENDPOINTS] = { INT_EP0_DONE, INT_EP1_DONE, INT_EP2_DONE, INT_EP3_DONE }; /* * ep_endpoints - map physical endpoints to logical endpoints */ static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; // static struct urb ep0_urb; static struct urb *ep0_urb; static unsigned char usb_address; static int udc_saw_sof; static int udc_suspended; static int udc_addressed; static struct tq_struct sl11_tq; extern unsigned int udc_interrupts; unsigned int udc_ticks; /* ********************************************************************************************* */ #ifdef CONFIG_X86 /** * sl11write_byte - write a byte to the sl11 * @a: sl11 address * @b: byte to write */ static __inline__ void sl11write_byte (unsigned char a, unsigned char b) { outb (a, UDC_ADDR); outb (b, UDC_ADDR + 1); } static __inline__ void _sl11write_byte (unsigned char a, unsigned char b) { printk (KERN_DEBUG "sl11write_byte: Reg: %02x Val: %02x\n", a, b); outb (a, UDC_ADDR); outb (b, UDC_ADDR + 1); } /** * sl11write_buffer - write a buffer to the sl11 using auto-increment mode * @a: sl11 address * @b: pointer to buffer to write * @size: number of bytes to write */ static __inline__ void sl11write_buffer (unsigned char a, unsigned char *b, unsigned char size) { outb (a, UDC_ADDR); while (size--) { outb (*b++, UDC_ADDR + 1); } } /** * sl11read_byte - read a byte from the sl11 and return * @a: sl11 address */ static __inline__ unsigned char sl11read_byte (unsigned char a) { outb (a, UDC_ADDR); return inb (UDC_ADDR + 1); } /** * sl11read_buffer - fill a buffer from the sl11 using auto-increment mode * @a: sl11 address * @b: pointer to buffer to fill * @size: number of bytes to read */ static __inline__ void sl11read_buffer (unsigned char a, unsigned char *b, unsigned char size) { outb (a, UDC_ADDR); while (size--) { *b++ = inb (UDC_ADDR + 1); } } /** * sl11clear - clear sl11 memory * @a: sl11 address * @size: bytes to clear */ static void sl11clear (unsigned char a, unsigned char size) { outb (a, UDC_ADDR); while (size--) { outb (0, UDC_ADDR + 1); } } #else #abort SL11 memory mapped IO not implemented #endif /* ********************************************************************************************* */ /** * sl11write_epn - write a byte to a sl11 endpoint register * @a: endpoint * @b: endpoint register * @d: byte to write */ static __inline__ void sl11write_epn (unsigned char ep, unsigned char b, unsigned char c) { sl11write_byte (ep_register[ep] + b, c); } static __inline__ void _sl11write_epn (unsigned char ep, unsigned char b, unsigned char c) { printk (KERN_DEBUG "sl11write_epn: Reg: %02x Val: %02x\n", ep_register[ep] + b, c); _sl11write_byte (ep_register[ep] + b, c); } /** * sl11read_epn - read a byte from a sl11 endpoint register * @a: endpoint * @b: endpoint register * @d: byte to write */ __inline__ unsigned char sl11read_epn (unsigned char ep, unsigned char b) { return sl11read_byte (ep_register[ep] + b); } /* ********************************************************************************************* */ /** * sl11_dump */ static void sl11_dump (unsigned char a, unsigned char size) { int i; for (i = 0; i < size; i++) { if ((i % 8) == 0) { printk ("\n[%02x]: ", a + i); } printk ("%02x ", sl11read_byte (a + i)); } printk ("\n"); } static void sl11_dump_epn (unsigned char ep, char *m) { printk ("ep[%d]: Ctl:%02x Adr:%02x Xfr:%02x Sts:%02x Cnt:%02x Seq:%d%s", ep, sl11read_epn (ep, EPN_Control), sl11read_epn (ep, EPN_Address), sl11read_epn (ep, EPN_XferLen), sl11read_epn (ep, EPN_Status), sl11read_epn (ep, EPN_Counter), ep_sequence[ep & 0x3], m); } /** * sl11_probe - initialize * * 0. Issue a reset to CtrlReg * * 1. After reset we typically see: * * IntStatus 0x40 or 0x60 * DATASet 0x10 * * 2. We test that IntStatus can be changed to 0x00 by writing 0xff to it. * * 3. If successful clear all chip memory. * **/ static int sl11_probe (void) { sl11_dump (0, 64); printk (KERN_DEBUG "sl11_probe: start\n"); // reset sl11write_byte (CtrlReg, CTRL_USB_RESET); udelay (100); // XXX may not be needed sl11write_byte (CtrlReg, 0); sl11_dump (0, 64); printk (KERN_DEBUG "sl11_probe: RESET\n"); /* * XXX * * If device is present DATASet&CDS_RESERVED will be true. * * After reset we will see IntStatus & INT_USB_RESET if cable is plugged in. * */ if (((sl11read_byte (IntStatus) & INT_USB_RESET) != INT_USB_RESET) || ((sl11read_byte (DATASet) & 0x10) != 0x10)) { sl11_dump (0, 64); printk (KERN_DEBUG "sl11_probe: cannot see " UDC_NAME ", failed initial probe\n"); //udc_regs(); //return -EINVAL; } // reset interrupt status sl11write_byte (IntStatus, 0xff); if ((sl11read_byte (IntStatus) != 0x00)) { sl11_dump (0, 64); printk (KERN_DEBUG "sl11_probe: cannot see " UDC_NAME ", failed interrupt status reset\n"); udc_regs (); return -EINVAL; } // success! // clear all memory sl11clear (0, 255); printk (KERN_DEBUG "sl11_probe: found and setup\n"); //sl11_dump(0, 64); //udc_regs(); return 0; } /* ********************************************************************************************* */ /** * sl11send_data - send packet via endpoint * @ep: logical endpoint number * @bp: pointer to data * @size: bytes to write */ static void __inline__ sl11send_data (unsigned char ep, unsigned char *bp, unsigned char size) { // copy data from buffer to chip if (bp && size) { sl11write_buffer (ep_address[ep], bp, size); } sl11write_epn (ep, EPN_XferLen, size); // arm sl11write_epn (ep, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_IN | (ep_sequence[ep] ? EPN_CTRL_SEQUENCE : 0) ); } /** * s111send_done * @ep */ static void __inline__ sl11send_done (unsigned ep) { // disarm sl11write_epn (ep, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_IN); // flip sequence ep_sequence[ep] = (~ep_sequence[ep]) & 0x1; } /* ********************************************************************************************* */ /** * s111read_init * @ep: endpoint */ static void sl11read_init (unsigned char ep, unsigned char size) { // setup packet size sl11write_epn (ep, EPN_XferLen, size); // arm sl11write_epn (ep, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); } /** * sl11read_done * @ep: endpoint */ static void sl11read_done (unsigned char ep, unsigned char *bp, unsigned char size) { // disarm sl11write_epn (ep, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); // copy data from chip to buffer sl11read_buffer (ep_address[ep], bp, size); } /* ********************************************************************************************* */ /** * sl11_out - process rcv interrupt * * Queue received data. */ static void __inline__ sl11_out (unsigned int ep, struct usb_endpoint_instance *endpoint) { int len; int cnt; int error = 0; int adr; int i; unsigned char status; unsigned char ctl; unsigned int cep; // loop alternating between A-B register sets looking for ACK in packet status // for (i = 0; i < 8; i++) { cep = ep + (ep_next[ep] ? 4 : 0); ep_next[ep] = ~ep_next[ep] & 0x1; status = sl11read_epn (cep, EPN_Status); //printk(KERN_DEBUG"[%d] out[%d]: seq: %d next: %d sts: %02x\n", udc_interrupts, ep, ep_sequence[ep], ep_next[ep], status); if (!(status & EPN_PSTS_ACK)) { continue; //return; } // check packet status bits for errors ctl = sl11read_epn (cep, EPN_Control); len = sl11read_epn (cep, EPN_XferLen); cnt = sl11read_epn (cep, EPN_Counter); adr = sl11read_epn (cep, EPN_Address); //printk(KERN_DEBUG"[%d] out[%d]: sts: %02x len: %d cnt: %d adr: %02x seq: %02x bytes: %0d\n", // udc_interrupts, cep, status, len, cnt, adr, ep_sequence[ep], len - cnt); //sl11_dump_epn(ep, " "); //sl11_dump_epn(ep + 4, "\n"); len -= cnt; if (status & (EPN_PSTS_ERROR | EPN_PSTS_TIMEOUT | EPN_PSTS_SETUP | EPN_PSTS_OVERFLOW)) { printk (KERN_DEBUG "sl11_out[%d]: ERROR\n", cep); error = 1; } if (((status & EPN_PSTS_SEQUENCE) && ep_sequence[ep]) || (!(status & EPN_PSTS_SEQUENCE) && !ep_sequence[ep])) { ep_sequence[ep] = (~ep_sequence[ep]) & 0x1; } else { // sequence error printk (KERN_DEBUG "sl11_out[%d]: SEQ\n", cep); error = 1; } if (endpoint->rcv_urb && len) { sl11read_buffer (adr, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, len); } // re-arm sl11write_epn (cep, EPN_Status, 0); sl11write_epn (cep, EPN_Counter, 0); sl11write_epn (cep, EPN_XferLen, endpoint->rcv_packetSize); //sl11_dump_epn(ep, "\n"); sl11write_epn (cep, EPN_Control, ctl | EPN_CTRL_ARM); if (endpoint->endpoint_address) { //printk(KERN_DEBUG"sl11_out: receive len: %d\n", len); usbd_rcv_complete_irq (endpoint, len, 0); } else { printk (KERN_DEBUG "sl11_out: cannot receive no endpoint address len: %d\n", len); } } } /* ********************************************************************************************* */ /** * sl11_start - start transmit * @ep: */ static void __inline__ sl11_start (unsigned int ep, struct usb_endpoint_instance *endpoint, int restart) { //printk(KERN_DEBUG"sl11_start: tx_urb: %p sent: %d\n", endpoint->tx_urb, endpoint->sent); if (endpoint->tx_urb) { struct urb *urb = endpoint->tx_urb; //printk(KERN_DEBUG"sl11_start: length: %d\n", endpoint->tx_urb->actual_length); if ((urb->actual_length - endpoint->sent) > 0) { endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); sl11send_data (ep, urb->buffer + endpoint->sent, endpoint->last); } else { // XXX ZLP endpoint->last = 0; sl11send_data (ep, urb->buffer + endpoint->sent, 0); } } } /** * sl11_in - process tx interrupt * @ep: * @endpoint: * * Determine status of last data sent, queue new data. */ static __inline__ void sl11_in (unsigned int ep, struct usb_endpoint_instance *endpoint) { int restart = 0; unsigned char status; restart = 0; //printk(KERN_DEBUG"in[%d]: seq: %d next: %d\n", ep, ep_sequence[ep], ep_next[ep]); // check packet status bits for errors status = sl11read_epn (ep, EPN_Status); if (status & (EPN_PSTS_ERROR | EPN_PSTS_TIMEOUT | EPN_PSTS_SETUP | EPN_PSTS_OVERFLOW)) { printk (KERN_DEBUG "sl11_in[%d]: ERROR status: %0x\n", ep, status); restart = 1; } else { // flip sequence ep_sequence[ep] = (~ep_sequence[ep]) & 0x1; } usbd_tx_complete_irq (endpoint, restart); sl11_start (ep, endpoint, restart); } /* ********************************************************************************************* */ static void sl11_ep0_setaddress (struct usb_endpoint_instance *endpoint) { //printk(KERN_DEBUG"sl11_ep0: finished set address: %d\n", usb_address); sl11write_byte (USBAdd, usb_address); udc_addressed = usb_address; usb_address = 0; // send ack sl11write_epn (0, EPN_XferLen, endpoint->rcv_packetSize); sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); printk ("\n"); sl11_dump_epn (0, "\n"); } static void sl11_ep0_ending (struct usb_endpoint_instance *endpoint) { //ep_sequence[0] = ~ep_sequence[0]; if (endpoint->tx_urb) { //printk(KERN_DEBUG"sl11_ep0: continue IN actual: %d sent: %d last: %d seq: %d\n", // endpoint->tx_urb->actual_length, endpoint->sent, endpoint->last, ep_sequence[0]); sl11_in (0, endpoint); } if (endpoint->tx_urb) { //printk(KERN_DEBUG"sl11_ep0: still sending\n"); return; } //printk(KERN_DEBUG"sl11_ep0: finished sending\n"); // send ack sl11write_epn (0, EPN_XferLen, endpoint->rcv_packetSize); sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); } static void sl11_ep0_receiving (struct usb_endpoint_instance *endpoint) { // XXX check sequence //ep_sequence[ep] = ~ep_sequence[ep]; // are we trying to read data? if (endpoint->rcv_urb) { // ok, handle incoming data sl11_out (0, endpoint); // more to send? if (endpoint->rcv_urb) { return; } // send ack sl11write_epn (0, EPN_XferLen, 0); sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); return; } //printk(KERN_DEBUG"sl11_ep0: received ACK\n"); sl11write_epn (0, EPN_XferLen, endpoint->rcv_packetSize); sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); // stall if we where not waiting for data //udc_stall_ep(0); } /** * sl11_ep0 - process an endpoint 0 interrupt * * Read and process a setup packet */ static void sl11_ep0 (struct usb_endpoint_instance *endpoint) { unsigned char control; unsigned char status; unsigned char *cp; //sl11_dump_epn(0, "\n"); // delayed set address, cannot set until we have received IN if (usb_address) { sl11_ep0_setaddress (endpoint); return; } // are we sending a reply control = sl11read_epn (0, EPN_Control); if (control & EPN_CTRL_DIRECTION_IN) { sl11_ep0_ending (endpoint); return; } status = sl11read_epn (0, EPN_Status); // is the host sending data? if (!(status & EPN_PSTS_SETUP)) { sl11_ep0_receiving (endpoint); return; } // check if host changed it's mind if (endpoint->tx_urb) { endpoint->tx_urb = NULL; endpoint->last = endpoint->sent = 0; printk (KERN_DEBUG "sl11_ep0: host cancelled tx\n"); } if (endpoint->rcv_urb) { endpoint->rcv_urb = NULL; ep0_urb->actual_length = 0; printk (KERN_DEBUG "sl11_ep0: host cancelled rcv\n"); } // read setup packet //len = sl11read_epn(0, EPN_XferLen); //printk(KERN_DEBUG"sl11_ep0: reading setup\n"); sl11read_buffer (ep_address[0], (unsigned char *) &ep0_urb->device_request, 8); //sl11_dump(0, 255); cp = (unsigned char *) &ep0_urb->device_request; //printk(KERN_DEBUG"setup: %02x %02x %02x %02x %02x %02x %02x %02x\n", // cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); // process setup packet if (usbd_recv_setup (ep0_urb)) { printk (KERN_DEBUG "sl11_ep0: setup failed\n"); //sl11write_epn(0, ) //udc_stall_ep(0); return; } //sl11write_epn(0, EPN_XferLen, 0); //sl11write_epn(0, EPN_Control, EPN_CTRL_SEQUENCE | EPN_CTRL_DIRECTION_IN | EPN_CTRL_ENABLE | EPN_CTRL_ARM); //return; // XXX // check data direction if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { // should we setup to receive data if (le16_to_cpu (ep0_urb->device_request.wLength)) { //printk(KERN_DEBUG"sl11_ep0: read data %d\n",le16_to_cpu(ep0_urb->device_request.wLength)); endpoint->rcv_urb = ep0_urb; endpoint->rcv_urb->actual_length = 0; sl11_out (0, endpoint); return; } //printk(KERN_DEBUG"sl11_ep0: send ack %d\n",le16_to_cpu(ep0_urb->device_request.wLength)); // should be finished, send ack sl11write_epn (0, EPN_XferLen, 0); sl11write_epn (0, EPN_Control, EPN_CTRL_SEQUENCE | EPN_CTRL_DIRECTION_IN | EPN_CTRL_ENABLE | EPN_CTRL_ARM); return; } // we should be sending data back //printk(KERN_DEBUG"sl11_ep0: send data: %d\n", le16_to_cpu(ep0_urb->device_request.wLength)); // verify that we have non-zero request length if (!le16_to_cpu (ep0_urb->device_request.wLength)) { udc_stall_ep (0); return; } // verify that we have non-zero length response if (!ep0_urb->actual_length) { udc_stall_ep (0); return; } // start sending //printk(KERN_DEBUG"sl11_ep0: start sending\n"); endpoint->tx_urb = ep0_urb; endpoint->sent = 0; endpoint->last = 0; ep_sequence[0] = 1; sl11_start (0, endpoint, 0); } /* ********************************************************************************************* */ /** * sl11_int_hndlr - interrupt handler * */ static void sl11_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) { int i; unsigned char status; int ep; udc_interrupts++; // loop while interrupt status register is non-zero for (i = 0; (i < 10) && (status = sl11read_byte (IntStatus)); i++) { //if ((status & ~INT_SOF_RECEIVED)) { // printk(KERN_DEBUG"[%d] %02x\n", udc_interrupts, status); //} // Common interrupts - Endpoint done for (ep = 1; ep < UDC_MAX_ENDPOINTS; ep++) { struct usb_endpoint_instance *endpoint; if ((status & ep_int_mask[ep]) && (endpoint = ep_endpoints[ep])) { // transmit on other than endpoint zero if (endpoint->endpoint_address & IN) { endpoint->tx_interrupts++; sl11_in (ep, endpoint); } // receive on other than endpoint zero else { endpoint->rcv_interrupts++; sl11_out (ep, endpoint); } } } // uncommon interrupts if (status & (INT_USB_RESET | INT_EP0_DONE | INT_DMA_DONE | INT_SOF_RECEIVED)) { // Reset if (status & INT_USB_RESET) { printk (KERN_DEBUG "sl11_int_hndlr: RESET: %02x\n", status); udc_suspended = 0; usbd_device_event (udc_device, DEVICE_RESET, 0); } // endpoint zero if ((status & INT_EP0_DONE)) { struct usb_endpoint_instance *endpoint; endpoint = ep_endpoints[0]; endpoint->ep0_interrupts++; sl11_ep0 (endpoint); } // DMA done if (status & INT_DMA_DONE) { printk (KERN_DEBUG "sl11_int_hndlr: DMA: %02x\n", status); } // SOF frame if (status & INT_SOF_RECEIVED) { udc_saw_sof = 1; if (udc_suspended) { udc_suspended = 0; usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); printk (KERN_DEBUG "sl11_tick: RESUMED\n"); } } } // clear all available status bits sl11write_byte (IntStatus, status); } } /* ********************************************************************************************* */ /** * sl11_tick - clock timer task * @data: * * Run from global clock tick to check if we are suspended. */ static void sl11_tick (void *data) { udc_ticks++; // is driver active if (data) { // if not suspended check if we should suspend if (udc_addressed && !udc_suspended) { int saw_sof = *((int *) data); if (!saw_sof) { // XXX // usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); udc_suspended = 1; printk (KERN_DEBUG "sl11_tick: SUSPENDED\n"); } *(int *) data = 0; } // re-queue task queue_task (&sl11_tq, &tq_timer); } else { printk (KERN_DEBUG "sl11_tick: not restarting\n"); } } /* ********************************************************************************************* */ /* * Start of public functions. */ /** * udc_start_in_irq - start transmit * @eendpoint: endpoint instance * * Called by bus interface driver to see if we need to start a data transmission. */ void udc_start_in_irq (struct usb_endpoint_instance *endpoint) { sl11_start (endpoint->endpoint_address & 0xf, endpoint, 0); } /** * udc_stall_ep - stall endpoint * @ep: physical endpoint * * Stall the endpoint. */ void udc_stall_ep (unsigned int ep) { unsigned char status; if (ep < UDC_MAX_ENDPOINTS) { status = sl11read_epn (ep, EPN_Control); sl11write_epn (ep, EPN_Control, status | EPN_CTRL_SENDSTALL); udelay (10); // XXX sl11write_epn (ep, EPN_Control, status); } } /** * udc_reset_ep - reset endpoint * @ep: physical endpoint * reset the endpoint. * * returns : 0 if ok, -1 otherwise */ void udc_reset_ep (unsigned int ep) { if (ep < UDC_MAX_ENDPOINTS) { ep_sequence[ep] = 0; } printk (KERN_DEBUG "udc_reset_ep[%d]:\n", ep); if (!ep) { if (udc_addressed) { printk (KERN_DEBUG "udc_reset_ep[%d]: reseting address\n", ep); usb_address = 0; udc_addressed = 0; sl11write_byte (USBAdd, 0); // reset UDC sl11write_byte (CtrlReg, CTRL_USB_RESET); udelay (100); // XXX may not be needed sl11write_byte (CtrlReg, 0); // enable UDC sl11write_byte (CtrlReg, CTRL_USB_ENABLE); } // arm EP0 sl11write_epn (0, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); sl11_dump_epn (ep + 0, " "); sl11_dump_epn (ep + 4, "\n"); } } /** * udc_endpoint_halted - is endpoint halted * @ep: * * Return non-zero if endpoint is halted */ int udc_endpoint_halted (unsigned int ep) { return 0; } /** * udc_set_address - set the USB address for this device * @address: * * Called from control endpoint function after it decodes a set address setup packet. */ void udc_set_address (unsigned char address) { // address cannot be setup until ack received usb_address = address; } /** * udc_serial_init - set a serial number if available */ static int __init udc_serial_init (struct usb_bus_instance *bus) { return -EINVAL; } /* ********************************************************************************************* */ /** * udc_max_endpoints - max physical endpoints * * Return number of physical endpoints. */ int udc_max_endpoints (void) { return UDC_MAX_ENDPOINTS; } /** * udc_check_ep - check logical endpoint * @lep: * * Return physical endpoint number to use for this logical endpoint or zero if not valid. */ int udc_check_ep (int logical_endpoint, int packetsize) { return (((logical_endpoint & 0xf) >= UDC_MAX_ENDPOINTS) || (packetsize > 64)) ? 0 : (logical_endpoint & 0xf); } /** * udc_set_ep - setup endpoint * @ep: * @endpoint: * * Associate a physical endpoint with endpoint_instance */ void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, struct usb_endpoint_instance *endpoint) { if (ep < UDC_MAX_ENDPOINTS) { ep_endpoints[ep] = endpoint; sl11write_epn (ep, EPN_Control, 0); sl11write_epn (ep, EPN_XferLen, 0); sl11write_epn (ep, EPN_Status, 0); sl11write_epn (ep, EPN_Counter, 0); ep_address[ep] = EP_S_BUF; ep_address[ep * 2] = EP_S_BUF; // ep0 if (ep == 0) { sl11write_epn (ep, EPN_Address, EP_S_BUF); sl11write_epn (ep, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); sl11write_epn (ep, EPN_XferLen, endpoint->rcv_packetSize); sl11write_epn (ep, EPN_Status, 0); sl11write_epn (ep, EPN_Counter, 0); } // IN else if (endpoint->endpoint_address & 0x80) { sl11write_epn (ep, EPN_Address, EP_S_BUF); sl11write_epn (ep, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_IN); } // OUT else if (endpoint->endpoint_address) { usbd_fill_rcv (device, endpoint, 5); endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); ep_sequence[ep] = 0; sl11write_epn (ep + 0, EPN_Address, EP_A_BUF); sl11write_epn (ep + 4, EPN_Address, EP_B_BUF); ep_address[ep + 0] = EP_A_BUF; ep_address[ep + 4] = EP_B_BUF; sl11write_epn (ep + 0, EPN_XferLen, endpoint->rcv_packetSize); sl11write_epn (ep + 4, EPN_XferLen, endpoint->rcv_packetSize); sl11write_epn (ep + 0, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); sl11write_epn (ep + 0, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT | EPN_CTRL_NEXTDATA); sl11write_epn (ep + 4, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); printk (KERN_DEBUG "udc_setup_ep[%d]: setup OUT\n", ep); sl11_dump_epn (ep + 0, " "); sl11_dump_epn (ep + 4, "\n"); } } //sl11_dump(0, 64); } /** * udc_disable_ep - disable endpoint * @ep: * * Disable specified endpoint */ void udc_disable_ep (unsigned int ep) { #if 0 if (ep < UDC_MAX_ENDPOINTS) { struct usb_endpoint_instance *endpoint; if ((endpoint = ep_endpoints[ep])) { ep_endpoints[ep] = NULL; usbd_flush_ep (endpoint); } } #endif } /* ********************************************************************************************* */ /** * udc_connected - is the USB cable connected * * Return non-zeron if cable is connected. */ int udc_connected (void) { return 1; } /** * udc_connect - enable pullup resistor * * Turn on the USB connection by enabling the pullup resistor. */ void udc_connect (void) { // enable UDC sl11write_byte (CtrlReg, CTRL_USB_ENABLE); } /** * udc_disconnect - disable pullup resistor * * Turn off the USB connection by disabling the pullup resistor. */ void udc_disconnect (void) { // disable UDC sl11write_byte (CtrlReg, 0); } /* ********************************************************************************************* */ /** * udc_enable_interrupts - enable interrupts * * Switch on UDC interrupts. * */ void udc_all_interrupts (struct usb_device_instance *device) { printk (KERN_DEBUG "udc_enable_interrupts:\n"); // set interrupt mask sl11write_byte (IntEna, INT_EP0_DONE | INT_EP1_DONE | INT_EP2_DONE | INT_EP3_DONE | INT_SOF_RECEIVED /*| INT_USB_RESET */ ); } /** * udc_suspended_interrupts - enable suspended interrupts * * Switch on only UDC resume interrupt. * */ void udc_suspended_interrupts (struct usb_device_instance *device) { printk (KERN_DEBUG "udc_enable_interrupts:\n"); // set interrupt mask sl11write_byte (IntEna, INT_SOF_RECEIVED | INT_USB_RESET); } /** * udc_disable_interrupts - disable interrupts. * * switch off interrupts */ void udc_disable_interrupts (struct usb_device_instance *device) { printk (KERN_DEBUG "udc_disable_interrupts:\n"); // reset interrupt mask sl11write_byte (IntEna, 0); } /* ********************************************************************************************* */ /** * udc_ep0_packetsize - return ep0 packetsize */ int udc_ep0_packetsize (void) { return EP0_PACKETSIZE; } /** * udc_enable - enable the UDC * * Switch on the UDC */ void udc_enable (struct usb_device_instance *device) { printk (KERN_DEBUG "\n"); printk (KERN_DEBUG "udc_enable: device: %p\n", device); // save the device structure pointer udc_device = device; // ep0 urb if (!ep0_urb) { if (!(ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) { printk (KERN_ERR "udc_enable: usbd_alloc_urb failed\n"); } } else { printk (KERN_ERR "udc_enable: ep0_urb already allocated\n"); } //ep0_urb->device = device; //usbd_alloc_urb_data(&ep0_urb, 512); // enable UDC // sl11write_byte(CtrlReg, CTRL_USB_ENABLE); // setup tick // XXX sl11_tq.sync = 0; sl11_tq.routine = sl11_tick; sl11_tq.data = &udc_saw_sof; queue_task (&sl11_tq, &tq_timer); udc_suspended = 1; } /** * udc_disable - disable the UDC * * Switch off the UDC */ void udc_disable (void) { printk (KERN_DEBUG "************************* udc_disable:\n"); // tell tick task to stop sl11_tq.data = NULL; while (sl11_tq.sync) { printk (KERN_DEBUG "waiting for sl11_tq to stop\n"); schedule_timeout (10 * HZ); } // XXX del_timer() or wait // disable UDC // sl11write_byte(CtrlReg, 0); // reset device pointer udc_device = NULL; // ep0 urb //kfree(ep0_urb.buffer); if (ep0_urb) { usbd_dealloc_urb (ep0_urb); ep0_urb = 0; } else { printk (KERN_ERR "udc_disable: ep0_urb already NULL\n"); } } /** * udc_startup - allow udc code to do any additional startup */ void udc_startup_events (struct usb_device_instance *device) { usbd_device_event (device, DEVICE_INIT, 0); usbd_device_event (device, DEVICE_CREATE, 0); usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); usbd_device_event (device, DEVICE_RESET, 0); // XXX should be done from device event } /* ********************************************************************************************* */ /** * udc_init - initialize USB Device Controller * * Get ready to use the USB Device Controller. * * Register an interrupt handler and IO region. Return non-zero for error. */ int udc_init (void) { printk (KERN_DEBUG "udc_init:\n"); // probe for Sl11 return sl11_probe (); } /** * udc_regs - dump registers * * Dump registers with printk */ void udc_regs (void) { sl11_dump_epn (0, " "); printk (" Ctrl: %02x Addr: %02x IntE: %02x IntS: %02x Data: %02x SOF: %02x %02x Sus: %d\n", sl11read_byte (CtrlReg), sl11read_byte (USBAdd), sl11read_byte (IntEna), sl11read_byte (IntStatus), sl11read_byte (DATASet), sl11read_byte (SOFHigh), sl11read_byte (SOFLow), udc_suspended); sl11_dump_epn (1, " "); sl11_dump_epn (5, "\n"); sl11_dump_epn (2, " "); sl11_dump_epn (3, "\n"); } /* ********************************************************************************************* */ /** * udc_name - return name of USB Device Controller */ char *udc_name (void) { return UDC_NAME; } /** * udc_request_udc_irq - request UDC interrupt * * Return non-zero if not successful. */ int udc_request_udc_irq () { // request IRQ and IO region if (request_irq (UDC_IRQ, sl11_int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME, NULL) != 0) { printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq\n"); return -EINVAL; } return 0; } /** * udc_request_cable_irq - request Cable interrupt * * Return non-zero if not successful. */ int udc_request_cable_irq () { return 0; } /** * udc_request_udc_io - request UDC io region * * Return non-zero if not successful. */ int udc_request_io () { int rc = 0; #ifdef CONFIG_X86 // reserve io { unsigned long flags; local_irq_save (flags); if (check_region (UDC_ADDR, UDC_ADDR_SIZE)) { printk (KERN_DEBUG "udc_request_io: failed %x %x\n", UDC_ADDR, UDC_ADDR_SIZE); rc = -EINVAL; } request_region (UDC_ADDR, UDC_ADDR_SIZE, UDC_NAME); local_irq_restore (flags); } #endif return rc; } /** * udc_release_udc_irq - release UDC irq */ void udc_release_udc_irq () { free_irq (UDC_IRQ, NULL); } /** * udc_release_cable_irq - release Cable irq */ void udc_release_cable_irq () { } /** * udc_release_io - release UDC io region */ void udc_release_io () { #ifdef CONFIG_X86 release_region (UDC_ADDR, UDC_ADDR_SIZE); #endif }