Join GitHub today
The following table provides drivers for Atheros bluetooth devices and the operating systems Windows 7, Windows 8 and Windows 10. For Windows XP, Windows Vista and Windows 7 please click here.
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign up Find file Copy path
meg4z0rdtreewide: Replace GPLv2 boilerplate/reference with SPDX - rule 1561a59d1bMay 30, 2019
60 contributors
// SPDX-License-Identifier: GPL-2.0-or-later |
/* |
* Copyright (c) 2008-2009 Atheros Communications Inc. |
*/ |
#include<linux/module.h> |
#include<linux/kernel.h> |
#include<linux/init.h> |
#include<linux/slab.h> |
#include<linux/types.h> |
#include<linux/errno.h> |
#include<linux/device.h> |
#include<linux/firmware.h> |
#include<linux/usb.h> |
#include<asm/unaligned.h> |
#include<net/bluetooth/bluetooth.h> |
#defineVERSION'1.0' |
#defineATH3K_FIRMWARE'ath3k-1.fw' |
#defineATH3K_DNLOAD0x01 |
#defineATH3K_GETSTATE0x05 |
#defineATH3K_SET_NORMAL_MODE0x07 |
#defineATH3K_GETVERSION0x09 |
#defineUSB_REG_SWITCH_VID_PID0x0a |
#defineATH3K_MODE_MASK0x3F |
#defineATH3K_NORMAL_MODE0x0E |
#defineATH3K_PATCH_UPDATE0x80 |
#defineATH3K_SYSCFG_UPDATE0x40 |
#defineATH3K_XTAL_FREQ_26M0x00 |
#defineATH3K_XTAL_FREQ_40M0x01 |
#defineATH3K_XTAL_FREQ_19P20x02 |
#defineATH3K_NAME_LEN0xFF |
struct ath3k_version { |
__le32 rom_version; |
__le32 build_version; |
__le32 ram_version; |
__u8 ref_clock; |
__u8 reserved[7]; |
} __packed; |
staticconststruct usb_device_id ath3k_table[] = { |
/* Atheros AR3011 */ |
{ USB_DEVICE(0x0CF3, 0x3000) }, |
/* Atheros AR3011 with sflash firmware*/ |
{ USB_DEVICE(0x0489, 0xE027) }, |
{ USB_DEVICE(0x0489, 0xE03D) }, |
{ USB_DEVICE(0x04F2, 0xAFF1) }, |
{ USB_DEVICE(0x0930, 0x0215) }, |
{ USB_DEVICE(0x0CF3, 0x3002) }, |
{ USB_DEVICE(0x0CF3, 0xE019) }, |
{ USB_DEVICE(0x13d3, 0x3304) }, |
/* Atheros AR9285 Malbec with sflash firmware */ |
{ USB_DEVICE(0x03F0, 0x311D) }, |
/* Atheros AR3012 with sflash firmware*/ |
{ USB_DEVICE(0x0489, 0xe04d) }, |
{ USB_DEVICE(0x0489, 0xe04e) }, |
{ USB_DEVICE(0x0489, 0xe057) }, |
{ USB_DEVICE(0x0489, 0xe056) }, |
{ USB_DEVICE(0x0489, 0xe05f) }, |
{ USB_DEVICE(0x0489, 0xe076) }, |
{ USB_DEVICE(0x0489, 0xe078) }, |
{ USB_DEVICE(0x0489, 0xe095) }, |
{ USB_DEVICE(0x04c5, 0x1330) }, |
{ USB_DEVICE(0x04CA, 0x3004) }, |
{ USB_DEVICE(0x04CA, 0x3005) }, |
{ USB_DEVICE(0x04CA, 0x3006) }, |
{ USB_DEVICE(0x04CA, 0x3007) }, |
{ USB_DEVICE(0x04CA, 0x3008) }, |
{ USB_DEVICE(0x04CA, 0x300b) }, |
{ USB_DEVICE(0x04CA, 0x300d) }, |
{ USB_DEVICE(0x04CA, 0x300f) }, |
{ USB_DEVICE(0x04CA, 0x3010) }, |
{ USB_DEVICE(0x04CA, 0x3014) }, |
{ USB_DEVICE(0x04CA, 0x3018) }, |
{ USB_DEVICE(0x0930, 0x0219) }, |
{ USB_DEVICE(0x0930, 0x021c) }, |
{ USB_DEVICE(0x0930, 0x0220) }, |
{ USB_DEVICE(0x0930, 0x0227) }, |
{ USB_DEVICE(0x0b05, 0x17d0) }, |
{ USB_DEVICE(0x0CF3, 0x0036) }, |
{ USB_DEVICE(0x0CF3, 0x3004) }, |
{ USB_DEVICE(0x0CF3, 0x3008) }, |
{ USB_DEVICE(0x0CF3, 0x311D) }, |
{ USB_DEVICE(0x0CF3, 0x311E) }, |
{ USB_DEVICE(0x0CF3, 0x311F) }, |
{ USB_DEVICE(0x0cf3, 0x3121) }, |
{ USB_DEVICE(0x0CF3, 0x817a) }, |
{ USB_DEVICE(0x0CF3, 0x817b) }, |
{ USB_DEVICE(0x0cf3, 0xe003) }, |
{ USB_DEVICE(0x0CF3, 0xE004) }, |
{ USB_DEVICE(0x0CF3, 0xE005) }, |
{ USB_DEVICE(0x0CF3, 0xE006) }, |
{ USB_DEVICE(0x13d3, 0x3362) }, |
{ USB_DEVICE(0x13d3, 0x3375) }, |
{ USB_DEVICE(0x13d3, 0x3393) }, |
{ USB_DEVICE(0x13d3, 0x3395) }, |
{ USB_DEVICE(0x13d3, 0x3402) }, |
{ USB_DEVICE(0x13d3, 0x3408) }, |
{ USB_DEVICE(0x13d3, 0x3423) }, |
{ USB_DEVICE(0x13d3, 0x3432) }, |
{ USB_DEVICE(0x13d3, 0x3472) }, |
{ USB_DEVICE(0x13d3, 0x3474) }, |
{ USB_DEVICE(0x13d3, 0x3487) }, |
{ USB_DEVICE(0x13d3, 0x3490) }, |
/* Atheros AR5BBU12 with sflash firmware */ |
{ USB_DEVICE(0x0489, 0xE02C) }, |
/* Atheros AR5BBU22 with sflash firmware */ |
{ USB_DEVICE(0x0489, 0xE036) }, |
{ USB_DEVICE(0x0489, 0xE03C) }, |
{ } /* Terminating entry */ |
}; |
MODULE_DEVICE_TABLE(usb, ath3k_table); |
#defineBTUSB_ATH30120x80 |
/* This table is to load patch and sysconfig files |
* for AR3012 |
*/ |
staticconststruct usb_device_id ath3k_blist_tbl[] = { |
/* Atheros AR3012 with sflash firmware*/ |
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0x311E), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0x311F), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0CF3, 0x817b), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3487), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x13d3, 0x3490), .driver_info = BTUSB_ATH3012 }, |
/* Atheros AR5BBU22 with sflash firmware */ |
{ USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 }, |
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 }, |
{ } /* Terminating entry */ |
}; |
staticinlinevoidath3k_log_failed_loading(int err, int len, int size, |
int count) |
{ |
BT_ERR('Firmware loading err = %d, len = %d, size = %d, count = %d', |
err, len, size, count); |
} |
#defineUSB_REQ_DFU_DNLOAD1 |
#defineBULK_SIZE4096 |
#defineFW_HDR_SIZE20 |
#defineTIMEGAP_USEC_MIN50 |
#defineTIMEGAP_USEC_MAX100 |
staticintath3k_load_firmware(struct usb_device *udev, |
conststruct firmware *firmware) |
{ |
u8 *send_buf; |
int len = 0; |
int err, pipe, size, sent = 0; |
int count = firmware->size; |
BT_DBG('udev %p', udev); |
pipe = usb_sndctrlpipe(udev, 0); |
send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); |
if (!send_buf) { |
BT_ERR('Can't allocate memory chunk for firmware'); |
return -ENOMEM; |
} |
memcpy(send_buf, firmware->data, FW_HDR_SIZE); |
err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR, |
0, 0, send_buf, FW_HDR_SIZE, |
USB_CTRL_SET_TIMEOUT); |
if (err < 0) { |
BT_ERR('Can't change to loading configuration err'); |
goto error; |
} |
sent += FW_HDR_SIZE; |
count -= FW_HDR_SIZE; |
pipe = usb_sndbulkpipe(udev, 0x02); |
while (count) { |
/* workaround the compatibility issue with xHCI controller*/ |
usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX); |
size = min_t(uint, count, BULK_SIZE); |
memcpy(send_buf, firmware->data + sent, size); |
err = usb_bulk_msg(udev, pipe, send_buf, size, |
&len, 3000); |
if (err || (len != size)) { |
ath3k_log_failed_loading(err, len, size, count); |
goto error; |
} |
sent += size; |
count -= size; |
} |
error: |
kfree(send_buf); |
return err; |
} |
staticintath3k_get_state(struct usb_device *udev, unsignedchar *state) |
{ |
int ret, pipe = 0; |
char *buf; |
buf = kmalloc(sizeof(*buf), GFP_KERNEL); |
if (!buf) |
return -ENOMEM; |
pipe = usb_rcvctrlpipe(udev, 0); |
ret = usb_control_msg(udev, pipe, ATH3K_GETSTATE, |
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, |
buf, sizeof(*buf), USB_CTRL_SET_TIMEOUT); |
*state = *buf; |
kfree(buf); |
return ret; |
} |
staticintath3k_get_version(struct usb_device *udev, |
struct ath3k_version *version) |
{ |
int ret, pipe = 0; |
struct ath3k_version *buf; |
constint size = sizeof(*buf); |
buf = kmalloc(size, GFP_KERNEL); |
if (!buf) |
return -ENOMEM; |
pipe = usb_rcvctrlpipe(udev, 0); |
ret = usb_control_msg(udev, pipe, ATH3K_GETVERSION, |
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, |
buf, size, USB_CTRL_SET_TIMEOUT); |
memcpy(version, buf, size); |
kfree(buf); |
return ret; |
} |
staticintath3k_load_fwfile(struct usb_device *udev, |
conststruct firmware *firmware) |
{ |
u8 *send_buf; |
int len = 0; |
int err, pipe, size, count, sent = 0; |
int ret; |
count = firmware->size; |
send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); |
if (!send_buf) { |
BT_ERR('Can't allocate memory chunk for firmware'); |
return -ENOMEM; |
} |
size = min_t(uint, count, FW_HDR_SIZE); |
memcpy(send_buf, firmware->data, size); |
pipe = usb_sndctrlpipe(udev, 0); |
ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD, |
USB_TYPE_VENDOR, 0, 0, send_buf, |
size, USB_CTRL_SET_TIMEOUT); |
if (ret < 0) { |
BT_ERR('Can't change to loading configuration err'); |
kfree(send_buf); |
return ret; |
} |
sent += size; |
count -= size; |
pipe = usb_sndbulkpipe(udev, 0x02); |
while (count) { |
/* workaround the compatibility issue with xHCI controller*/ |
usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX); |
size = min_t(uint, count, BULK_SIZE); |
memcpy(send_buf, firmware->data + sent, size); |
err = usb_bulk_msg(udev, pipe, send_buf, size, |
&len, 3000); |
if (err || (len != size)) { |
ath3k_log_failed_loading(err, len, size, count); |
kfree(send_buf); |
return err; |
} |
sent += size; |
count -= size; |
} |
kfree(send_buf); |
return0; |
} |
staticintath3k_switch_pid(struct usb_device *udev) |
{ |
intpipe = 0; |
pipe = usb_sndctrlpipe(udev, 0); |
returnusb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID, |
USB_TYPE_VENDOR, 0, 0, |
NULL, 0, USB_CTRL_SET_TIMEOUT); |
} |
staticintath3k_set_normal_mode(struct usb_device *udev) |
{ |
unsignedchar fw_state; |
intpipe = 0, ret; |
ret = ath3k_get_state(udev, &fw_state); |
if (ret < 0) { |
BT_ERR('Can't get state to change to normal mode err'); |
return ret; |
} |
if ((fw_state & ATH3K_MODE_MASK) ATH3K_NORMAL_MODE) { |
BT_DBG('firmware was already in normal mode'); |
return0; |
} |
pipe = usb_sndctrlpipe(udev, 0); |
returnusb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE, |
USB_TYPE_VENDOR, 0, 0, |
NULL, 0, USB_CTRL_SET_TIMEOUT); |
} |
staticintath3k_load_patch(struct usb_device *udev) |
{ |
unsignedchar fw_state; |
char filename[ATH3K_NAME_LEN]; |
conststruct firmware *firmware; |
struct ath3k_version fw_version; |
__u32 pt_rom_version, pt_build_version; |
int ret; |
ret = ath3k_get_state(udev, &fw_state); |
if (ret < 0) { |
BT_ERR('Can't get state to change to load ram patch err'); |
return ret; |
} |
if (fw_state & ATH3K_PATCH_UPDATE) { |
BT_DBG('Patch was already downloaded'); |
return0; |
} |
ret = ath3k_get_version(udev, &fw_version); |
if (ret < 0) { |
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', |
le32_to_cpu(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_rom_version = get_unaligned_le32(firmware->data + |
firmware->size - 8); |
pt_build_version = get_unaligned_le32(firmware->data + |
firmware->size - 4); |
if (pt_rom_version != le32_to_cpu(fw_version.rom_version) || |
pt_build_version <= le32_to_cpu(fw_version.build_version)) { |
BT_ERR('Patch file version did not match with firmware'); |
release_firmware(firmware); |
return -EINVAL; |
} |
ret = ath3k_load_fwfile(udev, firmware); |
release_firmware(firmware); |
return ret; |
} |
staticintath3k_load_syscfg(struct usb_device *udev) |
{ |
unsignedchar fw_state; |
char filename[ATH3K_NAME_LEN]; |
conststruct firmware *firmware; |
struct ath3k_version fw_version; |
int clk_value, ret; |
ret = ath3k_get_state(udev, &fw_state); |
if (ret < 0) { |
BT_ERR('Can't get state to change to load configuration err'); |
return -EBUSY; |
} |
ret = ath3k_get_version(udev, &fw_version); |
if (ret < 0) { |
BT_ERR('Can't get version to change to load ram patch err'); |
return ret; |
} |
switch (fw_version.ref_clock) { |
case ATH3K_XTAL_FREQ_26M: |
clk_value = 26; |
break; |
case ATH3K_XTAL_FREQ_40M: |
clk_value = 40; |
break; |
case ATH3K_XTAL_FREQ_19P2: |
clk_value = 19; |
break; |
default: |
clk_value = 0; |
break; |
} |
snprintf(filename, ATH3K_NAME_LEN, 'ar3k/ramps_0x%08x_%d%s', |
le32_to_cpu(fw_version.rom_version), clk_value, '.dfu'); |
ret = request_firmware(&firmware, filename, &udev->dev); |
if (ret < 0) { |
BT_ERR('Configuration file not found %s', filename); |
return ret; |
} |
ret = ath3k_load_fwfile(udev, firmware); |
release_firmware(firmware); |
return ret; |
} |
staticintath3k_probe(struct usb_interface *intf, |
conststruct usb_device_id *id) |
{ |
conststruct firmware *firmware; |
struct usb_device *udev = interface_to_usbdev(intf); |
int ret; |
BT_DBG('intf %p id %p', intf, id); |
if (intf->cur_altsetting->desc.bInterfaceNumber != 0) |
return -ENODEV; |
/* match device ID in ath3k blacklist table */ |
if (!id->driver_info) { |
conststruct usb_device_id *match; |
match = usb_match_id(intf, ath3k_blist_tbl); |
if (match) |
id = match; |
} |
/* load patch and sysconfig files for AR3012 */ |
if (id->driver_info & BTUSB_ATH3012) { |
/* New firmware with patch and sysconfig files already loaded */ |
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001) |
return -ENODEV; |
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; |
} |
ret = ath3k_set_normal_mode(udev); |
if (ret < 0) { |
BT_ERR('Set normal mode failed'); |
return ret; |
} |
ath3k_switch_pid(udev); |
return0; |
} |
ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev); |
if (ret < 0) { |
if (ret -ENOENT) |
BT_ERR('Firmware file '%s' not found', |
ATH3K_FIRMWARE); |
else |
BT_ERR('Firmware file '%s' request failed (err=%d)', |
ATH3K_FIRMWARE, ret); |
return ret; |
} |
ret = ath3k_load_firmware(udev, firmware); |
release_firmware(firmware); |
return ret; |
} |
staticvoidath3k_disconnect(struct usb_interface *intf) |
{ |
BT_DBG('%s intf %p', __func__, intf); |
} |
staticstruct usb_driver ath3k_driver = { |
.name = 'ath3k', |
.probe = ath3k_probe, |
.disconnect = ath3k_disconnect, |
.id_table = ath3k_table, |
.disable_hub_initiated_lpm = 1, |
}; |
module_usb_driver(ath3k_driver); |
MODULE_AUTHOR('Atheros Communications'); |
MODULE_DESCRIPTION('Atheros AR30xx firmware driver'); |
MODULE_VERSION(VERSION); |
MODULE_LICENSE('GPL'); |
MODULE_FIRMWARE(ATH3K_FIRMWARE); |
How To Update Graphics Driver Windows 8
Copy lines Copy permalink