add rtl8733 support, add logo support

This commit is contained in:
Lu
2024-05-31 17:39:15 +08:00
parent 82a0b70d86
commit 608df819e8
22 changed files with 5054 additions and 10 deletions

View File

@@ -51,3 +51,10 @@ mcopy -i install/xxx/xxx.img@@1s wifi.sta ::/
cd mountpoint
touch xxx
```
# logo
```
./host/make_logo.sh input.jpeg logo.jpeg
mcopy -i install/xxx/xxx.img@@1s logo.jpeg ::/
```

View File

@@ -57,15 +57,26 @@
status = "okay";
};
&uart0 {
clocks = <&clk CV181X_CLK_UART0>;
/delete-property/ clock-frequency;
};
&uart1 {
clocks = <&clk CV181X_CLK_UART1>;
/delete-property/ clock-frequency;
status = "okay";
};
&uart2 {
clocks = <&clk CV181X_CLK_UART2>;
/delete-property/ clock-frequency;
status = "okay";
};
&uart3 {
clocks = <&clk CV181X_CLK_UART3>;
/delete-property/ clock-frequency;
status = "okay";
};
@@ -213,12 +224,12 @@
reg_names = "core_mem";
src-frequency = <375000000>;
min-frequency = <400000>; // 400Khz
//max-frequency = <50000000>; // 50Mhz
max-frequency = <50000000>; // 50Mhz
//max-frequency = <45000000>; // 45Mhz
//max-frequency = <40000000>; // 40Mhz
//max-frequency = <35000000>; // 35Mhz
//max-frequency = <30000000>; // 30Mhz
max-frequency = <25000000>; // 25Mhz
//max-frequency = <25000000>; // 25Mhz
//max-frequency = <20000000>; // 20Mhz
//max-frequency = <15000000>; // 15Mhz
//max-frequency = <10000000>; // 10Mhz

View File

@@ -123,7 +123,7 @@ CONFIG_I2C_DESIGNWARE_SLAVE=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MUX=y
CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_I2C_GPIO=y
CONFIG_I2C_GPIO=m
# CONFIG_PTP_1588_CLOCK is not set
CONFIG_PINCTRL=y
CONFIG_CVITEK_PINCTRL_CV1835=y
@@ -495,7 +495,7 @@ CONFIG_USB_CONFIGFS_F_UAC1=y
CONFIG_USB_CONFIGFS_F_UVC=y
CONFIG_BUG=n
CONFIG_ADVISE_SYSCALLS=n
CONFIG_TIMERFD=n
CONFIG_TIMERFD=y
# for licheervnano
CONFIG_FB_SIMPLE=y
@@ -511,6 +511,9 @@ CONFIG_BT_HS=y
CONFIG_BT_LE=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_BCSP=y
CONFIG_BT_HCIUART_H4=y
CONFIG_BT_HCIUART_3WIRE=y
CONFIG_BT_HCIUART_RTL=y
CONFIG_SERIAL_DEV_BUS=y
CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
CONFIG_SWAP=y
@@ -555,7 +558,7 @@ CONFIG_BACKLIGHT_PWM=m
CONFIG_SPI=y
CONFIG_SPI_DESIGNWARE=y
CONFIG_SPI_DW_MMIO=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_GPIO=m
CONFIG_SPI_SPIDEV=y
# these driver may broken codec driver

View File

@@ -113,8 +113,8 @@ int cvi_board_init(void)
mmio_write_32(0x030010EC, 0x3); // GPIOB 0 GPIO_MODE
// for licheervnano beta
mmio_write_32(0x030010ac, 0x4); // PWRGPIO 2 PWM 10
//mmio_write_32(0x030010ac, 0x0); // PWRGPIO 2 GPIO_MODE
//mmio_write_32(0x030010ac, 0x4); // PWRGPIO 2 PWM 10
mmio_write_32(0x030010ac, 0x0); // PWRGPIO 2 GPIO_MODE
// camera function
//mmio_write_32(0x0300116C, 0x5); // RX4N CAM_MCLK0 for alpha

View File

@@ -8,6 +8,7 @@ image boot.vfat {
"usb.rndis0",
"wifi.sta",
"gt9xx",
"logo.jpeg",
"ver",
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -33,6 +33,7 @@ touch ${output_dir}/input/gt9xx
touch ${output_dir}/input/fb
echo ${image} > ${output_dir}/input/ver
cp -fv ${THISDIR}/genimage_rootless.cfg ${output_dir}/genimage.cfg
cp -fv ${THISDIR}/logo.jpeg ${output_dir}/input/logo.jpeg
sed -i -e "s/duo.img/${image}/g" ${output_dir}/genimage.cfg
cd ${output_dir}/
${THISDIR}/genimage

View File

@@ -5,6 +5,7 @@ then
. /etc/profile
if [ ! -e /boot/alpha ]
then
devmem 0x030010AC 0x04 # PINMUX PWM10
blpwm="/sys/class/pwm/pwmchip8/pwm2/"
if [ ! -e "${blpwm}" ]
then

View File

@@ -8,6 +8,7 @@ then
insmod cfg80211.ko
insmod 3rd/aic8800_bsp.ko
insmod 3rd/aic8800_fdrv.ko
insmod 3rd/8733bs.ko
echo "OK"
exit 0
fi

48
host/make_logo.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/sh
usage() {
echo "usage: $0 input output"
echo "output image must be logo.jpeg"
exit 1
}
src=${1}
dst=${2}
rm -rf ${dst}
if [ -z ${dst} ]
then
usage
fi
if [ ! -e ${src} ]
then
usage
fi
get_picture_w() {
convert "${1}" -print "%w" /dev/null
}
get_picture_h() {
convert "${1}" -print "%h" /dev/null
}
W=$(get_picture_w ${src})
H=$(get_picture_h ${src})
echo "picture w : ${W}"
echo "picture h : ${H}"
picturetoyuv420p() {
ffmpeg -i ${1} -pix_fmt yuv420p ${2}
}
yuv420ptopicture() {
ffmpeg -pix_fmt yuv420p -s ${3}x${4} -i ${1} ${2}
}
picturetoyuv420p $src $dst
yuv420ptopicture $src $dst $W $H

View File

@@ -12,6 +12,10 @@ config WLAN_VENDOR_REALTEK
if WLAN_VENDOR_REALTEK
config RTL8733BS
tristate "rtl8733bs sdio wifi"
default y
source "drivers/net/wireless/realtek/rtl818x/Kconfig"
source "drivers/net/wireless/realtek/rtlwifi/Kconfig"
source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig"

View File

@@ -8,6 +8,7 @@ obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/
obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
obj-$(CONFIG_WLAN_VENDOR_ICOMMSEMI) += icommsemi/
obj-y += rtl8733bs/
obj-y += rtl8733bs_bt_sdio/
all:
$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(shell pwd) modules

View File

@@ -102,7 +102,7 @@ EXTRA_CFLAGS += -DCONFIG_RTW_ANDROID=$(CONFIG_RTW_ANDROID)
endif
########################## Debug ###########################
CONFIG_RTW_DEBUG = y
CONFIG_RTW_DEBUG = n
# default log level is _DRV_INFO_ = 4,
# please refer to "How_to_set_driver_debug_log_level.doc" to set the available level.
CONFIG_RTW_LOG_LEVEL = 4

View File

@@ -1012,6 +1012,7 @@ static int rtw_drv_init(
struct acpi_device *adev;
#endif
#if defined(CONFIG_ACPI) && defined(CONFIG_GPIO_WAKEUP)
handle = ACPI_HANDLE(&func->dev);
@@ -1123,7 +1124,10 @@ free_if_vir:
free_dvobj:
if (status != _SUCCESS)
sdio_dvobj_deinit(func);
exit:
printk("%s: sdio device id , class = 0x%02X, vendor = 0x%04X, device = 0x%04X\n", __func__, id->class, id->vendor, id->device);
return status == _SUCCESS ? 0 : -ENODEV;
}

View File

@@ -0,0 +1,20 @@
# the share variable is exported from wifi, the following is required
ccflags-y += -DCONFIG_COMBO_MULTISDIO_EXPORT_FROM_RTW
KBUILD_EXTRA_SYMBOLS=/lib/firmware/Module.symvers
# please mark the above two lines when testing btrtksdio driver without wifi driver
ifneq ($(KERNELRELEASE),)
obj-m += btrtksdio.o
btrtksdio-y := btrtl.o btrtk_sdio.o rtk_coex.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
make ${KBUILD_EXTRA_SYMBOLS} -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
endif

View File

@@ -0,0 +1,3 @@
only working on:
{SDIO_DEVICE(0x024c, 0xB73A), .class = SDIO_CLASS_WLAN, .driver_data = RTL8733B}, /* SDIO multi */

View File

@@ -0,0 +1,920 @@
/*
*
* Generic Bluetooth SDIO driver
*
* Copyright (C) 2007 Cambridge Silicon Radio Ltd.
* Copyright (C) 2007 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2018 Realtek Semiconductor Corp
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/version.h>
#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/kthread.h>
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btrtl.h"
#define VERSION "0.12.4489ddc.20210423-153940"
#define BTSDIO_DMA_ALIGN 8
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)
#define hci_skb_pkt_type(skb) bt_cb((skb))->pkt_type
#endif
#define BTCOEX
#define CONFIG_COEX
/* #define CONFIG_TEST */
#define RTL8821DS_LPS
#ifdef BTCOEX
#include "rtk_coex.h"
#endif
#ifdef CONFIG_COMBO_MULTISDIO_EXPORT_FROM_RTW
extern int rtw_sdio_multi_state;
#else
int rtw_sdio_multi_state;
EXPORT_SYMBOL(rtw_sdio_multi_state);
#endif
#ifdef CONFIG_TEST
static int delay_time = 10;
static int loop_time = 100; /* default per 100ms */
#endif /* CONFIG_TEST */
static const struct sdio_device_id btsdio_table[] = {
/* Generic Bluetooth Type-A SDIO device */
{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) },
/* Generic Bluetooth Type-B SDIO device */
{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) },
/* Generic Bluetooth AMP controller */
{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_AMP) },
{ } /* Terminating entry */
};
char *pkt_type_str[] = {"command", "acl", "sco", "event"};
MODULE_DEVICE_TABLE(sdio, btsdio_table);
struct btsdio_data {
struct hci_dev *hdev;
struct sdio_func *func;
struct work_struct work;
struct sk_buff_head txq;
int int_count;
struct task_struct *thread;
wait_queue_head_t wq;
spinlock_t lock;
#ifdef CONFIG_TEST
struct delayed_work timer_loop;
struct delayed_work timer_delay;
struct workqueue_struct *test_workqueue;
#endif
#ifdef RTL8821DS_LPS
unsigned long last_busy;
#endif
};
#define REG_RDAT 0x00 /* Receiver Data */
#define REG_TDAT 0x00 /* Transmitter Data */
#define REG_PC_RRT 0x10 /* Read Packet Control */
#define REG_PC_WRT 0x11 /* Write Packet Control */
#define REG_RTC_STAT 0x12 /* Retry Control Status */
#define REG_RTC_SET 0x12 /* Retry Control Set */
#define REG_INTRD 0x13 /* Interrupt Indication */
#define REG_CL_INTRD 0x13 /* Interrupt Clear */
#define REG_EN_INTRD 0x14 /* Interrupt Enable */
#define REG_MD_STAT 0x20 /* Bluetooth Mode Status */
#define REG_MD_SET 0x20 /* Bluetooth Mode Set */
#define REG_FIFO_STATUS 0x41
#define REG_WAKEUP 0x40 /* Wake-up register */
#ifdef CONFIG_TEST
static void loop_work_func(struct work_struct *work)
{
struct btsdio_data *data;
data = container_of(work, struct btsdio_data, timer_delay.work);
queue_delayed_work(data->test_workqueue, &data->timer_loop,
msecs_to_jiffies(loop_time));
queue_delayed_work(data->test_workqueue, &data->timer_delay,
msecs_to_jiffies(delay_time));
rtw_sdio_multi_state = 1;
/* BT_INFO("multi_state = %d: BT claiming bus.", rtw_sdio_multi_state);
*/
}
static void delay_work_func(struct work_struct *work)
{
struct btsdio_data *data;
struct sdio_func *func;
int err;
data = container_of(work, struct btsdio_data, timer_delay.work);
func = data->func;
rtw_sdio_multi_state = 0;
sdio_claim_host(func);
sdio_writeb(func, 0x02, 0x40, &err);
sdio_release_host(func);
if (err)
BT_ERR("Write sdio reg in delay timer, error %d", err);
/* BT_INFO("multi_state = %d: BT releasing bus.", rtw_sdio_multi_state);
*/
}
#endif /* CONFIG_TEST */
static int wait_for_txfifo_ready(struct sdio_func *func)
{
u8 result;
int err;
for (;;) {
result = sdio_readb(func, REG_FIFO_STATUS, &err);
if (err) {
BT_ERR("Read error %d while waiting for tx fifo ready",
err);
return err;
}
if ((result & 0x01) || (result & 0x02))
break;
}
return 0;
}
static int wait_for_rxfifo_ready(struct sdio_func *func)
{
u8 result;
int err;
for (;;) {
result = sdio_readb(func, REG_FIFO_STATUS, &err);
if (err) {
BT_ERR("Read error %d while waiting for rx fifo ready",
err);
return err;
}
if ((result & 0x04) || (result & 0x08))
break;
}
return 0;
}
#ifdef RTL8821DS_LPS
static int wait_for_io_ready(struct sdio_func *func)
{
u8 result;
int err = 0;
for (;;) {
/* read function 0 cccr 0x03 reg that is io ready register */
result = sdio_f0_readb(func, 0x03, &err);
if (err) {
BT_ERR("wait for io ready error %d", err);
return err;
}
if (result & (1 << 2))
break;
}
return 0;
}
#endif
static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
{
int err;
int pkt_type;
#ifdef CONFIG_COEX
struct sdio_func *func = data->func;
#endif
unsigned int len;
struct sk_buff *tmpskb;
#ifdef RTL8821DS_LPS
long elapsed;
u8 reg_val;
#endif
pkt_type = hci_skb_pkt_type(skb);
BT_DBG("%s", data->hdev->name);
/* BT_INFO("tx packet: pkt_type %s, pkt_len: %d",
* pkt_type_str[pkt_type - 1], skb->len);
*/
#ifdef BTCOEX
switch (pkt_type) {
case HCI_COMMAND_PKT:
rtk_btcoex_parse_cmd(skb->data, skb->len);
break;
case HCI_ACLDATA_PKT:
rtk_btcoex_parse_l2cap_data_tx(skb->data, skb->len);
break;
}
#endif
/* Prepend Type-A header */
skb_push(skb, 4);
if (1) {
skb->data[0] = skb->len & 0xff;
skb->data[1] = (skb->len >> 8) & 0xff;
skb->data[2] = (skb->len >> 16) & 0xff;
} else {
skb->data[2] = skb->len & 0xff;
skb->data[1] = (skb->len >> 8) & 0xff;
skb->data[0] = (skb->len >> 16) & 0xff;
}
skb->data[3] = hci_skb_pkt_type(skb);
if ((unsigned long)skb->data & (BTSDIO_DMA_ALIGN - 1)) {
tmpskb = bt_skb_alloc(skb->len, GFP_ATOMIC);
if (!tmpskb) {
BT_ERR("Could not alloc tmp skb for btrtksdio tx");
return -ENOMEM;
}
memcpy(skb_put(tmpskb, skb->len), skb->data, skb->len);
if ((unsigned long)tmpskb->data & (BTSDIO_DMA_ALIGN - 1))
BT_ERR("Packet address not aligned, %p, %p",
skb->data, tmpskb->data);
kfree_skb(skb);
skb = tmpskb;
}
/* Realtek btsdio tx timing
* 1) repeatedly check fifo status until at least one tx fifo ready
* 2) write sdio header(4 bytes) and write len <= 128 ? len : 128
* bytes data
* 3) if not done, repeat the following until done:
* 3.1) check fifo status until at least one tx fifo ready
* 3.2) write at most 128 bytes data;
*/
sdio_claim_host(data->func);
#ifdef RTL8821DS_LPS
/* Check if the controller is in LPS */
elapsed = jiffies - data->last_busy;
if (elapsed >= 0 && elapsed < msecs_to_jiffies(5000))
goto exit_lps;
/* if jiffies has wrapped around (elapsed < 0) or elapsed is equal to
* or bigger than 5s, assume 8821DS has been in LPS and wake it up.
*/
BT_INFO("Tx: wake up controller that may be in LPS");
reg_val = 0x01;
sdio_writeb(func, reg_val, REG_WAKEUP, &err);
if (err) {
BT_ERR("Write REG_WAKEUP error %d", err);
goto exit;
}
/* Wait for the Bluetooth IO Ready */
err = wait_for_io_ready(data->func);
if (err)
goto exit;
exit_lps:
data->last_busy = jiffies;
#endif
#ifdef CONFIG_COEX
rtw_sdio_multi_state = 1;
/* BT_INFO("multi_state = %d: BT claiming bus.", rtw_sdio_multi_state);
*/
#endif
err = wait_for_txfifo_ready(data->func);
if (err)
goto exit;
err = sdio_writesb(data->func, REG_TDAT, skb->data, 4);
if (err) {
/* TODO: retry for header */
BT_ERR("Write BTSDIO packet header error %d", err);
goto exit;
}
/* get rid of header on success */
skb_pull(skb, 4);
data->hdev->stat.byte_tx += 4;
/* NOTE: FIFO is already ready to read the first part of payload */
do {
if (skb->len <= 128)
len = skb->len;
else
len = 128;
err = sdio_writesb(data->func, REG_TDAT, skb->data, len);
if (err) {
BT_ERR("Couldn't write %u byptes to card error %d",
len, err);
goto exit;
}
data->hdev->stat.byte_tx += len;
skb_pull(skb, len);
err = wait_for_txfifo_ready(data->func);
if (err)
break;
} while (skb->len);
exit:
#ifdef CONFIG_COEX
rtw_sdio_multi_state = 0;
sdio_writeb(func, 0x02, 0x40, &err);
if (err)
BT_ERR("rtlsdio coex tx error %d", err);
/* BT_INFO("multi_state = %d: BT releasing bus.", rtw_sdio_multi_state);
*/
#endif
sdio_release_host(data->func);
kfree_skb(skb);
return 0;
}
static int btsdio_rx_packet(struct btsdio_data *data)
{
u8 hdr[4] __aligned(4);
struct sk_buff *skb;
int err;
unsigned int len;
unsigned int hci_len;
#ifdef CONFIG_COEX
struct sdio_func *func = data->func;
#endif
BT_DBG("%s", data->hdev->name);
sdio_claim_host(data->func);
err = sdio_readsb(data->func, hdr, REG_RDAT, 4);
if (err < 0) {
BT_ERR("Read BTSDIO packet header error %d", err);
sdio_release_host(data->func);
return err;
}
#ifdef RTL8821DS_LPS
/* If we can read packet from controller, the controller is awake */
data->last_busy = jiffies;
#endif
#ifdef CONFIG_COEX
rtw_sdio_multi_state = 1;
/* BT_INFO("multi_state = %d: BT claiming bus.", rtw_sdio_multi_state);
*/
#endif
if (1)
len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16);
else
len = hdr[2] | (hdr[1] << 8) | (hdr[0] << 16);
if (len < 4 || len > 65543)
return -EILSEQ;
hci_len = len - 4;
skb = bt_skb_alloc(hci_len, GFP_KERNEL);
if (!skb) {
BT_ERR("Couldn't alloc skb for rx");
/* Out of memory. Prepare a read retry and just
* return with the expectation that the next time
* we're called we'll have more memory.
*/
return -ENOMEM;
}
/* Realtek btsdio rx timing:
* if hci_len <= 256, read it. Otherwise read 256, then repeat following
* repeatedly check for rx fifo confition until at least one is empty,
* then read 128.
*/
/* NOTE: FIFO is already ready to write the first part of payload */
len = 256;
do {
if (hci_len < len)
len = hci_len;
/* For the first time, read 256-byte data */
err = sdio_readsb(data->func, skb_put(skb, len), REG_RDAT, len);
if (err) {
BT_ERR("Read payload error %d", err);
goto exit;
}
hci_len -= len;
data->hdev->stat.byte_rx += len;
if (!hci_len)
break;
len = 128;
err = wait_for_rxfifo_ready(data->func);
if (err)
break;
} while (hci_len);
exit:
#ifdef CONFIG_COEX
rtw_sdio_multi_state = 0;
sdio_writeb(func, 0x02, 0x40, &err);
if (err)
BT_ERR("rtlsdio coex rx error %d", err);
/* BT_INFO("multi_state = %d: BT releasing bus.", rtw_sdio_multi_state);
*/
#endif
sdio_release_host(data->func);
/* BT_INFO("rx packet: pkt_type %s, pkt_len: %d",
* pkt_type_str[hdr[3] - 1], len - 4);
*/
hci_skb_pkt_type(skb) = hdr[3];
#ifdef BTCOEX
switch (hci_skb_pkt_type(skb)) {
case HCI_EVENT_PKT:
rtk_btcoex_parse_event(skb->data, skb->len);
break;
case HCI_ACLDATA_PKT:
rtk_btcoex_parse_l2cap_data_rx(skb->data, skb->len);
break;
}
#endif
err = hci_recv_frame(data->hdev, skb);
if (err < 0)
return err;
return 0;
}
static void btsdio_interrupt(struct sdio_func *func)
{
struct btsdio_data *data = sdio_get_drvdata(func);
u8 intrd;
ulong flags;
int err;
BT_DBG("%s", data->hdev->name);
/* BT_INFO("Got Data Interrupt"); */
intrd = sdio_readb(func, REG_INTRD, &err);
if (!err && (intrd & 0x01)) {
sdio_writeb(func, 0x01, REG_CL_INTRD, &err);
if (err) {
BT_ERR("Clear REG_CL_INTRD error %d", err);
return;
}
spin_lock_irqsave(&data->lock, flags);
data->int_count++;
spin_unlock_irqrestore(&data->lock, flags);
wake_up_interruptible(&data->wq);
} else {
BT_ERR("Unknown interrupt, err %d, intrd 0x%02x", err, intrd);
}
}
static int btsdio_open(struct hci_dev *hdev)
{
struct btsdio_data *data = hci_get_drvdata(hdev);
int err;
#ifdef RTL8821DS_LPS
u8 reg_val;
#endif
BT_DBG("%s", hdev->name);
sdio_claim_host(data->func);
#ifdef RTL8821DS_LPS
reg_val = 0x01;
sdio_writeb(data->func, reg_val, REG_WAKEUP, &err);
if (err) {
BT_ERR("Write REG_WAKEUP error while open, %d", err);
goto release;
}
data->last_busy = jiffies;
#endif
err = sdio_enable_func(data->func);
if (err < 0)
goto release;
err = sdio_claim_irq(data->func, btsdio_interrupt);
if (err < 0) {
sdio_disable_func(data->func);
goto release;
}
if (data->func->class == SDIO_CLASS_BT_B) {
sdio_writeb(data->func, 0x00, REG_MD_SET, &err);
if (err) {
BT_ERR("Clear REG_MD_SET error %d", err);
goto release;
}
}
sdio_writeb(data->func, 0x01, REG_EN_INTRD, &err);
if (err) {
BT_ERR("Set REG_EN_INTRD error %d", err);
goto release;
}
#ifdef BTCOEX
rtk_btcoex_open(hdev);
#endif
release:
sdio_release_host(data->func);
return err;
}
static int btsdio_close(struct hci_dev *hdev)
{
int err;
struct btsdio_data *data = hci_get_drvdata(hdev);
BT_DBG("%s", hdev->name);
sdio_claim_host(data->func);
sdio_writeb(data->func, 0x00, REG_EN_INTRD, &err);
if (err)
BT_ERR("Clear REG_EN_INTRD error %d", err);
sdio_release_irq(data->func);
sdio_disable_func(data->func);
sdio_release_host(data->func);
#ifdef BTCOEX
rtk_btcoex_close();
#endif
return 0;
}
static int btsdio_flush(struct hci_dev *hdev)
{
struct btsdio_data *data = hci_get_drvdata(hdev);
BT_DBG("%s", hdev->name);
skb_queue_purge(&data->txq);
return 0;
}
static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btsdio_data *data = hci_get_drvdata(hdev);
/* BT_INFO("btsdio_send_frame"); */
BT_DBG("%s", hdev->name);
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;
default:
return -EILSEQ;
}
skb_queue_tail(&data->txq, skb);
/* schedule_work(&data->work); */
wake_up_interruptible(&data->wq);
return 0;
}
static int main_thread(void *dat)
{
struct btsdio_data *data;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
wait_queue_entry_t wait;
#else
wait_queue_t wait;
#endif
ulong flags;
int err;
struct sk_buff *skb;
data = dat;
init_waitqueue_entry(&wait, current);
for (;;) {
add_wait_queue(&data->wq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop()) {
BT_INFO("break from main thread");
break;
}
if (data->int_count == 0 && skb_queue_empty(&data->txq)) {
/* BT_INFO("main thread sleeping"); */
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&data->wq, &wait);
/* BT_INFO("main thread woke up"); */
spin_lock_irqsave(&data->lock, flags);
if (data->int_count != 0) {
data->int_count = 0;
spin_unlock_irqrestore(&data->lock, flags);
btsdio_rx_packet(data);
} else
spin_unlock_irqrestore(&data->lock, flags);
skb = skb_dequeue(&data->txq);
if (skb) {
err = btsdio_tx_packet(data, skb);
if (err < 0) {
BT_ERR("BTSDIO Tx packet error %d", err);
data->hdev->stat.err_tx++;
skb_queue_head(&data->txq, skb);
break;
}
}
}
return 0;
}
static int btsdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
printk("rtk bt sdio driver probe\n\r");
struct btsdio_data *data;
struct hci_dev *hdev;
struct sdio_func_tuple *tuple = func->tuples;
int err;
u8 reg_val;
BT_INFO("func %p id %p class 0x%04x", func, id, func->class);
while (tuple) {
BT_DBG("code 0x%x size %d", tuple->code, tuple->size);
tuple = tuple->next;
}
data = devm_kzalloc(&func->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->func = func;
data->int_count = 0;
/* INIT_WORK(&data->work, btsdio_work); */
skb_queue_head_init(&data->txq);
init_waitqueue_head(&data->wq);
spin_lock_init(&data->lock);
if (func->vendor == 0x024c &&
(func->device == 0xb73a || func->device == 0x885a)) {
sdio_claim_host(func);
/* read and update */
reg_val = sdio_readb(func, 0x71, &err);
if (err) {
BT_ERR("Read 0x71 register failure, %d", err);
} else {
reg_val |= (1 << 2);
sdio_writeb(func, reg_val, 0x71, &err);
if (err)
BT_ERR("Enable ECO function error, %d", err);
}
sdio_release_host(func);
}
#ifdef RTL8821DS_LPS
reg_val = 0x01;
sdio_claim_host(func);
sdio_writeb(func, reg_val, REG_WAKEUP, &err);
sdio_release_host(func);
if (err) {
BT_ERR("Write REG_WAKEUP error while probe, %d", err);
return err;
}
data->last_busy = jiffies;
#endif
data->thread = kthread_run(main_thread, data, "btrtk");
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Couldn't alloc hdev");
err = -ENOMEM;
goto err_alloc_dev;
}
hdev->bus = HCI_SDIO;
hci_set_drvdata(hdev, data);
if (id->class == SDIO_CLASS_BT_AMP)
hdev->dev_type = HCI_AMP;
else
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
hdev->dev_type = HCI_BREDR;
#else
hdev->dev_type = HCI_PRIMARY;
#endif
data->hdev = hdev;
SET_HCIDEV_DEV(hdev, &func->dev);
hdev->open = btsdio_open;
hdev->setup = btrtl_setup_8821ds;
hdev->close = btsdio_close;
hdev->flush = btsdio_flush;
hdev->send = btsdio_send_frame;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
BT_DBG("set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);");
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
#endif
err = hci_register_dev(hdev);
if (err < 0) {
BT_ERR("Couldn't register hdev");
goto err_hci_reg;
}
#ifdef BTCOEX
rtk_btcoex_probe(hdev);
#endif
sdio_set_drvdata(func, data);
#ifdef CONFIG_TEST
BT_INFO("BT interrupt every %d mills", loop_time);
data->test_workqueue = create_workqueue("rtlsdiotest");
if (!data->test_workqueue) {
BT_ERR("Couldn't test create workqueue");
err = -ENOMEM;
goto err_create_wq;
}
INIT_DELAYED_WORK(&data->timer_loop, loop_work_func);
INIT_DELAYED_WORK(&data->timer_delay, delay_work_func);
queue_delayed_work(data->test_workqueue, &data->timer_loop,
msecs_to_jiffies(loop_time));
#endif
return 0;
#ifdef CONFIG_TEST
err_create_wq:
sdio_set_drvdata(func, NULL);
hci_unregister_dev(hdev);
#endif
err_hci_reg:
hci_free_dev(hdev);
err_alloc_dev:
kthread_stop(data->thread);
return err;
}
static void btsdio_remove(struct sdio_func *func)
{
struct btsdio_data *data = sdio_get_drvdata(func);
struct hci_dev *hdev;
BT_DBG("func %p", func);
if (!data)
return;
hdev = data->hdev;
#ifdef CONFIG_TEST
cancel_delayed_work_sync(&data->timer_loop);
cancel_delayed_work_sync(&data->timer_delay);
flush_workqueue(data->test_workqueue);
destroy_workqueue(data->test_workqueue);
#endif
sdio_claim_host(func);
sdio_release_irq(func);
sdio_disable_func(func);
sdio_release_host(func);
kthread_stop(data->thread);
sdio_set_drvdata(func, NULL);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
static struct sdio_driver btsdio_driver = {
.name = "btstdsdio",
.probe = btsdio_probe,
.remove = btsdio_remove,
.id_table = btsdio_table,
};
static int __init btsdio_init(void)
{
BT_INFO("Realtek Bluetooth SDIO driver ver %s", VERSION);
#ifdef BTCOEX
rtk_btcoex_init();
#endif
return sdio_register_driver(&btsdio_driver);
}
static void __exit btsdio_exit(void)
{
sdio_unregister_driver(&btsdio_driver);
#ifdef BTCOEX
rtk_btcoex_exit();
#endif
}
module_init(btsdio_init);
module_exit(btsdio_exit);
#ifdef CONFIG_TEST
module_param(delay_time, int, 0644);
module_param(loop_time, int, 0644);
#endif
MODULE_DESCRIPTION("Realtek Bluetooth SDIO driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,638 @@
/*
* Bluetooth support for Realtek devices
*
* Copyright (C) 2015 Endless Mobile, Inc.
* Copyright (C) 2018 Realtek Semiconductor Corp
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/firmware.h>
#include <asm/unaligned.h>
#include <linux/usb.h>
#include <linux/version.h>
#include <linux/file.h>
#include <linux/ctype.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btrtl.h"
#define VERSION "0.1"
#define BDADDR_STRING_LEN 17
#define BDADDR_FILE "/opt/bdaddr"
#define RTL_EPATCH_SIGNATURE "Realtech"
#define RTL_ROM_LMP_8821DS 0x8822
#define RTL_ROM_LMP_8723FS 0x8723
#define IC_MATCH_FL_LMPSUBV (1 << 0)
#define IC_MATCH_FL_HCIREV (1 << 1)
#define IC_INFO(lmps, hcir) \
.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV, \
.lmp_subver = (lmps), \
.hci_rev = (hcir)
struct id_table {
__u16 match_flags;
__u16 lmp_subver;
__u16 hci_rev;
bool config_needed;
char *fw_name;
char *cfg_name;
};
static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8821DS, 0xc),
.config_needed = false,
.fw_name = "rtl_bt/rtl8821ds_fw",
.cfg_name = "rtl_bt/rtl8821ds_config" },
{ IC_INFO(RTL_ROM_LMP_8723FS, 0xf),
.config_needed = false,
.fw_name = "rtl_bt/rtl8723fs_fw",
.cfg_name = "rtl_bt/rtl8723fs_config" },
};
struct rtl_config_item {
u16 offset;
u8 len;
u8 data[0];
} __attribute__((packed));
struct rtl_config_hdr {
u8 sign[4];
u16 len;
struct rtl_config_item item[0];
} __attribute__((packed));
struct cfg_list_item {
struct list_head list;
struct rtl_config_item *item;
} __attribute__((packed));
static char *bdaddr = "ff:ff:ff:ff:ff:ff";
module_param(bdaddr, charp, 0644);
static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
{
struct rtl_rom_version_evt *rom_version;
struct sk_buff *skb;
/* Read RTL ROM version command */
skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: Read ROM version failed (%ld)",
hdev->name, PTR_ERR(skb));
return PTR_ERR(skb);
}
if (skb->len != sizeof(*rom_version)) {
BT_ERR("%s: RTL version event length mismatch", hdev->name);
kfree_skb(skb);
return -EIO;
}
rom_version = (struct rtl_rom_version_evt *)skb->data;
bt_dev_info(hdev, "rom_version status=%x version=%x",
rom_version->status, rom_version->version);
*version = rom_version->version;
kfree_skb(skb);
return 0;
}
static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
const struct firmware *fw,
unsigned char **_buf)
{
const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
struct rtl_epatch_header *epatch_info;
unsigned char *buf;
int i, ret, len;
size_t min_size;
u8 opcode, length, data, rom_version = 0;
int project_id = -1;
const unsigned char *fwptr, *chip_id_base;
const unsigned char *patch_length_base, *patch_offset_base;
u32 patch_offset = 0;
u16 patch_length, num_patches;
static const struct {
__u16 lmp_subver;
__u8 id;
} project_id_to_lmp_subver[] = {
{ RTL_ROM_LMP_8821DS, 11 }, /* 8821DS */
{ RTL_ROM_LMP_8821DS, 13 }, /* 8821DS */
{ RTL_ROM_LMP_8723FS, 19 }, /* 8723FS */
};
ret = rtl_read_rom_version(hdev, &rom_version);
if (ret)
return ret;
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
if (fw->size < min_size)
return -EINVAL;
fwptr = fw->data + fw->size - sizeof(extension_sig);
if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
BT_ERR("%s: extension section signature mismatch", hdev->name);
return -EINVAL;
}
/* Loop from the end of the firmware parsing instructions, until
* we find an instruction that identifies the "project ID" for the
* hardware supported by this firwmare file.
* Once we have that, we double-check that that project_id is suitable
* for the hardware we are working with.
*/
while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) {
opcode = *--fwptr;
length = *--fwptr;
data = *--fwptr;
BT_DBG("check op=%x len=%x data=%x", opcode, length, data);
if (opcode == 0xff) /* EOF */
break;
if (length == 0) {
BT_ERR("%s: found instruction with length 0",
hdev->name);
return -EINVAL;
}
if (opcode == 0 && length == 1) {
project_id = data;
break;
}
fwptr -= length;
}
if (project_id < 0) {
BT_ERR("%s: failed to find version instruction", hdev->name);
return -EINVAL;
}
/* Find project_id in table */
for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
if (project_id == project_id_to_lmp_subver[i].id)
break;
}
if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
BT_ERR("%s: unknown project id %d", hdev->name, project_id);
return -EINVAL;
}
if (lmp_subver != project_id_to_lmp_subver[i].lmp_subver) {
BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
project_id_to_lmp_subver[i].lmp_subver, lmp_subver);
return -EINVAL;
}
epatch_info = (struct rtl_epatch_header *)fw->data;
if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
BT_ERR("%s: bad EPATCH signature", hdev->name);
return -EINVAL;
}
num_patches = le16_to_cpu(epatch_info->num_patches);
BT_DBG("fw_version=%x, num_patches=%d",
le32_to_cpu(epatch_info->fw_version), num_patches);
/* After the rtl_epatch_header there is a funky patch metadata section.
* Assuming 2 patches, the layout is:
* ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2
*
* Find the right patch for this chip.
*/
min_size += 8 * num_patches;
if (fw->size < min_size)
return -EINVAL;
chip_id_base = fw->data + sizeof(struct rtl_epatch_header);
patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
for (i = 0; i < num_patches; i++) {
u16 chip_id = get_unaligned_le16(chip_id_base +
(i * sizeof(u16)));
if (chip_id == rom_version + 1) {
patch_length = get_unaligned_le16(patch_length_base +
(i * sizeof(u16)));
patch_offset = get_unaligned_le32(patch_offset_base +
(i * sizeof(u32)));
break;
}
}
if (!patch_offset) {
BT_ERR("%s: didn't find patch for chip id %d",
hdev->name, rom_version);
return -EINVAL;
}
BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
min_size = patch_offset + patch_length;
if (fw->size < min_size)
return -EINVAL;
/* Copy the firmware into a new buffer and write the version at
* the end.
*/
len = patch_length;
buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
*_buf = buf;
return len;
}
static int rtl_download_firmware(struct hci_dev *hdev,
const unsigned char *data, int fw_len)
{
struct rtl_download_cmd *dl_cmd;
int frag_num = fw_len / RTL_FRAG_LEN + 1;
int frag_len = RTL_FRAG_LEN;
int ret = 0;
int i, j;
dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
if (!dl_cmd)
return -ENOMEM;
for (i = 0; i < frag_num; i++) {
struct sk_buff *skb;
BT_DBG("download fw (%d/%d)", i, frag_num);
if (i > 0x7f)
j = (i & 0x7f) + 1;
else
j = i;
dl_cmd->index = j;
if (i == (frag_num - 1)) {
dl_cmd->index |= 0x80; /* data end */
frag_len = fw_len % RTL_FRAG_LEN;
}
memcpy(dl_cmd->data, data, frag_len);
/* Send download command */
skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: download fw command failed (%ld)",
hdev->name, PTR_ERR(skb));
ret = PTR_ERR(skb);
goto out;
}
if (skb->len != sizeof(struct rtl_download_response)) {
BT_ERR("%s: download fw event length mismatch",
hdev->name);
kfree_skb(skb);
ret = -EIO;
goto out;
}
kfree_skb(skb);
data += RTL_FRAG_LEN;
}
out:
kfree(dl_cmd);
return ret;
}
int bachk(const char *str)
{
if (!str)
return -1;
if (strlen(str) != 17)
return -1;
while (*str) {
if (!isxdigit(*str++))
return -1;
if (!isxdigit(*str++))
return -1;
if (*str == 0)
break;
if (*str++ != ':')
return -1;
}
return 0;
}
static int rtl_parse_config(u8 **buff, int len)
{
u8 sign[4] = { 0x55, 0xab, 0x23, 0x87 };
struct rtl_config_hdr *cfg_hdr = (void *)*buff;
u16 data_len;
struct rtl_config_item *item;
u8 *pbuf;
u16 i;
struct list_head list_configs;
struct cfg_list_item *n;
u8 bdaddr_cfg = 0;
u8 bdaddr_buf[6];
struct list_head *pos, *next;
if (!*buff)
return len;
if (strcasecmp(bdaddr, "ff:ff:ff:ff:ff:ff")) {
if (!bachk(bdaddr)) {
char *str = bdaddr;
int j;
BT_INFO("local public address %s", bdaddr);
bdaddr_cfg = 1;
for (j = 5; j >= 0; j--, str += 3)
bdaddr_buf[j] = simple_strtoul(str, NULL, 16);
} else
BT_WARN("Invalid address %s", bdaddr);
}
if (memcmp(cfg_hdr->sign, sign, 4)) {
BT_ERR("signature [%02x %02x %02x %02x] error",
cfg_hdr->sign[0], cfg_hdr->sign[1],
cfg_hdr->sign[2], cfg_hdr->sign[3]);
return 0;
}
data_len = le16_to_cpu(cfg_hdr->len);
if (data_len != len - sizeof(*cfg_hdr)) {
BT_ERR("data len %u is not matched to %u",
data_len, (u16)(len - sizeof(*cfg_hdr)));
return 0;
}
INIT_LIST_HEAD(&list_configs);
i = 0;
pbuf = *buff + sizeof(*cfg_hdr);
item = (struct rtl_config_item *)pbuf;
while (i < data_len) {
u16 ofs;
ofs = le16_to_cpu(item->offset);
n = kzalloc(sizeof(*n), GFP_KERNEL);
if (n) {
n->item = item;
list_add_tail(&n->list, &list_configs);
} else {
BT_ERR("Couldn't alloc item for %04x", ofs);
}
i += (item->len + sizeof(*item));
item = (struct rtl_config_item *)(pbuf + i);
}
list_for_each_safe(pos, next, &list_configs) {
n = list_entry(pos, struct cfg_list_item, list);
if (le16_to_cpu(n->item->offset) == 0x0030 && bdaddr_cfg) {
memcpy(n->item->data, bdaddr_buf, 6);
bdaddr_cfg = 0;
}
}
/* Add bdaddr */
if (bdaddr_cfg) {
u8 *b;
b = kzalloc(len + 9, GFP_KERNEL);
if (!b) {
BT_ERR("Couldn't alloc config buffer");
return len;
}
memcpy(b, *buff, len);
data_len += 9;
b[sizeof(cfg_hdr->sign)] = (data_len & 0xff);
b[sizeof(cfg_hdr->sign) + 1] = ((data_len >> 8) & 0xff);
b[len] = 0x30;
b[len + 1] = 0x00;
b[len + 2] = 6;
memcpy(b + len + 3, bdaddr_buf, 6);
len += 9;
kfree(*buff);
*buff = b;
}
/* free list */
list_for_each_safe(pos, next, &list_configs) {
n = list_entry(pos, struct cfg_list_item, list);
list_del(pos);
kfree(n);
}
return len;
}
static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
{
const struct firmware *fw;
int ret;
bt_dev_info(hdev, "rtl: loading %s", name);
ret = request_firmware(&fw, name, &hdev->dev);
if (ret < 0)
return ret;
ret = fw->size;
*buff = kmemdup(fw->data, ret, GFP_KERNEL);
if (!*buff)
ret = -ENOMEM;
release_firmware(fw);
/* Parse config */
ret = rtl_parse_config(buff, ret);
return ret;
}
static int send_hci_reset_command(struct hci_dev *hdev)
{
struct sk_buff *skb;
int ret = 0;
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: send HCI reset command failed (%ld)",
hdev->name, PTR_ERR(skb));
ret = PTR_ERR(skb);
}
kfree_skb(skb);
return ret;
}
static int setup_rtl8821ds(struct hci_dev *hdev, u16 hci_rev,
u16 lmp_subver)
{
unsigned char *fw_data = NULL;
const struct firmware *fw;
int ret;
int cfg_sz;
u8 *cfg_buff = NULL;
u8 *tbuff;
char *cfg_name = NULL;
char *fw_name = NULL;
int i;
for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
(ic_id_table[i].lmp_subver != lmp_subver))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
(ic_id_table[i].hci_rev != hci_rev))
continue;
break;
}
if (i >= ARRAY_SIZE(ic_id_table)) {
BT_ERR("%s: unknown IC info, lmp subver %04x, hci rev %04x",
hdev->name, lmp_subver, hci_rev);
return -EINVAL;
}
cfg_name = ic_id_table[i].cfg_name;
if (cfg_name) {
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
if (cfg_sz < 0) {
cfg_sz = 0;
if (ic_id_table[i].config_needed)
BT_ERR("Necessary config file %s not found\n",
cfg_name);
}
} else
cfg_sz = 0;
fw_name = ic_id_table[i].fw_name;
bt_dev_info(hdev, "rtl: loading %s", fw_name);
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
goto err_req_fw;
}
ret = rtlbt_parse_firmware(hdev, lmp_subver, fw, &fw_data);
if (ret < 0)
goto out;
if (cfg_sz) {
tbuff = kzalloc(ret + cfg_sz, GFP_KERNEL);
if (!tbuff) {
ret = -ENOMEM;
goto out;
}
memcpy(tbuff, fw_data, ret);
kfree(fw_data);
memcpy(tbuff + ret, cfg_buff, cfg_sz);
ret += cfg_sz;
fw_data = tbuff;
}
bt_dev_info(hdev, "cfg_sz %d, total size %d", cfg_sz, ret);
ret = rtl_download_firmware(hdev, fw_data, ret);
if (ret < 0)
goto out;
ret = send_hci_reset_command(hdev);
out:
release_firmware(fw);
kfree(fw_data);
err_req_fw:
if (cfg_sz)
kfree(cfg_buff);
return ret;
}
static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
hdev->name, PTR_ERR(skb));
return skb;
}
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
hdev->name);
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
int btrtl_setup_8821ds(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct hci_rp_read_local_version *resp;
u16 hci_rev, lmp_subver;
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
resp = (struct hci_rp_read_local_version *)skb->data;
bt_dev_info(hdev, "rtl: examining hci_ver=%02x hci_rev=%04x "
"lmp_ver=%02x lmp_subver=%04x",
resp->hci_ver, resp->hci_rev,
resp->lmp_ver, resp->lmp_subver);
hci_rev = le16_to_cpu(resp->hci_rev);
lmp_subver = le16_to_cpu(resp->lmp_subver);
kfree_skb(skb);
switch (lmp_subver) {
case RTL_ROM_LMP_8821DS:
case RTL_ROM_LMP_8723FS:
return setup_rtl8821ds(hdev, hci_rev, lmp_subver);
default:
bt_dev_info(hdev, "rtl: assuming no firmware upload needed");
return 0;
}
}
MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,42 @@
/*
* Bluetooth support for Realtek devices
*
* Copyright (C) 2015 Endless Mobile, Inc.
* Copyright (C) 2018 Realtek Semiconductor Corp
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define RTL_FRAG_LEN 252
struct rtl_download_cmd {
__u8 index;
__u8 data[RTL_FRAG_LEN];
} __packed;
struct rtl_download_response {
__u8 status;
__u8 index;
} __packed;
struct rtl_rom_version_evt {
__u8 status;
__u8 version;
} __packed;
struct rtl_epatch_header {
__u8 signature[8];
__le32 fw_version;
__le16 num_patches;
} __packed;
int btrtl_setup_8821ds(struct hci_dev *hdev);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,352 @@
#include <net/bluetooth/hci_core.h>
#include <linux/list.h>
/***********************************
** Realtek - For coexistence **
***********************************/
#define BTRTL_HCIUSB 0
#define BTRTL_HCIUART 1
#define BTRTL_HCI_IF BTRTL_HCIUSB
#define TRUE 1
#define FALSE 0
#define CONNECT_PORT 30001
#define CONNECT_PORT_WIFI 30000
#define invite_req "INVITE_REQ"
#define invite_rsp "INVITE_RSP"
#define attend_req "ATTEND_REQ"
#define attend_ack "ATTEND_ACK"
#define wifi_leave "WIFI_LEAVE"
#define leave_ack "LEAVE_ACK"
#define bt_leave "BT_LEAVE"
#define HCI_OP_PERIODIC_INQ 0x0403
#define HCI_EV_LE_META 0x3e
#define HCI_EV_LE_CONN_COMPLETE 0x01
#define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03
//vendor cmd to fw
#define HCI_VENDOR_ENABLE_PROFILE_REPORT_COMMAND 0xfc18
#define HCI_VENDOR_SET_PROFILE_REPORT_COMMAND 0xfc19
#define HCI_VENDOR_MAILBOX_CMD 0xfc8f
#define HCI_VENDOR_SET_BITPOOL 0xfc51
//subcmd to fw
#define HCI_VENDOR_SUB_CMD_WIFI_CHANNEL_AND_BANDWIDTH_CMD 0x11
#define HCI_VENDOR_SUB_CMD_WIFI_FORCE_TX_POWER_CMD 0x17
#define HCI_VENDOR_SUB_CMD_BT_ENABLE_IGNORE_WLAN_ACT_CMD 0x1B
#define HCI_VENDOR_SUB_CMD_BT_REPORT_CONN_SCO_INQ_INFO 0x23
#define HCI_VENDOR_SUB_CMD_BT_AUTO_REPORT_STATUS_INFO 0x27
#define HCI_VENDOR_SUB_CMD_BT_AUTO_REPORT_ENABLE 0x28
#define HCI_VENDOR_SUB_CMD_BT_SET_TXRETRY_REPORT_PARAM 0x29
#define HCI_VENDOR_SUB_CMD_BT_SET_PTATABLE 0x2A
#define HCI_VENDOR_SUB_CMD_SET_BT_PSD_MODE 0x31
#define HCI_VENDOR_SUB_CMD_SET_BT_LNA_CONSTRAINT 0x32
#define HCI_VENDOR_SUB_CMD_GET_AFH_MAP_L 0x40
#define HCI_VENDOR_SUB_CMD_GET_AFH_MAP_M 0x41
#define HCI_VENDOR_SUB_CMD_GET_AFH_MAP_H 0x42
#define HCI_VENDOR_SUB_CMD_RD_REG_REQ 0x43
#define HCI_VENDOR_SUB_CMD_WR_REG_REQ 0x44
#define HCI_EV_VENDOR_SPECIFIC 0xff
//sub event from fw start
#define HCI_VENDOR_PTA_REPORT_EVENT 0x24
#define HCI_VENDOR_PTA_AUTO_REPORT_EVENT 0x25
//vendor cmd to wifi driver
#define HCI_GRP_VENDOR_SPECIFIC (0x3f << 10)
#define HCI_OP_HCI_EXTENSION_VERSION_NOTIFY (0x0100 | HCI_GRP_VENDOR_SPECIFIC)
#define HCI_OP_BT_OPERATION_NOTIFY (0x0102 | HCI_GRP_VENDOR_SPECIFIC)
#define HCI_OP_HCI_BT_INFO_NOTIFY (0x0106 | HCI_GRP_VENDOR_SPECIFIC)
#define HCI_OP_HCI_BT_COEX_NOTIFY (0x0107 | HCI_GRP_VENDOR_SPECIFIC)
#define HCI_OP_HCI_BT_PATCH_VER_NOTIFY (0x0108 | HCI_GRP_VENDOR_SPECIFIC)
#define HCI_OP_HCI_BT_AFH_MAP_NOTIFY (0x0109 | HCI_GRP_VENDOR_SPECIFIC)
#define HCI_OP_HCI_BT_REGISTER_VALUE_NOTIFY (0x010a | HCI_GRP_VENDOR_SPECIFIC)
//bt info reason to wifi
#define HOST_RESPONSE 0 //Host response when receive the BT Info Control Event
#define POLLING_RESPONSE 1 //The BT Info response for polling by BT firmware.
#define AUTO_REPORT 2 //BT auto report by BT firmware.
#define STACK_REPORT_WHILE_DEVICE_D2 3 //Stack report when BT firmware is under power save state(ex:D2)
// vendor event from wifi
#define RTK_HS_EXTENSION_EVENT_WIFI_SCAN 0x01
#define RTK_HS_EXTENSION_EVENT_RADIO_STATUS_NOTIFY 0x02
#define RTK_HS_EXTENSION_EVENT_HCI_BT_INFO_CONTROL 0x03
#define RTK_HS_EXTENSION_EVENT_HCI_BT_COEX_CONTROL 0x04
//op code from wifi
#define BT_PATCH_VERSION_QUERY 0x00
#define IGNORE_WLAN_ACTIVE_CONTROL 0x01
#define LNA_CONSTRAIN_CONTROL 0x02
#define BT_POWER_DECREASE_CONTROL 0x03
#define BT_PSD_MODE_CONTROL 0x04
#define WIFI_BW_CHNL_NOTIFY 0x05
#define QUERY_BT_AFH_MAP 0x06
#define BT_REGISTER_ACCESS 0x07
//bt operation to notify
#define BT_OPCODE_NONE 0
#define BT_OPCODE_INQUIRY_START 1
#define BT_OPCODE_INQUIRY_END 2
#define BT_OPCODE_PAGE_START 3
#define BT_OPCODE_PAGE_SUCCESS_END 4
#define BT_OPCODE_PAGE_UNSUCCESS_END 5
#define BT_OPCODE_PAIR_START 6
#define BT_OPCODE_PAIR_END 7
#define BT_OPCODE_ENABLE_BT 8
#define BT_OPCODE_DISABLE_BT 9
#define HCI_EXTENSION_VERSION 0x0004
#define HCI_CMD_PREAMBLE_SIZE 3
#define PAN_PACKET_COUNT 5
#define STREAM_TO_UINT16(u16, p) {u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); (p) += 2;}
#define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);}
#define PSM_SDP 0x0001
#define PSM_RFCOMM 0x0003
#define PSM_PAN 0x000F
#define PSM_HID 0x0011
#define PSM_HID_INT 0x0013
#define PSM_AVCTP 0x0017
#define PSM_AVDTP 0x0019
#define PSM_FTP 0x1001
#define PSM_BIP 0x1003
#define PSM_OPP 0x1015
//--add more if needed--//
enum {
profile_sco = 0,
profile_hid = 1,
profile_a2dp = 2,
profile_pan = 3,
profile_hid_interval = 4,
profile_hogp = 5,
profile_voice = 6,
profile_sink = 7,
profile_max = 8
};
#define A2DP_SIGNAL 0x01
#define A2DP_MEDIA 0x02
//profile info data
typedef struct {
struct list_head list;
uint16_t handle;
uint16_t psm;
uint16_t dcid;
uint16_t scid;
uint8_t profile_index;
uint8_t flags;
} rtk_prof_info, *prtk_prof_info;
//profile info for each connection
typedef struct rtl_hci_conn {
struct list_head list;
uint16_t handle;
uint8_t type; // 0:l2cap, 1:sco/esco, 2:le
uint8_t profile_bitmap;
int8_t profile_refcount[8];
} rtk_conn_prof, *prtk_conn_prof;
#ifdef RTB_SOFTWARE_MAILBOX
struct rtl_btinfo {
u8 cmd;
u8 len;
u8 data[6];
};
#define RTL_BTINFO_LEN (sizeof(struct rtl_btinfo))
/* typedef struct {
* uint8_t cmd_index;
* uint8_t cmd_length;
* uint8_t link_status;
* uint8_t retry_cnt;
* uint8_t rssi;
* uint8_t mailbox_info;
* uint16_t acl_throughput;
* } hci_linkstatus_report; */
typedef struct {
uint8_t type;
uint32_t offset;
uint32_t value;
} hci_mailbox_register;
struct rtl_btinfo_ctl {
uint8_t polling_enable;
uint8_t polling_time;
uint8_t autoreport_enable;
};
#endif /* RTB_SOFTWARE_MAILBOX */
#define MAX_LEN_OF_HCI_EV 32
#define NUM_RTL_HCI_EV 32
struct rtl_hci_ev {
__u8 data[MAX_LEN_OF_HCI_EV];
__u16 len;
struct list_head list;
};
#define L2_MAX_SUBSEC_LEN 128
#define L2_MAX_PKTS 16
struct rtl_l2_buff {
__u8 data[L2_MAX_SUBSEC_LEN];
__u16 len;
__u16 out;
struct list_head list;
};
struct rtl_coex_struct {
struct list_head conn_hash; //hash for connections
struct list_head profile_list; //hash for profile info
struct hci_dev *hdev;
#ifdef RTB_SOFTWARE_MAILBOX
struct socket *udpsock;
struct sockaddr_in addr;
struct sockaddr_in wifi_addr;
struct timer_list polling_timer;
#endif
struct timer_list a2dp_count_timer;
struct timer_list pan_count_timer;
struct timer_list hogp_count_timer;
#ifdef RTB_SOFTWARE_MAILBOX
struct workqueue_struct *sock_wq;
struct delayed_work sock_work;
#endif
struct workqueue_struct *fw_wq;
struct delayed_work fw_work;
struct delayed_work l2_work;
#ifdef RTB_SOFTWARE_MAILBOX
struct sock *sk;
#endif
struct urb *urb;
spinlock_t spin_lock_sock;
spinlock_t spin_lock_profile;
uint32_t a2dp_packet_count;
uint32_t pan_packet_count;
uint32_t hogp_packet_count;
uint32_t voice_packet_count;
uint8_t profile_bitmap;
uint8_t profile_status;
int8_t profile_refcount[8];
uint8_t ispairing;
uint8_t isinquirying;
uint8_t ispaging;
#ifdef RTB_SOFTWARE_MAILBOX
uint8_t wifi_state;
uint8_t autoreport;
uint8_t polling_enable;
uint8_t polling_interval;
uint8_t piconet_id;
uint8_t mode;
uint8_t afh_map[10];
#endif
uint16_t hci_reversion;
uint16_t lmp_subversion;
#ifdef RTB_SOFTWARE_MAILBOX
uint8_t wifi_on;
uint8_t sock_open;
#endif
unsigned long cmd_last_tx;
/* hci ev buff */
struct list_head ev_used_list;
struct list_head ev_free_list;
spinlock_t rxlock;
__u8 pkt_type;
__u16 expect;
__u8 *tbuff;
__u16 elen;
__u8 back_buff[HCI_MAX_EVENT_SIZE];
/* l2cap rx buff */
struct list_head l2_used_list;
struct list_head l2_free_list;
/* buff addr and size */
spinlock_t buff_lock;
unsigned long pages_addr;
unsigned long buff_size;
#define RTL_COEX_RUNNING (1 << 0)
unsigned long flags;
};
#ifdef __LITTLE_ENDIAN
struct sbc_frame_hdr {
uint8_t syncword:8; /* Sync word */
uint8_t subbands:1; /* Subbands */
uint8_t allocation_method:1; /* Allocation method */
uint8_t channel_mode:2; /* Channel mode */
uint8_t blocks:2; /* Blocks */
uint8_t sampling_frequency:2; /* Sampling frequency */
uint8_t bitpool:8; /* Bitpool */
uint8_t crc_check:8; /* CRC check */
} __attribute__ ((packed));
/* NOTE: The code is copied from pa.
* only the bit field in 8-bit is affected by endian, not the 16-bit or 32-bit.
* why?
*/
struct rtp_header {
unsigned cc:4;
unsigned x:1;
unsigned p:1;
unsigned v:2;
unsigned pt:7;
unsigned m:1;
uint16_t sequence_number;
uint32_t timestamp;
uint32_t ssrc;
uint32_t csrc[0];
} __attribute__ ((packed));
#else
/* big endian */
struct sbc_frame_hdr {
uint8_t syncword:8; /* Sync word */
uint8_t sampling_frequency:2; /* Sampling frequency */
uint8_t blocks:2; /* Blocks */
uint8_t channel_mode:2; /* Channel mode */
uint8_t allocation_method:1; /* Allocation method */
uint8_t subbands:1; /* Subbands */
uint8_t bitpool:8; /* Bitpool */
uint8_t crc_check:8; /* CRC check */
} __attribute__ ((packed));
struct rtp_header {
unsigned v:2;
unsigned p:1;
unsigned x:1;
unsigned cc:4;
unsigned m:1;
unsigned pt:7;
uint16_t sequence_number;
uint32_t timestamp;
uint32_t ssrc;
uint32_t csrc[0];
} __attribute__ ((packed));
#endif /* __LITTLE_ENDIAN */
void rtk_btcoex_parse_event(uint8_t *buffer, int count);
void rtk_btcoex_parse_cmd(uint8_t *buffer, int count);
void rtk_btcoex_parse_l2cap_data_tx(uint8_t *buffer, int count);
void rtk_btcoex_parse_l2cap_data_rx(uint8_t *buffer, int count);
void rtk_btcoex_open(struct hci_dev *hdev);
void rtk_btcoex_close(void);
void rtk_btcoex_probe(struct hci_dev *hdev);
void rtk_btcoex_init(void);
void rtk_btcoex_exit(void);

View File

@@ -22,10 +22,10 @@
// this function borken on licheervnano
// this function make lcd show green color on blank
//#define START_VL "startvl 0 " LOGO_READ_ADDR " " LOGO_RESERVED_ADDR " " LOGOSIZE " " VO_ALIGNMENT ";"
#define START_VL "startvl 0 " LOGO_READ_ADDR " " LOGO_RESERVED_ADDR " " LOGOSIZE " " VO_ALIGNMENT ";"
#ifndef START_VL
#define START_VL " "
#endif
#define SET_VO_BG "setvobg 0 0xffffffff;"
#define SET_VO_BG "setvobg 0 0xffffffff ; "
#endif