From f38c173fe1d78dee7a9f6dd3aad661d42304150f Mon Sep 17 00:00:00 2001 From: Bhasker Neti Date: Sun, 30 Mar 2014 15:28:58 +0530 Subject: [PATCH] Bluetooth: x86-Rome driver support Add USB client driver support for Rome USB chip. Change-Id: I781dcdc913291ab0695ced1b34fde2710049586e Signed-off-by: Bhasker Neti --- drivers/bluetooth/ath3k.c | 154 +++++++++++++++++++++++++++--- drivers/bluetooth/btusb.c | 12 ++- include/net/bluetooth/bluetooth.h | 5 + 3 files changed, 154 insertions(+), 17 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 0a327f4154a..7778e6aa63d 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2008-2009 Atheros Communications Inc. + * Copyright (c) 2014 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +33,19 @@ #define VERSION "1.0" #define ATH3K_FIRMWARE "ath3k-1.fw" +#define ROME2_1_USB_RAMPATCH_FILE "ar3k/rampatch_2.1.tlv" +#define ROME2_1_USB_NVM_FILE "ar3k/nvm_tlv_usb_2.1.bin" + +#define ROME1_1_USB_RAMPATCH_FILE "ar3k/rampatch_1.1.img" +#define ROME1_1_USB_NVM_FILE "ar3k/nvm_tlv_usb_1.1.bin" + +#define ROME2_1_USB_RAMPATCH_HEADER sizeof(struct rome2_1_version) +#define ROME1_1_USB_RAMPATCH_HEADER sizeof(struct rome1_1_version) +#define ROME1_1_USB_NVM_HEADER 0x04 +#define ROME2_1_USB_NVM_HEADER 0x04 +#define ROME2_1_USB_CHIP_VERSION 0x200 +#define ROME1_1_USB_CHIP_VERSION 0x101 + #define ATH3K_DNLOAD 0x01 #define ATH3K_GETSTATE 0x05 #define ATH3K_SET_NORMAL_MODE 0x07 @@ -57,6 +71,34 @@ struct ath3k_version { unsigned char reserved[0x07]; }; +struct __packed rome1_1_version { + u8 type; + u8 length[3]; + u8 sign_ver; + u8 sign_algo; + u8 resv1[2]; + u16 product_id; + u16 build_ver; + u16 patch_ver; + u8 resv2[2]; + u32 entry_addr; +}; +struct __packed rome2_1_version { + u8 type; + u8 length[3]; + u32 total_len; + u32 patch_len; + u8 sign_ver; + u8 sign_algo; + u8 resv1[2]; + u16 product_id; + u16 build_ver; + u16 patch_ver; + u8 resv2[2]; + u32 entry_addr; +}; + + static struct usb_device_id ath3k_table[] = { /* Atheros AR3011 */ { USB_DEVICE(0x0CF3, 0x3000) }, @@ -243,8 +285,27 @@ static int ath3k_get_version(struct usb_device *udev, return ret; } +int get_rome_version(struct usb_device *udev) +{ + struct ath3k_version fw_version; + int ret = 0; + + ret = ath3k_get_version(udev, &fw_version); + if (ret < 0) { + BT_ERR("Failed to get Rome Firmware version"); + return ret; + } + if ((fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) || + (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION)) + ret = fw_version.rom_version; + else + ret = 0; + return ret; +} +EXPORT_SYMBOL(get_rome_version); + static int ath3k_load_fwfile(struct usb_device *udev, - const struct firmware *firmware) + const struct firmware *firmware, int header_h) { u8 *send_buf; int err, pipe, len, size, count, sent = 0; @@ -258,7 +319,7 @@ static int ath3k_load_fwfile(struct usb_device *udev, return -ENOMEM; } - size = min_t(uint, count, FW_HDR_SIZE); + size = min_t(uint, count, header_h); memcpy(send_buf, firmware->data, size); pipe = usb_sndctrlpipe(udev, 0); @@ -334,6 +395,8 @@ static int ath3k_load_patch(struct usb_device *udev) char filename[ATH3K_NAME_LEN] = {0}; const struct firmware *firmware; struct ath3k_version fw_version, pt_version; + struct rome2_1_version *rome2_1_version; + struct rome1_1_version *rome1_1_version; int ret; ret = ath3k_get_state(udev, &fw_state); @@ -352,20 +415,45 @@ static int ath3k_load_patch(struct usb_device *udev) BT_ERR("Can't get version to change to load ram patch err"); return ret; } - - snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu", + if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION) { + BT_DBG("Chip Detected as ROME1.1"); + snprintf(filename, ATH3K_NAME_LEN, ROME1_1_USB_RAMPATCH_FILE); + } else if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) { + BT_DBG("Chip Detected as ROME2.1"); + snprintf(filename, ATH3K_NAME_LEN, ROME2_1_USB_RAMPATCH_FILE); + } else { + BT_DBG("Chip Detected as Ath3k"); + snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu", fw_version.rom_version); - + } ret = request_firmware(&firmware, filename, &udev->dev); if (ret < 0) { BT_ERR("Patch file not found %s", filename); return ret; } - pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8); - pt_version.build_version = *(int *) + if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) { + rome2_1_version = (struct rome2_1_version *) firmware->data; + pt_version.rom_version = rome2_1_version->build_ver; + pt_version.build_version = rome2_1_version->patch_ver; + BT_DBG("pt_ver.rom2.1_ver : 0x%x", pt_version.rom_version); + BT_DBG("pt_ver.build2.1_ver: 0x%x", pt_version.build_version); + BT_DBG("fw_ver.rom2.1_ver: 0x%x", fw_version.rom_version); + BT_DBG("fw_ver.build2.1_ver: 0x%x", fw_version.build_version); + } else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION) { + rome1_1_version = (struct rome1_1_version *) firmware->data; + pt_version.build_version = rome1_1_version->build_ver; + pt_version.rom_version = rome1_1_version->patch_ver; + BT_DBG("pt_ver.rom1.1_ver : 0x%x", pt_version.rom_version); + BT_DBG("pt_ver.build1.1_ver: 0x%x", pt_version.build_version); + BT_DBG("fw_ver.rom1.1_ver: 0x%x", fw_version.rom_version); + BT_DBG("fw_ver.build1.1_ver: 0x%x", fw_version.build_version); + } else { + pt_version.rom_version = *(int *)(firmware->data + + firmware->size - 8); + pt_version.build_version = *(int *) (firmware->data + firmware->size - 4); - + } if ((pt_version.rom_version != fw_version.rom_version) || (pt_version.build_version <= fw_version.build_version)) { BT_ERR("Patch file version did not match with firmware"); @@ -373,7 +461,15 @@ static int ath3k_load_patch(struct usb_device *udev) return -EINVAL; } - ret = ath3k_load_fwfile(udev, firmware); + if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) + ret = ath3k_load_fwfile(udev, firmware, + ROME2_1_USB_RAMPATCH_HEADER); + else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION) + ret = ath3k_load_fwfile(udev, firmware, + ROME1_1_USB_RAMPATCH_HEADER); + else + ret = ath3k_load_fwfile(udev, firmware, FW_HDR_SIZE); + release_firmware(firmware); return ret; @@ -415,8 +511,13 @@ static int ath3k_load_syscfg(struct usb_device *udev) break; } - snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s", - fw_version.rom_version, clk_value, ".dfu"); + if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) + snprintf(filename, ATH3K_NAME_LEN, ROME2_1_USB_NVM_FILE); + else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION) + snprintf(filename, ATH3K_NAME_LEN, ROME1_1_USB_NVM_FILE); + else + snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s", + fw_version.rom_version, clk_value, ".dfu"); ret = request_firmware(&firmware, filename, &udev->dev); if (ret < 0) { @@ -424,12 +525,36 @@ static int ath3k_load_syscfg(struct usb_device *udev) return ret; } - ret = ath3k_load_fwfile(udev, firmware); + if (fw_version.rom_version == ROME2_1_USB_CHIP_VERSION) + ret = ath3k_load_fwfile(udev, firmware, ROME2_1_USB_NVM_HEADER); + else if (fw_version.rom_version == ROME1_1_USB_CHIP_VERSION) + ret = ath3k_load_fwfile(udev, firmware, ROME1_1_USB_NVM_HEADER); + else + ret = ath3k_load_fwfile(udev, firmware, FW_HDR_SIZE); release_firmware(firmware); return ret; } + +int rome_download(struct usb_device *udev) +{ + int ret; + + ret = ath3k_load_patch(udev); + if (ret < 0) { + BT_ERR("Loading patch file failed"); + return ret; + } + ret = ath3k_load_syscfg(udev); + if (ret < 0) { + BT_ERR("Loading sysconfig file failed"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL(rome_download); static int ath3k_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -441,7 +566,10 @@ static int ath3k_probe(struct usb_interface *intf, if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; - + if (get_rome_version(udev)) { + BT_INFO("Rome detected, fw dnld will be triggered from btusb"); + return -ENODEV; + } /* match device ID in ath3k blacklist table */ if (!id->driver_info) { const struct usb_device_id *match; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 58491f1b279..65840bb9a30 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4,7 +4,6 @@ * * Copyright (C) 2005-2008 Marcel Holtmann * - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -1332,7 +1331,7 @@ static int btusb_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *ep_desc; struct btusb_data *data; struct hci_dev *hdev; - int i, err; + int i, version, err; BT_DBG("intf %p id %p", intf, id); @@ -1362,12 +1361,17 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_ATH3012) { struct usb_device *udev = interface_to_usbdev(intf); + version = get_rome_version(udev); + BT_INFO("Rome Version: 0x%x", version); /* Old firmware would otherwise let ath3k driver load * patch and sysconfig files */ - if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001) + if (version) + rome_download(udev); + else if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001) { + BT_INFO("FW for ar3k is yet to be downloaded"); return -ENODEV; + } } - data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 10eb9b38901..5ad826645e2 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -1,6 +1,7 @@ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (C) 2014 The Linux Foundation. All rights reserved. Written 2000,2001 by Maxim Krasnyansky @@ -28,6 +29,7 @@ #include #include #include +#include #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 31 @@ -344,4 +346,7 @@ void sco_exit(void); void bt_sock_reclassify_lock(struct sock *sk, int proto); +int get_rome_version(struct usb_device *udev); +int rome_download(struct usb_device *udev); + #endif /* __BLUETOOTH_H */