全志XR829 WIFI和BT模块在T7 Android8.1上调试

1、电路图兼容速鼎WIFI和BT模块

2、涉及到的接口和管脚如下:

WIFI涉及到的管脚:
power管脚:
VCC_WIFI(wlan_power)<---->aldo1
IO_VDD(wlan_io_regulator)<---->aldo1
使能管脚:
WIFI_PDn(PL07)
WIFI_WAKE(PL05)
数据接口:
SD_CMD<---->PG01
SD_CLK<---->PG00
SD_D0 <---->PG02
SD_D1 <---->PG03
SD_D2 <---->PG04
SD_D3 <---->PG05
时钟频率:使用低速1500000

BT涉及到的管脚:
控制管脚:
AP_WAKE_BT<---->PL06
BT_LDO_EN<----->PL07
BT_POWER <------>VCC_CTP(CLDO1-3.3V)
通信管脚:
UART1_TXD<----->PG06
UART1_RXD<----->PG07
UART1_RTS<----->PG08
UART1_CTS<----->PG09
PCM蓝牙音频通路管脚(对应I2S1,需要配置成PCM模式):
PCM_SYNC<------->PG10
PCM_BCLK<------->PG11
PCM_DO  <------->PG12
PCO_DI  <------->PG13
32K_OUT <------->PG14

3、WIFI部分的系统管脚配置

lichee\tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
;sdio1配置
[sdc1]
sdc1_used          = 1
bus-width          = 4
sdc1_clk           = port:PG00<2><1><0><default>
sdc1_cmd           = port:PG01<2><1><0><default>
sdc1_d0            = port:PG02<2><1><0><default>
sdc1_d1            = port:PG03<2><1><0><default>
sdc1_d2            = port:PG04<2><1><0><default>
sdc1_d3            = port:PG05<2><1><0><default>
;non-removable      =
;cd-gpios           =
;sunxi-power-save-mode =
ctl-spec-caps      = 0x1
sunxi-power-save-mode =
sunxi-dis-signal-vol-sw =
;sd-uhs-sdr50       =
;sd-uhs-ddr50       =
;sd-uhs-sdr104      =
;cap-sdio-irq       =
keep-power-in-suspend	=
ignore-pm-notify	=
;max-frequency	        = 150000000
min-frequency = 150000

;WIFI和BT管脚配置
;--------------------------------------------------------------------------------
;wlan configuration
;wlan_used:         0-not use, 1- use
;wlan_busnum:       sdio/usb index
;clocks:            external low power clock input (32.768KHz)
;wlan_power:        input supply voltage
;wlan_io_regulator: wlan/sdio I/O voltage
;wlan_regon:        power up/down internal regulators used by wifi section
;wlan_hostwake:     wlan to wake-up host
;wlan_clk_gpio:     wlan low power clock output pin
;--------------------------------------------------------------------------------
[wlan]
compatible = "allwinner,sunxi-wlan"
wlan_used           = 1
wlan_busnum         = 1
;wlan_usbnum         = 2
;clocks             = "&clk_outa"
wlan_power          = "vcc-wifi"
;wlan_power_ext      = "dvdd-csi"
;wlan_io_regulator   = "vcc-io-wifi"
wlan_io_regulator   = "vcc-wifi"
wlan_regon          = port:PL04<1><default><default><0>
wlan_hostwake       = port:PL05<6><default><default><0>
;wlan_clk_gpio       = port:PI12<4><default><default><0>


;--------------------------------------------------------------------------------
;bluetooth configuration
;bt_used:           0- no used, 1- used
;clocks:            external low power clock input (32.768KHz)
;bt_power:          input supply voltage
;bt_io_regulator:   bluetooth I/O voltage
;bt_rst_n:          power up/down internal regulators used by BT section
;--------------------------------------------------------------------------------
[bt]
bt_used             = 1
compatible = "allwinner,sunxi-bt"
bt_power            = "vcc-ctp"
;clocks             = 0x55
bt_rst_n            = port:PL07<1><default><default><0>
ap_wakeup_bt        = port:PL06<1><default><1><0>

4、WIFI部分的上电时序,BT部分后面再写

struct sunxi_wlan_platdata {
	int bus_index;
	struct regulator *wlan_power;
	struct regulator *wlan_power_ext;
	struct regulator *io_regulator;
	struct clk *lpo;
	int gpio_wlan_regon;
	int gpio_wlan_hostwake;
	int gpio_chip_en;

	char *wlan_power_name;
	char *wlan_power_ext_name;
	char *io_regulator_name;

	int power_state;
    int enable_state;
	struct platform_device *pdev;
};
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sunxi-gpio.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include <linux/pm_runtime.h>

#include <linux/etherdevice.h>
#include <linux/crypto.h>
#include <linux/miscdevice.h>
#include <linux/capability.h>

#include "sunxi-rfkill.h"

#define USE_HOSC_24M

static struct sunxi_wlan_platdata *wlan_data = NULL;

//static char *project_name = CONFIG_PROJECT_NAME;
static int g_wifi_module = 0;  //default xradio
static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off);
static int sunxi_wlan_enable(struct sunxi_wlan_platdata *data, bool enable);
static DEFINE_MUTEX(sunxi_wlan_mutex);

unsigned int wlan_power = 0;
void sunxi_wlan_set_enable(bool enable)
{
	struct platform_device *pdev;
	int ret = 0;
	if (!wlan_data)
		return;

	pdev = wlan_data->pdev;
	mutex_lock(&sunxi_wlan_mutex);
	if (enable != wlan_data->enable_state) {
		ret = sunxi_wlan_enable(wlan_data, enable);
		if (ret)
			dev_err(&pdev->dev, "set enable failed\n");
	}
	mutex_unlock(&sunxi_wlan_mutex);
	wlan_power = enable;
	dev_info(&pdev->dev,"%s,%d\n",__func__,enable);
}
EXPORT_SYMBOL(wlan_power);
EXPORT_SYMBOL_GPL(sunxi_wlan_set_enable);

void sunxi_wlan_set_power(bool on_off)
{
	struct platform_device *pdev;
	int ret = 0;
	if (!wlan_data)
		return;

	pdev = wlan_data->pdev;
	mutex_lock(&sunxi_wlan_mutex);
	if(on_off != wlan_data->power_state){
		ret = sunxi_wlan_on(wlan_data, on_off);
		if(ret)
			dev_err(&pdev->dev,"set power failed\n");
	}
	mutex_unlock(&sunxi_wlan_mutex);
}
EXPORT_SYMBOL_GPL(sunxi_wlan_set_power);

//nwd-zhaojr add for recognize wifi module
void sunxi_wlan_set_module(int flag)
{
	printk("nwd-wangtao set wifi module : %s\n", flag?"xr829":"xradio");
	g_wifi_module = flag;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_set_module);

int sunxi_wlan_get_bus_index(void)
{
	struct platform_device *pdev;
	pr_err("%s ---sunxi_wlan_get_bus_index()\n", __FUNCTION__);

	if (!wlan_data)
		return -EINVAL;

	pdev = wlan_data->pdev;
	dev_info(&pdev->dev,"bus_index: %d\n",wlan_data->bus_index);
	return wlan_data->bus_index;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_get_bus_index);

