summaryrefslogtreecommitdiff
path: root/libthor/thor.c
diff options
context:
space:
mode:
authorKrzysztof Opasiak <k.opasiak@samsung.com>2016-05-13 10:28:28 +0200
committerKrzysztof Opasiak <k.opasiak@samsung.com>2016-07-07 17:08:54 +0200
commitbec8c1780ee8f8f3b4498a8bfbbcef7e5146e891 (patch)
treecd8aa2470567df107e57e68be2188ecb2cfdbbd8 /libthor/thor.c
parent0a07fb2e67003144f54362aee9c07cb71f13e1df (diff)
downloadlthor-bec8c1780ee8f8f3b4498a8bfbbcef7e5146e891.tar.gz
lthor-bec8c1780ee8f8f3b4498a8bfbbcef7e5146e891.tar.bz2
lthor-bec8c1780ee8f8f3b4498a8bfbbcef7e5146e891.zip
Use asynchronous API to improve performance
Change-Id: I18f0aa14778d06751e7005e4b555c6c68f2839d4 Signed-off-by: Krzysztof Opasiak <k.opasiak@samsung.com>
Diffstat (limited to 'libthor/thor.c')
-rw-r--r--libthor/thor.c280
1 files changed, 234 insertions, 46 deletions
diff --git a/libthor/thor.c b/libthor/thor.c
index 31addcc..6a15155 100644
--- a/libthor/thor.c
+++ b/libthor/thor.c
@@ -175,27 +175,203 @@ int thor_end_session(thor_device_handle *th)
return ret;
}
-static int t_thor_send_chunk(thor_device_handle *th,
- unsigned char *chunk, size_t size,
- int chunk_number)
+static int t_thor_submit_chunk(struct t_thor_data_chunk *chunk)
{
- struct data_res_pkt resp;
int ret;
- ret = t_usb_send(th, chunk, size, DEFAULT_TIMEOUT);
- if (ret < 0)
- return ret;
+ chunk->data_finished = chunk->resp_finished = 0;
- memset(&resp, 0, DATA_RES_PKT_SIZE);
+ ret = t_usb_submit_transfer(&chunk->data_transfer);
+ if (ret)
+ goto out;
- ret = t_usb_recv(th, &resp, DATA_RES_PKT_SIZE, DEFAULT_TIMEOUT);
- if (ret < 0)
- return ret;
+ memset(&chunk->resp, 0, DATA_RES_PKT_SIZE);
+ ret = t_usb_submit_transfer(&chunk->resp_transfer);
+ if (ret)
+ goto cancel_data_transfer;
+
+ return 0;
+cancel_data_transfer:
+ t_usb_cancel_transfer(&chunk->data_transfer);
+out:
+ return ret;
+}
- if (resp.cnt != chunk_number)
+static int t_thor_prep_next_chunk(struct t_thor_data_chunk *chunk,
+ struct t_thor_data_transfer *transfer_data)
+{
+ size_t to_read;
+ int ret;
+
+ to_read = transfer_data->data_left - transfer_data->data_in_progress;
+ if (to_read <= 0) {
+ printf("to big data in progress\n");
+ fflush(stdout);
+ return -EINVAL;
+ }
+
+ chunk->useful_size = to_read > chunk->trans_unit_size ?
+ chunk->trans_unit_size : to_read;
+
+ ret = transfer_data->data->get_block(transfer_data->data,
+ chunk->buf, chunk->useful_size);
+ if (ret < 0 || ret != chunk->useful_size)
return ret;
- return resp.ack;
+ memset(chunk->buf + chunk->useful_size, 0,
+ chunk->trans_unit_size - chunk->useful_size);
+ chunk->chunk_number = transfer_data->chunk_number++;
+
+ ret = t_thor_submit_chunk(chunk);
+ if (!ret)
+ transfer_data->data_in_progress += chunk->useful_size;
+
+ return ret;
+}
+
+static void check_next_chunk(struct t_thor_data_chunk *chunk,
+ struct t_thor_data_transfer *transfer_data)
+{
+ /* If there is some more data to be queued */
+ if (transfer_data->data_left - transfer_data->data_in_progress) {
+ int ret;
+
+ ret = t_thor_prep_next_chunk(chunk, transfer_data);
+ if (ret) {
+ transfer_data->ret = ret;
+ transfer_data->completed = 1;
+ }
+ } else {
+ /* Last one turns the light off */
+ if (transfer_data->data_in_progress == 0)
+ transfer_data->completed = 1;
+ }
+}
+
+static void data_transfer_finished(struct t_usb_transfer *_data_transfer)
+{
+ struct t_thor_data_chunk *chunk = container_of(_data_transfer,
+ struct t_thor_data_chunk,
+ data_transfer);
+ struct t_thor_data_transfer *transfer_data = chunk->user_data;
+
+ chunk->data_finished = 1;
+
+ if (_data_transfer->cancelled || transfer_data->ret)
+ return;
+
+ if (_data_transfer->ret) {
+ transfer_data->ret = _data_transfer->ret;
+ transfer_data->completed = 1;
+ }
+
+ if (chunk->resp_finished)
+ check_next_chunk(chunk, transfer_data);
+}
+
+static void resp_transfer_finished(struct t_usb_transfer *_resp_transfer)
+{
+ struct t_thor_data_chunk *chunk = container_of(_resp_transfer,
+ struct t_thor_data_chunk,
+ resp_transfer);
+ struct t_thor_data_transfer *transfer_data = chunk->user_data;
+
+ chunk->resp_finished = 1;
+ transfer_data->data_in_progress -= chunk->useful_size;
+
+ if (_resp_transfer->cancelled || transfer_data->ret) {
+ if (transfer_data->data_in_progress == 0)
+ transfer_data->completed = 1;
+ return;
+ }
+
+ if (_resp_transfer->ret) {
+ transfer_data->ret = _resp_transfer->ret;
+ goto complete_all;
+ }
+
+ if (chunk->resp.cnt != chunk->chunk_number) {
+ printf ("chunk number mismatch: %d != %d\n",
+ chunk->resp.cnt, chunk->chunk_number);
+ fflush(stdout);
+ transfer_data->ret = -EINVAL;
+ goto complete_all;
+ }
+
+ transfer_data->data_sent += chunk->useful_size;
+ transfer_data->data_left -= chunk->useful_size;
+ if (transfer_data->report_progress)
+ transfer_data->report_progress(transfer_data->th,
+ transfer_data->data,
+ transfer_data->data_sent,
+ transfer_data->data_left,
+ chunk->chunk_number,
+ transfer_data->user_data);
+
+ if (chunk->data_finished)
+ check_next_chunk(chunk, transfer_data);
+
+ return;
+complete_all:
+ transfer_data->completed = 1;
+ return;
+}
+
+static int t_thor_init_chunk(struct t_thor_data_chunk *chunk,
+ thor_device_handle *th,
+ size_t trans_unit_size,
+ void *user_data)
+{
+ int ret;
+
+ chunk->user_data = user_data;
+ chunk->useful_size = 0;
+ chunk->trans_unit_size = trans_unit_size;
+
+ chunk->buf = malloc(trans_unit_size);
+ if (!chunk->buf)
+ return -ENOMEM;
+
+ ret = t_usb_init_out_transfer(&chunk->data_transfer, th, chunk->buf,
+ trans_unit_size, data_transfer_finished,
+ DEFAULT_TIMEOUT);
+ if (ret)
+ goto free_buf;
+
+ ret = t_usb_init_in_transfer(&chunk->resp_transfer, th,
+ (unsigned char *)&chunk->resp,
+ DATA_RES_PKT_SIZE,
+ resp_transfer_finished,
+ 2*DEFAULT_TIMEOUT);
+ if (ret)
+ goto cleanup_data_transfer;
+
+ return 0;
+cleanup_data_transfer:
+ t_usb_cleanup_transfer(&chunk->data_transfer);
+free_buf:
+ free(chunk->buf);
+
+ return ret;
+}
+
+static void t_thor_cleanup_chunk(struct t_thor_data_chunk *chunk)
+{
+ t_usb_cleanup_transfer(&chunk->data_transfer);
+ t_usb_cleanup_transfer(&chunk->resp_transfer);
+ free(chunk->buf);
+}
+
+static inline int
+t_thor_handle_events(struct t_thor_data_transfer *transfer_data)
+{
+ return t_usb_handle_events_completed(&transfer_data->completed);
+}
+
+static inline void t_thor_cancel_chunk(struct t_thor_data_chunk *chunk)
+{
+ t_usb_cancel_transfer(&chunk->data_transfer);
+ t_usb_cancel_transfer(&chunk->resp_transfer);
}
static int t_thor_send_raw_data(thor_device_handle *th,
@@ -204,48 +380,60 @@ static int t_thor_send_raw_data(thor_device_handle *th,
thor_progress_cb report_progress,
void *user_data)
{
- unsigned char *chunk;
- size_t data_left;
- size_t size;
- size_t data_sent = 0;
- int chunk_number = 1;
+ struct t_thor_data_chunk chunk[3];
+ struct t_thor_data_transfer transfer_data;
+ int i, j;
int ret;
- chunk = malloc(trans_unit_size);
- if (!chunk)
- return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(chunk); ++i) {
+ ret = t_thor_init_chunk(chunk + i, th, trans_unit_size,
+ &transfer_data);
+ if (ret)
+ goto cleanup_chunks;
+ }
- data_left = data->get_file_length(data);
+ transfer_data.data = data;
+ transfer_data.report_progress = report_progress;
+ transfer_data.user_data = user_data;
+ transfer_data.data_left = data->get_file_length(data);
+ transfer_data.data_sent = 0;
+ transfer_data.chunk_number = 1;
+ transfer_data.completed = 0;
+ transfer_data.data_in_progress = 0;
+ transfer_data.ret = 0;
+
+ for (i = 0;
+ i < ARRAY_SIZE(chunk)
+ && (transfer_data.data_left - transfer_data.data_in_progress > 0);
+ ++i) {
+ ret = t_thor_prep_next_chunk(chunk + i, &transfer_data);
+ if (ret)
+ goto cancel_chunks;
+ }
- while (data_left) {
- size = data_left > trans_unit_size ?
- trans_unit_size : data_left;
+ t_thor_handle_events(&transfer_data);
- ret = data->get_block(data, chunk, size);
- if (ret < 0 || ret != size)
- goto cleanup;
+ if (transfer_data.data_in_progress) {
+ ret = transfer_data.ret;
+ goto cancel_chunks;
+ }
- memset(chunk + size, 0, trans_unit_size - size);
- if (th) {
- ret = t_thor_send_chunk(th, chunk, trans_unit_size,
- chunk_number);
- if (ret)
- goto cleanup;
- }
+ for (i = 0; i < ARRAY_SIZE(chunk); ++i)
+ t_thor_cleanup_chunk(chunk + i);
- data_sent += size;
- data_left -= size;
- ++chunk_number;
- if (report_progress)
- report_progress(th, data, data_sent, data_left,
- chunk_number, user_data);
- }
+ return transfer_data.ret;
- ret = 0;
-cleanup:
- free(chunk);
- return ret;;
+cancel_chunks:
+ for (j = 0; j < i; ++j)
+ t_thor_cancel_chunk(chunk + j);
+ if (i)
+ t_thor_handle_events(&transfer_data);
+ i = ARRAY_SIZE(chunk);
+cleanup_chunks:
+ for (j = 0; j < i; ++j)
+ t_thor_cleanup_chunk(chunk + j);
+ return ret;
}
int thor_send_data(thor_device_handle *th, struct thor_data_src *data,