/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2006 by Tomasz Malesinski * Copyright (C) 2008 by Maurus Cuelenaere * * 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 distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include "usb_ch9.h" #include "usb_drv.h" #include "usb_core.h" #include "isp1583.h" #include "thread.h" #include "logf.h" #include "stdio.h" struct usb_endpoint { unsigned char *out_buf; short out_len; short out_ptr; void (*out_done)(int, unsigned char *, int); unsigned char out_in_progress; unsigned char *in_buf; short in_min_len; short in_max_len; short in_ptr; void (*in_done)(int, unsigned char *, int); unsigned char in_ack; unsigned char halt[2]; unsigned char enabled[2]; short max_pkt_size[2]; short type; char allocation; }; static unsigned char setup_pkt_buf[8]; static struct usb_endpoint endpoints[USB_NUM_ENDPOINTS]; #if 0 #define ZVM_SPECIFIC asm volatile( \ "LDR R12, =0x50FFC000\n" \ "LDRH R12, [R12]\n" \ : : : "r12"); #else #define ZVM_SPECIFIC #endif static bool high_speed_mode = false; static inline void or_int_value(volatile unsigned short *a, volatile unsigned short *b, unsigned long r, unsigned long value) { set_int_value(*a, *b, (r | value)); } static inline void bc_int_value(volatile unsigned short *a, volatile unsigned short *b, unsigned long r, unsigned long value) { set_int_value(*a, *b, (r & ~value)); } static inline void nop_f(void) { yield(); } #define NOP asm volatile("nop\n"); static inline int ep_index(int n, bool dir) { return (n << 1) | dir; } static inline bool epidx_dir(int idx) { return idx & 1; } static inline int epidx_n(int idx) { return idx >> 1; } static inline void usb_select_endpoint(int idx) { /* Select the endpoint */ ISP1583_DFLOW_EPINDEX = idx; /* The delay time from the Write Endpoint Index register to the Read Data Port register must be at least 190 ns. * The delay time from the Write Endpoint Index register to the Write Data Port register must be at least 100 ns. */ NOP; } static inline void usb_select_setup_endpoint(void) { /* Select the endpoint */ ISP1583_DFLOW_EPINDEX = DFLOW_EPINDEX_EP0SETUP; /* The delay time from the Write Endpoint Index register to the Read Data Port register must be at least 190 ns. * The delay time from the Write Endpoint Index register to the Write Data Port register must be at least 100 ns. */ NOP; } static void usb_setup_endpoint(int idx, int max_pkt_size, int type) { if(epidx_n(idx)!=EP_CONTROL) { usb_select_endpoint(idx); ISP1583_DFLOW_MAXPKSZ = max_pkt_size & 0x7FF; ISP1583_DFLOW_EPTYPE = (DFLOW_EPTYPE_NOEMPKT | DFLOW_EPTYPE_DBLBUF | (type & 0x3)); /* clear buffer ... */ ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_CLBUF; /* ... twice because of double buffering */ usb_select_endpoint(idx); ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_CLBUF; } struct usb_endpoint *ep; ep = &(endpoints[epidx_n(idx)]); ep->halt[epidx_dir(idx)] = 0; ep->enabled[epidx_dir(idx)] = 0; ep->out_in_progress = 0; ep->in_min_len = -1; ep->in_ack = 0; ep->type = type; ep->max_pkt_size[epidx_dir(idx)] = max_pkt_size; } static void usb_enable_endpoint(int idx) { if(epidx_n(idx)!=EP_CONTROL) { usb_select_endpoint(idx); /* Enable interrupt */ or_int_value(&ISP1583_INIT_INTEN_A, &ISP1583_INIT_INTEN_B, ISP1583_INIT_INTEN_READ, 1 << (10 + idx)); /* Enable endpoint */ ISP1583_DFLOW_EPTYPE |= DFLOW_EPTYPE_ENABLE; } endpoints[epidx_n(idx)].enabled[epidx_dir(idx)] = 1; } /* static void usb_disable_endpoint(int idx, bool set_struct) { usb_select_endpoint(idx); ISP1583_DFLOW_EPTYPE &= ~DFLOW_EPTYPE_ENABLE; bc_int_value(&ISP1583_INIT_INTEN_A, &ISP1583_INIT_INTEN_B, ISP1583_INIT_INTEN_READ, 1 << (10 + idx)); if(set_struct) endpoints[epidx_n(idx)].enabled[epidx_dir(idx)] = 0; } */ static int usb_get_packet(unsigned char *buf, int max_len) { int len, i; len = ISP1583_DFLOW_BUFLEN; if (max_len < 0 || max_len > len) max_len = len; i = 0; while (i < len) { unsigned short d = ISP1583_DFLOW_DATA; if (i < max_len) buf[i] = d & 0xff; i++; if (i < max_len) buf[i] = (d >> 8) & 0xff; i++; } return max_len; } static int usb_receive(int n) { logf("usb_receive(%d)", n); int len; if (endpoints[n].halt[DIR_RX] || !endpoints[n].enabled[DIR_RX] || endpoints[n].in_min_len < 0 || !endpoints[n].in_ack) return -1; endpoints[n].in_ack = 0; usb_select_endpoint(ep_index(n, DIR_RX)); len = usb_get_packet(endpoints[n].in_buf + endpoints[n].in_ptr, endpoints[n].in_max_len - endpoints[n].in_ptr); endpoints[n].in_ptr += len; if (endpoints[n].in_ptr >= endpoints[n].in_min_len) { endpoints[n].in_min_len = -1; if (endpoints[n].in_done) (*(endpoints[n].in_done))(n, endpoints[n].in_buf, endpoints[n].in_ptr); } logf("receive_end"); return 0; } static bool usb_out_buffer_full(int ep) { usb_select_endpoint(ep_index(ep, DIR_TX)); if (ISP1583_DFLOW_EPTYPE & 4) /* Check if type=bulk and double buffering is set */ return (ISP1583_DFLOW_BUFSTAT & 3) == 3; /* Return true if both buffers are filled */ else return (ISP1583_DFLOW_BUFSTAT & 3) != 0; /* Return true if one of the buffers are filled */ } static int usb_send(int n) { logf("usb_send(%d)", n); int max_pkt_size, len; int i; unsigned char *p; if (endpoints[n].halt[DIR_TX] || !endpoints[n].enabled[DIR_TX] || !endpoints[n].out_in_progress) { logf("NOT SEND TO EP!"); return -1; } if (endpoints[n].out_ptr < 0) { endpoints[n].out_in_progress = 0; if (endpoints[n].out_done) (*(endpoints[n].out_done))(n, endpoints[n].out_buf, endpoints[n].out_len); logf("ALREADY SENT TO EP!"); return -1; } if (usb_out_buffer_full(n)) { logf("BUFFER FULL!"); return -1; } usb_select_endpoint(ep_index(n, DIR_TX)); max_pkt_size = endpoints[n].max_pkt_size[DIR_TX]; len = endpoints[n].out_len - endpoints[n].out_ptr; if (len > max_pkt_size) len = max_pkt_size; if(len < max_pkt_size) ISP1583_DFLOW_BUFLEN = len; p = endpoints[n].out_buf + endpoints[n].out_ptr; i = 0; while (len - i >= 2) { ISP1583_DFLOW_DATA = p[i] | (p[i + 1] << 8); i += 2; } if (i < len) ISP1583_DFLOW_DATA = p[i]; endpoints[n].out_ptr += len; /* if (endpoints[n].out_ptr == endpoints[n].out_len && len < max_pkt_size) */ if (endpoints[n].out_ptr == endpoints[n].out_len) endpoints[n].out_ptr = -1; logf("send_end"); return 0; } static void usb_stall_endpoint(int idx) { usb_select_endpoint(idx); ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_STALL; endpoints[epidx_n(idx)].halt[epidx_dir(idx)] = 1; } static void usb_unstall_endpoint(int idx) { usb_select_endpoint(idx); ISP1583_DFLOW_CTRLFUN &= ~DFLOW_CTRLFUN_STALL; ISP1583_DFLOW_EPTYPE &= ~DFLOW_EPTYPE_ENABLE; ISP1583_DFLOW_EPTYPE |= DFLOW_EPTYPE_ENABLE; ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_CLBUF; if (epidx_dir(idx) == DIR_TX) endpoints[epidx_n(idx)].out_in_progress = 0; else { endpoints[epidx_n(idx)].in_min_len = -1; endpoints[epidx_n(idx)].in_ack = 0; } endpoints[epidx_n(idx)].halt[epidx_dir(idx)] = 0; } static void usb_status_ack(int ep, int dir) { logf("usb_status_ack(%d)", dir); if(ep == EP_CONTROL) usb_select_setup_endpoint(); else usb_select_endpoint(ep_index(ep, dir)); ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_STATUS; } static void usb_data_stage_enable(int ep, int dir) { logf("usb_data_stage_enable(%d)", dir); usb_select_endpoint(ep_index(ep, dir)); ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_DSEN; } static void usb_handle_setup_rx(void) { int len; usb_select_setup_endpoint(); len = usb_get_packet(setup_pkt_buf, 8); if (len == 8) { ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_STATUS; /* Acknowledge packet */ usb_core_control_request((struct usb_ctrlrequest*)setup_pkt_buf); } else { usb_drv_stall(EP_CONTROL, true, false); usb_drv_stall(EP_CONTROL, true, true); logf("usb_handle_setup_rx() failed"); return; } logf("usb_handle_setup_rx(): %02x %02x %02x %02x %02x %02x %02x %02x", setup_pkt_buf[0], setup_pkt_buf[1], setup_pkt_buf[2], setup_pkt_buf[3], setup_pkt_buf[4], setup_pkt_buf[5], setup_pkt_buf[6], setup_pkt_buf[7]); } static void usb_handle_data_int(int ep, int dir) { int len; if (dir == DIR_TX) len = usb_send(ep); else { len = usb_receive(ep); endpoints[ep].in_ack = 1; } logf("usb_handle_data_int(%d, %d) finished", ep, dir); } bool usb_drv_powered(void) { #if 0 return (ISP1583_INIT_OTG & INIT_OTG_BSESS_VALID) ? true : false; #else return (ISP1583_INIT_MODE & INIT_MODE_VBUSSTAT) ? true : false; #endif } static void setup_endpoints(void) { int i; int max_pkt_size = (high_speed_mode ? 512 : 64); usb_setup_endpoint(ep_index(EP_CONTROL, DIR_RX), 64, USB_ENDPOINT_XFER_CONTROL); usb_setup_endpoint(ep_index(EP_CONTROL, DIR_TX), 64, USB_ENDPOINT_XFER_CONTROL); for(i = 1; i < USB_NUM_ENDPOINTS-1; i++) { usb_setup_endpoint(ep_index(i, DIR_RX), max_pkt_size, USB_ENDPOINT_XFER_BULK); usb_setup_endpoint(ep_index(i, DIR_TX), max_pkt_size, USB_ENDPOINT_XFER_BULK); } usb_enable_endpoint(ep_index(EP_CONTROL, DIR_RX)); usb_enable_endpoint(ep_index(EP_CONTROL, DIR_TX)); for (i = 1; i < USB_NUM_ENDPOINTS-1; i++) { usb_enable_endpoint(ep_index(i, DIR_RX)); usb_enable_endpoint(ep_index(i, DIR_TX)); } ZVM_SPECIFIC; } #if 0 /* currently unused */ static void usb_helper(void) { if(ISP1583_GEN_INT_READ & ISP1583_INIT_INTEN_READ) { logf("Helper detected interrupt... [%d]", (int)current_tick); usb_drv_int(); } } #endif void usb_drv_init(void) { /* Disable interrupt at CPU level */ DIS_INT_CPU_TARGET; /* Unlock the device's registers */ ISP1583_GEN_UNLCKDEV = ISP1583_UNLOCK_CODE; /* Soft reset the device */ ISP1583_INIT_MODE = INIT_MODE_SFRESET; sleep(10); /* Enable CLKAON & GLINTENA */ ISP1583_INIT_MODE = STANDARD_INIT_MODE; /* Disable all OTG functions */ ISP1583_INIT_OTG = 0; #ifdef DEBUG logf("BUS_CONF/DA0:%d MODE0/DA1: %d MODE1: %d", (bool)(ISP1583_INIT_MODE & INIT_MODE_TEST0), (bool)(ISP1583_INIT_MODE & INIT_MODE_TEST1), (bool)(ISP1583_INIT_MODE & INIT_MODE_TEST2)); logf("Chip ID: 0x%x", ISP1583_GEN_CHIPID); //logf("INV0: 0x% IRQEDGE: 0x%x IRQPORT: 0x%x", IO_GIO_INV0, IO_GIO_IRQEDGE, IO_GIO_IRQPORT); #endif /*Set interrupt generation to target-specific mode + * Set the control pipe to ACK only interrupt + * Set the IN pipe to ACK only interrupt + * Set OUT pipe to ACK and NYET interrupt */ ISP1583_INIT_INTCONF = 0x54 | INT_CONF_TARGET; /* Clear all interrupts */ set_int_value(ISP1583_GEN_INT_A, ISP1583_GEN_INT_B, 0xFFFFFFFF); /* Enable USB interrupts */ set_int_value(ISP1583_INIT_INTEN_A, ISP1583_INIT_INTEN_B, STANDARD_INTEN); ZVM_SPECIFIC; /* Enable interrupt at CPU level */ EN_INT_CPU_TARGET; setup_endpoints(); /* Clear device address and disable it */ ISP1583_INIT_ADDRESS = 0; /* Turn SoftConnect on */ ISP1583_INIT_MODE |= INIT_MODE_SOFTCT; ZVM_SPECIFIC; //tick_add_task(usb_helper); logf("usb_init_device() finished"); } int usb_drv_port_speed(void) { return (int)high_speed_mode; } void usb_drv_exit(void) { logf("usb_drv_exit()"); /* Disable device */ ISP1583_INIT_MODE &= ~INIT_MODE_SOFTCT; ISP1583_INIT_ADDRESS = 0; /* Disable interrupts */ set_int_value(ISP1583_INIT_INTEN_A, ISP1583_INIT_INTEN_B, 0); /* and the CPU's one... */ DIS_INT_CPU_TARGET; /* Send usb controller to suspend mode */ ISP1583_INIT_MODE = INIT_MODE_GOSUSP; ISP1583_INIT_MODE = 0; //tick_remove_task(usb_helper); ZVM_SPECIFIC; } void usb_drv_stall(int endpoint, bool stall, bool in) { logf("%sstall EP%d %s", (stall ? "" : "un"), endpoint, (in ? "RX" : "TX" )); if (stall) usb_stall_endpoint(ep_index(endpoint, (int)in)); else usb_unstall_endpoint(ep_index(endpoint, (int)in)); } bool usb_drv_stalled(int endpoint, bool in) { return (endpoints[endpoint].halt[(int)in] == 1); } static void out_callback(int ep, unsigned char *buf, int len) { (void)buf; logf("out_callback(%d, 0x%x, %d)", ep, (int)buf, len); usb_status_ack(ep, DIR_RX); usb_core_transfer_complete(ep, true, 0, len); /* 0=>status succeeded, haven't worked out status failed yet... */ } static void in_callback(int ep, unsigned char *buf, int len) { (void)buf; logf("in_callback(%d, 0x%x, %d)", ep, (int)buf, len); usb_status_ack(ep, DIR_TX); usb_core_transfer_complete(ep, false, 0, len); } int usb_drv_recv(int ep, void* ptr, int length) { logf("usb_drv_recv(%d, 0x%x, %d)", ep, (int)ptr, length); if(ep == EP_CONTROL && length == 0 && ptr == NULL) { usb_status_ack(ep, DIR_TX); return 0; } endpoints[ep].in_done = in_callback; endpoints[ep].in_buf = ptr; endpoints[ep].in_max_len = length; endpoints[ep].in_min_len = length; endpoints[ep].in_ptr = 0; if(ep == EP_CONTROL) { usb_data_stage_enable(ep, DIR_RX); return usb_receive(ep); } else return usb_receive(ep); } int usb_drv_send_nonblocking(int ep, void* ptr, int length) { /* First implement DMA... */ return usb_drv_send(ep, ptr, length); } static void usb_drv_wait(int ep, bool send) { logf("usb_drv_wait(%d, %d)", ep, send); if(send) { while (endpoints[ep].out_in_progress) nop_f(); } else { while (endpoints[ep].in_ack) nop_f(); } } int usb_drv_send(int ep, void* ptr, int length) { logf("usb_drv_send_nb(%d, 0x%x, %d)", ep, (int)ptr, length); if(ep == EP_CONTROL && length == 0 && ptr == NULL) { usb_status_ack(ep, DIR_RX); return 0; } if(endpoints[ep].out_in_progress == 1) return -1; endpoints[ep].out_done = out_callback; endpoints[ep].out_buf = ptr; endpoints[ep].out_len = length; endpoints[ep].out_ptr = 0; endpoints[ep].out_in_progress = 1; if(ep == EP_CONTROL) { int rc = usb_send(ep); usb_data_stage_enable(ep, DIR_TX); usb_drv_wait(ep, DIR_TX); return rc; } else return usb_send(ep); } void usb_drv_reset_endpoint(int ep, bool send) { logf("reset endpoint(%d, %d)", ep, send); usb_setup_endpoint(ep_index(ep, (int)send), endpoints[ep].max_pkt_size[(int)send], endpoints[ep].type); usb_enable_endpoint(ep_index(ep, (int)send)); } void usb_drv_cancel_all_transfers(void) { logf("usb_drv_cancel_all_tranfers()"); int i; for(i=0;idone"); } /* Method for handling interrupts, must be called from usb-.c */ void IRAM_ATTR usb_drv_int(void) { unsigned long ints; ints = ISP1583_GEN_INT_READ & ISP1583_INIT_INTEN_READ; if(!ints) return; /* Unlock the device's registers */ ISP1583_GEN_UNLCKDEV = ISP1583_UNLOCK_CODE; //logf(" handling int [0x%lx & 0x%lx = 0x%x]", ISP1583_GEN_INT_READ, ISP1583_INIT_INTEN_READ, (int)ints); if(ints & INT_IEBRST) /* Bus reset */ { logf("BRESET"); high_speed_mode = false; bus_reset(); usb_core_bus_reset(); /* Mask bus reset interrupt */ set_int_value(ISP1583_GEN_INT_A, ISP1583_GEN_INT_B, INT_IEBRST); return; } if(ints & INT_IEP0SETUP) /* EP0SETUP interrupt */ { logf("EP0SETUP"); usb_handle_setup_rx(); } if(ints & INT_IEHS_STA) /* change from full-speed to high-speed mode -> endpoints need to get reconfigured!! */ { logf("HS_STA"); high_speed_mode = true; setup_endpoints(); } if(ints & INT_EP_MASK) /* Endpoints interrupt */ { unsigned long ep_event; unsigned short i = 10; ep_event = ints & INT_EP_MASK; while(ep_event) { if(i>25) break; if(ep_event & (1 << i)) { logf("EP%d %s interrupt", (i - 10) / 2, i % 2 ? "RX" : "TX"); usb_handle_data_int((i - 10) / 2, i % 2); ep_event &= ~(1 << i); } i++; } } if(ints & INT_IERESM && !(ints & INT_IESUSP)) /* Resume status: status change from suspend to resume (active) */ { logf("RESM"); } if(ints & INT_IESUSP && !(ints & INT_IERESM)) /* Suspend status: status change from active to suspend */ { logf("SUSP"); } if(ints & INT_IEDMA) /* change in the DMA Interrupt Reason register */ { logf("DMA"); } if(ints & INT_IEVBUS) /* transition from LOW to HIGH on VBUS */ { logf("VBUS"); } /* Mask all (enabled) interrupts */ set_int_value(ISP1583_GEN_INT_A, ISP1583_GEN_INT_B, ints); ZVM_SPECIFIC; } void usb_drv_set_address(int address) { logf("usb_drv_set_address(0x%x)", address); ISP1583_INIT_ADDRESS = (address & 0x7F) | INIT_ADDRESS_DEVEN; ZVM_SPECIFIC; } void usb_drv_set_test_mode(int mode) { logf("usb_drv_set_test_mode(%d)", mode); switch(mode){ case 0: ISP1583_GEN_TSTMOD = 0; /* Power cycle... */ break; case 1: ISP1583_GEN_TSTMOD = GEN_TSTMOD_JSTATE; break; case 2: ISP1583_GEN_TSTMOD = GEN_TSTMOD_KSTATE; break; case 3: ISP1583_GEN_TSTMOD = GEN_TSTMOD_SE0_NAK; break; case 4: //REG_PORTSC1 |= PORTSCX_PTC_PACKET; break; case 5: //REG_PORTSC1 |= PORTSCX_PTC_FORCE_EN; break; } } #ifndef BOOTLOADER int dbg_usb_num_items(void) { return 2+USB_NUM_ENDPOINTS*2; } const char* dbg_usb_item(int selected_item, void *data, char *buffer, size_t buffer_len) { if(selected_item < 2) { switch(selected_item) { case 0: snprintf(buffer, buffer_len, "USB connected: %s", (usb_drv_connected() ? "Yes" : "No")); return buffer; case 1: snprintf(buffer, buffer_len, "HS mode: %s", (high_speed_mode ? "Yes" : "No")); return buffer; } } else { int n = ep_index((selected_item - 2) / 2, (selected_item - 2) % 2); if(endpoints[n].enabled == false) snprintf(buffer, buffer_len, "EP%d[%s]: DISABLED", epidx_n(n), (epidx_dir(n) ? "TX" : "RX")); else { if(epidx_dir(n)) { if(endpoints[n].out_in_progress) snprintf(buffer, buffer_len, "EP%d[TX]: TRANSFERRING DATA -> %d bytes/%d bytes", epidx_n(n), (endpoints[n].out_len - endpoints[n].out_ptr), endpoints[n].out_len); else snprintf(buffer, buffer_len, "EP%d[TX]: STANDBY", epidx_n(n)); } else { if(endpoints[n].in_buf && !endpoints[n].in_ack) snprintf(buffer, buffer_len, "EP%d[RX]: RECEIVING DATA -> %d bytes/%d bytes", epidx_n(n), endpoints[n].in_ptr, endpoints[n].in_max_len); else snprintf(buffer, buffer_len, "EP%d[RX]: STANDBY", epidx_n(n)); } } return buffer; } return NULL; (void)data; } #endif