int sunxi_wlan_get_oob_irq(void)
{
	struct platform_device *pdev;
	int host_oob_irq = 0;
	if (!wlan_data || !gpio_is_valid(wlan_data->gpio_wlan_hostwake))
		return 0;

	pdev = wlan_data->pdev;
	host_oob_irq = gpio_to_irq(wlan_data->gpio_wlan_hostwake);
	if (IS_ERR_VALUE(host_oob_irq)) 
		dev_err(&pdev->dev,"map gpio [%d] to virq failed, errno = %d\n",
			wlan_data->gpio_wlan_hostwake,host_oob_irq);

	return host_oob_irq;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_get_oob_irq);

int sunxi_wlan_get_oob_irq_flags(void)
{
	int oob_irq_flags;
	if (!wlan_data)
		return 0;

	oob_irq_flags = (IRQF_TRIGGER_HIGH | IRQF_SHARED | IRQF_NO_SUSPEND);

	return oob_irq_flags;
}
EXPORT_SYMBOL_GPL(sunxi_wlan_get_oob_irq_flags);

static int sunxi_wlan_enable(struct sunxi_wlan_platdata *data, bool enable)
{
	struct platform_device *pdev = data->pdev;
	struct device *dev = &pdev->dev;

	if (gpio_is_valid(data->gpio_wlan_regon)) {
		if (enable) {
			gpio_direction_output(data->gpio_wlan_regon, 1);
		} else {
			gpio_direction_output(data->gpio_wlan_regon, 0);
		}
		dev_info(dev, "set wlan enable: %s\n", enable ? "1" : "0");
	}
	wlan_data->enable_state = enable;
	return 0;
}

static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off)
{
	struct platform_device *pdev = data->pdev;
	struct device *dev = &pdev->dev;
	int ret = 0;
	pr_err("%s ---sunxi_wlan_on()\n", __FUNCTION__);
	pr_err("sunxi_wlan_on()--regulator--:on_off: %d\n",on_off);
	
	//if (!on_off && gpio_is_valid(data->gpio_wlan_regon))
	//	gpio_set_value(data->gpio_wlan_regon, 0);

	if (data->wlan_power_name) {
		data->wlan_power = regulator_get(dev, data->wlan_power_name);
		if (!IS_ERR(data->wlan_power)) {
			if (on_off) {
				regulator_set_voltage(data->wlan_power,
						(int)3300*1000,
						(int)3300*1000);

				ret = regulator_enable(data->wlan_power);
				if (ret < 0) {
					dev_err(dev, "regulator wlan_power enable failed\n");
					regulator_put(data->wlan_power);
					return ret;
				}

				ret = regulator_get_voltage(data->wlan_power);
				if (ret < 0) {
					dev_err(dev, "regulator wlan_power get voltage failed\n");
					regulator_put(data->wlan_power);
					return ret;
				}
				dev_info(dev, "check wlan wlan_power voltage: %d\n",
						 ret);
			} else {
				ret = regulator_disable(data->wlan_power);
				if (ret < 0) {
					dev_err(dev, "regulator wlan_power disable failed\n");
					regulator_put(data->wlan_power);
					return ret;
				}
			}
			regulator_put(data->wlan_power);
		}
	}
#if 0 //no use
	if (data->wlan_power_ext_name) {
		data->wlan_power_ext = regulator_get(dev, data->wlan_power_ext_name);
		if (!IS_ERR(data->wlan_power_ext)) {
			if (on_off) {
				regulator_set_voltage(data->wlan_power_ext,
						(int)3300*1000,
						(int)3300*1000);
						//(int)1800*1000,
						//(int)1800*1000);

				ret = regulator_enable(data->wlan_power_ext);
				if (ret < 0) {
					dev_err(dev, "regulator wlan_power_ext enable failed\n");
					regulator_put(data->wlan_power_ext);
					//return ret;
				}

				ret = regulator_get_voltage(data->wlan_power_ext);
				if (ret < 0) {
					dev_err(dev, "regulator wlan_power_ext get voltage "
							"failed\n");
					regulator_put(data->wlan_power_ext);
					//return ret;
				}
				dev_info(dev, "check wlan BT power wlan_power_ext voltage: %d\n",
						ret);
			} else {
				ret = regulator_disable(data->wlan_power_ext);
				if (ret < 0) {
					dev_err(dev, "regulator wlan_power_ext disable failed\n");
					regulator_put(data->wlan_power_ext);
					//return ret;
				}
			}
			regulator_put(data->wlan_power_ext);
		}
	}
#endif
	if(data->io_regulator_name){
		data->io_regulator = regulator_get(dev, data->io_regulator_name);
		if (!IS_ERR(data->io_regulator)) {
			if(on_off){
				ret = regulator_enable(data->io_regulator);
				if (ret < 0){
					dev_err(dev, "regulator io_regulator enable failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}

				ret = regulator_get_voltage(data->io_regulator);
				if (ret < 0){
					dev_err(dev, "regulator io_regulator get voltage failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}
				dev_info(dev, "check wlan io_regulator voltage: %d\n",ret);
			}else{
				ret = regulator_disable(data->io_regulator);
				if (ret < 0){
					dev_err(dev, "regulator io_regulator disable failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}
			}
			regulator_put(data->io_regulator);
		}
	}

//	if (on_off && gpio_is_valid(data->gpio_wlan_regon)) {
//		mdelay(10);
//		gpio_set_value(data->gpio_wlan_regon, 1);
//	}
	wlan_data->power_state = on_off;

	return 0;
}

static ssize_t power_state_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	pr_err("%s ---power_state_show()\n", __FUNCTION__);
	return sprintf(buf, "%d\n", wlan_data->power_state);
}

static ssize_t power_state_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	unsigned long state;
	int err;
	pr_err("%s ---power_state_store()\n", __FUNCTION__);
	
	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	err = kstrtoul(buf, 0, &state);
	if (err)
		return err;

	if (state > 1)
		return -EINVAL;
		
	pr_err("power_state_store():state: %d\n",state);
	mutex_lock(&sunxi_wlan_mutex);
	if (state != wlan_data->power_state) {
		err = sunxi_wlan_on(wlan_data, state);
		if (err)
			dev_err(dev, "set power failed\n");
	}
	mutex_unlock(&sunxi_wlan_mutex);

	return count;
}

static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR | S_IWGRP,
		power_state_show, power_state_store);

static ssize_t enable_state_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", wlan_data->enable_state);
}

static ssize_t enable_state_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	unsigned long state;
	int err;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	err = kstrtoul(buf, 0, &state);
	if (err)
		return err;

	if (state > 1)
		return -EINVAL;

	mutex_lock(&sunxi_wlan_mutex);
	if (state != wlan_data->enable_state) {
		err = sunxi_wlan_enable(wlan_data, state);
		if (err)
			dev_err(dev,"set enable failed\n");
	}
	mutex_unlock(&sunxi_wlan_mutex);

	return count;
}

//static DEVICE_ATTR(enable_state, S_IWUGO | S_IRUGO,
//		enable_state_show, enable_state_store);
static DEVICE_ATTR(enable_state, S_IRUGO | S_IWUSR | S_IWGRP,
		enable_state_show, enable_state_store);

static ssize_t scan_device_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	unsigned long state;
	int err;
	int bus = wlan_data->bus_index;
	pr_err("%s ---scan_device_store()\n", __FUNCTION__);
	
	err = kstrtoul(buf, 0, &state);
	if (err)
		return err;
    
	//sunxi_wl_chipen_set(0, state);

	dev_info(dev, "scan_device_store()::start scan device on bus_index: %d state:%d\n",
			wlan_data->bus_index,state);
	if (bus < 0) {
		dev_err(dev, "scan device fail!\n");
		return -1;
	}
	sunxi_mmc_rescan_card(bus);

	return count;
}
static DEVICE_ATTR(scan_device, S_IRUGO | S_IWUSR | S_IWGRP,
		NULL, scan_device_store);

extern void sunxi_mmc_remove_sdio(unsigned ids);
static ssize_t remove_card_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	int bus = wlan_data->bus_index;

	dev_info(dev, "start remove card on bus_index: %d\n",
			wlan_data->bus_index);
	if (bus < 0) {
		dev_err(dev, "remove card fail!\n");
		return -1;
	}
	sunxi_mmc_remove_sdio(bus);
	return count;
}
static DEVICE_ATTR(remove_card, S_IRUGO | S_IWUSR | S_IWGRP,
		NULL, remove_card_store);
static struct attribute *misc_attributes[] = {
	&dev_attr_enable_state.attr,
	&dev_attr_scan_device.attr,
	&dev_attr_remove_card.attr,
	&dev_attr_power_state.attr,	
	NULL,
};

static struct attribute_group misc_attribute_group = {
	.name  = "rf-ctrl",
	.attrs = misc_attributes,
};

static struct miscdevice sunxi_wlan_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name  = "sunxi-wlan",
};

static char wifi_mac_str[18] = {0};

void sunxi_wlan_chipid_mac_address(u8 *mac)
{
#define MD5_SIZE	16
#define CHIP_SIZE	16

	struct crypto_hash *tfm;
	struct hash_desc desc;
	struct scatterlist sg;
	u8 result[MD5_SIZE];
	u8 chipid[CHIP_SIZE];
	int i = 0;
	int ret = -1;

	memset(chipid, 0, sizeof(chipid));
	memset(result, 0, sizeof(result));

	sunxi_get_soc_chipid((u8 *)chipid);

	tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
	if (IS_ERR(tfm)) {
		pr_err("Failed to alloc md5\n");
		return;
	}
	desc.tfm = tfm;
	desc.flags = 0;

	ret = crypto_hash_init(&desc);
	if (ret < 0) {
		pr_err("crypto_hash_init() failed\n");
		goto out;
	}

	sg_init_one(&sg, chipid, sizeof(chipid) - 1);
	ret = crypto_hash_update(&desc, &sg, sizeof(chipid) - 1);
	if (ret < 0) {
		pr_err("crypto_hash_update() failed for id\n");
		goto out;
	}

	crypto_hash_final(&desc, result);
	if (ret < 0) {
		pr_err("crypto_hash_final() failed for result\n");
		goto out;
	}

	/* Choose md5 result's [0][2][4][6][8][10] byte as mac address */
	for (i = 0; i < 6; i++)
		mac[i] = result[2*i];
	mac[0] &= 0xfe;     /* clear multicast bit */
	mac[0] &= 0xfd;     /* clear local assignment bit (IEEE802) */

out:
	crypto_free_hash(tfm);
}
EXPORT_SYMBOL(sunxi_wlan_chipid_mac_address);

void sunxi_wlan_custom_mac_address(u8 *mac)
{
	int i;
	char *p = wifi_mac_str;
	u8 mac_addr[ETH_ALEN] = {0};

	if (0 == strlen(p))
		return;

	for (i = 0; i < ETH_ALEN; i++, p++)
		mac_addr[i] = simple_strtoul(p, &p, 16);

	memcpy(mac, mac_addr, sizeof(mac_addr));
}
EXPORT_SYMBOL(sunxi_wlan_custom_mac_address);

#ifndef MODULE
static int __init set_wlan_mac_addr(char *str)
{
	char *p = str;

	if (str != NULL && *str)
		strlcpy(wifi_mac_str, p, 18);

	return 0;
}
__setup("wifi_mac=", set_wlan_mac_addr);
#endif

static int sunxi_wlan_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	struct sunxi_wlan_platdata *data;
	struct gpio_config config;
	u32 val;
	const char *power, *io_regulator, *power_ext;
	int ret = 0;
#ifdef USE_HOSC_24M
	void __iomem *vaddr;
#endif
	pr_err("%s ---sunxi_wlan_probe()\n", __FUNCTION__);
	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

	data->pdev = pdev;
	wlan_data = data;
    
	data->bus_index = -1;
	if (!of_property_read_u32(np, "wlan_busnum", &val)) {
		switch (val) {
		case 0:
		case 1:
		case 2:
			data->bus_index = val;
			break;
		default:
			dev_err(dev, "unsupported wlan_busnum (%u)\n", val);
			return -EINVAL;
		}
	}
	dev_info(dev, "wlan_busnum (%u)\n", val);

	if (of_property_read_string(np, "wlan_power", &power)) {
		dev_warn(dev, "Missing wlan_power.\n");
	} else {
		data->wlan_power_name = devm_kzalloc(dev, 64, GFP_KERNEL);
		if (!data->wlan_power_name)
			return -ENOMEM;
		strcpy(data->wlan_power_name, power);
	}
	dev_info(dev, "wlan_power_name (%s)\n", data->wlan_power_name);
#if 0 //no use
	if (of_property_read_string(np, "wlan_power_ext", &power_ext)) {
		dev_warn(dev, "Missing wlan_power_ext.\n");
	} else {
		data->wlan_power_ext_name = devm_kzalloc(dev, 64, GFP_KERNEL);
		if (!data->wlan_power_ext_name)
			return -ENOMEM;
		strcpy(data->wlan_power_ext_name, power_ext);

	}
	dev_info(dev, "wlan_power_ext_name (%s)\n", data->wlan_power_ext_name);
#endif
	if (of_property_read_string(np, "wlan_io_regulator", &io_regulator)) {
		dev_warn(dev, "Missing wlan_io_regulator.\n");
	} else {
		data->io_regulator_name = devm_kzalloc(dev, 64, GFP_KERNEL);
		if (!data->io_regulator_name)
			return -ENOMEM;
		strcpy(data->io_regulator_name, io_regulator);
	}
	dev_info(dev, "io_regulator_name (%s)\n", data->io_regulator_name);

	data->gpio_wlan_regon = of_get_named_gpio_flags(np, "wlan_regon",
			0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(data->gpio_wlan_regon)) {
		dev_err(dev, "get gpio wlan_regon failed\n");
	} else {
		dev_info(dev, "wlan_regon gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, data->gpio_wlan_regon,
				"wlan_regon");
		if (ret < 0) {
			dev_err(dev, "can't request wlan_regon gpio %d\n",
				data->gpio_wlan_regon);
			return ret;
		}

		ret = gpio_direction_output(data->gpio_wlan_regon, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction wlan_regon gpio %d\n",
				data->gpio_wlan_regon);
			return ret;
		}
	}
#if 0 //no use
	data->gpio_chip_en = of_get_named_gpio_flags(np, "chip_en",
			0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(data->gpio_chip_en)) {
		dev_err(dev, "get gpio chip_en failed\n");
	} else {
		dev_info(dev, "chip_en gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, data->gpio_chip_en, "chip_en");
		if (ret < 0) {
			dev_err(dev, "can't request chip_en gpio %d\n",
				data->gpio_chip_en);
			return ret;
		}

		ret = gpio_direction_output(data->gpio_chip_en, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction chip_en gpio %d\n",
				data->gpio_chip_en);
			return ret;
		}
	}
