mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	bluez5: parse bluez vendor/product ids
It seems few devices support the Device Id via bluez. Try to figure out vendor/product ids for usb devices also via sysfs. Also try to figure out the adapter bus type.
This commit is contained in:
		
							parent
							
								
									6c582d5445
								
							
						
					
					
						commit
						2c9764da1d
					
				
					 2 changed files with 157 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -524,6 +524,38 @@ static bool check_iter_signature(DBusMessageIter *it, const char *sig)
 | 
			
		|||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_modalias(const char *modalias, uint16_t *source, uint16_t *vendor,
 | 
			
		||||
		uint16_t *product, uint16_t *version)
 | 
			
		||||
{
 | 
			
		||||
	char *pos;
 | 
			
		||||
	unsigned int src, i, j, k;
 | 
			
		||||
 | 
			
		||||
	if (strncmp(modalias, "bluetooth:", strlen("bluetooth:")) == 0)
 | 
			
		||||
		src = SOURCE_ID_BLUETOOTH;
 | 
			
		||||
	else if (strncmp(modalias, "usb:", strlen("usb:")) == 0)
 | 
			
		||||
		src = SOURCE_ID_USB;
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	pos = strchr(modalias, ':');
 | 
			
		||||
	if (pos == NULL)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (sscanf(pos + 1, "v%04Xp%04Xd%04X", &i, &j, &k) != 3)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Ignore BlueZ placeholder value */
 | 
			
		||||
	if (src == SOURCE_ID_USB && i == 0x1d6b && j == 0x0246)
 | 
			
		||||
		return -ENXIO;
 | 
			
		||||
 | 
			
		||||
	*source = src;
 | 
			
		||||
	*vendor = i;
 | 
			
		||||
	*product = j;
 | 
			
		||||
	*version = k;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int adapter_update_props(struct spa_bt_adapter *adapter,
 | 
			
		||||
				DBusMessageIter *props_iter,
 | 
			
		||||
				DBusMessageIter *invalidated_iter)
 | 
			
		||||
| 
						 | 
				
			
			@ -561,6 +593,14 @@ static int adapter_update_props(struct spa_bt_adapter *adapter,
 | 
			
		|||
				free(adapter->address);
 | 
			
		||||
				adapter->address = strdup(value);
 | 
			
		||||
			}
 | 
			
		||||
			else if (spa_streq(key, "Modalias")) {
 | 
			
		||||
				int ret;
 | 
			
		||||
				ret = parse_modalias(value, &adapter->source_id, &adapter->vendor_id,
 | 
			
		||||
						&adapter->product_id, &adapter->version_id);
 | 
			
		||||
				if (ret < 0)
 | 
			
		||||
					spa_log_debug(monitor->log, "adapter %p: %s=%s ignored: %s",
 | 
			
		||||
							adapter, key, value, spa_strerror(ret));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (type == DBUS_TYPE_UINT32) {
 | 
			
		||||
			uint32_t value;
 | 
			
		||||
| 
						 | 
				
			
			@ -616,6 +656,63 @@ static int adapter_update_props(struct spa_bt_adapter *adapter,
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int adapter_init_bus_type(struct spa_bt_monitor *monitor, struct spa_bt_adapter *d)
 | 
			
		||||
{
 | 
			
		||||
	char path[1024], buf[1024];
 | 
			
		||||
	const char *str;
 | 
			
		||||
	ssize_t res = -EINVAL;
 | 
			
		||||
 | 
			
		||||
	d->bus_type = BUS_TYPE_OTHER;
 | 
			
		||||
 | 
			
		||||
	str = strrchr(d->path, '/');  /* hciXX */
 | 
			
		||||
	if (str == NULL)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	snprintf(path, sizeof(path), "/sys/class/bluetooth/%s/device/subsystem", str);
 | 
			
		||||
	if ((res = readlink(path, buf, sizeof(buf)-1)) < 0)
 | 
			
		||||
		return -errno;
 | 
			
		||||
	buf[res] = '\0';
 | 
			
		||||
 | 
			
		||||
	str = strrchr(buf, '/');
 | 
			
		||||
	if (str && spa_streq(str, "/usb"))
 | 
			
		||||
		d->bus_type = BUS_TYPE_USB;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int adapter_init_modalias(struct spa_bt_monitor *monitor, struct spa_bt_adapter *d)
 | 
			
		||||
{
 | 
			
		||||
	char path[1024];
 | 
			
		||||
	FILE *f = NULL;
 | 
			
		||||
	int vendor_id, product_id;
 | 
			
		||||
	const char *str;
 | 
			
		||||
	int res = -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Lookup vendor/product id for the device, if present */
 | 
			
		||||
	str = strrchr(d->path, '/');  /* hciXX */
 | 
			
		||||
	if (str == NULL)
 | 
			
		||||
		goto fail;
 | 
			
		||||
	snprintf(path, sizeof(path), "/sys/class/bluetooth/%s/device/modalias", str);
 | 
			
		||||
	if ((f = fopen(path, "rb")) == NULL) {
 | 
			
		||||
		res = -errno;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
	if (fscanf(f, "usb:v%04Xp%04X",  &vendor_id, &product_id) != 2)
 | 
			
		||||
		goto fail;
 | 
			
		||||
	d->source_id = SOURCE_ID_USB;
 | 
			
		||||
	d->vendor_id = vendor_id;
 | 
			
		||||
	d->product_id = product_id;
 | 
			
		||||
	fclose(f);
 | 
			
		||||
 | 
			
		||||
	spa_log_debug(monitor->log, "adapter %p: usb vendor:%04x product:%04x",
 | 
			
		||||
			d, vendor_id, product_id);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
	if (f)
 | 
			
		||||
		fclose(f);
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct spa_bt_adapter *adapter_create(struct spa_bt_monitor *monitor, const char *path)
 | 
			
		||||
{
 | 
			
		||||
	struct spa_bt_adapter *d;
 | 
			
		||||
| 
						 | 
				
			
			@ -629,6 +726,9 @@ static struct spa_bt_adapter *adapter_create(struct spa_bt_monitor *monitor, con
 | 
			
		|||
 | 
			
		||||
	spa_list_prepend(&monitor->adapter_list, &d->link);
 | 
			
		||||
 | 
			
		||||
	adapter_init_bus_type(monitor, d);
 | 
			
		||||
	adapter_init_modalias(monitor, d);
 | 
			
		||||
 | 
			
		||||
	return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -745,12 +845,33 @@ static void device_free(struct spa_bt_device *device)
 | 
			
		|||
	free(device);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int spa_bt_format_vendor_product_id(uint16_t source_id, uint16_t vendor_id, uint16_t product_id,
 | 
			
		||||
		char *vendor_str, int vendor_str_size, char *product_str, int product_str_size)
 | 
			
		||||
{
 | 
			
		||||
	char *source_str;
 | 
			
		||||
 | 
			
		||||
	switch (source_id) {
 | 
			
		||||
	case SOURCE_ID_USB:
 | 
			
		||||
		source_str = "usb";
 | 
			
		||||
		break;
 | 
			
		||||
	case SOURCE_ID_BLUETOOTH:
 | 
			
		||||
		source_str = "bluetooth";
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spa_scnprintf(vendor_str, vendor_str_size, "%s:%04x", source_str, (unsigned int)vendor_id);
 | 
			
		||||
	spa_scnprintf(product_str, product_str_size, "%04x", (unsigned int)product_id);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void emit_device_info(struct spa_bt_monitor *monitor,
 | 
			
		||||
		struct spa_bt_device *device, bool with_connection)
 | 
			
		||||
{
 | 
			
		||||
	struct spa_device_object_info info;
 | 
			
		||||
	char dev[32], name[128], class[16];
 | 
			
		||||
	struct spa_dict_item items[20];
 | 
			
		||||
	char dev[32], name[128], class[16], vendor_id[64], product_id[64], product_id_tot[67];
 | 
			
		||||
	struct spa_dict_item items[23];
 | 
			
		||||
	uint32_t n_items = 0;
 | 
			
		||||
 | 
			
		||||
	info = SPA_DEVICE_OBJECT_INFO_INIT();
 | 
			
		||||
| 
						 | 
				
			
			@ -767,6 +888,13 @@ static void emit_device_info(struct spa_bt_monitor *monitor,
 | 
			
		|||
	items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_NAME, name);
 | 
			
		||||
	items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_DESCRIPTION, device->alias);
 | 
			
		||||
	items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ALIAS, device->name);
 | 
			
		||||
	if (spa_bt_format_vendor_product_id(
 | 
			
		||||
				device->source_id, device->vendor_id, device->product_id,
 | 
			
		||||
				vendor_id, sizeof(vendor_id), product_id, sizeof(product_id)) == 0) {
 | 
			
		||||
		snprintf(product_id_tot, sizeof(product_id_tot), "0x%s", product_id);
 | 
			
		||||
		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, vendor_id);
 | 
			
		||||
		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, product_id_tot);
 | 
			
		||||
	}
 | 
			
		||||
	items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_FORM_FACTOR,
 | 
			
		||||
			spa_bt_form_factor_name(
 | 
			
		||||
				spa_bt_form_factor_from_class(device->bluetooth_class)));
 | 
			
		||||
| 
						 | 
				
			
			@ -1135,6 +1263,14 @@ static int device_update_props(struct spa_bt_device *device,
 | 
			
		|||
				free(device->icon);
 | 
			
		||||
				device->icon = strdup(value);
 | 
			
		||||
			}
 | 
			
		||||
			else if (spa_streq(key, "Modalias")) {
 | 
			
		||||
				int ret;
 | 
			
		||||
				ret = parse_modalias(value, &device->source_id, &device->vendor_id,
 | 
			
		||||
						&device->product_id, &device->version_id);
 | 
			
		||||
				if (ret < 0)
 | 
			
		||||
					spa_log_debug(monitor->log, "device %p: %s=%s ignored: %s",
 | 
			
		||||
							device, key, value, spa_strerror(ret));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (type == DBUS_TYPE_UINT32) {
 | 
			
		||||
			uint32_t value;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -154,6 +154,12 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
#define HSP_HS_DEFAULT_CHANNEL  3
 | 
			
		||||
 | 
			
		||||
#define SOURCE_ID_BLUETOOTH	0x1	/* Bluetooth SIG */
 | 
			
		||||
#define SOURCE_ID_USB		0x2	/* USB Implementer's Forum */
 | 
			
		||||
 | 
			
		||||
#define BUS_TYPE_USB		1
 | 
			
		||||
#define BUS_TYPE_OTHER		255
 | 
			
		||||
 | 
			
		||||
#define HFP_AUDIO_CODEC_CVSD    0x01
 | 
			
		||||
#define HFP_AUDIO_CODEC_MSBC    0x02
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +212,10 @@ static inline enum spa_bt_profile spa_bt_profile_from_uuid(const char *uuid)
 | 
			
		|||
}
 | 
			
		||||
int spa_bt_profiles_from_json_array(const char *str);
 | 
			
		||||
 | 
			
		||||
int spa_bt_format_vendor_product_id(uint16_t source_id, uint16_t vendor_id,
 | 
			
		||||
		uint16_t product_id, char *vendor_str, int vendor_str_size,
 | 
			
		||||
		char *product_str, int product_str_size);
 | 
			
		||||
 | 
			
		||||
enum spa_bt_hfp_ag_feature {
 | 
			
		||||
	SPA_BT_HFP_AG_FEATURE_NONE =			(0),
 | 
			
		||||
	SPA_BT_HFP_AG_FEATURE_3WAY =			(1 << 0),
 | 
			
		||||
| 
						 | 
				
			
			@ -290,6 +300,11 @@ struct spa_bt_adapter {
 | 
			
		|||
	char *alias;
 | 
			
		||||
	char *address;
 | 
			
		||||
	char *name;
 | 
			
		||||
	int bus_type;
 | 
			
		||||
	uint16_t source_id;
 | 
			
		||||
	uint16_t vendor_id;
 | 
			
		||||
	uint16_t product_id;
 | 
			
		||||
	uint16_t version_id;
 | 
			
		||||
	uint32_t bluetooth_class;
 | 
			
		||||
	uint32_t profiles;
 | 
			
		||||
	int powered;
 | 
			
		||||
| 
						 | 
				
			
			@ -405,6 +420,10 @@ struct spa_bt_device {
 | 
			
		|||
	char *battery_path;
 | 
			
		||||
	char *name;
 | 
			
		||||
	char *icon;
 | 
			
		||||
	uint16_t source_id;
 | 
			
		||||
	uint16_t vendor_id;
 | 
			
		||||
	uint16_t product_id;
 | 
			
		||||
	uint16_t version_id;
 | 
			
		||||
	uint32_t bluetooth_class;
 | 
			
		||||
	uint16_t appearance;
 | 
			
		||||
	uint16_t RSSI;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue