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