static int handle_ptp( int h, ptp_data *data, int opcode, int sess_id, int trans_id, int param1, int param2, int param3, int param4, int param5) { static union { char *str; } temp_data; static int temp_data_kind = 0; // 0: nothing, 1: ascii string static int temp_data_extra; // size (ascii string) PTPContainer ptp; // initialise default response memset(&ptp,0,sizeof(PTPContainer)); ptp.code = PTP_RC_OK; ptp.sess_id = sess_id; ptp.trans_id = trans_id; ptp.num_param = 0; // TODO // calling this on every PTP command is not good on cameras without CAM_FIRMWARE_MEMINFO // since it figures out free memory by repeatedly malloc'ing! // using half of available memory may be undesirable in some cases as well buf_size=(core_get_free_memory()>>1); // make sure size is an integer number of words (avoid some possible issues with multiple receive calls) buf_size &= 0xFFFFFFFC; // handle command switch ( param1 ) { case PTP_CHDK_Version: ptp.num_param = 2; ptp.param1 = PTP_CHDK_VERSION_MAJOR; ptp.param2 = PTP_CHDK_VERSION_MINOR; break; case PTP_CHDK_ScriptSupport: ptp.num_param = 1; ptp.param1 = 0; #ifdef OPT_LUA ptp.param1 |= PTP_CHDK_SCRIPT_SUPPORT_LUA; #endif break; case PTP_CHDK_ScriptStatus: ptp.num_param = 1; // TODO script_is_running should always be defined, just ret 0 if script disabled ptp.param1 = 0; #ifdef OPT_SCRIPTING ptp.param1 |= script_is_running()?PTP_CHDK_SCRIPT_STATUS_RUN:0; #ifdef OPT_LUA ptp.param1 |= (!script_msg_q_empty(&msg_q_out))?PTP_CHDK_SCRIPT_STATUS_MSG:0; #endif #endif break; case PTP_CHDK_GetMemory: if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size? { ptp.code = PTP_RC_GeneralError; break; } if ( !send_ptp_data(data,(char *) param2,param3) ) { ptp.code = PTP_RC_GeneralError; } break; case PTP_CHDK_SetMemory: if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size? { ptp.code = PTP_RC_GeneralError; break; } data->get_data_size(data->handle); // XXX required call before receiving if ( !recv_ptp_data(data,(char *) param2,param3) ) { ptp.code = PTP_RC_GeneralError; } break; case PTP_CHDK_CallFunction: { int s; int *buf = (int *) malloc((10+1)*sizeof(int)); if ( buf == NULL ) { ptp.code = PTP_RC_GeneralError; break; } s = data->get_data_size(data->handle); if ( !recv_ptp_data(data,(char *) buf,s) ) { ptp.code = PTP_RC_GeneralError; break; } ptp.num_param = 1; ptp.param1 = ((int (*)(int,int,int,int,int,int,int,int,int,int)) buf[0])(buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10]); free(buf); break; } case PTP_CHDK_TempData: if ( param2 & PTP_CHDK_TD_DOWNLOAD ) { const char *s = NULL; size_t l = 0; if ( temp_data_kind == 0 ) { ptp.code = PTP_RC_GeneralError; break; } if ( temp_data_kind == 1 ) { s = temp_data.str; l = temp_data_extra; } if ( !send_ptp_data(data,s,l) ) { ptp.code = PTP_RC_GeneralError; break; } } else if ( ! (param2 & PTP_CHDK_TD_CLEAR) ) { if ( temp_data_kind == 1 ) { free(temp_data.str); } temp_data_kind = 0; temp_data_extra = data->get_data_size(data->handle); temp_data.str = (char *) malloc(temp_data_extra); if ( temp_data.str == NULL ) { ptp.code = PTP_RC_GeneralError; break; } if ( !recv_ptp_data(data,temp_data.str,temp_data_extra) ) { ptp.code = PTP_RC_GeneralError; break; } temp_data_kind = 1; } if ( param2 & PTP_CHDK_TD_CLEAR ) { if ( temp_data_kind == 1 ) { free(temp_data.str); } temp_data_kind = 0; } break; case PTP_CHDK_UploadFile: { FILE *f=NULL; char *buf=NULL, *fn=NULL; unsigned data_size,fn_len,chunk_size; data_size = data->get_data_size(data->handle); while ( data_size > 0 ) { chunk_size = (data_size > buf_size) ? buf_size:data_size; // first time through // allocate buffer, parse out the file name and open file if(!buf) { buf=malloc(chunk_size); if(!buf) { ptp.code = PTP_RC_GeneralError; break; } recv_ptp_data(data,buf,chunk_size); fn_len = *(unsigned *)buf; fn = malloc(fn_len+1); if(!fn) { ptp.code = PTP_RC_GeneralError; break; } memcpy(fn,buf+4,fn_len); fn[fn_len] = 0; f = fopen(fn,"wb"); free(fn); if(!f) { ptp.code = PTP_RC_GeneralError; break; } fwrite(buf+4+fn_len,1,chunk_size - 4 - fn_len,f); } else { recv_ptp_data(data,buf,chunk_size); fwrite(buf,1,chunk_size,f); } data_size -= chunk_size; } if(f) { fclose(f); } free(buf); if(data_size > 0 && ptp.code != PTP_RC_OK) { flush_recv_ptp_data(data,data_size); } break; } case PTP_CHDK_DownloadFile: { FILE *f; int tmp,t,s,r,fn_len; char *buf, *fn; if ( temp_data_kind != 1 ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); ptp.code = PTP_RC_GeneralError; break; } fn = (char *) malloc(temp_data_extra+1); if ( fn == NULL ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); free(temp_data.str); temp_data_kind = 0; ptp.code = PTP_RC_GeneralError; break; } memcpy(fn,temp_data.str,temp_data_extra); fn[temp_data_extra] = '\0'; free(temp_data.str); temp_data_kind = 0; f = fopen(fn,"rb"); if ( f == NULL ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); ptp.code = PTP_RC_GeneralError; free(fn); break; } free(fn); fseek(f,0,SEEK_END); s = ftell(f); fseek(f,0,SEEK_SET); buf = (char *) malloc(buf_size); if ( buf == NULL ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); ptp.code = PTP_RC_GeneralError; break; } tmp = s; t = s; while ( (r = fread(buf,1,(t<buf_size)?t:buf_size,f)) > 0 ) { t -= r; // cannot use send_ptp_data here data->send_data(data->handle,buf,r,tmp,0,0,0); tmp = 0; } fclose(f); // XXX check that we actually read/send s bytes! (t == 0) ptp.num_param = 1; ptp.param1 = s; free(buf); break; } break; #ifdef OPT_LUA // TODO this should flush data even if scripting isn't supported case PTP_CHDK_ExecuteScript: { int s; char *buf; script_run_id++; ptp.num_param = 2; ptp.param1 = script_run_id; if ( param2 != PTP_CHDK_SL_LUA ) { ptp.code = PTP_RC_ParameterNotSupported; break; } s = data->get_data_size(data->handle); buf = (char *) malloc(s); if ( buf == NULL ) { ptp.code = PTP_RC_GeneralError; break; } recv_ptp_data(data,buf,s); // error details will be passed in a message if (script_start_ptp(buf) < 0) { ptp.param2 = PTP_CHDK_S_ERRTYPE_COMPILE; } else { ptp.param2 = PTP_CHDK_S_ERRTYPE_NONE; } free(buf); break; } case PTP_CHDK_ReadScriptMsg: { char *pdata=""; unsigned datasize=1; ptp_script_msg *msg = dequeue_script_msg(&msg_q_out); ptp.num_param = 4; if(msg) { ptp.param1 = msg->type; ptp.param2 = msg->subtype; ptp.param3 = msg->script_id; ptp.param4 = msg->size; // empty messages must have a data phase, so use default if no data if(msg->size) { datasize = msg->size; pdata = msg->data; } } else { // return a fully formed message for easier handling ptp.param1 = PTP_CHDK_S_MSGTYPE_NONE; ptp.param2 = 0; ptp.param3 = 0; ptp.param4 = 0; } // NOTE message is lost if sending failed if ( !send_ptp_data(data,pdata,datasize) ) { ptp.code = PTP_RC_GeneralError; } free(msg); break; } case PTP_CHDK_WriteScriptMsg: { int msg_size; ptp_script_msg *msg; ptp.num_param = 1; ptp.param1 = PTP_CHDK_S_MSGSTATUS_OK; if (!script_is_running()) { ptp.param1 = PTP_CHDK_S_MSGSTATUS_NOTRUN; } else if(param2 && param2 != script_run_id) {// check if target script for message is running ptp.param1 = PTP_CHDK_S_MSGSTATUS_BADID; } else if(script_msg_q_full(&msg_q_in)) { ptp.param1 = PTP_CHDK_S_MSGSTATUS_QFULL; } msg_size = data->get_data_size(data->handle); // if something was wrong, don't bother creating message, just flush if(ptp.param1 != PTP_CHDK_S_MSGSTATUS_OK) { flush_recv_ptp_data(data,msg_size); break; } msg = ptp_script_create_msg(PTP_CHDK_S_MSGTYPE_USER,PTP_CHDK_TYPE_STRING,msg_size,NULL); if ( !msg ) // malloc error or zero size { // if size is zero, things will get hosed no matter what flush_recv_ptp_data(data,msg_size); ptp.code = PTP_RC_GeneralError; break; } msg->script_id = param2; if ( !recv_ptp_data(data,msg->data,msg->size) ) { ptp.code = PTP_RC_GeneralError; free(msg); break; } if( !enqueue_script_msg(&msg_q_in,msg) ) { ptp.code = PTP_RC_GeneralError; free(msg); } break; } #endif case PTP_CHDK_GetDisplayData: ptp.num_param = 1; ptp.param1 = live_view_get_data(data,param2); if(!ptp.param1) { ptp.code = PTP_RC_GeneralError; // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); } break; default: ptp.code = PTP_RC_ParameterNotSupported; break; } // send response data->send_resp( data->handle, &ptp ); return 1; }
static int handle_ptp( int h, ptp_data *data, int opcode, int sess_id, int trans_id, int param1, int param2, int param3, int param4, int param5) { static union { char *str; } temp_data; static int temp_data_kind = 0; // 0: nothing, 1: ascii string static int temp_data_extra; // size (ascii string) PTPContainer ptp; // initialise default response memset(&ptp,0,sizeof(PTPContainer)); ptp.code = PTP_RC_OK; ptp.sess_id = sess_id; ptp.trans_id = trans_id; ptp.num_param = 0; // TODO // calling this on every PTP command is not good on cameras without CAM_FIRMWARE_MEMINFO // since it figures out free memory by repeatedly malloc'ing! // using half of available memory may be undesirable in some cases as well buf_size=(core_get_free_memory()>>1); // make sure size is an integer number of words (avoid some possible issues with multiple receive calls) buf_size &= 0xFFFFFFFC; // handle command switch ( param1 ) { case PTP_CHDK_Version: ptp.num_param = 2; ptp.param1 = PTP_CHDK_VERSION_MAJOR; ptp.param2 = PTP_CHDK_VERSION_MINOR; break; case PTP_CHDK_ScriptSupport: ptp.num_param = 1; ptp.param1 = 0; ptp.param1 |= PTP_CHDK_SCRIPT_SUPPORT_LUA; break; case PTP_CHDK_ScriptStatus: ptp.num_param = 1; ptp.param1 = 0; ptp.param1 |= script_is_running()?PTP_CHDK_SCRIPT_STATUS_RUN:0; ptp.param1 |= (!script_msg_q_empty(&msg_q_out))?PTP_CHDK_SCRIPT_STATUS_MSG:0; break; case PTP_CHDK_GetMemory: if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size? { ptp.code = PTP_RC_GeneralError; break; } if ( !send_ptp_data(data,(char *) param2,param3) ) { ptp.code = PTP_RC_GeneralError; } break; case PTP_CHDK_SetMemory: if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size? { ptp.code = PTP_RC_GeneralError; break; } data->get_data_size(data->handle); // XXX required call before receiving if ( !recv_ptp_data(data,(char *) param2,param3) ) { ptp.code = PTP_RC_GeneralError; } break; case PTP_CHDK_CallFunction: { int s = data->get_data_size(data->handle); if (s <= 0 || (s&3)) // no data or not an integer number of args { ptp.code = PTP_RC_GeneralError; break; } unsigned *buf = malloc(s); if ( buf == NULL ) { ptp.code = PTP_RC_GeneralError; break; } if ( recv_ptp_data(data,(char *) buf,s) ) { ptp.num_param = 1; ptp.param1 = call_func_ptr((void *)buf[0],(unsigned *)buf+1,(s-4)/4); } else { ptp.code = PTP_RC_GeneralError; } free(buf); break; } case PTP_CHDK_TempData: if ( param2 & PTP_CHDK_TD_DOWNLOAD ) { const char *s = NULL; size_t l = 0; if ( temp_data_kind == 0 ) { ptp.code = PTP_RC_GeneralError; break; } if ( temp_data_kind == 1 ) { s = temp_data.str; l = temp_data_extra; } if ( !send_ptp_data(data,s,l) ) { ptp.code = PTP_RC_GeneralError; break; } } else if ( ! (param2 & PTP_CHDK_TD_CLEAR) ) { if ( temp_data_kind == 1 ) { free(temp_data.str); } temp_data_kind = 0; temp_data_extra = data->get_data_size(data->handle); temp_data.str = (char *) malloc(temp_data_extra); if ( temp_data.str == NULL ) { ptp.code = PTP_RC_GeneralError; break; } if ( !recv_ptp_data(data,temp_data.str,temp_data_extra) ) { ptp.code = PTP_RC_GeneralError; break; } temp_data_kind = 1; } if ( param2 & PTP_CHDK_TD_CLEAR ) { if ( temp_data_kind == 1 ) { free(temp_data.str); } temp_data_kind = 0; } break; case PTP_CHDK_UploadFile: { FILE *f=NULL; char *buf=NULL, *fn=NULL; unsigned data_size,fn_len,chunk_size; data_size = data->get_data_size(data->handle); while ( data_size > 0 ) { chunk_size = (data_size > buf_size) ? buf_size:data_size; // first time through // allocate buffer, parse out the file name and open file if(!buf) { buf=malloc(chunk_size); if(!buf) { ptp.code = PTP_RC_GeneralError; break; } recv_ptp_data(data,buf,chunk_size); fn_len = *(unsigned *)buf; fn = malloc(fn_len+1); if(!fn) { ptp.code = PTP_RC_GeneralError; break; } memcpy(fn,buf+4,fn_len); fn[fn_len] = 0; f = fopen(fn,"wb"); free(fn); if(!f) { ptp.code = PTP_RC_GeneralError; break; } fwrite(buf+4+fn_len,1,chunk_size - 4 - fn_len,f); } else { recv_ptp_data(data,buf,chunk_size); fwrite(buf,1,chunk_size,f); } data_size -= chunk_size; } if(f) { fclose(f); } free(buf); if(data_size > 0 && ptp.code != PTP_RC_OK) { flush_recv_ptp_data(data,data_size); } break; } case PTP_CHDK_DownloadFile: { FILE *f; int tmp,t,s,r; char *buf, *fn; if ( temp_data_kind != 1 ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); ptp.code = PTP_RC_GeneralError; break; } fn = (char *) malloc(temp_data_extra+1); if ( fn == NULL ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); free(temp_data.str); temp_data_kind = 0; ptp.code = PTP_RC_GeneralError; break; } memcpy(fn,temp_data.str,temp_data_extra); fn[temp_data_extra] = '\0'; free(temp_data.str); temp_data_kind = 0; f = fopen(fn,"rb"); if ( f == NULL ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); ptp.code = PTP_RC_GeneralError; free(fn); break; } free(fn); fseek(f,0,SEEK_END); s = ftell(f); fseek(f,0,SEEK_SET); buf = (char *) malloc(buf_size); if ( buf == NULL ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); ptp.code = PTP_RC_GeneralError; break; } tmp = s; t = s; while ( (r = fread(buf,1,(t<buf_size)?t:buf_size,f)) > 0 ) { t -= r; // cannot use send_ptp_data here data->send_data(data->handle,buf,r,tmp,0,0,0); tmp = 0; } fclose(f); // XXX check that we actually read/send s bytes! (t == 0) ptp.num_param = 1; ptp.param1 = s; free(buf); break; } break; case PTP_CHDK_ExecuteScript: { int s; char *buf; ptp.num_param = 2; ptp.param1 = script_run_id; // in error case, ID of most recent script s = data->get_data_size(data->handle); if ( (param2&PTP_CHDK_SL_MASK) != PTP_CHDK_SL_LUA ) { flush_recv_ptp_data(data,s); ptp.code = PTP_RC_ParameterNotSupported; break; } buf = (char *) malloc(s); if ( buf == NULL ) { ptp.code = PTP_RC_GeneralError; break; } recv_ptp_data(data,buf,s); // applies to both running and "interrupted" state, since interrupted means running restore if (camera_info.state.state_kbd_script_run) { // note script ID is still incremented in this case if (param2 & PTP_CHDK_SCRIPT_FL_NOKILL) { // no message is added in this case, since the running script might also be doing // stuff with messages ptp.param2 = PTP_CHDK_S_ERR_SCRIPTRUNNING; free(buf); break; } // kill the script script_wait_terminate(); } // empty message queues if requested. if(param2 & PTP_CHDK_SCRIPT_FL_FLUSH_CAM_MSGS) { empty_script_msg_q(&msg_q_out); } // Script either was not running or has been killed, so safe to remove from inbound queue outside of kbd task if(param2 & PTP_CHDK_SCRIPT_FL_FLUSH_HOST_MSGS) { empty_script_msg_q(&msg_q_in); } // increment script ID if script is loaded script_run_id++; ptp.param1 = script_run_id; // error details will be passed in a message if (script_start_ptp(buf) < 0) { ptp.param2 = PTP_CHDK_S_ERRTYPE_COMPILE; } else { ptp.param2 = PTP_CHDK_S_ERRTYPE_NONE; } free(buf); break; } case PTP_CHDK_ReadScriptMsg: { char *pdata=""; unsigned datasize=1; ptp_script_msg *msg = dequeue_script_msg(&msg_q_out); ptp.num_param = 4; if(msg) { ptp.param1 = msg->type; ptp.param2 = msg->subtype; ptp.param3 = msg->script_id; ptp.param4 = msg->size; // empty messages must have a data phase, so use default if no data if(msg->size) { datasize = msg->size; pdata = msg->data; } } else { // return a fully formed message for easier handling ptp.param1 = PTP_CHDK_S_MSGTYPE_NONE; ptp.param2 = 0; ptp.param3 = 0; ptp.param4 = 0; } // NOTE message is lost if sending failed if ( !send_ptp_data(data,pdata,datasize) ) { ptp.code = PTP_RC_GeneralError; } free(msg); break; } case PTP_CHDK_WriteScriptMsg: { int msg_size; ptp_script_msg *msg; ptp.num_param = 1; ptp.param1 = PTP_CHDK_S_MSGSTATUS_OK; if (!script_is_running()) { ptp.param1 = PTP_CHDK_S_MSGSTATUS_NOTRUN; } else if(param2 && param2 != script_run_id) {// check if target script for message is running ptp.param1 = PTP_CHDK_S_MSGSTATUS_BADID; } else if(script_msg_q_full(&msg_q_in)) { ptp.param1 = PTP_CHDK_S_MSGSTATUS_QFULL; } msg_size = data->get_data_size(data->handle); // if something was wrong, don't bother creating message, just flush if(ptp.param1 != PTP_CHDK_S_MSGSTATUS_OK) { flush_recv_ptp_data(data,msg_size); break; } msg = ptp_script_create_msg(PTP_CHDK_S_MSGTYPE_USER,PTP_CHDK_TYPE_STRING,msg_size,NULL); if ( !msg ) // malloc error or zero size { // if size is zero, things will get hosed no matter what flush_recv_ptp_data(data,msg_size); ptp.code = PTP_RC_GeneralError; break; } msg->script_id = param2; if ( !recv_ptp_data(data,msg->data,msg->size) ) { ptp.code = PTP_RC_GeneralError; free(msg); break; } if( !enqueue_script_msg(&msg_q_in,msg) ) { ptp.code = PTP_RC_GeneralError; free(msg); } break; } case PTP_CHDK_GetDisplayData: { extern int live_view_get_data(ptp_data *data, int flags); ptp.num_param = 1; ptp.param1 = live_view_get_data(data,param2); if(!ptp.param1) { ptp.code = PTP_RC_GeneralError; // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); } } break; case PTP_CHDK_RemoteCaptureIsReady: ptp.num_param = 2; remotecap_is_ready(&ptp.param1,&ptp.param2); break; case PTP_CHDK_RemoteCaptureGetData: { unsigned int rcgd_size; int rcgd_status; char *rcgd_addr; int rcgd_pos; rcgd_status = remotecap_get_data_chunk(param2, &rcgd_addr, &rcgd_size, &rcgd_pos); ptp.num_param = 3; ptp.param3 = rcgd_pos; //client needs to seek to this file position before writing the chunk (-1 = ignore) if ( (rcgd_addr==0) || (rcgd_size==0) ) { // send dummy data, otherwise error hoses connection send_ptp_data(data,"\0",1); ptp.param1 = 0; //size ptp.param2 = 0; //0 = no more chunks } else { // send directly using send_data to avoid multiple send calls data->send_data(data->handle,rcgd_addr,rcgd_size,rcgd_size,0,0,0); ptp.param1 = rcgd_size; //size if(rcgd_status == REMOTECAP_CHUNK_STATUS_MORE) { ptp.param2 = 1; } else { ptp.param2 = 0; } } // data send complete, free hooks etc as needed, set error status if required if(!remotecap_send_complete(rcgd_status,param2)) { ptp.code = PTP_RC_GeneralError; } } break; default: ptp.code = PTP_RC_ParameterNotSupported; break; } // send response data->send_resp( data->handle, &ptp, 0 ); return 1; }