/** \file * PTP handlers to extend Magic Lantern to the USB port. * * These handlers are registered to allow Magic Lantern to interact with * a PTP client on the USB port. */ #include "dryos.h" #include "ptp.h" #include "ptp-ml.h" #include "tasks.h" #include "menu.h" #include "bmp.h" #include "property.h" #include "lens.h" #include "version.h" static void fill_uint32(char* data, uint32_t value) { uint8_t* d = (uint8_t*)data; *d = value & 0xFF; value >>= 8; d++; *d = value & 0xFF; value >>= 8; d++; *d = value & 0xFF; value >>= 8; d++; *d = value & 0xFF; } uint32_t menu_data_size(struct menu_entry * m2) { uint32_t size = 0; int i; size += 28 + strlen(m2->name); // id,min,max,flags*2,priv,name_size if (m2->choices) { for (i=0;i<=m2->max;i++) { // string for the choice size += 4 + strlen(m2->choices[i]); } } return size; } char* menu_data_fill(char* dptr, struct menu_entry * m2) { // id,min,max,flags*2,priv,name_size uint32_t strl = strlen(m2->name); uint32_t flags = 0; int i; if (m2->select) flags |= PTP_ML_SUBMENU_HAS_SELECT; //if (m2->select_reverse) flags |= PTP_ML_SUBMENU_HAS_SELECT_REVERSE; if (m2->select_Q) flags |= PTP_ML_SUBMENU_HAS_SELECT_Q; // if (!HAS_HIDDEN_FLAG(m2)) flags |= PTP_ML_SUBMENU_IS_ESSENTIAL; // probably needs fixing somewhere else too if (m2->choices) flags |= PTP_ML_SUBMENU_HAS_CHOICE; if (m2->children) flags |= PTP_ML_SUBMENU_HAS_SUBMENU; if (m2->selected) flags |= PTP_ML_SUBMENU_IS_SELECTED; //if (m2->display) flags |= PTP_ML_SUBMENU_HAS_DISPLAY; flags |= (m2->edit_mode&0xF) << PTP_ML_SUBMENU_EDIT_MODE_SHIFT; flags |= (m2->icon_type&0xF) << PTP_ML_SUBMENU_ICON_TYPE_SHIFT; flags |= (m2->unit&0xF) << PTP_ML_SUBMENU_UNIT_SHIFT; /*fill_uint32(dptr, m2->id);*/ dptr+=4; fill_uint32(dptr, m2->min); dptr+=4; fill_uint32(dptr, m2->max); dptr+=4; fill_uint32(dptr, flags); dptr+=8; //4 bytes reserved for later use if (m2->priv) fill_uint32(dptr, MEM(m2->priv)); else fill_uint32(dptr, -1); dptr+=4; fill_uint32(dptr, strl); dptr+=4; memcpy(dptr, m2->name, strl); dptr+=strl; if (m2->choices) { for (i=0;i<=m2->max;i++) { strl = strlen(m2->choices[i]); fill_uint32(dptr, strl); dptr+=4; memcpy(dptr, m2->choices[i], strl); dptr+=strl; } } return dptr; } PTP_HANDLER( PTP_ML_CODE, 0 ) { struct ptp_msg msg = { .id = PTP_RC_OK, .session = session, .transaction = transaction, .param_count = 4, .param = { 1, 2, 0xdeadbeef, 3 }, }; // handle command switch ( param1 ) { case PTP_ML_USB_Version: msg.param_count = 2; msg.param[0] = PTP_ML_VERSION_MAJOR; msg.param[1] = PTP_ML_VERSION_MINOR; break; case PTP_ML_Version: msg.param_count = 0; switch (param2) { case 0: if ( !send_ptp_data(context, (char *) build_version, strlen(build_version) ) ) msg.id = PTP_RC_GeneralError; break; case 1: if ( !send_ptp_data(context, (char *) build_id, strlen(build_id) ) ) msg.id = PTP_RC_GeneralError; break; case 2: if ( !send_ptp_data(context, (char *) build_date, strlen(build_date) ) ) msg.id = PTP_RC_GeneralError; break; case 3: if ( !send_ptp_data(context, (char *) build_user, strlen(build_user) ) ) msg.id = PTP_RC_GeneralError; break; default: msg.id = PTP_RC_ParameterNotSupported; } break; case PTP_ML_GetMemory: msg.param_count = 0; if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size? { msg.id = PTP_RC_GeneralError; break; } if ( !send_ptp_data(context, (char *) param2, param3) ) { msg.id = PTP_RC_GeneralError; } break; case PTP_ML_SetMemory: msg.param_count = 0; if ( param2 == 0 || param3 < 1 ) // null pointer or invalid size? { msg.id = PTP_RC_GeneralError; break; } context->get_data_size(context->handle); if ( !recv_ptp_data(context,(char *) param2,param3) ) { msg.id = PTP_RC_GeneralError; } break; case PTP_ML_GetMenus: { struct menu * m = menu_get_root(); char *data, *dptr; int size = 0; msg.param_count = 0; for ( ; m ; m = m->next ) { // ID + string length + string size += 8 + strlen(m->name); } m = menu_get_root(); data = SmallAlloc(size); if (!data) { msg.id = PTP_RC_GeneralError; break; } memset(data, '-', size); dptr = data; for ( ; m ; m = m->next ) { uint32_t strl = strlen(m->name); //fill_uint32(dptr, m->id); dptr+=4; fill_uint32(dptr, strl); dptr+=4; memcpy(dptr, m->name, strl); dptr+=strl; } if ( !send_ptp_data(context, data, size ) ) { msg.id = PTP_RC_GeneralError; } SmallFree(data); } break; case PTP_ML_GetMenuStructs: { struct menu_entry *m, *m2; char *data, *dptr; int size = 0; msg.param_count = 0; /* ToDo: replacement m = menu_find_by_id(param2); */ if (!m) { msg.id = PTP_RC_GeneralError; break; } m2 = m; for ( ; m2 ; m2 = m2->next ) { size += menu_data_size(m2); } m2 = m; data = SmallAlloc(size); if (!data) { msg.id = PTP_RC_GeneralError; break; } memset(data, '-', size); dptr = data; for ( ; m2 ; m2 = m2->next ) { dptr = menu_data_fill(dptr, m2); } if ( !send_ptp_data(context, data, size ) ) { msg.id = PTP_RC_GeneralError; } SmallFree(data); } break; case PTP_ML_GetMenuStruct: { struct menu_entry *m; char *data; int size = 0; msg.param_count = 0; /* ToDo: replacement m = menu_find_by_id(param2); */ if (!m) { msg.id = PTP_RC_GeneralError; break; } size = menu_data_size(m); data = SmallAlloc(size); if (!data) { msg.id = PTP_RC_GeneralError; break; } memset(data, '-', size); menu_data_fill(data, m); if ( !send_ptp_data(context, data, size ) ) { msg.id = PTP_RC_GeneralError; } SmallFree(data); } break; case PTP_ML_SetMenu: { struct menu_entry *entry; /* ToDo: replacement entry = menu_find_by_id(param2); */ msg.param_count = 0; if (!entry) { msg.id = PTP_RC_GeneralError; break; } if(param3 == 1) // decrement { //if( entry->select_reverse ) entry->select_reverse( entry->priv, -1 ); //else if (entry->select) entry->select( entry->priv, -1); else menu_numeric_toggle(entry->priv, -1, entry->min, entry->max); } else if (param3 == 2) // Q { if ( entry->select_Q ) entry->select_Q( entry->priv, 1); } else if (param3 == 0) // increment { if( entry->select ) entry->select( entry->priv, 1); else menu_numeric_toggle(entry->priv, 1, entry->min, entry->max); } else if (param3 == 3) { if (entry->priv) MEM(entry->priv) = param4; } msg.param_count = 1; if (entry->priv) msg.param[0] = MEM(entry->priv); else msg.param[0] = -1; } break; default: msg.id = PTP_RC_ParameterNotSupported; break; } context->send_resp( context->handle, &msg ); return 0; }