提交 750e9d15 编写于 作者: E Eliad Peller 提交者: Kalle Valo

wl18xx: add radar detection implementation

Add support for CAC start/stop commands, and pass
radar detection events from the fw to mac80211.

Bump fw name (to wl18xx-fw-4.bin) and min fw version
(to 8.9.*.*.11), and align event mailbox accordingly.
Signed-off-by: NGuy Mishol <guym@ti.com>
Signed-off-by: NEliad Peller <eliad@wizery.com>
Signed-off-by: NKalle Valo <kvalo@codeaurora.org>
上级 e2f1e50f
...@@ -167,3 +167,34 @@ int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, ...@@ -167,3 +167,34 @@ int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
out: out:
return ret; return ret;
} }
int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start)
{
struct wlcore_cmd_cac_start *cmd;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd cac (channel %d) %s",
wlvif->channel, start ? "start" : "stop");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->role_id = wlvif->role_id;
cmd->channel = wlvif->channel;
if (wlvif->band == IEEE80211_BAND_5GHZ)
cmd->band = WLCORE_BAND_5GHZ;
cmd->bandwidth = wlcore_get_native_channel_type(wlvif->channel_type);
ret = wl1271_cmd_send(wl,
start ? CMD_CAC_START : CMD_CAC_STOP,
cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send cac command");
goto out_free;
}
out_free:
kfree(cmd);
return ret;
}
...@@ -59,6 +59,16 @@ struct wl18xx_cmd_smart_config_set_group_key { ...@@ -59,6 +59,16 @@ struct wl18xx_cmd_smart_config_set_group_key {
u8 key[16]; u8 key[16];
} __packed; } __packed;
/* cac_start and cac_stop share the same params */
struct wlcore_cmd_cac_start {
struct wl1271_cmd_header header;
u8 role_id;
u8 channel;
u8 band;
u8 bandwidth;
} __packed;
int wl18xx_cmd_channel_switch(struct wl1271 *wl, int wl18xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif, struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch); struct ieee80211_channel_switch *ch_switch);
...@@ -66,4 +76,5 @@ int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap); ...@@ -66,4 +76,5 @@ int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap);
int wl18xx_cmd_smart_config_stop(struct wl1271 *wl); int wl18xx_cmd_smart_config_stop(struct wl1271 *wl);
int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
u8 key_len, u8 *key); u8 key_len, u8 *key);
int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start);
#endif #endif
...@@ -47,6 +47,19 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, ...@@ -47,6 +47,19 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout); return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
} }
static const char *wl18xx_radar_type_decode(u8 radar_type)
{
switch (radar_type) {
case RADAR_TYPE_REGULAR:
return "REGULAR";
case RADAR_TYPE_CHIRP:
return "CHIRP";
case RADAR_TYPE_NONE:
default:
return "N/A";
}
}
static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel, static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel,
u8 sync_band) u8 sync_band)
{ {
...@@ -115,6 +128,14 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) ...@@ -115,6 +128,14 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
wl18xx_scan_completed(wl, wl->scan_wlvif); wl18xx_scan_completed(wl, wl->scan_wlvif);
} }
if (vector & RADAR_DETECTED_EVENT_ID) {
wl1271_info("radar event: channel %d type %s",
mbox->radar_channel,
wl18xx_radar_type_decode(mbox->radar_type));
ieee80211_radar_detected(wl->hw);
}
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, wl1271_debug(DEBUG_EVENT,
"PERIODIC_SCAN_REPORT_EVENT (results %d)", "PERIODIC_SCAN_REPORT_EVENT (results %d)",
......
...@@ -42,6 +42,12 @@ enum { ...@@ -42,6 +42,12 @@ enum {
SMART_CONFIG_DECODE_EVENT_ID = BIT(23), SMART_CONFIG_DECODE_EVENT_ID = BIT(23),
}; };
enum wl18xx_radar_types {
RADAR_TYPE_NONE,
RADAR_TYPE_REGULAR,
RADAR_TYPE_CHIRP
};
struct wl18xx_event_mailbox { struct wl18xx_event_mailbox {
__le32 events_vector; __le32 events_vector;
...@@ -83,13 +89,19 @@ struct wl18xx_event_mailbox { ...@@ -83,13 +89,19 @@ struct wl18xx_event_mailbox {
u8 sc_token_len; u8 sc_token_len;
u8 padding1; u8 padding1;
u8 sc_ssid[32]; u8 sc_ssid[32];
u8 sc_pwd[32]; u8 sc_pwd[64];
u8 sc_token[32]; u8 sc_token[32];
/* smart config sync channel */ /* smart config sync channel */
u8 sc_sync_channel; u8 sc_sync_channel;
u8 sc_sync_band; u8 sc_sync_band;
u8 padding2[2]; u8 padding2[2];
/* radar detect */
u8 radar_channel;
u8 radar_type;
u8 padding3[2];
} __packed; } __packed;
int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
......
...@@ -655,7 +655,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { ...@@ -655,7 +655,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
}; };
/* TODO: maybe move to a new header file? */ /* TODO: maybe move to a new header file? */
#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin" #define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-4.bin"
static int wl18xx_identify_chip(struct wl1271 *wl) static int wl18xx_identify_chip(struct wl1271 *wl)
{ {
...@@ -990,6 +990,7 @@ static int wl18xx_boot(struct wl1271 *wl) ...@@ -990,6 +990,7 @@ static int wl18xx_boot(struct wl1271 *wl)
wl->event_mask = BSS_LOSS_EVENT_ID | wl->event_mask = BSS_LOSS_EVENT_ID |
SCAN_COMPLETE_EVENT_ID | SCAN_COMPLETE_EVENT_ID |
RADAR_DETECTED_EVENT_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID |
PERIODIC_SCAN_COMPLETE_EVENT_ID | PERIODIC_SCAN_COMPLETE_EVENT_ID |
PERIODIC_SCAN_REPORT_EVENT_ID | PERIODIC_SCAN_REPORT_EVENT_ID |
...@@ -1703,6 +1704,7 @@ static struct wlcore_ops wl18xx_ops = { ...@@ -1703,6 +1704,7 @@ static struct wlcore_ops wl18xx_ops = {
.interrupt_notify = wl18xx_acx_interrupt_notify_config, .interrupt_notify = wl18xx_acx_interrupt_notify_config,
.rx_ba_filter = wl18xx_acx_rx_ba_filter, .rx_ba_filter = wl18xx_acx_rx_ba_filter,
.ap_sleep = wl18xx_acx_ap_sleep, .ap_sleep = wl18xx_acx_ap_sleep,
.set_cac = wl18xx_cmd_set_cac,
}; };
/* HT cap appropriate for wide channels in 2Ghz */ /* HT cap appropriate for wide channels in 2Ghz */
......
...@@ -26,10 +26,10 @@ ...@@ -26,10 +26,10 @@
/* minimum FW required for driver */ /* minimum FW required for driver */
#define WL18XX_CHIP_VER 8 #define WL18XX_CHIP_VER 8
#define WL18XX_IFTYPE_VER 8 #define WL18XX_IFTYPE_VER 9
#define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE #define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE
#define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE #define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE
#define WL18XX_MINOR_VER 13 #define WL18XX_MINOR_VER 11
#define WL18XX_CMD_MAX_SIZE 740 #define WL18XX_CMD_MAX_SIZE 740
......
...@@ -403,7 +403,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) ...@@ -403,7 +403,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
WARN_ON_ONCE(wl->active_link_count < 0); WARN_ON_ONCE(wl->active_link_count < 0);
} }
static u8 wlcore_get_native_channel_type(u8 nl_channel_type) u8 wlcore_get_native_channel_type(u8 nl_channel_type)
{ {
switch (nl_channel_type) { switch (nl_channel_type) {
case NL80211_CHAN_NO_HT: case NL80211_CHAN_NO_HT:
...@@ -419,6 +419,7 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type) ...@@ -419,6 +419,7 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
return WLCORE_CHAN_NO_HT; return WLCORE_CHAN_NO_HT;
} }
} }
EXPORT_SYMBOL_GPL(wlcore_get_native_channel_type);
static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
struct wl12xx_vif *wlvif, struct wl12xx_vif *wlvif,
......
...@@ -105,6 +105,7 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -105,6 +105,7 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl, int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
u32 mask, bool *timeout); u32 mask, bool *timeout);
u8 wlcore_get_native_channel_type(u8 nl_channel_type);
enum wl1271_commands { enum wl1271_commands {
CMD_INTERROGATE = 1, /* use this to read information elements */ CMD_INTERROGATE = 1, /* use this to read information elements */
...@@ -172,6 +173,11 @@ enum wl1271_commands { ...@@ -172,6 +173,11 @@ enum wl1271_commands {
CMD_SMART_CONFIG_STOP = 62, CMD_SMART_CONFIG_STOP = 62,
CMD_SMART_CONFIG_SET_GROUP_KEY = 63, CMD_SMART_CONFIG_SET_GROUP_KEY = 63,
CMD_CAC_START = 64,
CMD_CAC_STOP = 65,
CMD_DFS_MASTER_RESTART = 66,
CMD_DFS_RADAR_DETECTION_DEBUG = 67,
MAX_COMMAND_ID = 0xFFFF, MAX_COMMAND_ID = 0xFFFF,
}; };
......
...@@ -311,4 +311,13 @@ wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, ...@@ -311,4 +311,13 @@ wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key); return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key);
} }
static inline int
wlcore_hw_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start)
{
if (!wl->ops->set_cac)
return -EINVAL;
return wl->ops->set_cac(wl, wlvif, start);
}
#endif #endif
...@@ -4629,10 +4629,46 @@ static void wlcore_op_change_chanctx(struct ieee80211_hw *hw, ...@@ -4629,10 +4629,46 @@ static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx, struct ieee80211_chanctx_conf *ctx,
u32 changed) u32 changed)
{ {
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif;
int ret;
int channel = ieee80211_frequency_to_channel(
ctx->def.chan->center_freq);
wl1271_debug(DEBUG_MAC80211, wl1271_debug(DEBUG_MAC80211,
"mac80211 change chanctx %d (type %d) changed 0x%x", "mac80211 change chanctx %d (type %d) changed 0x%x",
ieee80211_frequency_to_channel(ctx->def.chan->center_freq), channel, cfg80211_get_chandef_type(&ctx->def), changed);
cfg80211_get_chandef_type(&ctx->def), changed);
mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wl12xx_for_each_wlvif(wl, wlvif) {
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
rcu_read_lock();
if (rcu_access_pointer(vif->chanctx_conf) != ctx) {
rcu_read_unlock();
continue;
}
rcu_read_unlock();
/* start radar if needed */
if (changed & IEEE80211_CHANCTX_CHANGE_RADAR &&
wlvif->bss_type == BSS_TYPE_AP_BSS &&
ctx->radar_enabled && !wlvif->radar_enabled &&
ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
wl1271_debug(DEBUG_MAC80211, "Start radar detection");
wlcore_hw_set_cac(wl, wlvif, true);
wlvif->radar_enabled = true;
}
}
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
} }
static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
...@@ -4643,13 +4679,26 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, ...@@ -4643,13 +4679,26 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int channel = ieee80211_frequency_to_channel( int channel = ieee80211_frequency_to_channel(
ctx->def.chan->center_freq); ctx->def.chan->center_freq);
int ret = -EINVAL;
wl1271_debug(DEBUG_MAC80211, wl1271_debug(DEBUG_MAC80211,
"mac80211 assign chanctx (role %d) %d (type %d)", "mac80211 assign chanctx (role %d) %d (type %d) (radar %d dfs_state %d)",
wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def)); wlvif->role_id, channel,
cfg80211_get_chandef_type(&ctx->def),
ctx->radar_enabled, ctx->def.chan->dfs_state);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
goto out;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
wlvif->band = ctx->def.chan->band; wlvif->band = ctx->def.chan->band;
wlvif->channel = channel; wlvif->channel = channel;
wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def); wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
...@@ -4657,6 +4706,15 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, ...@@ -4657,6 +4706,15 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
/* update default rates according to the band */ /* update default rates according to the band */
wl1271_set_band_rate(wl, wlvif); wl1271_set_band_rate(wl, wlvif);
if (ctx->radar_enabled &&
ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
wl1271_debug(DEBUG_MAC80211, "Start radar detection");
wlcore_hw_set_cac(wl, wlvif, true);
wlvif->radar_enabled = true;
}
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return 0; return 0;
...@@ -4668,6 +4726,7 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, ...@@ -4668,6 +4726,7 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
wl1271_debug(DEBUG_MAC80211, wl1271_debug(DEBUG_MAC80211,
"mac80211 unassign chanctx (role %d) %d (type %d)", "mac80211 unassign chanctx (role %d) %d (type %d)",
...@@ -4676,6 +4735,97 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, ...@@ -4676,6 +4735,97 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
cfg80211_get_chandef_type(&ctx->def)); cfg80211_get_chandef_type(&ctx->def));
wl1271_tx_flush(wl); wl1271_tx_flush(wl);
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
goto out;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
if (wlvif->radar_enabled) {
wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
wlcore_hw_set_cac(wl, wlvif, false);
wlvif->radar_enabled = false;
}
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static int __wlcore_switch_vif_chan(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_chanctx_conf *new_ctx)
{
int channel = ieee80211_frequency_to_channel(
new_ctx->def.chan->center_freq);
wl1271_debug(DEBUG_MAC80211,
"switch vif (role %d) %d -> %d chan_type: %d",
wlvif->role_id, wlvif->channel, channel,
cfg80211_get_chandef_type(&new_ctx->def));
if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
return 0;
if (wlvif->radar_enabled) {
wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
wlcore_hw_set_cac(wl, wlvif, false);
wlvif->radar_enabled = false;
}
wlvif->band = new_ctx->def.chan->band;
wlvif->channel = channel;
wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def);
/* start radar if needed */
if (new_ctx->radar_enabled) {
wl1271_debug(DEBUG_MAC80211, "Start radar detection");
wlcore_hw_set_cac(wl, wlvif, true);
wlvif->radar_enabled = true;
}
return 0;
}
static int
wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif_chanctx_switch *vifs,
int n_vifs,
enum ieee80211_chanctx_switch_mode mode)
{
struct wl1271 *wl = hw->priv;
int i, ret;
wl1271_debug(DEBUG_MAC80211,
"mac80211 switch chanctx n_vifs %d mode %d",
n_vifs, mode);
mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
for (i = 0; i < n_vifs; i++) {
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif);
ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx);
if (ret)
goto out_sleep;
}
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return 0;
} }
static int wl1271_op_conf_tx(struct ieee80211_hw *hw, static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
...@@ -5665,6 +5815,7 @@ static const struct ieee80211_ops wl1271_ops = { ...@@ -5665,6 +5815,7 @@ static const struct ieee80211_ops wl1271_ops = {
.change_chanctx = wlcore_op_change_chanctx, .change_chanctx = wlcore_op_change_chanctx,
.assign_vif_chanctx = wlcore_op_assign_vif_chanctx, .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
.unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
.switch_vif_chanctx = wlcore_op_switch_vif_chanctx,
.sta_rc_update = wlcore_op_sta_rc_update, .sta_rc_update = wlcore_op_sta_rc_update,
.get_rssi = wlcore_op_get_rssi, .get_rssi = wlcore_op_get_rssi,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd) CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
......
...@@ -123,6 +123,8 @@ struct wlcore_ops { ...@@ -123,6 +123,8 @@ struct wlcore_ops {
int (*smart_config_stop)(struct wl1271 *wl); int (*smart_config_stop)(struct wl1271 *wl);
int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id, int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id,
u8 key_len, u8 *key); u8 key_len, u8 *key);
int (*set_cac)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool start);
}; };
enum wlcore_partitions { enum wlcore_partitions {
......
...@@ -434,6 +434,8 @@ struct wl12xx_vif { ...@@ -434,6 +434,8 @@ struct wl12xx_vif {
bool wmm_enabled; bool wmm_enabled;
bool radar_enabled;
/* Rx Streaming */ /* Rx Streaming */
struct work_struct rx_streaming_enable_work; struct work_struct rx_streaming_enable_work;
struct work_struct rx_streaming_disable_work; struct work_struct rx_streaming_disable_work;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册