4601 lines
142 KiB
C
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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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 &= 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 &= 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 &= 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 &= 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);
|
|
}
|
|
|