/* * BRIEF MODULE DESCRIPTION * Au1000 USB Device-Side (device layer) * * Copyright 2001-2002 MontaVista Software Inc. * Author: MontaVista Software, Inc. * stevel@mvista.com or source@mvista.com * * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * 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 #include #include #include #include #include #include #include #include #include #include #define DEBUG #include #include #include #include #include #include #include #include #ifdef DEBUG #undef VDEBUG #ifdef VDEBUG #define vdbg(fmt, arg...) printk(KERN_DEBUG __FILE__ ": " fmt "\n" , ## arg) #else #define vdbg(fmt, arg...) do {} while (0) #endif #else #define vdbg(fmt, arg...) do {} while (0) #endif #define ALLOC_FLAGS (in_interrupt () ? GFP_ATOMIC : GFP_KERNEL) #define EP_FIFO_DEPTH 8 typedef enum { SETUP_STAGE = 0, DATA_STAGE, STATUS_STAGE } ep0_stage_t; typedef struct { int read_fifo; int write_fifo; int ctrl_stat; int read_fifo_status; int write_fifo_status; } endpoint_reg_t; typedef struct { usbdev_pkt_t *head; usbdev_pkt_t *tail; int count; } pkt_list_t; typedef struct { int active; struct usb_endpoint_descriptor *desc; endpoint_reg_t *reg; /* Only one of these are used, unless this is the control ep */ pkt_list_t inlist; pkt_list_t outlist; unsigned int indma, outdma; /* DMA channel numbers for IN, OUT */ /* following are extracted from endpoint descriptor for easy access */ int max_pkt_size; int type; int direction; /* WE assign endpoint addresses! */ int address; spinlock_t lock; } endpoint_t; static struct usb_dev { endpoint_t ep[6]; ep0_stage_t ep0_stage; struct usb_device_descriptor * dev_desc; struct usb_interface_descriptor* if_desc; struct usb_config_descriptor * conf_desc; u8 * full_conf_desc; struct usb_string_descriptor * str_desc[6]; /* callback to function layer */ void (*func_cb)(usbdev_cb_type_t type, unsigned long arg, void *cb_data); void* cb_data; usbdev_state_t state; // device state int suspended; // suspended flag int address; // device address int interface; int num_ep; u8 alternate_setting; u8 configuration; // configuration value int remote_wakeup_en; } usbdev; static endpoint_reg_t ep_reg[] = { // FIFO's 0 and 1 are EP0 default control {USBD_EP0RD, USBD_EP0WR, USBD_EP0CS, USBD_EP0RDSTAT, USBD_EP0WRSTAT }, {0}, // FIFO 2 is EP2, IN { -1, USBD_EP2WR, USBD_EP2CS, -1, USBD_EP2WRSTAT }, // FIFO 3 is EP3, IN { -1, USBD_EP3WR, USBD_EP3CS, -1, USBD_EP3WRSTAT }, // FIFO 4 is EP4, OUT {USBD_EP4RD, -1, USBD_EP4CS, USBD_EP4RDSTAT, -1 }, // FIFO 5 is EP5, OUT {USBD_EP5RD, -1, USBD_EP5CS, USBD_EP5RDSTAT, -1 } }; static struct { unsigned int id; const char *str; } ep_dma_id[] = { { DMA_ID_USBDEV_EP0_TX, "USBDev EP0 IN" }, { DMA_ID_USBDEV_EP0_RX, "USBDev EP0 OUT" }, { DMA_ID_USBDEV_EP2_TX, "USBDev EP2 IN" }, { DMA_ID_USBDEV_EP3_TX, "USBDev EP3 IN" }, { DMA_ID_USBDEV_EP4_RX, "USBDev EP4 OUT" }, { DMA_ID_USBDEV_EP5_RX, "USBDev EP5 OUT" } }; #define DIR_OUT 0 #define DIR_IN (1<<3) #define CONTROL_EP USB_ENDPOINT_XFER_CONTROL #define BULK_EP USB_ENDPOINT_XFER_BULK static inline endpoint_t * epaddr_to_ep(struct usb_dev* dev, int ep_addr) { if (ep_addr >= 0 && ep_addr < 2) return &dev->ep[0]; if (ep_addr < 6) return &dev->ep[ep_addr]; return NULL; } static const char* std_req_name[] = { "GET_STATUS", "CLEAR_FEATURE", "RESERVED", "SET_FEATURE", "RESERVED", "SET_ADDRESS", "GET_DESCRIPTOR", "SET_DESCRIPTOR", "GET_CONFIGURATION", "SET_CONFIGURATION", "GET_INTERFACE", "SET_INTERFACE", "SYNCH_FRAME" }; static inline const char* get_std_req_name(int req) { return (req >= 0 && req <= 12) ? std_req_name[req] : "UNKNOWN"; } #if 0 static void dump_setup(struct usb_ctrlrequest* s) { dbg("%s: requesttype=%d", __FUNCTION__, s->requesttype); dbg("%s: request=%d %s", __FUNCTION__, s->request, get_std_req_name(s->request)); dbg("%s: value=0x%04x", __FUNCTION__, s->wValue); dbg("%s: index=%d", __FUNCTION__, s->index); dbg("%s: length=%d", __FUNCTION__, s->length); } #endif static inline usbdev_pkt_t * alloc_packet(endpoint_t * ep, int data_size, void* data) { usbdev_pkt_t* pkt = kmalloc(sizeof(usbdev_pkt_t) + data_size, ALLOC_FLAGS); if (!pkt) return NULL; pkt->ep_addr = ep->address; pkt->size = data_size; pkt->status = 0; pkt->next = NULL; if (data) memcpy(pkt->payload, data, data_size); return pkt; } /* * Link a packet to the tail of the enpoint's packet list. * EP spinlock must be held when calling. */ static void link_tail(endpoint_t * ep, pkt_list_t * list, usbdev_pkt_t * pkt) { if (!list->tail) { list->head = list->tail = pkt; list->count = 1; } else { list->tail->next = pkt; list->tail = pkt; list->count++; } } /* * Unlink and return a packet from the head of the given packet * list. It is the responsibility of the caller to free the packet. * EP spinlock must be held when calling. */ static usbdev_pkt_t * unlink_head(pkt_list_t * list) { usbdev_pkt_t *pkt; pkt = list->head; if (!pkt || !list->count) { return NULL; } list->head = pkt->next; if (!list->head) { list->head = list->tail = NULL; list->count = 0; } else list->count--; return pkt; } /* * Create and attach a new packet to the tail of the enpoint's * packet list. EP spinlock must be held when calling. */ static usbdev_pkt_t * add_packet(endpoint_t * ep, pkt_list_t * list, int size) { usbdev_pkt_t *pkt = alloc_packet(ep, size, NULL); if (!pkt) return NULL; link_tail(ep, list, pkt); return pkt; } /* * Unlink and free a packet from the head of the enpoint's * packet list. EP spinlock must be held when calling. */ static inline void free_packet(pkt_list_t * list) { kfree(unlink_head(list)); } /* EP spinlock must be held when calling. */ static inline void flush_pkt_list(pkt_list_t * list) { while (list->count) free_packet(list); } /* EP spinlock must be held when calling */ static inline void flush_write_fifo(endpoint_t * ep) { if (ep->reg->write_fifo_status >= 0) { au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->reg->write_fifo_status); //udelay(100); //au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, // ep->reg->write_fifo_status); } } /* EP spinlock must be held when calling */ static inline void flush_read_fifo(endpoint_t * ep) { if (ep->reg->read_fifo_status >= 0) { au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->reg->read_fifo_status); //udelay(100); //au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, // ep->reg->read_fifo_status); } } /* EP spinlock must be held when calling. */ static void endpoint_flush(endpoint_t * ep) { // First, flush all packets flush_pkt_list(&ep->inlist); flush_pkt_list(&ep->outlist); // Now flush the endpoint's h/w FIFO(s) flush_write_fifo(ep); flush_read_fifo(ep); } /* EP spinlock must be held when calling. */ static void endpoint_stall(endpoint_t * ep) { u32 cs; warn("%s", __FUNCTION__); cs = au_readl(ep->reg->ctrl_stat) | USBDEV_CS_STALL; au_writel(cs, ep->reg->ctrl_stat); } /* EP spinlock must be held when calling. */ static void endpoint_unstall(endpoint_t * ep) { u32 cs; warn("%s", __FUNCTION__); cs = au_readl(ep->reg->ctrl_stat) & ~USBDEV_CS_STALL; au_writel(cs, ep->reg->ctrl_stat); } static void endpoint_reset_datatoggle(endpoint_t * ep) { // FIXME: is this possible? } /* EP spinlock must be held when calling. */ static int endpoint_fifo_read(endpoint_t * ep) { int read_count = 0; u8 *bufptr; usbdev_pkt_t *pkt = ep->outlist.tail; if (!pkt) return -EINVAL; bufptr = &pkt->payload[pkt->size]; while (au_readl(ep->reg->read_fifo_status) & USBDEV_FSTAT_FCNT_MASK) { *bufptr++ = au_readl(ep->reg->read_fifo) & 0xff; read_count++; pkt->size++; } return read_count; } #if 0 /* EP spinlock must be held when calling. */ static int endpoint_fifo_write(endpoint_t * ep, int index) { int write_count = 0; u8 *bufptr; usbdev_pkt_t *pkt = ep->inlist.head; if (!pkt) return -EINVAL; bufptr = &pkt->payload[index]; while ((au_readl(ep->reg->write_fifo_status) & USBDEV_FSTAT_FCNT_MASK) < EP_FIFO_DEPTH) { if (bufptr < pkt->payload + pkt->size) { au_writel(*bufptr++, ep->reg->write_fifo); write_count++; } else { break; } } return write_count; } #endif /* * This routine is called to restart transmission of a packet. * The endpoint's TSIZE must be set to the new packet's size, * and DMA to the write FIFO needs to be restarted. * EP spinlock must be held when calling. */ static void kickstart_send_packet(endpoint_t * ep) { u32 cs; usbdev_pkt_t *pkt = ep->inlist.head; vdbg("%s: ep%d, pkt=%p", __FUNCTION__, ep->address, pkt); if (!pkt) { err("%s: head=NULL! list->count=%d", __FUNCTION__, ep->inlist.count); return; } dma_cache_wback_inv((unsigned long)pkt->payload, pkt->size); /* * make sure FIFO is empty */ flush_write_fifo(ep); cs = au_readl(ep->reg->ctrl_stat) & USBDEV_CS_STALL; cs |= (pkt->size << USBDEV_CS_TSIZE_BIT); au_writel(cs, ep->reg->ctrl_stat); if (get_dma_active_buffer(ep->indma) == 1) { set_dma_count1(ep->indma, pkt->size); set_dma_addr1(ep->indma, virt_to_phys(pkt->payload)); enable_dma_buffer1(ep->indma); // reenable } else { set_dma_count0(ep->indma, pkt->size); set_dma_addr0(ep->indma, virt_to_phys(pkt->payload)); enable_dma_buffer0(ep->indma); // reenable } if (dma_halted(ep->indma)) start_dma(ep->indma); } /* * This routine is called when a packet in the inlist has been * completed. Frees the completed packet and starts sending the * next. EP spinlock must be held when calling. */ static usbdev_pkt_t * send_packet_complete(endpoint_t * ep) { usbdev_pkt_t *pkt = unlink_head(&ep->inlist); if (pkt) { pkt->status = (au_readl(ep->reg->ctrl_stat) & USBDEV_CS_NAK) ? PKT_STATUS_NAK : PKT_STATUS_ACK; vdbg("%s: ep%d, %s pkt=%p, list count=%d", __FUNCTION__, ep->address, (pkt->status & PKT_STATUS_NAK) ? "NAK" : "ACK", pkt, ep->inlist.count); } /* * The write fifo should already be drained if things are * working right, but flush it anyway just in case. */ flush_write_fifo(ep); // begin transmitting next packet in the inlist if (ep->inlist.count) { kickstart_send_packet(ep); } return pkt; } /* * Add a new packet to the tail of the given ep's packet * inlist. The transmit complete interrupt frees packets from * the head of this list. EP spinlock must be held when calling. */ static int send_packet(struct usb_dev* dev, usbdev_pkt_t *pkt, int async) { pkt_list_t *list; endpoint_t* ep; if (!pkt || !(ep = epaddr_to_ep(dev, pkt->ep_addr))) return -EINVAL; if (!pkt->size) return 0; list = &ep->inlist; if (!async && list->count) { halt_dma(ep->indma); flush_pkt_list(list); } link_tail(ep, list, pkt); vdbg("%s: ep%d, pkt=%p, size=%d, list count=%d", __FUNCTION__, ep->address, pkt, pkt->size, list->count); if (list->count == 1) { /* * if the packet count is one, it means the list was empty, * and no more data will go out this ep until we kick-start * it again. */ kickstart_send_packet(ep); } return pkt->size; } /* * This routine is called to restart reception of a packet. * EP spinlock must be held when calling. */ static void kickstart_receive_packet(endpoint_t * ep) { usbdev_pkt_t *pkt; // get and link a new packet for next reception if (!(pkt = add_packet(ep, &ep->outlist, ep->max_pkt_size))) { err("%s: could not alloc new packet", __FUNCTION__); return; } if (get_dma_active_buffer(ep->outdma) == 1) { clear_dma_done1(ep->outdma); set_dma_count1(ep->outdma, ep->max_pkt_size); set_dma_count0(ep->outdma, 0); set_dma_addr1(ep->outdma, virt_to_phys(pkt->payload)); enable_dma_buffer1(ep->outdma); // reenable } else { clear_dma_done0(ep->outdma); set_dma_count0(ep->outdma, ep->max_pkt_size); set_dma_count1(ep->outdma, 0); set_dma_addr0(ep->outdma, virt_to_phys(pkt->payload)); enable_dma_buffer0(ep->outdma); // reenable } if (dma_halted(ep->outdma)) start_dma(ep->outdma); } /* * This routine is called when a packet in the outlist has been * completed (received) and we need to prepare for a new packet * to be received. Halts DMA and computes the packet size from the * remaining DMA counter. Then prepares a new packet for reception * and restarts DMA. FIXME: what if another packet comes in * on top of the completed packet? Counter would be wrong. * EP spinlock must be held when calling. */ static usbdev_pkt_t * receive_packet_complete(endpoint_t * ep) { usbdev_pkt_t *pkt = ep->outlist.tail; u32 cs; halt_dma(ep->outdma); cs = au_readl(ep->reg->ctrl_stat); if (!pkt) return NULL; pkt->size = ep->max_pkt_size - get_dma_residue(ep->outdma); if (pkt->size) dma_cache_inv((unsigned long)pkt->payload, pkt->size); /* * need to pull out any remaining bytes in the FIFO. */ endpoint_fifo_read(ep); /* * should be drained now, but flush anyway just in case. */ flush_read_fifo(ep); pkt->status = (cs & USBDEV_CS_NAK) ? PKT_STATUS_NAK : PKT_STATUS_ACK; if (ep->address == 0 && (cs & USBDEV_CS_SU)) pkt->status |= PKT_STATUS_SU; vdbg("%s: ep%d, %s pkt=%p, size=%d", __FUNCTION__, ep->address, (pkt->status & PKT_STATUS_NAK) ? "NAK" : "ACK", pkt, pkt->size); kickstart_receive_packet(ep); return pkt; } /* **************************************************************************** * Here starts the standard device request handlers. They are * all called by do_setup() via a table of function pointers. **************************************************************************** */ static ep0_stage_t do_get_status(struct usb_dev* dev, struct usb_ctrlrequest* setup) { switch (setup->bRequestType) { case 0x80: // Device // FIXME: send device status break; case 0x81: // Interface // FIXME: send interface status break; case 0x82: // End Point // FIXME: send endpoint status break; default: // Invalid Command endpoint_stall(&dev->ep[0]); // Stall End Point 0 break; } return STATUS_STAGE; } static ep0_stage_t do_clear_feature(struct usb_dev* dev, struct usb_ctrlrequest* setup) { switch (setup->bRequestType) { case 0x00: // Device if ((le16_to_cpu(setup->wValue) & 0xff) == 1) dev->remote_wakeup_en = 0; else endpoint_stall(&dev->ep[0]); break; case 0x02: // End Point if ((le16_to_cpu(setup->wValue) & 0xff) == 0) { endpoint_t *ep = epaddr_to_ep(dev, le16_to_cpu(setup->wIndex) & 0xff); endpoint_unstall(ep); endpoint_reset_datatoggle(ep); } else endpoint_stall(&dev->ep[0]); break; } return SETUP_STAGE; } static ep0_stage_t do_reserved(struct usb_dev* dev, struct usb_ctrlrequest* setup) { // Invalid request, stall End Point 0 endpoint_stall(&dev->ep[0]); return SETUP_STAGE; } static ep0_stage_t do_set_feature(struct usb_dev* dev, struct usb_ctrlrequest* setup) { switch (setup->bRequestType) { case 0x00: // Device if ((le16_to_cpu(setup->wValue) & 0xff) == 1) dev->remote_wakeup_en = 1; else endpoint_stall(&dev->ep[0]); break; case 0x02: // End Point if ((le16_to_cpu(setup->wValue) & 0xff) == 0) { endpoint_t *ep = epaddr_to_ep(dev, le16_to_cpu(setup->wIndex) & 0xff); endpoint_stall(ep); } else endpoint_stall(&dev->ep[0]); break; } return SETUP_STAGE; } static ep0_stage_t do_set_address(struct usb_dev* dev, struct usb_ctrlrequest* setup) { int new_state = dev->state; int new_addr = le16_to_cpu(setup->wValue); dbg("%s: our address=%d", __FUNCTION__, new_addr); if (new_addr > 127) { // usb spec doesn't tell us what to do, so just go to // default state new_state = DEFAULT; dev->address = 0; } else if (dev->address != new_addr) { dev->address = new_addr; new_state = ADDRESS; } if (dev->state != new_state) { dev->state = new_state; /* inform function layer of usbdev state change */ dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data); } return SETUP_STAGE; } static ep0_stage_t do_get_descriptor(struct usb_dev* dev, struct usb_ctrlrequest* setup) { int strnum, desc_len = le16_to_cpu(setup->wLength); switch (le16_to_cpu(setup->wValue) >> 8) { case USB_DT_DEVICE: // send device descriptor! desc_len = desc_len > dev->dev_desc->bLength ? dev->dev_desc->bLength : desc_len; dbg("sending device desc, size=%d", desc_len); send_packet(dev, alloc_packet(&dev->ep[0], desc_len, dev->dev_desc), 0); break; case USB_DT_CONFIG: // If the config descr index in low-byte of // setup->wValue is valid, send config descr, // otherwise stall ep0. if ((le16_to_cpu(setup->wValue) & 0xff) == 0) { // send config descriptor! if (desc_len <= USB_DT_CONFIG_SIZE) { dbg("sending partial config desc, size=%d", desc_len); send_packet(dev, alloc_packet(&dev->ep[0], desc_len, dev->conf_desc), 0); } else { int len = le16_to_cpu(dev->conf_desc->wTotalLength); dbg("sending whole config desc," " size=%d, our size=%d", desc_len, len); desc_len = desc_len > len ? len : desc_len; send_packet(dev, alloc_packet(&dev->ep[0], desc_len, dev->full_conf_desc), 0); } } else endpoint_stall(&dev->ep[0]); break; case USB_DT_STRING: // If the string descr index in low-byte of setup->wValue // is valid, send string descr, otherwise stall ep0. strnum = le16_to_cpu(setup->wValue) & 0xff; if (strnum >= 0 && strnum < 6) { struct usb_string_descriptor *desc = dev->str_desc[strnum]; desc_len = desc_len > desc->bLength ? desc->bLength : desc_len; dbg("sending string desc %d", strnum); send_packet(dev, alloc_packet(&dev->ep[0], desc_len, desc), 0); } else endpoint_stall(&dev->ep[0]); break; default: // Invalid request err("invalid get desc=%d, stalled", le16_to_cpu(setup->wValue) >> 8); endpoint_stall(&dev->ep[0]); // Stall endpoint 0 break; } return STATUS_STAGE; } static ep0_stage_t do_set_descriptor(struct usb_dev* dev, struct usb_ctrlrequest* setup) { // TODO: implement // there will be an OUT data stage (the descriptor to set) return DATA_STAGE; } static ep0_stage_t do_get_configuration(struct usb_dev* dev, struct usb_ctrlrequest* setup) { // send dev->configuration dbg("sending config"); send_packet(dev, alloc_packet(&dev->ep[0], 1, &dev->configuration), 0); return STATUS_STAGE; } static ep0_stage_t do_set_configuration(struct usb_dev* dev, struct usb_ctrlrequest* setup) { // set active config to low-byte of setup->wValue dev->configuration = le16_to_cpu(setup->wValue) & 0xff; dbg("set config, config=%d", dev->configuration); if (!dev->configuration && dev->state > DEFAULT) { dev->state = ADDRESS; /* inform function layer of usbdev state change */ dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data); } else if (dev->configuration == 1) { dev->state = CONFIGURED; /* inform function layer of usbdev state change */ dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data); } else { // FIXME: "respond with request error" - how? } return SETUP_STAGE; } static ep0_stage_t do_get_interface(struct usb_dev* dev, struct usb_ctrlrequest* setup) { // interface must be zero. if ((le16_to_cpu(setup->wIndex) & 0xff) || dev->state == ADDRESS) { // FIXME: respond with "request error". how? } else if (dev->state == CONFIGURED) { // send dev->alternate_setting dbg("sending alt setting"); send_packet(dev, alloc_packet(&dev->ep[0], 1, &dev->alternate_setting), 0); } return STATUS_STAGE; } static ep0_stage_t do_set_interface(struct usb_dev* dev, struct usb_ctrlrequest* setup) { if (dev->state == ADDRESS) { // FIXME: respond with "request error". how? } else if (dev->state == CONFIGURED) { dev->interface = le16_to_cpu(setup->wIndex) & 0xff; dev->alternate_setting = le16_to_cpu(setup->wValue) & 0xff; // interface and alternate_setting must be zero if (dev->interface || dev->alternate_setting) { // FIXME: respond with "request error". how? } } return SETUP_STAGE; } static ep0_stage_t do_synch_frame(struct usb_dev* dev, struct usb_ctrlrequest* setup) { // TODO return SETUP_STAGE; } typedef ep0_stage_t (*req_method_t)(struct usb_dev* dev, struct usb_ctrlrequest* setup); /* Table of the standard device request handlers */ static const req_method_t req_method[] = { do_get_status, do_clear_feature, do_reserved, do_set_feature, do_reserved, do_set_address, do_get_descriptor, do_set_descriptor, do_get_configuration, do_set_configuration, do_get_interface, do_set_interface, do_synch_frame }; // SETUP packet request dispatcher static void do_setup (struct usb_dev* dev, struct usb_ctrlrequest* setup) { req_method_t m; dbg("%s: req %d %s", __FUNCTION__, setup->bRequestType, get_std_req_name(setup->bRequestType)); if ((setup->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD || (setup->bRequestType & USB_RECIP_MASK) != USB_RECIP_DEVICE) { err("%s: invalid requesttype 0x%02x", __FUNCTION__, setup->bRequestType); return; } if ((setup->bRequestType & 0x80) == USB_DIR_OUT && setup->wLength) dbg("%s: OUT phase! length=%d", __FUNCTION__, setup->wLength); if (setup->bRequestType < sizeof(req_method)/sizeof(req_method_t)) m = req_method[setup->bRequestType]; else m = do_reserved; dev->ep0_stage = (*m)(dev, setup); } /* * A SETUP, DATA0, or DATA1 packet has been received * on the default control endpoint's fifo. */ static void process_ep0_receive (struct usb_dev* dev) { endpoint_t *ep0 = &dev->ep[0]; usbdev_pkt_t *pkt; spin_lock(&ep0->lock); // complete packet and prepare a new packet pkt = receive_packet_complete(ep0); if (!pkt) { // FIXME: should put a warn/err here. spin_unlock(&ep0->lock); return; } // unlink immediately from endpoint. unlink_head(&ep0->outlist); // override current stage if h/w says it's a setup packet if (pkt->status & PKT_STATUS_SU) dev->ep0_stage = SETUP_STAGE; switch (dev->ep0_stage) { case SETUP_STAGE: vdbg("SU bit is %s in setup stage", (pkt->status & PKT_STATUS_SU) ? "set" : "not set"); if (pkt->size == sizeof(struct usb_ctrlrequest)) { #ifdef VDEBUG if (pkt->status & PKT_STATUS_ACK) vdbg("received SETUP"); else vdbg("received NAK SETUP"); #endif do_setup(dev, (struct usb_ctrlrequest*)pkt->payload); } else err("%s: wrong size SETUP received", __FUNCTION__); break; case DATA_STAGE: /* * this setup has an OUT data stage. Of the standard * device requests, only set_descriptor has this stage, * so this packet is that descriptor. TODO: drop it for * now, set_descriptor not implemented. * * Need to place a byte in the write FIFO here, to prepare * to send a zero-length DATA ack packet to the host in the * STATUS stage. */ au_writel(0, ep0->reg->write_fifo); dbg("received OUT stage DATAx on EP0, size=%d", pkt->size); dev->ep0_stage = SETUP_STAGE; break; case STATUS_STAGE: // this setup had an IN data stage, and host is ACK'ing // the packet we sent during that stage. if (pkt->size != 0) warn("received non-zero ACK on EP0??"); #ifdef VDEBUG else vdbg("received ACK on EP0"); #endif dev->ep0_stage = SETUP_STAGE; break; } spin_unlock(&ep0->lock); // we're done processing the packet, free it kfree(pkt); } /* * A DATA0/1 packet has been received on one of the OUT endpoints (4 or 5) */ static void process_ep_receive (struct usb_dev* dev, endpoint_t *ep) { usbdev_pkt_t *pkt; spin_lock(&ep->lock); pkt = receive_packet_complete(ep); spin_unlock(&ep->lock); dev->func_cb(CB_PKT_COMPLETE, (unsigned long)pkt, dev->cb_data); } /* This ISR handles the receive complete and suspend events */ static void req_sus_intr (int irq, void *dev_id, struct pt_regs *regs) { struct usb_dev *dev = (struct usb_dev *) dev_id; u32 status; status = au_readl(USBD_INTSTAT); au_writel(status, USBD_INTSTAT); // ack'em if (status & (1<<0)) process_ep0_receive(dev); if (status & (1<<4)) process_ep_receive(dev, &dev->ep[4]); if (status & (1<<5)) process_ep_receive(dev, &dev->ep[5]); } /* This ISR handles the DMA done events on EP0 */ static void dma_done_ep0_intr(int irq, void *dev_id, struct pt_regs *regs) { struct usb_dev *dev = (struct usb_dev *) dev_id; usbdev_pkt_t* pkt; endpoint_t *ep0 = &dev->ep[0]; u32 cs0, buff_done; spin_lock(&ep0->lock); cs0 = au_readl(ep0->reg->ctrl_stat); // first check packet transmit done if ((buff_done = get_dma_buffer_done(ep0->indma)) != 0) { // transmitted a DATAx packet during DATA stage // on control endpoint 0 // clear DMA done bit if (buff_done & DMA_D0) clear_dma_done0(ep0->indma); if (buff_done & DMA_D1) clear_dma_done1(ep0->indma); pkt = send_packet_complete(ep0); kfree(pkt); } /* * Now check packet receive done. Shouldn't get these, * the receive packet complete intr should happen * before the DMA done intr occurs. */ if ((buff_done = get_dma_buffer_done(ep0->outdma)) != 0) { // clear DMA done bit if (buff_done & DMA_D0) clear_dma_done0(ep0->outdma); if (buff_done & DMA_D1) clear_dma_done1(ep0->outdma); //process_ep0_receive(dev); } spin_unlock(&ep0->lock); } /* This ISR handles the DMA done events on endpoints 2,3,4,5 */ static void dma_done_ep_intr(int irq, void *dev_id, struct pt_regs *regs) { struct usb_dev *dev = (struct usb_dev *) dev_id; int i; for (i = 2; i < 6; i++) { u32 buff_done; usbdev_pkt_t* pkt; endpoint_t *ep = &dev->ep[i]; if (!ep->active) continue; spin_lock(&ep->lock); if (ep->direction == USB_DIR_IN) { buff_done = get_dma_buffer_done(ep->indma); if (buff_done != 0) { // transmitted a DATAx pkt on the IN ep // clear DMA done bit if (buff_done & DMA_D0) clear_dma_done0(ep->indma); if (buff_done & DMA_D1) clear_dma_done1(ep->indma); pkt = send_packet_complete(ep); spin_unlock(&ep->lock); dev->func_cb(CB_PKT_COMPLETE, (unsigned long)pkt, dev->cb_data); spin_lock(&ep->lock); } } else { /* * Check packet receive done (OUT ep). Shouldn't get * these, the rx packet complete intr should happen * before the DMA done intr occurs. */ buff_done = get_dma_buffer_done(ep->outdma); if (buff_done != 0) { // received a DATAx pkt on the OUT ep // clear DMA done bit if (buff_done & DMA_D0) clear_dma_done0(ep->outdma); if (buff_done & DMA_D1) clear_dma_done1(ep->outdma); //process_ep_receive(dev, ep); } } spin_unlock(&ep->lock); } } /*************************************************************************** * Here begins the external interface functions *************************************************************************** */ /* * allocate a new packet */ int usbdev_alloc_packet(int ep_addr, int data_size, usbdev_pkt_t** pkt) { endpoint_t * ep = epaddr_to_ep(&usbdev, ep_addr); usbdev_pkt_t* lpkt = NULL; if (!ep || !ep->active || ep->address < 2) return -ENODEV; if (data_size > ep->max_pkt_size) return -EINVAL; lpkt = *pkt = alloc_packet(ep, data_size, NULL); if (!lpkt) return -ENOMEM; return 0; } /* * packet send */ int usbdev_send_packet(int ep_addr, usbdev_pkt_t * pkt) { unsigned long flags; int count; endpoint_t * ep; if (!pkt || !(ep = epaddr_to_ep(&usbdev, pkt->ep_addr)) || !ep->active || ep->address < 2) return -ENODEV; if (ep->direction != USB_DIR_IN) return -EINVAL; spin_lock_irqsave(&ep->lock, flags); count = send_packet(&usbdev, pkt, 1); spin_unlock_irqrestore(&ep->lock, flags); return count; } /* * packet receive */ int usbdev_receive_packet(int ep_addr, usbdev_pkt_t** pkt) { unsigned long flags; usbdev_pkt_t* lpkt = NULL; endpoint_t *ep = epaddr_to_ep(&usbdev, ep_addr); if (!ep || !ep->active || ep->address < 2) return -ENODEV; if (ep->direction != USB_DIR_OUT) return -EINVAL; spin_lock_irqsave(&ep->lock, flags); if (ep->outlist.count > 1) lpkt = unlink_head(&ep->outlist); spin_unlock_irqrestore(&ep->lock, flags); if (!lpkt) { /* no packet available */ *pkt = NULL; return -ENODATA; } *pkt = lpkt; return lpkt->size; } /* * return total queued byte count on the endpoint. */ int usbdev_get_byte_count(int ep_addr) { unsigned long flags; pkt_list_t *list; usbdev_pkt_t *scan; int count = 0; endpoint_t * ep = epaddr_to_ep(&usbdev, ep_addr); if (!ep || !ep->active || ep->address < 2) return -ENODEV; if (ep->direction == USB_DIR_IN) { list = &ep->inlist; spin_lock_irqsave(&ep->lock, flags); for (scan = list->head; scan; scan = scan->next) count += scan->size; spin_unlock_irqrestore(&ep->lock, flags); } else { list = &ep->outlist; spin_lock_irqsave(&ep->lock, flags); if (list->count > 1) { for (scan = list->head; scan != list->tail; scan = scan->next) count += scan->size; } spin_unlock_irqrestore(&ep->lock, flags); } return count; } void usbdev_exit(void) { endpoint_t *ep; int i; au_writel(0, USBD_INTEN); // disable usb dev ints au_writel(0, USBD_ENABLE); // disable usb dev free_irq(AU1000_USB_DEV_REQ_INT, &usbdev); free_irq(AU1000_USB_DEV_SUS_INT, &usbdev); // free all control endpoint resources ep = &usbdev.ep[0]; free_au1000_dma(ep->indma); free_au1000_dma(ep->outdma); endpoint_flush(ep); // free ep resources for (i = 2; i < 6; i++) { ep = &usbdev.ep[i]; if (!ep->active) continue; if (ep->direction == USB_DIR_IN) { free_au1000_dma(ep->indma); } else { free_au1000_dma(ep->outdma); } endpoint_flush(ep); } kfree(usbdev.full_conf_desc); } int usbdev_init(struct usb_device_descriptor* dev_desc, struct usb_config_descriptor* config_desc, struct usb_interface_descriptor* if_desc, struct usb_endpoint_descriptor* ep_desc, struct usb_string_descriptor* str_desc[], void (*cb)(usbdev_cb_type_t, unsigned long, void *), void* cb_data) { endpoint_t *ep0; int i, ret=0; u8* fcd; if (dev_desc->bNumConfigurations > 1 || config_desc->bNumInterfaces > 1 || if_desc->bNumEndpoints > 4) { err("Only one config, one i/f, and no more " "than 4 ep's allowed"); ret = -EINVAL; goto out; } if (!cb) { err("Function-layer callback required"); ret = -EINVAL; goto out; } if (dev_desc->bMaxPacketSize0 != USBDEV_EP0_MAX_PACKET_SIZE) { warn("EP0 Max Packet size must be %d", USBDEV_EP0_MAX_PACKET_SIZE); dev_desc->bMaxPacketSize0 = USBDEV_EP0_MAX_PACKET_SIZE; } memset(&usbdev, 0, sizeof(struct usb_dev)); usbdev.state = DEFAULT; usbdev.dev_desc = dev_desc; usbdev.if_desc = if_desc; usbdev.conf_desc = config_desc; for (i=0; i<6; i++) usbdev.str_desc[i] = str_desc[i]; usbdev.func_cb = cb; usbdev.cb_data = cb_data; /* Initialize default control endpoint */ ep0 = &usbdev.ep[0]; ep0->active = 1; ep0->type = CONTROL_EP; ep0->max_pkt_size = USBDEV_EP0_MAX_PACKET_SIZE; spin_lock_init(&ep0->lock); ep0->desc = NULL; // ep0 has no descriptor ep0->address = 0; ep0->direction = 0; ep0->reg = &ep_reg[0]; /* Initialize the other requested endpoints */ for (i = 0; i < if_desc->bNumEndpoints; i++) { struct usb_endpoint_descriptor* epd = &ep_desc[i]; endpoint_t *ep; if ((epd->bEndpointAddress & 0x80) == USB_DIR_IN) { ep = &usbdev.ep[2]; ep->address = 2; if (ep->active) { ep = &usbdev.ep[3]; ep->address = 3; if (ep->active) { err("too many IN ep's requested"); ret = -ENODEV; goto out; } } } else { ep = &usbdev.ep[4]; ep->address = 4; if (ep->active) { ep = &usbdev.ep[5]; ep->address = 5; if (ep->active) { err("too many OUT ep's requested"); ret = -ENODEV; goto out; } } } ep->active = 1; epd->bEndpointAddress &= ~0x0f; epd->bEndpointAddress |= (u8)ep->address; ep->direction = epd->bEndpointAddress & 0x80; ep->type = epd->bmAttributes & 0x03; ep->max_pkt_size = le16_to_cpu(epd->wMaxPacketSize); spin_lock_init(&ep->lock); ep->desc = epd; ep->reg = &ep_reg[ep->address]; } /* * initialize the full config descriptor */ usbdev.full_conf_desc = fcd = kmalloc(le16_to_cpu(config_desc->wTotalLength), ALLOC_FLAGS); if (!fcd) { err("failed to alloc full config descriptor"); ret = -ENOMEM; goto out; } memcpy(fcd, config_desc, USB_DT_CONFIG_SIZE); fcd += USB_DT_CONFIG_SIZE; memcpy(fcd, if_desc, USB_DT_INTERFACE_SIZE); fcd += USB_DT_INTERFACE_SIZE; for (i = 0; i < if_desc->bNumEndpoints; i++) { memcpy(fcd, &ep_desc[i], USB_DT_ENDPOINT_SIZE); fcd += USB_DT_ENDPOINT_SIZE; } /* Now we're ready to enable the controller */ au_writel(0x0002, USBD_ENABLE); udelay(100); au_writel(0x0003, USBD_ENABLE); udelay(100); /* build and send config table based on ep descriptors */ for (i = 0; i < 6; i++) { endpoint_t *ep; if (i == 1) continue; // skip dummy ep ep = &usbdev.ep[i]; if (ep->active) { au_writel((ep->address << 4) | 0x04, USBD_CONFIG); au_writel(((ep->max_pkt_size & 0x380) >> 7) | (ep->direction >> 4) | (ep->type << 4), USBD_CONFIG); au_writel((ep->max_pkt_size & 0x7f) << 1, USBD_CONFIG); au_writel(0x00, USBD_CONFIG); au_writel(ep->address, USBD_CONFIG); } else { u8 dir = (i==2 || i==3) ? DIR_IN : DIR_OUT; au_writel((i << 4) | 0x04, USBD_CONFIG); au_writel(((16 & 0x380) >> 7) | dir | (BULK_EP << 4), USBD_CONFIG); au_writel((16 & 0x7f) << 1, USBD_CONFIG); au_writel(0x00, USBD_CONFIG); au_writel(i, USBD_CONFIG); } } /* * Enable Receive FIFO Complete interrupts only. Transmit * complete is being handled by the DMA done interrupts. */ au_writel(0x31, USBD_INTEN); /* * Controller is now enabled, request DMA and IRQ * resources. */ /* request the USB device transfer complete interrupt */ if (request_irq(AU1000_USB_DEV_REQ_INT, req_sus_intr, SA_INTERRUPT, "USBdev req", &usbdev)) { err("Can't get device request intr"); ret = -ENXIO; goto out; } /* request the USB device suspend interrupt */ if (request_irq(AU1000_USB_DEV_SUS_INT, req_sus_intr, SA_INTERRUPT, "USBdev sus", &usbdev)) { err("Can't get device suspend intr"); ret = -ENXIO; goto out; } /* Request EP0 DMA and IRQ */ if ((ep0->indma = request_au1000_dma(ep_dma_id[0].id, ep_dma_id[0].str, dma_done_ep0_intr, SA_INTERRUPT, &usbdev)) < 0) { err("Can't get %s DMA", ep_dma_id[0].str); ret = -ENXIO; goto out; } if ((ep0->outdma = request_au1000_dma(ep_dma_id[1].id, ep_dma_id[1].str, NULL, 0, NULL)) < 0) { err("Can't get %s DMA", ep_dma_id[1].str); ret = -ENXIO; goto out; } // Flush the ep0 buffers and FIFOs endpoint_flush(ep0); // start packet reception on ep0 kickstart_receive_packet(ep0); /* Request DMA and IRQ for the other endpoints */ for (i = 2; i < 6; i++) { endpoint_t *ep = &usbdev.ep[i]; if (!ep->active) continue; // Flush the endpoint buffers and FIFOs endpoint_flush(ep); if (ep->direction == USB_DIR_IN) { ep->indma = request_au1000_dma(ep_dma_id[ep->address].id, ep_dma_id[ep->address].str, dma_done_ep_intr, SA_INTERRUPT, &usbdev); if (ep->indma < 0) { err("Can't get %s DMA", ep_dma_id[ep->address].str); ret = -ENXIO; goto out; } } else { ep->outdma = request_au1000_dma(ep_dma_id[ep->address].id, ep_dma_id[ep->address].str, NULL, 0, NULL); if (ep->outdma < 0) { err("Can't get %s DMA", ep_dma_id[ep->address].str); ret = -ENXIO; goto out; } // start packet reception on OUT endpoint kickstart_receive_packet(ep); } } out: if (ret) usbdev_exit(); return ret; } EXPORT_SYMBOL(usbdev_init); EXPORT_SYMBOL(usbdev_exit); EXPORT_SYMBOL(usbdev_alloc_packet); EXPORT_SYMBOL(usbdev_receive_packet); EXPORT_SYMBOL(usbdev_send_packet); EXPORT_SYMBOL(usbdev_get_byte_count);