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

Wen-chien Jesse Sung jesse.sung at canonical.com
Wed Nov 4 12:27:54 UTC 2015


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 at canonical.com>
Tested-by: Gavin Lin <gavin.lin at canonical.com>
Reviewed-by: Keng-Yu Lin <kengyu at 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





More information about the kernel-team mailing list