Message ID | 1446640074-29585-2-git-send-email-jesse.sung@canonical.com |
---|---|
State | New |
Headers | show |
+ 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 --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);