From 3dc9f06219f47b0d0f464d774c3aa6bc04f7b6d2 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sat, 4 Mar 2023 18:07:30 +0100 Subject: [PATCH] fxlink: basic writing logic and 'test' command --- fxlink/devices.c | 96 +++++++++++++++++++++++++++++++- fxlink/include/fxlink/devices.h | 4 ++ fxlink/include/fxlink/protocol.h | 9 ++- fxlink/modes/interactive.c | 3 +- fxlink/modes/tui-interactive.c | 21 ++++++- fxlink/protocol.c | 27 ++++++++- 6 files changed, 154 insertions(+), 6 deletions(-) diff --git a/fxlink/devices.c b/fxlink/devices.c index 86e0986..9edc256 100644 --- a/fxlink/devices.c +++ b/fxlink/devices.c @@ -460,7 +460,7 @@ void fxlink_device_start_bulk_IN(struct fxlink_device *fdev) int rc = libusb_submit_transfer(fdev->comm->tr_bulk_IN); if(rc < 0) { - elog("bulk IN transfer failed to submit: %s\n", libusb_strerror(rc)); + elog_libusb(rc, "bulk IN transfer failed to submit"); fdev->status = FXLINK_FDEV_STATUS_ERROR; return; } @@ -489,10 +489,104 @@ struct fxlink_message *fxlink_device_finish_bulk_IN(struct fxlink_device *fdev) log_("new message (v%d.%d): %.16s:%.16s, %s\n", version_major, version_minor, msg->application, msg->type, fxlink_size_string(msg->size)); + + fxlink_transfer_free(comm->ftransfer_IN); comm->ftransfer_IN = NULL; return msg; } +/* Note: this function is run by the even handler and can't do any crazy libusb + stuff like sync I/O or getting descriptors. */ +static void bulk_OUT_callback(struct libusb_transfer *transfer) +{ + struct fxlink_device *fdev = transfer->user_data; + struct fxlink_comm *comm = fdev->comm; + + int data_size = transfer->actual_length; + bool send_more = true; + struct fxlink_transfer *tr = comm->ftransfer_OUT; + + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) + hlog("calculators %s", fxlink_device_id(fdev)); + + switch(transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + if(tr->processed_size < 0) { + if(data_size != FXLINK_MESSAGE_HEADER_SIZE) { + elog("OUT for header only partially completed"); + send_more = false; + } + tr->processed_size = 0; + } + else { + tr->processed_size += data_size; + if(fxlink_transfer_complete(tr)) + send_more = false; + } + + if(send_more) { + libusb_fill_bulk_transfer(comm->tr_bulk_OUT, fdev->dh, + comm->ep_bulk_OUT, /* Endpoint */ + tr->msg.data + tr->processed_size, /* Buffer */ + tr->msg.size - tr->processed_size, /* Buffer size */ + bulk_OUT_callback, fdev, -1); /* Callback and timeout */ + libusb_submit_transfer(comm->tr_bulk_OUT); + } + else { + libusb_free_transfer(comm->tr_bulk_OUT); + comm->tr_bulk_OUT = NULL; + fxlink_transfer_free(comm->ftransfer_OUT); + comm->ftransfer_OUT = NULL; + } + break; + + /* Typical errors */ + case LIBUSB_TRANSFER_ERROR: log_("transfer error\n"); break; + case LIBUSB_TRANSFER_TIMED_OUT: log_("transfer timed out\n"); break; + case LIBUSB_TRANSFER_CANCELLED: log_("transfer cancelled\n"); break; + case LIBUSB_TRANSFER_STALL: log_("transfer stalled\n"); break; + case LIBUSB_TRANSFER_OVERFLOW: log_("transfer overflowed\n"); break; + /* No device: this is normal */ + case LIBUSB_TRANSFER_NO_DEVICE: + log_("stop listening (calculator disconnected)\n"); + break; + } +} + +void fxlink_device_start_bulk_OUT(struct fxlink_device *fdev, + char const *app, char const *type, void const *data, int size) +{ + struct fxlink_comm *comm = fdev->comm; + if(!comm || !comm->claimed || comm->ftransfer_OUT) + return; + + comm->ftransfer_OUT = fxlink_transfer_make_OUT(app, type, data, size); + if(!comm->ftransfer_OUT) { + elog("allocation of OUT transfer (protocol) failed\n"); + return; + } + + comm->tr_bulk_OUT = libusb_alloc_transfer(0); + if(!comm->tr_bulk_OUT) { + elog("allocation of bulk OUT transfer (libusb) failed\n"); + free(comm->ftransfer_OUT); + return; + } + + libusb_fill_bulk_transfer(comm->tr_bulk_OUT, fdev->dh, + comm->ep_bulk_OUT, /* Endpoint */ + (void *)&comm->ftransfer_OUT->msg, /* Buffer */ + FXLINK_MESSAGE_HEADER_SIZE, /* Buffer size */ + bulk_OUT_callback, fdev, -1); /* Callback and timeout */ + + int rc = libusb_submit_transfer(comm->tr_bulk_OUT); + if(rc < 0) { + elog_libusb(rc, "bulk OUT transfer failed to submit"); + fdev->status = FXLINK_FDEV_STATUS_ERROR; + return; + } +} + //--- // Polled file descriptor tracking //--- diff --git a/fxlink/include/fxlink/devices.h b/fxlink/include/fxlink/devices.h index 96d6bf6..41e56b2 100644 --- a/fxlink/include/fxlink/devices.h +++ b/fxlink/include/fxlink/devices.h @@ -210,6 +210,10 @@ void fxlink_device_start_bulk_IN(struct fxlink_device *fdev); struct fxlink_message *fxlink_device_finish_bulk_IN( struct fxlink_device *fdev); +/* Start an OUT transfer on the device. */ +void fxlink_device_start_bulk_OUT(struct fxlink_device *fdev, + char const *app, char const *type, void const *data, int size); + /* Interrupt any active transfers on the device. */ void fxlink_device_interrupt_transfers(struct fxlink_device *fdev); diff --git a/fxlink/include/fxlink/protocol.h b/fxlink/include/fxlink/protocol.h index 5c45962..7f839d2 100644 --- a/fxlink/include/fxlink/protocol.h +++ b/fxlink/include/fxlink/protocol.h @@ -112,7 +112,7 @@ struct fxlink_transfer { /* Transfer direction (FXLINK_TRANSFER_{IN,OUT}) */ uint8_t direction; /* Size of data sent or received so far */ - uint32_t processed_size; + int processed_size; }; enum { @@ -136,5 +136,12 @@ struct fxlink_message *fxlink_transfer_finish_IN(struct fxlink_transfer *tr); /* Append data to a previously-initialized inbound transfer. */ void fxlink_transfer_receive(struct fxlink_transfer *tr, void *data, int size); +/* Make an outbound transfer structure. */ +struct fxlink_transfer *fxlink_transfer_make_OUT(char const *application, + char const *type, void const *data, int size); + /* Check whether a transfer is complete. */ bool fxlink_transfer_complete(struct fxlink_transfer const *tr); + +/* Free a transfer structure and associated data. */ +void fxlink_transfer_free(struct fxlink_transfer *tr); diff --git a/fxlink/modes/interactive.c b/fxlink/modes/interactive.c index 1ed7ab7..8b2a18f 100644 --- a/fxlink/modes/interactive.c +++ b/fxlink/modes/interactive.c @@ -158,8 +158,7 @@ int main_interactive(struct fxlink_filter *filter, delay_t *delay, /* Warning for unfinished transfer */ if(tr) { wlog("unfinished transfer interrupted by disconnection\n"); - // TODO: Proper way to free a transfer without finishing it - free(tr); + fxlink_transfer_free(tr); } fxlink_device_cleanup(fdev); diff --git a/fxlink/modes/tui-interactive.c b/fxlink/modes/tui-interactive.c index 45473c8..1c681e9 100644 --- a/fxlink/modes/tui-interactive.c +++ b/fxlink/modes/tui-interactive.c @@ -221,7 +221,7 @@ static void TUI_render_transfers(void) if(OUT) { mvwaddstr(win, y, 1, fxlink_device_id(fdev)); mvwaddstr(win, y, 10, "OUT"); - mvwaddstr(win, y, 16, fxlink_size_string(IN->msg.size)); + mvwaddstr(win, y, 16, fxlink_size_string(OUT->msg.size)); has_transfers = true; y++; } @@ -482,6 +482,25 @@ int main_tui_interactive(libusb_context *ctx) log_("command: '%s'\n", command); if(!strcmp(command, "q")) break; + if(!strcmp(command, "test")) { + /* Find a device */ + struct fxlink_device *fdev = NULL; + for(int i = 0; i < TUI.devices.count; i++) { + fdev = &TUI.devices.devices[i]; + if(fdev->status == FXLINK_FDEV_STATUS_CONNECTED) + break; + else fdev = NULL; + } + if(fdev) { + print(TUI.wConsole, "using device %s (%s)\n", + fxlink_device_id(fdev), fdev->calc->serial); + fxlink_device_start_bulk_OUT(fdev, + "fxlink", "command", "test", 4); + } + else { + print(TUI.wConsole, "no connected device!\n"); + } + } fxlink_TUI_input_free(&input); print(TUI.wConsole, "%s", prompt); fxlink_TUI_input_init(&input, TUI.wConsole, 16); diff --git a/fxlink/protocol.c b/fxlink/protocol.c index 87e23ca..06d4101 100644 --- a/fxlink/protocol.c +++ b/fxlink/protocol.c @@ -282,7 +282,32 @@ void fxlink_transfer_receive(struct fxlink_transfer *tr, void *data, int size) tr->processed_size += size; } +struct fxlink_transfer *fxlink_transfer_make_OUT(char const *application, + char const *type, void const *data, int size) +{ + struct fxlink_transfer *tr = calloc(1, sizeof *tr); + if(!tr) + return NULL; + + tr->msg.version = 0x00000100; + tr->msg.size = size; + tr->msg.transfer_size = 0; + strncpy(tr->msg.application, application, 16); + strncpy(tr->msg.type, type, 16); + tr->msg.data = (void *)data; + tr->direction = FXLINK_TRANSFER_OUT; + tr->processed_size = -1; + return tr; +} + bool fxlink_transfer_complete(struct fxlink_transfer const *tr) { - return tr->processed_size >= tr->msg.size; + return tr->processed_size >= (int)tr->msg.size; +} + +void fxlink_transfer_free(struct fxlink_transfer *tr) +{ + if(tr->direction == FXLINK_TRANSFER_IN) + free(tr->msg.data); + free(tr); }