cake
/
libp7
Archived
1
0
Fork 1
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libp7/src/stream/libusb.c

296 lines
7.8 KiB
C

/* ************************************************************************** */
/* _____ _ */
/* handle/libusb.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libp7 | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2017/01/04 15:01:48 |___/ */
/* */
/* ************************************************************************** */
#include <libp7/internals.h>
#ifndef P7_DISABLED_LIBUSB
# include <libusb.h>
# include <stdlib.h>
# include <string.h>
/* ************************************************************************** */
/* Cookie structure */
/* ************************************************************************** */
/* libusb */
# define BUFSIZE 2048
typedef struct {
libusb_context *_context;
libusb_device_handle *_handle;
unsigned char _buffer[BUFSIZE];
ssize_t _start, _end;
} libusb_cookie_t;
/* ************************************************************************** */
/* Callbacks */
/* ************************************************************************** */
/**
* p7_libusb_read:
* Read using libusb cookie.
*
* @arg vcookie the cookie (voided)
* @arg data the data pointer.
* @arg size the data size.
* @arg timeout the timeout in milliseconds.
* @return the error code (0 if ok).
*/
# define ENDPOINT_IN (LIBUSB_ENDPOINT_IN | LIBUSB_TRANSFER_TYPE_BULK)
static int p7_libusb_read(void *vcookie,
unsigned char *dest, size_t size, unsigned int timeout)
{
libusb_cookie_t *cookie = (libusb_cookie_t*)vcookie;
/* transmit what's already in the buffer */
if (cookie->_start <= cookie->_end) {
size_t tocopy = cookie->_end - cookie->_start + 1;
if (tocopy > size) tocopy = size;
memcpy(dest, &cookie->_buffer[cookie->_start], tocopy);
cookie->_start += tocopy;
dest += tocopy;
size -= tocopy;
}
/* main receiving loop */
while (size) {
/* receive */
int recv;
int err = libusb_bulk_transfer(cookie->_handle, ENDPOINT_IN,
cookie->_buffer, BUFSIZE, &recv, timeout);
/* check error */
if (err) switch (err) {
case LIBUSB_ERROR_PIPE:
case LIBUSB_ERROR_NO_DEVICE:
case LIBUSB_ERROR_IO:
return (p7_error_nocalc);
default:
logr_fatal("libusb error was %d: %s", err,
libusb_strerror(err));
return (p7_error_unknown);
}
/* get the current size to copy */
size_t tocopy = (size_t)recv;
if (tocopy > size) tocopy = size;
/* copy to destination */
memcpy(dest, cookie->_buffer, tocopy);
dest += tocopy;
size -= tocopy;
/* correct start and end points */
cookie->_start = tocopy;
cookie->_end = (size_t)recv - 1;
}
/* no error */
return (0);
}
/**
* p7_libusb_write:
* Write using libusb cookie.
*
* @arg vcookie the cookie (uncasted)
* @arg data the source.
* @arg size the source size.
* @return the libp7 error (0 if ok).
*/
# define ENDPOINT_OUT (LIBUSB_ENDPOINT_OUT | LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
static int p7_libusb_write(void *vcookie,
const unsigned char *data, size_t size)
{
libusb_cookie_t *cookie = (libusb_cookie_t*)vcookie;
/* send */
int sent;
int err = libusb_bulk_transfer(cookie->_handle, ENDPOINT_OUT,
(unsigned char*)data, size, &sent, 0);
/* check the error */
if (err) switch (err) {
case LIBUSB_ERROR_PIPE:
case LIBUSB_ERROR_NO_DEVICE:
return (p7_error_nocalc);
default:
logr_fatal("libusb error was %d: %s", err,
libusb_strerror(err));
return (p7_error_unknown);
}
/* no error! */
return (0);
}
/**
* p7_libusb_close:
* Close libusb cookie.
*
* @arg vcookie the cookie (uncasted)
*/
static int p7_libusb_close(void *vcookie)
{
libusb_cookie_t *cookie = (libusb_cookie_t*)vcookie;
libusb_close(cookie->_handle);
libusb_exit(cookie->_context);
free(vcookie);
return (0);
}
/* ************************************************************************** */
/* With libusb */
/* ************************************************************************** */
# include <stdlib.h>
# include <string.h>
/**
* p7_libusbinit:
* Initialize libp7 with USB device using libusb.
*
* @arg handle the handle to create.
* @arg flags the flags.
* @return the error code (0 if you're a knoop).
*/
int p7_libusbinit(p7_handle_t **handle, unsigned int flags)
{
int err = 0;
libusb_context *context = NULL;
libusb_device_handle *dhandle = NULL;
/* open up context */
if (libusb_init(&context)) {
logr_fatal("Couldn't create libusb context.");
return (p7_error_nocalc);
}
/* get device list */
libusb_device **device_list = NULL;
int device_count = libusb_get_device_list(context, &device_list);
if (device_count < 0) {
logr_fatal("couldn't get device list.");
err = p7_error_nocalc;
goto fail;
}
/* look for the calculator */
libusb_device *calc = NULL;
for (int id = 0; id < device_count; id++) {
/* get the device descriptor */
struct libusb_device_descriptor descriptor;
if (libusb_get_device_descriptor(device_list[id], &descriptor))
continue;
/* check the IDs */
if (descriptor.idVendor == 0x07cf && descriptor.idProduct == 0x6101) {
calc = device_list[id];
break;
}
}
/* the calculator wasn't found */
if (!calc) {
libusb_free_device_list(device_list, 1);
err = p7_error_nocalc;
goto fail;
}
/* get calculator handle */
logr_info("getting the device handle");
int uerr = libusb_open(calc, &dhandle);
libusb_free_device_list(device_list, 1);
/* check if we have the handle */
switch (uerr) {
/* couldn't get access to the device */
case LIBUSB_ERROR_ACCESS:
err = p7_error_noaccess;
goto fail;
/* default cases */
case 0: break;
default:
logr_fatal("libusb_open returned %d: %s",
uerr, libusb_error_name(uerr));
err = p7_error_noaccess;
goto fail;
}
/* disconnect any kernel driver */
logr_info("Detaching kernel driver, if any.");
switch ((uerr = libusb_detach_kernel_driver(dhandle, 0))) {
/* cases where it's okay */
case 0: case LIBUSB_ERROR_NOT_SUPPORTED:
case LIBUSB_ERROR_NOT_FOUND: break;
/* cases where it's not okay */
case LIBUSB_ERROR_INVALID_PARAM:
logr_fatal("Interface 0 doesn't exist...?");
case LIBUSB_ERROR_NO_DEVICE:
err = p7_error_nocalc;
goto fail;
default:
logr_fatal("libusb returned %d: %s",
uerr, libusb_error_name(uerr));
goto fail;
}
/* check if the interface is active */
libusb_kernel_driver_active(dhandle, 0);
/* claim the interface */
logr_info("Claiming the interface");
switch ((uerr = libusb_claim_interface(dhandle, 0))) {
/* cases where it's okay (not a lot) */
case 0: break;
/* cases where it's not okay */
case LIBUSB_ERROR_NO_DEVICE:
case LIBUSB_ERROR_NOT_FOUND:
err = p7_error_nocalc;
goto fail;
case LIBUSB_ERROR_BUSY:
logr_info("Another program or driver has claimed the interface...");
err = p7_error_noaccess;
goto fail;
default:
logr_info("libusb returned %d: %s",
uerr, libusb_error_name(uerr));
goto fail;
}
/* make the cookie */
libusb_cookie_t *cookie = malloc(sizeof(libusb_cookie_t));
if (!cookie) { free(cookie); goto fail; }
*cookie = (libusb_cookie_t){
._context = context,
._handle = dhandle,
._start = 0,
._end = -1
};
/* final call. */
return (p7_sinit(handle, flags, NULL, (p7_stream_t){
.cookie = cookie,
.read = p7_libusb_read,
.write = p7_libusb_write,
.close = p7_libusb_close
}));
fail:
if (handle) libusb_close(dhandle);
if (context) libusb_exit(context);
return (err);
}
#endif