Bluetooth: ath3k: Add support for Fujitsu Lifebook UH5x2 [04c5:1330]
[pandora-kernel.git] / drivers / bluetooth / btusb.c
index 2cc5f77..8b8b85d 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/module.h>
 #include <linux/usb.h>
+#include <linux/firmware.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -47,6 +48,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_BROKEN_ISOC      0x20
 #define BTUSB_WRONG_SCO_MTU    0x40
 #define BTUSB_ATH3012          0x80
+#define BTUSB_INTEL            0x100
 
 static struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -148,6 +150,8 @@ static struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -206,6 +210,9 @@ static struct usb_device_id blacklist_table[] = {
        /* Frontline ComProbe Bluetooth Sniffer */
        { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
 
+       /* Intel Bluetooth device */
+       { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
+
        { }     /* Terminating entry */
 };
 
@@ -926,6 +933,391 @@ static void btusb_waker(struct work_struct *work)
        usb_autopm_put_interface(data->intf);
 }
 
+static int btusb_setup_bcm92035(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+       u8 val = 0x00;
+
+       BT_DBG("%s", hdev->name);
+
+       skb = __hci_cmd_sync(hdev, 0xfc3b, 1, &val, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb))
+               BT_ERR("BCM92035 command failed (%ld)", -PTR_ERR(skb));
+       else
+               kfree_skb(skb);
+
+       return 0;
+}
+
+struct intel_version {
+       u8 status;
+       u8 hw_platform;
+       u8 hw_variant;
+       u8 hw_revision;
+       u8 fw_variant;
+       u8 fw_revision;
+       u8 fw_build_num;
+       u8 fw_build_ww;
+       u8 fw_build_yy;
+       u8 fw_patch_num;
+} __packed;
+
+static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
+                                               struct intel_version *ver)
+{
+       const struct firmware *fw;
+       char fwname[64];
+       int ret;
+
+       snprintf(fwname, sizeof(fwname),
+                "intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.bseq",
+                ver->hw_platform, ver->hw_variant, ver->hw_revision,
+                ver->fw_variant,  ver->fw_revision, ver->fw_build_num,
+                ver->fw_build_ww, ver->fw_build_yy);
+
+       ret = request_firmware(&fw, fwname, &hdev->dev);
+       if (ret < 0) {
+               if (ret == -EINVAL) {
+                       BT_ERR("%s Intel firmware file request failed (%d)",
+                              hdev->name, ret);
+                       return NULL;
+               }
+
+               BT_ERR("%s failed to open Intel firmware file: %s(%d)",
+                      hdev->name, fwname, ret);
+
+               /* If the correct firmware patch file is not found, use the
+                * default firmware patch file instead
+                */
+               snprintf(fwname, sizeof(fwname), "intel/ibt-hw-%x.%x.bseq",
+                        ver->hw_platform, ver->hw_variant);
+               if (request_firmware(&fw, fwname, &hdev->dev) < 0) {
+                       BT_ERR("%s failed to open default Intel fw file: %s",
+                              hdev->name, fwname);
+                       return NULL;
+               }
+       }
+
+       BT_INFO("%s: Intel Bluetooth firmware file: %s", hdev->name, fwname);
+
+       return fw;
+}
+
+static int btusb_setup_intel_patching(struct hci_dev *hdev,
+                                     const struct firmware *fw,
+                                     const u8 **fw_ptr, int *disable_patch)
+{
+       struct sk_buff *skb;
+       struct hci_command_hdr *cmd;
+       const u8 *cmd_param;
+       struct hci_event_hdr *evt = NULL;
+       const u8 *evt_param = NULL;
+       int remain = fw->size - (*fw_ptr - fw->data);
+
+       /* The first byte indicates the types of the patch command or event.
+        * 0x01 means HCI command and 0x02 is HCI event. If the first bytes
+        * in the current firmware buffer doesn't start with 0x01 or
+        * the size of remain buffer is smaller than HCI command header,
+        * the firmware file is corrupted and it should stop the patching
+        * process.
+        */
+       if (remain > HCI_COMMAND_HDR_SIZE && *fw_ptr[0] != 0x01) {
+               BT_ERR("%s Intel fw corrupted: invalid cmd read", hdev->name);
+               return -EINVAL;
+       }
+       (*fw_ptr)++;
+       remain--;
+
+       cmd = (struct hci_command_hdr *)(*fw_ptr);
+       *fw_ptr += sizeof(*cmd);
+       remain -= sizeof(*cmd);
+
+       /* Ensure that the remain firmware data is long enough than the length
+        * of command parameter. If not, the firmware file is corrupted.
+        */
+       if (remain < cmd->plen) {
+               BT_ERR("%s Intel fw corrupted: invalid cmd len", hdev->name);
+               return -EFAULT;
+       }
+
+       /* If there is a command that loads a patch in the firmware
+        * file, then enable the patch upon success, otherwise just
+        * disable the manufacturer mode, for example patch activation
+        * is not required when the default firmware patch file is used
+        * because there are no patch data to load.
+        */
+       if (*disable_patch && le16_to_cpu(cmd->opcode) == 0xfc8e)
+               *disable_patch = 0;
+
+       cmd_param = *fw_ptr;
+       *fw_ptr += cmd->plen;
+       remain -= cmd->plen;
+
+       /* This reads the expected events when the above command is sent to the
+        * device. Some vendor commands expects more than one events, for
+        * example command status event followed by vendor specific event.
+        * For this case, it only keeps the last expected event. so the command
+        * can be sent with __hci_cmd_sync_ev() which returns the sk_buff of
+        * last expected event.
+        */
+       while (remain > HCI_EVENT_HDR_SIZE && *fw_ptr[0] == 0x02) {
+               (*fw_ptr)++;
+               remain--;
+
+               evt = (struct hci_event_hdr *)(*fw_ptr);
+               *fw_ptr += sizeof(*evt);
+               remain -= sizeof(*evt);
+
+               if (remain < evt->plen) {
+                       BT_ERR("%s Intel fw corrupted: invalid evt len",
+                              hdev->name);
+                       return -EFAULT;
+               }
+
+               evt_param = *fw_ptr;
+               *fw_ptr += evt->plen;
+               remain -= evt->plen;
+       }
+
+       /* Every HCI commands in the firmware file has its correspond event.
+        * If event is not found or remain is smaller than zero, the firmware
+        * file is corrupted.
+        */
+       if (!evt || !evt_param || remain < 0) {
+               BT_ERR("%s Intel fw corrupted: invalid evt read", hdev->name);
+               return -EFAULT;
+       }
+
+       skb = __hci_cmd_sync_ev(hdev, le16_to_cpu(cmd->opcode), cmd->plen,
+                               cmd_param, evt->evt, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s sending Intel patch command (0x%4.4x) failed (%ld)",
+                      hdev->name, cmd->opcode, PTR_ERR(skb));
+               return -PTR_ERR(skb);
+       }
+
+       /* It ensures that the returned event matches the event data read from
+        * the firmware file. At fist, it checks the length and then
+        * the contents of the event.
+        */
+       if (skb->len != evt->plen) {
+               BT_ERR("%s mismatch event length (opcode 0x%4.4x)", hdev->name,
+                      le16_to_cpu(cmd->opcode));
+               kfree_skb(skb);
+               return -EFAULT;
+       }
+
+       if (memcmp(skb->data, evt_param, evt->plen)) {
+               BT_ERR("%s mismatch event parameter (opcode 0x%4.4x)",
+                      hdev->name, le16_to_cpu(cmd->opcode));
+               kfree_skb(skb);
+               return -EFAULT;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+
+static int btusb_setup_intel(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+       const struct firmware *fw;
+       const u8 *fw_ptr;
+       int disable_patch;
+       struct intel_version *ver;
+
+       const u8 mfg_enable[] = { 0x01, 0x00 };
+       const u8 mfg_disable[] = { 0x00, 0x00 };
+       const u8 mfg_reset_deactivate[] = { 0x00, 0x01 };
+       const u8 mfg_reset_activate[] = { 0x00, 0x02 };
+
+       BT_DBG("%s", hdev->name);
+
+       /* The controller has a bug with the first HCI command sent to it
+        * returning number of completed commands as zero. This would stall the
+        * command processing in the Bluetooth core.
+        *
+        * As a workaround, send HCI Reset command first which will reset the
+        * number of completed commands and allow normal command processing
+        * from now on.
+        */
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s sending initial HCI reset command failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return -PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       /* Read Intel specific controller version first to allow selection of
+        * which firmware file to load.
+        *
+        * The returned information are hardware variant and revision plus
+        * firmware variant, revision and build number.
+        */
+       skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s reading Intel fw version command failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return -PTR_ERR(skb);
+       }
+
+       if (skb->len != sizeof(*ver)) {
+               BT_ERR("%s Intel version event length mismatch", hdev->name);
+               kfree_skb(skb);
+               return -EIO;
+       }
+
+       ver = (struct intel_version *)skb->data;
+       if (ver->status) {
+               BT_ERR("%s Intel fw version event failed (%02x)", hdev->name,
+                      ver->status);
+               kfree_skb(skb);
+               return -bt_to_errno(ver->status);
+       }
+
+       BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
+               hdev->name, ver->hw_platform, ver->hw_variant,
+               ver->hw_revision, ver->fw_variant,  ver->fw_revision,
+               ver->fw_build_num, ver->fw_build_ww, ver->fw_build_yy,
+               ver->fw_patch_num);
+
+       /* fw_patch_num indicates the version of patch the device currently
+        * have. If there is no patch data in the device, it is always 0x00.
+        * So, if it is other than 0x00, no need to patch the deivce again.
+        */
+       if (ver->fw_patch_num) {
+               BT_INFO("%s: Intel device is already patched. patch num: %02x",
+                       hdev->name, ver->fw_patch_num);
+               kfree_skb(skb);
+               return 0;
+       }
+
+       /* Opens the firmware patch file based on the firmware version read
+        * from the controller. If it fails to open the matching firmware
+        * patch file, it tries to open the default firmware patch file.
+        * If no patch file is found, allow the device to operate without
+        * a patch.
+        */
+       fw = btusb_setup_intel_get_fw(hdev, ver);
+       if (!fw) {
+               kfree_skb(skb);
+               return 0;
+       }
+       fw_ptr = fw->data;
+
+       /* This Intel specific command enables the manufacturer mode of the
+        * controller.
+        *
+        * Only while this mode is enabled, the driver can download the
+        * firmware patch data and configuration parameters.
+        */
+       skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s entering Intel manufacturer mode failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               release_firmware(fw);
+               return -PTR_ERR(skb);
+       }
+
+       if (skb->data[0]) {
+               u8 evt_status = skb->data[0];
+               BT_ERR("%s enable Intel manufacturer mode event failed (%02x)",
+                      hdev->name, evt_status);
+               kfree_skb(skb);
+               release_firmware(fw);
+               return -bt_to_errno(evt_status);
+       }
+       kfree_skb(skb);
+
+       disable_patch = 1;
+
+       /* The firmware data file consists of list of Intel specific HCI
+        * commands and its expected events. The first byte indicates the
+        * type of the message, either HCI command or HCI event.
+        *
+        * It reads the command and its expected event from the firmware file,
+        * and send to the controller. Once __hci_cmd_sync_ev() returns,
+        * the returned event is compared with the event read from the firmware
+        * file and it will continue until all the messages are downloaded to
+        * the controller.
+        *
+        * Once the firmware patching is completed successfully,
+        * the manufacturer mode is disabled with reset and activating the
+        * downloaded patch.
+        *
+        * If the firmware patching fails, the manufacturer mode is
+        * disabled with reset and deactivating the patch.
+        *
+        * If the default patch file is used, no reset is done when disabling
+        * the manufacturer.
+        */
+       while (fw->size > fw_ptr - fw->data) {
+               int ret;
+
+               ret = btusb_setup_intel_patching(hdev, fw, &fw_ptr,
+                                                &disable_patch);
+               if (ret < 0)
+                       goto exit_mfg_deactivate;
+       }
+
+       release_firmware(fw);
+
+       if (disable_patch)
+               goto exit_mfg_disable;
+
+       /* Patching completed successfully and disable the manufacturer mode
+        * with reset and activate the downloaded firmware patches.
+        */
+       skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate),
+                            mfg_reset_activate, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return -PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
+               hdev->name);
+
+       return 0;
+
+exit_mfg_disable:
+       /* Disable the manufacturer mode without reset */
+       skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return -PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
+       return 0;
+
+exit_mfg_deactivate:
+       release_firmware(fw);
+
+       /* Patching failed. Disable the manufacturer mode with reset and
+        * deactivate the downloaded firmware patches.
+        */
+       skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate),
+                            mfg_reset_deactivate, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return -PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
+               hdev->name);
+
+       return 0;
+}
+
 static int btusb_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
