Switch to libusb-1.0 and use async interface.
authorKeith Packard <keithp@keithp.com>
Sat, 27 Dec 2008 07:05:04 +0000 (23:05 -0800)
committerKeith Packard <keithp@keithp.com>
Sat, 27 Dec 2008 07:05:04 +0000 (23:05 -0800)
The async libusb interface offers substantial performance benefits by not
making each command wait for the reply. This makes talking over this
interface almost reasonable.

Signed-off-by: Keith Packard <keithp@keithp.com>
ccload/Makefile.am
configure.ac
lib/Makefile.am
lib/ccdbg-command.c
lib/ccdbg-debug.h [new file with mode: 0644]
lib/ccdbg-io.c
lib/ccdbg-manual.c
lib/ccdbg.h
lib/cp-usb-async.c [new file with mode: 0644]
lib/cp-usb-async.h [new file with mode: 0644]
s51/Makefile.am

index f54f4aa..3a754b2 100644 (file)
@@ -1,10 +1,10 @@
 bin_PROGRAMS=ccload
 
-AM_CFLAGS=-I$(top_srcdir)/lib
+AM_CFLAGS=-I$(top_srcdir)/lib $(LIBUSB_CFLAGS)
 CCLOAD_LIBS=../lib/libcc.a
 
 ccload_DEPENDENCIES = $(CCLOAD_LIBS)
 
-ccload_LDADD=$(CCLOAD_LIBS) $(USB_LIBS)
+ccload_LDADD=$(CCLOAD_LIBS) $(LIBUSB_LIBS)
 
 ccload_SOURCES = ccload.c
index a14c802..3ae8052 100644 (file)
@@ -30,7 +30,9 @@ AC_PROG_CC
 AC_PROG_INSTALL
 AC_PROG_LN_S
 AC_PROG_RANLIB
+PKG_PROG_PKG_CONFIG
 
+CFLAGS="-g"
 WARN_CFLAGS=""
 if test "x$GCC" = "xyes"; then
        WARN_CFLAGS="-Wall -Wpointer-arith -Wstrict-prototypes \
@@ -57,6 +59,8 @@ fi
 AC_MSG_RESULT([$CC_FOR_BUILD])
 AC_SUBST(CC_FOR_BUILD)
 
+PKG_CHECK_MODULES([LIBUSB], [libusb-1.0])
+
 AC_MSG_CHECKING([for suffix of executable build tools])
 if test $cross_compiling = yes; then
   cat >conftest.c <<\_______EOF
index a5e5932..16f5b92 100644 (file)
@@ -1,6 +1,6 @@
 noinst_LIBRARIES = libcc.a
 
-AM_CFLAGS=$(WARN_CFLAGS)
+AM_CFLAGS=$(WARN_CFLAGS) $(LIBUSB_CFLAGS)
 
 libcc_a_SOURCES = \
        ccdbg-command.c \
@@ -11,4 +11,5 @@ libcc_a_SOURCES = \
        ccdbg-io.c \
        ccdbg-manual.c \
        ccdbg-memory.c \
-       cp-usb.c
+       cp-usb.c \
+       cp-usb-async.c
index 30f5094..2b29fde 100644 (file)
@@ -31,6 +31,7 @@ ccdbg_debug_mode(struct ccdbg *dbg)
        ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N,          CC_DATA           );
        ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA           );
        ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N,          CC_DATA|CC_RESET_N);
+       ccdbg_sync_io(dbg);
 }
 
 void
@@ -45,6 +46,7 @@ ccdbg_reset(struct ccdbg *dbg)
        ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA           );
        ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA           );
        ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N);
+       ccdbg_sync_io(dbg);
 }
 
 uint8_t
