source: branches/reyalp-flt/core/ptp.c @ 1466

Revision 1451, 15.8 KB checked in by reyalp, 18 months ago (diff)

re-write ptp file upload code to workaround vxworks corruption issue, see http://chdk.setepontos.com/index.php?topic=6730.msg76760#msg76760

Line 
1#include "camera.h"
2#ifdef CAM_CHDK_PTP
3#include "stddef.h"
4#include "platform.h"
5#include "stdlib.h"
6#include "ptp.h"
7#include "kbd.h"
8
9#include "core.h"
10 
11static int buf_size=0;
12
13#ifdef OPT_LUA
14#include "script.h"
15#include "action_stack.h"
16// process id for scripts, increments before each attempt to run script
17// does not handle wraparound
18static unsigned script_run_id;
19#endif
20
21static int handle_ptp(
22                int h, ptp_data *data, int opcode, int sess_id, int trans_id,
23                int param1, int param2, int param3, int param4, int param5);
24
25void init_chdk_ptp()
26{
27  int r;
28 
29  // wait until ptp_handlers_info is initialised and add CHDK PTP interface
30  r = 0x17;
31  while ( r==0x17 )
32  {
33    r = add_ptp_handler(PTP_OC_CHDK,handle_ptp,0);
34    msleep(250);
35  }
36
37  ExitTask();
38}
39
40/*
41WARNING: it appears that on some vxworks cameras,
42if a call to recv_ptp_data doesn't end on a word (4 byte) boundery,
43subsequent calls will return corrupt data
44the final call not ending on a word boundery is OK.
45see http://chdk.setepontos.com/index.php?topic=6730.msg76760#msg76760
46*/
47static int recv_ptp_data(ptp_data *data, char *buf, int size)
48  // repeated calls per transaction are ok
49{
50  while ( size >= buf_size )
51  {
52    data->recv_data(data->handle,buf,buf_size,0,0);
53    // XXX check for success??
54
55    size -= buf_size;
56    buf += buf_size;
57  }
58  if ( size != 0 )
59  {
60    data->recv_data(data->handle,buf,size,0,0);
61    // XXX check for success??
62  }
63
64  return 1;
65}
66
67// camera will shut down if you ignore a recv data phase
68static void flush_recv_ptp_data(ptp_data *data,int size) {
69  char *buf;
70  buf = malloc((size > buf_size) ? buf_size:size);
71  if(!buf) // buf_size should always be less then available memory
72    return;
73  while ( size > 0 )
74  {
75    if ( size >= buf_size )
76    {
77      recv_ptp_data(data,buf,buf_size);
78      size -= buf_size;
79    } else {
80      recv_ptp_data(data,buf,size);
81      size = 0;
82    }
83  }
84  free(buf);
85}
86
87static int send_ptp_data(ptp_data *data, const char *buf, int size)
88  // repeated calls per transaction are *not* ok
89{
90  int tmpsize;
91 
92  tmpsize = size;
93  while ( size >= buf_size )
94  {
95    if ( data->send_data(data->handle,buf,buf_size,tmpsize,0,0,0) )
96    {
97      return 0;
98    }
99
100    tmpsize = 0;
101    size -= buf_size;
102    buf += buf_size;
103  }
104  if ( size != 0 )
105  {
106    if ( data->send_data(data->handle,buf,size,tmpsize,0,0,0) )
107    {
108      return 0;
109    }
110  }
111
112  return 1;
113}
114
115#ifdef OPT_LUA
116// TODO this could be a generic ring buffer of words
117#define PTP_SCRIPT_MSG_Q_LEN 16
118typedef struct {
119  unsigned r; // index of current read value
120  unsigned w; // index of next write value, if w == r, empty TODO "full" currently wastes a slot
121  ptp_script_msg *q[PTP_SCRIPT_MSG_Q_LEN];
122} ptp_script_msg_q;
123
124// TODO it would be better to allocate these only when needed
125ptp_script_msg_q msg_q_in; // messages to pc from script
126ptp_script_msg_q msg_q_out; // messages to script from pc
127
128unsigned script_msg_q_next(unsigned i) {
129  if(i == PTP_SCRIPT_MSG_Q_LEN - 1) {
130    return 0;
131  }
132  return i+1;
133}
134
135unsigned script_msg_q_full(ptp_script_msg_q *q) {
136  return (script_msg_q_next(q->w) == q->r);
137}
138
139unsigned script_msg_q_empty(ptp_script_msg_q *q) {
140  return (q->w == q->r);
141}
142
143int enqueue_script_msg(ptp_script_msg_q *q,ptp_script_msg *msg) {
144  unsigned w = script_msg_q_next(q->w);
145  if(w == q->r) {
146    return 0;
147  }
148  if(msg == NULL) {
149    return 0;
150  }
151  q->q[q->w] = msg;
152  q->w = w;
153  return 1;
154}
155
156ptp_script_msg* dequeue_script_msg(ptp_script_msg_q *q) {
157  ptp_script_msg *msg;
158  if(script_msg_q_empty(q)) {
159    return NULL;
160  }
161  msg = q->q[q->r];
162  q->r = script_msg_q_next(q->r);
163  return msg;
164}
165
166// public interface for script
167// create a message to be queued later
168ptp_script_msg* ptp_script_create_msg(unsigned type, unsigned subtype, unsigned datasize, const void *data) {
169  ptp_script_msg *msg;
170  msg = malloc(sizeof(ptp_script_msg) + datasize);
171  msg->size = datasize;
172  msg->type = type;
173  msg->subtype = subtype;
174  // caller may fill in data themselves
175  // datasize may be empty (e.g. empty string)
176  if(data && datasize) {
177      memcpy(msg->data,data,msg->size);
178  }
179  return msg;
180}
181
182// add a message to the outgoing queue
183int ptp_script_write_msg(ptp_script_msg *msg) {
184  msg->script_id = script_run_id;
185  return enqueue_script_msg(&msg_q_out,msg);
186}
187
188// retrieve the next message in the incoming queue
189ptp_script_msg* ptp_script_read_msg(void) {
190  ptp_script_msg *msg;
191  while(1) {
192    msg = dequeue_script_msg(&msg_q_in);
193    // no messages
194    if(!msg) {
195        return NULL;
196    }
197    // does message belong to our script
198    if(!msg->script_id || msg->script_id == script_run_id) {
199      return msg;
200    } else {
201    // no: discard and keep looking
202      free(msg);
203    }
204  }
205}
206
207// convenience function write an error message
208int ptp_script_write_error_msg(unsigned errtype, const char *err) {
209  if(script_msg_q_full(&msg_q_out)) {
210    return 0;
211  }
212  ptp_script_msg *msg = ptp_script_create_msg(PTP_CHDK_S_MSGTYPE_ERR,errtype,strlen(err),err);
213  if(!msg) {
214    return 0;
215  }
216  return ptp_script_write_msg(msg);
217}
218
219#endif
220
221static int handle_ptp(
222               int h, ptp_data *data, int opcode, int sess_id, int trans_id,
223               int param1, int param2, int param3, int param4, int param5)
224{
225  static union {
226    char *str;
227  } temp_data;
228  static int temp_data_kind = 0; // 0: nothing, 1: ascii string
229  static int temp_data_extra; // size (ascii string)
230  PTPContainer ptp;
231
232  // initialise default response
233  memset(&ptp,0,sizeof(PTPContainer));
234  ptp.code = PTP_RC_OK;
235  ptp.sess_id = sess_id;
236  ptp.trans_id = trans_id;
237  ptp.num_param = 0;
238 
239  // TODO
240  // calling this on every PTP command is not good on cameras without CAM_FIRMWARE_MEMINFO
241  // since it figures out free memory by repeatedly malloc'ing!
242  // using half of available memory may be undesirable in some cases as well
243  buf_size=(core_get_free_memory()>>1);
244  // make sure size is an integer number of words (avoid some possible issues with multiple receive calls)
245  buf_size &= 0xFFFFFFFC;
246
247  // handle command
248  switch ( param1 )
249  {
250
251    case PTP_CHDK_Version:
252      ptp.num_param = 2;
253      ptp.param1 = PTP_CHDK_VERSION_MAJOR;
254      ptp.param2 = PTP_CHDK_VERSION_MINOR;
255      break;
256    case PTP_CHDK_ScriptSupport:
257      ptp.num_param = 1;
258      ptp.param1 = 0;
259#ifdef OPT_LUA
260      ptp.param1 |= PTP_CHDK_SCRIPT_SUPPORT_LUA;
261#endif
262      break;
263    case PTP_CHDK_ScriptStatus:
264      ptp.num_param = 1;
265// TODO script_is_running should always be defined, just ret 0 if script disabled
266      ptp.param1 = 0;
267#ifdef OPT_SCRIPTING
268      ptp.param1 |= script_is_running()?PTP_CHDK_SCRIPT_STATUS_RUN:0;
269#ifdef OPT_LUA
270      ptp.param1 |= (!script_msg_q_empty(&msg_q_out))?PTP_CHDK_SCRIPT_STATUS_MSG:0;
271#endif
272#endif
273      break;
274    case PTP_CHDK_GetMemory:
275      if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size?
276      {
277        ptp.code = PTP_RC_GeneralError;
278        break;
279      }
280
281      if ( !send_ptp_data(data,(char *) param2,param3) )
282      {
283        ptp.code = PTP_RC_GeneralError;
284      }
285      break;
286     
287    case PTP_CHDK_SetMemory:
288      if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size?
289      {
290        ptp.code = PTP_RC_GeneralError;
291        break;
292      }
293
294      data->get_data_size(data->handle); // XXX required call before receiving
295      if ( !recv_ptp_data(data,(char *) param2,param3) )
296      {
297        ptp.code = PTP_RC_GeneralError;
298      }
299      break;
300
301    case PTP_CHDK_CallFunction:
302      if ( (param2 & 0x1) == 0 )
303      {
304        int s;
305        int *buf = (int *) malloc((10+1)*sizeof(int));
306
307        if ( buf == NULL )
308        {
309          ptp.code = PTP_RC_GeneralError;
310          break;
311        }
312
313        s = data->get_data_size(data->handle);
314        if ( !recv_ptp_data(data,(char *) buf,s) )
315        {
316          ptp.code = PTP_RC_GeneralError;
317          break;
318        }
319
320        ptp.num_param = 1;
321        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]);
322
323        free(buf);
324        break;
325      } else { // if ( (param2 & 0x1) != 0 )
326        ptp.num_param = 1;
327        ptp.param1 = ((int (*)(ptp_data*,int,int)) param3)(data,param4,param5);
328        break;
329      }
330
331    case PTP_CHDK_TempData:
332      if ( param2 & PTP_CHDK_TD_DOWNLOAD )
333      {
334        const char *s = NULL;
335        size_t l = 0;
336
337        if ( temp_data_kind == 0 )
338        {
339          ptp.code = PTP_RC_GeneralError;
340          break;
341        }
342
343        if ( temp_data_kind == 1 )
344        {
345          s = temp_data.str;
346          l = temp_data_extra;
347        }
348
349        if ( !send_ptp_data(data,s,l) )
350        {
351          ptp.code = PTP_RC_GeneralError;
352          break;
353        }
354       
355      } else if ( ! (param2 & PTP_CHDK_TD_CLEAR) ) {
356        if ( temp_data_kind == 1 )
357        {
358          free(temp_data.str);
359        }
360        temp_data_kind = 0;
361
362        temp_data_extra = data->get_data_size(data->handle);
363
364        temp_data.str = (char *) malloc(temp_data_extra);
365        if ( temp_data.str == NULL )
366        {
367          ptp.code = PTP_RC_GeneralError;
368          break;
369        }
370
371        if ( !recv_ptp_data(data,temp_data.str,temp_data_extra) )
372        {
373          ptp.code = PTP_RC_GeneralError;
374          break;
375        }
376        temp_data_kind = 1;
377      }
378      if ( param2 & PTP_CHDK_TD_CLEAR )
379      {
380        if ( temp_data_kind == 1 )
381        {
382          free(temp_data.str);
383        }
384        temp_data_kind = 0;
385      }
386      break;
387
388    case PTP_CHDK_UploadFile:
389      {
390        FILE *f=NULL;
391        char *buf=NULL, *fn=NULL;
392        unsigned data_size,fn_len,chunk_size;
393        data_size = data->get_data_size(data->handle);
394        while ( data_size > 0 ) {
395            chunk_size = (data_size > buf_size) ? buf_size:data_size;
396            // first time through
397            // allocate buffer, parse out the file name and open file
398            if(!buf) {
399                buf=malloc(chunk_size);
400                if(!buf) {
401                    ptp.code = PTP_RC_GeneralError;
402                    break;
403                }
404                recv_ptp_data(data,buf,chunk_size);
405                fn_len = *(unsigned *)buf;
406                fn = malloc(fn_len);
407                if(!fn) {
408                    ptp.code = PTP_RC_GeneralError;
409                    break;
410                }
411                memcpy(fn,buf+4,fn_len);
412                fn[fn_len] = 0;
413                f = fopen(fn,"wb");
414                free(fn);
415                if(!f) {
416                    ptp.code = PTP_RC_GeneralError;
417                    break;
418                }
419                fwrite(buf+4+fn_len,1,chunk_size - 4 - fn_len,f);
420            } else {
421                recv_ptp_data(data,buf,chunk_size);
422                fwrite(buf,1,chunk_size,f);
423            }
424            data_size -= chunk_size;
425        }
426        if(f) {
427            fclose(f);
428        }
429        free(buf);
430        break;
431      }
432     
433    case PTP_CHDK_DownloadFile:
434      {
435        FILE *f;
436        int tmp,t,s,r,fn_len;
437        char *buf, *fn;
438
439        if ( temp_data_kind != 1 )
440        {
441          // send dummy data, otherwise error hoses connection
442          send_ptp_data(data,"\0",1);
443          ptp.code = PTP_RC_GeneralError;
444          break;
445        }
446
447        fn = (char *) malloc(temp_data_extra+1);
448        if ( fn == NULL )
449        {
450          // send dummy data, otherwise error hoses connection
451          send_ptp_data(data,"\0",1);
452          free(temp_data.str);
453          temp_data_kind = 0;
454          ptp.code = PTP_RC_GeneralError;
455          break;
456        }
457        memcpy(fn,temp_data.str,temp_data_extra);
458        fn[temp_data_extra] = '\0';
459
460        free(temp_data.str);
461        temp_data_kind = 0;
462
463        f = fopen(fn,"rb");
464        if ( f == NULL )
465        {
466          // send dummy data, otherwise error hoses connection
467          send_ptp_data(data,"\0",1);
468          ptp.code = PTP_RC_GeneralError;
469          free(fn);
470          break;
471        }
472        free(fn);
473
474        fseek(f,0,SEEK_END);
475        s = ftell(f);
476        fseek(f,0,SEEK_SET);
477
478        buf = (char *) malloc(buf_size);
479        if ( buf == NULL )
480        {
481          // send dummy data, otherwise error hoses connection
482          send_ptp_data(data,"\0",1);
483          ptp.code = PTP_RC_GeneralError;
484          break;
485        }
486
487        tmp = s;
488        t = s;
489        while ( (r = fread(buf,1,(t<buf_size)?t:buf_size,f)) > 0 )
490        {
491          t -= r;
492          // cannot use send_ptp_data here
493          data->send_data(data->handle,buf,r,tmp,0,0,0);
494          tmp = 0;
495        }
496        fclose(f);
497        // XXX check that we actually read/send s bytes! (t == 0)
498
499        ptp.num_param = 1;
500        ptp.param1 = s;
501
502        free(buf);
503
504        break;
505      }
506      break;
507
508#ifdef OPT_LUA
509    // TODO this should flush data even if scripting isn't supported
510    case PTP_CHDK_ExecuteScript:
511      {
512        int s;
513        char *buf;
514
515        script_run_id++;
516        ptp.num_param = 2;
517        ptp.param1 = script_run_id;
518
519        if ( param2 != PTP_CHDK_SL_LUA )
520        {
521          ptp.code = PTP_RC_ParameterNotSupported;
522          break;
523        }
524
525        s = data->get_data_size(data->handle);
526       
527        buf = (char *) malloc(s);
528        if ( buf == NULL )
529        {
530          ptp.code = PTP_RC_GeneralError;
531          break;
532        }
533
534        recv_ptp_data(data,buf,s);
535
536        // error details will be passed in a message
537        if (script_start_ptp(buf) < 0) {
538          ptp.param2 = PTP_CHDK_S_ERRTYPE_COMPILE;
539        } else {
540          ptp.param2 = PTP_CHDK_S_ERRTYPE_NONE;
541        }
542
543        free(buf);
544       
545        break;
546      }
547    case PTP_CHDK_ReadScriptMsg:
548    {
549      char *pdata="";
550      unsigned datasize=1;
551
552      ptp_script_msg *msg = dequeue_script_msg(&msg_q_out);
553      ptp.num_param = 4;
554      if(msg) {
555        ptp.param1 = msg->type;
556        ptp.param2 = msg->subtype;
557        ptp.param3 = msg->script_id;
558        ptp.param4 = msg->size;
559        // empty messages must have a data phase, so use default if no data
560        if(msg->size) {
561            datasize = msg->size;
562            pdata = msg->data;
563        }
564          } else {
565        // return a fully formed message for easier handling
566        ptp.param1 = PTP_CHDK_S_MSGTYPE_NONE;
567        ptp.param2 = 0;
568        ptp.param3 = 0;
569        ptp.param4 = 0;
570      }
571
572      // NOTE message is lost if sending failed
573      if ( !send_ptp_data(data,pdata,datasize) )
574      {
575        ptp.code = PTP_RC_GeneralError;
576      }
577      free(msg);
578      break;
579    }
580    case PTP_CHDK_WriteScriptMsg:
581    {
582      int msg_size;
583      ptp_script_msg *msg;
584      ptp.num_param = 1;
585      ptp.param1 = PTP_CHDK_S_MSGSTATUS_OK;
586      if (!script_is_running()) {
587        ptp.param1 = PTP_CHDK_S_MSGSTATUS_NOTRUN;
588      } else if(param2 && param2 != script_run_id) {// check if target script for message is running
589        ptp.param1 = PTP_CHDK_S_MSGSTATUS_BADID;
590      } else if(script_msg_q_full(&msg_q_in)) {
591        ptp.param1 = PTP_CHDK_S_MSGSTATUS_QFULL;
592      }
593
594      msg_size = data->get_data_size(data->handle);
595
596      // if something was wrong, don't bother creating message, just flush
597      if(ptp.param1 != PTP_CHDK_S_MSGSTATUS_OK) {
598        flush_recv_ptp_data(data,msg_size);
599        break;
600      }
601      msg = ptp_script_create_msg(PTP_CHDK_S_MSGTYPE_USER,PTP_CHDK_TYPE_STRING,msg_size,NULL);
602      if ( !msg ) // malloc error or zero size
603      {
604        // if size is zero, things will get hosed no matter what
605        flush_recv_ptp_data(data,msg_size);
606        ptp.code = PTP_RC_GeneralError;
607        break;
608      }
609      msg->script_id = param2;
610      if ( !recv_ptp_data(data,msg->data,msg->size) )
611      {
612        ptp.code = PTP_RC_GeneralError;
613        free(msg);
614        break;
615      }
616      if( !enqueue_script_msg(&msg_q_in,msg) ) {
617        ptp.code = PTP_RC_GeneralError;
618        free(msg);
619      }
620      break;
621    }
622#endif
623
624    default:
625      ptp.code = PTP_RC_ParameterNotSupported;
626      break;
627  }
628
629  // send response
630  data->send_resp( data->handle, &ptp );
631 
632  return 1;
633}
634
635#endif // CAM_CHDK_PTP
Note: See TracBrowser for help on using the repository browser.