#endif
	data->gpio_wlan_hostwake = of_get_named_gpio_flags(np, "wlan_hostwake",
			0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(data->gpio_wlan_hostwake)) {
		dev_err(dev, "get gpio wlan_hostwake failed\n");
	} else {
		dev_info(dev,
				"wlan_hostwake gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, data->gpio_wlan_hostwake,
				"wlan_hostwake");
		if (ret < 0) {
			dev_err(dev, "can't request wlan_hostwake gpio %d\n",
				data->gpio_wlan_hostwake);
			return ret;
		}

		gpio_direction_input(data->gpio_wlan_hostwake);
		if (ret < 0) {
			dev_err(dev,
				"can't request input direction wlan_hostwake gpio %d\n",
				data->gpio_wlan_hostwake);
			return ret;
		}

#ifdef CONFIG_PM
		ret = enable_gpio_wakeup_src(data->gpio_wlan_hostwake);
		if (ret < 0) {
			dev_err(dev, "can't enable wakeup src for wlan_hostwake %d\n",
				data->gpio_wlan_hostwake);
			return ret;
		}
#endif		
	}
#if 0 //no use
	data->lpo = devm_clk_get(dev, NULL);
	if (IS_ERR_OR_NULL(data->lpo)) {
		dev_warn(dev, "clk not config\n");
	} else {
		ret = clk_prepare_enable(data->lpo);
		if (ret < 0)
			dev_warn(dev, "can't enable clk\n");
	}
#endif
#ifdef USE_HOSC_24M
	vaddr = ioremap(0x03001f20, sizeof(unsigned long));
	val = readl(vaddr);
	val |= 0x80000000;
	writel(val, vaddr);
	val = readl(vaddr);
	iounmap(vaddr);
	dev_info(dev, "HOSC_24M reg val is: 0x%08x\n", val);
#endif

	ret = misc_register(&sunxi_wlan_dev);
	if (ret) {
		dev_err(dev, "sunxi-wlan register driver as misc device error!\n");
		return ret;
	}
	ret = sysfs_create_group(&sunxi_wlan_dev.this_device->kobj,
			&misc_attribute_group);
	if (ret) {
		dev_err(dev, "sunxi-wlan register sysfs create group failed!\n");
		return ret;
	}

	data->power_state = 0;
	data->enable_state = 0;

	if (1 != data->power_state) {
		sunxi_wlan_set_power(1);
	}
	
	return 0;
}

static int sunxi_wlan_remove(struct platform_device *pdev)
{
	misc_deregister(&sunxi_wlan_dev);
	sysfs_remove_group(&(sunxi_wlan_dev.this_device->kobj),
			&misc_attribute_group);

	if (!IS_ERR_OR_NULL(wlan_data->lpo))
		clk_disable_unprepare(wlan_data->lpo);

	return 0;
}

static int wlan_pm_suspend(struct device *dev)
{
	//struct sunxi_wlan_platdata *platdata = dev_get_drvdata(dev);

	printk("CONFIG_PM:enter wlan_pm_suspend.\n");

	if (pm_runtime_suspended(dev))
		return 0;

	//if (hw_ver == HW_VER_V1_0 || hw_ver == HW_VER_V4_0) {
	//	//return 0;
	//}

	//sunxi_wlan_set_enable(0);
	sunxi_wlan_set_power(0);

	return 0;
}

static int wlan_pm_resume(struct device *dev)
{
	//struct sunxi_wlan_platdata *platdata = dev_get_drvdata(dev);

	printk("CONFIG_PM:enter wlan_pm_resume.\n");

	if (pm_runtime_suspended(dev))
		return 0;

	//if (hw_ver == HW_VER_V1_0 || hw_ver == HW_VER_V4_0) {
	//	//return 0;
	//}

	sunxi_wlan_set_power(1);
	//sunxi_wlan_set_enable(1);

	return 0;
}

static struct dev_pm_ops wlan_pm_ops = {
	.suspend = wlan_pm_suspend,
	.resume  = wlan_pm_resume,
};

static const struct of_device_id sunxi_wlan_ids[] = {
	{ .compatible = "allwinner,sunxi-wlan" },
	{ /* Sentinel */ }
};

static struct platform_driver sunxi_wlan_driver = {
	.probe	= sunxi_wlan_probe,
	.remove	= sunxi_wlan_remove,
	.driver	= {
		.owner	= THIS_MODULE,
		.name	= "sunxi-wlan",
		.pm     = &wlan_pm_ops,
		.of_match_table	= sunxi_wlan_ids,
	},
};

module_platform_driver(sunxi_wlan_driver);

MODULE_DESCRIPTION("sunxi wlan driver");
MODULE_LICENSE(GPL);

5、XR829驱动的整合

将全志给的linux4.9的驱动放到lichee\linux-4.9\drivers\net\wireless\xr829这个目录,修改
lichee\linux-4.9\drivers\net\wireless\Makefile
lichee\linux-4.9\drivers\net\wireless\Kconfig
这两个文件,注意跟公版XR819模块的兼容,如下:

lichee\linux-4.9\drivers\net\wireless\Kconfig
menuconfig WLAN
	bool "Wireless LAN"
	depends on !S390
	depends on NET
	select WIRELESS
	default y
	---help---
	  This section contains all the pre 802.11 and 802.11 wireless
	  device drivers. For a complete list of drivers and documentation
	  on them refer to the wireless wiki:
	  http://wireless.kernel.org/en/users/Drivers
//添加配置选项
config XR829_SUDING_DRIVERS_CHOOSE
	tristate "Choose XR829 and suding wifi&&bt support"
	---help---
	  Choose XR829 or suding configuration
config XR819_DRIVERS_CHOOSE
	tristate "Choose XR819 wifi&&bt support"
	---help---
	  Choose XR819 configuration

if WLAN

..............................................
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/ralink/Kconfig"
source "drivers/net/wireless/realtek/Kconfig"
source "drivers/net/wireless/rsi/Kconfig"
source "drivers/net/wireless/st/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zydas/Kconfig"
//根据不同的配置选择加载不同模块的配置文件
if XR829_SUDING_DRIVERS_CHOOSE
source "drivers/net/wireless/xr829/Kconfig"
endif #XR829_SUDING_DRIVERS_CHOOSE
if XR819_DRIVERS_CHOOSE
source "drivers/net/wireless/xradio/Kconfig"
endif #XR819_DRIVERS_CHOOSE

lichee\linux-4.9\drivers\net\wireless\Makefile
obj-$(CONFIG_BCMDHD)	+= bcmdhd/
//添加如下两行
obj-$(CONFIG_XR829_SUDING_DRIVERS_CHOOSE)   += xr829/
obj-$(CONFIG_XR819_DRIVERS_CHOOSE)   += xradio/

修改:
lichee\linux-4.9\drivers\net\wireless\xr829\Kconfig
menuconfig XR_WLAN
	tristate "XRadio WLAN support"
	default n
	select CFG80211
	select AVERAGE
	select CRYPTO
	select CRYPTO_ARC4
	---help---
	  To compile this code as a module, choose M here.
if XR_WLAN
#source "drivers/net/wireless/xr/umac/Kconfig"
#source "drivers/net/wireless/xradio/wlan/Kconfig"//修改成:
source "drivers/net/wireless/xr829/umac/Kconfig"
source "drivers/net/wireless/xr829/wlan/Kconfig"
endif # XR_WLAN

