From d032e8f8f011a4f061c677adf43bf09321087dee Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Mon, 18 May 2015 23:22:39 -0700 Subject: extra: add simple USB console app for discovery-stm32f072 This provides a very simple console interface for talking to the discovery-stm32f072 board over its USB connection. It's a simpler way to check that the board is working than configuring udev and/or various drivers to recognize USB device 18d1:500f as a serial console. BUG=none BRANCH=none TEST=manual Connect a discovery-stm32f072, then cd extra/usb_console make ./usb_console Change-Id: Ib25baebe5b4f3a930cdc3a1367d6d20d05b70c56 Signed-off-by: Bill Richardson Reviewed-on: https://chromium-review.googlesource.com/273570 Reviewed-by: Anton Staaf --- extra/usb_console/Makefile | 34 +++ extra/usb_console/usb_console.c | 457 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 491 insertions(+) create mode 100644 extra/usb_console/Makefile create mode 100644 extra/usb_console/usb_console.c diff --git a/extra/usb_console/Makefile b/extra/usb_console/Makefile new file mode 100644 index 0000000000..bddca1d0a2 --- /dev/null +++ b/extra/usb_console/Makefile @@ -0,0 +1,34 @@ +# Copyright 2015 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +PROGRAM := usb_console +SOURCE := $(PROGRAM).c +LIBS := +LFLAGS := +CFLAGS := -std=gnu99 \ + -g3 \ + -O3 \ + -Wall \ + -Werror \ + -Wpointer-arith \ + -Wcast-align \ + -Wcast-qual \ + -Wundef \ + -Wsign-compare \ + -Wredundant-decls \ + -Wmissing-declarations + +# +# Add libusb-1.0 required flags +# +LIBS += $(shell pkg-config --libs libusb-1.0) +CFLAGS += $(shell pkg-config --cflags libusb-1.0) + +$(PROGRAM): $(SOURCE) Makefile + gcc $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@ + +.PHONY: clean + +clean: + rm -rf $(PROGRAM) *~ diff --git a/extra/usb_console/usb_console.c b/extra/usb_console/usb_console.c new file mode 100644 index 0000000000..7d99bc3082 --- /dev/null +++ b/extra/usb_console/usb_console.c @@ -0,0 +1,457 @@ +/* + * Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Options */ +static uint16_t vid = 0x18d1; /* Google */ +static uint16_t pid = 0x500f; /* discovery-stm32f072 */ +static uint8_t ep_num = 4; /* console endpoint */ + +static unsigned char rx_buf[1024]; /* much too big */ +static unsigned char tx_buf[1024]; /* much too big */ +static const struct libusb_pollfd **usb_fds; +static struct libusb_device_handle *devh; +static struct libusb_transfer *rx_transfer; +static struct libusb_transfer *tx_transfer; +static int tx_ready; +static int do_exit; + +static void request_exit(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + do_exit++; +} + +#define BOO(msg, r) \ + request_exit("%s: line %d, %s\n", msg, __LINE__, \ + libusb_error_name(r)) + +static void sighandler(int signum) +{ + request_exit("caught signal %d: %s\n", signum, sys_siglist[signum]); +} + +#if 0 +static void show_xfer(const char *msg, struct libusb_transfer *t) +{ + printf("%s: f=%02x ep=%02x type=%d status=%d len=%d actlen=%d\n", msg, + t->flags, + t->endpoint, t->type, t->status, t->length, t->actual_length); +} +#endif + +static void LIBUSB_CALL cb_rx(struct libusb_transfer *transfer) +{ + int r; + + if (transfer->actual_length) { + transfer->buffer[transfer->actual_length] = '\0'; + fputs((char *)transfer->buffer, stdout); + fflush(stdout); + } + + if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + printf("rx_transfer cancelled\n"); + if (rx_transfer) + libusb_free_transfer(rx_transfer); + rx_transfer = NULL; + return; + } + + /* Try again */ + if (!do_exit) { + r = libusb_submit_transfer(rx_transfer); + if (r < 0) + BOO("resubmit rx_transfer failed", r); + } +} + +static void LIBUSB_CALL cb_tx(struct libusb_transfer *transfer) +{ + if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + if (tx_transfer) + libusb_free_transfer(tx_transfer); + tx_transfer = NULL; + request_exit("tx_transfer cancelled\n"); + return; + } + + if (tx_ready != transfer->actual_length) + printf("%s: only sent %d/%d bytes\n", __func__, + transfer->actual_length, tx_ready); + + tx_ready = 0; +} + +static void send_tx(int len) +{ + int r; + + libusb_fill_bulk_transfer(tx_transfer, devh, + ep_num, tx_buf, len, cb_tx, NULL, 0); + + r = libusb_submit_transfer(tx_transfer); + if (r < 0) + BOO("submit tx_transfer failed", r); +} + +static void handle_stdin(void) +{ + static unsigned int i; + int n; + + for (; i < sizeof(tx_buf) - 1; i++) { + n = read(0, tx_buf + i, 1); + if (n == 0) { + request_exit("EOF on stdin\n"); + return; + } + if (n < 0) { + request_exit("stdin: %s\n", strerror(errno)); + return; + } + + if (tx_buf[i] == '\n') { + i++; + tx_buf[i] = '\0'; + break; + } + } + + tx_ready = strlen((char *)tx_buf) + 1; + send_tx(tx_ready); + i = 0; +} + +static void handle_libusb(void) +{ + struct timeval tv = { 0, 0 }; + int r; + + r = libusb_handle_events_timeout_completed(NULL, &tv, &do_exit); + if (r < 0) + BOO("libusb event problem", r); +} + +static int wait_for_stuff_to_happen(void) +{ + int i, r, nfds = 0; + fd_set readset, writeset; + struct timeval tv = { 1, 0 }; + + if (!usb_fds) { + request_exit("No usb_fds to watch\n"); + return -1; + } + + FD_ZERO(&readset); + FD_ZERO(&writeset); + /* always watch stdin */ + FD_SET(0, &readset); + + for (i = 0; usb_fds[i]; i++) { + int fd = usb_fds[i]->fd; + short events = usb_fds[i]->events; + if (fd > nfds) + nfds = fd; + + if (events & POLLIN) + FD_SET(fd, &readset); + if (events & POLLOUT) + FD_SET(fd, &writeset); + } + + r = select(nfds + 1, &readset, &writeset, NULL, &tv); + if (r < 0) { + request_exit("select: %s\n", strerror(errno)); + return -1; + } + + if (r == 0) /* timed out */ + return 0; + + /* Ignore stdin until we've finished sending the current line */ + if (!tx_ready && FD_ISSET(0, &readset)) + return 1; + + /* libusb, then */ + return 2; +} + +static int find_interface_with_endpoint(int want_ep_num) +{ + int iface_num = -1; + int r, i, j, k; + struct libusb_device *dev; + struct libusb_config_descriptor *conf = 0; + const struct libusb_interface *iface0; + const struct libusb_interface_descriptor *iface; + const struct libusb_endpoint_descriptor *ep; + + dev = libusb_get_device(devh); + r = libusb_get_active_config_descriptor(dev, &conf); + if (r < 0) { + BOO("get_active_config", r); + return -1; + } + + for (i = 0; i < conf->bNumInterfaces; i++) { + iface0 = &conf->interface[i]; + for (j = 0; j < iface0->num_altsetting; j++) { + iface = &iface0->altsetting[j]; + for (k = 0; k < iface->bNumEndpoints; k++) { + ep = &iface->endpoint[k]; + if (ep->bEndpointAddress == want_ep_num) { + iface_num = i; + break; + } + } + } + } + + libusb_free_config_descriptor(conf); + return iface_num; +} + +static char *progname; +static char *short_opts = ":v:p:e:h"; +static const struct option long_opts[] = { + /* name hasarg *flag val */ + {"vid", 1, NULL, 'v'}, + {"pid", 1, NULL, 'p'}, + {"ep", 1, NULL, 'e'}, + {"help", 0, NULL, 'h'}, + {NULL, 0, NULL, 0}, +}; + +static void usage(int errs) +{ + printf("\nUsage: %s [options]\n" + "\n" + "A very simple serial console emulator\n" + "\n" + "Options:\n" + "\n" + " -v,--vid HEXVAL Vendor ID (default %04x)\n" + " -p,--pid HEXVAL Product ID (default %04x)\n" + " -e,--ep NUM Endpoint (default %d)\n" + " -h,--help Show this message\n" + "\n", progname, vid, pid, ep_num); + + exit(!!errs); +} + +int main(int argc, char *argv[]) +{ + struct sigaction sigact; + int iface_num; + int claimed_iface = 0; + int r = 1; + int errorcnt = 0; + char *e = 0; + int i; + + progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + + opterr = 0; /* quiet, you */ + while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { + switch (i) { + case 'p': + pid = (uint16_t) strtoul(optarg, &e, 16); + if (!*optarg || (e && *e)) { + printf("Invalid argument: \"%s\"\n", optarg); + errorcnt++; + } + break; + case 'v': + vid = (uint16_t) strtoul(optarg, &e, 16); + if (!*optarg || (e && *e)) { + printf("Invalid argument: \"%s\"\n", optarg); + errorcnt++; + } + break; + case 'e': + ep_num = (uint8_t) strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + printf("Invalid argument: \"%s\"\n", optarg); + errorcnt++; + } + break; + case 'h': + usage(errorcnt); + break; + case 0: /* auto-handled option */ + break; + case '?': + if (optopt) + printf("Unrecognized option: -%c\n", optopt); + else + printf("Unrecognized option: %s\n", + argv[optind - 1]); + errorcnt++; + break; + case ':': + printf("Missing argument to %s\n", argv[optind - 1]); + errorcnt++; + break; + default: + printf("Internal error at %s:%d\n", __FILE__, __LINE__); + exit(1); + } + } + + if (errorcnt) + usage(errorcnt); + + printf("init\n"); + r = libusb_init(NULL); + if (r < 0) { + BOO("init", r); + exit(1); + } + + printf("open_device %04x:%04x\n", vid, pid); + devh = libusb_open_device_with_vid_pid(NULL, vid, pid); + if (!devh) { + printf("can't find device\n"); + goto out; + } + + iface_num = find_interface_with_endpoint(ep_num); + if (iface_num < 0) { + printf("can't find interface owning EP %d\n", ep_num); + goto out; + } + /* NOTE: The EP might be on an alternate interface. We should switch + * to the correct one. */ + + printf("claim_interface %d to use endpoint %d\n", iface_num, ep_num); + r = libusb_claim_interface(devh, iface_num); + if (r < 0) { + BOO("claim interface", r); + goto out; + } + claimed_iface = 1; + + sigact.sa_handler = sighandler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); + + printf("alloc_transfers\n"); + rx_transfer = libusb_alloc_transfer(0); + if (!rx_transfer) { + printf("can't alloc rx_transfer"); + goto out; + } + libusb_fill_bulk_transfer(rx_transfer, devh, + 0x80 | ep_num, + rx_buf, sizeof(rx_buf), cb_rx, NULL, 0); + + tx_transfer = libusb_alloc_transfer(0); + if (!tx_transfer) { + printf("can't alloc tx_transfer"); + goto out; + } + + printf("get_pollfds\n"); + usb_fds = libusb_get_pollfds(NULL); + if (!usb_fds) { + printf("can't get usb_fds\n"); + goto out; + } + + printf("submit rx_transfer\n"); + r = libusb_submit_transfer(rx_transfer); + if (r < 0) { + BOO("submit rx_transfer", r); + goto out; + } + + printf("READY\n-------\n"); + while (!do_exit) { + r = wait_for_stuff_to_happen(); + switch (r) { + case 0: /* timed out */ + /* printf("."); */ + /* fflush(stdout); */ + break; + case 1: /* stdin ready */ + handle_stdin(); + break; + case 2: /* libusb ready */ + handle_libusb(); + break; + } + } + + printf("-------\nshutting down\n"); + + r = libusb_cancel_transfer(rx_transfer); + if (r < 0) { + BOO("cancel rx_transfer", r); + if (rx_transfer) + libusb_free_transfer(rx_transfer); + rx_transfer = 0; + } + + if (tx_ready) { + r = libusb_cancel_transfer(tx_transfer); + if (r < 0) { + BOO("cancel tx_transfer", r); + if (tx_transfer) + libusb_free_transfer(tx_transfer); + tx_transfer = 0; + } + } + + while (rx_transfer) { + printf("draining events...\n"); + r = libusb_handle_events(NULL); + if (r < 0) { + printf("Huh: %s\n", libusb_error_name(r)); + break; + } + } + + printf("bye\n"); + r = 0; + out: + if (tx_transfer) + libusb_free_transfer(tx_transfer); + if (rx_transfer) + libusb_free_transfer(rx_transfer); + + if (devh) { + if (claimed_iface) + libusb_release_interface(devh, iface_num); + libusb_close(devh); + } + libusb_exit(NULL); + + return r; +} -- cgit v1.2.1