diff mbox

[Vivid] UBUNTU: SAUCE: Bluetooth: Support for LED on Marvell modules

Message ID 1446640074-29585-2-git-send-email-jesse.sung@canonical.com
State New
Headers show

Commit Message

Wen-chien Jesse Sung Nov. 4, 2015, 12:27 p.m. UTC
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 <jesse.sung@canonical.com>
Tested-by: Gavin Lin <gavin.lin@canonical.com>
Reviewed-by: Keng-Yu Lin <kengyu@canonical.com>
---
 drivers/bluetooth/btusb.c | 91 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 86 insertions(+), 5 deletions(-)

Comments

Wen-chien Jesse Sung Nov. 4, 2015, 12:45 p.m. UTC | #1
+ Ricardo

2015-11-04 20:27 GMT+08:00 Wen-chien Jesse Sung <jesse.sung@canonical.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 <jesse.sung@canonical.com>
> Tested-by: Gavin Lin <gavin.lin@canonical.com>
> Reviewed-by: Keng-Yu Lin <kengyu@canonical.com>
> ---
>  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 <linux/module.h>
>  #include <linux/usb.h>
>  #include <linux/firmware.h>
> +#include <linux/dmi.h>
>
>  #include <net/bluetooth/bluetooth.h>
>  #include <net/bluetooth/hci_core.h>
> @@ -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);
> --
> 2.5.0
>
diff mbox

Patch

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 <linux/module.h>
 #include <linux/usb.h>
 #include <linux/firmware.h>
+#include <linux/dmi.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -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);