qtnfmac: send EAPOL frames via control path
Use control path to send EAPOL frames to make sure they are sent with higher priority with aggregation disabled. Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
committed by
Kalle Valo
parent
72b3270e01
commit
bc70732f9b
@@ -49,6 +49,7 @@ struct qtnf_bus {
|
|||||||
struct napi_struct mux_napi;
|
struct napi_struct mux_napi;
|
||||||
struct net_device mux_dev;
|
struct net_device mux_dev;
|
||||||
struct workqueue_struct *workqueue;
|
struct workqueue_struct *workqueue;
|
||||||
|
struct workqueue_struct *hprio_workqueue;
|
||||||
struct work_struct fw_work;
|
struct work_struct fw_work;
|
||||||
struct work_struct event_work;
|
struct work_struct event_work;
|
||||||
struct mutex bus_lock; /* lock during command/event processing */
|
struct mutex bus_lock; /* lock during command/event processing */
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
|
|||||||
{
|
{
|
||||||
struct net_device *netdev = wdev->netdev;
|
struct net_device *netdev = wdev->netdev;
|
||||||
struct qtnf_vif *vif;
|
struct qtnf_vif *vif;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
if (WARN_ON(!netdev))
|
if (WARN_ON(!netdev))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -157,6 +158,11 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
|
|||||||
if (netif_carrier_ok(netdev))
|
if (netif_carrier_ok(netdev))
|
||||||
netif_carrier_off(netdev);
|
netif_carrier_off(netdev);
|
||||||
|
|
||||||
|
while ((skb = skb_dequeue(&vif->high_pri_tx_queue)))
|
||||||
|
dev_kfree_skb_any(skb);
|
||||||
|
|
||||||
|
cancel_work_sync(&vif->high_pri_tx_work);
|
||||||
|
|
||||||
if (netdev->reg_state == NETREG_REGISTERED)
|
if (netdev->reg_state == NETREG_REGISTERED)
|
||||||
unregister_netdevice(netdev);
|
unregister_netdevice(netdev);
|
||||||
|
|
||||||
@@ -424,13 +430,13 @@ qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|||||||
*cookie = short_cookie;
|
*cookie = short_cookie;
|
||||||
|
|
||||||
if (params->offchan)
|
if (params->offchan)
|
||||||
flags |= QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN;
|
flags |= QLINK_FRAME_TX_FLAG_OFFCHAN;
|
||||||
|
|
||||||
if (params->no_cck)
|
if (params->no_cck)
|
||||||
flags |= QLINK_MGMT_FRAME_TX_FLAG_NO_CCK;
|
flags |= QLINK_FRAME_TX_FLAG_NO_CCK;
|
||||||
|
|
||||||
if (params->dont_wait_for_ack)
|
if (params->dont_wait_for_ack)
|
||||||
flags |= QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT;
|
flags |= QLINK_FRAME_TX_FLAG_ACK_NOWAIT;
|
||||||
|
|
||||||
/* If channel is not specified, pass "freq = 0" to tell device
|
/* If channel is not specified, pass "freq = 0" to tell device
|
||||||
* firmware to use current channel.
|
* firmware to use current channel.
|
||||||
@@ -445,9 +451,8 @@ qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|||||||
le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da,
|
le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da,
|
||||||
params->len, short_cookie, flags);
|
params->len, short_cookie, flags);
|
||||||
|
|
||||||
return qtnf_cmd_send_mgmt_frame(vif, short_cookie, flags,
|
return qtnf_cmd_send_frame(vif, short_cookie, flags,
|
||||||
freq,
|
freq, params->buf, params->len);
|
||||||
params->buf, params->len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
@@ -381,11 +381,11 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
|
int qtnf_cmd_send_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
|
||||||
u16 freq, const u8 *buf, size_t len)
|
u16 freq, const u8 *buf, size_t len)
|
||||||
{
|
{
|
||||||
struct sk_buff *cmd_skb;
|
struct sk_buff *cmd_skb;
|
||||||
struct qlink_cmd_mgmt_frame_tx *cmd;
|
struct qlink_cmd_frame_tx *cmd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) {
|
if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) {
|
||||||
@@ -395,14 +395,14 @@ int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
|
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
|
||||||
QLINK_CMD_SEND_MGMT_FRAME,
|
QLINK_CMD_SEND_FRAME,
|
||||||
sizeof(*cmd));
|
sizeof(*cmd));
|
||||||
if (!cmd_skb)
|
if (!cmd_skb)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
qtnf_bus_lock(vif->mac->bus);
|
qtnf_bus_lock(vif->mac->bus);
|
||||||
|
|
||||||
cmd = (struct qlink_cmd_mgmt_frame_tx *)cmd_skb->data;
|
cmd = (struct qlink_cmd_frame_tx *)cmd_skb->data;
|
||||||
cmd->cookie = cpu_to_le32(cookie);
|
cmd->cookie = cpu_to_le32(cookie);
|
||||||
cmd->freq = cpu_to_le16(freq);
|
cmd->freq = cpu_to_le16(freq);
|
||||||
cmd->flags = cpu_to_le16(flags);
|
cmd->flags = cpu_to_le16(flags);
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
|
|||||||
const struct cfg80211_ap_settings *s);
|
const struct cfg80211_ap_settings *s);
|
||||||
int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif);
|
int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif);
|
||||||
int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg);
|
int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg);
|
||||||
int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
|
int qtnf_cmd_send_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
|
||||||
u16 freq, const u8 *buf, size_t len);
|
u16 freq, const u8 *buf, size_t len);
|
||||||
int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type,
|
int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type,
|
||||||
const u8 *buf, size_t len);
|
const u8 *buf, size_t len);
|
||||||
int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
|
int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
|
||||||
|
|||||||
@@ -368,6 +368,23 @@ static void qtnf_mac_scan_timeout(struct work_struct *work)
|
|||||||
qtnf_mac_scan_finish(mac, true);
|
qtnf_mac_scan_finish(mac, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qtnf_vif_send_data_high_pri(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct qtnf_vif *vif =
|
||||||
|
container_of(work, struct qtnf_vif, high_pri_tx_work);
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
if (!vif->netdev ||
|
||||||
|
vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((skb = skb_dequeue(&vif->high_pri_tx_queue))) {
|
||||||
|
qtnf_cmd_send_frame(vif, 0, QLINK_FRAME_TX_FLAG_8023,
|
||||||
|
0, skb->data, skb->len);
|
||||||
|
dev_kfree_skb_any(skb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus,
|
static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus,
|
||||||
unsigned int macid)
|
unsigned int macid)
|
||||||
{
|
{
|
||||||
@@ -395,7 +412,8 @@ static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus,
|
|||||||
vif->mac = mac;
|
vif->mac = mac;
|
||||||
vif->vifid = i;
|
vif->vifid = i;
|
||||||
qtnf_sta_list_init(&vif->sta_list);
|
qtnf_sta_list_init(&vif->sta_list);
|
||||||
|
INIT_WORK(&vif->high_pri_tx_work, qtnf_vif_send_data_high_pri);
|
||||||
|
skb_queue_head_init(&vif->high_pri_tx_queue);
|
||||||
vif->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
vif->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
||||||
if (!vif->stats64)
|
if (!vif->stats64)
|
||||||
pr_warn("VIF%u.%u: per cpu stats allocation failed\n",
|
pr_warn("VIF%u.%u: per cpu stats allocation failed\n",
|
||||||
@@ -598,6 +616,13 @@ int qtnf_core_attach(struct qtnf_bus *bus)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bus->hprio_workqueue = alloc_workqueue("QTNF_HPRI", WQ_HIGHPRI, 0);
|
||||||
|
if (!bus->hprio_workqueue) {
|
||||||
|
pr_err("failed to alloc high prio workqueue\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
INIT_WORK(&bus->event_work, qtnf_event_work_handler);
|
INIT_WORK(&bus->event_work, qtnf_event_work_handler);
|
||||||
|
|
||||||
ret = qtnf_cmd_send_init_fw(bus);
|
ret = qtnf_cmd_send_init_fw(bus);
|
||||||
@@ -607,7 +632,6 @@ int qtnf_core_attach(struct qtnf_bus *bus)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bus->fw_state = QTNF_FW_STATE_ACTIVE;
|
bus->fw_state = QTNF_FW_STATE_ACTIVE;
|
||||||
|
|
||||||
ret = qtnf_cmd_get_hw_info(bus);
|
ret = qtnf_cmd_get_hw_info(bus);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("failed to get HW info: %d\n", ret);
|
pr_err("failed to get HW info: %d\n", ret);
|
||||||
@@ -642,7 +666,6 @@ int qtnf_core_attach(struct qtnf_bus *bus)
|
|||||||
|
|
||||||
error:
|
error:
|
||||||
qtnf_core_detach(bus);
|
qtnf_core_detach(bus);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qtnf_core_attach);
|
EXPORT_SYMBOL_GPL(qtnf_core_attach);
|
||||||
@@ -664,6 +687,13 @@ void qtnf_core_detach(struct qtnf_bus *bus)
|
|||||||
if (bus->workqueue) {
|
if (bus->workqueue) {
|
||||||
flush_workqueue(bus->workqueue);
|
flush_workqueue(bus->workqueue);
|
||||||
destroy_workqueue(bus->workqueue);
|
destroy_workqueue(bus->workqueue);
|
||||||
|
bus->workqueue = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bus->hprio_workqueue) {
|
||||||
|
flush_workqueue(bus->hprio_workqueue);
|
||||||
|
destroy_workqueue(bus->hprio_workqueue);
|
||||||
|
bus->hprio_workqueue = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qtnf_trans_free(bus);
|
qtnf_trans_free(bus);
|
||||||
@@ -800,6 +830,15 @@ void qtnf_update_tx_stats(struct net_device *ndev, const struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qtnf_update_tx_stats);
|
EXPORT_SYMBOL_GPL(qtnf_update_tx_stats);
|
||||||
|
|
||||||
|
void qtnf_packet_send_hi_pri(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct qtnf_vif *vif = qtnf_netdev_get_priv(skb->dev);
|
||||||
|
|
||||||
|
skb_queue_tail(&vif->high_pri_tx_queue, skb);
|
||||||
|
queue_work(vif->mac->bus->hprio_workqueue, &vif->high_pri_tx_work);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(qtnf_packet_send_hi_pri);
|
||||||
|
|
||||||
MODULE_AUTHOR("Quantenna Communications");
|
MODULE_AUTHOR("Quantenna Communications");
|
||||||
MODULE_DESCRIPTION("Quantenna 802.11 wireless LAN FullMAC driver.");
|
MODULE_DESCRIPTION("Quantenna 802.11 wireless LAN FullMAC driver.");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ struct qtnf_vif {
|
|||||||
struct qtnf_wmac *mac;
|
struct qtnf_wmac *mac;
|
||||||
|
|
||||||
struct work_struct reset_work;
|
struct work_struct reset_work;
|
||||||
|
struct work_struct high_pri_tx_work;
|
||||||
|
struct sk_buff_head high_pri_tx_queue;
|
||||||
struct qtnf_sta_list sta_list;
|
struct qtnf_sta_list sta_list;
|
||||||
unsigned long cons_tx_timeout_cnt;
|
unsigned long cons_tx_timeout_cnt;
|
||||||
int generation;
|
int generation;
|
||||||
@@ -149,6 +151,7 @@ void qtnf_virtual_intf_cleanup(struct net_device *ndev);
|
|||||||
|
|
||||||
void qtnf_netdev_updown(struct net_device *ndev, bool up);
|
void qtnf_netdev_updown(struct net_device *ndev, bool up);
|
||||||
void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted);
|
void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted);
|
||||||
|
void qtnf_packet_send_hi_pri(struct sk_buff *skb);
|
||||||
|
|
||||||
static inline struct qtnf_vif *qtnf_netdev_get_priv(struct net_device *dev)
|
static inline struct qtnf_vif *qtnf_netdev_get_priv(struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||||||
pcie_priv->pcie_irq_count = 0;
|
pcie_priv->pcie_irq_count = 0;
|
||||||
pcie_priv->tx_reclaim_done = 0;
|
pcie_priv->tx_reclaim_done = 0;
|
||||||
pcie_priv->tx_reclaim_req = 0;
|
pcie_priv->tx_reclaim_req = 0;
|
||||||
|
pcie_priv->tx_eapol = 0;
|
||||||
|
|
||||||
pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PCIE");
|
pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PCIE");
|
||||||
if (!pcie_priv->workqueue) {
|
if (!pcie_priv->workqueue) {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ struct qtnf_pcie_bus_priv {
|
|||||||
u32 tx_done_count;
|
u32 tx_done_count;
|
||||||
u32 tx_reclaim_done;
|
u32 tx_reclaim_done;
|
||||||
u32 tx_reclaim_req;
|
u32 tx_reclaim_req;
|
||||||
|
u32 tx_eapol;
|
||||||
|
|
||||||
u8 msi_enabled;
|
u8 msi_enabled;
|
||||||
u8 tx_stopped;
|
u8 tx_stopped;
|
||||||
|
|||||||
@@ -498,6 +498,13 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb)
|
|||||||
int len;
|
int len;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (unlikely(skb->protocol == htons(ETH_P_PAE))) {
|
||||||
|
qtnf_packet_send_hi_pri(skb);
|
||||||
|
qtnf_update_tx_stats(skb->dev, skb);
|
||||||
|
priv->tx_eapol++;
|
||||||
|
return NETDEV_TX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&priv->tx_lock, flags);
|
spin_lock_irqsave(&priv->tx_lock, flags);
|
||||||
|
|
||||||
if (!qtnf_tx_queue_ready(ts)) {
|
if (!qtnf_tx_queue_ready(ts)) {
|
||||||
@@ -761,6 +768,7 @@ static int qtnf_dbg_pkt_stats(struct seq_file *s, void *data)
|
|||||||
seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
|
seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
|
||||||
seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
|
seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
|
||||||
seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
|
seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
|
||||||
|
seq_printf(s, "tx_eapol(%u)\n", priv->tx_eapol);
|
||||||
|
|
||||||
seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
|
seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
|
||||||
seq_printf(s, "tx_done_index(%u)\n", tx_done_index);
|
seq_printf(s, "tx_done_index(%u)\n", tx_done_index);
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ struct qlink_sta_info_state {
|
|||||||
* execution status (one of &enum qlink_cmd_result). Reply message
|
* execution status (one of &enum qlink_cmd_result). Reply message
|
||||||
* may also contain data payload specific to the command type.
|
* may also contain data payload specific to the command type.
|
||||||
*
|
*
|
||||||
|
* @QLINK_CMD_SEND_FRAME: send specified frame over the air; firmware will
|
||||||
|
* encapsulate 802.3 packet into 802.11 frame automatically.
|
||||||
* @QLINK_CMD_BAND_INFO_GET: for the specified MAC and specified band, get
|
* @QLINK_CMD_BAND_INFO_GET: for the specified MAC and specified band, get
|
||||||
* the band's description including number of operational channels and
|
* the band's description including number of operational channels and
|
||||||
* info on each channel, HT/VHT capabilities, supported rates etc.
|
* info on each channel, HT/VHT capabilities, supported rates etc.
|
||||||
@@ -220,7 +222,7 @@ enum qlink_cmd_type {
|
|||||||
QLINK_CMD_FW_INIT = 0x0001,
|
QLINK_CMD_FW_INIT = 0x0001,
|
||||||
QLINK_CMD_FW_DEINIT = 0x0002,
|
QLINK_CMD_FW_DEINIT = 0x0002,
|
||||||
QLINK_CMD_REGISTER_MGMT = 0x0003,
|
QLINK_CMD_REGISTER_MGMT = 0x0003,
|
||||||
QLINK_CMD_SEND_MGMT_FRAME = 0x0004,
|
QLINK_CMD_SEND_FRAME = 0x0004,
|
||||||
QLINK_CMD_MGMT_SET_APPIE = 0x0005,
|
QLINK_CMD_MGMT_SET_APPIE = 0x0005,
|
||||||
QLINK_CMD_PHY_PARAMS_GET = 0x0011,
|
QLINK_CMD_PHY_PARAMS_GET = 0x0011,
|
||||||
QLINK_CMD_PHY_PARAMS_SET = 0x0012,
|
QLINK_CMD_PHY_PARAMS_SET = 0x0012,
|
||||||
@@ -321,22 +323,26 @@ struct qlink_cmd_mgmt_frame_register {
|
|||||||
u8 do_register;
|
u8 do_register;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
enum qlink_mgmt_frame_tx_flags {
|
/**
|
||||||
QLINK_MGMT_FRAME_TX_FLAG_NONE = 0,
|
* @QLINK_FRAME_TX_FLAG_8023: frame has a 802.3 header; if not set, frame
|
||||||
QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN = BIT(0),
|
* is a 802.11 encapsulated.
|
||||||
QLINK_MGMT_FRAME_TX_FLAG_NO_CCK = BIT(1),
|
*/
|
||||||
QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT = BIT(2),
|
enum qlink_frame_tx_flags {
|
||||||
|
QLINK_FRAME_TX_FLAG_OFFCHAN = BIT(0),
|
||||||
|
QLINK_FRAME_TX_FLAG_NO_CCK = BIT(1),
|
||||||
|
QLINK_FRAME_TX_FLAG_ACK_NOWAIT = BIT(2),
|
||||||
|
QLINK_FRAME_TX_FLAG_8023 = BIT(3),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct qlink_cmd_mgmt_frame_tx - data for QLINK_CMD_SEND_MGMT_FRAME command
|
* struct qlink_cmd_frame_tx - data for QLINK_CMD_SEND_FRAME command
|
||||||
*
|
*
|
||||||
* @cookie: opaque request identifier.
|
* @cookie: opaque request identifier.
|
||||||
* @freq: Frequency to use for frame transmission.
|
* @freq: Frequency to use for frame transmission.
|
||||||
* @flags: Transmission flags, one of &enum qlink_mgmt_frame_tx_flags.
|
* @flags: Transmission flags, one of &enum qlink_frame_tx_flags.
|
||||||
* @frame_data: frame to transmit.
|
* @frame_data: frame to transmit.
|
||||||
*/
|
*/
|
||||||
struct qlink_cmd_mgmt_frame_tx {
|
struct qlink_cmd_frame_tx {
|
||||||
struct qlink_cmd chdr;
|
struct qlink_cmd chdr;
|
||||||
__le32 cookie;
|
__le32 cookie;
|
||||||
__le16 freq;
|
__le16 freq;
|
||||||
|
|||||||
Reference in New Issue
Block a user