CHDK PTP interface addendum

This is an addendum to CHDK PTP interface concerning partial downloading and PTP events.

Partial downloading

By intercepting the PTP_OC_GetObject command, I've been able to figure out how the send_data function can be used to download a large block of data with multiple calls (similar to what was already possible with recv_data). I did this by adding a handler for PTP_OC_GetObject that calls the normal handler, but first replaces send_data with a wrapper function that outputs its arguments.

I would now declare send_data as follows:

int (*send_data)(int handle, char *buf,
int size, int total_size,
void (*f)(int), int d, int)

Here size is the size of the current chunk you want to obtain and total_size is the size of the whole block of data for this transaction. However, one must only supply the latter with the first call; following calls should have 0 for total_size.

In addition to the size parameters, the interception also showed that f and d were used for some sort of call-back functionality. Most likely f(d) is called after send_data has finished its main task. In the case of PTP_OC_GetObject (for the IXUS 870 IS 1.01a) f was 0xff8d222c, a wrapper around GiveSemaphore.

PTP events

Another open issue were PTP events. By searching the firmware for possible event codes, I eventually found an interesting function ("ptp_send_event") at 0xff9f138c with event code 0xc008 (PTP_EC_CANON_DeviceInfoChanged). Another way to find it (for my firmware at least) is to check the handler for PTP_OC_SendObjectInfo (0x100c); it is used at the end.

The function has the following signature:

int ptp_send_event(ptp_event *e, int flags);

Here ptp_event is a structure as indicated previously (but again with 4 bytes for the event code). Note that, like the specification, there is no num_param in this structure in the firmware (contrary to the one for requests and responses).

In the value of flags only the two least significant bits are used. Each set bit results in a call to a different function. At this point I've only been able to send events when flags & 1 == 1. However, in this case only one parameter seems to be transmitted, perhaps because that's all the firmware needs.

To be able to send an event, one has to make sure the event code is "registered" and enabled. These event codes are stored in the same structure (pointed to by 0xba20) that is used in add_ptp_handler (and initialised by the function at 0xff9ef52c). This structure can be defined as follows:

struct {
  int semaphore;
  list_item *handlers_list_prev;
  list_item *handlers_list_next;
  ptp_handler default_handler;
  int num_events;
  struct {
    int code;
    int enabled;
  } events[0x32];
};

typedef struct _list_item {
  struct _list_item *prev;
  struct _list_item *next;
  int code;
  ptp_handler handler;
} list_item;

Here semaphore is used to "lock" the structure, handlers_list_prev and handlers_list_next are (circular) linked lists with the PTP-command handlers (backwards and forwards, respectively) and default_handler is (obviously) the default PTP-command handler. For events we have the number (num_events) of "registered" event codes in the array events. Each event code is paired with a boolean enabled that seems to indicate whether or not it can be sent or not.

As long as the array events is not full, one can "manually" add alternative event codes. I haven't seen a firmware function to do this; the array is initialised by simply copying it from the firmware (0xffae1890).