diff options
Diffstat (limited to 'drivers/net')
37 files changed, 6406 insertions, 84 deletions
diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig index e49c0eff040b..a9481606bbcd 100644 --- a/drivers/net/ethernet/adi/Kconfig +++ b/drivers/net/ethernet/adi/Kconfig | |||
| @@ -61,6 +61,7 @@ config BFIN_RX_DESC_NUM | |||
| 61 | 61 | ||
| 62 | config BFIN_MAC_USE_HWSTAMP | 62 | config BFIN_MAC_USE_HWSTAMP |
| 63 | bool "Use IEEE 1588 hwstamp" | 63 | bool "Use IEEE 1588 hwstamp" |
| 64 | depends on BFIN_MAC && BF518 | ||
| 64 | select PTP_1588_CLOCK | 65 | select PTP_1588_CLOCK |
| 65 | default y | 66 | default y |
| 66 | ---help--- | 67 | ---help--- |
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 01588b66a38c..f771ddfba646 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | |||
| @@ -80,12 +80,37 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) | |||
| 80 | new_txdata_index = new_max_eth_txqs + FCOE_TXQ_IDX_OFFSET; | 80 | new_txdata_index = new_max_eth_txqs + FCOE_TXQ_IDX_OFFSET; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | memcpy(&bp->bnx2x_txq[old_txdata_index], | 83 | memcpy(&bp->bnx2x_txq[new_txdata_index], |
| 84 | &bp->bnx2x_txq[new_txdata_index], | 84 | &bp->bnx2x_txq[old_txdata_index], |
| 85 | sizeof(struct bnx2x_fp_txdata)); | 85 | sizeof(struct bnx2x_fp_txdata)); |
| 86 | to_fp->txdata_ptr[0] = &bp->bnx2x_txq[new_txdata_index]; | 86 | to_fp->txdata_ptr[0] = &bp->bnx2x_txq[new_txdata_index]; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | /** | ||
| 90 | * bnx2x_shrink_eth_fp - guarantees fastpath structures stay intact | ||
| 91 | * | ||
| 92 | * @bp: driver handle | ||
| 93 | * @delta: number of eth queues which were not allocated | ||
| 94 | */ | ||
| 95 | static void bnx2x_shrink_eth_fp(struct bnx2x *bp, int delta) | ||
| 96 | { | ||
| 97 | int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp); | ||
| 98 | |||
| 99 | /* Queue pointer cannot be re-set on an fp-basis, as moving pointer | ||
| 100 | * backward along the array could cause memory to be overriden | ||
| 101 | */ | ||
| 102 | for (cos = 1; cos < bp->max_cos; cos++) { | ||
| 103 | for (i = 0; i < old_eth_num - delta; i++) { | ||
| 104 | struct bnx2x_fastpath *fp = &bp->fp[i]; | ||
| 105 | int new_idx = cos * (old_eth_num - delta) + i; | ||
| 106 | |||
| 107 | memcpy(&bp->bnx2x_txq[new_idx], fp->txdata_ptr[cos], | ||
| 108 | sizeof(struct bnx2x_fp_txdata)); | ||
| 109 | fp->txdata_ptr[cos] = &bp->bnx2x_txq[new_idx]; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 89 | int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */ | 114 | int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */ |
| 90 | 115 | ||
| 91 | /* free skb in the packet ring at pos idx | 116 | /* free skb in the packet ring at pos idx |
| @@ -3863,6 +3888,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp) | |||
| 3863 | int delta = BNX2X_NUM_ETH_QUEUES(bp) - i; | 3888 | int delta = BNX2X_NUM_ETH_QUEUES(bp) - i; |
| 3864 | 3889 | ||
| 3865 | WARN_ON(delta < 0); | 3890 | WARN_ON(delta < 0); |
| 3891 | bnx2x_shrink_eth_fp(bp, delta); | ||
| 3866 | if (CNIC_SUPPORT(bp)) | 3892 | if (CNIC_SUPPORT(bp)) |
| 3867 | /* move non eth FPs next to last eth FP | 3893 | /* move non eth FPs next to last eth FP |
| 3868 | * must be done in that order | 3894 | * must be done in that order |
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 277f17e3c8f8..a427b49a886c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | |||
| @@ -2777,10 +2777,10 @@ static int bnx2x_set_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info) | |||
| 2777 | } else if ((info->flow_type == UDP_V6_FLOW) && | 2777 | } else if ((info->flow_type == UDP_V6_FLOW) && |
| 2778 | (bp->rss_conf_obj.udp_rss_v6 != udp_rss_requested)) { | 2778 | (bp->rss_conf_obj.udp_rss_v6 != udp_rss_requested)) { |
| 2779 | bp->rss_conf_obj.udp_rss_v6 = udp_rss_requested; | 2779 | bp->rss_conf_obj.udp_rss_v6 = udp_rss_requested; |
| 2780 | return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); | ||
| 2781 | DP(BNX2X_MSG_ETHTOOL, | 2780 | DP(BNX2X_MSG_ETHTOOL, |
| 2782 | "rss re-configured, UDP 4-tupple %s\n", | 2781 | "rss re-configured, UDP 4-tupple %s\n", |
| 2783 | udp_rss_requested ? "enabled" : "disabled"); | 2782 | udp_rss_requested ? "enabled" : "disabled"); |
| 2783 | return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); | ||
| 2784 | } else { | 2784 | } else { |
| 2785 | return 0; | 2785 | return 0; |
| 2786 | } | 2786 | } |
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 940ef859dc60..5523da3afcdc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | |||
| @@ -127,6 +127,17 @@ MODULE_PARM_DESC(debug, " Default debug msglevel"); | |||
| 127 | 127 | ||
| 128 | struct workqueue_struct *bnx2x_wq; | 128 | struct workqueue_struct *bnx2x_wq; |
| 129 | 129 | ||
| 130 | struct bnx2x_mac_vals { | ||
| 131 | u32 xmac_addr; | ||
| 132 | u32 xmac_val; | ||
| 133 | u32 emac_addr; | ||
| 134 | u32 emac_val; | ||
| 135 | u32 umac_addr; | ||
| 136 | u32 umac_val; | ||
| 137 | u32 bmac_addr; | ||
| 138 | u32 bmac_val[2]; | ||
| 139 | }; | ||
| 140 | |||
| 130 | enum bnx2x_board_type { | 141 | enum bnx2x_board_type { |
| 131 | BCM57710 = 0, | 142 | BCM57710 = 0, |
| 132 | BCM57711, | 143 | BCM57711, |
| @@ -9420,12 +9431,19 @@ static inline void bnx2x_undi_int_disable(struct bnx2x *bp) | |||
| 9420 | bnx2x_undi_int_disable_e1h(bp); | 9431 | bnx2x_undi_int_disable_e1h(bp); |
| 9421 | } | 9432 | } |
| 9422 | 9433 | ||
| 9423 | static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) | 9434 | static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, |
| 9435 | struct bnx2x_mac_vals *vals) | ||
| 9424 | { | 9436 | { |
| 9425 | u32 val, base_addr, offset, mask, reset_reg; | 9437 | u32 val, base_addr, offset, mask, reset_reg; |
| 9426 | bool mac_stopped = false; | 9438 | bool mac_stopped = false; |
| 9427 | u8 port = BP_PORT(bp); | 9439 | u8 port = BP_PORT(bp); |
| 9428 | 9440 | ||
| 9441 | /* reset addresses as they also mark which values were changed */ | ||
| 9442 | vals->bmac_addr = 0; | ||
| 9443 | vals->umac_addr = 0; | ||
| 9444 | vals->xmac_addr = 0; | ||
| 9445 | vals->emac_addr = 0; | ||
| 9446 | |||
| 9429 | reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2); | 9447 | reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2); |
| 9430 | 9448 | ||
| 9431 | if (!CHIP_IS_E3(bp)) { | 9449 | if (!CHIP_IS_E3(bp)) { |
| @@ -9447,14 +9465,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) | |||
| 9447 | */ | 9465 | */ |
| 9448 | wb_data[0] = REG_RD(bp, base_addr + offset); | 9466 | wb_data[0] = REG_RD(bp, base_addr + offset); |
| 9449 | wb_data[1] = REG_RD(bp, base_addr + offset + 0x4); | 9467 | wb_data[1] = REG_RD(bp, base_addr + offset + 0x4); |
| 9468 | vals->bmac_addr = base_addr + offset; | ||
| 9469 | vals->bmac_val[0] = wb_data[0]; | ||
| 9470 | vals->bmac_val[1] = wb_data[1]; | ||
| 9450 | wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; | 9471 | wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; |
| 9451 | REG_WR(bp, base_addr + offset, wb_data[0]); | 9472 | REG_WR(bp, vals->bmac_addr, wb_data[0]); |
| 9452 | REG_WR(bp, base_addr + offset + 0x4, wb_data[1]); | 9473 | REG_WR(bp, vals->bmac_addr + 0x4, wb_data[1]); |
| 9453 | 9474 | ||
| 9454 | } | 9475 | } |
| 9455 | BNX2X_DEV_INFO("Disable emac Rx\n"); | 9476 | BNX2X_DEV_INFO("Disable emac Rx\n"); |
| 9456 | REG_WR(bp, NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4, 0); | 9477 | vals->emac_addr = NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4; |
| 9457 | 9478 | vals->emac_val = REG_RD(bp, vals->emac_addr); | |
| 9479 | REG_WR(bp, vals->emac_addr, 0); | ||
| 9458 | mac_stopped = true; | 9480 | mac_stopped = true; |
| 9459 | } else { | 9481 | } else { |
| 9460 | if (reset_reg & MISC_REGISTERS_RESET_REG_2_XMAC) { | 9482 | if (reset_reg & MISC_REGISTERS_RESET_REG_2_XMAC) { |
| @@ -9465,14 +9487,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) | |||
| 9465 | val & ~(1 << 1)); | 9487 | val & ~(1 << 1)); |
| 9466 | REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI, | 9488 | REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI, |
| 9467 | val | (1 << 1)); | 9489 | val | (1 << 1)); |
| 9468 | REG_WR(bp, base_addr + XMAC_REG_CTRL, 0); | 9490 | vals->xmac_addr = base_addr + XMAC_REG_CTRL; |
| 9491 | vals->xmac_val = REG_RD(bp, vals->xmac_addr); | ||
| 9492 | REG_WR(bp, vals->xmac_addr, 0); | ||
| 9469 | mac_stopped = true; | 9493 | mac_stopped = true; |
| 9470 | } | 9494 | } |
| 9471 | mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; | 9495 | mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; |
| 9472 | if (mask & reset_reg) { | 9496 | if (mask & reset_reg) { |
| 9473 | BNX2X_DEV_INFO("Disable umac Rx\n"); | 9497 | BNX2X_DEV_INFO("Disable umac Rx\n"); |
| 9474 | base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; | 9498 | base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; |
| 9475 | REG_WR(bp, base_addr + UMAC_REG_COMMAND_CONFIG, 0); | 9499 | vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG; |
| 9500 | vals->umac_val = REG_RD(bp, vals->umac_addr); | ||
| 9501 | REG_WR(bp, vals->umac_addr, 0); | ||
| 9476 | mac_stopped = true; | 9502 | mac_stopped = true; |
| 9477 | } | 9503 | } |
| 9478 | } | 9504 | } |
| @@ -9664,12 +9690,16 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) | |||
| 9664 | { | 9690 | { |
| 9665 | u32 reset_reg, tmp_reg = 0, rc; | 9691 | u32 reset_reg, tmp_reg = 0, rc; |
| 9666 | bool prev_undi = false; | 9692 | bool prev_undi = false; |
| 9693 | struct bnx2x_mac_vals mac_vals; | ||
| 9694 | |||
| 9667 | /* It is possible a previous function received 'common' answer, | 9695 | /* It is possible a previous function received 'common' answer, |
| 9668 | * but hasn't loaded yet, therefore creating a scenario of | 9696 | * but hasn't loaded yet, therefore creating a scenario of |
| 9669 | * multiple functions receiving 'common' on the same path. | 9697 | * multiple functions receiving 'common' on the same path. |
| 9670 | */ | 9698 | */ |
| 9671 | BNX2X_DEV_INFO("Common unload Flow\n"); | 9699 | BNX2X_DEV_INFO("Common unload Flow\n"); |
| 9672 | 9700 | ||
| 9701 | memset(&mac_vals, 0, sizeof(mac_vals)); | ||
| 9702 | |||
| 9673 | if (bnx2x_prev_is_path_marked(bp)) | 9703 | if (bnx2x_prev_is_path_marked(bp)) |
| 9674 | return bnx2x_prev_mcp_done(bp); | 9704 | return bnx2x_prev_mcp_done(bp); |
| 9675 | 9705 | ||
| @@ -9680,7 +9710,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) | |||
| 9680 | u32 timer_count = 1000; | 9710 | u32 timer_count = 1000; |
| 9681 | 9711 | ||
| 9682 | /* Close the MAC Rx to prevent BRB from filling up */ | 9712 | /* Close the MAC Rx to prevent BRB from filling up */ |
| 9683 | bnx2x_prev_unload_close_mac(bp); | 9713 | bnx2x_prev_unload_close_mac(bp, &mac_vals); |
| 9714 | |||
| 9715 | /* close LLH filters towards the BRB */ | ||
| 9716 | bnx2x_set_rx_filter(&bp->link_params, 0); | ||
| 9684 | 9717 | ||
| 9685 | /* Check if the UNDI driver was previously loaded | 9718 | /* Check if the UNDI driver was previously loaded |
| 9686 | * UNDI driver initializes CID offset for normal bell to 0x7 | 9719 | * UNDI driver initializes CID offset for normal bell to 0x7 |
| @@ -9727,6 +9760,17 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) | |||
| 9727 | /* No packets are in the pipeline, path is ready for reset */ | 9760 | /* No packets are in the pipeline, path is ready for reset */ |
| 9728 | bnx2x_reset_common(bp); | 9761 | bnx2x_reset_common(bp); |
| 9729 | 9762 | ||
| 9763 | if (mac_vals.xmac_addr) | ||
| 9764 | REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val); | ||
| 9765 | if (mac_vals.umac_addr) | ||
| 9766 | REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val); | ||
| 9767 | if (mac_vals.emac_addr) | ||
| 9768 | REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val); | ||
| 9769 | if (mac_vals.bmac_addr) { | ||
| 9770 | REG_WR(bp, mac_vals.bmac_addr, mac_vals.bmac_val[0]); | ||
| 9771 | REG_WR(bp, mac_vals.bmac_addr + 4, mac_vals.bmac_val[1]); | ||
| 9772 | } | ||
| 9773 | |||
| 9730 | rc = bnx2x_prev_mark_path(bp, prev_undi); | 9774 | rc = bnx2x_prev_mark_path(bp, prev_undi); |
| 9731 | if (rc) { | 9775 | if (rc) { |
| 9732 | bnx2x_prev_mcp_done(bp); | 9776 | bnx2x_prev_mcp_done(bp); |
diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 3bc1912afba9..4eba17b83ba8 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h | |||
| @@ -190,6 +190,7 @@ struct be_eq_obj { | |||
| 190 | 190 | ||
| 191 | u8 idx; /* array index */ | 191 | u8 idx; /* array index */ |
| 192 | u16 tx_budget; | 192 | u16 tx_budget; |
| 193 | u16 spurious_intr; | ||
| 193 | struct napi_struct napi; | 194 | struct napi_struct napi; |
| 194 | struct be_adapter *adapter; | 195 | struct be_adapter *adapter; |
| 195 | } ____cacheline_aligned_in_smp; | 196 | } ____cacheline_aligned_in_smp; |
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 9dca22be8125..5c995700e534 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c | |||
| @@ -2026,19 +2026,30 @@ static irqreturn_t be_intx(int irq, void *dev) | |||
| 2026 | struct be_adapter *adapter = eqo->adapter; | 2026 | struct be_adapter *adapter = eqo->adapter; |
| 2027 | int num_evts = 0; | 2027 | int num_evts = 0; |
| 2028 | 2028 | ||
| 2029 | /* On Lancer, clear-intr bit of the EQ DB does not work. | 2029 | /* IRQ is not expected when NAPI is scheduled as the EQ |
| 2030 | * INTx is de-asserted only on notifying num evts. | 2030 | * will not be armed. |
| 2031 | * But, this can happen on Lancer INTx where it takes | ||
| 2032 | * a while to de-assert INTx or in BE2 where occasionaly | ||
| 2033 | * an interrupt may be raised even when EQ is unarmed. | ||
| 2034 | * If NAPI is already scheduled, then counting & notifying | ||
| 2035 | * events will orphan them. | ||
| 2031 | */ | 2036 | */ |
| 2032 | if (lancer_chip(adapter)) | 2037 | if (napi_schedule_prep(&eqo->napi)) { |
| 2033 | num_evts = events_get(eqo); | 2038 | num_evts = events_get(eqo); |
| 2039 | __napi_schedule(&eqo->napi); | ||
| 2040 | if (num_evts) | ||
| 2041 | eqo->spurious_intr = 0; | ||
| 2042 | } | ||
| 2043 | be_eq_notify(adapter, eqo->q.id, false, true, num_evts); | ||
| 2034 | 2044 | ||
| 2035 | /* The EQ-notify may not de-assert INTx rightaway, causing | 2045 | /* Return IRQ_HANDLED only for the the first spurious intr |
| 2036 | * the ISR to be invoked again. So, return HANDLED even when | 2046 | * after a valid intr to stop the kernel from branding |
| 2037 | * num_evts is zero. | 2047 | * this irq as a bad one! |
| 2038 | */ | 2048 | */ |
| 2039 | be_eq_notify(adapter, eqo->q.id, false, true, num_evts); | 2049 | if (num_evts || eqo->spurious_intr++ == 0) |
| 2040 | napi_schedule(&eqo->napi); | 2050 | return IRQ_HANDLED; |
| 2041 | return IRQ_HANDLED; | 2051 | else |
| 2052 | return IRQ_NONE; | ||
| 2042 | } | 2053 | } |
| 2043 | 2054 | ||
| 2044 | static irqreturn_t be_msix(int irq, void *dev) | 2055 | static irqreturn_t be_msix(int irq, void *dev) |
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index f80cd975daed..3e73742024b0 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c | |||
| @@ -4678,7 +4678,7 @@ static int qlge_probe(struct pci_dev *pdev, | |||
| 4678 | qdev = netdev_priv(ndev); | 4678 | qdev = netdev_priv(ndev); |
| 4679 | SET_NETDEV_DEV(ndev, &pdev->dev); | 4679 | SET_NETDEV_DEV(ndev, &pdev->dev); |
| 4680 | ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | | 4680 | ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | |
| 4681 | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | | 4681 | NETIF_F_TSO | NETIF_F_TSO_ECN | |
| 4682 | NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM; | 4682 | NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM; |
| 4683 | ndev->features = ndev->hw_features | | 4683 | ndev->features = ndev->hw_features | |
| 4684 | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; | 4684 | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; |
diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 5778a4ae1164..122d60c0481b 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig | |||
| @@ -27,7 +27,7 @@ config XILINX_EMACLITE | |||
| 27 | 27 | ||
| 28 | config XILINX_AXI_EMAC | 28 | config XILINX_AXI_EMAC |
| 29 | tristate "Xilinx 10/100/1000 AXI Ethernet support" | 29 | tristate "Xilinx 10/100/1000 AXI Ethernet support" |
| 30 | depends on (PPC32 || MICROBLAZE) | 30 | depends on MICROBLAZE |
| 31 | select PHYLIB | 31 | select PHYLIB |
| 32 | ---help--- | 32 | ---help--- |
| 33 | This driver supports the 10/100/1000 Ethernet from Xilinx for the | 33 | This driver supports the 10/100/1000 Ethernet from Xilinx for the |
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d9f69b82cc4f..6f47100e58d7 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c | |||
| @@ -1590,7 +1590,7 @@ static int axienet_of_probe(struct platform_device *op) | |||
| 1590 | lp->rx_irq = irq_of_parse_and_map(np, 1); | 1590 | lp->rx_irq = irq_of_parse_and_map(np, 1); |
| 1591 | lp->tx_irq = irq_of_parse_and_map(np, 0); | 1591 | lp->tx_irq = irq_of_parse_and_map(np, 0); |
| 1592 | of_node_put(np); | 1592 | of_node_put(np); |
| 1593 | if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) { | 1593 | if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { |
| 1594 | dev_err(&op->dev, "could not determine irqs\n"); | 1594 | dev_err(&op->dev, "could not determine irqs\n"); |
| 1595 | ret = -ENOMEM; | 1595 | ret = -ENOMEM; |
| 1596 | goto err_iounmap_2; | 1596 | goto err_iounmap_2; |
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index fbd106edbe59..af372d0957fe 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
| @@ -404,8 +404,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean) | |||
| 404 | struct tun_struct *tun; | 404 | struct tun_struct *tun; |
| 405 | struct net_device *dev; | 405 | struct net_device *dev; |
| 406 | 406 | ||
| 407 | tun = rcu_dereference_protected(tfile->tun, | 407 | tun = rtnl_dereference(tfile->tun); |
| 408 | lockdep_rtnl_is_held()); | 408 | |
| 409 | if (tun) { | 409 | if (tun) { |
| 410 | u16 index = tfile->queue_index; | 410 | u16 index = tfile->queue_index; |
| 411 | BUG_ON(index >= tun->numqueues); | 411 | BUG_ON(index >= tun->numqueues); |
| @@ -414,8 +414,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean) | |||
| 414 | rcu_assign_pointer(tun->tfiles[index], | 414 | rcu_assign_pointer(tun->tfiles[index], |
| 415 | tun->tfiles[tun->numqueues - 1]); | 415 | tun->tfiles[tun->numqueues - 1]); |
| 416 | rcu_assign_pointer(tfile->tun, NULL); | 416 | rcu_assign_pointer(tfile->tun, NULL); |
| 417 | ntfile = rcu_dereference_protected(tun->tfiles[index], | 417 | ntfile = rtnl_dereference(tun->tfiles[index]); |
| 418 | lockdep_rtnl_is_held()); | ||
| 419 | ntfile->queue_index = index; | 418 | ntfile->queue_index = index; |
| 420 | 419 | ||
| 421 | --tun->numqueues; | 420 | --tun->numqueues; |
| @@ -429,8 +428,10 @@ static void __tun_detach(struct tun_file *tfile, bool clean) | |||
| 429 | /* Drop read queue */ | 428 | /* Drop read queue */ |
| 430 | skb_queue_purge(&tfile->sk.sk_receive_queue); | 429 | skb_queue_purge(&tfile->sk.sk_receive_queue); |
| 431 | tun_set_real_num_queues(tun); | 430 | tun_set_real_num_queues(tun); |
| 432 | } else if (tfile->detached && clean) | 431 | } else if (tfile->detached && clean) { |
| 433 | tun = tun_enable_queue(tfile); | 432 | tun = tun_enable_queue(tfile); |
| 433 | sock_put(&tfile->sk); | ||
| 434 | } | ||
| 434 | 435 | ||
| 435 | if (clean) { | 436 | if (clean) { |
| 436 | if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && | 437 | if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && |
| @@ -458,8 +459,7 @@ static void tun_detach_all(struct net_device *dev) | |||
| 458 | int i, n = tun->numqueues; | 459 | int i, n = tun->numqueues; |
| 459 | 460 | ||
| 460 | for (i = 0; i < n; i++) { | 461 | for (i = 0; i < n; i++) { |
| 461 | tfile = rcu_dereference_protected(tun->tfiles[i], | 462 | tfile = rtnl_dereference(tun->tfiles[i]); |
| 462 | lockdep_rtnl_is_held()); | ||
| 463 | BUG_ON(!tfile); | 463 | BUG_ON(!tfile); |
| 464 | wake_up_all(&tfile->wq.wait); | 464 | wake_up_all(&tfile->wq.wait); |
| 465 | rcu_assign_pointer(tfile->tun, NULL); | 465 | rcu_assign_pointer(tfile->tun, NULL); |
| @@ -469,8 +469,7 @@ static void tun_detach_all(struct net_device *dev) | |||
| 469 | 469 | ||
| 470 | synchronize_net(); | 470 | synchronize_net(); |
| 471 | for (i = 0; i < n; i++) { | 471 | for (i = 0; i < n; i++) { |
| 472 | tfile = rcu_dereference_protected(tun->tfiles[i], | 472 | tfile = rtnl_dereference(tun->tfiles[i]); |
| 473 | lockdep_rtnl_is_held()); | ||
| 474 | /* Drop read queue */ | 473 | /* Drop read queue */ |
| 475 | skb_queue_purge(&tfile->sk.sk_receive_queue); | 474 | skb_queue_purge(&tfile->sk.sk_receive_queue); |
| 476 | sock_put(&tfile->sk); | 475 | sock_put(&tfile->sk); |
| @@ -481,6 +480,9 @@ static void tun_detach_all(struct net_device *dev) | |||
| 481 | sock_put(&tfile->sk); | 480 | sock_put(&tfile->sk); |
| 482 | } | 481 | } |
| 483 | BUG_ON(tun->numdisabled != 0); | 482 | BUG_ON(tun->numdisabled != 0); |
| 483 | |||
| 484 | if (tun->flags & TUN_PERSIST) | ||
| 485 | module_put(THIS_MODULE); | ||
| 484 | } | 486 | } |
| 485 | 487 | ||
| 486 | static int tun_attach(struct tun_struct *tun, struct file *file) | 488 | static int tun_attach(struct tun_struct *tun, struct file *file) |
| @@ -489,7 +491,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file) | |||
| 489 | int err; | 491 | int err; |
| 490 | 492 | ||
| 491 | err = -EINVAL; | 493 | err = -EINVAL; |
| 492 | if (rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held())) | 494 | if (rtnl_dereference(tfile->tun)) |
| 493 | goto out; | 495 | goto out; |
| 494 | 496 | ||
| 495 | err = -EBUSY; | 497 | err = -EBUSY; |
| @@ -1544,6 +1546,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
| 1544 | struct net_device *dev; | 1546 | struct net_device *dev; |
| 1545 | int err; | 1547 | int err; |
| 1546 | 1548 | ||
| 1549 | if (tfile->detached) | ||
| 1550 | return -EINVAL; | ||
| 1551 | |||
| 1547 | dev = __dev_get_by_name(net, ifr->ifr_name); | 1552 | dev = __dev_get_by_name(net, ifr->ifr_name); |
| 1548 | if (dev) { | 1553 | if (dev) { |
| 1549 | if (ifr->ifr_flags & IFF_TUN_EXCL) | 1554 | if (ifr->ifr_flags & IFF_TUN_EXCL) |
| @@ -1738,8 +1743,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n) | |||
| 1738 | struct tun_file *tfile; | 1743 | struct tun_file *tfile; |
| 1739 | 1744 | ||
| 1740 | for (i = 0; i < n; i++) { | 1745 | for (i = 0; i < n; i++) { |
| 1741 | tfile = rcu_dereference_protected(tun->tfiles[i], | 1746 | tfile = rtnl_dereference(tun->tfiles[i]); |
| 1742 | lockdep_rtnl_is_held()); | ||
| 1743 | sk_detach_filter(tfile->socket.sk); | 1747 | sk_detach_filter(tfile->socket.sk); |
| 1744 | } | 1748 | } |
| 1745 | 1749 | ||
| @@ -1752,8 +1756,7 @@ static int tun_attach_filter(struct tun_struct *tun) | |||
| 1752 | struct tun_file *tfile; | 1756 | struct tun_file *tfile; |
| 1753 | 1757 | ||
| 1754 | for (i = 0; i < tun->numqueues; i++) { | 1758 | for (i = 0; i < tun->numqueues; i++) { |
| 1755 | tfile = rcu_dereference_protected(tun->tfiles[i], | 1759 | tfile = rtnl_dereference(tun->tfiles[i]); |
| 1756 | lockdep_rtnl_is_held()); | ||
| 1757 | ret = sk_attach_filter(&tun->fprog, tfile->socket.sk); | 1760 | ret = sk_attach_filter(&tun->fprog, tfile->socket.sk); |
| 1758 | if (ret) { | 1761 | if (ret) { |
| 1759 | tun_detach_filter(tun, i); | 1762 | tun_detach_filter(tun, i); |
| @@ -1771,8 +1774,7 @@ static void tun_set_sndbuf(struct tun_struct *tun) | |||
| 1771 | int i; | 1774 | int i; |
| 1772 | 1775 | ||
| 1773 | for (i = 0; i < tun->numqueues; i++) { | 1776 | for (i = 0; i < tun->numqueues; i++) { |
| 1774 | tfile = rcu_dereference_protected(tun->tfiles[i], | 1777 | tfile = rtnl_dereference(tun->tfiles[i]); |
| 1775 | lockdep_rtnl_is_held()); | ||
| 1776 | tfile->socket.sk->sk_sndbuf = tun->sndbuf; | 1778 | tfile->socket.sk->sk_sndbuf = tun->sndbuf; |
| 1777 | } | 1779 | } |
| 1778 | } | 1780 | } |
| @@ -1789,13 +1791,10 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) | |||
| 1789 | tun = tfile->detached; | 1791 | tun = tfile->detached; |
| 1790 | if (!tun) | 1792 | if (!tun) |
| 1791 | ret = -EINVAL; | 1793 | ret = -EINVAL; |
| 1792 | else if (tun_not_capable(tun)) | ||
| 1793 | ret = -EPERM; | ||
| 1794 | else | 1794 | else |
| 1795 | ret = tun_attach(tun, file); | 1795 | ret = tun_attach(tun, file); |
| 1796 | } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { | 1796 | } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { |
| 1797 | tun = rcu_dereference_protected(tfile->tun, | 1797 | tun = rtnl_dereference(tfile->tun); |
| 1798 | lockdep_rtnl_is_held()); | ||
| 1799 | if (!tun || !(tun->flags & TUN_TAP_MQ)) | 1798 | if (!tun || !(tun->flags & TUN_TAP_MQ)) |
| 1800 | ret = -EINVAL; | 1799 | ret = -EINVAL; |
| 1801 | else | 1800 | else |
| @@ -1880,10 +1879,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
| 1880 | /* Disable/Enable persist mode. Keep an extra reference to the | 1879 | /* Disable/Enable persist mode. Keep an extra reference to the |
| 1881 | * module to prevent the module being unprobed. | 1880 | * module to prevent the module being unprobed. |
| 1882 | */ | 1881 | */ |
| 1883 | if (arg) { | 1882 | if (arg && !(tun->flags & TUN_PERSIST)) { |
| 1884 | tun->flags |= TUN_PERSIST; | 1883 | tun->flags |= TUN_PERSIST; |
| 1885 | __module_get(THIS_MODULE); | 1884 | __module_get(THIS_MODULE); |
| 1886 | } else { | 1885 | } |
| 1886 | if (!arg && (tun->flags & TUN_PERSIST)) { | ||
| 1887 | tun->flags &= ~TUN_PERSIST; | 1887 | tun->flags &= ~TUN_PERSIST; |
| 1888 | module_put(THIS_MODULE); | 1888 | module_put(THIS_MODULE); |
| 1889 | } | 1889 | } |
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1a67a4f829fe..2c02b4e84094 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig | |||
| @@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig" | |||
| 30 | source "drivers/net/wireless/ath/carl9170/Kconfig" | 30 | source "drivers/net/wireless/ath/carl9170/Kconfig" |
| 31 | source "drivers/net/wireless/ath/ath6kl/Kconfig" | 31 | source "drivers/net/wireless/ath/ath6kl/Kconfig" |
| 32 | source "drivers/net/wireless/ath/ar5523/Kconfig" | 32 | source "drivers/net/wireless/ath/ar5523/Kconfig" |
| 33 | source "drivers/net/wireless/ath/wil6210/Kconfig" | ||
| 33 | 34 | ||
| 34 | endif | 35 | endif |
diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 1e18621326dc..97b964ded2be 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile | |||
| @@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/ | |||
| 3 | obj-$(CONFIG_CARL9170) += carl9170/ | 3 | obj-$(CONFIG_CARL9170) += carl9170/ |
| 4 | obj-$(CONFIG_ATH6KL) += ath6kl/ | 4 | obj-$(CONFIG_ATH6KL) += ath6kl/ |
| 5 | obj-$(CONFIG_AR5523) += ar5523/ | 5 | obj-$(CONFIG_AR5523) += ar5523/ |
| 6 | obj-$(CONFIG_WIL6210) += wil6210/ | ||
| 6 | 7 | ||
| 7 | obj-$(CONFIG_ATH_COMMON) += ath.o | 8 | obj-$(CONFIG_ATH_COMMON) += ath.o |
| 8 | 9 | ||
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig new file mode 100644 index 000000000000..bac3d98a0cfb --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Kconfig | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | config WIL6210 | ||
| 2 | tristate "Wilocity 60g WiFi card wil6210 support" | ||
| 3 | depends on CFG80211 | ||
| 4 | depends on PCI | ||
| 5 | default n | ||
| 6 | ---help--- | ||
| 7 | This module adds support for wireless adapter based on | ||
| 8 | wil6210 chip by Wilocity. It supports operation on the | ||
| 9 | 60 GHz band, covered by the IEEE802.11ad standard. | ||
| 10 | |||
| 11 | http://wireless.kernel.org/en/users/Drivers/wil6210 | ||
| 12 | |||
| 13 | If you choose to build it as a module, it will be called | ||
| 14 | wil6210 | ||
| 15 | |||
| 16 | config WIL6210_ISR_COR | ||
| 17 | bool "Use Clear-On-Read mode for ISR registers for wil6210" | ||
| 18 | depends on WIL6210 | ||
| 19 | default y | ||
| 20 | ---help--- | ||
| 21 | ISR registers on wil6210 chip may operate in either | ||
| 22 | COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode. | ||
| 23 | For production code, use COR (say y); is default since | ||
| 24 | it saves extra target transaction; | ||
| 25 | For ISR debug, use W1C (say n); is allows to monitor ISR | ||
| 26 | registers with debugfs. If COR were used, ISR would | ||
| 27 | self-clear when accessed for debug purposes, it makes | ||
| 28 | such monitoring impossible. | ||
| 29 | Say y unless you debug interrupts | ||
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile new file mode 100644 index 000000000000..9396dc9fe3c5 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Makefile | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | obj-$(CONFIG_WIL6210) += wil6210.o | ||
| 2 | |||
| 3 | wil6210-objs := main.o | ||
| 4 | wil6210-objs += netdev.o | ||
| 5 | wil6210-objs += cfg80211.o | ||
| 6 | wil6210-objs += pcie_bus.o | ||
| 7 | wil6210-objs += debugfs.o | ||
| 8 | wil6210-objs += wmi.o | ||
| 9 | wil6210-objs += interrupt.o | ||
| 10 | wil6210-objs += txrx.o | ||
| 11 | |||
| 12 | subdir-ccflags-y += -Werror | ||
| 13 | subdir-ccflags-y += -D__CHECK_ENDIAN__ | ||
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c new file mode 100644 index 000000000000..116f4e807ae1 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c | |||
| @@ -0,0 +1,573 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/netdevice.h> | ||
| 19 | #include <linux/sched.h> | ||
| 20 | #include <linux/etherdevice.h> | ||
| 21 | #include <linux/wireless.h> | ||
| 22 | #include <linux/ieee80211.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/version.h> | ||
| 25 | #include <net/cfg80211.h> | ||
| 26 | |||
| 27 | #include "wil6210.h" | ||
| 28 | #include "wmi.h" | ||
| 29 | |||
| 30 | #define CHAN60G(_channel, _flags) { \ | ||
| 31 | .band = IEEE80211_BAND_60GHZ, \ | ||
| 32 | .center_freq = 56160 + (2160 * (_channel)), \ | ||
| 33 | .hw_value = (_channel), \ | ||
| 34 | .flags = (_flags), \ | ||
| 35 | .max_antenna_gain = 0, \ | ||
| 36 | .max_power = 40, \ | ||
| 37 | } | ||
| 38 | |||
| 39 | static struct ieee80211_channel wil_60ghz_channels[] = { | ||
| 40 | CHAN60G(1, 0), | ||
| 41 | CHAN60G(2, 0), | ||
| 42 | CHAN60G(3, 0), | ||
| 43 | /* channel 4 not supported yet */ | ||
| 44 | }; | ||
| 45 | |||
| 46 | static struct ieee80211_supported_band wil_band_60ghz = { | ||
| 47 | .channels = wil_60ghz_channels, | ||
| 48 | .n_channels = ARRAY_SIZE(wil_60ghz_channels), | ||
| 49 | .ht_cap = { | ||
| 50 | .ht_supported = true, | ||
| 51 | .cap = 0, /* TODO */ | ||
| 52 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ | ||
| 53 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ | ||
| 54 | .mcs = { | ||
| 55 | /* MCS 1..12 - SC PHY */ | ||
| 56 | .rx_mask = {0xfe, 0x1f}, /* 1..12 */ | ||
| 57 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ | ||
| 58 | }, | ||
| 59 | }, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static const struct ieee80211_txrx_stypes | ||
| 63 | wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { | ||
| 64 | [NL80211_IFTYPE_STATION] = { | ||
| 65 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 66 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
| 67 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 68 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
| 69 | }, | ||
| 70 | [NL80211_IFTYPE_AP] = { | ||
| 71 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 72 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
| 73 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 74 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
| 75 | }, | ||
| 76 | [NL80211_IFTYPE_P2P_CLIENT] = { | ||
| 77 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 78 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
| 79 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 80 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
| 81 | }, | ||
| 82 | [NL80211_IFTYPE_P2P_GO] = { | ||
| 83 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 84 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
| 85 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
| 86 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
| 87 | }, | ||
| 88 | }; | ||
| 89 | |||
| 90 | static const u32 wil_cipher_suites[] = { | ||
| 91 | WLAN_CIPHER_SUITE_GCMP, | ||
| 92 | }; | ||
| 93 | |||
| 94 | int wil_iftype_nl2wmi(enum nl80211_iftype type) | ||
| 95 | { | ||
| 96 | static const struct { | ||
| 97 | enum nl80211_iftype nl; | ||
| 98 | enum wmi_network_type wmi; | ||
| 99 | } __nl2wmi[] = { | ||
| 100 | {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC}, | ||
| 101 | {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA}, | ||
| 102 | {NL80211_IFTYPE_AP, WMI_NETTYPE_AP}, | ||
| 103 | {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P}, | ||
| 104 | {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P}, | ||
| 105 | {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */ | ||
| 106 | }; | ||
| 107 | uint i; | ||
| 108 | |||
| 109 | for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { | ||
| 110 | if (__nl2wmi[i].nl == type) | ||
| 111 | return __nl2wmi[i].wmi; | ||
| 112 | } | ||
| 113 | |||
| 114 | return -EOPNOTSUPP; | ||
| 115 | } | ||
| 116 | |||
| 117 | static int wil_cfg80211_get_station(struct wiphy *wiphy, | ||
| 118 | struct net_device *ndev, | ||
| 119 | u8 *mac, struct station_info *sinfo) | ||
| 120 | { | ||
| 121 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 122 | int rc; | ||
| 123 | struct wmi_notify_req_cmd cmd = { | ||
| 124 | .cid = 0, | ||
| 125 | .interval_usec = 0, | ||
| 126 | }; | ||
| 127 | |||
| 128 | if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) | ||
| 129 | return -ENOENT; | ||
| 130 | |||
| 131 | /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ | ||
| 132 | rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), | ||
| 133 | WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); | ||
| 134 | if (rc) | ||
| 135 | return rc; | ||
| 136 | |||
| 137 | sinfo->generation = wil->sinfo_gen; | ||
| 138 | |||
| 139 | sinfo->filled |= STATION_INFO_TX_BITRATE; | ||
| 140 | sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; | ||
| 141 | sinfo->txrate.mcs = wil->stats.bf_mcs; | ||
| 142 | sinfo->filled |= STATION_INFO_RX_BITRATE; | ||
| 143 | sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; | ||
| 144 | sinfo->rxrate.mcs = wil->stats.last_mcs_rx; | ||
| 145 | |||
| 146 | if (test_bit(wil_status_fwconnected, &wil->status)) { | ||
| 147 | sinfo->filled |= STATION_INFO_SIGNAL; | ||
| 148 | sinfo->signal = 12; /* TODO: provide real value */ | ||
| 149 | } | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | static int wil_cfg80211_change_iface(struct wiphy *wiphy, | ||
| 155 | struct net_device *ndev, | ||
| 156 | enum nl80211_iftype type, u32 *flags, | ||
| 157 | struct vif_params *params) | ||
| 158 | { | ||
| 159 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 160 | struct wireless_dev *wdev = wil->wdev; | ||
| 161 | |||
| 162 | switch (type) { | ||
| 163 | case NL80211_IFTYPE_STATION: | ||
| 164 | case NL80211_IFTYPE_AP: | ||
| 165 | case NL80211_IFTYPE_P2P_CLIENT: | ||
| 166 | case NL80211_IFTYPE_P2P_GO: | ||
| 167 | break; | ||
| 168 | case NL80211_IFTYPE_MONITOR: | ||
| 169 | if (flags) | ||
| 170 | wil->monitor_flags = *flags; | ||
| 171 | else | ||
| 172 | wil->monitor_flags = 0; | ||
| 173 | |||
| 174 | break; | ||
| 175 | default: | ||
| 176 | return -EOPNOTSUPP; | ||
| 177 | } | ||
| 178 | |||
| 179 | wdev->iftype = type; | ||
| 180 | |||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | static int wil_cfg80211_scan(struct wiphy *wiphy, | ||
| 185 | struct cfg80211_scan_request *request) | ||
| 186 | { | ||
| 187 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 188 | struct wireless_dev *wdev = wil->wdev; | ||
| 189 | struct { | ||
| 190 | struct wmi_start_scan_cmd cmd; | ||
| 191 | u16 chnl[4]; | ||
| 192 | } __packed cmd; | ||
| 193 | uint i, n; | ||
| 194 | |||
| 195 | if (wil->scan_request) { | ||
| 196 | wil_err(wil, "Already scanning\n"); | ||
| 197 | return -EAGAIN; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* check we are client side */ | ||
| 201 | switch (wdev->iftype) { | ||
| 202 | case NL80211_IFTYPE_STATION: | ||
| 203 | case NL80211_IFTYPE_P2P_CLIENT: | ||
| 204 | break; | ||
| 205 | default: | ||
| 206 | return -EOPNOTSUPP; | ||
| 207 | |||
| 208 | } | ||
| 209 | |||
| 210 | /* FW don't support scan after connection attempt */ | ||
| 211 | if (test_bit(wil_status_dontscan, &wil->status)) { | ||
| 212 | wil_err(wil, "Scan after connect attempt not supported\n"); | ||
| 213 | return -EBUSY; | ||
| 214 | } | ||
| 215 | |||
| 216 | wil->scan_request = request; | ||
| 217 | |||
| 218 | memset(&cmd, 0, sizeof(cmd)); | ||
| 219 | cmd.cmd.num_channels = 0; | ||
| 220 | n = min(request->n_channels, 4U); | ||
| 221 | for (i = 0; i < n; i++) { | ||
| 222 | int ch = request->channels[i]->hw_value; | ||
| 223 | if (ch == 0) { | ||
| 224 | wil_err(wil, | ||
| 225 | "Scan requested for unknown frequency %dMhz\n", | ||
| 226 | request->channels[i]->center_freq); | ||
| 227 | continue; | ||
| 228 | } | ||
| 229 | /* 0-based channel indexes */ | ||
| 230 | cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; | ||
| 231 | wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch, | ||
| 232 | request->channels[i]->center_freq); | ||
| 233 | } | ||
| 234 | |||
| 235 | return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + | ||
| 236 | cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); | ||
| 237 | } | ||
| 238 | |||
| 239 | static int wil_cfg80211_connect(struct wiphy *wiphy, | ||
| 240 | struct net_device *ndev, | ||
| 241 | struct cfg80211_connect_params *sme) | ||
| 242 | { | ||
| 243 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 244 | struct cfg80211_bss *bss; | ||
| 245 | struct wmi_connect_cmd conn; | ||
| 246 | const u8 *ssid_eid; | ||
| 247 | const u8 *rsn_eid; | ||
| 248 | int ch; | ||
| 249 | int rc = 0; | ||
| 250 | |||
| 251 | bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, | ||
| 252 | sme->ssid, sme->ssid_len, | ||
| 253 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | ||
| 254 | if (!bss) { | ||
| 255 | wil_err(wil, "Unable to find BSS\n"); | ||
| 256 | return -ENOENT; | ||
| 257 | } | ||
| 258 | |||
| 259 | ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); | ||
| 260 | if (!ssid_eid) { | ||
| 261 | wil_err(wil, "No SSID\n"); | ||
| 262 | rc = -ENOENT; | ||
| 263 | goto out; | ||
| 264 | } | ||
| 265 | |||
| 266 | rsn_eid = sme->ie ? | ||
| 267 | cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : | ||
| 268 | NULL; | ||
| 269 | if (rsn_eid) { | ||
| 270 | if (sme->ie_len > WMI_MAX_IE_LEN) { | ||
| 271 | rc = -ERANGE; | ||
| 272 | wil_err(wil, "IE too large (%td bytes)\n", | ||
| 273 | sme->ie_len); | ||
| 274 | goto out; | ||
| 275 | } | ||
| 276 | /* | ||
| 277 | * For secure assoc, send: | ||
| 278 | * (1) WMI_DELETE_CIPHER_KEY_CMD | ||
| 279 | * (2) WMI_SET_APPIE_CMD | ||
| 280 | */ | ||
| 281 | rc = wmi_del_cipher_key(wil, 0, bss->bssid); | ||
| 282 | if (rc) { | ||
| 283 | wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); | ||
| 284 | goto out; | ||
| 285 | } | ||
| 286 | /* WMI_SET_APPIE_CMD */ | ||
| 287 | rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); | ||
| 288 | if (rc) { | ||
| 289 | wil_err(wil, "WMI_SET_APPIE_CMD failed\n"); | ||
| 290 | goto out; | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | /* WMI_CONNECT_CMD */ | ||
| 295 | memset(&conn, 0, sizeof(conn)); | ||
| 296 | switch (bss->capability & 0x03) { | ||
| 297 | case WLAN_CAPABILITY_DMG_TYPE_AP: | ||
| 298 | conn.network_type = WMI_NETTYPE_INFRA; | ||
| 299 | break; | ||
| 300 | case WLAN_CAPABILITY_DMG_TYPE_PBSS: | ||
| 301 | conn.network_type = WMI_NETTYPE_P2P; | ||
| 302 | break; | ||
| 303 | default: | ||
| 304 | wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", | ||
| 305 | bss->capability); | ||
| 306 | goto out; | ||
| 307 | } | ||
| 308 | if (rsn_eid) { | ||
| 309 | conn.dot11_auth_mode = WMI_AUTH11_SHARED; | ||
| 310 | conn.auth_mode = WMI_AUTH_WPA2_PSK; | ||
| 311 | conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; | ||
| 312 | conn.pairwise_crypto_len = 16; | ||
| 313 | } else { | ||
| 314 | conn.dot11_auth_mode = WMI_AUTH11_OPEN; | ||
| 315 | conn.auth_mode = WMI_AUTH_NONE; | ||
| 316 | } | ||
| 317 | |||
| 318 | conn.ssid_len = min_t(u8, ssid_eid[1], 32); | ||
| 319 | memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); | ||
| 320 | |||
| 321 | ch = bss->channel->hw_value; | ||
| 322 | if (ch == 0) { | ||
| 323 | wil_err(wil, "BSS at unknown frequency %dMhz\n", | ||
| 324 | bss->channel->center_freq); | ||
| 325 | rc = -EOPNOTSUPP; | ||
| 326 | goto out; | ||
| 327 | } | ||
| 328 | conn.channel = ch - 1; | ||
| 329 | |||
| 330 | memcpy(conn.bssid, bss->bssid, 6); | ||
| 331 | memcpy(conn.dst_mac, bss->bssid, 6); | ||
| 332 | /* | ||
| 333 | * FW don't support scan after connection attempt | ||
| 334 | */ | ||
| 335 | set_bit(wil_status_dontscan, &wil->status); | ||
| 336 | |||
| 337 | rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); | ||
| 338 | if (rc == 0) { | ||
| 339 | /* Connect can take lots of time */ | ||
| 340 | mod_timer(&wil->connect_timer, | ||
| 341 | jiffies + msecs_to_jiffies(2000)); | ||
| 342 | } | ||
| 343 | |||
| 344 | out: | ||
| 345 | cfg80211_put_bss(bss); | ||
| 346 | |||
| 347 | return rc; | ||
| 348 | } | ||
| 349 | |||
| 350 | static int wil_cfg80211_disconnect(struct wiphy *wiphy, | ||
| 351 | struct net_device *ndev, | ||
| 352 | u16 reason_code) | ||
| 353 | { | ||
| 354 | int rc; | ||
| 355 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 356 | |||
| 357 | rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); | ||
| 358 | |||
| 359 | return rc; | ||
| 360 | } | ||
| 361 | |||
| 362 | static int wil_cfg80211_set_channel(struct wiphy *wiphy, | ||
| 363 | struct cfg80211_chan_def *chandef) | ||
| 364 | { | ||
| 365 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 366 | struct wireless_dev *wdev = wil->wdev; | ||
| 367 | |||
| 368 | wdev->preset_chandef = *chandef; | ||
| 369 | |||
| 370 | return 0; | ||
| 371 | } | ||
| 372 | |||
| 373 | static int wil_cfg80211_add_key(struct wiphy *wiphy, | ||
| 374 | struct net_device *ndev, | ||
| 375 | u8 key_index, bool pairwise, | ||
| 376 | const u8 *mac_addr, | ||
| 377 | struct key_params *params) | ||
| 378 | { | ||
| 379 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 380 | |||
| 381 | /* group key is not used */ | ||
| 382 | if (!pairwise) | ||
| 383 | return 0; | ||
| 384 | |||
| 385 | return wmi_add_cipher_key(wil, key_index, mac_addr, | ||
| 386 | params->key_len, params->key); | ||
| 387 | } | ||
| 388 | |||
| 389 | static int wil_cfg80211_del_key(struct wiphy *wiphy, | ||
| 390 | struct net_device *ndev, | ||
| 391 | u8 key_index, bool pairwise, | ||
| 392 | const u8 *mac_addr) | ||
| 393 | { | ||
| 394 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 395 | |||
| 396 | /* group key is not used */ | ||
| 397 | if (!pairwise) | ||
| 398 | return 0; | ||
| 399 | |||
| 400 | return wmi_del_cipher_key(wil, key_index, mac_addr); | ||
| 401 | } | ||
| 402 | |||
| 403 | /* Need to be present or wiphy_new() will WARN */ | ||
| 404 | static int wil_cfg80211_set_default_key(struct wiphy *wiphy, | ||
| 405 | struct net_device *ndev, | ||
| 406 | u8 key_index, bool unicast, | ||
| 407 | bool multicast) | ||
| 408 | { | ||
| 409 | return 0; | ||
| 410 | } | ||
| 411 | |||
| 412 | static int wil_cfg80211_start_ap(struct wiphy *wiphy, | ||
| 413 | struct net_device *ndev, | ||
| 414 | struct cfg80211_ap_settings *info) | ||
| 415 | { | ||
| 416 | int rc = 0; | ||
| 417 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 418 | struct wireless_dev *wdev = ndev->ieee80211_ptr; | ||
| 419 | struct ieee80211_channel *channel = info->chandef.chan; | ||
| 420 | struct cfg80211_beacon_data *bcon = &info->beacon; | ||
| 421 | u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); | ||
| 422 | |||
| 423 | if (!channel) { | ||
| 424 | wil_err(wil, "AP: No channel???\n"); | ||
| 425 | return -EINVAL; | ||
| 426 | } | ||
| 427 | |||
| 428 | wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, | ||
| 429 | channel->center_freq, info->privacy ? "secure" : "open"); | ||
| 430 | print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, | ||
| 431 | info->ssid, info->ssid_len); | ||
| 432 | |||
| 433 | rc = wil_reset(wil); | ||
| 434 | if (rc) | ||
| 435 | return rc; | ||
| 436 | |||
| 437 | rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); | ||
| 438 | if (rc) | ||
| 439 | return rc; | ||
| 440 | |||
| 441 | rc = wmi_set_channel(wil, channel->hw_value); | ||
| 442 | if (rc) | ||
| 443 | return rc; | ||
| 444 | |||
| 445 | /* MAC address - pre-requisite for other commands */ | ||
| 446 | wmi_set_mac_address(wil, ndev->dev_addr); | ||
| 447 | |||
| 448 | /* IE's */ | ||
| 449 | /* bcon 'head IE's are not relevant for 60g band */ | ||
| 450 | wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, | ||
| 451 | bcon->beacon_ies); | ||
| 452 | wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, | ||
| 453 | bcon->proberesp_ies); | ||
| 454 | wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, | ||
| 455 | bcon->assocresp_ies); | ||
| 456 | |||
| 457 | wil->secure_pcp = info->privacy; | ||
| 458 | |||
| 459 | rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype); | ||
| 460 | if (rc) | ||
| 461 | return rc; | ||
| 462 | |||
| 463 | /* Rx VRING. After MAC and beacon */ | ||
| 464 | rc = wil_rx_init(wil); | ||
| 465 | |||
| 466 | netif_carrier_on(ndev); | ||
| 467 | |||
| 468 | return rc; | ||
| 469 | } | ||
| 470 | |||
| 471 | static int wil_cfg80211_stop_ap(struct wiphy *wiphy, | ||
| 472 | struct net_device *ndev) | ||
| 473 | { | ||
| 474 | int rc = 0; | ||
| 475 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
| 476 | struct wireless_dev *wdev = ndev->ieee80211_ptr; | ||
| 477 | u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); | ||
| 478 | |||
| 479 | /* To stop beaconing, set BI to 0 */ | ||
| 480 | rc = wmi_set_bcon(wil, 0, wmi_nettype); | ||
| 481 | |||
| 482 | return rc; | ||
| 483 | } | ||
| 484 | |||
| 485 | static struct cfg80211_ops wil_cfg80211_ops = { | ||
| 486 | .scan = wil_cfg80211_scan, | ||
| 487 | .connect = wil_cfg80211_connect, | ||
| 488 | .disconnect = wil_cfg80211_disconnect, | ||
| 489 | .change_virtual_intf = wil_cfg80211_change_iface, | ||
| 490 | .get_station = wil_cfg80211_get_station, | ||
| 491 | .set_monitor_channel = wil_cfg80211_set_channel, | ||
| 492 | .add_key = wil_cfg80211_add_key, | ||
| 493 | .del_key = wil_cfg80211_del_key, | ||
| 494 | .set_default_key = wil_cfg80211_set_default_key, | ||
| 495 | /* AP mode */ | ||
| 496 | .start_ap = wil_cfg80211_start_ap, | ||
| 497 | .stop_ap = wil_cfg80211_stop_ap, | ||
| 498 | }; | ||
| 499 | |||
| 500 | static void wil_wiphy_init(struct wiphy *wiphy) | ||
| 501 | { | ||
| 502 | /* TODO: set real value */ | ||
| 503 | wiphy->max_scan_ssids = 10; | ||
| 504 | wiphy->max_num_pmkids = 0 /* TODO: */; | ||
| 505 | wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | ||
| 506 | BIT(NL80211_IFTYPE_AP) | | ||
| 507 | BIT(NL80211_IFTYPE_MONITOR); | ||
| 508 | /* TODO: enable P2P when integrated with supplicant: | ||
| 509 | * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | ||
| 510 | */ | ||
| 511 | wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | | ||
| 512 | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; | ||
| 513 | dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", | ||
| 514 | __func__, wiphy->flags); | ||
| 515 | wiphy->probe_resp_offload = | ||
| 516 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | | ||
| 517 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | | ||
| 518 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; | ||
| 519 | |||
| 520 | wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; | ||
| 521 | |||
| 522 | /* TODO: figure this out */ | ||
| 523 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | ||
| 524 | |||
| 525 | wiphy->cipher_suites = wil_cipher_suites; | ||
| 526 | wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); | ||
| 527 | wiphy->mgmt_stypes = wil_mgmt_stypes; | ||
| 528 | } | ||
| 529 | |||
| 530 | struct wireless_dev *wil_cfg80211_init(struct device *dev) | ||
| 531 | { | ||
| 532 | int rc = 0; | ||
| 533 | struct wireless_dev *wdev; | ||
| 534 | |||
| 535 | wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); | ||
| 536 | if (!wdev) | ||
| 537 | return ERR_PTR(-ENOMEM); | ||
| 538 | |||
| 539 | wdev->wiphy = wiphy_new(&wil_cfg80211_ops, | ||
| 540 | sizeof(struct wil6210_priv)); | ||
| 541 | if (!wdev->wiphy) { | ||
| 542 | rc = -ENOMEM; | ||
| 543 | goto out; | ||
| 544 | } | ||
| 545 | |||
| 546 | set_wiphy_dev(wdev->wiphy, dev); | ||
| 547 | wil_wiphy_init(wdev->wiphy); | ||
| 548 | |||
| 549 | rc = wiphy_register(wdev->wiphy); | ||
| 550 | if (rc < 0) | ||
| 551 | goto out_failed_reg; | ||
| 552 | |||
| 553 | return wdev; | ||
| 554 | |||
| 555 | out_failed_reg: | ||
| 556 | wiphy_free(wdev->wiphy); | ||
| 557 | out: | ||
| 558 | kfree(wdev); | ||
| 559 | |||
| 560 | return ERR_PTR(rc); | ||
| 561 | } | ||
| 562 | |||
| 563 | void wil_wdev_free(struct wil6210_priv *wil) | ||
| 564 | { | ||
| 565 | struct wireless_dev *wdev = wil_to_wdev(wil); | ||
| 566 | |||
| 567 | if (!wdev) | ||
| 568 | return; | ||
| 569 | |||
| 570 | wiphy_unregister(wdev->wiphy); | ||
| 571 | wiphy_free(wdev->wiphy); | ||
| 572 | kfree(wdev); | ||
| 573 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h new file mode 100644 index 000000000000..6a315ba5aa7d --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #ifndef WIL_DBG_HEXDUMP_H_ | ||
| 2 | #define WIL_DBG_HEXDUMP_H_ | ||
| 3 | |||
| 4 | #if defined(CONFIG_DYNAMIC_DEBUG) | ||
| 5 | #define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ | ||
| 6 | groupsize, buf, len, ascii) \ | ||
| 7 | do { \ | ||
| 8 | DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \ | ||
| 9 | __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\ | ||
| 10 | if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ | ||
| 11 | print_hex_dump(KERN_DEBUG, prefix_str, \ | ||
| 12 | prefix_type, rowsize, groupsize, \ | ||
| 13 | buf, len, ascii); \ | ||
| 14 | } while (0) | ||
| 15 | |||
| 16 | #define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ | ||
| 17 | groupsize, buf, len, ascii) \ | ||
| 18 | wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ | ||
| 19 | groupsize, buf, len, ascii) | ||
| 20 | |||
| 21 | #define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ | ||
| 22 | wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true) | ||
| 23 | #else /* defined(CONFIG_DYNAMIC_DEBUG) */ | ||
| 24 | #define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ | ||
| 25 | groupsize, buf, len, ascii) \ | ||
| 26 | print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ | ||
| 27 | groupsize, buf, len, ascii) | ||
| 28 | #endif /* defined(CONFIG_DYNAMIC_DEBUG) */ | ||
| 29 | |||
| 30 | #endif /* WIL_DBG_HEXDUMP_H_ */ | ||
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c new file mode 100644 index 000000000000..65fc9683bfd8 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debugfs.c | |||
| @@ -0,0 +1,603 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/debugfs.h> | ||
| 19 | #include <linux/seq_file.h> | ||
| 20 | #include <linux/pci.h> | ||
| 21 | #include <linux/rtnetlink.h> | ||
| 22 | |||
| 23 | #include "wil6210.h" | ||
| 24 | #include "txrx.h" | ||
| 25 | |||
| 26 | /* Nasty hack. Better have per device instances */ | ||
| 27 | static u32 mem_addr; | ||
| 28 | static u32 dbg_txdesc_index; | ||
| 29 | |||
| 30 | static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, | ||
| 31 | const char *name, struct vring *vring) | ||
| 32 | { | ||
| 33 | void __iomem *x = wmi_addr(wil, vring->hwtail); | ||
| 34 | |||
| 35 | seq_printf(s, "VRING %s = {\n", name); | ||
| 36 | seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); | ||
| 37 | seq_printf(s, " va = 0x%p\n", vring->va); | ||
| 38 | seq_printf(s, " size = %d\n", vring->size); | ||
| 39 | seq_printf(s, " swtail = %d\n", vring->swtail); | ||
| 40 | seq_printf(s, " swhead = %d\n", vring->swhead); | ||
| 41 | seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); | ||
| 42 | if (x) | ||
| 43 | seq_printf(s, "0x%08x\n", ioread32(x)); | ||
| 44 | else | ||
| 45 | seq_printf(s, "???\n"); | ||
| 46 | |||
| 47 | if (vring->va && (vring->size < 1025)) { | ||
| 48 | uint i; | ||
| 49 | for (i = 0; i < vring->size; i++) { | ||
| 50 | volatile struct vring_tx_desc *d = &vring->va[i].tx; | ||
| 51 | if ((i % 64) == 0 && (i != 0)) | ||
| 52 | seq_printf(s, "\n"); | ||
| 53 | seq_printf(s, "%s", (d->dma.status & BIT(0)) ? | ||
| 54 | "S" : (vring->ctx[i] ? "H" : "h")); | ||
| 55 | } | ||
| 56 | seq_printf(s, "\n"); | ||
| 57 | } | ||
| 58 | seq_printf(s, "}\n"); | ||
| 59 | } | ||
| 60 | |||
| 61 | static int wil_vring_debugfs_show(struct seq_file *s, void *data) | ||
| 62 | { | ||
| 63 | uint i; | ||
| 64 | struct wil6210_priv *wil = s->private; | ||
| 65 | |||
| 66 | wil_print_vring(s, wil, "rx", &wil->vring_rx); | ||
| 67 | |||
| 68 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { | ||
| 69 | struct vring *vring = &(wil->vring_tx[i]); | ||
| 70 | if (vring->va) { | ||
| 71 | char name[10]; | ||
| 72 | snprintf(name, sizeof(name), "tx_%2d", i); | ||
| 73 | wil_print_vring(s, wil, name, vring); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | static int wil_vring_seq_open(struct inode *inode, struct file *file) | ||
| 81 | { | ||
| 82 | return single_open(file, wil_vring_debugfs_show, inode->i_private); | ||
| 83 | } | ||
| 84 | |||
| 85 | static const struct file_operations fops_vring = { | ||
| 86 | .open = wil_vring_seq_open, | ||
| 87 | .release = single_release, | ||
| 88 | .read = seq_read, | ||
| 89 | .llseek = seq_lseek, | ||
| 90 | }; | ||
| 91 | |||
| 92 | static void wil_print_ring(struct seq_file *s, const char *prefix, | ||
| 93 | void __iomem *off) | ||
| 94 | { | ||
| 95 | struct wil6210_priv *wil = s->private; | ||
| 96 | struct wil6210_mbox_ring r; | ||
| 97 | int rsize; | ||
| 98 | uint i; | ||
| 99 | |||
| 100 | wil_memcpy_fromio_32(&r, off, sizeof(r)); | ||
| 101 | wil_mbox_ring_le2cpus(&r); | ||
| 102 | /* | ||
| 103 | * we just read memory block from NIC. This memory may be | ||
| 104 | * garbage. Check validity before using it. | ||
| 105 | */ | ||
| 106 | rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); | ||
| 107 | |||
| 108 | seq_printf(s, "ring %s = {\n", prefix); | ||
| 109 | seq_printf(s, " base = 0x%08x\n", r.base); | ||
| 110 | seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); | ||
| 111 | seq_printf(s, " tail = 0x%08x\n", r.tail); | ||
| 112 | seq_printf(s, " head = 0x%08x\n", r.head); | ||
| 113 | seq_printf(s, " entry size = %d\n", r.entry_size); | ||
| 114 | |||
| 115 | if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { | ||
| 116 | seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", | ||
| 117 | sizeof(struct wil6210_mbox_ring_desc)); | ||
| 118 | goto out; | ||
| 119 | } | ||
| 120 | |||
| 121 | if (!wmi_addr(wil, r.base) || | ||
| 122 | !wmi_addr(wil, r.tail) || | ||
| 123 | !wmi_addr(wil, r.head)) { | ||
| 124 | seq_printf(s, " ??? pointers are garbage?\n"); | ||
| 125 | goto out; | ||
| 126 | } | ||
| 127 | |||
| 128 | for (i = 0; i < rsize; i++) { | ||
| 129 | struct wil6210_mbox_ring_desc d; | ||
| 130 | struct wil6210_mbox_hdr hdr; | ||
| 131 | size_t delta = i * sizeof(d); | ||
| 132 | void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; | ||
| 133 | |||
| 134 | wil_memcpy_fromio_32(&d, x, sizeof(d)); | ||
| 135 | |||
| 136 | seq_printf(s, " [%2x] %s %s%s 0x%08x", i, | ||
| 137 | d.sync ? "F" : "E", | ||
| 138 | (r.tail - r.base == delta) ? "t" : " ", | ||
| 139 | (r.head - r.base == delta) ? "h" : " ", | ||
| 140 | le32_to_cpu(d.addr)); | ||
| 141 | if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { | ||
| 142 | u16 len = le16_to_cpu(hdr.len); | ||
| 143 | seq_printf(s, " -> %04x %04x %04x %02x\n", | ||
| 144 | le16_to_cpu(hdr.seq), len, | ||
| 145 | le16_to_cpu(hdr.type), hdr.flags); | ||
| 146 | if (len <= MAX_MBOXITEM_SIZE) { | ||
| 147 | int n = 0; | ||
| 148 | unsigned char printbuf[16 * 3 + 2]; | ||
| 149 | unsigned char databuf[MAX_MBOXITEM_SIZE]; | ||
| 150 | void __iomem *src = wmi_buffer(wil, d.addr) + | ||
| 151 | sizeof(struct wil6210_mbox_hdr); | ||
| 152 | /* | ||
| 153 | * No need to check @src for validity - | ||
| 154 | * we already validated @d.addr while | ||
| 155 | * reading header | ||
| 156 | */ | ||
| 157 | wil_memcpy_fromio_32(databuf, src, len); | ||
| 158 | while (n < len) { | ||
| 159 | int l = min(len - n, 16); | ||
| 160 | hex_dump_to_buffer(databuf + n, l, | ||
| 161 | 16, 1, printbuf, | ||
| 162 | sizeof(printbuf), | ||
| 163 | false); | ||
| 164 | seq_printf(s, " : %s\n", printbuf); | ||
| 165 | n += l; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } else { | ||
| 169 | seq_printf(s, "\n"); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | out: | ||
| 173 | seq_printf(s, "}\n"); | ||
| 174 | } | ||
| 175 | |||
| 176 | static int wil_mbox_debugfs_show(struct seq_file *s, void *data) | ||
| 177 | { | ||
| 178 | struct wil6210_priv *wil = s->private; | ||
| 179 | |||
| 180 | wil_print_ring(s, "tx", wil->csr + HOST_MBOX + | ||
| 181 | offsetof(struct wil6210_mbox_ctl, tx)); | ||
| 182 | wil_print_ring(s, "rx", wil->csr + HOST_MBOX + | ||
| 183 | offsetof(struct wil6210_mbox_ctl, rx)); | ||
| 184 | |||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | static int wil_mbox_seq_open(struct inode *inode, struct file *file) | ||
| 189 | { | ||
| 190 | return single_open(file, wil_mbox_debugfs_show, inode->i_private); | ||
| 191 | } | ||
| 192 | |||
| 193 | static const struct file_operations fops_mbox = { | ||
| 194 | .open = wil_mbox_seq_open, | ||
| 195 | .release = single_release, | ||
| 196 | .read = seq_read, | ||
| 197 | .llseek = seq_lseek, | ||
| 198 | }; | ||
| 199 | |||
| 200 | static int wil_debugfs_iomem_x32_set(void *data, u64 val) | ||
| 201 | { | ||
| 202 | iowrite32(val, (void __iomem *)data); | ||
| 203 | wmb(); /* make sure write propagated to HW */ | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | static int wil_debugfs_iomem_x32_get(void *data, u64 *val) | ||
| 209 | { | ||
| 210 | *val = ioread32((void __iomem *)data); | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | } | ||
| 214 | |||
| 215 | DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, | ||
| 216 | wil_debugfs_iomem_x32_set, "0x%08llx\n"); | ||
| 217 | |||
| 218 | static struct dentry *wil_debugfs_create_iomem_x32(const char *name, | ||
| 219 | mode_t mode, | ||
| 220 | struct dentry *parent, | ||
| 221 | void __iomem *value) | ||
| 222 | { | ||
| 223 | return debugfs_create_file(name, mode, parent, (void * __force)value, | ||
| 224 | &fops_iomem_x32); | ||
| 225 | } | ||
| 226 | |||
| 227 | static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, | ||
| 228 | const char *name, | ||
| 229 | struct dentry *parent, u32 off) | ||
| 230 | { | ||
| 231 | struct dentry *d = debugfs_create_dir(name, parent); | ||
| 232 | |||
| 233 | if (IS_ERR_OR_NULL(d)) | ||
| 234 | return -ENODEV; | ||
| 235 | |||
| 236 | wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, | ||
| 237 | wil->csr + off); | ||
| 238 | wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, | ||
| 239 | wil->csr + off + 4); | ||
| 240 | wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, | ||
| 241 | wil->csr + off + 8); | ||
| 242 | wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, | ||
| 243 | wil->csr + off + 12); | ||
| 244 | wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, | ||
| 245 | wil->csr + off + 16); | ||
| 246 | wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, | ||
| 247 | wil->csr + off + 20); | ||
| 248 | wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, | ||
| 249 | wil->csr + off + 24); | ||
| 250 | |||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | |||
| 254 | static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, | ||
| 255 | struct dentry *parent) | ||
| 256 | { | ||
| 257 | struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); | ||
| 258 | |||
| 259 | if (IS_ERR_OR_NULL(d)) | ||
| 260 | return -ENODEV; | ||
| 261 | |||
| 262 | wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + | ||
| 263 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); | ||
| 264 | wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + | ||
| 265 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); | ||
| 266 | wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + | ||
| 267 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); | ||
| 268 | |||
| 269 | return 0; | ||
| 270 | } | ||
| 271 | |||
| 272 | static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, | ||
| 273 | struct dentry *parent) | ||
| 274 | { | ||
| 275 | struct dentry *d = debugfs_create_dir("ITR_CNT", parent); | ||
| 276 | |||
| 277 | if (IS_ERR_OR_NULL(d)) | ||
| 278 | return -ENODEV; | ||
| 279 | |||
| 280 | wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + | ||
| 281 | HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); | ||
| 282 | wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + | ||
| 283 | HOSTADDR(RGF_DMA_ITR_CNT_DATA)); | ||
| 284 | wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + | ||
| 285 | HOSTADDR(RGF_DMA_ITR_CNT_CRL)); | ||
| 286 | |||
| 287 | return 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | static int wil_memread_debugfs_show(struct seq_file *s, void *data) | ||
| 291 | { | ||
| 292 | struct wil6210_priv *wil = s->private; | ||
| 293 | void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr)); | ||
| 294 | |||
| 295 | if (a) | ||
| 296 | seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); | ||
| 297 | else | ||
| 298 | seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); | ||
| 299 | |||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | static int wil_memread_seq_open(struct inode *inode, struct file *file) | ||
| 304 | { | ||
| 305 | return single_open(file, wil_memread_debugfs_show, inode->i_private); | ||
| 306 | } | ||
| 307 | |||
| 308 | static const struct file_operations fops_memread = { | ||
| 309 | .open = wil_memread_seq_open, | ||
| 310 | .release = single_release, | ||
| 311 | .read = seq_read, | ||
| 312 | .llseek = seq_lseek, | ||
| 313 | }; | ||
| 314 | |||
| 315 | static int wil_default_open(struct inode *inode, struct file *file) | ||
| 316 | { | ||
| 317 | if (inode->i_private) | ||
| 318 | file->private_data = inode->i_private; | ||
| 319 | |||
| 320 | return 0; | ||
| 321 | } | ||
| 322 | |||
| 323 | static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, | ||
| 324 | size_t count, loff_t *ppos) | ||
| 325 | { | ||
| 326 | enum { max_count = 4096 }; | ||
| 327 | struct debugfs_blob_wrapper *blob = file->private_data; | ||
| 328 | loff_t pos = *ppos; | ||
| 329 | size_t available = blob->size; | ||
| 330 | void *buf; | ||
| 331 | size_t ret; | ||
| 332 | |||
| 333 | if (pos < 0) | ||
| 334 | return -EINVAL; | ||
| 335 | |||
| 336 | if (pos >= available || !count) | ||
| 337 | return 0; | ||
| 338 | |||
| 339 | if (count > available - pos) | ||
| 340 | count = available - pos; | ||
| 341 | if (count > max_count) | ||
| 342 | count = max_count; | ||
| 343 | |||
| 344 | buf = kmalloc(count, GFP_KERNEL); | ||
| 345 | if (!buf) | ||
| 346 | return -ENOMEM; | ||
| 347 | |||
| 348 | wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + | ||
| 349 | pos, count); | ||
| 350 | |||
| 351 | ret = copy_to_user(user_buf, buf, count); | ||
| 352 | kfree(buf); | ||
| 353 | if (ret == count) | ||
| 354 | return -EFAULT; | ||
| 355 | |||
| 356 | count -= ret; | ||
| 357 | *ppos = pos + count; | ||
| 358 | |||
| 359 | return count; | ||
| 360 | } | ||
| 361 | |||
| 362 | static const struct file_operations fops_ioblob = { | ||
| 363 | .read = wil_read_file_ioblob, | ||
| 364 | .open = wil_default_open, | ||
| 365 | .llseek = default_llseek, | ||
| 366 | }; | ||
| 367 | |||
| 368 | static | ||
| 369 | struct dentry *wil_debugfs_create_ioblob(const char *name, | ||
| 370 | mode_t mode, | ||
| 371 | struct dentry *parent, | ||
| 372 | struct debugfs_blob_wrapper *blob) | ||
| 373 | { | ||
| 374 | return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); | ||
| 375 | } | ||
| 376 | /*---reset---*/ | ||
| 377 | static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, | ||
| 378 | size_t len, loff_t *ppos) | ||
| 379 | { | ||
| 380 | struct wil6210_priv *wil = file->private_data; | ||
| 381 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 382 | |||
| 383 | /** | ||
| 384 | * BUG: | ||
| 385 | * this code does NOT sync device state with the rest of system | ||
| 386 | * use with care, debug only!!! | ||
| 387 | */ | ||
| 388 | rtnl_lock(); | ||
| 389 | dev_close(ndev); | ||
| 390 | ndev->flags &= ~IFF_UP; | ||
| 391 | rtnl_unlock(); | ||
| 392 | wil_reset(wil); | ||
| 393 | |||
| 394 | return len; | ||
| 395 | } | ||
| 396 | |||
| 397 | static const struct file_operations fops_reset = { | ||
| 398 | .write = wil_write_file_reset, | ||
| 399 | .open = wil_default_open, | ||
| 400 | }; | ||
| 401 | /*---------Tx descriptor------------*/ | ||
| 402 | |||
| 403 | static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) | ||
| 404 | { | ||
| 405 | struct wil6210_priv *wil = s->private; | ||
| 406 | struct vring *vring = &(wil->vring_tx[0]); | ||
| 407 | |||
| 408 | if (!vring->va) { | ||
| 409 | seq_printf(s, "No Tx VRING\n"); | ||
| 410 | return 0; | ||
| 411 | } | ||
| 412 | |||
| 413 | if (dbg_txdesc_index < vring->size) { | ||
| 414 | volatile struct vring_tx_desc *d = | ||
| 415 | &(vring->va[dbg_txdesc_index].tx); | ||
| 416 | volatile u32 *u = (volatile u32 *)d; | ||
| 417 | struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; | ||
| 418 | |||
| 419 | seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); | ||
| 420 | seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", | ||
| 421 | u[0], u[1], u[2], u[3]); | ||
| 422 | seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", | ||
| 423 | u[4], u[5], u[6], u[7]); | ||
| 424 | seq_printf(s, " SKB = %p\n", skb); | ||
| 425 | |||
| 426 | if (skb) { | ||
| 427 | unsigned char printbuf[16 * 3 + 2]; | ||
| 428 | int i = 0; | ||
| 429 | int len = skb_headlen(skb); | ||
| 430 | void *p = skb->data; | ||
| 431 | |||
| 432 | seq_printf(s, " len = %d\n", len); | ||
| 433 | |||
| 434 | while (i < len) { | ||
| 435 | int l = min(len - i, 16); | ||
| 436 | hex_dump_to_buffer(p + i, l, 16, 1, printbuf, | ||
| 437 | sizeof(printbuf), false); | ||
| 438 | seq_printf(s, " : %s\n", printbuf); | ||
| 439 | i += l; | ||
| 440 | } | ||
| 441 | } | ||
| 442 | seq_printf(s, "}\n"); | ||
| 443 | } else { | ||
| 444 | seq_printf(s, "TxDesc index (%d) >= size (%d)\n", | ||
| 445 | dbg_txdesc_index, vring->size); | ||
| 446 | } | ||
| 447 | |||
| 448 | return 0; | ||
| 449 | } | ||
| 450 | |||
| 451 | static int wil_txdesc_seq_open(struct inode *inode, struct file *file) | ||
| 452 | { | ||
| 453 | return single_open(file, wil_txdesc_debugfs_show, inode->i_private); | ||
| 454 | } | ||
| 455 | |||
| 456 | static const struct file_operations fops_txdesc = { | ||
| 457 | .open = wil_txdesc_seq_open, | ||
| 458 | .release = single_release, | ||
| 459 | .read = seq_read, | ||
| 460 | .llseek = seq_lseek, | ||
| 461 | }; | ||
| 462 | |||
| 463 | /*---------beamforming------------*/ | ||
| 464 | static int wil_bf_debugfs_show(struct seq_file *s, void *data) | ||
| 465 | { | ||
| 466 | struct wil6210_priv *wil = s->private; | ||
| 467 | seq_printf(s, | ||
| 468 | "TSF : 0x%016llx\n" | ||
| 469 | "TxMCS : %d\n" | ||
| 470 | "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", | ||
| 471 | wil->stats.tsf, wil->stats.bf_mcs, | ||
| 472 | wil->stats.my_rx_sector, wil->stats.my_tx_sector, | ||
| 473 | wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); | ||
| 474 | return 0; | ||
| 475 | } | ||
| 476 | |||
| 477 | static int wil_bf_seq_open(struct inode *inode, struct file *file) | ||
| 478 | { | ||
| 479 | return single_open(file, wil_bf_debugfs_show, inode->i_private); | ||
| 480 | } | ||
| 481 | |||
| 482 | static const struct file_operations fops_bf = { | ||
| 483 | .open = wil_bf_seq_open, | ||
| 484 | .release = single_release, | ||
| 485 | .read = seq_read, | ||
| 486 | .llseek = seq_lseek, | ||
| 487 | }; | ||
| 488 | /*---------SSID------------*/ | ||
| 489 | static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, | ||
| 490 | size_t count, loff_t *ppos) | ||
| 491 | { | ||
| 492 | struct wil6210_priv *wil = file->private_data; | ||
| 493 | struct wireless_dev *wdev = wil_to_wdev(wil); | ||
| 494 | |||
| 495 | return simple_read_from_buffer(user_buf, count, ppos, | ||
| 496 | wdev->ssid, wdev->ssid_len); | ||
| 497 | } | ||
| 498 | |||
| 499 | static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, | ||
| 500 | size_t count, loff_t *ppos) | ||
| 501 | { | ||
| 502 | struct wil6210_priv *wil = file->private_data; | ||
| 503 | struct wireless_dev *wdev = wil_to_wdev(wil); | ||
| 504 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 505 | |||
| 506 | if (*ppos != 0) { | ||
| 507 | wil_err(wil, "Unable to set SSID substring from [%d]\n", | ||
| 508 | (int)*ppos); | ||
| 509 | return -EINVAL; | ||
| 510 | } | ||
| 511 | |||
| 512 | if (count > sizeof(wdev->ssid)) { | ||
| 513 | wil_err(wil, "SSID too long, len = %d\n", (int)count); | ||
| 514 | return -EINVAL; | ||
| 515 | } | ||
| 516 | if (netif_running(ndev)) { | ||
| 517 | wil_err(wil, "Unable to change SSID on running interface\n"); | ||
| 518 | return -EINVAL; | ||
| 519 | } | ||
| 520 | |||
| 521 | wdev->ssid_len = count; | ||
| 522 | return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, | ||
| 523 | buf, count); | ||
| 524 | } | ||
| 525 | |||
| 526 | static const struct file_operations fops_ssid = { | ||
| 527 | .read = wil_read_file_ssid, | ||
| 528 | .write = wil_write_file_ssid, | ||
| 529 | .open = wil_default_open, | ||
| 530 | }; | ||
| 531 | |||
| 532 | /*----------------*/ | ||
| 533 | int wil6210_debugfs_init(struct wil6210_priv *wil) | ||
| 534 | { | ||
| 535 | struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, | ||
| 536 | wil_to_wiphy(wil)->debugfsdir); | ||
| 537 | |||
| 538 | if (IS_ERR_OR_NULL(dbg)) | ||
| 539 | return -ENODEV; | ||
| 540 | |||
| 541 | debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); | ||
| 542 | debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); | ||
| 543 | debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); | ||
| 544 | debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, | ||
| 545 | &dbg_txdesc_index); | ||
| 546 | debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); | ||
| 547 | debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); | ||
| 548 | debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, | ||
| 549 | &wil->secure_pcp); | ||
| 550 | |||
| 551 | wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, | ||
| 552 | HOSTADDR(RGF_USER_USER_ICR)); | ||
| 553 | wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, | ||
| 554 | HOSTADDR(RGF_DMA_EP_TX_ICR)); | ||
| 555 | wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, | ||
| 556 | HOSTADDR(RGF_DMA_EP_RX_ICR)); | ||
| 557 | wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, | ||
| 558 | HOSTADDR(RGF_DMA_EP_MISC_ICR)); | ||
| 559 | wil6210_debugfs_create_pseudo_ISR(wil, dbg); | ||
| 560 | wil6210_debugfs_create_ITR_CNT(wil, dbg); | ||
| 561 | |||
| 562 | debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); | ||
| 563 | debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); | ||
| 564 | |||
| 565 | debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); | ||
| 566 | |||
| 567 | wil->rgf_blob.data = (void * __force)wil->csr + 0; | ||
| 568 | wil->rgf_blob.size = 0xa000; | ||
| 569 | wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); | ||
| 570 | |||
| 571 | wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; | ||
| 572 | wil->fw_code_blob.size = 0x40000; | ||
| 573 | wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, | ||
| 574 | &wil->fw_code_blob); | ||
| 575 | |||
| 576 | wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; | ||
| 577 | wil->fw_data_blob.size = 0x8000; | ||
| 578 | wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, | ||
| 579 | &wil->fw_data_blob); | ||
| 580 | |||
| 581 | wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; | ||
| 582 | wil->fw_peri_blob.size = 0x18000; | ||
| 583 | wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, | ||
| 584 | &wil->fw_peri_blob); | ||
| 585 | |||
| 586 | wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; | ||
| 587 | wil->uc_code_blob.size = 0x10000; | ||
| 588 | wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, | ||
| 589 | &wil->uc_code_blob); | ||
| 590 | |||
| 591 | wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; | ||
| 592 | wil->uc_data_blob.size = 0x4000; | ||
| 593 | wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, | ||
| 594 | &wil->uc_data_blob); | ||
| 595 | |||
| 596 | return 0; | ||
| 597 | } | ||
| 598 | |||
| 599 | void wil6210_debugfs_remove(struct wil6210_priv *wil) | ||
| 600 | { | ||
| 601 | debugfs_remove_recursive(wil->debug); | ||
| 602 | wil->debug = NULL; | ||
| 603 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c new file mode 100644 index 000000000000..38049da71049 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/interrupt.c | |||
| @@ -0,0 +1,471 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/interrupt.h> | ||
| 18 | |||
| 19 | #include "wil6210.h" | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Theory of operation: | ||
| 23 | * | ||
| 24 | * There is ISR pseudo-cause register, | ||
| 25 | * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE | ||
| 26 | * Its bits represents OR'ed bits from 3 real ISR registers: | ||
| 27 | * TX, RX, and MISC. | ||
| 28 | * | ||
| 29 | * Registers may be configured to either "write 1 to clear" or | ||
| 30 | * "clear on read" mode | ||
| 31 | * | ||
| 32 | * When handling interrupt, one have to mask/unmask interrupts for the | ||
| 33 | * real ISR registers, or hardware may malfunction. | ||
| 34 | * | ||
| 35 | */ | ||
| 36 | |||
| 37 | #define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) | ||
| 38 | #define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE | ||
| 39 | #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ | ||
| 40 | BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) | ||
| 41 | #define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) | ||
| 42 | |||
| 43 | #define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ | ||
| 44 | BIT_DMA_PSEUDO_CAUSE_TX | \ | ||
| 45 | BIT_DMA_PSEUDO_CAUSE_MISC)) | ||
| 46 | |||
| 47 | #if defined(CONFIG_WIL6210_ISR_COR) | ||
| 48 | /* configure to Clear-On-Read mode */ | ||
| 49 | #define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) | ||
| 50 | |||
| 51 | static inline void wil_icr_clear(u32 x, void __iomem *addr) | ||
| 52 | { | ||
| 53 | |||
| 54 | } | ||
| 55 | #else /* defined(CONFIG_WIL6210_ISR_COR) */ | ||
| 56 | /* configure to Write-1-to-Clear mode */ | ||
| 57 | #define WIL_ICR_ICC_VALUE (0UL) | ||
| 58 | |||
| 59 | static inline void wil_icr_clear(u32 x, void __iomem *addr) | ||
| 60 | { | ||
| 61 | iowrite32(x, addr); | ||
| 62 | } | ||
| 63 | #endif /* defined(CONFIG_WIL6210_ISR_COR) */ | ||
| 64 | |||
| 65 | static inline u32 wil_ioread32_and_clear(void __iomem *addr) | ||
| 66 | { | ||
| 67 | u32 x = ioread32(addr); | ||
| 68 | |||
| 69 | wil_icr_clear(x, addr); | ||
| 70 | |||
| 71 | return x; | ||
| 72 | } | ||
| 73 | |||
| 74 | static void wil6210_mask_irq_tx(struct wil6210_priv *wil) | ||
| 75 | { | ||
| 76 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
| 77 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
| 78 | offsetof(struct RGF_ICR, IMS)); | ||
| 79 | } | ||
| 80 | |||
| 81 | static void wil6210_mask_irq_rx(struct wil6210_priv *wil) | ||
| 82 | { | ||
| 83 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
| 84 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
| 85 | offsetof(struct RGF_ICR, IMS)); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void wil6210_mask_irq_misc(struct wil6210_priv *wil) | ||
| 89 | { | ||
| 90 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
| 91 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
| 92 | offsetof(struct RGF_ICR, IMS)); | ||
| 93 | } | ||
| 94 | |||
| 95 | static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) | ||
| 96 | { | ||
| 97 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
| 98 | |||
| 99 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
| 100 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); | ||
| 101 | |||
| 102 | clear_bit(wil_status_irqen, &wil->status); | ||
| 103 | } | ||
| 104 | |||
| 105 | static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) | ||
| 106 | { | ||
| 107 | iowrite32(WIL6210_IMC_TX, wil->csr + | ||
| 108 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
| 109 | offsetof(struct RGF_ICR, IMC)); | ||
| 110 | } | ||
| 111 | |||
| 112 | static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) | ||
| 113 | { | ||
| 114 | iowrite32(WIL6210_IMC_RX, wil->csr + | ||
| 115 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
| 116 | offsetof(struct RGF_ICR, IMC)); | ||
| 117 | } | ||
| 118 | |||
| 119 | static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) | ||
| 120 | { | ||
| 121 | iowrite32(WIL6210_IMC_MISC, wil->csr + | ||
| 122 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
| 123 | offsetof(struct RGF_ICR, IMC)); | ||
| 124 | } | ||
| 125 | |||
| 126 | static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) | ||
| 127 | { | ||
| 128 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
| 129 | |||
| 130 | set_bit(wil_status_irqen, &wil->status); | ||
| 131 | |||
| 132 | iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + | ||
| 133 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); | ||
| 134 | } | ||
| 135 | |||
| 136 | void wil6210_disable_irq(struct wil6210_priv *wil) | ||
| 137 | { | ||
| 138 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
| 139 | |||
| 140 | wil6210_mask_irq_tx(wil); | ||
| 141 | wil6210_mask_irq_rx(wil); | ||
| 142 | wil6210_mask_irq_misc(wil); | ||
| 143 | wil6210_mask_irq_pseudo(wil); | ||
| 144 | } | ||
| 145 | |||
| 146 | void wil6210_enable_irq(struct wil6210_priv *wil) | ||
| 147 | { | ||
| 148 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
| 149 | |||
| 150 | iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
| 151 | offsetof(struct RGF_ICR, ICC)); | ||
| 152 | iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
| 153 | offsetof(struct RGF_ICR, ICC)); | ||
| 154 | iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
| 155 | offsetof(struct RGF_ICR, ICC)); | ||
| 156 | |||
| 157 | wil6210_unmask_irq_pseudo(wil); | ||
| 158 | wil6210_unmask_irq_tx(wil); | ||
| 159 | wil6210_unmask_irq_rx(wil); | ||
| 160 | wil6210_unmask_irq_misc(wil); | ||
| 161 | } | ||
| 162 | |||
| 163 | static irqreturn_t wil6210_irq_rx(int irq, void *cookie) | ||
| 164 | { | ||
| 165 | struct wil6210_priv *wil = cookie; | ||
| 166 | u32 isr = wil_ioread32_and_clear(wil->csr + | ||
| 167 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
| 168 | offsetof(struct RGF_ICR, ICR)); | ||
| 169 | |||
| 170 | wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); | ||
| 171 | |||
| 172 | if (!isr) { | ||
| 173 | wil_err(wil, "spurious IRQ: RX\n"); | ||
| 174 | return IRQ_NONE; | ||
| 175 | } | ||
| 176 | |||
| 177 | wil6210_mask_irq_rx(wil); | ||
| 178 | |||
| 179 | if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { | ||
| 180 | wil_dbg_IRQ(wil, "RX done\n"); | ||
| 181 | isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; | ||
| 182 | wil_rx_handle(wil); | ||
| 183 | } | ||
| 184 | |||
| 185 | if (isr) | ||
| 186 | wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); | ||
| 187 | |||
| 188 | wil6210_unmask_irq_rx(wil); | ||
| 189 | |||
| 190 | return IRQ_HANDLED; | ||
| 191 | } | ||
| 192 | |||
| 193 | static irqreturn_t wil6210_irq_tx(int irq, void *cookie) | ||
| 194 | { | ||
| 195 | struct wil6210_priv *wil = cookie; | ||
| 196 | u32 isr = wil_ioread32_and_clear(wil->csr + | ||
| 197 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
| 198 | offsetof(struct RGF_ICR, ICR)); | ||
| 199 | |||
| 200 | wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); | ||
| 201 | |||
| 202 | if (!isr) { | ||
| 203 | wil_err(wil, "spurious IRQ: TX\n"); | ||
| 204 | return IRQ_NONE; | ||
| 205 | } | ||
| 206 | |||
| 207 | wil6210_mask_irq_tx(wil); | ||
| 208 | |||
| 209 | if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { | ||
| 210 | uint i; | ||
| 211 | wil_dbg_IRQ(wil, "TX done\n"); | ||
| 212 | isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; | ||
| 213 | for (i = 0; i < 24; i++) { | ||
| 214 | u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); | ||
| 215 | if (isr & mask) { | ||
| 216 | isr &= ~mask; | ||
| 217 | wil_dbg_IRQ(wil, "TX done(%i)\n", i); | ||
| 218 | wil_tx_complete(wil, i); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | if (isr) | ||
| 224 | wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); | ||
| 225 | |||
| 226 | wil6210_unmask_irq_tx(wil); | ||
| 227 | |||
| 228 | return IRQ_HANDLED; | ||
| 229 | } | ||
| 230 | |||
| 231 | static irqreturn_t wil6210_irq_misc(int irq, void *cookie) | ||
| 232 | { | ||
| 233 | struct wil6210_priv *wil = cookie; | ||
| 234 | u32 isr = wil_ioread32_and_clear(wil->csr + | ||
| 235 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
| 236 | offsetof(struct RGF_ICR, ICR)); | ||
| 237 | |||
| 238 | wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); | ||
| 239 | |||
| 240 | if (!isr) { | ||
| 241 | wil_err(wil, "spurious IRQ: MISC\n"); | ||
| 242 | return IRQ_NONE; | ||
| 243 | } | ||
| 244 | |||
| 245 | wil6210_mask_irq_misc(wil); | ||
| 246 | |||
| 247 | if (isr & ISR_MISC_FW_READY) { | ||
| 248 | wil_dbg_IRQ(wil, "IRQ: FW ready\n"); | ||
| 249 | /** | ||
| 250 | * Actual FW ready indicated by the | ||
| 251 | * WMI_FW_READY_EVENTID | ||
| 252 | */ | ||
| 253 | isr &= ~ISR_MISC_FW_READY; | ||
| 254 | } | ||
| 255 | |||
| 256 | wil->isr_misc = isr; | ||
| 257 | |||
| 258 | if (isr) { | ||
| 259 | return IRQ_WAKE_THREAD; | ||
| 260 | } else { | ||
| 261 | wil6210_unmask_irq_misc(wil); | ||
| 262 | return IRQ_HANDLED; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) | ||
| 267 | { | ||
| 268 | struct wil6210_priv *wil = cookie; | ||
| 269 | u32 isr = wil->isr_misc; | ||
| 270 | |||
| 271 | wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); | ||
| 272 | |||
| 273 | if (isr & ISR_MISC_MBOX_EVT) { | ||
| 274 | wil_dbg_IRQ(wil, "MBOX event\n"); | ||
| 275 | wmi_recv_cmd(wil); | ||
| 276 | isr &= ~ISR_MISC_MBOX_EVT; | ||
| 277 | } | ||
| 278 | |||
| 279 | if (isr) | ||
| 280 | wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); | ||
| 281 | |||
| 282 | wil->isr_misc = 0; | ||
| 283 | |||
| 284 | wil6210_unmask_irq_misc(wil); | ||
| 285 | |||
| 286 | return IRQ_HANDLED; | ||
| 287 | } | ||
| 288 | |||
| 289 | /** | ||
| 290 | * thread IRQ handler | ||
| 291 | */ | ||
| 292 | static irqreturn_t wil6210_thread_irq(int irq, void *cookie) | ||
| 293 | { | ||
| 294 | struct wil6210_priv *wil = cookie; | ||
| 295 | |||
| 296 | wil_dbg_IRQ(wil, "Thread IRQ\n"); | ||
| 297 | /* Discover real IRQ cause */ | ||
| 298 | if (wil->isr_misc) | ||
| 299 | wil6210_irq_misc_thread(irq, cookie); | ||
| 300 | |||
| 301 | wil6210_unmask_irq_pseudo(wil); | ||
| 302 | |||
| 303 | return IRQ_HANDLED; | ||
| 304 | } | ||
| 305 | |||
| 306 | /* DEBUG | ||
| 307 | * There is subtle bug in hardware that causes IRQ to raise when it should be | ||
| 308 | * masked. It is quite rare and hard to debug. | ||
| 309 | * | ||
| 310 | * Catch irq issue if it happens and print all I can. | ||
| 311 | */ | ||
| 312 | static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) | ||
| 313 | { | ||
| 314 | if (!test_bit(wil_status_irqen, &wil->status)) { | ||
| 315 | u32 icm_rx = wil_ioread32_and_clear(wil->csr + | ||
| 316 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
| 317 | offsetof(struct RGF_ICR, ICM)); | ||
| 318 | u32 icr_rx = wil_ioread32_and_clear(wil->csr + | ||
| 319 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
| 320 | offsetof(struct RGF_ICR, ICR)); | ||
| 321 | u32 imv_rx = ioread32(wil->csr + | ||
| 322 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
| 323 | offsetof(struct RGF_ICR, IMV)); | ||
| 324 | u32 icm_tx = wil_ioread32_and_clear(wil->csr + | ||
| 325 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
| 326 | offsetof(struct RGF_ICR, ICM)); | ||
| 327 | u32 icr_tx = wil_ioread32_and_clear(wil->csr + | ||
| 328 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
| 329 | offsetof(struct RGF_ICR, ICR)); | ||
| 330 | u32 imv_tx = ioread32(wil->csr + | ||
| 331 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
| 332 | offsetof(struct RGF_ICR, IMV)); | ||
| 333 | u32 icm_misc = wil_ioread32_and_clear(wil->csr + | ||
| 334 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
| 335 | offsetof(struct RGF_ICR, ICM)); | ||
| 336 | u32 icr_misc = wil_ioread32_and_clear(wil->csr + | ||
| 337 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
| 338 | offsetof(struct RGF_ICR, ICR)); | ||
| 339 | u32 imv_misc = ioread32(wil->csr + | ||
| 340 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
| 341 | offsetof(struct RGF_ICR, IMV)); | ||
| 342 | wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" | ||
| 343 | "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" | ||
| 344 | "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" | ||
| 345 | "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", | ||
| 346 | pseudo_cause, | ||
| 347 | icm_rx, icr_rx, imv_rx, | ||
| 348 | icm_tx, icr_tx, imv_tx, | ||
| 349 | icm_misc, icr_misc, imv_misc); | ||
| 350 | |||
| 351 | return -EINVAL; | ||
| 352 | } | ||
| 353 | |||
| 354 | return 0; | ||
| 355 | } | ||
| 356 | |||
| 357 | static irqreturn_t wil6210_hardirq(int irq, void *cookie) | ||
| 358 | { | ||
| 359 | irqreturn_t rc = IRQ_HANDLED; | ||
| 360 | struct wil6210_priv *wil = cookie; | ||
| 361 | u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); | ||
| 362 | |||
| 363 | /** | ||
| 364 | * pseudo_cause is Clear-On-Read, no need to ACK | ||
| 365 | */ | ||
| 366 | if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) | ||
| 367 | return IRQ_NONE; | ||
| 368 | |||
| 369 | /* FIXME: IRQ mask debug */ | ||
| 370 | if (wil6210_debug_irq_mask(wil, pseudo_cause)) | ||
| 371 | return IRQ_NONE; | ||
| 372 | |||
| 373 | wil6210_mask_irq_pseudo(wil); | ||
| 374 | |||
| 375 | /* Discover real IRQ cause | ||
| 376 | * There are 2 possible phases for every IRQ: | ||
| 377 | * - hard IRQ handler called right here | ||
| 378 | * - threaded handler called later | ||
| 379 | * | ||
| 380 | * Hard IRQ handler reads and clears ISR. | ||
| 381 | * | ||
| 382 | * If threaded handler requested, hard IRQ handler | ||
| 383 | * returns IRQ_WAKE_THREAD and saves ISR register value | ||
| 384 | * for the threaded handler use. | ||
| 385 | * | ||
| 386 | * voting for wake thread - need at least 1 vote | ||
| 387 | */ | ||
| 388 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && | ||
| 389 | (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) | ||
| 390 | rc = IRQ_WAKE_THREAD; | ||
| 391 | |||
| 392 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && | ||
| 393 | (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) | ||
| 394 | rc = IRQ_WAKE_THREAD; | ||
| 395 | |||
| 396 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && | ||
| 397 | (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) | ||
| 398 | rc = IRQ_WAKE_THREAD; | ||
| 399 | |||
| 400 | /* if thread is requested, it will unmask IRQ */ | ||
| 401 | if (rc != IRQ_WAKE_THREAD) | ||
| 402 | wil6210_unmask_irq_pseudo(wil); | ||
| 403 | |||
| 404 | wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); | ||
| 405 | |||
| 406 | return rc; | ||
| 407 | } | ||
| 408 | |||
| 409 | static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) | ||
| 410 | { | ||
| 411 | int rc; | ||
| 412 | /* | ||
| 413 | * IRQ's are in the following order: | ||
| 414 | * - Tx | ||
| 415 | * - Rx | ||
| 416 | * - Misc | ||
| 417 | */ | ||
| 418 | |||
| 419 | rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, | ||
| 420 | WIL_NAME"_tx", wil); | ||
| 421 | if (rc) | ||
| 422 | return rc; | ||
| 423 | |||
| 424 | rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, | ||
| 425 | WIL_NAME"_rx", wil); | ||
| 426 | if (rc) | ||
| 427 | goto free0; | ||
| 428 | |||
| 429 | rc = request_threaded_irq(irq + 2, wil6210_irq_misc, | ||
| 430 | wil6210_irq_misc_thread, | ||
| 431 | IRQF_SHARED, WIL_NAME"_misc", wil); | ||
| 432 | if (rc) | ||
| 433 | goto free1; | ||
| 434 | |||
| 435 | return 0; | ||
| 436 | /* error branch */ | ||
| 437 | free1: | ||
| 438 | free_irq(irq + 1, wil); | ||
| 439 | free0: | ||
| 440 | free_irq(irq, wil); | ||
| 441 | |||
| 442 | return rc; | ||
| 443 | } | ||
| 444 | |||
| 445 | int wil6210_init_irq(struct wil6210_priv *wil, int irq) | ||
| 446 | { | ||
| 447 | int rc; | ||
| 448 | if (wil->n_msi == 3) | ||
| 449 | rc = wil6210_request_3msi(wil, irq); | ||
| 450 | else | ||
| 451 | rc = request_threaded_irq(irq, wil6210_hardirq, | ||
| 452 | wil6210_thread_irq, | ||
| 453 | wil->n_msi ? 0 : IRQF_SHARED, | ||
| 454 | WIL_NAME, wil); | ||
| 455 | if (rc) | ||
| 456 | return rc; | ||
| 457 | |||
| 458 | wil6210_enable_irq(wil); | ||
| 459 | |||
| 460 | return 0; | ||
| 461 | } | ||
| 462 | |||
| 463 | void wil6210_fini_irq(struct wil6210_priv *wil, int irq) | ||
| 464 | { | ||
| 465 | wil6210_disable_irq(wil); | ||
| 466 | free_irq(irq, wil); | ||
| 467 | if (wil->n_msi == 3) { | ||
| 468 | free_irq(irq + 1, wil); | ||
| 469 | free_irq(irq + 2, wil); | ||
| 470 | } | ||
| 471 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c new file mode 100644 index 000000000000..95fcd361322b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/main.c | |||
| @@ -0,0 +1,407 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/netdevice.h> | ||
| 19 | #include <linux/sched.h> | ||
| 20 | #include <linux/ieee80211.h> | ||
| 21 | #include <linux/wireless.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | #include <linux/moduleparam.h> | ||
| 24 | #include <linux/if_arp.h> | ||
| 25 | |||
| 26 | #include "wil6210.h" | ||
| 27 | |||
| 28 | /* | ||
| 29 | * Due to a hardware issue, | ||
| 30 | * one has to read/write to/from NIC in 32-bit chunks; | ||
| 31 | * regular memcpy_fromio and siblings will | ||
| 32 | * not work on 64-bit platform - it uses 64-bit transactions | ||
| 33 | * | ||
| 34 | * Force 32-bit transactions to enable NIC on 64-bit platforms | ||
| 35 | * | ||
| 36 | * To avoid byte swap on big endian host, __raw_{read|write}l | ||
| 37 | * should be used - {read|write}l would swap bytes to provide | ||
| 38 | * little endian on PCI value in host endianness. | ||
| 39 | */ | ||
| 40 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | ||
| 41 | size_t count) | ||
| 42 | { | ||
| 43 | u32 *d = dst; | ||
| 44 | const volatile u32 __iomem *s = src; | ||
| 45 | |||
| 46 | /* size_t is unsigned, if (count%4 != 0) it will wrap */ | ||
| 47 | for (count += 4; count > 4; count -= 4) | ||
| 48 | *d++ = __raw_readl(s++); | ||
| 49 | } | ||
| 50 | |||
| 51 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | ||
| 52 | size_t count) | ||
| 53 | { | ||
| 54 | volatile u32 __iomem *d = dst; | ||
| 55 | const u32 *s = src; | ||
| 56 | |||
| 57 | for (count += 4; count > 4; count -= 4) | ||
| 58 | __raw_writel(*s++, d++); | ||
| 59 | } | ||
| 60 | |||
| 61 | static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | ||
| 62 | { | ||
| 63 | uint i; | ||
| 64 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 65 | struct wireless_dev *wdev = wil->wdev; | ||
| 66 | |||
| 67 | wil_dbg(wil, "%s()\n", __func__); | ||
| 68 | |||
| 69 | wil_link_off(wil); | ||
| 70 | clear_bit(wil_status_fwconnected, &wil->status); | ||
| 71 | |||
| 72 | switch (wdev->sme_state) { | ||
| 73 | case CFG80211_SME_CONNECTED: | ||
| 74 | cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
| 75 | NULL, 0, GFP_KERNEL); | ||
| 76 | break; | ||
| 77 | case CFG80211_SME_CONNECTING: | ||
| 78 | cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, | ||
| 79 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
| 80 | GFP_KERNEL); | ||
| 81 | break; | ||
| 82 | default: | ||
| 83 | ; | ||
| 84 | } | ||
| 85 | |||
| 86 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) | ||
| 87 | wil_vring_fini_tx(wil, i); | ||
| 88 | } | ||
| 89 | |||
| 90 | static void wil_disconnect_worker(struct work_struct *work) | ||
| 91 | { | ||
| 92 | struct wil6210_priv *wil = container_of(work, | ||
| 93 | struct wil6210_priv, disconnect_worker); | ||
| 94 | |||
| 95 | _wil6210_disconnect(wil, NULL); | ||
| 96 | } | ||
| 97 | |||
| 98 | static void wil_connect_timer_fn(ulong x) | ||
| 99 | { | ||
| 100 | struct wil6210_priv *wil = (void *)x; | ||
| 101 | |||
| 102 | wil_dbg(wil, "Connect timeout\n"); | ||
| 103 | |||
| 104 | /* reschedule to thread context - disconnect won't | ||
| 105 | * run from atomic context | ||
| 106 | */ | ||
| 107 | schedule_work(&wil->disconnect_worker); | ||
| 108 | } | ||
| 109 | |||
| 110 | int wil_priv_init(struct wil6210_priv *wil) | ||
| 111 | { | ||
| 112 | wil_dbg(wil, "%s()\n", __func__); | ||
| 113 | |||
| 114 | mutex_init(&wil->mutex); | ||
| 115 | mutex_init(&wil->wmi_mutex); | ||
| 116 | |||
| 117 | init_completion(&wil->wmi_ready); | ||
| 118 | |||
| 119 | wil->pending_connect_cid = -1; | ||
| 120 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); | ||
| 121 | |||
| 122 | INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); | ||
| 123 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); | ||
| 124 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | ||
| 125 | |||
| 126 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | ||
| 127 | spin_lock_init(&wil->wmi_ev_lock); | ||
| 128 | |||
| 129 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); | ||
| 130 | if (!wil->wmi_wq) | ||
| 131 | return -EAGAIN; | ||
| 132 | |||
| 133 | wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); | ||
| 134 | if (!wil->wmi_wq_conn) { | ||
| 135 | destroy_workqueue(wil->wmi_wq); | ||
| 136 | return -EAGAIN; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* make shadow copy of registers that should not change on run time */ | ||
| 140 | wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, | ||
| 141 | sizeof(struct wil6210_mbox_ctl)); | ||
| 142 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); | ||
| 143 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); | ||
| 144 | |||
| 145 | return 0; | ||
| 146 | } | ||
| 147 | |||
| 148 | void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | ||
| 149 | { | ||
| 150 | del_timer_sync(&wil->connect_timer); | ||
| 151 | _wil6210_disconnect(wil, bssid); | ||
| 152 | } | ||
| 153 | |||
| 154 | void wil_priv_deinit(struct wil6210_priv *wil) | ||
| 155 | { | ||
| 156 | cancel_work_sync(&wil->disconnect_worker); | ||
| 157 | wil6210_disconnect(wil, NULL); | ||
| 158 | wmi_event_flush(wil); | ||
| 159 | destroy_workqueue(wil->wmi_wq_conn); | ||
| 160 | destroy_workqueue(wil->wmi_wq); | ||
| 161 | } | ||
| 162 | |||
| 163 | static void wil_target_reset(struct wil6210_priv *wil) | ||
| 164 | { | ||
| 165 | wil_dbg(wil, "Resetting...\n"); | ||
| 166 | |||
| 167 | /* register write */ | ||
| 168 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) | ||
| 169 | /* register set = read, OR, write */ | ||
| 170 | #define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ | ||
| 171 | wil->csr + HOSTADDR(a)) | ||
| 172 | |||
| 173 | /* hpal_perst_from_pad_src_n_mask */ | ||
| 174 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); | ||
| 175 | /* car_perst_rst_src_n_mask */ | ||
| 176 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); | ||
| 177 | |||
| 178 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ | ||
| 179 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ | ||
| 180 | |||
| 181 | msleep(100); | ||
| 182 | |||
| 183 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); | ||
| 184 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | ||
| 185 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); | ||
| 186 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); | ||
| 187 | |||
| 188 | msleep(100); | ||
| 189 | |||
| 190 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); | ||
| 191 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | ||
| 192 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | ||
| 193 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | ||
| 194 | |||
| 195 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); | ||
| 196 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | ||
| 197 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | ||
| 198 | |||
| 199 | msleep(2000); | ||
| 200 | |||
| 201 | W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */ | ||
| 202 | |||
| 203 | msleep(2000); | ||
| 204 | |||
| 205 | wil_dbg(wil, "Reset completed\n"); | ||
| 206 | |||
| 207 | #undef W | ||
| 208 | #undef S | ||
| 209 | } | ||
| 210 | |||
| 211 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) | ||
| 212 | { | ||
| 213 | le32_to_cpus(&r->base); | ||
| 214 | le16_to_cpus(&r->entry_size); | ||
| 215 | le16_to_cpus(&r->size); | ||
| 216 | le32_to_cpus(&r->tail); | ||
| 217 | le32_to_cpus(&r->head); | ||
| 218 | } | ||
| 219 | |||
| 220 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) | ||
| 221 | { | ||
| 222 | ulong to = msecs_to_jiffies(1000); | ||
| 223 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | ||
| 224 | if (0 == left) { | ||
| 225 | wil_err(wil, "Firmware not ready\n"); | ||
| 226 | return -ETIME; | ||
| 227 | } else { | ||
| 228 | wil_dbg(wil, "FW ready after %d ms\n", | ||
| 229 | jiffies_to_msecs(to-left)); | ||
| 230 | } | ||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | |||
| 234 | /* | ||
| 235 | * We reset all the structures, and we reset the UMAC. | ||
| 236 | * After calling this routine, you're expected to reload | ||
| 237 | * the firmware. | ||
| 238 | */ | ||
| 239 | int wil_reset(struct wil6210_priv *wil) | ||
| 240 | { | ||
| 241 | int rc; | ||
| 242 | |||
| 243 | cancel_work_sync(&wil->disconnect_worker); | ||
| 244 | wil6210_disconnect(wil, NULL); | ||
| 245 | |||
| 246 | wmi_event_flush(wil); | ||
| 247 | |||
| 248 | flush_workqueue(wil->wmi_wq); | ||
| 249 | flush_workqueue(wil->wmi_wq_conn); | ||
| 250 | |||
| 251 | wil6210_disable_irq(wil); | ||
| 252 | wil->status = 0; | ||
| 253 | |||
| 254 | /* TODO: put MAC in reset */ | ||
| 255 | wil_target_reset(wil); | ||
| 256 | |||
| 257 | /* init after reset */ | ||
| 258 | wil->pending_connect_cid = -1; | ||
| 259 | INIT_COMPLETION(wil->wmi_ready); | ||
| 260 | |||
| 261 | /* make shadow copy of registers that should not change on run time */ | ||
| 262 | wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, | ||
| 263 | sizeof(struct wil6210_mbox_ctl)); | ||
| 264 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); | ||
| 265 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); | ||
| 266 | |||
| 267 | /* TODO: release MAC reset */ | ||
| 268 | wil6210_enable_irq(wil); | ||
| 269 | |||
| 270 | /* we just started MAC, wait for FW ready */ | ||
| 271 | rc = wil_wait_for_fw_ready(wil); | ||
| 272 | |||
| 273 | return rc; | ||
| 274 | } | ||
| 275 | |||
| 276 | |||
| 277 | void wil_link_on(struct wil6210_priv *wil) | ||
| 278 | { | ||
| 279 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 280 | |||
| 281 | wil_dbg(wil, "%s()\n", __func__); | ||
| 282 | |||
| 283 | netif_carrier_on(ndev); | ||
| 284 | netif_tx_wake_all_queues(ndev); | ||
| 285 | } | ||
| 286 | |||
| 287 | void wil_link_off(struct wil6210_priv *wil) | ||
| 288 | { | ||
| 289 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 290 | |||
| 291 | wil_dbg(wil, "%s()\n", __func__); | ||
| 292 | |||
| 293 | netif_tx_stop_all_queues(ndev); | ||
| 294 | netif_carrier_off(ndev); | ||
| 295 | } | ||
| 296 | |||
| 297 | static int __wil_up(struct wil6210_priv *wil) | ||
| 298 | { | ||
| 299 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 300 | struct wireless_dev *wdev = wil->wdev; | ||
| 301 | struct ieee80211_channel *channel = wdev->preset_chandef.chan; | ||
| 302 | int rc; | ||
| 303 | int bi; | ||
| 304 | u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); | ||
| 305 | |||
| 306 | rc = wil_reset(wil); | ||
| 307 | if (rc) | ||
| 308 | return rc; | ||
| 309 | |||
| 310 | /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ | ||
| 311 | wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); | ||
| 312 | switch (wdev->iftype) { | ||
| 313 | case NL80211_IFTYPE_STATION: | ||
| 314 | wil_dbg(wil, "type: STATION\n"); | ||
| 315 | bi = 0; | ||
| 316 | ndev->type = ARPHRD_ETHER; | ||
| 317 | break; | ||
| 318 | case NL80211_IFTYPE_AP: | ||
| 319 | wil_dbg(wil, "type: AP\n"); | ||
| 320 | bi = 100; | ||
| 321 | ndev->type = ARPHRD_ETHER; | ||
| 322 | break; | ||
| 323 | case NL80211_IFTYPE_P2P_CLIENT: | ||
| 324 | wil_dbg(wil, "type: P2P_CLIENT\n"); | ||
| 325 | bi = 0; | ||
| 326 | ndev->type = ARPHRD_ETHER; | ||
| 327 | break; | ||
| 328 | case NL80211_IFTYPE_P2P_GO: | ||
| 329 | wil_dbg(wil, "type: P2P_GO\n"); | ||
| 330 | bi = 100; | ||
| 331 | ndev->type = ARPHRD_ETHER; | ||
| 332 | break; | ||
| 333 | case NL80211_IFTYPE_MONITOR: | ||
| 334 | wil_dbg(wil, "type: Monitor\n"); | ||
| 335 | bi = 0; | ||
| 336 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; | ||
| 337 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | ||
| 338 | break; | ||
| 339 | default: | ||
| 340 | return -EOPNOTSUPP; | ||
| 341 | } | ||
| 342 | |||
| 343 | /* Apply profile in the following order: */ | ||
| 344 | /* SSID and channel for the AP */ | ||
| 345 | switch (wdev->iftype) { | ||
| 346 | case NL80211_IFTYPE_AP: | ||
| 347 | case NL80211_IFTYPE_P2P_GO: | ||
| 348 | if (wdev->ssid_len == 0) { | ||
| 349 | wil_err(wil, "SSID not set\n"); | ||
| 350 | return -EINVAL; | ||
| 351 | } | ||
| 352 | wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); | ||
| 353 | if (channel) | ||
| 354 | wmi_set_channel(wil, channel->hw_value); | ||
| 355 | break; | ||
| 356 | default: | ||
| 357 | ; | ||
| 358 | } | ||
| 359 | |||
| 360 | /* MAC address - pre-requisite for other commands */ | ||
| 361 | wmi_set_mac_address(wil, ndev->dev_addr); | ||
| 362 | |||
| 363 | /* Set up beaconing if required. */ | ||
| 364 | rc = wmi_set_bcon(wil, bi, wmi_nettype); | ||
| 365 | if (rc) | ||
| 366 | return rc; | ||
| 367 | |||
| 368 | /* Rx VRING. After MAC and beacon */ | ||
| 369 | wil_rx_init(wil); | ||
| 370 | |||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | int wil_up(struct wil6210_priv *wil) | ||
| 375 | { | ||
| 376 | int rc; | ||
| 377 | |||
| 378 | mutex_lock(&wil->mutex); | ||
| 379 | rc = __wil_up(wil); | ||
| 380 | mutex_unlock(&wil->mutex); | ||
| 381 | |||
| 382 | return rc; | ||
| 383 | } | ||
| 384 | |||
| 385 | static int __wil_down(struct wil6210_priv *wil) | ||
| 386 | { | ||
| 387 | if (wil->scan_request) { | ||
| 388 | cfg80211_scan_done(wil->scan_request, true); | ||
| 389 | wil->scan_request = NULL; | ||
| 390 | } | ||
| 391 | |||
| 392 | wil6210_disconnect(wil, NULL); | ||
| 393 | wil_rx_fini(wil); | ||
| 394 | |||
| 395 | return 0; | ||
| 396 | } | ||
| 397 | |||
| 398 | int wil_down(struct wil6210_priv *wil) | ||
| 399 | { | ||
| 400 | int rc; | ||
| 401 | |||
| 402 | mutex_lock(&wil->mutex); | ||
| 403 | rc = __wil_down(wil); | ||
| 404 | mutex_unlock(&wil->mutex); | ||
| 405 | |||
| 406 | return rc; | ||
| 407 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c new file mode 100644 index 000000000000..3068b5cb53a7 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/netdev.c | |||
| @@ -0,0 +1,157 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/netdevice.h> | ||
| 19 | #include <linux/etherdevice.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | |||
| 22 | #include "wil6210.h" | ||
| 23 | |||
| 24 | static int wil_open(struct net_device *ndev) | ||
| 25 | { | ||
| 26 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
| 27 | |||
| 28 | return wil_up(wil); | ||
| 29 | } | ||
| 30 | |||
| 31 | static int wil_stop(struct net_device *ndev) | ||
| 32 | { | ||
| 33 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
| 34 | |||
| 35 | return wil_down(wil); | ||
| 36 | } | ||
| 37 | |||
| 38 | /* | ||
| 39 | * AC to queue mapping | ||
| 40 | * | ||
| 41 | * AC_VO -> queue 3 | ||
| 42 | * AC_VI -> queue 2 | ||
| 43 | * AC_BE -> queue 1 | ||
| 44 | * AC_BK -> queue 0 | ||
| 45 | */ | ||
| 46 | static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) | ||
| 47 | { | ||
| 48 | static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; | ||
| 49 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
| 50 | u16 rc; | ||
| 51 | |||
| 52 | skb->priority = cfg80211_classify8021d(skb); | ||
| 53 | |||
| 54 | rc = wil_1d_to_queue[skb->priority]; | ||
| 55 | |||
| 56 | wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, | ||
| 57 | (int)rc); | ||
| 58 | |||
| 59 | return rc; | ||
| 60 | } | ||
| 61 | |||
| 62 | static const struct net_device_ops wil_netdev_ops = { | ||
| 63 | .ndo_open = wil_open, | ||
| 64 | .ndo_stop = wil_stop, | ||
| 65 | .ndo_start_xmit = wil_start_xmit, | ||
| 66 | .ndo_select_queue = wil_select_queue, | ||
| 67 | .ndo_set_mac_address = eth_mac_addr, | ||
| 68 | .ndo_validate_addr = eth_validate_addr, | ||
| 69 | }; | ||
| 70 | |||
| 71 | void *wil_if_alloc(struct device *dev, void __iomem *csr) | ||
| 72 | { | ||
| 73 | struct net_device *ndev; | ||
| 74 | struct wireless_dev *wdev; | ||
| 75 | struct wil6210_priv *wil; | ||
| 76 | struct ieee80211_channel *ch; | ||
| 77 | int rc = 0; | ||
| 78 | |||
| 79 | wdev = wil_cfg80211_init(dev); | ||
| 80 | if (IS_ERR(wdev)) { | ||
| 81 | dev_err(dev, "wil_cfg80211_init failed\n"); | ||
| 82 | return wdev; | ||
| 83 | } | ||
| 84 | |||
| 85 | wil = wdev_to_wil(wdev); | ||
| 86 | wil->csr = csr; | ||
| 87 | wil->wdev = wdev; | ||
| 88 | |||
| 89 | rc = wil_priv_init(wil); | ||
| 90 | if (rc) { | ||
| 91 | dev_err(dev, "wil_priv_init failed\n"); | ||
| 92 | goto out_wdev; | ||
| 93 | } | ||
| 94 | |||
| 95 | wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ | ||
| 96 | /* default monitor channel */ | ||
| 97 | ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; | ||
| 98 | cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); | ||
| 99 | |||
| 100 | ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); | ||
| 101 | if (!ndev) { | ||
| 102 | dev_err(dev, "alloc_netdev_mqs failed\n"); | ||
| 103 | rc = -ENOMEM; | ||
| 104 | goto out_priv; | ||
| 105 | } | ||
| 106 | |||
| 107 | ndev->netdev_ops = &wil_netdev_ops; | ||
| 108 | ndev->ieee80211_ptr = wdev; | ||
| 109 | SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); | ||
| 110 | wdev->netdev = ndev; | ||
| 111 | |||
| 112 | wil_link_off(wil); | ||
| 113 | |||
| 114 | return wil; | ||
| 115 | |||
| 116 | out_priv: | ||
| 117 | wil_priv_deinit(wil); | ||
| 118 | |||
| 119 | out_wdev: | ||
| 120 | wil_wdev_free(wil); | ||
| 121 | |||
| 122 | return ERR_PTR(rc); | ||
| 123 | } | ||
| 124 | |||
| 125 | void wil_if_free(struct wil6210_priv *wil) | ||
| 126 | { | ||
| 127 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 128 | if (!ndev) | ||
| 129 | return; | ||
| 130 | |||
| 131 | free_netdev(ndev); | ||
| 132 | wil_priv_deinit(wil); | ||
| 133 | wil_wdev_free(wil); | ||
| 134 | } | ||
| 135 | |||
| 136 | int wil_if_add(struct wil6210_priv *wil) | ||
| 137 | { | ||
| 138 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 139 | int rc; | ||
| 140 | |||
| 141 | rc = register_netdev(ndev); | ||
| 142 | if (rc < 0) { | ||
| 143 | dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); | ||
| 144 | return rc; | ||
| 145 | } | ||
| 146 | |||
| 147 | wil_link_off(wil); | ||
| 148 | |||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | |||
| 152 | void wil_if_remove(struct wil6210_priv *wil) | ||
| 153 | { | ||
| 154 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 155 | |||
| 156 | unregister_netdev(ndev); | ||
| 157 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c new file mode 100644 index 000000000000..0fc83edd6bad --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c | |||
| @@ -0,0 +1,223 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/netdevice.h> | ||
| 21 | #include <linux/debugfs.h> | ||
| 22 | #include <linux/pci.h> | ||
| 23 | #include <linux/moduleparam.h> | ||
| 24 | |||
| 25 | #include "wil6210.h" | ||
| 26 | |||
| 27 | static int use_msi = 1; | ||
| 28 | module_param(use_msi, int, S_IRUGO); | ||
| 29 | MODULE_PARM_DESC(use_msi, | ||
| 30 | " Use MSI interrupt: " | ||
| 31 | "0 - don't, 1 - (default) - single, or 3"); | ||
| 32 | |||
| 33 | /* Bus ops */ | ||
| 34 | static int wil_if_pcie_enable(struct wil6210_priv *wil) | ||
| 35 | { | ||
| 36 | struct pci_dev *pdev = wil->pdev; | ||
| 37 | int rc; | ||
| 38 | |||
| 39 | pci_set_master(pdev); | ||
| 40 | |||
| 41 | /* | ||
| 42 | * how many MSI interrupts to request? | ||
| 43 | */ | ||
| 44 | switch (use_msi) { | ||
| 45 | case 3: | ||
| 46 | case 1: | ||
| 47 | case 0: | ||
| 48 | break; | ||
| 49 | default: | ||
| 50 | wil_err(wil, "Invalid use_msi=%d, default to 1\n", | ||
| 51 | use_msi); | ||
| 52 | use_msi = 1; | ||
| 53 | } | ||
| 54 | wil->n_msi = use_msi; | ||
| 55 | if (wil->n_msi) { | ||
| 56 | wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); | ||
| 57 | rc = pci_enable_msi_block(pdev, wil->n_msi); | ||
| 58 | if (rc && (wil->n_msi == 3)) { | ||
| 59 | wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); | ||
| 60 | wil->n_msi = 1; | ||
| 61 | rc = pci_enable_msi_block(pdev, wil->n_msi); | ||
| 62 | } | ||
| 63 | if (rc) { | ||
| 64 | wil_err(wil, "pci_enable_msi failed, use INTx\n"); | ||
| 65 | wil->n_msi = 0; | ||
| 66 | } | ||
| 67 | } else { | ||
| 68 | wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); | ||
| 69 | } | ||
| 70 | |||
| 71 | rc = wil6210_init_irq(wil, pdev->irq); | ||
| 72 | if (rc) | ||
| 73 | goto stop_master; | ||
| 74 | |||
| 75 | /* need reset here to obtain MAC */ | ||
| 76 | rc = wil_reset(wil); | ||
| 77 | if (rc) | ||
| 78 | goto release_irq; | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | |||
| 82 | release_irq: | ||
| 83 | wil6210_fini_irq(wil, pdev->irq); | ||
| 84 | /* safe to call if no MSI */ | ||
| 85 | pci_disable_msi(pdev); | ||
| 86 | stop_master: | ||
| 87 | pci_clear_master(pdev); | ||
| 88 | return rc; | ||
| 89 | } | ||
| 90 | |||
| 91 | static int wil_if_pcie_disable(struct wil6210_priv *wil) | ||
| 92 | { | ||
| 93 | struct pci_dev *pdev = wil->pdev; | ||
| 94 | |||
| 95 | pci_clear_master(pdev); | ||
| 96 | /* disable and release IRQ */ | ||
| 97 | wil6210_fini_irq(wil, pdev->irq); | ||
| 98 | /* safe to call if no MSI */ | ||
| 99 | pci_disable_msi(pdev); | ||
| 100 | /* TODO: disable HW */ | ||
| 101 | |||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 105 | static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||
| 106 | { | ||
| 107 | struct wil6210_priv *wil; | ||
| 108 | struct device *dev = &pdev->dev; | ||
| 109 | void __iomem *csr; | ||
| 110 | int rc; | ||
| 111 | |||
| 112 | /* check HW */ | ||
| 113 | dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", | ||
| 114 | (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); | ||
| 115 | |||
| 116 | if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { | ||
| 117 | dev_err(&pdev->dev, "Not " WIL_NAME "? " | ||
| 118 | "BAR0 size is %lu while expecting %lu\n", | ||
| 119 | (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); | ||
| 120 | return -ENODEV; | ||
| 121 | } | ||
| 122 | |||
| 123 | rc = pci_enable_device(pdev); | ||
| 124 | if (rc) { | ||
| 125 | dev_err(&pdev->dev, "pci_enable_device failed\n"); | ||
| 126 | return -ENODEV; | ||
| 127 | } | ||
| 128 | /* rollback to err_disable_pdev */ | ||
| 129 | |||
| 130 | rc = pci_request_region(pdev, 0, WIL_NAME); | ||
| 131 | if (rc) { | ||
| 132 | dev_err(&pdev->dev, "pci_request_region failed\n"); | ||
| 133 | goto err_disable_pdev; | ||
| 134 | } | ||
| 135 | /* rollback to err_release_reg */ | ||
| 136 | |||
| 137 | csr = pci_ioremap_bar(pdev, 0); | ||
| 138 | if (!csr) { | ||
| 139 | dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); | ||
| 140 | rc = -ENODEV; | ||
| 141 | goto err_release_reg; | ||
| 142 | } | ||
| 143 | /* rollback to err_iounmap */ | ||
| 144 | dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); | ||
| 145 | |||
| 146 | wil = wil_if_alloc(dev, csr); | ||
| 147 | if (IS_ERR(wil)) { | ||
| 148 | rc = (int)PTR_ERR(wil); | ||
| 149 | dev_err(dev, "wil_if_alloc failed: %d\n", rc); | ||
| 150 | goto err_iounmap; | ||
| 151 | } | ||
| 152 | /* rollback to if_free */ | ||
| 153 | |||
| 154 | pci_set_drvdata(pdev, wil); | ||
| 155 | wil->pdev = pdev; | ||
| 156 | |||
| 157 | /* FW should raise IRQ when ready */ | ||
| 158 | rc = wil_if_pcie_enable(wil); | ||
| 159 | if (rc) { | ||
| 160 | wil_err(wil, "Enable device failed\n"); | ||
| 161 | goto if_free; | ||
| 162 | } | ||
| 163 | /* rollback to bus_disable */ | ||
| 164 | |||
| 165 | rc = wil_if_add(wil); | ||
| 166 | if (rc) { | ||
| 167 | wil_err(wil, "wil_if_add failed: %d\n", rc); | ||
| 168 | goto bus_disable; | ||
| 169 | } | ||
| 170 | |||
| 171 | wil6210_debugfs_init(wil); | ||
| 172 | |||
| 173 | /* check FW is alive */ | ||
| 174 | wmi_echo(wil); | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | |||
| 178 | bus_disable: | ||
| 179 | wil_if_pcie_disable(wil); | ||
| 180 | if_free: | ||
| 181 | wil_if_free(wil); | ||
| 182 | err_iounmap: | ||
| 183 | pci_iounmap(pdev, csr); | ||
| 184 | err_release_reg: | ||
| 185 | pci_release_region(pdev, 0); | ||
| 186 | err_disable_pdev: | ||
| 187 | pci_disable_device(pdev); | ||
| 188 | |||
| 189 | return rc; | ||
| 190 | } | ||
| 191 | |||
| 192 | static void wil_pcie_remove(struct pci_dev *pdev) | ||
| 193 | { | ||
| 194 | struct wil6210_priv *wil = pci_get_drvdata(pdev); | ||
| 195 | |||
| 196 | wil6210_debugfs_remove(wil); | ||
| 197 | wil_if_pcie_disable(wil); | ||
| 198 | wil_if_remove(wil); | ||
| 199 | wil_if_free(wil); | ||
| 200 | pci_iounmap(pdev, wil->csr); | ||
| 201 | pci_release_region(pdev, 0); | ||
| 202 | pci_disable_device(pdev); | ||
| 203 | pci_set_drvdata(pdev, NULL); | ||
| 204 | } | ||
| 205 | |||
| 206 | static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { | ||
| 207 | { PCI_DEVICE(0x1ae9, 0x0301) }, | ||
| 208 | { /* end: all zeroes */ }, | ||
| 209 | }; | ||
| 210 | MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); | ||
| 211 | |||
| 212 | static struct pci_driver wil6210_driver = { | ||
| 213 | .probe = wil_pcie_probe, | ||
| 214 | .remove = wil_pcie_remove, | ||
| 215 | .id_table = wil6210_pcie_ids, | ||
| 216 | .name = WIL_NAME, | ||
| 217 | }; | ||
| 218 | |||
| 219 | module_pci_driver(wil6210_driver); | ||
| 220 | |||
| 221 | MODULE_LICENSE("Dual BSD/GPL"); | ||
| 222 | MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>"); | ||
| 223 | MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); | ||
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c new file mode 100644 index 000000000000..f29c294413cf --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.c | |||
| @@ -0,0 +1,871 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/netdevice.h> | ||
| 19 | #include <linux/etherdevice.h> | ||
| 20 | #include <linux/hardirq.h> | ||
| 21 | #include <net/ieee80211_radiotap.h> | ||
| 22 | #include <linux/if_arp.h> | ||
| 23 | #include <linux/moduleparam.h> | ||
| 24 | |||
| 25 | #include "wil6210.h" | ||
| 26 | #include "wmi.h" | ||
| 27 | #include "txrx.h" | ||
| 28 | |||
| 29 | static bool rtap_include_phy_info; | ||
| 30 | module_param(rtap_include_phy_info, bool, S_IRUGO); | ||
| 31 | MODULE_PARM_DESC(rtap_include_phy_info, | ||
| 32 | " Include PHY info in the radiotap header, default - no"); | ||
| 33 | |||
| 34 | static inline int wil_vring_is_empty(struct vring *vring) | ||
| 35 | { | ||
| 36 | return vring->swhead == vring->swtail; | ||
| 37 | } | ||
| 38 | |||
| 39 | static inline u32 wil_vring_next_tail(struct vring *vring) | ||
| 40 | { | ||
| 41 | return (vring->swtail + 1) % vring->size; | ||
| 42 | } | ||
| 43 | |||
| 44 | static inline void wil_vring_advance_head(struct vring *vring, int n) | ||
| 45 | { | ||
| 46 | vring->swhead = (vring->swhead + n) % vring->size; | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline int wil_vring_is_full(struct vring *vring) | ||
| 50 | { | ||
| 51 | return wil_vring_next_tail(vring) == vring->swhead; | ||
| 52 | } | ||
| 53 | /* | ||
| 54 | * Available space in Tx Vring | ||
| 55 | */ | ||
| 56 | static inline int wil_vring_avail_tx(struct vring *vring) | ||
| 57 | { | ||
| 58 | u32 swhead = vring->swhead; | ||
| 59 | u32 swtail = vring->swtail; | ||
| 60 | int used = (vring->size + swhead - swtail) % vring->size; | ||
| 61 | |||
| 62 | return vring->size - used - 1; | ||
| 63 | } | ||
| 64 | |||
| 65 | static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) | ||
| 66 | { | ||
| 67 | struct device *dev = wil_to_dev(wil); | ||
| 68 | size_t sz = vring->size * sizeof(vring->va[0]); | ||
| 69 | uint i; | ||
| 70 | |||
| 71 | BUILD_BUG_ON(sizeof(vring->va[0]) != 32); | ||
| 72 | |||
| 73 | vring->swhead = 0; | ||
| 74 | vring->swtail = 0; | ||
| 75 | vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); | ||
| 76 | if (!vring->ctx) { | ||
| 77 | wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", | ||
| 78 | vring->size); | ||
| 79 | vring->va = NULL; | ||
| 80 | return -ENOMEM; | ||
| 81 | } | ||
| 82 | /* | ||
| 83 | * vring->va should be aligned on its size rounded up to power of 2 | ||
| 84 | * This is granted by the dma_alloc_coherent | ||
| 85 | */ | ||
| 86 | vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); | ||
| 87 | if (!vring->va) { | ||
| 88 | wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", | ||
| 89 | vring->size); | ||
| 90 | kfree(vring->ctx); | ||
| 91 | vring->ctx = NULL; | ||
| 92 | return -ENOMEM; | ||
| 93 | } | ||
| 94 | /* initially, all descriptors are SW owned | ||
| 95 | * For Tx and Rx, ownership bit is at the same location, thus | ||
| 96 | * we can use any | ||
| 97 | */ | ||
| 98 | for (i = 0; i < vring->size; i++) { | ||
| 99 | volatile struct vring_tx_desc *d = &(vring->va[i].tx); | ||
| 100 | d->dma.status = TX_DMA_STATUS_DU; | ||
| 101 | } | ||
| 102 | |||
| 103 | wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, | ||
| 104 | vring->va, (unsigned long long)vring->pa, vring->ctx); | ||
| 105 | |||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, | ||
| 110 | int tx) | ||
| 111 | { | ||
| 112 | struct device *dev = wil_to_dev(wil); | ||
| 113 | size_t sz = vring->size * sizeof(vring->va[0]); | ||
| 114 | |||
| 115 | while (!wil_vring_is_empty(vring)) { | ||
| 116 | if (tx) { | ||
| 117 | volatile struct vring_tx_desc *d = | ||
| 118 | &vring->va[vring->swtail].tx; | ||
| 119 | dma_addr_t pa = d->dma.addr_low | | ||
| 120 | ((u64)d->dma.addr_high << 32); | ||
| 121 | struct sk_buff *skb = vring->ctx[vring->swtail]; | ||
| 122 | if (skb) { | ||
| 123 | dma_unmap_single(dev, pa, d->dma.length, | ||
| 124 | DMA_TO_DEVICE); | ||
| 125 | dev_kfree_skb_any(skb); | ||
| 126 | vring->ctx[vring->swtail] = NULL; | ||
| 127 | } else { | ||
| 128 | dma_unmap_page(dev, pa, d->dma.length, | ||
| 129 | DMA_TO_DEVICE); | ||
| 130 | } | ||
| 131 | vring->swtail = wil_vring_next_tail(vring); | ||
| 132 | } else { /* rx */ | ||
| 133 | volatile struct vring_rx_desc *d = | ||
| 134 | &vring->va[vring->swtail].rx; | ||
| 135 | dma_addr_t pa = d->dma.addr_low | | ||
| 136 | ((u64)d->dma.addr_high << 32); | ||
| 137 | struct sk_buff *skb = vring->ctx[vring->swhead]; | ||
| 138 | dma_unmap_single(dev, pa, d->dma.length, | ||
| 139 | DMA_FROM_DEVICE); | ||
| 140 | kfree_skb(skb); | ||
| 141 | wil_vring_advance_head(vring, 1); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); | ||
| 145 | kfree(vring->ctx); | ||
| 146 | vring->pa = 0; | ||
| 147 | vring->va = NULL; | ||
| 148 | vring->ctx = NULL; | ||
| 149 | } | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Allocate one skb for Rx VRING | ||
| 153 | * | ||
| 154 | * Safe to call from IRQ | ||
| 155 | */ | ||
| 156 | static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, | ||
| 157 | u32 i, int headroom) | ||
| 158 | { | ||
| 159 | struct device *dev = wil_to_dev(wil); | ||
| 160 | unsigned int sz = RX_BUF_LEN; | ||
| 161 | volatile struct vring_rx_desc *d = &(vring->va[i].rx); | ||
| 162 | dma_addr_t pa; | ||
| 163 | |||
| 164 | /* TODO align */ | ||
| 165 | struct sk_buff *skb = dev_alloc_skb(sz + headroom); | ||
| 166 | if (unlikely(!skb)) | ||
| 167 | return -ENOMEM; | ||
| 168 | |||
| 169 | skb_reserve(skb, headroom); | ||
| 170 | skb_put(skb, sz); | ||
| 171 | |||
| 172 | pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); | ||
| 173 | if (unlikely(dma_mapping_error(dev, pa))) { | ||
| 174 | kfree_skb(skb); | ||
| 175 | return -ENOMEM; | ||
| 176 | } | ||
| 177 | |||
| 178 | d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; | ||
| 179 | d->dma.addr_low = lower_32_bits(pa); | ||
| 180 | d->dma.addr_high = (u16)upper_32_bits(pa); | ||
| 181 | /* ip_length don't care */ | ||
| 182 | /* b11 don't care */ | ||
| 183 | /* error don't care */ | ||
| 184 | d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ | ||
| 185 | d->dma.length = sz; | ||
| 186 | vring->ctx[i] = skb; | ||
| 187 | |||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * Adds radiotap header | ||
| 193 | * | ||
| 194 | * Any error indicated as "Bad FCS" | ||
| 195 | * | ||
| 196 | * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: | ||
| 197 | * - Rx descriptor: 32 bytes | ||
| 198 | * - Phy info | ||
| 199 | */ | ||
| 200 | static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, | ||
| 201 | struct sk_buff *skb, | ||
| 202 | volatile struct vring_rx_desc *d) | ||
| 203 | { | ||
| 204 | struct wireless_dev *wdev = wil->wdev; | ||
| 205 | struct wil6210_rtap { | ||
| 206 | struct ieee80211_radiotap_header rthdr; | ||
| 207 | /* fields should be in the order of bits in rthdr.it_present */ | ||
| 208 | /* flags */ | ||
| 209 | u8 flags; | ||
| 210 | /* channel */ | ||
| 211 | __le16 chnl_freq __aligned(2); | ||
| 212 | __le16 chnl_flags; | ||
| 213 | /* MCS */ | ||
| 214 | u8 mcs_present; | ||
| 215 | u8 mcs_flags; | ||
| 216 | u8 mcs_index; | ||
| 217 | } __packed; | ||
| 218 | struct wil6210_rtap_vendor { | ||
| 219 | struct wil6210_rtap rtap; | ||
| 220 | /* vendor */ | ||
| 221 | u8 vendor_oui[3] __aligned(2); | ||
| 222 | u8 vendor_ns; | ||
| 223 | __le16 vendor_skip; | ||
| 224 | u8 vendor_data[0]; | ||
| 225 | } __packed; | ||
| 226 | struct wil6210_rtap_vendor *rtap_vendor; | ||
| 227 | int rtap_len = sizeof(struct wil6210_rtap); | ||
| 228 | int phy_length = 0; /* phy info header size, bytes */ | ||
| 229 | static char phy_data[128]; | ||
| 230 | struct ieee80211_channel *ch = wdev->preset_chandef.chan; | ||
| 231 | |||
| 232 | if (rtap_include_phy_info) { | ||
| 233 | rtap_len = sizeof(*rtap_vendor) + sizeof(*d); | ||
| 234 | /* calculate additional length */ | ||
| 235 | if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { | ||
| 236 | /** | ||
| 237 | * PHY info starts from 8-byte boundary | ||
| 238 | * there are 8-byte lines, last line may be partially | ||
| 239 | * written (HW bug), thus FW configures for last line | ||
| 240 | * to be excessive. Driver skips this last line. | ||
| 241 | */ | ||
| 242 | int len = min_t(int, 8 + sizeof(phy_data), | ||
| 243 | wil_rxdesc_phy_length(d)); | ||
| 244 | if (len > 8) { | ||
| 245 | void *p = skb_tail_pointer(skb); | ||
| 246 | void *pa = PTR_ALIGN(p, 8); | ||
| 247 | if (skb_tailroom(skb) >= len + (pa - p)) { | ||
| 248 | phy_length = len - 8; | ||
| 249 | memcpy(phy_data, pa, phy_length); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | rtap_len += phy_length; | ||
| 254 | } | ||
| 255 | |||
| 256 | if (skb_headroom(skb) < rtap_len && | ||
| 257 | pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { | ||
| 258 | wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); | ||
| 259 | return; | ||
| 260 | } | ||
| 261 | |||
| 262 | rtap_vendor = (void *)skb_push(skb, rtap_len); | ||
| 263 | memset(rtap_vendor, 0, rtap_len); | ||
| 264 | |||
| 265 | rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; | ||
| 266 | rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); | ||
| 267 | rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( | ||
| 268 | (1 << IEEE80211_RADIOTAP_FLAGS) | | ||
| 269 | (1 << IEEE80211_RADIOTAP_CHANNEL) | | ||
| 270 | (1 << IEEE80211_RADIOTAP_MCS)); | ||
| 271 | if (d->dma.status & RX_DMA_STATUS_ERROR) | ||
| 272 | rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; | ||
| 273 | |||
| 274 | rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); | ||
| 275 | rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); | ||
| 276 | |||
| 277 | rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; | ||
| 278 | rtap_vendor->rtap.mcs_flags = 0; | ||
| 279 | rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d); | ||
| 280 | |||
| 281 | if (rtap_include_phy_info) { | ||
| 282 | rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << | ||
| 283 | IEEE80211_RADIOTAP_VENDOR_NAMESPACE); | ||
| 284 | /* OUI for Wilocity 04:ce:14 */ | ||
| 285 | rtap_vendor->vendor_oui[0] = 0x04; | ||
| 286 | rtap_vendor->vendor_oui[1] = 0xce; | ||
| 287 | rtap_vendor->vendor_oui[2] = 0x14; | ||
| 288 | rtap_vendor->vendor_ns = 1; | ||
| 289 | /* Rx descriptor + PHY data */ | ||
| 290 | rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + | ||
| 291 | phy_length); | ||
| 292 | memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d)); | ||
| 293 | memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, | ||
| 294 | phy_length); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | /* | ||
| 299 | * Fast swap in place between 2 registers | ||
| 300 | */ | ||
| 301 | static void wil_swap_u16(u16 *a, u16 *b) | ||
| 302 | { | ||
| 303 | *a ^= *b; | ||
| 304 | *b ^= *a; | ||
| 305 | *a ^= *b; | ||
| 306 | } | ||
| 307 | |||
| 308 | static void wil_swap_ethaddr(void *data) | ||
| 309 | { | ||
| 310 | struct ethhdr *eth = data; | ||
| 311 | u16 *s = (u16 *)eth->h_source; | ||
| 312 | u16 *d = (u16 *)eth->h_dest; | ||
| 313 | |||
| 314 | wil_swap_u16(s++, d++); | ||
| 315 | wil_swap_u16(s++, d++); | ||
| 316 | wil_swap_u16(s, d); | ||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * reap 1 frame from @swhead | ||
| 321 | * | ||
| 322 | * Safe to call from IRQ | ||
| 323 | */ | ||
| 324 | static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, | ||
| 325 | struct vring *vring) | ||
| 326 | { | ||
| 327 | struct device *dev = wil_to_dev(wil); | ||
| 328 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 329 | volatile struct vring_rx_desc *d; | ||
| 330 | struct sk_buff *skb; | ||
| 331 | dma_addr_t pa; | ||
| 332 | unsigned int sz = RX_BUF_LEN; | ||
| 333 | u8 ftype; | ||
| 334 | u8 ds_bits; | ||
| 335 | |||
| 336 | if (wil_vring_is_empty(vring)) | ||
| 337 | return NULL; | ||
| 338 | |||
| 339 | d = &(vring->va[vring->swhead].rx); | ||
| 340 | if (!(d->dma.status & RX_DMA_STATUS_DU)) { | ||
| 341 | /* it is not error, we just reached end of Rx done area */ | ||
| 342 | return NULL; | ||
| 343 | } | ||
| 344 | |||
| 345 | pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); | ||
| 346 | skb = vring->ctx[vring->swhead]; | ||
| 347 | dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); | ||
| 348 | skb_trim(skb, d->dma.length); | ||
| 349 | |||
| 350 | wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); | ||
| 351 | |||
| 352 | /* use radiotap header only if required */ | ||
| 353 | if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) | ||
| 354 | wil_rx_add_radiotap_header(wil, skb, d); | ||
| 355 | |||
| 356 | wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); | ||
| 357 | wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, | ||
| 358 | (const void *)d, sizeof(*d), false); | ||
| 359 | |||
| 360 | wil_vring_advance_head(vring, 1); | ||
| 361 | |||
| 362 | /* no extra checks if in sniffer mode */ | ||
| 363 | if (ndev->type != ARPHRD_ETHER) | ||
| 364 | return skb; | ||
| 365 | /* | ||
| 366 | * Non-data frames may be delivered through Rx DMA channel (ex: BAR) | ||
| 367 | * Driver should recognize it by frame type, that is found | ||
| 368 | * in Rx descriptor. If type is not data, it is 802.11 frame as is | ||
| 369 | */ | ||
| 370 | ftype = wil_rxdesc_ftype(d) << 2; | ||
| 371 | if (ftype != IEEE80211_FTYPE_DATA) { | ||
| 372 | wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); | ||
| 373 | /* TODO: process it */ | ||
| 374 | kfree_skb(skb); | ||
| 375 | return NULL; | ||
| 376 | } | ||
| 377 | |||
| 378 | if (skb->len < ETH_HLEN) { | ||
| 379 | wil_err(wil, "Short frame, len = %d\n", skb->len); | ||
| 380 | /* TODO: process it (i.e. BAR) */ | ||
| 381 | kfree_skb(skb); | ||
| 382 | return NULL; | ||
| 383 | } | ||
| 384 | |||
| 385 | ds_bits = wil_rxdesc_ds_bits(d); | ||
| 386 | if (ds_bits == 1) { | ||
| 387 | /* | ||
| 388 | * HW bug - in ToDS mode, i.e. Rx on AP side, | ||
| 389 | * addresses get swapped | ||
| 390 | */ | ||
| 391 | wil_swap_ethaddr(skb->data); | ||
| 392 | } | ||
| 393 | |||
| 394 | return skb; | ||
| 395 | } | ||
| 396 | |||
| 397 | /** | ||
| 398 | * allocate and fill up to @count buffers in rx ring | ||
| 399 | * buffers posted at @swtail | ||
| 400 | */ | ||
| 401 | static int wil_rx_refill(struct wil6210_priv *wil, int count) | ||
| 402 | { | ||
| 403 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 404 | struct vring *v = &wil->vring_rx; | ||
| 405 | u32 next_tail; | ||
| 406 | int rc = 0; | ||
| 407 | int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? | ||
| 408 | WIL6210_RTAP_SIZE : 0; | ||
| 409 | |||
| 410 | for (; next_tail = wil_vring_next_tail(v), | ||
| 411 | (next_tail != v->swhead) && (count-- > 0); | ||
| 412 | v->swtail = next_tail) { | ||
| 413 | rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); | ||
| 414 | if (rc) { | ||
| 415 | wil_err(wil, "Error %d in wil_rx_refill[%d]\n", | ||
| 416 | rc, v->swtail); | ||
| 417 | break; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); | ||
| 421 | |||
| 422 | return rc; | ||
| 423 | } | ||
| 424 | |||
| 425 | /* | ||
| 426 | * Pass Rx packet to the netif. Update statistics. | ||
| 427 | */ | ||
| 428 | static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) | ||
| 429 | { | ||
| 430 | int rc; | ||
| 431 | unsigned int len = skb->len; | ||
| 432 | |||
| 433 | if (in_interrupt()) | ||
| 434 | rc = netif_rx(skb); | ||
| 435 | else | ||
| 436 | rc = netif_rx_ni(skb); | ||
| 437 | |||
| 438 | if (likely(rc == NET_RX_SUCCESS)) { | ||
| 439 | ndev->stats.rx_packets++; | ||
| 440 | ndev->stats.rx_bytes += len; | ||
| 441 | |||
| 442 | } else { | ||
| 443 | ndev->stats.rx_dropped++; | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 447 | /** | ||
| 448 | * Proceed all completed skb's from Rx VRING | ||
| 449 | * | ||
| 450 | * Safe to call from IRQ | ||
| 451 | */ | ||
| 452 | void wil_rx_handle(struct wil6210_priv *wil) | ||
| 453 | { | ||
| 454 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 455 | struct vring *v = &wil->vring_rx; | ||
| 456 | struct sk_buff *skb; | ||
| 457 | |||
| 458 | if (!v->va) { | ||
| 459 | wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); | ||
| 460 | return; | ||
| 461 | } | ||
| 462 | wil_dbg_TXRX(wil, "%s()\n", __func__); | ||
| 463 | while (NULL != (skb = wil_vring_reap_rx(wil, v))) { | ||
| 464 | wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, | ||
| 465 | skb->data, skb_headlen(skb), false); | ||
| 466 | |||
| 467 | skb_orphan(skb); | ||
| 468 | |||
| 469 | if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { | ||
| 470 | skb->dev = ndev; | ||
| 471 | skb_reset_mac_header(skb); | ||
| 472 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 473 | skb->pkt_type = PACKET_OTHERHOST; | ||
| 474 | skb->protocol = htons(ETH_P_802_2); | ||
| 475 | |||
| 476 | } else { | ||
| 477 | skb->protocol = eth_type_trans(skb, ndev); | ||
| 478 | } | ||
| 479 | |||
| 480 | wil_netif_rx_any(skb, ndev); | ||
| 481 | } | ||
| 482 | wil_rx_refill(wil, v->size); | ||
| 483 | } | ||
| 484 | |||
| 485 | int wil_rx_init(struct wil6210_priv *wil) | ||
| 486 | { | ||
| 487 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 488 | struct wireless_dev *wdev = wil->wdev; | ||
| 489 | struct vring *vring = &wil->vring_rx; | ||
| 490 | int rc; | ||
| 491 | struct wmi_cfg_rx_chain_cmd cmd = { | ||
| 492 | .action = WMI_RX_CHAIN_ADD, | ||
| 493 | .rx_sw_ring = { | ||
| 494 | .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), | ||
| 495 | }, | ||
| 496 | .mid = 0, /* TODO - what is it? */ | ||
| 497 | .decap_trans_type = WMI_DECAP_TYPE_802_3, | ||
| 498 | }; | ||
| 499 | struct { | ||
| 500 | struct wil6210_mbox_hdr_wmi wmi; | ||
| 501 | struct wmi_cfg_rx_chain_done_event evt; | ||
| 502 | } __packed evt; | ||
| 503 | |||
| 504 | vring->size = WIL6210_RX_RING_SIZE; | ||
| 505 | rc = wil_vring_alloc(wil, vring); | ||
| 506 | if (rc) | ||
| 507 | return rc; | ||
| 508 | |||
| 509 | cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); | ||
| 510 | cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); | ||
| 511 | if (wdev->iftype == NL80211_IFTYPE_MONITOR) { | ||
| 512 | struct ieee80211_channel *ch = wdev->preset_chandef.chan; | ||
| 513 | |||
| 514 | cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); | ||
| 515 | if (ch) | ||
| 516 | cmd.sniffer_cfg.channel = ch->hw_value - 1; | ||
| 517 | cmd.sniffer_cfg.phy_info_mode = | ||
| 518 | cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); | ||
| 519 | cmd.sniffer_cfg.phy_support = | ||
| 520 | cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) | ||
| 521 | ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); | ||
| 522 | } | ||
| 523 | /* typical time for secure PCP is 840ms */ | ||
| 524 | rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), | ||
| 525 | WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); | ||
| 526 | if (rc) | ||
| 527 | goto err_free; | ||
| 528 | |||
| 529 | vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); | ||
| 530 | |||
| 531 | wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", | ||
| 532 | le32_to_cpu(evt.evt.status), vring->hwtail); | ||
| 533 | |||
| 534 | rc = wil_rx_refill(wil, vring->size); | ||
| 535 | if (rc) | ||
| 536 | goto err_free; | ||
| 537 | |||
| 538 | return 0; | ||
| 539 | err_free: | ||
| 540 | wil_vring_free(wil, vring, 0); | ||
| 541 | |||
| 542 | return rc; | ||
| 543 | } | ||
| 544 | |||
| 545 | void wil_rx_fini(struct wil6210_priv *wil) | ||
| 546 | { | ||
| 547 | struct vring *vring = &wil->vring_rx; | ||
| 548 | |||
| 549 | if (vring->va) { | ||
| 550 | int rc; | ||
| 551 | struct wmi_cfg_rx_chain_cmd cmd = { | ||
| 552 | .action = cpu_to_le32(WMI_RX_CHAIN_DEL), | ||
| 553 | .rx_sw_ring = { | ||
| 554 | .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), | ||
| 555 | }, | ||
| 556 | }; | ||
| 557 | struct { | ||
| 558 | struct wil6210_mbox_hdr_wmi wmi; | ||
| 559 | struct wmi_cfg_rx_chain_done_event cfg; | ||
| 560 | } __packed wmi_rx_cfg_reply; | ||
| 561 | |||
| 562 | rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), | ||
| 563 | WMI_CFG_RX_CHAIN_DONE_EVENTID, | ||
| 564 | &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), | ||
| 565 | 100); | ||
| 566 | wil_vring_free(wil, vring, 0); | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, | ||
| 571 | int cid, int tid) | ||
| 572 | { | ||
| 573 | int rc; | ||
| 574 | struct wmi_vring_cfg_cmd cmd = { | ||
| 575 | .action = cpu_to_le32(WMI_VRING_CMD_ADD), | ||
| 576 | .vring_cfg = { | ||
| 577 | .tx_sw_ring = { | ||
| 578 | .max_mpdu_size = cpu_to_le16(TX_BUF_LEN), | ||
| 579 | }, | ||
| 580 | .ringid = id, | ||
| 581 | .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), | ||
| 582 | .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, | ||
| 583 | .mac_ctrl = 0, | ||
| 584 | .to_resolution = 0, | ||
| 585 | .agg_max_wsize = 16, | ||
| 586 | .schd_params = { | ||
| 587 | .priority = cpu_to_le16(0), | ||
| 588 | .timeslot_us = cpu_to_le16(0xfff), | ||
| 589 | }, | ||
| 590 | }, | ||
| 591 | }; | ||
| 592 | struct { | ||
| 593 | struct wil6210_mbox_hdr_wmi wmi; | ||
| 594 | struct wmi_vring_cfg_done_event cmd; | ||
| 595 | } __packed reply; | ||
| 596 | struct vring *vring = &wil->vring_tx[id]; | ||
| 597 | |||
| 598 | if (vring->va) { | ||
| 599 | wil_err(wil, "Tx ring [%d] already allocated\n", id); | ||
| 600 | rc = -EINVAL; | ||
| 601 | goto out; | ||
| 602 | } | ||
| 603 | |||
| 604 | vring->size = size; | ||
| 605 | rc = wil_vring_alloc(wil, vring); | ||
| 606 | if (rc) | ||
| 607 | goto out; | ||
| 608 | |||
| 609 | cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); | ||
| 610 | cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size); | ||
| 611 | |||
| 612 | rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), | ||
| 613 | WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); | ||
| 614 | if (rc) | ||
| 615 | goto out_free; | ||
| 616 | |||
| 617 | if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { | ||
| 618 | wil_err(wil, "Tx config failed, status 0x%02x\n", | ||
| 619 | reply.cmd.status); | ||
| 620 | goto out_free; | ||
| 621 | } | ||
| 622 | vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); | ||
| 623 | |||
| 624 | return 0; | ||
| 625 | out_free: | ||
| 626 | wil_vring_free(wil, vring, 1); | ||
| 627 | out: | ||
| 628 | |||
| 629 | return rc; | ||
| 630 | } | ||
| 631 | |||
| 632 | void wil_vring_fini_tx(struct wil6210_priv *wil, int id) | ||
| 633 | { | ||
| 634 | struct vring *vring = &wil->vring_tx[id]; | ||
| 635 | |||
| 636 | if (!vring->va) | ||
| 637 | return; | ||
| 638 | |||
| 639 | wil_vring_free(wil, vring, 1); | ||
| 640 | } | ||
| 641 | |||
| 642 | static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, | ||
| 643 | struct sk_buff *skb) | ||
| 644 | { | ||
| 645 | struct vring *v = &wil->vring_tx[0]; | ||
| 646 | |||
| 647 | if (v->va) | ||
| 648 | return v; | ||
| 649 | |||
| 650 | return NULL; | ||
| 651 | } | ||
| 652 | |||
| 653 | static int wil_tx_desc_map(volatile struct vring_tx_desc *d, | ||
| 654 | dma_addr_t pa, u32 len) | ||
| 655 | { | ||
| 656 | d->dma.addr_low = lower_32_bits(pa); | ||
| 657 | d->dma.addr_high = (u16)upper_32_bits(pa); | ||
| 658 | d->dma.ip_length = 0; | ||
| 659 | /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ | ||
| 660 | d->dma.b11 = 0/*14 | BIT(7)*/; | ||
| 661 | d->dma.error = 0; | ||
| 662 | d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ | ||
| 663 | d->dma.length = len; | ||
| 664 | d->dma.d0 = 0; | ||
| 665 | d->mac.d[0] = 0; | ||
| 666 | d->mac.d[1] = 0; | ||
| 667 | d->mac.d[2] = 0; | ||
| 668 | d->mac.ucode_cmd = 0; | ||
| 669 | /* use dst index 0 */ | ||
| 670 | d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | | ||
| 671 | (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); | ||
| 672 | /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ | ||
| 673 | d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | | ||
| 674 | (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); | ||
| 675 | |||
| 676 | return 0; | ||
| 677 | } | ||
| 678 | |||
| 679 | static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, | ||
| 680 | struct sk_buff *skb) | ||
| 681 | { | ||
| 682 | struct device *dev = wil_to_dev(wil); | ||
| 683 | volatile struct vring_tx_desc *d; | ||
| 684 | u32 swhead = vring->swhead; | ||
| 685 | int avail = wil_vring_avail_tx(vring); | ||
| 686 | int nr_frags = skb_shinfo(skb)->nr_frags; | ||
| 687 | uint f; | ||
| 688 | int vring_index = vring - wil->vring_tx; | ||
| 689 | uint i = swhead; | ||
| 690 | dma_addr_t pa; | ||
| 691 | |||
| 692 | wil_dbg_TXRX(wil, "%s()\n", __func__); | ||
| 693 | |||
| 694 | if (avail < vring->size/8) | ||
| 695 | netif_tx_stop_all_queues(wil_to_ndev(wil)); | ||
| 696 | if (avail < 1 + nr_frags) { | ||
| 697 | wil_err(wil, "Tx ring full. No space for %d fragments\n", | ||
| 698 | 1 + nr_frags); | ||
| 699 | return -ENOMEM; | ||
| 700 | } | ||
| 701 | d = &(vring->va[i].tx); | ||
| 702 | |||
| 703 | /* FIXME FW can accept only unicast frames for the peer */ | ||
| 704 | memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); | ||
| 705 | |||
| 706 | pa = dma_map_single(dev, skb->data, | ||
| 707 | skb_headlen(skb), DMA_TO_DEVICE); | ||
| 708 | |||
| 709 | wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), | ||
| 710 | skb->data, (unsigned long long)pa); | ||
| 711 | wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, | ||
| 712 | skb->data, skb_headlen(skb), false); | ||
| 713 | |||
| 714 | if (unlikely(dma_mapping_error(dev, pa))) | ||
| 715 | return -EINVAL; | ||
| 716 | /* 1-st segment */ | ||
| 717 | wil_tx_desc_map(d, pa, skb_headlen(skb)); | ||
| 718 | d->mac.d[2] |= ((nr_frags + 1) << | ||
| 719 | MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); | ||
| 720 | /* middle segments */ | ||
| 721 | for (f = 0; f < nr_frags; f++) { | ||
| 722 | const struct skb_frag_struct *frag = | ||
| 723 | &skb_shinfo(skb)->frags[f]; | ||
| 724 | int len = skb_frag_size(frag); | ||
| 725 | i = (swhead + f + 1) % vring->size; | ||
| 726 | d = &(vring->va[i].tx); | ||
| 727 | pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), | ||
| 728 | DMA_TO_DEVICE); | ||
| 729 | if (unlikely(dma_mapping_error(dev, pa))) | ||
| 730 | goto dma_error; | ||
| 731 | wil_tx_desc_map(d, pa, len); | ||
| 732 | vring->ctx[i] = NULL; | ||
| 733 | } | ||
| 734 | /* for the last seg only */ | ||
| 735 | d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); | ||
| 736 | d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ | ||
| 737 | d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); | ||
| 738 | d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); | ||
| 739 | |||
| 740 | wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, | ||
| 741 | (const void *)d, sizeof(*d), false); | ||
| 742 | |||
| 743 | /* advance swhead */ | ||
| 744 | wil_vring_advance_head(vring, nr_frags + 1); | ||
| 745 | wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); | ||
| 746 | iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); | ||
| 747 | /* hold reference to skb | ||
| 748 | * to prevent skb release before accounting | ||
| 749 | * in case of immediate "tx done" | ||
| 750 | */ | ||
| 751 | vring->ctx[i] = skb_get(skb); | ||
| 752 | |||
| 753 | return 0; | ||
| 754 | dma_error: | ||
| 755 | /* unmap what we have mapped */ | ||
| 756 | /* Note: increment @f to operate with positive index */ | ||
| 757 | for (f++; f > 0; f--) { | ||
| 758 | i = (swhead + f) % vring->size; | ||
| 759 | d = &(vring->va[i].tx); | ||
| 760 | d->dma.status = TX_DMA_STATUS_DU; | ||
| 761 | pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); | ||
| 762 | if (vring->ctx[i]) | ||
| 763 | dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
| 764 | else | ||
| 765 | dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
| 766 | } | ||
| 767 | |||
| 768 | return -EINVAL; | ||
| 769 | } | ||
| 770 | |||
| 771 | |||
| 772 | netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) | ||
| 773 | { | ||
| 774 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
| 775 | struct vring *vring; | ||
| 776 | int rc; | ||
| 777 | |||
| 778 | wil_dbg_TXRX(wil, "%s()\n", __func__); | ||
| 779 | if (!test_bit(wil_status_fwready, &wil->status)) { | ||
| 780 | wil_err(wil, "FW not ready\n"); | ||
| 781 | goto drop; | ||
| 782 | } | ||
| 783 | if (!test_bit(wil_status_fwconnected, &wil->status)) { | ||
| 784 | wil_err(wil, "FW not connected\n"); | ||
| 785 | goto drop; | ||
| 786 | } | ||
| 787 | if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { | ||
| 788 | wil_err(wil, "Xmit in monitor mode not supported\n"); | ||
| 789 | goto drop; | ||
| 790 | } | ||
| 791 | if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { | ||
| 792 | rc = wmi_tx_eapol(wil, skb); | ||
| 793 | } else { | ||
| 794 | /* find vring */ | ||
| 795 | vring = wil_find_tx_vring(wil, skb); | ||
| 796 | if (!vring) { | ||
| 797 | wil_err(wil, "No Tx VRING available\n"); | ||
| 798 | goto drop; | ||
| 799 | } | ||
| 800 | /* set up vring entry */ | ||
| 801 | rc = wil_tx_vring(wil, vring, skb); | ||
| 802 | } | ||
| 803 | switch (rc) { | ||
| 804 | case 0: | ||
| 805 | ndev->stats.tx_packets++; | ||
| 806 | ndev->stats.tx_bytes += skb->len; | ||
| 807 | dev_kfree_skb_any(skb); | ||
| 808 | return NETDEV_TX_OK; | ||
| 809 | case -ENOMEM: | ||
| 810 | return NETDEV_TX_BUSY; | ||
| 811 | default: | ||
| 812 | ; /* goto drop; */ | ||
| 813 | break; | ||
| 814 | } | ||
| 815 | drop: | ||
| 816 | netif_tx_stop_all_queues(ndev); | ||
| 817 | ndev->stats.tx_dropped++; | ||
| 818 | dev_kfree_skb_any(skb); | ||
| 819 | |||
| 820 | return NET_XMIT_DROP; | ||
| 821 | } | ||
| 822 | |||
| 823 | /** | ||
| 824 | * Clean up transmitted skb's from the Tx VRING | ||
| 825 | * | ||
| 826 | * Safe to call from IRQ | ||
| 827 | */ | ||
| 828 | void wil_tx_complete(struct wil6210_priv *wil, int ringid) | ||
| 829 | { | ||
| 830 | struct device *dev = wil_to_dev(wil); | ||
| 831 | struct vring *vring = &wil->vring_tx[ringid]; | ||
| 832 | |||
| 833 | if (!vring->va) { | ||
| 834 | wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); | ||
| 835 | return; | ||
| 836 | } | ||
| 837 | |||
| 838 | wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); | ||
| 839 | |||
| 840 | while (!wil_vring_is_empty(vring)) { | ||
| 841 | volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; | ||
| 842 | dma_addr_t pa; | ||
| 843 | struct sk_buff *skb; | ||
| 844 | if (!(d->dma.status & TX_DMA_STATUS_DU)) | ||
| 845 | break; | ||
| 846 | |||
| 847 | wil_dbg_TXRX(wil, | ||
| 848 | "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", | ||
| 849 | vring->swtail, d->dma.length, d->dma.status, | ||
| 850 | d->dma.error); | ||
| 851 | wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, | ||
| 852 | (const void *)d, sizeof(*d), false); | ||
| 853 | |||
| 854 | pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); | ||
| 855 | skb = vring->ctx[vring->swtail]; | ||
| 856 | if (skb) { | ||
| 857 | dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
| 858 | dev_kfree_skb_any(skb); | ||
| 859 | vring->ctx[vring->swtail] = NULL; | ||
| 860 | } else { | ||
| 861 | dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
| 862 | } | ||
| 863 | d->dma.addr_low = 0; | ||
| 864 | d->dma.addr_high = 0; | ||
| 865 | d->dma.length = 0; | ||
| 866 | d->dma.status = TX_DMA_STATUS_DU; | ||
| 867 | vring->swtail = wil_vring_next_tail(vring); | ||
| 868 | } | ||
| 869 | if (wil_vring_avail_tx(vring) > vring->size/4) | ||
| 870 | netif_tx_wake_all_queues(wil_to_ndev(wil)); | ||
| 871 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h new file mode 100644 index 000000000000..45a61f597c5c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.h | |||
| @@ -0,0 +1,362 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef WIL6210_TXRX_H | ||
| 18 | #define WIL6210_TXRX_H | ||
| 19 | |||
| 20 | #define BUF_SW_OWNED (1) | ||
| 21 | #define BUF_HW_OWNED (0) | ||
| 22 | |||
| 23 | /* size of max. Rx packet */ | ||
| 24 | #define RX_BUF_LEN (2048) | ||
| 25 | #define TX_BUF_LEN (2048) | ||
| 26 | /* how many bytes to reserve for rtap header? */ | ||
| 27 | #define WIL6210_RTAP_SIZE (128) | ||
| 28 | |||
| 29 | /* Tx/Rx path */ | ||
| 30 | /* | ||
| 31 | * Tx descriptor - MAC part | ||
| 32 | * [dword 0] | ||
| 33 | * bit 0.. 9 : lifetime_expiry_value:10 | ||
| 34 | * bit 10 : interrup_en:1 | ||
| 35 | * bit 11 : status_en:1 | ||
| 36 | * bit 12..13 : txss_override:2 | ||
| 37 | * bit 14 : timestamp_insertion:1 | ||
| 38 | * bit 15 : duration_preserve:1 | ||
| 39 | * bit 16..21 : reserved0:6 | ||
| 40 | * bit 22..26 : mcs_index:5 | ||
| 41 | * bit 27 : mcs_en:1 | ||
| 42 | * bit 28..29 : reserved1:2 | ||
| 43 | * bit 30 : reserved2:1 | ||
| 44 | * bit 31 : sn_preserved:1 | ||
| 45 | * [dword 1] | ||
| 46 | * bit 0.. 3 : pkt_mode:4 | ||
| 47 | * bit 4 : pkt_mode_en:1 | ||
| 48 | * bit 5.. 7 : reserved0:3 | ||
| 49 | * bit 8..13 : reserved1:6 | ||
| 50 | * bit 14 : reserved2:1 | ||
| 51 | * bit 15 : ack_policy_en:1 | ||
| 52 | * bit 16..19 : dst_index:4 | ||
| 53 | * bit 20 : dst_index_en:1 | ||
| 54 | * bit 21..22 : ack_policy:2 | ||
| 55 | * bit 23 : lifetime_en:1 | ||
| 56 | * bit 24..30 : max_retry:7 | ||
| 57 | * bit 31 : max_retry_en:1 | ||
| 58 | * [dword 2] | ||
| 59 | * bit 0.. 7 : num_of_descriptors:8 | ||
| 60 | * bit 8..17 : reserved:10 | ||
| 61 | * bit 18..19 : l2_translation_type:2 | ||
| 62 | * bit 20 : snap_hdr_insertion_en:1 | ||
| 63 | * bit 21 : vlan_removal_en:1 | ||
| 64 | * bit 22..31 : reserved0:10 | ||
| 65 | * [dword 3] | ||
| 66 | * bit 0.. 31: ucode_cmd:32 | ||
| 67 | */ | ||
| 68 | struct vring_tx_mac { | ||
| 69 | u32 d[3]; | ||
| 70 | u32 ucode_cmd; | ||
| 71 | } __packed; | ||
| 72 | |||
| 73 | /* TX MAC Dword 0 */ | ||
| 74 | #define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0 | ||
| 75 | #define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10 | ||
| 76 | #define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF | ||
| 77 | |||
| 78 | #define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10 | ||
| 79 | #define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1 | ||
| 80 | #define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400 | ||
| 81 | |||
| 82 | #define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11 | ||
| 83 | #define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1 | ||
| 84 | #define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800 | ||
| 85 | |||
| 86 | #define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12 | ||
| 87 | #define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2 | ||
| 88 | #define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000 | ||
| 89 | |||
| 90 | #define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14 | ||
| 91 | #define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1 | ||
| 92 | #define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000 | ||
| 93 | |||
| 94 | #define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15 | ||
| 95 | #define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1 | ||
| 96 | #define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000 | ||
| 97 | |||
| 98 | #define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22 | ||
| 99 | #define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5 | ||
| 100 | #define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000 | ||
| 101 | |||
| 102 | #define MAC_CFG_DESC_TX_0_MCS_EN_POS 27 | ||
| 103 | #define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1 | ||
| 104 | #define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000 | ||
| 105 | |||
| 106 | #define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31 | ||
| 107 | #define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1 | ||
| 108 | #define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000 | ||
| 109 | |||
| 110 | /* TX MAC Dword 1 */ | ||
| 111 | #define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0 | ||
| 112 | #define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4 | ||
| 113 | #define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF | ||
| 114 | |||
| 115 | #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4 | ||
| 116 | #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 | ||
| 117 | #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 | ||
| 118 | |||
| 119 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 | ||
| 120 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 | ||
| 121 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 | ||
| 122 | |||
| 123 | #define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16 | ||
| 124 | #define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4 | ||
| 125 | #define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000 | ||
| 126 | |||
| 127 | #define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20 | ||
| 128 | #define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1 | ||
| 129 | #define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000 | ||
| 130 | |||
| 131 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21 | ||
| 132 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2 | ||
| 133 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000 | ||
| 134 | |||
| 135 | #define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23 | ||
| 136 | #define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1 | ||
| 137 | #define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000 | ||
| 138 | |||
| 139 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24 | ||
| 140 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7 | ||
| 141 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000 | ||
| 142 | |||
| 143 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31 | ||
| 144 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1 | ||
| 145 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000 | ||
| 146 | |||
| 147 | /* TX MAC Dword 2 */ | ||
| 148 | #define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0 | ||
| 149 | #define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8 | ||
| 150 | #define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF | ||
| 151 | |||
| 152 | #define MAC_CFG_DESC_TX_2_RESERVED_POS 8 | ||
| 153 | #define MAC_CFG_DESC_TX_2_RESERVED_LEN 10 | ||
| 154 | #define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00 | ||
| 155 | |||
| 156 | #define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18 | ||
| 157 | #define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2 | ||
| 158 | #define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000 | ||
| 159 | |||
| 160 | #define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20 | ||
| 161 | #define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1 | ||
| 162 | #define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 | ||
| 163 | |||
| 164 | #define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21 | ||
| 165 | #define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1 | ||
| 166 | #define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000 | ||
| 167 | |||
| 168 | /* TX MAC Dword 3 */ | ||
| 169 | #define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0 | ||
| 170 | #define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32 | ||
| 171 | #define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF | ||
| 172 | |||
| 173 | /* TX DMA Dword 0 */ | ||
| 174 | #define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0 | ||
| 175 | #define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8 | ||
| 176 | #define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF | ||
| 177 | |||
| 178 | #define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8 | ||
| 179 | #define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 | ||
| 180 | #define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 | ||
| 181 | |||
| 182 | #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 | ||
| 183 | #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 | ||
| 184 | #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 | ||
| 185 | |||
| 186 | #define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11 | ||
| 187 | #define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2 | ||
| 188 | #define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800 | ||
| 189 | |||
| 190 | #define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13 | ||
| 191 | #define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1 | ||
| 192 | #define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000 | ||
| 193 | |||
| 194 | #define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14 | ||
| 195 | #define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1 | ||
| 196 | #define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000 | ||
| 197 | |||
| 198 | #define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15 | ||
| 199 | #define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1 | ||
| 200 | #define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 | ||
| 201 | |||
| 202 | #define DMA_CFG_DESC_TX_0_QID_POS 16 | ||
| 203 | #define DMA_CFG_DESC_TX_0_QID_LEN 5 | ||
| 204 | #define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000 | ||
| 205 | |||
| 206 | #define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21 | ||
| 207 | #define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1 | ||
| 208 | #define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 | ||
| 209 | |||
| 210 | #define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 | ||
| 211 | #define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 | ||
| 212 | #define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 | ||
| 213 | |||
| 214 | |||
| 215 | #define TX_DMA_STATUS_DU BIT(0) | ||
| 216 | |||
| 217 | struct vring_tx_dma { | ||
| 218 | u32 d0; | ||
| 219 | u32 addr_low; | ||
| 220 | u16 addr_high; | ||
| 221 | u8 ip_length; | ||
| 222 | u8 b11; /* 0..6: mac_length; 7:ip_version */ | ||
| 223 | u8 error; /* 0..2: err; 3..7: reserved; */ | ||
| 224 | u8 status; /* 0: used; 1..7; reserved */ | ||
| 225 | u16 length; | ||
| 226 | } __packed; | ||
| 227 | |||
| 228 | /* | ||
| 229 | * Rx descriptor - MAC part | ||
| 230 | * [dword 0] | ||
| 231 | * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field | ||
| 232 | * bit 4.. 6 : connection_id:3 :The Source index that was found during | ||
| 233 | * Parsing the TA. This field is used to define the source of the packet | ||
| 234 | * bit 7 : reserved:1 | ||
| 235 | * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) | ||
| 236 | * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type | ||
| 237 | * (management, data, control and extension) | ||
| 238 | * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype | ||
| 239 | * bit 16..27 : seq_number:12 The received Sequence number field | ||
| 240 | * bit 28..31 : extended:4 extended subtype | ||
| 241 | * [dword 1] | ||
| 242 | * bit 0.. 3 : reserved | ||
| 243 | * bit 4.. 5 : key_id:2 | ||
| 244 | * bit 6 : decrypt_bypass:1 | ||
| 245 | * bit 7 : security:1 | ||
| 246 | * bit 8.. 9 : ds_bits:2 | ||
| 247 | * bit 10 : a_msdu_present:1 from qos header | ||
| 248 | * bit 11 : a_msdu_type:1 from qos header | ||
| 249 | * bit 12 : a_mpdu:1 part of AMPDU aggregation | ||
| 250 | * bit 13 : broadcast:1 | ||
| 251 | * bit 14 : mutlicast:1 | ||
| 252 | * bit 15 : reserved:1 | ||
| 253 | * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet | ||
| 254 | * is received from | ||
| 255 | * bit 21..24 : mcs:4 | ||
| 256 | * bit 25..28 : mic_icr:4 | ||
| 257 | * bit 29..31 : reserved:3 | ||
| 258 | * [dword 2] | ||
| 259 | * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received | ||
| 260 | * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version | ||
| 261 | * bit 4 : fc_order:1 The FC Control (b15) -Order | ||
| 262 | * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field | ||
| 263 | * bit 8 : esop:1 The QoS (b4) ESOP field | ||
| 264 | * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field | ||
| 265 | * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field | ||
| 266 | * bit 15 : qos_ac_constraint:1 | ||
| 267 | * bit 16..31 : pn_15_0:16 low 2 bytes of PN | ||
| 268 | * [dword 3] | ||
| 269 | * bit 0..31 : pn_47_16:32 high 4 bytes of PN | ||
| 270 | */ | ||
| 271 | struct vring_rx_mac { | ||
| 272 | u32 d0; | ||
| 273 | u32 d1; | ||
| 274 | u16 w4; | ||
| 275 | u16 pn_15_0; | ||
| 276 | u32 pn_47_16; | ||
| 277 | } __packed; | ||
| 278 | |||
| 279 | /* | ||
| 280 | * Rx descriptor - DMA part | ||
| 281 | * [dword 0] | ||
| 282 | * bit 0.. 7 : l4_length:8 layer 4 length | ||
| 283 | * bit 8.. 9 : reserved:2 | ||
| 284 | * bit 10 : cmd_dma_it:1 | ||
| 285 | * bit 11..15 : reserved:5 | ||
| 286 | * bit 16..29 : phy_info_length:14 | ||
| 287 | * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field | ||
| 288 | * [dword 1] | ||
| 289 | * bit 0..31 : addr_low:32 The payload buffer low address | ||
| 290 | * [dword 2] | ||
| 291 | * bit 0..15 : addr_high:16 The payload buffer high address | ||
| 292 | * bit 16..23 : ip_length:8 | ||
| 293 | * bit 24..30 : mac_length:7 | ||
| 294 | * bit 31 : ip_version:1 | ||
| 295 | * [dword 3] | ||
| 296 | * [byte 12] error | ||
| 297 | * [byte 13] status | ||
| 298 | * bit 0 : du:1 | ||
| 299 | * bit 1 : eop:1 | ||
| 300 | * bit 2 : error:1 | ||
| 301 | * bit 3 : mi:1 | ||
| 302 | * bit 4 : l3_identified:1 | ||
| 303 | * bit 5 : l4_identified:1 | ||
| 304 | * bit 6 : phy_info_included:1 | ||
| 305 | * bit 7 : reserved:1 | ||
| 306 | * [word 7] length | ||
| 307 | * | ||
| 308 | */ | ||
| 309 | |||
| 310 | #define RX_DMA_D0_CMD_DMA_IT BIT(10) | ||
| 311 | |||
| 312 | #define RX_DMA_STATUS_DU BIT(0) | ||
| 313 | #define RX_DMA_STATUS_ERROR BIT(2) | ||
| 314 | #define RX_DMA_STATUS_PHY_INFO BIT(6) | ||
| 315 | |||
| 316 | struct vring_rx_dma { | ||
| 317 | u32 d0; | ||
| 318 | u32 addr_low; | ||
| 319 | u16 addr_high; | ||
| 320 | u8 ip_length; | ||
| 321 | u8 b11; | ||
| 322 | u8 error; | ||
| 323 | u8 status; | ||
| 324 | u16 length; | ||
| 325 | } __packed; | ||
| 326 | |||
| 327 | struct vring_tx_desc { | ||
| 328 | struct vring_tx_mac mac; | ||
| 329 | struct vring_tx_dma dma; | ||
| 330 | } __packed; | ||
| 331 | |||
| 332 | struct vring_rx_desc { | ||
| 333 | struct vring_rx_mac mac; | ||
| 334 | struct vring_rx_dma dma; | ||
| 335 | } __packed; | ||
| 336 | |||
| 337 | union vring_desc { | ||
| 338 | struct vring_tx_desc tx; | ||
| 339 | struct vring_rx_desc rx; | ||
| 340 | } __packed; | ||
| 341 | |||
| 342 | static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d) | ||
| 343 | { | ||
| 344 | return WIL_GET_BITS(d->dma.d0, 16, 29); | ||
| 345 | } | ||
| 346 | |||
| 347 | static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d) | ||
| 348 | { | ||
| 349 | return WIL_GET_BITS(d->mac.d1, 21, 24); | ||
| 350 | } | ||
| 351 | |||
| 352 | static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d) | ||
| 353 | { | ||
| 354 | return WIL_GET_BITS(d->mac.d1, 8, 9); | ||
| 355 | } | ||
| 356 | |||
| 357 | static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d) | ||
| 358 | { | ||
| 359 | return WIL_GET_BITS(d->mac.d0, 10, 11); | ||
| 360 | } | ||
| 361 | |||
| 362 | #endif /* WIL6210_TXRX_H */ | ||
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h new file mode 100644 index 000000000000..9bcfffa4006c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210.h | |||
| @@ -0,0 +1,363 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef __WIL6210_H__ | ||
| 18 | #define __WIL6210_H__ | ||
| 19 | |||
| 20 | #include <linux/netdevice.h> | ||
| 21 | #include <linux/wireless.h> | ||
| 22 | #include <net/cfg80211.h> | ||
| 23 | |||
| 24 | #include "dbg_hexdump.h" | ||
| 25 | |||
| 26 | #define WIL_NAME "wil6210" | ||
| 27 | |||
| 28 | /** | ||
| 29 | * extract bits [@b0:@b1] (inclusive) from the value @x | ||
| 30 | * it should be @b0 <= @b1, or result is incorrect | ||
| 31 | */ | ||
| 32 | static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) | ||
| 33 | { | ||
| 34 | return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); | ||
| 35 | } | ||
| 36 | |||
| 37 | #define WIL6210_MEM_SIZE (2*1024*1024UL) | ||
| 38 | |||
| 39 | #define WIL6210_TX_QUEUES (4) | ||
| 40 | |||
| 41 | #define WIL6210_RX_RING_SIZE (128) | ||
| 42 | #define WIL6210_TX_RING_SIZE (128) | ||
| 43 | #define WIL6210_MAX_TX_RINGS (24) | ||
| 44 | |||
| 45 | /* Hardware definitions begin */ | ||
| 46 | |||
| 47 | /* | ||
| 48 | * Mapping | ||
| 49 | * RGF File | Host addr | FW addr | ||
| 50 | * | | | ||
| 51 | * user_rgf | 0x000000 | 0x880000 | ||
| 52 | * dma_rgf | 0x001000 | 0x881000 | ||
| 53 | * pcie_rgf | 0x002000 | 0x882000 | ||
| 54 | * | | | ||
| 55 | */ | ||
| 56 | |||
| 57 | /* Where various structures placed in host address space */ | ||
| 58 | #define WIL6210_FW_HOST_OFF (0x880000UL) | ||
| 59 | |||
| 60 | #define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF) | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Interrupt control registers block | ||
| 64 | * | ||
| 65 | * each interrupt controlled by the same bit in all registers | ||
| 66 | */ | ||
| 67 | struct RGF_ICR { | ||
| 68 | u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ | ||
| 69 | u32 ICR; /* Cause, W1C/COR depending on ICC */ | ||
| 70 | u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ | ||
| 71 | u32 ICS; /* Cause Set, WO */ | ||
| 72 | u32 IMV; /* Mask, RW+S/C */ | ||
| 73 | u32 IMS; /* Mask Set, write 1 to set */ | ||
| 74 | u32 IMC; /* Mask Clear, write 1 to clear */ | ||
| 75 | } __packed; | ||
| 76 | |||
| 77 | /* registers - FW addresses */ | ||
| 78 | #define RGF_USER_USER_SCRATCH_PAD (0x8802bc) | ||
| 79 | #define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ | ||
| 80 | #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) | ||
| 81 | #define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) | ||
| 82 | #define RGF_USER_MAC_CPU_0 (0x8801fc) | ||
| 83 | #define RGF_USER_USER_CPU_0 (0x8801e0) | ||
| 84 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) | ||
| 85 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) | ||
| 86 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) | ||
| 87 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) | ||
| 88 | |||
| 89 | #define RGF_DMA_PSEUDO_CAUSE (0x881c68) | ||
| 90 | #define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) | ||
| 91 | #define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) | ||
| 92 | #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) | ||
| 93 | #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) | ||
| 94 | #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) | ||
| 95 | |||
| 96 | #define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ | ||
| 97 | #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) | ||
| 98 | #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */ | ||
| 99 | #define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */ | ||
| 100 | #define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0) | ||
| 101 | #define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ | ||
| 102 | #define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0) | ||
| 103 | #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) | ||
| 104 | #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) | ||
| 105 | #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) | ||
| 106 | |||
| 107 | /* Interrupt moderation control */ | ||
| 108 | #define RGF_DMA_ITR_CNT_TRSH (0x881c5c) | ||
| 109 | #define RGF_DMA_ITR_CNT_DATA (0x881c60) | ||
| 110 | #define RGF_DMA_ITR_CNT_CRL (0x881C64) | ||
| 111 | #define BIT_DMA_ITR_CNT_CRL_EN BIT(0) | ||
| 112 | #define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1) | ||
| 113 | #define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2) | ||
| 114 | #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) | ||
| 115 | #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) | ||
| 116 | |||
| 117 | /* popular locations */ | ||
| 118 | #define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) | ||
| 119 | #define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ | ||
| 120 | offsetof(struct RGF_ICR, ICS)) | ||
| 121 | #define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 | ||
| 122 | |||
| 123 | /* ISR register bits */ | ||
| 124 | #define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 | ||
| 125 | #define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 | ||
| 126 | |||
| 127 | /* Hardware definitions end */ | ||
| 128 | |||
| 129 | struct wil6210_mbox_ring { | ||
| 130 | u32 base; | ||
| 131 | u16 entry_size; /* max. size of mbox entry, incl. all headers */ | ||
| 132 | u16 size; | ||
| 133 | u32 tail; | ||
| 134 | u32 head; | ||
| 135 | } __packed; | ||
| 136 | |||
| 137 | struct wil6210_mbox_ring_desc { | ||
| 138 | __le32 sync; | ||
| 139 | __le32 addr; | ||
| 140 | } __packed; | ||
| 141 | |||
| 142 | /* at HOST_OFF_WIL6210_MBOX_CTL */ | ||
| 143 | struct wil6210_mbox_ctl { | ||
| 144 | struct wil6210_mbox_ring tx; | ||
| 145 | struct wil6210_mbox_ring rx; | ||
| 146 | } __packed; | ||
| 147 | |||
| 148 | struct wil6210_mbox_hdr { | ||
| 149 | __le16 seq; | ||
| 150 | __le16 len; /* payload, bytes after this header */ | ||
| 151 | __le16 type; | ||
| 152 | u8 flags; | ||
| 153 | u8 reserved; | ||
| 154 | } __packed; | ||
| 155 | |||
| 156 | #define WIL_MBOX_HDR_TYPE_WMI (0) | ||
| 157 | |||
| 158 | /* max. value for wil6210_mbox_hdr.len */ | ||
| 159 | #define MAX_MBOXITEM_SIZE (240) | ||
| 160 | |||
| 161 | struct wil6210_mbox_hdr_wmi { | ||
| 162 | u8 reserved0[2]; | ||
| 163 | __le16 id; | ||
| 164 | __le16 info1; /* bits [0..3] - device_id, rest - unused */ | ||
| 165 | u8 reserved1[2]; | ||
| 166 | } __packed; | ||
| 167 | |||
| 168 | struct pending_wmi_event { | ||
| 169 | struct list_head list; | ||
| 170 | struct { | ||
| 171 | struct wil6210_mbox_hdr hdr; | ||
| 172 | struct wil6210_mbox_hdr_wmi wmi; | ||
| 173 | u8 data[0]; | ||
| 174 | } __packed event; | ||
| 175 | }; | ||
| 176 | |||
| 177 | union vring_desc; | ||
| 178 | |||
| 179 | struct vring { | ||
| 180 | dma_addr_t pa; | ||
| 181 | volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */ | ||
| 182 | u16 size; /* number of vring_desc elements */ | ||
| 183 | u32 swtail; | ||
| 184 | u32 swhead; | ||
| 185 | u32 hwtail; /* write here to inform hw */ | ||
| 186 | void **ctx; /* void *ctx[size] - software context */ | ||
| 187 | }; | ||
| 188 | |||
| 189 | enum { /* for wil6210_priv.status */ | ||
| 190 | wil_status_fwready = 0, | ||
| 191 | wil_status_fwconnected, | ||
| 192 | wil_status_dontscan, | ||
| 193 | wil_status_irqen, /* FIXME: interrupts enabled - for debug */ | ||
| 194 | }; | ||
| 195 | |||
| 196 | struct pci_dev; | ||
| 197 | |||
| 198 | struct wil6210_stats { | ||
| 199 | u64 tsf; | ||
| 200 | u32 snr; | ||
| 201 | u16 last_mcs_rx; | ||
| 202 | u16 bf_mcs; /* last BF, used for Tx */ | ||
| 203 | u16 my_rx_sector; | ||
| 204 | u16 my_tx_sector; | ||
| 205 | u16 peer_rx_sector; | ||
| 206 | u16 peer_tx_sector; | ||
| 207 | }; | ||
| 208 | |||
| 209 | struct wil6210_priv { | ||
| 210 | struct pci_dev *pdev; | ||
| 211 | int n_msi; | ||
| 212 | struct wireless_dev *wdev; | ||
| 213 | void __iomem *csr; | ||
| 214 | ulong status; | ||
| 215 | /* profile */ | ||
| 216 | u32 monitor_flags; | ||
| 217 | u32 secure_pcp; /* create secure PCP? */ | ||
| 218 | int sinfo_gen; | ||
| 219 | /* cached ISR registers */ | ||
| 220 | u32 isr_misc; | ||
| 221 | /* mailbox related */ | ||
| 222 | struct mutex wmi_mutex; | ||
| 223 | struct wil6210_mbox_ctl mbox_ctl; | ||
| 224 | struct completion wmi_ready; | ||
| 225 | u16 wmi_seq; | ||
| 226 | u16 reply_id; /**< wait for this WMI event */ | ||
| 227 | void *reply_buf; | ||
| 228 | u16 reply_size; | ||
| 229 | struct workqueue_struct *wmi_wq; /* for deferred calls */ | ||
| 230 | struct work_struct wmi_event_worker; | ||
| 231 | struct workqueue_struct *wmi_wq_conn; /* for connect worker */ | ||
| 232 | struct work_struct wmi_connect_worker; | ||
| 233 | struct work_struct disconnect_worker; | ||
| 234 | struct timer_list connect_timer; | ||
| 235 | int pending_connect_cid; | ||
| 236 | struct list_head pending_wmi_ev; | ||
| 237 | /* | ||
| 238 | * protect pending_wmi_ev | ||
| 239 | * - fill in IRQ from wil6210_irq_misc, | ||
| 240 | * - consumed in thread by wmi_event_worker | ||
| 241 | */ | ||
| 242 | spinlock_t wmi_ev_lock; | ||
| 243 | /* DMA related */ | ||
| 244 | struct vring vring_rx; | ||
| 245 | struct vring vring_tx[WIL6210_MAX_TX_RINGS]; | ||
| 246 | u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; | ||
| 247 | /* scan */ | ||
| 248 | struct cfg80211_scan_request *scan_request; | ||
| 249 | |||
| 250 | struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ | ||
| 251 | /* statistics */ | ||
| 252 | struct wil6210_stats stats; | ||
| 253 | /* debugfs */ | ||
| 254 | struct dentry *debug; | ||
| 255 | struct debugfs_blob_wrapper fw_code_blob; | ||
| 256 | struct debugfs_blob_wrapper fw_data_blob; | ||
| 257 | struct debugfs_blob_wrapper fw_peri_blob; | ||
| 258 | struct debugfs_blob_wrapper uc_code_blob; | ||
| 259 | struct debugfs_blob_wrapper uc_data_blob; | ||
| 260 | struct debugfs_blob_wrapper rgf_blob; | ||
| 261 | }; | ||
| 262 | |||
| 263 | #define wil_to_wiphy(i) (i->wdev->wiphy) | ||
| 264 | #define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) | ||
| 265 | #define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) | ||
| 266 | #define wil_to_wdev(i) (i->wdev) | ||
| 267 | #define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) | ||
| 268 | #define wil_to_ndev(i) (wil_to_wdev(i)->netdev) | ||
| 269 | #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) | ||
| 270 | |||
| 271 | #define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) | ||
| 272 | #define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) | ||
| 273 | #define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) | ||
| 274 | |||
| 275 | #define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) | ||
| 276 | #define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) | ||
| 277 | #define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) | ||
| 278 | |||
| 279 | #define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ | ||
| 280 | groupsize, buf, len, ascii) \ | ||
| 281 | wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ | ||
| 282 | prefix_type, rowsize, \ | ||
| 283 | groupsize, buf, len, ascii) | ||
| 284 | |||
| 285 | #define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \ | ||
| 286 | groupsize, buf, len, ascii) \ | ||
| 287 | wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ | ||
| 288 | prefix_type, rowsize, \ | ||
| 289 | groupsize, buf, len, ascii) | ||
| 290 | |||
| 291 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | ||
| 292 | size_t count); | ||
| 293 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | ||
| 294 | size_t count); | ||
| 295 | |||
| 296 | void *wil_if_alloc(struct device *dev, void __iomem *csr); | ||
| 297 | void wil_if_free(struct wil6210_priv *wil); | ||
| 298 | int wil_if_add(struct wil6210_priv *wil); | ||
| 299 | void wil_if_remove(struct wil6210_priv *wil); | ||
| 300 | int wil_priv_init(struct wil6210_priv *wil); | ||
| 301 | void wil_priv_deinit(struct wil6210_priv *wil); | ||
| 302 | int wil_reset(struct wil6210_priv *wil); | ||
| 303 | void wil_link_on(struct wil6210_priv *wil); | ||
| 304 | void wil_link_off(struct wil6210_priv *wil); | ||
| 305 | int wil_up(struct wil6210_priv *wil); | ||
| 306 | int wil_down(struct wil6210_priv *wil); | ||
| 307 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); | ||
| 308 | |||
| 309 | void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); | ||
| 310 | void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); | ||
| 311 | int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, | ||
| 312 | struct wil6210_mbox_hdr *hdr); | ||
| 313 | int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); | ||
| 314 | void wmi_recv_cmd(struct wil6210_priv *wil); | ||
| 315 | int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, | ||
| 316 | u16 reply_id, void *reply, u8 reply_size, int to_msec); | ||
| 317 | void wmi_connect_worker(struct work_struct *work); | ||
| 318 | void wmi_event_worker(struct work_struct *work); | ||
| 319 | void wmi_event_flush(struct wil6210_priv *wil); | ||
| 320 | int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); | ||
| 321 | int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); | ||
| 322 | int wmi_set_channel(struct wil6210_priv *wil, int channel); | ||
| 323 | int wmi_get_channel(struct wil6210_priv *wil, int *channel); | ||
| 324 | int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); | ||
| 325 | int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
| 326 | const void *mac_addr); | ||
| 327 | int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
| 328 | const void *mac_addr, int key_len, const void *key); | ||
| 329 | int wmi_echo(struct wil6210_priv *wil); | ||
| 330 | int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); | ||
| 331 | |||
| 332 | int wil6210_init_irq(struct wil6210_priv *wil, int irq); | ||
| 333 | void wil6210_fini_irq(struct wil6210_priv *wil, int irq); | ||
| 334 | void wil6210_disable_irq(struct wil6210_priv *wil); | ||
| 335 | void wil6210_enable_irq(struct wil6210_priv *wil); | ||
| 336 | |||
| 337 | int wil6210_debugfs_init(struct wil6210_priv *wil); | ||
| 338 | void wil6210_debugfs_remove(struct wil6210_priv *wil); | ||
| 339 | |||
| 340 | struct wireless_dev *wil_cfg80211_init(struct device *dev); | ||
| 341 | void wil_wdev_free(struct wil6210_priv *wil); | ||
| 342 | |||
| 343 | int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); | ||
| 344 | int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype); | ||
| 345 | void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); | ||
| 346 | |||
| 347 | int wil_rx_init(struct wil6210_priv *wil); | ||
| 348 | void wil_rx_fini(struct wil6210_priv *wil); | ||
| 349 | |||
| 350 | /* TX API */ | ||
| 351 | int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, | ||
| 352 | int cid, int tid); | ||
| 353 | void wil_vring_fini_tx(struct wil6210_priv *wil, int id); | ||
| 354 | |||
| 355 | netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); | ||
| 356 | void wil_tx_complete(struct wil6210_priv *wil, int ringid); | ||
| 357 | |||
| 358 | /* RX API */ | ||
| 359 | void wil_rx_handle(struct wil6210_priv *wil); | ||
| 360 | |||
| 361 | int wil_iftype_nl2wmi(enum nl80211_iftype type); | ||
| 362 | |||
| 363 | #endif /* __WIL6210_H__ */ | ||
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c new file mode 100644 index 000000000000..12915f6e7617 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.c | |||
| @@ -0,0 +1,975 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * | ||
| 4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 5 | * purpose with or without fee is hereby granted, provided that the above | ||
| 6 | * copyright notice and this permission notice appear in all copies. | ||
| 7 | * | ||
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/pci.h> | ||
| 18 | #include <linux/io.h> | ||
| 19 | #include <linux/list.h> | ||
| 20 | #include <linux/etherdevice.h> | ||
| 21 | |||
| 22 | #include "wil6210.h" | ||
| 23 | #include "wmi.h" | ||
| 24 | |||
| 25 | /** | ||
| 26 | * WMI event receiving - theory of operations | ||
| 27 | * | ||
| 28 | * When firmware about to report WMI event, it fills memory area | ||
| 29 | * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for | ||
| 30 | * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. | ||
| 31 | * | ||
| 32 | * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the | ||
| 33 | * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up | ||
| 34 | * and handles events within the @wmi_event_worker. Every event get detached | ||
| 35 | * from list, processed and deleted. | ||
| 36 | * | ||
| 37 | * Purpose for this mechanism is to release IRQ thread; otherwise, | ||
| 38 | * if WMI event handling involves another WMI command flow, this 2-nd flow | ||
| 39 | * won't be completed because of blocked IRQ thread. | ||
| 40 | */ | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Addressing - theory of operations | ||
| 44 | * | ||
| 45 | * There are several buses present on the WIL6210 card. | ||
| 46 | * Same memory areas are visible at different address on | ||
| 47 | * the different busses. There are 3 main bus masters: | ||
| 48 | * - MAC CPU (ucode) | ||
| 49 | * - User CPU (firmware) | ||
| 50 | * - AHB (host) | ||
| 51 | * | ||
| 52 | * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing | ||
| 53 | * AHB addresses starting from 0x880000 | ||
| 54 | * | ||
| 55 | * Internally, firmware uses addresses that allows faster access but | ||
| 56 | * are invisible from the host. To read from these addresses, alternative | ||
| 57 | * AHB address must be used. | ||
| 58 | * | ||
| 59 | * Memory mapping | ||
| 60 | * Linker address PCI/Host address | ||
| 61 | * 0x880000 .. 0xa80000 2Mb BAR0 | ||
| 62 | * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM | ||
| 63 | * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH | ||
| 64 | */ | ||
| 65 | |||
| 66 | /** | ||
| 67 | * @fw_mapping provides memory remapping table | ||
| 68 | */ | ||
| 69 | static const struct { | ||
| 70 | u32 from; /* linker address - from, inclusive */ | ||
| 71 | u32 to; /* linker address - to, exclusive */ | ||
| 72 | u32 host; /* PCI/Host address - BAR0 + 0x880000 */ | ||
| 73 | } fw_mapping[] = { | ||
| 74 | {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ | ||
| 75 | {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ | ||
| 76 | {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ | ||
| 77 | {0x880000, 0x88a000, 0x880000}, /* various RGF */ | ||
| 78 | {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ | ||
| 79 | /* | ||
| 80 | * 920000..930000 ucode code RAM | ||
| 81 | * 930000..932000 ucode data RAM | ||
| 82 | */ | ||
| 83 | }; | ||
| 84 | |||
| 85 | /** | ||
| 86 | * return AHB address for given firmware/ucode internal (linker) address | ||
| 87 | * @x - internal address | ||
| 88 | * If address have no valid AHB mapping, return 0 | ||
| 89 | */ | ||
| 90 | static u32 wmi_addr_remap(u32 x) | ||
| 91 | { | ||
| 92 | uint i; | ||
| 93 | |||
| 94 | for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { | ||
| 95 | if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) | ||
| 96 | return x + fw_mapping[i].host - fw_mapping[i].from; | ||
| 97 | } | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Check address validity for WMI buffer; remap if needed | ||
| 104 | * @ptr - internal (linker) fw/ucode address | ||
| 105 | * | ||
| 106 | * Valid buffer should be DWORD aligned | ||
| 107 | * | ||
| 108 | * return address for accessing buffer from the host; | ||
| 109 | * if buffer is not valid, return NULL. | ||
| 110 | */ | ||
| 111 | void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) | ||
| 112 | { | ||
| 113 | u32 off; | ||
| 114 | u32 ptr = le32_to_cpu(ptr_); | ||
| 115 | |||
| 116 | if (ptr % 4) | ||
| 117 | return NULL; | ||
| 118 | |||
| 119 | ptr = wmi_addr_remap(ptr); | ||
| 120 | if (ptr < WIL6210_FW_HOST_OFF) | ||
| 121 | return NULL; | ||
| 122 | |||
| 123 | off = HOSTADDR(ptr); | ||
| 124 | if (off > WIL6210_MEM_SIZE - 4) | ||
| 125 | return NULL; | ||
| 126 | |||
| 127 | return wil->csr + off; | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * Check address validity | ||
| 132 | */ | ||
| 133 | void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) | ||
| 134 | { | ||
| 135 | u32 off; | ||
| 136 | |||
| 137 | if (ptr % 4) | ||
| 138 | return NULL; | ||
| 139 | |||
| 140 | if (ptr < WIL6210_FW_HOST_OFF) | ||
| 141 | return NULL; | ||
| 142 | |||
| 143 | off = HOSTADDR(ptr); | ||
| 144 | if (off > WIL6210_MEM_SIZE - 4) | ||
| 145 | return NULL; | ||
| 146 | |||
| 147 | return wil->csr + off; | ||
| 148 | } | ||
| 149 | |||
| 150 | int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, | ||
| 151 | struct wil6210_mbox_hdr *hdr) | ||
| 152 | { | ||
| 153 | void __iomem *src = wmi_buffer(wil, ptr); | ||
| 154 | if (!src) | ||
| 155 | return -EINVAL; | ||
| 156 | |||
| 157 | wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); | ||
| 158 | |||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) | ||
| 163 | { | ||
| 164 | struct { | ||
| 165 | struct wil6210_mbox_hdr hdr; | ||
| 166 | struct wil6210_mbox_hdr_wmi wmi; | ||
| 167 | } __packed cmd = { | ||
| 168 | .hdr = { | ||
| 169 | .type = WIL_MBOX_HDR_TYPE_WMI, | ||
| 170 | .flags = 0, | ||
| 171 | .len = cpu_to_le16(sizeof(cmd.wmi) + len), | ||
| 172 | }, | ||
| 173 | .wmi = { | ||
| 174 | .id = cpu_to_le16(cmdid), | ||
| 175 | .info1 = 0, | ||
| 176 | }, | ||
| 177 | }; | ||
| 178 | struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; | ||
| 179 | struct wil6210_mbox_ring_desc d_head; | ||
| 180 | u32 next_head; | ||
| 181 | void __iomem *dst; | ||
| 182 | void __iomem *head = wmi_addr(wil, r->head); | ||
| 183 | uint retry; | ||
| 184 | |||
| 185 | if (sizeof(cmd) + len > r->entry_size) { | ||
| 186 | wil_err(wil, "WMI size too large: %d bytes, max is %d\n", | ||
| 187 | (int)(sizeof(cmd) + len), r->entry_size); | ||
| 188 | return -ERANGE; | ||
| 189 | |||
| 190 | } | ||
| 191 | |||
| 192 | might_sleep(); | ||
| 193 | |||
| 194 | if (!test_bit(wil_status_fwready, &wil->status)) { | ||
| 195 | wil_err(wil, "FW not ready\n"); | ||
| 196 | return -EAGAIN; | ||
| 197 | } | ||
| 198 | |||
| 199 | if (!head) { | ||
| 200 | wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); | ||
| 201 | return -EINVAL; | ||
| 202 | } | ||
| 203 | /* read Tx head till it is not busy */ | ||
| 204 | for (retry = 5; retry > 0; retry--) { | ||
| 205 | wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); | ||
| 206 | if (d_head.sync == 0) | ||
| 207 | break; | ||
| 208 | msleep(20); | ||
| 209 | } | ||
| 210 | if (d_head.sync != 0) { | ||
| 211 | wil_err(wil, "WMI head busy\n"); | ||
| 212 | return -EBUSY; | ||
| 213 | } | ||
| 214 | /* next head */ | ||
| 215 | next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); | ||
| 216 | wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); | ||
| 217 | /* wait till FW finish with previous command */ | ||
| 218 | for (retry = 5; retry > 0; retry--) { | ||
| 219 | r->tail = ioread32(wil->csr + HOST_MBOX + | ||
| 220 | offsetof(struct wil6210_mbox_ctl, tx.tail)); | ||
| 221 | if (next_head != r->tail) | ||
| 222 | break; | ||
| 223 | msleep(20); | ||
| 224 | } | ||
| 225 | if (next_head == r->tail) { | ||
| 226 | wil_err(wil, "WMI ring full\n"); | ||
| 227 | return -EBUSY; | ||
| 228 | } | ||
| 229 | dst = wmi_buffer(wil, d_head.addr); | ||
| 230 | if (!dst) { | ||
| 231 | wil_err(wil, "invalid WMI buffer: 0x%08x\n", | ||
| 232 | le32_to_cpu(d_head.addr)); | ||
| 233 | return -EINVAL; | ||
| 234 | } | ||
| 235 | cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); | ||
| 236 | /* set command */ | ||
| 237 | wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); | ||
| 238 | wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, | ||
| 239 | sizeof(cmd), true); | ||
| 240 | wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, | ||
| 241 | len, true); | ||
| 242 | wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); | ||
| 243 | wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); | ||
| 244 | /* mark entry as full */ | ||
| 245 | iowrite32(1, wil->csr + HOSTADDR(r->head) + | ||
| 246 | offsetof(struct wil6210_mbox_ring_desc, sync)); | ||
| 247 | /* advance next ptr */ | ||
| 248 | iowrite32(r->head = next_head, wil->csr + HOST_MBOX + | ||
| 249 | offsetof(struct wil6210_mbox_ctl, tx.head)); | ||
| 250 | |||
| 251 | /* interrupt to FW */ | ||
| 252 | iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); | ||
| 253 | |||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) | ||
| 258 | { | ||
| 259 | int rc; | ||
| 260 | |||
| 261 | mutex_lock(&wil->wmi_mutex); | ||
| 262 | rc = __wmi_send(wil, cmdid, buf, len); | ||
| 263 | mutex_unlock(&wil->wmi_mutex); | ||
| 264 | |||
| 265 | return rc; | ||
| 266 | } | ||
| 267 | |||
| 268 | /*=== Event handlers ===*/ | ||
| 269 | static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) | ||
| 270 | { | ||
| 271 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 272 | struct wireless_dev *wdev = wil->wdev; | ||
| 273 | struct wmi_ready_event *evt = d; | ||
| 274 | u32 ver = le32_to_cpu(evt->sw_version); | ||
| 275 | |||
| 276 | wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); | ||
| 277 | |||
| 278 | if (!is_valid_ether_addr(ndev->dev_addr)) { | ||
| 279 | memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); | ||
| 280 | memcpy(ndev->perm_addr, evt->mac, ETH_ALEN); | ||
| 281 | } | ||
| 282 | snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), | ||
| 283 | "%d", ver); | ||
| 284 | } | ||
| 285 | |||
| 286 | static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, | ||
| 287 | int len) | ||
| 288 | { | ||
| 289 | wil_dbg_WMI(wil, "WMI: FW ready\n"); | ||
| 290 | |||
| 291 | set_bit(wil_status_fwready, &wil->status); | ||
| 292 | /* reuse wmi_ready for the firmware ready indication */ | ||
| 293 | complete(&wil->wmi_ready); | ||
| 294 | } | ||
| 295 | |||
| 296 | static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) | ||
| 297 | { | ||
| 298 | struct wmi_rx_mgmt_packet_event *data = d; | ||
| 299 | struct wiphy *wiphy = wil_to_wiphy(wil); | ||
| 300 | struct ieee80211_mgmt *rx_mgmt_frame = | ||
| 301 | (struct ieee80211_mgmt *)data->payload; | ||
| 302 | int ch_no = data->info.channel+1; | ||
| 303 | u32 freq = ieee80211_channel_to_frequency(ch_no, | ||
| 304 | IEEE80211_BAND_60GHZ); | ||
| 305 | struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); | ||
| 306 | /* TODO convert LE to CPU */ | ||
| 307 | s32 signal = 0; /* TODO */ | ||
| 308 | __le16 fc = rx_mgmt_frame->frame_control; | ||
| 309 | u32 d_len = le32_to_cpu(data->info.len); | ||
| 310 | u16 d_status = le16_to_cpu(data->info.status); | ||
| 311 | |||
| 312 | wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", | ||
| 313 | data->info.channel, data->info.mcs, data->info.snr); | ||
| 314 | wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, | ||
| 315 | le16_to_cpu(data->info.stype)); | ||
| 316 | wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", | ||
| 317 | data->info.qid, data->info.mid, data->info.cid); | ||
| 318 | |||
| 319 | if (!channel) { | ||
| 320 | wil_err(wil, "Frame on unsupported channel\n"); | ||
| 321 | return; | ||
| 322 | } | ||
| 323 | |||
| 324 | if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { | ||
| 325 | struct cfg80211_bss *bss; | ||
| 326 | u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); | ||
| 327 | u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); | ||
| 328 | u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); | ||
| 329 | const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; | ||
| 330 | size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, | ||
| 331 | u.beacon.variable); | ||
| 332 | wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); | ||
| 333 | |||
| 334 | bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, | ||
| 335 | tsf, cap, bi, ie_buf, ie_len, | ||
| 336 | signal, GFP_KERNEL); | ||
| 337 | if (bss) { | ||
| 338 | wil_dbg_WMI(wil, "Added BSS %pM\n", | ||
| 339 | rx_mgmt_frame->bssid); | ||
| 340 | cfg80211_put_bss(bss); | ||
| 341 | } else { | ||
| 342 | wil_err(wil, "cfg80211_inform_bss() failed\n"); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, | ||
| 348 | void *d, int len) | ||
| 349 | { | ||
| 350 | if (wil->scan_request) { | ||
| 351 | struct wmi_scan_complete_event *data = d; | ||
| 352 | bool aborted = (data->status != 0); | ||
| 353 | |||
| 354 | wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); | ||
| 355 | cfg80211_scan_done(wil->scan_request, aborted); | ||
| 356 | wil->scan_request = NULL; | ||
| 357 | } else { | ||
| 358 | wil_err(wil, "SCAN_COMPLETE while not scanning\n"); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) | ||
| 363 | { | ||
| 364 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 365 | struct wireless_dev *wdev = wil->wdev; | ||
| 366 | struct wmi_connect_event *evt = d; | ||
| 367 | int ch; /* channel number */ | ||
| 368 | struct station_info sinfo; | ||
| 369 | u8 *assoc_req_ie, *assoc_resp_ie; | ||
| 370 | size_t assoc_req_ielen, assoc_resp_ielen; | ||
| 371 | /* capinfo(u16) + listen_interval(u16) + IEs */ | ||
| 372 | const size_t assoc_req_ie_offset = sizeof(u16) * 2; | ||
| 373 | /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ | ||
| 374 | const size_t assoc_resp_ie_offset = sizeof(u16) * 3; | ||
| 375 | |||
| 376 | if (len < sizeof(*evt)) { | ||
| 377 | wil_err(wil, "Connect event too short : %d bytes\n", len); | ||
| 378 | return; | ||
| 379 | } | ||
| 380 | if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + | ||
| 381 | evt->assoc_resp_len) { | ||
| 382 | wil_err(wil, | ||
| 383 | "Connect event corrupted : %d != %d + %d + %d + %d\n", | ||
| 384 | len, (int)sizeof(*evt), evt->beacon_ie_len, | ||
| 385 | evt->assoc_req_len, evt->assoc_resp_len); | ||
| 386 | return; | ||
| 387 | } | ||
| 388 | ch = evt->channel + 1; | ||
| 389 | wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", | ||
| 390 | evt->bssid, ch, evt->cid); | ||
| 391 | wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, | ||
| 392 | evt->assoc_info, len - sizeof(*evt), true); | ||
| 393 | |||
| 394 | /* figure out IE's */ | ||
| 395 | assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + | ||
| 396 | assoc_req_ie_offset]; | ||
| 397 | assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; | ||
| 398 | if (evt->assoc_req_len <= assoc_req_ie_offset) { | ||
| 399 | assoc_req_ie = NULL; | ||
| 400 | assoc_req_ielen = 0; | ||
| 401 | } | ||
| 402 | |||
| 403 | assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + | ||
| 404 | evt->assoc_req_len + | ||
| 405 | assoc_resp_ie_offset]; | ||
| 406 | assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; | ||
| 407 | if (evt->assoc_resp_len <= assoc_resp_ie_offset) { | ||
| 408 | assoc_resp_ie = NULL; | ||
| 409 | assoc_resp_ielen = 0; | ||
| 410 | } | ||
| 411 | |||
| 412 | if ((wdev->iftype == NL80211_IFTYPE_STATION) || | ||
| 413 | (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { | ||
| 414 | if (wdev->sme_state != CFG80211_SME_CONNECTING) { | ||
| 415 | wil_err(wil, "Not in connecting state\n"); | ||
| 416 | return; | ||
| 417 | } | ||
| 418 | del_timer_sync(&wil->connect_timer); | ||
| 419 | cfg80211_connect_result(ndev, evt->bssid, | ||
| 420 | assoc_req_ie, assoc_req_ielen, | ||
| 421 | assoc_resp_ie, assoc_resp_ielen, | ||
| 422 | WLAN_STATUS_SUCCESS, GFP_KERNEL); | ||
| 423 | |||
| 424 | } else if ((wdev->iftype == NL80211_IFTYPE_AP) || | ||
| 425 | (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { | ||
| 426 | memset(&sinfo, 0, sizeof(sinfo)); | ||
| 427 | |||
| 428 | sinfo.generation = wil->sinfo_gen++; | ||
| 429 | |||
| 430 | if (assoc_req_ie) { | ||
| 431 | sinfo.assoc_req_ies = assoc_req_ie; | ||
| 432 | sinfo.assoc_req_ies_len = assoc_req_ielen; | ||
| 433 | sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; | ||
| 434 | } | ||
| 435 | |||
| 436 | cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); | ||
| 437 | } | ||
| 438 | set_bit(wil_status_fwconnected, &wil->status); | ||
| 439 | |||
| 440 | /* FIXME FW can transmit only ucast frames to peer */ | ||
| 441 | /* FIXME real ring_id instead of hard coded 0 */ | ||
| 442 | memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); | ||
| 443 | |||
| 444 | wil->pending_connect_cid = evt->cid; | ||
| 445 | queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); | ||
| 446 | } | ||
| 447 | |||
| 448 | static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, | ||
| 449 | void *d, int len) | ||
| 450 | { | ||
| 451 | struct wmi_disconnect_event *evt = d; | ||
| 452 | |||
| 453 | wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", | ||
| 454 | evt->bssid, | ||
| 455 | evt->protocol_reason_status, evt->disconnect_reason); | ||
| 456 | |||
| 457 | wil->sinfo_gen++; | ||
| 458 | |||
| 459 | wil6210_disconnect(wil, evt->bssid); | ||
| 460 | clear_bit(wil_status_dontscan, &wil->status); | ||
| 461 | } | ||
| 462 | |||
| 463 | static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) | ||
| 464 | { | ||
| 465 | struct wmi_notify_req_done_event *evt = d; | ||
| 466 | |||
| 467 | if (len < sizeof(*evt)) { | ||
| 468 | wil_err(wil, "Short NOTIFY event\n"); | ||
| 469 | return; | ||
| 470 | } | ||
| 471 | |||
| 472 | wil->stats.tsf = le64_to_cpu(evt->tsf); | ||
| 473 | wil->stats.snr = le32_to_cpu(evt->snr_val); | ||
| 474 | wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); | ||
| 475 | wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); | ||
| 476 | wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); | ||
| 477 | wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); | ||
| 478 | wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); | ||
| 479 | wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" | ||
| 480 | "BF status 0x%08x SNR 0x%08x\n" | ||
| 481 | "Tx Tpt %d goodput %d Rx goodput %d\n" | ||
| 482 | "Sectors(rx:tx) my %d:%d peer %d:%d\n", | ||
| 483 | wil->stats.bf_mcs, wil->stats.tsf, evt->status, | ||
| 484 | wil->stats.snr, le32_to_cpu(evt->tx_tpt), | ||
| 485 | le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), | ||
| 486 | wil->stats.my_rx_sector, wil->stats.my_tx_sector, | ||
| 487 | wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); | ||
| 488 | } | ||
| 489 | |||
| 490 | /* | ||
| 491 | * Firmware reports EAPOL frame using WME event. | ||
| 492 | * Reconstruct Ethernet frame and deliver it via normal Rx | ||
| 493 | */ | ||
| 494 | static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, | ||
| 495 | void *d, int len) | ||
| 496 | { | ||
| 497 | struct net_device *ndev = wil_to_ndev(wil); | ||
| 498 | struct wmi_eapol_rx_event *evt = d; | ||
| 499 | u16 eapol_len = le16_to_cpu(evt->eapol_len); | ||
| 500 | int sz = eapol_len + ETH_HLEN; | ||
| 501 | struct sk_buff *skb; | ||
| 502 | struct ethhdr *eth; | ||
| 503 | |||
| 504 | wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, | ||
| 505 | evt->src_mac); | ||
| 506 | |||
| 507 | if (eapol_len > 196) { /* TODO: revisit size limit */ | ||
| 508 | wil_err(wil, "EAPOL too large\n"); | ||
| 509 | return; | ||
| 510 | } | ||
| 511 | |||
| 512 | skb = alloc_skb(sz, GFP_KERNEL); | ||
| 513 | if (!skb) { | ||
| 514 | wil_err(wil, "Failed to allocate skb\n"); | ||
| 515 | return; | ||
| 516 | } | ||
| 517 | eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); | ||
| 518 | memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); | ||
| 519 | memcpy(eth->h_source, evt->src_mac, ETH_ALEN); | ||
| 520 | eth->h_proto = cpu_to_be16(ETH_P_PAE); | ||
| 521 | memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); | ||
| 522 | skb->protocol = eth_type_trans(skb, ndev); | ||
| 523 | if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { | ||
| 524 | ndev->stats.rx_packets++; | ||
| 525 | ndev->stats.rx_bytes += skb->len; | ||
| 526 | } else { | ||
| 527 | ndev->stats.rx_dropped++; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | static const struct { | ||
| 532 | int eventid; | ||
| 533 | void (*handler)(struct wil6210_priv *wil, int eventid, | ||
| 534 | void *data, int data_len); | ||
| 535 | } wmi_evt_handlers[] = { | ||
| 536 | {WMI_READY_EVENTID, wmi_evt_ready}, | ||
| 537 | {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, | ||
| 538 | {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, | ||
| 539 | {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, | ||
| 540 | {WMI_CONNECT_EVENTID, wmi_evt_connect}, | ||
| 541 | {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, | ||
| 542 | {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, | ||
| 543 | {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, | ||
| 544 | }; | ||
| 545 | |||
| 546 | /* | ||
| 547 | * Run in IRQ context | ||
| 548 | * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev | ||
| 549 | * that will be eventually handled by the @wmi_event_worker in the thread | ||
| 550 | * context of thread "wil6210_wmi" | ||
| 551 | */ | ||
| 552 | void wmi_recv_cmd(struct wil6210_priv *wil) | ||
| 553 | { | ||
| 554 | struct wil6210_mbox_ring_desc d_tail; | ||
| 555 | struct wil6210_mbox_hdr hdr; | ||
| 556 | struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; | ||
| 557 | struct pending_wmi_event *evt; | ||
| 558 | u8 *cmd; | ||
| 559 | void __iomem *src; | ||
| 560 | ulong flags; | ||
| 561 | |||
| 562 | for (;;) { | ||
| 563 | u16 len; | ||
| 564 | |||
| 565 | r->head = ioread32(wil->csr + HOST_MBOX + | ||
| 566 | offsetof(struct wil6210_mbox_ctl, rx.head)); | ||
| 567 | if (r->tail == r->head) | ||
| 568 | return; | ||
| 569 | |||
| 570 | /* read cmd from tail */ | ||
| 571 | wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), | ||
| 572 | sizeof(struct wil6210_mbox_ring_desc)); | ||
| 573 | if (d_tail.sync == 0) { | ||
| 574 | wil_err(wil, "Mbox evt not owned by FW?\n"); | ||
| 575 | return; | ||
| 576 | } | ||
| 577 | |||
| 578 | if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { | ||
| 579 | wil_err(wil, "Mbox evt at 0x%08x?\n", | ||
| 580 | le32_to_cpu(d_tail.addr)); | ||
| 581 | return; | ||
| 582 | } | ||
| 583 | |||
| 584 | len = le16_to_cpu(hdr.len); | ||
| 585 | src = wmi_buffer(wil, d_tail.addr) + | ||
| 586 | sizeof(struct wil6210_mbox_hdr); | ||
| 587 | evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, | ||
| 588 | event.wmi) + len, 4), | ||
| 589 | GFP_KERNEL); | ||
| 590 | if (!evt) { | ||
| 591 | wil_err(wil, "kmalloc for WMI event (%d) failed\n", | ||
| 592 | len); | ||
| 593 | return; | ||
| 594 | } | ||
| 595 | evt->event.hdr = hdr; | ||
| 596 | cmd = (void *)&evt->event.wmi; | ||
| 597 | wil_memcpy_fromio_32(cmd, src, len); | ||
| 598 | /* mark entry as empty */ | ||
| 599 | iowrite32(0, wil->csr + HOSTADDR(r->tail) + | ||
| 600 | offsetof(struct wil6210_mbox_ring_desc, sync)); | ||
| 601 | /* indicate */ | ||
| 602 | wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", | ||
| 603 | le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), | ||
| 604 | hdr.flags); | ||
| 605 | if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && | ||
| 606 | (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { | ||
| 607 | wil_dbg_WMI(wil, "WMI event 0x%04x\n", | ||
| 608 | evt->event.wmi.id); | ||
| 609 | } | ||
| 610 | wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, | ||
| 611 | &evt->event.hdr, sizeof(hdr) + len, true); | ||
| 612 | |||
| 613 | /* advance tail */ | ||
| 614 | r->tail = r->base + ((r->tail - r->base + | ||
| 615 | sizeof(struct wil6210_mbox_ring_desc)) % r->size); | ||
| 616 | iowrite32(r->tail, wil->csr + HOST_MBOX + | ||
| 617 | offsetof(struct wil6210_mbox_ctl, rx.tail)); | ||
| 618 | |||
| 619 | /* add to the pending list */ | ||
| 620 | spin_lock_irqsave(&wil->wmi_ev_lock, flags); | ||
| 621 | list_add_tail(&evt->list, &wil->pending_wmi_ev); | ||
| 622 | spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); | ||
| 623 | { | ||
| 624 | int q = queue_work(wil->wmi_wq, | ||
| 625 | &wil->wmi_event_worker); | ||
| 626 | wil_dbg_WMI(wil, "queue_work -> %d\n", q); | ||
| 627 | } | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, | ||
| 632 | u16 reply_id, void *reply, u8 reply_size, int to_msec) | ||
| 633 | { | ||
| 634 | int rc; | ||
| 635 | int remain; | ||
| 636 | |||
| 637 | mutex_lock(&wil->wmi_mutex); | ||
| 638 | |||
| 639 | rc = __wmi_send(wil, cmdid, buf, len); | ||
| 640 | if (rc) | ||
| 641 | goto out; | ||
| 642 | |||
| 643 | wil->reply_id = reply_id; | ||
| 644 | wil->reply_buf = reply; | ||
| 645 | wil->reply_size = reply_size; | ||
| 646 | remain = wait_for_completion_timeout(&wil->wmi_ready, | ||
| 647 | msecs_to_jiffies(to_msec)); | ||
| 648 | if (0 == remain) { | ||
| 649 | wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", | ||
| 650 | cmdid, reply_id, to_msec); | ||
| 651 | rc = -ETIME; | ||
| 652 | } else { | ||
| 653 | wil_dbg_WMI(wil, | ||
| 654 | "wmi_call(0x%04x->0x%04x) completed in %d msec\n", | ||
| 655 | cmdid, reply_id, | ||
| 656 | to_msec - jiffies_to_msecs(remain)); | ||
| 657 | } | ||
| 658 | wil->reply_id = 0; | ||
| 659 | wil->reply_buf = NULL; | ||
| 660 | wil->reply_size = 0; | ||
| 661 | out: | ||
| 662 | mutex_unlock(&wil->wmi_mutex); | ||
| 663 | |||
| 664 | return rc; | ||
| 665 | } | ||
| 666 | |||
| 667 | int wmi_echo(struct wil6210_priv *wil) | ||
| 668 | { | ||
| 669 | struct wmi_echo_cmd cmd = { | ||
| 670 | .value = cpu_to_le32(0x12345678), | ||
| 671 | }; | ||
| 672 | |||
| 673 | return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), | ||
| 674 | WMI_ECHO_RSP_EVENTID, NULL, 0, 20); | ||
| 675 | } | ||
| 676 | |||
| 677 | int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) | ||
| 678 | { | ||
| 679 | struct wmi_set_mac_address_cmd cmd; | ||
| 680 | |||
| 681 | memcpy(cmd.mac, addr, ETH_ALEN); | ||
| 682 | |||
| 683 | wil_dbg_WMI(wil, "Set MAC %pM\n", addr); | ||
| 684 | |||
| 685 | return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); | ||
| 686 | } | ||
| 687 | |||
| 688 | int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype) | ||
| 689 | { | ||
| 690 | struct wmi_bcon_ctrl_cmd cmd = { | ||
| 691 | .bcon_interval = cpu_to_le16(bi), | ||
| 692 | .network_type = wmi_nettype, | ||
| 693 | .disable_sec_offload = 1, | ||
| 694 | }; | ||
| 695 | |||
| 696 | if (!wil->secure_pcp) | ||
| 697 | cmd.disable_sec = 1; | ||
| 698 | |||
| 699 | return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd)); | ||
| 700 | } | ||
| 701 | |||
| 702 | int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) | ||
| 703 | { | ||
| 704 | struct wmi_set_ssid_cmd cmd = { | ||
| 705 | .ssid_len = cpu_to_le32(ssid_len), | ||
| 706 | }; | ||
| 707 | |||
| 708 | if (ssid_len > sizeof(cmd.ssid)) | ||
| 709 | return -EINVAL; | ||
| 710 | |||
| 711 | memcpy(cmd.ssid, ssid, ssid_len); | ||
| 712 | |||
| 713 | return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); | ||
| 714 | } | ||
| 715 | |||
| 716 | int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) | ||
| 717 | { | ||
| 718 | int rc; | ||
| 719 | struct { | ||
| 720 | struct wil6210_mbox_hdr_wmi wmi; | ||
| 721 | struct wmi_set_ssid_cmd cmd; | ||
| 722 | } __packed reply; | ||
| 723 | int len; /* reply.cmd.ssid_len in CPU order */ | ||
| 724 | |||
| 725 | rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, | ||
| 726 | &reply, sizeof(reply), 20); | ||
| 727 | if (rc) | ||
| 728 | return rc; | ||
| 729 | |||
| 730 | len = le32_to_cpu(reply.cmd.ssid_len); | ||
| 731 | if (len > sizeof(reply.cmd.ssid)) | ||
| 732 | return -EINVAL; | ||
| 733 | |||
| 734 | *ssid_len = len; | ||
| 735 | memcpy(ssid, reply.cmd.ssid, len); | ||
| 736 | |||
| 737 | return 0; | ||
| 738 | } | ||
| 739 | |||
| 740 | int wmi_set_channel(struct wil6210_priv *wil, int channel) | ||
| 741 | { | ||
| 742 | struct wmi_set_pcp_channel_cmd cmd = { | ||
| 743 | .channel = channel - 1, | ||
| 744 | }; | ||
| 745 | |||
| 746 | return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); | ||
| 747 | } | ||
| 748 | |||
| 749 | int wmi_get_channel(struct wil6210_priv *wil, int *channel) | ||
| 750 | { | ||
| 751 | int rc; | ||
| 752 | struct { | ||
| 753 | struct wil6210_mbox_hdr_wmi wmi; | ||
| 754 | struct wmi_set_pcp_channel_cmd cmd; | ||
| 755 | } __packed reply; | ||
| 756 | |||
| 757 | rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, | ||
| 758 | WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); | ||
| 759 | if (rc) | ||
| 760 | return rc; | ||
| 761 | |||
| 762 | if (reply.cmd.channel > 3) | ||
| 763 | return -EINVAL; | ||
| 764 | |||
| 765 | *channel = reply.cmd.channel + 1; | ||
| 766 | |||
| 767 | return 0; | ||
| 768 | } | ||
| 769 | |||
| 770 | int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) | ||
| 771 | { | ||
| 772 | struct wmi_eapol_tx_cmd *cmd; | ||
| 773 | struct ethhdr *eth; | ||
| 774 | u16 eapol_len = skb->len - ETH_HLEN; | ||
| 775 | void *eapol = skb->data + ETH_HLEN; | ||
| 776 | uint i; | ||
| 777 | int rc; | ||
| 778 | |||
| 779 | skb_set_mac_header(skb, 0); | ||
| 780 | eth = eth_hdr(skb); | ||
| 781 | wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); | ||
| 782 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { | ||
| 783 | if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) | ||
| 784 | goto found_dest; | ||
| 785 | } | ||
| 786 | |||
| 787 | return -EINVAL; | ||
| 788 | |||
| 789 | found_dest: | ||
| 790 | /* find out eapol data & len */ | ||
| 791 | cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); | ||
| 792 | if (!cmd) | ||
| 793 | return -EINVAL; | ||
| 794 | |||
| 795 | memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); | ||
| 796 | cmd->eapol_len = cpu_to_le16(eapol_len); | ||
| 797 | memcpy(cmd->eapol, eapol, eapol_len); | ||
| 798 | rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); | ||
| 799 | kfree(cmd); | ||
| 800 | |||
| 801 | return rc; | ||
| 802 | } | ||
| 803 | |||
| 804 | int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
| 805 | const void *mac_addr) | ||
| 806 | { | ||
| 807 | struct wmi_delete_cipher_key_cmd cmd = { | ||
| 808 | .key_index = key_index, | ||
| 809 | }; | ||
| 810 | |||
| 811 | if (mac_addr) | ||
| 812 | memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); | ||
| 813 | |||
| 814 | return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); | ||
| 815 | } | ||
| 816 | |||
| 817 | int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
| 818 | const void *mac_addr, int key_len, const void *key) | ||
| 819 | { | ||
| 820 | struct wmi_add_cipher_key_cmd cmd = { | ||
| 821 | .key_index = key_index, | ||
| 822 | .key_usage = WMI_KEY_USE_PAIRWISE, | ||
| 823 | .key_len = key_len, | ||
| 824 | }; | ||
| 825 | |||
| 826 | if (!key || (key_len > sizeof(cmd.key))) | ||
| 827 | return -EINVAL; | ||
| 828 | |||
| 829 | memcpy(cmd.key, key, key_len); | ||
| 830 | if (mac_addr) | ||
| 831 | memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); | ||
| 832 | |||
| 833 | return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); | ||
| 834 | } | ||
| 835 | |||
| 836 | int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) | ||
| 837 | { | ||
| 838 | int rc; | ||
| 839 | u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; | ||
| 840 | struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); | ||
| 841 | if (!cmd) { | ||
| 842 | wil_err(wil, "kmalloc(%d) failed\n", len); | ||
| 843 | return -ENOMEM; | ||
| 844 | } | ||
| 845 | |||
| 846 | cmd->mgmt_frm_type = type; | ||
| 847 | /* BUG: FW API define ieLen as u8. Will fix FW */ | ||
| 848 | cmd->ie_len = cpu_to_le16(ie_len); | ||
| 849 | memcpy(cmd->ie_info, ie, ie_len); | ||
| 850 | rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len); | ||
| 851 | kfree(cmd); | ||
| 852 | |||
| 853 | return rc; | ||
| 854 | } | ||
| 855 | |||
| 856 | void wmi_event_flush(struct wil6210_priv *wil) | ||
| 857 | { | ||
| 858 | struct pending_wmi_event *evt, *t; | ||
| 859 | |||
| 860 | wil_dbg_WMI(wil, "%s()\n", __func__); | ||
| 861 | |||
| 862 | list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { | ||
| 863 | list_del(&evt->list); | ||
| 864 | kfree(evt); | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 868 | static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, | ||
| 869 | void *d, int len) | ||
| 870 | { | ||
| 871 | uint i; | ||
| 872 | |||
| 873 | for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { | ||
| 874 | if (wmi_evt_handlers[i].eventid == id) { | ||
| 875 | wmi_evt_handlers[i].handler(wil, id, d, len); | ||
| 876 | return true; | ||
| 877 | } | ||
| 878 | } | ||
| 879 | |||
| 880 | return false; | ||
| 881 | } | ||
| 882 | |||
| 883 | static void wmi_event_handle(struct wil6210_priv *wil, | ||
| 884 | struct wil6210_mbox_hdr *hdr) | ||
| 885 | { | ||
| 886 | u16 len = le16_to_cpu(hdr->len); | ||
| 887 | |||
| 888 | if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && | ||
| 889 | (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { | ||
| 890 | struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); | ||
| 891 | void *evt_data = (void *)(&wmi[1]); | ||
| 892 | u16 id = le16_to_cpu(wmi->id); | ||
| 893 | /* check if someone waits for this event */ | ||
| 894 | if (wil->reply_id && wil->reply_id == id) { | ||
| 895 | if (wil->reply_buf) { | ||
| 896 | memcpy(wil->reply_buf, wmi, | ||
| 897 | min(len, wil->reply_size)); | ||
| 898 | } else { | ||
| 899 | wmi_evt_call_handler(wil, id, evt_data, | ||
| 900 | len - sizeof(*wmi)); | ||
| 901 | } | ||
| 902 | wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); | ||
| 903 | complete(&wil->wmi_ready); | ||
| 904 | return; | ||
| 905 | } | ||
| 906 | /* unsolicited event */ | ||
| 907 | /* search for handler */ | ||
| 908 | if (!wmi_evt_call_handler(wil, id, evt_data, | ||
| 909 | len - sizeof(*wmi))) { | ||
| 910 | wil_err(wil, "Unhandled event 0x%04x\n", id); | ||
| 911 | } | ||
| 912 | } else { | ||
| 913 | wil_err(wil, "Unknown event type\n"); | ||
| 914 | print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, | ||
| 915 | hdr, sizeof(*hdr) + len, true); | ||
| 916 | } | ||
| 917 | } | ||
| 918 | |||
| 919 | /* | ||
| 920 | * Retrieve next WMI event from the pending list | ||
| 921 | */ | ||
| 922 | static struct list_head *next_wmi_ev(struct wil6210_priv *wil) | ||
| 923 | { | ||
| 924 | ulong flags; | ||
| 925 | struct list_head *ret = NULL; | ||
| 926 | |||
| 927 | spin_lock_irqsave(&wil->wmi_ev_lock, flags); | ||
| 928 | |||
| 929 | if (!list_empty(&wil->pending_wmi_ev)) { | ||
| 930 | ret = wil->pending_wmi_ev.next; | ||
| 931 | list_del(ret); | ||
| 932 | } | ||
| 933 | |||
| 934 | spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); | ||
| 935 | |||
| 936 | return ret; | ||
| 937 | } | ||
| 938 | |||
| 939 | /* | ||
| 940 | * Handler for the WMI events | ||
| 941 | */ | ||
| 942 | void wmi_event_worker(struct work_struct *work) | ||
| 943 | { | ||
| 944 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | ||
| 945 | wmi_event_worker); | ||
| 946 | struct pending_wmi_event *evt; | ||
| 947 | struct list_head *lh; | ||
| 948 | |||
| 949 | while ((lh = next_wmi_ev(wil)) != NULL) { | ||
| 950 | evt = list_entry(lh, struct pending_wmi_event, list); | ||
| 951 | wmi_event_handle(wil, &evt->event.hdr); | ||
| 952 | kfree(evt); | ||
| 953 | } | ||
| 954 | } | ||
| 955 | |||
| 956 | void wmi_connect_worker(struct work_struct *work) | ||
| 957 | { | ||
| 958 | int rc; | ||
| 959 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | ||
| 960 | wmi_connect_worker); | ||
| 961 | |||
| 962 | if (wil->pending_connect_cid < 0) { | ||
| 963 | wil_err(wil, "No connection pending\n"); | ||
| 964 | return; | ||
| 965 | } | ||
| 966 | |||
| 967 | wil_dbg_WMI(wil, "Configure for connection CID %d\n", | ||
| 968 | wil->pending_connect_cid); | ||
| 969 | |||
| 970 | rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, | ||
| 971 | wil->pending_connect_cid, 0); | ||
| 972 | wil->pending_connect_cid = -1; | ||
| 973 | if (rc == 0) | ||
| 974 | wil_link_on(wil); | ||
| 975 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h new file mode 100644 index 000000000000..3bbf87572b07 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.h | |||
| @@ -0,0 +1,1116 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
| 3 | * Copyright (c) 2006-2012 Wilocity . | ||
| 4 | * | ||
| 5 | * Permission to use, copy, modify, and/or distribute this software for any | ||
| 6 | * purpose with or without fee is hereby granted, provided that the above | ||
| 7 | * copyright notice and this permission notice appear in all copies. | ||
| 8 | * | ||
| 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 16 | */ | ||
| 17 | |||
| 18 | /* | ||
| 19 | * This file contains the definitions of the WMI protocol specified in the | ||
| 20 | * Wireless Module Interface (WMI) for the Wilocity | ||
| 21 | * MARLON 60 Gigabit wireless solution. | ||
| 22 | * It includes definitions of all the commands and events. | ||
| 23 | * Commands are messages from the host to the WM. | ||
| 24 | * Events are messages from the WM to the host. | ||
| 25 | */ | ||
| 26 | |||
| 27 | #ifndef __WILOCITY_WMI_H__ | ||
| 28 | #define __WILOCITY_WMI_H__ | ||
| 29 | |||
| 30 | /* General */ | ||
| 31 | |||
| 32 | #define WMI_MAC_LEN (6) | ||
| 33 | #define WMI_PROX_RANGE_NUM (3) | ||
| 34 | |||
| 35 | /* List of Commands */ | ||
| 36 | enum wmi_command_id { | ||
| 37 | WMI_CONNECT_CMDID = 0x0001, | ||
| 38 | WMI_DISCONNECT_CMDID = 0x0003, | ||
| 39 | WMI_START_SCAN_CMDID = 0x0007, | ||
| 40 | WMI_SET_BSS_FILTER_CMDID = 0x0009, | ||
| 41 | WMI_SET_PROBED_SSID_CMDID = 0x000a, | ||
| 42 | WMI_SET_LISTEN_INT_CMDID = 0x000b, | ||
| 43 | WMI_BCON_CTRL_CMDID = 0x000f, | ||
| 44 | WMI_ADD_CIPHER_KEY_CMDID = 0x0016, | ||
| 45 | WMI_DELETE_CIPHER_KEY_CMDID = 0x0017, | ||
| 46 | WMI_SET_APPIE_CMDID = 0x003f, | ||
| 47 | WMI_GET_APPIE_CMDID = 0x0040, | ||
| 48 | WMI_SET_WSC_STATUS_CMDID = 0x0041, | ||
| 49 | WMI_PXMT_RANGE_CFG_CMDID = 0x0042, | ||
| 50 | WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043, | ||
| 51 | WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, | ||
| 52 | WMI_MEM_READ_CMDID = 0x0800, | ||
| 53 | WMI_MEM_WR_CMDID = 0x0801, | ||
| 54 | WMI_ECHO_CMDID = 0x0803, | ||
| 55 | WMI_DEEP_ECHO_CMDID = 0x0804, | ||
| 56 | WMI_CONFIG_MAC_CMDID = 0x0805, | ||
| 57 | WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806, | ||
| 58 | WMI_ADD_STATION_CMDID = 0x0807, | ||
| 59 | WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808, | ||
| 60 | WMI_PHY_GET_STATISTICS_CMDID = 0x0809, | ||
| 61 | WMI_FS_TUNE_CMDID = 0x080a, | ||
| 62 | WMI_CORR_MEASURE_CMDID = 0x080b, | ||
| 63 | WMI_TEMP_SENSE_CMDID = 0x080e, | ||
| 64 | WMI_DC_CALIB_CMDID = 0x080f, | ||
| 65 | WMI_SEND_TONE_CMDID = 0x0810, | ||
| 66 | WMI_IQ_TX_CALIB_CMDID = 0x0811, | ||
| 67 | WMI_IQ_RX_CALIB_CMDID = 0x0812, | ||
| 68 | WMI_SET_UCODE_IDLE_CMDID = 0x0813, | ||
| 69 | WMI_SET_WORK_MODE_CMDID = 0x0815, | ||
| 70 | WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, | ||
| 71 | WMI_MARLON_R_ACTIVATE_CMDID = 0x0817, | ||
| 72 | WMI_MARLON_R_READ_CMDID = 0x0818, | ||
| 73 | WMI_MARLON_R_WRITE_CMDID = 0x0819, | ||
| 74 | WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a, | ||
| 75 | MAC_IO_STATIC_PARAMS_CMDID = 0x081b, | ||
| 76 | MAC_IO_DYNAMIC_PARAMS_CMDID = 0x081c, | ||
| 77 | WMI_SILENT_RSSI_CALIB_CMDID = 0x081d, | ||
| 78 | WMI_CFG_RX_CHAIN_CMDID = 0x0820, | ||
| 79 | WMI_VRING_CFG_CMDID = 0x0821, | ||
| 80 | WMI_RX_ON_CMDID = 0x0822, | ||
| 81 | WMI_VRING_BA_EN_CMDID = 0x0823, | ||
| 82 | WMI_VRING_BA_DIS_CMDID = 0x0824, | ||
| 83 | WMI_RCP_ADDBA_RESP_CMDID = 0x0825, | ||
| 84 | WMI_RCP_DELBA_CMDID = 0x0826, | ||
| 85 | WMI_SET_SSID_CMDID = 0x0827, | ||
| 86 | WMI_GET_SSID_CMDID = 0x0828, | ||
| 87 | WMI_SET_PCP_CHANNEL_CMDID = 0x0829, | ||
| 88 | WMI_GET_PCP_CHANNEL_CMDID = 0x082a, | ||
| 89 | WMI_SW_TX_REQ_CMDID = 0x082b, | ||
| 90 | WMI_RX_OFF_CMDID = 0x082c, | ||
| 91 | WMI_READ_MAC_RXQ_CMDID = 0x0830, | ||
| 92 | WMI_READ_MAC_TXQ_CMDID = 0x0831, | ||
| 93 | WMI_WRITE_MAC_RXQ_CMDID = 0x0832, | ||
| 94 | WMI_WRITE_MAC_TXQ_CMDID = 0x0833, | ||
| 95 | WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834, | ||
| 96 | WMI_MLME_PUSH_CMDID = 0x0835, | ||
| 97 | WMI_BEAMFORMING_MGMT_CMDID = 0x0836, | ||
| 98 | WMI_BF_TXSS_MGMT_CMDID = 0x0837, | ||
| 99 | WMI_BF_SM_MGMT_CMDID = 0x0838, | ||
| 100 | WMI_BF_RXSS_MGMT_CMDID = 0x0839, | ||
| 101 | WMI_SET_SECTORS_CMDID = 0x0849, | ||
| 102 | WMI_MAINTAIN_PAUSE_CMDID = 0x0850, | ||
| 103 | WMI_MAINTAIN_RESUME_CMDID = 0x0851, | ||
| 104 | WMI_RS_MGMT_CMDID = 0x0852, | ||
| 105 | WMI_RF_MGMT_CMDID = 0x0853, | ||
| 106 | /* Performance monitoring commands */ | ||
| 107 | WMI_BF_CTRL_CMDID = 0x0862, | ||
| 108 | WMI_NOTIFY_REQ_CMDID = 0x0863, | ||
| 109 | WMI_GET_STATUS_CMDID = 0x0864, | ||
| 110 | WMI_UNIT_TEST_CMDID = 0x0900, | ||
| 111 | WMI_HICCUP_CMDID = 0x0901, | ||
| 112 | WMI_FLASH_READ_CMDID = 0x0902, | ||
| 113 | WMI_FLASH_WRITE_CMDID = 0x0903, | ||
| 114 | WMI_SECURITY_UNIT_TEST_CMDID = 0x0904, | ||
| 115 | |||
| 116 | WMI_SET_MAC_ADDRESS_CMDID = 0xf003, | ||
| 117 | WMI_ABORT_SCAN_CMDID = 0xf007, | ||
| 118 | WMI_SET_PMK_CMDID = 0xf028, | ||
| 119 | |||
| 120 | WMI_SET_PROMISCUOUS_MODE_CMDID = 0xf041, | ||
| 121 | WMI_GET_PMK_CMDID = 0xf048, | ||
| 122 | WMI_SET_PASSPHRASE_CMDID = 0xf049, | ||
| 123 | WMI_SEND_ASSOC_RES_CMDID = 0xf04a, | ||
| 124 | WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xf04b, | ||
| 125 | WMI_EAPOL_TX_CMDID = 0xf04c, | ||
| 126 | WMI_MAC_ADDR_REQ_CMDID = 0xf04d, | ||
| 127 | WMI_FW_VER_CMDID = 0xf04e, | ||
| 128 | }; | ||
| 129 | |||
| 130 | /* | ||
| 131 | * Commands data structures | ||
| 132 | */ | ||
| 133 | |||
| 134 | /* | ||
| 135 | * Frame Types | ||
| 136 | */ | ||
| 137 | enum wmi_mgmt_frame_type { | ||
| 138 | WMI_FRAME_BEACON = 0, | ||
| 139 | WMI_FRAME_PROBE_REQ = 1, | ||
| 140 | WMI_FRAME_PROBE_RESP = 2, | ||
| 141 | WMI_FRAME_ASSOC_REQ = 3, | ||
| 142 | WMI_FRAME_ASSOC_RESP = 4, | ||
| 143 | WMI_NUM_MGMT_FRAME, | ||
| 144 | }; | ||
| 145 | |||
| 146 | /* | ||
| 147 | * WMI_CONNECT_CMDID | ||
| 148 | */ | ||
| 149 | enum wmi_network_type { | ||
| 150 | WMI_NETTYPE_INFRA = 0x01, | ||
| 151 | WMI_NETTYPE_ADHOC = 0x02, | ||
| 152 | WMI_NETTYPE_ADHOC_CREATOR = 0x04, | ||
| 153 | WMI_NETTYPE_AP = 0x10, | ||
| 154 | WMI_NETTYPE_P2P = 0x20, | ||
| 155 | WMI_NETTYPE_WBE = 0x40, /* PCIE over 60g */ | ||
| 156 | }; | ||
| 157 | |||
| 158 | enum wmi_dot11_auth_mode { | ||
| 159 | WMI_AUTH11_OPEN = 0x01, | ||
| 160 | WMI_AUTH11_SHARED = 0x02, | ||
| 161 | WMI_AUTH11_LEAP = 0x04, | ||
| 162 | WMI_AUTH11_WSC = 0x08, | ||
| 163 | }; | ||
| 164 | |||
| 165 | enum wmi_auth_mode { | ||
| 166 | WMI_AUTH_NONE = 0x01, | ||
| 167 | WMI_AUTH_WPA = 0x02, | ||
| 168 | WMI_AUTH_WPA2 = 0x04, | ||
| 169 | WMI_AUTH_WPA_PSK = 0x08, | ||
| 170 | WMI_AUTH_WPA2_PSK = 0x10, | ||
| 171 | WMI_AUTH_WPA_CCKM = 0x20, | ||
| 172 | WMI_AUTH_WPA2_CCKM = 0x40, | ||
| 173 | }; | ||
| 174 | |||
| 175 | enum wmi_crypto_type { | ||
| 176 | WMI_CRYPT_NONE = 0x01, | ||
| 177 | WMI_CRYPT_WEP = 0x02, | ||
| 178 | WMI_CRYPT_TKIP = 0x04, | ||
| 179 | WMI_CRYPT_AES = 0x08, | ||
| 180 | WMI_CRYPT_AES_GCMP = 0x20, | ||
| 181 | }; | ||
| 182 | |||
| 183 | |||
| 184 | enum wmi_connect_ctrl_flag_bits { | ||
| 185 | WMI_CONNECT_ASSOC_POLICY_USER = 0x0001, | ||
| 186 | WMI_CONNECT_SEND_REASSOC = 0x0002, | ||
| 187 | WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, | ||
| 188 | WMI_CONNECT_PROFILE_MATCH_DONE = 0x0008, | ||
| 189 | WMI_CONNECT_IGNORE_AAC_BEACON = 0x0010, | ||
| 190 | WMI_CONNECT_CSA_FOLLOW_BSS = 0x0020, | ||
| 191 | WMI_CONNECT_DO_WPA_OFFLOAD = 0x0040, | ||
| 192 | WMI_CONNECT_DO_NOT_DEAUTH = 0x0080, | ||
| 193 | }; | ||
| 194 | |||
| 195 | #define WMI_MAX_SSID_LEN (32) | ||
| 196 | |||
| 197 | struct wmi_connect_cmd { | ||
| 198 | u8 network_type; | ||
| 199 | u8 dot11_auth_mode; | ||
| 200 | u8 auth_mode; | ||
| 201 | u8 pairwise_crypto_type; | ||
| 202 | u8 pairwise_crypto_len; | ||
| 203 | u8 group_crypto_type; | ||
| 204 | u8 group_crypto_len; | ||
| 205 | u8 ssid_len; | ||
| 206 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
| 207 | u8 channel; | ||
| 208 | u8 reserved0; | ||
| 209 | u8 bssid[WMI_MAC_LEN]; | ||
| 210 | __le32 ctrl_flags; | ||
| 211 | u8 dst_mac[WMI_MAC_LEN]; | ||
| 212 | u8 reserved1[2]; | ||
| 213 | } __packed; | ||
| 214 | |||
| 215 | |||
| 216 | /* | ||
| 217 | * WMI_RECONNECT_CMDID | ||
| 218 | */ | ||
| 219 | struct wmi_reconnect_cmd { | ||
| 220 | u8 channel; /* hint */ | ||
| 221 | u8 reserved; | ||
| 222 | u8 bssid[WMI_MAC_LEN]; /* mandatory if set */ | ||
| 223 | } __packed; | ||
| 224 | |||
| 225 | |||
| 226 | /* | ||
| 227 | * WMI_SET_PMK_CMDID | ||
| 228 | */ | ||
| 229 | |||
| 230 | #define WMI_MIN_KEY_INDEX (0) | ||
| 231 | #define WMI_MAX_KEY_INDEX (3) | ||
| 232 | #define WMI_MAX_KEY_LEN (32) | ||
| 233 | #define WMI_PASSPHRASE_LEN (64) | ||
| 234 | #define WMI_PMK_LEN (32) | ||
| 235 | |||
| 236 | struct wmi_set_pmk_cmd { | ||
| 237 | u8 pmk[WMI_PMK_LEN]; | ||
| 238 | } __packed; | ||
| 239 | |||
| 240 | |||
| 241 | /* | ||
| 242 | * WMI_SET_PASSPHRASE_CMDID | ||
| 243 | */ | ||
| 244 | struct wmi_set_passphrase_cmd { | ||
| 245 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
| 246 | u8 passphrase[WMI_PASSPHRASE_LEN]; | ||
| 247 | u8 ssid_len; | ||
| 248 | u8 passphrase_len; | ||
| 249 | } __packed; | ||
| 250 | |||
| 251 | /* | ||
| 252 | * WMI_ADD_CIPHER_KEY_CMDID | ||
| 253 | */ | ||
| 254 | enum wmi_key_usage { | ||
| 255 | WMI_KEY_USE_PAIRWISE = 0, | ||
| 256 | WMI_KEY_USE_GROUP = 1, | ||
| 257 | WMI_KEY_USE_TX = 2, /* default Tx Key - Static WEP only */ | ||
| 258 | }; | ||
| 259 | |||
| 260 | struct wmi_add_cipher_key_cmd { | ||
| 261 | u8 key_index; | ||
| 262 | u8 key_type; | ||
| 263 | u8 key_usage; /* enum wmi_key_usage */ | ||
| 264 | u8 key_len; | ||
| 265 | u8 key_rsc[8]; /* key replay sequence counter */ | ||
| 266 | u8 key[WMI_MAX_KEY_LEN]; | ||
| 267 | u8 key_op_ctrl; /* Additional Key Control information */ | ||
| 268 | u8 mac[WMI_MAC_LEN]; | ||
| 269 | } __packed; | ||
| 270 | |||
| 271 | /* | ||
| 272 | * WMI_DELETE_CIPHER_KEY_CMDID | ||
| 273 | */ | ||
| 274 | struct wmi_delete_cipher_key_cmd { | ||
| 275 | u8 key_index; | ||
| 276 | u8 mac[WMI_MAC_LEN]; | ||
| 277 | } __packed; | ||
| 278 | |||
| 279 | |||
| 280 | /* | ||
| 281 | * WMI_START_SCAN_CMDID | ||
| 282 | * | ||
| 283 | * Start L1 scan operation | ||
| 284 | * | ||
| 285 | * Returned events: | ||
| 286 | * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp. | ||
| 287 | * - WMI_SCAN_COMPLETE_EVENTID | ||
| 288 | */ | ||
| 289 | enum wmi_scan_type { | ||
| 290 | WMI_LONG_SCAN = 0, | ||
| 291 | WMI_SHORT_SCAN = 1, | ||
| 292 | }; | ||
| 293 | |||
| 294 | struct wmi_start_scan_cmd { | ||
| 295 | u8 reserved[8]; | ||
| 296 | __le32 home_dwell_time; /* Max duration in the home channel(ms) */ | ||
| 297 | __le32 force_scan_interval; /* Time interval between scans (ms)*/ | ||
| 298 | u8 scan_type; /* wmi_scan_type */ | ||
| 299 | u8 num_channels; /* how many channels follow */ | ||
| 300 | struct { | ||
| 301 | u8 channel; | ||
| 302 | u8 reserved; | ||
| 303 | } channel_list[0]; /* channels ID's */ | ||
| 304 | /* 0 - 58320 MHz */ | ||
| 305 | /* 1 - 60480 MHz */ | ||
| 306 | /* 2 - 62640 MHz */ | ||
| 307 | } __packed; | ||
| 308 | |||
| 309 | /* | ||
| 310 | * WMI_SET_PROBED_SSID_CMDID | ||
| 311 | */ | ||
| 312 | #define MAX_PROBED_SSID_INDEX (15) | ||
| 313 | |||
| 314 | enum wmi_ssid_flag { | ||
| 315 | WMI_SSID_FLAG_DISABLE = 0, /* disables entry */ | ||
| 316 | WMI_SSID_FLAG_SPECIFIC = 1, /* probes specified ssid */ | ||
| 317 | WMI_SSID_FLAG_ANY = 2, /* probes for any ssid */ | ||
| 318 | }; | ||
| 319 | |||
| 320 | struct wmi_probed_ssid_cmd { | ||
| 321 | u8 entry_index; /* 0 to MAX_PROBED_SSID_INDEX */ | ||
| 322 | u8 flag; /* enum wmi_ssid_flag */ | ||
| 323 | u8 ssid_len; | ||
| 324 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
| 325 | } __packed; | ||
| 326 | |||
| 327 | /* | ||
| 328 | * WMI_SET_APPIE_CMDID | ||
| 329 | * Add Application specified IE to a management frame | ||
| 330 | */ | ||
| 331 | struct wmi_set_appie_cmd { | ||
| 332 | u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ | ||
| 333 | u8 reserved; | ||
| 334 | __le16 ie_len; /* Length of the IE to be added to MGMT frame */ | ||
| 335 | u8 ie_info[0]; | ||
| 336 | } __packed; | ||
| 337 | |||
| 338 | #define WMI_MAX_IE_LEN (1024) | ||
| 339 | |||
| 340 | struct wmi_pxmt_range_cfg_cmd { | ||
| 341 | u8 dst_mac[WMI_MAC_LEN]; | ||
| 342 | __le16 range; | ||
| 343 | } __packed; | ||
| 344 | |||
| 345 | struct wmi_pxmt_snr2_range_cfg_cmd { | ||
| 346 | s8 snr2range_arr[WMI_PROX_RANGE_NUM-1]; | ||
| 347 | } __packed; | ||
| 348 | |||
| 349 | /* | ||
| 350 | * WMI_RF_MGMT_CMDID | ||
| 351 | */ | ||
| 352 | enum wmi_rf_mgmt_type { | ||
| 353 | WMI_RF_MGMT_W_DISABLE = 0, | ||
| 354 | WMI_RF_MGMT_W_ENABLE = 1, | ||
| 355 | WMI_RF_MGMT_GET_STATUS = 2, | ||
| 356 | }; | ||
| 357 | |||
| 358 | struct wmi_rf_mgmt_cmd { | ||
| 359 | __le32 rf_mgmt_type; | ||
| 360 | } __packed; | ||
| 361 | |||
| 362 | /* | ||
| 363 | * WMI_SET_SSID_CMDID | ||
| 364 | */ | ||
| 365 | struct wmi_set_ssid_cmd { | ||
| 366 | __le32 ssid_len; | ||
| 367 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
| 368 | } __packed; | ||
| 369 | |||
| 370 | /* | ||
| 371 | * WMI_SET_PCP_CHANNEL_CMDID | ||
| 372 | */ | ||
| 373 | struct wmi_set_pcp_channel_cmd { | ||
| 374 | u8 channel; | ||
| 375 | u8 reserved[3]; | ||
| 376 | } __packed; | ||
| 377 | |||
| 378 | /* | ||
| 379 | * WMI_BCON_CTRL_CMDID | ||
| 380 | */ | ||
| 381 | struct wmi_bcon_ctrl_cmd { | ||
| 382 | __le16 bcon_interval; | ||
| 383 | __le16 frag_num; | ||
| 384 | __le64 ss_mask; | ||
| 385 | u8 network_type; | ||
| 386 | u8 reserved; | ||
| 387 | u8 disable_sec_offload; | ||
| 388 | u8 disable_sec; | ||
| 389 | } __packed; | ||
| 390 | |||
| 391 | /* | ||
| 392 | * WMI_SW_TX_REQ_CMDID | ||
| 393 | */ | ||
| 394 | struct wmi_sw_tx_req_cmd { | ||
| 395 | u8 dst_mac[WMI_MAC_LEN]; | ||
| 396 | __le16 len; | ||
| 397 | u8 payload[0]; | ||
| 398 | } __packed; | ||
| 399 | |||
| 400 | /* | ||
| 401 | * WMI_VRING_CFG_CMDID | ||
| 402 | */ | ||
| 403 | |||
| 404 | struct wmi_sw_ring_cfg { | ||
| 405 | __le64 ring_mem_base; | ||
| 406 | __le16 ring_size; | ||
| 407 | __le16 max_mpdu_size; | ||
| 408 | } __packed; | ||
| 409 | |||
| 410 | struct wmi_vring_cfg_schd { | ||
| 411 | __le16 priority; | ||
| 412 | __le16 timeslot_us; | ||
| 413 | } __packed; | ||
| 414 | |||
| 415 | enum wmi_vring_cfg_encap_trans_type { | ||
| 416 | WMI_VRING_ENC_TYPE_802_3 = 0, | ||
| 417 | WMI_VRING_ENC_TYPE_NATIVE_WIFI = 1, | ||
| 418 | }; | ||
| 419 | |||
| 420 | enum wmi_vring_cfg_ds_cfg { | ||
| 421 | WMI_VRING_DS_PBSS = 0, | ||
| 422 | WMI_VRING_DS_STATION = 1, | ||
| 423 | WMI_VRING_DS_AP = 2, | ||
| 424 | WMI_VRING_DS_ADDR4 = 3, | ||
| 425 | }; | ||
| 426 | |||
| 427 | enum wmi_vring_cfg_nwifi_ds_trans_type { | ||
| 428 | WMI_NWIFI_TX_TRANS_MODE_NO = 0, | ||
| 429 | WMI_NWIFI_TX_TRANS_MODE_AP2PBSS = 1, | ||
| 430 | WMI_NWIFI_TX_TRANS_MODE_STA2PBSS = 2, | ||
| 431 | }; | ||
| 432 | |||
| 433 | enum wmi_vring_cfg_schd_params_priority { | ||
| 434 | WMI_SCH_PRIO_REGULAR = 0, | ||
| 435 | WMI_SCH_PRIO_HIGH = 1, | ||
| 436 | }; | ||
| 437 | |||
| 438 | struct wmi_vring_cfg { | ||
| 439 | struct wmi_sw_ring_cfg tx_sw_ring; | ||
| 440 | u8 ringid; /* 0-23 vrings */ | ||
| 441 | |||
| 442 | #define CIDXTID_CID_POS (0) | ||
| 443 | #define CIDXTID_CID_LEN (4) | ||
| 444 | #define CIDXTID_CID_MSK (0xF) | ||
| 445 | #define CIDXTID_TID_POS (4) | ||
| 446 | #define CIDXTID_TID_LEN (4) | ||
| 447 | #define CIDXTID_TID_MSK (0xF0) | ||
| 448 | u8 cidxtid; | ||
| 449 | |||
| 450 | u8 encap_trans_type; | ||
| 451 | u8 ds_cfg; /* 802.3 DS cfg */ | ||
| 452 | u8 nwifi_ds_trans_type; | ||
| 453 | |||
| 454 | #define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0) | ||
| 455 | #define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1) | ||
| 456 | #define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1) | ||
| 457 | #define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1) | ||
| 458 | #define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1) | ||
| 459 | #define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2) | ||
| 460 | u8 mac_ctrl; | ||
| 461 | |||
| 462 | #define VRING_CFG_TO_RESOLUTION_VALUE_POS (0) | ||
| 463 | #define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6) | ||
| 464 | #define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F) | ||
| 465 | u8 to_resolution; | ||
| 466 | u8 agg_max_wsize; | ||
| 467 | struct wmi_vring_cfg_schd schd_params; | ||
| 468 | } __packed; | ||
| 469 | |||
| 470 | enum wmi_vring_cfg_cmd_action { | ||
| 471 | WMI_VRING_CMD_ADD = 0, | ||
| 472 | WMI_VRING_CMD_MODIFY = 1, | ||
| 473 | WMI_VRING_CMD_DELETE = 2, | ||
| 474 | }; | ||
| 475 | |||
| 476 | struct wmi_vring_cfg_cmd { | ||
| 477 | __le32 action; | ||
| 478 | struct wmi_vring_cfg vring_cfg; | ||
| 479 | } __packed; | ||
| 480 | |||
| 481 | /* | ||
| 482 | * WMI_VRING_BA_EN_CMDID | ||
| 483 | */ | ||
| 484 | struct wmi_vring_ba_en_cmd { | ||
| 485 | u8 ringid; | ||
| 486 | u8 agg_max_wsize; | ||
| 487 | __le16 ba_timeout; | ||
| 488 | } __packed; | ||
| 489 | |||
| 490 | /* | ||
| 491 | * WMI_VRING_BA_DIS_CMDID | ||
| 492 | */ | ||
| 493 | struct wmi_vring_ba_dis_cmd { | ||
| 494 | u8 ringid; | ||
| 495 | u8 reserved; | ||
| 496 | __le16 reason; | ||
| 497 | } __packed; | ||
| 498 | |||
| 499 | /* | ||
| 500 | * WMI_NOTIFY_REQ_CMDID | ||
| 501 | */ | ||
| 502 | struct wmi_notify_req_cmd { | ||
| 503 | u8 cid; | ||
| 504 | u8 reserved[3]; | ||
| 505 | __le32 interval_usec; | ||
| 506 | } __packed; | ||
| 507 | |||
| 508 | /* | ||
| 509 | * WMI_CFG_RX_CHAIN_CMDID | ||
| 510 | */ | ||
| 511 | enum wmi_sniffer_cfg_mode { | ||
| 512 | WMI_SNIFFER_OFF = 0, | ||
| 513 | WMI_SNIFFER_ON = 1, | ||
| 514 | }; | ||
| 515 | |||
| 516 | enum wmi_sniffer_cfg_phy_info_mode { | ||
| 517 | WMI_SNIFFER_PHY_INFO_DISABLED = 0, | ||
| 518 | WMI_SNIFFER_PHY_INFO_ENABLED = 1, | ||
| 519 | }; | ||
| 520 | |||
| 521 | enum wmi_sniffer_cfg_phy_support { | ||
| 522 | WMI_SNIFFER_CP = 0, | ||
| 523 | WMI_SNIFFER_DP = 1, | ||
| 524 | WMI_SNIFFER_BOTH_PHYS = 2, | ||
| 525 | }; | ||
| 526 | |||
| 527 | struct wmi_sniffer_cfg { | ||
| 528 | __le32 mode; /* enum wmi_sniffer_cfg_mode */ | ||
| 529 | __le32 phy_info_mode; /* enum wmi_sniffer_cfg_phy_info_mode */ | ||
| 530 | __le32 phy_support; /* enum wmi_sniffer_cfg_phy_support */ | ||
| 531 | u8 channel; | ||
| 532 | u8 reserved[3]; | ||
| 533 | } __packed; | ||
| 534 | |||
| 535 | enum wmi_cfg_rx_chain_cmd_action { | ||
| 536 | WMI_RX_CHAIN_ADD = 0, | ||
| 537 | WMI_RX_CHAIN_DEL = 1, | ||
| 538 | }; | ||
| 539 | |||
| 540 | enum wmi_cfg_rx_chain_cmd_decap_trans_type { | ||
| 541 | WMI_DECAP_TYPE_802_3 = 0, | ||
| 542 | WMI_DECAP_TYPE_NATIVE_WIFI = 1, | ||
| 543 | }; | ||
| 544 | |||
| 545 | enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { | ||
| 546 | WMI_NWIFI_RX_TRANS_MODE_NO = 0, | ||
| 547 | WMI_NWIFI_RX_TRANS_MODE_PBSS2AP = 1, | ||
| 548 | WMI_NWIFI_RX_TRANS_MODE_PBSS2STA = 2, | ||
| 549 | }; | ||
| 550 | |||
| 551 | struct wmi_cfg_rx_chain_cmd { | ||
| 552 | __le32 action; | ||
| 553 | struct wmi_sw_ring_cfg rx_sw_ring; | ||
| 554 | u8 mid; | ||
| 555 | u8 decap_trans_type; | ||
| 556 | |||
| 557 | #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) | ||
| 558 | #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) | ||
| 559 | #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) | ||
| 560 | u8 l2_802_3_offload_ctrl; | ||
| 561 | |||
| 562 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) | ||
| 563 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1) | ||
| 564 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1) | ||
| 565 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1) | ||
| 566 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1) | ||
| 567 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2) | ||
| 568 | u8 l2_nwifi_offload_ctrl; | ||
| 569 | |||
| 570 | u8 vlan_id; | ||
| 571 | u8 nwifi_ds_trans_type; | ||
| 572 | |||
| 573 | #define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0) | ||
| 574 | #define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1) | ||
| 575 | #define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1) | ||
| 576 | #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1) | ||
| 577 | #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1) | ||
| 578 | #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2) | ||
| 579 | u8 l3_l4_ctrl; | ||
| 580 | |||
| 581 | #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0) | ||
| 582 | #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1) | ||
| 583 | #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1) | ||
| 584 | #define RING_CTRL_OVERRIDE_WB_THRSH_POS (1) | ||
| 585 | #define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1) | ||
| 586 | #define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2) | ||
| 587 | #define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2) | ||
| 588 | #define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1) | ||
| 589 | #define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4) | ||
| 590 | #define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3) | ||
| 591 | #define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1) | ||
| 592 | #define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8) | ||
| 593 | u8 ring_ctrl; | ||
| 594 | |||
| 595 | __le16 prefetch_thrsh; | ||
| 596 | __le16 wb_thrsh; | ||
| 597 | __le32 itr_value; | ||
| 598 | __le16 host_thrsh; | ||
| 599 | u8 reserved[2]; | ||
| 600 | struct wmi_sniffer_cfg sniffer_cfg; | ||
| 601 | } __packed; | ||
| 602 | |||
| 603 | /* | ||
| 604 | * WMI_RCP_ADDBA_RESP_CMDID | ||
| 605 | */ | ||
| 606 | struct wmi_rcp_addba_resp_cmd { | ||
| 607 | |||
| 608 | #define CIDXTID_CID_POS (0) | ||
| 609 | #define CIDXTID_CID_LEN (4) | ||
| 610 | #define CIDXTID_CID_MSK (0xF) | ||
| 611 | #define CIDXTID_TID_POS (4) | ||
| 612 | #define CIDXTID_TID_LEN (4) | ||
| 613 | #define CIDXTID_TID_MSK (0xF0) | ||
| 614 | u8 cidxtid; | ||
| 615 | |||
| 616 | u8 dialog_token; | ||
| 617 | __le16 status_code; | ||
| 618 | __le16 ba_param_set; /* ieee80211_ba_parameterset field to send */ | ||
| 619 | __le16 ba_timeout; | ||
| 620 | } __packed; | ||
| 621 | |||
| 622 | /* | ||
| 623 | * WMI_RCP_DELBA_CMDID | ||
| 624 | */ | ||
| 625 | struct wmi_rcp_delba_cmd { | ||
| 626 | |||
| 627 | #define CIDXTID_CID_POS (0) | ||
| 628 | #define CIDXTID_CID_LEN (4) | ||
| 629 | #define CIDXTID_CID_MSK (0xF) | ||
| 630 | #define CIDXTID_TID_POS (4) | ||
| 631 | #define CIDXTID_TID_LEN (4) | ||
| 632 | #define CIDXTID_TID_MSK (0xF0) | ||
| 633 | u8 cidxtid; | ||
| 634 | |||
| 635 | u8 reserved; | ||
| 636 | __le16 reason; | ||
| 637 | } __packed; | ||
| 638 | |||
| 639 | /* | ||
| 640 | * WMI_RCP_ADDBA_REQ_CMDID | ||
| 641 | */ | ||
| 642 | struct wmi_rcp_addba_req_cmd { | ||
| 643 | |||
| 644 | #define CIDXTID_CID_POS (0) | ||
| 645 | #define CIDXTID_CID_LEN (4) | ||
| 646 | #define CIDXTID_CID_MSK (0xF) | ||
| 647 | #define CIDXTID_TID_POS (4) | ||
| 648 | #define CIDXTID_TID_LEN (4) | ||
| 649 | #define CIDXTID_TID_MSK (0xF0) | ||
| 650 | u8 cidxtid; | ||
| 651 | |||
| 652 | u8 dialog_token; | ||
| 653 | /* ieee80211_ba_parameterset field as it received */ | ||
| 654 | __le16 ba_param_set; | ||
| 655 | __le16 ba_timeout; | ||
| 656 | /* ieee80211_ba_seqstrl field as it received */ | ||
| 657 | __le16 ba_seq_ctrl; | ||
| 658 | } __packed; | ||
| 659 | |||
| 660 | /* | ||
| 661 | * WMI_SET_MAC_ADDRESS_CMDID | ||
| 662 | */ | ||
| 663 | struct wmi_set_mac_address_cmd { | ||
| 664 | u8 mac[WMI_MAC_LEN]; | ||
| 665 | u8 reserved[2]; | ||
| 666 | } __packed; | ||
| 667 | |||
| 668 | |||
| 669 | /* | ||
| 670 | * WMI_EAPOL_TX_CMDID | ||
| 671 | */ | ||
| 672 | struct wmi_eapol_tx_cmd { | ||
| 673 | u8 dst_mac[WMI_MAC_LEN]; | ||
| 674 | __le16 eapol_len; | ||
| 675 | u8 eapol[0]; | ||
| 676 | } __packed; | ||
| 677 | |||
| 678 | /* | ||
| 679 | * WMI_ECHO_CMDID | ||
| 680 | * | ||
| 681 | * Check FW is alive | ||
| 682 | * | ||
| 683 | * WMI_DEEP_ECHO_CMDID | ||
| 684 | * | ||
| 685 | * Check FW and ucode are alive | ||
| 686 | * | ||
| 687 | * Returned event: WMI_ECHO_RSP_EVENTID | ||
| 688 | * same event for both commands | ||
| 689 | */ | ||
| 690 | struct wmi_echo_cmd { | ||
| 691 | __le32 value; | ||
| 692 | } __packed; | ||
| 693 | |||
| 694 | /* | ||
| 695 | * WMI Events | ||
| 696 | */ | ||
| 697 | |||
| 698 | /* | ||
| 699 | * List of Events (target to host) | ||
| 700 | */ | ||
| 701 | enum wmi_event_id { | ||
| 702 | WMI_IMM_RSP_EVENTID = 0x0000, | ||
| 703 | WMI_READY_EVENTID = 0x1001, | ||
| 704 | WMI_CONNECT_EVENTID = 0x1002, | ||
| 705 | WMI_DISCONNECT_EVENTID = 0x1003, | ||
| 706 | WMI_SCAN_COMPLETE_EVENTID = 0x100a, | ||
| 707 | WMI_REPORT_STATISTICS_EVENTID = 0x100b, | ||
| 708 | WMI_RD_MEM_RSP_EVENTID = 0x1800, | ||
| 709 | WMI_FW_READY_EVENTID = 0x1801, | ||
| 710 | WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200, | ||
| 711 | WMI_ECHO_RSP_EVENTID = 0x1803, | ||
| 712 | WMI_CONFIG_MAC_DONE_EVENTID = 0x1805, | ||
| 713 | WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806, | ||
| 714 | WMI_ADD_STATION_DONE_EVENTID = 0x1807, | ||
| 715 | WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808, | ||
| 716 | WMI_PHY_GET_STATISTICS_EVENTID = 0x1809, | ||
| 717 | WMI_FS_TUNE_DONE_EVENTID = 0x180a, | ||
| 718 | WMI_CORR_MEASURE_DONE_EVENTID = 0x180b, | ||
| 719 | WMI_TEMP_SENSE_DONE_EVENTID = 0x180e, | ||
| 720 | WMI_DC_CALIB_DONE_EVENTID = 0x180f, | ||
| 721 | WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, | ||
| 722 | WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, | ||
| 723 | WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, | ||
| 724 | WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, | ||
| 725 | WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817, | ||
| 726 | WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, | ||
| 727 | WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, | ||
| 728 | WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a, | ||
| 729 | WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181d, | ||
| 730 | |||
| 731 | WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, | ||
| 732 | WMI_VRING_CFG_DONE_EVENTID = 0x1821, | ||
| 733 | WMI_RX_ON_DONE_EVENTID = 0x1822, | ||
| 734 | WMI_BA_STATUS_EVENTID = 0x1823, | ||
| 735 | WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, | ||
| 736 | WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, | ||
| 737 | WMI_DELBA_EVENTID = 0x1826, | ||
| 738 | WMI_GET_SSID_EVENTID = 0x1828, | ||
| 739 | WMI_GET_PCP_CHANNEL_EVENTID = 0x182a, | ||
| 740 | WMI_SW_TX_COMPLETE_EVENTID = 0x182b, | ||
| 741 | WMI_RX_OFF_DONE_EVENTID = 0x182c, | ||
| 742 | |||
| 743 | WMI_READ_MAC_RXQ_EVENTID = 0x1830, | ||
| 744 | WMI_READ_MAC_TXQ_EVENTID = 0x1831, | ||
| 745 | WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, | ||
| 746 | WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, | ||
| 747 | WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, | ||
| 748 | |||
| 749 | WMI_BEAFORMING_MGMT_DONE_EVENTID = 0x1836, | ||
| 750 | WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, | ||
| 751 | WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, | ||
| 752 | WMI_RS_MGMT_DONE_EVENTID = 0x1852, | ||
| 753 | WMI_RF_MGMT_STATUS_EVENTID = 0x1853, | ||
| 754 | WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, | ||
| 755 | WMI_RX_MGMT_PACKET_EVENTID = 0x1840, | ||
| 756 | |||
| 757 | /* Performance monitoring events */ | ||
| 758 | WMI_DATA_PORT_OPEN_EVENTID = 0x1860, | ||
| 759 | WMI_WBE_LINKDOWN_EVENTID = 0x1861, | ||
| 760 | |||
| 761 | WMI_BF_CTRL_DONE_EVENTID = 0x1862, | ||
| 762 | WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, | ||
| 763 | WMI_GET_STATUS_DONE_EVENTID = 0x1864, | ||
| 764 | |||
| 765 | WMI_UNIT_TEST_EVENTID = 0x1900, | ||
| 766 | WMI_FLASH_READ_DONE_EVENTID = 0x1902, | ||
| 767 | WMI_FLASH_WRITE_DONE_EVENTID = 0x1903, | ||
| 768 | |||
| 769 | WMI_SET_CHANNEL_EVENTID = 0x9000, | ||
| 770 | WMI_ASSOC_REQ_EVENTID = 0x9001, | ||
| 771 | WMI_EAPOL_RX_EVENTID = 0x9002, | ||
| 772 | WMI_MAC_ADDR_RESP_EVENTID = 0x9003, | ||
| 773 | WMI_FW_VER_EVENTID = 0x9004, | ||
| 774 | }; | ||
| 775 | |||
| 776 | /* | ||
| 777 | * Events data structures | ||
| 778 | */ | ||
| 779 | |||
| 780 | /* | ||
| 781 | * WMI_RF_MGMT_STATUS_EVENTID | ||
| 782 | */ | ||
| 783 | enum wmi_rf_status { | ||
| 784 | WMI_RF_ENABLED = 0, | ||
| 785 | WMI_RF_DISABLED_HW = 1, | ||
| 786 | WMI_RF_DISABLED_SW = 2, | ||
| 787 | WMI_RF_DISABLED_HW_SW = 3, | ||
| 788 | }; | ||
| 789 | |||
| 790 | struct wmi_rf_mgmt_status_event { | ||
| 791 | __le32 rf_status; | ||
| 792 | } __packed; | ||
| 793 | |||
| 794 | /* | ||
| 795 | * WMI_GET_STATUS_DONE_EVENTID | ||
| 796 | */ | ||
| 797 | struct wmi_get_status_done_event { | ||
| 798 | __le32 is_associated; | ||
| 799 | u8 cid; | ||
| 800 | u8 reserved0[3]; | ||
| 801 | u8 bssid[WMI_MAC_LEN]; | ||
| 802 | u8 channel; | ||
| 803 | u8 reserved1; | ||
| 804 | u8 network_type; | ||
| 805 | u8 reserved2[3]; | ||
| 806 | __le32 ssid_len; | ||
| 807 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
| 808 | __le32 rf_status; | ||
| 809 | __le32 is_secured; | ||
| 810 | } __packed; | ||
| 811 | |||
| 812 | /* | ||
| 813 | * WMI_FW_VER_EVENTID | ||
| 814 | */ | ||
| 815 | struct wmi_fw_ver_event { | ||
| 816 | u8 major; | ||
| 817 | u8 minor; | ||
| 818 | __le16 subminor; | ||
| 819 | __le16 build; | ||
| 820 | } __packed; | ||
| 821 | |||
| 822 | /* | ||
| 823 | * WMI_MAC_ADDR_RESP_EVENTID | ||
| 824 | */ | ||
| 825 | struct wmi_mac_addr_resp_event { | ||
| 826 | u8 mac[WMI_MAC_LEN]; | ||
| 827 | u8 auth_mode; | ||
| 828 | u8 crypt_mode; | ||
| 829 | __le32 offload_mode; | ||
| 830 | } __packed; | ||
| 831 | |||
| 832 | /* | ||
| 833 | * WMI_EAPOL_RX_EVENTID | ||
| 834 | */ | ||
| 835 | struct wmi_eapol_rx_event { | ||
| 836 | u8 src_mac[WMI_MAC_LEN]; | ||
| 837 | __le16 eapol_len; | ||
| 838 | u8 eapol[0]; | ||
| 839 | } __packed; | ||
| 840 | |||
| 841 | /* | ||
| 842 | * WMI_READY_EVENTID | ||
| 843 | */ | ||
| 844 | enum wmi_phy_capability { | ||
| 845 | WMI_11A_CAPABILITY = 1, | ||
| 846 | WMI_11G_CAPABILITY = 2, | ||
| 847 | WMI_11AG_CAPABILITY = 3, | ||
| 848 | WMI_11NA_CAPABILITY = 4, | ||
| 849 | WMI_11NG_CAPABILITY = 5, | ||
| 850 | WMI_11NAG_CAPABILITY = 6, | ||
| 851 | WMI_11AD_CAPABILITY = 7, | ||
| 852 | WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY, | ||
| 853 | }; | ||
| 854 | |||
| 855 | struct wmi_ready_event { | ||
| 856 | __le32 sw_version; | ||
| 857 | __le32 abi_version; | ||
| 858 | u8 mac[WMI_MAC_LEN]; | ||
| 859 | u8 phy_capability; /* enum wmi_phy_capability */ | ||
| 860 | u8 reserved; | ||
| 861 | } __packed; | ||
| 862 | |||
| 863 | /* | ||
| 864 | * WMI_NOTIFY_REQ_DONE_EVENTID | ||
| 865 | */ | ||
| 866 | struct wmi_notify_req_done_event { | ||
| 867 | __le32 status; | ||
| 868 | __le64 tsf; | ||
| 869 | __le32 snr_val; | ||
| 870 | __le32 tx_tpt; | ||
| 871 | __le32 tx_goodput; | ||
| 872 | __le32 rx_goodput; | ||
| 873 | __le16 bf_mcs; | ||
| 874 | __le16 my_rx_sector; | ||
| 875 | __le16 my_tx_sector; | ||
| 876 | __le16 other_rx_sector; | ||
| 877 | __le16 other_tx_sector; | ||
| 878 | __le16 range; | ||
| 879 | } __packed; | ||
| 880 | |||
| 881 | /* | ||
| 882 | * WMI_CONNECT_EVENTID | ||
| 883 | */ | ||
| 884 | struct wmi_connect_event { | ||
| 885 | u8 channel; | ||
| 886 | u8 reserved0; | ||
| 887 | u8 bssid[WMI_MAC_LEN]; | ||
| 888 | __le16 listen_interval; | ||
| 889 | __le16 beacon_interval; | ||
| 890 | u8 network_type; | ||
| 891 | u8 reserved1[3]; | ||
| 892 | u8 beacon_ie_len; | ||
| 893 | u8 assoc_req_len; | ||
| 894 | u8 assoc_resp_len; | ||
| 895 | u8 cid; | ||
| 896 | u8 reserved2[3]; | ||
| 897 | u8 assoc_info[0]; | ||
| 898 | } __packed; | ||
| 899 | |||
| 900 | /* | ||
| 901 | * WMI_DISCONNECT_EVENTID | ||
| 902 | */ | ||
| 903 | enum wmi_disconnect_reason { | ||
| 904 | WMI_DIS_REASON_NO_NETWORK_AVAIL = 1, | ||
| 905 | WMI_DIS_REASON_LOST_LINK = 2, /* bmiss */ | ||
| 906 | WMI_DIS_REASON_DISCONNECT_CMD = 3, | ||
| 907 | WMI_DIS_REASON_BSS_DISCONNECTED = 4, | ||
| 908 | WMI_DIS_REASON_AUTH_FAILED = 5, | ||
| 909 | WMI_DIS_REASON_ASSOC_FAILED = 6, | ||
| 910 | WMI_DIS_REASON_NO_RESOURCES_AVAIL = 7, | ||
| 911 | WMI_DIS_REASON_CSERV_DISCONNECT = 8, | ||
| 912 | WMI_DIS_REASON_INVALID_PROFILE = 10, | ||
| 913 | WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH = 11, | ||
| 914 | WMI_DIS_REASON_PROFILE_MISMATCH = 12, | ||
| 915 | WMI_DIS_REASON_CONNECTION_EVICTED = 13, | ||
| 916 | WMI_DIS_REASON_IBSS_MERGE = 14, | ||
| 917 | }; | ||
| 918 | |||
| 919 | struct wmi_disconnect_event { | ||
| 920 | __le16 protocol_reason_status; /* reason code, see 802.11 spec. */ | ||
| 921 | u8 bssid[WMI_MAC_LEN]; /* set if known */ | ||
| 922 | u8 disconnect_reason; /* see wmi_disconnect_reason_e */ | ||
| 923 | u8 assoc_resp_len; | ||
| 924 | u8 assoc_info[0]; | ||
| 925 | } __packed; | ||
| 926 | |||
| 927 | /* | ||
| 928 | * WMI_SCAN_COMPLETE_EVENTID | ||
| 929 | */ | ||
| 930 | struct wmi_scan_complete_event { | ||
| 931 | __le32 status; | ||
| 932 | } __packed; | ||
| 933 | |||
| 934 | /* | ||
| 935 | * WMI_BA_STATUS_EVENTID | ||
| 936 | */ | ||
| 937 | enum wmi_vring_ba_status { | ||
| 938 | WMI_BA_AGREED = 0, | ||
| 939 | WMI_BA_NON_AGREED = 1, | ||
| 940 | }; | ||
| 941 | |||
| 942 | struct wmi_vring_ba_status_event { | ||
| 943 | __le16 status; | ||
| 944 | u8 reserved[2]; | ||
| 945 | u8 ringid; | ||
| 946 | u8 agg_wsize; | ||
| 947 | __le16 ba_timeout; | ||
| 948 | } __packed; | ||
| 949 | |||
| 950 | /* | ||
| 951 | * WMI_DELBA_EVENTID | ||
| 952 | */ | ||
| 953 | struct wmi_delba_event { | ||
| 954 | |||
| 955 | #define CIDXTID_CID_POS (0) | ||
| 956 | #define CIDXTID_CID_LEN (4) | ||
| 957 | #define CIDXTID_CID_MSK (0xF) | ||
| 958 | #define CIDXTID_TID_POS (4) | ||
| 959 | #define CIDXTID_TID_LEN (4) | ||
| 960 | #define CIDXTID_TID_MSK (0xF0) | ||
| 961 | u8 cidxtid; | ||
| 962 | |||
| 963 | u8 from_initiator; | ||
| 964 | __le16 reason; | ||
| 965 | } __packed; | ||
| 966 | |||
| 967 | /* | ||
| 968 | * WMI_VRING_CFG_DONE_EVENTID | ||
| 969 | */ | ||
| 970 | enum wmi_vring_cfg_done_event_status { | ||
| 971 | WMI_VRING_CFG_SUCCESS = 0, | ||
| 972 | WMI_VRING_CFG_FAILURE = 1, | ||
| 973 | }; | ||
| 974 | |||
| 975 | struct wmi_vring_cfg_done_event { | ||
| 976 | u8 ringid; | ||
| 977 | u8 status; | ||
| 978 | u8 reserved[2]; | ||
| 979 | __le32 tx_vring_tail_ptr; | ||
| 980 | } __packed; | ||
| 981 | |||
| 982 | /* | ||
| 983 | * WMI_ADDBA_RESP_SENT_EVENTID | ||
| 984 | */ | ||
| 985 | enum wmi_rcp_addba_resp_sent_event_status { | ||
| 986 | WMI_ADDBA_SUCCESS = 0, | ||
| 987 | WMI_ADDBA_FAIL = 1, | ||
| 988 | }; | ||
| 989 | |||
| 990 | struct wmi_rcp_addba_resp_sent_event { | ||
| 991 | |||
| 992 | #define CIDXTID_CID_POS (0) | ||
| 993 | #define CIDXTID_CID_LEN (4) | ||
| 994 | #define CIDXTID_CID_MSK (0xF) | ||
| 995 | #define CIDXTID_TID_POS (4) | ||
| 996 | #define CIDXTID_TID_LEN (4) | ||
| 997 | #define CIDXTID_TID_MSK (0xF0) | ||
| 998 | u8 cidxtid; | ||
| 999 | |||
| 1000 | u8 reserved; | ||
| 1001 | __le16 status; | ||
| 1002 | } __packed; | ||
| 1003 | |||
| 1004 | /* | ||
| 1005 | * WMI_RCP_ADDBA_REQ_EVENTID | ||
| 1006 | */ | ||
| 1007 | struct wmi_rcp_addba_req_event { | ||
| 1008 | |||
| 1009 | #define CIDXTID_CID_POS (0) | ||
| 1010 | #define CIDXTID_CID_LEN (4) | ||
| 1011 | #define CIDXTID_CID_MSK (0xF) | ||
| 1012 | #define CIDXTID_TID_POS (4) | ||
| 1013 | #define CIDXTID_TID_LEN (4) | ||
| 1014 | #define CIDXTID_TID_MSK (0xF0) | ||
| 1015 | u8 cidxtid; | ||
| 1016 | |||
| 1017 | u8 dialog_token; | ||
| 1018 | __le16 ba_param_set; /* ieee80211_ba_parameterset as it received */ | ||
| 1019 | __le16 ba_timeout; | ||
| 1020 | __le16 ba_seq_ctrl; /* ieee80211_ba_seqstrl field as it received */ | ||
| 1021 | } __packed; | ||
| 1022 | |||
| 1023 | /* | ||
| 1024 | * WMI_CFG_RX_CHAIN_DONE_EVENTID | ||
| 1025 | */ | ||
| 1026 | enum wmi_cfg_rx_chain_done_event_status { | ||
| 1027 | WMI_CFG_RX_CHAIN_SUCCESS = 1, | ||
| 1028 | }; | ||
| 1029 | |||
| 1030 | struct wmi_cfg_rx_chain_done_event { | ||
| 1031 | __le32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */ | ||
| 1032 | __le32 status; | ||
| 1033 | } __packed; | ||
| 1034 | |||
| 1035 | /* | ||
| 1036 | * WMI_WBE_LINKDOWN_EVENTID | ||
| 1037 | */ | ||
| 1038 | enum wmi_wbe_link_down_event_reason { | ||
| 1039 | WMI_WBE_REASON_USER_REQUEST = 0, | ||
| 1040 | WMI_WBE_REASON_RX_DISASSOC = 1, | ||
| 1041 | WMI_WBE_REASON_BAD_PHY_LINK = 2, | ||
| 1042 | }; | ||
| 1043 | |||
| 1044 | struct wmi_wbe_link_down_event { | ||
| 1045 | u8 cid; | ||
| 1046 | u8 reserved[3]; | ||
| 1047 | __le32 reason; | ||
| 1048 | } __packed; | ||
| 1049 | |||
| 1050 | /* | ||
| 1051 | * WMI_DATA_PORT_OPEN_EVENTID | ||
| 1052 | */ | ||
| 1053 | struct wmi_data_port_open_event { | ||
| 1054 | u8 cid; | ||
| 1055 | u8 reserved[3]; | ||
| 1056 | } __packed; | ||
| 1057 | |||
| 1058 | /* | ||
| 1059 | * WMI_GET_PCP_CHANNEL_EVENTID | ||
| 1060 | */ | ||
| 1061 | struct wmi_get_pcp_channel_event { | ||
| 1062 | u8 channel; | ||
| 1063 | u8 reserved[3]; | ||
| 1064 | } __packed; | ||
| 1065 | |||
| 1066 | /* | ||
| 1067 | * WMI_SW_TX_COMPLETE_EVENTID | ||
| 1068 | */ | ||
| 1069 | enum wmi_sw_tx_status { | ||
| 1070 | WMI_TX_SW_STATUS_SUCCESS = 0, | ||
| 1071 | WMI_TX_SW_STATUS_FAILED_NO_RESOURCES = 1, | ||
| 1072 | WMI_TX_SW_STATUS_FAILED_TX = 2, | ||
| 1073 | }; | ||
| 1074 | |||
| 1075 | struct wmi_sw_tx_complete_event { | ||
| 1076 | u8 status; /* enum wmi_sw_tx_status */ | ||
| 1077 | u8 reserved[3]; | ||
| 1078 | } __packed; | ||
| 1079 | |||
| 1080 | /* | ||
| 1081 | * WMI_GET_SSID_EVENTID | ||
| 1082 | */ | ||
| 1083 | struct wmi_get_ssid_event { | ||
| 1084 | __le32 ssid_len; | ||
| 1085 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
| 1086 | } __packed; | ||
| 1087 | |||
| 1088 | /* | ||
| 1089 | * WMI_RX_MGMT_PACKET_EVENTID | ||
| 1090 | */ | ||
| 1091 | struct wmi_rx_mgmt_info { | ||
| 1092 | u8 mcs; | ||
| 1093 | s8 snr; | ||
| 1094 | __le16 range; | ||
| 1095 | __le16 stype; | ||
| 1096 | __le16 status; | ||
| 1097 | __le32 len; | ||
| 1098 | u8 qid; | ||
| 1099 | u8 mid; | ||
| 1100 | u8 cid; | ||
| 1101 | u8 channel; /* From Radio MNGR */ | ||
| 1102 | } __packed; | ||
| 1103 | |||
| 1104 | struct wmi_rx_mgmt_packet_event { | ||
| 1105 | struct wmi_rx_mgmt_info info; | ||
| 1106 | u8 payload[0]; | ||
| 1107 | } __packed; | ||
| 1108 | |||
| 1109 | /* | ||
| 1110 | * WMI_ECHO_RSP_EVENTID | ||
| 1111 | */ | ||
| 1112 | struct wmi_echo_event { | ||
| 1113 | __le32 echoed_value; | ||
| 1114 | } __packed; | ||
| 1115 | |||
| 1116 | #endif /* __WILOCITY_WMI_H__ */ | ||
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index b298e5d68be2..10e288d470e7 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <linux/hw_random.h> | 7 | #include <linux/hw_random.h> |
| 8 | #include <linux/bcma/bcma.h> | 8 | #include <linux/bcma/bcma.h> |
| 9 | #include <linux/ssb/ssb.h> | 9 | #include <linux/ssb/ssb.h> |
| 10 | #include <linux/completion.h> | ||
| 10 | #include <net/mac80211.h> | 11 | #include <net/mac80211.h> |
| 11 | 12 | ||
| 12 | #include "debugfs.h" | 13 | #include "debugfs.h" |
| @@ -722,6 +723,10 @@ enum b43_firmware_file_type { | |||
| 722 | struct b43_request_fw_context { | 723 | struct b43_request_fw_context { |
| 723 | /* The device we are requesting the fw for. */ | 724 | /* The device we are requesting the fw for. */ |
| 724 | struct b43_wldev *dev; | 725 | struct b43_wldev *dev; |
| 726 | /* a completion event structure needed if this call is asynchronous */ | ||
| 727 | struct completion fw_load_complete; | ||
| 728 | /* a pointer to the firmware object */ | ||
| 729 | const struct firmware *blob; | ||
| 725 | /* The type of firmware to request. */ | 730 | /* The type of firmware to request. */ |
| 726 | enum b43_firmware_file_type req_type; | 731 | enum b43_firmware_file_type req_type; |
| 727 | /* Error messages for each firmware type. */ | 732 | /* Error messages for each firmware type. */ |
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 16ab280359bd..806e34c19281 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c | |||
| @@ -2088,11 +2088,18 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error) | |||
| 2088 | b43warn(wl, text); | 2088 | b43warn(wl, text); |
| 2089 | } | 2089 | } |
| 2090 | 2090 | ||
| 2091 | static void b43_fw_cb(const struct firmware *firmware, void *context) | ||
| 2092 | { | ||
| 2093 | struct b43_request_fw_context *ctx = context; | ||
| 2094 | |||
| 2095 | ctx->blob = firmware; | ||
| 2096 | complete(&ctx->fw_load_complete); | ||
| 2097 | } | ||
| 2098 | |||
| 2091 | int b43_do_request_fw(struct b43_request_fw_context *ctx, | 2099 | int b43_do_request_fw(struct b43_request_fw_context *ctx, |
| 2092 | const char *name, | 2100 | const char *name, |
| 2093 | struct b43_firmware_file *fw) | 2101 | struct b43_firmware_file *fw, bool async) |
| 2094 | { | 2102 | { |
| 2095 | const struct firmware *blob; | ||
| 2096 | struct b43_fw_header *hdr; | 2103 | struct b43_fw_header *hdr; |
| 2097 | u32 size; | 2104 | u32 size; |
| 2098 | int err; | 2105 | int err; |
| @@ -2131,11 +2138,31 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, | |||
| 2131 | B43_WARN_ON(1); | 2138 | B43_WARN_ON(1); |
| 2132 | return -ENOSYS; | 2139 | return -ENOSYS; |
| 2133 | } | 2140 | } |
| 2134 | err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev); | 2141 | if (async) { |
| 2142 | /* do this part asynchronously */ | ||
| 2143 | init_completion(&ctx->fw_load_complete); | ||
| 2144 | err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname, | ||
| 2145 | ctx->dev->dev->dev, GFP_KERNEL, | ||
| 2146 | ctx, b43_fw_cb); | ||
| 2147 | if (err < 0) { | ||
| 2148 | pr_err("Unable to load firmware\n"); | ||
| 2149 | return err; | ||
| 2150 | } | ||
| 2151 | /* stall here until fw ready */ | ||
| 2152 | wait_for_completion(&ctx->fw_load_complete); | ||
| 2153 | if (ctx->blob) | ||
| 2154 | goto fw_ready; | ||
| 2155 | /* On some ARM systems, the async request will fail, but the next sync | ||
| 2156 | * request works. For this reason, we dall through here | ||
| 2157 | */ | ||
| 2158 | } | ||
| 2159 | err = request_firmware(&ctx->blob, ctx->fwname, | ||
| 2160 | ctx->dev->dev->dev); | ||
| 2135 | if (err == -ENOENT) { | 2161 | if (err == -ENOENT) { |
| 2136 | snprintf(ctx->errors[ctx->req_type], | 2162 | snprintf(ctx->errors[ctx->req_type], |
| 2137 | sizeof(ctx->errors[ctx->req_type]), | 2163 | sizeof(ctx->errors[ctx->req_type]), |
| 2138 | "Firmware file \"%s\" not found\n", ctx->fwname); | 2164 | "Firmware file \"%s\" not found\n", |
| 2165 | ctx->fwname); | ||
| 2139 | return err; | 2166 | return err; |
| 2140 | } else if (err) { | 2167 | } else if (err) { |
| 2141 | snprintf(ctx->errors[ctx->req_type], | 2168 | snprintf(ctx->errors[ctx->req_type], |
| @@ -2144,14 +2171,15 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, | |||
| 2144 | ctx->fwname, err); | 2171 | ctx->fwname, err); |
| 2145 | return err; | 2172 | return err; |
| 2146 | } | 2173 | } |
| 2147 | if (blob->size < sizeof(struct b43_fw_header)) | 2174 | fw_ready: |
| 2175 | if (ctx->blob->size < sizeof(struct b43_fw_header)) | ||
| 2148 | goto err_format; | 2176 | goto err_format; |
| 2149 | hdr = (struct b43_fw_header *)(blob->data); | 2177 | hdr = (struct b43_fw_header *)(ctx->blob->data); |
| 2150 | switch (hdr->type) { | 2178 | switch (hdr->type) { |
| 2151 | case B43_FW_TYPE_UCODE: | 2179 | case B43_FW_TYPE_UCODE: |
| 2152 | case B43_FW_TYPE_PCM: | 2180 | case B43_FW_TYPE_PCM: |
| 2153 | size = be32_to_cpu(hdr->size); | 2181 | size = be32_to_cpu(hdr->size); |
| 2154 | if (size != blob->size - sizeof(struct b43_fw_header)) | 2182 | if (size != ctx->blob->size - sizeof(struct b43_fw_header)) |
| 2155 | goto err_format; | 2183 | goto err_format; |
| 2156 | /* fallthrough */ | 2184 | /* fallthrough */ |
| 2157 | case B43_FW_TYPE_IV: | 2185 | case B43_FW_TYPE_IV: |
| @@ -2162,7 +2190,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, | |||
| 2162 | goto err_format; | 2190 | goto err_format; |
| 2163 | } | 2191 | } |
| 2164 | 2192 | ||
| 2165 | fw->data = blob; | 2193 | fw->data = ctx->blob; |
| 2166 | fw->filename = name; | 2194 | fw->filename = name; |
| 2167 | fw->type = ctx->req_type; | 2195 | fw->type = ctx->req_type; |
| 2168 | 2196 | ||
| @@ -2172,7 +2200,7 @@ err_format: | |||
| 2172 | snprintf(ctx->errors[ctx->req_type], | 2200 | snprintf(ctx->errors[ctx->req_type], |
| 2173 | sizeof(ctx->errors[ctx->req_type]), | 2201 | sizeof(ctx->errors[ctx->req_type]), |
| 2174 | "Firmware file \"%s\" format error.\n", ctx->fwname); | 2202 | "Firmware file \"%s\" format error.\n", ctx->fwname); |
| 2175 | release_firmware(blob); | 2203 | release_firmware(ctx->blob); |
| 2176 | 2204 | ||
| 2177 | return -EPROTO; | 2205 | return -EPROTO; |
| 2178 | } | 2206 | } |
| @@ -2223,7 +2251,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) | |||
| 2223 | goto err_no_ucode; | 2251 | goto err_no_ucode; |
| 2224 | } | 2252 | } |
| 2225 | } | 2253 | } |
| 2226 | err = b43_do_request_fw(ctx, filename, &fw->ucode); | 2254 | err = b43_do_request_fw(ctx, filename, &fw->ucode, true); |
| 2227 | if (err) | 2255 | if (err) |
| 2228 | goto err_load; | 2256 | goto err_load; |
| 2229 | 2257 | ||
| @@ -2235,7 +2263,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) | |||
| 2235 | else | 2263 | else |
| 2236 | goto err_no_pcm; | 2264 | goto err_no_pcm; |
| 2237 | fw->pcm_request_failed = false; | 2265 | fw->pcm_request_failed = false; |
| 2238 | err = b43_do_request_fw(ctx, filename, &fw->pcm); | 2266 | err = b43_do_request_fw(ctx, filename, &fw->pcm, false); |
| 2239 | if (err == -ENOENT) { | 2267 | if (err == -ENOENT) { |
| 2240 | /* We did not find a PCM file? Not fatal, but | 2268 | /* We did not find a PCM file? Not fatal, but |
| 2241 | * core rev <= 10 must do without hwcrypto then. */ | 2269 | * core rev <= 10 must do without hwcrypto then. */ |
| @@ -2296,7 +2324,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) | |||
| 2296 | default: | 2324 | default: |
| 2297 | goto err_no_initvals; | 2325 | goto err_no_initvals; |
| 2298 | } | 2326 | } |
| 2299 | err = b43_do_request_fw(ctx, filename, &fw->initvals); | 2327 | err = b43_do_request_fw(ctx, filename, &fw->initvals, false); |
| 2300 | if (err) | 2328 | if (err) |
| 2301 | goto err_load; | 2329 | goto err_load; |
| 2302 | 2330 | ||
| @@ -2355,7 +2383,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) | |||
| 2355 | default: | 2383 | default: |
| 2356 | goto err_no_initvals; | 2384 | goto err_no_initvals; |
| 2357 | } | 2385 | } |
| 2358 | err = b43_do_request_fw(ctx, filename, &fw->initvals_band); | 2386 | err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); |
| 2359 | if (err) | 2387 | if (err) |
| 2360 | goto err_load; | 2388 | goto err_load; |
| 2361 | 2389 | ||
diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index 8c684cd33529..abac25ee958d 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h | |||
| @@ -137,9 +137,8 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); | |||
| 137 | 137 | ||
| 138 | 138 | ||
| 139 | struct b43_request_fw_context; | 139 | struct b43_request_fw_context; |
| 140 | int b43_do_request_fw(struct b43_request_fw_context *ctx, | 140 | int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, |
| 141 | const char *name, | 141 | struct b43_firmware_file *fw, bool async); |
| 142 | struct b43_firmware_file *fw); | ||
| 143 | void b43_do_release_fw(struct b43_firmware_file *fw); | 142 | void b43_do_release_fw(struct b43_firmware_file *fw); |
| 144 | 143 | ||
| 145 | #endif /* B43_MAIN_H_ */ | 144 | #endif /* B43_MAIN_H_ */ |
diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index d604b4036a76..3726cd6fcd75 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c | |||
| @@ -3273,7 +3273,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr, | |||
| 3273 | 3273 | ||
| 3274 | if (count) { | 3274 | if (count) { |
| 3275 | char *p = buffer; | 3275 | char *p = buffer; |
| 3276 | strncpy(buffer, buf, min(sizeof(buffer), count)); | 3276 | strlcpy(buffer, buf, sizeof(buffer)); |
| 3277 | channel = simple_strtoul(p, NULL, 0); | 3277 | channel = simple_strtoul(p, NULL, 0); |
| 3278 | if (channel) | 3278 | if (channel) |
| 3279 | params.channel = channel; | 3279 | params.channel = channel; |
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index da21328ca8ed..a790599fe2c2 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c | |||
| @@ -1151,13 +1151,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, | |||
| 1151 | next_reclaimed = ssn; | 1151 | next_reclaimed = ssn; |
| 1152 | } | 1152 | } |
| 1153 | 1153 | ||
| 1154 | if (tid != IWL_TID_NON_QOS) { | ||
| 1155 | priv->tid_data[sta_id][tid].next_reclaimed = | ||
| 1156 | next_reclaimed; | ||
| 1157 | IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", | ||
| 1158 | next_reclaimed); | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); | 1154 | iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); |
| 1162 | 1155 | ||
| 1163 | iwlagn_check_ratid_empty(priv, sta_id, tid); | 1156 | iwlagn_check_ratid_empty(priv, sta_id, tid); |
| @@ -1208,11 +1201,28 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, | |||
| 1208 | if (!is_agg) | 1201 | if (!is_agg) |
| 1209 | iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); | 1202 | iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); |
| 1210 | 1203 | ||
| 1204 | /* | ||
| 1205 | * W/A for FW bug - the seq_ctl isn't updated when the | ||
| 1206 | * queues are flushed. Fetch it from the packet itself | ||
| 1207 | */ | ||
| 1208 | if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) { | ||
| 1209 | next_reclaimed = le16_to_cpu(hdr->seq_ctrl); | ||
| 1210 | next_reclaimed = | ||
| 1211 | SEQ_TO_SN(next_reclaimed + 0x10); | ||
| 1212 | } | ||
| 1213 | |||
| 1211 | is_offchannel_skb = | 1214 | is_offchannel_skb = |
| 1212 | (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); | 1215 | (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); |
| 1213 | freed++; | 1216 | freed++; |
| 1214 | } | 1217 | } |
| 1215 | 1218 | ||
| 1219 | if (tid != IWL_TID_NON_QOS) { | ||
| 1220 | priv->tid_data[sta_id][tid].next_reclaimed = | ||
| 1221 | next_reclaimed; | ||
| 1222 | IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", | ||
| 1223 | next_reclaimed); | ||
| 1224 | } | ||
| 1225 | |||
| 1216 | WARN_ON(!is_agg && freed != 1); | 1226 | WARN_ON(!is_agg && freed != 1); |
| 1217 | 1227 | ||
| 1218 | /* | 1228 | /* |
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index dad4c4aad91f..8389cd38338b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c | |||
| @@ -1166,6 +1166,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) | |||
| 1166 | else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && | 1166 | else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && |
| 1167 | !trans_pcie->inta) | 1167 | !trans_pcie->inta) |
| 1168 | iwl_enable_interrupts(trans); | 1168 | iwl_enable_interrupts(trans); |
| 1169 | return IRQ_HANDLED; | ||
| 1169 | 1170 | ||
| 1170 | none: | 1171 | none: |
| 1171 | /* re-enable interrupts here since we don't have anything to service. */ | 1172 | /* re-enable interrupts here since we don't have anything to service. */ |
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a875499f8945..efe525be27dd 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c | |||
| @@ -1709,7 +1709,7 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, | |||
| 1709 | NL80211_CHAN_NO_HT) | 1709 | NL80211_CHAN_NO_HT) |
| 1710 | config_bands |= BAND_GN; | 1710 | config_bands |= BAND_GN; |
| 1711 | } else { | 1711 | } else { |
| 1712 | if (cfg80211_get_chandef_type(¶ms->chandef) != | 1712 | if (cfg80211_get_chandef_type(¶ms->chandef) == |
| 1713 | NL80211_CHAN_NO_HT) | 1713 | NL80211_CHAN_NO_HT) |
| 1714 | config_bands = BAND_A; | 1714 | config_bands = BAND_A; |
| 1715 | else | 1715 | else |
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index cb682561c438..60e88b58039d 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c | |||
| @@ -56,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, | |||
| 56 | */ | 56 | */ |
| 57 | int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) | 57 | int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) |
| 58 | { | 58 | { |
| 59 | bool cancel_flag = false; | ||
| 60 | int status; | 59 | int status; |
| 61 | struct cmd_ctrl_node *cmd_queued; | 60 | struct cmd_ctrl_node *cmd_queued; |
| 62 | 61 | ||
| @@ -70,14 +69,11 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) | |||
| 70 | atomic_inc(&adapter->cmd_pending); | 69 | atomic_inc(&adapter->cmd_pending); |
| 71 | 70 | ||
| 72 | /* Wait for completion */ | 71 | /* Wait for completion */ |
| 73 | wait_event_interruptible(adapter->cmd_wait_q.wait, | 72 | status = wait_event_interruptible(adapter->cmd_wait_q.wait, |
| 74 | *(cmd_queued->condition)); | 73 | *(cmd_queued->condition)); |
| 75 | if (!*(cmd_queued->condition)) | 74 | if (status) { |
| 76 | cancel_flag = true; | 75 | dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status); |
| 77 | 76 | return status; | |
| 78 | if (cancel_flag) { | ||
| 79 | mwifiex_cancel_pending_ioctl(adapter); | ||
| 80 | dev_dbg(adapter->dev, "cmd cancel\n"); | ||
| 81 | } | 77 | } |
| 82 | 78 | ||
| 83 | status = adapter->cmd_wait_q.status; | 79 | status = adapter->cmd_wait_q.status; |
| @@ -496,8 +492,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) | |||
| 496 | return false; | 492 | return false; |
| 497 | } | 493 | } |
| 498 | 494 | ||
| 499 | wait_event_interruptible(adapter->hs_activate_wait_q, | 495 | if (wait_event_interruptible(adapter->hs_activate_wait_q, |
| 500 | adapter->hs_activate_wait_q_woken); | 496 | adapter->hs_activate_wait_q_woken)) { |
| 497 | dev_err(adapter->dev, "hs_activate_wait_q terminated\n"); | ||
| 498 | return false; | ||
| 499 | } | ||
| 501 | 500 | ||
| 502 | return true; | 501 | return true; |
| 503 | } | 502 | } |
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f221b95b90b3..83564d36e801 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c | |||
| @@ -4250,9 +4250,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, | |||
| 4250 | p->amsdu_enabled = 0; | 4250 | p->amsdu_enabled = 0; |
| 4251 | 4251 | ||
| 4252 | rc = mwl8k_post_cmd(hw, &cmd->header); | 4252 | rc = mwl8k_post_cmd(hw, &cmd->header); |
| 4253 | if (!rc) | ||
| 4254 | rc = p->station_id; | ||
| 4253 | kfree(cmd); | 4255 | kfree(cmd); |
| 4254 | 4256 | ||
| 4255 | return rc ? rc : p->station_id; | 4257 | return rc; |
| 4256 | } | 4258 | } |
| 4257 | 4259 | ||
| 4258 | static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, | 4260 | static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, |
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 1d5d3604e3e0..246e5352f2e1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c | |||
| @@ -692,7 +692,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw) | |||
| 692 | if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { | 692 | if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { |
| 693 | rtl92c_phy_sw_chnl_callback(hw); | 693 | rtl92c_phy_sw_chnl_callback(hw); |
| 694 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, | 694 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, |
| 695 | "sw_chnl_inprogress false schdule workitem\n"); | 695 | "sw_chnl_inprogress false schedule workitem\n"); |
| 696 | rtlphy->sw_chnl_inprogress = false; | 696 | rtlphy->sw_chnl_inprogress = false; |
| 697 | } else { | 697 | } else { |
| 698 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, | 698 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, |
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c index 39cc7938eedf..3d8536bb0d2b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c | |||
| @@ -1106,7 +1106,7 @@ u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw) | |||
| 1106 | if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { | 1106 | if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { |
| 1107 | rtl8723ae_phy_sw_chnl_callback(hw); | 1107 | rtl8723ae_phy_sw_chnl_callback(hw); |
| 1108 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, | 1108 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, |
| 1109 | "sw_chnl_inprogress false schdule workitem\n"); | 1109 | "sw_chnl_inprogress false schedule workitem\n"); |
| 1110 | rtlphy->sw_chnl_inprogress = false; | 1110 | rtlphy->sw_chnl_inprogress = false; |
| 1111 | } else { | 1111 | } else { |
| 1112 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, | 1112 | RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, |