diff --git a/lib/ccdbg-debug.h b/lib/ccdbg-debug.h
new file mode 100644 (file)
index 0000000..a09148d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2008 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it 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.
+ */
+
+#ifndef _CCDBG_DEBUG_H_
+#define _CCDBG_DEBUG_H_
+/* Debug levels
+ */
+#define CC_DEBUG_BITBANG       0x00000001
+#define CC_DEBUG_COMMAND       0x00000002
+#define CC_DEBUG_INSTRUCTIONS  0x00000004
+#define CC_DEBUG_EXECUTE       0x00000008
+#define CC_DEBUG_FLASH         0x00000010
+#define CC_DEBUG_MEMORY                0x00000020
+#define CC_DEBUG_USB_ASYNC     0x00000040
+
+/* ccdbg-debug.c */
+void
+ccdbg_debug(int level, char *format, ...);
+
+void
+ccdbg_add_debug(int level);
+
+void
+ccdbg_clear_debug(int level);
+
+void
+ccdbg_flush(void);
+
+#endif /* _CCDBG_DEBUG_H_ */
index 5ecea76..53ea758 100644 (file)
 
 #include "ccdbg.h"
 #include <time.h>
+#ifdef CP_USB_ASYNC
+#include "cp-usb-async.h"
+#else
+#include "cp-usb.h"
+#endif
 
 void
 ccdbg_half_clock(struct ccdbg *dbg)
@@ -38,32 +43,60 @@ ccdbg_open(void)
                perror("calloc");
                return NULL;
        }
+#ifdef CP_USB_ASYNC
+       dbg->cp_async = cp_usb_async_open();
+       if (!dbg->cp_async) {
+               free (dbg);
+               return NULL;
+       }
+#else
        dbg->cp = cp_usb_open ();
        if (!dbg->cp) {
                free (dbg);
                return NULL;
        }
+#endif
        return dbg;
 }
 
 void
 ccdbg_close(struct ccdbg *dbg)
 {
+#ifdef CP_USB_ASYNC
+       cp_usb_async_close(dbg->cp_async);
+#else
        cp_usb_close(dbg->cp);
+#endif
        free (dbg);
 }
 
 int
 ccdbg_write(struct ccdbg *dbg, uint8_t mask, uint8_t value)
 {
+#ifdef CP_USB_ASYNC
+       cp_usb_async_write(dbg->cp_async, mask, value);
+#else
        cp_usb_write(dbg->cp, mask, value);
+#endif
        return 0;
 }
 
-uint8_t
-ccdbg_read(struct ccdbg *dbg)
+void
+ccdbg_read(struct ccdbg *dbg, uint8_t *valuep)
+{
+#ifdef CP_USB_ASYNC
+       cp_usb_async_read(dbg->cp_async, valuep);
+#else
+       *valuep = cp_usb_read(dbg->cp);
+#endif
+}
+
+void
+ccdbg_sync_io(struct ccdbg *dbg)
 {
-       return cp_usb_read(dbg->cp);
+#ifdef CP_USB_ASYNC
+       cp_usb_async_sync(dbg->cp_async);
+#endif
 }
 
 static char
@@ -112,6 +145,7 @@ ccdbg_send_byte(struct ccdbg *dbg, uint8_t byte)
                if (bit == 3)
                        ccdbg_debug(CC_DEBUG_BITBANG, "\n");
        }
+       ccdbg_sync_io(dbg);
 }
 
 void
@@ -121,43 +155,47 @@ ccdbg_send_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes)
                ccdbg_send_byte(dbg, *bytes++);
 }
 
-uint8_t
-ccdbg_recv_bit(struct ccdbg *dbg, int first)
+void
+ccdbg_recv_bit(struct ccdbg *dbg, int first, uint8_t *bit)
 {
        uint8_t mask = first ? CC_DATA : 0;
-       uint8_t read;
 
        ccdbg_send(dbg, CC_CLOCK|mask|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N);
-       read = ccdbg_read(dbg);
-       ccdbg_print("#\t%c %c %c\n", CC_DATA, read);
+       ccdbg_read(dbg, bit);
        ccdbg_send(dbg, CC_CLOCK|     CC_RESET_N,                  CC_RESET_N);
-       return (read & CC_DATA) ? 1 : 0;
 }
 