修改:
lichee\linux-4.9\drivers\net\wireless\xr829\Makefile
#NOSTDINC_FLAGS := -I$(srctree)/drivers/net/wireless/xradio/include/ //修改成
NOSTDINC_FLAGS := -I$(srctree)/drivers/net/wireless/xr829/include/

obj-$(CONFIG_XR_WLAN) += umac/
obj-$(CONFIG_XR_WLAN) += wlan/

clean-files += Module.symvers Module.markers modules modules.order

在文件
lichee\linux-4.9\arch\arm\configs\sun8iw17p1_android_k23x3-wb_defconfig中添加
...........................
CONFIG_WLAN=y
CONFIG_XR829_SUDING_DRIVERS_CHOOSE=y  //添加这个配置,选择XR829的配置
CONFIG_WLAN_VENDOR_ADMTEK=y
CONFIG_WLAN_VENDOR_ATH=y
...............................

6、android平台OS的配置

android\device\softwinner\k23x3-wb\BoardConfig.mk
BOARD_WIFI_VENDOR := xradio
##BOARD_WIFI_VENDOR := QCOM
..............................
#1.4 xradio wifi config
ifeq ($(BOARD_WIFI_VENDOR), xradio)
    # WiFi Configuration
    WPA_SUPPLICANT_VERSION := VER_0_8_X
    BOARD_WPA_SUPPLICANT_DRIVER := NL80211
    BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_xr
    BOARD_HOSTAPD_DRIVER        := NL80211
    BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_xr

    BOARD_WLAN_DEVICE           := xradio

    WIFI_DRIVER_MODULE_PATH     := "/system/vendor/modules/xradio_wlan.ko"
    WIFI_DRIVER_MODULE_NAME     := "xradio_wlan"
    WIFI_DRIVER_MODULE_ARG      := ""
    include hardware/xradio/wlan/kernel-firmware/xradio-wlan.mk
endif
其它涉及到的文件:
device\softwinner\common\init.wireless.xradio.rc
android\hardware\xradio\
直接使用全志发布的公版代码即可。

以上的文件修改之后,编译整个系统,系统启动后打开WIFI基本就OK了。

二、BT部分的调试

1、android os平台部分的配置

android\device\softwinner\common\init.wireless.xradio.rc
在文件中添加:
 # to observe dnsmasq.leases file for dhcp information of soft ap.
    chown dhcp system /data/misc/dhcp
    #for XR829 by zhjr add
    #setprop persist.sys.i2s 1
    setprop persist.bt.sco.uart  1  //蓝牙电话走串口
    chmod 0666 /sys/class/bt_power/power_state
    chmod 0666 /sys/class/bt_power/uart_enable
    #end by zhaojr

android\device\softwinner\k23x3-wb\BoardConfig.mk
修改:
BOARD_WIFI_VENDOR := xradio
##BOARD_WIFI_VENDOR := QCOM
..................................
#1.4 xradio wifi config
ifeq ($(BOARD_WIFI_VENDOR), xradio)
    # WiFi Configuration
    WPA_SUPPLICANT_VERSION := VER_0_8_X
    BOARD_WPA_SUPPLICANT_DRIVER := NL80211
    BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_xr
    BOARD_HOSTAPD_DRIVER        := NL80211
    BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_xr

    BOARD_WLAN_DEVICE           := xradio

    WIFI_DRIVER_MODULE_PATH     := "/system/vendor/modules/xradio_wlan.ko"
    WIFI_DRIVER_MODULE_NAME     := "xradio_wlan"
    WIFI_DRIVER_MODULE_ARG      := ""

    include hardware/xradio/wlan/kernel-firmware/xradio-wlan.mk
endif
其他部分已经在WIFI中配置OK.

固件部分配置:
android\hardware\xradio\wlan\kernel-firmware\xradio-wlan.mk 添加:
hardware/xradio/wlan/kernel-firmware/boot_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/boot_xr829.bin \
    hardware/xradio/wlan/kernel-firmware/fw_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/fw_xr829.bin \
    hardware/xradio/wlan/kernel-firmware/sdd_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/sdd_xr829.bin \
    hardware/xradio/wlan/kernel-firmware/etf_xr829.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/etf_xr829.bin \
    hardware/xradio/wlan/kernel-firmware/fw_xr829_bt.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/fw_xr829_bt.bin \
    hardware/xradio/wlan/kernel-firmware/fw_xr829_bt_sco_hci_uart.bin:$(TARGET_COPY_OUT_VENDOR)/etc/firmware/fw_xr829_bt_sco_hci_uart.bin \
    hardware/xradio/wlan/kernel-firmware/fw_xr829_bt.bin:/system/etc/firmware/fw_xr829_bt.bin  \
    hardware/xradio/wlan/kernel-firmware/fw_xr829_bt_sco_hci_uart.bin:/system/etc/firmware/fw_xr829_bt_sco_hci_uart.bin //这个主要针对bt声音通道的固件

2、内核部分的配置修改:

lichee\linux-4.9\arch\arm\configs\sun8iw17p1_android_k23x3-wb_defconfig
添加以下配置:

CONFIG_WLAN=y
CONFIG_XR829_SUDING_DRIVERS_CHOOSE=y //便于XR829的上电时序和XR829模块驱动
# CONFIG_XR819_DRIVERS_CHOOSE is not set
CONFIG_WLAN_VENDOR_ADMTEK=y
CONFIG_WLAN_VENDOR_ATH=y
# CONFIG_ATH_DEBUG is not set
# CONFIG_ATH6KL is not set
CONFIG_WLAN_VENDOR_ATMEL=y
CONFIG_WLAN_VENDOR_BROADCOM=y
# CONFIG_BRCMFMAC is not set
CONFIG_WLAN_VENDOR_CISCO=y
CONFIG_WLAN_VENDOR_INTEL=y
CONFIG_WLAN_VENDOR_INTERSIL=y
# CONFIG_HOSTAP is not set
CONFIG_WLAN_VENDOR_MARVELL=y
# CONFIG_LIBERTAS is not set
# CONFIG_MWIFIEX is not set
CONFIG_WLAN_VENDOR_MEDIATEK=y
CONFIG_WLAN_VENDOR_RALINK=y
CONFIG_WLAN_VENDOR_REALTEK=y
CONFIG_WLAN_VENDOR_RSI=y
CONFIG_WLAN_VENDOR_ST=y
CONFIG_WLAN_VENDOR_TI=y
CONFIG_WLAN_VENDOR_ZYDAS=y
# CONFIG_USB_ZD1201 is not set

#CONFIG_XR_WLAN=m
#CONFIG_XRMAC=m
#CONFIG_XRMAC_RC_DEFAULT="minstrel_ht"
# CONFIG_XRMAC_RC_PID is not set
#CONFIG_XRMAC_RC_MINSTREL=y
#CONFIG_XRMAC_RC_MINSTREL_HT=y
#CONFIG_XRMAC_DEBUGFS=y
#CONFIG_XRADIO=m
#CONFIG_XRADIO_SDIO=y
#CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES=y
# CONFIG_XRADIO_USE_GPIO_IRQ is not set
#CONFIG_XRADIO_SUSPEND_POWER_OFF=y
# CONFIG_XRADIO_EXTEND_SUSPEND is not set

