Files
checkm8_tool/checkm8_remote/src/usb_helpers.c

672 lines
20 KiB
C

#include "usb_helpers.h"
#ifdef WITH_ARDUINO
#include <termio.h>
#include <fcntl.h>
#include <unistd.h>
#include "ard_protocol.h"
#else
#include <stdlib.h>
#include <string.h>
#include "libusbi.h"
#endif
int open_device_session(struct pwned_device *dev)
{
checkm8_debug_indent("open_device_session(dev = %p)\n", dev);
#ifdef WITH_ARDUINO
// based on https://github.com/todbot/arduino-serial/blob/master/arduino-serial-lib.c
struct termios toptions;
char buf;
int ard_fd = open(ARDUINO_DEV, O_RDWR | O_NONBLOCK);
if(ard_fd == -1)
{
checkm8_debug_indent("\tfailed to open arduino device %s\n", ARDUINO_DEV);
return CHECKM8_FAIL_NODEV;
}
checkm8_debug_indent("\topened arduino device %s\n", ARDUINO_DEV);
if(tcgetattr(ard_fd, &toptions) < 0)
{
checkm8_debug_indent("\tfailed to get arduino terminal attributes\n");
close(ard_fd);
return CHECKM8_FAIL_NODEV;
}
speed_t brate;
switch(ARDUINO_BAUD)
{
case 4800:
brate = B4800;
break;
case 9600:
brate = B9600;
break;
case 19200:
brate = B19200;
break;
case 38400:
brate = B38400;
break;
case 57600:
brate = B57600;
break;
case 115200:
brate = B115200;
break;
default:
brate = B9600;
break;
}
cfsetispeed(&toptions, brate);
cfsetospeed(&toptions, brate);
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
toptions.c_cflag &= ~CRTSCTS;
toptions.c_cflag |= CREAD | CLOCAL;
toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
toptions.c_oflag &= ~OPOST;
toptions.c_cc[VMIN] = 0;
toptions.c_cc[VTIME] = 0;
tcsetattr(ard_fd, TCSANOW, &toptions);
if(tcsetattr(ard_fd, TCSAFLUSH, &toptions) < 0)
{
checkm8_debug_indent("\tfailed to set terminal attributes");
close(ard_fd);
return CHECKM8_FAIL_NODEV;
}
checkm8_debug_indent("\tset arduino terminal attributes\n");
// read a setup verification byte
while(read(ard_fd, &buf, 1) == 0);
if(buf == PROT_SUCCESS)
{
checkm8_debug_indent("\tarduino successfully setup\n");
dev->ard_fd = ard_fd;
return CHECKM8_SUCCESS;
}
else if(buf == PROT_FAIL_INITUSB)
{
checkm8_debug_indent("\tarduino failed to init USB host shield\n");
close(ard_fd);
return CHECKM8_FAIL_NOTDONE;
}
else
{
checkm8_debug_indent("\tunexpected response %X\n", buf);
close(ard_fd);
return CHECKM8_FAIL_PROT;
}
#else
int i, usb_dev_count, ret = CHECKM8_FAIL_NODEV;
libusb_device **usb_device_list = NULL;
if(dev->bundle->ctx == NULL)
{
checkm8_debug_indent("\tbundle ctx is NULL, allocating\n");
dev->bundle->ctx = malloc(sizeof(libusb_context));
libusb_init(&dev->bundle->ctx);
}
else
{
if(dev->bundle->descriptor != NULL &&
dev->bundle->descriptor->idVendor == dev->idVendor &&
dev->bundle->descriptor->idProduct == dev->idProduct)
{
checkm8_debug_indent("\tbundle is already valid\n");
return CHECKM8_SUCCESS;
}
}
usb_dev_count = libusb_get_device_list(dev->bundle->ctx, &usb_device_list);
checkm8_debug_indent("\tfound %i USB devices\n", usb_dev_count);
dev->bundle->device = NULL;
dev->bundle->handle = NULL;
dev->bundle->descriptor = malloc(sizeof(struct libusb_device_descriptor));
for(i = 0; i < usb_dev_count; i++)
{
dev->bundle->device = usb_device_list[i];
libusb_get_device_descriptor(dev->bundle->device, dev->bundle->descriptor);
if(dev->bundle->descriptor->idVendor == dev->idVendor &&
dev->bundle->descriptor->idProduct == dev->idProduct)
{
checkm8_debug_indent("\tchecking device %i ... match!\n", i);
ret = CHECKM8_SUCCESS;
break;
}
checkm8_debug_indent("\tchecking device %i ... no match\n", i);
}
libusb_free_device_list(usb_device_list, usb_dev_count);
if(ret == CHECKM8_SUCCESS)
{
checkm8_debug_indent("\topening device and returning success\n");
ret = libusb_open(dev->bundle->device, &dev->bundle->handle);
if(ret == 0)
{
libusb_set_auto_detach_kernel_driver(dev->bundle->handle, 1);
}
else
{
checkm8_debug_indent("\tfailed to open device\n");
libusb_exit(dev->bundle->ctx);
free(dev->bundle->descriptor);
}
return ret;
}
else
{
checkm8_debug_indent("\tcould not find a matching device\n");
libusb_exit(dev->bundle->ctx);
free(dev->bundle->descriptor);
dev->bundle->ctx = NULL;
dev->bundle->device = NULL;
dev->bundle->handle = NULL;
dev->bundle->descriptor = NULL;
}
return ret;
#endif
}
int close_device_session(struct pwned_device *dev)
{
checkm8_debug_indent("close_device_session(dev = %p)\n", dev);
#ifdef WITH_ARDUINO
int ret = close(dev->ard_fd);
dev->ard_fd = -1;
if(ret == -1)
{
checkm8_debug_indent("\tfailed to close arduino fd\n");
return CHECKM8_FAIL_NODEV;
}
return CHECKM8_SUCCESS;
#else
if(dev->bundle->handle != NULL)
{
checkm8_debug_indent("\tclosing handle\n");
libusb_close(dev->bundle->handle);
dev->bundle->handle = NULL;
}
dev->bundle->device = NULL;
if(dev->bundle->ctx != NULL)
{
checkm8_debug_indent("\texiting context\n");;
libusb_exit(dev->bundle->ctx);
dev->bundle->ctx = NULL;
}
if(dev->bundle->descriptor != NULL)
{
checkm8_debug_indent("\tfreeing device descriptor\n");
free(dev->bundle->descriptor);
dev->bundle->descriptor = NULL;
}
return CHECKM8_SUCCESS;
#endif
}
int is_device_session_open(struct pwned_device *dev)
{
#ifdef WITH_ARDUINO
return dev->ard_fd != -1;
#else
return dev->bundle->ctx != NULL && dev->bundle->device != NULL &&
dev->bundle->handle != NULL && dev->bundle->descriptor != NULL;
#endif
}
int partial_ctrl_transfer(struct pwned_device *dev,
unsigned char bmRequestType, unsigned char bRequest,
unsigned short wValue, unsigned short wIndex,
unsigned char *data, unsigned short data_len,
unsigned int timeout)
{
checkm8_debug_indent(
"partial_ctrl_transfer(dev = %p, bmRequestType = %i, bRequest = %i, wValue = %i, wIndex = %i, data = %p, data_len = %i, timeout = %i)\n",
dev, bmRequestType, bRequest, wValue, wIndex, data, data_len, timeout);
#ifdef WITH_ARDUINO
char buf;
struct usb_xfer_args args;
args.bmRequestType = bmRequestType;
args.bRequest = bRequest;
args.wValue = wValue;
args.wIndex = wIndex;
args.data_len = data_len;
checkm8_debug_indent("\tsending data to arduino\n");
write(dev->ard_fd, &PROT_PARTIAL_CTRL_XFER, 1);
write(dev->ard_fd, &args, sizeof(struct usb_xfer_args));
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived ack\n");
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_SUCCESS)
{
checkm8_debug_indent("\tsuccess\n");
return CHECKM8_SUCCESS;
}
else if(buf == PROT_FAIL_USB)
{
while(read(dev->ard_fd, &buf, 1) == 0);
checkm8_debug_indent("\trequest failed with error %X\n", buf);
return CHECKM8_FAIL_XFER;
}
else
{
checkm8_debug_indent("\tunexpected response %X\n", buf);
return CHECKM8_FAIL_PROT;
}
}
else
{
checkm8_debug_indent("\tno ack received (got %x)\n", buf);
return CHECKM8_FAIL_PROT;
}
#else
struct timeval start, end;
unsigned char usb_transfer_buf[8 + data_len];
int ret;
gettimeofday(&start, NULL);
struct libusb_transfer *usb_transfer = libusb_alloc_transfer(0);
libusb_fill_control_setup(usb_transfer_buf, bmRequestType, bRequest, wValue, wIndex, data_len);
memcpy(&usb_transfer_buf[8], data, data_len);
libusb_fill_control_transfer(usb_transfer, dev->bundle->handle, usb_transfer_buf, NULL, NULL, 1);
checkm8_debug_indent("\tsubmiting urb\n");
ret = libusb_submit_transfer(usb_transfer);
if(ret != 0)
{
checkm8_debug_indent("\tfailed to submit async USB transfer: %s\n", libusb_error_name(ret));
libusb_free_transfer(usb_transfer);
return CHECKM8_FAIL_XFER;
}
while(1)
{
gettimeofday(&end, NULL);
if((1000000 * end.tv_sec + end.tv_usec) - (1000000 * end.tv_sec + start.tv_usec) > timeout)
{
ret = libusb_cancel_transfer(usb_transfer);
if(ret != 0)
{
checkm8_debug_indent("\tfailed to cancel async USB transfer: %s\n", libusb_error_name(ret));
return CHECKM8_FAIL_XFER;
}
return CHECKM8_SUCCESS;
}
}
#endif
}
int no_error_ctrl_transfer(struct pwned_device *dev,
unsigned char bmRequestType, unsigned char bRequest,
unsigned short wValue, unsigned short wIndex,
unsigned char *data, unsigned short data_len,
unsigned int timeout)
{
checkm8_debug_indent(
"no_error_ctrl_transfer(dev = %p, bmRequestType = %i, bRequest = %i, wValue = %i, wIndex = %i, data = %p, data_len = %i, timeout = %i)\n",
dev, bmRequestType, bRequest, wValue, wIndex, data, data_len, timeout);
#ifdef WITH_ARDUINO
unsigned char buf;
struct usb_xfer_args args;
args.bmRequestType = bmRequestType;
args.bRequest = bRequest;
args.wValue = wValue;
args.wIndex = wIndex;
args.data_len = data_len;
checkm8_debug_indent("\tsending data to arduino\n");
write(dev->ard_fd, &PROT_NO_ERROR_CTRL_XFER, 1);
write(dev->ard_fd, &args, sizeof(struct usb_xfer_args));
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived ack\n");
do
{
if(buf == PROT_FAIL_USB)
{
while(read(dev->ard_fd, &buf, 1) == 0);
checkm8_debug_indent("\treceived error %X but ignoring\n", buf);
}
while(read(dev->ard_fd, &buf, 1) == 0);
} while(buf != PROT_SUCCESS);
checkm8_debug_indent("\tsuccess\n");
return CHECKM8_SUCCESS;
}
else
{
checkm8_debug_indent("\tno ack received (got %x)\n", buf);
return CHECKM8_FAIL_PROT;
}
#else
int ret;
unsigned char recipient = bmRequestType & 3u;
unsigned char rqtype = bmRequestType & (3u << 5u);
if(recipient == 1 && rqtype == (2u << 5u))
{
unsigned short interface = wIndex & 0xFFu;
ret = libusb_claim_interface(dev->bundle->handle, interface);
if(ret > 0)
{
checkm8_debug_indent("\tfailed to claim interface: %s\n", libusb_error_name(ret));
return CHECKM8_FAIL_XFER;
}
}
ret = libusb_control_transfer(dev->bundle->handle, bmRequestType, bRequest, wValue, wIndex, data, data_len,
timeout);
checkm8_debug_indent("\tgot error %s but ignoring\n", libusb_error_name(ret));
return CHECKM8_SUCCESS;
#endif
}
int no_error_ctrl_transfer_data(struct pwned_device *dev,
unsigned char bmRequestType, unsigned char bRequest,
unsigned short wValue, unsigned short wIndex,
unsigned char *data, unsigned short data_len,
unsigned int timeout)
{
checkm8_debug_indent(
"no_error_ctrl_transfer_data(dev = %p, bmRequestType = %i, bRequest = %i, wValue = %i, wIndex = %i, data = %p, data_len = %i, timeout = %i)\n",
dev, bmRequestType, bRequest, wValue, wIndex, data, data_len, timeout);
#ifdef WITH_ARDUINO
int amount, index = 0;
char buf;
struct usb_xfer_args args;
args.bmRequestType = bmRequestType;
args.bRequest = bRequest;
args.wValue = wValue;
args.wIndex = wIndex;
args.data_len = data_len;
checkm8_debug_indent("\tsending data to arduino\n");
write(dev->ard_fd, &PROT_NO_ERROR_CTRL_XFER_DATA, 1);
write(dev->ard_fd, &args, sizeof(struct usb_xfer_args));
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived argument ack\n");
while(index < data_len)
{
if(data_len - index > ARD_BUF_SIZE) amount = ARD_BUF_SIZE;
else amount = data_len - index;
checkm8_debug_indent("\twriting data chunk of size %i\n", amount);
write(dev->ard_fd, &data[index], amount);
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived data ack\n");
index += amount;
}
else
{
checkm8_debug_indent("\treceived unexpected response %x\n", buf);
return CHECKM8_FAIL_PROT;
}
}
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_SUCCESS)
{
checkm8_debug_indent("\tsuccess\n");
return CHECKM8_SUCCESS;
}
else
{
checkm8_debug_indent("\tunexpected response %x\n", buf);
return CHECKM8_FAIL_PROT;
}
}
else
{
checkm8_debug_indent("\tno ack received (got %x)\n", buf);
return CHECKM8_FAIL_PROT;
}
#else
return no_error_ctrl_transfer(dev, bmRequestType, bRequest, wValue, wIndex, data, data_len, timeout);
#endif
}
int ctrl_transfer(struct pwned_device *dev,
unsigned char bmRequestType, unsigned char bRequest,
unsigned short wValue, unsigned short wIndex,
unsigned char *data, unsigned short data_len,
unsigned int timeout)
{
checkm8_debug_indent(
"ctrl_transfer(dev = %p, bmRequestType = %X, bRequest = %X, wValue = %i, wIndex = %i, data = %p, data_len = %i, timeout = %i)\n",
dev, bmRequestType, bRequest, wValue, wIndex, data, data_len, timeout);
#ifdef WITH_ARDUINO
int amount, index;
char buf;
struct usb_xfer_args args;
args.bmRequestType = bmRequestType;
args.bRequest = bRequest;
args.wValue = wValue;
args.wIndex = wIndex;
args.data_len = data_len;
checkm8_debug_indent("\tsending data to arduino\n");
write(dev->ard_fd, &PROT_CTRL_XFER, 1);
write(dev->ard_fd, &args, sizeof(struct usb_xfer_args));
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived argument ack\n");
if(bmRequestType & 0x80)
{
amount = 0;
while(amount < data_len)
{
// get the size of this chunk
while(read(dev->ard_fd, &buf, 1) == 0);
checkm8_debug_indent("\treceiving data chunk of size %i\n", buf);
index = 0;
while(index < buf)
{
index += read(dev->ard_fd, &data[amount + index], buf - index);
}
amount += buf;
}
}
else
{
index = 0;
while(index < data_len)
{
if(data_len - index > ARD_BUF_SIZE) amount = ARD_BUF_SIZE;
else amount = data_len - index;
checkm8_debug_indent("\twriting data chunk of size %i\n", amount);
write(dev->ard_fd, &data[index], amount);
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived data ack\n");
index += amount;
}
else
{
checkm8_debug_indent("\treceived unexpected response %x\n", buf);
return CHECKM8_FAIL_PROT;
}
}
}
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_SUCCESS)
{
checkm8_debug_indent("\tsuccess\n");
return data_len;
}
else
{
checkm8_debug_indent("\tunexpected response %x\n", buf);
return CHECKM8_FAIL_PROT;
}
}
else
{
checkm8_debug_indent("\tno ack received (got %x)\n", buf);
return CHECKM8_FAIL_PROT;
}
#else
return libusb_control_transfer(dev->bundle->handle,
bmRequestType, bRequest,
wValue, wIndex,
data, data_len,
timeout);
#endif
}
int reset(struct pwned_device *dev)
{
checkm8_debug_indent("reset(dev = %p)\n", dev);
#ifdef WITH_ARDUINO
char buf;
write(dev->ard_fd, &PROT_RESET, 1);
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived ack\n");
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_SUCCESS)
{
checkm8_debug_indent("\tsuccess\n");
return CHECKM8_SUCCESS;
}
else
{
checkm8_debug_indent("\tunexpected response %X\n", buf);
return CHECKM8_FAIL_PROT;
}
}
else
{
checkm8_debug_indent("\tno ack received (got %x)\n", buf);
return CHECKM8_FAIL_PROT;
}
#else
return libusb_reset_device(dev->bundle->handle);
#endif
}
int serial_descriptor(struct pwned_device *dev, unsigned char *serial_buf, int len)
{
checkm8_debug_indent("serial_descriptor(dev = %p, serial_buf = %p, len = %i)\n", dev, serial_buf, len);
#ifdef WITH_ARDUINO
char buf;
int curr, ret;
struct serial_desc_args args;
args.dev_idVendor = dev->idVendor;
args.dev_idProduct = dev->idProduct;
args.len = len;
checkm8_debug_indent("\tsending data to arduino\n");
write(dev->ard_fd, &PROT_SERIAL_DESC, 1);
write(dev->ard_fd, &args, sizeof(struct serial_desc_args));
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_ACK)
{
checkm8_debug_indent("\treceived ack\n");
while(read(dev->ard_fd, &buf, 1) == 0);
if(buf == PROT_FAIL_NODEV)
{
checkm8_debug_indent("\tno device attached\n");
return CHECKM8_FAIL_NODEV;
}
else if(buf == PROT_FAIL_WRONGDEV)
{
checkm8_debug_indent("\twrong device attached\n");
return CHECKM8_FAIL_NODEV;
}
else if(buf == PROT_SUCCESS)
{
checkm8_debug_indent("\tsuccess, reading serial descriptor\n");
curr = 0;
while(curr < len)
{
ret = read(dev->ard_fd, &serial_buf[curr], len - curr);
if(ret > 0) curr += ret;
}
return CHECKM8_SUCCESS;
}
else
{
checkm8_debug_indent("\tunexpected response %X\n", buf);
return CHECKM8_FAIL_PROT;
}
}
else
{
checkm8_debug_indent("\tno ack received (got %x)\n", buf);
return CHECKM8_FAIL_PROT;
}
#else
struct libusb_device_handle *handle = dev->bundle->handle;
struct libusb_device_descriptor *desc = dev->bundle->descriptor;
libusb_get_string_descriptor_ascii(handle, desc->iSerialNumber, serial_buf, len);
return CHECKM8_SUCCESS;
#endif
}