Files

1990 lines
61 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/of.h>
#include <linux/of_gpio.h>
#include <linux/version.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0))
#include <linux/panic_notifier.h>
#endif
#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 "intf.h"
#include "intf_usb.h"
#include "rc_list.h"
#include "debug_dev.h"
#include "driver.h"
#include "chip.h"
struct crg_msc_cbw *g_cmd_buf;
static void amlbt_intf_usb_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 amlbt_intf_usb_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;
}
int amlbt_intf_usb_check(void)
{
amlbt_t *p_bt = amlbt_intf_get_p_bt();
if (g_udev == NULL)
{
BTE("interface NULL");
return -1;
}
if (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 || p_bt->excp_res.notify_recy
#ifdef CONFIG_AML_BT_USB_HOTPLUG
|| bus_state_detect.usb_unplug
#endif
)
{
if (!(p_bt->pm_res.dr_state & BT_DRV_STATE_WAIT_RECOVERY))
{
amlbt_intf_drv_state_set(BT_DRV_STATE_WAIT_RECOVERY);
}
#ifdef CONFIG_AML_BT_USB_HOTPLUG
BTE("bus_err %d reset %d recy %d udev %d, unplug %d notify %d", bus_state_detect.bus_err, bus_state_detect.bus_reset_ongoing,
bus_state_detect.is_recy_ongoing, g_udev->state, bus_state_detect.usb_unplug, p_bt->excp_res.notify_recy);
#else
BTE("bus_err %d reset %d recy %d udev %d notify %d", bus_state_detect.bus_err, bus_state_detect.bus_reset_ongoing,
bus_state_detect.is_recy_ongoing, g_udev->state, p_bt->excp_res.notify_recy);
#endif
return -1;
}
return 0;
}
static int amlbt_intf_usb_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));
amlbt_intf_usb_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("amlbt_intf_usb_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
}
int amlbt_intf_usb_write_word(unsigned int addr,unsigned int data, unsigned int ep)
{
int len = 4;
int ret = 0;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
if (p_bt->pm_res.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, p_bt->pm_res.dr_state);
return -2;
}
ret = amlbt_intf_usb_check();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
case USB_EP4:
ret = amlbt_intf_usb_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 amlbt_intf_usb_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 *kmalloc_buf = NULL;
USB_BEGIN_LOCK();
kmalloc_buf = (unsigned char *)kzalloc(len, GFP_DMA | GFP_ATOMIC);
if (!kmalloc_buf) {
BTE("amlbt_intf_usb_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));
amlbt_intf_usb_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("amlbt_intf_usb_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 *)kmalloc_buf, len, &actual_length, AML_USB_CONTROL_MSG_TIMEOUT);
if (ret) {
BTE("amlbt_intf_usb_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, kmalloc_buf, actual_length);
kfree(kmalloc_buf);
USB_END_LOCK();
return ret;
err_kfree:
kfree(kmalloc_buf);
err_unlock:
USB_END_LOCK();
return ret;
}
int amlbt_intf_usb_read_word(unsigned int addr, unsigned int ep, unsigned int *value)
{
int len = 4;
int ret = 0;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
if (p_bt->pm_res.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, p_bt->pm_res.dr_state);
return -2;
}
ret = amlbt_intf_usb_check();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
case USB_EP4:
ret = amlbt_intf_usb_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 amlbt_intf_usb_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));
amlbt_intf_usb_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("amlbt_intf_usb_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));
amlbt_intf_usb_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("amlbt_intf_usb_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_DMA | GFP_ATOMIC);
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("amlbt_intf_usb_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;
}
int amlbt_intf_usb_write_sram(unsigned char *buf, unsigned char *sram_addr, unsigned int len, unsigned int ep)
{
int ret = 0;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
if (p_bt->pm_res.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, p_bt->pm_res.dr_state);
return -2;
}
if (len == 0)
{
BTE("EP-%d write len err!\n", ep);
return -1;
}
ret = amlbt_intf_usb_check();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
case USB_EP4:
ret = amlbt_intf_usb_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 amlbt_intf_usb_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));
amlbt_intf_usb_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("amlbt_intf_usb_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_DMA | GFP_ATOMIC);
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("amlbt_intf_usb_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;
}
int amlbt_intf_usb_read_sram(unsigned char *buf,unsigned char *sram_addr, unsigned int len, unsigned int ep)
{
int ret = 0;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
if (p_bt->pm_res.dr_state & BT_DRV_STATE_SUSPEND)
{
BTE("%d:suspend state %d\n", __LINE__, p_bt->pm_res.dr_state);
return -2;
}
if (len == 0)
{
BTE("EP-%d read len err!\n", ep);
return -1;
}
ret = amlbt_intf_usb_check();
if (ret != 0)
{
BTE("%s:%d, error!\n", __func__, __LINE__);
return -1;
}
switch (ep) {
case USB_EP2:
case USB_EP4:
ret = amlbt_intf_usb_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;
}
#if 0
/*
type 0x04: 1 byte
head[evt code, length]: 2 bytes
payload
*/
static int amlbt_intf_usb_event_to_skb_process(amlbt_t *p_bt, unsigned char *evt_buf, unsigned int len)
{
struct sk_buff *skb = NULL;
unsigned int read_len = 0;
unsigned int i = 0;
unsigned char *p = evt_buf;
int ret = 0;
unsigned int push_len = 0;
//BTI("process evt %d\n", len);
while (i < len)
{
read_len = 3 + p[2];
push_len = read_len;
read_len = (read_len + 3) & ~3;
if (i + read_len > len)
{
BTE("%s: Event exceeds buffer [%u,%u,%u], %u > %u\n", __func__, i, push_len, read_len, i+read_len, len);
DUMP_BUF("evt_buf", evt_buf, len);
DUMP_BUF("p", p, len - (p - evt_buf));
return -EMSGSIZE;
}
/* coverity[var_assign:SUPPRESS] */
/* coverity[alloc_fn:SUPPRESS] */
skb = alloc_skb(read_len, GFP_KERNEL);
if (!skb)
{
BTE("%s:%d alloc_skb Failed\n", __func__, __LINE__);
ret = -ENOMEM;
goto error;
}
//BTI("push evt %d[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n", read_len,
// p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
/* coverity[noescape:SUPPRESS] */
if (skb_tailroom(skb) < read_len) {
BTE("%s skb_tailroom(skb) failed!\n", __func__);
ret = -ENOSPC;
goto error;
}
/* coverity[noescape:SUPPRESS] */
if (skb_put_data(skb, p, push_len) == NULL)
{
BTE("%s: skb_put_data failed\n", __func__);
ret = -EFAULT;
goto error;
}
/* coverity[noescape:SUPPRESS] */
skb_queue_tail(&p_bt->bt_res.bt_rx_queue, skb);
p += read_len;
i += read_len;
}
return 0;
error:
if (skb) {
/* coverity[noescape:SUPPRESS] */
kfree_skb(skb);
}
/* coverity[leaked_storage:SUPPRESS] */
return ret;
}
/*
type 0x02: 4 byte
head[handle 2bytes, length 2bytes]: 4 bytes
payload
*/
static int amlbt_intf_usb_data_to_skb_process(amlbt_t *p_bt, unsigned char *data_buf, unsigned int len)
{
struct sk_buff *skb = NULL;
unsigned int read_len = 0;
unsigned int pkt_len = 0;
unsigned int i = 0;
unsigned char *p = data_buf;
int ret = 0;
//BTI("process data %d\n", len);
while (i < len)
{
if (i + 8 > len)
{
BTE("%s: Not enough data for packet header [i=%u, len=%u]\n", \
__func__, i, len);
return -EMSGSIZE;
}
read_len = 5 + ((p[7] << 8) | (p[6]));
//read_len = (read_len + 3) & ~3;
pkt_len = 8 + ((p[7] << 8) | (p[6]));
pkt_len = (pkt_len + 3) & ~3;
if (read_len < 5)
{
BTE("%s: Invalid read_len: %u (should be >= 5)\n", \
__func__, read_len);
return -EINVAL;
}
if (i + pkt_len > len)
{
BTE("%s: Data exceeds buffer [%u,%u,%u], %u > %u\n", __func__, i, pkt_len, read_len, i+pkt_len, len);
DUMP_BUF("data_buf", data_buf, len);
DUMP_BUF("p", p, len - (p - data_buf));
return -EMSGSIZE;
}
/* coverity[var_assign:SUPPRESS] */
/* coverity[alloc_fn:SUPPRESS] */
/* coverity[tainted_data:SUPPRESS] */
skb = alloc_skb(read_len, GFP_KERNEL);
if (!skb)
{
BTE("%s:%d alloc_skb Failed\n", __func__, __LINE__);
ret = -ENOMEM;
goto error;
}
//BTI("push data %d,%d[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n", pkt_len, read_len,
// p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
/* coverity[noescape:SUPPRESS] */
if (skb_tailroom(skb) < read_len) {
BTE("%s skb_tailroom(skb) failed!\n", __func__);
ret = -ENOSPC;
goto error;
}
/* coverity[noescape:SUPPRESS] */
if (skb_put_data(skb, p, 1) == NULL) //The type field is 4-byte aligned, but only one byte is actually used.
{
BTE("%s: skb_put_data failed\n", __func__);
ret = -EFAULT;
goto error;
}
p += 4; //The type field is 4-byte aligned, but only one byte is actually used.
/* coverity[noescape:SUPPRESS] */
/* coverity[overflow_const:SUPPRESS] */
if (skb_put_data(skb, p, read_len-1) == NULL)
{
BTE("%s: skb_put_data failed\n", __func__);
ret = -EFAULT;
goto error;
}
/* coverity[noescape:SUPPRESS] */
skb_queue_tail(&p_bt->bt_res.bt_rx_queue, skb);
p -= 4;
p += pkt_len;
i += pkt_len;
}
return 0;
error:
if (skb) {
/* coverity[noescape:SUPPRESS] */
kfree_skb(skb);
}
/* coverity[leaked_storage:SUPPRESS] */
return ret;
}
/*
type 0x10: 1 byte
head[MHDL(zigbee 0xF5, thread 0xFA), MID, length 2bytes]: 4 bytes
payload + 2 bytes crc
*/
static int amlbt_intf_usb_15p4_to_skb_process(amlbt_t *p_bt, unsigned char *data_buf, unsigned int len)
{
struct sk_buff *skb = NULL;
unsigned int read_len = 0;
unsigned int pkt_len = 0;
unsigned int i = 0;
unsigned char *p = data_buf;
int ret = 0;
//BTI("process iot %d\n", len);
while (i < len)
{
read_len = 7 + ((p[4] << 8) | (p[3]));
//read_len = (read_len + 3) & ~3;
pkt_len = 7 + ((p[4] << 8) | (p[3]));
pkt_len = (pkt_len + 3) & ~3;
if (i + pkt_len > len)
{
BTE("%s: 15p4 exceeds buffer [%u,%u,%u], %u > %u\n", __func__, i, pkt_len, read_len, i+pkt_len, len);
DUMP_BUF("data_buf", data_buf, len);
DUMP_BUF("p", p, len - (p - data_buf));
return -EMSGSIZE;
}
/* coverity[var_assign:SUPPRESS] */
/* coverity[alloc_fn:SUPPRESS] */
/* coverity[tainted_data:SUPPRESS] */
skb = alloc_skb(read_len, GFP_KERNEL);
if (!skb)
{
BTE("%s:%d alloc_skb Failed\n", __func__, __LINE__);
ret = -ENOMEM;
goto error;
}
//BTI("push data %d[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n", read_len,
// p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
/* coverity[noescape:SUPPRESS] */
if (skb_tailroom(skb) < read_len) {
BTE("%s skb_tailroom(skb) failed!\n", __func__);
ret = -ENOSPC;
goto error;
}
/* coverity[noescape:SUPPRESS] */
if (skb_put_data(skb, p, read_len) == NULL)
{
BTE("%s: skb_put_data failed\n", __func__);
ret = -EFAULT;
goto error;
}
if ((p[1] == HCI_15P4_ZIGBEE_PKT || p[1] == HCI_15P4_LOG_PKT) && p_bt->zigbee_res.zigbee_start)
{
/* coverity[noescape:SUPPRESS] */
skb_queue_tail(&p_bt->zigbee_res.zigbee_rx_queue, skb);
}
else if ((p[1] == HCI_15P4_THREAD_PKT || p[1] == HCI_15P4_LOG_PKT) && p_bt->thread_res.thread_start)
{
/* coverity[noescape:SUPPRESS] */
skb_queue_tail(&p_bt->thread_res.thread_rx_queue, skb);
}
else
{
BTE("%s: Unknown packet type %#x\n", __func__, p[1]);
kfree_skb(skb);
return -EPROTO;
}
p += pkt_len;
i += pkt_len;
}
return 0;
error:
if (skb) {
/* coverity[noescape:SUPPRESS] */
kfree_skb(skb);
}
/* coverity[leaked_storage:SUPPRESS] */
return ret;
}
#endif
/*
type 0x04: 1 byte
head[evt code, length]: 2 bytes
payload
*/
static int amlbt_intf_usb_event_to_skb_process(amlbt_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;
//BTI("%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;
//BTI("%s event_type %u event_len %u\n", __func__, pkt_type, event_len);
// 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
skb_queue_tail(&p_bt->bt_res.bt_rx_queue, event_skb);
event_skb = NULL;
offset += aligned_len;
}
return 0;
error:
if (event_skb)
kfree_skb(event_skb);
return ret;
}
/*
type 0x02: 4 byte
head[handle 2bytes, length 2bytes]: 4 bytes
payload
*/
static int amlbt_intf_usb_data_to_skb_process(amlbt_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;
//BTI("%s: acl_size = %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;
}
skb_queue_tail(&p_bt->bt_res.bt_rx_queue, acl_skb);
acl_skb = NULL;
offset += aligned_len;
}
return 0;
error:
if (acl_skb)
kfree_skb(acl_skb);
return ret;
}
/*
type 0x10: 1 byte
head[MHDL(zigbee 0xF5, thread 0xFA), MID, length 2bytes]: 4 bytes
payload + 2 bytes crc
*/
static int amlbt_intf_usb_15p4_to_skb_process(amlbt_t *p_bt, unsigned char *data_buf, unsigned int len)
{
struct sk_buff *skb = NULL;
unsigned int read_len = 0;
unsigned int pkt_len = 0;
unsigned int i = 0;
unsigned char *p = data_buf;
int ret = 0;
//BTI("%s: 15p4_size = %u\n", __func__, len);
while (i < len) {
read_len = 7 + ((p[4] << 8) | (p[3]));
pkt_len = 7 + ((p[4] << 8) | (p[3]));
pkt_len = (pkt_len + 3) & ~3;
if (i + pkt_len > len) {
BTE("%s: 15p4 exceeds buffer [%u,%u,%u], %u > %u\n", __func__, i, pkt_len, read_len, i+pkt_len, len);
DUMP_BUF("data_buf", data_buf, len);
DUMP_BUF("p", p, len - (p - data_buf));
return -EMSGSIZE;
}
skb = alloc_skb(read_len, GFP_KERNEL);
if (!skb) {
BTE("%s:%d alloc_skb Failed\n", __func__, __LINE__);
ret = -ENOMEM;
goto error;
}
//BTI("push data %d[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n", read_len,
// p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
if (skb_put_data(skb, p, read_len) == NULL) {
BTE("%s: skb_put_data failed\n", __func__);
ret = -EFAULT;
goto error;
}
if ((p[1] == HCI_15P4_ZIGBEE_PKT || p[1] == HCI_15P4_LOG_PKT) && p_bt->zigbee_res.zigbee_start) {
skb_queue_tail(&p_bt->zigbee_res.zigbee_rx_queue, skb);
} else if ((p[1] == HCI_15P4_THREAD_PKT || p[1] == HCI_15P4_LOG_PKT) && p_bt->thread_res.thread_start) {
skb_queue_tail(&p_bt->thread_res.thread_rx_queue, skb);
} else {
BTE("%s: Unknown packet type %#x\n", __func__, p[1]);
kfree_skb(skb);
return -EPROTO;
}
skb= NULL;
p += pkt_len;
i += pkt_len;
}
return 0;
error:
if (skb) {
kfree_skb(skb);
}
return ret;
}
static int amlbt_intf_usb_fw_data_process(amlbt_t *p_bt)
{
unsigned char *p_buf = p_bt->usb_res.usb_rx_buf;
amlbt_common_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 _15p4_size = 0;
unsigned char read_reg[16] = {0};
static unsigned char type_buff[USB_RX_TYPE_FIFO_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};
amlbt_res_usb_linux_t *info = &p_bt->usb_res_linux;
int ret = 0;
//check fw gpio
if (p_bt->pm_res.irq_handle)
{
amlbt_intf_wakeup_key_process(p_bt);
}
p_bt->usb_res.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->usb_res.fw_type_fifo->w == p_bt->usb_res.fw_type_fifo->r) // no data
{
return 0;
}
memset(type_buff, 0, sizeof(type_buff));
p_bt->usb_res.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->usb_res.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->diag_res.sink_mode = ((p_buf[23]<<24)|(p_buf[22]<<16)|(p_buf[21]<<8)|p_buf[20]);
p_bt->usb_res._15p4_rx_fifo->w =(unsigned char *)(unsigned long)((p_buf[31]<<24)|(p_buf[30]<<16)|(p_buf[29]<<8)|p_buf[28]);
BTD("dp1 type:w %#lx, r %#lx\n", (unsigned long)p_bt->usb_res.fw_type_fifo->w, (unsigned long)p_bt->usb_res.fw_type_fifo->r);
BTD("dp1 evt:w %#lx, r %#lx\n", (unsigned long)p_bt->usb_res.fw_evt_fifo->w, (unsigned long)p_bt->usb_res.fw_evt_fifo->r);
BTD("dp1 data:w %#lx, r %#lx\n", (unsigned long)p_bt->usb_res.fw_data_fifo->w, (unsigned long)p_bt->usb_res.fw_data_fifo->r);
if (((unsigned long)p_bt->usb_res.fw_type_fifo->w > p_bt->fw_res.rx_type_len) ||
((unsigned long)p_bt->usb_res.fw_evt_fifo->w > p_bt->fw_res.evt_len) ||
((unsigned long)p_bt->usb_res.fw_data_fifo->w > p_bt->fw_res.rx_q_len))
{
amlbt_intf_fw_info();
BTF("%s %d fw_type_fifo->w:%#x fw_type_fifo->r:%#x", __func__, __LINE__,
p_bt->usb_res.fw_type_fifo->w, p_bt->usb_res.fw_type_fifo->r);
BTF("%s %d fw_evt_fifo->w:%#x fw_evt_fifo->r:%#x", __func__, __LINE__,
p_bt->usb_res.fw_evt_fifo->w, p_bt->usb_res.fw_evt_fifo->r);
BTF("%s %d fw_data_fifo->w:%#x fw_data_fifo->r:%#x", __func__, __LINE__,
p_bt->usb_res.fw_data_fifo->w, p_bt->usb_res.fw_data_fifo->r);
ret = -2;
goto err_exit;
}
//copy type fifo
read_fifo.base_addr = &p_buf[p_bt->fw_res.rx_type_addr - p_bt->fw_res.poll_addr];
read_fifo.r = p_bt->usb_res.fw_type_fifo->r;
read_fifo.w = p_bt->usb_res.fw_type_fifo->w;
read_fifo.size = p_bt->fw_res.rx_type_len;
type_size = amlbt_common_gdsl_fifo_get_data(&read_fifo, type_buff, p_bt->fw_res.rx_type_len);
if (type_buff[0] != HCI_ACLDATA_PKT && type_buff[0] != HCI_EVENT_PKT &&
type_buff[0] != HCI_15P4_PKT && type_buff[0] != HCI_ISO_PKT)
{
BTE("%s:%d type error!\n", __func__, __LINE__);
BTE("fw_type_fifo->w:%#x fw_type_fifo->r:%#x", p_bt->usb_res.fw_type_fifo->w, p_bt->usb_res.fw_type_fifo->r);
BTE("fw_evt_fifo->w:%#x fw_evt_fifo->r:%#x", p_bt->usb_res.fw_evt_fifo->w, p_bt->usb_res.fw_evt_fifo->r);
BTE("fw_data_fifo->w:%#x fw_data_fifo->r:%#x", p_bt->usb_res.fw_data_fifo->w, p_bt->usb_res.fw_data_fifo->r);
ret = -2;
goto err_exit;
}
BTP("type fifo:w %#lx, r %#lx\n", (unsigned long)p_bt->usb_res.fw_type_fifo->w, (unsigned long)p_bt->usb_res.fw_type_fifo->r);
BTP("[%#x,%#x,%#x,%#x]\n", type_buff[0], type_buff[4], type_buff[8], type_buff[12]);
p_bt->usb_res.fw_type_fifo->r = read_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->usb_res.fw_evt_fifo->w != p_bt->usb_res.fw_evt_fifo->r)
{
read_fifo.base_addr = &p_buf[p_bt->fw_res.evt_addr - p_bt->fw_res.poll_addr];
read_fifo.r = p_bt->usb_res.fw_evt_fifo->r;
read_fifo.w = p_bt->usb_res.fw_evt_fifo->w;
read_fifo.size = p_bt->fw_res.evt_len;
evt_size = amlbt_common_gdsl_fifo_get_data(&read_fifo, fw_read_buff, p_bt->fw_res.evt_len);
}
BTP("evt fifo:w %#lx, r %#lx\n", (unsigned long)p_bt->usb_res.fw_evt_fifo->w, (unsigned long)p_bt->usb_res.fw_evt_fifo->r);
if (evt_size)
{
if (evt_size > p_bt->fw_res.evt_len)
{
BTE("evt_size %#x \n", evt_size);
ret = -2;
goto err_exit;
}
p_bt->usb_res.fw_evt_fifo->r = read_fifo.r;
/* coverity[overrun-buffer-val:SUPPRESS] */
/* coverity[tainted_data:SUPPRESS] */
if (0 != amlbt_intf_usb_event_to_skb_process(p_bt, fw_read_buff, evt_size))
{
ret = -2;
goto err_exit;
}
}
reg = (((unsigned int)(unsigned long)p_bt->usb_res.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->usb_res.fw_data_fifo->w != p_bt->usb_res.fw_data_fifo->r)
{
ret = amlbt_intf_read_sram(&fw_data_buff[0], p_bt->fw_res.rx_q_addr, p_bt->fw_res.rx_q_len);
if (ret == -1)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
read_fifo.base_addr = fw_data_buff;
read_fifo.r = p_bt->usb_res.fw_data_fifo->r;
read_fifo.w = p_bt->usb_res.fw_data_fifo->w;
read_fifo.size = p_bt->fw_res.rx_q_len;
data_size = amlbt_common_gdsl_fifo_get_data(&read_fifo, fw_read_buff, p_bt->fw_res.rx_q_len);
}
BTP("data fifo:w %#lx, r %#lx\n", (unsigned long)p_bt->usb_res.fw_data_fifo->w, (unsigned long)p_bt->usb_res.fw_data_fifo->r);
if (data_size)
{
if (data_size > p_bt->fw_res.rx_q_len)
{
BTE("data_size %#x \n", data_size);
ret = -2;
goto err_exit;
}
p_bt->usb_res.fw_data_fifo->r = read_fifo.r;
/* coverity[overrun-buffer-val:SUPPRESS] */
if (0 != amlbt_intf_usb_data_to_skb_process(p_bt, fw_read_buff, data_size))
{
ret = -2;
goto err_exit;
}
}
reg = (((unsigned int)(unsigned long)p_bt->usb_res.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);
if (p_bt->diag_res.chip_family_id >= CHIP_W2L)
{
//copy 15.4 fifo
if (p_bt->usb_res._15p4_rx_fifo->w != p_bt->usb_res._15p4_rx_fifo->r)
{
memset(fw_read_buff, 0, sizeof(fw_read_buff));
ret = amlbt_intf_read_sram(fw_read_buff, p_bt->fw_res._15p4_rx_addr, p_bt->fw_res._15p4_rx_len);
if (ret == -1)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
read_fifo.base_addr = fw_read_buff;
read_fifo.r = p_bt->usb_res._15p4_rx_fifo->r;
read_fifo.w = p_bt->usb_res._15p4_rx_fifo->w;
read_fifo.size = p_bt->fw_res._15p4_rx_len;
_15p4_size = amlbt_common_gdsl_fifo_get_data(&read_fifo, &fw_read_buff[p_bt->fw_res._15p4_rx_len], p_bt->fw_res._15p4_rx_len);
BTP("15.4 get size %d\n", _15p4_size);
BTP("15.4 rx:w %#lx, r %#lx\n", (unsigned long)p_bt->usb_res._15p4_rx_fifo->w, (unsigned long)p_bt->usb_res._15p4_rx_fifo->r);
if (_15p4_size)
{
if (_15p4_size > p_bt->fw_res._15p4_rx_len)
{
BTE("_15p4_size %#x \n", _15p4_size);
ret = -2;
goto err_exit;
}
p_bt->usb_res._15p4_rx_fifo->r = read_fifo.r;
/* coverity[overrun-buffer-val:SUPPRESS] */
amlbt_intf_usb_15p4_to_skb_process(p_bt, &fw_read_buff[p_bt->fw_res._15p4_rx_len], _15p4_size);
}
}
reg = (((unsigned int)(unsigned long)p_bt->usb_res._15p4_rx_fifo->r) & 0x7ff);
read_reg[8] = (reg & 0xff);
read_reg[9] = ((reg >> 8) & 0xff);
read_reg[10] = ((reg >> 16) & 0xff);
read_reg[11] = ((reg >> 24) & 0xff);
}
ret = amlbt_intf_write_sram(&read_reg[0], p_bt->fw_res.rx_type_r, sizeof(read_reg));
if (ret == -1)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
if (test_bit(HDEV_RUNNING, &info->hdev_flags) && !skb_queue_empty(&p_bt->bt_res.bt_rx_queue))
{
//wake_up_interruptible(&p_bt->bt_res.bt_wait_queue);
//queue_work(info->workqueue, &info->receive_work.work);
queue_delayed_work(info->workqueue, &info->receive_work, 0);
}
if (!skb_queue_empty(&p_bt->zigbee_res.zigbee_rx_queue))
{
wake_up_interruptible(&p_bt->zigbee_res.zigbee_wait_queue);
}
if (!skb_queue_empty(&p_bt->thread_res.thread_rx_queue))
{
wake_up_interruptible(&p_bt->thread_res.thread_wait_queue);
}
return 0;
err_exit:
return ret;
}
static int amlbt_intf_usb_driver_state_process(amlbt_t *p_bt)
{
int ret = BT_DRV_NONE;
if ((p_bt->pm_res.dr_state & BT_DRV_STATE_SUSPEND_ENTRY) || \
(p_bt->pm_res.dr_state & BT_DRV_STATE_SUSPEND))
{
ret = BT_DRV_SUSPEND;
goto exit;
}
if (p_bt->pm_res.dr_state & BT_DRV_STATE_RESUME)
{
ret = BT_DRV_RESUME;
goto exit;
}
if (p_bt->pm_res.dr_state & BT_DRV_STATE_WAIT_RECOVERY)
{
ret = BT_DRV_WAIT_RECOVERY;
goto exit;
}
if (p_bt->pm_res.dr_state & BT_DRV_STATE_RECOVERY)
{
ret = BT_DRV_CLOSED;
}
exit:
return ret;
}
void amlbt_intf_usb_rx_work(struct work_struct *work)
{
int actual_length = 0;
int ret = 0;
unsigned char *data;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
if (p_bt->pm_res.dr_state == 0)
{
mutex_lock(&p_bt->diag_res.bt_debug_mutex);
ret = amlbt_intf_read_sram(p_bt->usb_res.usb_rx_buf, p_bt->fw_res.poll_addr, p_bt->fw_res.poll_len);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
p_bt->usb_res.usb_rx_len = 0;
mutex_unlock(&p_bt->diag_res.bt_debug_mutex);
return ;
}
p_bt->usb_res.usb_rx_len = p_bt->fw_res.poll_len;
mutex_unlock(&p_bt->diag_res.bt_debug_mutex);
}
else
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, p_bt->pm_res.dr_state);
p_bt->usb_res.usb_rx_len = 0;
}
ret = amlbt_intf_usb_driver_state_process(p_bt);
if (ret == BT_DRV_NONE)
{
data = p_bt->usb_res.usb_rx_buf;
actual_length = p_bt->usb_res.usb_rx_len;
if (actual_length == p_bt->fw_res.poll_len)
{
mutex_lock(&p_bt->diag_res.bt_debug_mutex);
ret = amlbt_intf_usb_fw_data_process(p_bt);
mutex_unlock(&p_bt->diag_res.bt_debug_mutex);
if (ret == -2)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
if (!(p_bt->pm_res.dr_state & BT_DRV_STATE_WAIT_RECOVERY))
{
p_bt->pm_res.dr_state = BT_DRV_STATE_WAIT_RECOVERY;
amlbt_intf_queue_work(p_bt->excp_res.exception_work_wq, &p_bt->excp_res.exception_work);
}
return ;
}
}
else
{
BTE("%s:%d usb rx data length not match!!, %d\n", __func__, __LINE__, actual_length);
}
p_bt->usb_res.usb_rx_len = 0;
}
else if (ret == BT_DRV_CLOSED)
{
BTW("%s:%d BT_DRV_CLOSED!\n", __func__, __LINE__);
}
else if (ret == BT_DRV_SUSPEND || ret == BT_DRV_RESUME || ret == BT_DRV_WAIT_RECOVERY)
{
BTA("%s:%d BT_DRV_SUSPEND or BT_DRV_RESUME or BT_DRV_WAIT_RECOVERY!\n", __func__, __LINE__);
}
}
void amlbt_intf_usb_exception_func(struct work_struct *work)
{
//int ret = 0;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
BTF("%s \n", __func__);
amlbt_intf_exception_func(p_bt);
}
int amlbt_intf_usb_send_hci_cmd(amlbt_t *p_bt, unsigned char *data, unsigned int len)
{
int ret = 0;
unsigned int val = 0;
amlbt_common_gdsl_fifo_t t_fifo;
BTA("%s, len %d \n", __func__, len);
if (p_bt->usb_res.tx_cmd_fifo == NULL)
{
BTE("%s: p_bt->usb_res.tx_cmd_fifo NULL!!!!\n", __func__);
goto err_exit;
}
t_fifo = *p_bt->usb_res.tx_cmd_fifo;
if (len) {
//BTF("hci_cmd:%#x,%#x,%#x,%#x",data[0],data[1],data[2],data[3]);
BTD("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_intf_read_word(p_bt->fw_res.cmd_r, &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 = amlbt_common_gdsl_write_data(&t_fifo, data, len);
//step 4: Update the write pointer and write to WiFi SRAM
if (ret < 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
if (ret == -2) //fifo full
{
if (!(p_bt->pm_res.dr_state & BT_DRV_STATE_WAIT_RECOVERY))
{
amlbt_intf_drv_state_set(BT_DRV_STATE_RECOVERY);
}
}
goto err_exit;
}
BTA("before write:r:%#lx, w:%#lx\n", (unsigned long)p_bt->usb_res.tx_cmd_fifo->r, (unsigned long)p_bt->usb_res.tx_cmd_fifo->w);
ret = amlbt_intf_write_word(p_bt->fw_res.cmd_w, (unsigned long)t_fifo.w & 0xfff);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
*p_bt->usb_res.tx_cmd_fifo = t_fifo;
BTP("len %#x:w %#lx, r %#lx\n", len, (unsigned long)p_bt->usb_res.tx_cmd_fifo->w,
(unsigned long)p_bt->usb_res.tx_cmd_fifo->r);
//amlbt_debug_get_cmd((unsigned int)(unsigned long)p_bt->usb_res.tx_cmd_fifo->w,
// (unsigned int)(unsigned long)p_bt->usb_res.tx_cmd_fifo->r, data);
p_bt->diag_res.cmd_cnt++;
amlbt_intf_diag_add(p_bt, HCI_COMMAND_PKT, (unsigned int)(unsigned long)p_bt->usb_res.tx_cmd_fifo->w,
(unsigned int)(unsigned long)p_bt->usb_res.tx_cmd_fifo->r, data, 0, p_bt->diag_res.cmd_cnt);
return ret;
err_exit:
return ret;
}
static unsigned int amlbt_intf_usb_get_tx_prio(amlbt_common_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 = USB_TX_Q_MAX_PRIO;
}
return prio;
}
int amlbt_intf_usb_send_hci_data(amlbt_t *p_bt, unsigned char *data, unsigned int len)
{
int ret = 0;
unsigned int i = 0;
unsigned int used_cnt = 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
amlbt_common_gdsl_tx_q_t ro_tx_q = *p_bt->usb_res.tx_q;
BTA("%s, len:%d\n", __func__, len);
ret = amlbt_intf_read_sram((unsigned char *)tx_buff, p_bt->usb_res.tx_q[0].tx_q_prio_addr, sizeof(tx_buff));
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];
}
BTA("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]);
BTA("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]);
BTA("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] = USB_TX_Q_MAX_PRIO;
}
if (tx_q_status[i] == GDSL_TX_Q_USED)
{
used_cnt++;
}
}
for (i = 0; i < USB_TX_Q_NUM; i++)
{
p_bt->usb_res.tx_q[i].tx_q_dev_index = tx_q_index[i];
p_bt->usb_res.tx_q[i].tx_q_status = tx_q_status[i];
p_bt->usb_res.tx_q[i].tx_q_prio = tx_q_prio[i];
}
for (i = 0; i < USB_TX_Q_NUM; i++)
{
if (p_bt->usb_res.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__);
for (i = 0; i < USB_TX_Q_NUM; i++)
{
BTI("[%#x,%#x,%#x]", (unsigned int)p_bt->usb_res.tx_q[i].tx_q_prio,
(unsigned int)p_bt->usb_res.tx_q[i].tx_q_dev_index,
(unsigned int)p_bt->usb_res.tx_q[i].tx_q_status);
BTI("{%#x,%#x,%#x}", tx_q_prio[i], tx_q_index[i],tx_q_status[i]);
}
return -1;
}
ro_tx_q = *p_bt->usb_res.tx_q;
prio = amlbt_intf_usb_get_tx_prio(p_bt->usb_res.tx_q, acl_handle);
p_bt->usb_res.tx_q[i].tx_q_prio = (++prio & USB_TX_Q_MAX_PRIO);
p_bt->usb_res.tx_q[i].tx_q_dev_index = acl_handle;
p_bt->usb_res.tx_q[i].tx_q_status = GDSL_TX_Q_USED;
len = (len + 3) & ~3;
ret = amlbt_intf_write_sram(data, p_bt->usb_res.tx_q[i].tx_q_addr, len);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
USB_END_LOCK();
*p_bt->usb_res.tx_q = ro_tx_q;
goto err_exit;
}
tx_buff[0] = p_bt->usb_res.tx_q[i].tx_q_prio;
tx_buff[1] = p_bt->usb_res.tx_q[i].tx_q_dev_index;
tx_buff[2] = p_bt->usb_res.tx_q[i].tx_q_status;
tx_buff[3] = 0;
BTA("TX:%d,%d,%#x,%#x\n", i, len, acl_handle, p_bt->usb_res.tx_q[i].tx_q_prio);
ret = amlbt_intf_write_sram((unsigned char *)&tx_buff[0], p_bt->usb_res.tx_q[i].tx_q_prio_addr, sizeof(unsigned int)*4);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
*p_bt->usb_res.tx_q = ro_tx_q;
goto err_exit;
}
p_bt->diag_res.acl_cnt++;
amlbt_intf_diag_add(p_bt, HCI_ACLDATA_PKT, i, used_cnt, data, 0, p_bt->diag_res.acl_cnt);
BTA("%s, Actual length:%d\n", __func__, len);
return 0;
err_exit:
return ret;
}
int amlbt_intf_usb_send_15p4_data(amlbt_t *p_bt,unsigned char *data, unsigned int len)
{
int ret = 0;
unsigned int val = 0;
amlbt_common_gdsl_fifo_t p_fifo;
BTP("%s, len %d \n", __func__, len);
if (p_bt->usb_res._15p4_tx_fifo == NULL)
{
BTE("%s: p_bt->usb_res._15p4_tx_fifo NULL!!!!\n", __func__);
return -1;
}
p_fifo = *p_bt->usb_res._15p4_tx_fifo;
len = ((len + 3) & 0xFFFFFFFC);//Keep 4 bytes aligned
BTA("%s, Actual length %d \n", __func__, len);
//step 1: Update the tx FIFO read pointer
ret = amlbt_intf_read_word(p_bt->fw_res._15p4_tx_r, &val);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
p_fifo.r = (unsigned char *)(unsigned long)val;
BTP("15p4 tx r %#x\n", (unsigned long)p_fifo.r);
BTP("15p4 tx w %#x\n", (unsigned long)p_fifo.w);
//step 2: Check the command FIFO space
//step 3: Write HCI commands to WiFi SRAM
ret = amlbt_common_gdsl_write_data(&p_fifo, data, len);
if (ret < 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
if (ret == -2) //fifo full
{
if (!(p_bt->pm_res.dr_state & BT_DRV_STATE_WAIT_RECOVERY))
{
amlbt_intf_drv_state_set(BT_DRV_STATE_RECOVERY);
}
}
goto err_exit;
}
//step 4: Update the write pointer and write to WiFi SRAM
//BTI("15p4 before write:r:%#lx, w:%#lx\n", (unsigned long)p_bt->usb_res._15p4_tx_fifo->r, (unsigned long)p_bt->usb_res._15p4_tx_fifo->w);
ret = amlbt_intf_write_word(p_bt->fw_res._15p4_tx_w, (unsigned long)p_fifo.w & 0x7ff);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
*p_bt->usb_res._15p4_tx_fifo = p_fifo;
BTP("15p4 len %#x:w %#lx, r %#lx\n", len, (unsigned long)p_bt->usb_res._15p4_tx_fifo->w, (unsigned long)p_bt->usb_res._15p4_tx_fifo->r);
return ret;
err_exit:
return ret;
}
void amlbt_intf_usb_write_work(amlbt_t *p_bt)
{
struct sk_buff *skb;
unsigned char *p;
int ret = 0;
unsigned int len = 0;
restart:
//BTI("amlbt_w2ls_uart_write_work \n");
while ((skb = skb_dequeue(&p_bt->common_res.tx_queue)))
{
p = skb->data;
skb_pull(skb, 1);
if (*p == HCI_COMMAND_PKT)
{
p = skb->data;
len = 3 + p[2];
//BTI("hci cmd %d, %d:[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]", skb->len, len,
// p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
ret = amlbt_intf_usb_send_hci_cmd(p_bt, skb->data, len);
}
else if (*p == HCI_ACLDATA_PKT || *p == HCI_ISO_PKT)
{
p = skb->data;
//BTI("hci data %d, %d:[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n", skb->len, len,
// p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
ret = amlbt_intf_usb_send_hci_data(p_bt, skb->data, skb->len);
}
else if (*p == HCI_15P4_PKT)
{
p = skb->data;
//BTI("hci iot %d, %d:[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n", skb->len, len,
// p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
ret = amlbt_intf_usb_send_15p4_data(p_bt, skb->data, skb->len);
}
else
{
BTE("type error!\n");
BTE("raw %d, %d:[%#x,%#x,%#x,%#x,%#x,%#x,%#x,%#x]\n", skb->len, len,
p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
}
//BTI("amlbt_w2ls_uart_dequeue skb->len %d \n", skb->len);
if (skb)
{
kfree_skb(skb);
skb = NULL;
}
//BTI("amlbt_w2ls_uart_write_work complete! \n");
if (ret != 0)
{
BTF("%s:%d Failed : %d\n", __func__, __LINE__, ret);
}
#if defined(CONFIG_AML_BT_CHIP_W1D)
//w1d/usb interrupt set bt fw get data
amlbt_intf_write_word(REG_INTERRUPT_BT_SET, 0x1);
#endif
}
if (!skb_queue_empty(&p_bt->common_res.tx_queue))
{
goto restart;
}
}
#if defined(CONFIG_AML_BT_CHIP_W1D)
int amlbt_intf_usb_shutdown(amlbt_t *p_bt)
{
int ret = 0;
unsigned int cmd_r = 0;
unsigned int cmd_w = 0;
unsigned int len = 0;
uint8_t cmd[] = {SW_SHUTDOWN_CMD & 0xff, (SW_SHUTDOWN_CMD >> 8) & 0xff, 0x00, 0x00};
amlbt_intf_flush_workqueue(p_bt->common_res.write_work_wq, &p_bt->common_res.write_work);
len = ((sizeof(cmd) + 3) & 0xFFFFFFFC);//Keep 4 bytes aligned
//step 1: Update the command FIFO read/write pointer
ret = amlbt_intf_read_word(W1D_USB_CMD_Q_R_POINT, &cmd_r);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
ret = amlbt_intf_read_word(W1D_USB_CMD_Q_R_POINT, &cmd_w);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
//step 2: write data to fw
ret = amlbt_intf_write_sram(cmd, W1D_USB_CMD_Q_ADDR+cmd_w, len);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
//step 3: Update the write pointer
cmd_w += len;
ret = amlbt_intf_write_word(W1D_USB_CMD_Q_W_POINT, cmd_w & 0xfff);
if (ret != 0)
{
BTE("%s:%d Failed : %d\n", __func__, __LINE__, ret);
goto err_exit;
}
//w1d/usb interrupt set bt fw get data
amlbt_intf_write_word(REG_INTERRUPT_BT_SET, 0x1);
BTI("%s write len:%d w:%#x r:%#x\n", __func__, len, cmd_w, cmd_r);
BTI("%s write cmd[%#x,%#x,%#x,%#x]", __func__, cmd[0], cmd[1], cmd[2], cmd[3]);
err_exit:
return ret;
}
#endif
//linux bt driver for bluez stack
static int amlbt_hci_report_hardware_error(amlbt_res_usb_linux_t *info)
{
struct sk_buff *skb;
unsigned char hw_error_evt[4] = {0x04, 0x10, 0x01, 0x00};
BTI("%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;
BTI("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;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
//struct amlbt_res_usb_linux_t *info = &p_bt->usb_linux_res;
amlbt_res_usb_linux_t *info = container_of(to_delayed_work(work), amlbt_res_usb_linux_t, receive_work);
bool is_empty;
unsigned int len;
if (info != &p_bt->usb_res_linux) {
BTE("info does not belong to p_bt\n");
return;
}
if (!info || !info->hdev) {
BTE("Invalid info or hdev\n");
return;
}
if (p_bt->pm_res.dr_state & BT_DRV_STATE_RECOVERY) {
amlbt_hci_report_hardware_error(info);
return;
}
if (!test_bit(HDEV_RUNNING, &info->hdev_flags)) {
BTI("hdev not running return\n");
return;
}
while ((skb = skb_dequeue(&p_bt->bt_res.bt_rx_queue)) != NULL) {
len = skb->len;
hci_recv_frame(info->hdev, skb);
info->hdev->stat.byte_rx += len;
}
//is_empty = skb_queue_empty(&p_bt->bt_res.bt_rx_queue);
//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 pkt_type = 0;
unsigned int count = 0;
//unsigned char *p;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
amlbt_res_usb_linux_t *info = &p_bt->usb_res_linux;
BTA("%s skb %p\n", __func__,skb);
//memset(info->bluez_buf, 0, HCI_MAX_FRAME_SIZE);
//remove pkt type get hci raw data
count = skb->len - 1;
memcpy(info->bluez_buf, &skb->data[1], count);
//p = skb->data;
//skb_pull(skb, 1);
pkt_type = hci_skb_pkt_type(skb);
BTA("get hci raw data:w_type:%d\n",pkt_type);
switch (pkt_type) {
case HCI_COMMAND_PKT:
actual = amlbt_intf_usb_send_hci_cmd(p_bt, info->bluez_buf, count);//keep 4 bytes aligned len = len + 3;
break;
case HCI_ACLDATA_PKT:
actual = amlbt_intf_usb_send_hci_data(p_bt, info->bluez_buf, count);
break;
case HCI_ISO_PKT:
actual = amlbt_intf_usb_send_hci_data(p_bt, info->bluez_buf, count);
break;
case HCI_SCODATA_PKT:
break;
}
actual = skb->len;
return actual;
}
static void amlbt_hci_write_wakeup(amlbt_res_usb_linux_t *info)
{
BTA("%s\n", __func__);
if (!info || !info->hdev) {
BTE("%s: invalid info or hdev", __func__);
return;
}
if (test_and_set_bit(XMIT_SENDING, &info->tx_state)) {
set_bit(XMIT_WAKEUP, &info->tx_state);
BTF("%s: already sending, set wakeup flag", __func__);
return;
}
do {
struct sk_buff *skb = NULL;
int len = 0;
clear_bit(XMIT_WAKEUP, &info->tx_state);
skb = skb_dequeue(&info->txq);
if (!skb) {
BTA("%s: no skb in txq, exit loop", __func__);
break;
}
BTA("%s: send skb %p, len %d", __func__, skb, skb->len);
len = amlbt_hci_write(skb);
if (len > 0 && len == skb->len) {
info->hdev->stat.byte_tx += len;
kfree_skb(skb);
BTA("%s: skb %p sent completely, kfree", __func__, skb);
} else if (len > 0 && len < skb->len) {
skb_pull(skb, len);
info->hdev->stat.byte_tx += len;
skb_queue_head(&info->txq, skb);
BTA("%s: skb %p partially sent (len=%d), requeue", __func__, skb, len);
set_bit(XMIT_WAKEUP, &info->tx_state);
} else {
BTE("%s: skb %p send failed (len=%d)", __func__, skb, len);
kfree_skb(skb);
}
} while (test_bit(XMIT_WAKEUP, &info->tx_state));
clear_bit(XMIT_SENDING, &info->tx_state);
BTA("%s: exit, clear XMIT_SENDING", __func__);
}
static int amlbt_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
amlbt_res_usb_linux_t *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);
amlbt_hci_write_wakeup(info);
return 0;
}
static int amlbt_hci_flush(struct hci_dev *hdev)
{
amlbt_t *p_bt = amlbt_intf_get_p_bt();
amlbt_res_usb_linux_t *info = hci_get_drvdata(hdev);
BTI("%s\n", __func__);
if (!info) {
BTE("info is NULL, skip flush\n");
return 0;
}
//clean tx queue
skb_queue_purge(&(info->txq));
cancel_delayed_work_sync(&info->receive_work);
return 0;
}
static int amlbt_hci_open(struct hci_dev *hdev)
{
amlbt_t *p_bt = amlbt_intf_get_p_bt();
amlbt_res_usb_linux_t *info = hci_get_drvdata(hdev);
BTI("%s\n", __func__);
//clean tx queue
skb_queue_purge(&(info->txq));
skb_queue_purge(&p_bt->bt_res.bt_rx_queue);
set_bit(HDEV_RUNNING, &info->hdev_flags);
//queue_work(info->workqueue, &info->receive_work);
queue_delayed_work(info->workqueue, &info->receive_work, 0);
BTI("%s: start schedule bluez receive work\n", __func__);
return 0;
}
static int amlbt_hci_close(struct hci_dev *hdev)
{
amlbt_t *p_bt = amlbt_intf_get_p_bt();
amlbt_res_usb_linux_t *info = hci_get_drvdata(hdev);
BTI("%s\n", __func__);
clear_bit(HDEV_RUNNING, &info->hdev_flags);
amlbt_hci_flush(hdev);
return 0;
}
int amlbt_hci_register_dev(amlbt_t *p_bt)
{
//amlbt_t *p_bt = amlbt_intf_get_p_bt();
amlbt_res_usb_linux_t *info = &p_bt->usb_res_linux;
BTI("%s\n", __func__);
if (info->reg_flag) {
BTE("%s:hci_dev already registered, skip\n", __func__);
return 0;
}
if (!info->hdev) {
BTE("%s:hci_dev is NULL, can't register\n", __func__);
return -EINVAL;
}
if (hci_register_dev(info->hdev) < 0) {
BTE("%s:Failed to register hci_dev\n", __func__);
hci_free_dev(info->hdev);
info->hdev = NULL;
return -1;
}
info->reg_flag = 1;
BTI("success register hdev reg_flag=%d\n", info->reg_flag);
return 0;
}
int amlbt_hci_dev_init(struct platform_device *dev)
{
int err = 0;
struct hci_dev *hdev;
amlbt_t *p_bt = amlbt_intf_get_p_bt();
amlbt_res_usb_linux_t *info = &p_bt->usb_res_linux;
BTI("%s\n", __func__);
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) {
BTE("%s:Failed to create workqueue\n", __func__);
kfree(info);
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;
}
info->bluez_buf = kzalloc(HCI_MAX_FRAME_SIZE, GFP_DMA | GFP_KERNEL);
if (!info->bluez_buf) {
BTE("%s: bluez_buf kzalloc failed!\n", __func__);
hci_free_dev(hdev);
destroy_workqueue(info->workqueue);
return -ENOMEM;
}
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
platform_set_drvdata(dev, info);//set info point to dev point
BTI("%s success\n", __func__);
return err;
}
void amlbt_hci_dev_deinit(void)
{
amlbt_t *p_bt = amlbt_intf_get_p_bt();
amlbt_res_usb_linux_t *info = &p_bt->usb_res_linux;
BTI("%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;
BTI("%s: unregistered hdev\n", __func__);
}
if (info->hdev) {
hci_free_dev(info->hdev);
info->hdev = NULL;
}
if (info->bluez_buf) {
kfree(info->bluez_buf);
info->bluez_buf = NULL;
}
platform_set_drvdata(info->pdev, NULL);
}