--- include/script.h	(revision 811)
+++ include/script.h	(working copy)
@@ -24,4 +24,6 @@
 extern int load_params_values(const char *fn, int update_vars, int read_param_set);
 extern void save_params_values(int unconditional);
 //-------------------------------------------------------------------
+
+extern void lua_script_exec(char *script);
 #endif
--- include/platform.h	(revision 811)
+++ include/platform.h	(working copy)
@@ -498,4 +498,41 @@
 #define started() debug_led(1)
 #define finished() debug_led(0)
 
+
+
+typedef struct {
+    int code;
+    int sess_id;
+    int trans_id;
+    int num_param;
+    int param1;
+    int param2;
+    int param3;
+    int param4;
+    int param5;
+} PTPContainer;
+
+typedef struct {
+    int handle;
+    int (*send_data)(int handle, char *buf, int size, int size_again, int, int, int); // (0xFF9F525C)
+    int (*recv_data)(int handle, char *buf, int size, int, int); // (0xFF9F5500)
+    int (*send_resp)(int handle, PTPContainer *resp); // (0xFF9F5688)
+    int (*get_data_size)(int handle); // (0xFF9F5830)
+    int (*send_err_resp)(int handle, PTPContainer *resp); // (0xFF9F5784)
+    int unknown1; // ???
+    int (*f2)(); // ??? (0xFF8D5B24)
+    int (*f3)(); // ??? (0xFF8D5B5C)
+    // more??
+} ptp_data;
+
+typedef int (*ptp_handler)(int, ptp_data*, int, int, int, int, int, int, int, int);
+
+int add_ptp_handler(int opcode, ptp_handler handler, int unknown);
+
+void shutdown_soft();
+void reboot(char *fw_update); // fw_update == NULL implies normal reboot
+void switch_mode(int mode); // 0 = playback, 1 = record; only call while USB connected
+
+void ExitTask();
+
 #endif
--- include/core.h	(revision 811)
+++ include/core.h	(working copy)
@@ -19,5 +19,7 @@
 
 long core_get_noise_reduction_value();
 
+void init_chdk_ptp();
+
 #endif
 
--- include/lolevel.h	(revision 811)
+++ include/lolevel.h	(working copy)
@@ -232,4 +232,11 @@
 extern void _ScreenLock();
 // known in CHDK as _RefreshPhysicalScreen
 //extern void _ScreenUnLock();
+
+extern int _add_ptp_handler(int, void*, int);
+extern void _reboot_fw_update(char *);
+extern void _set_control_event(int);
+extern void _PB2Rec();
+extern void _Rec2PB();
+
 #endif
--- platform/ixus870_sd880/lib.c	(revision 811)
+++ platform/ixus870_sd880/lib.c	(working copy)
@@ -94,3 +94,36 @@
 {
   _RefreshPhysicalScreen(1);
 }
+
+void shutdown_soft()
+{
+   _PostLogicalEventForNotPowerType(0x1005,0);
+}
+
+void reboot(char *fw_update)
+{
+  if ( fw_update == 0 )
+  {
+    // sequence taken from reboot_fw_update
+    ((void (*)()) 0xFF94F7B8)();
+    ((void (*)()) 0xFF842480)();
+    ((void (*)()) 0xFF841594)();
+    ((void (*)()) 0xFF8704F4)();
+    ((void (*)(int)) 0xFF8293DC)(0);
+  } else {
+    _reboot_fw_update(fw_update);
+  }
+}
+
+void switch_mode(int mode)
+{
+  if ( mode == 0 )
+  {
+    _Rec2PB();
+    _set_control_event(0x80000902); // 0x10A5 ConnectUSBCable
+  } else if ( mode == 1 )
+  {
+    _set_control_event(0x902); // 0x10A6 DisconnectUSBCable
+    _PB2Rec();
+  }
+}
--- platform/ixus870_sd880/sub/101a/stubs_entry_2.S	(revision 811)
+++ platform/ixus870_sd880/sub/101a/stubs_entry_2.S	(working copy)
@@ -36,6 +36,11 @@
 NHSTUB(ScreenLock, 0xFF9B83D0)  // via aScreenlock
 NHSTUB(MakeAFScan,  0xFF933E54)  // via aSsprepareseqba (function with most uses)
 NHSTUB(ExpCtrlTool_StartContiAE, 0xFF830D00)  // via aStartcontiae
