/* * dfu-programmer * * $Id: dfu.c,v 1.3 2006/06/20 06:28:04 schmidtw Exp $ * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "dfu.h" /* DFU commands */ #define DFU_DETACH 0 #define DFU_DNLOAD 1 #define DFU_UPLOAD 2 #define DFU_GETSTATUS 3 #define DFU_CLRSTATUS 4 #define DFU_GETSTATE 5 #define DFU_ABORT 6 #define INVALID_DFU_TIMEOUT -1 static int dfu_timeout = INVALID_DFU_TIMEOUT; static unsigned short transaction = 0; static int dfu_debug_level = 0; void dfu_init( const int timeout ) { if( timeout > 0 ) { dfu_timeout = timeout; } else { if( 0 != dfu_debug_level ) fprintf( stderr, "dfu_init: Invalid timeout value.\n" ); } } static int dfu_verify_init( const char *function ) { if( INVALID_DFU_TIMEOUT == dfu_timeout ) { if( 0 != dfu_debug_level ) fprintf( stderr, "%s: dfu system not property initialized.\n", function ); return -1; } return 0; } void dfu_debug( const int level ) { dfu_debug_level = level; } /* * DFU_DETACH Request (DFU Spec 1.0, Section 5.1) * * device - the usb_dev_handle to communicate with * interface - the interface to communicate with * timeout - the timeout in ms the USB device should wait for a pending * USB reset before giving up and terminating the operation * * returns 0 or < 0 on error */ int dfu_detach( struct usb_dev_handle *device, const unsigned short interface, const unsigned short timeout ) { if( 0 != dfu_verify_init(__FUNCTION__) ) return -1; return usb_control_msg( device, /* bmRequestType */ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bRequest */ DFU_DETACH, /* wValue */ timeout, /* wIndex */ interface, /* Data */ NULL, /* wLength */ 0, dfu_timeout ); } /* * DFU_DNLOAD Request (DFU Spec 1.0, Section 6.1.1) * * device - the usb_dev_handle to communicate with * interface - the interface to communicate with * length - the total number of bytes to transfer to the USB * device - must be less than wTransferSize * data - the data to transfer * * returns the number of bytes written or < 0 on error */ int dfu_download( struct usb_dev_handle *device, const unsigned short interface, const unsigned short length, char* data ) { int status; if( 0 != dfu_verify_init(__FUNCTION__) ) return -1; /* Sanity checks */ if( (0 != length) && (NULL == data) ) { if( 0 != dfu_debug_level ) fprintf( stderr, "%s: data was NULL, but length != 0\n", __FUNCTION__ ); return -1; } if( (0 == length) && (NULL != data) ) { if( 0 != dfu_debug_level ) fprintf( stderr, "%s: data was not NULL, but length == 0\n", __FUNCTION__ ); return -2; } status = usb_control_msg( device, /* bmRequestType */ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bRequest */ DFU_DNLOAD, /* wValue */ transaction++, /* wIndex */ interface, /* Data */ data, /* wLength */ length, dfu_timeout ); if( status < 0 ) { fprintf( stderr, "%s error %d\n", __FUNCTION__, status ); } return status; } /* * DFU_UPLOAD Request (DFU Spec 1.0, Section 6.2) * * device - the usb_dev_handle to communicate with * interface - the interface to communicate with * length - the maximum number of bytes to receive from the USB * device - must be less than wTransferSize * data - the buffer to put the received data in * * returns the number of bytes received or < 0 on error */ int dfu_upload( struct usb_dev_handle *device, const unsigned short interface, const unsigned short length, char* data ) { int status; if( 0 != dfu_verify_init(__FUNCTION__) ) return -1; /* Sanity checks */ if( (0 == length) || (NULL == data) ) { if( 0 != dfu_debug_level ) fprintf( stderr, "%s: data was NULL, or length is 0\n", __FUNCTION__ ); return -1; } status = usb_control_msg( device, /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bRequest */ DFU_UPLOAD, /* wValue */ transaction++, /* wIndex */ interface, /* Data */ data, /* wLength */ length, dfu_timeout ); if( status < 0 ) { fprintf( stderr, "%s error %d\n", __FUNCTION__, status ); } return status; } /* * DFU_GETSTATUS Request (DFU Spec 1.0, Section 6.1.2) * * device - the usb_dev_handle to communicate with * interface - the interface to communicate with * status - the data structure to be populated with the results * * return the number of bytes read in or < 0 on an error */ int dfu_get_status( struct usb_dev_handle *device, const unsigned short interface, struct dfu_status *status ) { char buffer[6]; int result; if( 0 != dfu_verify_init(__FUNCTION__) ) return -1; /* Initialize the status data structure */ status->bStatus = DFU_STATUS_ERROR_UNKNOWN; status->bwPollTimeout = 0; status->bState = STATE_DFU_ERROR; status->iString = 0; result = usb_control_msg( device, /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bRequest */ DFU_GETSTATUS, /* wValue */ 0, /* wIndex */ interface, /* Data */ buffer, /* wLength */ 6, dfu_timeout ); if( 6 == result ) { status->bStatus = buffer[0]; status->bwPollTimeout = ((0xff & buffer[3]) << 16) | ((0xff & buffer[2]) << 8) | (0xff & buffer[1]); status->bState = buffer[4]; status->iString = buffer[5]; } return result; } /* * DFU_CLRSTATUS Request (DFU Spec 1.0, Section 6.1.3) * * device - the usb_dev_handle to communicate with * interface - the interface to communicate with * * return 0 or < 0 on an error */ int dfu_clear_status( struct usb_dev_handle *device, const unsigned short interface ) { if( 0 != dfu_verify_init(__FUNCTION__) ) return -1; return usb_control_msg( device, /* bmRequestType */ USB_ENDPOINT_OUT| USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bRequest */ DFU_CLRSTATUS, /* wValue */ 0, /* wIndex */ interface, /* Data */ NULL, /* wLength */ 0, dfu_timeout ); } /* * DFU_GETSTATE Request (DFU Spec 1.0, Section 6.1.5) * * device - the usb_dev_handle to communicate with * interface - the interface to communicate with * length - the maximum number of bytes to receive from the USB * device - must be less than wTransferSize * data - the buffer to put the received data in * * returns the state or < 0 on error */ int dfu_get_state( struct usb_dev_handle *device, const unsigned short interface ) { int result; char buffer[1]; if( 0 != dfu_verify_init(__FUNCTION__) ) return -1; result = usb_control_msg( device, /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bRequest */ DFU_GETSTATE, /* wValue */ 0, /* wIndex */ interface, /* Data */ buffer, /* wLength */ 1, dfu_timeout ); /* Return the error if there is one. */ if( result < 1 ) { return result; } /* Return the state. */ return buffer[0]; } /* * DFU_ABORT Request (DFU Spec 1.0, Section 6.1.4) * * device - the usb_dev_handle to communicate with * interface - the interface to communicate with * * returns 0 or < 0 on an error */ int dfu_abort( struct usb_dev_handle *device, const unsigned short interface ) { if( 0 != dfu_verify_init(__FUNCTION__) ) return -1; return usb_control_msg( device, /* bmRequestType */ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, /* bRequest */ DFU_ABORT, /* wValue */ 0, /* wIndex */ interface, /* Data */ NULL, /* wLength */ 0, dfu_timeout ); } char* dfu_state_to_string( int state ) { char *message = NULL; switch( state ) { case STATE_APP_IDLE: message = "appIDLE"; break; case STATE_APP_DETACH: message = "appDETACH"; break; case STATE_DFU_IDLE: message = "dfuIDLE"; break; case STATE_DFU_DOWNLOAD_SYNC: message = "dfuDNLOAD-SYNC"; break; case STATE_DFU_DOWNLOAD_BUSY: message = "dfuDNBUSY"; break; case STATE_DFU_DOWNLOAD_IDLE: message = "dfuDNLOAD-IDLE"; break; case STATE_DFU_MANIFEST_SYNC: message = "dfuMANIFEST-SYNC"; break; case STATE_DFU_MANIFEST: message = "dfuMANIFEST"; break; case STATE_DFU_MANIFEST_WAIT_RESET: message = "dfuMANIFEST-WAIT-RESET"; break; case STATE_DFU_UPLOAD_IDLE: message = "dfuUPLOAD-IDLE"; break; case STATE_DFU_ERROR: message = "dfuERROR"; break; } return message; } /* Chapter 6.1.2 */ static const char *dfu_status_names[] = { [DFU_STATUS_OK] = "No error condition is present", [DFU_STATUS_errTARGET] = "File is not targeted for use by this device", [DFU_STATUS_errFILE] = "File is for this device but fails some vendor-specific test", [DFU_STATUS_errWRITE] = "Device is unable to write memory", [DFU_STATUS_errERASE] = "Memory erase function failed", [DFU_STATUS_errCHECK_ERASED] = "Memory erase check failed", [DFU_STATUS_errPROG] = "Program memory function failed", [DFU_STATUS_errVERIFY] = "Programmed emmory failed verification", [DFU_STATUS_errADDRESS] = "Cannot program memory due to received address that is out of range", [DFU_STATUS_errNOTDONE] = "Received DFU_DNLOAD with wLength = 0, but device does not think that it has all data yet", [DFU_STATUS_errFIRMWARE] = "Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations", [DFU_STATUS_errVENDOR] = "iString indicates a vendor specific error", [DFU_STATUS_errUSBR] = "Device detected unexpected USB reset signalling", [DFU_STATUS_errPOR] = "Device detected unexpected power on reset", [DFU_STATUS_errUNKNOWN] = "Something went wrong, but the device does not know what it was", [DFU_STATUS_errSTALLEDPKT] = "Device stalled an unexpected request", }; const char *dfu_status_to_string(int status) { if (status > DFU_STATUS_errSTALLEDPKT) return "INVALID"; return dfu_status_names[status]; }