#
# Driver debug features
#
CONFIG_XR_WLAN=m
CONFIG_XRMAC=m
CONFIG_XRMAC_RC_DEFAULT="minstrel_ht"
# CONFIG_XRMAC_RC_PID is not set
CONFIG_XRMAC_RC_MINSTREL=y
CONFIG_XRMAC_RC_MINSTREL_HT=y
CONFIG_XRMAC_DEBUGFS=y
CONFIG_XRADIO=m
CONFIG_XRADIO_SDIO=y
CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES=y
CONFIG_XRADIO_USE_GPIO_IRQ=y //走GPIO驱动
CONFIG_XRADIO_SUSPEND_POWER_OFF=y
# CONFIG_XRADIO_EXTEND_SUSPEND is not set

#
# Driver debug features
#
CONFIG_XRADIO_DEBUG=y
CONFIG_XRADIO_ETF=y
# CONFIG_XRADIO_DUMP_ON_ERROR is not set
CONFIG_XRADIO_DEBUGFS=y
# CONFIG_USB_NET_RNDIS_WLAN is not set
# CONFIG_BCMDHD is not set
# CONFIG_RTL8723CS is not set
# CONFIG_RTL8723BS_VQ0 is not set

3、系统部分的配置和上电时序

3.1系统配置

lichee\tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
bt模块的管脚配置(请结合原理图来配置):
;--------------------------------------------------------------------------------
;bluetooth configuration
;bt_used:           0- no used, 1- used
;clocks:            external low power clock input (32.768KHz)
;bt_power:          input supply voltage
;bt_io_regulator:   bluetooth I/O voltage
;bt_rst_n:          power up/down internal regulators used by BT section
;--------------------------------------------------------------------------------
[bt]
bt_used             = 1
compatible = "allwinner,sunxi-bt"
bt_power            = "vcc-ctp"
;clocks             = 0x55
bt_rst_n            = port:PL07<1><default><default><0>
ap_wakeup_bt        = port:PL06<1><default><1><0>
音频的I2S1的PCM通道不要配置,保留速鼎模块的配置即可,最后XR829模块不用I2S1的PCM通道,走的是串口到主芯片,然后根据切源管理之后到声卡0到MCU的方式出声音(包括蓝牙电话和音乐)

3.2、上电时序

lichee\linux-4.9\drivers\misc\sunxi-rf\sunxi-bluetooth.c(跟速鼎模块共用一个上电时序)
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sunxi-gpio.h>
#include <linux/pm_runtime.h>

#include "sunxi-rfkill.h"

static struct sunxi_bt_platdata *bluetooth_data = NULL;
static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off);
static DEFINE_MUTEX(sunxi_bluetooth_mutex);

static int set_bt_uart_enable(struct sunxi_bt_platdata *platdata, int enable)
{
	int gpio_tx = GPIOG(6);
	int gpio_rx = GPIOG(7);
	int UART_FUNC = 2;
	char pin_name_tx[SUNXI_PIN_NAME_MAX_LEN];
	char pin_name_rx[SUNXI_PIN_NAME_MAX_LEN];
	long unsigned int config_set;
	long unsigned int config_get;

	sunxi_gpio_to_name(gpio_tx, pin_name_tx);
	sunxi_gpio_to_name(gpio_rx, pin_name_rx);

	if (0 == enable) {

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
				SUNXI_PIN_OUTPUT_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
				SUNXI_PIN_DATA_LOW);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
				SUNXI_PIN_OUTPUT_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
					pin_name_rx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
				SUNXI_PIN_DATA_LOW);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
					pin_name_rx);
		}

	} else if (1 == enable) {

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
				SUNXI_PIN_PULL_UP);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
					pin_name_rx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
				SUNXI_PIN_PULL_UP);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
					pin_name_rx);
		}
	}
	platdata->uart_enable = enable;

	return 0;
}

