/* Copyright (c) 2012, Philippe Retornaz Copyright (c) 2012, EPFL STI IMT LSRO1 -- Mobots group All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. */ #include "contiki.h" #include "usb-arch.h" #include "port2.h" #include "cc253x.h" #include "sfr-bits.h" #include "port.h" #include "dma.h" // P1_0 #define USB_PULLUP_PORT 1 #define USB_PULLUP_PIN 0 #define USBCTRL_USB_EN (1 << 0) #define USBCTRL_PLL_EN (1 << 1) #define USBCTRL_PLL_LOCKED (1 << 7) #define USBIIE_EP0IE (1 << 0) #define USBIIE_INEPxIE(x) (1 << x) #define USBOIE_OUEPxIE(x) (1 << x) #define USBCSxH_ISO (1 << 6) #define USBCS0_CLR_SETUP_END (1 << 7) #define USBCS0_CLR_OUTPKT_RDY (1 << 6) #define USBCS0_SEND_STALL (1 << 5) #define USBCS0_SETUP_END (1 << 4) #define USBCS0_DATA_END (1 << 3) #define USBCS0_SENT_STALL (1 << 2) #define USBCS0_INPKT_RDY (1 << 1) #define USBCS0_OUTPKT_RDY (1 << 0) #define USBCSIL_SENT_STALL (1 << 5) #define USBCSIL_SEND_STALL (1 << 4) #define USBCSIL_FLUSH_PACKET (1 << 3) #define USBCSIL_UNDERRUN (1 << 2) #define USBCSIL_PKT_PRESENT (1 << 1) #define USBCSIL_INPKT_RDY (1 << 0) #define USBCSOL_SENT_STALL (1 << 6) #define USBCSOL_SEND_STALL (1 << 5) #define USBCSOL_OVERRUN (1 << 2) #define USBCSOL_OUTPKT_RDY (1 << 0) // Double buffering not used // We assume only the IN or the OUT of // each endpoint is enabled and not both. #if CTRL_EP_SIZE > 32 #error Control endpoint size too big #endif #if USB_EP1_SIZE > 32 #error Endpoint 1 size too big #endif #if USB_EP2_SIZE > 64 #error Endpoint 2 size too big #endif #if USB_EP3_SIZE > 128 #error Endpoint 3 size too big #endif #if USB_EP4_SIZE > 256 #error Endpoint 4 size too big #endif #if USB_EP5_SIZE > 512 #error Endpoint 5 size too big #endif static const uint16_t ep_xfer_size[] = { CTRL_EP_SIZE, USB_EP1_SIZE, USB_EP2_SIZE, USB_EP3_SIZE, USB_EP4_SIZE, USB_EP5_SIZE, }; #define EP_IDLE 0 #define EP_RX 1 #define EP_TX 2 static uint8_t ep0status; typedef struct _USBEndpoint USBEndpoint; struct _USBEndpoint { uint8_t halted; uint8_t addr; uint8_t flags; USBBuffer *buffer; /* NULL if no current buffer */ struct process *event_process; unsigned int events; uint16_t xfer_size; }; #define USB_EP_FLAGS_TYPE_MASK 0x03 #define USB_EP_FLAGS_TYPE_BULK 0x00 #define USB_EP_FLAGS_TYPE_CONTROL 0x01 #define USB_EP_FLAGS_TYPE_ISO 0x02 #define USB_EP_FLAGS_TYPE_INTERRUPT 0x03 #define USB_EP_FLAGS_ENABLED 0x04 #define EP_TYPE(ep) ((ep)->flags & USB_EP_FLAGS_TYPE_MASK) #define IS_EP_TYPE(ep, type) (EP_TYPE(ep) == (type)) #define IS_CONTROL_EP(ep) IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_CONTROL) #define IS_BULK_EP(ep) IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_BULK) #define IS_INTERRUPT_EP(ep) IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_INTERRUPT) #define IS_ISO_EP(ep) IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_ISO) #define USB_WRITE_BLOCK 0x01 #define USB_READ_BLOCK 0x01 /* The currently submitted buffers can't hold the received data, wait for more buffers. No data was read from the hardware buffer */ #define USB_WRITE_NOTIFY 0x02 #define USB_READ_NOTIFY 0x02 /* Some buffers that had the USB_BUFFER_NOTIFY flags set were released */ #define USB_READ_FAIL 0x4 /* Index in endpoint array */ #define EP_INDEX(addr) ((addr) & 0x7f) /* Get address of endpoint struct */ #define EP_STRUCT(addr) &usb_endpoints[EP_INDEX(addr)]; /* Number of hardware endpoint */ #define EP_HW_NUM(addr) ((addr) & 0x7f) #define usb_irq_disable(flag) cc253x_p2_irq_disable(flag) #define usb_irq_enable(flag) cc253x_p2_irq_enable(flag) static uint8_t usb_irq(void); static void usb_arch_epin_irq(uint8_t ep_hw); static void usb_arch_epout_irq(uint8_t ep_hw); static void usb_arch_ep0_irq(void); static struct cc253x_p2_handler usb_irq_handler = { NULL, usb_irq }; static USBEndpoint usb_endpoints[USB_MAX_ENDPOINTS]; struct process *event_process = 0; volatile static unsigned int events = 0; static uint8_t ep0_tx(void); static uint8_t ep_tx(uint8_t ep_hw); static void notify_process(unsigned int e) { events |= e; if(event_process) { process_poll(event_process); } } static void notify_ep_process(USBEndpoint * ep, unsigned int e) { ep->events |= e; if(ep->event_process) { process_poll(ep->event_process); } } void usb_set_ep_event_process(unsigned char addr, struct process *p) { USBEndpoint *ep = EP_STRUCT(addr); ep->event_process = p; } void usb_arch_set_global_event_process(struct process *p) { event_process = p; } unsigned int usb_arch_get_global_events(void) { uint8_t flag; volatile unsigned int e; usb_irq_disable(flag); e = events; events = 0; usb_irq_enable(flag); return e; } unsigned int usb_get_ep_events(uint8_t addr) { volatile unsigned int e; uint8_t flag; USBEndpoint *ep = EP_STRUCT(addr); usb_irq_disable(flag); e = ep->events; ep->events = 0; usb_irq_enable(flag); return e; } #if DMA_ON #ifndef DMA_USB_CHANNEL #error You must set DMA_USB_CHANNEL to a valid dma channel. #endif static void read_hw_buffer_dma(uint8_t tl, uint8_t th, uint8_t __xdata * xptr, unsigned int len) { dma_conf[DMA_USB_CHANNEL].src_h = ((uint16_t) xptr) >> 8; dma_conf[DMA_USB_CHANNEL].src_l = ((uint16_t) xptr) & 0xFF; dma_conf[DMA_USB_CHANNEL].dst_h = th; dma_conf[DMA_USB_CHANNEL].dst_l = tl; // Maximum USB len transfert is 512bytes, maximum DMA len: 4096, we are safe. dma_conf[DMA_USB_CHANNEL].len_h = len >> 8; dma_conf[DMA_USB_CHANNEL].len_l = len & 0xFF; dma_conf[DMA_USB_CHANNEL].wtt = DMA_T_NONE | DMA_BLOCK; // Maximum prio, we will be polling until the transfert is done. dma_conf[DMA_USB_CHANNEL].inc_prio = DMA_SRC_INC_NO | DMA_DST_INC_1 | DMA_PRIO_HIGH; DMA_ARM(DMA_USB_CHANNEL); // Wait until the channel is armed while(!(DMAARM & (1 << DMA_USB_CHANNEL))); DMA_TRIGGER(DMA_USB_CHANNEL); // Wait until the transfert is done. // For some unknown reason, the DMA channel do not set the IRQ flag // sometimes, so use the DMAARM bit to check if transfert is done while(DMAARM & (1 << DMA_USB_CHANNEL)); // Clear interrupt flag DMAIRQ = ~(1 << DMA_USB_CHANNEL); } static void write_hw_buffer_dma(uint8_t __xdata * xptr, uint8_t fl, uint8_t fh, unsigned int len) { dma_conf[DMA_USB_CHANNEL].src_h = fh; dma_conf[DMA_USB_CHANNEL].src_l = fl; dma_conf[DMA_USB_CHANNEL].dst_h = ((uint16_t) xptr) >> 8; dma_conf[DMA_USB_CHANNEL].dst_l = ((uint16_t) xptr) & 0xFF; // Maximum USB len transfert is 512bytes, maximum DMA len: 4096, we are safe. dma_conf[DMA_USB_CHANNEL].len_h = len >> 8; dma_conf[DMA_USB_CHANNEL].len_l = len & 0xFF; dma_conf[DMA_USB_CHANNEL].wtt = DMA_T_NONE | DMA_BLOCK; // Maximum prio, we will be polling until the transfert is done. dma_conf[DMA_USB_CHANNEL].inc_prio = DMA_SRC_INC_1 | DMA_DST_INC_NO | DMA_PRIO_HIGH; DMA_ARM(DMA_USB_CHANNEL); // Wait until the channel is armed while(!(DMAARM & (1 << DMA_USB_CHANNEL))); DMA_TRIGGER(DMA_USB_CHANNEL); // Wait until the transfert is done. while(DMAARM & (1 << DMA_USB_CHANNEL)); // Clear interrupt flag DMAIRQ = ~(1 << DMA_USB_CHANNEL); } #endif static void read_hw_buffer(uint8_t * to, uint8_t hw_ep, unsigned int len) { __xdata uint8_t *from = &USBF0 + (hw_ep << 1); #if DMA_ON // For small transfers we use PIO // This check is specific to SDCC and large/huge memory model if(len > 8 && ((uint8_t *) & to)[2] == 0x0 /* x data pointer */ ) { read_hw_buffer_dma(((uint8_t *) & to)[0], ((uint8_t *) & to)[1], from, len); return; } #endif while(len--) { *to++ = *from; } } static void write_hw_buffer(uint8_t hw_ep, uint8_t * from, unsigned int len) { __xdata uint8_t *to = &USBF0 + (hw_ep << 1); #if DMA_ON // For small transfers we use PIO if(len > 8 && ((uint8_t *) & from)[2] == 0x0 /* x data pointer */ ) { write_hw_buffer_dma(to, ((uint8_t *) & from)[0], ((uint8_t *) & from)[1], len); return; } #endif while(len--) { *to = *from++; } } static void usb_arch_reset(void) { uint8_t e; for(e = 0; e < USB_MAX_ENDPOINTS; e++) { if(usb_endpoints[e].flags & USB_EP_FLAGS_ENABLED) { USBBuffer *buffer = usb_endpoints[e].buffer; usb_endpoints[e].flags = 0; usb_disable_endpoint(e); while(buffer) { buffer->flags &= ~USB_BUFFER_SUBMITTED; buffer = buffer->next; } } } usb_arch_setup_control_endpoint(0); } /* Init USB */ void usb_arch_setup(void) { uint8_t i; /* Switch on USB PLL & USB module */ USBCTRL = USBCTRL_USB_EN | USBCTRL_PLL_EN; /* Wait until USB PLL is stable */ while(!(USBCTRL & USBCTRL_PLL_LOCKED)); /* Enable pull-up on usb port */ PORT_SET(USB_PULLUP_PORT, USB_PULLUP_PIN); PORT_DIR_OUTPUT(USB_PULLUP_PORT, USB_PULLUP_PIN); for(i = 0; i < USB_MAX_ENDPOINTS; i++) { usb_endpoints[i].flags = 0; usb_endpoints[i].event_process = 0; } usb_arch_reset(); // Disable all endpoints interrupts // Enpoint 0 interrupt will be enabled later USBIIE = 0; USBOIE = 0; cc253x_p2_register_handler(&usb_irq_handler); // We have to force IRQ enable as we might be the first user cc253x_p2_irq_force_enable(); } void usb_submit_recv_buffer(uint8_t addr, USBBuffer * buffer) { USBBuffer **tailp; uint8_t flag; USBEndpoint *ep = EP_STRUCT(addr); if(!(ep->flags & USB_EP_FLAGS_ENABLED)) { return; } if(buffer->data == NULL && EP_HW_NUM(addr) == 0) { // the usb-core is trying to get the STATUS packet (ZLP in this case) // but the USB hardware is catching them, thus ignore this. // FIXME: Use the interrupt to release this buffer when the packet is sent // at least the timing would be better ... if(buffer->flags & USB_BUFFER_NOTIFY) { notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION); } return; } usb_irq_disable(flag); tailp = &ep->buffer; while(*tailp) { tailp = &(*tailp)->next; } *tailp = buffer; while(buffer) { buffer->flags |= USB_BUFFER_SUBMITTED; buffer = buffer->next; } USBINDEX = EP_HW_NUM(addr); if(!EP_HW_NUM(ep->addr)) { if(USBCS0 & USBCS0_OUTPKT_RDY) { usb_arch_ep0_irq(); } } else { if(USBCSOL & USBCSOL_OUTPKT_RDY) { usb_arch_epout_irq(EP_HW_NUM(ep->addr)); } } usb_irq_enable(flag); } void usb_submit_xmit_buffer(uint8_t addr, USBBuffer * buffer) { USBBuffer **tailp; uint8_t flag; uint8_t res; USBEndpoint *ep = EP_STRUCT(addr); if(!(ep->flags & USB_EP_FLAGS_ENABLED)) { return; } usb_irq_disable(flag); if(EP_HW_NUM(addr) == 0) { if(buffer->data == NULL) { // We are asked to send a STATUS packet. // But the USB hardware is doing this automatically // as soon as we release hw FIFO. USBINDEX = 0; USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END; notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION); usb_irq_enable(flag); return; } else { // Release the hw FIFO ... USBINDEX = 0; USBCS0 = USBCS0_CLR_OUTPKT_RDY; } } tailp = &ep->buffer; while(*tailp) { tailp = &(*tailp)->next; } *tailp = buffer; while(buffer) { buffer->flags |= USB_BUFFER_SUBMITTED | USB_BUFFER_IN; buffer = buffer->next; } USBINDEX = EP_HW_NUM(ep->addr); if(EP_HW_NUM(ep->addr)) { res = ep_tx(EP_HW_NUM(ep->addr)); } else { res = ep0_tx(); } usb_irq_enable(flag); if(res & USB_WRITE_NOTIFY) { notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION); } } static void usb_arch_setup_endpoint0(void) { USBIIE |= USBIIE_EP0IE; } static void usb_arch_setup_in_endpoint(uint8_t addr) { uint8_t ei = EP_HW_NUM(addr); USBEndpoint *ep = EP_STRUCT(addr); // Enable interrupt USBIIE |= USBIIE_INEPxIE(ei); // Set internal FIFO size USBMAXI = ep->xfer_size / 8; if(IS_ISO_EP(ep)) { USBCSIH |= USBCSxH_ISO; } else { USBCSIH &= ~USBCSxH_ISO; } } static void usb_arch_setup_out_endpoint(uint8_t addr) { uint8_t ei = EP_HW_NUM(addr); USBEndpoint *ep = EP_STRUCT(addr); // Enable interrupt USBOIE |= USBOIE_OUEPxIE(ei); // Set internal FIFO size USBMAXO = ep->xfer_size / 8; if(IS_ISO_EP(ep)) { USBCSOH |= USBCSxH_ISO; } else { USBCSOH &= ~USBCSxH_ISO; } } static void usb_arch_setup_endpoint(uint8_t addr) { uint8_t ei = EP_HW_NUM(addr); uint8_t flag; USBEndpoint *ep = EP_STRUCT(addr); ep->halted = 0; ep->flags |= USB_EP_FLAGS_ENABLED; ep->buffer = 0; ep->addr = addr; ep->events = 0; ep->xfer_size = ep_xfer_size[ei]; usb_irq_disable(flag); // Select endpoint banked register USBINDEX = ei; // special case for ep 0 if(ei == 0) { usb_arch_setup_endpoint0(); } else { if(addr & 0x80) { usb_arch_setup_in_endpoint(addr); } else { usb_arch_setup_out_endpoint(addr); } } usb_irq_enable(flag); } void usb_arch_setup_iso_endpoint(uint8_t addr) { USBEndpoint *ep = EP_STRUCT(addr); ep->flags = USB_EP_FLAGS_TYPE_ISO; usb_arch_setup_endpoint(addr); } void usb_arch_setup_control_endpoint(uint8_t addr) { USBEndpoint *ep = EP_STRUCT(addr); ep->flags = USB_EP_FLAGS_TYPE_CONTROL; usb_arch_setup_endpoint(addr); } void usb_arch_setup_bulk_endpoint(uint8_t addr) { USBEndpoint *ep = EP_STRUCT(addr); ep->flags = USB_EP_FLAGS_TYPE_BULK; usb_arch_setup_endpoint(addr); } void usb_arch_setup_interrupt_endpoint(uint8_t addr) { USBEndpoint *ep = EP_STRUCT(addr); ep->flags = USB_EP_FLAGS_TYPE_INTERRUPT; usb_arch_setup_endpoint(addr); } static void usb_arch_disable_ep0(void) { USBIIE &= ~USBIIE_EP0IE; USBCS0 = 0xC0; // Clear any pending status flags } static void usb_arch_disable_in_endpoint(uint8_t addr) { USBMAXI = 0; USBIIE &= ~USBIIE_INEPxIE(EP_HW_NUM(addr)); // Flush any pending packets USBCSIL = 0x08; // Double-buffering not used. Flush only once } static void usb_arch_disable_out_endpoint(uint8_t addr) { USBMAXO = 0; USBOIE &= ~USBOIE_OUEPxIE(EP_HW_NUM(addr)); // Flush any pending packets USBCSOL = 0x08; // Double buffering not used, flush only once } void usb_arch_disable_endpoint(uint8_t addr) { uint8_t ei = EP_HW_NUM(addr); uint8_t flag; USBEndpoint *ep = EP_STRUCT(addr); ep->flags &= ~USB_EP_FLAGS_ENABLED; usb_irq_disable(flag); USBINDEX = ei; if(ei == 0) { usb_arch_disable_ep0(); } else { if(addr & 0x80) { usb_arch_disable_in_endpoint(addr); } else { usb_arch_disable_out_endpoint(addr); } } usb_irq_enable(flag); } void usb_arch_discard_all_buffers(uint8_t addr) { USBBuffer *buffer; uint8_t flag; volatile USBEndpoint *ep = EP_STRUCT(addr); usb_irq_disable(flag); buffer = ep->buffer; ep->buffer = NULL; usb_irq_enable(flag); while(buffer) { buffer->flags &= ~USB_BUFFER_SUBMITTED; buffer = buffer->next; } } static void set_stall(uint8_t addr, uint8_t stall) { uint8_t ei = EP_HW_NUM(addr); USBINDEX = ei; if(ei == 0) { // Stall is automatically deasserted on EP0 if(stall) { ep0status = EP_IDLE; USBCS0 |= USBCS0_SEND_STALL | USBCS0_OUTPKT_RDY; } } else { if(addr & 0x80) { if(stall) { USBCSIL |= USBCSIL_SEND_STALL; } else { USBCSIL &= ~USBCSIL_SEND_STALL; } } else { if(stall) { USBCSOL |= USBCSOL_SEND_STALL; } else { USBCSOL &= ~USBCSOL_SEND_STALL; } } } } void usb_arch_control_stall(uint8_t addr) { uint8_t ei = EP_HW_NUM(addr); uint8_t flag; if(ei > USB_MAX_ENDPOINTS) { return; } usb_irq_disable(flag); set_stall(addr, 1); usb_irq_enable(flag); } void usb_arch_halt_endpoint(uint8_t addr, int halt) { uint8_t ei = EP_HW_NUM(addr); uint8_t flag; USBEndpoint *ep = EP_STRUCT(addr); if(ei > USB_MAX_ENDPOINTS) { return; } if(!(ep->flags & USB_EP_FLAGS_ENABLED)) { return; } usb_irq_disable(flag); if(halt) { ep->halted = 0x1; set_stall(addr, 1); } else { ep->halted = 0; set_stall(addr, 0); if(ep->buffer && (ep->buffer->flags & USB_BUFFER_HALT)) { ep->buffer->flags &= ~USB_BUFFER_SUBMITTED; if(ep->buffer->flags & USB_BUFFER_NOTIFY) { notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION); } ep->buffer = ep->buffer->next; } if(ei) { usb_arch_epout_irq(EP_HW_NUM(addr)); } } usb_irq_enable(flag); } void usb_arch_set_configuration(uint8_t usb_configuration_value) { // Nothing to do ? } uint16_t usb_arch_get_ep_status(uint8_t addr) { uint8_t ei = EP_INDEX(addr); USBEndpoint *ep = EP_STRUCT(addr); if(ei > USB_MAX_ENDPOINTS) { return 0; } return ep->halted; } void usb_arch_set_address(uint8_t addr) { USBADDR = addr; } int usb_arch_send_pending(uint8_t addr) { uint8_t flag; uint8_t ret; uint8_t ei = EP_INDEX(addr); usb_irq_disable(flag); USBINDEX = ei; if(ei == 0) { ret = USBCS0 & USBCS0_INPKT_RDY; } else { ret = USBCSIL & USBCSIL_INPKT_RDY; } usb_irq_enable(flag); return ret; } static unsigned int get_receive_capacity(USBBuffer * buffer) { unsigned int capacity = 0; while(buffer && !(buffer->flags & (USB_BUFFER_IN | USB_BUFFER_SETUP | USB_BUFFER_HALT))) { capacity += buffer->left; buffer = buffer->next; } return capacity; } static USBBuffer * skip_buffers_until(USBBuffer * buffer, unsigned int mask, unsigned int flags, uint8_t * resp) { while(buffer && !((buffer->flags & mask) == flags)) { buffer->flags &= ~USB_BUFFER_SUBMITTED; buffer->flags |= USB_BUFFER_FAILED; if(buffer->flags & USB_BUFFER_NOTIFY) { *resp |= USB_READ_NOTIFY; } buffer = buffer->next; } return buffer; } static uint8_t fill_buffers(USBBuffer * buffer, uint8_t hw_ep, unsigned int len, uint8_t short_packet) { unsigned int t; uint8_t res = 0; do { if(buffer->left < len) { t = buffer->left; } else { t = len; } len -= t; buffer->left -= t; read_hw_buffer(buffer->data, hw_ep, t); buffer->data += t; if(len == 0) { break; } buffer->flags &= ~(USB_BUFFER_SUBMITTED | USB_BUFFER_SHORT_PACKET); if(buffer->flags & USB_BUFFER_NOTIFY) { res |= USB_READ_NOTIFY; } buffer = buffer->next; } while(1); if(short_packet) { buffer->flags |= USB_BUFFER_SHORT_PACKET; } if((buffer->left == 0) || (buffer->flags & USB_BUFFER_PACKET_END)) { buffer->flags &= ~USB_BUFFER_SUBMITTED; if(buffer->flags & USB_BUFFER_NOTIFY) { res |= USB_READ_NOTIFY; } buffer = buffer->next; } else { if(short_packet) { if(buffer->left && !(buffer->flags & USB_BUFFER_SHORT_END)) { buffer->flags |= USB_BUFFER_FAILED; res |= USB_READ_FAIL; } buffer->flags &= ~USB_BUFFER_SUBMITTED; if(buffer->flags & USB_BUFFER_NOTIFY) { res |= USB_READ_NOTIFY; } buffer = buffer->next; } } usb_endpoints[hw_ep].buffer = buffer; return res; } static uint8_t ep0_get_setup_pkt(void) { // The USB controller check that the packet size is == 8 // First get a valid setup buffer uint8_t res; USBBuffer *buffer = skip_buffers_until(usb_endpoints[0].buffer, USB_BUFFER_SETUP, USB_BUFFER_SETUP, &res); usb_endpoints[0].buffer = buffer; if(!buffer || buffer->left < 8) { return USB_READ_BLOCK; } read_hw_buffer(buffer->data, 0, 8); buffer->left -= 8; buffer->flags &= ~USB_BUFFER_SUBMITTED; if(buffer->flags & USB_BUFFER_NOTIFY) { res |= USB_READ_NOTIFY; } if(buffer->data[6] || buffer->data[7]) { // DATA stage ... USBCS0 |= USBCS0_CLR_OUTPKT_RDY; ep0status = buffer->data[0] & 0x80 ? EP_TX : EP_RX; } else { // The usb-core will submit an empty data packet // This will trigger the clr (see the buffer submission routine) // USBCS0 |= USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END; } buffer->data += 8; usb_endpoints[0].buffer = buffer->next; return res; } static uint8_t ep0_get_data_pkt(void) { uint8_t res = 0; uint8_t short_packet = 0; USBBuffer *buffer = usb_endpoints[0].buffer; uint8_t len = USBCNT0; if(!buffer) { return USB_READ_BLOCK; } if(buffer->flags & (USB_BUFFER_SETUP | USB_BUFFER_IN)) { uint8_t temp; buffer->flags |= USB_BUFFER_FAILED; buffer->flags &= ~USB_BUFFER_SUBMITTED; if(buffer->flags & USB_BUFFER_NOTIFY) { res |= USB_READ_NOTIFY; } // Flush the fifo while(len--) { temp = USBF0; } usb_endpoints[0].buffer = buffer->next; // Force an end to the data stage USBCS0 |= USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END; ep0status = EP_IDLE; return res; } if(get_receive_capacity(buffer) < len) { // Wait until we queue more buffers. return USB_READ_BLOCK; } if(len < usb_endpoints[0].xfer_size) { short_packet = 1; } res = fill_buffers(buffer, 0, len, short_packet); if(short_packet) { /* The usb-core will send a status packet, we will release the fifo at this stage */ ep0status = EP_IDLE; } else { // More data to come USBCS0 |= USBCS0_CLR_OUTPKT_RDY; } return res; } static uint8_t ep0_tx(void) { USBBuffer *buffer = usb_endpoints[0].buffer; unsigned int len = usb_endpoints[0].xfer_size; uint8_t data_end = 0; uint8_t res = 0; // If TX Fifo still busy or ep0 not in TX data stage don't do anything if((USBCS0 & USBCS0_INPKT_RDY) || (ep0status != EP_TX)) { return 0; } if(!buffer) { return 0; } if(!(buffer->flags & USB_BUFFER_IN)) { return 0; // Huh .. problem ... we should TX but queued buffer is in RX ... } while(buffer) { unsigned int copy; if(buffer->left < len) { copy = buffer->left; } else { copy = len; } len -= copy; buffer->left -= copy; write_hw_buffer(0, buffer->data, copy); buffer->data += copy; if(buffer->left == 0) { if(buffer->flags & USB_BUFFER_SHORT_END) { if(len == 0) { break; // We keep the buffer in queue so we will send a ZLP next time. } else { data_end = 1; len = 0; // Stop looking for more data to send } } buffer->flags &= ~USB_BUFFER_SUBMITTED; if(buffer->flags & USB_BUFFER_NOTIFY) { res |= USB_WRITE_NOTIFY; } buffer = buffer->next; } if(len == 0) { break; // FIFO is full, send packet. } } if(len) { // Short packet will be sent. data_end = 1; } usb_endpoints[0].buffer = buffer; // Workaround the fact that the usb controller do not like to have DATA_END set after INPKT_RDY // for the last packet. Thus if no more is in the queue set DATA_END. if(data_end || !buffer) { ep0status = EP_IDLE; USBCS0 |= USBCS0_INPKT_RDY | USBCS0_DATA_END; } else { USBCS0 |= USBCS0_INPKT_RDY; } return res; } static void usb_arch_ep0_irq(void) { uint8_t cs0; uint8_t res; USBINDEX = 0; cs0 = USBCS0; if(cs0 & USBCS0_SENT_STALL) { // Ack the stall USBCS0 = 0; ep0status = EP_IDLE; } if(cs0 & USBCS0_SETUP_END) { // Clear it USBCS0 = USBCS0_CLR_SETUP_END; ep0status = EP_IDLE; } if(cs0 & USBCS0_OUTPKT_RDY) { if(ep0status == EP_IDLE) { res = ep0_get_setup_pkt(); } else { res = ep0_get_data_pkt(); } if(res & USB_READ_NOTIFY) { notify_ep_process(&usb_endpoints[0], USB_EP_EVENT_NOTIFICATION); } if(res & USB_READ_BLOCK) { return; } } // Trigger the TX path res = ep0_tx(); if(res & USB_WRITE_NOTIFY) { notify_ep_process(&usb_endpoints[0], USB_EP_EVENT_NOTIFICATION); } } static uint8_t ep_tx(uint8_t ep_hw) { unsigned int len; uint8_t res = 0; USBEndpoint *ep = EP_STRUCT(ep_hw); len = ep->xfer_size; if(ep->halted) { return 0; } if(!ep->buffer || !(ep->buffer->flags & USB_BUFFER_IN)) { return 0; } while(ep->buffer) { unsigned int copy; if(ep->buffer->left < len) { copy = ep->buffer->left; } else { copy = len; } len -= copy; ep->buffer->left -= copy; write_hw_buffer(EP_INDEX(ep_hw), ep->buffer->data, copy); ep->buffer->data += copy; if(ep->buffer->left == 0) { if(ep->buffer->flags & USB_BUFFER_SHORT_END) { if(len == 0) { break; // We keep the buffer in queue so we will send a ZLP next time. } else { len = 0; // Stop looking for more data to send } } ep->buffer->flags &= ~USB_BUFFER_SUBMITTED; if(ep->buffer->flags & USB_BUFFER_NOTIFY) { res |= USB_WRITE_NOTIFY; } ep->buffer = ep->buffer->next; } if(len == 0) break; // FIFO is full, send packet. } USBCSIL |= USBCSIL_INPKT_RDY; return res; } static uint8_t ep_get_data_pkt(uint8_t ep_hw) { uint16_t pkt_len; uint8_t res; uint8_t short_packet = 0; USBEndpoint *ep = EP_STRUCT(ep_hw); if(!ep->buffer) { return USB_READ_BLOCK; } if(ep->buffer->flags & USB_BUFFER_HALT) { ep->halted = 1; if(!(USBCSOL & USBCSOL_SEND_STALL)) { USBCSOL |= USBCSOL_SEND_STALL; } return 0; } pkt_len = USBCNTL | (USBCNTH << 8); if(get_receive_capacity(ep->buffer) < pkt_len) { return USB_READ_BLOCK; } if(pkt_len < ep->xfer_size) { short_packet = 1; } res = fill_buffers(ep->buffer, ep_hw, pkt_len, short_packet); USBCSOL &= ~USBCSOL_OUTPKT_RDY; return res; } static void usb_arch_epout_irq(uint8_t ep_hw) { // RX interrupt uint8_t csl; uint8_t res; USBEndpoint *ep = EP_STRUCT(ep_hw); USBINDEX = ep_hw; csl = USBCSOL; if(csl & USBCSOL_SENT_STALL) { USBCSOL &= ~USBCSOL_SENT_STALL; } if(csl & USBCSOL_OVERRUN) { // We lost one isochronous packet ... USBCSOL &= ~USBCSOL_OVERRUN; } if(csl & USBCSOL_OUTPKT_RDY) { res = ep_get_data_pkt(ep_hw); if(res & USB_READ_NOTIFY) { notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION); } } } static void usb_arch_epin_irq(uint8_t ep_hw) { uint8_t csl; uint8_t res; USBEndpoint *ep = EP_STRUCT(ep_hw); // TX interrupt USBINDEX = ep_hw; csl = USBCSIL; if(csl & USBCSIL_SENT_STALL) { USBCSIL &= ~USBCSIL_SENT_STALL; } if(csl & USBCSIL_UNDERRUN) { USBCSIL &= ~USBCSIL_UNDERRUN; } if(!(csl & USBCSIL_INPKT_RDY)) { res = ep_tx(ep_hw); if(res & USB_WRITE_NOTIFY) { notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION); } } } #define EPxIF(x) (1 << x) #define RSTIF (1 << 2) #define RESUMEIF (1 << 1) #define SUSPENDIF (1 << 0) uint8_t usb_irq(void) { uint8_t ep_in_if = USBIIF & USBIIE; uint8_t ep_out_if = USBOIF & USBOIE; uint8_t common_if = USBCIF & USBCIE; uint8_t i; if(ep_in_if) { /* EP0 is routed on IN irq flags */ if(ep_in_if & 0x1) { usb_arch_ep0_irq(); } for(i = 1; i < 6; i++) { if(ep_in_if & EPxIF(i)) { usb_arch_epin_irq(i); } } } if(ep_out_if) { for(i = 1; i < 6; i++) { if(ep_out_if & EPxIF(i)) { usb_arch_epout_irq(i); } } } if(common_if & RSTIF) { usb_arch_reset(); notify_process(USB_EVENT_RESET); } if(common_if & RESUMEIF) { notify_process(USB_EVENT_RESUME); } if(common_if & SUSPENDIF) { notify_process(USB_EVENT_SUSPEND); } return ep_in_if || ep_out_if || common_if ? CC253x_P2_ACK : CC253x_P2_NACK; }