From 24af49d55aab423bdca979916fb12f0c1fce183d Mon Sep 17 00:00:00 2001 From: Daniel C Date: Thu, 25 Jan 2024 15:43:53 -0500 Subject: [PATCH] New data struct API, also UintArray -> PtpArray Some steps to not depend on misaligned access so much --- src/bind.c | 4 ++-- src/camlib.h | 17 ++++++++++---- src/canon_adv.c | 58 ++++++++++++++++++++++++------------------------ src/cl_data.h | 1 + src/cl_ops.h | 51 ++++++++++++++++++++++++------------------ src/data.c | 1 - src/libusb.c | 2 ++ src/operations.c | 15 +++++++++---- src/packet.c | 28 ----------------------- test/test.c | 6 ++--- 10 files changed, 90 insertions(+), 93 deletions(-) diff --git a/src/bind.c b/src/bind.c index 651b55e..2655fce 100644 --- a/src/bind.c +++ b/src/bind.c @@ -86,7 +86,7 @@ int bind_get_device_info(struct BindReq *bind, struct PtpRuntime *r) { } int bind_get_storage_ids(struct BindReq *bind, struct PtpRuntime *r) { - struct UintArray *arr; + struct PtpArray *arr; int x = ptp_get_storage_ids(r, &arr); if (x) return sprintf(bind->buffer, "{\"error\": %d}", x); @@ -113,7 +113,7 @@ int bind_get_storage_info(struct BindReq *bind, struct PtpRuntime *r) { } int bind_get_object_handles(struct BindReq *bind, struct PtpRuntime *r) { - struct UintArray *arr; + struct PtpArray *arr; // Parameters changed to correct order 14 nov 2023 int x = ptp_get_object_handles(r, bind->params[0], bind->params[1], bind->params[2], &arr); if (x) return sprintf(bind->buffer, "{\"error\": %d}", x); diff --git a/src/camlib.h b/src/camlib.h index 7584036..03c6c16 100644 --- a/src/camlib.h +++ b/src/camlib.h @@ -109,7 +109,6 @@ struct PtpRuntime { /// @brief Set to 1 to kill all IO operations. By default, this is 1. When a valid connection /// is achieved by libusb, libwpd, and tcp backends, it will be set to 0. On IO error, it /// will be set to 1. - // TODO: Should the IO backend toggle the IO kill switch uint8_t io_kill_switch; /// @brief One of enum PtpConnType @@ -167,6 +166,12 @@ struct PtpCommand { int data_length; }; +/// @brief Generic Struct for arrays +struct PtpArray { + uint32_t length; + uint32_t data[]; +}; + /// @brief Returns the return code (RC) currently in the data buffer. /// @note Not thread safe. int ptp_get_return_code(struct PtpRuntime *r); @@ -258,6 +263,13 @@ int ptp_write_unicode_string(char *dat, char *string); int ptp_read_unicode_string(char *buffer, char *dat, int max); void ptp_read_utf8_string(void *dat, char *string, int max); +inline static int ptp_write_u8 (void *buf, uint8_t out) { ((uint8_t *)buf)[0] = out; return 1; } +inline static int ptp_write_u16(void *buf, uint16_t out) { ((uint16_t *)buf)[0] = out; return 2; } +inline static int ptp_write_u32(void *buf, uint32_t out) { ((uint32_t *)buf)[0] = out; return 4; } +inline static int ptp_read_u32 (void *buf, uint32_t *out) { *out = ((uint32_t *)buf)[0]; return 4; } +inline static int ptp_read_u16 (void *buf, uint16_t *out) { *out = ((uint16_t *)buf)[0]; return 2; } +inline static int ptp_read_u8 (void *buf, uint8_t *out) { *out = ((uint8_t *)buf)[0]; return 1; } + // Build a new PTP/IP or PTP/USB command packet in r->data int ptp_new_cmd_packet(struct PtpRuntime *r, struct PtpCommand *cmd); @@ -274,9 +286,6 @@ void ptp_update_transaction(struct PtpRuntime *r, int t); // Set avail info for prop void ptp_set_prop_avail_info(struct PtpRuntime *r, int code, int memb_size, int cnt, void *data); -// Duplicate array, return malloc'd buffer -struct UintArray *ptp_dup_uint_array(struct UintArray *arr) __attribute__ ((deprecated)); - void *ptp_dup_payload(struct PtpRuntime *r); // Write r->data to a file called DUMP diff --git a/src/canon_adv.c b/src/canon_adv.c index 7fda050..09e0102 100644 --- a/src/canon_adv.c +++ b/src/canon_adv.c @@ -8,6 +8,35 @@ #include #include +#define EOS_TOK_INT 2 +#define EOS_TOK_STR 4 + +struct EvProcParam { + uint32_t type; + uint32_t number; + uint32_t p3; + uint32_t p4; + uint32_t size; +}; + +enum Types { + TOK_TEXT, + TOK_STR, + TOK_INT, +}; + +#define MAX_STR 128 +#define MAX_TOK 10 + +struct Tokens { + struct T { + int type; + char string[MAX_STR]; + int integer; + } t[MAX_TOK]; + int length; +}; + // Required on some newer cameras, like the EOS M. int ptp_eos_activate_command(struct PtpRuntime *r) { if (!ptp_check_opcode(r, PTP_OC_EOS_EnableEventProc)) { @@ -53,35 +82,6 @@ int ptp_eos_evproc_return_data(struct PtpRuntime *r) { return ptp_send(r, &cmd); } -#define EOS_TOK_INT 2 -#define EOS_TOK_STR 4 - -struct EvProcParam { - uint32_t type; - uint32_t number; - uint32_t p3; - uint32_t p4; - uint32_t size; -}; - -enum Types { - TOK_TEXT, - TOK_STR, - TOK_INT, -}; - -#define MAX_STR 128 -#define MAX_TOK 10 - -struct Tokens { - struct T { - int type; - char string[MAX_STR]; - int integer; - } t[MAX_TOK]; - int length; -}; - static int alpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } diff --git a/src/cl_data.h b/src/cl_data.h index 62d4150..05ec083 100644 --- a/src/cl_data.h +++ b/src/cl_data.h @@ -14,6 +14,7 @@ struct PtpStorageIds { uint32_t data[4]; }; +// TODO: Rename PtpUintArray struct UintArray { uint32_t length; uint32_t data[]; diff --git a/src/cl_ops.h b/src/cl_ops.h index acdf2cf..a904c30 100644 --- a/src/cl_ops.h +++ b/src/cl_ops.h @@ -1,21 +1,26 @@ -// TODO: Documentation should be in header file or above C code? +/** \file */ #ifndef OPERATIONS_H #define OPERATIONS_H -// Generic device-independent functionality +/// @brief Set a generic property - abstraction over SetDeviceProp +/// @note May reject writes if an invalid property is found (see event code) int ptp_set_generic_property(struct PtpRuntime *r, char *name, int value); + +/// @brief Call before taking a picture - this is generally for 'focusing' +/// Sometimes will do nothing. +/// @note This is meant for a onMouseDown-like event. ptp_take_picture should be called on onMouseUp int ptp_pre_take_picture(struct PtpRuntime *r); -int ptp_take_picture(struct PtpRuntime *r); -// Standard PTP operation code functions +/// @brief Call after calling ptp_pre_take_picture - this time a picture will be taken. +int ptp_take_picture(struct PtpRuntime *r); -// Note that opening a session is required for most vendor commands +/// @brief Open a new session - required for most commands int ptp_open_session(struct PtpRuntime *r); int ptp_close_session(struct PtpRuntime *r); int ptp_get_device_info(struct PtpRuntime *r, struct PtpDeviceInfo *di); -// Recieves storage IDs into an UintArray. The 'pointer pointer' will be assigned to the r->data -// buffer, so it will eventually be overwritten. You can duplicate the array with ptp_dup_uint_array() -int ptp_get_storage_ids(struct PtpRuntime *r, struct UintArray **a); +/// @brief Returns allocated array of storage IDs +/// call free() afterwards +int ptp_get_storage_ids(struct PtpRuntime *r, struct PtpArray **a); int ptp_init_capture(struct PtpRuntime *r, int storage_id, int object_format); int ptp_init_open_capture(struct PtpRuntime *r, int storage_id, int object_format); int ptp_terminate_open_capture(struct PtpRuntime *r, int trans); @@ -25,29 +30,31 @@ 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); -// Gets a list of object handles in a storage device or folder. -// id: storage ID -// format: Can specify file format ID, or zero for all -// in: Can be folder object ID, or 0 for recursive all -// Output array is a pointer to data packet, and will be overwritten by new operations -int ptp_get_object_handles(struct PtpRuntime *r, int id, int format, int in, struct UintArray **a); +/// @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 +// @param in Can be folder object ID, or 0 for recursive (entire filesystem) +// @param[out] a Output array is a pointer to data packet, and will be overwritten by new operations +int ptp_get_object_handles(struct PtpRuntime *r, int id, int format, int in, struct PtpArray **a); int ptp_get_object_info(struct PtpRuntime *r, uint32_t handle, struct PtpObjectInfo *oi); int ptp_move_object(struct PtpRuntime *r, int storage_id, int handle, int folder); int ptp_delete_object(struct PtpRuntime *r, int handle, int format_code); -// Raw JPEG data is accessible from ptp_get_payload() +/// @brief Raw JPEG data is accessible from ptp_get_payload() +/// @note Not thread safe. int ptp_get_thumbnail(struct PtpRuntime *r, int handle); +/// @note Not thread safe. int ptp_get_partial_object(struct PtpRuntime *r, uint32_t handle, int offset, int max); -// Download an object from handle, to a local file (uses GetPartialObject) +/// @brief Download an object from handle, to a local file (uses GetPartialObject) int ptp_download_file(struct PtpRuntime *r, int handle, char *file); -// Run an opcode - For debugging and prototyping only -int ptp_custom_receive(struct PtpRuntime *r, int code); -// Recieve a generic list of all properties received in DeviceInfo - caller must free s -// This is similar to getting all events, but for first startup when you know nothing. -// Some vendors do this, but this gets all the properties manually. +/// @brief Recieve a generic list of all properties received in DeviceInfo +/// This is similar to getting all events, but for first startup when you know nothing. +/// Some vendors do this, but this gets all the properties manually. +/// @param[out] s Output structure, caller must free int ptp_get_all_known(struct PtpRuntime *r, struct PtpGenericEvent **s, int *length); -// PTP/IP only +/// @note PTP/IP only int ptpip_init_events(struct PtpRuntime *r); +/// @note PTP/IP only int ptpip_init_command_request(struct PtpRuntime *r, char *device_name); // EOS Only functions - not for Canon point and shoot diff --git a/src/data.c b/src/data.c index 8323002..59e9a01 100644 --- a/src/data.c +++ b/src/data.c @@ -10,7 +10,6 @@ #include // Custom snprint with offset - for safer string building -// Eventually, this should be used for all JSON string builders static int osnprintf(char *str, int cur, int size, const char *format, ...) { if (size - cur < 0) { return 0; diff --git a/src/libusb.c b/src/libusb.c index 62c51f4..1c16d25 100644 --- a/src/libusb.c +++ b/src/libusb.c @@ -49,6 +49,8 @@ struct PtpDeviceEntry *ptpusb_device_list(struct PtpRuntime *r) { struct LibUSBBackend *backend = (struct LibUSBBackend *)r->comm_backend; + // #warning "TODO: bad access @ libusb_get_device_list + 267 -> pthread_mutex_lock + 4" + // Caused by double calling ptpusb_device_list = race condition. GUI problem? libusb_device **list; ssize_t count = libusb_get_device_list(backend->ctx, &list); diff --git a/src/operations.c b/src/operations.c index d5ea438..b32c7ec 100644 --- a/src/operations.c +++ b/src/operations.c @@ -9,10 +9,17 @@ #include #include -static struct UintArray *dup_uint_array(struct UintArray *arr) { - struct UintArray *dup = malloc(4 + arr->length * 4); +static struct PtpArray *dup_uint_array(struct UintArray *arr) { + struct PtpArray *dup = malloc(4 + arr->length * 4); if (dup == NULL) return NULL; + memcpy(dup, arr, 4 + arr->length * 4); + + ptp_write_u32(&dup->length, arr->length); + for (int i = 0; i < arr->length; i++) { + ptp_write_u32(&dup->data[i], arr->data[i]); + } + return dup; } @@ -137,7 +144,7 @@ int ptp_terminate_open_capture(struct PtpRuntime *r, int trans) { } // TODO: Return PtpStorageIds -int ptp_get_storage_ids(struct PtpRuntime *r, struct UintArray **a) { +int ptp_get_storage_ids(struct PtpRuntime *r, struct PtpArray **a) { struct PtpCommand cmd; cmd.code = PTP_OC_GetStorageIDs; cmd.param_length = 0; @@ -207,7 +214,7 @@ int ptp_send_object_info(struct PtpRuntime *r, int storage_id, int handle, struc return ptp_send_data(r, &cmd, temp, length); } -int ptp_get_object_handles(struct PtpRuntime *r, int id, int format, int in, struct UintArray **a) { +int ptp_get_object_handles(struct PtpRuntime *r, int id, int format, int in, struct PtpArray **a) { struct PtpCommand cmd; cmd.code = PTP_OC_GetObjectHandles; cmd.param_length = 3; diff --git a/src/packet.c b/src/packet.c index aea268b..23afa54 100644 --- a/src/packet.c +++ b/src/packet.c @@ -30,34 +30,6 @@ uint32_t ptp_read_uint32(void *dat) { return x; } -#if 0 -// This seems slower than misaligned access lol -uint8_t ptp_read_u8(void *dat) { - uint8_t **p = (uint8_t **)dat; - uint8_t x = (**p); - (*p)++; - return x; -} - -uint16_t ptp_read_u6(void *dat) { - uint8_t **p = (uint8_t **)dat; - uint16_t x = (p[0][0] << 8) | p[0][0]; - (*p) += 2; - return x; -} - -uint32_t ptp_read_u32(void *dat) { - uint8_t **p = (uint8_t **)dat; - uint32_t x; - ((uint8_t *)&x)[0] = p[0][0]; - ((uint8_t *)&x)[1] = p[0][1]; - ((uint8_t *)&x)[2] = p[0][2]; - ((uint8_t *)&x)[3] = p[0][3]; - (*p) += 4; - return x; -} -#endif - void ptp_write_uint8(void *dat, uint8_t b) { uint8_t **ptr = (uint8_t **)dat; (**ptr) = b; diff --git a/test/test.c b/test/test.c index 02c3a4f..3ab1aba 100644 --- a/test/test.c +++ b/test/test.c @@ -93,7 +93,7 @@ int test_eos_t6() { assert(!strcmp(di.extensions, "G-V: 1.0;")); assert(!strcmp(di.model, "Canon EOS Rebel T6")); - struct UintArray *arr; + struct PtpArray *arr; rc = ptp_get_storage_ids(&r, &arr); if (rc) return rc; int id = arr->data[0]; @@ -149,7 +149,7 @@ int test_fs() { ptp_device_info_json(&di, buffer, sizeof(buffer)); printf("%s\n", buffer); - struct UintArray *arr; + struct PtpArray *arr; rc = ptp_get_storage_ids(&r, &arr); if (rc) return rc; int id = arr->data[0]; @@ -195,7 +195,7 @@ static void *thread(void *arg) { ptp_device_info_json(&di, buffer, sizeof(buffer)); printf("%s\n", buffer); } else { - struct UintArray *arr; + struct PtpArray *arr; int rc = ptp_get_storage_ids(r, &arr); if (rc) goto err; printf("%X\n", arr->data[0]);