8dd79199f8
The bes2600 driver is a fork of the upstream cw1200 driver
(drivers/net/wireless/st/cw1200/, ST-Ericsson, Dmitry Tarnyagin
2010-2011). The fork's file headers have three GPL-compliance issues:
1. NO SPDX-License-Identifier on any of 48 source files (cw1200
mainline has them on all 25). kernel.org-mandated since 2017.
2. Original "Copyright (c) 2010, ST-Ericsson" lines stripped from
all files inherited from cw1200, replaced with
"Copyright (c) 2010, Bestechnic" — factually impossible
(Bestechnic did not author the 2010 work) and a GPL-2.0 §1
attribution-preservation violation.
3. The "GPL version 2 as published by the Free Software Foundation"
boilerplate paragraph is redundant alongside SPDX and is the
legacy form modern kernel sources have replaced.
This patch corrects all three for the 48 .c/.h files in bes2600/:
- Adds `// SPDX-License-Identifier: GPL-2.0-only` (or `/* ... */`
for headers) as line 1 of every file.
- Restores `Copyright (c) 2010, ST-Ericsson` + `Author: Dmitry
Tarnyagin <dmitry.tarnyagin@lockless.no>` as the FIRST copyright
chain entry on all 22 files derived from cw1200 (bh.{c,h},
debug.{c,h}, fwio.{c,h}, hwio.{c,h}, main.c, pm.{c,h},
queue.{c,h}, scan.{c,h}, sta.{c,h}, txrx.{c,h}, wsm.{c,h}).
- Keeps `Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.` as
the SECOND chain entry where Bestechnic genuinely contributed.
- Notes "Derived from cw1200_sdio.c" + ST-Ericsson copyright on
bes2600_sdio.c (heavy derivation, not a literal rename).
- Notes "Replaces hwbus.h from cw1200/" + ST-Ericsson copyright
on sbus.h.
- Preserves the prism54/islsm authorship chain on main.c and
bes2600.h (Michael Wu 2006 + Jean-Baptiste Note 2004-2006).
- Drops the GPL-2.0 boilerplate paragraph in favour of SPDX.
No code changes — only file-header comment blocks. Module build is
unaffected (verified by header-only diff scope).
This is a prerequisite for any kernel.org submission attempt. The
existing MODULE_LICENSE("GPL") + MODULE_AUTHOR(Tarnyagin@stericsson.com)
declarations were already present and are unchanged here; the
mismatch between MODULE_AUTHOR and the (since-corrected) per-file
copyrights is now resolved.
747 lines
19 KiB
C
747 lines
19 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Character device for BES2600 mac80211 driver
|
|
*
|
|
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
|
|
*
|
|
*/
|
|
#include<linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/random.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/crc32.h>
|
|
#include <linux/version.h>
|
|
#include "bes2600.h"
|
|
#include "sbus.h"
|
|
#include "hwio.h"
|
|
#include "fwio.h"
|
|
#include "bes_chardev.h"
|
|
#include "tx_loop.h"
|
|
#include "bes_log.h"
|
|
|
|
enum wait_state {
|
|
BES2600_BOOT_WAIT_NONE = 0,
|
|
BES2600_BOOT_WAIT_PROBE_DONE,
|
|
BES2600_BOOT_WAIT_CLOSE,
|
|
};
|
|
|
|
enum bus_probe_state {
|
|
BES2600_BUS_PROBE_NONE = 0,
|
|
BES2600_BUS_PROBE_START,
|
|
BES2600_BUS_PROBE_OK,
|
|
BES2600_BUS_PROBE_TIMEOUT,
|
|
};
|
|
|
|
struct bes_cdev {
|
|
atomic_t num_proc;
|
|
wait_queue_head_t open_wq;
|
|
spinlock_t status_lock;
|
|
bool wifi_opened;
|
|
bool bt_opened;
|
|
bool bton_pending;
|
|
bool dpd_calied;
|
|
u8 *dpd_data;
|
|
u32 dpd_len;
|
|
enum wait_state wait_state;
|
|
wait_queue_head_t probe_done_wq;
|
|
const struct sbus_ops *sbus_ops;
|
|
struct sbus_priv *sbus_priv;
|
|
bool sig_mode;
|
|
int fw_type;
|
|
bool bus_error;
|
|
bool halt_dev;
|
|
struct delayed_work probe_timeout_work;
|
|
enum bus_probe_state bus_probe;
|
|
struct work_struct wifi_force_close_work;
|
|
enum pend_read_op read_flag;
|
|
enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */
|
|
u16 wakeup_state; /* for userspace check wakeup reason */
|
|
wait_queue_head_t wakeup_reason_wq;
|
|
u16 src_port;
|
|
#ifdef BES2600_DUMP_FW_DPD_LOG
|
|
u8 *dpd_log;
|
|
u16 dpd_log_len;
|
|
#endif
|
|
};
|
|
|
|
struct bes2600_op_map {
|
|
char op[20]; // operation
|
|
int op_len; // operation length, used for effiency
|
|
int (*handler)(const char *cmd); // handler
|
|
};
|
|
|
|
static struct bes_cdev bes2600_cdev;
|
|
module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644);
|
|
|
|
extern int bes2600_register_net_dev(struct sbus_priv *bus_priv);
|
|
extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
|
|
extern bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv);
|
|
|
|
static bool bes2600_bootup_end(void)
|
|
{
|
|
bool end;
|
|
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
end = (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_TIMEOUT ||
|
|
bes2600_cdev.sbus_priv != NULL ||
|
|
bes2600_cdev.bus_error);
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
return end;
|
|
}
|
|
|
|
static int bes2600_chrdev_switch_subsys(int wake_flag, int subsys, bool active)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (bes2600_cdev.sbus_priv == NULL)
|
|
return -EFAULT;
|
|
|
|
if (active) {
|
|
if (bes2600_cdev.sbus_ops->gpio_wake)
|
|
bes2600_cdev.sbus_ops->gpio_wake(bes2600_cdev.sbus_priv, wake_flag);
|
|
|
|
if (bes2600_cdev.sbus_ops->sbus_active)
|
|
ret = bes2600_cdev.sbus_ops->sbus_active(bes2600_cdev.sbus_priv, subsys);
|
|
|
|
if (bes2600_cdev.sbus_ops->gpio_sleep)
|
|
bes2600_cdev.sbus_ops->gpio_sleep(bes2600_cdev.sbus_priv, wake_flag);
|
|
} else {
|
|
if (bes2600_cdev.sbus_ops->gpio_wake)
|
|
bes2600_cdev.sbus_ops->gpio_wake(bes2600_cdev.sbus_priv, wake_flag);
|
|
|
|
if (bes2600_cdev.sbus_ops->sbus_deactive)
|
|
ret = bes2600_cdev.sbus_ops->sbus_deactive(bes2600_cdev.sbus_priv, subsys);
|
|
|
|
if (bes2600_cdev.sbus_ops->gpio_sleep)
|
|
bes2600_cdev.sbus_ops->gpio_sleep(bes2600_cdev.sbus_priv, wake_flag);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int bes2600_switch_wifi(bool on)
|
|
{
|
|
int ret = 0;
|
|
long status = 0;
|
|
|
|
if (bes2600_cdev.wifi_opened == on)
|
|
return 0;
|
|
|
|
if (on) {
|
|
if (bes2600_chrdev_check_system_close()) {
|
|
bes_devel("power up bes2600 when active wifi.\n");
|
|
/* reset bus error status when restart bes2600 */
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.bus_error = false;
|
|
bes2600_cdev.halt_dev = false;
|
|
bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
/* power up bes2600, trigger system to execute probe function */
|
|
bes2600_cdev.wifi_opened = true;
|
|
bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
|
|
|
/* wait probe done event */
|
|
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
|
bes2600_bootup_end(), HZ * 8);
|
|
WARN_ON(status <= 0);
|
|
ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
|
} else {
|
|
/* bes2600 is already powered up, we just need to create net device */
|
|
if (bes2600_cdev.sbus_priv) {
|
|
if (!bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
|
ret = bes2600_register_net_dev(bes2600_cdev.sbus_priv);
|
|
}
|
|
} else {
|
|
ret = -EFAULT;
|
|
}
|
|
}
|
|
} else {
|
|
if (bes2600_cdev.sbus_priv && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
|
bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
|
|
}
|
|
}
|
|
|
|
if (!ret) {
|
|
bes2600_cdev.wifi_opened = on;
|
|
} else {
|
|
bes2600_cdev.wifi_opened = false;
|
|
if (on)
|
|
bes_info("open wifi failed\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_switch_bt(bool on)
|
|
{
|
|
int ret = 0;
|
|
long status = 0;
|
|
|
|
if (bes2600_cdev.bt_opened == on)
|
|
return 0;
|
|
|
|
if (on) {
|
|
if (bes2600_chrdev_check_system_close()) {
|
|
bes_devel("power up bes2600 when active bt.\n");
|
|
/* reset bus error status when restart bes2600 */
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.bus_error = false;
|
|
bes2600_cdev.halt_dev = false;
|
|
bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
/* set opend state in advance */
|
|
bes2600_cdev.bt_opened = true;
|
|
bes2600_cdev.bton_pending = true;
|
|
|
|
/* power up bes2600, trigger system to execute probe function */
|
|
bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
|
|
|
/* wait bootup process end */
|
|
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
|
bes2600_bootup_end(), HZ * 8);
|
|
WARN_ON(status <= 0);
|
|
|
|
/* check if there is a error when bootup */
|
|
ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
|
} else {
|
|
bes_info("enable BT\n");
|
|
ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true);
|
|
}
|
|
} else {
|
|
bes_info("disable BT\n");
|
|
bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_OFF, SUBSYSTEM_BT, false);
|
|
}
|
|
|
|
if (!ret) {
|
|
bes2600_cdev.bt_opened = on;
|
|
} else {
|
|
bes2600_cdev.bt_opened = false;
|
|
bes2600_cdev.bton_pending = false;
|
|
if (ret)
|
|
bes_info("open bt failed\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef FW_DOWNLOAD_UART_DAEMON
|
|
#endif
|
|
|
|
|
|
static int bes2600_chrdev_check_system_close_internal(void)
|
|
{
|
|
return (bes2600_cdev.sbus_ops->power_switch != NULL &&
|
|
bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL)
|
|
&&(bes2600_cdev.bt_opened == false)
|
|
&& (bes2600_cdev.wifi_opened == false);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const u8* bes2600_chrdev_get_dpd_data(u32 *len)
|
|
{
|
|
if (!bes2600_cdev.dpd_calied)
|
|
return NULL;
|
|
if (len)
|
|
*len = bes2600_cdev.dpd_len;
|
|
|
|
return bes2600_cdev.dpd_data;
|
|
}
|
|
|
|
u8* bes2600_chrdev_get_dpd_buffer(u32 size)
|
|
{
|
|
if (bes2600_cdev.dpd_data)
|
|
kfree(bes2600_cdev.dpd_data);
|
|
|
|
bes2600_cdev.dpd_data = kmalloc(size, GFP_KERNEL);
|
|
if (!bes2600_cdev.dpd_data) {
|
|
return NULL;
|
|
}
|
|
|
|
bes2600_cdev.dpd_len = DPD_BIN_SIZE;
|
|
|
|
return bes2600_cdev.dpd_data;
|
|
}
|
|
|
|
void bes2600_chrdev_free_dpd_data(void)
|
|
{
|
|
if (bes2600_cdev.dpd_data)
|
|
kfree(bes2600_cdev.dpd_data);
|
|
|
|
bes2600_cdev.dpd_data = NULL;
|
|
bes2600_cdev.dpd_len = 0;
|
|
}
|
|
|
|
int bes2600_chrdev_update_dpd_data(void)
|
|
{
|
|
u32 cal_crc = 0;
|
|
u32 dpd_crc = le32_to_cpup((__le32 *)(bes2600_cdev.dpd_data));
|
|
|
|
/* check if the dpd data is valid */
|
|
cal_crc ^= 0xffffffffL;
|
|
cal_crc = crc32_le(cal_crc, bes2600_cdev.dpd_data + 4, bes2600_cdev.dpd_len - 4);
|
|
cal_crc ^= 0xffffffffL;
|
|
if (cal_crc != dpd_crc) {
|
|
bes_err(
|
|
"bes2600 dpd data check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
|
|
cal_crc, dpd_crc);
|
|
return -1;
|
|
}
|
|
|
|
bes_devel("bes2600 dpd cali pass.\n");
|
|
|
|
/* update dpd calibration and wait state */
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.dpd_calied = true;
|
|
if (bes2600_chrdev_check_system_close_internal()) {
|
|
bes2600_cdev.wait_state = BES2600_BOOT_WAIT_CLOSE;
|
|
} else {
|
|
bes2600_cdev.wait_state = BES2600_BOOT_WAIT_PROBE_DONE;
|
|
}
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef BES2600_DUMP_FW_DPD_LOG
|
|
void bes2600_free_dpd_log_buffer(void)
|
|
{
|
|
if (bes2600_cdev.dpd_log)
|
|
kfree(bes2600_cdev.dpd_log);
|
|
|
|
bes2600_cdev.dpd_log_len = 0;
|
|
bes2600_cdev.dpd_log = NULL;
|
|
}
|
|
|
|
u8* bes2600_alloc_dpd_log_buffer(u16 len)
|
|
{
|
|
bes2600_cdev.dpd_log_len = len;
|
|
|
|
if (bes2600_cdev.dpd_log)
|
|
kfree(bes2600_cdev.dpd_log);
|
|
|
|
bes2600_cdev.dpd_log = kzalloc(len, GFP_KERNEL);
|
|
if (!bes2600_cdev.dpd_log) {
|
|
bes2600_cdev.dpd_log_len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
return bes2600_cdev.dpd_log;
|
|
}
|
|
|
|
void bes2600_get_dpd_log(char **data, size_t *len)
|
|
{
|
|
if (!bes2600_cdev.dpd_log) {
|
|
*data = NULL;
|
|
*len = 0;
|
|
} else {
|
|
*data = bes2600_cdev.dpd_log;
|
|
*len = (size_t)bes2600_cdev.dpd_log_len;
|
|
}
|
|
}
|
|
#endif /* BES2600_DUMP_FW_DPD_LOG */
|
|
|
|
void bes2600_chrdev_set_sbus_priv_data(struct sbus_priv *priv, bool error)
|
|
{
|
|
bes2600_cdev.sbus_priv = priv;
|
|
if (priv) {
|
|
if (bes2600_cdev.bton_pending) {
|
|
bes_devel("execute pending bt on operation.\n");
|
|
bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true);
|
|
|
|
bes2600_cdev.bton_pending = false;
|
|
}
|
|
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
if (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_PROBE_DONE) {
|
|
bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE;
|
|
}
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
bes_devel("wakup proc on wq of probe_done.\n");
|
|
} else {
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE;
|
|
bes2600_cdev.bus_error = error;
|
|
if (bes2600_cdev.bus_error) {
|
|
bes2600_cdev.wifi_opened = false;
|
|
bes2600_cdev.bt_opened = false;
|
|
bes2600_cdev.bton_pending = false;
|
|
bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE;
|
|
}
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
bes_devel("wakup proc on wq of disconnect_done.\n");
|
|
}
|
|
|
|
wake_up(&bes2600_cdev.probe_done_wq);
|
|
}
|
|
|
|
struct sbus_priv * bes2600_chrdev_get_sbus_priv_data(void)
|
|
{
|
|
return bes2600_cdev.sbus_priv;
|
|
}
|
|
|
|
int bes2600_chrdev_check_system_close(void)
|
|
{
|
|
bool sys_closed = false;
|
|
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
sys_closed = bes2600_chrdev_check_system_close_internal();
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
return sys_closed;
|
|
}
|
|
|
|
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
|
|
{
|
|
int ret = 0;
|
|
long status = 0;
|
|
|
|
if (!sbus_ops || !priv) {
|
|
bes_warn("abort power down device.\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!sbus_ops->power_switch)
|
|
return 0;
|
|
|
|
bes_devel("power down bes2600.\n");
|
|
|
|
/* trigger system to execute disconnect function */
|
|
ret = sbus_ops->power_switch(priv, 0);
|
|
|
|
/* wait disconnect event */
|
|
status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 3);
|
|
WARN_ON(status <= 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Hard-reset the bus and wait for the bus core to remove the chip.
|
|
*
|
|
* Used by the firmware-wedge recovery path on platforms where the normal
|
|
* power_switch(0) sequence has no effective chip-reset signal. The bus
|
|
* implementation triggers an asynchronous re-detect; this helper waits for
|
|
* the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a
|
|
* subsequent bes2600_switch_wifi(true) sees a clean state and can wait on
|
|
* the fresh probe.
|
|
*/
|
|
int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
|
|
{
|
|
int ret;
|
|
long status;
|
|
|
|
if (!sbus_ops || !priv)
|
|
return -EINVAL;
|
|
|
|
if (!sbus_ops->bus_reset)
|
|
return -EOPNOTSUPP;
|
|
|
|
bes_info("trigger bus reset to recover wedged firmware.\n");
|
|
|
|
ret = sbus_ops->bus_reset(priv);
|
|
if (ret) {
|
|
bes_err("bus_reset failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* The bus reset is asynchronous: the bus core schedules a rescan
|
|
* which removes the bound function drivers and then re-detects the
|
|
* chip. Wait for the remove callback to clear sbus_priv. Do not
|
|
* dereference 'priv' after this point -- it may already be freed.
|
|
*/
|
|
status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
|
!bes2600_cdev.sbus_priv, HZ * 3);
|
|
WARN_ON(status <= 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Trigger bes2600_chrdev_do_bus_reset() against the file-global
|
|
* bes2600_cdev. Used by host-side recovery paths outside this
|
|
* compilation unit (e.g. sta.c connection-loss-storm fast-recover) so
|
|
* those callers do not need to reach the static bes2600_cdev directly.
|
|
*/
|
|
int bes2600_chrdev_trigger_bus_reset(void)
|
|
{
|
|
return bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
|
|
bes2600_cdev.sbus_priv);
|
|
}
|
|
|
|
bool bes2600_chrdev_is_wifi_opened(void)
|
|
{
|
|
bool wifi_opened = false;
|
|
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
wifi_opened = bes2600_cdev.wifi_opened;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
if (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL)
|
|
return true;
|
|
else if (bes2600_cdev.fw_type == BES2600_FW_TYPE_BT)
|
|
return false;
|
|
else if (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL)
|
|
return wifi_opened;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool bes2600_chrdev_is_bt_opened(void)
|
|
{
|
|
bool bt_opened = false;
|
|
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bt_opened = bes2600_cdev.bt_opened;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
if (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL)
|
|
return false;
|
|
else if (bes2600_cdev.fw_type == BES2600_FW_TYPE_BT)
|
|
return true;
|
|
else if (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL)
|
|
return bt_opened;
|
|
|
|
return false;
|
|
}
|
|
|
|
void bes2600_chrdev_wakeup_bt(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (bes2600_cdev.bt_opened && bes2600_cdev.sbus_priv) {
|
|
bes_devel("wakeup bt in resume flow\n");
|
|
ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
|
|
|
|
if (ret)
|
|
bes_err("Wakeup BT fail in resume\n");
|
|
}
|
|
}
|
|
|
|
int bes2600_chrdev_get_fw_type(void)
|
|
{
|
|
return bes2600_cdev.fw_type;
|
|
}
|
|
|
|
bool bes2600_chrdev_is_signal_mode(void)
|
|
{
|
|
return bes2600_cdev.sig_mode;
|
|
}
|
|
|
|
bool bes2600_chrdev_is_bus_error(void)
|
|
{
|
|
bool error = false;
|
|
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
error = (bes2600_cdev.bus_error || bes2600_cdev.bus_probe != BES2600_BUS_PROBE_OK);
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
void bes2600_chrdev_update_signal_mode(void)
|
|
{
|
|
if (bes2600_cdev.fw_type >= BES2600_FW_TYPE_MAX_NUM) {
|
|
bes2600_cdev.fw_type = BES2600_FW_TYPE_WIFI_SIGNAL;
|
|
bes_warn("unexpected fw type, switch to wifi signal mode\n");
|
|
}
|
|
|
|
if (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) {
|
|
bes2600_cdev.sig_mode = true;
|
|
} else if ((bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL)
|
|
|| (bes2600_cdev.fw_type == BES2600_FW_TYPE_BT)) {
|
|
bes2600_cdev.sig_mode = false;
|
|
}
|
|
}
|
|
|
|
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
|
{
|
|
if (bes2600_chrdev_is_wifi_opened()) {
|
|
bes_devel("system exeception, force wifi down\n");
|
|
|
|
/* halt device if needed */
|
|
if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->halt_device) {
|
|
bes2600_cdev.sbus_ops->halt_device(bes2600_cdev.sbus_priv);
|
|
}
|
|
|
|
/* unregister wifi */
|
|
bes2600_switch_wifi(0);
|
|
|
|
/*
|
|
* Hard exception with a bus_reset implementation: tear the
|
|
* bus down via mmc_hw_reset() (or equivalent) so the next
|
|
* bringup probes a freshly reset chip. On PineTab2 this is
|
|
* the only effective recovery path -- the existing
|
|
* power_switch(0)/(1) sequence has no chip-reset signal of
|
|
* its own (sdio_pwrseq owns wifi_reset).
|
|
*
|
|
* Soft close, or hard close on a board without bus_reset:
|
|
* fall back to the legacy power_switch(0) sequence.
|
|
*/
|
|
if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) {
|
|
bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
|
|
bes2600_cdev.sbus_priv);
|
|
} else if (bes2600_chrdev_check_system_close()) {
|
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
|
bes2600_cdev.sbus_priv);
|
|
}
|
|
}
|
|
}
|
|
|
|
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev)
|
|
{
|
|
if (hw_priv == NULL)
|
|
return;
|
|
|
|
if (bes2600_chrdev_is_wifi_opened() &&
|
|
!work_pending(&bes2600_cdev.wifi_force_close_work)) {
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.bus_error = true;
|
|
bes2600_cdev.halt_dev = halt_dev;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
bes2600_tx_loop_set_enable(hw_priv, true);
|
|
schedule_work(&bes2600_cdev.wifi_force_close_work);
|
|
}
|
|
}
|
|
|
|
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv)
|
|
{
|
|
if (hw_priv == NULL)
|
|
return;
|
|
|
|
if (bes2600_chrdev_is_wifi_opened() &&
|
|
!work_pending(&bes2600_cdev.wifi_force_close_work)) {
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.bus_error = true;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
bes2600_tx_loop_set_enable(hw_priv, false);
|
|
bes2600_chrdev_wifi_force_close_work(&bes2600_cdev.wifi_force_close_work);
|
|
}
|
|
}
|
|
|
|
static void bes2600_probe_timeout_work(struct work_struct *work)
|
|
{
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.bus_probe = BES2600_BUS_PROBE_TIMEOUT;
|
|
bes2600_cdev.wifi_opened = false;
|
|
bes2600_cdev.bt_opened = false;
|
|
bes2600_cdev.bton_pending = false;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
bes_devel("bus probe timeout\n");
|
|
wake_up(&bes2600_cdev.probe_done_wq);
|
|
}
|
|
|
|
void bes2600_chrdev_start_bus_probe(void)
|
|
{
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.bus_probe = BES2600_BUS_PROBE_START;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
|
|
cancel_delayed_work_sync(&bes2600_cdev.probe_timeout_work);
|
|
schedule_delayed_work(&bes2600_cdev.probe_timeout_work, 3 * HZ);
|
|
}
|
|
|
|
void bes2600_chrdev_bus_probe_notify(void)
|
|
{
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.bus_probe = BES2600_BUS_PROBE_OK;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
cancel_delayed_work_sync(&bes2600_cdev.probe_timeout_work);
|
|
|
|
wake_up(&bes2600_cdev.probe_done_wq);
|
|
}
|
|
|
|
void bes2600_chrdev_wifi_update_wakeup_reason(u16 reason, u16 port)
|
|
{
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.wakeup_state = reason;
|
|
bes2600_cdev.src_port = port;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
}
|
|
|
|
void bes2600_chrdev_wakeup_by_event_set(enum wakeup_event wakeup_event)
|
|
{
|
|
spin_lock(&bes2600_cdev.status_lock);
|
|
bes2600_cdev.wakeup_by_event = wakeup_event;
|
|
spin_unlock(&bes2600_cdev.status_lock);
|
|
if (wakeup_event == WAKEUP_EVENT_NONE)
|
|
wake_up(&bes2600_cdev.wakeup_reason_wq);
|
|
}
|
|
|
|
int bes2600_chrdev_wakeup_by_event_get(void)
|
|
{
|
|
return bes2600_cdev.wakeup_by_event;
|
|
}
|
|
|
|
int bes2600_chrdev_init(struct sbus_ops *ops)
|
|
{
|
|
/* initialise global variable */
|
|
atomic_set(&bes2600_cdev.num_proc, 0);
|
|
init_waitqueue_head(&bes2600_cdev.open_wq);
|
|
spin_lock_init(&bes2600_cdev.status_lock);
|
|
init_waitqueue_head(&bes2600_cdev.probe_done_wq);
|
|
INIT_WORK(&bes2600_cdev.wifi_force_close_work, bes2600_chrdev_wifi_force_close_work);
|
|
INIT_DELAYED_WORK(&bes2600_cdev.probe_timeout_work, bes2600_probe_timeout_work);
|
|
init_waitqueue_head(&bes2600_cdev.wakeup_reason_wq);
|
|
bes2600_chrdev_wakeup_by_event_set(WAKEUP_EVENT_NONE);
|
|
#ifdef CONFIG_BES2600_WIFI_BOOT_ON
|
|
bes2600_cdev.wifi_opened = true;
|
|
#else
|
|
bes2600_cdev.wifi_opened = false;
|
|
#endif
|
|
#ifdef CONFIG_BES2600_BT_BOOT_ON
|
|
bes2600_cdev.bt_opened = true;
|
|
bes2600_cdev.bton_pending = true;
|
|
#else
|
|
bes2600_cdev.bt_opened = false;
|
|
bes2600_cdev.bton_pending = false;
|
|
#endif
|
|
bes2600_cdev.dpd_calied = false;
|
|
bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE;
|
|
bes2600_cdev.sbus_ops = ops;
|
|
bes2600_cdev.bus_error = false;
|
|
bes2600_cdev.halt_dev = false;
|
|
bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
|
|
bes2600_cdev.wakeup_by_event = WAKEUP_EVENT_NONE;
|
|
bes_devel("%s done\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bes2600_chrdev_free(void)
|
|
{
|
|
cancel_delayed_work_sync(&bes2600_cdev.probe_timeout_work);
|
|
#ifdef BES2600_DUMP_FW_DPD_LOG
|
|
bes2600_free_dpd_log_buffer();
|
|
#endif
|
|
bes2600_chrdev_free_dpd_data();
|
|
bes_devel("%s done\n", __func__);
|
|
}
|