-uint8_t
-ccdbg_recv_byte(struct ccdbg *dbg, int first)
+void
+ccdbg_recv_byte(struct ccdbg *dbg, int first, uint8_t *bytep)
 {
        uint8_t byte = 0;
+       uint8_t bits[8];
        int     bit;
 
        ccdbg_debug(CC_DEBUG_BITBANG, "#\n# Recv byte\n#\n");
+       for (bit = 0; bit < 8; bit++) {
+               ccdbg_recv_bit(dbg, first, &bits[bit]);
+               first = 0;
+       }
+       ccdbg_sync_io(dbg);
        for (bit = 0; bit < 8; bit++) {
                byte = byte << 1;
-               byte |= ccdbg_recv_bit(dbg, first);
+               byte |= (bits[bit] & CC_DATA) ? 1 : 0;
+               ccdbg_print("#\t%c %c %c\n", CC_DATA, bits[bit]);
                if (bit == 3)
                        ccdbg_debug(CC_DEBUG_BITBANG, "\n");
-               first = 0;
        }
        ccdbg_debug(CC_DEBUG_BITBANG, "#\n# Recv 0x%02x\n#\n", byte);
-       return byte;
+       *bytep = byte;
 }
 
 void
 ccdbg_recv_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes)
 {
+       int i;
        int first = 1;
-       while (nbytes--) {
-               *bytes++ = ccdbg_recv_byte(dbg, first);
+       for (i = 0; i < nbytes; i++) {
+               ccdbg_recv_byte(dbg, first, &bytes[i]);
                first = 0;
        }
 }
index b83dc45..b48f8bb 100644 (file)
@@ -59,7 +59,7 @@ ccdbg_manual(struct ccdbg *dbg, FILE *input)
                get_bit(line, 4, 'R', CC_RESET_N, &set, &mask);
                if (mask != (CC_CLOCK|CC_DATA|CC_RESET_N)) {
                        uint8_t read;
-                       read = ccdbg_read(dbg);
+                       ccdbg_read(dbg, &read);
                        ccdbg_print("\t%c %c %c", CC_CLOCK|CC_DATA|CC_RESET_N, read);
                        if ((set & CC_CLOCK) == 0)
                                printf ("\t%d", (read&CC_DATA) ? 1 : 0);
index e0e5810..834092b 100644 (file)
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
-#include "cp-usb.h"
+#include "ccdbg-debug.h"
+
 #define CC_CLOCK       0x1
 #define CC_DATA                0x2
 #define CC_RESET_N     0x4
-#define CC_CLOCK_US    (40)
+#define CC_CLOCK_US    (0)
 
 /* 8051 instructions
  */
 /* Bit-addressable accumulator */
 #define ACC(bit)               (0xE0 | (bit))
 
+#define CP_USB_ASYNC
+
 struct ccdbg {
+#ifdef CP_USB_ASYNC
+       struct cp_usb_async *cp_async;
+#else
        struct cp_usb *cp;
+#endif
 };
 
 /* Intel hex file format data
@@ -153,15 +160,6 @@ struct hex_image {
 #define CC_STEP_REPLACE(n)     (0x64|(n))
 #define CC_GET_CHIP_ID         0x68
 
-/* Debug levels
- */
-#define CC_DEBUG_BITBANG       0x00000001
-#define CC_DEBUG_COMMAND       0x00000002
-#define CC_DEBUG_INSTRUCTIONS  0x00000004
-#define CC_DEBUG_EXECUTE       0x00000008
-#define CC_DEBUG_FLASH         0x00000010
-#define CC_DEBUG_MEMORY                0x00000020
-
 /* ccdbg-command.c */
 void
 ccdbg_debug_mode(struct ccdbg *dbg);
@@ -214,19 +212,6 @@ ccdbg_set_pc(struct ccdbg *dbg, uint16_t pc);
 uint8_t
 ccdbg_execute_hex_image(struct ccdbg *dbg, struct hex_image *image);
 
-/* ccdbg-debug.c */
-void
-ccdbg_debug(int level, char *format, ...);
-
-void
-ccdbg_add_debug(int level);
-
-void
-ccdbg_clear_debug(int level);
-
-void
-ccdbg_flush(void);
-
 /* ccdbg-flash.c */
 uint8_t
 ccdbg_flash_hex_image(struct ccdbg *dbg, struct hex_image *image);
@@ -254,8 +239,8 @@ ccdbg_half_clock(struct ccdbg *dbg);
 int
 ccdbg_write(struct ccdbg *dbg, uint8_t mask, uint8_t value);
 
-uint8_t
-ccdbg_read(struct ccdbg *dbg);
+void
+ccdbg_read(struct ccdbg *dbg, uint8_t *valuep);
 
 struct ccdbg *
 ccdbg_open(void);
@@ -302,15 +287,18 @@ ccdbg_send_byte(struct ccdbg *dbg, uint8_t byte);
 void
 ccdbg_send_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes);
 
-uint8_t
-ccdbg_recv_bit(struct ccdbg *dbg, int first);
+void
+ccdbg_recv_bit(struct ccdbg *dbg, int first, uint8_t *bit);
 
-uint8_t
-ccdbg_recv_byte(struct ccdbg *dbg, int first);
+void
+ccdbg_recv_byte(struct ccdbg *dbg, int first, uint8_t *byte);
 
 void
 ccdbg_recv_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes);
 
