2014-08-15 17:53:43 +00:00
/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>.
* Modifications by Jacob Alexander 2014 < haata @ kiibohd . com >
*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
// ----- Local Includes -----
# include "usb.h"
# include "dfu.h"
// ----- Functions -----
void dfu_write_done ( enum dfu_status err , struct dfu_ctx * ctx )
{
2015-03-09 01:40:01 +00:00
ctx - > status = err ;
if ( ctx - > status = = DFU_STATUS_OK ) {
switch ( ctx - > state ) {
case DFU_STATE_dfuDNBUSY :
ctx - > state = DFU_STATE_dfuDNLOAD_IDLE ;
break ;
default :
break ;
}
} else {
ctx - > state = DFU_STATE_dfuERROR ;
}
2014-08-15 17:53:43 +00:00
}
static void dfu_dnload_complete ( void * buf , ssize_t len , void * cbdata )
{
2015-03-09 01:40:01 +00:00
struct dfu_ctx * ctx = cbdata ;
2014-08-15 17:53:43 +00:00
2015-03-09 01:40:01 +00:00
if ( len > 0 )
ctx - > state = DFU_STATE_dfuDNBUSY ;
else
ctx - > state = DFU_STATE_dfuMANIFEST ;
ctx - > status = ctx - > finish_write ( buf , ctx - > off , len ) ;
ctx - > off + = len ;
ctx - > len = len ;
2014-08-15 17:53:43 +00:00
2015-03-09 01:40:01 +00:00
if ( ctx - > status ! = DFU_STATUS_async )
dfu_write_done ( ctx - > status , ctx ) ;
2014-08-15 17:53:43 +00:00
2015-03-09 01:40:01 +00:00
usb_handle_control_status ( ctx - > state = = DFU_STATE_dfuERROR ) ;
2014-08-15 17:53:43 +00:00
}
static void dfu_reset_system ( void * buf , ssize_t len , void * cbdata )
{
2015-03-09 01:40:01 +00:00
SOFTWARE_RESET ( ) ;
2014-08-15 17:53:43 +00:00
}
static int dfu_handle_control ( struct usb_ctrl_req_t * req , void * data )
{
2015-03-09 01:40:01 +00:00
struct dfu_ctx * ctx = data ;
int fail = 1 ;
switch ( ( enum dfu_ctrl_req_code ) req - > bRequest ) {
case USB_CTRL_REQ_DFU_DNLOAD : {
void * buf ;
switch ( ctx - > state ) {
case DFU_STATE_dfuIDLE :
ctx - > off = 0 ;
break ;
case DFU_STATE_dfuDNLOAD_IDLE :
break ;
default :
goto err ;
}
/**
* XXX we are not allowed to STALL here , and we need to eat all transferred data .
* better not allow setup_write to break the protocol .
*/
ctx - > status = ctx - > setup_write ( ctx - > off , req - > wLength , & buf ) ;
if ( ctx - > status ! = DFU_STATUS_OK ) {
ctx - > state = DFU_STATE_dfuERROR ;
goto err_have_status ;
}
if ( req - > wLength > 0 )
usb_ep0_rx ( buf , req - > wLength , dfu_dnload_complete , ctx ) ;
else
dfu_dnload_complete ( NULL , 0 , ctx ) ;
goto out_no_status ;
}
case USB_CTRL_REQ_DFU_GETSTATUS : {
struct dfu_status_t st ;
st . bState = ctx - > state ;
st . bStatus = ctx - > status ;
st . bwPollTimeout = 1000 ; /* XXX */
/**
* If we ' re in DFU_STATE_dfuMANIFEST , we just finished
* the download , and we ' re just about to send our last
* status report . Once the report has been sent , go
* and reset the system to put the new firmware into
* effect .
*/
usb_ep0_tx_cp ( & st , sizeof ( st ) , req - > wLength , NULL , NULL ) ;
if ( ctx - > state = = DFU_STATE_dfuMANIFEST ) {
usb_handle_control_status_cb ( dfu_reset_system ) ;
goto out_no_status ;
}
break ;
}
case USB_CTRL_REQ_DFU_CLRSTATUS :
ctx - > state = DFU_STATE_dfuIDLE ;
ctx - > status = DFU_STATUS_OK ;
break ;
case USB_CTRL_REQ_DFU_GETSTATE : {
uint8_t st = ctx - > state ;
usb_ep0_tx_cp ( & st , sizeof ( st ) , req - > wLength , NULL , NULL ) ;
break ;
}
case USB_CTRL_REQ_DFU_ABORT :
switch ( ctx - > state ) {
case DFU_STATE_dfuIDLE :
case DFU_STATE_dfuDNLOAD_IDLE :
/* case DFU_STATE_dfuUPLOAD_IDLE: */
ctx - > state = DFU_STATE_dfuIDLE ;
break ;
default :
goto err ;
}
break ;
/* case USB_CTRL_REQ_DFU_UPLOAD: */
default :
return ( 0 ) ;
}
fail = 0 ;
goto out ;
2014-08-15 17:53:43 +00:00
err :
2015-03-09 01:40:01 +00:00
ctx - > status = DFU_STATUS_errSTALLEDPKT ;
2014-08-15 17:53:43 +00:00
err_have_status :
2015-03-09 01:40:01 +00:00
ctx - > state = DFU_STATE_dfuERROR ;
2014-08-15 17:53:43 +00:00
out :
2015-03-09 01:40:01 +00:00
usb_handle_control_status ( fail ) ;
2014-08-15 17:53:43 +00:00
out_no_status :
2015-03-09 01:40:01 +00:00
return ( 1 ) ;
2014-08-15 17:53:43 +00:00
}
void dfu_init ( dfu_setup_write_t setup_write , dfu_finish_write_t finish_write , struct dfu_ctx * ctx )
{
2015-03-09 01:40:01 +00:00
ctx - > state = DFU_STATE_dfuIDLE ;
ctx - > setup_write = setup_write ;
ctx - > finish_write = finish_write ;
usb_attach_function ( & dfu_function , & ctx - > header ) ;
2014-08-15 17:53:43 +00:00
}
const struct usbd_function dfu_function = {
2015-03-09 01:40:01 +00:00
. control = dfu_handle_control ,
. interface_count = USB_FUNCTION_DFU_IFACE_COUNT ,
2014-08-15 17:53:43 +00:00
} ;