// 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/firmware.h>
#include <linux/usb.h>
#include <linux/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#define VERSION "1.0"
#define ATH3K_FIRMWARE "ath3k-1.fw"
#define ATH3K_DNLOAD 0 x01
#define ATH3K_GETSTATE 0 x05
#define ATH3K_SET_NORMAL_MODE 0 x07
#define ATH3K_GETVERSION 0 x09
#define USB_REG_SWITCH_VID_PID 0 x0a
#define ATH3K_MODE_MASK 0 x3F
#define ATH3K_NORMAL_MODE 0 x0E
#define ATH3K_PATCH_UPDATE 0 x80
#define ATH3K_SYSCFG_UPDATE 0 x40
#define ATH3K_XTAL_FREQ_26M 0 x00
#define ATH3K_XTAL_FREQ_40M 0 x01
#define ATH3K_XTAL_FREQ_19P2 0 x02
#define ATH3K_NAME_LEN 0 xFF
struct ath3k_version {
__le32 rom_version;
__le32 build_version;
__le32 ram_version;
__u8 ref_clock;
__u8 reserved[7 ];
} __packed;
static const struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 */
{ USB_DEVICE(0 x0CF3, 0 x3000) },
/* Atheros AR3011 with sflash firmware*/
{ USB_DEVICE(0 x0489, 0 xE027) },
{ USB_DEVICE(0 x0489, 0 xE03D) },
{ USB_DEVICE(0 x04F2, 0 xAFF1) },
{ USB_DEVICE(0 x0930, 0 x0215) },
{ USB_DEVICE(0 x0CF3, 0 x3002) },
{ USB_DEVICE(0 x0CF3, 0 xE019) },
{ USB_DEVICE(0 x13d3, 0 x3304) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0 x03F0, 0 x311D) },
/* Atheros AR3012 with sflash firmware*/
{ USB_DEVICE(0 x0489, 0 xe04d) },
{ USB_DEVICE(0 x0489, 0 xe04e) },
{ USB_DEVICE(0 x0489, 0 xe057) },
{ USB_DEVICE(0 x0489, 0 xe056) },
{ USB_DEVICE(0 x0489, 0 xe05f) },
{ USB_DEVICE(0 x0489, 0 xe076) },
{ USB_DEVICE(0 x0489, 0 xe078) },
{ USB_DEVICE(0 x0489, 0 xe095) },
{ USB_DEVICE(0 x04c5, 0 x1330) },
{ USB_DEVICE(0 x04CA, 0 x3004) },
{ USB_DEVICE(0 x04CA, 0 x3005) },
{ USB_DEVICE(0 x04CA, 0 x3006) },
{ USB_DEVICE(0 x04CA, 0 x3007) },
{ USB_DEVICE(0 x04CA, 0 x3008) },
{ USB_DEVICE(0 x04CA, 0 x300b) },
{ USB_DEVICE(0 x04CA, 0 x300d) },
{ USB_DEVICE(0 x04CA, 0 x300f) },
{ USB_DEVICE(0 x04CA, 0 x3010) },
{ USB_DEVICE(0 x04CA, 0 x3014) },
{ USB_DEVICE(0 x04CA, 0 x3018) },
{ USB_DEVICE(0 x0930, 0 x0219) },
{ USB_DEVICE(0 x0930, 0 x021c) },
{ USB_DEVICE(0 x0930, 0 x0220) },
{ USB_DEVICE(0 x0930, 0 x0227) },
{ USB_DEVICE(0 x0b05, 0 x17d0) },
{ USB_DEVICE(0 x0CF3, 0 x0036) },
{ USB_DEVICE(0 x0CF3, 0 x3004) },
{ USB_DEVICE(0 x0CF3, 0 x3008) },
{ USB_DEVICE(0 x0CF3, 0 x311D) },
{ USB_DEVICE(0 x0CF3, 0 x311E) },
{ USB_DEVICE(0 x0CF3, 0 x311F) },
{ USB_DEVICE(0 x0cf3, 0 x3121) },
{ USB_DEVICE(0 x0CF3, 0 x817a) },
{ USB_DEVICE(0 x0CF3, 0 x817b) },
{ USB_DEVICE(0 x0cf3, 0 xe003) },
{ USB_DEVICE(0 x0CF3, 0 xE004) },
{ USB_DEVICE(0 x0CF3, 0 xE005) },
{ USB_DEVICE(0 x0CF3, 0 xE006) },
{ USB_DEVICE(0 x13d3, 0 x3362) },
{ USB_DEVICE(0 x13d3, 0 x3375) },
{ USB_DEVICE(0 x13d3, 0 x3393) },
{ USB_DEVICE(0 x13d3, 0 x3395) },
{ USB_DEVICE(0 x13d3, 0 x3402) },
{ USB_DEVICE(0 x13d3, 0 x3408) },
{ USB_DEVICE(0 x13d3, 0 x3423) },
{ USB_DEVICE(0 x13d3, 0 x3432) },
{ USB_DEVICE(0 x13d3, 0 x3472) },
{ USB_DEVICE(0 x13d3, 0 x3474) },
{ USB_DEVICE(0 x13d3, 0 x3487) },
{ USB_DEVICE(0 x13d3, 0 x3490) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0 x0489, 0 xE02C) },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0 x0489, 0 xE036) },
{ USB_DEVICE(0 x0489, 0 xE03C) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ath3k_table);
#define BTUSB_ATH3012 0 x80
/* This table is to load patch and sysconfig files
* for AR3012
*/
static const struct usb_device_id ath3k_blist_tbl[] = {
/* Atheros AR3012 with sflash firmware*/
{ USB_DEVICE(0 x0489, 0 xe04e), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xe04d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xe095), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04c5, 0 x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x300b), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x300d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3014), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x04ca, 0 x3018), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0930, 0 x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0930, 0 x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0930, 0 x0220), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0930, 0 x0227), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0b05, 0 x17d0), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0CF3, 0 x0036), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 x311D), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 x311E), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 x311F), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 x3121), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0CF3, 0 x817a), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0CF3, 0 x817b), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 xe005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 xe006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0cf3, 0 xe003), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3395), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3432), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3472), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3474), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3487), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x13d3, 0 x3490), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0 x0489, 0 xE036), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0 x0489, 0 xE03C), .driver_info = BTUSB_ATH3012 },
{ } /* Terminating entry */
};
static inline void ath3k_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);
}
#define USB_REQ_DFU_DNLOAD 1
#define BULK_SIZE 4096
#define FW_HDR_SIZE 20
#define TIMEGAP_USEC_MIN 50
#define TIMEGAP_USEC_MAX 100
static int ath3k_load_firmware(struct usb_device *udev,
const struct firmware *firmware)
{
u8 *send_buf;
int len = 0 ;
int err, pipe, size, sent = 0 ;
int count = firmware->size;
BT_DBG("udev %p" , udev);
send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
if (!send_buf) {
BT_ERR("Can't allocate memory chunk for firmware" );
return -ENOMEM;
}
err = usb_control_msg_send(udev, 0 , USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR,
0 , 0 , firmware->data, FW_HDR_SIZE,
USB_CTRL_SET_TIMEOUT, GFP_KERNEL);
if (err) {
BT_ERR("Can't change to loading configuration err" );
goto error;
}
sent += FW_HDR_SIZE;
count -= FW_HDR_SIZE;
pipe = usb_sndbulkpipe(udev, 0 x02);
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;
}
static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
{
return usb_control_msg_recv(udev, 0 , ATH3K_GETSTATE,
USB_TYPE_VENDOR | USB_DIR_IN, 0 , 0 ,
state, 1 , USB_CTRL_SET_TIMEOUT,
GFP_KERNEL);
}
static int ath3k_get_version(struct usb_device *udev,
struct ath3k_version *version)
{
return usb_control_msg_recv(udev, 0 , ATH3K_GETVERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0 , 0 ,
version, sizeof (*version), USB_CTRL_SET_TIMEOUT,
GFP_KERNEL);
}
static int ath3k_load_fwfile(struct usb_device *udev,
const struct 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);
ret = usb_control_msg_send(udev, 0 , ATH3K_DNLOAD, USB_TYPE_VENDOR, 0 , 0 ,
firmware->data, size, USB_CTRL_SET_TIMEOUT,
GFP_KERNEL);
if (ret) {
BT_ERR("Can't change to loading configuration err" );
kfree(send_buf);
return ret;
}
sent += size;
count -= size;
pipe = usb_sndbulkpipe(udev, 0 x02);
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);
return 0 ;
}
static void ath3k_switch_pid(struct usb_device *udev)
{
usb_control_msg_send(udev, 0 , USB_REG_SWITCH_VID_PID, USB_TYPE_VENDOR,
0 , 0 , NULL, 0 , USB_CTRL_SET_TIMEOUT, GFP_KERNEL);
}
static int ath3k_set_normal_mode(struct usb_device *udev)
{
unsigned char fw_state;
int ret;
ret = ath3k_get_state(udev, &fw_state);
if (ret) {
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" );
return 0 ;
}
return usb_control_msg_send(udev, 0 , ATH3K_SET_NORMAL_MODE,
USB_TYPE_VENDOR, 0 , 0 , NULL, 0 ,
USB_CTRL_SET_TIMEOUT, GFP_KERNEL);
}
static int ath3k_load_patch(struct usb_device *udev)
{
unsigned char fw_state;
char filename[ATH3K_NAME_LEN];
const struct 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) {
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" );
return 0 ;
}
ret = ath3k_get_version(udev, &fw_version);
if (ret) {
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;
}
static int ath3k_load_syscfg(struct usb_device *udev)
{
unsigned char fw_state;
char filename[ATH3K_NAME_LEN];
const struct firmware *firmware;
struct ath3k_version fw_version;
int clk_value, ret;
ret = ath3k_get_state(udev, &fw_state);
if (ret) {
BT_ERR("Can't get state to change to load configuration err" );
return -EBUSY;
}
ret = ath3k_get_version(udev, &fw_version);
if (ret) {
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;
}
static int ath3k_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
const struct 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) {
const struct 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) > 0 x0001)
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) {
BT_ERR("Set normal mode failed" );
return ret;
}
ath3k_switch_pid(udev);
return 0 ;
}
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;
}
static void ath3k_disconnect(struct usb_interface *intf)
{
BT_DBG("%s intf %p" , __func__, intf);
}
static struct 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);
Messung V0.5 in Prozent C=95 H=91 G=92
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-06-06)
¤
*© Formatika GbR, Deutschland