diff options
Diffstat (limited to 'libusbx-1.0.18/examples')
-rw-r--r-- | libusbx-1.0.18/examples/Makefile.am | 19 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/dpfp.c | 506 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/dpfp_threaded.c | 544 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/ezusb.c | 827 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/ezusb.h | 120 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/fxload.c | 287 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/getopt/getopt.c | 1060 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/getopt/getopt.h | 180 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/getopt/getopt1.c | 188 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/hotplugtest.c | 104 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/listdevs.c | 71 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/sam3u_benchmark.c | 193 | ||||
-rw-r--r-- | libusbx-1.0.18/examples/xusb.c | 1109 |
13 files changed, 5208 insertions, 0 deletions
diff --git a/libusbx-1.0.18/examples/Makefile.am b/libusbx-1.0.18/examples/Makefile.am new file mode 100644 index 0000000..8081927 --- /dev/null +++ b/libusbx-1.0.18/examples/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = -I$(top_srcdir)/libusb +LDADD = ../libusb/libusb-1.0.la + +noinst_PROGRAMS = listdevs xusb fxload hotplugtest + +if HAVE_SIGACTION +noinst_PROGRAMS += dpfp + +if THREADS_POSIX +dpfp_threaded_CFLAGS = $(AM_CFLAGS) +noinst_PROGRAMS += dpfp_threaded +endif + +sam3u_benchmark_SOURCES = sam3u_benchmark.c +noinst_PROGRAMS += sam3u_benchmark +endif + +fxload_SOURCES = ezusb.c ezusb.h fxload.c +fxload_CFLAGS = $(THREAD_CFLAGS) $(AM_CFLAGS) diff --git a/libusbx-1.0.18/examples/dpfp.c b/libusbx-1.0.18/examples/dpfp.c new file mode 100644 index 0000000..3f41e0e --- /dev/null +++ b/libusbx-1.0.18/examples/dpfp.c @@ -0,0 +1,506 @@ +/* + * libusbx example program to manipulate U.are.U 4000B fingerprint scanner. + * Copyright © 2007 Daniel Drake <dsd@gentoo.org> + * + * Basic image capture program only, does not consider the powerup quirks or + * the fact that image encryption may be enabled. Not expected to work + * flawlessly all of the time. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "libusb.h" + +#define EP_INTR (1 | LIBUSB_ENDPOINT_IN) +#define EP_DATA (2 | LIBUSB_ENDPOINT_IN) +#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) +#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) +#define USB_RQ 0x04 +#define INTR_LENGTH 64 + +enum { + MODE_INIT = 0x00, + MODE_AWAIT_FINGER_ON = 0x10, + MODE_AWAIT_FINGER_OFF = 0x12, + MODE_CAPTURE = 0x20, + MODE_SHUT_UP = 0x30, + MODE_READY = 0x80, +}; + +static int next_state(void); + +enum { + STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, + STATE_AWAIT_IRQ_FINGER_DETECTED, + STATE_AWAIT_MODE_CHANGE_CAPTURE, + STATE_AWAIT_IMAGE, + STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, + STATE_AWAIT_IRQ_FINGER_REMOVED, +}; + +static int state = 0; +static struct libusb_device_handle *devh = NULL; +static unsigned char imgbuf[0x1b340]; +static unsigned char irqbuf[INTR_LENGTH]; +static struct libusb_transfer *img_transfer = NULL; +static struct libusb_transfer *irq_transfer = NULL; +static int img_idx = 0; +static int do_exit = 0; + +static int find_dpfp_device(void) +{ + devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); + return devh ? 0 : -EIO; +} + +static int print_f0_data(void) +{ + unsigned char data[0x10]; + int r; + unsigned int i; + + r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, + sizeof(data), 0); + if (r < 0) { + fprintf(stderr, "F0 error %d\n", r); + return r; + } + if ((unsigned int) r < sizeof(data)) { + fprintf(stderr, "short read (%d)\n", r); + return -1; + } + + printf("F0 data:"); + for (i = 0; i < sizeof(data); i++) + printf("%02x ", data[i]); + printf("\n"); + return 0; +} + +static int get_hwstat(unsigned char *status) +{ + int r; + + r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0); + if (r < 0) { + fprintf(stderr, "read hwstat error %d\n", r); + return r; + } + if ((unsigned int) r < 1) { + fprintf(stderr, "short read (%d)\n", r); + return -1; + } + + printf("hwstat reads %02x\n", *status); + return 0; +} + +static int set_hwstat(unsigned char data) +{ + int r; + + printf("set hwstat to %02x\n", data); + r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0); + if (r < 0) { + fprintf(stderr, "set hwstat error %d\n", r); + return r; + } + if ((unsigned int) r < 1) { + fprintf(stderr, "short write (%d)", r); + return -1; + } + + return 0; +} + +static int set_mode(unsigned char data) +{ + int r; + printf("set mode %02x\n", data); + + r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0); + if (r < 0) { + fprintf(stderr, "set mode error %d\n", r); + return r; + } + if ((unsigned int) r < 1) { + fprintf(stderr, "short write (%d)", r); + return -1; + } + + return 0; +} + +static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) +{ + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "mode change transfer not completed!\n"); + do_exit = 2; + } + + printf("async cb_mode_changed length=%d actual_length=%d\n", + transfer->length, transfer->actual_length); + if (next_state() < 0) + do_exit = 2; +} + +static int set_mode_async(unsigned char data) +{ + unsigned char *buf = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); + struct libusb_transfer *transfer; + + if (!buf) + return -ENOMEM; + + transfer = libusb_alloc_transfer(0); + if (!transfer) { + free(buf); + return -ENOMEM; + } + + printf("async set mode %02x\n", data); + libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); + buf[LIBUSB_CONTROL_SETUP_SIZE] = data; + libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, + 1000); + + transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK + | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; + return libusb_submit_transfer(transfer); +} + +static int do_sync_intr(unsigned char *data) +{ + int r; + int transferred; + + r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, + &transferred, 1000); + if (r < 0) { + fprintf(stderr, "intr error %d\n", r); + return r; + } + if (transferred < INTR_LENGTH) { + fprintf(stderr, "short read (%d)\n", r); + return -1; + } + + printf("recv interrupt %04x\n", *((uint16_t *) data)); + return 0; +} + +static int sync_intr(unsigned char type) +{ + int r; + unsigned char data[INTR_LENGTH]; + + while (1) { + r = do_sync_intr(data); + if (r < 0) + return r; + if (data[0] == type) + return 0; + } +} + +static int save_to_file(unsigned char *data) +{ + FILE *fd; + char filename[64]; + + snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++); + fd = fopen(filename, "w"); + if (!fd) + return -1; + + fputs("P5 384 289 255 ", fd); + (void) fwrite(data + 64, 1, 384*289, fd); + fclose(fd); + printf("saved image to %s\n", filename); + return 0; +} + +static int next_state(void) +{ + int r = 0; + printf("old state: %d\n", state); + switch (state) { + case STATE_AWAIT_IRQ_FINGER_REMOVED: + state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; + r = set_mode_async(MODE_AWAIT_FINGER_ON); + break; + case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: + state = STATE_AWAIT_IRQ_FINGER_DETECTED; + break; + case STATE_AWAIT_IRQ_FINGER_DETECTED: + state = STATE_AWAIT_MODE_CHANGE_CAPTURE; + r = set_mode_async(MODE_CAPTURE); + break; + case STATE_AWAIT_MODE_CHANGE_CAPTURE: + state = STATE_AWAIT_IMAGE; + break; + case STATE_AWAIT_IMAGE: + state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; + r = set_mode_async(MODE_AWAIT_FINGER_OFF); + break; + case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: + state = STATE_AWAIT_IRQ_FINGER_REMOVED; + break; + default: + printf("unrecognised state %d\n", state); + } + if (r < 0) { + fprintf(stderr, "error detected changing state\n"); + return r; + } + + printf("new state: %d\n", state); + return 0; +} + +static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) +{ + unsigned char irqtype = transfer->buffer[0]; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "irq transfer status %d?\n", transfer->status); + do_exit = 2; + libusb_free_transfer(transfer); + irq_transfer = NULL; + return; + } + + printf("IRQ callback %02x\n", irqtype); + switch (state) { + case STATE_AWAIT_IRQ_FINGER_DETECTED: + if (irqtype == 0x01) { + if (next_state() < 0) { + do_exit = 2; + return; + } + } else { + printf("finger-on-sensor detected in wrong state!\n"); + } + break; + case STATE_AWAIT_IRQ_FINGER_REMOVED: + if (irqtype == 0x02) { + if (next_state() < 0) { + do_exit = 2; + return; + } + } else { + printf("finger-on-sensor detected in wrong state!\n"); + } + break; + } + if (libusb_submit_transfer(irq_transfer) < 0) + do_exit = 2; +} + +static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) +{ + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "img transfer status %d?\n", transfer->status); + do_exit = 2; + libusb_free_transfer(transfer); + img_transfer = NULL; + return; + } + + printf("Image callback\n"); + save_to_file(imgbuf); + if (next_state() < 0) { + do_exit = 2; + return; + } + if (libusb_submit_transfer(img_transfer) < 0) + do_exit = 2; +} + +static int init_capture(void) +{ + int r; + + r = libusb_submit_transfer(irq_transfer); + if (r < 0) + return r; + + r = libusb_submit_transfer(img_transfer); + if (r < 0) { + libusb_cancel_transfer(irq_transfer); + while (irq_transfer) + if (libusb_handle_events(NULL) < 0) + break; + return r; + } + + /* start state machine */ + state = STATE_AWAIT_IRQ_FINGER_REMOVED; + return next_state(); +} + +static int do_init(void) +{ + unsigned char status; + int r; + + r = get_hwstat(&status); + if (r < 0) + return r; + + if (!(status & 0x80)) { + r = set_hwstat(status | 0x80); + if (r < 0) + return r; + r = get_hwstat(&status); + if (r < 0) + return r; + } + + status &= ~0x80; + r = set_hwstat(status); + if (r < 0) + return r; + + r = get_hwstat(&status); + if (r < 0) + return r; + + r = sync_intr(0x56); + if (r < 0) + return r; + + return 0; +} + +static int alloc_transfers(void) +{ + img_transfer = libusb_alloc_transfer(0); + if (!img_transfer) + return -ENOMEM; + + irq_transfer = libusb_alloc_transfer(0); + if (!irq_transfer) + return -ENOMEM; + + libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, + sizeof(imgbuf), cb_img, NULL, 0); + libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, + sizeof(irqbuf), cb_irq, NULL, 0); + + return 0; +} + +static void sighandler(int signum) +{ + do_exit = 1; +} + +int main(void) +{ + struct sigaction sigact; + int r = 1; + + r = libusb_init(NULL); + if (r < 0) { + fprintf(stderr, "failed to initialise libusb\n"); + exit(1); + } + + r = find_dpfp_device(); + if (r < 0) { + fprintf(stderr, "Could not find/open device\n"); + goto out; + } + + r = libusb_claim_interface(devh, 0); + if (r < 0) { + fprintf(stderr, "usb_claim_interface error %d\n", r); + goto out; + } + printf("claimed interface\n"); + + r = print_f0_data(); + if (r < 0) + goto out_release; + + r = do_init(); + if (r < 0) + goto out_deinit; + + /* async from here onwards */ + + r = alloc_transfers(); + if (r < 0) + goto out_deinit; + + r = init_capture(); + if (r < 0) + goto out_deinit; + + sigact.sa_handler = sighandler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); + + while (!do_exit) { + r = libusb_handle_events(NULL); + if (r < 0) + goto out_deinit; + } + + printf("shutting down...\n"); + + if (irq_transfer) { + r = libusb_cancel_transfer(irq_transfer); + if (r < 0) + goto out_deinit; + } + + if (img_transfer) { + r = libusb_cancel_transfer(img_transfer); + if (r < 0) + goto out_deinit; + } + + while (irq_transfer || img_transfer) + if (libusb_handle_events(NULL) < 0) + break; + + if (do_exit == 1) + r = 0; + else + r = 1; + +out_deinit: + libusb_free_transfer(img_transfer); + libusb_free_transfer(irq_transfer); + set_mode(0); + set_hwstat(0x80); +out_release: + libusb_release_interface(devh, 0); +out: + libusb_close(devh); + libusb_exit(NULL); + return r >= 0 ? r : -r; +} diff --git a/libusbx-1.0.18/examples/dpfp_threaded.c b/libusbx-1.0.18/examples/dpfp_threaded.c new file mode 100644 index 0000000..6970dac --- /dev/null +++ b/libusbx-1.0.18/examples/dpfp_threaded.c @@ -0,0 +1,544 @@ +/* + * libusbx example program to manipulate U.are.U 4000B fingerprint scanner. + * Copyright © 2007 Daniel Drake <dsd@gentoo.org> + * + * Basic image capture program only, does not consider the powerup quirks or + * the fact that image encryption may be enabled. Not expected to work + * flawlessly all of the time. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "libusb.h" + +#define EP_INTR (1 | LIBUSB_ENDPOINT_IN) +#define EP_DATA (2 | LIBUSB_ENDPOINT_IN) +#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) +#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) +#define USB_RQ 0x04 +#define INTR_LENGTH 64 + +enum { + MODE_INIT = 0x00, + MODE_AWAIT_FINGER_ON = 0x10, + MODE_AWAIT_FINGER_OFF = 0x12, + MODE_CAPTURE = 0x20, + MODE_SHUT_UP = 0x30, + MODE_READY = 0x80, +}; + +static int next_state(void); + +enum { + STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, + STATE_AWAIT_IRQ_FINGER_DETECTED, + STATE_AWAIT_MODE_CHANGE_CAPTURE, + STATE_AWAIT_IMAGE, + STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, + STATE_AWAIT_IRQ_FINGER_REMOVED, +}; + +static int state = 0; +static struct libusb_device_handle *devh = NULL; +static unsigned char imgbuf[0x1b340]; +static unsigned char irqbuf[INTR_LENGTH]; +static struct libusb_transfer *img_transfer = NULL; +static struct libusb_transfer *irq_transfer = NULL; +static int img_idx = 0; +static int do_exit = 0; + +static pthread_t poll_thread; +static pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t exit_cond_lock = PTHREAD_MUTEX_INITIALIZER; + +static void request_exit(int code) +{ + do_exit = code; + pthread_cond_signal(&exit_cond); +} + +static void *poll_thread_main(void *arg) +{ + int r = 0; + printf("poll thread running\n"); + + while (!do_exit) { + struct timeval tv = { 1, 0 }; + r = libusb_handle_events_timeout(NULL, &tv); + if (r < 0) { + request_exit(2); + break; + } + } + + printf("poll thread shutting down\n"); + return NULL; +} + +static int find_dpfp_device(void) +{ + devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); + return devh ? 0 : -EIO; +} + +static int print_f0_data(void) +{ + unsigned char data[0x10]; + int r; + unsigned int i; + + r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, + sizeof(data), 0); + if (r < 0) { + fprintf(stderr, "F0 error %d\n", r); + return r; + } + if ((unsigned int) r < sizeof(data)) { + fprintf(stderr, "short read (%d)\n", r); + return -1; + } + + printf("F0 data:"); + for (i = 0; i < sizeof(data); i++) + printf("%02x ", data[i]); + printf("\n"); + return 0; +} + +static int get_hwstat(unsigned char *status) +{ + int r; + + r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0); + if (r < 0) { + fprintf(stderr, "read hwstat error %d\n", r); + return r; + } + if ((unsigned int) r < 1) { + fprintf(stderr, "short read (%d)\n", r); + return -1; + } + + printf("hwstat reads %02x\n", *status); + return 0; +} + +static int set_hwstat(unsigned char data) +{ + int r; + + printf("set hwstat to %02x\n", data); + r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0); + if (r < 0) { + fprintf(stderr, "set hwstat error %d\n", r); + return r; + } + if ((unsigned int) r < 1) { + fprintf(stderr, "short write (%d)", r); + return -1; + } + + return 0; +} + +static int set_mode(unsigned char data) +{ + int r; + printf("set mode %02x\n", data); + + r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0); + if (r < 0) { + fprintf(stderr, "set mode error %d\n", r); + return r; + } + if ((unsigned int) r < 1) { + fprintf(stderr, "short write (%d)", r); + return -1; + } + + return 0; +} + +static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) +{ + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "mode change transfer not completed!\n"); + request_exit(2); + } + + printf("async cb_mode_changed length=%d actual_length=%d\n", + transfer->length, transfer->actual_length); + if (next_state() < 0) + request_exit(2); +} + +static int set_mode_async(unsigned char data) +{ + unsigned char *buf = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); + struct libusb_transfer *transfer; + + if (!buf) + return -ENOMEM; + + transfer = libusb_alloc_transfer(0); + if (!transfer) { + free(buf); + return -ENOMEM; + } + + printf("async set mode %02x\n", data); + libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); + buf[LIBUSB_CONTROL_SETUP_SIZE] = data; + libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, + 1000); + + transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK + | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; + return libusb_submit_transfer(transfer); +} + +static int do_sync_intr(unsigned char *data) +{ + int r; + int transferred; + + r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, + &transferred, 1000); + if (r < 0) { + fprintf(stderr, "intr error %d\n", r); + return r; + } + if (transferred < INTR_LENGTH) { + fprintf(stderr, "short read (%d)\n", r); + return -1; + } + + printf("recv interrupt %04x\n", *((uint16_t *) data)); + return 0; +} + +static int sync_intr(unsigned char type) +{ + int r; + unsigned char data[INTR_LENGTH]; + + while (1) { + r = do_sync_intr(data); + if (r < 0) + return r; + if (data[0] == type) + return 0; + } +} + +static int save_to_file(unsigned char *data) +{ + FILE *fd; + char filename[64]; + + snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++); + fd = fopen(filename, "w"); + if (!fd) + return -1; + + fputs("P5 384 289 255 ", fd); + (void) fwrite(data + 64, 1, 384*289, fd); + fclose(fd); + printf("saved image to %s\n", filename); + return 0; +} + +static int next_state(void) +{ + int r = 0; + printf("old state: %d\n", state); + switch (state) { + case STATE_AWAIT_IRQ_FINGER_REMOVED: + state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; + r = set_mode_async(MODE_AWAIT_FINGER_ON); + break; + case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: + state = STATE_AWAIT_IRQ_FINGER_DETECTED; + break; + case STATE_AWAIT_IRQ_FINGER_DETECTED: + state = STATE_AWAIT_MODE_CHANGE_CAPTURE; + r = set_mode_async(MODE_CAPTURE); + break; + case STATE_AWAIT_MODE_CHANGE_CAPTURE: + state = STATE_AWAIT_IMAGE; + break; + case STATE_AWAIT_IMAGE: + state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; + r = set_mode_async(MODE_AWAIT_FINGER_OFF); + break; + case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: + state = STATE_AWAIT_IRQ_FINGER_REMOVED; + break; + default: + printf("unrecognised state %d\n", state); + } + if (r < 0) { + fprintf(stderr, "error detected changing state\n"); + return r; + } + + printf("new state: %d\n", state); + return 0; +} + +static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) +{ + unsigned char irqtype = transfer->buffer[0]; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "irq transfer status %d?\n", transfer->status); + irq_transfer = NULL; + request_exit(2); + return; + } + + printf("IRQ callback %02x\n", irqtype); + switch (state) { + case STATE_AWAIT_IRQ_FINGER_DETECTED: + if (irqtype == 0x01) { + if (next_state() < 0) { + request_exit(2); + return; + } + } else { + printf("finger-on-sensor detected in wrong state!\n"); + } + break; + case STATE_AWAIT_IRQ_FINGER_REMOVED: + if (irqtype == 0x02) { + if (next_state() < 0) { + request_exit(2); + return; + } + } else { + printf("finger-on-sensor detected in wrong state!\n"); + } + break; + } + if (libusb_submit_transfer(irq_transfer) < 0) + request_exit(2); +} + +static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) +{ + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "img transfer status %d?\n", transfer->status); + img_transfer = NULL; + request_exit(2); + return; + } + + printf("Image callback\n"); + save_to_file(imgbuf); + if (next_state() < 0) { + request_exit(2); + return; + } + if (libusb_submit_transfer(img_transfer) < 0) + request_exit(2); +} + +static int init_capture(void) +{ + int r; + + r = libusb_submit_transfer(irq_transfer); + if (r < 0) + return r; + + r = libusb_submit_transfer(img_transfer); + if (r < 0) { + libusb_cancel_transfer(irq_transfer); + while (irq_transfer) + if (libusb_handle_events(NULL) < 0) + break; + return r; + } + + /* start state machine */ + state = STATE_AWAIT_IRQ_FINGER_REMOVED; + return next_state(); +} + +static int do_init(void) +{ + unsigned char status; + int r; + + r = get_hwstat(&status); + if (r < 0) + return r; + + if (!(status & 0x80)) { + r = set_hwstat(status | 0x80); + if (r < 0) + return r; + r = get_hwstat(&status); + if (r < 0) + return r; + } + + status &= ~0x80; + r = set_hwstat(status); + if (r < 0) + return r; + + r = get_hwstat(&status); + if (r < 0) + return r; + + r = sync_intr(0x56); + if (r < 0) + return r; + + return 0; +} + +static int alloc_transfers(void) +{ + img_transfer = libusb_alloc_transfer(0); + if (!img_transfer) + return -ENOMEM; + + irq_transfer = libusb_alloc_transfer(0); + if (!irq_transfer) + return -ENOMEM; + + libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, + sizeof(imgbuf), cb_img, NULL, 0); + libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, + sizeof(irqbuf), cb_irq, NULL, 0); + + return 0; +} + +static void sighandler(int signum) +{ + request_exit(1); +} + +int main(void) +{ + struct sigaction sigact; + int r = 1; + + r = libusb_init(NULL); + if (r < 0) { + fprintf(stderr, "failed to initialise libusb\n"); + exit(1); + } + + r = find_dpfp_device(); + if (r < 0) { + fprintf(stderr, "Could not find/open device\n"); + goto out; + } + + r = libusb_claim_interface(devh, 0); + if (r < 0) { + fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r)); + goto out; + } + printf("claimed interface\n"); + + r = print_f0_data(); + if (r < 0) + goto out_release; + + r = do_init(); + if (r < 0) + goto out_deinit; + + /* async from here onwards */ + + sigact.sa_handler = sighandler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); + + r = pthread_create(&poll_thread, NULL, poll_thread_main, NULL); + if (r) + goto out_deinit; + + r = alloc_transfers(); + if (r < 0) { + request_exit(1); + pthread_join(poll_thread, NULL); + goto out_deinit; + } + + r = init_capture(); + if (r < 0) { + request_exit(1); + pthread_join(poll_thread, NULL); + goto out_deinit; + } + + while (!do_exit) { + pthread_mutex_lock(&exit_cond_lock); + pthread_cond_wait(&exit_cond, &exit_cond_lock); + pthread_mutex_unlock(&exit_cond_lock); + } + + printf("shutting down...\n"); + pthread_join(poll_thread, NULL); + + r = libusb_cancel_transfer(irq_transfer); + if (r < 0) { + request_exit(1); + goto out_deinit; + } + + r = libusb_cancel_transfer(img_transfer); + if (r < 0) { + request_exit(1); + goto out_deinit; + } + + while (img_transfer || irq_transfer) + if (libusb_handle_events(NULL) < 0) + break; + + if (do_exit == 1) + r = 0; + else + r = 1; + +out_deinit: + libusb_free_transfer(img_transfer); + libusb_free_transfer(irq_transfer); + set_mode(0); + set_hwstat(0x80); +out_release: + libusb_release_interface(devh, 0); +out: + libusb_close(devh); + libusb_exit(NULL); + return r >= 0 ? r : -r; +} diff --git a/libusbx-1.0.18/examples/ezusb.c b/libusbx-1.0.18/examples/ezusb.c new file mode 100644 index 0000000..278af5d --- /dev/null +++ b/libusbx-1.0.18/examples/ezusb.c @@ -0,0 +1,827 @@ +/* + * Copyright © 2001 Stephen Williams (steve@icarus.com) + * Copyright © 2001-2002 David Brownell (dbrownell@users.sourceforge.net) + * Copyright © 2008 Roger Williams (rawqux@users.sourceforge.net) + * Copyright © 2012 Pete Batard (pete@akeo.ie) + * Copyright © 2013 Federico Manzan (f.manzan@gmail.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include "libusb.h" +#include "ezusb.h" + +extern void logerror(const char *format, ...) + __attribute__ ((format(printf, 1, 2))); + +/* + * This file contains functions for uploading firmware into Cypress + * EZ-USB microcontrollers. These chips use control endpoint 0 and vendor + * specific commands to support writing into the on-chip SRAM. They also + * support writing into the CPUCS register, which is how we reset the + * processor after loading firmware (including the reset vector). + * + * These Cypress devices are 8-bit 8051 based microcontrollers with + * special support for USB I/O. They come in several packages, and + * some can be set up with external memory when device costs allow. + * Note that the design was originally by AnchorChips, so you may find + * references to that vendor (which was later merged into Cypress). + * The Cypress FX parts are largely compatible with the Anchorhip ones. + */ + +int verbose = 1; + +/* + * return true if [addr,addr+len] includes external RAM + * for Anchorchips EZ-USB or Cypress EZ-USB FX + */ +static bool fx_is_external(uint32_t addr, size_t len) +{ + /* with 8KB RAM, 0x0000-0x1b3f can be written + * we can't tell if it's a 4KB device here + */ + if (addr <= 0x1b3f) + return ((addr + len) > 0x1b40); + + /* there may be more RAM; unclear if we can write it. + * some bulk buffers may be unused, 0x1b3f-0x1f3f + * firmware can set ISODISAB for 2KB at 0x2000-0x27ff + */ + return true; +} + +/* + * return true if [addr,addr+len] includes external RAM + * for Cypress EZ-USB FX2 + */ +static bool fx2_is_external(uint32_t addr, size_t len) +{ + /* 1st 8KB for data/code, 0x0000-0x1fff */ + if (addr <= 0x1fff) + return ((addr + len) > 0x2000); + + /* and 512 for data, 0xe000-0xe1ff */ + else if (addr >= 0xe000 && addr <= 0xe1ff) + return ((addr + len) > 0xe200); + + /* otherwise, it's certainly external */ + else + return true; +} + +/* + * return true if [addr,addr+len] includes external RAM + * for Cypress EZ-USB FX2LP + */ +static bool fx2lp_is_external(uint32_t addr, size_t len) +{ + /* 1st 16KB for data/code, 0x0000-0x3fff */ + if (addr <= 0x3fff) + return ((addr + len) > 0x4000); + + /* and 512 for data, 0xe000-0xe1ff */ + else if (addr >= 0xe000 && addr <= 0xe1ff) + return ((addr + len) > 0xe200); + + /* otherwise, it's certainly external */ + else + return true; +} + + +/*****************************************************************************/ + +/* + * These are the requests (bRequest) that the bootstrap loader is expected + * to recognize. The codes are reserved by Cypress, and these values match + * what EZ-USB hardware, or "Vend_Ax" firmware (2nd stage loader) uses. + * Cypress' "a3load" is nice because it supports both FX and FX2, although + * it doesn't have the EEPROM support (subset of "Vend_Ax"). + */ +#define RW_INTERNAL 0xA0 /* hardware implements this one */ +#define RW_MEMORY 0xA3 + +/* + * Issues the specified vendor-specific write request. + */ +static int ezusb_write(libusb_device_handle *device, const char *label, + uint8_t opcode, uint32_t addr, const unsigned char *data, size_t len) +{ + int status; + + if (verbose > 1) + logerror("%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, (unsigned)len, (unsigned)len); + status = libusb_control_transfer(device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + opcode, addr & 0xFFFF, addr >> 16, + (unsigned char*)data, (uint16_t)len, 1000); + if (status != len) { + if (status < 0) + logerror("%s: %s\n", label, libusb_error_name(status)); + else + logerror("%s ==> %d\n", label, status); + } + return (status < 0) ? -EIO : 0; +} + +/* + * Issues the specified vendor-specific read request. + */ +static int ezusb_read(libusb_device_handle *device, const char *label, + uint8_t opcode, uint32_t addr, const unsigned char *data, size_t len) +{ + int status; + + if (verbose > 1) + logerror("%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, (unsigned)len, (unsigned)len); + status = libusb_control_transfer(device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + opcode, addr & 0xFFFF, addr >> 16, + (unsigned char*)data, (uint16_t)len, 1000); + if (status != len) { + if (status < 0) + logerror("%s: %s\n", label, libusb_error_name(status)); + else + logerror("%s ==> %d\n", label, status); + } + return (status < 0) ? -EIO : 0; +} + +/* + * Modifies the CPUCS register to stop or reset the CPU. + * Returns false on error. + */ +static bool ezusb_cpucs(libusb_device_handle *device, uint32_t addr, bool doRun) +{ + int status; + uint8_t data = doRun ? 0x00 : 0x01; + + if (verbose) + logerror("%s\n", data ? "stop CPU" : "reset CPU"); + status = libusb_control_transfer(device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + RW_INTERNAL, addr & 0xFFFF, addr >> 16, + &data, 1, 1000); + if ((status != 1) && + /* We may get an I/O error from libusbx as the device disappears */ + ((!doRun) || (status != LIBUSB_ERROR_IO))) + { + const char *mesg = "can't modify CPUCS"; + if (status < 0) + logerror("%s: %s\n", mesg, libusb_error_name(status)); + else + logerror("%s\n", mesg); + return false; + } else + return true; +} + +/* + * Send an FX3 jumpt to address command + * Returns false on error. + */ +static bool ezusb_fx3_jump(libusb_device_handle *device, uint32_t addr) +{ + int status; + + if (verbose) + logerror("transfer execution to Program Entry at 0x%08x\n", addr); + status = libusb_control_transfer(device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + RW_INTERNAL, addr & 0xFFFF, addr >> 16, + NULL, 0, 1000); + /* We may get an I/O error from libusbx as the device disappears */ + if ((status != 0) && (status != LIBUSB_ERROR_IO)) + { + const char *mesg = "failed to send jump command"; + if (status < 0) + logerror("%s: %s\n", mesg, libusb_error_name(status)); + else + logerror("%s\n", mesg); + return false; + } else + return true; +} + +/*****************************************************************************/ + +/* + * Parse an Intel HEX image file and invoke the poke() function on the + * various segments to implement policies such as writing to RAM (with + * a one or two stage loader setup, depending on the firmware) or to + * EEPROM (two stages required). + * + * image - the hex image file + * context - for use by poke() + * is_external - if non-null, used to check which segments go into + * external memory (writable only by software loader) + * poke - called with each memory segment; errors indicated + * by returning negative values. + * + * Caller is responsible for halting CPU as needed, such as when + * overwriting a second stage loader. + */ +static int parse_ihex(FILE *image, void *context, + bool (*is_external)(uint32_t addr, size_t len), + int (*poke) (void *context, uint32_t addr, bool external, + const unsigned char *data, size_t len)) +{ + unsigned char data[1023]; + uint32_t data_addr = 0; + size_t data_len = 0; + int rc; + int first_line = 1; + bool external = false; + + /* Read the input file as an IHEX file, and report the memory segments + * as we go. Each line holds a max of 16 bytes, but uploading is + * faster (and EEPROM space smaller) if we merge those lines into larger + * chunks. Most hex files keep memory segments together, which makes + * such merging all but free. (But it may still be worth sorting the + * hex files to make up for undesirable behavior from tools.) + * + * Note that EEPROM segments max out at 1023 bytes; the upload protocol + * allows segments of up to 64 KBytes (more than a loader could handle). + */ + for (;;) { + char buf[512], *cp; + char tmp, type; + size_t len; + unsigned idx, off; + + cp = fgets(buf, sizeof(buf), image); + if (cp == NULL) { + logerror("EOF without EOF record!\n"); + break; + } + + /* EXTENSION: "# comment-till-end-of-line", for copyrights etc */ + if (buf[0] == '#') + continue; + + if (buf[0] != ':') { + logerror("not an ihex record: %s", buf); + return -2; + } + + /* ignore any newline */ + cp = strchr(buf, '\n'); + if (cp) + *cp = 0; + + if (verbose >= 3) + logerror("** LINE: %s\n", buf); + + /* Read the length field (up to 16 bytes) */ + tmp = buf[3]; + buf[3] = 0; + len = strtoul(buf+1, NULL, 16); + buf[3] = tmp; + + /* Read the target offset (address up to 64KB) */ + tmp = buf[7]; + buf[7] = 0; + off = (int)strtoul(buf+3, NULL, 16); + buf[7] = tmp; + + /* Initialize data_addr */ + if (first_line) { + data_addr = off; + first_line = 0; + } + + /* Read the record type */ + tmp = buf[9]; + buf[9] = 0; + type = (char)strtoul(buf+7, NULL, 16); + buf[9] = tmp; + + /* If this is an EOF record, then make it so. */ + if (type == 1) { + if (verbose >= 2) + logerror("EOF on hexfile\n"); + break; + } + + if (type != 0) { + logerror("unsupported record type: %u\n", type); + return -3; + } + + if ((len * 2) + 11 > strlen(buf)) { + logerror("record too short?\n"); + return -4; + } + + /* FIXME check for _physically_ contiguous not just virtually + * e.g. on FX2 0x1f00-0x2100 includes both on-chip and external + * memory so it's not really contiguous */ + + /* flush the saved data if it's not contiguous, + * or when we've buffered as much as we can. + */ + if (data_len != 0 + && (off != (data_addr + data_len) + /* || !merge */ + || (data_len + len) > sizeof(data))) { + if (is_external) + external = is_external(data_addr, data_len); + rc = poke(context, data_addr, external, data, data_len); + if (rc < 0) + return -1; + data_addr = off; + data_len = 0; + } + + /* append to saved data, flush later */ + for (idx = 0, cp = buf+9 ; idx < len ; idx += 1, cp += 2) { + tmp = cp[2]; + cp[2] = 0; + data[data_len + idx] = (uint8_t)strtoul(cp, NULL, 16); + cp[2] = tmp; + } + data_len += len; + } + + + /* flush any data remaining */ + if (data_len != 0) { + if (is_external) + external = is_external(data_addr, data_len); + rc = poke(context, data_addr, external, data, data_len); + if (rc < 0) + return -1; + } + return 0; +} + +/* + * Parse a binary image file and write it as is to the target. + * Applies to Cypress BIX images for RAM or Cypress IIC images + * for EEPROM. + * + * image - the BIX image file + * context - for use by poke() + * is_external - if non-null, used to check which segments go into + * external memory (writable only by software loader) + * poke - called with each memory segment; errors indicated + * by returning negative values. + * + * Caller is responsible for halting CPU as needed, such as when + * overwriting a second stage loader. + */ +static int parse_bin(FILE *image, void *context, + bool (*is_external)(uint32_t addr, size_t len), int (*poke)(void *context, + uint32_t addr, bool external, const unsigned char *data, size_t len)) +{ + unsigned char data[4096]; + uint32_t data_addr = 0; + size_t data_len = 0; + int rc; + bool external = false; + + for (;;) { + data_len = fread(data, 1, 4096, image); + if (data_len == 0) + break; + if (is_external) + external = is_external(data_addr, data_len); + rc = poke(context, data_addr, external, data, data_len); + if (rc < 0) + return -1; + data_addr += (uint32_t)data_len; + } + return feof(image)?0:-1; +} + +/* + * Parse a Cypress IIC image file and invoke the poke() function on the + * various segments for writing to RAM + * + * image - the IIC image file + * context - for use by poke() + * is_external - if non-null, used to check which segments go into + * external memory (writable only by software loader) + * poke - called with each memory segment; errors indicated + * by returning negative values. + * + * Caller is responsible for halting CPU as needed, such as when + * overwriting a second stage loader. + */ +static int parse_iic(FILE *image, void *context, + bool (*is_external)(uint32_t addr, size_t len), + int (*poke)(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len)) +{ + unsigned char data[4096]; + uint32_t data_addr = 0; + size_t data_len = 0, read_len; + uint8_t block_header[4]; + int rc; + bool external = false; + long file_size, initial_pos; + + initial_pos = ftell(image); + if (initial_pos < 0) + return -1; + + fseek(image, 0L, SEEK_END); + file_size = ftell(image); + fseek(image, initial_pos, SEEK_SET); + for (;;) { + /* Ignore the trailing reset IIC data (5 bytes) */ + if (ftell(image) >= (file_size - 5)) + break; + if (fread(&block_header, 1, sizeof(block_header), image) != 4) { + logerror("unable to read IIC block header\n"); + return -1; + } + data_len = (block_header[0] << 8) + block_header[1]; + data_addr = (block_header[2] << 8) + block_header[3]; + if (data_len > sizeof(data)) { + /* If this is ever reported as an error, switch to using malloc/realloc */ + logerror("IIC data block too small - please report this error to libusbx.org\n"); + return -1; + } + read_len = fread(data, 1, data_len, image); + if (read_len != data_len) { + logerror("read error\n"); + return -1; + } + if (is_external) + external = is_external(data_addr, data_len); + rc = poke(context, data_addr, external, data, data_len); + if (rc < 0) + return -1; + } + return 0; +} + +/* the parse call will be selected according to the image type */ +static int (*parse[IMG_TYPE_MAX])(FILE *image, void *context, bool (*is_external)(uint32_t addr, size_t len), + int (*poke)(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len)) + = { parse_ihex, parse_iic, parse_bin }; + +/*****************************************************************************/ + +/* + * For writing to RAM using a first (hardware) or second (software) + * stage loader and 0xA0 or 0xA3 vendor requests + */ +typedef enum { + _undef = 0, + internal_only, /* hardware first-stage loader */ + skip_internal, /* first phase, second-stage loader */ + skip_external /* second phase, second-stage loader */ +} ram_mode; + +struct ram_poke_context { + libusb_device_handle *device; + ram_mode mode; + size_t total, count; +}; + +#define RETRY_LIMIT 5 + +static int ram_poke(void *context, uint32_t addr, bool external, + const unsigned char *data, size_t len) +{ + struct ram_poke_context *ctx = (struct ram_poke_context*)context; + int rc; + unsigned retry = 0; + + switch (ctx->mode) { + case internal_only: /* CPU should be stopped */ + if (external) { + logerror("can't write %u bytes external memory at 0x%08x\n", + (unsigned)len, addr); + return -EINVAL; + } + break; + case skip_internal: /* CPU must be running */ + if (!external) { + if (verbose >= 2) { + logerror("SKIP on-chip RAM, %u bytes at 0x%08x\n", + (unsigned)len, addr); + } + return 0; + } + break; + case skip_external: /* CPU should be stopped */ + if (external) { + if (verbose >= 2) { + logerror("SKIP external RAM, %u bytes at 0x%08x\n", + (unsigned)len, addr); + } + return 0; + } + break; + case _undef: + default: + logerror("bug\n"); + return -EDOM; + } + + ctx->total += len; + ctx->count++; + + /* Retry this till we get a real error. Control messages are not + * NAKed (just dropped) so time out means is a real problem. + */ + while ((rc = ezusb_write(ctx->device, + external ? "write external" : "write on-chip", + external ? RW_MEMORY : RW_INTERNAL, + addr, data, len)) < 0 + && retry < RETRY_LIMIT) { + if (rc != LIBUSB_ERROR_TIMEOUT) + break; + retry += 1; + } + return rc; +} + +/* + * Load a Cypress Image file into target RAM. + * See http://www.cypress.com/?docID=41351 (AN76405 PDF) for more info. + */ +static int fx3_load_ram(libusb_device_handle *device, const char *path) +{ + uint32_t dCheckSum, dExpectedCheckSum, dAddress, i, dLen, dLength; + uint32_t* dImageBuf; + unsigned char *bBuf, hBuf[4], blBuf[4], rBuf[4096]; + FILE *image; + int ret = 0; + + image = fopen(path, "rb"); + if (image == NULL) { + logerror("unable to open '%s' for input\n", path); + return -2; + } else if (verbose) + logerror("open firmware image %s for RAM upload\n", path); + + // Read header + if (fread(hBuf, sizeof(char), sizeof(hBuf), image) != sizeof(hBuf)) { + logerror("could not read image header"); + ret = -3; + goto exit; + } + + // check "CY" signature byte and format + if ((hBuf[0] != 'C') || (hBuf[1] != 'Y')) { + logerror("image doesn't have a CYpress signature\n"); + ret = -3; + goto exit; + } + + // Check bImageType + switch(hBuf[3]) { + case 0xB0: + if (verbose) + logerror("normal FW binary %s image with checksum\n", (hBuf[2]&0x01)?"data":"executable"); + break; + case 0xB1: + logerror("security binary image is not currently supported\n"); + ret = -3; + goto exit; + case 0xB2: + logerror("VID:PID image is not currently supported\n"); + ret = -3; + goto exit; + default: + logerror("invalid image type 0x%02X\n", hBuf[3]); + ret = -3; + goto exit; + } + + // Read the bootloader version + if (verbose) { + if ((ezusb_read(device, "read bootloader version", RW_INTERNAL, 0xFFFF0020, blBuf, 4) < 0)) { + logerror("Could not read bootloader version\n"); + ret = -8; + goto exit; + } + logerror("FX3 bootloader version: 0x%02X%02X%02X%02X\n", blBuf[3], blBuf[2], blBuf[1], blBuf[0]); + } + + dCheckSum = 0; + if (verbose) + logerror("writing image...\n"); + while (1) { + if ((fread(&dLength, sizeof(uint32_t), 1, image) != 1) || // read dLength + (fread(&dAddress, sizeof(uint32_t), 1, image) != 1)) { // read dAddress + logerror("could not read image"); + ret = -3; + goto exit; + } + if (dLength == 0) + break; // done + + dImageBuf = calloc(dLength, sizeof(uint32_t)); + if (dImageBuf == NULL) { + logerror("could not allocate buffer for image chunk\n"); + ret = -4; + goto exit; + } + + // read sections + if (fread(dImageBuf, sizeof(uint32_t), dLength, image) != dLength) { + logerror("could not read image"); + free(dImageBuf); + ret = -3; + goto exit; + } + for (i = 0; i < dLength; i++) + dCheckSum += dImageBuf[i]; + dLength <<= 2; // convert to Byte length + bBuf = (unsigned char*) dImageBuf; + + while (dLength > 0) { + dLen = 4096; // 4K max + if (dLen > dLength) + dLen = dLength; + if ((ezusb_write(device, "write firmware", RW_INTERNAL, dAddress, bBuf, dLen) < 0) || + (ezusb_read(device, "read firmware", RW_INTERNAL, dAddress, rBuf, dLen) < 0)) { + logerror("R/W error\n"); + free(dImageBuf); + ret = -5; + goto exit; + } + // Verify data: rBuf with bBuf + for (i = 0; i < dLen; i++) { + if (rBuf[i] != bBuf[i]) { + logerror("verify error"); + free(dImageBuf); + ret = -6; + goto exit; + } + } + + dLength -= dLen; + bBuf += dLen; + dAddress += dLen; + } + free(dImageBuf); + } + + // read pre-computed checksum data + if ((fread(&dExpectedCheckSum, sizeof(uint32_t), 1, image) != 1) || + (dCheckSum != dExpectedCheckSum)) { + logerror("checksum error\n"); + ret = -7; + goto exit; + } + + // transfer execution to Program Entry + if (!ezusb_fx3_jump(device, dAddress)) { + ret = -6; + } + +exit: + fclose(image); + return ret; +} + +/* + * Load a firmware file into target RAM. device is the open libusbx + * device, and the path is the name of the source file. Open the file, + * parse the bytes, and write them in one or two phases. + * + * If stage == 0, this uses the first stage loader, built into EZ-USB + * hardware but limited to writing on-chip memory or CPUCS. Everything + * is written during one stage, unless there's an error such as the image + * holding data that needs to be written to external memory. + * + * Otherwise, things are written in two stages. First the external + * memory is written, expecting a second stage loader to have already + * been loaded. Then file is re-parsed and on-chip memory is written. + */ +int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, int img_type, int stage) +{ + FILE *image; + uint32_t cpucs_addr; + bool (*is_external)(uint32_t off, size_t len); + struct ram_poke_context ctx; + int status; + uint8_t iic_header[8] = { 0 }; + int ret = 0; + + if (fx_type == FX_TYPE_FX3) + return fx3_load_ram(device, path); + + image = fopen(path, "rb"); + if (image == NULL) { + logerror("%s: unable to open for input.\n", path); + return -2; + } else if (verbose > 1) + logerror("open firmware image %s for RAM upload\n", path); + + if (img_type == IMG_TYPE_IIC) { + if ( (fread(iic_header, 1, sizeof(iic_header), image) != sizeof(iic_header)) + || (((fx_type == FX_TYPE_FX2LP) || (fx_type == FX_TYPE_FX2)) && (iic_header[0] != 0xC2)) + || ((fx_type == FX_TYPE_AN21) && (iic_header[0] != 0xB2)) + || ((fx_type == FX_TYPE_FX1) && (iic_header[0] != 0xB6)) ) { + logerror("IIC image does not contain executable code - cannot load to RAM.\n"); + ret = -1; + goto exit; + } + } + + /* EZ-USB original/FX and FX2 devices differ, apart from the 8051 core */ + switch(fx_type) { + case FX_TYPE_FX2LP: + cpucs_addr = 0xe600; + is_external = fx2lp_is_external; + break; + case FX_TYPE_FX2: + cpucs_addr = 0xe600; + is_external = fx2_is_external; + break; + default: + cpucs_addr = 0x7f92; + is_external = fx_is_external; + break; + } + + /* use only first stage loader? */ + if (stage == 0) { + ctx.mode = internal_only; + + /* if required, halt the CPU while we overwrite its code/data */ + if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false)) + { + ret = -1; + goto exit; + } + + /* 2nd stage, first part? loader was already uploaded */ + } else { + ctx.mode = skip_internal; + + /* let CPU run; overwrite the 2nd stage loader later */ + if (verbose) + logerror("2nd stage: write external memory\n"); + } + + /* scan the image, first (maybe only) time */ + ctx.device = device; + ctx.total = ctx.count = 0; + status = parse[img_type](image, &ctx, is_external, ram_poke); + if (status < 0) { + logerror("unable to upload %s\n", path); + ret = status; + goto exit; + } + + /* second part of 2nd stage: rescan */ + // TODO: what should we do for non HEX images there? + if (stage) { + ctx.mode = skip_external; + + /* if needed, halt the CPU while we overwrite the 1st stage loader */ + if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false)) + { + ret = -1; + goto exit; + } + + /* at least write the interrupt vectors (at 0x0000) for reset! */ + rewind(image); + if (verbose) + logerror("2nd stage: write on-chip memory\n"); + status = parse_ihex(image, &ctx, is_external, ram_poke); + if (status < 0) { + logerror("unable to completely upload %s\n", path); + ret = status; + goto exit; + } + } + + if (verbose) + logerror("... WROTE: %d bytes, %d segments, avg %d\n", + (int)ctx.total, (int)ctx.count, (int)(ctx.total/ctx.count)); + + /* if required, reset the CPU so it runs what we just uploaded */ + if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, true)) + ret = -1; + +exit: + fclose(image); + return ret; +} diff --git a/libusbx-1.0.18/examples/ezusb.h b/libusbx-1.0.18/examples/ezusb.h new file mode 100644 index 0000000..cd0776d --- /dev/null +++ b/libusbx-1.0.18/examples/ezusb.h @@ -0,0 +1,120 @@ +#ifndef __ezusb_H +#define __ezusb_H +/* + * Copyright © 2001 Stephen Williams (steve@icarus.com) + * Copyright © 2002 David Brownell (dbrownell@users.sourceforge.net) + * Copyright © 2013 Federico Manzan (f.manzan@gmail.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(_MSC_VER) +#include <stdbool.h> +#else +#define __attribute__(x) +#if !defined(bool) +#define bool int +#endif +#if !defined(true) +#define true (1 == 1) +#endif +#if !defined(false) +#define false (!true) +#endif +#if defined(_PREFAST_) +#pragma warning(disable:28193) +#endif +#endif + +#define FX_TYPE_UNDEFINED -1 +#define FX_TYPE_AN21 0 /* Original AnchorChips parts */ +#define FX_TYPE_FX1 1 /* Updated Cypress versions */ +#define FX_TYPE_FX2 2 /* USB 2.0 versions */ +#define FX_TYPE_FX2LP 3 /* Updated FX2 */ +#define FX_TYPE_FX3 4 /* USB 3.0 versions */ +#define FX_TYPE_MAX 5 +#define FX_TYPE_NAMES { "an21", "fx", "fx2", "fx2lp", "fx3" } + +#define IMG_TYPE_UNDEFINED -1 +#define IMG_TYPE_HEX 0 /* Intel HEX */ +#define IMG_TYPE_IIC 1 /* Cypress 8051 IIC */ +#define IMG_TYPE_BIX 2 /* Cypress 8051 BIX */ +#define IMG_TYPE_IMG 3 /* Cypress IMG format */ +#define IMG_TYPE_MAX 4 +#define IMG_TYPE_NAMES { "Intel HEX", "Cypress 8051 IIC", "Cypress 8051 BIX", "Cypress IMG format" } + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Automatically identified devices (VID, PID, type, designation). + * TODO: Could use some validation. Also where's the FX2? + */ +typedef struct { + uint16_t vid; + uint16_t pid; + int type; + const char* designation; +} fx_known_device; + +#define FX_KNOWN_DEVICES { \ + { 0x0547, 0x2122, FX_TYPE_AN21, "Cypress EZ-USB (2122S)" },\ + { 0x0547, 0x2125, FX_TYPE_AN21, "Cypress EZ-USB (2121S/2125S)" },\ + { 0x0547, 0x2126, FX_TYPE_AN21, "Cypress EZ-USB (2126S)" },\ + { 0x0547, 0x2131, FX_TYPE_AN21, "Cypress EZ-USB (2131Q/2131S/2135S)" },\ + { 0x0547, 0x2136, FX_TYPE_AN21, "Cypress EZ-USB (2136S)" },\ + { 0x0547, 0x2225, FX_TYPE_AN21, "Cypress EZ-USB (2225)" },\ + { 0x0547, 0x2226, FX_TYPE_AN21, "Cypress EZ-USB (2226)" },\ + { 0x0547, 0x2235, FX_TYPE_AN21, "Cypress EZ-USB (2235)" },\ + { 0x0547, 0x2236, FX_TYPE_AN21, "Cypress EZ-USB (2236)" },\ + { 0x04b4, 0x6473, FX_TYPE_FX1, "Cypress EZ-USB FX1" },\ + { 0x04b4, 0x8613, FX_TYPE_FX2LP, "Cypress EZ-USB FX2LP (68013A/68014A/68015A/68016A)" }, \ + { 0x04b4, 0x00f3, FX_TYPE_FX3, "Cypress FX3" },\ +} + +/* + * This function uploads the firmware from the given file into RAM. + * Stage == 0 means this is a single stage load (or the first of + * two stages). Otherwise it's the second of two stages; the + * caller having preloaded the second stage loader. + * + * The target processor is reset at the end of this upload. + */ +extern int ezusb_load_ram(libusb_device_handle *device, + const char *path, int fx_type, int img_type, int stage); + +/* + * This function uploads the firmware from the given file into EEPROM. + * This uses the right CPUCS address to terminate the EEPROM load with + * a reset command where FX parts behave differently than FX2 ones. + * The configuration byte is as provided here (zero for an21xx parts) + * and the EEPROM type is set so that the microcontroller will boot + * from it. + * + * The caller must have preloaded a second stage loader that knows + * how to respond to the EEPROM write request. + */ +extern int ezusb_load_eeprom(libusb_device_handle *device, + const char *path, int fx_type, int img_type, int config); + +/* Verbosity level (default 1). Can be increased or decreased with options v/q */ +extern int verbose; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libusbx-1.0.18/examples/fxload.c b/libusbx-1.0.18/examples/fxload.c new file mode 100644 index 0000000..14226ca --- /dev/null +++ b/libusbx-1.0.18/examples/fxload.c @@ -0,0 +1,287 @@ +/* + * Copyright © 2001 Stephen Williams (steve@icarus.com) + * Copyright © 2001-2002 David Brownell (dbrownell@users.sourceforge.net) + * Copyright © 2008 Roger Williams (rawqux@users.sourceforge.net) + * Copyright © 2012 Pete Batard (pete@akeo.ie) + * Copyright © 2013 Federico Manzan (f.manzan@gmail.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdarg.h> +#include <sys/types.h> +#include <getopt.h> + +#include "libusb.h" +#include "ezusb.h" + +#if !defined(_WIN32) || defined(__CYGWIN__ ) +#include <syslog.h> +static bool dosyslog = false; +#include <strings.h> +#define _stricmp strcasecmp +#endif + +#ifndef FXLOAD_VERSION +#define FXLOAD_VERSION (__DATE__ " (libusbx)") +#endif + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +void logerror(const char *format, ...) + __attribute__ ((format (__printf__, 1, 2))); + +void logerror(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + +#if !defined(_WIN32) || defined(__CYGWIN__ ) + if (dosyslog) + vsyslog(LOG_ERR, format, ap); + else +#endif + vfprintf(stderr, format, ap); + va_end(ap); +} + +static int print_usage(int error_code) { + fprintf(stderr, "\nUsage: fxload [-v] [-V] [-t type] [-d vid:pid] [-p bus,addr] -i firmware\n"); + fprintf(stderr, " -i <path> -- Firmware to upload\n"); + fprintf(stderr, " -t <type> -- Target type: an21, fx, fx2, fx2lp, fx3\n"); + fprintf(stderr, " -d <vid:pid> -- Target device, as an USB VID:PID\n"); + fprintf(stderr, " -p <bus,addr> -- Target device, as a libusbx bus number and device address path\n"); + fprintf(stderr, " -v -- Increase verbosity\n"); + fprintf(stderr, " -q -- Decrease verbosity (silent mode)\n"); + fprintf(stderr, " -V -- Print program version\n"); + return error_code; +} + +#define FIRMWARE 0 +#define LOADER 1 +int main(int argc, char*argv[]) +{ + fx_known_device known_device[] = FX_KNOWN_DEVICES; + const char *path[] = { NULL, NULL }; + const char *device_id = NULL; + const char *device_path = getenv("DEVICE"); + const char *type = NULL; + const char *fx_name[FX_TYPE_MAX] = FX_TYPE_NAMES; + const char *ext, *img_name[] = IMG_TYPE_NAMES; + int fx_type = FX_TYPE_UNDEFINED, img_type[ARRAYSIZE(path)]; + int i, j, opt, status; + unsigned vid = 0, pid = 0; + unsigned busnum = 0, devaddr = 0, _busnum, _devaddr; + libusb_device *dev, **devs; + libusb_device_handle *device = NULL; + struct libusb_device_descriptor desc; + + while ((opt = getopt(argc, argv, "qvV?hd:p:i:I:t:")) != EOF) + switch (opt) { + + case 'd': + device_id = optarg; + if (sscanf(device_id, "%x:%x" , &vid, &pid) != 2 ) { + fputs ("please specify VID & PID as \"vid:pid\" in hexadecimal format\n", stderr); + return -1; + } + break; + + case 'p': + device_path = optarg; + if (sscanf(device_path, "%u,%u", &busnum, &devaddr) != 2 ) { + fputs ("please specify bus number & device number as \"bus,dev\" in decimal format\n", stderr); + return -1; + } + break; + + case 'i': + case 'I': + path[FIRMWARE] = optarg; + break; + + case 'V': + puts(FXLOAD_VERSION); + return 0; + + case 't': + type = optarg; + break; + + case 'v': + verbose++; + break; + + case 'q': + verbose--; + break; + + case '?': + case 'h': + default: + return print_usage(-1); + + } + + if (path[FIRMWARE] == NULL) { + logerror("no firmware specified!\n"); + return print_usage(-1); + } + if ((device_id != NULL) && (device_path != NULL)) { + logerror("only one of -d or -a can be specified\n"); + return print_usage(-1); + } + + /* determine the target type */ + if (type != NULL) { + for (i=0; i<FX_TYPE_MAX; i++) { + if (strcmp(type, fx_name[i]) == 0) { + fx_type = i; + break; + } + } + if (i >= FX_TYPE_MAX) { + logerror("illegal microcontroller type: %s\n", type); + return print_usage(-1); + } + } + + /* open the device using libusbx */ + status = libusb_init(NULL); + if (status < 0) { + logerror("libusb_init() failed: %s\n", libusb_error_name(status)); + return -1; + } + libusb_set_debug(NULL, verbose); + + /* try to pick up missing parameters from known devices */ + if ((type == NULL) || (device_id == NULL) || (device_path != NULL)) { + if (libusb_get_device_list(NULL, &devs) < 0) { + logerror("libusb_get_device_list() failed: %s\n", libusb_error_name(status)); + goto err; + } + for (i=0; (dev=devs[i]) != NULL; i++) { + _busnum = libusb_get_bus_number(dev); + _devaddr = libusb_get_device_address(dev); + if ((type != NULL) && (device_path != NULL)) { + // if both a type and bus,addr were specified, we just need to find our match + if ((libusb_get_bus_number(dev) == busnum) && (libusb_get_device_address(dev) == devaddr)) + break; + } else { + status = libusb_get_device_descriptor(dev, &desc); + if (status >= 0) { + if (verbose >= 3) { + logerror("examining %04x:%04x (%d,%d)\n", + desc.idVendor, desc.idProduct, _busnum, _devaddr); + } + for (j=0; j<ARRAYSIZE(known_device); j++) { + if ((desc.idVendor == known_device[j].vid) + && (desc.idProduct == known_device[j].pid)) { + if (// nothing was specified + ((type == NULL) && (device_id == NULL) && (device_path == NULL)) || + // vid:pid was specified and we have a match + ((type == NULL) && (device_id != NULL) && (vid == desc.idVendor) && (pid == desc.idProduct)) || + // bus,addr was specified and we have a match + ((type == NULL) && (device_path != NULL) && (busnum == _busnum) && (devaddr == _devaddr)) || + // type was specified and we have a match + ((type != NULL) && (device_id == NULL) && (device_path == NULL) && (fx_type == known_device[j].type)) ) { + fx_type = known_device[j].type; + vid = desc.idVendor; + pid = desc.idProduct; + busnum = _busnum; + devaddr = _devaddr; + break; + } + } + } + if (j < ARRAYSIZE(known_device)) { + if (verbose) + logerror("found device '%s' [%04x:%04x] (%d,%d)\n", + known_device[j].designation, vid, pid, busnum, devaddr); + break; + } + } + } + } + if (dev == NULL) { + libusb_free_device_list(devs, 1); + logerror("could not find a known device - please specify type and/or vid:pid and/or bus,dev\n"); + return print_usage(-1); + } + status = libusb_open(dev, &device); + if (status < 0) { + logerror("libusb_open() failed: %s\n", libusb_error_name(status)); + goto err; + } + libusb_free_device_list(devs, 1); + } else if (device_id != NULL) { + device = libusb_open_device_with_vid_pid(NULL, (uint16_t)vid, (uint16_t)pid); + if (device == NULL) { + logerror("libusb_open() failed\n"); + goto err; + } + } + + /* We need to claim the first interface */ + libusb_set_auto_detach_kernel_driver(device, 1); + status = libusb_claim_interface(device, 0); + if (status != LIBUSB_SUCCESS) { + logerror("libusb_claim_interface failed: %s\n", libusb_error_name(status)); + goto err; + } + + if (verbose) + logerror("microcontroller type: %s\n", fx_name[fx_type]); + + for (i=0; i<ARRAYSIZE(path); i++) { + if (path[i] != NULL) { + ext = path[i] + strlen(path[i]) - 4; + if ((_stricmp(ext, ".hex") == 0) || (strcmp(ext, ".ihx") == 0)) + img_type[i] = IMG_TYPE_HEX; + else if (_stricmp(ext, ".iic") == 0) + img_type[i] = IMG_TYPE_IIC; + else if (_stricmp(ext, ".bix") == 0) + img_type[i] = IMG_TYPE_BIX; + else if (_stricmp(ext, ".img") == 0) + img_type[i] = IMG_TYPE_IMG; + else { + logerror("%s is not a recognized image type\n", path[i]); + goto err; + } + } + if (verbose && path[i] != NULL) + logerror("%s: type %s\n", path[i], img_name[img_type[i]]); + } + + /* single stage, put into internal memory */ + if (verbose > 1) + logerror("single stage: load on-chip memory\n"); + status = ezusb_load_ram(device, path[FIRMWARE], fx_type, img_type[FIRMWARE], 0); + + libusb_release_interface(device, 0); + libusb_close(device); + libusb_exit(NULL); + return status; +err: + libusb_exit(NULL); + return -1; +} diff --git a/libusbx-1.0.18/examples/getopt/getopt.c b/libusbx-1.0.18/examples/getopt/getopt.c new file mode 100644 index 0000000..b7f26eb --- /dev/null +++ b/libusbx-1.0.18/examples/getopt/getopt.c @@ -0,0 +1,1060 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. + Ditto for AIX 3.2 and <stdlib.h>. */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include <gnu-versions.h> +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include <stdlib.h> +# include <unistd.h> +#endif /* GNU C library. */ + +#ifdef VMS +# include <unixlib.h> +# if HAVE_STRING_H - 0 +# include <string.h> +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include <libintl.h> +# ifndef _ +# define _(msgid) gettext (msgid) +# endif +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include <string.h> +# define my_index strchr +#else + +# if HAVE_STRING_H +# include <string.h> +# else +# include <strings.h> +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +#ifdef _MSC_VER +// DDK will complain if you don't use the stdlib defined getenv +#include <stdlib.h> +#else +extern char *getenv (); +#endif +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL + && argc == __libc_argc && argv == __libc_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (print_errors) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p != NULL && p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/libusbx-1.0.18/examples/getopt/getopt.h b/libusbx-1.0.18/examples/getopt/getopt.h new file mode 100644 index 0000000..a1b8dd6 --- /dev/null +++ b/libusbx-1.0.18/examples/getopt/getopt.h @@ -0,0 +1,180 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include <features.h>, but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include <ctype.h>, which will pull in <features.h> for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include <ctype.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +# if (defined __STDC__ && __STDC__) || defined __cplusplus + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if (defined __STDC__ && __STDC__) || defined __cplusplus +# ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int __argc, char *const *__argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ +extern int getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt +extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int getopt (); +# ifndef __need_getopt +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/libusbx-1.0.18/examples/getopt/getopt1.c b/libusbx-1.0.18/examples/getopt/getopt1.c new file mode 100644 index 0000000..22a7efb --- /dev/null +++ b/libusbx-1.0.18/examples/getopt/getopt1.c @@ -0,0 +1,188 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include <gnu-versions.h> +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include <stdlib.h> +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include <stdio.h> + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/libusbx-1.0.18/examples/hotplugtest.c b/libusbx-1.0.18/examples/hotplugtest.c new file mode 100644 index 0000000..368011b --- /dev/null +++ b/libusbx-1.0.18/examples/hotplugtest.c @@ -0,0 +1,104 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * libusb example program for hotplug API + * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.ccom> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "libusb.h" + +int done = 0; +libusb_device_handle *handle; + +static int LIBUSB_CALL hotplug_callback(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) +{ + struct libusb_device_descriptor desc; + int rc; + + rc = libusb_get_device_descriptor(dev, &desc); + if (LIBUSB_SUCCESS != rc) { + fprintf (stderr, "Error getting device descriptor\n"); + } + + printf ("Device attached: %04x:%04x\n", desc.idVendor, desc.idProduct); + + libusb_open (dev, &handle); + + done++; + + return 0; +} + +static int LIBUSB_CALL hotplug_callback_detach(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) +{ + printf ("Device detached\n"); + + libusb_close (handle); + + done++; + return 0; +} + +int main(int argc, char *argv[]) +{ + libusb_hotplug_callback_handle hp[2]; + int product_id, vendor_id, class_id; + int rc; + + vendor_id = (argc > 1) ? strtol (argv[1], NULL, 0) : 0x045a; + product_id = (argc > 2) ? strtol (argv[2], NULL, 0) : 0x5005; + class_id = (argc > 3) ? strtol (argv[3], NULL, 0) : LIBUSB_HOTPLUG_MATCH_ANY; + + rc = libusb_init (NULL); + if (rc < 0) + { + printf("failed to initialise libusb: %s\n", libusb_error_name(rc)); + return EXIT_FAILURE; + } + + if (!libusb_has_capability (LIBUSB_CAP_HAS_HOTPLUG)) { + printf ("Hotplug capabilites are not supported on this platform\n"); + libusb_exit (NULL); + return EXIT_FAILURE; + } + + rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 0, vendor_id, + product_id, class_id, hotplug_callback, NULL, &hp[0]); + if (LIBUSB_SUCCESS != rc) { + fprintf (stderr, "Error registering callback 0\n"); + libusb_exit (NULL); + return EXIT_FAILURE; + } + + rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, vendor_id, + product_id,class_id, hotplug_callback_detach, NULL, &hp[1]); + if (LIBUSB_SUCCESS != rc) { + fprintf (stderr, "Error registering callback 1\n"); + libusb_exit (NULL); + return EXIT_FAILURE; + } + + while (done < 2) { + rc = libusb_handle_events (NULL); + if (rc < 0) + printf("libusb_handle_events() failed: %s\n", libusb_error_name(rc)); + } + + libusb_exit (NULL); +} diff --git a/libusbx-1.0.18/examples/listdevs.c b/libusbx-1.0.18/examples/listdevs.c new file mode 100644 index 0000000..31ad26e --- /dev/null +++ b/libusbx-1.0.18/examples/listdevs.c @@ -0,0 +1,71 @@ +/* + * libusbx example program to list devices on the bus + * Copyright © 2007 Daniel Drake <dsd@gentoo.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> + +#include "libusb.h" + +static void print_devs(libusb_device **devs) +{ + libusb_device *dev; + int i = 0, j = 0; + uint8_t path[8]; + + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) { + fprintf(stderr, "failed to get device descriptor"); + return; + } + + printf("%04x:%04x (bus %d, device %d)", + desc.idVendor, desc.idProduct, + libusb_get_bus_number(dev), libusb_get_device_address(dev)); + + r = libusb_get_port_numbers(dev, path, sizeof(path)); + if (r > 0) { + printf(" path: %d", path[0]); + for (j = 1; j < r; j++) + printf(".%d", path[j]); + } + printf("\n"); + } +} + +int main(void) +{ + libusb_device **devs; + int r; + ssize_t cnt; + + r = libusb_init(NULL); + if (r < 0) + return r; + + cnt = libusb_get_device_list(NULL, &devs); + if (cnt < 0) + return (int) cnt; + + print_devs(devs); + libusb_free_device_list(devs, 1); + + libusb_exit(NULL); + return 0; +} diff --git a/libusbx-1.0.18/examples/sam3u_benchmark.c b/libusbx-1.0.18/examples/sam3u_benchmark.c new file mode 100644 index 0000000..99d6b0f --- /dev/null +++ b/libusbx-1.0.18/examples/sam3u_benchmark.c @@ -0,0 +1,193 @@ +/* + * libusb example program to measure Atmel SAM3U isochronous performance + * Copyright (C) 2012 Harald Welte <laforge@gnumonks.org> + * + * Copied with the author's permission under LGPL-2.1 from + * http://git.gnumonks.org/cgi-bin/gitweb.cgi?p=sam3u-tests.git;a=blob;f=usb-benchmark-project/host/benchmark.c;h=74959f7ee88f1597286cd435f312a8ff52c56b7e + * + * An Atmel SAM3U test firmware is also available in the above repository. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> + +#include <libusb.h> + + +#define EP_DATA_IN 0x82 +#define EP_ISO_IN 0x86 + +static int do_exit = 0; +static struct libusb_device_handle *devh = NULL; + +static unsigned long num_bytes = 0, num_xfer = 0; +static struct timeval tv_start; + +static void LIBUSB_CALL cb_xfr(struct libusb_transfer *xfr) +{ + unsigned int i; + + if (xfr->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "transfer status %d\n", xfr->status); + libusb_free_transfer(xfr); + exit(3); + } + + if (xfr->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + for (i = 0; i < xfr->num_iso_packets; i++) { + struct libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i]; + + if (pack->status != LIBUSB_TRANSFER_COMPLETED) { + fprintf(stderr, "Error: pack %u status %d\n", i, pack->status); + exit(5); + } + + printf("pack%u length:%u, actual_length:%u\n", i, pack->length, pack->actual_length); + } + } + + printf("length:%u, actual_length:%u\n", xfr->length, xfr->actual_length); + for (i = 0; i < xfr->actual_length; i++) { + printf("%02x", xfr->buffer[i]); + if (i % 16) + printf("\n"); + else if (i % 8) + printf(" "); + else + printf(" "); + } + num_bytes += xfr->actual_length; + num_xfer++; + + if (libusb_submit_transfer(xfr) < 0) { + fprintf(stderr, "error re-submitting URB\n"); + exit(1); + } +} + +static int benchmark_in(uint8_t ep) +{ + static uint8_t buf[2048]; + static struct libusb_transfer *xfr; + int num_iso_pack = 0; + + if (ep == EP_ISO_IN) + num_iso_pack = 16; + + xfr = libusb_alloc_transfer(num_iso_pack); + if (!xfr) + return -ENOMEM; + + if (ep == EP_ISO_IN) { + libusb_fill_iso_transfer(xfr, devh, ep, buf, + sizeof(buf), num_iso_pack, cb_xfr, NULL, 0); + libusb_set_iso_packet_lengths(xfr, sizeof(buf)/num_iso_pack); + } else + libusb_fill_bulk_transfer(xfr, devh, ep, buf, + sizeof(buf), cb_xfr, NULL, 0); + + gettimeofday(&tv_start, NULL); + + /* NOTE: To reach maximum possible performance the program must + * submit *multiple* transfers here, not just one. + * + * When only one transfer is submitted there is a gap in the bus + * schedule from when the transfer completes until a new transfer + * is submitted by the callback. This causes some jitter for + * isochronous transfers and loss of throughput for bulk transfers. + * + * This is avoided by queueing multiple transfers in advance, so + * that the host controller is always kept busy, and will schedule + * more transfers on the bus while the callback is running for + * transfers which have completed on the bus. + */ + + return libusb_submit_transfer(xfr); +} + +static void measure(void) +{ + struct timeval tv_stop; + unsigned int diff_msec; + + gettimeofday(&tv_stop, NULL); + + diff_msec = (tv_stop.tv_sec - tv_start.tv_sec)*1000; + diff_msec += (tv_stop.tv_usec - tv_start.tv_usec)/1000; + + printf("%lu transfers (total %lu bytes) in %u miliseconds => %lu bytes/sec\n", + num_xfer, num_bytes, diff_msec, (num_bytes*1000)/diff_msec); +} + +static void sig_hdlr(int signum) +{ + switch (signum) { + case SIGINT: + measure(); + do_exit = 1; + break; + } +} + +int main(int argc, char **argv) +{ + int rc; + struct sigaction sigact; + + sigact.sa_handler = sig_hdlr; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGINT, &sigact, NULL); + + rc = libusb_init(NULL); + if (rc < 0) { + fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc)); + exit(1); + } + + devh = libusb_open_device_with_vid_pid(NULL, 0x16c0, 0x0763); + if (!devh) { + fprintf(stderr, "Error finding USB device\n"); + goto out; + } + + rc = libusb_claim_interface(devh, 2); + if (rc < 0) { + fprintf(stderr, "Error claiming interface: %s\n", libusb_error_name(rc)); + goto out; + } + + benchmark_in(EP_ISO_IN); + + while (!do_exit) { + rc = libusb_handle_events(NULL); + if (rc != LIBUSB_SUCCESS) + break; + } + + /* Measurement has already been done by the signal handler. */ + + libusb_release_interface(devh, 0); +out: + if (devh) + libusb_close(devh); + libusb_exit(NULL); + return rc; +} diff --git a/libusbx-1.0.18/examples/xusb.c b/libusbx-1.0.18/examples/xusb.c new file mode 100644 index 0000000..56386df --- /dev/null +++ b/libusbx-1.0.18/examples/xusb.c @@ -0,0 +1,1109 @@ +/* + * xusb: Generic USB test program + * Copyright © 2009-2012 Pete Batard <pete@akeo.ie> + * Contributions to Mass Storage by Alan Stern. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "libusb.h" + +#if defined(_WIN32) +#define msleep(msecs) Sleep(msecs) +#else +#include <unistd.h> +#define msleep(msecs) usleep(1000*msecs) +#endif + +#if !defined(bool) +#define bool int +#endif +#if !defined(true) +#define true (1 == 1) +#endif +#if !defined(false) +#define false (!true) +#endif + +// Future versions of libusbx will use usb_interface instead of interface +// in libusb_config_descriptor => catter for that +#define usb_interface interface + +// Global variables +static bool binary_dump = false; +static bool extra_info = false; +static bool force_device_request = false; // For WCID descriptor queries +static const char* binary_name = NULL; + +static int perr(char const *format, ...) +{ + va_list args; + int r; + + va_start (args, format); + r = vfprintf(stderr, format, args); + va_end(args); + + return r; +} + +#define ERR_EXIT(errcode) do { perr(" %s\n", libusb_strerror((enum libusb_error)errcode)); return -1; } while (0) +#define CALL_CHECK(fcall) do { r=fcall; if (r < 0) ERR_EXIT(r); } while (0); +#define B(x) (((x)!=0)?1:0) +#define be_to_int32(buf) (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|(buf)[3]) + +#define RETRY_MAX 5 +#define REQUEST_SENSE_LENGTH 0x12 +#define INQUIRY_LENGTH 0x24 +#define READ_CAPACITY_LENGTH 0x08 + +// HID Class-Specific Requests values. See section 7.2 of the HID specifications +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B +#define HID_REPORT_TYPE_INPUT 0x01 +#define HID_REPORT_TYPE_OUTPUT 0x02 +#define HID_REPORT_TYPE_FEATURE 0x03 + +// Mass Storage Requests values. See section 3 of the Bulk-Only Mass Storage Class specifications +#define BOMS_RESET 0xFF +#define BOMS_GET_MAX_LUN 0xFE + +// Section 5.1: Command Block Wrapper (CBW) +struct command_block_wrapper { + uint8_t dCBWSignature[4]; + uint32_t dCBWTag; + uint32_t dCBWDataTransferLength; + uint8_t bmCBWFlags; + uint8_t bCBWLUN; + uint8_t bCBWCBLength; + uint8_t CBWCB[16]; +}; + +// Section 5.2: Command Status Wrapper (CSW) +struct command_status_wrapper { + uint8_t dCSWSignature[4]; + uint32_t dCSWTag; + uint32_t dCSWDataResidue; + uint8_t bCSWStatus; +}; + +static uint8_t cdb_length[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 06,06,06,06,06,06,06,06,06,06,06,06,06,06,06,06, // 0 + 06,06,06,06,06,06,06,06,06,06,06,06,06,06,06,06, // 1 + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 2 + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 3 + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 4 + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 5 + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // 6 + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // 7 + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, // 8 + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, // 9 + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, // A + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, // B + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // C + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // D + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // E + 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00, // F +}; + +static enum test_type { + USE_GENERIC, + USE_PS3, + USE_XBOX, + USE_SCSI, + USE_HID, +} test_mode; +static uint16_t VID, PID; + +static void display_buffer_hex(unsigned char *buffer, unsigned size) +{ + unsigned i, j, k; + + for (i=0; i<size; i+=16) { + printf("\n %08x ", i); + for(j=0,k=0; k<16; j++,k++) { + if (i+j < size) { + printf("%02x", buffer[i+j]); + } else { + printf(" "); + } + printf(" "); + } + printf(" "); + for(j=0,k=0; k<16; j++,k++) { + if (i+j < size) { + if ((buffer[i+j] < 32) || (buffer[i+j] > 126)) { + printf("."); + } else { + printf("%c", buffer[i+j]); + } + } + } + } + printf("\n" ); +} + +static char* uuid_to_string(const uint8_t* uuid) +{ + static char uuid_string[40]; + if (uuid == NULL) return NULL; + sprintf(uuid_string, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + return uuid_string; +} + +// The PS3 Controller is really a HID device that got its HID Report Descriptors +// removed by Sony +static int display_ps3_status(libusb_device_handle *handle) +{ + int r; + uint8_t input_report[49]; + uint8_t master_bt_address[8]; + uint8_t device_bt_address[18]; + + // Get the controller's bluetooth address of its master device + CALL_CHECK(libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + HID_GET_REPORT, 0x03f5, 0, master_bt_address, sizeof(master_bt_address), 100)); + printf("\nMaster's bluetooth address: %02X:%02X:%02X:%02X:%02X:%02X\n", master_bt_address[2], master_bt_address[3], + master_bt_address[4], master_bt_address[5], master_bt_address[6], master_bt_address[7]); + + // Get the controller's bluetooth address + CALL_CHECK(libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + HID_GET_REPORT, 0x03f2, 0, device_bt_address, sizeof(device_bt_address), 100)); + printf("\nMaster's bluetooth address: %02X:%02X:%02X:%02X:%02X:%02X\n", device_bt_address[4], device_bt_address[5], + device_bt_address[6], device_bt_address[7], device_bt_address[8], device_bt_address[9]); + + // Get the status of the controller's buttons via its HID report + printf("\nReading PS3 Input Report...\n"); + CALL_CHECK(libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + HID_GET_REPORT, (HID_REPORT_TYPE_INPUT<<8)|0x01, 0, input_report, sizeof(input_report), 1000)); + switch(input_report[2]){ /** Direction pad plus start, select, and joystick buttons */ + case 0x01: + printf("\tSELECT pressed\n"); + break; + case 0x02: + printf("\tLEFT 3 pressed\n"); + break; + case 0x04: + printf("\tRIGHT 3 pressed\n"); + break; + case 0x08: + printf("\tSTART presed\n"); + break; + case 0x10: + printf("\tUP pressed\n"); + break; + case 0x20: + printf("\tRIGHT pressed\n"); + break; + case 0x40: + printf("\tDOWN pressed\n"); + break; + case 0x80: + printf("\tLEFT pressed\n"); + break; + } + switch(input_report[3]){ /** Shapes plus top right and left buttons */ + case 0x01: + printf("\tLEFT 2 pressed\n"); + break; + case 0x02: + printf("\tRIGHT 2 pressed\n"); + break; + case 0x04: + printf("\tLEFT 1 pressed\n"); + break; + case 0x08: + printf("\tRIGHT 1 presed\n"); + break; + case 0x10: + printf("\tTRIANGLE pressed\n"); + break; + case 0x20: + printf("\tCIRCLE pressed\n"); + break; + case 0x40: + printf("\tCROSS pressed\n"); + break; + case 0x80: + printf("\tSQUARE pressed\n"); + break; + } + printf("\tPS button: %d\n", input_report[4]); + printf("\tLeft Analog (X,Y): (%d,%d)\n", input_report[6], input_report[7]); + printf("\tRight Analog (X,Y): (%d,%d)\n", input_report[8], input_report[9]); + printf("\tL2 Value: %d\tR2 Value: %d\n", input_report[18], input_report[19]); + printf("\tL1 Value: %d\tR1 Value: %d\n", input_report[20], input_report[21]); + printf("\tRoll (x axis): %d Yaw (y axis): %d Pitch (z axis) %d\n", + //(((input_report[42] + 128) % 256) - 128), + (int8_t)(input_report[42]), + (int8_t)(input_report[44]), + (int8_t)(input_report[46])); + printf("\tAcceleration: %d\n\n", (int8_t)(input_report[48])); + return 0; +} +// The XBOX Controller is really a HID device that got its HID Report Descriptors +// removed by Microsoft. +// Input/Output reports described at http://euc.jp/periphs/xbox-controller.ja.html +static int display_xbox_status(libusb_device_handle *handle) +{ + int r; + uint8_t input_report[20]; + printf("\nReading XBox Input Report...\n"); + CALL_CHECK(libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + HID_GET_REPORT, (HID_REPORT_TYPE_INPUT<<8)|0x00, 0, input_report, 20, 1000)); + printf(" D-pad: %02X\n", input_report[2]&0x0F); + printf(" Start:%d, Back:%d, Left Stick Press:%d, Right Stick Press:%d\n", B(input_report[2]&0x10), B(input_report[2]&0x20), + B(input_report[2]&0x40), B(input_report[2]&0x80)); + // A, B, X, Y, Black, White are pressure sensitive + printf(" A:%d, B:%d, X:%d, Y:%d, White:%d, Black:%d\n", input_report[4], input_report[5], + input_report[6], input_report[7], input_report[9], input_report[8]); + printf(" Left Trigger: %d, Right Trigger: %d\n", input_report[10], input_report[11]); + printf(" Left Analog (X,Y): (%d,%d)\n", (int16_t)((input_report[13]<<8)|input_report[12]), + (int16_t)((input_report[15]<<8)|input_report[14])); + printf(" Right Analog (X,Y): (%d,%d)\n", (int16_t)((input_report[17]<<8)|input_report[16]), + (int16_t)((input_report[19]<<8)|input_report[18])); + return 0; +} + +static int set_xbox_actuators(libusb_device_handle *handle, uint8_t left, uint8_t right) +{ + int r; + uint8_t output_report[6]; + + printf("\nWriting XBox Controller Output Report...\n"); + + memset(output_report, 0, sizeof(output_report)); + output_report[1] = sizeof(output_report); + output_report[3] = left; + output_report[5] = right; + + CALL_CHECK(libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + HID_SET_REPORT, (HID_REPORT_TYPE_OUTPUT<<8)|0x00, 0, output_report, 06, 1000)); + return 0; +} + +static int send_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint, uint8_t lun, + uint8_t *cdb, uint8_t direction, int data_length, uint32_t *ret_tag) +{ + static uint32_t tag = 1; + uint8_t cdb_len; + int i, r, size; + struct command_block_wrapper cbw; + + if (cdb == NULL) { + return -1; + } + + if (endpoint & LIBUSB_ENDPOINT_IN) { + perr("send_mass_storage_command: cannot send command on IN endpoint\n"); + return -1; + } + + cdb_len = cdb_length[cdb[0]]; + if ((cdb_len == 0) || (cdb_len > sizeof(cbw.CBWCB))) { + perr("send_mass_storage_command: don't know how to handle this command (%02X, length %d)\n", + cdb[0], cdb_len); + return -1; + } + + memset(&cbw, 0, sizeof(cbw)); + cbw.dCBWSignature[0] = 'U'; + cbw.dCBWSignature[1] = 'S'; + cbw.dCBWSignature[2] = 'B'; + cbw.dCBWSignature[3] = 'C'; + *ret_tag = tag; + cbw.dCBWTag = tag++; + cbw.dCBWDataTransferLength = data_length; + cbw.bmCBWFlags = direction; + cbw.bCBWLUN = lun; + // Subclass is 1 or 6 => cdb_len + cbw.bCBWCBLength = cdb_len; + memcpy(cbw.CBWCB, cdb, cdb_len); + + i = 0; + do { + // The transfer length must always be exactly 31 bytes. + r = libusb_bulk_transfer(handle, endpoint, (unsigned char*)&cbw, 31, &size, 1000); + if (r == LIBUSB_ERROR_PIPE) { + libusb_clear_halt(handle, endpoint); + } + i++; + } while ((r == LIBUSB_ERROR_PIPE) && (i<RETRY_MAX)); + if (r != LIBUSB_SUCCESS) { + perr(" send_mass_storage_command: %s\n", libusb_strerror((enum libusb_error)r)); + return -1; + } + + printf(" sent %d CDB bytes\n", cdb_len); + return 0; +} + +static int get_mass_storage_status(libusb_device_handle *handle, uint8_t endpoint, uint32_t expected_tag) +{ + int i, r, size; + struct command_status_wrapper csw; + + // The device is allowed to STALL this transfer. If it does, you have to + // clear the stall and try again. + i = 0; + do { + r = libusb_bulk_transfer(handle, endpoint, (unsigned char*)&csw, 13, &size, 1000); + if (r == LIBUSB_ERROR_PIPE) { + libusb_clear_halt(handle, endpoint); + } + i++; + } while ((r == LIBUSB_ERROR_PIPE) && (i<RETRY_MAX)); + if (r != LIBUSB_SUCCESS) { + perr(" get_mass_storage_status: %s\n", libusb_strerror((enum libusb_error)r)); + return -1; + } + if (size != 13) { + perr(" get_mass_storage_status: received %d bytes (expected 13)\n", size); + return -1; + } + if (csw.dCSWTag != expected_tag) { + perr(" get_mass_storage_status: mismatched tags (expected %08X, received %08X)\n", + expected_tag, csw.dCSWTag); + return -1; + } + // For this test, we ignore the dCSWSignature check for validity... + printf(" Mass Storage Status: %02X (%s)\n", csw.bCSWStatus, csw.bCSWStatus?"FAILED":"Success"); + if (csw.dCSWTag != expected_tag) + return -1; + if (csw.bCSWStatus) { + // REQUEST SENSE is appropriate only if bCSWStatus is 1, meaning that the + // command failed somehow. Larger values (2 in particular) mean that + // the command couldn't be understood. + if (csw.bCSWStatus == 1) + return -2; // request Get Sense + else + return -1; + } + + // In theory we also should check dCSWDataResidue. But lots of devices + // set it wrongly. + return 0; +} + +static void get_sense(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_out) +{ + uint8_t cdb[16]; // SCSI Command Descriptor Block + uint8_t sense[18]; + uint32_t expected_tag; + int size; + int rc; + + // Request Sense + printf("Request Sense:\n"); + memset(sense, 0, sizeof(sense)); + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x03; // Request Sense + cdb[4] = REQUEST_SENSE_LENGTH; + + send_mass_storage_command(handle, endpoint_out, 0, cdb, LIBUSB_ENDPOINT_IN, REQUEST_SENSE_LENGTH, &expected_tag); + rc = libusb_bulk_transfer(handle, endpoint_in, (unsigned char*)&sense, REQUEST_SENSE_LENGTH, &size, 1000); + if (rc < 0) + { + printf("libusb_bulk_transfer failed: %s\n", libusb_error_name(rc)); + return; + } + printf(" received %d bytes\n", size); + + if ((sense[0] != 0x70) && (sense[0] != 0x71)) { + perr(" ERROR No sense data\n"); + } else { + perr(" ERROR Sense: %02X %02X %02X\n", sense[2]&0x0F, sense[12], sense[13]); + } + // Strictly speaking, the get_mass_storage_status() call should come + // before these perr() lines. If the status is nonzero then we must + // assume there's no data in the buffer. For xusb it doesn't matter. + get_mass_storage_status(handle, endpoint_in, expected_tag); +} + +// Mass Storage device to test bulk transfers (non destructive test) +static int test_mass_storage(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_out) +{ + int r, size; + uint8_t lun; + uint32_t expected_tag; + uint32_t i, max_lba, block_size; + double device_size; + uint8_t cdb[16]; // SCSI Command Descriptor Block + uint8_t buffer[64]; + char vid[9], pid[9], rev[5]; + unsigned char *data; + FILE *fd; + + printf("Reading Max LUN:\n"); + r = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + BOMS_GET_MAX_LUN, 0, 0, &lun, 1, 1000); + // Some devices send a STALL instead of the actual value. + // In such cases we should set lun to 0. + if (r == 0) { + lun = 0; + } else if (r < 0) { + perr(" Failed: %s", libusb_strerror((enum libusb_error)r)); + } + printf(" Max LUN = %d\n", lun); + + // Send Inquiry + printf("Sending Inquiry:\n"); + memset(buffer, 0, sizeof(buffer)); + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x12; // Inquiry + cdb[4] = INQUIRY_LENGTH; + + send_mass_storage_command(handle, endpoint_out, lun, cdb, LIBUSB_ENDPOINT_IN, INQUIRY_LENGTH, &expected_tag); + CALL_CHECK(libusb_bulk_transfer(handle, endpoint_in, (unsigned char*)&buffer, INQUIRY_LENGTH, &size, 1000)); + printf(" received %d bytes\n", size); + // The following strings are not zero terminated + for (i=0; i<8; i++) { + vid[i] = buffer[8+i]; + pid[i] = buffer[16+i]; + rev[i/2] = buffer[32+i/2]; // instead of another loop + } + vid[8] = 0; + pid[8] = 0; + rev[4] = 0; + printf(" VID:PID:REV \"%8s\":\"%8s\":\"%4s\"\n", vid, pid, rev); + if (get_mass_storage_status(handle, endpoint_in, expected_tag) == -2) { + get_sense(handle, endpoint_in, endpoint_out); + } + + // Read capacity + printf("Reading Capacity:\n"); + memset(buffer, 0, sizeof(buffer)); + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x25; // Read Capacity + + send_mass_storage_command(handle, endpoint_out, lun, cdb, LIBUSB_ENDPOINT_IN, READ_CAPACITY_LENGTH, &expected_tag); + CALL_CHECK(libusb_bulk_transfer(handle, endpoint_in, (unsigned char*)&buffer, READ_CAPACITY_LENGTH, &size, 1000)); + printf(" received %d bytes\n", size); + max_lba = be_to_int32(&buffer[0]); + block_size = be_to_int32(&buffer[4]); + device_size = ((double)(max_lba+1))*block_size/(1024*1024*1024); + printf(" Max LBA: %08X, Block Size: %08X (%.2f GB)\n", max_lba, block_size, device_size); + if (get_mass_storage_status(handle, endpoint_in, expected_tag) == -2) { + get_sense(handle, endpoint_in, endpoint_out); + } + + data = (unsigned char*) calloc(1, block_size); + if (data == NULL) { + perr(" unable to allocate data buffer\n"); + return -1; + } + + // Send Read + printf("Attempting to read %d bytes:\n", block_size); + memset(cdb, 0, sizeof(cdb)); + + cdb[0] = 0x28; // Read(10) + cdb[8] = 0x01; // 1 block + + send_mass_storage_command(handle, endpoint_out, lun, cdb, LIBUSB_ENDPOINT_IN, block_size, &expected_tag); + libusb_bulk_transfer(handle, endpoint_in, data, block_size, &size, 5000); + printf(" READ: received %d bytes\n", size); + if (get_mass_storage_status(handle, endpoint_in, expected_tag) == -2) { + get_sense(handle, endpoint_in, endpoint_out); + } else { + display_buffer_hex(data, size); + if ((binary_dump) && ((fd = fopen(binary_name, "w")) != NULL)) { + if (fwrite(data, 1, (size_t)size, fd) != (unsigned int)size) { + perr(" unable to write binary data\n"); + } + fclose(fd); + } + } + free(data); + + return 0; +} + +// HID +static int get_hid_record_size(uint8_t *hid_report_descriptor, int size, int type) +{ + uint8_t i, j = 0; + uint8_t offset; + int record_size[3] = {0, 0, 0}; + int nb_bits = 0, nb_items = 0; + bool found_record_marker; + + found_record_marker = false; + for (i = hid_report_descriptor[0]+1; i < size; i += offset) { + offset = (hid_report_descriptor[i]&0x03) + 1; + if (offset == 4) + offset = 5; + switch (hid_report_descriptor[i] & 0xFC) { + case 0x74: // bitsize + nb_bits = hid_report_descriptor[i+1]; + break; + case 0x94: // count + nb_items = 0; + for (j=1; j<offset; j++) { + nb_items = ((uint32_t)hid_report_descriptor[i+j]) << (8*(j-1)); + } + break; + case 0x80: // input + found_record_marker = true; + j = 0; + break; + case 0x90: // output + found_record_marker = true; + j = 1; + break; + case 0xb0: // feature + found_record_marker = true; + j = 2; + break; + case 0xC0: // end of collection + nb_items = 0; + nb_bits = 0; + break; + default: + continue; + } + if (found_record_marker) { + found_record_marker = false; + record_size[j] += nb_items*nb_bits; + } + } + if ((type < HID_REPORT_TYPE_INPUT) || (type > HID_REPORT_TYPE_FEATURE)) { + return 0; + } else { + return (record_size[type - HID_REPORT_TYPE_INPUT]+7)/8; + } +} + +static int test_hid(libusb_device_handle *handle, uint8_t endpoint_in) +{ + int r, size, descriptor_size; + uint8_t hid_report_descriptor[256]; + uint8_t *report_buffer; + FILE *fd; + + printf("\nReading HID Report Descriptors:\n"); + descriptor_size = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_STANDARD|LIBUSB_RECIPIENT_INTERFACE, + LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_REPORT<<8, 0, hid_report_descriptor, sizeof(hid_report_descriptor), 1000); + if (descriptor_size < 0) { + printf(" Failed\n"); + return -1; + } + display_buffer_hex(hid_report_descriptor, descriptor_size); + if ((binary_dump) && ((fd = fopen(binary_name, "w")) != NULL)) { + if (fwrite(hid_report_descriptor, 1, descriptor_size, fd) != descriptor_size) { + printf(" Error writing descriptor to file\n"); + } + fclose(fd); + } + + size = get_hid_record_size(hid_report_descriptor, descriptor_size, HID_REPORT_TYPE_FEATURE); + if (size <= 0) { + printf("\nSkipping Feature Report readout (None detected)\n"); + } else { + report_buffer = (uint8_t*) calloc(size, 1); + if (report_buffer == NULL) { + return -1; + } + + printf("\nReading Feature Report (length %d)...\n", size); + r = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + HID_GET_REPORT, (HID_REPORT_TYPE_FEATURE<<8)|0, 0, report_buffer, (uint16_t)size, 5000); + if (r >= 0) { + display_buffer_hex(report_buffer, size); + } else { + switch(r) { + case LIBUSB_ERROR_NOT_FOUND: + printf(" No Feature Report available for this device\n"); + break; + case LIBUSB_ERROR_PIPE: + printf(" Detected stall - resetting pipe...\n"); + libusb_clear_halt(handle, 0); + break; + default: + printf(" Error: %s\n", libusb_strerror((enum libusb_error)r)); + break; + } + } + free(report_buffer); + } + + size = get_hid_record_size(hid_report_descriptor, descriptor_size, HID_REPORT_TYPE_INPUT); + if (size <= 0) { + printf("\nSkipping Input Report readout (None detected)\n"); + } else { + report_buffer = (uint8_t*) calloc(size, 1); + if (report_buffer == NULL) { + return -1; + } + + printf("\nReading Input Report (length %d)...\n", size); + r = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + HID_GET_REPORT, (HID_REPORT_TYPE_INPUT<<8)|0x00, 0, report_buffer, (uint16_t)size, 5000); + if (r >= 0) { + display_buffer_hex(report_buffer, size); + } else { + switch(r) { + case LIBUSB_ERROR_TIMEOUT: + printf(" Timeout! Please make sure you act on the device within the 5 seconds allocated...\n"); + break; + case LIBUSB_ERROR_PIPE: + printf(" Detected stall - resetting pipe...\n"); + libusb_clear_halt(handle, 0); + break; + default: + printf(" Error: %s\n", libusb_strerror((enum libusb_error)r)); + break; + } + } + + // Attempt a bulk read from endpoint 0 (this should just return a raw input report) + printf("\nTesting interrupt read using endpoint %02X...\n", endpoint_in); + r = libusb_interrupt_transfer(handle, endpoint_in, report_buffer, size, &size, 5000); + if (r >= 0) { + display_buffer_hex(report_buffer, size); + } else { + printf(" %s\n", libusb_strerror((enum libusb_error)r)); + } + + free(report_buffer); + } + return 0; +} + +// Read the MS WinUSB Feature Descriptors, that are used on Windows 8 for automated driver installation +static void read_ms_winsub_feature_descriptors(libusb_device_handle *handle, uint8_t bRequest, int iface_number) +{ +#define MAX_OS_FD_LENGTH 256 + int i, r; + uint8_t os_desc[MAX_OS_FD_LENGTH]; + uint32_t length; + void* le_type_punning_IS_fine; + struct { + const char* desc; + uint8_t recipient; + uint16_t index; + uint16_t header_size; + } os_fd[2] = { + {"Extended Compat ID", LIBUSB_RECIPIENT_DEVICE, 0x0004, 0x10}, + {"Extended Properties", LIBUSB_RECIPIENT_INTERFACE, 0x0005, 0x0A} + }; + + if (iface_number < 0) return; + // WinUSB has a limitation that forces wIndex to the interface number when issuing + // an Interface Request. To work around that, we can force a Device Request for + // the Extended Properties, assuming the device answers both equally. + if (force_device_request) + os_fd[1].recipient = LIBUSB_RECIPIENT_DEVICE; + + for (i=0; i<2; i++) { + printf("\nReading %s OS Feature Descriptor (wIndex = 0x%04d):\n", os_fd[i].desc, os_fd[i].index); + + // Read the header part + r = libusb_control_transfer(handle, (uint8_t)(LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_VENDOR|os_fd[i].recipient), + bRequest, (uint16_t)(((iface_number)<< 8)|0x00), os_fd[i].index, os_desc, os_fd[i].header_size, 1000); + if (r < os_fd[i].header_size) { + perr(" Failed: %s", (r<0)?libusb_strerror((enum libusb_error)r):"header size is too small"); + return; + } + le_type_punning_IS_fine = (void*)os_desc; + length = *((uint32_t*)le_type_punning_IS_fine); + if (length > MAX_OS_FD_LENGTH) { + length = MAX_OS_FD_LENGTH; + } + + // Read the full feature descriptor + r = libusb_control_transfer(handle, (uint8_t)(LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_VENDOR|os_fd[i].recipient), + bRequest, (uint16_t)(((iface_number)<< 8)|0x00), os_fd[i].index, os_desc, (uint16_t)length, 1000); + if (r < 0) { + perr(" Failed: %s", libusb_strerror((enum libusb_error)r)); + return; + } else { + display_buffer_hex(os_desc, r); + } + } +} + +static void print_device_cap(struct libusb_bos_dev_capability_descriptor *dev_cap) +{ + switch(dev_cap->bDevCapabilityType) { + case LIBUSB_BT_USB_2_0_EXTENSION: { + struct libusb_usb_2_0_extension_descriptor *usb_2_0_ext = NULL; + libusb_get_usb_2_0_extension_descriptor(NULL, dev_cap, &usb_2_0_ext); + if (usb_2_0_ext) { + printf(" USB 2.0 extension:\n"); + printf(" attributes : %02X\n", usb_2_0_ext->bmAttributes); + libusb_free_usb_2_0_extension_descriptor(usb_2_0_ext); + } + break; + } + case LIBUSB_BT_SS_USB_DEVICE_CAPABILITY: { + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap = NULL; + libusb_get_ss_usb_device_capability_descriptor(NULL, dev_cap, &ss_usb_device_cap); + if (ss_usb_device_cap) { + printf(" USB 3.0 capabilities:\n"); + printf(" attributes : %02X\n", ss_usb_device_cap->bmAttributes); + printf(" supported speeds : %04X\n", ss_usb_device_cap->wSpeedSupported); + printf(" supported functionality: %02X\n", ss_usb_device_cap->bFunctionalitySupport); + libusb_free_ss_usb_device_capability_descriptor(ss_usb_device_cap); + } + break; + } + case LIBUSB_BT_CONTAINER_ID: { + struct libusb_container_id_descriptor *container_id = NULL; + libusb_get_container_id_descriptor(NULL, dev_cap, &container_id); + if (container_id) { + printf(" Container ID:\n %s\n", uuid_to_string(container_id->ContainerID)); + libusb_free_container_id_descriptor(container_id); + } + break; + } + default: + printf(" Unknown BOS device capability %02x:\n", dev_cap->bDevCapabilityType); + } +} + +static int test_device(uint16_t vid, uint16_t pid) +{ + libusb_device_handle *handle; + libusb_device *dev; + uint8_t bus, port_path[8]; + struct libusb_bos_descriptor *bos_desc; + struct libusb_config_descriptor *conf_desc; + const struct libusb_endpoint_descriptor *endpoint; + int i, j, k, r; + int iface, nb_ifaces, first_iface = -1; + struct libusb_device_descriptor dev_desc; + const char* speed_name[5] = { "Unknown", "1.5 Mbit/s (USB LowSpeed)", "12 Mbit/s (USB FullSpeed)", + "480 Mbit/s (USB HighSpeed)", "5000 Mbit/s (USB SuperSpeed)"}; + char string[128]; + uint8_t string_index[3]; // indexes of the string descriptors + uint8_t endpoint_in = 0, endpoint_out = 0; // default IN and OUT endpoints + + printf("Opening device %04X:%04X...\n", vid, pid); + handle = libusb_open_device_with_vid_pid(NULL, vid, pid); + + if (handle == NULL) { + perr(" Failed.\n"); + return -1; + } + + dev = libusb_get_device(handle); + bus = libusb_get_bus_number(dev); + if (extra_info) { + r = libusb_get_port_numbers(dev, port_path, sizeof(port_path)); + if (r > 0) { + printf("\nDevice properties:\n"); + printf(" bus number: %d\n", bus); + printf(" port path: %d", port_path[0]); + for (i=1; i<r; i++) { + printf("->%d", port_path[i]); + } + printf(" (from root hub)\n"); + } + r = libusb_get_device_speed(dev); + if ((r<0) || (r>4)) r=0; + printf(" speed: %s\n", speed_name[r]); + } + + printf("\nReading device descriptor:\n"); + CALL_CHECK(libusb_get_device_descriptor(dev, &dev_desc)); + printf(" length: %d\n", dev_desc.bLength); + printf(" device class: %d\n", dev_desc.bDeviceClass); + printf(" S/N: %d\n", dev_desc.iSerialNumber); + printf(" VID:PID: %04X:%04X\n", dev_desc.idVendor, dev_desc.idProduct); + printf(" bcdDevice: %04X\n", dev_desc.bcdDevice); + printf(" iMan:iProd:iSer: %d:%d:%d\n", dev_desc.iManufacturer, dev_desc.iProduct, dev_desc.iSerialNumber); + printf(" nb confs: %d\n", dev_desc.bNumConfigurations); + // Copy the string descriptors for easier parsing + string_index[0] = dev_desc.iManufacturer; + string_index[1] = dev_desc.iProduct; + string_index[2] = dev_desc.iSerialNumber; + + printf("\nReading BOS descriptor: "); + if (libusb_get_bos_descriptor(handle, &bos_desc) == LIBUSB_SUCCESS) { + printf("%d caps\n", bos_desc->bNumDeviceCaps); + for (i = 0; i < bos_desc->bNumDeviceCaps; i++) + print_device_cap(bos_desc->dev_capability[i]); + libusb_free_bos_descriptor(bos_desc); + } else { + printf("no descriptor\n"); + } + + printf("\nReading first configuration descriptor:\n"); + CALL_CHECK(libusb_get_config_descriptor(dev, 0, &conf_desc)); + nb_ifaces = conf_desc->bNumInterfaces; + printf(" nb interfaces: %d\n", nb_ifaces); + if (nb_ifaces > 0) + first_iface = conf_desc->usb_interface[0].altsetting[0].bInterfaceNumber; + for (i=0; i<nb_ifaces; i++) { + printf(" interface[%d]: id = %d\n", i, + conf_desc->usb_interface[i].altsetting[0].bInterfaceNumber); + for (j=0; j<conf_desc->usb_interface[i].num_altsetting; j++) { + printf("interface[%d].altsetting[%d]: num endpoints = %d\n", + i, j, conf_desc->usb_interface[i].altsetting[j].bNumEndpoints); + printf(" Class.SubClass.Protocol: %02X.%02X.%02X\n", + conf_desc->usb_interface[i].altsetting[j].bInterfaceClass, + conf_desc->usb_interface[i].altsetting[j].bInterfaceSubClass, + conf_desc->usb_interface[i].altsetting[j].bInterfaceProtocol); + if ( (conf_desc->usb_interface[i].altsetting[j].bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE) + && ( (conf_desc->usb_interface[i].altsetting[j].bInterfaceSubClass == 0x01) + || (conf_desc->usb_interface[i].altsetting[j].bInterfaceSubClass == 0x06) ) + && (conf_desc->usb_interface[i].altsetting[j].bInterfaceProtocol == 0x50) ) { + // Mass storage devices that can use basic SCSI commands + test_mode = USE_SCSI; + } + for (k=0; k<conf_desc->usb_interface[i].altsetting[j].bNumEndpoints; k++) { + struct libusb_ss_endpoint_companion_descriptor *ep_comp = NULL; + endpoint = &conf_desc->usb_interface[i].altsetting[j].endpoint[k]; + printf(" endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress); + // Use the first interrupt or bulk IN/OUT endpoints as default for testing + if ((endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) & (LIBUSB_TRANSFER_TYPE_BULK | LIBUSB_TRANSFER_TYPE_INTERRUPT)) { + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { + if (!endpoint_in) + endpoint_in = endpoint->bEndpointAddress; + } else { + if (!endpoint_out) + endpoint_out = endpoint->bEndpointAddress; + } + } + printf(" max packet size: %04X\n", endpoint->wMaxPacketSize); + printf(" polling interval: %02X\n", endpoint->bInterval); + libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp); + if (ep_comp) { + printf(" max burst: %02X (USB 3.0)\n", ep_comp->bMaxBurst); + printf(" bytes per interval: %04X (USB 3.0)\n", ep_comp->wBytesPerInterval); + libusb_free_ss_endpoint_companion_descriptor(ep_comp); + } + } + } + } + libusb_free_config_descriptor(conf_desc); + + libusb_set_auto_detach_kernel_driver(handle, 1); + for (iface = 0; iface < nb_ifaces; iface++) + { + printf("\nClaiming interface %d...\n", iface); + r = libusb_claim_interface(handle, iface); + if (r != LIBUSB_SUCCESS) { + perr(" Failed.\n"); + } + } + + printf("\nReading string descriptors:\n"); + for (i=0; i<3; i++) { + if (string_index[i] == 0) { + continue; + } + if (libusb_get_string_descriptor_ascii(handle, string_index[i], (unsigned char*)string, 128) >= 0) { + printf(" String (0x%02X): \"%s\"\n", string_index[i], string); + } + } + // Read the OS String Descriptor + if (libusb_get_string_descriptor_ascii(handle, 0xEE, (unsigned char*)string, 128) >= 0) { + printf(" String (0x%02X): \"%s\"\n", 0xEE, string); + // If this is a Microsoft OS String Descriptor, + // attempt to read the WinUSB extended Feature Descriptors + if (strncmp(string, "MSFT100", 7) == 0) + read_ms_winsub_feature_descriptors(handle, string[7], first_iface); + } + + switch(test_mode) { + case USE_PS3: + CALL_CHECK(display_ps3_status(handle)); + break; + case USE_XBOX: + CALL_CHECK(display_xbox_status(handle)); + CALL_CHECK(set_xbox_actuators(handle, 128, 222)); + msleep(2000); + CALL_CHECK(set_xbox_actuators(handle, 0, 0)); + break; + case USE_HID: + test_hid(handle, endpoint_in); + break; + case USE_SCSI: + CALL_CHECK(test_mass_storage(handle, endpoint_in, endpoint_out)); + case USE_GENERIC: + break; + } + + printf("\n"); + for (iface = 0; iface<nb_ifaces; iface++) { + printf("Releasing interface %d...\n", iface); + libusb_release_interface(handle, iface); + } + + printf("Closing device...\n"); + libusb_close(handle); + + return 0; +} + +int main(int argc, char** argv) +{ + bool show_help = false; + bool debug_mode = false; + const struct libusb_version* version; + int j, r; + size_t i, arglen; + unsigned tmp_vid, tmp_pid; + uint16_t endian_test = 0xBE00; + char* error_lang = NULL; + + // Default to generic, expecting VID:PID + VID = 0; + PID = 0; + test_mode = USE_GENERIC; + + if (((uint8_t*)&endian_test)[0] == 0xBE) { + printf("Despite their natural superiority for end users, big endian\n" + "CPUs are not supported with this program, sorry.\n"); + return 0; + } + + if (argc >= 2) { + for (j = 1; j<argc; j++) { + arglen = strlen(argv[j]); + if ( ((argv[j][0] == '-') || (argv[j][0] == '/')) + && (arglen >= 2) ) { + switch(argv[j][1]) { + case 'd': + debug_mode = true; + break; + case 'i': + extra_info = true; + break; + case 'w': + force_device_request = true; + break; + case 'b': + if ((j+1 >= argc) || (argv[j+1][0] == '-') || (argv[j+1][0] == '/')) { + printf(" Option -b requires a file name\n"); + return 1; + } + binary_name = argv[++j]; + binary_dump = true; + break; + case 'l': + if ((j+1 >= argc) || (argv[j+1][0] == '-') || (argv[j+1][0] == '/')) { + printf(" Option -l requires an ISO 639-1 language parameter\n"); + return 1; + } + error_lang = argv[++j]; + break; + case 'j': + // OLIMEX ARM-USB-TINY JTAG, 2 channel composite device - 2 interfaces + if (!VID && !PID) { + VID = 0x15BA; + PID = 0x0004; + } + break; + case 'k': + // Generic 2 GB USB Key (SCSI Transparent/Bulk Only) - 1 interface + if (!VID && !PID) { + VID = 0x0204; + PID = 0x6025; + } + break; + // The following tests will force VID:PID if already provided + case 'p': + // Sony PS3 Controller - 1 interface + VID = 0x054C; + PID = 0x0268; + test_mode = USE_PS3; + break; + case 's': + // Microsoft Sidewinder Precision Pro Joystick - 1 HID interface + VID = 0x045E; + PID = 0x0008; + test_mode = USE_HID; + break; + case 'x': + // Microsoft XBox Controller Type S - 1 interface + VID = 0x045E; + PID = 0x0289; + test_mode = USE_XBOX; + break; + default: + show_help = true; + break; + } + } else { + for (i=0; i<arglen; i++) { + if (argv[j][i] == ':') + break; + } + if (i != arglen) { + if (sscanf(argv[j], "%x:%x" , &tmp_vid, &tmp_pid) != 2) { + printf(" Please specify VID & PID as \"vid:pid\" in hexadecimal format\n"); + return 1; + } + VID = (uint16_t)tmp_vid; + PID = (uint16_t)tmp_pid; + } else { + show_help = true; + } + } + } + } + + if ((show_help) || (argc == 1) || (argc > 7)) { + printf("usage: %s [-h] [-d] [-i] [-k] [-b file] [-l lang] [-j] [-x] [-s] [-p] [-w] [vid:pid]\n", argv[0]); + printf(" -h : display usage\n"); + printf(" -d : enable debug output\n"); + printf(" -i : print topology and speed info\n"); + printf(" -j : test composite FTDI based JTAG device\n"); + printf(" -k : test Mass Storage device\n"); + printf(" -b file : dump Mass Storage data to file 'file'\n"); + printf(" -p : test Sony PS3 SixAxis controller\n"); + printf(" -s : test Microsoft Sidewinder Precision Pro (HID)\n"); + printf(" -x : test Microsoft XBox Controller Type S\n"); + printf(" -l lang : language to report errors in (ISO 639-1)\n"); + printf(" -w : force the use of device requests when querying WCID descriptors\n"); + printf("If only the vid:pid is provided, xusb attempts to run the most appropriate test\n"); + return 0; + } + + version = libusb_get_version(); + printf("Using libusbx v%d.%d.%d.%d\n\n", version->major, version->minor, version->micro, version->nano); + r = libusb_init(NULL); + if (r < 0) + return r; + + libusb_set_debug(NULL, debug_mode?LIBUSB_LOG_LEVEL_DEBUG:LIBUSB_LOG_LEVEL_INFO); + if (error_lang != NULL) { + r = libusb_setlocale(error_lang); + if (r < 0) + printf("Invalid or unsupported locale '%s': %s\n", error_lang, libusb_strerror((enum libusb_error)r)); + } + + test_device(VID, PID); + + libusb_exit(NULL); + + return 0; +} |