/*
* 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