source: trunk/core/ptp.c @ 1155

Revision 1155, 14.4 KB checked in by reyalP, 2 years ago (diff)

ptp protocol update to version 2

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