Files

4601 lines
142 KiB
C

/*
* Copyright (c) 202X Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* Description:
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#include <linux/compat.h>
#include <linux/firmware.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/reboot.h>
#include <linux/time.h>
#include <linux/ktime.h>
#include <linux/kthread.h>
#include <linux/semaphore.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/amlogic/pm.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <linux/wait.h>
#include <linux/input.h>
#include <linux/version.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/clock.h>
#endif
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "common.h"
#include "w2_bt_entry.h"
#include "w2_usb_bt.h"
#include "rc_list.h"
#include "debug_dev.h"
/*------------------------------------------------------------------------------
Event FIFO, start:0x00514000, end:0x005151fc, length:4604 bytes
Rx Type FIFO Read Pointer : 0x00514000 [0,1,2,3]
HCI Event FIFO Read Pointer : 0x00514004 [4,5,6,7]
dummy : 0x00514008 [8,9,10,11]
rx data fifo r reg : 0x0051400c [12,13,14,15]
rx data fifo w reg : 0x00514010 [16,17,18,19]
sink mode status : 0x00514014 [20,21,22,23]
dummy : 0x00514018 [24,25,26,27]
dummy : 0x0051401c [28,29,30,31]
Rx Type FIFO Write Pointer : 0x00514020 [32,33,34,35]
HCI Event FIFO Write Pointer : 0x00514024 [36,37,38,39]
dummy : 0x00514028 [40,41,42,43]
dummy : 0x0051402c [44,45,46,47]
dummy : 0x00514030 [48,49,50,51]
dummy : 0x00514034 [52,53,54,55]
dummy : 0x00514038 [56,57,58,59]
Rx Type FIFO : 0x0051403c [256 bytes]
HCI Event FIFO : 0x0051413c [2048 bytes]
Tx Queue, start:0x00508000, end:0x0050a3fc, length:9212 bytes
Register RAM, start:0x00510000, end:0x00510200, length:512 bytes
HCI Command FIFO Read Pointer : 0x00510000
HCI Command FIFO Write Pointer: 0x00510004
Tx Queue Prio Pointer : 0x00510018
Tx Queue Acl Handle Pointer : 0x0051001c
Tx Queue Status Pointer : 0x00510020
Dummy : 0x00510024
Driver Firmware Status Pointer: 0x005101fc
Rx Queue, start:0x00500000, end:0x00501000, length:4096 bytes
Command FIFO, start:0x00518000, end:0x00519000, length:4096 bytes
---------------------------------------------------------------------------------*/
#define GDSL_TX_Q_MAX (8)
#define GDSL_TX_Q_USED (1)
#define GDSL_TX_Q_COMPLETE (2)
#define GDSL_TX_Q_UNUSED (0)
#define USB_TX_Q_NUM (8)
#define USB_TX_Q_LEN (1032)
#define USB_RX_Q_LEN (1032)
#define POLL_TOTAL_LEN (2364) //2048 + 316 bytes
#define RC_MANFDATA_LEN (256) //old (6*8)
#define HI_USB_RX_Q_ADDR 0x00500000 //length:4096 bytes
#define HI_USB_TX_Q_ADDR 0x00508000 //length:4096 bytes
#define HI_USB_MEM_ADDR 0x00510000 //length:512 bytes
#define HI_USB_EVENT_Q_ADDR 0x00514000 //length:2364 bytes
#define HI_USB_CMD_Q_ADDR 0x00518000 //length:4096 bytes
#define FIFO_FW_RX_TYPE_ADDR HI_USB_EVENT_Q_ADDR + 0x3c
#define FIFO_FW_RX_TYPE_LEN (256)
#define FIFO_FW_RX_TYPE_R HI_USB_EVENT_Q_ADDR
#define FIFO_FW_RX_TYPE_W HI_USB_EVENT_Q_ADDR + 0x20
#define FIFO_FW_EVT_ADDR HI_USB_EVENT_Q_ADDR + 0x13c
#define FIFO_FW_EVT_LEN (2048)
#define FIFO_FW_EVT_R HI_USB_EVENT_Q_ADDR + 0x04
#define FIFO_FW_EVT_W HI_USB_EVENT_Q_ADDR + 0x24
#define FIFO_FW_DATA_ADDR HI_USB_RX_Q_ADDR
#define FIFO_FW_DATA_LEN (USB_RX_Q_LEN * 4)
#define FIFO_FW_DATA_R HI_USB_EVENT_Q_ADDR + 0x0c
#define FIFO_FW_DATA_W HI_USB_EVENT_Q_ADDR + 0x10
#define FIFO_FW_CMD_ADDR HI_USB_CMD_Q_ADDR
#define FIFO_FW_CMD_LEN (4096)
#define FIFO_FW_CMD_R HI_USB_MEM_ADDR
#define FIFO_FW_CMD_W HI_USB_MEM_ADDR + 0x04
#define TX_Q_ADDR HI_USB_TX_Q_ADDR
#define TX_Q_PRIO_ADDR HI_USB_MEM_ADDR + 0x18
#define TX_Q_MAX_PRIO 0xFFFFFFFF
#define DRIVER_FW_STATUS 0x005101fc
#define SRAM_FD_INIT_FLAG (1 << 1)
// addr 0x514970 usb bulk 16 bit alignment
//#define FIFO_FW_MANFDATA_ADDR (HI_USB_EVENT_Q_ADDR + POLL_TOTAL_LEN + RC_MANFDATA_LEN + 4)
// addr 0x5149a0 usb bulk 16 bit alignment
//#define FIFO_FW_MAC_ADDR (HI_USB_EVENT_Q_ADDR + POLL_TOTAL_LEN + 2*RC_MANFDATA_LEN + 4)
#define FIFO_FW_MANFDATA_ADDR 0x514970 // addr 0x514970 usb bulk 16 bit alignment, mandata len 256bit
#define FIFO_FW_MAC_ADDR 0x514a70 // addr 0x514a70 usb bulk 16 bit alignment, mac len 6bit
// w2 download
#define AML_BT_CHAR_DEVICE_NAME "aml_btusb"
#define AML_BT_CONFIG_NAME "aml/aml_bt.conf"
#define AML_BT_FIRMWARE_NAME "aml/w2_bt_fw_usb.bin"
#define AML_BT_REVC_FIRMWARE_NAME "aml/w2_bt_revC_fw_usb.bin"
#define AML_BT_FIRMWARE_FT_NAME "aml/w2_bt_fw_usb_test.bin"
#define AML_BT_REVC_FIRMWARE_FT_NAME "aml/w2_bt_revC_fw_usb_test.bin"
// download param
#define ICCM_SIZE 0x40000
#define DCCM_SIZE 0x20000
#define ICCM_ROM_SIZE 256*1024
#define DOWNLOAD_SIZE 4096
#define ICCM_RAM_BASE (0x000000)
#define DCCM_RAM_BASE (0xd00000)
#define BT_ICCM_AHB_BASE 0x00300000
#define BT_DCCM_AHB_BASE 0x00400000
#define MAX_DOWNLOAD_RETRY 10
#define DEBUG_DCCM_ADDR 0x40a4f8 //Store information about the firmware crash
#ifndef BIT
#define BIT(_n) (1 << (_n))
#endif
#define BT_DRV_STATE_SUSPEND_ENTRY BIT(0)
#define BT_DRV_STATE_SUSPEND BIT(1)
#define BT_DRV_STATE_RESUME BIT(2)
#define BT_DRV_STATE_RECOVERY BIT(3)
#define BT_DRV_STATE_SENDING BIT(4)
#define BT_DRV_STATE_WAIT_RECOVERY BIT(5)
enum bt_drv_state
{
BT_DRV_NONE,
BT_DRV_CLOSED,
BT_DRV_SUSPEND_ENTRY,
BT_DRV_SUSPEND,
BT_DRV_RESUME_ENTRY,
BT_DRV_RESUME,
BT_DRV_RESUME_COMP,
};
//vendor cmd
#define REG_DEV_RESET 0xf03058
#define REG_FW_MODE 0xf000e0
#define REG_PMU_POWER_CFG 0xf03040
#define REG_RAM_PD_SHUTDWONW_SW 0xf03050
#define REG_FW_PC 0x200034
#define REG_DF_A194 0xf06308
#define BIT_PHY 1
#define BIT_MAC (1 << 1)
#define BIT_CPU (1 << 2)
#define BIT_RF_NUM 28
#define BT_SINK_MODE 25
#define CHIP_BT_PMU_REG_BASE (0xf03000)
#define RG_BT_PMU_A11 (CHIP_BT_PMU_REG_BASE + 0x2c)
#define RG_BT_PMU_A12 (CHIP_BT_PMU_REG_BASE + 0x30)
#define RG_BT_PMU_A13 (CHIP_BT_PMU_REG_BASE + 0x34)
#define RG_BT_PMU_A14 (CHIP_BT_PMU_REG_BASE + 0x38)
#define RG_BT_PMU_A15 (CHIP_BT_PMU_REG_BASE + 0x3c)
#define RG_BT_PMU_A16 (CHIP_BT_PMU_REG_BASE + 0x40)
#define RG_BT_PMU_A17 (CHIP_BT_PMU_REG_BASE + 0x44)
#define RG_BT_PMU_A18 (CHIP_BT_PMU_REG_BASE + 0x48)
#define RG_BT_PMU_A20 (CHIP_BT_PMU_REG_BASE + 0x50)
#define RG_BT_PMU_A22 (CHIP_BT_PMU_REG_BASE + 0x58)
#define CHIP_INTF_REG_BASE (0xf00000)
#define RG_AON_A15 (CHIP_INTF_REG_BASE + 0x3c)
#define RG_AON_A16 (CHIP_INTF_REG_BASE + 0x40)
#define RG_AON_A17 (CHIP_INTF_REG_BASE + 0x44)
#define RG_AON_A24 (CHIP_INTF_REG_BASE + 0x60)
#define RG_AON_A30 (CHIP_INTF_REG_BASE + 0x78)
#define RG_AON_A52 (CHIP_INTF_REG_BASE + 0xd0)
#define RG_AON_A53 (CHIP_INTF_REG_BASE + 0xd4)
/*********amlbt_tool*********/
#define RG_AON_A55 (CHIP_INTF_REG_BASE + 0xdc)
#define RG_AON_A56 (CHIP_INTF_REG_BASE + 0xe0)
#define RG_AON_A57 (CHIP_INTF_REG_BASE + 0xe4)
#define RG_AON_A58 (CHIP_INTF_REG_BASE + 0xe8)
#define RG_AON_A59 (CHIP_INTF_REG_BASE + 0xec)
#define RG_AON_A60 (CHIP_INTF_REG_BASE + 0xf0)
#define RG_AON_A61 (CHIP_INTF_REG_BASE + 0xf4)
#define RG_AON_A62 (CHIP_INTF_REG_BASE + 0xf8)
// pmu status
#define PMU_PWR_OFF 0x0
#define PMU_PWR_XOSC 0x1
#define PMU_XOSC_WAIT 0x2
#define PMU_XOSC_DPLL 0x3
#define PMU_DPLL_WAIT 0x4
#define PMU_DPLL_ACT 0x5
#define PMU_ACT_MODE 0x6
#define PMU_ACT_SLEEP 0x7
#define PMU_SLEEP_MODE 0x8
#define PMU_SLEEP_WAKE 0x9
#define PMU_WAKE_WAIT 0xa
#define PMU_WAKE_XOSC 0xb
enum wifi_cmd {
CMD_DOWNLOAD_WIFI = 0xC1,
CMD_START_WIFI,
CMD_STOP_WIFI,
CMD_READ_REG,
CMD_WRITE_REG,
CMD_READ_PACKET,
CMD_WRITE_PACKET,
CMD_WRITE_SRAM,
CMD_READ_SRAM,
CMD_DOWNLOAD_BT,
CMD_GET_TX_CFM,
CMD_OTHER_CMD,
CMD_USB_IRQ
};
#define AML_SIG_CBW 0x43425355
#define AML_XFER_TO_DEVICE 0
#define AML_XFER_TO_HOST 0x80
#define AML_USB_CONTROL_MSG_TIMEOUT 3000
#define WRITE_SRAM_DATA_LEN 477
//bluez param
//Transmit states
#define XMIT_SENDING 1
#define XMIT_WAKEUP 2
#define XMIT_WAITING 8
//Receiver states
#define RECV_WAIT_PACKET_TYPE 0
#define RECV_WAIT_EVENT_HEADER 1
#define RECV_WAIT_ACL_HEADER 2
#define RECV_WAIT_SCO_HEADER 3
#define RECV_WAIT_DATA 4
//system state
enum bt_system_state{
W2_BT_SYSTEM_RESUME,
W2_BT_SYSTEM_SUSPEND,
};
enum bt_rx_state{
HCI_RX_TYPE,
HCI_RX_HEADER,
HCI_RX_PAYLOAD,
HCI_RX_FATAL,
};
struct crg_msc_cbw {
unsigned int sig;
unsigned int tag;
unsigned int data_len;
unsigned char flag;
unsigned char lun;
unsigned char len;
unsigned int cdb[4];
unsigned char resv[481];
}__attribute__ ((packed));
//driver get hci dev info
struct aml_bt_info {
struct hci_dev *hdev;
struct platform_device *pdev;
struct workqueue_struct *workqueue;
//struct work_struct receive_work;
struct delayed_work receive_work;
unsigned int reg_flag;
spinlock_t lock; /* For serializing operations */
struct sk_buff_head txq;
unsigned long tx_state;
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
};
struct aml_bus_state_detect {
unsigned char bus_err;
unsigned char is_drv_load_finished;
unsigned char bus_reset_ongoing;
unsigned char is_load_by_timer;
unsigned char is_recy_ongoing;
struct timer_list timer;
struct work_struct detect_work;
int (*insmod_drv)(void);
};
//static struct mutex bt_usb_mutex;
extern struct mutex auc_usb_mutex;
extern struct usb_device *g_udev;
extern struct aml_pm_type g_wifi_pm;
extern unsigned int g_aml_device_id;
extern struct aml_bus_state_detect bus_state_detect;
extern unsigned char g_chip_function_ctrl;
//extern bt_shutdown_func g_bt_shutdown_func;
static struct crg_msc_cbw *g_cmd_buf;
static unsigned char *g_bluez_buf = NULL;
static struct aml_bt_info *info;
static w2_usb_bt_t amlbt_dev = {0};
static unsigned int suspend_value = 0;
//extern unsigned char aml_wifi_detect_bt_status __attribute__((weak));
#ifdef CONFIG_AMLOGIC_GX_SUSPEND
extern unsigned int get_resume_method(void);
#endif
#define USB_BEGIN_LOCK() do {\
mutex_lock(&auc_usb_mutex);\
} while (0)
#define USB_END_LOCK() do {\
mutex_unlock(&auc_usb_mutex);\
} while (0)
static void amlbt_release(struct device *dev);
static void amlbt_shutdown_func(void);
static int amlbt_probe(struct platform_device *dev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
static int amlbt_remove(struct platform_device *dev);
#else
static void amlbt_remove(struct platform_device *dev);
#endif
static int amlbt_suspend(struct platform_device *dev, pm_message_t state);
static int amlbt_resume(struct platform_device *dev);
static void amlbt_shutdown(struct platform_device *dev);
static int amlbt_char_open(struct inode *inode, struct file *file);
static int amlbt_char_close(struct inode *inode, struct file *file);
static ssize_t amlbt_char_write(struct file *file_p, const char __user *buf_p, size_t count, loff_t *pos_p);
static ssize_t amlbt_char_read(struct file *file_p, char __user *buf_p, size_t count, loff_t *pos_p);
static int amlbt_submit_poll_urb(w2_usb_bt_t *p_bt);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
static void amlbt_lateresume(struct early_suspend *h);
static void amlbt_earlysuspend(struct early_suspend *h);
#endif
static int amlbt_download_firmware(w2_usb_bt_t *p_bt);
static int amlbt_load_firmware(w2_usb_bt_t *p_bt);
static int amlbt_load_conf(w2_usb_bt_t *p_bt);
static int amlbt_res_init(w2_usb_bt_t *p_bt);
static void amlbt_res_deinit(w2_usb_bt_t *p_bt);
static int amlbt_task_start(w2_usb_bt_t *p_bt);
static void amlbt_kill_irq_task(w2_usb_bt_t *p_bt);
static int amlbt_hci_dev_init(struct platform_device *dev);
static void amlbt_hci_dev_deinit(void);
static int amlbt_register_hci_dev(void);
static int amlbt_sw_reset(void);
static int amlbt_bind_bus(void);
static void amlbt_unbind_bus(void);
static void amlbt_release(struct device *dev)
{
return;
}
static struct platform_device amlbt_device =
{
.name = "aml_btusb",
.id = -1,
.dev = {
.release = &amlbt_release,
}
};
static struct platform_driver amlbt_driver =
{
.probe = amlbt_probe,
.remove = amlbt_remove,
.suspend = amlbt_suspend,
.resume = amlbt_resume,
.shutdown = amlbt_shutdown,
.driver = {
.name = "aml_btusb",
.owner = THIS_MODULE,
},
};
static const struct file_operations amlbt_fops =
{
.open = amlbt_char_open,
.release = amlbt_char_close,
.write = amlbt_char_write,
.read = amlbt_char_read,
};
static void auc_build_cbw(struct crg_msc_cbw *cbw_buf,
unsigned char dir,
unsigned int len,
unsigned char cdb1,
unsigned int cdb2,
unsigned long cdb3,
unsigned long cdb4)
{
cbw_buf->sig = AML_SIG_CBW;
cbw_buf->tag = 0x5da729a0;
cbw_buf->data_len = len;
cbw_buf->flag = dir; //direction
cbw_buf->len = 16; //command length
cbw_buf->lun = 0;
cbw_buf->cdb[0] = cdb1;
cbw_buf->cdb[1] = cdb2; // read or write addr
cbw_buf->cdb[2] = (unsigned int)(unsigned long)cdb3;
cbw_buf->cdb[3] = cdb4; //read or write data length
}
static void auc_build_cbw_add_data(struct crg_msc_cbw *cbw_buf,
unsigned char dir,
unsigned int len,
unsigned char cdb1,
unsigned int cdb2,
unsigned long cdb3,
SYS_TYPE cdb4,unsigned char *data)
{
cbw_buf->sig = AML_SIG_CBW;
cbw_buf->tag = 0x5da729a0;
cbw_buf->data_len = len;
cbw_buf->flag = dir; //direction
cbw_buf->len = 16; //command length
cbw_buf->lun = 0;
cbw_buf->cdb[0] = cdb1;
cbw_buf->cdb[1] = cdb2; // read or write addr
cbw_buf->cdb[2] = (unsigned int)(unsigned long)cdb3;
cbw_buf->cdb[3] = cdb4; //read or write data length
memcpy(cbw_buf->resv + 1, (unsigned char *) data, len);
/*in case call cmd and data mode but fw call cmd+data stage*/
cbw_buf->resv[479] = cbw_buf->resv[480] = 0xFF;
}
static void get_btwakeup_work(unsigned int key)
{
switch (key)
{
case 0:
{
BTI("default value \n");
}
break;
case 1:
{
input_event(amlbt_dev.amlbt_input_dev, EV_KEY, KEY_POWER, 1);
input_sync(amlbt_dev.amlbt_input_dev);
input_event(amlbt_dev.amlbt_input_dev, EV_KEY, KEY_POWER, 0);
input_sync(amlbt_dev.amlbt_input_dev);
amlbt_dev.input_key = 0;
BTF("%s input power key\n", __func__);
}
break;
case 2:
{
input_event(amlbt_dev.amlbt_input_dev, EV_KEY, KEY_NETFLIX, 1);
input_sync(amlbt_dev.amlbt_input_dev);
input_event(amlbt_dev.amlbt_input_dev, EV_KEY, KEY_NETFLIX, 0);
input_sync(amlbt_dev.amlbt_input_dev);
amlbt_dev.input_key = 0;
BTF("%s input Netflix key\n", __func__);
}
break;
default:
BTF("No identification key\n");
break;
}
}
static void amlbt_drv_state_set(unsigned int bit)
{
unsigned int reg_value = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s\n", __func__);
reg_value = p_bt->dr_state;
BTD("amlbt_drv_state_set %#x: %#x\n", reg_value, bit);
reg_value |= bit;
BTD("amlbt_drv_state_set end %#x: %#x", reg_value, bit);
p_bt->dr_state = reg_value;
}
static void amlbt_drv_state_clr(unsigned int bit)
{
unsigned int reg_value = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s\n", __func__);
reg_value = p_bt->dr_state;
BTD("amlbt_drv_state_clr %#x: %#x\n", reg_value, bit);
reg_value &= ~bit;
BTD("amlbt_drv_state_clr end %#x: %#x", reg_value, bit);
p_bt->dr_state = reg_value;
}
static int amlbt_check_usb(void)
{
int wait_cnt = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
BTA("%s\n", __func__);
if (g_udev == NULL)
{
BTE("interface NULL");
return -1;
}
while (bus_state_detect.bus_err || bus_state_detect.bus_reset_ongoing || bus_state_detect.is_recy_ongoing || (enum usb_udev_state)g_udev->state != USB_CONFIGURED)
{
wait_cnt = 0;
BTI("recy start");
if (p_bt->firmware_start)
{
up(&amlbt_dev.sr_sem);//prevent use sr_sem before init.
}
amlbt_wakeup_lock();
while (bus_state_detect.bus_err || bus_state_detect.bus_reset_ongoing || bus_state_detect.is_recy_ongoing || (enum usb_udev_state)g_udev->state != USB_CONFIGURED)
{
usleep_range(20000, 20000);
if (wait_cnt++ >= 500)
{
BTE("bus_err %d reset %d recy %d udev %d", bus_state_detect.bus_err, bus_state_detect.bus_reset_ongoing,
bus_state_detect.is_recy_ongoing, g_udev->state);
break;
}
}
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
BTI("recy end");
return -1;
}
return 0;
}
static int auc_write_reg_by_ep(unsigned int addr, unsigned int value, unsigned int len, unsigned int ep)
{
int ret = 0;
int actual_length = 0;
USB_BEGIN_LOCK();
memset(g_cmd_buf, 0, sizeof(*g_cmd_buf));
auc_build_cbw(g_cmd_buf, AML_XFER_TO_DEVICE, 0, CMD_WRITE_REG, addr, value, len);
/* cmd stage */
ret = usb_bulk_msg(g_udev, (unsigned int)usb_sndbulkpipe(g_udev, ep), (void *)g_cmd_buf, sizeof(*g_cmd_buf), &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_write_reg_by_ep Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d, value: 0x%x\n", ret, ep, addr, len, value);
USB_END_LOCK();
return ret;
}
USB_END_LOCK();
return 0; //bt write maybe use the value
}
static int amlbt_write_word(unsigned int addr,unsigned int data, unsigned int ep)
{
int len = 4;
int ret = 0;
if (amlbt_dev.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, amlbt_dev.dr_state);
return -2;
}
ret = amlbt_check_usb();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
ret = auc_write_reg_by_ep(addr, data, len, ep);
if (ret != 0)
{
return ret;
}
break;
default:
BTE("EP-%d unsupported!\n", ep);
break;
}
return 0;
}
static int auc_read_reg_by_ep(unsigned int addr, unsigned int len, unsigned int ep, unsigned int *value)
{
int ret = 0;
int actual_length = 0;
unsigned char *data = NULL;
USB_BEGIN_LOCK();
data = (unsigned char *)kzalloc(len, GFP_KERNEL);
if (!data) {
BTE("auc_read_reg_by_ep data malloc fail, ep: %d, addr: 0x%x, len: %d\n", ep, addr, len);
goto err_unlock;
}
memset(g_cmd_buf, 0, sizeof(*g_cmd_buf));
auc_build_cbw(g_cmd_buf, AML_XFER_TO_HOST, len, CMD_READ_REG, addr, 0, len);
/* cmd stage */
ret = usb_bulk_msg(g_udev, usb_sndbulkpipe(g_udev, ep), (void *)g_cmd_buf, sizeof(*g_cmd_buf), &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_read_reg_by_ep cmd Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d\n", ret, ep, addr, len);
goto err_kfree;
}
/* data stage */
ret = usb_bulk_msg(g_udev, usb_rcvbulkpipe(g_udev, ep), (void *)data, len, &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_read_reg_by_ep data Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d\n", ret ,ep, addr, len);
goto err_kfree;
}
if (actual_length > len)
{
BTE("%s:%d, error! actual_length %#x\n", __func__, __LINE__, actual_length);
goto err_kfree;
}
memcpy(value, data, actual_length);
kfree(data);
USB_END_LOCK();
return ret;
err_kfree:
kfree(data);
err_unlock:
USB_END_LOCK();
return ret;
}
static int amlbt_read_word(unsigned int addr, unsigned int ep, unsigned int *value)
{
int len = 4;
int ret = 0;
if (amlbt_dev.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, amlbt_dev.dr_state);
return -2;
}
ret = amlbt_check_usb();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
ret = auc_read_reg_by_ep(addr, len, ep, value);
if (ret != 0)
{
return ret;
}
break;
default:
BTE("EP-%d unsupported!\n", ep);
break;
}
return ret;
}
static int auc_write_sram_by_ep(unsigned char *pdata, unsigned int addr, unsigned int len, unsigned int ep)
{
int ret = 0;
int actual_length = 0;
unsigned char *kmalloc_buf = NULL;
USB_BEGIN_LOCK();
//if (len < WRITE_SRAM_DATA_LEN)
if (0)
{
memset(g_cmd_buf, 0, sizeof(*g_cmd_buf));
auc_build_cbw_add_data(g_cmd_buf, AML_XFER_TO_DEVICE, len, CMD_WRITE_SRAM, addr, 0, len, pdata);
/* cmd stage */
ret = usb_bulk_msg(g_udev, usb_sndbulkpipe(g_udev, ep), (void *)g_cmd_buf, sizeof(*g_cmd_buf), &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_write_sram_by_ep 1 Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d\n", ret, ep, addr, len);
BTE("usb command transmit fail,g_cmd_buf->add is %d,len is %d\n", addr, len);
goto err_unlock;
}
g_cmd_buf->resv[479] = g_cmd_buf->resv[480] = 0;
}
else
{
memset(g_cmd_buf, 0, sizeof(*g_cmd_buf));
auc_build_cbw(g_cmd_buf, AML_XFER_TO_DEVICE, len, CMD_WRITE_SRAM, addr, 0, len);
/* cmd stage */
ret = usb_bulk_msg(g_udev, usb_sndbulkpipe(g_udev, ep), (void *)g_cmd_buf, sizeof(*g_cmd_buf), &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_write_sram_by_ep 2 Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d\n", ret, ep, addr, len);
BTE("usb command transmit fail,g_cmd_buf->add is %d,len is %d\n", addr, len);
goto err_unlock;
}
kmalloc_buf = (unsigned char *)kzalloc(len, GFP_KERNEL);
if (kmalloc_buf == NULL)
{
BTE("kmalloc buf fail, ep: %d, addr: 0x%x, len: %d\n", ep, addr, len);
goto err_unlock;
}
memcpy(kmalloc_buf, pdata, len);
/* data stage */
ret = usb_bulk_msg(g_udev, usb_sndbulkpipe(g_udev, ep), (void *)kmalloc_buf, len, &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_write_sram_by_ep data Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d\n", ret, ep, addr, len);
goto err_kfree;
}
kfree(kmalloc_buf);
}
USB_END_LOCK();
return ret;
err_kfree:
kfree(kmalloc_buf);
err_unlock:
USB_END_LOCK();
return ret;
}
static int amlbt_write_sram(unsigned char *buf, unsigned char *sram_addr, unsigned int len, unsigned int ep)
{
int ret = 0;
if (amlbt_dev.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, amlbt_dev.dr_state);
return -2;
}
if (len == 0)
{
BTE("EP-%d write len err!\n", ep);
return -1;
}
ret = amlbt_check_usb();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
ret = auc_write_sram_by_ep(buf, (unsigned int)(unsigned long)sram_addr, len, ep);
if (ret != 0)
{
return ret;
}
break;
default:
BTE("EP-%d unsupported!\n", ep);
break;
}
return ret;
}
static int auc_read_sram_by_ep(unsigned char *pdata, unsigned int addr, unsigned int len, unsigned int ep)
{
int ret = 0;
int actual_length = 0;
unsigned char *kmalloc_buf = NULL;
USB_BEGIN_LOCK();
memset(g_cmd_buf, 0, sizeof(*g_cmd_buf));
auc_build_cbw(g_cmd_buf, AML_XFER_TO_HOST, len, CMD_READ_SRAM, addr, 0, len);
/* cmd stage */
ret = usb_bulk_msg(g_udev, usb_sndbulkpipe(g_udev, ep), (void *)g_cmd_buf, sizeof(*g_cmd_buf), &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_read_sram_by_ep cmd Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d\n", ret, ep, addr, len);
BTE("usb command transmit fail,g_cmd_buf->add is %d,len is %d\n",addr,len);
goto err_unlock;
}
kmalloc_buf = (unsigned char *)kzalloc(len, GFP_KERNEL);
if (kmalloc_buf == NULL)
{
BTE("kmalloc buf fail, ep: %d, len: %d\n", ep, len);
goto err_unlock;
}
/* data stage */
ret = usb_bulk_msg(g_udev, usb_rcvbulkpipe(g_udev, ep),(void *)kmalloc_buf, len, &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("auc_read_sram_by_ep cmd Failed to usb_bulk_msg, ret %d, ep: %d, addr: 0x%x, len: %d\n", ret, ep, addr, len);
goto err_kfree;
}
if (actual_length > len)
{
BTE("%s:%d, error! actual_length %#x\n", __func__, __LINE__, actual_length);
goto err_kfree;
}
memcpy(pdata, kmalloc_buf, actual_length);
kfree(kmalloc_buf);
USB_END_LOCK();
return ret;
err_kfree:
kfree(kmalloc_buf);
err_unlock:
USB_END_LOCK();
return ret;
}
static int amlbt_read_sram(unsigned char *buf,unsigned char *sram_addr, unsigned int len, unsigned int ep)
{
int ret = 0;
if (amlbt_dev.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, amlbt_dev.dr_state);
return -2;
}
if (len == 0)
{
BTE("EP-%d read len err!\n", ep);
return -1;
}
ret = amlbt_check_usb();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
ret = auc_read_sram_by_ep(buf, (unsigned int)(unsigned long)sram_addr, len, ep);
if (ret != 0)
{
return ret;
}
break;
default:
BTE("EP-%d unsupported!\n", ep);
break;
}
return ret;
}
static int amlbt_aon_addr_bit_set(unsigned int addr, unsigned int bit)
{
int ret = 0;
unsigned int reg_value = 0;
ret = amlbt_read_word(addr, USB_EP2, &reg_value);
if (ret != 0)
{
goto err_exit;
}
BTI("%#x: %#x\n", addr, reg_value);
reg_value |= BIT(bit);
ret = amlbt_write_word(addr, reg_value, USB_EP2);
if (ret != 0)
{
goto err_exit;
}
ret = amlbt_read_word(addr, USB_EP2, &reg_value);
if (ret != 0)
{
goto err_exit;
}
BTI("%#x: %#x", addr, reg_value);
return ret;
err_exit:
return ret;
}
static int amlbt_aon_addr_bit_clr(unsigned int addr, unsigned int bit)
{
unsigned int reg_value = 0;
int ret = 0;
ret = amlbt_read_word(addr, USB_EP2, &reg_value);
if (ret != 0)
{
goto err_exit;
}
BTI("%#x: %#x\n", addr, reg_value);
reg_value &= ~BIT(bit);
ret = amlbt_write_word(addr, reg_value, USB_EP2);
if (ret != 0)
{
goto err_exit;
}
ret = amlbt_read_word(addr, USB_EP2, &reg_value);
if (ret != 0)
{
goto err_exit;
}
BTI("%#x: %#x", addr, reg_value);
return 0;
err_exit:
return ret;
}
static int amlbt_aon_addr_bit_get(unsigned int addr, unsigned int bit)
{
unsigned int reg_value = 0;
int bit_value = 0;
int ret;
ret = amlbt_read_word(addr, USB_EP2, &reg_value);
if (ret == -1)
{
return ret;
}
bit_value = (reg_value >> bit) & 0x1;
BTI("get %#x bit%d: %#x\n", addr, bit, bit_value);
return bit_value;
}
static unsigned int amlbt_fw_pmu_sleep_get(void)
{
unsigned int reg_value = 0;
amlbt_read_word(RG_BT_PMU_A15, USB_EP2, &reg_value);
BTI("%s PMU FSM %#x\n", __func__, (reg_value & 0xF));
if (((reg_value & 0xF) == PMU_SLEEP_MODE) || ((reg_value & 0xF) == PMU_ACT_SLEEP))
{
return TRUE;
}
else
{
return FALSE;
}
}
static int amlbt_wake_fw(void)
{
unsigned int reg_value = 0;
int ret = 0;
ret = amlbt_read_word(RG_BT_PMU_A16, USB_EP2, &reg_value);
if (ret != 0)
{
goto err_exit;
}
reg_value &= ~BIT(0);
reg_value |= BIT(1);
ret = amlbt_write_word(RG_BT_PMU_A16, reg_value, USB_EP2);
if (ret != 0)
{
goto err_exit;
}
ret = amlbt_read_word(RG_BT_PMU_A16, USB_EP2, &reg_value);
if (ret != 0)
{
goto err_exit;
}
BTF("%s RG_BT_PMU_A16 %#x\n", __func__, reg_value);
return ret;
err_exit:
return ret;
}
static int amlbt_powersave_clear(void)
{
int wait_cnt = 0;
int ret = 0;
BTF("%s\n", __func__);
// clear shutdown bit
ret = amlbt_aon_addr_bit_clr(RG_AON_A24, 27);
if (ret != 0)
{
goto exit;
}
// clear suspend bit
ret = amlbt_aon_addr_bit_clr(RG_AON_A24, 26);
if (ret != 0)
{
goto exit;
}
// wake bt fw
if (amlbt_fw_pmu_sleep_get())
{
usleep_range(1000, 1000);
ret = amlbt_wake_fw();
if (ret != 0)
{
goto exit;
}
}
// wait bt wake done 1s
do
{
ret = amlbt_aon_addr_bit_get(RG_AON_A17, 29);
if (ret == -1)
{
goto exit;
}
usleep_range(20000, 20000);
if (wait_cnt++ > 50)
break;
} while (ret);
return 0;
exit:
return ret;
}
static int amlbt_resume_fw(void)
{
int wait_cnt = 0;
int retry_cnt = 0;
int ret = -1;
BTF("%s\n", __func__);
//wait usb bus ready
if ((atomic_read(&g_wifi_pm.bus_suspend_cnt) == 0 && (enum usb_udev_state)g_udev->state == USB_CONFIGURED)
|| bus_state_detect.is_recy_ongoing)
{
BTF("%s g_wifi_pm.bus_suspend_cnt 0\n",__func__);
//forbid fw sleep
ret = amlbt_aon_addr_bit_set(RG_AON_A24, 25);
if (ret != 0)
{
goto error;
}
usleep_range(1000, 1000);
// wake bt fw
wake_retry:
if (amlbt_fw_pmu_sleep_get() == TRUE)
{
usleep_range(1000, 1000);
ret = amlbt_wake_fw();
if (ret != 0)
{
goto error;
}
}
wait_cnt = 0;
// wait bt fw wake done
while (amlbt_aon_addr_bit_get(RG_AON_A17, 29)) //fw will clear bit after wake done
{
usleep_range(10000, 10000);
if (wait_cnt++ > 5)//wait 50ms
{
BTE("%s wake fw failed\n", __func__);
if (retry_cnt++ < 3)
goto wake_retry;
break;
}
}
amlbt_clear_rclist_from_firmware();
ret = 0;
}
return ret;
error:
return ret;
}
static int amlbt_suspend_fw(w2_usb_bt_t *p_bt)
{
int ret = 0;
BTF("%s\n", __func__);
ret = amlbt_write_rclist_to_firmware();
if (ret != 0)
{
goto err_exit;
}
//set suspend bit
ret = amlbt_aon_addr_bit_set(RG_AON_A24, 26);
if (ret != 0)
{
goto err_exit;
}
//allow fw sleep
ret = amlbt_aon_addr_bit_clr(RG_AON_A24, 25);
if (ret != 0)
{
goto err_exit;
}
return ret;
err_exit:
return ret;
}
static int amlbt_write_manfdata_to_firmware(w2_usb_bt_t *p_bt)
{
int ret = 0;
int i = 0;
unsigned int total_len = 0;
unsigned char total_manf_data[RC_MANFDATA_LEN] = {0};
unsigned char p_manfdata[16][16] = {0};
size_t max_groups = 16;
unsigned char len = 0;
char line[128] = {0};
char tmp[8];
size_t pos = 0;
int b = 0;
memcpy(p_manfdata, p_bt->new_manfdata, sizeof(p_manfdata));
BTI("Start writing %d groups (TLV) to firmware", p_bt->manfdata_len);
for (i = 0; i < p_bt->manfdata_len && i < max_groups; i++) {
len = p_manfdata[i][0] + 1;
if (len == 0 || len > 16) {
BTE("Invalid TLV length %u at group %d", len, i);
continue;
}
if ((total_len + len) > RC_MANFDATA_LEN) {
BTE("Buffer overflow at group %d! total_len=%u, len=%u",
i, total_len, len);
break;
}
memcpy(&total_manf_data[total_len], p_manfdata[i], len);
total_len += len;
memset(line, 0, sizeof(line));
pos = 0;
for (b = 0; b < len; b++) {
snprintf(tmp, sizeof(tmp), "%02X ", p_manfdata[i][b]);
pos += snprintf(line + pos, sizeof(line) - pos, "%s", tmp);
}
BTI("Group %d (len=%u): %s", i, len, line);
}
BTI("Total %u bytes TLV data to write", total_len);
printk(KERN_CONT "manfdata:[ ");
for (i = 0; i < total_len; i++)
printk(KERN_CONT "%02x ", total_manf_data[i]);
printk(KERN_CONT "]\n");
ret = amlbt_write_sram(total_manf_data,
(unsigned char *)FIFO_FW_MANFDATA_ADDR,
total_len, USB_EP2);
BTI("Write TLV manfdata done, total_len=%u, ret=%d", total_len, ret);
return ret;
}
static int amlbt_write_macaddr_to_firmware(w2_usb_bt_t *p_bt)
{
int ret = 0;
ret = amlbt_write_sram(p_bt->mac_addr, (unsigned char *)FIFO_FW_MAC_ADDR, 6, USB_EP2);
BTI("macaddr:[%#x,%#x,%#x,%#x,%#x,%#x]\n", p_bt->mac_addr[0], p_bt->mac_addr[1], p_bt->mac_addr[2],
p_bt->mac_addr[3], p_bt->mac_addr[4], p_bt->mac_addr[5]);
BTI("mac addr end");
return ret;
}
static int amlbt_recy_shutdown_download(void)
{
int ret = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
//stop cpu
ret = amlbt_sw_reset();
if (ret != 0)
{
goto exit;
}
//init fifo
if (amlbt_res_init(p_bt) != 0)
{
BTI("amlbt_res_init failed!\n");
goto err_buf;
}
//read config
amlbt_load_conf(p_bt);
//download fw
ret = amlbt_load_firmware(p_bt);
if (ret != 0)
{
BTI("amlbt_load_firmware failed!\n");
goto err_buf;
}
//set mac addr
ret = amlbt_write_macaddr_to_firmware(p_bt);
if (ret != 0)
{
goto err_buf;
}
//set rc list
ret = amlbt_write_rclist_to_firmware();
if (ret != 0)
{
goto err_buf;
}
ret = amlbt_write_word(REG_DEV_RESET, 0, USB_EP2);
if (ret != 0)
{
goto err_buf;
}
amlbt_res_deinit(p_bt);
BTI("%s end\n", __func__);
return 0;
err_buf:
amlbt_res_deinit(p_bt);
exit:
return ret;
}
static void amlbt_shutdown_func(void)
{
int ret = 0;
BTF("%s\n", __func__);
#if 0
amlbt_drv_state_clr(BT_DRV_STATE_SUSPEND);
BTI("driver state %d amlbt_dev.firmware %d", amlbt_dev.dr_state, amlbt_dev.firmware_start);
do
{
ret = amlbt_recy_shutdown_download();
if (ret != 0)
{
continue;
}
ret = amlbt_aon_addr_bit_set(RG_AON_A24, 27);
}while (ret != 0);
#endif
amlbt_write_rclist_to_firmware();
amlbt_aon_addr_bit_set(RG_AON_A24, 27);
BTI("%s end\n", __func__);
}
static unsigned int gdsl_write_data_by_ep(gdsl_fifo_t *p_fifo, unsigned char *data, unsigned int len, unsigned int ep)
{
int ret = 0;
unsigned long offset = (unsigned long)p_fifo->w;
BTA("%s len:%d\n", __func__, len);
len = ((len + 3) & 0xFFFFFFFC);
if (gdsl_fifo_remain(p_fifo) < len)
{
BTE("write data no space!!\n");
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
return -1;
}
if (len < (p_fifo->size - offset))
{
ret = amlbt_write_sram(data, (unsigned char *)((unsigned long)p_fifo->w + (unsigned long)p_fifo->base_addr), len, ep);
if (ret != 0)
{
BTE("%s:%d Failed : %d len %d\n", __func__, __LINE__, ret, len);
return -1;
}
p_fifo->w = (unsigned char *)(((unsigned long)p_fifo->w + len) % p_fifo->size);
}
else
{
ret = amlbt_write_sram(data, (unsigned char *)((unsigned long)p_fifo->w + (unsigned long)p_fifo->base_addr),
p_fifo->size - offset, ep);
if (ret != 0)
{
BTE("%s:%d Failed : %d len %d\n", __func__, __LINE__, ret, len);
return -1;
}
if ((len - (p_fifo->size - offset)) != 0)
{
ret = amlbt_write_sram(&data[p_fifo->size - offset], p_fifo->base_addr, (len - (p_fifo->size - offset)), ep);
if (ret != 0)
{
BTE("%s:%d Failed : %d len %d\n", __func__, __LINE__, ret, len);
return -1;
}
}
p_fifo->w = (unsigned char *)((len - (p_fifo->size - offset)) % p_fifo->size);
}
return len;
}
static void amlbt_mutex_init(w2_usb_bt_t *p_bt)
{
BTF("%s \n", __func__);
mutex_init(&p_bt->dr_type_fifo_mutex);
mutex_init(&p_bt->dr_evt_fifo_mutex);
mutex_init(&p_bt->dr_data_fifo_mutex);
}
static void amlbt_mutex_deinit(w2_usb_bt_t *p_bt)
{
BTF("%s \n", __func__);
mutex_destroy(&p_bt->dr_type_fifo_mutex);
mutex_destroy(&p_bt->dr_evt_fifo_mutex);
mutex_destroy(&p_bt->dr_data_fifo_mutex);
}
static void amlbt_cmd_buf_init(void)
{
BTF("%s\n", __func__);
g_cmd_buf = kzalloc(sizeof(*g_cmd_buf), GFP_DMA | GFP_ATOMIC);
if (!g_cmd_buf) {
BTE("%s:%d g_cmd_buf kzalloc failed!\n", __func__, __LINE__);
return;
}
}
static void amlbt_cmd_buf_deinit(void)
{
BTF("%s\n", __func__);
if (g_cmd_buf != NULL)
{
kfree(g_cmd_buf);
g_cmd_buf = NULL;
}
}
static void amlbt_driver_init_flag_set(void)
{
unsigned int st_reg = 0;
BTF("%s \n", __func__);
amlbt_read_word(DRIVER_FW_STATUS, USB_EP2, &st_reg);
st_reg |= SRAM_FD_INIT_FLAG;
amlbt_write_word(DRIVER_FW_STATUS, st_reg, USB_EP2);
}
static void amlbt_driver_init_flag_clear(void)
{
unsigned int st_reg = 0;
BTF("%s \n", __func__);
st_reg &= ~(SRAM_FD_INIT_FLAG);
amlbt_write_word(DRIVER_FW_STATUS, st_reg, USB_EP2);
}
static gdsl_fifo_t *amlbt_fifo_init(gdsl_fifo_t **p_fifo, unsigned int len, unsigned char *base_addr,
unsigned int r_point_addr, unsigned int w_point_addr)
{
int ret = 0;
unsigned int w = 0;
unsigned int r = 0;
if (*p_fifo == NULL)
{
*p_fifo = gdsl_fifo_init(len, base_addr);
if (*p_fifo != NULL)
{
amlbt_write_word(r_point_addr, (unsigned int)(unsigned long)(*p_fifo)->r, USB_EP2);
amlbt_write_word(w_point_addr, (unsigned int)(unsigned long)(*p_fifo)->w, USB_EP2);
ret = amlbt_read_word(r_point_addr, USB_EP2, &r);
BTI("%s, %d, %#x r:%#lx", __func__, ret, r_point_addr, r);
ret = amlbt_read_word(w_point_addr, USB_EP2, &w);
BTI("%s, %d, %#x w:%#lx", __func__, ret, w_point_addr, w);
}
}
return *p_fifo;
}
static void amlbt_fifo_deinit(gdsl_fifo_t **p_fifo, w2_usb_bt_t *p_bt, unsigned int r_point_addr, unsigned int w_point_addr)
{
if (!(p_bt->dr_state & BT_DRV_STATE_RECOVERY))
{
amlbt_write_word(r_point_addr, 0, USB_EP2);
amlbt_write_word(w_point_addr, 0, USB_EP2);
}
if (*p_fifo != NULL)
{
gdsl_fifo_deinit(*p_fifo);
*p_fifo = NULL;
}
}
static void amlbt_res_deinit(w2_usb_bt_t *p_bt)
{
unsigned int st_reg = 0;
BTF("%s \n", __func__);
amlbt_mutex_deinit(p_bt);
if (g_bluez_buf != NULL)
{
kfree(g_bluez_buf);
g_bluez_buf = NULL;
}
if (p_bt->usb_rx_buf != NULL)
{
kfree(p_bt->usb_rx_buf);
p_bt->usb_rx_buf = NULL;
}
if (!(p_bt->dr_state & BT_DRV_STATE_RECOVERY))
{
amlbt_read_word(DRIVER_FW_STATUS, USB_EP2, &st_reg);
st_reg |= SRAM_FD_INIT_FLAG;
amlbt_write_word(DRIVER_FW_STATUS, st_reg, USB_EP2);
}
amlbt_fifo_deinit(&p_bt->fw_type_fifo, p_bt, FIFO_FW_RX_TYPE_R, FIFO_FW_RX_TYPE_W);
amlbt_fifo_deinit(&p_bt->fw_evt_fifo, p_bt, FIFO_FW_EVT_R, FIFO_FW_EVT_W);
amlbt_fifo_deinit(&p_bt->fw_data_fifo, p_bt, FIFO_FW_DATA_R, FIFO_FW_DATA_W);
gdsl_fifo_deinit(p_bt->dr_type_fifo);
gdsl_fifo_deinit(p_bt->dr_evt_fifo);
gdsl_fifo_deinit(p_bt->dr_data_fifo);
amlbt_fifo_deinit(&p_bt->tx_cmd_fifo, p_bt, FIFO_FW_CMD_R, FIFO_FW_CMD_W);
if (!(p_bt->dr_state & BT_DRV_STATE_RECOVERY))
{
st_reg &= ~(SRAM_FD_INIT_FLAG);
amlbt_write_word(DRIVER_FW_STATUS, st_reg, USB_EP2);
}
if (p_bt->bt_urb != NULL)
{
usb_free_urb(p_bt->bt_urb);
p_bt->bt_urb = NULL;
}
//mutex_destroy(&bt_usb_mutex);
skb_queue_purge(&p_bt->bt_rx_queue);
p_bt->firmware_start = 0;
p_bt->dr_state = 0;
BTF("%s finished \n", __func__);
}
static int amlbt_res_init(w2_usb_bt_t *p_bt)
{
unsigned int i = 0;
unsigned int st_reg = 0;
unsigned int tx_info[USB_TX_Q_NUM * 4] = {0};
unsigned char buf[2*RC_MANFDATA_LEN] = {0};
BTF("%s \n", __func__);
//mutex_init(&bt_usb_mutex);
p_bt->antenna = 2;
p_bt->fw_mode = 1;
p_bt->bt_sink = 0;
p_bt->pin_mux = 0;
p_bt->br_digit_gain = 66;
p_bt->edr_digit_gain = 98;
p_bt->fw_log = 0;
p_bt->driver_log = 3;
p_bt->factory = 0;
p_bt->dr_state = 0;
p_bt->rd_state = 0;
p_bt->sink_mode = 0;
p_bt->hardware_error = 0;
p_bt->isolation = 0;
p_bt->manf_cnt = 0;
p_bt->system = 0;
p_bt->project = 0;
amlbt_mutex_init(p_bt);
//add driver init flag to fw
amlbt_driver_init_flag_set();
//alloc send usb cmd buf
g_bluez_buf = kzalloc(HCI_MAX_FRAME_SIZE, GFP_DMA | GFP_ATOMIC);
if (!g_bluez_buf) {
BTE("%s:%d g_cmd_buf kzalloc failed!\n", __func__, __LINE__);
goto error;
}
p_bt->usb_rx_buf = kzalloc(POLL_TOTAL_LEN, GFP_DMA|GFP_ATOMIC);
if (!p_bt->usb_rx_buf)
{
BTE("%s:%d usb_rx_buf failed!\n", __func__, __LINE__);
goto error;
}
//fw type fifo init
if (NULL == amlbt_fifo_init(&p_bt->fw_type_fifo, FIFO_FW_RX_TYPE_LEN, (unsigned char *)FIFO_FW_RX_TYPE_ADDR,
FIFO_FW_RX_TYPE_R, FIFO_FW_RX_TYPE_W))
{
BTE("%s:%d fw type fifo init failed!\n", __func__, __LINE__);
goto error;
}
//fw event fifo init
if (NULL == amlbt_fifo_init(&p_bt->fw_evt_fifo, FIFO_FW_EVT_LEN, (unsigned char *)FIFO_FW_EVT_ADDR,
FIFO_FW_EVT_R, FIFO_FW_EVT_W))
{
BTE("%s:%d fw event fifo init failed!\n", __func__, __LINE__);
goto error;
}
//mac & rclist & mandata addr all init
amlbt_write_sram(buf, (unsigned char *)FIFO_FW_RC_LIST_ADDR, sizeof(buf), USB_EP2);
//fw data fifo init
if (NULL == amlbt_fifo_init(&p_bt->fw_data_fifo, FIFO_FW_DATA_LEN, (unsigned char *)FIFO_FW_DATA_ADDR,
FIFO_FW_DATA_R, FIFO_FW_DATA_W))
{
BTE("%s:%d fw data fifo init failed!\n", __func__, __LINE__);
goto error;
}
//driver local type fifo init
p_bt->dr_type_fifo = gdsl_fifo_init(sizeof(p_bt->dr_type_fifo_buf), p_bt->dr_type_fifo_buf);
if (p_bt->dr_type_fifo == NULL)
{
BTE("%s:%d driver local type fifo init failed!\n", __func__, __LINE__);
goto error;
}
//driver local event fifo init
p_bt->dr_evt_fifo = gdsl_fifo_init(sizeof(p_bt->dr_evt_fifo_buf), p_bt->dr_evt_fifo_buf);
if (p_bt->dr_evt_fifo == NULL)
{
BTE("%s:%d driver local event fifo init failed!\n", __func__, __LINE__);
goto error;
}
//driver local data fifo init
p_bt->dr_data_fifo = gdsl_fifo_init(sizeof(p_bt->dr_data_fifo_buf), p_bt->dr_data_fifo_buf);
if (p_bt->dr_data_fifo == NULL)
{
BTE("%s:%d driver local data fifo init failed!\n", __func__, __LINE__);
goto error;
}
//tx hci cmd fifo init
if (NULL == amlbt_fifo_init(&p_bt->tx_cmd_fifo, FIFO_FW_CMD_LEN, (unsigned char *)FIFO_FW_CMD_ADDR,
FIFO_FW_CMD_R, FIFO_FW_CMD_W))
{
BTE("%s:%d tx hci cmd fifo init failed!\n", __func__, __LINE__);
goto error;
}
for (i = 0; i < USB_TX_Q_NUM; i++)
{
p_bt->tx_q[i].tx_q_addr = (unsigned char *)(unsigned long)(HI_USB_TX_Q_ADDR + i * USB_TX_Q_LEN);
p_bt->tx_q[i].tx_q_prio_addr = (unsigned int *)(unsigned long)(TX_Q_PRIO_ADDR + i * 16);
p_bt->tx_q[i].tx_q_dev_index_addr = (unsigned int *)((unsigned long)p_bt->tx_q[i].tx_q_prio_addr + 4);
p_bt->tx_q[i].tx_q_status_addr = (unsigned int *)((unsigned long)p_bt->tx_q[i].tx_q_dev_index_addr + 4);
p_bt->tx_q[i].tx_q_dev_index = 0;
p_bt->tx_q[i].tx_q_prio = TX_Q_MAX_PRIO;
p_bt->tx_q[i].tx_q_status = GDSL_TX_Q_UNUSED;
tx_info[i*4] = p_bt->tx_q[i].tx_q_prio;
tx_info[i*4+1] = p_bt->tx_q[i].tx_q_dev_index;
tx_info[i*4+2] = p_bt->tx_q[i].tx_q_status;
BTP("tx_addr:%#lx,%#lx,%#lx\n", (unsigned long)p_bt->tx_q[i].tx_q_prio_addr,
(unsigned long)p_bt->tx_q[i].tx_q_dev_index_addr,
(unsigned long)p_bt->tx_q[i].tx_q_status_addr);
}
amlbt_write_sram((unsigned char *)tx_info, (unsigned char *)TX_Q_PRIO_ADDR, sizeof(tx_info), USB_EP2);
//driver init flag clear
amlbt_driver_init_flag_clear();
p_bt->bt_urb = usb_alloc_urb(0, GFP_ATOMIC);
if (p_bt->bt_urb == NULL)
{
BTE("%s:%d p_bt->bt_urb == NULL!!!\n", __func__, __LINE__);
goto error;
}
spin_lock_init(&p_bt->bt_rx_queue_lock);
skb_queue_head_init(&p_bt->bt_rx_queue);
init_completion(&p_bt->comp);
//init_completion(&p_bt->w_comp);
//init_completion(&p_bt->r_comp);
sema_init(&p_bt->sr_sem, 1);
init_waitqueue_head(&p_bt->rd_wait_queue);
return 0;
error:
amlbt_res_deinit(p_bt);
return -1;
}
static int amlbt_create_device(w2_usb_bt_t *p_bt)
{
int ret = 0;
int cdevErr = 0;
dev_t dev = 0;
BTF("%s \n", __func__);
ret = alloc_chrdev_region(&dev, 0, 1, AML_BT_CHAR_DEVICE_NAME);
if (ret)
{
BTE("fail to allocate chrdev\n");
return ret;
}
p_bt->dev_major = MAJOR(dev);
BTI("major number:%d\n", p_bt->dev_major);
cdev_init(&p_bt->dev_cdev, &amlbt_fops);
p_bt->dev_cdev.owner = THIS_MODULE;
cdevErr = cdev_add(&p_bt->dev_cdev, dev, 1);
if (cdevErr)
{
goto error;
}
BTI("driver(major %d) installed.\n", p_bt->dev_major);
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
p_bt->dev_class = class_create(THIS_MODULE, AML_BT_CHAR_DEVICE_NAME);
#else
p_bt->dev_class = class_create(AML_BT_CHAR_DEVICE_NAME);
#endif
if (IS_ERR(p_bt->dev_class))
{
BTE("class create fail, error code(%ld)\n", PTR_ERR(p_bt->dev_class));
goto err1;
}
p_bt->dev_device = device_create(p_bt->dev_class, NULL, dev, NULL, AML_BT_CHAR_DEVICE_NAME);
if (IS_ERR(p_bt->dev_device))
{
BTE("device create fail, error code(%ld)\n", PTR_ERR(p_bt->dev_device));
goto err2;
}
BTI("%s: BT_major %d\n", __func__, p_bt->dev_major);
BTI("%s: dev id %d\n", __func__, dev);
return 0;
err2:
if (p_bt->dev_class)
{
class_destroy(p_bt->dev_class);
p_bt->dev_class = NULL;
}
err1:
error:
if (cdevErr == 0)
cdev_del(&p_bt->dev_cdev);
if (ret == 0)
unregister_chrdev_region(dev, 1);
return -1;
}
static int amlbt_destroy_device(w2_usb_bt_t *p_bt)
{
dev_t dev = MKDEV(p_bt->dev_major, 0);
BTF("%s dev id %d\n", __func__, dev);
if (p_bt->dev_device)
{
device_destroy(p_bt->dev_class, dev);
p_bt->dev_device = NULL;
}
if (p_bt->dev_class)
{
class_destroy(p_bt->dev_class);
p_bt->dev_class = NULL;
}
cdev_del(&p_bt->dev_cdev);
unregister_chrdev_region(dev, 1);
BTI("%s driver removed.\n", AML_BT_CHAR_DEVICE_NAME);
return 0;
}
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
static void amlbt_earlysuspend(struct early_suspend *h)
{
BTF("%s \n", __func__);
}
static void amlbt_lateresume(struct early_suspend *h)
{
int wait_cnt = 0;
unsigned int reg_value = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
p_bt->input_key = 0;
BTF("%s %#x,%#x\n", __func__, p_bt->firmware_start, p_bt->dr_state);
if (p_bt->firmware_start && !(p_bt->dr_state & BT_DRV_STATE_RECOVERY))
{
while (p_bt->dr_state & BT_DRV_RESUME)
{
usleep_range(10000, 10000); //wait resume 1s
wait_cnt++;
if (wait_cnt > 100)
{
BTW("%s:%d amlbt_lateresume timeout!\n", __func__, __LINE__);
break;
}
}
amlbt_read_word(RG_AON_A24, USB_EP2, &reg_value);
BTI("%s RG_AON_A24:%#x\n", __func__, reg_value);
reg_value &= ~(1 << 26);
amlbt_write_word(RG_AON_A24, reg_value, USB_EP2);
amlbt_read_word(RG_AON_A24, USB_EP2, &reg_value);
BTI("RG_AON_A24:%#x\n", reg_value);
}
BTF("%s end\n", __func__);
}
static void amlbt_register_early_suspend(struct platform_device *dev)
{
BTF("%s \n", __func__);
amlbt_dev.early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
amlbt_dev.early_suspend.suspend = amlbt_earlysuspend;
amlbt_dev.early_suspend.resume = amlbt_lateresume;
amlbt_dev.early_suspend.param = dev;
register_early_suspend(&amlbt_dev.early_suspend);
}
static void amlbt_unregister_early_suspend(struct platform_device *dev)
{
BTF("%s \n", __func__);
unregister_early_suspend(&amlbt_dev.early_suspend);
}
#endif
static int amlbt_input_device_init(struct platform_device *pdev)
{
int err;
amlbt_dev.amlbt_input_dev = input_allocate_device();
if (!amlbt_dev.amlbt_input_dev)
{
BTF("[abner test]input_allocate_device failed:");
return -EINVAL;
}
set_bit(EV_KEY, amlbt_dev.amlbt_input_dev->evbit);
set_bit(KEY_POWER, amlbt_dev.amlbt_input_dev->keybit);
set_bit(KEY_NETFLIX, amlbt_dev.amlbt_input_dev->keybit);
amlbt_dev.amlbt_input_dev->name = INPUT_NAME;
amlbt_dev.amlbt_input_dev->phys = INPUT_PHYS;
amlbt_dev.amlbt_input_dev->dev.parent = &pdev->dev;
amlbt_dev.amlbt_input_dev->id.bustype = BUS_ISA;
amlbt_dev.amlbt_input_dev->id.vendor = 0x0001;
amlbt_dev.amlbt_input_dev->id.product = 0x0001;
amlbt_dev.amlbt_input_dev->id.version = 0x0100;
amlbt_dev.amlbt_input_dev->rep[REP_DELAY] = 0xffffffff;
amlbt_dev.amlbt_input_dev->rep[REP_PERIOD] = 0xffffffff;
amlbt_dev.amlbt_input_dev->keycodesize = sizeof(unsigned short);
amlbt_dev.amlbt_input_dev->keycodemax = 0x1ff;
err = input_register_device(amlbt_dev.amlbt_input_dev);
if (err < 0)
{
pr_err("[abner test]input_register_device failed: %d\n", err);
input_free_device(amlbt_dev.amlbt_input_dev);
return -EINVAL;
}
return err;
}
#if 0
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
w2_usb_bt_t *p_bt = &amlbt_dev;
if (p_bt->dr_state == 0)
{
schedule_work(&amlbt_dev.rx_work);
}
return IRQ_HANDLED;
}
static int amlbt_register_interrupt_gpio(w2_usb_bt_t *p_bt)
{
struct device_node *node;
int gpio_num;
int irq;
int ret;
BTI("%s \n", __func__);
node = of_find_node_by_name(NULL, "aml_bt");
if (!node)
{
BTE("Failed to find device node\n");
return -1;
}
BTI("%s find node success!\n", __func__);
gpio_num = of_get_named_gpio(node, "btwakeup-gpios", 0);
if (gpio_num < 0)
{
BTE("Failed to get GPIO\n");
return -1;
}
BTI("%s find gpio %d success!\n", __func__, gpio_num);
irq = gpio_to_irq(gpio_num);
if (irq < 0)
{
BTE("Failed to get IRQ for GPIO\n");
gpio_free(gpio_num);
return -1;
}
BTI("%s find irq %d success!\n", __func__, irq);
ret = request_irq(irq, gpio_irq_handler, IRQF_TRIGGER_FALLING, "usb_gpio_irq", p_bt->dev_device);
if (ret)
{
BTE("Failed to request IRQ %d: %d\n", irq, ret);
gpio_free(gpio_num);
return -1;
}
BTI("%s request irq %d success!\n", __func__, irq);
p_bt->irq = irq;
return 0;
}
static void amlbt_unregister_interrupt_gpio(w2_usb_bt_new_t *p_bt)
{
if (p_bt->irq > 0)
{
free_irq(p_bt->irq, p_bt->dev_device);
p_bt->irq = -1;
}
}
#endif
static int amlbt_probe(struct platform_device *dev)
{
//w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s \n", __func__);
g_bt_shutdown_func = amlbt_shutdown_func;
amlbt_create_device(&amlbt_dev);
amlbt_debug_level_init();
amlbt_rc_list_init(amlbt_dev.dev_device, amlbt_write_sram, amlbt_read_sram, NULL, NULL);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
amlbt_register_early_suspend(dev);
#endif
//amlbt_dev->wake_mux = 0;
amlbt_cmd_buf_init();
amlbt_hci_dev_init(dev);
//input devices
amlbt_input_device_init(dev);
return 0;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
static int amlbt_remove(struct platform_device *dev)
#else
static void amlbt_remove(struct platform_device *dev)
#endif
{
w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s \n", __func__);
//task quit
amlbt_kill_irq_task(p_bt);
input_unregister_device(amlbt_dev.amlbt_input_dev);
amlbt_dev.amlbt_input_dev = NULL;
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
amlbt_unregister_early_suspend(dev);
#endif
amlbt_debug_level_deinit();
amlbt_rc_list_deinit(amlbt_dev.dev_device);
amlbt_destroy_device(&amlbt_dev);
//amlbt_res_deinit(&amlbt_dev);//no need to clear wake_mux
amlbt_cmd_buf_deinit();
amlbt_hci_dev_deinit();
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
amlbt_unbind_bus();
#endif
if (g_bt_shutdown_func != NULL)
{
g_bt_shutdown_func = NULL;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
return 0;
#endif
}
static int amlbt_suspend(struct platform_device *dev, pm_message_t state)
{
int ret = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
p_bt->input_key = 0;
BTF("%s %#x,%#x\n", __func__, p_bt->firmware_start, p_bt->dr_state);
if (p_bt->firmware_start && !(p_bt->dr_state & BT_DRV_STATE_RECOVERY))
{
ret = down_interruptible(&p_bt->sr_sem);
if (ret == 0)
{
suspend_value = 1;
amlbt_drv_state_set(BT_DRV_STATE_SUSPEND_ENTRY);
BTF("%s %#x suspend_value %d\n", __func__, p_bt->dr_state,suspend_value);
}
else
{
/* interrupted, exit */
BTE("%s:%d %d wait sr_sem fail!\n", __func__, __LINE__, ret);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
return -1;
}
}
BTF("%s end\n", __func__);
return 0;
}
static int amlbt_resume(struct platform_device *dev)
{
int ret = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s %#x,%#x\n", __func__, p_bt->firmware_start, p_bt->dr_state);
if (p_bt->firmware_start && !(p_bt->dr_state & BT_DRV_STATE_RECOVERY))
{
ret = down_interruptible(&p_bt->sr_sem);
if (ret == 0)
{
suspend_value = 0;
BTF("%s start suspend_value %d\n", __func__,suspend_value);
amlbt_drv_state_set(BT_DRV_STATE_RESUME);
amlbt_drv_state_clr(BT_DRV_STATE_SUSPEND);
#ifdef CONFIG_AMLOGIC_GX_SUSPEND
BTF("WAKE REASON %d\n", get_resume_method());
if (((get_resume_method() != REMOTE_WAKEUP) && (get_resume_method() != BT_WAKEUP))
&& (get_resume_method() != REMOTE_CUS_WAKEUP))
{
p_bt->input_key = 1;
}
#endif
}
else
{
/* interrupted, exit */
BTE("%s:%d %d wait sr_sem fail!\n", __func__, __LINE__, ret);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
return -1;
}
}
BTF("%s end\n", __func__);
return 0;
}
static void amlbt_shutdown(struct platform_device *dev)
{
BTF("%s \n", __func__);
#if 0
amlbt_write_word(RG_BT_PMU_A16, 0, USB_EP2);
ret = amlbt_aon_addr_bit_set(RG_AON_A24, 27);
if (ret != 0)
{
BTE("%s:%d %d shutdown fail!\n", __func__, __LINE__, ret);
}
#endif
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
static int amlbt_bind_bus(void)
{
w2_usb_bt_t *p_bt = &amlbt_dev;
if (g_udev == NULL)
{
BTE("g_udev is NULL");
return -ENOMEM;
}
else
{
if (p_bt->link == NULL)
{
p_bt->link = device_link_add(&amlbt_device.dev, &g_udev->dev, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
if (p_bt->link == NULL)
{
BTE("Failed to create device link");
return -ENOMEM;
}
else
{
BTF("Success to create device link");
}
}
}
return 0;
}
static struct device_link *find_device_link(struct device *consumer, struct device *supplier)
{
struct device_link *link;
list_for_each_entry(link, &consumer->links.suppliers, c_node)
{
if (link->supplier == supplier)
{
return link;
}
}
return NULL;
}
static void amlbt_unbind_bus(void)
{
struct device_link *link = NULL;
w2_usb_bt_t *p_bt = &amlbt_dev;
if (p_bt->link != NULL)
{
if (g_udev && device_is_registered(&g_udev->dev))
{
link = find_device_link(&amlbt_device.dev, &g_udev->dev);
BTI("find_device_link : %#x", (unsigned long)link);
}
if (link != NULL && link == p_bt->link)
{
device_link_del(p_bt->link);
BTI("Success to del device link");
}
p_bt->link = NULL;
}
}
#endif
static int amlbt_sw_reset(void)
{
int ret = 0;
ret = amlbt_write_word(REG_DEV_RESET, ((BIT_PHY|BIT_CPU)<<16)|(BIT_PHY|BIT_CPU), USB_EP2);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto error;
}
usleep_range(1000, 1000);
ret = amlbt_write_word(REG_DEV_RESET, ((BIT_CPU)<<16)|(BIT_CPU), USB_EP2);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto error;
}
return ret;
error:
return ret;
}
static void amlbt_get_manfdata(const char *ptr, const char *key)
{
w2_usb_bt_t *p_bt = &amlbt_dev;
unsigned char (*target)[16] = NULL;
size_t max_groups = 16;
size_t max_bytes_per_group = 16;
size_t len = strlen(ptr);
size_t group = 0;
size_t byte = 0;
char sub_str[3] = {0};
size_t i = 0;
size_t g = 0;
char line[256] = {0};
char tmp[8];
size_t pos = 0;
size_t b = 0;
if (strcmp(key, "W1UManfData") == 0) {
target = p_bt->new_manfdata;
} else {
BTE("amlbt_get_manfdata key value error! Expected 'W1UManfData'\n");
return;
}
BTI("PTR %s len %zu", ptr, len);
for (i = 0; i + 1 < len && group < max_groups; ) {
if (ptr[i] == ' ') {
group++;
byte = 0;
i++;
continue;
}
if (byte >= max_bytes_per_group)
break;
sub_str[0] = ptr[i];
sub_str[1] = ptr[i+1];
target[group][byte++] = (unsigned char)simple_strtoul(sub_str, NULL, 16);
i += 2;
}
p_bt->manfdata_len = group + 1;
BTI("Parsed %zu groups", p_bt->manfdata_len);
// printf param
for (g = 0; g < p_bt->manfdata_len; g++) {
memset(line, 0, sizeof(line));
pos = 0;
for (b = 0; b < max_bytes_per_group; b++) {
if (b > 0 && target[g][b] == 0 && target[g][b-1] == 0)
break;
snprintf(tmp, sizeof(tmp), "%02X ", target[g][b]);
pos += snprintf(line + pos, sizeof(line) - pos, "%s", tmp);
}
BTI("Group %zu: %s", g, line);
}
}
static int parse_int_value(char *start, const char *key, int *value)
{
size_t key_len = strlen(key);
char *ptr = NULL;
if (strncmp(start, key, key_len) == 0 && start[key_len] == '=')
{
ptr = (start + key_len + 1);
if (strcmp(key, "W1UManfData") == 0)
{
amlbt_get_manfdata(ptr, key);
}
else
{
*value = simple_strtol(ptr, NULL, 10);
}
return 1;
}
return 0;
}
static int amlbt_load_conf(w2_usb_bt_t *p_bt)
{
int ret = 0;
const struct firmware *fw_entry = NULL;
char *data;
size_t len, pos = 0;
BTF("Firmware load:%s\n", AML_BT_CONFIG_NAME);
ret = request_firmware(&fw_entry, AML_BT_CONFIG_NAME, p_bt->dev_device);
if (ret)
{
BTE("%s:%d Failed to load config file: %d\n", __func__, __LINE__, ret);
return -EINVAL;
}
if (!fw_entry || !fw_entry->data)
{
BTE("Failed to load conf or data is empty\n");
release_firmware(fw_entry);
return -EINVAL;
}
data = (char *)fw_entry->data;
len = fw_entry->size;
// Manual parsing loop
while (pos < len)
{
char *line_start = data + pos;
char *line_end = strchr(line_start, '\n'); // Find end of line
if (!line_end)
{
line_end = data + len; // If no newline, this is the last line
}
*line_end = '\0'; // Null-terminate the current line
// Parse known keys
if (parse_int_value(line_start, "BtAntenna", &p_bt->antenna))
{
BTI("Parsed BtAntenna: %d\n", p_bt->antenna);
}
else if (parse_int_value(line_start, "FirmwareMode", &p_bt->fw_mode))
{
BTI("Parsed FirmwareMode: %d\n", p_bt->fw_mode);
}
else if (parse_int_value(line_start, "BtSink", &p_bt->bt_sink)) {
BTI("Parsed BtSink: %d\n", p_bt->bt_sink);
}
else if (parse_int_value(line_start, "PtaEnable", &p_bt->pta_enable)) {
BTI("Parsed PtaEnable: %d\n", p_bt->pta_enable);
}
else if (parse_int_value(line_start, "ChangePinMux", &p_bt->pin_mux))
{
BTI("Parsed ChangePinMux: %d\n", p_bt->pin_mux);
}
else if (parse_int_value(line_start, "BrDigitGain", &p_bt->br_digit_gain))
{
BTI("Parsed BrDigitGain: %d\n", p_bt->br_digit_gain);
}
else if (parse_int_value(line_start, "EdrDigitGain", &p_bt->edr_digit_gain))
{
BTI("Parsed EdrDigitGain: %d\n", p_bt->edr_digit_gain);
}
else if (parse_int_value(line_start, "Btfwlog", &p_bt->fw_log))
{
BTI("Parsed Btfwlog: %d\n", p_bt->fw_log);
}
else if (parse_int_value(line_start, "Btlog", &p_bt->driver_log))
{
BTI("Parsed Btlog: %d\n", p_bt->driver_log);
}
else if (parse_int_value(line_start, "Btfactory", &p_bt->factory))
{
BTI("Parsed Btfactory: %d\n", p_bt->factory);
}
else if (parse_int_value(line_start, "Manfcnt", &p_bt->manf_cnt))
{
BTI("Parsed Manfcnt: %d\n", p_bt->manf_cnt);
}
else if (parse_int_value(line_start, "W1UManfData", NULL))
{
BTI("Parsed W1UManfData len: %d\n", p_bt->manfdata_len);
}
else if (parse_int_value(line_start, "Btsystem", &p_bt->system))
{
BTI("Parsed Btsystem: %d\n", p_bt->system);
}
else if (parse_int_value(line_start, "BtIsolation", &p_bt->isolation))
{
BTI("Parsed isolatvalue: %d\n", p_bt->isolation);
}
else if (parse_int_value(line_start, "BtProject", &p_bt->project))
{
BTI("Parsed BtProject: %d\n", p_bt->project);
}
// Move to the next line
pos = (line_end - data) + 1;
}
release_firmware(fw_entry);
return 0;
}
static int amlbt_load_firmware(w2_usb_bt_t *p_bt)
{
int ret = 0;
unsigned int reg = 0;
const struct firmware *fw_entry = NULL;
unsigned int iccm_size;
unsigned int dccm_size;
const char *fw_name = NULL;
if (!amlbt_ft_mode)
{
fw_name = ((g_aml_device_id == W2u_C_PRODUCT_AMLOGIC_EFUSE) ?
AML_BT_REVC_FIRMWARE_NAME : AML_BT_FIRMWARE_NAME);
BTF("Firmware load:%s\n", fw_name);
ret = request_firmware(&fw_entry, fw_name, p_bt->dev_device);
if (ret)
{
BTE("%s:%d Failed to load firmware: %d\n", __func__, __LINE__, ret);
return ret;
}
}
else
{
fw_name = ((g_aml_device_id == W2u_C_PRODUCT_AMLOGIC_EFUSE) ?
AML_BT_REVC_FIRMWARE_FT_NAME : AML_BT_FIRMWARE_FT_NAME);
BTF("Firmware load:%s\n", fw_name);
ret = request_firmware(&fw_entry, fw_name, p_bt->dev_device);
if (ret)
{
BTE("%s:%d Failed to load firmware: %d\n", __func__, __LINE__, ret);
return ret;
}
}
BTI("Firmware [%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n",
fw_entry->data[0], fw_entry->data[1], fw_entry->data[2], fw_entry->data[3],
fw_entry->data[4], fw_entry->data[5], fw_entry->data[6], fw_entry->data[7]);
iccm_size = ((fw_entry->data[3]<<24)|(fw_entry->data[2]<<16)|(fw_entry->data[1]<<8)|(fw_entry->data[0]));
dccm_size = ((fw_entry->data[7]<<24)|(fw_entry->data[6]<<16)|(fw_entry->data[5]<<8)|(fw_entry->data[4]));
BTF("Firmware loaded successfully, iccm_size: %#x, dccm_size:%#x\n", iccm_size - ICCM_ROM_SIZE, dccm_size);
p_bt->iccm_buf = &fw_entry->data[ICCM_ROM_SIZE + 8];
p_bt->dccm_buf = &fw_entry->data[iccm_size + 8];
amlbt_write_word(REG_RAM_PD_SHUTDWONW_SW, 0, USB_EP2);
ret = amlbt_download_firmware(p_bt);
release_firmware(fw_entry);
if (ret != 0)
{
BTE("Download firmware failed!!\n");
return ret;
}
amlbt_read_word(REG_DF_A194, USB_EP2, &reg);
reg &= 0xfffffffc;
reg |= (p_bt->isolation & 0x3);
amlbt_write_word(REG_DF_A194, reg, USB_EP2);
amlbt_read_word(REG_PMU_POWER_CFG, USB_EP2, &reg);
reg &= 0xedffffff;
reg |= ((p_bt->antenna << BIT_RF_NUM)|(p_bt->bt_sink << BT_SINK_MODE));
amlbt_write_word(REG_PMU_POWER_CFG, reg, USB_EP2);
amlbt_read_word(RG_AON_A53, USB_EP2, &reg);
reg &= 0xf14f0000;
reg |= ((p_bt->pin_mux << 20) | (p_bt->factory << 21) | (p_bt->system << 23) | (p_bt->project << 25) | (p_bt->pta_enable << 27));
reg |= (((p_bt->edr_digit_gain & 0xff) << 8) | (p_bt->br_digit_gain & 0xff));
amlbt_write_word(RG_AON_A53, reg, USB_EP2);
amlbt_read_word(RG_AON_A59, USB_EP2, &reg);
reg &= 0xfffffffc;
reg |= (p_bt->fw_log & 0x3);
amlbt_write_word(RG_AON_A59, reg, USB_EP2);
amlbt_write_manfdata_to_firmware(p_bt);
p_bt->firmware_start = 1;
p_bt->iccm_buf = NULL;
p_bt->dccm_buf = NULL;
return 0;
}
static int amlbt_show_fw_debug_info(void)
{
unsigned int value1 = 0, value2 = 0, value3 = 0;
int ret = 0;
unsigned char debug_fw[128] = {0}; //32 *int
unsigned int *p_int;
unsigned int i;
ret = amlbt_read_word(REG_PMU_POWER_CFG, USB_EP2, &value1);
BTI("PMU 0x00f03040:%#x \n", value1);
if (ret != 0)
{
goto exit;
}
usleep_range(10000, 10000);
ret = amlbt_read_word(REG_FW_PC, USB_EP2, &value1);
value1 = (value1 >> 6);
BTI("pc1 0x200034:%#x\n", value1);
if (ret != 0)
{
goto exit;
}
usleep_range(10000, 10000);
ret = amlbt_read_word(REG_FW_PC, USB_EP2, &value2);
value2 = (value2 >> 6);
BTI("pc2 0x200034:%#x\n", value2);
if (ret != 0)
{
goto exit;
}
usleep_range(10000, 10000);
ret = amlbt_read_word(REG_FW_PC, USB_EP2, &value3);
value3 = (value3 >> 6);
BTI("pc3 0x200034:%#x\n", value3);
if (ret != 0)
{
goto exit;
}
if (value1 == value2 && value2 == value3)
{
ret = amlbt_read_sram(debug_fw,
(unsigned char *)(unsigned long)DEBUG_DCCM_ADDR,
sizeof(debug_fw), USB_EP2);
if (ret != 0)
{
goto exit;
}
p_int = (unsigned int *)debug_fw;
for (i = 0; i < sizeof(debug_fw) / 4; i++)
{
BTI("%#x: %#x \n", DEBUG_DCCM_ADDR+i*4, p_int[i]);
}
}
return ret;
exit:
return ret;
}
static int amlbt_char_open(struct inode *inode, struct file *file)
{
int ret = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s,%d, version:%s\n", __func__, amlbt_ft_mode, AML_W2U_VERSION);
/*if (info->reg_flag)
{
amlbt_hci_dev_deinit();
amlbt_hci_dev_init(p_bt->pdev);
BTF("unregister hci0 than init hdev\n");
}*/
ret = amlbt_check_usb();
if (ret != 0)
{
return nonseekable_open(inode, file);
}
if (amlbt_ft_mode && amlbt_dev.firmware_start)
{
BTF("%s FT MODE", __func__);
return nonseekable_open(inode, file);
}
else if (amlbt_dev.firmware_start)
{
BTE("repetition open!");
return nonseekable_open(inode, file);
}
else
{
ret = amlbt_powersave_clear();
if (ret != 0)
{
goto err_exit;
}
//stop bt cpu
ret = amlbt_sw_reset();
if (ret != 0)
{
goto err_exit;
}
if (amlbt_res_init(&amlbt_dev) != 0)
{
BTE("amlbt_res_init failed!\n");
goto err_exit;
}
amlbt_load_conf(&amlbt_dev);
ret = amlbt_load_firmware(&amlbt_dev);
if (ret != 0)
{
BTE("amlbt_load_firmware failed!\n");
goto err_buf;
}
BTF("amlbt_load_firmware success ret %d!\n",ret);
// inform fw reset mac
ret = amlbt_aon_addr_bit_set(RG_AON_A52, 0);
if (ret != 0)
{
goto err_buf;
}
/*ret = amlbt_register_interrupt_gpio(&amlbt_dev);
if (ret != 0)
{
BTI("irq register failed!\n");
goto err_buf;
}*/
//start cpu
ret = amlbt_write_word(REG_DEV_RESET, 0, USB_EP2);
if (ret != 0)
{
BTF("start cpu failed!\n");
goto err_buf;
}
ret = amlbt_show_fw_debug_info();
if (ret != 0)
{
BTI("show fw failed!\n");
goto err_buf;
}
file->private_data = &amlbt_dev;
amlbt_task_start(&amlbt_dev);
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
ret = amlbt_bind_bus();
if (ret != 0)
{
goto err_buf;
}
#endif
return nonseekable_open(inode, file);
err_buf:
amlbt_res_deinit(&amlbt_dev);
err_exit:
return nonseekable_open(inode, file);
}
}
static int amlbt_char_close(struct inode *inode, struct file *file)
{
int ret = 0;
w2_usb_bt_t *p_bt = (w2_usb_bt_t *)file->private_data;
BTF("%s, %d, version:%s\n", __func__, amlbt_ft_mode, AML_W2U_VERSION);
if (!amlbt_ft_mode)
{
p_bt->usb_irq_task_quit = 1;
wait_for_completion(&p_bt->comp);
BTF("BT closed wait_for_completion \n");
//clear bt open flag close bt notify firmware enter low power mode
/*ret = amlbt_aon_addr_bit_clr(RG_AON_A24, 24);
if (ret != 0)
{
amlbt_res_deinit(&amlbt_dev);
return 0;
}*/
//just release fifo
amlbt_res_deinit(&amlbt_dev);
//coexistence register notify wifi bt are turned off
//set bt_en auto mode
//amlbt_write_word(RG_AON_A15, 0, USB_EP2);
if (!info->reg_flag)
{
amlbt_register_hci_dev();
}
}
BTF("BT closed\n");
return 0;
}
static int amlbt_submit_poll_urb(w2_usb_bt_t *p_bt)
{
int ret = 0;
ret = amlbt_read_sram(p_bt->usb_rx_buf, (unsigned char *)(unsigned long)HI_USB_EVENT_Q_ADDR, POLL_TOTAL_LEN, USB_EP2);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
p_bt->usb_rx_len = 0;
up(&p_bt->usb_irq_sem);
return ret;
}
p_bt->usb_rx_len = POLL_TOTAL_LEN;
up(&p_bt->usb_irq_sem);
return ret;
}
static int amlbt_process_data_to_skb(w2_usb_bt_t *p_bt, unsigned char *data_buf, unsigned int len)
{
unsigned int offset = 0;
struct sk_buff *acl_skb = NULL;
struct sk_buff *skb = NULL;
unsigned int payload_len = 0;
unsigned int total_pkt_len = 0;
unsigned int aligned_len = 0;
int ret = 0;
BTP("%s: total len = %u\n", __func__, len);
while (offset + 8 <= len) {
payload_len = ((data_buf[offset + 7] << 8) | data_buf[offset + 6]);
if (payload_len > HCI_MAX_DATA_SIZE) {
BTE("Invalid payload_len %u at offset %u\n", payload_len, offset);
ret = -EINVAL;
goto error;
}
total_pkt_len = 8 + payload_len;
aligned_len = (total_pkt_len + 3) & ~3;
if (offset + aligned_len > len) {
BTE("Incomplete ACL packet: offset=%u needed=%u remaining=%u\n",
offset, aligned_len, len - offset);
ret = -EINVAL;
goto error;
}
acl_skb = alloc_skb(4 + payload_len, GFP_KERNEL);
if (!acl_skb) {
BTE("alloc_skb failed at offset %u\n", offset);
ret = -ENOMEM;
goto error;
}
hci_skb_pkt_type(acl_skb) = data_buf[offset];
if (!skb_put_data(acl_skb, &data_buf[offset + 4], 4 + payload_len)) {
BTE("skb_put_data failed at offset %u\n", offset);
kfree_skb(acl_skb);
ret = -EFAULT;
goto error;
}
spin_lock(&p_bt->bt_rx_queue_lock);
__skb_queue_tail(&p_bt->bt_rx_queue, acl_skb);
spin_unlock(&p_bt->bt_rx_queue_lock);
acl_skb = NULL;
offset += aligned_len;
}
return 0;
error:
if (acl_skb)
kfree_skb(acl_skb);
spin_lock(&p_bt->bt_rx_queue_lock);
while ((skb = __skb_dequeue(&p_bt->bt_rx_queue))) {
spin_unlock(&p_bt->bt_rx_queue_lock);
kfree_skb(skb);
spin_lock(&p_bt->bt_rx_queue_lock);
}
spin_unlock(&p_bt->bt_rx_queue_lock);
return ret;
}
static int amlbt_process_evt_to_skb(w2_usb_bt_t *p_bt, unsigned char *event_buff, unsigned int len)
{
unsigned int offset = 0;
struct sk_buff *event_skb = NULL;
struct sk_buff *skb = NULL;
unsigned int event_len = 0;
unsigned int total_pkt_len = 0;
unsigned int aligned_len = 0;
int ret = 0;
BTP("%s event_size %u\n", __func__, len);
while (offset + 3 <= len) {
unsigned char pkt_type = event_buff[offset];
unsigned char event_code = event_buff[offset + 1];
unsigned char param_len = event_buff[offset + 2];
if (param_len > HCI_MAX_EVENT_SIZE) {
BTE("Param_len %u too large at offset %u\n", param_len, offset);
ret = -EINVAL;
goto error;
}
event_len = 2 + param_len;
total_pkt_len = 1 + event_len;
aligned_len = (total_pkt_len + 3) & ~3;
if (offset + aligned_len > len) {
BTE("Incomplete event (0x%02x) at offset %u: need %u, remain %u\n",
event_code, offset, aligned_len, len - offset);
ret = -EINVAL;
goto error;
}
event_skb = alloc_skb(event_len, GFP_KERNEL);
if (!event_skb) {
BTE("alloc_skb failed at offset %u\n", offset);
ret = -ENOMEM;
goto error;
}
hci_skb_pkt_type(event_skb) = pkt_type;
// copy EventCode + ParamLen + Payload
if (!skb_put_data(event_skb, &event_buff[offset + 1], event_len)) {
BTE("skb_put_data failed at offset %u\n", offset);
kfree_skb(event_skb);
ret = -EFAULT;
goto error;
}
// queue skb
spin_lock(&p_bt->bt_rx_queue_lock);
__skb_queue_tail(&p_bt->bt_rx_queue, event_skb);
spin_unlock(&p_bt->bt_rx_queue_lock);
event_skb = NULL;
offset += aligned_len;
}
return 0;
error:
if (event_skb)
kfree_skb(event_skb);
spin_lock(&p_bt->bt_rx_queue_lock);
while ((skb = __skb_dequeue(&p_bt->bt_rx_queue))) {
spin_unlock(&p_bt->bt_rx_queue_lock);
kfree_skb(skb);
spin_lock(&p_bt->bt_rx_queue_lock);
}
spin_unlock(&p_bt->bt_rx_queue_lock);
return ret;
}
static int amlbt_firmware_data_process(w2_usb_bt_t *p_bt)
{
unsigned char *p_buf = p_bt->usb_rx_buf;
gdsl_fifo_t read_fifo = {0};
unsigned int reg = 0;
unsigned int type_size = 0;
unsigned int evt_size = 0;
unsigned int data_size = 0;
unsigned int i = 0;
unsigned char read_reg[16] = {0};
unsigned char *p_data = NULL;
unsigned int read_len = 0;
unsigned int d_index = 0;
unsigned int dropped_data =0;
unsigned int m = 0;
unsigned long idx = 0;
unsigned int key_value = 0;
static unsigned char type_buff[FIFO_FW_RX_TYPE_LEN+4] = {0};
static unsigned char fw_read_buff[USB_RX_Q_LEN*4] = {0};
static unsigned char fw_data_buff[USB_RX_Q_LEN*4] = {0};
int ret = 0;
int copy_result = 0;
gdsl_fifo_t ro_fw_evt_fifo = *p_bt->fw_evt_fifo;
gdsl_fifo_t ro_fw_type_fifo = *p_bt->fw_type_fifo;
gdsl_fifo_t ro_dr_data_fifo = *p_bt->dr_data_fifo;
gdsl_fifo_t ro_dr_evt_fifo = *p_bt->dr_evt_fifo;
gdsl_fifo_t ro_dr_type_fifo = *p_bt->dr_type_fifo;
gdsl_fifo_t ro_fw_data_fifo = *p_bt->fw_data_fifo;
//check fw gpio
if (p_bt->input_key)
{
ret = amlbt_read_word(RG_AON_A17, USB_EP2, &key_value);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
return -EFAULT;
}
//BTI("RG_AON_A17 %#x", key_value);
key_value = (key_value >> 5) & 0x3;
//BTI("key_value %#x", key_value);
if (key_value != 0)
{
get_btwakeup_work(key_value);
}
}
p_bt->fw_type_fifo->w = (unsigned char *)(unsigned long)((p_buf[35]<<24)|(p_buf[34]<<16)|(p_buf[33]<<8)|p_buf[32]);
if (p_bt->fw_type_fifo->w == p_bt->fw_type_fifo->r) // no data
{
return 0;
}
memset(type_buff, 0, sizeof(type_buff));
p_bt->fw_evt_fifo->w = (unsigned char *)(unsigned long)((p_buf[39]<<24)|(p_buf[38]<<16)|(p_buf[37]<<8)|p_buf[36]);
p_bt->fw_data_fifo->w = (unsigned char *)(unsigned long)((p_buf[19]<<24)|(p_buf[18]<<16)|(p_buf[17]<<8)|p_buf[16]);
p_bt->sink_mode = ((p_buf[23]<<24)|(p_buf[22]<<16)|(p_buf[21]<<8)|p_buf[20]);
BTP("t:w %#lx, r %#lx\n", (unsigned long)p_bt->fw_type_fifo->w, (unsigned long)p_bt->fw_type_fifo->r);
BTP("e:w %#lx, r %#lx\n", (unsigned long)p_bt->fw_evt_fifo->w, (unsigned long)p_bt->fw_evt_fifo->r);
BTP("d:w %#lx, r %#lx\n", (unsigned long)p_bt->fw_data_fifo->w, (unsigned long)p_bt->fw_data_fifo->r);
//copy type fifo
read_fifo.base_addr = &p_buf[FIFO_FW_RX_TYPE_ADDR - HI_USB_EVENT_Q_ADDR];
read_fifo.r = p_bt->fw_type_fifo->r;
read_fifo.w = p_bt->fw_type_fifo->w;
read_fifo.size = FIFO_FW_RX_TYPE_LEN;
type_size = gdsl_fifo_get_data(&read_fifo, type_buff, sizeof(type_buff));
if (type_buff[0] != HCI_ACLDATA_PKT && type_buff[0] != HCI_EVENT_PKT)
{
BTE("%s:%d type error!\n", __func__, __LINE__);
BTE("type_fifo w:%#lx r:%#lx", (unsigned long)p_bt->fw_type_fifo->w, (unsigned long)p_bt->fw_type_fifo->r);
BTE("evt_fifo w:%#lx r:%#lx", (unsigned long)p_bt->fw_evt_fifo->w, (unsigned long)p_bt->fw_evt_fifo->r);
BTE("data_fifo w:%#lx r:%#lx", (unsigned long)p_bt->fw_data_fifo->w, (unsigned long)p_bt->fw_data_fifo->r);
return -1;
}
BTP("type fifo:w %#lx, r %#lx\n", (unsigned long)p_bt->fw_type_fifo->w, (unsigned long)p_bt->fw_type_fifo->r);
BTP("[%#x,%#x,%#x,%#x]\n", type_buff[0], type_buff[4], type_buff[8], type_buff[12]);
p_bt->fw_type_fifo->r = read_fifo.r;//update r point
BTP("ut:r %#lx\n", (unsigned long)p_bt->fw_type_fifo->r);
reg = (((unsigned int)(unsigned long)read_fifo.r) & 0xff);
read_reg[0] = (reg & 0xff);
read_reg[1] = ((reg >> 8) & 0xff);
read_reg[2] = ((reg >> 16) & 0xff);
read_reg[3] = ((reg >> 24) & 0xff);
//copy event fifo
if (p_bt->fw_evt_fifo->w != p_bt->fw_evt_fifo->r)
{
read_fifo.base_addr = &p_buf[FIFO_FW_EVT_ADDR - HI_USB_EVENT_Q_ADDR];
read_fifo.r = p_bt->fw_evt_fifo->r;
read_fifo.w = p_bt->fw_evt_fifo->w;
read_fifo.size = FIFO_FW_EVT_LEN;
evt_size = gdsl_fifo_get_data(&read_fifo, fw_read_buff, FIFO_FW_EVT_LEN);
}
BTP("evt fifo:w %#lx, r %#lx\n", (unsigned long)p_bt->fw_evt_fifo->w, (unsigned long)p_bt->fw_evt_fifo->r);
if (evt_size)
{
p_bt->fw_evt_fifo->r = read_fifo.r;//update r point
BTP("ue:r %#lx\n", (unsigned long)p_bt->fw_evt_fifo->r);
//mutex_lock(&p_bt->dr_evt_fifo_mutex);
//copy_result = gdsl_fifo_copy_data(p_bt->dr_evt_fifo, fw_read_buff, evt_size);
//mutex_unlock(&p_bt->dr_evt_fifo_mutex);
if (0 != amlbt_process_evt_to_skb(p_bt, fw_read_buff, evt_size))
{
BTE("%s:%d copy Failed\n", __func__, __LINE__);
//amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
return -1;
}
}
reg = (((unsigned int)(unsigned long)p_bt->fw_evt_fifo->r) & 0x1fff);
read_reg[4] = (reg & 0xff);
read_reg[5] = ((reg >> 8) & 0xff);
read_reg[6] = ((reg >> 16) & 0xff);
read_reg[7] = ((reg >> 24) & 0xff);
//copy data fifo
if (p_bt->fw_data_fifo->w != p_bt->fw_data_fifo->r)
{
ret = amlbt_read_sram(&fw_data_buff[0],
(unsigned char *)(unsigned long)(FIFO_FW_DATA_ADDR), FIFO_FW_DATA_LEN,
USB_EP2);
if (ret == -1)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
else if (ret == -2)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_buf;
}
read_fifo.base_addr = fw_data_buff;
read_fifo.r = p_bt->fw_data_fifo->r;
read_fifo.w = p_bt->fw_data_fifo->w;
read_fifo.size = FIFO_FW_DATA_LEN;
data_size = gdsl_fifo_get_data(&read_fifo, fw_read_buff, FIFO_FW_DATA_LEN);
}
#if 0
while (p_bt->fw_data_fifo->w != p_bt->fw_data_fifo->r)
{
idx = (unsigned long)p_bt->fw_data_fifo->r;
BTP("data fifo w %#lx, r %#lx\n",(unsigned long)p_bt->fw_data_fifo->w, (unsigned long)p_bt->fw_data_fifo->r);
p_data = fw_read_buff;
read_len = ((p_data[((idx+7)% p_bt->fw_data_fifo->size)] << 8) | (p_data[((idx+6)% p_bt->fw_data_fifo->size)]));
read_len = ((read_len + 3) & 0xFFFFFFFC);
BTA("D:%d,[%#x|%#x|%#x|%#x|%#x|%#x|%#x|%#x|%#x|%#x|%#x|%#x]\n", read_len, p_data[((idx)% p_bt->fw_data_fifo->size)],
p_data[((idx+1)% p_bt->fw_data_fifo->size)], p_data[((idx+2)% p_bt->fw_data_fifo->size)],
p_data[((idx+3)% p_bt->fw_data_fifo->size)], p_data[((idx+4)% p_bt->fw_data_fifo->size)],
p_data[((idx+5)% p_bt->fw_data_fifo->size)], p_data[((idx+6)% p_bt->fw_data_fifo->size)],
p_data[((idx+7)% p_bt->fw_data_fifo->size)], p_data[((idx+8)% p_bt->fw_data_fifo->size)],
p_data[((idx+9)% p_bt->fw_data_fifo->size)], p_data[((idx+10)% p_bt->fw_data_fifo->size)],
p_data[((idx+11)% p_bt->fw_data_fifo->size)]);
if ((read_len == 0) || (read_len > 1024))
{
d_index = dropped_data;
for (m = 0; m < (type_size - 4); m+=4)
{
if (d_index == i && type_buff[m] == HCI_ACLDATA_PKT)
{
type_buff[m] = type_buff[m+4];
d_index += 4;
dropped_data += 4;
continue;
}
if (d_index > i)
{
type_buff[m] = type_buff[m+4];
}
else if (type_buff[m] == HCI_ACLDATA_PKT)
{
d_index += 4;
}
}
type_size -= 4;
BTF("data fifo w %#lx, r %#lx\n",(unsigned long)p_bt->fw_data_fifo->w, (unsigned long)p_bt->fw_data_fifo->r);
BTF("HEAD1:[%#x,%#x,%#x,%#x]\n",
p_data[((idx)% p_bt->fw_data_fifo->size)], p_data[((idx+1)% p_bt->fw_data_fifo->size)],
p_data[((idx+2)% p_bt->fw_data_fifo->size)], p_data[((idx+3)% p_bt->fw_data_fifo->size)]);
BTF("HEAD2:[%#x,%#x,%#x,%#x]\n",
p_data[((idx+4)% p_bt->fw_data_fifo->size)], p_data[((idx+5)% p_bt->fw_data_fifo->size)],
p_data[((idx+6)% p_bt->fw_data_fifo->size)], p_data[((idx+7)% p_bt->fw_data_fifo->size)]);
BTF("HEAD3:[%#x,%#x,%#x,%#x]\n",
p_data[((idx+8)% p_bt->fw_data_fifo->size)],p_data[((idx+9)% p_bt->fw_data_fifo->size)],
p_data[((idx+10)% p_bt->fw_data_fifo->size)], p_data[((idx+11)% p_bt->fw_data_fifo->size)]);
BTF("HEAD4:[%#x,%#x,%#x,%#x]\n",
p_data[((idx+12)% p_bt->fw_data_fifo->size)], p_data[((idx+13)% p_bt->fw_data_fifo->size)],
p_data[((idx+14)% p_bt->fw_data_fifo->size)], p_data[((idx+15)% p_bt->fw_data_fifo->size)]);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
return -EFAULT;
}
if (idx + read_len + 8 < p_bt->fw_data_fifo->size)
{
mutex_lock(&p_bt->dr_data_fifo_mutex);
copy_result = gdsl_fifo_copy_data(p_bt->dr_data_fifo, &fw_read_buff[idx], (read_len + 8));
mutex_unlock(&p_bt->dr_data_fifo_mutex);
if (copy_result == -1)
{
BTE("%s:%d copy Failed\n", __func__, __LINE__);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
}
}
else
{
mutex_lock(&p_bt->dr_data_fifo_mutex);
copy_result = gdsl_fifo_copy_data(p_bt->dr_data_fifo, &fw_read_buff[idx], p_bt->fw_data_fifo->size - idx);
mutex_unlock(&p_bt->dr_data_fifo_mutex);
if (copy_result == -1)
{
BTE("%s:%d copy Failed\n", __func__, __LINE__);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
}
mutex_lock(&p_bt->dr_data_fifo_mutex);
copy_result = gdsl_fifo_copy_data(p_bt->dr_data_fifo, &fw_read_buff[0],(read_len + 8) - (p_bt->fw_data_fifo->size - idx));
mutex_unlock(&p_bt->dr_data_fifo_mutex);
if (copy_result == -1)
{
BTE("%s:%d copy Failed\n", __func__, __LINE__);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
}
}
p_bt->fw_data_fifo->r = (unsigned char *)(((unsigned long)p_bt->fw_data_fifo->r + (read_len + 8)) % p_bt->fw_data_fifo->size);
}
//read_fifo.r = p_bt->fw_data_fifo->r;//update r point
BTP("ud:r %#lx\n", (unsigned long)p_bt->fw_data_fifo->r);
#endif
if (data_size)
{
if (data_size > FIFO_FW_DATA_LEN)
{
BTE("data_size %#x \n", data_size);
return -1;
}
p_bt->fw_data_fifo->r = read_fifo.r;
/* coverity[overrun-buffer-val:SUPPRESS] */
if (0 != amlbt_process_data_to_skb(p_bt, fw_read_buff, data_size))
{
return -1;
}
}
reg = (((unsigned int)(unsigned long)p_bt->fw_data_fifo->r) & 0x1fff);
read_reg[12] = (reg & 0xff);
read_reg[13] = ((reg >> 8) & 0xff);
read_reg[14] = ((reg >> 16) & 0xff);
read_reg[15] = ((reg >> 24) & 0xff);
ret = amlbt_write_sram(&read_reg[0], (unsigned char *)(unsigned long)(FIFO_FW_RX_TYPE_R), sizeof(read_reg), USB_EP2);
if (ret == -1)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
else if (ret == -2)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_buf;
}
#if 0
mutex_lock(&p_bt->dr_type_fifo_mutex);
copy_result = gdsl_fifo_copy_data(p_bt->dr_type_fifo, type_buff, type_size);
mutex_unlock(&p_bt->dr_type_fifo_mutex);
if (copy_result == -1)
{
BTE("%s:%d copy Failed\n", __func__, __LINE__);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
return -1;
}
BTP("dp2 type:w %#lx, r %#lx\n", (unsigned long)p_bt->dr_type_fifo->w, (unsigned long)p_bt->dr_type_fifo->r);
BTP("dp2 evt:w %#lx, r %#lx\n", (unsigned long)p_bt->dr_evt_fifo->w, (unsigned long)p_bt->dr_evt_fifo->r);
BTP("dp2 data:w %#lx, r %#lx\n", (unsigned long)p_bt->dr_data_fifo->w, (unsigned long)p_bt->dr_data_fifo->r);
//wake_up_interruptible(&p_bt->rd_wait_queue);android used
#endif
return 0;
err_buf:
*p_bt->fw_evt_fifo = ro_fw_evt_fifo;
*p_bt->fw_type_fifo = ro_fw_type_fifo;
*p_bt->dr_data_fifo = ro_dr_data_fifo;
*p_bt->dr_evt_fifo= ro_dr_evt_fifo;
*p_bt->dr_type_fifo = ro_dr_type_fifo;
*p_bt->fw_data_fifo = ro_fw_data_fifo;
return -2;
err_exit:
return -1;
}
static int amlbt_drv_state_process(w2_usb_bt_t *p_bt)
{
int ret = BT_DRV_NONE;
if (p_bt->dr_state & BT_DRV_STATE_RECOVERY)
{
p_bt->usb_irq_task_quit = 1;
wake_up_interruptible(&p_bt->rd_wait_queue);
}
if (p_bt->usb_irq_task_quit)
{
BTF("%s:%d usb_irq_task_quit == 1!\n", __func__, __LINE__);
complete(&p_bt->comp);
ret = BT_DRV_CLOSED;
goto exit;
}
if (p_bt->dr_state & BT_DRV_STATE_SUSPEND_ENTRY)
{
BTF("%s:%d BT_DRV_STATE_SUSPEND_ENTRY!\n", __func__, __LINE__);
if (0 == amlbt_suspend_fw(p_bt))
{
amlbt_drv_state_clr(BT_DRV_STATE_SUSPEND_ENTRY);
amlbt_drv_state_set(BT_DRV_STATE_SUSPEND);
ret = BT_DRV_SUSPEND;
}
else
{
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
amlbt_drv_state_clr(BT_DRV_STATE_SUSPEND_ENTRY);
ret = BT_DRV_CLOSED;
}
up(&p_bt->sr_sem);
goto exit;
}
if (p_bt->dr_state & BT_DRV_STATE_SUSPEND)
{
ret = BT_DRV_SUSPEND;
goto exit;
}
if (p_bt->dr_state & BT_DRV_STATE_RESUME)
{
BTD("%s:%d BT_DRV_STATE_RESUME_ENTRY!\n", __func__, __LINE__);
if (0 == amlbt_resume_fw())
{
amlbt_drv_state_clr(BT_DRV_STATE_RESUME);
ret = BT_DRV_NONE;
up(&p_bt->sr_sem);
goto exit;
}
else
{
ret = BT_DRV_RESUME;
}
}
exit:
return ret;
}
static void amlbt_polling_time_check(w2_usb_bt_t *p_bt)
{
if (p_bt->sink_mode)
{
polling_time = 1000;
}
else
{
polling_time = 8000;
}
return ;
}
static int amlbt_irq_task(void *data)
{
w2_usb_bt_t *p_bt = (w2_usb_bt_t *)data;
//process data
int actual_length = 0;
int ret = 0;
int err = 0;
while (1)
{
ret = amlbt_drv_state_process(p_bt);
if (ret == BT_DRV_NONE && suspend_value != W2_BT_SYSTEM_SUSPEND)
{
if (down_interruptible(&p_bt->usb_irq_sem) != 0)
{
/* interrupted, exit */
BTF("%s:%d wait usb_irq_sem fail!\n", __func__, __LINE__);
break;
}
data = p_bt->usb_rx_buf;
actual_length = p_bt->usb_rx_len;
if (actual_length == POLL_TOTAL_LEN)
{
err = amlbt_firmware_data_process(p_bt);
if (err == -1)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, err);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
complete(&p_bt->comp);
wake_up_interruptible(&p_bt->rd_wait_queue);
break;
}
else if (err == -2)
{
continue;
}
amlbt_polling_time_check(p_bt);
}
else
{
BTE("%s:%d usb rx data length not match!!, %d\n", __func__, __LINE__, actual_length);
}
usleep_range(polling_time, polling_time);
p_bt->usb_rx_len = 0;
err = amlbt_submit_poll_urb(p_bt);
if (err == -1)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, err);
amlbt_drv_state_set(BT_DRV_STATE_RECOVERY);
complete(&p_bt->comp);
wake_up_interruptible(&p_bt->rd_wait_queue);
break;
}
else if (err == -2)
{
continue;
}
}
else if (ret == BT_DRV_CLOSED)
{
BTF("%s:%d BT_DRV_CLOSED!\n", __func__, __LINE__);
break;
}
else if (ret == BT_DRV_SUSPEND || ret == BT_DRV_RESUME)
{
BTD("%s:%d BT_DRV_SUSPEND or BT_DRV_RESUME!\n", __func__, __LINE__);
usleep_range(polling_time, polling_time);
}
}
BTF("%s:%d amlbt_irq_task quit!\n", __func__, __LINE__);
return 0;
}
static int amlbt_task_start(w2_usb_bt_t *p_bt)
{
BTF("%s\n", __func__);
sema_init(&p_bt->usb_irq_sem, 0);//init semaphore
p_bt->usb_irq_task_quit = 0;
p_bt->usb_irq_task = kthread_run(amlbt_irq_task, &amlbt_dev, "aml_irq_task");
BTF("amlbt_task_start:%#lx\n", (unsigned long)&amlbt_dev);
if (IS_ERR(p_bt->usb_irq_task)) {
p_bt->usb_irq_task = NULL;
BTE("create usb task error!!!!\n");
return -1;
}
reinit_completion(&p_bt->comp);
return amlbt_submit_poll_urb(p_bt);
}
static void amlbt_kill_irq_task(w2_usb_bt_t *p_bt)
{
BTF("%s\n", __func__);
if (p_bt && p_bt->usb_irq_task != NULL) {
p_bt->usb_irq_task_quit = 1;
up(&p_bt->usb_irq_sem);
wait_for_completion(&p_bt->comp);
//just release fifo
amlbt_res_deinit(p_bt);
p_bt->usb_irq_task = NULL;
BTF("aml_irq_task killed successfully.\n");
}
}
static int amlbt_download_firmware(w2_usb_bt_t *p_bt)
{
int ret = 0;
unsigned int retry = 0;
unsigned int offset = 0;
unsigned int remain_len = 0;
unsigned int iccm_base_addr = BT_ICCM_AHB_BASE + ICCM_ROM_SIZE;
unsigned int dccm_base_addr = BT_DCCM_AHB_BASE;
uint8_t *check_buf = kzalloc(DOWNLOAD_SIZE, GFP_DMA|GFP_ATOMIC);
if (check_buf == NULL)
{
BTF("amlbt_download_firmware check_buf alloc failed!!!\n");
return -1;
}
memset(check_buf, 0, DOWNLOAD_SIZE);
remain_len = ICCM_SIZE;
offset = 0;
retry = 0;
BTF("amlbt_download_firmware:iccm size %#x, remain_len %#x\n", ICCM_SIZE, remain_len);
while (offset < ICCM_SIZE)
{
if (remain_len < DOWNLOAD_SIZE)
{
BTD("amlbt_download_firmware iccm1 offset %#x, addr %#x\n", offset, iccm_base_addr);
amlbt_write_sram((unsigned char *)&p_bt->iccm_buf[offset], (unsigned char *)(unsigned long)iccm_base_addr, remain_len, USB_EP2);
amlbt_read_sram(check_buf, (unsigned char *)(unsigned long)iccm_base_addr, remain_len, USB_EP2);
if (memcmp(check_buf, &p_bt->iccm_buf[offset], remain_len))
{
BTE("Firmware iccm check2 error! addr %#x offset %#x retry\n",
iccm_base_addr, offset, retry);
if (retry < MAX_DOWNLOAD_RETRY)
{
retry++;
continue;
}
ret = -1;
goto error;
}
offset += remain_len;
iccm_base_addr += remain_len;
BTD("amlbt_download_firmware iccm1 offset %#x, write_len %#x\n", offset, remain_len);
}
else
{
BTD("amlbt_download_firmware iccm2 offset %#x, write_len %#x, addr %#x\n", offset, DOWNLOAD_SIZE, iccm_base_addr);
amlbt_write_sram((unsigned char *)&p_bt->iccm_buf[offset], (unsigned char *)(unsigned long)iccm_base_addr, DOWNLOAD_SIZE, USB_EP2);
amlbt_read_sram(check_buf, (unsigned char *)(unsigned long)iccm_base_addr, DOWNLOAD_SIZE, USB_EP2);
if (memcmp(check_buf, &p_bt->iccm_buf[offset], DOWNLOAD_SIZE))
{
BTE("Firmware iccm check error! addr %#x offset %#x retry\n",
iccm_base_addr, offset, retry);
if (retry < MAX_DOWNLOAD_RETRY)
{
retry++;
continue;
}
ret = -1;
goto error;
}
offset += DOWNLOAD_SIZE;
remain_len -= DOWNLOAD_SIZE;
iccm_base_addr += DOWNLOAD_SIZE;
}
BTD("amlbt_download_firmware iccm remain_len %#x\n", remain_len);
}
BTF("Firmware iccm check pass, offset %#x\n", offset);
offset = 0;
retry = 0;
remain_len = DCCM_SIZE;
//download bt dccm
BTF("amlbt_download_firmware:dccm size %#x, remain_len %#x\n", DCCM_SIZE, remain_len);
while (offset < DCCM_SIZE)
{
if (remain_len < DOWNLOAD_SIZE)
{
BTD("amlbt_download_firmware dccm1 offset %#x, addr %#x\n", offset, dccm_base_addr);
amlbt_write_sram((unsigned char *)&p_bt->dccm_buf[offset], (unsigned char *)(unsigned long)dccm_base_addr, remain_len, USB_EP2);
amlbt_read_sram(check_buf, (unsigned char *)(unsigned long)dccm_base_addr, remain_len, USB_EP2);
if (memcmp(check_buf, &p_bt->dccm_buf[offset], remain_len))
{
BTE("Firmware dccm check2 error! addr %#x offset %#x retry\n",
dccm_base_addr, offset, retry);
if (retry < MAX_DOWNLOAD_RETRY)
{
retry++;
continue;
}
ret = -1;
goto error;
}
offset += remain_len;
dccm_base_addr += remain_len;
BTD("amlbt_download_firmware dccm1 offset %#x, write_len %#x\n", offset, remain_len);
}
else
{
BTD("amlbt_download_firmware dccm2 offset %#x, write_len %#x, addr%#x\n", offset, DOWNLOAD_SIZE, dccm_base_addr);
amlbt_write_sram((unsigned char *)&p_bt->dccm_buf[offset], (unsigned char *)(unsigned long)dccm_base_addr, DOWNLOAD_SIZE, USB_EP2);
amlbt_read_sram(check_buf, (unsigned char *)(unsigned long)dccm_base_addr, DOWNLOAD_SIZE, USB_EP2);
if (memcmp(check_buf, &p_bt->dccm_buf[offset], DOWNLOAD_SIZE))
{
BTE("Firmware dccm check error! addr %#x offset %#x retry\n",
dccm_base_addr, offset, retry);
if (retry < MAX_DOWNLOAD_RETRY)
{
retry++;
continue;
}
ret = -1;
goto error;
}
offset += DOWNLOAD_SIZE;
remain_len -= DOWNLOAD_SIZE;
dccm_base_addr += DOWNLOAD_SIZE;
}
BTD("amlbt_download_firmware dccm remain_len %#x \n", remain_len);
}
BTF("Firmware dccm check pass, offset %#x\n", offset);
error:
kfree(check_buf);
return ret;
}
static int amlbt_send_hci_cmd(w2_usb_bt_t *p_bt, unsigned char *data, unsigned int len)
{
int ret = 0;
unsigned int val = 0;
unsigned int length = 0;
gdsl_fifo_t t_fifo = *p_bt->tx_cmd_fifo;
BTA("%s, len %d \n", __func__, len);
length = len + 1;
if (p_bt->tx_cmd_fifo == NULL)
{
BTE("%s: p_bt->tx_cmd_fifo NULL!!!!\n", __func__);
goto err_exit;
}
if (len)
{
//BTF("hci_cmd:%#x,%#x,%#x,%#x",data[0],data[1],data[2],data[3]);
BTF("hci_cmd:%#x,%#x",data[0],data[1]);
}
len = ((len + 3) & 0xFFFFFFFC);//Keep 4 bytes aligned
BTA("%s, Actual length %d \n", __func__, len);
//step 1: Update the command FIFO read pointer
ret = amlbt_read_word(FIFO_FW_CMD_R, USB_EP2, &val);
BTA("cmd r %#x\n", val);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
t_fifo.r = (unsigned char *)(unsigned long)val;
//step 3: Write HCI commands to WiFi SRAM
ret = gdsl_write_data_by_ep(&t_fifo, data, len, USB_EP2);
//step 4: Update the write pointer and write to WiFi SRAM
if (ret < 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
BTA("before write:r:%#lx, w:%#lx\n", (unsigned long)p_bt->tx_cmd_fifo->r, (unsigned long)p_bt->tx_cmd_fifo->w);
ret = amlbt_write_word(FIFO_FW_CMD_W, (unsigned long)t_fifo.w & 0xfff, USB_EP2);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
*p_bt->tx_cmd_fifo = t_fifo;
BTP("len %#x:w %#lx, r %#lx\n", len, (unsigned long)p_bt->tx_cmd_fifo->w, (unsigned long)p_bt->tx_cmd_fifo->r);
//amlbt_debug_get_cmd((unsigned int)(unsigned long)p_bt->tx_cmd_fifo->w, (unsigned int)(unsigned long)p_bt->tx_cmd_fifo->r, data);
return length;
err_exit:
return ret;
}
static unsigned int amlbt_get_tx_prio(gdsl_tx_q_t *p_fifo, unsigned int acl_handle)
{
unsigned int prio = 0;
unsigned int i = 0;
unsigned int find = 0;
for (i = 0; i < USB_TX_Q_NUM; i++)
{
if (p_fifo[i].tx_q_dev_index == acl_handle/* && p_fifo[i].tx_q_status == GDSL_TX_Q_USED*/)
{
if (p_fifo[i].tx_q_prio >= prio)
{
prio = p_fifo[i].tx_q_prio;
find = 1;
}
}
}
if (!find)
{
prio = TX_Q_MAX_PRIO;
}
return prio;
}
static int amlbt_send_hci_data(w2_usb_bt_t *p_bt, unsigned char *data, unsigned int len)
{
int ret = 0;
unsigned int i = 0;
unsigned int acl_handle = (((data[1] << 8) | data[0]) & 0xfff);
unsigned int prio = 0;
unsigned int tx_q_prio[USB_TX_Q_NUM] = {0};
unsigned int tx_q_index[USB_TX_Q_NUM] = {0};
unsigned int tx_q_status[USB_TX_Q_NUM] = {0};
unsigned int tx_buff[USB_TX_Q_NUM * 4] = {0};//prio, index, status
gdsl_tx_q_t ro_tx_q = *p_bt->tx_q;
BTA("%s, len:%d\n", __func__, len);
ret = amlbt_read_sram((unsigned char *)tx_buff, (unsigned char *)p_bt->tx_q[0].tx_q_prio_addr,
sizeof(tx_buff), USB_EP2);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
USB_END_LOCK();
goto err_exit;
}
for (i = 0; i < USB_TX_Q_NUM; i++)
{
tx_q_prio[i] = tx_buff[i*4];
tx_q_index[i] = tx_buff[i*4+1];
tx_q_status[i] = tx_buff[i*4+2];
}
BTP("P %#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x\n", tx_q_prio[0],tx_q_prio[1],tx_q_prio[2],tx_q_prio[3],
tx_q_prio[4],tx_q_prio[5],tx_q_prio[6],tx_q_prio[7]);
BTP("A %#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x\n", tx_q_index[0],tx_q_index[1],tx_q_index[2],tx_q_index[3],
tx_q_index[4],tx_q_index[5],tx_q_index[6],tx_q_index[7]);
BTP("S %#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x\n", tx_q_status[0],tx_q_status[1],tx_q_status[2],tx_q_status[3],
tx_q_status[4],tx_q_status[5],tx_q_status[6],tx_q_status[7]);
for (i = 0; i < USB_TX_Q_NUM; i++)
{
if (tx_q_status[i] == GDSL_TX_Q_COMPLETE)
{
tx_q_index[i] = 0;
tx_q_status[i] = GDSL_TX_Q_UNUSED;
tx_q_prio[i] = TX_Q_MAX_PRIO;
}
}
for (i = 0; i < USB_TX_Q_NUM; i++)
{
p_bt->tx_q[i].tx_q_dev_index = tx_q_index[i];
p_bt->tx_q[i].tx_q_status = tx_q_status[i];
p_bt->tx_q[i].tx_q_prio = tx_q_prio[i];
}
for (i = 0; i < USB_TX_Q_NUM; i++)
{
if (p_bt->tx_q[i].tx_q_status == GDSL_TX_Q_UNUSED)
{
break;
}
}
if (i == USB_TX_Q_NUM)
{
BTE("%s: hci data space invalid!!!!\n", __func__);
BTE("%s, len:%d\n", __func__, len);
for (i = 0; i < USB_TX_Q_NUM; i++)
{
BTE("[%#x,%#x,%#x]", (unsigned int)p_bt->tx_q[i].tx_q_prio,
(unsigned int)p_bt->tx_q[i].tx_q_dev_index,
(unsigned int)p_bt->tx_q[i].tx_q_status);
BTE("{%#x,%#x,%#x}", tx_q_prio[i], tx_q_index[i],tx_q_status[i]);
}
return -1;
}
ro_tx_q = *p_bt->tx_q;
prio = amlbt_get_tx_prio(p_bt->tx_q, acl_handle);
p_bt->tx_q[i].tx_q_prio = (++prio & TX_Q_MAX_PRIO);
p_bt->tx_q[i].tx_q_dev_index = acl_handle;
p_bt->tx_q[i].tx_q_status = GDSL_TX_Q_USED;
len = (len + 3) & ~3;
ret = amlbt_write_sram(data, p_bt->tx_q[i].tx_q_addr, len, USB_EP2);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
USB_END_LOCK();
*p_bt->tx_q = ro_tx_q;
goto err_exit;
}
tx_buff[0] = p_bt->tx_q[i].tx_q_prio;
tx_buff[1] = p_bt->tx_q[i].tx_q_dev_index;
tx_buff[2] = p_bt->tx_q[i].tx_q_status;
tx_buff[3] = 0;
BTP("TX:%d,%d,%#x,%#x\n", i, len, acl_handle, p_bt->tx_q[i].tx_q_prio);
ret = amlbt_write_sram((unsigned char *)&tx_buff[0], (unsigned char *)p_bt->tx_q[i].tx_q_prio_addr,
sizeof(unsigned int)*4, USB_EP2);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
*p_bt->tx_q = ro_tx_q;
goto err_exit;
}
BTP("%s, Actual length:%d\n", __func__, len);
return 0;
err_exit:
return ret;
}
static ssize_t amlbt_char_write(struct file *file_p,
const char __user *buf_p,
size_t count,
loff_t *pos_p)
{
int ret = 0;
int wait_cnt = 0;
int i = 0;
static unsigned int w_type = 0;
w2_usb_bt_t *p_bt = (w2_usb_bt_t *)file_p->private_data;
static unsigned char hci_buf[HCI_MAX_FRAME_SIZE] = {0};
BTA("%s, count:%ld\n", __func__, count);
if (count > HCI_MAX_FRAME_SIZE)
{
BTE("%s:%d count > HCI_MAX_FRAME_SIZE %d, %d!\n", __func__, __LINE__, count, HCI_MAX_FRAME_SIZE);
return -EINVAL;
}
if (count == 1) //host write hci type
{
get_user(w_type, buf_p);
return count;
}
if (!p_bt->firmware_start)
{
BTE("%s:%d p_bt->firmware_start == 0!\n", __func__, __LINE__);
return -EINVAL;
}
if (copy_from_user(hci_buf, buf_p, count))
{
BTE("%s: Failed to get data from user space\n", __func__);
return -EFAULT;
}
if (count > 1 && w_type == HCI_COMMAND_PKT)
{
BTP("hci cmd:[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]",
hci_buf[0],hci_buf[1],hci_buf[2],hci_buf[3],
hci_buf[4],hci_buf[5],hci_buf[6],hci_buf[7]);
}
while ((p_bt->dr_state & BT_DRV_STATE_SUSPEND) || (p_bt->dr_state & BT_DRV_STATE_RESUME))
{
usleep_range(20000, 20000);
if (wait_cnt++ >= 100)
{
BTE("cmd timeout [%#x %#x]", hci_buf[0], hci_buf[1]);
break;
}
}
if (w_type == HCI_COMMAND_PKT)
{
if (count == 0x0f && hci_buf[0] == 0x27 && hci_buf[1] == 0xfc) //close
{
BTP("close cmd:[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]",
hci_buf[0],hci_buf[1],hci_buf[2],hci_buf[3],
hci_buf[4],hci_buf[5],hci_buf[6],hci_buf[7]);
BTW("bluetooth close!\n");
return count;
}
if (hci_buf[0] == 0x05 && hci_buf[1] == 0x14) // read rssi
{
BTI("rs\n");
}
if (hci_buf[0] == 0x1a && hci_buf[1] == 0xfc)
{
/*for (; i < sizeof(p_bt->mac_addr); i++)
{
p_bt->mac_addr[i] = hci_buf[i+3];
}*/
}
amlbt_send_hci_cmd(p_bt, hci_buf, count);
}
else if (w_type == HCI_ACLDATA_PKT)
{
amlbt_send_hci_data(p_bt, hci_buf, count);
}
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
return 0;
}
return count;
}
#if 0
static ssize_t amlbt_char_read(struct file *file_p,
char __user *buf_p,
size_t count,
loff_t *pos_p)
{
unsigned char hw_error_evt[5] = {0x04, 0x10, 0x01, 0x00, 0x00};
w2_usb_bt_t *p_bt = (w2_usb_bt_t *)file_p->private_data;
static unsigned char bt_type[4] = {0};
static unsigned char hci_read_buf[USB_TX_Q_LEN+64] = {0};
unsigned int read_len = 0;
unsigned int type_size = 0;
if (!p_bt->firmware_start)
{
BTE("%s:%d p_bt->firmware_start == 0!\n", __func__, __LINE__);
return -EFAULT;
}
BTD("rd %#x\n", p_bt->rd_state);
BTD("rd type:w %#lx, r %#lx\n", (unsigned long)p_bt->dr_type_fifo->w, (unsigned long)p_bt->dr_type_fifo->r);
BTD("rd evt:w %#lx, r %#lx\n", (unsigned long)p_bt->dr_evt_fifo->w, (unsigned long)p_bt->dr_evt_fifo->r);
BTD("rd data:w %#lx, r %#lx\n", (unsigned long)p_bt->dr_data_fifo->w, (unsigned long)p_bt->dr_data_fifo->r);
switch (p_bt->rd_state)
{
case HCI_RX_TYPE: //read type
memset(bt_type, 0, sizeof(bt_type));
if (p_bt->dr_state & BT_DRV_STATE_RECOVERY)
{
BTW("%s:%d BT_DRV_STATE_RECOVERY!\n", __func__, __LINE__);
if (copy_to_user(buf_p, &hw_error_evt[0], count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
p_bt->rd_state = HCI_RX_HEADER;
bt_type[0] = HCI_EVENT_PKT;
return count;
}
mutex_lock(&p_bt->dr_type_fifo_mutex);
type_size = gdsl_fifo_get_data(p_bt->dr_type_fifo, bt_type, sizeof(bt_type));
mutex_unlock(&p_bt->dr_type_fifo_mutex);
if (0 == type_size)
{
BTE("%s:%d p_bt->dr_type_fifo NULL!\n", __func__, __LINE__);
return -EFAULT;
}
BTD("tp(%#x,%#x,%#x,%#x)\n", bt_type[0],bt_type[1],bt_type[2],bt_type[3]);
if (bt_type[0] != HCI_ACLDATA_PKT && bt_type[0] != HCI_EVENT_PKT)
{
BTF("TYPE size %#x ERROR (%#x,%#x,%#x,%#x)\n", type_size, bt_type[0],bt_type[1],bt_type[2],bt_type[3]);
BTF("dr_type_fifo->w:%#lx dr_type_fifo->r:%#lx\n", (unsigned long)p_bt->dr_type_fifo->w, (unsigned long)p_bt->dr_type_fifo->r);
BTF("dr_evt_fifo->w:%#lx dr_evt_fifo->r:%#lx\n", (unsigned long)p_bt->dr_evt_fifo->w, (unsigned long)p_bt->dr_evt_fifo->r);
BTF("dr_data_fifo->w:%#lx dr_data_fifo->r:%#lx\n", (unsigned long)p_bt->dr_data_fifo->w, (unsigned long)p_bt->dr_data_fifo->r);
return -EFAULT;
}
if (copy_to_user(buf_p, bt_type, count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
p_bt->rd_state = HCI_RX_HEADER;
break;
case HCI_RX_HEADER: // read header
if (bt_type[0] == HCI_EVENT_PKT)
{
if (p_bt->dr_state & BT_DRV_STATE_RECOVERY)
{
if (copy_to_user(buf_p, &hw_error_evt[1], count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
p_bt->rd_state = HCI_RX_PAYLOAD;
return count;
}
gdsl_fifo_get_data(p_bt->dr_evt_fifo, hci_read_buf, 4);
if (copy_to_user(buf_p, &hci_read_buf[1], count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
BTD("E:%#x|%#x|%#x|%#x\n", hci_read_buf[0], hci_read_buf[1],
hci_read_buf[2], hci_read_buf[3]);
}
else if (bt_type[0] == HCI_ACLDATA_PKT)
{
gdsl_fifo_get_data(p_bt->dr_data_fifo, hci_read_buf, 8);
BTD("D:%#x|%#x|%#x|%#x\n", hci_read_buf[0], hci_read_buf[1],
hci_read_buf[2], hci_read_buf[3]);
if (copy_to_user(buf_p, &hci_read_buf[4], count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
}
p_bt->rd_state = HCI_RX_PAYLOAD;
break;
case HCI_RX_PAYLOAD: //read payload
if (bt_type[0] == HCI_EVENT_PKT)
{
if (p_bt->dr_state & BT_DRV_STATE_RECOVERY)
{
if (copy_to_user(buf_p, &hw_error_evt[3], count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
p_bt->rd_state = HCI_RX_FATAL;
return count;
}
read_len = hci_read_buf[2];
read_len -= 1;
read_len = ((read_len + 3) & 0xFFFFFFFC);
gdsl_fifo_get_data(p_bt->dr_evt_fifo, &hci_read_buf[4], read_len);
BTP("E:[%#x|%#x|%#x|%#x|%#x|%#x|%#x|%#x]\n", hci_read_buf[0], hci_read_buf[1],
hci_read_buf[2], hci_read_buf[3],hci_read_buf[4], hci_read_buf[5],
hci_read_buf[6], hci_read_buf[7]);
if (copy_to_user(buf_p, &hci_read_buf[3], count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
}
else if (bt_type[0] == HCI_ACLDATA_PKT)
{
read_len = ((hci_read_buf[7] << 8) | (hci_read_buf[6]));
read_len = ((read_len + 3) & 0xFFFFFFFC);
gdsl_fifo_get_data(p_bt->dr_data_fifo, &hci_read_buf[8], read_len);
BTP("D:%#x|%#x|%#x|%#x|%#x|%#x|%#x|%#x\n", hci_read_buf[4], hci_read_buf[5],
hci_read_buf[6], hci_read_buf[7], hci_read_buf[8],
hci_read_buf[9], hci_read_buf[10], hci_read_buf[11]);
if (copy_to_user(buf_p, &hci_read_buf[8], count))
{
BTE("%s, copy_to_user error \n", __func__);
return -EFAULT;
}
}
p_bt->rd_state = HCI_RX_TYPE;
break;
default:
BTE("%s, evt_state error!!\n", __func__);
break;
}
return count;
}
#endif
static ssize_t amlbt_char_read(struct file *file_p,
char __user *buf_p,
size_t count,
loff_t *pos_p)
{
unsigned char hw_error_evt[5] = {0x04, 0x10, 0x01, 0x00, 0x00};
w2_usb_bt_t *p_bt = (w2_usb_bt_t *)file_p->private_data;
static struct sk_buff *curr_skb = NULL;
unsigned char pkt_type;
unsigned int copy_len = 0;
if (!p_bt || !p_bt->firmware_start) {
BTE("%s: Invalid device or firmware not started\n", __func__);
return -EFAULT;
}
switch (p_bt->rd_state) {
case HCI_RX_TYPE:
if (p_bt->dr_state & BT_DRV_STATE_RECOVERY) {
if (count < 1) {
BTE("%s: Recovery state requires at least 1 byte\n", __func__);
return -EINVAL;
}
if (copy_to_user(buf_p, &hw_error_evt[0], 1)) {
BTE("%s: Failed to copy hardware error event\n", __func__);
return -EFAULT;
}
p_bt->rd_state = HCI_RX_HEADER;
return 1;
}
spin_lock(&p_bt->bt_rx_queue_lock);
curr_skb = skb_dequeue(&p_bt->bt_rx_queue);
spin_unlock(&p_bt->bt_rx_queue_lock);
if (!curr_skb) {
BTE("%s:%d rx_queue is empty\n", __func__, __LINE__);
return 0;
}
pkt_type = hci_skb_pkt_type(curr_skb);
if (copy_to_user(buf_p, &pkt_type, 1)) {
BTE("%s: Failed to copy packet type 0x%02x\n", __func__, pkt_type);
kfree_skb(curr_skb);
curr_skb = NULL;
return -EFAULT;
}
p_bt->rd_state = HCI_RX_HEADER;
return 1;
case HCI_RX_HEADER:
if (!curr_skb) {
BTE("%s: Invalid state - curr_skb is NULL in HEADER state\n", __func__);
return -EFAULT;
}
pkt_type = hci_skb_pkt_type(curr_skb);
if (pkt_type == HCI_EVENT_PKT) {
if (curr_skb->len < 2) {
BTE("%s: Event packet too short (%d < 2)\n", __func__, curr_skb->len);
return -EFAULT;
}
copy_len = 2;
} else if (pkt_type == HCI_ACLDATA_PKT) {
if (curr_skb->len < 4) {
BTE("%s: ACL data packet too short (%d < 4)\n", __func__, curr_skb->len);
return -EFAULT;
}
copy_len = 4;
} else {
BTE("%s: Invalid packet type 0x%02x\n", __func__, pkt_type);
kfree_skb(curr_skb);
curr_skb = NULL;
return -EINVAL;
}
if (copy_to_user(buf_p, curr_skb->data, copy_len)) {
BTE("%s: Failed to copy %d header bytes for packet type 0x%02x\n", __func__, copy_len, pkt_type);
kfree_skb(curr_skb);
curr_skb = NULL;
return -EFAULT;
}
skb_pull(curr_skb, copy_len);
if (curr_skb->len == 0) {
kfree_skb(curr_skb);
curr_skb = NULL;
p_bt->rd_state = HCI_RX_TYPE;
} else {
p_bt->rd_state = HCI_RX_PAYLOAD;
}
return copy_len;
case HCI_RX_PAYLOAD:
if (!curr_skb) {
BTE("%s: Invalid state - curr_skb is NULL in PAYLOAD state\n", __func__);
return -EFAULT;
}
copy_len = min_t(unsigned int, curr_skb->len, count);
if (copy_to_user(buf_p, curr_skb->data, copy_len)) {
BTE("%s: Failed to copy %d payload bytes (remaining %d)\n", __func__, copy_len, curr_skb->len);
kfree_skb(curr_skb);
curr_skb = NULL;
return -EFAULT;
}
if (copy_len == curr_skb->len) {
kfree_skb(curr_skb);
curr_skb = NULL;
p_bt->rd_state = HCI_RX_TYPE;
} else {
skb_pull(curr_skb, copy_len);
}
return copy_len;
default:
BTE("%s: Invalid read state %d\n", __func__, p_bt->rd_state);
return -EINVAL;
}
}
#if 0
//linux for bluez stack
static int amlbt_hci_read(unsigned char *buf_p)
{
static unsigned int bt_type = 0;
unsigned int fw_data = 0;
unsigned int fw_evt = 0;
unsigned int read_len = 0;
unsigned int size = 0;
unsigned char hw_error_evt[4] = {0x04, 0x10, 0x01, 0x00};
static unsigned char read_buff_1[1032+64] = {0};//copy data from fifo
static unsigned char read_buff_2[1032+64] = {0};//Remove the data after 4-byte alignment and reassemble it
w2_usb_bt_t *p_bt = &amlbt_dev;//get bt struct data
if (p_bt->dr_state & BT_DRV_STATE_RECOVERY)
{
size = 4;
p_bt->hardware_error = 1;
memcpy(buf_p,&hw_error_evt,size);
BTF("hw_error_evt:%#x|%#x|%#x|%#x\n", buf_p[0], buf_p[1],buf_p[2], buf_p[3]);
goto exit;
}
if ((p_bt->dr_evt_fifo != NULL) || (p_bt->dr_data_fifo != NULL))
{
mutex_lock(&p_bt->dr_evt_fifo_mutex);
fw_evt = gdsl_fifo_used_size(p_bt->dr_evt_fifo);
mutex_unlock(&p_bt->dr_evt_fifo_mutex);
mutex_lock(&p_bt->dr_data_fifo_mutex);
fw_data = gdsl_fifo_used_size(p_bt->dr_data_fifo);
mutex_unlock(&p_bt->dr_data_fifo_mutex);
}
if (fw_evt == 0 && fw_data == 0)
{
//BTA("read fail no data in fifo \n");
return 0;
}
BTD("%s g_fw_evt_fifo->r:%#x,g_fw_evt_fifo->w:%#x\n", __func__,(unsigned int)p_bt->dr_evt_fifo->r, (unsigned int)p_bt->dr_evt_fifo->w);
BTD("%s g_fw_data_fifo->r:%#x,g_fw_data_fifo->w:%#x\n", __func__,(unsigned int)p_bt->dr_data_fifo->r, (unsigned int)p_bt->dr_data_fifo->w);
//BTA("%s start recv evt_state:(%#x)\n", __func__,evt_state);
BTD("start recv rd %#x\n", p_bt->rd_state);
memset(read_buff_1, 0, sizeof(read_buff_1));
memset(read_buff_2, 0, sizeof(read_buff_2));
BTA("read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[6], read_buff_2[7]);
switch (p_bt->rd_state)
{
case HCI_RX_TYPE: //read type
memset(&bt_type, 0, sizeof(bt_type));
BTA("bt_type:%#x\n", bt_type);
//gdsl_fifo_get_data(g_fw_type_fifo, bt_type, sizeof(bt_type));
mutex_lock(&p_bt->dr_type_fifo_mutex);
gdsl_fifo_get_data(p_bt->dr_type_fifo, (unsigned char *)&bt_type, sizeof(bt_type));
mutex_unlock(&p_bt->dr_type_fifo_mutex);
BTA("evt r:%#x, w:%#x\n", (unsigned int)p_bt->dr_evt_fifo->r, (unsigned int)p_bt->dr_evt_fifo->w);
BTA("pkt_type:bt_type:%#x\n", bt_type);
memcpy(read_buff_2, &bt_type, 1);//copy 1 byte pkt type
BTA("pkt_type:read_buff_2:%#x\n", read_buff_2[0]);
p_bt->rd_state = HCI_RX_HEADER;
BTA("go to case HCI_RX_HEADER\n");
//break;
case HCI_RX_HEADER: // read header
if (bt_type == HCI_EVENT_PKT)
{
mutex_lock(&p_bt->dr_evt_fifo_mutex);
gdsl_fifo_get_data(p_bt->dr_evt_fifo, read_buff_1, 4);
mutex_unlock(&p_bt->dr_evt_fifo_mutex);
BTA("event_head:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[0], read_buff_1[1],read_buff_1[2], read_buff_1[3]);
BTA("event_head:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[4], read_buff_1[5],read_buff_1[6], read_buff_1[7]);
memcpy(&read_buff_2[1], &read_buff_1[1], 3);//event head 2 byte EventCode(1byte) + ParamLength
BTA("event_head:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("event_head:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[7], read_buff_2[7]);
}
else if (bt_type == HCI_ACLDATA_PKT)
{
mutex_lock(&p_bt->dr_data_fifo_mutex);
gdsl_fifo_get_data(p_bt->dr_data_fifo, read_buff_1, 8);
mutex_unlock(&p_bt->dr_data_fifo_mutex);
BTA("acldata_head:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[0], read_buff_1[1],read_buff_1[2], read_buff_1[3]);
BTA("acldata_head:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[4], read_buff_1[5],read_buff_1[6], read_buff_1[7]);
memcpy(&read_buff_2[1], &read_buff_1[4], 4);//copy acl head 4 bytes: Handle (12bits) + Packet_Boundary_Flag(2bits) + BC_flag(2bits) + data_len(2bytes)
BTA("acldata_head:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("acldata_head:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[6], read_buff_2[7]);
}
p_bt->rd_state = HCI_RX_PAYLOAD;
BTA("go to case HCI_RX_PAYLOAD\n");
//break;
case HCI_RX_PAYLOAD: //read payload
if (bt_type == HCI_EVENT_PKT)
{
read_len = read_buff_1[2];//payload len
read_len -= 1;//????
read_len = ((read_len + 3) & 0xFFFFFFFC);
BTA("read_len: %d\n",read_len);
mutex_lock(&p_bt->dr_evt_fifo_mutex);
gdsl_fifo_get_data(p_bt->dr_evt_fifo, read_buff_1, read_len);
mutex_unlock(&p_bt->dr_evt_fifo_mutex);
memcpy(&read_buff_2[4], read_buff_1, read_len);
BTA("event_payload:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[0], read_buff_1[1],read_buff_1[2], read_buff_1[3]);
BTA("event_payload:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[4], read_buff_1[5],read_buff_1[6], read_buff_1[7]);
BTA("event_payload:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("event_payload:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[6], read_buff_2[7]);
}
else if (bt_type == HCI_ACLDATA_PKT)
{
read_len = ((read_buff_1[7] << 8) | (read_buff_1[6]));
read_len = ((read_len + 3) & 0xFFFFFFFC);
BTA("read_len: %d\n",read_len);
mutex_lock(&p_bt->dr_data_fifo_mutex);
gdsl_fifo_get_data(p_bt->dr_data_fifo, read_buff_1, read_len);
mutex_unlock(&p_bt->dr_data_fifo_mutex);
memcpy(&read_buff_2[5], read_buff_1, read_len);
BTA("acl_payload:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[0], read_buff_1[1],read_buff_1[2], read_buff_1[3]);
BTA("acl_payload:read_buff_1:%#x|%#x|%#x|%#x\n", read_buff_1[4], read_buff_1[5],read_buff_1[6], read_buff_1[7]);
BTA("acl_payload:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("acl_payload:read_buff_2:%#x|%#x|%#x|%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[6], read_buff_2[7]);
}
p_bt->rd_state = HCI_RX_TYPE;
BTA("read data success go to next HCI_RX_TYPE\n");
break;
default:
BTA("%s, evt_state error!!\n", __func__);
break;
}
if (bt_type == HCI_EVENT_PKT)
{
size = read_buff_2[2] + 3;//hdr + ParamLength acl=4+ParamLength
BTA("amlbt event pkt size: %d\n",size);
BTA("read_buff_2:%#x,%#x,%#x,%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("read_buff_2:%#x,%#x,%#x,%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[6], read_buff_2[7]);
}
else if (bt_type == HCI_ACLDATA_PKT)
{
//size = read_buff_2[2] + 4;//hdr + ParamLength acl=4+ParamLength
size = ((read_buff_2[4] << 8) | (read_buff_2[3])) + 5;
BTA("amlbt acl pkt size: %d\n",size);
BTA("read_buff_2:%#x,%#x,%#x,%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("read_buff_2:%#x,%#x,%#x,%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[6], read_buff_2[7]);
}
if (size)
{
BTA("amlbt read END read_size: %d\n",size);
BTA("read_buff_2:%#x,%#x,%#x,%#x\n", read_buff_2[0], read_buff_2[1],read_buff_2[2], read_buff_2[3]);
BTA("read_buff_2:%#x,%#x,%#x,%#x\n", read_buff_2[4], read_buff_2[5],read_buff_2[6], read_buff_2[7]);
}
memcpy(buf_p,read_buff_2,size);
BTA("data-bluez:buf_p:%#x|%#x|%#x|%#x\n", buf_p[0], buf_p[1],buf_p[2], buf_p[3]);
BTA("data-bluez:buf_p:%#x|%#x|%#x|%#x\n", buf_p[4], buf_p[5],buf_p[6], buf_p[7]);
BTA("data-bluez:buf_p:%#x|%#x|%#x|%#x\n", buf_p[8], buf_p[9],buf_p[10], buf_p[11]);
BTA("data-bluez:buf_p:%#x|%#x|%#x|%#x\n", buf_p[12], buf_p[13],buf_p[14], buf_p[15]);
return size;
exit:
return size;
}
static void amlbt_hci_receive(struct work_struct *work)
{
unsigned char buf[1032+64] = {0};
int i, len = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;//get bt struct data
//struct aml_bt_info *info = container_of(work, struct aml_bt_info, receive_work);
struct aml_bt_info *info = container_of(to_delayed_work(work), struct aml_bt_info, receive_work);
if (!info) {
BTF("unknown device");
return;
}
len = amlbt_hci_read(buf);
if (len == 0) {
//BTF("fifo no data sleep 10ms\n");
//set_current_state(TASK_INTERRUPTIBLE);
//schedule_timeout(msecs_to_jiffies(10));
//schedule_work(&info->receive_work);
queue_delayed_work(info->workqueue, &info->receive_work, msecs_to_jiffies(10));
return;
}
for (i = 0; i < len; i++) {
//Allocate packet
if (!info->rx_skb) {
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
if (!info->rx_skb) {
BTF("Can't allocate mem for new packet");
return;
}
}
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
hci_skb_pkt_type(info->rx_skb) = buf[i];
switch (hci_skb_pkt_type(info->rx_skb)) {
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
info->rx_count = HCI_EVENT_HDR_SIZE;
BTA("check if in HCI_EVENT_PKT\n");
break;
case HCI_ACLDATA_PKT:
info->rx_state = RECV_WAIT_ACL_HEADER;
info->rx_count = HCI_ACL_HDR_SIZE;
BTA("check if in HCI_ACLDATA_PKT\n");
break;
case HCI_SCODATA_PKT:
info->rx_state = RECV_WAIT_SCO_HEADER;
info->rx_count = HCI_SCO_HDR_SIZE;
break;
default:
/* unknown packet */
BTF("unknown HCI packet with type 0x%02x received",hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
info->rx_skb = NULL;
info->rx_state = 0;
info->rx_count = 0;
break;
}
} else {
if (skb_tailroom(info->rx_skb) >= 1)
{
unsigned char *skb_tail = skb_put(info->rx_skb, 1);
*skb_tail = buf[i];
info->rx_count--;
} else {
kfree_skb(info->rx_skb);
info->rx_skb = NULL;
info->rx_state = 0;// Reset state and count
info->rx_count = 0;
BTF("skb tailroom < 1 return\n");
//schedule_work(&info->receive_work);
queue_delayed_work(info->workqueue, &info->receive_work, 0);
return;
}
if (info->rx_count == 0) {
int dlen;
struct hci_event_hdr *eh;
struct hci_acl_hdr *ah;
struct hci_sco_hdr *sh;
switch (info->rx_state) {
case RECV_WAIT_EVENT_HEADER:
eh = hci_event_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = eh->plen;
BTA("check if in RECV_WAIT_EVENT_HEADER\n");
break;
case RECV_WAIT_ACL_HEADER:
ah = hci_acl_hdr(info->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = dlen;
BTA("check if in RECV_WAIT_ACL_HEADER\n");
break;
case RECV_WAIT_SCO_HEADER:
sh = hci_sco_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = sh->dlen;
break;
case RECV_WAIT_DATA:
hci_recv_frame(info->hdev, info->rx_skb);
info->rx_skb = NULL;
info->rx_state = 0;//Reset state and count
info->rx_count = 0;
BTA("check if in RECV_WAIT_DATA\n");
break;
}
}
}
}
info->hdev->stat.byte_rx += len;
if (!p_bt->hardware_error)
{
//schedule_work(&info->receive_work);
queue_delayed_work(info->workqueue, &info->receive_work, 0);
}else {
p_bt->hardware_error = 0;
BTF("clear hardware error:%#x\n", p_bt->hardware_error);
}
}
#endif
static int amlbt_report_hardware_error(struct aml_bt_info *info)
{
struct sk_buff *skb;
unsigned char hw_error_evt[4] = {0x04, 0x10, 0x01, 0x00};
BTF("%s\n", __func__);
skb = bt_skb_alloc(4, GFP_ATOMIC);
if (!skb) {
BTE("Failed to allocate SKB for hardware error\n");
return -ENOMEM;
}
skb_put_data(skb, hw_error_evt, 4);
hci_recv_frame(info->hdev, skb);
info->hdev->stat.byte_rx += 4;
BTF("hw_error_evt: %#x|%#x|%#x|%#x\n",
hw_error_evt[0], hw_error_evt[1], hw_error_evt[2], hw_error_evt[3]);
return 0;
}
static void amlbt_hci_receive(struct work_struct *work)
{
struct sk_buff *skb = NULL;
struct aml_bt_info *info = container_of(to_delayed_work(work), struct aml_bt_info, receive_work);
w2_usb_bt_t *p_bt = &amlbt_dev;
unsigned long flags;
bool is_empty;
if (!info || !info->hdev) {
BTE("Invalid info or hdev\n");
return;
}
if (p_bt->dr_state & BT_DRV_STATE_RECOVERY)
{
amlbt_report_hardware_error(info);
return;
}
spin_lock(&p_bt->bt_rx_queue_lock);
while ((skb = skb_dequeue(&p_bt->bt_rx_queue)) != NULL) {
spin_unlock(&p_bt->bt_rx_queue_lock);
hci_recv_frame(info->hdev, skb);
info->hdev->stat.byte_rx += skb->len;
spin_lock(&p_bt->bt_rx_queue_lock);
}
spin_unlock(&p_bt->bt_rx_queue_lock);
spin_lock(&p_bt->bt_rx_queue_lock);
is_empty = skb_queue_empty(&p_bt->bt_rx_queue);
spin_unlock(&p_bt->bt_rx_queue_lock);
queue_delayed_work(info->workqueue, &info->receive_work, is_empty ? msecs_to_jiffies(10) : 0);
}
static int amlbt_hci_write(struct sk_buff *skb)
{
unsigned int actual = 0;
unsigned int i = 0;
static unsigned int w_type = 0;
unsigned int count = 0;
w2_usb_bt_t *p_bt = &amlbt_dev;//get bt struct data
BTA("%s skb %p\n", __func__,skb);
memset(g_bluez_buf, 0, HCI_MAX_FRAME_SIZE);
//remove pkt type get hci raw data
count = skb->len - 1;
memcpy(g_bluez_buf, &skb->data[1], count);
w_type = hci_skb_pkt_type(skb);
BTA("get hci raw data:w_type:%d\n",w_type);
/*
if (w_type == HCI_COMMAND_PKT)
{
for (i = 0; i < skb->len; i++)
{
BTA("g_bluez_buf %#x|", g_bluez_buf[i]);
}
}
*/
switch (w_type) {
case HCI_COMMAND_PKT:
actual = amlbt_send_hci_cmd(p_bt, g_bluez_buf, count);//keep 4 bytes aligned len = len + 3;
break;
case HCI_ACLDATA_PKT:
actual = amlbt_send_hci_data(p_bt, g_bluez_buf, count);
break;
case HCI_SCODATA_PKT:
break;
}
BTA("actual len-1:%d\n",actual);
actual = skb->len;
BTA("actual len-2:%d\n",actual);
return actual;
}
static void amlbt_hci_write_wakeup(struct aml_bt_info *info)
{
unsigned int n = 0;
BTA("%s\n", __func__);
if (!info)
{
BTF("unknown device");
return;
}
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
{
set_bit(XMIT_WAKEUP, &(info->tx_state));
BTF("now is XMIT_SENDING");
return;
}
do {
register struct sk_buff *skb;
int len;
clear_bit(XMIT_WAKEUP, &(info->tx_state));
skb = skb_dequeue(&(info->txq));
if (!skb)
{
BTF("skb is null");
clear_bit(XMIT_SENDING, &(info->tx_state));
break;
}
BTA("%s skb %p len %d",__func__, skb, skb->len);
/*
for (n = 0; n < skb->len; n++)
{
BTA("skb->data: %#x\n", skb->data[n]);
}
*/
//Send frame
len = amlbt_hci_write(skb);
set_bit(XMIT_WAKEUP, &(info->tx_state));
BTA("len %d",len);
if (len == skb->len) {
BTA("kfree_skb %p",skb);
kfree_skb(skb);
skb = NULL;
} else {
BTA("queue head");
skb_pull(skb, len);
skb_queue_head(&(info->txq), skb);
}
info->hdev->stat.byte_tx += len;
/*after sending the last data frame clear XMIT_WAKEUP status and exit loop*/
if (!skb)
{
clear_bit(XMIT_WAKEUP, &(info->tx_state));
BTA("clear_bit");
}
} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
clear_bit(XMIT_SENDING, &(info->tx_state));
}
/* ======================== HCI interface ======================== */
static int amlbt_hci_flush(struct hci_dev *hdev)
{
struct aml_bt_info *info = hci_get_drvdata(hdev);
BTF("%s\n", __func__);
if (!info) {
BTE("info is NULL, skip flush\n");
return 0;
}
//Drop TX queue
skb_queue_purge(&(info->txq));
// Cancel delayed work if it's still pending
if (delayed_work_pending(&info->receive_work)) {
cancel_delayed_work_sync(&info->receive_work);
}
return 0;
}
static int amlbt_hci_open(struct hci_dev *hdev)
{
struct aml_bt_info *info = hci_get_drvdata(hdev);
w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s, version:%s\n", __func__,AML_W2U_VERSION);
if (amlbt_res_init(&amlbt_dev) != 0)
{
BTE("amlbt_res_init failed!\n");
return -1;
}
amlbt_task_start(&amlbt_dev);
p_bt->firmware_start = 1;
BTF("firmware_start %#x\n", p_bt->firmware_start);
//send recv task to work queue
//schedule_work(&info->receive_work)
queue_delayed_work(info->workqueue, &info->receive_work, 0);;
BTF("%s: start schedule bluez receive work\n", __func__);
return 0;
}
static int amlbt_hci_close(struct hci_dev *hdev)
{
//struct aml_bt_info *info = hci_get_drvdata(hdev);
w2_usb_bt_t *p_bt = &amlbt_dev;
BTF("%s\n", __func__);
amlbt_hci_flush(hdev);
//task quit
amlbt_kill_irq_task(p_bt);
return 0;
}
static int amlbt_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct aml_bt_info *info = hci_get_drvdata(hdev);
BTA("%s \n", __func__);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
/*Preoend skb with frame type*/
memcpy(skb_push(skb,1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
//spin_lock(&(info->lock));
amlbt_hci_write_wakeup(info);
//spin_unlock(&(info->lock));
return 0;
}
static int amlbt_register_hci_dev(void)
{
BTF("%s \n", __func__);
if (hci_register_dev(info->hdev) < 0)
{
BTF("%s:Failed to register hci_dev\n", __func__);
info->hdev = NULL;
hci_free_dev(info->hdev);
return -1;
}
info->reg_flag = 1;
BTF("%s:success to register hci_dev reg_flag %d\n", __func__,info->reg_flag);
return 0;
}
static int amlbt_hci_dev_init(struct platform_device *dev)
{
int err = 0;
struct hci_dev *hdev;
BTF("%s\n", __func__);
//malloc aml_bt_info struct backup
info = devm_kzalloc(&dev->dev, sizeof(struct aml_bt_info), GFP_KERNEL);
if (!info) {
BTF("%s:Failed to malloc aml_bt \n", __func__);
return 0;
}
spin_lock_init(&(info->lock));
skb_queue_head_init(&(info->txq));
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = NULL;
info->reg_flag = 0;
//info->workqueue = create_singlethread_workqueue("bluetooth_wq");
info->workqueue = alloc_workqueue("bluetooth_wq", WQ_MEM_RECLAIM, 1);
if (!info->workqueue) {
BTF("%s:Failed to create workqueue\n", __func__);
return -1;
}
//INIT_WORK(&info->receive_work, amlbt_hci_receive);
INIT_DELAYED_WORK(&info->receive_work, amlbt_hci_receive);
// allocate hci dev
hdev = hci_alloc_dev();
if (!hdev)
{
BTE("%s:Failed to allocate hci_dev\n", __func__);
destroy_workqueue(info->workqueue);
return -1;
}
BTF("%s:success to allocate hci_dev\n", __func__);
info->hdev = hdev;
hdev->bus = 0;
hci_set_drvdata(hdev, info);
SET_HCIDEV_DEV(hdev, &dev->dev);
hdev->open = amlbt_hci_open;
hdev->close = amlbt_hci_close;
hdev->flush = amlbt_hci_flush;
hdev->send = amlbt_hci_send_frame;
info->pdev = dev;//get dev
//set info point to dev point
platform_set_drvdata(dev, info);
BTF("%s reg_flag %d\n", __func__,info->reg_flag);
return err;
}
static void amlbt_hci_dev_deinit(void)
{
BTF("%s\n", __func__);
if (!info) {
BTE("info is NULL, nothing to deinit\n");
return;
}
cancel_delayed_work_sync(&info->receive_work);
skb_queue_purge(&(info->txq));
if (info->workqueue) {
destroy_workqueue(info->workqueue);
info->workqueue = NULL;
}
if (info->reg_flag) {
hci_unregister_dev(info->hdev);
info->reg_flag = 0;
BTF("%s: unregistered hci_dev\n", __func__);
}
if (info->hdev) {
hci_free_dev(info->hdev);
info->hdev = NULL;
}
platform_set_drvdata(info->pdev, NULL);
//kfree(info);
//info = NULL;
}
int amlbt_w2u_init(void)
{
int ret = 0;
struct platform_device *p_device = &amlbt_device;
struct platform_driver *p_driver = &amlbt_driver;
BTF("%s, type:%d, log level:%d \n", __func__, amlbt_if_type, g_dbg_level);
ret = platform_device_register(p_device);
if (ret)
{
dev_err(&p_device->dev, "platform_device_register failed!\n");
return ret;
}
ret = platform_driver_register(p_driver);
if (ret)
{
dev_err(&p_device->dev, "platform_driver_register failed!\n");
platform_device_unregister(p_device);
return ret;
}
dev_info(&p_device->dev, "Init OK!\n");
return ret;
}
void amlbt_w2u_exit(void)
{
struct platform_device *p_device = &amlbt_device;
struct platform_driver *p_driver = &amlbt_driver;
BTF("%s, type:%d, log level:%d \n", __func__, amlbt_if_type, g_dbg_level);
platform_device_unregister(p_device);
platform_driver_unregister(p_driver);
}