diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000..60d318f805ff1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + checks: write + pull-requests: write + +jobs: + ci: + runs-on: ubuntu-latest + name: CI for Pull Request + steps: + - name: Checkout the source code + uses: actions/checkout@v3 + with: + path: src/src + + - name: CI + uses: bluez/action-ci@main + with: + task: ci + base_folder: src + space: kernel + github_token: ${{ secrets.GITHUB_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000000000..5e95af92ab1ab --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,44 @@ +name: Sync + +on: + schedule: + - cron: "*/5 * * * *" + +jobs: + sync_repo: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: master + + - name: Sync Repo + uses: bluez/action-ci@main + with: + task: sync + workflow: workflow + upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git' + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Cleanup PR + uses: bluez/action-ci@main + with: + task: cleanup + github_token: ${{ secrets.ACTION_TOKEN }} + + sync_patchwork: + needs: sync_repo + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Sync Patchwork + uses: bluez/action-ci@main + with: + task: patchwork + workflow: workflow + space: kernel + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 84614e60d142f..ffa2d6053b327 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -27,8 +27,9 @@ #include "btbcm.h" #include "btrtl.h" #include "btmtk.h" +#include "hci_uart.h" -#define VERSION "0.8" +#define VERSION "1.0" static bool disable_scofix; static bool force_scofix; @@ -983,6 +984,9 @@ struct btqca_data { #define BTUSB_ALT6_CONTINUOUS_TX 16 #define BTUSB_HW_SSR_ACTIVE 17 +#define BTUSB_PROTO_LEGACY 0x00 +#define BTUSB_PROTO_H4 0x01 + struct btusb_data { struct hci_dev *hdev; struct usb_device *udev; @@ -1016,6 +1020,7 @@ struct btusb_data { struct sk_buff *evt_skb; struct sk_buff *acl_skb; struct sk_buff *sco_skb; + struct sk_buff *rx_skb; struct usb_endpoint_descriptor *intr_ep; struct usb_endpoint_descriptor *bulk_tx_ep; @@ -1029,6 +1034,7 @@ struct btusb_data { __u8 cmdreq_type; __u8 cmdreq; + __u8 proto; unsigned int sco_num; unsigned int air_mode; @@ -1256,6 +1262,11 @@ static inline void btusb_free_frags(struct btusb_data *data) dev_kfree_skb_irq(data->sco_skb); data->sco_skb = NULL; + /* rx_skb may hold an ERR_PTR from a previous h4_recv_skb() call */ + if (!IS_ERR(data->rx_skb)) + dev_kfree_skb_irq(data->rx_skb); + data->rx_skb = NULL; + spin_unlock_irqrestore(&data->rxlock, flags); } @@ -1355,12 +1366,41 @@ static int btusb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb) return 0; } +/* Dispatch through the btusb_recv_* wrappers so that vendor specific + * handling (data->recv_event, data->recv_acl) is preserved in H:4 mode. + */ +static const struct h4_recv_pkt btusb_recv_pkts[] = { + { H4_RECV_ACL, .recv = btusb_recv_acl }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = btusb_recv_event }, + { H4_RECV_ISO, .recv = hci_recv_frame }, +}; + +static int btusb_recv_h4(struct btusb_data *data, void *buffer, int count) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&data->rxlock, flags); + data->rx_skb = h4_recv_skb(data->hdev, NULL, NULL, data->rx_skb, buffer, + count, btusb_recv_pkts, + ARRAY_SIZE(btusb_recv_pkts)); + if (IS_ERR(data->rx_skb)) + err = PTR_ERR(data->rx_skb); + spin_unlock_irqrestore(&data->rxlock, flags); + + return err; +} + static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) { struct sk_buff *skb; unsigned long flags; int err = 0; + if (data->proto == BTUSB_PROTO_H4) + return btusb_recv_h4(data, buffer, count); + spin_lock_irqsave(&data->rxlock, flags); skb = data->acl_skb; @@ -2038,12 +2078,14 @@ static int btusb_open(struct hci_dev *hdev) data->intf->needs_remote_wakeup = 1; - if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) - goto done; + if (data->proto == BTUSB_PROTO_LEGACY) { + if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) + goto done; - err = btusb_submit_intr_urb(hdev, GFP_KERNEL); - if (err < 0) - goto failed; + err = btusb_submit_intr_urb(hdev, GFP_KERNEL); + if (err < 0) + goto failed; + } err = btusb_submit_bulk_urb(hdev, GFP_KERNEL); if (err < 0) { @@ -2094,18 +2136,24 @@ static int btusb_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - cancel_delayed_work(&data->rx_work); cancel_work_sync(&data->work); cancel_work_sync(&data->waker); - skb_queue_purge(&data->acl_q); - clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_INTR_RUNNING, &data->flags); clear_bit(BTUSB_DIAG_RUNNING, &data->flags); btusb_stop_traffic(data); + + /* rx_work must only be canceled once the URBs that can rearm it are + * gone, and it must be canceled synchronously since btusb_disconnect() + * frees the btusb_data it dereferences right after hci_unregister_dev(). + */ + cancel_delayed_work_sync(&data->rx_work); + + skb_queue_purge(&data->acl_q); + btusb_free_frags(data); err = usb_autopm_get_interface(data->intf); @@ -2131,7 +2179,7 @@ static int btusb_flush(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - cancel_delayed_work(&data->rx_work); + cancel_delayed_work_sync(&data->rx_work); skb_queue_purge(&data->acl_q); @@ -2141,56 +2189,72 @@ static int btusb_flush(struct hci_dev *hdev) return 0; } -static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb) +static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb) { struct btusb_data *data = hci_get_drvdata(hdev); - struct usb_ctrlrequest *dr; struct urb *urb; unsigned int pipe; + if (!data->bulk_tx_ep) + return ERR_PTR(-ENODEV); + + if (data->proto == BTUSB_PROTO_H4) { + /* The frame type is prepended in place, so the buffer must not + * be shared with anyone else. + */ + if (skb_cow_head(skb, 1)) + return ERR_PTR(-ENOMEM); + } + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return ERR_PTR(-ENOMEM); - dr = kmalloc_obj(*dr); - if (!dr) { - usb_free_urb(urb); - return ERR_PTR(-ENOMEM); - } - - dr->bRequestType = data->cmdreq_type; - dr->bRequest = data->cmdreq; - dr->wIndex = 0; - dr->wValue = 0; - dr->wLength = __cpu_to_le16(skb->len); + pipe = usb_sndbulkpipe(data->udev, data->bulk_tx_ep->bEndpointAddress); - pipe = usb_sndctrlpipe(data->udev, 0x00); + if (data->proto == BTUSB_PROTO_H4) { + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); + } - usb_fill_control_urb(urb, data->udev, pipe, (void *)dr, - skb->data, skb->len, btusb_tx_complete, skb); + usb_fill_bulk_urb(urb, data->udev, pipe, + skb->data, skb->len, btusb_tx_complete, skb); skb->dev = (void *)hdev; return urb; } -static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb) +static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb) { struct btusb_data *data = hci_get_drvdata(hdev); + struct usb_ctrlrequest *dr; struct urb *urb; unsigned int pipe; - if (!data->bulk_tx_ep) - return ERR_PTR(-ENODEV); + if (data->proto == BTUSB_PROTO_H4) + return alloc_bulk_urb(hdev, skb); urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return ERR_PTR(-ENOMEM); - pipe = usb_sndbulkpipe(data->udev, data->bulk_tx_ep->bEndpointAddress); + dr = kmalloc_obj(*dr); + if (!dr) { + usb_free_urb(urb); + return ERR_PTR(-ENOMEM); + } - usb_fill_bulk_urb(urb, data->udev, pipe, - skb->data, skb->len, btusb_tx_complete, skb); + dr->bRequestType = data->cmdreq_type; + dr->bRequest = data->cmdreq; + dr->wIndex = 0; + dr->wValue = 0; + dr->wLength = __cpu_to_le16(skb->len); + + pipe = usb_sndctrlpipe(data->udev, 0x00); + + usb_fill_control_urb(urb, data->udev, pipe, (void *)dr, + skb->data, skb->len, btusb_tx_complete, skb); skb->dev = (void *)hdev; @@ -2203,6 +2267,9 @@ static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb) struct urb *urb; unsigned int pipe; + if (data->proto == BTUSB_PROTO_H4) + return alloc_bulk_urb(hdev, skb); + if (!data->isoc_tx_ep) return ERR_PTR(-ENODEV); @@ -2416,10 +2483,9 @@ static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts) return 0; } -static struct usb_host_interface *btusb_find_altsetting(struct btusb_data *data, - int alt) +static struct usb_host_interface * +btusb_find_altsetting(struct usb_interface *intf, int alt) { - struct usb_interface *intf = data->isoc; int i; BT_DBG("Looking for Alt no :%d", alt); @@ -2442,6 +2508,13 @@ static void btusb_work(struct work_struct *work) int new_alts = 0; int err; + /* In H:4 mode SCO/ISO data is carried over the bulk endpoints, so + * there is no isochronous interface to resume or to switch alternate + * settings on. + */ + if (data->proto == BTUSB_PROTO_H4) + return; + if (data->sco_num > 0) { if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) { err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf); @@ -2475,9 +2548,9 @@ static void btusb_work(struct work_struct *work) * MTU >= 3 (packets) * 25 (size) - 3 (headers) = 72 * see also Core spec 5, vol 4, B 2.1.1 & Table 2.1. */ - if (btusb_find_altsetting(data, 6)) + if (btusb_find_altsetting(data->isoc, 6)) new_alts = 6; - else if (btusb_find_altsetting(data, 3) && + else if (btusb_find_altsetting(data->isoc, 3) && hdev->sco_mtu >= 72 && test_bit(BTUSB_USE_ALT3_FOR_WBS, &data->flags)) new_alts = 3; @@ -3951,8 +4024,11 @@ static ssize_t force_poll_sync_write(struct file *file, if (err) return err; - /* Only allow changes while the adapter is down */ - if (test_bit(HCI_UP, &data->hdev->flags)) + /* Only allow changes while the adapter is down and it is using legacy + * protocol. + */ + if (test_bit(HCI_UP, &data->hdev->flags) || + data->proto != BTUSB_PROTO_LEGACY) return -EPERM; if (data->poll_sync == enable) @@ -4051,7 +4127,7 @@ static int btusb_hci_drv_supported_altsettings(struct hci_dev *hdev, void *data, goto done; for (i = 0; i <= 6; i++) { - if (btusb_find_altsetting(drvdata, i)) + if (btusb_find_altsetting(drvdata->isoc, i)) rp->altsettings[rp->num++] = i; } @@ -4105,6 +4181,8 @@ static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct gpio_desc *reset_gpio; + struct usb_host_interface *alt; + struct usb_endpoint_descriptor *bulk_rx_ep, *bulk_tx_ep, *intr_ep; struct btusb_data *data; struct hci_dev *hdev; unsigned ifnum_base; @@ -4146,10 +4224,38 @@ static int btusb_probe(struct usb_interface *intf, return -ENOMEM; data->match_id = id; + + /* Alternate setting 1 with a single pair of bulk endpoints and no + * interrupt endpoint means the controller supports Bulk Serialization + * Mode, in which every packet is prefixed with an H:4 header and + * carried over the bulk endpoints. + */ + alt = btusb_find_altsetting(intf, 1); + if (alt && usb_find_int_in_endpoint(alt, &intr_ep) && + !usb_find_common_endpoints(alt, &bulk_rx_ep, &bulk_tx_ep, NULL, + NULL)) { + err = usb_set_interface(interface_to_usbdev(intf), ifnum_base, 1); + if (!err) + data->proto = BTUSB_PROTO_H4; + else + dev_warn(&intf->dev, + "failed to select alt setting 1 (%d), using legacy mode", + err); + } + + /* Check if all endpoints could be enumerated, legacy mode requires + * interrupt and bulk endpoints while H4 mode only requires bulk + * endpoints. + */ err = usb_find_common_endpoints(intf->cur_altsetting, &data->bulk_rx_ep, - &data->bulk_tx_ep, &data->intr_ep, NULL); - if (err) + &data->bulk_tx_ep, + data->proto == BTUSB_PROTO_LEGACY ? + &data->intr_ep : NULL, + NULL); + if (err) { + dev_err(&intf->dev, "failed to enumerate endpoints\n"); goto err_free_data; + } if (id->driver_info & BTUSB_AMP) { data->cmdreq_type = USB_TYPE_CLASS | 0x01; @@ -4361,6 +4467,12 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_AMP) { /* AMP controllers do not support SCO packets */ data->isoc = NULL; + } else if (data->proto == BTUSB_PROTO_H4) { + /* In H:4 mode every packet, including SCO/ISO, is carried over + * the bulk endpoints, so the isochronous interface must not be + * claimed nor have its alternate settings switched. + */ + data->isoc = NULL; } else { /* Interface orders are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, ifnum_base + 1); @@ -4470,7 +4582,8 @@ static int btusb_probe(struct usb_interface *intf, if (enable_autosuspend) usb_enable_autosuspend(data->udev); - data->poll_sync = enable_poll_sync; + if (data->proto == BTUSB_PROTO_LEGACY) + data->poll_sync = enable_poll_sync; err = hci_register_dev(hdev); if (err < 0)