@@ -7,3 +7,16 @@ Description: This write-only attribute allows users to trigger the vendor reset
The reset may or may not be done through the device transport
(e.g., UART/USB), and can also be done through an out-of-band
approach such as GPIO.
+
+What: /sys/class/bluetooth/btusb<usb-intf>/isoc_alt
+Date: 13-Feb-2025
+KernelVersion: 6.13
+Contact: linux-bluetooth@vger.kernel.org
+Description: This attribute allows users to configure the USB Alternate setting
+ for the specific HCI device. Reading this attribute returns the
+ current setting, and writing any supported numbers would change
+ the setting. See the USB Alternate setting definition in Bluetooth
+ core spec 5, vol 4, part B, table 2.1.
+ If the HCI device is not yet init-ed, the write fails with -ENODEV.
+ If the data is not a valid number, the write fails with -EINVAL.
+ The other failures are vendor specific.
@@ -920,6 +920,8 @@ struct btusb_data {
int oob_wake_irq; /* irq for out-of-band wake-on-bt */
struct qca_dump_info qca_dump;
+
+ struct device dev;
};
static void btusb_reset(struct hci_dev *hdev)
@@ -3682,7 +3684,7 @@ static ssize_t isoc_alt_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct btusb_data *data = dev_get_drvdata(dev);
+ struct btusb_data *data = container_of(dev, struct btusb_data, dev);
return sysfs_emit(buf, "%d\n", data->isoc_altsetting);
}
@@ -3691,10 +3693,13 @@ static ssize_t isoc_alt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct btusb_data *data = dev_get_drvdata(dev);
+ struct btusb_data *data = container_of(dev, struct btusb_data, dev);
int alt;
int ret;
+ if (!data->hdev)
+ return -ENODEV;
+
if (kstrtoint(buf, 10, &alt))
return -EINVAL;
@@ -3704,6 +3709,36 @@ static ssize_t isoc_alt_store(struct device *dev,
static DEVICE_ATTR_RW(isoc_alt);
+static struct attribute *btusb_sysfs_attrs[] = {
+ NULL,
+};
+ATTRIBUTE_GROUPS(btusb_sysfs);
+
+static void btusb_sysfs_release(struct device *dev)
+{
+ struct btusb_data *data = container_of(dev, struct btusb_data, dev);
+
+ kfree(data);
+}
+
+static const struct device_type btusb_sysfs = {
+ .name = "btusb",
+ .release = btusb_sysfs_release,
+ .groups = btusb_sysfs_groups,
+};
+
+static struct attribute *btusb_sysfs_isoc_alt_attrs[] = {
+ &dev_attr_isoc_alt.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(btusb_sysfs_isoc_alt);
+
+static const struct device_type btusb_sysfs_isoc_alt = {
+ .name = "btusb",
+ .release = btusb_sysfs_release,
+ .groups = btusb_sysfs_isoc_alt_groups,
+};
+
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -3745,7 +3780,7 @@ static int btusb_probe(struct usb_interface *intf,
return -ENODEV;
}
- data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -3768,8 +3803,10 @@ static int btusb_probe(struct usb_interface *intf,
}
}
- if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep)
- return -ENODEV;
+ if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
+ err = -ENODEV;
+ goto out_free_data;
+ }
if (id->driver_info & BTUSB_AMP) {
data->cmdreq_type = USB_TYPE_CLASS | 0x01;
@@ -3823,16 +3860,46 @@ static int btusb_probe(struct usb_interface *intf,
data->recv_acl = hci_recv_frame;
+ if (id->driver_info & BTUSB_AMP) {
+ /* AMP controllers do not support SCO packets */
+ data->isoc = NULL;
+ } else {
+ /* Interface orders are hardcoded in the specification */
+ data->isoc = usb_ifnum_to_if(data->udev, ifnum_base + 1);
+ data->isoc_ifnum = ifnum_base + 1;
+ }
+
+ if (id->driver_info & BTUSB_BROKEN_ISOC)
+ data->isoc = NULL;
+
+ /* Init a dev for btusb. The attr depends on the support of isoc. */
+ if (data->isoc)
+ data->dev.type = &btusb_sysfs_isoc_alt;
+ else
+ data->dev.type = &btusb_sysfs;
+ data->dev.class = &bt_class;
+ data->dev.parent = &intf->dev;
+
+ err = dev_set_name(&data->dev, "btusb%s", dev_name(&intf->dev));
+ if (err)
+ goto out_free_data;
+
+ err = device_register(&data->dev);
+ if (err < 0)
+ goto out_put_sysfs;
+
hdev = hci_alloc_dev_priv(priv_size);
- if (!hdev)
- return -ENOMEM;
+ if (!hdev) {
+ err = -ENOMEM;
+ goto out_free_sysfs;
+ }
hdev->bus = HCI_USB;
hci_set_drvdata(hdev, data);
data->hdev = hdev;
- SET_HCIDEV_DEV(hdev, &intf->dev);
+ SET_HCIDEV_DEV(hdev, &data->dev);
reset_gpio = gpiod_get_optional(&data->udev->dev, "reset",
GPIOD_OUT_LOW);
@@ -3971,15 +4038,6 @@ static int btusb_probe(struct usb_interface *intf,
hci_set_msft_opcode(hdev, 0xFD70);
}
- if (id->driver_info & BTUSB_AMP) {
- /* AMP controllers do not support SCO packets */
- data->isoc = NULL;
- } else {
- /* Interface orders are hardcoded in the specification */
- data->isoc = usb_ifnum_to_if(data->udev, ifnum_base + 1);
- data->isoc_ifnum = ifnum_base + 1;
- }
-
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_RTL) &&
(id->driver_info & BTUSB_REALTEK)) {
btrtl_set_driver_name(hdev, btusb_driver.name);
@@ -4012,9 +4070,6 @@ static int btusb_probe(struct usb_interface *intf,
set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
}
- if (id->driver_info & BTUSB_BROKEN_ISOC)
- data->isoc = NULL;
-
if (id->driver_info & BTUSB_WIDEBAND_SPEECH)
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
@@ -4067,10 +4122,6 @@ static int btusb_probe(struct usb_interface *intf,
data->isoc, data);
if (err < 0)
goto out_free_dev;
-
- err = device_create_file(&intf->dev, &dev_attr_isoc_alt);
- if (err)
- goto out_free_dev;
}
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) {
@@ -4101,6 +4152,16 @@ static int btusb_probe(struct usb_interface *intf,
if (data->reset_gpio)
gpiod_put(data->reset_gpio);
hci_free_dev(hdev);
+
+out_free_sysfs:
+ device_del(&data->dev);
+
+out_put_sysfs:
+ put_device(&data->dev);
+ return err;
+
+out_free_data:
+ kfree(data);
return err;
}
@@ -4117,10 +4178,8 @@ static void btusb_disconnect(struct usb_interface *intf)
hdev = data->hdev;
usb_set_intfdata(data->intf, NULL);
- if (data->isoc) {
- device_remove_file(&intf->dev, &dev_attr_isoc_alt);
+ if (data->isoc)
usb_set_intfdata(data->isoc, NULL);
- }
if (data->diag)
usb_set_intfdata(data->diag, NULL);
@@ -4152,6 +4211,7 @@ static void btusb_disconnect(struct usb_interface *intf)
gpiod_put(data->reset_gpio);
hci_free_dev(hdev);
+ device_unregister(&data->dev);
}
#ifdef CONFIG_PM
@@ -1843,6 +1843,7 @@ int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev);
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
+extern const struct class bt_class;
void hci_init_sysfs(struct hci_dev *hdev);
void hci_conn_init_sysfs(struct hci_conn *conn);
void hci_conn_add_sysfs(struct hci_conn *conn);
@@ -6,9 +6,10 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-static const struct class bt_class = {
+const struct class bt_class = {
.name = "bluetooth",
};
+EXPORT_SYMBOL_GPL(bt_class);
static void bt_link_release(struct device *dev)
{