TeleStern/ao-tools/lib/cp-usb-async.c

190 lines
4.5 KiB
C

/*
* 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) {
fprintf(stderr, "Cannot find USB device 10c4:ea60\n");
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;
}