/* * This file is part of the Black Magic Debug project. * * Copyright (C) 2020 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Find all known serial connected debuggers */ #include "general.h" #include #include #include "bmp_hosted.h" #include "version.h" void bmp_ident(bmp_info_t *info) { PRINT_INFO("Black Magic Debug App (for BMP only) %s\n", FIRMWARE_VERSION); if (!info) return; PRINT_INFO("Using:\n %s %s %s\n", info->manufacturer, info->version, info->serial); } void libusb_exit_function(bmp_info_t *info) {(void)info;}; #ifdef __APPLE__ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) { DEBUG_WARN("Please implement find_debuggers for MACOS!\n"); (void)cl_opts; (void)info; return -1; } #elif defined(__WIN32__) || defined(__CYGWIN__) /* This source has been used as an example: * https://stackoverflow.com/questions/3438366/setupdigetdeviceproperty-usage-example */ #include #include #include // for MAX_DEVICE_ID_LEN, CM_Get_Parent and CM_Get_Device_ID #include #include /* include DEVPKEY_Device_BusReportedDeviceDesc from WinDDK\7600.16385.1\inc\api\devpropdef.h */ #ifdef DEFINE_DEVPROPKEY #undef DEFINE_DEVPROPKEY #endif #define DEFINE_DEVPROPKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) const DEVPROPKEY DECLSPEC_SELECTANY name = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid } /* include DEVPKEY_Device_BusReportedDeviceDesc from WinDDK\7600.16385.1\inc\api\devpkey.h */ DEFINE_DEVPROPKEY(DEVPKEY_Device_BusReportedDeviceDesc, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 4); // DEVPROP_TYPE_STRING /* List all USB devices with some additional information. * Unfortunately, this code is quite ugly. */ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) { unsigned i; DWORD dwSize; DEVPROPTYPE ulPropertyType; CONFIGRET status; HDEVINFO hDevInfo; SP_DEVINFO_DATA DeviceInfoData; TCHAR szDeviceInstanceID [MAX_DEVICE_ID_LEN]; WCHAR busReportedDeviceSesc[4096]; int probes_found = 0; bool is_printing_probes_info = cl_opts->opt_list_only != 0; info->bmp_type = BMP_TYPE_BMP; hDevInfo = SetupDiGetClassDevs (0, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); if (hDevInfo == INVALID_HANDLE_VALUE) return -1; print_probes_info: for (i = 0; ; i++) { char serial_number[sizeof info->serial]; DeviceInfoData.cbSize = sizeof (DeviceInfoData); if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData)) break; status = CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID , MAX_PATH, 0); if (status != CR_SUCCESS) continue; if (!sscanf(szDeviceInstanceID, "USB\\VID_1D50&PID_6018\\%s", serial_number)) continue; if (SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &ulPropertyType, (BYTE*)busReportedDeviceSesc, sizeof busReportedDeviceSesc, &dwSize, 0)) { probes_found ++; if (is_printing_probes_info) { DEBUG_WARN("%2d: %s, %ls\n", probes_found, serial_number, busReportedDeviceSesc); } else { bool probe_identified = true; if ((cl_opts->opt_serial && strstr(serial_number, cl_opts->opt_serial)) || (cl_opts->opt_position && cl_opts->opt_position == probes_found) || /* Special case for the very first probe found. */ (probe_identified = false, probes_found == 1)) { strncpy(info->serial, serial_number, sizeof info->serial); strncpy(info->manufacturer, "BMP", sizeof info->manufacturer); snprintf(info->product, sizeof info->product, "%ls", busReportedDeviceSesc); /* Don't bother to parse the version string. It is a part of the * product description string. It seems that at the moment it * is only being used to print a version string in response * to the 'monitor version' command, so it doesn't really matter * if the version string is printed as a part of the product string, * or as a separate string, the result is pretty much the same. */ info->version[0] = 0; if (probe_identified) return 0; } } } } if (is_printing_probes_info) return 1; if (probes_found == 1) /* Exactly one probe found. Its information has already been filled * in the detection loop, so use this probe. */ return 0; if (probes_found < 1) { DEBUG_WARN("No BMP probe found\n"); return -1; } /* Otherwise, if this line is reached, then more than one probe has been found, * and no probe was identified as selected by the user. * Restart the identification loop, this time printing the probe information, * and then return. */ DEBUG_WARN("%d debuggers found!\nSelect with -P , or " "-s <(partial)serial no.>\n", probes_found); probes_found = 0; is_printing_probes_info = true; goto print_probes_info; } #else /* Old ID: Black_Sphere_Technologies_Black_Magic_Probe_BFE4D6EC-if00 * Recent: Black_Sphere_Technologies_Black_Magic_Probe_v1.7.1-212-g212292ab_7BAE7AB8-if00 * usb-Black_Sphere_Technologies_Black_Magic_Probe__SWLINK__v1.7.1-155-gf55ad67b-dirty_DECB8811-if00 */ #define BMP_IDSTRING_BLACKSPHERE "usb-Black_Sphere_Technologies_Black_Magic_Probe" #define BMP_IDSTRING_BLACKMAGIC "usb-Black_Magic_Debug_Black_Magic_Probe" #define BMP_IDSTRING_1BITSQUARED "usb-1BitSquared_Black_Magic_Probe" #define DEVICE_BY_ID "/dev/serial/by-id/" /* * Extract type, version and serial from /dev/serial/by_id * Return 0 on success * * Old versions have different strings. Try to cope! */ static int scan_linux_id(char *name, char *type, char *version, char *serial) { name += strlen(BMP_IDSTRING_BLACKSPHERE) + 1; while (*name == '_') name++; if (!*name) { DEBUG_WARN("Unexpected end\n"); return -1; } char *p = name; char *delims[4] = {0,0,0,0}; int underscores = 0; while (*p) { if (*p == '_') { while (p[1] == '_') p++; /* remove multiple underscores */ if (underscores > 2) return -1; delims[underscores] = p; underscores ++; } p++; } if (underscores == 0) { /* Old BMP native */ int res; res = sscanf(name, "%8s-if00", serial); if (res != 1) return -1; strcpy(type, "Native"); strcpy(version, "Unknown"); } else if (underscores == 2) { strncpy(type, name, delims[0] - name - 1); strncpy(version, delims[0] + 1, delims[1] - delims[0] - 1); int res = sscanf(delims[1] + 1, "%8s-if00", serial); if (!res) return -1; } else { int res = sscanf(delims[0] + 1, "%8s-if00", serial); if (!res) return -1; if (name[0] == 'v') { strcpy(type, "Unknown"); strncpy(version, name, delims[0] - name - 1); } else { strncpy(type, name, delims[0] - name); strcpy(type, "Unknown"); } } return 0; } int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) { if (cl_opts->opt_device) return 1; info->bmp_type = BMP_TYPE_BMP; DIR *dir = opendir(DEVICE_BY_ID); if (!dir) /* No serial device connected!*/ return 0; int found_bmps = 0; struct dirent *dp; int i = 0; while ((dp = readdir(dir)) != NULL) { if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) || strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) || strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) && (strstr(dp->d_name, "-if00"))) { i++; char type[256], version[256], serial[256]; if (scan_linux_id(dp->d_name, type, version, serial)) { DEBUG_WARN("Unexpected device name found \"%s\"\n", dp->d_name); } if ((cl_opts->opt_serial && strstr(serial, cl_opts->opt_serial)) || (cl_opts->opt_position && cl_opts->opt_position == i)) { /* With serial number given and partial match, we are done!*/ strncpy(info->serial, serial, sizeof(info->serial)); int res = snprintf(info->manufacturer, sizeof(info->manufacturer), "Black Magic Probe (%s)", type); if (res) DEBUG_WARN("Overflow\n"); strncpy(info->version, version, sizeof(info->version)); found_bmps = 1; break; } else { found_bmps++; } } } closedir(dir); if (found_bmps < 1) { DEBUG_WARN("No BMP probe found\n"); return -1; } else if ((found_bmps > 1) || cl_opts->opt_list_only) { DEBUG_WARN("Available Probes:\n"); } dir = opendir(DEVICE_BY_ID); i = 0; while ((dp = readdir(dir)) != NULL) { if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) || strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) || strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) && (strstr(dp->d_name, "-if00"))) { i++; char type[256], version[256], serial[256]; if (scan_linux_id(dp->d_name, type, version, serial)) { DEBUG_WARN("Unexpected device name found \"%s\"\n", dp->d_name); } else if ((found_bmps == 1) && (!cl_opts->opt_list_only)) { strncpy(info->serial, serial, sizeof(info->serial)); found_bmps = 1; strncpy(info->serial, serial, sizeof(info->serial)); snprintf(info->manufacturer, sizeof(info->manufacturer), "Black Magic Probe (%s)", type); strncpy(info->version, version, sizeof(info->version)); break; } else if (found_bmps > 0) { DEBUG_WARN("%2d: %s, Black Magic Debug, Black Magic " "Probe (%s), %s\n", i, serial, type, version); } } } closedir(dir); return (found_bmps == 1 && !cl_opts->opt_list_only) ? 0 : 1; } #endif