1
0
Fork 0
mirror of https://github.com/eliboa/TegraRcmGUI.git synced 2025-01-06 07:26:04 +00:00
TegraRcmGUI/kourou/libs/tegrarcm/usb.c
2020-06-24 20:31:54 +02:00

353 lines
8.8 KiB
C

/*
< * Copyright (c) 2011-2016, NVIDIA CORPORATION
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <sys/param.h>
#include "usb.h"
#include "debug.h"
// USB xfer timeout in ms
#define USB_TIMEOUT 1000
#define USB_XFER_MAX 4096
uint32_t usb_timeout = USB_TIMEOUT;
//
// returns 1 if the specified usb device matches the vendor id
//
static int usb_match(libusb_device *dev, uint16_t venid, uint16_t *devid
#ifdef HAVE_USB_PORT_MATCH
,
bool *match_port, uint8_t *match_bus, uint8_t *match_ports,
int *match_ports_len
#endif
)
{
struct libusb_device_descriptor desc;
#ifdef HAVE_USB_PORT_MATCH
uint8_t dev_bus;
uint8_t dev_ports[PORT_MATCH_MAX_PORTS];
int dev_ports_len;
#ifdef DEBUG
int i;
char portstr[(PORT_MATCH_MAX_PORTS * 4)];
char *portstrp;
size_t portstr_lenleft;
int printed;
#endif
#endif
if (libusb_get_device_descriptor(dev, &desc)) {
dprintf("libusb_get_device_descriptor\n");
return 0;
}
if (desc.idVendor != venid) {
dprintf("non-NVIDIA USB device: 0x%x:0x%x\n",
desc.idVendor, desc.idProduct);
return 0;
}
switch (desc.idProduct & 0xff) {
case USB_DEVID_NVIDIA_TEGRA20:
case USB_DEVID_NINTENDO_SWITCH:
case USB_DEVID_NVIDIA_TEGRA30:
case USB_DEVID_NVIDIA_TEGRA114:
case USB_DEVID_NVIDIA_TEGRA124:
break;
default:
dprintf("non-Tegra NVIDIA USB device: 0x%x:0x%x\n",
desc.idVendor, desc.idProduct);
return 0;
}
#ifdef HAVE_USB_PORT_MATCH
dev_bus = libusb_get_bus_number(dev);
dev_ports_len = libusb_get_port_numbers(dev, dev_ports,
PORT_MATCH_MAX_PORTS);
if (dev_ports_len < 0) {
dprintf("libusb_get_port_numbers failed: %d\n", dev_ports_len);
return 0;
}
if (*match_port) {
if (dev_bus != *match_bus) {
dprintf("bus mismatch dev:%d match:%d\n", dev_bus,
*match_bus);
return 0;
}
if (memcmp(dev_ports, match_ports, dev_ports_len)) {
dprintf("ports mismatch\n");
return 0;
}
}
if (!*match_port) {
*match_port = true;
*match_bus = dev_bus;
memcpy(match_ports, dev_ports, dev_ports_len);
*match_ports_len = dev_ports_len;
#ifdef DEBUG
portstrp = portstr;
portstr_lenleft = sizeof(portstr);
printed = snprintf(portstrp, portstr_lenleft, "%d-%d",
dev_bus, dev_ports[0]);
portstrp += printed;
portstr_lenleft -= printed;
for (i = 1; i < dev_ports_len; i++) {
printed = snprintf(portstrp, portstr_lenleft, ".%d",
(int)dev_ports[i]);
portstrp += printed;
portstr_lenleft -= printed;
}
dprintf("Enabling future match %s\n", portstr);
#endif
}
#endif
dprintf("device matches\n");
*devid = desc.idProduct;
return 1;
}
static void usb_check_interface(const struct libusb_interface_descriptor *iface_desc,
uint8_t *endpt_in,
uint8_t *endpt_out,
int *found_in,
int *found_out)
{
int endpt_num;
const struct libusb_endpoint_descriptor *endpt_desc;
for (endpt_num = 0;
endpt_num < iface_desc->bNumEndpoints && (!*found_in || !*found_out);
endpt_num++) {
endpt_desc = &iface_desc->endpoint[endpt_num];
// skip this endpoint if it's not bulk
if ((endpt_desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != LIBUSB_TRANSFER_TYPE_BULK)
continue;
if ((endpt_desc->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
// found input endpoint
*endpt_in = endpt_desc->bEndpointAddress;
*found_in = 1;
} else {
// found output endpoint
*endpt_out = endpt_desc->bEndpointAddress;
*found_out = 1;
}
}
}
static int usb_get_interface(libusb_device_handle *handle,
uint8_t *ifc,
uint8_t *endpt_in,
uint8_t *endpt_out)
{
libusb_device *dev;
struct libusb_config_descriptor *config = NULL;
int iface_num, alt_iface;
const struct libusb_interface *iface;
const struct libusb_interface_descriptor *iface_desc;
int found_in = 0, found_out = 0;
dev = libusb_get_device(handle);
if (!dev) {
dprintf("libusb_get_device failed\n");
return ENODEV;
}
if (libusb_get_active_config_descriptor(dev, &config)) {
dprintf("libusb_get_active_config_descriptor failed");
return EIO;
}
for (iface_num = 0; iface_num < config->bNumInterfaces; iface_num++) {
iface = &config->interface[iface_num];
for (alt_iface = 0;
alt_iface < iface->num_altsetting && (!found_in || !found_out);
alt_iface++) {
iface_desc = &iface->altsetting[alt_iface];
usb_check_interface(iface_desc, endpt_in, endpt_out,
&found_in, &found_out);
if (found_in && found_out) {
// save off the interface
*ifc = iface_desc->bInterfaceNumber;
break;
}
}
}
if (config)
libusb_free_config_descriptor(config);
if (!found_in || !found_out) {
dprintf("failed to find input and output endpoints\n");
return ENODEV;
}
return 0;
}
usb_device_t *usb_open(uint16_t venid, uint16_t *devid
#ifdef HAVE_USB_PORT_MATCH
,
bool *match_port, uint8_t *match_bus, uint8_t *match_ports,
int *match_ports_len
#endif
)
{
libusb_device **list = NULL;
libusb_device *found = NULL;
ssize_t cnt, i=0;
usb_device_t *usb = NULL;
if (libusb_init(NULL)) {
dprintf("libusb_init\n");
goto fail;
}
cnt = libusb_get_device_list(NULL, &list);
if (cnt < 0) {
dprintf("libusb_get_device_list\n");
goto fail;
}
for (i = 0; i < cnt; i++) {
libusb_device *device = list[i];
if (usb_match(device, venid, devid
#ifdef HAVE_USB_PORT_MATCH
, match_port, match_bus, match_ports,
match_ports_len
#endif
)) {
found = device;
break;
}
}
if (!found) {
dprintf("could't find device\n");
goto fail;
}
usb = (usb_device_t *)malloc(sizeof(usb_device_t));
if (!usb) {
dprintf("out of mem\n");
goto fail;
}
memset(usb, 0, sizeof(usb_device_t));
if (libusb_open(found, &usb->handle)) {
dprintf("libusb_open failed\n");
goto fail;
}
if (usb_get_interface(usb->handle, &usb->iface_num,
&usb->endpt_in, &usb->endpt_out)) {
dprintf("usb_get_interface failed\n");
goto fail;
}
// claim the interface
libusb_claim_interface(usb->handle, usb->iface_num);
usb->initialized = 1;
libusb_free_device_list(list, 1);
return usb;
fail:
if (usb)
free(usb);
if (list)
libusb_free_device_list(list, 1);
return NULL;
}
void usb_close(usb_device_t *usb)
{
if (!usb)
return;
if (usb->initialized) {
libusb_release_interface(usb->handle, usb->iface_num);
if (usb->handle)
libusb_close(usb->handle);
}
libusb_exit(NULL);
}
int usb_write(usb_device_t *usb, uint8_t *buf, int len)
{
int ret;
int chunk_size;
int actual_chunk;
while (len) {
chunk_size = MIN(len, USB_XFER_MAX);
ret = libusb_bulk_transfer(usb->handle, usb->endpt_out, buf,
chunk_size, &actual_chunk, usb_timeout);
if (ret != LIBUSB_SUCCESS) {
dprintf("libusb write failure: %d: %s\n", ret, libusb_error_name(ret));
return EIO;
}
if (actual_chunk != chunk_size) {
dprintf("write truncated");
return EIO;
}
len -= actual_chunk;
buf += actual_chunk;
}
return 0;
}
int usb_read(usb_device_t *usb, uint8_t *buf, int len, int *actual_len)
{
int ret;
int chunk_size;
int actual_chunk;
*actual_len = 0;
while (len) {
chunk_size = MIN(len, USB_XFER_MAX);
ret = libusb_bulk_transfer(usb->handle, usb->endpt_in, buf,
chunk_size, &actual_chunk, usb_timeout);
if (ret != LIBUSB_SUCCESS) {
dprintf("libusb read failure: %d: %s\n", ret, libusb_error_name(ret));
return EIO;
}
len -= chunk_size;
buf += chunk_size;
*actual_len += actual_chunk;
if (actual_chunk < chunk_size)
break;
}
return 0;
}