Skip to content

Commit

Permalink
Finish ptp_parse_prop_desc impl + more type parsing code
Browse files Browse the repository at this point in the history
  • Loading branch information
petabyt committed Mar 23, 2024
1 parent ba30ced commit 476c5bf
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/camlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

// Logging+panic mechanism, define it yourself or link in log.c
void ptp_verbose_log(char *fmt, ...);
void ptp_panic(char *fmt, ...);
__attribute__ ((noreturn)) void ptp_panic(char *fmt, ...);

// 4mb recommended default buffer size
#define CAMLIB_DEFAULT_SIZE 1000000
Expand Down
25 changes: 20 additions & 5 deletions src/cl_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,30 @@ struct PtpObjectInfo {
char keywords[64];
};

struct PtpDevPropDesc {
struct PtpEnumerationForm {
uint16_t length;
char data[];
};

struct PtpRangeForm {
int min;
int max;
int step;
};

struct PtpPropDesc {
uint16_t code;
uint16_t data_type;
uint8_t read_only; // (get/set)

#define PTP_PROP_DESC_VAR_START 5
uint32_t default_value32;
uint32_t current_value32;
void *current_value;
void *default_value;

int default_value;
int current_value;
uint8_t form_type;
struct PtpRangeForm range_form;
struct PtpEnumerationForm *enum_form;
};

struct PtpObjPropDesc {
Expand Down Expand Up @@ -202,7 +217,7 @@ int ptp_pack_object_info(struct PtpRuntime *r, struct PtpObjectInfo *oi, uint8_t
int ptp_parse_prop_value(struct PtpRuntime *r);
int ptp_parse_device_info(struct PtpRuntime *r, struct PtpDeviceInfo *di);
int ptp_device_info_json(struct PtpDeviceInfo *di, char *buffer, int max);
int ptp_parse_prop_desc(struct PtpRuntime *r, struct PtpDevPropDesc *oi);
int ptp_parse_prop_desc(struct PtpRuntime *r, struct PtpPropDesc *oi);
int ptp_parse_object_info(struct PtpRuntime *r, struct PtpObjectInfo *oi);
int ptp_storage_info_json(struct PtpStorageInfo *so, char *buffer, int max);
int ptp_object_info_json(struct PtpObjectInfo *so, char *buffer, int max);
Expand Down
2 changes: 1 addition & 1 deletion src/cl_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ int ptp_send_object_info(struct PtpRuntime *r, int storage_id, int handle, struc
int ptp_get_prop_value(struct PtpRuntime *r, int code);
int ptp_set_prop_value(struct PtpRuntime *r, int code, int value);
int ptp_set_prop_value_data(struct PtpRuntime *r, int code, void *data, int length);
int ptp_get_prop_desc(struct PtpRuntime *r, int code, struct PtpDevPropDesc *pd);
int ptp_get_prop_desc(struct PtpRuntime *r, int code, struct PtpPropDesc *pd);
/// @brief Gets a list of object handles in a storage device or folder.
// @param id storage ID
// @param format Can specify file format ID, or zero for all IDs
Expand Down
88 changes: 80 additions & 8 deletions src/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// Custom snprint with offset - for safer string building
static int osnprintf(char *str, int cur, int size, const char *format, ...) {
if (size - cur < 0) {
ptp_panic("osnprintf overflow %d/%d", cur, size);
return 0;
}

Expand All @@ -25,6 +26,8 @@ static int osnprintf(char *str, int cur, int size, const char *format, ...) {
}

int ptp_get_data_size(void *d, int type) {
uint32_t length32;
uint8_t length8;
switch (type) {
case PTP_TC_INT8:
case PTP_TC_UINT8:
Expand All @@ -42,11 +45,14 @@ int ptp_get_data_size(void *d, int type) {
case PTP_TC_UINT16ARRAY:
case PTP_TC_UINT32ARRAY:
case PTP_TC_UINT64ARRAY:
return ((uint32_t *)d)[0];
ptp_read_u32(d, &length32);
return length32;
case PTP_TC_STRING:
return ((uint8_t *)d)[0];
ptp_read_u8(d, &length8);
return length8;
}

ptp_panic("Invalid size read");
return 0;
}

Expand Down Expand Up @@ -84,14 +90,80 @@ int ptp_parse_prop_value(struct PtpRuntime *r) {
return -1;
}

int ptp_parse_prop_desc(struct PtpRuntime *r, struct PtpDevPropDesc *oi) {
static int parse_data_data_or_u32(uint8_t *d, int type, uint32_t *u32, void **data) {
int size;
uint32_t length32;
uint8_t length8;
int len_total;
switch (type) {
case PTP_TC_INT8:
case PTP_TC_UINT8:
case PTP_TC_INT16:
case PTP_TC_UINT16:
case PTP_TC_INT32:
case PTP_TC_UINT32:
return ptp_parse_data(d, type, u32);
case PTP_TC_INT64:
case PTP_TC_UINT64:
(*data) = malloc(8);
memcpy((*data), d, 8);
return 8;
case PTP_TC_UINT8ARRAY:
size = 1;
case PTP_TC_UINT16ARRAY:
size = 2;
case PTP_TC_UINT32ARRAY:
size = 4;
case PTP_TC_UINT64ARRAY:
size = 8;
ptp_read_u32(d, &length32);
(*data) = malloc(4 + length32 * size);
memcpy((*data), d, 4 + length32 * size);
return 4 + length32 * size;
case PTP_TC_STRING:
ptp_read_u8(d, &length8);
len_total = 1 + (length8 * 2);
(*data) = malloc(len_total);
memcpy((*data), d, len_total);
return len_total;
default:
ptp_panic("Unknown data type %d\n", type);
}
}

int ptp_parse_prop_desc(struct PtpRuntime *r, struct PtpPropDesc *oi) {
uint8_t *d = ptp_get_payload(r);
memcpy(oi, d, PTP_PROP_DESC_VAR_START);
d += PTP_PROP_DESC_VAR_START;
d += ptp_parse_data(d, oi->data_type, &oi->default_value);
d += ptp_parse_data(d, oi->data_type, &oi->current_value);

// TODO: Form flag + form (for properties like date/time)
d += ptp_read_u16(d, &oi->code);
d += ptp_read_u16(d, &oi->data_type);
d += ptp_read_u8(d, &oi->read_only);

// TODO: Arrays will be ignored
d += parse_data_data_or_u32(d, oi->data_type, &oi->default_value32, &oi->default_value);
d += parse_data_data_or_u32(d, oi->data_type, &oi->current_value32, &oi->current_value);

d += ptp_read_u8(d, &oi->form_type);

if (oi->form_type == PTP_RangeForm) {
d += ptp_parse_data(d, oi->data_type, &oi->range_form.min);
d += ptp_parse_data(d, oi->data_type, &oi->range_form.max);
d += ptp_parse_data(d, oi->data_type, &oi->range_form.step);
} else if (oi->form_type == PTP_EnumerationForm) {
uint16_t num_values = 0;
d += ptp_read_u16(d, &num_values);
int length = 0;
for (uint32_t i = 0; i < num_values; i++) {
length += ptp_get_data_size(d + length, oi->data_type);
}
struct PtpEnumerationForm *form = (struct PtpEnumerationForm *)malloc(sizeof(struct PtpEnumerationForm) + length);
form->length = num_values;
memcpy(form->data, d, length);
oi->enum_form = form;
} else {
ptp_panic("Unknown form type %d\n", oi->form_type);
return -1;
}

return 0;
}

Expand Down
5 changes: 4 additions & 1 deletion src/libusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ int ptp_comm_init(struct PtpRuntime *r) {

ptp_verbose_log("Initializing libusb...\n");
libusb_init(&(backend->ctx));

//libusb_set_option(backend->ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
}

return 0;
Expand Down Expand Up @@ -297,7 +299,7 @@ int ptp_cmd_write(struct PtpRuntime *r, void *to, int length) {
const struct LibUSBBackend *backend = (struct LibUSBBackend *)r->comm_backend;

if (backend == NULL || r->io_kill_switch) {
return -1;
return -11;
}

int transferred;
Expand All @@ -306,6 +308,7 @@ int ptp_cmd_write(struct PtpRuntime *r, void *to, int length) {
backend->endpoint_out,
(unsigned char *)to, length, &transferred, PTP_TIMEOUT);
if (rc) {
perror("libusb_bulk_transfer");
return -1;
}

Expand Down
12 changes: 6 additions & 6 deletions src/log.c
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
// Optional implementation of a logging mechanism - you can replace this file
// with your own mechanism.
// Optional implementation of a logger - you can and should replace this file.

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <camlib.h>

void ptp_verbose_log(char *fmt, ...) {
#ifdef VERBOSE
#ifdef VERBOSE
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
#endif
#endif
}

__attribute__ ((noreturn))
void ptp_panic(char *fmt, ...) {
printf("PTP abort: ");
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);

printf("PTP triggered PANIC\n");
exit(1);
abort();
}
2 changes: 1 addition & 1 deletion src/operations.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ int ptp_get_prop_value(struct PtpRuntime *r, int code) {
return ptp_send(r, &cmd);
}

int ptp_get_prop_desc(struct PtpRuntime *r, int code, struct PtpDevPropDesc *pd) {
int ptp_get_prop_desc(struct PtpRuntime *r, int code, struct PtpPropDesc *pd) {
struct PtpCommand cmd;
cmd.code = PTP_OC_GetDevicePropDesc;
cmd.param_length = 1;
Expand Down
9 changes: 1 addition & 8 deletions src/pack.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,2 @@
#include <camlib.h>

int ptp_pack_fuji_object_info(struct PtpRuntime *r, struct PtpObjectInfo *oi, void *buffer, int max) {
void **ptr = (void **)(&buffer);

memcpy(*ptr, oi, PTP_FUJI_OBJ_INFO_VAR_START);
(*ptr) += PTP_FUJI_OBJ_INFO_VAR_START;
}

// ...
17 changes: 11 additions & 6 deletions src/ptp.h
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,9 @@ struct PtpIpInitPacket {
#define PTP_TC_UINT64ARRAY 0x4008
#define PTP_TC_STRING 0xFFFF

#define PTP_RangeForm 0x1
#define PTP_EnumerationForm 0x2

// Used for socket initialization
#define PTPIP_INIT_COMMAND_REQ 0x1
#define PTPIP_INIT_COMMAND_ACK 0x2
Expand All @@ -679,16 +682,18 @@ struct PtpIpInitPacket {
#define PTPIP_PING 0xD
#define PTPIP_PONG 0xE

// Standard interface Class ID for PTP.
// See https://en.wikipedia.org/wiki/USB#Device_classes
#define PTP_CLASS_ID 6

#define USB_VENDOR_CANON 0x4A9

// ISO number for PTP/IP, seems to be standard (?)
// ISO number for PTP/IP
#define PTP_IP_PORT 15740

// Vendor init/USB codes, not specifically PTP
// Standard interface Class ID for PTP.
// See https://en.wikipedia.org/wiki/USB#Device_classes
#define PTP_CLASS_ID 6

// bRequest codes
#define MTP_REQ_CANCEL 0x64
#define MTP_REQ_GET_EXT_EVENT_DATA 0x65
#define USB_REQ_RESET 0x66
#define USB_REQ_STATUS 0x67
#define USB_REQ_GET_STATUS 0x00
Expand Down
39 changes: 32 additions & 7 deletions test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ int ptp_vcam_magic() {
return 0;
}

// Test case for EOS T6/1300D (most common DSLR)
// Test case for EOS T6/1300D vcam
int test_eos_t6() {
struct PtpRuntime r;

Expand Down Expand Up @@ -138,6 +138,33 @@ int test_bind() {
return 0;
}

int test_props() {
struct PtpRuntime r;

int rc = test_setup_usb(&r);
if (rc) return rc;

struct PtpPropDesc pd;
rc = ptp_get_prop_desc(&r, PTP_PC_BatteryLevel, &pd);
if (rc) return rc;
assert(pd.current_value32 == 50);
assert(pd.default_value32 == 50);

assert(pd.range_form.min == 0);
assert(pd.range_form.max == 100);
assert(pd.range_form.step == 1);

rc = ptp_get_prop_desc(&r, PTP_PC_ImageSize, &pd);
if (rc) return rc;

char buffer[128];
ptp_read_string(pd.default_value, buffer, sizeof(buffer));
assert(!strcmp(buffer, "640x480"));

ptp_close(&r);
return 0;
}

int test_fs() {
struct PtpRuntime r;

Expand All @@ -151,12 +178,6 @@ int test_fs() {
ptp_device_info_json(&di, buffer, sizeof(buffer));
printf("%s\n", buffer);

struct PtpDevPropDesc pd;
rc = ptp_get_prop_desc(&r, PTP_PC_BatteryLevel, &pd);
if (rc) return rc;
assert(pd.current_value == 50);
assert(pd.default_value == 50);

struct PtpArray *arr;
rc = ptp_get_storage_ids(&r, &arr);
if (rc) return rc;
Expand Down Expand Up @@ -268,5 +289,9 @@ int main() {
printf("Return code: %d\n", rc);
if (rc) return rc;

rc = test_props();
printf("Return code: %d\n", rc);
if (rc) return rc;

return 0;
}

0 comments on commit 476c5bf

Please sign in to comment.