From patchwork Wed Nov 4 12:27:54 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wen-chien Jesse Sung X-Patchwork-Id: 539815 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) by ozlabs.org (Postfix) with ESMTP id 8CEC81402D6; Wed, 4 Nov 2015 23:28:11 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1ZtxAe-0003rK-3f; Wed, 04 Nov 2015 12:28:08 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtp (Exim 4.76) (envelope-from ) id 1ZtxAW-0003qN-PS for kernel-team@lists.ubuntu.com; Wed, 04 Nov 2015 12:28:00 +0000 Received: from [175.181.141.54] (helo=localhost.localdomain) by youngberry.canonical.com with esmtpsa (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.76) (envelope-from ) id 1ZtxAW-0003aV-Ew; Wed, 04 Nov 2015 12:28:00 +0000 From: Wen-chien Jesse Sung To: kernel-team@lists.ubuntu.com Subject: [Vivid][PATCH] UBUNTU: SAUCE: Bluetooth: Support for LED on Marvell modules Date: Wed, 4 Nov 2015 20:27:54 +0800 Message-Id: <1446640074-29585-2-git-send-email-jesse.sung@canonical.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1446640074-29585-1-git-send-email-jesse.sung@canonical.com> References: <1446640074-29585-1-git-send-email-jesse.sung@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.14 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: kernel-team-bounces@lists.ubuntu.com BugLink: https://launchpad.net/bugs/1512999 For Edge Gateway 5000/5100 only. Add code for controlling bluetooth LED via firmware, and turns the LED on and off when the interface is up and down accordingly. Base on the code found at https://chromium.googlesource.com/chromiumos/third_party/kernel/+/8300e2ccad47c5c2f10507661c10d4892211790e%5E%21/#F0 Signed-off-by: Wen-chien Jesse Sung Tested-by: Gavin Lin Reviewed-by: Keng-Yu Lin --- drivers/bluetooth/btusb.c | 91 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 11e9d8f..03055b8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,8 @@ static struct usb_driver btusb_driver; #define BTUSB_MARVELL 0x800 #define BTUSB_QCA_ROME 0x8000 +#define BTUSB_MARVELL_LED_COMMAND 0xfc77 + static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, @@ -325,6 +328,10 @@ struct btusb_data { int isoc_altsetting; int suspend_count; + bool is_edge_gateway; + int marvell_cmd_in_progress; + wait_queue_head_t marvell_wait_q; + int (*recv_bulk)(struct btusb_data *data, void *buffer, int count); int (*setup_on_usb)(struct hci_dev *hdev); @@ -530,10 +537,31 @@ static void btusb_intr_complete(struct urb *urb) if (urb->status == 0) { hdev->stat.byte_rx += urb->actual_length; - if (btusb_recv_intr(data, urb->transfer_buffer, - urb->actual_length) < 0) { - BT_ERR("%s corrupted event packet", hdev->name); - hdev->stat.err_rx++; + if (data->is_edge_gateway && data->marvell_cmd_in_progress) { + struct hci_ev_cmd_complete *ev; + struct hci_event_hdr *hdr; + bool consume_ev = false; + + hdr = urb->transfer_buffer; + if (hdr->evt == HCI_EV_CMD_COMPLETE) { + ev = (void *)((u8 *)hdr + HCI_EVENT_HDR_SIZE); + if (__le16_to_cpu(ev->opcode) == BTUSB_MARVELL_LED_COMMAND) { + consume_ev = true; + data->marvell_cmd_in_progress = false; + wake_up_interruptible(&data->marvell_wait_q); + } + } + + if (!consume_ev && btusb_recv_intr(data, urb->transfer_buffer, urb->actual_length) < 0) { + BT_ERR("%s corrupted event packet", hdev->name); + hdev->stat.err_rx++; + } + } else { + if (btusb_recv_intr(data, urb->transfer_buffer, + urb->actual_length) < 0) { + BT_ERR("%s corrupted event packet", hdev->name); + hdev->stat.err_rx++; + } } } else if (urb->status == -ENOENT) { /* Avoid suspend failed when usb_kill_urb */ @@ -862,6 +890,38 @@ done: kfree_skb(skb); } +static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb); + +static void btusb_marvell_config_led(struct hci_dev *hdev, bool status) +{ + u8 config_led[] = { 0x09, 0x00, 0x01, 0x01 }; + int len = HCI_COMMAND_HDR_SIZE + sizeof(config_led); + struct hci_command_hdr *hdr; + struct sk_buff *skb; + struct btusb_data *data = hci_get_drvdata(hdev); + + if ((!data->is_edge_gateway) || data->marvell_cmd_in_progress) + return; + + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) + return; + + hdr = (struct hci_command_hdr *)skb_put(skb, HCI_COMMAND_HDR_SIZE); + hdr->opcode = cpu_to_le16(BTUSB_MARVELL_LED_COMMAND); + hdr->plen = sizeof(config_led); + + if (status) + config_led[1] = 0x01; + + memcpy(skb_put(skb, sizeof(config_led)), config_led, sizeof(config_led)); + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + + data->marvell_cmd_in_progress = true; + btusb_send_frame(hdev, skb); + wait_event_interruptible_timeout(data->marvell_wait_q, !data->marvell_cmd_in_progress, HZ); +} + static int btusb_open(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); @@ -905,6 +965,9 @@ static int btusb_open(struct hci_dev *hdev) done: usb_autopm_put_interface(data->intf); + + if (data->is_edge_gateway) + btusb_marvell_config_led(hdev, true); return 0; failed: @@ -928,9 +991,17 @@ static int btusb_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (data->is_edge_gateway && usb_get_intfdata(data->intf)) + btusb_marvell_config_led(hdev, false); + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; + if (data->is_edge_gateway) { + data->marvell_cmd_in_progress = false; + wake_up_interruptible(&data->marvell_wait_q); + } + cancel_work_sync(&data->work); cancel_work_sync(&data->waker); @@ -2362,8 +2433,13 @@ static int btusb_probe(struct usb_interface *intf, hdev->set_bdaddr = btusb_set_bdaddr_intel; } - if (id->driver_info & BTUSB_MARVELL) + if (id->driver_info & BTUSB_MARVELL) { hdev->set_bdaddr = btusb_set_bdaddr_marvell; + if (dmi_match(DMI_PRODUCT_NAME, "Edge Gateway 5000") || + dmi_match(DMI_PRODUCT_NAME, "Edge Gateway 5100")) + data->is_edge_gateway = true; + init_waitqueue_head(&data->marvell_wait_q); + } if (id->driver_info & BTUSB_INTEL_BOOT) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); @@ -2495,6 +2571,11 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message) return -EBUSY; } + if (data->is_edge_gateway) { + data->marvell_cmd_in_progress = 0; + wake_up_interruptible(&data->marvell_wait_q); + } + cancel_work_sync(&data->work); btusb_stop_traffic(data);