Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the flashrom project. |
| 3 | * |
| 4 | * Copyright (C) 2016 secunet Security Networks AG |
| 5 | * Copyright (C) 2018 Linaro Limited |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | */ |
| 17 | |
Edward O'Callaghan | b863127 | 2019-10-22 10:46:04 +1100 | [diff] [blame] | 18 | #include <inttypes.h> |
Daniel Thompson | af49919 | 2018-07-12 12:03:51 +0100 | [diff] [blame] | 19 | #include <stdbool.h> |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 20 | #include <string.h> |
| 21 | #include <libusb.h> |
| 22 | #include "programmer.h" |
| 23 | |
Daniel Thompson | af49919 | 2018-07-12 12:03:51 +0100 | [diff] [blame] | 24 | /* |
| 25 | * Check whether we should filter the current device. |
| 26 | * |
| 27 | * The main code filters by VID/PID then calls out to the filter function for extra filtering. |
| 28 | * The filter function is called twice for each device. Once with handle == NULL to allow the |
| 29 | * filter to cull devices without opening them and, assuming the first filter does not trigger, |
| 30 | * also with a real handle to allow the filter to query the device further. |
| 31 | * |
| 32 | * Returns true if the device should be skipped. |
| 33 | */ |
| 34 | typedef bool (*filter_func)(struct libusb_device_descriptor *desc, struct libusb_device_handle *handle, void*ctx); |
| 35 | |
| 36 | static struct libusb_device_handle *get_by_vid_pid_filter(struct libusb_context *usb_ctx, |
| 37 | uint16_t vid, uint16_t pid, filter_func filter_fn, void *filter_ctx) |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 38 | { |
| 39 | struct libusb_device **list; |
| 40 | ssize_t count = libusb_get_device_list(usb_ctx, &list); |
| 41 | if (count < 0) { |
| 42 | msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count)); |
| 43 | return NULL; |
| 44 | } |
| 45 | |
| 46 | ssize_t i = 0; |
| 47 | for (i = 0; i < count; i++) { |
| 48 | struct libusb_device *dev = list[i]; |
| 49 | struct libusb_device_descriptor desc; |
| 50 | struct libusb_device_handle *handle; |
| 51 | |
| 52 | int res = libusb_get_device_descriptor(dev, &desc); |
| 53 | if (res != 0) { |
| 54 | msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(res)); |
| 55 | continue; |
| 56 | } |
| 57 | |
Patrick Rudolph | 4ca575d | 2019-05-20 11:31:44 +0200 | [diff] [blame] | 58 | if ((desc.idVendor != vid) || (desc.idProduct != pid)) |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 59 | continue; |
| 60 | |
| 61 | msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n", |
| 62 | desc.idVendor, desc.idProduct, |
| 63 | libusb_get_bus_number(dev), libusb_get_device_address(dev)); |
| 64 | |
Daniel Thompson | af49919 | 2018-07-12 12:03:51 +0100 | [diff] [blame] | 65 | /* allow filters to trigger before the device is opened */ |
| 66 | if (filter_fn(&desc, NULL, filter_ctx)) |
| 67 | continue; |
| 68 | |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 69 | res = libusb_open(dev, &handle); |
| 70 | if (res != 0) { |
Daniel Thompson | af49919 | 2018-07-12 12:03:51 +0100 | [diff] [blame] | 71 | msg_perr("Opening the USB device at address %d-%d failed (%s)!\n", |
| 72 | libusb_get_bus_number(dev), libusb_get_device_address(dev), |
| 73 | libusb_error_name(res)); |
| 74 | break; |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 75 | } |
| 76 | |
Daniel Thompson | af49919 | 2018-07-12 12:03:51 +0100 | [diff] [blame] | 77 | /* filter can also trigger after a device is opened */ |
| 78 | if (filter_fn(&desc, handle, filter_ctx)) { |
| 79 | libusb_close(handle); |
| 80 | continue; |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | libusb_free_device_list(list, 1); |
| 84 | return handle; |
| 85 | } |
| 86 | |
| 87 | libusb_free_device_list(list, 1); |
| 88 | return NULL; |
Daniel Thompson | af49919 | 2018-07-12 12:03:51 +0100 | [diff] [blame] | 89 | |
| 90 | } |
| 91 | |
| 92 | static bool filter_by_serial(struct libusb_device_descriptor *desc, struct libusb_device_handle *handle, |
| 93 | void *serialno) |
| 94 | { |
| 95 | /* Never filter if device is not yet open or when user did not provide a serial number */ |
| 96 | if (!handle || !serialno) |
| 97 | return false; |
| 98 | |
| 99 | unsigned char myserial[64]; |
| 100 | int res = libusb_get_string_descriptor_ascii(handle, desc->iSerialNumber, myserial, sizeof(myserial)); |
| 101 | if (res < 0) { |
| 102 | msg_perr("Reading the USB serialno failed (%s)!\n", libusb_error_name(res)); |
| 103 | return true; |
| 104 | } |
| 105 | msg_pdbg("Serial number is %s\n", myserial); |
| 106 | |
| 107 | /* Filter out any serial number that does not commence with serialno */ |
| 108 | return 0 != strncmp(serialno, (char *)myserial, strlen(serialno)); |
| 109 | } |
| 110 | |
| 111 | struct libusb_device_handle *usb_dev_get_by_vid_pid_serial( |
| 112 | struct libusb_context *usb_ctx, uint16_t vid, uint16_t pid, const char *serialno) |
| 113 | { |
| 114 | return get_by_vid_pid_filter(usb_ctx, vid, pid, filter_by_serial, (void *)serialno); |
| 115 | } |
| 116 | |
| 117 | static bool filter_by_number(struct libusb_device_descriptor *desc, struct libusb_device_handle *handle, |
| 118 | void *ctx) |
| 119 | { |
| 120 | /* This filter never triggers once it has allowed the device to be opened */ |
| 121 | if (handle != NULL) |
| 122 | return false; |
| 123 | |
| 124 | unsigned int *nump = ctx; |
| 125 | if (*nump) { |
| 126 | (*nump)--; |
| 127 | return true; |
| 128 | } |
| 129 | |
| 130 | return false; |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | /* |
| 134 | * This function allows different devices to be targeted based on enumeration order. Different |
| 135 | * hotplug sequencing (or simply a reboot) may change the enumeration order. This function should |
| 136 | * only be used if a programmers does not provide an alternative way to identify itself uniquely |
| 137 | * (such as a unique serial number). |
| 138 | */ |
| 139 | struct libusb_device_handle *usb_dev_get_by_vid_pid_number( |
| 140 | struct libusb_context *usb_ctx, uint16_t vid, uint16_t pid, unsigned int num) |
| 141 | { |
Daniel Thompson | af49919 | 2018-07-12 12:03:51 +0100 | [diff] [blame] | 142 | return get_by_vid_pid_filter(usb_ctx, vid, pid, filter_by_number, &num); |
Daniel Thompson | 1d507a0 | 2018-07-12 11:02:28 +0100 | [diff] [blame] | 143 | } |