一、电路原理和内核部分的配置
1、电路原理
其中,管脚连接如下:
电路连接管脚:
WIFI部分:
wlan_power----dldo2(3.3V)
wlan_io_regulator----dldo3(3.3V)
WIFI_PDn(wlan_on)-----PA04(GPIO)
WIFI_IRQ(wlan_hostwake)----PH18(GPIO)
SDIO使用SDIO3
BT部分:
UART口使用UART1
AP_WAKE_BT(bt_hostwake)-----PB10(GPIO)
GOC-BC8_EN(bt_rst_n)--------PA16(GPIO)
bt_power ------------dldo1(3.3V)
2、系统管脚和电源配置:
lichee\tools\pack\chips\sun8iw11p1\configs\x217\sys_config.fex
......................................................
;UART1口的配置
[uart1]
uart1_used = 1
uart1_port = 1
uart1_type = 4
uart1_tx = port:PA10<4><1><default><default>
uart1_rx = port:PA11<4><1><default><default>
uart1_rts = port:PA12<4><1><default><default>
uart1_cts = port:PA13<4><1><default><default>
uart1_dtr =
uart1_dsr =
uart1_dcd =
uart1_ring =
[uart1_suspend]
uart1_tx = port:PA10<7><1><default><default>
uart1_rx = port:PA11<7><1><default><default>
uart1_rts = port:PA12<7><1><default><default>
uart1_cts = port:PA13<7><1><default><default>
uart1_dtr =
uart1_dsr =
uart1_dcd =
uart1_ring =
....................................................
;SDIO3的配置
[sdc3]
sdc3_used = 1
;non-removable = 1
bus-width = 4
sdc3_clk = port:PI05<2><1><2><default>
sdc3_cmd = port:PI04<2><1><2><default>
sdc3_d0 = port:PI06<2><1><2><default>
sdc3_d1 = port:PI07<2><1><2><default>
sdc3_d2 = port:PI08<2><1><2><default>
sdc3_d3 = port:PI09<2><1><2><default>
;cd-gpios =
ctl-spec-caps = 0x1
sunxi-power-save-mode =
sunxi-dis-signal-vol-sw =
;sunxi-power-save-mode =
;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
......................................
............................................
;--------------------------------------------------------------------------------
;接口GPIO和电源配置
;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]
wlan_used = 1
wlan_busnum = 3
;clocks = "&clk_outa"
wlan_power = "vcc-wifi"
wlan_io_regulator = "vcc-wifi-ext"
wlan_18_power1 = "vcc-wifi-18"
wlan_18_power2 = "vdd-sata-12"
wlan_regon = port:PA04<1><default><default><0>
wlan_hostwake = port:PH18<6><default><default><0>
wlan_clk_gpio =
;--------------------------------------------------------------------------------
;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
;clocks = "&clk_outa"
bt_power = "vcc-io-wifi"
bt_io_regulator = "vcc-io-wifi"
bt_rst_n = port:PA16<1><0><default><0>
bt_ldo_en = port:PA16<1><0><default><0>
ap_wakeup_bt = port:PB10<1><default><1><0>
3、上电时序重新编写
WIFI上电时序(具体使用请参考T7 android8.1平台的WIFI部分的上电时序程序说明):
lichee\linux-3.10\drivers\misc\sunxi-rf\sunxi-wlan-xr829.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/interrupt.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sys_config.h>
#include <linux/if_ether.h>
#include <linux/etherdevice.h>
#include <linux/crypto.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <linux/pm_runtime.h>
#include <mach/sw-board-ctrl.h>
struct sunxi_wlan_platdata {
int bus_index;
struct regulator *wlan_power;
struct regulator *wlan_power_ext;
struct regulator *io_regulator;
struct regulator *wlan_18_power1;
struct regulator *wlan_18_power2;
struct clk *lpo;
int gpio_wlan_regon;
int gpio_wlan_hostwake;
char *wlan_power_name;
char *wlan_power_ext_name;
char *io_regulator_name;
char *wlan_18_power1_name;
char *wlan_18_power2_name;
int power_state;
int enable_state;
int power_flag;
struct platform_device *pdev;
};
static struct sunxi_wlan_platdata *wlan_data = NULL;
static int hw_ver = 0x0;
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);
u8 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-wangtao 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;
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;
if(data->wlan_power_name){
data->wlan_power = regulator_get(dev, data->wlan_power_name);
if (!IS_ERR(data->wlan_power)) {
if(on_off){
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(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){
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_err(dev, "check wlan 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);
}
}
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 (hw_ver == HW_VER_V1_0 || hw_ver == HW_VER_V4_0) {
if (data->wlan_18_power1_name) {
data->wlan_18_power1 = regulator_get(dev,
data->wlan_18_power1_name);
if (!IS_ERR(data->wlan_18_power1)) {
if (on_off) {
if(1 == g_wifi_module)
{
regulator_set_voltage(data->wlan_18_power1,
(int)3300*1000,
(int)3300*1000);
}
else if(0 == g_wifi_module)
{
regulator_set_voltage(data->wlan_18_power1,
(int)1800*1000,
(int)1800*1000);
}
ret = regulator_enable(data->wlan_18_power1);
if (ret < 0) {
dev_err(dev, "regulator wlan_18_power1 enable "
"failed\n");
regulator_put(data->wlan_18_power1);
return ret;
}
ret = regulator_get_voltage(data->wlan_18_power1);
if (ret < 0) {
dev_err(dev, "regulator wlan_18_power1 get voltage "
"failed\n");
regulator_put(data->wlan_18_power1);
return ret;
}
dev_info(dev, "check wlan wlan_18_power1 voltage: %d\n",
ret);
} else {
ret = regulator_disable(data->wlan_18_power1);
if (ret < 0) {
dev_err(dev, "regulator wlan_18_power1 disable "
"failed\n");
regulator_put(data->wlan_18_power1);
return ret;
}
}
regulator_put(data->wlan_18_power1);
}
}
if (data->wlan_18_power2_name) {
data->wlan_18_power2 = regulator_get(dev,
data->wlan_18_power2_name);
if (!IS_ERR(data->wlan_18_power2)) {
if (on_off) {
regulator_set_voltage(data->wlan_18_power2,
(int)1800*1000,
(int)1800*1000);
ret = regulator_enable(data->wlan_18_power2);
if (ret < 0) {
dev_err(dev, "regulator wlan_18_power2 enable "
"failed\n");
regulator_put(data->wlan_18_power2);
return ret;
}
ret = regulator_get_voltage(data->wlan_18_power2);
if (ret < 0) {
dev_err(dev, "regulator wlan_18_power2 get voltage "
"failed\n");
regulator_put(data->wlan_18_power2);
return ret;
}
dev_info(dev, "check wlan wlan_18_power2 voltage: %d\n",
ret);
} else {
ret = regulator_disable(data->wlan_18_power2);
if (ret < 0) {
dev_err(dev, "regulator wlan_18_power2 disable "
"failed\n");
regulator_put(data->wlan_18_power2);
return ret;
}
}
regulator_put(data->wlan_18_power2);
}
}
}
wlan_data->power_state = on_off;
return 0;
}
static ssize_t power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
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;
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->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;
}
int wlan_flag = -1;
static ssize_t wlan_flag_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", wlan_flag);
}
static ssize_t wlan_flag_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
err = kstrtoul(buf, 0, &state);
if(err)
return err;
wlan_flag = state;
dev_info(dev, "%s: wlan_flag:%d\n", __func__, wlan_flag);
return count;
}
EXPORT_SYMBOL(wlan_flag);
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;
}
extern void sunxi_mmc_rescan_card(unsigned ids);
static ssize_t scan_device_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int bus = wlan_data->bus_index;
dev_info(dev, "start scan device on bus_index: %d\n",
wlan_data->bus_index);
if (bus < 0) {
dev_err(dev, "scan device fail!\n");
return -1;
}
sunxi_mmc_rescan_card(bus);
return count;
}
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(power_state, S_IWUSR | S_IWGRP | S_IRUGO,
power_state_show, power_state_store);
static DEVICE_ATTR(enable_state, S_IWUGO | S_IRUGO,
enable_state_show, enable_state_store);
static DEVICE_ATTR(scan_device, S_IWUSR | S_IWGRP,
NULL, scan_device_store);
static DEVICE_ATTR(remove_card, S_IWUSR | S_IWGRP | S_IWOTH,
NULL, remove_card_store);
static DEVICE_ATTR(power_flag, S_IWUGO | S_IRUGO,
wlan_flag_show, wlan_flag_store);
static struct attribute *misc_attributes[] = {
//&dev_attr_power_state.attr,
&dev_attr_enable_state.attr,
&dev_attr_scan_device.attr,
&dev_attr_remove_card.attr,
&dev_attr_power_flag.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};
/*extern int sunxi_get_soc_chipid(uint8_t *chipid);*/
static 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)
{
char *p = wifi_mac_str;
u8 mac_addr[ETH_ALEN] = {0};
if (!strlen(p))
return;
/*
kstrtoul(p, 16, mac_addr);
*/
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;
unsigned long pin_config;
u32 val;
const char *power,*io_regulator,*power_ext;
const char *wlan_18_power1, *wlan_18_power2;
int ret = 0;
int clk_gpio;
char pin_name[32];
hw_ver = get_hw_ver();
dev_info(dev, "hardware version is (0x%2x)\n", hw_ver);
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:
case 3:
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;
else
strcpy(data->wlan_power_name,power);
}
dev_info(dev, "wlan_power_name (%s)\n", data->wlan_power_name);
/***************************************************************/
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)
strcpy(data->wlan_power_ext_name,power_ext);
}
dev_info(dev, "wlan_power_ext_name (%s)\n", data->wlan_power_ext_name);
/*******************************************************************/
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;
else
strcpy(data->io_regulator_name,io_regulator);
}
dev_info(dev, "io_regulator_name (%s)\n", data->io_regulator_name);
/*******************************************************************/
if (hw_ver == HW_VER_V1_0 || hw_ver == HW_VER_V4_0) {
if (of_property_read_string(np, "wlan_18_power1", &wlan_18_power1)) {
dev_warn(dev, "Missing wlan_18_power1.\n");
}else{
data->wlan_18_power1_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if(!data->wlan_18_power1_name)
return -ENOMEM;
else
strcpy(data->wlan_18_power1_name,wlan_18_power1);
}
dev_info(dev, "wlan_18_power1_name (%s)\n", data->wlan_18_power1_name);
if (of_property_read_string(np, "wlan_18_power2", &wlan_18_power2)) {
dev_warn(dev, "Missing wlan_18_power2.\n");
}else{
data->wlan_18_power2_name = devm_kzalloc(dev, 64, GFP_KERNEL);
if(!data->wlan_18_power2_name)
return -ENOMEM;
else
strcpy(data->wlan_18_power2_name,wlan_18_power2);
}
dev_info(dev, "wlan_18_power2_name (%s)\n", data->wlan_18_power2_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;
}
}
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;
}
}
clk_gpio = of_get_named_gpio_flags(np, "wlan_clk_gpio", 0,
(enum of_gpio_flags *)&config);
if (!gpio_is_valid(clk_gpio)) {
dev_err(dev, "get gpio wlan_clk_gpio failed\n");
} else {
dev_info(dev, "wlan_clk_gpio 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, clk_gpio, "wlan_clk_gpio");
if (ret < 0) {
dev_err(dev, "can't request wlan_clk_gpio gpio %d\n",
clk_gpio);
return ret;
}
sunxi_gpio_to_name(config.gpio, pin_name);
pin_config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
config.mul_sel);
pin_config_set(SUNXI_PINCTRL, pin_name, pin_config);
}
data->lpo = of_clk_get(np, 0);
if (IS_ERR_OR_NULL(data->lpo)){
dev_warn(dev, "clk not config\n");
}else{
dev_warn(dev, "enable clk\n");
ret = clk_prepare_enable(data->lpo);
if (ret < 0)
dev_warn(dev, "can't enable clk\n");
}
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;
wlan_flag = 0;
pm_runtime_set_active(dev);
pm_runtime_get(dev);
pm_runtime_enable(dev);
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 int sunxi_wlan_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
WARN_ON(0 != 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);
clk_put(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);
BT部分的上电时序:
lichee\linux-3.10\drivers\misc\sunxi-rf\sunxi-bluetooth-xr829.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/sys_config.h>
#include <linux/pm_runtime.h>
extern int set_bt_uart_enable(int enable);
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;
};
static struct sunxi_bt_platdata *bt_data = NULL;
static DEFINE_MUTEX(sunxi_bt_mutex);
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;
#if 0
if (!on_off && gpio_is_valid(data->gpio_bt_rst))
gpio_direction_output(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){
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_direction_output(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;
//}
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", bt_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;
err = kstrtoul(buf, 0, &state);
if (err)
return err;
bt_data->power_state = state;
printk("bt_xr829_enable = %ld\n",state);
if(state)
bt_xr829_enable(1);
else
bt_xr829_enable(0);
return count;
/*
if (state > 1 )
return -EINVAL;
mutex_lock(&sunxi_bt_mutex);
if (state != bt_data->power_state) {
dev_info(&bt_data->pdev->dev, "set power: %s\n", state ? "on" : "off");
err = sunxi_bt_on(bt_data, state);
if (err) {
dev_err(&bt_data->pdev->dev, "set power failed\n");
return err;
}
}
mutex_unlock(&sunxi_bt_mutex);
return count;
*/
}
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 -EINVAL;
if (enable != bt_data->uart_enable) {
dev_info(&bt_data->pdev->dev, "bt uart: %s\n", enable ? "on" : "off");
err = set_bt_uart_enable(enable);
if (0 == err) {
if (0 == enable) {
bt_data->uart_enable = 0;
} else {
bt_data->uart_enable = 1;
}
}
}
return count;
}
static struct class_attribute bt_attribute_group[] = {
__ATTR(power_state, S_IWUGO | S_IRUGO,
power_state_show, power_state_store),
__ATTR(uart_enable, S_IWUGO,
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)
{
printk("%s aa enable=%d\n",__func__,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;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!dev)
return -ENOMEM;
data->pdev = pdev;
bt_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)) {
printk("get gpio bt_rst failed\n");
} else {
printk("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) {
printk(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) {
printk(dev, "can't request output direction bt_rst gpio %d\n",
bt_xr829_reset);
return ret;
}
}
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)) {
printk("get gpio bt_xr829_wake failed\n");
} else {
printk("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) {
printk(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) {
printk(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 = of_clk_get(np, 0);
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-3.10\drivers\misc\sunxi-rf\Makefile
#
# Makefile for wifi bluetooth power controller drivers
#
ifdef CONFIG_XR_829
obj-$(CONFIG_SUNXI_RFKILL) += sunxi-wlan-xr829.o sunxi-bluetooth-xr829.o sunxi-gps-xr829.o
else
obj-$(CONFIG_SUNXI_RFKILL) += sunxi-wlan.o sunxi-bluetooth.o sunxi-gps.o
endif
4、内核代码的整合
将XR829模块原厂给的驱动拷贝到lichee\linux-3.10\drivers\net\wireless\xr829\目录中,修改KCONFIG和Makefile文件,如下:
lichee\linux-3.10\drivers\net\wireless\Kconfig 添加:
source "drivers/net/wireless/xradio/Kconfig"
source "drivers/net/wireless/xr829/Kconfig" //添加
lichee\linux-3.10\drivers\net\wireless\Makefile
obj-$(CONFIG_XR_WLAN) += xradio/
obj-$(CONFIG_XR_829) += xr829/ //添加
lichee\linux-3.10\drivers\net\wireless\xr829\Kconfig 内容如下:
menuconfig XR_829
tristate "XRadio WLAN support(xr829)"
default n
select CFG80211
select AVERAGE
select CRYPTO
select CRYPTO_ARC4
---help---
To compile this code as a module, choose M here.
if XR_829
source "drivers/net/wireless/xr829/umac/Kconfig"
source "drivers/net/wireless/xr829/wlan/Kconfig"
endif #XR_829
lichee\linux-3.10\drivers\net\wireless\xr829\Makefile 内容如下:
NOSTDINC_FLAGS := -I$(srctree)/drivers/net/wireless/xr829/include/
obj-$(CONFIG_XR_829) += umac/
obj-$(CONFIG_XR_829) += wlan/
clean-files += Module.symvers Module.markers modules modules.order
修改配置,使其编译出来xr829_xxx.ko文件,如下:
lichee\linux-3.10\drivers\net\wireless\xr829\umac\Makefile 内容如下,将xradio的字样修改成xr829,如下:
obj-$(CONFIG_XRMAC) += xr829_mac.o
ldflags-y += --strip-debug
# xr829_mac objects
xr829_mac-y := \
main.o status.o \
sta_info.o \
wep.o \
wpa.o \
wapi.o \
scan.o offchannel.o \
ht.o agg-tx.o agg-rx.o \
ibss.o \
mlme.o work.o \
iface.o \
rate.o \
michael.o \
tkip.o \
aes_ccm.o \
aes_cmac.o \
cfg.o \
rx.o \
spectmgmt.o \
tx.o \
key.o \
util.o \
wme.o \
event.o \
chan.o
xr829_mac-$(CONFIG_XRMAC_LEDS) += led.o
xr829_mac-$(CONFIG_XRMAC_DEBUGFS) += \
debugfs.o \
debugfs_sta.o \
debugfs_netdev.o \
debugfs_key.o
xr829_mac-$(CONFIG_XRMAC_MESH) += \
mesh.o \
mesh_pathtbl.o \
mesh_plink.o \
mesh_hwmp.o
xr829_mac-$(CONFIG_PM) += pm.o
xr829_mac-$(CONFIG_XRMAC_DRIVER_API_TRACER) += driver-trace.o
CFLAGS_driver-trace.o := -I$(src)
# objects for PID algorithm
rc80211_pid-y := rc80211_pid_algo.o
rc80211_pid-$(CONFIG_XRMAC_DEBUGFS) += rc80211_pid_debugfs.o
rc80211_minstrel-y := rc80211_minstrel.o
rc80211_minstrel-$(CONFIG_XRMAC_DEBUGFS) += rc80211_minstrel_debugfs.o
rc80211_minstrel_ht-y := rc80211_minstrel_ht.o
rc80211_minstrel_ht-$(CONFIG_XRMAC_DEBUGFS) += rc80211_minstrel_ht_debugfs.o
xr829_mac-$(CONFIG_XRMAC_RC_PID) += $(rc80211_pid-y)
xr829_mac-$(CONFIG_XRMAC_RC_MINSTREL) += $(rc80211_minstrel-y)
xr829_mac-$(CONFIG_XRMAC_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y)
ccflags-y += -D__CHECK_ENDIAN__
# Extra IE for probe response from upper layer is needed in P2P GO
# For offloading probe response to FW, the extra IE must be included
# in the probe response template
ccflags-y += -DPROBE_RESP_EXTRA_IE
ccflags-y += -DIPV6_FILTERING
#ccflags-y += -DCONFIG_XRMAC_XR_ROAMING_CHANGES
ccflags-y += -DUSE_RSSI_SMOOTH
lichee\linux-3.10\drivers\net\wireless\xr829\wlan\Makefile 内容如下:
xr829_core-y := \
common.o \
fwio.o \
txrx.o \
main.o \
queue.o \
hwio.o \
bh.o \
wsm.o \
sta.o \
ap.o \
scan.o \
platform.o \
fw_dbg_inf.o
xr829_core-$(CONFIG_PM) += pm.o
xr829_core-$(CONFIG_XRADIO_SDIO) += sdio.o
xr829_core-$(CONFIG_XRADIO_DEBUG) += debug.o
xr829_core-$(CONFIG_XRADIO_ITP) += itp.o
xr829_core-$(CONFIG_XRADIO_ETF) += etf.o
ldflags-y += --strip-debug
xr829_wlan-y := wlan_init.o
obj-$(CONFIG_XRADIO) += xr829_core.o
obj-m += xr829_wlan.o
##======= User Options =======
## Use vfs for firmware load when request_firmware
# can't work on other platform.
ccflags-y += -DUSE_VFS_FIRMWARE
## Mac addr config, disable hex for default.
#ccflags-y += -DXRADIO_MACPARAM_HEX
ccflags-y += -DMONITOR_MODE
##======= Follows just for xradio internal, Don't change these macro if not ensure. =======
ccflags-y += -DCONFIG_XRADIO_USE_EXTENSIONS
ccflags-y += -DP2P_MULTIVIF
ccflags-y += -DMCAST_FWDING
ccflags-y += -DXRADIO_SUSPEND_RESUME_FILTER_ENABLE
ccflags-y += -DAP_AGGREGATE_FW_FIX
ccflags-y += -DAP_HT_CAP_UPDATE
ccflags-y += -DAP_HT_COMPAT_FIX
#ccflags-y += -DAP_ARP_COMPAT_FIX
ccflags-y += -DENHANCE_ANTI_INTERFERE
#ccflags-y += -DUSE_RSSI_OFFSET
ccflags-y += -DSCAN_FAILED_WORKAROUND_OF_FW_EXCEPTION
ccflags-y += -DHW_RESTART
ccflags-y += -DHW_ERROR_WIFI_RESET
## Use semaphore to sync bh txrx.
#ccflags-y += -DBH_USE_SEMAPHORE
ccflags-y += -DBH_PROC_THREAD
ccflags-y += -DBH_COMINGRX_FORECAST
# Modified for power save.
#ccflags-y += -DXRADIO_USE_LONG_DTIM_PERIOD
ccflags-y += -DXRADIO_USE_LONG_KEEP_ALIVE_PERIOD
## Extra IE for probe response from upper layer is needed in P2P GO
## For offloading probe response to FW, the extra IE must be included
## in the probe response template
ccflags-y += -DPROBE_RESP_EXTRA_IE
ccflags-y += -DIPV6_FILTERING
## Modified for P2P stability.
ccflags-y += -DTES_P2P_0002_ROC_RESTART
ccflags-y += -DTES_P2P_000B_EXTEND_INACTIVITY_CNT
ccflags-y += -DTES_P2P_000B_DISABLE_EAPOL_FILTER
## for chips.
ccflags-y += -DSUPPORT_HT40
ccflags-y += -DSUPPORT_EPTA
ccflags-y += -DSUPPORT_DPLL_CHECK
#ccflags-y += -DSUPPORT_NON_HT40_CHIP
ccflags-y += -DBOOT_NOT_READY_FIX
## for interal debug.
ccflags-y += -DSUPPORT_FW_DBG_INF
#ccflags-y += -DERROR_HANG_DRIVER
lichee\linux-3.10\drivers\net\wireless\xr829\wlan\Kconfig 添加:
config XRADIO_SUSPEND_POWER_OFF
bool "Wlan power off in suspend"
depends on XRADIO && PM
default n
---help---
Say Y if you want to wlan power off in suspend,
and then WoW will be disable.
If unsure, say N.
config XRADIO_EXTEND_SUSPEND //添加选项
bool "Support extend suspend(both Wow and power off)"
depends on XRADIO && PM
select XRADIO_SUSPEND_POWER_OFF
default n
---help---
Say Y if you want to support extend supend, but
extend suspend need to be support on your platform.
If unsure, say N.
config XRADIO_NOMAL_SUSPEND_FORCE
........................................
修改lichee\linux-3.10\drivers\net\wireless\xr829\wlan\platform.c 添加模块的上电时序
/*
extern void xradio_wlan_set_power(int on);
extern int xradio_wlan_get_bus_index(void);
extern int xradio_wlan_get_oob_irq(void);
extern int xradio_wlan_get_oob_irq_flags(void);
*/
//add start
extern void sunxi_wlan_set_module(int flag);
extern void sunxi_wlan_set_power(bool on);
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 void sunxi_wlan_set_enable(int on);
extern void sunxi_mmc_remove_sdio(unsigned ids);
//add end
static int wlan_bus_id = 0;
static u32 gpio_irq_handle = 0;
int xradio_get_syscfg(void)
{
int wlan_bus_index = 0;
//wlan_bus_index = xradio_wlan_get_bus_index();
wlan_bus_index = sunxi_wlan_get_bus_index();
if(wlan_bus_index < 0)
return wlan_bus_index;
else
wlan_bus_id = wlan_bus_index;
//gpio_irq_handle = xradio_wlan_get_oob_irq();
gpio_irq_handle = sunxi_wlan_get_oob_irq();
return wlan_bus_index;
}
/*********************Interfaces called by xradio core. *********************/
int xradio_plat_init(void)
{
return 0;
}
void xradio_plat_deinit(void)
{
;
}
//chanage
int xradio_wlan_power(int on)
{
int ret = 0;
if(on){
ret = xradio_get_syscfg();
if(ret < 0)
return ret;
}
sunxi_wlan_set_module(1);
#if 1
if (on) {
//sunxi_wlan_set_power(on);
//mdelay(100);
sunxi_wlan_set_enable(on);
mdelay(200);
} else {
sunxi_wlan_set_enable(on);
//sunxi_wlan_set_power(on);
mdelay(200);
}
#endif
return ret;
}
//end chanage
void xradio_sdio_detect(int enable)
{
if (0 == enable) { //add start
xradio_dbg(XRADIO_DBG_ALWY, "%s SDIO card %d\n",
enable?"Detect":"Remove", wlan_bus_id);
sunxi_mmc_remove_sdio(wlan_bus_id);
mdelay(10);
return;
}
//add end
MCI_RESCAN_CARD(wlan_bus_id);
xradio_dbg(XRADIO_DBG_ALWY, "%s SDIO card %d\n",
enable?"Detect":"Remove", wlan_bus_id);
mdelay(10);
}
修改XR819模块:
lichee\linux-3.10\drivers\net\wireless\xradio\wlan\platform.c
/* Select hardware platform.*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
extern void sunxi_mmc_rescan_card(unsigned ids);
#define MCI_RESCAN_CARD(id) sunxi_mmc_rescan_card(id)
#ifdef CONFIG_XR_829
extern void sunxi_wlan_set_module(int flag);
#endif
extern void sunxi_wlan_set_power(int on);
extern void sunxi_wlan_set_enable(int on);
extern int sunxi_wlan_get_bus_index(void);
extern int sunxi_wlan_get_oob_irq(void);
extern void sunxi_mmc_remove_sdio(unsigned ids);
/*extern int sunxi_wlan_get_oob_irq_flags(void);*/
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
#define PLAT_ALLWINNER_SUNXI
extern void sunxi_mci_rescan_card(unsigned id, unsigned insert);
#define MCI_RESCAN_CARD(id, ins) sunxi_mci_rescan_card(id, ins)
#else
#define PLAT_ALLWINNER_SUN6I
extern void sw_mci_rescan_card(unsigned id, unsigned insert);
#define MCI_RESCAN_CARD(id, ins) sw_mci_rescan_card(id, ins)
#endif
static int wlan_bus_id = 0;
static u32 gpio_irq_handle = 0;
static int xradio_get_syscfg(void)
{
int wlan_bus_index = 0;
wlan_bus_index = sunxi_wlan_get_bus_index();
if(wlan_bus_index < 0)
return wlan_bus_index;
else
wlan_bus_id = wlan_bus_index;
gpio_irq_handle = sunxi_wlan_get_oob_irq();
return wlan_bus_index;
}
#if 0
static int h64_wlan_soc_power(int on)
{
struct regulator *ldo = NULL;
int ret = 0;
if (on) {
//wifi vcc 1.8v .
ldo = regulator_get(NULL, "axp81x_dldo2");
if (ldo) {
regulator_set_voltage(ldo,1800000, 1800000);
ret = regulator_enable(ldo);
if(ret < 0){
}
regulator_put(ldo);
}
}
else{
//wifi vcc 1.8v
ldo = regulator_get(NULL, "axp81x_dldo2");
if (ldo) {
ret = regulator_disable(ldo);
regulator_put(ldo);
}
}
return ret;
}
#endif
/*********************Interfaces called by xradio core. *********************/
int xradio_plat_init(void)
{
return 0;
}
void xradio_plat_deinit(void)
{
}
int xradio_wlan_power(int on)
{
int ret = 0;
if(on){
ret = xradio_get_syscfg();
if(ret < 0)
return ret;
}
#ifdef CONFIG_XR_829
sunxi_wlan_set_module(0);
#endif
if (on) {
sunxi_wlan_set_power(on);
mdelay(10);
sunxi_wlan_set_enable(on);
mdelay(200);
} else {
sunxi_wlan_set_enable(on);
sunxi_wlan_set_power(on);
mdelay(200);
}
return ret;
}
void xradio_sdio_detect(int enable)
{
if (0 == enable) {
xradio_dbg(XRADIO_DBG_ALWY, "%s SDIO card %d\n",
enable?"Detect":"Remove", wlan_bus_id);
sunxi_mmc_remove_sdio(wlan_bus_id);
mdelay(10);
return;
}
MCI_RESCAN_CARD(wlan_bus_id);
xradio_dbg(XRADIO_DBG_ALWY, "%s SDIO card %d\n",
enable?"Detect":"Remove", wlan_bus_id);
mdelay(10);
}
修改SDIO总线挂载部分:
lichee\linux-3.10\drivers\mmc\core\sdio_bus.c(CONFIG_XR_829部分就是添加的部分)
#ifdef CONFIG_XR_829
#define SDIO_VENDOR_ID_XRADIO 0x0020
#define SDIO_DEVICE_ID_XRADIO 0x2281
#define SDIO_VENDOR_ID_XR829 0x0A9E
#define SDIO_DEVICE_ID_XR829 0x2282
extern void sunxi_wlan_set_module(int flag);
#endif
static int
sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct sdio_func *func = dev_to_sdio_func(dev);
#ifdef CONFIG_XR_829
if(func->vendor == SDIO_VENDOR_ID_XRADIO && func->device == SDIO_DEVICE_ID_XRADIO) //xradio
{
sunxi_wlan_set_module(0);
}
else if(func->vendor == SDIO_VENDOR_ID_XR829 && func->device == SDIO_DEVICE_ID_XR829)
{
sunxi_wlan_set_module(1);
}
#endif
if (add_uevent_var(env,
"SDIO_CLASS=%02X", func->class))
return -ENOMEM;
#ifdef CONFIG_XR_829
printk("nwd-wangtao SDIO_ID=%04X:%04X\n", func->vendor, func->device);
#endif
if (add_uevent_var(env,
"SDIO_ID=%04X:%04X", func->vendor, func->device))
return -ENOMEM;
if (add_uevent_var(env,
"MODALIAS=sdio:c%02Xv%04Xd%04X",
func->class, func->vendor, func->device))
return -ENOMEM;
return 0;
}
内核配置部分:
lichee\linux-3.10\arch\arm\configs\sun8iw11p1_android_x217_defconfig
# CONFIG_XR_WLAN is not set
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
CONFIG_XRADIO_USE_EXTENSIONS=y
# CONFIG_XRADIO_SUSPEND_POWER_OFF is not set
# CONFIG_XRADIO_EXTEND_SUSPEND is not set
# CONFIG_XRADIO_NOMAL_SUSPEND_FORCE 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_XR_829=m
# CONFIG_XRADIO_NOMAL_SUSPEND_FORCE is not set
即编译XR829模块:
CONFIG_XR_829=m
CONFIG_XR_WLAN is not set
即编译XR819模块:
CONFIG_XR_WLAN=m
CONFIG_XR_829 is not set
二、Android os部分的配置
1、hardware层的修改:
android\hardware\libhardware_legacy\wifi_hardware_info\wifi_hardware_info.c
static const struct wifi_hardware_info wifi_list[] = {
..............................................................
+ {
+ .device_id = 0x12282,
+ .module_name = "xr829",
+ .driver_name = "xr829_wlan",
+ .vendor_name = "xradio",
+ .fw_path_sta = "STA",
+ .fw_path_ap = "AP",
+ .fw_path_p2p = "P2P",
+ },
};
static void parse_uevent(char *msg)
{
char sdio_device_id[10] = {0};
char device_type[10] = {0};
char *subsystem = "";
char *sdio_id = "";
char *usb_product = "";
unsigned long device_id = 0;
while(*msg) {
if(!strncmp(msg, "SUBSYSTEM=", 10)) {
msg += 10;
subsystem = msg;
} else if(!strncmp(msg, "SDIO_ID=", 8)) {
msg += 8;
sdio_id = msg;
} else if(!strncmp(msg, "PRODUCT=", 8)) {
msg += 8;
usb_product = msg;
}
/* advance to after the next \0 */
while(*msg++) {
/* do nothing */
}
}
if(!strncmp(subsystem, "sdio", 4)) {
ALOGI("get uevent, sdio_id = %s", sdio_id);
strcpy(device_type, "sdio");
char *subid = strrchr(sdio_id, ':');
if(subid == NULL) {
return;
}
subid++;
strcpy(sdio_device_id, subid);
device_id = strtoul(sdio_device_id, NULL, 16);
device_id += 0x10000;
} else if(!strncmp(subsystem, "usb", 3)) {
strcpy(device_type, "usb");
char *subid = NULL;
strtok(usb_product, "/");
subid = strtok( NULL, "/");
if(subid == NULL) {
return;
}
device_id = strtoul(subid, NULL, 16);
} else {
return;
}
if(!get_hardware_info_by_device_id(device_id)) {
FILE *file = fopen(WIFI_HARDWARE_INFO_PATH, "w");
char info[PROPERTY_VALUE_MAX] = {0};
snprintf(info, sizeof(info), "%s:%s", selected_wifi.vendor_name, selected_wifi.module_name);
if(file == NULL){
ALOGE("cannot open file %s to write", WIFI_HARDWARE_INFO_PATH);
return;
}
ALOGD("write wifi_hardware_info into file: %s:%s", selected_wifi.vendor_name,
selected_wifi.module_name);
//add by zhaojr
if(!strcmp(selected_wifi.module_name, "xr829"))
property_set("persist.sys.bluetooth_goc", "1");
else if(!strcmp(selected_wifi.module_name, "xr819"))
property_set("persist.sys.goc", "1");
//end add
fprintf(file,"%s\n", info);
fflush(file);
fsync(fileno(file));
fclose(file);
property_set("wlan.hardware.info", info);
thread_state = exiting;
}
}
将xr829的固件目录拷贝到android\hardware\broadcom\wlan\bcmdhd\firmware\xr829目录,编写
android\hardware\broadcom\wlan\bcmdhd\firmware\xr829\device-xr829.mk文件,内容如下:
android\hardware\broadcom\wlan\bcmdhd\firmware\xr829\device-xr829.mk
########################
PRODUCT_COPY_FILES += \
hardware/broadcom/wlan/bcmdhd/firmware/xr829/boot_xr829.bin:system/etc/firmware/boot_xr829.bin \
hardware/broadcom/wlan/bcmdhd/firmware/xr829/fw_xr829.bin:system/etc/firmware/fw_xr829.bin \
hardware/broadcom/wlan/bcmdhd/firmware/xr829/sdd_xr829.bin:system/etc/firmware/sdd_xr829.bin \
hardware/broadcom/wlan/bcmdhd/firmware/xr829/etf_xr829.bin:system/etc/firmware/etf_xr829.bin \
hardware/broadcom/wlan/bcmdhd/firmware/xr829/fw_xr829_bt.bin:system/etc/firmware/fw_xr829_bt.bin \
hardware/broadcom/wlan/bcmdhd/firmware/xr829/fw_xr829_bt_sco_hci_uart.bin:system/etc/firmware/fw_xr829_bt_sco_hci_uart.bin
########################
将以上文件添加到:
android\hardware\broadcom\wlan\bcmdhd\firmware\firmware-bcm.mk
#include hardware/broadcom/wlan/bcmdhd/firmware/ap6210/device-bcm.mk
#include hardware/broadcom/wlan/bcmdhd/firmware/ap6330/device-bcm.mk
#include hardware/broadcom/wlan/bcmdhd/firmware/ap6335/device-bcm.mk
#include hardware/broadcom/wlan/bcmdhd/firmware/ap6212/device-bcm.mk
#include hardware/broadcom/wlan/bcmdhd/firmware/ssv6051/device-esp.mk
#include hardware/broadcom/wlan/bcmdhd/firmware/rtl8189fs/device-rtw.mk
include hardware/broadcom/wlan/bcmdhd/firmware/xr819/device-xradio.mk
include hardware/broadcom/wlan/bcmdhd/firmware/xr829/device-xr829.mk //添加
2、平台配置:
android\device\softwinner\x217\BoardConfig.mk
# wifi and bt configuration
# 1. wifi Configuration
WPA_SUPPLICANT_VERSION := VER_0_8_X
BOARD_WPA_SUPPLICANT_DRIVER := NL80211
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd
BOARD_HOSTAPD_DRIVER := NL80211
BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd
include hardware/broadcom/wlan/bcmdhd/firmware/firmware-bcm.mk
include hardware/broadcom/wlan/bcmdhd/config/config-bcm.mk
android\device\softwinner\x217\init.rc
添加服务:
service dhcpcd_lte0 /system/bin/dhcpcd -ABKL
class main
disabled
oneshot
service iprenew_lte0 /system/bin/dhcpcd -n
class main
disabled
oneshot
..............................................
# wifi service
# 1 wifi station and softap
service wpa_supplicant /system/bin/logwrapper /system/bin/wpa_supplicant \
p2p_supported=false
class main
socket wpa_wlan0 dgram 660 wifi wifi
disabled
oneshot
# 2 wifi sta p2p concurrent service
service p2p_supplicant /system/bin/logwrapper /system/bin/wpa_supplicant \
p2p_supported=true
class main
socket wpa_wlan0 dgram 660 wifi wifi
disabled
oneshot
#nwd patch, added by ww, 2016.1.17
#wenqiang bluetooch
service gocsdk /system/bin/gocsdk /dev/ttyS1 500000
class main
user root
group root
disabled
service gocsdk_xr829 /system/bin/gocsdk_xr829 /dev/ttyS1 1500000
class main
user root
group root
disabled
on property:persist.sys.goc=1
insmod /system/vendor/modules/xradio_mac.ko
insmod /system/vendor/modules/xradio_core.ko
start gocsdk
on property:persist.sys.bluetooth_goc=1
insmod /system/vendor/modules/xr829_mac.ko
insmod /system/vendor/modules/xr829_core.ko
start gocsdk_xr829
android\device\softwinner\x217\ueventd.sun8iw11p1.rc
...................................................
/dev/snd/pcmC0D0c 0770 media media
/dev/snd/pcmC0D0p 0770 media media
......................................................
/dev/ttyS1 0777 system system
..................................................
/dev/audio 0666 system system
.......................................................
android\device\softwinner\t3-common\t3-common.mk
#open wireless debugging feature
PRODUCT_PROPERTY_OVERRIDES += \
service.adb.tcp.port=5555 \
ro.adb.secure=0 \
persist.service.adb.enable=1 \
ro.debuggable=1 \
persist.sys.i2s=0 \
persist.sys.camera.debug=0 \
persist.sys.nwd.ethernet.ctrl=0 \
sys.nwd.tve.enable=1 \
persist.sys.bluetooth_goc=1 \ ------->//加载XR829模块
persist.bt.sco.uart=true \ ----------->蓝牙电话和蓝牙音乐走蓝牙模块的串口,不走PCM
ro.single.double.mic=0
最后,全新编译整个系统,编译之后,将协议栈厂家给的gocsdk_xr829放到系统中的/system/bin/目录中,如下:
adb push gocsdk_xr829 /system/bin/
adb shell
chmod 777 /system/bin/gocsdk_xr829
sync
之后重启系统,运行起来之后打开WIFI,即可连接到路由器,之后打开蓝牙电话,手机连接之后,到蓝牙音乐界面,手机播放音乐OK之后,手机打电话测试,经过测试是OK的。