+NHSTUB(add_ptp_handler, 0xFF9EF744) // via ptp handler table
+NHSTUB(set_control_event, 0xFF878894) // via eventproc_export_IsControlEventActive (last call)
+NHSTUB(PB2Rec, 0xFF87BF0C) // via aAcPb2rec
+NHSTUB(Rec2PB, 0xFF87A8A4) // via aAcRec2pb
+NHSTUB(reboot_fw_update, 0xFFA296A0) // via aFirmupgrade_c
 
 
 // Corrections for uncertain entries in stubs_entry.S
--- platform/ixus870_sd880/sub/101a/boot.c	(revision 811)
+++ platform/ixus870_sd880/sub/101a/boot.c	(working copy)
@@ -34,6 +34,9 @@
         _CreateTask("SpyTask", 0x19, 0x2000, core_spytask, 0);
 };
 
+void CreateTask_init_chdk_ptp() {
+        _CreateTask("InitCHDKPTP", 0x19, 0x2000, init_chdk_ptp, 0);
+};
 
 void __attribute__((naked,noinline)) boot() {
 
@@ -294,6 +297,7 @@
                  "BL      sub_FF826C64\n"
                  "BL      sub_FF829898\n"
                  "BL      CreateTask_spytask\n"    // +
+                 "BL      CreateTask_init_chdk_ptp\n"    // +
                  "BL      sub_FF821848\n"  // calls mykbd_task and JogDial_task
                                            //   - SleepTask
                                            //   - JogDialTask
--- platform/generic/wrappers.c	(revision 811)
+++ platform/generic/wrappers.c	(working copy)
@@ -947,3 +947,13 @@
 void __attribute__((weak)) vid_turn_on_updates()
 {
 }
+
+int add_ptp_handler(int opcode, ptp_handler handler, int unknown)
+{
+  return _add_ptp_handler(opcode,handler,unknown);
+}
+
+void ExitTask()
+{
+  _ExitTask();
+}
--- core/Makefile	(revision 811)
+++ core/Makefile	(working copy)
@@ -56,7 +56,7 @@
      gui_fselect.o gui.o kbd.o conf.o \
      histogram.o gui_batt.o gui_space.o gui_osd.o script.o raw.o \
      gui_lang.o gui_mpopup.o gui_grid.o motion_detector.o raw_merge.o \
-     luascript.o shot_histogram.o dng.o $(OPT_OBJS)
+     luascript.o shot_histogram.o dng.o ptp.o $(OPT_OBJS)
 
 gui.o: FORCE
 
--- core/ptp.c	(revision 0)
+++ core/ptp.c	(revision 0)
@@ -0,0 +1,395 @@
+#include "platform.h"
+#include "stdlib.h"
+#include "ptp.h"
+#include "script.h"
+
+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);
+
+void init_chdk_ptp()
+{
+  int r;
+ 
+  // wait until ptp_handlers_info is initialised and add CHDK PTP interface
+  r = 0x17;
+  while ( r==0x17 )
+  {
+    r = add_ptp_handler(PTP_OC_CHDK,handle_ptp,0);
+    msleep(250);
+  }
+
+  ExitTask();
+}
+
+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 char *temp_data = NULL;
+  static int temp_data_size = 0;
+  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;
+
+  // handle command
+  switch ( param1 )
+  {
+
+    case PTP_CHDK_Shutdown:
+      ptp.code = PTP_RC_GeneralError; // default in case shutdown/reboot fails
+      switch ( param2 )
+      {
+        case 0: // hard shutdown
+          shutdown();
+          break;
+        case 1: // soft shutdown
+          shutdown_soft();
+          ptp.code = PTP_RC_OK; // shutdown is not immediate
+          break;
+        case 2: // reboot
+          reboot(NULL);
+          break;
+        case 3: // reboot using firmware update
+          {
+            FILE *f;
+            int s = data->get_data_size(data->handle);
+            char *fn = (char *) malloc(s+1);
+
+            sprintf(fn,"%i",s);
+            script_console_add_line(fn);
+            if ( fn == NULL )
+            {
+              ptp.code = PTP_RC_GeneralError;
+              break;
+            }
+            fn[s] = '\0';
+
+            data->recv_data(data->handle,fn,s,0,0);
+            
+            if ( s == 0 )
+            {
+              free(fn);
+              fn = "A/PS.FI2";
+            }
+
+            if ( (f = fopen(fn,"rb")) == NULL )
+            {
+              ptp.code = PTP_RC_GeneralError;
+            } else {
+              fclose(f);
+              reboot(fn);
+            }
+           
+            if ( s != 0 )
+            {
+              free(fn);
+            }
+            break;
+          }
+        default:
+          ptp.code = PTP_RC_ParameterNotSupported;
+          break;
+      }
+      break;
+
+    case PTP_CHDK_GetMemory:
+      {
+        if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size?
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+
+        if ( data->send_data(data->handle,(char *) param2,param3,param3,0,0,0) )
+        {
+          ptp.code = PTP_RC_GeneralError;
+        }
+        break;
+      }
+      
+    case PTP_CHDK_SetMemoryLong:
+      *((volatile long *) param2) = param3;
+      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);
+        data->recv_data(data->handle,(char *) buf,s,0,0);
+
+        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_GetPropCase:
+      {
+        int *buf, i;
+
+        if ( param3 < 1 ) // invalid number of properties
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+
+        buf = (int *) malloc(param3*sizeof(int));
+        if ( buf == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+
+        for (i=0; i<param3; i++)
+        {
+          get_property_case(param2+i,(char *) &buf[i],4);
+        }
+
+        if ( data->send_data(data->handle,(char *) buf,param3*sizeof(int),param3*sizeof(int),0,0,0) )
+        {
+          ptp.code = PTP_RC_GeneralError;
+        }
+        free(buf);
+        break;
+      }
+
+    case PTP_CHDK_GetParamData:
+      {
+        extern long* FlashParamsTable[];
+        char *buf, *p;
+        int i, s;
+
+        if ( param3 < 1 ) // invalid number of parameters
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+
+        // calculate required size for all properties (and lengths)
+        s = 0;
+        for (i=0; i<param3; i++)
+        {
+          s += 4 + (FlashParamsTable[param2+i][1]>>16); // room for size and actual data
+        }
+
+        buf = (char *) malloc(s);
+        if ( buf == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+
+        // construct result
+        p = buf;
+        for (i=0; i<param3; i++)
+        {
+          int t = FlashParamsTable[param2+i][1]>>16;
+          // write size
+          memcpy(p,&t,4);
+          p += 4;
+          // write value
+          get_parameter_data(param2+i,p,t);
+          p += t;
+        }
+
+        if ( data->send_data(data->handle,buf,s,s,0,0,0) )
+        {
+          ptp.code = PTP_RC_GeneralError;
+        }
+        free(buf);
+        break;
+      }
+      
+    case PTP_CHDK_TempData:
+      if ( temp_data != NULL )
+      {
+        free(temp_data);
+        temp_data = NULL;
+      }
+
+      temp_data_size = data->get_data_size(data->handle);
+
+      temp_data = (char *) malloc(temp_data_size);
+      if ( temp_data == NULL )
+      {
+        ptp.code = PTP_RC_GeneralError;
+        break;
+      }
+
+      data->recv_data(data->handle,temp_data,temp_data_size,0,0);
+
+      break;
+
+    case PTP_CHDK_UploadFile:
+      {
+        FILE *f;
+        int s,r,fn_len;
+        char *buf, *fn;
+
+        s = data->get_data_size(data->handle);
+
+        data->recv_data(data->handle,(char *) &fn_len,4,0,0);
+        s -= 4;
+
+        fn = (char *) malloc(fn_len+1);
+        if ( fn == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+        fn[fn_len] = '\0';
+
+        data->recv_data(data->handle,fn,fn_len,0,0);
+        s -= fn_len;
+
+        f = fopen(fn,"wb");
+        if ( f == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          free(fn);
+          break;
+        }
+        free(fn);
+
+#define BUF_SIZE 4096
+        buf = (char *) malloc(BUF_SIZE);
+        if ( buf == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+        while ( s > 0 )
+        {
+          if ( s >= BUF_SIZE )
+          {
+            r = data->recv_data(data->handle,buf,BUF_SIZE,0,0);
+            fwrite(buf,1,BUF_SIZE,f);
+            s -= BUF_SIZE;
+          } else {
+            data->recv_data(data->handle,buf,s,0,0);
+            fwrite(buf,1,s,f);
+            s = 0;
+          }
+        }
+
+        fclose(f);
+
+        free(buf);
+        break;
+      }
+      
+    case PTP_CHDK_DownloadFile:
+      {
+        FILE *f;
+        int s,r,fn_len;
+        char *buf, *fn;
+
+        if ( temp_data == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+
+        fn = (char *) malloc(temp_data_size+1);
+        if ( fn == NULL )
+        {
+          free(temp_data);
+          temp_data = NULL;
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+        memcpy(fn,temp_data,temp_data_size);
+        fn[temp_data_size] = '\0';
+
+        free(temp_data);
+        temp_data = NULL;
+
+        f = fopen(fn,"rb");
+        if ( f == NULL )
+        {
+          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(s);
+        if ( buf == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          fclose(f);
+          break;
+        }
+
+        fread(buf,1,s,f);
+        fclose(f);
+
+        data->send_data(data->handle,buf,s,s,0,0,0);
+        ptp.num_param = 1;
+        ptp.param1 = s;
+
+        free(buf);
+
+        break;
+      }
+      break;
+
+    case PTP_CHDK_SwitchMode:
+      if ( param2 != 0 && param2 != 1 )
+      {
+        ptp.code = PTP_RC_ParameterNotSupported;
+      } else {
+        switch_mode(param2);
+      }
+      break;
+
+    case PTP_CHDK_ExecuteLUA:
+      {
+        int s;
+        char *buf;
+
+        s = data->get_data_size(data->handle);
+        
+        buf = (char *) malloc(s);
+        if ( buf == NULL )
+        {
+          ptp.code = PTP_RC_GeneralError;
+          break;
+        }
+
+        data->recv_data(data->handle,buf,s,0,0);
+
+        lua_script_exec(buf);
+
+        free(buf);
+
+        break;
+      }
+
+    default:
+      ptp.code = PTP_RC_ParameterNotSupported;
+      break;
+  }
+
+  // send response
+  data->send_resp( data->handle, &ptp );
+  
+  return 1;
+}
--- core/ptp.h	(revision 0)
+++ core/ptp.h	(revision 0)
@@ -0,0 +1,34 @@
+#ifndef __PTP_H
+#define __PTP_H
+
+#define PTP_OC_CHDK 0x9999
+
+#define PTP_RC_OK 0x2001
+#define PTP_RC_GeneralError 0x2002
+#define PTP_RC_ParameterNotSupported 0x2006
+
+enum {
+  PTP_CHDK_Shutdown = 0,    // param2 is 0 (hard), 1 (soft), 2 (reboot) or 3 (reboot fw update)
+                            // if param2 == 3, then filename of fw update is send as data (empty for default)
+  PTP_CHDK_GetMemory,       // param2 is base address (not NULL; circumvent by taking 0xFFFFFFFF and size+1)
+                            // param3 is size (in bytes)
+                            // return data is memory block
+  PTP_CHDK_SetMemoryLong,   // param2 is address
+                            // param3 is value
+  PTP_CHDK_CallFunction,    // data is array of function pointer and (long) arguments  (max: 10 args)
+                            // return param1 is return value
+  PTP_CHDK_GetPropCase,     // param2 is base id
+                            // param3 is number of properties
+                            // return data is array of longs
+  PTP_CHDK_GetParamData,    // param2 is base id
+                            // param3 is number of parameters
+                            // return data is sequence of strings prefixed by their length (as long)
+  PTP_CHDK_TempData,        // data is data to be stored for later
+  PTP_CHDK_UploadFile,      // data is 4-byte length of filename, followed by filename and contents
+  PTP_CHDK_DownloadFile,    // preceded by PTP_CHDK_TempData with filename
+                            // return data are file contents
+  PTP_CHDK_SwitchMode,      // param2 is 0 (playback) or 1 (record)
+  PTP_CHDK_ExecuteLUA,      // data is script to be executed
+} ptp_chdk_command;
+
+#endif // __PTP_H
--- core/kbd.c	(revision 811)
+++ core/kbd.c	(working copy)
@@ -246,6 +246,15 @@
   return 1;
 }
 
+void lua_script_exec( char *script )
+{
+  lua_script_start(script);
+  state_lua_kbd_first_call_to_resume = 1;
+  state_kbd_script_run = 1;
+  kbd_blocked = 1;
+}
+
+
 static void wait_and_end(void)
 {
 	script_console_add_line("PRESS SHUTTER TO CLOSE");