static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off)
{
	struct platform_device *pdev = data->pdev;
	struct device *dev = &pdev->dev;
	int ret = 0;
	pr_err("%s ---sunxi_bt_on()\n", __FUNCTION__);
#if 0
	if (!on_off && gpio_is_valid(data->gpio_bt_rst))
		gpio_set_value(data->gpio_bt_rst, 0);
#endif

	if (data->bt_power_name) {
		data->bt_power = regulator_get(dev, data->bt_power_name);
		if (!IS_ERR(data->bt_power)) {
			if (on_off) {
				regulator_set_voltage(data->bt_power,
						(int)3300*1000,
						(int)3300*1000);

				ret = regulator_enable(data->bt_power);
				if (ret < 0) {
					dev_err(dev, "regulator bt_power enable failed\n");
					regulator_put(data->bt_power);
					return ret;
				}

				ret = regulator_get_voltage(data->bt_power);
				if (ret < 0) {
					dev_err(dev, "regulator bt_power get voltage failed\n");
					regulator_put(data->bt_power);
					return ret;
				}
				dev_info(dev, "check bluetooth bt_power voltage: %d\n", ret);
			} else {
				ret = regulator_disable(data->bt_power);
				if (ret < 0) {
					dev_err(dev, "regulator bt_power disable failed\n");
					regulator_put(data->bt_power);
					return ret;
				}
			}
			regulator_put(data->bt_power);
		}
	}

#if 0
	if(data->io_regulator_name){
		data->io_regulator = regulator_get(dev, data->io_regulator_name);
		if (!IS_ERR(data->io_regulator)) {
			if (on_off) {
				regulator_set_voltage(data->io_regulator,
						(int)3300*1000,
						(int)3300*1000);

				ret = regulator_enable(data->io_regulator);
				if (ret < 0) {
					dev_err(dev, "regulator io_regulator enable failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}

				ret = regulator_get_voltage(data->io_regulator);
				if (ret < 0) {
					dev_err(dev, "regulator io_regulator get voltage failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}
				dev_info(dev, "check bluetooth io_regulator voltage: %d\n",ret);
			}else{
				ret = regulator_disable(data->io_regulator);
				if (ret < 0) {
					dev_err(dev, "regulator io_regulator disable failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}
			}
			regulator_put(data->io_regulator);
		}
	}

	if (on_off && gpio_is_valid(data->gpio_bt_rst)) {
		mdelay(10);
		gpio_set_value(data->gpio_bt_rst, 1);
	}
#endif
	data->power_state = on_off;

	return 0;
}

static int sunxi_bt_set_block(void *data, bool blocked)
{
	struct sunxi_bt_platdata *platdata = data;
	struct platform_device *pdev = platdata->pdev;
	int ret;

	if (blocked != platdata->power_state) {
		dev_warn(&pdev->dev, "block state already is %d\n", blocked);
		return 0;
	}

	dev_info(&pdev->dev, "set block: %d\n", blocked);
	//ret = sunxi_bt_on(platdata, !blocked);
	//if (ret) {
	//	dev_err(&pdev->dev, "set block failed\n");
	//	return ret;
	//}

	//sunxi_wl_chipen_set(1, !blocked);

	return 0;
}

static const struct rfkill_ops sunxi_bt_rfkill_ops = {
	.set_block = sunxi_bt_set_block,
};

static ssize_t power_state_show(struct class *class,
		struct class_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", bluetooth_data->power_state);
}

void bt_xr829_enable(int enable);
static ssize_t power_state_store(struct class *class,
		struct class_attribute *attr, const char *buf, size_t count)
{
	unsigned long state;
	int err;
	
	pr_err("%s ---power_state_store()\n", __FUNCTION__);
	err = kstrtoul(buf, 0, &state);
	if (err)
		return err;
    	bluetooth_data->power_state = state;
	pr_err("%s ---bt_xr829_enable = %ld\n",__FUNCTION__,state);
	if(state)
		bt_xr829_enable(1);
	else
		bt_xr829_enable(0);
	return count;

#if 0
	if (state > 1)
		return count;

	mutex_lock(&sunxi_bluetooth_mutex);
	if (state != bluetooth_data->power_state) {
		dev_info(&bluetooth_data->pdev->dev, "set power: %s\n", state ? "on" : "off");
		err = sunxi_bt_on(bluetooth_data, state);
		if (err) {
			dev_err(&bluetooth_data->pdev->dev, "set power failed\n");
		}
	}
	sunxi_wl_chipen_set(1, state);
	mutex_unlock(&sunxi_bluetooth_mutex);

	return count;
#endif
}

static ssize_t uart_enable_store(struct class *class,
		struct class_attribute *attr, const char *buf, size_t count)
{
	unsigned long enable;
	int err;

	err = kstrtoul(buf, 0, &enable);
	if (err)
		return err;

	if (enable > 1)
		return count;
	
	pr_err("%s ---enable:%d\n", __FUNCTION__,enable);
	if (enable != bluetooth_data->uart_enable) {
		dev_info(&bluetooth_data->pdev->dev, "bt uart: %s\n", enable ? "on" : "off");
		err = set_bt_uart_enable(bluetooth_data, enable);
		if(0==err)
		{
		  if (0 == enable){
		  	bluetooth_data->uart_enable = 0;
		  } else {
				bluetooth_data->uart_enable = 1;
		  }
	   }
	   else{
			dev_err(&bluetooth_data->pdev->dev, "set uart failed\n");
		}
	}

	return count;
}

static struct class_attribute bt_attribute_group[] = {
	__ATTR(power_state, S_IRUGO | S_IWUSR | S_IWGRP,
			power_state_show, power_state_store),
	__ATTR(uart_enable, S_IWUSR | S_IWGRP,
			NULL, uart_enable_store),
	__ATTR_NULL
};

static struct class bt_power_class = {
	.name = "bt_power",
	.class_attrs = bt_attribute_group,
};
static int bt_xr829_reset = 0;
static int bt_xr829_wake = 0;
void bt_xr829_enable(int enable)
{
	pr_err("%s aa enable=%d\n",__FUNCTION__,enable);
	if(enable){
	gpio_direction_output(bt_xr829_reset, 1);
	gpio_direction_output(bt_xr829_wake, 1);
	}else{
			gpio_direction_output(bt_xr829_wake, 0);
			gpio_direction_output(bt_xr829_reset, 0);
	}
}

static int sunxi_bt_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	struct sunxi_bt_platdata *data;
	struct gpio_config config;
	const char *power, *io_regulator;
	int ret = 0;
    dev_info(dev, "sunxi_bt_probe()\n");
	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

	data->pdev = pdev;
	bluetooth_data = data;

	if (of_property_read_string(np, "bt_power", &power)) {
		dev_warn(dev, "Missing bt_power.\n");
	} else {
		data->bt_power_name = devm_kzalloc(dev, 64, GFP_KERNEL);
		if (!data->bt_power_name)
			return -ENOMEM;
		else
			strcpy(data->bt_power_name,power);
	}
	dev_info(dev, "bt_power_name (%s)\n", data->bt_power_name);
	bt_xr829_reset = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(bt_xr829_reset)) {
		pr_err("get gpio bt_rst failed\n");
	} else {
		pr_err("bt_rst gpio=%d gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",bt_xr829_reset,
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, bt_xr829_reset, "bt_xr829_reset");
		if (ret < 0) {
			dev_err(dev, "can't request bt_rst gpio %d\n",
				bt_xr829_reset);
			return ret;
		}

		ret = gpio_direction_output(bt_xr829_reset, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction bt_rst gpio %d\n",
				bt_xr829_reset);
			return ret;
		}
		gpio_set_value(data->gpio_bt_rst, 0);
	}

	bt_xr829_wake = of_get_named_gpio_flags(np, "ap_wakeup_bt", 0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(bt_xr829_wake)) {
		pr_err("get gpio bt_xr829_wake failed\n");
	} else {
		pr_err("bt_xr829_wake gpio=%d gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",bt_xr829_wake,
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, bt_xr829_wake, "ap_wakeup_bt");
		if (ret < 0) {
			dev_err(dev, "can't request bt_xr829_wake gpio %d\n",
				bt_xr829_wake);
			return ret;
		}

		ret = gpio_direction_output(bt_xr829_wake, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction bt_xr829_wake %d\n",
				bt_xr829_wake);
			return ret;
		}
	}

#if 0
	if (of_property_read_string(np, "bt_io_regulator", &io_regulator)) {
		dev_warn(dev, "Missing bt_io_regulator.\n");
	} else {
		data->io_regulator_name = devm_kzalloc(dev, 64, GFP_KERNEL);
		if (!data->io_regulator_name)
			return -ENOMEM;
		else
			strcpy(data->io_regulator_name,io_regulator);
	}
	dev_info(dev, "io_regulator_name (%s)\n", data->io_regulator_name);

	data->gpio_bt_rst = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(data->gpio_bt_rst)) {
		dev_err(dev, "get gpio bt_rst failed\n");
	} else {
		dev_info(dev, "bt_rst gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, data->gpio_bt_rst, "bt_rst");
		if (ret < 0) {
			dev_err(dev, "can't request bt_rst gpio %d\n",
				data->gpio_bt_rst);
			return ret;
		}

		ret = gpio_direction_output(data->gpio_bt_rst, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction bt_rst gpio %d\n",
				data->gpio_bt_rst);
			return ret;
		}
	}

	data->lpo = devm_clk_get(dev, NULL);
	if (IS_ERR_OR_NULL(data->lpo)) {
		dev_warn(dev, "clk not config\n");
	} else {
		ret = clk_prepare_enable(data->lpo);
		if (ret < 0)
			dev_warn(dev, "can't enable clk\n");
	}
#endif

	data->rfkill = rfkill_alloc("sunxi-bt", dev, RFKILL_TYPE_BLUETOOTH,
				&sunxi_bt_rfkill_ops, data);
	if (!data->rfkill) {
		ret = -ENOMEM;
		goto failed_alloc;
	}

	rfkill_set_states(data->rfkill, true, false);

	ret = rfkill_register(data->rfkill);
	if (ret){
		goto fail_rfkill;
	}
	platform_set_drvdata(pdev, data);

	class_register(&bt_power_class);

	data->power_state = 0;
	data->uart_enable = 1;

	pm_runtime_set_active(dev);
	pm_runtime_get(dev);
	pm_runtime_enable(dev);

	if (1 != data->power_state) {
		sunxi_bt_on(data, 1);
	}

	return 0;

fail_rfkill:
	if (data->rfkill) 
		rfkill_destroy(data->rfkill);
failed_alloc:
	//if (!IS_ERR_OR_NULL(data->lpo)) {
	//	clk_disable_unprepare(data->lpo);
	//	clk_put(data->lpo);
	//}
	return ret;
}

static int sunxi_bt_remove(struct platform_device *pdev)
{
	struct sunxi_bt_platdata *data = platform_get_drvdata(pdev);
	struct rfkill *rfk = data->rfkill;

	pm_runtime_disable(&pdev->dev);
	pm_runtime_set_suspended(&pdev->dev);

	class_unregister(&bt_power_class);

	platform_set_drvdata(pdev, NULL);
	
	if(rfk){
		rfkill_unregister(rfk);
		rfkill_destroy(rfk);
	}

	//if (!IS_ERR_OR_NULL(data->lpo)) {
	//	clk_disable_unprepare(data->lpo);
	//	clk_put(data->lpo);
	//}

	return 0;
}

static int bt_pm_suspend(struct device *dev)
{
	struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);

	printk("CONFIG_PM:enter bt_pm_suspend.\n");

	if (pm_runtime_suspended(dev))
		return 0;

	if (platdata) {
		if (0 != platdata->power_state) {
			sunxi_bt_on(platdata, 0);
		}
	}

	return 0;
}

