summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDidier Raboud <odyx@debian.org>2018-07-24 08:21:33 +0200
committerDidier Raboud <odyx@debian.org>2018-07-24 08:21:33 +0200
commit876ffda4e40b8410f5fe65caf24736558284ba3b (patch)
tree9bf1fe911d83306a6fabc7a0448b693fdca9e164
parent7ceb9c6060461ba4c7fad6999c63fafc28f090d7 (diff)
parent8782a5cf9a99c876e197d0f26151ba45c6de68f1 (diff)
record new upstream branch and merge it
-rw-r--r--debian/.git-dpm14
-rw-r--r--readme.md2
-rw-r--r--src/http.c573
-rw-r--r--src/http.h44
-rw-r--r--src/ippusbxd.c767
-rw-r--r--src/options.h1
-rw-r--r--src/tcp.c133
-rw-r--r--src/tcp.h12
-rw-r--r--src/usb.c307
-rw-r--r--src/usb.h6
10 files changed, 699 insertions, 1160 deletions
diff --git a/debian/.git-dpm b/debian/.git-dpm
index 861de66..0c7ebde 100644
--- a/debian/.git-dpm
+++ b/debian/.git-dpm
@@ -1,8 +1,8 @@
# see git-dpm(1) from git-dpm package
-1e3fde8c875770a5e906d026f96798c956563137
-1e3fde8c875770a5e906d026f96798c956563137
-1e3fde8c875770a5e906d026f96798c956563137
-1e3fde8c875770a5e906d026f96798c956563137
-ippusbxd_1.32.orig.tar.gz
-78ed507f1ba804713de9ab897b8b20950fac28bc
-87106
+8782a5cf9a99c876e197d0f26151ba45c6de68f1
+8782a5cf9a99c876e197d0f26151ba45c6de68f1
+8782a5cf9a99c876e197d0f26151ba45c6de68f1
+8782a5cf9a99c876e197d0f26151ba45c6de68f1
+ippusbxd_1.33.orig.tar.gz
+06fabfe6e59263aac473d803a89e14bfe11c3c5b
+84316
diff --git a/readme.md b/readme.md
index 6f3502c..0fcbb56 100644
--- a/readme.md
+++ b/readme.md
@@ -1,6 +1,6 @@
# IPPUSBXD [![Coverity analysis status](https://scan.coverity.com/projects/2634/badge.svg)](https://scan.coverity.com/projects/2634)
-Version 1.31
+Version 1.33
## About
diff --git a/src/http.c b/src/http.c
index a2c897b..bdfe46b 100644
--- a/src/http.c
+++ b/src/http.c
@@ -27,551 +27,28 @@
#define BUFFER_STEP (1 << 12)
-struct http_message_t *http_message_new()
+struct http_packet_t *packet_new()
{
- struct http_message_t *msg = calloc(1, sizeof(*msg));
- if (msg == NULL) {
- ERR("failed to alloc space for http message");
- return NULL;
- }
-
- msg->spare_capacity = 0;
- msg->spare_filled = 0;
- msg->spare_buffer = NULL;
-
- return msg;
-}
-
-void message_free(struct http_message_t *msg)
-{
- free(msg->spare_buffer);
- free(msg);
-}
-
-static void packet_check_completion(struct http_packet_t *pkt)
-{
- struct http_message_t *msg = pkt->parent_message;
- /* Msg full */
- if (msg->claimed_size && msg->received_size >= msg->claimed_size) {
- msg->is_completed = 1;
- NOTE("http: Message completed: Received size >= claimed size");
-
- /* Sanity check */
- if (msg->spare_filled > 0)
- ERR_AND_EXIT("Msg spare not empty upon completion");
- }
-
- /* Pkt full */
- if (pkt->expected_size && pkt->filled_size >= pkt->expected_size) {
- pkt->is_completed = 1;
- NOTE("http: Packet completed: Packet full");
- }
-
- /* Pkt over capacity */
- if (pkt->filled_size > pkt->buffer_capacity) {
- /* Santiy check */
- ERR_AND_EXIT("Overflowed packet buffer");
- }
-}
-
-static int doesMatch(const char *matcher, size_t matcher_len,
- const uint8_t *key, size_t key_len)
-{
- for (size_t i = 0; i < matcher_len; i++)
- if (i >= key_len || matcher[i] != key[i])
- return 0;
- return 1;
-}
-
-static int inspect_header_field(struct http_packet_t *pkt, size_t header_size,
- char *key, size_t key_size)
-{
- /* Find key */
- uint8_t *pos = memmem(pkt->buffer, header_size, key, key_size);
- if (pos == NULL)
- return -1;
-
- /* Find first digit */
- size_t number_pos = (size_t) (pos - pkt->buffer) + key_size;
- while (number_pos < pkt->filled_size && !isdigit(pkt->buffer[number_pos]))
- ++number_pos;
-
- /* Find next non-digit */
- size_t number_end = number_pos;
- while (number_end < pkt->filled_size && isdigit(pkt->buffer[number_end]))
- ++number_end;
-
- /* Failed to find next non-digit
- header field might be broken */
- if (number_end >= pkt->filled_size)
- return -1;
-
- /* Temporary stringification of buffer for atoi() */
- uint8_t original_char = pkt->buffer[number_end];
- pkt->buffer[number_end] = '\0';
- int val = atoi((const char *)(pkt->buffer + number_pos));
-
- /* Restore buffer */
- pkt->buffer[number_end] = original_char;
- return val;
-}
-
-static void packet_store_excess(struct http_packet_t *pkt)
-{
- struct http_message_t *msg = pkt->parent_message;
- if (msg->spare_buffer != NULL)
- ERR_AND_EXIT("Do not store excess to non-empty packet");
-
- if (pkt->expected_size >= pkt->filled_size)
- ERR_AND_EXIT("Do not call packet_store_excess() unless needed");
-
- size_t spare_size = pkt->filled_size - pkt->expected_size;
- size_t non_spare = pkt->expected_size;
- NOTE("HTTP: Storing %d bytes of excess", spare_size);
-
- /* Align to BUFFER_STEP */
- size_t needed_size = 0;
- needed_size += spare_size / BUFFER_STEP;
- needed_size += (spare_size % BUFFER_STEP) > 0 ? BUFFER_STEP : 0;
-
- if (msg->spare_buffer == NULL) {
- uint8_t *buffer = calloc(1, needed_size);
- if (buffer == NULL)
- ERR_AND_EXIT("Failed to alloc msg spare buffer");
- msg->spare_buffer = buffer;
- }
-
- memcpy(msg->spare_buffer, pkt->buffer + non_spare, spare_size);
- pkt->filled_size = non_spare;
-
- msg->spare_capacity = needed_size;
- msg->spare_filled = spare_size;
-}
-
-static void packet_take_spare(struct http_packet_t *pkt)
-{
- struct http_message_t *msg = pkt->parent_message;
- if (msg->spare_filled == 0)
- return;
-
- if (msg->spare_buffer == NULL)
- return;
-
- if (pkt->filled_size > 0)
- ERR_AND_EXIT("pkt should be empty when loading msg spare");
-
- /* Take message's buffer */
- size_t msg_size = msg->spare_capacity;
- size_t msg_filled = msg->spare_filled;
- uint8_t *msg_buffer = msg->spare_buffer;
-
- pkt->buffer_capacity = msg_size;
- pkt->filled_size = msg_filled;
- pkt->buffer = msg_buffer;
-
- msg->spare_capacity = 0;
- msg->spare_filled = 0;
- msg->spare_buffer = NULL;
-}
-
-static ssize_t packet_find_chunked_size(struct http_packet_t *pkt)
-{
- /* NOTE:
- chunks can have trailers which are
- tacked on http header fields.
- NOTE:
- chunks may also have extensions.
- No one uses or supports them. */
-
- /* Find end of size string */
- if (pkt->filled_size >= SSIZE_MAX)
- ERR_AND_EXIT("Buffer beyond sane size");
-
- ssize_t max = (ssize_t) pkt->filled_size;
- ssize_t size_end = -1;
- ssize_t miniheader_end = -1;
- ssize_t delimiter_start = -1;
- for (ssize_t i = 0; i < max; i++) {
-
- uint8_t *buf = pkt->buffer;
- if (size_end < 0) {
- /* No extension */
- if (i + 1 < max &&
- (buf[i] == '\r' && /* CR */
- buf[i + 1] == '\n')) { /* LF */
- size_end = i + 1;
- miniheader_end = size_end;
- delimiter_start = i;
- break;
- }
-
- /* No extension */
- if (buf[i] == '\n') { /* LF */
- size_end = i;
- miniheader_end = size_end;
- delimiter_start = i;
- break;
- }
-
- /* Has extensions */
- if (buf[i] == ';') {
- size_end = i;
- continue;
- }
- }
- if (miniheader_end < 0) {
- if (i + 1 < max &&
- (buf[i] == '\r' && /* CR */
- buf[i + 1] == '\n')) { /* LF */
- miniheader_end = i + 1;
- delimiter_start = i;
- break;
- }
-
- if (buf[i] == '\n') { /* LF */
- miniheader_end = i;
- delimiter_start = i;
- break;
- }
- }
- }
-
- if (miniheader_end < 0) {
- /* NOTE: knowing just the size field
- is not enough since the extensions
- are not included in the size */
- NOTE("failed to find chunk mini-header so far");
- return -1;
- }
-
- /* Temporary stringification for strtol() */
- uint8_t original_char = *(pkt->buffer + size_end);
- *(pkt->buffer + size_end) = '\0';
- size_t size = strtoul((char *)pkt->buffer, NULL, 16);
- NOTE("Chunk size raw: %s", pkt->buffer);
- *(pkt->buffer + size_end) = original_char;
- if (size > SSIZE_MAX)
- ERR_AND_EXIT("chunk size is insane");
-
- if (size > 0) {
- /* Regular chunk */
- ssize_t chunk_size = (ssize_t) size; /* Chunk body */
- chunk_size += miniheader_end + 1; /* Mini-header */
- chunk_size += 2; /* Trailing CRLF */
- NOTE("HTTP: Chunk size: %lu", chunk_size);
- return (ssize_t) chunk_size;
- }
-
- /* Terminator chunk
- May have trailers in body */
- ssize_t full_size = -1;
- for (ssize_t i = delimiter_start; i < max; i++) {
- uint8_t *buf = pkt->buffer;
- if (i + 3 < max &&
- (buf[i] == '\r' && /* CR */
- buf[i + 1] == '\n' && /* LF */
- buf[i + 2] == '\r' && /* CR */
- buf[i + 3] == '\n')) { /* LF */
- full_size = i + 4;
- break;
- }
-
- if (i + 1 < max &&
- buf[i] == '\n' && /* LF */
- buf[i + 1] == '\n') { /* LF */
- full_size = i + 2;
- break;
- }
- }
-
- if (full_size < 0) {
- NOTE("Chunk miniheader present but body incomplete");
- return -1;
- }
-
- NOTE("Found end chunked packet");
- pkt->parent_message->is_completed = 1;
- pkt->is_completed = 1;
- return full_size;
-}
-
-static ssize_t packet_get_header_size(struct http_packet_t *pkt)
-{
- if (pkt->header_size != 0)
- goto found;
-
- /*
- * RFC2616 recomends we match newline on \n despite full
- * complience requires the message to use only \r\n
- * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.3
- */
-
- /* Find header */
- for (size_t i = 0; i < pkt->filled_size && i < SSIZE_MAX; i++) {
- /* two \r\n pairs */
- if ((i + 3) < pkt->filled_size &&
- '\r' == pkt->buffer[i] &&
- '\n' == pkt->buffer[i + 1] &&
- '\r' == pkt->buffer[i + 2] &&
- '\n' == pkt->buffer[i + 3]) {
- pkt->header_size = i + 4;
- goto found;
- }
-
- /* two \n pairs */
- if ((i + 1) < pkt->filled_size &&
- '\n' == pkt->buffer[i] &&
- '\n' == pkt->buffer[i + 1]) {
- pkt->header_size = i + 2;
- goto found;
- }
- }
-
- return -1;
-
- found:
- return (ssize_t) pkt->header_size;
-}
-
-enum http_request_t packet_find_type(struct http_packet_t *pkt)
-{
- enum http_request_t type = HTTP_UNSET;
- size_t size = 0;
- /*
- * Valid methods for determining http request
- * size are defined by W3 in RFC2616 section 4.4
- * link: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
- */
-
- /*
- * This function attempts to find what method this
- * packet would use. This is only possible in specific case:
- * 1. if the request uses method 1 we can check the http
- * request type. We must be called on a packet which
- * has the full header.
- * 2. if the request uses method 2 we need the full header
- * but a simple network-byte-order-aware string search
- * works. This function does not work if called with
- * a chunked transport's sub-packet.
- * 3. if the request uses method 3 we again perform the
- * string search.
- *
- * All cases require the packat to contain the full header.
- */
-
- ssize_t header_size_raw = packet_get_header_size(pkt);
- if (header_size_raw < 0) {
- /* We don't have the header yet */
- goto do_ret;
- }
- size_t header_size = (size_t) header_size_raw;
-
- /* Try Transfer-Encoding Chunked */
- char xfer_encode_str[] = "Transfer-Encoding: chunked";
- size_t xfer_encode_str_size = sizeof(xfer_encode_str) - 1;
- uint8_t *xfer_encode_pos = memmem(pkt->buffer, header_size,
- xfer_encode_str,
- xfer_encode_str_size);
- if (xfer_encode_pos != NULL) {
- size = 0;
- type = HTTP_CHUNKED;
- goto do_ret;
- }
-
- /* Try Content-Length */
- char content_length_str[] = "Content-Length: ";
- ssize_t contlen_size =
- inspect_header_field(pkt, header_size,
- content_length_str, sizeof(content_length_str) - 1);
- if (contlen_size >= 0) {
- size = (size_t) contlen_size + header_size;
- type = HTTP_CONTENT_LENGTH;
- goto do_ret;
- }
-
- /* GET requests (start with GET) or answers from the server (start
- with HTTP) */
- if (doesMatch("GET", 3, pkt->buffer, pkt->filled_size) ||
- doesMatch("HTTP", 4, pkt->buffer, pkt->filled_size)) {
- size = header_size;
- type = HTTP_HEADER_ONLY;
- goto do_ret;
- }
-
- /* No size was detectable yet header was found */
- type = HTTP_UNKNOWN;
- size = 0;
-
- do_ret:
- pkt->parent_message->claimed_size = size;
- pkt->parent_message->type = type;
- return type;
-}
-
-size_t packet_pending_bytes(struct http_packet_t *pkt)
-{
- struct http_message_t *msg = pkt->parent_message;
-
- /* Check Cache */
- if (pkt->expected_size > 0)
- goto pending_known;
-
- if (HTTP_UNSET == msg->type) {
- msg->type = packet_find_type(pkt);
-
- if (HTTP_CHUNKED == msg->type) {
- /* Note: this was the packet with the
- header of our chunked message. */
-
- /* Save any non-header data we got */
- ssize_t header_size = packet_get_header_size(pkt);
-
- /* Sanity check */
- if (header_size < 0 ||
- (size_t)header_size > pkt->filled_size)
- ERR_AND_EXIT("HTTP: Could not find header twice");
-
- NOTE("HTTP: Chunked header size is %ld bytes",
- header_size);
- pkt->expected_size = (size_t) header_size;
- msg->claimed_size = 0;
- goto pending_known;
- }
- }
-
- if (HTTP_CHUNKED == msg->type) {
- if (pkt->filled_size == 0) {
- /* Grab chunk's mini-header */
- goto pending_known;
- }
-
- if (pkt->expected_size == 0) {
- /* Check chunk's mini-header */
- ssize_t size = packet_find_chunked_size(pkt);
- if (size <= 0) {
- ERR("=============================================");
- ERR("Malformed chunk-transport http header receivd");
- ERR("Missing chunk's mini-headers in first data");
- ERR("Have %d bytes", pkt->filled_size);
- printf("%.*s\n", (int)pkt->filled_size, pkt->buffer);
- ERR("Malformed chunk-transport http header receivd");
- ERR("=============================================");
- goto pending_known;
- }
-
- pkt->expected_size = (size_t) size;
- msg->claimed_size = 0;
- }
-
- goto pending_known;
- }
- if (HTTP_HEADER_ONLY == msg->type) {
- /* Note: we can only know it is header only
- when the buffer already contains the header.
- So this next call cannot fail. */
- pkt->expected_size = (size_t) packet_get_header_size(pkt);
- msg->claimed_size = pkt->expected_size;
- goto pending_known;
- }
- if (HTTP_CONTENT_LENGTH == msg->type) {
- /* Note: find_header() has
- filled msg's claimed_size */
- msg->claimed_size = msg->claimed_size;
- pkt->expected_size = msg->claimed_size;
- goto pending_known;
- }
-
- pending_known:
-
- /* Save excess data */
- if (pkt->expected_size && pkt->filled_size > pkt->expected_size)
- packet_store_excess(pkt);
-
- size_t expected = pkt->expected_size;
- if (expected == 0)
- expected = msg->claimed_size;
- if (expected == 0)
- expected = pkt->buffer_capacity;
-
- /* Sanity check */
- if (expected < pkt->filled_size)
- ERR_AND_EXIT("Expected cannot be larger than filled");
-
- size_t pending = expected - pkt->filled_size;
-
- /* Expand buffer as needed */
- while (pending + pkt->filled_size > pkt->buffer_capacity) {
- ssize_t size_added = packet_expand(pkt);
- if (size_added < 0) {
- WARN("packet at max allowed size");
- return 0;
- }
- if (size_added == 0) {
- ERR("Failed to expand packet");
- return 0;
- }
- }
-
- packet_check_completion(pkt);
-
- return pending;
-}
-
-void packet_mark_received(struct http_packet_t *pkt, size_t received)
-{
- struct http_message_t *msg = pkt->parent_message;
- msg->received_size += received;
-
- pkt->filled_size += received;
- if (received) {
- NOTE("HTTP: got %lu bytes so: pkt has %lu bytes, "
- "msg has %lu bytes",
- received, pkt->filled_size, msg->received_size);
- }
-
- packet_check_completion(pkt);
-
- if (pkt->filled_size > pkt->buffer_capacity)
- ERR_AND_EXIT("Overflowed packet's buffer");
-
- if (pkt->expected_size && pkt->filled_size > pkt->expected_size) {
- /* Store excess data */
- packet_store_excess(pkt);
- }
-}
-
-struct http_packet_t *packet_new(struct http_message_t *parent_msg)
-{
- struct http_packet_t *pkt = NULL;
- uint8_t *buf = NULL;
size_t const capacity = BUFFER_STEP;
- assert(parent_msg != NULL);
- pkt = calloc(1, sizeof(*pkt));
+ struct http_packet_t *pkt = calloc(1, sizeof(*pkt));
if (pkt == NULL) {
ERR("failed to alloc packet");
return NULL;
}
- pkt->parent_message = parent_msg;
- pkt->expected_size = 0;
-
- /* Claim any spare data from prior packets */
- packet_take_spare(pkt);
- if (pkt->buffer == NULL) {
- buf = calloc(capacity, sizeof(*buf));
- if (buf == NULL) {
- ERR("failed to alloc space for packet's buffer or space for packet");
- free(pkt);
- return NULL;
- }
-
- /* Assemble packet */
- pkt->buffer = buf;
- pkt->buffer_capacity = capacity;
- pkt->filled_size = 0;
+ uint8_t *buf = calloc(capacity, sizeof(*buf));
+ if (buf == NULL) {
+ ERR("failed to alloc space for packet's buffer or space for packet");
+ free(pkt);
+ return NULL;
}
+ /* Assemble packet */
+ pkt->buffer = buf;
+ pkt->buffer_capacity = capacity;
+ pkt->filled_size = 0;
+
return pkt;
}
@@ -580,29 +57,3 @@ void packet_free(struct http_packet_t *pkt)
free(pkt->buffer);
free(pkt);
}
-
-#define MAX_PACKET_SIZE (1 << 26) /* 64MiB */
-ssize_t packet_expand(struct http_packet_t *pkt)
-{
- size_t cur_size = pkt->buffer_capacity;
- size_t new_size = cur_size * 2;
- if (new_size > MAX_PACKET_SIZE) {
- WARN("HTTP: cannot expand packet beyond limit");
- return -1;
- }
- NOTE("HTTP: doubling packet buffer to %lu", new_size);
-
- uint8_t *new_buf = realloc(pkt->buffer, new_size);
- if (new_buf == NULL) {
- /* If realloc fails the original buffer is still valid */
- WARN("Failed to expand packet");
- return 0;
- }
- pkt->buffer = new_buf;
- pkt->buffer_capacity = new_size;
-
- size_t diff = new_size - cur_size;
- if (diff > SSIZE_MAX)
- ERR_AND_EXIT("Buffer expanded beyond sane limit");
- return (ssize_t) diff;
-}
diff --git a/src/http.h b/src/http.h
index 44744ec..664fae4 100644
--- a/src/http.h
+++ b/src/http.h
@@ -16,51 +16,11 @@
#include <stdint.h>
#include <sys/types.h>
-enum http_request_t {
- HTTP_UNSET,
- HTTP_UNKNOWN,
- HTTP_CHUNKED,
- HTTP_CONTENT_LENGTH,
- HTTP_HEADER_ONLY
-};
-
-struct http_message_t {
- enum http_request_t type;
-
- size_t spare_filled;
- size_t spare_capacity;
- uint8_t *spare_buffer;
-
- size_t unreceived_size;
- uint8_t is_completed;
-
- /* Detected from child packets */
- size_t claimed_size;
- size_t received_size;
-};
-
struct http_packet_t {
- /* Cache */
- size_t header_size;
-
size_t filled_size;
- size_t expected_size;
-
size_t buffer_capacity;
uint8_t *buffer;
-
- struct http_message_t *parent_message;
-
- uint8_t is_completed;
};
-struct http_message_t *http_message_new(void);
-void message_free(struct http_message_t *);
-
-enum http_request_t packet_find_type(struct http_packet_t *pkt);
-size_t packet_pending_bytes(struct http_packet_t *);
-void packet_mark_received(struct http_packet_t *, size_t);
-
-struct http_packet_t *packet_new(struct http_message_t *);
-void packet_free(struct http_packet_t *);
-ssize_t packet_expand(struct http_packet_t *);
+struct http_packet_t *packet_new();
+void packet_free(struct http_packet_t *pkt);
diff --git a/src/ippusbxd.c b/src/ippusbxd.c
index ead0670..b0fae90 100644
--- a/src/ippusbxd.c
+++ b/src/ippusbxd.c
@@ -22,6 +22,9 @@
#include <unistd.h>
#include <getopt.h>
#include <pthread.h>
+#include <errno.h>
+
+#include <libusb.h>
#include "options.h"
#include "logging.h"
@@ -33,13 +36,52 @@
struct service_thread_param {
struct tcp_conn_t *tcp;
struct usb_sock_t *usb_sock;
+ struct usb_conn_t *usb_conn;
pthread_t thread_handle;
- int thread_num;
+ uint32_t thread_num;
+ pthread_cond_t *cond;
+};
+
+struct libusb_callback_data {
+ int *read_inflight;
+ uint32_t thread_num;
+ struct tcp_conn_t *tcp;
+ struct http_packet_t *pkt;
+ pthread_mutex_t *read_inflight_mutex;
+ pthread_cond_t *read_inflight_cond;
};
+/* Function prototypes */
+static void *service_connection(void *params_void);
+
+static void service_socket_connection(struct service_thread_param *params);
+
+static void *service_printer_connection(void *params_void);
+
+static int allocate_socket_connection(struct service_thread_param *param);
+
+static int setup_socket_connection(struct service_thread_param *param);
+
+static int setup_usb_connection(struct usb_sock_t *usb_sock,
+ struct service_thread_param *param);
+
+static int setup_communication_thread(void *(*routine)(void *),
+ struct service_thread_param *param);
+
+static int get_read_inflight(const int *read_inflight,
+ pthread_mutex_t *read_inflight_mutex);
+
+static struct libusb_callback_data *setup_libusb_callback_data(
+ struct http_packet_t *pkt, int *read_inflight,
+ struct service_thread_param *thread_param,
+ pthread_mutex_t *read_inflight_mutex);
+
+static int is_socket_open(const struct service_thread_param *param);
+
+/* Global variables */
static pthread_mutex_t thread_register_mutex;
static struct service_thread_param **service_threads = NULL;
-static int num_service_threads = 0;
+static uint32_t num_service_threads = 0;
static void sigterm_handler(int sig)
{
@@ -48,10 +90,11 @@ static void sigterm_handler(int sig)
NOTE("Caught signal %d, shutting down ...", sig);
}
-static void list_service_threads(int num_service_threads,
- struct service_thread_param **service_threads)
+static void list_service_threads(
+ uint32_t num_service_threads,
+ struct service_thread_param **service_threads)
{
- int i;
+ uint32_t i;
char *p;
char buf[10240];
@@ -61,7 +104,7 @@ static void list_service_threads(int num_service_threads,
snprintf(p, sizeof(buf) - strlen(buf), "None");
} else {
for (i = 0; i < num_service_threads; i ++) {
- snprintf(p, sizeof(buf) - strlen(buf), "#%d, ",
+ snprintf(p, sizeof(buf) - strlen(buf), "#%u, ",
service_threads[i]->thread_num);
p = buf + strlen(buf);
}
@@ -72,291 +115,515 @@ static void list_service_threads(int num_service_threads,
NOTE("%s", buf);
}
-static int register_service_thread(int *num_service_threads,
- struct service_thread_param ***service_threads,
- struct service_thread_param *new_thread)
+static int register_service_thread(
+ uint32_t *num_service_threads,
+ struct service_thread_param ***service_threads,
+ struct service_thread_param *new_thread)
{
- NOTE("Registering thread #%d", new_thread->thread_num);
- (*num_service_threads) ++;
- *service_threads = realloc(*service_threads,
- *num_service_threads * sizeof(void*));
+ NOTE("Registering thread #%u", new_thread->thread_num);
+ (*num_service_threads)++;
+ *service_threads =
+ realloc(*service_threads, *num_service_threads * sizeof(void *));
if (*service_threads == NULL) {
- ERR("Registering thread #%d: Failed to alloc space for thread registration list",
- new_thread->thread_num);
+ ERR("Registering thread #%u: Failed to alloc space for thread registration "
+ "list",
+ new_thread->thread_num);
return -1;
}
(*service_threads)[*num_service_threads - 1] = new_thread;
return 0;
}
-static int unregister_service_thread(int *num_service_threads,
- struct service_thread_param ***service_threads,
- int thread_num)
+static int unregister_service_thread(
+ uint32_t *num_service_threads,
+ struct service_thread_param ***service_threads, uint32_t thread_num)
{
- int i;
+ uint32_t i;
+
+ NOTE("Unregistering thread #%u", thread_num);
+ /* Search |service_threads| for an element with a matching thread number. */
+ for (i = 0; i < *num_service_threads; i++) {
+ if ((*service_threads)[i]->thread_num == thread_num) break;
+ }
- NOTE("Unregistering thread #%d", thread_num);
- for (i = 0; i < *num_service_threads; i ++)
- if ((*service_threads)[i]->thread_num == thread_num)
- break;
if (i >= *num_service_threads) {
- ERR("Unregistering thread #%d: Cannot unregister, not found", thread_num);
+ ERR("Unregistering thread #%u: Cannot unregister, not found", thread_num);
return -1;
}
- (*num_service_threads) --;
- for (; i < *num_service_threads; i ++)
+
+ (*num_service_threads)--;
+ struct service_thread_param *removed_thread = (*service_threads)[i];
+ /* Shift the contents after |removed_thread| down. */
+ for (; i < *num_service_threads; i++) {
(*service_threads)[i] = (*service_threads)[i + 1];
- *service_threads = realloc(*service_threads,
- *num_service_threads * sizeof(void*));
- if (*num_service_threads == 0)
+ }
+ free(removed_thread);
+
+ *service_threads =
+ realloc(*service_threads, *num_service_threads * sizeof(void *));
+
+ if (*num_service_threads == 0) {
*service_threads = NULL;
- else if (*service_threads == NULL) {
- ERR("Unregistering thread #%d: Failed to alloc space for thread registration list",
- thread_num);
+ } else if (*service_threads == NULL) {
+ ERR("Unregistering thread #%u: Failed to alloc space for thread "
+ "registration list",
+ thread_num);
return -1;
}
+
return 0;
}
static void
cleanup_handler(void *arg_void)
{
- int thread_num = *((int *)(arg_void));
- NOTE("Thread #%d: Called clean-up handler", thread_num);
+ uint32_t thread_num = *((int *)(arg_void));
+ NOTE("Thread #%u: Called clean-up handler", thread_num);
pthread_mutex_lock(&thread_register_mutex);
unregister_service_thread(&num_service_threads, &service_threads, thread_num);
list_service_threads(num_service_threads, service_threads);
pthread_mutex_unlock(&thread_register_mutex);
}
-static void *service_connection(void *arg_void)
+static void read_transfer_callback(struct libusb_transfer *transfer)
{
- struct service_thread_param *arg =
- (struct service_thread_param *)arg_void;
- int thread_num = arg->thread_num;
+ struct libusb_callback_data *user_data =
+ (struct libusb_callback_data *)transfer->user_data;
+
+ uint32_t thread_num = user_data->thread_num;
+ pthread_mutex_t *read_inflight_mutex = user_data->read_inflight_mutex;
+ pthread_cond_t *read_inflight_cond = user_data->read_inflight_cond;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ user_data->pkt->filled_size = transfer->actual_length;
+
+ if (transfer->actual_length) {
+ NOTE("Thread #%u: Pkt from %s (buffer size: %zu)\n===\n%s===", thread_num,
+ "usb", user_data->pkt->filled_size,
+ hexdump(user_data->pkt->buffer, (int)user_data->pkt->filled_size));
+
+ tcp_packet_send(user_data->tcp, user_data->pkt);
+ /* Mark the tcp socket as active. */
+ set_is_active(user_data->tcp, 1);
+ }
+
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ ERR("Thread #%u: There was an error completing the transfer", thread_num);
+ g_options.terminate = 1;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ NOTE(
+ "Thread #%u: The transfer timed out before it could be completed: "
+ "Received %u bytes",
+ thread_num, transfer->actual_length);
+ break;
+ case LIBUSB_TRANSFER_CANCELLED:
+ NOTE("Thread #%u: The transfer was cancelled", thread_num);
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ ERR("Thread #%u: The transfer has stalled", thread_num);
+ g_options.terminate = 1;
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ ERR("Thread #%u: The printer was disconnected during the transfer",
+ thread_num);
+ g_options.terminate = 1;
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ ERR("Thread #%u: The printer sent more data than was requested",
+ thread_num);
+ g_options.terminate = 1;
+ break;
+ default:
+ ERR("Thread #%u: Something unexpected happened", thread_num);
+ g_options.terminate = 1;
+ }
- NOTE("Thread #%d: Starting", thread_num);
+ /* Free the packet used for the transfer. */
+ packet_free(user_data->pkt);
+
+ /* Mark the transfer as completed. */
+ pthread_mutex_lock(read_inflight_mutex);
+ *user_data->read_inflight = 0;
+ pthread_cond_broadcast(read_inflight_cond);
+ pthread_mutex_unlock(read_inflight_mutex);
+
+ /* Cleanup the data used for the transfer */
+ free(user_data);
+ libusb_free_transfer(transfer);
+}
+
+/* This function is responsible for handling connection requests and
+ is run in a separate thread. It detaches itself from the main thread and sets
+ up a USB connection with the printer. This function spawns a partner thread
+ which is responsible for reading from the printer, and then this function
+ calls into service_socket_connection() which is responsible for reading from
+ the socket which made the connection request. Once the socket has closed its
+ end of communiction, this function notifies its partner thread that the
+ connection has been closed and then joins on the partner thread before
+ shutting down. */
+static void *service_connection(void *params_void)
+{
+ struct service_thread_param *params =
+ (struct service_thread_param *)params_void;
+ uint32_t thread_num = params->thread_num;
- /* Detach this thread so that the main thread does not need to join this thread
- after termination for clean-up */
+ /* Detach this thread so that the main thread does not need to join this
+ thread after termination for clean-up. */
pthread_detach(pthread_self());
- /* Register clean-up handler */
+ /* Register clean-up handler. */
pthread_cleanup_push(cleanup_handler, &thread_num);
- /* Allow immediate cancelling of this thread */
+ /* Allow immediate cancelling of this thread. */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
- /* classify priority */
- struct usb_conn_t *usb = NULL;
- int usb_failed = 0;
- while (!arg->tcp->is_closed && usb_failed == 0 && !g_options.terminate) {
- struct http_message_t *server_msg = NULL;
- struct http_message_t *client_msg = NULL;
-
- /* Client's request */
- client_msg = http_message_new();
- if (client_msg == NULL) {
- ERR("Thread #%d: Failed to create client message", thread_num);
- break;
- }
- NOTE("Thread #%d: M %p: Client msg starting",
- thread_num, client_msg);
-
- while (!client_msg->is_completed && !g_options.terminate) {
- struct http_packet_t *pkt;
- pkt = tcp_packet_get(arg->tcp, client_msg);
- if (pkt == NULL) {
- if (arg->tcp->is_closed) {
- NOTE("Thread #%d: M %p: Client closed connection",
- thread_num, client_msg);
- goto cleanup_subconn;
- }
- ERR("Thread #%d: M %p: Got null packet from tcp",
- thread_num, client_msg);
- goto cleanup_subconn;
- }
- if (usb == NULL && arg->usb_sock != NULL) {
- usb = usb_conn_acquire(arg->usb_sock);
- if (usb == NULL) {
- ERR("Thread #%d: M %p: Failed to acquire usb interface",
- thread_num, client_msg);
- packet_free(pkt);
- usb_failed = 1;
- goto cleanup_subconn;
- }
- usb_failed = 0;
- NOTE("Thread #%d: M %p: Interface #%d: acquired usb conn",
- thread_num, client_msg,
- usb->interface_index);
- }
+ /* Attempt to establish a connection with the printer. */
+ if (setup_usb_connection(params->usb_sock, params))
+ goto cleanup;
+
+ /* Condition variable used to broadcast updates to the printer thread. */
+ pthread_cond_t cond;
+ if (pthread_cond_init(&cond, NULL))
+ goto cleanup;
+ params->cond = &cond;
+
+ /* Copy the contents of |params| into |printer_params|. The only
+ differences between the two are the |thread_num| and |thread_handle|. */
+ struct service_thread_param *printer_params =
+ calloc(1, sizeof(*printer_params));
+ memcpy(printer_params, params, sizeof(*printer_params));
+ printer_params->thread_num += 1;
+
+ /* Attempt to start the printer's end of the communication. */
+ if (setup_communication_thread(&service_printer_connection, printer_params))
+ goto cleanup;
+
+ pthread_t printer_params_thread_handle = printer_params->thread_handle;
+
+ /* This function will run until the socket has been closed. When this function
+ returns it means that the communication has been completed. */
+ service_socket_connection(params);
+
+ /* Notify the printer's end that the socket has closed so that it does not
+ have to wait for any pending asynchronous transfers to complete. */
+ pthread_cond_broadcast(params->cond);
+
+ /* Wait for the printer thread to exit. */
+ NOTE("Thread #%u: Waiting for thread #%u to complete", thread_num,
+ thread_num + 1);
+ if (pthread_join(printer_params_thread_handle, NULL))
+ ERR("Thread #%u: Something went wrong trying to join the printer thread",
+ thread_num);
+
+cleanup:
+ if (params->usb_conn != NULL) {
+ NOTE("Thread #%u: interface #%u: releasing usb conn", thread_num,
+ params->usb_conn->interface_index);
+ usb_conn_release(params->usb_conn);
+ params->usb_conn = NULL;
+ }
- if (g_options.terminate)
- goto cleanup_subconn;
-
- NOTE("Thread #%d: M %p P %p: Pkt from tcp (buffer size: %d)\n===\n%s===",
- thread_num, client_msg, pkt,
- pkt->filled_size,
- hexdump(pkt->buffer, (int)pkt->filled_size));
- /* In no-printer mode we simply ignore passing the
- client message on to the printer */
- if (arg->usb_sock != NULL) {
- if (usb_conn_packet_send(usb, pkt) != 0) {
- ERR("Thread #%d: M %p P %p: Interface #%d: Unable to send client package via USB",
- thread_num,
- client_msg, pkt, usb->interface_index);
- packet_free(pkt);
- goto cleanup_subconn;
- }
- NOTE("Thread #%d: M %p P %p: Interface #%d: Client pkt done",
- thread_num,
- client_msg, pkt, usb->interface_index);
- }
- packet_free(pkt);
- }
- if (usb != NULL)
- NOTE("Thread #%d: M %p: Interface #%d: Client msg completed",
- thread_num, client_msg,
- usb->interface_index);
- else
- NOTE("Thread #%d: M %p: Client msg completed",
- thread_num, client_msg);
- message_free(client_msg);
- client_msg = NULL;
-
- if (g_options.terminate)
- goto cleanup_subconn;
-
-
- /* Server's response */
- server_msg = http_message_new();
- if (server_msg == NULL) {
- ERR("Thread #%d: Failed to create server message",
- thread_num);
- goto cleanup_subconn;
+ NOTE("Thread #%u: closing, %s", thread_num,
+ g_options.terminate ? "shutdown requested"
+ : "communication thread terminated");
+ tcp_conn_close(params->tcp);
+
+ /* Execute clean-up handler. */
+ pthread_cleanup_pop(1);
+ pthread_exit(NULL);
+}
+
+/* Reads from the socket and writes data to the printer. */
+static void service_socket_connection(struct service_thread_param *params)
+{
+ uint32_t thread_num = params->thread_num;
+
+ while (is_socket_open(params) && !g_options.terminate) {
+ int result = poll_tcp_socket(params->tcp);
+ if (result < 0 || !is_socket_open(params)) {
+ NOTE("Thread #%u: Client closed connection", thread_num);
+ return;
+ } else if (result == 0) {
+ continue;
}
- if (usb != NULL)
- NOTE("Thread #%d: M %p: Interface #%d: Server msg starting",
- thread_num, server_msg,
- usb->interface_index);
- else
- NOTE("Thread #%d: M %p: Server msg starting",
- thread_num, server_msg);
- while (!server_msg->is_completed && !g_options.terminate) {
- struct http_packet_t *pkt;
- if (arg->usb_sock != NULL) {
- pkt = usb_conn_packet_get(usb, server_msg);
- if (pkt == NULL) {
- usb_failed = 1;
- goto cleanup_subconn;
- }
- } else {
- /* In no-printer mode we "invent" the answer
- of the printer, a simple HTML message as
- a pseudo web interface */
- pkt = packet_new(server_msg);
- snprintf((char*)(pkt->buffer),
- pkt->buffer_capacity - 1,
- "HTTP/1.1 200 OK\r\nContent-Type: text/html; name=ippusbxd.html; charset=UTF-8\r\n\r\n<html><h2>ippusbxd</h2><p>Debug/development mode without connection to IPP-over-USB printer</p></html>\r\n");
- pkt->filled_size = 183;
- /* End the TCP connection, so that a
- web browser does not wait for more data */
- server_msg->is_completed = 1;
- arg->tcp->is_closed = 1;
- }
- if (g_options.terminate)
- goto cleanup_subconn;
-
- NOTE("Thread #%d: M %p P %p: Pkt from usb (buffer size: %d)\n===\n%s===",
- thread_num, server_msg, pkt, pkt->filled_size,
- hexdump(pkt->buffer, (int)pkt->filled_size));
- if (tcp_packet_send(arg->tcp, pkt) != 0) {
- ERR("Thread #%d: M %p P %p: Unable to send client package via TCP",
- thread_num,
- client_msg, pkt);
- packet_free(pkt);
- goto cleanup_subconn;
- }
- if (usb != NULL)
- NOTE("Thread #%d: M %p P %p: Interface #%d: Server pkt done",
- thread_num, server_msg, pkt,
- usb->interface_index);
- else
- NOTE("Thread #%d: M %p P %p: Server pkt done",
- thread_num, server_msg, pkt);
- packet_free(pkt);
+ struct http_packet_t *pkt = tcp_packet_get(params->tcp);
+ if (pkt == NULL) {
+ NOTE("Thread #%u: There was an error reading from the socket",
+ thread_num);
+ return;
}
- if (usb != NULL)
- NOTE("Thread #%d: M %p: Interface #%d: Server msg completed",
- thread_num, server_msg,
- usb->interface_index);
- else
- NOTE("Thread #%d: M %p: Server msg completed",
- thread_num, server_msg);
-
- cleanup_subconn:
- if (usb != NULL && (arg->tcp->is_closed || usb_failed == 1)) {
- NOTE("Thread #%d: M %p: Interface #%d: releasing usb conn",
- thread_num, server_msg, usb->interface_index);
- usb_conn_release(usb);
- usb = NULL;
+
+ if (!is_socket_open(params)) {
+ NOTE("Thread #%u: Client closed connection", thread_num);
+ return;
}
- if (client_msg != NULL)
- message_free(client_msg);
- if (server_msg != NULL)
- message_free(server_msg);
+
+ NOTE("Thread #%u: Pkt from tcp (buffer size: %zu)\n===\n%s===", thread_num,
+ pkt->filled_size, hexdump(pkt->buffer, (int)pkt->filled_size));
+
+ /* Send pkt to printer. */
+ usb_conn_packet_send(params->usb_conn, pkt);
+
+ packet_free(pkt);
}
+}
- NOTE("Thread #%d: Closing, %s", thread_num,
- g_options.terminate ? "shutdown requested" : "communication thread terminated");
- tcp_conn_close(arg->tcp);
- free(arg);
+/* Returns the value of |read_inflight|. Uses a mutex since another thread which
+ is processing the asynchronous transfer may change the value once the
+ transfer is complete. */
+static int get_read_inflight(const int *read_inflight, pthread_mutex_t *mtx)
+{
+ pthread_mutex_lock(mtx);
+ int val = *read_inflight;
+ pthread_mutex_unlock(mtx);
- /* Execute clean-up handler */
- pthread_cleanup_pop(1);
+ return val;
+}
- pthread_exit(NULL);
+/* Sets the value of |read_inflight| to |val|. Uses a mutex since another thread
+ which is processing the asynchronous transfer may change the value once the
+ transfer is complete. */
+static void set_read_inflight(int val, pthread_mutex_t *mtx, int *read_inflight)
+{
+ pthread_mutex_lock(mtx);
+ *read_inflight = val;
+ pthread_mutex_unlock(mtx);
}
-static void start_daemon()
+/* Reads from the printer and writes to the socket. */
+static void *service_printer_connection(void *params_void)
{
- /* Capture USB device if not in no-printer mode */
- struct usb_sock_t *usb_sock;
+ struct service_thread_param *params =
+ (struct service_thread_param *)params_void;
+ uint32_t thread_num = params->thread_num;
- /* Termination flag */
- g_options.terminate = 0;
+ /* Register clean-up handler. */
+ pthread_cleanup_push(cleanup_handler, &thread_num);
- if (g_options.noprinter_mode == 0) {
- usb_sock = usb_open();
- if (usb_sock == NULL)
- goto cleanup_usb;
- } else {
- usb_sock = NULL;
- g_options.device_id = "MFG:Acme;MDL:LaserStar 2000;CMD:AppleRaster,PWGRaster;CLS:PRINTER;DES:Acme LaserStar 2000;SN:001;";
+ int read_inflight = 0;
+ pthread_mutex_t read_inflight_mutex;
+ if (pthread_mutex_init(&read_inflight_mutex, NULL))
+ goto cleanup;
+
+ struct libusb_transfer *read_transfer = NULL;
+
+ while (is_socket_open(params) && !g_options.terminate) {
+ /* If there is already a read from the printer underway, block until it has
+ completed. */
+ pthread_mutex_lock(&read_inflight_mutex);
+ while (is_socket_open(params) && read_inflight)
+ pthread_cond_wait(params->cond, &read_inflight_mutex);
+ pthread_mutex_unlock(&read_inflight_mutex);
+
+ /* After waking up due to a completed transfer, verify that the socket is
+ still open and that the termination flag has not been set before
+ attempting to start another transfer. */
+ if (!is_socket_open(params) || g_options.terminate)
+ break;
+
+ struct http_packet_t *pkt = packet_new();
+ if (pkt == NULL) {
+ ERR("Thread #%u: Failed to allocate packet", thread_num);
+ break;
+ }
+
+ struct libusb_callback_data *user_data = setup_libusb_callback_data(
+ pkt, &read_inflight, params, &read_inflight_mutex);
+
+ if (user_data == NULL) {
+ ERR("Thread #%u: Failed to allocate memory for libusb_callback_data",
+ thread_num);
+ break;
+ }
+
+ read_transfer = setup_async_read(
+ params->usb_conn, pkt, read_transfer_callback, (void *)user_data, 5000);
+
+ if (read_transfer == NULL) {
+ ERR("Thread #%u: Failed to allocate memory for libusb transfer",
+ thread_num);
+ break;
+ }
+
+ /* Mark that there is a new read in flight. A mutex should not be needed
+ here since the transfer callback won't be fired until after calling
+ libusb_submit_transfer() */
+ read_inflight = 1;
+
+ if (libusb_submit_transfer(read_transfer)) {
+ ERR("Thread #%u: Failed to submit asynchronous USB transfer", thread_num);
+ set_read_inflight(0, &read_inflight_mutex, &read_inflight);
+ break;
+ }
}
- /* Capture a socket */
+ /* If the socket used for communication has closed and there is still a
+ transfer from the printer in flight then we attempt to cancel it. */
+ if (get_read_inflight(&read_inflight, &read_inflight_mutex)) {
+ NOTE(
+ "Thread #%u: There was a read in flight when the connection was "
+ "closed, cancelling transfer", thread_num);
+ int cancel_status = libusb_cancel_transfer(read_transfer);
+ if (!cancel_status) {
+ /* Wait until the cancellation has completed. */
+ NOTE("Thread #%u: Waiting until the transfer has been cancelled",
+ thread_num);
+ pthread_mutex_lock(&read_inflight_mutex);
+ while (read_inflight)
+ pthread_cond_wait(params->cond, &read_inflight_mutex);
+ pthread_mutex_unlock(&read_inflight_mutex);
+ } else if (cancel_status == LIBUSB_ERROR_NOT_FOUND) {
+ NOTE("Thread #%u: The transfer has already completed", thread_num);
+ } else {
+ NOTE("Thread #%u: Failed to cancel transfer");
+ g_options.terminate = 1;
+ }
+ }
+
+ pthread_mutex_destroy(&read_inflight_mutex);
+
+cleanup:
+ /* Execute clean-up handler. */
+ pthread_cleanup_pop(1);
+ pthread_exit(NULL);
+}
+
+static uint16_t open_tcp_socket(void)
+{
uint16_t desired_port = g_options.desired_port;
g_options.tcp_socket = NULL;
g_options.tcp6_socket = NULL;
+
for (;;) {
g_options.tcp_socket = tcp_open(desired_port, g_options.interface);
g_options.tcp6_socket = tcp6_open(desired_port, g_options.interface);
- if (g_options.tcp_socket || g_options.tcp6_socket || g_options.only_desired_port)
+ if (g_options.tcp_socket || g_options.tcp6_socket ||
+ g_options.only_desired_port)
break;
- /* Search for a free port */
+ /* Search for a free port. */
desired_port ++;
- /* We failed with 0 as port number or we reached the max
- port number */
+ /* We failed with 0 as port number or we reached the max port number. */
if (desired_port == 1 || desired_port == 0)
- /* IANA recommendation of 49152 to 65535 for ephemeral
- ports
- https://en.wikipedia.org/wiki/Ephemeral_port */
+ /* IANA recommendation of 49152 to 65535 for ephemeral ports. */
desired_port = 49152;
- NOTE("Access to desired port failed, trying alternative port %d", desired_port);
+ NOTE("Access to desired port failed, trying alternative port %d",
+ desired_port);
+ }
+
+ return desired_port;
+}
+
+/* Attempts to allocate space for a tcp socket. If the allocation is
+ successful then a value of 0 is returned, otherwise a non-zero value is
+ returned. */
+static int allocate_socket_connection(struct service_thread_param *param)
+{
+ param->tcp = calloc(1, sizeof(*param->tcp));
+
+ if (param->tcp == NULL) {
+ ERR("Preparing thread #%u: Failed to allocate space for cups connection",
+ param->thread_num);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Attempts to setup a connection for to a tcp socket. Returns a 0 value on
+ success and a non-zero value if something went wrong attempting to establish
+ the connection. */
+static int setup_socket_connection(struct service_thread_param *param)
+{
+ param->tcp = tcp_conn_select(g_options.tcp_socket, g_options.tcp6_socket);
+ if (g_options.terminate || param->tcp == NULL)
+ return -1;
+ return 0;
+}
+
+/* Attempt to create a new usb_conn_t and assign it to |param| by acquiring an
+ available usb interface. Returns 0 if the creating on the connection struct
+ was successful, and non-zero if there was an error attempting to acquire the
+ interface. */
+static int setup_usb_connection(struct usb_sock_t *usb_sock,
+ struct service_thread_param *param)
+{
+ param->usb_conn = usb_conn_acquire(usb_sock);
+ if (param->usb_conn == NULL) {
+ ERR("Thread #%u: Failed to acquire usb interface", param->thread_num);
+ return -1;
}
+
+ return 0;
+}
+
+/* Attempt to register a new communication thread to execute the function
+ |routine| with the given |params|. If successful a 0 value is returned,
+ otherwise a non-zero value is returned. */
+static int setup_communication_thread(void *(*routine)(void *),
+ struct service_thread_param *param)
+{
+ pthread_mutex_lock(&thread_register_mutex);
+ register_service_thread(&num_service_threads, &service_threads, param);
+ list_service_threads(num_service_threads, service_threads);
+ pthread_mutex_unlock(&thread_register_mutex);
+
+ int status =
+ pthread_create(&param->thread_handle, NULL, routine, param);
+
+ if (status) {
+ ERR("Creating thread #%u: Failed to spawn thread, error %d",
+ param->thread_num, status);
+ pthread_mutex_lock(&thread_register_mutex);
+ unregister_service_thread(&num_service_threads, &service_threads,
+ param->thread_num);
+ list_service_threads(num_service_threads, service_threads);
+ pthread_mutex_unlock(&thread_register_mutex);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct libusb_callback_data *setup_libusb_callback_data(
+ struct http_packet_t *pkt, int *read_inflight,
+ struct service_thread_param *thread_param,
+ pthread_mutex_t *read_inflight_mutex) {
+ struct libusb_callback_data *data = calloc(1, sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->pkt = pkt;
+ data->read_inflight = read_inflight;
+ data->thread_num = thread_param->thread_num;
+ data->read_inflight_mutex = read_inflight_mutex;
+ data->read_inflight_cond = thread_param->cond;
+ data->tcp = thread_param->tcp;
+
+ return data;
+}
+
+static int is_socket_open(const struct service_thread_param *param) {
+ return !param->tcp->is_closed;
+}
+
+static void start_daemon()
+{
+ /* Capture USB device. */
+ struct usb_sock_t *usb_sock;
+
+ /* Termination flag */
+ g_options.terminate = 0;
+
+ usb_sock = usb_open();
+ if (usb_sock == NULL) goto cleanup_usb;
+
+ /* Capture a socket */
+ uint16_t desired_port = open_tcp_socket();
if (g_options.tcp_socket == NULL && g_options.tcp6_socket == NULL)
goto cleanup_tcp;
@@ -419,45 +686,32 @@ static void start_daemon()
}
/* Main loop */
- int i = 0;
+ uint32_t i = 1;
pthread_mutex_init(&thread_register_mutex, NULL);
while (!g_options.terminate) {
- i ++;
struct service_thread_param *args = calloc(1, sizeof(*args));
if (args == NULL) {
- ERR("Preparing thread #%d: Failed to alloc space for thread args",
- i);
+ ERR("Preparing thread #%u: Failed to alloc space for thread args", i);
goto cleanup_thread;
}
args->thread_num = i;
args->usb_sock = usb_sock;
- /* For each request/response round we use the socket (IPv4 or
- IPv6) which receives data first */
- args->tcp = tcp_conn_select(g_options.tcp_socket, g_options.tcp6_socket);
- if (g_options.terminate)
+ /* Allocate space for a tcp socket to be used for communication. */
+ if (allocate_socket_connection(args))
goto cleanup_thread;
- if (args->tcp == NULL) {
- ERR("Preparing thread #%d: Failed to open tcp connection", i);
+
+ /* Attempt to establish a connection to the relevant socket. */
+ if (setup_socket_connection(args))
goto cleanup_thread;
- }
- pthread_mutex_lock(&thread_register_mutex);
- register_service_thread(&num_service_threads, &service_threads, args);
- list_service_threads(num_service_threads, service_threads);
- pthread_mutex_unlock(&thread_register_mutex);
- int status = pthread_create(&args->thread_handle, NULL,
- &service_connection, args);
- if (status) {
- ERR("Creating thread #%d: Failed to spawn thread, error %d",
- i, status);
- pthread_mutex_lock(&thread_register_mutex);
- unregister_service_thread(&num_service_threads, &service_threads, i);
- list_service_threads(num_service_threads, service_threads);
- pthread_mutex_unlock(&thread_register_mutex);
+ /* Attempt to start up a new thread to handle the socket's end of
+ communication. */
+ if (setup_communication_thread(&service_connection, args))
goto cleanup_thread;
- }
+
+ i += 2;
continue;
@@ -479,7 +733,7 @@ static void start_daemon()
stopping ippusbxd, so that no USB communication with the printer can
happen after the final reset */
while (num_service_threads) {
- NOTE("Thread #%d did not terminate, canceling it now ...",
+ NOTE("Thread #%u did not terminate, canceling it now ...",
service_threads[0]->thread_num);
i = num_service_threads;
pthread_cancel(service_threads[0]->thread_handle);
@@ -488,6 +742,7 @@ static void start_daemon()
}
/* Wait for USB unplug event observer thread to terminate */
+ NOTE("Shutting down usb observer thread");
pthread_join(g_options.usb_event_thread_handle, NULL);
/* TCP clean-up */
@@ -538,7 +793,6 @@ int main(int argc, char *argv[])
{"verbose", no_argument, 0, 'q' },
{"no-fork", no_argument, 0, 'n' },
{"no-broadcast", no_argument, 0, 'B' },
- {"no-printer", no_argument, 0, 'N' },
{"help", no_argument, 0, 'h' },
{NULL, 0, 0, 0 }
};
@@ -552,7 +806,7 @@ int main(int argc, char *argv[])
g_options.bus = 0;
g_options.device = 0;
- while ((c = getopt_long(argc, argv, "qnhdp:P:i:s:lv:m:NB",
+ while ((c = getopt_long(argc, argv, "qnhdp:P:i:s:lv:m:B",
long_options, &option_index)) != -1) {
switch (c) {
case '?':
@@ -625,9 +879,6 @@ int main(int argc, char *argv[])
case 's':
g_options.serial_num = (unsigned char *)optarg;
break;
- case 'N':
- g_options.noprinter_mode = 1;
- break;
case 'B':
g_options.nobroadcast = 1;
break;
@@ -673,13 +924,11 @@ int main(int argc, char *argv[])
" -n No-fork mode\n"
" --no-broadcast\n"
" -B No-broadcast mode, do not DNS-SD-broadcast\n"
- " --no-printer\n"
- " -N No-printer mode, debug/developer mode which makes ippusbxd\n"
- " run without IPP-over-USB printer\n"
, argv[0], argv[0], argv[0]);
return 0;
}
start_daemon();
+ NOTE("ippusbxd completed successfully");
return 0;
}
diff --git a/src/options.h b/src/options.h
index 2de31a2..e167514 100644
--- a/src/options.h
+++ b/src/options.h
@@ -35,7 +35,6 @@ struct options {
int help_mode;
int verbose_mode;
int nofork_mode;
- int noprinter_mode;
int nobroadcast;
/* Printer identity */
diff --git a/src/tcp.c b/src/tcp.c
index 5e11884..92a128c 100644
--- a/src/tcp.c
+++ b/src/tcp.c
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <pthread.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
@@ -30,11 +31,11 @@
#include <unistd.h>
#include <errno.h>
-#include "options.h"
+#include "http.h"
#include "logging.h"
+#include "options.h"
#include "tcp.h"
-
struct tcp_sock_t *tcp_open(uint16_t port, char* interface)
{
struct tcp_sock_t *this = calloc(1, sizeof *this);
@@ -221,56 +222,37 @@ uint16_t tcp_port_number_get(struct tcp_sock_t *sock)
return 0;
}
-struct http_packet_t *tcp_packet_get(struct tcp_conn_t *tcp,
- struct http_message_t *msg)
+struct http_packet_t *tcp_packet_get(struct tcp_conn_t *tcp)
{
- /* Alloc packet ==---------------------------------------------------== */
- struct http_packet_t *pkt = packet_new(msg);
+ /* Allocate packet for incoming message. */
+ struct http_packet_t *pkt = packet_new();
if (pkt == NULL) {
ERR("failed to create packet for incoming tcp message");
goto error;
}
- size_t want_size = packet_pending_bytes(pkt);
- if (want_size == 0) {
- NOTE("TCP: Got %lu from spare buffer", pkt->filled_size);
- return pkt;
- }
-
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
- setsockopt(tcp->sd, SOL_SOCKET, SO_RCVTIMEO,
- (char *)&tv, sizeof(struct timeval));
-
- while (want_size != 0 && !msg->is_completed && !g_options.terminate) {
- NOTE("TCP: Getting %d bytes", want_size);
- uint8_t *subbuffer = pkt->buffer + pkt->filled_size;
- ssize_t gotten_size = recv(tcp->sd, subbuffer, want_size, 0);
- if (gotten_size < 0) {
- int errno_saved = errno;
- ERR("recv failed with err %d:%s", errno_saved,
- strerror(errno_saved));
- tcp->is_closed = 1;
- goto error;
- }
- NOTE("TCP: Got %d bytes", gotten_size);
- if (gotten_size == 0) {
- tcp->is_closed = 1;
- if (pkt->filled_size == 0) {
- /* Client closed TCP conn */
- goto error;
- } else {
- break;
- }
- }
+ if (setsockopt(tcp->sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
+ ERR("TCP: Setting options for tcp connection socket failed");
+ goto error;
+ }
+
+ ssize_t gotten_size = recv(tcp->sd, pkt->buffer, pkt->buffer_capacity, 0);
+
+ if (gotten_size < 0) {
+ int errno_saved = errno;
+ ERR("recv failed with err %d:%s", errno_saved, strerror(errno_saved));
+ tcp->is_closed = 1;
+ goto error;
+ }
- packet_mark_received(pkt, (unsigned) gotten_size);
- want_size = packet_pending_bytes(pkt);
- NOTE("TCP: Want more %d bytes; Message %scompleted", want_size, msg->is_completed ? "" : "not ");
+ if (gotten_size == 0) {
+ tcp->is_closed = 1;
}
- NOTE("TCP: Received %lu bytes", pkt->filled_size);
+ pkt->filled_size = gotten_size;
return pkt;
error:
@@ -283,9 +265,10 @@ int tcp_packet_send(struct tcp_conn_t *conn, struct http_packet_t *pkt)
{
size_t remaining = pkt->filled_size;
size_t total = 0;
+
while (remaining > 0 && !g_options.terminate) {
- ssize_t sent = send(conn->sd, pkt->buffer + total,
- remaining, MSG_NOSIGNAL);
+ ssize_t sent = send(conn->sd, pkt->buffer + total, remaining, MSG_NOSIGNAL);
+
if (sent < 0) {
if (errno == EPIPE) {
conn->is_closed = 1;
@@ -295,13 +278,14 @@ int tcp_packet_send(struct tcp_conn_t *conn, struct http_packet_t *pkt)
return -1;
}
- size_t sent_ulong = (unsigned) sent;
- total += sent_ulong;
- if (sent_ulong >= remaining)
+ size_t sent_unsigned = (size_t)sent;
+ total += sent_unsigned;
+ if (sent_unsigned >= remaining)
remaining = 0;
else
- remaining -= sent_ulong;
+ remaining -= sent_unsigned;
}
+
NOTE("TCP: sent %lu bytes", total);
return 0;
}
@@ -315,6 +299,7 @@ struct tcp_conn_t *tcp_conn_select(struct tcp_sock_t *sock,
ERR("Calloc for connection struct failed");
goto error;
}
+
fd_set rfds;
int retval = 0;
int nfds = 0;
@@ -354,6 +339,11 @@ struct tcp_conn_t *tcp_conn_select(struct tcp_sock_t *sock,
ERR("accept failed");
goto error;
}
+
+ /* Attempt to initialize the connection's mutex. */
+ if (pthread_mutex_init(&conn->mutex, NULL))
+ goto error;
+
return conn;
error:
@@ -371,5 +361,56 @@ void tcp_conn_close(struct tcp_conn_t *conn)
shutdown(conn->sd, SHUT_RDWR);
close(conn->sd);
+ pthread_mutex_destroy(&conn->mutex);
free(conn);
}
+
+/* Poll the tcp socket to determine if it is ready to transmit data. */
+int poll_tcp_socket(struct tcp_conn_t *tcp)
+{
+ struct pollfd poll_fd;
+ poll_fd.fd = tcp->sd;
+ poll_fd.events = POLLIN;
+ const int nfds = 1;
+ const int timeout = 5000; /* 5 seconds. */
+
+ int result = poll(&poll_fd, nfds, timeout);
+ if (result < 0) {
+ ERR("poll failed with error %d:%s", errno, strerror(errno));
+ tcp->is_closed = 1;
+ } else if (result == 0) {
+ /* In the case where the poll timed out, check to see whether or not data
+ * has recently been sent from the printer along the socket. If so, then we
+ * keep the connection alive an reset the is_active flag. Otherwise, close
+ * the connection. */
+ if (get_is_active(tcp)) {
+ set_is_active(tcp, 0);
+ } else {
+ tcp->is_closed = 1;
+ }
+ } else {
+ if (poll_fd.revents != POLLIN) {
+ ERR("poll returned an unexpected event");
+ tcp->is_closed = 1;
+ return -1;
+ }
+ }
+
+ return result;
+}
+
+int get_is_active(struct tcp_conn_t *tcp)
+{
+ pthread_mutex_lock(&tcp->mutex);
+ int val = tcp->is_active;
+ pthread_mutex_unlock(&tcp->mutex);
+
+ return val;
+}
+
+void set_is_active(struct tcp_conn_t *tcp, int val)
+{
+ pthread_mutex_lock(&tcp->mutex);
+ tcp->is_active = val;
+ pthread_mutex_unlock(&tcp->mutex);
+}
diff --git a/src/tcp.h b/src/tcp.h
index e58cb17..8231119 100644
--- a/src/tcp.h
+++ b/src/tcp.h
@@ -15,6 +15,8 @@
#pragma once
#include <stdint.h>
+#include <pthread.h>
+
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -37,6 +39,8 @@ struct tcp_sock_t {
struct tcp_conn_t {
int sd;
int is_closed;
+ int is_active;
+ pthread_mutex_t mutex;
};
struct tcp_sock_t *tcp_open(uint16_t, char* interface);
@@ -48,6 +52,10 @@ struct tcp_conn_t *tcp_conn_select(struct tcp_sock_t *sock,
struct tcp_sock_t *sock6);
void tcp_conn_close(struct tcp_conn_t *);
-struct http_packet_t *tcp_packet_get(struct tcp_conn_t *,
- struct http_message_t *);
+struct http_packet_t *tcp_packet_get(struct tcp_conn_t *);
int tcp_packet_send(struct tcp_conn_t *, struct http_packet_t *);
+
+int poll_tcp_socket(struct tcp_conn_t *tcp);
+
+int get_is_active(struct tcp_conn_t *tcp);
+void set_is_active(struct tcp_conn_t *tcp, int val);
diff --git a/src/usb.c b/src/usb.c
index 51fdc68..fa8ce59 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -168,6 +168,43 @@ int get_device_id(struct libusb_device_handle *handle,
return (0);
}
+static void try_detach_kernel_driver(struct usb_sock_t *usb,
+ struct usb_interface *uf) {
+ /* Make kernel release interface */
+ if (libusb_kernel_driver_active(usb->printer, uf->libusb_interface_index) ==
+ 1) {
+ /* Only linux supports this other platforms will fail thus we ignore the
+ error code it either works or it does not */
+ libusb_detach_kernel_driver(usb->printer, uf->libusb_interface_index);
+ }
+}
+
+static int try_claim_usb_interface(struct usb_sock_t *usb,
+ struct usb_interface *uf) {
+ /* Claim the whole interface */
+ int status = 0;
+ do {
+ /* Spinlock-like Libusb does not offer a blocking call so we're left with a
+ spinlock. */
+ status = libusb_claim_interface(usb->printer, uf->libusb_interface_index);
+ if (status)
+ NOTE("Failed to claim interface %d, retrying",
+ uf->libusb_interface_index);
+ switch (status) {
+ case LIBUSB_ERROR_NOT_FOUND:
+ ERR("USB Interface did not exist");
+ return -1;
+ case LIBUSB_ERROR_NO_DEVICE:
+ ERR("Printer was removed");
+ return -1;
+ default:
+ break;
+ }
+ } while (status != 0 && !g_options.terminate);
+
+ return 0;
+}
+
struct usb_sock_t *usb_open()
{
int status_lock;
@@ -372,6 +409,23 @@ struct usb_sock_t *usb_open()
goto error;
}
+ /* Try to make the kernel release the usb interface. */
+ try_detach_kernel_driver(usb, uf);
+
+ /* Try to claim the usb interface. */
+ if (try_claim_usb_interface(usb, uf)) {
+ ERR("Failed to claim usb interface #%d", uf->interface_number);
+ goto error;
+ }
+
+ /* Select the IPP-USB alt setting of the interface. */
+ if (libusb_set_interface_alt_setting(
+ usb->printer, uf->libusb_interface_index, uf->interface_alt)) {
+ ERR("Failed to set alt setting for interface #%d",
+ uf->interface_number);
+ goto error;
+ }
+
break;
}
}
@@ -435,7 +489,10 @@ void usb_close(struct usb_sock_t *usb)
NOTE("Resetting printer ...");
libusb_reset_device(usb->printer);
NOTE("Reset completed.");
+ NOTE("Closing device handle...");
libusb_close(usb->printer);
+ NOTE("Closed device handle.");
+
if (usb != NULL) {
if (usb->context != NULL)
libusb_exit(usb->context);
@@ -552,55 +609,6 @@ void usb_register_callback(struct usb_sock_t *usb)
ERR("Failed to register unplug callback");
}
-static void usb_conn_mark_staled(struct usb_conn_t *conn)
-{
- if (conn->is_staled)
- return;
-
- struct usb_sock_t *usb = conn->parent;
-
- sem_wait(&usb->num_staled_lock);
- {
- usb->num_staled++;
- }
- sem_post(&usb->num_staled_lock);
-
- conn->is_staled = 1;
-}
-
-static void usb_conn_mark_moving(struct usb_conn_t *conn)
-{
- if (!conn->is_staled)
- return;
-
- struct usb_sock_t *usb = conn->parent;
-
- sem_wait(&usb->num_staled_lock);
- {
- usb->num_staled--;
- }
- sem_post(&usb->num_staled_lock);
-
- conn->is_staled = 0;
-}
-
-static int usb_all_conns_staled(struct usb_sock_t *usb)
-{
- int staled;
-
- sem_wait(&usb->num_staled_lock);
- {
- sem_wait(&usb->pool_manage_lock);
- {
- staled = usb->num_staled == usb->num_taken;
- }
- sem_post(&usb->pool_manage_lock);
- }
- sem_post(&usb->num_staled_lock);
-
- return staled;
-}
-
struct usb_conn_t *usb_conn_acquire(struct usb_sock_t *usb)
{
int i;
@@ -616,7 +624,6 @@ struct usb_conn_t *usb_conn_acquire(struct usb_sock_t *usb)
ERR("Timed out waiting for a free USB interface");
return NULL;
}
- usleep(100000);
}
struct usb_conn_t *conn = calloc(1, sizeof(*conn));
@@ -643,45 +650,6 @@ struct usb_conn_t *usb_conn_acquire(struct usb_sock_t *usb)
goto acquire_error;
}
- /* Make kernel release interface */
- if (libusb_kernel_driver_active(usb->printer,
- uf->libusb_interface_index) == 1) {
- /* Only linux supports this
- other platforms will fail
- thus we ignore the error code
- it either works or it does not */
- libusb_detach_kernel_driver(usb->printer,
- uf->libusb_interface_index);
- }
-
- /* Claim the whole interface */
- int status = 0;
- do {
- /* Spinlock-like
- Libusb does not offer a blocking call
- so we're left with a spinlock */
- status = libusb_claim_interface(usb->printer, uf->libusb_interface_index);
- if (status) NOTE("Failed to claim interface %d, retrying", conn->interface_index);
- switch (status) {
- case LIBUSB_ERROR_NOT_FOUND:
- ERR("USB Interface did not exist");
- goto acquire_error;
- case LIBUSB_ERROR_NO_DEVICE:
- ERR("Printer was removed");
- goto acquire_error;
- default:
- break;
- }
- } while (status != 0 && !g_options.terminate);
-
- if (g_options.terminate)
- goto acquire_error;
-
- /* Select the IPP-USB alt setting of the interface */
- libusb_set_interface_alt_setting(usb->printer,
- uf->libusb_interface_index,
- uf->interface_alt);
-
/* Take successfully acquired interface from the pool */
usb->num_taken++;
usb->num_avail--;
@@ -701,16 +669,6 @@ void usb_conn_release(struct usb_conn_t *conn)
struct usb_sock_t *usb = conn->parent;
sem_wait(&usb->pool_manage_lock);
{
- int status = 0;
- do {
- /* Spinlock-like
- libusb does not offer a blocking call
- so we're left with a spinlock */
- status = libusb_release_interface(usb->printer,
- conn->interface->libusb_interface_index);
- if (status) NOTE("Failed to release interface %d, retrying", conn->interface_index);
- } while (status != 0 && !g_options.terminate);
-
/* Return usb interface to pool */
usb->num_taken--;
usb->num_avail++;
@@ -719,7 +677,6 @@ void usb_conn_release(struct usb_conn_t *conn)
/* Release our interface lock */
sem_post(&conn->interface->lock);
-
free(conn);
}
sem_post(&usb->pool_manage_lock);
@@ -780,148 +737,18 @@ int usb_conn_packet_send(struct usb_conn_t *conn, struct http_packet_t *pkt)
return 0;
}
-struct http_packet_t *usb_conn_packet_get(struct usb_conn_t *conn, struct http_message_t *msg)
+struct libusb_transfer *setup_async_read(struct usb_conn_t *conn,
+ struct http_packet_t *pkt,
+ libusb_transfer_cb_fn callback,
+ void *user_data, uint32_t timeout)
{
- if (msg->is_completed)
+ struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+ if (transfer == NULL)
return NULL;
- struct http_packet_t *pkt = packet_new(msg);
- if (pkt == NULL) {
- ERR("failed to create packet for incoming usb message");
- goto cleanup;
- }
-
- /* File packet */
- const int timeout = 1000; /* 1 sec */
- size_t read_size_ulong = packet_pending_bytes(pkt);
- if (read_size_ulong == 0)
- return pkt;
-
- uint64_t times_staled = 0;
- while (read_size_ulong > 0 && !msg->is_completed && !g_options.terminate) {
- if (read_size_ulong >= INT_MAX)
- goto cleanup;
- int read_size = (int)read_size_ulong;
-
- /* Pad read_size to multiple of usb's max packet size */
- read_size += (512 - (read_size % 512)) % 512;
-
- /* Expand buffer if needed */
- if (pkt->buffer_capacity < pkt->filled_size + read_size_ulong)
- if (packet_expand(pkt) < 0) {
- ERR("Failed to ensure room for usb pkt");
- goto cleanup;
- }
+ libusb_fill_bulk_transfer(transfer, conn->parent->printer,
+ conn->interface->endpoint_in, pkt->buffer,
+ pkt->buffer_capacity, callback, user_data, timeout);
- int gotten_size = 0;
- int status = libusb_bulk_transfer(conn->parent->printer,
- conn->interface->endpoint_in,
- pkt->buffer + pkt->filled_size,
- read_size,
- &gotten_size, timeout);
-
- if (status == LIBUSB_ERROR_NO_DEVICE) {
- ERR("Printer has been disconnected");
- goto cleanup;
- }
-
- if (status != 0 && status != LIBUSB_ERROR_TIMEOUT) {
- ERR("bulk xfer failed with error code %d", status);
- ERR("tried reading %d bytes", read_size);
- goto cleanup;
- } else if (status == LIBUSB_ERROR_TIMEOUT) {
- ERR("bulk xfer timed out, retrying ...");
- ERR("tried reading %d bytes, actually read %d bytes",
- read_size, gotten_size);
- }
-
- if (gotten_size < 0) {
- ERR("Negative read size unexpected");
- goto cleanup;
- }
-
- if (gotten_size > 0) {
- times_staled = 0;
- usb_conn_mark_moving(conn);
- } else {
-
- /* Performance Test ---------------
- How long we sleep here has a
- dramatic affect on how long it
- takes to load a page.
- Earlier versions waited a tenth
- of a second which resulted in
- minute long page loads.
- On my HP printer the most obvious
- bottleneck is the "Unified.js" file
- which weighs 517.87KB. My profiling
- looked at how shortening this sleep
- could improve this file's load times.
- The cycle count is from perf and
- covers an entire page load.
-
- Below are my results:
- 1 in 100 == 2447ms, 261M cycles
- 1 in 1,000 == 483ms, 500M cycles
- 5 in 10,000 == 433ms, 800M cycles
- 1 in 10,000 == 320ms, 3000M cycles */
- #define TIMEOUT_RATIO (10000 / 5)
- static uint64_t stale_timeout =
- CONN_STALE_THRESHHOLD * TIMEOUT_RATIO;
- static uint64_t crash_timeout =
- PRINTER_CRASH_TIMEOUT_ANSWER * TIMEOUT_RATIO;
- static uint64_t skip_timeout =
- 1000000000 / TIMEOUT_RATIO;
-
- struct timespec sleep_dur;
- sleep_dur.tv_sec = 0;
- sleep_dur.tv_nsec = skip_timeout;
- nanosleep(&sleep_dur, NULL);
-
- if (status == LIBUSB_ERROR_TIMEOUT)
- times_staled += TIMEOUT_RATIO * timeout / 1000;
- else
- times_staled++;
- if (times_staled % TIMEOUT_RATIO == 0 ||
- status == LIBUSB_ERROR_TIMEOUT) {
- NOTE("No bytes received for %d sec.",
- times_staled / TIMEOUT_RATIO);
- if (pkt->filled_size > 0)
- NOTE("Packet so far \n===\n%s===\n",
- hexdump(pkt->buffer,
- pkt->filled_size));
- }
-
- if (times_staled > stale_timeout) {
- usb_conn_mark_staled(conn);
-
- if (pkt->filled_size > 0 ||
- usb_all_conns_staled(conn->parent) ||
- times_staled > crash_timeout) {
- ERR("USB timed out, giving up waiting for more data");
- break;
- }
- }
- }
-
- if (gotten_size) {
- NOTE("USB: Getting %d bytes of %d",
- read_size, pkt->expected_size);
- NOTE("USB: Got %d bytes", gotten_size);
- }
- packet_mark_received(pkt, (size_t)gotten_size);
- read_size_ulong = packet_pending_bytes(pkt);
- }
- NOTE("USB: Received %d bytes of %d with type %d",
- pkt->filled_size, pkt->expected_size, msg->type);
-
- if (pkt->filled_size == 0)
- goto cleanup;
-
- return pkt;
-
- cleanup:
- if (pkt != NULL)
- packet_free(pkt);
- return NULL;
+ return transfer;
}
diff --git a/src/usb.h b/src/usb.h
index e23e03c..3aa939c 100644
--- a/src/usb.h
+++ b/src/usb.h
@@ -67,4 +67,8 @@ struct usb_conn_t *usb_conn_acquire(struct usb_sock_t *);
void usb_conn_release(struct usb_conn_t *);
int usb_conn_packet_send(struct usb_conn_t *, struct http_packet_t *);
-struct http_packet_t *usb_conn_packet_get(struct usb_conn_t *, struct http_message_t *);
+
+struct libusb_transfer *setup_async_read(struct usb_conn_t *conn,
+ struct http_packet_t *pkt,
+ libusb_transfer_cb_fn callback,
+ void *user_data, uint32_t timeout);