diff options
| author | Keith Packard <keithp@keithp.com> | 2009-04-06 11:31:49 -0700 | 
|---|---|---|
| committer | Keith Packard <keithp@keithp.com> | 2009-04-06 11:32:21 -0700 | 
| commit | 2d9b8a83a2d9f495199033e43f519d26f27938fe (patch) | |
| tree | 495a2cbf1573ac0b42d6331e9627e28448c65ef7 /lib | |
| parent | 24edd56155ed0fa02fdd8f66fdc7aa5a1021bf7d (diff) | |
Add support for a serial-connected custom debug dongle
This uses the cc1111 board as a custom debug dongle with faster
methods for communicating with the debug target.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile.am | 4 | ||||
| -rw-r--r-- | lib/cc-bitbang.c | 270 | ||||
| -rw-r--r-- | lib/cc-bitbang.h | 94 | ||||
| -rw-r--r-- | lib/cc-usb.c | 355 | ||||
| -rw-r--r-- | lib/cc-usb.h | 53 | ||||
| -rw-r--r-- | lib/ccdbg-command.c | 52 | ||||
| -rw-r--r-- | lib/ccdbg-debug.c | 9 | ||||
| -rw-r--r-- | lib/ccdbg-io.c | 213 | ||||
| -rw-r--r-- | lib/ccdbg-manual.c | 15 | ||||
| -rw-r--r-- | lib/ccdbg-memory.c | 4 | ||||
| -rw-r--r-- | lib/ccdbg.h | 88 | ||||
| -rw-r--r-- | lib/cp-usb.c | 1 | 
12 files changed, 885 insertions, 273 deletions
| diff --git a/lib/Makefile.am b/lib/Makefile.am index ba6f9725..4d9ded3a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -13,5 +13,9 @@ libcc_a_SOURCES = \  	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..a5d2d369 --- /dev/null +++ b/lib/cc-bitbang.c @@ -0,0 +1,270 @@ +/* + * 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..a85765af --- /dev/null +++ b/lib/cc-usb.c @@ -0,0 +1,355 @@ +/* + * 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 +#define CC_BUF			1024 +#define DEFAULT_TTY		"/dev/ttyACM0" + +struct cc_read { +	uint8_t	*buf; +	int	len; +}; + +struct cc_usb { +	int		fd; +	uint8_t		in_buf[CC_BUF]; +	int		in_count; +	uint8_t		out_buf[CC_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_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_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); +			} +		} +		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; +			} +		} +	} +} + +static 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_BUF - cc->out_count) +			this_time = CC_BUF - cc->out_count; +		memcpy(cc->out_buf + cc->out_count, b, this_time); +		cc->out_count += this_time; +		ret -= this_time; +		while (cc->out_count >= CC_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; +} + +static 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(void) +{ +	struct cc_usb	*cc; +	char		*tty; +	struct termios	termios; +	 +	tty = getenv("CCDBG_TTY"); +	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..235e9918 --- /dev/null +++ b/lib/cc-usb.h @@ -0,0 +1,53 @@ +/* + * 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(void); + +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); + +#endif /* _CC_USB_H_ */ diff --git a/lib/ccdbg-command.c b/lib/ccdbg-command.c index 74313bdf..7d1ae067 100644 --- a/lib/ccdbg-command.c +++ b/lib/ccdbg-command.c @@ -18,39 +18,6 @@  #include "ccdbg.h" -void -ccdbg_debug_mode(struct ccdbg *dbg) -{ -	/* 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"); -	ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N); -	ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N,          CC_DATA           ); -	ccdbg_wait_reset(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_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_wait_reset(dbg); -} - -void -ccdbg_reset(struct ccdbg *dbg) -{ -	ccdbg_debug(CC_DEBUG_COMMAND, "#\n"); -	ccdbg_debug(CC_DEBUG_COMMAND, "# Reset\n"); -	ccdbg_debug(CC_DEBUG_COMMAND, "#\n"); -	ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N); -	ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA           ); -	ccdbg_wait_reset(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           ); -	ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N); -	ccdbg_wait_reset(dbg); -} -  uint8_t  ccdbg_chip_erase(struct ccdbg *dbg)  { @@ -117,6 +84,22 @@ 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)  { @@ -148,12 +131,13 @@ ccdbg_execute(struct ccdbg *dbg, uint8_t *inst)  		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]); -		status = ccdbg_debug_instr(dbg, inst+1, len); +		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;  } diff --git a/lib/ccdbg-debug.c b/lib/ccdbg-debug.c index 847361c7..6eb4e0c5 100644 --- a/lib/ccdbg-debug.c +++ b/lib/ccdbg-debug.c @@ -34,11 +34,20 @@ 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); diff --git a/lib/ccdbg-io.c b/lib/ccdbg-io.c index 3606c57c..acd44f10 100644 --- a/lib/ccdbg-io.c +++ b/lib/ccdbg-io.c @@ -18,41 +18,10 @@  #include "ccdbg.h"  #include <time.h> -#ifdef CP_USB_ASYNC -#include "cp-usb-async.h" -#else -#include "cp-usb.h" -#endif +#include "cc-usb.h" +#include "cc-bitbang.h" -static uint32_t	cc_clock_us = CC_CLOCK_US; -static uint32_t	cc_reset_us = CC_RESET_US; -void -ccdbg_set_clock(uint32_t us) -{ -	cc_clock_us = us; -} - -void -ccdbg_half_clock(struct ccdbg *dbg) -{ -	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 -ccdbg_wait_reset(struct ccdbg *dbg) -{ -	struct timespec	req, rem; -	 -	ccdbg_sync_io(dbg); -	req.tv_sec = (cc_reset_us) / 1000000; -	req.tv_nsec = ((cc_reset_us) % 1000000) * 1000; -	nanosleep(&req, &rem); -} -	  struct ccdbg *  ccdbg_open(void)  { @@ -63,170 +32,77 @@ 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; +	dbg->usb = cc_usb_open(); +	if (!dbg->usb) { +		dbg->bb = cc_bitbang_open(); +		if (!dbg->bb) { +			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 +	if (dbg->usb) +		cc_usb_close(dbg->usb); +	if (dbg->bb) +		cc_bitbang_close(dbg->bb);  	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; -} - -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) -{ -#ifdef CP_USB_ASYNC -	cp_usb_async_sync(dbg->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 -ccdbg_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 -ccdbg_send(struct ccdbg *dbg, uint8_t mask, uint8_t set) +ccdbg_debug_mode(struct ccdbg *dbg)  { -	ccdbg_write(dbg, mask, set); -	ccdbg_print("%c %c %c\n", mask, set); -	ccdbg_half_clock(dbg); +	if (dbg->usb) +		cc_usb_debug_mode(dbg->usb); +	else if (dbg->bb) +		cc_bitbang_debug_mode(dbg->bb);  }  void -ccdbg_send_bit(struct ccdbg *dbg, uint8_t bit) +ccdbg_reset(struct ccdbg *dbg)  { -	if (bit) bit = CC_DATA; -	ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N, CC_CLOCK|bit|CC_RESET_N); -	ccdbg_send(dbg, CC_CLOCK|CC_DATA|CC_RESET_N,          bit|CC_RESET_N); -} - -void -ccdbg_send_byte(struct ccdbg *dbg, uint8_t byte) -{ -	int bit; -	ccdbg_debug(CC_DEBUG_BITBANG, "#\n# Send Byte 0x%02x\n#\n", byte); -	for (bit = 7; bit >= 0; bit--) { -		ccdbg_send_bit(dbg, (byte >> bit) & 1); -		if (bit == 3) -			ccdbg_debug(CC_DEBUG_BITBANG, "\n"); -	} -	ccdbg_sync_io(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)  { -	while (nbytes--) -		ccdbg_send_byte(dbg, *bytes++); -} - -void -ccdbg_recv_bit(struct ccdbg *dbg, int first, uint8_t *bit) -{ -	uint8_t mask = first ? CC_DATA : 0; - -	ccdbg_send(dbg, CC_CLOCK|mask|CC_RESET_N, CC_CLOCK|CC_DATA|CC_RESET_N); -	ccdbg_read(dbg, bit); -	ccdbg_send(dbg, CC_CLOCK|     CC_RESET_N,                  CC_RESET_N); +	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_byte(struct ccdbg *dbg, int first, uint8_t *bytep) +ccdbg_recv_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes)  { -	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 |= (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"); -	} -	ccdbg_debug(CC_DEBUG_BITBANG, "#\n# Recv 0x%02x\n#\n", byte); -	*bytep = byte; +	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_recv_bytes(struct ccdbg *dbg, uint8_t *bytes, int nbytes) +ccdbg_sync(struct ccdbg *dbg)  { -	int i; -	int first = 1; -	for (i = 0; i < nbytes; i++) { -		ccdbg_recv_byte(dbg, first, &bytes[i]); -		first = 0; -	} +	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)  { -	int	i; -	ccdbg_send_byte(dbg, cmd); -	for (i = 0; i < len; i++) -		ccdbg_send_byte(dbg, data[i]); +	ccdbg_send_bytes(dbg, &cmd, 1); +	ccdbg_send_bytes(dbg, data, len);  }  uint8_t @@ -235,6 +111,7 @@ 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];  } @@ -244,5 +121,15 @@ 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 index df79d88d..0e811b76 100644 --- a/lib/ccdbg-manual.c +++ b/lib/ccdbg-manual.c @@ -17,6 +17,7 @@   */  #include "ccdbg.h" +#include "cc-bitbang.h"  /*   * Manual bit-banging to debug the low level protocol @@ -47,6 +48,10 @@ 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); @@ -59,14 +64,14 @@ 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; -			ccdbg_read(dbg, &read); -			ccdbg_sync_io(dbg); -			ccdbg_print("\t%c %c %c", CC_CLOCK|CC_DATA|CC_RESET_N, 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");  		} -		ccdbg_send(dbg, mask, set); -		ccdbg_sync_io(dbg); +		cc_bitbang_send(dbg->bb, mask, set); +		cc_bitbang_sync(dbg->bb);  	}  } diff --git a/lib/ccdbg-memory.c b/lib/ccdbg-memory.c index 20a24799..878c5f97 100644 --- a/lib/ccdbg-memory.c +++ b/lib/ccdbg-memory.c @@ -50,6 +50,8 @@ 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; @@ -83,6 +85,8 @@ ccdbg_read_memory(struct ccdbg *dbg, uint16_t addr, uint8_t *bytes, int 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; diff --git a/lib/ccdbg.h b/lib/ccdbg.h index e0e12c8b..fe0ea3a0 100644 --- a/lib/ccdbg.h +++ b/lib/ccdbg.h @@ -31,17 +31,8 @@  #include <sys/ioctl.h>  #include <sys/stat.h>  #include "ccdbg-debug.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) +#include "cc-bitbang.h" +#include "cc-usb.h"  /* 8051 instructions   */ @@ -109,15 +100,10 @@  /* Bit-addressable status word */  #define PSW(bit)		(0xD0 | (bit)) -#define CP_USB_ASYNC -  struct ccdbg { -#ifdef CP_USB_ASYNC -	struct cp_usb_async *cp_async; -#else -	struct cp_usb *cp; -#endif -	struct hex_image *rom; +	struct cc_bitbang	*bb; +	struct cc_usb		*usb; +	struct hex_image	*rom;  };  /* Intel hex file format data @@ -225,6 +211,13 @@ 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); @@ -264,21 +257,6 @@ int  ccdbg_hex_image_equal(struct hex_image *a, struct hex_image *b);  /* ccdbg-io.c */ -void -ccdbg_set_clock(uint32_t us); - -void -ccdbg_half_clock(struct ccdbg *dbg); - -void -ccdbg_wait_reset(struct ccdbg *dbg); - -int -ccdbg_write(struct ccdbg *dbg, uint8_t mask, uint8_t value); - -void -ccdbg_read(struct ccdbg *dbg, uint8_t *valuep); -  struct ccdbg *  ccdbg_open(void); @@ -286,58 +264,26 @@ void  ccdbg_close(struct ccdbg *dbg);  void -ccdbg_clock_1_0(struct ccdbg *dbg); - -void -ccdbg_clock_0_1(struct ccdbg *dbg); - -void -ccdbg_write_bit(struct ccdbg *dbg, uint8_t bit); - -void -ccdbg_write_byte(struct ccdbg *dbg, uint8_t byte); - -uint8_t -ccdbg_read_bit(struct ccdbg *dbg); - -uint8_t -ccdbg_read_byte(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); -uint16_t -ccdbg_cmd_write_read16(struct ccdbg *dbg, uint8_t cmd, uint8_t *data, int len); -  void -ccdbg_send(struct ccdbg *dbg, uint8_t mask, uint8_t set); +ccdbg_cmd_write_queue8(struct ccdbg *dbg, uint8_t cmd, +		       uint8_t *data, int len, uint8_t *reply); -void -ccdbg_send_bit(struct ccdbg *dbg, uint8_t bit); - -void -ccdbg_send_byte(struct ccdbg *dbg, uint8_t byte); +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_bit(struct ccdbg *dbg, int first, uint8_t *bit); - -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); +ccdbg_sync(struct ccdbg *dbg);  /* ccdbg-manual.c */ diff --git a/lib/cp-usb.c b/lib/cp-usb.c index 6ab9092c..d227b78c 100644 --- a/lib/cp-usb.c +++ b/lib/cp-usb.c @@ -25,6 +25,7 @@  #include "cp-usb.h"  #include <stdio.h>  #include <errno.h> +#include <libusb.h>  struct cp_usb {  	usb_dev_handle *usb_dev; | 