static int bt_pm_resume(struct device *dev)
{
	struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);

	printk("CONFIG_PM:enter bt_pm_resume.\n");

	if (pm_runtime_suspended(dev))
		return 0;

	if (platdata) {
		if (1 != platdata->power_state) {
			sunxi_bt_on(platdata, 1);
		}
	}

	return 0;
}
static struct dev_pm_ops bt_pm_ops = {
	.suspend = bt_pm_suspend,
	.resume  = bt_pm_resume,
};

static const struct of_device_id sunxi_bt_ids[] = {
	{ .compatible = "allwinner,sunxi-bt" },
	{ /* Sentinel */ }
};

static struct platform_driver sunxi_bt_driver = {
	.probe	= sunxi_bt_probe,
	.remove	= sunxi_bt_remove,
	.driver	= {
		.owner	= THIS_MODULE,
		.name	= "sunxi-bt",
		.pm     = &bt_pm_ops,
		.of_match_table	= sunxi_bt_ids,
	},
};

module_platform_driver(sunxi_bt_driver);

MODULE_DESCRIPTION("sunxi bluetooth driver");
MODULE_LICENSE(GPL);

lichee\linux-4.9\drivers\misc\sunxi-rf\sunxi-rfkill.h
/*
 * drivers/misc/sunxi-rf/sunxi-rfkill.h
 *
 * Copyright (c) 2014 softwinner.
 *
 * 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.
 *
 */

#ifndef __SUNXI_RFKILL_H
#define __SUNXI_RFKILL_H

#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/rfkill.h>

struct sunxi_bt_platdata {
	struct regulator *bt_power;
	struct regulator *io_regulator;
	struct clk *lpo;
	int gpio_bt_rst;
	char *bt_power_name;
	char *io_regulator_name;

	int power_state;
	int uart_enable;
	struct rfkill *rfkill;
	struct platform_device *pdev;
};

struct sunxi_wlan_platdata {
	int bus_index;
	struct regulator *wlan_power;
	struct regulator *wlan_power_ext;
	struct regulator *io_regulator;
	struct clk *lpo;
	int gpio_wlan_regon;
	int gpio_wlan_hostwake;
	int gpio_chip_en;

	char *wlan_power_name;
	char *wlan_power_ext_name;
	char *io_regulator_name;

	int power_state;
       int enable_state;
	struct platform_device *pdev;
};

extern void sunxi_wl_chipen_set(int dev, int on_off);
extern void sunxi_wlan_set_power(bool on_off);
extern int  sunxi_wlan_get_bus_index(void);
extern int  sunxi_wlan_get_oob_irq(void);
extern int  sunxi_wlan_get_oob_irq_flags(void);
extern int  enable_gpio_wakeup_src(int para);
extern void sunxi_mmc_rescan_card(unsigned ids);
extern int  sunxi_get_soc_chipid(uint8_t *chipid);

#endif /* SUNXI_RFKILL_H */

最后将蓝牙协议栈供应商给的gocsdk放到/system/bin下,如下:

adb push gocsdk /system/bin/
adb shell
chmod 777 /system/bin/gocsdk
sync
之后重新上电即可

系统起来之后,到蓝牙电话界面打开,手机连接车机即可,之后打开WIFI连接到路由器,进行协议栈的注册授权,之后就可以使用蓝牙音乐和蓝牙电话了,注意授权只在第一次刷机之后运行的情况,没有刷机是不需要每次都授权的。

r18_ap6330的步骤.txt 2017/2/9 10:47 开始整理 原始SDK默认配置为rtl8723bs,现在要修改为:AP6330。 硬件平台:全志R18。 开发板:DP-18 SDK:Android6.0.1/Linux3.10.65(v2.1版本,发布日期20161008WIFI测试需要支持5.8G的AP来测试。BT用普通的Android手机发送图片即可。 1、请严重注意全志R18的架构选择:ARCH=arm64 rootroot@cm-System-Product-Name:/home/wwt/dp18_ap6330/lichee/linux-3.10$ rootroot@cm-System-Product-Name:/home/wwt/dp18_ap6330/lichee/linux-3.10$ make ARCH=arm64 menuconfig rootroot@cm-System-Product-Name:/home/wwt/dp18_ap6330/lichee/linux-3.10$ WIFI部分可以关闭realtek: Device Drivers ---> [*] Network device support ---> [*] Wireless LAN ---> Realtek 8723B SDIO or SPI WiFi Realtek 8723BS_VQ0 WiFi Realtek 8189F SDIO WiFi Realtek 8723C SDIO or SPI WiFi 可以配置内核打印级别为18(默认为17?) General setup ---> (18) Kernel log buffer size (16 => 64KB, 17 => 128KB) 2、 Z:\home\wwt\dp18_ap6330\android\device\softwinner\tulip-d1\BoardConfig.mk 关闭这里: # BOARD_WIFI_VENDOR := realtek 打开这里: BOARD_WIFI_VENDOR := broadcom 蓝牙的修改: ##BOARD_HAVE_BLUETOOTH_BCM := true BOARD_HAVE_BLUETOOTH_NAME := rtl8723bs_vq0 BOARD_HAVE_BLUETOOTH_RTK_COEX := true BOARD_HAVE_BLUETOOTH_RTK := true BLUETOOTH_HCI_USE_RTK_H5 := true 修改为: BOARD_HAVE_BLUETOOTH_BCM := true BOARD_HAVE_BLUETOOTH_NAME := ap6330 #BOARD_HAVE_BLUETOOTH_NAME := rtl8723bs_vq0 #BOARD_HAVE_BLUETOOTH_RTK_COEX := true #BOARD_HAVE_BLUETOOTH_RTK := true #BLUETOOTH_HCI_USE_RTK_H5 := true 3、 Z:\home\wwt\dp18_ap6330\android\device\softwinner\tulip-d1\init.sun50iw1p1.rc 蓝牙部分: #bluesleep insmod /system/vendor/modules/rtl_btlpm.ko 修改为: #bluesleep #insmod /system/vendor/modules/rtl_btlpm.ko #network insmod /system/vendor/modules/bcmdhd.ko insmod /system/vendor/modules/bcm_btlpm.ko WIFI部分: 关闭这里: # 1. realtek & eagle wifi service # 1.1 realtek & eagle wifi sta service service wpa_supplicant /system/bin/wpa_supplicant \ -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \ -I/system/etc/wifi/wpa_supplicant_overlay.conf \ -O/data/misc/wifi/sockets \ -e
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值