From 5d199352417dc8a7a14c458c9242342efa029f46 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 14 Sep 2024 19:48:38 -0400 Subject: [PATCH] Implement PTP_COMMAND_IGNORED and command resending if ignored + some docs --- src/camlib.h | 9 +++- src/cl_backend.h | 6 ++- src/lib.c | 113 +++++++++++++++++++++++++++++------------------ src/no_ip.c | 2 +- src/transport.c | 9 ++-- 5 files changed, 88 insertions(+), 51 deletions(-) diff --git a/src/camlib.h b/src/camlib.h index 31d2e17..2caf3f7 100644 --- a/src/camlib.h +++ b/src/camlib.h @@ -95,6 +95,7 @@ enum ImageFormats { /// @brief Tells lib what backend and packet style to use enum PtpConnType { + // why this this a bitmask?? PTP_IP = (1 << 0), PTP_IP_USB = (1 << 1), // TCP-based, but using USB-style packets (Fujifilm) PTP_USB = (1 << 2), @@ -141,7 +142,7 @@ struct PtpRuntime { int device_type; /// @brief For Windows compatibility, this is set to indicate lenth for a data packet - /// that will be sent after a command packet. Will be set to zero when ptp_send_bulk_packets is called. + /// that will be sent after a command packet. Will be set to zero when ptp_send_packet is called. int data_phase_length; /// @brief For session comm/io structures (holds backend instance pointers) @@ -253,6 +254,10 @@ PUB int ptp_get_event(struct PtpRuntime *r, struct PtpEventContainer *ec); /// @memberof PtpRuntime PUB void ptp_mutex_unlock(struct PtpRuntime *r); +/// @brief Completely unlock the mutex for the current thread, to ensure there isn't a deadlock +/// @memberof PtpRuntime +PUB void ptp_mutex_unlock_thread(struct PtpRuntime *r); + /// @brief Keep the mutex locked one more time for the current thread /// @note When calling a thread-safe function, this will garuntee the mutex locked, in the /// case that you want to continue using the buffer. Must be unlocked or will cause deadlock. @@ -318,7 +323,7 @@ void ptp_set_prop_avail_info(struct PtpRuntime *r, int code, int memb_size, int void *ptp_dup_payload(struct PtpRuntime *r); -// Write r->data to a file called DUMP +/// @brief Write r->data to a file called DUMP /// @note Debugging only int ptp_dump(struct PtpRuntime *r); diff --git a/src/cl_backend.h b/src/cl_backend.h index 04c634a..fe60031 100644 --- a/src/cl_backend.h +++ b/src/cl_backend.h @@ -54,10 +54,12 @@ int ptp_device_reset(struct PtpRuntime *r); /// @brief Send packets in r->data /// @memberof PTP/USB -int ptp_send_bulk_packets(struct PtpRuntime *r, int length); +int ptp_send_packet(struct PtpRuntime *r, int length); + /// @brief Receive all packets into r->data /// @memberof PTP/USB -int ptp_receive_bulk_packets(struct PtpRuntime *r); +int ptp_receive_all_packets(struct PtpRuntime *r); + /// @brief Poll the interrupt endpoint /// @memberof PTP/USB int ptp_read_int(struct PtpRuntime *r, void *to, int length); diff --git a/src/lib.c b/src/lib.c index 53e887e..3089623 100644 --- a/src/lib.c +++ b/src/lib.c @@ -146,95 +146,124 @@ void ptp_mutex_unlock_thread(struct PtpRuntime *r) { while (pthread_mutex_unlock(r->mutex) == 0); } -// Perform a generic command transaction - no data phase -int ptp_send(struct PtpRuntime *r, struct PtpCommand *cmd) { - ptp_mutex_lock(r); +static int ptp_check_rc(struct PtpRuntime *r) { + if (ptp_get_return_code(r) != PTP_RC_OK) { + ptp_verbose_log("Invalid return code: %X\n", ptp_get_return_code(r)); + return PTP_CHECK_CODE; + } - r->data_phase_length = 0; + return 0; +} +static int ptp_send_try(struct PtpRuntime *r, struct PtpCommand *cmd) { int length = ptp_new_cmd_packet(r, cmd); - if (ptp_send_bulk_packets(r, length) != length) { - ptp_mutex_unlock_thread(r); + if (ptp_send_packet(r, length) != length) { ptp_verbose_log("Didn't send all packets\n"); return PTP_IO_ERR; } - int rc = ptp_receive_bulk_packets(r); + int rc = ptp_receive_all_packets(r); if (rc < 0) { - ptp_mutex_unlock_thread(r); ptp_verbose_log("Failed to receive packets: %d\n", rc); - return PTP_IO_ERR; + return rc; } - r->transaction++; - - if (ptp_get_return_code(r) != PTP_RC_OK) { - ptp_verbose_log("Invalid return code: %X\n", ptp_get_return_code(r)); - ptp_mutex_unlock_thread(r); - return PTP_CHECK_CODE; - } - - ptp_mutex_unlock(r); return 0; } -// Perform a command request with a data phase to the camera -int ptp_send_data(struct PtpRuntime *r, struct PtpCommand *cmd, void *data, int length) { +// Perform a generic command transaction - no data phase +int ptp_send(struct PtpRuntime *r, struct PtpCommand *cmd) { ptp_mutex_lock(r); - // Required for libWPD and PTP/IP - r->data_phase_length = length; + r->data_phase_length = 0; - // Resize buffer if needed - if (length + 50 > r->data_length) { - ptp_buffer_resize(r, 100 + length); + int rc = ptp_send_try(r, cmd); + if (rc == PTP_COMMAND_IGNORED) { + ptp_verbose_log("Command ignored, trying again...\n"); + rc = ptp_send_try(r, cmd); + if (rc) { + ptp_verbose_log("Command ignored again.\n"); + ptp_mutex_unlock(r); + return PTP_IO_ERR; + } + } else if (rc) { + ptp_mutex_unlock_thread(r); + return PTP_IO_ERR; } - // Send operation request (data phase later on) + r->transaction++; + + rc = ptp_check_rc(r); + ptp_mutex_unlock(r); + return rc; +} + +static int ptp_send_data_try(struct PtpRuntime *r, struct PtpCommand *cmd, void *data, int length) { + // Send operation request (data packet later on) int plength = ptp_new_cmd_packet(r, cmd); - if (ptp_send_bulk_packets(r, plength) != plength) { - ptp_mutex_unlock_thread(r); + if (ptp_send_packet(r, plength) != plength) { return PTP_IO_ERR; } if (r->connection_type == PTP_IP) { // Send data start packet first (only has payload length) plength = ptpip_data_start_packet(r, length); - if (ptp_send_bulk_packets(r, plength) != plength) { - ptp_mutex_unlock_thread(r); + if (ptp_send_packet(r, plength) != plength) { return PTP_IO_ERR; } // Send data end packet, with payload plength = ptpip_data_end_packet(r, data, length); - if (ptp_send_bulk_packets(r, plength) != plength) { - ptp_mutex_unlock_thread(r); + if (ptp_send_packet(r, plength) != plength) { return PTP_IO_ERR; } } else { // Single data packet plength = ptp_new_data_packet(r, cmd, data, length); - if (ptp_send_bulk_packets(r, plength) != plength) { - ptp_mutex_unlock_thread(r); + if (ptp_send_packet(r, plength) != plength) { ptp_verbose_log("Failed to send data packet (%d)\n", plength); return PTP_IO_ERR; } } - if (ptp_receive_bulk_packets(r) < 0) { - ptp_mutex_unlock_thread(r); + int rc = ptp_receive_all_packets(r); + if (rc < 0) return rc; + return 0; +} + +// Perform a command request with a data phase to the camera +int ptp_send_data(struct PtpRuntime *r, struct PtpCommand *cmd, void *data, int length) { + ptp_mutex_lock(r); + + // Required for libWPD and PTP/IP + r->data_phase_length = length; + + // Resize buffer if needed + if (length + 50 > r->data_length) { + ptp_buffer_resize(r, 100 + length); + } + + // If our command is ignored (we get 0 bytes as a response), try sending the + // commands again. + int rc = ptp_send_data_try(r, cmd, data, length); + if (rc == PTP_COMMAND_IGNORED) { + ptp_verbose_log("Command ignored, trying again...\n"); + rc = ptp_send_data_try(r, cmd, data, length); + if (rc) { + ptp_verbose_log("Command ignored again.\n"); + ptp_mutex_unlock(r); + return PTP_IO_ERR; + } + } else if (rc) { + ptp_mutex_unlock(r); return PTP_IO_ERR; } r->transaction++; - if (ptp_get_return_code(r) != PTP_RC_OK) { - ptp_mutex_unlock_thread(r); - return PTP_CHECK_CODE; - } - + rc = ptp_check_rc(r); ptp_mutex_unlock(r); - return 0; + return rc; } int ptp_device_type(struct PtpRuntime *r) { diff --git a/src/no_ip.c b/src/no_ip.c index d669aee..083c50b 100644 --- a/src/no_ip.c +++ b/src/no_ip.c @@ -7,7 +7,7 @@ int ptpip_new_timeout_socket(char *addr, int port) { return -1; } -int ptpip_connect(struct PtpRuntime *r, const char *addr, int port) { +int ptpip_connect(struct PtpRuntime *r, const char *addr, int port, int extra_tmout) { return -1; } diff --git a/src/transport.c b/src/transport.c index 401dc5b..a7f603d 100644 --- a/src/transport.c +++ b/src/transport.c @@ -33,7 +33,7 @@ int ptpusb_send_bulk_packets(struct PtpRuntime *r, int length) { } } -int ptp_send_bulk_packets(struct PtpRuntime *r, int length) { +int ptp_send_packet(struct PtpRuntime *r, int length) { if (r->connection_type == PTP_USB) { return ptpusb_send_bulk_packets(r, length); } @@ -82,7 +82,7 @@ int ptpip_read_packet(struct PtpRuntime *r, int of) { if (rc < 0) { ptp_verbose_log("Failed to read packet length: %d\n", rc); - return PTP_IO_ERR; + return PTP_COMMAND_IGNORED; } if (rc < 4) { @@ -258,7 +258,7 @@ int ptpipusb_read_packet(struct PtpRuntime *r, int of) { if (rc < 0) { ptp_verbose_log("Failed to read packet length: %d\n", rc); - return PTP_IO_ERR; + return PTP_COMMAND_IGNORED; } if (rc < 4) { @@ -319,6 +319,7 @@ int ptpipusb_receive_bulk_packets(struct PtpRuntime *r) { // Handle data phase if (c->type == PTP_PACKET_TYPE_DATA) { rc = ptpipusb_read_packet(r, read); + if (rc == PTP_COMMAND_IGNORED) rc = PTP_IO_ERR; // Command was not ignored as we got a data packet if (rc < 0) return rc; read += rc; @@ -330,7 +331,7 @@ int ptpipusb_receive_bulk_packets(struct PtpRuntime *r) { return read; } -int ptp_receive_bulk_packets(struct PtpRuntime *r) { +int ptp_receive_all_packets(struct PtpRuntime *r) { if (r->io_kill_switch) return -1; if (r->connection_type == PTP_IP) { return ptpip_receive_bulk_packets(r);