+void
+ccdbg_sync_io(struct ccdbg *dbg);
+
 void
 ccdbg_print(char *format, uint8_t mask, uint8_t set);
 
diff --git a/lib/cp-usb-async.c b/lib/cp-usb-async.c
new file mode 100644 (file)
index 0000000..3f5f76a
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2008 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it 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 <unistd.h>
+#include "cp-usb-async.h"
+#include "ccdbg-debug.h"
+
+#define MAX_OUTSTANDING                256
+#define CP_TIMEOUT             1000    /* ms */
+
+struct cp_usb_packet {
+       struct libusb_transfer  *transfer;
+       enum { packet_read, packet_write } direction;
+       unsigned char           data[9];
+       uint8_t                 *valuep;
+};
+
+struct cp_usb_async {
+       libusb_context          *ctx;
+       libusb_device_handle    *handle;
+       struct cp_usb_packet    packet[MAX_OUTSTANDING];
+       int                     p, ack;
+};
+
+struct cp_usb_async *
+cp_usb_async_open(void)
+{
+       struct cp_usb_async *cp;
+       int ret;
+
+       cp = calloc(sizeof (struct cp_usb_async), 1);
+       if (!cp)
+               return NULL;
+       ret = libusb_init(&cp->ctx);
+       if (ret) {
+               free(cp);
+               return NULL;
+       }
+       cp->handle = libusb_open_device_with_vid_pid(cp->ctx,
+                                                    0x10c4, 0xea60);
+       if (!cp->handle) {
+               libusb_exit(cp->ctx);
+               free(cp);
+               return NULL;
+       }
+       return cp;
+}
+
+void
+cp_usb_async_close(struct cp_usb_async *cp)
+{
+       libusb_close(cp->handle);
+       libusb_exit(cp->ctx);
+       free(cp);
+}
+
+static void
+cp_usb_async_transfer_callback(struct libusb_transfer *transfer)
+{
+       struct cp_usb_async *cp = transfer->user_data;
+       int p;
+
+       for (p = 0; p < cp->p; p++)
+               if (cp->packet[p].transfer == transfer)
+                       break;
+       if (p == cp->p) {
+               fprintf(stderr, "unknown transfer\n");
+               return;
+       }
+       switch (cp->packet[p].direction) {
+       case packet_read:
+               ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack read %d 0x%02x\n",
+                           p, cp->packet[p].data[8]);
+               *cp->packet[p].valuep = cp->packet[p].data[8];
+               break;
+       case packet_write:
+               ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack write %d\n", p);
+               break;
+       }
+       if (p > cp->ack)
+               cp->ack = p;
+}
+
+void
+cp_usb_async_write(struct cp_usb_async *cp, uint8_t mask, uint8_t value)
+{
+       int     p;
+       uint16_t gpio_set = ((uint16_t) value << 8) | mask;
+       int     ret;
+
+       if (cp->p == MAX_OUTSTANDING)
+               cp_usb_async_sync(cp);
+       p = cp->p;
+       if (!cp->packet[p].transfer)
+               cp->packet[p].transfer = libusb_alloc_transfer(0);
+       cp->packet[p].direction = packet_write;
+       libusb_fill_control_setup(cp->packet[p].data,
+                                 0x40,                 /* request */
+                                 0xff,                 /* request type */
+                                 0x37e1,               /* value */
+                                 gpio_set,             /* index */
+                                 0);                   /* length */
+       
+       libusb_fill_control_transfer(cp->packet[p].transfer,
+                                    cp->handle,
+                                    cp->packet[p].data,
+                                    cp_usb_async_transfer_callback,
+                                    cp,
+                                    CP_TIMEOUT);
+       ccdbg_debug(CC_DEBUG_USB_ASYNC, "Write packet %d 0x%x 0x%x\n", p, mask, value);
+       ret = libusb_submit_transfer(cp->packet[p].transfer);
+       if (ret)
+               fprintf(stderr, "libusb_submit_transfer failed %d\n", ret);
+       cp->p++;
+}
+
+void
+cp_usb_async_read(struct cp_usb_async *cp, uint8_t *valuep)
+{
+       int     p;
+       int     ret;
+
+       if (cp->p == MAX_OUTSTANDING)
+               cp_usb_async_sync(cp);
+       p = cp->p;
+       if (!cp->packet[p].transfer)
+               cp->packet[p].transfer = libusb_alloc_transfer(0);
+       cp->packet[p].valuep = valuep;
+       cp->packet[p].direction = packet_read;
+       libusb_fill_control_setup(cp->packet[p].data,
+                                 0xc0,                 /* request */
+                                 0xff,                 /* request type */
+                                 0x00c2,               /* value */
+                                 0,                    /* index */
+                                 1);                   /* length */
+       
+       libusb_fill_control_transfer(cp->packet[p].transfer,
+                                    cp->handle,
+                                    cp->packet[p].data,
+                                    cp_usb_async_transfer_callback,
+                                    cp,
+                                    CP_TIMEOUT);
+       ccdbg_debug(CC_DEBUG_USB_ASYNC, "Read packet %d\n", p);
+       ret = libusb_submit_transfer(cp->packet[p].transfer);
+       if (ret)
+               fprintf(stderr, "libusb_submit_transfer failed %d\n", ret);
+       cp->p++;
+}
+
+void
+cp_usb_async_sync(struct cp_usb_async *cp)
+{
+       while (cp->ack < cp->p - 1) {
+               libusb_handle_events(cp->ctx);
+       }
+       cp->p = 0;
+       cp->ack = 0;
+}
diff --git a/lib/cp-usb-async.h b/lib/cp-usb-async.h
new file mode 100644 (file)
index 0000000..976a320
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2008 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it 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.
+ */
+
+#ifndef _CP_USB_ASYNC_H_
+#define _CP_USB_ASYNC_H_
+#include <libusb.h>
+
+struct cp_usb_async *
+cp_usb_async_open(void);
+
+void
+cp_usb_async_close(struct cp_usb_async *cp);
+
+void
+cp_usb_async_write(struct cp_usb_async *cp, uint8_t mask, uint8_t value);
+
+void
+cp_usb_async_read(struct cp_usb_async *cp, uint8_t *valuep);
+
+void
+cp_usb_async_sync(struct cp_usb_async *cp);
+
+#endif
index cfa183d..fa6fc69 100644 (file)
@@ -1,10 +1,10 @@
 bin_PROGRAMS=s51
 
-AM_CFLAGS=-I$(top_srcdir)/lib
+AM_CFLAGS=-I$(top_srcdir)/lib $(LIBUSB_CFLAGS)
 S51_LIBS=../lib/libcc.a
 
 s51_DEPENDENCIES = $(S51_LIBS)
 
-s51_LDADD=$(S51_LIBS) $(USB_LIBS)
+s51_LDADD=$(S51_LIBS) $(LIBUSB_LIBS)
 
 s51_SOURCES = s51-parse.c s51-command.c s51-main.c