jtag/drivers/cmsis_dap: fix usb bulk connection logic
[openocd.git] / src / jtag / drivers / cmsis_dap_usb_bulk.c
1 /***************************************************************************
2 * Copyright (C) 2018 by Mickaƫl Thomas *
3 * mickael9@gmail.com *
4 * *
5 * Copyright (C) 2016 by Maksym Hilliaka *
6 * oter@frozen-team.com *
7 * *
8 * Copyright (C) 2016 by Phillip Pearson *
9 * pp@myelin.co.nz *
10 * *
11 * Copyright (C) 2014 by Paul Fertser *
12 * fercerpav@gmail.com *
13 * *
14 * Copyright (C) 2013 by mike brown *
15 * mike@theshedworks.org.uk *
16 * *
17 * Copyright (C) 2013 by Spencer Oliver *
18 * spen@spen-soft.co.uk *
19 * *
20 * This program is free software; you can redistribute it and/or modify *
21 * it under the terms of the GNU General Public License as published by *
22 * the Free Software Foundation; either version 2 of the License, or *
23 * (at your option) any later version. *
24 * *
25 * This program is distributed in the hope that it will be useful, *
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
28 * GNU General Public License for more details. *
29 * *
30 * You should have received a copy of the GNU General Public License *
31 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
32 ***************************************************************************/
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include <libusb.h>
39 #include <helper/log.h>
40
41 #include "cmsis_dap.h"
42
43 struct cmsis_dap_backend_data {
44 libusb_context *usb_ctx;
45 libusb_device_handle *dev_handle;
46 unsigned int ep_out;
47 unsigned int ep_in;
48 int interface;
49 };
50
51 static int cmsis_dap_usb_interface = -1;
52
53 static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], char *serial)
54 {
55 int err;
56 libusb_context *ctx;
57 libusb_device **device_list;
58
59 err = libusb_init(&ctx);
60 if (err) {
61 LOG_ERROR("libusb initialization failed: %s", libusb_strerror(err));
62 return ERROR_FAIL;
63 }
64
65 int num_devices = libusb_get_device_list(ctx, &device_list);
66 if (num_devices < 0) {
67 LOG_ERROR("could not enumerate USB devices: %s", libusb_strerror(num_devices));
68 libusb_exit(ctx);
69 return ERROR_FAIL;
70 }
71
72 for (int i = 0; i < num_devices; i++) {
73 libusb_device *dev = device_list[i];
74 struct libusb_device_descriptor dev_desc;
75
76 err = libusb_get_device_descriptor(dev, &dev_desc);
77 if (err) {
78 LOG_ERROR("could not get device descriptor for device %d: %s", i, libusb_strerror(err));
79 continue;
80 }
81
82 /* Match VID/PID */
83
84 bool id_match = false;
85 bool id_filter = vids[0] || pids[0];
86 for (int id = 0; vids[id] || pids[id]; id++) {
87 id_match = !vids[id] || dev_desc.idVendor == vids[id];
88 id_match &= !pids[id] || dev_desc.idProduct == pids[id];
89
90 if (id_match)
91 break;
92 }
93
94 if (id_filter && !id_match)
95 continue;
96
97 /* Don't continue if we asked for a serial number and the device doesn't have one */
98 if (dev_desc.iSerialNumber == 0 && serial && serial[0])
99 continue;
100
101 libusb_device_handle *dev_handle = NULL;
102 err = libusb_open(dev, &dev_handle);
103 if (err) {
104 /* It's to be expected that most USB devices can't be opened
105 * so only report an error if it was explicitly selected
106 */
107 if (id_filter) {
108 LOG_ERROR("could not open device 0x%04x:0x%04x: %s",
109 dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
110 } else {
111 LOG_DEBUG("could not open device 0x%04x:0x%04x: %s",
112 dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
113 }
114 continue;
115 }
116
117 /* Match serial number */
118
119 bool serial_match = false;
120 char dev_serial[256] = {0};
121 if (dev_desc.iSerialNumber > 0) {
122 err = libusb_get_string_descriptor_ascii(
123 dev_handle, dev_desc.iSerialNumber,
124 (uint8_t *)dev_serial, sizeof(dev_serial));
125
126 if (err < 0) {
127 const char *msg = "could not read serial number for device 0x%04x:0x%04x: %s";
128 if (serial)
129 LOG_WARNING(msg, dev_desc.idVendor, dev_desc.idProduct,
130 libusb_strerror(err));
131 else
132 LOG_DEBUG(msg, dev_desc.idVendor, dev_desc.idProduct,
133 libusb_strerror(err));
134 } else if (serial && strncmp(dev_serial, serial, sizeof(dev_serial)) == 0) {
135 serial_match = true;
136 }
137 }
138
139 if (serial && !serial_match) {
140 libusb_close(dev_handle);
141 continue;
142 }
143
144 /* Find the CMSIS-DAP string in product string */
145
146 bool cmsis_dap_in_product_str = false;
147 char product_string[256] = {0};
148 if (dev_desc.iProduct > 0) {
149 err = libusb_get_string_descriptor_ascii(
150 dev_handle, dev_desc.iProduct,
151 (uint8_t *)product_string, sizeof(product_string));
152 if (err < 0) {
153 LOG_WARNING("could not read product string for device 0x%04x:0x%04x: %s",
154 dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
155 } else if (strstr(product_string, "CMSIS-DAP")) {
156 LOG_DEBUG("found product string of 0x%04x:0x%04x '%s'",
157 dev_desc.idVendor, dev_desc.idProduct, product_string);
158 cmsis_dap_in_product_str = true;
159 }
160 }
161
162 bool device_identified_reliably = cmsis_dap_in_product_str
163 || serial_match || id_match;
164
165 /* Find the CMSIS-DAP interface */
166
167 for (int config = 0; config < dev_desc.bNumConfigurations; config++) {
168 struct libusb_config_descriptor *config_desc;
169 err = libusb_get_config_descriptor(dev, config, &config_desc);
170 if (err) {
171 LOG_ERROR("could not get configuration descriptor %d for device 0x%04x:0x%04x: %s",
172 config, dev_desc.idVendor, dev_desc.idProduct, libusb_strerror(err));
173 continue;
174 }
175
176 LOG_DEBUG("enumerating interfaces of 0x%04x:0x%04x",
177 dev_desc.idVendor, dev_desc.idProduct);
178 int config_num = config_desc->bConfigurationValue;
179 const struct libusb_interface_descriptor *intf_desc_candidate = NULL;
180 const struct libusb_interface_descriptor *intf_desc_found = NULL;
181
182 for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) {
183 const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0];
184 int interface_num = intf_desc->bInterfaceNumber;
185
186 /* Skip this interface if another one was requested explicitly */
187 if (cmsis_dap_usb_interface != -1 && cmsis_dap_usb_interface != interface_num)
188 continue;
189
190 /* CMSIS-DAP v2 spec says:
191 *
192 * CMSIS-DAP with default V2 configuration uses WinUSB and is therefore faster.
193 * Optionally support for streaming SWO trace is provided via an additional USB endpoint.
194 *
195 * The WinUSB configuration requires custom class support with the interface setting
196 * Class Code: 0xFF (Vendor specific)
197 * Subclass: 0x00
198 * Protocol code: 0x00
199 *
200 * Depending on the configuration it uses the following USB endpoints which should be configured
201 * in the interface descriptor in this order:
202 * - Endpoint 1: Bulk Out ā€“ used for commands received from host PC.
203 * - Endpoint 2: Bulk In ā€“ used for responses send to host PC.
204 * - Endpoint 3: Bulk In (optional) ā€“ used for streaming SWO trace (if enabled with SWO_STREAM).
205 */
206
207 /* Search for "CMSIS-DAP" in the interface string */
208 bool cmsis_dap_in_interface_str = false;
209 if (intf_desc->iInterface != 0) {
210
211 char interface_str[256] = {0};
212
213 err = libusb_get_string_descriptor_ascii(
214 dev_handle, intf_desc->iInterface,
215 (uint8_t *)interface_str, sizeof(interface_str));
216 if (err < 0) {
217 LOG_DEBUG("could not read interface string %d for device 0x%04x:0x%04x: %s",
218 intf_desc->iInterface,
219 dev_desc.idVendor, dev_desc.idProduct,
220 libusb_strerror(err));
221 } else if (strstr(interface_str, "CMSIS-DAP")) {
222 cmsis_dap_in_interface_str = true;
223 LOG_DEBUG("found interface %d string '%s'",
224 interface_num, interface_str);
225 }
226 }
227
228 /* Bypass the following check if this interface was explicitly requested. */
229 if (cmsis_dap_usb_interface == -1) {
230 if (!cmsis_dap_in_product_str && !cmsis_dap_in_interface_str)
231 continue;
232 }
233
234 /* check endpoints */
235 if (intf_desc->bNumEndpoints < 2) {
236 LOG_DEBUG("skipping interface %d, has only %d endpoints",
237 interface_num, intf_desc->bNumEndpoints);
238 continue;
239 }
240
241 if ((intf_desc->endpoint[0].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK ||
242 (intf_desc->endpoint[0].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_OUT) {
243 LOG_DEBUG("skipping interface %d, endpoint[0] is not bulk out",
244 interface_num);
245 continue;
246 }
247
248 if ((intf_desc->endpoint[1].bmAttributes & 3) != LIBUSB_TRANSFER_TYPE_BULK ||
249 (intf_desc->endpoint[1].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_IN) {
250 LOG_DEBUG("skipping interface %d, endpoint[1] is not bulk in",
251 interface_num);
252 continue;
253 }
254
255 /* We can rely on the interface is really CMSIS-DAP if
256 * - we've seen CMSIS-DAP in the interface string
257 * - config asked explicitly for an interface number
258 * - the device has only one interface
259 * The later two cases should be honored only if we know
260 * we are on the rigt device */
261 bool intf_identified_reliably = cmsis_dap_in_interface_str
262 || (device_identified_reliably &&
263 (cmsis_dap_usb_interface != -1
264 || config_desc->bNumInterfaces == 1));
265
266 if (intf_desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC ||
267 intf_desc->bInterfaceSubClass != 0 || intf_desc->bInterfaceProtocol != 0) {
268 /* If the interface is reliably identified
269 * then we need not insist on setting USB class, subclass and protocol
270 * exactly as the specification requires.
271 * At least KitProg3 uses class 0 contrary to the specification */
272 if (intf_identified_reliably) {
273 LOG_WARNING("Using CMSIS-DAPv2 interface %d with wrong class %" PRId8
274 " subclass %" PRId8 " or protocol %" PRId8,
275 interface_num,
276 intf_desc->bInterfaceClass,
277 intf_desc->bInterfaceSubClass,
278 intf_desc->bInterfaceProtocol);
279 } else {
280 LOG_DEBUG("skipping interface %d, class %" PRId8
281 " subclass %" PRId8 " protocol %" PRId8,
282 interface_num,
283 intf_desc->bInterfaceClass,
284 intf_desc->bInterfaceSubClass,
285 intf_desc->bInterfaceProtocol);
286 continue;
287
288 }
289 }
290
291 if (intf_identified_reliably) {
292 /* That's the one! */
293 intf_desc_found = intf_desc;
294 break;
295 }
296
297 if (!intf_desc_candidate && device_identified_reliably) {
298 /* This interface looks suitable for CMSIS-DAP. Store the pointer to it
299 * and keep searching for another one with CMSIS-DAP in interface string */
300 intf_desc_candidate = intf_desc;
301 }
302 }
303
304 if (!intf_desc_found) {
305 /* We were not able to identify reliably which interface is CMSIS-DAP.
306 * Let's use the first suitable if we found one */
307 intf_desc_found = intf_desc_candidate;
308 }
309
310 if (!intf_desc_found) {
311 libusb_free_config_descriptor(config_desc);
312 continue;
313 }
314
315 /* We've chosen an interface, connect to it */
316 int interface_num = intf_desc_found->bInterfaceNumber;
317 int packet_size = intf_desc_found->endpoint[0].wMaxPacketSize;
318 int ep_out = intf_desc_found->endpoint[0].bEndpointAddress;
319 int ep_in = intf_desc_found->endpoint[1].bEndpointAddress;
320
321 libusb_free_config_descriptor(config_desc);
322 libusb_free_device_list(device_list, true);
323
324 LOG_INFO("Using CMSIS-DAPv2 interface with VID:PID=0x%04x:0x%04x, serial=%s",
325 dev_desc.idVendor, dev_desc.idProduct, dev_serial);
326
327 int current_config;
328 err = libusb_get_configuration(dev_handle, &current_config);
329 if (err) {
330 LOG_ERROR("could not find current configuration: %s", libusb_strerror(err));
331 libusb_close(dev_handle);
332 libusb_exit(ctx);
333 return ERROR_FAIL;
334 }
335
336 if (config_num != current_config) {
337 err = libusb_set_configuration(dev_handle, config_num);
338 if (err) {
339 LOG_ERROR("could not set configuration: %s", libusb_strerror(err));
340 libusb_close(dev_handle);
341 libusb_exit(ctx);
342 return ERROR_FAIL;
343 }
344 }
345
346 err = libusb_claim_interface(dev_handle, interface_num);
347 if (err)
348 LOG_WARNING("could not claim interface: %s", libusb_strerror(err));
349
350 dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data));
351 if (dap->bdata == NULL) {
352 LOG_ERROR("unable to allocate memory");
353 libusb_release_interface(dev_handle, interface_num);
354 libusb_close(dev_handle);
355 libusb_exit(ctx);
356 return ERROR_FAIL;
357 }
358
359 dap->packet_size = packet_size + 1; /* "+ 1" for compatibility with the HID backend */
360 dap->bdata->usb_ctx = ctx;
361 dap->bdata->dev_handle = dev_handle;
362 dap->bdata->ep_out = ep_out;
363 dap->bdata->ep_in = ep_in;
364 dap->bdata->interface = interface_num;
365 return ERROR_OK;
366 }
367
368 libusb_close(dev_handle);
369 }
370
371 libusb_free_device_list(device_list, true);
372
373 libusb_exit(ctx);
374 return ERROR_FAIL;
375 }
376
377 static void cmsis_dap_usb_close(struct cmsis_dap *dap)
378 {
379 libusb_release_interface(dap->bdata->dev_handle, dap->bdata->interface);
380 libusb_close(dap->bdata->dev_handle);
381 libusb_exit(dap->bdata->usb_ctx);
382 free(dap->bdata);
383 dap->bdata = NULL;
384 }
385
386 static int cmsis_dap_usb_read(struct cmsis_dap *dap, int timeout_ms)
387 {
388 int transferred = 0;
389 int err;
390
391 err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_in,
392 dap->packet_buffer, dap->packet_size, &transferred, timeout_ms);
393 if (err) {
394 if (err == LIBUSB_ERROR_TIMEOUT) {
395 return ERROR_TIMEOUT_REACHED;
396 } else {
397 LOG_ERROR("error reading data: %s", libusb_strerror(err));
398 return ERROR_FAIL;
399 }
400 }
401
402 memset(&dap->packet_buffer[transferred], 0, dap->packet_size - transferred);
403
404 return transferred;
405 }
406
407 static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen, int timeout_ms)
408 {
409 int transferred = 0;
410 int err;
411
412 /* skip the first byte that is only used by the HID backend */
413 err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_out,
414 dap->packet_buffer + 1, txlen - 1, &transferred, timeout_ms);
415 if (err) {
416 if (err == LIBUSB_ERROR_TIMEOUT) {
417 return ERROR_TIMEOUT_REACHED;
418 } else {
419 LOG_ERROR("error writing data: %s", libusb_strerror(err));
420 return ERROR_FAIL;
421 }
422 }
423
424 return transferred;
425 }
426
427 COMMAND_HANDLER(cmsis_dap_handle_usb_interface_command)
428 {
429 if (CMD_ARGC == 1)
430 cmsis_dap_usb_interface = strtoul(CMD_ARGV[0], NULL, 10);
431 else
432 LOG_ERROR("expected exactly one argument to cmsis_dap_usb_interface <interface_number>");
433
434 return ERROR_OK;
435 }
436
437 const struct command_registration cmsis_dap_usb_subcommand_handlers[] = {
438 {
439 .name = "interface",
440 .handler = &cmsis_dap_handle_usb_interface_command,
441 .mode = COMMAND_CONFIG,
442 .help = "set the USB interface number to use (for USB bulk backend only)",
443 .usage = "<interface_number>",
444 },
445 COMMAND_REGISTRATION_DONE
446 };
447
448 const struct cmsis_dap_backend cmsis_dap_usb_backend = {
449 .name = "usb_bulk",
450 .open = cmsis_dap_usb_open,
451 .close = cmsis_dap_usb_close,
452 .read = cmsis_dap_usb_read,
453 .write = cmsis_dap_usb_write,
454 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)