summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am21
-rw-r--r--lib/cc-bitbang.c269
-rw-r--r--lib/cc-bitbang.h94
-rw-r--r--lib/cc-usb.c359
-rw-r--r--lib/cc-usb.h59
-rw-r--r--lib/cccp.c112
-rw-r--r--lib/cccp.h41
-rw-r--r--lib/ccdbg-command.c176
-rw-r--r--lib/ccdbg-debug.c63
-rw-r--r--lib/ccdbg-debug.h44
-rw-r--r--lib/ccdbg-flash.c356
-rw-r--r--lib/ccdbg-hex.c330
-rw-r--r--lib/ccdbg-io.c137
-rw-r--r--lib/ccdbg-manual.c77
-rw-r--r--lib/ccdbg-memory.c179
-rw-r--r--lib/ccdbg-rom.c63
-rw-r--r--lib/ccdbg-state.c128
-rw-r--r--lib/ccdbg.h341
-rw-r--r--lib/cp-usb-async.c188
-rw-r--r--lib/cp-usb-async.h38
-rw-r--r--lib/cp-usb.c157
-rw-r--r--lib/cp-usb.h36
22 files changed, 3268 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 00000000..4d9ded3a
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,21 @@
+noinst_LIBRARIES = libcc.a
+
+AM_CFLAGS=$(WARN_CFLAGS) $(LIBUSB_CFLAGS)
+
+libcc_a_SOURCES = \
+ ccdbg-command.c \
+ ccdbg-debug.c \
+ ccdbg-flash.c \
+ ccdbg.h \
+ ccdbg-hex.c \
+ ccdbg-io.c \
+ ccdbg-manual.c \
+ ccdbg-memory.c \
+ ccdbg-rom.c \
+ ccdbg-state.c \
+ cc-usb.c \
+ cc-usb.h \
+ cc-bitbang.c \
+ cc-bitbang.h \
+ cp-usb.c \
+ cp-usb-async.c
diff --git a/lib/cc-bitbang.c b/lib/cc-bitbang.c
new file mode 100644
index 00000000..1d3ba476
--- /dev/null
+++ b/lib/cc-bitbang.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright © 2009 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 <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ccdbg-debug.h"
+#include "cc-bitbang.h"
+
+#define CP_USB_ASYNC
+
+#ifdef CP_USB_ASYNC
+#include "cp-usb-async.h"
+#else
+#include "cp-usb.h"
+#endif
+
+struct cc_bitbang {
+#ifdef CP_USB_ASYNC
+ struct cp_usb_async *cp_async;
+#else
+ struct cp_usb *cp;
+#endif
+};
+
+static uint32_t cc_clock_us = CC_CLOCK_US;
+static uint32_t cc_reset_us = CC_RESET_US;
+
+void
+cc_bitbang_set_clock(uint32_t us)
+{
+ cc_clock_us = us;
+}
+
+void
+cc_bitbang_half_clock(struct cc_bitbang *bb)
+{
+ struct timespec req, rem;
+ req.tv_sec = (cc_clock_us / 2) / 1000000;
+ req.tv_nsec = ((cc_clock_us / 2) % 1000000) * 1000;
+ nanosleep(&req, &rem);
+}
+
+void
+cc_bitbang_wait_reset(struct cc_bitbang *bb)
+{
+ struct timespec req, rem;
+
+ cc_bitbang_sync(bb);
+ req.tv_sec = (cc_reset_us) / 1000000;
+ req.tv_nsec = ((cc_reset_us) % 1000000) * 1000;
+ nanosleep(&req, &rem);
+}
+
+struct cc_bitbang *
+cc_bitbang_open(void)
+{
+ struct cc_bitbang *bb;
+
+ bb = calloc(sizeof (struct cc_bitbang), 1);
+ if (!bb) {
+ perror("calloc");
+ return NULL;
+ }
+#ifdef CP_USB_ASYNC
+ bb->cp_async = cp_usb_async_open();
+ if (!bb->cp_async) {
+ free (bb);
+ return NULL;
+ }
+#else
+ bb->cp = cp_usb_open ();
+ if (!bb->cp) {
+ free (bb);
+ return NULL;
+ }
+#endif
+ return bb;
+}
+
+void
+cc_bitbang_close(struct cc_bitbang *bb)
+{
+#ifdef CP_USB_ASYNC
+ cp_usb_async_close(bb->cp_async);
+#else
+ cp_usb_close(bb->cp);
+#endif
+ free (bb);
+}
+
+void
+cc_bitbang_debug_mode(struct cc_bitbang *bb)
+{
+ /* force two rising clocks while holding RESET_N low */
+ ccdbg_debug(CC_DEBUG_COMMAND, "#\n");
+ ccdbg_debug(CC_DEBUG_COMMAND, "# Debug mode\n");
+ ccdbg_debug(CC_DEBUG_COMMAND, "#\n");
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N);
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_DATA );
+ cc_bitbang_wait_reset(bb);
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA );
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_DATA );
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA );
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_DATA|CC_RESET_N);
+ cc_bitbang_wait_reset(bb);
+}
+
+void
+cc_bitbang_reset(struct cc_bitbang *bb)
+{
+ ccdbg_debug(CC_DEBUG_COMMAND, "#\n");
+ ccdbg_debug(CC_DEBUG_COMMAND, "# Reset\n");
+ ccdbg_debug(CC_DEBUG_COMMAND, "#\n");
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N);
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA );
+ cc_bitbang_wait_reset(bb);
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA );
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA );
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA );
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N);
+ cc_bitbang_wait_reset(bb);
+}
+
+int
+cc_bitbang_write(struct cc_bitbang *bb, uint8_t mask, uint8_t value)
+{
+#ifdef CP_USB_ASYNC
+ cp_usb_async_write(bb->cp_async, mask, value);
+#else
+ cp_usb_write(bb->cp, mask, value);
+#endif
+ return 0;
+}
+
+void
+cc_bitbang_read(struct cc_bitbang *bb, uint8_t *valuep)
+{
+#ifdef CP_USB_ASYNC
+ cp_usb_async_read(bb->cp_async, valuep);
+#else
+ *valuep = cp_usb_read(bb->cp);
+#endif
+}
+
+void
+cc_bitbang_sync(struct cc_bitbang *bb)
+{
+#ifdef CP_USB_ASYNC
+ cp_usb_async_sync(bb->cp_async);
+#endif
+}
+
+static char
+is_bit(uint8_t get, uint8_t mask, char on, uint8_t bit)
+{
+ if (mask&bit) {
+ if (get&bit)
+ return on;
+ else
+ return '.';
+ } else
+ return '-';
+}
+
+void
+cc_bitbang_print(char *format, uint8_t mask, uint8_t set)
+{
+ ccdbg_debug (CC_DEBUG_BITBANG, format,
+ is_bit(set, mask, 'C', CC_CLOCK),
+ is_bit(set, mask, 'D', CC_DATA),
+ is_bit(set, mask, 'R', CC_RESET_N));
+}
+
+void
+cc_bitbang_send(struct cc_bitbang *bb, uint8_t mask, uint8_t set)
+{
+ cc_bitbang_write(bb, mask, set);
+ cc_bitbang_print("%c %c %c\n", mask, set);
+ cc_bitbang_half_clock(bb);
+}
+
+void
+cc_bitbang_send_bit(struct cc_bitbang *bb, uint8_t bit)
+{
+ if (bit) bit = CC_DATA;
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|bit|CC_RESET_N);
+ cc_bitbang_send(bb, CC_CLOCK|CC_DATA|CC_RESET_N, bit|CC_RESET_N);
+}
+
+void
+cc_bitbang_send_byte(struct cc_bitbang *bb, uint8_t byte)
+{
+ int bit;
+ ccdbg_debug(CC_DEBUG_BITBANG, "#\n# Send Byte 0x%02x\n#\n", byte);
+ for (bit = 7; bit >= 0; bit--) {
+ cc_bitbang_send_bit(bb, (byte >> bit) & 1);
+ if (bit == 3)
+ ccdbg_debug(CC_DEBUG_BITBANG, "\n");
+ }
+ cc_bitbang_sync(bb);
+}
+
+void
+cc_bitbang_send_bytes(struct cc_bitbang *bb, uint8_t *bytes, int nbytes)
+{
+ while (nbytes--)
+ cc_bitbang_send_byte(bb, *bytes++);
+}
+
+void
+cc_bitbang_recv_bit(struct cc_bitbang *bb, int first, uint8_t *bit)
+{
+ uint8_t mask = first ? CC_DATA : 0;
+
+ cc_bitbang_send(bb, CC_CLOCK|mask|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N);
+ cc_bitbang_read(bb, bit);
+ cc_bitbang_send(bb, CC_CLOCK| CC_RESET_N, CC_RESET_N);
+}
+
+void
+cc_bitbang_recv_byte(struct cc_bitbang *bb, 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++) {
+ cc_bitbang_recv_bit(bb, first, &bits[bit]);
+ first = 0;
+ }
+ cc_bitbang_sync(bb);
+ for (bit = 0; bit < 8; bit++) {
+ byte = byte << 1;
+ byte |= (bits[bit] & CC_DATA) ? 1 : 0;
+ cc_bitbang_print("#\t%c %c %c\n", CC_DATA, bits[bit]);
+ if (bit == 3)
+ ccdbg_debug(CC_DEBUG_BITBANG, "\n");
+ }
+ ccdbg_debug(CC_DEBUG_BITBANG, "#\n# Recv 0x%02x\n#\n", byte);
+ *bytep = byte;
+}
+
+void
+cc_bitbang_recv_bytes(struct cc_bitbang *bb, uint8_t *bytes, int nbytes)
+{
+ int i;
+ int first = 1;
+ for (i = 0; i < nbytes; i++) {
+ cc_bitbang_recv_byte(bb, first, &bytes[i]);
+ first = 0;
+ }
+}
diff --git a/lib/cc-bitbang.h b/lib/cc-bitbang.h
new file mode 100644
index 00000000..54b20e2c
--- /dev/null
+++ b/lib/cc-bitbang.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2009 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 _CC_BITBANG_H_
+#define _CC_BITBANG_H_
+
+#include <stdint.h>
+
+#define CC_CLOCK 0x1
+#define CC_DATA 0x2
+#define CC_RESET_N 0x4
+#define CC_CLOCK_US (2)
+
+/* Telemetrum has a 10k pull-up to 3.3v, a 0.001uF cap to ground
+ * and a 2.7k resistor to the reset line. This takes about 6us
+ * to settle, so we'll wait longer than that after changing the reset line
+ */
+#define CC_RESET_US (12)
+
+struct cc_bitbang;
+
+void
+cc_bitbang_set_clock(uint32_t us);
+
+void
+cc_bitbang_half_clock(struct cc_bitbang *bb);
+
+void
+cc_bitbang_wait_reset(struct cc_bitbang *bb);
+
+struct cc_bitbang *
+cc_bitbang_open(void);
+
+void
+cc_bitbang_close(struct cc_bitbang *bb);
+
+void
+cc_bitbang_debug_mode(struct cc_bitbang *bb);
+
+void
+cc_bitbang_reset(struct cc_bitbang *bb);
+
+int
+cc_bitbang_write(struct cc_bitbang *bb, uint8_t mask, uint8_t value);
+
+void
+cc_bitbang_read(struct cc_bitbang *bb, uint8_t *valuep);
+
+void
+cc_bitbang_sync(struct cc_bitbang *bb);
+
+void
+cc_bitbang_print(char *format, uint8_t mask, uint8_t set);
+
+void
+cc_bitbang_print(char *format, uint8_t mask, uint8_t set);
+
+void
+cc_bitbang_send(struct cc_bitbang *bb, uint8_t mask, uint8_t set);
+
+void
+cc_bitbang_send_bit(struct cc_bitbang *bb, uint8_t bit);
+
+void
+cc_bitbang_send_byte(struct cc_bitbang *bb, uint8_t byte);
+
+void
+cc_bitbang_send_bytes(struct cc_bitbang *bb, uint8_t *bytes, int nbytes);
+
+void
+cc_bitbang_recv_bit(struct cc_bitbang *bb, int first, uint8_t *bit);
+
+void
+cc_bitbang_recv_byte(struct cc_bitbang *bb, int first, uint8_t *bytep);
+
+void
+cc_bitbang_recv_bytes(struct cc_bitbang *bb, uint8_t *bytes, int nbytes);
+
+#endif /* _CC_BITBANG_H_ */
diff --git a/lib/cc-usb.c b/lib/cc-usb.c
new file mode 100644
index 00000000..dc764c24
--- /dev/null
+++ b/lib/cc-usb.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright © 2009 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include "ccdbg-debug.h"
+#include "cc-usb.h"
+
+
+#define CC_NUM_READ 16
+/*
+ * AltOS has different buffer sizes for in/out packets
+ */
+#define CC_IN_BUF 256
+#define CC_OUT_BUF 64
+#define DEFAULT_TTY "/dev/ttyACM0"
+
+struct cc_read {
+ uint8_t *buf;
+ int len;
+};
+
+struct cc_usb {
+ int fd;
+ uint8_t in_buf[CC_IN_BUF];
+ int in_count;
+ uint8_t out_buf[CC_OUT_BUF];
+ int out_count;
+ struct cc_read read_buf[CC_NUM_READ];
+ int read_count;
+};
+
+#define NOT_HEX 0xff
+
+static uint8_t
+cc_hex_nibble(uint8_t c)
+{
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return c - 'a' + 10;
+ if ('A' <= c && c <= 'F')
+ return c - 'A' + 10;
+ return NOT_HEX;
+}
+
+/*
+ * Take raw input bytes, parse them as hex
+ * and write them to the waiting buffer
+ */
+static void
+cc_handle_in(struct cc_usb *cc)
+{
+ uint8_t h, l;
+ int in_pos;
+ int read_pos;
+
+ in_pos = 0;
+ read_pos = 0;
+ while (read_pos < cc->read_count && in_pos < cc->in_count) {
+ /*
+ * Skip to next hex character
+ */
+ while (in_pos < cc->in_count &&
+ cc_hex_nibble(cc->in_buf[in_pos]) == NOT_HEX)
+ in_pos++;
+ /*
+ * Make sure we have two characters left
+ */
+ if (cc->in_count - in_pos < 2)
+ break;
+ /*
+ * Parse hex number
+ */
+ h = cc_hex_nibble(cc->in_buf[in_pos]);
+ l = cc_hex_nibble(cc->in_buf[in_pos+1]);
+ if (h == NOT_HEX || l == NOT_HEX) {
+ fprintf(stderr, "hex read error\n");
+ break;
+ }
+ in_pos += 2;
+ /*
+ * Store hex number
+ */
+ *cc->read_buf[read_pos].buf++ = (h << 4) | l;
+ if (--cc->read_buf[read_pos].len <= 0)
+ read_pos++;
+ }
+
+ /* Move remaining bytes to the start of the input buffer */
+ if (in_pos) {
+ memmove(cc->in_buf, cc->in_buf + in_pos,
+ cc->in_count - in_pos);
+ cc->in_count -= in_pos;
+ }
+
+ /* Move pending reads to the start of the array */
+ if (read_pos) {
+ memmove(cc->read_buf, cc->read_buf + read_pos,
+ (cc->read_count - read_pos) * sizeof (cc->read_buf[0]));
+ cc->read_count -= read_pos;
+ }
+
+ /* Once we're done reading, flush any pending input */
+ if (cc->read_count == 0)
+ cc->in_count = 0;
+}
+
+static void
+cc_usb_dbg(int indent, uint8_t *bytes, int len)
+{
+ int eol = 1;
+ int i;
+ uint8_t c;
+ while (len--) {
+ c = *bytes++;
+ if (eol) {
+ for (i = 0; i < indent; i++)
+ ccdbg_debug(CC_DEBUG_BITBANG, " ");
+ eol = 0;
+ }
+ switch (c) {
+ case '\r':
+ ccdbg_debug(CC_DEBUG_BITBANG, "^M");
+ break;
+ case '\n':
+ eol = 1;
+ default:
+ ccdbg_debug(CC_DEBUG_BITBANG, "%c", c);
+ }
+ }
+}
+
+/*
+ * Flush pending writes, fill pending reads
+ */
+void
+cc_usb_sync(struct cc_usb *cc)
+{
+ int ret;
+ struct pollfd fds;
+ int timeout;
+
+ fds.fd = cc->fd;
+ for (;;) {
+ if (cc->read_count || cc->out_count)
+ timeout = -1;
+ else
+ timeout = 0;
+ fds.events = 0;
+ if (cc->in_count < CC_IN_BUF)
+ fds.events |= POLLIN;
+ if (cc->out_count)
+ fds.events |= POLLOUT;
+ ret = poll(&fds, 1, timeout);
+ if (ret == 0)
+ break;
+ if (ret < 0) {
+ perror("poll");
+ break;
+ }
+ if (fds.revents & POLLIN) {
+ ret = read(cc->fd, cc->in_buf + cc->in_count,
+ CC_IN_BUF - cc->in_count);
+ if (ret > 0) {
+ cc_usb_dbg(24, cc->in_buf + cc->in_count, ret);
+ cc->in_count += ret;
+ cc_handle_in(cc);
+ } else if (ret < 0)
+ perror("read");
+ }
+ if (fds.revents & POLLOUT) {
+ ret = write(cc->fd, cc->out_buf,
+ cc->out_count);
+ if (ret > 0) {
+ cc_usb_dbg(0, cc->out_buf, ret);
+ memmove(cc->out_buf,
+ cc->out_buf + ret,
+ cc->out_count - ret);
+ cc->out_count -= ret;
+ } else if (ret < 0)
+ perror("write");
+ }
+ }
+}
+
+void
+cc_usb_printf(struct cc_usb *cc, char *format, ...)
+{
+ char buf[1024], *b;
+ va_list ap;
+ int ret, this_time;
+
+ /* sprintf to a local buffer */
+ va_start(ap, format);
+ ret = vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ if (ret > sizeof(buf)) {
+ fprintf(stderr, "printf overflow for format %s\n",
+ format);
+ }
+
+ /* flush local buffer to the wire */
+ b = buf;
+ while (ret > 0) {
+ this_time = ret;
+ if (this_time > CC_OUT_BUF - cc->out_count)
+ this_time = CC_OUT_BUF - cc->out_count;
+ memcpy(cc->out_buf + cc->out_count, b, this_time);
+ cc->out_count += this_time;
+ ret -= this_time;
+ b += this_time;
+ while (cc->out_count >= CC_OUT_BUF)
+ cc_usb_sync(cc);
+ }
+}
+
+int
+cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len)
+{
+ int this_len;
+ int ret = len;
+
+ while (len) {
+ this_len = len;
+ if (this_len > 8)
+ this_len = 8;
+ len -= this_len;
+ cc_usb_printf(cc, "P");
+ while (this_len--)
+ cc_usb_printf (cc, " %02x", (*bytes++) & 0xff);
+ cc_usb_printf(cc, "\n");
+ }
+ return ret;
+}
+
+void
+cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len)
+{
+ struct cc_read *read_buf;
+ while (cc->read_count >= CC_NUM_READ)
+ cc_usb_sync(cc);
+ read_buf = &cc->read_buf[cc->read_count++];
+ read_buf->buf = buf;
+ read_buf->len = len;
+}
+
+int
+cc_usb_recv_bytes(struct cc_usb *cc, uint8_t *buf, int len)
+{
+ cc_queue_read(cc, buf, len);
+ cc_usb_printf(cc, "G %x\n", len);
+ return len;
+}
+
+int
+cc_usb_write_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
+{
+ cc_usb_printf(cc, "O %x %x\n", len, addr);
+ while (len--)
+ cc_usb_printf(cc, "%02x", *bytes++);
+ return 0;
+}
+
+int
+cc_usb_read_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len)
+{
+ int i;
+ cc_queue_read(cc, bytes, len);
+ cc_usb_printf(cc, "I %x %x\n", len, addr);
+ cc_usb_sync(cc);
+ for (i = 0; i < len; i++) {
+ if ((i & 15) == 0) {
+ if (i)
+ ccdbg_debug(CC_DEBUG_MEMORY, "\n");
+ ccdbg_debug(CC_DEBUG_MEMORY, "\t%04x", addr + i);
+ }
+ ccdbg_debug(CC_DEBUG_MEMORY, " %02x", bytes[i]);
+ }
+ ccdbg_debug(CC_DEBUG_MEMORY, "\n");
+ return 0;
+}
+
+int
+cc_usb_debug_mode(struct cc_usb *cc)
+{
+ cc_usb_sync(cc);
+ cc_usb_printf(cc, "D\n");
+ return 1;
+}
+
+int
+cc_usb_reset(struct cc_usb *cc)
+{
+ cc_usb_sync(cc);
+ cc_usb_printf(cc, "R\n");
+ return 1;
+}
+
+static struct termios save_termios;
+
+struct cc_usb *
+cc_usb_open(char *tty)
+{
+ struct cc_usb *cc;
+ struct termios termios;
+
+ if (!tty)
+ tty = DEFAULT_TTY;
+ cc = calloc (sizeof (struct cc_usb), 1);
+ if (!cc)
+ return NULL;
+ cc->fd = open(tty, O_RDWR | O_NONBLOCK);
+ if (cc->fd < 0) {
+ perror(tty);
+ free (cc);
+ return NULL;
+ }
+ tcgetattr(cc->fd, &termios);
+ save_termios = termios;
+ cfmakeraw(&termios);
+ tcsetattr(cc->fd, TCSAFLUSH, &termios);
+ cc_usb_printf(cc, "E 0\n");
+ cc_usb_sync(cc);
+ sleep(1);
+ cc_usb_sync(cc);
+ return cc;
+}
+
+void
+cc_usb_close(struct cc_usb *cc)
+{
+ tcsetattr(cc->fd, TCSAFLUSH, &save_termios);
+ close (cc->fd);
+ free (cc);
+}
diff --git a/lib/cc-usb.h b/lib/cc-usb.h
new file mode 100644
index 00000000..d7acfbd2
--- /dev/null
+++ b/lib/cc-usb.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2009 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 _CC_USB_H_
+#define _CC_USB_H_
+
+#include <stdint.h>
+
+struct cc_usb;
+
+struct cc_usb *
+cc_usb_open(char *tty);
+
+void
+cc_usb_close(struct cc_usb *cc);
+
+int
+cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len);
+
+int
+cc_usb_recv_bytes(struct cc_usb *cc, uint8_t *bytes, int len);
+
+int
+cc_usb_write_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len);
+
+int
+cc_usb_read_memory(struct cc_usb *cc, uint16_t addr, uint8_t *bytes, int len);
+
+int
+cc_usb_debug_mode(struct cc_usb *cc);
+
+int
+cc_usb_reset(struct cc_usb *cc);
+
+void
+cc_usb_sync(struct cc_usb *cc);
+
+void
+cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len);
+
+void
+cc_usb_printf(struct cc_usb *cc, char *format, ...);
+
+#endif /* _CC_USB_H_ */
diff --git a/lib/cccp.c b/lib/cccp.c
new file mode 100644
index 00000000..34e866e8
--- /dev/null
+++ b/lib/cccp.c
@@ -0,0 +1,112 @@
+/*
+ * 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 "ccdbg.h"
+
+static void
+say(char *name, uint8_t bits)
+{
+ printf("%s: ", name);
+ if (bits & CC_RESET_N)
+ printf ("R ");
+ else
+ printf (". ");
+ if (bits & CC_CLOCK)
+ printf ("C ");
+ else
+ printf (". ");
+ if (bits & CC_DATA)
+ printf ("D\n");
+ else
+ printf (".\n");
+}
+
+static void
+_cccp_write(struct ccdbg *dbg, uint8_t mask, uint8_t value)
+{
+ uint16_t set;
+ int ret;
+
+ set = (mask) | (value << 8);
+ dbg->debug_data = (dbg->debug_data & ~mask) | (value & mask);
+ ret = ioctl(dbg->fd, CP2101_IOCTL_GPIOSET, &set);
+ if (ret < 0)
+ perror("CP2101_IOCTL_GPIOSET");
+}
+
+void
+cccp_write(struct ccdbg *dbg, uint8_t mask, uint8_t value)
+{
+ _cccp_write(dbg, mask, value);
+// say("w", dbg->debug_data);
+}
+
+uint8_t
+cccp_read_all(struct ccdbg *dbg)
+{
+ int ret;
+ uint8_t get;
+ ret = ioctl(dbg->fd, CP2101_IOCTL_GPIOGET, &get);
+ if (ret < 0) {
+ perror("CP2101_IOCTL_GPIOGET");
+ get = 0;
+ }
+ return get;
+}
+
+uint8_t
+cccp_read(struct ccdbg *dbg, uint8_t mask)
+{
+ uint8_t pull_up;
+ uint8_t get;
+
+ /* tri-state the bits of interest */
+ pull_up = (~dbg->debug_data) & mask;
+ if (pull_up)
+ _cccp_write(dbg, pull_up, pull_up);
+ get = cccp_read_all(dbg);
+ say("\t\tr", get);
+ return get & mask;
+}
+
+void
+cccp_init(struct ccdbg *dbg)
+{
+ /* set all of the GPIOs to a known state */
+ cccp_write(dbg, 0xf, 0xf);
+}
+
+void
+cccp_fini(struct ccdbg *dbg)
+{
+ /* set all of the GPIOs to a known state */
+ cccp_write(dbg, 0xf, 0xf);
+ dbg->clock = 1;
+}
+
+cccp_open()
+{
+ dbg->fd = open("/dev/ttyUSB0", 2);
+ if (dbg->fd < 0) {
+ perror(file);
+ free(dbg);
+ return NULL;
+ }
+ cccp_init(dbg);
+ cccp_write(dbg, CC_CLOCK, CC_CLOCK);
+}
diff --git a/lib/cccp.h b/lib/cccp.h
new file mode 100644
index 00000000..eecdbb49
--- /dev/null
+++ b/lib/cccp.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+/*
+ * Interface for using a CP2103 to talk to a CC1111
+ */
+
+#ifndef _CCCP_H_
+#define _CCCP_H_
+
+void
+cccp_write(struct ccdbg *dbg, uint8_t mask, uint8_t value);
+
+uint8_t
+cccp_read_all(struct ccdbg *dbg);
+
+uint8_t
+cccp_read(struct ccdbg *dbg, uint8_t mask);
+
+void
+cccp_init(struct ccdbg *dbg);
+
+void
+cccp_fini(struct ccdbg *dbg);
+
+#endif /* _CCCP_H_ */
diff --git a/lib/ccdbg-command.c b/lib/ccdbg-command.c
new file mode 100644
index 00000000..a1002879
--- /dev/null
+++ b/lib/ccdbg-command.c
@@ -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 "ccdbg.h"
+
+uint8_t
+ccdbg_chip_erase(struct ccdbg *dbg)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_CHIP_ERASE, NULL, 0);
+}
+
+uint8_t
+ccdbg_wr_config(struct ccdbg *dbg, uint8_t config)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_WR_CONFIG, &config, 1);
+}
+
+uint8_t
+ccdbg_rd_config(struct ccdbg *dbg)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_RD_CONFIG, NULL, 0);
+}
+
+uint16_t
+ccdbg_get_pc(struct ccdbg *dbg)
+{
+ uint16_t pc1, pc2;
+
+ pc1 = ccdbg_cmd_write_read16(dbg, CC_GET_PC, NULL, 0);
+ pc2 = ccdbg_cmd_write_read16(dbg, CC_GET_PC, NULL, 0);
+ if (pc1 != pc2)
+ fprintf (stderr, "Invalid pc %04x != %04x\n",
+ pc1, pc2);
+ return pc2;
+}
+
+uint8_t
+ccdbg_read_status(struct ccdbg *dbg)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_READ_STATUS, NULL, 0);
+}
+
+uint8_t
+ccdbg_set_hw_brkpnt(struct ccdbg *dbg, uint8_t number, uint8_t enable, uint16_t addr)
+{
+ uint8_t data[3];
+
+ data[0] = (number << 3) | (enable << 2);
+ data[1] = (addr >> 8);
+ data[2] = addr;
+ return ccdbg_cmd_write_read8(dbg, CC_SET_HW_BRKPNT, data, 3);
+}
+
+uint8_t
+ccdbg_halt(struct ccdbg *dbg)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_HALT, NULL, 0);
+}
+
+uint8_t
+ccdbg_resume(struct ccdbg *dbg)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_RESUME, NULL, 0);
+}
+
+uint8_t
+ccdbg_debug_instr(struct ccdbg *dbg, uint8_t *instr, int nbytes)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_DEBUG_INSTR(nbytes), instr, nbytes);
+}
+
+void
+ccdbg_debug_instr_discard(struct ccdbg *dbg, uint8_t *instr, int nbytes)
+{
+ static uint8_t discard;
+ ccdbg_cmd_write_queue8(dbg, CC_DEBUG_INSTR(nbytes),
+ instr, nbytes, &discard);
+}
+
+void
+ccdbg_debug_instr_queue(struct ccdbg *dbg, uint8_t *instr, int nbytes,
+ uint8_t *reply)
+{
+ return ccdbg_cmd_write_queue8(dbg, CC_DEBUG_INSTR(nbytes),
+ instr, nbytes, reply);
+}
+
+uint8_t
+ccdbg_step_instr(struct ccdbg *dbg)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_STEP_INSTR, NULL, 0);
+}
+
+uint8_t
+ccdbg_step_replace(struct ccdbg *dbg, uint8_t *instr, int nbytes)
+{
+ return ccdbg_cmd_write_read8(dbg, CC_STEP_REPLACE(nbytes), instr, nbytes);
+}
+
+uint16_t
+ccdbg_get_chip_id(struct ccdbg *dbg)
+{
+ return ccdbg_cmd_write_read16(dbg, CC_GET_CHIP_ID, NULL, 0);
+}
+
+/*
+ * Execute a sequence of instructions
+ */
+uint8_t
+ccdbg_execute(struct ccdbg *dbg, uint8_t *inst)
+{
+ uint8_t status = 0;
+ while(inst[0] != 0) {
+ uint8_t len = inst[0];
+ int i;
+ ccdbg_debug(CC_DEBUG_INSTRUCTIONS, "\t%02x", inst[1]);
+ for (i = 0; i < len - 1; i++)
+ ccdbg_debug(CC_DEBUG_INSTRUCTIONS, " %02x", inst[i+2]);
+ ccdbg_debug_instr_queue(dbg, inst+1, len, &status);
+ for (; i < 3; i++)
+ ccdbg_debug(CC_DEBUG_INSTRUCTIONS, " ");
+ ccdbg_debug(CC_DEBUG_INSTRUCTIONS, " -> %02x\n", status);
+ inst += len + 1;
+ }
+ ccdbg_sync(dbg);
+ return status;
+}
+
+static uint8_t jump_mem[] = {
+ 3, LJMP, 0xf0, 0x00,
+#define PC_HIGH 2
+#define PC_LOW 3
+ 0
+};
+
+uint8_t
+ccdbg_set_pc(struct ccdbg *dbg, uint16_t pc)
+{
+ jump_mem[PC_HIGH] = pc >> 8;
+ jump_mem[PC_LOW] = pc & 0xff;
+ return ccdbg_execute(dbg, jump_mem);
+}
+
+uint8_t
+ccdbg_execute_hex_image(struct ccdbg *dbg, struct hex_image *image)
+{
+ uint16_t pc;
+ uint8_t status;
+
+ if (image->address < 0xf000) {
+ fprintf(stderr, "Cannot execute program starting at 0x%04x\n", image->address);
+ return -1;
+ }
+ ccdbg_write_hex_image(dbg, image, 0);
+ ccdbg_set_pc(dbg, image->address);
+ pc = ccdbg_get_pc(dbg);
+ ccdbg_debug(CC_DEBUG_EXECUTE, "pc starts at 0x%04x\n", pc);
+ status = ccdbg_resume(dbg);
+ ccdbg_debug(CC_DEBUG_EXECUTE, "resume status: 0x%02x\n", status);
+ return 0;
+}
diff --git a/lib/ccdbg-debug.c b/lib/ccdbg-debug.c
new file mode 100644
index 00000000..6eb4e0c5
--- /dev/null
+++ b/lib/ccdbg-debug.c
@@ -0,0 +1,63 @@
+/*
+ * 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 "ccdbg.h"
+#include <stdarg.h>
+
+int
+ccdbg_level = 0;
+
+void
+ccdbg_add_debug(int level)
+{
+ ccdbg_level |= level;
+}
+
+void
+ccdbg_clear_debug(int level)
+{
+ ccdbg_level &= ~level;
+}
+
+static int initialized;
+
+void
+ccdbg_debug(int level, char *format, ...)
+{
+ va_list ap;
+
+ if (!initialized) {
+ char *level;
+ initialized = 1;
+ level = getenv("CCDEBUG");
+ if (level)
+ ccdbg_level |= strtoul(level, NULL, 0);
+ }
+ if (ccdbg_level & level) {
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ }
+}
+
+void
+ccdbg_flush(int level)
+{
+ if (ccdbg_level & level)
+ fflush(stdout);
+}
diff --git a/lib/ccdbg-debug.h b/lib/ccdbg-debug.h
new file mode 100644
index 00000000..0b5b44c1
--- /dev/null
+++ b/lib/ccdbg-debug.h
@@ -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(int level);
+
+#endif /* _CCDBG_DEBUG_H_ */
diff --git a/lib/ccdbg-flash.c b/lib/ccdbg-flash.c
new file mode 100644
index 00000000..3e672985
--- /dev/null
+++ b/lib/ccdbg-flash.c
@@ -0,0 +1,356 @@
+/*
+ * 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 "ccdbg.h"
+
+/* From SWRA124 section 3.1.6 */
+
+static uint8_t flash_page[] = {
+
+ MOV_direct_data, P1DIR, 0x02,
+ MOV_direct_data, P1, 0xFF,
+
+ MOV_direct_data, FADDRH, 0,
+#define FLASH_ADDR_HIGH 8
+
+ MOV_direct_data, FADDRL, 0,
+#define FLASH_ADDR_LOW 11
+
+ MOV_DPTR_data16, 0, 0,
+#define RAM_ADDR_HIGH 13
+#define RAM_ADDR_LOW 14
+
+ MOV_Rn_data(7), 0,
+#define FLASH_WORDS_HIGH 16
+
+ MOV_Rn_data(6), 0,
+#define FLASH_WORDS_LOW 18
+
+ MOV_direct_data, FWT, 0x20,
+#define FLASH_TIMING 21
+
+ MOV_direct_data, FCTL, FCTL_ERASE,
+/* eraseWaitLoop: */
+ MOV_A_direct, FCTL,
+ JB, ACC(FCTL_BUSY_BIT), 0xfb,
+
+ MOV_direct_data, P1, 0xfd,
+
+ MOV_direct_data, FCTL, FCTL_WRITE,
+/* writeLoop: */
+ MOV_Rn_data(5), 2,
+/* writeWordLoop: */
+ MOVX_A_atDPTR,
+ INC_DPTR,
+ MOV_direct_A, FWDATA,
+ DJNZ_Rn_rel(5), 0xfa, /* writeWordLoop */
+/* writeWaitLoop: */
+ MOV_A_direct, FCTL,
+ JB, ACC(FCTL_SWBSY_BIT), 0xfb, /* writeWaitLoop */
+ DJNZ_Rn_rel(6), 0xf1, /* writeLoop */
+ DJNZ_Rn_rel(7), 0xef, /* writeLoop */
+
+ MOV_direct_data, P1DIR, 0x00,
+ MOV_direct_data, P1, 0xFF,
+ TRAP,
+};
+
+#define FLASH_RAM 0xf000
+
+#if 0
+static uint8_t flash_erase_page[] = {
+ 3, MOV_direct_data, FADDRH, 0,
+#define ERASE_PAGE_HIGH 3
+
+ 3, MOV_direct_data, FADDRL, 0,
+#define ERASE_PAGE_LOW 7
+
+ 3, MOV_direct_data, FWT, 0x2A,
+ 3, MOV_direct_data, FCTL, FCTL_ERASE,
+ 0
+};
+
+static uint8_t flash_read_control[] = {
+ 2, MOV_A_direct, FCTL,
+ 0
+};
+#endif
+
+#if 0
+static uint8_t flash_control_clear[] = {
+ 3, MOV_direct_data, FCTL, 0,
+ 2, MOV_A_direct, FCTL,
+ 0
+};
+#endif
+
+#if 0
+static uint8_t
+ccdbg_flash_erase_page(struct ccdbg *dbg, uint16_t addr)
+{
+ uint16_t page_addr = addr >> 1;
+ uint8_t status;
+ uint8_t old[0x10], new[0x10];
+ int i;
+
+ ccdbg_read_memory(dbg, addr, old, 0x10);
+ flash_erase_page[ERASE_PAGE_HIGH] = page_addr >> 8;
+ flash_erase_page[ERASE_PAGE_LOW] = page_addr & 0xff;
+ status = ccdbg_execute(dbg, flash_erase_page);
+ ccdbg_debug(CC_DEBUG_FLASH, "erase status 0x%02x\n", status);
+ do {
+ status = ccdbg_execute(dbg, flash_read_control);
+ ccdbg_debug(CC_DEBUG_FLASH, "fctl 0x%02x\n", status);
+ } while (status & FCTL_BUSY);
+ ccdbg_read_memory(dbg, addr, new, 0x10);
+ for (i = 0; i < 0x10; i++)
+ ccdbg_debug(CC_DEBUG_FLASH, "0x%02x -> 0x%02x\n", old[i], new[i]);
+ status = ccdbg_execute(dbg, flash_control_clear);
+ ccdbg_debug(CC_DEBUG_FLASH, "clear fctl 0x%02x\n", status);
+ return 0;
+}
+#endif
+
+#if 0
+static uint8_t flash_write[] = {
+ MOV_direct_data, P1DIR, 0x02,
+ MOV_direct_data, P1, 0xFD,
+
+ MOV_A_direct, FCTL,
+ JB, ACC(FCTL_BUSY_BIT), 0xf1,
+
+ MOV_direct_data, FCTL, 0x20,
+
+ MOV_direct_data, FADDRH, 0,
+#define WRITE_PAGE_HIGH 16
+
+ MOV_direct_data, FADDRL, 0,
+#define WRITE_PAGE_LOW 19
+
+ MOV_direct_data, FCTL, FCTL_WRITE,
+ MOV_direct_data, FWDATA, 0,
+#define WRITE_BYTE_0 25
+ MOV_direct_data, FWDATA, 0,
+#define WRITE_BYTE_1 28
+ MOV_A_direct, FCTL,
+ JB, ACC(FCTL_SWBSY_BIT), 0xf1,
+
+ MOV_direct_data, P1, 0xFF,
+ TRAP,
+};
+#endif
+
+static uint8_t
+ccdbg_clock_init(struct ccdbg *dbg)
+{
+ static uint8_t set_clkcon_fast[] = {
+ 3, MOV_direct_data, CLKCON, 0x00,
+ 0
+ };
+
+ static uint8_t get_sleep[] = {
+ 2, MOV_A_direct, SLEEP,
+ 0
+ };
+
+ uint8_t status;
+
+ ccdbg_execute(dbg, set_clkcon_fast);
+ do {
+ status = ccdbg_execute(dbg, get_sleep);
+ } while (!(status & 0x40));
+ return 0;
+}
+
+#if 0
+static uint8_t
+ccdbg_flash_write_word(struct ccdbg *dbg, uint16_t addr, uint8_t data[2])
+{
+ uint16_t page_addr = addr >> 1;
+ uint8_t check[2];
+ uint8_t status;
+ int i;
+
+ flash_write[WRITE_PAGE_HIGH] = page_addr >> 8;
+ flash_write[WRITE_PAGE_LOW] = page_addr & 0xff;
+ flash_write[WRITE_BYTE_0] = data[0];
+ flash_write[WRITE_BYTE_1] = data[1];
+ ccdbg_debug(CC_DEBUG_FLASH, "upload flash write\n");
+ ccdbg_write_memory(dbg, 0xf000, flash_write, sizeof(flash_write));
+ ccdbg_set_pc(dbg, 0xf000);
+ ccdbg_resume(dbg);
+ for (;;) {
+ status = ccdbg_read_status(dbg);
+ ccdbg_debug(CC_DEBUG_FLASH, "waiting for write 0x%02x\n", status);
+ if ((status & CC_STATUS_CPU_HALTED) != 0)
+ break;
+ sleep (1);
+ }
+ status = ccdbg_execute(dbg, flash_control_clear);
+ ccdbg_debug(CC_DEBUG_FLASH, "clear fctl 0x%02x\n", status);
+ ccdbg_read_memory(dbg, addr, check, 2);
+ for (i = 0; i < 2; i++)
+ ccdbg_debug(CC_DEBUG_FLASH, "0x%02x : 0x%02x\n", data[i], check[i]);
+ return 0;
+}
+#endif
+
+#define TIMERS_OFF 0x08
+#define DMA_PAUSE 0x04
+#define TIMER_SUSPEND 0x02
+#define SEL_FLASH_INFO_PAGE 0x01
+
+#if 0
+static uint8_t
+ccdbg_flash_lock(struct ccdbg *dbg, uint8_t lock)
+{
+ uint8_t config;
+ uint8_t bytes[2];
+ uint8_t old[1], new[1];
+
+ config = ccdbg_rd_config(dbg);
+ ccdbg_wr_config(dbg, config|SEL_FLASH_INFO_PAGE);
+ bytes[0] = lock;
+ bytes[1] = 0;
+ ccdbg_flash_erase_page(dbg, 0);
+ ccdbg_read_memory(dbg, 0, old, 1);
+ ccdbg_flash_write_word(dbg, 0, bytes);
+ ccdbg_read_memory(dbg, 0, new, 1);
+ ccdbg_debug(CC_DEBUG_FLASH, "flash lock 0x%02x -> 0x%02x\n", old[0], new[0]);
+ ccdbg_wr_config(dbg, config & ~SEL_FLASH_INFO_PAGE);
+ return 0;
+}
+#endif
+
+uint8_t
+ccdbg_flash_hex_image(struct ccdbg *dbg, struct hex_image *image)
+{
+ uint16_t offset;
+ uint16_t flash_prog;
+ uint16_t flash_len;
+ uint8_t fwt;
+ uint16_t flash_addr;
+ uint16_t flash_word_addr;
+ uint16_t flash_words;
+ uint8_t flash_words_high, flash_words_low;
+ uint16_t ram_addr;
+ uint16_t pc;
+ uint8_t status;
+ uint16_t remain, this_time, start;
+ uint8_t verify[0x400];
+ int times;
+
+ ccdbg_clock_init(dbg);
+ if (image->address + image->length > 0x8000) {
+ fprintf(stderr, "cannot flash image from 0x%04x to 0x%04x\n",
+ image->address, image->address + image->length);
+ return 1;
+ }
+ if (image->address & 0x3ff) {
+ fprintf(stderr, "flash image must start on page boundary\n");
+ return 1;
+ }
+ ram_addr = 0xf000;
+
+
+ flash_prog = 0xf400;
+
+ fwt = 0x20;
+
+ flash_page[FLASH_TIMING] = fwt;
+ ccdbg_debug(CC_DEBUG_FLASH, "Upload %d flash program bytes to 0x%04x\n",
+ sizeof (flash_page), flash_prog);
+ ccdbg_write_memory(dbg, flash_prog, flash_page, sizeof(flash_page));
+
+ remain = image->length;
+ start = 0;
+ while (remain) {
+ this_time = remain;
+ if (this_time > 0x400)
+ this_time = 0x400;
+
+ offset = ram_addr - (image->address + start);
+
+ ccdbg_debug(CC_DEBUG_FLASH, "Upload %d bytes at 0x%04x\n", this_time, ram_addr);
+ ccdbg_write_memory(dbg, ram_addr, image->data + start, this_time);
+#if 0
+ ccdbg_debug(CC_DEBUG_FLASH, "Verify %d bytes in ram\n", this_time);
+ ccdbg_read_memory(dbg, ram_addr, verify, this_time);
+ if (memcmp (image->data + start, verify, this_time) != 0) {
+ fprintf(stderr, "ram verify failed\n");
+ return 1;
+ }
+#endif
+
+ flash_addr = image->address + start;
+ flash_word_addr = flash_addr >> 1;
+ flash_len = this_time + (this_time & 1);
+ flash_words = flash_len >> 1;
+
+ flash_words_low = flash_words & 0xff;
+ flash_words_high = flash_words >> 8;
+
+ /* The flash code above is lame */
+ if (flash_words_low)
+ flash_words_high++;
+
+ ccdbg_write_uint8(dbg, flash_prog + FLASH_ADDR_HIGH, flash_word_addr >> 8);
+ ccdbg_write_uint8(dbg, flash_prog + FLASH_ADDR_LOW, flash_word_addr & 0xff);
+
+ ccdbg_write_uint8(dbg, flash_prog + RAM_ADDR_HIGH, ram_addr >> 8);
+ ccdbg_write_uint8(dbg, flash_prog + RAM_ADDR_LOW, ram_addr & 0xff);
+
+ ccdbg_write_uint8(dbg, flash_prog + FLASH_WORDS_HIGH, flash_words_high);
+ ccdbg_write_uint8(dbg, flash_prog + FLASH_WORDS_LOW, flash_words_low);
+
+ ccdbg_set_pc(dbg, flash_prog);
+ pc = ccdbg_get_pc(dbg);
+ ccdbg_debug(CC_DEBUG_FLASH, "Flashing %d bytes at 0x%04x\n",
+ this_time, flash_addr);
+ status = ccdbg_resume(dbg);
+ for (times = 0; times < 10; times++) {
+ status = ccdbg_read_status(dbg);
+ ccdbg_debug(CC_DEBUG_FLASH, ".");
+ ccdbg_flush(CC_DEBUG_FLASH);
+ if ((status & CC_STATUS_CPU_HALTED) != 0)
+ break;
+ usleep(10000);
+ }
+ ccdbg_debug(CC_DEBUG_FLASH, "\n");
+ if (times == 10) {
+ fprintf(stderr, "flash page timed out\n");
+ return 1;
+ }
+
+ ccdbg_debug(CC_DEBUG_FLASH, "Verify %d bytes in flash\n", this_time);
+ ccdbg_read_memory(dbg, flash_addr, verify, this_time);
+ if (memcmp (image->data + start, verify, this_time) != 0) {
+ int i;
+ fprintf(stderr, "flash verify failed\n");
+ for (i = 0; i < this_time; i++) {
+ if (image->data[start + i] != verify[i])
+ fprintf(stderr, "0x%04x: 0x%02x != 0x%02x\n",
+ start + i, image->data[start+i], verify[i]);
+ }
+ return 1;
+ }
+ remain -= this_time;
+ start += this_time;
+ }
+ return 0;
+}
diff --git a/lib/ccdbg-hex.c b/lib/ccdbg-hex.c
new file mode 100644
index 00000000..dfea9156
--- /dev/null
+++ b/lib/ccdbg-hex.c
@@ -0,0 +1,330 @@
+/*
+ * 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 "ccdbg.h"
+#include <stdarg.h>
+#include <ctype.h>
+
+struct hex_input {
+ FILE *file;
+ int line;
+ char *name;
+};
+
+enum hex_read_state {
+ read_marker,
+ read_length,
+ read_address,
+ read_type,
+ read_data,
+ read_checksum,
+ read_newline,
+ read_white,
+ read_done,
+};
+
+
+static void
+ccdbg_hex_error(struct hex_input *input, char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ fprintf(stderr, "Hex error %s:%d: ", input->name, input->line);
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static void
+ccdbg_hex_free(struct hex_record *record)
+{
+ if (!record) return;
+ free(record);
+}
+
+static struct hex_record *
+ccdbg_hex_alloc(uint8_t length)
+{
+ struct hex_record *record;
+
+ record = calloc(1, sizeof(struct hex_record) + length);
+ record->length = length;
+ return record;
+}
+
+static int
+ishex(char c)
+{
+ return isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+}
+
+static int
+fromhex(char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return c - 'a' + 10;
+ if ('A' <= c && c <= 'F')
+ return c - 'A' + 10;
+ abort();
+ return 0;
+}
+
+static uint8_t
+ccdbg_hex_checksum(struct hex_record *record)
+{
+ uint8_t checksum = 0;
+ int i;
+
+ checksum += record->length;
+ checksum += record->address >> 8;
+ checksum += record->address & 0xff;
+ checksum += record->type;
+ for (i = 0; i < record->length; i++)
+ checksum += record->data[i];
+ return -checksum;
+}
+
+static struct hex_record *
+ccdbg_hex_read_record(struct hex_input *input)
+{
+ struct hex_record *record = NULL;
+ enum hex_read_state state = read_marker;
+ char c;
+ int nhexbytes;
+ uint32_t hex;
+ uint32_t ndata;
+ uint8_t checksum;
+
+ while (state != read_done) {
+ c = getc(input->file);
+ if (c == EOF && state != read_white) {
+ ccdbg_hex_error(input, "Unexpected EOF");
+ goto bail;
+ }
+ if (c == ' ')
+ continue;
+ if (c == '\n')
+ input->line++;
+ switch (state) {
+ case read_marker:
+ if (c != ':') {
+ ccdbg_hex_error(input, "Missing ':'");
+ goto bail;
+ }
+ state = read_length;
+ nhexbytes = 2;
+ hex = 0;
+ break;
+ case read_length:
+ case read_address:
+ case read_type:
+ case read_data:
+ case read_checksum:
+ if (!ishex(c)) {
+ ccdbg_hex_error(input, "Non-hex char '%c'",
+ c);
+ goto bail;
+ }
+ hex = hex << 4 | fromhex(c);
+ --nhexbytes;
+ if (nhexbytes != 0)
+ break;
+
+ switch (state) {
+ case read_length:
+ record = ccdbg_hex_alloc(hex);
+ if (!record) {
+ ccdbg_hex_error(input, "Out of memory");
+ goto bail;
+ }
+ state = read_address;
+ nhexbytes = 4;
+ break;
+ case read_address:
+ record->address = hex;
+ state = read_type;
+ nhexbytes = 2;
+ break;
+ case read_type:
+ record->type = hex;
+ state = read_data;
+ nhexbytes = 2;
+ ndata = 0;
+ break;
+ case read_data:
+ record->data[ndata] = hex;
+ ndata++;
+ nhexbytes = 2;
+ break;
+ case read_checksum:
+ record->checksum = hex;
+ state = read_newline;
+ break;
+ default:
+ break;
+ }
+ if (state == read_data)
+ if (ndata == record->length) {
+ nhexbytes = 2;
+ state = read_checksum;
+ }
+ hex = 0;
+ break;
+ case read_newline:
+ if (c != '\n' && c != '\r') {
+ ccdbg_hex_error(input, "Missing newline");
+ goto bail;
+ }
+ state = read_white;
+ break;
+ case read_white:
+ if (!isspace(c)) {
+ if (c == '\n')
+ input->line--;
+ if (c != EOF)
+ ungetc(c, input->file);
+ state = read_done;
+ }
+ break;
+ case read_done:
+ break;
+ }
+ }
+ checksum = ccdbg_hex_checksum(record);
+ if (checksum != record->checksum) {
+ ccdbg_hex_error(input, "Invalid checksum (read 0x%02x computed 0x%02x)\n",
+ record->checksum, checksum);
+ goto bail;
+ }
+ return record;
+
+bail:
+ ccdbg_hex_free(record);
+ return NULL;
+}
+
+void
+ccdbg_hex_file_free(struct hex_file *hex)
+{
+ int i;
+
+ if (!hex)
+ return;
+ for (i = 0; i < hex->nrecord; i++)
+ ccdbg_hex_free(hex->records[i]);
+ free(hex);
+}
+
+static int
+ccdbg_hex_record_compar(const void *av, const void *bv)
+{
+ const struct hex_record *a = *(struct hex_record **) av;
+ const struct hex_record *b = *(struct hex_record **) bv;
+
+ return (int) a->address - (int) b->address;
+}
+
+struct hex_file *
+ccdbg_hex_file_read(FILE *file, char *name)
+{
+ struct hex_input input;
+ struct hex_file *hex = NULL, *newhex;
+ struct hex_record *record;
+ int srecord = 1;
+ int done = 0;
+
+ hex = calloc(sizeof (struct hex_file) + sizeof (struct hex_record *), 1);
+ input.name = name;
+ input.line = 1;
+ input.file = file;
+ while (!done) {
+ record = ccdbg_hex_read_record(&input);
+ if (!record)
+ goto bail;
+ if (hex->nrecord == srecord) {
+ srecord *= 2;
+ newhex = realloc(hex,
+ sizeof (struct hex_file) +
+ srecord * sizeof (struct hex_record *));
+ if (!newhex)
+ goto bail;
+ hex = newhex;
+ }
+ hex->records[hex->nrecord++] = record;
+ if (record->type == HEX_RECORD_EOF)
+ done = 1;
+ }
+ /*
+ * Sort them into increasing addresses, except for EOF
+ */
+ qsort(hex->records, hex->nrecord - 1, sizeof (struct hex_record *),
+ ccdbg_hex_record_compar);
+ return hex;
+
+bail:
+ ccdbg_hex_file_free(hex);
+ return NULL;
+}
+
+struct hex_image *
+ccdbg_hex_image_create(struct hex_file *hex)
+{
+ struct hex_image *image;
+ struct hex_record *first, *last, *record;
+ int i;
+ uint32_t base, bound;
+ uint32_t offset;
+ int length;
+
+ first = hex->records[0];
+ last = hex->records[hex->nrecord - 2]; /* skip EOF */
+ base = (uint32_t) first->address;
+ bound = (uint32_t) last->address + (uint32_t) last->length;
+ length = bound - base;
+ image = calloc(sizeof(struct hex_image) + length, 1);
+ if (!image)
+ return NULL;
+ image->address = base;
+ image->length = length;
+ memset(image->data, 0xff, length);
+ for (i = 0; i < hex->nrecord - 1; i++) {
+ record = hex->records[i];
+ offset = record->address - base;
+ memcpy(image->data + offset, record->data, record->length);
+ }
+ return image;
+}
+
+void
+ccdbg_hex_image_free(struct hex_image *image)
+{
+ free(image);
+}
+
+int
+ccdbg_hex_image_equal(struct hex_image *a, struct hex_image *b)
+{
+ if (a->length != b->length)
+ return 0;
+ if (memcmp(a->data, b->data, a->length) != 0)
+ return 0;
+ return 1;
+}
diff --git a/lib/ccdbg-io.c b/lib/ccdbg-io.c
new file mode 100644
index 00000000..9c6693cd
--- /dev/null
+++ b/lib/ccdbg-io.c
@@ -0,0 +1,137 @@
+/*
+ * 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 "ccdbg.h"
+#include <time.h>
+#include "cc-usb.h"
+#include "cc-bitbang.h"
+
+struct ccdbg *
+ccdbg_open(void)
+{
+ struct ccdbg *dbg;
+ char *tty;
+
+ dbg = calloc(sizeof (struct ccdbg), 1);
+ if (!dbg) {
+ perror("calloc");
+ return NULL;
+ }
+ tty = getenv("CCDBG_TTY");
+ if (!tty || tty[0] == '/')
+ dbg->usb = cc_usb_open(tty);
+ if (!dbg->usb) {
+ dbg->bb = cc_bitbang_open();
+ if (!dbg->bb) {
+ free(dbg);
+ return NULL;
+ }
+ }
+ return dbg;
+}
+
+void
+ccdbg_close(struct ccdbg *dbg)
+{
+ if (dbg->usb)
+ cc_usb_close(dbg->usb);
+ if (dbg->bb)
+ cc_bitbang_close(dbg->bb);
+ free (dbg);
+}
+
+void
+ccdbg_debug_mode(struct ccdbg *dbg)
+{
+ if (dbg->usb)
+ cc_usb_debug_mode(dbg->usb);
+ else if (dbg->bb)
+ cc_bitbang_debug_mode(dbg->bb);
+}
+
+void
+ccdbg_reset(struct ccdbg *dbg)
+{
+ if (dbg->usb)
+ cc_usb_reset(dbg->usb);
+ else if (dbg->bb)
+ cc_bitbang_reset(dbg->bb);
+}
+
+void
+ccdbg_send_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes)
+{
+ if (dbg->usb)
+ cc_usb_send_bytes(dbg->usb, bytes, nbytes);
+ else if (dbg->bb)
+ cc_bitbang_send_bytes(dbg->bb, bytes, nbytes);
+}
+
+void
+ccdbg_recv_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes)
+{
+ if (dbg->usb)
+ cc_usb_recv_bytes(dbg->usb, bytes, nbytes);
+ else if (dbg->bb)
+ cc_bitbang_recv_bytes(dbg->bb, bytes, nbytes);
+}
+
+void
+ccdbg_sync(struct ccdbg *dbg)
+{
+ if (dbg->usb)
+ cc_usb_sync(dbg->usb);
+ else if (dbg->bb)
+ cc_bitbang_sync(dbg->bb);
+}
+
+void
+ccdbg_cmd_write(struct ccdbg *dbg, uint8_t cmd, uint8_t *data, int len)
+{
+ ccdbg_send_bytes(dbg, &cmd, 1);
+ ccdbg_send_bytes(dbg, data, len);
+}
+
+uint8_t
+ccdbg_cmd_write_read8(struct ccdbg *dbg, uint8_t cmd, uint8_t *data, int len)
+{
+ uint8_t byte[1];
+ ccdbg_cmd_write(dbg, cmd, data, len);
+ ccdbg_recv_bytes(dbg, byte, 1);
+ ccdbg_sync(dbg);
+ return byte[0];
+}
+
+uint16_t
+ccdbg_cmd_write_read16(struct ccdbg *dbg, uint8_t cmd, uint8_t *data, int len)
+{
+ uint8_t byte[2];
+ ccdbg_cmd_write(dbg, cmd, data, len);
+ ccdbg_recv_bytes(dbg, byte, 2);
+ ccdbg_sync(dbg);
+ return (byte[0] << 8) | byte[1];
+}
+
+void
+ccdbg_cmd_write_queue8(struct ccdbg *dbg, uint8_t cmd,
+ uint8_t *data, int len,
+ uint8_t *reply)
+{
+ ccdbg_cmd_write(dbg, cmd, data, len);
+ ccdbg_recv_bytes(dbg, reply, 1);
+}
diff --git a/lib/ccdbg-manual.c b/lib/ccdbg-manual.c
new file mode 100644
index 00000000..0e811b76
--- /dev/null
+++ b/lib/ccdbg-manual.c
@@ -0,0 +1,77 @@
+/*
+ * 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 "ccdbg.h"
+#include "cc-bitbang.h"
+
+/*
+ * Manual bit-banging to debug the low level protocol
+ */
+
+static void
+get_bit(char *line, int i, char on, uint8_t bit, uint8_t *bits, uint8_t *masks)
+{
+ if (line[i] == on) {
+ *bits |= bit;
+ *masks |= bit;
+ return;
+ }
+ if (line[i] == '.') {
+ *masks |= bit;
+ return;
+ }
+ if (line[i] == '-') {
+ return;
+ }
+ fprintf(stderr, "bad line %s\n", line);
+ exit (1);
+}
+
+void
+ccdbg_manual(struct ccdbg *dbg, FILE *input)
+{
+ char line[80];
+ uint8_t set, mask;
+
+ if (dbg->bb == NULL) {
+ fprintf(stderr, "Must use bitbang API for manual mode\n");
+ return;
+ }
+ while (fgets(line, sizeof line, input)) {
+ if (line[0] == '#' || line[0] == '\n') {
+ printf ("%s", line);
+ continue;
+ }
+ set = 0;
+ mask = 0;
+ get_bit(line, 0, 'C', CC_CLOCK, &set, &mask);
+ get_bit(line, 2, 'D', CC_DATA, &set, &mask);
+ get_bit(line, 4, 'R', CC_RESET_N, &set, &mask);
+ if (mask != (CC_CLOCK|CC_DATA|CC_RESET_N)) {
+ uint8_t read;
+ cc_bitbang_read(dbg->bb, &read);
+ cc_bitbang_sync(dbg->bb);
+ cc_bitbang_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);
+ printf ("\n");
+ }
+ cc_bitbang_send(dbg->bb, mask, set);
+ cc_bitbang_sync(dbg->bb);
+ }
+}
diff --git a/lib/ccdbg-memory.c b/lib/ccdbg-memory.c
new file mode 100644
index 00000000..554ac637
--- /dev/null
+++ b/lib/ccdbg-memory.c
@@ -0,0 +1,179 @@
+/*
+ * 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 "ccdbg.h"
+
+/*
+ * Read and write arbitrary memory through the debug port
+ */
+
+static uint8_t memory_init[] = {
+ 3, MOV_DPTR_data16, 0, 0,
+#define HIGH_START 2
+#define LOW_START 3
+ 0,
+};
+
+
+static uint8_t write8[] = {
+ 2, MOV_A_data, 0,
+#define DATA_BYTE 2
+ 1, MOVX_atDPTR_A,
+ 1, INC_DPTR,
+ 0
+};
+
+static uint8_t read8[] = {
+ 1, MOVX_A_atDPTR,
+ 1, INC_DPTR,
+ 0,
+};
+
+uint8_t
+ccdbg_write_memory(struct ccdbg *dbg, uint16_t addr, uint8_t *bytes, int nbytes)
+{
+ int i, nl = 0;
+ struct ccstate state;
+
+ if (dbg->usb)
+ return cc_usb_write_memory(dbg->usb, addr, bytes, nbytes);
+ ccdbg_state_save(dbg, &state, CC_STATE_ACC | CC_STATE_PSW | CC_STATE_DP);
+ memory_init[HIGH_START] = addr >> 8;
+ memory_init[LOW_START] = addr;
+ (void) ccdbg_execute(dbg, memory_init);
+ for (i = 0; i < nbytes; i++) {
+ write8[DATA_BYTE] = *bytes++;
+ ccdbg_execute(dbg, write8);
+ if ((i & 0xf) == 0xf) {
+ ccdbg_debug(CC_DEBUG_MEMORY, ".");
+ ccdbg_flush(CC_DEBUG_MEMORY);
+ nl = 1;
+ }
+ if ((i & 0xff) == 0xff) {
+ ccdbg_debug(CC_DEBUG_MEMORY, "\n");
+ nl = 0;
+ }
+ }
+ ccdbg_state_restore(dbg, &state);
+ if (nl)
+ ccdbg_debug(CC_DEBUG_MEMORY, "\n");
+ return 0;
+}
+
+uint8_t
+ccdbg_read_memory(struct ccdbg *dbg, uint16_t addr, uint8_t *bytes, int nbytes)
+{
+ int i, nl = 0;
+ struct ccstate state;
+
+ if (ccdbg_rom_contains(dbg, addr, nbytes)) {
+ ccdbg_rom_replace_xmem(dbg, addr, bytes, nbytes);
+ return 0;
+ }
+ if (dbg->usb)
+ return cc_usb_read_memory(dbg->usb, addr, bytes, nbytes);
+ ccdbg_state_save(dbg, &state, CC_STATE_ACC | CC_STATE_PSW | CC_STATE_DP);
+ memory_init[HIGH_START] = addr >> 8;
+ memory_init[LOW_START] = addr;
+ (void) ccdbg_execute(dbg, memory_init);
+ for (i = 0; i < nbytes; i++) {
+ *bytes++ = ccdbg_execute(dbg, read8);
+ if ((i & 0xf) == 0xf) {
+ ccdbg_debug(CC_DEBUG_MEMORY, ".");
+ ccdbg_flush(CC_DEBUG_MEMORY);
+ nl = 1;
+ }
+ if ((i & 0xff) == 0xff) {
+ ccdbg_debug(CC_DEBUG_MEMORY, "\n");
+ nl = 0;
+ }
+ }
+ ccdbg_state_replace_xmem(dbg, &state, addr, bytes, nbytes);
+ ccdbg_state_restore(dbg, &state);
+ if (nl)
+ ccdbg_debug(CC_DEBUG_MEMORY, "\n");
+ return 0;
+}
+
+uint8_t
+ccdbg_write_uint8(struct ccdbg *dbg, uint16_t addr, uint8_t byte)
+{
+ return ccdbg_write_memory(dbg, addr, &byte, 1);
+}
+
+uint8_t
+ccdbg_write_hex_image(struct ccdbg *dbg, struct hex_image *image, uint16_t offset)
+{
+ ccdbg_write_memory(dbg, image->address + offset, image->data, image->length);
+ return 0;
+}
+
+struct hex_image *
+ccdbg_read_hex_image(struct ccdbg *dbg, uint16_t address, uint16_t length)
+{
+ struct hex_image *image;
+
+ image = calloc(sizeof(struct hex_image) + length, 1);
+ image->address = address;
+ image->length = length;
+ memset(image->data, 0xff, length);
+ ccdbg_read_memory(dbg, address, image->data, length);
+ return image;
+}
+
+static uint8_t sfr_read[] = {
+ 2, MOV_A_direct, 0,
+#define SFR_READ_ADDR 2
+ 0,
+};
+
+static uint8_t sfr_write[] = {
+ 3, MOV_direct_data, 0, 0,
+#define SFR_WRITE_ADDR 2
+#define SFR_WRITE_DATA 3
+ 0,
+};
+
+uint8_t
+ccdbg_read_sfr(struct ccdbg *dbg, uint8_t addr, uint8_t *bytes, int nbytes)
+{
+ int i;
+ struct ccstate state;
+
+ ccdbg_state_save(dbg, &state, CC_STATE_ACC);
+ for (i = 0; i < nbytes; i++) {
+ sfr_read[SFR_READ_ADDR] = addr + i;
+ *bytes++ = ccdbg_execute(dbg, sfr_read);
+ }
+ ccdbg_state_replace_sfr(dbg, &state, addr, bytes, nbytes);
+ ccdbg_state_restore(dbg, &state);
+ return 0;
+}
+
+uint8_t
+ccdbg_write_sfr(struct ccdbg *dbg, uint8_t addr, uint8_t *bytes, int nbytes)
+{
+ int i;
+
+ for (i = 0; i < nbytes; i++) {
+ sfr_write[SFR_WRITE_ADDR] = addr + i;
+ sfr_write[SFR_WRITE_DATA] = *bytes++;
+ ccdbg_execute(dbg, sfr_write);
+ }
+ return 0;
+}
diff --git a/lib/ccdbg-rom.c b/lib/ccdbg-rom.c
new file mode 100644
index 00000000..71bed220
--- /dev/null
+++ b/lib/ccdbg-rom.c
@@ -0,0 +1,63 @@
+/*
+ * 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 "ccdbg.h"
+
+uint8_t
+ccdbg_set_rom(struct ccdbg *dbg, struct hex_image *rom)
+{
+ if (dbg->rom)
+ ccdbg_hex_image_free(dbg->rom);
+ dbg->rom = rom;
+ return 0;
+}
+
+uint8_t
+ccdbg_rom_contains(struct ccdbg *dbg, uint16_t addr, int nbytes)
+{
+ struct hex_image *rom = dbg->rom;
+ if (!rom)
+ return 0;
+ if (addr < rom->address || rom->address + rom->length < addr + nbytes)
+ return 0;
+ return 1;
+}
+
+uint8_t
+ccdbg_rom_replace_xmem(struct ccdbg *dbg,
+ uint16_t addr, uint8_t *bytes, int nbytes)
+{
+ struct hex_image *rom = dbg->rom;
+ if (!rom)
+ return 0;
+
+ if (rom->address < addr + nbytes && addr < rom->address + rom->length) {
+ int start, stop;
+
+ start = addr;
+ if (addr < rom->address)
+ start = rom->address;
+ stop = addr + nbytes;
+ if (rom->address + rom->length < stop)
+ stop = rom->address + rom->length;
+ memcpy(bytes + start - addr, rom->data + start - rom->address,
+ stop - start);
+ return 1;
+ }
+ return 0;
+}
diff --git a/lib/ccdbg-state.c b/lib/ccdbg-state.c
new file mode 100644
index 00000000..9aca8d2e
--- /dev/null
+++ b/lib/ccdbg-state.c
@@ -0,0 +1,128 @@
+/*
+ * 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 "ccdbg.h"
+
+static uint8_t save_acc[] = {
+ 1, NOP,
+ 0
+};
+
+static uint8_t save_sfr[] = {
+ 2, MOV_A_direct, 0,
+#define SAVE_SFR_ADDR 2
+ 0,
+};
+
+struct sfr_state {
+ uint8_t address;
+ uint16_t mask;
+ char *name;
+};
+
+static struct sfr_state sfrs[CC_STATE_NSFR] = {
+ { SFR_DPL0, CC_STATE_DP, "dpl0" },
+ { SFR_DPH0, CC_STATE_DP, "dph0" },
+ { SFR_DPL1, CC_STATE_DP, "dpl1" },
+ { SFR_DPH1, CC_STATE_DP, "dph1" },
+ { PSW(0), CC_STATE_PSW, "psw" },
+};
+
+uint8_t
+ccdbg_state_save(struct ccdbg *dbg, struct ccstate *state, unsigned int mask)
+{
+ int i;
+
+ mask |= CC_STATE_ACC;
+ if (mask & CC_STATE_ACC)
+ state->acc = ccdbg_execute(dbg, save_acc);
+ for (i = 0; i < CC_STATE_NSFR; i++) {
+ if (sfrs[i].mask & mask) {
+ save_sfr[SAVE_SFR_ADDR] = sfrs[i].address;
+ state->sfr[i] = ccdbg_execute(dbg, save_sfr);
+ }
+ }
+ state->mask = mask;
+ return 0;
+}
+
+static uint8_t restore_sfr[] = {
+ 3, MOV_direct_data, 0, 0,
+#define RESTORE_SFR_ADDR 2
+#define RESTORE_SFR_DATA 3
+ 0
+};
+
+static uint8_t restore_acc[] = {
+ 2, MOV_A_data, 0,
+#define RESTORE_ACC_DATA 2
+ 0
+};
+
+uint8_t
+ccdbg_state_restore(struct ccdbg *dbg, struct ccstate *state)
+{
+ int i;
+ for (i = CC_STATE_NSFR - 1; i >= 0; i--) {
+ if (sfrs[i].mask & state->mask) {
+ restore_sfr[RESTORE_SFR_ADDR] = sfrs[i].address;
+ restore_sfr[RESTORE_SFR_DATA] = state->sfr[i];
+ ccdbg_execute(dbg, restore_sfr);
+ }
+ }
+ if (state->mask & CC_STATE_ACC) {
+ restore_acc[RESTORE_ACC_DATA] = state->acc;
+ ccdbg_execute(dbg, restore_acc);
+ }
+ state->mask = 0;
+ return 0;
+}
+
+static void
+ccdbg_state_replace(uint16_t sfr_addr, uint8_t sfr, char *name,
+ uint16_t addr, uint8_t *bytes, int nbytes)
+{
+ sfr_addr += 0xdf00;
+
+ if (addr <= sfr_addr && sfr_addr < addr + nbytes) {
+ fprintf(stderr, "replacing %s at 0x%04x - read 0x%02x saved 0x%02x\n",
+ name, sfr_addr, bytes[sfr_addr - addr], sfr);
+ bytes[sfr_addr - addr] = sfr;
+ }
+}
+
+void
+ccdbg_state_replace_xmem(struct ccdbg *dbg, struct ccstate *state,
+ uint16_t addr, uint8_t *bytes, int nbytes)
+{
+ int i;
+ if (state->mask & CC_STATE_ACC)
+ ccdbg_state_replace(ACC(0), state->acc, "acc",
+ addr, bytes, nbytes);
+ for (i = 0; i < CC_STATE_NSFR; i++)
+ if (state->mask & sfrs[i].mask)
+ ccdbg_state_replace(sfrs[i].address, state->sfr[i],
+ sfrs[i].name, addr, bytes, nbytes);
+}
+
+void
+ccdbg_state_replace_sfr(struct ccdbg *dbg, struct ccstate *state,
+ uint8_t addr, uint8_t *bytes, int nbytes)
+{
+ ccdbg_state_replace_xmem(dbg, state, (uint16_t) addr + 0xdf00, bytes, nbytes);
+}
diff --git a/lib/ccdbg.h b/lib/ccdbg.h
new file mode 100644
index 00000000..4a2e3b9f
--- /dev/null
+++ b/lib/ccdbg.h
@@ -0,0 +1,341 @@
+/*
+ * 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_H_
+#define _CCDBG_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include "ccdbg-debug.h"
+#include "cc-bitbang.h"
+#include "cc-usb.h"
+
+/* 8051 instructions
+ */
+#define NOP 0x00
+#define MOV_direct_data 0x75
+#define LJMP 0x02
+#define MOV_Rn_data(n) (0x78 | (n))
+#define DJNZ_Rn_rel(n) (0xd8 | (n))
+#define MOV_A_direct 0xe5
+#define MOV_direct1_direct2 0x85
+#define MOV_direct_A 0xf5
+#define MOV_DPTR_data16 0x90
+#define MOV_A_data 0x74
+#define MOVX_atDPTR_A 0xf0
+#define MOVX_A_atDPTR 0xe0
+#define INC_DPTR 0xa3
+#define TRAP 0xa5
+#define SJMP 0x80
+#define JB 0x20
+
+/* 8051 special function registers
+ */
+
+#define SFR_P0 0x80
+#define SFR_SP 0x81
+#define SFR_DPL0 0x82
+#define SFR_DPH0 0x83
+#define SFR_DPL1 0x84
+#define SFR_DPH1 0x85
+
+/* flash controller */
+#define FWT 0xAB
+#define FADDRL 0xAC
+#define FADDRH 0xAD
+#define FCTL 0xAE
+# define FCTL_BUSY 0x80
+# define FCTL_BUSY_BIT 7
+# define FCTL_SWBSY 0x40
+# define FCTL_SWBSY_BIT 6
+# define FCTL_CONTRD 0x10
+# define FCTL_WRITE 0x02
+# define FCTL_ERASE 0x01
+#define FWDATA 0xAF
+
+#define SLEEP 0xBE
+
+/* clock controller */
+#define CLKCON 0xC6
+#define CLKCON_OSC32K 0x80
+#define CLKCON_OSC 0x40
+#define CLKCON_TICKSPD 0x38
+#define CLKCON_CLKSPD 0x07
+
+/* I/O pins */
+#define P0 0x80
+#define P1 0x90
+#define P2 0xA0
+#define P0DIR 0xFD
+#define P1DIR 0xFE
+#define P2DIR 0xFF
+
+/* Bit-addressable accumulator */
+#define ACC(bit) (0xE0 | (bit))
+
+/* Bit-addressable status word */
+#define PSW(bit) (0xD0 | (bit))
+
+struct ccdbg {
+ struct cc_bitbang *bb;
+ struct cc_usb *usb;
+ struct hex_image *rom;
+};
+
+/* Intel hex file format data
+ */
+struct hex_record {
+ uint8_t length;
+ uint16_t address;
+ uint8_t type;
+ uint8_t checksum;
+ uint8_t data[0];
+};
+
+struct hex_file {
+ int nrecord;
+ struct hex_record *records[0];
+};
+
+struct hex_image {
+ uint16_t address;
+ uint16_t length;
+ uint8_t data[0];
+};
+
+#define CC_STATE_ACC 0x1
+#define CC_STATE_PSW 0x2
+#define CC_STATE_DP 0x4
+
+#define CC_STATE_NSFR 5
+
+struct ccstate {
+ uint16_t mask;
+ uint8_t acc;
+ uint8_t sfr[CC_STATE_NSFR];
+};
+
+#define HEX_RECORD_NORMAL 0x00
+#define HEX_RECORD_EOF 0x01
+#define HEX_RECORD_EXTENDED_ADDRESS 0x02
+
+/* CC1111 debug port commands
+ */
+#define CC_CHIP_ERASE 0x14
+
+#define CC_WR_CONFIG 0x1d
+#define CC_RD_CONFIG 0x24
+# define CC_CONFIG_TIMERS_OFF (1 << 3)
+# define CC_CONFIG_DMA_PAUSE (1 << 2)
+# define CC_CONFIG_TIMER_SUSPEND (1 << 1)
+# define CC_SET_FLASH_INFO_PAGE (1 << 0)
+
+#define CC_GET_PC 0x28
+#define CC_READ_STATUS 0x34
+# define CC_STATUS_CHIP_ERASE_DONE (1 << 7)
+# define CC_STATUS_PCON_IDLE (1 << 6)
+# define CC_STATUS_CPU_HALTED (1 << 5)
+# define CC_STATUS_POWER_MODE_0 (1 << 4)
+# define CC_STATUS_HALT_STATUS (1 << 3)
+# define CC_STATUS_DEBUG_LOCKED (1 << 2)
+# define CC_STATUS_OSCILLATOR_STABLE (1 << 1)
+# define CC_STATUS_STACK_OVERFLOW (1 << 0)
+
+#define CC_SET_HW_BRKPNT 0x3b
+# define CC_HW_BRKPNT_N(n) ((n) << 3)
+# define CC_HW_BRKPNT_N_MASK (0x3 << 3)
+# define CC_HW_BRKPNT_ENABLE (1 << 2)
+
+#define CC_HALT 0x44
+#define CC_RESUME 0x4c
+#define CC_DEBUG_INSTR(n) (0x54|(n))
+#define CC_STEP_INSTR 0x5c
+#define CC_STEP_REPLACE(n) (0x64|(n))
+#define CC_GET_CHIP_ID 0x68
+
+/* ccdbg-command.c */
+void
+ccdbg_debug_mode(struct ccdbg *dbg);
+
+void
+ccdbg_reset(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_chip_erase(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_wr_config(struct ccdbg *dbg, uint8_t config);
+
+uint8_t
+ccdbg_rd_config(struct ccdbg *dbg);
+
+uint16_t
+ccdbg_get_pc(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_read_status(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_set_hw_brkpnt(struct ccdbg *dbg, uint8_t number, uint8_t enable, uint16_t addr);
+
+uint8_t
+ccdbg_halt(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_resume(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_debug_instr(struct ccdbg *dbg, uint8_t *instr, int nbytes);
+
+void
+ccdbg_debug_instr_discard(struct ccdbg *dbg, uint8_t *instr, int nbytes);
+
+void
+ccdbg_debug_instr_queue(struct ccdbg *dbg, uint8_t *instr, int nbytes,
+ uint8_t *reply);
+
+uint8_t
+ccdbg_step_instr(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_step_replace(struct ccdbg *dbg, uint8_t *instr, int nbytes);
+
+uint16_t
+ccdbg_get_chip_id(struct ccdbg *dbg);
+
+uint8_t
+ccdbg_execute(struct ccdbg *dbg, uint8_t *inst);
+
+uint8_t
+ccdbg_set_pc(struct ccdbg *dbg, uint16_t pc);
+
+uint8_t
+ccdbg_execute_hex_image(struct ccdbg *dbg, struct hex_image *image);
+
+/* ccdbg-flash.c */
+uint8_t
+ccdbg_flash_hex_image(struct ccdbg *dbg, struct hex_image *image);
+
+/* ccdbg-hex.c */
+struct hex_file *
+ccdbg_hex_file_read(FILE *file, char *name);
+
+void
+ccdbg_hex_file_free(struct hex_file *hex);
+
+struct hex_image *
+ccdbg_hex_image_create(struct hex_file *hex);
+
+void
+ccdbg_hex_image_free(struct hex_image *image);
+
+int
+ccdbg_hex_image_equal(struct hex_image *a, struct hex_image *b);
+
+/* ccdbg-io.c */
+struct ccdbg *
+ccdbg_open(void);
+
+void
+ccdbg_close(struct ccdbg *dbg);
+
+void
+ccdbg_cmd_write(struct ccdbg *dbg, uint8_t cmd, uint8_t *data, int len);
+
+uint8_t
+ccdbg_cmd_write_read8(struct ccdbg *dbg, uint8_t cmd, uint8_t *data, int len);
+
+void
+ccdbg_cmd_write_queue8(struct ccdbg *dbg, uint8_t cmd,
+ uint8_t *data, int len, uint8_t *reply);
+
+uint16_t
+ccdbg_cmd_write_read16(struct ccdbg *dbg, uint8_t cmd, uint8_t *data, int len);
+
+void
+ccdbg_send_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes);
+
+void
+ccdbg_recv_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes);
+
+void
+ccdbg_sync(struct ccdbg *dbg);
+
+/* ccdbg-manual.c */
+
+void
+ccdbg_manual(struct ccdbg *dbg, FILE *input);
+
+/* ccdbg-memory.c */
+uint8_t
+ccdbg_write_memory(struct ccdbg *dbg, uint16_t addr, uint8_t *bytes, int nbytes);
+
+uint8_t
+ccdbg_read_memory(struct ccdbg *dbg, uint16_t addr, uint8_t *bytes, int nbytes);
+
+uint8_t
+ccdbg_write_uint8(struct ccdbg *dbg, uint16_t addr, uint8_t byte);
+
+uint8_t
+ccdbg_write_hex_image(struct ccdbg *dbg, struct hex_image *image, uint16_t offset);
+
+struct hex_image *
+ccdbg_read_hex_image(struct ccdbg *dbg, uint16_t address, uint16_t length);
+
+uint8_t
+ccdbg_read_sfr(struct ccdbg *dbg, uint8_t addr, uint8_t *bytes, int nbytes);
+
+uint8_t
+ccdbg_write_sfr(struct ccdbg *dbg, uint8_t addr, uint8_t *bytes, int nbytes);
+
+/* ccdbg-rom.c */
+uint8_t
+ccdbg_set_rom(struct ccdbg *dbg, struct hex_image *rom);
+
+uint8_t
+ccdbg_rom_contains(struct ccdbg *dbg, uint16_t addr, int nbytes);
+
+uint8_t
+ccdbg_rom_replace_xmem(struct ccdbg *dbg,
+ uint16_t addrp, uint8_t *bytesp, int nbytes);
+
+/* ccdbg-state.c */
+uint8_t
+ccdbg_state_save(struct ccdbg *dbg, struct ccstate *state, unsigned int mask);
+
+uint8_t
+ccdbg_state_restore(struct ccdbg *dbg, struct ccstate *state);
+
+void
+ccdbg_state_replace_xmem(struct ccdbg *dbg, struct ccstate *state,
+ uint16_t addr, uint8_t *bytes, int nbytes);
+
+void
+ccdbg_state_replace_sfr(struct ccdbg *dbg, struct ccstate *state,
+ uint8_t addr, uint8_t *bytes, int nbytes);
+
+#endif /* _CCDBG_H_ */
diff --git a/lib/cp-usb-async.c b/lib/cp-usb-async.c
new file mode 100644
index 00000000..6539394b
--- /dev/null
+++ b/lib/cp-usb-async.c
@@ -0,0 +1,188 @@
+/*
+ * 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;
+ uint8_t value;
+ uint8_t set;
+};
+
+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);
+ cp->ack = -1;
+ if (!cp->handle) {
+ libusb_exit(cp->ctx);
+ free(cp);
+ return NULL;
+ }
+ cp->value = 0;
+ cp->set = 0;
+ 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;
+ int ret;
+
+ if (cp->set) {
+ value = (cp->value & ~mask) | (value & mask);
+ mask = value ^ cp->value;
+ }
+ cp->set = 1;
+ cp->value = value;
+ gpio_set = ((uint16_t) value << 8) | mask;
+ 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 = -1;
+}
diff --git a/lib/cp-usb-async.h b/lib/cp-usb-async.h
new file mode 100644
index 00000000..976a320e
--- /dev/null
+++ b/lib/cp-usb-async.h
@@ -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
diff --git a/lib/cp-usb.c b/lib/cp-usb.c
new file mode 100644
index 00000000..530848db
--- /dev/null
+++ b/lib/cp-usb.c
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+/*
+ * libusb interface to the GPIO pins on a CP2103.
+ *
+ * Various magic constants came from the cp210x driver published by silabs.
+ */
+
+#include "cp-usb.h"
+#include <stdio.h>
+#include <errno.h>
+#include <libusb.h>
+
+struct cp_usb {
+ usb_dev_handle *usb_dev;
+ uint8_t gpio;
+};
+
+#define CP2101_UART 0x00
+#define UART_ENABLE 0x0001
+#define UART_DISABLE 0x0000
+#define REQTYPE_HOST_TO_DEVICE 0x41
+#define REQTYPE_DEVICE_TO_HOST 0xc1
+
+static int
+cp_usb_gpio_get(struct cp_usb *cp, uint8_t *gpio_get)
+{
+ return usb_control_msg(cp->usb_dev, /* dev */
+ 0xc0, /* request */
+ 0xff, /* requesttype */
+ 0x00c2, /* value */
+ 0, /* index */
+ (char *) gpio_get, /* bytes */
+ 1, /* size */
+ 300); /* timeout */
+}
+
+static int
+cp_usb_gpio_set(struct cp_usb *cp, uint8_t mask, uint8_t value)
+{
+ uint16_t gpio_set = ((uint16_t) value << 8) | mask;
+
+ return usb_control_msg(cp->usb_dev, /* dev */
+ 0x40, /* request */
+ 0xff, /* requesttype */
+ 0x37e1, /* value */
+ gpio_set, /* index */
+ NULL, /* bytes */
+ 0, /* size */
+ 300); /* timeout */
+}
+
+static int
+cp_usb_uart_enable_disable(struct cp_usb *cp, uint16_t enable)
+{
+ return usb_control_msg(cp->usb_dev,
+ CP2101_UART,
+ REQTYPE_HOST_TO_DEVICE,
+ enable,
+ 0,
+ NULL,
+ 0,
+ 300);
+}
+
+struct cp_usb *
+cp_usb_open(void)
+{
+ struct cp_usb *cp;
+ usb_dev_handle *dev_handle;
+ struct usb_device *dev = NULL;
+ struct usb_bus *bus, *busses;
+ int interface;
+ int ret;
+ uint8_t gpio;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ busses = usb_get_busses();
+ for (bus = busses; bus; bus = bus->next) {
+ for (dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.idVendor == 0x10c4 &&
+ dev->descriptor.idProduct == 0xea60)
+ break;
+ }
+ if (dev)
+ break;
+ }
+ if (!dev){
+ perror("No CP2103 found");
+ return NULL;
+ }
+ cp = calloc(sizeof(struct cp_usb), 1);
+ interface = 0;
+ dev_handle = usb_open(dev);
+ usb_detach_kernel_driver_np(dev_handle, interface);
+ usb_claim_interface(dev_handle, interface);
+ cp->usb_dev = dev_handle;
+ ret = cp_usb_uart_enable_disable(cp, UART_DISABLE);
+ cp->gpio = 0xf;
+ ret = cp_usb_gpio_set(cp, 0xf, cp->gpio);
+ ret = cp_usb_gpio_get(cp, &gpio);
+ return cp;
+}
+
+void
+cp_usb_close(struct cp_usb *cp)
+{
+ cp_usb_uart_enable_disable(cp, UART_DISABLE);
+ usb_close(cp->usb_dev);
+ free(cp);
+}
+
+void
+cp_usb_write(struct cp_usb *cp, uint8_t mask, uint8_t value)
+{
+ uint8_t new_gpio;
+ int ret;
+
+ new_gpio = (cp->gpio & ~mask) | (value & mask);
+ if (new_gpio != cp->gpio) {
+ ret = cp_usb_gpio_set(cp, new_gpio ^ cp->gpio, new_gpio);
+ if (ret < 0)
+ perror("gpio_set");
+ cp->gpio = new_gpio;
+ }
+}
+
+uint8_t
+cp_usb_read(struct cp_usb *cp)
+{
+ int ret;
+ uint8_t gpio;
+
+ ret = cp_usb_gpio_get(cp, &gpio);
+ if (ret < 0)
+ perror("gpio_get");
+ return gpio;
+}
diff --git a/lib/cp-usb.h b/lib/cp-usb.h
new file mode 100644
index 00000000..3e5f25ff
--- /dev/null
+++ b/lib/cp-usb.h
@@ -0,0 +1,36 @@
+/*
+ * 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_H_
+#define _CP_USB_H_
+#include <usb.h>
+
+struct cp_usb *
+cp_usb_open(void);
+
+void
+cp_usb_close(struct cp_usb *cp);
+
+void
+cp_usb_write(struct cp_usb *cp, uint8_t mask, uint8_t value);
+
+uint8_t
+cp_usb_read(struct cp_usb *cp);
+
+
+#endif