diff options
Diffstat (limited to 'lib/cp-usb-async.c')
| -rw-r--r-- | lib/cp-usb-async.c | 176 | 
1 files changed, 176 insertions, 0 deletions
| diff --git a/lib/cp-usb-async.c b/lib/cp-usb-async.c new file mode 100644 index 00000000..3f5f76ab --- /dev/null +++ b/lib/cp-usb-async.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 <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include "cp-usb-async.h" +#include "ccdbg-debug.h" + +#define MAX_OUTSTANDING		256 +#define CP_TIMEOUT		1000	/* ms */ + +struct cp_usb_packet { +	struct libusb_transfer	*transfer; +	enum { packet_read, packet_write } direction; +	unsigned char		data[9]; +	uint8_t			*valuep; +}; + +struct cp_usb_async { +	libusb_context		*ctx; +	libusb_device_handle	*handle; +	struct cp_usb_packet	packet[MAX_OUTSTANDING]; +	int			p, ack; +}; + +struct cp_usb_async * +cp_usb_async_open(void) +{ +	struct cp_usb_async *cp; +	int ret; + +	cp = calloc(sizeof (struct cp_usb_async), 1); +	if (!cp) +		return NULL; +	ret = libusb_init(&cp->ctx); +	if (ret) { +		free(cp); +		return NULL; +	} +	cp->handle = libusb_open_device_with_vid_pid(cp->ctx, +						     0x10c4, 0xea60); +	if (!cp->handle) { +		libusb_exit(cp->ctx); +		free(cp); +		return NULL; +	} +	return cp; +} + +void +cp_usb_async_close(struct cp_usb_async *cp) +{ +	libusb_close(cp->handle); +	libusb_exit(cp->ctx); +	free(cp); +} + +static void +cp_usb_async_transfer_callback(struct libusb_transfer *transfer) +{ +	struct cp_usb_async *cp = transfer->user_data; +	int p; + +	for (p = 0; p < cp->p; p++) +		if (cp->packet[p].transfer == transfer) +			break; +	if (p == cp->p) { +		fprintf(stderr, "unknown transfer\n"); +		return; +	} +	switch (cp->packet[p].direction) { +	case packet_read: +		ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack read %d 0x%02x\n", +			    p, cp->packet[p].data[8]); +		*cp->packet[p].valuep = cp->packet[p].data[8]; +		break; +	case packet_write: +		ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack write %d\n", p); +		break; +	} +	if (p > cp->ack) +		cp->ack = p; +} + +void +cp_usb_async_write(struct cp_usb_async *cp, uint8_t mask, uint8_t value) +{ +	int	p; +	uint16_t gpio_set = ((uint16_t) value << 8) | mask; +	int	ret; + +	if (cp->p == MAX_OUTSTANDING) +		cp_usb_async_sync(cp); +	p = cp->p; +	if (!cp->packet[p].transfer) +		cp->packet[p].transfer = libusb_alloc_transfer(0); +	cp->packet[p].direction = packet_write; +	libusb_fill_control_setup(cp->packet[p].data, +				  0x40,			/* request */ +				  0xff,			/* request type */ +				  0x37e1,		/* value */ +				  gpio_set,		/* index */ +				  0);			/* length */ +	 +	libusb_fill_control_transfer(cp->packet[p].transfer, +				     cp->handle, +				     cp->packet[p].data, +				     cp_usb_async_transfer_callback, +				     cp, +				     CP_TIMEOUT); +	ccdbg_debug(CC_DEBUG_USB_ASYNC, "Write packet %d 0x%x 0x%x\n", p, mask, value); +	ret = libusb_submit_transfer(cp->packet[p].transfer); +	if (ret) +		fprintf(stderr, "libusb_submit_transfer failed %d\n", ret); +	cp->p++; +} + +void +cp_usb_async_read(struct cp_usb_async *cp, uint8_t *valuep) +{ +	int	p; +	int	ret; + +	if (cp->p == MAX_OUTSTANDING) +		cp_usb_async_sync(cp); +	p = cp->p; +	if (!cp->packet[p].transfer) +		cp->packet[p].transfer = libusb_alloc_transfer(0); +	cp->packet[p].valuep = valuep; +	cp->packet[p].direction = packet_read; +	libusb_fill_control_setup(cp->packet[p].data, +				  0xc0,			/* request */ +				  0xff,			/* request type */ +				  0x00c2,		/* value */ +				  0,			/* index */ +				  1);			/* length */ +	 +	libusb_fill_control_transfer(cp->packet[p].transfer, +				     cp->handle, +				     cp->packet[p].data, +				     cp_usb_async_transfer_callback, +				     cp, +				     CP_TIMEOUT); +	ccdbg_debug(CC_DEBUG_USB_ASYNC, "Read packet %d\n", p); +	ret = libusb_submit_transfer(cp->packet[p].transfer); +	if (ret) +		fprintf(stderr, "libusb_submit_transfer failed %d\n", ret); +	cp->p++; +} + +void +cp_usb_async_sync(struct cp_usb_async *cp) +{ +	while (cp->ack < cp->p - 1) { +		libusb_handle_events(cp->ctx); +	} +	cp->p = 0; +	cp->ack = 0; +} | 
