a51879dd01
zvm is the only user of this file git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31495 a1c6a512-1295-4272-9138-f99709370657
823 lines
22 KiB
C
823 lines
22 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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;i<USB_NUM_ENDPOINTS-1;i++)
|
|
endpoints[i].halt[0] = endpoints[i].halt[1] = 1;
|
|
}
|
|
|
|
int usb_drv_request_endpoint(int type, int dir)
|
|
{
|
|
int i, bit;
|
|
|
|
if (type != USB_ENDPOINT_XFER_BULK)
|
|
return -1;
|
|
|
|
bit=(dir & USB_DIR_IN)? 2:1;
|
|
|
|
for (i=1; i < USB_NUM_ENDPOINTS; i++) {
|
|
if((endpoints[i].allocation & bit)!=0)
|
|
continue;
|
|
endpoints[i].allocation |= bit;
|
|
return i | dir;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void usb_drv_release_endpoint(int ep)
|
|
{
|
|
int mask = (ep & USB_DIR_IN)? ~2:~1;
|
|
endpoints[ep & 0x7f].allocation &= mask;
|
|
}
|
|
|
|
static void bus_reset(void)
|
|
{
|
|
/* Enable CLKAON & GLINTENA */
|
|
ISP1583_INIT_MODE = STANDARD_INIT_MODE;
|
|
/* Enable USB interrupts */
|
|
ISP1583_INIT_INTCONF = 0x54 | INT_CONF_TARGET;
|
|
set_int_value(ISP1583_INIT_INTEN_A, ISP1583_INIT_INTEN_B, STANDARD_INTEN);
|
|
|
|
/* Disable all OTG functions */
|
|
ISP1583_INIT_OTG = 0;
|
|
|
|
/* Clear device address and enable it */
|
|
ISP1583_INIT_ADDRESS = INIT_ADDRESS_DEVEN;
|
|
|
|
ZVM_SPECIFIC;
|
|
|
|
/* Reset endpoints to default */
|
|
setup_endpoints();
|
|
|
|
logf("bus reset->done");
|
|
}
|
|
|
|
/* Method for handling interrupts, must be called from usb-<target>.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
|