#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include "general.h"
#include "bmp_hosted.h"
#include "probe_info.h"
#include "utils.h"
#include "version.h"
void bmp_ident(bmda_probe_s *info)
{
DEBUG_INFO("Black Magic Debug App (for BMP only) %s\n", FIRMWARE_VERSION);
if (!info)
return;
DEBUG_INFO("Using:\n %s %s %s\n", info->manufacturer, info->version, info->serial);
}
void libusb_exit_function(bmda_probe_s *info)
{
(void)info;
};
#ifdef __APPLE__
int find_debuggers(bmda_cli_options_s *cl_opts, bmda_probe_s *info)
{
DEBUG_ERROR("Please implement find_debuggers for MACOS!\n");
(void)cl_opts;
(void)info;
return -1;
}
#elif defined(__WIN32__) || defined(__CYGWIN__)
#include <windows.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <tchar.h>
#include <stdio.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}
DEFINE_DEVPROPKEY(DEVPKEY_Device_BusReportedDeviceDesc, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c,
0xbd, 0xa2, 4);
int find_debuggers(bmda_cli_options_s *cl_opts, bmda_probe_s *info)
{
unsigned i;
DWORD dwSize;
DEVPROPTYPE ulPropertyType;
CONFIGRET status;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
TCHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
WCHAR busReportedDeviceSesc[4096];
size_t probes_found = 0;
bool is_printing_probes_info = cl_opts->opt_list_only != 0;
info->type = PROBE_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("%2zu: %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) ||
(probe_identified = false, probes_found == 1U)) {
strncpy(info->serial, serial_number, sizeof info->serial);
strncpy(info->manufacturer, "BMP", sizeof info->manufacturer);
snprintf(info->product, sizeof info->product, "%ls", busReportedDeviceSesc);
info->version[0] = 0;
if (probe_identified)
return 0;
}
}
}
}
if (is_printing_probes_info)
return 1;
if (probes_found == 1U)
return 0;
if (probes_found < 1U) {
DEBUG_ERROR("No BMP probe found\n");
return -1;
}
DEBUG_WARN("%zu debuggers found!\nSelect with -P <pos>, or "
"-s <(partial)serial no.>\n",
probes_found);
probes_found = 0;
is_printing_probes_info = true;
goto print_probes_info;
}
#else
#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"
#define BMP_PRODUCT_STRING "Black Magic Probe"
typedef struct dirent dirent_s;
typedef enum scan_mode {
SCAN_FIND,
SCAN_LIST
} scan_mode_e;
size_t find_prefix_length(const char *name, const size_t name_len)
{
if (begins_with(name, name_len, BMP_IDSTRING_BLACKSPHERE))
return sizeof(BMP_IDSTRING_BLACKSPHERE);
if (begins_with(name, name_len, BMP_IDSTRING_BLACKMAGIC))
return sizeof(BMP_IDSTRING_BLACKMAGIC);
if (begins_with(name, name_len, BMP_IDSTRING_1BITSQUARED))
return sizeof(BMP_IDSTRING_1BITSQUARED);
return 0;
}
char *extract_serial(const char *const device, const size_t length)
{
const char *const last_underscore = strrchr(device, '_');
if (!last_underscore)
return NULL;
const char *const begin = last_underscore + 1;
const char *const end = device + length - 5;
const size_t result_length = end - begin;
char *const result = (char *)malloc(result_length);
memcpy(result, begin, result_length);
result[result_length - 1U] = '\0';
return result;
}
static probe_info_s *parse_device_node(const char *name, probe_info_s *probe_list)
{
const size_t name_len = strlen(name) + 1U;
size_t prefix_length = find_prefix_length(name, name_len);
while (name[prefix_length] == '_' && prefix_length < name_len)
++prefix_length;
if (prefix_length == name_len) {
DEBUG_ERROR("Unexpected end\n");
return probe_list;
}
size_t offsets[3][2] = {{prefix_length, 0U}, {0U, 0U}};
size_t underscores = 0;
for (size_t offset = prefix_length; offset < name_len; ++offset) {
if (name[offset] == '_') {
offsets[underscores][1] = offset - offsets[underscores][0];
++underscores;
if (underscores > 2U)
return probe_list;
while (name[offset + 1U] == '_' && offset < name_len)
++offset;
if (offset == name_len)
break;
offsets[underscores][0] = offset;
}
}
offsets[underscores][1] = name_len - offsets[underscores][0];
char *const serial = extract_serial(name, name_len);
if (!serial)
return probe_list;
char *version = NULL;
char *type = NULL;
char *product = strdup(BMP_PRODUCT_STRING);
if (!product) {
free(serial);
return probe_list;
}
if (underscores == 0) {
version = strdup("Unknown");
type = strdup("Native");
} else if (underscores == 2) {
version = strndup(name + offsets[0][0], offsets[0][1]);
type = strndup(name + offsets[1][0], offsets[1][1]);
} else {
if (name[prefix_length] == 'v') {
version = strndup(name + offsets[0][0], offsets[0][1]);
type = strdup("Native");
} else {
version = strdup("Unknown");
type = strndup(name + offsets[0][0], offsets[0][1]);
}
}
if (!version || !type) {
DEBUG_ERROR("Failed to construct version of type string");
free(serial);
free(version);
free(type);
free(product);
return probe_list;
}
return probe_info_add_by_serial(probe_list, PROBE_TYPE_BMP, type, product, serial, version);
}
static const probe_info_s *scan_for_devices(void)
{
DIR *dir = opendir(DEVICE_BY_ID);
if (!dir)
return NULL;
probe_info_s *probe_list = NULL;
while (true) {
const dirent_s *const entry = readdir(dir);
if (entry == NULL)
break;
if (device_is_bmp_gdb_port(entry->d_name)) {
probe_info_s *probe_info = parse_device_node(entry->d_name, probe_list);
if (!probe_info) {
probe_info_list_free(probe_list);
probe_list = NULL;
break;
}
if (probe_info == probe_list)
DEBUG_ERROR("Error parsing device name \"%s\"\n", entry->d_name);
probe_list = probe_info;
}
}
closedir(dir);
return probe_info_correct_order(probe_list);
}
int find_debuggers(bmda_cli_options_s *cl_opts, bmda_probe_s *info)
{
if (cl_opts->opt_device)
return 1;
const probe_info_s *const probe_list = scan_for_devices();
if (!probe_list) {
DEBUG_ERROR("No BMP probe found\n");
return -1;
}
const size_t probes = probe_info_count(probe_list);
const probe_info_s *probe = NULL;
if (probes == 1U && !cl_opts->opt_serial && !cl_opts->opt_position)
probe = probe_list;
else
probe = probe_info_filter(probe_list, cl_opts->opt_serial, cl_opts->opt_position);
if (!probe || cl_opts->opt_list_only) {
DEBUG_WARN("Available Probes:\n");
probe = probe_list;
for (size_t position = 1U; probe; probe = probe->next, ++position)
DEBUG_WARN(
"%2zu: %s, Black Magic Debug, %s, %s\n", position, probe->serial, probe->manufacturer, probe->version);
probe_info_list_free(probe_list);
return 1; }
probe_info_to_bmda_probe(probe, info);
probe_info_list_free(probe_list);
return 0; }
#endif