316 lines
13 KiB
C
316 lines
13 KiB
C
|
|
#include "usb.h"
|
|
|
|
//used to store success/error code of last transfer for debugging
|
|
//static uint8_t usbWrite_status;
|
|
|
|
//USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]);
|
|
/* This function is called when the driver receives a SETUP transaction from
|
|
* the host which is not answered by the driver itself (in practice: class and
|
|
* vendor requests). All control transfers start with a SETUP transaction where
|
|
* the host communicates the parameters of the following (optional) data
|
|
* transfer. The SETUP data is available in the 'data' parameter which can
|
|
* (and should) be casted to 'usbRequest_t *' for a more user-friendly access
|
|
* to parameters.
|
|
*
|
|
* If the SETUP indicates a control-in transfer, you should provide the
|
|
* requested data to the driver. There are two ways to transfer this data:
|
|
* (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data
|
|
* block and return the length of the data in 'usbFunctionSetup()'. The driver
|
|
* will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The
|
|
* driver will then call 'usbFunctionRead()' when data is needed. See the
|
|
* documentation for usbFunctionRead() for details.
|
|
*
|
|
* If the SETUP indicates a control-out transfer, the only way to receive the
|
|
* data from the host is through the 'usbFunctionWrite()' call. If you
|
|
* implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()'
|
|
* to indicate that 'usbFunctionWrite()' should be used. See the documentation
|
|
* of this function for more information. If you just want to ignore the data
|
|
* sent by the host, return 0 in 'usbFunctionSetup()'.
|
|
*
|
|
* Note that calls to the functions usbFunctionRead() and usbFunctionWrite()
|
|
* are only done if enabled by the configuration in usbconfig.h.
|
|
*/
|
|
|
|
//typedef struct usbRequest{
|
|
// uchar bmRequestType;
|
|
// uchar bRequest;
|
|
// usbWord_t wValue;
|
|
// usbWord_t wIndex;
|
|
// usbWord_t wLength;
|
|
//}usbRequest_t;
|
|
|
|
|
|
|
|
#ifdef AVR_CORE
|
|
USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]) {
|
|
#endif
|
|
#ifdef STM_CORE
|
|
uint16_t usbFunctionSetup(uint8_t data[8]) {
|
|
#endif
|
|
|
|
|
|
//defined and controled by buffer.c
|
|
// extern buffer *cur_usb_load_buff;
|
|
|
|
//cast incoming data into the the usb setup packet it is
|
|
setup_packet *spacket = (void *)data;
|
|
|
|
//default 8 Byte buffer to be used for returning error code and return values
|
|
//must be static so USB driver can still access after function return
|
|
//static uint8_t rv[RETURN_BUFF_SIZE];
|
|
//need to make sure this return array is aligned on arm core to prevent unaligned access
|
|
//best way I can think to do this is instantiate it as a 16bit array,
|
|
//then point to it with an 8bit pointer and now it can be accessed as 8bit array.
|
|
//without this stm32 was hardfaulting I'm not certain, but I think this is why
|
|
#ifdef STM_CORE
|
|
static uint16_t rv16[RETURN_BUFF_SIZE/2];
|
|
uint8_t *rv = (uint8_t*)rv16;
|
|
#else
|
|
static uint8_t rv[RETURN_BUFF_SIZE];
|
|
#endif
|
|
//perhaps a better solution for stm32 is just provide a pointer to usb buffer ram
|
|
//but that doesn't expand past 8bytes easily, and starts to necessitate special cases
|
|
//diverging from V-usb norms..
|
|
rv[RETURN_ERR_IDX] = GEN_FAIL; //default to error till opcode updates.
|
|
rv[RETURN_LEN_IDX] = 0; //reset to zero, number of bytes in return data (excluding ERR & LEN)
|
|
//now it's the opcode's responsiblity to update these values
|
|
//rv[RETURN_DATA] start of return data
|
|
|
|
/* (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data
|
|
* block and return the length of the data in 'usbFunctionSetup()'. The driver
|
|
* will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The
|
|
* driver will then call 'usbFunctionRead()' when data is needed. See the
|
|
*/
|
|
//by default want to return some portion of the 8 byte rv "return value"
|
|
//buffer. If no return data requested from host rlen = 0, so this wouldn't matter
|
|
//Some dictionaries/opcodes that want to return larger buffers though
|
|
//this function will set usbMsgPtr to point to that larger buffer when supported
|
|
usbMsgPtr = (usbMsgPtr_t)rv;
|
|
|
|
|
|
#if USB_CFG_LONG_TRANSFERS
|
|
//number of bytes to return to host
|
|
//16bit meets max possible 16KBytes with V-USB long transfers enabled
|
|
uint16_t rlen = spacket->wLength; //the speed loss doesn't make long transfers worth it for now
|
|
#else
|
|
//8bit is enough for 254 bit non-long xfrs
|
|
//also gives ~0.7KBps speed up compared to 16bit rlen on V-USB
|
|
uint8_t rlen = (uint8_t) spacket->wLength;
|
|
#endif
|
|
|
|
|
|
switch(spacket->bRequest) {
|
|
case DICT_PINPORT:
|
|
rv[RETURN_ERR_IDX] = pinport_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_IO:
|
|
rv[RETURN_ERR_IDX] = io_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_NES:
|
|
rv[RETURN_ERR_IDX] = nes_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_SNES:
|
|
rv[RETURN_ERR_IDX] = snes_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_GAMEBOY:
|
|
rv[RETURN_ERR_IDX] = gameboy_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_GBA:
|
|
rv[RETURN_ERR_IDX] = gba_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_SEGA:
|
|
rv[RETURN_ERR_IDX] = sega_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_N64:
|
|
rv[RETURN_ERR_IDX] = n64_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_SWIM:
|
|
rv[RETURN_ERR_IDX] = swim_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_JTAG:
|
|
rv[RETURN_ERR_IDX] = jtag_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_BOOTLOAD:
|
|
rv[RETURN_ERR_IDX] = bootload_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
case DICT_CICCOM:
|
|
rv[RETURN_ERR_IDX] = ciccom_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] );
|
|
break;
|
|
|
|
|
|
case DICT_BUFFER:
|
|
//just give buffer.c the setup packet and let it figure things out for itself
|
|
usbMsgPtr = (usbMsgPtr_t)buffer_usb_call( spacket, rv, &rlen );
|
|
break; //end of BUFFER
|
|
|
|
/*
|
|
case DICT_USB:
|
|
//currently just a simple way to read back usbFunctionWrite status SUCCESS/ERROR
|
|
//if there are future status' to read back may have to create some functions
|
|
rv[RV_ERR_IDX] = SUCCESS;
|
|
rv[RV_DATA0_IDX] = usbWrite_status;
|
|
rv[RV_DATA0_IDX+1] = cur_usb_load_buff->last_idx;
|
|
rlen = 3;
|
|
break; //end of USB
|
|
*/
|
|
|
|
case DICT_OPER:
|
|
//just give operation.c the setup packet and let it figure things out for itself
|
|
usbMsgPtr = (usbMsgPtr_t)operation_usb_call( spacket, rv, &rlen );
|
|
break; //end of OPER
|
|
|
|
default:
|
|
//request (aka dictionary) is unknown
|
|
rv[RETURN_ERR_IDX] = ERR_UNKN_DICTIONARY;
|
|
}
|
|
|
|
return rlen;
|
|
|
|
//need to return USB_NO_MSG for OUT transfers to make usbFunctionWrite called
|
|
|
|
}
|
|
|
|
|
|
//USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len);
|
|
/* This function is called by the driver to ask the application for a control
|
|
* transfer's payload data (control-in). It is called in chunks of up to 8
|
|
* bytes each. You should copy the data to the location given by 'data' and
|
|
* return the actual number of bytes copied. If you return less than requested,
|
|
* the control-in transfer is terminated. If you return 0xff, the driver aborts
|
|
* the transfer with a STALL token.
|
|
* In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ
|
|
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup()..
|
|
*/
|
|
//USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len) {
|
|
// //this function should only get called if usbFunctionSetup returns USB_NO_MSG
|
|
// return len;
|
|
//}
|
|
|
|
|
|
// V-USB description of this function:
|
|
//USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len);
|
|
/* This function is called by the driver to provide a control transfer's
|
|
* payload data (control-out). It is called in chunks of up to 8 bytes. The
|
|
* total count provided in the current control transfer can be obtained from
|
|
* the 'length' property in the setup data. If an error occurred during
|
|
* processing, return 0xff (== -1). The driver will answer the entire transfer
|
|
* with a STALL token in this case. If you have received the entire payload
|
|
* successfully, return 1. If you expect more data, return 0. If you don't
|
|
* know whether the host will send more data (you should know, the total is
|
|
* provided in the usbFunctionSetup() call!), return 1.
|
|
* NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called
|
|
* for the remaining data. You must continue to return 0xff for STALL in these
|
|
* calls.
|
|
* In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE
|
|
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup()..
|
|
*/
|
|
|
|
/* Desc:USB Write routine for OUT transfers
|
|
* the V-USB drivers call this function on OUT tokens
|
|
* and provide upto 8 byte data packet's payload
|
|
* for payloads longer than 8Bytes this gets called multiple times
|
|
* until all bytes have been transferred. Real thing to understand
|
|
* is that this function gets called once per data packet (max 8bytes)
|
|
* based on USB 1.1 low speed standard.
|
|
* buffer.c is the govnerning module for what buffer gets filled
|
|
* Pre: buffer.c must have set current usb loading buffer with global var
|
|
* the current buffer must have enough room for incoming data
|
|
* possible to use mutliple buffers for a single transfer, but
|
|
* buffer.c must orchestrate the swap to new buffer object.
|
|
* buffer.c sets global incoming bytes remain so it can keep
|
|
* track of this function's progress
|
|
* buffer object must be initialized, allocated, and status USB_LOADING
|
|
* Post:usbWrite_status updated with SUCCESS/ERROR number
|
|
* incoming data packet copied to cur_usb_load_buff
|
|
* global incoming_bytes_remain updated
|
|
* cur_usb_load_buff cur_byte updated based on data length
|
|
* cur_usb_load_buff status updated when it's full
|
|
* Rtn: message to V-USB driver so it can respond to host
|
|
*/
|
|
|
|
//removing checks from this function speeds up transfers by ~1KBps
|
|
//this data is based on doing nothing with data once it arrives
|
|
//long transfers disabled, and using 254 byte transfers with 2 bytes stuffed in setup packet
|
|
// with checks: 512KByte = 18.7sec = 27.4KBps
|
|
// w/o checks: 512KByte = 17.9sec = 28.5KBps
|
|
// w/o checks: using 8bit rlen = 17.5sec = 29.2KBps
|
|
// with checks: using 8bit rlen = 18sec = 28.3KBps
|
|
//#define MAKECHECKS
|
|
|
|
#ifdef AVR_CORE
|
|
USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len) {
|
|
#endif
|
|
#ifdef STM_CORE
|
|
uint8_t usbFunctionWrite(uint8_t *data, uint8_t len) {
|
|
#endif
|
|
|
|
//defined and controled by buffer.c
|
|
extern buffer *cur_usb_load_buff;
|
|
extern uint8_t incoming_bytes_remain;
|
|
|
|
uint8_t data_cur = 0; //current incoming byte to copy
|
|
uint8_t buf_cur = cur_usb_load_buff->cur_byte; //current buffer byte
|
|
uint8_t *buf_data = cur_usb_load_buff->data; //current buffer data array
|
|
|
|
#ifdef MAKECHECKS
|
|
//check that current buffer's status is USB_LOADING
|
|
if (cur_usb_load_buff->status != USB_LOADING) {
|
|
usbWrite_status = ERR_OUT_CURLDBUF_STATUS;
|
|
return STALL;
|
|
}
|
|
//check that current buffer's has enough room
|
|
if ( ((cur_usb_load_buff->last_idx) + 1 - buf_cur) < len ) {
|
|
usbWrite_status = ERR_OUT_CURLDBUF_TO_SMALL;
|
|
return STALL;
|
|
}
|
|
#endif
|
|
|
|
//copy 1-8bytes of payload into buffer
|
|
while ( data_cur < len ) {
|
|
buf_data[buf_cur] = data[data_cur];
|
|
buf_cur++;
|
|
data_cur++;
|
|
}
|
|
|
|
#ifdef MAKECHECKS
|
|
//need to account for the fact that cur_byte will roll over being 8bit value
|
|
if ( cur_usb_load_buff->last_idx == (cur_usb_load_buff->cur_byte + len - 1) ) {
|
|
//this signals to buffer.c so it can update cur_usb_load_buf
|
|
//and start tasking this buffer to programming
|
|
cur_usb_load_buff->status = USB_FULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
//update counters and status
|
|
cur_usb_load_buff->cur_byte += len;
|
|
incoming_bytes_remain -= len;
|
|
//usbWrite_status = SUCCESS;
|
|
|
|
//want this function to be as fast as possible, so buffer.c checks if
|
|
//the buffer is full 'behind the scenes' outside of this function.
|
|
|
|
|
|
if ( incoming_bytes_remain == 0 ) { //done with OUT transfer
|
|
//alternate to MAKECHECKS should be faster but require transfer sizes to match buffer size
|
|
cur_usb_load_buff->status = USB_FULL;
|
|
return PAYLD_DONE;
|
|
} else { //more data packets remain to complete OUT transfer
|
|
return NOT_DONE;
|
|
}
|
|
|
|
}
|
|
|