coreboot-libre-fam15h-rdimm/3rdparty/chromeec/util/usb_if.c

230 lines
5.2 KiB
C

/*
* Copyright 2018 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usb_if.h"
/* Return 0 on error, since it's never gonna be EP 0 */
static int find_endpoint(const struct libusb_interface_descriptor *iface,
uint16_t subclass,
uint16_t protocol,
struct usb_endpoint *uep)
{
const struct libusb_endpoint_descriptor *ep;
if (iface->bInterfaceClass == 255 &&
iface->bInterfaceSubClass == subclass &&
iface->bInterfaceProtocol == protocol &&
iface->bNumEndpoints) {
ep = &iface->endpoint[0];
uep->ep_num = ep->bEndpointAddress & 0x7f;
uep->chunk_len = ep->wMaxPacketSize;
return 1;
}
return 0;
}
/* Return -1 on error */
static int find_interface(uint16_t subclass,
uint16_t protocol,
struct usb_endpoint *uep)
{
int iface_num = -1;
int r, i, j;
struct libusb_device *dev;
struct libusb_config_descriptor *conf = 0;
const struct libusb_interface *iface0;
const struct libusb_interface_descriptor *iface;
dev = libusb_get_device(uep->devh);
r = libusb_get_active_config_descriptor(dev, &conf);
if (r < 0) {
USB_ERROR("libusb_get_active_config_descriptor", r);
goto out;
}
for (i = 0; i < conf->bNumInterfaces; i++) {
iface0 = &conf->interface[i];
for (j = 0; j < iface0->num_altsetting; j++) {
iface = &iface0->altsetting[j];
if (find_endpoint(iface, subclass, protocol, uep)) {
iface_num = i;
goto out;
}
}
}
out:
libusb_free_config_descriptor(conf);
return iface_num;
}
static libusb_device_handle *check_device(libusb_device *dev,
uint16_t vid, uint16_t pid, const char *serial)
{
struct libusb_device_descriptor desc;
libusb_device_handle *handle = NULL;
char sn[256];
size_t sn_size = 0;
if (libusb_get_device_descriptor(dev, &desc) < 0)
return NULL;
if (libusb_open(dev, &handle) != LIBUSB_SUCCESS)
return NULL;
if (desc.iSerialNumber && serial) {
sn_size = libusb_get_string_descriptor_ascii(handle,
desc.iSerialNumber, (unsigned char *)sn,
sizeof(sn));
}
/*
* If the VID, PID, and serial number don't match, then it's not the
* correct device. Close the handle and return NULL.
*/
if ((vid && vid != desc.idVendor) ||
(pid && pid != desc.idProduct) ||
(serial && ((sn_size != strlen(serial)) ||
memcmp(sn, serial, sn_size)))) {
libusb_close(handle);
return NULL;
}
return handle;
}
int usb_findit(const char *serial, uint16_t vid, uint16_t pid,
uint16_t subclass, uint16_t protocol, struct usb_endpoint *uep)
{
int iface_num, r, i;
libusb_device **devs;
libusb_device_handle *devh = NULL;
ssize_t count;
memset(uep, 0, sizeof(*uep));
/* Must supply either serial or vendor and product ids. */
if (!serial && !(vid && pid))
goto terminate_usb_findit;
r = libusb_init(NULL);
if (r < 0) {
USB_ERROR("libusb_init", r);
goto terminate_usb_findit;
}
printf("finding_device ");
if (vid)
printf("%04x:%04x ", vid, pid);
if (serial)
printf("%s", serial);
printf("\n");
count = libusb_get_device_list(NULL, &devs);
if (count < 0)
goto terminate_usb_findit;
for (i = 0; i < count; i++) {
devh = check_device(devs[i], vid, pid, serial);
if (devh) {
printf("Found device.\n");
break;
}
}
libusb_free_device_list(devs, 1);
if (!devh) {
fprintf(stderr, "Can't find device\n");
goto terminate_usb_findit;
}
uep->devh = devh;
iface_num = find_interface(subclass, protocol, uep);
if (iface_num < 0) {
fprintf(stderr, "USB interface %d is not found\n", uep->ep_num);
goto terminate_usb_findit;
}
if (!uep->chunk_len) {
fprintf(stderr, "wMaxPacketSize isn't valid\n");
goto terminate_usb_findit;
}
printf("found interface %d endpoint %d, chunk_len %d\n",
iface_num, uep->ep_num, uep->chunk_len);
libusb_set_auto_detach_kernel_driver(uep->devh, 1);
r = libusb_claim_interface(uep->devh, iface_num);
if (r < 0) {
USB_ERROR("libusb_claim_interface", r);
goto terminate_usb_findit;
}
printf("READY\n-------\n");
return 0;
terminate_usb_findit:
if (uep->devh)
usb_shut_down(uep);
return -1;
}
int usb_trx(struct usb_endpoint *uep, void *outbuf, int outlen,
void *inbuf, int inlen, int allow_less, size_t *rxed_count)
{
int r, actual;
/* Send data out */
if (outbuf && outlen) {
actual = 0;
r = libusb_bulk_transfer(uep->devh, uep->ep_num,
outbuf, outlen,
&actual, 1000);
if (r < 0) {
USB_ERROR("libusb_bulk_transfer", r);
return -1;
}
if (actual != outlen) {
fprintf(stderr, "%s:%d, only sent %d/%d bytes\n",
__FILE__, __LINE__, actual, outlen);
usb_shut_down(uep);
}
}
/* Read reply back */
if (inbuf && inlen) {
actual = 0;
r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80,
inbuf, inlen,
&actual, 1000);
if (r < 0) {
USB_ERROR("libusb_bulk_transfer", r);
return -1;
}
if ((actual != inlen) && !allow_less) {
fprintf(stderr, "%s:%d, only received %d/%d bytes\n",
__FILE__, __LINE__, actual, inlen);
usb_shut_down(uep);
}
if (rxed_count)
*rxed_count = actual;
}
return 0;
}
void usb_shut_down(struct usb_endpoint *uep)
{
libusb_close(uep->devh);
libusb_exit(NULL);
}