@@ -1022,11 +1414,17 @@ static int btusb_probe(struct usb_interface *intf,
 
        SET_HCIDEV_DEV(hdev, &intf->dev);
 
-       hdev->open     = btusb_open;
-       hdev->close    = btusb_close;
-       hdev->flush    = btusb_flush;
-       hdev->send     = btusb_send_frame;
-       hdev->notify   = btusb_notify;
+       hdev->open   = btusb_open;
+       hdev->close  = btusb_close;
+       hdev->flush  = btusb_flush;
+       hdev->send   = btusb_send_frame;
+       hdev->notify = btusb_notify;
+
+       if (id->driver_info & BTUSB_BCM92035)
+               hdev->setup = btusb_setup_bcm92035;
+
+       if (id->driver_info & BTUSB_INTEL)
+               hdev->setup = btusb_setup_intel;
 
        /* Interface numbers are hardcoded in the specification */
        data->isoc = usb_ifnum_to_if(data->udev, 1);
@@ -1065,17 +1463,6 @@ static int btusb_probe(struct usb_interface *intf,
                data->isoc = NULL;
        }
 
-       if (id->driver_info & BTUSB_BCM92035) {
-               unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
-               struct sk_buff *skb;
-
-               skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
-               if (skb) {
-                       memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
-                       skb_queue_tail(&hdev->driver_init, skb);
-               }
-       }
-
        if (data->isoc) {
                err = usb_driver_claim_interface(&btusb_driver,
                                                        data->isoc, data);