diff --git a/firmware/source/logic.h b/firmware/source/logic.h index 0149a87..537e5bc 100644 --- a/firmware/source/logic.h +++ b/firmware/source/logic.h @@ -1,3 +1,5 @@ +#ifndef _logic_h +#define _logic_h #define LO 0x00 #define HI 0xFF @@ -6,10 +8,4 @@ #define IP 0x00 #define OP 0xFF -#define SUCCESS 0x00 -#define ERR_UNKN_PP_OPCODE_ONLY 1 -#define ERR_UNKN_PP_OPCODE_8BOP 2 -#define ERR_UNKN_PP_OPCODE_16BOP 3 -#define ERR_UNKN_PP_OPCODE_24BOP 4 -#define ERR_UNKN_PP_OPCODE_8BRV 5 - +#endif diff --git a/firmware/source/main.c b/firmware/source/main.c index 50be6e3..30ad199 100644 --- a/firmware/source/main.c +++ b/firmware/source/main.c @@ -1,127 +1,40 @@ -#include #include #include #include #include "usbdrv.h" #include "io.h" -#include "shared_usb_commands.h" +#include "pinport.h" - -//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. - */ -USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]) { - //cast incoming data into the the usb setup packet it is - usbRequest_t *spacket = (void *)data; - //typedef struct usbRequest{ - // uchar bmRequestType; - // uchar bRequest; - // usbWord_t wValue; - // usbWord_t wIndex; - // usbWord_t wLength; - //}usbRequest_t; - - switch(spacket->bRequest) { - case REQ_LED_ON: - _LED_ON(); //from macros - break; - case REQ_LED_OFF: - _LED_OFF(); - break; - default: - return 1; - } - - return 0; //ignore any data from the host for now -} - - -//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) { - return len; -} - - -//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().. - */ -USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len) { - return 1; //"don't know how much data coming -} - - -int main() +int __attribute__((noreturn)) main(void) { - uint8_t i; - - //enable watchdog timer with 1 second timeout + + //set watch dog timer with 1 second timer wdt_enable(WDTO_1S); + /* Even if you don't use the watchdog, turn it off here. On newer devices, + * the status of the watchdog (on/off, period) is PRESERVED OVER RESET! + */ + /* RESET status: all port bits are inputs without pull-up. + * That's the way we need D+ and D-. Therefore we don't need any + * additional hardware initialization. + */ - //need USB pins input w/o pullup which is default at startup. - //So further HW init is not needed/desired. + //odDebugInit(); //intialize debuging printing via serial port + //DBG1(0x00, 0, 0); //debug serial op: main starts - //initialize V-USB driver - usbInit(); - - //enforce re-enumeration - //interupts must be disabled! + //initialize V-usb driver before interupts enabled and entering main loop + usbInit(); + //disconnect from host enforce re-enumeration, interupts must be disabled during this. usbDeviceDisconnect(); - i = 0; - while (--i) { - //fake USB disconnect for >250 ms - //without WDT timing out - wdt_reset(); - _delay_ms(1); - } + //fake USB disconnect for over 250ms + uint8_t index = 0; + while(--index){ //loop 256 times + wdt_reset(); //keep wdt happy during this time + _delay_ms(1); //delay 256msec + } + //reconnect to host usbDeviceConnect(); //intialize i/o to pullup state @@ -131,22 +44,20 @@ int main() _LED_OP(); //boot with LED on to differentiate bettwen BL/RUN _LED_ON(); - + //enable interrupts sei(); //================= //MAIN LOOP //================= - while(1) { + while (1) { - //reset WDT - wdt_reset(); + //pet the watch doggie to keep him happy + wdt_reset(); - //V-USB driver poll - usbPoll(); - - }//End MAIN LOOP - - return 0; + //must call at regular intervals no longer than 50msec + //checks for setup packets from what I understand + usbPoll(); + } } diff --git a/firmware/source/pinport.c b/firmware/source/pinport.c index 6ecf449..67f5933 100644 --- a/firmware/source/pinport.c +++ b/firmware/source/pinport.c @@ -1,7 +1,4 @@ -#include -#include "logic.h" #include "pinport.h" -#include "shared_pinport.h" //This file was created based on pinport.h //the close relationship between these two files must be kept in mind when making changes. diff --git a/firmware/source/pinport.h b/firmware/source/pinport.h index a0e394a..6772d78 100644 --- a/firmware/source/pinport.h +++ b/firmware/source/pinport.h @@ -3,6 +3,8 @@ #include #include "logic.h" +#include "shared_errors.h" +#include "shared_dict_pinport.h" uint8_t pinport_opcode_only( uint8_t opcode ); uint8_t pinport_opcode_8b_operand( uint8_t opcode, uint8_t operand ); diff --git a/firmware/source/usb.c b/firmware/source/usb.c new file mode 100644 index 0000000..e492392 --- /dev/null +++ b/firmware/source/usb.c @@ -0,0 +1,168 @@ + +#include "usb.h" + +#include "pinport.h" + +//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; + +#define ENDPOINT_BIT 0x80 //Bit 7 of bmRequest type determines endpoint +#define ENDPOINT_IN 0x80 //In: device-to-host. +#define ENDPOINT_OUT 0x00 //Out: host-to-device. + + +typedef struct setup_packet{ + uint8_t bmRequestType; //contains endpoint + uint8_t bRequest; //designates dictionary of opcode + uint8_t opcode; //wValueLSB (little endian) + uint8_t wValueMSB; //expansion byte + uint8_t wIndexLSB; //operand LSB + uint8_t wIndexMSB; //operand MSB + uint16_t wLength; +}setup_packet; + +USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]) { + + //cast incoming data into the the usb setup packet it is + setup_packet *spacket = (void *)data; + + //8 Byte buffer to be used for returning error code and return values + //must be static so driver can still access after function return + static uchar rv[8]; + //rv[0] contains opcode success/error code + //rv[1-7] available for return data, start with index 1 + + //number of bytes to return to host + uint8_t rlen = 0; + + //determine endpoint IN/OUT + if ( (spacket->bmRequestType & ENDPOINT_BIT) == ENDPOINT_IN ) { + //read from device request + //send error code and return value + rlen = 1; //min value of error code + } else { + //write to device request + //host doesn't want to waste time with reading back error codes + rlen = 0; + //must also come here if opcode has payload coming in data packets to follow + //in that case host would have to send follow up read request to get error code + } + + + switch(spacket->bRequest) { + case PINPORT: + if((spacket->opcode > PP_OPCODE_ONLY_MIN) + & (spacket->opcode < PP_OPCODE_ONLY_MAX)) { + rv[0] = pinport_opcode_only( spacket->opcode ); + + } else if ((spacket->opcode > PP_OPCODE_8BOP_MIN) + & (spacket->opcode < PP_OPCODE_8BOP_MAX)) { + rv[0] = pinport_opcode_8b_operand( spacket->opcode, spacket->wIndexLSB ); + + } else if ((spacket->opcode > PP_OPCODE_16BOP_MIN) + & (spacket->opcode < PP_OPCODE_16BOP_MAX)) { + rv[0] = pinport_opcode_16b_operand( spacket->opcode, + spacket->wIndexMSB, spacket->wIndexLSB ); + + } else if ((spacket->opcode > PP_OPCODE_24BOP_MIN) + & (spacket->opcode < PP_OPCODE_24BOP_MAX)) { + rv[0] = pinport_opcode_24b_operand( spacket->opcode, + spacket->wValueMSB, spacket->wIndexMSB, spacket->wIndexLSB ); + + } else if ((spacket->opcode > PP_OPCODE_8BRV_MIN) + & (spacket->opcode < PP_OPCODE_8BRV_MAX)) { + rv[0] = pinport_opcode_8b_return( spacket->opcode, &rv[1]); + //return error code plus opcode return value + rlen ++; + } + break; //end of PINPORT + default: + //request (aka dictionary) is unknown + rv[0] = ERR_UNKN_DICTIONARY; + } + + /* (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 + */ + usbMsgPtr = rv; + return rlen; + + //return USB_NO_MSG; + //Don't have a use for usbFunctionRead yet.. Not expecting to anytime soon + //probably easier and perhaps faster to send cart dump commands and store rom image + //in a buffer to be returned here. +} + + +//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 +// data[0] = 0xAA; + return len; +} + + +//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().. + */ +USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len) { + return 1; //"don't know how much data coming +} + diff --git a/firmware/source/usb.h b/firmware/source/usb.h new file mode 100644 index 0000000..604dd81 --- /dev/null +++ b/firmware/source/usb.h @@ -0,0 +1,11 @@ +#ifndef _usb_h +#define _usb_h + +#include + +#include "usbdrv.h" +#include "logic.h" +#include "shared_dictionaries.h" + + +#endif diff --git a/host/source/inlprog.c b/host/source/inlprog.c index 26c5c89..2cc4d31 100644 --- a/host/source/inlprog.c +++ b/host/source/inlprog.c @@ -14,9 +14,7 @@ #include "usb_operations.h" #include "write_operations.h" - -//TODO remove once commands removed from main -#include "shared_usb_commands.h" +#include "shared_dictionaries.h" int main(int argc, char *argv[]) @@ -84,35 +82,72 @@ int main(int argc, char *argv[]) //context set to NULL since only acting as single user of libusb libusb_context *context = NULL; - //create usb device handle pointer to interact with retro-prog - libusb_device_handle *rprog_handle = NULL; - rprog_handle = open_usb_device( context ); - check( rprog_handle != NULL, "Unable to open INL retro-prog usb device handle."); + + //create USBtransfer struct to hold all transfer info + USBtransfer *transfer = malloc( sizeof(USBtransfer)); + check_mem(transfer); + + //create usb device handle pointer to interact with retro-prog + //libusb_device_handle *rprog_handle = NULL; + transfer->handle = NULL; + + + //open INL retro prog with firmware version 2.0 or greater + transfer->handle = open_usb_device( context ); + check( transfer->handle != NULL, "Unable to open INL retro-prog usb device handle."); int xfr_cnt = 0; - //uint8_t buffer8[8]; //8 is max payload for low speed devices' data packet - //uint8_t buffer128[128]; //128 largest power of 2 for non-LONG_TRANSFERS with V-USB - uint8_t buffer254[254]; //254 is max for non-LONG_TRANSFERS with V-USB - //uint8_t buffer16k[16384]; //16384 is max for LONG_TRANSFERS with V-USB + uint8_t rbuf[8]; - if (o_flag) { //ON send REQ_LED_ON - xfr_cnt = usb_write_to_device( rprog_handle, - REQ_LED_ON, (unsigned char *)buffer254, sizeof(buffer254) ); - printf("total bytes xfrd: %d \n", xfr_cnt); + int i; + printf("before return buffer: "); + for (i = 0; i < 8; i++) { + rbuf[i] = 7; + printf("%x ", rbuf[i]); } - if (f_flag) { //OFF send REQ_LED_OFF - xfr_cnt = usb_write_to_device( rprog_handle, - REQ_LED_OFF, (unsigned char *)buffer254, sizeof(buffer254) ); - printf("total bytes xfrd: %d \n", xfr_cnt); - } - if (w_value) { //OFF send REQ_LED_OFF - check( write_file( rprog_handle, w_value, i_value, b_value) == SUCCESS, - "Failed to write file: %s", w_value); + printf("\n"); + + //handle simple LED ON/OFF within main for now + if (o_flag | f_flag) { + transfer->endpoint = USB_IN; + transfer->request = PINPORT; + if (o_flag) transfer->wValueLSB = LED_ON; + if (f_flag) transfer->wValueLSB = LED_OFF; + transfer->data = rbuf; + transfer->wLength = 1; + + //send command + xfr_cnt = usb_transfer( transfer ); } + printf("total bytes xfrd: %d \n", xfr_cnt); + printf("after buffer: "); + for (i = 0; i < 8; i++) { + printf("%x ", rbuf[i]); + } + printf("\n"); - close_usb( context, rprog_handle); + + //if (o_flag) { //ON send REQ_LED_ON + // xfr_cnt = usb_transfer( transfer.handle, + // REQ_LED_ON, + // (unsigned char *)buffer254, sizeof(buffer254) ); + // printf("total bytes xfrd: %d \n", xfr_cnt); + //} + //if (f_flag) { //OFF send REQ_LED_OFF + // xfr_cnt = usb_write_to_device( transfer.handle, + // REQ_LED_OFF, (unsigned char *)buffer254, sizeof(buffer254) ); + // printf("total bytes xfrd: %d \n", xfr_cnt); + //} + + //if (w_value) { //OFF send REQ_LED_OFF + // check( write_file( transfer.handle, w_value, i_value, b_value) == SUCCESS, + // "Failed to write file: %s", w_value); + //} + + + close_usb( context, transfer->handle); return 0; diff --git a/host/source/usb_operations.c b/host/source/usb_operations.c index e1073ad..186a765 100644 --- a/host/source/usb_operations.c +++ b/host/source/usb_operations.c @@ -205,61 +205,77 @@ void close_usb(libusb_context *context, libusb_device_handle *handle) return; } - //int libusb_control_transfer (libusb_device_handle *dev_handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) - // - //SETUP PACKET FIELDS: - //bmRequestType: ORing of req type (STD/VENDOR), recipient (think we only care about DEVICE), endpoint direction IN-dev->host OUT-host->dev - //bRequest: single byte that can signify any 'command' or 'request' we setup. - //The wValue and wIndex fields allow parameters to be passed with the request. Think we can do whatever we want with these - //wLength is used the specify the number of bytes to be transferred should there be a data phase. - //wLength the length field for the setup packet. The data buffer should be at least this size. - // USB 1.1 low speed standard limits to 8 bytes - // V-USB seems to break this limit with max of 254 bytes (255 designates "USB_NO_MSG" - // V-USB allows "LONG TRANSFERS" utilizing full 16bit wLength for upto 16384 bytes = exactly 16KBytes - // although considering sram on AVR doesn't explode with long transfers and read/write functions are in 8byte chunks, - // I think it really is limited to 8bytes - // One idea to squeeze more data is have a request type defined that utilizes wValue and wIndex to gain 4bytes + 8buffer = 12bytes 50% gain - // Not sure how to gain access to wValue/wIndex with vusb drivers... - // answer: usbFunctionSetup will get called for every setup packet and pass all 8 bytes of setup packet - // Can ultimately answer this question by counting how many startup packets are recieved by usbFunciton setup for transfers >8 bytes - // If when sending >8 byte control transfers, a setup packet only comes once, then there is nothing to abuse - // however if the same setup packet gets sent for every 8 bytes, it would be better to only perform 8byte transfers and stuff - // 4 more bytes in wValue and wIndex fields to increase throughput by ~50%!!! - // Testing shows that usbFunctionSetup only gets called once for transfers of 254 bytes - // So there is only one setup packet for multiple data packets of 8bytes each - // - //Still not sure increasing transfer length doesn't simply break up into bunch of small 8byte transfers although it doesn't sound like it. - //245byte limit is kind of a pain.. but wValue/wIndex fields could be abused to send 256 bytes - //Long transfers apparently max out speed @ 24KBps with 300 bytes: https://forums.obdev.at/viewtopic.php?t=3059 - // - //PAYLOAD: - //data: a suitably-sized data buffer for either input or output (depending on direction bits within bmRequestType) - // - //TIMEOUT: - //timeout: (in millseconds) that this function should wait before giving up due to no response being received. - // For an unlimited timeout, use value 0 - // USB nutshell: A compliant host requires control transfer response within 5sec - // - //RETURN: - // Returns on success, the number of bytes actually transferred - // LIBUSB_ERROR_TIMEOUT if the transfer timed out - // LIBUSB_ERROR_PIPE if the control request was not supported by the device - // LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - // another LIBUSB_ERROR code on other failures -int usb_write_to_device( libusb_device_handle *handle, int command, unsigned char *data, uint16_t len ) + +/* USB transfer + *Desc: primary means of sending and recieving commands and data to retro programmer + * makes calls to libusb drivers to send/recieve control transfer setup, data, and status packets + * See USBtransfer struct explaination in usb_operations.h for more details + *Pre: libusb must be initialized + * USBtransfer struct must be initialized as follows: + * -handle must point to open usb device + * -endpoint "direction" must be defined + * -request must be a valid and defined in a command dictionary + * -wValue and wIndex must be valid as defined by request dictionary + * -WLength must equal number of bytes to be transferred with max of 254 + * -data points to buffer of raw data to send for reads or dump into for reads + * if wLength is zero, data can be NULL + *Post: USB control transfer complete (setup, data, and status packets) + * data buffer pointed by USBtransfer struct filled with read data for USB_IN requests. + * USBtransfer struct is unmodified can be reused for identical transfers with different payload + * libusb is still initialized and open + *Rtn: Number of bytes transferred on success (positive) + * ERROR if unable to transfer USBtransfer's wLength number of bytes + * prints libusb_error if there was usb problem + */ +int usb_transfer( USBtransfer *transfer ) { - //TODO translate command into request, value, index, etc - uint8_t request = command; - uint16_t wValue = 0; //setup packet wValue field - uint16_t wIndex = 0; //setup packet wIndex field + check( transfer->wLength <= MAX_VUSB, "Can't transfer more than %d bytes!", MAX_VUSB); - int xfr_cnt = libusb_control_transfer( handle, + if ( transfer->wLength != 0) { + check( transfer->data != NULL, "data buffer isn't initialized it's: %s", transfer->data); + } else { + debug("USB transfer with no data payload."); + } + //TODO create a check to verify dictionary is defined, and opcode/operands are valid + //TODO also check to ensure opcode supports endpoint direction + //many operations could be performed with IN/OUT and no data packet + //but all avr operations should have a return value success/error code + //one way to control whether those retrun values are read back is endpoint direction + + uint16_t wValue = transfer->wValueMSB; + wValue = wValue << 8; + wValue |= transfer->wValueLSB; + + uint16_t wIndex = transfer->wIndexMSB; + wIndex = wIndex << 8; + wIndex |= transfer->wIndexLSB; + + debug("request h: %x d: %d", transfer->request, transfer->request); + debug("wValueMSB h: %x d: %d", transfer->wValueMSB, transfer->wValueMSB); + debug("wValueLSB h: %x d: %d", transfer->wValueLSB, transfer->wValueLSB); + debug("wValue h: %x", wValue); + debug("wValue d: %d", wValue); + debug("wIndexMSB h: %x d: %d", transfer->wIndexMSB, transfer->wIndexMSB); + debug("wIndexLSB h: %x d: %d", transfer->wIndexLSB, transfer->wIndexLSB); + debug("wIndex h: %x", wIndex); + debug("wIndex d: %d", wIndex); + + int xfr_cnt = libusb_control_transfer( + transfer->handle, //Request type: vendor (as we define), recip: device, out: host->device - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - request, wValue, wIndex, data, len, SEC_5); + //LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | transfer->endpoint, + //request, wValue, wIndex, data, len, SEC_5); + transfer->request, + wValue, wIndex, + transfer->data, + transfer->wLength, + TIMEOUT_1_SEC); + debug("%d bytes transfered", xfr_cnt); check( xfr_cnt >=0, "Write xfr failed with libusb error: %s", libusb_strerror(xfr_cnt)); - check( xfr_cnt == len, "Write transfer failed only %dB sent when expecting %dB", xfr_cnt, len); + check( xfr_cnt == transfer->wLength, "Write transfer failed only %d Bytes sent expected %dBytes", + xfr_cnt, transfer->wLength); return xfr_cnt; diff --git a/host/source/usb_operations.h b/host/source/usb_operations.h index 41aba89..f85a002 100644 --- a/host/source/usb_operations.h +++ b/host/source/usb_operations.h @@ -8,13 +8,14 @@ #include #include +//list of included dictionaries for defining request, wValue, and wIndex fields +#include "shared_dictionaries.h" + //uncomment to DEBUG this file alone -//#define DEBUG +#define DEBUG //"make debug" to get DEBUG msgs on entire program #include "dbg.h" -#include "shared_usb_commands.h" - //control transfer request types //uint8_t libusb_control_setup::bmRequestType //Request type. @@ -32,16 +33,115 @@ //LIBUSB_RECIPIENT_ENDPOINT Endpoint. //LIBUSB_RECIPIENT_OTHER Other. // -//LIBUSB_ENDPOINT_IN In: device-to-host. -//LIBUSB_ENDPOINT_OUT Out: host-to-device. +//LIBUSB_ENDPOINT_IN 0x80 In: device-to-host. +//LIBUSB_ENDPOINT_OUT 0x00 Out: host-to-device. +#define USB_IN LIBUSB_ENDPOINT_IN +#define USB_OUT LIBUSB_ENDPOINT_OUT //USB timeout -#define SEC_5 5000 +#define TIMEOUT_1_SEC 1000 +#define TIMEOUT_5_SEC 5000 + +//Max transfer length +#define MAX_VUSB 245 //Max VUSB transfers without long transfers enabled +#define USB_NO_MSG 255 //designates transfer with no message +#define MAX_VUSB_LONGXFR 16384 //16KByte biggest value 16bit wLength can hold + +//typedef struct USBtransfer { +//This is the primary USB request struct used by host app used for all application USB communications. +//handle is retrieved from open_usb_device gives us a means to point to the opened USB device. +//The remaining elements are all directly fed to the outgoing USB setup & data packets to/from the device. +// Every USB transfer starts with host sending one of these setup packets to the device. +// the setup packets are unidirectinal always coming from the bus master (host). +// The data packet(s) for the transfer then follow and are bidirectional. +// The drivers handle final NACK/ACK/STALL packet for the most part.. +// Note V-USB mcu driver doesn't have time to check CRC so it sends ACK assuming no corruption. +//endpoint basically this is the direction of the data packet to follow. +// the usb device has a OUT and IN endpoint buffer and this defines which is being accessed. +// OUT "out of host" is for writting data to the usb device. +// IN "in to host" is for reading data from the usb device. +// endpoint is the only portion of setup packet's requestType field that isn't hardcoded. +// Vendor request types used exclusively as they meet the our intent and 'hard coded' into this struct. +// The recipient of this setup packet is also 'hard coded' to the usb device. +//request is more like request type in this scope and designates the 'dictionary' containing the command. +// pinport is the first dictionary of commands, more to come as things develop. +// these requests/dictionaries define how the wValue and wIndex fields are utilized. +// anything can be placed in the 4 bytes of wValue/wIndex as defined by the dictionary. +//wValueMSB:wValueLSB +// This is where the app places the 'command' being given to the retro programmer. +// LSB is big enough for now and contains the actual opcode. +// MSB is used as an overflow for operands/data if wIndex is not large enough. +// future dicts with more than 256 opcodes could define some/all of MSB to contain the opcode as well. +//wIndexMSB:wIndexLSB +// This typically contains the operand/data for the opcode but could be used for anything +// as defined by the opcode. Raw buffer data could even be placed here to cheat the 254 Byte transfer +// limit of V-USB (w/o long xfrs), the two bytes of wIndex bring to full page of 256 Bytes. +// Planning for this to contain the page index (aka memory addr) of the transmitted data buffer. +//wLength must be set to the size of the data transfer to follow this setup packet in Bytes. +// if wLength is >8, drivers split data into 8byte packets and final packet of < 8Bytes if needed. +// with 16bit wLength 16KB transfers are largest possible, requires using long xfrs on device driver. +// The max length without long xfrs is 254Bytes, value 255 is reserved for USB_NO_MSG. +// This means wLength's MSByte is under utilized but prob shouldn't concern ourselves with that. +//data is a pointer to the data buffer being sent for writes (OUT transfers). +// read (IN transfers) utilize data pointer to pass location of where to dump read data. + +typedef struct USBtransfer { + libusb_device_handle *handle; + uint8_t endpoint; + uint8_t request; + uint8_t wValueMSB; + uint8_t wValueLSB; + uint8_t wIndexMSB; + uint8_t wIndexLSB; + uint16_t wLength; + unsigned char *data; +} USBtransfer; libusb_device_handle * open_usb_device( libusb_context *context ); void close_usb(libusb_context *context, libusb_device_handle *handle); -int usb_write_to_device( libusb_device_handle *handle, int command, unsigned char *data, uint16_t len ); + //int libusb_control_transfer (libusb_device_handle *dev_handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) + // + //SETUP PACKET FIELDS: + //bmRequestType: ORing of req type (STD/VENDOR), recipient (think we only care about DEVICE), endpoint direction IN-dev->host OUT-host->dev + //bRequest: single byte that can signify any 'command' or 'request' we setup. + //The wValue and wIndex fields allow parameters to be passed with the request. Think we can do whatever we want with these + //wLength is used the specify the number of bytes to be transferred should there be a data phase. + //wLength the length field for the setup packet. The data buffer should be at least this size. + // USB 1.1 low speed standard limits to 8 bytes + // V-USB seems to break this limit with max of 254 bytes (255 designates "USB_NO_MSG" + // V-USB allows "LONG TRANSFERS" utilizing full 16bit wLength for upto 16384 bytes = exactly 16KBytes + // although considering sram on AVR doesn't explode with long transfers and read/write functions are in 8byte chunks, + // I think it really is limited to 8bytes + // One idea to squeeze more data is have a request type defined that utilizes wValue and wIndex to gain 4bytes + 8buffer = 12bytes 50% gain + // Not sure how to gain access to wValue/wIndex with vusb drivers... + // answer: usbFunctionSetup will get called for every setup packet and pass all 8 bytes of setup packet + // Can ultimately answer this question by counting how many startup packets are recieved by usbFunciton setup for transfers >8 bytes + // If when sending >8 byte control transfers, a setup packet only comes once, then there is nothing to abuse + // however if the same setup packet gets sent for every 8 bytes, it would be better to only perform 8byte transfers and stuff + // 4 more bytes in wValue and wIndex fields to increase throughput by ~50%!!! + // Testing shows that usbFunctionSetup only gets called once for transfers of 254 bytes + // So there is only one setup packet for multiple data packets of 8bytes each + // + //Still not sure increasing transfer length doesn't simply break up into bunch of small 8byte transfers although it doesn't sound like it. + //245byte limit is kind of a pain.. but wValue/wIndex fields could be abused to send 256 bytes + //Long transfers apparently max out speed @ 24KBps with 300 bytes: https://forums.obdev.at/viewtopic.php?t=3059 + // + //PAYLOAD: + //data: a suitably-sized data buffer for either input or output (depending on direction bits within bmRequestType) + // + //TIMEOUT: + //timeout: (in millseconds) that this function should wait before giving up due to no response being received. + // For an unlimited timeout, use value 0 + // USB nutshell: A compliant host requires control transfer response within 5sec + // + //RETURN: + // Returns on success, the number of bytes actually transferred + // LIBUSB_ERROR_TIMEOUT if the transfer timed out + // LIBUSB_ERROR_PIPE if the control request was not supported by the device + // LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + // another LIBUSB_ERROR code on other failures +int usb_transfer( USBtransfer *transfer ); #endif diff --git a/host/source/write_operations.c b/host/source/write_operations.c index 91600bc..9a2f09a 100644 --- a/host/source/write_operations.c +++ b/host/source/write_operations.c @@ -9,7 +9,7 @@ int write_file( libusb_device_handle *usbhandle, char *filename, char *ines_mapp int rv = 0; int index = 0; FILE *fileptr = NULL; - uint8_t data[128]; +//warn uint8_t data[128]; //first open file fileptr = fopen( filename, "rb"); @@ -35,11 +35,11 @@ int write_file( libusb_device_handle *usbhandle, char *filename, char *ines_mapp //9: Flags 9 //10: Flags 10 (unofficial) //11-15: Zero filled - uint8_t num_prg_banks = header[4]; - uint8_t num_chr_banks = header[5]; - - int prg_size = num_prg_banks * SIZE_PRG_BANK; - int chr_size = num_chr_banks * SIZE_CHR_BANK; +//warn uint8_t num_prg_banks = header[4]; +//warn uint8_t num_chr_banks = header[5]; +//warn +//warn int prg_size = num_prg_banks * SIZE_PRG_BANK; +//warn int chr_size = num_chr_banks * SIZE_CHR_BANK; //next check board inserted diff --git a/host/source/write_operations.h b/host/source/write_operations.h index 892150a..ab05bee 100644 --- a/host/source/write_operations.h +++ b/host/source/write_operations.h @@ -9,7 +9,6 @@ #include #include "usb_operations.h" -#include "shared_usb_commands.h" //uncomment to DEBUG this file alone #define DEBUG diff --git a/shared/shared_pinport.h b/shared/shared_dict_pinport.h similarity index 91% rename from shared/shared_pinport.h rename to shared/shared_dict_pinport.h index 43166d0..0439948 100644 --- a/shared/shared_pinport.h +++ b/shared/shared_dict_pinport.h @@ -1,5 +1,17 @@ -#ifndef _shared_pinport_h -#define _shared_pinport_h +#ifndef _shared_dict_pinport_h +#define _shared_dict_pinport_h + +//define dictionary's reference number in the shared_dictionaries.h file +//then include this dictionary file in shared_dictionaries.h +//The dictionary number is literally used as usb transfer request field +//the opcodes and operands in this dictionary are fed directly into usb setup packet's wValue wIndex fields + + +//============================================================================================= +//============================================================================================= +// PINPORT DICTIONARY +//============================================================================================= +//============================================================================================= //This file was created based on firmware version of pinport.h and pinport.c //the close relationship between these two files must be kept in mind when making changes. @@ -11,6 +23,7 @@ + //============================================================================================= // OPCODES with no operand and no return value besides SUCCESS/ERROR_CODE //============================================================================================= @@ -22,6 +35,10 @@ // Current limit for these types of opcodes is 0-127 // This allows for the MSB to be used for decoding pinport opcode to this type // +// Detect this opcode/operand setup with opcode between the following defines: +#define PP_OPCODE_ONLY_MIN 0x00 +#define PP_OPCODE_ONLY_MAX 0x7F +// //============================================================================================= //============================================================================================= @@ -230,14 +247,22 @@ //============================================================================================= // OPCODES WITH OPERAND and no return value besides SUCCESS/ERROR_CODE //============================================================================================= +// +#define PP_OPCODE_8BOP_MIN 0x80 +#define PP_OPCODE_8BOP_MAX 0x9F // 0x80-0x9F: opcodes with 8bit operand // 0x80-8A are only ones currently in use +// +#define PP_OPCODE_16BOP_MIN 0xA0 +#define PP_OPCODE_16BOP_MAX 0xAF // 0xA0-0xAF: opcodes with 16bit operand // 0xA0-A2 are only ones currently in use +// +#define PP_OPCODE_24BOP_MIN 0xB0 +#define PP_OPCODE_24BOP_MAX 0xBF // 0xB0-0xBF: opcodes with 24bit operand // 0xA0 is currently only one in use // -// // Current limit for these types of opcodes is 128-191 (0x80-0xBF) // This allows for the MSBs' to be used for decoding pinport opcode to this type // @@ -287,9 +312,6 @@ #define ctl_port_set 0x89 #define aux_port_set 0x8A -//TODO consider listing AVR internal registers here..? -//could be useful when utilizing SPI/I2C communications etc - //================================= @@ -340,6 +362,9 @@ //============================================================================================= // OPCODES with NO OPERAND but have RETURN VALUE plus SUCCESS/ERROR_CODE //============================================================================================= +// +#define PP_OPCODE_8BRV_MIN 0xC0 +#define PP_OPCODE_8BRV_MAX 0xFF // 0xC0-0xFF: opcodes with 8bit return value (plus SuCCESS/ERROR) // 0xC0-CB are only ones currently in use // @@ -348,6 +373,9 @@ // // Current limit for these types of opcodes is 192-255 (0xC0-0xFF) // This allows for the MSBs' to be used for decoding pinport opcode to this type +// Detect this opcode/operand setup with opcode between the following defines: +// +// Detect this opcode/operand setup with opcode between the following defines: // //============================================================================================= //============================================================================================= @@ -395,11 +423,6 @@ -//TODO consider listing AVR internal registers here..? -//could be useful when utilizing SPI/I2C communications etc - - - //============================================================================================= // OPCODES with OPERAND and RETURN VALUE plus SUCCESS/ERROR_CODE //============================================================================================= diff --git a/shared/shared_dictionaries.h b/shared/shared_dictionaries.h new file mode 100644 index 0000000..148cf2f --- /dev/null +++ b/shared/shared_dictionaries.h @@ -0,0 +1,16 @@ +#ifndef _shared_dictionaries_h +#define _shared_dictionaries_h + +//list of dictionary reference numbers +//these numbers literally sent in usb control transfer in request field +//the included dictionaries define opcodes and operands contained in transfer wValue wIndex fields +//they also define expected data buffer sizes and contents. + + +#define PINPORT 1 +#include "shared_dict_pinport.h" +//pinport dictionary has various commands giving low and mid level access to retro prog's i/o pins. +//It also contains internal avr registers associated with the avr's io. +//Access to other internal avr registers should be placed in other associated dictionaries. + +#endif diff --git a/shared/shared_errors.h b/shared/shared_errors.h new file mode 100644 index 0000000..a17b906 --- /dev/null +++ b/shared/shared_errors.h @@ -0,0 +1,15 @@ +#ifndef _error_codes_h +#define _error_codes_h + +#define SUCCESS 0 + +//greater than 128 are possible avr return codes +#define ERR_UNKN_DICTIONARY 128 + +#define ERR_UNKN_PP_OPCODE_ONLY 129 +#define ERR_UNKN_PP_OPCODE_8BOP 130 +#define ERR_UNKN_PP_OPCODE_16BOP 131 +#define ERR_UNKN_PP_OPCODE_24BOP 132 +#define ERR_UNKN_PP_OPCODE_8BRV 133 + +#endif diff --git a/shared/shared_usb_commands.h b/shared/shared_usb_commands.h deleted file mode 100644 index 2d7c453..0000000 --- a/shared/shared_usb_commands.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _shared_usb_commands_h -#define _shared_usb_commands_h - -#define REQ_LED_ON 1 -#define REQ_LED_OFF 2 - -#endif