From cf8f3d4deb02a8fdc806c46d4112b69868544697 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Wed, 15 Feb 2023 20:31:36 +0200 Subject: wifi: ath11k: Set ext passive scan flag to adjust passive scan start time Set the WMI_SCAN_FLAG_EXT_PASSIVE_SCAN_START_TIME_ENHANCE flag while sending the scan command. If this flag is enabled when the incoming scan request comes with a strict start time and its duration overlaps with next TBTT, then target adjust the start time accordingly for passive scan. Target supporting this feature will advertise WMI_TLV_SERVICE_PASSIVE_SCAN_START_TIME_ENHANCE. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-01467-QCAHKSWPL_SILICONZ-1 Signed-off-by: Tamizh Chelvam Raja Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221222131720.11368-1-quic_tamizhr@quicinc.com --- drivers/net/wireless/ath/ath11k/wmi.c | 8 ++++++++ drivers/net/wireless/ath/ath11k/wmi.h | 3 +++ 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index b3a7d7bfe17c..7057c7916d5d 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2068,6 +2068,12 @@ void ath11k_wmi_start_scan_init(struct ath11k *ar, WMI_SCAN_EVENT_FOREIGN_CHAN | WMI_SCAN_EVENT_DEQUEUED; arg->scan_flags |= WMI_SCAN_CHAN_STAT_EVENT; + + if (test_bit(WMI_TLV_SERVICE_PASSIVE_SCAN_START_TIME_ENHANCE, + ar->ab->wmi_ab.svc_map)) + arg->scan_ctrl_flags_ext |= + WMI_SCAN_FLAG_EXT_PASSIVE_SCAN_START_TIME_ENHANCE; + arg->num_bssid = 1; /* fill bssid_list[0] with 0xff, otherwise bssid and RA will be @@ -2149,6 +2155,8 @@ ath11k_wmi_copy_scan_event_cntrl_flags(struct wmi_start_scan_cmd *cmd, /* for adaptive scan mode using 3 bits (21 - 23 bits) */ WMI_SCAN_SET_DWELL_MODE(cmd->scan_ctrl_flags, param->adaptive_dwell_time_mode); + + cmd->scan_ctrl_flags_ext = param->scan_ctrl_flags_ext; } int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 0a045af5419b..2273d885f7fa 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2093,6 +2093,7 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_EXT2_MSG = 220, WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT = 246, WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249, + WMI_TLV_SERVICE_PASSIVE_SCAN_START_TIME_ENHANCE = 263, /* The second 128 bits */ WMI_MAX_EXT_SERVICE = 256, @@ -3223,6 +3224,7 @@ struct wmi_start_scan_cmd { #define WMI_SCAN_DWELL_MODE_MASK 0x00E00000 #define WMI_SCAN_DWELL_MODE_SHIFT 21 +#define WMI_SCAN_FLAG_EXT_PASSIVE_SCAN_START_TIME_ENHANCE 0x00000800 enum { WMI_SCAN_DWELL_MODE_DEFAULT = 0, @@ -3270,6 +3272,7 @@ struct scan_req_params { }; u32 scan_events; }; + u32 scan_ctrl_flags_ext; u32 dwell_time_active; u32 dwell_time_active_2g; u32 dwell_time_passive; -- cgit v1.2.3 From 778f83f889e7fca37780d9640fcbd0229ae38eaa Mon Sep 17 00:00:00 2001 From: "Alexey V. Vissarionov" Date: Wed, 15 Feb 2023 20:31:37 +0200 Subject: wifi: ath6kl: minor fix for allocation size Although the "param" pointer occupies more or equal space compared to "*param", the allocation size should use the size of variable itself. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: bdcd81707973cf8a ("Add ath6kl cleaned up driver") Signed-off-by: Alexey V. Vissarionov Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230117110414.GC12547@altlinux.org --- drivers/net/wireless/ath/ath6kl/bmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/bmi.c b/drivers/net/wireless/ath/ath6kl/bmi.c index bde5a10d470c..af98e871199d 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.c +++ b/drivers/net/wireless/ath/ath6kl/bmi.c @@ -246,7 +246,7 @@ int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) return -EACCES; } - size = sizeof(cid) + sizeof(addr) + sizeof(param); + size = sizeof(cid) + sizeof(addr) + sizeof(*param); if (size > ar->bmi.max_cmd_size) { WARN_ON(1); return -EINVAL; -- cgit v1.2.3 From 480c9df5778774117546f6389be1a8dc8cc935db Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 15 Feb 2023 20:31:37 +0200 Subject: wifi: ath12k: Fix spelling mistakes in warning messages and comments There are quite a few spelling mistakes in warning messages and a lot of the comments. Fix these. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092122.265336-1-colin.i.king@gmail.com --- drivers/net/wireless/ath/ath12k/ce.c | 2 +- drivers/net/wireless/ath/ath12k/core.h | 2 +- drivers/net/wireless/ath/ath12k/dp.c | 2 +- drivers/net/wireless/ath/ath12k/dp.h | 6 +++--- drivers/net/wireless/ath/ath12k/dp_mon.c | 10 +++++----- drivers/net/wireless/ath/ath12k/dp_rx.c | 8 ++++---- drivers/net/wireless/ath/ath12k/hal.c | 2 +- drivers/net/wireless/ath/ath12k/hal.h | 12 ++++++------ drivers/net/wireless/ath/ath12k/hal_desc.h | 10 +++++----- drivers/net/wireless/ath/ath12k/rx_desc.h | 2 +- drivers/net/wireless/ath/ath12k/wmi.c | 6 +++--- drivers/net/wireless/ath/ath12k/wmi.h | 4 ++-- 12 files changed, 33 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c index aed6987804bf..be0d669d31fc 100644 --- a/drivers/net/wireless/ath/ath12k/ce.c +++ b/drivers/net/wireless/ath/ath12k/ce.c @@ -946,7 +946,7 @@ int ath12k_ce_alloc_pipes(struct ath12k_base *ab) ret = ath12k_ce_alloc_pipe(ab, i); if (ret) { - /* Free any parial successful allocation */ + /* Free any partial successful allocation */ ath12k_ce_free_pipes(ab); return ret; } diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index a54ae74543c1..dffa687ee40e 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -691,7 +691,7 @@ struct ath12k_base { /* Below regd's are protected by ab->data_lock */ /* This is the regd set for every radio - * by the firmware during initializatin + * by the firmware during initialization */ struct ieee80211_regdomain *default_regd[MAX_RADIOS]; /* This regd is set during dynamic country setting diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index eb0261500ab0..9926d81c5fe4 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -1429,7 +1429,7 @@ static int ath12k_dp_cc_init(struct ath12k_base *ab) } if (dp->spt_info[i].paddr & ATH12K_SPT_4K_ALIGN_CHECK) { - ath12k_warn(ab, "SPT allocated memoty is not 4K aligned"); + ath12k_warn(ab, "SPT allocated memory is not 4K aligned"); ret = -EINVAL; goto free; } diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 36a876d7f61d..7c5dafce5a68 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -371,7 +371,7 @@ struct ath12k_dp { #define HTT_TX_WBM_COMP_STATUS_OFFSET 8 -/* HTT tx completion is overlayed in wbm_release_ring */ +/* HTT tx completion is overlaid in wbm_release_ring */ #define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(16, 13) #define HTT_TX_WBM_COMP_INFO1_REINJECT_REASON GENMASK(3, 0) #define HTT_TX_WBM_COMP_INFO1_EXCEPTION_FRAME BIT(4) @@ -545,7 +545,7 @@ enum htt_srng_ring_id { * 3'b010: 4 usec * 3'b011: 8 usec (default) * 3'b100: 16 usec - * Others: Reserverd + * Others: Reserved * b'19 - response_required: * Host needs HTT_T2H_MSG_TYPE_SRING_SETUP_DONE as response * b'20:31 - reserved: reserved for future use @@ -1126,7 +1126,7 @@ struct htt_tx_ring_selection_cfg_cmd { __le32 tlv_filter_mask_in1; __le32 tlv_filter_mask_in2; __le32 tlv_filter_mask_in3; - __le32 reserverd[3]; + __le32 reserved[3]; } __packed; #define HTT_TX_RING_TLV_FILTER_MGMT_DMA_LEN GENMASK(3, 0) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index a214797c96a2..4f93e4c95fed 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -813,7 +813,7 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, spin_unlock_bh(&buf_ring->idr_lock); if (unlikely(!msdu)) { - ath12k_warn(ab, "montior destination with invalid buf_id %d\n", + ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", buf_id); return HAL_RX_MON_STATUS_PPDU_NOT_DONE; } @@ -1124,7 +1124,7 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct /* PN for multicast packets are not validate in HW, * so skip 802.3 rx path - * Also, fast_rx expectes the STA to be authorized, hence + * Also, fast_rx expects the STA to be authorized, hence * eapol packets are sent in slow path. */ if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol_tkip && @@ -1917,7 +1917,7 @@ ath12k_dp_mon_tx_parse_status_tlv(struct ath12k_base *ab, spin_unlock_bh(&buf_ring->idr_lock); if (unlikely(!msdu)) { - ath12k_warn(ab, "montior destination with invalid buf_id %d\n", + ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", buf_id); return DP_MON_TX_STATUS_PPDU_NOT_DONE; } @@ -2110,7 +2110,7 @@ int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, int *budget, spin_unlock_bh(&buf_ring->idr_lock); if (unlikely(!skb)) { - ath12k_warn(ab, "montior destination with invalid buf_id %d\n", + ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", buf_id); goto move_next; } @@ -2511,7 +2511,7 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, spin_unlock_bh(&buf_ring->idr_lock); if (unlikely(!skb)) { - ath12k_warn(ab, "montior destination with invalid buf_id %d\n", + ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", buf_id); goto move_next; } diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 83a43ad48c51..eb67b3409f85 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -2443,7 +2443,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap /* PN for multicast packets are not validate in HW, * so skip 802.3 rx path - * Also, fast_rx expectes the STA to be authorized, hence + * Also, fast_rx expects the STA to be authorized, hence * eapol packets are sent in slow path. */ if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol && @@ -2611,7 +2611,7 @@ try_again: if (!desc_info) { desc_info = ath12k_dp_get_rx_desc(ab, cookie); if (!desc_info) { - ath12k_warn(ab, "Invalid cookie in manual desc retrival"); + ath12k_warn(ab, "Invalid cookie in manual desc retrieval"); continue; } } @@ -3297,7 +3297,7 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, if (!desc_info) { desc_info = ath12k_dp_get_rx_desc(ab, cookie); if (!desc_info) { - ath12k_warn(ab, "Invalid cookie in manual desc retrival"); + ath12k_warn(ab, "Invalid cookie in manual desc retrieval"); return -EINVAL; } } @@ -3718,7 +3718,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, if (!desc_info) { desc_info = ath12k_dp_get_rx_desc(ab, err_info.cookie); if (!desc_info) { - ath12k_warn(ab, "Invalid cookie in manual desc retrival"); + ath12k_warn(ab, "Invalid cookie in manual desc retrieval"); continue; } } diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index 95d04819083f..0ec53afe9915 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -609,7 +609,7 @@ static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab) HAL_WBM0_RELEASE_RING_BASE_LSB(ab); s->reg_size[1] = HAL_WBM1_RELEASE_RING_HP - HAL_WBM0_RELEASE_RING_HP; - /* Some LMAC rings are not accesed from the host: + /* Some LMAC rings are not accessed from the host: * RXDMA_BUG, RXDMA_DST, RXDMA_MONITOR_BUF, RXDMA_MONITOR_STATUS, * RXDMA_MONITOR_DST, RXDMA_MONITOR_DESC, RXDMA_DIR_BUF_SRC, * RXDMA_RX_MONITOR_BUF, TX_MONITOR_BUF, TX_MONITOR_DST, SW2RXDMA diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index dfbd8bce70e5..0d4fa12ea622 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -270,7 +270,7 @@ struct ath12k_base; #define HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW4_EN BIT(5) #define HAL_WBM_SW_COOKIE_CONV_CFG_GLOBAL_EN BIT(8) -/* TCL ring feild mask and offset */ +/* TCL ring field mask and offset */ #define HAL_TCL1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) #define HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) #define HAL_TCL1_RING_ID_ENTRY_SIZE GENMASK(7, 0) @@ -296,7 +296,7 @@ struct ath12k_base; #define HAL_TCL1_RING_FIELD_DSCP_TID_MAP6 GENMASK(20, 18) #define HAL_TCL1_RING_FIELD_DSCP_TID_MAP7 GENMASK(23, 21) -/* REO ring feild mask and offset */ +/* REO ring field mask and offset */ #define HAL_REO1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) #define HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) #define HAL_REO1_RING_ID_RING_ID GENMASK(15, 8) @@ -738,7 +738,7 @@ struct hal_srng { } u; }; -/* Interrupt mitigation - Batch threshold in terms of numer of frames */ +/* Interrupt mitigation - Batch threshold in terms of number of frames */ #define HAL_SRNG_INT_BATCH_THRESHOLD_TX 256 #define HAL_SRNG_INT_BATCH_THRESHOLD_RX 128 #define HAL_SRNG_INT_BATCH_THRESHOLD_OTHER 1 @@ -813,7 +813,7 @@ enum hal_rx_buf_return_buf_manager { #define HAL_REO_CMD_FLG_UNBLK_RESOURCE BIT(7) #define HAL_REO_CMD_FLG_UNBLK_CACHE BIT(8) -/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* feilds */ +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* fields */ #define HAL_REO_CMD_UPD0_RX_QUEUE_NUM BIT(8) #define HAL_REO_CMD_UPD0_VLD BIT(9) #define HAL_REO_CMD_UPD0_ALDC BIT(10) @@ -838,7 +838,7 @@ enum hal_rx_buf_return_buf_manager { #define HAL_REO_CMD_UPD0_PN_VALID BIT(29) #define HAL_REO_CMD_UPD0_PN BIT(30) -/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* feilds */ +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* fields */ #define HAL_REO_CMD_UPD1_VLD BIT(16) #define HAL_REO_CMD_UPD1_ALDC GENMASK(18, 17) #define HAL_REO_CMD_UPD1_DIS_DUP_DETECTION BIT(19) @@ -854,7 +854,7 @@ enum hal_rx_buf_return_buf_manager { #define HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE BIT(30) #define HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG BIT(31) -/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* feilds */ +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* fields */ #define HAL_REO_CMD_UPD2_SVLD BIT(10) #define HAL_REO_CMD_UPD2_SSN GENMASK(22, 11) #define HAL_REO_CMD_UPD2_SEQ_2K_ERR BIT(23) diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index 2250ca2d19a3..6c17adc6d60b 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -706,7 +706,7 @@ struct rx_msdu_desc { * * msdu_continuation * When set, this MSDU buffer was not able to hold the entire MSDU. - * The next buffer will therefor contain additional information + * The next buffer will therefore contain additional information * related to this MSDU. * * msdu_length @@ -1294,7 +1294,7 @@ struct hal_tcl_data_cmd { * link descriptor. * * tcl_cmd_type - * used to select the type of TCL Command decriptor + * used to select the type of TCL Command descriptor * * desc_type * Indicates the type of address provided in the buf_addr_info. @@ -1408,7 +1408,7 @@ struct hal_tcl_data_cmd { * index_loop_override * When set, address search and packet routing is forced to use * 'search_index' instead of following the register configuration - * seleced by Bank_id. + * selected by Bank_id. * * ring_id * The buffer pointer ring ID. @@ -1990,7 +1990,7 @@ struct hal_wbm_release_ring { * Producer: SW/TQM/RXDMA/REO/SWITCH * Consumer: WBM/SW/FW * - * HTT tx status is overlayed on wbm_release ring on 4-byte words 2, 3, 4 and 5 + * HTT tx status is overlaid on wbm_release ring on 4-byte words 2, 3, 4 and 5 * for software based completions. * * buf_addr_info @@ -2552,7 +2552,7 @@ struct hal_reo_status_hdr { * commands. * * execution_time (in us) - * The amount of time REO took to excecute the command. Note that + * The amount of time REO took to execute the command. Note that * this time does not include the duration of the command waiting * in the command ring, before the execution started. * diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h index 5feaff6450ad..f99556a253e5 100644 --- a/drivers/net/wireless/ath/ath12k/rx_desc.h +++ b/drivers/net/wireless/ath/ath12k/rx_desc.h @@ -1072,7 +1072,7 @@ struct rx_msdu_end_qcn9274 { * * l4_offset * Depending upon mode bit, this field either indicates the - * L4 offset nin bytes from the start of RX_HEADER (only valid + * L4 offset in bytes from the start of RX_HEADER (only valid * if either ipv4_proto or ipv6_proto is set to 1) or indicates * the offset in bytes to the start of TCP or UDP header from * the start of the IP header after decapsulation (Only valid if diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index f6df14149531..3e6991120e53 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -494,7 +494,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, /* tx/rx chainmask reported from fw depends on the actual hw chains used, * For example, for 4x4 capable macphys, first 4 chains can be used for first - * mac and the remaing 4 chains can be used for the second mac or vice-versa. + * mac and the remaining 4 chains can be used for the second mac or vice-versa. * In this case, tx/rx chainmask 0xf will be advertised for first mac and 0xf0 * will be advertised for second mac or vice-versa. Compute the shift value * for tx/rx chainmask which will be used to advertise supported ht/vht rates to @@ -1743,7 +1743,7 @@ int ath12k_wmi_vdev_install_key(struct ath12k *ar, int ret, len, key_len_aligned; /* WMI_TAG_ARRAY_BYTE needs to be aligned with 4, the actual key - * length is specifed in cmd->key_len. + * length is specified in cmd->key_len. */ key_len_aligned = roundup(arg->key_len, 4); @@ -5995,7 +5995,7 @@ static void ath12k_service_available_event(struct ath12k_base *ab, struct sk_buf } /* TODO: Use wmi_service_segment_offset information to get the service - * especially when more services are advertised in multiple sevice + * especially when more services are advertised in multiple service * available events. */ for (i = 0, j = WMI_MAX_SERVICE; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 84e3fb918e43..08a8c9e0f59f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -4002,7 +4002,7 @@ struct ath12k_wmi_pdev_radar_event { } __packed; struct wmi_pdev_temperature_event { - /* temperature value in Celcius degree */ + /* temperature value in Celsius degree */ a_sle32 temp; __le32 pdev_id; } __packed; @@ -4192,7 +4192,7 @@ enum wmi_sta_ps_param_tx_wake_threshold { */ enum wmi_sta_ps_param_pspoll_count { WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0, - /* Values greater than 0 indicate the maximum numer of PS-Poll frames + /* Values greater than 0 indicate the maximum number of PS-Poll frames * FW will send before waking up. */ }; -- cgit v1.2.3 From 731e1b36656a30ffa9c77de673695cdc733d6c92 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 15 Feb 2023 20:31:37 +0200 Subject: wifi: ath12k: dp_mon: Fix unsigned comparison with less than zero The return value from the call to idr_alloc() is int. However, the return value is being assigned to an unsigned int variable 'buf_id', so making 'buf_id' an int. Eliminate the following warning: ./drivers/net/wireless/ath/ath12k/dp_mon.c:1300:15-21: WARNING: Unsigned expression compared with zero: buf_id < 0 Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=4060 Signed-off-by: Yang Li Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230215011453.73466-1-yang.lee@linux.alibaba.com --- drivers/net/wireless/ath/ath12k/dp_mon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 4f93e4c95fed..85f8f802340d 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1268,7 +1268,8 @@ int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab, struct sk_buff *skb; struct hal_srng *srng; dma_addr_t paddr; - u32 cookie, buf_id; + u32 cookie; + int buf_id; srng = &ab->hal.srng_list[buf_ring->refill_buf_ring.ring_id]; spin_lock_bh(&srng->lock); -- cgit v1.2.3 From df8e8db22c25f3d0f75ee0c79342d4a0841f4757 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 15 Feb 2023 20:31:38 +0200 Subject: wifi: ath12k: dp_mon: clean up some inconsistent indentings drivers/net/wireless/ath/ath12k/dp_mon.c:532 ath12k_dp_mon_parse_he_sig_su() warn: inconsistent indenting Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=4062 Signed-off-by: Yang Li Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230215012548.90989-1-yang.lee@linux.alibaba.com --- drivers/net/wireless/ath/ath12k/dp_mon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 85f8f802340d..f1e57e98bdc6 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -529,12 +529,12 @@ static void ath12k_dp_mon_parse_he_sig_su(u8 *tlv_data, case 3: if (he_dcm && he_stbc) { he_gi = HE_GI_0_8; - he_ltf = HE_LTF_4_X; + he_ltf = HE_LTF_4_X; } else { he_gi = HE_GI_3_2; he_ltf = HE_LTF_4_X; - } - break; + } + break; } ppdu_info->gi = he_gi; value = he_gi << HE_GI_SHIFT; -- cgit v1.2.3 From 3c3ab8c9a29662ef25f8f62b8f118cd9b0eadd88 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 15 Feb 2023 20:31:38 +0200 Subject: wifi: ath10k: Remove the unused function shadow_dst_wr_ind_addr() and ath10k_ce_error_intr_enable() The function shadow_dst_wr_ind_addr() and ath10k_ce_error_intr_enable() are defined in the ce.c file, the code calling them has been removed, so remove these unused functions. Eliminate the following warnings: drivers/net/wireless/ath/ath10k/ce.c:80:19: warning: unused function 'shadow_dst_wr_ind_addr' drivers/net/wireless/ath/ath10k/ce.c:441:20: warning: unused function 'ath10k_ce_error_intr_enable' Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=4063 Signed-off-by: Yang Li Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230215014058.116775-1-yang.lee@linux.alibaba.com --- drivers/net/wireless/ath/ath10k/ce.c | 52 ------------------------------------ 1 file changed, 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index c2f3bd35c392..b656cfc03648 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -77,45 +77,6 @@ static inline u32 shadow_sr_wr_ind_addr(struct ath10k *ar, return addr; } -static inline u32 shadow_dst_wr_ind_addr(struct ath10k *ar, - struct ath10k_ce_pipe *ce_state) -{ - u32 ce_id = ce_state->id; - u32 addr = 0; - - switch (ce_id) { - case 1: - addr = 0x00032034; - break; - case 2: - addr = 0x00032038; - break; - case 5: - addr = 0x00032044; - break; - case 7: - addr = 0x0003204C; - break; - case 8: - addr = 0x00032050; - break; - case 9: - addr = 0x00032054; - break; - case 10: - addr = 0x00032058; - break; - case 11: - addr = 0x0003205C; - break; - default: - ath10k_warn(ar, "invalid CE id: %d", ce_id); - break; - } - - return addr; -} - static inline unsigned int ath10k_set_ring_byte(unsigned int offset, struct ath10k_hw_ce_regs_addr_map *addr_map) @@ -438,19 +399,6 @@ static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, host_ie_addr & ~(wm_regs->wm_mask)); } -static inline void ath10k_ce_error_intr_enable(struct ath10k *ar, - u32 ce_ctrl_addr) -{ - struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; - - u32 misc_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + - ar->hw_ce_regs->misc_ie_addr); - - ath10k_ce_write32(ar, - ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr, - misc_ie_addr | misc_regs->err_mask); -} - static inline void ath10k_ce_error_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { -- cgit v1.2.3 From bfcc8ba45eb87bfaaff900bbad2b87b204899d41 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 15 Feb 2023 20:31:38 +0200 Subject: wifi: ath: Silence memcpy run-time false positive warning The memcpy() in ath_key_config() was attempting to write across neighboring struct members in struct ath_keyval. Introduce a wrapping struct_group, kv_values, to be the addressable target of the memcpy without overflowing an individual member. Silences the false positive run-time warning: memcpy: detected field-spanning write (size 32) of single field "hk.kv_val" at drivers/net/wireless/ath/key.c:506 (size 16) Link: https://bbs.archlinux.org/viewtopic.php?id=282254 Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230210054310.never.554-kees@kernel.org --- drivers/net/wireless/ath/ath.h | 12 +++++++----- drivers/net/wireless/ath/key.c | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index f083fb9038c3..f02a308a9ffc 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -96,11 +96,13 @@ struct ath_keyval { u8 kv_type; u8 kv_pad; u16 kv_len; - u8 kv_val[16]; /* TK */ - u8 kv_mic[8]; /* Michael MIC key */ - u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware - * supports both MIC keys in the same key cache entry; - * in that case, kv_mic is the RX key) */ + struct_group(kv_values, + u8 kv_val[16]; /* TK */ + u8 kv_mic[8]; /* Michael MIC key */ + u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware + * supports both MIC keys in the same key cache entry; + * in that case, kv_mic is the RX key) */ + ); }; enum ath_cipher { diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c index 61b59a804e30..b7b61d4f02ba 100644 --- a/drivers/net/wireless/ath/key.c +++ b/drivers/net/wireless/ath/key.c @@ -503,7 +503,7 @@ int ath_key_config(struct ath_common *common, hk.kv_len = key->keylen; if (key->keylen) - memcpy(hk.kv_val, key->key, key->keylen); + memcpy(&hk.kv_values, key->key, key->keylen); if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { switch (vif->type) { -- cgit v1.2.3 From 8c464d16809fa02982f6341ea598ec5d07457f19 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 16 Feb 2023 15:16:14 +0300 Subject: wifi: ath12k: use kfree_skb() instead of kfree() Sk_buffs are supposed to be freed with kfree_skb(). Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/Y+4ejiYakhEvEw7c@kili --- drivers/net/wireless/ath/ath12k/dp_tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 95294f35155c..fd8d850f9818 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -270,7 +270,7 @@ tcl_ring_sel: skb_ext_desc->len, DMA_TO_DEVICE); ret = dma_mapping_error(ab->dev, ti.paddr); if (ret) { - kfree(skb_ext_desc); + kfree_skb(skb_ext_desc); goto fail_unmap_dma; } -- cgit v1.2.3 From 342fcde9d91460f01f65707e16368a1571271a3a Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 17 Feb 2023 11:00:31 +0800 Subject: wifi: ath11k: fix return value check in ath11k_ahb_probe() ioremap() returns NULL pointer not PTR_ERR() when it fails, so replace the IS_ERR() check with NULL pointer check. Fixes: b42b3678c91f ("wifi: ath11k: remap ce register space for IPQ5018") Signed-off-by: Yang Yingliang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230217030031.4021289-1-yangyingliang@huawei.com --- drivers/net/wireless/ath/ath11k/ahb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index cd48eca494ed..022caacd2822 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1174,7 +1174,7 @@ static int ath11k_ahb_probe(struct platform_device *pdev) * to a new space for accessing them. */ ab->mem_ce = ioremap(ce_remap->base, ce_remap->size); - if (IS_ERR(ab->mem_ce)) { + if (!ab->mem_ce) { dev_err(&pdev->dev, "ce ioremap error\n"); ret = -ENOMEM; goto err_core_free; -- cgit v1.2.3 From 7654cc03eb699297130b693ec34e25f77b17c947 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Thu, 16 Feb 2023 22:23:01 +0300 Subject: wifi: ath9k: hif_usb: fix memory leak of remain_skbs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hif_dev->remain_skb is allocated and used exclusively in ath9k_hif_usb_rx_stream(). It is implied that an allocated remain_skb is processed and subsequently freed (in error paths) only during the next call of ath9k_hif_usb_rx_stream(). So, if the urbs are deallocated between those two calls due to the device deinitialization or suspend, it is possible that ath9k_hif_usb_rx_stream() is not called next time and the allocated remain_skb is leaked. Our local Syzkaller instance was able to trigger that. remain_skb makes sense when receiving two consecutive urbs which are logically linked together, i.e. a specific data field from the first skb indicates a cached skb to be allocated, memcpy'd with some data and subsequently processed in the next call to ath9k_hif_usb_rx_stream(). Urbs deallocation supposedly makes that link irrelevant so we need to free the cached skb in those cases. Fix the leak by introducing a function to explicitly free remain_skb (if it is not NULL) when the rx urbs have been deallocated. remain_skb is NULL when it has not been allocated at all (hif_dev struct is kzalloced) or when it has been processed in next call to ath9k_hif_usb_rx_stream(). Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.") Signed-off-by: Fedor Pchelkin Signed-off-by: Alexey Khoroshilov Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230216192301.171225-1-pchelkin@ispras.ru --- drivers/net/wireless/ath/ath9k/hif_usb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index f521dfa2f194..e0130beb304d 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -534,6 +534,24 @@ static struct ath9k_htc_hif hif_usb = { .send = hif_usb_send, }; +/* Need to free remain_skb allocated in ath9k_hif_usb_rx_stream + * in case ath9k_hif_usb_rx_stream wasn't called next time to + * process the buffer and subsequently free it. + */ +static void ath9k_hif_usb_free_rx_remain_skb(struct hif_device_usb *hif_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&hif_dev->rx_lock, flags); + if (hif_dev->remain_skb) { + dev_kfree_skb_any(hif_dev->remain_skb); + hif_dev->remain_skb = NULL; + hif_dev->rx_remain_len = 0; + RX_STAT_INC(hif_dev, skb_dropped); + } + spin_unlock_irqrestore(&hif_dev->rx_lock, flags); +} + static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, struct sk_buff *skb) { @@ -868,6 +886,7 @@ err: static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev) { usb_kill_anchored_urbs(&hif_dev->rx_submitted); + ath9k_hif_usb_free_rx_remain_skb(hif_dev); } static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev) -- cgit v1.2.3 From 95a389e2ff3212d866cc51c77d682d2934074eb8 Mon Sep 17 00:00:00 2001 From: Ramya Gnanasekar Date: Sun, 22 Jan 2023 07:19:36 +0530 Subject: wifi: ath12k: Handle lock during peer_id find ath12k_peer_find_by_id() requires that the caller hold the ab->base_lock. Currently the WBM error path does not hold the lock and calling that function, leads to the following lockdep_assert()in QCN9274: [105162.160893] ------------[ cut here ]------------ [105162.160916] WARNING: CPU: 3 PID: 0 at drivers/net/wireless/ath/ath12k/peer.c:71 ath12k_peer_find_by_id+0x52/0x60 [ath12k] [105162.160933] Modules linked in: ath12k(O) qrtr_mhi qrtr mac80211 cfg80211 mhi qmi_helpers libarc4 nvme nvme_core [last unloaded: ath12k(O)] [105162.160967] CPU: 3 PID: 0 Comm: swapper/3 Tainted: G W O 6.1.0-rc2+ #3 [105162.160972] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0056.2019.0506.1527 05/06/2019 [105162.160977] RIP: 0010:ath12k_peer_find_by_id+0x52/0x60 [ath12k] [105162.160990] Code: 07 eb 0f 39 68 24 74 0a 48 8b 00 48 39 f8 75 f3 31 c0 5b 5d c3 48 8d bf b0 f2 00 00 be ff ff ff ff e8 22 20 c4 e2 85 c0 75 bf <0f> 0b eb bb 66 2e 0f 1f 84 00 00 00 00 00 41 54 4c 8d a7 98 f2 00 [105162.160996] RSP: 0018:ffffa223001acc60 EFLAGS: 00010246 [105162.161003] RAX: 0000000000000000 RBX: ffff9f0573940000 RCX: 0000000000000000 [105162.161008] RDX: 0000000000000001 RSI: ffffffffa3951c8e RDI: ffffffffa39a96d7 [105162.161013] RBP: 000000000000000a R08: 0000000000000000 R09: 0000000000000000 [105162.161017] R10: ffffa223001acb40 R11: ffffffffa3d57c60 R12: ffff9f057394f2e0 [105162.161022] R13: ffff9f0573940000 R14: ffff9f04ecd659c0 R15: ffff9f04d5a9b040 [105162.161026] FS: 0000000000000000(0000) GS:ffff9f0575600000(0000) knlGS:0000000000000000 [105162.161031] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [105162.161036] CR2: 00001d5c8277a008 CR3: 00000001e6224006 CR4: 00000000003706e0 [105162.161041] Call Trace: [105162.161046] [105162.161051] ath12k_dp_rx_process_wbm_err+0x6da/0xaf0 [ath12k] [105162.161072] ? ath12k_dp_rx_process_err+0x80e/0x15a0 [ath12k] [105162.161084] ? __lock_acquire+0x4ca/0x1a60 [105162.161104] ath12k_dp_service_srng+0x263/0x310 [ath12k] [105162.161120] ath12k_pci_ext_grp_napi_poll+0x1c/0x70 [ath12k] [105162.161133] __napi_poll+0x22/0x260 [105162.161141] net_rx_action+0x2f8/0x380 [105162.161153] __do_softirq+0xd0/0x4c9 [105162.161162] irq_exit_rcu+0x88/0xe0 [105162.161169] common_interrupt+0xa5/0xc0 [105162.161174] [105162.161179] [105162.161184] asm_common_interrupt+0x22/0x40 Handle spin lock/unlock in WBM error path to hold the necessary lock expected by ath12k_peer_find_by_id(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0-03171-QCAHKSWPL_SILICONZ-1 Signed-off-by: Ramya Gnanasekar Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230122014936.3594-1-quic_rgnanase@quicinc.com --- drivers/net/wireless/ath/ath12k/dp_rx.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index eb67b3409f85..0adcbcfa0db5 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -3494,11 +3494,14 @@ static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu, msdu_len = ath12k_dp_rx_h_msdu_len(ab, desc); peer_id = ath12k_dp_rx_h_peer_id(ab, desc); + spin_lock(&ab->base_lock); if (!ath12k_peer_find_by_id(ab, peer_id)) { + spin_unlock(&ab->base_lock); ath12k_dbg(ab, ATH12K_DBG_DATA, "invalid peer id received in wbm err pkt%d\n", peer_id); return -EINVAL; } + spin_unlock(&ab->base_lock); if (!rxcb->is_frag && ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE)) { /* First buffer will be freed by the caller, so deduct it's length */ -- cgit v1.2.3 From 80e396586d0a94c42015dd9472176d89a3b0e4ca Mon Sep 17 00:00:00 2001 From: Ramya Gnanasekar Date: Mon, 23 Jan 2023 15:21:41 +0530 Subject: wifi: ath12k: PCI ops for wakeup/release MHI Wakeup/release MHI is not needed before pci_read/write for QCN9274. Since wakeup & release MHI is enabled for all QCN9274 and WCN7850, below MHI assert is seen in QCN9274 [ 784.906613] BUG: sleeping function called from invalid context at drivers/bus/mhi/host/pm.c:989 [ 784.906633] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 0, name: swapper/3 [ 784.906637] preempt_count: 503, expected: 0 [ 784.906641] RCU nest depth: 0, expected: 0 [ 784.906644] 2 locks held by swapper/3/0: [ 784.906646] #0: ffff8ed348e429e0 (&ab->ce.ce_lock){+.-.}-{2:2}, at: ath12k_ce_recv_process_cb+0xb3/0x2f0 [ath12k] [ 784.906664] #1: ffff8ed348e491f0 (&srng->lock_key#3){+.-.}-{2:2}, at: ath12k_ce_recv_process_cb+0xfb/0x2f0 [ath12k] [ 784.906678] Preemption disabled at: [ 784.906680] [<0000000000000000>] 0x0 [ 784.906686] CPU: 3 PID: 0 Comm: swapper/3 Tainted: G W O 6.1.0-rc2+ #3 [ 784.906688] Hardware name: Intel(R) Client Systems NUC8i7HVK/NUC8i7HVB, BIOS HNKBLi70.86A.0056.2019.0506.1527 05/06/2019 [ 784.906690] Call Trace: [ 784.906691] [ 784.906693] dump_stack_lvl+0x56/0x7b [ 784.906698] __might_resched+0x21c/0x270 [ 784.906704] __mhi_device_get_sync+0x7d/0x1c0 [mhi] [ 784.906714] mhi_device_get_sync+0xd/0x20 [mhi] [ 784.906719] ath12k_pci_write32+0x75/0x170 [ath12k] [ 784.906729] ath12k_hal_srng_access_end+0x55/0xc0 [ath12k] [ 784.906737] ath12k_ce_recv_process_cb+0x1f3/0x2f0 [ath12k] [ 784.906776] ? ath12k_pci_ce_tasklet+0x11/0x30 [ath12k] [ 784.906788] ath12k_pci_ce_tasklet+0x11/0x30 [ath12k] [ 784.906813] tasklet_action_common.isra.18+0xb7/0xe0 [ 784.906820] __do_softirq+0xd0/0x4c9 [ 784.906826] irq_exit_rcu+0x88/0xe0 [ 784.906828] common_interrupt+0xa5/0xc0 [ 784.906831] [ 784.906832] Adding function callbacks for MHI wakeup and release operations. QCN9274 does not need wakeup/release, function callbacks are initialized to NULL. In case of WCN7850, shadow registers are used to access rings. Since, shadow register's offset is less than ACCESS_ALWAYS_OFF, mhi_device_get_sync() or mhi_device_put() to wakeup and release mhi will not be called during service ring accesses. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0-03171-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Signed-off-by: Ramya Gnanasekar Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230123095141.5310-1-quic_rgnanase@quicinc.com --- drivers/net/wireless/ath/ath12k/pci.c | 47 ++++++++++++++++++++++++++++------- drivers/net/wireless/ath/ath12k/pci.h | 6 +++++ 2 files changed, 44 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index ae7f6083c9fc..d32637b0113d 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -119,6 +119,30 @@ static const char *irq_name[ATH12K_IRQ_NUM_MAX] = { "tcl2host-status-ring", }; +static int ath12k_pci_bus_wake_up(struct ath12k_base *ab) +{ + struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); + + return mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); +} + +static void ath12k_pci_bus_release(struct ath12k_base *ab) +{ + struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); + + mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); +} + +static const struct ath12k_pci_ops ath12k_pci_ops_qcn9274 = { + .wakeup = NULL, + .release = NULL, +}; + +static const struct ath12k_pci_ops ath12k_pci_ops_wcn7850 = { + .wakeup = ath12k_pci_bus_wake_up, + .release = ath12k_pci_bus_release, +}; + static void ath12k_pci_select_window(struct ath12k_pci *ab_pci, u32 offset) { struct ath12k_base *ab = ab_pci->ab; @@ -989,13 +1013,14 @@ u32 ath12k_pci_read32(struct ath12k_base *ab, u32 offset) { struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); u32 val, window_start; + int ret = 0; /* for offset beyond BAR + 4K - 32, may * need to wakeup MHI to access. */ if (test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && - offset >= ACCESS_ALWAYS_OFF) - mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + offset >= ACCESS_ALWAYS_OFF && ab_pci->pci_ops->wakeup) + ret = ab_pci->pci_ops->wakeup(ab); if (offset < WINDOW_START) { val = ioread32(ab->mem + offset); @@ -1023,9 +1048,9 @@ u32 ath12k_pci_read32(struct ath12k_base *ab, u32 offset) } if (test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && - offset >= ACCESS_ALWAYS_OFF) - mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); - + offset >= ACCESS_ALWAYS_OFF && ab_pci->pci_ops->release && + !ret) + ab_pci->pci_ops->release(ab); return val; } @@ -1033,13 +1058,14 @@ void ath12k_pci_write32(struct ath12k_base *ab, u32 offset, u32 value) { struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); u32 window_start; + int ret = 0; /* for offset beyond BAR + 4K - 32, may * need to wakeup MHI to access. */ if (test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && - offset >= ACCESS_ALWAYS_OFF) - mhi_device_get_sync(ab_pci->mhi_ctrl->mhi_dev); + offset >= ACCESS_ALWAYS_OFF && ab_pci->pci_ops->wakeup) + ret = ab_pci->pci_ops->wakeup(ab); if (offset < WINDOW_START) { iowrite32(value, ab->mem + offset); @@ -1067,8 +1093,9 @@ void ath12k_pci_write32(struct ath12k_base *ab, u32 offset, u32 value) } if (test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags) && - offset >= ACCESS_ALWAYS_OFF) - mhi_device_put(ab_pci->mhi_ctrl->mhi_dev); + offset >= ACCESS_ALWAYS_OFF && ab_pci->pci_ops->release && + !ret) + ab_pci->pci_ops->release(ab); } int ath12k_pci_power_up(struct ath12k_base *ab) @@ -1182,6 +1209,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, case QCN9274_DEVICE_ID: ab_pci->msi_config = &ath12k_msi_config[0]; ab->static_window_map = true; + ab_pci->pci_ops = &ath12k_pci_ops_qcn9274; ath12k_pci_read_hw_version(ab, &soc_hw_version_major, &soc_hw_version_minor); switch (soc_hw_version_major) { @@ -1202,6 +1230,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ab_pci->msi_config = &ath12k_msi_config[0]; ab->static_window_map = false; ab->hw_rev = ATH12K_HW_WCN7850_HW20; + ab_pci->pci_ops = &ath12k_pci_ops_wcn7850; break; default: diff --git a/drivers/net/wireless/ath/ath12k/pci.h b/drivers/net/wireless/ath/ath12k/pci.h index 0d9e40ab31f2..0f24fd9395cd 100644 --- a/drivers/net/wireless/ath/ath12k/pci.h +++ b/drivers/net/wireless/ath/ath12k/pci.h @@ -86,6 +86,11 @@ enum ath12k_pci_flags { ATH12K_PCI_ASPM_RESTORE, }; +struct ath12k_pci_ops { + int (*wakeup)(struct ath12k_base *ab); + void (*release)(struct ath12k_base *ab); +}; + struct ath12k_pci { struct pci_dev *pdev; struct ath12k_base *ab; @@ -103,6 +108,7 @@ struct ath12k_pci { /* enum ath12k_pci_flags */ unsigned long flags; u16 link_ctl; + const struct ath12k_pci_ops *pci_ops; }; static inline struct ath12k_pci *ath12k_pci_priv(struct ath12k_base *ab) -- cgit v1.2.3 From f117276638b7600b981b3fe28550823cfbe1ef23 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 1 Feb 2023 08:54:42 -0800 Subject: wifi: ath11k: Use platform_get_irq() to get the interrupt As of commit a1a2b7125e10 ("of/platform: Drop static setup of IRQ resource from DT core"), we need to use platform_get_irq() instead of platform_get_resource() to get our IRQs because platform_get_resource() simply won't get them anymore. This was already fixed in several other Atheros WiFi drivers, apparently in response to Zeal Robot reports. An example of another fix is commit 9503a1fc123d ("ath9k: Use platform_get_irq() to get the interrupt"). ath11k seems to have been missed in this effort, though. Without this change, WiFi wasn't coming up on my Qualcomm sc7280-based hardware. Specifically, "platform_get_resource(pdev, IORESOURCE_IRQ, i)" was failing even for i=0. Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 Fixes: a1a2b7125e10 ("of/platform: Drop static setup of IRQ resource from DT core") Fixes: 00402f49d26f ("ath11k: Add support for WCN6750 device") Signed-off-by: Douglas Anderson Tested-by: Jun Yu Reviewed-by: Lad Prabhakar Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230201084131.v2.1.I69cf3d56c97098287fe3a70084ee515098390b70@changeid --- drivers/net/wireless/ath/ath11k/ahb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 022caacd2822..6b6059d5dc38 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -874,11 +874,11 @@ static int ath11k_ahb_setup_msi_resources(struct ath11k_base *ab) ab->pci.msi.ep_base_data = int_prop + 32; for (i = 0; i < ab->pci.msi.config->total_vectors; i++) { - res = platform_get_resource(pdev, IORESOURCE_IRQ, i); - if (!res) - return -ENODEV; + ret = platform_get_irq(pdev, i); + if (ret < 0) + return ret; - ab->pci.msi.irqs[i] = res->start; + ab->pci.msi.irqs[i] = ret; } set_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags); -- cgit v1.2.3 From 95c95251d0547b46d6571e4fbd51b42865c15a4a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 1 Feb 2023 08:54:43 -0800 Subject: wifi: ath5k: Use platform_get_irq() to get the interrupt As of commit a1a2b7125e10 ("of/platform: Drop static setup of IRQ resource from DT core"), we need to use platform_get_irq() instead of platform_get_resource() to get our IRQs because platform_get_resource() simply won't get them anymore. This was already fixed in several other Atheros WiFi drivers, apparently in response to Zeal Robot reports. An example of another fix is commit 9503a1fc123d ("ath9k: Use platform_get_irq() to get the interrupt"). ath5k seems to have been missed in this effort, though. Fixes: a1a2b7125e10 ("of/platform: Drop static setup of IRQ resource from DT core") Signed-off-by: Douglas Anderson Reviewed-by: Lad Prabhakar Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230201084131.v2.2.Ic4f8542b0588d7eb4bc6e322d4af3d2064e84ff0@changeid --- drivers/net/wireless/ath/ath5k/ahb.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index 2c9cec8b53d9..28a1e5eff204 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -113,15 +113,13 @@ static int ath_ahb_probe(struct platform_device *pdev) goto err_out; } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no IRQ resource found\n"); - ret = -ENXIO; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource found: %d\n", irq); + ret = irq; goto err_iounmap; } - irq = res->start; - hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops); if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); -- cgit v1.2.3 From 4c856ee12df85aabd437c3836ed9f68d94268358 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 6 Feb 2023 16:15:48 +0300 Subject: wifi: ath5k: fix an off by one check in ath5k_eeprom_read_freq_list() This loop checks that i < max at the start of loop but then it does i++ which could put it past the end of the array. It's harmless to check again and prevent a potential out of bounds. Fixes: 1048643ea94d ("ath5k: Clean up eeprom parsing and add missing calibration data") Signed-off-by: Dan Carpenter Reviewed-by: Luis Chamberlain Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/Y+D9hPQrHfWBJhXz@kili --- drivers/net/wireless/ath/ath5k/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index d444b3d70ba2..58d3e86f6256 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -529,7 +529,7 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, ee->ee_n_piers[mode]++; freq2 = (val >> 8) & 0xff; - if (!freq2) + if (!freq2 || i >= max) break; pc[i++].freq = ath5k_eeprom_bin2freq(ee, -- cgit v1.2.3 From 8c68fe00344c01bfba95b9f978af0aa236a821f7 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 3 Feb 2023 00:01:40 +0000 Subject: wifi: ath10k: snoc: enable threaded napi on WCN3990 NAPI poll can be done in threaded context along with soft irq context. Threaded context can be scheduled efficiently, thus creating less of bottleneck during Rx processing. This patch is to enable threaded NAPI on ath10k driver. Based on testing, it was observed that on WCN3990, the CPU0 reaches 100% utilization when napi runs in softirq context. At the same time the other CPUs are at low consumption percentage. This does not allow device to reach its maximum throughput potential. After enabling threaded napi, CPU load is balanced across all CPUs and following improvments were observed: - UDP_RX increase by ~22-25% - TCP_RX increase by ~15% Here are some of the additional raw data with and without threaded napi: ================================================== udp_rx(Without threaded NAPI) 435.98+-5.16 : Channel 44 439.06+-0.66 : Channel 157 udp_rx(With threaded NAPI) 509.73+-41.03 : Channel 44 549.97+-7.62 : Channel 157 =================================================== udp_tx(Without threaded NAPI) 461.31+-0.69 : Channel 44 461.46+-0.78 : Channel 157 udp_tx(With threaded NAPI) 459.20+-0.77 : Channel 44 459.78+-1.08 : Channel 157 =================================================== tcp_rx(Without threaded NAPI) 472.63+-2.35 : Channel 44 469.29+-6.31 : Channel 157 tcp_rx(With threaded NAPI) 498.49+-2.44 : Channel 44 541.14+-40.65 : Channel 157 =================================================== tcp_tx(Without threaded NAPI) 317.34+-2.37 : Channel 44 317.01+-2.56 : Channel 157 tcp_tx(With threaded NAPI) 371.34+-2.36 : Channel 44 376.95+-9.40 : Channel 157 =================================================== Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.2.2-00696-QCAHLSWMTPL-1 Signed-off-by: Abhishek Kumar Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230203000116.v2.1.I5bb9c164a2d2025655dee810b983e01ecd81c14e@changeid --- drivers/net/wireless/ath/ath10k/snoc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index cfcb759a87de..0f6d2f67ff6b 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -927,6 +927,7 @@ static int ath10k_snoc_hif_start(struct ath10k *ar) bitmap_clear(ar_snoc->pending_ce_irqs, 0, CE_COUNT_MAX); + dev_set_threaded(&ar->napi_dev, true); ath10k_core_napi_enable(ar); ath10k_snoc_irq_enable(ar); ath10k_snoc_rx_post(ar); -- cgit v1.2.3 From 7c4c511f74ba65a18f0beeb9b2fc0e34f28ee79d Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Tue, 7 Feb 2023 13:24:10 +0800 Subject: wifi: ath10k: Remove redundant assignment to changed_flags Variable changed_flags is assigned, but is not effectively used, so delete it. drivers/net/wireless/ath/ath10k/mac.c:6024:22: warning: parameter 'changed_flags' set but not used. Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3963 Signed-off-by: Jiapeng Chong Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230207052410.26337-1-jiapeng.chong@linux.alibaba.com --- drivers/net/wireless/ath/ath10k/mac.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ec8d5b29bc72..7675858f069b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6030,7 +6030,6 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); - changed_flags &= SUPPORTED_FILTERS; *total_flags &= SUPPORTED_FILTERS; ar->filter_flags = *total_flags; -- cgit v1.2.3 From 60b7d62ba8cdbd073997bff0f1cdae8d844002c0 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 9 Feb 2023 23:26:22 +0100 Subject: wifi: ath11k: fix SAC bug on peer addition with sta band migration Fix sleep in atomic context warning detected by Smatch static checker analyzer. Following the locking pattern for peer_rhash_add lock tbl_mtx_lock mutex always even if sta is not transitioning to another band. This is peer_add function and a more secure locking should not cause performance regression. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1 Fixes: d673cb6fe6c0 ("wifi: ath11k: fix peer addition/deletion error on sta band migration") Reported-by: Dan Carpenter Signed-off-by: Christian Marangi Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230209222622.1751-1-ansuelsmth@gmail.com --- drivers/net/wireless/ath/ath11k/peer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c index 1ae7af02c364..1380811827a8 100644 --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -382,22 +382,23 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, return -ENOBUFS; } + mutex_lock(&ar->ab->tbl_mtx_lock); spin_lock_bh(&ar->ab->base_lock); peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr); if (peer) { if (peer->vdev_id == param->vdev_id) { spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); return -EINVAL; } /* Assume sta is transitioning to another band. * Remove here the peer from rhash. */ - mutex_lock(&ar->ab->tbl_mtx_lock); ath11k_peer_rhash_delete(ar->ab, peer); - mutex_unlock(&ar->ab->tbl_mtx_lock); } spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); ret = ath11k_wmi_send_peer_create_cmd(ar, param); if (ret) { -- cgit v1.2.3 From 59e6ded57cc1b3b5117cef66dabe6d7977208ba0 Mon Sep 17 00:00:00 2001 From: Lu jicong Date: Tue, 14 Feb 2023 06:36:02 +0000 Subject: wifi: rtlwifi: rtl8192ce: fix dealing empty EEPROM values On OpenWRT platform, RTL8192CE could be soldered on board, but not standard PCI module. In this case, some EEPROM values aren't programmed and left 0xff. Load default values when the EEPROM values are empty to avoid problems. Signed-off-by: Lu jicong Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214063602.2257263-1-jiconglu58@gmail.com --- .../net/wireless/realtek/rtlwifi/rtl8192ce/hw.c | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c index b9c62640d2cb..dc480323c9cb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c @@ -1428,7 +1428,9 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, for (rf_path = 0; rf_path < 2; rf_path++) { for (i = 0; i < 3; i++) { - if (!autoload_fail) { + if (!autoload_fail && + hwinfo[EEPROM_TXPOWERCCK + rf_path * 3 + i] != 0xff && + hwinfo[EEPROM_TXPOWERHT40_1S + rf_path * 3 + i] != 0xff) { rtlefuse-> eeprom_chnlarea_txpwr_cck[rf_path][i] = hwinfo[EEPROM_TXPOWERCCK + rf_path * 3 + i]; @@ -1448,7 +1450,8 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, } for (i = 0; i < 3; i++) { - if (!autoload_fail) + if (!autoload_fail && + hwinfo[EEPROM_TXPOWERHT40_2SDIFF + i] != 0xff) tempval = hwinfo[EEPROM_TXPOWERHT40_2SDIFF + i]; else tempval = EEPROM_DEFAULT_HT40_2SDIFF; @@ -1518,7 +1521,9 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, } for (i = 0; i < 3; i++) { - if (!autoload_fail) { + if (!autoload_fail && + hwinfo[EEPROM_TXPWR_GROUP + i] != 0xff && + hwinfo[EEPROM_TXPWR_GROUP + 3 + i] != 0xff) { rtlefuse->eeprom_pwrlimit_ht40[i] = hwinfo[EEPROM_TXPWR_GROUP + i]; rtlefuse->eeprom_pwrlimit_ht20[i] = @@ -1563,7 +1568,8 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, for (i = 0; i < 14; i++) { index = rtl92c_get_chnl_group((u8)i); - if (!autoload_fail) + if (!autoload_fail && + hwinfo[EEPROM_TXPOWERHT20DIFF + index] != 0xff) tempval = hwinfo[EEPROM_TXPOWERHT20DIFF + index]; else tempval = EEPROM_DEFAULT_HT20_DIFF; @@ -1580,7 +1586,8 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, index = rtl92c_get_chnl_group((u8)i); - if (!autoload_fail) + if (!autoload_fail && + hwinfo[EEPROM_TXPOWER_OFDMDIFF + index] != 0xff) tempval = hwinfo[EEPROM_TXPOWER_OFDMDIFF + index]; else tempval = EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF; @@ -1610,14 +1617,16 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, "RF-B Legacy to HT40 Diff[%d] = 0x%x\n", i, rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]); - if (!autoload_fail) + if (!autoload_fail && hwinfo[RF_OPTION1] != 0xff) rtlefuse->eeprom_regulatory = (hwinfo[RF_OPTION1] & 0x7); else rtlefuse->eeprom_regulatory = 0; RTPRINT(rtlpriv, FINIT, INIT_TXPOWER, "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory); - if (!autoload_fail) { + if (!autoload_fail && + hwinfo[EEPROM_TSSI_A] != 0xff && + hwinfo[EEPROM_TSSI_B] != 0xff) { rtlefuse->eeprom_tssi[RF90_PATH_A] = hwinfo[EEPROM_TSSI_A]; rtlefuse->eeprom_tssi[RF90_PATH_B] = hwinfo[EEPROM_TSSI_B]; } else { @@ -1628,7 +1637,7 @@ static void _rtl92ce_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, rtlefuse->eeprom_tssi[RF90_PATH_A], rtlefuse->eeprom_tssi[RF90_PATH_B]); - if (!autoload_fail) + if (!autoload_fail && hwinfo[EEPROM_THERMAL_METER] != 0xff) tempval = hwinfo[EEPROM_THERMAL_METER]; else tempval = EEPROM_DEFAULT_THERMALMETER; -- cgit v1.2.3 From 015bf4df8ea67080668ac8314e692076dea1ce6d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 15 Feb 2023 13:34:37 +0100 Subject: wifi: wfx: Remove some dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wait_for_completion_timeout() can not return a <0 value. So simplify the logic and remove dead code. -ERESTARTSYS can not be returned by do_wait_for_common() for tasks with TASK_UNINTERRUPTIBLE, which is the case for wait_for_completion_timeout() Signed-off-by: Christophe JAILLET Reviewed-by: Jérôme Pouiller Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/809c4a645c8d1306c0d256345515865c40ec731c.1676464422.git.christophe.jaillet@wanadoo.fr --- drivers/net/wireless/silabs/wfx/main.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c index 6b9864e478ac..0b50f7058bbb 100644 --- a/drivers/net/wireless/silabs/wfx/main.c +++ b/drivers/net/wireless/silabs/wfx/main.c @@ -358,13 +358,9 @@ int wfx_probe(struct wfx_dev *wdev) wfx_bh_poll_irq(wdev); err = wait_for_completion_timeout(&wdev->firmware_ready, 1 * HZ); - if (err <= 0) { - if (err == 0) { - dev_err(wdev->dev, "timeout while waiting for startup indication\n"); - err = -ETIMEDOUT; - } else if (err == -ERESTARTSYS) { - dev_info(wdev->dev, "probe interrupted by user\n"); - } + if (err == 0) { + dev_err(wdev->dev, "timeout while waiting for startup indication\n"); + err = -ETIMEDOUT; goto bh_unregister; } -- cgit v1.2.3 From 398eb19415ebb01ccc8e5bb75ff04cb760b01e05 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 16 Feb 2023 08:46:54 +0800 Subject: wifi: rtl8xxxu: 8188e: parse single one element of RA report for station mode Intentionally parsing single one element of RA report by breaking loop causes a smatch warning: drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c:1678 rtl8188e_handle_ra_tx_report2() warn: ignoring unreachable code. With existing comments, it intends to process single one element for station mode, but it will parse more elements in AP mode if it's implemented. Implement program logic according to existing comment to avoid smatch warning, and also be usable for both AP and stations modes. Compile test only. Reported-by: kernel test robot Reported-by: Dan Carpenter Link: https://lore.kernel.org/r/202302142135.LCqUTVGY-lkp@intel.com/ Cc: Bitterblue Smith Signed-off-by: Ping-Ke Shih Tested-by: Bitterblue Smith Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230216004654.4642-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c index a99ddb41cd24..f15b099899e5 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c @@ -1699,6 +1699,12 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s dev_dbg(dev, "%s: len: %d items: %d\n", __func__, tx_rpt_len, items); + /* We only use macid 0, so only the first item is relevant. + * AP mode will use more of them if it's ever implemented. + */ + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_STATION) + items = 1; + for (macid = 0; macid < items; macid++) { valid = false; @@ -1741,12 +1747,6 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s min_rpt_time = ra->rpt_time; rpt += TX_RPT2_ITEM_SIZE; - - /* - * We only use macid 0, so only the first item is relevant. - * AP mode will use more of them if it's ever implemented. - */ - break; } if (min_rpt_time != ra->pre_min_rpt_time) { -- cgit v1.2.3 From db5e4b3645534bc8f8d6c24537e9564a62073ac8 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Fri, 17 Feb 2023 17:25:29 +0800 Subject: wifi: rtlwifi: rtl8192de: Remove the unused variable bcnfunc_enable Variable bcnfunc_enable is not effectively used, so delete it. drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c:1050:5: warning: variable 'bcnfunc_enable' set but not used. Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=4110 Signed-off-by: Jiapeng Chong Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230217092529.105899-1-jiapeng.chong@linux.alibaba.com --- drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c index 2aecb2583f75..df1e36fbc348 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/hw.c @@ -1047,7 +1047,6 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bt_msr = rtl_read_byte(rtlpriv, MSR); enum led_ctl_mode ledaction = LED_CTL_NO_LINK; - u8 bcnfunc_enable; bt_msr &= 0xfc; @@ -1064,31 +1063,26 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw, "Set HW_VAR_MEDIA_STATUS: No such media status(%x)\n", type); } - bcnfunc_enable = rtl_read_byte(rtlpriv, REG_BCN_CTRL); switch (type) { case NL80211_IFTYPE_UNSPECIFIED: bt_msr |= MSR_NOLINK; ledaction = LED_CTL_LINK; - bcnfunc_enable &= 0xF7; rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: bt_msr |= MSR_ADHOC; - bcnfunc_enable |= 0x08; rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: bt_msr |= MSR_INFRA; ledaction = LED_CTL_LINK; - bcnfunc_enable &= 0xF7; rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: bt_msr |= MSR_AP; - bcnfunc_enable |= 0x08; rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to AP!\n"); break; -- cgit v1.2.3 From c6aa9a9c47252ac7b07ed6d10459027e2f2a2de0 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 17 Feb 2023 20:00:07 +0800 Subject: wifi: rtw89: add RNR support for 6 GHz scan Since 6 GHz band has around 60 channels and more strict rules for active probing. Reduced neighbor report can be used to reduce the channels we scan and get specific target BSS info to probe for. Declare flag WIPHY_FLAG_SPLIT_SCAN_6GHZ so the scan request could be divided into two portions: legacy bands and 6 GHz bands. So RNR information from legacy bands could later be used when 6 GHz scan. When the scan flag NL80211_SCAN_FLAG_COLOCATED_6GHZ is set, cfg80211 will pass down a reduced channel set which contains PSCs and non-PSC with RNR info received in the 2 GHz/5 GHz band. This reduces the scan duration by allowing us to only scan for channels in which APs are currently operating. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230217120007.8835-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 35 +++++++- drivers/net/wireless/realtek/rtw89/fw.c | 136 ++++++++++++++++++++++++++---- drivers/net/wireless/realtek/rtw89/fw.h | 7 ++ 3 files changed, 162 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index f09361bc4a4d..489fa7a86160 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1400,6 +1400,34 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev, } } +static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, + struct sk_buff *skb) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct list_head *pkt_list = rtwdev->scan_info.pkt_list; + struct rtw89_pktofld_info *info; + const u8 *ies = mgmt->u.beacon.variable, *ssid_ie; + + if (rx_status->band != NL80211_BAND_6GHZ) + return; + + ssid_ie = cfg80211_find_ie(WLAN_EID_SSID, ies, skb->len); + + list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) { + if (ether_addr_equal(info->bssid, mgmt->bssid)) { + rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); + continue; + } + + if (!ssid_ie || ssid_ie[1] != info->ssid_len || info->ssid_len == 0) + continue; + + if (memcmp(&ssid_ie[2], info->ssid, info->ssid_len) == 0) + rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); + } +} + static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { @@ -1412,6 +1440,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; const u8 *bssid = iter_data->bssid; + if (rtwdev->scanning && + (ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control))) + rtw89_core_cancel_6ghz_probe_tx(rtwdev, skb); + if (!vif->bss_conf.bssid) return; @@ -3372,7 +3405,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_TDLS_EXTERNAL_SETUP | - WIPHY_FLAG_AP_UAPSD; + WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_SPLIT_SCAN_6GHZ; hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; hw->wiphy->max_scan_ssids = RTW89_SCANOFLD_MAX_SSID; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 0b73dc2e9ad7..a88f7974c1d6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2702,9 +2702,29 @@ static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev) } } +static bool rtw89_is_6ghz_wildcard_probe_req(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct rtw89_pktofld_info *info, + enum nl80211_band band, u8 ssid_idx) +{ + struct cfg80211_scan_request *req = rtwvif->scan_req; + + if (band != NL80211_BAND_6GHZ) + return false; + + if (req->ssids[ssid_idx].ssid_len) { + memcpy(info->ssid, req->ssids[ssid_idx].ssid, + req->ssids[ssid_idx].ssid_len); + info->ssid_len = req->ssids[ssid_idx].ssid_len; + return false; + } else { + return true; + } +} + static int rtw89_append_probe_req_ie(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, - struct sk_buff *skb) + struct sk_buff *skb, u8 ssid_idx) { struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct ieee80211_scan_ies *ies = rtwvif->scan_ies; @@ -2732,6 +2752,13 @@ static int rtw89_append_probe_req_ie(struct rtw89_dev *rtwdev, goto out; } + if (rtw89_is_6ghz_wildcard_probe_req(rtwdev, rtwvif, info, band, + ssid_idx)) { + kfree_skb(new); + kfree(info); + goto out; + } + ret = rtw89_fw_h2c_add_pkt_offload(rtwdev, &info->id, new); if (ret) { kfree_skb(new); @@ -2762,7 +2789,7 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, if (!skb) return -ENOMEM; - ret = rtw89_append_probe_req_ie(rtwdev, rtwvif, skb); + ret = rtw89_append_probe_req_ie(rtwdev, rtwvif, skb, i); kfree_skb(skb); if (ret) @@ -2772,6 +2799,77 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, return 0; } +static int rtw89_update_6ghz_rnr_chan(struct rtw89_dev *rtwdev, + struct cfg80211_scan_request *req, + struct rtw89_mac_chinfo *ch_info) +{ + struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; + struct list_head *pkt_list = rtwdev->scan_info.pkt_list; + struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif); + struct ieee80211_scan_ies *ies = rtwvif->scan_ies; + struct cfg80211_scan_6ghz_params *params; + struct rtw89_pktofld_info *info, *tmp; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + bool found; + int ret = 0; + u8 i; + + if (!req->n_6ghz_params) + return 0; + + for (i = 0; i < req->n_6ghz_params; i++) { + params = &req->scan_6ghz_params[i]; + + if (req->channels[params->channel_idx]->hw_value != + ch_info->pri_ch) + continue; + + found = false; + list_for_each_entry(tmp, &pkt_list[NL80211_BAND_6GHZ], list) { + if (ether_addr_equal(tmp->bssid, params->bssid)) { + found = true; + break; + } + } + if (found) + continue; + + skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr, + NULL, 0, req->ie_len); + skb_put_data(skb, ies->ies[NL80211_BAND_6GHZ], ies->len[NL80211_BAND_6GHZ]); + skb_put_data(skb, ies->common_ies, ies->common_ie_len); + hdr = (struct ieee80211_hdr *)skb->data; + ether_addr_copy(hdr->addr3, params->bssid); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + kfree_skb(skb); + goto out; + } + + ret = rtw89_fw_h2c_add_pkt_offload(rtwdev, &info->id, skb); + if (ret) { + kfree_skb(skb); + kfree(info); + goto out; + } + + ether_addr_copy(info->bssid, params->bssid); + info->channel_6ghz = req->channels[params->channel_idx]->hw_value; + list_add_tail(&info->list, &rtwdev->scan_info.pkt_list[NL80211_BAND_6GHZ]); + + ch_info->tx_pkt = true; + ch_info->period = RTW89_CHANNEL_TIME_6G + RTW89_DWELL_TIME_6G; + + kfree_skb(skb); + } + +out: + return ret; +} + static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, int ssid_num, struct rtw89_mac_chinfo *ch_info) @@ -2782,6 +2880,7 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, struct cfg80211_scan_request *req = rtwvif->scan_req; struct rtw89_pktofld_info *info; u8 band, probe_count = 0; + int ret; ch_info->notify_action = RTW89_SCANOFLD_DEBUG_MASK; ch_info->dfs_ch = chan_type == RTW89_CHAN_DFS; @@ -2793,25 +2892,31 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, ch_info->pause_data = false; ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; + if (ch_info->ch_band == RTW89_BAND_6G) { + if ((ssid_num == 1 && req->ssids[0].ssid_len == 0) || + !ch_info->is_psc) { + ch_info->tx_pkt = false; + if (!req->duration_mandatory) + ch_info->period -= RTW89_DWELL_TIME_6G; + } + } + + ret = rtw89_update_6ghz_rnr_chan(rtwdev, req, ch_info); + if (ret) + rtw89_warn(rtwdev, "RNR fails: %d\n", ret); + if (ssid_num) { - ch_info->num_pkt = ssid_num; band = rtw89_hw_to_nl80211_band(ch_info->ch_band); list_for_each_entry(info, &scan_info->pkt_list[band], list) { - ch_info->pkt_id[probe_count] = info->id; - if (++probe_count >= ssid_num) + if (info->channel_6ghz && + ch_info->pri_ch != info->channel_6ghz) + continue; + ch_info->pkt_id[probe_count++] = info->id; + if (probe_count >= RTW89_SCANOFLD_MAX_SSID) break; } - if (probe_count != ssid_num) - rtw89_err(rtwdev, "SSID num differs from list len\n"); - } - - if (ch_info->ch_band == RTW89_BAND_6G) { - if (ssid_num == 1 && req->ssids[0].ssid_len == 0) { - ch_info->tx_pkt = false; - if (!req->duration_mandatory) - ch_info->period -= RTW89_DWELL_TIME_6G; - } + ch_info->num_pkt = probe_count; } switch (chan_type) { @@ -2872,6 +2977,7 @@ static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, ch_info->central_ch = channel->hw_value; ch_info->pri_ch = channel->hw_value; ch_info->rand_seq_num = random_seq; + ch_info->is_psc = cfg80211_channel_is_psc(channel); if (channel->flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index cae07e325326..3f6e0871381d 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -237,6 +237,7 @@ struct rtw89_mac_chinfo { u16 tx_pwr_idx; u8 rsvd1; struct list_head list; + bool is_psc; }; struct rtw89_scan_option { @@ -247,6 +248,12 @@ struct rtw89_scan_option { struct rtw89_pktofld_info { struct list_head list; u8 id; + + /* Below fields are for 6 GHz RNR use only */ + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; + u8 bssid[ETH_ALEN]; + u16 channel_6ghz; }; static inline void RTW89_SET_FWCMD_RA_IS_DIS(void *cmd, u32 val) -- cgit v1.2.3 From b9b1e4fe2957f361c86e288ecf373dc7895cf7c7 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 17 Feb 2023 20:49:12 +0200 Subject: wifi: rtl8xxxu: Remove always true condition in rtl8xxxu_print_chipinfo Fix a new smatch warning: drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c:1580 rtl8xxxu_print_chipinfo() warn: always true condition '(priv->chip_cut <= 15) => (0-15 <= 15)' Reported-by: kernel test robot Link: https://lore.kernel.org/oe-kbuild-all/202302140753.71IgU77A-lkp@intel.com/ Fixes: 7b0ac469e331 ("wifi: rtl8xxxu: Recognise all possible chip cuts") Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/68eff98b-a022-5a00-f330-adf623a35772@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 620a5cc2bfdd..54ca6f2ced3f 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -1575,11 +1575,7 @@ rtl8xxxu_set_spec_sifs(struct rtl8xxxu_priv *priv, u16 cck, u16 ofdm) static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv) { struct device *dev = &priv->udev->dev; - char cut = '?'; - - /* Currently always true: chip_cut is 4 bits. */ - if (priv->chip_cut <= 15) - cut = 'A' + priv->chip_cut; + char cut = 'A' + priv->chip_cut; dev_info(dev, "RTL%s rev %c (%s) romver %d, %iT%iR, TX queues %i, WiFi=%i, BT=%i, GPS=%i, HI PA=%i\n", -- cgit v1.2.3 From 96c79da2e4d1a25e73916c656fe4ccf7fc8176ae Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 18 Feb 2023 16:29:40 +0100 Subject: wifi: rtw88: mac: Add support for the SDIO HCI in rtw_pwr_seq_parser() rtw_pwr_seq_parser() needs to know about the HCI bus interface mask for the SDIO bus so it can parse the chip state change sequences. Signed-off-by: Martin Blumenstingl Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230218152944.48842-2-martin.blumenstingl@googlemail.com --- drivers/net/wireless/realtek/rtw88/mac.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index dae64901bac5..c08071a67b41 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -222,6 +222,9 @@ static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev, case RTW_HCI_TYPE_USB: intf_mask = RTW_PWR_INTF_USB_MSK; break; + case RTW_HCI_TYPE_SDIO: + intf_mask = RTW_PWR_INTF_SDIO_MSK; + break; default: return -EINVAL; } -- cgit v1.2.3 From 8599ea40582d49afe437badde76e31bd7429c607 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 18 Feb 2023 16:29:41 +0100 Subject: wifi: rtw88: mac: Add SDIO HCI support in the TX/page table setup txdma_queue_mapping() and priority_queue_cfg() can use the first entry of each chip's rqpn_table and page_table. Add this mapping so data transmission is possible on SDIO based chipsets. Signed-off-by: Martin Blumenstingl Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230218152944.48842-3-martin.blumenstingl@googlemail.com --- drivers/net/wireless/realtek/rtw88/mac.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index c08071a67b41..1c9530a0eb69 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -1043,6 +1043,9 @@ static int txdma_queue_mapping(struct rtw_dev *rtwdev) else return -EINVAL; break; + case RTW_HCI_TYPE_SDIO: + rqpn = &chip->rqpn_table[0]; + break; default: return -EINVAL; } @@ -1205,6 +1208,9 @@ static int priority_queue_cfg(struct rtw_dev *rtwdev) else return -EINVAL; break; + case RTW_HCI_TYPE_SDIO: + pg_tbl = &chip->page_table[0]; + break; default: return -EINVAL; } -- cgit v1.2.3 From 64e9d564653566812dfade36d20f1794407e6ca1 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 18 Feb 2023 16:29:42 +0100 Subject: wifi: rtw88: rtw8821c: Implement RTL8821CS (SDIO) efuse parsing The efuse of the SDIO RTL8821CS chip has only one known member: the mac address is at offset 0x11a. Add a struct rtw8821cs_efuse describing this and use it for copying the mac address when the SDIO bus is used. Signed-off-by: Martin Blumenstingl Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230218152944.48842-4-martin.blumenstingl@googlemail.com --- drivers/net/wireless/realtek/rtw88/rtw8821c.c | 9 +++++++++ drivers/net/wireless/realtek/rtw88/rtw8821c.h | 6 ++++++ 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index 17f800f6efbd..7ae0541d7b99 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -32,6 +32,12 @@ static void rtw8821cu_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->u.mac_addr); } +static void rtw8821cs_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8821c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->s.mac_addr); +} + enum rtw8821ce_rf_set { SWITCH_TO_BTG, SWITCH_TO_WLG, @@ -77,6 +83,9 @@ static int rtw8821c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) case RTW_HCI_TYPE_USB: rtw8821cu_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_SDIO: + rtw8821cs_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.h b/drivers/net/wireless/realtek/rtw88/rtw8821c.h index 1c81260f3a54..fcff31688c45 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.h +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.h @@ -65,6 +65,11 @@ struct rtw8821ce_efuse { u8 res7; }; +struct rtw8821cs_efuse { + u8 res4[0x4a]; /* 0xd0 */ + u8 mac_addr[ETH_ALEN]; /* 0x11a */ +} __packed; + struct rtw8821c_efuse { __le16 rtl_id; u8 res0[0x0e]; @@ -94,6 +99,7 @@ struct rtw8821c_efuse { union { struct rtw8821ce_efuse e; struct rtw8821cu_efuse u; + struct rtw8821cs_efuse s; }; }; -- cgit v1.2.3 From 9e688784b8a13515ee186d0ee1cfab12b6d05241 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 18 Feb 2023 16:29:43 +0100 Subject: wifi: rtw88: rtw8822b: Implement RTL8822BS (SDIO) efuse parsing The efuse of the SDIO RTL8822BS chip has only one known member: the mac address is at offset 0x11a. Add a struct rtw8822bs_efuse describing this and use it for copying the mac address when the SDIO bus is used. Signed-off-by: Martin Blumenstingl Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230218152944.48842-5-martin.blumenstingl@googlemail.com --- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 9 +++++++++ drivers/net/wireless/realtek/rtw88/rtw8822b.h | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 74dfb89b2c94..531b67787e2e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -32,6 +32,12 @@ static void rtw8822bu_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->u.mac_addr); } +static void rtw8822bs_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822b_efuse *map) +{ + ether_addr_copy(efuse->addr, map->s.mac_addr); +} + static int rtw8822b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; @@ -65,6 +71,9 @@ static int rtw8822b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) case RTW_HCI_TYPE_USB: rtw8822bu_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_SDIO: + rtw8822bs_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.h b/drivers/net/wireless/realtek/rtw88/rtw8822b.h index 01d3644e0c94..2dc3a6660f06 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.h +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.h @@ -65,6 +65,11 @@ struct rtw8822be_efuse { u8 res7; }; +struct rtw8822bs_efuse { + u8 res4[0x4a]; /* 0xd0 */ + u8 mac_addr[ETH_ALEN]; /* 0x11a */ +} __packed; + struct rtw8822b_efuse { __le16 rtl_id; u8 res0[0x0e]; @@ -92,8 +97,9 @@ struct rtw8822b_efuse { u8 country_code[2]; u8 res[3]; union { - struct rtw8822bu_efuse u; struct rtw8822be_efuse e; + struct rtw8822bu_efuse u; + struct rtw8822bs_efuse s; }; }; -- cgit v1.2.3 From ad0a677bce20c7fa2e8af3fd7f23c0686018202b Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 18 Feb 2023 16:29:44 +0100 Subject: wifi: rtw88: rtw8822c: Implement RTL8822CS (SDIO) efuse parsing The efuse of the SDIO RTL8822CS chip has only one known member: the mac address is at offset 0x16a. Add a struct rtw8822cs_efuse describing this and use it for copying the mac address when the SDIO bus is used. Signed-off-by: Martin Blumenstingl Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230218152944.48842-6-martin.blumenstingl@googlemail.com --- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 9 +++++++++ drivers/net/wireless/realtek/rtw88/rtw8822c.h | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 964e27887fe2..5a2c004b12df 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -35,6 +35,12 @@ static void rtw8822cu_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->u.mac_addr); } +static void rtw8822cs_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->s.mac_addr); +} + static int rtw8822c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; @@ -67,6 +73,9 @@ static int rtw8822c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) case RTW_HCI_TYPE_USB: rtw8822cu_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_SDIO: + rtw8822cs_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.h b/drivers/net/wireless/realtek/rtw88/rtw8822c.h index 479d5d769c52..1bc0e7f5d6bb 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.h +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.h @@ -16,6 +16,11 @@ struct rtw8822cu_efuse { u8 res2[0x3d]; }; +struct rtw8822cs_efuse { + u8 res0[0x4a]; /* 0x120 */ + u8 mac_addr[ETH_ALEN]; /* 0x16a */ +} __packed; + struct rtw8822ce_efuse { u8 mac_addr[ETH_ALEN]; /* 0x120 */ u8 vender_id[2]; @@ -91,8 +96,9 @@ struct rtw8822c_efuse { u8 res9; u8 res10[0x42]; union { - struct rtw8822cu_efuse u; struct rtw8822ce_efuse e; + struct rtw8822cu_efuse u; + struct rtw8822cs_efuse s; }; }; -- cgit v1.2.3 From aa4e055945462e645224795e174c25c82f6002ac Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Mon, 20 Feb 2023 15:01:57 +0800 Subject: wifi: rtw89: add tx_wake notify for 8852B 8852B has the same issue: management frames get stuck when wifi chip enters low ps mode, so we alse add notify wake function to trigger wifi chip wake before forwarding management frames. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230220070202.29868-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index a88f7974c1d6..9dc0abc1fc37 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -258,6 +258,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8852A, ge, 0, 13, 36, 0, CRASH_TRIGGER), __CFG_FW_FEAT(RTL8852A, ge, 0, 13, 38, 0, PACKET_DROP), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, NO_LPS_PG), + __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, TX_WAKE), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 20, 0, PACKET_DROP), __CFG_FW_FEAT(RTL8852C, le, 0, 27, 33, 0, NO_DEEP_PS), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 34, 0, TX_WAKE), -- cgit v1.2.3 From 31c416e69dbf416583d0d8245d91e7e82b7846b1 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Feb 2023 15:01:58 +0800 Subject: wifi: rtw89: fw: configure CRASH_TRIGGER feature for 8852B RTL8852B firmware supports CRASH_TRIGGER feature from v0.29.29.0. After this is configured, debugfs fw_crash can support type 1 on RTL8852B to trigger firmware crash and verify L2 recovery through simulation. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230220070202.29868-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 9dc0abc1fc37..d5b1f1b0fd67 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -259,6 +259,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8852A, ge, 0, 13, 38, 0, PACKET_DROP), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, NO_LPS_PG), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, TX_WAKE), + __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, CRASH_TRIGGER), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 20, 0, PACKET_DROP), __CFG_FW_FEAT(RTL8852C, le, 0, 27, 33, 0, NO_DEEP_PS), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 34, 0, TX_WAKE), -- cgit v1.2.3 From bb9040b3ff970529ceaa20b064c113d5ffd5520f Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Mon, 20 Feb 2023 15:01:59 +0800 Subject: wifi: rtw89: adjust channel encoding to common function Since the range of channel table is identical among ICs. Make channel encode/decode function common and not IC dependent. So all ICs with matching firmware that needs this kind of coding can use it directly. This patch doesn't change logic at all. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230220070202.29868-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 72 ++++++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 3 ++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 74 +-------------------------- 3 files changed, 77 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index d9f61ba3d176..d8b035972dd4 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4294,3 +4294,75 @@ void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, data[RTW89_TSSI_SBW20]); } EXPORT_SYMBOL(rtw89_phy_tssi_ctrl_set_bandedge_cfg); + +static +const u8 rtw89_ch_base_table[16] = {1, 0xff, + 36, 100, 132, 149, 0xff, + 1, 33, 65, 97, 129, 161, 193, 225, 0xff}; +#define RTW89_CH_BASE_IDX_2G 0 +#define RTW89_CH_BASE_IDX_5G_FIRST 2 +#define RTW89_CH_BASE_IDX_5G_LAST 5 +#define RTW89_CH_BASE_IDX_6G_FIRST 7 +#define RTW89_CH_BASE_IDX_6G_LAST 14 + +#define RTW89_CH_BASE_IDX_MASK GENMASK(7, 4) +#define RTW89_CH_OFFSET_MASK GENMASK(3, 0) + +u8 rtw89_encode_chan_idx(struct rtw89_dev *rtwdev, u8 central_ch, u8 band) +{ + u8 chan_idx; + u8 last, first; + u8 idx; + + switch (band) { + case RTW89_BAND_2G: + chan_idx = FIELD_PREP(RTW89_CH_BASE_IDX_MASK, RTW89_CH_BASE_IDX_2G) | + FIELD_PREP(RTW89_CH_OFFSET_MASK, central_ch); + return chan_idx; + case RTW89_BAND_5G: + first = RTW89_CH_BASE_IDX_5G_FIRST; + last = RTW89_CH_BASE_IDX_5G_LAST; + break; + case RTW89_BAND_6G: + first = RTW89_CH_BASE_IDX_6G_FIRST; + last = RTW89_CH_BASE_IDX_6G_LAST; + break; + default: + rtw89_warn(rtwdev, "Unsupported band %d\n", band); + return 0; + } + + for (idx = last; idx >= first; idx--) + if (central_ch >= rtw89_ch_base_table[idx]) + break; + + if (idx < first) { + rtw89_warn(rtwdev, "Unknown band %d channel %d\n", band, central_ch); + return 0; + } + + chan_idx = FIELD_PREP(RTW89_CH_BASE_IDX_MASK, idx) | + FIELD_PREP(RTW89_CH_OFFSET_MASK, + (central_ch - rtw89_ch_base_table[idx]) >> 1); + return chan_idx; +} +EXPORT_SYMBOL(rtw89_encode_chan_idx); + +void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, + u8 *ch, enum nl80211_band *band) +{ + u8 idx, offset; + + idx = FIELD_GET(RTW89_CH_BASE_IDX_MASK, chan_idx); + offset = FIELD_GET(RTW89_CH_OFFSET_MASK, chan_idx); + + if (idx == RTW89_CH_BASE_IDX_2G) { + *band = NL80211_BAND_2GHZ; + *ch = offset; + return; + } + + *band = idx <= RTW89_CH_BASE_IDX_5G_LAST ? NL80211_BAND_5GHZ : NL80211_BAND_6GHZ; + *ch = rtw89_ch_base_table[idx] + (offset << 1); +} +EXPORT_SYMBOL(rtw89_decode_chan_idx); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 21233f094644..de0a9abf646e 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -555,5 +555,8 @@ void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, enum rtw89_tssi_bandedge_cfg bandedge_cfg); void rtw89_phy_ul_tb_assoc(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev); +u8 rtw89_encode_chan_idx(struct rtw89_dev *rtwdev, u8 central_ch, u8 band); +void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, + u8 *ch, enum nl80211_band *band); #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index d2dde21d3daf..8af813132f71 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -852,76 +852,6 @@ static void rtw8852c_set_gain_error(struct rtw89_dev *rtwdev, } } -static -const u8 rtw8852c_ch_base_table[16] = {1, 0xff, - 36, 100, 132, 149, 0xff, - 1, 33, 65, 97, 129, 161, 193, 225, 0xff}; -#define RTW8852C_CH_BASE_IDX_2G 0 -#define RTW8852C_CH_BASE_IDX_5G_FIRST 2 -#define RTW8852C_CH_BASE_IDX_5G_LAST 5 -#define RTW8852C_CH_BASE_IDX_6G_FIRST 7 -#define RTW8852C_CH_BASE_IDX_6G_LAST 14 - -#define RTW8852C_CH_BASE_IDX_MASK GENMASK(7, 4) -#define RTW8852C_CH_OFFSET_MASK GENMASK(3, 0) - -static u8 rtw8852c_encode_chan_idx(struct rtw89_dev *rtwdev, u8 central_ch, u8 band) -{ - u8 chan_idx; - u8 last, first; - u8 idx; - - switch (band) { - case RTW89_BAND_2G: - chan_idx = FIELD_PREP(RTW8852C_CH_BASE_IDX_MASK, RTW8852C_CH_BASE_IDX_2G) | - FIELD_PREP(RTW8852C_CH_OFFSET_MASK, central_ch); - return chan_idx; - case RTW89_BAND_5G: - first = RTW8852C_CH_BASE_IDX_5G_FIRST; - last = RTW8852C_CH_BASE_IDX_5G_LAST; - break; - case RTW89_BAND_6G: - first = RTW8852C_CH_BASE_IDX_6G_FIRST; - last = RTW8852C_CH_BASE_IDX_6G_LAST; - break; - default: - rtw89_warn(rtwdev, "Unsupported band %d\n", band); - return 0; - } - - for (idx = last; idx >= first; idx--) - if (central_ch >= rtw8852c_ch_base_table[idx]) - break; - - if (idx < first) { - rtw89_warn(rtwdev, "Unknown band %d channel %d\n", band, central_ch); - return 0; - } - - chan_idx = FIELD_PREP(RTW8852C_CH_BASE_IDX_MASK, idx) | - FIELD_PREP(RTW8852C_CH_OFFSET_MASK, - (central_ch - rtw8852c_ch_base_table[idx]) >> 1); - return chan_idx; -} - -static void rtw8852c_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, - u8 *ch, enum nl80211_band *band) -{ - u8 idx, offset; - - idx = FIELD_GET(RTW8852C_CH_BASE_IDX_MASK, chan_idx); - offset = FIELD_GET(RTW8852C_CH_OFFSET_MASK, chan_idx); - - if (idx == RTW8852C_CH_BASE_IDX_2G) { - *band = NL80211_BAND_2GHZ; - *ch = offset; - return; - } - - *band = idx <= RTW8852C_CH_BASE_IDX_5G_LAST ? NL80211_BAND_5GHZ : NL80211_BAND_6GHZ; - *ch = rtw8852c_ch_base_table[idx] + (offset << 1); -} - static void rtw8852c_set_gain_offset(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx, @@ -1084,7 +1014,7 @@ static void rtw8852c_ctrl_ch(struct rtw89_dev *rtwdev, } } - chan_idx = rtw8852c_encode_chan_idx(rtwdev, chan->primary_channel, band); + chan_idx = rtw89_encode_chan_idx(rtwdev, chan->primary_channel, band); rtw89_phy_write32_idx(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0, chan_idx, phy_idx); } @@ -2730,7 +2660,7 @@ static void rtw8852c_fill_freq_with_ppdu(struct rtw89_dev *rtwdev, if (chan_idx == 0) return; - rtw8852c_decode_chan_idx(rtwdev, chan_idx, &ch, &band); + rtw89_decode_chan_idx(rtwdev, chan_idx, &ch, &band); status->freq = ieee80211_channel_to_frequency(ch, band); status->band = band; } -- cgit v1.2.3 From 4f24d7aa575a3c01192faa7f4fb6a91c54f4ef47 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Mon, 20 Feb 2023 15:02:00 +0800 Subject: wifi: rtw89: 8852b: add channel encoding for hw_scan To obtain correct packet frequency for hw_scan, 52b needs to decode the channel index obtained from hardware. Change the driver related set channel part as well to make driver/firmware compatible. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230220070202.29868-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index ee8dba7e0074..499ae0389c71 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1422,6 +1422,7 @@ static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89 { bool cck_en = chan->channel <= 14; u8 pri_ch_idx = chan->pri_ch_idx; + u8 band = chan->band_type, chan_idx; if (cck_en) rtw8852b_ctrl_sco_cck(rtwdev, chan->primary_channel); @@ -1444,8 +1445,8 @@ static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89 B_BT_DYN_DC_EST_EN_MSK, 0x0); rtw89_phy_write32_mask(rtwdev, R_GNT_BT_WGT_EN, B_GNT_BT_WGT_EN, 0x0); } - rtw89_phy_write32_mask(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0, - chan->primary_channel); + chan_idx = rtw89_encode_chan_idx(rtwdev, chan->primary_channel, band); + rtw89_phy_write32_mask(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0, chan_idx); rtw8852b_5m_mask(rtwdev, chan, phy_idx); rtw8852b_bb_set_pop(rtwdev); rtw8852b_bb_reset_all(rtwdev, phy_idx); @@ -2299,13 +2300,14 @@ static void rtw8852b_fill_freq_with_ppdu(struct rtw89_dev *rtwdev, struct ieee80211_rx_status *status) { u16 chan = phy_ppdu->chan_idx; - u8 band; + enum nl80211_band band; + u8 ch; if (chan == 0) return; - band = chan <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; - status->freq = ieee80211_channel_to_frequency(chan, band); + rtw89_decode_chan_idx(rtwdev, chan, &ch, &band); + status->freq = ieee80211_channel_to_frequency(ch, band); status->band = band; } -- cgit v1.2.3 From 357277e1afdae0f56d08846e818579f5a020709d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 20 Feb 2023 15:02:01 +0800 Subject: wifi: rtw89: 8852b: enable hw_scan support This enables hw_scan for 8852b after firmware version 0.29.29.0. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230220070202.29868-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index d5b1f1b0fd67..dceb033acbc8 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -260,6 +260,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, NO_LPS_PG), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, TX_WAKE), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, CRASH_TRIGGER), + __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, SCAN_OFFLOAD), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 20, 0, PACKET_DROP), __CFG_FW_FEAT(RTL8852C, le, 0, 27, 33, 0, NO_DEEP_PS), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 34, 0, TX_WAKE), -- cgit v1.2.3 From 0d1f7ff19d4f8a6da5a6b60d2afd1d34b5d5ebfc Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Feb 2023 15:02:02 +0800 Subject: wifi: rtw89: refine FW feature judgement on packet drop The newer chips use the newer firmware branches, e.g. v027, v029. And, those firmware branches are supposed to support packet drop when they are just branched out. The initial firmware branch used by each chip is as below. * 8852A: v009 * 8852C: v027 * 8852B: v027 So, only 8852A may use firmware which doesn't support packet drop at runtime. To save trivial positive listing in firmware feature table, we change to reverse judgment. Besides, rtw89_mac_ptk_drop_by_band_and_wait() missed to check firmware feature before calling rtw89_fw_h2c_pkt_drop(). We also fix it. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230220070202.29868-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/fw.c | 4 ++-- drivers/net/wireless/realtek/rtw89/mac.c | 2 +- drivers/net/wireless/realtek/rtw89/mac80211.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 41365ffb7e5e..b1a886898c5a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3023,7 +3023,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_SCAN_OFFLOAD, RTW89_FW_FEATURE_TX_WAKE, RTW89_FW_FEATURE_CRASH_TRIGGER, - RTW89_FW_FEATURE_PACKET_DROP, + RTW89_FW_FEATURE_NO_PACKET_DROP, RTW89_FW_FEATURE_NO_DEEP_PS, RTW89_FW_FEATURE_NO_LPS_PG, }; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index dceb033acbc8..1a4ff24078fb 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -235,6 +235,7 @@ static bool __fw_feat_cond_ ## __cond(u32 suit_ver_code, u32 comp_ver_code) \ __DEF_FW_FEAT_COND(ge, >=); /* greater or equal */ __DEF_FW_FEAT_COND(le, <=); /* less or equal */ +__DEF_FW_FEAT_COND(lt, <); /* less than */ struct __fw_feat_cfg { enum rtw89_core_chip_id chip_id; @@ -256,12 +257,11 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8852A, ge, 0, 13, 35, 0, SCAN_OFFLOAD), __CFG_FW_FEAT(RTL8852A, ge, 0, 13, 35, 0, TX_WAKE), __CFG_FW_FEAT(RTL8852A, ge, 0, 13, 36, 0, CRASH_TRIGGER), - __CFG_FW_FEAT(RTL8852A, ge, 0, 13, 38, 0, PACKET_DROP), + __CFG_FW_FEAT(RTL8852A, lt, 0, 13, 38, 0, NO_PACKET_DROP), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, NO_LPS_PG), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, TX_WAKE), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, CRASH_TRIGGER), __CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, SCAN_OFFLOAD), - __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 20, 0, PACKET_DROP), __CFG_FW_FEAT(RTL8852C, le, 0, 27, 33, 0, NO_DEEP_PS), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 34, 0, TX_WAKE), __CFG_FW_FEAT(RTL8852C, ge, 0, 27, 36, 0, SCAN_OFFLOAD), diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 2e2a2b6eab09..3d1e4ffef1b1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5426,7 +5426,7 @@ int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev, for (i = 0; i < try_cnt; i++) { ret = read_poll_timeout(mac_is_txq_empty, empty, empty, 50, 50000, false, rtwdev); - if (ret) + if (ret && !RTW89_CHK_FW_FEATURE(NO_PACKET_DROP, &rtwdev->fw)) rtw89_fw_h2c_pkt_drop(rtwdev, ¶ms); else return 0; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index d43281f7335b..367a7bf319da 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -676,7 +676,7 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtw89_leave_lps(rtwdev); rtw89_hci_flush_queues(rtwdev, queues, drop); - if (drop && RTW89_CHK_FW_FEATURE(PACKET_DROP, &rtwdev->fw)) + if (drop && !RTW89_CHK_FW_FEATURE(NO_PACKET_DROP, &rtwdev->fw)) __rtw89_drop_packets(rtwdev, vif); else rtw89_mac_flush_txq(rtwdev, queues, drop); -- cgit v1.2.3 From 4a51e66fe96dfde76894c4eb445ef1b14d312014 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 2 Feb 2023 21:58:10 +0100 Subject: wifi: wcn36xx: Slightly optimize PREPARE_HAL_BUF() In most (likely all) cases, INIT_HAL_MSG() is called before PREPARE_HAL_BUF(). In such cases calling memset() is useless because: msg_body.header.len = sizeof(msg_body) So, instead of writing twice the memory, we just have a sanity check to make sure that some potential trailing memory is zeroed. It even gives the opportunity to see that by itself and optimize it away. Signed-off-by: Christophe JAILLET Acked-by: Loic Poulain Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/7d8ab7fee45222cdbaf80c507525f2d3941587c1.1675371372.git.christophe.jaillet@wanadoo.fr --- drivers/net/wireless/ath/wcn36xx/smd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 566f0b9c1584..17e1919d1cd8 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -475,8 +475,8 @@ out: #define PREPARE_HAL_BUF(send_buf, msg_body) \ do { \ - memset(send_buf, 0, msg_body.header.len); \ - memcpy(send_buf, &msg_body, sizeof(msg_body)); \ + memcpy_and_pad(send_buf, msg_body.header.len, \ + &msg_body, sizeof(msg_body), 0); \ } while (0) \ #define PREPARE_HAL_PTT_MSG_BUF(send_buf, p_msg_body) \ -- cgit v1.2.3 From 28013c35629647679e10cb1bbf36c3feec2af756 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 22 Feb 2023 18:40:14 +0200 Subject: wifi: ath12k: remove memset with byte count of 278528 Sparse warns: drivers/net/wireless/ath/ath12k/dp.c:1471:15: warning: memset with byte count of 278528 There's no need to use memset() here, instead call dma_alloc_coherent() with __GFP_ZERO. While at it, remove an extra line before the error handler. Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230222164014.860-1-kvalo@kernel.org --- drivers/net/wireless/ath/ath12k/dp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 9926d81c5fe4..ae1645d0f42a 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -1461,15 +1461,12 @@ static int ath12k_dp_reoq_lut_setup(struct ath12k_base *ab) dp->reoq_lut.vaddr = dma_alloc_coherent(ab->dev, DP_REOQ_LUT_SIZE, &dp->reoq_lut.paddr, - GFP_KERNEL); - + GFP_KERNEL | __GFP_ZERO); if (!dp->reoq_lut.vaddr) { ath12k_warn(ab, "failed to allocate memory for reoq table"); return -ENOMEM; } - memset(dp->reoq_lut.vaddr, 0, DP_REOQ_LUT_SIZE); - ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE0(ab), dp->reoq_lut.paddr); return 0; -- cgit v1.2.3 From 0f485805d008aa56644f68179a7e6579fc1515e7 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 17:00:33 +0900 Subject: wifi: brcmfmac: acpi: Add support for fetching Apple ACPI properties On DT platforms, the module-instance and antenna-sku-info properties are passed in the DT. On ACPI platforms, module-instance is passed via the analogous Apple device property mechanism, while the antenna SKU info is instead obtained via an ACPI method that grabs it from non-volatile storage. Add support for this, to allow proper firmware selection on Apple platforms. Signed-off-by: Hector Martin Reviewed-by: Julian Calaby Acked-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214080034.3828-2-marcan@marcan.st --- .../wireless/broadcom/brcm80211/brcmfmac/Makefile | 2 + .../wireless/broadcom/brcm80211/brcmfmac/acpi.c | 51 ++++++++++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/common.c | 1 + .../wireless/broadcom/brcm80211/brcmfmac/common.h | 9 ++++ 4 files changed, 63 insertions(+) create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/acpi.c (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index 0e996cf24f88..dc6d27a36faa 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -48,6 +48,8 @@ brcmfmac-$(CONFIG_OF) += \ of.o brcmfmac-$(CONFIG_DMI) += \ dmi.o +brcmfmac-$(CONFIG_ACPI) += \ + acpi.o ifeq ($(CONFIG_BRCMFMAC),m) obj-m += wcc/ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/acpi.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/acpi.c new file mode 100644 index 000000000000..c4a54861bfb4 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/acpi.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright The Asahi Linux Contributors + */ + +#include +#include "debug.h" +#include "core.h" +#include "common.h" + +void brcmf_acpi_probe(struct device *dev, enum brcmf_bus_type bus_type, + struct brcmf_mp_device *settings) +{ + acpi_status status; + const union acpi_object *o; + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_device *adev = ACPI_COMPANION(dev); + + if (!adev) + return; + + if (!ACPI_FAILURE(acpi_dev_get_property(adev, "module-instance", + ACPI_TYPE_STRING, &o))) { + brcmf_dbg(INFO, "ACPI module-instance=%s\n", o->string.pointer); + settings->board_type = devm_kasprintf(dev, GFP_KERNEL, + "apple,%s", + o->string.pointer); + } else { + brcmf_dbg(INFO, "No ACPI module-instance\n"); + return; + } + + status = acpi_evaluate_object(adev->handle, "RWCV", NULL, &buf); + o = buf.pointer; + if (!ACPI_FAILURE(status) && o && o->type == ACPI_TYPE_BUFFER && + o->buffer.length >= 2) { + char *antenna_sku = devm_kzalloc(dev, 3, GFP_KERNEL); + + if (antenna_sku) { + memcpy(antenna_sku, o->buffer.pointer, 2); + brcmf_dbg(INFO, "ACPI RWCV data=%*phN antenna-sku=%s\n", + (int)o->buffer.length, o->buffer.pointer, + antenna_sku); + settings->antenna_sku = antenna_sku; + } + + kfree(buf.pointer); + } else { + brcmf_dbg(INFO, "No ACPI antenna-sku\n"); + } +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index f235beaddddb..4fea6f0ea8c8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -487,6 +487,7 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, /* No platform data for this device, try OF and DMI data */ brcmf_dmi_probe(settings, chip, chiprev); brcmf_of_probe(dev, bus_type, settings); + brcmf_acpi_probe(dev, bus_type, settings); } return settings; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index aa25abffcc7d..7167fd4f8c63 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -77,6 +77,15 @@ static inline void brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev) {} #endif +#ifdef CONFIG_ACPI +void brcmf_acpi_probe(struct device *dev, enum brcmf_bus_type bus_type, + struct brcmf_mp_device *settings); +#else +static inline void brcmf_acpi_probe(struct device *dev, + enum brcmf_bus_type bus_type, + struct brcmf_mp_device *settings) {} +#endif + u8 brcmf_map_prio_to_prec(void *cfg, u8 prio); u8 brcmf_map_prio_to_aci(void *cfg, u8 prio); -- cgit v1.2.3 From 91918ce88d9fef408bb12c46a27c73d79b604c20 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 17:00:34 +0900 Subject: wifi: brcmfmac: pcie: Provide a buffer of random bytes to the device Newer Apple firmwares on chipsets without a hardware RNG require the host to provide a buffer of 256 random bytes to the device on initialization. This buffer is present immediately before NVRAM, suffixed by a footer containing a magic number and the buffer length. This won't affect chips/firmwares that do not use this feature, so do it unconditionally for all Apple platforms (those with an Apple OTP). Reviewed-by: Linus Walleij Signed-off-by: Hector Martin Reviewed-by: Julian Calaby Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214080034.3828-3-marcan@marcan.st --- .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index a9b9b2dc62d4..2835ef4edb18 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -1653,6 +1654,13 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, return 0; } +struct brcmf_random_seed_footer { + __le32 length; + __le32 magic; +}; + +#define BRCMF_RANDOM_SEED_MAGIC 0xfeedc0de +#define BRCMF_RANDOM_SEED_LENGTH 0x100 static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, const struct firmware *fw, void *nvram, @@ -1689,6 +1697,30 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, nvram_len; memcpy_toio(devinfo->tcm + address, nvram, nvram_len); brcmf_fw_nvram_free(nvram); + + if (devinfo->otp.valid) { + size_t rand_len = BRCMF_RANDOM_SEED_LENGTH; + struct brcmf_random_seed_footer footer = { + .length = cpu_to_le32(rand_len), + .magic = cpu_to_le32(BRCMF_RANDOM_SEED_MAGIC), + }; + void *randbuf; + + /* Some Apple chips/firmwares expect a buffer of random + * data to be present before NVRAM + */ + brcmf_dbg(PCIE, "Download random seed\n"); + + address -= sizeof(footer); + memcpy_toio(devinfo->tcm + address, &footer, + sizeof(footer)); + + address -= rand_len; + randbuf = kzalloc(rand_len, GFP_KERNEL); + get_random_bytes(randbuf, rand_len); + memcpy_toio(devinfo->tcm + address, randbuf, rand_len); + kfree(randbuf); + } } else { brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", devinfo->nvram_name); -- cgit v1.2.3 From ec52d77d077529f198fd874c550a26b9cc86a331 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Tue, 24 Jan 2023 10:42:48 +0000 Subject: wifi: brcmfmac: support CQM RSSI notification with older firmware Using the BCM4339 firmware from linux-firmware (version "BCM4339/2 wl0: Sep 5 2019 11:05:52 version 6.37.39.113 (r722271 CY)" from cypress/cyfmac4339-sdio.bin) the RSSI respose is only 4 bytes, which results in an error being logged. It seems that older devices send only the RSSI field and neither SNR nor noise is included. Handle this by accepting a 4 byte message and reading only the RSSI from it. Fixes: 7dd56ea45a66 ("brcmfmac: add support for CQM RSSI notifications") Signed-off-by: John Keeping Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230124104248.2917465-1-john@metanate.com --- .../net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index a9690ec4c850..d80d2b9b15c7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6489,18 +6489,20 @@ static s32 brcmf_notify_rssi(struct brcmf_if *ifp, { struct brcmf_cfg80211_vif *vif = ifp->vif; struct brcmf_rssi_be *info = data; - s32 rssi, snr, noise; + s32 rssi, snr = 0, noise = 0; s32 low, high, last; - if (e->datalen < sizeof(*info)) { + if (e->datalen >= sizeof(*info)) { + rssi = be32_to_cpu(info->rssi); + snr = be32_to_cpu(info->snr); + noise = be32_to_cpu(info->noise); + } else if (e->datalen >= sizeof(rssi)) { + rssi = be32_to_cpu(*(__be32 *)data); + } else { brcmf_err("insufficient RSSI event data\n"); return 0; } - rssi = be32_to_cpu(info->rssi); - snr = be32_to_cpu(info->snr); - noise = be32_to_cpu(info->noise); - low = vif->cqm_rssi_low; high = vif->cqm_rssi_high; last = vif->cqm_rssi_last; -- cgit v1.2.3 From 75c4a8154cb6c7239fb55d5550f481f6765fb83c Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Fri, 24 Feb 2023 12:28:05 +0200 Subject: wifi: ath6kl: reduce WARN to dev_dbg() in callback The warn is triggered on a known race condition, documented in the code above the test, that is correctly handled. Using WARN() hinders automated testing. Reducing severity. Fixes: de2070fc4aa7 ("ath6kl: Fix kernel panic on continuous driver load/unload") Reported-and-tested-by: syzbot+555908813b2ea35dae9a@syzkaller.appspotmail.com Signed-off-by: Oliver Neukum Signed-off-by: Fedor Pchelkin Signed-off-by: Alexey Khoroshilov Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230126182431.867984-1-pchelkin@ispras.ru --- drivers/net/wireless/ath/ath6kl/htc_pipe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index c68848819a52..9b88d96bfe96 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -960,8 +960,8 @@ static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb, * Thus the possibility of ar->htc_target being NULL * via ath6kl_recv_complete -> ath6kl_usb_io_comp_work. */ - if (WARN_ON_ONCE(!target)) { - ath6kl_err("Target not yet initialized\n"); + if (!target) { + ath6kl_dbg(ATH6KL_DBG_HTC, "Target not yet initialized\n"); status = -EINVAL; goto free_skb; } -- cgit v1.2.3 From a96f10422e74cde27c100b321b127ec32ae75747 Mon Sep 17 00:00:00 2001 From: Muna Sinada Date: Fri, 24 Feb 2023 12:28:03 +0200 Subject: wifi: ath11k: modify accessor macros to match index size HE PHY is only 11 bytes, therefore it should be using byte indexes instead of dword. Change corresponding macros to reflect this. Signed-off-by: Muna Sinada Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1666128501-12364-2-git-send-email-quic_msinada@quicinc.com --- drivers/net/wireless/ath/ath11k/wmi.h | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 2273d885f7fa..9f50be411a8c 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2859,30 +2859,32 @@ struct rx_reorder_queue_remove_params { #define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2) #define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3) -#define HECAP_PHYDWORD_0 0 -#define HECAP_PHYDWORD_1 1 -#define HECAP_PHYDWORD_2 2 +#define HE_PHYCAP_BYTE_0 0 +#define HE_PHYCAP_BYTE_1 1 +#define HE_PHYCAP_BYTE_2 2 +#define HE_PHYCAP_BYTE_3 3 +#define HE_PHYCAP_BYTE_4 4 -#define HECAP_PHY_SU_BFER BIT(31) +#define HECAP_PHY_SU_BFER BIT(7) #define HECAP_PHY_SU_BFEE BIT(0) #define HECAP_PHY_MU_BFER BIT(1) -#define HECAP_PHY_UL_MUMIMO BIT(22) -#define HECAP_PHY_UL_MUOFDMA BIT(23) +#define HECAP_PHY_UL_MUMIMO BIT(6) +#define HECAP_PHY_UL_MUOFDMA BIT(7) #define HECAP_PHY_SUBFMR_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HECAP_PHYDWORD_0]) + FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HE_PHYCAP_BYTE_3]) #define HECAP_PHY_SUBFME_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HECAP_PHYDWORD_1]) + FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HE_PHYCAP_BYTE_4]) #define HECAP_PHY_MUBFMR_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HECAP_PHYDWORD_1]) + FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HE_PHYCAP_BYTE_4]) #define HECAP_PHY_ULMUMIMO_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HECAP_PHYDWORD_0]) + FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HE_PHYCAP_BYTE_2]) #define HECAP_PHY_ULOFDMA_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HECAP_PHYDWORD_0]) + FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HE_PHYCAP_BYTE_2]) #define HE_MODE_SU_TX_BFEE BIT(0) #define HE_MODE_SU_TX_BFER BIT(1) -- cgit v1.2.3 From 38dfe775d0abf511341f37c1cb77b919a3ad410b Mon Sep 17 00:00:00 2001 From: Muna Sinada Date: Fri, 24 Feb 2023 12:28:04 +0200 Subject: wifi: ath11k: push MU-MIMO params from hostapd to hardware In the previous behaviour only HE IE in management frames are changed regarding MU-MIMO configurations and not in hardware. Adding push of MU-MIMO configurations to the hardware as well. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00356-QCAHKSWPL_SILICONZ-1 Co-developed-by: Anilkumar Kolli Signed-off-by: Anilkumar Kolli Signed-off-by: Muna Sinada Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1666128501-12364-3-git-send-email-quic_msinada@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 200 +++++++++++++++++++++------------- drivers/net/wireless/ath/ath11k/wmi.h | 3 + 2 files changed, 130 insertions(+), 73 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 110a38cce0a7..7c4f424180dc 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -2699,6 +2699,117 @@ static int ath11k_setup_peer_smps(struct ath11k *ar, struct ath11k_vif *arvif, ath11k_smps_map[smps]); } +static bool ath11k_mac_set_he_txbf_conf(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + u32 param, value; + int ret; + + if (!arvif->vif->bss_conf.he_support) + return true; + + param = WMI_VDEV_PARAM_SET_HEMU_MODE; + value = 0; + if (arvif->vif->bss_conf.he_su_beamformer) { + value |= FIELD_PREP(HE_MODE_SU_TX_BFER, HE_SU_BFER_ENABLE); + if (arvif->vif->bss_conf.he_mu_beamformer && + arvif->vdev_type == WMI_VDEV_TYPE_AP) + value |= FIELD_PREP(HE_MODE_MU_TX_BFER, HE_MU_BFER_ENABLE); + } + + if (arvif->vif->type != NL80211_IFTYPE_MESH_POINT) { + value |= FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | + FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); + + if (arvif->vif->bss_conf.he_full_ul_mumimo) + value |= FIELD_PREP(HE_MODE_UL_MUMIMO, HE_UL_MUMIMO_ENABLE); + + if (arvif->vif->bss_conf.he_su_beamformee) + value |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE); + } + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d HE MU mode: %d\n", + arvif->vdev_id, ret); + return false; + } + + param = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE; + value = FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) | + FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE, + HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE); + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param, value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d sounding mode: %d\n", + arvif->vdev_id, ret); + return false; + } + return true; +} + +static bool ath11k_mac_vif_recalc_sta_he_txbf(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta_he_cap *he_cap) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ieee80211_he_cap_elem he_cap_elem = {0}; + struct ieee80211_sta_he_cap *cap_band = NULL; + struct cfg80211_chan_def def; + u32 param = WMI_VDEV_PARAM_SET_HEMU_MODE; + u32 hemode = 0; + int ret; + + if (!vif->bss_conf.he_support) + return true; + + if (vif->type != NL80211_IFTYPE_STATION) + return false; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return false; + + if (def.chan->band == NL80211_BAND_2GHZ) + cap_band = &ar->mac.iftype[NL80211_BAND_2GHZ][vif->type].he_cap; + else + cap_band = &ar->mac.iftype[NL80211_BAND_5GHZ][vif->type].he_cap; + + memcpy(&he_cap_elem, &cap_band->he_cap_elem, sizeof(he_cap_elem)); + + if (HECAP_PHY_SUBFME_GET(he_cap_elem.phy_cap_info)) { + if (HECAP_PHY_SUBFMR_GET(he_cap->he_cap_elem.phy_cap_info)) + hemode |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE); + if (HECAP_PHY_MUBFMR_GET(he_cap->he_cap_elem.phy_cap_info)) + hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE); + } + + if (vif->type != NL80211_IFTYPE_MESH_POINT) { + hemode |= FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | + FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); + + if (HECAP_PHY_ULMUMIMO_GET(he_cap_elem.phy_cap_info)) + if (HECAP_PHY_ULMUMIMO_GET(he_cap->he_cap_elem.phy_cap_info)) + hemode |= FIELD_PREP(HE_MODE_UL_MUMIMO, + HE_UL_MUMIMO_ENABLE); + + if (FIELD_GET(HE_MODE_MU_TX_BFEE, hemode)) + hemode |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE); + + if (FIELD_GET(HE_MODE_MU_TX_BFER, hemode)) + hemode |= FIELD_PREP(HE_MODE_SU_TX_BFER, HE_SU_BFER_ENABLE); + } + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, hemode); + if (ret) { + ath11k_warn(ar->ab, "failed to submit vdev param txbf 0x%x: %d\n", + hemode, ret); + return false; + } + + return true; +} + static void ath11k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) @@ -2709,6 +2820,7 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_sta *ap_sta; struct ath11k_peer *peer; bool is_auth = false; + struct ieee80211_sta_he_cap he_cap; int ret; lockdep_assert_held(&ar->conf_mutex); @@ -2726,6 +2838,9 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw, return; } + /* he_cap here is updated at assoc success for sta mode only */ + he_cap = ap_sta->deflink.he_cap; + ath11k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg, false); rcu_read_unlock(); @@ -2753,6 +2868,12 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw, return; } + if (!ath11k_mac_vif_recalc_sta_he_txbf(ar, vif, &he_cap)) { + ath11k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM\n", + arvif->vdev_id, bss_conf->bssid); + return; + } + WARN_ON(arvif->is_up); arvif->aid = vif->cfg.aid; @@ -3202,6 +3323,8 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, ether_addr_copy(arvif->bssid, info->bssid); if (changed & BSS_CHANGED_BEACON_ENABLED) { + if (info->enable_beacon) + ath11k_mac_set_he_txbf_conf(arvif); ath11k_control_beaconing(arvif, info); if (arvif->is_up && vif->bss_conf.he_support && @@ -5392,6 +5515,10 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar, he_cap_elem->mac_cap_info[1] &= IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK; + he_cap_elem->phy_cap_info[0] &= + ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + he_cap_elem->phy_cap_info[0] &= + ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; he_cap_elem->phy_cap_info[5] &= ~IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK; @@ -6026,69 +6153,6 @@ ath11k_mac_setup_vdev_create_params(struct ath11k_vif *arvif, } } -static u32 -ath11k_mac_prepare_he_mode(struct ath11k_pdev *pdev, u32 viftype) -{ - struct ath11k_pdev_cap *pdev_cap = &pdev->cap; - struct ath11k_band_cap *cap_band = NULL; - u32 *hecap_phy_ptr = NULL; - u32 hemode = 0; - - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) - cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; - else - cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; - - hecap_phy_ptr = &cap_band->he_cap_phy_info[0]; - - hemode = FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE) | - FIELD_PREP(HE_MODE_SU_TX_BFER, HECAP_PHY_SUBFMR_GET(hecap_phy_ptr)) | - FIELD_PREP(HE_MODE_UL_MUMIMO, HECAP_PHY_ULMUMIMO_GET(hecap_phy_ptr)); - - /* TODO WDS and other modes */ - if (viftype == NL80211_IFTYPE_AP) { - hemode |= FIELD_PREP(HE_MODE_MU_TX_BFER, - HECAP_PHY_MUBFMR_GET(hecap_phy_ptr)) | - FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | - FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); - } else { - hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE); - } - - return hemode; -} - -static int ath11k_set_he_mu_sounding_mode(struct ath11k *ar, - struct ath11k_vif *arvif) -{ - u32 param_id, param_value; - struct ath11k_base *ab = ar->ab; - int ret = 0; - - param_id = WMI_VDEV_PARAM_SET_HEMU_MODE; - param_value = ath11k_mac_prepare_he_mode(ar->pdev, arvif->vif->type); - ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, - param_id, param_value); - if (ret) { - ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d param_value %x\n", - arvif->vdev_id, ret, param_value); - return ret; - } - param_id = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE; - param_value = - FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) | - FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE, - HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE); - ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, - param_id, param_value); - if (ret) { - ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d\n", - arvif->vdev_id, ret); - return ret; - } - return ret; -} - static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -6757,7 +6821,6 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, struct ath11k_base *ab = ar->ab; struct wmi_vdev_start_req_arg arg = {}; const struct cfg80211_chan_def *chandef = &ctx->def; - int he_support = arvif->vif->bss_conf.he_support; int ret = 0; lockdep_assert_held(&ar->conf_mutex); @@ -6798,15 +6861,6 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, spin_lock_bh(&ab->base_lock); arg.regdomain = ar->ab->dfs_region; spin_unlock_bh(&ab->base_lock); - - if (he_support) { - ret = ath11k_set_he_mu_sounding_mode(ar, arvif); - if (ret) { - ath11k_warn(ar->ab, "failed to set he mode vdev %i\n", - arg.vdev_id); - return ret; - } - } } arg.channel.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 9f50be411a8c..d3b74f1346f5 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2897,8 +2897,11 @@ struct rx_reorder_queue_remove_params { #define HE_DL_MUOFDMA_ENABLE 1 #define HE_UL_MUOFDMA_ENABLE 1 #define HE_DL_MUMIMO_ENABLE 1 +#define HE_UL_MUMIMO_ENABLE 1 #define HE_MU_BFEE_ENABLE 1 #define HE_SU_BFEE_ENABLE 1 +#define HE_MU_BFER_ENABLE 1 +#define HE_SU_BFER_ENABLE 1 #define HE_VHT_SOUNDING_MODE_ENABLE 1 #define HE_SU_MU_SOUNDING_MODE_ENABLE 1 -- cgit v1.2.3 From 8077c1bbbc28e527fb29143c46f32c6a9d6cadf0 Mon Sep 17 00:00:00 2001 From: Muna Sinada Date: Fri, 24 Feb 2023 12:28:04 +0200 Subject: wifi: ath11k: move HE MCS mapper to a separate function Move HE MCS mapper to a separate function and call new function in ath11k_mac_copy_he_cap(). Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00356-QCAHKSWPL_SILICONZ-1 Signed-off-by: Muna Sinada Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1666128501-12364-4-git-send-email-quic_msinada@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 7c4f424180dc..0835ab8b389a 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -5483,6 +5483,27 @@ static __le16 ath11k_mac_setup_he_6ghz_cap(struct ath11k_pdev_cap *pcap, return cpu_to_le16(bcap->he_6ghz_capa); } +static void ath11k_mac_set_hemcsmap(struct ath11k *ar, + struct ath11k_pdev_cap *cap, + struct ieee80211_sta_he_cap *he_cap, + int band) +{ + struct ath11k_band_cap *band_cap = &cap->band[band]; + + he_cap->he_mcs_nss_supp.rx_mcs_80 = + cpu_to_le16(band_cap->he_mcs & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80 = + cpu_to_le16(band_cap->he_mcs & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_160 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_160 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_80p80 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80p80 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); +} + static int ath11k_mac_copy_he_cap(struct ath11k *ar, struct ath11k_pdev_cap *cap, struct ieee80211_sband_iftype_data *data, @@ -5544,18 +5565,7 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar, break; } - he_cap->he_mcs_nss_supp.rx_mcs_80 = - cpu_to_le16(band_cap->he_mcs & 0xffff); - he_cap->he_mcs_nss_supp.tx_mcs_80 = - cpu_to_le16(band_cap->he_mcs & 0xffff); - he_cap->he_mcs_nss_supp.rx_mcs_160 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); - he_cap->he_mcs_nss_supp.tx_mcs_160 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); - he_cap->he_mcs_nss_supp.rx_mcs_80p80 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); - he_cap->he_mcs_nss_supp.tx_mcs_80p80 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + ath11k_mac_set_hemcsmap(ar, cap, he_cap, band); memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); if (he_cap_elem->phy_cap_info[6] & -- cgit v1.2.3 From ebf82988f844dd98e6b007cffcc5e95986056995 Mon Sep 17 00:00:00 2001 From: Muna Sinada Date: Fri, 24 Feb 2023 12:28:04 +0200 Subject: wifi: ath11k: generate rx and tx mcs maps for supported HE mcs Generate rx and tx mcs maps in ath11k_mac_set_hemcsmap() and set them in supported mcs/nss for HE capabilities. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.4.0.1-00356-QCAHKSWPL_SILICONZ-1 Signed-off-by: Muna Sinada Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/1666128501-12364-5-git-send-email-quic_msinada@quicinc.com --- drivers/net/wireless/ath/ath11k/mac.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 0835ab8b389a..fd1a23ead5e2 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -5488,20 +5488,36 @@ static void ath11k_mac_set_hemcsmap(struct ath11k *ar, struct ieee80211_sta_he_cap *he_cap, int band) { - struct ath11k_band_cap *band_cap = &cap->band[band]; + u16 txmcs_map, rxmcs_map; + u32 i; + rxmcs_map = 0; + txmcs_map = 0; + for (i = 0; i < 8; i++) { + if (i < ar->num_tx_chains && + (ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift) & BIT(i)) + txmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); + else + txmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2); + + if (i < ar->num_rx_chains && + (ar->cfg_rx_chainmask >> cap->tx_chain_mask_shift) & BIT(i)) + rxmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); + else + rxmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2); + } he_cap->he_mcs_nss_supp.rx_mcs_80 = - cpu_to_le16(band_cap->he_mcs & 0xffff); + cpu_to_le16(rxmcs_map & 0xffff); he_cap->he_mcs_nss_supp.tx_mcs_80 = - cpu_to_le16(band_cap->he_mcs & 0xffff); + cpu_to_le16(txmcs_map & 0xffff); he_cap->he_mcs_nss_supp.rx_mcs_160 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + cpu_to_le16(rxmcs_map & 0xffff); he_cap->he_mcs_nss_supp.tx_mcs_160 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + cpu_to_le16(txmcs_map & 0xffff); he_cap->he_mcs_nss_supp.rx_mcs_80p80 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + cpu_to_le16(rxmcs_map & 0xffff); he_cap->he_mcs_nss_supp.tx_mcs_80p80 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + cpu_to_le16(txmcs_map & 0xffff); } static int ath11k_mac_copy_he_cap(struct ath11k *ar, -- cgit v1.2.3 From 3c7c07ca7ab144ef25402858078b27806322b752 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:14 +0900 Subject: wifi: brcmfmac: chip: Only disable D11 cores; handle an arbitrary number At least on BCM4387, the D11 cores are held in reset on cold startup and firmware expects to release reset itself. Just assert reset here and let firmware deassert it. Premature deassertion results in the firmware failing to initialize properly some of the time, with strange AXI bus errors. Also, BCM4387 has 3 cores, up from 2. The logic for handling that is in brcmf_chip_ai_resetcore(), but since we aren't using that any more, just handle it here. Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-1-marcan@marcan.st --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 8073f31be27d..a6239051404b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -1292,15 +1292,18 @@ static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) static inline void brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) { + int i; struct brcmf_core *core; brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); - brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN); + /* Disable the cores only and let the firmware enable them. + * Releasing reset ourselves breaks BCM4387 in weird ways. + */ + for (i = 0; (core = brcmf_chip_get_d11core(&chip->pub, i)); i++) + brcmf_chip_coredisable(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); } static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) -- cgit v1.2.3 From 098e0b105ce1047ad9984dc79287573e313b1232 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:15 +0900 Subject: wifi: brcmfmac: chip: Handle 1024-unit sizes for TCM blocks BCM4387 has trailing odd-sized blocks as part of TCM which have their size described as a multiple of 1024 instead of 8192. Handle this so we can compute the TCM size properly. Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-2-marcan@marcan.st --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index a6239051404b..50e0c015cf43 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -212,8 +212,9 @@ struct sbsocramregs { #define ARMCR4_TCBANB_MASK 0xf #define ARMCR4_TCBANB_SHIFT 0 -#define ARMCR4_BSZ_MASK 0x3f +#define ARMCR4_BSZ_MASK 0x7f #define ARMCR4_BSZ_MULT 8192 +#define ARMCR4_BLK_1K_MASK 0x200 struct brcmf_core_priv { struct brcmf_core pub; @@ -684,6 +685,7 @@ static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) u32 nbb; u32 totb; u32 bxinfo; + u32 blksize; u32 idx; corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP); @@ -695,7 +697,11 @@ static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) for (idx = 0; idx < totb; idx++) { brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx); bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO); - memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; + blksize = ARMCR4_BSZ_MULT; + if (bxinfo & ARMCR4_BLK_1K_MASK) + blksize >>= 3; + + memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * blksize; } return memsize; -- cgit v1.2.3 From 398ce273d6b16a57dee99e4054a2be37f0a958ed Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:16 +0900 Subject: wifi: brcmfmac: cfg80211: Add support for scan params v2 This new API version is required for at least the BCM4387 firmware. Add support for it, with a fallback to the v1 API. Acked-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-3-marcan@marcan.st --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 245 +++++++++++++-------- .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 1 + .../wireless/broadcom/brcm80211/brcmfmac/feature.h | 4 +- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 49 ++++- 4 files changed, 209 insertions(+), 90 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index d80d2b9b15c7..77706e204a8f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1039,12 +1039,134 @@ void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) } } +static void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le, + struct brcmf_scan_params_le *params_le) +{ + size_t params_size; + u32 ch; + int n_channels, n_ssids; + + memcpy(¶ms_le->ssid_le, ¶ms_v2_le->ssid_le, + sizeof(params_le->ssid_le)); + memcpy(¶ms_le->bssid, ¶ms_v2_le->bssid, + sizeof(params_le->bssid)); + + params_le->bss_type = params_v2_le->bss_type; + params_le->scan_type = le32_to_cpu(params_v2_le->scan_type); + params_le->nprobes = params_v2_le->nprobes; + params_le->active_time = params_v2_le->active_time; + params_le->passive_time = params_v2_le->passive_time; + params_le->home_time = params_v2_le->home_time; + params_le->channel_num = params_v2_le->channel_num; + + ch = le32_to_cpu(params_v2_le->channel_num); + n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK; + n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT; + + params_size = sizeof(u16) * n_channels; + if (n_ssids > 0) { + params_size = roundup(params_size, sizeof(u32)); + params_size += sizeof(struct brcmf_ssid_le) * n_ssids; + } + + memcpy(¶ms_le->channel_list[0], + ¶ms_v2_le->channel_list[0], params_size); +} + +static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, + struct brcmf_scan_params_v2_le *params_le, + struct cfg80211_scan_request *request) +{ + u32 n_ssids; + u32 n_channels; + s32 i; + s32 offset; + u16 chanspec; + char *ptr; + int length; + struct brcmf_ssid_le ssid_le; + + eth_broadcast_addr(params_le->bssid); + + length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE; + + params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2); + params_le->bss_type = DOT11_BSSTYPE_ANY; + params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE); + params_le->channel_num = 0; + params_le->nprobes = cpu_to_le32(-1); + params_le->active_time = cpu_to_le32(-1); + params_le->passive_time = cpu_to_le32(-1); + params_le->home_time = cpu_to_le32(-1); + memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); + + /* Scan abort */ + if (!request) { + length += sizeof(u16); + params_le->channel_num = cpu_to_le32(1); + params_le->channel_list[0] = cpu_to_le16(-1); + params_le->length = cpu_to_le16(length); + return; + } + + n_ssids = request->n_ssids; + n_channels = request->n_channels; + + /* Copy channel array if applicable */ + brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n", + n_channels); + if (n_channels > 0) { + length += roundup(sizeof(u16) * n_channels, sizeof(u32)); + for (i = 0; i < n_channels; i++) { + chanspec = channel_to_chanspec(&cfg->d11inf, + request->channels[i]); + brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n", + request->channels[i]->hw_value, chanspec); + params_le->channel_list[i] = cpu_to_le16(chanspec); + } + } else { + brcmf_dbg(SCAN, "Scanning all channels\n"); + } + + /* Copy ssid array if applicable */ + brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids); + if (n_ssids > 0) { + offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) + + n_channels * sizeof(u16); + offset = roundup(offset, sizeof(u32)); + length += sizeof(ssid_le) * n_ssids, + ptr = (char *)params_le + offset; + for (i = 0; i < n_ssids; i++) { + memset(&ssid_le, 0, sizeof(ssid_le)); + ssid_le.SSID_len = + cpu_to_le32(request->ssids[i].ssid_len); + memcpy(ssid_le.SSID, request->ssids[i].ssid, + request->ssids[i].ssid_len); + if (!ssid_le.SSID_len) + brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); + else + brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n", + i, ssid_le.SSID, ssid_le.SSID_len); + memcpy(ptr, &ssid_le, sizeof(ssid_le)); + ptr += sizeof(ssid_le); + } + } else { + brcmf_dbg(SCAN, "Performing passive scan\n"); + params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE); + } + params_le->length = cpu_to_le16(length); + /* Adding mask to channel numbers */ + params_le->channel_num = + cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | + (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); +} + s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, bool aborted, bool fw_abort) { struct brcmf_pub *drvr = cfg->pub; - struct brcmf_scan_params_le params_le; + struct brcmf_scan_params_v2_le params_v2_le; struct cfg80211_scan_request *scan_request; u64 reqid; u32 bucket; @@ -1063,20 +1185,23 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, if (fw_abort) { /* Do a scan abort to stop the driver's scan engine */ brcmf_dbg(SCAN, "ABORT scan in firmware\n"); - memset(¶ms_le, 0, sizeof(params_le)); - eth_broadcast_addr(params_le.bssid); - params_le.bss_type = DOT11_BSSTYPE_ANY; - params_le.scan_type = 0; - params_le.channel_num = cpu_to_le32(1); - params_le.nprobes = cpu_to_le32(1); - params_le.active_time = cpu_to_le32(-1); - params_le.passive_time = cpu_to_le32(-1); - params_le.home_time = cpu_to_le32(-1); - /* Scan is aborted by setting channel_list[0] to -1 */ - params_le.channel_list[0] = cpu_to_le16(-1); + + brcmf_escan_prep(cfg, ¶ms_v2_le, NULL); + /* E-Scan (or anyother type) can be aborted by SCAN */ - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, - ¶ms_le, sizeof(params_le)); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) { + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, + ¶ms_v2_le, + sizeof(params_v2_le)); + } else { + struct brcmf_scan_params_le params_le; + + brcmf_scan_params_v2_to_v1(¶ms_v2_le, ¶ms_le); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, + ¶ms_le, + sizeof(params_le)); + } + if (err) bphy_err(drvr, "Scan abort failed\n"); } @@ -1295,83 +1420,13 @@ done: return err; } -static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, - struct brcmf_scan_params_le *params_le, - struct cfg80211_scan_request *request) -{ - u32 n_ssids; - u32 n_channels; - s32 i; - s32 offset; - u16 chanspec; - char *ptr; - struct brcmf_ssid_le ssid_le; - - eth_broadcast_addr(params_le->bssid); - params_le->bss_type = DOT11_BSSTYPE_ANY; - params_le->scan_type = BRCMF_SCANTYPE_ACTIVE; - params_le->channel_num = 0; - params_le->nprobes = cpu_to_le32(-1); - params_le->active_time = cpu_to_le32(-1); - params_le->passive_time = cpu_to_le32(-1); - params_le->home_time = cpu_to_le32(-1); - memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); - - n_ssids = request->n_ssids; - n_channels = request->n_channels; - - /* Copy channel array if applicable */ - brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n", - n_channels); - if (n_channels > 0) { - for (i = 0; i < n_channels; i++) { - chanspec = channel_to_chanspec(&cfg->d11inf, - request->channels[i]); - brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n", - request->channels[i]->hw_value, chanspec); - params_le->channel_list[i] = cpu_to_le16(chanspec); - } - } else { - brcmf_dbg(SCAN, "Scanning all channels\n"); - } - /* Copy ssid array if applicable */ - brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids); - if (n_ssids > 0) { - offset = offsetof(struct brcmf_scan_params_le, channel_list) + - n_channels * sizeof(u16); - offset = roundup(offset, sizeof(u32)); - ptr = (char *)params_le + offset; - for (i = 0; i < n_ssids; i++) { - memset(&ssid_le, 0, sizeof(ssid_le)); - ssid_le.SSID_len = - cpu_to_le32(request->ssids[i].ssid_len); - memcpy(ssid_le.SSID, request->ssids[i].ssid, - request->ssids[i].ssid_len); - if (!ssid_le.SSID_len) - brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); - else - brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n", - i, ssid_le.SSID, ssid_le.SSID_len); - memcpy(ptr, &ssid_le, sizeof(ssid_le)); - ptr += sizeof(ssid_le); - } - } else { - brcmf_dbg(SCAN, "Performing passive scan\n"); - params_le->scan_type = BRCMF_SCANTYPE_PASSIVE; - } - /* Adding mask to channel numbers */ - params_le->channel_num = - cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | - (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); -} - static s32 brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, struct cfg80211_scan_request *request) { struct brcmf_pub *drvr = cfg->pub; - s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + - offsetof(struct brcmf_escan_params_le, params_le); + s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE + + offsetof(struct brcmf_escan_params_le, params_v2_le); struct brcmf_escan_params_le *params; s32 err = 0; @@ -1391,8 +1446,22 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, goto exit; } BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); - brcmf_escan_prep(cfg, ¶ms->params_le, request); - params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); + brcmf_escan_prep(cfg, ¶ms->params_v2_le, request); + + params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2); + + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) { + struct brcmf_escan_params_le *params_v1; + + params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE; + params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE; + params_v1 = kzalloc(params_size, GFP_KERNEL); + params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); + brcmf_scan_params_v2_to_v1(¶ms->params_v2_le, ¶ms_v1->params_le); + kfree(params); + params = params_v1; + } + params->action = cpu_to_le16(WL_ESCAN_ACTION_START); params->sync_id = cpu_to_le16(0x1234); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 10bac865d724..b6797f800e55 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -290,6 +290,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_SCAN_V2, "scan_ver"); if (drvr->settings->feature_disable) { brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index f1b086a69d73..549298c55b55 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -30,6 +30,7 @@ * SAE: simultaneous authentication of equals * FWAUTH: Firmware authenticator * DUMP_OBSS: Firmware has capable to dump obss info to support ACS + * SCAN_V2: Version 2 scan params */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -53,7 +54,8 @@ BRCMF_FEAT_DEF(DOT11H) \ BRCMF_FEAT_DEF(SAE) \ BRCMF_FEAT_DEF(FWAUTH) \ - BRCMF_FEAT_DEF(DUMP_OBSS) + BRCMF_FEAT_DEF(DUMP_OBSS) \ + BRCMF_FEAT_DEF(SCAN_V2) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 04e1beedfd81..b3844d0d1adb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -48,6 +48,10 @@ /* size of brcmf_scan_params not including variable length array */ #define BRCMF_SCAN_PARAMS_FIXED_SIZE 64 +#define BRCMF_SCAN_PARAMS_V2_FIXED_SIZE 72 + +/* version of brcmf_scan_params structure */ +#define BRCMF_SCAN_PARAMS_VERSION_V2 2 /* masks for channel and ssid count */ #define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff @@ -67,6 +71,7 @@ #define BRCMF_PRIMARY_KEY (1 << 1) #define DOT11_BSSTYPE_ANY 2 #define BRCMF_ESCAN_REQ_VERSION 1 +#define BRCMF_ESCAN_REQ_VERSION_V2 2 #define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ @@ -386,6 +391,45 @@ struct brcmf_scan_params_le { __le16 channel_list[1]; /* list of chanspecs */ }; +struct brcmf_scan_params_v2_le { + __le16 version; /* structure version */ + __le16 length; /* structure length */ + struct brcmf_ssid_le ssid_le; /* default: {0, ""} */ + u8 bssid[ETH_ALEN]; /* default: bcast */ + s8 bss_type; /* default: any, + * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT + */ + u8 pad; + __le32 scan_type; /* flags, 0 use default */ + __le32 nprobes; /* -1 use default, number of probes per channel */ + __le32 active_time; /* -1 use default, dwell time per channel for + * active scanning + */ + __le32 passive_time; /* -1 use default, dwell time per channel + * for passive scanning + */ + __le32 home_time; /* -1 use default, dwell time for the + * home channel between channel scans + */ + __le32 channel_num; /* count of channels and ssids that follow + * + * low half is count of channels in + * channel_list, 0 means default (use all + * available channels) + * + * high half is entries in struct brcmf_ssid + * array that follows channel_list, aligned for + * s32 (4 bytes) meaning an odd channel count + * implies a 2-byte pad between end of + * channel_list and first ssid + * + * if ssid count is zero, single ssid in the + * fixed parameter portion is assumed, otherwise + * ssid in the fixed portion is ignored + */ + __le16 channel_list[1]; /* list of chanspecs */ +}; + struct brcmf_scan_results { u32 buflen; u32 version; @@ -397,7 +441,10 @@ struct brcmf_escan_params_le { __le32 version; __le16 action; __le16 sync_id; - struct brcmf_scan_params_le params_le; + union { + struct brcmf_scan_params_le params_le; + struct brcmf_scan_params_v2_le params_v2_le; + }; }; struct brcmf_escan_result_le { -- cgit v1.2.3 From d75ef1f81e42dfccfeb97952e2ab70376832cf7a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:17 +0900 Subject: wifi: brcmfmac: feature: Add support for setting feats based on WLC version The "wlc_ver" iovar returns information on the WLC and EPI versions. This can be used to determine whether the PMKID_V2 and _V3 features are supported. Reviewed-by: Linus Walleij Acked-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-4-marcan@marcan.st --- .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 48 ++++++++++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/feature.h | 4 +- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 25 +++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index b6797f800e55..6d10c9efbe93 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -126,6 +126,53 @@ static void brcmf_feat_firmware_overrides(struct brcmf_pub *drv) drv->feat_flags |= feat_flags; } +struct brcmf_feat_wlcfeat { + u16 min_ver_major; + u16 min_ver_minor; + u32 feat_flags; +}; + +static const struct brcmf_feat_wlcfeat brcmf_feat_wlcfeat_map[] = { + { 12, 0, BIT(BRCMF_FEAT_PMKID_V2) }, + { 13, 0, BIT(BRCMF_FEAT_PMKID_V3) }, +}; + +static void brcmf_feat_wlc_version_overrides(struct brcmf_pub *drv) +{ + struct brcmf_if *ifp = brcmf_get_ifp(drv, 0); + const struct brcmf_feat_wlcfeat *e; + struct brcmf_wlc_version_le ver; + u32 feat_flags = 0; + int i, err, major, minor; + + err = brcmf_fil_iovar_data_get(ifp, "wlc_ver", &ver, sizeof(ver)); + if (err) + return; + + major = le16_to_cpu(ver.wlc_ver_major); + minor = le16_to_cpu(ver.wlc_ver_minor); + + brcmf_dbg(INFO, "WLC version: %d.%d\n", major, minor); + + for (i = 0; i < ARRAY_SIZE(brcmf_feat_wlcfeat_map); i++) { + e = &brcmf_feat_wlcfeat_map[i]; + if (major > e->min_ver_major || + (major == e->min_ver_major && + minor >= e->min_ver_minor)) { + feat_flags |= e->feat_flags; + } + } + + if (!feat_flags) + return; + + for (i = 0; i < BRCMF_FEAT_LAST; i++) + if (feat_flags & BIT(i)) + brcmf_dbg(INFO, "enabling firmware feature: %s\n", + brcmf_feat_names[i]); + drv->feat_flags |= feat_flags; +} + /** * brcmf_feat_iovar_int_get() - determine feature through iovar query. * @@ -299,6 +346,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) ifp->drvr->feat_flags &= ~drvr->settings->feature_disable; } + brcmf_feat_wlc_version_overrides(drvr); brcmf_feat_firmware_overrides(drvr); /* set chip related quirks */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 549298c55b55..7f4f0b3e4a7b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -55,7 +55,9 @@ BRCMF_FEAT_DEF(SAE) \ BRCMF_FEAT_DEF(FWAUTH) \ BRCMF_FEAT_DEF(DUMP_OBSS) \ - BRCMF_FEAT_DEF(SCAN_V2) + BRCMF_FEAT_DEF(SCAN_V2) \ + BRCMF_FEAT_DEF(PMKID_V2) \ + BRCMF_FEAT_DEF(PMKID_V3) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index b3844d0d1adb..801709c26b7b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -788,6 +788,31 @@ struct brcmf_rev_info_le { __le32 nvramrev; }; +/** + * struct brcmf_wlc_version_le - firmware revision info. + * + * @version: structure version. + * @length: structure length. + * @epi_ver_major: EPI major version + * @epi_ver_minor: EPI minor version + * @epi_ver_rc: EPI rc version + * @epi_ver_incr: EPI increment version + * @wlc_ver_major: WLC major version + * @wlc_ver_minor: WLC minor version + */ +struct brcmf_wlc_version_le { + __le16 version; + __le16 length; + + __le16 epi_ver_major; + __le16 epi_ver_minor; + __le16 epi_ver_rc; + __le16 epi_ver_incr; + + __le16 wlc_ver_major; + __le16 wlc_ver_minor; +}; + /** * struct brcmf_assoclist_le - request assoc list. * -- cgit v1.2.3 From a96202acaea47fa8377088e0952bb63bd02a3bab Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:18 +0900 Subject: wifi: brcmfmac: cfg80211: Add support for PMKID_V3 operations Add support for the new PMKID_V3 API, which allows performing PMKID mutations individually, instead of requiring the driver to keep track of the full list. This new API is required by at least BCM4387. Note that PMKID_V2 is not implemented yet. Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-5-marcan@marcan.st --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 52 +++++++++++++- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 83 ++++++++++++++++++++++ 2 files changed, 132 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 77706e204a8f..b561ca6b467e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -4306,6 +4306,37 @@ exit: return 0; } +static s32 +brcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa, + bool alive) +{ + struct brcmf_pmk_op_v3_le *pmk_op; + int length = offsetof(struct brcmf_pmk_op_v3_le, pmk); + int ret; + + pmk_op = kzalloc(sizeof(*pmk_op), GFP_KERNEL); + pmk_op->version = cpu_to_le16(BRCMF_PMKSA_VER_3); + + if (!pmksa) { + /* Flush operation, operate on entire list */ + pmk_op->count = cpu_to_le16(0); + } else { + /* Single PMK operation */ + pmk_op->count = cpu_to_le16(1); + length += sizeof(struct brcmf_pmksa_v3); + memcpy(pmk_op->pmk[0].bssid, pmksa->bssid, ETH_ALEN); + memcpy(pmk_op->pmk[0].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); + pmk_op->pmk[0].pmkid_len = WLAN_PMKID_LEN; + pmk_op->pmk[0].time_left = cpu_to_le32(alive ? BRCMF_PMKSA_NO_EXPIRY : 0); + } + + pmk_op->length = cpu_to_le16(length); + + ret = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_op, sizeof(*pmk_op)); + kfree(pmk_op); + return ret; +} + static __used s32 brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { @@ -4339,6 +4370,14 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; + brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmksa->bssid); + brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmksa->pmkid); + + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3)) + return brcmf_pmksa_v3_op(ifp, pmksa, true); + + /* TODO: implement PMKID_V2 */ + npmk = le32_to_cpu(cfg->pmk_list.npmk); for (i = 0; i < npmk; i++) if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN)) @@ -4355,9 +4394,6 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, return -EINVAL; } - brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid); - brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmk[npmk].pmkid); - err = brcmf_update_pmklist(cfg, ifp); brcmf_dbg(TRACE, "Exit\n"); @@ -4381,6 +4417,11 @@ brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3)) + return brcmf_pmksa_v3_op(ifp, pmksa, false); + + /* TODO: implement PMKID_V2 */ + npmk = le32_to_cpu(cfg->pmk_list.npmk); for (i = 0; i < npmk; i++) if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN)) @@ -4417,6 +4458,11 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) if (!check_vif_up(ifp->vif)) return -EIO; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3)) + return brcmf_pmksa_v3_op(ifp, NULL, false); + + /* TODO: implement PMKID_V2 */ + memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list)); err = brcmf_update_pmklist(cfg, ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 801709c26b7b..792adaf880b4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -174,6 +174,10 @@ #define BRCMF_HE_CAP_MCS_MAP_NSS_MAX 8 +#define BRCMF_PMKSA_VER_2 2 +#define BRCMF_PMKSA_VER_3 3 +#define BRCMF_PMKSA_NO_EXPIRY 0xffffffff + /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each * ioctl. It is relatively small because firmware has small maximum size input * playload restriction for ioctls. @@ -355,6 +359,12 @@ struct brcmf_ssid_le { unsigned char SSID[IEEE80211_MAX_SSID_LEN]; }; +/* Alternate SSID structure used in some places... */ +struct brcmf_ssid8_le { + u8 SSID_len; + unsigned char SSID[IEEE80211_MAX_SSID_LEN]; +}; + struct brcmf_scan_params_le { struct brcmf_ssid_le ssid_le; /* default: {0, ""} */ u8 bssid[ETH_ALEN]; /* default: bcast */ @@ -875,6 +885,51 @@ struct brcmf_pmksa { u8 pmkid[WLAN_PMKID_LEN]; }; +/** + * struct brcmf_pmksa_v2 - PMK Security Association + * + * @length: Length of the structure. + * @bssid: The AP's BSSID. + * @pmkid: The PMK ID. + * @pmk: PMK material for FILS key derivation. + * @pmk_len: Length of PMK data. + * @ssid: The AP's SSID. + * @fils_cache_id: FILS cache identifier + */ +struct brcmf_pmksa_v2 { + __le16 length; + u8 bssid[ETH_ALEN]; + u8 pmkid[WLAN_PMKID_LEN]; + u8 pmk[WLAN_PMK_LEN_SUITE_B_192]; + __le16 pmk_len; + struct brcmf_ssid8_le ssid; + u16 fils_cache_id; +}; + +/** + * struct brcmf_pmksa_v3 - PMK Security Association + * + * @bssid: The AP's BSSID. + * @pmkid: The PMK ID. + * @pmkid_len: The length of the PMK ID. + * @pmk: PMK material for FILS key derivation. + * @pmk_len: Length of PMK data. + * @fils_cache_id: FILS cache identifier + * @ssid: The AP's SSID. + * @time_left: Remaining time until expiry. 0 = expired, ~0 = no expiry. + */ +struct brcmf_pmksa_v3 { + u8 bssid[ETH_ALEN]; + u8 pmkid[WLAN_PMKID_LEN]; + u8 pmkid_len; + u8 pmk[WLAN_PMK_LEN_SUITE_B_192]; + u8 pmk_len; + __le16 fils_cache_id; + u8 pad; + struct brcmf_ssid8_le ssid; + __le32 time_left; +}; + /** * struct brcmf_pmk_list_le - List of pmksa's. * @@ -886,6 +941,34 @@ struct brcmf_pmk_list_le { struct brcmf_pmksa pmk[BRCMF_MAXPMKID]; }; +/** + * struct brcmf_pmk_list_v2_le - List of pmksa's. + * + * @version: Request version. + * @length: Length of this structure. + * @pmk: PMK SA information. + */ +struct brcmf_pmk_list_v2_le { + __le16 version; + __le16 length; + struct brcmf_pmksa_v2 pmk[BRCMF_MAXPMKID]; +}; + +/** + * struct brcmf_pmk_op_v3_le - Operation on PMKSA list. + * + * @version: Request version. + * @length: Length of this structure. + * @pmk: PMK SA information. + */ +struct brcmf_pmk_op_v3_le { + __le16 version; + __le16 length; + __le16 count; + __le16 pad; + struct brcmf_pmksa_v3 pmk[BRCMF_MAXPMKID]; +}; + /** * struct brcmf_pno_param_le - PNO scan configuration parameters * -- cgit v1.2.3 From 89b89e52153fda2733562776c7c9d9d3ebf8dd6d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:19 +0900 Subject: wifi: brcmfmac: cfg80211: Pass the PMK in binary instead of hex Apparently the hex passphrase mechanism does not work on newer chips/firmware (e.g. BCM4387). It seems there was a simple way of passing it in binary all along, so use that and avoid the hexification. OpenBSD has been doing it like this from the beginning, so this should work on all chips. Also clear the structure before setting the PMK. This was leaking uninitialized stack contents to the device. Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-6-marcan@marcan.st --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index b561ca6b467e..e0a70a671550 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1686,13 +1686,14 @@ static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len) { struct brcmf_pub *drvr = ifp->drvr; struct brcmf_wsec_pmk_le pmk; - int i, err; + int err; + + memset(&pmk, 0, sizeof(pmk)); - /* convert to firmware key format */ - pmk.key_len = cpu_to_le16(pmk_len << 1); - pmk.flags = cpu_to_le16(BRCMF_WSEC_PASSPHRASE); - for (i = 0; i < pmk_len; i++) - snprintf(&pmk.key[2 * i], 3, "%02x", pmk_data[i]); + /* pass pmk directly */ + pmk.key_len = cpu_to_le16(pmk_len); + pmk.flags = cpu_to_le16(0); + memcpy(pmk.key, pmk_data, pmk_len); /* store psk in firmware */ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK, -- cgit v1.2.3 From 117ace4014cce3fb78b40eb8028bb0f4fc37dd6f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:20 +0900 Subject: wifi: brcmfmac: pcie: Add IDs/properties for BCM4387 This chip is present on Apple M1 Pro/Max (t600x) platforms: * maldives (apple,j314s): MacBook Pro (14-inch, M1 Pro, 2021) * maldives (apple,j314c): MacBook Pro (14-inch, M1 Max, 2021) * madagascar (apple,j316s): MacBook Pro (16-inch, M1 Pro, 2021) * madagascar (apple,j316c): MacBook Pro (16-inch, M1 Max, 2021) Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-7-marcan@marcan.st --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 2 ++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 8 ++++++++ drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 2 ++ 3 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 50e0c015cf43..9f9bf08a70bb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -743,6 +743,8 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) return 0x170000; case BRCM_CC_4378_CHIP_ID: return 0x352000; + case BRCM_CC_4387_CHIP_ID: + return 0x740000; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 2835ef4edb18..d2dad5414f39 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -67,6 +67,7 @@ BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie"); BRCMF_FW_DEF(4371, "brcmfmac4371-pcie"); BRCMF_FW_CLM_DEF(4377B3, "brcmfmac4377b3-pcie"); BRCMF_FW_CLM_DEF(4378B1, "brcmfmac4378b1-pcie"); +BRCMF_FW_CLM_DEF(4387C2, "brcmfmac4387c2-pcie"); /* firmware config files */ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.txt"); @@ -101,6 +102,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), BRCMF_FW_ENTRY(BRCM_CC_4377_CHIP_ID, 0xFFFFFFFF, 4377B3), /* revision ID 4 */ BRCMF_FW_ENTRY(BRCM_CC_4378_CHIP_ID, 0xFFFFFFFF, 4378B1), /* revision ID 3 */ + BRCMF_FW_ENTRY(BRCM_CC_4387_CHIP_ID, 0xFFFFFFFF, 4387C2), /* revision ID 7 */ }; #define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */ @@ -2048,6 +2050,11 @@ static int brcmf_pcie_read_otp(struct brcmf_pciedev_info *devinfo) base = 0x1120; words = 0x170; break; + case BRCM_CC_4387_CHIP_ID: + coreid = BCMA_CORE_GCI; + base = 0x113c; + words = 0x170; + break; default: /* OTP not supported on this chip */ return 0; @@ -2662,6 +2669,7 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_43596_DEVICE_ID, CYW), BRCMF_PCIE_DEVICE(BRCM_PCIE_4377_DEVICE_ID, WCC), BRCMF_PCIE_DEVICE(BRCM_PCIE_4378_DEVICE_ID, WCC), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4387_DEVICE_ID, WCC), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index 896615f57952..44684bf1b9ac 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -54,6 +54,7 @@ #define BRCM_CC_4371_CHIP_ID 0x4371 #define BRCM_CC_4377_CHIP_ID 0x4377 #define BRCM_CC_4378_CHIP_ID 0x4378 +#define BRCM_CC_4387_CHIP_ID 0x4387 #define CY_CC_4373_CHIP_ID 0x4373 #define CY_CC_43012_CHIP_ID 43012 #define CY_CC_43439_CHIP_ID 43439 @@ -95,6 +96,7 @@ #define BRCM_PCIE_43596_DEVICE_ID 0x4415 #define BRCM_PCIE_4377_DEVICE_ID 0x4488 #define BRCM_PCIE_4378_DEVICE_ID 0x4425 +#define BRCM_PCIE_4387_DEVICE_ID 0x4433 /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ -- cgit v1.2.3 From dd7e55401fec58a2d03e5fdcb0c6d20e8fbe450a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:21 +0900 Subject: wifi: brcmfmac: common: Add support for downloading TxCap blobs The TxCap blobs are additional data blobs used on Apple devices, and are uploaded analogously to CLM blobs. Add core support for doing this. Acked-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-8-marcan@marcan.st --- .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 1 + .../wireless/broadcom/brcm80211/brcmfmac/common.c | 93 ++++++++++++++++------ 2 files changed, 70 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 501136e011b5..fe31051a9e11 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -55,6 +55,7 @@ enum brcmf_bus_protocol_type { /* Firmware blobs that may be available */ enum brcmf_blob_type { BRCMF_BLOB_CLM, + BRCMF_BLOB_TXCAP, }; struct brcmf_mp_device; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 4fea6f0ea8c8..88e81b83af9d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -101,7 +101,7 @@ void brcmf_c_set_joinpref_default(struct brcmf_if *ifp) static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, struct brcmf_dload_data_le *dload_buf, - u32 len) + u32 len, const char *var) { s32 err; @@ -111,18 +111,18 @@ static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, dload_buf->len = cpu_to_le32(len); dload_buf->crc = cpu_to_le32(0); - err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, + err = brcmf_fil_iovar_data_set(ifp, var, dload_buf, struct_size(dload_buf, data, len)); return err; } -static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) +static int brcmf_c_download_blob(struct brcmf_if *ifp, + const void *data, size_t size, + const char *loadvar, const char *statvar) { struct brcmf_pub *drvr = ifp->drvr; - struct brcmf_bus *bus = drvr->bus_if; struct brcmf_dload_data_le *chunk_buf; - const struct firmware *clm = NULL; u32 chunk_len; u32 datalen; u32 cumulative_len; @@ -132,21 +132,14 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) brcmf_dbg(TRACE, "Enter\n"); - err = brcmf_bus_get_blob(bus, &clm, BRCMF_BLOB_CLM); - if (err || !clm) { - brcmf_info("no clm_blob available (err=%d), device may have limited channels available\n", - err); - return 0; - } - chunk_buf = kzalloc(struct_size(chunk_buf, data, MAX_CHUNK_LEN), GFP_KERNEL); if (!chunk_buf) { err = -ENOMEM; - goto done; + return -ENOMEM; } - datalen = clm->size; + datalen = size; cumulative_len = 0; do { if (datalen > MAX_CHUNK_LEN) { @@ -155,9 +148,10 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) chunk_len = datalen; dl_flag |= DL_END; } - memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len); + memcpy(chunk_buf->data, data + cumulative_len, chunk_len); - err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len); + err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len, + loadvar); dl_flag &= ~DL_BEGIN; @@ -166,20 +160,64 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) } while ((datalen > 0) && (err == 0)); if (err) { - bphy_err(drvr, "clmload (%zu byte file) failed (%d)\n", - clm->size, err); - /* Retrieve clmload_status and print */ - err = brcmf_fil_iovar_int_get(ifp, "clmload_status", &status); + bphy_err(drvr, "%s (%zu byte file) failed (%d)\n", + loadvar, size, err); + /* Retrieve status and print */ + err = brcmf_fil_iovar_int_get(ifp, statvar, &status); if (err) - bphy_err(drvr, "get clmload_status failed (%d)\n", err); + bphy_err(drvr, "get %s failed (%d)\n", statvar, err); else - brcmf_dbg(INFO, "clmload_status=%d\n", status); + brcmf_dbg(INFO, "%s=%d\n", statvar, status); err = -EIO; } kfree(chunk_buf); -done: - release_firmware(clm); + return err; +} + +static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_bus *bus = drvr->bus_if; + const struct firmware *fw = NULL; + s32 err; + + brcmf_dbg(TRACE, "Enter\n"); + + err = brcmf_bus_get_blob(bus, &fw, BRCMF_BLOB_CLM); + if (err || !fw) { + brcmf_info("no clm_blob available (err=%d), device may have limited channels available\n", + err); + return 0; + } + + err = brcmf_c_download_blob(ifp, fw->data, fw->size, + "clmload", "clmload_status"); + + release_firmware(fw); + return err; +} + +static int brcmf_c_process_txcap_blob(struct brcmf_if *ifp) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_bus *bus = drvr->bus_if; + const struct firmware *fw = NULL; + s32 err; + + brcmf_dbg(TRACE, "Enter\n"); + + err = brcmf_bus_get_blob(bus, &fw, BRCMF_BLOB_TXCAP); + if (err || !fw) { + brcmf_info("no txcap_blob available (err=%d)\n", err); + return 0; + } + + brcmf_info("TxCap blob found, loading\n"); + err = brcmf_c_download_blob(ifp, fw->data, fw->size, + "txcapload", "txcapload_status"); + + release_firmware(fw); return err; } @@ -291,6 +329,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Do TxCap downloading, if needed */ + err = brcmf_c_process_txcap_blob(ifp); + if (err < 0) { + bphy_err(drvr, "download TxCap blob file failed, %d\n", err); + goto done; + } + /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); -- cgit v1.2.3 From 75102b7543ed87cf7d599a382e7340e572da6987 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:22 +0900 Subject: wifi: brcmfmac: pcie: Load and provide TxCap blobs These blobs are named .txcap_blob, and exist alongside the existing .clm_blob files. Use the existing firmware machinery to provide them to the core. Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-9-marcan@marcan.st --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index d2dad5414f39..0fcdd84bbed0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -76,6 +76,7 @@ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.txt"); /* per-board firmware binaries */ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.bin"); MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.clm_blob"); +MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-pcie.*.txcap_blob"); static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602), @@ -329,7 +330,9 @@ struct brcmf_pciedev_info { char fw_name[BRCMF_FW_NAME_LEN]; char nvram_name[BRCMF_FW_NAME_LEN]; char clm_name[BRCMF_FW_NAME_LEN]; + char txcap_name[BRCMF_FW_NAME_LEN]; const struct firmware *clm_fw; + const struct firmware *txcap_fw; const struct brcmf_pcie_reginfo *reginfo; void __iomem *regs; void __iomem *tcm; @@ -1520,6 +1523,10 @@ static int brcmf_pcie_get_blob(struct device *dev, const struct firmware **fw, *fw = devinfo->clm_fw; devinfo->clm_fw = NULL; break; + case BRCMF_BLOB_TXCAP: + *fw = devinfo->txcap_fw; + devinfo->txcap_fw = NULL; + break; default: return -ENOENT; } @@ -2112,6 +2119,7 @@ static int brcmf_pcie_read_otp(struct brcmf_pciedev_info *devinfo) #define BRCMF_PCIE_FW_CODE 0 #define BRCMF_PCIE_FW_NVRAM 1 #define BRCMF_PCIE_FW_CLM 2 +#define BRCMF_PCIE_FW_TXCAP 3 static void brcmf_pcie_setup(struct device *dev, int ret, struct brcmf_fw_request *fwreq) @@ -2138,6 +2146,7 @@ static void brcmf_pcie_setup(struct device *dev, int ret, nvram = fwreq->items[BRCMF_PCIE_FW_NVRAM].nv_data.data; nvram_len = fwreq->items[BRCMF_PCIE_FW_NVRAM].nv_data.len; devinfo->clm_fw = fwreq->items[BRCMF_PCIE_FW_CLM].binary; + devinfo->txcap_fw = fwreq->items[BRCMF_PCIE_FW_TXCAP].binary; kfree(fwreq); ret = brcmf_chip_get_raminfo(devinfo->ci); @@ -2219,6 +2228,7 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo) { ".bin", devinfo->fw_name }, { ".txt", devinfo->nvram_name }, { ".clm_blob", devinfo->clm_name }, + { ".txcap_blob", devinfo->txcap_name }, }; fwreq = brcmf_fw_alloc_request(devinfo->ci->chip, devinfo->ci->chiprev, @@ -2233,6 +2243,8 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo) fwreq->items[BRCMF_PCIE_FW_NVRAM].flags = BRCMF_FW_REQF_OPTIONAL; fwreq->items[BRCMF_PCIE_FW_CLM].type = BRCMF_FW_TYPE_BINARY; fwreq->items[BRCMF_PCIE_FW_CLM].flags = BRCMF_FW_REQF_OPTIONAL; + fwreq->items[BRCMF_PCIE_FW_TXCAP].type = BRCMF_FW_TYPE_BINARY; + fwreq->items[BRCMF_PCIE_FW_TXCAP].flags = BRCMF_FW_REQF_OPTIONAL; /* NVRAM reserves PCI domain 0 for Broadcom's SDK faked bus */ fwreq->domain_nr = pci_domain_nr(devinfo->pdev->bus) + 1; fwreq->bus_nr = devinfo->pdev->bus->number; @@ -2530,6 +2542,7 @@ brcmf_pcie_remove(struct pci_dev *pdev) brcmf_pcie_reset_device(devinfo); brcmf_pcie_release_resource(devinfo); release_firmware(devinfo->clm_fw); + release_firmware(devinfo->txcap_fw); if (devinfo->ci) brcmf_chip_detach(devinfo->ci); -- cgit v1.2.3 From 5b3ee9987f5856080509e62968f812961173d336 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:24:23 +0900 Subject: wifi: brcmfmac: common: Add support for external calibration blobs The calibration blob for a chip is normally stored in SROM and loaded internally by the firmware. However, Apple ARM64 platforms instead store it as part of platform configuration data, and provide it via the Apple Device Tree. We forward this into the Linux DT in the bootloader. Add support for taking this blob from the DT and loading it into the dongle. The loading mechanism is the same as used for the CLM and TxCap blobs. Reviewed-by: Linus Walleij Reviewed-by: Arend van Spriel Signed-off-by: Hector Martin Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092423.15175-10-marcan@marcan.st --- .../wireless/broadcom/brcm80211/brcmfmac/common.c | 24 ++++++++++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/common.h | 2 ++ .../net/wireless/broadcom/brcm80211/brcmfmac/of.c | 7 +++++++ 3 files changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 88e81b83af9d..a194b0e68eb5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -246,6 +246,23 @@ static const u8 brcmf_default_mac_address[ETH_ALEN] = { 0x00, 0x90, 0x4c, 0xc5, 0x12, 0x38 }; +static int brcmf_c_process_cal_blob(struct brcmf_if *ifp) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_mp_device *settings = drvr->settings; + s32 err; + + brcmf_dbg(TRACE, "Enter\n"); + + if (!settings->cal_blob || !settings->cal_size) + return 0; + + brcmf_info("Calibration blob provided by platform, loading\n"); + err = brcmf_c_download_blob(ifp, settings->cal_blob, settings->cal_size, + "calload", "calload_status"); + return err; +} + int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; @@ -336,6 +353,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Download external calibration blob, if available */ + err = brcmf_c_process_cal_blob(ifp); + if (err < 0) { + bphy_err(drvr, "download calibration blob file failed, %d\n", err); + goto done; + } + /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index 7167fd4f8c63..2be2986d2110 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -54,6 +54,8 @@ struct brcmf_mp_device { const char *board_type; unsigned char mac[ETH_ALEN]; const char *antenna_sku; + const void *cal_blob; + int cal_size; union { struct brcmfmac_sdio_pd sdio; } bus; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index fdd0c9abc1a1..52527b61341e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -86,6 +86,13 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, if (!of_property_read_string(np, "apple,antenna-sku", &prop)) settings->antenna_sku = prop; + /* The WLAN calibration blob is normally stored in SROM, but Apple + * ARM64 platforms pass it via the DT instead. + */ + prop = of_get_property(np, "brcm,cal-blob", &settings->cal_size); + if (prop && settings->cal_size) + settings->cal_blob = prop; + /* Set board-type to the first string of the machine compatible prop */ root = of_find_node_by_path("/"); if (root && err) { -- cgit v1.2.3 From 1d5003d05f983eee28756896328e4949d9a97b7f Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 14 Feb 2023 18:28:38 +0900 Subject: wifi: brcmfmac: pcie: Add BCM4378B3 support BCM4378B3 is a new silicon revision of BCM4378 present on the Apple M2 13" MacBook Pro "kyushu". Its PCI revision number is 5. Signed-off-by: Hector Martin Reviewed-by: Julian Calaby Reviewed-by: Linus Walleij Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230214092838.17869-1-marcan@marcan.st --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 0fcdd84bbed0..848f05ec1268 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -67,6 +67,7 @@ BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie"); BRCMF_FW_DEF(4371, "brcmfmac4371-pcie"); BRCMF_FW_CLM_DEF(4377B3, "brcmfmac4377b3-pcie"); BRCMF_FW_CLM_DEF(4378B1, "brcmfmac4378b1-pcie"); +BRCMF_FW_CLM_DEF(4378B3, "brcmfmac4378b3-pcie"); BRCMF_FW_CLM_DEF(4387C2, "brcmfmac4387c2-pcie"); /* firmware config files */ @@ -102,7 +103,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43666_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), BRCMF_FW_ENTRY(BRCM_CC_4377_CHIP_ID, 0xFFFFFFFF, 4377B3), /* revision ID 4 */ - BRCMF_FW_ENTRY(BRCM_CC_4378_CHIP_ID, 0xFFFFFFFF, 4378B1), /* revision ID 3 */ + BRCMF_FW_ENTRY(BRCM_CC_4378_CHIP_ID, 0x0000000F, 4378B1), /* revision ID 3 */ + BRCMF_FW_ENTRY(BRCM_CC_4378_CHIP_ID, 0xFFFFFFE0, 4378B3), /* revision ID 5 */ BRCMF_FW_ENTRY(BRCM_CC_4387_CHIP_ID, 0xFFFFFFFF, 4387C2), /* revision ID 7 */ }; -- cgit v1.2.3 From 01c6c9fccbd51c1d9eab0f5794b0271b026178df Mon Sep 17 00:00:00 2001 From: Abinaya Kalaiselvan Date: Mon, 19 Dec 2022 11:08:44 +0530 Subject: wifi: ath11k: Add tx ack signal support for management packets Add support to notify tx ack signal values for management packets to userspace through nl80211 interface. Advertise NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT flag to enable this feature and it will be used for data packets as well. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Signed-off-by: Abinaya Kalaiselvan Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221219053844.4084486-1-quic_mkenna@quicinc.com --- drivers/net/wireless/ath/ath11k/hw.c | 1 + drivers/net/wireless/ath/ath11k/mac.c | 5 +++++ drivers/net/wireless/ath/ath11k/wmi.c | 27 ++++++++++++++++----------- drivers/net/wireless/ath/ath11k/wmi.h | 3 +++ 4 files changed, 25 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c index ab8f0ccacc6b..60ac215e0678 100644 --- a/drivers/net/wireless/ath/ath11k/hw.c +++ b/drivers/net/wireless/ath/ath11k/hw.c @@ -201,6 +201,7 @@ static void ath11k_init_wmi_config_ipq8074(struct ath11k_base *ab, config->twt_ap_pdev_count = ab->num_radios; config->twt_ap_sta_count = 1000; config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64; + config->flag1 |= WMI_RSRC_CFG_FLAG1_ACK_RSSI; } static int ath11k_hw_mac_id_to_pdev_id_ipq8074(struct ath11k_hw_params *hw, diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index fd1a23ead5e2..cad832e0e6b8 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -9174,6 +9174,11 @@ static int __ath11k_mac_register(struct ath11k *ar) goto err_free_if_combs; } + if (test_bit(WMI_TLV_SERVICE_TX_DATA_MGMT_ACK_RSSI, + ar->ab->wmi_ab.svc_map)) + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + ar->hw->queues = ATH11K_HW_MAX_QUEUES; ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 7057c7916d5d..815f49c3b3ad 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -5229,8 +5229,8 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab, return 0; } -static int wmi_process_mgmt_tx_comp(struct ath11k *ar, u32 desc_id, - u32 status) +static int wmi_process_mgmt_tx_comp(struct ath11k *ar, + struct wmi_mgmt_tx_compl_event *tx_compl_param) { struct sk_buff *msdu; struct ieee80211_tx_info *info; @@ -5238,24 +5238,29 @@ static int wmi_process_mgmt_tx_comp(struct ath11k *ar, u32 desc_id, int num_mgmt; spin_lock_bh(&ar->txmgmt_idr_lock); - msdu = idr_find(&ar->txmgmt_idr, desc_id); + msdu = idr_find(&ar->txmgmt_idr, tx_compl_param->desc_id); if (!msdu) { ath11k_warn(ar->ab, "received mgmt tx compl for invalid msdu_id: %d\n", - desc_id); + tx_compl_param->desc_id); spin_unlock_bh(&ar->txmgmt_idr_lock); return -ENOENT; } - idr_remove(&ar->txmgmt_idr, desc_id); + idr_remove(&ar->txmgmt_idr, tx_compl_param->desc_id); spin_unlock_bh(&ar->txmgmt_idr_lock); skb_cb = ATH11K_SKB_CB(msdu); dma_unmap_single(ar->ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); info = IEEE80211_SKB_CB(msdu); - if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status) + if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && + !tx_compl_param->status) { info->flags |= IEEE80211_TX_STAT_ACK; + if (test_bit(WMI_TLV_SERVICE_TX_DATA_MGMT_ACK_RSSI, + ar->ab->wmi_ab.svc_map)) + info->status.ack_signal = tx_compl_param->ack_rssi; + } ieee80211_tx_status_irqsafe(ar->hw, msdu); @@ -5267,7 +5272,7 @@ static int wmi_process_mgmt_tx_comp(struct ath11k *ar, u32 desc_id, ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi mgmt tx comp pending %d desc id %d\n", - num_mgmt, desc_id); + num_mgmt, tx_compl_param->desc_id); if (!num_mgmt) wake_up(&ar->txmgmt_empty_waitq); @@ -5300,6 +5305,7 @@ static int ath11k_pull_mgmt_tx_compl_param_tlv(struct ath11k_base *ab, param->pdev_id = ev->pdev_id; param->desc_id = ev->desc_id; param->status = ev->status; + param->ack_rssi = ev->ack_rssi; kfree(tb); return 0; @@ -7070,13 +7076,12 @@ static void ath11k_mgmt_tx_compl_event(struct ath11k_base *ab, struct sk_buff *s goto exit; } - wmi_process_mgmt_tx_comp(ar, tx_compl_param.desc_id, - tx_compl_param.status); + wmi_process_mgmt_tx_comp(ar, &tx_compl_param); ath11k_dbg(ab, ATH11K_DBG_MGMT, - "mgmt tx compl ev pdev_id %d, desc_id %d, status %d", + "mgmt tx compl ev pdev_id %d, desc_id %d, status %d ack_rssi %d", tx_compl_param.pdev_id, tx_compl_param.desc_id, - tx_compl_param.status); + tx_compl_param.status, tx_compl_param.ack_rssi); exit: rcu_read_unlock(); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index d3b74f1346f5..63dbb085949f 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2311,6 +2311,7 @@ struct wmi_init_cmd { } __packed; #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) +#define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) struct wmi_resource_config { u32 tlv_header; @@ -4550,6 +4551,8 @@ struct wmi_mgmt_tx_compl_event { u32 desc_id; u32 status; u32 pdev_id; + u32 ppdu_id; + u32 ack_rssi; } __packed; struct wmi_scan_event { -- cgit v1.2.3 From 5c48f9432d06bf85ca934d7c4ecb14a7ec0c7f5d Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Fri, 24 Feb 2023 16:21:17 +0800 Subject: wifi: rtw89: fix SER L1 might stop entering LPS issue When SER L1 triggered, driver need to stop Rx and clear RTW89_FLAG_RUNNING flag. If track_work check RTW89_FLAG_RUNNING simultaneously, it will check failed and never queue track_work again, and LPS won't enter either. Therefore, we cancel delayed work when enter SER L1, and queue delayed work when leave SER L1. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230224082117.21241-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/ser.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index 61db7189fdab..9e9f6947e7f1 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -414,8 +414,11 @@ static void ser_idle_st_hdl(struct rtw89_ser *ser, u8 evt) static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) { + struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); + switch (evt) { case SER_EV_STATE_IN: + cancel_delayed_work_sync(&rtwdev->track_work); drv_stop_tx(ser); if (hal_stop_dma(ser)) { @@ -446,6 +449,8 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) hal_enable_dma(ser); drv_resume_rx(ser); drv_resume_tx(ser); + ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); break; default: -- cgit v1.2.3 From e5c3da9abd44bee298070d4e66b502067a8f1715 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 24 Feb 2023 13:59:17 +0100 Subject: wifi: brcmfmac: pcie: Add 4359C0 firmware definition Some phones from around 2016, as well as other random devices have this chip called 43956 or 4359C0 or 43596A0, which is more or less just a rev bump (v9) of the already-supported 4359. Add a corresponding firmware definition to allow for choosing the correct blob. Suggested-by: Arend van Spriel Signed-off-by: Konrad Dybcio Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230224-topic-brcm_tone-v1-1-333b0ac67934@linaro.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 848f05ec1268..59f3e9c5e139 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -58,6 +58,7 @@ BRCMF_FW_CLM_DEF(4356, "brcmfmac4356-pcie"); BRCMF_FW_CLM_DEF(43570, "brcmfmac43570-pcie"); BRCMF_FW_DEF(4358, "brcmfmac4358-pcie"); BRCMF_FW_DEF(4359, "brcmfmac4359-pcie"); +BRCMF_FW_DEF(4359C, "brcmfmac4359c-pcie"); BRCMF_FW_CLM_DEF(4364B2, "brcmfmac4364b2-pcie"); BRCMF_FW_CLM_DEF(4364B3, "brcmfmac4364b3-pcie"); BRCMF_FW_DEF(4365B, "brcmfmac4365b-pcie"); @@ -92,7 +93,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570), BRCMF_FW_ENTRY(BRCM_CC_43570_CHIP_ID, 0xFFFFFFFF, 43570), BRCMF_FW_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358), - BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), + BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0x000001FF, 4359), + BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFE00, 4359C), BRCMF_FW_ENTRY(BRCM_CC_4364_CHIP_ID, 0x0000000F, 4364B2), /* 3 */ BRCMF_FW_ENTRY(BRCM_CC_4364_CHIP_ID, 0xFFFFFFF0, 4364B3), /* 4 */ BRCMF_FW_ENTRY(BRCM_CC_4365_CHIP_ID, 0x0000000F, 4365B), -- cgit v1.2.3 From b7ed9fa2cb76ca7a3c3cd4a6d35748fe1fbda9f6 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 26 Feb 2023 23:10:03 +0100 Subject: wifi: rtw88: mac: Return the original error from rtw_pwr_seq_parser() rtw_pwr_seq_parser() calls rtw_sub_pwr_seq_parser() which can either return -EBUSY, -EINVAL or 0. Propagate the original error code instead of unconditionally returning -EBUSY in case of an error. Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver") Signed-off-by: Martin Blumenstingl Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230226221004.138331-2-martin.blumenstingl@googlemail.com --- drivers/net/wireless/realtek/rtw88/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index 1c9530a0eb69..4749d75fefee 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -236,7 +236,7 @@ static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev, ret = rtw_sub_pwr_seq_parser(rtwdev, intf_mask, cut_mask, cmd); if (ret) - return -EBUSY; + return ret; idx++; } while (1); -- cgit v1.2.3 From 15c8e267dfa62f207ee1db666c822324e3362b84 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 26 Feb 2023 23:10:04 +0100 Subject: wifi: rtw88: mac: Return the original error from rtw_mac_power_switch() rtw_mac_power_switch() calls rtw_pwr_seq_parser() which can return -EINVAL, -EBUSY or 0. Propagate the original error code instead of unconditionally returning -EINVAL in case of an error. Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver") Signed-off-by: Martin Blumenstingl Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230226221004.138331-3-martin.blumenstingl@googlemail.com --- drivers/net/wireless/realtek/rtw88/mac.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index 4749d75fefee..f3a566cf979b 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -250,6 +250,7 @@ static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on) const struct rtw_pwr_seq_cmd **pwr_seq; u8 rpwm; bool cur_pwr; + int ret; if (rtw_chip_wcpu_11ac(rtwdev)) { rpwm = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr); @@ -273,8 +274,9 @@ static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on) return -EALREADY; pwr_seq = pwr_on ? chip->pwr_on_seq : chip->pwr_off_seq; - if (rtw_pwr_seq_parser(rtwdev, pwr_seq)) - return -EINVAL; + ret = rtw_pwr_seq_parser(rtwdev, pwr_seq); + if (ret) + return ret; if (pwr_on) set_bit(RTW_FLAG_POWERON, rtwdev->flags); -- cgit v1.2.3 From ff6f38eb920bd2b86e1d3157a01f409e2dce1320 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Thu, 2 Mar 2023 10:39:11 +0800 Subject: wifi: rtlwifi: rtl8192se: Remove some unused variables Variables bcntime_cfg, bcn_cw and bcn_ifs are not effectively used, so delete them. drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c:1555:6: warning: variable 'bcntime_cfg' set but not used. Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=4240 Signed-off-by: Jiapeng Chong Acked-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230302023911.59278-1-jiapeng.chong@linux.alibaba.com --- drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c index bd0b7e365edb..a8b5bf45b1bb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/hw.c @@ -1552,8 +1552,6 @@ void rtl92se_set_beacon_related_registers(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - u16 bcntime_cfg = 0; - u16 bcn_cw = 6, bcn_ifs = 0xf; u16 atim_window = 2; /* ATIM Window (in unit of TU). */ @@ -1576,13 +1574,6 @@ void rtl92se_set_beacon_related_registers(struct ieee80211_hw *hw) * other ad hoc STA */ rtl_write_byte(rtlpriv, BCN_ERR_THRESH, 100); - /* Beacon Time Configuration */ - if (mac->opmode == NL80211_IFTYPE_ADHOC) - bcntime_cfg |= (bcn_cw << BCN_TCFG_CW_SHIFT); - - /* TODO: bcn_ifs may required to be changed on ASIC */ - bcntime_cfg |= bcn_ifs << BCN_TCFG_IFS; - /*for beacon changed */ rtl92s_phy_set_beacon_hwreg(hw, mac->beacon_interval); } -- cgit v1.2.3 From 5cf10940a47985260ebbdc4665a8081c6b575e17 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 1 Mar 2023 12:09:28 +0200 Subject: wifi: mac80211_hwsim: Indicate support for NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT mac80211_hwsim always uses the probe request coming from mac80211, which has support for minimal probe request content. Signed-off-by: Ilan Peer Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230301115906.67d31166ffd1.I908b68c5127525da3388c0d61a3d5d221ba451f6@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4cc4eaf80b14..e9b9340a97dc 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -4446,6 +4446,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT); + hw->wiphy->interface_modes = param->iftypes; /* ask mac80211 to reserve space for magic */ -- cgit v1.2.3 From 9179dff82598ab8b4e88dcc93c9e26a2594efd1a Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Wed, 1 Mar 2023 12:09:35 +0200 Subject: wifi: mac80211: add support for driver adding radiotap TLVs The new TLV format enables adding TLVs after the fixed fields in radiotap, as part of the radiotap header. Support this and move vendor data to the TLV format, allowing a reuse of the RX_FLAG_RADIOTAP_VENDOR_DATA as the new RX_FLAG_RADIOTAP_TLV_AT_END flag. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230301115906.b18fd5da8477.I576400ec40a7b35ef97a3b09a99b3a49e9174786@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 27 ++++----- drivers/net/wireless/mac80211_hwsim.c | 45 ++++++++------- include/net/ieee80211_radiotap.h | 20 +++++++ include/net/mac80211.h | 44 +++----------- net/mac80211/rx.c | 82 +++++++++------------------ 5 files changed, 91 insertions(+), 127 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 549dbe0be223..d1769464d75b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -209,33 +209,34 @@ static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_vendor_radiotap *radiotap; - const int size = sizeof(*radiotap) + sizeof(__le16); + struct ieee80211_radiotap_vendor_tlv *radiotap; + const u16 vendor_data_len = sizeof(mvm->cur_aid); + const u16 padding = ALIGN(vendor_data_len, 4) - vendor_data_len; if (!mvm->cur_aid) return; - /* ensure alignment */ - BUILD_BUG_ON((size + 2) % 4); + radiotap = skb_put(skb, sizeof(*radiotap) + vendor_data_len + padding); + radiotap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + radiotap->len = cpu_to_le16(sizeof(*radiotap) - + sizeof(struct ieee80211_radiotap_tlv) + + vendor_data_len); - radiotap = skb_put(skb, size + 2); - radiotap->align = 1; /* Intel OUI */ radiotap->oui[0] = 0xf6; radiotap->oui[1] = 0x54; radiotap->oui[2] = 0x25; /* radiotap sniffer config sub-namespace */ - radiotap->subns = 1; - radiotap->present = 0x1; - radiotap->len = size - sizeof(*radiotap); - radiotap->pad = 2; - + radiotap->oui_subtype = 1; + radiotap->vendor_type = 0; + /* clear reserved field */ + radiotap->reserved = 0; /* fill the data now */ memcpy(radiotap->data, &mvm->cur_aid, sizeof(mvm->cur_aid)); /* and clear the padding */ - memset(radiotap->data + sizeof(__le16), 0, radiotap->pad); + memset(radiotap->data + vendor_data_len, 0, padding); - rx_status->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA; + rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; } /* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */ diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index e9b9340a97dc..152617034d19 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1534,37 +1534,38 @@ static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb) * the values accordingly. */ #ifdef HWSIM_RADIOTAP_OUI - struct ieee80211_vendor_radiotap *rtap; + struct ieee80211_radiotap_vendor_tlv *rtap; + static const char vendor_data[8] = "ABCDEFGH"; + + // Make sure no padding is needed + BUILD_BUG_ON(sizeof(vendor_data) % 4); + /* this is last radiotap info before the mac header, so + * skb_reset_mac_header for mac8022 to know the end of + * the radiotap TLV/beginning of the 802.11 header + */ + skb_reset_mac_header(skb); /* * Note that this code requires the headroom in the SKB * that was allocated earlier. */ - rtap = skb_push(skb, sizeof(*rtap) + 8 + 4); + rtap = skb_push(skb, sizeof(*rtap) + sizeof(vendor_data)); + + rtap->len = cpu_to_le16(sizeof(*rtap) - + sizeof(struct ieee80211_radiotap_tlv) + + sizeof(vendor_data)); + rtap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + rtap->oui[0] = HWSIM_RADIOTAP_OUI[0]; rtap->oui[1] = HWSIM_RADIOTAP_OUI[1]; rtap->oui[2] = HWSIM_RADIOTAP_OUI[2]; - rtap->subns = 127; + rtap->oui_subtype = 127; + /* clear reserved field */ + rtap->reserved = 0; + rtap->vendor_type = 0; + memcpy(rtap->data, vendor_data, sizeof(vendor_data)); - /* - * Radiotap vendor namespaces can (and should) also be - * split into fields by using the standard radiotap - * presence bitmap mechanism. Use just BIT(0) here for - * the presence bitmap. - */ - rtap->present = BIT(0); - /* We have 8 bytes of (dummy) data */ - rtap->len = 8; - /* For testing, also require it to be aligned */ - rtap->align = 8; - /* And also test that padding works, 4 bytes */ - rtap->pad = 4; - /* push the data */ - memcpy(rtap->data, "ABCDEFGH", 8); - /* make sure to clear padding, mac80211 doesn't */ - memset(rtap->data + 8, 0, 4); - - IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA; + IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; #endif } diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 0fc2667a9a5d..95436686d3fe 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -369,6 +369,26 @@ struct ieee80211_radiotap_tlv { u8 data[]; } __packed; +/** + * struct ieee80211_radiotap_vendor_tlv - vendor radiotap data information + * @type: should always be set to IEEE80211_RADIOTAP_VENDOR_NAMESPACE + * @len: length of data + * @oui: radiotap vendor namespace OUI + * @oui_subtype: radiotap vendor sub namespace + * @vendor_type: radiotap vendor type + * @reserved: should always be set to zero (to avoid leaking memory) + * @data: the actual vendor namespace data + */ +struct ieee80211_radiotap_vendor_tlv { + __le16 type; /* IEEE80211_RADIOTAP_VENDOR_NAMESPACE */ + __le16 len; + u8 oui[3]; + u8 oui_subtype; + __le16 vendor_type; + __le16 reserved; + u8 data[]; +} __packed; + /* ieee80211_radiotap_eht_usig - content of U-SIG tlv (type 33) * see www.radiotap.org/fields/U-SIG.html for details */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5df9eb828a58..3a43ce5fd4ec 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1375,9 +1375,12 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * subframes share the same sequence number. Reported subframes can be * either regular MSDU or singly A-MSDUs. Subframes must not be * interleaved with other frames. - * @RX_FLAG_RADIOTAP_VENDOR_DATA: This frame contains vendor-specific - * radiotap data in the skb->data (before the frame) as described by - * the &struct ieee80211_vendor_radiotap. + * @RX_FLAG_RADIOTAP_TLV_AT_END: This frame contains radiotap TLVs in the + * skb->data (before the 802.11 header). + * If used, the SKB's mac_header pointer must be set to point + * to the 802.11 header after the TLVs, and any padding added after TLV + * data to align to 4 must be cleared by the driver putting the TLVs + * in the skb. * @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before. * This is used for AMSDU subframes which can have the same PN as * the first subframe. @@ -1429,7 +1432,7 @@ enum mac80211_rx_flags { RX_FLAG_ONLY_MONITOR = BIT(17), RX_FLAG_SKIP_MONITOR = BIT(18), RX_FLAG_AMSDU_MORE = BIT(19), - RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(20), + RX_FLAG_RADIOTAP_TLV_AT_END = BIT(20), RX_FLAG_MIC_STRIPPED = BIT(21), RX_FLAG_ALLOW_SAME_PN = BIT(22), RX_FLAG_ICV_STRIPPED = BIT(23), @@ -1569,39 +1572,6 @@ ieee80211_rx_status_to_khz(struct ieee80211_rx_status *rx_status) (rx_status->freq_offset ? 500 : 0); } -/** - * struct ieee80211_vendor_radiotap - vendor radiotap data information - * @present: presence bitmap for this vendor namespace - * (this could be extended in the future if any vendor needs more - * bits, the radiotap spec does allow for that) - * @align: radiotap vendor namespace alignment. This defines the needed - * alignment for the @data field below, not for the vendor namespace - * description itself (which has a fixed 2-byte alignment) - * Must be a power of two, and be set to at least 1! - * @oui: radiotap vendor namespace OUI - * @subns: radiotap vendor sub namespace - * @len: radiotap vendor sub namespace skip length, if alignment is done - * then that's added to this, i.e. this is only the length of the - * @data field. - * @pad: number of bytes of padding after the @data, this exists so that - * the skb data alignment can be preserved even if the data has odd - * length - * @data: the actual vendor namespace data - * - * This struct, including the vendor data, goes into the skb->data before - * the 802.11 header. It's split up in mac80211 using the align/oui/subns - * data. - */ -struct ieee80211_vendor_radiotap { - u32 present; - u8 align; - u8 oui[3]; - u8 subns; - u8 pad; - u16 len; - u8 data[]; -} __packed; - /** * enum ieee80211_conf_flags - configuration flags * diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f63ed6b91d9b..0255c5745e1c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -55,7 +55,7 @@ static struct sk_buff *ieee80211_clean_skb(struct sk_buff *skb, /* After pulling radiotap header, clear all flags that indicate * info in skb->data. */ - status->flag &= ~(RX_FLAG_RADIOTAP_VENDOR_DATA | + status->flag &= ~(RX_FLAG_RADIOTAP_TLV_AT_END | RX_FLAG_RADIOTAP_LSIG | RX_FLAG_RADIOTAP_HE_MU | RX_FLAG_RADIOTAP_HE); @@ -126,9 +126,6 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, /* allocate extra bitmaps */ if (status->chains) len += 4 * hweight8(status->chains); - /* vendor presence bitmap */ - if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) - len += 4; if (ieee80211_have_rx_timestamp(status)) { len = ALIGN(len, 8); @@ -190,34 +187,28 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, len += 2 * hweight8(status->chains); } - if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { - struct ieee80211_vendor_radiotap *rtap; - int vendor_data_offset = 0; + if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END) { + int tlv_offset = 0; /* * The position to look at depends on the existence (or non- * existence) of other elements, so take that into account... */ if (status->flag & RX_FLAG_RADIOTAP_HE) - vendor_data_offset += + tlv_offset += sizeof(struct ieee80211_radiotap_he); if (status->flag & RX_FLAG_RADIOTAP_HE_MU) - vendor_data_offset += + tlv_offset += sizeof(struct ieee80211_radiotap_he_mu); if (status->flag & RX_FLAG_RADIOTAP_LSIG) - vendor_data_offset += + tlv_offset += sizeof(struct ieee80211_radiotap_lsig); - rtap = (void *)&skb->data[vendor_data_offset]; + /* ensure 4 byte alignment for TLV */ + len = ALIGN(len, 4); - /* alignment for fixed 6-byte vendor data header */ - len = ALIGN(len, 2); - /* vendor data header */ - len += 6; - if (WARN_ON(rtap->align == 0)) - rtap->align = 1; - len = ALIGN(len, rtap->align); - len += rtap->len + rtap->pad; + /* TLVs until the mac header */ + len += skb_mac_header(skb) - &skb->data[tlv_offset]; } return len; @@ -313,9 +304,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, u32 it_present_val; u16 rx_flags = 0; u16 channel_flags = 0; + u32 tlvs_len = 0; int mpdulen, chain; unsigned long chains = status->chains; - struct ieee80211_vendor_radiotap rtap = {}; struct ieee80211_radiotap_he he = {}; struct ieee80211_radiotap_he_mu he_mu = {}; struct ieee80211_radiotap_lsig lsig = {}; @@ -336,18 +327,17 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, skb_pull(skb, sizeof(lsig)); } - if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { - rtap = *(struct ieee80211_vendor_radiotap *)skb->data; - /* rtap.len and rtap.pad are undone immediately */ - skb_pull(skb, sizeof(rtap) + rtap.len + rtap.pad); + if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END) { + /* data is pointer at tlv all other info was pulled off */ + tlvs_len = skb_mac_header(skb) - skb->data; } mpdulen = skb->len; if (!(has_fcs && ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS))) mpdulen += FCS_LEN; - rthdr = skb_push(skb, rtap_len); - memset(rthdr, 0, rtap_len - rtap.len - rtap.pad); + rthdr = skb_push(skb, rtap_len - tlvs_len); + memset(rthdr, 0, rtap_len - tlvs_len); it_present = &rthdr->it_present; /* radiotap header, set always present flags */ @@ -369,13 +359,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL); } - if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { - it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | - BIT(IEEE80211_RADIOTAP_EXT); - put_unaligned_le32(it_present_val, it_present); - it_present++; - it_present_val = rtap.present; - } + if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END) + it_present_val |= BIT(IEEE80211_RADIOTAP_TLV); put_unaligned_le32(it_present_val, it_present); @@ -706,22 +691,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = status->chain_signal[chain]; *pos++ = chain; } - - if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { - /* ensure 2 byte alignment for the vendor field as required */ - if ((pos - (u8 *)rthdr) & 1) - *pos++ = 0; - *pos++ = rtap.oui[0]; - *pos++ = rtap.oui[1]; - *pos++ = rtap.oui[2]; - *pos++ = rtap.subns; - put_unaligned_le16(rtap.len, pos); - pos += 2; - /* align the actual payload as requested */ - while ((pos - (u8 *)rthdr) & (rtap.align - 1)) - *pos++ = 0; - /* data (and possible padding) already follows */ - } } static struct sk_buff * @@ -797,6 +766,13 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, bool only_monitor = false; unsigned int min_head_len; + if (WARN_ON_ONCE(status->flag & RX_FLAG_RADIOTAP_TLV_AT_END && + !skb_mac_header_was_set(origskb))) { + /* with this skb no way to know where frame payload starts */ + dev_kfree_skb(origskb); + return NULL; + } + if (status->flag & RX_FLAG_RADIOTAP_HE) rtap_space += sizeof(struct ieee80211_radiotap_he); @@ -806,12 +782,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (status->flag & RX_FLAG_RADIOTAP_LSIG) rtap_space += sizeof(struct ieee80211_radiotap_lsig); - if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { - struct ieee80211_vendor_radiotap *rtap = - (void *)(origskb->data + rtap_space); - - rtap_space += sizeof(*rtap) + rtap->len + rtap->pad; - } + if (status->flag & RX_FLAG_RADIOTAP_TLV_AT_END) + rtap_space += skb_mac_header(origskb) - &origskb->data[rtap_space]; min_head_len = rtap_space; -- cgit v1.2.3 From eccfe0176cc11432f1bc7e8b035164d6263df715 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:15 +0200 Subject: wifi: iwlwifi: mvm: add LSIG info to radio tap info in EHT Nothing change with LSIG in EHT so just extend the switch case to include EHT standard. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124406.53edcfc049fb.Iec0401faead108aa5130c23c604cfb88ef2943de@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index d1769464d75b..3d7717fdb4d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1594,6 +1594,10 @@ static void iwl_mvm_decode_lsig(struct sk_buff *skb, case IWL_RX_PHY_INFO_TYPE_HE_MU: case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: case IWL_RX_PHY_INFO_TYPE_HE_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU: + case IWL_RX_PHY_INFO_TYPE_EHT_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT: lsig = skb_put(skb, sizeof(*lsig)); lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN); lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->d1, -- cgit v1.2.3 From f567b9b46c9e8ecc12190a109ed16006c9f441fa Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:16 +0200 Subject: wifi: iwlwifi: mvm: mark mac header with no data frames Although no data is presented in the skb, but upper layers need it for calculating where radio tap header are done, so we mark it. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124406.8f53cc8579bf.I8819c005b4953cea03346d0aff82b31d94f79fe5@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 3d7717fdb4d5..ba9f79ada82f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -2147,11 +2147,8 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, * * We mark it as mac header, for upper layers to know where * all radio tap header ends. - * - * Since data doesn't move data while putting data on skb and that is - * the only way we use, data + len is the next place that hdr would be put */ - skb_set_mac_header(skb, skb->len); + skb_reset_mac_header(skb); /* * Override the nss from the rx_vec since the rate_n_flags has -- cgit v1.2.3 From 7f165fdf2967a5b7aa4b123cc9586e9f5068325b Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Sun, 5 Mar 2023 14:16:17 +0200 Subject: wifi: iwlwifi: Adding the code to get RF name for MsP device Add missing RF name extraction for MsP device from hardware rf-id. Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124406.ae831f21ec55.Iad71cc30f9930f7ebe079d210ebe6c968a159273@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 3e1f011e93aa..bece76b1a514 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -348,6 +348,7 @@ enum { #define CSR_HW_RF_ID_TYPE_HRCDB (0x00109F00) #define CSR_HW_RF_ID_TYPE_GF (0x0010D000) #define CSR_HW_RF_ID_TYPE_GF4 (0x0010E000) +#define CSR_HW_RF_ID_TYPE_MS (0x00111000) /* HW_RF CHIP STEP */ #define CSR_HW_RF_STEP(_val) (((_val) >> 8) & 0xF) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 94f40c4d2421..1e263154e9eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2022 Intel Corporation */ #include "iwl-trans.h" #include "iwl-prph.h" @@ -277,6 +277,9 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB): pos = scnprintf(buf, buflen, "HRCDB"); break; + case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_MS): + pos = scnprintf(buf, buflen, "MS"); + break; default: return; } -- cgit v1.2.3 From 876882b51569bf686ae4c421b0a738b78a536aa3 Mon Sep 17 00:00:00 2001 From: Golan Ben Ami Date: Sun, 5 Mar 2023 14:16:18 +0200 Subject: wifi: iwlwifi: reduce verbosity of some logging events These are cases in which we'd like to warn that something unexpected happened but they may not be errors. Reduce verbosity. Signed-off-by: Golan Ben Ami Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.5eea0f58a74f.Ifb6b35903a5a452a757bfe50b6a7e58b3fd2ef23@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 48e7376a5fea..8e0bc1f5f6c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -350,9 +350,9 @@ void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv, ret = dbg_tlv_alloc[tlv_idx](trans, tlv); if (ret) { - IWL_ERR(trans, - "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n", - type, ret, ext); + IWL_WARN(trans, + "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n", + type, ret, ext); goto out_err; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 49ca1e168fc5..ab9a51375c8a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -190,7 +190,7 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, default: /* Expected in monitor (not having the keys) */ if (!mvm->monitor_on) - IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); + IWL_WARN(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); } return 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index ba9f79ada82f..2db4f68becff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -444,7 +444,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta, */ if (!is_multicast_ether_addr(hdr->addr1) && !mvm->monitor_on && net_ratelimit()) - IWL_ERR(mvm, "Unhandled alg: 0x%x\n", status); + IWL_WARN(mvm, "Unhandled alg: 0x%x\n", status); } return 0; -- cgit v1.2.3 From 11a2638d120b9d998916efb6fc55c6422e469ffa Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:19 +0200 Subject: wifi: radiotap: separate vendor TLV into header/content To be able to use a general function later for any kind of TLV, separate the vendor TLV header/content in the structs. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.8ac5195bb3e6.I19ad99c1ad3108453aede64bddf6ef1a7c4a0b74@changeid [separate from the original combined patch] Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 16 ++++++++-------- drivers/net/wireless/mac80211_hwsim.c | 14 +++++++------- include/net/ieee80211_radiotap.h | 20 ++++++++++++++------ 3 files changed, 29 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 2db4f68becff..71a6555f90d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -223,18 +223,18 @@ static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm, vendor_data_len); /* Intel OUI */ - radiotap->oui[0] = 0xf6; - radiotap->oui[1] = 0x54; - radiotap->oui[2] = 0x25; + radiotap->content.oui[0] = 0xf6; + radiotap->content.oui[1] = 0x54; + radiotap->content.oui[2] = 0x25; /* radiotap sniffer config sub-namespace */ - radiotap->oui_subtype = 1; - radiotap->vendor_type = 0; + radiotap->content.oui_subtype = 1; + radiotap->content.vendor_type = 0; /* clear reserved field */ - radiotap->reserved = 0; + radiotap->content.reserved = 0; /* fill the data now */ - memcpy(radiotap->data, &mvm->cur_aid, sizeof(mvm->cur_aid)); + memcpy(radiotap->content.data, &mvm->cur_aid, sizeof(mvm->cur_aid)); /* and clear the padding */ - memset(radiotap->data + vendor_data_len, 0, padding); + memset(radiotap->content.data + vendor_data_len, 0, padding); rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 152617034d19..f4bdc243ea0d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1556,14 +1556,14 @@ static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb) sizeof(vendor_data)); rtap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE); - rtap->oui[0] = HWSIM_RADIOTAP_OUI[0]; - rtap->oui[1] = HWSIM_RADIOTAP_OUI[1]; - rtap->oui[2] = HWSIM_RADIOTAP_OUI[2]; - rtap->oui_subtype = 127; + rtap->content.oui[0] = HWSIM_RADIOTAP_OUI[0]; + rtap->content.oui[1] = HWSIM_RADIOTAP_OUI[1]; + rtap->content.oui[2] = HWSIM_RADIOTAP_OUI[2]; + rtap->content.oui_subtype = 127; /* clear reserved field */ - rtap->reserved = 0; - rtap->vendor_type = 0; - memcpy(rtap->data, vendor_data, sizeof(vendor_data)); + rtap->content.reserved = 0; + rtap->content.vendor_type = 0; + memcpy(rtap->content.data, vendor_data, sizeof(vendor_data)); IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; #endif diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 95436686d3fe..f980a72f2ce6 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -370,18 +370,14 @@ struct ieee80211_radiotap_tlv { } __packed; /** - * struct ieee80211_radiotap_vendor_tlv - vendor radiotap data information - * @type: should always be set to IEEE80211_RADIOTAP_VENDOR_NAMESPACE - * @len: length of data + * struct ieee80211_radiotap_vendor_content - radiotap vendor data content * @oui: radiotap vendor namespace OUI * @oui_subtype: radiotap vendor sub namespace * @vendor_type: radiotap vendor type * @reserved: should always be set to zero (to avoid leaking memory) * @data: the actual vendor namespace data */ -struct ieee80211_radiotap_vendor_tlv { - __le16 type; /* IEEE80211_RADIOTAP_VENDOR_NAMESPACE */ - __le16 len; +struct ieee80211_radiotap_vendor_content { u8 oui[3]; u8 oui_subtype; __le16 vendor_type; @@ -389,6 +385,18 @@ struct ieee80211_radiotap_vendor_tlv { u8 data[]; } __packed; +/** + * struct ieee80211_radiotap_vendor_tlv - vendor radiotap data information + * @type: should always be set to IEEE80211_RADIOTAP_VENDOR_NAMESPACE + * @len: length of data + * @content: vendor content see @ieee80211_radiotap_vendor_content + */ +struct ieee80211_radiotap_vendor_tlv { + __le16 type; /* IEEE80211_RADIOTAP_VENDOR_NAMESPACE */ + __le16 len; + struct ieee80211_radiotap_vendor_content content; +}; + /* ieee80211_radiotap_eht_usig - content of U-SIG tlv (type 33) * see www.radiotap.org/fields/U-SIG.html for details */ -- cgit v1.2.3 From 056805bcc6bc439de1c9fbb1854042fdf40c8638 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:19 +0200 Subject: wifi: iwlwifi: mvm: add an helper function radiotap TLVs Add a helper function setting type, length, zeroing out TLV data and including adding padding if necessary. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.8ac5195bb3e6.I19ad99c1ad3108453aede64bddf6ef1a7c4a0b74@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 43 ++++++++++++++++----------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 71a6555f90d9..206b56ca8db8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -205,36 +205,45 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb, return 0; } +/* put a TLV on the skb and return data pointer + * + * Also pad to 4 the len and zero out all data part + */ +static void * +iwl_mvm_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len) +{ + struct ieee80211_radiotap_tlv *tlv; + + tlv = skb_put(skb, sizeof(*tlv)); + tlv->type = cpu_to_le16(type); + tlv->len = cpu_to_le16(len); + return skb_put_zero(skb, ALIGN(len, 4)); +} + static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm, struct sk_buff *skb) { struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_radiotap_vendor_tlv *radiotap; + struct ieee80211_radiotap_vendor_content *radiotap; const u16 vendor_data_len = sizeof(mvm->cur_aid); - const u16 padding = ALIGN(vendor_data_len, 4) - vendor_data_len; if (!mvm->cur_aid) return; - radiotap = skb_put(skb, sizeof(*radiotap) + vendor_data_len + padding); - radiotap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE); - radiotap->len = cpu_to_le16(sizeof(*radiotap) - - sizeof(struct ieee80211_radiotap_tlv) + - vendor_data_len); + radiotap = iwl_mvm_radiotap_put_tlv(skb, + IEEE80211_RADIOTAP_VENDOR_NAMESPACE, + sizeof(*radiotap) + vendor_data_len); /* Intel OUI */ - radiotap->content.oui[0] = 0xf6; - radiotap->content.oui[1] = 0x54; - radiotap->content.oui[2] = 0x25; + radiotap->oui[0] = 0xf6; + radiotap->oui[1] = 0x54; + radiotap->oui[2] = 0x25; /* radiotap sniffer config sub-namespace */ - radiotap->content.oui_subtype = 1; - radiotap->content.vendor_type = 0; - /* clear reserved field */ - radiotap->content.reserved = 0; + radiotap->oui_subtype = 1; + radiotap->vendor_type = 0; + /* fill the data now */ - memcpy(radiotap->content.data, &mvm->cur_aid, sizeof(mvm->cur_aid)); - /* and clear the padding */ - memset(radiotap->content.data + vendor_data_len, 0, padding); + memcpy(radiotap->data, &mvm->cur_aid, sizeof(mvm->cur_aid)); rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; } -- cgit v1.2.3 From 24f7f6e3ed3b91381307c649a32aff9a69b7cae6 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:20 +0200 Subject: wifi: iwlwifi: mvm: add EHT radiotap info based on rate_n_flags rate_n_flags is always present in the data so at least give all of the information we can extract from rate_n_flags Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.b1c7d49ad35e.Ie2412ac6f88700aa3767ff95ffb52a806b13b7ce@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/rs.h | 24 +++-- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 135 +++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index ddacd5b45aea..1d372a09ebb4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -449,11 +449,16 @@ enum { * 1 2xLTF+0.8us * 2 2xLTF+1.6us * 3 4xLTF+3.2us - * HE TRIG: + * HE-EHT TRIG: * 0 1xLTF+1.6us * 1 2xLTF+1.6us * 2 4xLTF+3.2us * 3 (does not occur) + * EHT MU: + * 0 2xLTF+0.8us + * 1 2xLTF+1.6us + * 2 4xLTF+0.8us + * 3 4xLTF+3.2us */ #define RATE_MCS_HE_GI_LTF_POS 20 #define RATE_MCS_HE_GI_LTF_MSK_V1 (3 << RATE_MCS_HE_GI_LTF_POS) @@ -546,12 +551,17 @@ enum { /* * Bits 13-11: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz, (4) 320MHz */ -#define RATE_MCS_CHAN_WIDTH_MSK (0x7 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_320 (4 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_MSK (0x7 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_20_VAL 0 +#define RATE_MCS_CHAN_WIDTH_20 (RATE_MCS_CHAN_WIDTH_20_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_40_VAL 1 +#define RATE_MCS_CHAN_WIDTH_40 (RATE_MCS_CHAN_WIDTH_40_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_80_VAL 2 +#define RATE_MCS_CHAN_WIDTH_80 (RATE_MCS_CHAN_WIDTH_80_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_160_VAL 3 +#define RATE_MCS_CHAN_WIDTH_160 (RATE_MCS_CHAN_WIDTH_160_VAL << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_320_VAL 4 +#define RATE_MCS_CHAN_WIDTH_320 (RATE_MCS_CHAN_WIDTH_320_VAL << RATE_MCS_CHAN_WIDTH_POS) /* Bit 15-14: Antenna selection: * Bit 14: Ant A active diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 206b56ca8db8..aa72910f8ff2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1456,6 +1456,137 @@ static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm, } } +static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, + struct iwl_mvm_rx_phy_data *phy_data, + int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + + struct ieee80211_radiotap_eht *eht; + struct ieee80211_radiotap_eht_usig *usig; + + u32 rate_n_flags = phy_data->rate_n_flags; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + /* EHT and HE have the same valus for LTF */ + u8 ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN; + u16 phy_info = phy_data->phy_info; + u32 bw; + + /* u32 for 1 user_info */ + eht = iwl_mvm_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, + sizeof(*eht) + sizeof(u32)); + + usig = iwl_mvm_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG, + sizeof(*usig)); + rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; + usig->common |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN); + + /* specific handling for 320MHz */ + bw = FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, rate_n_flags); + if (bw == RATE_MCS_CHAN_WIDTH_320_VAL) + bw += FIELD_GET(IWL_RX_PHY_DATA0_EHT_BW320_SLOT, + le32_to_cpu(phy_data->d0)); + + usig->common |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW, bw)); + + /* report the AMPDU-EOF bit on single frames */ + if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && + (phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; + + /* toggle is switched whenever new aggregation starts */ + if (toggle_bit != mvm->ampdu_toggle) { + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + } + + /* TODO: fill usig info (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) */ + +#define CHECK_TYPE(F) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ + (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) + + CHECK_TYPE(SU); + CHECK_TYPE(EXT_SU); + CHECK_TYPE(MU); + CHECK_TYPE(TRIG); + + switch (FIELD_GET(RATE_MCS_HE_GI_LTF_MSK, rate_n_flags)) { + case 0: + if (he_type == RATE_MCS_HE_TYPE_TRIG) { + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X; + } else { + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + } + break; + case 1: + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + break; + case 2: + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; + else + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; + break; + case 3: + if (he_type != RATE_MCS_HE_TYPE_TRIG) { + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; + } + break; + default: + /* nothing here */ + break; + } + + if (ltf != IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN) { + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_GI); + eht->data[0] |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_LTF, + ltf) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_GI, + rx_status->eht.gi)); + } + + eht->user_info[0] |= cpu_to_le32 + (IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_DATA_FOR_USER); + + if (rate_n_flags & RATE_MCS_BF_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + + if (rate_n_flags & RATE_MCS_LDPC_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING); + + eht->user_info[0] |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS, + FIELD_GET(RATE_VHT_MCS_RATE_CODE_MSK, + rate_n_flags)) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O, + FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags))); +} + static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb, struct iwl_mvm_rx_phy_data *phy_data, int queue) @@ -1703,6 +1834,10 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm, iwl_mvm_get_signal_strength(mvm, rx_status, rate_n_flags, phy_data->energy_a, phy_data->energy_b); + /* using TLV format and must be after all fixed len fields */ + if (format == RATE_MCS_EHT_MSK) + iwl_mvm_rx_eht(mvm, skb, phy_data, queue); + if (unlikely(mvm->monitor_on)) iwl_mvm_add_rtap_sniffer_config(mvm, skb); -- cgit v1.2.3 From 4ec825854c3c1e7a2cb3fedd6d1acbba0a3e2d74 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:21 +0200 Subject: wifi: iwlwifi: mvm: add all EHT based on data0 info from HW Update all radiotap EHT TLVs that we can extract from data0 in HW. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.730f219e02ee.Ife3dd85c65758694d7602e8bc8660887d77faacf@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/rx.h | 3 +- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 73 +++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index 1c4e84932058..544d22472a6f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -367,7 +367,8 @@ enum iwl_rx_phy_eht_data1 { /* number of EHT-LTF symbols 0 - 1 EHT-LTF, 1 - 2 EHT-LTFs, 2 - 4 EHT-LTFs, * 3 - 6 EHT-LTFs, 4 - 8 EHT-LTFs */ IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM = 0x000000e0, - IWL_RX_PHY_DATA1_EHT_RU_ALLOC = 0x0000ff00, + IWL_RX_PHY_DATA1_EHT_B0 = 0x00000100, + IWL_RX_PHY_DATA1_EHT_RU_B1_B7_ALLOC = 0x0000fe00, }; /* goes into Metadata DW 7 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index aa72910f8ff2..174bed9b5074 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -1456,6 +1456,74 @@ static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm, } } +#define LE32_DEC_ENC(value, dec_bits, enc_bits) \ + le32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) + +static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, + struct iwl_mvm_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) + +{ + __le32 data0 = phy_data->d0; + __le32 data1 = phy_data->d1; + u8 info_type = phy_data->info_type; + + /* Not in EHT range */ + if (info_type < IWL_RX_PHY_INFO_TYPE_EHT_MU || + info_type > IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT) + return; + + usig->common |= cpu_to_le32 + (IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN); + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_UPLINK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE); + eht->data[0] |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_ETH_SPATIAL_REUSE_MASK, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + + /* All RU allocating size/index is in TB format */ + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT); + eht->data[8] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PS160, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160); + eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_B0, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0); + eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_B1_B7_ALLOC, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN); + usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_LDPC_EXT_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PRE_FEC_PAD_MASK, + IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PE_DISAMBIG, + IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM); + + /* TODO: what about IWL_RX_PHY_DATA0_EHT_BW320_SLOT */ + + if (!le32_get_bits(data0, IWL_RX_PHY_DATA0_EHT_SIGA_CRC_OK)) + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN); + usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PHY_VER, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER); +} + static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, struct iwl_mvm_rx_phy_data *phy_data, int queue) @@ -1512,7 +1580,8 @@ static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, } } - /* TODO: fill usig info (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) */ + if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) + iwl_mvm_decode_eht_phy_data(mvm, phy_data, rx_status, eht, usig); #define CHECK_TYPE(F) \ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ -- cgit v1.2.3 From 3ecf34118dc80a0508dc82e284617513cbb270e8 Mon Sep 17 00:00:00 2001 From: Alon Giladi Date: Sun, 5 Mar 2023 14:16:22 +0200 Subject: wifi: iwlwifi: mvm: allow Microsoft to use TAS Add Microsoft to the list of OEMs which allowed to use TAS. Signed-off-by: Alon Giladi Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.662967fec1cc.Icb30cddc049cb5402fd5ab2ce7f95033e478b1b9@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0c6b49fcb00d..45981e22b2db 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1084,6 +1084,11 @@ static const struct dmi_system_id dmi_tas_approved_list[] = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), }, }, + { .ident = "MSFT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + }, + }, /* keep last */ {} -- cgit v1.2.3 From c0da321b601a92453f928c819dbd65c5712480d3 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:23 +0200 Subject: wifi: iwlwifi: mvm: rename define to generic name The type RX_NO_DATA_INFO_TYPE_HE_TB_UNMATCHED is applied to all TB frames including EHT mode, so rename accordingly. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.e4f51f347e48.I2d6ecb6eadc95666d2ef9794662ee779488ceac1@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/rx.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index 544d22472a6f..0f190266fffd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -726,7 +726,7 @@ struct iwl_rx_mpdu_desc { #define RX_NO_DATA_INFO_TYPE_RX_ERR 1 #define RX_NO_DATA_INFO_TYPE_NDP 2 #define RX_NO_DATA_INFO_TYPE_MU_UNMATCHED 3 -#define RX_NO_DATA_INFO_TYPE_HE_TB_UNMATCHED 4 +#define RX_NO_DATA_INFO_TYPE_TB_UNMATCHED 4 #define RX_NO_DATA_INFO_ERR_POS 8 #define RX_NO_DATA_INFO_ERR_MSK (0xff << RX_NO_DATA_INFO_ERR_POS) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 174bed9b5074..1fb77cda568a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -2341,7 +2341,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING; break; case RX_NO_DATA_INFO_TYPE_MU_UNMATCHED: - case RX_NO_DATA_INFO_TYPE_HE_TB_UNMATCHED: + case RX_NO_DATA_INFO_TYPE_TB_UNMATCHED: rx_status->zero_length_psdu_type = IEEE80211_RADIOTAP_ZERO_LEN_PSDU_NOT_CAPTURED; break; -- cgit v1.2.3 From e8c0a6fd08d7dc6c178e77f154794cdd55575780 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:24 +0200 Subject: wifi: iwlwifi: mvm: decode USIG_B1_B7 RU to nl80211 RU width This is based on 802.11be D1.5 table 9-53a Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.0b720d6d6a48.I0034dd108696223494799d3ffe4f09685800b831@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 71 +++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 1fb77cda568a..c069e81bf3f1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1459,6 +1459,75 @@ static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm, #define LE32_DEC_ENC(value, dec_bits, enc_bits) \ le32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) +static void iwl_mvm_decode_eht_ru(struct iwl_mvm *mvm, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht) +{ + u32 ru = le32_get_bits(eht->data[8], + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + enum nl80211_eht_ru_alloc nl_ru; + + /* Using D1.5 Table 9-53a - Encoding of PS160 and RU Allocation subfields + * in an EHT variant User Info field + */ + + switch (ru) { + case 0 ... 36: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_26; + break; + case 37 ... 52: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52; + break; + case 53 ... 60: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106; + break; + case 61 ... 64: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_242; + break; + case 65 ... 66: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484; + break; + case 67: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996; + break; + case 68: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; + break; + case 69: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; + break; + case 70 ... 81: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; + break; + case 82 ... 89: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; + break; + case 90 ... 93: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; + break; + case 94 ... 95: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; + break; + case 96 ... 99: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; + break; + case 100 ... 103: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; + break; + case 104: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; + break; + case 105 ... 106: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; + break; + default: + return; + } + + rx_status->bw = RATE_INFO_BW_EHT_RU; + rx_status->eht.ru = nl_ru; +} + static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, struct iwl_mvm_rx_phy_data *phy_data, struct ieee80211_rx_status *rx_status, @@ -1499,6 +1568,8 @@ static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_B1_B7_ALLOC, IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + iwl_mvm_decode_eht_ru(mvm, rx_status, eht); + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN); usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); -- cgit v1.2.3 From feb4a0e215fa98f72c37b51344d3c013bb43e013 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:25 +0200 Subject: wifi: iwlwifi: mvm: parse FW frame metadata for EHT sniffer mode In EHT sniffer mode DW4 is all used for sniffer data (unlike we have in HE mode), so move the full DW4 into a union, and we extract the new data5 used for parsing USIG info and set all to radiotap TLVs with the extracted data. Also parse OFDM_RX_VECTOR_USIG_A1_OUT and OFDM_RX_VECTOR_USIG_A2_OUT for rx_no_data notification. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.557d3870753b.I4e9fa4d21900a187753529d46956ba2a7ee75fda@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/rx.h | 66 ++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 170 +++++++++++++++++++++++-- 2 files changed, 213 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index 0f190266fffd..97e946e70279 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -674,22 +674,31 @@ struct iwl_rx_mpdu_desc { * @mac_phy_idx: MAC/PHY index */ u8 mac_phy_idx; - /* DW4 - carries csum data only when rpa_en == 1 */ - /** - * @raw_csum: raw checksum (alledgedly unreliable) - */ - __le16 raw_csum; - + /* DW4 */ union { + struct { + /* carries csum data only when rpa_en == 1 */ + /** + * @raw_csum: raw checksum (alledgedly unreliable) + */ + __le16 raw_csum; + + union { + /** + * @l3l4_flags: &enum iwl_rx_l3l4_flags + */ + __le16 l3l4_flags; + + /** + * @phy_data4: depends on info type, see phy_data1 + */ + __le16 phy_data4; + }; + }; /** - * @l3l4_flags: &enum iwl_rx_l3l4_flags - */ - __le16 l3l4_flags; - - /** - * @phy_data4: depends on info type, see phy_data1 + * @phy_eht_data4: depends on info type, see phy_data1 */ - __le16 phy_data4; + __le32 phy_eht_data4; }; /* DW5 */ /** @@ -744,6 +753,35 @@ struct iwl_rx_mpdu_desc { #define RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK 0x38000000 #define RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK 0x00f00000 +/* content of OFDM_RX_VECTOR_USIG_A1_OUT */ +enum iwl_rx_usig_a1 { + IWL_RX_USIG_A1_ENHANCED_WIFI_VER_ID = 0x00000007, + IWL_RX_USIG_A1_BANDWIDTH = 0x00000038, + IWL_RX_USIG_A1_UL_FLAG = 0x00000040, + IWL_RX_USIG_A1_BSS_COLOR = 0x00001f80, + IWL_RX_USIG_A1_TXOP_DURATION = 0x000fe000, + IWL_RX_USIG_A1_DISREGARD = 0x01f00000, + IWL_RX_USIG_A1_VALIDATE = 0x02000000, + IWL_RX_USIG_A1_EHT_BW320_SLOT = 0x04000000, + IWL_RX_USIG_A1_EHT_TYPE = 0x18000000, + IWL_RX_USIG_A1_RDY = 0x80000000, +}; + +/* content of OFDM_RX_VECTOR_USIG_A2_EHT_OUT */ +enum iwl_rx_usig_a2_eht { + IWL_RX_USIG_A2_EHT_PPDU_TYPE = 0x00000003, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2 = 0x00000004, + IWL_RX_USIG_A2_EHT_PUNC_CHANNEL = 0x000000f8, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8 = 0x00000100, + IWL_RX_USIG_A2_EHT_SIG_MCS = 0x00000600, + IWL_RX_USIG_A2_EHT_SIG_SYM_NUM = 0x0000f800, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1 = 0x000f0000, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2 = 0x00f00000, + IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD = 0x1f000000, + IWL_RX_USIG_A2_EHT_CRC_OK = 0x40000000, + IWL_RX_USIG_A2_EHT_RDY = 0x80000000, +}; + /** * struct iwl_rx_no_data - RX no data descriptor * @info: 7:0 frame type, 15:8 RX error type @@ -781,7 +819,7 @@ struct iwl_rx_no_data { * @rx_vec: DW-12:9 raw RX vectors from DSP according to modulation type. * for VHT: OFDM_RX_VECTOR_SIGA1_OUT, OFDM_RX_VECTOR_SIGA2_OUT * for HE: OFDM_RX_VECTOR_HE_SIGA1_OUT, OFDM_RX_VECTOR_HE_SIGA2_OUT - * for EHT: OFDM_RX_VECTOR_USIG_A1_OUT, OFDM_RX_VECTOR_USIG_A2_OUT, + * for EHT: OFDM_RX_VECTOR_USIG_A1_OUT, OFDM_RX_VECTOR_USIG_A2_EHT_OUT, * OFDM_RX_VECTOR_EHT_OUT, OFDM_RX_VECTOR_EHT_USER_FIELD_OUT */ struct iwl_rx_no_data_ver_3 { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index c069e81bf3f1..1334c96098a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1177,8 +1177,10 @@ static void iwl_mvm_flip_address(u8 *addr) struct iwl_mvm_rx_phy_data { enum iwl_rx_phy_info_type info_type; - __le32 d0, d1, d2, d3; + __le32 d0, d1, d2, d3, eht_d4, d5; __le16 d4; + bool with_data; + __le32 rx_vec[4]; u32 rate_n_flags; u32 gp2_on_air_rise; @@ -1459,6 +1461,119 @@ static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm, #define LE32_DEC_ENC(value, dec_bits, enc_bits) \ le32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) +#define IWL_MVM_ENC_USIG_VALUE_MASK(usig, in_value, dec_bits, enc_bits) do { \ + typeof(enc_bits) _enc_bits = enc_bits; \ + typeof(usig) _usig = usig; \ + (_usig)->mask |= cpu_to_le32(_enc_bits); \ + (_usig)->value |= LE32_DEC_ENC(in_value, dec_bits, _enc_bits); \ +} while (0) + +static void iwl_mvm_decode_eht_ext_mu(struct iwl_mvm *mvm, + struct iwl_mvm_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) +{ + __le32 data1 = phy_data->d1; + + if (phy_data->with_data) { + __le32 data4 = phy_data->eht_d4; + __le32 data5 = phy_data->d5; + + IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_MU_PUNC_CH_CODE, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, data4, + IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); + IWL_MVM_ENC_USIG_VALUE_MASK + (usig, data1, IWL_RX_PHY_DATA1_EHT_MU_NUM_SIG_SYM_USIGA2, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + + } else { + __le32 usig_a1 = phy_data->rx_vec[0]; + __le32 usig_a2 = phy_data->rx_vec[1]; + + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_VALIDATE, + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PPDU_TYPE, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PUNC_CHANNEL, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_SIG_MCS, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); + IWL_MVM_ENC_USIG_VALUE_MASK + (usig, usig_a2, IWL_RX_USIG_A2_EHT_SIG_SYM_NUM, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_CRC_OK, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC); + } +} + +static void iwl_mvm_decode_eht_ext_tb(struct iwl_mvm *mvm, + struct iwl_mvm_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) +{ + if (phy_data->with_data) { + __le32 data5 = phy_data->d5; + + IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE1, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); + + IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); + } else { + __le32 usig_a1 = phy_data->rx_vec[0]; + __le32 usig_a2 = phy_data->rx_vec[1]; + + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PPDU_TYPE, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD); + IWL_MVM_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_CRC_OK, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC); + } +} + static void iwl_mvm_decode_eht_ru(struct iwl_mvm *mvm, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_eht *eht) @@ -1537,6 +1652,7 @@ static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, { __le32 data0 = phy_data->d0; __le32 data1 = phy_data->d1; + __le32 usig_a1 = phy_data->rx_vec[0]; u8 info_type = phy_data->info_type; /* Not in EHT range */ @@ -1547,12 +1663,21 @@ static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, usig->common |= cpu_to_le32 (IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN); - usig->common |= LE32_DEC_ENC(data0, - IWL_RX_PHY_DATA0_EHT_UPLINK, - IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); - usig->common |= LE32_DEC_ENC(data0, - IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK, - IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN); + if (phy_data->with_data) { + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_UPLINK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); + } else { + usig->common |= LE32_DEC_ENC(usig_a1, + IWL_RX_USIG_A1_UL_FLAG, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); + usig->common |= LE32_DEC_ENC(usig_a1, + IWL_RX_USIG_A1_BSS_COLOR, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); + } eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE); eht->data[0] |= LE32_DEC_ENC(data0, @@ -1571,8 +1696,13 @@ static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, iwl_mvm_decode_eht_ru(mvm, rx_status, eht); usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN); - usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, - IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + if (phy_data->with_data) + usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + else + usig->common |= LE32_DEC_ENC(usig_a1, IWL_RX_USIG_A1_TXOP_DURATION, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM); eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_LDPC_EXT_SYM, IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); @@ -1593,6 +1723,23 @@ static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN); usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PHY_VER, IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER); + + /* + * TODO: what about TB - IWL_RX_PHY_DATA1_EHT_TB_PILOT_TYPE, + * IWL_RX_PHY_DATA1_EHT_TB_LOW_SS + */ + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF); + eht->data[0] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT || + info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB) + iwl_mvm_decode_eht_ext_tb(mvm, phy_data, rx_status, eht, usig); + + if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT || + info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU) + iwl_mvm_decode_eht_ext_mu(mvm, phy_data, rx_status, eht, usig); } static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, @@ -2077,6 +2224,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, phy_data.d1 = desc->v3.phy_data1; phy_data.d2 = desc->v3.phy_data2; phy_data.d3 = desc->v3.phy_data3; + phy_data.eht_d4 = desc->phy_eht_data4; + phy_data.d5 = desc->v3.phy_data5; } else { phy_data.rate_n_flags = le32_to_cpu(desc->v1.rate_n_flags); phy_data.channel = desc->v1.channel; @@ -2106,6 +2255,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, return; } + phy_data.with_data = true; phy_data.phy_info = le16_to_cpu(desc->phy_info); phy_data.d4 = desc->phy_data4; @@ -2368,6 +2518,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK); phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK); phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); + phy_data.with_data = false; if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, RX_NO_DATA_NOTIF, 0) < 2) { @@ -2386,6 +2537,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, sizeof(struct iwl_rx_no_data_ver_3))) /* invalid len for ver 3 */ return; + memcpy(phy_data.rx_vec, desc->rx_vec, sizeof(phy_data.rx_vec)); } else { if (format == RATE_MCS_EHT_MSK) /* no support for EHT before version 3 API */ -- cgit v1.2.3 From 5abf31544a4d2bcb3a0dd2fd080f9448d4026975 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:26 +0200 Subject: wifi: iwlwifi: mvm: add primary 80 known for EHT radiotap Calculate the position of the control channel in the wide channel based on the chandef, this is used to obtain the value of N in 802.11be D1.5 Table 9-53a in the column PHY MU/MRU index. To avoid the need to calculate every frame the value, do it once monitor vif is added. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.fe9a5b58e241.I291ee480252d098f62d9ec39040284d3e521d88e@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 27 ++++++++++++++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 +++++ drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 9 ++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 565522466eba..51c7652df0ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1362,6 +1362,28 @@ static void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk) ieee80211_chswitch_done(vif, false); } +static u8 +iwl_mvm_chandef_get_primary_80(struct cfg80211_chan_def *chandef) +{ + int data_start; + int control_start; + int bw; + + if (chandef->width == NL80211_CHAN_WIDTH_320) + bw = 320; + else if (chandef->width == NL80211_CHAN_WIDTH_160) + bw = 160; + else + return 0; + + /* data is bw wide so the start is half the width */ + data_start = chandef->center_freq1 - bw / 2; + /* control is 20Mhz width */ + control_start = chandef->chan->center_freq - 10; + + return (control_start - data_start) / 80; +} + static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -1478,8 +1500,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, INIT_DELAYED_WORK(&mvmvif->csa_work, iwl_mvm_channel_switch_disconnect_wk); - if (vif->type == NL80211_IFTYPE_MONITOR) + if (vif->type == NL80211_IFTYPE_MONITOR) { mvm->monitor_on = true; + mvm->monitor_p80 = + iwl_mvm_chandef_get_primary_80(&vif->bss_conf.chandef); + } iwl_mvm_vif_dbgfs_register(mvm, vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ce6b701f3f4c..301f39d9efe7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1096,6 +1096,11 @@ struct iwl_mvm { /* does a monitor vif exist (only one can exist hence bool) */ bool monitor_on; + /* + * primary channel position relative to he whole bandwidth, + * in steps of 80 MHz + */ + u8 monitor_p80; /* sniffer data to include in radiotap */ __le16 cur_aid; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 1334c96098a5..b545673fb150 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1695,6 +1695,15 @@ static void iwl_mvm_decode_eht_phy_data(struct iwl_mvm *mvm, iwl_mvm_decode_eht_ru(mvm, rx_status, eht); + /* We only get here in case of IWL_RX_MPDU_PHY_TSF_OVERLOAD is set + * which is on only in case of monitor mode so no need to check monitor + * mode + */ + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80); + eht->data[1] |= + le32_encode_bits(mvm->monitor_p80, + IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80); + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN); if (phy_data->with_data) usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, -- cgit v1.2.3 From b85f7ebb24974e016520b5ea39394ab4cb08af0c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 5 Mar 2023 14:16:27 +0200 Subject: wifi: iwlwifi: mvm: avoid UB shift of snif_queue For the old TX API we need the tfd_queue_msk, but for the new TX API we don't need it here because we add it to the station later. However, for the new API mvm->snif_queue is set to IWL_MVM_INVALID_QUEUE == 0xffff, so the BIT() here is undefined behaviour. Since we don't need the tfd_queue_msk value for the new TX API at all, simply fill it in only for the old API. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.b8da0b7eb194.I53744fd7cfb6e146a9393272a2a61852841238d9@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index aa791dbc3066..114c96ba39ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -654,7 +654,7 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, u32 action) { struct iwl_mac_ctx_cmd cmd = {}; - u32 tfd_queue_msk = BIT(mvm->snif_queue); + u32 tfd_queue_msk = 0; int ret; WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); @@ -669,6 +669,14 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, MAC_FILTER_ACCEPT_GRP); ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); + /* + * the queue mask is only relevant for old TX API, and + * mvm->snif_queue isn't set here (it's still set to + * IWL_MVM_INVALID_QUEUE so the BIT() of it is UB) + */ + if (!iwl_mvm_has_new_tx_api(mvm)) + tfd_queue_msk = BIT(mvm->snif_queue); + /* Allocate sniffer station */ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->snif_sta, tfd_queue_msk, vif->type, IWL_STA_GENERAL_PURPOSE); -- cgit v1.2.3 From f7bd883b3fca50f8429a67b4bfdf82a22e8dac53 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 5 Mar 2023 14:16:28 +0200 Subject: wifi: iwlwifi: mvm: make flush code a bit clearer The mask building here is only relevant for the old TX API, so move it into the else branch. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.c0795543f254.I302124a8584dd049577b0c2c74ecd7c48ddf4f3e@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 51c7652df0ef..ab02c6076276 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -5058,9 +5058,10 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, if (iwl_mvm_flush_sta(mvm, mvmsta, false)) IWL_ERR(mvm, "flush request fail\n"); } else { - msk |= mvmsta->tfd_queue_msk; if (iwl_mvm_has_new_tx_api(mvm)) iwl_mvm_wait_sta_queues_empty(mvm, mvmsta); + else /* only used for !iwl_mvm_has_new_tx_api() below */ + msk |= mvmsta->tfd_queue_msk; } } -- cgit v1.2.3 From b96e516ccf9f5a227756989d4e47297f65e12e60 Mon Sep 17 00:00:00 2001 From: Golan Ben Ami Date: Sun, 5 Mar 2023 14:16:29 +0200 Subject: wifi: iwlwifi: Add support for B step of BnJ-Fm4 Support new HW step of BnJ-Fm4 device Signed-off-by: Golan Ben Ami Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.bb0591c59898.If04d7a45707ba008981f8c8ea7f7f107880f146c@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/22000.c | 12 ++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 5 +++++ 3 files changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 3bdd6774716d..05720352e49f 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -62,6 +62,7 @@ #define IWL_BZ_Z_GF_A_FW_PRE "iwlwifi-bz-z0-gf-a0-" #define IWL_BNJ_A_FM_A_FW_PRE "iwlwifi-BzBnj-a0-fm-a0-" #define IWL_BNJ_A_FM4_A_FW_PRE "iwlwifi-BzBnj-a0-fm4-a0-" +#define IWL_BNJ_B_FM4_B_FW_PRE "iwlwifi-BzBnj-b0-fm4-b0-" #define IWL_BNJ_A_GF_A_FW_PRE "iwlwifi-BzBnj-a0-gf-a0-" #define IWL_BNJ_A_GF4_A_FW_PRE "iwlwifi-BzBnj-a0-gf4-a0-" #define IWL_BNJ_A_HR_B_FW_PRE "iwlwifi-BzBnj-a0-hr-b0-" @@ -132,6 +133,8 @@ IWL_BNJ_A_FM_A_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_FM4_A_MODULE_FIRMWARE(api) \ IWL_BNJ_A_FM4_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_B_FM4_B_MODULE_FIRMWARE(api) \ + IWL_BNJ_B_FM4_B_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_GF_A_MODULE_FIRMWARE(api) \ IWL_BNJ_A_GF_A_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_GF4_A_MODULE_FIRMWARE(api) \ @@ -998,6 +1001,14 @@ const struct iwl_cfg iwl_cfg_bnj_a0_fm4_a0 = { .num_rbds = IWL_NUM_RBDS_AX210_HE, }; +const struct iwl_cfg iwl_cfg_bnj_b0_fm4_b0 = { + .fw_name_pre = IWL_BNJ_B_FM4_B_FW_PRE, + .uhb_supported = true, + IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0 = { .fw_name_pre = IWL_BNJ_A_GF_A_FW_PRE, .uhb_supported = true, @@ -1059,6 +1070,7 @@ MODULE_FIRMWARE(IWL_BZ_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_GL_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_FM4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BNJ_B_FM4_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_GF_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_GF4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index cfa5e1b3c3f6..eaa0ff2736c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -659,6 +659,7 @@ extern const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0; extern const struct iwl_cfg iwl_cfg_bnj_a0_gf4_a0; extern const struct iwl_cfg iwl_cfg_bnj_a0_hr_b0; extern const struct iwl_cfg iwl_cfg_bnj_b0_fm_b0; +extern const struct iwl_cfg iwl_cfg_bnj_b0_fm4_b0; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 99768d6a6032..8aa8a678475c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1193,6 +1193,11 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, iwl_cfg_bnj_a0_fm4_a0, iwl_bz_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, + iwl_cfg_bnj_b0_fm4_b0, iwl_bz_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, -- cgit v1.2.3 From beddcdc489864e234af844d906547b1cb70e9036 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:30 +0200 Subject: wifi: iwlwifi: rs-fw: break out for unsupported bandwidth Currently the for loop runs also over unsupported bandwidth in the command, shorten the path in case we don't support it. Also use the right macro for setting BW20. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.0264ba9df63b.I6c7c9efc806e0ffb7cb3b6051b2d109646e8708c@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index f30eeab5505b..e3fb1b2cea6d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -337,10 +337,14 @@ static void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta, const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx = rs_fw_rs_mcs2eht_mcs(bw, eht_tx_mcs); - /* got unsuppored index for bw */ + /* got unsupported index for bw */ if (!mcs_rx || !mcs_tx) continue; + /* break out if we don't support the bandwidth */ + if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ)) + break; + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, MAX_NSS_MCS(9, mcs_rx, mcs_tx), GENMASK(9, 0)); rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, @@ -550,7 +554,7 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_tlc_config_cmd_v4 cfg_cmd = { .sta_id = mvmsta->sta_id, .max_ch_width = update ? - rs_fw_bw_from_sta_bw(sta) : RATE_MCS_CHAN_WIDTH_20, + rs_fw_bw_from_sta_bw(sta) : IWL_TLC_MNG_CH_WIDTH_20MHZ, .flags = cpu_to_le16(rs_fw_get_config_flags(mvm, sta, sband)), .chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)), .sgi_ch_width_supp = rs_fw_sgi_cw_support(sta), -- cgit v1.2.3 From 774302d2d3ad916f415d651362550daf22f630b7 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:31 +0200 Subject: wifi: iwlwifi: mvm: clean up duplicated defines VHT, HE and EHT rates use the same bits for NSS, so no need for defines per PHY version. Also use spatch to replace bit manipulation with FIELD_GET: @@ identifier rate; @@ -((rate & RATE_MCS_NSS_MSK) >> RATE_MCS_NSS_POS) +FIELD_GET(RATE_MCS_NSS_MSK, rate) Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.167ed9477aa8.Ibd8e71d31896e8d8f067ce4e3a6e9a0e86c78f3f@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/rs.h | 3 --- drivers/net/wireless/intel/iwlwifi/fw/rs.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 17 ++++++----------- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 6 ++---- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 7 +++---- 5 files changed, 13 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 1d372a09ebb4..c9a48fc5fac8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -373,9 +373,6 @@ enum { /* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ #define RATE_VHT_MCS_RATE_CODE_MSK 0xf -#define RATE_VHT_MCS_NSS_POS 4 -#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS) -#define RATE_VHT_MCS_MIMO2_MSK BIT(RATE_VHT_MCS_NSS_POS) /* * Legacy OFDM rate format for bits 7:0 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c index e128d2e07f38..b09e68dbf5a9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2021 Intel Corporation + * Copyright (C) 2021-2022 Intel Corporation */ #include @@ -126,7 +126,7 @@ u32 iwl_new_rate_from_v1(u32 rate_v1) rate_v1 & RATE_MCS_HE_MSK_V1) { rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK; - rate_v2 |= rate_v1 & RATE_VHT_MCS_MIMO2_MSK; + rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK; if (rate_v1 & RATE_MCS_HE_MSK_V1) { u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 0b50b816684a..1f81dff71bc4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /****************************************************************************** * - * Copyright(c) 2005 - 2014, 2018 - 2021 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014, 2018 - 2022 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH *****************************************************************************/ @@ -895,8 +895,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, WARN_ON_ONCE(1); } } else if (ucode_rate & RATE_MCS_VHT_MSK_V1) { - nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; + nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1; if (nss == 1) { rate->type = LQ_VHT_SISO; @@ -910,8 +909,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, WARN_ON_ONCE(1); } } else if (ucode_rate & RATE_MCS_HE_MSK_V1) { - nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; + nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1; if (nss == 1) { rate->type = LQ_HE_SISO; @@ -2885,8 +2883,7 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) nss = ((rate & RATE_HT_MCS_NSS_MSK_V1) >> RATE_HT_MCS_NSS_POS_V1) + 1; } else if (rate & RATE_MCS_VHT_MSK_V1) { mvm->drv_rx_stats.vht_frames++; - nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; + nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1; } else { mvm->drv_rx_stats.legacy_frames++; } @@ -3665,8 +3662,7 @@ int rs_pretty_print_rate_v1(char *buf, int bufsz, const u32 rate) if (rate & RATE_MCS_VHT_MSK_V1) { type = "VHT"; mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_VHT_MCS_NSS_MSK) - >> RATE_VHT_MCS_NSS_POS) + 1; + nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1; } else if (rate & RATE_MCS_HT_MSK_V1) { type = "HT"; mcs = rate & RATE_HT_MCS_INDEX_MSK_V1; @@ -3675,8 +3671,7 @@ int rs_pretty_print_rate_v1(char *buf, int bufsz, const u32 rate) } else if (rate & RATE_MCS_HE_MSK_V1) { type = "HE"; mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_VHT_MCS_NSS_MSK) - >> RATE_VHT_MCS_NSS_POS) + 1; + nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1; } else { type = "Unknown"; /* shouldn't happen */ } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index ab9a51375c8a..d2ce414879aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -253,8 +253,7 @@ static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm, ARRAY_SIZE(thresh_tpt))) return; thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK]; - thr *= 1 + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS); + thr *= 1 + FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags); } thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK_V1) >> @@ -500,8 +499,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->nss = - ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; + FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1; rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; rx_status->encoding = RX_ENC_VHT; rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 9813d7fa1800..a6d69885cd3f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1396,8 +1396,8 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, r->idx = rate; } else if (format == RATE_MCS_VHT_MSK) { ieee80211_rate_set_vht(r, rate, - ((rate_n_flags & RATE_MCS_NSS_MSK) >> - RATE_MCS_NSS_POS) + 1); + FIELD_GET(RATE_MCS_NSS_MSK, + rate_n_flags) + 1); r->flags |= IEEE80211_TX_RC_VHT_MCS; } else if (format == RATE_MCS_HE_MSK) { /* mac80211 cannot do this without ieee80211_tx_status_ext() @@ -1428,8 +1428,7 @@ void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags, } else if (rate_n_flags & RATE_MCS_VHT_MSK_V1) { ieee80211_rate_set_vht( r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK, - ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1); + FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1); r->flags |= IEEE80211_TX_RC_VHT_MCS; } else { r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, -- cgit v1.2.3 From 558f874ea049af3c0e2c25b4de2b7fcf3f40e43b Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Sun, 5 Mar 2023 14:16:32 +0200 Subject: wifi: iwlwifi: Update logs for yoyo reset sw changes Update the log category for the reset-fw changes. Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.852a6b5f95fa.Ie67bd28da65c7e42424cacb37495930475de2dad@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 8e0bc1f5f6c6..87366b70b17f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1218,11 +1218,12 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync, } fwrt->trans->dbg.restart_required = FALSE; - IWL_DEBUG_INFO(fwrt, "WRT: tp %d, reset_fw %d\n", - tp, dump_data.trig->reset_fw); - IWL_DEBUG_INFO(fwrt, "WRT: restart_required %d, last_tp_resetfw %d\n", - fwrt->trans->dbg.restart_required, - fwrt->trans->dbg.last_tp_resetfw); + IWL_DEBUG_FW(fwrt, "WRT: tp %d, reset_fw %d\n", + tp, dump_data.trig->reset_fw); + IWL_DEBUG_FW(fwrt, + "WRT: restart_required %d, last_tp_resetfw %d\n", + fwrt->trans->dbg.restart_required, + fwrt->trans->dbg.last_tp_resetfw); if (fwrt->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000) { @@ -1235,18 +1236,19 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync, IWL_DEBUG_FW(fwrt, "WRT: FW_ASSERT due to reset_fw_mode-no restart\n"); } else if (le32_to_cpu(dump_data.trig->reset_fw) == IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) { - IWL_DEBUG_INFO(fwrt, "WRT: stop and reload firmware\n"); + IWL_DEBUG_FW(fwrt, "WRT: stop and reload firmware\n"); fwrt->trans->dbg.restart_required = TRUE; } else if (le32_to_cpu(dump_data.trig->reset_fw) == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) { - IWL_DEBUG_INFO(fwrt, "WRT: stop only and no reload firmware\n"); + IWL_DEBUG_FW(fwrt, + "WRT: stop only and no reload firmware\n"); fwrt->trans->dbg.restart_required = FALSE; fwrt->trans->dbg.last_tp_resetfw = le32_to_cpu(dump_data.trig->reset_fw); } else if (le32_to_cpu(dump_data.trig->reset_fw) == IWL_FW_INI_RESET_FW_MODE_NOTHING) { - IWL_DEBUG_INFO(fwrt, - "WRT: nothing need to be done after debug collection\n"); + IWL_DEBUG_FW(fwrt, + "WRT: nothing need to be done after debug collection\n"); } else { IWL_ERR(fwrt, "WRT: wrong resetfw %d\n", le32_to_cpu(dump_data.trig->reset_fw)); -- cgit v1.2.3 From 7696c07bfbded95ce22aa41b6411534d0d06990d Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Sun, 5 Mar 2023 14:16:33 +0200 Subject: wifi: iwlwifi: mvm: add EHT RU allocation to radiotap FW new API added the info missing for update RU allocation, so use the new API to update radiotap information. Signed-off-by: Mordechay Goodstein Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.b16acaa4bad1.I53afa03058dbd2cd8afbaf5e82596c8ed501a476@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/api/rx.h | 15 ++--- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 90 +++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index 97e946e70279..fdd8b01f09e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -414,7 +414,7 @@ enum iwl_rx_phy_eht_data2 { /* OFDM_RX_VECTOR_COMMON_RU_ALLOC_0_OUT */ IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_A1 = 0x000001ff, IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_A2 = 0x0003fe00, - IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_A3 = 0x01fc0000, + IWL_RX_PHY_DATA2_EHT_MU_EXT_RU_ALLOC_B1 = 0x07fc0000, /* info type: EHT-TB-EXT */ IWL_RX_PHY_DATA2_EHT_TB_EXT_TRIG_SIGA1 = 0xffffffff, @@ -424,19 +424,18 @@ enum iwl_rx_phy_eht_data2 { enum iwl_rx_phy_eht_data3 { /* info type: EHT-MU-EXT */ /* OFDM_RX_VECTOR_COMMON_RU_ALLOC_1_OUT */ - IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B1 = 0x000001ff, - IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B2 = 0x0003fe00, - IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B3 = 0x01fc0000, + IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_B2 = 0x000001ff, + IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_C1 = 0x0003fe00, + IWL_RX_PHY_DATA3_EHT_MU_EXT_RU_ALLOC_C2 = 0x07fc0000, }; /* goes into Metadata DW 4 */ enum iwl_rx_phy_eht_data4 { /* info type: EHT-MU-EXT */ /* OFDM_RX_VECTOR_COMMON_RU_ALLOC_2_OUT */ - IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_C1 = 0x000001ff, - IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_C2 = 0x0003fe00, - IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_C3 = 0x01fc0000, - IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS = 0x18000000, + IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_D1 = 0x000001ff, + IWL_RX_PHY_DATA4_EHT_MU_EXT_RU_ALLOC_D2 = 0x0003fe00, + IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS = 0x000c0000, }; /* goes into Metadata DW 16 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index b545673fb150..c07ed33395b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1468,17 +1468,54 @@ static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm, (_usig)->value |= LE32_DEC_ENC(in_value, dec_bits, _enc_bits); \ } while (0) +#define __IWL_MVM_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \ + eht->data[(rt_data)] |= \ + (cpu_to_le32 \ + (IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru ## _KNOWN) | \ + LE32_DEC_ENC(data ## fw_data, \ + IWL_RX_PHY_DATA ## fw_data ## _EHT_MU_EXT_RU_ALLOC_ ## fw_ru, \ + IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru)) + +#define _IWL_MVM_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \ + __IWL_MVM_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) + +#define IEEE80211_RADIOTAP_RU_DATA_1_1_1 1 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_1 2 +#define IEEE80211_RADIOTAP_RU_DATA_1_1_2 2 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_2 2 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_1 3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_1 3 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_2 3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_2 4 + +#define IWL_RX_RU_DATA_A1 2 +#define IWL_RX_RU_DATA_A2 2 +#define IWL_RX_RU_DATA_B1 2 +#define IWL_RX_RU_DATA_B2 3 +#define IWL_RX_RU_DATA_C1 3 +#define IWL_RX_RU_DATA_C2 3 +#define IWL_RX_RU_DATA_D1 4 +#define IWL_RX_RU_DATA_D2 4 + +#define IWL_MVM_ENC_EHT_RU(rt_ru, fw_ru) \ + _IWL_MVM_ENC_EHT_RU(IEEE80211_RADIOTAP_RU_DATA_ ## rt_ru, \ + rt_ru, \ + IWL_RX_RU_DATA_ ## fw_ru, \ + fw_ru) + static void iwl_mvm_decode_eht_ext_mu(struct iwl_mvm *mvm, struct iwl_mvm_rx_phy_data *phy_data, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_eht *eht, struct ieee80211_radiotap_eht_usig *usig) { - __le32 data1 = phy_data->d1; - if (phy_data->with_data) { + __le32 data1 = phy_data->d1; + __le32 data2 = phy_data->d2; + __le32 data3 = phy_data->d3; __le32 data4 = phy_data->eht_d4; __le32 data5 = phy_data->d5; + u32 phy_bw = phy_data->rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; IWL_MVM_ENC_USIG_VALUE_MASK(usig, data5, IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, @@ -1493,6 +1530,55 @@ static void iwl_mvm_decode_eht_ext_mu(struct iwl_mvm *mvm, (usig, data1, IWL_RX_PHY_DATA1_EHT_MU_NUM_SIG_SYM_USIGA2, IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) | + LE32_DEC_ENC(data5, IWL_RX_PHY_DATA5_EHT_MU_STA_ID_USR, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M); + eht->data[7] |= LE32_DEC_ENC + (data5, IWL_RX_PHY_DATA5_EHT_MU_NUM_USR_NON_OFDMA, + IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS); + + /* + * Hardware labels the content channels/RU allocation values + * as follows: + * Content Channel 1 Content Channel 2 + * 20 MHz: A1 + * 40 MHz: A1 B1 + * 80 MHz: A1 C1 B1 D1 + * 160 MHz: A1 C1 A2 C2 B1 D1 B2 D2 + * 320 MHz: A1 C1 A2 C2 A3 C3 A4 C4 B1 D1 B2 D2 B3 D3 B4 D4 + * + * However firmware can only give us A1-D2, so the higher + * frequencies are missing. + */ + + switch (phy_bw) { + case RATE_MCS_CHAN_WIDTH_320: + /* additional values are missing in RX metadata */ + case RATE_MCS_CHAN_WIDTH_160: + /* content channel 1 */ + IWL_MVM_ENC_EHT_RU(1_2_1, A2); + IWL_MVM_ENC_EHT_RU(1_2_2, C2); + /* content channel 2 */ + IWL_MVM_ENC_EHT_RU(2_2_1, B2); + IWL_MVM_ENC_EHT_RU(2_2_2, D2); + fallthrough; + case RATE_MCS_CHAN_WIDTH_80: + /* content channel 1 */ + IWL_MVM_ENC_EHT_RU(1_1_2, C1); + /* content channel 2 */ + IWL_MVM_ENC_EHT_RU(2_1_2, D1); + fallthrough; + case RATE_MCS_CHAN_WIDTH_40: + /* content channel 2 */ + IWL_MVM_ENC_EHT_RU(2_1_1, B1); + fallthrough; + case RATE_MCS_CHAN_WIDTH_20: + IWL_MVM_ENC_EHT_RU(1_1_1, A1); + break; + } } else { __le32 usig_a1 = phy_data->rx_vec[0]; __le32 usig_a2 = phy_data->rx_vec[1]; -- cgit v1.2.3 From b55c1f4ec535f87a4063a8cbb75b014cdfc41bcb Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 5 Mar 2023 14:16:34 +0200 Subject: wifi: iwlwifi: Do not include radiotap EHT user info if not needed Do not include user information in radtiotap EHT data for EHT sounding NDP as the frame doesn't include the user specific field. Instead, encode the NSS and the beamforming information in the EHT data. Signed-off-by: Ilan Peer Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.ac6474ded9bd.I9655589e9afbacc16820f35f6f5d90c6a91b8b05@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 58 +++++++++++++++++---------- 1 file changed, 37 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index c07ed33395b6..2ea1fbf81d3b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1845,6 +1845,7 @@ static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_radiotap_eht *eht; struct ieee80211_radiotap_eht_usig *usig; + size_t eht_len = sizeof(*eht); u32 rate_n_flags = phy_data->rate_n_flags; u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; @@ -1854,8 +1855,10 @@ static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, u32 bw; /* u32 for 1 user_info */ - eht = iwl_mvm_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, - sizeof(*eht) + sizeof(u32)); + if (phy_data->with_data) + eht_len += sizeof(u32); + + eht = iwl_mvm_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len); usig = iwl_mvm_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG, sizeof(*usig)); @@ -1946,27 +1949,40 @@ static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, rx_status->eht.gi)); } - eht->user_info[0] |= cpu_to_le32 - (IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | - IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | - IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | - IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | - IEEE80211_RADIOTAP_EHT_USER_INFO_DATA_FOR_USER); - - if (rate_n_flags & RATE_MCS_BF_MSK) - eht->user_info[0] |= - cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); - if (rate_n_flags & RATE_MCS_LDPC_MSK) + if (!phy_data->with_data) { + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S | + IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S); + eht->data[7] |= + le32_encode_bits(le32_get_bits(phy_data->rx_vec[2], + RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK), + IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); + if (rate_n_flags & RATE_MCS_BF_MSK) + eht->data[7] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S); + } else { eht->user_info[0] |= - cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING); - - eht->user_info[0] |= cpu_to_le32 - (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS, - FIELD_GET(RATE_VHT_MCS_RATE_CODE_MSK, - rate_n_flags)) | - FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O, - FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags))); + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_DATA_FOR_USER); + + if (rate_n_flags & RATE_MCS_BF_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + + if (rate_n_flags & RATE_MCS_LDPC_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING); + + eht->user_info[0] |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS, + FIELD_GET(RATE_VHT_MCS_RATE_CODE_MSK, + rate_n_flags)) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O, + FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags))); + } } static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb, -- cgit v1.2.3 From da1185449c669076276027c600666286124eef9f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 5 Mar 2023 14:16:35 +0200 Subject: wifi: iwlwifi: mvm: fix EOF bit reporting In monitor mode, we try to report the EOF bit on the first MPDU of an A-MPDU (hardware duplicates this bit over all MPDUs, so it's only trustable on the first). However, due to reshuffling in an ealier commit, the toggle_bit != mvm->ampdu_toggle logic can no longer work since mvm->ampdu_toggle is now set before this code runs. Fix this by tracking the first_subframe status in the phy data struct and using that instead of checking. Fixes: f1490546bec9 ("wifi: iwlwifi: mvm: rxmq: refactor mac80211 rx_status setting") Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230305124407.e273aa0d3fdc.I77db4cc247898eae8a98b80659386d6737052b95@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 28 ++++++++++----------------- 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 2ea1fbf81d3b..91556d43735a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1180,6 +1180,7 @@ struct iwl_mvm_rx_phy_data { __le32 d0, d1, d2, d3, eht_d4, d5; __le16 d4; bool with_data; + bool first_subframe; __le32 rx_vec[4]; u32 rate_n_flags; @@ -1885,15 +1886,10 @@ static void iwl_mvm_rx_eht(struct iwl_mvm *mvm, struct sk_buff *skb, /* update aggregation data for monitor sake on default queue */ if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && - (phy_info & IWL_RX_MPDU_PHY_AMPDU)) { - bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; - - /* toggle is switched whenever new aggregation starts */ - if (toggle_bit != mvm->ampdu_toggle) { - rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; - if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) - rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; - } + (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) { + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; } if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) @@ -2036,15 +2032,10 @@ static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb, /* update aggregation data for monitor sake on default queue */ if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && - (phy_info & IWL_RX_MPDU_PHY_AMPDU)) { - bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; - - /* toggle is switched whenever new aggregation starts */ - if (toggle_bit != mvm->ampdu_toggle) { - rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; - if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF)) - rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; - } + (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) { + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->d0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; } if (he_type == RATE_MCS_HE_TYPE_EXT_SU && @@ -2447,6 +2438,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (mvm->ampdu_ref == 0) mvm->ampdu_ref++; mvm->ampdu_toggle = toggle_bit; + phy_data.first_subframe = true; } rx_status->ampdu_reference = mvm->ampdu_ref; } -- cgit v1.2.3 From ae4fe46983007bc46d87dcb284a5e5851c3e1c84 Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Thu, 16 Feb 2023 10:07:31 +0800 Subject: igc: Add qbv_config_change_errors counter Add ConfigChangeError(qbv_config_change_errors) when user try to set the AdminBaseTime to past value while the current GCL is still running. The ConfigChangeError counter should not be increased when a gate control list is scheduled into the future. User can use "ethtool -S | grep qbv_config_change_errors" command to check the counter values. Signed-off-by: Muhammad Husaini Zulkifli Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igc/igc.h | 1 + drivers/net/ethernet/intel/igc/igc_ethtool.c | 1 + drivers/net/ethernet/intel/igc/igc_main.c | 1 + drivers/net/ethernet/intel/igc/igc_tsn.c | 12 ++++++++++++ 4 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index df3e26c0cf01..79cc99af4317 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -185,6 +185,7 @@ struct igc_adapter { ktime_t base_time; ktime_t cycle_time; bool qbv_enable; + u32 qbv_config_change_errors; /* OS defined structs */ struct pci_dev *pdev; diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 5a26a7805ef8..0e2cb00622d1 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -67,6 +67,7 @@ static const struct igc_stats igc_gstrings_stats[] = { IGC_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared), IGC_STAT("tx_lpi_counter", stats.tlpic), IGC_STAT("rx_lpi_counter", stats.rlpic), + IGC_STAT("qbv_config_change_errors", qbv_config_change_errors), }; #define IGC_NETDEV_STAT(_net_stat) { \ diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 2928a6c73692..4992cca4029d 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -6049,6 +6049,7 @@ static int igc_tsn_clear_schedule(struct igc_adapter *adapter) adapter->base_time = 0; adapter->cycle_time = NSEC_PER_SEC; + adapter->qbv_config_change_errors = 0; for (i = 0; i < adapter->num_tx_queues; i++) { struct igc_ring *ring = adapter->tx_ring[i]; diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index a386c8d61dbf..94a2b0dfb54d 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -114,6 +114,7 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter) static int igc_tsn_enable_offload(struct igc_adapter *adapter) { struct igc_hw *hw = &adapter->hw; + bool tsn_mode_reconfig = false; u32 tqavctrl, baset_l, baset_h; u32 sec, nsec, cycle; ktime_t base_time, systim; @@ -226,6 +227,10 @@ skip_cbs: } tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS; + + if (tqavctrl & IGC_TQAVCTRL_TRANSMIT_MODE_TSN) + tsn_mode_reconfig = true; + tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV; cycle = adapter->cycle_time; @@ -239,6 +244,13 @@ skip_cbs: s64 n = div64_s64(ktime_sub_ns(systim, base_time), cycle); base_time = ktime_add_ns(base_time, (n + 1) * cycle); + + /* Increase the counter if scheduling into the past while + * Gate Control List (GCL) is running. + */ + if ((rd32(IGC_BASET_H) || rd32(IGC_BASET_L)) && + tsn_mode_reconfig) + adapter->qbv_config_change_errors++; } else { /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit * has to be configured before the cycle time and base time. -- cgit v1.2.3 From 92a0dcb8427dc52eb0ec806206e2bdb23cc13840 Mon Sep 17 00:00:00 2001 From: Tan Tee Min Date: Thu, 16 Feb 2023 09:16:24 +0800 Subject: igc: offload queue max SDU from tc-taprio Add support for configuring the max SDU for each Tx queue. If not specified, keep the default. Signed-off-by: Tan Tee Min Signed-off-by: Muhammad Husaini Zulkifli Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igc/igc.h | 1 + drivers/net/ethernet/intel/igc/igc_hw.h | 1 + drivers/net/ethernet/intel/igc/igc_main.c | 33 +++++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 79cc99af4317..c0c00b8bd8d8 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -99,6 +99,7 @@ struct igc_ring { u32 start_time; u32 end_time; + u32 max_sdu; /* CBS parameters */ bool cbs_enable; /* indicates if CBS is enabled */ diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h index 88680e3d613d..e1c572e0d4ef 100644 --- a/drivers/net/ethernet/intel/igc/igc_hw.h +++ b/drivers/net/ethernet/intel/igc/igc_hw.h @@ -273,6 +273,7 @@ struct igc_hw_stats { u64 o2bspc; u64 b2ospc; u64 b2ogprc; + u64 txdrop; }; struct net_device *igc_get_hw_dev(struct igc_hw *hw); diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 4992cca4029d..1e1245085a36 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -1501,6 +1501,7 @@ static int igc_tso(struct igc_ring *tx_ring, static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, struct igc_ring *tx_ring) { + struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); bool first_flag = false, insert_empty = false; u16 count = TXD_USE_COUNT(skb_headlen(skb)); __be16 protocol = vlan_get_protocol(skb); @@ -1563,9 +1564,19 @@ done: first->bytecount = skb->len; first->gso_segs = 1; - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { - struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); + if (tx_ring->max_sdu > 0) { + u32 max_sdu = 0; + + max_sdu = tx_ring->max_sdu + + (skb_vlan_tagged(first->skb) ? VLAN_HLEN : 0); + if (first->bytecount > max_sdu) { + adapter->stats.txdrop++; + goto out_drop; + } + } + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { /* FIXME: add support for retrieving timestamps from * the other timer registers before skipping the * timestamping request. @@ -4920,7 +4931,8 @@ void igc_update_stats(struct igc_adapter *adapter) net_stats->tx_window_errors = adapter->stats.latecol; net_stats->tx_carrier_errors = adapter->stats.tncrs; - /* Tx Dropped needs to be maintained elsewhere */ + /* Tx Dropped */ + net_stats->tx_dropped = adapter->stats.txdrop; /* Management Stats */ adapter->stats.mgptc += rd32(IGC_MGTPTC); @@ -6056,6 +6068,7 @@ static int igc_tsn_clear_schedule(struct igc_adapter *adapter) ring->start_time = 0; ring->end_time = NSEC_PER_SEC; + ring->max_sdu = 0; } return 0; @@ -6139,6 +6152,16 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, } } + for (i = 0; i < adapter->num_tx_queues; i++) { + struct igc_ring *ring = adapter->tx_ring[i]; + struct net_device *dev = adapter->netdev; + + if (qopt->max_sdu[i]) + ring->max_sdu = qopt->max_sdu[i] + dev->hard_header_len; + else + ring->max_sdu = 0; + } + return 0; } @@ -6237,8 +6260,10 @@ static int igc_tc_query_caps(struct igc_adapter *adapter, caps->broken_mqprio = true; - if (hw->mac.type == igc_i225) + if (hw->mac.type == igc_i225) { + caps->supports_queue_max_sdu = true; caps->gate_mask_per_txq = true; + } return 0; } -- cgit v1.2.3 From 6cc1b2fd736dbc4c846078459c3c00a0372247da Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Sun, 20 Nov 2022 10:15:11 +0200 Subject: igc: Clean up and optimize watchdog task i225/i226 parts used only one media type copper. The copper media type is not replaceable. Clean up the code accordingly, and remove the obsolete media replacement and reset options. Signed-off-by: Sasha Neftin Tested-by: Naama Meir Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igc/igc.h | 2 -- drivers/net/ethernet/intel/igc/igc_main.c | 17 ----------------- 2 files changed, 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index c0c00b8bd8d8..34aebf00a512 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -294,8 +294,6 @@ extern char igc_driver_name[]; #define IGC_FLAG_PTP BIT(8) #define IGC_FLAG_WOL_SUPPORTED BIT(8) #define IGC_FLAG_NEED_LINK_UPDATE BIT(9) -#define IGC_FLAG_MEDIA_RESET BIT(10) -#define IGC_FLAG_MAS_ENABLE BIT(12) #define IGC_FLAG_HAS_MSIX BIT(13) #define IGC_FLAG_EEE BIT(14) #define IGC_FLAG_VLAN_PROMISC BIT(15) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 1e1245085a36..2c40796c151f 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -5578,25 +5578,8 @@ no_wait: mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); - /* link is down, time to check for alternate media */ - if (adapter->flags & IGC_FLAG_MAS_ENABLE) { - if (adapter->flags & IGC_FLAG_MEDIA_RESET) { - schedule_work(&adapter->reset_task); - /* return immediately */ - return; - } - } pm_schedule_suspend(netdev->dev.parent, MSEC_PER_SEC * 5); - - /* also check for alternate media here */ - } else if (!netif_carrier_ok(netdev) && - (adapter->flags & IGC_FLAG_MAS_ENABLE)) { - if (adapter->flags & IGC_FLAG_MEDIA_RESET) { - schedule_work(&adapter->reset_task); - /* return immediately */ - return; - } } } -- cgit v1.2.3 From 4310e2f42030ffda0997fb87e2ebdd9711030218 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 6 Mar 2023 23:10:57 +0100 Subject: net: phy: smsc: simplify lan95xx_config_aneg_ext lan95xx_config_aneg_ext() can be simplified by using phy_set_bits(). Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/3da785c7-3ef8-b5d3-89a0-340f550be3c2@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/smsc.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index ac7481ce2fc1..af89f3ef1c4f 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -178,18 +178,15 @@ static int lan87xx_config_aneg(struct phy_device *phydev) static int lan95xx_config_aneg_ext(struct phy_device *phydev) { - int rc; - - if (phydev->phy_id != 0x0007c0f0) /* not (LAN9500A or LAN9505A) */ - return lan87xx_config_aneg(phydev); + if (phydev->phy_id == 0x0007c0f0) { /* LAN9500A or LAN9505A */ + /* Extend Manual AutoMDIX timer */ + int rc = phy_set_bits(phydev, PHY_EDPD_CONFIG, + PHY_EDPD_CONFIG_EXT_CROSSOVER_); - /* Extend Manual AutoMDIX timer */ - rc = phy_read(phydev, PHY_EDPD_CONFIG); - if (rc < 0) - return rc; + if (rc < 0) + return rc; + } - rc |= PHY_EDPD_CONFIG_EXT_CROSSOVER_; - phy_write(phydev, PHY_EDPD_CONFIG, rc); return lan87xx_config_aneg(phydev); } -- cgit v1.2.3 From 2549347972a8e94a9261c125e494955d873b1a9f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 6 Mar 2023 17:40:28 -0600 Subject: netxen_nic: Replace fake flex-array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zero-length arrays as fake flexible arrays are deprecated and we are moving towards adopting C99 flexible-array members instead. Transform zero-length array into flexible-array member in struct nx_cardrsp_rx_ctx_t. Address the following warnings found with GCC-13 and -fstrict-flex-arrays=3 enabled: drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c:361:26: warning: array subscript is outside array bounds of ‘char[0]’ [-Warray-bounds=] drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c:372:25: warning: array subscript is outside array bounds of ‘char[0]’ [-Warray-bounds=] This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy() and help us make progress towards globally enabling -fstrict-flex-arrays=3 [1]. Link: https://github.com/KSPP/linux/issues/21 Link: https://github.com/KSPP/linux/issues/265 Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/ZAZ57I6WdQEwWh7v@work Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/netxen/netxen_nic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index f13fa7396aef..3d36d23df0c6 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -854,7 +854,7 @@ typedef struct { The following is packed: - N cardrsp_rds_rings - N cardrs_sds_rings */ - char data[0]; + char data[]; } nx_cardrsp_rx_ctx_t; #define SIZEOF_HOSTRQ_RX(HOSTRQ_RX, rds_rings, sds_rings) \ -- cgit v1.2.3 From 91c8643578a21e435c412ffbe902bb4b4773e262 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 6 Mar 2023 22:23:15 +0100 Subject: r8169: use spinlock to protect mac ocp register access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For disabling ASPM during NAPI poll we'll have to access mac ocp registers in atomic context. This could result in races because a mac ocp read consists of a write to register OCPDR, followed by a read from the same register. Therefore add a spinlock to protect access to mac ocp registers. Reviewed-by: Simon Horman Tested-by: Kai-Heng Feng Tested-by: Holger Hoffstätte Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 37 +++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 45147a1016be..259eac5b0616 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -613,6 +613,8 @@ struct rtl8169_private { struct work_struct work; } wk; + spinlock_t mac_ocp_lock; + unsigned supports_gmii:1; unsigned aspm_manageable:1; dma_addr_t counters_phys_addr; @@ -847,7 +849,7 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg) (RTL_R32(tp, GPHY_OCP) & 0xffff) : -ETIMEDOUT; } -static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) +static void __r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) { if (rtl_ocp_reg_failure(reg)) return; @@ -855,7 +857,16 @@ static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data); } -static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) +static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->mac_ocp_lock, flags); + __r8168_mac_ocp_write(tp, reg, data); + spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); +} + +static u16 __r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) { if (rtl_ocp_reg_failure(reg)) return 0; @@ -865,12 +876,28 @@ static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) return RTL_R32(tp, OCPDR); } +static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) +{ + unsigned long flags; + u16 val; + + spin_lock_irqsave(&tp->mac_ocp_lock, flags); + val = __r8168_mac_ocp_read(tp, reg); + spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); + + return val; +} + static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask, u16 set) { - u16 data = r8168_mac_ocp_read(tp, reg); + unsigned long flags; + u16 data; - r8168_mac_ocp_write(tp, reg, (data & ~mask) | set); + spin_lock_irqsave(&tp->mac_ocp_lock, flags); + data = __r8168_mac_ocp_read(tp, reg); + __r8168_mac_ocp_write(tp, reg, (data & ~mask) | set); + spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); } /* Work around a hw issue with RTL8168g PHY, the quirk disables @@ -5176,6 +5203,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->eee_adv = -1; tp->ocp_base = OCP_STD_PHY_BASE; + spin_lock_init(&tp->mac_ocp_lock); + dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev, struct pcpu_sw_netstats); if (!dev->tstats) -- cgit v1.2.3 From 6bc6c4e6893ee79a9862c61d1635e7da6d5a3333 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 6 Mar 2023 22:24:00 +0100 Subject: r8169: use spinlock to protect access to registers Config2 and Config5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For disabling ASPM during NAPI poll we'll have to access both registers in atomic context. Use a spinlock to protect access. Reviewed-by: Simon Horman Tested-by: Kai-Heng Feng Tested-by: Holger Hoffstätte Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 47 ++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 259eac5b0616..e6f3f1947488 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -613,6 +613,7 @@ struct rtl8169_private { struct work_struct work; } wk; + spinlock_t config25_lock; spinlock_t mac_ocp_lock; unsigned supports_gmii:1; @@ -677,6 +678,28 @@ static void rtl_pci_commit(struct rtl8169_private *tp) RTL_R8(tp, ChipCmd); } +static void rtl_mod_config2(struct rtl8169_private *tp, u8 clear, u8 set) +{ + unsigned long flags; + u8 val; + + spin_lock_irqsave(&tp->config25_lock, flags); + val = RTL_R8(tp, Config2); + RTL_W8(tp, Config2, (val & ~clear) | set); + spin_unlock_irqrestore(&tp->config25_lock, flags); +} + +static void rtl_mod_config5(struct rtl8169_private *tp, u8 clear, u8 set) +{ + unsigned long flags; + u8 val; + + spin_lock_irqsave(&tp->config25_lock, flags); + val = RTL_R8(tp, Config5); + RTL_W8(tp, Config5, (val & ~clear) | set); + spin_unlock_irqrestore(&tp->config25_lock, flags); +} + static bool rtl_is_8125(struct rtl8169_private *tp) { return tp->mac_version >= RTL_GIGA_MAC_VER_61; @@ -1363,6 +1386,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) { WAKE_MAGIC, Config3, MagicPacket } }; unsigned int i, tmp = ARRAY_SIZE(cfg); + unsigned long flags; u8 options; rtl_unlock_config_regs(tp); @@ -1381,12 +1405,14 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0); } + spin_lock_irqsave(&tp->config25_lock, flags); for (i = 0; i < tmp; i++) { options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask; if (wolopts & cfg[i].opt) options |= cfg[i].mask; RTL_W8(tp, cfg[i].reg, options); } + spin_unlock_irqrestore(&tp->config25_lock, flags); switch (tp->mac_version) { case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: @@ -1398,10 +1424,10 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) case RTL_GIGA_MAC_VER_34: case RTL_GIGA_MAC_VER_37: case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63: - options = RTL_R8(tp, Config2) & ~PME_SIGNAL; if (wolopts) - options |= PME_SIGNAL; - RTL_W8(tp, Config2, options); + rtl_mod_config2(tp, 0, PME_SIGNAL); + else + rtl_mod_config2(tp, PME_SIGNAL, 0); break; default: break; @@ -2704,8 +2730,8 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) { /* Don't enable ASPM in the chip if OS can't control ASPM */ if (enable && tp->aspm_manageable) { - RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en); - RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn); + rtl_mod_config5(tp, 0, ASPM_en); + rtl_mod_config2(tp, 0, ClkReqEn); switch (tp->mac_version) { case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: @@ -2728,8 +2754,8 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) break; } - RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn); - RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en); + rtl_mod_config2(tp, ClkReqEn, 0); + rtl_mod_config5(tp, ASPM_en, 0); } udelay(10); @@ -2890,7 +2916,7 @@ static void rtl_hw_start_8168e_1(struct rtl8169_private *tp) RTL_W32(tp, MISC, RTL_R32(tp, MISC) | TXPLA_RST); RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~TXPLA_RST); - RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); + rtl_mod_config5(tp, Spi_en, 0); } static void rtl_hw_start_8168e_2(struct rtl8169_private *tp) @@ -2923,7 +2949,7 @@ static void rtl_hw_start_8168e_2(struct rtl8169_private *tp) RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); - RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); + rtl_mod_config5(tp, Spi_en, 0); rtl_hw_aspm_clkreq_enable(tp, true); } @@ -2946,7 +2972,7 @@ static void rtl_hw_start_8168f(struct rtl8169_private *tp) RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); - RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); + rtl_mod_config5(tp, Spi_en, 0); rtl8168_config_eee_mac(tp); } @@ -5203,6 +5229,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->eee_adv = -1; tp->ocp_base = OCP_STD_PHY_BASE; + spin_lock_init(&tp->config25_lock); spin_lock_init(&tp->mac_ocp_lock); dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev, -- cgit v1.2.3 From 59ee97c0c1a8d0dadc092897ca7d5fe3a80e1bc3 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 6 Mar 2023 22:24:49 +0100 Subject: r8169: enable cfg9346 config register access in atomic context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For disabling ASPM during NAPI poll we'll have to unlock access to the config registers in atomic context. Other code parts running with config register access unlocked are partially longer and can sleep. Add a usage counter to enable parallel execution of code parts requiring unlocked config registers. Reviewed-by: Simon Horman Tested-by: Kai-Heng Feng Tested-by: Holger Hoffstätte Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index e6f3f1947488..61cbf498fd07 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -616,6 +616,9 @@ struct rtl8169_private { spinlock_t config25_lock; spinlock_t mac_ocp_lock; + spinlock_t cfg9346_usage_lock; + int cfg9346_usage_count; + unsigned supports_gmii:1; unsigned aspm_manageable:1; dma_addr_t counters_phys_addr; @@ -664,12 +667,22 @@ static inline struct device *tp_to_dev(struct rtl8169_private *tp) static void rtl_lock_config_regs(struct rtl8169_private *tp) { - RTL_W8(tp, Cfg9346, Cfg9346_Lock); + unsigned long flags; + + spin_lock_irqsave(&tp->cfg9346_usage_lock, flags); + if (!--tp->cfg9346_usage_count) + RTL_W8(tp, Cfg9346, Cfg9346_Lock); + spin_unlock_irqrestore(&tp->cfg9346_usage_lock, flags); } static void rtl_unlock_config_regs(struct rtl8169_private *tp) { - RTL_W8(tp, Cfg9346, Cfg9346_Unlock); + unsigned long flags; + + spin_lock_irqsave(&tp->cfg9346_usage_lock, flags); + if (!tp->cfg9346_usage_count++) + RTL_W8(tp, Cfg9346, Cfg9346_Unlock); + spin_unlock_irqrestore(&tp->cfg9346_usage_lock, flags); } static void rtl_pci_commit(struct rtl8169_private *tp) @@ -5229,6 +5242,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->eee_adv = -1; tp->ocp_base = OCP_STD_PHY_BASE; + spin_lock_init(&tp->cfg9346_usage_lock); spin_lock_init(&tp->config25_lock); spin_lock_init(&tp->mac_ocp_lock); -- cgit v1.2.3 From 49ef7d846d4bd77b0b9f1f801fc765b004690a07 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 6 Mar 2023 22:25:49 +0100 Subject: r8169: prepare rtl_hw_aspm_clkreq_enable for usage in atomic context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bail out if the function is used with chip versions that don't support ASPM configuration. In addition remove the delay, it tuned out that it's not needed, also vendor driver r8125 doesn't have it. Suggested-by: Kai-Heng Feng Reviewed-by: Simon Horman Tested-by: Kai-Heng Feng Tested-by: Holger Hoffstätte Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 61cbf498fd07..96af31aeab0a 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -2741,6 +2741,9 @@ static void rtl_disable_exit_l1(struct rtl8169_private *tp) static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) { + if (tp->mac_version < RTL_GIGA_MAC_VER_32) + return; + /* Don't enable ASPM in the chip if OS can't control ASPM */ if (enable && tp->aspm_manageable) { rtl_mod_config5(tp, 0, ASPM_en); @@ -2770,8 +2773,6 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) rtl_mod_config2(tp, ClkReqEn, 0); rtl_mod_config5(tp, ASPM_en, 0); } - - udelay(10); } static void rtl_set_fifo_size(struct rtl8169_private *tp, u16 rx_stat, -- cgit v1.2.3 From e1ed3e4d91112027b90c7ee61479141b3f948e6a Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 6 Mar 2023 22:26:47 +0100 Subject: r8169: disable ASPM during NAPI poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several chip versions have problems with ASPM, what may result in rx_missed errors or tx timeouts. The root cause isn't known but experience shows that disabling ASPM during NAPI poll can avoid these problems. Suggested-by: Kai-Heng Feng Reviewed-by: Simon Horman Tested-by: Kai-Heng Feng Tested-by: Holger Hoffstätte Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 96af31aeab0a..2897b9bf29af 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4577,6 +4577,10 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) } if (napi_schedule_prep(&tp->napi)) { + rtl_unlock_config_regs(tp); + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_lock_config_regs(tp); + rtl_irq_disable(tp); __napi_schedule(&tp->napi); } @@ -4636,9 +4640,14 @@ static int rtl8169_poll(struct napi_struct *napi, int budget) work_done = rtl_rx(dev, tp, budget); - if (work_done < budget && napi_complete_done(napi, work_done)) + if (work_done < budget && napi_complete_done(napi, work_done)) { rtl_irq_enable(tp); + rtl_unlock_config_regs(tp); + rtl_hw_aspm_clkreq_enable(tp, true); + rtl_lock_config_regs(tp); + } + return work_done; } -- cgit v1.2.3 From 2ab19de62d67e403105ba860971e5ff0d511ad15 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Mon, 6 Mar 2023 22:28:06 +0100 Subject: r8169: remove ASPM restrictions now that ASPM is disabled during NAPI poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that ASPM is disabled during NAPI poll, we can remove all ASPM restrictions. This allows for higher power savings if the network isn't fully loaded. Reviewed-by: Simon Horman Tested-by: Kai-Heng Feng Tested-by: Holger Hoffstätte Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 2897b9bf29af..6563e4c6a136 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -620,7 +620,6 @@ struct rtl8169_private { int cfg9346_usage_count; unsigned supports_gmii:1; - unsigned aspm_manageable:1; dma_addr_t counters_phys_addr; struct rtl8169_counters *counters; struct rtl8169_tc_offsets tc_offset; @@ -2744,8 +2743,7 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) if (tp->mac_version < RTL_GIGA_MAC_VER_32) return; - /* Don't enable ASPM in the chip if OS can't control ASPM */ - if (enable && tp->aspm_manageable) { + if (enable) { rtl_mod_config5(tp, 0, ASPM_en); rtl_mod_config2(tp, 0, ClkReqEn); @@ -5221,16 +5219,6 @@ done: rtl_rar_set(tp, mac_addr); } -/* register is set if system vendor successfully tested ASPM 1.2 */ -static bool rtl_aspm_is_safe(struct rtl8169_private *tp) -{ - if (tp->mac_version >= RTL_GIGA_MAC_VER_61 && - r8168_mac_ocp_read(tp, 0xc0b2) & 0xf) - return true; - - return false; -} - static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct rtl8169_private *tp; @@ -5302,19 +5290,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->mac_version = chipset; - /* Disable ASPM L1 as that cause random device stop working - * problems as well as full system hangs for some PCIe devices users. - * Chips from RTL8168h partially have issues with L1.2, but seem - * to work fine with L1 and L1.1. - */ - if (rtl_aspm_is_safe(tp)) - rc = 0; - else if (tp->mac_version >= RTL_GIGA_MAC_VER_46) - rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1_2); - else - rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); - tp->aspm_manageable = !rc; - tp->dash_type = rtl_check_dash(tp); tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK; -- cgit v1.2.3 From 966b6b809f900b6c41f3b559fd57ec567d735dc9 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 6 Mar 2023 17:51:52 -0600 Subject: net/mlx4_en: Replace fake flex-array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zero-length arrays as fake flexible arrays are deprecated and we are moving towards adopting C99 flexible-array members instead. Transform zero-length array into flexible-array member in struct mlx4_en_rx_desc. Address the following warnings found with GCC-13 and -fstrict-flex-arrays=3 enabled: drivers/net/ethernet/mellanox/mlx4/en_rx.c:88:30: warning: array subscript i is outside array bounds of ‘struct mlx4_wqe_data_seg[0]’ [-Warray-bounds=] drivers/net/ethernet/mellanox/mlx4/en_rx.c:149:30: warning: array subscript 0 is outside array bounds of ‘struct mlx4_wqe_data_seg[0]’ [-Warray-bounds=] drivers/net/ethernet/mellanox/mlx4/en_rx.c:127:30: warning: array subscript i is outside array bounds of ‘struct mlx4_wqe_data_seg[0]’ [-Warray-bounds=] drivers/net/ethernet/mellanox/mlx4/en_rx.c:128:30: warning: array subscript i is outside array bounds of ‘struct mlx4_wqe_data_seg[0]’ [-Warray-bounds=] drivers/net/ethernet/mellanox/mlx4/en_rx.c:129:30: warning: array subscript i is outside array bounds of ‘struct mlx4_wqe_data_seg[0]’ [-Warray-bounds=] drivers/net/ethernet/mellanox/mlx4/en_rx.c:117:30: warning: array subscript i is outside array bounds of ‘struct mlx4_wqe_data_seg[0]’ [-Warray-bounds=] drivers/net/ethernet/mellanox/mlx4/en_rx.c:119:30: warning: array subscript i is outside array bounds of ‘struct mlx4_wqe_data_seg[0]’ [-Warray-bounds=] This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy() and help us make progress towards globally enabling -fstrict-flex-arrays=3 [1]. Link: https://github.com/KSPP/linux/issues/21 Link: https://github.com/KSPP/linux/issues/264 Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 544e09b97483..034733b13b1a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -323,7 +323,7 @@ struct mlx4_en_tx_ring { struct mlx4_en_rx_desc { /* actual number of entries depends on rx ring stride */ - struct mlx4_wqe_data_seg data[0]; + DECLARE_FLEX_ARRAY(struct mlx4_wqe_data_seg, data); }; struct mlx4_en_rx_ring { -- cgit v1.2.3 From ecf729f93bd5a2117ef82d1e281bfc780c643e36 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Tue, 7 Mar 2023 13:41:38 +0800 Subject: emulex/benet: clean up some inconsistent indenting No functional modification involved. drivers/net/ethernet/emulex/benet/be_cmds.c:1120 be_cmd_pmac_add() warn: inconsistent indenting. Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=4396 Signed-off-by: Jiapeng Chong Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 08ec84cd21c0..61adcebeef01 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -135,7 +135,8 @@ static int be_mcc_notify(struct be_adapter *adapter) /* To check if valid bit is set, check the entire word as we don't know * the endianness of the data (old entry is host endian while a new entry is - * little endian) */ + * little endian) + */ static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl) { u32 flags; @@ -248,7 +249,8 @@ static int be_mcc_compl_process(struct be_adapter *adapter, u8 opcode = 0, subsystem = 0; /* Just swap the status to host endian; mcc tag is opaquely copied - * from mcc_wrb */ + * from mcc_wrb + */ be_dws_le_to_cpu(compl, 4); base_status = base_status(compl->status); @@ -657,8 +659,7 @@ static int be_mbox_db_ready_wait(struct be_adapter *adapter, void __iomem *db) return 0; } -/* - * Insert the mailbox address into the doorbell in two steps +/* Insert the mailbox address into the doorbell in two steps * Polls on the mbox doorbell till a command completion (or a timeout) occurs */ static int be_mbox_notify_wait(struct be_adapter *adapter) @@ -802,7 +803,7 @@ static void be_wrb_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr, req_hdr->subsystem = subsystem; req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr)); req_hdr->version = 0; - fill_wrb_tags(wrb, (ulong) req_hdr); + fill_wrb_tags(wrb, (ulong)req_hdr); wrb->payload_length = cmd_len; if (mem) { wrb->embedded |= (1 & MCC_WRB_SGE_CNT_MASK) << @@ -832,8 +833,8 @@ static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages, static inline struct be_mcc_wrb *wrb_from_mbox(struct be_adapter *adapter) { struct be_dma_mem *mbox_mem = &adapter->mbox_mem; - struct be_mcc_wrb *wrb - = &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb; + struct be_mcc_wrb *wrb = &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb; + memset(wrb, 0, sizeof(*wrb)); return wrb; } @@ -896,7 +897,7 @@ static struct be_mcc_wrb *be_cmd_copy(struct be_adapter *adapter, memcpy(dest_wrb, wrb, sizeof(*wrb)); if (wrb->embedded & cpu_to_le32(MCC_WRB_EMBEDDED_MASK)) - fill_wrb_tags(dest_wrb, (ulong) embedded_payload(wrb)); + fill_wrb_tags(dest_wrb, (ulong)embedded_payload(wrb)); return dest_wrb; } @@ -1114,7 +1115,7 @@ int be_cmd_pmac_add(struct be_adapter *adapter, const u8 *mac_addr, err: mutex_unlock(&adapter->mcc_lock); - if (base_status(status) == MCC_STATUS_UNAUTHORIZED_REQUEST) + if (base_status(status) == MCC_STATUS_UNAUTHORIZED_REQUEST) status = -EPERM; return status; @@ -1803,7 +1804,7 @@ int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf) total_size = buf_len; - get_fat_cmd.size = sizeof(struct be_cmd_req_get_fat) + 60*1024; + get_fat_cmd.size = sizeof(struct be_cmd_req_get_fat) + 60 * 1024; get_fat_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, get_fat_cmd.size, &get_fat_cmd.dma, GFP_ATOMIC); @@ -1813,7 +1814,7 @@ int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf) mutex_lock(&adapter->mcc_lock); while (total_size) { - buf_size = min(total_size, (u32)60*1024); + buf_size = min(total_size, (u32)60 * 1024); total_size -= buf_size; wrb = wrb_from_mccq(adapter); @@ -3362,7 +3363,7 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern, req->pattern = cpu_to_le64(pattern); req->byte_count = cpu_to_le32(byte_cnt); for (i = 0; i < byte_cnt; i++) { - req->snd_buff[i] = (u8)(pattern >> (j*8)); + req->snd_buff[i] = (u8)(pattern >> (j * 8)); j++; if (j > 7) j = 0; @@ -3846,7 +3847,7 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array, req->hdr.domain = domain; req->mac_count = mac_count; if (mac_count) - memcpy(req->mac, mac_array, ETH_ALEN*mac_count); + memcpy(req->mac, mac_array, ETH_ALEN * mac_count); status = be_mcc_notify_wait(adapter); -- cgit v1.2.3 From fbd3dce9580882580b7d4779569062e371e8be48 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Tue, 7 Mar 2023 14:40:59 +0100 Subject: net: microchip: sparx5: Correct the spelling of the keysets in debugfs Correct the name used in the debugfs output. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c index 07b472c84a47..12722f728ef7 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c @@ -198,7 +198,7 @@ static void sparx5_vcap_is2_port_keys(struct sparx5 *sparx5, out->prf(out->dst, "ip6_std"); break; case VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER: - out->prf(out->dst, "ip4_tcp_udp ipv4_other"); + out->prf(out->dst, "ip4_tcp_udp ip4_other"); break; } out->prf(out->dst, "\n ipv6_uc: "); -- cgit v1.2.3 From bfcb94aacc92f7025bd86a545037e5eb8db00e32 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Tue, 7 Mar 2023 14:41:00 +0100 Subject: net: microchip: sparx5: Provide rule count, key removal and keyset select This provides these 3 functions in the VCAP API: - Count the number of rules in a VCAP lookup (chain) - Remove a key from a VCAP rule - Find the keyset that gives the smallest rule list from a list of keysets Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/vcap_api.c | 61 ++++++++++++++++++++++ .../net/ethernet/microchip/vcap/vcap_api_client.h | 11 ++++ 2 files changed, 72 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 4847d0d99ec9..5675b0962bc3 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -976,6 +976,25 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) } EXPORT_SYMBOL_GPL(vcap_lookup_rule_by_cookie); +/* Get number of rules in a vcap instance lookup chain id range */ +int vcap_admin_rule_count(struct vcap_admin *admin, int cid) +{ + int max_cid = roundup(cid + 1, VCAP_CID_LOOKUP_SIZE); + int min_cid = rounddown(cid, VCAP_CID_LOOKUP_SIZE); + struct vcap_rule_internal *elem; + int count = 0; + + list_for_each_entry(elem, &admin->rules, list) { + mutex_lock(&admin->lock); + if (elem->data.vcap_chain_id >= min_cid && + elem->data.vcap_chain_id < max_cid) + ++count; + mutex_unlock(&admin->lock); + } + return count; +} +EXPORT_SYMBOL_GPL(vcap_admin_rule_count); + /* Make a copy of the rule, shallow or full */ static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri, bool full) @@ -3403,6 +3422,25 @@ int vcap_rule_mod_key_u32(struct vcap_rule *rule, enum vcap_key_field key, } EXPORT_SYMBOL_GPL(vcap_rule_mod_key_u32); +/* Remove a key field with value and mask in the rule */ +int vcap_rule_rem_key(struct vcap_rule *rule, enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_keyfield *field; + + field = vcap_find_keyfield(rule, key); + if (!field) { + pr_err("%s:%d: key %s is not in the rule\n", + __func__, __LINE__, vcap_keyfield_name(ri->vctrl, key)); + return -EINVAL; + } + /* Deallocate the key field */ + list_del(&field->ctrl.list); + kfree(field); + return 0; +} +EXPORT_SYMBOL_GPL(vcap_rule_rem_key); + static int vcap_rule_mod_action(struct vcap_rule *rule, enum vcap_action_field action, enum vcap_field_type ftype, @@ -3475,6 +3513,29 @@ int vcap_filter_rule_keys(struct vcap_rule *rule, } EXPORT_SYMBOL_GPL(vcap_filter_rule_keys); +/* Select the keyset from the list that results in the smallest rule size */ +enum vcap_keyfield_set +vcap_select_min_rule_keyset(struct vcap_control *vctrl, + enum vcap_type vtype, + struct vcap_keyset_list *kslist) +{ + enum vcap_keyfield_set ret = VCAP_KFS_NO_VALUE; + const struct vcap_set *kset; + int max = 100, idx; + + for (idx = 0; idx < kslist->cnt; ++idx) { + kset = vcap_keyfieldset(vctrl, vtype, kslist->keysets[idx]); + if (!kset) + continue; + if (kset->sw_per_item >= max) + continue; + max = kset->sw_per_item; + ret = kslist->keysets[idx]; + } + return ret; +} +EXPORT_SYMBOL_GPL(vcap_select_min_rule_keyset); + /* Make a full copy of an existing rule with a new rule id */ struct vcap_rule *vcap_copy_rule(struct vcap_rule *erule) { diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h index 417af9754bcc..d9d1f7c9d762 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -201,6 +201,9 @@ int vcap_rule_add_action_bit(struct vcap_rule *rule, int vcap_rule_add_action_u32(struct vcap_rule *rule, enum vcap_action_field action, u32 value); +/* Get number of rules in a vcap instance lookup chain id range */ +int vcap_admin_rule_count(struct vcap_admin *admin, int cid); + /* VCAP rule counter operations */ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, struct vcap_counter *ctr, u64 cookie); @@ -269,6 +272,14 @@ int vcap_rule_mod_action_u32(struct vcap_rule *rule, int vcap_rule_get_key_u32(struct vcap_rule *rule, enum vcap_key_field key, u32 *value, u32 *mask); +/* Remove a key field with value and mask in the rule */ +int vcap_rule_rem_key(struct vcap_rule *rule, enum vcap_key_field key); + +/* Select the keyset from the list that results in the smallest rule size */ +enum vcap_keyfield_set +vcap_select_min_rule_keyset(struct vcap_control *vctrl, enum vcap_type vtype, + struct vcap_keyset_list *kslist); + struct vcap_client_actionfield * vcap_find_actionfield(struct vcap_rule *rule, enum vcap_action_field act); #endif /* __VCAP_API_CLIENT__ */ -- cgit v1.2.3 From 1c14432dcefaf44c2e3fa11ac61097ee342eecfe Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Tue, 7 Mar 2023 14:41:01 +0100 Subject: net: microchip: sparx5: Add TC template list to a port This adds a list that is used to collect the templates that are active on a port. This allows the template creation to change the port configuration and the template destruction to change it back. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 1 + drivers/net/ethernet/microchip/sparx5/sparx5_main.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 42b77ba9b572..a7edf524eedb 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -282,6 +282,7 @@ static int sparx5_create_port(struct sparx5 *sparx5, spx5_port->phylink_pcs.poll = true; spx5_port->phylink_pcs.ops = &sparx5_phylink_pcs_ops; spx5_port->is_mrouter = false; + INIT_LIST_HEAD(&spx5_port->tc_templates); sparx5->ports[config->portno] = spx5_port; err = sparx5_port_init(sparx5, spx5_port, &config->conf); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 72e7928912eb..62c85463b634 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -192,6 +192,7 @@ struct sparx5_port { u16 ts_id; struct sk_buff_head tx_skbs; bool is_mrouter; + struct list_head tc_templates; /* list of TC templates on this port */ }; enum sparx5_core_clockfreq { -- cgit v1.2.3 From d9f175b0dffbf70add634319a81d683be782c2e1 Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Tue, 7 Mar 2023 14:41:02 +0100 Subject: net: microchip: sparx5: Add port keyset changing functionality With this its is now possible for clients (like TC) to change the port keyset configuration in the Sparx5 VCAPs. This is typically done per traffic class which is guided with the L3 protocol information. Before the change the current keyset configuration is collected in a list that is handed back to the client. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../ethernet/microchip/sparx5/sparx5_vcap_impl.c | 270 +++++++++++++++++++++ .../ethernet/microchip/sparx5/sparx5_vcap_impl.h | 6 + 2 files changed, 276 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index d0d4e0385ac7..187efa1fc904 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -1519,6 +1519,276 @@ static struct vcap_operations sparx5_vcap_ops = { .port_info = sparx5_port_info, }; +static u32 sparx5_vcap_is0_keyset_to_etype_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_NORMAL_7TUPLE: + return VCAP_IS0_PS_ETYPE_NORMAL_7TUPLE; + case VCAP_KFS_NORMAL_5TUPLE_IP4: + return VCAP_IS0_PS_ETYPE_NORMAL_5TUPLE_IP4; + default: + return VCAP_IS0_PS_ETYPE_NORMAL_7TUPLE; + } +} + +static void sparx5_vcap_is0_set_port_keyset(struct net_device *ndev, int lookup, + enum vcap_keyfield_set keyset, + int l3_proto) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + int portno = port->portno; + u32 value; + + switch (l3_proto) { + case ETH_P_IP: + value = sparx5_vcap_is0_keyset_to_etype_ps(keyset); + spx5_rmw(ANA_CL_ADV_CL_CFG_IP4_CLM_KEY_SEL_SET(value), + ANA_CL_ADV_CL_CFG_IP4_CLM_KEY_SEL, + sparx5, + ANA_CL_ADV_CL_CFG(portno, lookup)); + break; + case ETH_P_IPV6: + value = sparx5_vcap_is0_keyset_to_etype_ps(keyset); + spx5_rmw(ANA_CL_ADV_CL_CFG_IP6_CLM_KEY_SEL_SET(value), + ANA_CL_ADV_CL_CFG_IP6_CLM_KEY_SEL, + sparx5, + ANA_CL_ADV_CL_CFG(portno, lookup)); + break; + default: + value = sparx5_vcap_is0_keyset_to_etype_ps(keyset); + spx5_rmw(ANA_CL_ADV_CL_CFG_ETYPE_CLM_KEY_SEL_SET(value), + ANA_CL_ADV_CL_CFG_ETYPE_CLM_KEY_SEL, + sparx5, + ANA_CL_ADV_CL_CFG(portno, lookup)); + break; + } +} + +static u32 sparx5_vcap_is2_keyset_to_arp_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_ARP: + return VCAP_IS2_PS_ARP_ARP; + default: + return VCAP_IS2_PS_ARP_MAC_ETYPE; + } +} + +static u32 sparx5_vcap_is2_keyset_to_ipv4_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_MAC_ETYPE: + return VCAP_IS2_PS_IPV4_UC_MAC_ETYPE; + case VCAP_KFS_IP4_OTHER: + case VCAP_KFS_IP4_TCP_UDP: + return VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER; + case VCAP_KFS_IP_7TUPLE: + return VCAP_IS2_PS_IPV4_UC_IP_7TUPLE; + default: + return VCAP_KFS_NO_VALUE; + } +} + +static u32 sparx5_vcap_is2_keyset_to_ipv6_uc_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_MAC_ETYPE: + return VCAP_IS2_PS_IPV6_UC_MAC_ETYPE; + case VCAP_KFS_IP4_OTHER: + case VCAP_KFS_IP4_TCP_UDP: + return VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER; + case VCAP_KFS_IP_7TUPLE: + return VCAP_IS2_PS_IPV6_UC_IP_7TUPLE; + default: + return VCAP_KFS_NO_VALUE; + } +} + +static u32 sparx5_vcap_is2_keyset_to_ipv6_mc_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_MAC_ETYPE: + return VCAP_IS2_PS_IPV6_MC_MAC_ETYPE; + case VCAP_KFS_IP4_OTHER: + case VCAP_KFS_IP4_TCP_UDP: + return VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER; + case VCAP_KFS_IP_7TUPLE: + return VCAP_IS2_PS_IPV6_MC_IP_7TUPLE; + default: + return VCAP_KFS_NO_VALUE; + } +} + +static void sparx5_vcap_is2_set_port_keyset(struct net_device *ndev, int lookup, + enum vcap_keyfield_set keyset, + int l3_proto) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + int portno = port->portno; + u32 value; + + switch (l3_proto) { + case ETH_P_ARP: + value = sparx5_vcap_is2_keyset_to_arp_ps(keyset); + spx5_rmw(ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(value), + ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL, + sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + break; + case ETH_P_IP: + value = sparx5_vcap_is2_keyset_to_ipv4_ps(keyset); + spx5_rmw(ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(value), + ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL, + sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + spx5_rmw(ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(value), + ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL, + sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + break; + case ETH_P_IPV6: + value = sparx5_vcap_is2_keyset_to_ipv6_uc_ps(keyset); + spx5_rmw(ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(value), + ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL, + sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + value = sparx5_vcap_is2_keyset_to_ipv6_mc_ps(keyset); + spx5_rmw(ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(value), + ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL, + sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + break; + default: + value = VCAP_IS2_PS_NONETH_MAC_ETYPE; + spx5_rmw(ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(value), + ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL, + sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + break; + } +} + +static u32 sparx5_vcap_es2_keyset_to_arp_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_ARP: + return VCAP_ES2_PS_ARP_ARP; + default: + return VCAP_ES2_PS_ARP_MAC_ETYPE; + } +} + +static u32 sparx5_vcap_es2_keyset_to_ipv4_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_MAC_ETYPE: + return VCAP_ES2_PS_IPV4_MAC_ETYPE; + case VCAP_KFS_IP_7TUPLE: + return VCAP_ES2_PS_IPV4_IP_7TUPLE; + case VCAP_KFS_IP4_TCP_UDP: + return VCAP_ES2_PS_IPV4_IP4_TCP_UDP_OTHER; + case VCAP_KFS_IP4_OTHER: + return VCAP_ES2_PS_IPV4_IP4_OTHER; + default: + return VCAP_ES2_PS_IPV4_MAC_ETYPE; + } +} + +static u32 sparx5_vcap_es2_keyset_to_ipv6_ps(enum vcap_keyfield_set keyset) +{ + switch (keyset) { + case VCAP_KFS_MAC_ETYPE: + return VCAP_ES2_PS_IPV6_MAC_ETYPE; + case VCAP_KFS_IP4_TCP_UDP: + case VCAP_KFS_IP4_OTHER: + return VCAP_ES2_PS_IPV6_IP4_DOWNGRADE; + case VCAP_KFS_IP_7TUPLE: + return VCAP_ES2_PS_IPV6_IP_7TUPLE; + case VCAP_KFS_IP6_STD: + return VCAP_ES2_PS_IPV6_IP6_STD; + default: + return VCAP_ES2_PS_IPV6_MAC_ETYPE; + } +} + +static void sparx5_vcap_es2_set_port_keyset(struct net_device *ndev, int lookup, + enum vcap_keyfield_set keyset, + int l3_proto) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + int portno = port->portno; + u32 value; + + switch (l3_proto) { + case ETH_P_IP: + value = sparx5_vcap_es2_keyset_to_ipv4_ps(keyset); + spx5_rmw(EACL_VCAP_ES2_KEY_SEL_IP4_KEY_SEL_SET(value), + EACL_VCAP_ES2_KEY_SEL_IP4_KEY_SEL, + sparx5, + EACL_VCAP_ES2_KEY_SEL(portno, lookup)); + break; + case ETH_P_IPV6: + value = sparx5_vcap_es2_keyset_to_ipv6_ps(keyset); + spx5_rmw(EACL_VCAP_ES2_KEY_SEL_IP6_KEY_SEL_SET(value), + EACL_VCAP_ES2_KEY_SEL_IP6_KEY_SEL, + sparx5, + EACL_VCAP_ES2_KEY_SEL(portno, lookup)); + break; + case ETH_P_ARP: + value = sparx5_vcap_es2_keyset_to_arp_ps(keyset); + spx5_rmw(EACL_VCAP_ES2_KEY_SEL_ARP_KEY_SEL_SET(value), + EACL_VCAP_ES2_KEY_SEL_ARP_KEY_SEL, + sparx5, + EACL_VCAP_ES2_KEY_SEL(portno, lookup)); + break; + } +} + +/* Change the port keyset for the lookup and protocol */ +void sparx5_vcap_set_port_keyset(struct net_device *ndev, + struct vcap_admin *admin, + int cid, + u16 l3_proto, + enum vcap_keyfield_set keyset, + struct vcap_keyset_list *orig) +{ + struct sparx5_port *port; + int lookup; + + switch (admin->vtype) { + case VCAP_TYPE_IS0: + lookup = sparx5_vcap_is0_cid_to_lookup(cid); + if (orig) + sparx5_vcap_is0_get_port_keysets(ndev, lookup, orig, + l3_proto); + sparx5_vcap_is0_set_port_keyset(ndev, lookup, keyset, l3_proto); + break; + case VCAP_TYPE_IS2: + lookup = sparx5_vcap_is2_cid_to_lookup(cid); + if (orig) + sparx5_vcap_is2_get_port_keysets(ndev, lookup, orig, + l3_proto); + sparx5_vcap_is2_set_port_keyset(ndev, lookup, keyset, l3_proto); + break; + case VCAP_TYPE_ES0: + break; + case VCAP_TYPE_ES2: + lookup = sparx5_vcap_es2_cid_to_lookup(cid); + if (orig) + sparx5_vcap_es2_get_port_keysets(ndev, lookup, orig, + l3_proto); + sparx5_vcap_es2_set_port_keyset(ndev, lookup, keyset, l3_proto); + break; + default: + port = netdev_priv(ndev); + sparx5_vcap_type_err(port->sparx5, admin, __func__); + break; + } +} + /* Enable IS0 lookups per port and set the keyset generation */ static void sparx5_vcap_is0_port_key_selection(struct sparx5 *sparx5, struct vcap_admin *admin) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h index 3260ab5e3a82..2684d9199b05 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h @@ -195,6 +195,12 @@ int sparx5_vcap_get_port_keyset(struct net_device *ndev, u16 l3_proto, struct vcap_keyset_list *kslist); +/* Change the port keyset for the lookup and protocol */ +void sparx5_vcap_set_port_keyset(struct net_device *ndev, + struct vcap_admin *admin, int cid, + u16 l3_proto, enum vcap_keyfield_set keyset, + struct vcap_keyset_list *orig); + /* Check if the ethertype is supported by the vcap port classification */ bool sparx5_vcap_is_known_etype(struct vcap_admin *admin, u16 etype); -- cgit v1.2.3 From e1d597ecbe5c5d7128630d4c9ec27c42018613ed Mon Sep 17 00:00:00 2001 From: Steen Hegelund Date: Tue, 7 Mar 2023 14:41:03 +0100 Subject: net: microchip: sparx5: Add TC template support This adds support for using the "template add" and "template destroy" functionality to change the port keyset configuration. If the VCAP lookup already contains rules, the port keyset is left unchanged, as a change would make these rules unusable. When the template is destroyed the port keyset configuration is restored. The filters using the template chain will automatically be deleted by the TC framework. Signed-off-by: Steen Hegelund Signed-off-by: David S. Miller --- .../ethernet/microchip/sparx5/sparx5_tc_flower.c | 209 ++++++++++++++++++++- 1 file changed, 202 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index b36819aafaca..3f87a5285a6d 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -28,6 +28,14 @@ struct sparx5_multiple_rules { struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; }; +struct sparx5_tc_flower_template { + struct list_head list; /* for insertion in the list of templates */ + int cid; /* chain id */ + enum vcap_keyfield_set orig; /* keyset used before the template */ + enum vcap_keyfield_set keyset; /* new keyset used by template */ + u16 l3_proto; /* protocol specified in the template */ +}; + static int sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st) { @@ -382,7 +390,7 @@ static int sparx5_tc_select_protocol_keyset(struct net_device *ndev, /* Find the keysets that the rule can use */ matches.keysets = keysets; matches.max = ARRAY_SIZE(keysets); - if (vcap_rule_find_keysets(vrule, &matches) == 0) + if (!vcap_rule_find_keysets(vrule, &matches)) return -EINVAL; /* Find the keysets that the port configuration supports */ @@ -996,6 +1004,73 @@ static int sparx5_tc_action_vlan_push(struct vcap_admin *admin, return err; } +/* Remove rule keys that may prevent templates from matching a keyset */ +static void sparx5_tc_flower_simplify_rule(struct vcap_admin *admin, + struct vcap_rule *vrule, + u16 l3_proto) +{ + switch (admin->vtype) { + case VCAP_TYPE_IS0: + vcap_rule_rem_key(vrule, VCAP_KF_ETYPE); + switch (l3_proto) { + case ETH_P_IP: + break; + case ETH_P_IPV6: + vcap_rule_rem_key(vrule, VCAP_KF_IP_SNAP_IS); + break; + default: + break; + } + break; + case VCAP_TYPE_ES2: + switch (l3_proto) { + case ETH_P_IP: + if (vrule->keyset == VCAP_KFS_IP4_OTHER) + vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS); + break; + case ETH_P_IPV6: + if (vrule->keyset == VCAP_KFS_IP6_STD) + vcap_rule_rem_key(vrule, VCAP_KF_TCP_IS); + vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS); + break; + default: + break; + } + break; + case VCAP_TYPE_IS2: + switch (l3_proto) { + case ETH_P_IP: + case ETH_P_IPV6: + vcap_rule_rem_key(vrule, VCAP_KF_IP4_IS); + break; + default: + break; + } + break; + default: + break; + } +} + +static bool sparx5_tc_flower_use_template(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin, + struct vcap_rule *vrule) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5_tc_flower_template *ftp; + + list_for_each_entry(ftp, &port->tc_templates, list) { + if (ftp->cid != fco->common.chain_index) + continue; + + vcap_set_rule_set_keyset(vrule, ftp->keyset); + sparx5_tc_flower_simplify_rule(admin, vrule, ftp->l3_proto); + return true; + } + return false; +} + static int sparx5_tc_flower_replace(struct net_device *ndev, struct flow_cls_offload *fco, struct vcap_admin *admin, @@ -1122,12 +1197,14 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, goto out; } - err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, - state.l3_proto, &multi); - if (err) { - NL_SET_ERR_MSG_MOD(fco->common.extack, - "No matching port keyset for filter protocol and keys"); - goto out; + if (!sparx5_tc_flower_use_template(ndev, fco, admin, vrule)) { + err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, + state.l3_proto, &multi); + if (err) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No matching port keyset for filter protocol and keys"); + goto out; + } } /* provide the l3 protocol to guide the keyset selection */ @@ -1259,6 +1336,120 @@ static int sparx5_tc_flower_stats(struct net_device *ndev, return err; } +static int sparx5_tc_flower_template_create(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_tc_flower_parse_usage state = { + .fco = fco, + .l3_proto = ETH_P_ALL, + .admin = admin, + }; + struct sparx5_tc_flower_template *ftp; + struct vcap_keyset_list kslist = {}; + enum vcap_keyfield_set keysets[10]; + struct vcap_control *vctrl; + struct vcap_rule *vrule; + int count, err; + + if (admin->vtype == VCAP_TYPE_ES0) { + pr_err("%s:%d: %s\n", __func__, __LINE__, + "VCAP does not support templates"); + return -EINVAL; + } + + count = vcap_admin_rule_count(admin, fco->common.chain_index); + if (count > 0) { + pr_err("%s:%d: %s\n", __func__, __LINE__, + "Filters are already present"); + return -EBUSY; + } + + ftp = kzalloc(sizeof(*ftp), GFP_KERNEL); + if (!ftp) + return -ENOMEM; + + ftp->cid = fco->common.chain_index; + ftp->orig = VCAP_KFS_NO_VALUE; + ftp->keyset = VCAP_KFS_NO_VALUE; + + vctrl = port->sparx5->vcap_ctrl; + vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, + VCAP_USER_TC, fco->common.prio, 0); + if (IS_ERR(vrule)) { + err = PTR_ERR(vrule); + goto err_rule; + } + + state.vrule = vrule; + state.frule = flow_cls_offload_flow_rule(fco); + err = sparx5_tc_use_dissectors(&state, admin, vrule); + if (err) { + pr_err("%s:%d: key error: %d\n", __func__, __LINE__, err); + goto out; + } + + ftp->l3_proto = state.l3_proto; + + sparx5_tc_flower_simplify_rule(admin, vrule, state.l3_proto); + + /* Find the keysets that the rule can use */ + kslist.keysets = keysets; + kslist.max = ARRAY_SIZE(keysets); + if (!vcap_rule_find_keysets(vrule, &kslist)) { + pr_err("%s:%d: %s\n", __func__, __LINE__, + "Could not find a suitable keyset"); + err = -ENOENT; + goto out; + } + + ftp->keyset = vcap_select_min_rule_keyset(vctrl, admin->vtype, &kslist); + kslist.cnt = 0; + sparx5_vcap_set_port_keyset(ndev, admin, fco->common.chain_index, + state.l3_proto, + ftp->keyset, + &kslist); + + if (kslist.cnt > 0) + ftp->orig = kslist.keysets[0]; + + /* Store new template */ + list_add_tail(&ftp->list, &port->tc_templates); + vcap_free_rule(vrule); + return 0; + +out: + vcap_free_rule(vrule); +err_rule: + kfree(ftp); + return err; +} + +static int sparx5_tc_flower_template_destroy(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5_tc_flower_template *ftp, *tmp; + int err = -ENOENT; + + /* Rules using the template are removed by the tc framework */ + list_for_each_entry_safe(ftp, tmp, &port->tc_templates, list) { + if (ftp->cid != fco->common.chain_index) + continue; + + sparx5_vcap_set_port_keyset(ndev, admin, + fco->common.chain_index, + ftp->l3_proto, ftp->orig, + NULL); + list_del(&ftp->list); + kfree(ftp); + break; + } + return err; +} + int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, bool ingress) { @@ -1282,6 +1473,10 @@ int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, return sparx5_tc_flower_destroy(ndev, fco, admin); case FLOW_CLS_STATS: return sparx5_tc_flower_stats(ndev, fco, admin); + case FLOW_CLS_TMPLT_CREATE: + return sparx5_tc_flower_template_create(ndev, fco, admin); + case FLOW_CLS_TMPLT_DESTROY: + return sparx5_tc_flower_template_destroy(ndev, fco, admin); default: return -EOPNOTSUPP; } -- cgit v1.2.3 From 6bf0ad7f29173869de6a5a359554426a7127c247 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 7 Mar 2023 17:30:35 +0100 Subject: ravb: remove R-Car H3 ES1.* handling R-Car H3 ES1.* was only available to an internal development group and needed a lot of quirks and workarounds. These become a maintenance burden now, so our development group decided to remove upstream support and disable booting for this SoC. Public users only have ES2 onwards. Reviewed-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang Signed-off-by: Simon Horman Reviewed-by: Sergey Shtylyov Link: https://lore.kernel.org/all/20230307163041.3815-8-wsa+renesas@sang-engineering.com/ Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/renesas/ravb_main.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 0f54849a3823..b81f0d8dfda8 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -1390,11 +1389,6 @@ static void ravb_adjust_link(struct net_device *ndev) phy_print_status(phydev); } -static const struct soc_device_attribute r8a7795es10[] = { - { .soc_id = "r8a7795", .revision = "ES1.0", }, - { /* sentinel */ } -}; - /* PHY init function */ static int ravb_phy_init(struct net_device *ndev) { @@ -1434,15 +1428,6 @@ static int ravb_phy_init(struct net_device *ndev) goto err_deregister_fixed_link; } - /* This driver only support 10/100Mbit speeds on R-Car H3 ES1.0 - * at this time. - */ - if (soc_device_match(r8a7795es10)) { - phy_set_max_speed(phydev, SPEED_100); - - netdev_info(ndev, "limited PHY to 100Mbit/s\n"); - } - if (!info->half_duplex) { /* 10BASE, Pause and Asym Pause is not supported */ phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); -- cgit v1.2.3 From 1de2a84dd06091cee943ab796660ba64c8479d33 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:12 -0600 Subject: alx: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Chris Snook Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/atheros/alx/main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 306393f8eeca..49bb9a8f00e6 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -1745,7 +1744,6 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_pci_disable; } - pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); if (!pdev->pm_cap) { @@ -1879,7 +1877,6 @@ out_free_netdev: free_netdev(netdev); out_pci_release: pci_release_mem_regions(pdev); - pci_disable_pcie_error_reporting(pdev); out_pci_disable: pci_disable_device(pdev); return err; @@ -1897,7 +1894,6 @@ static void alx_remove(struct pci_dev *pdev) iounmap(hw->hw_addr); pci_release_mem_regions(pdev); - pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); mutex_destroy(&alx->mtx); -- cgit v1.2.3 From b4e24578b4841b6abac38fd40fddba6caa9a656a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:13 -0600 Subject: be2net: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Ajit Khaparde Cc: Sriharsha Basavapatna Cc: Somnath Kotur Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/emulex/benet/be_main.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 46fe3d74e2e9..aed1b622f51f 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -16,7 +16,6 @@ #include "be.h" #include "be_cmds.h" #include -#include #include #include #include @@ -5726,8 +5725,6 @@ static void be_remove(struct pci_dev *pdev) be_unmap_pci_bars(adapter); be_drv_cleanup(adapter); - pci_disable_pcie_error_reporting(pdev); - pci_release_regions(pdev); pci_disable_device(pdev); @@ -5845,10 +5842,6 @@ static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id) goto free_netdev; } - status = pci_enable_pcie_error_reporting(pdev); - if (!status) - dev_info(&pdev->dev, "PCIe error reporting enabled\n"); - status = be_map_pci_bars(adapter); if (status) goto free_netdev; @@ -5893,7 +5886,6 @@ drv_cleanup: unmap_bars: be_unmap_pci_bars(adapter); free_netdev: - pci_disable_pcie_error_reporting(pdev); free_netdev(netdev); rel_reg: pci_release_regions(pdev); -- cgit v1.2.3 From 5f00358b5e905a1db1077006d01cd5d207f91fb0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:14 -0600 Subject: bnx2: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. cd709aa90648 ("bnx2: Add PCI Advanced Error Reporting support.") added pci_enable_pcie_error_reporting() for all devices, and c239f279e571 ("bnx2: Enable AER on PCIE devices only") restricted it to BNX2_CHIP_5709 devices to avoid an error message when it failed on non-PCIe devices. The PCI core only enables PCIe error reporting on PCIe devices, which I assume means BNX2_CHIP_5709. Signed-off-by: Bjorn Helgaas Cc: Rasesh Mody Cc: GR-Linux-NIC-Dev@marvell.com Reviewed-by: Michael Chan Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnx2.c | 21 --------------------- drivers/net/ethernet/broadcom/bnx2.h | 1 - 2 files changed, 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 9f473854b0f4..a66137b8d1a6 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #if IS_ENABLED(CONFIG_CNIC) @@ -8093,7 +8092,6 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) int rc, i, j; u32 reg; u64 dma_mask, persist_dma_mask; - int err; SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); @@ -8176,12 +8174,6 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->flags |= BNX2_FLAG_PCIE; if (BNX2_CHIP_REV(bp) == BNX2_CHIP_REV_Ax) bp->flags |= BNX2_FLAG_JUMBO_BROKEN; - - /* AER (Advanced Error Reporting) hooks */ - err = pci_enable_pcie_error_reporting(pdev); - if (!err) - bp->flags |= BNX2_FLAG_AER_ENABLED; - } else { bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX); if (bp->pcix_cap == 0) { @@ -8460,11 +8452,6 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) return 0; err_out_unmap: - if (bp->flags & BNX2_FLAG_AER_ENABLED) { - pci_disable_pcie_error_reporting(pdev); - bp->flags &= ~BNX2_FLAG_AER_ENABLED; - } - pci_iounmap(pdev, bp->regview); bp->regview = NULL; @@ -8638,11 +8625,6 @@ bnx2_remove_one(struct pci_dev *pdev) bnx2_free_stats_blk(dev); kfree(bp->temp_stats_blk); - if (bp->flags & BNX2_FLAG_AER_ENABLED) { - pci_disable_pcie_error_reporting(pdev); - bp->flags &= ~BNX2_FLAG_AER_ENABLED; - } - bnx2_release_firmware(bp); free_netdev(dev); @@ -8766,9 +8748,6 @@ static pci_ers_result_t bnx2_io_slot_reset(struct pci_dev *pdev) } rtnl_unlock(); - if (!(bp->flags & BNX2_FLAG_AER_ENABLED)) - return result; - return result; } diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index a09ec47461c9..315b08c64edd 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -6808,7 +6808,6 @@ struct bnx2 { #define BNX2_FLAG_JUMBO_BROKEN 0x00000800 #define BNX2_FLAG_CAN_KEEP_VLAN 0x00001000 #define BNX2_FLAG_BROKEN_STATS 0x00002000 -#define BNX2_FLAG_AER_ENABLED 0x00004000 struct bnx2_napi bnx2_napi[BNX2_MAX_MSIX_VEC]; -- cgit v1.2.3 From 2fba753cc9b57d82800db0997b9271898323c57e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:15 -0600 Subject: bnx2x: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Ariel Elior Cc: Sudarsana Kalluru Cc: Manish Chopra Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 1 - drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 19 ------------------- 2 files changed, 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index dd5945c4bfec..8bcde0a6e011 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1486,7 +1486,6 @@ struct bnx2x { #define IS_VF_FLAG (1 << 22) #define BC_SUPPORTS_RMMOD_CMD (1 << 23) #define HAS_PHYS_PORT_ID (1 << 24) -#define AER_ENABLED (1 << 25) #define PTP_SUPPORTED (1 << 26) #define TX_TIMESTAMPING_EN (1 << 27) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 5d1e4fe335aa..3bb5ea570c87 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -13037,14 +13036,6 @@ static const struct net_device_ops bnx2x_netdev_ops = { .ndo_features_check = bnx2x_features_check, }; -static void bnx2x_disable_pcie_error_reporting(struct bnx2x *bp) -{ - if (bp->flags & AER_ENABLED) { - pci_disable_pcie_error_reporting(bp->pdev); - bp->flags &= ~AER_ENABLED; - } -} - static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, struct net_device *dev, unsigned long board_type) { @@ -13157,13 +13148,6 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, /* Set PCIe reset type to fundamental for EEH recovery */ pdev->needs_freset = 1; - /* AER (Advanced Error reporting) configuration */ - rc = pci_enable_pcie_error_reporting(pdev); - if (!rc) - bp->flags |= AER_ENABLED; - else - BNX2X_DEV_INFO("Failed To configure PCIe AER [%d]\n", rc); - /* * Clean the following indirect addresses for all functions since it * is not used by the driver. @@ -14020,8 +14004,6 @@ init_one_freemem: bnx2x_free_mem_bp(bp); init_one_exit: - bnx2x_disable_pcie_error_reporting(bp); - if (bp->regview) iounmap(bp->regview); @@ -14102,7 +14084,6 @@ static void __bnx2x_remove(struct pci_dev *pdev, pci_set_power_state(pdev, PCI_D3hot); } - bnx2x_disable_pcie_error_reporting(bp); if (remove_netdev) { if (bp->regview) iounmap(bp->regview); -- cgit v1.2.3 From 5f29b73d4eba2926ab99d7bf5f2028810af3c66c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:16 -0600 Subject: bnxt: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Reviewed-by: Michael Chan Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 5d4b1f2ebeac..7245fee13ad0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -12706,8 +12705,6 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev) goto init_err_release; } - pci_enable_pcie_error_reporting(pdev); - INIT_WORK(&bp->sp_task, bnxt_sp_task); INIT_DELAYED_WORK(&bp->fw_reset_task, bnxt_fw_reset_task); @@ -13187,7 +13184,6 @@ static void bnxt_remove_one(struct pci_dev *pdev) bnxt_rdma_aux_device_uninit(bp); bnxt_ptp_clear(bp); - pci_disable_pcie_error_reporting(pdev); unregister_netdev(dev); clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state); /* Flush any pending tasks */ -- cgit v1.2.3 From ca7f175fc24eea4638f93a4a49d229c34ae0c770 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:17 -0600 Subject: cxgb4: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Raju Rangoju Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 7db2403c4c9c..f0bc7396ce2b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -6687,7 +6686,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_free_adapter; } - pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); pci_save_state(pdev); adap_idx++; @@ -7092,7 +7090,6 @@ fw_attach_fail: out_unmap_bar0: iounmap(regs); out_disable_device: - pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); out_release_regions: pci_release_regions(pdev); @@ -7171,7 +7168,6 @@ static void remove_one(struct pci_dev *pdev) } #endif iounmap(adapter->regs); - pci_disable_pcie_error_reporting(pdev); if ((adapter->flags & CXGB4_DEV_ENABLED)) { pci_disable_device(pdev); adapter->flags &= ~CXGB4_DEV_ENABLED; -- cgit v1.2.3 From 49f79ac22f89476cb2eac018676490e33a7c4a10 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:18 -0600 Subject: net/fungible: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Acked-by: Dimitris Michailidis Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/fungible/funcore/fun_dev.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/fungible/funcore/fun_dev.c b/drivers/net/ethernet/fungible/funcore/fun_dev.c index fb5120d90f26..3680f83feba2 100644 --- a/drivers/net/ethernet/fungible/funcore/fun_dev.c +++ b/drivers/net/ethernet/fungible/funcore/fun_dev.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -#include #include #include #include @@ -748,7 +747,6 @@ void fun_dev_disable(struct fun_dev *fdev) pci_free_irq_vectors(pdev); pci_clear_master(pdev); - pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); fun_unmap_bars(fdev); @@ -781,8 +779,6 @@ int fun_dev_enable(struct fun_dev *fdev, struct pci_dev *pdev, goto unmap; } - pci_enable_pcie_error_reporting(pdev); - rc = sanitize_dev(fdev); if (rc) goto disable_dev; @@ -830,7 +826,6 @@ free_irq_mgr: free_irqs: pci_free_irq_vectors(pdev); disable_dev: - pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); unmap: fun_unmap_bars(fdev); -- cgit v1.2.3 From c183033f631a9265e5f85827b1cfcdb8d5a7ee35 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:19 -0600 Subject: net: hns3: remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Yisen Zhuang Cc: Salil Mehta Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 25be7f8ac7cd..5caea154362f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 2d0e0372069d3050479d62d48f3b04445f60083a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:20 -0600 Subject: netxen_nic: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Also note that the driver only called these for NX_IS_REVISION_P3 devices, so since f26e58bf6f54, error reporting has been enabled for devices other than NX_IS_REVISION_P3. Signed-off-by: Bjorn Helgaas Cc: Manish Chopra Cc: Rahul Verma Cc: GR-Linux-NIC-Dev@marvell.com Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index de8d54b23f73..59d0dd862fd1 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -18,7 +18,6 @@ #include #include #include -#include MODULE_DESCRIPTION("QLogic/NetXen (1/10) GbE Intelligent Ethernet Driver"); MODULE_LICENSE("GPL"); @@ -1464,9 +1463,6 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if ((err = pci_request_regions(pdev, netxen_nic_driver_name))) goto err_out_disable_pdev; - if (NX_IS_REVISION_P3(pdev->revision)) - pci_enable_pcie_error_reporting(pdev); - pci_set_master(pdev); netdev = alloc_etherdev(sizeof(struct netxen_adapter)); @@ -1603,8 +1599,6 @@ err_out_free_netdev: free_netdev(netdev); err_out_free_res: - if (NX_IS_REVISION_P3(pdev->revision)) - pci_disable_pcie_error_reporting(pdev); pci_release_regions(pdev); err_out_disable_pdev: @@ -1659,10 +1653,8 @@ static void netxen_nic_remove(struct pci_dev *pdev) netxen_release_firmware(adapter); - if (NX_IS_REVISION_P3(pdev->revision)) { + if (NX_IS_REVISION_P3(pdev->revision)) netxen_cleanup_minidump(adapter); - pci_disable_pcie_error_reporting(pdev); - } pci_release_regions(pdev); pci_disable_device(pdev); -- cgit v1.2.3 From fe3f4c292da1908eda5d322e60b3c09ed9508288 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:21 -0600 Subject: octeon_ep: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Veerasenareddy Burru Cc: Abhijit Ayarekar Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeon_ep/octep_main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c index 5a898fb88e37..fdce78ceea87 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -1050,7 +1049,6 @@ static int octep_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_pci_regions; } - pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); netdev = alloc_etherdev_mq(sizeof(struct octep_device), @@ -1106,7 +1104,6 @@ register_dev_err: err_octep_config: free_netdev(netdev); err_alloc_netdev: - pci_disable_pcie_error_reporting(pdev); pci_release_mem_regions(pdev); err_pci_regions: err_dma_mask: @@ -1139,7 +1136,6 @@ static void octep_remove(struct pci_dev *pdev) octep_device_cleanup(oct); pci_release_mem_regions(pdev); free_netdev(netdev); - pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); } -- cgit v1.2.3 From 1263c7b78315f6682a106d56e9878b3a7b176660 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:22 -0600 Subject: qed: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Ariel Elior Cc: Manish Chopra Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qed/qed_main.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index c91898be7c03..f5af83342856 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include "qed.h" @@ -259,8 +258,6 @@ static void qed_free_pci(struct qed_dev *cdev) { struct pci_dev *pdev = cdev->pdev; - pci_disable_pcie_error_reporting(pdev); - if (cdev->doorbells && cdev->db_size) iounmap(cdev->doorbells); if (cdev->regview) @@ -366,12 +363,6 @@ static int qed_init_pci(struct qed_dev *cdev, struct pci_dev *pdev) return -ENOMEM; } - /* AER (Advanced Error reporting) configuration */ - rc = pci_enable_pcie_error_reporting(pdev); - if (rc) - DP_VERBOSE(cdev, NETIF_MSG_DRV, - "Failed to configure PCIe AER [%d]\n", rc); - return 0; err2: -- cgit v1.2.3 From 5f1fbdc168f4bf9705b81a8844d0f8ca201a7363 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:23 -0600 Subject: net: qede: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Ariel Elior Cc: Manish Chopra Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qede/qede_main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 261f982ca40d..4c6c685820e3 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "qede.h" #include "qede_ptp.h" -- cgit v1.2.3 From 95e35f5994075090ed086ad3bcef33878218784e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:24 -0600 Subject: qlcnic: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Shahed Shaikh Cc: Manish Chopra Cc: GR-Linux-NIC-Dev@marvell.com Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 4 ---- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 1 - 2 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 44dac3c0908e..90df4a0909fa 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -2445,7 +2444,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_disable_pdev; pci_set_master(pdev); - pci_enable_pcie_error_reporting(pdev); ahw = kzalloc(sizeof(struct qlcnic_hardware_context), GFP_KERNEL); if (!ahw) { @@ -2675,7 +2673,6 @@ err_out_free_hw_res: kfree(ahw); err_out_free_res: - pci_disable_pcie_error_reporting(pdev); pci_release_regions(pdev); err_out_disable_pdev: @@ -2757,7 +2754,6 @@ static void qlcnic_remove(struct pci_dev *pdev) qlcnic_release_firmware(adapter); - pci_disable_pcie_error_reporting(pdev); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 5c2edb715d3e..74125188beb8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #ifdef CONFIG_QLCNIC_HWMON #include -- cgit v1.2.3 From e07ce5567194dd14f4fcbbaac4a1c48bb97da8e3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:25 -0600 Subject: qlcnic: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Shahed Shaikh Cc: Manish Chopra Cc: GR-Linux-NIC-Dev@marvell.com Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 2fd5c6fdb500..bcef8ab715bf 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "qlcnic.h" #include "qlcnic_sriov.h" -- cgit v1.2.3 From bdedf705688c7704f2efc3ce4930a32612e4efdc Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:26 -0600 Subject: sfc: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Martin Habets Acked-by: Edward Cree Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/efx.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 02c2adeb0a12..92c390ec4735 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include "net_driver.h" #include @@ -892,8 +891,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev) free_netdev(efx->net_dev); probe_data = container_of(efx, struct efx_probe_data, efx); kfree(probe_data); - - pci_disable_pcie_error_reporting(pci_dev); }; /* NIC VPD information @@ -1126,8 +1123,6 @@ static int efx_pci_probe(struct pci_dev *pci_dev, netif_warn(efx, probe, efx->net_dev, "failed to create MTDs (%d)\n", rc); - (void)pci_enable_pcie_error_reporting(pci_dev); - if (efx->type->udp_tnl_push_ports) efx->type->udp_tnl_push_ports(efx); -- cgit v1.2.3 From 4ac9272691a402d5e862be627bac54d2cd6dacbc Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:27 -0600 Subject: sfc: falcon: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Martin Habets Acked-by: Edward Cree Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/falcon/efx.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/falcon/efx.c b/drivers/net/ethernet/sfc/falcon/efx.c index e151b0957751..e001f27085c6 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.c +++ b/drivers/net/ethernet/sfc/falcon/efx.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include "net_driver.h" #include "efx.h" @@ -2765,8 +2764,6 @@ static void ef4_pci_remove(struct pci_dev *pci_dev) ef4_fini_struct(efx); free_netdev(efx->net_dev); - - pci_disable_pcie_error_reporting(pci_dev); }; /* NIC VPD information @@ -2927,12 +2924,6 @@ static int ef4_pci_probe(struct pci_dev *pci_dev, netif_warn(efx, probe, efx->net_dev, "failed to create MTDs (%d)\n", rc); - rc = pci_enable_pcie_error_reporting(pci_dev); - if (rc && rc != -EINVAL) - netif_notice(efx, probe, efx->net_dev, - "PCIE error reporting unavailable (%d).\n", - rc); - return 0; fail4: -- cgit v1.2.3 From ecded61ceb89880f0b0490a168d18c9845b60eb3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:28 -0600 Subject: sfc/siena: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Martin Habets Acked-by: Edward Cree Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/siena/efx.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/siena/efx.c b/drivers/net/ethernet/sfc/siena/efx.c index ef52ec71d197..8c557f6a183c 100644 --- a/drivers/net/ethernet/sfc/siena/efx.c +++ b/drivers/net/ethernet/sfc/siena/efx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include "net_driver.h" #include @@ -874,8 +873,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev) efx_siena_fini_struct(efx); free_netdev(efx->net_dev); - - pci_disable_pcie_error_reporting(pci_dev); }; /* NIC VPD information @@ -1094,8 +1091,6 @@ static int efx_pci_probe(struct pci_dev *pci_dev, netif_warn(efx, probe, efx->net_dev, "failed to create MTDs (%d)\n", rc); - (void)pci_enable_pcie_error_reporting(pci_dev); - if (efx->type->udp_tnl_push_ports) efx->type->udp_tnl_push_ports(efx); -- cgit v1.2.3 From c39abdd396bc2b299798e8422538806a8dc29d20 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:29 -0600 Subject: sfc_ef100: Drop redundant pci_disable_pcie_error_reporting() 51b35a454efd ("sfc: skeleton EF100 PF driver") added a call to pci_disable_pcie_error_reporting() in ef100_pci_remove(). Remove this call since there's no apparent reason to disable error reporting when it was not previously enabled. Note that since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core enables PCIe error reporting for all devices during enumeration, so the driver doesn't need to do it itself. Signed-off-by: Bjorn Helgaas Cc: Martin Habets Acked-by: Edward Cree Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/ef100.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/ef100.c b/drivers/net/ethernet/sfc/ef100.c index 71aab3d0480f..6334992b0af4 100644 --- a/drivers/net/ethernet/sfc/ef100.c +++ b/drivers/net/ethernet/sfc/ef100.c @@ -11,7 +11,6 @@ #include "net_driver.h" #include -#include #include "efx_common.h" #include "efx_channels.h" #include "io.h" @@ -440,8 +439,6 @@ static void ef100_pci_remove(struct pci_dev *pci_dev) pci_dbg(pci_dev, "shutdown successful\n"); - pci_disable_pcie_error_reporting(pci_dev); - pci_set_drvdata(pci_dev, NULL); efx_fini_struct(efx); kfree(probe_data); -- cgit v1.2.3 From a7edf8e5142f2037db944ec87c0f2491dadfe2a6 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:30 -0600 Subject: net: ngbe: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Jiawen Wu Cc: Mengyuan Lou Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 5b564d348c09..0e4163e1106f 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -520,7 +519,6 @@ static int ngbe_probe(struct pci_dev *pdev, goto err_pci_disable_dev; } - pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); netdev = devm_alloc_etherdev_mqs(&pdev->dev, @@ -669,7 +667,6 @@ err_clear_interrupt_scheme: err_free_mac_table: kfree(wx->mac_table); err_pci_release_regions: - pci_disable_pcie_error_reporting(pdev); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); err_pci_disable_dev: @@ -698,7 +695,6 @@ static void ngbe_remove(struct pci_dev *pdev) kfree(wx->mac_table); wx_clear_interrupt_scheme(wx); - pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); } -- cgit v1.2.3 From 1fccc781bf7e32b89ecb289987670b040f97d7c0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:31 -0600 Subject: net: txgbe: Drop redundant pci_enable_pcie_error_reporting() pci_enable_pcie_error_reporting() enables the device to send ERR_* Messages. Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core does this for all devices during enumeration, so the driver doesn't need to do it itself. Remove the redundant pci_enable_pcie_error_reporting() call from the driver. Also remove the corresponding pci_disable_pcie_error_reporting() from the driver .remove() path. Note that this only controls ERR_* Messages from the device. An ERR_* Message may cause the Root Port to generate an interrupt, depending on the AER Root Error Command register managed by the AER service driver. Signed-off-by: Bjorn Helgaas Cc: Jiawen Wu Cc: Mengyuan Lou Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 6c0a98230557..859feaafd350 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -538,7 +537,6 @@ static int txgbe_probe(struct pci_dev *pdev, goto err_pci_disable_dev; } - pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); netdev = devm_alloc_etherdev_mqs(&pdev->dev, @@ -698,7 +696,6 @@ err_release_hw: err_free_mac_table: kfree(wx->mac_table); err_pci_release_regions: - pci_disable_pcie_error_reporting(pdev); pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); err_pci_disable_dev: @@ -729,8 +726,6 @@ static void txgbe_remove(struct pci_dev *pdev) kfree(wx->mac_table); wx_clear_interrupt_scheme(wx); - pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); } -- cgit v1.2.3 From ab76f2bff0f3d165428fb71532c7e6ba81df8317 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:32 -0600 Subject: e1000e: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Cc: intel-wired-lan@lists.osuosl.org Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/e1000e/netdev.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index e1eb1de88bf9..6f5c16aebcbf 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 8be901a6715f290131ca919bef425717538382b5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:33 -0600 Subject: fm10k: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 027d721feb18..d748b98274e7 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -3,7 +3,6 @@ #include #include -#include #include "fm10k.h" -- cgit v1.2.3 From acd2bb015fae8d67ff11d11dfe250b0fb6c6081c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:34 -0600 Subject: i40e: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/i40e/i40e.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 60ce4d15d82a..6e310a539467 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 495b72c79302bb827ff14686000edbba1dd9e372 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:35 -0600 Subject: iavf: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/iavf/iavf.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h index 232bc61d9eee..2cdce251472c 100644 --- a/drivers/net/ethernet/intel/iavf/iavf.h +++ b/drivers/net/ethernet/intel/iavf/iavf.h @@ -6,7 +6,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From ddd652ef30e396efe006494a67e43e174099b419 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:36 -0600 Subject: ice: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index b0e29e342401..d79a48d27f1a 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 648a2020fdac17591b909849513a10a65daa8b27 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:37 -0600 Subject: igb: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igb/igb_main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 03bc1e8af575..a2914298dd69 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 1530522f101f5f58f73bb3c9f5b1be39cb1c2a62 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:38 -0600 Subject: igc: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 2928a6c73692..d7ee06d28b50 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From f3468e3944398c4de9b2deff73041394fba632e7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 12:19:39 -0600 Subject: ixgbe: Remove unnecessary aer.h include is unused, so remove it. Signed-off-by: Bjorn Helgaas Cc: Jesse Brandeburg Cc: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 8736ca4b2628..63d4e32df029 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 04eb3d1cede014defaa8c259263439cc7e9ceda6 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 7 Mar 2023 16:19:26 +0000 Subject: net: mtk_eth_soc: tidy mtk_gmac0_rgmii_adjust() Get rid of the multiple tenary operators in mtk_gmac0_rgmii_adjust() replacing them with a single if(), thus making the code easier to read. Signed-off-by: Russell King (Oracle) Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 14be6ea51b88..c63f17929ccf 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -397,38 +397,42 @@ static int mt7621_gmac0_rgmii_adjust(struct mtk_eth *eth, static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth, phy_interface_t interface, int speed) { - u32 val; + unsigned long rate; + u32 tck, rck, intf; int ret; if (interface == PHY_INTERFACE_MODE_TRGMII) { mtk_w32(eth, TRGMII_MODE, INTF_MODE); - val = 500000000; - ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], val); + ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], 500000000); if (ret) dev_err(eth->dev, "Failed to set trgmii pll: %d\n", ret); return; } - val = (speed == SPEED_1000) ? - INTF_MODE_RGMII_1000 : INTF_MODE_RGMII_10_100; - mtk_w32(eth, val, INTF_MODE); + if (speed == SPEED_1000) { + intf = INTF_MODE_RGMII_1000; + rate = 250000000; + rck = RCK_CTRL_RGMII_1000; + tck = TCK_CTRL_RGMII_1000; + } else { + intf = INTF_MODE_RGMII_10_100; + rate = 500000000; + rck = RCK_CTRL_RGMII_10_100; + tck = TCK_CTRL_RGMII_10_100; + } + + mtk_w32(eth, intf, INTF_MODE); regmap_update_bits(eth->ethsys, ETHSYS_CLKCFG0, ETHSYS_TRGMII_CLK_SEL362_5, ETHSYS_TRGMII_CLK_SEL362_5); - val = (speed == SPEED_1000) ? 250000000 : 500000000; - ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], val); + ret = clk_set_rate(eth->clks[MTK_CLK_TRGPLL], rate); if (ret) dev_err(eth->dev, "Failed to set trgmii pll: %d\n", ret); - val = (speed == SPEED_1000) ? - RCK_CTRL_RGMII_1000 : RCK_CTRL_RGMII_10_100; - mtk_w32(eth, val, TRGMII_RCK_CTRL); - - val = (speed == SPEED_1000) ? - TCK_CTRL_RGMII_1000 : TCK_CTRL_RGMII_10_100; - mtk_w32(eth, val, TRGMII_TCK_CTRL); + mtk_w32(eth, rck, TRGMII_RCK_CTRL); + mtk_w32(eth, tck, TRGMII_TCK_CTRL); } static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, -- cgit v1.2.3 From 7910898e1b2ae8ba0646434294931bf69cc0fdb5 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 7 Mar 2023 16:19:31 +0000 Subject: net: mtk_eth_soc: move trgmii ddr2 check to probe function If TRGMII mode is not permitted when using DDR2 mode, we should handle that when setting up phylink's ->supported_interfaces so phylink knows that this is not supported by the hardware. Move this check to mtk_add_mac(). Signed-off-by: Russell King (Oracle) Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index c63f17929ccf..1b385dfe620f 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -374,17 +374,6 @@ static int mt7621_gmac0_rgmii_adjust(struct mtk_eth *eth, { u32 val; - /* Check DDR memory type. - * Currently TRGMII mode with DDR2 memory is not supported. - */ - regmap_read(eth->ethsys, ETHSYS_SYSCFG, &val); - if (interface == PHY_INTERFACE_MODE_TRGMII && - val & SYSCFG_DRAM_TYPE_DDR2) { - dev_err(eth->dev, - "TRGMII mode with DDR2 memory is not supported!\n"); - return -EOPNOTSUPP; - } - val = (interface == PHY_INTERFACE_MODE_TRGMII) ? ETHSYS_TRGMII_MT7621_DDR_PLL : 0; @@ -4333,6 +4322,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) struct mtk_mac *mac; int id, err; int txqs = 1; + u32 val; if (!_id) { dev_err(eth->dev, "missing mac id\n"); @@ -4409,6 +4399,15 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) __set_bit(PHY_INTERFACE_MODE_TRGMII, mac->phylink_config.supported_interfaces); + /* TRGMII is not permitted on MT7621 if using DDR2 */ + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GMAC1_TRGMII) && + MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII_MT7621_CLK)) { + regmap_read(eth->ethsys, ETHSYS_SYSCFG, &val); + if (val & SYSCFG_DRAM_TYPE_DDR2) + __clear_bit(PHY_INTERFACE_MODE_TRGMII, + mac->phylink_config.supported_interfaces); + } + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) { __set_bit(PHY_INTERFACE_MODE_SGMII, mac->phylink_config.supported_interfaces); -- cgit v1.2.3 From c9f9e3a3289f2f068db45ea1219aa5b295762f44 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 7 Mar 2023 16:19:36 +0000 Subject: net: mtk_eth_soc: remove unnecessary checks in mtk_mac_config() mtk_mac_config() checks that the interface mode is permitted for the capabilities, but we already do these checks in mtk_add_mac() when initialising phylink's supported_interfaces bitmap. Remove the unnecessary tests. Signed-off-by: Russell King (Oracle) Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 1b385dfe620f..f78810717f66 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -458,12 +458,6 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode, /* Setup soc pin functions */ switch (state->interface) { case PHY_INTERFACE_MODE_TRGMII: - if (mac->id) - goto err_phy; - if (!MTK_HAS_CAPS(mac->hw->soc->caps, - MTK_GMAC1_TRGMII)) - goto err_phy; - fallthrough; case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_ID: @@ -480,11 +474,9 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode, case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: case PHY_INTERFACE_MODE_SGMII: - if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { - err = mtk_gmac_sgmii_path_setup(eth, mac->id); - if (err) - goto init_err; - } + err = mtk_gmac_sgmii_path_setup(eth, mac->id); + if (err) + goto init_err; break; case PHY_INTERFACE_MODE_GMII: if (MTK_HAS_CAPS(eth->soc->caps, MTK_GEPHY)) { -- cgit v1.2.3 From 8cd9de08ccf6e1575d4994ba8347fa9ac083f900 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 7 Mar 2023 16:19:41 +0000 Subject: net: mtk_eth_soc: remove support for RMII and REVMII modes Since the conversion of mtk_eth_soc to phylink's supported_interfaces bitmap, these two modes have not been selectable. No one has raised this as an issue. Checking the in-kernel DT files, none of them use either of these modes with this hardware. Daniel Golle concurs: A quick grep through the device trees of the more than 650 ramips and mediatek boards we support in OpenWrt has revealed that *none* of them uses either reduced-MII or reverse-MII PHY modes. I could imaging that some more specialized ramips boards may use the RMII 100M PHY mode to connect with exotic PHYs for industrial or automotive applications (think: for 100BASE-T1 PHY connected via RMII). I have never seen or touched such boards, but there are hints that they do exist. For reverse-MII there are cases in which the Ralink SoC (Rt305x, for example) is used in iNIC mode, ie. connected as a PHY to another SoC, and running only a minimal firmware rather than running Linux. Due to the lack of external DRAM for the Ralink SoC on this kind of boards, the Ralink SoC there will anyway never be able to boot Linux. I've seen this e.g. in multimedia devices like early WiFi-connected not-yet-so-smart TVs. Consequently, the conclusion is that no one uses these modes with this hardware, so we might as well drop support for them. Signed-off-by: Russell King (Oracle) Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index f78810717f66..b22cd160554e 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -463,8 +463,6 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode, case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_MII: - case PHY_INTERFACE_MODE_REVMII: - case PHY_INTERFACE_MODE_RMII: if (MTK_HAS_CAPS(eth->soc->caps, MTK_RGMII)) { err = mtk_gmac_rgmii_path_setup(eth, mac->id); if (err) @@ -524,21 +522,13 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode, } } - ge_mode = 0; switch (state->interface) { case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_GMII: ge_mode = 1; break; - case PHY_INTERFACE_MODE_REVMII: - ge_mode = 2; - break; - case PHY_INTERFACE_MODE_RMII: - if (mac->id) - goto err_phy; - ge_mode = 3; - break; default: + ge_mode = 0; break; } -- cgit v1.2.3 From a4ba62906db80246b24408e5c8a9e9d78b647b00 Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:08 +0530 Subject: i40e: consolidate maximum frame size calculation for vsi Introduce new helper function to calculate max frame size for validating and setting of vsi frame size. This is used while configuring vsi, changing the MTU and attaching an XDP program to the vsi. This is in preparation of the legacy rx and multi-buffer changes to be introduced in later patches. Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_main.c | 71 +++++++++++++++-------------- 1 file changed, 38 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 467001db5070..d375d7940308 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2896,15 +2896,35 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) } /** - * i40e_max_xdp_frame_size - returns the maximum allowed frame size for XDP + * i40e_calculate_vsi_rx_buf_len - Calculates buffer length + * + * @vsi: VSI to calculate rx_buf_len from + */ +static u16 i40e_calculate_vsi_rx_buf_len(struct i40e_vsi *vsi) +{ + if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) + return I40E_RXBUFFER_2048; + + return PAGE_SIZE < 8192 ? I40E_RXBUFFER_3072 : I40E_RXBUFFER_2048; +} + +/** + * i40e_max_vsi_frame_size - returns the maximum allowed frame size for VSI * @vsi: the vsi + * @xdp_prog: XDP program **/ -static int i40e_max_xdp_frame_size(struct i40e_vsi *vsi) +static int i40e_max_vsi_frame_size(struct i40e_vsi *vsi, + struct bpf_prog *xdp_prog) { - if (PAGE_SIZE >= 8192 || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) - return I40E_RXBUFFER_2048; + u16 rx_buf_len = i40e_calculate_vsi_rx_buf_len(vsi); + u16 chain_len; + + if (xdp_prog) + chain_len = 1; else - return I40E_RXBUFFER_3072; + chain_len = I40E_MAX_CHAINED_RX_BUFFERS; + + return min_t(u16, rx_buf_len * chain_len, I40E_MAX_RXBUFFER); } /** @@ -2919,12 +2939,13 @@ static int i40e_change_mtu(struct net_device *netdev, int new_mtu) struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + int frame_size; - if (i40e_enabled_xdp_vsi(vsi)) { - int frame_size = new_mtu + I40E_PACKET_HDR_PAD; - - if (frame_size > i40e_max_xdp_frame_size(vsi)) - return -EINVAL; + frame_size = i40e_max_vsi_frame_size(vsi, vsi->xdp_prog); + if (new_mtu > frame_size - I40E_PACKET_HDR_PAD) { + netdev_err(netdev, "Error changing mtu to %d, Max is %d\n", + new_mtu, frame_size - I40E_PACKET_HDR_PAD); + return -EINVAL; } netdev_dbg(netdev, "changing MTU from %d to %d\n", @@ -3693,24 +3714,6 @@ static int i40e_vsi_configure_tx(struct i40e_vsi *vsi) return err; } -/** - * i40e_calculate_vsi_rx_buf_len - Calculates buffer length - * - * @vsi: VSI to calculate rx_buf_len from - */ -static u16 i40e_calculate_vsi_rx_buf_len(struct i40e_vsi *vsi) -{ - if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) - return I40E_RXBUFFER_2048; - -#if (PAGE_SIZE < 8192) - if (!I40E_2K_TOO_SMALL_WITH_PADDING && vsi->netdev->mtu <= ETH_DATA_LEN) - return I40E_RXBUFFER_1536 - NET_IP_ALIGN; -#endif - - return PAGE_SIZE < 8192 ? I40E_RXBUFFER_3072 : I40E_RXBUFFER_2048; -} - /** * i40e_vsi_configure_rx - Configure the VSI for Rx * @vsi: the VSI being configured @@ -3722,13 +3725,15 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) int err = 0; u16 i; - vsi->max_frame = I40E_MAX_RXBUFFER; + vsi->max_frame = i40e_max_vsi_frame_size(vsi, vsi->xdp_prog); vsi->rx_buf_len = i40e_calculate_vsi_rx_buf_len(vsi); #if (PAGE_SIZE < 8192) if (vsi->netdev && !I40E_2K_TOO_SMALL_WITH_PADDING && - vsi->netdev->mtu <= ETH_DATA_LEN) - vsi->max_frame = I40E_RXBUFFER_1536 - NET_IP_ALIGN; + vsi->netdev->mtu <= ETH_DATA_LEN) { + vsi->rx_buf_len = I40E_RXBUFFER_1536 - NET_IP_ALIGN; + vsi->max_frame = vsi->rx_buf_len; + } #endif /* set up individual rings */ @@ -13316,14 +13321,14 @@ out_err: static int i40e_xdp_setup(struct i40e_vsi *vsi, struct bpf_prog *prog, struct netlink_ext_ack *extack) { - int frame_size = vsi->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + int frame_size = i40e_max_vsi_frame_size(vsi, prog); struct i40e_pf *pf = vsi->back; struct bpf_prog *old_prog; bool need_reset; int i; /* Don't allow frames that span over multiple buffers */ - if (frame_size > i40e_calculate_vsi_rx_buf_len(vsi)) { + if (vsi->netdev->mtu > frame_size - I40E_PACKET_HDR_PAD) { NL_SET_ERR_MSG_MOD(extack, "MTU too large to enable XDP"); return -EINVAL; } -- cgit v1.2.3 From f7f732a7196d2e876639b1af453b30a6a5fe4a90 Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:09 +0530 Subject: i40e: change Rx buffer size for legacy-rx to support XDP multi-buffer Adding support for XDP multi-buffer entails adding information of all the fragments of the packet in the xdp_buff. This approach implies that underlying buffer has to provide tailroom for skb_shared_info. In the legacy-rx mode, driver can only configure up to 2k sized Rx buffers and with the current configuration of 2k sized Rx buffers there is no way to do tailroom reservation for skb_shared_info. Hence size of Rx buffers is now lowered to 2048 - sizeof(skb_shared_info). Also, driver can only chain up to 5 Rx buffers and this means max MTU supported for legacy-rx is now 8614 (5 * rx_buffer_len - ETH header with VLAN). Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 7 +++++++ drivers/net/ethernet/intel/i40e/i40e_main.c | 12 +++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 4934ff58332c..afc4fa8c66af 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -5402,6 +5402,13 @@ flags_complete: return -EOPNOTSUPP; } + if ((changed_flags & I40E_FLAG_LEGACY_RX) && + I40E_2K_TOO_SMALL_WITH_PADDING) { + dev_warn(&pf->pdev->dev, + "2k Rx buffer is too small to fit standard MTU and skb_shared_info\n"); + return -EOPNOTSUPP; + } + if ((changed_flags & new_flags & I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED) && (new_flags & I40E_FLAG_MFP_ENABLED)) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index d375d7940308..e8cf5644bf10 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2903,7 +2903,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) static u16 i40e_calculate_vsi_rx_buf_len(struct i40e_vsi *vsi) { if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) - return I40E_RXBUFFER_2048; + return SKB_WITH_OVERHEAD(I40E_RXBUFFER_2048); return PAGE_SIZE < 8192 ? I40E_RXBUFFER_3072 : I40E_RXBUFFER_2048; } @@ -3661,10 +3661,16 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) } /* configure Rx buffer alignment */ - if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) + if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) { + if (I40E_2K_TOO_SMALL_WITH_PADDING) { + dev_info(&vsi->back->pdev->dev, + "2k Rx buffer is too small to fit standard MTU and skb_shared_info\n"); + return -EOPNOTSUPP; + } clear_ring_build_skb_enabled(ring); - else + } else { set_ring_build_skb_enabled(ring); + } ring->rx_offset = i40e_rx_offset(ring); -- cgit v1.2.3 From e2843f037127c3a9db26718aaa293d2a8e5881e4 Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:10 +0530 Subject: i40e: add pre-xdp page_count in rx_buffer Page count of rx_buffer needs to be stored prior to XDP call to prevent page recycling in case that buffer would be freed within xdp redirect path. Instead of storing it on the stack, now it is stored in the rx_buffer struct. This will help in processing multi-buffers as the page counts of all rx_buffers (of the same packet) don't need to be stored on stack. Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 23 ++++++++--------------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 + 2 files changed, 9 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 924f972b91fa..a7fba294a8f4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1970,7 +1970,6 @@ static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb, * i40e_can_reuse_rx_page - Determine if page can be reused for another Rx * @rx_buffer: buffer containing the page * @rx_stats: rx stats structure for the rx ring - * @rx_buffer_pgcnt: buffer page refcount pre xdp_do_redirect() call * * If page is reusable, we have a green light for calling i40e_reuse_rx_page, * which will assign the current buffer to the buffer that next_to_alloc is @@ -1981,8 +1980,7 @@ static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb, * or busy if it could not be reused. */ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, - struct i40e_rx_queue_stats *rx_stats, - int rx_buffer_pgcnt) + struct i40e_rx_queue_stats *rx_stats) { unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; struct page *page = rx_buffer->page; @@ -1995,7 +1993,7 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, #if (PAGE_SIZE < 8192) /* if we are only owner of page we can reuse it */ - if (unlikely((rx_buffer_pgcnt - pagecnt_bias) > 1)) { + if (unlikely((rx_buffer->page_count - pagecnt_bias) > 1)) { rx_stats->page_busy_count++; return false; } @@ -2058,19 +2056,17 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, * i40e_get_rx_buffer - Fetch Rx buffer and synchronize data for use * @rx_ring: rx descriptor ring to transact packets on * @size: size of buffer to add to skb - * @rx_buffer_pgcnt: buffer page refcount * * This function will pull an Rx buffer from the ring and synchronize it * for use by the CPU. */ static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, - const unsigned int size, - int *rx_buffer_pgcnt) + const unsigned int size) { struct i40e_rx_buffer *rx_buffer; rx_buffer = i40e_rx_bi(rx_ring, rx_ring->next_to_clean); - *rx_buffer_pgcnt = + rx_buffer->page_count = #if (PAGE_SIZE < 8192) page_count(rx_buffer->page); #else @@ -2226,16 +2222,14 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, * i40e_put_rx_buffer - Clean up used buffer and either recycle or free * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: rx buffer to pull data from - * @rx_buffer_pgcnt: rx buffer page refcount pre xdp_do_redirect() call * * This function will clean up the contents of the rx_buffer. It will * either recycle the buffer or unmap it and free the associated resources. */ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer, - int rx_buffer_pgcnt) + struct i40e_rx_buffer *rx_buffer) { - if (i40e_can_reuse_rx_page(rx_buffer, &rx_ring->rx_stats, rx_buffer_pgcnt)) { + if (i40e_can_reuse_rx_page(rx_buffer, &rx_ring->rx_stats)) { /* hand second half of page back to the ring */ i40e_reuse_rx_page(rx_ring, rx_buffer); } else { @@ -2457,7 +2451,6 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, while (likely(total_rx_packets < (unsigned int)budget)) { struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; - int rx_buffer_pgcnt; unsigned int size; u64 qword; @@ -2500,7 +2493,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, break; i40e_trace(clean_rx_irq, rx_ring, rx_desc, skb); - rx_buffer = i40e_get_rx_buffer(rx_ring, size, &rx_buffer_pgcnt); + rx_buffer = i40e_get_rx_buffer(rx_ring, size); /* retrieve a buffer from the ring */ if (!skb) { @@ -2541,7 +2534,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, break; } - i40e_put_rx_buffer(rx_ring, rx_buffer, rx_buffer_pgcnt); + i40e_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; i40e_inc_ntc(rx_ring); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 768290dc6f48..eec4a4a99b9c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -277,6 +277,7 @@ struct i40e_rx_buffer { struct page *page; __u32 page_offset; __u16 pagecnt_bias; + __u32 page_count; }; struct i40e_queue_stats { -- cgit v1.2.3 From 03e88c8a791c0655ddea07fd4c7cd6cb16087c8f Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:11 +0530 Subject: i40e: Change size to truesize when using i40e_rx_buffer_flip() Truesize is now passed directly to i40e_rx_buffer_flip() instead of size so that it does not need to recalculate truesize from size using i40e_rx_frame_truesize() before adjusting page offset. With these change the function can now be used during skb building and adding frags. In later patches it will also be easier for adjusting page offsets for multi-buffers. Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 54 ++++++++++------------------- 1 file changed, 19 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index a7fba294a8f4..fa44a2c353b2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2018,6 +2018,21 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, return true; } +/** + * i40e_rx_buffer_flip - adjusted rx_buffer to point to an unused region + * @rx_buffer: Rx buffer to adjust + * @truesize: Size of adjustment + **/ +static void i40e_rx_buffer_flip(struct i40e_rx_buffer *rx_buffer, + unsigned int truesize) +{ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif +} + /** * i40e_add_rx_frag - Add contents of Rx buffer to sk_buff * @rx_ring: rx descriptor ring to transact packets on @@ -2045,11 +2060,7 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, rx_buffer->page_offset, size, truesize); /* page is being used so we must update the page offset */ -#if (PAGE_SIZE < 8192) - rx_buffer->page_offset ^= truesize; -#else - rx_buffer->page_offset += truesize; -#endif + i40e_rx_buffer_flip(rx_buffer, truesize); } /** @@ -2154,11 +2165,7 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, size, truesize); /* buffer is used by skb, update page_offset */ -#if (PAGE_SIZE < 8192) - rx_buffer->page_offset ^= truesize; -#else - rx_buffer->page_offset += truesize; -#endif + i40e_rx_buffer_flip(rx_buffer, truesize); } else { /* buffer is unused, reset bias back to rx_buffer */ rx_buffer->pagecnt_bias++; @@ -2209,11 +2216,7 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, skb_metadata_set(skb, metasize); /* buffer is used by skb, update page_offset */ -#if (PAGE_SIZE < 8192) - rx_buffer->page_offset ^= truesize; -#else - rx_buffer->page_offset += truesize; -#endif + i40e_rx_buffer_flip(rx_buffer, truesize); return skb; } @@ -2326,25 +2329,6 @@ xdp_out: return result; } -/** - * i40e_rx_buffer_flip - adjusted rx_buffer to point to an unused region - * @rx_ring: Rx ring - * @rx_buffer: Rx buffer to adjust - * @size: Size of adjustment - **/ -static void i40e_rx_buffer_flip(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer, - unsigned int size) -{ - unsigned int truesize = i40e_rx_frame_truesize(rx_ring, size); - -#if (PAGE_SIZE < 8192) - rx_buffer->page_offset ^= truesize; -#else - rx_buffer->page_offset += truesize; -#endif -} - /** * i40e_xdp_ring_update_tail - Updates the XDP Tx ring tail register * @xdp_ring: XDP Tx ring @@ -2513,7 +2497,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, if (xdp_res) { if (xdp_res & (I40E_XDP_TX | I40E_XDP_REDIR)) { xdp_xmit |= xdp_res; - i40e_rx_buffer_flip(rx_ring, rx_buffer, size); + i40e_rx_buffer_flip(rx_buffer, xdp.frame_sz); } else { rx_buffer->pagecnt_bias++; } -- cgit v1.2.3 From 2bc0de9aca3ebdf24674f5a2a7890fde6304f5ca Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:12 +0530 Subject: i40e: use frame_sz instead of recalculating truesize for building skb In skb path truesize is calculated while building skb. This is now avoided and xdp->frame_is used instead for both i40e_build_skb() and i40e_construct_skb(). Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index fa44a2c353b2..e34595ca4fbe 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2113,11 +2113,6 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, struct xdp_buff *xdp) { unsigned int size = xdp->data_end - xdp->data; -#if (PAGE_SIZE < 8192) - unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; -#else - unsigned int truesize = SKB_DATA_ALIGN(size); -#endif unsigned int headlen; struct sk_buff *skb; @@ -2162,10 +2157,10 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, if (size) { skb_add_rx_frag(skb, 0, rx_buffer->page, rx_buffer->page_offset + headlen, - size, truesize); + size, xdp->frame_sz); /* buffer is used by skb, update page_offset */ - i40e_rx_buffer_flip(rx_buffer, truesize); + i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); } else { /* buffer is unused, reset bias back to rx_buffer */ rx_buffer->pagecnt_bias++; @@ -2188,13 +2183,6 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, struct xdp_buff *xdp) { unsigned int metasize = xdp->data - xdp->data_meta; -#if (PAGE_SIZE < 8192) - unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; -#else - unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + - SKB_DATA_ALIGN(xdp->data_end - - xdp->data_hard_start); -#endif struct sk_buff *skb; /* Prefetch first cache line of first page. If xdp->data_meta @@ -2205,7 +2193,7 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, net_prefetch(xdp->data_meta); /* build an skb around the page buffer */ - skb = napi_build_skb(xdp->data_hard_start, truesize); + skb = napi_build_skb(xdp->data_hard_start, xdp->frame_sz); if (unlikely(!skb)) return NULL; @@ -2216,7 +2204,7 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, skb_metadata_set(skb, metasize); /* buffer is used by skb, update page_offset */ - i40e_rx_buffer_flip(rx_buffer, truesize); + i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); return skb; } -- cgit v1.2.3 From e9031f2da1aef34b0b4c659ead613c335b46ae92 Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:13 +0530 Subject: i40e: introduce next_to_process to i40e_ring Add a new field called next_to_process in the i40e_ring that is advanced for every buffer and change the semantics of next_to_clean to point to the first buffer of a packet. Driver will use next_to_process in the same way next_to_clean was used previously. For the non multi-buffer case, next_to_process and next_to_clean will always be the same since each packet consists of a single buffer. Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 26 +++++++++++++++----------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 4 ++++ 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index e34595ca4fbe..7fa35ff52689 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1524,6 +1524,7 @@ skip_free: rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; + rx_ring->next_to_process = 0; rx_ring->next_to_use = 0; } @@ -1576,6 +1577,7 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; + rx_ring->next_to_process = 0; rx_ring->next_to_use = 0; /* XDP RX-queue info only needed for RX rings exposed to XDP */ @@ -2076,7 +2078,7 @@ static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, { struct i40e_rx_buffer *rx_buffer; - rx_buffer = i40e_rx_bi(rx_ring, rx_ring->next_to_clean); + rx_buffer = i40e_rx_bi(rx_ring, rx_ring->next_to_process); rx_buffer->page_count = #if (PAGE_SIZE < 8192) page_count(rx_buffer->page); @@ -2375,16 +2377,16 @@ void i40e_finalize_xdp_rx(struct i40e_ring *rx_ring, unsigned int xdp_res) } /** - * i40e_inc_ntc: Advance the next_to_clean index + * i40e_inc_ntp: Advance the next_to_process index * @rx_ring: Rx ring **/ -static void i40e_inc_ntc(struct i40e_ring *rx_ring) +static void i40e_inc_ntp(struct i40e_ring *rx_ring) { - u32 ntc = rx_ring->next_to_clean + 1; + u32 ntp = rx_ring->next_to_process + 1; - ntc = (ntc < rx_ring->count) ? ntc : 0; - rx_ring->next_to_clean = ntc; - prefetch(I40E_RX_DESC(rx_ring, ntc)); + ntp = (ntp < rx_ring->count) ? ntp : 0; + rx_ring->next_to_process = ntp; + prefetch(I40E_RX_DESC(rx_ring, ntp)); } /** @@ -2421,6 +2423,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, xdp_prog = READ_ONCE(rx_ring->xdp_prog); while (likely(total_rx_packets < (unsigned int)budget)) { + u16 ntp = rx_ring->next_to_process; struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; unsigned int size; @@ -2433,7 +2436,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, cleaned_count = 0; } - rx_desc = I40E_RX_DESC(rx_ring, rx_ring->next_to_clean); + rx_desc = I40E_RX_DESC(rx_ring, ntp); /* status_error_len will always be zero for unused descriptors * because it's cleared in cleanup, and overlaps with hdr_addr @@ -2452,8 +2455,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, i40e_clean_programming_status(rx_ring, rx_desc->raw.qword[0], qword); - rx_buffer = i40e_rx_bi(rx_ring, rx_ring->next_to_clean); - i40e_inc_ntc(rx_ring); + rx_buffer = i40e_rx_bi(rx_ring, ntp); + i40e_inc_ntp(rx_ring); i40e_reuse_rx_page(rx_ring, rx_buffer); cleaned_count++; continue; @@ -2509,7 +2512,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, i40e_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; - i40e_inc_ntc(rx_ring); + i40e_inc_ntp(rx_ring); + rx_ring->next_to_clean = rx_ring->next_to_process; if (i40e_is_non_eop(rx_ring, rx_desc)) continue; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index eec4a4a99b9c..75888a9ab06d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -337,6 +337,10 @@ struct i40e_ring { u8 dcb_tc; /* Traffic class of ring */ u8 __iomem *tail; + /* Next descriptor to be processed; next_to_clean is updated only on + * processing EOP descriptor + */ + u16 next_to_process; /* high bit set means dynamic, use accessor routines to read/write. * hardware only supports 2us resolution for the ITR registers. * these values always store the USER setting, and must be converted -- cgit v1.2.3 From 01aa49e31e1674e22dd9c868ca6b4b945acd621e Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:14 +0530 Subject: i40e: add xdp_buff to i40e_ring struct Store xdp_buff on Rx ring struct in preparation for XDP multi-buffer support. This will allow us to combine fragmented frames across separate NAPI cycles in the same way as currently skb fragments are handled. This means that skb pointer on Rx ring will become redundant and will be removed in a later patch. As a consequence i40e_trace() now uses xdp instead of skb pointer. Truesize only needs to be calculated for page sizes bigger than 4k as it is always half-page for 4k pages. With xdp_buff on ring, frame size can now be set during xdp_init_buff() and need not be repopulated in each NAPI call for 4k pages. As a consequence i40e_rx_frame_truesize() is now used only for bigger pages. Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 ++ drivers/net/ethernet/intel/i40e/i40e_trace.h | 20 ++++++++--------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 33 +++++++++++----------------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 7 ++++++ 4 files changed, 32 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e8cf5644bf10..f2c151a01e77 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3616,6 +3616,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) } } + xdp_init_buff(&ring->xdp, i40e_rx_pg_size(ring) / 2, &ring->xdp_rxq); + rx_ctx.dbuff = DIV_ROUND_UP(ring->rx_buf_len, BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT)); diff --git a/drivers/net/ethernet/intel/i40e/i40e_trace.h b/drivers/net/ethernet/intel/i40e/i40e_trace.h index 79d587ad5409..33b4e30f5e00 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_trace.h +++ b/drivers/net/ethernet/intel/i40e/i40e_trace.h @@ -162,45 +162,45 @@ DECLARE_EVENT_CLASS( TP_PROTO(struct i40e_ring *ring, union i40e_16byte_rx_desc *desc, - struct sk_buff *skb), + struct xdp_buff *xdp), - TP_ARGS(ring, desc, skb), + TP_ARGS(ring, desc, xdp), TP_STRUCT__entry( __field(void*, ring) __field(void*, desc) - __field(void*, skb) + __field(void*, xdp) __string(devname, ring->netdev->name) ), TP_fast_assign( __entry->ring = ring; __entry->desc = desc; - __entry->skb = skb; + __entry->xdp = xdp; __assign_str(devname, ring->netdev->name); ), TP_printk( - "netdev: %s ring: %p desc: %p skb %p", + "netdev: %s ring: %p desc: %p xdp %p", __get_str(devname), __entry->ring, - __entry->desc, __entry->skb) + __entry->desc, __entry->xdp) ); DEFINE_EVENT( i40e_rx_template, i40e_clean_rx_irq, TP_PROTO(struct i40e_ring *ring, union i40e_16byte_rx_desc *desc, - struct sk_buff *skb), + struct xdp_buff *xdp), - TP_ARGS(ring, desc, skb)); + TP_ARGS(ring, desc, xdp)); DEFINE_EVENT( i40e_rx_template, i40e_clean_rx_irq_rx, TP_PROTO(struct i40e_ring *ring, union i40e_16byte_rx_desc *desc, - struct sk_buff *skb), + struct xdp_buff *xdp), - TP_ARGS(ring, desc, skb)); + TP_ARGS(ring, desc, xdp)); DECLARE_EVENT_CLASS( i40e_xmit_template, diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 7fa35ff52689..5544c2d43a92 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1619,21 +1619,19 @@ void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) writel(val, rx_ring->tail); } +#if (PAGE_SIZE >= 8192) static unsigned int i40e_rx_frame_truesize(struct i40e_ring *rx_ring, unsigned int size) { unsigned int truesize; -#if (PAGE_SIZE < 8192) - truesize = i40e_rx_pg_size(rx_ring) / 2; /* Must be power-of-2 */ -#else truesize = rx_ring->rx_offset ? SKB_DATA_ALIGN(size + rx_ring->rx_offset) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) : SKB_DATA_ALIGN(size); -#endif return truesize; } +#endif /** * i40e_alloc_mapped_page - recycle or make a new page @@ -2405,21 +2403,16 @@ static void i40e_inc_ntp(struct i40e_ring *rx_ring) static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, unsigned int *rx_cleaned) { - unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); unsigned int offset = rx_ring->rx_offset; + struct xdp_buff *xdp = &rx_ring->xdp; struct sk_buff *skb = rx_ring->skb; unsigned int xdp_xmit = 0; struct bpf_prog *xdp_prog; bool failure = false; - struct xdp_buff xdp; int xdp_res = 0; -#if (PAGE_SIZE < 8192) - frame_sz = i40e_rx_frame_truesize(rx_ring, 0); -#endif - xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); - xdp_prog = READ_ONCE(rx_ring->xdp_prog); while (likely(total_rx_packets < (unsigned int)budget)) { @@ -2467,7 +2460,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, if (!size) break; - i40e_trace(clean_rx_irq, rx_ring, rx_desc, skb); + i40e_trace(clean_rx_irq, rx_ring, rx_desc, xdp); rx_buffer = i40e_get_rx_buffer(rx_ring, size); /* retrieve a buffer from the ring */ @@ -2476,19 +2469,19 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, hard_start = page_address(rx_buffer->page) + rx_buffer->page_offset - offset; - xdp_prepare_buff(&xdp, hard_start, offset, size, true); - xdp_buff_clear_frags_flag(&xdp); + xdp_prepare_buff(xdp, hard_start, offset, size, true); + xdp_buff_clear_frags_flag(xdp); #if (PAGE_SIZE > 4096) /* At larger PAGE_SIZE, frame_sz depend on len size */ - xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, size); + xdp->frame_sz = i40e_rx_frame_truesize(rx_ring, size); #endif - xdp_res = i40e_run_xdp(rx_ring, &xdp, xdp_prog); + xdp_res = i40e_run_xdp(rx_ring, xdp, xdp_prog); } if (xdp_res) { if (xdp_res & (I40E_XDP_TX | I40E_XDP_REDIR)) { xdp_xmit |= xdp_res; - i40e_rx_buffer_flip(rx_buffer, xdp.frame_sz); + i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); } else { rx_buffer->pagecnt_bias++; } @@ -2497,9 +2490,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, } else if (skb) { i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); } else if (ring_uses_build_skb(rx_ring)) { - skb = i40e_build_skb(rx_ring, rx_buffer, &xdp); + skb = i40e_build_skb(rx_ring, rx_buffer, xdp); } else { - skb = i40e_construct_skb(rx_ring, rx_buffer, &xdp); + skb = i40e_construct_skb(rx_ring, rx_buffer, xdp); } /* exit if we failed to retrieve a buffer */ @@ -2528,7 +2521,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, /* populate checksum, VLAN, and protocol */ i40e_process_skb_fields(rx_ring, rx_desc, skb); - i40e_trace(clean_rx_irq_rx, rx_ring, rx_desc, skb); + i40e_trace(clean_rx_irq_rx, rx_ring, rx_desc, xdp); napi_gro_receive(&rx_ring->q_vector->napi, skb); skb = NULL; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 75888a9ab06d..55da9fde9d17 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -337,6 +337,13 @@ struct i40e_ring { u8 dcb_tc; /* Traffic class of ring */ u8 __iomem *tail; + /* Storing xdp_buff on ring helps in saving the state of partially built + * packet when i40e_clean_rx_ring_irq() must return before it sees EOP + * and to resume packet building for this ring in the next call to + * i40e_clean_rx_ring_irq(). + */ + struct xdp_buff xdp; + /* Next descriptor to be processed; next_to_clean is updated only on * processing EOP descriptor */ -- cgit v1.2.3 From e213ced19befc09d6d6913799053b67896596cd1 Mon Sep 17 00:00:00 2001 From: Tirthendu Sarkar Date: Fri, 10 Mar 2023 00:26:15 +0530 Subject: i40e: add support for XDP multi-buffer Rx This patch adds multi-buffer support for the i40e_driver. i40e_clean_rx_irq() is modified to collate all the buffers of a packet before calling the XDP program. xdp_buff is built for the first frag of the packet and subsequent frags are added to it. 'next_to_process' is incremented for all non-EOP frags while 'next_to_clean' stays at the first descriptor of the packet. XDP program is called only on receiving EOP frag. New functions are added for adding frags to xdp_buff and for post processing of the buffers once the xdp prog has run. For XDP_PASS this results in a skb with multiple fragments. i40e_build_skb() builds the skb around xdp buffer that already contains frags data. So i40e_add_rx_frag() helper function is now removed. Since fields before 'dataref' in skb_shared_info are cleared during napi_skb_build(), xdp_update_skb_shared_info() is called to set those. For i40e_construct_skb(), all the frags data needs to be copied from xdp_buffer's shared_skb_info to newly constructed skb's shared_skb_info. This also means 'skb' does not need to be preserved across i40e_napi_poll() calls and hence is removed from i40e_ring structure. Previously i40e_alloc_rx_buffers() was called for every 32 cleaned buffers. For multi-buffers this may not be optimal as there may be more cleaned buffers in each i40e_clean_rx_irq() call. So this is now called when at least half of the ring size has been cleaned. Signed-off-by: Tirthendu Sarkar Tested-by: Chandan Kumar Rout (A Contingent Worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/i40e/i40e_main.c | 7 +- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 314 ++++++++++++++++++---------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 8 - 3 files changed, 211 insertions(+), 118 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index f2c151a01e77..053be338e4ba 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2919,7 +2919,7 @@ static int i40e_max_vsi_frame_size(struct i40e_vsi *vsi, u16 rx_buf_len = i40e_calculate_vsi_rx_buf_len(vsi); u16 chain_len; - if (xdp_prog) + if (xdp_prog && !xdp_prog->aux->xdp_has_frags) chain_len = 1; else chain_len = I40E_MAX_CHAINED_RX_BUFFERS; @@ -13337,7 +13337,7 @@ static int i40e_xdp_setup(struct i40e_vsi *vsi, struct bpf_prog *prog, /* Don't allow frames that span over multiple buffers */ if (vsi->netdev->mtu > frame_size - I40E_PACKET_HDR_PAD) { - NL_SET_ERR_MSG_MOD(extack, "MTU too large to enable XDP"); + NL_SET_ERR_MSG_MOD(extack, "MTU too large for linear frames and XDP prog does not support frags"); return -EINVAL; } @@ -13823,7 +13823,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | - NETDEV_XDP_ACT_XSK_ZEROCOPY; + NETDEV_XDP_ACT_XSK_ZEROCOPY | + NETDEV_XDP_ACT_RX_SG; } else { /* Relate the VSI_VMDQ name to the VSI_MAIN name. Note that we * are still limited by IFNAMSIZ, but we're adding 'v%d\0' to diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 5544c2d43a92..32cce90abbb4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1477,9 +1477,6 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) if (!rx_ring->rx_bi) return; - dev_kfree_skb(rx_ring->skb); - rx_ring->skb = NULL; - if (rx_ring->xsk_pool) { i40e_xsk_clean_rx_ring(rx_ring); goto skip_free; @@ -2033,36 +2030,6 @@ static void i40e_rx_buffer_flip(struct i40e_rx_buffer *rx_buffer, #endif } -/** - * i40e_add_rx_frag - Add contents of Rx buffer to sk_buff - * @rx_ring: rx descriptor ring to transact packets on - * @rx_buffer: buffer containing page to add - * @skb: sk_buff to place the data into - * @size: packet length from rx_desc - * - * This function will add the data contained in rx_buffer->page to the skb. - * It will just attach the page as a frag to the skb. - * - * The function will then update the page offset. - **/ -static void i40e_add_rx_frag(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer, - struct sk_buff *skb, - unsigned int size) -{ -#if (PAGE_SIZE < 8192) - unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; -#else - unsigned int truesize = SKB_DATA_ALIGN(size + rx_ring->rx_offset); -#endif - - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, - rx_buffer->page_offset, size, truesize); - - /* page is being used so we must update the page offset */ - i40e_rx_buffer_flip(rx_buffer, truesize); -} - /** * i40e_get_rx_buffer - Fetch Rx buffer and synchronize data for use * @rx_ring: rx descriptor ring to transact packets on @@ -2099,20 +2066,82 @@ static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, } /** - * i40e_construct_skb - Allocate skb and populate it + * i40e_put_rx_buffer - Clean up used buffer and either recycle or free * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: rx buffer to pull data from + * + * This function will clean up the contents of the rx_buffer. It will + * either recycle the buffer or unmap it and free the associated resources. + */ +static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer) +{ + if (i40e_can_reuse_rx_page(rx_buffer, &rx_ring->rx_stats)) { + /* hand second half of page back to the ring */ + i40e_reuse_rx_page(rx_ring, rx_buffer); + } else { + /* we are not reusing the buffer so unmap it */ + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + i40e_rx_pg_size(rx_ring), + DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + /* clear contents of buffer_info */ + rx_buffer->page = NULL; + } +} + +/** + * i40e_process_rx_buffs- Processing of buffers post XDP prog or on error + * @rx_ring: Rx descriptor ring to transact packets on + * @xdp_res: Result of the XDP program + * @xdp: xdp_buff pointing to the data + **/ +static void i40e_process_rx_buffs(struct i40e_ring *rx_ring, int xdp_res, + struct xdp_buff *xdp) +{ + u32 next = rx_ring->next_to_clean; + struct i40e_rx_buffer *rx_buffer; + + xdp->flags = 0; + + while (1) { + rx_buffer = i40e_rx_bi(rx_ring, next); + if (++next == rx_ring->count) + next = 0; + + if (!rx_buffer->page) + continue; + + if (xdp_res == I40E_XDP_CONSUMED) + rx_buffer->pagecnt_bias++; + else + i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); + + /* EOP buffer will be put in i40e_clean_rx_irq() */ + if (next == rx_ring->next_to_process) + return; + + i40e_put_rx_buffer(rx_ring, rx_buffer); + } +} + +/** + * i40e_construct_skb - Allocate skb and populate it + * @rx_ring: rx descriptor ring to transact packets on * @xdp: xdp_buff pointing to the data + * @nr_frags: number of buffers for the packet * * This function allocates an skb. It then populates it with the page * data from the current receive descriptor, taking care to set up the * skb correctly. */ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer, - struct xdp_buff *xdp) + struct xdp_buff *xdp, + u32 nr_frags) { unsigned int size = xdp->data_end - xdp->data; + struct i40e_rx_buffer *rx_buffer; unsigned int headlen; struct sk_buff *skb; @@ -2152,13 +2181,17 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, memcpy(__skb_put(skb, headlen), xdp->data, ALIGN(headlen, sizeof(long))); + rx_buffer = i40e_rx_bi(rx_ring, rx_ring->next_to_clean); /* update all of the pointers */ size -= headlen; if (size) { + if (unlikely(nr_frags >= MAX_SKB_FRAGS)) { + dev_kfree_skb(skb); + return NULL; + } skb_add_rx_frag(skb, 0, rx_buffer->page, rx_buffer->page_offset + headlen, size, xdp->frame_sz); - /* buffer is used by skb, update page_offset */ i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); } else { @@ -2166,21 +2199,40 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, rx_buffer->pagecnt_bias++; } + if (unlikely(xdp_buff_has_frags(xdp))) { + struct skb_shared_info *sinfo, *skinfo = skb_shinfo(skb); + + sinfo = xdp_get_shared_info_from_buff(xdp); + memcpy(&skinfo->frags[skinfo->nr_frags], &sinfo->frags[0], + sizeof(skb_frag_t) * nr_frags); + + xdp_update_skb_shared_info(skb, skinfo->nr_frags + nr_frags, + sinfo->xdp_frags_size, + nr_frags * xdp->frame_sz, + xdp_buff_is_frag_pfmemalloc(xdp)); + + /* First buffer has already been processed, so bump ntc */ + if (++rx_ring->next_to_clean == rx_ring->count) + rx_ring->next_to_clean = 0; + + i40e_process_rx_buffs(rx_ring, I40E_XDP_PASS, xdp); + } + return skb; } /** * i40e_build_skb - Build skb around an existing buffer * @rx_ring: Rx descriptor ring to transact packets on - * @rx_buffer: Rx buffer to pull data from * @xdp: xdp_buff pointing to the data + * @nr_frags: number of buffers for the packet * * This function builds an skb around an existing Rx buffer, taking care * to set up the skb correctly and avoid any memcpy overhead. */ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer, - struct xdp_buff *xdp) + struct xdp_buff *xdp, + u32 nr_frags) { unsigned int metasize = xdp->data - xdp->data_meta; struct sk_buff *skb; @@ -2203,36 +2255,25 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, if (metasize) skb_metadata_set(skb, metasize); - /* buffer is used by skb, update page_offset */ - i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); + if (unlikely(xdp_buff_has_frags(xdp))) { + struct skb_shared_info *sinfo; - return skb; -} + sinfo = xdp_get_shared_info_from_buff(xdp); + xdp_update_skb_shared_info(skb, nr_frags, + sinfo->xdp_frags_size, + nr_frags * xdp->frame_sz, + xdp_buff_is_frag_pfmemalloc(xdp)); -/** - * i40e_put_rx_buffer - Clean up used buffer and either recycle or free - * @rx_ring: rx descriptor ring to transact packets on - * @rx_buffer: rx buffer to pull data from - * - * This function will clean up the contents of the rx_buffer. It will - * either recycle the buffer or unmap it and free the associated resources. - */ -static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer) -{ - if (i40e_can_reuse_rx_page(rx_buffer, &rx_ring->rx_stats)) { - /* hand second half of page back to the ring */ - i40e_reuse_rx_page(rx_ring, rx_buffer); + i40e_process_rx_buffs(rx_ring, I40E_XDP_PASS, xdp); } else { - /* we are not reusing the buffer so unmap it */ - dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, - i40e_rx_pg_size(rx_ring), - DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); - __page_frag_cache_drain(rx_buffer->page, - rx_buffer->pagecnt_bias); - /* clear contents of buffer_info */ - rx_buffer->page = NULL; + struct i40e_rx_buffer *rx_buffer; + + rx_buffer = i40e_rx_bi(rx_ring, rx_ring->next_to_clean); + /* buffer is used by skb, update page_offset */ + i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); } + + return skb; } /** @@ -2387,6 +2428,55 @@ static void i40e_inc_ntp(struct i40e_ring *rx_ring) prefetch(I40E_RX_DESC(rx_ring, ntp)); } +/** + * i40e_add_xdp_frag: Add a frag to xdp_buff + * @xdp: xdp_buff pointing to the data + * @nr_frags: return number of buffers for the packet + * @rx_buffer: rx_buffer holding data of the current frag + * @size: size of data of current frag + */ +static int i40e_add_xdp_frag(struct xdp_buff *xdp, u32 *nr_frags, + struct i40e_rx_buffer *rx_buffer, u32 size) +{ + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); + + if (!xdp_buff_has_frags(xdp)) { + sinfo->nr_frags = 0; + sinfo->xdp_frags_size = 0; + xdp_buff_set_frags_flag(xdp); + } else if (unlikely(sinfo->nr_frags >= MAX_SKB_FRAGS)) { + /* Overflowing packet: All frags need to be dropped */ + return -ENOMEM; + } + + __skb_fill_page_desc_noacc(sinfo, sinfo->nr_frags++, rx_buffer->page, + rx_buffer->page_offset, size); + + sinfo->xdp_frags_size += size; + + if (page_is_pfmemalloc(rx_buffer->page)) + xdp_buff_set_frag_pfmemalloc(xdp); + *nr_frags = sinfo->nr_frags; + + return 0; +} + +/** + * i40e_consume_xdp_buff - Consume all the buffers of the packet and update ntc + * @rx_ring: rx descriptor ring to transact packets on + * @xdp: xdp_buff pointing to the data + * @rx_buffer: rx_buffer of eop desc + */ +static void i40e_consume_xdp_buff(struct i40e_ring *rx_ring, + struct xdp_buff *xdp, + struct i40e_rx_buffer *rx_buffer) +{ + i40e_process_rx_buffs(rx_ring, I40E_XDP_CONSUMED, xdp); + i40e_put_rx_buffer(rx_ring, rx_buffer); + rx_ring->next_to_clean = rx_ring->next_to_process; + xdp->data = NULL; +} + /** * i40e_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf * @rx_ring: rx descriptor ring to transact packets on @@ -2405,9 +2495,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, { unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); + u16 clean_threshold = rx_ring->count / 2; unsigned int offset = rx_ring->rx_offset; struct xdp_buff *xdp = &rx_ring->xdp; - struct sk_buff *skb = rx_ring->skb; unsigned int xdp_xmit = 0; struct bpf_prog *xdp_prog; bool failure = false; @@ -2419,11 +2509,14 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, u16 ntp = rx_ring->next_to_process; struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; + struct sk_buff *skb; unsigned int size; + u32 nfrags = 0; + bool neop; u64 qword; /* return some buffers to hardware, one at a time is too slow */ - if (cleaned_count >= I40E_RX_BUFFER_WRITE) { + if (cleaned_count >= clean_threshold) { failure = failure || i40e_alloc_rx_buffers(rx_ring, cleaned_count); cleaned_count = 0; @@ -2461,76 +2554,83 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget, break; i40e_trace(clean_rx_irq, rx_ring, rx_desc, xdp); + /* retrieve a buffer from the ring */ rx_buffer = i40e_get_rx_buffer(rx_ring, size); - /* retrieve a buffer from the ring */ - if (!skb) { + neop = i40e_is_non_eop(rx_ring, rx_desc); + i40e_inc_ntp(rx_ring); + + if (!xdp->data) { unsigned char *hard_start; hard_start = page_address(rx_buffer->page) + rx_buffer->page_offset - offset; xdp_prepare_buff(xdp, hard_start, offset, size, true); - xdp_buff_clear_frags_flag(xdp); #if (PAGE_SIZE > 4096) /* At larger PAGE_SIZE, frame_sz depend on len size */ xdp->frame_sz = i40e_rx_frame_truesize(rx_ring, size); #endif - xdp_res = i40e_run_xdp(rx_ring, xdp, xdp_prog); + } else if (i40e_add_xdp_frag(xdp, &nfrags, rx_buffer, size) && + !neop) { + /* Overflowing packet: Drop all frags on EOP */ + i40e_consume_xdp_buff(rx_ring, xdp, rx_buffer); + break; } + if (neop) + continue; + + xdp_res = i40e_run_xdp(rx_ring, xdp, xdp_prog); + if (xdp_res) { - if (xdp_res & (I40E_XDP_TX | I40E_XDP_REDIR)) { - xdp_xmit |= xdp_res; + xdp_xmit |= xdp_res & (I40E_XDP_TX | I40E_XDP_REDIR); + + if (unlikely(xdp_buff_has_frags(xdp))) { + i40e_process_rx_buffs(rx_ring, xdp_res, xdp); + size = xdp_get_buff_len(xdp); + } else if (xdp_res & (I40E_XDP_TX | I40E_XDP_REDIR)) { i40e_rx_buffer_flip(rx_buffer, xdp->frame_sz); } else { rx_buffer->pagecnt_bias++; } total_rx_bytes += size; - total_rx_packets++; - } else if (skb) { - i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); - } else if (ring_uses_build_skb(rx_ring)) { - skb = i40e_build_skb(rx_ring, rx_buffer, xdp); } else { - skb = i40e_construct_skb(rx_ring, rx_buffer, xdp); - } + if (ring_uses_build_skb(rx_ring)) + skb = i40e_build_skb(rx_ring, xdp, nfrags); + else + skb = i40e_construct_skb(rx_ring, xdp, nfrags); + + /* drop if we failed to retrieve a buffer */ + if (!skb) { + rx_ring->rx_stats.alloc_buff_failed++; + i40e_consume_xdp_buff(rx_ring, xdp, rx_buffer); + break; + } - /* exit if we failed to retrieve a buffer */ - if (!xdp_res && !skb) { - rx_ring->rx_stats.alloc_buff_failed++; - rx_buffer->pagecnt_bias++; - break; - } + if (i40e_cleanup_headers(rx_ring, skb, rx_desc)) + goto process_next; - i40e_put_rx_buffer(rx_ring, rx_buffer); - cleaned_count++; + /* probably a little skewed due to removing CRC */ + total_rx_bytes += skb->len; - i40e_inc_ntp(rx_ring); - rx_ring->next_to_clean = rx_ring->next_to_process; - if (i40e_is_non_eop(rx_ring, rx_desc)) - continue; + /* populate checksum, VLAN, and protocol */ + i40e_process_skb_fields(rx_ring, rx_desc, skb); - if (xdp_res || i40e_cleanup_headers(rx_ring, skb, rx_desc)) { - skb = NULL; - continue; + i40e_trace(clean_rx_irq_rx, rx_ring, rx_desc, xdp); + napi_gro_receive(&rx_ring->q_vector->napi, skb); } - /* probably a little skewed due to removing CRC */ - total_rx_bytes += skb->len; - - /* populate checksum, VLAN, and protocol */ - i40e_process_skb_fields(rx_ring, rx_desc, skb); - - i40e_trace(clean_rx_irq_rx, rx_ring, rx_desc, xdp); - napi_gro_receive(&rx_ring->q_vector->napi, skb); - skb = NULL; - /* update budget accounting */ total_rx_packets++; +process_next: + cleaned_count += nfrags + 1; + i40e_put_rx_buffer(rx_ring, rx_buffer); + rx_ring->next_to_clean = rx_ring->next_to_process; + + xdp->data = NULL; } i40e_finalize_xdp_rx(rx_ring, xdp_xmit); - rx_ring->skb = skb; i40e_update_rx_stats(rx_ring, total_rx_bytes, total_rx_packets); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 55da9fde9d17..8c3d24012c54 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -392,14 +392,6 @@ struct i40e_ring { struct rcu_head rcu; /* to avoid race on free */ u16 next_to_alloc; - struct sk_buff *skb; /* When i40e_clean_rx_ring_irq() must - * return before it sees the EOP for - * the current packet, we save that skb - * here and resume receiving this - * packet the next time - * i40e_clean_rx_ring_irq() is called - * for this ring. - */ struct i40e_channel *ch; u16 rx_offset; -- cgit v1.2.3 From 513bdd9473888ba4b1441923c2711cb5ecd06cfd Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 8 Mar 2023 21:19:55 +0100 Subject: net: phy: smsc: use phy_set_bits in smsc_phy_config_init Simplify the code by using phy_set_bits(). Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/b64d9f86-d029-b911-bbe9-6ca6889399d7@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/smsc.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index feb6ac3939ac..ac951d67edcc 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -99,20 +99,13 @@ static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev) static int smsc_phy_config_init(struct phy_device *phydev) { struct smsc_phy_priv *priv = phydev->priv; - int rc; if (!priv->energy_enable || phydev->irq != PHY_POLL) return 0; - rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); - - if (rc < 0) - return rc; - - /* Enable energy detect mode for this SMSC Transceivers */ - rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, - rc | MII_LAN83C185_EDPWRDOWN); - return rc; + /* Enable energy detect power down mode */ + return phy_set_bits(phydev, MII_LAN83C185_CTRL_STATUS, + MII_LAN83C185_EDPWRDOWN); } static int smsc_phy_reset(struct phy_device *phydev) -- cgit v1.2.3 From b3a8df9f27c094dd41fe88b7b97f64d8dcb7d696 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 8 Mar 2023 14:04:52 +0100 Subject: net: ethernet: ti: am65-cpsw: Convert to devm_of_phy_optional_get() Use the new devm_of_phy_optional_get() helper instead of open-coding the same operation. Signed-off-by: Geert Uytterhoeven Reviewed-by: Siddharth Vadapalli Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/01605ea233ff7fc09bb0ea34fc8126af73db83f9.1678280599.git.geert+renesas@glider.be Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 4e3861c47708..25996826edab 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1470,11 +1470,9 @@ static int am65_cpsw_init_serdes_phy(struct device *dev, struct device_node *por struct phy *phy; int ret; - phy = devm_of_phy_get(dev, port_np, name); - if (PTR_ERR(phy) == -ENODEV) - return 0; - if (IS_ERR(phy)) - return PTR_ERR(phy); + phy = devm_of_phy_optional_get(dev, port_np, name); + if (IS_ERR_OR_NULL(phy)) + return PTR_ERR_OR_ZERO(phy); /* Serdes PHY exists. Store it. */ port->slave.serdes_phy = phy; -- cgit v1.2.3 From a1331535aeb41b08fe0c2c78af51885edc93615b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Mar 2023 16:10:51 -0600 Subject: net: restore alpha order to Ethernet devices in config The filename "wangxun" sorts between "intel" and "xscale", but xscale/Kconfig contains "Intel XScale" prompts, so Wangxun ends up in the wrong place in the config front-ends. Move wangxun/Kconfig so the Wangxun devices appear in order in the user interface. Fixes: 3ce7547e5b71 ("net: txgbe: Add build support for txgbe") Signed-off-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20230307221051.890135-1-helgaas@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 1917da784191..5a274b99f299 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -84,7 +84,6 @@ source "drivers/net/ethernet/huawei/Kconfig" source "drivers/net/ethernet/i825xx/Kconfig" source "drivers/net/ethernet/ibm/Kconfig" source "drivers/net/ethernet/intel/Kconfig" -source "drivers/net/ethernet/wangxun/Kconfig" source "drivers/net/ethernet/xscale/Kconfig" config JME @@ -189,6 +188,7 @@ source "drivers/net/ethernet/toshiba/Kconfig" source "drivers/net/ethernet/tundra/Kconfig" source "drivers/net/ethernet/vertexcom/Kconfig" source "drivers/net/ethernet/via/Kconfig" +source "drivers/net/ethernet/wangxun/Kconfig" source "drivers/net/ethernet/wiznet/Kconfig" source "drivers/net/ethernet/xilinx/Kconfig" source "drivers/net/ethernet/xircom/Kconfig" -- cgit v1.2.3 From cc4342f60f1a6d0f4a30ae1887a75834d0109444 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 7 Mar 2023 20:29:27 +0100 Subject: net: mvpp2: Defer probe if MAC address source is not yet ready NVMEM layouts are no longer registered early, and thus may not yet be available when Ethernet drivers (or any other consumer) probe, leading to possible probe deferrals errors. Forward the error code if this happens. All other errors being discarded, the driver will eventually use a random MAC address if no other source was considered valid (no functional change on this regard). Signed-off-by: Miquel Raynal Reviewed-by: Marcin Wojtas Link: https://lore.kernel.org/r/20230307192927.512757-1-miquel.raynal@bootlin.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 9b4ecbe4f36d..e7c7652ffac5 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -6081,18 +6081,19 @@ static bool mvpp2_port_has_irqs(struct mvpp2 *priv, return true; } -static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, - struct fwnode_handle *fwnode, - char **mac_from) +static int mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, + struct fwnode_handle *fwnode, + char **mac_from) { struct mvpp2_port *port = netdev_priv(dev); char hw_mac_addr[ETH_ALEN] = {0}; char fw_mac_addr[ETH_ALEN]; + int ret; if (!fwnode_get_mac_address(fwnode, fw_mac_addr)) { *mac_from = "firmware node"; eth_hw_addr_set(dev, fw_mac_addr); - return; + return 0; } if (priv->hw_version == MVPP21) { @@ -6100,19 +6101,24 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, if (is_valid_ether_addr(hw_mac_addr)) { *mac_from = "hardware"; eth_hw_addr_set(dev, hw_mac_addr); - return; + return 0; } } /* Only valid on OF enabled platforms */ - if (!of_get_mac_address_nvmem(to_of_node(fwnode), fw_mac_addr)) { + ret = of_get_mac_address_nvmem(to_of_node(fwnode), fw_mac_addr); + if (ret == -EPROBE_DEFER) + return ret; + if (!ret) { *mac_from = "nvmem cell"; eth_hw_addr_set(dev, fw_mac_addr); - return; + return 0; } *mac_from = "random"; eth_hw_addr_random(dev); + + return 0; } static struct mvpp2_port *mvpp2_phylink_to_port(struct phylink_config *config) @@ -6815,7 +6821,9 @@ static int mvpp2_port_probe(struct platform_device *pdev, mutex_init(&port->gather_stats_lock); INIT_DELAYED_WORK(&port->stats_work, mvpp2_gather_hw_statistics); - mvpp2_port_copy_mac_addr(dev, priv, port_fwnode, &mac_from); + err = mvpp2_port_copy_mac_addr(dev, priv, port_fwnode, &mac_from); + if (err < 0) + goto err_free_stats; port->tx_ring_size = MVPP2_MAX_TXD_DFLT; port->rx_ring_size = MVPP2_MAX_RXD_DFLT; -- cgit v1.2.3 From 99ce286d2d30a31eba4171036bc3f32eeb59e5f3 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 7 Mar 2023 23:09:25 +0100 Subject: net: lan966x: Add IS1 VCAP model Provide IS1 (ingress stage 1) VCAP model for lan966x. This provides classification actions for lan966x. Signed-off-by: Horatiu Vultur Signed-off-by: Jakub Kicinski --- .../microchip/lan966x/lan966x_vcap_ag_api.c | 1402 +++++++++++++++++++- drivers/net/ethernet/microchip/vcap/vcap_ag_api.h | 217 ++- .../microchip/vcap/vcap_api_debugfs_kunit.c | 4 +- 3 files changed, 1563 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c index 928e711960e6..66400a082d02 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c @@ -6,6 +6,965 @@ #include "lan966x_vcap_ag_api.h" /* keyfields */ +static const struct vcap_field is1_normal_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_LOOKUP_INDEX] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 9, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 17, + .width = 1, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 19, + .width = 12, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 31, + .width = 1, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 32, + .width = 3, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 35, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 100, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 101, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 102, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 103, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 104, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 105, + .width = 6, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 32, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 143, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 144, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 145, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 161, + .width = 8, + }, +}; + +static const struct vcap_field is1_5tuple_ip4_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_LOOKUP_INDEX] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 9, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 17, + .width = 1, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 19, + .width = 12, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 31, + .width = 1, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 32, + .width = 3, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_BIT, + .offset = 35, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 36, + .width = 12, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 48, + .width = 1, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 49, + .width = 3, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 6, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 62, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 94, + .width = 32, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 126, + .width = 8, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 134, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 135, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 8, + }, + [VCAP_KF_IP_PAYLOAD_5TUPLE] = { + .type = VCAP_FIELD_U32, + .offset = 144, + .width = 32, + }, +}; + +static const struct vcap_field is1_normal_ip6_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_INDEX] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 9, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 17, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 12, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 32, + .width = 1, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 33, + .width = 3, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_BIT, + .offset = 36, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 12, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 49, + .width = 1, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 50, + .width = 3, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 53, + .width = 48, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 101, + .width = 6, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 107, + .width = 128, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 235, + .width = 8, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 243, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 244, + .width = 8, + }, + [VCAP_KF_IP_PAYLOAD_S1_IP6] = { + .type = VCAP_FIELD_U112, + .offset = 252, + .width = 112, + }, +}; + +static const struct vcap_field is1_7tuple_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_INDEX] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 9, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 17, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 12, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 32, + .width = 1, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 33, + .width = 3, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_BIT, + .offset = 36, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 12, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 49, + .width = 1, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 50, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 53, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 101, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 149, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 150, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 166, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 167, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 169, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 170, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 171, + .width = 6, + }, + [VCAP_KF_L3_IP6_DIP_MSB] = { + .type = VCAP_FIELD_U32, + .offset = 177, + .width = 16, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U64, + .offset = 193, + .width = 64, + }, + [VCAP_KF_L3_IP6_SIP_MSB] = { + .type = VCAP_FIELD_U32, + .offset = 257, + .width = 16, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U64, + .offset = 273, + .width = 64, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 337, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 338, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 339, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 355, + .width = 8, + }, +}; + +static const struct vcap_field is1_5tuple_ip6_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_INDEX] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 9, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 17, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 12, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 32, + .width = 1, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 33, + .width = 3, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_BIT, + .offset = 36, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 12, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 49, + .width = 1, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 50, + .width = 3, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 53, + .width = 6, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 59, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 187, + .width = 128, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 315, + .width = 8, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 323, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 324, + .width = 8, + }, + [VCAP_KF_IP_PAYLOAD_5TUPLE] = { + .type = VCAP_FIELD_U32, + .offset = 332, + .width = 32, + }, +}; + +static const struct vcap_field is1_dbl_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_INDEX] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 9, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 17, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 12, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 32, + .width = 1, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 33, + .width = 3, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_BIT, + .offset = 36, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 12, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 49, + .width = 1, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 50, + .width = 3, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 54, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 70, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 71, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 72, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 73, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 74, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 75, + .width = 6, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 82, + .width = 1, + }, +}; + +static const struct vcap_field is1_rt_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 3, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 6, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_KF_L2_MAC] = { + .type = VCAP_FIELD_U48, + .offset = 9, + .width = 48, + }, + [VCAP_KF_RT_VLAN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 57, + .width = 3, + }, + [VCAP_KF_RT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 60, + .width = 2, + }, + [VCAP_KF_RT_FRMID] = { + .type = VCAP_FIELD_U32, + .offset = 62, + .width = 32, + }, +}; + +static const struct vcap_field is1_dmac_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_INDEX] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 9, + }, + [VCAP_KF_8021CB_R_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 12, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 29, + .width = 1, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 30, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 33, + .width = 48, + }, +}; + static const struct vcap_field is2_mac_etype_keyfield[] = { [VCAP_KF_TYPE] = { .type = VCAP_FIELD_U32, @@ -1163,6 +2122,49 @@ static const struct vcap_field is2_smac_sip6_keyfield[] = { }; /* keyfield_set */ +static const struct vcap_set is1_keyfield_set[] = { + [VCAP_KFS_NORMAL] = { + .type_id = 0, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_5TUPLE_IP4] = { + .type_id = 1, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_NORMAL_IP6] = { + .type_id = 0, + .sw_per_item = 4, + .sw_cnt = 1, + }, + [VCAP_KFS_7TUPLE] = { + .type_id = 1, + .sw_per_item = 4, + .sw_cnt = 1, + }, + [VCAP_KFS_5TUPLE_IP6] = { + .type_id = 2, + .sw_per_item = 4, + .sw_cnt = 1, + }, + [VCAP_KFS_DBL_VID] = { + .type_id = 0, + .sw_per_item = 1, + .sw_cnt = 4, + }, + [VCAP_KFS_RT] = { + .type_id = 1, + .sw_per_item = 1, + .sw_cnt = 4, + }, + [VCAP_KFS_DMAC_VID] = { + .type_id = 2, + .sw_per_item = 1, + .sw_cnt = 4, + }, +}; + static const struct vcap_set is2_keyfield_set[] = { [VCAP_KFS_MAC_ETYPE] = { .type_id = 0, @@ -1227,6 +2229,17 @@ static const struct vcap_set is2_keyfield_set[] = { }; /* keyfield_set map */ +static const struct vcap_field *is1_keyfield_set_map[] = { + [VCAP_KFS_NORMAL] = is1_normal_keyfield, + [VCAP_KFS_5TUPLE_IP4] = is1_5tuple_ip4_keyfield, + [VCAP_KFS_NORMAL_IP6] = is1_normal_ip6_keyfield, + [VCAP_KFS_7TUPLE] = is1_7tuple_keyfield, + [VCAP_KFS_5TUPLE_IP6] = is1_5tuple_ip6_keyfield, + [VCAP_KFS_DBL_VID] = is1_dbl_vid_keyfield, + [VCAP_KFS_RT] = is1_rt_keyfield, + [VCAP_KFS_DMAC_VID] = is1_dmac_vid_keyfield, +}; + static const struct vcap_field *is2_keyfield_set_map[] = { [VCAP_KFS_MAC_ETYPE] = is2_mac_etype_keyfield, [VCAP_KFS_MAC_LLC] = is2_mac_llc_keyfield, @@ -1243,6 +2256,17 @@ static const struct vcap_field *is2_keyfield_set_map[] = { }; /* keyfield_set map sizes */ +static int is1_keyfield_set_map_size[] = { + [VCAP_KFS_NORMAL] = ARRAY_SIZE(is1_normal_keyfield), + [VCAP_KFS_5TUPLE_IP4] = ARRAY_SIZE(is1_5tuple_ip4_keyfield), + [VCAP_KFS_NORMAL_IP6] = ARRAY_SIZE(is1_normal_ip6_keyfield), + [VCAP_KFS_7TUPLE] = ARRAY_SIZE(is1_7tuple_keyfield), + [VCAP_KFS_5TUPLE_IP6] = ARRAY_SIZE(is1_5tuple_ip6_keyfield), + [VCAP_KFS_DBL_VID] = ARRAY_SIZE(is1_dbl_vid_keyfield), + [VCAP_KFS_RT] = ARRAY_SIZE(is1_rt_keyfield), + [VCAP_KFS_DMAC_VID] = ARRAY_SIZE(is1_dmac_vid_keyfield), +}; + static int is2_keyfield_set_map_size[] = { [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(is2_mac_etype_keyfield), [VCAP_KFS_MAC_LLC] = ARRAY_SIZE(is2_mac_llc_keyfield), @@ -1259,6 +2283,154 @@ static int is2_keyfield_set_map_size[] = { }; /* actionfields */ +static const struct vcap_field is1_s1_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_DSCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_DSCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 6, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 9, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_AF_PAG_OVERRIDE_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 8, + }, + [VCAP_AF_PAG_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 22, + .width = 8, + }, + [VCAP_AF_ISDX_REPLACE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 30, + .width = 1, + }, + [VCAP_AF_ISDX_ADD_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 31, + .width = 8, + }, + [VCAP_AF_VID_REPLACE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 12, + }, + [VCAP_AF_PCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 67, + .width = 1, + }, + [VCAP_AF_PCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 3, + }, + [VCAP_AF_DEI_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 71, + .width = 1, + }, + [VCAP_AF_DEI_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 72, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 73, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 74, + .width = 2, + }, + [VCAP_AF_CUSTOM_ACE_TYPE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 76, + .width = 4, + }, + [VCAP_AF_SFID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 80, + .width = 1, + }, + [VCAP_AF_SFID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 81, + .width = 8, + }, + [VCAP_AF_SGID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_AF_SGID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 90, + .width = 8, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 98, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 9, + }, + [VCAP_AF_OAM_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 108, + .width = 3, + }, + [VCAP_AF_MRP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 2, + }, + [VCAP_AF_DLR_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 113, + .width = 2, + }, +}; + static const struct vcap_field is2_base_type_actionfield[] = { [VCAP_AF_HIT_ME_ONCE] = { .type = VCAP_FIELD_BIT, @@ -1351,6 +2523,14 @@ static const struct vcap_field is2_smac_sip_actionfield[] = { }; /* actionfield_set */ +static const struct vcap_set is1_actionfield_set[] = { + [VCAP_AFS_S1] = { + .type_id = 0, + .sw_per_item = 1, + .sw_cnt = 4, + }, +}; + static const struct vcap_set is2_actionfield_set[] = { [VCAP_AFS_BASE_TYPE] = { .type_id = -1, @@ -1365,18 +2545,73 @@ static const struct vcap_set is2_actionfield_set[] = { }; /* actionfield_set map */ +static const struct vcap_field *is1_actionfield_set_map[] = { + [VCAP_AFS_S1] = is1_s1_actionfield, +}; + static const struct vcap_field *is2_actionfield_set_map[] = { [VCAP_AFS_BASE_TYPE] = is2_base_type_actionfield, [VCAP_AFS_SMAC_SIP] = is2_smac_sip_actionfield, }; /* actionfield_set map size */ +static int is1_actionfield_set_map_size[] = { + [VCAP_AFS_S1] = ARRAY_SIZE(is1_s1_actionfield), +}; + static int is2_actionfield_set_map_size[] = { [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(is2_base_type_actionfield), [VCAP_AFS_SMAC_SIP] = ARRAY_SIZE(is2_smac_sip_actionfield), }; /* Type Groups */ +static const struct vcap_typegroup is1_x4_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 96, + .width = 1, + .value = 0, + }, + { + .offset = 192, + .width = 2, + .value = 0, + }, + { + .offset = 288, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is1_x2_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 96, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is1_x1_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + static const struct vcap_typegroup is2_x4_keyfield_set_typegroups[] = { { .offset = 0, @@ -1424,6 +2659,13 @@ static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { {} }; +static const struct vcap_typegroup *is1_keyfield_set_typegroups[] = { + [4] = is1_x4_keyfield_set_typegroups, + [2] = is1_x2_keyfield_set_typegroups, + [1] = is1_x1_keyfield_set_typegroups, + [5] = NULL, +}; + static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { [4] = is2_x4_keyfield_set_typegroups, [2] = is2_x2_keyfield_set_typegroups, @@ -1431,6 +2673,10 @@ static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { [5] = NULL, }; +static const struct vcap_typegroup is1_x1_actionfield_set_typegroups[] = { + {} +}; + static const struct vcap_typegroup is2_x2_actionfield_set_typegroups[] = { { .offset = 0, @@ -1454,6 +2700,11 @@ static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { {} }; +static const struct vcap_typegroup *is1_actionfield_set_typegroups[] = { + [1] = is1_x1_actionfield_set_typegroups, + [5] = NULL, +}; + static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { [2] = is2_x2_actionfield_set_typegroups, [1] = is2_x1_actionfield_set_typegroups, @@ -1463,16 +2714,33 @@ static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { /* Keyfieldset names */ static const char * const vcap_keyfield_set_names[] = { [VCAP_KFS_NO_VALUE] = "(None)", + [VCAP_KFS_5TUPLE_IP4] = "VCAP_KFS_5TUPLE_IP4", + [VCAP_KFS_5TUPLE_IP6] = "VCAP_KFS_5TUPLE_IP6", + [VCAP_KFS_7TUPLE] = "VCAP_KFS_7TUPLE", [VCAP_KFS_ARP] = "VCAP_KFS_ARP", + [VCAP_KFS_DBL_VID] = "VCAP_KFS_DBL_VID", + [VCAP_KFS_DMAC_VID] = "VCAP_KFS_DMAC_VID", + [VCAP_KFS_ETAG] = "VCAP_KFS_ETAG", [VCAP_KFS_IP4_OTHER] = "VCAP_KFS_IP4_OTHER", [VCAP_KFS_IP4_TCP_UDP] = "VCAP_KFS_IP4_TCP_UDP", + [VCAP_KFS_IP4_VID] = "VCAP_KFS_IP4_VID", [VCAP_KFS_IP6_OTHER] = "VCAP_KFS_IP6_OTHER", [VCAP_KFS_IP6_STD] = "VCAP_KFS_IP6_STD", [VCAP_KFS_IP6_TCP_UDP] = "VCAP_KFS_IP6_TCP_UDP", + [VCAP_KFS_IP6_VID] = "VCAP_KFS_IP6_VID", + [VCAP_KFS_IP_7TUPLE] = "VCAP_KFS_IP_7TUPLE", + [VCAP_KFS_ISDX] = "VCAP_KFS_ISDX", + [VCAP_KFS_LL_FULL] = "VCAP_KFS_LL_FULL", [VCAP_KFS_MAC_ETYPE] = "VCAP_KFS_MAC_ETYPE", [VCAP_KFS_MAC_LLC] = "VCAP_KFS_MAC_LLC", [VCAP_KFS_MAC_SNAP] = "VCAP_KFS_MAC_SNAP", + [VCAP_KFS_NORMAL] = "VCAP_KFS_NORMAL", + [VCAP_KFS_NORMAL_5TUPLE_IP4] = "VCAP_KFS_NORMAL_5TUPLE_IP4", + [VCAP_KFS_NORMAL_7TUPLE] = "VCAP_KFS_NORMAL_7TUPLE", + [VCAP_KFS_NORMAL_IP6] = "VCAP_KFS_NORMAL_IP6", [VCAP_KFS_OAM] = "VCAP_KFS_OAM", + [VCAP_KFS_PURE_5TUPLE_IP4] = "VCAP_KFS_PURE_5TUPLE_IP4", + [VCAP_KFS_RT] = "VCAP_KFS_RT", [VCAP_KFS_SMAC_SIP4] = "VCAP_KFS_SMAC_SIP4", [VCAP_KFS_SMAC_SIP6] = "VCAP_KFS_SMAC_SIP6", }; @@ -1481,16 +2749,42 @@ static const char * const vcap_keyfield_set_names[] = { static const char * const vcap_actionfield_set_names[] = { [VCAP_AFS_NO_VALUE] = "(None)", [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", + [VCAP_AFS_CLASSIFICATION] = "VCAP_AFS_CLASSIFICATION", + [VCAP_AFS_CLASS_REDUCED] = "VCAP_AFS_CLASS_REDUCED", + [VCAP_AFS_FULL] = "VCAP_AFS_FULL", + [VCAP_AFS_S1] = "VCAP_AFS_S1", [VCAP_AFS_SMAC_SIP] = "VCAP_AFS_SMAC_SIP", }; /* Keyfield names */ static const char * const vcap_keyfield_names[] = { [VCAP_KF_NO_VALUE] = "(None)", + [VCAP_KF_8021BR_ECID_BASE] = "8021BR_ECID_BASE", + [VCAP_KF_8021BR_ECID_EXT] = "8021BR_ECID_EXT", + [VCAP_KF_8021BR_E_TAGGED] = "8021BR_E_TAGGED", + [VCAP_KF_8021BR_GRP] = "8021BR_GRP", + [VCAP_KF_8021BR_IGR_ECID_BASE] = "8021BR_IGR_ECID_BASE", + [VCAP_KF_8021BR_IGR_ECID_EXT] = "8021BR_IGR_ECID_EXT", + [VCAP_KF_8021CB_R_TAGGED_IS] = "8021CB_R_TAGGED_IS", + [VCAP_KF_8021Q_DEI0] = "8021Q_DEI0", + [VCAP_KF_8021Q_DEI1] = "8021Q_DEI1", + [VCAP_KF_8021Q_DEI2] = "8021Q_DEI2", [VCAP_KF_8021Q_DEI_CLS] = "8021Q_DEI_CLS", + [VCAP_KF_8021Q_PCP0] = "8021Q_PCP0", + [VCAP_KF_8021Q_PCP1] = "8021Q_PCP1", + [VCAP_KF_8021Q_PCP2] = "8021Q_PCP2", [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_TPID0] = "8021Q_TPID0", + [VCAP_KF_8021Q_TPID1] = "8021Q_TPID1", + [VCAP_KF_8021Q_TPID2] = "8021Q_TPID2", + [VCAP_KF_8021Q_VID0] = "8021Q_VID0", + [VCAP_KF_8021Q_VID1] = "8021Q_VID1", + [VCAP_KF_8021Q_VID2] = "8021Q_VID2", [VCAP_KF_8021Q_VID_CLS] = "8021Q_VID_CLS", + [VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS] = "8021Q_VLAN_DBL_TAGGED_IS", [VCAP_KF_8021Q_VLAN_TAGGED_IS] = "8021Q_VLAN_TAGGED_IS", + [VCAP_KF_8021Q_VLAN_TAGS] = "8021Q_VLAN_TAGS", + [VCAP_KF_ACL_GRP_ID] = "ACL_GRP_ID", [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = "ARP_ADDR_SPACE_OK_IS", [VCAP_KF_ARP_LEN_OK_IS] = "ARP_LEN_OK_IS", [VCAP_KF_ARP_OPCODE] = "ARP_OPCODE", @@ -1498,32 +2792,57 @@ static const char * const vcap_keyfield_names[] = { [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = "ARP_PROTO_SPACE_OK_IS", [VCAP_KF_ARP_SENDER_MATCH_IS] = "ARP_SENDER_MATCH_IS", [VCAP_KF_ARP_TGT_MATCH_IS] = "ARP_TGT_MATCH_IS", + [VCAP_KF_COSID_CLS] = "COSID_CLS", + [VCAP_KF_ES0_ISDX_KEY_ENA] = "ES0_ISDX_KEY_ENA", [VCAP_KF_ETYPE] = "ETYPE", + [VCAP_KF_ETYPE_LEN_IS] = "ETYPE_LEN_IS", [VCAP_KF_HOST_MATCH] = "HOST_MATCH", + [VCAP_KF_IF_EGR_PORT_MASK] = "IF_EGR_PORT_MASK", + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = "IF_EGR_PORT_MASK_RNG", [VCAP_KF_IF_IGR_PORT] = "IF_IGR_PORT", [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", + [VCAP_KF_IF_IGR_PORT_MASK_L3] = "IF_IGR_PORT_MASK_L3", + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = "IF_IGR_PORT_MASK_RNG", + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = "IF_IGR_PORT_MASK_SEL", + [VCAP_KF_IF_IGR_PORT_SEL] = "IF_IGR_PORT_SEL", [VCAP_KF_IP4_IS] = "IP4_IS", + [VCAP_KF_IP_MC_IS] = "IP_MC_IS", + [VCAP_KF_IP_PAYLOAD_5TUPLE] = "IP_PAYLOAD_5TUPLE", + [VCAP_KF_IP_PAYLOAD_S1_IP6] = "IP_PAYLOAD_S1_IP6", + [VCAP_KF_IP_SNAP_IS] = "IP_SNAP_IS", + [VCAP_KF_ISDX_CLS] = "ISDX_CLS", [VCAP_KF_ISDX_GT0_IS] = "ISDX_GT0_IS", [VCAP_KF_L2_BC_IS] = "L2_BC_IS", [VCAP_KF_L2_DMAC] = "L2_DMAC", [VCAP_KF_L2_FRM_TYPE] = "L2_FRM_TYPE", + [VCAP_KF_L2_FWD_IS] = "L2_FWD_IS", [VCAP_KF_L2_LLC] = "L2_LLC", + [VCAP_KF_L2_MAC] = "L2_MAC", [VCAP_KF_L2_MC_IS] = "L2_MC_IS", [VCAP_KF_L2_PAYLOAD0] = "L2_PAYLOAD0", [VCAP_KF_L2_PAYLOAD1] = "L2_PAYLOAD1", [VCAP_KF_L2_PAYLOAD2] = "L2_PAYLOAD2", + [VCAP_KF_L2_PAYLOAD_ETYPE] = "L2_PAYLOAD_ETYPE", [VCAP_KF_L2_SMAC] = "L2_SMAC", [VCAP_KF_L2_SNAP] = "L2_SNAP", [VCAP_KF_L3_DIP_EQ_SIP_IS] = "L3_DIP_EQ_SIP_IS", + [VCAP_KF_L3_DPL_CLS] = "L3_DPL_CLS", + [VCAP_KF_L3_DSCP] = "L3_DSCP", + [VCAP_KF_L3_DST_IS] = "L3_DST_IS", [VCAP_KF_L3_FRAGMENT] = "L3_FRAGMENT", + [VCAP_KF_L3_FRAGMENT_TYPE] = "L3_FRAGMENT_TYPE", + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = "L3_FRAG_INVLD_L4_LEN", [VCAP_KF_L3_FRAG_OFS_GT0] = "L3_FRAG_OFS_GT0", [VCAP_KF_L3_IP4_DIP] = "L3_IP4_DIP", [VCAP_KF_L3_IP4_SIP] = "L3_IP4_SIP", [VCAP_KF_L3_IP6_DIP] = "L3_IP6_DIP", + [VCAP_KF_L3_IP6_DIP_MSB] = "L3_IP6_DIP_MSB", [VCAP_KF_L3_IP6_SIP] = "L3_IP6_SIP", + [VCAP_KF_L3_IP6_SIP_MSB] = "L3_IP6_SIP_MSB", [VCAP_KF_L3_IP_PROTO] = "L3_IP_PROTO", [VCAP_KF_L3_OPTIONS_IS] = "L3_OPTIONS_IS", [VCAP_KF_L3_PAYLOAD] = "L3_PAYLOAD", + [VCAP_KF_L3_RT_IS] = "L3_RT_IS", [VCAP_KF_L3_TOS] = "L3_TOS", [VCAP_KF_L3_TTL_GT0] = "L3_TTL_GT0", [VCAP_KF_L4_1588_DOM] = "L4_1588_DOM", @@ -1531,6 +2850,7 @@ static const char * const vcap_keyfield_names[] = { [VCAP_KF_L4_ACK] = "L4_ACK", [VCAP_KF_L4_DPORT] = "L4_DPORT", [VCAP_KF_L4_FIN] = "L4_FIN", + [VCAP_KF_L4_PAYLOAD] = "L4_PAYLOAD", [VCAP_KF_L4_PSH] = "L4_PSH", [VCAP_KF_L4_RNG] = "L4_RNG", [VCAP_KF_L4_RST] = "L4_RST", @@ -1540,7 +2860,11 @@ static const char * const vcap_keyfield_names[] = { [VCAP_KF_L4_SYN] = "L4_SYN", [VCAP_KF_L4_URG] = "L4_URG", [VCAP_KF_LOOKUP_FIRST_IS] = "LOOKUP_FIRST_IS", + [VCAP_KF_LOOKUP_GEN_IDX] = "LOOKUP_GEN_IDX", + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = "LOOKUP_GEN_IDX_SEL", + [VCAP_KF_LOOKUP_INDEX] = "LOOKUP_INDEX", [VCAP_KF_LOOKUP_PAG] = "LOOKUP_PAG", + [VCAP_KF_MIRROR_PROBE] = "MIRROR_PROBE", [VCAP_KF_OAM_CCM_CNTS_EQ0] = "OAM_CCM_CNTS_EQ0", [VCAP_KF_OAM_DETECTED] = "OAM_DETECTED", [VCAP_KF_OAM_FLAGS] = "OAM_FLAGS", @@ -1549,7 +2873,12 @@ static const char * const vcap_keyfield_names[] = { [VCAP_KF_OAM_OPCODE] = "OAM_OPCODE", [VCAP_KF_OAM_VER] = "OAM_VER", [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_PROT_ACTIVE] = "PROT_ACTIVE", + [VCAP_KF_RT_FRMID] = "RT_FRMID", + [VCAP_KF_RT_TYPE] = "RT_TYPE", + [VCAP_KF_RT_VLAN_IDX] = "RT_VLAN_IDX", [VCAP_KF_TCP_IS] = "TCP_IS", + [VCAP_KF_TCP_UDP_IS] = "TCP_UDP_IS", [VCAP_KF_TYPE] = "TYPE", }; @@ -1557,24 +2886,95 @@ static const char * const vcap_keyfield_names[] = { static const char * const vcap_actionfield_names[] = { [VCAP_AF_NO_VALUE] = "(None)", [VCAP_AF_ACL_ID] = "ACL_ID", + [VCAP_AF_CLS_VID_SEL] = "CLS_VID_SEL", + [VCAP_AF_CNT_ID] = "CNT_ID", + [VCAP_AF_COPY_PORT_NUM] = "COPY_PORT_NUM", + [VCAP_AF_COPY_QUEUE_NUM] = "COPY_QUEUE_NUM", [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", + [VCAP_AF_CUSTOM_ACE_TYPE_ENA] = "CUSTOM_ACE_TYPE_ENA", + [VCAP_AF_DEI_ENA] = "DEI_ENA", + [VCAP_AF_DEI_VAL] = "DEI_VAL", + [VCAP_AF_DLR_SEL] = "DLR_SEL", + [VCAP_AF_DP_ENA] = "DP_ENA", + [VCAP_AF_DP_VAL] = "DP_VAL", + [VCAP_AF_DSCP_ENA] = "DSCP_ENA", + [VCAP_AF_DSCP_VAL] = "DSCP_VAL", + [VCAP_AF_ES2_REW_CMD] = "ES2_REW_CMD", [VCAP_AF_FWD_KILL_ENA] = "FWD_KILL_ENA", + [VCAP_AF_FWD_MODE] = "FWD_MODE", [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", [VCAP_AF_HOST_MATCH] = "HOST_MATCH", + [VCAP_AF_IGNORE_PIPELINE_CTRL] = "IGNORE_PIPELINE_CTRL", + [VCAP_AF_INTR_ENA] = "INTR_ENA", + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = "ISDX_ADD_REPLACE_SEL", + [VCAP_AF_ISDX_ADD_VAL] = "ISDX_ADD_VAL", [VCAP_AF_ISDX_ENA] = "ISDX_ENA", + [VCAP_AF_ISDX_REPLACE_ENA] = "ISDX_REPLACE_ENA", + [VCAP_AF_ISDX_VAL] = "ISDX_VAL", [VCAP_AF_LRN_DIS] = "LRN_DIS", + [VCAP_AF_MAP_IDX] = "MAP_IDX", + [VCAP_AF_MAP_KEY] = "MAP_KEY", + [VCAP_AF_MAP_LOOKUP_SEL] = "MAP_LOOKUP_SEL", [VCAP_AF_MASK_MODE] = "MASK_MODE", + [VCAP_AF_MATCH_ID] = "MATCH_ID", + [VCAP_AF_MATCH_ID_MASK] = "MATCH_ID_MASK", [VCAP_AF_MIRROR_ENA] = "MIRROR_ENA", + [VCAP_AF_MIRROR_PROBE] = "MIRROR_PROBE", + [VCAP_AF_MIRROR_PROBE_ID] = "MIRROR_PROBE_ID", + [VCAP_AF_MRP_SEL] = "MRP_SEL", + [VCAP_AF_NXT_IDX] = "NXT_IDX", + [VCAP_AF_NXT_IDX_CTRL] = "NXT_IDX_CTRL", + [VCAP_AF_OAM_SEL] = "OAM_SEL", + [VCAP_AF_PAG_OVERRIDE_MASK] = "PAG_OVERRIDE_MASK", + [VCAP_AF_PAG_VAL] = "PAG_VAL", + [VCAP_AF_PCP_ENA] = "PCP_ENA", + [VCAP_AF_PCP_VAL] = "PCP_VAL", + [VCAP_AF_PIPELINE_FORCE_ENA] = "PIPELINE_FORCE_ENA", + [VCAP_AF_PIPELINE_PT] = "PIPELINE_PT", [VCAP_AF_POLICE_ENA] = "POLICE_ENA", [VCAP_AF_POLICE_IDX] = "POLICE_IDX", + [VCAP_AF_POLICE_REMARK] = "POLICE_REMARK", [VCAP_AF_POLICE_VCAP_ONLY] = "POLICE_VCAP_ONLY", [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_QOS_ENA] = "QOS_ENA", + [VCAP_AF_QOS_VAL] = "QOS_VAL", [VCAP_AF_REW_OP] = "REW_OP", + [VCAP_AF_RT_DIS] = "RT_DIS", + [VCAP_AF_SFID_ENA] = "SFID_ENA", + [VCAP_AF_SFID_VAL] = "SFID_VAL", + [VCAP_AF_SGID_ENA] = "SGID_ENA", + [VCAP_AF_SGID_VAL] = "SGID_VAL", + [VCAP_AF_TYPE] = "TYPE", + [VCAP_AF_VID_REPLACE_ENA] = "VID_REPLACE_ENA", + [VCAP_AF_VID_VAL] = "VID_VAL", + [VCAP_AF_VLAN_POP_CNT] = "VLAN_POP_CNT", + [VCAP_AF_VLAN_POP_CNT_ENA] = "VLAN_POP_CNT_ENA", }; /* VCAPs */ const struct vcap_info lan966x_vcaps[] = { + [VCAP_TYPE_IS1] = { + .name = "is1", + .rows = 192, + .sw_count = 4, + .sw_width = 96, + .sticky_width = 32, + .act_width = 123, + .default_cnt = 0, + .require_cnt_dis = 1, + .version = 1, + .keyfield_set = is1_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is1_keyfield_set), + .actionfield_set = is1_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is1_actionfield_set), + .keyfield_set_map = is1_keyfield_set_map, + .keyfield_set_map_size = is1_keyfield_set_map_size, + .actionfield_set_map = is1_actionfield_set_map, + .actionfield_set_map_size = is1_actionfield_set_map_size, + .keyfield_set_typegroups = is1_keyfield_set_typegroups, + .actionfield_set_typegroups = is1_actionfield_set_typegroups, + }, [VCAP_TYPE_IS2] = { .name = "is2", .rows = 64, @@ -1600,7 +3000,7 @@ const struct vcap_info lan966x_vcaps[] = { const struct vcap_statistics lan966x_vcap_stats = { .name = "lan966x", - .count = 1, + .count = 2, .keyfield_set_names = vcap_keyfield_set_names, .actionfield_set_names = vcap_actionfield_set_names, .keyfield_names = vcap_keyfield_names, diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h index 0844fcaeee68..a556c4419986 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h @@ -3,8 +3,8 @@ * Microchip VCAP API */ -/* This file is autogenerated by cml-utils 2023-02-10 11:15:56 +0100. - * Commit ID: c30fb4bf0281cd4a7133bdab6682f9e43c872ada +/* This file is autogenerated by cml-utils 2023-02-16 11:41:14 +0100. + * Commit ID: be85f176b3a151fa748dcaf97c8824a5c2e065f3 */ #ifndef __VCAP_AG_API__ @@ -14,6 +14,7 @@ enum vcap_type { VCAP_TYPE_ES0, VCAP_TYPE_ES2, VCAP_TYPE_IS0, + VCAP_TYPE_IS1, VCAP_TYPE_IS2, VCAP_TYPE_MAX }; @@ -21,7 +22,12 @@ enum vcap_type { /* Keyfieldset names with origin information */ enum vcap_keyfield_set { VCAP_KFS_NO_VALUE, /* initial value */ + VCAP_KFS_5TUPLE_IP4, /* lan966x is1 X2 */ + VCAP_KFS_5TUPLE_IP6, /* lan966x is1 X4 */ + VCAP_KFS_7TUPLE, /* lan966x is1 X4 */ VCAP_KFS_ARP, /* sparx5 is2 X6, sparx5 es2 X6, lan966x is2 X2 */ + VCAP_KFS_DBL_VID, /* lan966x is1 X1 */ + VCAP_KFS_DMAC_VID, /* lan966x is1 X1 */ VCAP_KFS_ETAG, /* sparx5 is0 X2 */ VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6, sparx5 es2 X6, lan966x is2 X2 */ VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6, sparx5 es2 X6, lan966x is2 X2 */ @@ -36,10 +42,13 @@ enum vcap_keyfield_set { VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6, sparx5 es2 X6, lan966x is2 X2 */ VCAP_KFS_MAC_LLC, /* lan966x is2 X2 */ VCAP_KFS_MAC_SNAP, /* lan966x is2 X2 */ + VCAP_KFS_NORMAL, /* lan966x is1 X2 */ VCAP_KFS_NORMAL_5TUPLE_IP4, /* sparx5 is0 X6 */ VCAP_KFS_NORMAL_7TUPLE, /* sparx5 is0 X12 */ + VCAP_KFS_NORMAL_IP6, /* lan966x is1 X4 */ VCAP_KFS_OAM, /* lan966x is2 X2 */ VCAP_KFS_PURE_5TUPLE_IP4, /* sparx5 is0 X3 */ + VCAP_KFS_RT, /* lan966x is1 X1 */ VCAP_KFS_SMAC_SIP4, /* lan966x is2 X1 */ VCAP_KFS_SMAC_SIP6, /* lan966x is2 X2 */ }; @@ -61,17 +70,20 @@ enum vcap_keyfield_set { * Used by 802.1BR Bridge Port Extension in an E-Tag * VCAP_KF_8021BR_IGR_ECID_EXT: W8, sparx5: is0 * Used by 802.1BR Bridge Port Extension in an E-Tag - * VCAP_KF_8021Q_DEI0: W1, sparx5: is0 + * VCAP_KF_8021CB_R_TAGGED_IS: W1, lan966x: is1 + * Set if frame contains an RTAG: IEEE 802.1CB (FRER Redundancy tag, Ethertype + * 0xf1c1) + * VCAP_KF_8021Q_DEI0: W1, sparx5: is0, lan966x: is1 * First DEI in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_DEI1: W1, sparx5: is0 + * VCAP_KF_8021Q_DEI1: W1, sparx5: is0, lan966x: is1 * Second DEI in multiple vlan tags (inner tag) * VCAP_KF_8021Q_DEI2: W1, sparx5: is0 * Third DEI in multiple vlan tags (not always available) * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2, lan966x: is2 * Classified DEI - * VCAP_KF_8021Q_PCP0: W3, sparx5: is0 + * VCAP_KF_8021Q_PCP0: W3, sparx5: is0, lan966x: is1 * First PCP in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_PCP1: W3, sparx5: is0 + * VCAP_KF_8021Q_PCP1: W3, sparx5: is0, lan966x: is1 * Second PCP in multiple vlan tags (inner tag) * VCAP_KF_8021Q_PCP2: W3, sparx5: is0 * Third PCP in multiple vlan tags (not always available) @@ -79,22 +91,24 @@ enum vcap_keyfield_set { * Classified PCP * VCAP_KF_8021Q_TPID: W3, sparx5: es0 * TPID for outer tag: 0: Customer TPID 1: Service TPID (88A8 or programmable) - * VCAP_KF_8021Q_TPID0: W3, sparx5: is0 + * VCAP_KF_8021Q_TPID0: sparx5 is0 W3, lan966x is1 W1 * First TPIC in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_TPID1: W3, sparx5: is0 + * VCAP_KF_8021Q_TPID1: sparx5 is0 W3, lan966x is1 W1 * Second TPID in multiple vlan tags (inner tag) * VCAP_KF_8021Q_TPID2: W3, sparx5: is0 * Third TPID in multiple vlan tags (not always available) - * VCAP_KF_8021Q_VID0: W12, sparx5: is0 + * VCAP_KF_8021Q_VID0: W12, sparx5: is0, lan966x: is1 * First VID in multiple vlan tags (outer tag or default port tag) - * VCAP_KF_8021Q_VID1: W12, sparx5: is0 + * VCAP_KF_8021Q_VID1: W12, sparx5: is0, lan966x: is1 * Second VID in multiple vlan tags (inner tag) * VCAP_KF_8021Q_VID2: W12, sparx5: is0 * Third VID in multiple vlan tags (not always available) * VCAP_KF_8021Q_VID_CLS: sparx5 is2 W13, sparx5 es0 W13, sparx5 es2 W13, * lan966x is2 W12 * Classified VID - * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2, lan966x: is2 + * VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS: W1, lan966x: is1 + * Set if frame has two or more Q-tags. Independent of port VLAN awareness + * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2, lan966x: is1/is2 * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has * one or more Q-tags. Independent of port VLAN awareness * VCAP_KF_8021Q_VLAN_TAGS: W3, sparx5: is0 @@ -120,9 +134,9 @@ enum vcap_keyfield_set { * Class of service * VCAP_KF_ES0_ISDX_KEY_ENA: W1, sparx5: es2 * The value taken from the IFH .FWD.ES0_ISDX_KEY_ENA - * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2, lan966x: is1/is2 * Ethernet type - * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is0/is2/es2, lan966x: is1 * Set if frame has EtherType >= 0x600 * VCAP_KF_HOST_MATCH: W1, lan966x: is2 * The action from the SMAC_SIP4 or SMAC_SIP6 lookups. Used for IP source @@ -134,11 +148,12 @@ enum vcap_keyfield_set { * CPU queue) * VCAP_KF_IF_EGR_PORT_NO: W7, sparx5: es0 * Egress port number - * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9, lan966x is2 W4 + * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9, lan966x is1 W3, lan966x + * is2 W4 * Sparx5: Logical ingress port number retrieved from * ANA_CL::PORT_ID_CFG.LPORT_NUM or ERLEG, LAN966x: ingress port nunmber * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65, - * lan966x is2 W9 + * lan966x is1 W9, lan966x is2 W9 * Ingress port mask, one bit per port/erleg * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are @@ -151,24 +166,26 @@ enum vcap_keyfield_set { * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD * VCAP_KF_IF_IGR_PORT_SEL: W1, sparx5: es2 * Selector for IF_IGR_PORT: physical port number or ERLEG - * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 * Set if frame has EtherType = 0x800 and IP version = 4 - * VCAP_KF_IP_MC_IS: W1, sparx5: is0 + * VCAP_KF_IP_MC_IS: W1, sparx5: is0, lan966x: is1 * Set if frame is IPv4 frame and frame's destination MAC address is an IPv4 * multicast address (0x01005E0 /25). Set if frame is IPv6 frame and frame's * destination MAC address is an IPv6 multicast address (0x3333/16). - * VCAP_KF_IP_PAYLOAD_5TUPLE: W32, sparx5: is0 + * VCAP_KF_IP_PAYLOAD_5TUPLE: W32, sparx5: is0, lan966x: is1 * Payload bytes after IP header - * VCAP_KF_IP_SNAP_IS: W1, sparx5: is0 + * VCAP_KF_IP_PAYLOAD_S1_IP6: W112, lan966x: is1 + * Payload after IPv6 header + * VCAP_KF_IP_SNAP_IS: W1, sparx5: is0, lan966x: is1 * Set if frame is IPv4, IPv6, or SNAP frame * VCAP_KF_ISDX_CLS: W12, sparx5: is2/es0/es2 * Classified ISDX * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es0/es2, lan966x: is2 * Set if classified ISDX > 0 - * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 * Set if frame's destination MAC address is the broadcast address * (FF-FF-FF-FF-FF-FF). - * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2, lan966x: is1/is2 * Destination MAC address * VCAP_KF_L2_FRM_TYPE: W4, lan966x: is2 * Frame subtype for specific EtherTypes (MRP, DLR) @@ -176,7 +193,9 @@ enum vcap_keyfield_set { * Set if the frame is allowed to be forwarded to front ports * VCAP_KF_L2_LLC: W40, lan966x: is2 * LLC header and data after up to two VLAN tags and the type/length field - * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L2_MAC: W48, lan966x: is1 + * MAC address (FIRST=1: SMAC, FIRST=0: DMAC) + * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 * Set if frame's destination MAC address is a multicast address (bit 40 = 1). * VCAP_KF_L2_PAYLOAD0: W16, lan966x: is2 * Payload bytes 0-1 after the frame's EtherType @@ -188,7 +207,7 @@ enum vcap_keyfield_set { * specifically for PTP frames. * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2/es2 * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM - * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2, lan966x: is1/is2 * Source MAC address * VCAP_KF_L2_SNAP: W40, lan966x: is2 * SNAP header after LLC header (AA-AA-03) @@ -196,32 +215,38 @@ enum vcap_keyfield_set { * Set if Src IP matches Dst IP address * VCAP_KF_L3_DPL_CLS: W1, sparx5: es0/es2 * The frames drop precedence level - * VCAP_KF_L3_DSCP: W6, sparx5: is0 + * VCAP_KF_L3_DSCP: W6, sparx5: is0, lan966x: is1 * Frame's DSCP value * VCAP_KF_L3_DST_IS: W1, sparx5: is2 * Set if lookup is done for egress router leg - * VCAP_KF_L3_FRAGMENT: W1, lan966x: is2 + * VCAP_KF_L3_FRAGMENT: W1, lan966x: is1/is2 * Set if IPv4 frame is fragmented * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is0/is2/es2 * L3 Fragmentation type (none, initial, suspicious, valid follow up) * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is0/is2 * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L * EN - * VCAP_KF_L3_FRAG_OFS_GT0: W1, lan966x: is2 + * VCAP_KF_L3_FRAG_OFS_GT0: W1, lan966x: is1/is2 * Set if IPv4 frame is fragmented and it is not the first fragment - * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2, lan966x: is1/is2 * Destination IPv4 Address - * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2, lan966x: is1/is2 * Source IPv4 Address - * VCAP_KF_L3_IP6_DIP: W128, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L3_IP6_DIP: sparx5 is0 W128, sparx5 is2 W128, sparx5 es2 W128, + * lan966x is1 W64, lan966x is1 W128, lan966x is2 W128 * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on * frame type - * VCAP_KF_L3_IP6_SIP: W128, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L3_IP6_DIP_MSB: W16, lan966x: is1 + * MS 16bits of IPv6 DIP + * VCAP_KF_L3_IP6_SIP: sparx5 is0 W128, sparx5 is2 W128, sparx5 es2 W128, + * lan966x is1 W128, lan966x is1 W64, lan966x is2 W128 * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on * frame type - * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L3_IP6_SIP_MSB: W16, lan966x: is1 + * MS 16bits of IPv6 DIP + * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2, lan966x: is1/is2 * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 - * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 * Set if IPv4 frame contains options (IP len > 5) * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40, sparx5 es2 W96, sparx5 * es2 W40, lan966x is2 W56 @@ -254,7 +279,8 @@ enum vcap_keyfield_set { * VCAP_KF_L4_PSH: W1, sparx5: is2/es2, lan966x: is2 * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit * 1 (twoStepFlag) - * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16, lan966x is2 W8 + * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16, lan966x is1 + * W8, lan966x is2 W8 * Range checker bitmask (one for each range checker). Input into range checkers * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, * outer VID, inner VID) @@ -264,7 +290,7 @@ enum vcap_keyfield_set { * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: * messageType bit 0 - * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2, lan966x: is1/is2 * TCP/UDP source port * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if UDP or TCP source port equals UDP or TCP destination port @@ -274,13 +300,16 @@ enum vcap_keyfield_set { * VCAP_KF_L4_URG: W1, sparx5: is2/es2, lan966x: is2 * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit * 7 (reserved) - * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 * Selects between entries relevant for first and second lookup. Set for first * lookup, cleared for second lookup. * VCAP_KF_LOOKUP_GEN_IDX: W12, sparx5: is0 * Generic index - for chaining CLM instances * VCAP_KF_LOOKUP_GEN_IDX_SEL: W2, sparx5: is0 * Select the mode of the Generic Index + * VCAP_KF_LOOKUP_INDEX: W2, lan966x: is1 + * 0: First lookup, 1: Second lookup, 2: Third lookup, Similar to VCAP_KF_FIRST + * but with extra info * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2, lan966x: is2 * Classified Policy Association Group: chains rules from IS1/CLM to IS2 * VCAP_KF_MIRROR_PROBE: W2, sparx5: es2 @@ -303,14 +332,22 @@ enum vcap_keyfield_set { * Set if frame's EtherType = 0x8902 * VCAP_KF_PROT_ACTIVE: W1, sparx5: es0/es2 * Protection is active - * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * VCAP_KF_RT_FRMID: W32, lan966x: is1 + * Profinet or OPC-UA FrameId + * VCAP_KF_RT_TYPE: W2, lan966x: is1 + * Encoding of frame's EtherType: 0: Other, 1: Profinet, 2: OPC-UA, 3: Custom + * (ANA::RT_CUSTOM) + * VCAP_KF_RT_VLAN_IDX: W3, lan966x: is1 + * Real-time VLAN index from ANA::RT_VLAN_PCP + * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next * header = 6) - * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2 + * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2, lan966x: is1 * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 * or 17) * VCAP_KF_TYPE: sparx5 is0 W2, sparx5 is0 W1, sparx5 is2 W4, sparx5 is2 W2, - * sparx5 es0 W1, sparx5 es2 W3, lan966x is2 W4, lan966x is2 W2 + * sparx5 es0 W1, sparx5 es2 W3, lan966x is1 W1, lan966x is1 W2, lan966x is2 W4, + * lan966x is2 W2 * Keyset type id - set by the API */ @@ -323,6 +360,7 @@ enum vcap_key_field { VCAP_KF_8021BR_GRP, VCAP_KF_8021BR_IGR_ECID_BASE, VCAP_KF_8021BR_IGR_ECID_EXT, + VCAP_KF_8021CB_R_TAGGED_IS, VCAP_KF_8021Q_DEI0, VCAP_KF_8021Q_DEI1, VCAP_KF_8021Q_DEI2, @@ -339,6 +377,7 @@ enum vcap_key_field { VCAP_KF_8021Q_VID1, VCAP_KF_8021Q_VID2, VCAP_KF_8021Q_VID_CLS, + VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS, VCAP_KF_8021Q_VLAN_TAGGED_IS, VCAP_KF_8021Q_VLAN_TAGS, VCAP_KF_ACL_GRP_ID, @@ -366,6 +405,7 @@ enum vcap_key_field { VCAP_KF_IP4_IS, VCAP_KF_IP_MC_IS, VCAP_KF_IP_PAYLOAD_5TUPLE, + VCAP_KF_IP_PAYLOAD_S1_IP6, VCAP_KF_IP_SNAP_IS, VCAP_KF_ISDX_CLS, VCAP_KF_ISDX_GT0_IS, @@ -374,6 +414,7 @@ enum vcap_key_field { VCAP_KF_L2_FRM_TYPE, VCAP_KF_L2_FWD_IS, VCAP_KF_L2_LLC, + VCAP_KF_L2_MAC, VCAP_KF_L2_MC_IS, VCAP_KF_L2_PAYLOAD0, VCAP_KF_L2_PAYLOAD1, @@ -392,7 +433,9 @@ enum vcap_key_field { VCAP_KF_L3_IP4_DIP, VCAP_KF_L3_IP4_SIP, VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_DIP_MSB, VCAP_KF_L3_IP6_SIP, + VCAP_KF_L3_IP6_SIP_MSB, VCAP_KF_L3_IP_PROTO, VCAP_KF_L3_OPTIONS_IS, VCAP_KF_L3_PAYLOAD, @@ -416,6 +459,7 @@ enum vcap_key_field { VCAP_KF_LOOKUP_FIRST_IS, VCAP_KF_LOOKUP_GEN_IDX, VCAP_KF_LOOKUP_GEN_IDX_SEL, + VCAP_KF_LOOKUP_INDEX, VCAP_KF_LOOKUP_PAG, VCAP_KF_MIRROR_PROBE, VCAP_KF_OAM_CCM_CNTS_EQ0, @@ -427,6 +471,9 @@ enum vcap_key_field { VCAP_KF_OAM_VER, VCAP_KF_OAM_Y1731_IS, VCAP_KF_PROT_ACTIVE, + VCAP_KF_RT_FRMID, + VCAP_KF_RT_TYPE, + VCAP_KF_RT_VLAN_IDX, VCAP_KF_TCP_IS, VCAP_KF_TCP_UDP_IS, VCAP_KF_TYPE, @@ -440,6 +487,7 @@ enum vcap_actionfield_set { VCAP_AFS_CLASS_REDUCED, /* sparx5 is0 X1 */ VCAP_AFS_ES0, /* sparx5 es0 X1 */ VCAP_AFS_FULL, /* sparx5 is0 X3 */ + VCAP_AFS_S1, /* lan966x is1 X1 */ VCAP_AFS_SMAC_SIP, /* lan966x is2 X1 */ }; @@ -470,23 +518,31 @@ enum vcap_actionfield_set { * CPU extraction queue. Used when FWD_SEL >0 and PIPELINE_ACT = XTR. * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2/es2, lan966x: is2 * CPU queue number. Used when CPU_COPY_ENA is set. + * VCAP_AF_CUSTOM_ACE_TYPE_ENA: W4, lan966x: is1 + * Enables use of custom keys in IS2. Bits 3:2 control second lookup in IS2 + * while bits 1:0 control first lookup. Encoding per lookup: 0: Disabled. 1: + * Extract 40 bytes after position corresponding to the location of the IPv4 + * header and use as key. 2: Extract 40 bytes after SMAC and use as key * VCAP_AF_DEI_A_VAL: W1, sparx5: es0 * DEI used in ES0 tag A. See TAG_A_DEI_SEL. * VCAP_AF_DEI_B_VAL: W1, sparx5: es0 * DEI used in ES0 tag B. See TAG_B_DEI_SEL. * VCAP_AF_DEI_C_VAL: W1, sparx5: es0 * DEI used in ES0 tag C. See TAG_C_DEI_SEL. - * VCAP_AF_DEI_ENA: W1, sparx5: is0 + * VCAP_AF_DEI_ENA: W1, sparx5: is0, lan966x: is1 * If set, use DEI_VAL as classified DEI value. Otherwise, DEI from basic * classification is used - * VCAP_AF_DEI_VAL: W1, sparx5: is0 + * VCAP_AF_DEI_VAL: W1, sparx5: is0, lan966x: is1 * See DEI_ENA - * VCAP_AF_DP_ENA: W1, sparx5: is0 + * VCAP_AF_DLR_SEL: W2, lan966x: is1 + * 0: No changes to port-based selection in ANA:PORT:OAM_CFG.DLR_ENA. 1: Enable + * DLR frame processing 2: Disable DLR processing + * VCAP_AF_DP_ENA: W1, sparx5: is0, lan966x: is1 * If set, use DP_VAL as classified drop precedence level. Otherwise, drop * precedence level from basic classification is used. - * VCAP_AF_DP_VAL: W2, sparx5: is0 + * VCAP_AF_DP_VAL: sparx5 is0 W2, lan966x is1 W1 * See DP_ENA. - * VCAP_AF_DSCP_ENA: W1, sparx5: is0 + * VCAP_AF_DSCP_ENA: W1, sparx5: is0, lan966x: is1 * If set, use DSCP_VAL as classified DSCP value. Otherwise, DSCP value from * basic classification is used. * VCAP_AF_DSCP_SEL: W3, sparx5: es0 @@ -495,7 +551,7 @@ enum vcap_actionfield_set { * table 0, otherwise use DSCP_VAL. 5: Mapped using mapping table 1, otherwise * use mapping table 0. 6: Mapped using mapping table 2, otherwise use DSCP_VAL. * 7: Mapped using mapping table 3, otherwise use mapping table 2 - * VCAP_AF_DSCP_VAL: W6, sparx5: is0/es0 + * VCAP_AF_DSCP_VAL: W6, sparx5: is0/es0, lan966x: is1 * See DSCP_ENA. * VCAP_AF_ES2_REW_CMD: W3, sparx5: es2 * Command forwarded to REW: 0: No action. 1: SWAP MAC addresses. 2: Do L2CP @@ -529,9 +585,16 @@ enum vcap_actionfield_set { * VCAP_AF_ISDX_ADD_REPLACE_SEL: W1, sparx5: is0 * Controls the classified ISDX. 0: New ISDX = old ISDX + ISDX_VAL. 1: New ISDX * = ISDX_VAL. + * VCAP_AF_ISDX_ADD_VAL: W8, lan966x: is1 + * If ISDX_REPLACE_ENA is set, ISDX_ADD_VAL is used directly as the new ISDX. + * Encoding: ISDX_REPLACE_ENA=0, ISDX_ADD_VAL=0: Disabled ISDX_EPLACE_ENA=0, + * ISDX_ADD_VAL>0: Add value to classified ISDX. ISDX_REPLACE_ENA=1: Replace + * with ISDX_ADD_VAL value. * VCAP_AF_ISDX_ENA: W1, lan966x: is2 * Setting this bit to 1 causes the classified ISDX to be set to the value of * POLICE_IDX[8:0]. + * VCAP_AF_ISDX_REPLACE_ENA: W1, lan966x: is1 + * If set, classified ISDX is set to ISDX_ADD_VAL. * VCAP_AF_ISDX_VAL: W12, sparx5: is0 * See isdx_add_replace_sel * VCAP_AF_LOOP_ENA: W1, sparx5: es0 @@ -572,14 +635,22 @@ enum vcap_actionfield_set { * VCAP_AF_MIRROR_PROBE_ID: W2, sparx5: es2 * Signals a mirror probe to be placed in the IFH. Only possible when FWD_MODE * is copy. 0: No mirroring. 1-3: Use mirror probe 0-2. + * VCAP_AF_MRP_SEL: W2, lan966x: is1 + * 0: No changes to port-based selection in ANA:PORT:OAM_CFG.MRP_ENA. 1: Enable + * MRP frame processing 2: Disable MRP processing * VCAP_AF_NXT_IDX: W12, sparx5: is0 * Index used as part of key (field G_IDX) in the next lookup. * VCAP_AF_NXT_IDX_CTRL: W3, sparx5: is0 * Controls the generation of the G_IDX used in the VCAP CLM next lookup - * VCAP_AF_PAG_OVERRIDE_MASK: W8, sparx5: is0 + * VCAP_AF_OAM_SEL: W3, lan966x: is1 + * 0: No changes to port-based selection in ANA:PORT:OAM_CFG.OAM_CFG 1: Enable + * OAM frame processing for untagged frames 2: Enable OAM frame processing for + * single frames 3: Enable OAM frame processing for double frames 4: Disable OAM + * frame processing + * VCAP_AF_PAG_OVERRIDE_MASK: W8, sparx5: is0, lan966x: is1 * Bits set in this mask will override PAG_VAL from port profile. New PAG = (PAG * (input) AND ~PAG_OVERRIDE_MASK) OR (PAG_VAL AND PAG_OVERRIDE_MASK) - * VCAP_AF_PAG_VAL: W8, sparx5: is0 + * VCAP_AF_PAG_VAL: W8, sparx5: is0, lan966x: is1 * See PAG_OVERRIDE_MASK. * VCAP_AF_PCP_A_VAL: W3, sparx5: es0 * PCP used in ES0 tag A. See TAG_A_PCP_SEL. @@ -587,10 +658,10 @@ enum vcap_actionfield_set { * PCP used in ES0 tag B. See TAG_B_PCP_SEL. * VCAP_AF_PCP_C_VAL: W3, sparx5: es0 * PCP used in ES0 tag C. See TAG_C_PCP_SEL. - * VCAP_AF_PCP_ENA: W1, sparx5: is0 + * VCAP_AF_PCP_ENA: W1, sparx5: is0, lan966x: is1 * If set, use PCP_VAL as classified PCP value. Otherwise, PCP from basic * classification is used. - * VCAP_AF_PCP_VAL: W3, sparx5: is0 + * VCAP_AF_PCP_VAL: W3, sparx5: is0, lan966x: is1 * See PCP_ENA. * VCAP_AF_PIPELINE_ACT: W1, sparx5: es0 * Pipeline action when FWD_SEL > 0. 0: XTR. CPU_QU selects CPU extraction queue @@ -600,11 +671,11 @@ enum vcap_actionfield_set { * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. * VCAP_AF_PIPELINE_PT: sparx5 is2 W5, sparx5 es0 W2 * Pipeline point used if PIPELINE_FORCE_ENA is set - * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2, lan966x: is2 - * Setting this bit to 1 causes frames that hit this action to be policed by the - * ACL policer specified in POLICE_IDX. Only applies to the first lookup. - * VCAP_AF_POLICE_IDX: sparx5 is2 W6, sparx5 es2 W6, lan966x is2 W9 - * Selects VCAP policer used when policing frames (POLICE_ENA) + * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2, lan966x: is1/is2 + * If set, POLICE_IDX is used to lookup ANA::POL. + * VCAP_AF_POLICE_IDX: sparx5 is2 W6, sparx5 es2 W6, lan966x is1 W9, lan966x is2 + * W9 + * Policer index. * VCAP_AF_POLICE_REMARK: W1, sparx5: es2 * If set, frames exceeding policer rates are marked as yellow but not * discarded. @@ -628,16 +699,24 @@ enum vcap_actionfield_set { * port. 1: ES0 tag A: Push ES0 tag A. No port tag. 2: Force port tag: Always * push port tag. No ES0 tag A. 3: Force untag: Never push port tag or ES0 tag * A. - * VCAP_AF_QOS_ENA: W1, sparx5: is0 + * VCAP_AF_QOS_ENA: W1, sparx5: is0, lan966x: is1 * If set, use QOS_VAL as classified QoS class. Otherwise, QoS class from basic * classification is used. - * VCAP_AF_QOS_VAL: W3, sparx5: is0 + * VCAP_AF_QOS_VAL: W3, sparx5: is0, lan966x: is1 * See QOS_ENA. * VCAP_AF_REW_OP: W16, lan966x: is2 * Rewriter operation command. * VCAP_AF_RT_DIS: W1, sparx5: is2 * If set, routing is disallowed. Only applies when IS_INNER_ACL is 0. See also * IGR_ACL_ENA, EGR_ACL_ENA, and RLEG_STAT_IDX. + * VCAP_AF_SFID_ENA: W1, lan966x: is1 + * If set, SFID_VAL is used to lookup ANA::SFID. + * VCAP_AF_SFID_VAL: W8, lan966x: is1 + * Stream filter identifier. + * VCAP_AF_SGID_ENA: W1, lan966x: is1 + * If set, SGID_VAL is used to lookup ANA::SGID. + * VCAP_AF_SGID_VAL: W8, lan966x: is1 + * Stream gate identifier. * VCAP_AF_SWAP_MACS_ENA: W1, sparx5: es0 * This setting is only active when FWD_SEL = 1 or FWD_SEL = 2 and PIPELINE_ACT * = LBK_ASM. 0: No action. 1: Swap MACs and clear bit 40 in new SMAC. @@ -686,7 +765,7 @@ enum vcap_actionfield_set { * VCAP_AF_TAG_C_VID_SEL: W2, sparx5: es0 * Selects VID for ES0 tag C. The resulting VID is termed C-TAG.VID. 0: * Classified VID. 1: VID_C_VAL. 2: IFH.ENCAP.GVID. 3: Reserved. - * VCAP_AF_TYPE: W1, sparx5: is0 + * VCAP_AF_TYPE: W1, sparx5: is0, lan966x: is1 * Actionset type id - Set by the API * VCAP_AF_UNTAG_VID_ENA: W1, sparx5: es0 * Controls insertion of tag C. Untag or insert mode can be selected. See @@ -697,8 +776,19 @@ enum vcap_actionfield_set { * VID used in ES0 tag B. See TAG_B_VID_SEL. * VCAP_AF_VID_C_VAL: W12, sparx5: es0 * VID used in ES0 tag C. See TAG_C_VID_SEL. - * VCAP_AF_VID_VAL: W13, sparx5: is0 + * VCAP_AF_VID_REPLACE_ENA: W1, lan966x: is1 + * Controls the classified VID: VID_REPLACE_ENA=0: Add VID_ADD_VAL to basic + * classified VID and use result as new classified VID. VID_REPLACE_ENA = 1: + * Replace basic classified VID with VID_VAL value and use as new classified + * VID. + * VCAP_AF_VID_VAL: sparx5 is0 W13, lan966x is1 W12 * New VID Value + * VCAP_AF_VLAN_POP_CNT: W2, lan966x: is1 + * See VLAN_POP_CNT_ENA + * VCAP_AF_VLAN_POP_CNT_ENA: W1, lan966x: is1 + * If set, use VLAN_POP_CNT as the number of VLAN tags to pop from the incoming + * frame. This number is used by the Rewriter. Otherwise, VLAN_POP_CNT from + * ANA:PORT:VLAN_CFG.VLAN_POP_CNT is used */ /* Actionfield names */ @@ -712,11 +802,13 @@ enum vcap_action_field { VCAP_AF_CPU_COPY_ENA, VCAP_AF_CPU_QU, VCAP_AF_CPU_QUEUE_NUM, + VCAP_AF_CUSTOM_ACE_TYPE_ENA, VCAP_AF_DEI_A_VAL, VCAP_AF_DEI_B_VAL, VCAP_AF_DEI_C_VAL, VCAP_AF_DEI_ENA, VCAP_AF_DEI_VAL, + VCAP_AF_DLR_SEL, VCAP_AF_DP_ENA, VCAP_AF_DP_VAL, VCAP_AF_DSCP_ENA, @@ -732,7 +824,9 @@ enum vcap_action_field { VCAP_AF_IGNORE_PIPELINE_CTRL, VCAP_AF_INTR_ENA, VCAP_AF_ISDX_ADD_REPLACE_SEL, + VCAP_AF_ISDX_ADD_VAL, VCAP_AF_ISDX_ENA, + VCAP_AF_ISDX_REPLACE_ENA, VCAP_AF_ISDX_VAL, VCAP_AF_LOOP_ENA, VCAP_AF_LRN_DIS, @@ -745,8 +839,10 @@ enum vcap_action_field { VCAP_AF_MIRROR_ENA, VCAP_AF_MIRROR_PROBE, VCAP_AF_MIRROR_PROBE_ID, + VCAP_AF_MRP_SEL, VCAP_AF_NXT_IDX, VCAP_AF_NXT_IDX_CTRL, + VCAP_AF_OAM_SEL, VCAP_AF_PAG_OVERRIDE_MASK, VCAP_AF_PAG_VAL, VCAP_AF_PCP_A_VAL, @@ -770,6 +866,10 @@ enum vcap_action_field { VCAP_AF_QOS_VAL, VCAP_AF_REW_OP, VCAP_AF_RT_DIS, + VCAP_AF_SFID_ENA, + VCAP_AF_SFID_VAL, + VCAP_AF_SGID_ENA, + VCAP_AF_SGID_VAL, VCAP_AF_SWAP_MACS_ENA, VCAP_AF_TAG_A_DEI_SEL, VCAP_AF_TAG_A_PCP_SEL, @@ -788,7 +888,10 @@ enum vcap_action_field { VCAP_AF_VID_A_VAL, VCAP_AF_VID_B_VAL, VCAP_AF_VID_C_VAL, + VCAP_AF_VID_REPLACE_ENA, VCAP_AF_VID_VAL, + VCAP_AF_VLAN_POP_CNT, + VCAP_AF_VLAN_POP_CNT_ENA, }; #endif /* __VCAP_AG_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c index 0de3f677135a..b23c11b0647c 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -387,7 +387,7 @@ static const char * const test_admin_info_expect[] = { "default_cnt: 73\n", "require_cnt_dis: 0\n", "version: 1\n", - "vtype: 3\n", + "vtype: 4\n", "vinst: 0\n", "ingress: 1\n", "first_cid: 10000\n", @@ -435,7 +435,7 @@ static const char * const test_admin_expect[] = { "default_cnt: 73\n", "require_cnt_dis: 0\n", "version: 1\n", - "vtype: 3\n", + "vtype: 4\n", "vinst: 0\n", "ingress: 1\n", "first_cid: 8000000\n", -- cgit v1.2.3 From a4d9b3ec63de3c36890fb440980ebf8f642bab88 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 7 Mar 2023 23:09:26 +0100 Subject: net: lan966x: Add IS1 VCAP keyset configuration for lan966x Add IS1 VCAP port keyset configuration for lan966x and also update debug fs support to show the keyset configuration. Signed-off-by: Horatiu Vultur Signed-off-by: Jakub Kicinski --- .../net/ethernet/microchip/lan966x/lan966x_main.h | 38 ++++ .../net/ethernet/microchip/lan966x/lan966x_regs.h | 36 ++++ .../microchip/lan966x/lan966x_vcap_debugfs.c | 133 +++++++++++++- .../ethernet/microchip/lan966x/lan966x_vcap_impl.c | 192 +++++++++++++++++++-- 4 files changed, 383 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 49f5159afbf3..cbdae0ab8bb6 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -92,6 +92,11 @@ #define SE_IDX_QUEUE 0 /* 0-79 : Queue scheduler elements */ #define SE_IDX_PORT 80 /* 80-89 : Port schedular elements */ +#define LAN966X_VCAP_CID_IS1_L0 VCAP_CID_INGRESS_L0 /* IS1 lookup 0 */ +#define LAN966X_VCAP_CID_IS1_L1 VCAP_CID_INGRESS_L1 /* IS1 lookup 1 */ +#define LAN966X_VCAP_CID_IS1_L2 VCAP_CID_INGRESS_L2 /* IS1 lookup 2 */ +#define LAN966X_VCAP_CID_IS1_MAX (VCAP_CID_INGRESS_L3 - 1) /* IS1 Max */ + #define LAN966X_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ #define LAN966X_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ #define LAN966X_VCAP_CID_IS2_MAX (VCAP_CID_INGRESS_STAGE2_L2 - 1) /* IS2 Max */ @@ -139,6 +144,39 @@ enum vcap_is2_port_sel_ipv6 { VCAP_IS2_PS_IPV6_MAC_ETYPE, }; +enum vcap_is1_port_sel_other { + VCAP_IS1_PS_OTHER_NORMAL, + VCAP_IS1_PS_OTHER_7TUPLE, + VCAP_IS1_PS_OTHER_DBL_VID, + VCAP_IS1_PS_OTHER_DMAC_VID, +}; + +enum vcap_is1_port_sel_ipv4 { + VCAP_IS1_PS_IPV4_NORMAL, + VCAP_IS1_PS_IPV4_7TUPLE, + VCAP_IS1_PS_IPV4_5TUPLE_IP4, + VCAP_IS1_PS_IPV4_DBL_VID, + VCAP_IS1_PS_IPV4_DMAC_VID, +}; + +enum vcap_is1_port_sel_ipv6 { + VCAP_IS1_PS_IPV6_NORMAL, + VCAP_IS1_PS_IPV6_7TUPLE, + VCAP_IS1_PS_IPV6_5TUPLE_IP4, + VCAP_IS1_PS_IPV6_NORMAL_IP6, + VCAP_IS1_PS_IPV6_5TUPLE_IP6, + VCAP_IS1_PS_IPV6_DBL_VID, + VCAP_IS1_PS_IPV6_DMAC_VID, +}; + +enum vcap_is1_port_sel_rt { + VCAP_IS1_PS_RT_NORMAL, + VCAP_IS1_PS_RT_7TUPLE, + VCAP_IS1_PS_RT_DBL_VID, + VCAP_IS1_PS_RT_DMAC_VID, + VCAP_IS1_PS_RT_FOLLOW_OTHER = 7, +}; + struct lan966x_port; struct lan966x_db { diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h index 9767b5a1c958..f99f88b5caa8 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h @@ -316,6 +316,42 @@ enum lan966x_target { #define ANA_DROP_CFG_DROP_MC_SMAC_ENA_GET(x)\ FIELD_GET(ANA_DROP_CFG_DROP_MC_SMAC_ENA, x) +/* ANA:PORT:VCAP_CFG */ +#define ANA_VCAP_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 12, 0, 1, 4) + +#define ANA_VCAP_CFG_S1_ENA BIT(14) +#define ANA_VCAP_CFG_S1_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_CFG_S1_ENA, x) +#define ANA_VCAP_CFG_S1_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_CFG_S1_ENA, x) + +/* ANA:PORT:VCAP_S1_KEY_CFG */ +#define ANA_VCAP_S1_CFG(g, r) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 16, r, 3, 4) + +#define ANA_VCAP_S1_CFG_KEY_RT_CFG GENMASK(11, 9) +#define ANA_VCAP_S1_CFG_KEY_RT_CFG_SET(x)\ + FIELD_PREP(ANA_VCAP_S1_CFG_KEY_RT_CFG, x) +#define ANA_VCAP_S1_CFG_KEY_RT_CFG_GET(x)\ + FIELD_GET(ANA_VCAP_S1_CFG_KEY_RT_CFG, x) + +#define ANA_VCAP_S1_CFG_KEY_IP6_CFG GENMASK(8, 6) +#define ANA_VCAP_S1_CFG_KEY_IP6_CFG_SET(x)\ + FIELD_PREP(ANA_VCAP_S1_CFG_KEY_IP6_CFG, x) +#define ANA_VCAP_S1_CFG_KEY_IP6_CFG_GET(x)\ + FIELD_GET(ANA_VCAP_S1_CFG_KEY_IP6_CFG, x) + +#define ANA_VCAP_S1_CFG_KEY_IP4_CFG GENMASK(5, 3) +#define ANA_VCAP_S1_CFG_KEY_IP4_CFG_SET(x)\ + FIELD_PREP(ANA_VCAP_S1_CFG_KEY_IP4_CFG, x) +#define ANA_VCAP_S1_CFG_KEY_IP4_CFG_GET(x)\ + FIELD_GET(ANA_VCAP_S1_CFG_KEY_IP4_CFG, x) + +#define ANA_VCAP_S1_CFG_KEY_OTHER_CFG GENMASK(2, 0) +#define ANA_VCAP_S1_CFG_KEY_OTHER_CFG_SET(x)\ + FIELD_PREP(ANA_VCAP_S1_CFG_KEY_OTHER_CFG, x) +#define ANA_VCAP_S1_CFG_KEY_OTHER_CFG_GET(x)\ + FIELD_GET(ANA_VCAP_S1_CFG_KEY_OTHER_CFG, x) + /* ANA:PORT:VCAP_S2_CFG */ #define ANA_VCAP_S2_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 28, 0, 1, 4) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c index 7a0db58f5513..d90c08cfcf14 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c @@ -5,9 +5,124 @@ #include "vcap_api.h" #include "vcap_api_client.h" -static void lan966x_vcap_port_keys(struct lan966x_port *port, - struct vcap_admin *admin, - struct vcap_output_print *out) +static void lan966x_vcap_is1_port_keys(struct lan966x_port *port, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + struct lan966x *lan966x = port->lan966x; + u32 val; + + out->prf(out->dst, " port[%d] (%s): ", port->chip_port, + netdev_name(port->dev)); + + val = lan_rd(lan966x, ANA_VCAP_CFG(port->chip_port)); + out->prf(out->dst, "\n state: "); + if (ANA_VCAP_CFG_S1_ENA_GET(val)) + out->prf(out->dst, "on"); + else + out->prf(out->dst, "off"); + + for (int l = 0; l < admin->lookups; ++l) { + out->prf(out->dst, "\n Lookup %d: ", l); + + out->prf(out->dst, "\n other: "); + switch (ANA_VCAP_S1_CFG_KEY_OTHER_CFG_GET(val)) { + case VCAP_IS1_PS_OTHER_NORMAL: + out->prf(out->dst, "normal"); + break; + case VCAP_IS1_PS_OTHER_7TUPLE: + out->prf(out->dst, "7tuple"); + break; + case VCAP_IS1_PS_OTHER_DBL_VID: + out->prf(out->dst, "dbl_vid"); + break; + case VCAP_IS1_PS_OTHER_DMAC_VID: + out->prf(out->dst, "dmac_vid"); + break; + default: + out->prf(out->dst, "-"); + break; + } + + out->prf(out->dst, "\n ipv4: "); + switch (ANA_VCAP_S1_CFG_KEY_IP4_CFG_GET(val)) { + case VCAP_IS1_PS_IPV4_NORMAL: + out->prf(out->dst, "normal"); + break; + case VCAP_IS1_PS_IPV4_7TUPLE: + out->prf(out->dst, "7tuple"); + break; + case VCAP_IS1_PS_IPV4_5TUPLE_IP4: + out->prf(out->dst, "5tuple_ipv4"); + break; + case VCAP_IS1_PS_IPV4_DBL_VID: + out->prf(out->dst, "dbl_vid"); + break; + case VCAP_IS1_PS_IPV4_DMAC_VID: + out->prf(out->dst, "dmac_vid"); + break; + default: + out->prf(out->dst, "-"); + break; + } + + out->prf(out->dst, "\n ipv6: "); + switch (ANA_VCAP_S1_CFG_KEY_IP6_CFG_GET(val)) { + case VCAP_IS1_PS_IPV6_NORMAL: + out->prf(out->dst, "normal"); + break; + case VCAP_IS1_PS_IPV6_7TUPLE: + out->prf(out->dst, "7tuple"); + break; + case VCAP_IS1_PS_IPV6_5TUPLE_IP4: + out->prf(out->dst, "5tuple_ip4"); + break; + case VCAP_IS1_PS_IPV6_NORMAL_IP6: + out->prf(out->dst, "normal_ip6"); + break; + case VCAP_IS1_PS_IPV6_5TUPLE_IP6: + out->prf(out->dst, "5tuple_ip6"); + break; + case VCAP_IS1_PS_IPV6_DBL_VID: + out->prf(out->dst, "dbl_vid"); + break; + case VCAP_IS1_PS_IPV6_DMAC_VID: + out->prf(out->dst, "dmac_vid"); + break; + default: + out->prf(out->dst, "-"); + break; + } + + out->prf(out->dst, "\n rt: "); + switch (ANA_VCAP_S1_CFG_KEY_RT_CFG_GET(val)) { + case VCAP_IS1_PS_RT_NORMAL: + out->prf(out->dst, "normal"); + break; + case VCAP_IS1_PS_RT_7TUPLE: + out->prf(out->dst, "7tuple"); + break; + case VCAP_IS1_PS_RT_DBL_VID: + out->prf(out->dst, "dbl_vid"); + break; + case VCAP_IS1_PS_RT_DMAC_VID: + out->prf(out->dst, "dmac_vid"); + break; + case VCAP_IS1_PS_RT_FOLLOW_OTHER: + out->prf(out->dst, "follow_other"); + break; + default: + out->prf(out->dst, "-"); + break; + } + } + + out->prf(out->dst, "\n"); +} + +static void lan966x_vcap_is2_port_keys(struct lan966x_port *port, + struct vcap_admin *admin, + struct vcap_output_print *out) { struct lan966x *lan966x = port->lan966x; u32 val; @@ -88,7 +203,17 @@ int lan966x_vcap_port_info(struct net_device *dev, vcap = &vctrl->vcaps[admin->vtype]; out->prf(out->dst, "%s:\n", vcap->name); - lan966x_vcap_port_keys(port, admin, out); + switch (admin->vtype) { + case VCAP_TYPE_IS2: + lan966x_vcap_is2_port_keys(port, admin, out); + break; + case VCAP_TYPE_IS1: + lan966x_vcap_is1_port_keys(port, admin, out); + break; + default: + out->prf(out->dst, " no info\n"); + break; + } return 0; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 68f9d69fd37b..7ea8e8633609 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -8,6 +8,7 @@ #define STREAMSIZE (64 * 4) +#define LAN966X_IS1_LOOKUPS 3 #define LAN966X_IS2_LOOKUPS 2 static struct lan966x_vcap_inst { @@ -19,6 +20,15 @@ static struct lan966x_vcap_inst { int count; /* number of available addresses */ bool ingress; /* is vcap in the ingress path */ } lan966x_vcap_inst_cfg[] = { + { + .vtype = VCAP_TYPE_IS1, /* IS1-0 */ + .tgt_inst = 1, + .lookups = LAN966X_IS1_LOOKUPS, + .first_cid = LAN966X_VCAP_CID_IS1_L0, + .last_cid = LAN966X_VCAP_CID_IS1_MAX, + .count = 768, + .ingress = true, + }, { .vtype = VCAP_TYPE_IS2, /* IS2-0 */ .tgt_inst = 2, @@ -72,7 +82,21 @@ static void __lan966x_vcap_range_init(struct lan966x *lan966x, lan966x_vcap_wait_update(lan966x, admin->tgt_inst); } -static int lan966x_vcap_cid_to_lookup(int cid) +static int lan966x_vcap_is1_cid_to_lookup(int cid) +{ + int lookup = 0; + + if (cid >= LAN966X_VCAP_CID_IS1_L1 && + cid < LAN966X_VCAP_CID_IS1_L2) + lookup = 1; + else if (cid >= LAN966X_VCAP_CID_IS1_L2 && + cid < LAN966X_VCAP_CID_IS1_MAX) + lookup = 2; + + return lookup; +} + +static int lan966x_vcap_is2_cid_to_lookup(int cid) { if (cid >= LAN966X_VCAP_CID_IS2_L1 && cid < LAN966X_VCAP_CID_IS2_MAX) @@ -81,6 +105,67 @@ static int lan966x_vcap_cid_to_lookup(int cid) return 0; } +/* Return the list of keysets for the vcap port configuration */ +static int +lan966x_vcap_is1_get_port_keysets(struct net_device *ndev, int lookup, + struct vcap_keyset_list *keysetlist, + u16 l3_proto) +{ + struct lan966x_port *port = netdev_priv(ndev); + struct lan966x *lan966x = port->lan966x; + u32 val; + + val = lan_rd(lan966x, ANA_VCAP_S1_CFG(port->chip_port, lookup)); + + /* Collect all keysets for the port in a list */ + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { + switch (ANA_VCAP_S1_CFG_KEY_IP4_CFG_GET(val)) { + case VCAP_IS1_PS_IPV4_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); + break; + case VCAP_IS1_PS_IPV4_5TUPLE_IP4: + vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP4); + break; + case VCAP_IS1_PS_IPV4_NORMAL: + vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); + break; + } + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { + switch (ANA_VCAP_S1_CFG_KEY_IP6_CFG_GET(val)) { + case VCAP_IS1_PS_IPV6_NORMAL: + case VCAP_IS1_PS_IPV6_NORMAL_IP6: + vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); + vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL_IP6); + break; + case VCAP_IS1_PS_IPV6_5TUPLE_IP6: + vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP6); + break; + case VCAP_IS1_PS_IPV6_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); + break; + case VCAP_IS1_PS_IPV6_5TUPLE_IP4: + vcap_keyset_list_add(keysetlist, VCAP_KFS_5TUPLE_IP4); + break; + case VCAP_IS1_PS_IPV6_DMAC_VID: + vcap_keyset_list_add(keysetlist, VCAP_KFS_DMAC_VID); + break; + } + } + + switch (ANA_VCAP_S1_CFG_KEY_OTHER_CFG_GET(val)) { + case VCAP_IS1_PS_OTHER_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_7TUPLE); + break; + case VCAP_IS1_PS_OTHER_NORMAL: + vcap_keyset_list_add(keysetlist, VCAP_KFS_NORMAL); + break; + } + + return 0; +} + static int lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup, struct vcap_keyset_list *keysetlist, @@ -180,11 +265,26 @@ lan966x_vcap_validate_keyset(struct net_device *dev, if (!kslist || kslist->cnt == 0) return VCAP_KFS_NO_VALUE; - lookup = lan966x_vcap_cid_to_lookup(rule->vcap_chain_id); keysetlist.max = ARRAY_SIZE(keysets); keysetlist.keysets = keysets; - err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist, - l3_proto); + + switch (admin->vtype) { + case VCAP_TYPE_IS1: + lookup = lan966x_vcap_is1_cid_to_lookup(rule->vcap_chain_id); + err = lan966x_vcap_is1_get_port_keysets(dev, lookup, &keysetlist, + l3_proto); + break; + case VCAP_TYPE_IS2: + lookup = lan966x_vcap_is2_cid_to_lookup(rule->vcap_chain_id); + err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist, + l3_proto); + break; + default: + pr_err("vcap type: %s not supported\n", + lan966x_vcaps[admin->vtype].name); + return VCAP_KFS_NO_VALUE; + } + if (err) return VCAP_KFS_NO_VALUE; @@ -197,17 +297,32 @@ lan966x_vcap_validate_keyset(struct net_device *dev, return VCAP_KFS_NO_VALUE; } -static bool lan966x_vcap_is_first_chain(struct vcap_rule *rule) +static bool lan966x_vcap_is2_is_first_chain(struct vcap_rule *rule) { return (rule->vcap_chain_id >= LAN966X_VCAP_CID_IS2_L0 && rule->vcap_chain_id < LAN966X_VCAP_CID_IS2_L1); } -static void lan966x_vcap_add_default_fields(struct net_device *dev, - struct vcap_admin *admin, - struct vcap_rule *rule) +static void lan966x_vcap_is1_add_default_fields(struct lan966x_port *port, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + u32 value, mask; + u32 lookup; + + if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, + &value, &mask)) + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, + ~BIT(port->chip_port)); + + lookup = lan966x_vcap_is1_cid_to_lookup(rule->vcap_chain_id); + vcap_rule_add_key_u32(rule, VCAP_KF_LOOKUP_INDEX, lookup, 0x3); +} + +static void lan966x_vcap_is2_add_default_fields(struct lan966x_port *port, + struct vcap_admin *admin, + struct vcap_rule *rule) { - struct lan966x_port *port = netdev_priv(dev); u32 value, mask; if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, @@ -215,7 +330,7 @@ static void lan966x_vcap_add_default_fields(struct net_device *dev, vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, ~BIT(port->chip_port)); - if (lan966x_vcap_is_first_chain(rule)) + if (lan966x_vcap_is2_is_first_chain(rule)) vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); else @@ -223,6 +338,26 @@ static void lan966x_vcap_add_default_fields(struct net_device *dev, VCAP_BIT_0); } +static void lan966x_vcap_add_default_fields(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + struct lan966x_port *port = netdev_priv(dev); + + switch (admin->vtype) { + case VCAP_TYPE_IS1: + lan966x_vcap_is1_add_default_fields(port, admin, rule); + break; + case VCAP_TYPE_IS2: + lan966x_vcap_is2_add_default_fields(port, admin, rule); + break; + default: + pr_err("vcap type: %s not supported\n", + lan966x_vcaps[admin->vtype].name); + break; + } +} + static void lan966x_vcap_cache_erase(struct vcap_admin *admin) { memset(admin->cache.keystream, 0, STREAMSIZE); @@ -464,8 +599,37 @@ static void lan966x_vcap_block_init(struct lan966x *lan966x, static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x, struct vcap_admin *admin) { - for (int p = 0; p < lan966x->num_phys_ports; ++p) - lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p)); + u32 val; + + switch (admin->vtype) { + case VCAP_TYPE_IS1: + val = ANA_VCAP_S1_CFG_KEY_IP6_CFG_SET(VCAP_IS1_PS_IPV6_5TUPLE_IP6) | + ANA_VCAP_S1_CFG_KEY_IP4_CFG_SET(VCAP_IS1_PS_IPV4_5TUPLE_IP4) | + ANA_VCAP_S1_CFG_KEY_OTHER_CFG_SET(VCAP_IS1_PS_OTHER_NORMAL); + + for (int p = 0; p < lan966x->num_phys_ports; ++p) { + if (!lan966x->ports[p]) + continue; + + for (int l = 0; l < LAN966X_IS1_LOOKUPS; ++l) + lan_wr(val, lan966x, ANA_VCAP_S1_CFG(p, l)); + + lan_rmw(ANA_VCAP_CFG_S1_ENA_SET(true), + ANA_VCAP_CFG_S1_ENA, lan966x, + ANA_VCAP_CFG(p)); + } + + break; + case VCAP_TYPE_IS2: + for (int p = 0; p < lan966x->num_phys_ports; ++p) + lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p)); + + break; + default: + pr_err("vcap type: %s not supported\n", + lan966x_vcaps[admin->vtype].name); + break; + } } int lan966x_vcap_init(struct lan966x *lan966x) @@ -506,6 +670,10 @@ int lan966x_vcap_init(struct lan966x *lan966x) lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(true), ANA_VCAP_S2_CFG_ENA, lan966x, ANA_VCAP_S2_CFG(lan966x->ports[p]->chip_port)); + + lan_rmw(ANA_VCAP_CFG_S1_ENA_SET(true), + ANA_VCAP_CFG_S1_ENA, lan966x, + ANA_VCAP_CFG(lan966x->ports[p]->chip_port)); } } -- cgit v1.2.3 From 135c2116fd03642b74fa2c0cd1cbd2614ca3d80c Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 7 Mar 2023 23:09:27 +0100 Subject: net: lan966x: Add TC support for IS1 VCAP Enable TC command to use IS1 VCAP Signed-off-by: Horatiu Vultur Signed-off-by: Jakub Kicinski --- .../ethernet/microchip/lan966x/lan966x_tc_flower.c | 105 ++++++++++++++++++++- 1 file changed, 102 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c index f960727ecaee..8391652c1c45 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -79,18 +79,61 @@ lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) VCAP_BIT_1); if (err) goto out; + } else if (st->l3_proto == ETH_P_IPV6 && + st->admin->vtype == VCAP_TYPE_IS1) { + /* Don't set any keys in this case */ + } else if (st->l3_proto == ETH_P_SNAP && + st->admin->vtype == VCAP_TYPE_IS1) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_ETYPE_LEN_IS, + VCAP_BIT_0); + if (err) + goto out; + + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_IP_SNAP_IS, + VCAP_BIT_1); + if (err) + goto out; + } else if (st->admin->vtype == VCAP_TYPE_IS1) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_ETYPE_LEN_IS, + VCAP_BIT_1); + if (err) + goto out; + + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, + st->l3_proto, ~0); + if (err) + goto out; } } if (match.mask->ip_proto) { st->l4_proto = match.key->ip_proto; if (st->l4_proto == IPPROTO_TCP) { + if (st->admin->vtype == VCAP_TYPE_IS1) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_UDP_IS, + VCAP_BIT_1); + if (err) + goto out; + } + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_TCP_IS, VCAP_BIT_1); if (err) goto out; } else if (st->l4_proto == IPPROTO_UDP) { + if (st->admin->vtype == VCAP_TYPE_IS1) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_UDP_IS, + VCAP_BIT_1); + if (err) + goto out; + } + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_TCP_IS, VCAP_BIT_0); @@ -112,12 +155,30 @@ out: return err; } +static int +lan966x_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st) +{ + if (st->admin->vtype != VCAP_TYPE_IS1) { + NL_SET_ERR_MSG_MOD(st->fco->common.extack, + "cvlan not supported in this VCAP"); + return -EINVAL; + } + + return vcap_tc_flower_handler_cvlan_usage(st); +} + static int lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st) { - return vcap_tc_flower_handler_vlan_usage(st, - VCAP_KF_8021Q_VID_CLS, - VCAP_KF_8021Q_PCP_CLS); + enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; + enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; + + if (st->admin->vtype == VCAP_TYPE_IS1) { + vid_key = VCAP_KF_8021Q_VID0; + pcp_key = VCAP_KF_8021Q_PCP0; + } + + return vcap_tc_flower_handler_vlan_usage(st, vid_key, pcp_key); } static int @@ -128,6 +189,7 @@ static int [FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage, [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage, [FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage, + [FLOW_DISSECTOR_KEY_CVLAN] = lan966x_tc_flower_handler_cvlan_usage, [FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage, [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage, [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage, @@ -143,6 +205,7 @@ static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, .fco = f, .vrule = vrule, .l3_proto = ETH_P_ALL, + .admin = admin, }; int err = 0; @@ -221,6 +284,31 @@ static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, return 0; } +/* Add the actionset that is the default for the VCAP type */ +static int lan966x_tc_set_actionset(struct vcap_admin *admin, + struct vcap_rule *vrule) +{ + enum vcap_actionfield_set aset; + int err = 0; + + switch (admin->vtype) { + case VCAP_TYPE_IS1: + aset = VCAP_AFS_S1; + break; + case VCAP_TYPE_IS2: + aset = VCAP_AFS_BASE_TYPE; + break; + default: + return -EINVAL; + } + + /* Do not overwrite any current actionset */ + if (vrule->actionset == VCAP_AFS_NO_VALUE) + err = vcap_set_rule_set_actionset(vrule, aset); + + return err; +} + static int lan966x_tc_flower_add(struct lan966x_port *port, struct flow_cls_offload *f, struct vcap_admin *admin, @@ -253,6 +341,13 @@ static int lan966x_tc_flower_add(struct lan966x_port *port, flow_action_for_each(idx, act, &frule->action) { switch (act->id) { case FLOW_ACTION_TRAP: + if (admin->vtype != VCAP_TYPE_IS2) { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Trap action not supported in this VCAP"); + err = -EOPNOTSUPP; + goto out; + } + err = vcap_rule_add_action_bit(vrule, VCAP_AF_CPU_COPY_ENA, VCAP_BIT_1); @@ -266,6 +361,10 @@ static int lan966x_tc_flower_add(struct lan966x_port *port, break; case FLOW_ACTION_GOTO: + err = lan966x_tc_set_actionset(admin, vrule); + if (err) + goto out; + break; default: NL_SET_ERR_MSG_MOD(f->common.extack, -- cgit v1.2.3 From b3762a9db39c40a2e508a8edb91217676a74f299 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 7 Mar 2023 23:09:28 +0100 Subject: net: lan966x: Add TC filter chaining support for IS1 and IS2 VCAPs Allow rules to be chained between IS1 VCAP and IS2 VCAP. Chaining between IS1 lookups or between IS2 lookups are not supported by the hardware. Signed-off-by: Horatiu Vultur Signed-off-by: Jakub Kicinski --- .../ethernet/microchip/lan966x/lan966x_tc_flower.c | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c index 8391652c1c45..570ac28736e0 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -309,6 +309,75 @@ static int lan966x_tc_set_actionset(struct vcap_admin *admin, return err; } +static int lan966x_tc_add_rule_link_target(struct vcap_admin *admin, + struct vcap_rule *vrule, + int target_cid) +{ + int link_val = target_cid % VCAP_CID_LOOKUP_SIZE; + int err; + + if (!link_val) + return 0; + + switch (admin->vtype) { + case VCAP_TYPE_IS1: + /* Choose IS1 specific NXT_IDX key (for chaining rules from IS1) */ + err = vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX_SEL, + 1, ~0); + if (err) + return err; + + return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_GEN_IDX, + link_val, ~0); + case VCAP_TYPE_IS2: + /* Add IS2 specific PAG key (for chaining rules from IS1) */ + return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG, + link_val, ~0); + default: + break; + } + return 0; +} + +static int lan966x_tc_add_rule_link(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_rule *vrule, + struct flow_cls_offload *f, + int to_cid) +{ + struct vcap_admin *to_admin = vcap_find_admin(vctrl, to_cid); + int diff, err = 0; + + if (!to_admin) { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Unknown destination chain"); + return -EINVAL; + } + + diff = vcap_chain_offset(vctrl, f->common.chain_index, to_cid); + if (!diff) + return 0; + + /* Between IS1 and IS2 the PAG value is used */ + if (admin->vtype == VCAP_TYPE_IS1 && to_admin->vtype == VCAP_TYPE_IS2) { + /* This works for IS1->IS2 */ + err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_VAL, diff); + if (err) + return err; + + err = vcap_rule_add_action_u32(vrule, VCAP_AF_PAG_OVERRIDE_MASK, + 0xff); + if (err) + return err; + } else { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Unsupported chain destination"); + return -EOPNOTSUPP; + } + + return err; +} + static int lan966x_tc_flower_add(struct lan966x_port *port, struct flow_cls_offload *f, struct vcap_admin *admin, @@ -336,6 +405,11 @@ static int lan966x_tc_flower_add(struct lan966x_port *port, if (err) goto out; + err = lan966x_tc_add_rule_link_target(admin, vrule, + f->common.chain_index); + if (err) + goto out; + frule = flow_cls_offload_flow_rule(f); flow_action_for_each(idx, act, &frule->action) { @@ -365,6 +439,12 @@ static int lan966x_tc_flower_add(struct lan966x_port *port, if (err) goto out; + err = lan966x_tc_add_rule_link(port->lan966x->vcap_ctrl, + admin, vrule, + f, act->chain_index); + if (err) + goto out; + break; default: NL_SET_ERR_MSG_MOD(f->common.extack, -- cgit v1.2.3 From 44d706fde75518781f6859febb0979cf5e7327d4 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 7 Mar 2023 23:09:29 +0100 Subject: net: lan966x: Add support for IS1 VCAP ethernet protocol types IS1 VCAP has it's own list of supported ethernet protocol types which is different than the IS2 VCAP. Therefore separate the list of known protocol types based on the VCAP type. Signed-off-by: Horatiu Vultur Signed-off-by: Jakub Kicinski --- .../ethernet/microchip/lan966x/lan966x_tc_flower.c | 36 +++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c index 570ac28736e0..47b2f7579dd2 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -5,14 +5,34 @@ #include "vcap_api_client.h" #include "vcap_tc.h" -static bool lan966x_tc_is_known_etype(u16 etype) +static bool lan966x_tc_is_known_etype(struct vcap_tc_flower_parse_usage *st, + u16 etype) { - switch (etype) { - case ETH_P_ALL: - case ETH_P_ARP: - case ETH_P_IP: - case ETH_P_IPV6: - return true; + switch (st->admin->vtype) { + case VCAP_TYPE_IS1: + switch (etype) { + case ETH_P_ALL: + case ETH_P_ARP: + case ETH_P_IP: + case ETH_P_IPV6: + return true; + } + break; + case VCAP_TYPE_IS2: + switch (etype) { + case ETH_P_ALL: + case ETH_P_ARP: + case ETH_P_IP: + case ETH_P_IPV6: + case ETH_P_SNAP: + case ETH_P_802_2: + return true; + } + break; + default: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, + "VCAP type not supported"); + return false; } return false; @@ -69,7 +89,7 @@ lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) flow_rule_match_basic(st->frule, &match); if (match.mask->n_proto) { st->l3_proto = be16_to_cpu(match.key->n_proto); - if (!lan966x_tc_is_known_etype(st->l3_proto)) { + if (!lan966x_tc_is_known_etype(st, st->l3_proto)) { err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, st->l3_proto, ~0); if (err) -- cgit v1.2.3 From 438b406055cd21105aad77db7938ee4720b09bee Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 7 Mar 2023 20:45:56 -0700 Subject: tun: flag the device as supporting FMODE_NOWAIT tun already checks for both O_NONBLOCK and IOCB_NOWAIT in its read and write iter handlers, so it's fully ready for FMODE_NOWAIT. But for some reason it doesn't set it. Rectify that oversight. Signed-off-by: Jens Axboe Link: https://lore.kernel.org/r/3f7dc1f0-79ca-d85c-4d16-8c12c5bd492d@kernel.dk Signed-off-by: Jakub Kicinski --- drivers/net/tun.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index ad653b32b2f0..4c7f74904c25 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -3463,6 +3463,8 @@ static int tun_chr_open(struct inode *inode, struct file * file) sock_set_flag(&tfile->sk, SOCK_ZEROCOPY); + /* tun groks IOCB_NOWAIT just fine, mark it as such */ + file->f_mode |= FMODE_NOWAIT; return 0; } -- cgit v1.2.3 From f758bfec377ad2f15d7683473b1db1cfbf8e1bb0 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 7 Mar 2023 21:18:21 -0700 Subject: tap: add support for IOCB_NOWAIT The tap driver already supports passing in nonblocking state based on O_NONBLOCK, add support for checking IOCB_NOWAIT as well. With that done, we can flag it with FMODE_NOWAIT as well. Signed-off-by: Jens Axboe Link: https://lore.kernel.org/r/8f859870-e6e2-09ca-9c0f-a2aa7c984fb2@kernel.dk Signed-off-by: Jakub Kicinski --- drivers/net/tap.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 8941aa199ea3..ce993cc75bf3 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -555,6 +555,9 @@ static int tap_open(struct inode *inode, struct file *file) goto err_put; } + /* tap groks IOCB_NOWAIT just fine, mark it as such */ + file->f_mode |= FMODE_NOWAIT; + dev_put(tap->dev); rtnl_unlock(); @@ -771,8 +774,12 @@ static ssize_t tap_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct tap_queue *q = file->private_data; + int noblock = 0; + + if ((file->f_flags & O_NONBLOCK) || (iocb->ki_flags & IOCB_NOWAIT)) + noblock = 1; - return tap_get_user(q, NULL, from, file->f_flags & O_NONBLOCK); + return tap_get_user(q, NULL, from, noblock); } /* Put packet to the user space buffer */ @@ -888,8 +895,12 @@ static ssize_t tap_read_iter(struct kiocb *iocb, struct iov_iter *to) struct file *file = iocb->ki_filp; struct tap_queue *q = file->private_data; ssize_t len = iov_iter_count(to), ret; + int noblock = 0; + + if ((file->f_flags & O_NONBLOCK) || (iocb->ki_flags & IOCB_NOWAIT)) + noblock = 1; - ret = tap_do_read(q, to, file->f_flags & O_NONBLOCK, NULL); + ret = tap_do_read(q, to, noblock, NULL); ret = min_t(ssize_t, ret, len); if (ret > 0) iocb->ki_pos = ret; -- cgit v1.2.3 From f94b9bed12e8244717512941494fe83c94773a58 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 9 Mar 2023 15:57:11 +0000 Subject: net: sfp: add A2h presence flag The hwmon code wants to know when it is safe to access the A2h data stored in a separate address. We indicate that this is present when we have SFF-8472 compliance and the lack of an address-change sequence., The same conditions are also true if we want to access other controls and status in the A2h address. So let's make a flag to indicate whether we can access it, instead of repeating the conditions throughout the code. For now, only convert the hwmon code. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index c02cad6478a8..4ff07b5a5590 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -255,6 +255,8 @@ struct sfp { unsigned int module_power_mW; unsigned int module_t_start_up; unsigned int module_t_wait; + + bool have_a2; bool tx_fault_ignore; const struct sfp_quirk *quirk; @@ -1453,20 +1455,10 @@ static void sfp_hwmon_probe(struct work_struct *work) static int sfp_hwmon_insert(struct sfp *sfp) { - if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE) - return 0; - - if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) - return 0; - - if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) - /* This driver in general does not support address - * change. - */ - return 0; - - mod_delayed_work(system_wq, &sfp->hwmon_probe, 1); - sfp->hwmon_tries = R_PROBE_RETRY_SLOW; + if (sfp->have_a2 && sfp->id.ext.diagmon & SFP_DIAGMON_DDM) { + mod_delayed_work(system_wq, &sfp->hwmon_probe, 1); + sfp->hwmon_tries = R_PROBE_RETRY_SLOW; + } return 0; } @@ -1916,6 +1908,18 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) return 0; } +static int sfp_module_parse_sff8472(struct sfp *sfp) +{ + /* If the module requires address swap mode, warn about it */ + if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) + dev_warn(sfp->dev, + "module address swap to access page 0xA2 is not supported.\n"); + else + sfp->have_a2 = true; + + return 0; +} + static int sfp_sm_mod_probe(struct sfp *sfp, bool report) { /* SFP module inserted - read I2C data */ @@ -2053,10 +2057,11 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) return -EINVAL; } - /* If the module requires address swap mode, warn about it */ - if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) - dev_warn(sfp->dev, - "module address swap to access page 0xA2 is not supported.\n"); + if (sfp->id.ext.sff8472_compliance != SFP_SFF8472_COMPLIANCE_NONE) { + ret = sfp_module_parse_sff8472(sfp); + if (ret < 0) + return ret; + } /* Parse the module power requirement */ ret = sfp_module_parse_power(sfp); @@ -2103,6 +2108,7 @@ static void sfp_sm_mod_remove(struct sfp *sfp) memset(&sfp->id, 0, sizeof(sfp->id)); sfp->module_power_mW = 0; + sfp->have_a2 = false; dev_info(sfp->dev, "module removed\n"); } -- cgit v1.2.3 From 5daed426f012a1c0db0048339e359ee98a2c8752 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 9 Mar 2023 15:57:16 +0000 Subject: net: sfp: only use soft polling if we have A2h access The soft state bits are stored in the A2h memory space, and require SFF-8472 compliance. This is what our have_a2 flag tells us, so use this to indicate whether we should attempt to use the soft signals. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 4ff07b5a5590..39e3095796d0 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2284,7 +2284,11 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) sfp->sm_dev_state != SFP_DEV_UP) break; - if (!(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)) + /* Only use the soft state bits if we have access to the A2h + * memory, which implies that we have some level of SFF-8472 + * compliance. + */ + if (sfp->have_a2) sfp_soft_start_poll(sfp); sfp_module_tx_enable(sfp); -- cgit v1.2.3 From 939a3f2a76e3216253b0a596b67ce9bd9ff6af51 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Thu, 9 Mar 2023 02:54:21 -0800 Subject: ptp_ocp: add force_irq to xilinx_spi configuration Flashing firmware via devlink flash was failing on PTP OCP devices because it is using Quad SPI mode, but the driver was not properly behaving. With force_irq flag landed it now can be fixed. Signed-off-by: Vadim Fedorenko Link: https://lore.kernel.org/r/20230309105421.2953451-1-vadfed@meta.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_ocp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 4bbaccd543ad..2b63f3487645 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -662,6 +662,7 @@ static struct ocp_resource ocp_fb_resource[] = { .num_chipselect = 1, .bits_per_word = 8, .num_devices = 1, + .force_irq = true, .devices = &(struct spi_board_info) { .modalias = "spi-nor", }, -- cgit v1.2.3 From bca93b20c3976ff5ca9fe373cf7acd0277492ae8 Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Thu, 9 Mar 2023 13:06:12 +0530 Subject: net: ethernet: ti: am65-cpsw: Update name of Serdes PHY The bindings for the am65-cpsw driver use the name "serdes" to refer to the Serdes PHY. Thus, update the name used for the Serdes PHY within the am65_cpsw_init_serdes_phy() function from "serdes-phy" to "serdes". Signed-off-by: Siddharth Vadapalli Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 25996826edab..4cfbc1c2b1c4 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1466,7 +1466,7 @@ static void am65_cpsw_disable_serdes_phy(struct am65_cpsw_common *common) static int am65_cpsw_init_serdes_phy(struct device *dev, struct device_node *port_np, struct am65_cpsw_port *port) { - const char *name = "serdes-phy"; + const char *name = "serdes"; struct phy *phy; int ret; -- cgit v1.2.3 From 05ccd8d8a15e6b9c99b86cf8a2fd78e3f0c60a84 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Thu, 9 Mar 2023 11:59:04 +0000 Subject: sfc: support offloading TC VLAN push/pop actions to the MAE EF100 can pop and/or push up to two VLAN tags. Signed-off-by: Edward Cree Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230309115904.56442-1-edward.cree@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mae.c | 16 ++++++++++++++++ drivers/net/ethernet/sfc/mcdi.h | 5 +++++ drivers/net/ethernet/sfc/tc.c | 42 +++++++++++++++++++++++++++++++++++++++++ drivers/net/ethernet/sfc/tc.h | 4 ++++ 4 files changed, 67 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 2d32abe5f478..c53d354c1fb2 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -682,6 +682,10 @@ int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) size_t outlen; int rc; + MCDI_POPULATE_DWORD_2(inbuf, MAE_ACTION_SET_ALLOC_IN_FLAGS, + MAE_ACTION_SET_ALLOC_IN_VLAN_PUSH, act->vlan_push, + MAE_ACTION_SET_ALLOC_IN_VLAN_POP, act->vlan_pop); + MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID, MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID, @@ -694,6 +698,18 @@ int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL); MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID, MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL); + if (act->vlan_push) { + MCDI_SET_WORD_BE(inbuf, MAE_ACTION_SET_ALLOC_IN_VLAN0_TCI_BE, + act->vlan_tci[0]); + MCDI_SET_WORD_BE(inbuf, MAE_ACTION_SET_ALLOC_IN_VLAN0_PROTO_BE, + act->vlan_proto[0]); + } + if (act->vlan_push >= 2) { + MCDI_SET_WORD_BE(inbuf, MAE_ACTION_SET_ALLOC_IN_VLAN1_TCI_BE, + act->vlan_tci[1]); + MCDI_SET_WORD_BE(inbuf, MAE_ACTION_SET_ALLOC_IN_VLAN1_PROTO_BE, + act->vlan_proto[1]); + } MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID, MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL); if (act->deliver) diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index b139b76febff..454e9d51a4c2 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -233,6 +233,11 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); ((void)BUILD_BUG_ON_ZERO(_field ## _LEN != 2), \ le16_to_cpu(*(__force const __le16 *)MCDI_STRUCT_PTR(_buf, _field))) /* Write a 16-bit field defined in the protocol as being big-endian. */ +#define MCDI_SET_WORD_BE(_buf, _field, _value) do { \ + BUILD_BUG_ON(MC_CMD_ ## _field ## _LEN != 2); \ + BUILD_BUG_ON(MC_CMD_ ## _field ## _OFST & 1); \ + *(__force __be16 *)MCDI_PTR(_buf, _field) = (_value); \ + } while (0) #define MCDI_STRUCT_SET_WORD_BE(_buf, _field, _value) do { \ BUILD_BUG_ON(_field ## _LEN != 2); \ BUILD_BUG_ON(_field ## _OFST & 1); \ diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index deeaab9ee761..2b07bb2fd735 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -286,6 +286,8 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, /* For details of action order constraints refer to SF-123102-TC-1§12.6.1 */ enum efx_tc_action_order { + EFX_TC_AO_VLAN_POP, + EFX_TC_AO_VLAN_PUSH, EFX_TC_AO_COUNT, EFX_TC_AO_DELIVER }; @@ -294,6 +296,20 @@ static bool efx_tc_flower_action_order_ok(const struct efx_tc_action_set *act, enum efx_tc_action_order new) { switch (new) { + case EFX_TC_AO_VLAN_POP: + if (act->vlan_pop >= 2) + return false; + /* If we've already pushed a VLAN, we can't then pop it; + * the hardware would instead try to pop an existing VLAN + * before pushing the new one. + */ + if (act->vlan_push) + return false; + fallthrough; + case EFX_TC_AO_VLAN_PUSH: + if (act->vlan_push >= 2) + return false; + fallthrough; case EFX_TC_AO_COUNT: if (act->count) return false; @@ -393,6 +409,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, flow_action_for_each(i, fa, &fr->action) { struct efx_tc_action_set save; + u16 tci; if (!act) { /* more actions after a non-pipe action */ @@ -494,6 +511,31 @@ static int efx_tc_flower_replace(struct efx_nic *efx, } *act = save; break; + case FLOW_ACTION_VLAN_POP: + if (act->vlan_push) { + act->vlan_push--; + } else if (efx_tc_flower_action_order_ok(act, EFX_TC_AO_VLAN_POP)) { + act->vlan_pop++; + } else { + NL_SET_ERR_MSG_MOD(extack, + "More than two VLAN pops, or action order violated"); + rc = -EINVAL; + goto release; + } + break; + case FLOW_ACTION_VLAN_PUSH: + if (!efx_tc_flower_action_order_ok(act, EFX_TC_AO_VLAN_PUSH)) { + rc = -EINVAL; + NL_SET_ERR_MSG_MOD(extack, + "More than two VLAN pushes, or action order violated"); + goto release; + } + tci = fa->vlan.vid & VLAN_VID_MASK; + tci |= fa->vlan.prio << VLAN_PRIO_SHIFT; + act->vlan_tci[act->vlan_push] = cpu_to_be16(tci); + act->vlan_proto[act->vlan_push] = fa->vlan.proto; + act->vlan_push++; + break; default: NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled action %u", fa->id); diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 418ce8c13a06..542853f60c2a 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -19,7 +19,11 @@ #define IS_ALL_ONES(v) (!(typeof (v))~(v)) struct efx_tc_action_set { + u16 vlan_push:2; + u16 vlan_pop:2; u16 deliver:1; + __be16 vlan_tci[2]; /* TCIs for vlan_push */ + __be16 vlan_proto[2]; /* Ethertypes for vlan_push */ struct efx_tc_counter_index *count; u32 dest_mport; u32 fw_id; /* index of this entry in firmware actions table */ -- cgit v1.2.3 From 95b744508d4d5135ae2a096ff3f0ee882bcc52b3 Mon Sep 17 00:00:00 2001 From: Muhammad Usama Anjum Date: Fri, 10 Mar 2023 03:52:05 +0500 Subject: qede: remove linux/version.h and linux/compiler.h make versioncheck reports the following: ./drivers/net/ethernet/qlogic/qede/qede.h: 10 linux/version.h not needed. ./drivers/net/ethernet/qlogic/qede/qede_ethtool.c: 7 linux/version.h not needed. So remove linux/version.h from both of these files. Also remove linux/compiler.h while at it as it is also not being used. Signed-off-by: Muhammad Usama Anjum Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230309225206.2473644-1-usama.anjum@collabora.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/qlogic/qede/qede.h | 2 -- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 1 - 2 files changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index f90dcfe9ee68..f9931ecb7baa 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -6,8 +6,6 @@ #ifndef _QEDE_H_ #define _QEDE_H_ -#include -#include #include #include #include diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 8034d812d5a0..374a86b875a3 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -4,7 +4,6 @@ * Copyright (c) 2019-2020 Marvell International Ltd. */ -#include #include #include #include -- cgit v1.2.3 From 25e289e1f52e1f4fb1d07622c6a24f8d8a8e420d Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Wed, 1 Mar 2023 16:20:58 +0200 Subject: wifi: ath11k: use proper regulatory reference for bands Currently, during regulatory event, 2 GHz/5 GHz is referred to as 2G/5G including variable names. However, there is no such entity as 2G or 5G. Re-name such occurences to its proper name. No functional changes. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230110121024.14051-2-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath11k/reg.c | 20 ++++++------ drivers/net/wireless/ath/ath11k/wmi.c | 58 ++++++++++++++++++----------------- drivers/net/wireless/ath/ath11k/wmi.h | 28 ++++++++--------- 3 files changed, 54 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 6fae4e61ede7..c231abdf49cb 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -619,7 +619,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab, u32 flags; char alpha2[3]; - num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; + num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules; if (!num_rules) goto ret; @@ -644,20 +644,20 @@ ath11k_reg_build_regd(struct ath11k_base *ab, alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region), reg_info->dfs_region, num_rules); /* Update reg_rules[] below. Firmware is expected to - * send these rules in order(2G rules first and then 5G) + * send these rules in order(2 GHz rules first and then 5 GHz) */ for (; i < num_rules; i++) { - if (reg_info->num_2g_reg_rules && - (i < reg_info->num_2g_reg_rules)) { - reg_rule = reg_info->reg_rules_2g_ptr + i; + if (reg_info->num_2ghz_reg_rules && + (i < reg_info->num_2ghz_reg_rules)) { + reg_rule = reg_info->reg_rules_2ghz_ptr + i; max_bw = min_t(u16, reg_rule->max_bw, - reg_info->max_bw_2g); + reg_info->max_bw_2ghz); flags = 0; - } else if (reg_info->num_5g_reg_rules && - (j < reg_info->num_5g_reg_rules)) { - reg_rule = reg_info->reg_rules_5g_ptr + j++; + } else if (reg_info->num_5ghz_reg_rules && + (j < reg_info->num_5ghz_reg_rules)) { + reg_rule = reg_info->reg_rules_5ghz_ptr + j++; max_bw = min_t(u16, reg_rule->max_bw, - reg_info->max_bw_5g); + reg_info->max_bw_5ghz); /* FW doesn't pass NL80211_RRF_AUTO_BW flag for * BW Auto correction, we can enable this by default diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 815f49c3b3ad..ecd51d285103 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4959,7 +4959,7 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, const void **tb; const struct wmi_reg_chan_list_cc_event *chan_list_event_hdr; struct wmi_regulatory_rule_struct *wmi_reg_rule; - u32 num_2g_reg_rules, num_5g_reg_rules; + u32 num_2ghz_reg_rules, num_5ghz_reg_rules; int ret; ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory channel list\n"); @@ -4978,10 +4978,10 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, return -EPROTO; } - reg_info->num_2g_reg_rules = chan_list_event_hdr->num_2g_reg_rules; - reg_info->num_5g_reg_rules = chan_list_event_hdr->num_5g_reg_rules; + reg_info->num_2ghz_reg_rules = chan_list_event_hdr->num_2ghz_reg_rules; + reg_info->num_5ghz_reg_rules = chan_list_event_hdr->num_5ghz_reg_rules; - if (!(reg_info->num_2g_reg_rules + reg_info->num_5g_reg_rules)) { + if (!(reg_info->num_2ghz_reg_rules + reg_info->num_5ghz_reg_rules)) { ath11k_warn(ab, "No regulatory rules available in the event info\n"); kfree(tb); return -EINVAL; @@ -5008,46 +5008,48 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_FAIL) reg_info->status_code = REG_SET_CC_STATUS_FAIL; - reg_info->min_bw_2g = chan_list_event_hdr->min_bw_2g; - reg_info->max_bw_2g = chan_list_event_hdr->max_bw_2g; - reg_info->min_bw_5g = chan_list_event_hdr->min_bw_5g; - reg_info->max_bw_5g = chan_list_event_hdr->max_bw_5g; + reg_info->min_bw_2ghz = chan_list_event_hdr->min_bw_2ghz; + reg_info->max_bw_2ghz = chan_list_event_hdr->max_bw_2ghz; + reg_info->min_bw_5ghz = chan_list_event_hdr->min_bw_5ghz; + reg_info->max_bw_5ghz = chan_list_event_hdr->max_bw_5ghz; - num_2g_reg_rules = reg_info->num_2g_reg_rules; - num_5g_reg_rules = reg_info->num_5g_reg_rules; + num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules; + num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules; ath11k_dbg(ab, ATH11K_DBG_WMI, - "%s:cc %s dsf %d BW: min_2g %d max_2g %d min_5g %d max_5g %d", + "%s:cc %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", __func__, reg_info->alpha2, reg_info->dfs_region, - reg_info->min_bw_2g, reg_info->max_bw_2g, - reg_info->min_bw_5g, reg_info->max_bw_5g); + reg_info->min_bw_2ghz, reg_info->max_bw_2ghz, + reg_info->min_bw_5ghz, reg_info->max_bw_5ghz); ath11k_dbg(ab, ATH11K_DBG_WMI, - "%s: num_2g_reg_rules %d num_5g_reg_rules %d", __func__, - num_2g_reg_rules, num_5g_reg_rules); + "%s: num_2ghz_reg_rules %d num_5ghz_reg_rules %d", __func__, + num_2ghz_reg_rules, num_5ghz_reg_rules); wmi_reg_rule = (struct wmi_regulatory_rule_struct *)((u8 *)chan_list_event_hdr + sizeof(*chan_list_event_hdr) + sizeof(struct wmi_tlv)); - if (num_2g_reg_rules) { - reg_info->reg_rules_2g_ptr = create_reg_rules_from_wmi(num_2g_reg_rules, - wmi_reg_rule); - if (!reg_info->reg_rules_2g_ptr) { + if (num_2ghz_reg_rules) { + reg_info->reg_rules_2ghz_ptr = + create_reg_rules_from_wmi(num_2ghz_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_2ghz_ptr) { kfree(tb); - ath11k_warn(ab, "Unable to Allocate memory for 2g rules\n"); + ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n"); return -ENOMEM; } } - if (num_5g_reg_rules) { - wmi_reg_rule += num_2g_reg_rules; - reg_info->reg_rules_5g_ptr = create_reg_rules_from_wmi(num_5g_reg_rules, - wmi_reg_rule); - if (!reg_info->reg_rules_5g_ptr) { + if (num_5ghz_reg_rules) { + wmi_reg_rule += num_2ghz_reg_rules; + reg_info->reg_rules_5ghz_ptr = + create_reg_rules_from_wmi(num_5ghz_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_5ghz_ptr) { kfree(tb); - ath11k_warn(ab, "Unable to Allocate memory for 5g rules\n"); + ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n"); return -ENOMEM; } } @@ -6619,8 +6621,8 @@ fallback: WARN_ON(1); mem_free: if (reg_info) { - kfree(reg_info->reg_rules_2g_ptr); - kfree(reg_info->reg_rules_5g_ptr); + kfree(reg_info->reg_rules_2ghz_ptr); + kfree(reg_info->reg_rules_5ghz_ptr); kfree(reg_info); } return ret; diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 63dbb085949f..858403f190c2 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4129,14 +4129,14 @@ struct cur_regulatory_info { u8 alpha2[REG_ALPHA2_LEN + 1]; u32 dfs_region; u32 phybitmap; - u32 min_bw_2g; - u32 max_bw_2g; - u32 min_bw_5g; - u32 max_bw_5g; - u32 num_2g_reg_rules; - u32 num_5g_reg_rules; - struct cur_reg_rule *reg_rules_2g_ptr; - struct cur_reg_rule *reg_rules_5g_ptr; + u32 min_bw_2ghz; + u32 max_bw_2ghz; + u32 min_bw_5ghz; + u32 max_bw_5ghz; + u32 num_2ghz_reg_rules; + u32 num_5ghz_reg_rules; + struct cur_reg_rule *reg_rules_2ghz_ptr; + struct cur_reg_rule *reg_rules_5ghz_ptr; }; struct wmi_reg_chan_list_cc_event { @@ -4148,12 +4148,12 @@ struct wmi_reg_chan_list_cc_event { u32 domain_code; u32 dfs_region; u32 phybitmap; - u32 min_bw_2g; - u32 max_bw_2g; - u32 min_bw_5g; - u32 max_bw_5g; - u32 num_2g_reg_rules; - u32 num_5g_reg_rules; + u32 min_bw_2ghz; + u32 max_bw_2ghz; + u32 min_bw_5ghz; + u32 max_bw_5ghz; + u32 num_2ghz_reg_rules; + u32 num_5ghz_reg_rules; } __packed; struct wmi_regulatory_rule_struct { -- cgit v1.2.3 From 91fa00fa69224aae5afb720c5e68b22e4c4f7333 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Wed, 1 Mar 2023 16:20:59 +0200 Subject: wifi: ath11k: add support to parse new WMI event for 6 GHz In order to support different power levels of 6 GHz AP and client, new WMI event for regulatory - WMI_REG_CHAN_LIST_CC_EXT_EVENTID is added in firmware. This event provides new parameters required for 6 GHz regulatory rules. Add support for parsing 2.4 GHz, 5 GHz and 6 GHz reg rules and other parameters from WMI_REG_CHAN_LIST_CC_EXT_EVENTID. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 Signed-off-by: Lavanya Suresh Signed-off-by: Wen Gong Signed-off-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230110121024.14051-3-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath11k/reg.c | 37 ++- drivers/net/wireless/ath/ath11k/wmi.c | 418 ++++++++++++++++++++++++++++++++-- drivers/net/wireless/ath/ath11k/wmi.h | 163 ++++++++++++- 3 files changed, 584 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index c231abdf49cb..08621bd13a12 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -613,7 +613,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab, { struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; struct cur_reg_rule *reg_rule; - u8 i = 0, j = 0; + u8 i = 0, j = 0, k = 0; u8 num_rules; u16 max_bw; u32 flags; @@ -621,6 +621,12 @@ ath11k_reg_build_regd(struct ath11k_base *ab, num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules; + /* FIXME: Currently taking reg rules for 6 GHz only from Indoor AP mode list. + * This can be updated after complete 6 GHz regulatory support is added. + */ + if (reg_info->is_ext_reg_event) + num_rules += reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP]; + if (!num_rules) goto ret; @@ -666,6 +672,14 @@ ath11k_reg_build_regd(struct ath11k_base *ab, * per other BW rule flags we pass from here */ flags = NL80211_RRF_AUTO_BW; + } else if (reg_info->is_ext_reg_event && + reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] && + (k < reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP])) { + reg_rule = reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP] + + k++; + max_bw = min_t(u16, reg_rule->max_bw, + reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP]); + flags = NL80211_RRF_AUTO_BW; } else { break; } @@ -693,12 +707,21 @@ ath11k_reg_build_regd(struct ath11k_base *ab, continue; } - ath11k_dbg(ab, ATH11K_DBG_REG, - "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", - i + 1, reg_rule->start_freq, reg_rule->end_freq, - max_bw, reg_rule->ant_gain, reg_rule->reg_power, - tmp_regd->reg_rules[i].dfs_cac_ms, - flags); + if (reg_info->is_ext_reg_event) { + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n", + i + 1, reg_rule->start_freq, reg_rule->end_freq, + max_bw, reg_rule->ant_gain, reg_rule->reg_power, + tmp_regd->reg_rules[i].dfs_cac_ms, flags, + reg_rule->psd_flag, reg_rule->psd_eirp); + } else { + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, reg_rule->start_freq, reg_rule->end_freq, + max_bw, reg_rule->ant_gain, reg_rule->reg_power, + tmp_regd->reg_rules[i].dfs_cac_ms, + flags); + } } tmp_regd->n_reg_rules = i; diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index ecd51d285103..34731d0d1efc 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -105,6 +105,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { = { .min_len = sizeof(struct wmi_vdev_stopped_event) }, [WMI_TAG_REG_CHAN_LIST_CC_EVENT] = { .min_len = sizeof(struct wmi_reg_chan_list_cc_event) }, + [WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT] + = { .min_len = sizeof(struct wmi_reg_chan_list_cc_ext_event) }, [WMI_TAG_MGMT_RX_HDR] = { .min_len = sizeof(struct wmi_mgmt_rx_hdr) }, [WMI_TAG_MGMT_TX_COMPL_EVENT] @@ -3974,6 +3976,10 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg, wmi_cfg->sched_params = tg_cfg->sched_params; wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count; wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count; + wmi_cfg->host_service_flags &= + ~(1 << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT); + wmi_cfg->host_service_flags |= (tg_cfg->is_reg_cc_ext_event_supported << + WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT); } static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, @@ -4192,6 +4198,10 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab) ab->hw_params.hw_ops->wmi_init_config(ab, &config); + if (test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, + ab->wmi_ab.svc_map)) + config.is_reg_cc_ext_event_supported = 1; + memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config)); init_param.res_cfg = &wmi_sc->wlan_resource_config; @@ -4995,18 +5005,11 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, reg_info->phy_id = chan_list_event_hdr->phy_id; reg_info->ctry_code = chan_list_event_hdr->country_id; reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code; - if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_PASS) - reg_info->status_code = REG_SET_CC_STATUS_PASS; - else if (chan_list_event_hdr->status_code == WMI_REG_CURRENT_ALPHA2_NOT_FOUND) - reg_info->status_code = REG_CURRENT_ALPHA2_NOT_FOUND; - else if (chan_list_event_hdr->status_code == WMI_REG_INIT_ALPHA2_NOT_FOUND) - reg_info->status_code = REG_INIT_ALPHA2_NOT_FOUND; - else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_CHANGE_NOT_ALLOWED) - reg_info->status_code = REG_SET_CC_CHANGE_NOT_ALLOWED; - else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_NO_MEMORY) - reg_info->status_code = REG_SET_CC_STATUS_NO_MEMORY; - else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_FAIL) - reg_info->status_code = REG_SET_CC_STATUS_FAIL; + + reg_info->status_code = + ath11k_wmi_cc_setting_code_to_reg(chan_list_event_hdr->status_code); + + reg_info->is_ext_reg_event = false; reg_info->min_bw_2ghz = chan_list_event_hdr->min_bw_2ghz; reg_info->max_bw_2ghz = chan_list_event_hdr->max_bw_2ghz; @@ -5060,6 +5063,372 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, return 0; } +static struct cur_reg_rule +*create_ext_reg_rules_from_wmi(u32 num_reg_rules, + struct wmi_regulatory_ext_rule *wmi_reg_rule) +{ + struct cur_reg_rule *reg_rule_ptr; + u32 count; + + reg_rule_ptr = kcalloc(num_reg_rules, sizeof(*reg_rule_ptr), GFP_ATOMIC); + + if (!reg_rule_ptr) + return NULL; + + for (count = 0; count < num_reg_rules; count++) { + reg_rule_ptr[count].start_freq = + u32_get_bits(wmi_reg_rule[count].freq_info, + REG_RULE_START_FREQ); + reg_rule_ptr[count].end_freq = + u32_get_bits(wmi_reg_rule[count].freq_info, + REG_RULE_END_FREQ); + reg_rule_ptr[count].max_bw = + u32_get_bits(wmi_reg_rule[count].bw_pwr_info, + REG_RULE_MAX_BW); + reg_rule_ptr[count].reg_power = + u32_get_bits(wmi_reg_rule[count].bw_pwr_info, + REG_RULE_REG_PWR); + reg_rule_ptr[count].ant_gain = + u32_get_bits(wmi_reg_rule[count].bw_pwr_info, + REG_RULE_ANT_GAIN); + reg_rule_ptr[count].flags = + u32_get_bits(wmi_reg_rule[count].flag_info, + REG_RULE_FLAGS); + reg_rule_ptr[count].psd_flag = + u32_get_bits(wmi_reg_rule[count].psd_power_info, + REG_RULE_PSD_INFO); + reg_rule_ptr[count].psd_eirp = + u32_get_bits(wmi_reg_rule[count].psd_power_info, + REG_RULE_PSD_EIRP); + } + + return reg_rule_ptr; +} + +static u8 +ath11k_invalid_5ghz_reg_ext_rules_from_wmi(u32 num_reg_rules, + const struct wmi_regulatory_ext_rule *rule) +{ + u8 num_invalid_5ghz_rules = 0; + u32 count, start_freq; + + for (count = 0; count < num_reg_rules; count++) { + start_freq = u32_get_bits(rule[count].freq_info, + REG_RULE_START_FREQ); + + if (start_freq >= ATH11K_MIN_6G_FREQ) + num_invalid_5ghz_rules++; + } + + return num_invalid_5ghz_rules; +} + +static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, + struct sk_buff *skb, + struct cur_regulatory_info *reg_info) +{ + const void **tb; + const struct wmi_reg_chan_list_cc_ext_event *ext_chan_list_event_hdr; + struct wmi_regulatory_ext_rule *ext_wmi_reg_rule; + u32 num_2ghz_reg_rules, num_5ghz_reg_rules; + u32 num_6ghz_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 num_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 total_reg_rules = 0; + int ret, i, j, num_invalid_5ghz_ext_rules = 0; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory ext channel list\n"); + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ext_chan_list_event_hdr = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; + if (!ext_chan_list_event_hdr) { + ath11k_warn(ab, "failed to fetch reg chan list ext update ev\n"); + kfree(tb); + return -EPROTO; + } + + reg_info->num_2ghz_reg_rules = + ext_chan_list_event_hdr->num_2ghz_reg_rules; + reg_info->num_5ghz_reg_rules = + ext_chan_list_event_hdr->num_5ghz_reg_rules; + reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] = + ext_chan_list_event_hdr->num_6ghz_reg_rules_ap_lpi; + reg_info->num_6ghz_rules_ap[WMI_REG_STANDARD_POWER_AP] = + ext_chan_list_event_hdr->num_6ghz_reg_rules_ap_sp; + reg_info->num_6ghz_rules_ap[WMI_REG_VERY_LOW_POWER_AP] = + ext_chan_list_event_hdr->num_6ghz_reg_rules_ap_vlp; + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->num_6ghz_rules_client[WMI_REG_INDOOR_AP][i] = + ext_chan_list_event_hdr->num_6ghz_reg_rules_client_lpi[i]; + reg_info->num_6ghz_rules_client[WMI_REG_STANDARD_POWER_AP][i] = + ext_chan_list_event_hdr->num_6ghz_reg_rules_client_sp[i]; + reg_info->num_6ghz_rules_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ext_chan_list_event_hdr->num_6ghz_reg_rules_client_vlp[i]; + } + + num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules; + num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules; + + total_reg_rules += num_2ghz_reg_rules; + total_reg_rules += num_5ghz_reg_rules; + + if ((num_2ghz_reg_rules > MAX_REG_RULES) || + (num_5ghz_reg_rules > MAX_REG_RULES)) { + ath11k_warn(ab, "Num reg rules for 2.4 GHz/5 GHz exceeds max limit (num_2ghz_reg_rules: %d num_5ghz_reg_rules: %d max_rules: %d)\n", + num_2ghz_reg_rules, num_5ghz_reg_rules, MAX_REG_RULES); + kfree(tb); + return -EINVAL; + } + + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + num_6ghz_reg_rules_ap[i] = reg_info->num_6ghz_rules_ap[i]; + + if (num_6ghz_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) { + ath11k_warn(ab, "Num 6 GHz reg rules for AP mode(%d) exceeds max limit (num_6ghz_reg_rules_ap: %d, max_rules: %d)\n", + i, num_6ghz_reg_rules_ap[i], MAX_6GHZ_REG_RULES); + kfree(tb); + return -EINVAL; + } + + total_reg_rules += num_6ghz_reg_rules_ap[i]; + } + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + num_6ghz_client[WMI_REG_INDOOR_AP][i] = + reg_info->num_6ghz_rules_client[WMI_REG_INDOOR_AP][i]; + total_reg_rules += num_6ghz_client[WMI_REG_INDOOR_AP][i]; + + num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + reg_info->num_6ghz_rules_client[WMI_REG_STANDARD_POWER_AP][i]; + total_reg_rules += num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i]; + + num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + reg_info->num_6ghz_rules_client[WMI_REG_VERY_LOW_POWER_AP][i]; + total_reg_rules += num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i]; + + if ((num_6ghz_client[WMI_REG_INDOOR_AP][i] > MAX_6GHZ_REG_RULES) || + (num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] > + MAX_6GHZ_REG_RULES) || + (num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] > + MAX_6GHZ_REG_RULES)) { + ath11k_warn(ab, + "Num 6 GHz client reg rules exceeds max limit, for client(type: %d)\n", + i); + kfree(tb); + return -EINVAL; + } + } + + if (!total_reg_rules) { + ath11k_warn(ab, "No reg rules available\n"); + kfree(tb); + return -EINVAL; + } + + memcpy(reg_info->alpha2, &ext_chan_list_event_hdr->alpha2, + REG_ALPHA2_LEN); + + reg_info->dfs_region = ext_chan_list_event_hdr->dfs_region; + reg_info->phybitmap = ext_chan_list_event_hdr->phybitmap; + reg_info->num_phy = ext_chan_list_event_hdr->num_phy; + reg_info->phy_id = ext_chan_list_event_hdr->phy_id; + reg_info->ctry_code = ext_chan_list_event_hdr->country_id; + reg_info->reg_dmn_pair = ext_chan_list_event_hdr->domain_code; + + reg_info->status_code = + ath11k_wmi_cc_setting_code_to_reg(ext_chan_list_event_hdr->status_code); + + reg_info->is_ext_reg_event = true; + + reg_info->min_bw_2ghz = ext_chan_list_event_hdr->min_bw_2ghz; + reg_info->max_bw_2ghz = ext_chan_list_event_hdr->max_bw_2ghz; + reg_info->min_bw_5ghz = ext_chan_list_event_hdr->min_bw_5ghz; + reg_info->max_bw_5ghz = ext_chan_list_event_hdr->max_bw_5ghz; + + reg_info->min_bw_6ghz_ap[WMI_REG_INDOOR_AP] = + ext_chan_list_event_hdr->min_bw_6ghz_ap_lpi; + reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP] = + ext_chan_list_event_hdr->max_bw_6ghz_ap_lpi; + reg_info->min_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = + ext_chan_list_event_hdr->min_bw_6ghz_ap_sp; + reg_info->max_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = + ext_chan_list_event_hdr->max_bw_6ghz_ap_sp; + reg_info->min_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = + ext_chan_list_event_hdr->min_bw_6ghz_ap_vlp; + reg_info->max_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = + ext_chan_list_event_hdr->max_bw_6ghz_ap_vlp; + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->min_bw_6ghz_client[WMI_REG_INDOOR_AP][i] = + ext_chan_list_event_hdr->min_bw_6ghz_client_lpi[i]; + reg_info->max_bw_6ghz_client[WMI_REG_INDOOR_AP][i] = + ext_chan_list_event_hdr->max_bw_6ghz_client_lpi[i]; + reg_info->min_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + ext_chan_list_event_hdr->min_bw_6ghz_client_sp[i]; + reg_info->max_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + ext_chan_list_event_hdr->max_bw_6ghz_client_sp[i]; + reg_info->min_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ext_chan_list_event_hdr->min_bw_6ghz_client_vlp[i]; + reg_info->max_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ext_chan_list_event_hdr->max_bw_6ghz_client_vlp[i]; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "%s:cc_ext %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", + __func__, reg_info->alpha2, reg_info->dfs_region, + reg_info->min_bw_2ghz, reg_info->max_bw_2ghz, + reg_info->min_bw_5ghz, reg_info->max_bw_5ghz); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "num_2ghz_reg_rules %d num_5ghz_reg_rules %d", + num_2ghz_reg_rules, num_5ghz_reg_rules); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "num_6ghz_reg_rules_ap_lpi: %d num_6ghz_reg_rules_ap_sp: %d num_6ghz_reg_rules_ap_vlp: %d", + num_6ghz_reg_rules_ap[WMI_REG_INDOOR_AP], + num_6ghz_reg_rules_ap[WMI_REG_STANDARD_POWER_AP], + num_6ghz_reg_rules_ap[WMI_REG_VERY_LOW_POWER_AP]); + + j = WMI_REG_DEFAULT_CLIENT; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz Regular client: num_6ghz_reg_rules_lpi: %d num_6ghz_reg_rules_sp: %d num_6ghz_reg_rules_vlp: %d", + num_6ghz_client[WMI_REG_INDOOR_AP][j], + num_6ghz_client[WMI_REG_STANDARD_POWER_AP][j], + num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][j]); + + j = WMI_REG_SUBORDINATE_CLIENT; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz Subordinate client: num_6ghz_reg_rules_lpi: %d num_6ghz_reg_rules_sp: %d num_6ghz_reg_rules_vlp: %d", + num_6ghz_client[WMI_REG_INDOOR_AP][j], + num_6ghz_client[WMI_REG_STANDARD_POWER_AP][j], + num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][j]); + + ext_wmi_reg_rule = + (struct wmi_regulatory_ext_rule *)((u8 *)ext_chan_list_event_hdr + + sizeof(*ext_chan_list_event_hdr) + + sizeof(struct wmi_tlv)); + if (num_2ghz_reg_rules) { + reg_info->reg_rules_2ghz_ptr = + create_ext_reg_rules_from_wmi(num_2ghz_reg_rules, + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_2ghz_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n"); + return -ENOMEM; + } + } + + ext_wmi_reg_rule += num_2ghz_reg_rules; + + /* Firmware might include 6 GHz reg rule in 5 GHz rule list + * for few countries along with separate 6 GHz rule. + * Having same 6 GHz reg rule in 5 GHz and 6 GHz rules list + * causes intersect check to be true, and same rules will be + * shown multiple times in iw cmd. + * Hence, avoid parsing 6 GHz rule from 5 GHz reg rule list + */ + num_invalid_5ghz_ext_rules = + ath11k_invalid_5ghz_reg_ext_rules_from_wmi(num_5ghz_reg_rules, + ext_wmi_reg_rule); + + if (num_invalid_5ghz_ext_rules) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "CC: %s 5 GHz reg rules number %d from fw, %d number of invalid 5 GHz rules", + reg_info->alpha2, reg_info->num_5ghz_reg_rules, + num_invalid_5ghz_ext_rules); + + num_5ghz_reg_rules = num_5ghz_reg_rules - num_invalid_5ghz_ext_rules; + reg_info->num_5ghz_reg_rules = num_5ghz_reg_rules; + } + + if (num_5ghz_reg_rules) { + reg_info->reg_rules_5ghz_ptr = + create_ext_reg_rules_from_wmi(num_5ghz_reg_rules, + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_5ghz_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n"); + return -ENOMEM; + } + } + + /* We have adjusted the number of 5 GHz reg rules above. But still those + * many rules needs to be adjusted in ext_wmi_reg_rule. + * + * NOTE: num_invalid_5ghz_ext_rules will be 0 for rest other cases. + */ + ext_wmi_reg_rule += (num_5ghz_reg_rules + num_invalid_5ghz_ext_rules); + + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + reg_info->reg_rules_6ghz_ap_ptr[i] = + create_ext_reg_rules_from_wmi(num_6ghz_reg_rules_ap[i], + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_6ghz_ap_ptr[i]) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 6 GHz AP rules\n"); + return -ENOMEM; + } + + ext_wmi_reg_rule += num_6ghz_reg_rules_ap[i]; + } + + for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) { + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->reg_rules_6ghz_client_ptr[j][i] = + create_ext_reg_rules_from_wmi(num_6ghz_client[j][i], + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_6ghz_client_ptr[j][i]) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 6 GHz client rules\n"); + return -ENOMEM; + } + + ext_wmi_reg_rule += num_6ghz_client[j][i]; + } + } + + reg_info->client_type = ext_chan_list_event_hdr->client_type; + reg_info->rnr_tpe_usable = ext_chan_list_event_hdr->rnr_tpe_usable; + reg_info->unspecified_ap_usable = + ext_chan_list_event_hdr->unspecified_ap_usable; + reg_info->domain_code_6ghz_ap[WMI_REG_INDOOR_AP] = + ext_chan_list_event_hdr->domain_code_6ghz_ap_lpi; + reg_info->domain_code_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = + ext_chan_list_event_hdr->domain_code_6ghz_ap_sp; + reg_info->domain_code_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = + ext_chan_list_event_hdr->domain_code_6ghz_ap_vlp; + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->domain_code_6ghz_client[WMI_REG_INDOOR_AP][i] = + ext_chan_list_event_hdr->domain_code_6ghz_client_lpi[i]; + reg_info->domain_code_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + ext_chan_list_event_hdr->domain_code_6ghz_client_sp[i]; + reg_info->domain_code_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ext_chan_list_event_hdr->domain_code_6ghz_client_vlp[i]; + } + + reg_info->domain_code_6ghz_super_id = + ext_chan_list_event_hdr->domain_code_6ghz_super_id; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "6 GHz client_type: %d domain_code_6ghz_super_id: %d", + reg_info->client_type, reg_info->domain_code_6ghz_super_id); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory ext channel list\n"); + + kfree(tb); + return 0; +} + static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *skb, struct wmi_peer_delete_resp_event *peer_del_resp) { @@ -6507,12 +6876,14 @@ static bool ath11k_reg_is_world_alpha(char *alpha) return false; } -static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb) +static int ath11k_reg_chan_list_event(struct ath11k_base *ab, + struct sk_buff *skb, + enum wmi_reg_chan_list_cmd_type id) { struct cur_regulatory_info *reg_info = NULL; struct ieee80211_regdomain *regd = NULL; bool intersect = false; - int ret = 0, pdev_idx; + int ret = 0, pdev_idx, i, j; struct ath11k *ar; reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); @@ -6521,7 +6892,11 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk goto fallback; } - ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info); + if (id == WMI_REG_CHAN_LIST_CC_ID) + ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info); + else + ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info); + if (ret) { ath11k_warn(ab, "failed to extract regulatory info from received event\n"); goto fallback; @@ -6623,6 +6998,14 @@ mem_free: if (reg_info) { kfree(reg_info->reg_rules_2ghz_ptr); kfree(reg_info->reg_rules_5ghz_ptr); + if (reg_info->is_ext_reg_event) { + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) + kfree(reg_info->reg_rules_6ghz_ap_ptr[i]); + + for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) + kfree(reg_info->reg_rules_6ghz_client_ptr[j][i]); + } kfree(reg_info); } return ret; @@ -8054,7 +8437,10 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) ath11k_service_ready_ext2_event(ab, skb); break; case WMI_REG_CHAN_LIST_CC_EVENTID: - ath11k_reg_chan_list_event(ab, skb); + ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_ID); + break; + case WMI_REG_CHAN_LIST_CC_EXT_EVENTID: + ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_EXT_ID); break; case WMI_READY_EVENTID: ath11k_ready_event(ab, skb); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 858403f190c2..3ec3fa0aa5c8 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -797,6 +797,7 @@ enum wmi_tlv_event_id { WMI_RMC_NEW_LEADER_EVENTID = WMI_TLV_CMD(WMI_GRP_RMC), WMI_REG_CHAN_LIST_CC_EVENTID = WMI_TLV_CMD(WMI_GRP_REGULATORY), WMI_11D_NEW_COUNTRY_EVENTID, + WMI_REG_CHAN_LIST_CC_EXT_EVENTID, WMI_NDI_CAP_RSP_EVENTID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), WMI_NDP_INITIATOR_RSP_EVENTID, WMI_NDP_RESPONDER_RSP_EVENTID, @@ -1865,6 +1866,8 @@ enum wmi_tlv_tag { WMI_TAG_PDEV_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, WMI_TAG_PDEV_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMD, WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, + WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, + WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD, WMI_TAG_MAX @@ -2097,6 +2100,7 @@ enum wmi_tlv_service { /* The second 128 bits */ WMI_MAX_EXT_SERVICE = 256, + WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326, /* The third 128 bits */ @@ -2313,6 +2317,8 @@ struct wmi_init_cmd { #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) +#define WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT 4 + struct wmi_resource_config { u32 tlv_header; u32 num_vdevs; @@ -2372,6 +2378,15 @@ struct wmi_resource_config { u32 sched_params; u32 twt_ap_pdev_count; u32 twt_ap_sta_count; + u32 max_nlo_ssids; + u32 num_pkt_filters; + u32 num_max_sta_vdevs; + u32 max_bssid_indicator; + u32 ul_resp_config; + u32 msdu_flow_override_config0; + u32 msdu_flow_override_config1; + u32 flags2; + u32 host_service_flags; } __packed; struct wmi_service_ready_event { @@ -2854,6 +2869,8 @@ struct rx_reorder_queue_remove_params { #define REG_RULE_MAX_BW 0x0000ffff #define REG_RULE_REG_PWR 0x00ff0000 #define REG_RULE_ANT_GAIN 0xff000000 +#define REG_RULE_PSD_INFO BIT(0) +#define REG_RULE_PSD_EIRP 0xff0000 #define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0) #define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1) @@ -4049,6 +4066,7 @@ struct wmi_he_rate_set { #define MAX_REG_RULES 10 #define REG_ALPHA2_LEN 2 +#define MAX_6GHZ_REG_RULES 5 enum wmi_start_event_param { WMI_VDEV_START_RESP_EVENT = 0, @@ -4079,16 +4097,6 @@ enum wmi_vdev_start_resp_status_code { WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN = 4, }; -; -enum cc_setting_code { - REG_SET_CC_STATUS_PASS = 0, - REG_CURRENT_ALPHA2_NOT_FOUND = 1, - REG_INIT_ALPHA2_NOT_FOUND = 2, - REG_SET_CC_CHANGE_NOT_ALLOWED = 3, - REG_SET_CC_STATUS_NO_MEMORY = 4, - REG_SET_CC_STATUS_FAIL = 5, -}; - /* Regaulatory Rule Flags Passed by FW */ #define REGULATORY_CHAN_DISABLED BIT(0) #define REGULATORY_CHAN_NO_IR BIT(1) @@ -4102,13 +4110,72 @@ enum cc_setting_code { #define REGULATORY_CHAN_NO_20MHZ BIT(11) #define REGULATORY_CHAN_NO_10MHZ BIT(12) -enum { +enum wmi_reg_chan_list_cmd_type { + WMI_REG_CHAN_LIST_CC_ID = 0, + WMI_REG_CHAN_LIST_CC_EXT_ID = 1, +}; + +enum wmi_reg_cc_setting_code { WMI_REG_SET_CC_STATUS_PASS = 0, WMI_REG_CURRENT_ALPHA2_NOT_FOUND = 1, WMI_REG_INIT_ALPHA2_NOT_FOUND = 2, WMI_REG_SET_CC_CHANGE_NOT_ALLOWED = 3, WMI_REG_SET_CC_STATUS_NO_MEMORY = 4, WMI_REG_SET_CC_STATUS_FAIL = 5, + + /* add new setting code above, update in + * @enum cc_setting_code as well. + * Also handle it in ath11k_wmi_cc_setting_code_to_reg() + */ +}; + +enum cc_setting_code { + REG_SET_CC_STATUS_PASS = 0, + REG_CURRENT_ALPHA2_NOT_FOUND = 1, + REG_INIT_ALPHA2_NOT_FOUND = 2, + REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + REG_SET_CC_STATUS_NO_MEMORY = 4, + REG_SET_CC_STATUS_FAIL = 5, + + /* add new setting code above, update in + * @enum wmi_reg_cc_setting_code as well. + */ +}; + +static inline enum cc_setting_code +ath11k_wmi_cc_setting_code_to_reg(enum wmi_reg_cc_setting_code status_code) +{ + switch (status_code) { + case WMI_REG_SET_CC_STATUS_PASS: + return REG_SET_CC_STATUS_PASS; + case WMI_REG_CURRENT_ALPHA2_NOT_FOUND: + return REG_CURRENT_ALPHA2_NOT_FOUND; + case WMI_REG_INIT_ALPHA2_NOT_FOUND: + return REG_INIT_ALPHA2_NOT_FOUND; + case WMI_REG_SET_CC_CHANGE_NOT_ALLOWED: + return REG_SET_CC_CHANGE_NOT_ALLOWED; + case WMI_REG_SET_CC_STATUS_NO_MEMORY: + return REG_SET_CC_STATUS_NO_MEMORY; + case WMI_REG_SET_CC_STATUS_FAIL: + return REG_SET_CC_STATUS_FAIL; + } + + return REG_SET_CC_STATUS_FAIL; +} + +enum wmi_reg_6ghz_ap_type { + WMI_REG_INDOOR_AP = 0, + WMI_REG_STANDARD_POWER_AP = 1, + WMI_REG_VERY_LOW_POWER_AP = 2, + + WMI_REG_CURRENT_MAX_AP_TYPE, + WMI_REG_MAX_AP_TYPE = 7, +}; + +enum wmi_reg_6ghz_client_type { + WMI_REG_DEFAULT_CLIENT = 0, + WMI_REG_SUBORDINATE_CLIENT = 1, + WMI_REG_MAX_CLIENT_TYPE = 2, }; struct cur_reg_rule { @@ -4118,6 +4185,8 @@ struct cur_reg_rule { u8 reg_power; u8 ant_gain; u16 flags; + bool psd_flag; + s8 psd_eirp; }; struct cur_regulatory_info { @@ -4137,6 +4206,22 @@ struct cur_regulatory_info { u32 num_5ghz_reg_rules; struct cur_reg_rule *reg_rules_2ghz_ptr; struct cur_reg_rule *reg_rules_5ghz_ptr; + bool is_ext_reg_event; + enum wmi_reg_6ghz_client_type client_type; + bool rnr_tpe_usable; + bool unspecified_ap_usable; + u8 domain_code_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u8 domain_code_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 domain_code_6ghz_super_id; + u32 min_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 max_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 min_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 max_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 num_6ghz_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 num_6ghz_rules_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + struct cur_reg_rule *reg_rules_6ghz_ap_ptr[WMI_REG_CURRENT_MAX_AP_TYPE]; + struct cur_reg_rule *reg_rules_6ghz_client_ptr + [WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; }; struct wmi_reg_chan_list_cc_event { @@ -4163,6 +4248,61 @@ struct wmi_regulatory_rule_struct { u32 flag_info; }; +#define WMI_REG_CLIENT_MAX 4 + +struct wmi_reg_chan_list_cc_ext_event { + u32 status_code; + u32 phy_id; + u32 alpha2; + u32 num_phy; + u32 country_id; + u32 domain_code; + u32 dfs_region; + u32 phybitmap; + u32 min_bw_2ghz; + u32 max_bw_2ghz; + u32 min_bw_5ghz; + u32 max_bw_5ghz; + u32 num_2ghz_reg_rules; + u32 num_5ghz_reg_rules; + u32 client_type; + u32 rnr_tpe_usable; + u32 unspecified_ap_usable; + u32 domain_code_6ghz_ap_lpi; + u32 domain_code_6ghz_ap_sp; + u32 domain_code_6ghz_ap_vlp; + u32 domain_code_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + u32 domain_code_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + u32 domain_code_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + u32 domain_code_6ghz_super_id; + u32 min_bw_6ghz_ap_sp; + u32 max_bw_6ghz_ap_sp; + u32 min_bw_6ghz_ap_lpi; + u32 max_bw_6ghz_ap_lpi; + u32 min_bw_6ghz_ap_vlp; + u32 max_bw_6ghz_ap_vlp; + u32 min_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + u32 max_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + u32 min_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + u32 max_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + u32 min_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + u32 max_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + u32 num_6ghz_reg_rules_ap_sp; + u32 num_6ghz_reg_rules_ap_lpi; + u32 num_6ghz_reg_rules_ap_vlp; + u32 num_6ghz_reg_rules_client_sp[WMI_REG_CLIENT_MAX]; + u32 num_6ghz_reg_rules_client_lpi[WMI_REG_CLIENT_MAX]; + u32 num_6ghz_reg_rules_client_vlp[WMI_REG_CLIENT_MAX]; +} __packed; + +struct wmi_regulatory_ext_rule { + u32 tlv_header; + u32 freq_info; + u32 bw_pwr_info; + u32 flag_info; + u32 psd_power_info; +} __packed; + struct wmi_vdev_delete_resp_event { u32 vdev_id; } __packed; @@ -5358,6 +5498,7 @@ struct target_resource_config { u32 sched_params; u32 twt_ap_pdev_count; u32 twt_ap_sta_count; + u8 is_reg_cc_ext_event_supported; }; enum wmi_debug_log_param { -- cgit v1.2.3 From e238e62ba8868a784e485eb94451c87cd1b85cee Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Wed, 1 Mar 2023 16:20:59 +0200 Subject: wifi: ath11k: add debug prints in regulatory WMI event processing Add some more debug prints in processing regulatory WMI event in order to increase more debuggability. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230110121024.14051-4-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath11k/reg.c | 2 +- drivers/net/wireless/ath/ath11k/wmi.c | 207 ++++++++++++++++++++++++---------- drivers/net/wireless/ath/ath11k/wmi.h | 142 +++++++++++++++++++++++ 3 files changed, 291 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 08621bd13a12..67443457f4da 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -646,7 +646,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab, tmp_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region); ath11k_dbg(ab, ATH11K_DBG_REG, - "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", + "Country %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region), reg_info->dfs_region, num_rules); /* Update reg_rules[] below. Firmware is expected to diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 34731d0d1efc..27f3fceb33c5 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4925,6 +4925,26 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf return 0; } +static void ath11k_print_reg_rule(struct ath11k_base *ab, const char *band, + u32 num_reg_rules, + struct cur_reg_rule *reg_rule_ptr) +{ + struct cur_reg_rule *reg_rule = reg_rule_ptr; + u32 count; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "number of reg rules in %s band: %d\n", + band, num_reg_rules); + + for (count = 0; count < num_reg_rules; count++) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "reg rule %d: (%d - %d @ %d) (%d, %d) (FLAGS %d)\n", + count + 1, reg_rule->start_freq, reg_rule->end_freq, + reg_rule->max_bw, reg_rule->ant_gain, + reg_rule->reg_power, reg_rule->flags); + reg_rule++; + } +} + static struct cur_reg_rule *create_reg_rules_from_wmi(u32 num_reg_rules, struct wmi_regulatory_rule_struct *wmi_reg_rule) @@ -5006,6 +5026,10 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, reg_info->ctry_code = chan_list_event_hdr->country_id; reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "status_code %s", + ath11k_cc_status_to_str(reg_info->status_code)); + reg_info->status_code = ath11k_wmi_cc_setting_code_to_reg(chan_list_event_hdr->status_code); @@ -5020,13 +5044,13 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules; ath11k_dbg(ab, ATH11K_DBG_WMI, - "%s:cc %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", - __func__, reg_info->alpha2, reg_info->dfs_region, + "cc %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", + reg_info->alpha2, reg_info->dfs_region, reg_info->min_bw_2ghz, reg_info->max_bw_2ghz, reg_info->min_bw_5ghz, reg_info->max_bw_5ghz); ath11k_dbg(ab, ATH11K_DBG_WMI, - "%s: num_2ghz_reg_rules %d num_5ghz_reg_rules %d", __func__, + "num_2ghz_reg_rules %d num_5ghz_reg_rules %d", num_2ghz_reg_rules, num_5ghz_reg_rules); wmi_reg_rule = @@ -5043,6 +5067,10 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n"); return -ENOMEM; } + + ath11k_print_reg_rule(ab, "2 GHz", + num_2ghz_reg_rules, + reg_info->reg_rules_2ghz_ptr); } if (num_5ghz_reg_rules) { @@ -5055,6 +5083,10 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n"); return -ENOMEM; } + + ath11k_print_reg_rule(ab, "5 GHz", + num_5ghz_reg_rules, + reg_info->reg_rules_5ghz_ptr); } ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory channel list\n"); @@ -5128,7 +5160,7 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, struct cur_regulatory_info *reg_info) { const void **tb; - const struct wmi_reg_chan_list_cc_ext_event *ext_chan_list_event_hdr; + const struct wmi_reg_chan_list_cc_ext_event *ev; struct wmi_regulatory_ext_rule *ext_wmi_reg_rule; u32 num_2ghz_reg_rules, num_5ghz_reg_rules; u32 num_6ghz_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; @@ -5145,31 +5177,29 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, return ret; } - ext_chan_list_event_hdr = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; - if (!ext_chan_list_event_hdr) { + ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; + if (!ev) { ath11k_warn(ab, "failed to fetch reg chan list ext update ev\n"); kfree(tb); return -EPROTO; } - reg_info->num_2ghz_reg_rules = - ext_chan_list_event_hdr->num_2ghz_reg_rules; - reg_info->num_5ghz_reg_rules = - ext_chan_list_event_hdr->num_5ghz_reg_rules; + reg_info->num_2ghz_reg_rules = ev->num_2ghz_reg_rules; + reg_info->num_5ghz_reg_rules = ev->num_5ghz_reg_rules; reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] = - ext_chan_list_event_hdr->num_6ghz_reg_rules_ap_lpi; + ev->num_6ghz_reg_rules_ap_lpi; reg_info->num_6ghz_rules_ap[WMI_REG_STANDARD_POWER_AP] = - ext_chan_list_event_hdr->num_6ghz_reg_rules_ap_sp; + ev->num_6ghz_reg_rules_ap_sp; reg_info->num_6ghz_rules_ap[WMI_REG_VERY_LOW_POWER_AP] = - ext_chan_list_event_hdr->num_6ghz_reg_rules_ap_vlp; + ev->num_6ghz_reg_rules_ap_vlp; for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { reg_info->num_6ghz_rules_client[WMI_REG_INDOOR_AP][i] = - ext_chan_list_event_hdr->num_6ghz_reg_rules_client_lpi[i]; + ev->num_6ghz_reg_rules_client_lpi[i]; reg_info->num_6ghz_rules_client[WMI_REG_STANDARD_POWER_AP][i] = - ext_chan_list_event_hdr->num_6ghz_reg_rules_client_sp[i]; + ev->num_6ghz_reg_rules_client_sp[i]; reg_info->num_6ghz_rules_client[WMI_REG_VERY_LOW_POWER_AP][i] = - ext_chan_list_event_hdr->num_6ghz_reg_rules_client_vlp[i]; + ev->num_6ghz_reg_rules_client_vlp[i]; } num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules; @@ -5231,57 +5261,79 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, return -EINVAL; } - memcpy(reg_info->alpha2, &ext_chan_list_event_hdr->alpha2, - REG_ALPHA2_LEN); + memcpy(reg_info->alpha2, &ev->alpha2, REG_ALPHA2_LEN); - reg_info->dfs_region = ext_chan_list_event_hdr->dfs_region; - reg_info->phybitmap = ext_chan_list_event_hdr->phybitmap; - reg_info->num_phy = ext_chan_list_event_hdr->num_phy; - reg_info->phy_id = ext_chan_list_event_hdr->phy_id; - reg_info->ctry_code = ext_chan_list_event_hdr->country_id; - reg_info->reg_dmn_pair = ext_chan_list_event_hdr->domain_code; + reg_info->dfs_region = ev->dfs_region; + reg_info->phybitmap = ev->phybitmap; + reg_info->num_phy = ev->num_phy; + reg_info->phy_id = ev->phy_id; + reg_info->ctry_code = ev->country_id; + reg_info->reg_dmn_pair = ev->domain_code; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "status_code %s", + ath11k_cc_status_to_str(reg_info->status_code)); reg_info->status_code = - ath11k_wmi_cc_setting_code_to_reg(ext_chan_list_event_hdr->status_code); + ath11k_wmi_cc_setting_code_to_reg(ev->status_code); reg_info->is_ext_reg_event = true; - reg_info->min_bw_2ghz = ext_chan_list_event_hdr->min_bw_2ghz; - reg_info->max_bw_2ghz = ext_chan_list_event_hdr->max_bw_2ghz; - reg_info->min_bw_5ghz = ext_chan_list_event_hdr->min_bw_5ghz; - reg_info->max_bw_5ghz = ext_chan_list_event_hdr->max_bw_5ghz; + reg_info->min_bw_2ghz = ev->min_bw_2ghz; + reg_info->max_bw_2ghz = ev->max_bw_2ghz; + reg_info->min_bw_5ghz = ev->min_bw_5ghz; + reg_info->max_bw_5ghz = ev->max_bw_5ghz; reg_info->min_bw_6ghz_ap[WMI_REG_INDOOR_AP] = - ext_chan_list_event_hdr->min_bw_6ghz_ap_lpi; + ev->min_bw_6ghz_ap_lpi; reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP] = - ext_chan_list_event_hdr->max_bw_6ghz_ap_lpi; + ev->max_bw_6ghz_ap_lpi; reg_info->min_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = - ext_chan_list_event_hdr->min_bw_6ghz_ap_sp; + ev->min_bw_6ghz_ap_sp; reg_info->max_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = - ext_chan_list_event_hdr->max_bw_6ghz_ap_sp; + ev->max_bw_6ghz_ap_sp; reg_info->min_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = - ext_chan_list_event_hdr->min_bw_6ghz_ap_vlp; + ev->min_bw_6ghz_ap_vlp; reg_info->max_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = - ext_chan_list_event_hdr->max_bw_6ghz_ap_vlp; + ev->max_bw_6ghz_ap_vlp; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz AP BW: LPI (%d - %d), SP (%d - %d), VLP (%d - %d)\n", + reg_info->min_bw_6ghz_ap[WMI_REG_INDOOR_AP], + reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP], + reg_info->min_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP], + reg_info->max_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP], + reg_info->min_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP], + reg_info->max_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP]); for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { reg_info->min_bw_6ghz_client[WMI_REG_INDOOR_AP][i] = - ext_chan_list_event_hdr->min_bw_6ghz_client_lpi[i]; + ev->min_bw_6ghz_client_lpi[i]; reg_info->max_bw_6ghz_client[WMI_REG_INDOOR_AP][i] = - ext_chan_list_event_hdr->max_bw_6ghz_client_lpi[i]; + ev->max_bw_6ghz_client_lpi[i]; reg_info->min_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = - ext_chan_list_event_hdr->min_bw_6ghz_client_sp[i]; + ev->min_bw_6ghz_client_sp[i]; reg_info->max_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = - ext_chan_list_event_hdr->max_bw_6ghz_client_sp[i]; + ev->max_bw_6ghz_client_sp[i]; reg_info->min_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = - ext_chan_list_event_hdr->min_bw_6ghz_client_vlp[i]; + ev->min_bw_6ghz_client_vlp[i]; reg_info->max_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = - ext_chan_list_event_hdr->max_bw_6ghz_client_vlp[i]; + ev->max_bw_6ghz_client_vlp[i]; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz %s BW: LPI (%d - %d), SP (%d - %d), VLP (%d - %d)\n", + ath11k_6ghz_client_type_to_str(i), + reg_info->min_bw_6ghz_client[WMI_REG_INDOOR_AP][i], + reg_info->max_bw_6ghz_client[WMI_REG_INDOOR_AP][i], + reg_info->min_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i], + reg_info->max_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i], + reg_info->min_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i], + reg_info->max_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i]); } ath11k_dbg(ab, ATH11K_DBG_WMI, - "%s:cc_ext %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", - __func__, reg_info->alpha2, reg_info->dfs_region, + "cc_ext %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", + reg_info->alpha2, reg_info->dfs_region, reg_info->min_bw_2ghz, reg_info->max_bw_2ghz, reg_info->min_bw_5ghz, reg_info->max_bw_5ghz); @@ -5310,9 +5362,8 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][j]); ext_wmi_reg_rule = - (struct wmi_regulatory_ext_rule *)((u8 *)ext_chan_list_event_hdr - + sizeof(*ext_chan_list_event_hdr) - + sizeof(struct wmi_tlv)); + (struct wmi_regulatory_ext_rule *)((u8 *)ev + sizeof(*ev) + + sizeof(struct wmi_tlv)); if (num_2ghz_reg_rules) { reg_info->reg_rules_2ghz_ptr = create_ext_reg_rules_from_wmi(num_2ghz_reg_rules, @@ -5323,6 +5374,10 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n"); return -ENOMEM; } + + ath11k_print_reg_rule(ab, "2 GHz", + num_2ghz_reg_rules, + reg_info->reg_rules_2ghz_ptr); } ext_wmi_reg_rule += num_2ghz_reg_rules; @@ -5358,6 +5413,10 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n"); return -ENOMEM; } + + ath11k_print_reg_rule(ab, "5 GHz", + num_5ghz_reg_rules, + reg_info->reg_rules_5ghz_ptr); } /* We have adjusted the number of 5 GHz reg rules above. But still those @@ -5378,10 +5437,17 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, return -ENOMEM; } + ath11k_print_reg_rule(ab, ath11k_6ghz_ap_type_to_str(i), + num_6ghz_reg_rules_ap[i], + reg_info->reg_rules_6ghz_ap_ptr[i]); + ext_wmi_reg_rule += num_6ghz_reg_rules_ap[i]; } for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz AP type %s", ath11k_6ghz_ap_type_to_str(j)); + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { reg_info->reg_rules_6ghz_client_ptr[j][i] = create_ext_reg_rules_from_wmi(num_6ghz_client[j][i], @@ -5393,35 +5459,58 @@ static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, return -ENOMEM; } + ath11k_print_reg_rule(ab, + ath11k_6ghz_client_type_to_str(i), + num_6ghz_client[j][i], + reg_info->reg_rules_6ghz_client_ptr[j][i]); + ext_wmi_reg_rule += num_6ghz_client[j][i]; } } - reg_info->client_type = ext_chan_list_event_hdr->client_type; - reg_info->rnr_tpe_usable = ext_chan_list_event_hdr->rnr_tpe_usable; + reg_info->client_type = ev->client_type; + reg_info->rnr_tpe_usable = ev->rnr_tpe_usable; reg_info->unspecified_ap_usable = - ext_chan_list_event_hdr->unspecified_ap_usable; + ev->unspecified_ap_usable; reg_info->domain_code_6ghz_ap[WMI_REG_INDOOR_AP] = - ext_chan_list_event_hdr->domain_code_6ghz_ap_lpi; + ev->domain_code_6ghz_ap_lpi; reg_info->domain_code_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = - ext_chan_list_event_hdr->domain_code_6ghz_ap_sp; + ev->domain_code_6ghz_ap_sp; reg_info->domain_code_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = - ext_chan_list_event_hdr->domain_code_6ghz_ap_vlp; + ev->domain_code_6ghz_ap_vlp; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz reg info client type %s rnr_tpe_usable %d unspecified_ap_usable %d AP sub domain: lpi %s, sp %s, vlp %s\n", + ath11k_6ghz_client_type_to_str(reg_info->client_type), + reg_info->rnr_tpe_usable, + reg_info->unspecified_ap_usable, + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_lpi), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_sp), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_vlp)); for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { reg_info->domain_code_6ghz_client[WMI_REG_INDOOR_AP][i] = - ext_chan_list_event_hdr->domain_code_6ghz_client_lpi[i]; + ev->domain_code_6ghz_client_lpi[i]; reg_info->domain_code_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = - ext_chan_list_event_hdr->domain_code_6ghz_client_sp[i]; + ev->domain_code_6ghz_client_sp[i]; reg_info->domain_code_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = - ext_chan_list_event_hdr->domain_code_6ghz_client_vlp[i]; + ev->domain_code_6ghz_client_vlp[i]; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz client type %s client sub domain: lpi %s, sp %s, vlp %s\n", + ath11k_6ghz_client_type_to_str(i), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_lpi[i]), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_sp[i]), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_vlp[i]) + ); } - reg_info->domain_code_6ghz_super_id = - ext_chan_list_event_hdr->domain_code_6ghz_super_id; + reg_info->domain_code_6ghz_super_id = ev->domain_code_6ghz_super_id; - ath11k_dbg(ab, ATH11K_DBG_WMI, "6 GHz client_type: %d domain_code_6ghz_super_id: %d", - reg_info->client_type, reg_info->domain_code_6ghz_super_id); + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz client_type %s 6 GHz super domain %s", + ath11k_6ghz_client_type_to_str(reg_info->client_type), + ath11k_super_reg_6ghz_to_str(reg_info->domain_code_6ghz_super_id)); ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory ext channel list\n"); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 3ec3fa0aa5c8..b23b7a22bc9a 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4139,6 +4139,7 @@ enum cc_setting_code { /* add new setting code above, update in * @enum wmi_reg_cc_setting_code as well. + * Also handle it in ath11k_cc_status_to_str() */ }; @@ -4163,21 +4164,162 @@ ath11k_wmi_cc_setting_code_to_reg(enum wmi_reg_cc_setting_code status_code) return REG_SET_CC_STATUS_FAIL; } +static inline const char *ath11k_cc_status_to_str(enum cc_setting_code code) +{ + switch (code) { + case REG_SET_CC_STATUS_PASS: + return "REG_SET_CC_STATUS_PASS"; + case REG_CURRENT_ALPHA2_NOT_FOUND: + return "REG_CURRENT_ALPHA2_NOT_FOUND"; + case REG_INIT_ALPHA2_NOT_FOUND: + return "REG_INIT_ALPHA2_NOT_FOUND"; + case REG_SET_CC_CHANGE_NOT_ALLOWED: + return "REG_SET_CC_CHANGE_NOT_ALLOWED"; + case REG_SET_CC_STATUS_NO_MEMORY: + return "REG_SET_CC_STATUS_NO_MEMORY"; + case REG_SET_CC_STATUS_FAIL: + return "REG_SET_CC_STATUS_FAIL"; + } + + return "Unknown CC status"; +} + enum wmi_reg_6ghz_ap_type { WMI_REG_INDOOR_AP = 0, WMI_REG_STANDARD_POWER_AP = 1, WMI_REG_VERY_LOW_POWER_AP = 2, + /* add AP type above, handle in ath11k_6ghz_ap_type_to_str() + */ WMI_REG_CURRENT_MAX_AP_TYPE, WMI_REG_MAX_AP_TYPE = 7, }; +static inline const char * +ath11k_6ghz_ap_type_to_str(enum wmi_reg_6ghz_ap_type type) +{ + switch (type) { + case WMI_REG_INDOOR_AP: + return "INDOOR AP"; + case WMI_REG_STANDARD_POWER_AP: + return "STANDARD POWER AP"; + case WMI_REG_VERY_LOW_POWER_AP: + return "VERY LOW POWER AP"; + case WMI_REG_CURRENT_MAX_AP_TYPE: + return "CURRENT_MAX_AP_TYPE"; + case WMI_REG_MAX_AP_TYPE: + return "MAX_AP_TYPE"; + } + + return "unknown 6 GHz AP type"; +} + enum wmi_reg_6ghz_client_type { WMI_REG_DEFAULT_CLIENT = 0, WMI_REG_SUBORDINATE_CLIENT = 1, WMI_REG_MAX_CLIENT_TYPE = 2, + + /* add client type above, handle it in + * ath11k_6ghz_client_type_to_str() + */ }; +static inline const char * +ath11k_6ghz_client_type_to_str(enum wmi_reg_6ghz_client_type type) +{ + switch (type) { + case WMI_REG_DEFAULT_CLIENT: + return "DEFAULT CLIENT"; + case WMI_REG_SUBORDINATE_CLIENT: + return "SUBORDINATE CLIENT"; + case WMI_REG_MAX_CLIENT_TYPE: + return "MAX_CLIENT_TYPE"; + } + + return "unknown 6 GHz client type"; +} + +enum reg_subdomains_6ghz { + EMPTY_6GHZ = 0x0, + FCC1_CLIENT_LPI_REGULAR_6GHZ = 0x01, + FCC1_CLIENT_SP_6GHZ = 0x02, + FCC1_AP_LPI_6GHZ = 0x03, + FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6GHZ, + FCC1_AP_SP_6GHZ = 0x04, + ETSI1_LPI_6GHZ = 0x10, + ETSI1_VLP_6GHZ = 0x11, + ETSI2_LPI_6GHZ = 0x12, + ETSI2_VLP_6GHZ = 0x13, + APL1_LPI_6GHZ = 0x20, + APL1_VLP_6GHZ = 0x21, + + /* add sub-domain above, handle it in + * ath11k_sub_reg_6ghz_to_str() + */ +}; + +static inline const char * +ath11k_sub_reg_6ghz_to_str(enum reg_subdomains_6ghz sub_id) +{ + switch (sub_id) { + case EMPTY_6GHZ: + return "N/A"; + case FCC1_CLIENT_LPI_REGULAR_6GHZ: + return "FCC1_CLIENT_LPI_REGULAR_6GHZ"; + case FCC1_CLIENT_SP_6GHZ: + return "FCC1_CLIENT_SP_6GHZ"; + case FCC1_AP_LPI_6GHZ: + return "FCC1_AP_LPI_6GHZ/FCC1_CLIENT_LPI_SUBORDINATE"; + case FCC1_AP_SP_6GHZ: + return "FCC1_AP_SP_6GHZ"; + case ETSI1_LPI_6GHZ: + return "ETSI1_LPI_6GHZ"; + case ETSI1_VLP_6GHZ: + return "ETSI1_VLP_6GHZ"; + case ETSI2_LPI_6GHZ: + return "ETSI2_LPI_6GHZ"; + case ETSI2_VLP_6GHZ: + return "ETSI2_VLP_6GHZ"; + case APL1_LPI_6GHZ: + return "APL1_LPI_6GHZ"; + case APL1_VLP_6GHZ: + return "APL1_VLP_6GHZ"; + } + + return "unknown sub reg id"; +} + +enum reg_super_domain_6ghz { + FCC1_6GHZ = 0x01, + ETSI1_6GHZ = 0x02, + ETSI2_6GHZ = 0x03, + APL1_6GHZ = 0x04, + FCC1_6GHZ_CL = 0x05, + + /* add super domain above, handle it in + * ath11k_super_reg_6ghz_to_str() + */ +}; + +static inline const char * +ath11k_super_reg_6ghz_to_str(enum reg_super_domain_6ghz domain_id) +{ + switch (domain_id) { + case FCC1_6GHZ: + return "FCC1_6GHZ"; + case ETSI1_6GHZ: + return "ETSI1_6GHZ"; + case ETSI2_6GHZ: + return "ETSI2_6GHZ"; + case APL1_6GHZ: + return "APL1_6GHZ"; + case FCC1_6GHZ_CL: + return "FCC1_6GHZ_CL"; + } + + return "unknown domain id"; +} + struct cur_reg_rule { u16 start_freq; u16 end_freq; -- cgit v1.2.3 From f79cbc77abde22dd01ac90393a7bfb283d9d50a3 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Mon, 27 Feb 2023 14:17:31 +0200 Subject: wifi: move mac80211_hwsim and virt_wifi to virtual directory To clean up drivers/net/wireless move the virtual drivers to a new virtual directory. I did consider adding CONFIG_WLAN_VENDOR_VIRTUAL like other vendors have but then dropped the idea as we are not real drivers. There should be no changes in compilation or in Kconfig options, merely moving files. The order in menuconfig is slightly changed, the virtual drivers are now last in the list. Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230227121732.8967-2-kvalo@kernel.org --- drivers/net/wireless/Kconfig | 20 +- drivers/net/wireless/Makefile | 4 +- drivers/net/wireless/mac80211_hwsim.c | 5901 ------------------------- drivers/net/wireless/mac80211_hwsim.h | 280 -- drivers/net/wireless/virt_wifi.c | 697 --- drivers/net/wireless/virtual/Kconfig | 20 + drivers/net/wireless/virtual/Makefile | 3 + drivers/net/wireless/virtual/mac80211_hwsim.c | 5901 +++++++++++++++++++++++++ drivers/net/wireless/virtual/mac80211_hwsim.h | 280 ++ drivers/net/wireless/virtual/virt_wifi.c | 697 +++ 10 files changed, 6903 insertions(+), 6900 deletions(-) delete mode 100644 drivers/net/wireless/mac80211_hwsim.c delete mode 100644 drivers/net/wireless/mac80211_hwsim.h delete mode 100644 drivers/net/wireless/virt_wifi.c create mode 100644 drivers/net/wireless/virtual/Kconfig create mode 100644 drivers/net/wireless/virtual/Makefile create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim.c create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim.h create mode 100644 drivers/net/wireless/virtual/virt_wifi.c (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index cb1c15012dd0..42b40cc96b21 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -64,19 +64,6 @@ config PCMCIA_WL3501 It has basic support for Linux wireless extensions and initial micro support for ethtool. -config MAC80211_HWSIM - tristate "Simulated radio testing tool for mac80211" - depends on MAC80211 - help - This driver is a developer testing tool that can be used to test - IEEE 802.11 networking stack (mac80211) functionality. This is not - needed for normal wireless LAN usage and is only for testing. See - Documentation/networking/mac80211_hwsim for more information on how - to use this tool. - - To compile this driver as a module, choose M here: the module will be - called mac80211_hwsim. If unsure, say N. - config USB_NET_RNDIS_WLAN tristate "Wireless RNDIS USB support" depends on USB @@ -106,11 +93,6 @@ config USB_NET_RNDIS_WLAN If you choose to build a module, it'll be called rndis_wlan. -config VIRT_WIFI - tristate "Wifi wrapper for ethernet drivers" - depends on CFG80211 - help - This option adds support for ethernet connections to appear as if they - are wifi connections through a special rtnetlink device. +source "drivers/net/wireless/virtual/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index a61cf6c90343..1b697cfe0a13 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -29,6 +29,4 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o -obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o - -obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o +obj-$(CONFIG_WLAN) += virtual/ diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c deleted file mode 100644 index f4bdc243ea0d..000000000000 --- a/drivers/net/wireless/mac80211_hwsim.c +++ /dev/null @@ -1,5901 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 - * Copyright (c) 2008, Jouni Malinen - * Copyright (c) 2011, Javier Lopez - * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2022 Intel Corporation - */ - -/* - * TODO: - * - Add TSF sync and fix IBSS beacon transmission by adding - * competition for "air time" at TBTT - * - RX filtering based on filter configuration (data->rx_filter) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mac80211_hwsim.h" - -#define WARN_QUEUE 100 -#define MAX_QUEUE 200 - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211"); -MODULE_LICENSE("GPL"); - -static int radios = 2; -module_param(radios, int, 0444); -MODULE_PARM_DESC(radios, "Number of simulated radios"); - -static int channels = 1; -module_param(channels, int, 0444); -MODULE_PARM_DESC(channels, "Number of concurrent channels"); - -static bool paged_rx = false; -module_param(paged_rx, bool, 0644); -MODULE_PARM_DESC(paged_rx, "Use paged SKBs for RX instead of linear ones"); - -static bool rctbl = false; -module_param(rctbl, bool, 0444); -MODULE_PARM_DESC(rctbl, "Handle rate control table"); - -static bool support_p2p_device = true; -module_param(support_p2p_device, bool, 0444); -MODULE_PARM_DESC(support_p2p_device, "Support P2P-Device interface type"); - -static bool mlo; -module_param(mlo, bool, 0444); -MODULE_PARM_DESC(mlo, "Support MLO"); - -/** - * enum hwsim_regtest - the type of regulatory tests we offer - * - * These are the different values you can use for the regtest - * module parameter. This is useful to help test world roaming - * and the driver regulatory_hint() call and combinations of these. - * If you want to do specific alpha2 regulatory domain tests simply - * use the userspace regulatory request as that will be respected as - * well without the need of this module parameter. This is designed - * only for testing the driver regulatory request, world roaming - * and all possible combinations. - * - * @HWSIM_REGTEST_DISABLED: No regulatory tests are performed, - * this is the default value. - * @HWSIM_REGTEST_DRIVER_REG_FOLLOW: Used for testing the driver regulatory - * hint, only one driver regulatory hint will be sent as such the - * secondary radios are expected to follow. - * @HWSIM_REGTEST_DRIVER_REG_ALL: Used for testing the driver regulatory - * request with all radios reporting the same regulatory domain. - * @HWSIM_REGTEST_DIFF_COUNTRY: Used for testing the drivers calling - * different regulatory domains requests. Expected behaviour is for - * an intersection to occur but each device will still use their - * respective regulatory requested domains. Subsequent radios will - * use the resulting intersection. - * @HWSIM_REGTEST_WORLD_ROAM: Used for testing the world roaming. We accomplish - * this by using a custom beacon-capable regulatory domain for the first - * radio. All other device world roam. - * @HWSIM_REGTEST_CUSTOM_WORLD: Used for testing the custom world regulatory - * domain requests. All radios will adhere to this custom world regulatory - * domain. - * @HWSIM_REGTEST_CUSTOM_WORLD_2: Used for testing 2 custom world regulatory - * domain requests. The first radio will adhere to the first custom world - * regulatory domain, the second one to the second custom world regulatory - * domain. All other devices will world roam. - * @HWSIM_REGTEST_STRICT_FOLLOW: Used for testing strict regulatory domain - * settings, only the first radio will send a regulatory domain request - * and use strict settings. The rest of the radios are expected to follow. - * @HWSIM_REGTEST_STRICT_ALL: Used for testing strict regulatory domain - * settings. All radios will adhere to this. - * @HWSIM_REGTEST_STRICT_AND_DRIVER_REG: Used for testing strict regulatory - * domain settings, combined with secondary driver regulatory domain - * settings. The first radio will get a strict regulatory domain setting - * using the first driver regulatory request and the second radio will use - * non-strict settings using the second driver regulatory request. All - * other devices should follow the intersection created between the - * first two. - * @HWSIM_REGTEST_ALL: Used for testing every possible mix. You will need - * at least 6 radios for a complete test. We will test in this order: - * 1 - driver custom world regulatory domain - * 2 - second custom world regulatory domain - * 3 - first driver regulatory domain request - * 4 - second driver regulatory domain request - * 5 - strict regulatory domain settings using the third driver regulatory - * domain request - * 6 and on - should follow the intersection of the 3rd, 4rth and 5th radio - * regulatory requests. - */ -enum hwsim_regtest { - HWSIM_REGTEST_DISABLED = 0, - HWSIM_REGTEST_DRIVER_REG_FOLLOW = 1, - HWSIM_REGTEST_DRIVER_REG_ALL = 2, - HWSIM_REGTEST_DIFF_COUNTRY = 3, - HWSIM_REGTEST_WORLD_ROAM = 4, - HWSIM_REGTEST_CUSTOM_WORLD = 5, - HWSIM_REGTEST_CUSTOM_WORLD_2 = 6, - HWSIM_REGTEST_STRICT_FOLLOW = 7, - HWSIM_REGTEST_STRICT_ALL = 8, - HWSIM_REGTEST_STRICT_AND_DRIVER_REG = 9, - HWSIM_REGTEST_ALL = 10, -}; - -/* Set to one of the HWSIM_REGTEST_* values above */ -static int regtest = HWSIM_REGTEST_DISABLED; -module_param(regtest, int, 0444); -MODULE_PARM_DESC(regtest, "The type of regulatory test we want to run"); - -static const char *hwsim_alpha2s[] = { - "FI", - "AL", - "US", - "DE", - "JP", - "AL", -}; - -static const struct ieee80211_regdomain hwsim_world_regdom_custom_01 = { - .n_reg_rules = 5, - .alpha2 = "99", - .reg_rules = { - REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), - REG_RULE(2484-10, 2484+10, 40, 0, 20, 0), - REG_RULE(5150-10, 5240+10, 40, 0, 30, 0), - REG_RULE(5745-10, 5825+10, 40, 0, 30, 0), - REG_RULE(5855-10, 5925+10, 40, 0, 33, 0), - } -}; - -static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = { - .n_reg_rules = 3, - .alpha2 = "99", - .reg_rules = { - REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), - REG_RULE(5725-10, 5850+10, 40, 0, 30, - NL80211_RRF_NO_IR), - REG_RULE(5855-10, 5925+10, 40, 0, 33, 0), - } -}; - -static const struct ieee80211_regdomain hwsim_world_regdom_custom_03 = { - .n_reg_rules = 6, - .alpha2 = "99", - .reg_rules = { - REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0), - REG_RULE(2484 - 10, 2484 + 10, 40, 0, 20, 0), - REG_RULE(5150 - 10, 5240 + 10, 40, 0, 30, 0), - REG_RULE(5745 - 10, 5825 + 10, 40, 0, 30, 0), - REG_RULE(5855 - 10, 5925 + 10, 40, 0, 33, 0), - REG_RULE(5955 - 10, 7125 + 10, 320, 0, 33, 0), - } -}; - -static const struct ieee80211_regdomain *hwsim_world_regdom_custom[] = { - &hwsim_world_regdom_custom_01, - &hwsim_world_regdom_custom_02, - &hwsim_world_regdom_custom_03, -}; - -struct hwsim_vif_priv { - u32 magic; - u8 bssid[ETH_ALEN]; - bool assoc; - bool bcn_en; - u16 aid; -}; - -#define HWSIM_VIF_MAGIC 0x69537748 - -static inline void hwsim_check_magic(struct ieee80211_vif *vif) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - WARN(vp->magic != HWSIM_VIF_MAGIC, - "Invalid VIF (%p) magic %#x, %pM, %d/%d\n", - vif, vp->magic, vif->addr, vif->type, vif->p2p); -} - -static inline void hwsim_set_magic(struct ieee80211_vif *vif) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - vp->magic = HWSIM_VIF_MAGIC; -} - -static inline void hwsim_clear_magic(struct ieee80211_vif *vif) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - vp->magic = 0; -} - -struct hwsim_sta_priv { - u32 magic; - unsigned int last_link; - u16 active_links_rx; -}; - -#define HWSIM_STA_MAGIC 0x6d537749 - -static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta) -{ - struct hwsim_sta_priv *sp = (void *)sta->drv_priv; - WARN_ON(sp->magic != HWSIM_STA_MAGIC); -} - -static inline void hwsim_set_sta_magic(struct ieee80211_sta *sta) -{ - struct hwsim_sta_priv *sp = (void *)sta->drv_priv; - sp->magic = HWSIM_STA_MAGIC; -} - -static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta) -{ - struct hwsim_sta_priv *sp = (void *)sta->drv_priv; - sp->magic = 0; -} - -struct hwsim_chanctx_priv { - u32 magic; -}; - -#define HWSIM_CHANCTX_MAGIC 0x6d53774a - -static inline void hwsim_check_chanctx_magic(struct ieee80211_chanctx_conf *c) -{ - struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; - WARN_ON(cp->magic != HWSIM_CHANCTX_MAGIC); -} - -static inline void hwsim_set_chanctx_magic(struct ieee80211_chanctx_conf *c) -{ - struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; - cp->magic = HWSIM_CHANCTX_MAGIC; -} - -static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c) -{ - struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; - cp->magic = 0; -} - -static unsigned int hwsim_net_id; - -static DEFINE_IDA(hwsim_netgroup_ida); - -struct hwsim_net { - int netgroup; - u32 wmediumd; -}; - -static inline int hwsim_net_get_netgroup(struct net *net) -{ - struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); - - return hwsim_net->netgroup; -} - -static inline int hwsim_net_set_netgroup(struct net *net) -{ - struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); - - hwsim_net->netgroup = ida_alloc(&hwsim_netgroup_ida, GFP_KERNEL); - return hwsim_net->netgroup >= 0 ? 0 : -ENOMEM; -} - -static inline u32 hwsim_net_get_wmediumd(struct net *net) -{ - struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); - - return hwsim_net->wmediumd; -} - -static inline void hwsim_net_set_wmediumd(struct net *net, u32 portid) -{ - struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); - - hwsim_net->wmediumd = portid; -} - -static struct class *hwsim_class; - -static struct net_device *hwsim_mon; /* global monitor netdev */ - -#define CHAN2G(_freq) { \ - .band = NL80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_freq), \ -} - -#define CHAN5G(_freq) { \ - .band = NL80211_BAND_5GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_freq), \ -} - -#define CHAN6G(_freq) { \ - .band = NL80211_BAND_6GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_freq), \ -} - -static const struct ieee80211_channel hwsim_channels_2ghz[] = { - CHAN2G(2412), /* Channel 1 */ - CHAN2G(2417), /* Channel 2 */ - CHAN2G(2422), /* Channel 3 */ - CHAN2G(2427), /* Channel 4 */ - CHAN2G(2432), /* Channel 5 */ - CHAN2G(2437), /* Channel 6 */ - CHAN2G(2442), /* Channel 7 */ - CHAN2G(2447), /* Channel 8 */ - CHAN2G(2452), /* Channel 9 */ - CHAN2G(2457), /* Channel 10 */ - CHAN2G(2462), /* Channel 11 */ - CHAN2G(2467), /* Channel 12 */ - CHAN2G(2472), /* Channel 13 */ - CHAN2G(2484), /* Channel 14 */ -}; - -static const struct ieee80211_channel hwsim_channels_5ghz[] = { - CHAN5G(5180), /* Channel 36 */ - CHAN5G(5200), /* Channel 40 */ - CHAN5G(5220), /* Channel 44 */ - CHAN5G(5240), /* Channel 48 */ - - CHAN5G(5260), /* Channel 52 */ - CHAN5G(5280), /* Channel 56 */ - CHAN5G(5300), /* Channel 60 */ - CHAN5G(5320), /* Channel 64 */ - - CHAN5G(5500), /* Channel 100 */ - CHAN5G(5520), /* Channel 104 */ - CHAN5G(5540), /* Channel 108 */ - CHAN5G(5560), /* Channel 112 */ - CHAN5G(5580), /* Channel 116 */ - CHAN5G(5600), /* Channel 120 */ - CHAN5G(5620), /* Channel 124 */ - CHAN5G(5640), /* Channel 128 */ - CHAN5G(5660), /* Channel 132 */ - CHAN5G(5680), /* Channel 136 */ - CHAN5G(5700), /* Channel 140 */ - - CHAN5G(5745), /* Channel 149 */ - CHAN5G(5765), /* Channel 153 */ - CHAN5G(5785), /* Channel 157 */ - CHAN5G(5805), /* Channel 161 */ - CHAN5G(5825), /* Channel 165 */ - CHAN5G(5845), /* Channel 169 */ - - CHAN5G(5855), /* Channel 171 */ - CHAN5G(5860), /* Channel 172 */ - CHAN5G(5865), /* Channel 173 */ - CHAN5G(5870), /* Channel 174 */ - - CHAN5G(5875), /* Channel 175 */ - CHAN5G(5880), /* Channel 176 */ - CHAN5G(5885), /* Channel 177 */ - CHAN5G(5890), /* Channel 178 */ - CHAN5G(5895), /* Channel 179 */ - CHAN5G(5900), /* Channel 180 */ - CHAN5G(5905), /* Channel 181 */ - - CHAN5G(5910), /* Channel 182 */ - CHAN5G(5915), /* Channel 183 */ - CHAN5G(5920), /* Channel 184 */ - CHAN5G(5925), /* Channel 185 */ -}; - -static const struct ieee80211_channel hwsim_channels_6ghz[] = { - CHAN6G(5955), /* Channel 1 */ - CHAN6G(5975), /* Channel 5 */ - CHAN6G(5995), /* Channel 9 */ - CHAN6G(6015), /* Channel 13 */ - CHAN6G(6035), /* Channel 17 */ - CHAN6G(6055), /* Channel 21 */ - CHAN6G(6075), /* Channel 25 */ - CHAN6G(6095), /* Channel 29 */ - CHAN6G(6115), /* Channel 33 */ - CHAN6G(6135), /* Channel 37 */ - CHAN6G(6155), /* Channel 41 */ - CHAN6G(6175), /* Channel 45 */ - CHAN6G(6195), /* Channel 49 */ - CHAN6G(6215), /* Channel 53 */ - CHAN6G(6235), /* Channel 57 */ - CHAN6G(6255), /* Channel 61 */ - CHAN6G(6275), /* Channel 65 */ - CHAN6G(6295), /* Channel 69 */ - CHAN6G(6315), /* Channel 73 */ - CHAN6G(6335), /* Channel 77 */ - CHAN6G(6355), /* Channel 81 */ - CHAN6G(6375), /* Channel 85 */ - CHAN6G(6395), /* Channel 89 */ - CHAN6G(6415), /* Channel 93 */ - CHAN6G(6435), /* Channel 97 */ - CHAN6G(6455), /* Channel 181 */ - CHAN6G(6475), /* Channel 105 */ - CHAN6G(6495), /* Channel 109 */ - CHAN6G(6515), /* Channel 113 */ - CHAN6G(6535), /* Channel 117 */ - CHAN6G(6555), /* Channel 121 */ - CHAN6G(6575), /* Channel 125 */ - CHAN6G(6595), /* Channel 129 */ - CHAN6G(6615), /* Channel 133 */ - CHAN6G(6635), /* Channel 137 */ - CHAN6G(6655), /* Channel 141 */ - CHAN6G(6675), /* Channel 145 */ - CHAN6G(6695), /* Channel 149 */ - CHAN6G(6715), /* Channel 153 */ - CHAN6G(6735), /* Channel 157 */ - CHAN6G(6755), /* Channel 161 */ - CHAN6G(6775), /* Channel 165 */ - CHAN6G(6795), /* Channel 169 */ - CHAN6G(6815), /* Channel 173 */ - CHAN6G(6835), /* Channel 177 */ - CHAN6G(6855), /* Channel 181 */ - CHAN6G(6875), /* Channel 185 */ - CHAN6G(6895), /* Channel 189 */ - CHAN6G(6915), /* Channel 193 */ - CHAN6G(6935), /* Channel 197 */ - CHAN6G(6955), /* Channel 201 */ - CHAN6G(6975), /* Channel 205 */ - CHAN6G(6995), /* Channel 209 */ - CHAN6G(7015), /* Channel 213 */ - CHAN6G(7035), /* Channel 217 */ - CHAN6G(7055), /* Channel 221 */ - CHAN6G(7075), /* Channel 225 */ - CHAN6G(7095), /* Channel 229 */ - CHAN6G(7115), /* Channel 233 */ -}; - -#define NUM_S1G_CHANS_US 51 -static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US]; - -static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = { - .s1g = true, - .cap = { S1G_CAP0_SGI_1MHZ | S1G_CAP0_SGI_2MHZ, - 0, - 0, - S1G_CAP3_MAX_MPDU_LEN, - 0, - S1G_CAP5_AMPDU, - 0, - S1G_CAP7_DUP_1MHZ, - S1G_CAP8_TWT_RESPOND | S1G_CAP8_TWT_REQUEST, - 0}, - .nss_mcs = { 0xfc | 1, /* MCS 7 for 1 SS */ - /* RX Highest Supported Long GI Data Rate 0:7 */ - 0, - /* RX Highest Supported Long GI Data Rate 0:7 */ - /* TX S1G MCS Map 0:6 */ - 0xfa, - /* TX S1G MCS Map :7 */ - /* TX Highest Supported Long GI Data Rate 0:6 */ - 0x80, - /* TX Highest Supported Long GI Data Rate 7:8 */ - /* Rx Single spatial stream and S1G-MCS Map for 1MHz */ - /* Tx Single spatial stream and S1G-MCS Map for 1MHz */ - 0 }, -}; - -static void hwsim_init_s1g_channels(struct ieee80211_channel *chans) -{ - int ch, freq; - - for (ch = 0; ch < NUM_S1G_CHANS_US; ch++) { - freq = 902000 + (ch + 1) * 500; - chans[ch].band = NL80211_BAND_S1GHZ; - chans[ch].center_freq = KHZ_TO_MHZ(freq); - chans[ch].freq_offset = freq % 1000; - chans[ch].hw_value = ch + 1; - } -} - -static const struct ieee80211_rate hwsim_rates[] = { - { .bitrate = 10 }, - { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60 }, - { .bitrate = 90 }, - { .bitrate = 120 }, - { .bitrate = 180 }, - { .bitrate = 240 }, - { .bitrate = 360 }, - { .bitrate = 480 }, - { .bitrate = 540 } -}; - -#define DEFAULT_RX_RSSI -50 - -static const u32 hwsim_ciphers[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WLAN_CIPHER_SUITE_CCMP_256, - WLAN_CIPHER_SUITE_GCMP, - WLAN_CIPHER_SUITE_GCMP_256, - WLAN_CIPHER_SUITE_AES_CMAC, - WLAN_CIPHER_SUITE_BIP_CMAC_256, - WLAN_CIPHER_SUITE_BIP_GMAC_128, - WLAN_CIPHER_SUITE_BIP_GMAC_256, -}; - -#define OUI_QCA 0x001374 -#define QCA_NL80211_SUBCMD_TEST 1 -enum qca_nl80211_vendor_subcmds { - QCA_WLAN_VENDOR_ATTR_TEST = 8, - QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_TEST -}; - -static const struct nla_policy -hwsim_vendor_test_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = { - [QCA_WLAN_VENDOR_ATTR_MAX] = { .type = NLA_U32 }, -}; - -static int mac80211_hwsim_vendor_cmd_test(struct wiphy *wiphy, - struct wireless_dev *wdev, - const void *data, int data_len) -{ - struct sk_buff *skb; - struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; - int err; - u32 val; - - err = nla_parse_deprecated(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, - data_len, hwsim_vendor_test_policy, NULL); - if (err) - return err; - if (!tb[QCA_WLAN_VENDOR_ATTR_TEST]) - return -EINVAL; - val = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_TEST]); - wiphy_dbg(wiphy, "%s: test=%u\n", __func__, val); - - /* Send a vendor event as a test. Note that this would not normally be - * done within a command handler, but rather, based on some other - * trigger. For simplicity, this command is used to trigger the event - * here. - * - * event_idx = 0 (index in mac80211_hwsim_vendor_commands) - */ - skb = cfg80211_vendor_event_alloc(wiphy, wdev, 100, 0, GFP_KERNEL); - if (skb) { - /* skb_put() or nla_put() will fill up data within - * NL80211_ATTR_VENDOR_DATA. - */ - - /* Add vendor data */ - nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 1); - - /* Send the event - this will call nla_nest_end() */ - cfg80211_vendor_event(skb, GFP_KERNEL); - } - - /* Send a response to the command */ - skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 10); - if (!skb) - return -ENOMEM; - - /* skb_put() or nla_put() will fill up data within - * NL80211_ATTR_VENDOR_DATA - */ - nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 2); - - return cfg80211_vendor_cmd_reply(skb); -} - -static struct wiphy_vendor_command mac80211_hwsim_vendor_commands[] = { - { - .info = { .vendor_id = OUI_QCA, - .subcmd = QCA_NL80211_SUBCMD_TEST }, - .flags = WIPHY_VENDOR_CMD_NEED_NETDEV, - .doit = mac80211_hwsim_vendor_cmd_test, - .policy = hwsim_vendor_test_policy, - .maxattr = QCA_WLAN_VENDOR_ATTR_MAX, - } -}; - -/* Advertise support vendor specific events */ -static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = { - { .vendor_id = OUI_QCA, .subcmd = 1 }, -}; - -static DEFINE_SPINLOCK(hwsim_radio_lock); -static LIST_HEAD(hwsim_radios); -static struct rhashtable hwsim_radios_rht; -static int hwsim_radio_idx; -static int hwsim_radios_generation = 1; - -static struct platform_driver mac80211_hwsim_driver = { - .driver = { - .name = "mac80211_hwsim", - }, -}; - -struct mac80211_hwsim_link_data { - u32 link_id; - u64 beacon_int /* beacon interval in us */; - struct hrtimer beacon_timer; -}; - -struct mac80211_hwsim_data { - struct list_head list; - struct rhash_head rht; - struct ieee80211_hw *hw; - struct device *dev; - struct ieee80211_supported_band bands[NUM_NL80211_BANDS]; - struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; - struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; - struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)]; - struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)]; - struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; - struct ieee80211_iface_combination if_combination; - struct ieee80211_iface_limit if_limits[3]; - int n_if_limits; - - u32 ciphers[ARRAY_SIZE(hwsim_ciphers)]; - - struct mac_address addresses[2]; - int channels, idx; - bool use_chanctx; - bool destroy_on_close; - u32 portid; - char alpha2[2]; - const struct ieee80211_regdomain *regd; - - struct ieee80211_channel *tmp_chan; - struct ieee80211_channel *roc_chan; - u32 roc_duration; - struct delayed_work roc_start; - struct delayed_work roc_done; - struct delayed_work hw_scan; - struct cfg80211_scan_request *hw_scan_request; - struct ieee80211_vif *hw_scan_vif; - int scan_chan_idx; - u8 scan_addr[ETH_ALEN]; - struct { - struct ieee80211_channel *channel; - unsigned long next_start, start, end; - } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) + - ARRAY_SIZE(hwsim_channels_5ghz) + - ARRAY_SIZE(hwsim_channels_6ghz)]; - - struct ieee80211_channel *channel; - enum nl80211_chan_width bw; - unsigned int rx_filter; - bool started, idle, scanning; - struct mutex mutex; - enum ps_mode { - PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL - } ps; - bool ps_poll_pending; - struct dentry *debugfs; - - atomic_t pending_cookie; - struct sk_buff_head pending; /* packets pending */ - /* - * Only radios in the same group can communicate together (the - * channel has to match too). Each bit represents a group. A - * radio can be in more than one group. - */ - u64 group; - - /* group shared by radios created in the same netns */ - int netgroup; - /* wmediumd portid responsible for netgroup of this radio */ - u32 wmediumd; - - /* difference between this hw's clock and the real clock, in usecs */ - s64 tsf_offset; - s64 bcn_delta; - /* absolute beacon transmission time. Used to cover up "tx" delay. */ - u64 abs_bcn_ts; - - /* Stats */ - u64 tx_pkts; - u64 rx_pkts; - u64 tx_bytes; - u64 rx_bytes; - u64 tx_dropped; - u64 tx_failed; - - /* RSSI in rx status of the receiver */ - int rx_rssi; - - struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS]; -}; - -static const struct rhashtable_params hwsim_rht_params = { - .nelem_hint = 2, - .automatic_shrinking = true, - .key_len = ETH_ALEN, - .key_offset = offsetof(struct mac80211_hwsim_data, addresses[1]), - .head_offset = offsetof(struct mac80211_hwsim_data, rht), -}; - -struct hwsim_radiotap_hdr { - struct ieee80211_radiotap_header hdr; - __le64 rt_tsft; - u8 rt_flags; - u8 rt_rate; - __le16 rt_channel; - __le16 rt_chbitmask; -} __packed; - -struct hwsim_radiotap_ack_hdr { - struct ieee80211_radiotap_header hdr; - u8 rt_flags; - u8 pad; - __le16 rt_channel; - __le16 rt_chbitmask; -} __packed; - -/* MAC80211_HWSIM netlink family */ -static struct genl_family hwsim_genl_family; - -enum hwsim_multicast_groups { - HWSIM_MCGRP_CONFIG, -}; - -static const struct genl_multicast_group hwsim_mcgrps[] = { - [HWSIM_MCGRP_CONFIG] = { .name = "config", }, -}; - -/* MAC80211_HWSIM netlink policy */ - -static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { - [HWSIM_ATTR_ADDR_RECEIVER] = NLA_POLICY_ETH_ADDR_COMPAT, - [HWSIM_ATTR_ADDR_TRANSMITTER] = NLA_POLICY_ETH_ADDR_COMPAT, - [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 }, - [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 }, - [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 }, - [HWSIM_ATTR_TX_INFO] = { .type = NLA_BINARY, - .len = IEEE80211_TX_MAX_RATES * - sizeof(struct hwsim_tx_rate)}, - [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, - [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 }, - [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 }, - [HWSIM_ATTR_REG_HINT_ALPHA2] = { .type = NLA_STRING, .len = 2 }, - [HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 }, - [HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG }, - [HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG }, - [HWSIM_ATTR_USE_CHANCTX] = { .type = NLA_FLAG }, - [HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG }, - [HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING }, - [HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG }, - [HWSIM_ATTR_FREQ] = { .type = NLA_U32 }, - [HWSIM_ATTR_TX_INFO_FLAGS] = { .type = NLA_BINARY }, - [HWSIM_ATTR_PERM_ADDR] = NLA_POLICY_ETH_ADDR_COMPAT, - [HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 }, - [HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY }, - [HWSIM_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, -}; - -#if IS_REACHABLE(CONFIG_VIRTIO) - -/* MAC80211_HWSIM virtio queues */ -static struct virtqueue *hwsim_vqs[HWSIM_NUM_VQS]; -static bool hwsim_virtio_enabled; -static DEFINE_SPINLOCK(hwsim_virtio_lock); - -static void hwsim_virtio_rx_work(struct work_struct *work); -static DECLARE_WORK(hwsim_virtio_rx, hwsim_virtio_rx_work); - -static int hwsim_tx_virtio(struct mac80211_hwsim_data *data, - struct sk_buff *skb) -{ - struct scatterlist sg[1]; - unsigned long flags; - int err; - - spin_lock_irqsave(&hwsim_virtio_lock, flags); - if (!hwsim_virtio_enabled) { - err = -ENODEV; - goto out_free; - } - - sg_init_one(sg, skb->head, skb_end_offset(skb)); - err = virtqueue_add_outbuf(hwsim_vqs[HWSIM_VQ_TX], sg, 1, skb, - GFP_ATOMIC); - if (err) - goto out_free; - virtqueue_kick(hwsim_vqs[HWSIM_VQ_TX]); - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); - return 0; - -out_free: - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); - nlmsg_free(skb); - return err; -} -#else -/* cause a linker error if this ends up being needed */ -extern int hwsim_tx_virtio(struct mac80211_hwsim_data *data, - struct sk_buff *skb); -#define hwsim_virtio_enabled false -#endif - -static int hwsim_get_chanwidth(enum nl80211_chan_width bw) -{ - switch (bw) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - return 20; - case NL80211_CHAN_WIDTH_40: - return 40; - case NL80211_CHAN_WIDTH_80: - return 80; - case NL80211_CHAN_WIDTH_80P80: - case NL80211_CHAN_WIDTH_160: - return 160; - case NL80211_CHAN_WIDTH_320: - return 320; - case NL80211_CHAN_WIDTH_5: - return 5; - case NL80211_CHAN_WIDTH_10: - return 10; - case NL80211_CHAN_WIDTH_1: - return 1; - case NL80211_CHAN_WIDTH_2: - return 2; - case NL80211_CHAN_WIDTH_4: - return 4; - case NL80211_CHAN_WIDTH_8: - return 8; - case NL80211_CHAN_WIDTH_16: - return 16; - } - - return INT_MAX; -} - -static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_channel *chan); - -/* sysfs attributes */ -static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct sk_buff *skb; - struct ieee80211_pspoll *pspoll; - - if (!vp->assoc) - return; - - wiphy_dbg(data->hw->wiphy, - "%s: send PS-Poll to %pM for aid %d\n", - __func__, vp->bssid, vp->aid); - - skb = dev_alloc_skb(sizeof(*pspoll)); - if (!skb) - return; - pspoll = skb_put(skb, sizeof(*pspoll)); - pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_PSPOLL | - IEEE80211_FCTL_PM); - pspoll->aid = cpu_to_le16(0xc000 | vp->aid); - memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); - memcpy(pspoll->ta, mac, ETH_ALEN); - - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->bss_conf.chanctx_conf)->def.chan); - rcu_read_unlock(); -} - -static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, - struct ieee80211_vif *vif, int ps) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct sk_buff *skb; - struct ieee80211_hdr *hdr; - struct ieee80211_tx_info *cb; - - if (!vp->assoc) - return; - - wiphy_dbg(data->hw->wiphy, - "%s: send data::nullfunc to %pM ps=%d\n", - __func__, vp->bssid, ps); - - skb = dev_alloc_skb(sizeof(*hdr)); - if (!skb) - return; - hdr = skb_put(skb, sizeof(*hdr) - ETH_ALEN); - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS | - (ps ? IEEE80211_FCTL_PM : 0)); - hdr->duration_id = cpu_to_le16(0); - memcpy(hdr->addr1, vp->bssid, ETH_ALEN); - memcpy(hdr->addr2, mac, ETH_ALEN); - memcpy(hdr->addr3, vp->bssid, ETH_ALEN); - - cb = IEEE80211_SKB_CB(skb); - cb->control.rates[0].count = 1; - cb->control.rates[1].idx = -1; - - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->bss_conf.chanctx_conf)->def.chan); - rcu_read_unlock(); -} - - -static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - hwsim_send_nullfunc(data, mac, vif, 1); -} - -static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - hwsim_send_nullfunc(data, mac, vif, 0); -} - -static int hwsim_fops_ps_read(void *dat, u64 *val) -{ - struct mac80211_hwsim_data *data = dat; - *val = data->ps; - return 0; -} - -static int hwsim_fops_ps_write(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - enum ps_mode old_ps; - - if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && - val != PS_MANUAL_POLL) - return -EINVAL; - - if (val == PS_MANUAL_POLL) { - if (data->ps != PS_ENABLED) - return -EINVAL; - local_bh_disable(); - ieee80211_iterate_active_interfaces_atomic( - data->hw, IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_ps_poll, data); - local_bh_enable(); - return 0; - } - old_ps = data->ps; - data->ps = val; - - local_bh_disable(); - if (old_ps == PS_DISABLED && val != PS_DISABLED) { - ieee80211_iterate_active_interfaces_atomic( - data->hw, IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_ps, data); - } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { - ieee80211_iterate_active_interfaces_atomic( - data->hw, IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_no_ps, data); - } - local_bh_enable(); - - return 0; -} - -DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, - "%llu\n"); - -static int hwsim_write_simulate_radar(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - - ieee80211_radar_detected(data->hw); - - return 0; -} - -DEFINE_DEBUGFS_ATTRIBUTE(hwsim_simulate_radar, NULL, - hwsim_write_simulate_radar, "%llu\n"); - -static int hwsim_fops_group_read(void *dat, u64 *val) -{ - struct mac80211_hwsim_data *data = dat; - *val = data->group; - return 0; -} - -static int hwsim_fops_group_write(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - data->group = val; - return 0; -} - -DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_group, - hwsim_fops_group_read, hwsim_fops_group_write, - "%llx\n"); - -static int hwsim_fops_rx_rssi_read(void *dat, u64 *val) -{ - struct mac80211_hwsim_data *data = dat; - *val = data->rx_rssi; - return 0; -} - -static int hwsim_fops_rx_rssi_write(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - int rssi = (int)val; - - if (rssi >= 0 || rssi < -100) - return -EINVAL; - - data->rx_rssi = rssi; - return 0; -} - -DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_rx_rssi, - hwsim_fops_rx_rssi_read, hwsim_fops_rx_rssi_write, - "%lld\n"); - -static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - /* TODO: allow packet injection */ - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static inline u64 mac80211_hwsim_get_tsf_raw(void) -{ - return ktime_to_us(ktime_get_real()); -} - -static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) -{ - u64 now = mac80211_hwsim_get_tsf_raw(); - return cpu_to_le64(now + data->tsf_offset); -} - -static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = hw->priv; - return le64_to_cpu(__mac80211_hwsim_get_tsf(data)); -} - -static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u64 tsf) -{ - struct mac80211_hwsim_data *data = hw->priv; - u64 now = mac80211_hwsim_get_tsf(hw, vif); - /* MLD not supported here */ - u32 bcn_int = data->link_data[0].beacon_int; - u64 delta = abs(tsf - now); - - /* adjust after beaconing with new timestamp at old TBTT */ - if (tsf > now) { - data->tsf_offset += delta; - data->bcn_delta = do_div(delta, bcn_int); - } else { - data->tsf_offset -= delta; - data->bcn_delta = -(s64)do_div(delta, bcn_int); - } -} - -static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, - struct sk_buff *tx_skb, - struct ieee80211_channel *chan) -{ - struct mac80211_hwsim_data *data = hw->priv; - struct sk_buff *skb; - struct hwsim_radiotap_hdr *hdr; - u16 flags, bitrate; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb); - struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); - - if (!txrate) - bitrate = 0; - else - bitrate = txrate->bitrate; - - if (!netif_running(hwsim_mon)) - return; - - skb = skb_copy_expand(tx_skb, sizeof(*hdr), 0, GFP_ATOMIC); - if (skb == NULL) - return; - - hdr = skb_push(skb, sizeof(*hdr)); - hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; - hdr->hdr.it_pad = 0; - hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); - hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | - (1 << IEEE80211_RADIOTAP_RATE) | - (1 << IEEE80211_RADIOTAP_TSFT) | - (1 << IEEE80211_RADIOTAP_CHANNEL)); - hdr->rt_tsft = __mac80211_hwsim_get_tsf(data); - hdr->rt_flags = 0; - hdr->rt_rate = bitrate / 5; - hdr->rt_channel = cpu_to_le16(chan->center_freq); - flags = IEEE80211_CHAN_2GHZ; - if (txrate && txrate->flags & IEEE80211_RATE_ERP_G) - flags |= IEEE80211_CHAN_OFDM; - else - flags |= IEEE80211_CHAN_CCK; - hdr->rt_chbitmask = cpu_to_le16(flags); - - skb->dev = hwsim_mon; - skb_reset_mac_header(skb); - skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = htons(ETH_P_802_2); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx(skb); -} - - -static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, - const u8 *addr) -{ - struct sk_buff *skb; - struct hwsim_radiotap_ack_hdr *hdr; - u16 flags; - struct ieee80211_hdr *hdr11; - - if (!netif_running(hwsim_mon)) - return; - - skb = dev_alloc_skb(100); - if (skb == NULL) - return; - - hdr = skb_put(skb, sizeof(*hdr)); - hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; - hdr->hdr.it_pad = 0; - hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); - hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | - (1 << IEEE80211_RADIOTAP_CHANNEL)); - hdr->rt_flags = 0; - hdr->pad = 0; - hdr->rt_channel = cpu_to_le16(chan->center_freq); - flags = IEEE80211_CHAN_2GHZ; - hdr->rt_chbitmask = cpu_to_le16(flags); - - hdr11 = skb_put(skb, 10); - hdr11->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_ACK); - hdr11->duration_id = cpu_to_le16(0); - memcpy(hdr11->addr1, addr, ETH_ALEN); - - skb->dev = hwsim_mon; - skb_reset_mac_header(skb); - skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = htons(ETH_P_802_2); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx(skb); -} - -struct mac80211_hwsim_addr_match_data { - u8 addr[ETH_ALEN]; - bool ret; -}; - -static void mac80211_hwsim_addr_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - int i; - struct mac80211_hwsim_addr_match_data *md = data; - - if (memcmp(mac, md->addr, ETH_ALEN) == 0) { - md->ret = true; - return; - } - - /* Match the link address */ - for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { - struct ieee80211_bss_conf *conf; - - conf = rcu_dereference(vif->link_conf[i]); - if (!conf) - continue; - - if (memcmp(conf->addr, md->addr, ETH_ALEN) == 0) { - md->ret = true; - return; - } - } -} - -static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, - const u8 *addr) -{ - struct mac80211_hwsim_addr_match_data md = { - .ret = false, - }; - - if (data->scanning && memcmp(addr, data->scan_addr, ETH_ALEN) == 0) - return true; - - memcpy(md.addr, addr, ETH_ALEN); - - ieee80211_iterate_active_interfaces_atomic(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_addr_iter, - &md); - - return md.ret; -} - -static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, - struct sk_buff *skb) -{ - switch (data->ps) { - case PS_DISABLED: - return true; - case PS_ENABLED: - return false; - case PS_AUTO_POLL: - /* TODO: accept (some) Beacons by default and other frames only - * if pending PS-Poll has been sent */ - return true; - case PS_MANUAL_POLL: - /* Allow unicast frames to own address if there is a pending - * PS-Poll */ - if (data->ps_poll_pending && - mac80211_hwsim_addr_match(data, skb->data + 4)) { - data->ps_poll_pending = false; - return true; - } - return false; - } - - return true; -} - -static int hwsim_unicast_netgroup(struct mac80211_hwsim_data *data, - struct sk_buff *skb, int portid) -{ - struct net *net; - bool found = false; - int res = -ENOENT; - - rcu_read_lock(); - for_each_net_rcu(net) { - if (data->netgroup == hwsim_net_get_netgroup(net)) { - res = genlmsg_unicast(net, skb, portid); - found = true; - break; - } - } - rcu_read_unlock(); - - if (!found) - nlmsg_free(skb); - - return res; -} - -static void mac80211_hwsim_config_mac_nl(struct ieee80211_hw *hw, - const u8 *addr, bool add) -{ - struct mac80211_hwsim_data *data = hw->priv; - u32 _portid = READ_ONCE(data->wmediumd); - struct sk_buff *skb; - void *msg_head; - - WARN_ON(!is_valid_ether_addr(addr)); - - if (!_portid && !hwsim_virtio_enabled) - return; - - skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!skb) - return; - - msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, - add ? HWSIM_CMD_ADD_MAC_ADDR : - HWSIM_CMD_DEL_MAC_ADDR); - if (!msg_head) { - pr_debug("mac80211_hwsim: problem with msg_head\n"); - goto nla_put_failure; - } - - if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, - ETH_ALEN, data->addresses[1].addr)) - goto nla_put_failure; - - if (nla_put(skb, HWSIM_ATTR_ADDR_RECEIVER, ETH_ALEN, addr)) - goto nla_put_failure; - - genlmsg_end(skb, msg_head); - - if (hwsim_virtio_enabled) - hwsim_tx_virtio(data, skb); - else - hwsim_unicast_netgroup(data, skb, _portid); - return; -nla_put_failure: - nlmsg_free(skb); -} - -static inline u16 trans_tx_rate_flags_ieee2hwsim(struct ieee80211_tx_rate *rate) -{ - u16 result = 0; - - if (rate->flags & IEEE80211_TX_RC_USE_RTS_CTS) - result |= MAC80211_HWSIM_TX_RC_USE_RTS_CTS; - if (rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - result |= MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT; - if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - result |= MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE; - if (rate->flags & IEEE80211_TX_RC_MCS) - result |= MAC80211_HWSIM_TX_RC_MCS; - if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) - result |= MAC80211_HWSIM_TX_RC_GREEN_FIELD; - if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - result |= MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH; - if (rate->flags & IEEE80211_TX_RC_DUP_DATA) - result |= MAC80211_HWSIM_TX_RC_DUP_DATA; - if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - result |= MAC80211_HWSIM_TX_RC_SHORT_GI; - if (rate->flags & IEEE80211_TX_RC_VHT_MCS) - result |= MAC80211_HWSIM_TX_RC_VHT_MCS; - if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) - result |= MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH; - if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) - result |= MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH; - - return result; -} - -static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, - struct sk_buff *my_skb, - int dst_portid, - struct ieee80211_channel *channel) -{ - struct sk_buff *skb; - struct mac80211_hwsim_data *data = hw->priv; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb); - void *msg_head; - unsigned int hwsim_flags = 0; - int i; - struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES]; - struct hwsim_tx_rate_flag tx_attempts_flags[IEEE80211_TX_MAX_RATES]; - uintptr_t cookie; - - if (data->ps != PS_DISABLED) - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); - /* If the queue contains MAX_QUEUE skb's drop some */ - if (skb_queue_len(&data->pending) >= MAX_QUEUE) { - /* Dropping until WARN_QUEUE level */ - while (skb_queue_len(&data->pending) >= WARN_QUEUE) { - ieee80211_free_txskb(hw, skb_dequeue(&data->pending)); - data->tx_dropped++; - } - } - - skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (skb == NULL) - goto nla_put_failure; - - msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, - HWSIM_CMD_FRAME); - if (msg_head == NULL) { - pr_debug("mac80211_hwsim: problem with msg_head\n"); - goto nla_put_failure; - } - - if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, - ETH_ALEN, data->addresses[1].addr)) - goto nla_put_failure; - - /* We get the skb->data */ - if (nla_put(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data)) - goto nla_put_failure; - - /* We get the flags for this transmission, and we translate them to - wmediumd flags */ - - if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) - hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS; - - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - hwsim_flags |= HWSIM_TX_CTL_NO_ACK; - - if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags)) - goto nla_put_failure; - - if (nla_put_u32(skb, HWSIM_ATTR_FREQ, channel->center_freq)) - goto nla_put_failure; - - /* We get the tx control (rate and retries) info*/ - - for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { - tx_attempts[i].idx = info->status.rates[i].idx; - tx_attempts_flags[i].idx = info->status.rates[i].idx; - tx_attempts[i].count = info->status.rates[i].count; - tx_attempts_flags[i].flags = - trans_tx_rate_flags_ieee2hwsim( - &info->status.rates[i]); - } - - if (nla_put(skb, HWSIM_ATTR_TX_INFO, - sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES, - tx_attempts)) - goto nla_put_failure; - - if (nla_put(skb, HWSIM_ATTR_TX_INFO_FLAGS, - sizeof(struct hwsim_tx_rate_flag) * IEEE80211_TX_MAX_RATES, - tx_attempts_flags)) - goto nla_put_failure; - - /* We create a cookie to identify this skb */ - cookie = atomic_inc_return(&data->pending_cookie); - info->rate_driver_data[0] = (void *)cookie; - if (nla_put_u64_64bit(skb, HWSIM_ATTR_COOKIE, cookie, HWSIM_ATTR_PAD)) - goto nla_put_failure; - - genlmsg_end(skb, msg_head); - - if (hwsim_virtio_enabled) { - if (hwsim_tx_virtio(data, skb)) - goto err_free_txskb; - } else { - if (hwsim_unicast_netgroup(data, skb, dst_portid)) - goto err_free_txskb; - } - - /* Enqueue the packet */ - skb_queue_tail(&data->pending, my_skb); - data->tx_pkts++; - data->tx_bytes += my_skb->len; - return; - -nla_put_failure: - nlmsg_free(skb); -err_free_txskb: - pr_debug("mac80211_hwsim: error occurred in %s\n", __func__); - ieee80211_free_txskb(hw, my_skb); - data->tx_failed++; -} - -static bool hwsim_chans_compat(struct ieee80211_channel *c1, - struct ieee80211_channel *c2) -{ - if (!c1 || !c2) - return false; - - return c1->center_freq == c2->center_freq; -} - -struct tx_iter_data { - struct ieee80211_channel *channel; - bool receive; -}; - -static void mac80211_hwsim_tx_iter(void *_data, u8 *addr, - struct ieee80211_vif *vif) -{ - struct tx_iter_data *data = _data; - int i; - - for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { - struct ieee80211_bss_conf *conf; - struct ieee80211_chanctx_conf *chanctx; - - conf = rcu_dereference(vif->link_conf[i]); - if (!conf) - continue; - - chanctx = rcu_dereference(conf->chanctx_conf); - if (!chanctx) - continue; - - if (!hwsim_chans_compat(data->channel, chanctx->def.chan)) - continue; - - data->receive = true; - return; - } -} - -static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb) -{ - /* - * To enable this code, #define the HWSIM_RADIOTAP_OUI, - * e.g. like this: - * #define HWSIM_RADIOTAP_OUI "\x02\x00\x00" - * (but you should use a valid OUI, not that) - * - * If anyone wants to 'donate' a radiotap OUI/subns code - * please send a patch removing this #ifdef and changing - * the values accordingly. - */ -#ifdef HWSIM_RADIOTAP_OUI - struct ieee80211_radiotap_vendor_tlv *rtap; - static const char vendor_data[8] = "ABCDEFGH"; - - // Make sure no padding is needed - BUILD_BUG_ON(sizeof(vendor_data) % 4); - /* this is last radiotap info before the mac header, so - * skb_reset_mac_header for mac8022 to know the end of - * the radiotap TLV/beginning of the 802.11 header - */ - skb_reset_mac_header(skb); - - /* - * Note that this code requires the headroom in the SKB - * that was allocated earlier. - */ - rtap = skb_push(skb, sizeof(*rtap) + sizeof(vendor_data)); - - rtap->len = cpu_to_le16(sizeof(*rtap) - - sizeof(struct ieee80211_radiotap_tlv) + - sizeof(vendor_data)); - rtap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE); - - rtap->content.oui[0] = HWSIM_RADIOTAP_OUI[0]; - rtap->content.oui[1] = HWSIM_RADIOTAP_OUI[1]; - rtap->content.oui[2] = HWSIM_RADIOTAP_OUI[2]; - rtap->content.oui_subtype = 127; - /* clear reserved field */ - rtap->content.reserved = 0; - rtap->content.vendor_type = 0; - memcpy(rtap->content.data, vendor_data, sizeof(vendor_data)); - - IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; -#endif -} - -static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data, - struct ieee80211_rx_status *rx_status, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (void *)skb->data; - - if (!ieee80211_has_morefrags(hdr->frame_control) && - !is_multicast_ether_addr(hdr->addr1) && - (ieee80211_is_mgmt(hdr->frame_control) || - ieee80211_is_data(hdr->frame_control))) { - struct ieee80211_sta *sta; - unsigned int link_id; - - rcu_read_lock(); - sta = ieee80211_find_sta_by_link_addrs(data->hw, hdr->addr2, - hdr->addr1, &link_id); - if (sta) { - struct hwsim_sta_priv *sp = (void *)sta->drv_priv; - - if (ieee80211_has_pm(hdr->frame_control)) - sp->active_links_rx &= ~BIT(link_id); - else - sp->active_links_rx |= BIT(link_id); - } - rcu_read_unlock(); - } - - memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status)); - - mac80211_hwsim_add_vendor_rtap(skb); - - data->rx_pkts++; - data->rx_bytes += skb->len; - ieee80211_rx_irqsafe(data->hw, skb); -} - -static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_channel *chan) -{ - struct mac80211_hwsim_data *data = hw->priv, *data2; - bool ack = false; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_rx_status rx_status; - u64 now; - - memset(&rx_status, 0, sizeof(rx_status)); - rx_status.flag |= RX_FLAG_MACTIME_START; - rx_status.freq = chan->center_freq; - rx_status.freq_offset = chan->freq_offset ? 1 : 0; - rx_status.band = chan->band; - if (info->control.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) { - rx_status.rate_idx = - ieee80211_rate_get_vht_mcs(&info->control.rates[0]); - rx_status.nss = - ieee80211_rate_get_vht_nss(&info->control.rates[0]); - rx_status.encoding = RX_ENC_VHT; - } else { - rx_status.rate_idx = info->control.rates[0].idx; - if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) - rx_status.encoding = RX_ENC_HT; - } - if (info->control.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - rx_status.bw = RATE_INFO_BW_40; - else if (info->control.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH) - rx_status.bw = RATE_INFO_BW_80; - else if (info->control.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH) - rx_status.bw = RATE_INFO_BW_160; - else - rx_status.bw = RATE_INFO_BW_20; - if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) - rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI; - /* TODO: simulate optional packet loss */ - rx_status.signal = data->rx_rssi; - if (info->control.vif) - rx_status.signal += info->control.vif->bss_conf.txpower; - - if (data->ps != PS_DISABLED) - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); - - /* release the skb's source info */ - skb_orphan(skb); - skb_dst_drop(skb); - skb->mark = 0; - skb_ext_reset(skb); - nf_reset_ct(skb); - - /* - * Get absolute mactime here so all HWs RX at the "same time", and - * absolute TX time for beacon mactime so the timestamp matches. - * Giving beacons a different mactime than non-beacons looks messy, but - * it helps the Toffset be exact and a ~10us mactime discrepancy - * probably doesn't really matter. - */ - if (ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control)) { - rx_status.boottime_ns = ktime_get_boottime_ns(); - now = data->abs_bcn_ts; - } else { - now = mac80211_hwsim_get_tsf_raw(); - } - - /* Copy skb to all enabled radios that are on the current frequency */ - spin_lock(&hwsim_radio_lock); - list_for_each_entry(data2, &hwsim_radios, list) { - struct sk_buff *nskb; - struct tx_iter_data tx_iter_data = { - .receive = false, - .channel = chan, - }; - - if (data == data2) - continue; - - if (!data2->started || (data2->idle && !data2->tmp_chan) || - !hwsim_ps_rx_ok(data2, skb)) - continue; - - if (!(data->group & data2->group)) - continue; - - if (data->netgroup != data2->netgroup) - continue; - - if (!hwsim_chans_compat(chan, data2->tmp_chan) && - !hwsim_chans_compat(chan, data2->channel)) { - ieee80211_iterate_active_interfaces_atomic( - data2->hw, IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_tx_iter, &tx_iter_data); - if (!tx_iter_data.receive) - continue; - } - - /* - * reserve some space for our vendor and the normal - * radiotap header, since we're copying anyway - */ - if (skb->len < PAGE_SIZE && paged_rx) { - struct page *page = alloc_page(GFP_ATOMIC); - - if (!page) - continue; - - nskb = dev_alloc_skb(128); - if (!nskb) { - __free_page(page); - continue; - } - - memcpy(page_address(page), skb->data, skb->len); - skb_add_rx_frag(nskb, 0, page, 0, skb->len, skb->len); - } else { - nskb = skb_copy(skb, GFP_ATOMIC); - if (!nskb) - continue; - } - - if (mac80211_hwsim_addr_match(data2, hdr->addr1)) - ack = true; - - rx_status.mactime = now + data2->tsf_offset; - - mac80211_hwsim_rx(data2, &rx_status, nskb); - } - spin_unlock(&hwsim_radio_lock); - - return ack; -} - -static struct ieee80211_bss_conf * -mac80211_hwsim_select_tx_link(struct mac80211_hwsim_data *data, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_hdr *hdr, - struct ieee80211_link_sta **link_sta) -{ - struct hwsim_sta_priv *sp = (void *)sta->drv_priv; - int i; - - if (!vif->valid_links) - return &vif->bss_conf; - - WARN_ON(is_multicast_ether_addr(hdr->addr1)); - - if (WARN_ON_ONCE(!sta->valid_links)) - return &vif->bss_conf; - - for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { - struct ieee80211_bss_conf *bss_conf; - unsigned int link_id; - - /* round-robin the available link IDs */ - link_id = (sp->last_link + i + 1) % ARRAY_SIZE(vif->link_conf); - - if (!(vif->active_links & BIT(link_id))) - continue; - - if (!(sp->active_links_rx & BIT(link_id))) - continue; - - *link_sta = rcu_dereference(sta->link[link_id]); - if (!*link_sta) - continue; - - bss_conf = rcu_dereference(vif->link_conf[link_id]); - if (WARN_ON_ONCE(!bss_conf)) - continue; - - /* can happen while switching links */ - if (!rcu_access_pointer(bss_conf->chanctx_conf)) - continue; - - sp->last_link = link_id; - return bss_conf; - } - - return NULL; -} - -static void mac80211_hwsim_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct mac80211_hwsim_data *data = hw->priv; - struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (void *)skb->data; - struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *channel; - bool ack; - enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; - u32 _portid, i; - - if (WARN_ON(skb->len < 10)) { - /* Should not happen; just a sanity check for addr1 use */ - ieee80211_free_txskb(hw, skb); - return; - } - - if (!data->use_chanctx) { - channel = data->channel; - confbw = data->bw; - } else if (txi->hw_queue == 4) { - channel = data->tmp_chan; - } else { - u8 link = u32_get_bits(IEEE80211_SKB_CB(skb)->control.flags, - IEEE80211_TX_CTRL_MLO_LINK); - struct ieee80211_vif *vif = txi->control.vif; - struct ieee80211_link_sta *link_sta = NULL; - struct ieee80211_sta *sta = control->sta; - struct ieee80211_bss_conf *bss_conf; - - if (link != IEEE80211_LINK_UNSPECIFIED) { - bss_conf = rcu_dereference(txi->control.vif->link_conf[link]); - if (sta) - link_sta = rcu_dereference(sta->link[link]); - } else { - bss_conf = mac80211_hwsim_select_tx_link(data, vif, sta, - hdr, &link_sta); - } - - if (WARN_ON(!bss_conf)) { - ieee80211_free_txskb(hw, skb); - return; - } - - if (sta && sta->mlo) { - if (WARN_ON(!link_sta)) { - ieee80211_free_txskb(hw, skb); - return; - } - /* address translation to link addresses on TX */ - ether_addr_copy(hdr->addr1, link_sta->addr); - ether_addr_copy(hdr->addr2, bss_conf->addr); - /* translate A3 only if it's the BSSID */ - if (!ieee80211_has_tods(hdr->frame_control) && - !ieee80211_has_fromds(hdr->frame_control)) { - if (ether_addr_equal(hdr->addr3, sta->addr)) - ether_addr_copy(hdr->addr3, link_sta->addr); - else if (ether_addr_equal(hdr->addr3, vif->addr)) - ether_addr_copy(hdr->addr3, bss_conf->addr); - } - /* no need to look at A4, if present it's SA */ - } - - chanctx_conf = rcu_dereference(bss_conf->chanctx_conf); - if (chanctx_conf) { - channel = chanctx_conf->def.chan; - confbw = chanctx_conf->def.width; - } else { - channel = NULL; - } - } - - if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) { - ieee80211_free_txskb(hw, skb); - return; - } - - if (data->idle && !data->tmp_chan) { - wiphy_dbg(hw->wiphy, "Trying to TX when idle - reject\n"); - ieee80211_free_txskb(hw, skb); - return; - } - - if (txi->control.vif) - hwsim_check_magic(txi->control.vif); - if (control->sta) - hwsim_check_sta_magic(control->sta); - - if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) - ieee80211_get_tx_rates(txi->control.vif, control->sta, skb, - txi->control.rates, - ARRAY_SIZE(txi->control.rates)); - - for (i = 0; i < ARRAY_SIZE(txi->control.rates); i++) { - u16 rflags = txi->control.rates[i].flags; - /* initialize to data->bw for 5/10 MHz handling */ - enum nl80211_chan_width bw = data->bw; - - if (txi->control.rates[i].idx == -1) - break; - - if (rflags & IEEE80211_TX_RC_40_MHZ_WIDTH) - bw = NL80211_CHAN_WIDTH_40; - else if (rflags & IEEE80211_TX_RC_80_MHZ_WIDTH) - bw = NL80211_CHAN_WIDTH_80; - else if (rflags & IEEE80211_TX_RC_160_MHZ_WIDTH) - bw = NL80211_CHAN_WIDTH_160; - - if (WARN_ON(hwsim_get_chanwidth(bw) > hwsim_get_chanwidth(confbw))) - return; - } - - if (skb->len >= 24 + 8 && - ieee80211_is_probe_resp(hdr->frame_control)) { - /* fake header transmission time */ - struct ieee80211_mgmt *mgmt; - struct ieee80211_rate *txrate; - /* TODO: get MCS */ - int bitrate = 100; - u64 ts; - - mgmt = (struct ieee80211_mgmt *)skb->data; - txrate = ieee80211_get_tx_rate(hw, txi); - if (txrate) - bitrate = txrate->bitrate; - ts = mac80211_hwsim_get_tsf_raw(); - mgmt->u.probe_resp.timestamp = - cpu_to_le64(ts + data->tsf_offset + - 24 * 8 * 10 / bitrate); - } - - mac80211_hwsim_monitor_rx(hw, skb, channel); - - /* wmediumd mode check */ - _portid = READ_ONCE(data->wmediumd); - - if (_portid || hwsim_virtio_enabled) - return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, channel); - - /* NO wmediumd detected, perfect medium simulation */ - data->tx_pkts++; - data->tx_bytes += skb->len; - ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel); - - if (ack && skb->len >= 16) - mac80211_hwsim_monitor_ack(channel, hdr->addr2); - - ieee80211_tx_info_clear_status(txi); - - /* frame was transmitted at most favorable rate at first attempt */ - txi->control.rates[0].count = 1; - txi->control.rates[1].idx = -1; - - if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack) - txi->flags |= IEEE80211_TX_STAT_ACK; - ieee80211_tx_status_irqsafe(hw, skb); -} - - -static int mac80211_hwsim_start(struct ieee80211_hw *hw) -{ - struct mac80211_hwsim_data *data = hw->priv; - wiphy_dbg(hw->wiphy, "%s\n", __func__); - data->started = true; - return 0; -} - - -static void mac80211_hwsim_stop(struct ieee80211_hw *hw) -{ - struct mac80211_hwsim_data *data = hw->priv; - int i; - - data->started = false; - - for (i = 0; i < ARRAY_SIZE(data->link_data); i++) - hrtimer_cancel(&data->link_data[i].beacon_timer); - - while (!skb_queue_empty(&data->pending)) - ieee80211_free_txskb(hw, skb_dequeue(&data->pending)); - - wiphy_dbg(hw->wiphy, "%s\n", __func__); -} - - -static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - wiphy_dbg(hw->wiphy, "%s (type=%d mac_addr=%pM)\n", - __func__, ieee80211_vif_type_p2p(vif), - vif->addr); - hwsim_set_magic(vif); - - if (vif->type != NL80211_IFTYPE_MONITOR) - mac80211_hwsim_config_mac_nl(hw, vif->addr, true); - - vif->cab_queue = 0; - vif->hw_queue[IEEE80211_AC_VO] = 0; - vif->hw_queue[IEEE80211_AC_VI] = 1; - vif->hw_queue[IEEE80211_AC_BE] = 2; - vif->hw_queue[IEEE80211_AC_BK] = 3; - - return 0; -} - - -static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum nl80211_iftype newtype, - bool newp2p) -{ - newtype = ieee80211_iftype_p2p(newtype, newp2p); - wiphy_dbg(hw->wiphy, - "%s (old type=%d, new type=%d, mac_addr=%pM)\n", - __func__, ieee80211_vif_type_p2p(vif), - newtype, vif->addr); - hwsim_check_magic(vif); - - /* - * interface may change from non-AP to AP in - * which case this needs to be set up again - */ - vif->cab_queue = 0; - - return 0; -} - -static void mac80211_hwsim_remove_interface( - struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - wiphy_dbg(hw->wiphy, "%s (type=%d mac_addr=%pM)\n", - __func__, ieee80211_vif_type_p2p(vif), - vif->addr); - hwsim_check_magic(vif); - hwsim_clear_magic(vif); - if (vif->type != NL80211_IFTYPE_MONITOR) - mac80211_hwsim_config_mac_nl(hw, vif->addr, false); -} - -static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_channel *chan) -{ - struct mac80211_hwsim_data *data = hw->priv; - u32 _portid = READ_ONCE(data->wmediumd); - - if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) { - struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); - ieee80211_get_tx_rates(txi->control.vif, NULL, skb, - txi->control.rates, - ARRAY_SIZE(txi->control.rates)); - } - - mac80211_hwsim_monitor_rx(hw, skb, chan); - - if (_portid || hwsim_virtio_enabled) - return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, chan); - - data->tx_pkts++; - data->tx_bytes += skb->len; - mac80211_hwsim_tx_frame_no_nl(hw, skb, chan); - dev_kfree_skb(skb); -} - -static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_link_data *link_data = arg; - u32 link_id = link_data->link_id; - struct ieee80211_bss_conf *link_conf; - struct mac80211_hwsim_data *data = - container_of(link_data, struct mac80211_hwsim_data, - link_data[link_id]); - struct ieee80211_hw *hw = data->hw; - struct ieee80211_tx_info *info; - struct ieee80211_rate *txrate; - struct ieee80211_mgmt *mgmt; - struct sk_buff *skb; - /* TODO: get MCS */ - int bitrate = 100; - - hwsim_check_magic(vif); - - link_conf = rcu_dereference(vif->link_conf[link_id]); - if (!link_conf) - return; - - if (vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_MESH_POINT && - vif->type != NL80211_IFTYPE_ADHOC && - vif->type != NL80211_IFTYPE_OCB) - return; - - skb = ieee80211_beacon_get(hw, vif, link_data->link_id); - if (skb == NULL) - return; - info = IEEE80211_SKB_CB(skb); - if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) - ieee80211_get_tx_rates(vif, NULL, skb, - info->control.rates, - ARRAY_SIZE(info->control.rates)); - - txrate = ieee80211_get_tx_rate(hw, info); - if (txrate) - bitrate = txrate->bitrate; - - mgmt = (struct ieee80211_mgmt *) skb->data; - /* fake header transmission time */ - data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw(); - if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { - struct ieee80211_ext *ext = (void *) mgmt; - - ext->u.s1g_beacon.timestamp = cpu_to_le32(data->abs_bcn_ts + - data->tsf_offset + - 10 * 8 * 10 / - bitrate); - } else { - mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts + - data->tsf_offset + - 24 * 8 * 10 / - bitrate); - } - - mac80211_hwsim_tx_frame(hw, skb, - rcu_dereference(link_conf->chanctx_conf)->def.chan); - - while ((skb = ieee80211_get_buffered_bc(hw, vif)) != NULL) { - mac80211_hwsim_tx_frame(hw, skb, - rcu_dereference(link_conf->chanctx_conf)->def.chan); - } - - if (link_conf->csa_active && ieee80211_beacon_cntdwn_is_complete(vif)) - ieee80211_csa_finish(vif); -} - -static enum hrtimer_restart -mac80211_hwsim_beacon(struct hrtimer *timer) -{ - struct mac80211_hwsim_link_data *link_data = - container_of(timer, struct mac80211_hwsim_link_data, beacon_timer); - struct mac80211_hwsim_data *data = - container_of(link_data, struct mac80211_hwsim_data, - link_data[link_data->link_id]); - struct ieee80211_hw *hw = data->hw; - u64 bcn_int = link_data->beacon_int; - - if (!data->started) - return HRTIMER_NORESTART; - - ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_beacon_tx, link_data); - - /* beacon at new TBTT + beacon interval */ - if (data->bcn_delta) { - bcn_int -= data->bcn_delta; - data->bcn_delta = 0; - } - hrtimer_forward_now(&link_data->beacon_timer, - ns_to_ktime(bcn_int * NSEC_PER_USEC)); - return HRTIMER_RESTART; -} - -static const char * const hwsim_chanwidths[] = { - [NL80211_CHAN_WIDTH_5] = "ht5", - [NL80211_CHAN_WIDTH_10] = "ht10", - [NL80211_CHAN_WIDTH_20_NOHT] = "noht", - [NL80211_CHAN_WIDTH_20] = "ht20", - [NL80211_CHAN_WIDTH_40] = "ht40", - [NL80211_CHAN_WIDTH_80] = "vht80", - [NL80211_CHAN_WIDTH_80P80] = "vht80p80", - [NL80211_CHAN_WIDTH_160] = "vht160", - [NL80211_CHAN_WIDTH_1] = "1MHz", - [NL80211_CHAN_WIDTH_2] = "2MHz", - [NL80211_CHAN_WIDTH_4] = "4MHz", - [NL80211_CHAN_WIDTH_8] = "8MHz", - [NL80211_CHAN_WIDTH_16] = "16MHz", -}; - -static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) -{ - struct mac80211_hwsim_data *data = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { - [IEEE80211_SMPS_AUTOMATIC] = "auto", - [IEEE80211_SMPS_OFF] = "off", - [IEEE80211_SMPS_STATIC] = "static", - [IEEE80211_SMPS_DYNAMIC] = "dynamic", - }; - int idx; - - if (conf->chandef.chan) - wiphy_dbg(hw->wiphy, - "%s (freq=%d(%d - %d)/%s idle=%d ps=%d smps=%s)\n", - __func__, - conf->chandef.chan->center_freq, - conf->chandef.center_freq1, - conf->chandef.center_freq2, - hwsim_chanwidths[conf->chandef.width], - !!(conf->flags & IEEE80211_CONF_IDLE), - !!(conf->flags & IEEE80211_CONF_PS), - smps_modes[conf->smps_mode]); - else - wiphy_dbg(hw->wiphy, - "%s (freq=0 idle=%d ps=%d smps=%s)\n", - __func__, - !!(conf->flags & IEEE80211_CONF_IDLE), - !!(conf->flags & IEEE80211_CONF_PS), - smps_modes[conf->smps_mode]); - - data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); - - WARN_ON(conf->chandef.chan && data->use_chanctx); - - mutex_lock(&data->mutex); - if (data->scanning && conf->chandef.chan) { - for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { - if (data->survey_data[idx].channel == data->channel) { - data->survey_data[idx].start = - data->survey_data[idx].next_start; - data->survey_data[idx].end = jiffies; - break; - } - } - - data->channel = conf->chandef.chan; - data->bw = conf->chandef.width; - - for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { - if (data->survey_data[idx].channel && - data->survey_data[idx].channel != data->channel) - continue; - data->survey_data[idx].channel = data->channel; - data->survey_data[idx].next_start = jiffies; - break; - } - } else { - data->channel = conf->chandef.chan; - data->bw = conf->chandef.width; - } - mutex_unlock(&data->mutex); - - for (idx = 0; idx < ARRAY_SIZE(data->link_data); idx++) { - struct mac80211_hwsim_link_data *link_data = - &data->link_data[idx]; - - if (!data->started || !link_data->beacon_int) { - hrtimer_cancel(&link_data->beacon_timer); - } else if (!hrtimer_is_queued(&link_data->beacon_timer)) { - u64 tsf = mac80211_hwsim_get_tsf(hw, NULL); - u32 bcn_int = link_data->beacon_int; - u64 until_tbtt = bcn_int - do_div(tsf, bcn_int); - - hrtimer_start(&link_data->beacon_timer, - ns_to_ktime(until_tbtt * NSEC_PER_USEC), - HRTIMER_MODE_REL_SOFT); - } - } - - return 0; -} - - -static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags,u64 multicast) -{ - struct mac80211_hwsim_data *data = hw->priv; - - wiphy_dbg(hw->wiphy, "%s\n", __func__); - - data->rx_filter = 0; - if (*total_flags & FIF_ALLMULTI) - data->rx_filter |= FIF_ALLMULTI; - if (*total_flags & FIF_MCAST_ACTION) - data->rx_filter |= FIF_MCAST_ACTION; - - *total_flags = data->rx_filter; -} - -static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - unsigned int *count = data; - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - - if (vp->bcn_en) - (*count)++; -} - -static void mac80211_hwsim_vif_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u64 changed) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - - hwsim_check_magic(vif); - - wiphy_dbg(hw->wiphy, "%s(changed=0x%llx vif->addr=%pM)\n", - __func__, changed, vif->addr); - - if (changed & BSS_CHANGED_ASSOC) { - wiphy_dbg(hw->wiphy, " ASSOC: assoc=%d aid=%d\n", - vif->cfg.assoc, vif->cfg.aid); - vp->assoc = vif->cfg.assoc; - vp->aid = vif->cfg.aid; - } -} - -static void mac80211_hwsim_link_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u64 changed) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct mac80211_hwsim_data *data = hw->priv; - unsigned int link_id = info->link_id; - struct mac80211_hwsim_link_data *link_data = &data->link_data[link_id]; - - hwsim_check_magic(vif); - - wiphy_dbg(hw->wiphy, "%s(changed=0x%llx vif->addr=%pM, link id %u)\n", - __func__, (unsigned long long)changed, vif->addr, link_id); - - if (changed & BSS_CHANGED_BSSID) { - wiphy_dbg(hw->wiphy, "%s: BSSID changed: %pM\n", - __func__, info->bssid); - memcpy(vp->bssid, info->bssid, ETH_ALEN); - } - - if (changed & BSS_CHANGED_BEACON_ENABLED) { - wiphy_dbg(hw->wiphy, " BCN EN: %d (BI=%u)\n", - info->enable_beacon, info->beacon_int); - vp->bcn_en = info->enable_beacon; - if (data->started && - !hrtimer_is_queued(&link_data->beacon_timer) && - info->enable_beacon) { - u64 tsf, until_tbtt; - u32 bcn_int; - link_data->beacon_int = info->beacon_int * 1024; - tsf = mac80211_hwsim_get_tsf(hw, vif); - bcn_int = link_data->beacon_int; - until_tbtt = bcn_int - do_div(tsf, bcn_int); - - hrtimer_start(&link_data->beacon_timer, - ns_to_ktime(until_tbtt * NSEC_PER_USEC), - HRTIMER_MODE_REL_SOFT); - } else if (!info->enable_beacon) { - unsigned int count = 0; - ieee80211_iterate_active_interfaces_atomic( - data->hw, IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_bcn_en_iter, &count); - wiphy_dbg(hw->wiphy, " beaconing vifs remaining: %u", - count); - if (count == 0) { - hrtimer_cancel(&link_data->beacon_timer); - link_data->beacon_int = 0; - } - } - } - - if (changed & BSS_CHANGED_ERP_CTS_PROT) { - wiphy_dbg(hw->wiphy, " ERP_CTS_PROT: %d\n", - info->use_cts_prot); - } - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - wiphy_dbg(hw->wiphy, " ERP_PREAMBLE: %d\n", - info->use_short_preamble); - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - wiphy_dbg(hw->wiphy, " ERP_SLOT: %d\n", info->use_short_slot); - } - - if (changed & BSS_CHANGED_HT) { - wiphy_dbg(hw->wiphy, " HT: op_mode=0x%x\n", - info->ht_operation_mode); - } - - if (changed & BSS_CHANGED_BASIC_RATES) { - wiphy_dbg(hw->wiphy, " BASIC_RATES: 0x%llx\n", - (unsigned long long) info->basic_rates); - } - - if (changed & BSS_CHANGED_TXPOWER) - wiphy_dbg(hw->wiphy, " TX Power: %d dBm\n", info->txpower); -} - -static void -mac80211_hwsim_sta_rc_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u32 changed) -{ - struct mac80211_hwsim_data *data = hw->priv; - u32 bw = U32_MAX; - int link_id; - - rcu_read_lock(); - for (link_id = 0; - link_id < ARRAY_SIZE(vif->link_conf); - link_id++) { - enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; - struct ieee80211_bss_conf *vif_conf; - struct ieee80211_link_sta *link_sta; - - link_sta = rcu_dereference(sta->link[link_id]); - - if (!link_sta) - continue; - - switch (link_sta->bandwidth) { -#define C(_bw) case IEEE80211_STA_RX_BW_##_bw: bw = _bw; break - C(20); - C(40); - C(80); - C(160); - C(320); -#undef C - } - - if (!data->use_chanctx) { - confbw = data->bw; - } else { - struct ieee80211_chanctx_conf *chanctx_conf; - - vif_conf = rcu_dereference(vif->link_conf[link_id]); - if (WARN_ON(!vif_conf)) - continue; - - chanctx_conf = rcu_dereference(vif_conf->chanctx_conf); - - if (!WARN_ON(!chanctx_conf)) - confbw = chanctx_conf->def.width; - } - - WARN(bw > hwsim_get_chanwidth(confbw), - "intf %pM [link=%d]: bad STA %pM bandwidth %d MHz (%d) > channel config %d MHz (%d)\n", - vif->addr, link_id, sta->addr, bw, sta->deflink.bandwidth, - hwsim_get_chanwidth(data->bw), data->bw); - - - } - rcu_read_unlock(); - - -} - -static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct hwsim_sta_priv *sp = (void *)sta->drv_priv; - - hwsim_check_magic(vif); - hwsim_set_sta_magic(sta); - mac80211_hwsim_sta_rc_update(hw, vif, sta, 0); - - if (sta->valid_links) { - WARN(hweight16(sta->valid_links) > 1, - "expect to add STA with single link, have 0x%x\n", - sta->valid_links); - sp->active_links_rx = sta->valid_links; - } - - return 0; -} - -static int mac80211_hwsim_sta_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - hwsim_check_magic(vif); - hwsim_clear_sta_magic(sta); - - return 0; -} - -static int mac80211_hwsim_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) -{ - if (new_state == IEEE80211_STA_NOTEXIST) - return mac80211_hwsim_sta_remove(hw, vif, sta); - - if (old_state == IEEE80211_STA_NOTEXIST) - return mac80211_hwsim_sta_add(hw, vif, sta); - - /* - * when client is authorized (AP station marked as such), - * enable all links - */ - if (vif->type == NL80211_IFTYPE_STATION && - new_state == IEEE80211_STA_AUTHORIZED && !sta->tdls) - ieee80211_set_active_links_async(vif, vif->valid_links); - - return 0; -} - -static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) -{ - hwsim_check_magic(vif); - - switch (cmd) { - case STA_NOTIFY_SLEEP: - case STA_NOTIFY_AWAKE: - /* TODO: make good use of these flags */ - break; - default: - WARN(1, "Invalid sta notify: %d\n", cmd); - break; - } -} - -static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, - bool set) -{ - hwsim_check_sta_magic(sta); - return 0; -} - -static int mac80211_hwsim_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - unsigned int link_id, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - wiphy_dbg(hw->wiphy, - "%s (queue=%d txop=%d cw_min=%d cw_max=%d aifs=%d)\n", - __func__, queue, - params->txop, params->cw_min, - params->cw_max, params->aifs); - return 0; -} - -static int mac80211_hwsim_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - - if (idx < 0 || idx >= ARRAY_SIZE(hwsim->survey_data)) - return -ENOENT; - - mutex_lock(&hwsim->mutex); - survey->channel = hwsim->survey_data[idx].channel; - if (!survey->channel) { - mutex_unlock(&hwsim->mutex); - return -ENOENT; - } - - /* - * Magically conjured dummy values --- this is only ok for simulated hardware. - * - * A real driver which cannot determine real values noise MUST NOT - * report any, especially not a magically conjured ones :-) - */ - survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY; - survey->noise = -92; - survey->time = - jiffies_to_msecs(hwsim->survey_data[idx].end - - hwsim->survey_data[idx].start); - /* report 12.5% of channel time is used */ - survey->time_busy = survey->time/8; - mutex_unlock(&hwsim->mutex); - - return 0; -} - -#ifdef CONFIG_NL80211_TESTMODE -/* - * This section contains example code for using netlink - * attributes with the testmode command in nl80211. - */ - -/* These enums need to be kept in sync with userspace */ -enum hwsim_testmode_attr { - __HWSIM_TM_ATTR_INVALID = 0, - HWSIM_TM_ATTR_CMD = 1, - HWSIM_TM_ATTR_PS = 2, - - /* keep last */ - __HWSIM_TM_ATTR_AFTER_LAST, - HWSIM_TM_ATTR_MAX = __HWSIM_TM_ATTR_AFTER_LAST - 1 -}; - -enum hwsim_testmode_cmd { - HWSIM_TM_CMD_SET_PS = 0, - HWSIM_TM_CMD_GET_PS = 1, - HWSIM_TM_CMD_STOP_QUEUES = 2, - HWSIM_TM_CMD_WAKE_QUEUES = 3, -}; - -static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { - [HWSIM_TM_ATTR_CMD] = { .type = NLA_U32 }, - [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, -}; - -static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - void *data, int len) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - struct nlattr *tb[HWSIM_TM_ATTR_MAX + 1]; - struct sk_buff *skb; - int err, ps; - - err = nla_parse_deprecated(tb, HWSIM_TM_ATTR_MAX, data, len, - hwsim_testmode_policy, NULL); - if (err) - return err; - - if (!tb[HWSIM_TM_ATTR_CMD]) - return -EINVAL; - - switch (nla_get_u32(tb[HWSIM_TM_ATTR_CMD])) { - case HWSIM_TM_CMD_SET_PS: - if (!tb[HWSIM_TM_ATTR_PS]) - return -EINVAL; - ps = nla_get_u32(tb[HWSIM_TM_ATTR_PS]); - return hwsim_fops_ps_write(hwsim, ps); - case HWSIM_TM_CMD_GET_PS: - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - nla_total_size(sizeof(u32))); - if (!skb) - return -ENOMEM; - if (nla_put_u32(skb, HWSIM_TM_ATTR_PS, hwsim->ps)) - goto nla_put_failure; - return cfg80211_testmode_reply(skb); - case HWSIM_TM_CMD_STOP_QUEUES: - ieee80211_stop_queues(hw); - return 0; - case HWSIM_TM_CMD_WAKE_QUEUES: - ieee80211_wake_queues(hw); - return 0; - default: - return -EOPNOTSUPP; - } - - nla_put_failure: - kfree_skb(skb); - return -ENOBUFS; -} -#endif - -static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_ampdu_params *params) -{ - struct ieee80211_sta *sta = params->sta; - enum ieee80211_ampdu_mlme_action action = params->action; - u16 tid = params->tid; - - switch (action) { - case IEEE80211_AMPDU_TX_START: - return IEEE80211_AMPDU_TX_START_IMMEDIATE; - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - break; - case IEEE80211_AMPDU_RX_START: - case IEEE80211_AMPDU_RX_STOP: - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static void mac80211_hwsim_flush(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u32 queues, bool drop) -{ - /* Not implemented, queues only on kernel side */ -} - -static void hw_scan_work(struct work_struct *work) -{ - struct mac80211_hwsim_data *hwsim = - container_of(work, struct mac80211_hwsim_data, hw_scan.work); - struct cfg80211_scan_request *req = hwsim->hw_scan_request; - int dwell, i; - - mutex_lock(&hwsim->mutex); - if (hwsim->scan_chan_idx >= req->n_channels) { - struct cfg80211_scan_info info = { - .aborted = false, - }; - - wiphy_dbg(hwsim->hw->wiphy, "hw scan complete\n"); - ieee80211_scan_completed(hwsim->hw, &info); - hwsim->hw_scan_request = NULL; - hwsim->hw_scan_vif = NULL; - hwsim->tmp_chan = NULL; - mutex_unlock(&hwsim->mutex); - mac80211_hwsim_config_mac_nl(hwsim->hw, hwsim->scan_addr, - false); - return; - } - - wiphy_dbg(hwsim->hw->wiphy, "hw scan %d MHz\n", - req->channels[hwsim->scan_chan_idx]->center_freq); - - hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx]; - if (hwsim->tmp_chan->flags & (IEEE80211_CHAN_NO_IR | - IEEE80211_CHAN_RADAR) || - !req->n_ssids) { - dwell = 120; - } else { - dwell = 30; - /* send probes */ - for (i = 0; i < req->n_ssids; i++) { - struct sk_buff *probe; - struct ieee80211_mgmt *mgmt; - - probe = ieee80211_probereq_get(hwsim->hw, - hwsim->scan_addr, - req->ssids[i].ssid, - req->ssids[i].ssid_len, - req->ie_len); - if (!probe) - continue; - - mgmt = (struct ieee80211_mgmt *) probe->data; - memcpy(mgmt->da, req->bssid, ETH_ALEN); - memcpy(mgmt->bssid, req->bssid, ETH_ALEN); - - if (req->ie_len) - skb_put_data(probe, req->ie, req->ie_len); - - rcu_read_lock(); - if (!ieee80211_tx_prepare_skb(hwsim->hw, - hwsim->hw_scan_vif, - probe, - hwsim->tmp_chan->band, - NULL)) { - rcu_read_unlock(); - kfree_skb(probe); - continue; - } - - local_bh_disable(); - mac80211_hwsim_tx_frame(hwsim->hw, probe, - hwsim->tmp_chan); - rcu_read_unlock(); - local_bh_enable(); - } - } - ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, - msecs_to_jiffies(dwell)); - hwsim->survey_data[hwsim->scan_chan_idx].channel = hwsim->tmp_chan; - hwsim->survey_data[hwsim->scan_chan_idx].start = jiffies; - hwsim->survey_data[hwsim->scan_chan_idx].end = - jiffies + msecs_to_jiffies(dwell); - hwsim->scan_chan_idx++; - mutex_unlock(&hwsim->mutex); -} - -static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - struct cfg80211_scan_request *req = &hw_req->req; - - mutex_lock(&hwsim->mutex); - if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { - mutex_unlock(&hwsim->mutex); - return -EBUSY; - } - hwsim->hw_scan_request = req; - hwsim->hw_scan_vif = vif; - hwsim->scan_chan_idx = 0; - if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) - get_random_mask_addr(hwsim->scan_addr, - hw_req->req.mac_addr, - hw_req->req.mac_addr_mask); - else - memcpy(hwsim->scan_addr, vif->addr, ETH_ALEN); - memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); - mutex_unlock(&hwsim->mutex); - - mac80211_hwsim_config_mac_nl(hw, hwsim->scan_addr, true); - wiphy_dbg(hw->wiphy, "hwsim hw_scan request\n"); - - ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, 0); - - return 0; -} - -static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - struct cfg80211_scan_info info = { - .aborted = true, - }; - - wiphy_dbg(hw->wiphy, "hwsim cancel_hw_scan\n"); - - cancel_delayed_work_sync(&hwsim->hw_scan); - - mutex_lock(&hwsim->mutex); - ieee80211_scan_completed(hwsim->hw, &info); - hwsim->tmp_chan = NULL; - hwsim->hw_scan_request = NULL; - hwsim->hw_scan_vif = NULL; - mutex_unlock(&hwsim->mutex); -} - -static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *mac_addr) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - - mutex_lock(&hwsim->mutex); - - if (hwsim->scanning) { - pr_debug("two hwsim sw_scans detected!\n"); - goto out; - } - - pr_debug("hwsim sw_scan request, prepping stuff\n"); - - memcpy(hwsim->scan_addr, mac_addr, ETH_ALEN); - mac80211_hwsim_config_mac_nl(hw, hwsim->scan_addr, true); - hwsim->scanning = true; - memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); - -out: - mutex_unlock(&hwsim->mutex); -} - -static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - - mutex_lock(&hwsim->mutex); - - pr_debug("hwsim sw_scan_complete\n"); - hwsim->scanning = false; - mac80211_hwsim_config_mac_nl(hw, hwsim->scan_addr, false); - eth_zero_addr(hwsim->scan_addr); - - mutex_unlock(&hwsim->mutex); -} - -static void hw_roc_start(struct work_struct *work) -{ - struct mac80211_hwsim_data *hwsim = - container_of(work, struct mac80211_hwsim_data, roc_start.work); - - mutex_lock(&hwsim->mutex); - - wiphy_dbg(hwsim->hw->wiphy, "hwsim ROC begins\n"); - hwsim->tmp_chan = hwsim->roc_chan; - ieee80211_ready_on_channel(hwsim->hw); - - ieee80211_queue_delayed_work(hwsim->hw, &hwsim->roc_done, - msecs_to_jiffies(hwsim->roc_duration)); - - mutex_unlock(&hwsim->mutex); -} - -static void hw_roc_done(struct work_struct *work) -{ - struct mac80211_hwsim_data *hwsim = - container_of(work, struct mac80211_hwsim_data, roc_done.work); - - mutex_lock(&hwsim->mutex); - ieee80211_remain_on_channel_expired(hwsim->hw); - hwsim->tmp_chan = NULL; - mutex_unlock(&hwsim->mutex); - - wiphy_dbg(hwsim->hw->wiphy, "hwsim ROC expired\n"); -} - -static int mac80211_hwsim_roc(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel *chan, - int duration, - enum ieee80211_roc_type type) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - - mutex_lock(&hwsim->mutex); - if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { - mutex_unlock(&hwsim->mutex); - return -EBUSY; - } - - hwsim->roc_chan = chan; - hwsim->roc_duration = duration; - mutex_unlock(&hwsim->mutex); - - wiphy_dbg(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n", - chan->center_freq, duration); - ieee80211_queue_delayed_work(hw, &hwsim->roc_start, HZ/50); - - return 0; -} - -static int mac80211_hwsim_croc(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *hwsim = hw->priv; - - cancel_delayed_work_sync(&hwsim->roc_start); - cancel_delayed_work_sync(&hwsim->roc_done); - - mutex_lock(&hwsim->mutex); - hwsim->tmp_chan = NULL; - mutex_unlock(&hwsim->mutex); - - wiphy_dbg(hw->wiphy, "hwsim ROC canceled\n"); - - return 0; -} - -static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) -{ - hwsim_set_chanctx_magic(ctx); - wiphy_dbg(hw->wiphy, - "add channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", - ctx->def.chan->center_freq, ctx->def.width, - ctx->def.center_freq1, ctx->def.center_freq2); - return 0; -} - -static void mac80211_hwsim_remove_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) -{ - wiphy_dbg(hw->wiphy, - "remove channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", - ctx->def.chan->center_freq, ctx->def.width, - ctx->def.center_freq1, ctx->def.center_freq2); - hwsim_check_chanctx_magic(ctx); - hwsim_clear_chanctx_magic(ctx); -} - -static void mac80211_hwsim_change_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx, - u32 changed) -{ - hwsim_check_chanctx_magic(ctx); - wiphy_dbg(hw->wiphy, - "change channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", - ctx->def.chan->center_freq, ctx->def.width, - ctx->def.center_freq1, ctx->def.center_freq2); -} - -static int mac80211_hwsim_assign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link_conf, - struct ieee80211_chanctx_conf *ctx) -{ - hwsim_check_magic(vif); - hwsim_check_chanctx_magic(ctx); - - /* if we activate a link while already associated wake it up */ - if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc) { - struct sk_buff *skb; - - skb = ieee80211_nullfunc_get(hw, vif, link_conf->link_id, true); - if (skb) { - local_bh_disable(); - mac80211_hwsim_tx_frame(hw, skb, ctx->def.chan); - local_bh_enable(); - } - } - - return 0; -} - -static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link_conf, - struct ieee80211_chanctx_conf *ctx) -{ - hwsim_check_magic(vif); - hwsim_check_chanctx_magic(ctx); - - /* if we deactivate a link while associated suspend it first */ - if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc) { - struct sk_buff *skb; - - skb = ieee80211_nullfunc_get(hw, vif, link_conf->link_id, true); - if (skb) { - struct ieee80211_hdr *hdr = (void *)skb->data; - - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); - - local_bh_disable(); - mac80211_hwsim_tx_frame(hw, skb, ctx->def.chan); - local_bh_enable(); - } - } -} - -static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = { - "tx_pkts_nic", - "tx_bytes_nic", - "rx_pkts_nic", - "rx_bytes_nic", - "d_tx_dropped", - "d_tx_failed", - "d_ps_mode", - "d_group", -}; - -#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats) - -static void mac80211_hwsim_get_et_strings(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u32 sset, u8 *data) -{ - if (sset == ETH_SS_STATS) - memcpy(data, *mac80211_hwsim_gstrings_stats, - sizeof(mac80211_hwsim_gstrings_stats)); -} - -static int mac80211_hwsim_get_et_sset_count(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, int sset) -{ - if (sset == ETH_SS_STATS) - return MAC80211_HWSIM_SSTATS_LEN; - return 0; -} - -static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ethtool_stats *stats, u64 *data) -{ - struct mac80211_hwsim_data *ar = hw->priv; - int i = 0; - - data[i++] = ar->tx_pkts; - data[i++] = ar->tx_bytes; - data[i++] = ar->rx_pkts; - data[i++] = ar->rx_bytes; - data[i++] = ar->tx_dropped; - data[i++] = ar->tx_failed; - data[i++] = ar->ps; - data[i++] = ar->group; - - WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN); -} - -static int mac80211_hwsim_tx_last_beacon(struct ieee80211_hw *hw) -{ - return 1; -} - -static int mac80211_hwsim_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - return -EOPNOTSUPP; -} - -static int mac80211_hwsim_change_vif_links(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u16 old_links, u16 new_links, - struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) -{ - unsigned long rem = old_links & ~new_links; - unsigned long add = new_links & ~old_links; - int i; - - if (!old_links) - rem |= BIT(0); - if (!new_links) - add |= BIT(0); - - for_each_set_bit(i, &rem, IEEE80211_MLD_MAX_NUM_LINKS) - mac80211_hwsim_config_mac_nl(hw, old[i]->addr, false); - - for_each_set_bit(i, &add, IEEE80211_MLD_MAX_NUM_LINKS) { - struct ieee80211_bss_conf *link_conf; - - link_conf = link_conf_dereference_protected(vif, i); - if (WARN_ON(!link_conf)) - continue; - - mac80211_hwsim_config_mac_nl(hw, link_conf->addr, true); - } - - return 0; -} - -static int mac80211_hwsim_change_sta_links(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - u16 old_links, u16 new_links) -{ - struct hwsim_sta_priv *sp = (void *)sta->drv_priv; - - hwsim_check_sta_magic(sta); - - if (vif->type == NL80211_IFTYPE_STATION) - sp->active_links_rx = new_links; - - return 0; -} - -#define HWSIM_COMMON_OPS \ - .tx = mac80211_hwsim_tx, \ - .wake_tx_queue = ieee80211_handle_wake_tx_queue, \ - .start = mac80211_hwsim_start, \ - .stop = mac80211_hwsim_stop, \ - .add_interface = mac80211_hwsim_add_interface, \ - .change_interface = mac80211_hwsim_change_interface, \ - .remove_interface = mac80211_hwsim_remove_interface, \ - .config = mac80211_hwsim_config, \ - .configure_filter = mac80211_hwsim_configure_filter, \ - .vif_cfg_changed = mac80211_hwsim_vif_info_changed, \ - .link_info_changed = mac80211_hwsim_link_info_changed, \ - .tx_last_beacon = mac80211_hwsim_tx_last_beacon, \ - .sta_notify = mac80211_hwsim_sta_notify, \ - .sta_rc_update = mac80211_hwsim_sta_rc_update, \ - .conf_tx = mac80211_hwsim_conf_tx, \ - .get_survey = mac80211_hwsim_get_survey, \ - CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) \ - .ampdu_action = mac80211_hwsim_ampdu_action, \ - .flush = mac80211_hwsim_flush, \ - .get_et_sset_count = mac80211_hwsim_get_et_sset_count, \ - .get_et_stats = mac80211_hwsim_get_et_stats, \ - .get_et_strings = mac80211_hwsim_get_et_strings, - -#define HWSIM_NON_MLO_OPS \ - .sta_add = mac80211_hwsim_sta_add, \ - .sta_remove = mac80211_hwsim_sta_remove, \ - .set_tim = mac80211_hwsim_set_tim, \ - .get_tsf = mac80211_hwsim_get_tsf, \ - .set_tsf = mac80211_hwsim_set_tsf, - -static const struct ieee80211_ops mac80211_hwsim_ops = { - HWSIM_COMMON_OPS - HWSIM_NON_MLO_OPS - .sw_scan_start = mac80211_hwsim_sw_scan, - .sw_scan_complete = mac80211_hwsim_sw_scan_complete, -}; - -#define HWSIM_CHANCTX_OPS \ - .hw_scan = mac80211_hwsim_hw_scan, \ - .cancel_hw_scan = mac80211_hwsim_cancel_hw_scan, \ - .remain_on_channel = mac80211_hwsim_roc, \ - .cancel_remain_on_channel = mac80211_hwsim_croc, \ - .add_chanctx = mac80211_hwsim_add_chanctx, \ - .remove_chanctx = mac80211_hwsim_remove_chanctx, \ - .change_chanctx = mac80211_hwsim_change_chanctx, \ - .assign_vif_chanctx = mac80211_hwsim_assign_vif_chanctx,\ - .unassign_vif_chanctx = mac80211_hwsim_unassign_vif_chanctx, - -static const struct ieee80211_ops mac80211_hwsim_mchan_ops = { - HWSIM_COMMON_OPS - HWSIM_NON_MLO_OPS - HWSIM_CHANCTX_OPS -}; - -static const struct ieee80211_ops mac80211_hwsim_mlo_ops = { - HWSIM_COMMON_OPS - HWSIM_CHANCTX_OPS - .set_rts_threshold = mac80211_hwsim_set_rts_threshold, - .change_vif_links = mac80211_hwsim_change_vif_links, - .change_sta_links = mac80211_hwsim_change_sta_links, - .sta_state = mac80211_hwsim_sta_state, -}; - -struct hwsim_new_radio_params { - unsigned int channels; - const char *reg_alpha2; - const struct ieee80211_regdomain *regd; - bool reg_strict; - bool p2p_device; - bool use_chanctx; - bool destroy_on_close; - const char *hwname; - bool no_vif; - const u8 *perm_addr; - u32 iftypes; - u32 *ciphers; - u8 n_ciphers; - bool mlo; -}; - -static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, - struct genl_info *info) -{ - if (info) - genl_notify(&hwsim_genl_family, mcast_skb, info, - HWSIM_MCGRP_CONFIG, GFP_KERNEL); - else - genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0, - HWSIM_MCGRP_CONFIG, GFP_KERNEL); -} - -static int append_radio_msg(struct sk_buff *skb, int id, - struct hwsim_new_radio_params *param) -{ - int ret; - - ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id); - if (ret < 0) - return ret; - - if (param->channels) { - ret = nla_put_u32(skb, HWSIM_ATTR_CHANNELS, param->channels); - if (ret < 0) - return ret; - } - - if (param->reg_alpha2) { - ret = nla_put(skb, HWSIM_ATTR_REG_HINT_ALPHA2, 2, - param->reg_alpha2); - if (ret < 0) - return ret; - } - - if (param->regd) { - int i; - - for (i = 0; i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) { - if (hwsim_world_regdom_custom[i] != param->regd) - continue; - - ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i); - if (ret < 0) - return ret; - break; - } - } - - if (param->reg_strict) { - ret = nla_put_flag(skb, HWSIM_ATTR_REG_STRICT_REG); - if (ret < 0) - return ret; - } - - if (param->p2p_device) { - ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_P2P_DEVICE); - if (ret < 0) - return ret; - } - - if (param->use_chanctx) { - ret = nla_put_flag(skb, HWSIM_ATTR_USE_CHANCTX); - if (ret < 0) - return ret; - } - - if (param->hwname) { - ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, - strlen(param->hwname), param->hwname); - if (ret < 0) - return ret; - } - - return 0; -} - -static void hwsim_mcast_new_radio(int id, struct genl_info *info, - struct hwsim_new_radio_params *param) -{ - struct sk_buff *mcast_skb; - void *data; - - mcast_skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!mcast_skb) - return; - - data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0, - HWSIM_CMD_NEW_RADIO); - if (!data) - goto out_err; - - if (append_radio_msg(mcast_skb, id, param) < 0) - goto out_err; - - genlmsg_end(mcast_skb, data); - - hwsim_mcast_config_msg(mcast_skb, info); - return; - -out_err: - nlmsg_free(mcast_skb); -} - -static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { - { - .types_mask = BIT(NL80211_IFTYPE_STATION), - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_BSR | - IEEE80211_HE_MAC_CAP2_MU_CASCADING | - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = - IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | - IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xffff), - .tx_mcs_160 = cpu_to_le16(0xffff), - .rx_mcs_80p80 = cpu_to_le16(0xffff), - .tx_mcs_80p80 = cpu_to_le16(0xffff), - }, - }, - .eht_cap = { - .has_eht = true, - .eht_cap_elem = { - .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | - IEEE80211_EHT_MAC_CAP0_OM_CONTROL | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, - .phy_cap_info[0] = - IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | - IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | - IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE, - .phy_cap_info[3] = - IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, - .phy_cap_info[4] = - IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | - IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | - IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | - IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | - IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, - .phy_cap_info[5] = - IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | - IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | - IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | - IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, - .phy_cap_info[6] = - IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | - IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, - .phy_cap_info[7] = - IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW, - }, - - /* For all MCS and bandwidth, set 8 NSS for both Tx and - * Rx - */ - .eht_mcs_nss_supp = { - /* - * Since B0, B1, B2 and B3 are not set in - * the supported channel width set field in the - * HE PHY capabilities information field the - * device is a 20MHz only device on 2.4GHz band. - */ - .only_20mhz = { - .rx_tx_mcs7_max_nss = 0x88, - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - }, - /* PPE threshold information is not supported */ - }, - }, - { - .types_mask = BIT(NL80211_IFTYPE_AP), - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_BSR | - IEEE80211_HE_MAC_CAP2_MU_CASCADING | - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = - IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | - IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xffff), - .tx_mcs_160 = cpu_to_le16(0xffff), - .rx_mcs_80p80 = cpu_to_le16(0xffff), - .tx_mcs_80p80 = cpu_to_le16(0xffff), - }, - }, - .eht_cap = { - .has_eht = true, - .eht_cap_elem = { - .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | - IEEE80211_EHT_MAC_CAP0_OM_CONTROL | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, - .phy_cap_info[0] = - IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | - IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | - IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE, - .phy_cap_info[3] = - IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, - .phy_cap_info[4] = - IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | - IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | - IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | - IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | - IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, - .phy_cap_info[5] = - IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | - IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | - IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | - IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, - .phy_cap_info[6] = - IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | - IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, - .phy_cap_info[7] = - IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW, - }, - - /* For all MCS and bandwidth, set 8 NSS for both Tx and - * Rx - */ - .eht_mcs_nss_supp = { - /* - * Since B0, B1, B2 and B3 are not set in - * the supported channel width set field in the - * HE PHY capabilities information field the - * device is a 20MHz only device on 2.4GHz band. - */ - .only_20mhz = { - .rx_tx_mcs7_max_nss = 0x88, - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - }, - /* PPE threshold information is not supported */ - }, - }, -#ifdef CONFIG_MAC80211_MESH - { - .types_mask = BIT(NL80211_IFTYPE_MESH_POINT), - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = 0, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xffff), - .tx_mcs_160 = cpu_to_le16(0xffff), - .rx_mcs_80p80 = cpu_to_le16(0xffff), - .tx_mcs_80p80 = cpu_to_le16(0xffff), - }, - }, - }, -#endif -}; - -static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { - { - /* TODO: should we support other types, e.g., P2P? */ - .types_mask = BIT(NL80211_IFTYPE_STATION), - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_BSR | - IEEE80211_HE_MAC_CAP2_MU_CASCADING | - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = - IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | - IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xfffa), - .tx_mcs_160 = cpu_to_le16(0xfffa), - .rx_mcs_80p80 = cpu_to_le16(0xfffa), - .tx_mcs_80p80 = cpu_to_le16(0xfffa), - }, - }, - .eht_cap = { - .has_eht = true, - .eht_cap_elem = { - .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | - IEEE80211_EHT_MAC_CAP0_OM_CONTROL | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, - .phy_cap_info[0] = - IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | - IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | - IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | - IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, - .phy_cap_info[1] = - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK, - .phy_cap_info[2] = - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK, - .phy_cap_info[3] = - IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, - .phy_cap_info[4] = - IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | - IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | - IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | - IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | - IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, - .phy_cap_info[5] = - IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | - IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | - IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | - IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, - .phy_cap_info[6] = - IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | - IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, - .phy_cap_info[7] = - IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ, - }, - - /* For all MCS and bandwidth, set 8 NSS for both Tx and - * Rx - */ - .eht_mcs_nss_supp = { - /* - * As B1 and B2 are set in the supported - * channel width set field in the HE PHY - * capabilities information field include all - * the following MCS/NSS. - */ - .bw._80 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - .bw._160 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - }, - /* PPE threshold information is not supported */ - }, - }, - { - .types_mask = BIT(NL80211_IFTYPE_AP), - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_BSR | - IEEE80211_HE_MAC_CAP2_MU_CASCADING | - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = - IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | - IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xfffa), - .tx_mcs_160 = cpu_to_le16(0xfffa), - .rx_mcs_80p80 = cpu_to_le16(0xfffa), - .tx_mcs_80p80 = cpu_to_le16(0xfffa), - }, - }, - .eht_cap = { - .has_eht = true, - .eht_cap_elem = { - .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | - IEEE80211_EHT_MAC_CAP0_OM_CONTROL | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, - .phy_cap_info[0] = - IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | - IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | - IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | - IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, - .phy_cap_info[1] = - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK, - .phy_cap_info[2] = - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK, - .phy_cap_info[3] = - IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, - .phy_cap_info[4] = - IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | - IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | - IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | - IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | - IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, - .phy_cap_info[5] = - IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | - IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | - IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | - IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, - .phy_cap_info[6] = - IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | - IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, - .phy_cap_info[7] = - IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ, - }, - - /* For all MCS and bandwidth, set 8 NSS for both Tx and - * Rx - */ - .eht_mcs_nss_supp = { - /* - * As B1 and B2 are set in the supported - * channel width set field in the HE PHY - * capabilities information field include all - * the following MCS/NSS. - */ - .bw._80 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - .bw._160 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - }, - /* PPE threshold information is not supported */ - }, - }, -#ifdef CONFIG_MAC80211_MESH - { - /* TODO: should we support other types, e.g., IBSS?*/ - .types_mask = BIT(NL80211_IFTYPE_MESH_POINT), - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = 0, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xfffa), - .tx_mcs_160 = cpu_to_le16(0xfffa), - .rx_mcs_80p80 = cpu_to_le16(0xfffa), - .tx_mcs_80p80 = cpu_to_le16(0xfffa), - }, - }, - }, -#endif -}; - -static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { - { - /* TODO: should we support other types, e.g., P2P? */ - .types_mask = BIT(NL80211_IFTYPE_STATION), - .he_6ghz_capa = { - .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START | - IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP | - IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN | - IEEE80211_HE_6GHZ_CAP_SM_PS | - IEEE80211_HE_6GHZ_CAP_RD_RESPONDER | - IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | - IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS), - }, - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_BSR | - IEEE80211_HE_MAC_CAP2_MU_CASCADING | - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = - IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | - IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xfffa), - .tx_mcs_160 = cpu_to_le16(0xfffa), - .rx_mcs_80p80 = cpu_to_le16(0xfffa), - .tx_mcs_80p80 = cpu_to_le16(0xfffa), - }, - }, - .eht_cap = { - .has_eht = true, - .eht_cap_elem = { - .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | - IEEE80211_EHT_MAC_CAP0_OM_CONTROL | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, - .phy_cap_info[0] = - IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ | - IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | - IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | - IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | - IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, - .phy_cap_info[1] = - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK | - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK, - .phy_cap_info[2] = - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK | - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK, - .phy_cap_info[3] = - IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, - .phy_cap_info[4] = - IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | - IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | - IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | - IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | - IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, - .phy_cap_info[5] = - IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | - IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | - IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | - IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, - .phy_cap_info[6] = - IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | - IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | - IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, - .phy_cap_info[7] = - IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ, - }, - - /* For all MCS and bandwidth, set 8 NSS for both Tx and - * Rx - */ - .eht_mcs_nss_supp = { - /* - * As B1 and B2 are set in the supported - * channel width set field in the HE PHY - * capabilities information field and 320MHz in - * 6GHz is supported include all the following - * MCS/NSS. - */ - .bw._80 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - .bw._160 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - .bw._320 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - }, - /* PPE threshold information is not supported */ - }, - }, - { - .types_mask = BIT(NL80211_IFTYPE_AP), - .he_6ghz_capa = { - .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START | - IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP | - IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN | - IEEE80211_HE_6GHZ_CAP_SM_PS | - IEEE80211_HE_6GHZ_CAP_RD_RESPONDER | - IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | - IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS), - }, - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_BSR | - IEEE80211_HE_MAC_CAP2_MU_CASCADING | - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = - IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | - IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | - IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xfffa), - .tx_mcs_160 = cpu_to_le16(0xfffa), - .rx_mcs_80p80 = cpu_to_le16(0xfffa), - .tx_mcs_80p80 = cpu_to_le16(0xfffa), - }, - }, - .eht_cap = { - .has_eht = true, - .eht_cap_elem = { - .mac_cap_info[0] = - IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | - IEEE80211_EHT_MAC_CAP0_OM_CONTROL | - IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, - .phy_cap_info[0] = - IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ | - IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | - IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | - IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | - IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | - IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, - .phy_cap_info[1] = - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK | - IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK, - .phy_cap_info[2] = - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK | - IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK, - .phy_cap_info[3] = - IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | - IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | - IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, - .phy_cap_info[4] = - IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | - IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | - IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | - IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | - IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, - .phy_cap_info[5] = - IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | - IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | - IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | - IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | - IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, - .phy_cap_info[6] = - IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | - IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | - IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, - .phy_cap_info[7] = - IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | - IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ | - IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ, - }, - - /* For all MCS and bandwidth, set 8 NSS for both Tx and - * Rx - */ - .eht_mcs_nss_supp = { - /* - * As B1 and B2 are set in the supported - * channel width set field in the HE PHY - * capabilities information field and 320MHz in - * 6GHz is supported include all the following - * MCS/NSS. - */ - .bw._80 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - .bw._160 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - .bw._320 = { - .rx_tx_mcs9_max_nss = 0x88, - .rx_tx_mcs11_max_nss = 0x88, - .rx_tx_mcs13_max_nss = 0x88, - }, - }, - /* PPE threshold information is not supported */ - }, - }, -#ifdef CONFIG_MAC80211_MESH - { - /* TODO: should we support other types, e.g., IBSS?*/ - .types_mask = BIT(NL80211_IFTYPE_MESH_POINT), - .he_6ghz_capa = { - .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START | - IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP | - IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN | - IEEE80211_HE_6GHZ_CAP_SM_PS | - IEEE80211_HE_6GHZ_CAP_RD_RESPONDER | - IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | - IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS), - }, - .he_cap = { - .has_he = true, - .he_cap_elem = { - .mac_cap_info[0] = - IEEE80211_HE_MAC_CAP0_HTC_HE, - .mac_cap_info[1] = - IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, - .mac_cap_info[2] = - IEEE80211_HE_MAC_CAP2_ACK_EN, - .mac_cap_info[3] = - IEEE80211_HE_MAC_CAP3_OMI_CONTROL | - IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, - .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, - .phy_cap_info[1] = - IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | - IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | - IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, - .phy_cap_info[2] = 0, - - /* Leave all the other PHY capability bytes - * unset, as DCM, beam forming, RU and PPE - * threshold information are not supported - */ - }, - .he_mcs_nss_supp = { - .rx_mcs_80 = cpu_to_le16(0xfffa), - .tx_mcs_80 = cpu_to_le16(0xfffa), - .rx_mcs_160 = cpu_to_le16(0xfffa), - .tx_mcs_160 = cpu_to_le16(0xfffa), - .rx_mcs_80p80 = cpu_to_le16(0xfffa), - .tx_mcs_80p80 = cpu_to_le16(0xfffa), - }, - }, - }, -#endif -}; - -static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband) -{ - u16 n_iftype_data; - - if (sband->band == NL80211_BAND_2GHZ) { - n_iftype_data = ARRAY_SIZE(sband_capa_2ghz); - sband->iftype_data = - (struct ieee80211_sband_iftype_data *)sband_capa_2ghz; - } else if (sband->band == NL80211_BAND_5GHZ) { - n_iftype_data = ARRAY_SIZE(sband_capa_5ghz); - sband->iftype_data = - (struct ieee80211_sband_iftype_data *)sband_capa_5ghz; - } else if (sband->band == NL80211_BAND_6GHZ) { - n_iftype_data = ARRAY_SIZE(sband_capa_6ghz); - sband->iftype_data = - (struct ieee80211_sband_iftype_data *)sband_capa_6ghz; - } else { - return; - } - - sband->n_iftype_data = n_iftype_data; -} - -#ifdef CONFIG_MAC80211_MESH -#define HWSIM_MESH_BIT BIT(NL80211_IFTYPE_MESH_POINT) -#else -#define HWSIM_MESH_BIT 0 -#endif - -#define HWSIM_DEFAULT_IF_LIMIT \ - (BIT(NL80211_IFTYPE_STATION) | \ - BIT(NL80211_IFTYPE_P2P_CLIENT) | \ - BIT(NL80211_IFTYPE_AP) | \ - BIT(NL80211_IFTYPE_P2P_GO) | \ - HWSIM_MESH_BIT) - -#define HWSIM_IFTYPE_SUPPORT_MASK \ - (BIT(NL80211_IFTYPE_STATION) | \ - BIT(NL80211_IFTYPE_AP) | \ - BIT(NL80211_IFTYPE_P2P_CLIENT) | \ - BIT(NL80211_IFTYPE_P2P_GO) | \ - BIT(NL80211_IFTYPE_ADHOC) | \ - BIT(NL80211_IFTYPE_MESH_POINT) | \ - BIT(NL80211_IFTYPE_OCB)) - -static int mac80211_hwsim_new_radio(struct genl_info *info, - struct hwsim_new_radio_params *param) -{ - int err; - u8 addr[ETH_ALEN]; - struct mac80211_hwsim_data *data; - struct ieee80211_hw *hw; - enum nl80211_band band; - const struct ieee80211_ops *ops = &mac80211_hwsim_ops; - struct net *net; - int idx, i; - int n_limits = 0; - - if (WARN_ON(param->channels > 1 && !param->use_chanctx)) - return -EINVAL; - - spin_lock_bh(&hwsim_radio_lock); - idx = hwsim_radio_idx++; - spin_unlock_bh(&hwsim_radio_lock); - - if (param->mlo) - ops = &mac80211_hwsim_mlo_ops; - else if (param->use_chanctx) - ops = &mac80211_hwsim_mchan_ops; - hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname); - if (!hw) { - pr_debug("mac80211_hwsim: ieee80211_alloc_hw failed\n"); - err = -ENOMEM; - goto failed; - } - - /* ieee80211_alloc_hw_nm may have used a default name */ - param->hwname = wiphy_name(hw->wiphy); - - if (info) - net = genl_info_net(info); - else - net = &init_net; - wiphy_net_set(hw->wiphy, net); - - data = hw->priv; - data->hw = hw; - - data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); - if (IS_ERR(data->dev)) { - printk(KERN_DEBUG - "mac80211_hwsim: device_create failed (%ld)\n", - PTR_ERR(data->dev)); - err = -ENOMEM; - goto failed_drvdata; - } - data->dev->driver = &mac80211_hwsim_driver.driver; - err = device_bind_driver(data->dev); - if (err != 0) { - pr_debug("mac80211_hwsim: device_bind_driver failed (%d)\n", - err); - goto failed_bind; - } - - skb_queue_head_init(&data->pending); - - SET_IEEE80211_DEV(hw, data->dev); - if (!param->perm_addr) { - eth_zero_addr(addr); - addr[0] = 0x02; - addr[3] = idx >> 8; - addr[4] = idx; - memcpy(data->addresses[0].addr, addr, ETH_ALEN); - /* Why need here second address ? */ - memcpy(data->addresses[1].addr, addr, ETH_ALEN); - data->addresses[1].addr[0] |= 0x40; - hw->wiphy->n_addresses = 2; - hw->wiphy->addresses = data->addresses; - /* possible address clash is checked at hash table insertion */ - } else { - memcpy(data->addresses[0].addr, param->perm_addr, ETH_ALEN); - /* compatibility with automatically generated mac addr */ - memcpy(data->addresses[1].addr, param->perm_addr, ETH_ALEN); - hw->wiphy->n_addresses = 2; - hw->wiphy->addresses = data->addresses; - } - - data->channels = param->channels; - data->use_chanctx = param->use_chanctx; - data->idx = idx; - data->destroy_on_close = param->destroy_on_close; - if (info) - data->portid = info->snd_portid; - - /* setup interface limits, only on interface types we support */ - if (param->iftypes & BIT(NL80211_IFTYPE_ADHOC)) { - data->if_limits[n_limits].max = 1; - data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_ADHOC); - n_limits++; - } - - if (param->iftypes & HWSIM_DEFAULT_IF_LIMIT) { - data->if_limits[n_limits].max = 2048; - /* - * For this case, we may only support a subset of - * HWSIM_DEFAULT_IF_LIMIT, therefore we only want to add the - * bits that both param->iftype & HWSIM_DEFAULT_IF_LIMIT have. - */ - data->if_limits[n_limits].types = - HWSIM_DEFAULT_IF_LIMIT & param->iftypes; - n_limits++; - } - - if (param->iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) { - data->if_limits[n_limits].max = 1; - data->if_limits[n_limits].types = - BIT(NL80211_IFTYPE_P2P_DEVICE); - n_limits++; - } - - if (data->use_chanctx) { - hw->wiphy->max_scan_ssids = 255; - hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; - hw->wiphy->max_remain_on_channel_duration = 1000; - data->if_combination.radar_detect_widths = 0; - data->if_combination.num_different_channels = data->channels; - } else { - data->if_combination.num_different_channels = 1; - data->if_combination.radar_detect_widths = - BIT(NL80211_CHAN_WIDTH_5) | - BIT(NL80211_CHAN_WIDTH_10) | - BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80) | - BIT(NL80211_CHAN_WIDTH_160); - } - - if (!n_limits) { - err = -EINVAL; - goto failed_hw; - } - - data->if_combination.max_interfaces = 0; - for (i = 0; i < n_limits; i++) - data->if_combination.max_interfaces += - data->if_limits[i].max; - - data->if_combination.n_limits = n_limits; - data->if_combination.limits = data->if_limits; - - /* - * If we actually were asked to support combinations, - * advertise them - if there's only a single thing like - * only IBSS then don't advertise it as combinations. - */ - if (data->if_combination.max_interfaces > 1) { - hw->wiphy->iface_combinations = &data->if_combination; - hw->wiphy->n_iface_combinations = 1; - } - - if (param->ciphers) { - memcpy(data->ciphers, param->ciphers, - param->n_ciphers * sizeof(u32)); - hw->wiphy->cipher_suites = data->ciphers; - hw->wiphy->n_cipher_suites = param->n_ciphers; - } - - data->rx_rssi = DEFAULT_RX_RSSI; - - INIT_DELAYED_WORK(&data->roc_start, hw_roc_start); - INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); - INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); - - hw->queues = 5; - hw->offchannel_tx_hw_queue = 4; - - ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); - ieee80211_hw_set(hw, CHANCTX_STA_CSA); - ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES); - ieee80211_hw_set(hw, QUEUE_CONTROL); - ieee80211_hw_set(hw, WANT_MONITOR_VIF); - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - ieee80211_hw_set(hw, MFP_CAPABLE); - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, SUPPORTS_PS); - ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(hw, TDLS_WIDER_BW); - ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); - - if (param->mlo) { - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; - ieee80211_hw_set(hw, HAS_RATE_CONTROL); - ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); - ieee80211_hw_set(hw, CONNECTION_MONITOR); - ieee80211_hw_set(hw, AP_LINK_PS); - } else { - ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); - ieee80211_hw_set(hw, PS_NULLFUNC_STACK); - if (rctbl) - ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); - } - - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_SUPPORTS_5_10_MHZ | - WIPHY_FLAG_HAS_CHANNEL_SWITCH; - hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR | - NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | - NL80211_FEATURE_STATIC_SMPS | - NL80211_FEATURE_DYNAMIC_SMPS | - NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; - wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS); - wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION); - wiphy_ext_feature_set(hw->wiphy, - NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); - wiphy_ext_feature_set(hw->wiphy, - NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); - - wiphy_ext_feature_set(hw->wiphy, - NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT); - - hw->wiphy->interface_modes = param->iftypes; - - /* ask mac80211 to reserve space for magic */ - hw->vif_data_size = sizeof(struct hwsim_vif_priv); - hw->sta_data_size = sizeof(struct hwsim_sta_priv); - hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); - - memcpy(data->channels_2ghz, hwsim_channels_2ghz, - sizeof(hwsim_channels_2ghz)); - memcpy(data->channels_5ghz, hwsim_channels_5ghz, - sizeof(hwsim_channels_5ghz)); - memcpy(data->channels_6ghz, hwsim_channels_6ghz, - sizeof(hwsim_channels_6ghz)); - memcpy(data->channels_s1g, hwsim_channels_s1g, - sizeof(hwsim_channels_s1g)); - memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); - - for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { - struct ieee80211_supported_band *sband = &data->bands[band]; - - sband->band = band; - - switch (band) { - case NL80211_BAND_2GHZ: - sband->channels = data->channels_2ghz; - sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); - sband->bitrates = data->rates; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates); - break; - case NL80211_BAND_5GHZ: - sband->channels = data->channels_5ghz; - sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); - sband->bitrates = data->rates + 4; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; - - sband->vht_cap.vht_supported = true; - sband->vht_cap.cap = - IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | - IEEE80211_VHT_CAP_RXLDPC | - IEEE80211_VHT_CAP_SHORT_GI_80 | - IEEE80211_VHT_CAP_SHORT_GI_160 | - IEEE80211_VHT_CAP_TXSTBC | - IEEE80211_VHT_CAP_RXSTBC_4 | - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; - sband->vht_cap.vht_mcs.rx_mcs_map = - cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 14); - sband->vht_cap.vht_mcs.tx_mcs_map = - sband->vht_cap.vht_mcs.rx_mcs_map; - break; - case NL80211_BAND_6GHZ: - sband->channels = data->channels_6ghz; - sband->n_channels = ARRAY_SIZE(hwsim_channels_6ghz); - sband->bitrates = data->rates + 4; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; - break; - case NL80211_BAND_S1GHZ: - memcpy(&sband->s1g_cap, &hwsim_s1g_cap, - sizeof(sband->s1g_cap)); - sband->channels = data->channels_s1g; - sband->n_channels = ARRAY_SIZE(hwsim_channels_s1g); - break; - default: - continue; - } - - if (band != NL80211_BAND_6GHZ){ - sband->ht_cap.ht_supported = true; - sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - sband->ht_cap.ampdu_factor = 0x3; - sband->ht_cap.ampdu_density = 0x6; - memset(&sband->ht_cap.mcs, 0, - sizeof(sband->ht_cap.mcs)); - sband->ht_cap.mcs.rx_mask[0] = 0xff; - sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - } - - mac80211_hwsim_sband_capab(sband); - - hw->wiphy->bands[band] = sband; - } - - /* By default all radios belong to the first group */ - data->group = 1; - mutex_init(&data->mutex); - - data->netgroup = hwsim_net_get_netgroup(net); - data->wmediumd = hwsim_net_get_wmediumd(net); - - /* Enable frame retransmissions for lossy channels */ - hw->max_rates = 4; - hw->max_rate_tries = 11; - - hw->wiphy->vendor_commands = mac80211_hwsim_vendor_commands; - hw->wiphy->n_vendor_commands = - ARRAY_SIZE(mac80211_hwsim_vendor_commands); - hw->wiphy->vendor_events = mac80211_hwsim_vendor_events; - hw->wiphy->n_vendor_events = ARRAY_SIZE(mac80211_hwsim_vendor_events); - - if (param->reg_strict) - hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG; - if (param->regd) { - data->regd = param->regd; - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(hw->wiphy, param->regd); - /* give the regulatory workqueue a chance to run */ - schedule_timeout_interruptible(1); - } - - if (param->no_vif) - ieee80211_hw_set(hw, NO_AUTO_VIF); - - wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); - - for (i = 0; i < ARRAY_SIZE(data->link_data); i++) { - hrtimer_init(&data->link_data[i].beacon_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_ABS_SOFT); - data->link_data[i].beacon_timer.function = - mac80211_hwsim_beacon; - data->link_data[i].link_id = i; - } - - err = ieee80211_register_hw(hw); - if (err < 0) { - pr_debug("mac80211_hwsim: ieee80211_register_hw failed (%d)\n", - err); - goto failed_hw; - } - - wiphy_dbg(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); - - if (param->reg_alpha2) { - data->alpha2[0] = param->reg_alpha2[0]; - data->alpha2[1] = param->reg_alpha2[1]; - regulatory_hint(hw->wiphy, param->reg_alpha2); - } - - data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); - debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); - debugfs_create_file("group", 0666, data->debugfs, data, - &hwsim_fops_group); - debugfs_create_file("rx_rssi", 0666, data->debugfs, data, - &hwsim_fops_rx_rssi); - if (!data->use_chanctx) - debugfs_create_file("dfs_simulate_radar", 0222, - data->debugfs, - data, &hwsim_simulate_radar); - - spin_lock_bh(&hwsim_radio_lock); - err = rhashtable_insert_fast(&hwsim_radios_rht, &data->rht, - hwsim_rht_params); - if (err < 0) { - if (info) { - GENL_SET_ERR_MSG(info, "perm addr already present"); - NL_SET_BAD_ATTR(info->extack, - info->attrs[HWSIM_ATTR_PERM_ADDR]); - } - spin_unlock_bh(&hwsim_radio_lock); - goto failed_final_insert; - } - - list_add_tail(&data->list, &hwsim_radios); - hwsim_radios_generation++; - spin_unlock_bh(&hwsim_radio_lock); - - hwsim_mcast_new_radio(idx, info, param); - - return idx; - -failed_final_insert: - debugfs_remove_recursive(data->debugfs); - ieee80211_unregister_hw(data->hw); -failed_hw: - device_release_driver(data->dev); -failed_bind: - device_unregister(data->dev); -failed_drvdata: - ieee80211_free_hw(hw); -failed: - return err; -} - -static void hwsim_mcast_del_radio(int id, const char *hwname, - struct genl_info *info) -{ - struct sk_buff *skb; - void *data; - int ret; - - skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!skb) - return; - - data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, - HWSIM_CMD_DEL_RADIO); - if (!data) - goto error; - - ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id); - if (ret < 0) - goto error; - - ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname), - hwname); - if (ret < 0) - goto error; - - genlmsg_end(skb, data); - - hwsim_mcast_config_msg(skb, info); - - return; - -error: - nlmsg_free(skb); -} - -static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data, - const char *hwname, - struct genl_info *info) -{ - hwsim_mcast_del_radio(data->idx, hwname, info); - debugfs_remove_recursive(data->debugfs); - ieee80211_unregister_hw(data->hw); - device_release_driver(data->dev); - device_unregister(data->dev); - ieee80211_free_hw(data->hw); -} - -static int mac80211_hwsim_get_radio(struct sk_buff *skb, - struct mac80211_hwsim_data *data, - u32 portid, u32 seq, - struct netlink_callback *cb, int flags) -{ - void *hdr; - struct hwsim_new_radio_params param = { }; - int res = -EMSGSIZE; - - hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags, - HWSIM_CMD_GET_RADIO); - if (!hdr) - return -EMSGSIZE; - - if (cb) - genl_dump_check_consistent(cb, hdr); - - if (data->alpha2[0] && data->alpha2[1]) - param.reg_alpha2 = data->alpha2; - - param.reg_strict = !!(data->hw->wiphy->regulatory_flags & - REGULATORY_STRICT_REG); - param.p2p_device = !!(data->hw->wiphy->interface_modes & - BIT(NL80211_IFTYPE_P2P_DEVICE)); - param.use_chanctx = data->use_chanctx; - param.regd = data->regd; - param.channels = data->channels; - param.hwname = wiphy_name(data->hw->wiphy); - - res = append_radio_msg(skb, data->idx, ¶m); - if (res < 0) - goto out_err; - - genlmsg_end(skb, hdr); - return 0; - -out_err: - genlmsg_cancel(skb, hdr); - return res; -} - -static void mac80211_hwsim_free(void) -{ - struct mac80211_hwsim_data *data; - - spin_lock_bh(&hwsim_radio_lock); - while ((data = list_first_entry_or_null(&hwsim_radios, - struct mac80211_hwsim_data, - list))) { - list_del(&data->list); - spin_unlock_bh(&hwsim_radio_lock); - mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), - NULL); - spin_lock_bh(&hwsim_radio_lock); - } - spin_unlock_bh(&hwsim_radio_lock); - class_destroy(hwsim_class); -} - -static const struct net_device_ops hwsim_netdev_ops = { - .ndo_start_xmit = hwsim_mon_xmit, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static void hwsim_mon_setup(struct net_device *dev) -{ - u8 addr[ETH_ALEN]; - - dev->netdev_ops = &hwsim_netdev_ops; - dev->needs_free_netdev = true; - ether_setup(dev); - dev->priv_flags |= IFF_NO_QUEUE; - dev->type = ARPHRD_IEEE80211_RADIOTAP; - eth_zero_addr(addr); - addr[0] = 0x12; - eth_hw_addr_set(dev, addr); -} - -static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) -{ - return rhashtable_lookup_fast(&hwsim_radios_rht, - addr, - hwsim_rht_params); -} - -static void hwsim_register_wmediumd(struct net *net, u32 portid) -{ - struct mac80211_hwsim_data *data; - - hwsim_net_set_wmediumd(net, portid); - - spin_lock_bh(&hwsim_radio_lock); - list_for_each_entry(data, &hwsim_radios, list) { - if (data->netgroup == hwsim_net_get_netgroup(net)) - data->wmediumd = portid; - } - spin_unlock_bh(&hwsim_radio_lock); -} - -static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, - struct genl_info *info) -{ - - struct ieee80211_hdr *hdr; - struct mac80211_hwsim_data *data2; - struct ieee80211_tx_info *txi; - struct hwsim_tx_rate *tx_attempts; - u64 ret_skb_cookie; - struct sk_buff *skb, *tmp; - const u8 *src; - unsigned int hwsim_flags; - int i; - unsigned long flags; - bool found = false; - - if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || - !info->attrs[HWSIM_ATTR_FLAGS] || - !info->attrs[HWSIM_ATTR_COOKIE] || - !info->attrs[HWSIM_ATTR_SIGNAL] || - !info->attrs[HWSIM_ATTR_TX_INFO]) - goto out; - - src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); - hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]); - ret_skb_cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); - - data2 = get_hwsim_data_ref_from_addr(src); - if (!data2) - goto out; - - if (!hwsim_virtio_enabled) { - if (hwsim_net_get_netgroup(genl_info_net(info)) != - data2->netgroup) - goto out; - - if (info->snd_portid != data2->wmediumd) - goto out; - } - - /* look for the skb matching the cookie passed back from user */ - spin_lock_irqsave(&data2->pending.lock, flags); - skb_queue_walk_safe(&data2->pending, skb, tmp) { - uintptr_t skb_cookie; - - txi = IEEE80211_SKB_CB(skb); - skb_cookie = (uintptr_t)txi->rate_driver_data[0]; - - if (skb_cookie == ret_skb_cookie) { - __skb_unlink(skb, &data2->pending); - found = true; - break; - } - } - spin_unlock_irqrestore(&data2->pending.lock, flags); - - /* not found */ - if (!found) - goto out; - - /* Tx info received because the frame was broadcasted on user space, - so we get all the necessary info: tx attempts and skb control buff */ - - tx_attempts = (struct hwsim_tx_rate *)nla_data( - info->attrs[HWSIM_ATTR_TX_INFO]); - - /* now send back TX status */ - txi = IEEE80211_SKB_CB(skb); - - ieee80211_tx_info_clear_status(txi); - - for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { - txi->status.rates[i].idx = tx_attempts[i].idx; - txi->status.rates[i].count = tx_attempts[i].count; - } - - txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]); - - if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) && - (hwsim_flags & HWSIM_TX_STAT_ACK)) { - if (skb->len >= 16) { - hdr = (struct ieee80211_hdr *) skb->data; - mac80211_hwsim_monitor_ack(data2->channel, - hdr->addr2); - } - txi->flags |= IEEE80211_TX_STAT_ACK; - } - - if (hwsim_flags & HWSIM_TX_CTL_NO_ACK) - txi->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; - - ieee80211_tx_status_irqsafe(data2->hw, skb); - return 0; -out: - return -EINVAL; - -} - -static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, - struct genl_info *info) -{ - struct mac80211_hwsim_data *data2; - struct ieee80211_rx_status rx_status; - struct ieee80211_hdr *hdr; - const u8 *dst; - int frame_data_len; - void *frame_data; - struct sk_buff *skb = NULL; - struct ieee80211_channel *channel = NULL; - - if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || - !info->attrs[HWSIM_ATTR_FRAME] || - !info->attrs[HWSIM_ATTR_RX_RATE] || - !info->attrs[HWSIM_ATTR_SIGNAL]) - goto out; - - dst = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_RECEIVER]); - frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]); - frame_data = (void *)nla_data(info->attrs[HWSIM_ATTR_FRAME]); - - /* Allocate new skb here */ - skb = alloc_skb(frame_data_len, GFP_KERNEL); - if (skb == NULL) - goto err; - - if (frame_data_len > IEEE80211_MAX_DATA_LEN) - goto err; - - /* Copy the data */ - skb_put_data(skb, frame_data, frame_data_len); - - data2 = get_hwsim_data_ref_from_addr(dst); - if (!data2) - goto out; - - if (data2->use_chanctx) { - if (data2->tmp_chan) - channel = data2->tmp_chan; - } else { - channel = data2->channel; - } - - if (!hwsim_virtio_enabled) { - if (hwsim_net_get_netgroup(genl_info_net(info)) != - data2->netgroup) - goto out; - - if (info->snd_portid != data2->wmediumd) - goto out; - } - - /* check if radio is configured properly */ - - if ((data2->idle && !data2->tmp_chan) || !data2->started) - goto out; - - /* A frame is received from user space */ - memset(&rx_status, 0, sizeof(rx_status)); - if (info->attrs[HWSIM_ATTR_FREQ]) { - struct tx_iter_data iter_data = {}; - - /* throw away off-channel packets, but allow both the temporary - * ("hw" scan/remain-on-channel), regular channels and links, - * since the internal datapath also allows this - */ - rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]); - - iter_data.channel = ieee80211_get_channel(data2->hw->wiphy, - rx_status.freq); - if (!iter_data.channel) - goto out; - rx_status.band = iter_data.channel->band; - - mutex_lock(&data2->mutex); - if (!hwsim_chans_compat(iter_data.channel, channel)) { - ieee80211_iterate_active_interfaces_atomic( - data2->hw, IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_tx_iter, &iter_data); - if (!iter_data.receive) { - mutex_unlock(&data2->mutex); - goto out; - } - } - mutex_unlock(&data2->mutex); - } else if (!channel) { - goto out; - } else { - rx_status.freq = channel->center_freq; - rx_status.band = channel->band; - } - - rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]); - if (rx_status.rate_idx >= data2->hw->wiphy->bands[rx_status.band]->n_bitrates) - goto out; - rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]); - - hdr = (void *)skb->data; - - if (ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control)) - rx_status.boottime_ns = ktime_get_boottime_ns(); - - mac80211_hwsim_rx(data2, &rx_status, skb); - - return 0; -err: - pr_debug("mac80211_hwsim: error occurred in %s\n", __func__); -out: - dev_kfree_skb(skb); - return -EINVAL; -} - -static int hwsim_register_received_nl(struct sk_buff *skb_2, - struct genl_info *info) -{ - struct net *net = genl_info_net(info); - struct mac80211_hwsim_data *data; - int chans = 1; - - spin_lock_bh(&hwsim_radio_lock); - list_for_each_entry(data, &hwsim_radios, list) - chans = max(chans, data->channels); - spin_unlock_bh(&hwsim_radio_lock); - - /* In the future we should revise the userspace API and allow it - * to set a flag that it does support multi-channel, then we can - * let this pass conditionally on the flag. - * For current userspace, prohibit it since it won't work right. - */ - if (chans > 1) - return -EOPNOTSUPP; - - if (hwsim_net_get_wmediumd(net)) - return -EBUSY; - - hwsim_register_wmediumd(net, info->snd_portid); - - pr_debug("mac80211_hwsim: received a REGISTER, " - "switching to wmediumd mode with pid %d\n", info->snd_portid); - - return 0; -} - -/* ensures ciphers only include ciphers listed in 'hwsim_ciphers' array */ -static bool hwsim_known_ciphers(const u32 *ciphers, int n_ciphers) -{ - int i; - - for (i = 0; i < n_ciphers; i++) { - int j; - int found = 0; - - for (j = 0; j < ARRAY_SIZE(hwsim_ciphers); j++) { - if (ciphers[i] == hwsim_ciphers[j]) { - found = 1; - break; - } - } - - if (!found) - return false; - } - - return true; -} - -static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) -{ - struct hwsim_new_radio_params param = { 0 }; - const char *hwname = NULL; - int ret; - - param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG]; - param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE]; - param.channels = channels; - param.destroy_on_close = - info->attrs[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE]; - - if (info->attrs[HWSIM_ATTR_CHANNELS]) - param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]); - - if (param.channels < 1) { - GENL_SET_ERR_MSG(info, "must have at least one channel"); - return -EINVAL; - } - - if (info->attrs[HWSIM_ATTR_NO_VIF]) - param.no_vif = true; - - if (info->attrs[HWSIM_ATTR_USE_CHANCTX]) - param.use_chanctx = true; - else - param.use_chanctx = (param.channels > 1); - - if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]) - param.reg_alpha2 = - nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]); - - if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) { - u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]); - - if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) - return -EINVAL; - - idx = array_index_nospec(idx, - ARRAY_SIZE(hwsim_world_regdom_custom)); - param.regd = hwsim_world_regdom_custom[idx]; - } - - if (info->attrs[HWSIM_ATTR_PERM_ADDR]) { - if (!is_valid_ether_addr( - nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]))) { - GENL_SET_ERR_MSG(info,"MAC is no valid source addr"); - NL_SET_BAD_ATTR(info->extack, - info->attrs[HWSIM_ATTR_PERM_ADDR]); - return -EINVAL; - } - - param.perm_addr = nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]); - } - - if (info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]) { - param.iftypes = - nla_get_u32(info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]); - - if (param.iftypes & ~HWSIM_IFTYPE_SUPPORT_MASK) { - NL_SET_ERR_MSG_ATTR(info->extack, - info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT], - "cannot support more iftypes than kernel"); - return -EINVAL; - } - } else { - param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK; - } - - /* ensure both flag and iftype support is honored */ - if (param.p2p_device || - param.iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) { - param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE); - param.p2p_device = true; - } - - if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) { - u32 len = nla_len(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]); - - param.ciphers = - nla_data(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]); - - if (len % sizeof(u32)) { - NL_SET_ERR_MSG_ATTR(info->extack, - info->attrs[HWSIM_ATTR_CIPHER_SUPPORT], - "bad cipher list length"); - return -EINVAL; - } - - param.n_ciphers = len / sizeof(u32); - - if (param.n_ciphers > ARRAY_SIZE(hwsim_ciphers)) { - NL_SET_ERR_MSG_ATTR(info->extack, - info->attrs[HWSIM_ATTR_CIPHER_SUPPORT], - "too many ciphers specified"); - return -EINVAL; - } - - if (!hwsim_known_ciphers(param.ciphers, param.n_ciphers)) { - NL_SET_ERR_MSG_ATTR(info->extack, - info->attrs[HWSIM_ATTR_CIPHER_SUPPORT], - "unsupported ciphers specified"); - return -EINVAL; - } - } - - param.mlo = info->attrs[HWSIM_ATTR_MLO_SUPPORT]; - - if (param.mlo) - param.use_chanctx = true; - - if (info->attrs[HWSIM_ATTR_RADIO_NAME]) { - hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]), - nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]), - GFP_KERNEL); - if (!hwname) - return -ENOMEM; - param.hwname = hwname; - } - - ret = mac80211_hwsim_new_radio(info, ¶m); - kfree(hwname); - return ret; -} - -static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info) -{ - struct mac80211_hwsim_data *data; - s64 idx = -1; - const char *hwname = NULL; - - if (info->attrs[HWSIM_ATTR_RADIO_ID]) { - idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]); - } else if (info->attrs[HWSIM_ATTR_RADIO_NAME]) { - hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]), - nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]), - GFP_KERNEL); - if (!hwname) - return -ENOMEM; - } else - return -EINVAL; - - spin_lock_bh(&hwsim_radio_lock); - list_for_each_entry(data, &hwsim_radios, list) { - if (idx >= 0) { - if (data->idx != idx) - continue; - } else { - if (!hwname || - strcmp(hwname, wiphy_name(data->hw->wiphy))) - continue; - } - - if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info))) - continue; - - list_del(&data->list); - rhashtable_remove_fast(&hwsim_radios_rht, &data->rht, - hwsim_rht_params); - hwsim_radios_generation++; - spin_unlock_bh(&hwsim_radio_lock); - mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), - info); - kfree(hwname); - return 0; - } - spin_unlock_bh(&hwsim_radio_lock); - - kfree(hwname); - return -ENODEV; -} - -static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info) -{ - struct mac80211_hwsim_data *data; - struct sk_buff *skb; - int idx, res = -ENODEV; - - if (!info->attrs[HWSIM_ATTR_RADIO_ID]) - return -EINVAL; - idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]); - - spin_lock_bh(&hwsim_radio_lock); - list_for_each_entry(data, &hwsim_radios, list) { - if (data->idx != idx) - continue; - - if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info))) - continue; - - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!skb) { - res = -ENOMEM; - goto out_err; - } - - res = mac80211_hwsim_get_radio(skb, data, info->snd_portid, - info->snd_seq, NULL, 0); - if (res < 0) { - nlmsg_free(skb); - goto out_err; - } - - res = genlmsg_reply(skb, info); - break; - } - -out_err: - spin_unlock_bh(&hwsim_radio_lock); - - return res; -} - -static int hwsim_dump_radio_nl(struct sk_buff *skb, - struct netlink_callback *cb) -{ - int last_idx = cb->args[0] - 1; - struct mac80211_hwsim_data *data = NULL; - int res = 0; - void *hdr; - - spin_lock_bh(&hwsim_radio_lock); - cb->seq = hwsim_radios_generation; - - if (last_idx >= hwsim_radio_idx-1) - goto done; - - list_for_each_entry(data, &hwsim_radios, list) { - if (data->idx <= last_idx) - continue; - - if (!net_eq(wiphy_net(data->hw->wiphy), sock_net(skb->sk))) - continue; - - res = mac80211_hwsim_get_radio(skb, data, - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, cb, - NLM_F_MULTI); - if (res < 0) - break; - - last_idx = data->idx; - } - - cb->args[0] = last_idx + 1; - - /* list changed, but no new element sent, set interrupted flag */ - if (skb->len == 0 && cb->prev_seq && cb->seq != cb->prev_seq) { - hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, &hwsim_genl_family, - NLM_F_MULTI, HWSIM_CMD_GET_RADIO); - if (hdr) { - genl_dump_check_consistent(cb, hdr); - genlmsg_end(skb, hdr); - } else { - res = -EMSGSIZE; - } - } - -done: - spin_unlock_bh(&hwsim_radio_lock); - return res ?: skb->len; -} - -/* Generic Netlink operations array */ -static const struct genl_small_ops hwsim_ops[] = { - { - .cmd = HWSIM_CMD_REGISTER, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = hwsim_register_received_nl, - .flags = GENL_UNS_ADMIN_PERM, - }, - { - .cmd = HWSIM_CMD_FRAME, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = hwsim_cloned_frame_received_nl, - }, - { - .cmd = HWSIM_CMD_TX_INFO_FRAME, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = hwsim_tx_info_frame_received_nl, - }, - { - .cmd = HWSIM_CMD_NEW_RADIO, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = hwsim_new_radio_nl, - .flags = GENL_UNS_ADMIN_PERM, - }, - { - .cmd = HWSIM_CMD_DEL_RADIO, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = hwsim_del_radio_nl, - .flags = GENL_UNS_ADMIN_PERM, - }, - { - .cmd = HWSIM_CMD_GET_RADIO, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = hwsim_get_radio_nl, - .dumpit = hwsim_dump_radio_nl, - }, -}; - -static struct genl_family hwsim_genl_family __ro_after_init = { - .name = "MAC80211_HWSIM", - .version = 1, - .maxattr = HWSIM_ATTR_MAX, - .policy = hwsim_genl_policy, - .netnsok = true, - .module = THIS_MODULE, - .small_ops = hwsim_ops, - .n_small_ops = ARRAY_SIZE(hwsim_ops), - .resv_start_op = HWSIM_CMD_DEL_MAC_ADDR + 1, - .mcgrps = hwsim_mcgrps, - .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps), -}; - -static void remove_user_radios(u32 portid) -{ - struct mac80211_hwsim_data *entry, *tmp; - LIST_HEAD(list); - - spin_lock_bh(&hwsim_radio_lock); - list_for_each_entry_safe(entry, tmp, &hwsim_radios, list) { - if (entry->destroy_on_close && entry->portid == portid) { - list_move(&entry->list, &list); - rhashtable_remove_fast(&hwsim_radios_rht, &entry->rht, - hwsim_rht_params); - hwsim_radios_generation++; - } - } - spin_unlock_bh(&hwsim_radio_lock); - - list_for_each_entry_safe(entry, tmp, &list, list) { - list_del(&entry->list); - mac80211_hwsim_del_radio(entry, wiphy_name(entry->hw->wiphy), - NULL); - } -} - -static int mac80211_hwsim_netlink_notify(struct notifier_block *nb, - unsigned long state, - void *_notify) -{ - struct netlink_notify *notify = _notify; - - if (state != NETLINK_URELEASE) - return NOTIFY_DONE; - - remove_user_radios(notify->portid); - - if (notify->portid == hwsim_net_get_wmediumd(notify->net)) { - printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink" - " socket, switching to perfect channel medium\n"); - hwsim_register_wmediumd(notify->net, 0); - } - return NOTIFY_DONE; - -} - -static struct notifier_block hwsim_netlink_notifier = { - .notifier_call = mac80211_hwsim_netlink_notify, -}; - -static int __init hwsim_init_netlink(void) -{ - int rc; - - printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); - - rc = genl_register_family(&hwsim_genl_family); - if (rc) - goto failure; - - rc = netlink_register_notifier(&hwsim_netlink_notifier); - if (rc) { - genl_unregister_family(&hwsim_genl_family); - goto failure; - } - - return 0; - -failure: - pr_debug("mac80211_hwsim: error occurred in %s\n", __func__); - return -EINVAL; -} - -static __net_init int hwsim_init_net(struct net *net) -{ - return hwsim_net_set_netgroup(net); -} - -static void __net_exit hwsim_exit_net(struct net *net) -{ - struct mac80211_hwsim_data *data, *tmp; - LIST_HEAD(list); - - spin_lock_bh(&hwsim_radio_lock); - list_for_each_entry_safe(data, tmp, &hwsim_radios, list) { - if (!net_eq(wiphy_net(data->hw->wiphy), net)) - continue; - - /* Radios created in init_net are returned to init_net. */ - if (data->netgroup == hwsim_net_get_netgroup(&init_net)) - continue; - - list_move(&data->list, &list); - rhashtable_remove_fast(&hwsim_radios_rht, &data->rht, - hwsim_rht_params); - hwsim_radios_generation++; - } - spin_unlock_bh(&hwsim_radio_lock); - - list_for_each_entry_safe(data, tmp, &list, list) { - list_del(&data->list); - mac80211_hwsim_del_radio(data, - wiphy_name(data->hw->wiphy), - NULL); - } - - ida_free(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net)); -} - -static struct pernet_operations hwsim_net_ops = { - .init = hwsim_init_net, - .exit = hwsim_exit_net, - .id = &hwsim_net_id, - .size = sizeof(struct hwsim_net), -}; - -static void hwsim_exit_netlink(void) -{ - /* unregister the notifier */ - netlink_unregister_notifier(&hwsim_netlink_notifier); - /* unregister the family */ - genl_unregister_family(&hwsim_genl_family); -} - -#if IS_REACHABLE(CONFIG_VIRTIO) -static void hwsim_virtio_tx_done(struct virtqueue *vq) -{ - unsigned int len; - struct sk_buff *skb; - unsigned long flags; - - spin_lock_irqsave(&hwsim_virtio_lock, flags); - while ((skb = virtqueue_get_buf(vq, &len))) - nlmsg_free(skb); - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); -} - -static int hwsim_virtio_handle_cmd(struct sk_buff *skb) -{ - struct nlmsghdr *nlh; - struct genlmsghdr *gnlh; - struct nlattr *tb[HWSIM_ATTR_MAX + 1]; - struct genl_info info = {}; - int err; - - nlh = nlmsg_hdr(skb); - gnlh = nlmsg_data(nlh); - - if (skb->len < nlh->nlmsg_len) - return -EINVAL; - - err = genlmsg_parse(nlh, &hwsim_genl_family, tb, HWSIM_ATTR_MAX, - hwsim_genl_policy, NULL); - if (err) { - pr_err_ratelimited("hwsim: genlmsg_parse returned %d\n", err); - return err; - } - - info.attrs = tb; - - switch (gnlh->cmd) { - case HWSIM_CMD_FRAME: - hwsim_cloned_frame_received_nl(skb, &info); - break; - case HWSIM_CMD_TX_INFO_FRAME: - hwsim_tx_info_frame_received_nl(skb, &info); - break; - default: - pr_err_ratelimited("hwsim: invalid cmd: %d\n", gnlh->cmd); - return -EPROTO; - } - return 0; -} - -static void hwsim_virtio_rx_work(struct work_struct *work) -{ - struct virtqueue *vq; - unsigned int len; - struct sk_buff *skb; - struct scatterlist sg[1]; - int err; - unsigned long flags; - - spin_lock_irqsave(&hwsim_virtio_lock, flags); - if (!hwsim_virtio_enabled) - goto out_unlock; - - skb = virtqueue_get_buf(hwsim_vqs[HWSIM_VQ_RX], &len); - if (!skb) - goto out_unlock; - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); - - skb->data = skb->head; - skb_reset_tail_pointer(skb); - skb_put(skb, len); - hwsim_virtio_handle_cmd(skb); - - spin_lock_irqsave(&hwsim_virtio_lock, flags); - if (!hwsim_virtio_enabled) { - nlmsg_free(skb); - goto out_unlock; - } - vq = hwsim_vqs[HWSIM_VQ_RX]; - sg_init_one(sg, skb->head, skb_end_offset(skb)); - err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_ATOMIC); - if (WARN(err, "virtqueue_add_inbuf returned %d\n", err)) - nlmsg_free(skb); - else - virtqueue_kick(vq); - schedule_work(&hwsim_virtio_rx); - -out_unlock: - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); -} - -static void hwsim_virtio_rx_done(struct virtqueue *vq) -{ - schedule_work(&hwsim_virtio_rx); -} - -static int init_vqs(struct virtio_device *vdev) -{ - vq_callback_t *callbacks[HWSIM_NUM_VQS] = { - [HWSIM_VQ_TX] = hwsim_virtio_tx_done, - [HWSIM_VQ_RX] = hwsim_virtio_rx_done, - }; - const char *names[HWSIM_NUM_VQS] = { - [HWSIM_VQ_TX] = "tx", - [HWSIM_VQ_RX] = "rx", - }; - - return virtio_find_vqs(vdev, HWSIM_NUM_VQS, - hwsim_vqs, callbacks, names, NULL); -} - -static int fill_vq(struct virtqueue *vq) -{ - int i, err; - struct sk_buff *skb; - struct scatterlist sg[1]; - - for (i = 0; i < virtqueue_get_vring_size(vq); i++) { - skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - sg_init_one(sg, skb->head, skb_end_offset(skb)); - err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL); - if (err) { - nlmsg_free(skb); - return err; - } - } - virtqueue_kick(vq); - return 0; -} - -static void remove_vqs(struct virtio_device *vdev) -{ - int i; - - virtio_reset_device(vdev); - - for (i = 0; i < ARRAY_SIZE(hwsim_vqs); i++) { - struct virtqueue *vq = hwsim_vqs[i]; - struct sk_buff *skb; - - while ((skb = virtqueue_detach_unused_buf(vq))) - nlmsg_free(skb); - } - - vdev->config->del_vqs(vdev); -} - -static int hwsim_virtio_probe(struct virtio_device *vdev) -{ - int err; - unsigned long flags; - - spin_lock_irqsave(&hwsim_virtio_lock, flags); - if (hwsim_virtio_enabled) { - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); - return -EEXIST; - } - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); - - err = init_vqs(vdev); - if (err) - return err; - - virtio_device_ready(vdev); - - err = fill_vq(hwsim_vqs[HWSIM_VQ_RX]); - if (err) - goto out_remove; - - spin_lock_irqsave(&hwsim_virtio_lock, flags); - hwsim_virtio_enabled = true; - spin_unlock_irqrestore(&hwsim_virtio_lock, flags); - - schedule_work(&hwsim_virtio_rx); - return 0; - -out_remove: - remove_vqs(vdev); - return err; -} - -static void hwsim_virtio_remove(struct virtio_device *vdev) -{ - hwsim_virtio_enabled = false; - - cancel_work_sync(&hwsim_virtio_rx); - - remove_vqs(vdev); -} - -/* MAC80211_HWSIM virtio device id table */ -static const struct virtio_device_id id_table[] = { - { VIRTIO_ID_MAC80211_HWSIM, VIRTIO_DEV_ANY_ID }, - { 0 } -}; -MODULE_DEVICE_TABLE(virtio, id_table); - -static struct virtio_driver virtio_hwsim = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = hwsim_virtio_probe, - .remove = hwsim_virtio_remove, -}; - -static int hwsim_register_virtio_driver(void) -{ - return register_virtio_driver(&virtio_hwsim); -} - -static void hwsim_unregister_virtio_driver(void) -{ - unregister_virtio_driver(&virtio_hwsim); -} -#else -static inline int hwsim_register_virtio_driver(void) -{ - return 0; -} - -static inline void hwsim_unregister_virtio_driver(void) -{ -} -#endif - -static int __init init_mac80211_hwsim(void) -{ - int i, err; - - if (radios < 0 || radios > 100) - return -EINVAL; - - if (channels < 1) - return -EINVAL; - - err = rhashtable_init(&hwsim_radios_rht, &hwsim_rht_params); - if (err) - return err; - - err = register_pernet_device(&hwsim_net_ops); - if (err) - goto out_free_rht; - - err = platform_driver_register(&mac80211_hwsim_driver); - if (err) - goto out_unregister_pernet; - - err = hwsim_init_netlink(); - if (err) - goto out_unregister_driver; - - err = hwsim_register_virtio_driver(); - if (err) - goto out_exit_netlink; - - hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); - if (IS_ERR(hwsim_class)) { - err = PTR_ERR(hwsim_class); - goto out_exit_virtio; - } - - hwsim_init_s1g_channels(hwsim_channels_s1g); - - for (i = 0; i < radios; i++) { - struct hwsim_new_radio_params param = { 0 }; - - param.channels = channels; - - switch (regtest) { - case HWSIM_REGTEST_DIFF_COUNTRY: - if (i < ARRAY_SIZE(hwsim_alpha2s)) - param.reg_alpha2 = hwsim_alpha2s[i]; - break; - case HWSIM_REGTEST_DRIVER_REG_FOLLOW: - if (!i) - param.reg_alpha2 = hwsim_alpha2s[0]; - break; - case HWSIM_REGTEST_STRICT_ALL: - param.reg_strict = true; - fallthrough; - case HWSIM_REGTEST_DRIVER_REG_ALL: - param.reg_alpha2 = hwsim_alpha2s[0]; - break; - case HWSIM_REGTEST_WORLD_ROAM: - if (i == 0) - param.regd = &hwsim_world_regdom_custom_01; - break; - case HWSIM_REGTEST_CUSTOM_WORLD: - param.regd = &hwsim_world_regdom_custom_01; - break; - case HWSIM_REGTEST_CUSTOM_WORLD_2: - if (i == 0) - param.regd = &hwsim_world_regdom_custom_01; - else if (i == 1) - param.regd = &hwsim_world_regdom_custom_02; - break; - case HWSIM_REGTEST_STRICT_FOLLOW: - if (i == 0) { - param.reg_strict = true; - param.reg_alpha2 = hwsim_alpha2s[0]; - } - break; - case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: - if (i == 0) { - param.reg_strict = true; - param.reg_alpha2 = hwsim_alpha2s[0]; - } else if (i == 1) { - param.reg_alpha2 = hwsim_alpha2s[1]; - } - break; - case HWSIM_REGTEST_ALL: - switch (i) { - case 0: - param.regd = &hwsim_world_regdom_custom_01; - break; - case 1: - param.regd = &hwsim_world_regdom_custom_02; - break; - case 2: - param.reg_alpha2 = hwsim_alpha2s[0]; - break; - case 3: - param.reg_alpha2 = hwsim_alpha2s[1]; - break; - case 4: - param.reg_strict = true; - param.reg_alpha2 = hwsim_alpha2s[2]; - break; - } - break; - default: - break; - } - - param.p2p_device = support_p2p_device; - param.mlo = mlo; - param.use_chanctx = channels > 1 || mlo; - param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK; - if (param.p2p_device) - param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE); - - err = mac80211_hwsim_new_radio(NULL, ¶m); - if (err < 0) - goto out_free_radios; - } - - hwsim_mon = alloc_netdev(0, "hwsim%d", NET_NAME_UNKNOWN, - hwsim_mon_setup); - if (hwsim_mon == NULL) { - err = -ENOMEM; - goto out_free_radios; - } - - rtnl_lock(); - err = dev_alloc_name(hwsim_mon, hwsim_mon->name); - if (err < 0) { - rtnl_unlock(); - goto out_free_mon; - } - - err = register_netdevice(hwsim_mon); - if (err < 0) { - rtnl_unlock(); - goto out_free_mon; - } - rtnl_unlock(); - - return 0; - -out_free_mon: - free_netdev(hwsim_mon); -out_free_radios: - mac80211_hwsim_free(); -out_exit_virtio: - hwsim_unregister_virtio_driver(); -out_exit_netlink: - hwsim_exit_netlink(); -out_unregister_driver: - platform_driver_unregister(&mac80211_hwsim_driver); -out_unregister_pernet: - unregister_pernet_device(&hwsim_net_ops); -out_free_rht: - rhashtable_destroy(&hwsim_radios_rht); - return err; -} -module_init(init_mac80211_hwsim); - -static void __exit exit_mac80211_hwsim(void) -{ - pr_debug("mac80211_hwsim: unregister radios\n"); - - hwsim_unregister_virtio_driver(); - hwsim_exit_netlink(); - - mac80211_hwsim_free(); - - rhashtable_destroy(&hwsim_radios_rht); - unregister_netdev(hwsim_mon); - platform_driver_unregister(&mac80211_hwsim_driver); - unregister_pernet_device(&hwsim_net_ops); -} -module_exit(exit_mac80211_hwsim); diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h deleted file mode 100644 index 527799b2de0f..000000000000 --- a/drivers/net/wireless/mac80211_hwsim.h +++ /dev/null @@ -1,280 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 - * Copyright (c) 2008, Jouni Malinen - * Copyright (c) 2011, Javier Lopez - * Copyright (C) 2020, 2022 Intel Corporation - */ - -#ifndef __MAC80211_HWSIM_H -#define __MAC80211_HWSIM_H - -/** - * enum hwsim_tx_control_flags - flags to describe transmission info/status - * - * These flags are used to give the wmediumd extra information in order to - * modify its behavior for each frame - * - * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame. - * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack - * @HWSIM_TX_STAT_ACK: Frame was acknowledged - * - */ -enum hwsim_tx_control_flags { - HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0), - HWSIM_TX_CTL_NO_ACK = BIT(1), - HWSIM_TX_STAT_ACK = BIT(2), -}; - -/** - * DOC: Frame transmission/registration support - * - * Frame transmission and registration support exists to allow userspace - * entities such as wmediumd to receive and process all broadcasted - * frames from a mac80211_hwsim radio device. - * - * This allow user space applications to decide if the frame should be - * dropped or not and implement a wireless medium simulator at user space. - * - * Registration is done by sending a register message to the driver and - * will be automatically unregistered if the user application doesn't - * responds to sent frames. - * Once registered the user application has to take responsibility of - * broadcasting the frames to all listening mac80211_hwsim radio - * interfaces. - * - * For more technical details, see the corresponding command descriptions - * below. - */ - -/** - * enum hwsim_commands - supported hwsim commands - * - * @HWSIM_CMD_UNSPEC: unspecified command to catch errors - * - * @HWSIM_CMD_REGISTER: request to register and received all broadcasted - * frames by any mac80211_hwsim radio device. - * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user - * space, uses: - * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER, - * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE, - * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional) - * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to - * kernel, uses: - * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS, - * %HWSIM_ATTR_TX_INFO, %WSIM_ATTR_TX_INFO_FLAGS, - * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE - * @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters, - * returns the radio ID (>= 0) or negative on errors, if successful - * then multicast the result, uses optional parameter: - * %HWSIM_ATTR_REG_STRICT_REG, %HWSIM_ATTR_SUPPORT_P2P_DEVICE, - * %HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, %HWSIM_ATTR_CHANNELS, - * %HWSIM_ATTR_NO_VIF, %HWSIM_ATTR_RADIO_NAME, %HWSIM_ATTR_USE_CHANCTX, - * %HWSIM_ATTR_REG_HINT_ALPHA2, %HWSIM_ATTR_REG_CUSTOM_REG, - * %HWSIM_ATTR_PERM_ADDR - * @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted - * @HWSIM_CMD_GET_RADIO: fetch information about existing radios, uses: - * %HWSIM_ATTR_RADIO_ID - * @HWSIM_CMD_ADD_MAC_ADDR: add a receive MAC address (given in the - * %HWSIM_ATTR_ADDR_RECEIVER attribute) to a device identified by - * %HWSIM_ATTR_ADDR_TRANSMITTER. This lets wmediumd forward frames - * to this receiver address for a given station. - * @HWSIM_CMD_DEL_MAC_ADDR: remove the MAC address again, the attributes - * are the same as to @HWSIM_CMD_ADD_MAC_ADDR. - * @__HWSIM_CMD_MAX: enum limit - */ -enum { - HWSIM_CMD_UNSPEC, - HWSIM_CMD_REGISTER, - HWSIM_CMD_FRAME, - HWSIM_CMD_TX_INFO_FRAME, - HWSIM_CMD_NEW_RADIO, - HWSIM_CMD_DEL_RADIO, - HWSIM_CMD_GET_RADIO, - HWSIM_CMD_ADD_MAC_ADDR, - HWSIM_CMD_DEL_MAC_ADDR, - __HWSIM_CMD_MAX, -}; -#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1) - -#define HWSIM_CMD_CREATE_RADIO HWSIM_CMD_NEW_RADIO -#define HWSIM_CMD_DESTROY_RADIO HWSIM_CMD_DEL_RADIO - -/** - * enum hwsim_attrs - hwsim netlink attributes - * - * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors - * - * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that - * the frame is broadcasted to - * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that - * the frame was broadcasted from - * @HWSIM_ATTR_FRAME: Data array - * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process - properly the frame at user space - * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user - space - * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user - space - * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array - * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame - * @HWSIM_ATTR_CHANNELS: u32 attribute used with the %HWSIM_CMD_CREATE_RADIO - * command giving the number of channels supported by the new radio - * @HWSIM_ATTR_RADIO_ID: u32 attribute used with %HWSIM_CMD_DESTROY_RADIO - * only to destroy a radio - * @HWSIM_ATTR_REG_HINT_ALPHA2: alpha2 for regulatoro driver hint - * (nla string, length 2) - * @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute) - * @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute) - * @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag) - * @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO - * command to force use of channel contexts even when only a - * single channel is supported - * @HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE: used with the %HWSIM_CMD_CREATE_RADIO - * command to force radio removal when process that created the radio dies - * @HWSIM_ATTR_RADIO_NAME: Name of radio, e.g. phy666 - * @HWSIM_ATTR_NO_VIF: Do not create vif (wlanX) when creating radio. - * @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received. - * @HWSIM_ATTR_TX_INFO_FLAGS: additional flags for corresponding - * rates of %HWSIM_ATTR_TX_INFO - * @HWSIM_ATTR_PERM_ADDR: permanent mac address of new radio - * @HWSIM_ATTR_IFTYPE_SUPPORT: u32 attribute of supported interface types bits - * @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types - * @HWSIM_ATTR_MLO_SUPPORT: claim MLO support (exact parameters TBD) for - * the new radio - * @__HWSIM_ATTR_MAX: enum limit - */ - - -enum { - HWSIM_ATTR_UNSPEC, - HWSIM_ATTR_ADDR_RECEIVER, - HWSIM_ATTR_ADDR_TRANSMITTER, - HWSIM_ATTR_FRAME, - HWSIM_ATTR_FLAGS, - HWSIM_ATTR_RX_RATE, - HWSIM_ATTR_SIGNAL, - HWSIM_ATTR_TX_INFO, - HWSIM_ATTR_COOKIE, - HWSIM_ATTR_CHANNELS, - HWSIM_ATTR_RADIO_ID, - HWSIM_ATTR_REG_HINT_ALPHA2, - HWSIM_ATTR_REG_CUSTOM_REG, - HWSIM_ATTR_REG_STRICT_REG, - HWSIM_ATTR_SUPPORT_P2P_DEVICE, - HWSIM_ATTR_USE_CHANCTX, - HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, - HWSIM_ATTR_RADIO_NAME, - HWSIM_ATTR_NO_VIF, - HWSIM_ATTR_FREQ, - HWSIM_ATTR_PAD, - HWSIM_ATTR_TX_INFO_FLAGS, - HWSIM_ATTR_PERM_ADDR, - HWSIM_ATTR_IFTYPE_SUPPORT, - HWSIM_ATTR_CIPHER_SUPPORT, - HWSIM_ATTR_MLO_SUPPORT, - __HWSIM_ATTR_MAX, -}; -#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) - -/** - * struct hwsim_tx_rate - rate selection/status - * - * @idx: rate index to attempt to send with - * @count: number of tries in this rate before going to the next rate - * - * A value of -1 for @idx indicates an invalid rate and, if used - * in an array of retry rates, that no more rates should be tried. - * - * When used for transmit status reporting, the driver should - * always report the rate and number of retries used. - * - */ -struct hwsim_tx_rate { - s8 idx; - u8 count; -} __packed; - -/** - * enum hwsim_tx_rate_flags - per-rate flags set by the rate control algorithm. - * Inspired by structure mac80211_rate_control_flags. New flags may be - * appended, but old flags not deleted, to keep compatibility for - * userspace. - * - * These flags are set by the Rate control algorithm for each rate during tx, - * in the @flags member of struct ieee80211_tx_rate. - * - * @MAC80211_HWSIM_TX_RC_USE_RTS_CTS: Use RTS/CTS exchange for this rate. - * @MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT: CTS-to-self protection is required. - * This is set if the current BSS requires ERP protection. - * @MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE: Use short preamble. - * @MAC80211_HWSIM_TX_RC_MCS: HT rate. - * @MAC80211_HWSIM_TX_RC_VHT_MCS: VHT MCS rate, in this case the idx field is - * split into a higher 4 bits (Nss) and lower 4 bits (MCS number) - * @MAC80211_HWSIM_TX_RC_GREEN_FIELD: Indicates whether this rate should be used - * in Greenfield mode. - * @MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be - * 40 MHz. - * @MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH: Indicates 80 MHz transmission - * @MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH: Indicates 160 MHz transmission - * (80+80 isn't supported yet) - * @MAC80211_HWSIM_TX_RC_DUP_DATA: The frame should be transmitted on both of - * the adjacent 20 MHz channels, if the current channel type is - * NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS. - * @MAC80211_HWSIM_TX_RC_SHORT_GI: Short Guard interval should be used for this - * rate. - */ -enum hwsim_tx_rate_flags { - MAC80211_HWSIM_TX_RC_USE_RTS_CTS = BIT(0), - MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT = BIT(1), - MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE = BIT(2), - - /* rate index is an HT/VHT MCS instead of an index */ - MAC80211_HWSIM_TX_RC_MCS = BIT(3), - MAC80211_HWSIM_TX_RC_GREEN_FIELD = BIT(4), - MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH = BIT(5), - MAC80211_HWSIM_TX_RC_DUP_DATA = BIT(6), - MAC80211_HWSIM_TX_RC_SHORT_GI = BIT(7), - MAC80211_HWSIM_TX_RC_VHT_MCS = BIT(8), - MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH = BIT(9), - MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH = BIT(10), -}; - -/** - * struct hwsim_tx_rate - rate selection/status - * - * @idx: rate index to attempt to send with - * @count: number of tries in this rate before going to the next rate - * - * A value of -1 for @idx indicates an invalid rate and, if used - * in an array of retry rates, that no more rates should be tried. - * - * When used for transmit status reporting, the driver should - * always report the rate and number of retries used. - * - */ -struct hwsim_tx_rate_flag { - s8 idx; - u16 flags; -} __packed; - -/** - * DOC: Frame transmission support over virtio - * - * Frame transmission is also supported over virtio to allow communication - * with external entities. - */ - -/** - * enum hwsim_vqs - queues for virtio frame transmission - * - * @HWSIM_VQ_TX: send frames to external entity - * @HWSIM_VQ_RX: receive frames and transmission info reports - * @HWSIM_NUM_VQS: enum limit - */ -enum { - HWSIM_VQ_TX, - HWSIM_VQ_RX, - HWSIM_NUM_VQS, -}; -#endif /* __MAC80211_HWSIM_H */ diff --git a/drivers/net/wireless/virt_wifi.c b/drivers/net/wireless/virt_wifi.c deleted file mode 100644 index ba14d83353a4..000000000000 --- a/drivers/net/wireless/virt_wifi.c +++ /dev/null @@ -1,697 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* drivers/net/wireless/virt_wifi.c - * - * A fake implementation of cfg80211_ops that can be tacked on to an ethernet - * net_device to make it appear as a wireless connection. - * - * Copyright (C) 2018 Google, Inc. - * - * Author: schuffelen@google.com - */ - -#include -#include -#include -#include -#include - -static struct wiphy *common_wiphy; - -struct virt_wifi_wiphy_priv { - struct delayed_work scan_result; - struct cfg80211_scan_request *scan_request; - bool being_deleted; -}; - -static struct ieee80211_channel channel_2ghz = { - .band = NL80211_BAND_2GHZ, - .center_freq = 2432, - .hw_value = 2432, - .max_power = 20, -}; - -static struct ieee80211_rate bitrates_2ghz[] = { - { .bitrate = 10 }, - { .bitrate = 20 }, - { .bitrate = 55 }, - { .bitrate = 110 }, - { .bitrate = 60 }, - { .bitrate = 120 }, - { .bitrate = 240 }, -}; - -static struct ieee80211_supported_band band_2ghz = { - .channels = &channel_2ghz, - .bitrates = bitrates_2ghz, - .band = NL80211_BAND_2GHZ, - .n_channels = 1, - .n_bitrates = ARRAY_SIZE(bitrates_2ghz), - .ht_cap = { - .ht_supported = true, - .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40, - .ampdu_factor = 0x3, - .ampdu_density = 0x6, - .mcs = { - .rx_mask = {0xff, 0xff}, - .tx_params = IEEE80211_HT_MCS_TX_DEFINED, - }, - }, -}; - -static struct ieee80211_channel channel_5ghz = { - .band = NL80211_BAND_5GHZ, - .center_freq = 5240, - .hw_value = 5240, - .max_power = 20, -}; - -static struct ieee80211_rate bitrates_5ghz[] = { - { .bitrate = 60 }, - { .bitrate = 120 }, - { .bitrate = 240 }, -}; - -#define RX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) - -#define TX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \ - IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) - -static struct ieee80211_supported_band band_5ghz = { - .channels = &channel_5ghz, - .bitrates = bitrates_5ghz, - .band = NL80211_BAND_5GHZ, - .n_channels = 1, - .n_bitrates = ARRAY_SIZE(bitrates_5ghz), - .ht_cap = { - .ht_supported = true, - .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40, - .ampdu_factor = 0x3, - .ampdu_density = 0x6, - .mcs = { - .rx_mask = {0xff, 0xff}, - .tx_params = IEEE80211_HT_MCS_TX_DEFINED, - }, - }, - .vht_cap = { - .vht_supported = true, - .cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | - IEEE80211_VHT_CAP_RXLDPC | - IEEE80211_VHT_CAP_SHORT_GI_80 | - IEEE80211_VHT_CAP_SHORT_GI_160 | - IEEE80211_VHT_CAP_TXSTBC | - IEEE80211_VHT_CAP_RXSTBC_1 | - IEEE80211_VHT_CAP_RXSTBC_2 | - IEEE80211_VHT_CAP_RXSTBC_3 | - IEEE80211_VHT_CAP_RXSTBC_4 | - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, - .vht_mcs = { - .rx_mcs_map = cpu_to_le16(RX_MCS_MAP), - .tx_mcs_map = cpu_to_le16(TX_MCS_MAP), - } - }, -}; - -/* Assigned at module init. Guaranteed locally-administered and unicast. */ -static u8 fake_router_bssid[ETH_ALEN] __ro_after_init = {}; - -static void virt_wifi_inform_bss(struct wiphy *wiphy) -{ - u64 tsf = div_u64(ktime_get_boottime_ns(), 1000); - struct cfg80211_bss *informed_bss; - static const struct { - u8 tag; - u8 len; - u8 ssid[8]; - } __packed ssid = { - .tag = WLAN_EID_SSID, - .len = 8, - .ssid = "VirtWifi", - }; - - informed_bss = cfg80211_inform_bss(wiphy, &channel_5ghz, - CFG80211_BSS_FTYPE_PRESP, - fake_router_bssid, tsf, - WLAN_CAPABILITY_ESS, 0, - (void *)&ssid, sizeof(ssid), - DBM_TO_MBM(-50), GFP_KERNEL); - cfg80211_put_bss(wiphy, informed_bss); -} - -/* Called with the rtnl lock held. */ -static int virt_wifi_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request) -{ - struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy); - - wiphy_debug(wiphy, "scan\n"); - - if (priv->scan_request || priv->being_deleted) - return -EBUSY; - - priv->scan_request = request; - schedule_delayed_work(&priv->scan_result, HZ * 2); - - return 0; -} - -/* Acquires and releases the rdev BSS lock. */ -static void virt_wifi_scan_result(struct work_struct *work) -{ - struct virt_wifi_wiphy_priv *priv = - container_of(work, struct virt_wifi_wiphy_priv, - scan_result.work); - struct wiphy *wiphy = priv_to_wiphy(priv); - struct cfg80211_scan_info scan_info = { .aborted = false }; - - virt_wifi_inform_bss(wiphy); - - /* Schedules work which acquires and releases the rtnl lock. */ - cfg80211_scan_done(priv->scan_request, &scan_info); - priv->scan_request = NULL; -} - -/* May acquire and release the rdev BSS lock. */ -static void virt_wifi_cancel_scan(struct wiphy *wiphy) -{ - struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy); - - cancel_delayed_work_sync(&priv->scan_result); - /* Clean up dangling callbacks if necessary. */ - if (priv->scan_request) { - struct cfg80211_scan_info scan_info = { .aborted = true }; - /* Schedules work which acquires and releases the rtnl lock. */ - cfg80211_scan_done(priv->scan_request, &scan_info); - priv->scan_request = NULL; - } -} - -struct virt_wifi_netdev_priv { - struct delayed_work connect; - struct net_device *lowerdev; - struct net_device *upperdev; - u32 tx_packets; - u32 tx_failed; - u8 connect_requested_bss[ETH_ALEN]; - bool is_up; - bool is_connected; - bool being_deleted; -}; - -/* Called with the rtnl lock held. */ -static int virt_wifi_connect(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_connect_params *sme) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(netdev); - bool could_schedule; - - if (priv->being_deleted || !priv->is_up) - return -EBUSY; - - could_schedule = schedule_delayed_work(&priv->connect, HZ * 2); - if (!could_schedule) - return -EBUSY; - - if (sme->bssid) { - ether_addr_copy(priv->connect_requested_bss, sme->bssid); - } else { - virt_wifi_inform_bss(wiphy); - eth_zero_addr(priv->connect_requested_bss); - } - - wiphy_debug(wiphy, "connect\n"); - - return 0; -} - -/* Acquires and releases the rdev event lock. */ -static void virt_wifi_connect_complete(struct work_struct *work) -{ - struct virt_wifi_netdev_priv *priv = - container_of(work, struct virt_wifi_netdev_priv, connect.work); - u8 *requested_bss = priv->connect_requested_bss; - bool right_addr = ether_addr_equal(requested_bss, fake_router_bssid); - u16 status = WLAN_STATUS_SUCCESS; - - if (is_zero_ether_addr(requested_bss)) - requested_bss = NULL; - - if (!priv->is_up || (requested_bss && !right_addr)) - status = WLAN_STATUS_UNSPECIFIED_FAILURE; - else - priv->is_connected = true; - - /* Schedules an event that acquires the rtnl lock. */ - cfg80211_connect_result(priv->upperdev, requested_bss, NULL, 0, NULL, 0, - status, GFP_KERNEL); - netif_carrier_on(priv->upperdev); -} - -/* May acquire and release the rdev event lock. */ -static void virt_wifi_cancel_connect(struct net_device *netdev) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(netdev); - - /* If there is work pending, clean up dangling callbacks. */ - if (cancel_delayed_work_sync(&priv->connect)) { - /* Schedules an event that acquires the rtnl lock. */ - cfg80211_connect_result(priv->upperdev, - priv->connect_requested_bss, NULL, 0, - NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); - } -} - -/* Called with the rtnl lock held. Acquires the rdev event lock. */ -static int virt_wifi_disconnect(struct wiphy *wiphy, struct net_device *netdev, - u16 reason_code) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(netdev); - - if (priv->being_deleted) - return -EBUSY; - - wiphy_debug(wiphy, "disconnect\n"); - virt_wifi_cancel_connect(netdev); - - cfg80211_disconnected(netdev, reason_code, NULL, 0, true, GFP_KERNEL); - priv->is_connected = false; - netif_carrier_off(netdev); - - return 0; -} - -/* Called with the rtnl lock held. */ -static int virt_wifi_get_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_info *sinfo) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(dev); - - wiphy_debug(wiphy, "get_station\n"); - - if (!priv->is_connected || !ether_addr_equal(mac, fake_router_bssid)) - return -ENOENT; - - sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) | - BIT_ULL(NL80211_STA_INFO_TX_FAILED) | - BIT_ULL(NL80211_STA_INFO_SIGNAL) | - BIT_ULL(NL80211_STA_INFO_TX_BITRATE); - sinfo->tx_packets = priv->tx_packets; - sinfo->tx_failed = priv->tx_failed; - /* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_ */ - sinfo->signal = -50; - sinfo->txrate = (struct rate_info) { - .legacy = 10, /* units are 100kbit/s */ - }; - return 0; -} - -/* Called with the rtnl lock held. */ -static int virt_wifi_dump_station(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(dev); - - wiphy_debug(wiphy, "dump_station\n"); - - if (idx != 0 || !priv->is_connected) - return -ENOENT; - - ether_addr_copy(mac, fake_router_bssid); - return virt_wifi_get_station(wiphy, dev, fake_router_bssid, sinfo); -} - -static const struct cfg80211_ops virt_wifi_cfg80211_ops = { - .scan = virt_wifi_scan, - - .connect = virt_wifi_connect, - .disconnect = virt_wifi_disconnect, - - .get_station = virt_wifi_get_station, - .dump_station = virt_wifi_dump_station, -}; - -/* Acquires and releases the rtnl lock. */ -static struct wiphy *virt_wifi_make_wiphy(void) -{ - struct wiphy *wiphy; - struct virt_wifi_wiphy_priv *priv; - int err; - - wiphy = wiphy_new(&virt_wifi_cfg80211_ops, sizeof(*priv)); - - if (!wiphy) - return NULL; - - wiphy->max_scan_ssids = 4; - wiphy->max_scan_ie_len = 1000; - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - - wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz; - wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz; - wiphy->bands[NL80211_BAND_60GHZ] = NULL; - - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - - priv = wiphy_priv(wiphy); - priv->being_deleted = false; - priv->scan_request = NULL; - INIT_DELAYED_WORK(&priv->scan_result, virt_wifi_scan_result); - - err = wiphy_register(wiphy); - if (err < 0) { - wiphy_free(wiphy); - return NULL; - } - - return wiphy; -} - -/* Acquires and releases the rtnl lock. */ -static void virt_wifi_destroy_wiphy(struct wiphy *wiphy) -{ - struct virt_wifi_wiphy_priv *priv; - - WARN(!wiphy, "%s called with null wiphy", __func__); - if (!wiphy) - return; - - priv = wiphy_priv(wiphy); - priv->being_deleted = true; - virt_wifi_cancel_scan(wiphy); - - if (wiphy->registered) - wiphy_unregister(wiphy); - wiphy_free(wiphy); -} - -/* Enters and exits a RCU-bh critical section. */ -static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(dev); - - priv->tx_packets++; - if (!priv->is_connected) { - priv->tx_failed++; - return NET_XMIT_DROP; - } - - skb->dev = priv->lowerdev; - return dev_queue_xmit(skb); -} - -/* Called with rtnl lock held. */ -static int virt_wifi_net_device_open(struct net_device *dev) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(dev); - - priv->is_up = true; - return 0; -} - -/* Called with rtnl lock held. */ -static int virt_wifi_net_device_stop(struct net_device *dev) -{ - struct virt_wifi_netdev_priv *n_priv = netdev_priv(dev); - - n_priv->is_up = false; - - if (!dev->ieee80211_ptr) - return 0; - - virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy); - virt_wifi_cancel_connect(dev); - netif_carrier_off(dev); - - return 0; -} - -static int virt_wifi_net_device_get_iflink(const struct net_device *dev) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(dev); - - return priv->lowerdev->ifindex; -} - -static const struct net_device_ops virt_wifi_ops = { - .ndo_start_xmit = virt_wifi_start_xmit, - .ndo_open = virt_wifi_net_device_open, - .ndo_stop = virt_wifi_net_device_stop, - .ndo_get_iflink = virt_wifi_net_device_get_iflink, -}; - -/* Invoked as part of rtnl lock release. */ -static void virt_wifi_net_device_destructor(struct net_device *dev) -{ - /* Delayed past dellink to allow nl80211 to react to the device being - * deleted. - */ - kfree(dev->ieee80211_ptr); - dev->ieee80211_ptr = NULL; -} - -/* No lock interaction. */ -static void virt_wifi_setup(struct net_device *dev) -{ - ether_setup(dev); - dev->netdev_ops = &virt_wifi_ops; - dev->needs_free_netdev = true; -} - -/* Called in a RCU read critical section from netif_receive_skb */ -static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct virt_wifi_netdev_priv *priv = - rcu_dereference(skb->dev->rx_handler_data); - - if (!priv->is_connected) - return RX_HANDLER_PASS; - - /* GFP_ATOMIC because this is a packet interrupt handler. */ - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) { - dev_err(&priv->upperdev->dev, "can't skb_share_check\n"); - return RX_HANDLER_CONSUMED; - } - - *pskb = skb; - skb->dev = priv->upperdev; - skb->pkt_type = PACKET_HOST; - return RX_HANDLER_ANOTHER; -} - -/* Called with rtnl lock held. */ -static int virt_wifi_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(dev); - int err; - - if (!tb[IFLA_LINK]) - return -EINVAL; - - netif_carrier_off(dev); - - priv->upperdev = dev; - priv->lowerdev = __dev_get_by_index(src_net, - nla_get_u32(tb[IFLA_LINK])); - - if (!priv->lowerdev) - return -ENODEV; - if (!tb[IFLA_MTU]) - dev->mtu = priv->lowerdev->mtu; - else if (dev->mtu > priv->lowerdev->mtu) - return -EINVAL; - - err = netdev_rx_handler_register(priv->lowerdev, virt_wifi_rx_handler, - priv); - if (err) { - dev_err(&priv->lowerdev->dev, - "can't netdev_rx_handler_register: %d\n", err); - return err; - } - - eth_hw_addr_inherit(dev, priv->lowerdev); - netif_stacked_transfer_operstate(priv->lowerdev, dev); - - SET_NETDEV_DEV(dev, &priv->lowerdev->dev); - dev->ieee80211_ptr = kzalloc(sizeof(*dev->ieee80211_ptr), GFP_KERNEL); - - if (!dev->ieee80211_ptr) { - err = -ENOMEM; - goto remove_handler; - } - - dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; - dev->ieee80211_ptr->wiphy = common_wiphy; - - err = register_netdevice(dev); - if (err) { - dev_err(&priv->lowerdev->dev, "can't register_netdevice: %d\n", - err); - goto free_wireless_dev; - } - - err = netdev_upper_dev_link(priv->lowerdev, dev, extack); - if (err) { - dev_err(&priv->lowerdev->dev, "can't netdev_upper_dev_link: %d\n", - err); - goto unregister_netdev; - } - - dev->priv_destructor = virt_wifi_net_device_destructor; - priv->being_deleted = false; - priv->is_connected = false; - priv->is_up = false; - INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete); - __module_get(THIS_MODULE); - - return 0; -unregister_netdev: - unregister_netdevice(dev); -free_wireless_dev: - kfree(dev->ieee80211_ptr); - dev->ieee80211_ptr = NULL; -remove_handler: - netdev_rx_handler_unregister(priv->lowerdev); - - return err; -} - -/* Called with rtnl lock held. */ -static void virt_wifi_dellink(struct net_device *dev, - struct list_head *head) -{ - struct virt_wifi_netdev_priv *priv = netdev_priv(dev); - - if (dev->ieee80211_ptr) - virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy); - - priv->being_deleted = true; - virt_wifi_cancel_connect(dev); - netif_carrier_off(dev); - - netdev_rx_handler_unregister(priv->lowerdev); - netdev_upper_dev_unlink(priv->lowerdev, dev); - - unregister_netdevice_queue(dev, head); - module_put(THIS_MODULE); - - /* Deleting the wiphy is handled in the module destructor. */ -} - -static struct rtnl_link_ops virt_wifi_link_ops = { - .kind = "virt_wifi", - .setup = virt_wifi_setup, - .newlink = virt_wifi_newlink, - .dellink = virt_wifi_dellink, - .priv_size = sizeof(struct virt_wifi_netdev_priv), -}; - -static bool netif_is_virt_wifi_dev(const struct net_device *dev) -{ - return rcu_access_pointer(dev->rx_handler) == virt_wifi_rx_handler; -} - -static int virt_wifi_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *lower_dev = netdev_notifier_info_to_dev(ptr); - struct virt_wifi_netdev_priv *priv; - struct net_device *upper_dev; - LIST_HEAD(list_kill); - - if (!netif_is_virt_wifi_dev(lower_dev)) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UNREGISTER: - priv = rtnl_dereference(lower_dev->rx_handler_data); - if (!priv) - return NOTIFY_DONE; - - upper_dev = priv->upperdev; - - upper_dev->rtnl_link_ops->dellink(upper_dev, &list_kill); - unregister_netdevice_many(&list_kill); - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block virt_wifi_notifier = { - .notifier_call = virt_wifi_event, -}; - -/* Acquires and releases the rtnl lock. */ -static int __init virt_wifi_init_module(void) -{ - int err; - - /* Guaranteed to be locally-administered and not multicast. */ - eth_random_addr(fake_router_bssid); - - err = register_netdevice_notifier(&virt_wifi_notifier); - if (err) - return err; - - err = -ENOMEM; - common_wiphy = virt_wifi_make_wiphy(); - if (!common_wiphy) - goto notifier; - - err = rtnl_link_register(&virt_wifi_link_ops); - if (err) - goto destroy_wiphy; - - return 0; - -destroy_wiphy: - virt_wifi_destroy_wiphy(common_wiphy); -notifier: - unregister_netdevice_notifier(&virt_wifi_notifier); - return err; -} - -/* Acquires and releases the rtnl lock. */ -static void __exit virt_wifi_cleanup_module(void) -{ - /* Will delete any devices that depend on the wiphy. */ - rtnl_link_unregister(&virt_wifi_link_ops); - virt_wifi_destroy_wiphy(common_wiphy); - unregister_netdevice_notifier(&virt_wifi_notifier); -} - -module_init(virt_wifi_init_module); -module_exit(virt_wifi_cleanup_module); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Cody Schuffelen "); -MODULE_DESCRIPTION("Driver for a wireless wrapper of ethernet devices"); -MODULE_ALIAS_RTNL_LINK("virt_wifi"); diff --git a/drivers/net/wireless/virtual/Kconfig b/drivers/net/wireless/virtual/Kconfig new file mode 100644 index 000000000000..fb3b4b69f26b --- /dev/null +++ b/drivers/net/wireless/virtual/Kconfig @@ -0,0 +1,20 @@ +config MAC80211_HWSIM + tristate "Simulated radio testing tool for mac80211" + depends on MAC80211 + help + This driver is a developer testing tool that can be used to test + IEEE 802.11 networking stack (mac80211) functionality. This is not + needed for normal wireless LAN usage and is only for testing. See + Documentation/networking/mac80211_hwsim for more information on how + to use this tool. + + To compile this driver as a module, choose M here: the module will be + called mac80211_hwsim. If unsure, say N. + +config VIRT_WIFI + tristate "Wifi wrapper for ethernet drivers" + depends on CFG80211 + help + This option adds support for ethernet connections to appear as if they + are wifi connections through a special rtnetlink device. + diff --git a/drivers/net/wireless/virtual/Makefile b/drivers/net/wireless/virtual/Makefile new file mode 100644 index 000000000000..5773cc6d643e --- /dev/null +++ b/drivers/net/wireless/virtual/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o + +obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c new file mode 100644 index 000000000000..f4bdc243ea0d --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -0,0 +1,5901 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 + * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2011, Javier Lopez + * Copyright (c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 - 2022 Intel Corporation + */ + +/* + * TODO: + * - Add TSF sync and fix IBSS beacon transmission by adding + * competition for "air time" at TBTT + * - RX filtering based on filter configuration (data->rx_filter) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mac80211_hwsim.h" + +#define WARN_QUEUE 100 +#define MAX_QUEUE 200 + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211"); +MODULE_LICENSE("GPL"); + +static int radios = 2; +module_param(radios, int, 0444); +MODULE_PARM_DESC(radios, "Number of simulated radios"); + +static int channels = 1; +module_param(channels, int, 0444); +MODULE_PARM_DESC(channels, "Number of concurrent channels"); + +static bool paged_rx = false; +module_param(paged_rx, bool, 0644); +MODULE_PARM_DESC(paged_rx, "Use paged SKBs for RX instead of linear ones"); + +static bool rctbl = false; +module_param(rctbl, bool, 0444); +MODULE_PARM_DESC(rctbl, "Handle rate control table"); + +static bool support_p2p_device = true; +module_param(support_p2p_device, bool, 0444); +MODULE_PARM_DESC(support_p2p_device, "Support P2P-Device interface type"); + +static bool mlo; +module_param(mlo, bool, 0444); +MODULE_PARM_DESC(mlo, "Support MLO"); + +/** + * enum hwsim_regtest - the type of regulatory tests we offer + * + * These are the different values you can use for the regtest + * module parameter. This is useful to help test world roaming + * and the driver regulatory_hint() call and combinations of these. + * If you want to do specific alpha2 regulatory domain tests simply + * use the userspace regulatory request as that will be respected as + * well without the need of this module parameter. This is designed + * only for testing the driver regulatory request, world roaming + * and all possible combinations. + * + * @HWSIM_REGTEST_DISABLED: No regulatory tests are performed, + * this is the default value. + * @HWSIM_REGTEST_DRIVER_REG_FOLLOW: Used for testing the driver regulatory + * hint, only one driver regulatory hint will be sent as such the + * secondary radios are expected to follow. + * @HWSIM_REGTEST_DRIVER_REG_ALL: Used for testing the driver regulatory + * request with all radios reporting the same regulatory domain. + * @HWSIM_REGTEST_DIFF_COUNTRY: Used for testing the drivers calling + * different regulatory domains requests. Expected behaviour is for + * an intersection to occur but each device will still use their + * respective regulatory requested domains. Subsequent radios will + * use the resulting intersection. + * @HWSIM_REGTEST_WORLD_ROAM: Used for testing the world roaming. We accomplish + * this by using a custom beacon-capable regulatory domain for the first + * radio. All other device world roam. + * @HWSIM_REGTEST_CUSTOM_WORLD: Used for testing the custom world regulatory + * domain requests. All radios will adhere to this custom world regulatory + * domain. + * @HWSIM_REGTEST_CUSTOM_WORLD_2: Used for testing 2 custom world regulatory + * domain requests. The first radio will adhere to the first custom world + * regulatory domain, the second one to the second custom world regulatory + * domain. All other devices will world roam. + * @HWSIM_REGTEST_STRICT_FOLLOW: Used for testing strict regulatory domain + * settings, only the first radio will send a regulatory domain request + * and use strict settings. The rest of the radios are expected to follow. + * @HWSIM_REGTEST_STRICT_ALL: Used for testing strict regulatory domain + * settings. All radios will adhere to this. + * @HWSIM_REGTEST_STRICT_AND_DRIVER_REG: Used for testing strict regulatory + * domain settings, combined with secondary driver regulatory domain + * settings. The first radio will get a strict regulatory domain setting + * using the first driver regulatory request and the second radio will use + * non-strict settings using the second driver regulatory request. All + * other devices should follow the intersection created between the + * first two. + * @HWSIM_REGTEST_ALL: Used for testing every possible mix. You will need + * at least 6 radios for a complete test. We will test in this order: + * 1 - driver custom world regulatory domain + * 2 - second custom world regulatory domain + * 3 - first driver regulatory domain request + * 4 - second driver regulatory domain request + * 5 - strict regulatory domain settings using the third driver regulatory + * domain request + * 6 and on - should follow the intersection of the 3rd, 4rth and 5th radio + * regulatory requests. + */ +enum hwsim_regtest { + HWSIM_REGTEST_DISABLED = 0, + HWSIM_REGTEST_DRIVER_REG_FOLLOW = 1, + HWSIM_REGTEST_DRIVER_REG_ALL = 2, + HWSIM_REGTEST_DIFF_COUNTRY = 3, + HWSIM_REGTEST_WORLD_ROAM = 4, + HWSIM_REGTEST_CUSTOM_WORLD = 5, + HWSIM_REGTEST_CUSTOM_WORLD_2 = 6, + HWSIM_REGTEST_STRICT_FOLLOW = 7, + HWSIM_REGTEST_STRICT_ALL = 8, + HWSIM_REGTEST_STRICT_AND_DRIVER_REG = 9, + HWSIM_REGTEST_ALL = 10, +}; + +/* Set to one of the HWSIM_REGTEST_* values above */ +static int regtest = HWSIM_REGTEST_DISABLED; +module_param(regtest, int, 0444); +MODULE_PARM_DESC(regtest, "The type of regulatory test we want to run"); + +static const char *hwsim_alpha2s[] = { + "FI", + "AL", + "US", + "DE", + "JP", + "AL", +}; + +static const struct ieee80211_regdomain hwsim_world_regdom_custom_01 = { + .n_reg_rules = 5, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), + REG_RULE(2484-10, 2484+10, 40, 0, 20, 0), + REG_RULE(5150-10, 5240+10, 40, 0, 30, 0), + REG_RULE(5745-10, 5825+10, 40, 0, 30, 0), + REG_RULE(5855-10, 5925+10, 40, 0, 33, 0), + } +}; + +static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = { + .n_reg_rules = 3, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), + REG_RULE(5725-10, 5850+10, 40, 0, 30, + NL80211_RRF_NO_IR), + REG_RULE(5855-10, 5925+10, 40, 0, 33, 0), + } +}; + +static const struct ieee80211_regdomain hwsim_world_regdom_custom_03 = { + .n_reg_rules = 6, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0), + REG_RULE(2484 - 10, 2484 + 10, 40, 0, 20, 0), + REG_RULE(5150 - 10, 5240 + 10, 40, 0, 30, 0), + REG_RULE(5745 - 10, 5825 + 10, 40, 0, 30, 0), + REG_RULE(5855 - 10, 5925 + 10, 40, 0, 33, 0), + REG_RULE(5955 - 10, 7125 + 10, 320, 0, 33, 0), + } +}; + +static const struct ieee80211_regdomain *hwsim_world_regdom_custom[] = { + &hwsim_world_regdom_custom_01, + &hwsim_world_regdom_custom_02, + &hwsim_world_regdom_custom_03, +}; + +struct hwsim_vif_priv { + u32 magic; + u8 bssid[ETH_ALEN]; + bool assoc; + bool bcn_en; + u16 aid; +}; + +#define HWSIM_VIF_MAGIC 0x69537748 + +static inline void hwsim_check_magic(struct ieee80211_vif *vif) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + WARN(vp->magic != HWSIM_VIF_MAGIC, + "Invalid VIF (%p) magic %#x, %pM, %d/%d\n", + vif, vp->magic, vif->addr, vif->type, vif->p2p); +} + +static inline void hwsim_set_magic(struct ieee80211_vif *vif) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + vp->magic = HWSIM_VIF_MAGIC; +} + +static inline void hwsim_clear_magic(struct ieee80211_vif *vif) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + vp->magic = 0; +} + +struct hwsim_sta_priv { + u32 magic; + unsigned int last_link; + u16 active_links_rx; +}; + +#define HWSIM_STA_MAGIC 0x6d537749 + +static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + WARN_ON(sp->magic != HWSIM_STA_MAGIC); +} + +static inline void hwsim_set_sta_magic(struct ieee80211_sta *sta) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + sp->magic = HWSIM_STA_MAGIC; +} + +static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + sp->magic = 0; +} + +struct hwsim_chanctx_priv { + u32 magic; +}; + +#define HWSIM_CHANCTX_MAGIC 0x6d53774a + +static inline void hwsim_check_chanctx_magic(struct ieee80211_chanctx_conf *c) +{ + struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; + WARN_ON(cp->magic != HWSIM_CHANCTX_MAGIC); +} + +static inline void hwsim_set_chanctx_magic(struct ieee80211_chanctx_conf *c) +{ + struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; + cp->magic = HWSIM_CHANCTX_MAGIC; +} + +static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c) +{ + struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; + cp->magic = 0; +} + +static unsigned int hwsim_net_id; + +static DEFINE_IDA(hwsim_netgroup_ida); + +struct hwsim_net { + int netgroup; + u32 wmediumd; +}; + +static inline int hwsim_net_get_netgroup(struct net *net) +{ + struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); + + return hwsim_net->netgroup; +} + +static inline int hwsim_net_set_netgroup(struct net *net) +{ + struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); + + hwsim_net->netgroup = ida_alloc(&hwsim_netgroup_ida, GFP_KERNEL); + return hwsim_net->netgroup >= 0 ? 0 : -ENOMEM; +} + +static inline u32 hwsim_net_get_wmediumd(struct net *net) +{ + struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); + + return hwsim_net->wmediumd; +} + +static inline void hwsim_net_set_wmediumd(struct net *net, u32 portid) +{ + struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id); + + hwsim_net->wmediumd = portid; +} + +static struct class *hwsim_class; + +static struct net_device *hwsim_mon; /* global monitor netdev */ + +#define CHAN2G(_freq) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_freq), \ +} + +#define CHAN5G(_freq) { \ + .band = NL80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_freq), \ +} + +#define CHAN6G(_freq) { \ + .band = NL80211_BAND_6GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_freq), \ +} + +static const struct ieee80211_channel hwsim_channels_2ghz[] = { + CHAN2G(2412), /* Channel 1 */ + CHAN2G(2417), /* Channel 2 */ + CHAN2G(2422), /* Channel 3 */ + CHAN2G(2427), /* Channel 4 */ + CHAN2G(2432), /* Channel 5 */ + CHAN2G(2437), /* Channel 6 */ + CHAN2G(2442), /* Channel 7 */ + CHAN2G(2447), /* Channel 8 */ + CHAN2G(2452), /* Channel 9 */ + CHAN2G(2457), /* Channel 10 */ + CHAN2G(2462), /* Channel 11 */ + CHAN2G(2467), /* Channel 12 */ + CHAN2G(2472), /* Channel 13 */ + CHAN2G(2484), /* Channel 14 */ +}; + +static const struct ieee80211_channel hwsim_channels_5ghz[] = { + CHAN5G(5180), /* Channel 36 */ + CHAN5G(5200), /* Channel 40 */ + CHAN5G(5220), /* Channel 44 */ + CHAN5G(5240), /* Channel 48 */ + + CHAN5G(5260), /* Channel 52 */ + CHAN5G(5280), /* Channel 56 */ + CHAN5G(5300), /* Channel 60 */ + CHAN5G(5320), /* Channel 64 */ + + CHAN5G(5500), /* Channel 100 */ + CHAN5G(5520), /* Channel 104 */ + CHAN5G(5540), /* Channel 108 */ + CHAN5G(5560), /* Channel 112 */ + CHAN5G(5580), /* Channel 116 */ + CHAN5G(5600), /* Channel 120 */ + CHAN5G(5620), /* Channel 124 */ + CHAN5G(5640), /* Channel 128 */ + CHAN5G(5660), /* Channel 132 */ + CHAN5G(5680), /* Channel 136 */ + CHAN5G(5700), /* Channel 140 */ + + CHAN5G(5745), /* Channel 149 */ + CHAN5G(5765), /* Channel 153 */ + CHAN5G(5785), /* Channel 157 */ + CHAN5G(5805), /* Channel 161 */ + CHAN5G(5825), /* Channel 165 */ + CHAN5G(5845), /* Channel 169 */ + + CHAN5G(5855), /* Channel 171 */ + CHAN5G(5860), /* Channel 172 */ + CHAN5G(5865), /* Channel 173 */ + CHAN5G(5870), /* Channel 174 */ + + CHAN5G(5875), /* Channel 175 */ + CHAN5G(5880), /* Channel 176 */ + CHAN5G(5885), /* Channel 177 */ + CHAN5G(5890), /* Channel 178 */ + CHAN5G(5895), /* Channel 179 */ + CHAN5G(5900), /* Channel 180 */ + CHAN5G(5905), /* Channel 181 */ + + CHAN5G(5910), /* Channel 182 */ + CHAN5G(5915), /* Channel 183 */ + CHAN5G(5920), /* Channel 184 */ + CHAN5G(5925), /* Channel 185 */ +}; + +static const struct ieee80211_channel hwsim_channels_6ghz[] = { + CHAN6G(5955), /* Channel 1 */ + CHAN6G(5975), /* Channel 5 */ + CHAN6G(5995), /* Channel 9 */ + CHAN6G(6015), /* Channel 13 */ + CHAN6G(6035), /* Channel 17 */ + CHAN6G(6055), /* Channel 21 */ + CHAN6G(6075), /* Channel 25 */ + CHAN6G(6095), /* Channel 29 */ + CHAN6G(6115), /* Channel 33 */ + CHAN6G(6135), /* Channel 37 */ + CHAN6G(6155), /* Channel 41 */ + CHAN6G(6175), /* Channel 45 */ + CHAN6G(6195), /* Channel 49 */ + CHAN6G(6215), /* Channel 53 */ + CHAN6G(6235), /* Channel 57 */ + CHAN6G(6255), /* Channel 61 */ + CHAN6G(6275), /* Channel 65 */ + CHAN6G(6295), /* Channel 69 */ + CHAN6G(6315), /* Channel 73 */ + CHAN6G(6335), /* Channel 77 */ + CHAN6G(6355), /* Channel 81 */ + CHAN6G(6375), /* Channel 85 */ + CHAN6G(6395), /* Channel 89 */ + CHAN6G(6415), /* Channel 93 */ + CHAN6G(6435), /* Channel 97 */ + CHAN6G(6455), /* Channel 181 */ + CHAN6G(6475), /* Channel 105 */ + CHAN6G(6495), /* Channel 109 */ + CHAN6G(6515), /* Channel 113 */ + CHAN6G(6535), /* Channel 117 */ + CHAN6G(6555), /* Channel 121 */ + CHAN6G(6575), /* Channel 125 */ + CHAN6G(6595), /* Channel 129 */ + CHAN6G(6615), /* Channel 133 */ + CHAN6G(6635), /* Channel 137 */ + CHAN6G(6655), /* Channel 141 */ + CHAN6G(6675), /* Channel 145 */ + CHAN6G(6695), /* Channel 149 */ + CHAN6G(6715), /* Channel 153 */ + CHAN6G(6735), /* Channel 157 */ + CHAN6G(6755), /* Channel 161 */ + CHAN6G(6775), /* Channel 165 */ + CHAN6G(6795), /* Channel 169 */ + CHAN6G(6815), /* Channel 173 */ + CHAN6G(6835), /* Channel 177 */ + CHAN6G(6855), /* Channel 181 */ + CHAN6G(6875), /* Channel 185 */ + CHAN6G(6895), /* Channel 189 */ + CHAN6G(6915), /* Channel 193 */ + CHAN6G(6935), /* Channel 197 */ + CHAN6G(6955), /* Channel 201 */ + CHAN6G(6975), /* Channel 205 */ + CHAN6G(6995), /* Channel 209 */ + CHAN6G(7015), /* Channel 213 */ + CHAN6G(7035), /* Channel 217 */ + CHAN6G(7055), /* Channel 221 */ + CHAN6G(7075), /* Channel 225 */ + CHAN6G(7095), /* Channel 229 */ + CHAN6G(7115), /* Channel 233 */ +}; + +#define NUM_S1G_CHANS_US 51 +static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US]; + +static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = { + .s1g = true, + .cap = { S1G_CAP0_SGI_1MHZ | S1G_CAP0_SGI_2MHZ, + 0, + 0, + S1G_CAP3_MAX_MPDU_LEN, + 0, + S1G_CAP5_AMPDU, + 0, + S1G_CAP7_DUP_1MHZ, + S1G_CAP8_TWT_RESPOND | S1G_CAP8_TWT_REQUEST, + 0}, + .nss_mcs = { 0xfc | 1, /* MCS 7 for 1 SS */ + /* RX Highest Supported Long GI Data Rate 0:7 */ + 0, + /* RX Highest Supported Long GI Data Rate 0:7 */ + /* TX S1G MCS Map 0:6 */ + 0xfa, + /* TX S1G MCS Map :7 */ + /* TX Highest Supported Long GI Data Rate 0:6 */ + 0x80, + /* TX Highest Supported Long GI Data Rate 7:8 */ + /* Rx Single spatial stream and S1G-MCS Map for 1MHz */ + /* Tx Single spatial stream and S1G-MCS Map for 1MHz */ + 0 }, +}; + +static void hwsim_init_s1g_channels(struct ieee80211_channel *chans) +{ + int ch, freq; + + for (ch = 0; ch < NUM_S1G_CHANS_US; ch++) { + freq = 902000 + (ch + 1) * 500; + chans[ch].band = NL80211_BAND_S1GHZ; + chans[ch].center_freq = KHZ_TO_MHZ(freq); + chans[ch].freq_offset = freq % 1000; + chans[ch].hw_value = ch + 1; + } +} + +static const struct ieee80211_rate hwsim_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60 }, + { .bitrate = 90 }, + { .bitrate = 120 }, + { .bitrate = 180 }, + { .bitrate = 240 }, + { .bitrate = 360 }, + { .bitrate = 480 }, + { .bitrate = 540 } +}; + +#define DEFAULT_RX_RSSI -50 + +static const u32 hwsim_ciphers[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_CCMP_256, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, + WLAN_CIPHER_SUITE_AES_CMAC, + WLAN_CIPHER_SUITE_BIP_CMAC_256, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256, +}; + +#define OUI_QCA 0x001374 +#define QCA_NL80211_SUBCMD_TEST 1 +enum qca_nl80211_vendor_subcmds { + QCA_WLAN_VENDOR_ATTR_TEST = 8, + QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_TEST +}; + +static const struct nla_policy +hwsim_vendor_test_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_MAX] = { .type = NLA_U32 }, +}; + +static int mac80211_hwsim_vendor_cmd_test(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + int err; + u32 val; + + err = nla_parse_deprecated(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, + data_len, hwsim_vendor_test_policy, NULL); + if (err) + return err; + if (!tb[QCA_WLAN_VENDOR_ATTR_TEST]) + return -EINVAL; + val = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_TEST]); + wiphy_dbg(wiphy, "%s: test=%u\n", __func__, val); + + /* Send a vendor event as a test. Note that this would not normally be + * done within a command handler, but rather, based on some other + * trigger. For simplicity, this command is used to trigger the event + * here. + * + * event_idx = 0 (index in mac80211_hwsim_vendor_commands) + */ + skb = cfg80211_vendor_event_alloc(wiphy, wdev, 100, 0, GFP_KERNEL); + if (skb) { + /* skb_put() or nla_put() will fill up data within + * NL80211_ATTR_VENDOR_DATA. + */ + + /* Add vendor data */ + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 1); + + /* Send the event - this will call nla_nest_end() */ + cfg80211_vendor_event(skb, GFP_KERNEL); + } + + /* Send a response to the command */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 10); + if (!skb) + return -ENOMEM; + + /* skb_put() or nla_put() will fill up data within + * NL80211_ATTR_VENDOR_DATA + */ + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TEST, val + 2); + + return cfg80211_vendor_cmd_reply(skb); +} + +static struct wiphy_vendor_command mac80211_hwsim_vendor_commands[] = { + { + .info = { .vendor_id = OUI_QCA, + .subcmd = QCA_NL80211_SUBCMD_TEST }, + .flags = WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = mac80211_hwsim_vendor_cmd_test, + .policy = hwsim_vendor_test_policy, + .maxattr = QCA_WLAN_VENDOR_ATTR_MAX, + } +}; + +/* Advertise support vendor specific events */ +static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = { + { .vendor_id = OUI_QCA, .subcmd = 1 }, +}; + +static DEFINE_SPINLOCK(hwsim_radio_lock); +static LIST_HEAD(hwsim_radios); +static struct rhashtable hwsim_radios_rht; +static int hwsim_radio_idx; +static int hwsim_radios_generation = 1; + +static struct platform_driver mac80211_hwsim_driver = { + .driver = { + .name = "mac80211_hwsim", + }, +}; + +struct mac80211_hwsim_link_data { + u32 link_id; + u64 beacon_int /* beacon interval in us */; + struct hrtimer beacon_timer; +}; + +struct mac80211_hwsim_data { + struct list_head list; + struct rhash_head rht; + struct ieee80211_hw *hw; + struct device *dev; + struct ieee80211_supported_band bands[NUM_NL80211_BANDS]; + struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; + struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; + struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)]; + struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)]; + struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; + struct ieee80211_iface_combination if_combination; + struct ieee80211_iface_limit if_limits[3]; + int n_if_limits; + + u32 ciphers[ARRAY_SIZE(hwsim_ciphers)]; + + struct mac_address addresses[2]; + int channels, idx; + bool use_chanctx; + bool destroy_on_close; + u32 portid; + char alpha2[2]; + const struct ieee80211_regdomain *regd; + + struct ieee80211_channel *tmp_chan; + struct ieee80211_channel *roc_chan; + u32 roc_duration; + struct delayed_work roc_start; + struct delayed_work roc_done; + struct delayed_work hw_scan; + struct cfg80211_scan_request *hw_scan_request; + struct ieee80211_vif *hw_scan_vif; + int scan_chan_idx; + u8 scan_addr[ETH_ALEN]; + struct { + struct ieee80211_channel *channel; + unsigned long next_start, start, end; + } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) + + ARRAY_SIZE(hwsim_channels_5ghz) + + ARRAY_SIZE(hwsim_channels_6ghz)]; + + struct ieee80211_channel *channel; + enum nl80211_chan_width bw; + unsigned int rx_filter; + bool started, idle, scanning; + struct mutex mutex; + enum ps_mode { + PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL + } ps; + bool ps_poll_pending; + struct dentry *debugfs; + + atomic_t pending_cookie; + struct sk_buff_head pending; /* packets pending */ + /* + * Only radios in the same group can communicate together (the + * channel has to match too). Each bit represents a group. A + * radio can be in more than one group. + */ + u64 group; + + /* group shared by radios created in the same netns */ + int netgroup; + /* wmediumd portid responsible for netgroup of this radio */ + u32 wmediumd; + + /* difference between this hw's clock and the real clock, in usecs */ + s64 tsf_offset; + s64 bcn_delta; + /* absolute beacon transmission time. Used to cover up "tx" delay. */ + u64 abs_bcn_ts; + + /* Stats */ + u64 tx_pkts; + u64 rx_pkts; + u64 tx_bytes; + u64 rx_bytes; + u64 tx_dropped; + u64 tx_failed; + + /* RSSI in rx status of the receiver */ + int rx_rssi; + + struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS]; +}; + +static const struct rhashtable_params hwsim_rht_params = { + .nelem_hint = 2, + .automatic_shrinking = true, + .key_len = ETH_ALEN, + .key_offset = offsetof(struct mac80211_hwsim_data, addresses[1]), + .head_offset = offsetof(struct mac80211_hwsim_data, rht), +}; + +struct hwsim_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + __le64 rt_tsft; + u8 rt_flags; + u8 rt_rate; + __le16 rt_channel; + __le16 rt_chbitmask; +} __packed; + +struct hwsim_radiotap_ack_hdr { + struct ieee80211_radiotap_header hdr; + u8 rt_flags; + u8 pad; + __le16 rt_channel; + __le16 rt_chbitmask; +} __packed; + +/* MAC80211_HWSIM netlink family */ +static struct genl_family hwsim_genl_family; + +enum hwsim_multicast_groups { + HWSIM_MCGRP_CONFIG, +}; + +static const struct genl_multicast_group hwsim_mcgrps[] = { + [HWSIM_MCGRP_CONFIG] = { .name = "config", }, +}; + +/* MAC80211_HWSIM netlink policy */ + +static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { + [HWSIM_ATTR_ADDR_RECEIVER] = NLA_POLICY_ETH_ADDR_COMPAT, + [HWSIM_ATTR_ADDR_TRANSMITTER] = NLA_POLICY_ETH_ADDR_COMPAT, + [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 }, + [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 }, + [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 }, + [HWSIM_ATTR_TX_INFO] = { .type = NLA_BINARY, + .len = IEEE80211_TX_MAX_RATES * + sizeof(struct hwsim_tx_rate)}, + [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, + [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 }, + [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 }, + [HWSIM_ATTR_REG_HINT_ALPHA2] = { .type = NLA_STRING, .len = 2 }, + [HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 }, + [HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG }, + [HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG }, + [HWSIM_ATTR_USE_CHANCTX] = { .type = NLA_FLAG }, + [HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG }, + [HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING }, + [HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG }, + [HWSIM_ATTR_FREQ] = { .type = NLA_U32 }, + [HWSIM_ATTR_TX_INFO_FLAGS] = { .type = NLA_BINARY }, + [HWSIM_ATTR_PERM_ADDR] = NLA_POLICY_ETH_ADDR_COMPAT, + [HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 }, + [HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY }, + [HWSIM_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, +}; + +#if IS_REACHABLE(CONFIG_VIRTIO) + +/* MAC80211_HWSIM virtio queues */ +static struct virtqueue *hwsim_vqs[HWSIM_NUM_VQS]; +static bool hwsim_virtio_enabled; +static DEFINE_SPINLOCK(hwsim_virtio_lock); + +static void hwsim_virtio_rx_work(struct work_struct *work); +static DECLARE_WORK(hwsim_virtio_rx, hwsim_virtio_rx_work); + +static int hwsim_tx_virtio(struct mac80211_hwsim_data *data, + struct sk_buff *skb) +{ + struct scatterlist sg[1]; + unsigned long flags; + int err; + + spin_lock_irqsave(&hwsim_virtio_lock, flags); + if (!hwsim_virtio_enabled) { + err = -ENODEV; + goto out_free; + } + + sg_init_one(sg, skb->head, skb_end_offset(skb)); + err = virtqueue_add_outbuf(hwsim_vqs[HWSIM_VQ_TX], sg, 1, skb, + GFP_ATOMIC); + if (err) + goto out_free; + virtqueue_kick(hwsim_vqs[HWSIM_VQ_TX]); + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); + return 0; + +out_free: + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); + nlmsg_free(skb); + return err; +} +#else +/* cause a linker error if this ends up being needed */ +extern int hwsim_tx_virtio(struct mac80211_hwsim_data *data, + struct sk_buff *skb); +#define hwsim_virtio_enabled false +#endif + +static int hwsim_get_chanwidth(enum nl80211_chan_width bw) +{ + switch (bw) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return 20; + case NL80211_CHAN_WIDTH_40: + return 40; + case NL80211_CHAN_WIDTH_80: + return 80; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + return 160; + case NL80211_CHAN_WIDTH_320: + return 320; + case NL80211_CHAN_WIDTH_5: + return 5; + case NL80211_CHAN_WIDTH_10: + return 10; + case NL80211_CHAN_WIDTH_1: + return 1; + case NL80211_CHAN_WIDTH_2: + return 2; + case NL80211_CHAN_WIDTH_4: + return 4; + case NL80211_CHAN_WIDTH_8: + return 8; + case NL80211_CHAN_WIDTH_16: + return 16; + } + + return INT_MAX; +} + +static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan); + +/* sysfs attributes */ +static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct sk_buff *skb; + struct ieee80211_pspoll *pspoll; + + if (!vp->assoc) + return; + + wiphy_dbg(data->hw->wiphy, + "%s: send PS-Poll to %pM for aid %d\n", + __func__, vp->bssid, vp->aid); + + skb = dev_alloc_skb(sizeof(*pspoll)); + if (!skb) + return; + pspoll = skb_put(skb, sizeof(*pspoll)); + pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_PSPOLL | + IEEE80211_FCTL_PM); + pspoll->aid = cpu_to_le16(0xc000 | vp->aid); + memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); + memcpy(pspoll->ta, mac, ETH_ALEN); + + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->bss_conf.chanctx_conf)->def.chan); + rcu_read_unlock(); +} + +static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, + struct ieee80211_vif *vif, int ps) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *cb; + + if (!vp->assoc) + return; + + wiphy_dbg(data->hw->wiphy, + "%s: send data::nullfunc to %pM ps=%d\n", + __func__, vp->bssid, ps); + + skb = dev_alloc_skb(sizeof(*hdr)); + if (!skb) + return; + hdr = skb_put(skb, sizeof(*hdr) - ETH_ALEN); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS | + (ps ? IEEE80211_FCTL_PM : 0)); + hdr->duration_id = cpu_to_le16(0); + memcpy(hdr->addr1, vp->bssid, ETH_ALEN); + memcpy(hdr->addr2, mac, ETH_ALEN); + memcpy(hdr->addr3, vp->bssid, ETH_ALEN); + + cb = IEEE80211_SKB_CB(skb); + cb->control.rates[0].count = 1; + cb->control.rates[1].idx = -1; + + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->bss_conf.chanctx_conf)->def.chan); + rcu_read_unlock(); +} + + +static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + hwsim_send_nullfunc(data, mac, vif, 1); +} + +static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + hwsim_send_nullfunc(data, mac, vif, 0); +} + +static int hwsim_fops_ps_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->ps; + return 0; +} + +static int hwsim_fops_ps_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + enum ps_mode old_ps; + + if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && + val != PS_MANUAL_POLL) + return -EINVAL; + + if (val == PS_MANUAL_POLL) { + if (data->ps != PS_ENABLED) + return -EINVAL; + local_bh_disable(); + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_ps_poll, data); + local_bh_enable(); + return 0; + } + old_ps = data->ps; + data->ps = val; + + local_bh_disable(); + if (old_ps == PS_DISABLED && val != PS_DISABLED) { + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_ps, data); + } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_no_ps, data); + } + local_bh_enable(); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, + "%llu\n"); + +static int hwsim_write_simulate_radar(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + + ieee80211_radar_detected(data->hw); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(hwsim_simulate_radar, NULL, + hwsim_write_simulate_radar, "%llu\n"); + +static int hwsim_fops_group_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->group; + return 0; +} + +static int hwsim_fops_group_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + data->group = val; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_group, + hwsim_fops_group_read, hwsim_fops_group_write, + "%llx\n"); + +static int hwsim_fops_rx_rssi_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->rx_rssi; + return 0; +} + +static int hwsim_fops_rx_rssi_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + int rssi = (int)val; + + if (rssi >= 0 || rssi < -100) + return -EINVAL; + + data->rx_rssi = rssi; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(hwsim_fops_rx_rssi, + hwsim_fops_rx_rssi_read, hwsim_fops_rx_rssi_write, + "%lld\n"); + +static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + /* TODO: allow packet injection */ + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static inline u64 mac80211_hwsim_get_tsf_raw(void) +{ + return ktime_to_us(ktime_get_real()); +} + +static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) +{ + u64 now = mac80211_hwsim_get_tsf_raw(); + return cpu_to_le64(now + data->tsf_offset); +} + +static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = hw->priv; + return le64_to_cpu(__mac80211_hwsim_get_tsf(data)); +} + +static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) +{ + struct mac80211_hwsim_data *data = hw->priv; + u64 now = mac80211_hwsim_get_tsf(hw, vif); + /* MLD not supported here */ + u32 bcn_int = data->link_data[0].beacon_int; + u64 delta = abs(tsf - now); + + /* adjust after beaconing with new timestamp at old TBTT */ + if (tsf > now) { + data->tsf_offset += delta; + data->bcn_delta = do_div(delta, bcn_int); + } else { + data->tsf_offset -= delta; + data->bcn_delta = -(s64)do_div(delta, bcn_int); + } +} + +static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, + struct sk_buff *tx_skb, + struct ieee80211_channel *chan) +{ + struct mac80211_hwsim_data *data = hw->priv; + struct sk_buff *skb; + struct hwsim_radiotap_hdr *hdr; + u16 flags, bitrate; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb); + struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); + + if (!txrate) + bitrate = 0; + else + bitrate = txrate->bitrate; + + if (!netif_running(hwsim_mon)) + return; + + skb = skb_copy_expand(tx_skb, sizeof(*hdr), 0, GFP_ATOMIC); + if (skb == NULL) + return; + + hdr = skb_push(skb, sizeof(*hdr)); + hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; + hdr->hdr.it_pad = 0; + hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); + hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_RATE) | + (1 << IEEE80211_RADIOTAP_TSFT) | + (1 << IEEE80211_RADIOTAP_CHANNEL)); + hdr->rt_tsft = __mac80211_hwsim_get_tsf(data); + hdr->rt_flags = 0; + hdr->rt_rate = bitrate / 5; + hdr->rt_channel = cpu_to_le16(chan->center_freq); + flags = IEEE80211_CHAN_2GHZ; + if (txrate && txrate->flags & IEEE80211_RATE_ERP_G) + flags |= IEEE80211_CHAN_OFDM; + else + flags |= IEEE80211_CHAN_CCK; + hdr->rt_chbitmask = cpu_to_le16(flags); + + skb->dev = hwsim_mon; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + +static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, + const u8 *addr) +{ + struct sk_buff *skb; + struct hwsim_radiotap_ack_hdr *hdr; + u16 flags; + struct ieee80211_hdr *hdr11; + + if (!netif_running(hwsim_mon)) + return; + + skb = dev_alloc_skb(100); + if (skb == NULL) + return; + + hdr = skb_put(skb, sizeof(*hdr)); + hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; + hdr->hdr.it_pad = 0; + hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); + hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL)); + hdr->rt_flags = 0; + hdr->pad = 0; + hdr->rt_channel = cpu_to_le16(chan->center_freq); + flags = IEEE80211_CHAN_2GHZ; + hdr->rt_chbitmask = cpu_to_le16(flags); + + hdr11 = skb_put(skb, 10); + hdr11->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_ACK); + hdr11->duration_id = cpu_to_le16(0); + memcpy(hdr11->addr1, addr, ETH_ALEN); + + skb->dev = hwsim_mon; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + +struct mac80211_hwsim_addr_match_data { + u8 addr[ETH_ALEN]; + bool ret; +}; + +static void mac80211_hwsim_addr_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + int i; + struct mac80211_hwsim_addr_match_data *md = data; + + if (memcmp(mac, md->addr, ETH_ALEN) == 0) { + md->ret = true; + return; + } + + /* Match the link address */ + for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { + struct ieee80211_bss_conf *conf; + + conf = rcu_dereference(vif->link_conf[i]); + if (!conf) + continue; + + if (memcmp(conf->addr, md->addr, ETH_ALEN) == 0) { + md->ret = true; + return; + } + } +} + +static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, + const u8 *addr) +{ + struct mac80211_hwsim_addr_match_data md = { + .ret = false, + }; + + if (data->scanning && memcmp(addr, data->scan_addr, ETH_ALEN) == 0) + return true; + + memcpy(md.addr, addr, ETH_ALEN); + + ieee80211_iterate_active_interfaces_atomic(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_addr_iter, + &md); + + return md.ret; +} + +static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data, + struct sk_buff *skb) +{ + switch (data->ps) { + case PS_DISABLED: + return true; + case PS_ENABLED: + return false; + case PS_AUTO_POLL: + /* TODO: accept (some) Beacons by default and other frames only + * if pending PS-Poll has been sent */ + return true; + case PS_MANUAL_POLL: + /* Allow unicast frames to own address if there is a pending + * PS-Poll */ + if (data->ps_poll_pending && + mac80211_hwsim_addr_match(data, skb->data + 4)) { + data->ps_poll_pending = false; + return true; + } + return false; + } + + return true; +} + +static int hwsim_unicast_netgroup(struct mac80211_hwsim_data *data, + struct sk_buff *skb, int portid) +{ + struct net *net; + bool found = false; + int res = -ENOENT; + + rcu_read_lock(); + for_each_net_rcu(net) { + if (data->netgroup == hwsim_net_get_netgroup(net)) { + res = genlmsg_unicast(net, skb, portid); + found = true; + break; + } + } + rcu_read_unlock(); + + if (!found) + nlmsg_free(skb); + + return res; +} + +static void mac80211_hwsim_config_mac_nl(struct ieee80211_hw *hw, + const u8 *addr, bool add) +{ + struct mac80211_hwsim_data *data = hw->priv; + u32 _portid = READ_ONCE(data->wmediumd); + struct sk_buff *skb; + void *msg_head; + + WARN_ON(!is_valid_ether_addr(addr)); + + if (!_portid && !hwsim_virtio_enabled) + return; + + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!skb) + return; + + msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, + add ? HWSIM_CMD_ADD_MAC_ADDR : + HWSIM_CMD_DEL_MAC_ADDR); + if (!msg_head) { + pr_debug("mac80211_hwsim: problem with msg_head\n"); + goto nla_put_failure; + } + + if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, + ETH_ALEN, data->addresses[1].addr)) + goto nla_put_failure; + + if (nla_put(skb, HWSIM_ATTR_ADDR_RECEIVER, ETH_ALEN, addr)) + goto nla_put_failure; + + genlmsg_end(skb, msg_head); + + if (hwsim_virtio_enabled) + hwsim_tx_virtio(data, skb); + else + hwsim_unicast_netgroup(data, skb, _portid); + return; +nla_put_failure: + nlmsg_free(skb); +} + +static inline u16 trans_tx_rate_flags_ieee2hwsim(struct ieee80211_tx_rate *rate) +{ + u16 result = 0; + + if (rate->flags & IEEE80211_TX_RC_USE_RTS_CTS) + result |= MAC80211_HWSIM_TX_RC_USE_RTS_CTS; + if (rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + result |= MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + result |= MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE; + if (rate->flags & IEEE80211_TX_RC_MCS) + result |= MAC80211_HWSIM_TX_RC_MCS; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + result |= MAC80211_HWSIM_TX_RC_GREEN_FIELD; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + result |= MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH; + if (rate->flags & IEEE80211_TX_RC_DUP_DATA) + result |= MAC80211_HWSIM_TX_RC_DUP_DATA; + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + result |= MAC80211_HWSIM_TX_RC_SHORT_GI; + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) + result |= MAC80211_HWSIM_TX_RC_VHT_MCS; + if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + result |= MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH; + if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + result |= MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH; + + return result; +} + +static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, + struct sk_buff *my_skb, + int dst_portid, + struct ieee80211_channel *channel) +{ + struct sk_buff *skb; + struct mac80211_hwsim_data *data = hw->priv; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb); + void *msg_head; + unsigned int hwsim_flags = 0; + int i; + struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES]; + struct hwsim_tx_rate_flag tx_attempts_flags[IEEE80211_TX_MAX_RATES]; + uintptr_t cookie; + + if (data->ps != PS_DISABLED) + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + /* If the queue contains MAX_QUEUE skb's drop some */ + if (skb_queue_len(&data->pending) >= MAX_QUEUE) { + /* Dropping until WARN_QUEUE level */ + while (skb_queue_len(&data->pending) >= WARN_QUEUE) { + ieee80211_free_txskb(hw, skb_dequeue(&data->pending)); + data->tx_dropped++; + } + } + + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (skb == NULL) + goto nla_put_failure; + + msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, + HWSIM_CMD_FRAME); + if (msg_head == NULL) { + pr_debug("mac80211_hwsim: problem with msg_head\n"); + goto nla_put_failure; + } + + if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, + ETH_ALEN, data->addresses[1].addr)) + goto nla_put_failure; + + /* We get the skb->data */ + if (nla_put(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data)) + goto nla_put_failure; + + /* We get the flags for this transmission, and we translate them to + wmediumd flags */ + + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) + hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS; + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + hwsim_flags |= HWSIM_TX_CTL_NO_ACK; + + if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags)) + goto nla_put_failure; + + if (nla_put_u32(skb, HWSIM_ATTR_FREQ, channel->center_freq)) + goto nla_put_failure; + + /* We get the tx control (rate and retries) info*/ + + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + tx_attempts[i].idx = info->status.rates[i].idx; + tx_attempts_flags[i].idx = info->status.rates[i].idx; + tx_attempts[i].count = info->status.rates[i].count; + tx_attempts_flags[i].flags = + trans_tx_rate_flags_ieee2hwsim( + &info->status.rates[i]); + } + + if (nla_put(skb, HWSIM_ATTR_TX_INFO, + sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES, + tx_attempts)) + goto nla_put_failure; + + if (nla_put(skb, HWSIM_ATTR_TX_INFO_FLAGS, + sizeof(struct hwsim_tx_rate_flag) * IEEE80211_TX_MAX_RATES, + tx_attempts_flags)) + goto nla_put_failure; + + /* We create a cookie to identify this skb */ + cookie = atomic_inc_return(&data->pending_cookie); + info->rate_driver_data[0] = (void *)cookie; + if (nla_put_u64_64bit(skb, HWSIM_ATTR_COOKIE, cookie, HWSIM_ATTR_PAD)) + goto nla_put_failure; + + genlmsg_end(skb, msg_head); + + if (hwsim_virtio_enabled) { + if (hwsim_tx_virtio(data, skb)) + goto err_free_txskb; + } else { + if (hwsim_unicast_netgroup(data, skb, dst_portid)) + goto err_free_txskb; + } + + /* Enqueue the packet */ + skb_queue_tail(&data->pending, my_skb); + data->tx_pkts++; + data->tx_bytes += my_skb->len; + return; + +nla_put_failure: + nlmsg_free(skb); +err_free_txskb: + pr_debug("mac80211_hwsim: error occurred in %s\n", __func__); + ieee80211_free_txskb(hw, my_skb); + data->tx_failed++; +} + +static bool hwsim_chans_compat(struct ieee80211_channel *c1, + struct ieee80211_channel *c2) +{ + if (!c1 || !c2) + return false; + + return c1->center_freq == c2->center_freq; +} + +struct tx_iter_data { + struct ieee80211_channel *channel; + bool receive; +}; + +static void mac80211_hwsim_tx_iter(void *_data, u8 *addr, + struct ieee80211_vif *vif) +{ + struct tx_iter_data *data = _data; + int i; + + for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { + struct ieee80211_bss_conf *conf; + struct ieee80211_chanctx_conf *chanctx; + + conf = rcu_dereference(vif->link_conf[i]); + if (!conf) + continue; + + chanctx = rcu_dereference(conf->chanctx_conf); + if (!chanctx) + continue; + + if (!hwsim_chans_compat(data->channel, chanctx->def.chan)) + continue; + + data->receive = true; + return; + } +} + +static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb) +{ + /* + * To enable this code, #define the HWSIM_RADIOTAP_OUI, + * e.g. like this: + * #define HWSIM_RADIOTAP_OUI "\x02\x00\x00" + * (but you should use a valid OUI, not that) + * + * If anyone wants to 'donate' a radiotap OUI/subns code + * please send a patch removing this #ifdef and changing + * the values accordingly. + */ +#ifdef HWSIM_RADIOTAP_OUI + struct ieee80211_radiotap_vendor_tlv *rtap; + static const char vendor_data[8] = "ABCDEFGH"; + + // Make sure no padding is needed + BUILD_BUG_ON(sizeof(vendor_data) % 4); + /* this is last radiotap info before the mac header, so + * skb_reset_mac_header for mac8022 to know the end of + * the radiotap TLV/beginning of the 802.11 header + */ + skb_reset_mac_header(skb); + + /* + * Note that this code requires the headroom in the SKB + * that was allocated earlier. + */ + rtap = skb_push(skb, sizeof(*rtap) + sizeof(vendor_data)); + + rtap->len = cpu_to_le16(sizeof(*rtap) - + sizeof(struct ieee80211_radiotap_tlv) + + sizeof(vendor_data)); + rtap->type = cpu_to_le16(IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + + rtap->content.oui[0] = HWSIM_RADIOTAP_OUI[0]; + rtap->content.oui[1] = HWSIM_RADIOTAP_OUI[1]; + rtap->content.oui[2] = HWSIM_RADIOTAP_OUI[2]; + rtap->content.oui_subtype = 127; + /* clear reserved field */ + rtap->content.reserved = 0; + rtap->content.vendor_type = 0; + memcpy(rtap->content.data, vendor_data, sizeof(vendor_data)); + + IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; +#endif +} + +static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data, + struct ieee80211_rx_status *rx_status, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + + if (!ieee80211_has_morefrags(hdr->frame_control) && + !is_multicast_ether_addr(hdr->addr1) && + (ieee80211_is_mgmt(hdr->frame_control) || + ieee80211_is_data(hdr->frame_control))) { + struct ieee80211_sta *sta; + unsigned int link_id; + + rcu_read_lock(); + sta = ieee80211_find_sta_by_link_addrs(data->hw, hdr->addr2, + hdr->addr1, &link_id); + if (sta) { + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + + if (ieee80211_has_pm(hdr->frame_control)) + sp->active_links_rx &= ~BIT(link_id); + else + sp->active_links_rx |= BIT(link_id); + } + rcu_read_unlock(); + } + + memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status)); + + mac80211_hwsim_add_vendor_rtap(skb); + + data->rx_pkts++; + data->rx_bytes += skb->len; + ieee80211_rx_irqsafe(data->hw, skb); +} + +static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan) +{ + struct mac80211_hwsim_data *data = hw->priv, *data2; + bool ack = false; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_rx_status rx_status; + u64 now; + + memset(&rx_status, 0, sizeof(rx_status)); + rx_status.flag |= RX_FLAG_MACTIME_START; + rx_status.freq = chan->center_freq; + rx_status.freq_offset = chan->freq_offset ? 1 : 0; + rx_status.band = chan->band; + if (info->control.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) { + rx_status.rate_idx = + ieee80211_rate_get_vht_mcs(&info->control.rates[0]); + rx_status.nss = + ieee80211_rate_get_vht_nss(&info->control.rates[0]); + rx_status.encoding = RX_ENC_VHT; + } else { + rx_status.rate_idx = info->control.rates[0].idx; + if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) + rx_status.encoding = RX_ENC_HT; + } + if (info->control.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + rx_status.bw = RATE_INFO_BW_40; + else if (info->control.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + rx_status.bw = RATE_INFO_BW_80; + else if (info->control.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + rx_status.bw = RATE_INFO_BW_160; + else + rx_status.bw = RATE_INFO_BW_20; + if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) + rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI; + /* TODO: simulate optional packet loss */ + rx_status.signal = data->rx_rssi; + if (info->control.vif) + rx_status.signal += info->control.vif->bss_conf.txpower; + + if (data->ps != PS_DISABLED) + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + + /* release the skb's source info */ + skb_orphan(skb); + skb_dst_drop(skb); + skb->mark = 0; + skb_ext_reset(skb); + nf_reset_ct(skb); + + /* + * Get absolute mactime here so all HWs RX at the "same time", and + * absolute TX time for beacon mactime so the timestamp matches. + * Giving beacons a different mactime than non-beacons looks messy, but + * it helps the Toffset be exact and a ~10us mactime discrepancy + * probably doesn't really matter. + */ + if (ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) { + rx_status.boottime_ns = ktime_get_boottime_ns(); + now = data->abs_bcn_ts; + } else { + now = mac80211_hwsim_get_tsf_raw(); + } + + /* Copy skb to all enabled radios that are on the current frequency */ + spin_lock(&hwsim_radio_lock); + list_for_each_entry(data2, &hwsim_radios, list) { + struct sk_buff *nskb; + struct tx_iter_data tx_iter_data = { + .receive = false, + .channel = chan, + }; + + if (data == data2) + continue; + + if (!data2->started || (data2->idle && !data2->tmp_chan) || + !hwsim_ps_rx_ok(data2, skb)) + continue; + + if (!(data->group & data2->group)) + continue; + + if (data->netgroup != data2->netgroup) + continue; + + if (!hwsim_chans_compat(chan, data2->tmp_chan) && + !hwsim_chans_compat(chan, data2->channel)) { + ieee80211_iterate_active_interfaces_atomic( + data2->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_tx_iter, &tx_iter_data); + if (!tx_iter_data.receive) + continue; + } + + /* + * reserve some space for our vendor and the normal + * radiotap header, since we're copying anyway + */ + if (skb->len < PAGE_SIZE && paged_rx) { + struct page *page = alloc_page(GFP_ATOMIC); + + if (!page) + continue; + + nskb = dev_alloc_skb(128); + if (!nskb) { + __free_page(page); + continue; + } + + memcpy(page_address(page), skb->data, skb->len); + skb_add_rx_frag(nskb, 0, page, 0, skb->len, skb->len); + } else { + nskb = skb_copy(skb, GFP_ATOMIC); + if (!nskb) + continue; + } + + if (mac80211_hwsim_addr_match(data2, hdr->addr1)) + ack = true; + + rx_status.mactime = now + data2->tsf_offset; + + mac80211_hwsim_rx(data2, &rx_status, nskb); + } + spin_unlock(&hwsim_radio_lock); + + return ack; +} + +static struct ieee80211_bss_conf * +mac80211_hwsim_select_tx_link(struct mac80211_hwsim_data *data, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + struct ieee80211_link_sta **link_sta) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + int i; + + if (!vif->valid_links) + return &vif->bss_conf; + + WARN_ON(is_multicast_ether_addr(hdr->addr1)); + + if (WARN_ON_ONCE(!sta->valid_links)) + return &vif->bss_conf; + + for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) { + struct ieee80211_bss_conf *bss_conf; + unsigned int link_id; + + /* round-robin the available link IDs */ + link_id = (sp->last_link + i + 1) % ARRAY_SIZE(vif->link_conf); + + if (!(vif->active_links & BIT(link_id))) + continue; + + if (!(sp->active_links_rx & BIT(link_id))) + continue; + + *link_sta = rcu_dereference(sta->link[link_id]); + if (!*link_sta) + continue; + + bss_conf = rcu_dereference(vif->link_conf[link_id]); + if (WARN_ON_ONCE(!bss_conf)) + continue; + + /* can happen while switching links */ + if (!rcu_access_pointer(bss_conf->chanctx_conf)) + continue; + + sp->last_link = link_id; + return bss_conf; + } + + return NULL; +} + +static void mac80211_hwsim_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct mac80211_hwsim_data *data = hw->priv; + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *channel; + bool ack; + enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; + u32 _portid, i; + + if (WARN_ON(skb->len < 10)) { + /* Should not happen; just a sanity check for addr1 use */ + ieee80211_free_txskb(hw, skb); + return; + } + + if (!data->use_chanctx) { + channel = data->channel; + confbw = data->bw; + } else if (txi->hw_queue == 4) { + channel = data->tmp_chan; + } else { + u8 link = u32_get_bits(IEEE80211_SKB_CB(skb)->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + struct ieee80211_vif *vif = txi->control.vif; + struct ieee80211_link_sta *link_sta = NULL; + struct ieee80211_sta *sta = control->sta; + struct ieee80211_bss_conf *bss_conf; + + if (link != IEEE80211_LINK_UNSPECIFIED) { + bss_conf = rcu_dereference(txi->control.vif->link_conf[link]); + if (sta) + link_sta = rcu_dereference(sta->link[link]); + } else { + bss_conf = mac80211_hwsim_select_tx_link(data, vif, sta, + hdr, &link_sta); + } + + if (WARN_ON(!bss_conf)) { + ieee80211_free_txskb(hw, skb); + return; + } + + if (sta && sta->mlo) { + if (WARN_ON(!link_sta)) { + ieee80211_free_txskb(hw, skb); + return; + } + /* address translation to link addresses on TX */ + ether_addr_copy(hdr->addr1, link_sta->addr); + ether_addr_copy(hdr->addr2, bss_conf->addr); + /* translate A3 only if it's the BSSID */ + if (!ieee80211_has_tods(hdr->frame_control) && + !ieee80211_has_fromds(hdr->frame_control)) { + if (ether_addr_equal(hdr->addr3, sta->addr)) + ether_addr_copy(hdr->addr3, link_sta->addr); + else if (ether_addr_equal(hdr->addr3, vif->addr)) + ether_addr_copy(hdr->addr3, bss_conf->addr); + } + /* no need to look at A4, if present it's SA */ + } + + chanctx_conf = rcu_dereference(bss_conf->chanctx_conf); + if (chanctx_conf) { + channel = chanctx_conf->def.chan; + confbw = chanctx_conf->def.width; + } else { + channel = NULL; + } + } + + if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) { + ieee80211_free_txskb(hw, skb); + return; + } + + if (data->idle && !data->tmp_chan) { + wiphy_dbg(hw->wiphy, "Trying to TX when idle - reject\n"); + ieee80211_free_txskb(hw, skb); + return; + } + + if (txi->control.vif) + hwsim_check_magic(txi->control.vif); + if (control->sta) + hwsim_check_sta_magic(control->sta); + + if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) + ieee80211_get_tx_rates(txi->control.vif, control->sta, skb, + txi->control.rates, + ARRAY_SIZE(txi->control.rates)); + + for (i = 0; i < ARRAY_SIZE(txi->control.rates); i++) { + u16 rflags = txi->control.rates[i].flags; + /* initialize to data->bw for 5/10 MHz handling */ + enum nl80211_chan_width bw = data->bw; + + if (txi->control.rates[i].idx == -1) + break; + + if (rflags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = NL80211_CHAN_WIDTH_40; + else if (rflags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = NL80211_CHAN_WIDTH_80; + else if (rflags & IEEE80211_TX_RC_160_MHZ_WIDTH) + bw = NL80211_CHAN_WIDTH_160; + + if (WARN_ON(hwsim_get_chanwidth(bw) > hwsim_get_chanwidth(confbw))) + return; + } + + if (skb->len >= 24 + 8 && + ieee80211_is_probe_resp(hdr->frame_control)) { + /* fake header transmission time */ + struct ieee80211_mgmt *mgmt; + struct ieee80211_rate *txrate; + /* TODO: get MCS */ + int bitrate = 100; + u64 ts; + + mgmt = (struct ieee80211_mgmt *)skb->data; + txrate = ieee80211_get_tx_rate(hw, txi); + if (txrate) + bitrate = txrate->bitrate; + ts = mac80211_hwsim_get_tsf_raw(); + mgmt->u.probe_resp.timestamp = + cpu_to_le64(ts + data->tsf_offset + + 24 * 8 * 10 / bitrate); + } + + mac80211_hwsim_monitor_rx(hw, skb, channel); + + /* wmediumd mode check */ + _portid = READ_ONCE(data->wmediumd); + + if (_portid || hwsim_virtio_enabled) + return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, channel); + + /* NO wmediumd detected, perfect medium simulation */ + data->tx_pkts++; + data->tx_bytes += skb->len; + ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel); + + if (ack && skb->len >= 16) + mac80211_hwsim_monitor_ack(channel, hdr->addr2); + + ieee80211_tx_info_clear_status(txi); + + /* frame was transmitted at most favorable rate at first attempt */ + txi->control.rates[0].count = 1; + txi->control.rates[1].idx = -1; + + if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack) + txi->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(hw, skb); +} + + +static int mac80211_hwsim_start(struct ieee80211_hw *hw) +{ + struct mac80211_hwsim_data *data = hw->priv; + wiphy_dbg(hw->wiphy, "%s\n", __func__); + data->started = true; + return 0; +} + + +static void mac80211_hwsim_stop(struct ieee80211_hw *hw) +{ + struct mac80211_hwsim_data *data = hw->priv; + int i; + + data->started = false; + + for (i = 0; i < ARRAY_SIZE(data->link_data); i++) + hrtimer_cancel(&data->link_data[i].beacon_timer); + + while (!skb_queue_empty(&data->pending)) + ieee80211_free_txskb(hw, skb_dequeue(&data->pending)); + + wiphy_dbg(hw->wiphy, "%s\n", __func__); +} + + +static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + wiphy_dbg(hw->wiphy, "%s (type=%d mac_addr=%pM)\n", + __func__, ieee80211_vif_type_p2p(vif), + vif->addr); + hwsim_set_magic(vif); + + if (vif->type != NL80211_IFTYPE_MONITOR) + mac80211_hwsim_config_mac_nl(hw, vif->addr, true); + + vif->cab_queue = 0; + vif->hw_queue[IEEE80211_AC_VO] = 0; + vif->hw_queue[IEEE80211_AC_VI] = 1; + vif->hw_queue[IEEE80211_AC_BE] = 2; + vif->hw_queue[IEEE80211_AC_BK] = 3; + + return 0; +} + + +static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype newtype, + bool newp2p) +{ + newtype = ieee80211_iftype_p2p(newtype, newp2p); + wiphy_dbg(hw->wiphy, + "%s (old type=%d, new type=%d, mac_addr=%pM)\n", + __func__, ieee80211_vif_type_p2p(vif), + newtype, vif->addr); + hwsim_check_magic(vif); + + /* + * interface may change from non-AP to AP in + * which case this needs to be set up again + */ + vif->cab_queue = 0; + + return 0; +} + +static void mac80211_hwsim_remove_interface( + struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + wiphy_dbg(hw->wiphy, "%s (type=%d mac_addr=%pM)\n", + __func__, ieee80211_vif_type_p2p(vif), + vif->addr); + hwsim_check_magic(vif); + hwsim_clear_magic(vif); + if (vif->type != NL80211_IFTYPE_MONITOR) + mac80211_hwsim_config_mac_nl(hw, vif->addr, false); +} + +static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan) +{ + struct mac80211_hwsim_data *data = hw->priv; + u32 _portid = READ_ONCE(data->wmediumd); + + if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) { + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + ieee80211_get_tx_rates(txi->control.vif, NULL, skb, + txi->control.rates, + ARRAY_SIZE(txi->control.rates)); + } + + mac80211_hwsim_monitor_rx(hw, skb, chan); + + if (_portid || hwsim_virtio_enabled) + return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, chan); + + data->tx_pkts++; + data->tx_bytes += skb->len; + mac80211_hwsim_tx_frame_no_nl(hw, skb, chan); + dev_kfree_skb(skb); +} + +static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_link_data *link_data = arg; + u32 link_id = link_data->link_id; + struct ieee80211_bss_conf *link_conf; + struct mac80211_hwsim_data *data = + container_of(link_data, struct mac80211_hwsim_data, + link_data[link_id]); + struct ieee80211_hw *hw = data->hw; + struct ieee80211_tx_info *info; + struct ieee80211_rate *txrate; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + /* TODO: get MCS */ + int bitrate = 100; + + hwsim_check_magic(vif); + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (!link_conf) + return; + + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_ADHOC && + vif->type != NL80211_IFTYPE_OCB) + return; + + skb = ieee80211_beacon_get(hw, vif, link_data->link_id); + if (skb == NULL) + return; + info = IEEE80211_SKB_CB(skb); + if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE)) + ieee80211_get_tx_rates(vif, NULL, skb, + info->control.rates, + ARRAY_SIZE(info->control.rates)); + + txrate = ieee80211_get_tx_rate(hw, info); + if (txrate) + bitrate = txrate->bitrate; + + mgmt = (struct ieee80211_mgmt *) skb->data; + /* fake header transmission time */ + data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw(); + if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { + struct ieee80211_ext *ext = (void *) mgmt; + + ext->u.s1g_beacon.timestamp = cpu_to_le32(data->abs_bcn_ts + + data->tsf_offset + + 10 * 8 * 10 / + bitrate); + } else { + mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts + + data->tsf_offset + + 24 * 8 * 10 / + bitrate); + } + + mac80211_hwsim_tx_frame(hw, skb, + rcu_dereference(link_conf->chanctx_conf)->def.chan); + + while ((skb = ieee80211_get_buffered_bc(hw, vif)) != NULL) { + mac80211_hwsim_tx_frame(hw, skb, + rcu_dereference(link_conf->chanctx_conf)->def.chan); + } + + if (link_conf->csa_active && ieee80211_beacon_cntdwn_is_complete(vif)) + ieee80211_csa_finish(vif); +} + +static enum hrtimer_restart +mac80211_hwsim_beacon(struct hrtimer *timer) +{ + struct mac80211_hwsim_link_data *link_data = + container_of(timer, struct mac80211_hwsim_link_data, beacon_timer); + struct mac80211_hwsim_data *data = + container_of(link_data, struct mac80211_hwsim_data, + link_data[link_data->link_id]); + struct ieee80211_hw *hw = data->hw; + u64 bcn_int = link_data->beacon_int; + + if (!data->started) + return HRTIMER_NORESTART; + + ieee80211_iterate_active_interfaces_atomic( + hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_beacon_tx, link_data); + + /* beacon at new TBTT + beacon interval */ + if (data->bcn_delta) { + bcn_int -= data->bcn_delta; + data->bcn_delta = 0; + } + hrtimer_forward_now(&link_data->beacon_timer, + ns_to_ktime(bcn_int * NSEC_PER_USEC)); + return HRTIMER_RESTART; +} + +static const char * const hwsim_chanwidths[] = { + [NL80211_CHAN_WIDTH_5] = "ht5", + [NL80211_CHAN_WIDTH_10] = "ht10", + [NL80211_CHAN_WIDTH_20_NOHT] = "noht", + [NL80211_CHAN_WIDTH_20] = "ht20", + [NL80211_CHAN_WIDTH_40] = "ht40", + [NL80211_CHAN_WIDTH_80] = "vht80", + [NL80211_CHAN_WIDTH_80P80] = "vht80p80", + [NL80211_CHAN_WIDTH_160] = "vht160", + [NL80211_CHAN_WIDTH_1] = "1MHz", + [NL80211_CHAN_WIDTH_2] = "2MHz", + [NL80211_CHAN_WIDTH_4] = "4MHz", + [NL80211_CHAN_WIDTH_8] = "8MHz", + [NL80211_CHAN_WIDTH_16] = "16MHz", +}; + +static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) +{ + struct mac80211_hwsim_data *data = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { + [IEEE80211_SMPS_AUTOMATIC] = "auto", + [IEEE80211_SMPS_OFF] = "off", + [IEEE80211_SMPS_STATIC] = "static", + [IEEE80211_SMPS_DYNAMIC] = "dynamic", + }; + int idx; + + if (conf->chandef.chan) + wiphy_dbg(hw->wiphy, + "%s (freq=%d(%d - %d)/%s idle=%d ps=%d smps=%s)\n", + __func__, + conf->chandef.chan->center_freq, + conf->chandef.center_freq1, + conf->chandef.center_freq2, + hwsim_chanwidths[conf->chandef.width], + !!(conf->flags & IEEE80211_CONF_IDLE), + !!(conf->flags & IEEE80211_CONF_PS), + smps_modes[conf->smps_mode]); + else + wiphy_dbg(hw->wiphy, + "%s (freq=0 idle=%d ps=%d smps=%s)\n", + __func__, + !!(conf->flags & IEEE80211_CONF_IDLE), + !!(conf->flags & IEEE80211_CONF_PS), + smps_modes[conf->smps_mode]); + + data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); + + WARN_ON(conf->chandef.chan && data->use_chanctx); + + mutex_lock(&data->mutex); + if (data->scanning && conf->chandef.chan) { + for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { + if (data->survey_data[idx].channel == data->channel) { + data->survey_data[idx].start = + data->survey_data[idx].next_start; + data->survey_data[idx].end = jiffies; + break; + } + } + + data->channel = conf->chandef.chan; + data->bw = conf->chandef.width; + + for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { + if (data->survey_data[idx].channel && + data->survey_data[idx].channel != data->channel) + continue; + data->survey_data[idx].channel = data->channel; + data->survey_data[idx].next_start = jiffies; + break; + } + } else { + data->channel = conf->chandef.chan; + data->bw = conf->chandef.width; + } + mutex_unlock(&data->mutex); + + for (idx = 0; idx < ARRAY_SIZE(data->link_data); idx++) { + struct mac80211_hwsim_link_data *link_data = + &data->link_data[idx]; + + if (!data->started || !link_data->beacon_int) { + hrtimer_cancel(&link_data->beacon_timer); + } else if (!hrtimer_is_queued(&link_data->beacon_timer)) { + u64 tsf = mac80211_hwsim_get_tsf(hw, NULL); + u32 bcn_int = link_data->beacon_int; + u64 until_tbtt = bcn_int - do_div(tsf, bcn_int); + + hrtimer_start(&link_data->beacon_timer, + ns_to_ktime(until_tbtt * NSEC_PER_USEC), + HRTIMER_MODE_REL_SOFT); + } + } + + return 0; +} + + +static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags,u64 multicast) +{ + struct mac80211_hwsim_data *data = hw->priv; + + wiphy_dbg(hw->wiphy, "%s\n", __func__); + + data->rx_filter = 0; + if (*total_flags & FIF_ALLMULTI) + data->rx_filter |= FIF_ALLMULTI; + if (*total_flags & FIF_MCAST_ACTION) + data->rx_filter |= FIF_MCAST_ACTION; + + *total_flags = data->rx_filter; +} + +static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + unsigned int *count = data; + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + + if (vp->bcn_en) + (*count)++; +} + +static void mac80211_hwsim_vif_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u64 changed) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + + hwsim_check_magic(vif); + + wiphy_dbg(hw->wiphy, "%s(changed=0x%llx vif->addr=%pM)\n", + __func__, changed, vif->addr); + + if (changed & BSS_CHANGED_ASSOC) { + wiphy_dbg(hw->wiphy, " ASSOC: assoc=%d aid=%d\n", + vif->cfg.assoc, vif->cfg.aid); + vp->assoc = vif->cfg.assoc; + vp->aid = vif->cfg.aid; + } +} + +static void mac80211_hwsim_link_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct mac80211_hwsim_data *data = hw->priv; + unsigned int link_id = info->link_id; + struct mac80211_hwsim_link_data *link_data = &data->link_data[link_id]; + + hwsim_check_magic(vif); + + wiphy_dbg(hw->wiphy, "%s(changed=0x%llx vif->addr=%pM, link id %u)\n", + __func__, (unsigned long long)changed, vif->addr, link_id); + + if (changed & BSS_CHANGED_BSSID) { + wiphy_dbg(hw->wiphy, "%s: BSSID changed: %pM\n", + __func__, info->bssid); + memcpy(vp->bssid, info->bssid, ETH_ALEN); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + wiphy_dbg(hw->wiphy, " BCN EN: %d (BI=%u)\n", + info->enable_beacon, info->beacon_int); + vp->bcn_en = info->enable_beacon; + if (data->started && + !hrtimer_is_queued(&link_data->beacon_timer) && + info->enable_beacon) { + u64 tsf, until_tbtt; + u32 bcn_int; + link_data->beacon_int = info->beacon_int * 1024; + tsf = mac80211_hwsim_get_tsf(hw, vif); + bcn_int = link_data->beacon_int; + until_tbtt = bcn_int - do_div(tsf, bcn_int); + + hrtimer_start(&link_data->beacon_timer, + ns_to_ktime(until_tbtt * NSEC_PER_USEC), + HRTIMER_MODE_REL_SOFT); + } else if (!info->enable_beacon) { + unsigned int count = 0; + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_bcn_en_iter, &count); + wiphy_dbg(hw->wiphy, " beaconing vifs remaining: %u", + count); + if (count == 0) { + hrtimer_cancel(&link_data->beacon_timer); + link_data->beacon_int = 0; + } + } + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + wiphy_dbg(hw->wiphy, " ERP_CTS_PROT: %d\n", + info->use_cts_prot); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + wiphy_dbg(hw->wiphy, " ERP_PREAMBLE: %d\n", + info->use_short_preamble); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + wiphy_dbg(hw->wiphy, " ERP_SLOT: %d\n", info->use_short_slot); + } + + if (changed & BSS_CHANGED_HT) { + wiphy_dbg(hw->wiphy, " HT: op_mode=0x%x\n", + info->ht_operation_mode); + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + wiphy_dbg(hw->wiphy, " BASIC_RATES: 0x%llx\n", + (unsigned long long) info->basic_rates); + } + + if (changed & BSS_CHANGED_TXPOWER) + wiphy_dbg(hw->wiphy, " TX Power: %d dBm\n", info->txpower); +} + +static void +mac80211_hwsim_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct mac80211_hwsim_data *data = hw->priv; + u32 bw = U32_MAX; + int link_id; + + rcu_read_lock(); + for (link_id = 0; + link_id < ARRAY_SIZE(vif->link_conf); + link_id++) { + enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT; + struct ieee80211_bss_conf *vif_conf; + struct ieee80211_link_sta *link_sta; + + link_sta = rcu_dereference(sta->link[link_id]); + + if (!link_sta) + continue; + + switch (link_sta->bandwidth) { +#define C(_bw) case IEEE80211_STA_RX_BW_##_bw: bw = _bw; break + C(20); + C(40); + C(80); + C(160); + C(320); +#undef C + } + + if (!data->use_chanctx) { + confbw = data->bw; + } else { + struct ieee80211_chanctx_conf *chanctx_conf; + + vif_conf = rcu_dereference(vif->link_conf[link_id]); + if (WARN_ON(!vif_conf)) + continue; + + chanctx_conf = rcu_dereference(vif_conf->chanctx_conf); + + if (!WARN_ON(!chanctx_conf)) + confbw = chanctx_conf->def.width; + } + + WARN(bw > hwsim_get_chanwidth(confbw), + "intf %pM [link=%d]: bad STA %pM bandwidth %d MHz (%d) > channel config %d MHz (%d)\n", + vif->addr, link_id, sta->addr, bw, sta->deflink.bandwidth, + hwsim_get_chanwidth(data->bw), data->bw); + + + } + rcu_read_unlock(); + + +} + +static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + + hwsim_check_magic(vif); + hwsim_set_sta_magic(sta); + mac80211_hwsim_sta_rc_update(hw, vif, sta, 0); + + if (sta->valid_links) { + WARN(hweight16(sta->valid_links) > 1, + "expect to add STA with single link, have 0x%x\n", + sta->valid_links); + sp->active_links_rx = sta->valid_links; + } + + return 0; +} + +static int mac80211_hwsim_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + hwsim_check_magic(vif); + hwsim_clear_sta_magic(sta); + + return 0; +} + +static int mac80211_hwsim_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + if (new_state == IEEE80211_STA_NOTEXIST) + return mac80211_hwsim_sta_remove(hw, vif, sta); + + if (old_state == IEEE80211_STA_NOTEXIST) + return mac80211_hwsim_sta_add(hw, vif, sta); + + /* + * when client is authorized (AP station marked as such), + * enable all links + */ + if (vif->type == NL80211_IFTYPE_STATION && + new_state == IEEE80211_STA_AUTHORIZED && !sta->tdls) + ieee80211_set_active_links_async(vif, vif->valid_links); + + return 0; +} + +static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + hwsim_check_magic(vif); + + switch (cmd) { + case STA_NOTIFY_SLEEP: + case STA_NOTIFY_AWAKE: + /* TODO: make good use of these flags */ + break; + default: + WARN(1, "Invalid sta notify: %d\n", cmd); + break; + } +} + +static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + bool set) +{ + hwsim_check_sta_magic(sta); + return 0; +} + +static int mac80211_hwsim_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + wiphy_dbg(hw->wiphy, + "%s (queue=%d txop=%d cw_min=%d cw_max=%d aifs=%d)\n", + __func__, queue, + params->txop, params->cw_min, + params->cw_max, params->aifs); + return 0; +} + +static int mac80211_hwsim_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + if (idx < 0 || idx >= ARRAY_SIZE(hwsim->survey_data)) + return -ENOENT; + + mutex_lock(&hwsim->mutex); + survey->channel = hwsim->survey_data[idx].channel; + if (!survey->channel) { + mutex_unlock(&hwsim->mutex); + return -ENOENT; + } + + /* + * Magically conjured dummy values --- this is only ok for simulated hardware. + * + * A real driver which cannot determine real values noise MUST NOT + * report any, especially not a magically conjured ones :-) + */ + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->noise = -92; + survey->time = + jiffies_to_msecs(hwsim->survey_data[idx].end - + hwsim->survey_data[idx].start); + /* report 12.5% of channel time is used */ + survey->time_busy = survey->time/8; + mutex_unlock(&hwsim->mutex); + + return 0; +} + +#ifdef CONFIG_NL80211_TESTMODE +/* + * This section contains example code for using netlink + * attributes with the testmode command in nl80211. + */ + +/* These enums need to be kept in sync with userspace */ +enum hwsim_testmode_attr { + __HWSIM_TM_ATTR_INVALID = 0, + HWSIM_TM_ATTR_CMD = 1, + HWSIM_TM_ATTR_PS = 2, + + /* keep last */ + __HWSIM_TM_ATTR_AFTER_LAST, + HWSIM_TM_ATTR_MAX = __HWSIM_TM_ATTR_AFTER_LAST - 1 +}; + +enum hwsim_testmode_cmd { + HWSIM_TM_CMD_SET_PS = 0, + HWSIM_TM_CMD_GET_PS = 1, + HWSIM_TM_CMD_STOP_QUEUES = 2, + HWSIM_TM_CMD_WAKE_QUEUES = 3, +}; + +static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { + [HWSIM_TM_ATTR_CMD] = { .type = NLA_U32 }, + [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, +}; + +static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + struct nlattr *tb[HWSIM_TM_ATTR_MAX + 1]; + struct sk_buff *skb; + int err, ps; + + err = nla_parse_deprecated(tb, HWSIM_TM_ATTR_MAX, data, len, + hwsim_testmode_policy, NULL); + if (err) + return err; + + if (!tb[HWSIM_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[HWSIM_TM_ATTR_CMD])) { + case HWSIM_TM_CMD_SET_PS: + if (!tb[HWSIM_TM_ATTR_PS]) + return -EINVAL; + ps = nla_get_u32(tb[HWSIM_TM_ATTR_PS]); + return hwsim_fops_ps_write(hwsim, ps); + case HWSIM_TM_CMD_GET_PS: + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, + nla_total_size(sizeof(u32))); + if (!skb) + return -ENOMEM; + if (nla_put_u32(skb, HWSIM_TM_ATTR_PS, hwsim->ps)) + goto nla_put_failure; + return cfg80211_testmode_reply(skb); + case HWSIM_TM_CMD_STOP_QUEUES: + ieee80211_stop_queues(hw); + return 0; + case HWSIM_TM_CMD_WAKE_QUEUES: + ieee80211_wake_queues(hw); + return 0; + default: + return -EOPNOTSUPP; + } + + nla_put_failure: + kfree_skb(skb); + return -ENOBUFS; +} +#endif + +static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + + switch (action) { + case IEEE80211_AMPDU_TX_START: + return IEEE80211_AMPDU_TX_START_IMMEDIATE; + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + break; + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static void mac80211_hwsim_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + /* Not implemented, queues only on kernel side */ +} + +static void hw_scan_work(struct work_struct *work) +{ + struct mac80211_hwsim_data *hwsim = + container_of(work, struct mac80211_hwsim_data, hw_scan.work); + struct cfg80211_scan_request *req = hwsim->hw_scan_request; + int dwell, i; + + mutex_lock(&hwsim->mutex); + if (hwsim->scan_chan_idx >= req->n_channels) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + + wiphy_dbg(hwsim->hw->wiphy, "hw scan complete\n"); + ieee80211_scan_completed(hwsim->hw, &info); + hwsim->hw_scan_request = NULL; + hwsim->hw_scan_vif = NULL; + hwsim->tmp_chan = NULL; + mutex_unlock(&hwsim->mutex); + mac80211_hwsim_config_mac_nl(hwsim->hw, hwsim->scan_addr, + false); + return; + } + + wiphy_dbg(hwsim->hw->wiphy, "hw scan %d MHz\n", + req->channels[hwsim->scan_chan_idx]->center_freq); + + hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx]; + if (hwsim->tmp_chan->flags & (IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_RADAR) || + !req->n_ssids) { + dwell = 120; + } else { + dwell = 30; + /* send probes */ + for (i = 0; i < req->n_ssids; i++) { + struct sk_buff *probe; + struct ieee80211_mgmt *mgmt; + + probe = ieee80211_probereq_get(hwsim->hw, + hwsim->scan_addr, + req->ssids[i].ssid, + req->ssids[i].ssid_len, + req->ie_len); + if (!probe) + continue; + + mgmt = (struct ieee80211_mgmt *) probe->data; + memcpy(mgmt->da, req->bssid, ETH_ALEN); + memcpy(mgmt->bssid, req->bssid, ETH_ALEN); + + if (req->ie_len) + skb_put_data(probe, req->ie, req->ie_len); + + rcu_read_lock(); + if (!ieee80211_tx_prepare_skb(hwsim->hw, + hwsim->hw_scan_vif, + probe, + hwsim->tmp_chan->band, + NULL)) { + rcu_read_unlock(); + kfree_skb(probe); + continue; + } + + local_bh_disable(); + mac80211_hwsim_tx_frame(hwsim->hw, probe, + hwsim->tmp_chan); + rcu_read_unlock(); + local_bh_enable(); + } + } + ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, + msecs_to_jiffies(dwell)); + hwsim->survey_data[hwsim->scan_chan_idx].channel = hwsim->tmp_chan; + hwsim->survey_data[hwsim->scan_chan_idx].start = jiffies; + hwsim->survey_data[hwsim->scan_chan_idx].end = + jiffies + msecs_to_jiffies(dwell); + hwsim->scan_chan_idx++; + mutex_unlock(&hwsim->mutex); +} + +static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + struct cfg80211_scan_request *req = &hw_req->req; + + mutex_lock(&hwsim->mutex); + if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { + mutex_unlock(&hwsim->mutex); + return -EBUSY; + } + hwsim->hw_scan_request = req; + hwsim->hw_scan_vif = vif; + hwsim->scan_chan_idx = 0; + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) + get_random_mask_addr(hwsim->scan_addr, + hw_req->req.mac_addr, + hw_req->req.mac_addr_mask); + else + memcpy(hwsim->scan_addr, vif->addr, ETH_ALEN); + memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); + mutex_unlock(&hwsim->mutex); + + mac80211_hwsim_config_mac_nl(hw, hwsim->scan_addr, true); + wiphy_dbg(hw->wiphy, "hwsim hw_scan request\n"); + + ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, 0); + + return 0; +} + +static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + struct cfg80211_scan_info info = { + .aborted = true, + }; + + wiphy_dbg(hw->wiphy, "hwsim cancel_hw_scan\n"); + + cancel_delayed_work_sync(&hwsim->hw_scan); + + mutex_lock(&hwsim->mutex); + ieee80211_scan_completed(hwsim->hw, &info); + hwsim->tmp_chan = NULL; + hwsim->hw_scan_request = NULL; + hwsim->hw_scan_vif = NULL; + mutex_unlock(&hwsim->mutex); +} + +static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + mutex_lock(&hwsim->mutex); + + if (hwsim->scanning) { + pr_debug("two hwsim sw_scans detected!\n"); + goto out; + } + + pr_debug("hwsim sw_scan request, prepping stuff\n"); + + memcpy(hwsim->scan_addr, mac_addr, ETH_ALEN); + mac80211_hwsim_config_mac_nl(hw, hwsim->scan_addr, true); + hwsim->scanning = true; + memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); + +out: + mutex_unlock(&hwsim->mutex); +} + +static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + mutex_lock(&hwsim->mutex); + + pr_debug("hwsim sw_scan_complete\n"); + hwsim->scanning = false; + mac80211_hwsim_config_mac_nl(hw, hwsim->scan_addr, false); + eth_zero_addr(hwsim->scan_addr); + + mutex_unlock(&hwsim->mutex); +} + +static void hw_roc_start(struct work_struct *work) +{ + struct mac80211_hwsim_data *hwsim = + container_of(work, struct mac80211_hwsim_data, roc_start.work); + + mutex_lock(&hwsim->mutex); + + wiphy_dbg(hwsim->hw->wiphy, "hwsim ROC begins\n"); + hwsim->tmp_chan = hwsim->roc_chan; + ieee80211_ready_on_channel(hwsim->hw); + + ieee80211_queue_delayed_work(hwsim->hw, &hwsim->roc_done, + msecs_to_jiffies(hwsim->roc_duration)); + + mutex_unlock(&hwsim->mutex); +} + +static void hw_roc_done(struct work_struct *work) +{ + struct mac80211_hwsim_data *hwsim = + container_of(work, struct mac80211_hwsim_data, roc_done.work); + + mutex_lock(&hwsim->mutex); + ieee80211_remain_on_channel_expired(hwsim->hw); + hwsim->tmp_chan = NULL; + mutex_unlock(&hwsim->mutex); + + wiphy_dbg(hwsim->hw->wiphy, "hwsim ROC expired\n"); +} + +static int mac80211_hwsim_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + mutex_lock(&hwsim->mutex); + if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { + mutex_unlock(&hwsim->mutex); + return -EBUSY; + } + + hwsim->roc_chan = chan; + hwsim->roc_duration = duration; + mutex_unlock(&hwsim->mutex); + + wiphy_dbg(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n", + chan->center_freq, duration); + ieee80211_queue_delayed_work(hw, &hwsim->roc_start, HZ/50); + + return 0; +} + +static int mac80211_hwsim_croc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + cancel_delayed_work_sync(&hwsim->roc_start); + cancel_delayed_work_sync(&hwsim->roc_done); + + mutex_lock(&hwsim->mutex); + hwsim->tmp_chan = NULL; + mutex_unlock(&hwsim->mutex); + + wiphy_dbg(hw->wiphy, "hwsim ROC canceled\n"); + + return 0; +} + +static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + hwsim_set_chanctx_magic(ctx); + wiphy_dbg(hw->wiphy, + "add channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", + ctx->def.chan->center_freq, ctx->def.width, + ctx->def.center_freq1, ctx->def.center_freq2); + return 0; +} + +static void mac80211_hwsim_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + wiphy_dbg(hw->wiphy, + "remove channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", + ctx->def.chan->center_freq, ctx->def.width, + ctx->def.center_freq1, ctx->def.center_freq2); + hwsim_check_chanctx_magic(ctx); + hwsim_clear_chanctx_magic(ctx); +} + +static void mac80211_hwsim_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + hwsim_check_chanctx_magic(ctx); + wiphy_dbg(hw->wiphy, + "change channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", + ctx->def.chan->center_freq, ctx->def.width, + ctx->def.center_freq1, ctx->def.center_freq2); +} + +static int mac80211_hwsim_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + hwsim_check_magic(vif); + hwsim_check_chanctx_magic(ctx); + + /* if we activate a link while already associated wake it up */ + if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc) { + struct sk_buff *skb; + + skb = ieee80211_nullfunc_get(hw, vif, link_conf->link_id, true); + if (skb) { + local_bh_disable(); + mac80211_hwsim_tx_frame(hw, skb, ctx->def.chan); + local_bh_enable(); + } + } + + return 0; +} + +static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + hwsim_check_magic(vif); + hwsim_check_chanctx_magic(ctx); + + /* if we deactivate a link while associated suspend it first */ + if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc) { + struct sk_buff *skb; + + skb = ieee80211_nullfunc_get(hw, vif, link_conf->link_id, true); + if (skb) { + struct ieee80211_hdr *hdr = (void *)skb->data; + + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + + local_bh_disable(); + mac80211_hwsim_tx_frame(hw, skb, ctx->def.chan); + local_bh_enable(); + } + } +} + +static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_pkts_nic", + "tx_bytes_nic", + "rx_pkts_nic", + "rx_bytes_nic", + "d_tx_dropped", + "d_tx_failed", + "d_ps_mode", + "d_group", +}; + +#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats) + +static void mac80211_hwsim_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) + memcpy(data, *mac80211_hwsim_gstrings_stats, + sizeof(mac80211_hwsim_gstrings_stats)); +} + +static int mac80211_hwsim_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset == ETH_SS_STATS) + return MAC80211_HWSIM_SSTATS_LEN; + return 0; +} + +static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct mac80211_hwsim_data *ar = hw->priv; + int i = 0; + + data[i++] = ar->tx_pkts; + data[i++] = ar->tx_bytes; + data[i++] = ar->rx_pkts; + data[i++] = ar->rx_bytes; + data[i++] = ar->tx_dropped; + data[i++] = ar->tx_failed; + data[i++] = ar->ps; + data[i++] = ar->group; + + WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN); +} + +static int mac80211_hwsim_tx_last_beacon(struct ieee80211_hw *hw) +{ + return 1; +} + +static int mac80211_hwsim_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return -EOPNOTSUPP; +} + +static int mac80211_hwsim_change_vif_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 old_links, u16 new_links, + struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) +{ + unsigned long rem = old_links & ~new_links; + unsigned long add = new_links & ~old_links; + int i; + + if (!old_links) + rem |= BIT(0); + if (!new_links) + add |= BIT(0); + + for_each_set_bit(i, &rem, IEEE80211_MLD_MAX_NUM_LINKS) + mac80211_hwsim_config_mac_nl(hw, old[i]->addr, false); + + for_each_set_bit(i, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + + link_conf = link_conf_dereference_protected(vif, i); + if (WARN_ON(!link_conf)) + continue; + + mac80211_hwsim_config_mac_nl(hw, link_conf->addr, true); + } + + return 0; +} + +static int mac80211_hwsim_change_sta_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + + hwsim_check_sta_magic(sta); + + if (vif->type == NL80211_IFTYPE_STATION) + sp->active_links_rx = new_links; + + return 0; +} + +#define HWSIM_COMMON_OPS \ + .tx = mac80211_hwsim_tx, \ + .wake_tx_queue = ieee80211_handle_wake_tx_queue, \ + .start = mac80211_hwsim_start, \ + .stop = mac80211_hwsim_stop, \ + .add_interface = mac80211_hwsim_add_interface, \ + .change_interface = mac80211_hwsim_change_interface, \ + .remove_interface = mac80211_hwsim_remove_interface, \ + .config = mac80211_hwsim_config, \ + .configure_filter = mac80211_hwsim_configure_filter, \ + .vif_cfg_changed = mac80211_hwsim_vif_info_changed, \ + .link_info_changed = mac80211_hwsim_link_info_changed, \ + .tx_last_beacon = mac80211_hwsim_tx_last_beacon, \ + .sta_notify = mac80211_hwsim_sta_notify, \ + .sta_rc_update = mac80211_hwsim_sta_rc_update, \ + .conf_tx = mac80211_hwsim_conf_tx, \ + .get_survey = mac80211_hwsim_get_survey, \ + CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) \ + .ampdu_action = mac80211_hwsim_ampdu_action, \ + .flush = mac80211_hwsim_flush, \ + .get_et_sset_count = mac80211_hwsim_get_et_sset_count, \ + .get_et_stats = mac80211_hwsim_get_et_stats, \ + .get_et_strings = mac80211_hwsim_get_et_strings, + +#define HWSIM_NON_MLO_OPS \ + .sta_add = mac80211_hwsim_sta_add, \ + .sta_remove = mac80211_hwsim_sta_remove, \ + .set_tim = mac80211_hwsim_set_tim, \ + .get_tsf = mac80211_hwsim_get_tsf, \ + .set_tsf = mac80211_hwsim_set_tsf, + +static const struct ieee80211_ops mac80211_hwsim_ops = { + HWSIM_COMMON_OPS + HWSIM_NON_MLO_OPS + .sw_scan_start = mac80211_hwsim_sw_scan, + .sw_scan_complete = mac80211_hwsim_sw_scan_complete, +}; + +#define HWSIM_CHANCTX_OPS \ + .hw_scan = mac80211_hwsim_hw_scan, \ + .cancel_hw_scan = mac80211_hwsim_cancel_hw_scan, \ + .remain_on_channel = mac80211_hwsim_roc, \ + .cancel_remain_on_channel = mac80211_hwsim_croc, \ + .add_chanctx = mac80211_hwsim_add_chanctx, \ + .remove_chanctx = mac80211_hwsim_remove_chanctx, \ + .change_chanctx = mac80211_hwsim_change_chanctx, \ + .assign_vif_chanctx = mac80211_hwsim_assign_vif_chanctx,\ + .unassign_vif_chanctx = mac80211_hwsim_unassign_vif_chanctx, + +static const struct ieee80211_ops mac80211_hwsim_mchan_ops = { + HWSIM_COMMON_OPS + HWSIM_NON_MLO_OPS + HWSIM_CHANCTX_OPS +}; + +static const struct ieee80211_ops mac80211_hwsim_mlo_ops = { + HWSIM_COMMON_OPS + HWSIM_CHANCTX_OPS + .set_rts_threshold = mac80211_hwsim_set_rts_threshold, + .change_vif_links = mac80211_hwsim_change_vif_links, + .change_sta_links = mac80211_hwsim_change_sta_links, + .sta_state = mac80211_hwsim_sta_state, +}; + +struct hwsim_new_radio_params { + unsigned int channels; + const char *reg_alpha2; + const struct ieee80211_regdomain *regd; + bool reg_strict; + bool p2p_device; + bool use_chanctx; + bool destroy_on_close; + const char *hwname; + bool no_vif; + const u8 *perm_addr; + u32 iftypes; + u32 *ciphers; + u8 n_ciphers; + bool mlo; +}; + +static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, + struct genl_info *info) +{ + if (info) + genl_notify(&hwsim_genl_family, mcast_skb, info, + HWSIM_MCGRP_CONFIG, GFP_KERNEL); + else + genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0, + HWSIM_MCGRP_CONFIG, GFP_KERNEL); +} + +static int append_radio_msg(struct sk_buff *skb, int id, + struct hwsim_new_radio_params *param) +{ + int ret; + + ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id); + if (ret < 0) + return ret; + + if (param->channels) { + ret = nla_put_u32(skb, HWSIM_ATTR_CHANNELS, param->channels); + if (ret < 0) + return ret; + } + + if (param->reg_alpha2) { + ret = nla_put(skb, HWSIM_ATTR_REG_HINT_ALPHA2, 2, + param->reg_alpha2); + if (ret < 0) + return ret; + } + + if (param->regd) { + int i; + + for (i = 0; i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) { + if (hwsim_world_regdom_custom[i] != param->regd) + continue; + + ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i); + if (ret < 0) + return ret; + break; + } + } + + if (param->reg_strict) { + ret = nla_put_flag(skb, HWSIM_ATTR_REG_STRICT_REG); + if (ret < 0) + return ret; + } + + if (param->p2p_device) { + ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_P2P_DEVICE); + if (ret < 0) + return ret; + } + + if (param->use_chanctx) { + ret = nla_put_flag(skb, HWSIM_ATTR_USE_CHANCTX); + if (ret < 0) + return ret; + } + + if (param->hwname) { + ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, + strlen(param->hwname), param->hwname); + if (ret < 0) + return ret; + } + + return 0; +} + +static void hwsim_mcast_new_radio(int id, struct genl_info *info, + struct hwsim_new_radio_params *param) +{ + struct sk_buff *mcast_skb; + void *data; + + mcast_skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!mcast_skb) + return; + + data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0, + HWSIM_CMD_NEW_RADIO); + if (!data) + goto out_err; + + if (append_radio_msg(mcast_skb, id, param) < 0) + goto out_err; + + genlmsg_end(mcast_skb, data); + + hwsim_mcast_config_msg(mcast_skb, info); + return; + +out_err: + nlmsg_free(mcast_skb); +} + +static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { + { + .types_mask = BIT(NL80211_IFTYPE_STATION), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_MU_CASCADING | + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xffff), + .tx_mcs_160 = cpu_to_le16(0xffff), + .rx_mcs_80p80 = cpu_to_le16(0xffff), + .tx_mcs_80p80 = cpu_to_le16(0xffff), + }, + }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * Since B0, B1, B2 and B3 are not set in + * the supported channel width set field in the + * HE PHY capabilities information field the + * device is a 20MHz only device on 2.4GHz band. + */ + .only_20mhz = { + .rx_tx_mcs7_max_nss = 0x88, + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, + }, + { + .types_mask = BIT(NL80211_IFTYPE_AP), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_MU_CASCADING | + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xffff), + .tx_mcs_160 = cpu_to_le16(0xffff), + .rx_mcs_80p80 = cpu_to_le16(0xffff), + .tx_mcs_80p80 = cpu_to_le16(0xffff), + }, + }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * Since B0, B1, B2 and B3 are not set in + * the supported channel width set field in the + * HE PHY capabilities information field the + * device is a 20MHz only device on 2.4GHz band. + */ + .only_20mhz = { + .rx_tx_mcs7_max_nss = 0x88, + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, + }, +#ifdef CONFIG_MAC80211_MESH + { + .types_mask = BIT(NL80211_IFTYPE_MESH_POINT), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = 0, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xffff), + .tx_mcs_160 = cpu_to_le16(0xffff), + .rx_mcs_80p80 = cpu_to_le16(0xffff), + .tx_mcs_80p80 = cpu_to_le16(0xffff), + }, + }, + }, +#endif +}; + +static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { + { + /* TODO: should we support other types, e.g., P2P? */ + .types_mask = BIT(NL80211_IFTYPE_STATION), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_MU_CASCADING | + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xfffa), + .tx_mcs_160 = cpu_to_le16(0xfffa), + .rx_mcs_80p80 = cpu_to_le16(0xfffa), + .tx_mcs_80p80 = cpu_to_le16(0xfffa), + }, + }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK, + .phy_cap_info[2] = + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * As B1 and B2 are set in the supported + * channel width set field in the HE PHY + * capabilities information field include all + * the following MCS/NSS. + */ + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, + }, + { + .types_mask = BIT(NL80211_IFTYPE_AP), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_MU_CASCADING | + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xfffa), + .tx_mcs_160 = cpu_to_le16(0xfffa), + .rx_mcs_80p80 = cpu_to_le16(0xfffa), + .tx_mcs_80p80 = cpu_to_le16(0xfffa), + }, + }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK, + .phy_cap_info[2] = + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * As B1 and B2 are set in the supported + * channel width set field in the HE PHY + * capabilities information field include all + * the following MCS/NSS. + */ + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, + }, +#ifdef CONFIG_MAC80211_MESH + { + /* TODO: should we support other types, e.g., IBSS?*/ + .types_mask = BIT(NL80211_IFTYPE_MESH_POINT), + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = 0, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xfffa), + .tx_mcs_160 = cpu_to_le16(0xfffa), + .rx_mcs_80p80 = cpu_to_le16(0xfffa), + .tx_mcs_80p80 = cpu_to_le16(0xfffa), + }, + }, + }, +#endif +}; + +static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { + { + /* TODO: should we support other types, e.g., P2P? */ + .types_mask = BIT(NL80211_IFTYPE_STATION), + .he_6ghz_capa = { + .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START | + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP | + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN | + IEEE80211_HE_6GHZ_CAP_SM_PS | + IEEE80211_HE_6GHZ_CAP_RD_RESPONDER | + IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS), + }, + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_MU_CASCADING | + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xfffa), + .tx_mcs_160 = cpu_to_le16(0xfffa), + .rx_mcs_80p80 = cpu_to_le16(0xfffa), + .tx_mcs_80p80 = cpu_to_le16(0xfffa), + }, + }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ | + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK, + .phy_cap_info[2] = + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * As B1 and B2 are set in the supported + * channel width set field in the HE PHY + * capabilities information field and 320MHz in + * 6GHz is supported include all the following + * MCS/NSS. + */ + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._320 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, + }, + { + .types_mask = BIT(NL80211_IFTYPE_AP), + .he_6ghz_capa = { + .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START | + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP | + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN | + IEEE80211_HE_6GHZ_CAP_SM_PS | + IEEE80211_HE_6GHZ_CAP_RD_RESPONDER | + IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS), + }, + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_MU_CASCADING | + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xfffa), + .tx_mcs_160 = cpu_to_le16(0xfffa), + .rx_mcs_80p80 = cpu_to_le16(0xfffa), + .tx_mcs_80p80 = cpu_to_le16(0xfffa), + }, + }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ | + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK, + .phy_cap_info[2] = + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT | + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK | + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, + .phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ, + }, + + /* For all MCS and bandwidth, set 8 NSS for both Tx and + * Rx + */ + .eht_mcs_nss_supp = { + /* + * As B1 and B2 are set in the supported + * channel width set field in the HE PHY + * capabilities information field and 320MHz in + * 6GHz is supported include all the following + * MCS/NSS. + */ + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + .bw._320 = { + .rx_tx_mcs9_max_nss = 0x88, + .rx_tx_mcs11_max_nss = 0x88, + .rx_tx_mcs13_max_nss = 0x88, + }, + }, + /* PPE threshold information is not supported */ + }, + }, +#ifdef CONFIG_MAC80211_MESH + { + /* TODO: should we support other types, e.g., IBSS?*/ + .types_mask = BIT(NL80211_IFTYPE_MESH_POINT), + .he_6ghz_capa = { + .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START | + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP | + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN | + IEEE80211_HE_6GHZ_CAP_SM_PS | + IEEE80211_HE_6GHZ_CAP_RD_RESPONDER | + IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS), + }, + .he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = 0, + + /* Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xfffa), + .tx_mcs_160 = cpu_to_le16(0xfffa), + .rx_mcs_80p80 = cpu_to_le16(0xfffa), + .tx_mcs_80p80 = cpu_to_le16(0xfffa), + }, + }, + }, +#endif +}; + +static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband) +{ + u16 n_iftype_data; + + if (sband->band == NL80211_BAND_2GHZ) { + n_iftype_data = ARRAY_SIZE(sband_capa_2ghz); + sband->iftype_data = + (struct ieee80211_sband_iftype_data *)sband_capa_2ghz; + } else if (sband->band == NL80211_BAND_5GHZ) { + n_iftype_data = ARRAY_SIZE(sband_capa_5ghz); + sband->iftype_data = + (struct ieee80211_sband_iftype_data *)sband_capa_5ghz; + } else if (sband->band == NL80211_BAND_6GHZ) { + n_iftype_data = ARRAY_SIZE(sband_capa_6ghz); + sband->iftype_data = + (struct ieee80211_sband_iftype_data *)sband_capa_6ghz; + } else { + return; + } + + sband->n_iftype_data = n_iftype_data; +} + +#ifdef CONFIG_MAC80211_MESH +#define HWSIM_MESH_BIT BIT(NL80211_IFTYPE_MESH_POINT) +#else +#define HWSIM_MESH_BIT 0 +#endif + +#define HWSIM_DEFAULT_IF_LIMIT \ + (BIT(NL80211_IFTYPE_STATION) | \ + BIT(NL80211_IFTYPE_P2P_CLIENT) | \ + BIT(NL80211_IFTYPE_AP) | \ + BIT(NL80211_IFTYPE_P2P_GO) | \ + HWSIM_MESH_BIT) + +#define HWSIM_IFTYPE_SUPPORT_MASK \ + (BIT(NL80211_IFTYPE_STATION) | \ + BIT(NL80211_IFTYPE_AP) | \ + BIT(NL80211_IFTYPE_P2P_CLIENT) | \ + BIT(NL80211_IFTYPE_P2P_GO) | \ + BIT(NL80211_IFTYPE_ADHOC) | \ + BIT(NL80211_IFTYPE_MESH_POINT) | \ + BIT(NL80211_IFTYPE_OCB)) + +static int mac80211_hwsim_new_radio(struct genl_info *info, + struct hwsim_new_radio_params *param) +{ + int err; + u8 addr[ETH_ALEN]; + struct mac80211_hwsim_data *data; + struct ieee80211_hw *hw; + enum nl80211_band band; + const struct ieee80211_ops *ops = &mac80211_hwsim_ops; + struct net *net; + int idx, i; + int n_limits = 0; + + if (WARN_ON(param->channels > 1 && !param->use_chanctx)) + return -EINVAL; + + spin_lock_bh(&hwsim_radio_lock); + idx = hwsim_radio_idx++; + spin_unlock_bh(&hwsim_radio_lock); + + if (param->mlo) + ops = &mac80211_hwsim_mlo_ops; + else if (param->use_chanctx) + ops = &mac80211_hwsim_mchan_ops; + hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname); + if (!hw) { + pr_debug("mac80211_hwsim: ieee80211_alloc_hw failed\n"); + err = -ENOMEM; + goto failed; + } + + /* ieee80211_alloc_hw_nm may have used a default name */ + param->hwname = wiphy_name(hw->wiphy); + + if (info) + net = genl_info_net(info); + else + net = &init_net; + wiphy_net_set(hw->wiphy, net); + + data = hw->priv; + data->hw = hw; + + data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); + if (IS_ERR(data->dev)) { + printk(KERN_DEBUG + "mac80211_hwsim: device_create failed (%ld)\n", + PTR_ERR(data->dev)); + err = -ENOMEM; + goto failed_drvdata; + } + data->dev->driver = &mac80211_hwsim_driver.driver; + err = device_bind_driver(data->dev); + if (err != 0) { + pr_debug("mac80211_hwsim: device_bind_driver failed (%d)\n", + err); + goto failed_bind; + } + + skb_queue_head_init(&data->pending); + + SET_IEEE80211_DEV(hw, data->dev); + if (!param->perm_addr) { + eth_zero_addr(addr); + addr[0] = 0x02; + addr[3] = idx >> 8; + addr[4] = idx; + memcpy(data->addresses[0].addr, addr, ETH_ALEN); + /* Why need here second address ? */ + memcpy(data->addresses[1].addr, addr, ETH_ALEN); + data->addresses[1].addr[0] |= 0x40; + hw->wiphy->n_addresses = 2; + hw->wiphy->addresses = data->addresses; + /* possible address clash is checked at hash table insertion */ + } else { + memcpy(data->addresses[0].addr, param->perm_addr, ETH_ALEN); + /* compatibility with automatically generated mac addr */ + memcpy(data->addresses[1].addr, param->perm_addr, ETH_ALEN); + hw->wiphy->n_addresses = 2; + hw->wiphy->addresses = data->addresses; + } + + data->channels = param->channels; + data->use_chanctx = param->use_chanctx; + data->idx = idx; + data->destroy_on_close = param->destroy_on_close; + if (info) + data->portid = info->snd_portid; + + /* setup interface limits, only on interface types we support */ + if (param->iftypes & BIT(NL80211_IFTYPE_ADHOC)) { + data->if_limits[n_limits].max = 1; + data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_ADHOC); + n_limits++; + } + + if (param->iftypes & HWSIM_DEFAULT_IF_LIMIT) { + data->if_limits[n_limits].max = 2048; + /* + * For this case, we may only support a subset of + * HWSIM_DEFAULT_IF_LIMIT, therefore we only want to add the + * bits that both param->iftype & HWSIM_DEFAULT_IF_LIMIT have. + */ + data->if_limits[n_limits].types = + HWSIM_DEFAULT_IF_LIMIT & param->iftypes; + n_limits++; + } + + if (param->iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) { + data->if_limits[n_limits].max = 1; + data->if_limits[n_limits].types = + BIT(NL80211_IFTYPE_P2P_DEVICE); + n_limits++; + } + + if (data->use_chanctx) { + hw->wiphy->max_scan_ssids = 255; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 1000; + data->if_combination.radar_detect_widths = 0; + data->if_combination.num_different_channels = data->channels; + } else { + data->if_combination.num_different_channels = 1; + data->if_combination.radar_detect_widths = + BIT(NL80211_CHAN_WIDTH_5) | + BIT(NL80211_CHAN_WIDTH_10) | + BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160); + } + + if (!n_limits) { + err = -EINVAL; + goto failed_hw; + } + + data->if_combination.max_interfaces = 0; + for (i = 0; i < n_limits; i++) + data->if_combination.max_interfaces += + data->if_limits[i].max; + + data->if_combination.n_limits = n_limits; + data->if_combination.limits = data->if_limits; + + /* + * If we actually were asked to support combinations, + * advertise them - if there's only a single thing like + * only IBSS then don't advertise it as combinations. + */ + if (data->if_combination.max_interfaces > 1) { + hw->wiphy->iface_combinations = &data->if_combination; + hw->wiphy->n_iface_combinations = 1; + } + + if (param->ciphers) { + memcpy(data->ciphers, param->ciphers, + param->n_ciphers * sizeof(u32)); + hw->wiphy->cipher_suites = data->ciphers; + hw->wiphy->n_cipher_suites = param->n_ciphers; + } + + data->rx_rssi = DEFAULT_RX_RSSI; + + INIT_DELAYED_WORK(&data->roc_start, hw_roc_start); + INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); + INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); + + hw->queues = 5; + hw->offchannel_tx_hw_queue = 4; + + ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, CHANCTX_STA_CSA); + ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES); + ieee80211_hw_set(hw, QUEUE_CONTROL); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, MFP_CAPABLE); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, TDLS_WIDER_BW); + ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + + if (param->mlo) { + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, AP_LINK_PS); + } else { + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(hw, PS_NULLFUNC_STACK); + if (rctbl) + ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); + } + + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_SUPPORTS_5_10_MHZ | + WIPHY_FLAG_HAS_CHANNEL_SWITCH; + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR | + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | + NL80211_FEATURE_STATIC_SMPS | + NL80211_FEATURE_DYNAMIC_SMPS | + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION); + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT); + + hw->wiphy->interface_modes = param->iftypes; + + /* ask mac80211 to reserve space for magic */ + hw->vif_data_size = sizeof(struct hwsim_vif_priv); + hw->sta_data_size = sizeof(struct hwsim_sta_priv); + hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); + + memcpy(data->channels_2ghz, hwsim_channels_2ghz, + sizeof(hwsim_channels_2ghz)); + memcpy(data->channels_5ghz, hwsim_channels_5ghz, + sizeof(hwsim_channels_5ghz)); + memcpy(data->channels_6ghz, hwsim_channels_6ghz, + sizeof(hwsim_channels_6ghz)); + memcpy(data->channels_s1g, hwsim_channels_s1g, + sizeof(hwsim_channels_s1g)); + memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); + + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { + struct ieee80211_supported_band *sband = &data->bands[band]; + + sband->band = band; + + switch (band) { + case NL80211_BAND_2GHZ: + sband->channels = data->channels_2ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); + sband->bitrates = data->rates; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates); + break; + case NL80211_BAND_5GHZ: + sband->channels = data->channels_5ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); + sband->bitrates = data->rates + 4; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; + + sband->vht_cap.vht_supported = true; + sband->vht_cap.cap = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_TXSTBC | + IEEE80211_VHT_CAP_RXSTBC_4 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + sband->vht_cap.vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 14); + sband->vht_cap.vht_mcs.tx_mcs_map = + sband->vht_cap.vht_mcs.rx_mcs_map; + break; + case NL80211_BAND_6GHZ: + sband->channels = data->channels_6ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_6ghz); + sband->bitrates = data->rates + 4; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; + break; + case NL80211_BAND_S1GHZ: + memcpy(&sband->s1g_cap, &hwsim_s1g_cap, + sizeof(sband->s1g_cap)); + sband->channels = data->channels_s1g; + sband->n_channels = ARRAY_SIZE(hwsim_channels_s1g); + break; + default: + continue; + } + + if (band != NL80211_BAND_6GHZ){ + sband->ht_cap.ht_supported = true; + sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + sband->ht_cap.ampdu_factor = 0x3; + sband->ht_cap.ampdu_density = 0x6; + memset(&sband->ht_cap.mcs, 0, + sizeof(sband->ht_cap.mcs)); + sband->ht_cap.mcs.rx_mask[0] = 0xff; + sband->ht_cap.mcs.rx_mask[1] = 0xff; + sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + } + + mac80211_hwsim_sband_capab(sband); + + hw->wiphy->bands[band] = sband; + } + + /* By default all radios belong to the first group */ + data->group = 1; + mutex_init(&data->mutex); + + data->netgroup = hwsim_net_get_netgroup(net); + data->wmediumd = hwsim_net_get_wmediumd(net); + + /* Enable frame retransmissions for lossy channels */ + hw->max_rates = 4; + hw->max_rate_tries = 11; + + hw->wiphy->vendor_commands = mac80211_hwsim_vendor_commands; + hw->wiphy->n_vendor_commands = + ARRAY_SIZE(mac80211_hwsim_vendor_commands); + hw->wiphy->vendor_events = mac80211_hwsim_vendor_events; + hw->wiphy->n_vendor_events = ARRAY_SIZE(mac80211_hwsim_vendor_events); + + if (param->reg_strict) + hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG; + if (param->regd) { + data->regd = param->regd; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(hw->wiphy, param->regd); + /* give the regulatory workqueue a chance to run */ + schedule_timeout_interruptible(1); + } + + if (param->no_vif) + ieee80211_hw_set(hw, NO_AUTO_VIF); + + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + + for (i = 0; i < ARRAY_SIZE(data->link_data); i++) { + hrtimer_init(&data->link_data[i].beacon_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS_SOFT); + data->link_data[i].beacon_timer.function = + mac80211_hwsim_beacon; + data->link_data[i].link_id = i; + } + + err = ieee80211_register_hw(hw); + if (err < 0) { + pr_debug("mac80211_hwsim: ieee80211_register_hw failed (%d)\n", + err); + goto failed_hw; + } + + wiphy_dbg(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); + + if (param->reg_alpha2) { + data->alpha2[0] = param->reg_alpha2[0]; + data->alpha2[1] = param->reg_alpha2[1]; + regulatory_hint(hw->wiphy, param->reg_alpha2); + } + + data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); + debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); + debugfs_create_file("group", 0666, data->debugfs, data, + &hwsim_fops_group); + debugfs_create_file("rx_rssi", 0666, data->debugfs, data, + &hwsim_fops_rx_rssi); + if (!data->use_chanctx) + debugfs_create_file("dfs_simulate_radar", 0222, + data->debugfs, + data, &hwsim_simulate_radar); + + spin_lock_bh(&hwsim_radio_lock); + err = rhashtable_insert_fast(&hwsim_radios_rht, &data->rht, + hwsim_rht_params); + if (err < 0) { + if (info) { + GENL_SET_ERR_MSG(info, "perm addr already present"); + NL_SET_BAD_ATTR(info->extack, + info->attrs[HWSIM_ATTR_PERM_ADDR]); + } + spin_unlock_bh(&hwsim_radio_lock); + goto failed_final_insert; + } + + list_add_tail(&data->list, &hwsim_radios); + hwsim_radios_generation++; + spin_unlock_bh(&hwsim_radio_lock); + + hwsim_mcast_new_radio(idx, info, param); + + return idx; + +failed_final_insert: + debugfs_remove_recursive(data->debugfs); + ieee80211_unregister_hw(data->hw); +failed_hw: + device_release_driver(data->dev); +failed_bind: + device_unregister(data->dev); +failed_drvdata: + ieee80211_free_hw(hw); +failed: + return err; +} + +static void hwsim_mcast_del_radio(int id, const char *hwname, + struct genl_info *info) +{ + struct sk_buff *skb; + void *data; + int ret; + + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!skb) + return; + + data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, + HWSIM_CMD_DEL_RADIO); + if (!data) + goto error; + + ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id); + if (ret < 0) + goto error; + + ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname), + hwname); + if (ret < 0) + goto error; + + genlmsg_end(skb, data); + + hwsim_mcast_config_msg(skb, info); + + return; + +error: + nlmsg_free(skb); +} + +static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data, + const char *hwname, + struct genl_info *info) +{ + hwsim_mcast_del_radio(data->idx, hwname, info); + debugfs_remove_recursive(data->debugfs); + ieee80211_unregister_hw(data->hw); + device_release_driver(data->dev); + device_unregister(data->dev); + ieee80211_free_hw(data->hw); +} + +static int mac80211_hwsim_get_radio(struct sk_buff *skb, + struct mac80211_hwsim_data *data, + u32 portid, u32 seq, + struct netlink_callback *cb, int flags) +{ + void *hdr; + struct hwsim_new_radio_params param = { }; + int res = -EMSGSIZE; + + hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags, + HWSIM_CMD_GET_RADIO); + if (!hdr) + return -EMSGSIZE; + + if (cb) + genl_dump_check_consistent(cb, hdr); + + if (data->alpha2[0] && data->alpha2[1]) + param.reg_alpha2 = data->alpha2; + + param.reg_strict = !!(data->hw->wiphy->regulatory_flags & + REGULATORY_STRICT_REG); + param.p2p_device = !!(data->hw->wiphy->interface_modes & + BIT(NL80211_IFTYPE_P2P_DEVICE)); + param.use_chanctx = data->use_chanctx; + param.regd = data->regd; + param.channels = data->channels; + param.hwname = wiphy_name(data->hw->wiphy); + + res = append_radio_msg(skb, data->idx, ¶m); + if (res < 0) + goto out_err; + + genlmsg_end(skb, hdr); + return 0; + +out_err: + genlmsg_cancel(skb, hdr); + return res; +} + +static void mac80211_hwsim_free(void) +{ + struct mac80211_hwsim_data *data; + + spin_lock_bh(&hwsim_radio_lock); + while ((data = list_first_entry_or_null(&hwsim_radios, + struct mac80211_hwsim_data, + list))) { + list_del(&data->list); + spin_unlock_bh(&hwsim_radio_lock); + mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), + NULL); + spin_lock_bh(&hwsim_radio_lock); + } + spin_unlock_bh(&hwsim_radio_lock); + class_destroy(hwsim_class); +} + +static const struct net_device_ops hwsim_netdev_ops = { + .ndo_start_xmit = hwsim_mon_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static void hwsim_mon_setup(struct net_device *dev) +{ + u8 addr[ETH_ALEN]; + + dev->netdev_ops = &hwsim_netdev_ops; + dev->needs_free_netdev = true; + ether_setup(dev); + dev->priv_flags |= IFF_NO_QUEUE; + dev->type = ARPHRD_IEEE80211_RADIOTAP; + eth_zero_addr(addr); + addr[0] = 0x12; + eth_hw_addr_set(dev, addr); +} + +static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) +{ + return rhashtable_lookup_fast(&hwsim_radios_rht, + addr, + hwsim_rht_params); +} + +static void hwsim_register_wmediumd(struct net *net, u32 portid) +{ + struct mac80211_hwsim_data *data; + + hwsim_net_set_wmediumd(net, portid); + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) { + if (data->netgroup == hwsim_net_get_netgroup(net)) + data->wmediumd = portid; + } + spin_unlock_bh(&hwsim_radio_lock); +} + +static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, + struct genl_info *info) +{ + + struct ieee80211_hdr *hdr; + struct mac80211_hwsim_data *data2; + struct ieee80211_tx_info *txi; + struct hwsim_tx_rate *tx_attempts; + u64 ret_skb_cookie; + struct sk_buff *skb, *tmp; + const u8 *src; + unsigned int hwsim_flags; + int i; + unsigned long flags; + bool found = false; + + if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || + !info->attrs[HWSIM_ATTR_FLAGS] || + !info->attrs[HWSIM_ATTR_COOKIE] || + !info->attrs[HWSIM_ATTR_SIGNAL] || + !info->attrs[HWSIM_ATTR_TX_INFO]) + goto out; + + src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); + hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]); + ret_skb_cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); + + data2 = get_hwsim_data_ref_from_addr(src); + if (!data2) + goto out; + + if (!hwsim_virtio_enabled) { + if (hwsim_net_get_netgroup(genl_info_net(info)) != + data2->netgroup) + goto out; + + if (info->snd_portid != data2->wmediumd) + goto out; + } + + /* look for the skb matching the cookie passed back from user */ + spin_lock_irqsave(&data2->pending.lock, flags); + skb_queue_walk_safe(&data2->pending, skb, tmp) { + uintptr_t skb_cookie; + + txi = IEEE80211_SKB_CB(skb); + skb_cookie = (uintptr_t)txi->rate_driver_data[0]; + + if (skb_cookie == ret_skb_cookie) { + __skb_unlink(skb, &data2->pending); + found = true; + break; + } + } + spin_unlock_irqrestore(&data2->pending.lock, flags); + + /* not found */ + if (!found) + goto out; + + /* Tx info received because the frame was broadcasted on user space, + so we get all the necessary info: tx attempts and skb control buff */ + + tx_attempts = (struct hwsim_tx_rate *)nla_data( + info->attrs[HWSIM_ATTR_TX_INFO]); + + /* now send back TX status */ + txi = IEEE80211_SKB_CB(skb); + + ieee80211_tx_info_clear_status(txi); + + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + txi->status.rates[i].idx = tx_attempts[i].idx; + txi->status.rates[i].count = tx_attempts[i].count; + } + + txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]); + + if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) && + (hwsim_flags & HWSIM_TX_STAT_ACK)) { + if (skb->len >= 16) { + hdr = (struct ieee80211_hdr *) skb->data; + mac80211_hwsim_monitor_ack(data2->channel, + hdr->addr2); + } + txi->flags |= IEEE80211_TX_STAT_ACK; + } + + if (hwsim_flags & HWSIM_TX_CTL_NO_ACK) + txi->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + + ieee80211_tx_status_irqsafe(data2->hw, skb); + return 0; +out: + return -EINVAL; + +} + +static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, + struct genl_info *info) +{ + struct mac80211_hwsim_data *data2; + struct ieee80211_rx_status rx_status; + struct ieee80211_hdr *hdr; + const u8 *dst; + int frame_data_len; + void *frame_data; + struct sk_buff *skb = NULL; + struct ieee80211_channel *channel = NULL; + + if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || + !info->attrs[HWSIM_ATTR_FRAME] || + !info->attrs[HWSIM_ATTR_RX_RATE] || + !info->attrs[HWSIM_ATTR_SIGNAL]) + goto out; + + dst = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_RECEIVER]); + frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]); + frame_data = (void *)nla_data(info->attrs[HWSIM_ATTR_FRAME]); + + /* Allocate new skb here */ + skb = alloc_skb(frame_data_len, GFP_KERNEL); + if (skb == NULL) + goto err; + + if (frame_data_len > IEEE80211_MAX_DATA_LEN) + goto err; + + /* Copy the data */ + skb_put_data(skb, frame_data, frame_data_len); + + data2 = get_hwsim_data_ref_from_addr(dst); + if (!data2) + goto out; + + if (data2->use_chanctx) { + if (data2->tmp_chan) + channel = data2->tmp_chan; + } else { + channel = data2->channel; + } + + if (!hwsim_virtio_enabled) { + if (hwsim_net_get_netgroup(genl_info_net(info)) != + data2->netgroup) + goto out; + + if (info->snd_portid != data2->wmediumd) + goto out; + } + + /* check if radio is configured properly */ + + if ((data2->idle && !data2->tmp_chan) || !data2->started) + goto out; + + /* A frame is received from user space */ + memset(&rx_status, 0, sizeof(rx_status)); + if (info->attrs[HWSIM_ATTR_FREQ]) { + struct tx_iter_data iter_data = {}; + + /* throw away off-channel packets, but allow both the temporary + * ("hw" scan/remain-on-channel), regular channels and links, + * since the internal datapath also allows this + */ + rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]); + + iter_data.channel = ieee80211_get_channel(data2->hw->wiphy, + rx_status.freq); + if (!iter_data.channel) + goto out; + rx_status.band = iter_data.channel->band; + + mutex_lock(&data2->mutex); + if (!hwsim_chans_compat(iter_data.channel, channel)) { + ieee80211_iterate_active_interfaces_atomic( + data2->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_tx_iter, &iter_data); + if (!iter_data.receive) { + mutex_unlock(&data2->mutex); + goto out; + } + } + mutex_unlock(&data2->mutex); + } else if (!channel) { + goto out; + } else { + rx_status.freq = channel->center_freq; + rx_status.band = channel->band; + } + + rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]); + if (rx_status.rate_idx >= data2->hw->wiphy->bands[rx_status.band]->n_bitrates) + goto out; + rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]); + + hdr = (void *)skb->data; + + if (ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) + rx_status.boottime_ns = ktime_get_boottime_ns(); + + mac80211_hwsim_rx(data2, &rx_status, skb); + + return 0; +err: + pr_debug("mac80211_hwsim: error occurred in %s\n", __func__); +out: + dev_kfree_skb(skb); + return -EINVAL; +} + +static int hwsim_register_received_nl(struct sk_buff *skb_2, + struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct mac80211_hwsim_data *data; + int chans = 1; + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) + chans = max(chans, data->channels); + spin_unlock_bh(&hwsim_radio_lock); + + /* In the future we should revise the userspace API and allow it + * to set a flag that it does support multi-channel, then we can + * let this pass conditionally on the flag. + * For current userspace, prohibit it since it won't work right. + */ + if (chans > 1) + return -EOPNOTSUPP; + + if (hwsim_net_get_wmediumd(net)) + return -EBUSY; + + hwsim_register_wmediumd(net, info->snd_portid); + + pr_debug("mac80211_hwsim: received a REGISTER, " + "switching to wmediumd mode with pid %d\n", info->snd_portid); + + return 0; +} + +/* ensures ciphers only include ciphers listed in 'hwsim_ciphers' array */ +static bool hwsim_known_ciphers(const u32 *ciphers, int n_ciphers) +{ + int i; + + for (i = 0; i < n_ciphers; i++) { + int j; + int found = 0; + + for (j = 0; j < ARRAY_SIZE(hwsim_ciphers); j++) { + if (ciphers[i] == hwsim_ciphers[j]) { + found = 1; + break; + } + } + + if (!found) + return false; + } + + return true; +} + +static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct hwsim_new_radio_params param = { 0 }; + const char *hwname = NULL; + int ret; + + param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG]; + param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE]; + param.channels = channels; + param.destroy_on_close = + info->attrs[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE]; + + if (info->attrs[HWSIM_ATTR_CHANNELS]) + param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]); + + if (param.channels < 1) { + GENL_SET_ERR_MSG(info, "must have at least one channel"); + return -EINVAL; + } + + if (info->attrs[HWSIM_ATTR_NO_VIF]) + param.no_vif = true; + + if (info->attrs[HWSIM_ATTR_USE_CHANCTX]) + param.use_chanctx = true; + else + param.use_chanctx = (param.channels > 1); + + if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]) + param.reg_alpha2 = + nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]); + + if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) { + u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]); + + if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) + return -EINVAL; + + idx = array_index_nospec(idx, + ARRAY_SIZE(hwsim_world_regdom_custom)); + param.regd = hwsim_world_regdom_custom[idx]; + } + + if (info->attrs[HWSIM_ATTR_PERM_ADDR]) { + if (!is_valid_ether_addr( + nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]))) { + GENL_SET_ERR_MSG(info,"MAC is no valid source addr"); + NL_SET_BAD_ATTR(info->extack, + info->attrs[HWSIM_ATTR_PERM_ADDR]); + return -EINVAL; + } + + param.perm_addr = nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]); + } + + if (info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]) { + param.iftypes = + nla_get_u32(info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]); + + if (param.iftypes & ~HWSIM_IFTYPE_SUPPORT_MASK) { + NL_SET_ERR_MSG_ATTR(info->extack, + info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT], + "cannot support more iftypes than kernel"); + return -EINVAL; + } + } else { + param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK; + } + + /* ensure both flag and iftype support is honored */ + if (param.p2p_device || + param.iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) { + param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE); + param.p2p_device = true; + } + + if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) { + u32 len = nla_len(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]); + + param.ciphers = + nla_data(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]); + + if (len % sizeof(u32)) { + NL_SET_ERR_MSG_ATTR(info->extack, + info->attrs[HWSIM_ATTR_CIPHER_SUPPORT], + "bad cipher list length"); + return -EINVAL; + } + + param.n_ciphers = len / sizeof(u32); + + if (param.n_ciphers > ARRAY_SIZE(hwsim_ciphers)) { + NL_SET_ERR_MSG_ATTR(info->extack, + info->attrs[HWSIM_ATTR_CIPHER_SUPPORT], + "too many ciphers specified"); + return -EINVAL; + } + + if (!hwsim_known_ciphers(param.ciphers, param.n_ciphers)) { + NL_SET_ERR_MSG_ATTR(info->extack, + info->attrs[HWSIM_ATTR_CIPHER_SUPPORT], + "unsupported ciphers specified"); + return -EINVAL; + } + } + + param.mlo = info->attrs[HWSIM_ATTR_MLO_SUPPORT]; + + if (param.mlo) + param.use_chanctx = true; + + if (info->attrs[HWSIM_ATTR_RADIO_NAME]) { + hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]), + nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]), + GFP_KERNEL); + if (!hwname) + return -ENOMEM; + param.hwname = hwname; + } + + ret = mac80211_hwsim_new_radio(info, ¶m); + kfree(hwname); + return ret; +} + +static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct mac80211_hwsim_data *data; + s64 idx = -1; + const char *hwname = NULL; + + if (info->attrs[HWSIM_ATTR_RADIO_ID]) { + idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]); + } else if (info->attrs[HWSIM_ATTR_RADIO_NAME]) { + hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]), + nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]), + GFP_KERNEL); + if (!hwname) + return -ENOMEM; + } else + return -EINVAL; + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) { + if (idx >= 0) { + if (data->idx != idx) + continue; + } else { + if (!hwname || + strcmp(hwname, wiphy_name(data->hw->wiphy))) + continue; + } + + if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info))) + continue; + + list_del(&data->list); + rhashtable_remove_fast(&hwsim_radios_rht, &data->rht, + hwsim_rht_params); + hwsim_radios_generation++; + spin_unlock_bh(&hwsim_radio_lock); + mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), + info); + kfree(hwname); + return 0; + } + spin_unlock_bh(&hwsim_radio_lock); + + kfree(hwname); + return -ENODEV; +} + +static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct mac80211_hwsim_data *data; + struct sk_buff *skb; + int idx, res = -ENODEV; + + if (!info->attrs[HWSIM_ATTR_RADIO_ID]) + return -EINVAL; + idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]); + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) { + if (data->idx != idx) + continue; + + if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info))) + continue; + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!skb) { + res = -ENOMEM; + goto out_err; + } + + res = mac80211_hwsim_get_radio(skb, data, info->snd_portid, + info->snd_seq, NULL, 0); + if (res < 0) { + nlmsg_free(skb); + goto out_err; + } + + res = genlmsg_reply(skb, info); + break; + } + +out_err: + spin_unlock_bh(&hwsim_radio_lock); + + return res; +} + +static int hwsim_dump_radio_nl(struct sk_buff *skb, + struct netlink_callback *cb) +{ + int last_idx = cb->args[0] - 1; + struct mac80211_hwsim_data *data = NULL; + int res = 0; + void *hdr; + + spin_lock_bh(&hwsim_radio_lock); + cb->seq = hwsim_radios_generation; + + if (last_idx >= hwsim_radio_idx-1) + goto done; + + list_for_each_entry(data, &hwsim_radios, list) { + if (data->idx <= last_idx) + continue; + + if (!net_eq(wiphy_net(data->hw->wiphy), sock_net(skb->sk))) + continue; + + res = mac80211_hwsim_get_radio(skb, data, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, cb, + NLM_F_MULTI); + if (res < 0) + break; + + last_idx = data->idx; + } + + cb->args[0] = last_idx + 1; + + /* list changed, but no new element sent, set interrupted flag */ + if (skb->len == 0 && cb->prev_seq && cb->seq != cb->prev_seq) { + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, &hwsim_genl_family, + NLM_F_MULTI, HWSIM_CMD_GET_RADIO); + if (hdr) { + genl_dump_check_consistent(cb, hdr); + genlmsg_end(skb, hdr); + } else { + res = -EMSGSIZE; + } + } + +done: + spin_unlock_bh(&hwsim_radio_lock); + return res ?: skb->len; +} + +/* Generic Netlink operations array */ +static const struct genl_small_ops hwsim_ops[] = { + { + .cmd = HWSIM_CMD_REGISTER, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = hwsim_register_received_nl, + .flags = GENL_UNS_ADMIN_PERM, + }, + { + .cmd = HWSIM_CMD_FRAME, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = hwsim_cloned_frame_received_nl, + }, + { + .cmd = HWSIM_CMD_TX_INFO_FRAME, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = hwsim_tx_info_frame_received_nl, + }, + { + .cmd = HWSIM_CMD_NEW_RADIO, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = hwsim_new_radio_nl, + .flags = GENL_UNS_ADMIN_PERM, + }, + { + .cmd = HWSIM_CMD_DEL_RADIO, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = hwsim_del_radio_nl, + .flags = GENL_UNS_ADMIN_PERM, + }, + { + .cmd = HWSIM_CMD_GET_RADIO, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = hwsim_get_radio_nl, + .dumpit = hwsim_dump_radio_nl, + }, +}; + +static struct genl_family hwsim_genl_family __ro_after_init = { + .name = "MAC80211_HWSIM", + .version = 1, + .maxattr = HWSIM_ATTR_MAX, + .policy = hwsim_genl_policy, + .netnsok = true, + .module = THIS_MODULE, + .small_ops = hwsim_ops, + .n_small_ops = ARRAY_SIZE(hwsim_ops), + .resv_start_op = HWSIM_CMD_DEL_MAC_ADDR + 1, + .mcgrps = hwsim_mcgrps, + .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps), +}; + +static void remove_user_radios(u32 portid) +{ + struct mac80211_hwsim_data *entry, *tmp; + LIST_HEAD(list); + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry_safe(entry, tmp, &hwsim_radios, list) { + if (entry->destroy_on_close && entry->portid == portid) { + list_move(&entry->list, &list); + rhashtable_remove_fast(&hwsim_radios_rht, &entry->rht, + hwsim_rht_params); + hwsim_radios_generation++; + } + } + spin_unlock_bh(&hwsim_radio_lock); + + list_for_each_entry_safe(entry, tmp, &list, list) { + list_del(&entry->list); + mac80211_hwsim_del_radio(entry, wiphy_name(entry->hw->wiphy), + NULL); + } +} + +static int mac80211_hwsim_netlink_notify(struct notifier_block *nb, + unsigned long state, + void *_notify) +{ + struct netlink_notify *notify = _notify; + + if (state != NETLINK_URELEASE) + return NOTIFY_DONE; + + remove_user_radios(notify->portid); + + if (notify->portid == hwsim_net_get_wmediumd(notify->net)) { + printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink" + " socket, switching to perfect channel medium\n"); + hwsim_register_wmediumd(notify->net, 0); + } + return NOTIFY_DONE; + +} + +static struct notifier_block hwsim_netlink_notifier = { + .notifier_call = mac80211_hwsim_netlink_notify, +}; + +static int __init hwsim_init_netlink(void) +{ + int rc; + + printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); + + rc = genl_register_family(&hwsim_genl_family); + if (rc) + goto failure; + + rc = netlink_register_notifier(&hwsim_netlink_notifier); + if (rc) { + genl_unregister_family(&hwsim_genl_family); + goto failure; + } + + return 0; + +failure: + pr_debug("mac80211_hwsim: error occurred in %s\n", __func__); + return -EINVAL; +} + +static __net_init int hwsim_init_net(struct net *net) +{ + return hwsim_net_set_netgroup(net); +} + +static void __net_exit hwsim_exit_net(struct net *net) +{ + struct mac80211_hwsim_data *data, *tmp; + LIST_HEAD(list); + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry_safe(data, tmp, &hwsim_radios, list) { + if (!net_eq(wiphy_net(data->hw->wiphy), net)) + continue; + + /* Radios created in init_net are returned to init_net. */ + if (data->netgroup == hwsim_net_get_netgroup(&init_net)) + continue; + + list_move(&data->list, &list); + rhashtable_remove_fast(&hwsim_radios_rht, &data->rht, + hwsim_rht_params); + hwsim_radios_generation++; + } + spin_unlock_bh(&hwsim_radio_lock); + + list_for_each_entry_safe(data, tmp, &list, list) { + list_del(&data->list); + mac80211_hwsim_del_radio(data, + wiphy_name(data->hw->wiphy), + NULL); + } + + ida_free(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net)); +} + +static struct pernet_operations hwsim_net_ops = { + .init = hwsim_init_net, + .exit = hwsim_exit_net, + .id = &hwsim_net_id, + .size = sizeof(struct hwsim_net), +}; + +static void hwsim_exit_netlink(void) +{ + /* unregister the notifier */ + netlink_unregister_notifier(&hwsim_netlink_notifier); + /* unregister the family */ + genl_unregister_family(&hwsim_genl_family); +} + +#if IS_REACHABLE(CONFIG_VIRTIO) +static void hwsim_virtio_tx_done(struct virtqueue *vq) +{ + unsigned int len; + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&hwsim_virtio_lock, flags); + while ((skb = virtqueue_get_buf(vq, &len))) + nlmsg_free(skb); + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); +} + +static int hwsim_virtio_handle_cmd(struct sk_buff *skb) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *gnlh; + struct nlattr *tb[HWSIM_ATTR_MAX + 1]; + struct genl_info info = {}; + int err; + + nlh = nlmsg_hdr(skb); + gnlh = nlmsg_data(nlh); + + if (skb->len < nlh->nlmsg_len) + return -EINVAL; + + err = genlmsg_parse(nlh, &hwsim_genl_family, tb, HWSIM_ATTR_MAX, + hwsim_genl_policy, NULL); + if (err) { + pr_err_ratelimited("hwsim: genlmsg_parse returned %d\n", err); + return err; + } + + info.attrs = tb; + + switch (gnlh->cmd) { + case HWSIM_CMD_FRAME: + hwsim_cloned_frame_received_nl(skb, &info); + break; + case HWSIM_CMD_TX_INFO_FRAME: + hwsim_tx_info_frame_received_nl(skb, &info); + break; + default: + pr_err_ratelimited("hwsim: invalid cmd: %d\n", gnlh->cmd); + return -EPROTO; + } + return 0; +} + +static void hwsim_virtio_rx_work(struct work_struct *work) +{ + struct virtqueue *vq; + unsigned int len; + struct sk_buff *skb; + struct scatterlist sg[1]; + int err; + unsigned long flags; + + spin_lock_irqsave(&hwsim_virtio_lock, flags); + if (!hwsim_virtio_enabled) + goto out_unlock; + + skb = virtqueue_get_buf(hwsim_vqs[HWSIM_VQ_RX], &len); + if (!skb) + goto out_unlock; + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); + + skb->data = skb->head; + skb_reset_tail_pointer(skb); + skb_put(skb, len); + hwsim_virtio_handle_cmd(skb); + + spin_lock_irqsave(&hwsim_virtio_lock, flags); + if (!hwsim_virtio_enabled) { + nlmsg_free(skb); + goto out_unlock; + } + vq = hwsim_vqs[HWSIM_VQ_RX]; + sg_init_one(sg, skb->head, skb_end_offset(skb)); + err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_ATOMIC); + if (WARN(err, "virtqueue_add_inbuf returned %d\n", err)) + nlmsg_free(skb); + else + virtqueue_kick(vq); + schedule_work(&hwsim_virtio_rx); + +out_unlock: + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); +} + +static void hwsim_virtio_rx_done(struct virtqueue *vq) +{ + schedule_work(&hwsim_virtio_rx); +} + +static int init_vqs(struct virtio_device *vdev) +{ + vq_callback_t *callbacks[HWSIM_NUM_VQS] = { + [HWSIM_VQ_TX] = hwsim_virtio_tx_done, + [HWSIM_VQ_RX] = hwsim_virtio_rx_done, + }; + const char *names[HWSIM_NUM_VQS] = { + [HWSIM_VQ_TX] = "tx", + [HWSIM_VQ_RX] = "rx", + }; + + return virtio_find_vqs(vdev, HWSIM_NUM_VQS, + hwsim_vqs, callbacks, names, NULL); +} + +static int fill_vq(struct virtqueue *vq) +{ + int i, err; + struct sk_buff *skb; + struct scatterlist sg[1]; + + for (i = 0; i < virtqueue_get_vring_size(vq); i++) { + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + sg_init_one(sg, skb->head, skb_end_offset(skb)); + err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL); + if (err) { + nlmsg_free(skb); + return err; + } + } + virtqueue_kick(vq); + return 0; +} + +static void remove_vqs(struct virtio_device *vdev) +{ + int i; + + virtio_reset_device(vdev); + + for (i = 0; i < ARRAY_SIZE(hwsim_vqs); i++) { + struct virtqueue *vq = hwsim_vqs[i]; + struct sk_buff *skb; + + while ((skb = virtqueue_detach_unused_buf(vq))) + nlmsg_free(skb); + } + + vdev->config->del_vqs(vdev); +} + +static int hwsim_virtio_probe(struct virtio_device *vdev) +{ + int err; + unsigned long flags; + + spin_lock_irqsave(&hwsim_virtio_lock, flags); + if (hwsim_virtio_enabled) { + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); + return -EEXIST; + } + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); + + err = init_vqs(vdev); + if (err) + return err; + + virtio_device_ready(vdev); + + err = fill_vq(hwsim_vqs[HWSIM_VQ_RX]); + if (err) + goto out_remove; + + spin_lock_irqsave(&hwsim_virtio_lock, flags); + hwsim_virtio_enabled = true; + spin_unlock_irqrestore(&hwsim_virtio_lock, flags); + + schedule_work(&hwsim_virtio_rx); + return 0; + +out_remove: + remove_vqs(vdev); + return err; +} + +static void hwsim_virtio_remove(struct virtio_device *vdev) +{ + hwsim_virtio_enabled = false; + + cancel_work_sync(&hwsim_virtio_rx); + + remove_vqs(vdev); +} + +/* MAC80211_HWSIM virtio device id table */ +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_MAC80211_HWSIM, VIRTIO_DEV_ANY_ID }, + { 0 } +}; +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_hwsim = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = hwsim_virtio_probe, + .remove = hwsim_virtio_remove, +}; + +static int hwsim_register_virtio_driver(void) +{ + return register_virtio_driver(&virtio_hwsim); +} + +static void hwsim_unregister_virtio_driver(void) +{ + unregister_virtio_driver(&virtio_hwsim); +} +#else +static inline int hwsim_register_virtio_driver(void) +{ + return 0; +} + +static inline void hwsim_unregister_virtio_driver(void) +{ +} +#endif + +static int __init init_mac80211_hwsim(void) +{ + int i, err; + + if (radios < 0 || radios > 100) + return -EINVAL; + + if (channels < 1) + return -EINVAL; + + err = rhashtable_init(&hwsim_radios_rht, &hwsim_rht_params); + if (err) + return err; + + err = register_pernet_device(&hwsim_net_ops); + if (err) + goto out_free_rht; + + err = platform_driver_register(&mac80211_hwsim_driver); + if (err) + goto out_unregister_pernet; + + err = hwsim_init_netlink(); + if (err) + goto out_unregister_driver; + + err = hwsim_register_virtio_driver(); + if (err) + goto out_exit_netlink; + + hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); + if (IS_ERR(hwsim_class)) { + err = PTR_ERR(hwsim_class); + goto out_exit_virtio; + } + + hwsim_init_s1g_channels(hwsim_channels_s1g); + + for (i = 0; i < radios; i++) { + struct hwsim_new_radio_params param = { 0 }; + + param.channels = channels; + + switch (regtest) { + case HWSIM_REGTEST_DIFF_COUNTRY: + if (i < ARRAY_SIZE(hwsim_alpha2s)) + param.reg_alpha2 = hwsim_alpha2s[i]; + break; + case HWSIM_REGTEST_DRIVER_REG_FOLLOW: + if (!i) + param.reg_alpha2 = hwsim_alpha2s[0]; + break; + case HWSIM_REGTEST_STRICT_ALL: + param.reg_strict = true; + fallthrough; + case HWSIM_REGTEST_DRIVER_REG_ALL: + param.reg_alpha2 = hwsim_alpha2s[0]; + break; + case HWSIM_REGTEST_WORLD_ROAM: + if (i == 0) + param.regd = &hwsim_world_regdom_custom_01; + break; + case HWSIM_REGTEST_CUSTOM_WORLD: + param.regd = &hwsim_world_regdom_custom_01; + break; + case HWSIM_REGTEST_CUSTOM_WORLD_2: + if (i == 0) + param.regd = &hwsim_world_regdom_custom_01; + else if (i == 1) + param.regd = &hwsim_world_regdom_custom_02; + break; + case HWSIM_REGTEST_STRICT_FOLLOW: + if (i == 0) { + param.reg_strict = true; + param.reg_alpha2 = hwsim_alpha2s[0]; + } + break; + case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: + if (i == 0) { + param.reg_strict = true; + param.reg_alpha2 = hwsim_alpha2s[0]; + } else if (i == 1) { + param.reg_alpha2 = hwsim_alpha2s[1]; + } + break; + case HWSIM_REGTEST_ALL: + switch (i) { + case 0: + param.regd = &hwsim_world_regdom_custom_01; + break; + case 1: + param.regd = &hwsim_world_regdom_custom_02; + break; + case 2: + param.reg_alpha2 = hwsim_alpha2s[0]; + break; + case 3: + param.reg_alpha2 = hwsim_alpha2s[1]; + break; + case 4: + param.reg_strict = true; + param.reg_alpha2 = hwsim_alpha2s[2]; + break; + } + break; + default: + break; + } + + param.p2p_device = support_p2p_device; + param.mlo = mlo; + param.use_chanctx = channels > 1 || mlo; + param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK; + if (param.p2p_device) + param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE); + + err = mac80211_hwsim_new_radio(NULL, ¶m); + if (err < 0) + goto out_free_radios; + } + + hwsim_mon = alloc_netdev(0, "hwsim%d", NET_NAME_UNKNOWN, + hwsim_mon_setup); + if (hwsim_mon == NULL) { + err = -ENOMEM; + goto out_free_radios; + } + + rtnl_lock(); + err = dev_alloc_name(hwsim_mon, hwsim_mon->name); + if (err < 0) { + rtnl_unlock(); + goto out_free_mon; + } + + err = register_netdevice(hwsim_mon); + if (err < 0) { + rtnl_unlock(); + goto out_free_mon; + } + rtnl_unlock(); + + return 0; + +out_free_mon: + free_netdev(hwsim_mon); +out_free_radios: + mac80211_hwsim_free(); +out_exit_virtio: + hwsim_unregister_virtio_driver(); +out_exit_netlink: + hwsim_exit_netlink(); +out_unregister_driver: + platform_driver_unregister(&mac80211_hwsim_driver); +out_unregister_pernet: + unregister_pernet_device(&hwsim_net_ops); +out_free_rht: + rhashtable_destroy(&hwsim_radios_rht); + return err; +} +module_init(init_mac80211_hwsim); + +static void __exit exit_mac80211_hwsim(void) +{ + pr_debug("mac80211_hwsim: unregister radios\n"); + + hwsim_unregister_virtio_driver(); + hwsim_exit_netlink(); + + mac80211_hwsim_free(); + + rhashtable_destroy(&hwsim_radios_rht); + unregister_netdev(hwsim_mon); + platform_driver_unregister(&mac80211_hwsim_driver); + unregister_pernet_device(&hwsim_net_ops); +} +module_exit(exit_mac80211_hwsim); diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.h b/drivers/net/wireless/virtual/mac80211_hwsim.h new file mode 100644 index 000000000000..527799b2de0f --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim.h @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 + * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2011, Javier Lopez + * Copyright (C) 2020, 2022 Intel Corporation + */ + +#ifndef __MAC80211_HWSIM_H +#define __MAC80211_HWSIM_H + +/** + * enum hwsim_tx_control_flags - flags to describe transmission info/status + * + * These flags are used to give the wmediumd extra information in order to + * modify its behavior for each frame + * + * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame. + * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack + * @HWSIM_TX_STAT_ACK: Frame was acknowledged + * + */ +enum hwsim_tx_control_flags { + HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0), + HWSIM_TX_CTL_NO_ACK = BIT(1), + HWSIM_TX_STAT_ACK = BIT(2), +}; + +/** + * DOC: Frame transmission/registration support + * + * Frame transmission and registration support exists to allow userspace + * entities such as wmediumd to receive and process all broadcasted + * frames from a mac80211_hwsim radio device. + * + * This allow user space applications to decide if the frame should be + * dropped or not and implement a wireless medium simulator at user space. + * + * Registration is done by sending a register message to the driver and + * will be automatically unregistered if the user application doesn't + * responds to sent frames. + * Once registered the user application has to take responsibility of + * broadcasting the frames to all listening mac80211_hwsim radio + * interfaces. + * + * For more technical details, see the corresponding command descriptions + * below. + */ + +/** + * enum hwsim_commands - supported hwsim commands + * + * @HWSIM_CMD_UNSPEC: unspecified command to catch errors + * + * @HWSIM_CMD_REGISTER: request to register and received all broadcasted + * frames by any mac80211_hwsim radio device. + * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user + * space, uses: + * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER, + * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE, + * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional) + * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to + * kernel, uses: + * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS, + * %HWSIM_ATTR_TX_INFO, %WSIM_ATTR_TX_INFO_FLAGS, + * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE + * @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters, + * returns the radio ID (>= 0) or negative on errors, if successful + * then multicast the result, uses optional parameter: + * %HWSIM_ATTR_REG_STRICT_REG, %HWSIM_ATTR_SUPPORT_P2P_DEVICE, + * %HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, %HWSIM_ATTR_CHANNELS, + * %HWSIM_ATTR_NO_VIF, %HWSIM_ATTR_RADIO_NAME, %HWSIM_ATTR_USE_CHANCTX, + * %HWSIM_ATTR_REG_HINT_ALPHA2, %HWSIM_ATTR_REG_CUSTOM_REG, + * %HWSIM_ATTR_PERM_ADDR + * @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted + * @HWSIM_CMD_GET_RADIO: fetch information about existing radios, uses: + * %HWSIM_ATTR_RADIO_ID + * @HWSIM_CMD_ADD_MAC_ADDR: add a receive MAC address (given in the + * %HWSIM_ATTR_ADDR_RECEIVER attribute) to a device identified by + * %HWSIM_ATTR_ADDR_TRANSMITTER. This lets wmediumd forward frames + * to this receiver address for a given station. + * @HWSIM_CMD_DEL_MAC_ADDR: remove the MAC address again, the attributes + * are the same as to @HWSIM_CMD_ADD_MAC_ADDR. + * @__HWSIM_CMD_MAX: enum limit + */ +enum { + HWSIM_CMD_UNSPEC, + HWSIM_CMD_REGISTER, + HWSIM_CMD_FRAME, + HWSIM_CMD_TX_INFO_FRAME, + HWSIM_CMD_NEW_RADIO, + HWSIM_CMD_DEL_RADIO, + HWSIM_CMD_GET_RADIO, + HWSIM_CMD_ADD_MAC_ADDR, + HWSIM_CMD_DEL_MAC_ADDR, + __HWSIM_CMD_MAX, +}; +#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1) + +#define HWSIM_CMD_CREATE_RADIO HWSIM_CMD_NEW_RADIO +#define HWSIM_CMD_DESTROY_RADIO HWSIM_CMD_DEL_RADIO + +/** + * enum hwsim_attrs - hwsim netlink attributes + * + * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that + * the frame is broadcasted to + * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that + * the frame was broadcasted from + * @HWSIM_ATTR_FRAME: Data array + * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process + properly the frame at user space + * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user + space + * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user + space + * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array + * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame + * @HWSIM_ATTR_CHANNELS: u32 attribute used with the %HWSIM_CMD_CREATE_RADIO + * command giving the number of channels supported by the new radio + * @HWSIM_ATTR_RADIO_ID: u32 attribute used with %HWSIM_CMD_DESTROY_RADIO + * only to destroy a radio + * @HWSIM_ATTR_REG_HINT_ALPHA2: alpha2 for regulatoro driver hint + * (nla string, length 2) + * @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute) + * @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute) + * @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag) + * @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO + * command to force use of channel contexts even when only a + * single channel is supported + * @HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE: used with the %HWSIM_CMD_CREATE_RADIO + * command to force radio removal when process that created the radio dies + * @HWSIM_ATTR_RADIO_NAME: Name of radio, e.g. phy666 + * @HWSIM_ATTR_NO_VIF: Do not create vif (wlanX) when creating radio. + * @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received. + * @HWSIM_ATTR_TX_INFO_FLAGS: additional flags for corresponding + * rates of %HWSIM_ATTR_TX_INFO + * @HWSIM_ATTR_PERM_ADDR: permanent mac address of new radio + * @HWSIM_ATTR_IFTYPE_SUPPORT: u32 attribute of supported interface types bits + * @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types + * @HWSIM_ATTR_MLO_SUPPORT: claim MLO support (exact parameters TBD) for + * the new radio + * @__HWSIM_ATTR_MAX: enum limit + */ + + +enum { + HWSIM_ATTR_UNSPEC, + HWSIM_ATTR_ADDR_RECEIVER, + HWSIM_ATTR_ADDR_TRANSMITTER, + HWSIM_ATTR_FRAME, + HWSIM_ATTR_FLAGS, + HWSIM_ATTR_RX_RATE, + HWSIM_ATTR_SIGNAL, + HWSIM_ATTR_TX_INFO, + HWSIM_ATTR_COOKIE, + HWSIM_ATTR_CHANNELS, + HWSIM_ATTR_RADIO_ID, + HWSIM_ATTR_REG_HINT_ALPHA2, + HWSIM_ATTR_REG_CUSTOM_REG, + HWSIM_ATTR_REG_STRICT_REG, + HWSIM_ATTR_SUPPORT_P2P_DEVICE, + HWSIM_ATTR_USE_CHANCTX, + HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, + HWSIM_ATTR_RADIO_NAME, + HWSIM_ATTR_NO_VIF, + HWSIM_ATTR_FREQ, + HWSIM_ATTR_PAD, + HWSIM_ATTR_TX_INFO_FLAGS, + HWSIM_ATTR_PERM_ADDR, + HWSIM_ATTR_IFTYPE_SUPPORT, + HWSIM_ATTR_CIPHER_SUPPORT, + HWSIM_ATTR_MLO_SUPPORT, + __HWSIM_ATTR_MAX, +}; +#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) + +/** + * struct hwsim_tx_rate - rate selection/status + * + * @idx: rate index to attempt to send with + * @count: number of tries in this rate before going to the next rate + * + * A value of -1 for @idx indicates an invalid rate and, if used + * in an array of retry rates, that no more rates should be tried. + * + * When used for transmit status reporting, the driver should + * always report the rate and number of retries used. + * + */ +struct hwsim_tx_rate { + s8 idx; + u8 count; +} __packed; + +/** + * enum hwsim_tx_rate_flags - per-rate flags set by the rate control algorithm. + * Inspired by structure mac80211_rate_control_flags. New flags may be + * appended, but old flags not deleted, to keep compatibility for + * userspace. + * + * These flags are set by the Rate control algorithm for each rate during tx, + * in the @flags member of struct ieee80211_tx_rate. + * + * @MAC80211_HWSIM_TX_RC_USE_RTS_CTS: Use RTS/CTS exchange for this rate. + * @MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT: CTS-to-self protection is required. + * This is set if the current BSS requires ERP protection. + * @MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE: Use short preamble. + * @MAC80211_HWSIM_TX_RC_MCS: HT rate. + * @MAC80211_HWSIM_TX_RC_VHT_MCS: VHT MCS rate, in this case the idx field is + * split into a higher 4 bits (Nss) and lower 4 bits (MCS number) + * @MAC80211_HWSIM_TX_RC_GREEN_FIELD: Indicates whether this rate should be used + * in Greenfield mode. + * @MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be + * 40 MHz. + * @MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH: Indicates 80 MHz transmission + * @MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH: Indicates 160 MHz transmission + * (80+80 isn't supported yet) + * @MAC80211_HWSIM_TX_RC_DUP_DATA: The frame should be transmitted on both of + * the adjacent 20 MHz channels, if the current channel type is + * NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS. + * @MAC80211_HWSIM_TX_RC_SHORT_GI: Short Guard interval should be used for this + * rate. + */ +enum hwsim_tx_rate_flags { + MAC80211_HWSIM_TX_RC_USE_RTS_CTS = BIT(0), + MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT = BIT(1), + MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE = BIT(2), + + /* rate index is an HT/VHT MCS instead of an index */ + MAC80211_HWSIM_TX_RC_MCS = BIT(3), + MAC80211_HWSIM_TX_RC_GREEN_FIELD = BIT(4), + MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH = BIT(5), + MAC80211_HWSIM_TX_RC_DUP_DATA = BIT(6), + MAC80211_HWSIM_TX_RC_SHORT_GI = BIT(7), + MAC80211_HWSIM_TX_RC_VHT_MCS = BIT(8), + MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH = BIT(9), + MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH = BIT(10), +}; + +/** + * struct hwsim_tx_rate - rate selection/status + * + * @idx: rate index to attempt to send with + * @count: number of tries in this rate before going to the next rate + * + * A value of -1 for @idx indicates an invalid rate and, if used + * in an array of retry rates, that no more rates should be tried. + * + * When used for transmit status reporting, the driver should + * always report the rate and number of retries used. + * + */ +struct hwsim_tx_rate_flag { + s8 idx; + u16 flags; +} __packed; + +/** + * DOC: Frame transmission support over virtio + * + * Frame transmission is also supported over virtio to allow communication + * with external entities. + */ + +/** + * enum hwsim_vqs - queues for virtio frame transmission + * + * @HWSIM_VQ_TX: send frames to external entity + * @HWSIM_VQ_RX: receive frames and transmission info reports + * @HWSIM_NUM_VQS: enum limit + */ +enum { + HWSIM_VQ_TX, + HWSIM_VQ_RX, + HWSIM_NUM_VQS, +}; +#endif /* __MAC80211_HWSIM_H */ diff --git a/drivers/net/wireless/virtual/virt_wifi.c b/drivers/net/wireless/virtual/virt_wifi.c new file mode 100644 index 000000000000..ba14d83353a4 --- /dev/null +++ b/drivers/net/wireless/virtual/virt_wifi.c @@ -0,0 +1,697 @@ +// SPDX-License-Identifier: GPL-2.0 +/* drivers/net/wireless/virt_wifi.c + * + * A fake implementation of cfg80211_ops that can be tacked on to an ethernet + * net_device to make it appear as a wireless connection. + * + * Copyright (C) 2018 Google, Inc. + * + * Author: schuffelen@google.com + */ + +#include +#include +#include +#include +#include + +static struct wiphy *common_wiphy; + +struct virt_wifi_wiphy_priv { + struct delayed_work scan_result; + struct cfg80211_scan_request *scan_request; + bool being_deleted; +}; + +static struct ieee80211_channel channel_2ghz = { + .band = NL80211_BAND_2GHZ, + .center_freq = 2432, + .hw_value = 2432, + .max_power = 20, +}; + +static struct ieee80211_rate bitrates_2ghz[] = { + { .bitrate = 10 }, + { .bitrate = 20 }, + { .bitrate = 55 }, + { .bitrate = 110 }, + { .bitrate = 60 }, + { .bitrate = 120 }, + { .bitrate = 240 }, +}; + +static struct ieee80211_supported_band band_2ghz = { + .channels = &channel_2ghz, + .bitrates = bitrates_2ghz, + .band = NL80211_BAND_2GHZ, + .n_channels = 1, + .n_bitrates = ARRAY_SIZE(bitrates_2ghz), + .ht_cap = { + .ht_supported = true, + .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40, + .ampdu_factor = 0x3, + .ampdu_density = 0x6, + .mcs = { + .rx_mask = {0xff, 0xff}, + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static struct ieee80211_channel channel_5ghz = { + .band = NL80211_BAND_5GHZ, + .center_freq = 5240, + .hw_value = 5240, + .max_power = 20, +}; + +static struct ieee80211_rate bitrates_5ghz[] = { + { .bitrate = 60 }, + { .bitrate = 120 }, + { .bitrate = 240 }, +}; + +#define RX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) + +#define TX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) + +static struct ieee80211_supported_band band_5ghz = { + .channels = &channel_5ghz, + .bitrates = bitrates_5ghz, + .band = NL80211_BAND_5GHZ, + .n_channels = 1, + .n_bitrates = ARRAY_SIZE(bitrates_5ghz), + .ht_cap = { + .ht_supported = true, + .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40, + .ampdu_factor = 0x3, + .ampdu_density = 0x6, + .mcs = { + .rx_mask = {0xff, 0xff}, + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, + .vht_cap = { + .vht_supported = true, + .cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_TXSTBC | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_RXSTBC_2 | + IEEE80211_VHT_CAP_RXSTBC_3 | + IEEE80211_VHT_CAP_RXSTBC_4 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + .vht_mcs = { + .rx_mcs_map = cpu_to_le16(RX_MCS_MAP), + .tx_mcs_map = cpu_to_le16(TX_MCS_MAP), + } + }, +}; + +/* Assigned at module init. Guaranteed locally-administered and unicast. */ +static u8 fake_router_bssid[ETH_ALEN] __ro_after_init = {}; + +static void virt_wifi_inform_bss(struct wiphy *wiphy) +{ + u64 tsf = div_u64(ktime_get_boottime_ns(), 1000); + struct cfg80211_bss *informed_bss; + static const struct { + u8 tag; + u8 len; + u8 ssid[8]; + } __packed ssid = { + .tag = WLAN_EID_SSID, + .len = 8, + .ssid = "VirtWifi", + }; + + informed_bss = cfg80211_inform_bss(wiphy, &channel_5ghz, + CFG80211_BSS_FTYPE_PRESP, + fake_router_bssid, tsf, + WLAN_CAPABILITY_ESS, 0, + (void *)&ssid, sizeof(ssid), + DBM_TO_MBM(-50), GFP_KERNEL); + cfg80211_put_bss(wiphy, informed_bss); +} + +/* Called with the rtnl lock held. */ +static int virt_wifi_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy); + + wiphy_debug(wiphy, "scan\n"); + + if (priv->scan_request || priv->being_deleted) + return -EBUSY; + + priv->scan_request = request; + schedule_delayed_work(&priv->scan_result, HZ * 2); + + return 0; +} + +/* Acquires and releases the rdev BSS lock. */ +static void virt_wifi_scan_result(struct work_struct *work) +{ + struct virt_wifi_wiphy_priv *priv = + container_of(work, struct virt_wifi_wiphy_priv, + scan_result.work); + struct wiphy *wiphy = priv_to_wiphy(priv); + struct cfg80211_scan_info scan_info = { .aborted = false }; + + virt_wifi_inform_bss(wiphy); + + /* Schedules work which acquires and releases the rtnl lock. */ + cfg80211_scan_done(priv->scan_request, &scan_info); + priv->scan_request = NULL; +} + +/* May acquire and release the rdev BSS lock. */ +static void virt_wifi_cancel_scan(struct wiphy *wiphy) +{ + struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy); + + cancel_delayed_work_sync(&priv->scan_result); + /* Clean up dangling callbacks if necessary. */ + if (priv->scan_request) { + struct cfg80211_scan_info scan_info = { .aborted = true }; + /* Schedules work which acquires and releases the rtnl lock. */ + cfg80211_scan_done(priv->scan_request, &scan_info); + priv->scan_request = NULL; + } +} + +struct virt_wifi_netdev_priv { + struct delayed_work connect; + struct net_device *lowerdev; + struct net_device *upperdev; + u32 tx_packets; + u32 tx_failed; + u8 connect_requested_bss[ETH_ALEN]; + bool is_up; + bool is_connected; + bool being_deleted; +}; + +/* Called with the rtnl lock held. */ +static int virt_wifi_connect(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_connect_params *sme) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(netdev); + bool could_schedule; + + if (priv->being_deleted || !priv->is_up) + return -EBUSY; + + could_schedule = schedule_delayed_work(&priv->connect, HZ * 2); + if (!could_schedule) + return -EBUSY; + + if (sme->bssid) { + ether_addr_copy(priv->connect_requested_bss, sme->bssid); + } else { + virt_wifi_inform_bss(wiphy); + eth_zero_addr(priv->connect_requested_bss); + } + + wiphy_debug(wiphy, "connect\n"); + + return 0; +} + +/* Acquires and releases the rdev event lock. */ +static void virt_wifi_connect_complete(struct work_struct *work) +{ + struct virt_wifi_netdev_priv *priv = + container_of(work, struct virt_wifi_netdev_priv, connect.work); + u8 *requested_bss = priv->connect_requested_bss; + bool right_addr = ether_addr_equal(requested_bss, fake_router_bssid); + u16 status = WLAN_STATUS_SUCCESS; + + if (is_zero_ether_addr(requested_bss)) + requested_bss = NULL; + + if (!priv->is_up || (requested_bss && !right_addr)) + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + else + priv->is_connected = true; + + /* Schedules an event that acquires the rtnl lock. */ + cfg80211_connect_result(priv->upperdev, requested_bss, NULL, 0, NULL, 0, + status, GFP_KERNEL); + netif_carrier_on(priv->upperdev); +} + +/* May acquire and release the rdev event lock. */ +static void virt_wifi_cancel_connect(struct net_device *netdev) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(netdev); + + /* If there is work pending, clean up dangling callbacks. */ + if (cancel_delayed_work_sync(&priv->connect)) { + /* Schedules an event that acquires the rtnl lock. */ + cfg80211_connect_result(priv->upperdev, + priv->connect_requested_bss, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } +} + +/* Called with the rtnl lock held. Acquires the rdev event lock. */ +static int virt_wifi_disconnect(struct wiphy *wiphy, struct net_device *netdev, + u16 reason_code) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(netdev); + + if (priv->being_deleted) + return -EBUSY; + + wiphy_debug(wiphy, "disconnect\n"); + virt_wifi_cancel_connect(netdev); + + cfg80211_disconnected(netdev, reason_code, NULL, 0, true, GFP_KERNEL); + priv->is_connected = false; + netif_carrier_off(netdev); + + return 0; +} + +/* Called with the rtnl lock held. */ +static int virt_wifi_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + + wiphy_debug(wiphy, "get_station\n"); + + if (!priv->is_connected || !ether_addr_equal(mac, fake_router_bssid)) + return -ENOENT; + + sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) | + BIT_ULL(NL80211_STA_INFO_TX_FAILED) | + BIT_ULL(NL80211_STA_INFO_SIGNAL) | + BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + sinfo->tx_packets = priv->tx_packets; + sinfo->tx_failed = priv->tx_failed; + /* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_ */ + sinfo->signal = -50; + sinfo->txrate = (struct rate_info) { + .legacy = 10, /* units are 100kbit/s */ + }; + return 0; +} + +/* Called with the rtnl lock held. */ +static int virt_wifi_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + + wiphy_debug(wiphy, "dump_station\n"); + + if (idx != 0 || !priv->is_connected) + return -ENOENT; + + ether_addr_copy(mac, fake_router_bssid); + return virt_wifi_get_station(wiphy, dev, fake_router_bssid, sinfo); +} + +static const struct cfg80211_ops virt_wifi_cfg80211_ops = { + .scan = virt_wifi_scan, + + .connect = virt_wifi_connect, + .disconnect = virt_wifi_disconnect, + + .get_station = virt_wifi_get_station, + .dump_station = virt_wifi_dump_station, +}; + +/* Acquires and releases the rtnl lock. */ +static struct wiphy *virt_wifi_make_wiphy(void) +{ + struct wiphy *wiphy; + struct virt_wifi_wiphy_priv *priv; + int err; + + wiphy = wiphy_new(&virt_wifi_cfg80211_ops, sizeof(*priv)); + + if (!wiphy) + return NULL; + + wiphy->max_scan_ssids = 4; + wiphy->max_scan_ie_len = 1000; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz; + wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz; + wiphy->bands[NL80211_BAND_60GHZ] = NULL; + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + priv = wiphy_priv(wiphy); + priv->being_deleted = false; + priv->scan_request = NULL; + INIT_DELAYED_WORK(&priv->scan_result, virt_wifi_scan_result); + + err = wiphy_register(wiphy); + if (err < 0) { + wiphy_free(wiphy); + return NULL; + } + + return wiphy; +} + +/* Acquires and releases the rtnl lock. */ +static void virt_wifi_destroy_wiphy(struct wiphy *wiphy) +{ + struct virt_wifi_wiphy_priv *priv; + + WARN(!wiphy, "%s called with null wiphy", __func__); + if (!wiphy) + return; + + priv = wiphy_priv(wiphy); + priv->being_deleted = true; + virt_wifi_cancel_scan(wiphy); + + if (wiphy->registered) + wiphy_unregister(wiphy); + wiphy_free(wiphy); +} + +/* Enters and exits a RCU-bh critical section. */ +static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + + priv->tx_packets++; + if (!priv->is_connected) { + priv->tx_failed++; + return NET_XMIT_DROP; + } + + skb->dev = priv->lowerdev; + return dev_queue_xmit(skb); +} + +/* Called with rtnl lock held. */ +static int virt_wifi_net_device_open(struct net_device *dev) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + + priv->is_up = true; + return 0; +} + +/* Called with rtnl lock held. */ +static int virt_wifi_net_device_stop(struct net_device *dev) +{ + struct virt_wifi_netdev_priv *n_priv = netdev_priv(dev); + + n_priv->is_up = false; + + if (!dev->ieee80211_ptr) + return 0; + + virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy); + virt_wifi_cancel_connect(dev); + netif_carrier_off(dev); + + return 0; +} + +static int virt_wifi_net_device_get_iflink(const struct net_device *dev) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + + return priv->lowerdev->ifindex; +} + +static const struct net_device_ops virt_wifi_ops = { + .ndo_start_xmit = virt_wifi_start_xmit, + .ndo_open = virt_wifi_net_device_open, + .ndo_stop = virt_wifi_net_device_stop, + .ndo_get_iflink = virt_wifi_net_device_get_iflink, +}; + +/* Invoked as part of rtnl lock release. */ +static void virt_wifi_net_device_destructor(struct net_device *dev) +{ + /* Delayed past dellink to allow nl80211 to react to the device being + * deleted. + */ + kfree(dev->ieee80211_ptr); + dev->ieee80211_ptr = NULL; +} + +/* No lock interaction. */ +static void virt_wifi_setup(struct net_device *dev) +{ + ether_setup(dev); + dev->netdev_ops = &virt_wifi_ops; + dev->needs_free_netdev = true; +} + +/* Called in a RCU read critical section from netif_receive_skb */ +static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + struct virt_wifi_netdev_priv *priv = + rcu_dereference(skb->dev->rx_handler_data); + + if (!priv->is_connected) + return RX_HANDLER_PASS; + + /* GFP_ATOMIC because this is a packet interrupt handler. */ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) { + dev_err(&priv->upperdev->dev, "can't skb_share_check\n"); + return RX_HANDLER_CONSUMED; + } + + *pskb = skb; + skb->dev = priv->upperdev; + skb->pkt_type = PACKET_HOST; + return RX_HANDLER_ANOTHER; +} + +/* Called with rtnl lock held. */ +static int virt_wifi_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + int err; + + if (!tb[IFLA_LINK]) + return -EINVAL; + + netif_carrier_off(dev); + + priv->upperdev = dev; + priv->lowerdev = __dev_get_by_index(src_net, + nla_get_u32(tb[IFLA_LINK])); + + if (!priv->lowerdev) + return -ENODEV; + if (!tb[IFLA_MTU]) + dev->mtu = priv->lowerdev->mtu; + else if (dev->mtu > priv->lowerdev->mtu) + return -EINVAL; + + err = netdev_rx_handler_register(priv->lowerdev, virt_wifi_rx_handler, + priv); + if (err) { + dev_err(&priv->lowerdev->dev, + "can't netdev_rx_handler_register: %d\n", err); + return err; + } + + eth_hw_addr_inherit(dev, priv->lowerdev); + netif_stacked_transfer_operstate(priv->lowerdev, dev); + + SET_NETDEV_DEV(dev, &priv->lowerdev->dev); + dev->ieee80211_ptr = kzalloc(sizeof(*dev->ieee80211_ptr), GFP_KERNEL); + + if (!dev->ieee80211_ptr) { + err = -ENOMEM; + goto remove_handler; + } + + dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; + dev->ieee80211_ptr->wiphy = common_wiphy; + + err = register_netdevice(dev); + if (err) { + dev_err(&priv->lowerdev->dev, "can't register_netdevice: %d\n", + err); + goto free_wireless_dev; + } + + err = netdev_upper_dev_link(priv->lowerdev, dev, extack); + if (err) { + dev_err(&priv->lowerdev->dev, "can't netdev_upper_dev_link: %d\n", + err); + goto unregister_netdev; + } + + dev->priv_destructor = virt_wifi_net_device_destructor; + priv->being_deleted = false; + priv->is_connected = false; + priv->is_up = false; + INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete); + __module_get(THIS_MODULE); + + return 0; +unregister_netdev: + unregister_netdevice(dev); +free_wireless_dev: + kfree(dev->ieee80211_ptr); + dev->ieee80211_ptr = NULL; +remove_handler: + netdev_rx_handler_unregister(priv->lowerdev); + + return err; +} + +/* Called with rtnl lock held. */ +static void virt_wifi_dellink(struct net_device *dev, + struct list_head *head) +{ + struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + + if (dev->ieee80211_ptr) + virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy); + + priv->being_deleted = true; + virt_wifi_cancel_connect(dev); + netif_carrier_off(dev); + + netdev_rx_handler_unregister(priv->lowerdev); + netdev_upper_dev_unlink(priv->lowerdev, dev); + + unregister_netdevice_queue(dev, head); + module_put(THIS_MODULE); + + /* Deleting the wiphy is handled in the module destructor. */ +} + +static struct rtnl_link_ops virt_wifi_link_ops = { + .kind = "virt_wifi", + .setup = virt_wifi_setup, + .newlink = virt_wifi_newlink, + .dellink = virt_wifi_dellink, + .priv_size = sizeof(struct virt_wifi_netdev_priv), +}; + +static bool netif_is_virt_wifi_dev(const struct net_device *dev) +{ + return rcu_access_pointer(dev->rx_handler) == virt_wifi_rx_handler; +} + +static int virt_wifi_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *lower_dev = netdev_notifier_info_to_dev(ptr); + struct virt_wifi_netdev_priv *priv; + struct net_device *upper_dev; + LIST_HEAD(list_kill); + + if (!netif_is_virt_wifi_dev(lower_dev)) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_UNREGISTER: + priv = rtnl_dereference(lower_dev->rx_handler_data); + if (!priv) + return NOTIFY_DONE; + + upper_dev = priv->upperdev; + + upper_dev->rtnl_link_ops->dellink(upper_dev, &list_kill); + unregister_netdevice_many(&list_kill); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block virt_wifi_notifier = { + .notifier_call = virt_wifi_event, +}; + +/* Acquires and releases the rtnl lock. */ +static int __init virt_wifi_init_module(void) +{ + int err; + + /* Guaranteed to be locally-administered and not multicast. */ + eth_random_addr(fake_router_bssid); + + err = register_netdevice_notifier(&virt_wifi_notifier); + if (err) + return err; + + err = -ENOMEM; + common_wiphy = virt_wifi_make_wiphy(); + if (!common_wiphy) + goto notifier; + + err = rtnl_link_register(&virt_wifi_link_ops); + if (err) + goto destroy_wiphy; + + return 0; + +destroy_wiphy: + virt_wifi_destroy_wiphy(common_wiphy); +notifier: + unregister_netdevice_notifier(&virt_wifi_notifier); + return err; +} + +/* Acquires and releases the rtnl lock. */ +static void __exit virt_wifi_cleanup_module(void) +{ + /* Will delete any devices that depend on the wiphy. */ + rtnl_link_unregister(&virt_wifi_link_ops); + virt_wifi_destroy_wiphy(common_wiphy); + unregister_netdevice_notifier(&virt_wifi_notifier); +} + +module_init(virt_wifi_init_module); +module_exit(virt_wifi_cleanup_module); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cody Schuffelen "); +MODULE_DESCRIPTION("Driver for a wireless wrapper of ethernet devices"); +MODULE_ALIAS_RTNL_LINK("virt_wifi"); -- cgit v1.2.3 From 298e50ad8eb8fa12ea68bb2da45bb8ef4edcd0ec Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Mon, 27 Feb 2023 14:17:32 +0200 Subject: wifi: move raycs, wl3501 and rndis_wlan to legacy directory To clean up drivers/net/wireless move the old drivers drivers left in the directory to a new "legacy" directory. I did consider adding CONFIG_WLAN_VENDOR_LEGACY like other vendors have but then dropped the idea as these are really old drivers and hopefully we get to remove them soon. There should be no changes in compilation or in Kconfig options, merely moving files. Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230227121732.8967-3-kvalo@kernel.org --- drivers/net/wireless/Kconfig | 55 +- drivers/net/wireless/Makefile | 7 +- drivers/net/wireless/legacy/Kconfig | 55 + drivers/net/wireless/legacy/Makefile | 6 + drivers/net/wireless/legacy/ray_cs.c | 2819 ++++++++++++++++++++++ drivers/net/wireless/legacy/ray_cs.h | 74 + drivers/net/wireless/legacy/rayctl.h | 734 ++++++ drivers/net/wireless/legacy/rndis_wlan.c | 3760 ++++++++++++++++++++++++++++++ drivers/net/wireless/legacy/wl3501.h | 615 +++++ drivers/net/wireless/legacy/wl3501_cs.c | 2030 ++++++++++++++++ drivers/net/wireless/ray_cs.c | 2819 ---------------------- drivers/net/wireless/ray_cs.h | 74 - drivers/net/wireless/rayctl.h | 734 ------ drivers/net/wireless/rndis_wlan.c | 3760 ------------------------------ drivers/net/wireless/wl3501.h | 615 ----- drivers/net/wireless/wl3501_cs.c | 2030 ---------------- 16 files changed, 10095 insertions(+), 10092 deletions(-) create mode 100644 drivers/net/wireless/legacy/Kconfig create mode 100644 drivers/net/wireless/legacy/Makefile create mode 100644 drivers/net/wireless/legacy/ray_cs.c create mode 100644 drivers/net/wireless/legacy/ray_cs.h create mode 100644 drivers/net/wireless/legacy/rayctl.h create mode 100644 drivers/net/wireless/legacy/rndis_wlan.c create mode 100644 drivers/net/wireless/legacy/wl3501.h create mode 100644 drivers/net/wireless/legacy/wl3501_cs.c delete mode 100644 drivers/net/wireless/ray_cs.c delete mode 100644 drivers/net/wireless/ray_cs.h delete mode 100644 drivers/net/wireless/rayctl.h delete mode 100644 drivers/net/wireless/rndis_wlan.c delete mode 100644 drivers/net/wireless/wl3501.h delete mode 100644 drivers/net/wireless/wl3501_cs.c (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 42b40cc96b21..7555af5195ec 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -38,60 +38,7 @@ source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zydas/Kconfig" source "drivers/net/wireless/quantenna/Kconfig" -config PCMCIA_RAYCS - tristate "Aviator/Raytheon 2.4GHz wireless support" - depends on PCMCIA - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - help - Say Y here if you intend to attach an Aviator/Raytheon PCMCIA - (PC-card) wireless Ethernet networking card to your computer. - Please read the file - for - details. - - To compile this driver as a module, choose M here: the module will be - called ray_cs. If unsure, say N. - -config PCMCIA_WL3501 - tristate "Planet WL3501 PCMCIA cards" - depends on CFG80211 && PCMCIA - select WIRELESS_EXT - select WEXT_SPY - help - A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. - It has basic support for Linux wireless extensions and initial - micro support for ethtool. - -config USB_NET_RNDIS_WLAN - tristate "Wireless RNDIS USB support" - depends on USB - depends on CFG80211 - select USB_NET_DRIVERS - select USB_USBNET - select USB_NET_CDCETHER - select USB_NET_RNDIS_HOST - help - This is a driver for wireless RNDIS devices. - These are USB based adapters found in devices such as: - - Buffalo WLI-U2-KG125S - U.S. Robotics USR5421 - Belkin F5D7051 - Linksys WUSB54GSv2 - Linksys WUSB54GSC - Asus WL169gE - Eminent EM4045 - BT Voyager 1055 - Linksys WUSB54GSv1 - U.S. Robotics USR5420 - BUFFALO WLI-USB-G54 - - All of these devices are based on Broadcom 4320 chip which is the - only wireless RNDIS chip known to date. - - If you choose to build a module, it'll be called rndis_wlan. +source "drivers/net/wireless/legacy/Kconfig" source "drivers/net/wireless/virtual/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 1b697cfe0a13..4d7374d567d1 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -23,10 +23,5 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_WLAN_VENDOR_TI) += ti/ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ -# 16-bit wireless PCMCIA client drivers -obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o -obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o - -obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o - +obj-$(CONFIG_WLAN) += legacy/ obj-$(CONFIG_WLAN) += virtual/ diff --git a/drivers/net/wireless/legacy/Kconfig b/drivers/net/wireless/legacy/Kconfig new file mode 100644 index 000000000000..3a5275941212 --- /dev/null +++ b/drivers/net/wireless/legacy/Kconfig @@ -0,0 +1,55 @@ +config PCMCIA_RAYCS + tristate "Aviator/Raytheon 2.4GHz wireless support" + depends on PCMCIA + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + help + Say Y here if you intend to attach an Aviator/Raytheon PCMCIA + (PC-card) wireless Ethernet networking card to your computer. + Please read the file + for + details. + + To compile this driver as a module, choose M here: the module will be + called ray_cs. If unsure, say N. + +config PCMCIA_WL3501 + tristate "Planet WL3501 PCMCIA cards" + depends on CFG80211 && PCMCIA + select WIRELESS_EXT + select WEXT_SPY + help + A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. + It has basic support for Linux wireless extensions and initial + micro support for ethtool. + +config USB_NET_RNDIS_WLAN + tristate "Wireless RNDIS USB support" + depends on USB + depends on CFG80211 + select USB_NET_DRIVERS + select USB_USBNET + select USB_NET_CDCETHER + select USB_NET_RNDIS_HOST + help + This is a driver for wireless RNDIS devices. + These are USB based adapters found in devices such as: + + Buffalo WLI-U2-KG125S + U.S. Robotics USR5421 + Belkin F5D7051 + Linksys WUSB54GSv2 + Linksys WUSB54GSC + Asus WL169gE + Eminent EM4045 + BT Voyager 1055 + Linksys WUSB54GSv1 + U.S. Robotics USR5420 + BUFFALO WLI-USB-G54 + + All of these devices are based on Broadcom 4320 chip which is the + only wireless RNDIS chip known to date. + + If you choose to build a module, it'll be called rndis_wlan. + diff --git a/drivers/net/wireless/legacy/Makefile b/drivers/net/wireless/legacy/Makefile new file mode 100644 index 000000000000..36878f080bfc --- /dev/null +++ b/drivers/net/wireless/legacy/Makefile @@ -0,0 +1,6 @@ +# 16-bit wireless PCMCIA client drivers +obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o +obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o + +obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o + diff --git a/drivers/net/wireless/legacy/ray_cs.c b/drivers/net/wireless/legacy/ray_cs.c new file mode 100644 index 000000000000..1f57a0055bbd --- /dev/null +++ b/drivers/net/wireless/legacy/ray_cs.c @@ -0,0 +1,2819 @@ +// SPDX-License-Identifier: GPL-2.0-only +/*============================================================================= + * + * A PCMCIA client driver for the Raylink wireless LAN card. + * The starting point for this module was the skeleton.c in the + * PCMCIA 2.9.12 package written by David Hinds, dahinds@users.sourceforge.net + * + * Copyright (c) 1998 Corey Thomas (corey@world.std.com) + * + * Changes: + * Arnaldo Carvalho de Melo - 08/08/2000 + * - reorganize kmallocs in ray_attach, checking all for failure + * and releasing the previous allocations if one fails + * + * Daniele Bellucci - 07/10/2003 + * - Audit copy_to_user in ioctl(SIOCGIWESSID) + * +=============================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* Warning : these stuff will slow down the driver... */ +#define WIRELESS_SPY /* Enable spying addresses */ +/* Definitions we need for spy */ +typedef struct iw_statistics iw_stats; +typedef u_char mac_addr[ETH_ALEN]; /* Hardware address */ + +#include "rayctl.h" +#include "ray_cs.h" + + +/** Prototypes based on PCMCIA skeleton driver *******************************/ +static int ray_config(struct pcmcia_device *link); +static void ray_release(struct pcmcia_device *link); +static void ray_detach(struct pcmcia_device *p_dev); + +/***** Prototypes indicated by device structure ******************************/ +static int ray_dev_close(struct net_device *dev); +static int ray_dev_config(struct net_device *dev, struct ifmap *map); +static struct net_device_stats *ray_get_stats(struct net_device *dev); +static int ray_dev_init(struct net_device *dev); + +static int ray_open(struct net_device *dev); +static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void ray_update_multi_list(struct net_device *dev, int all); +static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx, + unsigned char *data, int len); +static void ray_build_header(ray_dev_t *local, struct tx_msg __iomem *ptx, + UCHAR msg_type, unsigned char *data); +static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len); +static iw_stats *ray_get_wireless_stats(struct net_device *dev); +static const struct iw_handler_def ray_handler_def; + +/***** Prototypes for raylink functions **************************************/ +static void authenticate(ray_dev_t *local); +static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type); +static void authenticate_timeout(struct timer_list *t); +static int get_free_ccs(ray_dev_t *local); +static int get_free_tx_ccs(ray_dev_t *local); +static void init_startup_params(ray_dev_t *local); +static int parse_addr(char *in_str, UCHAR *out); +static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev, UCHAR type); +static int ray_init(struct net_device *dev); +static int interrupt_ecf(ray_dev_t *local, int ccs); +static void ray_reset(struct net_device *dev); +static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len); +static void verify_dl_startup(struct timer_list *t); + +/* Prototypes for interrpt time functions **********************************/ +static irqreturn_t ray_interrupt(int reg, void *dev_id); +static void clear_interrupt(ray_dev_t *local); +static void rx_deauthenticate(ray_dev_t *local, struct rcs __iomem *prcs, + unsigned int pkt_addr, int rx_len); +static int copy_from_rx_buff(ray_dev_t *local, UCHAR *dest, int pkt_addr, int len); +static void ray_rx(struct net_device *dev, ray_dev_t *local, struct rcs __iomem *prcs); +static void release_frag_chain(ray_dev_t *local, struct rcs __iomem *prcs); +static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs, + unsigned int pkt_addr, int rx_len); +static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, + unsigned int pkt_addr, int rx_len); +static void associate(ray_dev_t *local); + +/* Card command functions */ +static int dl_startup_params(struct net_device *dev); +static void join_net(struct timer_list *t); +static void start_net(struct timer_list *t); + +/*===========================================================================*/ +/* Parameters that can be set with 'insmod' */ + +/* ADHOC=0, Infrastructure=1 */ +static int net_type = ADHOC; + +/* Hop dwell time in Kus (1024 us units defined by 802.11) */ +static int hop_dwell = 128; + +/* Beacon period in Kus */ +static int beacon_period = 256; + +/* power save mode (0 = off, 1 = save power) */ +static int psm; + +/* String for network's Extended Service Set ID. 32 Characters max */ +static char *essid; + +/* Default to encapsulation unless translation requested */ +static bool translate = true; + +static int country = USA; + +static int sniffer; + +static int bc; + +/* 48 bit physical card address if overriding card's real physical + * address is required. Since IEEE 802.11 addresses are 48 bits + * like ethernet, an int can't be used, so a string is used. To + * allow use of addresses starting with a decimal digit, the first + * character must be a letter and will be ignored. This letter is + * followed by up to 12 hex digits which are the address. If less + * than 12 digits are used, the address will be left filled with 0's. + * Note that bit 0 of the first byte is the broadcast bit, and evil + * things will happen if it is not 0 in a card address. + */ +static char *phy_addr = NULL; + +static unsigned int ray_mem_speed = 500; + +/* WARNING: THIS DRIVER IS NOT CAPABLE OF HANDLING MULTIPLE DEVICES! */ +static struct pcmcia_device *this_device = NULL; + +MODULE_AUTHOR("Corey Thomas "); +MODULE_DESCRIPTION("Raylink/WebGear wireless LAN driver"); +MODULE_LICENSE("GPL"); + +module_param(net_type, int, 0); +module_param(hop_dwell, int, 0); +module_param(beacon_period, int, 0); +module_param(psm, int, 0); +module_param(essid, charp, 0); +module_param(translate, bool, 0); +module_param(country, int, 0); +module_param(sniffer, int, 0); +module_param(bc, int, 0); +module_param(phy_addr, charp, 0); +module_param(ray_mem_speed, int, 0); + +static const UCHAR b5_default_startup_parms[] = { + 0, 0, /* Adhoc station */ + 'L', 'I', 'N', 'U', 'X', 0, 0, 0, /* 32 char ESSID */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, /* Active scan, CA Mode */ + 0, 0, 0, 0, 0, 0, /* No default MAC addr */ + 0x7f, 0xff, /* Frag threshold */ + 0x00, 0x80, /* Hop time 128 Kus */ + 0x01, 0x00, /* Beacon period 256 Kus */ + 0x01, 0x07, 0xa3, /* DTIM, retries, ack timeout */ + 0x1d, 0x82, 0x4e, /* SIFS, DIFS, PIFS */ + 0x7f, 0xff, /* RTS threshold */ + 0x04, 0xe2, 0x38, 0xA4, /* scan_dwell, max_scan_dwell */ + 0x05, /* assoc resp timeout thresh */ + 0x08, 0x02, 0x08, /* adhoc, infra, super cycle max */ + 0, /* Promiscuous mode */ + 0x0c, 0x0bd, /* Unique word */ + 0x32, /* Slot time */ + 0xff, 0xff, /* roam-low snr, low snr count */ + 0x05, 0xff, /* Infra, adhoc missed bcn thresh */ + 0x01, 0x0b, 0x4f, /* USA, hop pattern, hop pat length */ +/* b4 - b5 differences start here */ + 0x00, 0x3f, /* CW max */ + 0x00, 0x0f, /* CW min */ + 0x04, 0x08, /* Noise gain, limit offset */ + 0x28, 0x28, /* det rssi, med busy offsets */ + 7, /* det sync thresh */ + 0, 2, 2, /* test mode, min, max */ + 0, /* allow broadcast SSID probe resp */ + 0, 0, /* privacy must start, can join */ + 2, 0, 0, 0, 0, 0, 0, 0 /* basic rate set */ +}; + +static const UCHAR b4_default_startup_parms[] = { + 0, 0, /* Adhoc station */ + 'L', 'I', 'N', 'U', 'X', 0, 0, 0, /* 32 char ESSID */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, /* Active scan, CA Mode */ + 0, 0, 0, 0, 0, 0, /* No default MAC addr */ + 0x7f, 0xff, /* Frag threshold */ + 0x02, 0x00, /* Hop time */ + 0x00, 0x01, /* Beacon period */ + 0x01, 0x07, 0xa3, /* DTIM, retries, ack timeout */ + 0x1d, 0x82, 0xce, /* SIFS, DIFS, PIFS */ + 0x7f, 0xff, /* RTS threshold */ + 0xfb, 0x1e, 0xc7, 0x5c, /* scan_dwell, max_scan_dwell */ + 0x05, /* assoc resp timeout thresh */ + 0x04, 0x02, 0x4, /* adhoc, infra, super cycle max */ + 0, /* Promiscuous mode */ + 0x0c, 0x0bd, /* Unique word */ + 0x4e, /* Slot time (TBD seems wrong) */ + 0xff, 0xff, /* roam-low snr, low snr count */ + 0x05, 0xff, /* Infra, adhoc missed bcn thresh */ + 0x01, 0x0b, 0x4e, /* USA, hop pattern, hop pat length */ +/* b4 - b5 differences start here */ + 0x3f, 0x0f, /* CW max, min */ + 0x04, 0x08, /* Noise gain, limit offset */ + 0x28, 0x28, /* det rssi, med busy offsets */ + 7, /* det sync thresh */ + 0, 2, 2, /* test mode, min, max */ + 0, /* rx/tx delay */ + 0, 0, 0, 0, 0, 0, /* current BSS id */ + 0 /* hop set */ +}; + +/*===========================================================================*/ +static const u8 eth2_llc[] = { 0xaa, 0xaa, 3, 0, 0, 0 }; + +static const char hop_pattern_length[] = { 1, + USA_HOP_MOD, EUROPE_HOP_MOD, + JAPAN_HOP_MOD, KOREA_HOP_MOD, + SPAIN_HOP_MOD, FRANCE_HOP_MOD, + ISRAEL_HOP_MOD, AUSTRALIA_HOP_MOD, + JAPAN_TEST_HOP_MOD +}; + +static const char rcsid[] = + "Raylink/WebGear wireless LAN - Corey "; + +static const struct net_device_ops ray_netdev_ops = { + .ndo_init = ray_dev_init, + .ndo_open = ray_open, + .ndo_stop = ray_dev_close, + .ndo_start_xmit = ray_dev_start_xmit, + .ndo_set_config = ray_dev_config, + .ndo_get_stats = ray_get_stats, + .ndo_set_rx_mode = set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int ray_probe(struct pcmcia_device *p_dev) +{ + ray_dev_t *local; + struct net_device *dev; + + dev_dbg(&p_dev->dev, "ray_attach()\n"); + + /* Allocate space for private device-specific data */ + dev = alloc_etherdev(sizeof(ray_dev_t)); + if (!dev) + goto fail_alloc_dev; + + local = netdev_priv(dev); + local->finder = p_dev; + + /* The io structure describes IO port mapping. None used here */ + p_dev->resource[0]->end = 0; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + /* General socket configuration */ + p_dev->config_flags |= CONF_ENABLE_IRQ; + p_dev->config_index = 1; + + p_dev->priv = dev; + + local->finder = p_dev; + local->card_status = CARD_INSERTED; + local->authentication_state = UNAUTHENTICATED; + local->num_multi = 0; + dev_dbg(&p_dev->dev, "ray_attach p_dev = %p, dev = %p, local = %p, intr = %p\n", + p_dev, dev, local, &ray_interrupt); + + /* Raylink entries in the device structure */ + dev->netdev_ops = &ray_netdev_ops; + dev->wireless_handlers = &ray_handler_def; +#ifdef WIRELESS_SPY + local->wireless_data.spy_data = &local->spy_data; + dev->wireless_data = &local->wireless_data; +#endif /* WIRELESS_SPY */ + + + dev_dbg(&p_dev->dev, "ray_cs ray_attach calling ether_setup.)\n"); + netif_stop_queue(dev); + + timer_setup(&local->timer, NULL, 0); + + this_device = p_dev; + return ray_config(p_dev); + +fail_alloc_dev: + return -ENOMEM; +} /* ray_attach */ + +static void ray_detach(struct pcmcia_device *link) +{ + struct net_device *dev; + ray_dev_t *local; + + dev_dbg(&link->dev, "ray_detach\n"); + + this_device = NULL; + dev = link->priv; + + ray_release(link); + + local = netdev_priv(dev); + del_timer_sync(&local->timer); + + if (link->priv) { + unregister_netdev(dev); + free_netdev(dev); + } + dev_dbg(&link->dev, "ray_cs ray_detach ending\n"); +} /* ray_detach */ + +#define MAX_TUPLE_SIZE 128 +static int ray_config(struct pcmcia_device *link) +{ + int ret = 0; + int i; + struct net_device *dev = (struct net_device *)link->priv; + ray_dev_t *local = netdev_priv(dev); + + dev_dbg(&link->dev, "ray_config\n"); + + /* Determine card type and firmware version */ + printk(KERN_INFO "ray_cs Detected: %s%s%s%s\n", + link->prod_id[0] ? link->prod_id[0] : " ", + link->prod_id[1] ? link->prod_id[1] : " ", + link->prod_id[2] ? link->prod_id[2] : " ", + link->prod_id[3] ? link->prod_id[3] : " "); + + /* Now allocate an interrupt line. Note that this does not + actually assign a handler to the interrupt. + */ + ret = pcmcia_request_irq(link, ray_interrupt); + if (ret) + goto failed; + dev->irq = link->irq; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + +/*** Set up 32k window for shared memory (transmit and control) ************/ + link->resource[2]->flags |= WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; + link->resource[2]->start = 0; + link->resource[2]->end = 0x8000; + ret = pcmcia_request_window(link, link->resource[2], ray_mem_speed); + if (ret) + goto failed; + ret = pcmcia_map_mem_page(link, link->resource[2], 0); + if (ret) + goto failed; + local->sram = ioremap(link->resource[2]->start, + resource_size(link->resource[2])); + if (!local->sram) + goto failed; + +/*** Set up 16k window for shared memory (receive buffer) ***************/ + link->resource[3]->flags |= + WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; + link->resource[3]->start = 0; + link->resource[3]->end = 0x4000; + ret = pcmcia_request_window(link, link->resource[3], ray_mem_speed); + if (ret) + goto failed; + ret = pcmcia_map_mem_page(link, link->resource[3], 0x8000); + if (ret) + goto failed; + local->rmem = ioremap(link->resource[3]->start, + resource_size(link->resource[3])); + if (!local->rmem) + goto failed; + +/*** Set up window for attribute memory ***********************************/ + link->resource[4]->flags |= + WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_AM | WIN_ENABLE | WIN_USE_WAIT; + link->resource[4]->start = 0; + link->resource[4]->end = 0x1000; + ret = pcmcia_request_window(link, link->resource[4], ray_mem_speed); + if (ret) + goto failed; + ret = pcmcia_map_mem_page(link, link->resource[4], 0); + if (ret) + goto failed; + local->amem = ioremap(link->resource[4]->start, + resource_size(link->resource[4])); + if (!local->amem) + goto failed; + + dev_dbg(&link->dev, "ray_config sram=%p\n", local->sram); + dev_dbg(&link->dev, "ray_config rmem=%p\n", local->rmem); + dev_dbg(&link->dev, "ray_config amem=%p\n", local->amem); + if (ray_init(dev) < 0) { + ray_release(link); + return -ENODEV; + } + + SET_NETDEV_DEV(dev, &link->dev); + i = register_netdev(dev); + if (i != 0) { + printk("ray_config register_netdev() failed\n"); + ray_release(link); + return i; + } + + printk(KERN_INFO "%s: RayLink, irq %d, hw_addr %pM\n", + dev->name, dev->irq, dev->dev_addr); + + return 0; + +failed: + ray_release(link); + return -ENODEV; +} /* ray_config */ + +static inline struct ccs __iomem *ccs_base(ray_dev_t *dev) +{ + return dev->sram + CCS_BASE; +} + +static inline struct rcs __iomem *rcs_base(ray_dev_t *dev) +{ + /* + * This looks nonsensical, since there is a separate + * RCS_BASE. But the difference between a "struct rcs" + * and a "struct ccs" ends up being in the _index_ off + * the base, so the base pointer is the same for both + * ccs/rcs. + */ + return dev->sram + CCS_BASE; +} + +/*===========================================================================*/ +static int ray_init(struct net_device *dev) +{ + int i; + struct ccs __iomem *pccs; + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + dev_dbg(&link->dev, "ray_init(0x%p)\n", dev); + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_init - device not present\n"); + return -1; + } + + local->net_type = net_type; + local->sta_type = TYPE_STA; + + /* Copy the startup results to local memory */ + memcpy_fromio(&local->startup_res, local->sram + ECF_TO_HOST_BASE, + sizeof(struct startup_res_6)); + + /* Check Power up test status and get mac address from card */ + if (local->startup_res.startup_word != 0x80) { + printk(KERN_INFO "ray_init ERROR card status = %2x\n", + local->startup_res.startup_word); + local->card_status = CARD_INIT_ERROR; + return -1; + } + + local->fw_ver = local->startup_res.firmware_version[0]; + local->fw_bld = local->startup_res.firmware_version[1]; + local->fw_var = local->startup_res.firmware_version[2]; + dev_dbg(&link->dev, "ray_init firmware version %d.%d\n", local->fw_ver, + local->fw_bld); + + local->tib_length = 0x20; + if ((local->fw_ver == 5) && (local->fw_bld >= 30)) + local->tib_length = local->startup_res.tib_length; + dev_dbg(&link->dev, "ray_init tib_length = 0x%02x\n", local->tib_length); + /* Initialize CCS's to buffer free state */ + pccs = ccs_base(local); + for (i = 0; i < NUMBER_OF_CCS; i++) { + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + } + init_startup_params(local); + + /* copy mac address to startup parameters */ + if (!parse_addr(phy_addr, local->sparm.b4.a_mac_addr)) { + memcpy(&local->sparm.b4.a_mac_addr, + &local->startup_res.station_addr, ADDRLEN); + } + + clear_interrupt(local); /* Clear any interrupt from the card */ + local->card_status = CARD_AWAITING_PARAM; + dev_dbg(&link->dev, "ray_init ending\n"); + return 0; +} /* ray_init */ + +/*===========================================================================*/ +/* Download startup parameters to the card and command it to read them */ +static int dl_startup_params(struct net_device *dev) +{ + int ccsindex; + ray_dev_t *local = netdev_priv(dev); + struct ccs __iomem *pccs; + struct pcmcia_device *link = local->finder; + + dev_dbg(&link->dev, "dl_startup_params entered\n"); + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs dl_startup_params - device not present\n"); + return -1; + } + + /* Copy parameters to host to ECF area */ + if (local->fw_ver == 0x55) + memcpy_toio(local->sram + HOST_TO_ECF_BASE, &local->sparm.b4, + sizeof(struct b4_startup_params)); + else + memcpy_toio(local->sram + HOST_TO_ECF_BASE, &local->sparm.b5, + sizeof(struct b5_startup_params)); + + /* Fill in the CCS fields for the ECF */ + if ((ccsindex = get_free_ccs(local)) < 0) + return -1; + local->dl_param_ccs = ccsindex; + pccs = ccs_base(local) + ccsindex; + writeb(CCS_DOWNLOAD_STARTUP_PARAMS, &pccs->cmd); + dev_dbg(&link->dev, "dl_startup_params start ccsindex = %d\n", + local->dl_param_ccs); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + printk(KERN_INFO "ray dl_startup_params failed - " + "ECF not ready for intr\n"); + local->card_status = CARD_DL_PARAM_ERROR; + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return -2; + } + local->card_status = CARD_DL_PARAM; + /* Start kernel timer to wait for dl startup to complete. */ + local->timer.expires = jiffies + HZ / 2; + local->timer.function = verify_dl_startup; + add_timer(&local->timer); + dev_dbg(&link->dev, + "ray_cs dl_startup_params started timer for verify_dl_startup\n"); + return 0; +} /* dl_startup_params */ + +/*===========================================================================*/ +static void init_startup_params(ray_dev_t *local) +{ + int i; + + if (country > JAPAN_TEST) + country = USA; + else if (country < USA) + country = USA; + /* structure for hop time and beacon period is defined here using + * New 802.11D6.1 format. Card firmware is still using old format + * until version 6. + * Before After + * a_hop_time ms byte a_hop_time ms byte + * a_hop_time 2s byte a_hop_time ls byte + * a_hop_time ls byte a_beacon_period ms byte + * a_beacon_period a_beacon_period ls byte + * + * a_hop_time = uS a_hop_time = KuS + * a_beacon_period = hops a_beacon_period = KuS + *//* 64ms = 010000 */ + if (local->fw_ver == 0x55) { + memcpy(&local->sparm.b4, b4_default_startup_parms, + sizeof(struct b4_startup_params)); + /* Translate sane kus input values to old build 4/5 format */ + /* i = hop time in uS truncated to 3 bytes */ + i = (hop_dwell * 1024) & 0xffffff; + local->sparm.b4.a_hop_time[0] = (i >> 16) & 0xff; + local->sparm.b4.a_hop_time[1] = (i >> 8) & 0xff; + local->sparm.b4.a_beacon_period[0] = 0; + local->sparm.b4.a_beacon_period[1] = + ((beacon_period / hop_dwell) - 1) & 0xff; + local->sparm.b4.a_curr_country_code = country; + local->sparm.b4.a_hop_pattern_length = + hop_pattern_length[(int)country] - 1; + if (bc) { + local->sparm.b4.a_ack_timeout = 0x50; + local->sparm.b4.a_sifs = 0x3f; + } + } else { /* Version 5 uses real kus values */ + memcpy((UCHAR *) &local->sparm.b5, b5_default_startup_parms, + sizeof(struct b5_startup_params)); + + local->sparm.b5.a_hop_time[0] = (hop_dwell >> 8) & 0xff; + local->sparm.b5.a_hop_time[1] = hop_dwell & 0xff; + local->sparm.b5.a_beacon_period[0] = + (beacon_period >> 8) & 0xff; + local->sparm.b5.a_beacon_period[1] = beacon_period & 0xff; + if (psm) + local->sparm.b5.a_power_mgt_state = 1; + local->sparm.b5.a_curr_country_code = country; + local->sparm.b5.a_hop_pattern_length = + hop_pattern_length[(int)country]; + } + + local->sparm.b4.a_network_type = net_type & 0x01; + local->sparm.b4.a_acting_as_ap_status = TYPE_STA; + + if (essid != NULL) + strncpy(local->sparm.b4.a_current_ess_id, essid, ESSID_SIZE); +} /* init_startup_params */ + +/*===========================================================================*/ +static void verify_dl_startup(struct timer_list *t) +{ + ray_dev_t *local = from_timer(local, t, timer); + struct ccs __iomem *pccs = ccs_base(local) + local->dl_param_ccs; + UCHAR status; + struct pcmcia_device *link = local->finder; + + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs verify_dl_startup - device not present\n"); + return; + } +#if 0 + { + int i; + printk(KERN_DEBUG + "verify_dl_startup parameters sent via ccs %d:\n", + local->dl_param_ccs); + for (i = 0; i < sizeof(struct b5_startup_params); i++) { + printk(" %2x", + (unsigned int)readb(local->sram + + HOST_TO_ECF_BASE + i)); + } + printk("\n"); + } +#endif + + status = readb(&pccs->buffer_status); + if (status != CCS_BUFFER_FREE) { + printk(KERN_INFO + "Download startup params failed. Status = %d\n", + status); + local->card_status = CARD_DL_PARAM_ERROR; + return; + } + if (local->sparm.b4.a_network_type == ADHOC) + start_net(&local->timer); + else + join_net(&local->timer); +} /* end verify_dl_startup */ + +/*===========================================================================*/ +/* Command card to start a network */ +static void start_net(struct timer_list *t) +{ + ray_dev_t *local = from_timer(local, t, timer); + struct ccs __iomem *pccs; + int ccsindex; + struct pcmcia_device *link = local->finder; + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs start_net - device not present\n"); + return; + } + /* Fill in the CCS fields for the ECF */ + if ((ccsindex = get_free_ccs(local)) < 0) + return; + pccs = ccs_base(local) + ccsindex; + writeb(CCS_START_NETWORK, &pccs->cmd); + writeb(0, &pccs->var.start_network.update_param); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + dev_dbg(&link->dev, "ray start net failed - card not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return; + } + local->card_status = CARD_DOING_ACQ; +} /* end start_net */ + +/*===========================================================================*/ +/* Command card to join a network */ +static void join_net(struct timer_list *t) +{ + ray_dev_t *local = from_timer(local, t, timer); + + struct ccs __iomem *pccs; + int ccsindex; + struct pcmcia_device *link = local->finder; + + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs join_net - device not present\n"); + return; + } + /* Fill in the CCS fields for the ECF */ + if ((ccsindex = get_free_ccs(local)) < 0) + return; + pccs = ccs_base(local) + ccsindex; + writeb(CCS_JOIN_NETWORK, &pccs->cmd); + writeb(0, &pccs->var.join_network.update_param); + writeb(0, &pccs->var.join_network.net_initiated); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + dev_dbg(&link->dev, "ray join net failed - card not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return; + } + local->card_status = CARD_DOING_ACQ; +} + + +static void ray_release(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + ray_dev_t *local = netdev_priv(dev); + + dev_dbg(&link->dev, "ray_release\n"); + + del_timer(&local->timer); + + iounmap(local->sram); + iounmap(local->rmem); + iounmap(local->amem); + pcmcia_disable_device(link); + + dev_dbg(&link->dev, "ray_release ending\n"); +} + +static int ray_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int ray_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) { + ray_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + +/*===========================================================================*/ +static int ray_dev_init(struct net_device *dev) +{ +#ifdef RAY_IMMEDIATE_INIT + int i; +#endif /* RAY_IMMEDIATE_INIT */ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + + dev_dbg(&link->dev, "ray_dev_init(dev=%p)\n", dev); + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_dev_init - device not present\n"); + return -1; + } +#ifdef RAY_IMMEDIATE_INIT + /* Download startup parameters */ + if ((i = dl_startup_params(dev)) < 0) { + printk(KERN_INFO "ray_dev_init dl_startup_params failed - " + "returns 0x%x\n", i); + return -1; + } +#else /* RAY_IMMEDIATE_INIT */ + /* Postpone the card init so that we can still configure the card, + * for example using the Wireless Extensions. The init will happen + * in ray_open() - Jean II */ + dev_dbg(&link->dev, + "ray_dev_init: postponing card init to ray_open() ; Status = %d\n", + local->card_status); +#endif /* RAY_IMMEDIATE_INIT */ + + /* copy mac and broadcast addresses to linux device */ + eth_hw_addr_set(dev, local->sparm.b4.a_mac_addr); + eth_broadcast_addr(dev->broadcast); + + dev_dbg(&link->dev, "ray_dev_init ending\n"); + return 0; +} + +/*===========================================================================*/ +static int ray_dev_config(struct net_device *dev, struct ifmap *map) +{ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + /* Dummy routine to satisfy device structure */ + dev_dbg(&link->dev, "ray_dev_config(dev=%p,ifmap=%p)\n", dev, map); + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_dev_config - device not present\n"); + return -1; + } + + return 0; +} + +/*===========================================================================*/ +static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + short length = skb->len; + + if (!pcmcia_dev_present(link)) { + dev_dbg(&link->dev, "ray_dev_start_xmit - device not present\n"); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + dev_dbg(&link->dev, "ray_dev_start_xmit(skb=%p, dev=%p)\n", skb, dev); + if (local->authentication_state == NEED_TO_AUTH) { + dev_dbg(&link->dev, "ray_cs Sending authentication request.\n"); + if (!build_auth_frame(local, local->auth_id, OPEN_AUTH_REQUEST)) { + local->authentication_state = AUTHENTICATED; + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } + } + + if (length < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + switch (ray_hw_xmit(skb->data, length, dev, DATA_TYPE)) { + case XMIT_NO_CCS: + case XMIT_NEED_AUTH: + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + case XMIT_NO_INTR: + case XMIT_MSG_BAD: + case XMIT_OK: + default: + dev_kfree_skb(skb); + } + + return NETDEV_TX_OK; +} /* ray_dev_start_xmit */ + +/*===========================================================================*/ +static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev, + UCHAR msg_type) +{ + ray_dev_t *local = netdev_priv(dev); + struct ccs __iomem *pccs; + int ccsindex; + int offset; + struct tx_msg __iomem *ptx; /* Address of xmit buffer in PC space */ + short int addr; /* Address of xmit buffer in card space */ + + pr_debug("ray_hw_xmit(data=%p, len=%d, dev=%p)\n", data, len, dev); + if (len + TX_HEADER_LENGTH > TX_BUF_SIZE) { + printk(KERN_INFO "ray_hw_xmit packet too large: %d bytes\n", + len); + return XMIT_MSG_BAD; + } + switch (ccsindex = get_free_tx_ccs(local)) { + case ECCSBUSY: + pr_debug("ray_hw_xmit tx_ccs table busy\n"); + fallthrough; + case ECCSFULL: + pr_debug("ray_hw_xmit No free tx ccs\n"); + fallthrough; + case ECARDGONE: + netif_stop_queue(dev); + return XMIT_NO_CCS; + default: + break; + } + addr = TX_BUF_BASE + (ccsindex << 11); + + if (msg_type == DATA_TYPE) { + local->stats.tx_bytes += len; + local->stats.tx_packets++; + } + + ptx = local->sram + addr; + + ray_build_header(local, ptx, msg_type, data); + if (translate) { + offset = translate_frame(local, ptx, data, len); + } else { /* Encapsulate frame */ + /* TBD TIB length will move address of ptx->var */ + memcpy_toio(&ptx->var, data, len); + offset = 0; + } + + /* fill in the CCS */ + pccs = ccs_base(local) + ccsindex; + len += TX_HEADER_LENGTH + offset; + writeb(CCS_TX_REQUEST, &pccs->cmd); + writeb(addr >> 8, &pccs->var.tx_request.tx_data_ptr[0]); + writeb(local->tib_length, &pccs->var.tx_request.tx_data_ptr[1]); + writeb(len >> 8, &pccs->var.tx_request.tx_data_length[0]); + writeb(len & 0xff, &pccs->var.tx_request.tx_data_length[1]); +/* TBD still need psm_cam? */ + writeb(PSM_CAM, &pccs->var.tx_request.pow_sav_mode); + writeb(local->net_default_tx_rate, &pccs->var.tx_request.tx_rate); + writeb(0, &pccs->var.tx_request.antenna); + pr_debug("ray_hw_xmit default_tx_rate = 0x%x\n", + local->net_default_tx_rate); + + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + pr_debug("ray_hw_xmit failed - ECF not ready for intr\n"); +/* TBD very inefficient to copy packet to buffer, and then not + send it, but the alternative is to queue the messages and that + won't be done for a while. Maybe set tbusy until a CCS is free? +*/ + writeb(CCS_BUFFER_FREE, &pccs->buffer_status); + return XMIT_NO_INTR; + } + return XMIT_OK; +} /* end ray_hw_xmit */ + +/*===========================================================================*/ +static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx, + unsigned char *data, int len) +{ + __be16 proto = ((struct ethhdr *)data)->h_proto; + if (ntohs(proto) >= ETH_P_802_3_MIN) { /* DIX II ethernet frame */ + pr_debug("ray_cs translate_frame DIX II\n"); + /* Copy LLC header to card buffer */ + memcpy_toio(&ptx->var, eth2_llc, sizeof(eth2_llc)); + memcpy_toio(((void __iomem *)&ptx->var) + sizeof(eth2_llc), + (UCHAR *) &proto, 2); + if (proto == htons(ETH_P_AARP) || proto == htons(ETH_P_IPX)) { + /* This is the selective translation table, only 2 entries */ + writeb(0xf8, + &((struct snaphdr_t __iomem *)ptx->var)->org[2]); + } + /* Copy body of ethernet packet without ethernet header */ + memcpy_toio((void __iomem *)&ptx->var + + sizeof(struct snaphdr_t), data + ETH_HLEN, + len - ETH_HLEN); + return (int)sizeof(struct snaphdr_t) - ETH_HLEN; + } else { /* already 802 type, and proto is length */ + pr_debug("ray_cs translate_frame 802\n"); + if (proto == htons(0xffff)) { /* evil netware IPX 802.3 without LLC */ + pr_debug("ray_cs translate_frame evil IPX\n"); + memcpy_toio(&ptx->var, data + ETH_HLEN, len - ETH_HLEN); + return 0 - ETH_HLEN; + } + memcpy_toio(&ptx->var, data + ETH_HLEN, len - ETH_HLEN); + return 0 - ETH_HLEN; + } + /* TBD do other frame types */ +} /* end translate_frame */ + +/*===========================================================================*/ +static void ray_build_header(ray_dev_t *local, struct tx_msg __iomem *ptx, + UCHAR msg_type, unsigned char *data) +{ + writeb(PROTOCOL_VER | msg_type, &ptx->mac.frame_ctl_1); +/*** IEEE 802.11 Address field assignments ************* + TODS FROMDS addr_1 addr_2 addr_3 addr_4 +Adhoc 0 0 dest src (terminal) BSSID N/A +AP to Terminal 0 1 dest AP(BSSID) source N/A +Terminal to AP 1 0 AP(BSSID) src (terminal) dest N/A +AP to AP 1 1 dest AP src AP dest source +*******************************************************/ + if (local->net_type == ADHOC) { + writeb(0, &ptx->mac.frame_ctl_2); + memcpy_toio(ptx->mac.addr_1, ((struct ethhdr *)data)->h_dest, + ADDRLEN); + memcpy_toio(ptx->mac.addr_2, ((struct ethhdr *)data)->h_source, + ADDRLEN); + memcpy_toio(ptx->mac.addr_3, local->bss_id, ADDRLEN); + } else { /* infrastructure */ + + if (local->sparm.b4.a_acting_as_ap_status) { + writeb(FC2_FROM_DS, &ptx->mac.frame_ctl_2); + memcpy_toio(ptx->mac.addr_1, + ((struct ethhdr *)data)->h_dest, ADDRLEN); + memcpy_toio(ptx->mac.addr_2, local->bss_id, 6); + memcpy_toio(ptx->mac.addr_3, + ((struct ethhdr *)data)->h_source, ADDRLEN); + } else { /* Terminal */ + + writeb(FC2_TO_DS, &ptx->mac.frame_ctl_2); + memcpy_toio(ptx->mac.addr_1, local->bss_id, ADDRLEN); + memcpy_toio(ptx->mac.addr_2, + ((struct ethhdr *)data)->h_source, ADDRLEN); + memcpy_toio(ptx->mac.addr_3, + ((struct ethhdr *)data)->h_dest, ADDRLEN); + } + } +} /* end encapsulate_frame */ + +/*====================================================================*/ + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get protocol name + */ +static int ray_get_name(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + strcpy(wrqu->name, "IEEE 802.11-FH"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set frequency + */ +static int ray_set_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + int err = -EINPROGRESS; /* Call commit handler */ + + /* Reject if card is already initialised */ + if (local->card_status != CARD_AWAITING_PARAM) + return -EBUSY; + + /* Setting by channel number */ + if ((wrqu->freq.m > USA_HOP_MOD) || (wrqu->freq.e > 0)) + err = -EOPNOTSUPP; + else + local->sparm.b5.a_hop_pattern = wrqu->freq.m; + + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get frequency + */ +static int ray_get_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + wrqu->freq.m = local->sparm.b5.a_hop_pattern; + wrqu->freq.e = 0; + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set ESSID + */ +static int ray_set_essid(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + /* Reject if card is already initialised */ + if (local->card_status != CARD_AWAITING_PARAM) + return -EBUSY; + + /* Check if we asked for `any' */ + if (wrqu->essid.flags == 0) + /* Corey : can you do that ? */ + return -EOPNOTSUPP; + + /* Check the size of the string */ + if (wrqu->essid.length > IW_ESSID_MAX_SIZE) + return -E2BIG; + + /* Set the ESSID in the card */ + memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE); + memcpy(local->sparm.b5.a_current_ess_id, extra, wrqu->essid.length); + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get ESSID + */ +static int ray_get_essid(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + UCHAR tmp[IW_ESSID_MAX_SIZE + 1]; + + /* Get the essid that was set */ + memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); + memcpy(tmp, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); + tmp[IW_ESSID_MAX_SIZE] = '\0'; + + /* Push it out ! */ + wrqu->essid.length = strlen(tmp); + wrqu->essid.flags = 1; /* active */ + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get AP address + */ +static int ray_get_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + memcpy(wrqu->ap_addr.sa_data, local->bss_id, ETH_ALEN); + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Bit-Rate + */ +static int ray_set_rate(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + /* Reject if card is already initialised */ + if (local->card_status != CARD_AWAITING_PARAM) + return -EBUSY; + + /* Check if rate is in range */ + if ((wrqu->bitrate.value != 1000000) && (wrqu->bitrate.value != 2000000)) + return -EINVAL; + + /* Hack for 1.5 Mb/s instead of 2 Mb/s */ + if ((local->fw_ver == 0x55) && /* Please check */ + (wrqu->bitrate.value == 2000000)) + local->net_default_tx_rate = 3; + else + local->net_default_tx_rate = wrqu->bitrate.value / 500000; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Bit-Rate + */ +static int ray_get_rate(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + if (local->net_default_tx_rate == 3) + wrqu->bitrate.value = 2000000; /* Hum... */ + else + wrqu->bitrate.value = local->net_default_tx_rate * 500000; + wrqu->bitrate.fixed = 0; /* We are in auto mode */ + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set RTS threshold + */ +static int ray_set_rts(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + int rthr = wrqu->rts.value; + + /* Reject if card is already initialised */ + if (local->card_status != CARD_AWAITING_PARAM) + return -EBUSY; + + /* if(wrq->u.rts.fixed == 0) we should complain */ + if (wrqu->rts.disabled) + rthr = 32767; + else { + if ((rthr < 0) || (rthr > 2347)) /* What's the max packet size ??? */ + return -EINVAL; + } + local->sparm.b5.a_rts_threshold[0] = (rthr >> 8) & 0xFF; + local->sparm.b5.a_rts_threshold[1] = rthr & 0xFF; + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get RTS threshold + */ +static int ray_get_rts(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + wrqu->rts.value = (local->sparm.b5.a_rts_threshold[0] << 8) + + local->sparm.b5.a_rts_threshold[1]; + wrqu->rts.disabled = (wrqu->rts.value == 32767); + wrqu->rts.fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Fragmentation threshold + */ +static int ray_set_frag(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + int fthr = wrqu->frag.value; + + /* Reject if card is already initialised */ + if (local->card_status != CARD_AWAITING_PARAM) + return -EBUSY; + + /* if(wrq->u.frag.fixed == 0) should complain */ + if (wrqu->frag.disabled) + fthr = 32767; + else { + if ((fthr < 256) || (fthr > 2347)) /* To check out ! */ + return -EINVAL; + } + local->sparm.b5.a_frag_threshold[0] = (fthr >> 8) & 0xFF; + local->sparm.b5.a_frag_threshold[1] = fthr & 0xFF; + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Fragmentation threshold + */ +static int ray_get_frag(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + wrqu->frag.value = (local->sparm.b5.a_frag_threshold[0] << 8) + + local->sparm.b5.a_frag_threshold[1]; + wrqu->frag.disabled = (wrqu->frag.value == 32767); + wrqu->frag.fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Mode of Operation + */ +static int ray_set_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + int err = -EINPROGRESS; /* Call commit handler */ + char card_mode = 1; + + /* Reject if card is already initialised */ + if (local->card_status != CARD_AWAITING_PARAM) + return -EBUSY; + + switch (wrqu->mode) { + case IW_MODE_ADHOC: + card_mode = 0; + fallthrough; + case IW_MODE_INFRA: + local->sparm.b5.a_network_type = card_mode; + break; + default: + err = -EINVAL; + } + + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Mode of Operation + */ +static int ray_get_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + ray_dev_t *local = netdev_priv(dev); + + if (local->sparm.b5.a_network_type) + wrqu->mode = IW_MODE_INFRA; + else + wrqu->mode = IW_MODE_ADHOC; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get range info + */ +static int ray_get_range(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + + memset(range, 0, sizeof(struct iw_range)); + + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(struct iw_range); + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 9; + + /* Set information in the range struct */ + range->throughput = 1.1 * 1000 * 1000; /* Put the right number here */ + range->num_channels = hop_pattern_length[(int)country]; + range->num_frequency = 0; + range->max_qual.qual = 0; + range->max_qual.level = 255; /* What's the correct value ? */ + range->max_qual.noise = 255; /* Idem */ + range->num_bitrates = 2; + range->bitrate[0] = 1000000; /* 1 Mb/s */ + range->bitrate[1] = 2000000; /* 2 Mb/s */ + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : set framing mode + */ +static int ray_set_framing(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + translate = !!*(extra); /* Set framing mode */ + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get framing mode + */ +static int ray_get_framing(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + *(extra) = translate; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Private Handler : get country + */ +static int ray_get_country(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + *(extra) = country; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Commit handler : called after a bunch of SET operations + */ +static int ray_commit(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Stats handler : return Wireless Stats + */ +static iw_stats *ray_get_wireless_stats(struct net_device *dev) +{ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + struct status __iomem *p = local->sram + STATUS_BASE; + + local->wstats.status = local->card_status; +#ifdef WIRELESS_SPY + if ((local->spy_data.spy_number > 0) + && (local->sparm.b5.a_network_type == 0)) { + /* Get it from the first node in spy list */ + local->wstats.qual.qual = local->spy_data.spy_stat[0].qual; + local->wstats.qual.level = local->spy_data.spy_stat[0].level; + local->wstats.qual.noise = local->spy_data.spy_stat[0].noise; + local->wstats.qual.updated = + local->spy_data.spy_stat[0].updated; + } +#endif /* WIRELESS_SPY */ + + if (pcmcia_dev_present(link)) { + local->wstats.qual.noise = readb(&p->rxnoise); + local->wstats.qual.updated |= 4; + } + + return &local->wstats; +} /* end ray_get_wireless_stats */ + +/*------------------------------------------------------------------*/ +/* + * Structures to export the Wireless Handlers + */ + +static const iw_handler ray_handler[] = { + IW_HANDLER(SIOCSIWCOMMIT, ray_commit), + IW_HANDLER(SIOCGIWNAME, ray_get_name), + IW_HANDLER(SIOCSIWFREQ, ray_set_freq), + IW_HANDLER(SIOCGIWFREQ, ray_get_freq), + IW_HANDLER(SIOCSIWMODE, ray_set_mode), + IW_HANDLER(SIOCGIWMODE, ray_get_mode), + IW_HANDLER(SIOCGIWRANGE, ray_get_range), +#ifdef WIRELESS_SPY + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), +#endif /* WIRELESS_SPY */ + IW_HANDLER(SIOCGIWAP, ray_get_wap), + IW_HANDLER(SIOCSIWESSID, ray_set_essid), + IW_HANDLER(SIOCGIWESSID, ray_get_essid), + IW_HANDLER(SIOCSIWRATE, ray_set_rate), + IW_HANDLER(SIOCGIWRATE, ray_get_rate), + IW_HANDLER(SIOCSIWRTS, ray_set_rts), + IW_HANDLER(SIOCGIWRTS, ray_get_rts), + IW_HANDLER(SIOCSIWFRAG, ray_set_frag), + IW_HANDLER(SIOCGIWFRAG, ray_get_frag), +}; + +#define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */ +#define SIOCGIPFRAMING SIOCIWFIRSTPRIV + 1 /* Get framing mode */ +#define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */ + +static const iw_handler ray_private_handler[] = { + [0] = ray_set_framing, + [1] = ray_get_framing, + [3] = ray_get_country, +}; + +static const struct iw_priv_args ray_private_args[] = { +/* cmd, set_args, get_args, name */ + {SIOCSIPFRAMING, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, + "set_framing"}, + {SIOCGIPFRAMING, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + "get_framing"}, + {SIOCGIPCOUNTRY, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + "get_country"}, +}; + +static const struct iw_handler_def ray_handler_def = { + .num_standard = ARRAY_SIZE(ray_handler), + .num_private = ARRAY_SIZE(ray_private_handler), + .num_private_args = ARRAY_SIZE(ray_private_args), + .standard = ray_handler, + .private = ray_private_handler, + .private_args = ray_private_args, + .get_wireless_stats = ray_get_wireless_stats, +}; + +/*===========================================================================*/ +static int ray_open(struct net_device *dev) +{ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link; + link = local->finder; + + dev_dbg(&link->dev, "ray_open('%s')\n", dev->name); + + if (link->open == 0) + local->num_multi = 0; + link->open++; + + /* If the card is not started, time to start it ! - Jean II */ + if (local->card_status == CARD_AWAITING_PARAM) { + int i; + + dev_dbg(&link->dev, "ray_open: doing init now !\n"); + + /* Download startup parameters */ + if ((i = dl_startup_params(dev)) < 0) { + printk(KERN_INFO + "ray_dev_init dl_startup_params failed - " + "returns 0x%x\n", i); + return -1; + } + } + + if (sniffer) + netif_stop_queue(dev); + else + netif_start_queue(dev); + + dev_dbg(&link->dev, "ray_open ending\n"); + return 0; +} /* end ray_open */ + +/*===========================================================================*/ +static int ray_dev_close(struct net_device *dev) +{ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link; + link = local->finder; + + dev_dbg(&link->dev, "ray_dev_close('%s')\n", dev->name); + + link->open--; + netif_stop_queue(dev); + + /* In here, we should stop the hardware (stop card from beeing active) + * and set local->card_status to CARD_AWAITING_PARAM, so that while the + * card is closed we can chage its configuration. + * Probably also need a COR reset to get sane state - Jean II */ + + return 0; +} /* end ray_dev_close */ + +/*===========================================================================*/ +static void ray_reset(struct net_device *dev) +{ + pr_debug("ray_reset entered\n"); +} + +/*===========================================================================*/ +/* Cause a firmware interrupt if it is ready for one */ +/* Return nonzero if not ready */ +static int interrupt_ecf(ray_dev_t *local, int ccs) +{ + int i = 50; + struct pcmcia_device *link = local->finder; + + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs interrupt_ecf - device not present\n"); + return -1; + } + dev_dbg(&link->dev, "interrupt_ecf(local=%p, ccs = 0x%x\n", local, ccs); + + while (i && + (readb(local->amem + CIS_OFFSET + ECF_INTR_OFFSET) & + ECF_INTR_SET)) + i--; + if (i == 0) { + dev_dbg(&link->dev, "ray_cs interrupt_ecf card not ready for interrupt\n"); + return -1; + } + /* Fill the mailbox, then kick the card */ + writeb(ccs, local->sram + SCB_BASE); + writeb(ECF_INTR_SET, local->amem + CIS_OFFSET + ECF_INTR_OFFSET); + return 0; +} /* interrupt_ecf */ + +/*===========================================================================*/ +/* Get next free transmit CCS */ +/* Return - index of current tx ccs */ +static int get_free_tx_ccs(ray_dev_t *local) +{ + int i; + struct ccs __iomem *pccs = ccs_base(local); + struct pcmcia_device *link = local->finder; + + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs get_free_tx_ccs - device not present\n"); + return ECARDGONE; + } + + if (test_and_set_bit(0, &local->tx_ccs_lock)) { + dev_dbg(&link->dev, "ray_cs tx_ccs_lock busy\n"); + return ECCSBUSY; + } + + for (i = 0; i < NUMBER_OF_TX_CCS; i++) { + if (readb(&(pccs + i)->buffer_status) == CCS_BUFFER_FREE) { + writeb(CCS_BUFFER_BUSY, &(pccs + i)->buffer_status); + writeb(CCS_END_LIST, &(pccs + i)->link); + local->tx_ccs_lock = 0; + return i; + } + } + local->tx_ccs_lock = 0; + dev_dbg(&link->dev, "ray_cs ERROR no free tx CCS for raylink card\n"); + return ECCSFULL; +} /* get_free_tx_ccs */ + +/*===========================================================================*/ +/* Get next free CCS */ +/* Return - index of current ccs */ +static int get_free_ccs(ray_dev_t *local) +{ + int i; + struct ccs __iomem *pccs = ccs_base(local); + struct pcmcia_device *link = local->finder; + + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs get_free_ccs - device not present\n"); + return ECARDGONE; + } + if (test_and_set_bit(0, &local->ccs_lock)) { + dev_dbg(&link->dev, "ray_cs ccs_lock busy\n"); + return ECCSBUSY; + } + + for (i = NUMBER_OF_TX_CCS; i < NUMBER_OF_CCS; i++) { + if (readb(&(pccs + i)->buffer_status) == CCS_BUFFER_FREE) { + writeb(CCS_BUFFER_BUSY, &(pccs + i)->buffer_status); + writeb(CCS_END_LIST, &(pccs + i)->link); + local->ccs_lock = 0; + return i; + } + } + local->ccs_lock = 0; + dev_dbg(&link->dev, "ray_cs ERROR no free CCS for raylink card\n"); + return ECCSFULL; +} /* get_free_ccs */ + +/*===========================================================================*/ +static void authenticate_timeout(struct timer_list *t) +{ + ray_dev_t *local = from_timer(local, t, timer); + del_timer(&local->timer); + printk(KERN_INFO "ray_cs Authentication with access point failed" + " - timeout\n"); + join_net(&local->timer); +} + +/*===========================================================================*/ +static int parse_addr(char *in_str, UCHAR *out) +{ + int i, k; + int len; + + if (in_str == NULL) + return 0; + len = strnlen(in_str, ADDRLEN * 2 + 1) - 1; + if (len < 1) + return 0; + memset(out, 0, ADDRLEN); + + i = 5; + + while (len > 0) { + if ((k = hex_to_bin(in_str[len--])) != -1) + out[i] = k; + else + return 0; + + if (len == 0) + break; + if ((k = hex_to_bin(in_str[len--])) != -1) + out[i] += k << 4; + else + return 0; + if (!i--) + break; + } + return 1; +} + +/*===========================================================================*/ +static struct net_device_stats *ray_get_stats(struct net_device *dev) +{ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + struct status __iomem *p = local->sram + STATUS_BASE; + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs net_device_stats - device not present\n"); + return &local->stats; + } + if (readb(&p->mrx_overflow_for_host)) { + local->stats.rx_over_errors += swab16(readw(&p->mrx_overflow)); + writeb(0, &p->mrx_overflow); + writeb(0, &p->mrx_overflow_for_host); + } + if (readb(&p->mrx_checksum_error_for_host)) { + local->stats.rx_crc_errors += + swab16(readw(&p->mrx_checksum_error)); + writeb(0, &p->mrx_checksum_error); + writeb(0, &p->mrx_checksum_error_for_host); + } + if (readb(&p->rx_hec_error_for_host)) { + local->stats.rx_frame_errors += swab16(readw(&p->rx_hec_error)); + writeb(0, &p->rx_hec_error); + writeb(0, &p->rx_hec_error_for_host); + } + return &local->stats; +} + +/*===========================================================================*/ +static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, + int len) +{ + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + int ccsindex; + int i; + struct ccs __iomem *pccs; + + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_update_parm - device not present\n"); + return; + } + + if ((ccsindex = get_free_ccs(local)) < 0) { + dev_dbg(&link->dev, "ray_update_parm - No free ccs\n"); + return; + } + pccs = ccs_base(local) + ccsindex; + writeb(CCS_UPDATE_PARAMS, &pccs->cmd); + writeb(objid, &pccs->var.update_param.object_id); + writeb(1, &pccs->var.update_param.number_objects); + writeb(0, &pccs->var.update_param.failure_cause); + for (i = 0; i < len; i++) { + writeb(value[i], local->sram + HOST_TO_ECF_BASE); + } + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + dev_dbg(&link->dev, "ray_cs associate failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + } +} + +/*===========================================================================*/ +static void ray_update_multi_list(struct net_device *dev, int all) +{ + int ccsindex; + struct ccs __iomem *pccs; + ray_dev_t *local = netdev_priv(dev); + struct pcmcia_device *link = local->finder; + void __iomem *p = local->sram + HOST_TO_ECF_BASE; + + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_update_multi_list - device not present\n"); + return; + } else + dev_dbg(&link->dev, "ray_update_multi_list(%p)\n", dev); + if ((ccsindex = get_free_ccs(local)) < 0) { + dev_dbg(&link->dev, "ray_update_multi - No free ccs\n"); + return; + } + pccs = ccs_base(local) + ccsindex; + writeb(CCS_UPDATE_MULTICAST_LIST, &pccs->cmd); + + if (all) { + writeb(0xff, &pccs->var); + local->num_multi = 0xff; + } else { + struct netdev_hw_addr *ha; + int i = 0; + + /* Copy the kernel's list of MC addresses to card */ + netdev_for_each_mc_addr(ha, dev) { + memcpy_toio(p, ha->addr, ETH_ALEN); + dev_dbg(&link->dev, "ray_update_multi add addr %pm\n", + ha->addr); + p += ETH_ALEN; + i++; + } + if (i > 256 / ADDRLEN) + i = 256 / ADDRLEN; + writeb((UCHAR) i, &pccs->var); + dev_dbg(&link->dev, "ray_cs update_multi %d addresses in list\n", i); + /* Interrupt the firmware to process the command */ + local->num_multi = i; + } + if (interrupt_ecf(local, ccsindex)) { + dev_dbg(&link->dev, + "ray_cs update_multi failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + } +} /* end ray_update_multi_list */ + +/*===========================================================================*/ +static void set_multicast_list(struct net_device *dev) +{ + ray_dev_t *local = netdev_priv(dev); + UCHAR promisc; + + pr_debug("ray_cs set_multicast_list(%p)\n", dev); + + if (dev->flags & IFF_PROMISC) { + if (local->sparm.b5.a_promiscuous_mode == 0) { + pr_debug("ray_cs set_multicast_list promisc on\n"); + local->sparm.b5.a_promiscuous_mode = 1; + promisc = 1; + ray_update_parm(dev, OBJID_promiscuous_mode, + &promisc, sizeof(promisc)); + } + } else { + if (local->sparm.b5.a_promiscuous_mode == 1) { + pr_debug("ray_cs set_multicast_list promisc off\n"); + local->sparm.b5.a_promiscuous_mode = 0; + promisc = 0; + ray_update_parm(dev, OBJID_promiscuous_mode, + &promisc, sizeof(promisc)); + } + } + + if (dev->flags & IFF_ALLMULTI) + ray_update_multi_list(dev, 1); + else { + if (local->num_multi != netdev_mc_count(dev)) + ray_update_multi_list(dev, 0); + } +} /* end set_multicast_list */ + +/*============================================================================= + * All routines below here are run at interrupt time. +=============================================================================*/ +static irqreturn_t ray_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct pcmcia_device *link; + ray_dev_t *local; + struct ccs __iomem *pccs; + struct rcs __iomem *prcs; + UCHAR rcsindex; + UCHAR tmp; + UCHAR cmd; + UCHAR status; + UCHAR memtmp[ESSID_SIZE + 1]; + + + if (dev == NULL) /* Note that we want interrupts with dev->start == 0 */ + return IRQ_NONE; + + pr_debug("ray_cs: interrupt for *dev=%p\n", dev); + + local = netdev_priv(dev); + link = local->finder; + if (!pcmcia_dev_present(link)) { + pr_debug( + "ray_cs interrupt from device not present or suspended.\n"); + return IRQ_NONE; + } + rcsindex = readb(&((struct scb __iomem *)(local->sram))->rcs_index); + + if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) { + dev_dbg(&link->dev, "ray_cs interrupt bad rcsindex = 0x%x\n", rcsindex); + clear_interrupt(local); + return IRQ_HANDLED; + } + if (rcsindex < NUMBER_OF_CCS) { /* If it's a returned CCS */ + pccs = ccs_base(local) + rcsindex; + cmd = readb(&pccs->cmd); + status = readb(&pccs->buffer_status); + switch (cmd) { + case CCS_DOWNLOAD_STARTUP_PARAMS: /* Happens in firmware someday */ + del_timer(&local->timer); + if (status == CCS_COMMAND_COMPLETE) { + dev_dbg(&link->dev, + "ray_cs interrupt download_startup_parameters OK\n"); + } else { + dev_dbg(&link->dev, + "ray_cs interrupt download_startup_parameters fail\n"); + } + break; + case CCS_UPDATE_PARAMS: + dev_dbg(&link->dev, "ray_cs interrupt update params done\n"); + if (status != CCS_COMMAND_COMPLETE) { + tmp = + readb(&pccs->var.update_param. + failure_cause); + dev_dbg(&link->dev, + "ray_cs interrupt update params failed - reason %d\n", + tmp); + } + break; + case CCS_REPORT_PARAMS: + dev_dbg(&link->dev, "ray_cs interrupt report params done\n"); + break; + case CCS_UPDATE_MULTICAST_LIST: /* Note that this CCS isn't returned */ + dev_dbg(&link->dev, + "ray_cs interrupt CCS Update Multicast List done\n"); + break; + case CCS_UPDATE_POWER_SAVINGS_MODE: + dev_dbg(&link->dev, + "ray_cs interrupt update power save mode done\n"); + break; + case CCS_START_NETWORK: + case CCS_JOIN_NETWORK: + memcpy(memtmp, local->sparm.b4.a_current_ess_id, + ESSID_SIZE); + memtmp[ESSID_SIZE] = '\0'; + + if (status == CCS_COMMAND_COMPLETE) { + if (readb + (&pccs->var.start_network.net_initiated) == + 1) { + dev_dbg(&link->dev, + "ray_cs interrupt network \"%s\" started\n", + memtmp); + } else { + dev_dbg(&link->dev, + "ray_cs interrupt network \"%s\" joined\n", + memtmp); + } + memcpy_fromio(&local->bss_id, + pccs->var.start_network.bssid, + ADDRLEN); + + if (local->fw_ver == 0x55) + local->net_default_tx_rate = 3; + else + local->net_default_tx_rate = + readb(&pccs->var.start_network. + net_default_tx_rate); + local->encryption = + readb(&pccs->var.start_network.encryption); + if (!sniffer && (local->net_type == INFRA) + && !(local->sparm.b4.a_acting_as_ap_status)) { + authenticate(local); + } + local->card_status = CARD_ACQ_COMPLETE; + } else { + local->card_status = CARD_ACQ_FAILED; + + del_timer(&local->timer); + local->timer.expires = jiffies + HZ * 5; + if (status == CCS_START_NETWORK) { + dev_dbg(&link->dev, + "ray_cs interrupt network \"%s\" start failed\n", + memtmp); + local->timer.function = start_net; + } else { + dev_dbg(&link->dev, + "ray_cs interrupt network \"%s\" join failed\n", + memtmp); + local->timer.function = join_net; + } + add_timer(&local->timer); + } + break; + case CCS_START_ASSOCIATION: + if (status == CCS_COMMAND_COMPLETE) { + local->card_status = CARD_ASSOC_COMPLETE; + dev_dbg(&link->dev, "ray_cs association successful\n"); + } else { + dev_dbg(&link->dev, "ray_cs association failed,\n"); + local->card_status = CARD_ASSOC_FAILED; + join_net(&local->timer); + } + break; + case CCS_TX_REQUEST: + if (status == CCS_COMMAND_COMPLETE) { + dev_dbg(&link->dev, + "ray_cs interrupt tx request complete\n"); + } else { + dev_dbg(&link->dev, + "ray_cs interrupt tx request failed\n"); + } + if (!sniffer) + netif_start_queue(dev); + netif_wake_queue(dev); + break; + case CCS_TEST_MEMORY: + dev_dbg(&link->dev, "ray_cs interrupt mem test done\n"); + break; + case CCS_SHUTDOWN: + dev_dbg(&link->dev, + "ray_cs interrupt Unexpected CCS returned - Shutdown\n"); + break; + case CCS_DUMP_MEMORY: + dev_dbg(&link->dev, "ray_cs interrupt dump memory done\n"); + break; + case CCS_START_TIMER: + dev_dbg(&link->dev, + "ray_cs interrupt DING - raylink timer expired\n"); + break; + default: + dev_dbg(&link->dev, + "ray_cs interrupt Unexpected CCS 0x%x returned 0x%x\n", + rcsindex, cmd); + } + writeb(CCS_BUFFER_FREE, &pccs->buffer_status); + } else { /* It's an RCS */ + + prcs = rcs_base(local) + rcsindex; + + switch (readb(&prcs->interrupt_id)) { + case PROCESS_RX_PACKET: + ray_rx(dev, local, prcs); + break; + case REJOIN_NET_COMPLETE: + dev_dbg(&link->dev, "ray_cs interrupt rejoin net complete\n"); + local->card_status = CARD_ACQ_COMPLETE; + /* do we need to clear tx buffers CCS's? */ + if (local->sparm.b4.a_network_type == ADHOC) { + if (!sniffer) + netif_start_queue(dev); + } else { + memcpy_fromio(&local->bss_id, + prcs->var.rejoin_net_complete. + bssid, ADDRLEN); + dev_dbg(&link->dev, "ray_cs new BSSID = %pm\n", + local->bss_id); + if (!sniffer) + authenticate(local); + } + break; + case ROAMING_INITIATED: + dev_dbg(&link->dev, "ray_cs interrupt roaming initiated\n"); + netif_stop_queue(dev); + local->card_status = CARD_DOING_ACQ; + break; + case JAPAN_CALL_SIGN_RXD: + dev_dbg(&link->dev, "ray_cs interrupt japan call sign rx\n"); + break; + default: + dev_dbg(&link->dev, + "ray_cs Unexpected interrupt for RCS 0x%x cmd = 0x%x\n", + rcsindex, + (unsigned int)readb(&prcs->interrupt_id)); + break; + } + writeb(CCS_BUFFER_FREE, &prcs->buffer_status); + } + clear_interrupt(local); + return IRQ_HANDLED; +} /* ray_interrupt */ + +/*===========================================================================*/ +static void ray_rx(struct net_device *dev, ray_dev_t *local, + struct rcs __iomem *prcs) +{ + int rx_len; + unsigned int pkt_addr; + void __iomem *pmsg; + pr_debug("ray_rx process rx packet\n"); + + /* Calculate address of packet within Rx buffer */ + pkt_addr = ((readb(&prcs->var.rx_packet.rx_data_ptr[0]) << 8) + + readb(&prcs->var.rx_packet.rx_data_ptr[1])) & RX_BUFF_END; + /* Length of first packet fragment */ + rx_len = (readb(&prcs->var.rx_packet.rx_data_length[0]) << 8) + + readb(&prcs->var.rx_packet.rx_data_length[1]); + + local->last_rsl = readb(&prcs->var.rx_packet.rx_sig_lev); + pmsg = local->rmem + pkt_addr; + switch (readb(pmsg)) { + case DATA_TYPE: + pr_debug("ray_rx data type\n"); + rx_data(dev, prcs, pkt_addr, rx_len); + break; + case AUTHENTIC_TYPE: + pr_debug("ray_rx authentic type\n"); + if (sniffer) + rx_data(dev, prcs, pkt_addr, rx_len); + else + rx_authenticate(local, prcs, pkt_addr, rx_len); + break; + case DEAUTHENTIC_TYPE: + pr_debug("ray_rx deauth type\n"); + if (sniffer) + rx_data(dev, prcs, pkt_addr, rx_len); + else + rx_deauthenticate(local, prcs, pkt_addr, rx_len); + break; + case NULL_MSG_TYPE: + pr_debug("ray_cs rx NULL msg\n"); + break; + case BEACON_TYPE: + pr_debug("ray_rx beacon type\n"); + if (sniffer) + rx_data(dev, prcs, pkt_addr, rx_len); + + copy_from_rx_buff(local, (UCHAR *) &local->last_bcn, pkt_addr, + rx_len < sizeof(struct beacon_rx) ? + rx_len : sizeof(struct beacon_rx)); + + local->beacon_rxed = 1; + /* Get the statistics so the card counters never overflow */ + ray_get_stats(dev); + break; + default: + pr_debug("ray_cs unknown pkt type %2x\n", + (unsigned int)readb(pmsg)); + break; + } + +} /* end ray_rx */ + +/*===========================================================================*/ +static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, + unsigned int pkt_addr, int rx_len) +{ + struct sk_buff *skb = NULL; + struct rcs __iomem *prcslink = prcs; + ray_dev_t *local = netdev_priv(dev); + UCHAR *rx_ptr; + int total_len; + int tmp; +#ifdef WIRELESS_SPY + int siglev = local->last_rsl; + u_char linksrcaddr[ETH_ALEN]; /* Other end of the wireless link */ +#endif + + if (!sniffer) { + if (translate) { +/* TBD length needs fixing for translated header */ + if (rx_len < (ETH_HLEN + RX_MAC_HEADER_LENGTH) || + rx_len > + (dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN + + FCS_LEN)) { + pr_debug( + "ray_cs invalid packet length %d received\n", + rx_len); + return; + } + } else { /* encapsulated ethernet */ + + if (rx_len < (ETH_HLEN + RX_MAC_HEADER_LENGTH) || + rx_len > + (dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN + + FCS_LEN)) { + pr_debug( + "ray_cs invalid packet length %d received\n", + rx_len); + return; + } + } + } + pr_debug("ray_cs rx_data packet\n"); + /* If fragmented packet, verify sizes of fragments add up */ + if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) { + pr_debug("ray_cs rx'ed fragment\n"); + tmp = (readb(&prcs->var.rx_packet.totalpacketlength[0]) << 8) + + readb(&prcs->var.rx_packet.totalpacketlength[1]); + total_len = tmp; + prcslink = prcs; + do { + tmp -= + (readb(&prcslink->var.rx_packet.rx_data_length[0]) + << 8) + + readb(&prcslink->var.rx_packet.rx_data_length[1]); + if (readb(&prcslink->var.rx_packet.next_frag_rcs_index) + == 0xFF || tmp < 0) + break; + prcslink = rcs_base(local) + + readb(&prcslink->link_field); + } while (1); + + if (tmp < 0) { + pr_debug( + "ray_cs rx_data fragment lengths don't add up\n"); + local->stats.rx_dropped++; + release_frag_chain(local, prcs); + return; + } + } else { /* Single unfragmented packet */ + total_len = rx_len; + } + + skb = dev_alloc_skb(total_len + 5); + if (skb == NULL) { + pr_debug("ray_cs rx_data could not allocate skb\n"); + local->stats.rx_dropped++; + if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) + release_frag_chain(local, prcs); + return; + } + skb_reserve(skb, 2); /* Align IP on 16 byte (TBD check this) */ + + pr_debug("ray_cs rx_data total_len = %x, rx_len = %x\n", total_len, + rx_len); + +/************************/ + /* Reserve enough room for the whole damn packet. */ + rx_ptr = skb_put(skb, total_len); + /* Copy the whole packet to sk_buff */ + rx_ptr += + copy_from_rx_buff(local, rx_ptr, pkt_addr & RX_BUFF_END, rx_len); + /* Get source address */ +#ifdef WIRELESS_SPY + skb_copy_from_linear_data_offset(skb, + offsetof(struct mac_header, addr_2), + linksrcaddr, ETH_ALEN); +#endif + /* Now, deal with encapsulation/translation/sniffer */ + if (!sniffer) { + if (!translate) { + /* Encapsulated ethernet, so just lop off 802.11 MAC header */ +/* TBD reserve skb_reserve( skb, RX_MAC_HEADER_LENGTH); */ + skb_pull(skb, RX_MAC_HEADER_LENGTH); + } else { + /* Do translation */ + untranslate(local, skb, total_len); + } + } else { /* sniffer mode, so just pass whole packet */ + } + +/************************/ + /* Now pick up the rest of the fragments if any */ + tmp = 17; + if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) { + prcslink = prcs; + pr_debug("ray_cs rx_data in fragment loop\n"); + do { + prcslink = rcs_base(local) + + + readb(&prcslink->var.rx_packet.next_frag_rcs_index); + rx_len = + ((readb(&prcslink->var.rx_packet.rx_data_length[0]) + << 8) + + + readb(&prcslink->var.rx_packet.rx_data_length[1])) + & RX_BUFF_END; + pkt_addr = + ((readb(&prcslink->var.rx_packet.rx_data_ptr[0]) << + 8) + + readb(&prcslink->var.rx_packet.rx_data_ptr[1])) + & RX_BUFF_END; + + rx_ptr += + copy_from_rx_buff(local, rx_ptr, pkt_addr, rx_len); + + } while (tmp-- && + readb(&prcslink->var.rx_packet.next_frag_rcs_index) != + 0xFF); + release_frag_chain(local, prcs); + } + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + local->stats.rx_packets++; + local->stats.rx_bytes += total_len; + + /* Gather signal strength per address */ +#ifdef WIRELESS_SPY + /* For the Access Point or the node having started the ad-hoc net + * note : ad-hoc work only in some specific configurations, but we + * kludge in ray_get_wireless_stats... */ + if (!memcmp(linksrcaddr, local->bss_id, ETH_ALEN)) { + /* Update statistics */ + /*local->wstats.qual.qual = none ? */ + local->wstats.qual.level = siglev; + /*local->wstats.qual.noise = none ? */ + local->wstats.qual.updated = 0x2; + } + /* Now, update the spy stuff */ + { + struct iw_quality wstats; + wstats.level = siglev; + /* wstats.noise = none ? */ + /* wstats.qual = none ? */ + wstats.updated = 0x2; + /* Update spy records */ + wireless_spy_update(dev, linksrcaddr, &wstats); + } +#endif /* WIRELESS_SPY */ +} /* end rx_data */ + +/*===========================================================================*/ +static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len) +{ + snaphdr_t *psnap = (snaphdr_t *) (skb->data + RX_MAC_HEADER_LENGTH); + struct ieee80211_hdr *pmac = (struct ieee80211_hdr *)skb->data; + __be16 type = *(__be16 *) psnap->ethertype; + int delta; + struct ethhdr *peth; + UCHAR srcaddr[ADDRLEN]; + UCHAR destaddr[ADDRLEN]; + static const UCHAR org_bridge[3] = { 0, 0, 0xf8 }; + static const UCHAR org_1042[3] = { 0, 0, 0 }; + + memcpy(destaddr, ieee80211_get_DA(pmac), ADDRLEN); + memcpy(srcaddr, ieee80211_get_SA(pmac), ADDRLEN); + +#if 0 + if { + print_hex_dump(KERN_DEBUG, "skb->data before untranslate: ", + DUMP_PREFIX_NONE, 16, 1, + skb->data, 64, true); + printk(KERN_DEBUG + "type = %08x, xsap = %02x%02x%02x, org = %02x02x02x\n", + ntohs(type), psnap->dsap, psnap->ssap, psnap->ctrl, + psnap->org[0], psnap->org[1], psnap->org[2]); + printk(KERN_DEBUG "untranslate skb->data = %p\n", skb->data); + } +#endif + + if (psnap->dsap != 0xaa || psnap->ssap != 0xaa || psnap->ctrl != 3) { + /* not a snap type so leave it alone */ + pr_debug("ray_cs untranslate NOT SNAP %02x %02x %02x\n", + psnap->dsap, psnap->ssap, psnap->ctrl); + + delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = htons(len - RX_MAC_HEADER_LENGTH); + } else { /* Its a SNAP */ + if (memcmp(psnap->org, org_bridge, 3) == 0) { + /* EtherII and nuke the LLC */ + pr_debug("ray_cs untranslate Bridge encap\n"); + delta = RX_MAC_HEADER_LENGTH + + sizeof(struct snaphdr_t) - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = type; + } else if (memcmp(psnap->org, org_1042, 3) == 0) { + switch (ntohs(type)) { + case ETH_P_IPX: + case ETH_P_AARP: + pr_debug("ray_cs untranslate RFC IPX/AARP\n"); + delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = + htons(len - RX_MAC_HEADER_LENGTH); + break; + default: + pr_debug("ray_cs untranslate RFC default\n"); + delta = RX_MAC_HEADER_LENGTH + + sizeof(struct snaphdr_t) - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = type; + break; + } + } else { + printk("ray_cs untranslate very confused by packet\n"); + delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; + peth = (struct ethhdr *)(skb->data + delta); + peth->h_proto = type; + } + } +/* TBD reserve skb_reserve(skb, delta); */ + skb_pull(skb, delta); + pr_debug("untranslate after skb_pull(%d), skb->data = %p\n", delta, + skb->data); + memcpy(peth->h_dest, destaddr, ADDRLEN); + memcpy(peth->h_source, srcaddr, ADDRLEN); +#if 0 + { + int i; + printk(KERN_DEBUG "skb->data after untranslate:"); + for (i = 0; i < 64; i++) + printk("%02x ", skb->data[i]); + printk("\n"); + } +#endif +} /* end untranslate */ + +/*===========================================================================*/ +/* Copy data from circular receive buffer to PC memory. + * dest = destination address in PC memory + * pkt_addr = source address in receive buffer + * len = length of packet to copy + */ +static int copy_from_rx_buff(ray_dev_t *local, UCHAR *dest, int pkt_addr, + int length) +{ + int wrap_bytes = (pkt_addr + length) - (RX_BUFF_END + 1); + if (wrap_bytes <= 0) { + memcpy_fromio(dest, local->rmem + pkt_addr, length); + } else { /* Packet wrapped in circular buffer */ + + memcpy_fromio(dest, local->rmem + pkt_addr, + length - wrap_bytes); + memcpy_fromio(dest + length - wrap_bytes, local->rmem, + wrap_bytes); + } + return length; +} + +/*===========================================================================*/ +static void release_frag_chain(ray_dev_t *local, struct rcs __iomem *prcs) +{ + struct rcs __iomem *prcslink = prcs; + int tmp = 17; + unsigned rcsindex = readb(&prcs->var.rx_packet.next_frag_rcs_index); + + while (tmp--) { + writeb(CCS_BUFFER_FREE, &prcslink->buffer_status); + if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) { + pr_debug("ray_cs interrupt bad rcsindex = 0x%x\n", + rcsindex); + break; + } + prcslink = rcs_base(local) + rcsindex; + rcsindex = readb(&prcslink->var.rx_packet.next_frag_rcs_index); + } + writeb(CCS_BUFFER_FREE, &prcslink->buffer_status); +} + +/*===========================================================================*/ +static void authenticate(ray_dev_t *local) +{ + struct pcmcia_device *link = local->finder; + dev_dbg(&link->dev, "ray_cs Starting authentication.\n"); + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs authenticate - device not present\n"); + return; + } + + del_timer(&local->timer); + if (build_auth_frame(local, local->bss_id, OPEN_AUTH_REQUEST)) { + local->timer.function = join_net; + } else { + local->timer.function = authenticate_timeout; + } + local->timer.expires = jiffies + HZ * 2; + add_timer(&local->timer); + local->authentication_state = AWAITING_RESPONSE; +} /* end authenticate */ + +/*===========================================================================*/ +static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs, + unsigned int pkt_addr, int rx_len) +{ + UCHAR buff[256]; + struct ray_rx_msg *msg = (struct ray_rx_msg *) buff; + + del_timer(&local->timer); + + copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); + /* if we are trying to get authenticated */ + if (local->sparm.b4.a_network_type == ADHOC) { + pr_debug("ray_cs rx_auth var= %6ph\n", msg->var); + if (msg->var[2] == 1) { + pr_debug("ray_cs Sending authentication response.\n"); + if (!build_auth_frame + (local, msg->mac.addr_2, OPEN_AUTH_RESPONSE)) { + local->authentication_state = NEED_TO_AUTH; + memcpy(local->auth_id, msg->mac.addr_2, + ADDRLEN); + } + } + } else { /* Infrastructure network */ + + if (local->authentication_state == AWAITING_RESPONSE) { + /* Verify authentication sequence #2 and success */ + if (msg->var[2] == 2) { + if ((msg->var[3] | msg->var[4]) == 0) { + pr_debug("Authentication successful\n"); + local->card_status = CARD_AUTH_COMPLETE; + associate(local); + local->authentication_state = + AUTHENTICATED; + } else { + pr_debug("Authentication refused\n"); + local->card_status = CARD_AUTH_REFUSED; + join_net(&local->timer); + local->authentication_state = + UNAUTHENTICATED; + } + } + } + } + +} /* end rx_authenticate */ + +/*===========================================================================*/ +static void associate(ray_dev_t *local) +{ + struct ccs __iomem *pccs; + struct pcmcia_device *link = local->finder; + struct net_device *dev = link->priv; + int ccsindex; + if (!(pcmcia_dev_present(link))) { + dev_dbg(&link->dev, "ray_cs associate - device not present\n"); + return; + } + /* If no tx buffers available, return */ + if ((ccsindex = get_free_ccs(local)) < 0) { +/* TBD should never be here but... what if we are? */ + dev_dbg(&link->dev, "ray_cs associate - No free ccs\n"); + return; + } + dev_dbg(&link->dev, "ray_cs Starting association with access point\n"); + pccs = ccs_base(local) + ccsindex; + /* fill in the CCS */ + writeb(CCS_START_ASSOCIATION, &pccs->cmd); + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + dev_dbg(&link->dev, "ray_cs associate failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + + del_timer(&local->timer); + local->timer.expires = jiffies + HZ * 2; + local->timer.function = join_net; + add_timer(&local->timer); + local->card_status = CARD_ASSOC_FAILED; + return; + } + if (!sniffer) + netif_start_queue(dev); + +} /* end associate */ + +/*===========================================================================*/ +static void rx_deauthenticate(ray_dev_t *local, struct rcs __iomem *prcs, + unsigned int pkt_addr, int rx_len) +{ +/* UCHAR buff[256]; + struct ray_rx_msg *msg = (struct ray_rx_msg *) buff; +*/ + pr_debug("Deauthentication frame received\n"); + local->authentication_state = UNAUTHENTICATED; + /* Need to reauthenticate or rejoin depending on reason code */ +/* copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); + */ +} + +/*===========================================================================*/ +static void clear_interrupt(ray_dev_t *local) +{ + writeb(0, local->amem + CIS_OFFSET + HCS_INTR_OFFSET); +} + +/*===========================================================================*/ +#ifdef CONFIG_PROC_FS +#define MAXDATA (PAGE_SIZE - 80) + +static const char *card_status[] = { + "Card inserted - uninitialized", /* 0 */ + "Card not downloaded", /* 1 */ + "Waiting for download parameters", /* 2 */ + "Card doing acquisition", /* 3 */ + "Acquisition complete", /* 4 */ + "Authentication complete", /* 5 */ + "Association complete", /* 6 */ + "???", "???", "???", "???", /* 7 8 9 10 undefined */ + "Card init error", /* 11 */ + "Download parameters error", /* 12 */ + "???", /* 13 */ + "Acquisition failed", /* 14 */ + "Authentication refused", /* 15 */ + "Association failed" /* 16 */ +}; + +static const char *nettype[] = { "Adhoc", "Infra " }; +static const char *framing[] = { "Encapsulation", "Translation" } + +; +/*===========================================================================*/ +static int ray_cs_proc_show(struct seq_file *m, void *v) +{ +/* Print current values which are not available via other means + * eg ifconfig + */ + int i; + struct pcmcia_device *link; + struct net_device *dev; + ray_dev_t *local; + UCHAR *p; + struct freq_hop_element *pfh; + UCHAR c[33]; + + link = this_device; + if (!link) + return 0; + dev = (struct net_device *)link->priv; + if (!dev) + return 0; + local = netdev_priv(dev); + if (!local) + return 0; + + seq_puts(m, "Raylink Wireless LAN driver status\n"); + seq_printf(m, "%s\n", rcsid); + /* build 4 does not report version, and field is 0x55 after memtest */ + seq_puts(m, "Firmware version = "); + if (local->fw_ver == 0x55) + seq_puts(m, "4 - Use dump_cis for more details\n"); + else + seq_printf(m, "%2d.%02d.%02d\n", + local->fw_ver, local->fw_bld, local->fw_var); + + for (i = 0; i < 32; i++) + c[i] = local->sparm.b5.a_current_ess_id[i]; + c[32] = 0; + seq_printf(m, "%s network ESSID = \"%s\"\n", + nettype[local->sparm.b5.a_network_type], c); + + p = local->bss_id; + seq_printf(m, "BSSID = %pM\n", p); + + seq_printf(m, "Country code = %d\n", + local->sparm.b5.a_curr_country_code); + + i = local->card_status; + if (i < 0) + i = 10; + if (i > 16) + i = 10; + seq_printf(m, "Card status = %s\n", card_status[i]); + + seq_printf(m, "Framing mode = %s\n", framing[translate]); + + seq_printf(m, "Last pkt signal lvl = %d\n", local->last_rsl); + + if (local->beacon_rxed) { + /* Pull some fields out of last beacon received */ + seq_printf(m, "Beacon Interval = %d Kus\n", + local->last_bcn.beacon_intvl[0] + + 256 * local->last_bcn.beacon_intvl[1]); + + p = local->last_bcn.elements; + if (p[0] == C_ESSID_ELEMENT_ID) + p += p[1] + 2; + else { + seq_printf(m, + "Parse beacon failed at essid element id = %d\n", + p[0]); + return 0; + } + + if (p[0] == C_SUPPORTED_RATES_ELEMENT_ID) { + seq_puts(m, "Supported rate codes = "); + for (i = 2; i < p[1] + 2; i++) + seq_printf(m, "0x%02x ", p[i]); + seq_putc(m, '\n'); + p += p[1] + 2; + } else { + seq_puts(m, "Parse beacon failed at rates element\n"); + return 0; + } + + if (p[0] == C_FH_PARAM_SET_ELEMENT_ID) { + pfh = (struct freq_hop_element *)p; + seq_printf(m, "Hop dwell = %d Kus\n", + pfh->dwell_time[0] + + 256 * pfh->dwell_time[1]); + seq_printf(m, "Hop set = %d\n", + pfh->hop_set); + seq_printf(m, "Hop pattern = %d\n", + pfh->hop_pattern); + seq_printf(m, "Hop index = %d\n", + pfh->hop_index); + p += p[1] + 2; + } else { + seq_puts(m, + "Parse beacon failed at FH param element\n"); + return 0; + } + } else { + seq_puts(m, "No beacons received\n"); + } + return 0; +} +#endif +/*===========================================================================*/ +static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type) +{ + int addr; + struct ccs __iomem *pccs; + struct tx_msg __iomem *ptx; + int ccsindex; + + /* If no tx buffers available, return */ + if ((ccsindex = get_free_tx_ccs(local)) < 0) { + pr_debug("ray_cs send authenticate - No free tx ccs\n"); + return -1; + } + + pccs = ccs_base(local) + ccsindex; + + /* Address in card space */ + addr = TX_BUF_BASE + (ccsindex << 11); + /* fill in the CCS */ + writeb(CCS_TX_REQUEST, &pccs->cmd); + writeb(addr >> 8, pccs->var.tx_request.tx_data_ptr); + writeb(0x20, pccs->var.tx_request.tx_data_ptr + 1); + writeb(TX_AUTHENTICATE_LENGTH_MSB, pccs->var.tx_request.tx_data_length); + writeb(TX_AUTHENTICATE_LENGTH_LSB, + pccs->var.tx_request.tx_data_length + 1); + writeb(0, &pccs->var.tx_request.pow_sav_mode); + + ptx = local->sram + addr; + /* fill in the mac header */ + writeb(PROTOCOL_VER | AUTHENTIC_TYPE, &ptx->mac.frame_ctl_1); + writeb(0, &ptx->mac.frame_ctl_2); + + memcpy_toio(ptx->mac.addr_1, dest, ADDRLEN); + memcpy_toio(ptx->mac.addr_2, local->sparm.b4.a_mac_addr, ADDRLEN); + memcpy_toio(ptx->mac.addr_3, local->bss_id, ADDRLEN); + + /* Fill in msg body with protocol 00 00, sequence 01 00 ,status 00 00 */ + memset_io(ptx->var, 0, 6); + writeb(auth_type & 0xff, ptx->var + 2); + + /* Interrupt the firmware to process the command */ + if (interrupt_ecf(local, ccsindex)) { + pr_debug( + "ray_cs send authentication request failed - ECF not ready for intr\n"); + writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); + return -1; + } + return 0; +} /* End build_auth_frame */ + +/*===========================================================================*/ +#ifdef CONFIG_PROC_FS +static ssize_t ray_cs_essid_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) +{ + static char proc_essid[33]; + unsigned int len = count; + + if (len > 32) + len = 32; + memset(proc_essid, 0, 33); + if (copy_from_user(proc_essid, buffer, len)) + return -EFAULT; + essid = proc_essid; + return count; +} + +static const struct proc_ops ray_cs_essid_proc_ops = { + .proc_write = ray_cs_essid_proc_write, + .proc_lseek = noop_llseek, +}; + +static ssize_t int_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + static char proc_number[10]; + char *p; + int nr, len; + + if (!count) + return 0; + + if (count > 9) + return -EINVAL; + if (copy_from_user(proc_number, buffer, count)) + return -EFAULT; + p = proc_number; + nr = 0; + len = count; + do { + unsigned int c = *p - '0'; + if (c > 9) + return -EINVAL; + nr = nr * 10 + c; + p++; + } while (--len); + *(int *)pde_data(file_inode(file)) = nr; + return count; +} + +static const struct proc_ops int_proc_ops = { + .proc_write = int_proc_write, + .proc_lseek = noop_llseek, +}; +#endif + +static const struct pcmcia_device_id ray_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x01a6, 0x0000), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, ray_ids); + +static struct pcmcia_driver ray_driver = { + .owner = THIS_MODULE, + .name = "ray_cs", + .probe = ray_probe, + .remove = ray_detach, + .id_table = ray_ids, + .suspend = ray_suspend, + .resume = ray_resume, +}; + +static int __init init_ray_cs(void) +{ + int rc; + + pr_debug("%s\n", rcsid); + rc = pcmcia_register_driver(&ray_driver); + pr_debug("raylink init_module register_pcmcia_driver returns 0x%x\n", + rc); + if (rc) + return rc; + +#ifdef CONFIG_PROC_FS + proc_mkdir("driver/ray_cs", NULL); + + proc_create_single("driver/ray_cs/ray_cs", 0, NULL, ray_cs_proc_show); + proc_create("driver/ray_cs/essid", 0200, NULL, &ray_cs_essid_proc_ops); + proc_create_data("driver/ray_cs/net_type", 0200, NULL, &int_proc_ops, + &net_type); + proc_create_data("driver/ray_cs/translate", 0200, NULL, &int_proc_ops, + &translate); +#endif + translate = !!translate; + return 0; +} /* init_ray_cs */ + +/*===========================================================================*/ + +static void __exit exit_ray_cs(void) +{ + pr_debug("ray_cs: cleanup_module\n"); + +#ifdef CONFIG_PROC_FS + remove_proc_subtree("driver/ray_cs", NULL); +#endif + + pcmcia_unregister_driver(&ray_driver); +} /* exit_ray_cs */ + +module_init(init_ray_cs); +module_exit(exit_ray_cs); + +/*===========================================================================*/ diff --git a/drivers/net/wireless/legacy/ray_cs.h b/drivers/net/wireless/legacy/ray_cs.h new file mode 100644 index 000000000000..0609d8625019 --- /dev/null +++ b/drivers/net/wireless/legacy/ray_cs.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Raytheon wireless LAN PCMCIA card driver for Linux + A PCMCIA client driver for the Raylink wireless network card + Written by Corey Thomas +*/ + +#ifndef _RAY_CS_H_ +#define _RAY_CS_H_ + +struct beacon_rx { + struct mac_header mac; + UCHAR timestamp[8]; + UCHAR beacon_intvl[2]; + UCHAR capability[2]; + UCHAR elements[sizeof(struct essid_element) + + sizeof(struct rates_element) + + sizeof(struct freq_hop_element) + + sizeof(struct japan_call_sign_element) + + sizeof(struct tim_element)]; +}; + +/* Return values for get_free{,_tx}_ccs */ +#define ECCSFULL (-1) +#define ECCSBUSY (-2) +#define ECARDGONE (-3) + +typedef struct ray_dev_t { + int card_status; + int authentication_state; + void __iomem *sram; /* pointer to beginning of shared RAM */ + void __iomem *amem; /* pointer to attribute mem window */ + void __iomem *rmem; /* pointer to receive buffer window */ + struct pcmcia_device *finder; /* pointer back to struct pcmcia_device for card */ + struct timer_list timer; + unsigned long tx_ccs_lock; + unsigned long ccs_lock; + int dl_param_ccs; + union { + struct b4_startup_params b4; + struct b5_startup_params b5; + } sparm; + int timeout_flag; + UCHAR supported_rates[8]; + UCHAR japan_call_sign[12]; + struct startup_res_6 startup_res; + int num_multi; + /* Network parameters from start/join */ + UCHAR bss_id[6]; + UCHAR auth_id[6]; + UCHAR net_default_tx_rate; + UCHAR encryption; + struct net_device_stats stats; + + UCHAR net_type; + UCHAR sta_type; + UCHAR fw_ver; + UCHAR fw_bld; + UCHAR fw_var; + UCHAR ASIC_version; + UCHAR assoc_id[2]; + UCHAR tib_length; + UCHAR last_rsl; + int beacon_rxed; + struct beacon_rx last_bcn; + iw_stats wstats; /* Wireless specific stats */ +#ifdef WIRELESS_SPY + struct iw_spy_data spy_data; + struct iw_public_data wireless_data; +#endif /* WIRELESS_SPY */ + +} ray_dev_t; +/*****************************************************************************/ + +#endif /* _RAY_CS_H_ */ diff --git a/drivers/net/wireless/legacy/rayctl.h b/drivers/net/wireless/legacy/rayctl.h new file mode 100644 index 000000000000..2b0f332043d7 --- /dev/null +++ b/drivers/net/wireless/legacy/rayctl.h @@ -0,0 +1,734 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RAYCTL_H_ +#define _RAYCTL_H_ + +typedef unsigned char UCHAR; + +/****** IEEE 802.11 constants ************************************************/ +#define ADDRLEN 6 +/* Frame control 1 bit fields */ +#define PROTOCOL_VER 0x00 +#define DATA_TYPE 0x08 +#define ASSOC_REQ_TYPE 0x00 +#define ASSOC_RESP_TYPE 0x10 +#define REASSOC_REQ_TYPE 0x20 +#define REASSOC_RESP_TYPE 0x30 +#define NULL_MSG_TYPE 0x48 +#define BEACON_TYPE 0x80 +#define DISASSOC_TYPE 0xA0 +#define PSPOLL_TYPE 0xA4 +#define AUTHENTIC_TYPE 0xB0 +#define DEAUTHENTIC_TYPE 0xC0 +/* Frame control 2 bit fields */ +#define FC2_TO_DS 0x01 +#define FC2_FROM_DS 0x02 +#define FC2_MORE_FRAG 0x04 +#define FC2_RETRY 0x08 +#define FC2_PSM 0x10 +#define FC2_MORE_DATA 0x20 +#define FC2_WEP 0x40 +#define FC2_ORDER 0x80 +/*****************************************************************************/ +/* 802.11 element ID's and lengths */ +#define C_BP_CAPABILITY_ESS 0x01 +#define C_BP_CAPABILITY_IBSS 0x02 +#define C_BP_CAPABILITY_CF_POLLABLE 0x04 +#define C_BP_CAPABILITY_CF_POLL_REQUEST 0x08 +#define C_BP_CAPABILITY_PRIVACY 0x10 + +#define C_ESSID_ELEMENT_ID 0 +#define C_ESSID_ELEMENT_MAX_LENGTH 32 + +#define C_SUPPORTED_RATES_ELEMENT_ID 1 +#define C_SUPPORTED_RATES_ELEMENT_LENGTH 2 + +#define C_FH_PARAM_SET_ELEMENT_ID 2 +#define C_FH_PARAM_SET_ELEMENT_LNGTH 5 + +#define C_CF_PARAM_SET_ELEMENT_ID 4 +#define C_CF_PARAM_SET_ELEMENT_LNGTH 6 + +#define C_TIM_ELEMENT_ID 5 +#define C_TIM_BITMAP_LENGTH 251 +#define C_TIM_BMCAST_BIT 0x01 + +#define C_IBSS_ELEMENT_ID 6 +#define C_IBSS_ELEMENT_LENGTH 2 + +#define C_JAPAN_CALL_SIGN_ELEMENT_ID 51 +#define C_JAPAN_CALL_SIGN_ELEMENT_LNGTH 12 + +#define C_DISASSOC_REASON_CODE_LEN 2 +#define C_DISASSOC_REASON_CODE_DEFAULT 8 + +#define C_CRC_LEN 4 +#define C_NUM_SUPPORTED_RATES 8 +/****** IEEE 802.11 mac header for type data packets *************************/ +struct mac_header { + UCHAR frame_ctl_1; + UCHAR frame_ctl_2; + UCHAR duration_lsb; + UCHAR duration_msb; + UCHAR addr_1[ADDRLEN]; + UCHAR addr_2[ADDRLEN]; + UCHAR addr_3[ADDRLEN]; + UCHAR seq_frag_num[2]; +/* UCHAR addr_4[ADDRLEN]; *//* only present for AP to AP (TO DS and FROM DS */ +}; +/****** IEEE 802.11 frame element structures *********************************/ +struct essid_element +{ + UCHAR id; + UCHAR length; + UCHAR text[C_ESSID_ELEMENT_MAX_LENGTH]; +}; +struct rates_element +{ + UCHAR id; + UCHAR length; + UCHAR value[8]; +}; +struct freq_hop_element +{ + UCHAR id; + UCHAR length; + UCHAR dwell_time[2]; + UCHAR hop_set; + UCHAR hop_pattern; + UCHAR hop_index; +}; +struct tim_element +{ + UCHAR id; + UCHAR length; + UCHAR dtim_count; + UCHAR dtim_period; + UCHAR bitmap_control; + UCHAR tim[C_TIM_BITMAP_LENGTH]; +}; +struct ibss_element +{ + UCHAR id; + UCHAR length; + UCHAR atim_window[2]; +}; +struct japan_call_sign_element +{ + UCHAR id; + UCHAR length; + UCHAR call_sign[12]; +}; +/****** Beacon message structures ********************************************/ +/* .elements is a large lump of max size because elements are variable size */ +struct infra_beacon +{ + UCHAR timestamp[8]; + UCHAR beacon_intvl[2]; + UCHAR capability[2]; + UCHAR elements[sizeof(struct essid_element) + + sizeof(struct rates_element) + + sizeof(struct freq_hop_element) + + sizeof(struct japan_call_sign_element) + + sizeof(struct tim_element)]; +}; +struct adhoc_beacon +{ + UCHAR timestamp[8]; + UCHAR beacon_intvl[2]; + UCHAR capability[2]; + UCHAR elements[sizeof(struct essid_element) + + sizeof(struct rates_element) + + sizeof(struct freq_hop_element) + + sizeof(struct japan_call_sign_element) + + sizeof(struct ibss_element)]; +}; +/*****************************************************************************/ +/*****************************************************************************/ +/* #define C_MAC_HDR_2_WEP 0x40 */ +/* TX/RX CCS constants */ +#define TX_HEADER_LENGTH 0x1C +#define RX_MAC_HEADER_LENGTH 0x18 +#define TX_AUTHENTICATE_LENGTH (TX_HEADER_LENGTH + 6) +#define TX_AUTHENTICATE_LENGTH_MSB (TX_AUTHENTICATE_LENGTH >> 8) +#define TX_AUTHENTICATE_LENGTH_LSB (TX_AUTHENTICATE_LENGTH & 0xff) +#define TX_DEAUTHENTICATE_LENGTH (TX_HEADER_LENGTH + 2) +#define TX_DEAUTHENTICATE_LENGTH_MSB (TX_AUTHENTICATE_LENGTH >> 8) +#define TX_DEAUTHENTICATE_LENGTH_LSB (TX_AUTHENTICATE_LENGTH & 0xff) +#define FCS_LEN 4 + +#define ADHOC 0 +#define INFRA 1 + +#define TYPE_STA 0 +#define TYPE_AP 1 + +#define PASSIVE_SCAN 1 +#define ACTIVE_SCAN 1 + +#define PSM_CAM 0 + +/* Country codes */ +#define USA 1 +#define EUROPE 2 +#define JAPAN 3 +#define KOREA 4 +#define SPAIN 5 +#define FRANCE 6 +#define ISRAEL 7 +#define AUSTRALIA 8 +#define JAPAN_TEST 9 + +/* Hop pattern lengths */ +#define USA_HOP_MOD 79 +#define EUROPE_HOP_MOD 79 +#define JAPAN_HOP_MOD 23 +#define KOREA_HOP_MOD 23 +#define SPAIN_HOP_MOD 27 +#define FRANCE_HOP_MOD 35 +#define ISRAEL_HOP_MOD 35 +#define AUSTRALIA_HOP_MOD 47 +#define JAPAN_TEST_HOP_MOD 23 + +#define ESSID_SIZE 32 +/**********************************************************************/ +/* CIS Register Constants */ +#define CIS_OFFSET 0x0f00 +/* Configuration Option Register (0x0F00) */ +#define COR_OFFSET 0x00 +#define COR_SOFT_RESET 0x80 +#define COR_LEVEL_IRQ 0x40 +#define COR_CONFIG_NUM 0x01 +#define COR_DEFAULT (COR_LEVEL_IRQ | COR_CONFIG_NUM) + +/* Card Configuration and Status Register (0x0F01) */ +#define CCSR_OFFSET 0x01 +#define CCSR_HOST_INTR_PENDING 0x01 +#define CCSR_POWER_DOWN 0x04 + +/* HCS Interrupt Register (0x0F05) */ +#define HCS_INTR_OFFSET 0x05 +/* #define HCS_INTR_OFFSET 0x0A */ +#define HCS_INTR_CLEAR 0x00 + +/* ECF Interrupt Register (0x0F06) */ +#define ECF_INTR_OFFSET 0x06 +/* #define ECF_INTR_OFFSET 0x0C */ +#define ECF_INTR_SET 0x01 + +/* Authorization Register 0 (0x0F08) */ +#define AUTH_0_ON 0x57 + +/* Authorization Register 1 (0x0F09) */ +#define AUTH_1_ON 0x82 + +/* Program Mode Register (0x0F0A) */ +#define PC2PM 0x02 +#define PC2CAL 0x10 +#define PC2MLSE 0x20 + +/* PC Test Mode Register (0x0F0B) */ +#define PC_TEST_MODE 0x08 + +/* Frequency Control Word (0x0F10) */ +/* Range 0x02 - 0xA6 */ + +/* Test Mode Control 1-4 (0x0F14 - 0x0F17) */ + +/**********************************************************************/ + +/* Shared RAM Area */ +#define SCB_BASE 0x0000 +#define STATUS_BASE 0x0100 +#define HOST_TO_ECF_BASE 0x0200 +#define ECF_TO_HOST_BASE 0x0300 +#define CCS_BASE 0x0400 +#define RCS_BASE 0x0800 +#define INFRA_TIM_BASE 0x0C00 +#define SSID_LIST_BASE 0x0D00 +#define TX_BUF_BASE 0x1000 +#define RX_BUF_BASE 0x8000 + +#define NUMBER_OF_CCS 64 +#define NUMBER_OF_RCS 64 +/*#define NUMBER_OF_TX_CCS 14 */ +#define NUMBER_OF_TX_CCS 14 + +#define TX_BUF_SIZE (2048 - sizeof(struct tx_msg)) +#define RX_BUFF_END 0x3FFF +/* Values for buffer_status */ +#define CCS_BUFFER_FREE 0 +#define CCS_BUFFER_BUSY 1 +#define CCS_COMMAND_COMPLETE 2 +#define CCS_COMMAND_FAILED 3 + +/* Values for cmd */ +#define CCS_DOWNLOAD_STARTUP_PARAMS 1 +#define CCS_UPDATE_PARAMS 2 +#define CCS_REPORT_PARAMS 3 +#define CCS_UPDATE_MULTICAST_LIST 4 +#define CCS_UPDATE_POWER_SAVINGS_MODE 5 +#define CCS_START_NETWORK 6 +#define CCS_JOIN_NETWORK 7 +#define CCS_START_ASSOCIATION 8 +#define CCS_TX_REQUEST 9 +#define CCS_TEST_MEMORY 0xa +#define CCS_SHUTDOWN 0xb +#define CCS_DUMP_MEMORY 0xc +#define CCS_START_TIMER 0xe +#define CCS_LAST_CMD CCS_START_TIMER + +/* Values for link field */ +#define CCS_END_LIST 0xff + +/* values for buffer_status field */ +#define RCS_BUFFER_FREE 0 +#define RCS_BUFFER_BUSY 1 +#define RCS_COMPLETE 2 +#define RCS_FAILED 3 +#define RCS_BUFFER_RELEASE 0xFF + +/* values for interrupt_id field */ +#define PROCESS_RX_PACKET 0x80 /* */ +#define REJOIN_NET_COMPLETE 0x81 /* RCS ID: Rejoin Net Complete */ +#define ROAMING_INITIATED 0x82 /* RCS ID: Roaming Initiated */ +#define JAPAN_CALL_SIGN_RXD 0x83 /* RCS ID: New Japan Call Sign */ + +/*****************************************************************************/ +/* Memory types for dump memory command */ +#define C_MEM_PROG 0 +#define C_MEM_XDATA 1 +#define C_MEM_SFR 2 +#define C_MEM_IDATA 3 + +/*** Return values for hw_xmit **********/ +#define XMIT_OK (0) +#define XMIT_MSG_BAD (-1) +#define XMIT_NO_CCS (-2) +#define XMIT_NO_INTR (-3) +#define XMIT_NEED_AUTH (-4) + +/*** Values for card status */ +#define CARD_INSERTED (0) + +#define CARD_AWAITING_PARAM (1) +#define CARD_INIT_ERROR (11) + +#define CARD_DL_PARAM (2) +#define CARD_DL_PARAM_ERROR (12) + +#define CARD_DOING_ACQ (3) + +#define CARD_ACQ_COMPLETE (4) +#define CARD_ACQ_FAILED (14) + +#define CARD_AUTH_COMPLETE (5) +#define CARD_AUTH_REFUSED (15) + +#define CARD_ASSOC_COMPLETE (6) +#define CARD_ASSOC_FAILED (16) + +/*** Values for authentication_state ***********************************/ +#define UNAUTHENTICATED (0) +#define AWAITING_RESPONSE (1) +#define AUTHENTICATED (2) +#define NEED_TO_AUTH (3) + +/*** Values for authentication type ************************************/ +#define OPEN_AUTH_REQUEST (1) +#define OPEN_AUTH_RESPONSE (2) +#define BROADCAST_DEAUTH (0xc0) +/*** Values for timer functions ****************************************/ +#define TODO_NOTHING (0) +#define TODO_VERIFY_DL_START (-1) +#define TODO_START_NET (-2) +#define TODO_JOIN_NET (-3) +#define TODO_AUTHENTICATE_TIMEOUT (-4) +#define TODO_SEND_CCS (-5) +/***********************************************************************/ +/* Parameter passing structure for update/report parameter CCS's */ +struct object_id { + void *object_addr; + unsigned char object_length; +}; + +#define OBJID_network_type 0 +#define OBJID_acting_as_ap_status 1 +#define OBJID_current_ess_id 2 +#define OBJID_scanning_mode 3 +#define OBJID_power_mgt_state 4 +#define OBJID_mac_address 5 +#define OBJID_frag_threshold 6 +#define OBJID_hop_time 7 +#define OBJID_beacon_period 8 +#define OBJID_dtim_period 9 +#define OBJID_retry_max 10 +#define OBJID_ack_timeout 11 +#define OBJID_sifs 12 +#define OBJID_difs 13 +#define OBJID_pifs 14 +#define OBJID_rts_threshold 15 +#define OBJID_scan_dwell_time 16 +#define OBJID_max_scan_dwell_time 17 +#define OBJID_assoc_resp_timeout 18 +#define OBJID_adhoc_scan_cycle_max 19 +#define OBJID_infra_scan_cycle_max 20 +#define OBJID_infra_super_cycle_max 21 +#define OBJID_promiscuous_mode 22 +#define OBJID_unique_word 23 +#define OBJID_slot_time 24 +#define OBJID_roaming_low_snr 25 +#define OBJID_low_snr_count_thresh 26 +#define OBJID_infra_missed_bcn 27 +#define OBJID_adhoc_missed_bcn 28 +#define OBJID_curr_country_code 29 +#define OBJID_hop_pattern 30 +#define OBJID_reserved 31 +#define OBJID_cw_max_msb 32 +#define OBJID_cw_min_msb 33 +#define OBJID_noise_filter_gain 34 +#define OBJID_noise_limit_offset 35 +#define OBJID_det_rssi_thresh_offset 36 +#define OBJID_med_busy_thresh_offset 37 +#define OBJID_det_sync_thresh 38 +#define OBJID_test_mode 39 +#define OBJID_test_min_chan_num 40 +#define OBJID_test_max_chan_num 41 +#define OBJID_allow_bcast_ID_prbrsp 42 +#define OBJID_privacy_must_start 43 +#define OBJID_privacy_can_join 44 +#define OBJID_basic_rate_set 45 + +/**** Configuration/Status/Control Area ***************************/ +/* System Control Block (SCB) Area + * Located at Shared RAM offset 0 + */ +struct scb { + UCHAR ccs_index; + UCHAR rcs_index; +}; + +/****** Status area at Shared RAM offset 0x0100 ******************************/ +struct status { + UCHAR mrx_overflow_for_host; /* 0=ECF may write, 1=host may write*/ + UCHAR mrx_checksum_error_for_host; /* 0=ECF may write, 1=host may write*/ + UCHAR rx_hec_error_for_host; /* 0=ECF may write, 1=host may write*/ + UCHAR reserved1; + short mrx_overflow; /* ECF increments on rx overflow */ + short mrx_checksum_error; /* ECF increments on rx CRC error */ + short rx_hec_error; /* ECF incs on mac header CRC error */ + UCHAR rxnoise; /* Average RSL measurement */ +}; + +/****** Host-to-ECF Data Area at Shared RAM offset 0x200 *********************/ +struct host_to_ecf_area { + +}; + +/****** ECF-to-Host Data Area at Shared RAM offset 0x0300 ********************/ +struct startup_res_518 { + UCHAR startup_word; + UCHAR station_addr[ADDRLEN]; + UCHAR calc_prog_chksum; + UCHAR calc_cis_chksum; + UCHAR ecf_spare[7]; + UCHAR japan_call_sign[12]; +}; + +struct startup_res_6 { + UCHAR startup_word; + UCHAR station_addr[ADDRLEN]; + UCHAR reserved; + UCHAR supp_rates[8]; + UCHAR japan_call_sign[12]; + UCHAR calc_prog_chksum; + UCHAR calc_cis_chksum; + UCHAR firmware_version[3]; + UCHAR asic_version; + UCHAR tib_length; +}; + +struct start_join_net_params { + UCHAR net_type; + UCHAR ssid[ESSID_SIZE]; + UCHAR reserved; + UCHAR privacy_can_join; +}; + +/****** Command Control Structure area at Shared ram offset 0x0400 ***********/ +/* Structures for command specific parameters (ccs.var) */ +struct update_param_cmd { + UCHAR object_id; + UCHAR number_objects; + UCHAR failure_cause; +}; +struct report_param_cmd { + UCHAR object_id; + UCHAR number_objects; + UCHAR failure_cause; + UCHAR length; +}; +struct start_network_cmd { + UCHAR update_param; + UCHAR bssid[ADDRLEN]; + UCHAR net_initiated; + UCHAR net_default_tx_rate; + UCHAR encryption; +}; +struct join_network_cmd { + UCHAR update_param; + UCHAR bssid[ADDRLEN]; + UCHAR net_initiated; + UCHAR net_default_tx_rate; + UCHAR encryption; +}; +struct tx_requested_cmd { + + UCHAR tx_data_ptr[2]; + UCHAR tx_data_length[2]; + UCHAR host_reserved[2]; + UCHAR reserved[3]; + UCHAR tx_rate; + UCHAR pow_sav_mode; + UCHAR retries; + UCHAR antenna; +}; +struct tx_requested_cmd_4 { + + UCHAR tx_data_ptr[2]; + UCHAR tx_data_length[2]; + UCHAR dest_addr[ADDRLEN]; + UCHAR pow_sav_mode; + UCHAR retries; + UCHAR station_id; +}; +struct memory_dump_cmd { + UCHAR memory_type; + UCHAR memory_ptr[2]; + UCHAR length; +}; +struct update_association_cmd { + UCHAR status; + UCHAR aid[2]; +}; +struct start_timer_cmd { + UCHAR duration[2]; +}; + +struct ccs { + UCHAR buffer_status; /* 0 = buffer free, 1 = buffer busy */ + /* 2 = command complete, 3 = failed */ + UCHAR cmd; /* command to ECF */ + UCHAR link; /* link to next CCS, FF=end of list */ + /* command specific parameters */ + union { + char reserved[13]; + struct update_param_cmd update_param; + struct report_param_cmd report_param; + UCHAR nummulticast; + UCHAR mode; + struct start_network_cmd start_network; + struct join_network_cmd join_network; + struct tx_requested_cmd tx_request; + struct memory_dump_cmd memory_dump; + struct update_association_cmd update_assoc; + struct start_timer_cmd start_timer; + } var; +}; + +/*****************************************************************************/ +/* Transmit buffer structures */ +struct tib_structure { + UCHAR ccs_index; + UCHAR psm; + UCHAR pass_fail; + UCHAR retry_count; + UCHAR max_retries; + UCHAR frags_remaining; + UCHAR no_rb; + UCHAR rts_reqd; + UCHAR csma_tx_cntrl_2; + UCHAR sifs_tx_cntrl_2; + UCHAR tx_dma_addr_1[2]; + UCHAR tx_dma_addr_2[2]; + UCHAR var_dur_2mhz[2]; + UCHAR var_dur_1mhz[2]; + UCHAR max_dur_2mhz[2]; + UCHAR max_dur_1mhz[2]; + UCHAR hdr_len; + UCHAR max_frag_len[2]; + UCHAR var_len[2]; + UCHAR phy_hdr_4; + UCHAR mac_hdr_1; + UCHAR mac_hdr_2; + UCHAR sid[2]; +}; + +struct phy_header { + UCHAR sfd[2]; + UCHAR hdr_3; + UCHAR hdr_4; +}; +struct ray_rx_msg { + struct mac_header mac; + UCHAR var[]; +}; + +struct tx_msg { + struct tib_structure tib; + struct phy_header phy; + struct mac_header mac; + UCHAR var[1]; +}; + +/****** ECF Receive Control Structure (RCS) Area at Shared RAM offset 0x0800 */ +/* Structures for command specific parameters (rcs.var) */ +struct rx_packet_cmd { + UCHAR rx_data_ptr[2]; + UCHAR rx_data_length[2]; + UCHAR rx_sig_lev; + UCHAR next_frag_rcs_index; + UCHAR totalpacketlength[2]; +}; +struct rejoin_net_cmplt_cmd { + UCHAR reserved; + UCHAR bssid[ADDRLEN]; +}; +struct japan_call_sign_rxd { + UCHAR rxd_call_sign[8]; + UCHAR reserved[5]; +}; + +struct rcs { + UCHAR buffer_status; + UCHAR interrupt_id; + UCHAR link_field; + /* command specific parameters */ + union { + UCHAR reserved[13]; + struct rx_packet_cmd rx_packet; + struct rejoin_net_cmplt_cmd rejoin_net_complete; + struct japan_call_sign_rxd japan_call_sign; + } var; +}; + +/****** Startup parameter structures for both versions of firmware ***********/ +struct b4_startup_params { + UCHAR a_network_type; /* C_ADHOC, C_INFRA */ + UCHAR a_acting_as_ap_status; /* C_TYPE_STA, C_TYPE_AP */ + UCHAR a_current_ess_id[ESSID_SIZE]; /* Null terminated unless 32 long */ + UCHAR a_scanning_mode; /* passive 0, active 1 */ + UCHAR a_power_mgt_state; /* CAM 0, */ + UCHAR a_mac_addr[ADDRLEN]; /* */ + UCHAR a_frag_threshold[2]; /* 512 */ + UCHAR a_hop_time[2]; /* 16k * 2**n, n=0-4 in Kus */ + UCHAR a_beacon_period[2]; /* n * a_hop_time in Kus */ + UCHAR a_dtim_period; /* in beacons */ + UCHAR a_retry_max; /* */ + UCHAR a_ack_timeout; /* */ + UCHAR a_sifs; /* */ + UCHAR a_difs; /* */ + UCHAR a_pifs; /* */ + UCHAR a_rts_threshold[2]; /* */ + UCHAR a_scan_dwell_time[2]; /* */ + UCHAR a_max_scan_dwell_time[2]; /* */ + UCHAR a_assoc_resp_timeout_thresh; /* */ + UCHAR a_adhoc_scan_cycle_max; /* */ + UCHAR a_infra_scan_cycle_max; /* */ + UCHAR a_infra_super_scan_cycle_max; /* */ + UCHAR a_promiscuous_mode; /* */ + UCHAR a_unique_word[2]; /* */ + UCHAR a_slot_time; /* */ + UCHAR a_roaming_low_snr_thresh; /* */ + UCHAR a_low_snr_count_thresh; /* */ + UCHAR a_infra_missed_bcn_thresh; /* */ + UCHAR a_adhoc_missed_bcn_thresh; /* */ + UCHAR a_curr_country_code; /* C_USA */ + UCHAR a_hop_pattern; /* */ + UCHAR a_hop_pattern_length; /* */ +/* b4 - b5 differences start here */ + UCHAR a_cw_max; /* */ + UCHAR a_cw_min; /* */ + UCHAR a_noise_filter_gain; /* */ + UCHAR a_noise_limit_offset; /* */ + UCHAR a_det_rssi_thresh_offset; /* */ + UCHAR a_med_busy_thresh_offset; /* */ + UCHAR a_det_sync_thresh; /* */ + UCHAR a_test_mode; /* */ + UCHAR a_test_min_chan_num; /* */ + UCHAR a_test_max_chan_num; /* */ + UCHAR a_rx_tx_delay; /* */ + UCHAR a_current_bss_id[ADDRLEN]; /* */ + UCHAR a_hop_set; /* */ +}; +struct b5_startup_params { + UCHAR a_network_type; /* C_ADHOC, C_INFRA */ + UCHAR a_acting_as_ap_status; /* C_TYPE_STA, C_TYPE_AP */ + UCHAR a_current_ess_id[ESSID_SIZE]; /* Null terminated unless 32 long */ + UCHAR a_scanning_mode; /* passive 0, active 1 */ + UCHAR a_power_mgt_state; /* CAM 0, */ + UCHAR a_mac_addr[ADDRLEN]; /* */ + UCHAR a_frag_threshold[2]; /* 512 */ + UCHAR a_hop_time[2]; /* 16k * 2**n, n=0-4 in Kus */ + UCHAR a_beacon_period[2]; /* n * a_hop_time in Kus */ + UCHAR a_dtim_period; /* in beacons */ + UCHAR a_retry_max; /* 4 */ + UCHAR a_ack_timeout; /* */ + UCHAR a_sifs; /* */ + UCHAR a_difs; /* */ + UCHAR a_pifs; /* */ + UCHAR a_rts_threshold[2]; /* */ + UCHAR a_scan_dwell_time[2]; /* */ + UCHAR a_max_scan_dwell_time[2]; /* */ + UCHAR a_assoc_resp_timeout_thresh; /* */ + UCHAR a_adhoc_scan_cycle_max; /* */ + UCHAR a_infra_scan_cycle_max; /* */ + UCHAR a_infra_super_scan_cycle_max; /* */ + UCHAR a_promiscuous_mode; /* */ + UCHAR a_unique_word[2]; /* */ + UCHAR a_slot_time; /* */ + UCHAR a_roaming_low_snr_thresh; /* */ + UCHAR a_low_snr_count_thresh; /* */ + UCHAR a_infra_missed_bcn_thresh; /* */ + UCHAR a_adhoc_missed_bcn_thresh; /* */ + UCHAR a_curr_country_code; /* C_USA */ + UCHAR a_hop_pattern; /* */ + UCHAR a_hop_pattern_length; /* */ +/* b4 - b5 differences start here */ + UCHAR a_cw_max[2]; /* */ + UCHAR a_cw_min[2]; /* */ + UCHAR a_noise_filter_gain; /* */ + UCHAR a_noise_limit_offset; /* */ + UCHAR a_det_rssi_thresh_offset; /* */ + UCHAR a_med_busy_thresh_offset; /* */ + UCHAR a_det_sync_thresh; /* */ + UCHAR a_test_mode; /* */ + UCHAR a_test_min_chan_num; /* */ + UCHAR a_test_max_chan_num; /* */ + UCHAR a_allow_bcast_SSID_probe_rsp; + UCHAR a_privacy_must_start; + UCHAR a_privacy_can_join; + UCHAR a_basic_rate_set[8]; +}; + +/*****************************************************************************/ +#define RAY_IOCG_PARMS (SIOCDEVPRIVATE) +#define RAY_IOCS_PARMS (SIOCDEVPRIVATE + 1) +#define RAY_DO_CMD (SIOCDEVPRIVATE + 2) + +/****** ethernet <-> 802.11 translation **************************************/ +typedef struct snaphdr_t +{ + UCHAR dsap; + UCHAR ssap; + UCHAR ctrl; + UCHAR org[3]; + UCHAR ethertype[2]; +} snaphdr_t; + +#define BRIDGE_ENCAP 0xf80000 +#define RFC1042_ENCAP 0 +#define SNAP_ID 0x0003aaaa +#define RAY_IPX_TYPE 0x8137 +#define APPLEARP_TYPE 0x80f3 +/*****************************************************************************/ +#endif /* _RAYCTL_H_ */ diff --git a/drivers/net/wireless/legacy/rndis_wlan.c b/drivers/net/wireless/legacy/rndis_wlan.c new file mode 100644 index 000000000000..bf72e5fd39cf --- /dev/null +++ b/drivers/net/wireless/legacy/rndis_wlan.c @@ -0,0 +1,3760 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for RNDIS based wireless USB devices. + * + * Copyright (C) 2007 by Bjorge Dijkstra + * Copyright (C) 2008-2009 by Jussi Kivilinna + * + * Portions of this file are based on NDISwrapper project, + * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani + * http://ndiswrapper.sourceforge.net/ + */ + +// #define DEBUG // error path messages, extra info +// #define VERBOSE // more; success messages + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* NOTE: All these are settings for Broadcom chipset */ +static char modparam_country[4] = "EU"; +module_param_string(country, modparam_country, 4, 0444); +MODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU"); + +static int modparam_frameburst = 1; +module_param_named(frameburst, modparam_frameburst, int, 0444); +MODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)"); + +static int modparam_afterburner = 0; +module_param_named(afterburner, modparam_afterburner, int, 0444); +MODULE_PARM_DESC(afterburner, + "enable afterburner aka '125 High Speed Mode' (default: off)"); + +static int modparam_power_save = 0; +module_param_named(power_save, modparam_power_save, int, 0444); +MODULE_PARM_DESC(power_save, + "set power save mode: 0=off, 1=on, 2=fast (default: off)"); + +static int modparam_power_output = 3; +module_param_named(power_output, modparam_power_output, int, 0444); +MODULE_PARM_DESC(power_output, + "set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)"); + +static int modparam_roamtrigger = -70; +module_param_named(roamtrigger, modparam_roamtrigger, int, 0444); +MODULE_PARM_DESC(roamtrigger, + "set roaming dBm trigger: -80=optimize for distance, " + "-60=bandwidth (default: -70)"); + +static int modparam_roamdelta = 1; +module_param_named(roamdelta, modparam_roamdelta, int, 0444); +MODULE_PARM_DESC(roamdelta, + "set roaming tendency: 0=aggressive, 1=moderate, " + "2=conservative (default: moderate)"); + +static int modparam_workaround_interval; +module_param_named(workaround_interval, modparam_workaround_interval, + int, 0444); +MODULE_PARM_DESC(workaround_interval, + "set stall workaround interval in msecs (0=disabled) (default: 0)"); + +/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */ +#define WL_NOISE -96 /* typical noise level in dBm */ +#define WL_SIGMAX -32 /* typical maximum signal level in dBm */ + + +/* Assume that Broadcom 4320 (only chipset at time of writing known to be + * based on wireless rndis) has default txpower of 13dBm. + * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications. + * 100% : 20 mW ~ 13dBm + * 75% : 15 mW ~ 12dBm + * 50% : 10 mW ~ 10dBm + * 25% : 5 mW ~ 7dBm + */ +#define BCM4320_DEFAULT_TXPOWER_DBM_100 13 +#define BCM4320_DEFAULT_TXPOWER_DBM_75 12 +#define BCM4320_DEFAULT_TXPOWER_DBM_50 10 +#define BCM4320_DEFAULT_TXPOWER_DBM_25 7 + +/* Known device types */ +#define RNDIS_UNKNOWN 0 +#define RNDIS_BCM4320A 1 +#define RNDIS_BCM4320B 2 + + +/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c + * slightly modified for datatype endianess, etc + */ +#define NDIS_802_11_LENGTH_SSID 32 +#define NDIS_802_11_LENGTH_RATES 8 +#define NDIS_802_11_LENGTH_RATES_EX 16 + +enum ndis_80211_net_type { + NDIS_80211_TYPE_FREQ_HOP, + NDIS_80211_TYPE_DIRECT_SEQ, + NDIS_80211_TYPE_OFDM_A, + NDIS_80211_TYPE_OFDM_G +}; + +enum ndis_80211_net_infra { + NDIS_80211_INFRA_ADHOC, + NDIS_80211_INFRA_INFRA, + NDIS_80211_INFRA_AUTO_UNKNOWN +}; + +enum ndis_80211_auth_mode { + NDIS_80211_AUTH_OPEN, + NDIS_80211_AUTH_SHARED, + NDIS_80211_AUTH_AUTO_SWITCH, + NDIS_80211_AUTH_WPA, + NDIS_80211_AUTH_WPA_PSK, + NDIS_80211_AUTH_WPA_NONE, + NDIS_80211_AUTH_WPA2, + NDIS_80211_AUTH_WPA2_PSK +}; + +enum ndis_80211_encr_status { + NDIS_80211_ENCR_WEP_ENABLED, + NDIS_80211_ENCR_DISABLED, + NDIS_80211_ENCR_WEP_KEY_ABSENT, + NDIS_80211_ENCR_NOT_SUPPORTED, + NDIS_80211_ENCR_TKIP_ENABLED, + NDIS_80211_ENCR_TKIP_KEY_ABSENT, + NDIS_80211_ENCR_CCMP_ENABLED, + NDIS_80211_ENCR_CCMP_KEY_ABSENT +}; + +enum ndis_80211_priv_filter { + NDIS_80211_PRIV_ACCEPT_ALL, + NDIS_80211_PRIV_8021X_WEP +}; + +enum ndis_80211_status_type { + NDIS_80211_STATUSTYPE_AUTHENTICATION, + NDIS_80211_STATUSTYPE_MEDIASTREAMMODE, + NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST, + NDIS_80211_STATUSTYPE_RADIOSTATE, +}; + +enum ndis_80211_media_stream_mode { + NDIS_80211_MEDIA_STREAM_OFF, + NDIS_80211_MEDIA_STREAM_ON +}; + +enum ndis_80211_radio_status { + NDIS_80211_RADIO_STATUS_ON, + NDIS_80211_RADIO_STATUS_HARDWARE_OFF, + NDIS_80211_RADIO_STATUS_SOFTWARE_OFF, +}; + +enum ndis_80211_addkey_bits { + NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28), + NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29), + NDIS_80211_ADDKEY_PAIRWISE_KEY = cpu_to_le32(1 << 30), + NDIS_80211_ADDKEY_TRANSMIT_KEY = cpu_to_le32(1 << 31) +}; + +enum ndis_80211_addwep_bits { + NDIS_80211_ADDWEP_PERCLIENT_KEY = cpu_to_le32(1 << 30), + NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) +}; + +enum ndis_80211_power_mode { + NDIS_80211_POWER_MODE_CAM, + NDIS_80211_POWER_MODE_MAX_PSP, + NDIS_80211_POWER_MODE_FAST_PSP, +}; + +enum ndis_80211_pmkid_cand_list_flag_bits { + NDIS_80211_PMKID_CAND_PREAUTH = cpu_to_le32(1 << 0) +}; + +struct ndis_80211_auth_request { + __le32 length; + u8 bssid[ETH_ALEN]; + u8 padding[2]; + __le32 flags; +} __packed; + +struct ndis_80211_pmkid_candidate { + u8 bssid[ETH_ALEN]; + u8 padding[2]; + __le32 flags; +} __packed; + +struct ndis_80211_pmkid_cand_list { + __le32 version; + __le32 num_candidates; + struct ndis_80211_pmkid_candidate candidate_list[]; +} __packed; + +struct ndis_80211_status_indication { + __le32 status_type; + union { + __le32 media_stream_mode; + __le32 radio_status; + struct ndis_80211_auth_request auth_request[0]; + struct ndis_80211_pmkid_cand_list cand_list; + } u; +} __packed; + +struct ndis_80211_ssid { + __le32 length; + u8 essid[NDIS_802_11_LENGTH_SSID]; +} __packed; + +struct ndis_80211_conf_freq_hop { + __le32 length; + __le32 hop_pattern; + __le32 hop_set; + __le32 dwell_time; +} __packed; + +struct ndis_80211_conf { + __le32 length; + __le32 beacon_period; + __le32 atim_window; + __le32 ds_config; + struct ndis_80211_conf_freq_hop fh_config; +} __packed; + +struct ndis_80211_bssid_ex { + __le32 length; + u8 mac[ETH_ALEN]; + u8 padding[2]; + struct ndis_80211_ssid ssid; + __le32 privacy; + __le32 rssi; + __le32 net_type; + struct ndis_80211_conf config; + __le32 net_infra; + u8 rates[NDIS_802_11_LENGTH_RATES_EX]; + __le32 ie_length; + u8 ies[]; +} __packed; + +struct ndis_80211_bssid_list_ex { + __le32 num_items; + u8 bssid_data[]; +} __packed; + +struct ndis_80211_fixed_ies { + u8 timestamp[8]; + __le16 beacon_interval; + __le16 capabilities; +} __packed; + +struct ndis_80211_wep_key { + __le32 size; + __le32 index; + __le32 length; + u8 material[32]; +} __packed; + +struct ndis_80211_key { + __le32 size; + __le32 index; + __le32 length; + u8 bssid[ETH_ALEN]; + u8 padding[6]; + u8 rsc[8]; + u8 material[32]; +} __packed; + +struct ndis_80211_remove_key { + __le32 size; + __le32 index; + u8 bssid[ETH_ALEN]; + u8 padding[2]; +} __packed; + +struct ndis_config_param { + __le32 name_offs; + __le32 name_length; + __le32 type; + __le32 value_offs; + __le32 value_length; +} __packed; + +struct ndis_80211_assoc_info { + __le32 length; + __le16 req_ies; + struct req_ie { + __le16 capa; + __le16 listen_interval; + u8 cur_ap_address[ETH_ALEN]; + } req_ie; + __le32 req_ie_length; + __le32 offset_req_ies; + __le16 resp_ies; + struct resp_ie { + __le16 capa; + __le16 status_code; + __le16 assoc_id; + } resp_ie; + __le32 resp_ie_length; + __le32 offset_resp_ies; +} __packed; + +struct ndis_80211_capability { + __le32 length; + __le32 version; + __le32 num_pmkids; + __le32 num_auth_encr_pair; +} __packed; + +struct ndis_80211_bssid_info { + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +} __packed; + +struct ndis_80211_pmkid { + __le32 length; + __le32 bssid_info_count; + struct ndis_80211_bssid_info bssid_info[]; +} __packed; + +/* + * private data + */ +#define CAP_MODE_80211A 1 +#define CAP_MODE_80211B 2 +#define CAP_MODE_80211G 4 +#define CAP_MODE_MASK 7 + +#define WORK_LINK_UP 0 +#define WORK_LINK_DOWN 1 +#define WORK_SET_MULTICAST_LIST 2 + +#define RNDIS_WLAN_ALG_NONE 0 +#define RNDIS_WLAN_ALG_WEP (1<<0) +#define RNDIS_WLAN_ALG_TKIP (1<<1) +#define RNDIS_WLAN_ALG_CCMP (1<<2) + +#define RNDIS_WLAN_NUM_KEYS 4 +#define RNDIS_WLAN_KEY_MGMT_NONE 0 +#define RNDIS_WLAN_KEY_MGMT_802_1X (1<<0) +#define RNDIS_WLAN_KEY_MGMT_PSK (1<<1) + +#define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set)) + +static const struct ieee80211_channel rndis_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; + +static const struct ieee80211_rate rndis_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60 }, + { .bitrate = 90 }, + { .bitrate = 120 }, + { .bitrate = 180 }, + { .bitrate = 240 }, + { .bitrate = 360 }, + { .bitrate = 480 }, + { .bitrate = 540 } +}; + +static const u32 rndis_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, +}; + +struct rndis_wlan_encr_key { + int len; + u32 cipher; + u8 material[32]; + u8 bssid[ETH_ALEN]; + bool pairwise; + bool tx_key; +}; + +/* RNDIS device private data */ +struct rndis_wlan_private { + struct usbnet *usbdev; + + struct wireless_dev wdev; + + struct cfg80211_scan_request *scan_request; + + struct workqueue_struct *workqueue; + struct delayed_work dev_poller_work; + struct delayed_work scan_work; + struct work_struct work; + struct mutex command_lock; + unsigned long work_pending; + int last_qual; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; + int last_cqm_event_rssi; + + struct ieee80211_supported_band band; + struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; + struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)]; + u32 cipher_suites[ARRAY_SIZE(rndis_cipher_suites)]; + + int device_type; + int caps; + int multicast_size; + + /* module parameters */ + char param_country[4]; + int param_frameburst; + int param_afterburner; + int param_power_save; + int param_power_output; + int param_roamtrigger; + int param_roamdelta; + u32 param_workaround_interval; + + /* hardware state */ + bool radio_on; + int power_mode; + int infra_mode; + bool connected; + u8 bssid[ETH_ALEN]; + u32 current_command_oid; + + /* encryption stuff */ + u8 encr_tx_key_index; + struct rndis_wlan_encr_key encr_keys[RNDIS_WLAN_NUM_KEYS]; + int wpa_version; + + u8 command_buffer[COMMAND_BUFFER_SIZE]; +}; + +/* + * cfg80211 ops + */ +static int rndis_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, + struct vif_params *params); + +static int rndis_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request); + +static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); + +static int rndis_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm); +static int rndis_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm); + +static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); + +static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code); + +static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params); + +static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev); + +static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr, struct key_params *params); + +static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr); + +static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool unicast, + bool multicast); + +static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo); + +static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo); + +static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa); + +static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa); + +static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev); + +static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int timeout); + +static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); + +static const struct cfg80211_ops rndis_config_ops = { + .change_virtual_intf = rndis_change_virtual_intf, + .scan = rndis_scan, + .set_wiphy_params = rndis_set_wiphy_params, + .set_tx_power = rndis_set_tx_power, + .get_tx_power = rndis_get_tx_power, + .connect = rndis_connect, + .disconnect = rndis_disconnect, + .join_ibss = rndis_join_ibss, + .leave_ibss = rndis_leave_ibss, + .add_key = rndis_add_key, + .del_key = rndis_del_key, + .set_default_key = rndis_set_default_key, + .get_station = rndis_get_station, + .dump_station = rndis_dump_station, + .set_pmksa = rndis_set_pmksa, + .del_pmksa = rndis_del_pmksa, + .flush_pmksa = rndis_flush_pmksa, + .set_power_mgmt = rndis_set_power_mgmt, + .set_cqm_rssi_config = rndis_set_cqm_rssi_config, +}; + +static void *rndis_wiphy_privid = &rndis_wiphy_privid; + + +static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) +{ + return (struct rndis_wlan_private *)dev->driver_priv; +} + +static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv) +{ + switch (priv->param_power_output) { + default: + case 3: + return BCM4320_DEFAULT_TXPOWER_DBM_100; + case 2: + return BCM4320_DEFAULT_TXPOWER_DBM_75; + case 1: + return BCM4320_DEFAULT_TXPOWER_DBM_50; + case 0: + return BCM4320_DEFAULT_TXPOWER_DBM_25; + } +} + +static bool is_wpa_key(struct rndis_wlan_private *priv, u8 idx) +{ + int cipher = priv->encr_keys[idx].cipher; + + return (cipher == WLAN_CIPHER_SUITE_CCMP || + cipher == WLAN_CIPHER_SUITE_TKIP); +} + +static int rndis_cipher_to_alg(u32 cipher) +{ + switch (cipher) { + default: + return RNDIS_WLAN_ALG_NONE; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return RNDIS_WLAN_ALG_WEP; + case WLAN_CIPHER_SUITE_TKIP: + return RNDIS_WLAN_ALG_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return RNDIS_WLAN_ALG_CCMP; + } +} + +static int rndis_akm_suite_to_key_mgmt(u32 akm_suite) +{ + switch (akm_suite) { + default: + return RNDIS_WLAN_KEY_MGMT_NONE; + case WLAN_AKM_SUITE_8021X: + return RNDIS_WLAN_KEY_MGMT_802_1X; + case WLAN_AKM_SUITE_PSK: + return RNDIS_WLAN_KEY_MGMT_PSK; + } +} + +#ifdef DEBUG +static const char *oid_to_string(u32 oid) +{ + switch (oid) { +#define OID_STR(oid) case oid: return(#oid) + /* from rndis_host.h */ + OID_STR(RNDIS_OID_802_3_PERMANENT_ADDRESS); + OID_STR(RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE); + OID_STR(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); + OID_STR(RNDIS_OID_GEN_PHYSICAL_MEDIUM); + + /* from rndis_wlan.c */ + OID_STR(RNDIS_OID_GEN_LINK_SPEED); + OID_STR(RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER); + + OID_STR(RNDIS_OID_GEN_XMIT_OK); + OID_STR(RNDIS_OID_GEN_RCV_OK); + OID_STR(RNDIS_OID_GEN_XMIT_ERROR); + OID_STR(RNDIS_OID_GEN_RCV_ERROR); + OID_STR(RNDIS_OID_GEN_RCV_NO_BUFFER); + + OID_STR(RNDIS_OID_802_3_CURRENT_ADDRESS); + OID_STR(RNDIS_OID_802_3_MULTICAST_LIST); + OID_STR(RNDIS_OID_802_3_MAXIMUM_LIST_SIZE); + + OID_STR(RNDIS_OID_802_11_BSSID); + OID_STR(RNDIS_OID_802_11_SSID); + OID_STR(RNDIS_OID_802_11_INFRASTRUCTURE_MODE); + OID_STR(RNDIS_OID_802_11_ADD_WEP); + OID_STR(RNDIS_OID_802_11_REMOVE_WEP); + OID_STR(RNDIS_OID_802_11_DISASSOCIATE); + OID_STR(RNDIS_OID_802_11_AUTHENTICATION_MODE); + OID_STR(RNDIS_OID_802_11_PRIVACY_FILTER); + OID_STR(RNDIS_OID_802_11_BSSID_LIST_SCAN); + OID_STR(RNDIS_OID_802_11_ENCRYPTION_STATUS); + OID_STR(RNDIS_OID_802_11_ADD_KEY); + OID_STR(RNDIS_OID_802_11_REMOVE_KEY); + OID_STR(RNDIS_OID_802_11_ASSOCIATION_INFORMATION); + OID_STR(RNDIS_OID_802_11_CAPABILITY); + OID_STR(RNDIS_OID_802_11_PMKID); + OID_STR(RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED); + OID_STR(RNDIS_OID_802_11_NETWORK_TYPE_IN_USE); + OID_STR(RNDIS_OID_802_11_TX_POWER_LEVEL); + OID_STR(RNDIS_OID_802_11_RSSI); + OID_STR(RNDIS_OID_802_11_RSSI_TRIGGER); + OID_STR(RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD); + OID_STR(RNDIS_OID_802_11_RTS_THRESHOLD); + OID_STR(RNDIS_OID_802_11_SUPPORTED_RATES); + OID_STR(RNDIS_OID_802_11_CONFIGURATION); + OID_STR(RNDIS_OID_802_11_POWER_MODE); + OID_STR(RNDIS_OID_802_11_BSSID_LIST); +#undef OID_STR + } + + return "?"; +} +#else +static const char *oid_to_string(u32 oid) +{ + return "?"; +} +#endif + +/* translate error code */ +static int rndis_error_status(__le32 rndis_status) +{ + int ret = -EINVAL; + switch (le32_to_cpu(rndis_status)) { + case RNDIS_STATUS_SUCCESS: + ret = 0; + break; + case RNDIS_STATUS_FAILURE: + case RNDIS_STATUS_INVALID_DATA: + ret = -EINVAL; + break; + case RNDIS_STATUS_NOT_SUPPORTED: + ret = -EOPNOTSUPP; + break; + case RNDIS_STATUS_ADAPTER_NOT_READY: + case RNDIS_STATUS_ADAPTER_NOT_OPEN: + ret = -EBUSY; + break; + } + return ret; +} + +static int rndis_query_oid(struct usbnet *dev, u32 oid, void *data, int *len) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); + union { + void *buf; + struct rndis_msg_hdr *header; + struct rndis_query *get; + struct rndis_query_c *get_c; + } u; + int ret; + size_t buflen, resplen, respoffs, copylen; + + buflen = *len + sizeof(*u.get); + if (buflen < CONTROL_BUFFER_SIZE) + buflen = CONTROL_BUFFER_SIZE; + + if (buflen > COMMAND_BUFFER_SIZE) { + u.buf = kmalloc(buflen, GFP_KERNEL); + if (!u.buf) + return -ENOMEM; + } else { + u.buf = priv->command_buffer; + } + + mutex_lock(&priv->command_lock); + + memset(u.get, 0, sizeof *u.get); + u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY); + u.get->msg_len = cpu_to_le32(sizeof *u.get); + u.get->oid = cpu_to_le32(oid); + + priv->current_command_oid = oid; + ret = rndis_command(dev, u.header, buflen); + priv->current_command_oid = 0; + if (ret < 0) + netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", + __func__, oid_to_string(oid), ret, + le32_to_cpu(u.get_c->status)); + + if (ret == 0) { + resplen = le32_to_cpu(u.get_c->len); + respoffs = le32_to_cpu(u.get_c->offset) + 8; + + if (respoffs > buflen) { + /* Device returned data offset outside buffer, error. */ + netdev_dbg(dev->net, + "%s(%s): received invalid data offset: %zu > %zu\n", + __func__, oid_to_string(oid), respoffs, buflen); + + ret = -EINVAL; + goto exit_unlock; + } + + copylen = min(resplen, buflen - respoffs); + + if (copylen > *len) + copylen = *len; + + memcpy(data, u.buf + respoffs, copylen); + + *len = resplen; + + ret = rndis_error_status(u.get_c->status); + if (ret < 0) + netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", + __func__, oid_to_string(oid), + le32_to_cpu(u.get_c->status), ret); + } + +exit_unlock: + mutex_unlock(&priv->command_lock); + + if (u.buf != priv->command_buffer) + kfree(u.buf); + return ret; +} + +static int rndis_set_oid(struct usbnet *dev, u32 oid, const void *data, + int len) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); + union { + void *buf; + struct rndis_msg_hdr *header; + struct rndis_set *set; + struct rndis_set_c *set_c; + } u; + int ret, buflen; + + buflen = len + sizeof(*u.set); + if (buflen < CONTROL_BUFFER_SIZE) + buflen = CONTROL_BUFFER_SIZE; + + if (buflen > COMMAND_BUFFER_SIZE) { + u.buf = kmalloc(buflen, GFP_KERNEL); + if (!u.buf) + return -ENOMEM; + } else { + u.buf = priv->command_buffer; + } + + mutex_lock(&priv->command_lock); + + memset(u.set, 0, sizeof *u.set); + u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); + u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len); + u.set->oid = cpu_to_le32(oid); + u.set->len = cpu_to_le32(len); + u.set->offset = cpu_to_le32(sizeof(*u.set) - 8); + u.set->handle = cpu_to_le32(0); + memcpy(u.buf + sizeof(*u.set), data, len); + + priv->current_command_oid = oid; + ret = rndis_command(dev, u.header, buflen); + priv->current_command_oid = 0; + if (ret < 0) + netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", + __func__, oid_to_string(oid), ret, + le32_to_cpu(u.set_c->status)); + + if (ret == 0) { + ret = rndis_error_status(u.set_c->status); + + if (ret < 0) + netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", + __func__, oid_to_string(oid), + le32_to_cpu(u.set_c->status), ret); + } + + mutex_unlock(&priv->command_lock); + + if (u.buf != priv->command_buffer) + kfree(u.buf); + return ret; +} + +static int rndis_reset(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct rndis_reset *reset; + int ret; + + mutex_lock(&priv->command_lock); + + reset = (void *)priv->command_buffer; + memset(reset, 0, sizeof(*reset)); + reset->msg_type = cpu_to_le32(RNDIS_MSG_RESET); + reset->msg_len = cpu_to_le32(sizeof(*reset)); + priv->current_command_oid = 0; + ret = rndis_command(usbdev, (void *)reset, CONTROL_BUFFER_SIZE); + + mutex_unlock(&priv->command_lock); + + if (ret < 0) + return ret; + return 0; +} + +/* + * Specs say that we can only set config parameters only soon after device + * initialization. + * value_type: 0 = u32, 2 = unicode string + */ +static int rndis_set_config_parameter(struct usbnet *dev, char *param, + int value_type, void *value) +{ + struct ndis_config_param *infobuf; + int value_len, info_len, param_len, ret, i; + __le16 *unibuf; + __le32 *dst_value; + + if (value_type == 0) + value_len = sizeof(__le32); + else if (value_type == 2) + value_len = strlen(value) * sizeof(__le16); + else + return -EINVAL; + + param_len = strlen(param) * sizeof(__le16); + info_len = sizeof(*infobuf) + param_len + value_len; + +#ifdef DEBUG + info_len += 12; +#endif + infobuf = kmalloc(info_len, GFP_KERNEL); + if (!infobuf) + return -ENOMEM; + +#ifdef DEBUG + info_len -= 12; + /* extra 12 bytes are for padding (debug output) */ + memset(infobuf, 0xCC, info_len + 12); +#endif + + if (value_type == 2) + netdev_dbg(dev->net, "setting config parameter: %s, value: %s\n", + param, (u8 *)value); + else + netdev_dbg(dev->net, "setting config parameter: %s, value: %d\n", + param, *(u32 *)value); + + infobuf->name_offs = cpu_to_le32(sizeof(*infobuf)); + infobuf->name_length = cpu_to_le32(param_len); + infobuf->type = cpu_to_le32(value_type); + infobuf->value_offs = cpu_to_le32(sizeof(*infobuf) + param_len); + infobuf->value_length = cpu_to_le32(value_len); + + /* simple string to unicode string conversion */ + unibuf = (void *)infobuf + sizeof(*infobuf); + for (i = 0; i < param_len / sizeof(__le16); i++) + unibuf[i] = cpu_to_le16(param[i]); + + if (value_type == 2) { + unibuf = (void *)infobuf + sizeof(*infobuf) + param_len; + for (i = 0; i < value_len / sizeof(__le16); i++) + unibuf[i] = cpu_to_le16(((u8 *)value)[i]); + } else { + dst_value = (void *)infobuf + sizeof(*infobuf) + param_len; + *dst_value = cpu_to_le32(*(u32 *)value); + } + +#ifdef DEBUG + netdev_dbg(dev->net, "info buffer (len: %d)\n", info_len); + for (i = 0; i < info_len; i += 12) { + u32 *tmp = (u32 *)((u8 *)infobuf + i); + netdev_dbg(dev->net, "%08X:%08X:%08X\n", + cpu_to_be32(tmp[0]), + cpu_to_be32(tmp[1]), + cpu_to_be32(tmp[2])); + } +#endif + + ret = rndis_set_oid(dev, RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER, + infobuf, info_len); + if (ret != 0) + netdev_dbg(dev->net, "setting rndis config parameter failed, %d\n", + ret); + + kfree(infobuf); + return ret; +} + +static int rndis_set_config_parameter_str(struct usbnet *dev, + char *param, char *value) +{ + return rndis_set_config_parameter(dev, param, 2, value); +} + +/* + * data conversion functions + */ +static int level_to_qual(int level) +{ + int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE); + return qual >= 0 ? (qual <= 100 ? qual : 100) : 0; +} + +/* + * common functions + */ +static int set_infra_mode(struct usbnet *usbdev, int mode); +static void restore_keys(struct usbnet *usbdev); +static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, + bool *matched); + +static int rndis_start_bssid_list_scan(struct usbnet *usbdev) +{ + __le32 tmp; + + /* Note: RNDIS_OID_802_11_BSSID_LIST_SCAN clears internal BSS list. */ + tmp = cpu_to_le32(1); + return rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST_SCAN, &tmp, + sizeof(tmp)); +} + +static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int ret; + + ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_SSID, + ssid, sizeof(*ssid)); + if (ret < 0) { + netdev_warn(usbdev->net, "setting SSID failed (%08X)\n", ret); + return ret; + } + if (ret == 0) { + priv->radio_on = true; + netdev_dbg(usbdev->net, "%s(): radio_on = true\n", __func__); + } + + return ret; +} + +static int set_bssid(struct usbnet *usbdev, const u8 *bssid) +{ + int ret; + + ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID, + bssid, ETH_ALEN); + if (ret < 0) { + netdev_warn(usbdev->net, "setting BSSID[%pM] failed (%08X)\n", + bssid, ret); + return ret; + } + + return ret; +} + +static int clear_bssid(struct usbnet *usbdev) +{ + static const u8 broadcast_mac[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + return set_bssid(usbdev, broadcast_mac); +} + +static int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN]) +{ + int ret, len; + + len = ETH_ALEN; + ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID, + bssid, &len); + + if (ret != 0) + eth_zero_addr(bssid); + + return ret; +} + +static int get_association_info(struct usbnet *usbdev, + struct ndis_80211_assoc_info *info, int len) +{ + return rndis_query_oid(usbdev, + RNDIS_OID_802_11_ASSOCIATION_INFORMATION, + info, &len); +} + +static bool is_associated(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + u8 bssid[ETH_ALEN]; + + if (!priv->radio_on) + return false; + + return (get_bssid(usbdev, bssid) == 0 && !is_zero_ether_addr(bssid)); +} + +static int disassociate(struct usbnet *usbdev, bool reset_ssid) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_ssid ssid; + int i, ret = 0; + + if (priv->radio_on) { + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_DISASSOCIATE, + NULL, 0); + if (ret == 0) { + priv->radio_on = false; + netdev_dbg(usbdev->net, "%s(): radio_on = false\n", + __func__); + + if (reset_ssid) + msleep(100); + } + } + + /* disassociate causes radio to be turned off; if reset_ssid + * is given, set random ssid to enable radio */ + if (reset_ssid) { + /* Set device to infrastructure mode so we don't get ad-hoc + * 'media connect' indications with the random ssid. + */ + set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); + + ssid.length = cpu_to_le32(sizeof(ssid.essid)); + get_random_bytes(&ssid.essid[2], sizeof(ssid.essid)-2); + ssid.essid[0] = 0x1; + ssid.essid[1] = 0xff; + for (i = 2; i < sizeof(ssid.essid); i++) + ssid.essid[i] = 0x1 + (ssid.essid[i] * 0xfe / 0xff); + ret = set_essid(usbdev, &ssid); + } + return ret; +} + +static int set_auth_mode(struct usbnet *usbdev, u32 wpa_version, + enum nl80211_auth_type auth_type, int keymgmt) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + __le32 tmp; + int auth_mode, ret; + + netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x authalg=0x%x keymgmt=0x%x\n", + __func__, wpa_version, auth_type, keymgmt); + + if (wpa_version & NL80211_WPA_VERSION_2) { + if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) + auth_mode = NDIS_80211_AUTH_WPA2; + else + auth_mode = NDIS_80211_AUTH_WPA2_PSK; + } else if (wpa_version & NL80211_WPA_VERSION_1) { + if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) + auth_mode = NDIS_80211_AUTH_WPA; + else if (keymgmt & RNDIS_WLAN_KEY_MGMT_PSK) + auth_mode = NDIS_80211_AUTH_WPA_PSK; + else + auth_mode = NDIS_80211_AUTH_WPA_NONE; + } else if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) + auth_mode = NDIS_80211_AUTH_SHARED; + else if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) + auth_mode = NDIS_80211_AUTH_OPEN; + else if (auth_type == NL80211_AUTHTYPE_AUTOMATIC) + auth_mode = NDIS_80211_AUTH_AUTO_SWITCH; + else + return -ENOTSUPP; + + tmp = cpu_to_le32(auth_mode); + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_AUTHENTICATION_MODE, + &tmp, sizeof(tmp)); + if (ret != 0) { + netdev_warn(usbdev->net, "setting auth mode failed (%08X)\n", + ret); + return ret; + } + + priv->wpa_version = wpa_version; + + return 0; +} + +static int set_priv_filter(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + __le32 tmp; + + netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x\n", + __func__, priv->wpa_version); + + if (priv->wpa_version & NL80211_WPA_VERSION_2 || + priv->wpa_version & NL80211_WPA_VERSION_1) + tmp = cpu_to_le32(NDIS_80211_PRIV_8021X_WEP); + else + tmp = cpu_to_le32(NDIS_80211_PRIV_ACCEPT_ALL); + + return rndis_set_oid(usbdev, + RNDIS_OID_802_11_PRIVACY_FILTER, &tmp, + sizeof(tmp)); +} + +static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) +{ + __le32 tmp; + int encr_mode, ret; + + netdev_dbg(usbdev->net, "%s(): cipher_pair=0x%x cipher_group=0x%x\n", + __func__, pairwise, groupwise); + + if (pairwise & RNDIS_WLAN_ALG_CCMP) + encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; + else if (pairwise & RNDIS_WLAN_ALG_TKIP) + encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; + else if (pairwise & RNDIS_WLAN_ALG_WEP) + encr_mode = NDIS_80211_ENCR_WEP_ENABLED; + else if (groupwise & RNDIS_WLAN_ALG_CCMP) + encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; + else if (groupwise & RNDIS_WLAN_ALG_TKIP) + encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; + else + encr_mode = NDIS_80211_ENCR_DISABLED; + + tmp = cpu_to_le32(encr_mode); + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_ENCRYPTION_STATUS, &tmp, + sizeof(tmp)); + if (ret != 0) { + netdev_warn(usbdev->net, "setting encr mode failed (%08X)\n", + ret); + return ret; + } + + return 0; +} + +static int set_infra_mode(struct usbnet *usbdev, int mode) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + __le32 tmp; + int ret; + + netdev_dbg(usbdev->net, "%s(): infra_mode=0x%x\n", + __func__, priv->infra_mode); + + tmp = cpu_to_le32(mode); + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_INFRASTRUCTURE_MODE, + &tmp, sizeof(tmp)); + if (ret != 0) { + netdev_warn(usbdev->net, "setting infra mode failed (%08X)\n", + ret); + return ret; + } + + /* NDIS drivers clear keys when infrastructure mode is + * changed. But Linux tools assume otherwise. So set the + * keys */ + restore_keys(usbdev); + + priv->infra_mode = mode; + return 0; +} + +static int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold) +{ + __le32 tmp; + + netdev_dbg(usbdev->net, "%s(): %i\n", __func__, rts_threshold); + + if (rts_threshold == -1 || rts_threshold > 2347) + rts_threshold = 2347; + + tmp = cpu_to_le32(rts_threshold); + return rndis_set_oid(usbdev, + RNDIS_OID_802_11_RTS_THRESHOLD, + &tmp, sizeof(tmp)); +} + +static int set_frag_threshold(struct usbnet *usbdev, u32 frag_threshold) +{ + __le32 tmp; + + netdev_dbg(usbdev->net, "%s(): %i\n", __func__, frag_threshold); + + if (frag_threshold < 256 || frag_threshold > 2346) + frag_threshold = 2346; + + tmp = cpu_to_le32(frag_threshold); + return rndis_set_oid(usbdev, + RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD, + &tmp, sizeof(tmp)); +} + +static void set_default_iw_params(struct usbnet *usbdev) +{ + set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); + set_auth_mode(usbdev, 0, NL80211_AUTHTYPE_OPEN_SYSTEM, + RNDIS_WLAN_KEY_MGMT_NONE); + set_priv_filter(usbdev); + set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); +} + +static int deauthenticate(struct usbnet *usbdev) +{ + int ret; + + ret = disassociate(usbdev, true); + set_default_iw_params(usbdev); + return ret; +} + +static int set_channel(struct usbnet *usbdev, int channel) +{ + struct ndis_80211_conf config; + unsigned int dsconfig; + int len, ret; + + netdev_dbg(usbdev->net, "%s(%d)\n", __func__, channel); + + /* this OID is valid only when not associated */ + if (is_associated(usbdev)) + return 0; + + dsconfig = 1000 * + ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ); + + len = sizeof(config); + ret = rndis_query_oid(usbdev, + RNDIS_OID_802_11_CONFIGURATION, + &config, &len); + if (ret < 0) { + netdev_dbg(usbdev->net, "%s(): querying configuration failed\n", + __func__); + return ret; + } + + config.ds_config = cpu_to_le32(dsconfig); + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_CONFIGURATION, + &config, sizeof(config)); + + netdev_dbg(usbdev->net, "%s(): %d -> %d\n", __func__, channel, ret); + + return ret; +} + +static struct ieee80211_channel *get_current_channel(struct usbnet *usbdev, + u32 *beacon_period) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ieee80211_channel *channel; + struct ndis_80211_conf config; + int len, ret; + + /* Get channel and beacon interval */ + len = sizeof(config); + ret = rndis_query_oid(usbdev, + RNDIS_OID_802_11_CONFIGURATION, + &config, &len); + netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_CONFIGURATION -> %d\n", + __func__, ret); + if (ret < 0) + return NULL; + + channel = ieee80211_get_channel(priv->wdev.wiphy, + KHZ_TO_MHZ(le32_to_cpu(config.ds_config))); + if (!channel) + return NULL; + + if (beacon_period) + *beacon_period = le32_to_cpu(config.beacon_period); + return channel; +} + +/* index must be 0 - N, as per NDIS */ +static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len, + u8 index) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_wep_key ndis_key; + u32 cipher; + int ret; + + netdev_dbg(usbdev->net, "%s(idx: %d, len: %d)\n", + __func__, index, key_len); + + if (index >= RNDIS_WLAN_NUM_KEYS) + return -EINVAL; + + if (key_len == 5) + cipher = WLAN_CIPHER_SUITE_WEP40; + else if (key_len == 13) + cipher = WLAN_CIPHER_SUITE_WEP104; + else + return -EINVAL; + + memset(&ndis_key, 0, sizeof(ndis_key)); + + ndis_key.size = cpu_to_le32(sizeof(ndis_key)); + ndis_key.length = cpu_to_le32(key_len); + ndis_key.index = cpu_to_le32(index); + memcpy(&ndis_key.material, key, key_len); + + if (index == priv->encr_tx_key_index) { + ndis_key.index |= NDIS_80211_ADDWEP_TRANSMIT_KEY; + ret = set_encr_mode(usbdev, RNDIS_WLAN_ALG_WEP, + RNDIS_WLAN_ALG_NONE); + if (ret) + netdev_warn(usbdev->net, "encryption couldn't be enabled (%08X)\n", + ret); + } + + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_ADD_WEP, &ndis_key, + sizeof(ndis_key)); + if (ret != 0) { + netdev_warn(usbdev->net, "adding encryption key %d failed (%08X)\n", + index + 1, ret); + return ret; + } + + priv->encr_keys[index].len = key_len; + priv->encr_keys[index].cipher = cipher; + memcpy(&priv->encr_keys[index].material, key, key_len); + eth_broadcast_addr(priv->encr_keys[index].bssid); + + return 0; +} + +static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, + u8 index, const u8 *addr, const u8 *rx_seq, + int seq_len, u32 cipher, __le32 flags) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_key ndis_key; + bool is_addr_ok; + int ret; + + if (index >= RNDIS_WLAN_NUM_KEYS) { + netdev_dbg(usbdev->net, "%s(): index out of range (%i)\n", + __func__, index); + return -EINVAL; + } + if (key_len > sizeof(ndis_key.material) || key_len < 0) { + netdev_dbg(usbdev->net, "%s(): key length out of range (%i)\n", + __func__, key_len); + return -EINVAL; + } + if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) { + if (!rx_seq || seq_len <= 0) { + netdev_dbg(usbdev->net, "%s(): recv seq flag without buffer\n", + __func__); + return -EINVAL; + } + if (rx_seq && seq_len > sizeof(ndis_key.rsc)) { + netdev_dbg(usbdev->net, "%s(): too big recv seq buffer\n", __func__); + return -EINVAL; + } + } + + is_addr_ok = addr && !is_zero_ether_addr(addr) && + !is_broadcast_ether_addr(addr); + if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !is_addr_ok) { + netdev_dbg(usbdev->net, "%s(): pairwise but bssid invalid (%pM)\n", + __func__, addr); + return -EINVAL; + } + + netdev_dbg(usbdev->net, "%s(%i): flags:%i%i%i\n", + __func__, index, + !!(flags & NDIS_80211_ADDKEY_TRANSMIT_KEY), + !!(flags & NDIS_80211_ADDKEY_PAIRWISE_KEY), + !!(flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ)); + + memset(&ndis_key, 0, sizeof(ndis_key)); + + ndis_key.size = cpu_to_le32(sizeof(ndis_key) - + sizeof(ndis_key.material) + key_len); + ndis_key.length = cpu_to_le32(key_len); + ndis_key.index = cpu_to_le32(index) | flags; + + if (cipher == WLAN_CIPHER_SUITE_TKIP && key_len == 32) { + /* wpa_supplicant gives us the Michael MIC RX/TX keys in + * different order than NDIS spec, so swap the order here. */ + memcpy(ndis_key.material, key, 16); + memcpy(ndis_key.material + 16, key + 24, 8); + memcpy(ndis_key.material + 24, key + 16, 8); + } else + memcpy(ndis_key.material, key, key_len); + + if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) + memcpy(ndis_key.rsc, rx_seq, seq_len); + + if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) { + /* pairwise key */ + memcpy(ndis_key.bssid, addr, ETH_ALEN); + } else { + /* group key */ + if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) + eth_broadcast_addr(ndis_key.bssid); + else + get_bssid(usbdev, ndis_key.bssid); + } + + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_ADD_KEY, &ndis_key, + le32_to_cpu(ndis_key.size)); + netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_ADD_KEY -> %08X\n", + __func__, ret); + if (ret != 0) + return ret; + + memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index])); + priv->encr_keys[index].len = key_len; + priv->encr_keys[index].cipher = cipher; + memcpy(&priv->encr_keys[index].material, key, key_len); + if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) + memcpy(&priv->encr_keys[index].bssid, ndis_key.bssid, ETH_ALEN); + else + eth_broadcast_addr(priv->encr_keys[index].bssid); + + if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY) + priv->encr_tx_key_index = index; + + return 0; +} + +static int restore_key(struct usbnet *usbdev, u8 key_idx) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct rndis_wlan_encr_key key; + + if (is_wpa_key(priv, key_idx)) + return 0; + + key = priv->encr_keys[key_idx]; + + netdev_dbg(usbdev->net, "%s(): %i:%i\n", __func__, key_idx, key.len); + + if (key.len == 0) + return 0; + + return add_wep_key(usbdev, key.material, key.len, key_idx); +} + +static void restore_keys(struct usbnet *usbdev) +{ + int i; + + for (i = 0; i < 4; i++) + restore_key(usbdev, i); +} + +static void clear_key(struct rndis_wlan_private *priv, u8 idx) +{ + memset(&priv->encr_keys[idx], 0, sizeof(priv->encr_keys[idx])); +} + +/* remove_key is for both wep and wpa */ +static int remove_key(struct usbnet *usbdev, u8 index, const u8 *bssid) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_remove_key remove_key; + __le32 keyindex; + bool is_wpa; + int ret; + + if (index >= RNDIS_WLAN_NUM_KEYS) + return -ENOENT; + + if (priv->encr_keys[index].len == 0) + return 0; + + is_wpa = is_wpa_key(priv, index); + + netdev_dbg(usbdev->net, "%s(): %i:%s:%i\n", + __func__, index, is_wpa ? "wpa" : "wep", + priv->encr_keys[index].len); + + clear_key(priv, index); + + if (is_wpa) { + remove_key.size = cpu_to_le32(sizeof(remove_key)); + remove_key.index = cpu_to_le32(index); + if (bssid) { + /* pairwise key */ + if (!is_broadcast_ether_addr(bssid)) + remove_key.index |= + NDIS_80211_ADDKEY_PAIRWISE_KEY; + memcpy(remove_key.bssid, bssid, + sizeof(remove_key.bssid)); + } else + memset(remove_key.bssid, 0xff, + sizeof(remove_key.bssid)); + + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_REMOVE_KEY, + &remove_key, sizeof(remove_key)); + if (ret != 0) + return ret; + } else { + keyindex = cpu_to_le32(index); + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_11_REMOVE_WEP, + &keyindex, sizeof(keyindex)); + if (ret != 0) { + netdev_warn(usbdev->net, + "removing encryption key %d failed (%08X)\n", + index, ret); + return ret; + } + } + + /* if it is transmit key, disable encryption */ + if (index == priv->encr_tx_key_index) + set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); + + return 0; +} + +static void set_multicast_list(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct netdev_hw_addr *ha; + __le32 filter, basefilter; + int ret; + char *mc_addrs = NULL; + int mc_count; + + basefilter = filter = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | + RNDIS_PACKET_TYPE_BROADCAST); + + if (usbdev->net->flags & IFF_PROMISC) { + filter |= cpu_to_le32(RNDIS_PACKET_TYPE_PROMISCUOUS | + RNDIS_PACKET_TYPE_ALL_LOCAL); + } else if (usbdev->net->flags & IFF_ALLMULTI) { + filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); + } + + if (filter != basefilter) + goto set_filter; + + /* + * mc_list should be accessed holding the lock, so copy addresses to + * local buffer first. + */ + netif_addr_lock_bh(usbdev->net); + mc_count = netdev_mc_count(usbdev->net); + if (mc_count > priv->multicast_size) { + filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); + } else if (mc_count) { + int i = 0; + + mc_addrs = kmalloc_array(mc_count, ETH_ALEN, GFP_ATOMIC); + if (!mc_addrs) { + netif_addr_unlock_bh(usbdev->net); + return; + } + + netdev_for_each_mc_addr(ha, usbdev->net) + memcpy(mc_addrs + i++ * ETH_ALEN, + ha->addr, ETH_ALEN); + } + netif_addr_unlock_bh(usbdev->net); + + if (filter != basefilter) + goto set_filter; + + if (mc_count) { + ret = rndis_set_oid(usbdev, + RNDIS_OID_802_3_MULTICAST_LIST, + mc_addrs, mc_count * ETH_ALEN); + kfree(mc_addrs); + if (ret == 0) + filter |= cpu_to_le32(RNDIS_PACKET_TYPE_MULTICAST); + else + filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); + + netdev_dbg(usbdev->net, "RNDIS_OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n", + mc_count, priv->multicast_size, ret); + } + +set_filter: + ret = rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, + sizeof(filter)); + if (ret < 0) { + netdev_warn(usbdev->net, "couldn't set packet filter: %08x\n", + le32_to_cpu(filter)); + } + + netdev_dbg(usbdev->net, "RNDIS_OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d\n", + le32_to_cpu(filter), ret); +} + +#ifdef DEBUG +static void debug_print_pmkids(struct usbnet *usbdev, + struct ndis_80211_pmkid *pmkids, + const char *func_str) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int i, len, count, max_pmkids, entry_len; + + max_pmkids = priv->wdev.wiphy->max_num_pmkids; + len = le32_to_cpu(pmkids->length); + count = le32_to_cpu(pmkids->bssid_info_count); + + entry_len = (count > 0) ? (len - sizeof(*pmkids)) / count : -1; + + netdev_dbg(usbdev->net, "%s(): %d PMKIDs (data len: %d, entry len: " + "%d)\n", func_str, count, len, entry_len); + + if (count > max_pmkids) + count = max_pmkids; + + for (i = 0; i < count; i++) { + u32 *tmp = (u32 *)pmkids->bssid_info[i].pmkid; + + netdev_dbg(usbdev->net, "%s(): bssid: %pM, " + "pmkid: %08X:%08X:%08X:%08X\n", + func_str, pmkids->bssid_info[i].bssid, + cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), + cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); + } +} +#else +static void debug_print_pmkids(struct usbnet *usbdev, + struct ndis_80211_pmkid *pmkids, + const char *func_str) +{ + return; +} +#endif + +static struct ndis_80211_pmkid *get_device_pmkids(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_pmkid *pmkids; + int len, ret, max_pmkids; + + max_pmkids = priv->wdev.wiphy->max_num_pmkids; + len = struct_size(pmkids, bssid_info, max_pmkids); + + pmkids = kzalloc(len, GFP_KERNEL); + if (!pmkids) + return ERR_PTR(-ENOMEM); + + pmkids->length = cpu_to_le32(len); + pmkids->bssid_info_count = cpu_to_le32(max_pmkids); + + ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_PMKID, + pmkids, &len); + if (ret < 0) { + netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d)" + " -> %d\n", __func__, len, max_pmkids, ret); + + kfree(pmkids); + return ERR_PTR(ret); + } + + if (le32_to_cpu(pmkids->bssid_info_count) > max_pmkids) + pmkids->bssid_info_count = cpu_to_le32(max_pmkids); + + debug_print_pmkids(usbdev, pmkids, __func__); + + return pmkids; +} + +static int set_device_pmkids(struct usbnet *usbdev, + struct ndis_80211_pmkid *pmkids) +{ + int ret, len, num_pmkids; + + num_pmkids = le32_to_cpu(pmkids->bssid_info_count); + len = struct_size(pmkids, bssid_info, num_pmkids); + pmkids->length = cpu_to_le32(len); + + debug_print_pmkids(usbdev, pmkids, __func__); + + ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, pmkids, + le32_to_cpu(pmkids->length)); + if (ret < 0) { + netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d) -> %d" + "\n", __func__, len, num_pmkids, ret); + } + + kfree(pmkids); + return ret; +} + +static struct ndis_80211_pmkid *remove_pmkid(struct usbnet *usbdev, + struct ndis_80211_pmkid *pmkids, + struct cfg80211_pmksa *pmksa, + int max_pmkids) +{ + int i, err; + unsigned int count; + + count = le32_to_cpu(pmkids->bssid_info_count); + + if (count > max_pmkids) + count = max_pmkids; + + for (i = 0; i < count; i++) + if (ether_addr_equal(pmkids->bssid_info[i].bssid, + pmksa->bssid)) + break; + + /* pmkid not found */ + if (i == count) { + netdev_dbg(usbdev->net, "%s(): bssid not found (%pM)\n", + __func__, pmksa->bssid); + err = -ENOENT; + goto error; + } + + for (; i + 1 < count; i++) + pmkids->bssid_info[i] = pmkids->bssid_info[i + 1]; + + count--; + pmkids->length = cpu_to_le32(struct_size(pmkids, bssid_info, count)); + pmkids->bssid_info_count = cpu_to_le32(count); + + return pmkids; +error: + kfree(pmkids); + return ERR_PTR(err); +} + +static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev, + struct ndis_80211_pmkid *pmkids, + struct cfg80211_pmksa *pmksa, + int max_pmkids) +{ + struct ndis_80211_pmkid *new_pmkids; + int i, err, newlen; + unsigned int count; + + count = le32_to_cpu(pmkids->bssid_info_count); + + if (count > max_pmkids) + count = max_pmkids; + + /* update with new pmkid */ + for (i = 0; i < count; i++) { + if (!ether_addr_equal(pmkids->bssid_info[i].bssid, + pmksa->bssid)) + continue; + + memcpy(pmkids->bssid_info[i].pmkid, pmksa->pmkid, + WLAN_PMKID_LEN); + + return pmkids; + } + + /* out of space, return error */ + if (i == max_pmkids) { + netdev_dbg(usbdev->net, "%s(): out of space\n", __func__); + err = -ENOSPC; + goto error; + } + + /* add new pmkid */ + newlen = struct_size(pmkids, bssid_info, count + 1); + + new_pmkids = krealloc(pmkids, newlen, GFP_KERNEL); + if (!new_pmkids) { + err = -ENOMEM; + goto error; + } + pmkids = new_pmkids; + + pmkids->length = cpu_to_le32(newlen); + pmkids->bssid_info_count = cpu_to_le32(count + 1); + + memcpy(pmkids->bssid_info[count].bssid, pmksa->bssid, ETH_ALEN); + memcpy(pmkids->bssid_info[count].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); + + return pmkids; +error: + kfree(pmkids); + return ERR_PTR(err); +} + +/* + * cfg80211 ops + */ +static int rndis_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, + struct vif_params *params) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + int mode; + + switch (type) { + case NL80211_IFTYPE_ADHOC: + mode = NDIS_80211_INFRA_ADHOC; + break; + case NL80211_IFTYPE_STATION: + mode = NDIS_80211_INFRA_INFRA; + break; + default: + return -EINVAL; + } + + priv->wdev.iftype = type; + + return set_infra_mode(usbdev, mode); +} + +static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + int err; + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + err = set_frag_threshold(usbdev, wiphy->frag_threshold); + if (err < 0) + return err; + } + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + err = set_rts_threshold(usbdev, wiphy->rts_threshold); + if (err < 0) + return err; + } + + return 0; +} + +static int rndis_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + netdev_dbg(usbdev->net, "%s(): type:0x%x mbm:%i\n", + __func__, type, mbm); + + if (mbm < 0 || (mbm % 100)) + return -ENOTSUPP; + + /* Device doesn't support changing txpower after initialization, only + * turn off/on radio. Support 'auto' mode and setting same dBm that is + * currently used. + */ + if (type == NL80211_TX_POWER_AUTOMATIC || + MBM_TO_DBM(mbm) == get_bcm4320_power_dbm(priv)) { + if (!priv->radio_on) + disassociate(usbdev, true); /* turn on radio */ + + return 0; + } + + return -ENOTSUPP; +} + +static int rndis_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + *dbm = get_bcm4320_power_dbm(priv); + + netdev_dbg(usbdev->net, "%s(): dbm:%i\n", __func__, *dbm); + + return 0; +} + +#define SCAN_DELAY_JIFFIES (6 * HZ) +static int rndis_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct net_device *dev = request->wdev->netdev; + struct usbnet *usbdev = netdev_priv(dev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int ret; + int delay = SCAN_DELAY_JIFFIES; + + netdev_dbg(usbdev->net, "cfg80211.scan\n"); + + /* Get current bssid list from device before new scan, as new scan + * clears internal bssid list. + */ + rndis_check_bssid_list(usbdev, NULL, NULL); + + if (priv->scan_request && priv->scan_request != request) + return -EBUSY; + + priv->scan_request = request; + + ret = rndis_start_bssid_list_scan(usbdev); + if (ret == 0) { + if (priv->device_type == RNDIS_BCM4320A) + delay = HZ; + + /* Wait before retrieving scan results from device */ + queue_delayed_work(priv->workqueue, &priv->scan_work, delay); + } + + return ret; +} + +static bool rndis_bss_info_update(struct usbnet *usbdev, + struct ndis_80211_bssid_ex *bssid) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ieee80211_channel *channel; + struct cfg80211_bss *bss; + s32 signal; + u64 timestamp; + u16 capability; + u16 beacon_interval; + struct ndis_80211_fixed_ies *fixed; + int ie_len, bssid_len; + u8 *ie; + + netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM], len: %d\n", + bssid->ssid.essid, bssid->mac, le32_to_cpu(bssid->length)); + + /* parse bssid structure */ + bssid_len = le32_to_cpu(bssid->length); + + if (bssid_len < sizeof(struct ndis_80211_bssid_ex) + + sizeof(struct ndis_80211_fixed_ies)) + return NULL; + + fixed = (struct ndis_80211_fixed_ies *)bssid->ies; + + ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); + ie_len = min(bssid_len - (int)sizeof(*bssid), + (int)le32_to_cpu(bssid->ie_length)); + ie_len -= sizeof(struct ndis_80211_fixed_ies); + if (ie_len < 0) + return NULL; + + /* extract data for cfg80211_inform_bss */ + channel = ieee80211_get_channel(priv->wdev.wiphy, + KHZ_TO_MHZ(le32_to_cpu(bssid->config.ds_config))); + if (!channel) + return NULL; + + signal = level_to_qual(le32_to_cpu(bssid->rssi)); + timestamp = le64_to_cpu(*(__le64 *)fixed->timestamp); + capability = le16_to_cpu(fixed->capabilities); + beacon_interval = le16_to_cpu(fixed->beacon_interval); + + bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid->mac, + timestamp, capability, beacon_interval, + ie, ie_len, signal, GFP_KERNEL); + cfg80211_put_bss(priv->wdev.wiphy, bss); + + return (bss != NULL); +} + +static struct ndis_80211_bssid_ex *next_bssid_list_item( + struct ndis_80211_bssid_ex *bssid, + int *bssid_len, void *buf, int len) +{ + void *buf_end, *bssid_end; + + buf_end = (char *)buf + len; + bssid_end = (char *)bssid + *bssid_len; + + if ((int)(buf_end - bssid_end) < sizeof(bssid->length)) { + *bssid_len = 0; + return NULL; + } else { + bssid = (void *)((char *)bssid + *bssid_len); + *bssid_len = le32_to_cpu(bssid->length); + return bssid; + } +} + +static bool check_bssid_list_item(struct ndis_80211_bssid_ex *bssid, + int bssid_len, void *buf, int len) +{ + void *buf_end, *bssid_end; + + if (!bssid || bssid_len <= 0 || bssid_len > len) + return false; + + buf_end = (char *)buf + len; + bssid_end = (char *)bssid + bssid_len; + + return (int)(buf_end - bssid_end) >= 0 && (int)(bssid_end - buf) >= 0; +} + +static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, + bool *matched) +{ + void *buf = NULL; + struct ndis_80211_bssid_list_ex *bssid_list; + struct ndis_80211_bssid_ex *bssid; + int ret = -EINVAL, len, count, bssid_len, real_count, new_len; + + netdev_dbg(usbdev->net, "%s()\n", __func__); + + len = CONTROL_BUFFER_SIZE; +resize_buf: + buf = kzalloc(len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* BSSID-list might have got bigger last time we checked, keep + * resizing until it won't get any bigger. + */ + new_len = len; + ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST, + buf, &new_len); + if (ret != 0 || new_len < sizeof(struct ndis_80211_bssid_list_ex)) + goto out; + + if (new_len > len) { + len = new_len; + kfree(buf); + goto resize_buf; + } + + len = new_len; + + bssid_list = buf; + count = le32_to_cpu(bssid_list->num_items); + real_count = 0; + netdev_dbg(usbdev->net, "%s(): buflen: %d\n", __func__, len); + + bssid_len = 0; + bssid = next_bssid_list_item((void *)bssid_list->bssid_data, + &bssid_len, buf, len); + + /* Device returns incorrect 'num_items'. Workaround by ignoring the + * received 'num_items' and walking through full bssid buffer instead. + */ + while (check_bssid_list_item(bssid, bssid_len, buf, len)) { + if (rndis_bss_info_update(usbdev, bssid) && match_bssid && + matched) { + if (ether_addr_equal(bssid->mac, match_bssid)) + *matched = true; + } + + real_count++; + bssid = next_bssid_list_item(bssid, &bssid_len, buf, len); + } + + netdev_dbg(usbdev->net, "%s(): num_items from device: %d, really found:" + " %d\n", __func__, count, real_count); + +out: + kfree(buf); + return ret; +} + +static void rndis_get_scan_results(struct work_struct *work) +{ + struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, scan_work.work); + struct usbnet *usbdev = priv->usbdev; + struct cfg80211_scan_info info = {}; + int ret; + + netdev_dbg(usbdev->net, "get_scan_results\n"); + + if (!priv->scan_request) + return; + + ret = rndis_check_bssid_list(usbdev, NULL, NULL); + + info.aborted = ret < 0; + cfg80211_scan_done(priv->scan_request, &info); + + priv->scan_request = NULL; +} + +static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct ieee80211_channel *channel = sme->channel; + struct ndis_80211_ssid ssid; + int pairwise = RNDIS_WLAN_ALG_NONE; + int groupwise = RNDIS_WLAN_ALG_NONE; + int keymgmt = RNDIS_WLAN_KEY_MGMT_NONE; + int length, i, ret, chan = -1; + + if (channel) + chan = ieee80211_frequency_to_channel(channel->center_freq); + + groupwise = rndis_cipher_to_alg(sme->crypto.cipher_group); + for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) + pairwise |= + rndis_cipher_to_alg(sme->crypto.ciphers_pairwise[i]); + + if (sme->crypto.n_ciphers_pairwise > 0 && + pairwise == RNDIS_WLAN_ALG_NONE) { + netdev_err(usbdev->net, "Unsupported pairwise cipher\n"); + return -ENOTSUPP; + } + + for (i = 0; i < sme->crypto.n_akm_suites; i++) + keymgmt |= + rndis_akm_suite_to_key_mgmt(sme->crypto.akm_suites[i]); + + if (sme->crypto.n_akm_suites > 0 && + keymgmt == RNDIS_WLAN_KEY_MGMT_NONE) { + netdev_err(usbdev->net, "Invalid keymgmt\n"); + return -ENOTSUPP; + } + + netdev_dbg(usbdev->net, "cfg80211.connect('%.32s':[%pM]:%d:[%d,0x%x:0x%x]:[0x%x:0x%x]:0x%x)\n", + sme->ssid, sme->bssid, chan, + sme->privacy, sme->crypto.wpa_versions, sme->auth_type, + groupwise, pairwise, keymgmt); + + if (is_associated(usbdev)) + disassociate(usbdev, false); + + ret = set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); + if (ret < 0) { + netdev_dbg(usbdev->net, "connect: set_infra_mode failed, %d\n", + ret); + goto err_turn_radio_on; + } + + ret = set_auth_mode(usbdev, sme->crypto.wpa_versions, sme->auth_type, + keymgmt); + if (ret < 0) { + netdev_dbg(usbdev->net, "connect: set_auth_mode failed, %d\n", + ret); + goto err_turn_radio_on; + } + + set_priv_filter(usbdev); + + ret = set_encr_mode(usbdev, pairwise, groupwise); + if (ret < 0) { + netdev_dbg(usbdev->net, "connect: set_encr_mode failed, %d\n", + ret); + goto err_turn_radio_on; + } + + if (channel) { + ret = set_channel(usbdev, chan); + if (ret < 0) { + netdev_dbg(usbdev->net, "connect: set_channel failed, %d\n", + ret); + goto err_turn_radio_on; + } + } + + if (sme->key && ((groupwise | pairwise) & RNDIS_WLAN_ALG_WEP)) { + priv->encr_tx_key_index = sme->key_idx; + ret = add_wep_key(usbdev, sme->key, sme->key_len, sme->key_idx); + if (ret < 0) { + netdev_dbg(usbdev->net, "connect: add_wep_key failed, %d (%d, %d)\n", + ret, sme->key_len, sme->key_idx); + goto err_turn_radio_on; + } + } + + if (sme->bssid && !is_zero_ether_addr(sme->bssid) && + !is_broadcast_ether_addr(sme->bssid)) { + ret = set_bssid(usbdev, sme->bssid); + if (ret < 0) { + netdev_dbg(usbdev->net, "connect: set_bssid failed, %d\n", + ret); + goto err_turn_radio_on; + } + } else + clear_bssid(usbdev); + + length = sme->ssid_len; + if (length > NDIS_802_11_LENGTH_SSID) + length = NDIS_802_11_LENGTH_SSID; + + memset(&ssid, 0, sizeof(ssid)); + ssid.length = cpu_to_le32(length); + memcpy(ssid.essid, sme->ssid, length); + + /* Pause and purge rx queue, so we don't pass packets before + * 'media connect'-indication. + */ + usbnet_pause_rx(usbdev); + usbnet_purge_paused_rxq(usbdev); + + ret = set_essid(usbdev, &ssid); + if (ret < 0) + netdev_dbg(usbdev->net, "connect: set_essid failed, %d\n", ret); + return ret; + +err_turn_radio_on: + disassociate(usbdev, true); + + return ret; +} + +static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + netdev_dbg(usbdev->net, "cfg80211.disconnect(%d)\n", reason_code); + + priv->connected = false; + eth_zero_addr(priv->bssid); + + return deauthenticate(usbdev); +} + +static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct ieee80211_channel *channel = params->chandef.chan; + struct ndis_80211_ssid ssid; + enum nl80211_auth_type auth_type; + int ret, alg, length, chan = -1; + + if (channel) + chan = ieee80211_frequency_to_channel(channel->center_freq); + + /* TODO: How to handle ad-hoc encryption? + * connect() has *key, join_ibss() doesn't. RNDIS requires key to be + * pre-shared for encryption (open/shared/wpa), is key set before + * join_ibss? Which auth_type to use (not in params)? What about WPA? + */ + if (params->privacy) { + auth_type = NL80211_AUTHTYPE_SHARED_KEY; + alg = RNDIS_WLAN_ALG_WEP; + } else { + auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + alg = RNDIS_WLAN_ALG_NONE; + } + + netdev_dbg(usbdev->net, "cfg80211.join_ibss('%.32s':[%pM]:%d:%d)\n", + params->ssid, params->bssid, chan, params->privacy); + + if (is_associated(usbdev)) + disassociate(usbdev, false); + + ret = set_infra_mode(usbdev, NDIS_80211_INFRA_ADHOC); + if (ret < 0) { + netdev_dbg(usbdev->net, "join_ibss: set_infra_mode failed, %d\n", + ret); + goto err_turn_radio_on; + } + + ret = set_auth_mode(usbdev, 0, auth_type, RNDIS_WLAN_KEY_MGMT_NONE); + if (ret < 0) { + netdev_dbg(usbdev->net, "join_ibss: set_auth_mode failed, %d\n", + ret); + goto err_turn_radio_on; + } + + set_priv_filter(usbdev); + + ret = set_encr_mode(usbdev, alg, RNDIS_WLAN_ALG_NONE); + if (ret < 0) { + netdev_dbg(usbdev->net, "join_ibss: set_encr_mode failed, %d\n", + ret); + goto err_turn_radio_on; + } + + if (channel) { + ret = set_channel(usbdev, chan); + if (ret < 0) { + netdev_dbg(usbdev->net, "join_ibss: set_channel failed, %d\n", + ret); + goto err_turn_radio_on; + } + } + + if (params->bssid && !is_zero_ether_addr(params->bssid) && + !is_broadcast_ether_addr(params->bssid)) { + ret = set_bssid(usbdev, params->bssid); + if (ret < 0) { + netdev_dbg(usbdev->net, "join_ibss: set_bssid failed, %d\n", + ret); + goto err_turn_radio_on; + } + } else + clear_bssid(usbdev); + + length = params->ssid_len; + if (length > NDIS_802_11_LENGTH_SSID) + length = NDIS_802_11_LENGTH_SSID; + + memset(&ssid, 0, sizeof(ssid)); + ssid.length = cpu_to_le32(length); + memcpy(ssid.essid, params->ssid, length); + + /* Don't need to pause rx queue for ad-hoc. */ + usbnet_purge_paused_rxq(usbdev); + usbnet_resume_rx(usbdev); + + ret = set_essid(usbdev, &ssid); + if (ret < 0) + netdev_dbg(usbdev->net, "join_ibss: set_essid failed, %d\n", + ret); + return ret; + +err_turn_radio_on: + disassociate(usbdev, true); + + return ret; +} + +static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + netdev_dbg(usbdev->net, "cfg80211.leave_ibss()\n"); + + priv->connected = false; + eth_zero_addr(priv->bssid); + + return deauthenticate(usbdev); +} + +static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr, struct key_params *params) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + __le32 flags; + + netdev_dbg(usbdev->net, "%s(%i, %pM, %08x)\n", + __func__, key_index, mac_addr, params->cipher); + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return add_wep_key(usbdev, params->key, params->key_len, + key_index); + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + flags = 0; + + if (params->seq && params->seq_len > 0) + flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ; + if (mac_addr) + flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY | + NDIS_80211_ADDKEY_TRANSMIT_KEY; + + return add_wpa_key(usbdev, params->key, params->key_len, + key_index, mac_addr, params->seq, + params->seq_len, params->cipher, flags); + default: + netdev_dbg(usbdev->net, "%s(): unsupported cipher %08x\n", + __func__, params->cipher); + return -ENOTSUPP; + } +} + +static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + netdev_dbg(usbdev->net, "%s(%i, %pM)\n", __func__, key_index, mac_addr); + + return remove_key(usbdev, key_index, mac_addr); +} + +static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool unicast, + bool multicast) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct rndis_wlan_encr_key key; + + netdev_dbg(usbdev->net, "%s(%i)\n", __func__, key_index); + + if (key_index >= RNDIS_WLAN_NUM_KEYS) + return -ENOENT; + + priv->encr_tx_key_index = key_index; + + if (is_wpa_key(priv, key_index)) + return 0; + + key = priv->encr_keys[key_index]; + + return add_wep_key(usbdev, key.material, key.len, key_index); +} + +static void rndis_fill_station_info(struct usbnet *usbdev, + struct station_info *sinfo) +{ + __le32 linkspeed, rssi; + int ret, len; + + memset(sinfo, 0, sizeof(*sinfo)); + + len = sizeof(linkspeed); + ret = rndis_query_oid(usbdev, RNDIS_OID_GEN_LINK_SPEED, &linkspeed, &len); + if (ret == 0) { + sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + } + + len = sizeof(rssi); + ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, + &rssi, &len); + if (ret == 0) { + sinfo->signal = level_to_qual(le32_to_cpu(rssi)); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + } +} + +static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + if (!ether_addr_equal(priv->bssid, mac)) + return -ENOENT; + + rndis_fill_station_info(usbdev, sinfo); + + return 0; +} + +static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + if (idx != 0) + return -ENOENT; + + memcpy(mac, priv->bssid, ETH_ALEN); + + rndis_fill_station_info(usbdev, sinfo); + + return 0; +} + +static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct ndis_80211_pmkid *pmkids; + u32 *tmp = (u32 *)pmksa->pmkid; + + netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, + pmksa->bssid, + cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), + cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); + + pmkids = get_device_pmkids(usbdev); + if (IS_ERR(pmkids)) { + /* couldn't read PMKID cache from device */ + return PTR_ERR(pmkids); + } + + pmkids = update_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); + if (IS_ERR(pmkids)) { + /* not found, list full, etc */ + return PTR_ERR(pmkids); + } + + return set_device_pmkids(usbdev, pmkids); +} + +static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_pmksa *pmksa) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct ndis_80211_pmkid *pmkids; + u32 *tmp = (u32 *)pmksa->pmkid; + + netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, + pmksa->bssid, + cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), + cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); + + pmkids = get_device_pmkids(usbdev); + if (IS_ERR(pmkids)) { + /* Couldn't read PMKID cache from device */ + return PTR_ERR(pmkids); + } + + pmkids = remove_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); + if (IS_ERR(pmkids)) { + /* not found, etc */ + return PTR_ERR(pmkids); + } + + return set_device_pmkids(usbdev, pmkids); +} + +static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct ndis_80211_pmkid pmkid; + + netdev_dbg(usbdev->net, "%s()\n", __func__); + + memset(&pmkid, 0, sizeof(pmkid)); + + pmkid.length = cpu_to_le32(sizeof(pmkid)); + pmkid.bssid_info_count = cpu_to_le32(0); + + return rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, + &pmkid, sizeof(pmkid)); +} + +static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int timeout) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + int power_mode; + __le32 mode; + int ret; + + if (priv->device_type != RNDIS_BCM4320B) + return -ENOTSUPP; + + netdev_dbg(usbdev->net, "%s(): %s, %d\n", __func__, + enabled ? "enabled" : "disabled", + timeout); + + if (enabled) + power_mode = NDIS_80211_POWER_MODE_FAST_PSP; + else + power_mode = NDIS_80211_POWER_MODE_CAM; + + if (power_mode == priv->power_mode) + return 0; + + priv->power_mode = power_mode; + + mode = cpu_to_le32(power_mode); + ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_POWER_MODE, + &mode, sizeof(mode)); + + netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_POWER_MODE -> %d\n", + __func__, ret); + + return ret; +} + +static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + priv->last_cqm_event_rssi = 0; + + return 0; +} + +static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, + struct ndis_80211_assoc_info *info) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ieee80211_channel *channel; + struct ndis_80211_ssid ssid; + struct cfg80211_bss *bss; + s32 signal; + u64 timestamp; + u16 capability; + u32 beacon_period = 0; + __le32 rssi; + u8 ie_buf[34]; + int len, ret, ie_len; + + /* Get signal quality, in case of error use rssi=0 and ignore error. */ + len = sizeof(rssi); + rssi = 0; + ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, + &rssi, &len); + signal = level_to_qual(le32_to_cpu(rssi)); + + netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_RSSI -> %d, " + "rssi:%d, qual: %d\n", __func__, ret, le32_to_cpu(rssi), + level_to_qual(le32_to_cpu(rssi))); + + /* Get AP capabilities */ + if (info) { + capability = le16_to_cpu(info->resp_ie.capa); + } else { + /* Set atleast ESS/IBSS capability */ + capability = (priv->infra_mode == NDIS_80211_INFRA_INFRA) ? + WLAN_CAPABILITY_ESS : WLAN_CAPABILITY_IBSS; + } + + /* Get channel and beacon interval */ + channel = get_current_channel(usbdev, &beacon_period); + if (!channel) { + netdev_warn(usbdev->net, "%s(): could not get channel.\n", + __func__); + return; + } + + /* Get SSID, in case of error, use zero length SSID and ignore error. */ + len = sizeof(ssid); + memset(&ssid, 0, sizeof(ssid)); + ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_SSID, + &ssid, &len); + netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_SSID -> %d, len: %d, ssid: " + "'%.32s'\n", __func__, ret, + le32_to_cpu(ssid.length), ssid.essid); + + if (le32_to_cpu(ssid.length) > 32) + ssid.length = cpu_to_le32(32); + + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = le32_to_cpu(ssid.length); + memcpy(&ie_buf[2], ssid.essid, le32_to_cpu(ssid.length)); + + ie_len = le32_to_cpu(ssid.length) + 2; + + /* no tsf */ + timestamp = 0; + + netdev_dbg(usbdev->net, "%s(): channel:%d(freq), bssid:[%pM], tsf:%d, " + "capa:%x, beacon int:%d, resp_ie(len:%d, essid:'%.32s'), " + "signal:%d\n", __func__, (channel ? channel->center_freq : -1), + bssid, (u32)timestamp, capability, beacon_period, ie_len, + ssid.essid, signal); + + bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, + timestamp, capability, beacon_period, + ie_buf, ie_len, signal, GFP_KERNEL); + cfg80211_put_bss(priv->wdev.wiphy, bss); +} + +/* + * workers, indication handlers, device poller + */ +static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_assoc_info *info = NULL; + u8 bssid[ETH_ALEN]; + unsigned int resp_ie_len, req_ie_len; + unsigned int offset; + u8 *req_ie, *resp_ie; + int ret; + bool roamed = false; + bool match_bss; + + if (priv->infra_mode == NDIS_80211_INFRA_INFRA && priv->connected) { + /* received media connect indication while connected, either + * device reassociated with same AP or roamed to new. */ + roamed = true; + } + + req_ie_len = 0; + resp_ie_len = 0; + req_ie = NULL; + resp_ie = NULL; + + if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { + info = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); + if (!info) { + /* No memory? Try resume work later */ + set_bit(WORK_LINK_UP, &priv->work_pending); + queue_work(priv->workqueue, &priv->work); + return; + } + + /* Get association info IEs from device. */ + ret = get_association_info(usbdev, info, CONTROL_BUFFER_SIZE); + if (!ret) { + req_ie_len = le32_to_cpu(info->req_ie_length); + if (req_ie_len > CONTROL_BUFFER_SIZE) + req_ie_len = CONTROL_BUFFER_SIZE; + if (req_ie_len != 0) { + offset = le32_to_cpu(info->offset_req_ies); + + if (offset > CONTROL_BUFFER_SIZE) + offset = CONTROL_BUFFER_SIZE; + + req_ie = (u8 *)info + offset; + + if (offset + req_ie_len > CONTROL_BUFFER_SIZE) + req_ie_len = + CONTROL_BUFFER_SIZE - offset; + } + + resp_ie_len = le32_to_cpu(info->resp_ie_length); + if (resp_ie_len > CONTROL_BUFFER_SIZE) + resp_ie_len = CONTROL_BUFFER_SIZE; + if (resp_ie_len != 0) { + offset = le32_to_cpu(info->offset_resp_ies); + + if (offset > CONTROL_BUFFER_SIZE) + offset = CONTROL_BUFFER_SIZE; + + resp_ie = (u8 *)info + offset; + + if (offset + resp_ie_len > CONTROL_BUFFER_SIZE) + resp_ie_len = + CONTROL_BUFFER_SIZE - offset; + } + } else { + /* Since rndis_wlan_craft_connected_bss() might use info + * later and expects info to contain valid data if + * non-null, free info and set NULL here. + */ + kfree(info); + info = NULL; + } + } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC)) + return; + + ret = get_bssid(usbdev, bssid); + if (ret < 0) + memset(bssid, 0, sizeof(bssid)); + + netdev_dbg(usbdev->net, "link up work: [%pM]%s\n", + bssid, roamed ? " roamed" : ""); + + /* Internal bss list in device should contain at least the currently + * connected bss and we can get it to cfg80211 with + * rndis_check_bssid_list(). + * + * NDIS spec says: "If the device is associated, but the associated + * BSSID is not in its BSSID scan list, then the driver must add an + * entry for the BSSID at the end of the data that it returns in + * response to query of RNDIS_OID_802_11_BSSID_LIST." + * + * NOTE: Seems to be true for BCM4320b variant, but not BCM4320a. + */ + match_bss = false; + rndis_check_bssid_list(usbdev, bssid, &match_bss); + + if (!is_zero_ether_addr(bssid) && !match_bss) { + /* Couldn't get bss from device, we need to manually craft bss + * for cfg80211. + */ + rndis_wlan_craft_connected_bss(usbdev, bssid, info); + } + + if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { + if (!roamed) { + cfg80211_connect_result(usbdev->net, bssid, req_ie, + req_ie_len, resp_ie, + resp_ie_len, 0, GFP_KERNEL); + } else { + struct cfg80211_roam_info roam_info = { + .links[0].channel = + get_current_channel(usbdev, NULL), + .links[0].bssid = bssid, + .req_ie = req_ie, + .req_ie_len = req_ie_len, + .resp_ie = resp_ie, + .resp_ie_len = resp_ie_len, + }; + + cfg80211_roamed(usbdev->net, &roam_info, GFP_KERNEL); + } + } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) + cfg80211_ibss_joined(usbdev->net, bssid, + get_current_channel(usbdev, NULL), + GFP_KERNEL); + + kfree(info); + + priv->connected = true; + memcpy(priv->bssid, bssid, ETH_ALEN); + + usbnet_resume_rx(usbdev); + netif_carrier_on(usbdev->net); +} + +static void rndis_wlan_do_link_down_work(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + + if (priv->connected) { + priv->connected = false; + eth_zero_addr(priv->bssid); + + deauthenticate(usbdev); + + cfg80211_disconnected(usbdev->net, 0, NULL, 0, true, GFP_KERNEL); + } + + netif_carrier_off(usbdev->net); +} + +static void rndis_wlan_worker(struct work_struct *work) +{ + struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, work); + struct usbnet *usbdev = priv->usbdev; + + if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) + rndis_wlan_do_link_up_work(usbdev); + + if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) + rndis_wlan_do_link_down_work(usbdev); + + if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) + set_multicast_list(usbdev); +} + +static void rndis_wlan_set_multicast_list(struct net_device *dev) +{ + struct usbnet *usbdev = netdev_priv(dev); + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + + if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) + return; + + set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending); + queue_work(priv->workqueue, &priv->work); +} + +static void rndis_wlan_auth_indication(struct usbnet *usbdev, + struct ndis_80211_status_indication *indication, + int len) +{ + u8 *buf; + const char *type; + int flags, buflen, key_id; + bool pairwise_error, group_error; + struct ndis_80211_auth_request *auth_req; + enum nl80211_key_type key_type; + + /* must have at least one array entry */ + if (len < offsetof(struct ndis_80211_status_indication, u) + + sizeof(struct ndis_80211_auth_request)) { + netdev_info(usbdev->net, "authentication indication: too short message (%i)\n", + len); + return; + } + + buf = (void *)&indication->u.auth_request[0]; + buflen = len - offsetof(struct ndis_80211_status_indication, u); + + while (buflen >= sizeof(*auth_req)) { + auth_req = (void *)buf; + if (buflen < le32_to_cpu(auth_req->length)) + return; + type = "unknown"; + flags = le32_to_cpu(auth_req->flags); + pairwise_error = false; + group_error = false; + + if (flags & 0x1) + type = "reauth request"; + if (flags & 0x2) + type = "key update request"; + if (flags & 0x6) { + pairwise_error = true; + type = "pairwise_error"; + } + if (flags & 0xe) { + group_error = true; + type = "group_error"; + } + + netdev_info(usbdev->net, "authentication indication: %s (0x%08x)\n", + type, le32_to_cpu(auth_req->flags)); + + if (pairwise_error) { + key_type = NL80211_KEYTYPE_PAIRWISE; + key_id = -1; + + cfg80211_michael_mic_failure(usbdev->net, + auth_req->bssid, + key_type, key_id, NULL, + GFP_KERNEL); + } + + if (group_error) { + key_type = NL80211_KEYTYPE_GROUP; + key_id = -1; + + cfg80211_michael_mic_failure(usbdev->net, + auth_req->bssid, + key_type, key_id, NULL, + GFP_KERNEL); + } + + buflen -= le32_to_cpu(auth_req->length); + buf += le32_to_cpu(auth_req->length); + } +} + +static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev, + struct ndis_80211_status_indication *indication, + int len) +{ + struct ndis_80211_pmkid_cand_list *cand_list; + int list_len, expected_len, i; + + if (len < offsetof(struct ndis_80211_status_indication, u) + + sizeof(struct ndis_80211_pmkid_cand_list)) { + netdev_info(usbdev->net, "pmkid candidate list indication: too short message (%i)\n", + len); + return; + } + + list_len = le32_to_cpu(indication->u.cand_list.num_candidates) * + sizeof(struct ndis_80211_pmkid_candidate); + expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len + + offsetof(struct ndis_80211_status_indication, u); + + if (len < expected_len) { + netdev_info(usbdev->net, "pmkid candidate list indication: list larger than buffer (%i < %i)\n", + len, expected_len); + return; + } + + cand_list = &indication->u.cand_list; + + netdev_info(usbdev->net, "pmkid candidate list indication: version %i, candidates %i\n", + le32_to_cpu(cand_list->version), + le32_to_cpu(cand_list->num_candidates)); + + if (le32_to_cpu(cand_list->version) != 1) + return; + + for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) { + struct ndis_80211_pmkid_candidate *cand = + &cand_list->candidate_list[i]; + bool preauth = !!(cand->flags & NDIS_80211_PMKID_CAND_PREAUTH); + + netdev_dbg(usbdev->net, "cand[%i]: flags: 0x%08x, preauth: %d, bssid: %pM\n", + i, le32_to_cpu(cand->flags), preauth, cand->bssid); + + cfg80211_pmksa_candidate_notify(usbdev->net, i, cand->bssid, + preauth, GFP_ATOMIC); + } +} + +static void rndis_wlan_media_specific_indication(struct usbnet *usbdev, + struct rndis_indicate *msg, int buflen) +{ + struct ndis_80211_status_indication *indication; + unsigned int len, offset; + + offset = offsetof(struct rndis_indicate, status) + + le32_to_cpu(msg->offset); + len = le32_to_cpu(msg->length); + + if (len < 8) { + netdev_info(usbdev->net, "media specific indication, ignore too short message (%i < 8)\n", + len); + return; + } + + if (len > buflen || offset > buflen || offset + len > buflen) { + netdev_info(usbdev->net, "media specific indication, too large to fit to buffer (%i > %i)\n", + offset + len, buflen); + return; + } + + indication = (void *)((u8 *)msg + offset); + + switch (le32_to_cpu(indication->status_type)) { + case NDIS_80211_STATUSTYPE_RADIOSTATE: + netdev_info(usbdev->net, "radio state indication: %i\n", + le32_to_cpu(indication->u.radio_status)); + return; + + case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE: + netdev_info(usbdev->net, "media stream mode indication: %i\n", + le32_to_cpu(indication->u.media_stream_mode)); + return; + + case NDIS_80211_STATUSTYPE_AUTHENTICATION: + rndis_wlan_auth_indication(usbdev, indication, len); + return; + + case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST: + rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len); + return; + + default: + netdev_info(usbdev->net, "media specific indication: unknown status type 0x%08x\n", + le32_to_cpu(indication->status_type)); + } +} + +static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct rndis_indicate *msg = ind; + + switch (le32_to_cpu(msg->status)) { + case RNDIS_STATUS_MEDIA_CONNECT: + if (priv->current_command_oid == RNDIS_OID_802_11_ADD_KEY) { + /* RNDIS_OID_802_11_ADD_KEY causes sometimes extra + * "media connect" indications which confuses driver + * and userspace to think that device is + * roaming/reassociating when it isn't. + */ + netdev_dbg(usbdev->net, "ignored RNDIS_OID_802_11_ADD_KEY triggered 'media connect'\n"); + return; + } + + usbnet_pause_rx(usbdev); + + netdev_info(usbdev->net, "media connect\n"); + + /* queue work to avoid recursive calls into rndis_command */ + set_bit(WORK_LINK_UP, &priv->work_pending); + queue_work(priv->workqueue, &priv->work); + break; + + case RNDIS_STATUS_MEDIA_DISCONNECT: + netdev_info(usbdev->net, "media disconnect\n"); + + /* queue work to avoid recursive calls into rndis_command */ + set_bit(WORK_LINK_DOWN, &priv->work_pending); + queue_work(priv->workqueue, &priv->work); + break; + + case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION: + rndis_wlan_media_specific_indication(usbdev, msg, buflen); + break; + + default: + netdev_info(usbdev->net, "indication: 0x%08x\n", + le32_to_cpu(msg->status)); + break; + } +} + +static int rndis_wlan_get_caps(struct usbnet *usbdev, struct wiphy *wiphy) +{ + struct { + __le32 num_items; + __le32 items[8]; + } networks_supported; + struct ndis_80211_capability caps; + int len, retval, i, n; + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + + /* determine supported modes */ + len = sizeof(networks_supported); + retval = rndis_query_oid(usbdev, + RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED, + &networks_supported, &len); + if (!retval) { + n = le32_to_cpu(networks_supported.num_items); + if (n > 8) + n = 8; + for (i = 0; i < n; i++) { + switch (le32_to_cpu(networks_supported.items[i])) { + case NDIS_80211_TYPE_FREQ_HOP: + case NDIS_80211_TYPE_DIRECT_SEQ: + priv->caps |= CAP_MODE_80211B; + break; + case NDIS_80211_TYPE_OFDM_A: + priv->caps |= CAP_MODE_80211A; + break; + case NDIS_80211_TYPE_OFDM_G: + priv->caps |= CAP_MODE_80211G; + break; + } + } + } + + /* get device 802.11 capabilities, number of PMKIDs */ + len = sizeof(caps); + retval = rndis_query_oid(usbdev, + RNDIS_OID_802_11_CAPABILITY, + &caps, &len); + if (!retval) { + netdev_dbg(usbdev->net, "RNDIS_OID_802_11_CAPABILITY -> len %d, " + "ver %d, pmkids %d, auth-encr-pairs %d\n", + le32_to_cpu(caps.length), + le32_to_cpu(caps.version), + le32_to_cpu(caps.num_pmkids), + le32_to_cpu(caps.num_auth_encr_pair)); + wiphy->max_num_pmkids = le32_to_cpu(caps.num_pmkids); + } else + wiphy->max_num_pmkids = 0; + + return retval; +} + +static void rndis_do_cqm(struct usbnet *usbdev, s32 rssi) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + enum nl80211_cqm_rssi_threshold_event event; + int thold, hyst, last_event; + + if (priv->cqm_rssi_thold >= 0 || rssi >= 0) + return; + if (priv->infra_mode != NDIS_80211_INFRA_INFRA) + return; + + last_event = priv->last_cqm_event_rssi; + thold = priv->cqm_rssi_thold; + hyst = priv->cqm_rssi_hyst; + + if (rssi < thold && (last_event == 0 || rssi < last_event - hyst)) + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + else if (rssi > thold && (last_event == 0 || rssi > last_event + hyst)) + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + else + return; + + priv->last_cqm_event_rssi = rssi; + cfg80211_cqm_rssi_notify(usbdev->net, event, rssi, GFP_KERNEL); +} + +#define DEVICE_POLLER_JIFFIES (HZ) +static void rndis_device_poller(struct work_struct *work) +{ + struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, + dev_poller_work.work); + struct usbnet *usbdev = priv->usbdev; + __le32 rssi, tmp; + int len, ret, j; + int update_jiffies = DEVICE_POLLER_JIFFIES; + void *buf; + + /* Only check/do workaround when connected. Calling is_associated() + * also polls device with rndis_command() and catches for media link + * indications. + */ + if (!is_associated(usbdev)) { + /* Workaround bad scanning in BCM4320a devices with active + * background scanning when not associated. + */ + if (priv->device_type == RNDIS_BCM4320A && priv->radio_on && + !priv->scan_request) { + /* Get previous scan results */ + rndis_check_bssid_list(usbdev, NULL, NULL); + + /* Initiate new scan */ + rndis_start_bssid_list_scan(usbdev); + } + + goto end; + } + + len = sizeof(rssi); + ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, + &rssi, &len); + if (ret == 0) { + priv->last_qual = level_to_qual(le32_to_cpu(rssi)); + rndis_do_cqm(usbdev, le32_to_cpu(rssi)); + } + + netdev_dbg(usbdev->net, "dev-poller: RNDIS_OID_802_11_RSSI -> %d, rssi:%d, qual: %d\n", + ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); + + /* Workaround transfer stalls on poor quality links. + * TODO: find right way to fix these stalls (as stalls do not happen + * with ndiswrapper/windows driver). */ + if (priv->param_workaround_interval > 0 && priv->last_qual <= 25) { + /* Decrease stats worker interval to catch stalls. + * faster. Faster than 400-500ms causes packet loss, + * Slower doesn't catch stalls fast enough. + */ + j = msecs_to_jiffies(priv->param_workaround_interval); + if (j > DEVICE_POLLER_JIFFIES) + j = DEVICE_POLLER_JIFFIES; + else if (j <= 0) + j = 1; + update_jiffies = j; + + /* Send scan OID. Use of both OIDs is required to get device + * working. + */ + tmp = cpu_to_le32(1); + rndis_set_oid(usbdev, + RNDIS_OID_802_11_BSSID_LIST_SCAN, + &tmp, sizeof(tmp)); + + len = CONTROL_BUFFER_SIZE; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + goto end; + + rndis_query_oid(usbdev, + RNDIS_OID_802_11_BSSID_LIST, + buf, &len); + kfree(buf); + } + +end: + if (update_jiffies >= HZ) + update_jiffies = round_jiffies_relative(update_jiffies); + else { + j = round_jiffies_relative(update_jiffies); + if (abs(j - update_jiffies) <= 10) + update_jiffies = j; + } + + queue_delayed_work(priv->workqueue, &priv->dev_poller_work, + update_jiffies); +} + +/* + * driver/device initialization + */ +static void rndis_copy_module_params(struct usbnet *usbdev, int device_type) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + + priv->device_type = device_type; + + priv->param_country[0] = modparam_country[0]; + priv->param_country[1] = modparam_country[1]; + priv->param_country[2] = 0; + priv->param_frameburst = modparam_frameburst; + priv->param_afterburner = modparam_afterburner; + priv->param_power_save = modparam_power_save; + priv->param_power_output = modparam_power_output; + priv->param_roamtrigger = modparam_roamtrigger; + priv->param_roamdelta = modparam_roamdelta; + + priv->param_country[0] = toupper(priv->param_country[0]); + priv->param_country[1] = toupper(priv->param_country[1]); + /* doesn't support EU as country code, use FI instead */ + if (!strcmp(priv->param_country, "EU")) + strcpy(priv->param_country, "FI"); + + if (priv->param_power_save < 0) + priv->param_power_save = 0; + else if (priv->param_power_save > 2) + priv->param_power_save = 2; + + if (priv->param_power_output < 0) + priv->param_power_output = 0; + else if (priv->param_power_output > 3) + priv->param_power_output = 3; + + if (priv->param_roamtrigger < -80) + priv->param_roamtrigger = -80; + else if (priv->param_roamtrigger > -60) + priv->param_roamtrigger = -60; + + if (priv->param_roamdelta < 0) + priv->param_roamdelta = 0; + else if (priv->param_roamdelta > 2) + priv->param_roamdelta = 2; + + if (modparam_workaround_interval < 0) + priv->param_workaround_interval = 500; + else + priv->param_workaround_interval = modparam_workaround_interval; +} + +static int unknown_early_init(struct usbnet *usbdev) +{ + /* copy module parameters for unknown so that iwconfig reports txpower + * and workaround parameter is copied to private structure correctly. + */ + rndis_copy_module_params(usbdev, RNDIS_UNKNOWN); + + /* This is unknown device, so do not try set configuration parameters. + */ + + return 0; +} + +static int bcm4320a_early_init(struct usbnet *usbdev) +{ + /* copy module parameters for bcm4320a so that iwconfig reports txpower + * and workaround parameter is copied to private structure correctly. + */ + rndis_copy_module_params(usbdev, RNDIS_BCM4320A); + + /* bcm4320a doesn't handle configuration parameters well. Try + * set any and you get partially zeroed mac and broken device. + */ + + return 0; +} + +static int bcm4320b_early_init(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + char buf[8]; + + rndis_copy_module_params(usbdev, RNDIS_BCM4320B); + + /* Early initialization settings, setting these won't have effect + * if called after generic_rndis_bind(). + */ + + rndis_set_config_parameter_str(usbdev, "Country", priv->param_country); + rndis_set_config_parameter_str(usbdev, "FrameBursting", + priv->param_frameburst ? "1" : "0"); + rndis_set_config_parameter_str(usbdev, "Afterburner", + priv->param_afterburner ? "1" : "0"); + sprintf(buf, "%d", priv->param_power_save); + rndis_set_config_parameter_str(usbdev, "PowerSaveMode", buf); + sprintf(buf, "%d", priv->param_power_output); + rndis_set_config_parameter_str(usbdev, "PwrOut", buf); + sprintf(buf, "%d", priv->param_roamtrigger); + rndis_set_config_parameter_str(usbdev, "RoamTrigger", buf); + sprintf(buf, "%d", priv->param_roamdelta); + rndis_set_config_parameter_str(usbdev, "RoamDelta", buf); + + return 0; +} + +/* same as rndis_netdev_ops but with local multicast handler */ +static const struct net_device_ops rndis_wlan_netdev_ops = { + .ndo_open = usbnet_open, + .ndo_stop = usbnet_stop, + .ndo_start_xmit = usbnet_start_xmit, + .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = dev_get_tstats64, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rndis_wlan_set_multicast_list, +}; + +static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) +{ + struct wiphy *wiphy; + struct rndis_wlan_private *priv; + int retval, len; + __le32 tmp; + + /* allocate wiphy and rndis private data + * NOTE: We only support a single virtual interface, so wiphy + * and wireless_dev are somewhat synonymous for this device. + */ + wiphy = wiphy_new(&rndis_config_ops, sizeof(struct rndis_wlan_private)); + if (!wiphy) + return -ENOMEM; + + priv = wiphy_priv(wiphy); + usbdev->net->ieee80211_ptr = &priv->wdev; + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_STATION; + + /* These have to be initialized before calling generic_rndis_bind(). + * Otherwise we'll be in big trouble in rndis_wlan_early_init(). + */ + usbdev->driver_priv = priv; + priv->usbdev = usbdev; + + mutex_init(&priv->command_lock); + + /* because rndis_command() sleeps we need to use workqueue */ + priv->workqueue = create_singlethread_workqueue("rndis_wlan"); + if (!priv->workqueue) { + wiphy_free(wiphy); + return -ENOMEM; + } + INIT_WORK(&priv->work, rndis_wlan_worker); + INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller); + INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results); + + /* try bind rndis_host */ + retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS); + if (retval < 0) + goto fail; + + /* generic_rndis_bind set packet filter to multicast_all+ + * promisc mode which doesn't work well for our devices (device + * picks up rssi to closest station instead of to access point). + * + * rndis_host wants to avoid all OID as much as possible + * so do promisc/multicast handling in rndis_wlan. + */ + usbdev->net->netdev_ops = &rndis_wlan_netdev_ops; + + tmp = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST); + retval = rndis_set_oid(usbdev, + RNDIS_OID_GEN_CURRENT_PACKET_FILTER, + &tmp, sizeof(tmp)); + + len = sizeof(tmp); + retval = rndis_query_oid(usbdev, + RNDIS_OID_802_3_MAXIMUM_LIST_SIZE, + &tmp, &len); + priv->multicast_size = le32_to_cpu(tmp); + if (retval < 0 || priv->multicast_size < 0) + priv->multicast_size = 0; + if (priv->multicast_size > 0) + usbdev->net->flags |= IFF_MULTICAST; + else + usbdev->net->flags &= ~IFF_MULTICAST; + + /* fill-out wiphy structure and register w/ cfg80211 */ + memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN); + wiphy->privid = rndis_wiphy_privid; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC); + wiphy->max_scan_ssids = 1; + + /* TODO: fill-out band/encr information based on priv->caps */ + rndis_wlan_get_caps(usbdev, wiphy); + + memcpy(priv->channels, rndis_channels, sizeof(rndis_channels)); + memcpy(priv->rates, rndis_rates, sizeof(rndis_rates)); + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(rndis_channels); + priv->band.bitrates = priv->rates; + priv->band.n_bitrates = ARRAY_SIZE(rndis_rates); + wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; + + memcpy(priv->cipher_suites, rndis_cipher_suites, + sizeof(rndis_cipher_suites)); + wiphy->cipher_suites = priv->cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(rndis_cipher_suites); + + set_wiphy_dev(wiphy, &usbdev->udev->dev); + + if (wiphy_register(wiphy)) { + retval = -ENODEV; + goto fail; + } + + set_default_iw_params(usbdev); + + priv->power_mode = -1; + + /* set default rts/frag */ + rndis_set_wiphy_params(wiphy, + WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); + + /* turn radio off on init */ + priv->radio_on = false; + disassociate(usbdev, false); + netif_carrier_off(usbdev->net); + + return 0; + +fail: + cancel_delayed_work_sync(&priv->dev_poller_work); + cancel_delayed_work_sync(&priv->scan_work); + cancel_work_sync(&priv->work); + destroy_workqueue(priv->workqueue); + + wiphy_free(wiphy); + return retval; +} + +static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + + /* turn radio off */ + disassociate(usbdev, false); + + cancel_delayed_work_sync(&priv->dev_poller_work); + cancel_delayed_work_sync(&priv->scan_work); + cancel_work_sync(&priv->work); + destroy_workqueue(priv->workqueue); + + rndis_unbind(usbdev, intf); + + wiphy_unregister(priv->wdev.wiphy); + wiphy_free(priv->wdev.wiphy); +} + +static int rndis_wlan_reset(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int retval; + + netdev_dbg(usbdev->net, "%s()\n", __func__); + + retval = rndis_reset(usbdev); + if (retval) + netdev_warn(usbdev->net, "rndis_reset failed: %d\n", retval); + + /* rndis_reset cleared multicast list, so restore here. + (set_multicast_list() also turns on current packet filter) */ + set_multicast_list(usbdev); + + queue_delayed_work(priv->workqueue, &priv->dev_poller_work, + round_jiffies_relative(DEVICE_POLLER_JIFFIES)); + + return deauthenticate(usbdev); +} + +static int rndis_wlan_stop(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int retval; + __le32 filter; + + netdev_dbg(usbdev->net, "%s()\n", __func__); + + retval = disassociate(usbdev, false); + + priv->work_pending = 0; + cancel_delayed_work_sync(&priv->dev_poller_work); + cancel_delayed_work_sync(&priv->scan_work); + cancel_work_sync(&priv->work); + flush_workqueue(priv->workqueue); + + if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request = NULL; + } + + /* Set current packet filter zero to block receiving data packets from + device. */ + filter = 0; + rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, + sizeof(filter)); + + return retval; +} + +static const struct driver_info bcm4320b_info = { + .description = "Wireless RNDIS device, BCM4320b based", + .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | + FLAG_AVOID_UNLINK_URBS, + .bind = rndis_wlan_bind, + .unbind = rndis_wlan_unbind, + .status = rndis_status, + .rx_fixup = rndis_rx_fixup, + .tx_fixup = rndis_tx_fixup, + .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, + .early_init = bcm4320b_early_init, + .indication = rndis_wlan_indication, +}; + +static const struct driver_info bcm4320a_info = { + .description = "Wireless RNDIS device, BCM4320a based", + .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | + FLAG_AVOID_UNLINK_URBS, + .bind = rndis_wlan_bind, + .unbind = rndis_wlan_unbind, + .status = rndis_status, + .rx_fixup = rndis_rx_fixup, + .tx_fixup = rndis_tx_fixup, + .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, + .early_init = bcm4320a_early_init, + .indication = rndis_wlan_indication, +}; + +static const struct driver_info rndis_wlan_info = { + .description = "Wireless RNDIS device", + .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | + FLAG_AVOID_UNLINK_URBS, + .bind = rndis_wlan_bind, + .unbind = rndis_wlan_unbind, + .status = rndis_status, + .rx_fixup = rndis_rx_fixup, + .tx_fixup = rndis_tx_fixup, + .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, + .early_init = unknown_early_init, + .indication = rndis_wlan_indication, +}; + +/*-------------------------------------------------------------------------*/ + +static const struct usb_device_id products [] = { +#define RNDIS_MASTER_INTERFACE \ + .bInterfaceClass = USB_CLASS_COMM, \ + .bInterfaceSubClass = 2 /* ACM */, \ + .bInterfaceProtocol = 0x0ff + +/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom + * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki. + */ +{ + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x0411, + .idProduct = 0x00bc, /* Buffalo WLI-U2-KG125S */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x0baf, + .idProduct = 0x011b, /* U.S. Robotics USR5421 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x050d, + .idProduct = 0x011b, /* Belkin F5D7051 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x1799, /* Belkin has two vendor ids */ + .idProduct = 0x011b, /* Belkin F5D7051 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x13b1, + .idProduct = 0x0014, /* Linksys WUSB54GSv2 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x13b1, + .idProduct = 0x0026, /* Linksys WUSB54GSC */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x0b05, + .idProduct = 0x1717, /* Asus WL169gE */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x0a5c, + .idProduct = 0xd11b, /* Eminent EM4045 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x1690, + .idProduct = 0x0715, /* BT Voyager 1055 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320b_info, +}, +/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom + * parameters available, hardware probably contain older firmware version with + * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki. + */ +{ + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x13b1, + .idProduct = 0x000e, /* Linksys WUSB54GSv1 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320a_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x0baf, + .idProduct = 0x0111, /* U.S. Robotics USR5420 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320a_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x0411, + .idProduct = 0x004b, /* BUFFALO WLI-USB-G54 */ + RNDIS_MASTER_INTERFACE, + .driver_info = (unsigned long) &bcm4320a_info, +}, +/* Generic Wireless RNDIS devices that we don't have exact + * idVendor/idProduct/chip yet. + */ +{ + /* RNDIS is MSFT's un-official variant of CDC ACM */ + USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), + .driver_info = (unsigned long) &rndis_wlan_info, +}, { + /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ + USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), + .driver_info = (unsigned long) &rndis_wlan_info, +}, + { }, // END +}; +MODULE_DEVICE_TABLE(usb, products); + +static struct usb_driver rndis_wlan_driver = { + .name = "rndis_wlan", + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, + .suspend = usbnet_suspend, + .resume = usbnet_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(rndis_wlan_driver); + +MODULE_AUTHOR("Bjorge Dijkstra"); +MODULE_AUTHOR("Jussi Kivilinna"); +MODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/net/wireless/legacy/wl3501.h b/drivers/net/wireless/legacy/wl3501.h new file mode 100644 index 000000000000..91f276dd22a1 --- /dev/null +++ b/drivers/net/wireless/legacy/wl3501.h @@ -0,0 +1,615 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __WL3501_H__ +#define __WL3501_H__ + +#include +#include + +/* define for WLA 2.0 */ +#define WL3501_BLKSZ 256 +/* + * ID for input Signals of DRIVER block + * bit[7-5] is block ID: 000 + * bit[4-0] is signal ID +*/ +enum wl3501_signals { + WL3501_SIG_ALARM, + WL3501_SIG_MD_CONFIRM, + WL3501_SIG_MD_IND, + WL3501_SIG_ASSOC_CONFIRM, + WL3501_SIG_ASSOC_IND, + WL3501_SIG_AUTH_CONFIRM, + WL3501_SIG_AUTH_IND, + WL3501_SIG_DEAUTH_CONFIRM, + WL3501_SIG_DEAUTH_IND, + WL3501_SIG_DISASSOC_CONFIRM, + WL3501_SIG_DISASSOC_IND, + WL3501_SIG_GET_CONFIRM, + WL3501_SIG_JOIN_CONFIRM, + WL3501_SIG_PWR_MGMT_CONFIRM, + WL3501_SIG_REASSOC_CONFIRM, + WL3501_SIG_REASSOC_IND, + WL3501_SIG_SCAN_CONFIRM, + WL3501_SIG_SET_CONFIRM, + WL3501_SIG_START_CONFIRM, + WL3501_SIG_RESYNC_CONFIRM, + WL3501_SIG_SITE_CONFIRM, + WL3501_SIG_SAVE_CONFIRM, + WL3501_SIG_RFTEST_CONFIRM, +/* + * ID for input Signals of MLME block + * bit[7-5] is block ID: 010 + * bit[4-0] is signal ID + */ + WL3501_SIG_ASSOC_REQ = 0x20, + WL3501_SIG_AUTH_REQ, + WL3501_SIG_DEAUTH_REQ, + WL3501_SIG_DISASSOC_REQ, + WL3501_SIG_GET_REQ, + WL3501_SIG_JOIN_REQ, + WL3501_SIG_PWR_MGMT_REQ, + WL3501_SIG_REASSOC_REQ, + WL3501_SIG_SCAN_REQ, + WL3501_SIG_SET_REQ, + WL3501_SIG_START_REQ, + WL3501_SIG_MD_REQ, + WL3501_SIG_RESYNC_REQ, + WL3501_SIG_SITE_REQ, + WL3501_SIG_SAVE_REQ, + WL3501_SIG_RF_TEST_REQ, + WL3501_SIG_MM_CONFIRM = 0x60, + WL3501_SIG_MM_IND, +}; + +enum wl3501_mib_attribs { + WL3501_MIB_ATTR_STATION_ID, + WL3501_MIB_ATTR_AUTH_ALGORITHMS, + WL3501_MIB_ATTR_AUTH_TYPE, + WL3501_MIB_ATTR_MEDIUM_OCCUPANCY_LIMIT, + WL3501_MIB_ATTR_CF_POLLABLE, + WL3501_MIB_ATTR_CFP_PERIOD, + WL3501_MIB_ATTR_CFPMAX_DURATION, + WL3501_MIB_ATTR_AUTH_RESP_TMOUT, + WL3501_MIB_ATTR_RX_DTIMS, + WL3501_MIB_ATTR_PRIV_OPT_IMPLEMENTED, + WL3501_MIB_ATTR_PRIV_INVOKED, + WL3501_MIB_ATTR_WEP_DEFAULT_KEYS, + WL3501_MIB_ATTR_WEP_DEFAULT_KEY_ID, + WL3501_MIB_ATTR_WEP_KEY_MAPPINGS, + WL3501_MIB_ATTR_WEP_KEY_MAPPINGS_LEN, + WL3501_MIB_ATTR_EXCLUDE_UNENCRYPTED, + WL3501_MIB_ATTR_WEP_ICV_ERROR_COUNT, + WL3501_MIB_ATTR_WEP_UNDECRYPTABLE_COUNT, + WL3501_MIB_ATTR_WEP_EXCLUDED_COUNT, + WL3501_MIB_ATTR_MAC_ADDR, + WL3501_MIB_ATTR_GROUP_ADDRS, + WL3501_MIB_ATTR_RTS_THRESHOLD, + WL3501_MIB_ATTR_SHORT_RETRY_LIMIT, + WL3501_MIB_ATTR_LONG_RETRY_LIMIT, + WL3501_MIB_ATTR_FRAG_THRESHOLD, + WL3501_MIB_ATTR_MAX_TX_MSDU_LIFETIME, + WL3501_MIB_ATTR_MAX_RX_LIFETIME, + WL3501_MIB_ATTR_MANUFACTURER_ID, + WL3501_MIB_ATTR_PRODUCT_ID, + WL3501_MIB_ATTR_TX_FRAG_COUNT, + WL3501_MIB_ATTR_MULTICAST_TX_FRAME_COUNT, + WL3501_MIB_ATTR_FAILED_COUNT, + WL3501_MIB_ATTR_RX_FRAG_COUNT, + WL3501_MIB_ATTR_MULTICAST_RX_COUNT, + WL3501_MIB_ATTR_FCS_ERROR_COUNT, + WL3501_MIB_ATTR_RETRY_COUNT, + WL3501_MIB_ATTR_MULTIPLE_RETRY_COUNT, + WL3501_MIB_ATTR_RTS_SUCCESS_COUNT, + WL3501_MIB_ATTR_RTS_FAILURE_COUNT, + WL3501_MIB_ATTR_ACK_FAILURE_COUNT, + WL3501_MIB_ATTR_FRAME_DUPLICATE_COUNT, + WL3501_MIB_ATTR_PHY_TYPE, + WL3501_MIB_ATTR_REG_DOMAINS_SUPPORT, + WL3501_MIB_ATTR_CURRENT_REG_DOMAIN, + WL3501_MIB_ATTR_SLOT_TIME, + WL3501_MIB_ATTR_CCA_TIME, + WL3501_MIB_ATTR_RX_TX_TURNAROUND_TIME, + WL3501_MIB_ATTR_TX_PLCP_DELAY, + WL3501_MIB_ATTR_RX_TX_SWITCH_TIME, + WL3501_MIB_ATTR_TX_RAMP_ON_TIME, + WL3501_MIB_ATTR_TX_RF_DELAY, + WL3501_MIB_ATTR_SIFS_TIME, + WL3501_MIB_ATTR_RX_RF_DELAY, + WL3501_MIB_ATTR_RX_PLCP_DELAY, + WL3501_MIB_ATTR_MAC_PROCESSING_DELAY, + WL3501_MIB_ATTR_TX_RAMP_OFF_TIME, + WL3501_MIB_ATTR_PREAMBLE_LEN, + WL3501_MIB_ATTR_PLCP_HEADER_LEN, + WL3501_MIB_ATTR_MPDU_DURATION_FACTOR, + WL3501_MIB_ATTR_AIR_PROPAGATION_TIME, + WL3501_MIB_ATTR_TEMP_TYPE, + WL3501_MIB_ATTR_CW_MIN, + WL3501_MIB_ATTR_CW_MAX, + WL3501_MIB_ATTR_SUPPORT_DATA_RATES_TX, + WL3501_MIB_ATTR_SUPPORT_DATA_RATES_RX, + WL3501_MIB_ATTR_MPDU_MAX_LEN, + WL3501_MIB_ATTR_SUPPORT_TX_ANTENNAS, + WL3501_MIB_ATTR_CURRENT_TX_ANTENNA, + WL3501_MIB_ATTR_SUPPORT_RX_ANTENNAS, + WL3501_MIB_ATTR_DIVERSITY_SUPPORT, + WL3501_MIB_ATTR_DIVERSITY_SELECTION_RS, + WL3501_MIB_ATTR_NR_SUPPORTED_PWR_LEVELS, + WL3501_MIB_ATTR_TX_PWR_LEVEL1, + WL3501_MIB_ATTR_TX_PWR_LEVEL2, + WL3501_MIB_ATTR_TX_PWR_LEVEL3, + WL3501_MIB_ATTR_TX_PWR_LEVEL4, + WL3501_MIB_ATTR_TX_PWR_LEVEL5, + WL3501_MIB_ATTR_TX_PWR_LEVEL6, + WL3501_MIB_ATTR_TX_PWR_LEVEL7, + WL3501_MIB_ATTR_TX_PWR_LEVEL8, + WL3501_MIB_ATTR_CURRENT_TX_PWR_LEVEL, + WL3501_MIB_ATTR_CURRENT_CHAN, + WL3501_MIB_ATTR_CCA_MODE_SUPPORTED, + WL3501_MIB_ATTR_CURRENT_CCA_MODE, + WL3501_MIB_ATTR_ED_THRESHOLD, + WL3501_MIB_ATTR_SINTHESIZER_LOCKED, + WL3501_MIB_ATTR_CURRENT_PWR_STATE, + WL3501_MIB_ATTR_DOZE_TURNON_TIME, + WL3501_MIB_ATTR_RCR33, + WL3501_MIB_ATTR_DEFAULT_CHAN, + WL3501_MIB_ATTR_SSID, + WL3501_MIB_ATTR_PWR_MGMT_ENABLE, + WL3501_MIB_ATTR_NET_CAPABILITY, + WL3501_MIB_ATTR_ROUTING, +}; + +enum wl3501_net_type { + WL3501_NET_TYPE_INFRA, + WL3501_NET_TYPE_ADHOC, + WL3501_NET_TYPE_ANY_BSS, +}; + +enum wl3501_scan_type { + WL3501_SCAN_TYPE_ACTIVE, + WL3501_SCAN_TYPE_PASSIVE, +}; + +enum wl3501_tx_result { + WL3501_TX_RESULT_SUCCESS, + WL3501_TX_RESULT_NO_BSS, + WL3501_TX_RESULT_RETRY_LIMIT, +}; + +enum wl3501_sys_type { + WL3501_SYS_TYPE_OPEN, + WL3501_SYS_TYPE_SHARE_KEY, +}; + +enum wl3501_status { + WL3501_STATUS_SUCCESS, + WL3501_STATUS_INVALID, + WL3501_STATUS_TIMEOUT, + WL3501_STATUS_REFUSED, + WL3501_STATUS_MANY_REQ, + WL3501_STATUS_ALREADY_BSS, +}; + +#define WL3501_MGMT_CAPABILITY_ESS 0x0001 /* see 802.11 p.58 */ +#define WL3501_MGMT_CAPABILITY_IBSS 0x0002 /* - " - */ +#define WL3501_MGMT_CAPABILITY_CF_POLLABLE 0x0004 /* - " - */ +#define WL3501_MGMT_CAPABILITY_CF_POLL_REQUEST 0x0008 /* - " - */ +#define WL3501_MGMT_CAPABILITY_PRIVACY 0x0010 /* - " - */ + +#define IW_REG_DOMAIN_FCC 0x10 /* Channel 1 to 11 USA */ +#define IW_REG_DOMAIN_DOC 0x20 /* Channel 1 to 11 Canada */ +#define IW_REG_DOMAIN_ETSI 0x30 /* Channel 1 to 13 Europe */ +#define IW_REG_DOMAIN_SPAIN 0x31 /* Channel 10 to 11 Spain */ +#define IW_REG_DOMAIN_FRANCE 0x32 /* Channel 10 to 13 France */ +#define IW_REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ +#define IW_REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan */ +#define IW_REG_DOMAIN_ISRAEL 0x50 /* Channel 3 - 9 Israel */ + +#define IW_MGMT_RATE_LABEL_MANDATORY 128 /* MSB */ + +enum iw_mgmt_rate_labels { + IW_MGMT_RATE_LABEL_1MBIT = 2, + IW_MGMT_RATE_LABEL_2MBIT = 4, + IW_MGMT_RATE_LABEL_5_5MBIT = 11, + IW_MGMT_RATE_LABEL_11MBIT = 22, +}; + +enum iw_mgmt_info_element_ids { + IW_MGMT_INFO_ELEMENT_SSID, /* Service Set Identity */ + IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES, + IW_MGMT_INFO_ELEMENT_FH_PARAMETER_SET, + IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET, + IW_MGMT_INFO_ELEMENT_CS_PARAMETER_SET, + IW_MGMT_INFO_ELEMENT_CS_TIM, /* Traffic Information Map */ + IW_MGMT_INFO_ELEMENT_IBSS_PARAMETER_SET, + /* 7-15: Reserved, unused */ + IW_MGMT_INFO_ELEMENT_CHALLENGE_TEXT = 16, + /* 17-31 Reserved for challenge text extension */ + /* 32-255 Reserved, unused */ +}; + +struct iw_mgmt_info_element { + u8 id; /* one of enum iw_mgmt_info_element_ids, + but sizeof(enum) > sizeof(u8) :-( */ + u8 len; + u8 data[]; +} __packed; + +struct iw_mgmt_essid_pset { + struct iw_mgmt_info_element el; + u8 essid[IW_ESSID_MAX_SIZE]; +} __packed; + +/* + * According to 802.11 Wireless Networks, the definitive guide - O'Reilly + * Pg 75 + */ +#define IW_DATA_RATE_MAX_LABELS 8 + +struct iw_mgmt_data_rset { + struct iw_mgmt_info_element el; + u8 data_rate_labels[IW_DATA_RATE_MAX_LABELS]; +} __packed; + +struct iw_mgmt_ds_pset { + struct iw_mgmt_info_element el; + u8 chan; +} __packed; + +struct iw_mgmt_cf_pset { + struct iw_mgmt_info_element el; + u8 cfp_count; + u8 cfp_period; + u16 cfp_max_duration; + u16 cfp_dur_remaining; +} __packed; + +struct iw_mgmt_ibss_pset { + struct iw_mgmt_info_element el; + u16 atim_window; +} __packed; + +struct wl3501_tx_hdr { + u16 tx_cnt; + u8 sync[16]; + u16 sfd; + u8 signal; + u8 service; + u16 len; + u16 crc16; + u16 frame_ctrl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctrl; + u8 addr4[ETH_ALEN]; +}; + +struct wl3501_rx_hdr { + u16 rx_next_blk; + u16 rc_next_frame_blk; + u8 rx_blk_ctrl; + u8 rx_next_frame; + u8 rx_next_frame1; + u8 rssi; + char time[8]; + u8 signal; + u8 service; + u16 len; + u16 crc16; + u16 frame_ctrl; + u16 duration; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq; + u8 addr4[ETH_ALEN]; +}; + +struct wl3501_start_req { + u16 next_blk; + u8 sig_id; + u8 bss_type; + u16 beacon_period; + u16 dtim_period; + u16 probe_delay; + u16 cap_info; + struct iw_mgmt_essid_pset ssid; + struct iw_mgmt_data_rset bss_basic_rset; + struct iw_mgmt_data_rset operational_rset; + struct iw_mgmt_cf_pset cf_pset; + struct iw_mgmt_ds_pset ds_pset; + struct iw_mgmt_ibss_pset ibss_pset; +}; + +struct wl3501_assoc_req { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 timeout; + u16 cap_info; + u16 listen_interval; + u8 mac_addr[ETH_ALEN]; +}; + +struct wl3501_assoc_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 status; +}; + +struct wl3501_assoc_ind { + u16 next_blk; + u8 sig_id; + u8 mac_addr[ETH_ALEN]; +}; + +struct wl3501_auth_req { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 type; + u16 timeout; + u8 mac_addr[ETH_ALEN]; +}; + +struct wl3501_auth_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 type; + u16 status; + u8 mac_addr[ETH_ALEN]; +}; + +struct wl3501_get_req { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 mib_attrib; +}; + +struct wl3501_get_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 mib_status; + u16 mib_attrib; + u8 mib_value[100]; +}; + +struct wl3501_req { + u16 beacon_period; + u16 dtim_period; + u16 cap_info; + u8 bss_type; + u8 bssid[ETH_ALEN]; + struct iw_mgmt_essid_pset ssid; + struct iw_mgmt_ds_pset ds_pset; + struct iw_mgmt_cf_pset cf_pset; + struct iw_mgmt_ibss_pset ibss_pset; + struct iw_mgmt_data_rset bss_basic_rset; +}; + +struct wl3501_join_req { + u16 next_blk; + u8 sig_id; + u8 reserved; + struct iw_mgmt_data_rset operational_rset; + u16 reserved2; + u16 timeout; + u16 probe_delay; + u8 timestamp[8]; + u8 local_time[8]; + struct wl3501_req req; +}; + +struct wl3501_join_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 status; +}; + +struct wl3501_pwr_mgmt_req { + u16 next_blk; + u8 sig_id; + u8 pwr_save; + u8 wake_up; + u8 receive_dtims; +}; + +struct wl3501_pwr_mgmt_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 status; +}; + +struct wl3501_scan_req { + u16 next_blk; + u8 sig_id; + u8 bss_type; + u16 probe_delay; + u16 min_chan_time; + u16 max_chan_time; + u8 chan_list[14]; + u8 bssid[ETH_ALEN]; + struct iw_mgmt_essid_pset ssid; + enum wl3501_scan_type scan_type; +}; + +struct wl3501_scan_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 status; + char timestamp[8]; + char localtime[8]; + struct wl3501_req req; + u8 rssi; +}; + +struct wl3501_start_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 status; +}; + +struct wl3501_md_req { + u16 next_blk; + u8 sig_id; + u8 routing; + u16 data; + u16 size; + u8 pri; + u8 service_class; + struct { + u8 daddr[ETH_ALEN]; + u8 saddr[ETH_ALEN]; + } addr; +}; + +struct wl3501_md_ind { + u16 next_blk; + u8 sig_id; + u8 routing; + u16 data; + u16 size; + u8 reception; + u8 pri; + u8 service_class; + struct { + u8 daddr[ETH_ALEN]; + u8 saddr[ETH_ALEN]; + } addr; +}; + +struct wl3501_md_confirm { + u16 next_blk; + u8 sig_id; + u8 reserved; + u16 data; + u8 status; + u8 pri; + u8 service_class; +}; + +struct wl3501_resync_req { + u16 next_blk; + u8 sig_id; +}; + +/* Definitions for supporting clone adapters. */ +/* System Interface Registers (SIR space) */ +#define WL3501_NIC_GCR ((u8)0x00) /* SIR0 - General Conf Register */ +#define WL3501_NIC_BSS ((u8)0x01) /* SIR1 - Bank Switching Select Reg */ +#define WL3501_NIC_LMAL ((u8)0x02) /* SIR2 - Local Mem addr Reg [7:0] */ +#define WL3501_NIC_LMAH ((u8)0x03) /* SIR3 - Local Mem addr Reg [14:8] */ +#define WL3501_NIC_IODPA ((u8)0x04) /* SIR4 - I/O Data Port A */ +#define WL3501_NIC_IODPB ((u8)0x05) /* SIR5 - I/O Data Port B */ +#define WL3501_NIC_IODPC ((u8)0x06) /* SIR6 - I/O Data Port C */ +#define WL3501_NIC_IODPD ((u8)0x07) /* SIR7 - I/O Data Port D */ + +/* Bits in GCR */ +#define WL3501_GCR_SWRESET ((u8)0x80) +#define WL3501_GCR_CORESET ((u8)0x40) +#define WL3501_GCR_DISPWDN ((u8)0x20) +#define WL3501_GCR_ECWAIT ((u8)0x10) +#define WL3501_GCR_ECINT ((u8)0x08) +#define WL3501_GCR_INT2EC ((u8)0x04) +#define WL3501_GCR_ENECINT ((u8)0x02) +#define WL3501_GCR_DAM ((u8)0x01) + +/* Bits in BSS (Bank Switching Select Register) */ +#define WL3501_BSS_FPAGE0 ((u8)0x20) /* Flash memory page0 */ +#define WL3501_BSS_FPAGE1 ((u8)0x28) +#define WL3501_BSS_FPAGE2 ((u8)0x30) +#define WL3501_BSS_FPAGE3 ((u8)0x38) +#define WL3501_BSS_SPAGE0 ((u8)0x00) /* SRAM page0 */ +#define WL3501_BSS_SPAGE1 ((u8)0x08) +#define WL3501_BSS_SPAGE2 ((u8)0x10) +#define WL3501_BSS_SPAGE3 ((u8)0x18) + +/* Define Driver Interface */ +/* Refer IEEE 802.11 */ +/* Tx packet header, include PLCP and MPDU */ +/* Tx PLCP Header */ +struct wl3501_80211_tx_plcp_hdr { + u8 sync[16]; + u16 sfd; + u8 signal; + u8 service; + u16 len; + u16 crc16; +} __packed; + +struct wl3501_80211_tx_hdr { + struct wl3501_80211_tx_plcp_hdr pclp_hdr; + struct ieee80211_hdr mac_hdr; +} __packed __aligned(2); + +/* + Reserve the beginning Tx space for descriptor use. + + TxBlockOffset --> *----*----*----*----* \ + (TxFreeDesc) | 0 | 1 | 2 | 3 | \ + | 4 | 5 | 6 | 7 | | + | 8 | 9 | 10 | 11 | TX_DESC * 20 + | 12 | 13 | 14 | 15 | | + | 16 | 17 | 18 | 19 | / + TxBufferBegin --> *----*----*----*----* / + (TxBufferHead) | | + (TxBufferTail) | | + | Send Buffer | + | | + | | + *-------------------* + TxBufferEnd -------------------------/ + +*/ + +struct wl3501_card { + int base_addr; + u8 mac_addr[ETH_ALEN]; + spinlock_t lock; + wait_queue_head_t wait; + struct wl3501_get_confirm sig_get_confirm; + struct wl3501_pwr_mgmt_confirm sig_pwr_mgmt_confirm; + u16 tx_buffer_size; + u16 tx_buffer_head; + u16 tx_buffer_tail; + u16 tx_buffer_cnt; + u16 esbq_req_start; + u16 esbq_req_end; + u16 esbq_req_head; + u16 esbq_req_tail; + u16 esbq_confirm_start; + u16 esbq_confirm_end; + u16 esbq_confirm; + struct iw_mgmt_essid_pset essid; + struct iw_mgmt_essid_pset keep_essid; + u8 bssid[ETH_ALEN]; + int net_type; + char nick[32]; + char card_name[32]; + char firmware_date[32]; + u8 chan; + u8 cap_info; + u16 start_seg; + u16 bss_cnt; + u16 join_sta_bss; + u8 rssi; + u8 adhoc_times; + u8 reg_domain; + u8 version[2]; + struct wl3501_scan_confirm bss_set[20]; + + struct iw_statistics wstats; + struct iw_spy_data spy_data; + struct iw_public_data wireless_data; + struct pcmcia_device *p_dev; +}; +#endif diff --git a/drivers/net/wireless/legacy/wl3501_cs.c b/drivers/net/wireless/legacy/wl3501_cs.c new file mode 100644 index 000000000000..7fb2f9513476 --- /dev/null +++ b/drivers/net/wireless/legacy/wl3501_cs.c @@ -0,0 +1,2030 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * WL3501 Wireless LAN PCMCIA Card Driver for Linux + * Written originally for Linux 2.0.30 by Fox Chen, mhchen@golf.ccl.itri.org.tw + * Ported to 2.2, 2.4 & 2.5 by Arnaldo Carvalho de Melo + * Wireless extensions in 2.4 by Gustavo Niemeyer + * + * References used by Fox Chen while writing the original driver for 2.0.30: + * + * 1. WL24xx packet drivers (tooasm.asm) + * 2. Access Point Firmware Interface Specification for IEEE 802.11 SUTRO + * 3. IEEE 802.11 + * 4. Linux network driver (/usr/src/linux/drivers/net) + * 5. ISA card driver - wl24.c + * 6. Linux PCMCIA skeleton driver - skeleton.c + * 7. Linux PCMCIA 3c589 network driver - 3c589_cs.c + * + * Tested with WL2400 firmware 1.2, Linux 2.0.30, and pcmcia-cs-2.9.12 + * 1. Performance: about 165 Kbytes/sec in TCP/IP with Ad-Hoc mode. + * rsh 192.168.1.3 "dd if=/dev/zero bs=1k count=1000" > /dev/null + * (Specification 2M bits/sec. is about 250 Kbytes/sec., but we must deduct + * ETHER/IP/UDP/TCP header, and acknowledgement overhead) + * + * Tested with Planet AP in 2.4.17, 184 Kbytes/s in UDP in Infrastructure mode, + * 173 Kbytes/s in TCP. + * + * Tested with Planet AP in 2.5.73-bk, 216 Kbytes/s in Infrastructure mode + * with a SMP machine (dual pentium 100), using pktgen, 432 pps (pkt_size = 60) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "wl3501.h" + +#ifndef __i386__ +#define slow_down_io() +#endif + +/* For rough constant delay */ +#define WL3501_NOPLOOP(n) { int x = 0; while (x++ < n) slow_down_io(); } + + + +#define wl3501_outb(a, b) { outb(a, b); slow_down_io(); } +#define wl3501_outb_p(a, b) { outb_p(a, b); slow_down_io(); } +#define wl3501_outsb(a, b, c) { outsb(a, b, c); slow_down_io(); } + +#define WL3501_RELEASE_TIMEOUT (25 * HZ) +#define WL3501_MAX_ADHOC_TRIES 16 + +#define WL3501_RESUME 0 +#define WL3501_SUSPEND 1 + +static int wl3501_config(struct pcmcia_device *link); +static void wl3501_release(struct pcmcia_device *link); + +static const struct { + int reg_domain; + int min, max, deflt; +} iw_channel_table[] = { + { + .reg_domain = IW_REG_DOMAIN_FCC, + .min = 1, + .max = 11, + .deflt = 1, + }, + { + .reg_domain = IW_REG_DOMAIN_DOC, + .min = 1, + .max = 11, + .deflt = 1, + }, + { + .reg_domain = IW_REG_DOMAIN_ETSI, + .min = 1, + .max = 13, + .deflt = 1, + }, + { + .reg_domain = IW_REG_DOMAIN_SPAIN, + .min = 10, + .max = 11, + .deflt = 10, + }, + { + .reg_domain = IW_REG_DOMAIN_FRANCE, + .min = 10, + .max = 13, + .deflt = 10, + }, + { + .reg_domain = IW_REG_DOMAIN_MKK, + .min = 14, + .max = 14, + .deflt = 14, + }, + { + .reg_domain = IW_REG_DOMAIN_MKK1, + .min = 1, + .max = 14, + .deflt = 1, + }, + { + .reg_domain = IW_REG_DOMAIN_ISRAEL, + .min = 3, + .max = 9, + .deflt = 9, + }, +}; + +/** + * iw_valid_channel - validate channel in regulatory domain + * @reg_domain: regulatory domain + * @channel: channel to validate + * + * Returns 0 if invalid in the specified regulatory domain, non-zero if valid. + */ +static int iw_valid_channel(int reg_domain, int channel) +{ + int i, rc = 0; + + for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++) + if (reg_domain == iw_channel_table[i].reg_domain) { + rc = channel >= iw_channel_table[i].min && + channel <= iw_channel_table[i].max; + break; + } + return rc; +} + +/** + * iw_default_channel - get default channel for a regulatory domain + * @reg_domain: regulatory domain + * + * Returns the default channel for a regulatory domain + */ +static int iw_default_channel(int reg_domain) +{ + int i, rc = 1; + + for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++) + if (reg_domain == iw_channel_table[i].reg_domain) { + rc = iw_channel_table[i].deflt; + break; + } + return rc; +} + +static void iw_set_mgmt_info_element(enum iw_mgmt_info_element_ids id, + struct iw_mgmt_info_element *el, + void *value, int len) +{ + el->id = id; + el->len = len; + memcpy(el->data, value, len); +} + +static void iw_copy_mgmt_info_element(struct iw_mgmt_info_element *to, + struct iw_mgmt_info_element *from) +{ + iw_set_mgmt_info_element(from->id, to, from->data, from->len); +} + +static inline void wl3501_switch_page(struct wl3501_card *this, u8 page) +{ + wl3501_outb(page, this->base_addr + WL3501_NIC_BSS); +} + +/* + * Get Ethernet MAC address. + * + * WARNING: We switch to FPAGE0 and switc back again. + * Making sure there is no other WL function beening called by ISR. + */ +static int wl3501_get_flash_mac_addr(struct wl3501_card *this) +{ + int base_addr = this->base_addr; + + /* get MAC addr */ + wl3501_outb(WL3501_BSS_FPAGE3, base_addr + WL3501_NIC_BSS); /* BSS */ + wl3501_outb(0x00, base_addr + WL3501_NIC_LMAL); /* LMAL */ + wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH); /* LMAH */ + + /* wait for reading EEPROM */ + WL3501_NOPLOOP(100); + this->mac_addr[0] = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + this->mac_addr[1] = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + this->mac_addr[2] = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + this->mac_addr[3] = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + this->mac_addr[4] = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + this->mac_addr[5] = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + this->reg_domain = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + wl3501_outb(WL3501_BSS_FPAGE0, base_addr + WL3501_NIC_BSS); + wl3501_outb(0x04, base_addr + WL3501_NIC_LMAL); + wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH); + WL3501_NOPLOOP(100); + this->version[0] = inb(base_addr + WL3501_NIC_IODPA); + WL3501_NOPLOOP(100); + this->version[1] = inb(base_addr + WL3501_NIC_IODPA); + /* switch to SRAM Page 0 (for safety) */ + wl3501_switch_page(this, WL3501_BSS_SPAGE0); + + /* The MAC addr should be 00:60:... */ + return this->mac_addr[0] == 0x00 && this->mac_addr[1] == 0x60; +} + +/** + * wl3501_set_to_wla - Move 'size' bytes from PC to card + * @this: Card + * @dest: Card addressing space + * @src: PC addressing space + * @size: Bytes to move + * + * Move 'size' bytes from PC to card. (Shouldn't be interrupted) + */ +static void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, + int size) +{ + /* switch to SRAM Page 0 */ + wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 : + WL3501_BSS_SPAGE0); + /* set LMAL and LMAH */ + wl3501_outb(dest & 0xff, this->base_addr + WL3501_NIC_LMAL); + wl3501_outb(((dest >> 8) & 0x7f), this->base_addr + WL3501_NIC_LMAH); + + /* rep out to Port A */ + wl3501_outsb(this->base_addr + WL3501_NIC_IODPA, src, size); +} + +/** + * wl3501_get_from_wla - Move 'size' bytes from card to PC + * @this: Card + * @src: Card addressing space + * @dest: PC addressing space + * @size: Bytes to move + * + * Move 'size' bytes from card to PC. (Shouldn't be interrupted) + */ +static void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest, + int size) +{ + /* switch to SRAM Page 0 */ + wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 : + WL3501_BSS_SPAGE0); + /* set LMAL and LMAH */ + wl3501_outb(src & 0xff, this->base_addr + WL3501_NIC_LMAL); + wl3501_outb((src >> 8) & 0x7f, this->base_addr + WL3501_NIC_LMAH); + + /* rep get from Port A */ + insb(this->base_addr + WL3501_NIC_IODPA, dest, size); +} + +/* + * Get/Allocate a free Tx Data Buffer + * + * *--------------*-----------------*----------------------------------* + * | PLCP | MAC Header | DST SRC Data ... | + * | (24 bytes) | (30 bytes) | (6) (6) (Ethernet Row Data) | + * *--------------*-----------------*----------------------------------* + * \ \- IEEE 802.11 -/ \-------------- len --------------/ + * \-struct wl3501_80211_tx_hdr--/ \-------- Ethernet Frame -------/ + * + * Return = Position in Card + */ +static u16 wl3501_get_tx_buffer(struct wl3501_card *this, u16 len) +{ + u16 next, blk_cnt = 0, zero = 0; + u16 full_len = sizeof(struct wl3501_80211_tx_hdr) + len; + u16 ret = 0; + + if (full_len > this->tx_buffer_cnt * 254) + goto out; + ret = this->tx_buffer_head; + while (full_len) { + if (full_len < 254) + full_len = 0; + else + full_len -= 254; + wl3501_get_from_wla(this, this->tx_buffer_head, &next, + sizeof(next)); + if (!full_len) + wl3501_set_to_wla(this, this->tx_buffer_head, &zero, + sizeof(zero)); + this->tx_buffer_head = next; + blk_cnt++; + /* if buffer is not enough */ + if (!next && full_len) { + this->tx_buffer_head = ret; + ret = 0; + goto out; + } + } + this->tx_buffer_cnt -= blk_cnt; +out: + return ret; +} + +/* + * Free an allocated Tx Buffer. ptr must be correct position. + */ +static void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr) +{ + /* check if all space is not free */ + if (!this->tx_buffer_head) + this->tx_buffer_head = ptr; + else + wl3501_set_to_wla(this, this->tx_buffer_tail, + &ptr, sizeof(ptr)); + while (ptr) { + u16 next; + + this->tx_buffer_cnt++; + wl3501_get_from_wla(this, ptr, &next, sizeof(next)); + this->tx_buffer_tail = ptr; + ptr = next; + } +} + +static int wl3501_esbq_req_test(struct wl3501_card *this) +{ + u8 tmp = 0; + + wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp)); + return tmp & 0x80; +} + +static void wl3501_esbq_req(struct wl3501_card *this, u16 *ptr) +{ + u16 tmp = 0; + + wl3501_set_to_wla(this, this->esbq_req_head, ptr, 2); + wl3501_set_to_wla(this, this->esbq_req_head + 2, &tmp, sizeof(tmp)); + this->esbq_req_head += 4; + if (this->esbq_req_head >= this->esbq_req_end) + this->esbq_req_head = this->esbq_req_start; +} + +static int wl3501_esbq_exec(struct wl3501_card *this, void *sig, int sig_size) +{ + int rc = -EIO; + + if (wl3501_esbq_req_test(this)) { + u16 ptr = wl3501_get_tx_buffer(this, sig_size); + if (ptr) { + wl3501_set_to_wla(this, ptr, sig, sig_size); + wl3501_esbq_req(this, &ptr); + rc = 0; + } + } + return rc; +} + +static int wl3501_request_mib(struct wl3501_card *this, u8 index, void *bf) +{ + struct wl3501_get_req sig = { + .sig_id = WL3501_SIG_GET_REQ, + .mib_attrib = index, + }; + unsigned long flags; + int rc = -EIO; + + spin_lock_irqsave(&this->lock, flags); + if (wl3501_esbq_req_test(this)) { + u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig)); + if (ptr) { + wl3501_set_to_wla(this, ptr, &sig, sizeof(sig)); + wl3501_esbq_req(this, &ptr); + this->sig_get_confirm.mib_status = 255; + rc = 0; + } + } + spin_unlock_irqrestore(&this->lock, flags); + + return rc; +} + +static int wl3501_get_mib_value(struct wl3501_card *this, u8 index, + void *bf, int size) +{ + int rc; + + rc = wl3501_request_mib(this, index, bf); + if (rc) + return rc; + + rc = wait_event_interruptible(this->wait, + this->sig_get_confirm.mib_status != 255); + if (rc) + return rc; + + memcpy(bf, this->sig_get_confirm.mib_value, size); + return 0; +} + +static int wl3501_pwr_mgmt(struct wl3501_card *this, int suspend) +{ + struct wl3501_pwr_mgmt_req sig = { + .sig_id = WL3501_SIG_PWR_MGMT_REQ, + .pwr_save = suspend, + .wake_up = !suspend, + .receive_dtims = 10, + }; + unsigned long flags; + int rc = -EIO; + + spin_lock_irqsave(&this->lock, flags); + if (wl3501_esbq_req_test(this)) { + u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig)); + if (ptr) { + wl3501_set_to_wla(this, ptr, &sig, sizeof(sig)); + wl3501_esbq_req(this, &ptr); + this->sig_pwr_mgmt_confirm.status = 255; + spin_unlock_irqrestore(&this->lock, flags); + rc = wait_event_interruptible(this->wait, + this->sig_pwr_mgmt_confirm.status != 255); + printk(KERN_INFO "%s: %s status=%d\n", __func__, + suspend ? "suspend" : "resume", + this->sig_pwr_mgmt_confirm.status); + goto out; + } + } + spin_unlock_irqrestore(&this->lock, flags); +out: + return rc; +} + +/** + * wl3501_send_pkt - Send a packet. + * @this: Card + * @data: Ethernet raw frame. (e.g. data[0] - data[5] is Dest MAC Addr, + * data[6] - data[11] is Src MAC Addr) + * @len: Packet length + * Ref: IEEE 802.11 + */ +static int wl3501_send_pkt(struct wl3501_card *this, u8 *data, u16 len) +{ + u16 bf, sig_bf, next, tmplen, pktlen; + struct wl3501_md_req sig = { + .sig_id = WL3501_SIG_MD_REQ, + }; + size_t sig_addr_len = sizeof(sig.addr); + u8 *pdata = (char *)data; + int rc = -EIO; + + if (wl3501_esbq_req_test(this)) { + sig_bf = wl3501_get_tx_buffer(this, sizeof(sig)); + rc = -ENOMEM; + if (!sig_bf) /* No free buffer available */ + goto out; + bf = wl3501_get_tx_buffer(this, len + 26 + 24); + if (!bf) { + /* No free buffer available */ + wl3501_free_tx_buffer(this, sig_bf); + goto out; + } + rc = 0; + memcpy(&sig.addr, pdata, sig_addr_len); + pktlen = len - sig_addr_len; + pdata += sig_addr_len; + sig.data = bf; + if (((*pdata) * 256 + (*(pdata + 1))) > 1500) { + u8 addr4[ETH_ALEN] = { + [0] = 0xAA, [1] = 0xAA, [2] = 0x03, [4] = 0x00, + }; + + wl3501_set_to_wla(this, bf + 2 + + offsetof(struct wl3501_tx_hdr, addr4), + addr4, sizeof(addr4)); + sig.size = pktlen + 24 + 4 + 6; + if (pktlen > (254 - sizeof(struct wl3501_tx_hdr))) { + tmplen = 254 - sizeof(struct wl3501_tx_hdr); + pktlen -= tmplen; + } else { + tmplen = pktlen; + pktlen = 0; + } + wl3501_set_to_wla(this, + bf + 2 + sizeof(struct wl3501_tx_hdr), + pdata, tmplen); + pdata += tmplen; + wl3501_get_from_wla(this, bf, &next, sizeof(next)); + bf = next; + } else { + sig.size = pktlen + 24 + 4 - 2; + pdata += 2; + pktlen -= 2; + if (pktlen > (254 - sizeof(struct wl3501_tx_hdr) + 6)) { + tmplen = 254 - sizeof(struct wl3501_tx_hdr) + 6; + pktlen -= tmplen; + } else { + tmplen = pktlen; + pktlen = 0; + } + wl3501_set_to_wla(this, bf + 2 + + offsetof(struct wl3501_tx_hdr, addr4), + pdata, tmplen); + pdata += tmplen; + wl3501_get_from_wla(this, bf, &next, sizeof(next)); + bf = next; + } + while (pktlen > 0) { + if (pktlen > 254) { + tmplen = 254; + pktlen -= 254; + } else { + tmplen = pktlen; + pktlen = 0; + } + wl3501_set_to_wla(this, bf + 2, pdata, tmplen); + pdata += tmplen; + wl3501_get_from_wla(this, bf, &next, sizeof(next)); + bf = next; + } + wl3501_set_to_wla(this, sig_bf, &sig, sizeof(sig)); + wl3501_esbq_req(this, &sig_bf); + } +out: + return rc; +} + +static int wl3501_mgmt_resync(struct wl3501_card *this) +{ + struct wl3501_resync_req sig = { + .sig_id = WL3501_SIG_RESYNC_REQ, + }; + + return wl3501_esbq_exec(this, &sig, sizeof(sig)); +} + +static inline int wl3501_fw_bss_type(struct wl3501_card *this) +{ + return this->net_type == IW_MODE_INFRA ? WL3501_NET_TYPE_INFRA : + WL3501_NET_TYPE_ADHOC; +} + +static inline int wl3501_fw_cap_info(struct wl3501_card *this) +{ + return this->net_type == IW_MODE_INFRA ? WL3501_MGMT_CAPABILITY_ESS : + WL3501_MGMT_CAPABILITY_IBSS; +} + +static int wl3501_mgmt_scan(struct wl3501_card *this, u16 chan_time) +{ + struct wl3501_scan_req sig = { + .sig_id = WL3501_SIG_SCAN_REQ, + .scan_type = WL3501_SCAN_TYPE_ACTIVE, + .probe_delay = 0x10, + .min_chan_time = chan_time, + .max_chan_time = chan_time, + .bss_type = wl3501_fw_bss_type(this), + }; + + this->bss_cnt = this->join_sta_bss = 0; + return wl3501_esbq_exec(this, &sig, sizeof(sig)); +} + +static int wl3501_mgmt_join(struct wl3501_card *this, u16 stas) +{ + struct wl3501_join_req sig = { + .sig_id = WL3501_SIG_JOIN_REQ, + .timeout = 10, + .req.ds_pset = { + .el = { + .id = IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET, + .len = 1, + }, + .chan = this->chan, + }, + }; + + memcpy(&sig.req, &this->bss_set[stas].req, sizeof(sig.req)); + return wl3501_esbq_exec(this, &sig, sizeof(sig)); +} + +static int wl3501_mgmt_start(struct wl3501_card *this) +{ + struct wl3501_start_req sig = { + .sig_id = WL3501_SIG_START_REQ, + .beacon_period = 400, + .dtim_period = 1, + .ds_pset = { + .el = { + .id = IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET, + .len = 1, + }, + .chan = this->chan, + }, + .bss_basic_rset = { + .el = { + .id = IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES, + .len = 2, + }, + .data_rate_labels = { + [0] = IW_MGMT_RATE_LABEL_MANDATORY | + IW_MGMT_RATE_LABEL_1MBIT, + [1] = IW_MGMT_RATE_LABEL_MANDATORY | + IW_MGMT_RATE_LABEL_2MBIT, + }, + }, + .operational_rset = { + .el = { + .id = IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES, + .len = 2, + }, + .data_rate_labels = { + [0] = IW_MGMT_RATE_LABEL_MANDATORY | + IW_MGMT_RATE_LABEL_1MBIT, + [1] = IW_MGMT_RATE_LABEL_MANDATORY | + IW_MGMT_RATE_LABEL_2MBIT, + }, + }, + .ibss_pset = { + .el = { + .id = IW_MGMT_INFO_ELEMENT_IBSS_PARAMETER_SET, + .len = 2, + }, + .atim_window = 10, + }, + .bss_type = wl3501_fw_bss_type(this), + .cap_info = wl3501_fw_cap_info(this), + }; + + iw_copy_mgmt_info_element(&sig.ssid.el, &this->essid.el); + iw_copy_mgmt_info_element(&this->keep_essid.el, &this->essid.el); + return wl3501_esbq_exec(this, &sig, sizeof(sig)); +} + +static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr) +{ + u16 i = 0; + int matchflag = 0; + struct wl3501_scan_confirm sig; + + pr_debug("entry"); + wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); + if (sig.status == WL3501_STATUS_SUCCESS) { + pr_debug("success"); + if ((this->net_type == IW_MODE_INFRA && + (sig.req.cap_info & WL3501_MGMT_CAPABILITY_ESS)) || + (this->net_type == IW_MODE_ADHOC && + (sig.req.cap_info & WL3501_MGMT_CAPABILITY_IBSS)) || + this->net_type == IW_MODE_AUTO) { + if (!this->essid.el.len) + matchflag = 1; + else if (this->essid.el.len == 3 && + !memcmp(this->essid.essid, "ANY", 3)) + matchflag = 1; + else if (this->essid.el.len != sig.req.ssid.el.len) + matchflag = 0; + else if (memcmp(this->essid.essid, sig.req.ssid.essid, + this->essid.el.len)) + matchflag = 0; + else + matchflag = 1; + if (matchflag) { + for (i = 0; i < this->bss_cnt; i++) { + if (ether_addr_equal_unaligned(this->bss_set[i].req.bssid, + sig.req.bssid)) { + matchflag = 0; + break; + } + } + } + if (matchflag && (i < 20)) { + memcpy(&this->bss_set[i].req, + &sig.req, sizeof(sig.req)); + this->bss_cnt++; + this->rssi = sig.rssi; + this->bss_set[i].rssi = sig.rssi; + } + } + } else if (sig.status == WL3501_STATUS_TIMEOUT) { + pr_debug("timeout"); + this->join_sta_bss = 0; + for (i = this->join_sta_bss; i < this->bss_cnt; i++) + if (!wl3501_mgmt_join(this, i)) + break; + this->join_sta_bss = i; + if (this->join_sta_bss == this->bss_cnt) { + if (this->net_type == IW_MODE_INFRA) + wl3501_mgmt_scan(this, 100); + else { + this->adhoc_times++; + if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES) + wl3501_mgmt_start(this); + else + wl3501_mgmt_scan(this, 100); + } + } + } +} + +/** + * wl3501_block_interrupt - Mask interrupt from SUTRO + * @this: Card + * + * Mask interrupt from SUTRO. (i.e. SUTRO cannot interrupt the HOST) + * Return: 1 if interrupt is originally enabled + */ +static int wl3501_block_interrupt(struct wl3501_card *this) +{ + u8 old = inb(this->base_addr + WL3501_NIC_GCR); + u8 new = old & (~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC | + WL3501_GCR_ENECINT)); + + wl3501_outb(new, this->base_addr + WL3501_NIC_GCR); + return old & WL3501_GCR_ENECINT; +} + +/** + * wl3501_unblock_interrupt - Enable interrupt from SUTRO + * @this: Card + * + * Enable interrupt from SUTRO. (i.e. SUTRO can interrupt the HOST) + * Return: 1 if interrupt is originally enabled + */ +static int wl3501_unblock_interrupt(struct wl3501_card *this) +{ + u8 old = inb(this->base_addr + WL3501_NIC_GCR); + u8 new = (old & ~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC)) | + WL3501_GCR_ENECINT; + + wl3501_outb(new, this->base_addr + WL3501_NIC_GCR); + return old & WL3501_GCR_ENECINT; +} + +/** + * wl3501_receive - Receive data from Receive Queue. + * + * Receive data from Receive Queue. + * + * @this: card + * @bf: address of host + * @size: size of buffer. + */ +static u16 wl3501_receive(struct wl3501_card *this, u8 *bf, u16 size) +{ + u16 next_addr, next_addr1; + u8 *data = bf + 12; + + size -= 12; + wl3501_get_from_wla(this, this->start_seg + 2, + &next_addr, sizeof(next_addr)); + if (size > WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr)) { + wl3501_get_from_wla(this, + this->start_seg + + sizeof(struct wl3501_rx_hdr), data, + WL3501_BLKSZ - + sizeof(struct wl3501_rx_hdr)); + size -= WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr); + data += WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr); + } else { + wl3501_get_from_wla(this, + this->start_seg + + sizeof(struct wl3501_rx_hdr), + data, size); + size = 0; + } + while (size > 0) { + if (size > WL3501_BLKSZ - 5) { + wl3501_get_from_wla(this, next_addr + 5, data, + WL3501_BLKSZ - 5); + size -= WL3501_BLKSZ - 5; + data += WL3501_BLKSZ - 5; + wl3501_get_from_wla(this, next_addr + 2, &next_addr1, + sizeof(next_addr1)); + next_addr = next_addr1; + } else { + wl3501_get_from_wla(this, next_addr + 5, data, size); + size = 0; + } + } + return 0; +} + +static void wl3501_esbq_req_free(struct wl3501_card *this) +{ + u8 tmp; + u16 addr; + + if (this->esbq_req_head == this->esbq_req_tail) + goto out; + wl3501_get_from_wla(this, this->esbq_req_tail + 3, &tmp, sizeof(tmp)); + if (!(tmp & 0x80)) + goto out; + wl3501_get_from_wla(this, this->esbq_req_tail, &addr, sizeof(addr)); + wl3501_free_tx_buffer(this, addr); + this->esbq_req_tail += 4; + if (this->esbq_req_tail >= this->esbq_req_end) + this->esbq_req_tail = this->esbq_req_start; +out: + return; +} + +static int wl3501_esbq_confirm(struct wl3501_card *this) +{ + u8 tmp; + + wl3501_get_from_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp)); + return tmp & 0x80; +} + +static void wl3501_online(struct net_device *dev) +{ + struct wl3501_card *this = netdev_priv(dev); + + printk(KERN_INFO "%s: Wireless LAN online. BSSID: %pM\n", + dev->name, this->bssid); + netif_wake_queue(dev); +} + +static void wl3501_esbq_confirm_done(struct wl3501_card *this) +{ + u8 tmp = 0; + + wl3501_set_to_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp)); + this->esbq_confirm += 4; + if (this->esbq_confirm >= this->esbq_confirm_end) + this->esbq_confirm = this->esbq_confirm_start; +} + +static int wl3501_mgmt_auth(struct wl3501_card *this) +{ + struct wl3501_auth_req sig = { + .sig_id = WL3501_SIG_AUTH_REQ, + .type = WL3501_SYS_TYPE_OPEN, + .timeout = 1000, + }; + + pr_debug("entry"); + memcpy(sig.mac_addr, this->bssid, ETH_ALEN); + return wl3501_esbq_exec(this, &sig, sizeof(sig)); +} + +static int wl3501_mgmt_association(struct wl3501_card *this) +{ + struct wl3501_assoc_req sig = { + .sig_id = WL3501_SIG_ASSOC_REQ, + .timeout = 1000, + .listen_interval = 5, + .cap_info = this->cap_info, + }; + + pr_debug("entry"); + memcpy(sig.mac_addr, this->bssid, ETH_ALEN); + return wl3501_esbq_exec(this, &sig, sizeof(sig)); +} + +static void wl3501_mgmt_join_confirm(struct net_device *dev, u16 addr) +{ + struct wl3501_card *this = netdev_priv(dev); + struct wl3501_join_confirm sig; + + pr_debug("entry"); + wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); + if (sig.status == WL3501_STATUS_SUCCESS) { + if (this->net_type == IW_MODE_INFRA) { + if (this->join_sta_bss < this->bss_cnt) { + const int i = this->join_sta_bss; + memcpy(this->bssid, + this->bss_set[i].req.bssid, ETH_ALEN); + this->chan = this->bss_set[i].req.ds_pset.chan; + iw_copy_mgmt_info_element(&this->keep_essid.el, + &this->bss_set[i].req.ssid.el); + wl3501_mgmt_auth(this); + } + } else { + const int i = this->join_sta_bss; + + memcpy(&this->bssid, &this->bss_set[i].req.bssid, ETH_ALEN); + this->chan = this->bss_set[i].req.ds_pset.chan; + iw_copy_mgmt_info_element(&this->keep_essid.el, + &this->bss_set[i].req.ssid.el); + wl3501_online(dev); + } + } else { + int i; + this->join_sta_bss++; + for (i = this->join_sta_bss; i < this->bss_cnt; i++) + if (!wl3501_mgmt_join(this, i)) + break; + this->join_sta_bss = i; + if (this->join_sta_bss == this->bss_cnt) { + if (this->net_type == IW_MODE_INFRA) + wl3501_mgmt_scan(this, 100); + else { + this->adhoc_times++; + if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES) + wl3501_mgmt_start(this); + else + wl3501_mgmt_scan(this, 100); + } + } + } +} + +static inline void wl3501_alarm_interrupt(struct net_device *dev, + struct wl3501_card *this) +{ + if (this->net_type == IW_MODE_INFRA) { + printk(KERN_INFO "Wireless LAN offline\n"); + netif_stop_queue(dev); + wl3501_mgmt_resync(this); + } +} + +static inline void wl3501_md_confirm_interrupt(struct net_device *dev, + struct wl3501_card *this, + u16 addr) +{ + struct wl3501_md_confirm sig; + + pr_debug("entry"); + wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); + wl3501_free_tx_buffer(this, sig.data); + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +} + +static inline void wl3501_md_ind_interrupt(struct net_device *dev, + struct wl3501_card *this, u16 addr) +{ + struct wl3501_md_ind sig; + struct sk_buff *skb; + u8 rssi, addr4[ETH_ALEN]; + u16 pkt_len; + + wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); + this->start_seg = sig.data; + wl3501_get_from_wla(this, + sig.data + offsetof(struct wl3501_rx_hdr, rssi), + &rssi, sizeof(rssi)); + this->rssi = rssi <= 63 ? (rssi * 100) / 64 : 255; + + wl3501_get_from_wla(this, + sig.data + + offsetof(struct wl3501_rx_hdr, addr4), + &addr4, sizeof(addr4)); + if (!(addr4[0] == 0xAA && addr4[1] == 0xAA && + addr4[2] == 0x03 && addr4[4] == 0x00)) { + printk(KERN_INFO "Unsupported packet type!\n"); + return; + } + pkt_len = sig.size + 12 - 24 - 4 - 6; + + skb = dev_alloc_skb(pkt_len + 5); + + if (!skb) { + printk(KERN_WARNING "%s: Can't alloc a sk_buff of size %d.\n", + dev->name, pkt_len); + dev->stats.rx_dropped++; + } else { + skb->dev = dev; + skb_reserve(skb, 2); /* IP headers on 16 bytes boundaries */ + skb_copy_to_linear_data(skb, (unsigned char *)&sig.addr, + sizeof(sig.addr)); + wl3501_receive(this, skb->data, pkt_len); + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, dev); + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + netif_rx(skb); + } +} + +static inline void wl3501_get_confirm_interrupt(struct wl3501_card *this, + u16 addr, void *sig, int size) +{ + pr_debug("entry"); + wl3501_get_from_wla(this, addr, &this->sig_get_confirm, + sizeof(this->sig_get_confirm)); + wake_up(&this->wait); +} + +static inline void wl3501_start_confirm_interrupt(struct net_device *dev, + struct wl3501_card *this, + u16 addr) +{ + struct wl3501_start_confirm sig; + + pr_debug("entry"); + wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); + if (sig.status == WL3501_STATUS_SUCCESS) + netif_wake_queue(dev); +} + +static inline void wl3501_assoc_confirm_interrupt(struct net_device *dev, + u16 addr) +{ + struct wl3501_card *this = netdev_priv(dev); + struct wl3501_assoc_confirm sig; + + pr_debug("entry"); + wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); + + if (sig.status == WL3501_STATUS_SUCCESS) + wl3501_online(dev); +} + +static inline void wl3501_auth_confirm_interrupt(struct wl3501_card *this, + u16 addr) +{ + struct wl3501_auth_confirm sig; + + pr_debug("entry"); + wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); + + if (sig.status == WL3501_STATUS_SUCCESS) + wl3501_mgmt_association(this); + else + wl3501_mgmt_resync(this); +} + +static inline void wl3501_rx_interrupt(struct net_device *dev) +{ + int morepkts; + u16 addr; + u8 sig_id; + struct wl3501_card *this = netdev_priv(dev); + + pr_debug("entry"); +loop: + morepkts = 0; + if (!wl3501_esbq_confirm(this)) + goto free; + wl3501_get_from_wla(this, this->esbq_confirm, &addr, sizeof(addr)); + wl3501_get_from_wla(this, addr + 2, &sig_id, sizeof(sig_id)); + + switch (sig_id) { + case WL3501_SIG_DEAUTH_IND: + case WL3501_SIG_DISASSOC_IND: + case WL3501_SIG_ALARM: + wl3501_alarm_interrupt(dev, this); + break; + case WL3501_SIG_MD_CONFIRM: + wl3501_md_confirm_interrupt(dev, this, addr); + break; + case WL3501_SIG_MD_IND: + wl3501_md_ind_interrupt(dev, this, addr); + break; + case WL3501_SIG_GET_CONFIRM: + wl3501_get_confirm_interrupt(this, addr, + &this->sig_get_confirm, + sizeof(this->sig_get_confirm)); + break; + case WL3501_SIG_PWR_MGMT_CONFIRM: + wl3501_get_confirm_interrupt(this, addr, + &this->sig_pwr_mgmt_confirm, + sizeof(this->sig_pwr_mgmt_confirm)); + break; + case WL3501_SIG_START_CONFIRM: + wl3501_start_confirm_interrupt(dev, this, addr); + break; + case WL3501_SIG_SCAN_CONFIRM: + wl3501_mgmt_scan_confirm(this, addr); + break; + case WL3501_SIG_JOIN_CONFIRM: + wl3501_mgmt_join_confirm(dev, addr); + break; + case WL3501_SIG_ASSOC_CONFIRM: + wl3501_assoc_confirm_interrupt(dev, addr); + break; + case WL3501_SIG_AUTH_CONFIRM: + wl3501_auth_confirm_interrupt(this, addr); + break; + case WL3501_SIG_RESYNC_CONFIRM: + wl3501_mgmt_resync(this); /* FIXME: should be resync_confirm */ + break; + } + wl3501_esbq_confirm_done(this); + morepkts = 1; + /* free request if necessary */ +free: + wl3501_esbq_req_free(this); + if (morepkts) + goto loop; +} + +static inline void wl3501_ack_interrupt(struct wl3501_card *this) +{ + wl3501_outb(WL3501_GCR_ECINT, this->base_addr + WL3501_NIC_GCR); +} + +/** + * wl3501_interrupt - Hardware interrupt from card. + * @irq: Interrupt number + * @dev_id: net_device + * + * We must acknowledge the interrupt as soon as possible, and block the + * interrupt from the same card immediately to prevent re-entry. + * + * Before accessing the Control_Status_Block, we must lock SUTRO first. + * On the other hand, to prevent SUTRO from malfunctioning, we must + * unlock the SUTRO as soon as possible. + */ +static irqreturn_t wl3501_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct wl3501_card *this; + + this = netdev_priv(dev); + spin_lock(&this->lock); + wl3501_ack_interrupt(this); + wl3501_block_interrupt(this); + wl3501_rx_interrupt(dev); + wl3501_unblock_interrupt(this); + spin_unlock(&this->lock); + + return IRQ_HANDLED; +} + +static int wl3501_reset_board(struct wl3501_card *this) +{ + u8 tmp = 0; + int i, rc = 0; + + /* Coreset */ + wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR); + wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR); + wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR); + + /* Reset SRAM 0x480 to zero */ + wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp)); + + /* Start up */ + wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR); + + WL3501_NOPLOOP(1024 * 50); + + wl3501_unblock_interrupt(this); /* acme: was commented */ + + /* Polling Self_Test_Status */ + for (i = 0; i < 10000; i++) { + wl3501_get_from_wla(this, 0x480, &tmp, sizeof(tmp)); + + if (tmp == 'W') { + /* firmware complete all test successfully */ + tmp = 'A'; + wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp)); + goto out; + } + WL3501_NOPLOOP(10); + } + printk(KERN_WARNING "%s: failed to reset the board!\n", __func__); + rc = -ENODEV; +out: + return rc; +} + +static int wl3501_init_firmware(struct wl3501_card *this) +{ + u16 ptr, next; + int rc = wl3501_reset_board(this); + + if (rc) + goto fail; + this->card_name[0] = '\0'; + wl3501_get_from_wla(this, 0x1a00, + this->card_name, sizeof(this->card_name)); + this->card_name[sizeof(this->card_name) - 1] = '\0'; + this->firmware_date[0] = '\0'; + wl3501_get_from_wla(this, 0x1a40, + this->firmware_date, sizeof(this->firmware_date)); + this->firmware_date[sizeof(this->firmware_date) - 1] = '\0'; + /* Switch to SRAM Page 0 */ + wl3501_switch_page(this, WL3501_BSS_SPAGE0); + /* Read parameter from card */ + wl3501_get_from_wla(this, 0x482, &this->esbq_req_start, 2); + wl3501_get_from_wla(this, 0x486, &this->esbq_req_end, 2); + wl3501_get_from_wla(this, 0x488, &this->esbq_confirm_start, 2); + wl3501_get_from_wla(this, 0x48c, &this->esbq_confirm_end, 2); + wl3501_get_from_wla(this, 0x48e, &this->tx_buffer_head, 2); + wl3501_get_from_wla(this, 0x492, &this->tx_buffer_size, 2); + this->esbq_req_tail = this->esbq_req_head = this->esbq_req_start; + this->esbq_req_end += this->esbq_req_start; + this->esbq_confirm = this->esbq_confirm_start; + this->esbq_confirm_end += this->esbq_confirm_start; + /* Initial Tx Buffer */ + this->tx_buffer_cnt = 1; + ptr = this->tx_buffer_head; + next = ptr + WL3501_BLKSZ; + while ((next - this->tx_buffer_head) < this->tx_buffer_size) { + this->tx_buffer_cnt++; + wl3501_set_to_wla(this, ptr, &next, sizeof(next)); + ptr = next; + next = ptr + WL3501_BLKSZ; + } + rc = 0; + next = 0; + wl3501_set_to_wla(this, ptr, &next, sizeof(next)); + this->tx_buffer_tail = ptr; +out: + return rc; +fail: + printk(KERN_WARNING "%s: failed!\n", __func__); + goto out; +} + +static int wl3501_close(struct net_device *dev) +{ + struct wl3501_card *this = netdev_priv(dev); + unsigned long flags; + struct pcmcia_device *link; + link = this->p_dev; + + spin_lock_irqsave(&this->lock, flags); + link->open--; + + /* Stop wl3501_hard_start_xmit() from now on */ + netif_stop_queue(dev); + wl3501_ack_interrupt(this); + + /* Mask interrupts from the SUTRO */ + wl3501_block_interrupt(this); + + printk(KERN_INFO "%s: WL3501 closed\n", dev->name); + spin_unlock_irqrestore(&this->lock, flags); + return 0; +} + +/** + * wl3501_reset - Reset the SUTRO. + * @dev: network device + * + * It is almost the same as wl3501_open(). In fact, we may just wl3501_close() + * and wl3501_open() again, but I wouldn't like to free_irq() when the driver + * is running. It seems to be dangerous. + */ +static int wl3501_reset(struct net_device *dev) +{ + struct wl3501_card *this = netdev_priv(dev); + int rc = -ENODEV; + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + wl3501_block_interrupt(this); + + if (wl3501_init_firmware(this)) { + printk(KERN_WARNING "%s: Can't initialize Firmware!\n", + dev->name); + /* Free IRQ, and mark IRQ as unused */ + free_irq(dev->irq, dev); + goto out; + } + + /* + * Queue has to be started only when the Card is Started + */ + netif_stop_queue(dev); + this->adhoc_times = 0; + wl3501_ack_interrupt(this); + wl3501_unblock_interrupt(this); + wl3501_mgmt_scan(this, 100); + pr_debug("%s: device reset", dev->name); + rc = 0; +out: + spin_unlock_irqrestore(&this->lock, flags); + return rc; +} + +static void wl3501_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct net_device_stats *stats = &dev->stats; + int rc; + + stats->tx_errors++; + rc = wl3501_reset(dev); + if (rc) + printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n", + dev->name, rc); + else { + netif_trans_update(dev); /* prevent tx timeout */ + netif_wake_queue(dev); + } +} + +/* + * Return : 0 - OK + * 1 - Could not transmit (dev_queue_xmit will queue it) + * and try to sent it later + */ +static netdev_tx_t wl3501_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + int enabled, rc; + struct wl3501_card *this = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + enabled = wl3501_block_interrupt(this); + rc = wl3501_send_pkt(this, skb->data, skb->len); + if (enabled) + wl3501_unblock_interrupt(this); + if (rc) { + ++dev->stats.tx_dropped; + netif_stop_queue(dev); + } else { + ++dev->stats.tx_packets; + dev->stats.tx_bytes += skb->len; + dev_kfree_skb_irq(skb); + + if (this->tx_buffer_cnt < 2) + netif_stop_queue(dev); + } + spin_unlock_irqrestore(&this->lock, flags); + return NETDEV_TX_OK; +} + +static int wl3501_open(struct net_device *dev) +{ + int rc = -ENODEV; + struct wl3501_card *this = netdev_priv(dev); + unsigned long flags; + struct pcmcia_device *link; + link = this->p_dev; + + spin_lock_irqsave(&this->lock, flags); + if (!pcmcia_dev_present(link)) + goto out; + netif_device_attach(dev); + link->open++; + + /* Initial WL3501 firmware */ + pr_debug("%s: Initialize WL3501 firmware...", dev->name); + if (wl3501_init_firmware(this)) + goto fail; + /* Initial device variables */ + this->adhoc_times = 0; + /* Acknowledge Interrupt, for cleaning last state */ + wl3501_ack_interrupt(this); + + /* Enable interrupt from card after all */ + wl3501_unblock_interrupt(this); + wl3501_mgmt_scan(this, 100); + rc = 0; + pr_debug("%s: WL3501 opened", dev->name); + printk(KERN_INFO "%s: Card Name: %s\n" + "%s: Firmware Date: %s\n", + dev->name, this->card_name, + dev->name, this->firmware_date); +out: + spin_unlock_irqrestore(&this->lock, flags); + return rc; +fail: + printk(KERN_WARNING "%s: Can't initialize firmware!\n", dev->name); + goto out; +} + +static struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev) +{ + struct wl3501_card *this = netdev_priv(dev); + struct iw_statistics *wstats = &this->wstats; + u32 value; /* size checked: it is u32 */ + + memset(wstats, 0, sizeof(*wstats)); + wstats->status = netif_running(dev); + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_ICV_ERROR_COUNT, + &value, sizeof(value))) + wstats->discard.code += value; + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_UNDECRYPTABLE_COUNT, + &value, sizeof(value))) + wstats->discard.code += value; + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_EXCLUDED_COUNT, + &value, sizeof(value))) + wstats->discard.code += value; + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RETRY_COUNT, + &value, sizeof(value))) + wstats->discard.retries = value; + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FAILED_COUNT, + &value, sizeof(value))) + wstats->discard.misc += value; + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_FAILURE_COUNT, + &value, sizeof(value))) + wstats->discard.misc += value; + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_ACK_FAILURE_COUNT, + &value, sizeof(value))) + wstats->discard.misc += value; + if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAME_DUPLICATE_COUNT, + &value, sizeof(value))) + wstats->discard.misc += value; + return wstats; +} + +/** + * wl3501_detach - deletes a driver "instance" + * @link: FILL_IN + * + * This deletes a driver "instance". The device is de-registered with Card + * Services. If it has been released, all local data structures are freed. + * Otherwise, the structures will be freed when the device is released. + */ +static void wl3501_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + /* If the device is currently configured and active, we won't actually + * delete it yet. Instead, it is marked so that when the release() + * function is called, that will trigger a proper detach(). */ + + while (link->open > 0) + wl3501_close(dev); + + netif_device_detach(dev); + wl3501_release(link); + + unregister_netdev(dev); + free_netdev(dev); +} + +static int wl3501_get_name(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + strscpy(wrqu->name, "IEEE 802.11-DS", sizeof(wrqu->name)); + return 0; +} + +static int wl3501_set_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + int channel = wrqu->freq.m; + int rc = -EINVAL; + + if (iw_valid_channel(this->reg_domain, channel)) { + this->chan = channel; + rc = wl3501_reset(dev); + } + return rc; +} + +static int wl3501_get_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + + wrqu->freq.m = 100000 * + ieee80211_channel_to_frequency(this->chan, NL80211_BAND_2GHZ); + wrqu->freq.e = 1; + return 0; +} + +static int wl3501_set_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int rc = -EINVAL; + + if (wrqu->mode == IW_MODE_INFRA || + wrqu->mode == IW_MODE_ADHOC || + wrqu->mode == IW_MODE_AUTO) { + struct wl3501_card *this = netdev_priv(dev); + + this->net_type = wrqu->mode; + rc = wl3501_reset(dev); + } + return rc; +} + +static int wl3501_get_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + + wrqu->mode = this->net_type; + return 0; +} + +static int wl3501_get_sens(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + + wrqu->sens.value = this->rssi; + wrqu->sens.disabled = !wrqu->sens.value; + wrqu->sens.fixed = 1; + return 0; +} + +static int wl3501_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + + /* Set the length (very important for backward compatibility) */ + wrqu->data.length = sizeof(*range); + + /* Set all the info we don't care or don't know about to zero */ + memset(range, 0, sizeof(*range)); + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 1; + range->throughput = 2 * 1000 * 1000; /* ~2 Mb/s */ + /* FIXME: study the code to fill in more fields... */ + return 0; +} + +static int wl3501_set_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + int rc = -EINVAL; + + /* FIXME: we support other ARPHRDs...*/ + if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) + goto out; + if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data)) { + /* FIXME: rescan? */ + } else + memcpy(this->bssid, wrqu->ap_addr.sa_data, ETH_ALEN); + /* FIXME: rescan? deassoc & scan? */ + rc = 0; +out: + return rc; +} + +static int wl3501_get_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu->ap_addr.sa_data, this->bssid, ETH_ALEN); + return 0; +} + +static int wl3501_set_scan(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * FIXME: trigger scanning with a reset, yes, I'm lazy + */ + return wl3501_reset(dev); +} + +static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + int i; + char *current_ev = extra; + struct iw_event iwe; + + for (i = 0; i < this->bss_cnt; ++i) { + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, this->bss_set[i].req.bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_ADDR_LEN); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.u.data.length = this->bss_set[i].req.ssid.el.len; + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, + this->bss_set[i].req.ssid.essid); + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = this->bss_set[i].req.bss_type; + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_UINT_LEN); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = this->bss_set[i].req.ds_pset.chan; + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_FREQ_LEN); + iwe.cmd = SIOCGIWENCODE; + if (this->bss_set[i].req.cap_info & WL3501_MGMT_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, NULL); + } + /* Length of data */ + wrqu->data.length = (current_ev - extra); + wrqu->data.flags = 0; /* FIXME: set properly these flags */ + return 0; +} + +static int wl3501_set_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + + if (wrqu->data.flags) { + iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID, + &this->essid.el, + extra, wrqu->data.length); + } else { /* We accept any ESSID */ + iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID, + &this->essid.el, "ANY", 3); + } + return wl3501_reset(dev); +} + +static int wl3501_get_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + wrqu->essid.flags = 1; + wrqu->essid.length = this->essid.el.len; + memcpy(extra, this->essid.essid, this->essid.el.len); + spin_unlock_irqrestore(&this->lock, flags); + return 0; +} + +static int wl3501_set_nick(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + + if (wrqu->data.length > sizeof(this->nick)) + return -E2BIG; + strscpy(this->nick, extra, wrqu->data.length); + return 0; +} + +static int wl3501_get_nick(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct wl3501_card *this = netdev_priv(dev); + + strscpy(extra, this->nick, 32); + wrqu->data.length = strlen(extra); + return 0; +} + +static int wl3501_get_rate(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * FIXME: have to see from where to get this info, perhaps this card + * works at 1 Mbit/s too... for now leave at 2 Mbit/s that is the most + * common with the Planet Access Points. -acme + */ + wrqu->bitrate.value = 2000000; + wrqu->bitrate.fixed = 1; + return 0; +} + +static int wl3501_get_rts_threshold(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u16 threshold; /* size checked: it is u16 */ + struct wl3501_card *this = netdev_priv(dev); + int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_THRESHOLD, + &threshold, sizeof(threshold)); + if (!rc) { + wrqu->rts.value = threshold; + wrqu->rts.disabled = threshold >= 2347; + wrqu->rts.fixed = 1; + } + return rc; +} + +static int wl3501_get_frag_threshold(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u16 threshold; /* size checked: it is u16 */ + struct wl3501_card *this = netdev_priv(dev); + int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAG_THRESHOLD, + &threshold, sizeof(threshold)); + if (!rc) { + wrqu->frag.value = threshold; + wrqu->frag.disabled = threshold >= 2346; + wrqu->frag.fixed = 1; + } + return rc; +} + +static int wl3501_get_txpow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u16 txpow; + struct wl3501_card *this = netdev_priv(dev); + int rc = wl3501_get_mib_value(this, + WL3501_MIB_ATTR_CURRENT_TX_PWR_LEVEL, + &txpow, sizeof(txpow)); + if (!rc) { + wrqu->txpower.value = txpow; + wrqu->txpower.disabled = 0; + /* + * From the MIB values I think this can be configurable, + * as it lists several tx power levels -acme + */ + wrqu->txpower.fixed = 0; + wrqu->txpower.flags = IW_TXPOW_MWATT; + } + return rc; +} + +static int wl3501_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u8 retry; /* size checked: it is u8 */ + struct wl3501_card *this = netdev_priv(dev); + int rc = wl3501_get_mib_value(this, + WL3501_MIB_ATTR_LONG_RETRY_LIMIT, + &retry, sizeof(retry)); + if (rc) + goto out; + if (wrqu->retry.flags & IW_RETRY_LONG) { + wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + goto set_value; + } + rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_SHORT_RETRY_LIMIT, + &retry, sizeof(retry)); + if (rc) + goto out; + wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; +set_value: + wrqu->retry.value = retry; + wrqu->retry.disabled = 0; +out: + return rc; +} + +static int wl3501_get_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u8 implemented, restricted, keys[100], len_keys, tocopy; + struct wl3501_card *this = netdev_priv(dev); + int rc = wl3501_get_mib_value(this, + WL3501_MIB_ATTR_PRIV_OPT_IMPLEMENTED, + &implemented, sizeof(implemented)); + if (rc) + goto out; + if (!implemented) { + wrqu->encoding.flags = IW_ENCODE_DISABLED; + goto out; + } + rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_EXCLUDE_UNENCRYPTED, + &restricted, sizeof(restricted)); + if (rc) + goto out; + wrqu->encoding.flags = restricted ? IW_ENCODE_RESTRICTED : + IW_ENCODE_OPEN; + rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS_LEN, + &len_keys, sizeof(len_keys)); + if (rc) + goto out; + rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS, + keys, len_keys); + if (rc) + goto out; + tocopy = min_t(u16, len_keys, wrqu->encoding.length); + tocopy = min_t(u8, tocopy, 100); + wrqu->encoding.length = tocopy; + memcpy(extra, keys, tocopy); +out: + return rc; +} + +static int wl3501_get_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u8 pwr_state; + struct wl3501_card *this = netdev_priv(dev); + int rc = wl3501_get_mib_value(this, + WL3501_MIB_ATTR_CURRENT_PWR_STATE, + &pwr_state, sizeof(pwr_state)); + if (rc) + goto out; + wrqu->power.disabled = !pwr_state; + wrqu->power.flags = IW_POWER_ON; +out: + return rc; +} + +static const iw_handler wl3501_handler[] = { + IW_HANDLER(SIOCGIWNAME, wl3501_get_name), + IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq), + IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq), + IW_HANDLER(SIOCSIWMODE, wl3501_set_mode), + IW_HANDLER(SIOCGIWMODE, wl3501_get_mode), + IW_HANDLER(SIOCGIWSENS, wl3501_get_sens), + IW_HANDLER(SIOCGIWRANGE, wl3501_get_range), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, wl3501_set_wap), + IW_HANDLER(SIOCGIWAP, wl3501_get_wap), + IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan), + IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan), + IW_HANDLER(SIOCSIWESSID, wl3501_set_essid), + IW_HANDLER(SIOCGIWESSID, wl3501_get_essid), + IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick), + IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick), + IW_HANDLER(SIOCGIWRATE, wl3501_get_rate), + IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold), + IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold), + IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow), + IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry), + IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode), + IW_HANDLER(SIOCGIWPOWER, wl3501_get_power), +}; + +static const struct iw_handler_def wl3501_handler_def = { + .num_standard = ARRAY_SIZE(wl3501_handler), + .standard = (iw_handler *)wl3501_handler, + .get_wireless_stats = wl3501_get_wireless_stats, +}; + +static const struct net_device_ops wl3501_netdev_ops = { + .ndo_open = wl3501_open, + .ndo_stop = wl3501_close, + .ndo_start_xmit = wl3501_hard_start_xmit, + .ndo_tx_timeout = wl3501_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int wl3501_probe(struct pcmcia_device *p_dev) +{ + struct net_device *dev; + struct wl3501_card *this; + + /* The io structure describes IO port mapping */ + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags = IO_DATA_PATH_WIDTH_8; + + /* General socket configuration */ + p_dev->config_flags = CONF_ENABLE_IRQ; + p_dev->config_index = 1; + + dev = alloc_etherdev(sizeof(struct wl3501_card)); + if (!dev) + goto out_link; + + + dev->netdev_ops = &wl3501_netdev_ops; + dev->watchdog_timeo = 5 * HZ; + + this = netdev_priv(dev); + this->wireless_data.spy_data = &this->spy_data; + this->p_dev = p_dev; + dev->wireless_data = &this->wireless_data; + dev->wireless_handlers = &wl3501_handler_def; + netif_stop_queue(dev); + p_dev->priv = dev; + + return wl3501_config(p_dev); +out_link: + return -ENOMEM; +} + +static int wl3501_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + int i = 0, j, ret; + struct wl3501_card *this; + + /* Try allocating IO ports. This tries a few fixed addresses. If you + * want, you can also read the card's config table to pick addresses -- + * see the serial driver for an example. */ + link->io_lines = 5; + + for (j = 0x280; j < 0x400; j += 0x20) { + /* The '^0x300' is so that we probe 0x300-0x3ff first, then + * 0x200-0x2ff, and so on, because this seems safer */ + link->resource[0]->start = j; + link->resource[1]->start = link->resource[0]->start + 0x10; + i = pcmcia_request_io(link); + if (i == 0) + break; + } + if (i != 0) + goto failed; + + /* Now allocate an interrupt line. Note that this does not actually + * assign a handler to the interrupt. */ + + ret = pcmcia_request_irq(link, wl3501_interrupt); + if (ret) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + SET_NETDEV_DEV(dev, &link->dev); + if (register_netdev(dev)) { + printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n"); + goto failed; + } + + this = netdev_priv(dev); + + this->base_addr = dev->base_addr; + + if (!wl3501_get_flash_mac_addr(this)) { + printk(KERN_WARNING "%s: Can't read MAC addr in flash ROM?\n", + dev->name); + unregister_netdev(dev); + goto failed; + } + + eth_hw_addr_set(dev, this->mac_addr); + + /* print probe information */ + printk(KERN_INFO "%s: wl3501 @ 0x%3.3x, IRQ %d, " + "MAC addr in flash ROM:%pM\n", + dev->name, this->base_addr, (int)dev->irq, + dev->dev_addr); + /* + * Initialize card parameters - added by jss + */ + this->net_type = IW_MODE_INFRA; + this->bss_cnt = 0; + this->join_sta_bss = 0; + this->adhoc_times = 0; + iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID, &this->essid.el, + "ANY", 3); + this->card_name[0] = '\0'; + this->firmware_date[0] = '\0'; + this->rssi = 255; + this->chan = iw_default_channel(this->reg_domain); + strscpy(this->nick, "Planet WL3501", sizeof(this->nick)); + spin_lock_init(&this->lock); + init_waitqueue_head(&this->wait); + netif_start_queue(dev); + return 0; + +failed: + wl3501_release(link); + return -ENODEV; +} + +static void wl3501_release(struct pcmcia_device *link) +{ + pcmcia_disable_device(link); +} + +static int wl3501_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + wl3501_pwr_mgmt(netdev_priv(dev), WL3501_SUSPEND); + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int wl3501_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + wl3501_pwr_mgmt(netdev_priv(dev), WL3501_RESUME); + if (link->open) { + wl3501_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + + +static const struct pcmcia_device_id wl3501_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0001), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, wl3501_ids); + +static struct pcmcia_driver wl3501_driver = { + .owner = THIS_MODULE, + .name = "wl3501_cs", + .probe = wl3501_probe, + .remove = wl3501_detach, + .id_table = wl3501_ids, + .suspend = wl3501_suspend, + .resume = wl3501_resume, +}; +module_pcmcia_driver(wl3501_driver); + +MODULE_AUTHOR("Fox Chen , " + "Arnaldo Carvalho de Melo ," + "Gustavo Niemeyer "); +MODULE_DESCRIPTION("Planet wl3501 wireless driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c deleted file mode 100644 index 1f57a0055bbd..000000000000 --- a/drivers/net/wireless/ray_cs.c +++ /dev/null @@ -1,2819 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/*============================================================================= - * - * A PCMCIA client driver for the Raylink wireless LAN card. - * The starting point for this module was the skeleton.c in the - * PCMCIA 2.9.12 package written by David Hinds, dahinds@users.sourceforge.net - * - * Copyright (c) 1998 Corey Thomas (corey@world.std.com) - * - * Changes: - * Arnaldo Carvalho de Melo - 08/08/2000 - * - reorganize kmallocs in ray_attach, checking all for failure - * and releasing the previous allocations if one fails - * - * Daniele Bellucci - 07/10/2003 - * - Audit copy_to_user in ioctl(SIOCGIWESSID) - * -=============================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include - -/* Warning : these stuff will slow down the driver... */ -#define WIRELESS_SPY /* Enable spying addresses */ -/* Definitions we need for spy */ -typedef struct iw_statistics iw_stats; -typedef u_char mac_addr[ETH_ALEN]; /* Hardware address */ - -#include "rayctl.h" -#include "ray_cs.h" - - -/** Prototypes based on PCMCIA skeleton driver *******************************/ -static int ray_config(struct pcmcia_device *link); -static void ray_release(struct pcmcia_device *link); -static void ray_detach(struct pcmcia_device *p_dev); - -/***** Prototypes indicated by device structure ******************************/ -static int ray_dev_close(struct net_device *dev); -static int ray_dev_config(struct net_device *dev, struct ifmap *map); -static struct net_device_stats *ray_get_stats(struct net_device *dev); -static int ray_dev_init(struct net_device *dev); - -static int ray_open(struct net_device *dev); -static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void ray_update_multi_list(struct net_device *dev, int all); -static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx, - unsigned char *data, int len); -static void ray_build_header(ray_dev_t *local, struct tx_msg __iomem *ptx, - UCHAR msg_type, unsigned char *data); -static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len); -static iw_stats *ray_get_wireless_stats(struct net_device *dev); -static const struct iw_handler_def ray_handler_def; - -/***** Prototypes for raylink functions **************************************/ -static void authenticate(ray_dev_t *local); -static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type); -static void authenticate_timeout(struct timer_list *t); -static int get_free_ccs(ray_dev_t *local); -static int get_free_tx_ccs(ray_dev_t *local); -static void init_startup_params(ray_dev_t *local); -static int parse_addr(char *in_str, UCHAR *out); -static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev, UCHAR type); -static int ray_init(struct net_device *dev); -static int interrupt_ecf(ray_dev_t *local, int ccs); -static void ray_reset(struct net_device *dev); -static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len); -static void verify_dl_startup(struct timer_list *t); - -/* Prototypes for interrpt time functions **********************************/ -static irqreturn_t ray_interrupt(int reg, void *dev_id); -static void clear_interrupt(ray_dev_t *local); -static void rx_deauthenticate(ray_dev_t *local, struct rcs __iomem *prcs, - unsigned int pkt_addr, int rx_len); -static int copy_from_rx_buff(ray_dev_t *local, UCHAR *dest, int pkt_addr, int len); -static void ray_rx(struct net_device *dev, ray_dev_t *local, struct rcs __iomem *prcs); -static void release_frag_chain(ray_dev_t *local, struct rcs __iomem *prcs); -static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs, - unsigned int pkt_addr, int rx_len); -static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, - unsigned int pkt_addr, int rx_len); -static void associate(ray_dev_t *local); - -/* Card command functions */ -static int dl_startup_params(struct net_device *dev); -static void join_net(struct timer_list *t); -static void start_net(struct timer_list *t); - -/*===========================================================================*/ -/* Parameters that can be set with 'insmod' */ - -/* ADHOC=0, Infrastructure=1 */ -static int net_type = ADHOC; - -/* Hop dwell time in Kus (1024 us units defined by 802.11) */ -static int hop_dwell = 128; - -/* Beacon period in Kus */ -static int beacon_period = 256; - -/* power save mode (0 = off, 1 = save power) */ -static int psm; - -/* String for network's Extended Service Set ID. 32 Characters max */ -static char *essid; - -/* Default to encapsulation unless translation requested */ -static bool translate = true; - -static int country = USA; - -static int sniffer; - -static int bc; - -/* 48 bit physical card address if overriding card's real physical - * address is required. Since IEEE 802.11 addresses are 48 bits - * like ethernet, an int can't be used, so a string is used. To - * allow use of addresses starting with a decimal digit, the first - * character must be a letter and will be ignored. This letter is - * followed by up to 12 hex digits which are the address. If less - * than 12 digits are used, the address will be left filled with 0's. - * Note that bit 0 of the first byte is the broadcast bit, and evil - * things will happen if it is not 0 in a card address. - */ -static char *phy_addr = NULL; - -static unsigned int ray_mem_speed = 500; - -/* WARNING: THIS DRIVER IS NOT CAPABLE OF HANDLING MULTIPLE DEVICES! */ -static struct pcmcia_device *this_device = NULL; - -MODULE_AUTHOR("Corey Thomas "); -MODULE_DESCRIPTION("Raylink/WebGear wireless LAN driver"); -MODULE_LICENSE("GPL"); - -module_param(net_type, int, 0); -module_param(hop_dwell, int, 0); -module_param(beacon_period, int, 0); -module_param(psm, int, 0); -module_param(essid, charp, 0); -module_param(translate, bool, 0); -module_param(country, int, 0); -module_param(sniffer, int, 0); -module_param(bc, int, 0); -module_param(phy_addr, charp, 0); -module_param(ray_mem_speed, int, 0); - -static const UCHAR b5_default_startup_parms[] = { - 0, 0, /* Adhoc station */ - 'L', 'I', 'N', 'U', 'X', 0, 0, 0, /* 32 char ESSID */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, /* Active scan, CA Mode */ - 0, 0, 0, 0, 0, 0, /* No default MAC addr */ - 0x7f, 0xff, /* Frag threshold */ - 0x00, 0x80, /* Hop time 128 Kus */ - 0x01, 0x00, /* Beacon period 256 Kus */ - 0x01, 0x07, 0xa3, /* DTIM, retries, ack timeout */ - 0x1d, 0x82, 0x4e, /* SIFS, DIFS, PIFS */ - 0x7f, 0xff, /* RTS threshold */ - 0x04, 0xe2, 0x38, 0xA4, /* scan_dwell, max_scan_dwell */ - 0x05, /* assoc resp timeout thresh */ - 0x08, 0x02, 0x08, /* adhoc, infra, super cycle max */ - 0, /* Promiscuous mode */ - 0x0c, 0x0bd, /* Unique word */ - 0x32, /* Slot time */ - 0xff, 0xff, /* roam-low snr, low snr count */ - 0x05, 0xff, /* Infra, adhoc missed bcn thresh */ - 0x01, 0x0b, 0x4f, /* USA, hop pattern, hop pat length */ -/* b4 - b5 differences start here */ - 0x00, 0x3f, /* CW max */ - 0x00, 0x0f, /* CW min */ - 0x04, 0x08, /* Noise gain, limit offset */ - 0x28, 0x28, /* det rssi, med busy offsets */ - 7, /* det sync thresh */ - 0, 2, 2, /* test mode, min, max */ - 0, /* allow broadcast SSID probe resp */ - 0, 0, /* privacy must start, can join */ - 2, 0, 0, 0, 0, 0, 0, 0 /* basic rate set */ -}; - -static const UCHAR b4_default_startup_parms[] = { - 0, 0, /* Adhoc station */ - 'L', 'I', 'N', 'U', 'X', 0, 0, 0, /* 32 char ESSID */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, /* Active scan, CA Mode */ - 0, 0, 0, 0, 0, 0, /* No default MAC addr */ - 0x7f, 0xff, /* Frag threshold */ - 0x02, 0x00, /* Hop time */ - 0x00, 0x01, /* Beacon period */ - 0x01, 0x07, 0xa3, /* DTIM, retries, ack timeout */ - 0x1d, 0x82, 0xce, /* SIFS, DIFS, PIFS */ - 0x7f, 0xff, /* RTS threshold */ - 0xfb, 0x1e, 0xc7, 0x5c, /* scan_dwell, max_scan_dwell */ - 0x05, /* assoc resp timeout thresh */ - 0x04, 0x02, 0x4, /* adhoc, infra, super cycle max */ - 0, /* Promiscuous mode */ - 0x0c, 0x0bd, /* Unique word */ - 0x4e, /* Slot time (TBD seems wrong) */ - 0xff, 0xff, /* roam-low snr, low snr count */ - 0x05, 0xff, /* Infra, adhoc missed bcn thresh */ - 0x01, 0x0b, 0x4e, /* USA, hop pattern, hop pat length */ -/* b4 - b5 differences start here */ - 0x3f, 0x0f, /* CW max, min */ - 0x04, 0x08, /* Noise gain, limit offset */ - 0x28, 0x28, /* det rssi, med busy offsets */ - 7, /* det sync thresh */ - 0, 2, 2, /* test mode, min, max */ - 0, /* rx/tx delay */ - 0, 0, 0, 0, 0, 0, /* current BSS id */ - 0 /* hop set */ -}; - -/*===========================================================================*/ -static const u8 eth2_llc[] = { 0xaa, 0xaa, 3, 0, 0, 0 }; - -static const char hop_pattern_length[] = { 1, - USA_HOP_MOD, EUROPE_HOP_MOD, - JAPAN_HOP_MOD, KOREA_HOP_MOD, - SPAIN_HOP_MOD, FRANCE_HOP_MOD, - ISRAEL_HOP_MOD, AUSTRALIA_HOP_MOD, - JAPAN_TEST_HOP_MOD -}; - -static const char rcsid[] = - "Raylink/WebGear wireless LAN - Corey "; - -static const struct net_device_ops ray_netdev_ops = { - .ndo_init = ray_dev_init, - .ndo_open = ray_open, - .ndo_stop = ray_dev_close, - .ndo_start_xmit = ray_dev_start_xmit, - .ndo_set_config = ray_dev_config, - .ndo_get_stats = ray_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int ray_probe(struct pcmcia_device *p_dev) -{ - ray_dev_t *local; - struct net_device *dev; - - dev_dbg(&p_dev->dev, "ray_attach()\n"); - - /* Allocate space for private device-specific data */ - dev = alloc_etherdev(sizeof(ray_dev_t)); - if (!dev) - goto fail_alloc_dev; - - local = netdev_priv(dev); - local->finder = p_dev; - - /* The io structure describes IO port mapping. None used here */ - p_dev->resource[0]->end = 0; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - - /* General socket configuration */ - p_dev->config_flags |= CONF_ENABLE_IRQ; - p_dev->config_index = 1; - - p_dev->priv = dev; - - local->finder = p_dev; - local->card_status = CARD_INSERTED; - local->authentication_state = UNAUTHENTICATED; - local->num_multi = 0; - dev_dbg(&p_dev->dev, "ray_attach p_dev = %p, dev = %p, local = %p, intr = %p\n", - p_dev, dev, local, &ray_interrupt); - - /* Raylink entries in the device structure */ - dev->netdev_ops = &ray_netdev_ops; - dev->wireless_handlers = &ray_handler_def; -#ifdef WIRELESS_SPY - local->wireless_data.spy_data = &local->spy_data; - dev->wireless_data = &local->wireless_data; -#endif /* WIRELESS_SPY */ - - - dev_dbg(&p_dev->dev, "ray_cs ray_attach calling ether_setup.)\n"); - netif_stop_queue(dev); - - timer_setup(&local->timer, NULL, 0); - - this_device = p_dev; - return ray_config(p_dev); - -fail_alloc_dev: - return -ENOMEM; -} /* ray_attach */ - -static void ray_detach(struct pcmcia_device *link) -{ - struct net_device *dev; - ray_dev_t *local; - - dev_dbg(&link->dev, "ray_detach\n"); - - this_device = NULL; - dev = link->priv; - - ray_release(link); - - local = netdev_priv(dev); - del_timer_sync(&local->timer); - - if (link->priv) { - unregister_netdev(dev); - free_netdev(dev); - } - dev_dbg(&link->dev, "ray_cs ray_detach ending\n"); -} /* ray_detach */ - -#define MAX_TUPLE_SIZE 128 -static int ray_config(struct pcmcia_device *link) -{ - int ret = 0; - int i; - struct net_device *dev = (struct net_device *)link->priv; - ray_dev_t *local = netdev_priv(dev); - - dev_dbg(&link->dev, "ray_config\n"); - - /* Determine card type and firmware version */ - printk(KERN_INFO "ray_cs Detected: %s%s%s%s\n", - link->prod_id[0] ? link->prod_id[0] : " ", - link->prod_id[1] ? link->prod_id[1] : " ", - link->prod_id[2] ? link->prod_id[2] : " ", - link->prod_id[3] ? link->prod_id[3] : " "); - - /* Now allocate an interrupt line. Note that this does not - actually assign a handler to the interrupt. - */ - ret = pcmcia_request_irq(link, ray_interrupt); - if (ret) - goto failed; - dev->irq = link->irq; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - -/*** Set up 32k window for shared memory (transmit and control) ************/ - link->resource[2]->flags |= WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; - link->resource[2]->start = 0; - link->resource[2]->end = 0x8000; - ret = pcmcia_request_window(link, link->resource[2], ray_mem_speed); - if (ret) - goto failed; - ret = pcmcia_map_mem_page(link, link->resource[2], 0); - if (ret) - goto failed; - local->sram = ioremap(link->resource[2]->start, - resource_size(link->resource[2])); - if (!local->sram) - goto failed; - -/*** Set up 16k window for shared memory (receive buffer) ***************/ - link->resource[3]->flags |= - WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; - link->resource[3]->start = 0; - link->resource[3]->end = 0x4000; - ret = pcmcia_request_window(link, link->resource[3], ray_mem_speed); - if (ret) - goto failed; - ret = pcmcia_map_mem_page(link, link->resource[3], 0x8000); - if (ret) - goto failed; - local->rmem = ioremap(link->resource[3]->start, - resource_size(link->resource[3])); - if (!local->rmem) - goto failed; - -/*** Set up window for attribute memory ***********************************/ - link->resource[4]->flags |= - WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_AM | WIN_ENABLE | WIN_USE_WAIT; - link->resource[4]->start = 0; - link->resource[4]->end = 0x1000; - ret = pcmcia_request_window(link, link->resource[4], ray_mem_speed); - if (ret) - goto failed; - ret = pcmcia_map_mem_page(link, link->resource[4], 0); - if (ret) - goto failed; - local->amem = ioremap(link->resource[4]->start, - resource_size(link->resource[4])); - if (!local->amem) - goto failed; - - dev_dbg(&link->dev, "ray_config sram=%p\n", local->sram); - dev_dbg(&link->dev, "ray_config rmem=%p\n", local->rmem); - dev_dbg(&link->dev, "ray_config amem=%p\n", local->amem); - if (ray_init(dev) < 0) { - ray_release(link); - return -ENODEV; - } - - SET_NETDEV_DEV(dev, &link->dev); - i = register_netdev(dev); - if (i != 0) { - printk("ray_config register_netdev() failed\n"); - ray_release(link); - return i; - } - - printk(KERN_INFO "%s: RayLink, irq %d, hw_addr %pM\n", - dev->name, dev->irq, dev->dev_addr); - - return 0; - -failed: - ray_release(link); - return -ENODEV; -} /* ray_config */ - -static inline struct ccs __iomem *ccs_base(ray_dev_t *dev) -{ - return dev->sram + CCS_BASE; -} - -static inline struct rcs __iomem *rcs_base(ray_dev_t *dev) -{ - /* - * This looks nonsensical, since there is a separate - * RCS_BASE. But the difference between a "struct rcs" - * and a "struct ccs" ends up being in the _index_ off - * the base, so the base pointer is the same for both - * ccs/rcs. - */ - return dev->sram + CCS_BASE; -} - -/*===========================================================================*/ -static int ray_init(struct net_device *dev) -{ - int i; - struct ccs __iomem *pccs; - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - dev_dbg(&link->dev, "ray_init(0x%p)\n", dev); - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_init - device not present\n"); - return -1; - } - - local->net_type = net_type; - local->sta_type = TYPE_STA; - - /* Copy the startup results to local memory */ - memcpy_fromio(&local->startup_res, local->sram + ECF_TO_HOST_BASE, - sizeof(struct startup_res_6)); - - /* Check Power up test status and get mac address from card */ - if (local->startup_res.startup_word != 0x80) { - printk(KERN_INFO "ray_init ERROR card status = %2x\n", - local->startup_res.startup_word); - local->card_status = CARD_INIT_ERROR; - return -1; - } - - local->fw_ver = local->startup_res.firmware_version[0]; - local->fw_bld = local->startup_res.firmware_version[1]; - local->fw_var = local->startup_res.firmware_version[2]; - dev_dbg(&link->dev, "ray_init firmware version %d.%d\n", local->fw_ver, - local->fw_bld); - - local->tib_length = 0x20; - if ((local->fw_ver == 5) && (local->fw_bld >= 30)) - local->tib_length = local->startup_res.tib_length; - dev_dbg(&link->dev, "ray_init tib_length = 0x%02x\n", local->tib_length); - /* Initialize CCS's to buffer free state */ - pccs = ccs_base(local); - for (i = 0; i < NUMBER_OF_CCS; i++) { - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - } - init_startup_params(local); - - /* copy mac address to startup parameters */ - if (!parse_addr(phy_addr, local->sparm.b4.a_mac_addr)) { - memcpy(&local->sparm.b4.a_mac_addr, - &local->startup_res.station_addr, ADDRLEN); - } - - clear_interrupt(local); /* Clear any interrupt from the card */ - local->card_status = CARD_AWAITING_PARAM; - dev_dbg(&link->dev, "ray_init ending\n"); - return 0; -} /* ray_init */ - -/*===========================================================================*/ -/* Download startup parameters to the card and command it to read them */ -static int dl_startup_params(struct net_device *dev) -{ - int ccsindex; - ray_dev_t *local = netdev_priv(dev); - struct ccs __iomem *pccs; - struct pcmcia_device *link = local->finder; - - dev_dbg(&link->dev, "dl_startup_params entered\n"); - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs dl_startup_params - device not present\n"); - return -1; - } - - /* Copy parameters to host to ECF area */ - if (local->fw_ver == 0x55) - memcpy_toio(local->sram + HOST_TO_ECF_BASE, &local->sparm.b4, - sizeof(struct b4_startup_params)); - else - memcpy_toio(local->sram + HOST_TO_ECF_BASE, &local->sparm.b5, - sizeof(struct b5_startup_params)); - - /* Fill in the CCS fields for the ECF */ - if ((ccsindex = get_free_ccs(local)) < 0) - return -1; - local->dl_param_ccs = ccsindex; - pccs = ccs_base(local) + ccsindex; - writeb(CCS_DOWNLOAD_STARTUP_PARAMS, &pccs->cmd); - dev_dbg(&link->dev, "dl_startup_params start ccsindex = %d\n", - local->dl_param_ccs); - /* Interrupt the firmware to process the command */ - if (interrupt_ecf(local, ccsindex)) { - printk(KERN_INFO "ray dl_startup_params failed - " - "ECF not ready for intr\n"); - local->card_status = CARD_DL_PARAM_ERROR; - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - return -2; - } - local->card_status = CARD_DL_PARAM; - /* Start kernel timer to wait for dl startup to complete. */ - local->timer.expires = jiffies + HZ / 2; - local->timer.function = verify_dl_startup; - add_timer(&local->timer); - dev_dbg(&link->dev, - "ray_cs dl_startup_params started timer for verify_dl_startup\n"); - return 0; -} /* dl_startup_params */ - -/*===========================================================================*/ -static void init_startup_params(ray_dev_t *local) -{ - int i; - - if (country > JAPAN_TEST) - country = USA; - else if (country < USA) - country = USA; - /* structure for hop time and beacon period is defined here using - * New 802.11D6.1 format. Card firmware is still using old format - * until version 6. - * Before After - * a_hop_time ms byte a_hop_time ms byte - * a_hop_time 2s byte a_hop_time ls byte - * a_hop_time ls byte a_beacon_period ms byte - * a_beacon_period a_beacon_period ls byte - * - * a_hop_time = uS a_hop_time = KuS - * a_beacon_period = hops a_beacon_period = KuS - *//* 64ms = 010000 */ - if (local->fw_ver == 0x55) { - memcpy(&local->sparm.b4, b4_default_startup_parms, - sizeof(struct b4_startup_params)); - /* Translate sane kus input values to old build 4/5 format */ - /* i = hop time in uS truncated to 3 bytes */ - i = (hop_dwell * 1024) & 0xffffff; - local->sparm.b4.a_hop_time[0] = (i >> 16) & 0xff; - local->sparm.b4.a_hop_time[1] = (i >> 8) & 0xff; - local->sparm.b4.a_beacon_period[0] = 0; - local->sparm.b4.a_beacon_period[1] = - ((beacon_period / hop_dwell) - 1) & 0xff; - local->sparm.b4.a_curr_country_code = country; - local->sparm.b4.a_hop_pattern_length = - hop_pattern_length[(int)country] - 1; - if (bc) { - local->sparm.b4.a_ack_timeout = 0x50; - local->sparm.b4.a_sifs = 0x3f; - } - } else { /* Version 5 uses real kus values */ - memcpy((UCHAR *) &local->sparm.b5, b5_default_startup_parms, - sizeof(struct b5_startup_params)); - - local->sparm.b5.a_hop_time[0] = (hop_dwell >> 8) & 0xff; - local->sparm.b5.a_hop_time[1] = hop_dwell & 0xff; - local->sparm.b5.a_beacon_period[0] = - (beacon_period >> 8) & 0xff; - local->sparm.b5.a_beacon_period[1] = beacon_period & 0xff; - if (psm) - local->sparm.b5.a_power_mgt_state = 1; - local->sparm.b5.a_curr_country_code = country; - local->sparm.b5.a_hop_pattern_length = - hop_pattern_length[(int)country]; - } - - local->sparm.b4.a_network_type = net_type & 0x01; - local->sparm.b4.a_acting_as_ap_status = TYPE_STA; - - if (essid != NULL) - strncpy(local->sparm.b4.a_current_ess_id, essid, ESSID_SIZE); -} /* init_startup_params */ - -/*===========================================================================*/ -static void verify_dl_startup(struct timer_list *t) -{ - ray_dev_t *local = from_timer(local, t, timer); - struct ccs __iomem *pccs = ccs_base(local) + local->dl_param_ccs; - UCHAR status; - struct pcmcia_device *link = local->finder; - - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs verify_dl_startup - device not present\n"); - return; - } -#if 0 - { - int i; - printk(KERN_DEBUG - "verify_dl_startup parameters sent via ccs %d:\n", - local->dl_param_ccs); - for (i = 0; i < sizeof(struct b5_startup_params); i++) { - printk(" %2x", - (unsigned int)readb(local->sram + - HOST_TO_ECF_BASE + i)); - } - printk("\n"); - } -#endif - - status = readb(&pccs->buffer_status); - if (status != CCS_BUFFER_FREE) { - printk(KERN_INFO - "Download startup params failed. Status = %d\n", - status); - local->card_status = CARD_DL_PARAM_ERROR; - return; - } - if (local->sparm.b4.a_network_type == ADHOC) - start_net(&local->timer); - else - join_net(&local->timer); -} /* end verify_dl_startup */ - -/*===========================================================================*/ -/* Command card to start a network */ -static void start_net(struct timer_list *t) -{ - ray_dev_t *local = from_timer(local, t, timer); - struct ccs __iomem *pccs; - int ccsindex; - struct pcmcia_device *link = local->finder; - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs start_net - device not present\n"); - return; - } - /* Fill in the CCS fields for the ECF */ - if ((ccsindex = get_free_ccs(local)) < 0) - return; - pccs = ccs_base(local) + ccsindex; - writeb(CCS_START_NETWORK, &pccs->cmd); - writeb(0, &pccs->var.start_network.update_param); - /* Interrupt the firmware to process the command */ - if (interrupt_ecf(local, ccsindex)) { - dev_dbg(&link->dev, "ray start net failed - card not ready for intr\n"); - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - return; - } - local->card_status = CARD_DOING_ACQ; -} /* end start_net */ - -/*===========================================================================*/ -/* Command card to join a network */ -static void join_net(struct timer_list *t) -{ - ray_dev_t *local = from_timer(local, t, timer); - - struct ccs __iomem *pccs; - int ccsindex; - struct pcmcia_device *link = local->finder; - - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs join_net - device not present\n"); - return; - } - /* Fill in the CCS fields for the ECF */ - if ((ccsindex = get_free_ccs(local)) < 0) - return; - pccs = ccs_base(local) + ccsindex; - writeb(CCS_JOIN_NETWORK, &pccs->cmd); - writeb(0, &pccs->var.join_network.update_param); - writeb(0, &pccs->var.join_network.net_initiated); - /* Interrupt the firmware to process the command */ - if (interrupt_ecf(local, ccsindex)) { - dev_dbg(&link->dev, "ray join net failed - card not ready for intr\n"); - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - return; - } - local->card_status = CARD_DOING_ACQ; -} - - -static void ray_release(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - ray_dev_t *local = netdev_priv(dev); - - dev_dbg(&link->dev, "ray_release\n"); - - del_timer(&local->timer); - - iounmap(local->sram); - iounmap(local->rmem); - iounmap(local->amem); - pcmcia_disable_device(link); - - dev_dbg(&link->dev, "ray_release ending\n"); -} - -static int ray_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int ray_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - ray_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - -/*===========================================================================*/ -static int ray_dev_init(struct net_device *dev) -{ -#ifdef RAY_IMMEDIATE_INIT - int i; -#endif /* RAY_IMMEDIATE_INIT */ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - - dev_dbg(&link->dev, "ray_dev_init(dev=%p)\n", dev); - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_dev_init - device not present\n"); - return -1; - } -#ifdef RAY_IMMEDIATE_INIT - /* Download startup parameters */ - if ((i = dl_startup_params(dev)) < 0) { - printk(KERN_INFO "ray_dev_init dl_startup_params failed - " - "returns 0x%x\n", i); - return -1; - } -#else /* RAY_IMMEDIATE_INIT */ - /* Postpone the card init so that we can still configure the card, - * for example using the Wireless Extensions. The init will happen - * in ray_open() - Jean II */ - dev_dbg(&link->dev, - "ray_dev_init: postponing card init to ray_open() ; Status = %d\n", - local->card_status); -#endif /* RAY_IMMEDIATE_INIT */ - - /* copy mac and broadcast addresses to linux device */ - eth_hw_addr_set(dev, local->sparm.b4.a_mac_addr); - eth_broadcast_addr(dev->broadcast); - - dev_dbg(&link->dev, "ray_dev_init ending\n"); - return 0; -} - -/*===========================================================================*/ -static int ray_dev_config(struct net_device *dev, struct ifmap *map) -{ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - /* Dummy routine to satisfy device structure */ - dev_dbg(&link->dev, "ray_dev_config(dev=%p,ifmap=%p)\n", dev, map); - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_dev_config - device not present\n"); - return -1; - } - - return 0; -} - -/*===========================================================================*/ -static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - short length = skb->len; - - if (!pcmcia_dev_present(link)) { - dev_dbg(&link->dev, "ray_dev_start_xmit - device not present\n"); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - dev_dbg(&link->dev, "ray_dev_start_xmit(skb=%p, dev=%p)\n", skb, dev); - if (local->authentication_state == NEED_TO_AUTH) { - dev_dbg(&link->dev, "ray_cs Sending authentication request.\n"); - if (!build_auth_frame(local, local->auth_id, OPEN_AUTH_REQUEST)) { - local->authentication_state = AUTHENTICATED; - netif_stop_queue(dev); - return NETDEV_TX_BUSY; - } - } - - if (length < ETH_ZLEN) { - if (skb_padto(skb, ETH_ZLEN)) - return NETDEV_TX_OK; - length = ETH_ZLEN; - } - switch (ray_hw_xmit(skb->data, length, dev, DATA_TYPE)) { - case XMIT_NO_CCS: - case XMIT_NEED_AUTH: - netif_stop_queue(dev); - return NETDEV_TX_BUSY; - case XMIT_NO_INTR: - case XMIT_MSG_BAD: - case XMIT_OK: - default: - dev_kfree_skb(skb); - } - - return NETDEV_TX_OK; -} /* ray_dev_start_xmit */ - -/*===========================================================================*/ -static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev, - UCHAR msg_type) -{ - ray_dev_t *local = netdev_priv(dev); - struct ccs __iomem *pccs; - int ccsindex; - int offset; - struct tx_msg __iomem *ptx; /* Address of xmit buffer in PC space */ - short int addr; /* Address of xmit buffer in card space */ - - pr_debug("ray_hw_xmit(data=%p, len=%d, dev=%p)\n", data, len, dev); - if (len + TX_HEADER_LENGTH > TX_BUF_SIZE) { - printk(KERN_INFO "ray_hw_xmit packet too large: %d bytes\n", - len); - return XMIT_MSG_BAD; - } - switch (ccsindex = get_free_tx_ccs(local)) { - case ECCSBUSY: - pr_debug("ray_hw_xmit tx_ccs table busy\n"); - fallthrough; - case ECCSFULL: - pr_debug("ray_hw_xmit No free tx ccs\n"); - fallthrough; - case ECARDGONE: - netif_stop_queue(dev); - return XMIT_NO_CCS; - default: - break; - } - addr = TX_BUF_BASE + (ccsindex << 11); - - if (msg_type == DATA_TYPE) { - local->stats.tx_bytes += len; - local->stats.tx_packets++; - } - - ptx = local->sram + addr; - - ray_build_header(local, ptx, msg_type, data); - if (translate) { - offset = translate_frame(local, ptx, data, len); - } else { /* Encapsulate frame */ - /* TBD TIB length will move address of ptx->var */ - memcpy_toio(&ptx->var, data, len); - offset = 0; - } - - /* fill in the CCS */ - pccs = ccs_base(local) + ccsindex; - len += TX_HEADER_LENGTH + offset; - writeb(CCS_TX_REQUEST, &pccs->cmd); - writeb(addr >> 8, &pccs->var.tx_request.tx_data_ptr[0]); - writeb(local->tib_length, &pccs->var.tx_request.tx_data_ptr[1]); - writeb(len >> 8, &pccs->var.tx_request.tx_data_length[0]); - writeb(len & 0xff, &pccs->var.tx_request.tx_data_length[1]); -/* TBD still need psm_cam? */ - writeb(PSM_CAM, &pccs->var.tx_request.pow_sav_mode); - writeb(local->net_default_tx_rate, &pccs->var.tx_request.tx_rate); - writeb(0, &pccs->var.tx_request.antenna); - pr_debug("ray_hw_xmit default_tx_rate = 0x%x\n", - local->net_default_tx_rate); - - /* Interrupt the firmware to process the command */ - if (interrupt_ecf(local, ccsindex)) { - pr_debug("ray_hw_xmit failed - ECF not ready for intr\n"); -/* TBD very inefficient to copy packet to buffer, and then not - send it, but the alternative is to queue the messages and that - won't be done for a while. Maybe set tbusy until a CCS is free? -*/ - writeb(CCS_BUFFER_FREE, &pccs->buffer_status); - return XMIT_NO_INTR; - } - return XMIT_OK; -} /* end ray_hw_xmit */ - -/*===========================================================================*/ -static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx, - unsigned char *data, int len) -{ - __be16 proto = ((struct ethhdr *)data)->h_proto; - if (ntohs(proto) >= ETH_P_802_3_MIN) { /* DIX II ethernet frame */ - pr_debug("ray_cs translate_frame DIX II\n"); - /* Copy LLC header to card buffer */ - memcpy_toio(&ptx->var, eth2_llc, sizeof(eth2_llc)); - memcpy_toio(((void __iomem *)&ptx->var) + sizeof(eth2_llc), - (UCHAR *) &proto, 2); - if (proto == htons(ETH_P_AARP) || proto == htons(ETH_P_IPX)) { - /* This is the selective translation table, only 2 entries */ - writeb(0xf8, - &((struct snaphdr_t __iomem *)ptx->var)->org[2]); - } - /* Copy body of ethernet packet without ethernet header */ - memcpy_toio((void __iomem *)&ptx->var + - sizeof(struct snaphdr_t), data + ETH_HLEN, - len - ETH_HLEN); - return (int)sizeof(struct snaphdr_t) - ETH_HLEN; - } else { /* already 802 type, and proto is length */ - pr_debug("ray_cs translate_frame 802\n"); - if (proto == htons(0xffff)) { /* evil netware IPX 802.3 without LLC */ - pr_debug("ray_cs translate_frame evil IPX\n"); - memcpy_toio(&ptx->var, data + ETH_HLEN, len - ETH_HLEN); - return 0 - ETH_HLEN; - } - memcpy_toio(&ptx->var, data + ETH_HLEN, len - ETH_HLEN); - return 0 - ETH_HLEN; - } - /* TBD do other frame types */ -} /* end translate_frame */ - -/*===========================================================================*/ -static void ray_build_header(ray_dev_t *local, struct tx_msg __iomem *ptx, - UCHAR msg_type, unsigned char *data) -{ - writeb(PROTOCOL_VER | msg_type, &ptx->mac.frame_ctl_1); -/*** IEEE 802.11 Address field assignments ************* - TODS FROMDS addr_1 addr_2 addr_3 addr_4 -Adhoc 0 0 dest src (terminal) BSSID N/A -AP to Terminal 0 1 dest AP(BSSID) source N/A -Terminal to AP 1 0 AP(BSSID) src (terminal) dest N/A -AP to AP 1 1 dest AP src AP dest source -*******************************************************/ - if (local->net_type == ADHOC) { - writeb(0, &ptx->mac.frame_ctl_2); - memcpy_toio(ptx->mac.addr_1, ((struct ethhdr *)data)->h_dest, - ADDRLEN); - memcpy_toio(ptx->mac.addr_2, ((struct ethhdr *)data)->h_source, - ADDRLEN); - memcpy_toio(ptx->mac.addr_3, local->bss_id, ADDRLEN); - } else { /* infrastructure */ - - if (local->sparm.b4.a_acting_as_ap_status) { - writeb(FC2_FROM_DS, &ptx->mac.frame_ctl_2); - memcpy_toio(ptx->mac.addr_1, - ((struct ethhdr *)data)->h_dest, ADDRLEN); - memcpy_toio(ptx->mac.addr_2, local->bss_id, 6); - memcpy_toio(ptx->mac.addr_3, - ((struct ethhdr *)data)->h_source, ADDRLEN); - } else { /* Terminal */ - - writeb(FC2_TO_DS, &ptx->mac.frame_ctl_2); - memcpy_toio(ptx->mac.addr_1, local->bss_id, ADDRLEN); - memcpy_toio(ptx->mac.addr_2, - ((struct ethhdr *)data)->h_source, ADDRLEN); - memcpy_toio(ptx->mac.addr_3, - ((struct ethhdr *)data)->h_dest, ADDRLEN); - } - } -} /* end encapsulate_frame */ - -/*====================================================================*/ - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get protocol name - */ -static int ray_get_name(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - strcpy(wrqu->name, "IEEE 802.11-FH"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set frequency - */ -static int ray_set_freq(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - int err = -EINPROGRESS; /* Call commit handler */ - - /* Reject if card is already initialised */ - if (local->card_status != CARD_AWAITING_PARAM) - return -EBUSY; - - /* Setting by channel number */ - if ((wrqu->freq.m > USA_HOP_MOD) || (wrqu->freq.e > 0)) - err = -EOPNOTSUPP; - else - local->sparm.b5.a_hop_pattern = wrqu->freq.m; - - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get frequency - */ -static int ray_get_freq(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - wrqu->freq.m = local->sparm.b5.a_hop_pattern; - wrqu->freq.e = 0; - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set ESSID - */ -static int ray_set_essid(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - /* Reject if card is already initialised */ - if (local->card_status != CARD_AWAITING_PARAM) - return -EBUSY; - - /* Check if we asked for `any' */ - if (wrqu->essid.flags == 0) - /* Corey : can you do that ? */ - return -EOPNOTSUPP; - - /* Check the size of the string */ - if (wrqu->essid.length > IW_ESSID_MAX_SIZE) - return -E2BIG; - - /* Set the ESSID in the card */ - memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE); - memcpy(local->sparm.b5.a_current_ess_id, extra, wrqu->essid.length); - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get ESSID - */ -static int ray_get_essid(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - UCHAR tmp[IW_ESSID_MAX_SIZE + 1]; - - /* Get the essid that was set */ - memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); - memcpy(tmp, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); - tmp[IW_ESSID_MAX_SIZE] = '\0'; - - /* Push it out ! */ - wrqu->essid.length = strlen(tmp); - wrqu->essid.flags = 1; /* active */ - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get AP address - */ -static int ray_get_wap(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - memcpy(wrqu->ap_addr.sa_data, local->bss_id, ETH_ALEN); - wrqu->ap_addr.sa_family = ARPHRD_ETHER; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Bit-Rate - */ -static int ray_set_rate(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - /* Reject if card is already initialised */ - if (local->card_status != CARD_AWAITING_PARAM) - return -EBUSY; - - /* Check if rate is in range */ - if ((wrqu->bitrate.value != 1000000) && (wrqu->bitrate.value != 2000000)) - return -EINVAL; - - /* Hack for 1.5 Mb/s instead of 2 Mb/s */ - if ((local->fw_ver == 0x55) && /* Please check */ - (wrqu->bitrate.value == 2000000)) - local->net_default_tx_rate = 3; - else - local->net_default_tx_rate = wrqu->bitrate.value / 500000; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Bit-Rate - */ -static int ray_get_rate(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - if (local->net_default_tx_rate == 3) - wrqu->bitrate.value = 2000000; /* Hum... */ - else - wrqu->bitrate.value = local->net_default_tx_rate * 500000; - wrqu->bitrate.fixed = 0; /* We are in auto mode */ - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set RTS threshold - */ -static int ray_set_rts(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - int rthr = wrqu->rts.value; - - /* Reject if card is already initialised */ - if (local->card_status != CARD_AWAITING_PARAM) - return -EBUSY; - - /* if(wrq->u.rts.fixed == 0) we should complain */ - if (wrqu->rts.disabled) - rthr = 32767; - else { - if ((rthr < 0) || (rthr > 2347)) /* What's the max packet size ??? */ - return -EINVAL; - } - local->sparm.b5.a_rts_threshold[0] = (rthr >> 8) & 0xFF; - local->sparm.b5.a_rts_threshold[1] = rthr & 0xFF; - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get RTS threshold - */ -static int ray_get_rts(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - wrqu->rts.value = (local->sparm.b5.a_rts_threshold[0] << 8) - + local->sparm.b5.a_rts_threshold[1]; - wrqu->rts.disabled = (wrqu->rts.value == 32767); - wrqu->rts.fixed = 1; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Fragmentation threshold - */ -static int ray_set_frag(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - int fthr = wrqu->frag.value; - - /* Reject if card is already initialised */ - if (local->card_status != CARD_AWAITING_PARAM) - return -EBUSY; - - /* if(wrq->u.frag.fixed == 0) should complain */ - if (wrqu->frag.disabled) - fthr = 32767; - else { - if ((fthr < 256) || (fthr > 2347)) /* To check out ! */ - return -EINVAL; - } - local->sparm.b5.a_frag_threshold[0] = (fthr >> 8) & 0xFF; - local->sparm.b5.a_frag_threshold[1] = fthr & 0xFF; - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Fragmentation threshold - */ -static int ray_get_frag(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - wrqu->frag.value = (local->sparm.b5.a_frag_threshold[0] << 8) - + local->sparm.b5.a_frag_threshold[1]; - wrqu->frag.disabled = (wrqu->frag.value == 32767); - wrqu->frag.fixed = 1; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Mode of Operation - */ -static int ray_set_mode(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - int err = -EINPROGRESS; /* Call commit handler */ - char card_mode = 1; - - /* Reject if card is already initialised */ - if (local->card_status != CARD_AWAITING_PARAM) - return -EBUSY; - - switch (wrqu->mode) { - case IW_MODE_ADHOC: - card_mode = 0; - fallthrough; - case IW_MODE_INFRA: - local->sparm.b5.a_network_type = card_mode; - break; - default: - err = -EINVAL; - } - - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Mode of Operation - */ -static int ray_get_mode(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - ray_dev_t *local = netdev_priv(dev); - - if (local->sparm.b5.a_network_type) - wrqu->mode = IW_MODE_INFRA; - else - wrqu->mode = IW_MODE_ADHOC; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get range info - */ -static int ray_get_range(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct iw_range *range = (struct iw_range *)extra; - - memset(range, 0, sizeof(struct iw_range)); - - /* Set the length (very important for backward compatibility) */ - wrqu->data.length = sizeof(struct iw_range); - - /* Set the Wireless Extension versions */ - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 9; - - /* Set information in the range struct */ - range->throughput = 1.1 * 1000 * 1000; /* Put the right number here */ - range->num_channels = hop_pattern_length[(int)country]; - range->num_frequency = 0; - range->max_qual.qual = 0; - range->max_qual.level = 255; /* What's the correct value ? */ - range->max_qual.noise = 255; /* Idem */ - range->num_bitrates = 2; - range->bitrate[0] = 1000000; /* 1 Mb/s */ - range->bitrate[1] = 2000000; /* 2 Mb/s */ - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : set framing mode - */ -static int ray_set_framing(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - translate = !!*(extra); /* Set framing mode */ - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : get framing mode - */ -static int ray_get_framing(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - *(extra) = translate; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Private Handler : get country - */ -static int ray_get_country(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - *(extra) = country; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Commit handler : called after a bunch of SET operations - */ -static int ray_commit(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Stats handler : return Wireless Stats - */ -static iw_stats *ray_get_wireless_stats(struct net_device *dev) -{ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - struct status __iomem *p = local->sram + STATUS_BASE; - - local->wstats.status = local->card_status; -#ifdef WIRELESS_SPY - if ((local->spy_data.spy_number > 0) - && (local->sparm.b5.a_network_type == 0)) { - /* Get it from the first node in spy list */ - local->wstats.qual.qual = local->spy_data.spy_stat[0].qual; - local->wstats.qual.level = local->spy_data.spy_stat[0].level; - local->wstats.qual.noise = local->spy_data.spy_stat[0].noise; - local->wstats.qual.updated = - local->spy_data.spy_stat[0].updated; - } -#endif /* WIRELESS_SPY */ - - if (pcmcia_dev_present(link)) { - local->wstats.qual.noise = readb(&p->rxnoise); - local->wstats.qual.updated |= 4; - } - - return &local->wstats; -} /* end ray_get_wireless_stats */ - -/*------------------------------------------------------------------*/ -/* - * Structures to export the Wireless Handlers - */ - -static const iw_handler ray_handler[] = { - IW_HANDLER(SIOCSIWCOMMIT, ray_commit), - IW_HANDLER(SIOCGIWNAME, ray_get_name), - IW_HANDLER(SIOCSIWFREQ, ray_set_freq), - IW_HANDLER(SIOCGIWFREQ, ray_get_freq), - IW_HANDLER(SIOCSIWMODE, ray_set_mode), - IW_HANDLER(SIOCGIWMODE, ray_get_mode), - IW_HANDLER(SIOCGIWRANGE, ray_get_range), -#ifdef WIRELESS_SPY - IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), - IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), - IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), - IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), -#endif /* WIRELESS_SPY */ - IW_HANDLER(SIOCGIWAP, ray_get_wap), - IW_HANDLER(SIOCSIWESSID, ray_set_essid), - IW_HANDLER(SIOCGIWESSID, ray_get_essid), - IW_HANDLER(SIOCSIWRATE, ray_set_rate), - IW_HANDLER(SIOCGIWRATE, ray_get_rate), - IW_HANDLER(SIOCSIWRTS, ray_set_rts), - IW_HANDLER(SIOCGIWRTS, ray_get_rts), - IW_HANDLER(SIOCSIWFRAG, ray_set_frag), - IW_HANDLER(SIOCGIWFRAG, ray_get_frag), -}; - -#define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */ -#define SIOCGIPFRAMING SIOCIWFIRSTPRIV + 1 /* Get framing mode */ -#define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */ - -static const iw_handler ray_private_handler[] = { - [0] = ray_set_framing, - [1] = ray_get_framing, - [3] = ray_get_country, -}; - -static const struct iw_priv_args ray_private_args[] = { -/* cmd, set_args, get_args, name */ - {SIOCSIPFRAMING, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, - "set_framing"}, - {SIOCGIPFRAMING, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, - "get_framing"}, - {SIOCGIPCOUNTRY, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, - "get_country"}, -}; - -static const struct iw_handler_def ray_handler_def = { - .num_standard = ARRAY_SIZE(ray_handler), - .num_private = ARRAY_SIZE(ray_private_handler), - .num_private_args = ARRAY_SIZE(ray_private_args), - .standard = ray_handler, - .private = ray_private_handler, - .private_args = ray_private_args, - .get_wireless_stats = ray_get_wireless_stats, -}; - -/*===========================================================================*/ -static int ray_open(struct net_device *dev) -{ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link; - link = local->finder; - - dev_dbg(&link->dev, "ray_open('%s')\n", dev->name); - - if (link->open == 0) - local->num_multi = 0; - link->open++; - - /* If the card is not started, time to start it ! - Jean II */ - if (local->card_status == CARD_AWAITING_PARAM) { - int i; - - dev_dbg(&link->dev, "ray_open: doing init now !\n"); - - /* Download startup parameters */ - if ((i = dl_startup_params(dev)) < 0) { - printk(KERN_INFO - "ray_dev_init dl_startup_params failed - " - "returns 0x%x\n", i); - return -1; - } - } - - if (sniffer) - netif_stop_queue(dev); - else - netif_start_queue(dev); - - dev_dbg(&link->dev, "ray_open ending\n"); - return 0; -} /* end ray_open */ - -/*===========================================================================*/ -static int ray_dev_close(struct net_device *dev) -{ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link; - link = local->finder; - - dev_dbg(&link->dev, "ray_dev_close('%s')\n", dev->name); - - link->open--; - netif_stop_queue(dev); - - /* In here, we should stop the hardware (stop card from beeing active) - * and set local->card_status to CARD_AWAITING_PARAM, so that while the - * card is closed we can chage its configuration. - * Probably also need a COR reset to get sane state - Jean II */ - - return 0; -} /* end ray_dev_close */ - -/*===========================================================================*/ -static void ray_reset(struct net_device *dev) -{ - pr_debug("ray_reset entered\n"); -} - -/*===========================================================================*/ -/* Cause a firmware interrupt if it is ready for one */ -/* Return nonzero if not ready */ -static int interrupt_ecf(ray_dev_t *local, int ccs) -{ - int i = 50; - struct pcmcia_device *link = local->finder; - - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs interrupt_ecf - device not present\n"); - return -1; - } - dev_dbg(&link->dev, "interrupt_ecf(local=%p, ccs = 0x%x\n", local, ccs); - - while (i && - (readb(local->amem + CIS_OFFSET + ECF_INTR_OFFSET) & - ECF_INTR_SET)) - i--; - if (i == 0) { - dev_dbg(&link->dev, "ray_cs interrupt_ecf card not ready for interrupt\n"); - return -1; - } - /* Fill the mailbox, then kick the card */ - writeb(ccs, local->sram + SCB_BASE); - writeb(ECF_INTR_SET, local->amem + CIS_OFFSET + ECF_INTR_OFFSET); - return 0; -} /* interrupt_ecf */ - -/*===========================================================================*/ -/* Get next free transmit CCS */ -/* Return - index of current tx ccs */ -static int get_free_tx_ccs(ray_dev_t *local) -{ - int i; - struct ccs __iomem *pccs = ccs_base(local); - struct pcmcia_device *link = local->finder; - - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs get_free_tx_ccs - device not present\n"); - return ECARDGONE; - } - - if (test_and_set_bit(0, &local->tx_ccs_lock)) { - dev_dbg(&link->dev, "ray_cs tx_ccs_lock busy\n"); - return ECCSBUSY; - } - - for (i = 0; i < NUMBER_OF_TX_CCS; i++) { - if (readb(&(pccs + i)->buffer_status) == CCS_BUFFER_FREE) { - writeb(CCS_BUFFER_BUSY, &(pccs + i)->buffer_status); - writeb(CCS_END_LIST, &(pccs + i)->link); - local->tx_ccs_lock = 0; - return i; - } - } - local->tx_ccs_lock = 0; - dev_dbg(&link->dev, "ray_cs ERROR no free tx CCS for raylink card\n"); - return ECCSFULL; -} /* get_free_tx_ccs */ - -/*===========================================================================*/ -/* Get next free CCS */ -/* Return - index of current ccs */ -static int get_free_ccs(ray_dev_t *local) -{ - int i; - struct ccs __iomem *pccs = ccs_base(local); - struct pcmcia_device *link = local->finder; - - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs get_free_ccs - device not present\n"); - return ECARDGONE; - } - if (test_and_set_bit(0, &local->ccs_lock)) { - dev_dbg(&link->dev, "ray_cs ccs_lock busy\n"); - return ECCSBUSY; - } - - for (i = NUMBER_OF_TX_CCS; i < NUMBER_OF_CCS; i++) { - if (readb(&(pccs + i)->buffer_status) == CCS_BUFFER_FREE) { - writeb(CCS_BUFFER_BUSY, &(pccs + i)->buffer_status); - writeb(CCS_END_LIST, &(pccs + i)->link); - local->ccs_lock = 0; - return i; - } - } - local->ccs_lock = 0; - dev_dbg(&link->dev, "ray_cs ERROR no free CCS for raylink card\n"); - return ECCSFULL; -} /* get_free_ccs */ - -/*===========================================================================*/ -static void authenticate_timeout(struct timer_list *t) -{ - ray_dev_t *local = from_timer(local, t, timer); - del_timer(&local->timer); - printk(KERN_INFO "ray_cs Authentication with access point failed" - " - timeout\n"); - join_net(&local->timer); -} - -/*===========================================================================*/ -static int parse_addr(char *in_str, UCHAR *out) -{ - int i, k; - int len; - - if (in_str == NULL) - return 0; - len = strnlen(in_str, ADDRLEN * 2 + 1) - 1; - if (len < 1) - return 0; - memset(out, 0, ADDRLEN); - - i = 5; - - while (len > 0) { - if ((k = hex_to_bin(in_str[len--])) != -1) - out[i] = k; - else - return 0; - - if (len == 0) - break; - if ((k = hex_to_bin(in_str[len--])) != -1) - out[i] += k << 4; - else - return 0; - if (!i--) - break; - } - return 1; -} - -/*===========================================================================*/ -static struct net_device_stats *ray_get_stats(struct net_device *dev) -{ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - struct status __iomem *p = local->sram + STATUS_BASE; - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs net_device_stats - device not present\n"); - return &local->stats; - } - if (readb(&p->mrx_overflow_for_host)) { - local->stats.rx_over_errors += swab16(readw(&p->mrx_overflow)); - writeb(0, &p->mrx_overflow); - writeb(0, &p->mrx_overflow_for_host); - } - if (readb(&p->mrx_checksum_error_for_host)) { - local->stats.rx_crc_errors += - swab16(readw(&p->mrx_checksum_error)); - writeb(0, &p->mrx_checksum_error); - writeb(0, &p->mrx_checksum_error_for_host); - } - if (readb(&p->rx_hec_error_for_host)) { - local->stats.rx_frame_errors += swab16(readw(&p->rx_hec_error)); - writeb(0, &p->rx_hec_error); - writeb(0, &p->rx_hec_error_for_host); - } - return &local->stats; -} - -/*===========================================================================*/ -static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, - int len) -{ - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - int ccsindex; - int i; - struct ccs __iomem *pccs; - - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_update_parm - device not present\n"); - return; - } - - if ((ccsindex = get_free_ccs(local)) < 0) { - dev_dbg(&link->dev, "ray_update_parm - No free ccs\n"); - return; - } - pccs = ccs_base(local) + ccsindex; - writeb(CCS_UPDATE_PARAMS, &pccs->cmd); - writeb(objid, &pccs->var.update_param.object_id); - writeb(1, &pccs->var.update_param.number_objects); - writeb(0, &pccs->var.update_param.failure_cause); - for (i = 0; i < len; i++) { - writeb(value[i], local->sram + HOST_TO_ECF_BASE); - } - /* Interrupt the firmware to process the command */ - if (interrupt_ecf(local, ccsindex)) { - dev_dbg(&link->dev, "ray_cs associate failed - ECF not ready for intr\n"); - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - } -} - -/*===========================================================================*/ -static void ray_update_multi_list(struct net_device *dev, int all) -{ - int ccsindex; - struct ccs __iomem *pccs; - ray_dev_t *local = netdev_priv(dev); - struct pcmcia_device *link = local->finder; - void __iomem *p = local->sram + HOST_TO_ECF_BASE; - - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_update_multi_list - device not present\n"); - return; - } else - dev_dbg(&link->dev, "ray_update_multi_list(%p)\n", dev); - if ((ccsindex = get_free_ccs(local)) < 0) { - dev_dbg(&link->dev, "ray_update_multi - No free ccs\n"); - return; - } - pccs = ccs_base(local) + ccsindex; - writeb(CCS_UPDATE_MULTICAST_LIST, &pccs->cmd); - - if (all) { - writeb(0xff, &pccs->var); - local->num_multi = 0xff; - } else { - struct netdev_hw_addr *ha; - int i = 0; - - /* Copy the kernel's list of MC addresses to card */ - netdev_for_each_mc_addr(ha, dev) { - memcpy_toio(p, ha->addr, ETH_ALEN); - dev_dbg(&link->dev, "ray_update_multi add addr %pm\n", - ha->addr); - p += ETH_ALEN; - i++; - } - if (i > 256 / ADDRLEN) - i = 256 / ADDRLEN; - writeb((UCHAR) i, &pccs->var); - dev_dbg(&link->dev, "ray_cs update_multi %d addresses in list\n", i); - /* Interrupt the firmware to process the command */ - local->num_multi = i; - } - if (interrupt_ecf(local, ccsindex)) { - dev_dbg(&link->dev, - "ray_cs update_multi failed - ECF not ready for intr\n"); - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - } -} /* end ray_update_multi_list */ - -/*===========================================================================*/ -static void set_multicast_list(struct net_device *dev) -{ - ray_dev_t *local = netdev_priv(dev); - UCHAR promisc; - - pr_debug("ray_cs set_multicast_list(%p)\n", dev); - - if (dev->flags & IFF_PROMISC) { - if (local->sparm.b5.a_promiscuous_mode == 0) { - pr_debug("ray_cs set_multicast_list promisc on\n"); - local->sparm.b5.a_promiscuous_mode = 1; - promisc = 1; - ray_update_parm(dev, OBJID_promiscuous_mode, - &promisc, sizeof(promisc)); - } - } else { - if (local->sparm.b5.a_promiscuous_mode == 1) { - pr_debug("ray_cs set_multicast_list promisc off\n"); - local->sparm.b5.a_promiscuous_mode = 0; - promisc = 0; - ray_update_parm(dev, OBJID_promiscuous_mode, - &promisc, sizeof(promisc)); - } - } - - if (dev->flags & IFF_ALLMULTI) - ray_update_multi_list(dev, 1); - else { - if (local->num_multi != netdev_mc_count(dev)) - ray_update_multi_list(dev, 0); - } -} /* end set_multicast_list */ - -/*============================================================================= - * All routines below here are run at interrupt time. -=============================================================================*/ -static irqreturn_t ray_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct pcmcia_device *link; - ray_dev_t *local; - struct ccs __iomem *pccs; - struct rcs __iomem *prcs; - UCHAR rcsindex; - UCHAR tmp; - UCHAR cmd; - UCHAR status; - UCHAR memtmp[ESSID_SIZE + 1]; - - - if (dev == NULL) /* Note that we want interrupts with dev->start == 0 */ - return IRQ_NONE; - - pr_debug("ray_cs: interrupt for *dev=%p\n", dev); - - local = netdev_priv(dev); - link = local->finder; - if (!pcmcia_dev_present(link)) { - pr_debug( - "ray_cs interrupt from device not present or suspended.\n"); - return IRQ_NONE; - } - rcsindex = readb(&((struct scb __iomem *)(local->sram))->rcs_index); - - if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) { - dev_dbg(&link->dev, "ray_cs interrupt bad rcsindex = 0x%x\n", rcsindex); - clear_interrupt(local); - return IRQ_HANDLED; - } - if (rcsindex < NUMBER_OF_CCS) { /* If it's a returned CCS */ - pccs = ccs_base(local) + rcsindex; - cmd = readb(&pccs->cmd); - status = readb(&pccs->buffer_status); - switch (cmd) { - case CCS_DOWNLOAD_STARTUP_PARAMS: /* Happens in firmware someday */ - del_timer(&local->timer); - if (status == CCS_COMMAND_COMPLETE) { - dev_dbg(&link->dev, - "ray_cs interrupt download_startup_parameters OK\n"); - } else { - dev_dbg(&link->dev, - "ray_cs interrupt download_startup_parameters fail\n"); - } - break; - case CCS_UPDATE_PARAMS: - dev_dbg(&link->dev, "ray_cs interrupt update params done\n"); - if (status != CCS_COMMAND_COMPLETE) { - tmp = - readb(&pccs->var.update_param. - failure_cause); - dev_dbg(&link->dev, - "ray_cs interrupt update params failed - reason %d\n", - tmp); - } - break; - case CCS_REPORT_PARAMS: - dev_dbg(&link->dev, "ray_cs interrupt report params done\n"); - break; - case CCS_UPDATE_MULTICAST_LIST: /* Note that this CCS isn't returned */ - dev_dbg(&link->dev, - "ray_cs interrupt CCS Update Multicast List done\n"); - break; - case CCS_UPDATE_POWER_SAVINGS_MODE: - dev_dbg(&link->dev, - "ray_cs interrupt update power save mode done\n"); - break; - case CCS_START_NETWORK: - case CCS_JOIN_NETWORK: - memcpy(memtmp, local->sparm.b4.a_current_ess_id, - ESSID_SIZE); - memtmp[ESSID_SIZE] = '\0'; - - if (status == CCS_COMMAND_COMPLETE) { - if (readb - (&pccs->var.start_network.net_initiated) == - 1) { - dev_dbg(&link->dev, - "ray_cs interrupt network \"%s\" started\n", - memtmp); - } else { - dev_dbg(&link->dev, - "ray_cs interrupt network \"%s\" joined\n", - memtmp); - } - memcpy_fromio(&local->bss_id, - pccs->var.start_network.bssid, - ADDRLEN); - - if (local->fw_ver == 0x55) - local->net_default_tx_rate = 3; - else - local->net_default_tx_rate = - readb(&pccs->var.start_network. - net_default_tx_rate); - local->encryption = - readb(&pccs->var.start_network.encryption); - if (!sniffer && (local->net_type == INFRA) - && !(local->sparm.b4.a_acting_as_ap_status)) { - authenticate(local); - } - local->card_status = CARD_ACQ_COMPLETE; - } else { - local->card_status = CARD_ACQ_FAILED; - - del_timer(&local->timer); - local->timer.expires = jiffies + HZ * 5; - if (status == CCS_START_NETWORK) { - dev_dbg(&link->dev, - "ray_cs interrupt network \"%s\" start failed\n", - memtmp); - local->timer.function = start_net; - } else { - dev_dbg(&link->dev, - "ray_cs interrupt network \"%s\" join failed\n", - memtmp); - local->timer.function = join_net; - } - add_timer(&local->timer); - } - break; - case CCS_START_ASSOCIATION: - if (status == CCS_COMMAND_COMPLETE) { - local->card_status = CARD_ASSOC_COMPLETE; - dev_dbg(&link->dev, "ray_cs association successful\n"); - } else { - dev_dbg(&link->dev, "ray_cs association failed,\n"); - local->card_status = CARD_ASSOC_FAILED; - join_net(&local->timer); - } - break; - case CCS_TX_REQUEST: - if (status == CCS_COMMAND_COMPLETE) { - dev_dbg(&link->dev, - "ray_cs interrupt tx request complete\n"); - } else { - dev_dbg(&link->dev, - "ray_cs interrupt tx request failed\n"); - } - if (!sniffer) - netif_start_queue(dev); - netif_wake_queue(dev); - break; - case CCS_TEST_MEMORY: - dev_dbg(&link->dev, "ray_cs interrupt mem test done\n"); - break; - case CCS_SHUTDOWN: - dev_dbg(&link->dev, - "ray_cs interrupt Unexpected CCS returned - Shutdown\n"); - break; - case CCS_DUMP_MEMORY: - dev_dbg(&link->dev, "ray_cs interrupt dump memory done\n"); - break; - case CCS_START_TIMER: - dev_dbg(&link->dev, - "ray_cs interrupt DING - raylink timer expired\n"); - break; - default: - dev_dbg(&link->dev, - "ray_cs interrupt Unexpected CCS 0x%x returned 0x%x\n", - rcsindex, cmd); - } - writeb(CCS_BUFFER_FREE, &pccs->buffer_status); - } else { /* It's an RCS */ - - prcs = rcs_base(local) + rcsindex; - - switch (readb(&prcs->interrupt_id)) { - case PROCESS_RX_PACKET: - ray_rx(dev, local, prcs); - break; - case REJOIN_NET_COMPLETE: - dev_dbg(&link->dev, "ray_cs interrupt rejoin net complete\n"); - local->card_status = CARD_ACQ_COMPLETE; - /* do we need to clear tx buffers CCS's? */ - if (local->sparm.b4.a_network_type == ADHOC) { - if (!sniffer) - netif_start_queue(dev); - } else { - memcpy_fromio(&local->bss_id, - prcs->var.rejoin_net_complete. - bssid, ADDRLEN); - dev_dbg(&link->dev, "ray_cs new BSSID = %pm\n", - local->bss_id); - if (!sniffer) - authenticate(local); - } - break; - case ROAMING_INITIATED: - dev_dbg(&link->dev, "ray_cs interrupt roaming initiated\n"); - netif_stop_queue(dev); - local->card_status = CARD_DOING_ACQ; - break; - case JAPAN_CALL_SIGN_RXD: - dev_dbg(&link->dev, "ray_cs interrupt japan call sign rx\n"); - break; - default: - dev_dbg(&link->dev, - "ray_cs Unexpected interrupt for RCS 0x%x cmd = 0x%x\n", - rcsindex, - (unsigned int)readb(&prcs->interrupt_id)); - break; - } - writeb(CCS_BUFFER_FREE, &prcs->buffer_status); - } - clear_interrupt(local); - return IRQ_HANDLED; -} /* ray_interrupt */ - -/*===========================================================================*/ -static void ray_rx(struct net_device *dev, ray_dev_t *local, - struct rcs __iomem *prcs) -{ - int rx_len; - unsigned int pkt_addr; - void __iomem *pmsg; - pr_debug("ray_rx process rx packet\n"); - - /* Calculate address of packet within Rx buffer */ - pkt_addr = ((readb(&prcs->var.rx_packet.rx_data_ptr[0]) << 8) - + readb(&prcs->var.rx_packet.rx_data_ptr[1])) & RX_BUFF_END; - /* Length of first packet fragment */ - rx_len = (readb(&prcs->var.rx_packet.rx_data_length[0]) << 8) - + readb(&prcs->var.rx_packet.rx_data_length[1]); - - local->last_rsl = readb(&prcs->var.rx_packet.rx_sig_lev); - pmsg = local->rmem + pkt_addr; - switch (readb(pmsg)) { - case DATA_TYPE: - pr_debug("ray_rx data type\n"); - rx_data(dev, prcs, pkt_addr, rx_len); - break; - case AUTHENTIC_TYPE: - pr_debug("ray_rx authentic type\n"); - if (sniffer) - rx_data(dev, prcs, pkt_addr, rx_len); - else - rx_authenticate(local, prcs, pkt_addr, rx_len); - break; - case DEAUTHENTIC_TYPE: - pr_debug("ray_rx deauth type\n"); - if (sniffer) - rx_data(dev, prcs, pkt_addr, rx_len); - else - rx_deauthenticate(local, prcs, pkt_addr, rx_len); - break; - case NULL_MSG_TYPE: - pr_debug("ray_cs rx NULL msg\n"); - break; - case BEACON_TYPE: - pr_debug("ray_rx beacon type\n"); - if (sniffer) - rx_data(dev, prcs, pkt_addr, rx_len); - - copy_from_rx_buff(local, (UCHAR *) &local->last_bcn, pkt_addr, - rx_len < sizeof(struct beacon_rx) ? - rx_len : sizeof(struct beacon_rx)); - - local->beacon_rxed = 1; - /* Get the statistics so the card counters never overflow */ - ray_get_stats(dev); - break; - default: - pr_debug("ray_cs unknown pkt type %2x\n", - (unsigned int)readb(pmsg)); - break; - } - -} /* end ray_rx */ - -/*===========================================================================*/ -static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, - unsigned int pkt_addr, int rx_len) -{ - struct sk_buff *skb = NULL; - struct rcs __iomem *prcslink = prcs; - ray_dev_t *local = netdev_priv(dev); - UCHAR *rx_ptr; - int total_len; - int tmp; -#ifdef WIRELESS_SPY - int siglev = local->last_rsl; - u_char linksrcaddr[ETH_ALEN]; /* Other end of the wireless link */ -#endif - - if (!sniffer) { - if (translate) { -/* TBD length needs fixing for translated header */ - if (rx_len < (ETH_HLEN + RX_MAC_HEADER_LENGTH) || - rx_len > - (dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN + - FCS_LEN)) { - pr_debug( - "ray_cs invalid packet length %d received\n", - rx_len); - return; - } - } else { /* encapsulated ethernet */ - - if (rx_len < (ETH_HLEN + RX_MAC_HEADER_LENGTH) || - rx_len > - (dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN + - FCS_LEN)) { - pr_debug( - "ray_cs invalid packet length %d received\n", - rx_len); - return; - } - } - } - pr_debug("ray_cs rx_data packet\n"); - /* If fragmented packet, verify sizes of fragments add up */ - if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) { - pr_debug("ray_cs rx'ed fragment\n"); - tmp = (readb(&prcs->var.rx_packet.totalpacketlength[0]) << 8) - + readb(&prcs->var.rx_packet.totalpacketlength[1]); - total_len = tmp; - prcslink = prcs; - do { - tmp -= - (readb(&prcslink->var.rx_packet.rx_data_length[0]) - << 8) - + readb(&prcslink->var.rx_packet.rx_data_length[1]); - if (readb(&prcslink->var.rx_packet.next_frag_rcs_index) - == 0xFF || tmp < 0) - break; - prcslink = rcs_base(local) - + readb(&prcslink->link_field); - } while (1); - - if (tmp < 0) { - pr_debug( - "ray_cs rx_data fragment lengths don't add up\n"); - local->stats.rx_dropped++; - release_frag_chain(local, prcs); - return; - } - } else { /* Single unfragmented packet */ - total_len = rx_len; - } - - skb = dev_alloc_skb(total_len + 5); - if (skb == NULL) { - pr_debug("ray_cs rx_data could not allocate skb\n"); - local->stats.rx_dropped++; - if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) - release_frag_chain(local, prcs); - return; - } - skb_reserve(skb, 2); /* Align IP on 16 byte (TBD check this) */ - - pr_debug("ray_cs rx_data total_len = %x, rx_len = %x\n", total_len, - rx_len); - -/************************/ - /* Reserve enough room for the whole damn packet. */ - rx_ptr = skb_put(skb, total_len); - /* Copy the whole packet to sk_buff */ - rx_ptr += - copy_from_rx_buff(local, rx_ptr, pkt_addr & RX_BUFF_END, rx_len); - /* Get source address */ -#ifdef WIRELESS_SPY - skb_copy_from_linear_data_offset(skb, - offsetof(struct mac_header, addr_2), - linksrcaddr, ETH_ALEN); -#endif - /* Now, deal with encapsulation/translation/sniffer */ - if (!sniffer) { - if (!translate) { - /* Encapsulated ethernet, so just lop off 802.11 MAC header */ -/* TBD reserve skb_reserve( skb, RX_MAC_HEADER_LENGTH); */ - skb_pull(skb, RX_MAC_HEADER_LENGTH); - } else { - /* Do translation */ - untranslate(local, skb, total_len); - } - } else { /* sniffer mode, so just pass whole packet */ - } - -/************************/ - /* Now pick up the rest of the fragments if any */ - tmp = 17; - if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) { - prcslink = prcs; - pr_debug("ray_cs rx_data in fragment loop\n"); - do { - prcslink = rcs_base(local) - + - readb(&prcslink->var.rx_packet.next_frag_rcs_index); - rx_len = - ((readb(&prcslink->var.rx_packet.rx_data_length[0]) - << 8) - + - readb(&prcslink->var.rx_packet.rx_data_length[1])) - & RX_BUFF_END; - pkt_addr = - ((readb(&prcslink->var.rx_packet.rx_data_ptr[0]) << - 8) - + readb(&prcslink->var.rx_packet.rx_data_ptr[1])) - & RX_BUFF_END; - - rx_ptr += - copy_from_rx_buff(local, rx_ptr, pkt_addr, rx_len); - - } while (tmp-- && - readb(&prcslink->var.rx_packet.next_frag_rcs_index) != - 0xFF); - release_frag_chain(local, prcs); - } - - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - local->stats.rx_packets++; - local->stats.rx_bytes += total_len; - - /* Gather signal strength per address */ -#ifdef WIRELESS_SPY - /* For the Access Point or the node having started the ad-hoc net - * note : ad-hoc work only in some specific configurations, but we - * kludge in ray_get_wireless_stats... */ - if (!memcmp(linksrcaddr, local->bss_id, ETH_ALEN)) { - /* Update statistics */ - /*local->wstats.qual.qual = none ? */ - local->wstats.qual.level = siglev; - /*local->wstats.qual.noise = none ? */ - local->wstats.qual.updated = 0x2; - } - /* Now, update the spy stuff */ - { - struct iw_quality wstats; - wstats.level = siglev; - /* wstats.noise = none ? */ - /* wstats.qual = none ? */ - wstats.updated = 0x2; - /* Update spy records */ - wireless_spy_update(dev, linksrcaddr, &wstats); - } -#endif /* WIRELESS_SPY */ -} /* end rx_data */ - -/*===========================================================================*/ -static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len) -{ - snaphdr_t *psnap = (snaphdr_t *) (skb->data + RX_MAC_HEADER_LENGTH); - struct ieee80211_hdr *pmac = (struct ieee80211_hdr *)skb->data; - __be16 type = *(__be16 *) psnap->ethertype; - int delta; - struct ethhdr *peth; - UCHAR srcaddr[ADDRLEN]; - UCHAR destaddr[ADDRLEN]; - static const UCHAR org_bridge[3] = { 0, 0, 0xf8 }; - static const UCHAR org_1042[3] = { 0, 0, 0 }; - - memcpy(destaddr, ieee80211_get_DA(pmac), ADDRLEN); - memcpy(srcaddr, ieee80211_get_SA(pmac), ADDRLEN); - -#if 0 - if { - print_hex_dump(KERN_DEBUG, "skb->data before untranslate: ", - DUMP_PREFIX_NONE, 16, 1, - skb->data, 64, true); - printk(KERN_DEBUG - "type = %08x, xsap = %02x%02x%02x, org = %02x02x02x\n", - ntohs(type), psnap->dsap, psnap->ssap, psnap->ctrl, - psnap->org[0], psnap->org[1], psnap->org[2]); - printk(KERN_DEBUG "untranslate skb->data = %p\n", skb->data); - } -#endif - - if (psnap->dsap != 0xaa || psnap->ssap != 0xaa || psnap->ctrl != 3) { - /* not a snap type so leave it alone */ - pr_debug("ray_cs untranslate NOT SNAP %02x %02x %02x\n", - psnap->dsap, psnap->ssap, psnap->ctrl); - - delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; - peth = (struct ethhdr *)(skb->data + delta); - peth->h_proto = htons(len - RX_MAC_HEADER_LENGTH); - } else { /* Its a SNAP */ - if (memcmp(psnap->org, org_bridge, 3) == 0) { - /* EtherII and nuke the LLC */ - pr_debug("ray_cs untranslate Bridge encap\n"); - delta = RX_MAC_HEADER_LENGTH - + sizeof(struct snaphdr_t) - ETH_HLEN; - peth = (struct ethhdr *)(skb->data + delta); - peth->h_proto = type; - } else if (memcmp(psnap->org, org_1042, 3) == 0) { - switch (ntohs(type)) { - case ETH_P_IPX: - case ETH_P_AARP: - pr_debug("ray_cs untranslate RFC IPX/AARP\n"); - delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; - peth = (struct ethhdr *)(skb->data + delta); - peth->h_proto = - htons(len - RX_MAC_HEADER_LENGTH); - break; - default: - pr_debug("ray_cs untranslate RFC default\n"); - delta = RX_MAC_HEADER_LENGTH + - sizeof(struct snaphdr_t) - ETH_HLEN; - peth = (struct ethhdr *)(skb->data + delta); - peth->h_proto = type; - break; - } - } else { - printk("ray_cs untranslate very confused by packet\n"); - delta = RX_MAC_HEADER_LENGTH - ETH_HLEN; - peth = (struct ethhdr *)(skb->data + delta); - peth->h_proto = type; - } - } -/* TBD reserve skb_reserve(skb, delta); */ - skb_pull(skb, delta); - pr_debug("untranslate after skb_pull(%d), skb->data = %p\n", delta, - skb->data); - memcpy(peth->h_dest, destaddr, ADDRLEN); - memcpy(peth->h_source, srcaddr, ADDRLEN); -#if 0 - { - int i; - printk(KERN_DEBUG "skb->data after untranslate:"); - for (i = 0; i < 64; i++) - printk("%02x ", skb->data[i]); - printk("\n"); - } -#endif -} /* end untranslate */ - -/*===========================================================================*/ -/* Copy data from circular receive buffer to PC memory. - * dest = destination address in PC memory - * pkt_addr = source address in receive buffer - * len = length of packet to copy - */ -static int copy_from_rx_buff(ray_dev_t *local, UCHAR *dest, int pkt_addr, - int length) -{ - int wrap_bytes = (pkt_addr + length) - (RX_BUFF_END + 1); - if (wrap_bytes <= 0) { - memcpy_fromio(dest, local->rmem + pkt_addr, length); - } else { /* Packet wrapped in circular buffer */ - - memcpy_fromio(dest, local->rmem + pkt_addr, - length - wrap_bytes); - memcpy_fromio(dest + length - wrap_bytes, local->rmem, - wrap_bytes); - } - return length; -} - -/*===========================================================================*/ -static void release_frag_chain(ray_dev_t *local, struct rcs __iomem *prcs) -{ - struct rcs __iomem *prcslink = prcs; - int tmp = 17; - unsigned rcsindex = readb(&prcs->var.rx_packet.next_frag_rcs_index); - - while (tmp--) { - writeb(CCS_BUFFER_FREE, &prcslink->buffer_status); - if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) { - pr_debug("ray_cs interrupt bad rcsindex = 0x%x\n", - rcsindex); - break; - } - prcslink = rcs_base(local) + rcsindex; - rcsindex = readb(&prcslink->var.rx_packet.next_frag_rcs_index); - } - writeb(CCS_BUFFER_FREE, &prcslink->buffer_status); -} - -/*===========================================================================*/ -static void authenticate(ray_dev_t *local) -{ - struct pcmcia_device *link = local->finder; - dev_dbg(&link->dev, "ray_cs Starting authentication.\n"); - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs authenticate - device not present\n"); - return; - } - - del_timer(&local->timer); - if (build_auth_frame(local, local->bss_id, OPEN_AUTH_REQUEST)) { - local->timer.function = join_net; - } else { - local->timer.function = authenticate_timeout; - } - local->timer.expires = jiffies + HZ * 2; - add_timer(&local->timer); - local->authentication_state = AWAITING_RESPONSE; -} /* end authenticate */ - -/*===========================================================================*/ -static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs, - unsigned int pkt_addr, int rx_len) -{ - UCHAR buff[256]; - struct ray_rx_msg *msg = (struct ray_rx_msg *) buff; - - del_timer(&local->timer); - - copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); - /* if we are trying to get authenticated */ - if (local->sparm.b4.a_network_type == ADHOC) { - pr_debug("ray_cs rx_auth var= %6ph\n", msg->var); - if (msg->var[2] == 1) { - pr_debug("ray_cs Sending authentication response.\n"); - if (!build_auth_frame - (local, msg->mac.addr_2, OPEN_AUTH_RESPONSE)) { - local->authentication_state = NEED_TO_AUTH; - memcpy(local->auth_id, msg->mac.addr_2, - ADDRLEN); - } - } - } else { /* Infrastructure network */ - - if (local->authentication_state == AWAITING_RESPONSE) { - /* Verify authentication sequence #2 and success */ - if (msg->var[2] == 2) { - if ((msg->var[3] | msg->var[4]) == 0) { - pr_debug("Authentication successful\n"); - local->card_status = CARD_AUTH_COMPLETE; - associate(local); - local->authentication_state = - AUTHENTICATED; - } else { - pr_debug("Authentication refused\n"); - local->card_status = CARD_AUTH_REFUSED; - join_net(&local->timer); - local->authentication_state = - UNAUTHENTICATED; - } - } - } - } - -} /* end rx_authenticate */ - -/*===========================================================================*/ -static void associate(ray_dev_t *local) -{ - struct ccs __iomem *pccs; - struct pcmcia_device *link = local->finder; - struct net_device *dev = link->priv; - int ccsindex; - if (!(pcmcia_dev_present(link))) { - dev_dbg(&link->dev, "ray_cs associate - device not present\n"); - return; - } - /* If no tx buffers available, return */ - if ((ccsindex = get_free_ccs(local)) < 0) { -/* TBD should never be here but... what if we are? */ - dev_dbg(&link->dev, "ray_cs associate - No free ccs\n"); - return; - } - dev_dbg(&link->dev, "ray_cs Starting association with access point\n"); - pccs = ccs_base(local) + ccsindex; - /* fill in the CCS */ - writeb(CCS_START_ASSOCIATION, &pccs->cmd); - /* Interrupt the firmware to process the command */ - if (interrupt_ecf(local, ccsindex)) { - dev_dbg(&link->dev, "ray_cs associate failed - ECF not ready for intr\n"); - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - - del_timer(&local->timer); - local->timer.expires = jiffies + HZ * 2; - local->timer.function = join_net; - add_timer(&local->timer); - local->card_status = CARD_ASSOC_FAILED; - return; - } - if (!sniffer) - netif_start_queue(dev); - -} /* end associate */ - -/*===========================================================================*/ -static void rx_deauthenticate(ray_dev_t *local, struct rcs __iomem *prcs, - unsigned int pkt_addr, int rx_len) -{ -/* UCHAR buff[256]; - struct ray_rx_msg *msg = (struct ray_rx_msg *) buff; -*/ - pr_debug("Deauthentication frame received\n"); - local->authentication_state = UNAUTHENTICATED; - /* Need to reauthenticate or rejoin depending on reason code */ -/* copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff); - */ -} - -/*===========================================================================*/ -static void clear_interrupt(ray_dev_t *local) -{ - writeb(0, local->amem + CIS_OFFSET + HCS_INTR_OFFSET); -} - -/*===========================================================================*/ -#ifdef CONFIG_PROC_FS -#define MAXDATA (PAGE_SIZE - 80) - -static const char *card_status[] = { - "Card inserted - uninitialized", /* 0 */ - "Card not downloaded", /* 1 */ - "Waiting for download parameters", /* 2 */ - "Card doing acquisition", /* 3 */ - "Acquisition complete", /* 4 */ - "Authentication complete", /* 5 */ - "Association complete", /* 6 */ - "???", "???", "???", "???", /* 7 8 9 10 undefined */ - "Card init error", /* 11 */ - "Download parameters error", /* 12 */ - "???", /* 13 */ - "Acquisition failed", /* 14 */ - "Authentication refused", /* 15 */ - "Association failed" /* 16 */ -}; - -static const char *nettype[] = { "Adhoc", "Infra " }; -static const char *framing[] = { "Encapsulation", "Translation" } - -; -/*===========================================================================*/ -static int ray_cs_proc_show(struct seq_file *m, void *v) -{ -/* Print current values which are not available via other means - * eg ifconfig - */ - int i; - struct pcmcia_device *link; - struct net_device *dev; - ray_dev_t *local; - UCHAR *p; - struct freq_hop_element *pfh; - UCHAR c[33]; - - link = this_device; - if (!link) - return 0; - dev = (struct net_device *)link->priv; - if (!dev) - return 0; - local = netdev_priv(dev); - if (!local) - return 0; - - seq_puts(m, "Raylink Wireless LAN driver status\n"); - seq_printf(m, "%s\n", rcsid); - /* build 4 does not report version, and field is 0x55 after memtest */ - seq_puts(m, "Firmware version = "); - if (local->fw_ver == 0x55) - seq_puts(m, "4 - Use dump_cis for more details\n"); - else - seq_printf(m, "%2d.%02d.%02d\n", - local->fw_ver, local->fw_bld, local->fw_var); - - for (i = 0; i < 32; i++) - c[i] = local->sparm.b5.a_current_ess_id[i]; - c[32] = 0; - seq_printf(m, "%s network ESSID = \"%s\"\n", - nettype[local->sparm.b5.a_network_type], c); - - p = local->bss_id; - seq_printf(m, "BSSID = %pM\n", p); - - seq_printf(m, "Country code = %d\n", - local->sparm.b5.a_curr_country_code); - - i = local->card_status; - if (i < 0) - i = 10; - if (i > 16) - i = 10; - seq_printf(m, "Card status = %s\n", card_status[i]); - - seq_printf(m, "Framing mode = %s\n", framing[translate]); - - seq_printf(m, "Last pkt signal lvl = %d\n", local->last_rsl); - - if (local->beacon_rxed) { - /* Pull some fields out of last beacon received */ - seq_printf(m, "Beacon Interval = %d Kus\n", - local->last_bcn.beacon_intvl[0] - + 256 * local->last_bcn.beacon_intvl[1]); - - p = local->last_bcn.elements; - if (p[0] == C_ESSID_ELEMENT_ID) - p += p[1] + 2; - else { - seq_printf(m, - "Parse beacon failed at essid element id = %d\n", - p[0]); - return 0; - } - - if (p[0] == C_SUPPORTED_RATES_ELEMENT_ID) { - seq_puts(m, "Supported rate codes = "); - for (i = 2; i < p[1] + 2; i++) - seq_printf(m, "0x%02x ", p[i]); - seq_putc(m, '\n'); - p += p[1] + 2; - } else { - seq_puts(m, "Parse beacon failed at rates element\n"); - return 0; - } - - if (p[0] == C_FH_PARAM_SET_ELEMENT_ID) { - pfh = (struct freq_hop_element *)p; - seq_printf(m, "Hop dwell = %d Kus\n", - pfh->dwell_time[0] + - 256 * pfh->dwell_time[1]); - seq_printf(m, "Hop set = %d\n", - pfh->hop_set); - seq_printf(m, "Hop pattern = %d\n", - pfh->hop_pattern); - seq_printf(m, "Hop index = %d\n", - pfh->hop_index); - p += p[1] + 2; - } else { - seq_puts(m, - "Parse beacon failed at FH param element\n"); - return 0; - } - } else { - seq_puts(m, "No beacons received\n"); - } - return 0; -} -#endif -/*===========================================================================*/ -static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type) -{ - int addr; - struct ccs __iomem *pccs; - struct tx_msg __iomem *ptx; - int ccsindex; - - /* If no tx buffers available, return */ - if ((ccsindex = get_free_tx_ccs(local)) < 0) { - pr_debug("ray_cs send authenticate - No free tx ccs\n"); - return -1; - } - - pccs = ccs_base(local) + ccsindex; - - /* Address in card space */ - addr = TX_BUF_BASE + (ccsindex << 11); - /* fill in the CCS */ - writeb(CCS_TX_REQUEST, &pccs->cmd); - writeb(addr >> 8, pccs->var.tx_request.tx_data_ptr); - writeb(0x20, pccs->var.tx_request.tx_data_ptr + 1); - writeb(TX_AUTHENTICATE_LENGTH_MSB, pccs->var.tx_request.tx_data_length); - writeb(TX_AUTHENTICATE_LENGTH_LSB, - pccs->var.tx_request.tx_data_length + 1); - writeb(0, &pccs->var.tx_request.pow_sav_mode); - - ptx = local->sram + addr; - /* fill in the mac header */ - writeb(PROTOCOL_VER | AUTHENTIC_TYPE, &ptx->mac.frame_ctl_1); - writeb(0, &ptx->mac.frame_ctl_2); - - memcpy_toio(ptx->mac.addr_1, dest, ADDRLEN); - memcpy_toio(ptx->mac.addr_2, local->sparm.b4.a_mac_addr, ADDRLEN); - memcpy_toio(ptx->mac.addr_3, local->bss_id, ADDRLEN); - - /* Fill in msg body with protocol 00 00, sequence 01 00 ,status 00 00 */ - memset_io(ptx->var, 0, 6); - writeb(auth_type & 0xff, ptx->var + 2); - - /* Interrupt the firmware to process the command */ - if (interrupt_ecf(local, ccsindex)) { - pr_debug( - "ray_cs send authentication request failed - ECF not ready for intr\n"); - writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status); - return -1; - } - return 0; -} /* End build_auth_frame */ - -/*===========================================================================*/ -#ifdef CONFIG_PROC_FS -static ssize_t ray_cs_essid_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *pos) -{ - static char proc_essid[33]; - unsigned int len = count; - - if (len > 32) - len = 32; - memset(proc_essid, 0, 33); - if (copy_from_user(proc_essid, buffer, len)) - return -EFAULT; - essid = proc_essid; - return count; -} - -static const struct proc_ops ray_cs_essid_proc_ops = { - .proc_write = ray_cs_essid_proc_write, - .proc_lseek = noop_llseek, -}; - -static ssize_t int_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - static char proc_number[10]; - char *p; - int nr, len; - - if (!count) - return 0; - - if (count > 9) - return -EINVAL; - if (copy_from_user(proc_number, buffer, count)) - return -EFAULT; - p = proc_number; - nr = 0; - len = count; - do { - unsigned int c = *p - '0'; - if (c > 9) - return -EINVAL; - nr = nr * 10 + c; - p++; - } while (--len); - *(int *)pde_data(file_inode(file)) = nr; - return count; -} - -static const struct proc_ops int_proc_ops = { - .proc_write = int_proc_write, - .proc_lseek = noop_llseek, -}; -#endif - -static const struct pcmcia_device_id ray_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x01a6, 0x0000), - PCMCIA_DEVICE_NULL, -}; - -MODULE_DEVICE_TABLE(pcmcia, ray_ids); - -static struct pcmcia_driver ray_driver = { - .owner = THIS_MODULE, - .name = "ray_cs", - .probe = ray_probe, - .remove = ray_detach, - .id_table = ray_ids, - .suspend = ray_suspend, - .resume = ray_resume, -}; - -static int __init init_ray_cs(void) -{ - int rc; - - pr_debug("%s\n", rcsid); - rc = pcmcia_register_driver(&ray_driver); - pr_debug("raylink init_module register_pcmcia_driver returns 0x%x\n", - rc); - if (rc) - return rc; - -#ifdef CONFIG_PROC_FS - proc_mkdir("driver/ray_cs", NULL); - - proc_create_single("driver/ray_cs/ray_cs", 0, NULL, ray_cs_proc_show); - proc_create("driver/ray_cs/essid", 0200, NULL, &ray_cs_essid_proc_ops); - proc_create_data("driver/ray_cs/net_type", 0200, NULL, &int_proc_ops, - &net_type); - proc_create_data("driver/ray_cs/translate", 0200, NULL, &int_proc_ops, - &translate); -#endif - translate = !!translate; - return 0; -} /* init_ray_cs */ - -/*===========================================================================*/ - -static void __exit exit_ray_cs(void) -{ - pr_debug("ray_cs: cleanup_module\n"); - -#ifdef CONFIG_PROC_FS - remove_proc_subtree("driver/ray_cs", NULL); -#endif - - pcmcia_unregister_driver(&ray_driver); -} /* exit_ray_cs */ - -module_init(init_ray_cs); -module_exit(exit_ray_cs); - -/*===========================================================================*/ diff --git a/drivers/net/wireless/ray_cs.h b/drivers/net/wireless/ray_cs.h deleted file mode 100644 index 0609d8625019..000000000000 --- a/drivers/net/wireless/ray_cs.h +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Raytheon wireless LAN PCMCIA card driver for Linux - A PCMCIA client driver for the Raylink wireless network card - Written by Corey Thomas -*/ - -#ifndef _RAY_CS_H_ -#define _RAY_CS_H_ - -struct beacon_rx { - struct mac_header mac; - UCHAR timestamp[8]; - UCHAR beacon_intvl[2]; - UCHAR capability[2]; - UCHAR elements[sizeof(struct essid_element) - + sizeof(struct rates_element) - + sizeof(struct freq_hop_element) - + sizeof(struct japan_call_sign_element) - + sizeof(struct tim_element)]; -}; - -/* Return values for get_free{,_tx}_ccs */ -#define ECCSFULL (-1) -#define ECCSBUSY (-2) -#define ECARDGONE (-3) - -typedef struct ray_dev_t { - int card_status; - int authentication_state; - void __iomem *sram; /* pointer to beginning of shared RAM */ - void __iomem *amem; /* pointer to attribute mem window */ - void __iomem *rmem; /* pointer to receive buffer window */ - struct pcmcia_device *finder; /* pointer back to struct pcmcia_device for card */ - struct timer_list timer; - unsigned long tx_ccs_lock; - unsigned long ccs_lock; - int dl_param_ccs; - union { - struct b4_startup_params b4; - struct b5_startup_params b5; - } sparm; - int timeout_flag; - UCHAR supported_rates[8]; - UCHAR japan_call_sign[12]; - struct startup_res_6 startup_res; - int num_multi; - /* Network parameters from start/join */ - UCHAR bss_id[6]; - UCHAR auth_id[6]; - UCHAR net_default_tx_rate; - UCHAR encryption; - struct net_device_stats stats; - - UCHAR net_type; - UCHAR sta_type; - UCHAR fw_ver; - UCHAR fw_bld; - UCHAR fw_var; - UCHAR ASIC_version; - UCHAR assoc_id[2]; - UCHAR tib_length; - UCHAR last_rsl; - int beacon_rxed; - struct beacon_rx last_bcn; - iw_stats wstats; /* Wireless specific stats */ -#ifdef WIRELESS_SPY - struct iw_spy_data spy_data; - struct iw_public_data wireless_data; -#endif /* WIRELESS_SPY */ - -} ray_dev_t; -/*****************************************************************************/ - -#endif /* _RAY_CS_H_ */ diff --git a/drivers/net/wireless/rayctl.h b/drivers/net/wireless/rayctl.h deleted file mode 100644 index 2b0f332043d7..000000000000 --- a/drivers/net/wireless/rayctl.h +++ /dev/null @@ -1,734 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _RAYCTL_H_ -#define _RAYCTL_H_ - -typedef unsigned char UCHAR; - -/****** IEEE 802.11 constants ************************************************/ -#define ADDRLEN 6 -/* Frame control 1 bit fields */ -#define PROTOCOL_VER 0x00 -#define DATA_TYPE 0x08 -#define ASSOC_REQ_TYPE 0x00 -#define ASSOC_RESP_TYPE 0x10 -#define REASSOC_REQ_TYPE 0x20 -#define REASSOC_RESP_TYPE 0x30 -#define NULL_MSG_TYPE 0x48 -#define BEACON_TYPE 0x80 -#define DISASSOC_TYPE 0xA0 -#define PSPOLL_TYPE 0xA4 -#define AUTHENTIC_TYPE 0xB0 -#define DEAUTHENTIC_TYPE 0xC0 -/* Frame control 2 bit fields */ -#define FC2_TO_DS 0x01 -#define FC2_FROM_DS 0x02 -#define FC2_MORE_FRAG 0x04 -#define FC2_RETRY 0x08 -#define FC2_PSM 0x10 -#define FC2_MORE_DATA 0x20 -#define FC2_WEP 0x40 -#define FC2_ORDER 0x80 -/*****************************************************************************/ -/* 802.11 element ID's and lengths */ -#define C_BP_CAPABILITY_ESS 0x01 -#define C_BP_CAPABILITY_IBSS 0x02 -#define C_BP_CAPABILITY_CF_POLLABLE 0x04 -#define C_BP_CAPABILITY_CF_POLL_REQUEST 0x08 -#define C_BP_CAPABILITY_PRIVACY 0x10 - -#define C_ESSID_ELEMENT_ID 0 -#define C_ESSID_ELEMENT_MAX_LENGTH 32 - -#define C_SUPPORTED_RATES_ELEMENT_ID 1 -#define C_SUPPORTED_RATES_ELEMENT_LENGTH 2 - -#define C_FH_PARAM_SET_ELEMENT_ID 2 -#define C_FH_PARAM_SET_ELEMENT_LNGTH 5 - -#define C_CF_PARAM_SET_ELEMENT_ID 4 -#define C_CF_PARAM_SET_ELEMENT_LNGTH 6 - -#define C_TIM_ELEMENT_ID 5 -#define C_TIM_BITMAP_LENGTH 251 -#define C_TIM_BMCAST_BIT 0x01 - -#define C_IBSS_ELEMENT_ID 6 -#define C_IBSS_ELEMENT_LENGTH 2 - -#define C_JAPAN_CALL_SIGN_ELEMENT_ID 51 -#define C_JAPAN_CALL_SIGN_ELEMENT_LNGTH 12 - -#define C_DISASSOC_REASON_CODE_LEN 2 -#define C_DISASSOC_REASON_CODE_DEFAULT 8 - -#define C_CRC_LEN 4 -#define C_NUM_SUPPORTED_RATES 8 -/****** IEEE 802.11 mac header for type data packets *************************/ -struct mac_header { - UCHAR frame_ctl_1; - UCHAR frame_ctl_2; - UCHAR duration_lsb; - UCHAR duration_msb; - UCHAR addr_1[ADDRLEN]; - UCHAR addr_2[ADDRLEN]; - UCHAR addr_3[ADDRLEN]; - UCHAR seq_frag_num[2]; -/* UCHAR addr_4[ADDRLEN]; *//* only present for AP to AP (TO DS and FROM DS */ -}; -/****** IEEE 802.11 frame element structures *********************************/ -struct essid_element -{ - UCHAR id; - UCHAR length; - UCHAR text[C_ESSID_ELEMENT_MAX_LENGTH]; -}; -struct rates_element -{ - UCHAR id; - UCHAR length; - UCHAR value[8]; -}; -struct freq_hop_element -{ - UCHAR id; - UCHAR length; - UCHAR dwell_time[2]; - UCHAR hop_set; - UCHAR hop_pattern; - UCHAR hop_index; -}; -struct tim_element -{ - UCHAR id; - UCHAR length; - UCHAR dtim_count; - UCHAR dtim_period; - UCHAR bitmap_control; - UCHAR tim[C_TIM_BITMAP_LENGTH]; -}; -struct ibss_element -{ - UCHAR id; - UCHAR length; - UCHAR atim_window[2]; -}; -struct japan_call_sign_element -{ - UCHAR id; - UCHAR length; - UCHAR call_sign[12]; -}; -/****** Beacon message structures ********************************************/ -/* .elements is a large lump of max size because elements are variable size */ -struct infra_beacon -{ - UCHAR timestamp[8]; - UCHAR beacon_intvl[2]; - UCHAR capability[2]; - UCHAR elements[sizeof(struct essid_element) - + sizeof(struct rates_element) - + sizeof(struct freq_hop_element) - + sizeof(struct japan_call_sign_element) - + sizeof(struct tim_element)]; -}; -struct adhoc_beacon -{ - UCHAR timestamp[8]; - UCHAR beacon_intvl[2]; - UCHAR capability[2]; - UCHAR elements[sizeof(struct essid_element) - + sizeof(struct rates_element) - + sizeof(struct freq_hop_element) - + sizeof(struct japan_call_sign_element) - + sizeof(struct ibss_element)]; -}; -/*****************************************************************************/ -/*****************************************************************************/ -/* #define C_MAC_HDR_2_WEP 0x40 */ -/* TX/RX CCS constants */ -#define TX_HEADER_LENGTH 0x1C -#define RX_MAC_HEADER_LENGTH 0x18 -#define TX_AUTHENTICATE_LENGTH (TX_HEADER_LENGTH + 6) -#define TX_AUTHENTICATE_LENGTH_MSB (TX_AUTHENTICATE_LENGTH >> 8) -#define TX_AUTHENTICATE_LENGTH_LSB (TX_AUTHENTICATE_LENGTH & 0xff) -#define TX_DEAUTHENTICATE_LENGTH (TX_HEADER_LENGTH + 2) -#define TX_DEAUTHENTICATE_LENGTH_MSB (TX_AUTHENTICATE_LENGTH >> 8) -#define TX_DEAUTHENTICATE_LENGTH_LSB (TX_AUTHENTICATE_LENGTH & 0xff) -#define FCS_LEN 4 - -#define ADHOC 0 -#define INFRA 1 - -#define TYPE_STA 0 -#define TYPE_AP 1 - -#define PASSIVE_SCAN 1 -#define ACTIVE_SCAN 1 - -#define PSM_CAM 0 - -/* Country codes */ -#define USA 1 -#define EUROPE 2 -#define JAPAN 3 -#define KOREA 4 -#define SPAIN 5 -#define FRANCE 6 -#define ISRAEL 7 -#define AUSTRALIA 8 -#define JAPAN_TEST 9 - -/* Hop pattern lengths */ -#define USA_HOP_MOD 79 -#define EUROPE_HOP_MOD 79 -#define JAPAN_HOP_MOD 23 -#define KOREA_HOP_MOD 23 -#define SPAIN_HOP_MOD 27 -#define FRANCE_HOP_MOD 35 -#define ISRAEL_HOP_MOD 35 -#define AUSTRALIA_HOP_MOD 47 -#define JAPAN_TEST_HOP_MOD 23 - -#define ESSID_SIZE 32 -/**********************************************************************/ -/* CIS Register Constants */ -#define CIS_OFFSET 0x0f00 -/* Configuration Option Register (0x0F00) */ -#define COR_OFFSET 0x00 -#define COR_SOFT_RESET 0x80 -#define COR_LEVEL_IRQ 0x40 -#define COR_CONFIG_NUM 0x01 -#define COR_DEFAULT (COR_LEVEL_IRQ | COR_CONFIG_NUM) - -/* Card Configuration and Status Register (0x0F01) */ -#define CCSR_OFFSET 0x01 -#define CCSR_HOST_INTR_PENDING 0x01 -#define CCSR_POWER_DOWN 0x04 - -/* HCS Interrupt Register (0x0F05) */ -#define HCS_INTR_OFFSET 0x05 -/* #define HCS_INTR_OFFSET 0x0A */ -#define HCS_INTR_CLEAR 0x00 - -/* ECF Interrupt Register (0x0F06) */ -#define ECF_INTR_OFFSET 0x06 -/* #define ECF_INTR_OFFSET 0x0C */ -#define ECF_INTR_SET 0x01 - -/* Authorization Register 0 (0x0F08) */ -#define AUTH_0_ON 0x57 - -/* Authorization Register 1 (0x0F09) */ -#define AUTH_1_ON 0x82 - -/* Program Mode Register (0x0F0A) */ -#define PC2PM 0x02 -#define PC2CAL 0x10 -#define PC2MLSE 0x20 - -/* PC Test Mode Register (0x0F0B) */ -#define PC_TEST_MODE 0x08 - -/* Frequency Control Word (0x0F10) */ -/* Range 0x02 - 0xA6 */ - -/* Test Mode Control 1-4 (0x0F14 - 0x0F17) */ - -/**********************************************************************/ - -/* Shared RAM Area */ -#define SCB_BASE 0x0000 -#define STATUS_BASE 0x0100 -#define HOST_TO_ECF_BASE 0x0200 -#define ECF_TO_HOST_BASE 0x0300 -#define CCS_BASE 0x0400 -#define RCS_BASE 0x0800 -#define INFRA_TIM_BASE 0x0C00 -#define SSID_LIST_BASE 0x0D00 -#define TX_BUF_BASE 0x1000 -#define RX_BUF_BASE 0x8000 - -#define NUMBER_OF_CCS 64 -#define NUMBER_OF_RCS 64 -/*#define NUMBER_OF_TX_CCS 14 */ -#define NUMBER_OF_TX_CCS 14 - -#define TX_BUF_SIZE (2048 - sizeof(struct tx_msg)) -#define RX_BUFF_END 0x3FFF -/* Values for buffer_status */ -#define CCS_BUFFER_FREE 0 -#define CCS_BUFFER_BUSY 1 -#define CCS_COMMAND_COMPLETE 2 -#define CCS_COMMAND_FAILED 3 - -/* Values for cmd */ -#define CCS_DOWNLOAD_STARTUP_PARAMS 1 -#define CCS_UPDATE_PARAMS 2 -#define CCS_REPORT_PARAMS 3 -#define CCS_UPDATE_MULTICAST_LIST 4 -#define CCS_UPDATE_POWER_SAVINGS_MODE 5 -#define CCS_START_NETWORK 6 -#define CCS_JOIN_NETWORK 7 -#define CCS_START_ASSOCIATION 8 -#define CCS_TX_REQUEST 9 -#define CCS_TEST_MEMORY 0xa -#define CCS_SHUTDOWN 0xb -#define CCS_DUMP_MEMORY 0xc -#define CCS_START_TIMER 0xe -#define CCS_LAST_CMD CCS_START_TIMER - -/* Values for link field */ -#define CCS_END_LIST 0xff - -/* values for buffer_status field */ -#define RCS_BUFFER_FREE 0 -#define RCS_BUFFER_BUSY 1 -#define RCS_COMPLETE 2 -#define RCS_FAILED 3 -#define RCS_BUFFER_RELEASE 0xFF - -/* values for interrupt_id field */ -#define PROCESS_RX_PACKET 0x80 /* */ -#define REJOIN_NET_COMPLETE 0x81 /* RCS ID: Rejoin Net Complete */ -#define ROAMING_INITIATED 0x82 /* RCS ID: Roaming Initiated */ -#define JAPAN_CALL_SIGN_RXD 0x83 /* RCS ID: New Japan Call Sign */ - -/*****************************************************************************/ -/* Memory types for dump memory command */ -#define C_MEM_PROG 0 -#define C_MEM_XDATA 1 -#define C_MEM_SFR 2 -#define C_MEM_IDATA 3 - -/*** Return values for hw_xmit **********/ -#define XMIT_OK (0) -#define XMIT_MSG_BAD (-1) -#define XMIT_NO_CCS (-2) -#define XMIT_NO_INTR (-3) -#define XMIT_NEED_AUTH (-4) - -/*** Values for card status */ -#define CARD_INSERTED (0) - -#define CARD_AWAITING_PARAM (1) -#define CARD_INIT_ERROR (11) - -#define CARD_DL_PARAM (2) -#define CARD_DL_PARAM_ERROR (12) - -#define CARD_DOING_ACQ (3) - -#define CARD_ACQ_COMPLETE (4) -#define CARD_ACQ_FAILED (14) - -#define CARD_AUTH_COMPLETE (5) -#define CARD_AUTH_REFUSED (15) - -#define CARD_ASSOC_COMPLETE (6) -#define CARD_ASSOC_FAILED (16) - -/*** Values for authentication_state ***********************************/ -#define UNAUTHENTICATED (0) -#define AWAITING_RESPONSE (1) -#define AUTHENTICATED (2) -#define NEED_TO_AUTH (3) - -/*** Values for authentication type ************************************/ -#define OPEN_AUTH_REQUEST (1) -#define OPEN_AUTH_RESPONSE (2) -#define BROADCAST_DEAUTH (0xc0) -/*** Values for timer functions ****************************************/ -#define TODO_NOTHING (0) -#define TODO_VERIFY_DL_START (-1) -#define TODO_START_NET (-2) -#define TODO_JOIN_NET (-3) -#define TODO_AUTHENTICATE_TIMEOUT (-4) -#define TODO_SEND_CCS (-5) -/***********************************************************************/ -/* Parameter passing structure for update/report parameter CCS's */ -struct object_id { - void *object_addr; - unsigned char object_length; -}; - -#define OBJID_network_type 0 -#define OBJID_acting_as_ap_status 1 -#define OBJID_current_ess_id 2 -#define OBJID_scanning_mode 3 -#define OBJID_power_mgt_state 4 -#define OBJID_mac_address 5 -#define OBJID_frag_threshold 6 -#define OBJID_hop_time 7 -#define OBJID_beacon_period 8 -#define OBJID_dtim_period 9 -#define OBJID_retry_max 10 -#define OBJID_ack_timeout 11 -#define OBJID_sifs 12 -#define OBJID_difs 13 -#define OBJID_pifs 14 -#define OBJID_rts_threshold 15 -#define OBJID_scan_dwell_time 16 -#define OBJID_max_scan_dwell_time 17 -#define OBJID_assoc_resp_timeout 18 -#define OBJID_adhoc_scan_cycle_max 19 -#define OBJID_infra_scan_cycle_max 20 -#define OBJID_infra_super_cycle_max 21 -#define OBJID_promiscuous_mode 22 -#define OBJID_unique_word 23 -#define OBJID_slot_time 24 -#define OBJID_roaming_low_snr 25 -#define OBJID_low_snr_count_thresh 26 -#define OBJID_infra_missed_bcn 27 -#define OBJID_adhoc_missed_bcn 28 -#define OBJID_curr_country_code 29 -#define OBJID_hop_pattern 30 -#define OBJID_reserved 31 -#define OBJID_cw_max_msb 32 -#define OBJID_cw_min_msb 33 -#define OBJID_noise_filter_gain 34 -#define OBJID_noise_limit_offset 35 -#define OBJID_det_rssi_thresh_offset 36 -#define OBJID_med_busy_thresh_offset 37 -#define OBJID_det_sync_thresh 38 -#define OBJID_test_mode 39 -#define OBJID_test_min_chan_num 40 -#define OBJID_test_max_chan_num 41 -#define OBJID_allow_bcast_ID_prbrsp 42 -#define OBJID_privacy_must_start 43 -#define OBJID_privacy_can_join 44 -#define OBJID_basic_rate_set 45 - -/**** Configuration/Status/Control Area ***************************/ -/* System Control Block (SCB) Area - * Located at Shared RAM offset 0 - */ -struct scb { - UCHAR ccs_index; - UCHAR rcs_index; -}; - -/****** Status area at Shared RAM offset 0x0100 ******************************/ -struct status { - UCHAR mrx_overflow_for_host; /* 0=ECF may write, 1=host may write*/ - UCHAR mrx_checksum_error_for_host; /* 0=ECF may write, 1=host may write*/ - UCHAR rx_hec_error_for_host; /* 0=ECF may write, 1=host may write*/ - UCHAR reserved1; - short mrx_overflow; /* ECF increments on rx overflow */ - short mrx_checksum_error; /* ECF increments on rx CRC error */ - short rx_hec_error; /* ECF incs on mac header CRC error */ - UCHAR rxnoise; /* Average RSL measurement */ -}; - -/****** Host-to-ECF Data Area at Shared RAM offset 0x200 *********************/ -struct host_to_ecf_area { - -}; - -/****** ECF-to-Host Data Area at Shared RAM offset 0x0300 ********************/ -struct startup_res_518 { - UCHAR startup_word; - UCHAR station_addr[ADDRLEN]; - UCHAR calc_prog_chksum; - UCHAR calc_cis_chksum; - UCHAR ecf_spare[7]; - UCHAR japan_call_sign[12]; -}; - -struct startup_res_6 { - UCHAR startup_word; - UCHAR station_addr[ADDRLEN]; - UCHAR reserved; - UCHAR supp_rates[8]; - UCHAR japan_call_sign[12]; - UCHAR calc_prog_chksum; - UCHAR calc_cis_chksum; - UCHAR firmware_version[3]; - UCHAR asic_version; - UCHAR tib_length; -}; - -struct start_join_net_params { - UCHAR net_type; - UCHAR ssid[ESSID_SIZE]; - UCHAR reserved; - UCHAR privacy_can_join; -}; - -/****** Command Control Structure area at Shared ram offset 0x0400 ***********/ -/* Structures for command specific parameters (ccs.var) */ -struct update_param_cmd { - UCHAR object_id; - UCHAR number_objects; - UCHAR failure_cause; -}; -struct report_param_cmd { - UCHAR object_id; - UCHAR number_objects; - UCHAR failure_cause; - UCHAR length; -}; -struct start_network_cmd { - UCHAR update_param; - UCHAR bssid[ADDRLEN]; - UCHAR net_initiated; - UCHAR net_default_tx_rate; - UCHAR encryption; -}; -struct join_network_cmd { - UCHAR update_param; - UCHAR bssid[ADDRLEN]; - UCHAR net_initiated; - UCHAR net_default_tx_rate; - UCHAR encryption; -}; -struct tx_requested_cmd { - - UCHAR tx_data_ptr[2]; - UCHAR tx_data_length[2]; - UCHAR host_reserved[2]; - UCHAR reserved[3]; - UCHAR tx_rate; - UCHAR pow_sav_mode; - UCHAR retries; - UCHAR antenna; -}; -struct tx_requested_cmd_4 { - - UCHAR tx_data_ptr[2]; - UCHAR tx_data_length[2]; - UCHAR dest_addr[ADDRLEN]; - UCHAR pow_sav_mode; - UCHAR retries; - UCHAR station_id; -}; -struct memory_dump_cmd { - UCHAR memory_type; - UCHAR memory_ptr[2]; - UCHAR length; -}; -struct update_association_cmd { - UCHAR status; - UCHAR aid[2]; -}; -struct start_timer_cmd { - UCHAR duration[2]; -}; - -struct ccs { - UCHAR buffer_status; /* 0 = buffer free, 1 = buffer busy */ - /* 2 = command complete, 3 = failed */ - UCHAR cmd; /* command to ECF */ - UCHAR link; /* link to next CCS, FF=end of list */ - /* command specific parameters */ - union { - char reserved[13]; - struct update_param_cmd update_param; - struct report_param_cmd report_param; - UCHAR nummulticast; - UCHAR mode; - struct start_network_cmd start_network; - struct join_network_cmd join_network; - struct tx_requested_cmd tx_request; - struct memory_dump_cmd memory_dump; - struct update_association_cmd update_assoc; - struct start_timer_cmd start_timer; - } var; -}; - -/*****************************************************************************/ -/* Transmit buffer structures */ -struct tib_structure { - UCHAR ccs_index; - UCHAR psm; - UCHAR pass_fail; - UCHAR retry_count; - UCHAR max_retries; - UCHAR frags_remaining; - UCHAR no_rb; - UCHAR rts_reqd; - UCHAR csma_tx_cntrl_2; - UCHAR sifs_tx_cntrl_2; - UCHAR tx_dma_addr_1[2]; - UCHAR tx_dma_addr_2[2]; - UCHAR var_dur_2mhz[2]; - UCHAR var_dur_1mhz[2]; - UCHAR max_dur_2mhz[2]; - UCHAR max_dur_1mhz[2]; - UCHAR hdr_len; - UCHAR max_frag_len[2]; - UCHAR var_len[2]; - UCHAR phy_hdr_4; - UCHAR mac_hdr_1; - UCHAR mac_hdr_2; - UCHAR sid[2]; -}; - -struct phy_header { - UCHAR sfd[2]; - UCHAR hdr_3; - UCHAR hdr_4; -}; -struct ray_rx_msg { - struct mac_header mac; - UCHAR var[]; -}; - -struct tx_msg { - struct tib_structure tib; - struct phy_header phy; - struct mac_header mac; - UCHAR var[1]; -}; - -/****** ECF Receive Control Structure (RCS) Area at Shared RAM offset 0x0800 */ -/* Structures for command specific parameters (rcs.var) */ -struct rx_packet_cmd { - UCHAR rx_data_ptr[2]; - UCHAR rx_data_length[2]; - UCHAR rx_sig_lev; - UCHAR next_frag_rcs_index; - UCHAR totalpacketlength[2]; -}; -struct rejoin_net_cmplt_cmd { - UCHAR reserved; - UCHAR bssid[ADDRLEN]; -}; -struct japan_call_sign_rxd { - UCHAR rxd_call_sign[8]; - UCHAR reserved[5]; -}; - -struct rcs { - UCHAR buffer_status; - UCHAR interrupt_id; - UCHAR link_field; - /* command specific parameters */ - union { - UCHAR reserved[13]; - struct rx_packet_cmd rx_packet; - struct rejoin_net_cmplt_cmd rejoin_net_complete; - struct japan_call_sign_rxd japan_call_sign; - } var; -}; - -/****** Startup parameter structures for both versions of firmware ***********/ -struct b4_startup_params { - UCHAR a_network_type; /* C_ADHOC, C_INFRA */ - UCHAR a_acting_as_ap_status; /* C_TYPE_STA, C_TYPE_AP */ - UCHAR a_current_ess_id[ESSID_SIZE]; /* Null terminated unless 32 long */ - UCHAR a_scanning_mode; /* passive 0, active 1 */ - UCHAR a_power_mgt_state; /* CAM 0, */ - UCHAR a_mac_addr[ADDRLEN]; /* */ - UCHAR a_frag_threshold[2]; /* 512 */ - UCHAR a_hop_time[2]; /* 16k * 2**n, n=0-4 in Kus */ - UCHAR a_beacon_period[2]; /* n * a_hop_time in Kus */ - UCHAR a_dtim_period; /* in beacons */ - UCHAR a_retry_max; /* */ - UCHAR a_ack_timeout; /* */ - UCHAR a_sifs; /* */ - UCHAR a_difs; /* */ - UCHAR a_pifs; /* */ - UCHAR a_rts_threshold[2]; /* */ - UCHAR a_scan_dwell_time[2]; /* */ - UCHAR a_max_scan_dwell_time[2]; /* */ - UCHAR a_assoc_resp_timeout_thresh; /* */ - UCHAR a_adhoc_scan_cycle_max; /* */ - UCHAR a_infra_scan_cycle_max; /* */ - UCHAR a_infra_super_scan_cycle_max; /* */ - UCHAR a_promiscuous_mode; /* */ - UCHAR a_unique_word[2]; /* */ - UCHAR a_slot_time; /* */ - UCHAR a_roaming_low_snr_thresh; /* */ - UCHAR a_low_snr_count_thresh; /* */ - UCHAR a_infra_missed_bcn_thresh; /* */ - UCHAR a_adhoc_missed_bcn_thresh; /* */ - UCHAR a_curr_country_code; /* C_USA */ - UCHAR a_hop_pattern; /* */ - UCHAR a_hop_pattern_length; /* */ -/* b4 - b5 differences start here */ - UCHAR a_cw_max; /* */ - UCHAR a_cw_min; /* */ - UCHAR a_noise_filter_gain; /* */ - UCHAR a_noise_limit_offset; /* */ - UCHAR a_det_rssi_thresh_offset; /* */ - UCHAR a_med_busy_thresh_offset; /* */ - UCHAR a_det_sync_thresh; /* */ - UCHAR a_test_mode; /* */ - UCHAR a_test_min_chan_num; /* */ - UCHAR a_test_max_chan_num; /* */ - UCHAR a_rx_tx_delay; /* */ - UCHAR a_current_bss_id[ADDRLEN]; /* */ - UCHAR a_hop_set; /* */ -}; -struct b5_startup_params { - UCHAR a_network_type; /* C_ADHOC, C_INFRA */ - UCHAR a_acting_as_ap_status; /* C_TYPE_STA, C_TYPE_AP */ - UCHAR a_current_ess_id[ESSID_SIZE]; /* Null terminated unless 32 long */ - UCHAR a_scanning_mode; /* passive 0, active 1 */ - UCHAR a_power_mgt_state; /* CAM 0, */ - UCHAR a_mac_addr[ADDRLEN]; /* */ - UCHAR a_frag_threshold[2]; /* 512 */ - UCHAR a_hop_time[2]; /* 16k * 2**n, n=0-4 in Kus */ - UCHAR a_beacon_period[2]; /* n * a_hop_time in Kus */ - UCHAR a_dtim_period; /* in beacons */ - UCHAR a_retry_max; /* 4 */ - UCHAR a_ack_timeout; /* */ - UCHAR a_sifs; /* */ - UCHAR a_difs; /* */ - UCHAR a_pifs; /* */ - UCHAR a_rts_threshold[2]; /* */ - UCHAR a_scan_dwell_time[2]; /* */ - UCHAR a_max_scan_dwell_time[2]; /* */ - UCHAR a_assoc_resp_timeout_thresh; /* */ - UCHAR a_adhoc_scan_cycle_max; /* */ - UCHAR a_infra_scan_cycle_max; /* */ - UCHAR a_infra_super_scan_cycle_max; /* */ - UCHAR a_promiscuous_mode; /* */ - UCHAR a_unique_word[2]; /* */ - UCHAR a_slot_time; /* */ - UCHAR a_roaming_low_snr_thresh; /* */ - UCHAR a_low_snr_count_thresh; /* */ - UCHAR a_infra_missed_bcn_thresh; /* */ - UCHAR a_adhoc_missed_bcn_thresh; /* */ - UCHAR a_curr_country_code; /* C_USA */ - UCHAR a_hop_pattern; /* */ - UCHAR a_hop_pattern_length; /* */ -/* b4 - b5 differences start here */ - UCHAR a_cw_max[2]; /* */ - UCHAR a_cw_min[2]; /* */ - UCHAR a_noise_filter_gain; /* */ - UCHAR a_noise_limit_offset; /* */ - UCHAR a_det_rssi_thresh_offset; /* */ - UCHAR a_med_busy_thresh_offset; /* */ - UCHAR a_det_sync_thresh; /* */ - UCHAR a_test_mode; /* */ - UCHAR a_test_min_chan_num; /* */ - UCHAR a_test_max_chan_num; /* */ - UCHAR a_allow_bcast_SSID_probe_rsp; - UCHAR a_privacy_must_start; - UCHAR a_privacy_can_join; - UCHAR a_basic_rate_set[8]; -}; - -/*****************************************************************************/ -#define RAY_IOCG_PARMS (SIOCDEVPRIVATE) -#define RAY_IOCS_PARMS (SIOCDEVPRIVATE + 1) -#define RAY_DO_CMD (SIOCDEVPRIVATE + 2) - -/****** ethernet <-> 802.11 translation **************************************/ -typedef struct snaphdr_t -{ - UCHAR dsap; - UCHAR ssap; - UCHAR ctrl; - UCHAR org[3]; - UCHAR ethertype[2]; -} snaphdr_t; - -#define BRIDGE_ENCAP 0xf80000 -#define RFC1042_ENCAP 0 -#define SNAP_ID 0x0003aaaa -#define RAY_IPX_TYPE 0x8137 -#define APPLEARP_TYPE 0x80f3 -/*****************************************************************************/ -#endif /* _RAYCTL_H_ */ diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c deleted file mode 100644 index bf72e5fd39cf..000000000000 --- a/drivers/net/wireless/rndis_wlan.c +++ /dev/null @@ -1,3760 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for RNDIS based wireless USB devices. - * - * Copyright (C) 2007 by Bjorge Dijkstra - * Copyright (C) 2008-2009 by Jussi Kivilinna - * - * Portions of this file are based on NDISwrapper project, - * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani - * http://ndiswrapper.sourceforge.net/ - */ - -// #define DEBUG // error path messages, extra info -// #define VERBOSE // more; success messages - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* NOTE: All these are settings for Broadcom chipset */ -static char modparam_country[4] = "EU"; -module_param_string(country, modparam_country, 4, 0444); -MODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU"); - -static int modparam_frameburst = 1; -module_param_named(frameburst, modparam_frameburst, int, 0444); -MODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)"); - -static int modparam_afterburner = 0; -module_param_named(afterburner, modparam_afterburner, int, 0444); -MODULE_PARM_DESC(afterburner, - "enable afterburner aka '125 High Speed Mode' (default: off)"); - -static int modparam_power_save = 0; -module_param_named(power_save, modparam_power_save, int, 0444); -MODULE_PARM_DESC(power_save, - "set power save mode: 0=off, 1=on, 2=fast (default: off)"); - -static int modparam_power_output = 3; -module_param_named(power_output, modparam_power_output, int, 0444); -MODULE_PARM_DESC(power_output, - "set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)"); - -static int modparam_roamtrigger = -70; -module_param_named(roamtrigger, modparam_roamtrigger, int, 0444); -MODULE_PARM_DESC(roamtrigger, - "set roaming dBm trigger: -80=optimize for distance, " - "-60=bandwidth (default: -70)"); - -static int modparam_roamdelta = 1; -module_param_named(roamdelta, modparam_roamdelta, int, 0444); -MODULE_PARM_DESC(roamdelta, - "set roaming tendency: 0=aggressive, 1=moderate, " - "2=conservative (default: moderate)"); - -static int modparam_workaround_interval; -module_param_named(workaround_interval, modparam_workaround_interval, - int, 0444); -MODULE_PARM_DESC(workaround_interval, - "set stall workaround interval in msecs (0=disabled) (default: 0)"); - -/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */ -#define WL_NOISE -96 /* typical noise level in dBm */ -#define WL_SIGMAX -32 /* typical maximum signal level in dBm */ - - -/* Assume that Broadcom 4320 (only chipset at time of writing known to be - * based on wireless rndis) has default txpower of 13dBm. - * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications. - * 100% : 20 mW ~ 13dBm - * 75% : 15 mW ~ 12dBm - * 50% : 10 mW ~ 10dBm - * 25% : 5 mW ~ 7dBm - */ -#define BCM4320_DEFAULT_TXPOWER_DBM_100 13 -#define BCM4320_DEFAULT_TXPOWER_DBM_75 12 -#define BCM4320_DEFAULT_TXPOWER_DBM_50 10 -#define BCM4320_DEFAULT_TXPOWER_DBM_25 7 - -/* Known device types */ -#define RNDIS_UNKNOWN 0 -#define RNDIS_BCM4320A 1 -#define RNDIS_BCM4320B 2 - - -/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c - * slightly modified for datatype endianess, etc - */ -#define NDIS_802_11_LENGTH_SSID 32 -#define NDIS_802_11_LENGTH_RATES 8 -#define NDIS_802_11_LENGTH_RATES_EX 16 - -enum ndis_80211_net_type { - NDIS_80211_TYPE_FREQ_HOP, - NDIS_80211_TYPE_DIRECT_SEQ, - NDIS_80211_TYPE_OFDM_A, - NDIS_80211_TYPE_OFDM_G -}; - -enum ndis_80211_net_infra { - NDIS_80211_INFRA_ADHOC, - NDIS_80211_INFRA_INFRA, - NDIS_80211_INFRA_AUTO_UNKNOWN -}; - -enum ndis_80211_auth_mode { - NDIS_80211_AUTH_OPEN, - NDIS_80211_AUTH_SHARED, - NDIS_80211_AUTH_AUTO_SWITCH, - NDIS_80211_AUTH_WPA, - NDIS_80211_AUTH_WPA_PSK, - NDIS_80211_AUTH_WPA_NONE, - NDIS_80211_AUTH_WPA2, - NDIS_80211_AUTH_WPA2_PSK -}; - -enum ndis_80211_encr_status { - NDIS_80211_ENCR_WEP_ENABLED, - NDIS_80211_ENCR_DISABLED, - NDIS_80211_ENCR_WEP_KEY_ABSENT, - NDIS_80211_ENCR_NOT_SUPPORTED, - NDIS_80211_ENCR_TKIP_ENABLED, - NDIS_80211_ENCR_TKIP_KEY_ABSENT, - NDIS_80211_ENCR_CCMP_ENABLED, - NDIS_80211_ENCR_CCMP_KEY_ABSENT -}; - -enum ndis_80211_priv_filter { - NDIS_80211_PRIV_ACCEPT_ALL, - NDIS_80211_PRIV_8021X_WEP -}; - -enum ndis_80211_status_type { - NDIS_80211_STATUSTYPE_AUTHENTICATION, - NDIS_80211_STATUSTYPE_MEDIASTREAMMODE, - NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST, - NDIS_80211_STATUSTYPE_RADIOSTATE, -}; - -enum ndis_80211_media_stream_mode { - NDIS_80211_MEDIA_STREAM_OFF, - NDIS_80211_MEDIA_STREAM_ON -}; - -enum ndis_80211_radio_status { - NDIS_80211_RADIO_STATUS_ON, - NDIS_80211_RADIO_STATUS_HARDWARE_OFF, - NDIS_80211_RADIO_STATUS_SOFTWARE_OFF, -}; - -enum ndis_80211_addkey_bits { - NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28), - NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29), - NDIS_80211_ADDKEY_PAIRWISE_KEY = cpu_to_le32(1 << 30), - NDIS_80211_ADDKEY_TRANSMIT_KEY = cpu_to_le32(1 << 31) -}; - -enum ndis_80211_addwep_bits { - NDIS_80211_ADDWEP_PERCLIENT_KEY = cpu_to_le32(1 << 30), - NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) -}; - -enum ndis_80211_power_mode { - NDIS_80211_POWER_MODE_CAM, - NDIS_80211_POWER_MODE_MAX_PSP, - NDIS_80211_POWER_MODE_FAST_PSP, -}; - -enum ndis_80211_pmkid_cand_list_flag_bits { - NDIS_80211_PMKID_CAND_PREAUTH = cpu_to_le32(1 << 0) -}; - -struct ndis_80211_auth_request { - __le32 length; - u8 bssid[ETH_ALEN]; - u8 padding[2]; - __le32 flags; -} __packed; - -struct ndis_80211_pmkid_candidate { - u8 bssid[ETH_ALEN]; - u8 padding[2]; - __le32 flags; -} __packed; - -struct ndis_80211_pmkid_cand_list { - __le32 version; - __le32 num_candidates; - struct ndis_80211_pmkid_candidate candidate_list[]; -} __packed; - -struct ndis_80211_status_indication { - __le32 status_type; - union { - __le32 media_stream_mode; - __le32 radio_status; - struct ndis_80211_auth_request auth_request[0]; - struct ndis_80211_pmkid_cand_list cand_list; - } u; -} __packed; - -struct ndis_80211_ssid { - __le32 length; - u8 essid[NDIS_802_11_LENGTH_SSID]; -} __packed; - -struct ndis_80211_conf_freq_hop { - __le32 length; - __le32 hop_pattern; - __le32 hop_set; - __le32 dwell_time; -} __packed; - -struct ndis_80211_conf { - __le32 length; - __le32 beacon_period; - __le32 atim_window; - __le32 ds_config; - struct ndis_80211_conf_freq_hop fh_config; -} __packed; - -struct ndis_80211_bssid_ex { - __le32 length; - u8 mac[ETH_ALEN]; - u8 padding[2]; - struct ndis_80211_ssid ssid; - __le32 privacy; - __le32 rssi; - __le32 net_type; - struct ndis_80211_conf config; - __le32 net_infra; - u8 rates[NDIS_802_11_LENGTH_RATES_EX]; - __le32 ie_length; - u8 ies[]; -} __packed; - -struct ndis_80211_bssid_list_ex { - __le32 num_items; - u8 bssid_data[]; -} __packed; - -struct ndis_80211_fixed_ies { - u8 timestamp[8]; - __le16 beacon_interval; - __le16 capabilities; -} __packed; - -struct ndis_80211_wep_key { - __le32 size; - __le32 index; - __le32 length; - u8 material[32]; -} __packed; - -struct ndis_80211_key { - __le32 size; - __le32 index; - __le32 length; - u8 bssid[ETH_ALEN]; - u8 padding[6]; - u8 rsc[8]; - u8 material[32]; -} __packed; - -struct ndis_80211_remove_key { - __le32 size; - __le32 index; - u8 bssid[ETH_ALEN]; - u8 padding[2]; -} __packed; - -struct ndis_config_param { - __le32 name_offs; - __le32 name_length; - __le32 type; - __le32 value_offs; - __le32 value_length; -} __packed; - -struct ndis_80211_assoc_info { - __le32 length; - __le16 req_ies; - struct req_ie { - __le16 capa; - __le16 listen_interval; - u8 cur_ap_address[ETH_ALEN]; - } req_ie; - __le32 req_ie_length; - __le32 offset_req_ies; - __le16 resp_ies; - struct resp_ie { - __le16 capa; - __le16 status_code; - __le16 assoc_id; - } resp_ie; - __le32 resp_ie_length; - __le32 offset_resp_ies; -} __packed; - -struct ndis_80211_capability { - __le32 length; - __le32 version; - __le32 num_pmkids; - __le32 num_auth_encr_pair; -} __packed; - -struct ndis_80211_bssid_info { - u8 bssid[ETH_ALEN]; - u8 pmkid[16]; -} __packed; - -struct ndis_80211_pmkid { - __le32 length; - __le32 bssid_info_count; - struct ndis_80211_bssid_info bssid_info[]; -} __packed; - -/* - * private data - */ -#define CAP_MODE_80211A 1 -#define CAP_MODE_80211B 2 -#define CAP_MODE_80211G 4 -#define CAP_MODE_MASK 7 - -#define WORK_LINK_UP 0 -#define WORK_LINK_DOWN 1 -#define WORK_SET_MULTICAST_LIST 2 - -#define RNDIS_WLAN_ALG_NONE 0 -#define RNDIS_WLAN_ALG_WEP (1<<0) -#define RNDIS_WLAN_ALG_TKIP (1<<1) -#define RNDIS_WLAN_ALG_CCMP (1<<2) - -#define RNDIS_WLAN_NUM_KEYS 4 -#define RNDIS_WLAN_KEY_MGMT_NONE 0 -#define RNDIS_WLAN_KEY_MGMT_802_1X (1<<0) -#define RNDIS_WLAN_KEY_MGMT_PSK (1<<1) - -#define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set)) - -static const struct ieee80211_channel rndis_channels[] = { - { .center_freq = 2412 }, - { .center_freq = 2417 }, - { .center_freq = 2422 }, - { .center_freq = 2427 }, - { .center_freq = 2432 }, - { .center_freq = 2437 }, - { .center_freq = 2442 }, - { .center_freq = 2447 }, - { .center_freq = 2452 }, - { .center_freq = 2457 }, - { .center_freq = 2462 }, - { .center_freq = 2467 }, - { .center_freq = 2472 }, - { .center_freq = 2484 }, -}; - -static const struct ieee80211_rate rndis_rates[] = { - { .bitrate = 10 }, - { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60 }, - { .bitrate = 90 }, - { .bitrate = 120 }, - { .bitrate = 180 }, - { .bitrate = 240 }, - { .bitrate = 360 }, - { .bitrate = 480 }, - { .bitrate = 540 } -}; - -static const u32 rndis_cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, -}; - -struct rndis_wlan_encr_key { - int len; - u32 cipher; - u8 material[32]; - u8 bssid[ETH_ALEN]; - bool pairwise; - bool tx_key; -}; - -/* RNDIS device private data */ -struct rndis_wlan_private { - struct usbnet *usbdev; - - struct wireless_dev wdev; - - struct cfg80211_scan_request *scan_request; - - struct workqueue_struct *workqueue; - struct delayed_work dev_poller_work; - struct delayed_work scan_work; - struct work_struct work; - struct mutex command_lock; - unsigned long work_pending; - int last_qual; - s32 cqm_rssi_thold; - u32 cqm_rssi_hyst; - int last_cqm_event_rssi; - - struct ieee80211_supported_band band; - struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; - struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)]; - u32 cipher_suites[ARRAY_SIZE(rndis_cipher_suites)]; - - int device_type; - int caps; - int multicast_size; - - /* module parameters */ - char param_country[4]; - int param_frameburst; - int param_afterburner; - int param_power_save; - int param_power_output; - int param_roamtrigger; - int param_roamdelta; - u32 param_workaround_interval; - - /* hardware state */ - bool radio_on; - int power_mode; - int infra_mode; - bool connected; - u8 bssid[ETH_ALEN]; - u32 current_command_oid; - - /* encryption stuff */ - u8 encr_tx_key_index; - struct rndis_wlan_encr_key encr_keys[RNDIS_WLAN_NUM_KEYS]; - int wpa_version; - - u8 command_buffer[COMMAND_BUFFER_SIZE]; -}; - -/* - * cfg80211 ops - */ -static int rndis_change_virtual_intf(struct wiphy *wiphy, - struct net_device *dev, - enum nl80211_iftype type, - struct vif_params *params); - -static int rndis_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request); - -static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); - -static int rndis_set_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - enum nl80211_tx_power_setting type, - int mbm); -static int rndis_get_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - int *dbm); - -static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_connect_params *sme); - -static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code); - -static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_ibss_params *params); - -static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev); - -static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, - int link_id, u8 key_index, bool pairwise, - const u8 *mac_addr, struct key_params *params); - -static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, - int link_id, u8 key_index, bool pairwise, - const u8 *mac_addr); - -static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, - int link_id, u8 key_index, bool unicast, - bool multicast); - -static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_info *sinfo); - -static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo); - -static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_pmksa *pmksa); - -static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_pmksa *pmksa); - -static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev); - -static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, - bool enabled, int timeout); - -static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, - struct net_device *dev, - s32 rssi_thold, u32 rssi_hyst); - -static const struct cfg80211_ops rndis_config_ops = { - .change_virtual_intf = rndis_change_virtual_intf, - .scan = rndis_scan, - .set_wiphy_params = rndis_set_wiphy_params, - .set_tx_power = rndis_set_tx_power, - .get_tx_power = rndis_get_tx_power, - .connect = rndis_connect, - .disconnect = rndis_disconnect, - .join_ibss = rndis_join_ibss, - .leave_ibss = rndis_leave_ibss, - .add_key = rndis_add_key, - .del_key = rndis_del_key, - .set_default_key = rndis_set_default_key, - .get_station = rndis_get_station, - .dump_station = rndis_dump_station, - .set_pmksa = rndis_set_pmksa, - .del_pmksa = rndis_del_pmksa, - .flush_pmksa = rndis_flush_pmksa, - .set_power_mgmt = rndis_set_power_mgmt, - .set_cqm_rssi_config = rndis_set_cqm_rssi_config, -}; - -static void *rndis_wiphy_privid = &rndis_wiphy_privid; - - -static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) -{ - return (struct rndis_wlan_private *)dev->driver_priv; -} - -static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv) -{ - switch (priv->param_power_output) { - default: - case 3: - return BCM4320_DEFAULT_TXPOWER_DBM_100; - case 2: - return BCM4320_DEFAULT_TXPOWER_DBM_75; - case 1: - return BCM4320_DEFAULT_TXPOWER_DBM_50; - case 0: - return BCM4320_DEFAULT_TXPOWER_DBM_25; - } -} - -static bool is_wpa_key(struct rndis_wlan_private *priv, u8 idx) -{ - int cipher = priv->encr_keys[idx].cipher; - - return (cipher == WLAN_CIPHER_SUITE_CCMP || - cipher == WLAN_CIPHER_SUITE_TKIP); -} - -static int rndis_cipher_to_alg(u32 cipher) -{ - switch (cipher) { - default: - return RNDIS_WLAN_ALG_NONE; - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - return RNDIS_WLAN_ALG_WEP; - case WLAN_CIPHER_SUITE_TKIP: - return RNDIS_WLAN_ALG_TKIP; - case WLAN_CIPHER_SUITE_CCMP: - return RNDIS_WLAN_ALG_CCMP; - } -} - -static int rndis_akm_suite_to_key_mgmt(u32 akm_suite) -{ - switch (akm_suite) { - default: - return RNDIS_WLAN_KEY_MGMT_NONE; - case WLAN_AKM_SUITE_8021X: - return RNDIS_WLAN_KEY_MGMT_802_1X; - case WLAN_AKM_SUITE_PSK: - return RNDIS_WLAN_KEY_MGMT_PSK; - } -} - -#ifdef DEBUG -static const char *oid_to_string(u32 oid) -{ - switch (oid) { -#define OID_STR(oid) case oid: return(#oid) - /* from rndis_host.h */ - OID_STR(RNDIS_OID_802_3_PERMANENT_ADDRESS); - OID_STR(RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE); - OID_STR(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); - OID_STR(RNDIS_OID_GEN_PHYSICAL_MEDIUM); - - /* from rndis_wlan.c */ - OID_STR(RNDIS_OID_GEN_LINK_SPEED); - OID_STR(RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER); - - OID_STR(RNDIS_OID_GEN_XMIT_OK); - OID_STR(RNDIS_OID_GEN_RCV_OK); - OID_STR(RNDIS_OID_GEN_XMIT_ERROR); - OID_STR(RNDIS_OID_GEN_RCV_ERROR); - OID_STR(RNDIS_OID_GEN_RCV_NO_BUFFER); - - OID_STR(RNDIS_OID_802_3_CURRENT_ADDRESS); - OID_STR(RNDIS_OID_802_3_MULTICAST_LIST); - OID_STR(RNDIS_OID_802_3_MAXIMUM_LIST_SIZE); - - OID_STR(RNDIS_OID_802_11_BSSID); - OID_STR(RNDIS_OID_802_11_SSID); - OID_STR(RNDIS_OID_802_11_INFRASTRUCTURE_MODE); - OID_STR(RNDIS_OID_802_11_ADD_WEP); - OID_STR(RNDIS_OID_802_11_REMOVE_WEP); - OID_STR(RNDIS_OID_802_11_DISASSOCIATE); - OID_STR(RNDIS_OID_802_11_AUTHENTICATION_MODE); - OID_STR(RNDIS_OID_802_11_PRIVACY_FILTER); - OID_STR(RNDIS_OID_802_11_BSSID_LIST_SCAN); - OID_STR(RNDIS_OID_802_11_ENCRYPTION_STATUS); - OID_STR(RNDIS_OID_802_11_ADD_KEY); - OID_STR(RNDIS_OID_802_11_REMOVE_KEY); - OID_STR(RNDIS_OID_802_11_ASSOCIATION_INFORMATION); - OID_STR(RNDIS_OID_802_11_CAPABILITY); - OID_STR(RNDIS_OID_802_11_PMKID); - OID_STR(RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED); - OID_STR(RNDIS_OID_802_11_NETWORK_TYPE_IN_USE); - OID_STR(RNDIS_OID_802_11_TX_POWER_LEVEL); - OID_STR(RNDIS_OID_802_11_RSSI); - OID_STR(RNDIS_OID_802_11_RSSI_TRIGGER); - OID_STR(RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD); - OID_STR(RNDIS_OID_802_11_RTS_THRESHOLD); - OID_STR(RNDIS_OID_802_11_SUPPORTED_RATES); - OID_STR(RNDIS_OID_802_11_CONFIGURATION); - OID_STR(RNDIS_OID_802_11_POWER_MODE); - OID_STR(RNDIS_OID_802_11_BSSID_LIST); -#undef OID_STR - } - - return "?"; -} -#else -static const char *oid_to_string(u32 oid) -{ - return "?"; -} -#endif - -/* translate error code */ -static int rndis_error_status(__le32 rndis_status) -{ - int ret = -EINVAL; - switch (le32_to_cpu(rndis_status)) { - case RNDIS_STATUS_SUCCESS: - ret = 0; - break; - case RNDIS_STATUS_FAILURE: - case RNDIS_STATUS_INVALID_DATA: - ret = -EINVAL; - break; - case RNDIS_STATUS_NOT_SUPPORTED: - ret = -EOPNOTSUPP; - break; - case RNDIS_STATUS_ADAPTER_NOT_READY: - case RNDIS_STATUS_ADAPTER_NOT_OPEN: - ret = -EBUSY; - break; - } - return ret; -} - -static int rndis_query_oid(struct usbnet *dev, u32 oid, void *data, int *len) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); - union { - void *buf; - struct rndis_msg_hdr *header; - struct rndis_query *get; - struct rndis_query_c *get_c; - } u; - int ret; - size_t buflen, resplen, respoffs, copylen; - - buflen = *len + sizeof(*u.get); - if (buflen < CONTROL_BUFFER_SIZE) - buflen = CONTROL_BUFFER_SIZE; - - if (buflen > COMMAND_BUFFER_SIZE) { - u.buf = kmalloc(buflen, GFP_KERNEL); - if (!u.buf) - return -ENOMEM; - } else { - u.buf = priv->command_buffer; - } - - mutex_lock(&priv->command_lock); - - memset(u.get, 0, sizeof *u.get); - u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY); - u.get->msg_len = cpu_to_le32(sizeof *u.get); - u.get->oid = cpu_to_le32(oid); - - priv->current_command_oid = oid; - ret = rndis_command(dev, u.header, buflen); - priv->current_command_oid = 0; - if (ret < 0) - netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", - __func__, oid_to_string(oid), ret, - le32_to_cpu(u.get_c->status)); - - if (ret == 0) { - resplen = le32_to_cpu(u.get_c->len); - respoffs = le32_to_cpu(u.get_c->offset) + 8; - - if (respoffs > buflen) { - /* Device returned data offset outside buffer, error. */ - netdev_dbg(dev->net, - "%s(%s): received invalid data offset: %zu > %zu\n", - __func__, oid_to_string(oid), respoffs, buflen); - - ret = -EINVAL; - goto exit_unlock; - } - - copylen = min(resplen, buflen - respoffs); - - if (copylen > *len) - copylen = *len; - - memcpy(data, u.buf + respoffs, copylen); - - *len = resplen; - - ret = rndis_error_status(u.get_c->status); - if (ret < 0) - netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", - __func__, oid_to_string(oid), - le32_to_cpu(u.get_c->status), ret); - } - -exit_unlock: - mutex_unlock(&priv->command_lock); - - if (u.buf != priv->command_buffer) - kfree(u.buf); - return ret; -} - -static int rndis_set_oid(struct usbnet *dev, u32 oid, const void *data, - int len) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(dev); - union { - void *buf; - struct rndis_msg_hdr *header; - struct rndis_set *set; - struct rndis_set_c *set_c; - } u; - int ret, buflen; - - buflen = len + sizeof(*u.set); - if (buflen < CONTROL_BUFFER_SIZE) - buflen = CONTROL_BUFFER_SIZE; - - if (buflen > COMMAND_BUFFER_SIZE) { - u.buf = kmalloc(buflen, GFP_KERNEL); - if (!u.buf) - return -ENOMEM; - } else { - u.buf = priv->command_buffer; - } - - mutex_lock(&priv->command_lock); - - memset(u.set, 0, sizeof *u.set); - u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); - u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len); - u.set->oid = cpu_to_le32(oid); - u.set->len = cpu_to_le32(len); - u.set->offset = cpu_to_le32(sizeof(*u.set) - 8); - u.set->handle = cpu_to_le32(0); - memcpy(u.buf + sizeof(*u.set), data, len); - - priv->current_command_oid = oid; - ret = rndis_command(dev, u.header, buflen); - priv->current_command_oid = 0; - if (ret < 0) - netdev_dbg(dev->net, "%s(%s): rndis_command() failed, %d (%08x)\n", - __func__, oid_to_string(oid), ret, - le32_to_cpu(u.set_c->status)); - - if (ret == 0) { - ret = rndis_error_status(u.set_c->status); - - if (ret < 0) - netdev_dbg(dev->net, "%s(%s): device returned error, 0x%08x (%d)\n", - __func__, oid_to_string(oid), - le32_to_cpu(u.set_c->status), ret); - } - - mutex_unlock(&priv->command_lock); - - if (u.buf != priv->command_buffer) - kfree(u.buf); - return ret; -} - -static int rndis_reset(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct rndis_reset *reset; - int ret; - - mutex_lock(&priv->command_lock); - - reset = (void *)priv->command_buffer; - memset(reset, 0, sizeof(*reset)); - reset->msg_type = cpu_to_le32(RNDIS_MSG_RESET); - reset->msg_len = cpu_to_le32(sizeof(*reset)); - priv->current_command_oid = 0; - ret = rndis_command(usbdev, (void *)reset, CONTROL_BUFFER_SIZE); - - mutex_unlock(&priv->command_lock); - - if (ret < 0) - return ret; - return 0; -} - -/* - * Specs say that we can only set config parameters only soon after device - * initialization. - * value_type: 0 = u32, 2 = unicode string - */ -static int rndis_set_config_parameter(struct usbnet *dev, char *param, - int value_type, void *value) -{ - struct ndis_config_param *infobuf; - int value_len, info_len, param_len, ret, i; - __le16 *unibuf; - __le32 *dst_value; - - if (value_type == 0) - value_len = sizeof(__le32); - else if (value_type == 2) - value_len = strlen(value) * sizeof(__le16); - else - return -EINVAL; - - param_len = strlen(param) * sizeof(__le16); - info_len = sizeof(*infobuf) + param_len + value_len; - -#ifdef DEBUG - info_len += 12; -#endif - infobuf = kmalloc(info_len, GFP_KERNEL); - if (!infobuf) - return -ENOMEM; - -#ifdef DEBUG - info_len -= 12; - /* extra 12 bytes are for padding (debug output) */ - memset(infobuf, 0xCC, info_len + 12); -#endif - - if (value_type == 2) - netdev_dbg(dev->net, "setting config parameter: %s, value: %s\n", - param, (u8 *)value); - else - netdev_dbg(dev->net, "setting config parameter: %s, value: %d\n", - param, *(u32 *)value); - - infobuf->name_offs = cpu_to_le32(sizeof(*infobuf)); - infobuf->name_length = cpu_to_le32(param_len); - infobuf->type = cpu_to_le32(value_type); - infobuf->value_offs = cpu_to_le32(sizeof(*infobuf) + param_len); - infobuf->value_length = cpu_to_le32(value_len); - - /* simple string to unicode string conversion */ - unibuf = (void *)infobuf + sizeof(*infobuf); - for (i = 0; i < param_len / sizeof(__le16); i++) - unibuf[i] = cpu_to_le16(param[i]); - - if (value_type == 2) { - unibuf = (void *)infobuf + sizeof(*infobuf) + param_len; - for (i = 0; i < value_len / sizeof(__le16); i++) - unibuf[i] = cpu_to_le16(((u8 *)value)[i]); - } else { - dst_value = (void *)infobuf + sizeof(*infobuf) + param_len; - *dst_value = cpu_to_le32(*(u32 *)value); - } - -#ifdef DEBUG - netdev_dbg(dev->net, "info buffer (len: %d)\n", info_len); - for (i = 0; i < info_len; i += 12) { - u32 *tmp = (u32 *)((u8 *)infobuf + i); - netdev_dbg(dev->net, "%08X:%08X:%08X\n", - cpu_to_be32(tmp[0]), - cpu_to_be32(tmp[1]), - cpu_to_be32(tmp[2])); - } -#endif - - ret = rndis_set_oid(dev, RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER, - infobuf, info_len); - if (ret != 0) - netdev_dbg(dev->net, "setting rndis config parameter failed, %d\n", - ret); - - kfree(infobuf); - return ret; -} - -static int rndis_set_config_parameter_str(struct usbnet *dev, - char *param, char *value) -{ - return rndis_set_config_parameter(dev, param, 2, value); -} - -/* - * data conversion functions - */ -static int level_to_qual(int level) -{ - int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE); - return qual >= 0 ? (qual <= 100 ? qual : 100) : 0; -} - -/* - * common functions - */ -static int set_infra_mode(struct usbnet *usbdev, int mode); -static void restore_keys(struct usbnet *usbdev); -static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, - bool *matched); - -static int rndis_start_bssid_list_scan(struct usbnet *usbdev) -{ - __le32 tmp; - - /* Note: RNDIS_OID_802_11_BSSID_LIST_SCAN clears internal BSS list. */ - tmp = cpu_to_le32(1); - return rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST_SCAN, &tmp, - sizeof(tmp)); -} - -static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int ret; - - ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_SSID, - ssid, sizeof(*ssid)); - if (ret < 0) { - netdev_warn(usbdev->net, "setting SSID failed (%08X)\n", ret); - return ret; - } - if (ret == 0) { - priv->radio_on = true; - netdev_dbg(usbdev->net, "%s(): radio_on = true\n", __func__); - } - - return ret; -} - -static int set_bssid(struct usbnet *usbdev, const u8 *bssid) -{ - int ret; - - ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_BSSID, - bssid, ETH_ALEN); - if (ret < 0) { - netdev_warn(usbdev->net, "setting BSSID[%pM] failed (%08X)\n", - bssid, ret); - return ret; - } - - return ret; -} - -static int clear_bssid(struct usbnet *usbdev) -{ - static const u8 broadcast_mac[ETH_ALEN] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - - return set_bssid(usbdev, broadcast_mac); -} - -static int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN]) -{ - int ret, len; - - len = ETH_ALEN; - ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID, - bssid, &len); - - if (ret != 0) - eth_zero_addr(bssid); - - return ret; -} - -static int get_association_info(struct usbnet *usbdev, - struct ndis_80211_assoc_info *info, int len) -{ - return rndis_query_oid(usbdev, - RNDIS_OID_802_11_ASSOCIATION_INFORMATION, - info, &len); -} - -static bool is_associated(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - u8 bssid[ETH_ALEN]; - - if (!priv->radio_on) - return false; - - return (get_bssid(usbdev, bssid) == 0 && !is_zero_ether_addr(bssid)); -} - -static int disassociate(struct usbnet *usbdev, bool reset_ssid) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ndis_80211_ssid ssid; - int i, ret = 0; - - if (priv->radio_on) { - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_DISASSOCIATE, - NULL, 0); - if (ret == 0) { - priv->radio_on = false; - netdev_dbg(usbdev->net, "%s(): radio_on = false\n", - __func__); - - if (reset_ssid) - msleep(100); - } - } - - /* disassociate causes radio to be turned off; if reset_ssid - * is given, set random ssid to enable radio */ - if (reset_ssid) { - /* Set device to infrastructure mode so we don't get ad-hoc - * 'media connect' indications with the random ssid. - */ - set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); - - ssid.length = cpu_to_le32(sizeof(ssid.essid)); - get_random_bytes(&ssid.essid[2], sizeof(ssid.essid)-2); - ssid.essid[0] = 0x1; - ssid.essid[1] = 0xff; - for (i = 2; i < sizeof(ssid.essid); i++) - ssid.essid[i] = 0x1 + (ssid.essid[i] * 0xfe / 0xff); - ret = set_essid(usbdev, &ssid); - } - return ret; -} - -static int set_auth_mode(struct usbnet *usbdev, u32 wpa_version, - enum nl80211_auth_type auth_type, int keymgmt) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - __le32 tmp; - int auth_mode, ret; - - netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x authalg=0x%x keymgmt=0x%x\n", - __func__, wpa_version, auth_type, keymgmt); - - if (wpa_version & NL80211_WPA_VERSION_2) { - if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) - auth_mode = NDIS_80211_AUTH_WPA2; - else - auth_mode = NDIS_80211_AUTH_WPA2_PSK; - } else if (wpa_version & NL80211_WPA_VERSION_1) { - if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) - auth_mode = NDIS_80211_AUTH_WPA; - else if (keymgmt & RNDIS_WLAN_KEY_MGMT_PSK) - auth_mode = NDIS_80211_AUTH_WPA_PSK; - else - auth_mode = NDIS_80211_AUTH_WPA_NONE; - } else if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) - auth_mode = NDIS_80211_AUTH_SHARED; - else if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) - auth_mode = NDIS_80211_AUTH_OPEN; - else if (auth_type == NL80211_AUTHTYPE_AUTOMATIC) - auth_mode = NDIS_80211_AUTH_AUTO_SWITCH; - else - return -ENOTSUPP; - - tmp = cpu_to_le32(auth_mode); - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_AUTHENTICATION_MODE, - &tmp, sizeof(tmp)); - if (ret != 0) { - netdev_warn(usbdev->net, "setting auth mode failed (%08X)\n", - ret); - return ret; - } - - priv->wpa_version = wpa_version; - - return 0; -} - -static int set_priv_filter(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - __le32 tmp; - - netdev_dbg(usbdev->net, "%s(): wpa_version=0x%x\n", - __func__, priv->wpa_version); - - if (priv->wpa_version & NL80211_WPA_VERSION_2 || - priv->wpa_version & NL80211_WPA_VERSION_1) - tmp = cpu_to_le32(NDIS_80211_PRIV_8021X_WEP); - else - tmp = cpu_to_le32(NDIS_80211_PRIV_ACCEPT_ALL); - - return rndis_set_oid(usbdev, - RNDIS_OID_802_11_PRIVACY_FILTER, &tmp, - sizeof(tmp)); -} - -static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) -{ - __le32 tmp; - int encr_mode, ret; - - netdev_dbg(usbdev->net, "%s(): cipher_pair=0x%x cipher_group=0x%x\n", - __func__, pairwise, groupwise); - - if (pairwise & RNDIS_WLAN_ALG_CCMP) - encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; - else if (pairwise & RNDIS_WLAN_ALG_TKIP) - encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; - else if (pairwise & RNDIS_WLAN_ALG_WEP) - encr_mode = NDIS_80211_ENCR_WEP_ENABLED; - else if (groupwise & RNDIS_WLAN_ALG_CCMP) - encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; - else if (groupwise & RNDIS_WLAN_ALG_TKIP) - encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; - else - encr_mode = NDIS_80211_ENCR_DISABLED; - - tmp = cpu_to_le32(encr_mode); - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_ENCRYPTION_STATUS, &tmp, - sizeof(tmp)); - if (ret != 0) { - netdev_warn(usbdev->net, "setting encr mode failed (%08X)\n", - ret); - return ret; - } - - return 0; -} - -static int set_infra_mode(struct usbnet *usbdev, int mode) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - __le32 tmp; - int ret; - - netdev_dbg(usbdev->net, "%s(): infra_mode=0x%x\n", - __func__, priv->infra_mode); - - tmp = cpu_to_le32(mode); - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_INFRASTRUCTURE_MODE, - &tmp, sizeof(tmp)); - if (ret != 0) { - netdev_warn(usbdev->net, "setting infra mode failed (%08X)\n", - ret); - return ret; - } - - /* NDIS drivers clear keys when infrastructure mode is - * changed. But Linux tools assume otherwise. So set the - * keys */ - restore_keys(usbdev); - - priv->infra_mode = mode; - return 0; -} - -static int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold) -{ - __le32 tmp; - - netdev_dbg(usbdev->net, "%s(): %i\n", __func__, rts_threshold); - - if (rts_threshold == -1 || rts_threshold > 2347) - rts_threshold = 2347; - - tmp = cpu_to_le32(rts_threshold); - return rndis_set_oid(usbdev, - RNDIS_OID_802_11_RTS_THRESHOLD, - &tmp, sizeof(tmp)); -} - -static int set_frag_threshold(struct usbnet *usbdev, u32 frag_threshold) -{ - __le32 tmp; - - netdev_dbg(usbdev->net, "%s(): %i\n", __func__, frag_threshold); - - if (frag_threshold < 256 || frag_threshold > 2346) - frag_threshold = 2346; - - tmp = cpu_to_le32(frag_threshold); - return rndis_set_oid(usbdev, - RNDIS_OID_802_11_FRAGMENTATION_THRESHOLD, - &tmp, sizeof(tmp)); -} - -static void set_default_iw_params(struct usbnet *usbdev) -{ - set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); - set_auth_mode(usbdev, 0, NL80211_AUTHTYPE_OPEN_SYSTEM, - RNDIS_WLAN_KEY_MGMT_NONE); - set_priv_filter(usbdev); - set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); -} - -static int deauthenticate(struct usbnet *usbdev) -{ - int ret; - - ret = disassociate(usbdev, true); - set_default_iw_params(usbdev); - return ret; -} - -static int set_channel(struct usbnet *usbdev, int channel) -{ - struct ndis_80211_conf config; - unsigned int dsconfig; - int len, ret; - - netdev_dbg(usbdev->net, "%s(%d)\n", __func__, channel); - - /* this OID is valid only when not associated */ - if (is_associated(usbdev)) - return 0; - - dsconfig = 1000 * - ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ); - - len = sizeof(config); - ret = rndis_query_oid(usbdev, - RNDIS_OID_802_11_CONFIGURATION, - &config, &len); - if (ret < 0) { - netdev_dbg(usbdev->net, "%s(): querying configuration failed\n", - __func__); - return ret; - } - - config.ds_config = cpu_to_le32(dsconfig); - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_CONFIGURATION, - &config, sizeof(config)); - - netdev_dbg(usbdev->net, "%s(): %d -> %d\n", __func__, channel, ret); - - return ret; -} - -static struct ieee80211_channel *get_current_channel(struct usbnet *usbdev, - u32 *beacon_period) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ieee80211_channel *channel; - struct ndis_80211_conf config; - int len, ret; - - /* Get channel and beacon interval */ - len = sizeof(config); - ret = rndis_query_oid(usbdev, - RNDIS_OID_802_11_CONFIGURATION, - &config, &len); - netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_CONFIGURATION -> %d\n", - __func__, ret); - if (ret < 0) - return NULL; - - channel = ieee80211_get_channel(priv->wdev.wiphy, - KHZ_TO_MHZ(le32_to_cpu(config.ds_config))); - if (!channel) - return NULL; - - if (beacon_period) - *beacon_period = le32_to_cpu(config.beacon_period); - return channel; -} - -/* index must be 0 - N, as per NDIS */ -static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len, - u8 index) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ndis_80211_wep_key ndis_key; - u32 cipher; - int ret; - - netdev_dbg(usbdev->net, "%s(idx: %d, len: %d)\n", - __func__, index, key_len); - - if (index >= RNDIS_WLAN_NUM_KEYS) - return -EINVAL; - - if (key_len == 5) - cipher = WLAN_CIPHER_SUITE_WEP40; - else if (key_len == 13) - cipher = WLAN_CIPHER_SUITE_WEP104; - else - return -EINVAL; - - memset(&ndis_key, 0, sizeof(ndis_key)); - - ndis_key.size = cpu_to_le32(sizeof(ndis_key)); - ndis_key.length = cpu_to_le32(key_len); - ndis_key.index = cpu_to_le32(index); - memcpy(&ndis_key.material, key, key_len); - - if (index == priv->encr_tx_key_index) { - ndis_key.index |= NDIS_80211_ADDWEP_TRANSMIT_KEY; - ret = set_encr_mode(usbdev, RNDIS_WLAN_ALG_WEP, - RNDIS_WLAN_ALG_NONE); - if (ret) - netdev_warn(usbdev->net, "encryption couldn't be enabled (%08X)\n", - ret); - } - - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_ADD_WEP, &ndis_key, - sizeof(ndis_key)); - if (ret != 0) { - netdev_warn(usbdev->net, "adding encryption key %d failed (%08X)\n", - index + 1, ret); - return ret; - } - - priv->encr_keys[index].len = key_len; - priv->encr_keys[index].cipher = cipher; - memcpy(&priv->encr_keys[index].material, key, key_len); - eth_broadcast_addr(priv->encr_keys[index].bssid); - - return 0; -} - -static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, - u8 index, const u8 *addr, const u8 *rx_seq, - int seq_len, u32 cipher, __le32 flags) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ndis_80211_key ndis_key; - bool is_addr_ok; - int ret; - - if (index >= RNDIS_WLAN_NUM_KEYS) { - netdev_dbg(usbdev->net, "%s(): index out of range (%i)\n", - __func__, index); - return -EINVAL; - } - if (key_len > sizeof(ndis_key.material) || key_len < 0) { - netdev_dbg(usbdev->net, "%s(): key length out of range (%i)\n", - __func__, key_len); - return -EINVAL; - } - if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) { - if (!rx_seq || seq_len <= 0) { - netdev_dbg(usbdev->net, "%s(): recv seq flag without buffer\n", - __func__); - return -EINVAL; - } - if (rx_seq && seq_len > sizeof(ndis_key.rsc)) { - netdev_dbg(usbdev->net, "%s(): too big recv seq buffer\n", __func__); - return -EINVAL; - } - } - - is_addr_ok = addr && !is_zero_ether_addr(addr) && - !is_broadcast_ether_addr(addr); - if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !is_addr_ok) { - netdev_dbg(usbdev->net, "%s(): pairwise but bssid invalid (%pM)\n", - __func__, addr); - return -EINVAL; - } - - netdev_dbg(usbdev->net, "%s(%i): flags:%i%i%i\n", - __func__, index, - !!(flags & NDIS_80211_ADDKEY_TRANSMIT_KEY), - !!(flags & NDIS_80211_ADDKEY_PAIRWISE_KEY), - !!(flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ)); - - memset(&ndis_key, 0, sizeof(ndis_key)); - - ndis_key.size = cpu_to_le32(sizeof(ndis_key) - - sizeof(ndis_key.material) + key_len); - ndis_key.length = cpu_to_le32(key_len); - ndis_key.index = cpu_to_le32(index) | flags; - - if (cipher == WLAN_CIPHER_SUITE_TKIP && key_len == 32) { - /* wpa_supplicant gives us the Michael MIC RX/TX keys in - * different order than NDIS spec, so swap the order here. */ - memcpy(ndis_key.material, key, 16); - memcpy(ndis_key.material + 16, key + 24, 8); - memcpy(ndis_key.material + 24, key + 16, 8); - } else - memcpy(ndis_key.material, key, key_len); - - if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) - memcpy(ndis_key.rsc, rx_seq, seq_len); - - if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) { - /* pairwise key */ - memcpy(ndis_key.bssid, addr, ETH_ALEN); - } else { - /* group key */ - if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) - eth_broadcast_addr(ndis_key.bssid); - else - get_bssid(usbdev, ndis_key.bssid); - } - - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_ADD_KEY, &ndis_key, - le32_to_cpu(ndis_key.size)); - netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_ADD_KEY -> %08X\n", - __func__, ret); - if (ret != 0) - return ret; - - memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index])); - priv->encr_keys[index].len = key_len; - priv->encr_keys[index].cipher = cipher; - memcpy(&priv->encr_keys[index].material, key, key_len); - if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) - memcpy(&priv->encr_keys[index].bssid, ndis_key.bssid, ETH_ALEN); - else - eth_broadcast_addr(priv->encr_keys[index].bssid); - - if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY) - priv->encr_tx_key_index = index; - - return 0; -} - -static int restore_key(struct usbnet *usbdev, u8 key_idx) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct rndis_wlan_encr_key key; - - if (is_wpa_key(priv, key_idx)) - return 0; - - key = priv->encr_keys[key_idx]; - - netdev_dbg(usbdev->net, "%s(): %i:%i\n", __func__, key_idx, key.len); - - if (key.len == 0) - return 0; - - return add_wep_key(usbdev, key.material, key.len, key_idx); -} - -static void restore_keys(struct usbnet *usbdev) -{ - int i; - - for (i = 0; i < 4; i++) - restore_key(usbdev, i); -} - -static void clear_key(struct rndis_wlan_private *priv, u8 idx) -{ - memset(&priv->encr_keys[idx], 0, sizeof(priv->encr_keys[idx])); -} - -/* remove_key is for both wep and wpa */ -static int remove_key(struct usbnet *usbdev, u8 index, const u8 *bssid) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ndis_80211_remove_key remove_key; - __le32 keyindex; - bool is_wpa; - int ret; - - if (index >= RNDIS_WLAN_NUM_KEYS) - return -ENOENT; - - if (priv->encr_keys[index].len == 0) - return 0; - - is_wpa = is_wpa_key(priv, index); - - netdev_dbg(usbdev->net, "%s(): %i:%s:%i\n", - __func__, index, is_wpa ? "wpa" : "wep", - priv->encr_keys[index].len); - - clear_key(priv, index); - - if (is_wpa) { - remove_key.size = cpu_to_le32(sizeof(remove_key)); - remove_key.index = cpu_to_le32(index); - if (bssid) { - /* pairwise key */ - if (!is_broadcast_ether_addr(bssid)) - remove_key.index |= - NDIS_80211_ADDKEY_PAIRWISE_KEY; - memcpy(remove_key.bssid, bssid, - sizeof(remove_key.bssid)); - } else - memset(remove_key.bssid, 0xff, - sizeof(remove_key.bssid)); - - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_REMOVE_KEY, - &remove_key, sizeof(remove_key)); - if (ret != 0) - return ret; - } else { - keyindex = cpu_to_le32(index); - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_11_REMOVE_WEP, - &keyindex, sizeof(keyindex)); - if (ret != 0) { - netdev_warn(usbdev->net, - "removing encryption key %d failed (%08X)\n", - index, ret); - return ret; - } - } - - /* if it is transmit key, disable encryption */ - if (index == priv->encr_tx_key_index) - set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); - - return 0; -} - -static void set_multicast_list(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct netdev_hw_addr *ha; - __le32 filter, basefilter; - int ret; - char *mc_addrs = NULL; - int mc_count; - - basefilter = filter = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | - RNDIS_PACKET_TYPE_BROADCAST); - - if (usbdev->net->flags & IFF_PROMISC) { - filter |= cpu_to_le32(RNDIS_PACKET_TYPE_PROMISCUOUS | - RNDIS_PACKET_TYPE_ALL_LOCAL); - } else if (usbdev->net->flags & IFF_ALLMULTI) { - filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); - } - - if (filter != basefilter) - goto set_filter; - - /* - * mc_list should be accessed holding the lock, so copy addresses to - * local buffer first. - */ - netif_addr_lock_bh(usbdev->net); - mc_count = netdev_mc_count(usbdev->net); - if (mc_count > priv->multicast_size) { - filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); - } else if (mc_count) { - int i = 0; - - mc_addrs = kmalloc_array(mc_count, ETH_ALEN, GFP_ATOMIC); - if (!mc_addrs) { - netif_addr_unlock_bh(usbdev->net); - return; - } - - netdev_for_each_mc_addr(ha, usbdev->net) - memcpy(mc_addrs + i++ * ETH_ALEN, - ha->addr, ETH_ALEN); - } - netif_addr_unlock_bh(usbdev->net); - - if (filter != basefilter) - goto set_filter; - - if (mc_count) { - ret = rndis_set_oid(usbdev, - RNDIS_OID_802_3_MULTICAST_LIST, - mc_addrs, mc_count * ETH_ALEN); - kfree(mc_addrs); - if (ret == 0) - filter |= cpu_to_le32(RNDIS_PACKET_TYPE_MULTICAST); - else - filter |= cpu_to_le32(RNDIS_PACKET_TYPE_ALL_MULTICAST); - - netdev_dbg(usbdev->net, "RNDIS_OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n", - mc_count, priv->multicast_size, ret); - } - -set_filter: - ret = rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, - sizeof(filter)); - if (ret < 0) { - netdev_warn(usbdev->net, "couldn't set packet filter: %08x\n", - le32_to_cpu(filter)); - } - - netdev_dbg(usbdev->net, "RNDIS_OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d\n", - le32_to_cpu(filter), ret); -} - -#ifdef DEBUG -static void debug_print_pmkids(struct usbnet *usbdev, - struct ndis_80211_pmkid *pmkids, - const char *func_str) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int i, len, count, max_pmkids, entry_len; - - max_pmkids = priv->wdev.wiphy->max_num_pmkids; - len = le32_to_cpu(pmkids->length); - count = le32_to_cpu(pmkids->bssid_info_count); - - entry_len = (count > 0) ? (len - sizeof(*pmkids)) / count : -1; - - netdev_dbg(usbdev->net, "%s(): %d PMKIDs (data len: %d, entry len: " - "%d)\n", func_str, count, len, entry_len); - - if (count > max_pmkids) - count = max_pmkids; - - for (i = 0; i < count; i++) { - u32 *tmp = (u32 *)pmkids->bssid_info[i].pmkid; - - netdev_dbg(usbdev->net, "%s(): bssid: %pM, " - "pmkid: %08X:%08X:%08X:%08X\n", - func_str, pmkids->bssid_info[i].bssid, - cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), - cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); - } -} -#else -static void debug_print_pmkids(struct usbnet *usbdev, - struct ndis_80211_pmkid *pmkids, - const char *func_str) -{ - return; -} -#endif - -static struct ndis_80211_pmkid *get_device_pmkids(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ndis_80211_pmkid *pmkids; - int len, ret, max_pmkids; - - max_pmkids = priv->wdev.wiphy->max_num_pmkids; - len = struct_size(pmkids, bssid_info, max_pmkids); - - pmkids = kzalloc(len, GFP_KERNEL); - if (!pmkids) - return ERR_PTR(-ENOMEM); - - pmkids->length = cpu_to_le32(len); - pmkids->bssid_info_count = cpu_to_le32(max_pmkids); - - ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_PMKID, - pmkids, &len); - if (ret < 0) { - netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d)" - " -> %d\n", __func__, len, max_pmkids, ret); - - kfree(pmkids); - return ERR_PTR(ret); - } - - if (le32_to_cpu(pmkids->bssid_info_count) > max_pmkids) - pmkids->bssid_info_count = cpu_to_le32(max_pmkids); - - debug_print_pmkids(usbdev, pmkids, __func__); - - return pmkids; -} - -static int set_device_pmkids(struct usbnet *usbdev, - struct ndis_80211_pmkid *pmkids) -{ - int ret, len, num_pmkids; - - num_pmkids = le32_to_cpu(pmkids->bssid_info_count); - len = struct_size(pmkids, bssid_info, num_pmkids); - pmkids->length = cpu_to_le32(len); - - debug_print_pmkids(usbdev, pmkids, __func__); - - ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, pmkids, - le32_to_cpu(pmkids->length)); - if (ret < 0) { - netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_PMKID(%d, %d) -> %d" - "\n", __func__, len, num_pmkids, ret); - } - - kfree(pmkids); - return ret; -} - -static struct ndis_80211_pmkid *remove_pmkid(struct usbnet *usbdev, - struct ndis_80211_pmkid *pmkids, - struct cfg80211_pmksa *pmksa, - int max_pmkids) -{ - int i, err; - unsigned int count; - - count = le32_to_cpu(pmkids->bssid_info_count); - - if (count > max_pmkids) - count = max_pmkids; - - for (i = 0; i < count; i++) - if (ether_addr_equal(pmkids->bssid_info[i].bssid, - pmksa->bssid)) - break; - - /* pmkid not found */ - if (i == count) { - netdev_dbg(usbdev->net, "%s(): bssid not found (%pM)\n", - __func__, pmksa->bssid); - err = -ENOENT; - goto error; - } - - for (; i + 1 < count; i++) - pmkids->bssid_info[i] = pmkids->bssid_info[i + 1]; - - count--; - pmkids->length = cpu_to_le32(struct_size(pmkids, bssid_info, count)); - pmkids->bssid_info_count = cpu_to_le32(count); - - return pmkids; -error: - kfree(pmkids); - return ERR_PTR(err); -} - -static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev, - struct ndis_80211_pmkid *pmkids, - struct cfg80211_pmksa *pmksa, - int max_pmkids) -{ - struct ndis_80211_pmkid *new_pmkids; - int i, err, newlen; - unsigned int count; - - count = le32_to_cpu(pmkids->bssid_info_count); - - if (count > max_pmkids) - count = max_pmkids; - - /* update with new pmkid */ - for (i = 0; i < count; i++) { - if (!ether_addr_equal(pmkids->bssid_info[i].bssid, - pmksa->bssid)) - continue; - - memcpy(pmkids->bssid_info[i].pmkid, pmksa->pmkid, - WLAN_PMKID_LEN); - - return pmkids; - } - - /* out of space, return error */ - if (i == max_pmkids) { - netdev_dbg(usbdev->net, "%s(): out of space\n", __func__); - err = -ENOSPC; - goto error; - } - - /* add new pmkid */ - newlen = struct_size(pmkids, bssid_info, count + 1); - - new_pmkids = krealloc(pmkids, newlen, GFP_KERNEL); - if (!new_pmkids) { - err = -ENOMEM; - goto error; - } - pmkids = new_pmkids; - - pmkids->length = cpu_to_le32(newlen); - pmkids->bssid_info_count = cpu_to_le32(count + 1); - - memcpy(pmkids->bssid_info[count].bssid, pmksa->bssid, ETH_ALEN); - memcpy(pmkids->bssid_info[count].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); - - return pmkids; -error: - kfree(pmkids); - return ERR_PTR(err); -} - -/* - * cfg80211 ops - */ -static int rndis_change_virtual_intf(struct wiphy *wiphy, - struct net_device *dev, - enum nl80211_iftype type, - struct vif_params *params) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - int mode; - - switch (type) { - case NL80211_IFTYPE_ADHOC: - mode = NDIS_80211_INFRA_ADHOC; - break; - case NL80211_IFTYPE_STATION: - mode = NDIS_80211_INFRA_INFRA; - break; - default: - return -EINVAL; - } - - priv->wdev.iftype = type; - - return set_infra_mode(usbdev, mode); -} - -static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - int err; - - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { - err = set_frag_threshold(usbdev, wiphy->frag_threshold); - if (err < 0) - return err; - } - - if (changed & WIPHY_PARAM_RTS_THRESHOLD) { - err = set_rts_threshold(usbdev, wiphy->rts_threshold); - if (err < 0) - return err; - } - - return 0; -} - -static int rndis_set_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - enum nl80211_tx_power_setting type, - int mbm) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - - netdev_dbg(usbdev->net, "%s(): type:0x%x mbm:%i\n", - __func__, type, mbm); - - if (mbm < 0 || (mbm % 100)) - return -ENOTSUPP; - - /* Device doesn't support changing txpower after initialization, only - * turn off/on radio. Support 'auto' mode and setting same dBm that is - * currently used. - */ - if (type == NL80211_TX_POWER_AUTOMATIC || - MBM_TO_DBM(mbm) == get_bcm4320_power_dbm(priv)) { - if (!priv->radio_on) - disassociate(usbdev, true); /* turn on radio */ - - return 0; - } - - return -ENOTSUPP; -} - -static int rndis_get_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - int *dbm) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - - *dbm = get_bcm4320_power_dbm(priv); - - netdev_dbg(usbdev->net, "%s(): dbm:%i\n", __func__, *dbm); - - return 0; -} - -#define SCAN_DELAY_JIFFIES (6 * HZ) -static int rndis_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request) -{ - struct net_device *dev = request->wdev->netdev; - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int ret; - int delay = SCAN_DELAY_JIFFIES; - - netdev_dbg(usbdev->net, "cfg80211.scan\n"); - - /* Get current bssid list from device before new scan, as new scan - * clears internal bssid list. - */ - rndis_check_bssid_list(usbdev, NULL, NULL); - - if (priv->scan_request && priv->scan_request != request) - return -EBUSY; - - priv->scan_request = request; - - ret = rndis_start_bssid_list_scan(usbdev); - if (ret == 0) { - if (priv->device_type == RNDIS_BCM4320A) - delay = HZ; - - /* Wait before retrieving scan results from device */ - queue_delayed_work(priv->workqueue, &priv->scan_work, delay); - } - - return ret; -} - -static bool rndis_bss_info_update(struct usbnet *usbdev, - struct ndis_80211_bssid_ex *bssid) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ieee80211_channel *channel; - struct cfg80211_bss *bss; - s32 signal; - u64 timestamp; - u16 capability; - u16 beacon_interval; - struct ndis_80211_fixed_ies *fixed; - int ie_len, bssid_len; - u8 *ie; - - netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM], len: %d\n", - bssid->ssid.essid, bssid->mac, le32_to_cpu(bssid->length)); - - /* parse bssid structure */ - bssid_len = le32_to_cpu(bssid->length); - - if (bssid_len < sizeof(struct ndis_80211_bssid_ex) + - sizeof(struct ndis_80211_fixed_ies)) - return NULL; - - fixed = (struct ndis_80211_fixed_ies *)bssid->ies; - - ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); - ie_len = min(bssid_len - (int)sizeof(*bssid), - (int)le32_to_cpu(bssid->ie_length)); - ie_len -= sizeof(struct ndis_80211_fixed_ies); - if (ie_len < 0) - return NULL; - - /* extract data for cfg80211_inform_bss */ - channel = ieee80211_get_channel(priv->wdev.wiphy, - KHZ_TO_MHZ(le32_to_cpu(bssid->config.ds_config))); - if (!channel) - return NULL; - - signal = level_to_qual(le32_to_cpu(bssid->rssi)); - timestamp = le64_to_cpu(*(__le64 *)fixed->timestamp); - capability = le16_to_cpu(fixed->capabilities); - beacon_interval = le16_to_cpu(fixed->beacon_interval); - - bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, - CFG80211_BSS_FTYPE_UNKNOWN, bssid->mac, - timestamp, capability, beacon_interval, - ie, ie_len, signal, GFP_KERNEL); - cfg80211_put_bss(priv->wdev.wiphy, bss); - - return (bss != NULL); -} - -static struct ndis_80211_bssid_ex *next_bssid_list_item( - struct ndis_80211_bssid_ex *bssid, - int *bssid_len, void *buf, int len) -{ - void *buf_end, *bssid_end; - - buf_end = (char *)buf + len; - bssid_end = (char *)bssid + *bssid_len; - - if ((int)(buf_end - bssid_end) < sizeof(bssid->length)) { - *bssid_len = 0; - return NULL; - } else { - bssid = (void *)((char *)bssid + *bssid_len); - *bssid_len = le32_to_cpu(bssid->length); - return bssid; - } -} - -static bool check_bssid_list_item(struct ndis_80211_bssid_ex *bssid, - int bssid_len, void *buf, int len) -{ - void *buf_end, *bssid_end; - - if (!bssid || bssid_len <= 0 || bssid_len > len) - return false; - - buf_end = (char *)buf + len; - bssid_end = (char *)bssid + bssid_len; - - return (int)(buf_end - bssid_end) >= 0 && (int)(bssid_end - buf) >= 0; -} - -static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, - bool *matched) -{ - void *buf = NULL; - struct ndis_80211_bssid_list_ex *bssid_list; - struct ndis_80211_bssid_ex *bssid; - int ret = -EINVAL, len, count, bssid_len, real_count, new_len; - - netdev_dbg(usbdev->net, "%s()\n", __func__); - - len = CONTROL_BUFFER_SIZE; -resize_buf: - buf = kzalloc(len, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto out; - } - - /* BSSID-list might have got bigger last time we checked, keep - * resizing until it won't get any bigger. - */ - new_len = len; - ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_BSSID_LIST, - buf, &new_len); - if (ret != 0 || new_len < sizeof(struct ndis_80211_bssid_list_ex)) - goto out; - - if (new_len > len) { - len = new_len; - kfree(buf); - goto resize_buf; - } - - len = new_len; - - bssid_list = buf; - count = le32_to_cpu(bssid_list->num_items); - real_count = 0; - netdev_dbg(usbdev->net, "%s(): buflen: %d\n", __func__, len); - - bssid_len = 0; - bssid = next_bssid_list_item((void *)bssid_list->bssid_data, - &bssid_len, buf, len); - - /* Device returns incorrect 'num_items'. Workaround by ignoring the - * received 'num_items' and walking through full bssid buffer instead. - */ - while (check_bssid_list_item(bssid, bssid_len, buf, len)) { - if (rndis_bss_info_update(usbdev, bssid) && match_bssid && - matched) { - if (ether_addr_equal(bssid->mac, match_bssid)) - *matched = true; - } - - real_count++; - bssid = next_bssid_list_item(bssid, &bssid_len, buf, len); - } - - netdev_dbg(usbdev->net, "%s(): num_items from device: %d, really found:" - " %d\n", __func__, count, real_count); - -out: - kfree(buf); - return ret; -} - -static void rndis_get_scan_results(struct work_struct *work) -{ - struct rndis_wlan_private *priv = - container_of(work, struct rndis_wlan_private, scan_work.work); - struct usbnet *usbdev = priv->usbdev; - struct cfg80211_scan_info info = {}; - int ret; - - netdev_dbg(usbdev->net, "get_scan_results\n"); - - if (!priv->scan_request) - return; - - ret = rndis_check_bssid_list(usbdev, NULL, NULL); - - info.aborted = ret < 0; - cfg80211_scan_done(priv->scan_request, &info); - - priv->scan_request = NULL; -} - -static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_connect_params *sme) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - struct ieee80211_channel *channel = sme->channel; - struct ndis_80211_ssid ssid; - int pairwise = RNDIS_WLAN_ALG_NONE; - int groupwise = RNDIS_WLAN_ALG_NONE; - int keymgmt = RNDIS_WLAN_KEY_MGMT_NONE; - int length, i, ret, chan = -1; - - if (channel) - chan = ieee80211_frequency_to_channel(channel->center_freq); - - groupwise = rndis_cipher_to_alg(sme->crypto.cipher_group); - for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) - pairwise |= - rndis_cipher_to_alg(sme->crypto.ciphers_pairwise[i]); - - if (sme->crypto.n_ciphers_pairwise > 0 && - pairwise == RNDIS_WLAN_ALG_NONE) { - netdev_err(usbdev->net, "Unsupported pairwise cipher\n"); - return -ENOTSUPP; - } - - for (i = 0; i < sme->crypto.n_akm_suites; i++) - keymgmt |= - rndis_akm_suite_to_key_mgmt(sme->crypto.akm_suites[i]); - - if (sme->crypto.n_akm_suites > 0 && - keymgmt == RNDIS_WLAN_KEY_MGMT_NONE) { - netdev_err(usbdev->net, "Invalid keymgmt\n"); - return -ENOTSUPP; - } - - netdev_dbg(usbdev->net, "cfg80211.connect('%.32s':[%pM]:%d:[%d,0x%x:0x%x]:[0x%x:0x%x]:0x%x)\n", - sme->ssid, sme->bssid, chan, - sme->privacy, sme->crypto.wpa_versions, sme->auth_type, - groupwise, pairwise, keymgmt); - - if (is_associated(usbdev)) - disassociate(usbdev, false); - - ret = set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); - if (ret < 0) { - netdev_dbg(usbdev->net, "connect: set_infra_mode failed, %d\n", - ret); - goto err_turn_radio_on; - } - - ret = set_auth_mode(usbdev, sme->crypto.wpa_versions, sme->auth_type, - keymgmt); - if (ret < 0) { - netdev_dbg(usbdev->net, "connect: set_auth_mode failed, %d\n", - ret); - goto err_turn_radio_on; - } - - set_priv_filter(usbdev); - - ret = set_encr_mode(usbdev, pairwise, groupwise); - if (ret < 0) { - netdev_dbg(usbdev->net, "connect: set_encr_mode failed, %d\n", - ret); - goto err_turn_radio_on; - } - - if (channel) { - ret = set_channel(usbdev, chan); - if (ret < 0) { - netdev_dbg(usbdev->net, "connect: set_channel failed, %d\n", - ret); - goto err_turn_radio_on; - } - } - - if (sme->key && ((groupwise | pairwise) & RNDIS_WLAN_ALG_WEP)) { - priv->encr_tx_key_index = sme->key_idx; - ret = add_wep_key(usbdev, sme->key, sme->key_len, sme->key_idx); - if (ret < 0) { - netdev_dbg(usbdev->net, "connect: add_wep_key failed, %d (%d, %d)\n", - ret, sme->key_len, sme->key_idx); - goto err_turn_radio_on; - } - } - - if (sme->bssid && !is_zero_ether_addr(sme->bssid) && - !is_broadcast_ether_addr(sme->bssid)) { - ret = set_bssid(usbdev, sme->bssid); - if (ret < 0) { - netdev_dbg(usbdev->net, "connect: set_bssid failed, %d\n", - ret); - goto err_turn_radio_on; - } - } else - clear_bssid(usbdev); - - length = sme->ssid_len; - if (length > NDIS_802_11_LENGTH_SSID) - length = NDIS_802_11_LENGTH_SSID; - - memset(&ssid, 0, sizeof(ssid)); - ssid.length = cpu_to_le32(length); - memcpy(ssid.essid, sme->ssid, length); - - /* Pause and purge rx queue, so we don't pass packets before - * 'media connect'-indication. - */ - usbnet_pause_rx(usbdev); - usbnet_purge_paused_rxq(usbdev); - - ret = set_essid(usbdev, &ssid); - if (ret < 0) - netdev_dbg(usbdev->net, "connect: set_essid failed, %d\n", ret); - return ret; - -err_turn_radio_on: - disassociate(usbdev, true); - - return ret; -} - -static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - - netdev_dbg(usbdev->net, "cfg80211.disconnect(%d)\n", reason_code); - - priv->connected = false; - eth_zero_addr(priv->bssid); - - return deauthenticate(usbdev); -} - -static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_ibss_params *params) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - struct ieee80211_channel *channel = params->chandef.chan; - struct ndis_80211_ssid ssid; - enum nl80211_auth_type auth_type; - int ret, alg, length, chan = -1; - - if (channel) - chan = ieee80211_frequency_to_channel(channel->center_freq); - - /* TODO: How to handle ad-hoc encryption? - * connect() has *key, join_ibss() doesn't. RNDIS requires key to be - * pre-shared for encryption (open/shared/wpa), is key set before - * join_ibss? Which auth_type to use (not in params)? What about WPA? - */ - if (params->privacy) { - auth_type = NL80211_AUTHTYPE_SHARED_KEY; - alg = RNDIS_WLAN_ALG_WEP; - } else { - auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; - alg = RNDIS_WLAN_ALG_NONE; - } - - netdev_dbg(usbdev->net, "cfg80211.join_ibss('%.32s':[%pM]:%d:%d)\n", - params->ssid, params->bssid, chan, params->privacy); - - if (is_associated(usbdev)) - disassociate(usbdev, false); - - ret = set_infra_mode(usbdev, NDIS_80211_INFRA_ADHOC); - if (ret < 0) { - netdev_dbg(usbdev->net, "join_ibss: set_infra_mode failed, %d\n", - ret); - goto err_turn_radio_on; - } - - ret = set_auth_mode(usbdev, 0, auth_type, RNDIS_WLAN_KEY_MGMT_NONE); - if (ret < 0) { - netdev_dbg(usbdev->net, "join_ibss: set_auth_mode failed, %d\n", - ret); - goto err_turn_radio_on; - } - - set_priv_filter(usbdev); - - ret = set_encr_mode(usbdev, alg, RNDIS_WLAN_ALG_NONE); - if (ret < 0) { - netdev_dbg(usbdev->net, "join_ibss: set_encr_mode failed, %d\n", - ret); - goto err_turn_radio_on; - } - - if (channel) { - ret = set_channel(usbdev, chan); - if (ret < 0) { - netdev_dbg(usbdev->net, "join_ibss: set_channel failed, %d\n", - ret); - goto err_turn_radio_on; - } - } - - if (params->bssid && !is_zero_ether_addr(params->bssid) && - !is_broadcast_ether_addr(params->bssid)) { - ret = set_bssid(usbdev, params->bssid); - if (ret < 0) { - netdev_dbg(usbdev->net, "join_ibss: set_bssid failed, %d\n", - ret); - goto err_turn_radio_on; - } - } else - clear_bssid(usbdev); - - length = params->ssid_len; - if (length > NDIS_802_11_LENGTH_SSID) - length = NDIS_802_11_LENGTH_SSID; - - memset(&ssid, 0, sizeof(ssid)); - ssid.length = cpu_to_le32(length); - memcpy(ssid.essid, params->ssid, length); - - /* Don't need to pause rx queue for ad-hoc. */ - usbnet_purge_paused_rxq(usbdev); - usbnet_resume_rx(usbdev); - - ret = set_essid(usbdev, &ssid); - if (ret < 0) - netdev_dbg(usbdev->net, "join_ibss: set_essid failed, %d\n", - ret); - return ret; - -err_turn_radio_on: - disassociate(usbdev, true); - - return ret; -} - -static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - - netdev_dbg(usbdev->net, "cfg80211.leave_ibss()\n"); - - priv->connected = false; - eth_zero_addr(priv->bssid); - - return deauthenticate(usbdev); -} - -static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, - int link_id, u8 key_index, bool pairwise, - const u8 *mac_addr, struct key_params *params) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - __le32 flags; - - netdev_dbg(usbdev->net, "%s(%i, %pM, %08x)\n", - __func__, key_index, mac_addr, params->cipher); - - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - return add_wep_key(usbdev, params->key, params->key_len, - key_index); - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - flags = 0; - - if (params->seq && params->seq_len > 0) - flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ; - if (mac_addr) - flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY | - NDIS_80211_ADDKEY_TRANSMIT_KEY; - - return add_wpa_key(usbdev, params->key, params->key_len, - key_index, mac_addr, params->seq, - params->seq_len, params->cipher, flags); - default: - netdev_dbg(usbdev->net, "%s(): unsupported cipher %08x\n", - __func__, params->cipher); - return -ENOTSUPP; - } -} - -static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, - int link_id, u8 key_index, bool pairwise, - const u8 *mac_addr) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - - netdev_dbg(usbdev->net, "%s(%i, %pM)\n", __func__, key_index, mac_addr); - - return remove_key(usbdev, key_index, mac_addr); -} - -static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, - int link_id, u8 key_index, bool unicast, - bool multicast) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - struct rndis_wlan_encr_key key; - - netdev_dbg(usbdev->net, "%s(%i)\n", __func__, key_index); - - if (key_index >= RNDIS_WLAN_NUM_KEYS) - return -ENOENT; - - priv->encr_tx_key_index = key_index; - - if (is_wpa_key(priv, key_index)) - return 0; - - key = priv->encr_keys[key_index]; - - return add_wep_key(usbdev, key.material, key.len, key_index); -} - -static void rndis_fill_station_info(struct usbnet *usbdev, - struct station_info *sinfo) -{ - __le32 linkspeed, rssi; - int ret, len; - - memset(sinfo, 0, sizeof(*sinfo)); - - len = sizeof(linkspeed); - ret = rndis_query_oid(usbdev, RNDIS_OID_GEN_LINK_SPEED, &linkspeed, &len); - if (ret == 0) { - sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); - } - - len = sizeof(rssi); - ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, - &rssi, &len); - if (ret == 0) { - sinfo->signal = level_to_qual(le32_to_cpu(rssi)); - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); - } -} - -static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_info *sinfo) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - - if (!ether_addr_equal(priv->bssid, mac)) - return -ENOENT; - - rndis_fill_station_info(usbdev, sinfo); - - return 0; -} - -static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - - if (idx != 0) - return -ENOENT; - - memcpy(mac, priv->bssid, ETH_ALEN); - - rndis_fill_station_info(usbdev, sinfo); - - return 0; -} - -static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_pmksa *pmksa) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - struct ndis_80211_pmkid *pmkids; - u32 *tmp = (u32 *)pmksa->pmkid; - - netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, - pmksa->bssid, - cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), - cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); - - pmkids = get_device_pmkids(usbdev); - if (IS_ERR(pmkids)) { - /* couldn't read PMKID cache from device */ - return PTR_ERR(pmkids); - } - - pmkids = update_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); - if (IS_ERR(pmkids)) { - /* not found, list full, etc */ - return PTR_ERR(pmkids); - } - - return set_device_pmkids(usbdev, pmkids); -} - -static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_pmksa *pmksa) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - struct ndis_80211_pmkid *pmkids; - u32 *tmp = (u32 *)pmksa->pmkid; - - netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, - pmksa->bssid, - cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), - cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); - - pmkids = get_device_pmkids(usbdev); - if (IS_ERR(pmkids)) { - /* Couldn't read PMKID cache from device */ - return PTR_ERR(pmkids); - } - - pmkids = remove_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); - if (IS_ERR(pmkids)) { - /* not found, etc */ - return PTR_ERR(pmkids); - } - - return set_device_pmkids(usbdev, pmkids); -} - -static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - struct ndis_80211_pmkid pmkid; - - netdev_dbg(usbdev->net, "%s()\n", __func__); - - memset(&pmkid, 0, sizeof(pmkid)); - - pmkid.length = cpu_to_le32(sizeof(pmkid)); - pmkid.bssid_info_count = cpu_to_le32(0); - - return rndis_set_oid(usbdev, RNDIS_OID_802_11_PMKID, - &pmkid, sizeof(pmkid)); -} - -static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, - bool enabled, int timeout) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - struct usbnet *usbdev = priv->usbdev; - int power_mode; - __le32 mode; - int ret; - - if (priv->device_type != RNDIS_BCM4320B) - return -ENOTSUPP; - - netdev_dbg(usbdev->net, "%s(): %s, %d\n", __func__, - enabled ? "enabled" : "disabled", - timeout); - - if (enabled) - power_mode = NDIS_80211_POWER_MODE_FAST_PSP; - else - power_mode = NDIS_80211_POWER_MODE_CAM; - - if (power_mode == priv->power_mode) - return 0; - - priv->power_mode = power_mode; - - mode = cpu_to_le32(power_mode); - ret = rndis_set_oid(usbdev, RNDIS_OID_802_11_POWER_MODE, - &mode, sizeof(mode)); - - netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_POWER_MODE -> %d\n", - __func__, ret); - - return ret; -} - -static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, - struct net_device *dev, - s32 rssi_thold, u32 rssi_hyst) -{ - struct rndis_wlan_private *priv = wiphy_priv(wiphy); - - priv->cqm_rssi_thold = rssi_thold; - priv->cqm_rssi_hyst = rssi_hyst; - priv->last_cqm_event_rssi = 0; - - return 0; -} - -static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, - struct ndis_80211_assoc_info *info) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ieee80211_channel *channel; - struct ndis_80211_ssid ssid; - struct cfg80211_bss *bss; - s32 signal; - u64 timestamp; - u16 capability; - u32 beacon_period = 0; - __le32 rssi; - u8 ie_buf[34]; - int len, ret, ie_len; - - /* Get signal quality, in case of error use rssi=0 and ignore error. */ - len = sizeof(rssi); - rssi = 0; - ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, - &rssi, &len); - signal = level_to_qual(le32_to_cpu(rssi)); - - netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_RSSI -> %d, " - "rssi:%d, qual: %d\n", __func__, ret, le32_to_cpu(rssi), - level_to_qual(le32_to_cpu(rssi))); - - /* Get AP capabilities */ - if (info) { - capability = le16_to_cpu(info->resp_ie.capa); - } else { - /* Set atleast ESS/IBSS capability */ - capability = (priv->infra_mode == NDIS_80211_INFRA_INFRA) ? - WLAN_CAPABILITY_ESS : WLAN_CAPABILITY_IBSS; - } - - /* Get channel and beacon interval */ - channel = get_current_channel(usbdev, &beacon_period); - if (!channel) { - netdev_warn(usbdev->net, "%s(): could not get channel.\n", - __func__); - return; - } - - /* Get SSID, in case of error, use zero length SSID and ignore error. */ - len = sizeof(ssid); - memset(&ssid, 0, sizeof(ssid)); - ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_SSID, - &ssid, &len); - netdev_dbg(usbdev->net, "%s(): RNDIS_OID_802_11_SSID -> %d, len: %d, ssid: " - "'%.32s'\n", __func__, ret, - le32_to_cpu(ssid.length), ssid.essid); - - if (le32_to_cpu(ssid.length) > 32) - ssid.length = cpu_to_le32(32); - - ie_buf[0] = WLAN_EID_SSID; - ie_buf[1] = le32_to_cpu(ssid.length); - memcpy(&ie_buf[2], ssid.essid, le32_to_cpu(ssid.length)); - - ie_len = le32_to_cpu(ssid.length) + 2; - - /* no tsf */ - timestamp = 0; - - netdev_dbg(usbdev->net, "%s(): channel:%d(freq), bssid:[%pM], tsf:%d, " - "capa:%x, beacon int:%d, resp_ie(len:%d, essid:'%.32s'), " - "signal:%d\n", __func__, (channel ? channel->center_freq : -1), - bssid, (u32)timestamp, capability, beacon_period, ie_len, - ssid.essid, signal); - - bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, - CFG80211_BSS_FTYPE_UNKNOWN, bssid, - timestamp, capability, beacon_period, - ie_buf, ie_len, signal, GFP_KERNEL); - cfg80211_put_bss(priv->wdev.wiphy, bss); -} - -/* - * workers, indication handlers, device poller - */ -static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct ndis_80211_assoc_info *info = NULL; - u8 bssid[ETH_ALEN]; - unsigned int resp_ie_len, req_ie_len; - unsigned int offset; - u8 *req_ie, *resp_ie; - int ret; - bool roamed = false; - bool match_bss; - - if (priv->infra_mode == NDIS_80211_INFRA_INFRA && priv->connected) { - /* received media connect indication while connected, either - * device reassociated with same AP or roamed to new. */ - roamed = true; - } - - req_ie_len = 0; - resp_ie_len = 0; - req_ie = NULL; - resp_ie = NULL; - - if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { - info = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); - if (!info) { - /* No memory? Try resume work later */ - set_bit(WORK_LINK_UP, &priv->work_pending); - queue_work(priv->workqueue, &priv->work); - return; - } - - /* Get association info IEs from device. */ - ret = get_association_info(usbdev, info, CONTROL_BUFFER_SIZE); - if (!ret) { - req_ie_len = le32_to_cpu(info->req_ie_length); - if (req_ie_len > CONTROL_BUFFER_SIZE) - req_ie_len = CONTROL_BUFFER_SIZE; - if (req_ie_len != 0) { - offset = le32_to_cpu(info->offset_req_ies); - - if (offset > CONTROL_BUFFER_SIZE) - offset = CONTROL_BUFFER_SIZE; - - req_ie = (u8 *)info + offset; - - if (offset + req_ie_len > CONTROL_BUFFER_SIZE) - req_ie_len = - CONTROL_BUFFER_SIZE - offset; - } - - resp_ie_len = le32_to_cpu(info->resp_ie_length); - if (resp_ie_len > CONTROL_BUFFER_SIZE) - resp_ie_len = CONTROL_BUFFER_SIZE; - if (resp_ie_len != 0) { - offset = le32_to_cpu(info->offset_resp_ies); - - if (offset > CONTROL_BUFFER_SIZE) - offset = CONTROL_BUFFER_SIZE; - - resp_ie = (u8 *)info + offset; - - if (offset + resp_ie_len > CONTROL_BUFFER_SIZE) - resp_ie_len = - CONTROL_BUFFER_SIZE - offset; - } - } else { - /* Since rndis_wlan_craft_connected_bss() might use info - * later and expects info to contain valid data if - * non-null, free info and set NULL here. - */ - kfree(info); - info = NULL; - } - } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC)) - return; - - ret = get_bssid(usbdev, bssid); - if (ret < 0) - memset(bssid, 0, sizeof(bssid)); - - netdev_dbg(usbdev->net, "link up work: [%pM]%s\n", - bssid, roamed ? " roamed" : ""); - - /* Internal bss list in device should contain at least the currently - * connected bss and we can get it to cfg80211 with - * rndis_check_bssid_list(). - * - * NDIS spec says: "If the device is associated, but the associated - * BSSID is not in its BSSID scan list, then the driver must add an - * entry for the BSSID at the end of the data that it returns in - * response to query of RNDIS_OID_802_11_BSSID_LIST." - * - * NOTE: Seems to be true for BCM4320b variant, but not BCM4320a. - */ - match_bss = false; - rndis_check_bssid_list(usbdev, bssid, &match_bss); - - if (!is_zero_ether_addr(bssid) && !match_bss) { - /* Couldn't get bss from device, we need to manually craft bss - * for cfg80211. - */ - rndis_wlan_craft_connected_bss(usbdev, bssid, info); - } - - if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { - if (!roamed) { - cfg80211_connect_result(usbdev->net, bssid, req_ie, - req_ie_len, resp_ie, - resp_ie_len, 0, GFP_KERNEL); - } else { - struct cfg80211_roam_info roam_info = { - .links[0].channel = - get_current_channel(usbdev, NULL), - .links[0].bssid = bssid, - .req_ie = req_ie, - .req_ie_len = req_ie_len, - .resp_ie = resp_ie, - .resp_ie_len = resp_ie_len, - }; - - cfg80211_roamed(usbdev->net, &roam_info, GFP_KERNEL); - } - } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) - cfg80211_ibss_joined(usbdev->net, bssid, - get_current_channel(usbdev, NULL), - GFP_KERNEL); - - kfree(info); - - priv->connected = true; - memcpy(priv->bssid, bssid, ETH_ALEN); - - usbnet_resume_rx(usbdev); - netif_carrier_on(usbdev->net); -} - -static void rndis_wlan_do_link_down_work(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - - if (priv->connected) { - priv->connected = false; - eth_zero_addr(priv->bssid); - - deauthenticate(usbdev); - - cfg80211_disconnected(usbdev->net, 0, NULL, 0, true, GFP_KERNEL); - } - - netif_carrier_off(usbdev->net); -} - -static void rndis_wlan_worker(struct work_struct *work) -{ - struct rndis_wlan_private *priv = - container_of(work, struct rndis_wlan_private, work); - struct usbnet *usbdev = priv->usbdev; - - if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) - rndis_wlan_do_link_up_work(usbdev); - - if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) - rndis_wlan_do_link_down_work(usbdev); - - if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) - set_multicast_list(usbdev); -} - -static void rndis_wlan_set_multicast_list(struct net_device *dev) -{ - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - - if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) - return; - - set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending); - queue_work(priv->workqueue, &priv->work); -} - -static void rndis_wlan_auth_indication(struct usbnet *usbdev, - struct ndis_80211_status_indication *indication, - int len) -{ - u8 *buf; - const char *type; - int flags, buflen, key_id; - bool pairwise_error, group_error; - struct ndis_80211_auth_request *auth_req; - enum nl80211_key_type key_type; - - /* must have at least one array entry */ - if (len < offsetof(struct ndis_80211_status_indication, u) + - sizeof(struct ndis_80211_auth_request)) { - netdev_info(usbdev->net, "authentication indication: too short message (%i)\n", - len); - return; - } - - buf = (void *)&indication->u.auth_request[0]; - buflen = len - offsetof(struct ndis_80211_status_indication, u); - - while (buflen >= sizeof(*auth_req)) { - auth_req = (void *)buf; - if (buflen < le32_to_cpu(auth_req->length)) - return; - type = "unknown"; - flags = le32_to_cpu(auth_req->flags); - pairwise_error = false; - group_error = false; - - if (flags & 0x1) - type = "reauth request"; - if (flags & 0x2) - type = "key update request"; - if (flags & 0x6) { - pairwise_error = true; - type = "pairwise_error"; - } - if (flags & 0xe) { - group_error = true; - type = "group_error"; - } - - netdev_info(usbdev->net, "authentication indication: %s (0x%08x)\n", - type, le32_to_cpu(auth_req->flags)); - - if (pairwise_error) { - key_type = NL80211_KEYTYPE_PAIRWISE; - key_id = -1; - - cfg80211_michael_mic_failure(usbdev->net, - auth_req->bssid, - key_type, key_id, NULL, - GFP_KERNEL); - } - - if (group_error) { - key_type = NL80211_KEYTYPE_GROUP; - key_id = -1; - - cfg80211_michael_mic_failure(usbdev->net, - auth_req->bssid, - key_type, key_id, NULL, - GFP_KERNEL); - } - - buflen -= le32_to_cpu(auth_req->length); - buf += le32_to_cpu(auth_req->length); - } -} - -static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev, - struct ndis_80211_status_indication *indication, - int len) -{ - struct ndis_80211_pmkid_cand_list *cand_list; - int list_len, expected_len, i; - - if (len < offsetof(struct ndis_80211_status_indication, u) + - sizeof(struct ndis_80211_pmkid_cand_list)) { - netdev_info(usbdev->net, "pmkid candidate list indication: too short message (%i)\n", - len); - return; - } - - list_len = le32_to_cpu(indication->u.cand_list.num_candidates) * - sizeof(struct ndis_80211_pmkid_candidate); - expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len + - offsetof(struct ndis_80211_status_indication, u); - - if (len < expected_len) { - netdev_info(usbdev->net, "pmkid candidate list indication: list larger than buffer (%i < %i)\n", - len, expected_len); - return; - } - - cand_list = &indication->u.cand_list; - - netdev_info(usbdev->net, "pmkid candidate list indication: version %i, candidates %i\n", - le32_to_cpu(cand_list->version), - le32_to_cpu(cand_list->num_candidates)); - - if (le32_to_cpu(cand_list->version) != 1) - return; - - for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) { - struct ndis_80211_pmkid_candidate *cand = - &cand_list->candidate_list[i]; - bool preauth = !!(cand->flags & NDIS_80211_PMKID_CAND_PREAUTH); - - netdev_dbg(usbdev->net, "cand[%i]: flags: 0x%08x, preauth: %d, bssid: %pM\n", - i, le32_to_cpu(cand->flags), preauth, cand->bssid); - - cfg80211_pmksa_candidate_notify(usbdev->net, i, cand->bssid, - preauth, GFP_ATOMIC); - } -} - -static void rndis_wlan_media_specific_indication(struct usbnet *usbdev, - struct rndis_indicate *msg, int buflen) -{ - struct ndis_80211_status_indication *indication; - unsigned int len, offset; - - offset = offsetof(struct rndis_indicate, status) + - le32_to_cpu(msg->offset); - len = le32_to_cpu(msg->length); - - if (len < 8) { - netdev_info(usbdev->net, "media specific indication, ignore too short message (%i < 8)\n", - len); - return; - } - - if (len > buflen || offset > buflen || offset + len > buflen) { - netdev_info(usbdev->net, "media specific indication, too large to fit to buffer (%i > %i)\n", - offset + len, buflen); - return; - } - - indication = (void *)((u8 *)msg + offset); - - switch (le32_to_cpu(indication->status_type)) { - case NDIS_80211_STATUSTYPE_RADIOSTATE: - netdev_info(usbdev->net, "radio state indication: %i\n", - le32_to_cpu(indication->u.radio_status)); - return; - - case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE: - netdev_info(usbdev->net, "media stream mode indication: %i\n", - le32_to_cpu(indication->u.media_stream_mode)); - return; - - case NDIS_80211_STATUSTYPE_AUTHENTICATION: - rndis_wlan_auth_indication(usbdev, indication, len); - return; - - case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST: - rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len); - return; - - default: - netdev_info(usbdev->net, "media specific indication: unknown status type 0x%08x\n", - le32_to_cpu(indication->status_type)); - } -} - -static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct rndis_indicate *msg = ind; - - switch (le32_to_cpu(msg->status)) { - case RNDIS_STATUS_MEDIA_CONNECT: - if (priv->current_command_oid == RNDIS_OID_802_11_ADD_KEY) { - /* RNDIS_OID_802_11_ADD_KEY causes sometimes extra - * "media connect" indications which confuses driver - * and userspace to think that device is - * roaming/reassociating when it isn't. - */ - netdev_dbg(usbdev->net, "ignored RNDIS_OID_802_11_ADD_KEY triggered 'media connect'\n"); - return; - } - - usbnet_pause_rx(usbdev); - - netdev_info(usbdev->net, "media connect\n"); - - /* queue work to avoid recursive calls into rndis_command */ - set_bit(WORK_LINK_UP, &priv->work_pending); - queue_work(priv->workqueue, &priv->work); - break; - - case RNDIS_STATUS_MEDIA_DISCONNECT: - netdev_info(usbdev->net, "media disconnect\n"); - - /* queue work to avoid recursive calls into rndis_command */ - set_bit(WORK_LINK_DOWN, &priv->work_pending); - queue_work(priv->workqueue, &priv->work); - break; - - case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION: - rndis_wlan_media_specific_indication(usbdev, msg, buflen); - break; - - default: - netdev_info(usbdev->net, "indication: 0x%08x\n", - le32_to_cpu(msg->status)); - break; - } -} - -static int rndis_wlan_get_caps(struct usbnet *usbdev, struct wiphy *wiphy) -{ - struct { - __le32 num_items; - __le32 items[8]; - } networks_supported; - struct ndis_80211_capability caps; - int len, retval, i, n; - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - - /* determine supported modes */ - len = sizeof(networks_supported); - retval = rndis_query_oid(usbdev, - RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED, - &networks_supported, &len); - if (!retval) { - n = le32_to_cpu(networks_supported.num_items); - if (n > 8) - n = 8; - for (i = 0; i < n; i++) { - switch (le32_to_cpu(networks_supported.items[i])) { - case NDIS_80211_TYPE_FREQ_HOP: - case NDIS_80211_TYPE_DIRECT_SEQ: - priv->caps |= CAP_MODE_80211B; - break; - case NDIS_80211_TYPE_OFDM_A: - priv->caps |= CAP_MODE_80211A; - break; - case NDIS_80211_TYPE_OFDM_G: - priv->caps |= CAP_MODE_80211G; - break; - } - } - } - - /* get device 802.11 capabilities, number of PMKIDs */ - len = sizeof(caps); - retval = rndis_query_oid(usbdev, - RNDIS_OID_802_11_CAPABILITY, - &caps, &len); - if (!retval) { - netdev_dbg(usbdev->net, "RNDIS_OID_802_11_CAPABILITY -> len %d, " - "ver %d, pmkids %d, auth-encr-pairs %d\n", - le32_to_cpu(caps.length), - le32_to_cpu(caps.version), - le32_to_cpu(caps.num_pmkids), - le32_to_cpu(caps.num_auth_encr_pair)); - wiphy->max_num_pmkids = le32_to_cpu(caps.num_pmkids); - } else - wiphy->max_num_pmkids = 0; - - return retval; -} - -static void rndis_do_cqm(struct usbnet *usbdev, s32 rssi) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - enum nl80211_cqm_rssi_threshold_event event; - int thold, hyst, last_event; - - if (priv->cqm_rssi_thold >= 0 || rssi >= 0) - return; - if (priv->infra_mode != NDIS_80211_INFRA_INFRA) - return; - - last_event = priv->last_cqm_event_rssi; - thold = priv->cqm_rssi_thold; - hyst = priv->cqm_rssi_hyst; - - if (rssi < thold && (last_event == 0 || rssi < last_event - hyst)) - event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; - else if (rssi > thold && (last_event == 0 || rssi > last_event + hyst)) - event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; - else - return; - - priv->last_cqm_event_rssi = rssi; - cfg80211_cqm_rssi_notify(usbdev->net, event, rssi, GFP_KERNEL); -} - -#define DEVICE_POLLER_JIFFIES (HZ) -static void rndis_device_poller(struct work_struct *work) -{ - struct rndis_wlan_private *priv = - container_of(work, struct rndis_wlan_private, - dev_poller_work.work); - struct usbnet *usbdev = priv->usbdev; - __le32 rssi, tmp; - int len, ret, j; - int update_jiffies = DEVICE_POLLER_JIFFIES; - void *buf; - - /* Only check/do workaround when connected. Calling is_associated() - * also polls device with rndis_command() and catches for media link - * indications. - */ - if (!is_associated(usbdev)) { - /* Workaround bad scanning in BCM4320a devices with active - * background scanning when not associated. - */ - if (priv->device_type == RNDIS_BCM4320A && priv->radio_on && - !priv->scan_request) { - /* Get previous scan results */ - rndis_check_bssid_list(usbdev, NULL, NULL); - - /* Initiate new scan */ - rndis_start_bssid_list_scan(usbdev); - } - - goto end; - } - - len = sizeof(rssi); - ret = rndis_query_oid(usbdev, RNDIS_OID_802_11_RSSI, - &rssi, &len); - if (ret == 0) { - priv->last_qual = level_to_qual(le32_to_cpu(rssi)); - rndis_do_cqm(usbdev, le32_to_cpu(rssi)); - } - - netdev_dbg(usbdev->net, "dev-poller: RNDIS_OID_802_11_RSSI -> %d, rssi:%d, qual: %d\n", - ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); - - /* Workaround transfer stalls on poor quality links. - * TODO: find right way to fix these stalls (as stalls do not happen - * with ndiswrapper/windows driver). */ - if (priv->param_workaround_interval > 0 && priv->last_qual <= 25) { - /* Decrease stats worker interval to catch stalls. - * faster. Faster than 400-500ms causes packet loss, - * Slower doesn't catch stalls fast enough. - */ - j = msecs_to_jiffies(priv->param_workaround_interval); - if (j > DEVICE_POLLER_JIFFIES) - j = DEVICE_POLLER_JIFFIES; - else if (j <= 0) - j = 1; - update_jiffies = j; - - /* Send scan OID. Use of both OIDs is required to get device - * working. - */ - tmp = cpu_to_le32(1); - rndis_set_oid(usbdev, - RNDIS_OID_802_11_BSSID_LIST_SCAN, - &tmp, sizeof(tmp)); - - len = CONTROL_BUFFER_SIZE; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - goto end; - - rndis_query_oid(usbdev, - RNDIS_OID_802_11_BSSID_LIST, - buf, &len); - kfree(buf); - } - -end: - if (update_jiffies >= HZ) - update_jiffies = round_jiffies_relative(update_jiffies); - else { - j = round_jiffies_relative(update_jiffies); - if (abs(j - update_jiffies) <= 10) - update_jiffies = j; - } - - queue_delayed_work(priv->workqueue, &priv->dev_poller_work, - update_jiffies); -} - -/* - * driver/device initialization - */ -static void rndis_copy_module_params(struct usbnet *usbdev, int device_type) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - - priv->device_type = device_type; - - priv->param_country[0] = modparam_country[0]; - priv->param_country[1] = modparam_country[1]; - priv->param_country[2] = 0; - priv->param_frameburst = modparam_frameburst; - priv->param_afterburner = modparam_afterburner; - priv->param_power_save = modparam_power_save; - priv->param_power_output = modparam_power_output; - priv->param_roamtrigger = modparam_roamtrigger; - priv->param_roamdelta = modparam_roamdelta; - - priv->param_country[0] = toupper(priv->param_country[0]); - priv->param_country[1] = toupper(priv->param_country[1]); - /* doesn't support EU as country code, use FI instead */ - if (!strcmp(priv->param_country, "EU")) - strcpy(priv->param_country, "FI"); - - if (priv->param_power_save < 0) - priv->param_power_save = 0; - else if (priv->param_power_save > 2) - priv->param_power_save = 2; - - if (priv->param_power_output < 0) - priv->param_power_output = 0; - else if (priv->param_power_output > 3) - priv->param_power_output = 3; - - if (priv->param_roamtrigger < -80) - priv->param_roamtrigger = -80; - else if (priv->param_roamtrigger > -60) - priv->param_roamtrigger = -60; - - if (priv->param_roamdelta < 0) - priv->param_roamdelta = 0; - else if (priv->param_roamdelta > 2) - priv->param_roamdelta = 2; - - if (modparam_workaround_interval < 0) - priv->param_workaround_interval = 500; - else - priv->param_workaround_interval = modparam_workaround_interval; -} - -static int unknown_early_init(struct usbnet *usbdev) -{ - /* copy module parameters for unknown so that iwconfig reports txpower - * and workaround parameter is copied to private structure correctly. - */ - rndis_copy_module_params(usbdev, RNDIS_UNKNOWN); - - /* This is unknown device, so do not try set configuration parameters. - */ - - return 0; -} - -static int bcm4320a_early_init(struct usbnet *usbdev) -{ - /* copy module parameters for bcm4320a so that iwconfig reports txpower - * and workaround parameter is copied to private structure correctly. - */ - rndis_copy_module_params(usbdev, RNDIS_BCM4320A); - - /* bcm4320a doesn't handle configuration parameters well. Try - * set any and you get partially zeroed mac and broken device. - */ - - return 0; -} - -static int bcm4320b_early_init(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - char buf[8]; - - rndis_copy_module_params(usbdev, RNDIS_BCM4320B); - - /* Early initialization settings, setting these won't have effect - * if called after generic_rndis_bind(). - */ - - rndis_set_config_parameter_str(usbdev, "Country", priv->param_country); - rndis_set_config_parameter_str(usbdev, "FrameBursting", - priv->param_frameburst ? "1" : "0"); - rndis_set_config_parameter_str(usbdev, "Afterburner", - priv->param_afterburner ? "1" : "0"); - sprintf(buf, "%d", priv->param_power_save); - rndis_set_config_parameter_str(usbdev, "PowerSaveMode", buf); - sprintf(buf, "%d", priv->param_power_output); - rndis_set_config_parameter_str(usbdev, "PwrOut", buf); - sprintf(buf, "%d", priv->param_roamtrigger); - rndis_set_config_parameter_str(usbdev, "RoamTrigger", buf); - sprintf(buf, "%d", priv->param_roamdelta); - rndis_set_config_parameter_str(usbdev, "RoamDelta", buf); - - return 0; -} - -/* same as rndis_netdev_ops but with local multicast handler */ -static const struct net_device_ops rndis_wlan_netdev_ops = { - .ndo_open = usbnet_open, - .ndo_stop = usbnet_stop, - .ndo_start_xmit = usbnet_start_xmit, - .ndo_tx_timeout = usbnet_tx_timeout, - .ndo_get_stats64 = dev_get_tstats64, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = rndis_wlan_set_multicast_list, -}; - -static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) -{ - struct wiphy *wiphy; - struct rndis_wlan_private *priv; - int retval, len; - __le32 tmp; - - /* allocate wiphy and rndis private data - * NOTE: We only support a single virtual interface, so wiphy - * and wireless_dev are somewhat synonymous for this device. - */ - wiphy = wiphy_new(&rndis_config_ops, sizeof(struct rndis_wlan_private)); - if (!wiphy) - return -ENOMEM; - - priv = wiphy_priv(wiphy); - usbdev->net->ieee80211_ptr = &priv->wdev; - priv->wdev.wiphy = wiphy; - priv->wdev.iftype = NL80211_IFTYPE_STATION; - - /* These have to be initialized before calling generic_rndis_bind(). - * Otherwise we'll be in big trouble in rndis_wlan_early_init(). - */ - usbdev->driver_priv = priv; - priv->usbdev = usbdev; - - mutex_init(&priv->command_lock); - - /* because rndis_command() sleeps we need to use workqueue */ - priv->workqueue = create_singlethread_workqueue("rndis_wlan"); - if (!priv->workqueue) { - wiphy_free(wiphy); - return -ENOMEM; - } - INIT_WORK(&priv->work, rndis_wlan_worker); - INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller); - INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results); - - /* try bind rndis_host */ - retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS); - if (retval < 0) - goto fail; - - /* generic_rndis_bind set packet filter to multicast_all+ - * promisc mode which doesn't work well for our devices (device - * picks up rssi to closest station instead of to access point). - * - * rndis_host wants to avoid all OID as much as possible - * so do promisc/multicast handling in rndis_wlan. - */ - usbdev->net->netdev_ops = &rndis_wlan_netdev_ops; - - tmp = cpu_to_le32(RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST); - retval = rndis_set_oid(usbdev, - RNDIS_OID_GEN_CURRENT_PACKET_FILTER, - &tmp, sizeof(tmp)); - - len = sizeof(tmp); - retval = rndis_query_oid(usbdev, - RNDIS_OID_802_3_MAXIMUM_LIST_SIZE, - &tmp, &len); - priv->multicast_size = le32_to_cpu(tmp); - if (retval < 0 || priv->multicast_size < 0) - priv->multicast_size = 0; - if (priv->multicast_size > 0) - usbdev->net->flags |= IFF_MULTICAST; - else - usbdev->net->flags &= ~IFF_MULTICAST; - - /* fill-out wiphy structure and register w/ cfg80211 */ - memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN); - wiphy->privid = rndis_wiphy_privid; - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) - | BIT(NL80211_IFTYPE_ADHOC); - wiphy->max_scan_ssids = 1; - - /* TODO: fill-out band/encr information based on priv->caps */ - rndis_wlan_get_caps(usbdev, wiphy); - - memcpy(priv->channels, rndis_channels, sizeof(rndis_channels)); - memcpy(priv->rates, rndis_rates, sizeof(rndis_rates)); - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(rndis_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = ARRAY_SIZE(rndis_rates); - wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; - wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; - - memcpy(priv->cipher_suites, rndis_cipher_suites, - sizeof(rndis_cipher_suites)); - wiphy->cipher_suites = priv->cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(rndis_cipher_suites); - - set_wiphy_dev(wiphy, &usbdev->udev->dev); - - if (wiphy_register(wiphy)) { - retval = -ENODEV; - goto fail; - } - - set_default_iw_params(usbdev); - - priv->power_mode = -1; - - /* set default rts/frag */ - rndis_set_wiphy_params(wiphy, - WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); - - /* turn radio off on init */ - priv->radio_on = false; - disassociate(usbdev, false); - netif_carrier_off(usbdev->net); - - return 0; - -fail: - cancel_delayed_work_sync(&priv->dev_poller_work); - cancel_delayed_work_sync(&priv->scan_work); - cancel_work_sync(&priv->work); - destroy_workqueue(priv->workqueue); - - wiphy_free(wiphy); - return retval; -} - -static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - - /* turn radio off */ - disassociate(usbdev, false); - - cancel_delayed_work_sync(&priv->dev_poller_work); - cancel_delayed_work_sync(&priv->scan_work); - cancel_work_sync(&priv->work); - destroy_workqueue(priv->workqueue); - - rndis_unbind(usbdev, intf); - - wiphy_unregister(priv->wdev.wiphy); - wiphy_free(priv->wdev.wiphy); -} - -static int rndis_wlan_reset(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int retval; - - netdev_dbg(usbdev->net, "%s()\n", __func__); - - retval = rndis_reset(usbdev); - if (retval) - netdev_warn(usbdev->net, "rndis_reset failed: %d\n", retval); - - /* rndis_reset cleared multicast list, so restore here. - (set_multicast_list() also turns on current packet filter) */ - set_multicast_list(usbdev); - - queue_delayed_work(priv->workqueue, &priv->dev_poller_work, - round_jiffies_relative(DEVICE_POLLER_JIFFIES)); - - return deauthenticate(usbdev); -} - -static int rndis_wlan_stop(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int retval; - __le32 filter; - - netdev_dbg(usbdev->net, "%s()\n", __func__); - - retval = disassociate(usbdev, false); - - priv->work_pending = 0; - cancel_delayed_work_sync(&priv->dev_poller_work); - cancel_delayed_work_sync(&priv->scan_work); - cancel_work_sync(&priv->work); - flush_workqueue(priv->workqueue); - - if (priv->scan_request) { - struct cfg80211_scan_info info = { - .aborted = true, - }; - - cfg80211_scan_done(priv->scan_request, &info); - priv->scan_request = NULL; - } - - /* Set current packet filter zero to block receiving data packets from - device. */ - filter = 0; - rndis_set_oid(usbdev, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, &filter, - sizeof(filter)); - - return retval; -} - -static const struct driver_info bcm4320b_info = { - .description = "Wireless RNDIS device, BCM4320b based", - .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | - FLAG_AVOID_UNLINK_URBS, - .bind = rndis_wlan_bind, - .unbind = rndis_wlan_unbind, - .status = rndis_status, - .rx_fixup = rndis_rx_fixup, - .tx_fixup = rndis_tx_fixup, - .reset = rndis_wlan_reset, - .stop = rndis_wlan_stop, - .early_init = bcm4320b_early_init, - .indication = rndis_wlan_indication, -}; - -static const struct driver_info bcm4320a_info = { - .description = "Wireless RNDIS device, BCM4320a based", - .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | - FLAG_AVOID_UNLINK_URBS, - .bind = rndis_wlan_bind, - .unbind = rndis_wlan_unbind, - .status = rndis_status, - .rx_fixup = rndis_rx_fixup, - .tx_fixup = rndis_tx_fixup, - .reset = rndis_wlan_reset, - .stop = rndis_wlan_stop, - .early_init = bcm4320a_early_init, - .indication = rndis_wlan_indication, -}; - -static const struct driver_info rndis_wlan_info = { - .description = "Wireless RNDIS device", - .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | - FLAG_AVOID_UNLINK_URBS, - .bind = rndis_wlan_bind, - .unbind = rndis_wlan_unbind, - .status = rndis_status, - .rx_fixup = rndis_rx_fixup, - .tx_fixup = rndis_tx_fixup, - .reset = rndis_wlan_reset, - .stop = rndis_wlan_stop, - .early_init = unknown_early_init, - .indication = rndis_wlan_indication, -}; - -/*-------------------------------------------------------------------------*/ - -static const struct usb_device_id products [] = { -#define RNDIS_MASTER_INTERFACE \ - .bInterfaceClass = USB_CLASS_COMM, \ - .bInterfaceSubClass = 2 /* ACM */, \ - .bInterfaceProtocol = 0x0ff - -/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom - * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki. - */ -{ - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x0411, - .idProduct = 0x00bc, /* Buffalo WLI-U2-KG125S */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x0baf, - .idProduct = 0x011b, /* U.S. Robotics USR5421 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x050d, - .idProduct = 0x011b, /* Belkin F5D7051 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x1799, /* Belkin has two vendor ids */ - .idProduct = 0x011b, /* Belkin F5D7051 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x13b1, - .idProduct = 0x0014, /* Linksys WUSB54GSv2 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x13b1, - .idProduct = 0x0026, /* Linksys WUSB54GSC */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x0b05, - .idProduct = 0x1717, /* Asus WL169gE */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x0a5c, - .idProduct = 0xd11b, /* Eminent EM4045 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x1690, - .idProduct = 0x0715, /* BT Voyager 1055 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320b_info, -}, -/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom - * parameters available, hardware probably contain older firmware version with - * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki. - */ -{ - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x13b1, - .idProduct = 0x000e, /* Linksys WUSB54GSv1 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320a_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x0baf, - .idProduct = 0x0111, /* U.S. Robotics USR5420 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320a_info, -}, { - .match_flags = USB_DEVICE_ID_MATCH_INT_INFO - | USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x0411, - .idProduct = 0x004b, /* BUFFALO WLI-USB-G54 */ - RNDIS_MASTER_INTERFACE, - .driver_info = (unsigned long) &bcm4320a_info, -}, -/* Generic Wireless RNDIS devices that we don't have exact - * idVendor/idProduct/chip yet. - */ -{ - /* RNDIS is MSFT's un-official variant of CDC ACM */ - USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), - .driver_info = (unsigned long) &rndis_wlan_info, -}, { - /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ - USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), - .driver_info = (unsigned long) &rndis_wlan_info, -}, - { }, // END -}; -MODULE_DEVICE_TABLE(usb, products); - -static struct usb_driver rndis_wlan_driver = { - .name = "rndis_wlan", - .id_table = products, - .probe = usbnet_probe, - .disconnect = usbnet_disconnect, - .suspend = usbnet_suspend, - .resume = usbnet_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(rndis_wlan_driver); - -MODULE_AUTHOR("Bjorge Dijkstra"); -MODULE_AUTHOR("Jussi Kivilinna"); -MODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/net/wireless/wl3501.h b/drivers/net/wireless/wl3501.h deleted file mode 100644 index 91f276dd22a1..000000000000 --- a/drivers/net/wireless/wl3501.h +++ /dev/null @@ -1,615 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __WL3501_H__ -#define __WL3501_H__ - -#include -#include - -/* define for WLA 2.0 */ -#define WL3501_BLKSZ 256 -/* - * ID for input Signals of DRIVER block - * bit[7-5] is block ID: 000 - * bit[4-0] is signal ID -*/ -enum wl3501_signals { - WL3501_SIG_ALARM, - WL3501_SIG_MD_CONFIRM, - WL3501_SIG_MD_IND, - WL3501_SIG_ASSOC_CONFIRM, - WL3501_SIG_ASSOC_IND, - WL3501_SIG_AUTH_CONFIRM, - WL3501_SIG_AUTH_IND, - WL3501_SIG_DEAUTH_CONFIRM, - WL3501_SIG_DEAUTH_IND, - WL3501_SIG_DISASSOC_CONFIRM, - WL3501_SIG_DISASSOC_IND, - WL3501_SIG_GET_CONFIRM, - WL3501_SIG_JOIN_CONFIRM, - WL3501_SIG_PWR_MGMT_CONFIRM, - WL3501_SIG_REASSOC_CONFIRM, - WL3501_SIG_REASSOC_IND, - WL3501_SIG_SCAN_CONFIRM, - WL3501_SIG_SET_CONFIRM, - WL3501_SIG_START_CONFIRM, - WL3501_SIG_RESYNC_CONFIRM, - WL3501_SIG_SITE_CONFIRM, - WL3501_SIG_SAVE_CONFIRM, - WL3501_SIG_RFTEST_CONFIRM, -/* - * ID for input Signals of MLME block - * bit[7-5] is block ID: 010 - * bit[4-0] is signal ID - */ - WL3501_SIG_ASSOC_REQ = 0x20, - WL3501_SIG_AUTH_REQ, - WL3501_SIG_DEAUTH_REQ, - WL3501_SIG_DISASSOC_REQ, - WL3501_SIG_GET_REQ, - WL3501_SIG_JOIN_REQ, - WL3501_SIG_PWR_MGMT_REQ, - WL3501_SIG_REASSOC_REQ, - WL3501_SIG_SCAN_REQ, - WL3501_SIG_SET_REQ, - WL3501_SIG_START_REQ, - WL3501_SIG_MD_REQ, - WL3501_SIG_RESYNC_REQ, - WL3501_SIG_SITE_REQ, - WL3501_SIG_SAVE_REQ, - WL3501_SIG_RF_TEST_REQ, - WL3501_SIG_MM_CONFIRM = 0x60, - WL3501_SIG_MM_IND, -}; - -enum wl3501_mib_attribs { - WL3501_MIB_ATTR_STATION_ID, - WL3501_MIB_ATTR_AUTH_ALGORITHMS, - WL3501_MIB_ATTR_AUTH_TYPE, - WL3501_MIB_ATTR_MEDIUM_OCCUPANCY_LIMIT, - WL3501_MIB_ATTR_CF_POLLABLE, - WL3501_MIB_ATTR_CFP_PERIOD, - WL3501_MIB_ATTR_CFPMAX_DURATION, - WL3501_MIB_ATTR_AUTH_RESP_TMOUT, - WL3501_MIB_ATTR_RX_DTIMS, - WL3501_MIB_ATTR_PRIV_OPT_IMPLEMENTED, - WL3501_MIB_ATTR_PRIV_INVOKED, - WL3501_MIB_ATTR_WEP_DEFAULT_KEYS, - WL3501_MIB_ATTR_WEP_DEFAULT_KEY_ID, - WL3501_MIB_ATTR_WEP_KEY_MAPPINGS, - WL3501_MIB_ATTR_WEP_KEY_MAPPINGS_LEN, - WL3501_MIB_ATTR_EXCLUDE_UNENCRYPTED, - WL3501_MIB_ATTR_WEP_ICV_ERROR_COUNT, - WL3501_MIB_ATTR_WEP_UNDECRYPTABLE_COUNT, - WL3501_MIB_ATTR_WEP_EXCLUDED_COUNT, - WL3501_MIB_ATTR_MAC_ADDR, - WL3501_MIB_ATTR_GROUP_ADDRS, - WL3501_MIB_ATTR_RTS_THRESHOLD, - WL3501_MIB_ATTR_SHORT_RETRY_LIMIT, - WL3501_MIB_ATTR_LONG_RETRY_LIMIT, - WL3501_MIB_ATTR_FRAG_THRESHOLD, - WL3501_MIB_ATTR_MAX_TX_MSDU_LIFETIME, - WL3501_MIB_ATTR_MAX_RX_LIFETIME, - WL3501_MIB_ATTR_MANUFACTURER_ID, - WL3501_MIB_ATTR_PRODUCT_ID, - WL3501_MIB_ATTR_TX_FRAG_COUNT, - WL3501_MIB_ATTR_MULTICAST_TX_FRAME_COUNT, - WL3501_MIB_ATTR_FAILED_COUNT, - WL3501_MIB_ATTR_RX_FRAG_COUNT, - WL3501_MIB_ATTR_MULTICAST_RX_COUNT, - WL3501_MIB_ATTR_FCS_ERROR_COUNT, - WL3501_MIB_ATTR_RETRY_COUNT, - WL3501_MIB_ATTR_MULTIPLE_RETRY_COUNT, - WL3501_MIB_ATTR_RTS_SUCCESS_COUNT, - WL3501_MIB_ATTR_RTS_FAILURE_COUNT, - WL3501_MIB_ATTR_ACK_FAILURE_COUNT, - WL3501_MIB_ATTR_FRAME_DUPLICATE_COUNT, - WL3501_MIB_ATTR_PHY_TYPE, - WL3501_MIB_ATTR_REG_DOMAINS_SUPPORT, - WL3501_MIB_ATTR_CURRENT_REG_DOMAIN, - WL3501_MIB_ATTR_SLOT_TIME, - WL3501_MIB_ATTR_CCA_TIME, - WL3501_MIB_ATTR_RX_TX_TURNAROUND_TIME, - WL3501_MIB_ATTR_TX_PLCP_DELAY, - WL3501_MIB_ATTR_RX_TX_SWITCH_TIME, - WL3501_MIB_ATTR_TX_RAMP_ON_TIME, - WL3501_MIB_ATTR_TX_RF_DELAY, - WL3501_MIB_ATTR_SIFS_TIME, - WL3501_MIB_ATTR_RX_RF_DELAY, - WL3501_MIB_ATTR_RX_PLCP_DELAY, - WL3501_MIB_ATTR_MAC_PROCESSING_DELAY, - WL3501_MIB_ATTR_TX_RAMP_OFF_TIME, - WL3501_MIB_ATTR_PREAMBLE_LEN, - WL3501_MIB_ATTR_PLCP_HEADER_LEN, - WL3501_MIB_ATTR_MPDU_DURATION_FACTOR, - WL3501_MIB_ATTR_AIR_PROPAGATION_TIME, - WL3501_MIB_ATTR_TEMP_TYPE, - WL3501_MIB_ATTR_CW_MIN, - WL3501_MIB_ATTR_CW_MAX, - WL3501_MIB_ATTR_SUPPORT_DATA_RATES_TX, - WL3501_MIB_ATTR_SUPPORT_DATA_RATES_RX, - WL3501_MIB_ATTR_MPDU_MAX_LEN, - WL3501_MIB_ATTR_SUPPORT_TX_ANTENNAS, - WL3501_MIB_ATTR_CURRENT_TX_ANTENNA, - WL3501_MIB_ATTR_SUPPORT_RX_ANTENNAS, - WL3501_MIB_ATTR_DIVERSITY_SUPPORT, - WL3501_MIB_ATTR_DIVERSITY_SELECTION_RS, - WL3501_MIB_ATTR_NR_SUPPORTED_PWR_LEVELS, - WL3501_MIB_ATTR_TX_PWR_LEVEL1, - WL3501_MIB_ATTR_TX_PWR_LEVEL2, - WL3501_MIB_ATTR_TX_PWR_LEVEL3, - WL3501_MIB_ATTR_TX_PWR_LEVEL4, - WL3501_MIB_ATTR_TX_PWR_LEVEL5, - WL3501_MIB_ATTR_TX_PWR_LEVEL6, - WL3501_MIB_ATTR_TX_PWR_LEVEL7, - WL3501_MIB_ATTR_TX_PWR_LEVEL8, - WL3501_MIB_ATTR_CURRENT_TX_PWR_LEVEL, - WL3501_MIB_ATTR_CURRENT_CHAN, - WL3501_MIB_ATTR_CCA_MODE_SUPPORTED, - WL3501_MIB_ATTR_CURRENT_CCA_MODE, - WL3501_MIB_ATTR_ED_THRESHOLD, - WL3501_MIB_ATTR_SINTHESIZER_LOCKED, - WL3501_MIB_ATTR_CURRENT_PWR_STATE, - WL3501_MIB_ATTR_DOZE_TURNON_TIME, - WL3501_MIB_ATTR_RCR33, - WL3501_MIB_ATTR_DEFAULT_CHAN, - WL3501_MIB_ATTR_SSID, - WL3501_MIB_ATTR_PWR_MGMT_ENABLE, - WL3501_MIB_ATTR_NET_CAPABILITY, - WL3501_MIB_ATTR_ROUTING, -}; - -enum wl3501_net_type { - WL3501_NET_TYPE_INFRA, - WL3501_NET_TYPE_ADHOC, - WL3501_NET_TYPE_ANY_BSS, -}; - -enum wl3501_scan_type { - WL3501_SCAN_TYPE_ACTIVE, - WL3501_SCAN_TYPE_PASSIVE, -}; - -enum wl3501_tx_result { - WL3501_TX_RESULT_SUCCESS, - WL3501_TX_RESULT_NO_BSS, - WL3501_TX_RESULT_RETRY_LIMIT, -}; - -enum wl3501_sys_type { - WL3501_SYS_TYPE_OPEN, - WL3501_SYS_TYPE_SHARE_KEY, -}; - -enum wl3501_status { - WL3501_STATUS_SUCCESS, - WL3501_STATUS_INVALID, - WL3501_STATUS_TIMEOUT, - WL3501_STATUS_REFUSED, - WL3501_STATUS_MANY_REQ, - WL3501_STATUS_ALREADY_BSS, -}; - -#define WL3501_MGMT_CAPABILITY_ESS 0x0001 /* see 802.11 p.58 */ -#define WL3501_MGMT_CAPABILITY_IBSS 0x0002 /* - " - */ -#define WL3501_MGMT_CAPABILITY_CF_POLLABLE 0x0004 /* - " - */ -#define WL3501_MGMT_CAPABILITY_CF_POLL_REQUEST 0x0008 /* - " - */ -#define WL3501_MGMT_CAPABILITY_PRIVACY 0x0010 /* - " - */ - -#define IW_REG_DOMAIN_FCC 0x10 /* Channel 1 to 11 USA */ -#define IW_REG_DOMAIN_DOC 0x20 /* Channel 1 to 11 Canada */ -#define IW_REG_DOMAIN_ETSI 0x30 /* Channel 1 to 13 Europe */ -#define IW_REG_DOMAIN_SPAIN 0x31 /* Channel 10 to 11 Spain */ -#define IW_REG_DOMAIN_FRANCE 0x32 /* Channel 10 to 13 France */ -#define IW_REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ -#define IW_REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan */ -#define IW_REG_DOMAIN_ISRAEL 0x50 /* Channel 3 - 9 Israel */ - -#define IW_MGMT_RATE_LABEL_MANDATORY 128 /* MSB */ - -enum iw_mgmt_rate_labels { - IW_MGMT_RATE_LABEL_1MBIT = 2, - IW_MGMT_RATE_LABEL_2MBIT = 4, - IW_MGMT_RATE_LABEL_5_5MBIT = 11, - IW_MGMT_RATE_LABEL_11MBIT = 22, -}; - -enum iw_mgmt_info_element_ids { - IW_MGMT_INFO_ELEMENT_SSID, /* Service Set Identity */ - IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES, - IW_MGMT_INFO_ELEMENT_FH_PARAMETER_SET, - IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET, - IW_MGMT_INFO_ELEMENT_CS_PARAMETER_SET, - IW_MGMT_INFO_ELEMENT_CS_TIM, /* Traffic Information Map */ - IW_MGMT_INFO_ELEMENT_IBSS_PARAMETER_SET, - /* 7-15: Reserved, unused */ - IW_MGMT_INFO_ELEMENT_CHALLENGE_TEXT = 16, - /* 17-31 Reserved for challenge text extension */ - /* 32-255 Reserved, unused */ -}; - -struct iw_mgmt_info_element { - u8 id; /* one of enum iw_mgmt_info_element_ids, - but sizeof(enum) > sizeof(u8) :-( */ - u8 len; - u8 data[]; -} __packed; - -struct iw_mgmt_essid_pset { - struct iw_mgmt_info_element el; - u8 essid[IW_ESSID_MAX_SIZE]; -} __packed; - -/* - * According to 802.11 Wireless Networks, the definitive guide - O'Reilly - * Pg 75 - */ -#define IW_DATA_RATE_MAX_LABELS 8 - -struct iw_mgmt_data_rset { - struct iw_mgmt_info_element el; - u8 data_rate_labels[IW_DATA_RATE_MAX_LABELS]; -} __packed; - -struct iw_mgmt_ds_pset { - struct iw_mgmt_info_element el; - u8 chan; -} __packed; - -struct iw_mgmt_cf_pset { - struct iw_mgmt_info_element el; - u8 cfp_count; - u8 cfp_period; - u16 cfp_max_duration; - u16 cfp_dur_remaining; -} __packed; - -struct iw_mgmt_ibss_pset { - struct iw_mgmt_info_element el; - u16 atim_window; -} __packed; - -struct wl3501_tx_hdr { - u16 tx_cnt; - u8 sync[16]; - u16 sfd; - u8 signal; - u8 service; - u16 len; - u16 crc16; - u16 frame_ctrl; - u16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - u16 seq_ctrl; - u8 addr4[ETH_ALEN]; -}; - -struct wl3501_rx_hdr { - u16 rx_next_blk; - u16 rc_next_frame_blk; - u8 rx_blk_ctrl; - u8 rx_next_frame; - u8 rx_next_frame1; - u8 rssi; - char time[8]; - u8 signal; - u8 service; - u16 len; - u16 crc16; - u16 frame_ctrl; - u16 duration; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - u16 seq; - u8 addr4[ETH_ALEN]; -}; - -struct wl3501_start_req { - u16 next_blk; - u8 sig_id; - u8 bss_type; - u16 beacon_period; - u16 dtim_period; - u16 probe_delay; - u16 cap_info; - struct iw_mgmt_essid_pset ssid; - struct iw_mgmt_data_rset bss_basic_rset; - struct iw_mgmt_data_rset operational_rset; - struct iw_mgmt_cf_pset cf_pset; - struct iw_mgmt_ds_pset ds_pset; - struct iw_mgmt_ibss_pset ibss_pset; -}; - -struct wl3501_assoc_req { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 timeout; - u16 cap_info; - u16 listen_interval; - u8 mac_addr[ETH_ALEN]; -}; - -struct wl3501_assoc_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 status; -}; - -struct wl3501_assoc_ind { - u16 next_blk; - u8 sig_id; - u8 mac_addr[ETH_ALEN]; -}; - -struct wl3501_auth_req { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 type; - u16 timeout; - u8 mac_addr[ETH_ALEN]; -}; - -struct wl3501_auth_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 type; - u16 status; - u8 mac_addr[ETH_ALEN]; -}; - -struct wl3501_get_req { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 mib_attrib; -}; - -struct wl3501_get_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 mib_status; - u16 mib_attrib; - u8 mib_value[100]; -}; - -struct wl3501_req { - u16 beacon_period; - u16 dtim_period; - u16 cap_info; - u8 bss_type; - u8 bssid[ETH_ALEN]; - struct iw_mgmt_essid_pset ssid; - struct iw_mgmt_ds_pset ds_pset; - struct iw_mgmt_cf_pset cf_pset; - struct iw_mgmt_ibss_pset ibss_pset; - struct iw_mgmt_data_rset bss_basic_rset; -}; - -struct wl3501_join_req { - u16 next_blk; - u8 sig_id; - u8 reserved; - struct iw_mgmt_data_rset operational_rset; - u16 reserved2; - u16 timeout; - u16 probe_delay; - u8 timestamp[8]; - u8 local_time[8]; - struct wl3501_req req; -}; - -struct wl3501_join_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 status; -}; - -struct wl3501_pwr_mgmt_req { - u16 next_blk; - u8 sig_id; - u8 pwr_save; - u8 wake_up; - u8 receive_dtims; -}; - -struct wl3501_pwr_mgmt_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 status; -}; - -struct wl3501_scan_req { - u16 next_blk; - u8 sig_id; - u8 bss_type; - u16 probe_delay; - u16 min_chan_time; - u16 max_chan_time; - u8 chan_list[14]; - u8 bssid[ETH_ALEN]; - struct iw_mgmt_essid_pset ssid; - enum wl3501_scan_type scan_type; -}; - -struct wl3501_scan_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 status; - char timestamp[8]; - char localtime[8]; - struct wl3501_req req; - u8 rssi; -}; - -struct wl3501_start_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 status; -}; - -struct wl3501_md_req { - u16 next_blk; - u8 sig_id; - u8 routing; - u16 data; - u16 size; - u8 pri; - u8 service_class; - struct { - u8 daddr[ETH_ALEN]; - u8 saddr[ETH_ALEN]; - } addr; -}; - -struct wl3501_md_ind { - u16 next_blk; - u8 sig_id; - u8 routing; - u16 data; - u16 size; - u8 reception; - u8 pri; - u8 service_class; - struct { - u8 daddr[ETH_ALEN]; - u8 saddr[ETH_ALEN]; - } addr; -}; - -struct wl3501_md_confirm { - u16 next_blk; - u8 sig_id; - u8 reserved; - u16 data; - u8 status; - u8 pri; - u8 service_class; -}; - -struct wl3501_resync_req { - u16 next_blk; - u8 sig_id; -}; - -/* Definitions for supporting clone adapters. */ -/* System Interface Registers (SIR space) */ -#define WL3501_NIC_GCR ((u8)0x00) /* SIR0 - General Conf Register */ -#define WL3501_NIC_BSS ((u8)0x01) /* SIR1 - Bank Switching Select Reg */ -#define WL3501_NIC_LMAL ((u8)0x02) /* SIR2 - Local Mem addr Reg [7:0] */ -#define WL3501_NIC_LMAH ((u8)0x03) /* SIR3 - Local Mem addr Reg [14:8] */ -#define WL3501_NIC_IODPA ((u8)0x04) /* SIR4 - I/O Data Port A */ -#define WL3501_NIC_IODPB ((u8)0x05) /* SIR5 - I/O Data Port B */ -#define WL3501_NIC_IODPC ((u8)0x06) /* SIR6 - I/O Data Port C */ -#define WL3501_NIC_IODPD ((u8)0x07) /* SIR7 - I/O Data Port D */ - -/* Bits in GCR */ -#define WL3501_GCR_SWRESET ((u8)0x80) -#define WL3501_GCR_CORESET ((u8)0x40) -#define WL3501_GCR_DISPWDN ((u8)0x20) -#define WL3501_GCR_ECWAIT ((u8)0x10) -#define WL3501_GCR_ECINT ((u8)0x08) -#define WL3501_GCR_INT2EC ((u8)0x04) -#define WL3501_GCR_ENECINT ((u8)0x02) -#define WL3501_GCR_DAM ((u8)0x01) - -/* Bits in BSS (Bank Switching Select Register) */ -#define WL3501_BSS_FPAGE0 ((u8)0x20) /* Flash memory page0 */ -#define WL3501_BSS_FPAGE1 ((u8)0x28) -#define WL3501_BSS_FPAGE2 ((u8)0x30) -#define WL3501_BSS_FPAGE3 ((u8)0x38) -#define WL3501_BSS_SPAGE0 ((u8)0x00) /* SRAM page0 */ -#define WL3501_BSS_SPAGE1 ((u8)0x08) -#define WL3501_BSS_SPAGE2 ((u8)0x10) -#define WL3501_BSS_SPAGE3 ((u8)0x18) - -/* Define Driver Interface */ -/* Refer IEEE 802.11 */ -/* Tx packet header, include PLCP and MPDU */ -/* Tx PLCP Header */ -struct wl3501_80211_tx_plcp_hdr { - u8 sync[16]; - u16 sfd; - u8 signal; - u8 service; - u16 len; - u16 crc16; -} __packed; - -struct wl3501_80211_tx_hdr { - struct wl3501_80211_tx_plcp_hdr pclp_hdr; - struct ieee80211_hdr mac_hdr; -} __packed __aligned(2); - -/* - Reserve the beginning Tx space for descriptor use. - - TxBlockOffset --> *----*----*----*----* \ - (TxFreeDesc) | 0 | 1 | 2 | 3 | \ - | 4 | 5 | 6 | 7 | | - | 8 | 9 | 10 | 11 | TX_DESC * 20 - | 12 | 13 | 14 | 15 | | - | 16 | 17 | 18 | 19 | / - TxBufferBegin --> *----*----*----*----* / - (TxBufferHead) | | - (TxBufferTail) | | - | Send Buffer | - | | - | | - *-------------------* - TxBufferEnd -------------------------/ - -*/ - -struct wl3501_card { - int base_addr; - u8 mac_addr[ETH_ALEN]; - spinlock_t lock; - wait_queue_head_t wait; - struct wl3501_get_confirm sig_get_confirm; - struct wl3501_pwr_mgmt_confirm sig_pwr_mgmt_confirm; - u16 tx_buffer_size; - u16 tx_buffer_head; - u16 tx_buffer_tail; - u16 tx_buffer_cnt; - u16 esbq_req_start; - u16 esbq_req_end; - u16 esbq_req_head; - u16 esbq_req_tail; - u16 esbq_confirm_start; - u16 esbq_confirm_end; - u16 esbq_confirm; - struct iw_mgmt_essid_pset essid; - struct iw_mgmt_essid_pset keep_essid; - u8 bssid[ETH_ALEN]; - int net_type; - char nick[32]; - char card_name[32]; - char firmware_date[32]; - u8 chan; - u8 cap_info; - u16 start_seg; - u16 bss_cnt; - u16 join_sta_bss; - u8 rssi; - u8 adhoc_times; - u8 reg_domain; - u8 version[2]; - struct wl3501_scan_confirm bss_set[20]; - - struct iw_statistics wstats; - struct iw_spy_data spy_data; - struct iw_public_data wireless_data; - struct pcmcia_device *p_dev; -}; -#endif diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c deleted file mode 100644 index 7fb2f9513476..000000000000 --- a/drivers/net/wireless/wl3501_cs.c +++ /dev/null @@ -1,2030 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * WL3501 Wireless LAN PCMCIA Card Driver for Linux - * Written originally for Linux 2.0.30 by Fox Chen, mhchen@golf.ccl.itri.org.tw - * Ported to 2.2, 2.4 & 2.5 by Arnaldo Carvalho de Melo - * Wireless extensions in 2.4 by Gustavo Niemeyer - * - * References used by Fox Chen while writing the original driver for 2.0.30: - * - * 1. WL24xx packet drivers (tooasm.asm) - * 2. Access Point Firmware Interface Specification for IEEE 802.11 SUTRO - * 3. IEEE 802.11 - * 4. Linux network driver (/usr/src/linux/drivers/net) - * 5. ISA card driver - wl24.c - * 6. Linux PCMCIA skeleton driver - skeleton.c - * 7. Linux PCMCIA 3c589 network driver - 3c589_cs.c - * - * Tested with WL2400 firmware 1.2, Linux 2.0.30, and pcmcia-cs-2.9.12 - * 1. Performance: about 165 Kbytes/sec in TCP/IP with Ad-Hoc mode. - * rsh 192.168.1.3 "dd if=/dev/zero bs=1k count=1000" > /dev/null - * (Specification 2M bits/sec. is about 250 Kbytes/sec., but we must deduct - * ETHER/IP/UDP/TCP header, and acknowledgement overhead) - * - * Tested with Planet AP in 2.4.17, 184 Kbytes/s in UDP in Infrastructure mode, - * 173 Kbytes/s in TCP. - * - * Tested with Planet AP in 2.5.73-bk, 216 Kbytes/s in Infrastructure mode - * with a SMP machine (dual pentium 100), using pktgen, 432 pps (pkt_size = 60) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -#include "wl3501.h" - -#ifndef __i386__ -#define slow_down_io() -#endif - -/* For rough constant delay */ -#define WL3501_NOPLOOP(n) { int x = 0; while (x++ < n) slow_down_io(); } - - - -#define wl3501_outb(a, b) { outb(a, b); slow_down_io(); } -#define wl3501_outb_p(a, b) { outb_p(a, b); slow_down_io(); } -#define wl3501_outsb(a, b, c) { outsb(a, b, c); slow_down_io(); } - -#define WL3501_RELEASE_TIMEOUT (25 * HZ) -#define WL3501_MAX_ADHOC_TRIES 16 - -#define WL3501_RESUME 0 -#define WL3501_SUSPEND 1 - -static int wl3501_config(struct pcmcia_device *link); -static void wl3501_release(struct pcmcia_device *link); - -static const struct { - int reg_domain; - int min, max, deflt; -} iw_channel_table[] = { - { - .reg_domain = IW_REG_DOMAIN_FCC, - .min = 1, - .max = 11, - .deflt = 1, - }, - { - .reg_domain = IW_REG_DOMAIN_DOC, - .min = 1, - .max = 11, - .deflt = 1, - }, - { - .reg_domain = IW_REG_DOMAIN_ETSI, - .min = 1, - .max = 13, - .deflt = 1, - }, - { - .reg_domain = IW_REG_DOMAIN_SPAIN, - .min = 10, - .max = 11, - .deflt = 10, - }, - { - .reg_domain = IW_REG_DOMAIN_FRANCE, - .min = 10, - .max = 13, - .deflt = 10, - }, - { - .reg_domain = IW_REG_DOMAIN_MKK, - .min = 14, - .max = 14, - .deflt = 14, - }, - { - .reg_domain = IW_REG_DOMAIN_MKK1, - .min = 1, - .max = 14, - .deflt = 1, - }, - { - .reg_domain = IW_REG_DOMAIN_ISRAEL, - .min = 3, - .max = 9, - .deflt = 9, - }, -}; - -/** - * iw_valid_channel - validate channel in regulatory domain - * @reg_domain: regulatory domain - * @channel: channel to validate - * - * Returns 0 if invalid in the specified regulatory domain, non-zero if valid. - */ -static int iw_valid_channel(int reg_domain, int channel) -{ - int i, rc = 0; - - for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++) - if (reg_domain == iw_channel_table[i].reg_domain) { - rc = channel >= iw_channel_table[i].min && - channel <= iw_channel_table[i].max; - break; - } - return rc; -} - -/** - * iw_default_channel - get default channel for a regulatory domain - * @reg_domain: regulatory domain - * - * Returns the default channel for a regulatory domain - */ -static int iw_default_channel(int reg_domain) -{ - int i, rc = 1; - - for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++) - if (reg_domain == iw_channel_table[i].reg_domain) { - rc = iw_channel_table[i].deflt; - break; - } - return rc; -} - -static void iw_set_mgmt_info_element(enum iw_mgmt_info_element_ids id, - struct iw_mgmt_info_element *el, - void *value, int len) -{ - el->id = id; - el->len = len; - memcpy(el->data, value, len); -} - -static void iw_copy_mgmt_info_element(struct iw_mgmt_info_element *to, - struct iw_mgmt_info_element *from) -{ - iw_set_mgmt_info_element(from->id, to, from->data, from->len); -} - -static inline void wl3501_switch_page(struct wl3501_card *this, u8 page) -{ - wl3501_outb(page, this->base_addr + WL3501_NIC_BSS); -} - -/* - * Get Ethernet MAC address. - * - * WARNING: We switch to FPAGE0 and switc back again. - * Making sure there is no other WL function beening called by ISR. - */ -static int wl3501_get_flash_mac_addr(struct wl3501_card *this) -{ - int base_addr = this->base_addr; - - /* get MAC addr */ - wl3501_outb(WL3501_BSS_FPAGE3, base_addr + WL3501_NIC_BSS); /* BSS */ - wl3501_outb(0x00, base_addr + WL3501_NIC_LMAL); /* LMAL */ - wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH); /* LMAH */ - - /* wait for reading EEPROM */ - WL3501_NOPLOOP(100); - this->mac_addr[0] = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - this->mac_addr[1] = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - this->mac_addr[2] = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - this->mac_addr[3] = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - this->mac_addr[4] = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - this->mac_addr[5] = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - this->reg_domain = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - wl3501_outb(WL3501_BSS_FPAGE0, base_addr + WL3501_NIC_BSS); - wl3501_outb(0x04, base_addr + WL3501_NIC_LMAL); - wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH); - WL3501_NOPLOOP(100); - this->version[0] = inb(base_addr + WL3501_NIC_IODPA); - WL3501_NOPLOOP(100); - this->version[1] = inb(base_addr + WL3501_NIC_IODPA); - /* switch to SRAM Page 0 (for safety) */ - wl3501_switch_page(this, WL3501_BSS_SPAGE0); - - /* The MAC addr should be 00:60:... */ - return this->mac_addr[0] == 0x00 && this->mac_addr[1] == 0x60; -} - -/** - * wl3501_set_to_wla - Move 'size' bytes from PC to card - * @this: Card - * @dest: Card addressing space - * @src: PC addressing space - * @size: Bytes to move - * - * Move 'size' bytes from PC to card. (Shouldn't be interrupted) - */ -static void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, - int size) -{ - /* switch to SRAM Page 0 */ - wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 : - WL3501_BSS_SPAGE0); - /* set LMAL and LMAH */ - wl3501_outb(dest & 0xff, this->base_addr + WL3501_NIC_LMAL); - wl3501_outb(((dest >> 8) & 0x7f), this->base_addr + WL3501_NIC_LMAH); - - /* rep out to Port A */ - wl3501_outsb(this->base_addr + WL3501_NIC_IODPA, src, size); -} - -/** - * wl3501_get_from_wla - Move 'size' bytes from card to PC - * @this: Card - * @src: Card addressing space - * @dest: PC addressing space - * @size: Bytes to move - * - * Move 'size' bytes from card to PC. (Shouldn't be interrupted) - */ -static void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest, - int size) -{ - /* switch to SRAM Page 0 */ - wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 : - WL3501_BSS_SPAGE0); - /* set LMAL and LMAH */ - wl3501_outb(src & 0xff, this->base_addr + WL3501_NIC_LMAL); - wl3501_outb((src >> 8) & 0x7f, this->base_addr + WL3501_NIC_LMAH); - - /* rep get from Port A */ - insb(this->base_addr + WL3501_NIC_IODPA, dest, size); -} - -/* - * Get/Allocate a free Tx Data Buffer - * - * *--------------*-----------------*----------------------------------* - * | PLCP | MAC Header | DST SRC Data ... | - * | (24 bytes) | (30 bytes) | (6) (6) (Ethernet Row Data) | - * *--------------*-----------------*----------------------------------* - * \ \- IEEE 802.11 -/ \-------------- len --------------/ - * \-struct wl3501_80211_tx_hdr--/ \-------- Ethernet Frame -------/ - * - * Return = Position in Card - */ -static u16 wl3501_get_tx_buffer(struct wl3501_card *this, u16 len) -{ - u16 next, blk_cnt = 0, zero = 0; - u16 full_len = sizeof(struct wl3501_80211_tx_hdr) + len; - u16 ret = 0; - - if (full_len > this->tx_buffer_cnt * 254) - goto out; - ret = this->tx_buffer_head; - while (full_len) { - if (full_len < 254) - full_len = 0; - else - full_len -= 254; - wl3501_get_from_wla(this, this->tx_buffer_head, &next, - sizeof(next)); - if (!full_len) - wl3501_set_to_wla(this, this->tx_buffer_head, &zero, - sizeof(zero)); - this->tx_buffer_head = next; - blk_cnt++; - /* if buffer is not enough */ - if (!next && full_len) { - this->tx_buffer_head = ret; - ret = 0; - goto out; - } - } - this->tx_buffer_cnt -= blk_cnt; -out: - return ret; -} - -/* - * Free an allocated Tx Buffer. ptr must be correct position. - */ -static void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr) -{ - /* check if all space is not free */ - if (!this->tx_buffer_head) - this->tx_buffer_head = ptr; - else - wl3501_set_to_wla(this, this->tx_buffer_tail, - &ptr, sizeof(ptr)); - while (ptr) { - u16 next; - - this->tx_buffer_cnt++; - wl3501_get_from_wla(this, ptr, &next, sizeof(next)); - this->tx_buffer_tail = ptr; - ptr = next; - } -} - -static int wl3501_esbq_req_test(struct wl3501_card *this) -{ - u8 tmp = 0; - - wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp)); - return tmp & 0x80; -} - -static void wl3501_esbq_req(struct wl3501_card *this, u16 *ptr) -{ - u16 tmp = 0; - - wl3501_set_to_wla(this, this->esbq_req_head, ptr, 2); - wl3501_set_to_wla(this, this->esbq_req_head + 2, &tmp, sizeof(tmp)); - this->esbq_req_head += 4; - if (this->esbq_req_head >= this->esbq_req_end) - this->esbq_req_head = this->esbq_req_start; -} - -static int wl3501_esbq_exec(struct wl3501_card *this, void *sig, int sig_size) -{ - int rc = -EIO; - - if (wl3501_esbq_req_test(this)) { - u16 ptr = wl3501_get_tx_buffer(this, sig_size); - if (ptr) { - wl3501_set_to_wla(this, ptr, sig, sig_size); - wl3501_esbq_req(this, &ptr); - rc = 0; - } - } - return rc; -} - -static int wl3501_request_mib(struct wl3501_card *this, u8 index, void *bf) -{ - struct wl3501_get_req sig = { - .sig_id = WL3501_SIG_GET_REQ, - .mib_attrib = index, - }; - unsigned long flags; - int rc = -EIO; - - spin_lock_irqsave(&this->lock, flags); - if (wl3501_esbq_req_test(this)) { - u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig)); - if (ptr) { - wl3501_set_to_wla(this, ptr, &sig, sizeof(sig)); - wl3501_esbq_req(this, &ptr); - this->sig_get_confirm.mib_status = 255; - rc = 0; - } - } - spin_unlock_irqrestore(&this->lock, flags); - - return rc; -} - -static int wl3501_get_mib_value(struct wl3501_card *this, u8 index, - void *bf, int size) -{ - int rc; - - rc = wl3501_request_mib(this, index, bf); - if (rc) - return rc; - - rc = wait_event_interruptible(this->wait, - this->sig_get_confirm.mib_status != 255); - if (rc) - return rc; - - memcpy(bf, this->sig_get_confirm.mib_value, size); - return 0; -} - -static int wl3501_pwr_mgmt(struct wl3501_card *this, int suspend) -{ - struct wl3501_pwr_mgmt_req sig = { - .sig_id = WL3501_SIG_PWR_MGMT_REQ, - .pwr_save = suspend, - .wake_up = !suspend, - .receive_dtims = 10, - }; - unsigned long flags; - int rc = -EIO; - - spin_lock_irqsave(&this->lock, flags); - if (wl3501_esbq_req_test(this)) { - u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig)); - if (ptr) { - wl3501_set_to_wla(this, ptr, &sig, sizeof(sig)); - wl3501_esbq_req(this, &ptr); - this->sig_pwr_mgmt_confirm.status = 255; - spin_unlock_irqrestore(&this->lock, flags); - rc = wait_event_interruptible(this->wait, - this->sig_pwr_mgmt_confirm.status != 255); - printk(KERN_INFO "%s: %s status=%d\n", __func__, - suspend ? "suspend" : "resume", - this->sig_pwr_mgmt_confirm.status); - goto out; - } - } - spin_unlock_irqrestore(&this->lock, flags); -out: - return rc; -} - -/** - * wl3501_send_pkt - Send a packet. - * @this: Card - * @data: Ethernet raw frame. (e.g. data[0] - data[5] is Dest MAC Addr, - * data[6] - data[11] is Src MAC Addr) - * @len: Packet length - * Ref: IEEE 802.11 - */ -static int wl3501_send_pkt(struct wl3501_card *this, u8 *data, u16 len) -{ - u16 bf, sig_bf, next, tmplen, pktlen; - struct wl3501_md_req sig = { - .sig_id = WL3501_SIG_MD_REQ, - }; - size_t sig_addr_len = sizeof(sig.addr); - u8 *pdata = (char *)data; - int rc = -EIO; - - if (wl3501_esbq_req_test(this)) { - sig_bf = wl3501_get_tx_buffer(this, sizeof(sig)); - rc = -ENOMEM; - if (!sig_bf) /* No free buffer available */ - goto out; - bf = wl3501_get_tx_buffer(this, len + 26 + 24); - if (!bf) { - /* No free buffer available */ - wl3501_free_tx_buffer(this, sig_bf); - goto out; - } - rc = 0; - memcpy(&sig.addr, pdata, sig_addr_len); - pktlen = len - sig_addr_len; - pdata += sig_addr_len; - sig.data = bf; - if (((*pdata) * 256 + (*(pdata + 1))) > 1500) { - u8 addr4[ETH_ALEN] = { - [0] = 0xAA, [1] = 0xAA, [2] = 0x03, [4] = 0x00, - }; - - wl3501_set_to_wla(this, bf + 2 + - offsetof(struct wl3501_tx_hdr, addr4), - addr4, sizeof(addr4)); - sig.size = pktlen + 24 + 4 + 6; - if (pktlen > (254 - sizeof(struct wl3501_tx_hdr))) { - tmplen = 254 - sizeof(struct wl3501_tx_hdr); - pktlen -= tmplen; - } else { - tmplen = pktlen; - pktlen = 0; - } - wl3501_set_to_wla(this, - bf + 2 + sizeof(struct wl3501_tx_hdr), - pdata, tmplen); - pdata += tmplen; - wl3501_get_from_wla(this, bf, &next, sizeof(next)); - bf = next; - } else { - sig.size = pktlen + 24 + 4 - 2; - pdata += 2; - pktlen -= 2; - if (pktlen > (254 - sizeof(struct wl3501_tx_hdr) + 6)) { - tmplen = 254 - sizeof(struct wl3501_tx_hdr) + 6; - pktlen -= tmplen; - } else { - tmplen = pktlen; - pktlen = 0; - } - wl3501_set_to_wla(this, bf + 2 + - offsetof(struct wl3501_tx_hdr, addr4), - pdata, tmplen); - pdata += tmplen; - wl3501_get_from_wla(this, bf, &next, sizeof(next)); - bf = next; - } - while (pktlen > 0) { - if (pktlen > 254) { - tmplen = 254; - pktlen -= 254; - } else { - tmplen = pktlen; - pktlen = 0; - } - wl3501_set_to_wla(this, bf + 2, pdata, tmplen); - pdata += tmplen; - wl3501_get_from_wla(this, bf, &next, sizeof(next)); - bf = next; - } - wl3501_set_to_wla(this, sig_bf, &sig, sizeof(sig)); - wl3501_esbq_req(this, &sig_bf); - } -out: - return rc; -} - -static int wl3501_mgmt_resync(struct wl3501_card *this) -{ - struct wl3501_resync_req sig = { - .sig_id = WL3501_SIG_RESYNC_REQ, - }; - - return wl3501_esbq_exec(this, &sig, sizeof(sig)); -} - -static inline int wl3501_fw_bss_type(struct wl3501_card *this) -{ - return this->net_type == IW_MODE_INFRA ? WL3501_NET_TYPE_INFRA : - WL3501_NET_TYPE_ADHOC; -} - -static inline int wl3501_fw_cap_info(struct wl3501_card *this) -{ - return this->net_type == IW_MODE_INFRA ? WL3501_MGMT_CAPABILITY_ESS : - WL3501_MGMT_CAPABILITY_IBSS; -} - -static int wl3501_mgmt_scan(struct wl3501_card *this, u16 chan_time) -{ - struct wl3501_scan_req sig = { - .sig_id = WL3501_SIG_SCAN_REQ, - .scan_type = WL3501_SCAN_TYPE_ACTIVE, - .probe_delay = 0x10, - .min_chan_time = chan_time, - .max_chan_time = chan_time, - .bss_type = wl3501_fw_bss_type(this), - }; - - this->bss_cnt = this->join_sta_bss = 0; - return wl3501_esbq_exec(this, &sig, sizeof(sig)); -} - -static int wl3501_mgmt_join(struct wl3501_card *this, u16 stas) -{ - struct wl3501_join_req sig = { - .sig_id = WL3501_SIG_JOIN_REQ, - .timeout = 10, - .req.ds_pset = { - .el = { - .id = IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET, - .len = 1, - }, - .chan = this->chan, - }, - }; - - memcpy(&sig.req, &this->bss_set[stas].req, sizeof(sig.req)); - return wl3501_esbq_exec(this, &sig, sizeof(sig)); -} - -static int wl3501_mgmt_start(struct wl3501_card *this) -{ - struct wl3501_start_req sig = { - .sig_id = WL3501_SIG_START_REQ, - .beacon_period = 400, - .dtim_period = 1, - .ds_pset = { - .el = { - .id = IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET, - .len = 1, - }, - .chan = this->chan, - }, - .bss_basic_rset = { - .el = { - .id = IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES, - .len = 2, - }, - .data_rate_labels = { - [0] = IW_MGMT_RATE_LABEL_MANDATORY | - IW_MGMT_RATE_LABEL_1MBIT, - [1] = IW_MGMT_RATE_LABEL_MANDATORY | - IW_MGMT_RATE_LABEL_2MBIT, - }, - }, - .operational_rset = { - .el = { - .id = IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES, - .len = 2, - }, - .data_rate_labels = { - [0] = IW_MGMT_RATE_LABEL_MANDATORY | - IW_MGMT_RATE_LABEL_1MBIT, - [1] = IW_MGMT_RATE_LABEL_MANDATORY | - IW_MGMT_RATE_LABEL_2MBIT, - }, - }, - .ibss_pset = { - .el = { - .id = IW_MGMT_INFO_ELEMENT_IBSS_PARAMETER_SET, - .len = 2, - }, - .atim_window = 10, - }, - .bss_type = wl3501_fw_bss_type(this), - .cap_info = wl3501_fw_cap_info(this), - }; - - iw_copy_mgmt_info_element(&sig.ssid.el, &this->essid.el); - iw_copy_mgmt_info_element(&this->keep_essid.el, &this->essid.el); - return wl3501_esbq_exec(this, &sig, sizeof(sig)); -} - -static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr) -{ - u16 i = 0; - int matchflag = 0; - struct wl3501_scan_confirm sig; - - pr_debug("entry"); - wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); - if (sig.status == WL3501_STATUS_SUCCESS) { - pr_debug("success"); - if ((this->net_type == IW_MODE_INFRA && - (sig.req.cap_info & WL3501_MGMT_CAPABILITY_ESS)) || - (this->net_type == IW_MODE_ADHOC && - (sig.req.cap_info & WL3501_MGMT_CAPABILITY_IBSS)) || - this->net_type == IW_MODE_AUTO) { - if (!this->essid.el.len) - matchflag = 1; - else if (this->essid.el.len == 3 && - !memcmp(this->essid.essid, "ANY", 3)) - matchflag = 1; - else if (this->essid.el.len != sig.req.ssid.el.len) - matchflag = 0; - else if (memcmp(this->essid.essid, sig.req.ssid.essid, - this->essid.el.len)) - matchflag = 0; - else - matchflag = 1; - if (matchflag) { - for (i = 0; i < this->bss_cnt; i++) { - if (ether_addr_equal_unaligned(this->bss_set[i].req.bssid, - sig.req.bssid)) { - matchflag = 0; - break; - } - } - } - if (matchflag && (i < 20)) { - memcpy(&this->bss_set[i].req, - &sig.req, sizeof(sig.req)); - this->bss_cnt++; - this->rssi = sig.rssi; - this->bss_set[i].rssi = sig.rssi; - } - } - } else if (sig.status == WL3501_STATUS_TIMEOUT) { - pr_debug("timeout"); - this->join_sta_bss = 0; - for (i = this->join_sta_bss; i < this->bss_cnt; i++) - if (!wl3501_mgmt_join(this, i)) - break; - this->join_sta_bss = i; - if (this->join_sta_bss == this->bss_cnt) { - if (this->net_type == IW_MODE_INFRA) - wl3501_mgmt_scan(this, 100); - else { - this->adhoc_times++; - if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES) - wl3501_mgmt_start(this); - else - wl3501_mgmt_scan(this, 100); - } - } - } -} - -/** - * wl3501_block_interrupt - Mask interrupt from SUTRO - * @this: Card - * - * Mask interrupt from SUTRO. (i.e. SUTRO cannot interrupt the HOST) - * Return: 1 if interrupt is originally enabled - */ -static int wl3501_block_interrupt(struct wl3501_card *this) -{ - u8 old = inb(this->base_addr + WL3501_NIC_GCR); - u8 new = old & (~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC | - WL3501_GCR_ENECINT)); - - wl3501_outb(new, this->base_addr + WL3501_NIC_GCR); - return old & WL3501_GCR_ENECINT; -} - -/** - * wl3501_unblock_interrupt - Enable interrupt from SUTRO - * @this: Card - * - * Enable interrupt from SUTRO. (i.e. SUTRO can interrupt the HOST) - * Return: 1 if interrupt is originally enabled - */ -static int wl3501_unblock_interrupt(struct wl3501_card *this) -{ - u8 old = inb(this->base_addr + WL3501_NIC_GCR); - u8 new = (old & ~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC)) | - WL3501_GCR_ENECINT; - - wl3501_outb(new, this->base_addr + WL3501_NIC_GCR); - return old & WL3501_GCR_ENECINT; -} - -/** - * wl3501_receive - Receive data from Receive Queue. - * - * Receive data from Receive Queue. - * - * @this: card - * @bf: address of host - * @size: size of buffer. - */ -static u16 wl3501_receive(struct wl3501_card *this, u8 *bf, u16 size) -{ - u16 next_addr, next_addr1; - u8 *data = bf + 12; - - size -= 12; - wl3501_get_from_wla(this, this->start_seg + 2, - &next_addr, sizeof(next_addr)); - if (size > WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr)) { - wl3501_get_from_wla(this, - this->start_seg + - sizeof(struct wl3501_rx_hdr), data, - WL3501_BLKSZ - - sizeof(struct wl3501_rx_hdr)); - size -= WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr); - data += WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr); - } else { - wl3501_get_from_wla(this, - this->start_seg + - sizeof(struct wl3501_rx_hdr), - data, size); - size = 0; - } - while (size > 0) { - if (size > WL3501_BLKSZ - 5) { - wl3501_get_from_wla(this, next_addr + 5, data, - WL3501_BLKSZ - 5); - size -= WL3501_BLKSZ - 5; - data += WL3501_BLKSZ - 5; - wl3501_get_from_wla(this, next_addr + 2, &next_addr1, - sizeof(next_addr1)); - next_addr = next_addr1; - } else { - wl3501_get_from_wla(this, next_addr + 5, data, size); - size = 0; - } - } - return 0; -} - -static void wl3501_esbq_req_free(struct wl3501_card *this) -{ - u8 tmp; - u16 addr; - - if (this->esbq_req_head == this->esbq_req_tail) - goto out; - wl3501_get_from_wla(this, this->esbq_req_tail + 3, &tmp, sizeof(tmp)); - if (!(tmp & 0x80)) - goto out; - wl3501_get_from_wla(this, this->esbq_req_tail, &addr, sizeof(addr)); - wl3501_free_tx_buffer(this, addr); - this->esbq_req_tail += 4; - if (this->esbq_req_tail >= this->esbq_req_end) - this->esbq_req_tail = this->esbq_req_start; -out: - return; -} - -static int wl3501_esbq_confirm(struct wl3501_card *this) -{ - u8 tmp; - - wl3501_get_from_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp)); - return tmp & 0x80; -} - -static void wl3501_online(struct net_device *dev) -{ - struct wl3501_card *this = netdev_priv(dev); - - printk(KERN_INFO "%s: Wireless LAN online. BSSID: %pM\n", - dev->name, this->bssid); - netif_wake_queue(dev); -} - -static void wl3501_esbq_confirm_done(struct wl3501_card *this) -{ - u8 tmp = 0; - - wl3501_set_to_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp)); - this->esbq_confirm += 4; - if (this->esbq_confirm >= this->esbq_confirm_end) - this->esbq_confirm = this->esbq_confirm_start; -} - -static int wl3501_mgmt_auth(struct wl3501_card *this) -{ - struct wl3501_auth_req sig = { - .sig_id = WL3501_SIG_AUTH_REQ, - .type = WL3501_SYS_TYPE_OPEN, - .timeout = 1000, - }; - - pr_debug("entry"); - memcpy(sig.mac_addr, this->bssid, ETH_ALEN); - return wl3501_esbq_exec(this, &sig, sizeof(sig)); -} - -static int wl3501_mgmt_association(struct wl3501_card *this) -{ - struct wl3501_assoc_req sig = { - .sig_id = WL3501_SIG_ASSOC_REQ, - .timeout = 1000, - .listen_interval = 5, - .cap_info = this->cap_info, - }; - - pr_debug("entry"); - memcpy(sig.mac_addr, this->bssid, ETH_ALEN); - return wl3501_esbq_exec(this, &sig, sizeof(sig)); -} - -static void wl3501_mgmt_join_confirm(struct net_device *dev, u16 addr) -{ - struct wl3501_card *this = netdev_priv(dev); - struct wl3501_join_confirm sig; - - pr_debug("entry"); - wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); - if (sig.status == WL3501_STATUS_SUCCESS) { - if (this->net_type == IW_MODE_INFRA) { - if (this->join_sta_bss < this->bss_cnt) { - const int i = this->join_sta_bss; - memcpy(this->bssid, - this->bss_set[i].req.bssid, ETH_ALEN); - this->chan = this->bss_set[i].req.ds_pset.chan; - iw_copy_mgmt_info_element(&this->keep_essid.el, - &this->bss_set[i].req.ssid.el); - wl3501_mgmt_auth(this); - } - } else { - const int i = this->join_sta_bss; - - memcpy(&this->bssid, &this->bss_set[i].req.bssid, ETH_ALEN); - this->chan = this->bss_set[i].req.ds_pset.chan; - iw_copy_mgmt_info_element(&this->keep_essid.el, - &this->bss_set[i].req.ssid.el); - wl3501_online(dev); - } - } else { - int i; - this->join_sta_bss++; - for (i = this->join_sta_bss; i < this->bss_cnt; i++) - if (!wl3501_mgmt_join(this, i)) - break; - this->join_sta_bss = i; - if (this->join_sta_bss == this->bss_cnt) { - if (this->net_type == IW_MODE_INFRA) - wl3501_mgmt_scan(this, 100); - else { - this->adhoc_times++; - if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES) - wl3501_mgmt_start(this); - else - wl3501_mgmt_scan(this, 100); - } - } - } -} - -static inline void wl3501_alarm_interrupt(struct net_device *dev, - struct wl3501_card *this) -{ - if (this->net_type == IW_MODE_INFRA) { - printk(KERN_INFO "Wireless LAN offline\n"); - netif_stop_queue(dev); - wl3501_mgmt_resync(this); - } -} - -static inline void wl3501_md_confirm_interrupt(struct net_device *dev, - struct wl3501_card *this, - u16 addr) -{ - struct wl3501_md_confirm sig; - - pr_debug("entry"); - wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); - wl3501_free_tx_buffer(this, sig.data); - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -} - -static inline void wl3501_md_ind_interrupt(struct net_device *dev, - struct wl3501_card *this, u16 addr) -{ - struct wl3501_md_ind sig; - struct sk_buff *skb; - u8 rssi, addr4[ETH_ALEN]; - u16 pkt_len; - - wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); - this->start_seg = sig.data; - wl3501_get_from_wla(this, - sig.data + offsetof(struct wl3501_rx_hdr, rssi), - &rssi, sizeof(rssi)); - this->rssi = rssi <= 63 ? (rssi * 100) / 64 : 255; - - wl3501_get_from_wla(this, - sig.data + - offsetof(struct wl3501_rx_hdr, addr4), - &addr4, sizeof(addr4)); - if (!(addr4[0] == 0xAA && addr4[1] == 0xAA && - addr4[2] == 0x03 && addr4[4] == 0x00)) { - printk(KERN_INFO "Unsupported packet type!\n"); - return; - } - pkt_len = sig.size + 12 - 24 - 4 - 6; - - skb = dev_alloc_skb(pkt_len + 5); - - if (!skb) { - printk(KERN_WARNING "%s: Can't alloc a sk_buff of size %d.\n", - dev->name, pkt_len); - dev->stats.rx_dropped++; - } else { - skb->dev = dev; - skb_reserve(skb, 2); /* IP headers on 16 bytes boundaries */ - skb_copy_to_linear_data(skb, (unsigned char *)&sig.addr, - sizeof(sig.addr)); - wl3501_receive(this, skb->data, pkt_len); - skb_put(skb, pkt_len); - skb->protocol = eth_type_trans(skb, dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - netif_rx(skb); - } -} - -static inline void wl3501_get_confirm_interrupt(struct wl3501_card *this, - u16 addr, void *sig, int size) -{ - pr_debug("entry"); - wl3501_get_from_wla(this, addr, &this->sig_get_confirm, - sizeof(this->sig_get_confirm)); - wake_up(&this->wait); -} - -static inline void wl3501_start_confirm_interrupt(struct net_device *dev, - struct wl3501_card *this, - u16 addr) -{ - struct wl3501_start_confirm sig; - - pr_debug("entry"); - wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); - if (sig.status == WL3501_STATUS_SUCCESS) - netif_wake_queue(dev); -} - -static inline void wl3501_assoc_confirm_interrupt(struct net_device *dev, - u16 addr) -{ - struct wl3501_card *this = netdev_priv(dev); - struct wl3501_assoc_confirm sig; - - pr_debug("entry"); - wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); - - if (sig.status == WL3501_STATUS_SUCCESS) - wl3501_online(dev); -} - -static inline void wl3501_auth_confirm_interrupt(struct wl3501_card *this, - u16 addr) -{ - struct wl3501_auth_confirm sig; - - pr_debug("entry"); - wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); - - if (sig.status == WL3501_STATUS_SUCCESS) - wl3501_mgmt_association(this); - else - wl3501_mgmt_resync(this); -} - -static inline void wl3501_rx_interrupt(struct net_device *dev) -{ - int morepkts; - u16 addr; - u8 sig_id; - struct wl3501_card *this = netdev_priv(dev); - - pr_debug("entry"); -loop: - morepkts = 0; - if (!wl3501_esbq_confirm(this)) - goto free; - wl3501_get_from_wla(this, this->esbq_confirm, &addr, sizeof(addr)); - wl3501_get_from_wla(this, addr + 2, &sig_id, sizeof(sig_id)); - - switch (sig_id) { - case WL3501_SIG_DEAUTH_IND: - case WL3501_SIG_DISASSOC_IND: - case WL3501_SIG_ALARM: - wl3501_alarm_interrupt(dev, this); - break; - case WL3501_SIG_MD_CONFIRM: - wl3501_md_confirm_interrupt(dev, this, addr); - break; - case WL3501_SIG_MD_IND: - wl3501_md_ind_interrupt(dev, this, addr); - break; - case WL3501_SIG_GET_CONFIRM: - wl3501_get_confirm_interrupt(this, addr, - &this->sig_get_confirm, - sizeof(this->sig_get_confirm)); - break; - case WL3501_SIG_PWR_MGMT_CONFIRM: - wl3501_get_confirm_interrupt(this, addr, - &this->sig_pwr_mgmt_confirm, - sizeof(this->sig_pwr_mgmt_confirm)); - break; - case WL3501_SIG_START_CONFIRM: - wl3501_start_confirm_interrupt(dev, this, addr); - break; - case WL3501_SIG_SCAN_CONFIRM: - wl3501_mgmt_scan_confirm(this, addr); - break; - case WL3501_SIG_JOIN_CONFIRM: - wl3501_mgmt_join_confirm(dev, addr); - break; - case WL3501_SIG_ASSOC_CONFIRM: - wl3501_assoc_confirm_interrupt(dev, addr); - break; - case WL3501_SIG_AUTH_CONFIRM: - wl3501_auth_confirm_interrupt(this, addr); - break; - case WL3501_SIG_RESYNC_CONFIRM: - wl3501_mgmt_resync(this); /* FIXME: should be resync_confirm */ - break; - } - wl3501_esbq_confirm_done(this); - morepkts = 1; - /* free request if necessary */ -free: - wl3501_esbq_req_free(this); - if (morepkts) - goto loop; -} - -static inline void wl3501_ack_interrupt(struct wl3501_card *this) -{ - wl3501_outb(WL3501_GCR_ECINT, this->base_addr + WL3501_NIC_GCR); -} - -/** - * wl3501_interrupt - Hardware interrupt from card. - * @irq: Interrupt number - * @dev_id: net_device - * - * We must acknowledge the interrupt as soon as possible, and block the - * interrupt from the same card immediately to prevent re-entry. - * - * Before accessing the Control_Status_Block, we must lock SUTRO first. - * On the other hand, to prevent SUTRO from malfunctioning, we must - * unlock the SUTRO as soon as possible. - */ -static irqreturn_t wl3501_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct wl3501_card *this; - - this = netdev_priv(dev); - spin_lock(&this->lock); - wl3501_ack_interrupt(this); - wl3501_block_interrupt(this); - wl3501_rx_interrupt(dev); - wl3501_unblock_interrupt(this); - spin_unlock(&this->lock); - - return IRQ_HANDLED; -} - -static int wl3501_reset_board(struct wl3501_card *this) -{ - u8 tmp = 0; - int i, rc = 0; - - /* Coreset */ - wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR); - wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR); - wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR); - - /* Reset SRAM 0x480 to zero */ - wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp)); - - /* Start up */ - wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR); - - WL3501_NOPLOOP(1024 * 50); - - wl3501_unblock_interrupt(this); /* acme: was commented */ - - /* Polling Self_Test_Status */ - for (i = 0; i < 10000; i++) { - wl3501_get_from_wla(this, 0x480, &tmp, sizeof(tmp)); - - if (tmp == 'W') { - /* firmware complete all test successfully */ - tmp = 'A'; - wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp)); - goto out; - } - WL3501_NOPLOOP(10); - } - printk(KERN_WARNING "%s: failed to reset the board!\n", __func__); - rc = -ENODEV; -out: - return rc; -} - -static int wl3501_init_firmware(struct wl3501_card *this) -{ - u16 ptr, next; - int rc = wl3501_reset_board(this); - - if (rc) - goto fail; - this->card_name[0] = '\0'; - wl3501_get_from_wla(this, 0x1a00, - this->card_name, sizeof(this->card_name)); - this->card_name[sizeof(this->card_name) - 1] = '\0'; - this->firmware_date[0] = '\0'; - wl3501_get_from_wla(this, 0x1a40, - this->firmware_date, sizeof(this->firmware_date)); - this->firmware_date[sizeof(this->firmware_date) - 1] = '\0'; - /* Switch to SRAM Page 0 */ - wl3501_switch_page(this, WL3501_BSS_SPAGE0); - /* Read parameter from card */ - wl3501_get_from_wla(this, 0x482, &this->esbq_req_start, 2); - wl3501_get_from_wla(this, 0x486, &this->esbq_req_end, 2); - wl3501_get_from_wla(this, 0x488, &this->esbq_confirm_start, 2); - wl3501_get_from_wla(this, 0x48c, &this->esbq_confirm_end, 2); - wl3501_get_from_wla(this, 0x48e, &this->tx_buffer_head, 2); - wl3501_get_from_wla(this, 0x492, &this->tx_buffer_size, 2); - this->esbq_req_tail = this->esbq_req_head = this->esbq_req_start; - this->esbq_req_end += this->esbq_req_start; - this->esbq_confirm = this->esbq_confirm_start; - this->esbq_confirm_end += this->esbq_confirm_start; - /* Initial Tx Buffer */ - this->tx_buffer_cnt = 1; - ptr = this->tx_buffer_head; - next = ptr + WL3501_BLKSZ; - while ((next - this->tx_buffer_head) < this->tx_buffer_size) { - this->tx_buffer_cnt++; - wl3501_set_to_wla(this, ptr, &next, sizeof(next)); - ptr = next; - next = ptr + WL3501_BLKSZ; - } - rc = 0; - next = 0; - wl3501_set_to_wla(this, ptr, &next, sizeof(next)); - this->tx_buffer_tail = ptr; -out: - return rc; -fail: - printk(KERN_WARNING "%s: failed!\n", __func__); - goto out; -} - -static int wl3501_close(struct net_device *dev) -{ - struct wl3501_card *this = netdev_priv(dev); - unsigned long flags; - struct pcmcia_device *link; - link = this->p_dev; - - spin_lock_irqsave(&this->lock, flags); - link->open--; - - /* Stop wl3501_hard_start_xmit() from now on */ - netif_stop_queue(dev); - wl3501_ack_interrupt(this); - - /* Mask interrupts from the SUTRO */ - wl3501_block_interrupt(this); - - printk(KERN_INFO "%s: WL3501 closed\n", dev->name); - spin_unlock_irqrestore(&this->lock, flags); - return 0; -} - -/** - * wl3501_reset - Reset the SUTRO. - * @dev: network device - * - * It is almost the same as wl3501_open(). In fact, we may just wl3501_close() - * and wl3501_open() again, but I wouldn't like to free_irq() when the driver - * is running. It seems to be dangerous. - */ -static int wl3501_reset(struct net_device *dev) -{ - struct wl3501_card *this = netdev_priv(dev); - int rc = -ENODEV; - unsigned long flags; - - spin_lock_irqsave(&this->lock, flags); - wl3501_block_interrupt(this); - - if (wl3501_init_firmware(this)) { - printk(KERN_WARNING "%s: Can't initialize Firmware!\n", - dev->name); - /* Free IRQ, and mark IRQ as unused */ - free_irq(dev->irq, dev); - goto out; - } - - /* - * Queue has to be started only when the Card is Started - */ - netif_stop_queue(dev); - this->adhoc_times = 0; - wl3501_ack_interrupt(this); - wl3501_unblock_interrupt(this); - wl3501_mgmt_scan(this, 100); - pr_debug("%s: device reset", dev->name); - rc = 0; -out: - spin_unlock_irqrestore(&this->lock, flags); - return rc; -} - -static void wl3501_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct net_device_stats *stats = &dev->stats; - int rc; - - stats->tx_errors++; - rc = wl3501_reset(dev); - if (rc) - printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n", - dev->name, rc); - else { - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue(dev); - } -} - -/* - * Return : 0 - OK - * 1 - Could not transmit (dev_queue_xmit will queue it) - * and try to sent it later - */ -static netdev_tx_t wl3501_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - int enabled, rc; - struct wl3501_card *this = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&this->lock, flags); - enabled = wl3501_block_interrupt(this); - rc = wl3501_send_pkt(this, skb->data, skb->len); - if (enabled) - wl3501_unblock_interrupt(this); - if (rc) { - ++dev->stats.tx_dropped; - netif_stop_queue(dev); - } else { - ++dev->stats.tx_packets; - dev->stats.tx_bytes += skb->len; - dev_kfree_skb_irq(skb); - - if (this->tx_buffer_cnt < 2) - netif_stop_queue(dev); - } - spin_unlock_irqrestore(&this->lock, flags); - return NETDEV_TX_OK; -} - -static int wl3501_open(struct net_device *dev) -{ - int rc = -ENODEV; - struct wl3501_card *this = netdev_priv(dev); - unsigned long flags; - struct pcmcia_device *link; - link = this->p_dev; - - spin_lock_irqsave(&this->lock, flags); - if (!pcmcia_dev_present(link)) - goto out; - netif_device_attach(dev); - link->open++; - - /* Initial WL3501 firmware */ - pr_debug("%s: Initialize WL3501 firmware...", dev->name); - if (wl3501_init_firmware(this)) - goto fail; - /* Initial device variables */ - this->adhoc_times = 0; - /* Acknowledge Interrupt, for cleaning last state */ - wl3501_ack_interrupt(this); - - /* Enable interrupt from card after all */ - wl3501_unblock_interrupt(this); - wl3501_mgmt_scan(this, 100); - rc = 0; - pr_debug("%s: WL3501 opened", dev->name); - printk(KERN_INFO "%s: Card Name: %s\n" - "%s: Firmware Date: %s\n", - dev->name, this->card_name, - dev->name, this->firmware_date); -out: - spin_unlock_irqrestore(&this->lock, flags); - return rc; -fail: - printk(KERN_WARNING "%s: Can't initialize firmware!\n", dev->name); - goto out; -} - -static struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev) -{ - struct wl3501_card *this = netdev_priv(dev); - struct iw_statistics *wstats = &this->wstats; - u32 value; /* size checked: it is u32 */ - - memset(wstats, 0, sizeof(*wstats)); - wstats->status = netif_running(dev); - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_ICV_ERROR_COUNT, - &value, sizeof(value))) - wstats->discard.code += value; - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_UNDECRYPTABLE_COUNT, - &value, sizeof(value))) - wstats->discard.code += value; - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_EXCLUDED_COUNT, - &value, sizeof(value))) - wstats->discard.code += value; - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RETRY_COUNT, - &value, sizeof(value))) - wstats->discard.retries = value; - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FAILED_COUNT, - &value, sizeof(value))) - wstats->discard.misc += value; - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_FAILURE_COUNT, - &value, sizeof(value))) - wstats->discard.misc += value; - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_ACK_FAILURE_COUNT, - &value, sizeof(value))) - wstats->discard.misc += value; - if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAME_DUPLICATE_COUNT, - &value, sizeof(value))) - wstats->discard.misc += value; - return wstats; -} - -/** - * wl3501_detach - deletes a driver "instance" - * @link: FILL_IN - * - * This deletes a driver "instance". The device is de-registered with Card - * Services. If it has been released, all local data structures are freed. - * Otherwise, the structures will be freed when the device is released. - */ -static void wl3501_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - /* If the device is currently configured and active, we won't actually - * delete it yet. Instead, it is marked so that when the release() - * function is called, that will trigger a proper detach(). */ - - while (link->open > 0) - wl3501_close(dev); - - netif_device_detach(dev); - wl3501_release(link); - - unregister_netdev(dev); - free_netdev(dev); -} - -static int wl3501_get_name(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - strscpy(wrqu->name, "IEEE 802.11-DS", sizeof(wrqu->name)); - return 0; -} - -static int wl3501_set_freq(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - int channel = wrqu->freq.m; - int rc = -EINVAL; - - if (iw_valid_channel(this->reg_domain, channel)) { - this->chan = channel; - rc = wl3501_reset(dev); - } - return rc; -} - -static int wl3501_get_freq(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - - wrqu->freq.m = 100000 * - ieee80211_channel_to_frequency(this->chan, NL80211_BAND_2GHZ); - wrqu->freq.e = 1; - return 0; -} - -static int wl3501_set_mode(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - int rc = -EINVAL; - - if (wrqu->mode == IW_MODE_INFRA || - wrqu->mode == IW_MODE_ADHOC || - wrqu->mode == IW_MODE_AUTO) { - struct wl3501_card *this = netdev_priv(dev); - - this->net_type = wrqu->mode; - rc = wl3501_reset(dev); - } - return rc; -} - -static int wl3501_get_mode(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - - wrqu->mode = this->net_type; - return 0; -} - -static int wl3501_get_sens(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - - wrqu->sens.value = this->rssi; - wrqu->sens.disabled = !wrqu->sens.value; - wrqu->sens.fixed = 1; - return 0; -} - -static int wl3501_get_range(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct iw_range *range = (struct iw_range *)extra; - - /* Set the length (very important for backward compatibility) */ - wrqu->data.length = sizeof(*range); - - /* Set all the info we don't care or don't know about to zero */ - memset(range, 0, sizeof(*range)); - - /* Set the Wireless Extension versions */ - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 1; - range->throughput = 2 * 1000 * 1000; /* ~2 Mb/s */ - /* FIXME: study the code to fill in more fields... */ - return 0; -} - -static int wl3501_set_wap(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - int rc = -EINVAL; - - /* FIXME: we support other ARPHRDs...*/ - if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) - goto out; - if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data)) { - /* FIXME: rescan? */ - } else - memcpy(this->bssid, wrqu->ap_addr.sa_data, ETH_ALEN); - /* FIXME: rescan? deassoc & scan? */ - rc = 0; -out: - return rc; -} - -static int wl3501_get_wap(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - - wrqu->ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu->ap_addr.sa_data, this->bssid, ETH_ALEN); - return 0; -} - -static int wl3501_set_scan(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * FIXME: trigger scanning with a reset, yes, I'm lazy - */ - return wl3501_reset(dev); -} - -static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - int i; - char *current_ev = extra; - struct iw_event iwe; - - for (i = 0; i < this->bss_cnt; ++i) { - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, this->bss_set[i].req.bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_ADDR_LEN); - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - iwe.u.data.length = this->bss_set[i].req.ssid.el.len; - current_ev = iwe_stream_add_point(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, - this->bss_set[i].req.ssid.essid); - iwe.cmd = SIOCGIWMODE; - iwe.u.mode = this->bss_set[i].req.bss_type; - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_UINT_LEN); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = this->bss_set[i].req.ds_pset.chan; - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_FREQ_LEN); - iwe.cmd = SIOCGIWENCODE; - if (this->bss_set[i].req.cap_info & WL3501_MGMT_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, NULL); - } - /* Length of data */ - wrqu->data.length = (current_ev - extra); - wrqu->data.flags = 0; /* FIXME: set properly these flags */ - return 0; -} - -static int wl3501_set_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - - if (wrqu->data.flags) { - iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID, - &this->essid.el, - extra, wrqu->data.length); - } else { /* We accept any ESSID */ - iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID, - &this->essid.el, "ANY", 3); - } - return wl3501_reset(dev); -} - -static int wl3501_get_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&this->lock, flags); - wrqu->essid.flags = 1; - wrqu->essid.length = this->essid.el.len; - memcpy(extra, this->essid.essid, this->essid.el.len); - spin_unlock_irqrestore(&this->lock, flags); - return 0; -} - -static int wl3501_set_nick(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - - if (wrqu->data.length > sizeof(this->nick)) - return -E2BIG; - strscpy(this->nick, extra, wrqu->data.length); - return 0; -} - -static int wl3501_get_nick(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct wl3501_card *this = netdev_priv(dev); - - strscpy(extra, this->nick, 32); - wrqu->data.length = strlen(extra); - return 0; -} - -static int wl3501_get_rate(struct net_device *dev, struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * FIXME: have to see from where to get this info, perhaps this card - * works at 1 Mbit/s too... for now leave at 2 Mbit/s that is the most - * common with the Planet Access Points. -acme - */ - wrqu->bitrate.value = 2000000; - wrqu->bitrate.fixed = 1; - return 0; -} - -static int wl3501_get_rts_threshold(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - u16 threshold; /* size checked: it is u16 */ - struct wl3501_card *this = netdev_priv(dev); - int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_THRESHOLD, - &threshold, sizeof(threshold)); - if (!rc) { - wrqu->rts.value = threshold; - wrqu->rts.disabled = threshold >= 2347; - wrqu->rts.fixed = 1; - } - return rc; -} - -static int wl3501_get_frag_threshold(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - u16 threshold; /* size checked: it is u16 */ - struct wl3501_card *this = netdev_priv(dev); - int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAG_THRESHOLD, - &threshold, sizeof(threshold)); - if (!rc) { - wrqu->frag.value = threshold; - wrqu->frag.disabled = threshold >= 2346; - wrqu->frag.fixed = 1; - } - return rc; -} - -static int wl3501_get_txpow(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - u16 txpow; - struct wl3501_card *this = netdev_priv(dev); - int rc = wl3501_get_mib_value(this, - WL3501_MIB_ATTR_CURRENT_TX_PWR_LEVEL, - &txpow, sizeof(txpow)); - if (!rc) { - wrqu->txpower.value = txpow; - wrqu->txpower.disabled = 0; - /* - * From the MIB values I think this can be configurable, - * as it lists several tx power levels -acme - */ - wrqu->txpower.fixed = 0; - wrqu->txpower.flags = IW_TXPOW_MWATT; - } - return rc; -} - -static int wl3501_get_retry(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - u8 retry; /* size checked: it is u8 */ - struct wl3501_card *this = netdev_priv(dev); - int rc = wl3501_get_mib_value(this, - WL3501_MIB_ATTR_LONG_RETRY_LIMIT, - &retry, sizeof(retry)); - if (rc) - goto out; - if (wrqu->retry.flags & IW_RETRY_LONG) { - wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - goto set_value; - } - rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_SHORT_RETRY_LIMIT, - &retry, sizeof(retry)); - if (rc) - goto out; - wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; -set_value: - wrqu->retry.value = retry; - wrqu->retry.disabled = 0; -out: - return rc; -} - -static int wl3501_get_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - u8 implemented, restricted, keys[100], len_keys, tocopy; - struct wl3501_card *this = netdev_priv(dev); - int rc = wl3501_get_mib_value(this, - WL3501_MIB_ATTR_PRIV_OPT_IMPLEMENTED, - &implemented, sizeof(implemented)); - if (rc) - goto out; - if (!implemented) { - wrqu->encoding.flags = IW_ENCODE_DISABLED; - goto out; - } - rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_EXCLUDE_UNENCRYPTED, - &restricted, sizeof(restricted)); - if (rc) - goto out; - wrqu->encoding.flags = restricted ? IW_ENCODE_RESTRICTED : - IW_ENCODE_OPEN; - rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS_LEN, - &len_keys, sizeof(len_keys)); - if (rc) - goto out; - rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS, - keys, len_keys); - if (rc) - goto out; - tocopy = min_t(u16, len_keys, wrqu->encoding.length); - tocopy = min_t(u8, tocopy, 100); - wrqu->encoding.length = tocopy; - memcpy(extra, keys, tocopy); -out: - return rc; -} - -static int wl3501_get_power(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - u8 pwr_state; - struct wl3501_card *this = netdev_priv(dev); - int rc = wl3501_get_mib_value(this, - WL3501_MIB_ATTR_CURRENT_PWR_STATE, - &pwr_state, sizeof(pwr_state)); - if (rc) - goto out; - wrqu->power.disabled = !pwr_state; - wrqu->power.flags = IW_POWER_ON; -out: - return rc; -} - -static const iw_handler wl3501_handler[] = { - IW_HANDLER(SIOCGIWNAME, wl3501_get_name), - IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq), - IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq), - IW_HANDLER(SIOCSIWMODE, wl3501_set_mode), - IW_HANDLER(SIOCGIWMODE, wl3501_get_mode), - IW_HANDLER(SIOCGIWSENS, wl3501_get_sens), - IW_HANDLER(SIOCGIWRANGE, wl3501_get_range), - IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), - IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), - IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), - IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), - IW_HANDLER(SIOCSIWAP, wl3501_set_wap), - IW_HANDLER(SIOCGIWAP, wl3501_get_wap), - IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan), - IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan), - IW_HANDLER(SIOCSIWESSID, wl3501_set_essid), - IW_HANDLER(SIOCGIWESSID, wl3501_get_essid), - IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick), - IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick), - IW_HANDLER(SIOCGIWRATE, wl3501_get_rate), - IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold), - IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold), - IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow), - IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry), - IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode), - IW_HANDLER(SIOCGIWPOWER, wl3501_get_power), -}; - -static const struct iw_handler_def wl3501_handler_def = { - .num_standard = ARRAY_SIZE(wl3501_handler), - .standard = (iw_handler *)wl3501_handler, - .get_wireless_stats = wl3501_get_wireless_stats, -}; - -static const struct net_device_ops wl3501_netdev_ops = { - .ndo_open = wl3501_open, - .ndo_stop = wl3501_close, - .ndo_start_xmit = wl3501_hard_start_xmit, - .ndo_tx_timeout = wl3501_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int wl3501_probe(struct pcmcia_device *p_dev) -{ - struct net_device *dev; - struct wl3501_card *this; - - /* The io structure describes IO port mapping */ - p_dev->resource[0]->end = 16; - p_dev->resource[0]->flags = IO_DATA_PATH_WIDTH_8; - - /* General socket configuration */ - p_dev->config_flags = CONF_ENABLE_IRQ; - p_dev->config_index = 1; - - dev = alloc_etherdev(sizeof(struct wl3501_card)); - if (!dev) - goto out_link; - - - dev->netdev_ops = &wl3501_netdev_ops; - dev->watchdog_timeo = 5 * HZ; - - this = netdev_priv(dev); - this->wireless_data.spy_data = &this->spy_data; - this->p_dev = p_dev; - dev->wireless_data = &this->wireless_data; - dev->wireless_handlers = &wl3501_handler_def; - netif_stop_queue(dev); - p_dev->priv = dev; - - return wl3501_config(p_dev); -out_link: - return -ENOMEM; -} - -static int wl3501_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - int i = 0, j, ret; - struct wl3501_card *this; - - /* Try allocating IO ports. This tries a few fixed addresses. If you - * want, you can also read the card's config table to pick addresses -- - * see the serial driver for an example. */ - link->io_lines = 5; - - for (j = 0x280; j < 0x400; j += 0x20) { - /* The '^0x300' is so that we probe 0x300-0x3ff first, then - * 0x200-0x2ff, and so on, because this seems safer */ - link->resource[0]->start = j; - link->resource[1]->start = link->resource[0]->start + 0x10; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - if (i != 0) - goto failed; - - /* Now allocate an interrupt line. Note that this does not actually - * assign a handler to the interrupt. */ - - ret = pcmcia_request_irq(link, wl3501_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - SET_NETDEV_DEV(dev, &link->dev); - if (register_netdev(dev)) { - printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n"); - goto failed; - } - - this = netdev_priv(dev); - - this->base_addr = dev->base_addr; - - if (!wl3501_get_flash_mac_addr(this)) { - printk(KERN_WARNING "%s: Can't read MAC addr in flash ROM?\n", - dev->name); - unregister_netdev(dev); - goto failed; - } - - eth_hw_addr_set(dev, this->mac_addr); - - /* print probe information */ - printk(KERN_INFO "%s: wl3501 @ 0x%3.3x, IRQ %d, " - "MAC addr in flash ROM:%pM\n", - dev->name, this->base_addr, (int)dev->irq, - dev->dev_addr); - /* - * Initialize card parameters - added by jss - */ - this->net_type = IW_MODE_INFRA; - this->bss_cnt = 0; - this->join_sta_bss = 0; - this->adhoc_times = 0; - iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID, &this->essid.el, - "ANY", 3); - this->card_name[0] = '\0'; - this->firmware_date[0] = '\0'; - this->rssi = 255; - this->chan = iw_default_channel(this->reg_domain); - strscpy(this->nick, "Planet WL3501", sizeof(this->nick)); - spin_lock_init(&this->lock); - init_waitqueue_head(&this->wait); - netif_start_queue(dev); - return 0; - -failed: - wl3501_release(link); - return -ENODEV; -} - -static void wl3501_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int wl3501_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - wl3501_pwr_mgmt(netdev_priv(dev), WL3501_SUSPEND); - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int wl3501_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - wl3501_pwr_mgmt(netdev_priv(dev), WL3501_RESUME); - if (link->open) { - wl3501_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - - -static const struct pcmcia_device_id wl3501_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0001), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, wl3501_ids); - -static struct pcmcia_driver wl3501_driver = { - .owner = THIS_MODULE, - .name = "wl3501_cs", - .probe = wl3501_probe, - .remove = wl3501_detach, - .id_table = wl3501_ids, - .suspend = wl3501_suspend, - .resume = wl3501_resume, -}; -module_pcmcia_driver(wl3501_driver); - -MODULE_AUTHOR("Fox Chen , " - "Arnaldo Carvalho de Melo ," - "Gustavo Niemeyer "); -MODULE_DESCRIPTION("Planet wl3501 wireless driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 659fda7f35f0d11126edfb6c6cd9ace99a2525c0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 3 Mar 2023 23:23:31 +0100 Subject: wifi: brcmfmac: Use ISO3166 country code and rev 0 as fallback on 4356 Many devices ship with a nvram ccode value of X2/XT/XU/XV/ALL which are all special world-wide compatibility ccode-s. Most of these world-wide ccode-s allow passive scan mode only for 2.4GHz channels 12-14, only enabling them when an AP is seen on them. Since linux-firmware has moved to the new cyfmac4356-pci.bin + cyfmac4356-pci.clm_blob firmware files this no longer works and 4356 devices using e.g. an X2 ccode fail to connect to an AP on channel 13. Add the 4356 chip-id to the list of chips for which to use the ISO3166 country code + rev 0 as fallback in brcmf_translate_country_code() to fix this. Signed-off-by: Hans de Goede Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230303222331.285663-1-hdegoede@redhat.com --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index e0a70a671550..548799fefb4b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -7881,6 +7881,7 @@ static bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr) switch (drvr->bus_if->chip) { case BRCM_CC_43430_CHIP_ID: case BRCM_CC_4345_CHIP_ID: + case BRCM_CC_4356_CHIP_ID: case BRCM_CC_43602_CHIP_ID: return true; default: -- cgit v1.2.3 From df259fc12b366097fc6b7c3ed1a2f9f77b4ba355 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Sun, 5 Mar 2023 18:59:32 +0100 Subject: wifi: rtl8xxxu: mark Edimax EW-7811Un V2 as tested The Edimax V2 (vid 0x7392, pid 0xb811) works well with the rtl8xxxu driver since rtl8188eu support has been added. Remove the untested flag for this device. Signed-off-by: Martin Kaiser Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230305175932.719103-1-martin@kaiser.cx --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 54ca6f2ced3f..47dcca842d7c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -7012,7 +7012,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, } break; case 0x7392: - if (id->idProduct == 0x7811 || id->idProduct == 0xa611) + if (id->idProduct == 0x7811 || id->idProduct == 0xa611 || id->idProduct == 0xb811) untested = 0; break; case 0x050d: -- cgit v1.2.3 From 8a66293e73a520a42a7653d2ca32074ba323ff56 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 7 Mar 2023 22:18:48 +0800 Subject: wifi: rtw89: release RX standby timer of beamformee CSI to save power Originally, we keep RX standby timer to handle beamformee CSI, but this spends power and causes system not entering power save mode. To improve power consumption, release the timer if throughput becomes low. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230307141848.26403-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 35 +++++++++++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/reg.h | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b1a886898c5a..9073f526b5a3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3168,6 +3168,7 @@ enum rtw89_flags { RTW89_FLAG_RUNNING, RTW89_FLAG_BFEE_MON, RTW89_FLAG_BFEE_EN, + RTW89_FLAG_BFEE_TIMER_KEEP, RTW89_FLAG_NAPI_RUNNING, RTW89_FLAG_LEISURE_PS, RTW89_FLAG_LOW_POWER_MODE, diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3d1e4ffef1b1..7866fe925d24 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4931,6 +4931,24 @@ u16 rtw89_mac_get_plt_cnt(struct rtw89_dev *rtwdev, u8 band) return cnt; } +static void rtw89_mac_bfee_standby_timer(struct rtw89_dev *rtwdev, u8 mac_idx, + bool keep) +{ + u32 reg; + + rtw89_debug(rtwdev, RTW89_DBG_BF, "set bfee standby_timer to %d\n", keep); + reg = rtw89_mac_reg_by_idx(R_AX_BFMEE_RESP_OPTION, mac_idx); + if (keep) { + set_bit(RTW89_FLAG_BFEE_TIMER_KEEP, rtwdev->flags); + rtw89_write32_mask(rtwdev, reg, B_AX_BFMEE_BFRP_RX_STANDBY_TIMER_MASK, + BFRP_RX_STANDBY_TIMER_KEEP); + } else { + clear_bit(RTW89_FLAG_BFEE_TIMER_KEEP, rtwdev->flags); + rtw89_write32_mask(rtwdev, reg, B_AX_BFMEE_BFRP_RX_STANDBY_TIMER_MASK, + BFRP_RX_STANDBY_TIMER_RELEASE); + } +} + static void rtw89_mac_bfee_ctrl(struct rtw89_dev *rtwdev, u8 mac_idx, bool en) { u32 reg; @@ -4967,9 +4985,9 @@ static int rtw89_mac_init_bfee(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32(rtwdev, reg, CSI_RRSC_BMAP); reg = rtw89_mac_reg_by_idx(R_AX_BFMEE_RESP_OPTION, mac_idx); - val32 = FIELD_PREP(B_AX_BFMEE_BFRP_RX_STANDBY_TIMER_MASK, BFRP_RX_STANDBY_TIMER); - val32 |= FIELD_PREP(B_AX_BFMEE_NDP_RX_STANDBY_TIMER_MASK, NDP_RX_STANDBY_TIMER); + val32 = FIELD_PREP(B_AX_BFMEE_NDP_RX_STANDBY_TIMER_MASK, NDP_RX_STANDBY_TIMER); rtw89_write32(rtwdev, reg, val32); + rtw89_mac_bfee_standby_timer(rtwdev, mac_idx, true); rtw89_mac_bfee_ctrl(rtwdev, mac_idx, true); reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_CTRL_0, mac_idx); @@ -5181,6 +5199,19 @@ void _rtw89_mac_bf_monitor_track(struct rtw89_dev *rtwdev) struct rtw89_vif *rtwvif; bool en = stats->tx_tfc_lv <= stats->rx_tfc_lv; bool old = test_bit(RTW89_FLAG_BFEE_EN, rtwdev->flags); + bool keep_timer = true; + bool old_keep_timer; + + old_keep_timer = test_bit(RTW89_FLAG_BFEE_TIMER_KEEP, rtwdev->flags); + + if (stats->tx_tfc_lv <= RTW89_TFC_LOW && stats->rx_tfc_lv <= RTW89_TFC_LOW) + keep_timer = false; + + if (keep_timer != old_keep_timer) { + rtw89_for_each_rtwvif(rtwdev, rtwvif) + rtw89_mac_bfee_standby_timer(rtwdev, rtwvif->mac_idx, + keep_timer); + } if (en == old) return; diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 600257909df2..e5c0ab43ab7a 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3056,6 +3056,8 @@ #define R_AX_BFMEE_RESP_OPTION_C1 0xED80 #define B_AX_BFMEE_NDP_RX_STANDBY_TIMER_MASK GENMASK(31, 24) #define B_AX_BFMEE_BFRP_RX_STANDBY_TIMER_MASK GENMASK(23, 20) +#define BFRP_RX_STANDBY_TIMER_KEEP 0x0 +#define BFRP_RX_STANDBY_TIMER_RELEASE 0x1 #define B_AX_MU_BFRPTSEG_SEL_MASK GENMASK(18, 17) #define B_AX_BFMEE_NDP_RXSTDBY_SEL BIT(16) #define BFRP_RX_STANDBY_TIMER 0x0 -- cgit v1.2.3 From 0606b344021a37140ef467ddf30d4b8182e7bbe1 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 7 Mar 2023 20:57:17 +0100 Subject: wifi: rtl8xxxu: use module_usb_driver We can use the module_usb_driver macro instead of open-coding the driver's init and exit functions. This is simpler and saves some lines of code. Other realtek wireless drivers use module_usb_driver as well. Signed-off-by: Martin Kaiser Reviewed-by: Ping-Ke Shih Tested-by: Philipp Hortmann # Edimax N150 Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230307195718.168021-1-martin@kaiser.cx --- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 47dcca842d7c..280dc3e26ca7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -7451,24 +7451,6 @@ static struct usb_driver rtl8xxxu_driver = { .disable_hub_initiated_lpm = 1, }; -static int __init rtl8xxxu_module_init(void) -{ - int res; - - res = usb_register(&rtl8xxxu_driver); - if (res < 0) - pr_err(DRIVER_NAME ": usb_register() failed (%i)\n", res); - - return res; -} - -static void __exit rtl8xxxu_module_exit(void) -{ - usb_deregister(&rtl8xxxu_driver); -} - - MODULE_DEVICE_TABLE(usb, dev_table); -module_init(rtl8xxxu_module_init); -module_exit(rtl8xxxu_module_exit); +module_usb_driver(rtl8xxxu_driver); -- cgit v1.2.3 From a23c82e006db2f073fb65da8762f63735a510409 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 7 Mar 2023 15:01:48 -0800 Subject: wifi: ipw2x00: convert ipw_fw_error->elem to flexible array[] The ipw_fw_error structure contains a payload[] flexible array as well as two pointers to this array area, ->elem, and ->log. The total size of the allocated structure is computed without use of the macros. There's no reason to keep both a payload[] and an extra pointer to both the elem and log members. Convert the elem pointer member into the flexible array member, removing payload. Fix the allocation of the ipw_fw_error structure to use size_add(), struct_size(), and array_size() to compute the allocation. This ensures that any overflow saturates at SIZE_MAX rather than overflowing and potentially allowing an undersized allocation. Before the structure change, the layout of ipw_fw_error was: struct ipw_fw_error { long unsigned int jiffies; /* 0 8 */ u32 status; /* 8 4 */ u32 config; /* 12 4 */ u32 elem_len; /* 16 4 */ u32 log_len; /* 20 4 */ struct ipw_error_elem * elem; /* 24 8 */ struct ipw_event * log; /* 32 8 */ u8 payload[]; /* 40 0 */ /* size: 40, cachelines: 1, members: 8 */ /* last cacheline: 40 bytes */ }; After this change, the layout is now: struct ipw_fw_error { long unsigned int jiffies; /* 0 8 */ u32 status; /* 8 4 */ u32 config; /* 12 4 */ u32 elem_len; /* 16 4 */ u32 log_len; /* 20 4 */ struct ipw_event * log; /* 24 8 */ struct ipw_error_elem elem[]; /* 32 0 */ /* size: 32, cachelines: 1, members: 7 */ /* last cacheline: 32 bytes */ }; This saves a total of 8 bytes for every ipw_fw_error allocation, and removes the risk of a potential overflow on the allocation. Signed-off-by: Jacob Keller Cc: Stanislav Yakovlev Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230307230148.3735684-1-jacob.e.keller@intel.com --- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 7 +++---- drivers/net/wireless/intel/ipw2x00/ipw2200.h | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index d382f2017325..b91b1a2d0be7 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -1234,9 +1234,9 @@ static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv) u32 base = ipw_read32(priv, IPW_ERROR_LOG); u32 elem_len = ipw_read_reg32(priv, base); - error = kmalloc(sizeof(*error) + - sizeof(*error->elem) * elem_len + - sizeof(*error->log) * log_len, GFP_ATOMIC); + error = kmalloc(size_add(struct_size(error, elem, elem_len), + array_size(sizeof(*error->log), log_len)), + GFP_ATOMIC); if (!error) { IPW_ERROR("Memory allocation for firmware error log " "failed.\n"); @@ -1247,7 +1247,6 @@ static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv) error->config = priv->config; error->elem_len = elem_len; error->log_len = log_len; - error->elem = (struct ipw_error_elem *)error->payload; error->log = (struct ipw_event *)(error->elem + elem_len); ipw_capture_event_log(priv, log_len, error->log); diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.h b/drivers/net/wireless/intel/ipw2x00/ipw2200.h index 09ddd21608d4..8ebf09121e17 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.h @@ -1106,9 +1106,8 @@ struct ipw_fw_error { /* XXX */ u32 config; u32 elem_len; u32 log_len; - struct ipw_error_elem *elem; struct ipw_event *log; - u8 payload[]; + struct ipw_error_elem elem[]; } __packed; #ifdef CONFIG_IPW2200_PROMISCUOUS -- cgit v1.2.3 From 84e9e2102bdc4f60ce131dcb74046219dc4425ae Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 7 Mar 2023 15:02:12 -0800 Subject: wifi: qtnfmac: use struct_size and size_sub for payload length Replace the calculations for the payload length in qtnf_cmd_band_fill_iftype with struct_size() and size_sub(). While the payload length does not get directly passed to an allocation function, the performed calculation is still calculating the size of a flexible array structure (minus the size of a header structure). Signed-off-by: Jacob Keller Cc: Igor Mitsyanko Cc: Sergey Matyukevich Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230307230212.3735818-1-jacob.e.keller@intel.com --- drivers/net/wireless/quantenna/qtnfmac/commands.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index b1b73478d89b..68ae9c7ea95a 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -1325,9 +1325,10 @@ static int qtnf_cmd_band_fill_iftype(const u8 *data, struct ieee80211_sband_iftype_data *iftype_data; const struct qlink_tlv_iftype_data *tlv = (const struct qlink_tlv_iftype_data *)data; - size_t payload_len = tlv->n_iftype_data * sizeof(*tlv->iftype_data) + - sizeof(*tlv) - - sizeof(struct qlink_tlv_hdr); + size_t payload_len; + + payload_len = struct_size(tlv, iftype_data, tlv->n_iftype_data); + payload_len = size_sub(payload_len, sizeof(struct qlink_tlv_hdr)); if (tlv->hdr.len != cpu_to_le16(payload_len)) { pr_err("bad IFTYPE_DATA TLV len %u\n", tlv->hdr.len); -- cgit v1.2.3 From e49bdd85c92dacb12151aa1b9cf48b81c81a6f98 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 8 Mar 2023 13:32:19 +0800 Subject: wifi: rtw89: coex: Add more error_map and counter to log The error map and counter can help to analyze is coexistence mechanism going well or not. For example, if there is E2G (External control Wi-Fi slot for Wi-Fi 2.4 GHz) hang counter, it means Wi-Fi firmware didn't cut a slot for Wi-Fi 2.4 GHz. Maybe something wrong with firmware timer. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230308053225.24377-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 112 ++++++++++++++++++------------ drivers/net/wireless/realtek/rtw89/core.h | 31 ++++++--- 2 files changed, 88 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index bcf483cafd20..f80952e6e11b 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -734,6 +734,7 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type) #define BTC_RPT_HDR_SIZE 3 #define BTC_CHK_WLSLOT_DRIFT_MAX 15 +#define BTC_CHK_BTSLOT_DRIFT_MAX 15 #define BTC_CHK_HANG_MAX 3 static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) @@ -748,62 +749,76 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) __func__, type, cnt); switch (type) { - case BTC_DCNT_RPT_FREEZE: + case BTC_DCNT_RPT_HANG: if (dm->cnt_dm[BTC_DCNT_RPT] == cnt && btc->fwinfo.rpt_en_map) - dm->cnt_dm[BTC_DCNT_RPT_FREEZE]++; + dm->cnt_dm[BTC_DCNT_RPT_HANG]++; else - dm->cnt_dm[BTC_DCNT_RPT_FREEZE] = 0; + dm->cnt_dm[BTC_DCNT_RPT_HANG] = 0; - if (dm->cnt_dm[BTC_DCNT_RPT_FREEZE] >= BTC_CHK_HANG_MAX) + if (dm->cnt_dm[BTC_DCNT_RPT_HANG] >= BTC_CHK_HANG_MAX) dm->error.map.wl_fw_hang = true; else dm->error.map.wl_fw_hang = false; dm->cnt_dm[BTC_DCNT_RPT] = cnt; break; - case BTC_DCNT_CYCLE_FREEZE: + case BTC_DCNT_CYCLE_HANG: if (dm->cnt_dm[BTC_DCNT_CYCLE] == cnt && (dm->tdma_now.type != CXTDMA_OFF || dm->tdma_now.ext_ctrl == CXECTL_EXT)) - dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE]++; + dm->cnt_dm[BTC_DCNT_CYCLE_HANG]++; else - dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE] = 0; + dm->cnt_dm[BTC_DCNT_CYCLE_HANG] = 0; - if (dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE] >= BTC_CHK_HANG_MAX) + if (dm->cnt_dm[BTC_DCNT_CYCLE_HANG] >= BTC_CHK_HANG_MAX) dm->error.map.cycle_hang = true; else dm->error.map.cycle_hang = false; dm->cnt_dm[BTC_DCNT_CYCLE] = cnt; break; - case BTC_DCNT_W1_FREEZE: + case BTC_DCNT_W1_HANG: if (dm->cnt_dm[BTC_DCNT_W1] == cnt && dm->tdma_now.type != CXTDMA_OFF) - dm->cnt_dm[BTC_DCNT_W1_FREEZE]++; + dm->cnt_dm[BTC_DCNT_W1_HANG]++; else - dm->cnt_dm[BTC_DCNT_W1_FREEZE] = 0; + dm->cnt_dm[BTC_DCNT_W1_HANG] = 0; - if (dm->cnt_dm[BTC_DCNT_W1_FREEZE] >= BTC_CHK_HANG_MAX) + if (dm->cnt_dm[BTC_DCNT_W1_HANG] >= BTC_CHK_HANG_MAX) dm->error.map.w1_hang = true; else dm->error.map.w1_hang = false; dm->cnt_dm[BTC_DCNT_W1] = cnt; break; - case BTC_DCNT_B1_FREEZE: + case BTC_DCNT_B1_HANG: if (dm->cnt_dm[BTC_DCNT_B1] == cnt && dm->tdma_now.type != CXTDMA_OFF) - dm->cnt_dm[BTC_DCNT_B1_FREEZE]++; + dm->cnt_dm[BTC_DCNT_B1_HANG]++; else - dm->cnt_dm[BTC_DCNT_B1_FREEZE] = 0; + dm->cnt_dm[BTC_DCNT_B1_HANG] = 0; - if (dm->cnt_dm[BTC_DCNT_B1_FREEZE] >= BTC_CHK_HANG_MAX) + if (dm->cnt_dm[BTC_DCNT_B1_HANG] >= BTC_CHK_HANG_MAX) dm->error.map.b1_hang = true; else dm->error.map.b1_hang = false; dm->cnt_dm[BTC_DCNT_B1] = cnt; break; + case BTC_DCNT_E2G_HANG: + if (dm->cnt_dm[BTC_DCNT_E2G] == cnt && + dm->tdma_now.ext_ctrl == CXECTL_EXT) + dm->cnt_dm[BTC_DCNT_E2G_HANG]++; + else + dm->cnt_dm[BTC_DCNT_E2G_HANG] = 0; + + if (dm->cnt_dm[BTC_DCNT_E2G_HANG] >= BTC_CHK_HANG_MAX) + dm->error.map.wl_e2g_hang = true; + else + dm->error.map.wl_e2g_hang = false; + + dm->cnt_dm[BTC_DCNT_E2G] = cnt; + break; case BTC_DCNT_TDMA_NONSYNC: if (cnt != 0) /* if tdma not sync between drv/fw */ dm->cnt_dm[BTC_DCNT_TDMA_NONSYNC]++; @@ -822,23 +837,23 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC] = 0; if (dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC] >= BTC_CHK_HANG_MAX) - dm->error.map.tdma_no_sync = true; + dm->error.map.slot_no_sync = true; else - dm->error.map.tdma_no_sync = false; + dm->error.map.slot_no_sync = false; break; - case BTC_DCNT_BTCNT_FREEZE: + case BTC_DCNT_BTCNT_HANG: cnt = cx->cnt_bt[BTC_BCNT_HIPRI_RX] + cx->cnt_bt[BTC_BCNT_HIPRI_TX] + cx->cnt_bt[BTC_BCNT_LOPRI_RX] + cx->cnt_bt[BTC_BCNT_LOPRI_TX]; if (cnt == 0) - dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE]++; + dm->cnt_dm[BTC_DCNT_BTCNT_HANG]++; else - dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE] = 0; + dm->cnt_dm[BTC_DCNT_BTCNT_HANG] = 0; - if ((dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE] >= BTC_CHK_HANG_MAX && - bt->enable.now) || (!dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE] && + if ((dm->cnt_dm[BTC_DCNT_BTCNT_HANG] >= BTC_CHK_HANG_MAX && + bt->enable.now) || (!dm->cnt_dm[BTC_DCNT_BTCNT_HANG] && !bt->enable.now)) _update_bt_scbd(rtwdev, false); break; @@ -853,6 +868,18 @@ static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt) else dm->error.map.wl_slot_drift = false; break; + case BTC_DCNT_BT_SLOT_DRIFT: + if (cnt >= BTC_CHK_BTSLOT_DRIFT_MAX) + dm->cnt_dm[BTC_DCNT_BT_SLOT_DRIFT]++; + else + dm->cnt_dm[BTC_DCNT_BT_SLOT_DRIFT] = 0; + + if (dm->cnt_dm[BTC_DCNT_BT_SLOT_DRIFT] >= BTC_CHK_HANG_MAX) + dm->error.map.bt_slot_drift = true; + else + dm->error.map.bt_slot_drift = false; + + break; } } @@ -1129,14 +1156,14 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = prpt->v1.wl_fw_ver; dm->wl_fw_cx_offload = !!prpt->v1.wl_fw_cx_offload; - _chk_btc_err(rtwdev, BTC_DCNT_RPT_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_RPT_HANG, pfwinfo->event[BTF_EVNT_RPT]); /* To avoid I/O if WL LPS or power-off */ if (wl->status.map.lps != BTC_LPS_RF_OFF && !wl->status.map.rf_off) { rtwdev->chip->ops->btc_update_bt_cnt(rtwdev); - _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_FREEZE, 0); + _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); btc->cx.cnt_bt[BTC_BCNT_POLUT] = rtw89_mac_get_plt_cnt(rtwdev, @@ -1164,8 +1191,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, btc->cx.cnt_bt[BTC_BCNT_POLUT] = le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_POLLUTED]); - _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_FREEZE, 0); - _chk_btc_err(rtwdev, BTC_DCNT_RPT_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); + _chk_btc_err(rtwdev, BTC_DCNT_RPT_HANG, pfwinfo->event[BTF_EVNT_RPT]); if (le32_to_cpu(prpt->v4.bt_cnt[BTC_BCNT_RFK_TIMEOUT]) > 0) @@ -1196,8 +1223,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, btc->cx.cnt_bt[BTC_BCNT_POLUT] = le16_to_cpu(prpt->v5.bt_cnt[BTC_BCNT_POLLUTED]); - _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_FREEZE, 0); - _chk_btc_err(rtwdev, BTC_DCNT_RPT_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_HANG, 0); + _chk_btc_err(rtwdev, BTC_DCNT_RPT_HANG, pfwinfo->event[BTF_EVNT_RPT]); dm->error.map.bt_rfk_timeout = bt->rfk_info.map.timeout; @@ -1258,11 +1285,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, BTC_DCNT_WL_SLOT_DRIFT, diff_t); } - _chk_btc_err(rtwdev, BTC_DCNT_W1_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_W1_HANG, le32_to_cpu(pcysta->v2.slot_cnt[CXST_W1])); - _chk_btc_err(rtwdev, BTC_DCNT_W1_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_W1_HANG, le32_to_cpu(pcysta->v2.slot_cnt[CXST_B1])); - _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_HANG, le16_to_cpu(pcysta->v2.cycles)); } else if (ver->fcxcysta == 3) { if (le16_to_cpu(pcysta->v3.cycles) < BTC_CYSTA_CHK_PERIOD) @@ -1299,11 +1326,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, } } - _chk_btc_err(rtwdev, BTC_DCNT_W1_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_W1_HANG, le32_to_cpu(pcysta->v3.slot_cnt[CXST_W1])); - _chk_btc_err(rtwdev, BTC_DCNT_B1_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_B1_HANG, le32_to_cpu(pcysta->v3.slot_cnt[CXST_B1])); - _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_HANG, le16_to_cpu(pcysta->v3.cycles)); } else if (ver->fcxcysta == 4) { if (le16_to_cpu(pcysta->v4.cycles) < BTC_CYSTA_CHK_PERIOD) @@ -1341,11 +1368,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, } } - _chk_btc_err(rtwdev, BTC_DCNT_W1_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_W1_HANG, le16_to_cpu(pcysta->v4.slot_cnt[CXST_W1])); - _chk_btc_err(rtwdev, BTC_DCNT_B1_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_B1_HANG, le16_to_cpu(pcysta->v4.slot_cnt[CXST_B1])); - _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_FREEZE, + _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_HANG, le16_to_cpu(pcysta->v4.cycles)); } else { goto err; @@ -4578,7 +4605,7 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) } if (!(val & BTC_BSCB_ON) || - btc->dm.cnt_dm[BTC_DCNT_BTCNT_FREEZE] >= BTC_CHK_HANG_MAX) + btc->dm.cnt_dm[BTC_DCNT_BTCNT_HANG] >= BTC_CHK_HANG_MAX) bt->enable.now = 0; else bt->enable.now = 1; @@ -5349,7 +5376,7 @@ void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_sta _write_scbd(rtwdev, BTC_WSCB_ALL, false); } - btc->dm.cnt_dm[BTC_DCNT_BTCNT_FREEZE] = 0; + btc->dm.cnt_dm[BTC_DCNT_BTCNT_HANG] = 0; if (wl->status.map.lps_pre == BTC_LPS_OFF && wl->status.map.lps_pre != wl->status.map.lps) btc->dm.tdma_instant_excute = 1; @@ -5700,11 +5727,6 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) seq_printf(m, " %-15s : Coex:%d.%d.%d(branch:%d), ", "[coex_version]", ver_main, ver_sub, ver_hotfix, id_branch); - if (dm->wl_fw_cx_offload != BTC_CX_FW_OFFLOAD) - dm->error.map.offload_mismatch = true; - else - dm->error.map.offload_mismatch = false; - ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex); ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw_coex); ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw_coex); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 9073f526b5a3..f998f8a3f277 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -883,20 +883,24 @@ enum rtw89_btc_dcnt { BTC_DCNT_RUN = 0x0, BTC_DCNT_CX_RUNINFO, BTC_DCNT_RPT, - BTC_DCNT_RPT_FREEZE, + BTC_DCNT_RPT_HANG, BTC_DCNT_CYCLE, - BTC_DCNT_CYCLE_FREEZE, + BTC_DCNT_CYCLE_HANG, BTC_DCNT_W1, - BTC_DCNT_W1_FREEZE, + BTC_DCNT_W1_HANG, BTC_DCNT_B1, - BTC_DCNT_B1_FREEZE, + BTC_DCNT_B1_HANG, BTC_DCNT_TDMA_NONSYNC, BTC_DCNT_SLOT_NONSYNC, - BTC_DCNT_BTCNT_FREEZE, + BTC_DCNT_BTCNT_HANG, BTC_DCNT_WL_SLOT_DRIFT, - BTC_DCNT_BT_SLOT_DRIFT, BTC_DCNT_WL_STA_LAST, - BTC_DCNT_NUM, + BTC_DCNT_BT_SLOT_DRIFT, + BTC_DCNT_BT_SLOT_FLOOD, + BTC_DCNT_FDDT_TRIG, + BTC_DCNT_E2G, + BTC_DCNT_E2G_HANG, + BTC_DCNT_NUM }; enum rtw89_btc_wl_state_cnt { @@ -1302,15 +1306,22 @@ struct rtw89_btc_dm_emap { u32 pta_owner: 1; u32 wl_rfk_timeout: 1; u32 bt_rfk_timeout: 1; - u32 wl_fw_hang: 1; - u32 offload_mismatch: 1; u32 cycle_hang: 1; u32 w1_hang: 1; - u32 b1_hang: 1; u32 tdma_no_sync: 1; + u32 slot_no_sync: 1; u32 wl_slot_drift: 1; + u32 bt_slot_drift: 1; + u32 role_num_mismatch: 1; + u32 null1_tx_late: 1; + u32 bt_afh_conflict: 1; + u32 bt_leafh_conflict: 1; + u32 bt_slot_flood: 1; + u32 wl_e2g_hang: 1; + u32 wl_ver_mismatch: 1; + u32 bt_ver_mismatch: 1; }; union rtw89_btc_dm_error_map { -- cgit v1.2.3 From 5049964c4af86865153c553fc6a138df02685ffb Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 8 Mar 2023 13:32:20 +0800 Subject: wifi: rtw89: coex: Add WiFi role info v2 Remove WiFi traffic busy level & traffic rate from active role information. This information will move to v5 version TDMA cycle info. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230308053225.24377-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 277 +++++++++++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 32 ++++ drivers/net/wireless/realtek/rtw89/fw.c | 86 ++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 51 ++++++ 4 files changed, 439 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index f80952e6e11b..b56897529504 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1819,6 +1819,8 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) rtw89_fw_h2c_cxdrv_role(rtwdev); else if (ver->fwlrole == 1) rtw89_fw_h2c_cxdrv_role_v1(rtwdev); + else if (ver->fwlrole == 2) + rtw89_fw_h2c_cxdrv_role_v2(rtwdev); break; case CXDRVINFO_CTRL: rtw89_fw_h2c_cxdrv_ctrl(rtwdev); @@ -2113,8 +2115,10 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) struct rtw89_btc_bt_link_info *b = &bt->link_info; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_wl_active_role *r; struct rtw89_btc_wl_active_role_v1 *r1; + struct rtw89_btc_wl_active_role_v2 *r2; u8 en = 0, i, ch = 0, bw = 0; u8 mode, connect_cnt; @@ -2124,9 +2128,14 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) if (ver->fwlrole == 0) { mode = wl_rinfo->link_mode; connect_cnt = wl_rinfo->connect_cnt; - } else { + } else if (ver->fwlrole == 1) { mode = wl_rinfo_v1->link_mode; connect_cnt = wl_rinfo_v1->connect_cnt; + } else if (ver->fwlrole == 2) { + mode = wl_rinfo_v2->link_mode; + connect_cnt = wl_rinfo_v2->connect_cnt; + } else { + return; } if (wl->status.map.rf_off || bt->whql_test || @@ -2139,6 +2148,7 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) for (i = 0; i < RTW89_PORT_NUM; i++) { r = &wl_rinfo->active_role[i]; r1 = &wl_rinfo_v1->active_role_v1[i]; + r2 = &wl_rinfo_v2->active_role_v2[i]; if (ver->fwlrole == 0 && (r->role == RTW89_WIFI_ROLE_P2P_GO || @@ -2152,6 +2162,12 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) ch = r1->ch; bw = r1->bw; break; + } else if (ver->fwlrole == 2 && + (r2->role == RTW89_WIFI_ROLE_P2P_GO || + r2->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { + ch = r2->ch; + bw = r2->bw; + break; } } } else { @@ -2160,6 +2176,7 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) for (i = 0; i < RTW89_PORT_NUM; i++) { r = &wl_rinfo->active_role[i]; r1 = &wl_rinfo_v1->active_role_v1[i]; + r2 = &wl_rinfo_v2->active_role_v2[i]; if (ver->fwlrole == 0 && r->connected && r->band == RTW89_BAND_2G) { @@ -2171,6 +2188,11 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev) ch = r1->ch; bw = r1->bw; break; + } else if (ver->fwlrole == 2 && + r2->connected && r2->band == RTW89_BAND_2G) { + ch = r2->ch; + bw = r2->bw; + break; } } } @@ -3625,6 +3647,7 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; bool is_btg; u8 mode; @@ -3634,8 +3657,12 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) if (ver->fwlrole == 0) mode = wl_rinfo->link_mode; - else + else if (ver->fwlrole == 1) mode = wl_rinfo_v1->link_mode; + else if (ver->fwlrole == 2) + mode = wl_rinfo_v2->link_mode; + else + return; /* notify halbb ignore GNT_BT or not for WL BB Rx-AGC control */ if (mode == BTC_WLINK_5G) /* always 0 if 5G */ @@ -3736,6 +3763,7 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; struct rtw89_txtime_data data = {.rtwdev = rtwdev}; u8 mode; u8 tx_retry; @@ -3748,8 +3776,12 @@ static void _set_wl_tx_limit(struct rtw89_dev *rtwdev) if (ver->fwlrole == 0) mode = wl_rinfo->link_mode; - else + else if (ver->fwlrole == 1) mode = wl_rinfo_v1->link_mode; + else if (ver->fwlrole == 2) + mode = wl_rinfo_v2->link_mode; + else + return; if (btc->dm.freerun || btc->ctrl.igno_bt || b->profile_cnt.now == 0 || mode == BTC_WLINK_5G || mode == BTC_WLINK_NOLINK) { @@ -3799,14 +3831,19 @@ static void _set_bt_rx_agc(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_bt_info *bt = &btc->cx.bt; bool bt_hi_lna_rx = false; u8 mode; if (ver->fwlrole == 0) mode = wl_rinfo->link_mode; - else + else if (ver->fwlrole == 1) mode = wl_rinfo_v1->link_mode; + else if (ver->fwlrole == 2) + mode = wl_rinfo_v2->link_mode; + else + return; if (mode != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx) bt_hi_lna_rx = true; @@ -4079,6 +4116,68 @@ static void _action_wl_2g_scc_v1(struct rtw89_dev *rtwdev) _set_policy(rtwdev, policy_type, BTC_ACT_WL_2G_SCC); } +static void _action_wl_2g_scc_v2(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo = &wl->role_info_v2; + u16 policy_type = BTC_CXP_OFF_BT; + u32 dur; + + if (btc->mdinfo.ant.type == BTC_ANT_DEDICATED) { + policy_type = BTC_CXP_OFF_EQ0; + } else { + /* shared-antenna */ + switch (wl_rinfo->mrole_type) { + case BTC_WLMROLE_STA_GC: + dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; + dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_P2P_CLIENT; + dm->wl_scc.ebt_null = 0; /* no ext-slot-control */ + _action_by_bt(rtwdev); + return; + case BTC_WLMROLE_STA_STA: + dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; + dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_STATION; + dm->wl_scc.ebt_null = 0; /* no ext-slot-control */ + _action_by_bt(rtwdev); + return; + case BTC_WLMROLE_STA_GC_NOA: + case BTC_WLMROLE_STA_GO: + case BTC_WLMROLE_STA_GO_NOA: + dm->wl_scc.null_role1 = RTW89_WIFI_ROLE_STATION; + dm->wl_scc.null_role2 = RTW89_WIFI_ROLE_NONE; + dur = wl_rinfo->mrole_noa_duration; + + if (wl->status.map._4way) { + dm->wl_scc.ebt_null = 0; + policy_type = BTC_CXP_OFFE_WL; + } else if (bt->link_info.status.map.connect == 0) { + dm->wl_scc.ebt_null = 0; + policy_type = BTC_CXP_OFFE_2GISOB; + } else if (bt->link_info.a2dp_desc.exist && + dur < btc->bt_req_len) { + dm->wl_scc.ebt_null = 1; /* tx null at EBT */ + policy_type = BTC_CXP_OFFE_2GBWMIXB2; + } else if (bt->link_info.a2dp_desc.exist || + bt->link_info.pan_desc.exist) { + dm->wl_scc.ebt_null = 1; /* tx null at EBT */ + policy_type = BTC_CXP_OFFE_2GBWISOB; + } else { + dm->wl_scc.ebt_null = 0; + policy_type = BTC_CXP_OFFE_2GBWISOB; + } + break; + default: + break; + } + } + + _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); + _set_policy(rtwdev, policy_type, BTC_ACT_WL_2G_SCC); +} + static void _action_wl_2g_ap(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; @@ -4518,6 +4617,156 @@ static void _update_wl_info_v1(struct rtw89_dev *rtwdev) _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); } +static void _update_wl_info_v2(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo = &wl->role_info_v2; + struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; + u8 cnt_connect = 0, cnt_connecting = 0, cnt_active = 0; + u8 cnt_2g = 0, cnt_5g = 0, phy; + u32 wl_2g_ch[2] = {}, wl_5g_ch[2] = {}; + bool b2g = false, b5g = false, client_joined = false; + u8 i; + + memset(wl_rinfo, 0, sizeof(*wl_rinfo)); + + for (i = 0; i < RTW89_PORT_NUM; i++) { + if (!wl_linfo[i].active) + continue; + + cnt_active++; + wl_rinfo->active_role_v2[cnt_active - 1].role = wl_linfo[i].role; + wl_rinfo->active_role_v2[cnt_active - 1].pid = wl_linfo[i].pid; + wl_rinfo->active_role_v2[cnt_active - 1].phy = wl_linfo[i].phy; + wl_rinfo->active_role_v2[cnt_active - 1].band = wl_linfo[i].band; + wl_rinfo->active_role_v2[cnt_active - 1].noa = (u8)wl_linfo[i].noa; + wl_rinfo->active_role_v2[cnt_active - 1].connected = 0; + + wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid; + + phy = wl_linfo[i].phy; + + if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { + wl_dinfo->role[phy] = wl_linfo[i].role; + wl_dinfo->op_band[phy] = wl_linfo[i].band; + _update_dbcc_band(rtwdev, phy); + _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC); + } + + if (wl_linfo[i].connected == MLME_NO_LINK) { + continue; + } else if (wl_linfo[i].connected == MLME_LINKING) { + cnt_connecting++; + } else { + cnt_connect++; + if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO || + wl_linfo[i].role == RTW89_WIFI_ROLE_AP) && + wl_linfo[i].client_cnt > 1) + client_joined = true; + } + + wl_rinfo->role_map.val |= BIT(wl_linfo[i].role); + wl_rinfo->active_role_v2[cnt_active - 1].ch = wl_linfo[i].ch; + wl_rinfo->active_role_v2[cnt_active - 1].bw = wl_linfo[i].bw; + wl_rinfo->active_role_v2[cnt_active - 1].connected = 1; + + /* only care 2 roles + BT coex */ + if (wl_linfo[i].band != RTW89_BAND_2G) { + if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1) + wl_5g_ch[cnt_5g] = wl_linfo[i].ch; + cnt_5g++; + b5g = true; + } else { + if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1) + wl_2g_ch[cnt_2g] = wl_linfo[i].ch; + cnt_2g++; + b2g = true; + } + } + + wl_rinfo->connect_cnt = cnt_connect; + + /* Be careful to change the following sequence!! */ + if (cnt_connect == 0) { + wl_rinfo->link_mode = BTC_WLINK_NOLINK; + wl_rinfo->role_map.role.none = 1; + } else if (!b2g && b5g) { + wl_rinfo->link_mode = BTC_WLINK_5G; + } else if (wl_rinfo->role_map.role.nan) { + wl_rinfo->link_mode = BTC_WLINK_2G_NAN; + } else if (cnt_connect > BTC_TDMA_WLROLE_MAX) { + wl_rinfo->link_mode = BTC_WLINK_OTHER; + } else if (b2g && b5g && cnt_connect == 2) { + if (rtwdev->dbcc_en) { + switch (wl_dinfo->role[RTW89_PHY_0]) { + case RTW89_WIFI_ROLE_STATION: + wl_rinfo->link_mode = BTC_WLINK_2G_STA; + break; + case RTW89_WIFI_ROLE_P2P_GO: + wl_rinfo->link_mode = BTC_WLINK_2G_GO; + break; + case RTW89_WIFI_ROLE_P2P_CLIENT: + wl_rinfo->link_mode = BTC_WLINK_2G_GC; + break; + case RTW89_WIFI_ROLE_AP: + wl_rinfo->link_mode = BTC_WLINK_2G_AP; + break; + default: + wl_rinfo->link_mode = BTC_WLINK_OTHER; + break; + } + } else { + wl_rinfo->link_mode = BTC_WLINK_25G_MCC; + } + } else if (!b5g && cnt_connect == 2) { + if (wl_rinfo->role_map.role.station && + (wl_rinfo->role_map.role.p2p_go || + wl_rinfo->role_map.role.p2p_gc || + wl_rinfo->role_map.role.ap)) { + if (wl_2g_ch[0] == wl_2g_ch[1]) + wl_rinfo->link_mode = BTC_WLINK_2G_SCC; + else + wl_rinfo->link_mode = BTC_WLINK_2G_MCC; + } else { + wl_rinfo->link_mode = BTC_WLINK_2G_MCC; + } + } else if (!b5g && cnt_connect == 1) { + if (wl_rinfo->role_map.role.station) + wl_rinfo->link_mode = BTC_WLINK_2G_STA; + else if (wl_rinfo->role_map.role.ap) + wl_rinfo->link_mode = BTC_WLINK_2G_AP; + else if (wl_rinfo->role_map.role.p2p_go) + wl_rinfo->link_mode = BTC_WLINK_2G_GO; + else if (wl_rinfo->role_map.role.p2p_gc) + wl_rinfo->link_mode = BTC_WLINK_2G_GC; + else + wl_rinfo->link_mode = BTC_WLINK_OTHER; + } + + /* if no client_joined, don't care P2P-GO/AP role */ + if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) { + if (!client_joined) { + if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC || + wl_rinfo->link_mode == BTC_WLINK_2G_MCC) { + wl_rinfo->link_mode = BTC_WLINK_2G_STA; + wl_rinfo->connect_cnt = 1; + } else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO || + wl_rinfo->link_mode == BTC_WLINK_2G_AP) { + wl_rinfo->link_mode = BTC_WLINK_NOLINK; + wl_rinfo->connect_cnt = 0; + } + } + } + + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], cnt_connect = %d, connecting = %d, link_mode = %d\n", + cnt_connect, cnt_connecting, wl_rinfo->link_mode); + + _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); +} + #define BTC_CHK_HANG_MAX 3 #define BTC_SCB_INV_VALUE GENMASK(31, 0) @@ -4676,6 +4925,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) struct rtw89_btc_bt_info *bt = &btc->cx.bt; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; u8 mode; lockdep_assert_held(&rtwdev->mutex); @@ -4686,8 +4936,12 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) if (ver->fwlrole == 0) mode = wl_rinfo->link_mode; - else + else if (ver->fwlrole == 1) mode = wl_rinfo_v1->link_mode; + else if (ver->fwlrole == 2) + mode = wl_rinfo_v2->link_mode; + else + return; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): reason=%d, mode=%d\n", __func__, reason, mode); @@ -4812,6 +5066,8 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) _action_wl_2g_scc(rtwdev); else if (ver->fwlrole == 1) _action_wl_2g_scc_v1(rtwdev); + else if (ver->fwlrole == 2) + _action_wl_2g_scc_v2(rtwdev); break; case BTC_WLINK_2G_MCC: bt->scan_rx_low_pri = true; @@ -5317,8 +5573,10 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif memcpy(wlinfo, &r, sizeof(*wlinfo)); if (ver->fwlrole == 0) _update_wl_info(rtwdev); - else + else if (ver->fwlrole == 1) _update_wl_info_v1(rtwdev); + else if (ver->fwlrole == 2) + _update_wl_info_v2(rtwdev); if (wlinfo->role == RTW89_WIFI_ROLE_STATION && wlinfo->connected == MLME_NO_LINK) @@ -5838,6 +6096,7 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; struct rtw89_btc_wl_role_info_v1 *wl_rinfo_v1 = &wl->role_info_v1; + struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; u8 mode; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_WL)) @@ -5847,8 +6106,12 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) if (ver->fwlrole == 0) mode = wl_rinfo->link_mode; - else + else if (ver->fwlrole == 1) mode = wl_rinfo_v1->link_mode; + else if (ver->fwlrole == 2) + mode = wl_rinfo_v2->link_mode; + else + return; seq_printf(m, " %-15s : link_mode:%d, ", "[status]", mode); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f998f8a3f277..f51cd8665192 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1180,6 +1180,22 @@ struct rtw89_btc_wl_active_role_v1 { u32 noa_duration; /* ms */ }; +struct rtw89_btc_wl_active_role_v2 { + u8 connected: 1; + u8 pid: 3; + u8 phy: 1; + u8 noa: 1; + u8 band: 2; + + u8 client_ps: 1; + u8 bw: 7; + + u8 role; + u8 ch; + + u32 noa_duration; /* ms */ +}; + struct rtw89_btc_wl_role_info_bpos { u16 none: 1; u16 station: 1; @@ -1228,6 +1244,21 @@ struct rtw89_btc_wl_role_info_v1 { /* struct size must be n*4 bytes */ u32 rsvd: 27; }; +struct rtw89_btc_wl_role_info_v2 { /* struct size must be n*4 bytes */ + u8 connect_cnt; + u8 link_mode; + union rtw89_btc_wl_role_info_map role_map; + struct rtw89_btc_wl_active_role_v2 active_role_v2[RTW89_PORT_NUM]; + u32 mrole_type; /* btc_wl_mrole_type */ + u32 mrole_noa_duration; /* ms */ + + u32 dbcc_en: 1; + u32 dbcc_chg: 1; + u32 dbcc_2g_phy: 2; /* which phy operate in 2G, HW_PHY_0 or HW_PHY_1 */ + u32 link_mode_chg: 1; + u32 rsvd: 27; +}; + struct rtw89_btc_wl_ver_info { u32 fw_coex; /* match with which coex_ver */ u32 fw; @@ -1343,6 +1374,7 @@ struct rtw89_btc_wl_info { struct rtw89_btc_wl_afh_info afh_info; struct rtw89_btc_wl_role_info role_info; struct rtw89_btc_wl_role_info_v1 role_info_v1; + struct rtw89_btc_wl_role_info_v2 role_info_v2; struct rtw89_btc_wl_scan_info scan_info; struct rtw89_btc_wl_dbcc_info dbcc_info; struct rtw89_btc_rf_para rf_para; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 1a4ff24078fb..46d9cda2c617 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2038,6 +2038,92 @@ fail: return ret; } +#define H2C_LEN_CXDRVINFO_ROLE_SIZE_V2(max_role_num) \ + (4 + 8 * (max_role_num) + H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN + H2C_LEN_CXDRVHDR) + +int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + const struct rtw89_btc_ver *ver = btc->ver; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_wl_role_info_v2 *role_info = &wl->role_info_v2; + struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role; + struct rtw89_btc_wl_active_role_v2 *active = role_info->active_role_v2; + struct sk_buff *skb; + u32 len; + u8 *cmd, offset; + int ret; + int i; + + len = H2C_LEN_CXDRVINFO_ROLE_SIZE_V2(ver->max_role_num); + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_role\n"); + return -ENOMEM; + } + skb_put(skb, len); + cmd = skb->data; + + RTW89_SET_FWCMD_CXHDR_TYPE(cmd, CXDRVINFO_ROLE); + RTW89_SET_FWCMD_CXHDR_LEN(cmd, len - H2C_LEN_CXDRVHDR); + + RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt); + RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode); + + RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none); + RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station); + RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap); + RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc); + RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master); + RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh); + RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc); + RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go); + RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan); + + offset = PORT_DATA_OFFSET; + for (i = 0; i < RTW89_PORT_NUM; i++, active++) { + RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED_V2(cmd, active->connected, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PID_V2(cmd, active->pid, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_PHY_V2(cmd, active->phy, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_NOA_V2(cmd, active->noa, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BAND_V2(cmd, active->band, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS_V2(cmd, active->client_ps, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_BW_V2(cmd, active->bw, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_ROLE_V2(cmd, active->role, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_CH_V2(cmd, active->ch, i, offset); + RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR_V2(cmd, active->noa_duration, i, offset); + } + + offset = len - H2C_LEN_CXDRVINFO_ROLE_DBCC_LEN; + RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(cmd, role_info->mrole_type, offset); + RTW89_SET_FWCMD_CXROLE_MROLE_NOA(cmd, role_info->mrole_noa_duration, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_EN(cmd, role_info->dbcc_en, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_CHG(cmd, role_info->dbcc_chg, offset); + RTW89_SET_FWCMD_CXROLE_DBCC_2G_PHY(cmd, role_info->dbcc_2g_phy, offset); + RTW89_SET_FWCMD_CXROLE_LINK_MODE_CHG(cmd, role_info->link_mode_chg, offset); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, BTFC_SET, + SET_DRV_INFO, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + #define H2C_LEN_CXDRVINFO_CTRL (4 + H2C_LEN_CXDRVHDR) int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev) { diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 3f6e0871381d..fa9e786ad7c9 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2393,6 +2393,56 @@ static inline void RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR(void *cmd, u32 val, int n, le32p_replace_bits((__le32 *)((u8 *)cmd + (20 + (12 + offset) * n)), val, GENMASK(31, 0)); } +static inline void RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (6 + (12 + offset) * n), val, BIT(0)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_PID_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (6 + (12 + offset) * n), val, GENMASK(3, 1)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_PHY_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (6 + (12 + offset) * n), val, BIT(4)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_NOA_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (6 + (12 + offset) * n), val, BIT(5)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_BAND_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (6 + (12 + offset) * n), val, GENMASK(7, 6)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (7 + (12 + offset) * n), val, BIT(0)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_BW_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (7 + (12 + offset) * n), val, GENMASK(7, 1)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_ROLE_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (8 + (12 + offset) * n), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_CH_V2(void *cmd, u8 val, int n, u8 offset) +{ + u8p_replace_bits((u8 *)cmd + (9 + (12 + offset) * n), val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXROLE_ACT_NOA_DUR_V2(void *cmd, u32 val, int n, u8 offset) +{ + le32p_replace_bits((__le32 *)((u8 *)cmd + (10 + (12 + offset) * n)), val, GENMASK(31, 0)); +} + static inline void RTW89_SET_FWCMD_CXROLE_MROLE_TYPE(void *cmd, u32 val, u8 offset) { le32p_replace_bits((__le32 *)((u8 *)cmd + offset), val, GENMASK(31, 0)); @@ -3505,6 +3555,7 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev); +int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id); -- cgit v1.2.3 From a2c0ce5d01a2218af4756d311ae91845b67ac5b9 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 8 Mar 2023 13:32:21 +0800 Subject: wifi: rtw89: coex: Add traffic TX/RX info and its H2C There is a new mechanism which can do some real time performance tuning for WiFi and BT. This TX/RX info is a condition provide to firmware to do traffic analysis. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230308053225.24377-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 59 +++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 43 +++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.c | 56 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 87 +++++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index b56897529504..908ef8bb149a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1809,6 +1809,9 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_rf_trx_para rf_para = dm->rf_trx_para; switch (type) { case CXDRVINFO_INIT: @@ -1825,6 +1828,19 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type) case CXDRVINFO_CTRL: rtw89_fw_h2c_cxdrv_ctrl(rtwdev); break; + case CXDRVINFO_TRX: + dm->trx_info.tx_power = u32_get_bits(rf_para.wl_tx_power, + RTW89_BTC_WL_DEF_TX_PWR); + dm->trx_info.rx_gain = u32_get_bits(rf_para.wl_rx_gain, + RTW89_BTC_WL_DEF_TX_PWR); + dm->trx_info.bt_tx_power = u32_get_bits(rf_para.bt_tx_power, + RTW89_BTC_WL_DEF_TX_PWR); + dm->trx_info.bt_rx_gain = u32_get_bits(rf_para.bt_rx_gain, + RTW89_BTC_WL_DEF_TX_PWR); + dm->trx_info.cn = wl->cn_report; + dm->trx_info.nhm = wl->nhm.pwr; + rtw89_fw_h2c_cxdrv_trx(rtwdev); + break; case CXDRVINFO_RFK: rtw89_fw_h2c_cxdrv_rfk(rtwdev); break; @@ -5361,6 +5377,8 @@ void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } +#define BT_PROFILE_PROTOCOL_MASK GENMASK(7, 4) + static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) { const struct rtw89_chip_info *chip = rtwdev->chip; @@ -5417,6 +5435,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) a2dp->exist = btinfo.lb2.a2dp; b->profile_cnt.now += (u8)a2dp->exist; pan->active = btinfo.lb2.pan; + btc->dm.trx_info.bt_profile = u32_get_bits(btinfo.val, BT_PROFILE_PROTOCOL_MASK); /* parse raw info low-Byte3 */ btinfo.val = bt->raw_info[BTC_BTINFO_L3]; @@ -5433,6 +5452,7 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) btinfo.val = bt->raw_info[BTC_BTINFO_H0]; /* raw val is dBm unit, translate from -100~ 0dBm to 0~100%*/ b->rssi = chip->ops->btc_get_bt_rssi(rtwdev, btinfo.hb0.rssi); + btc->dm.trx_info.bt_rssi = b->rssi; /* parse raw info high-Byte1 */ btinfo.val = bt->raw_info[BTC_BTINFO_H1]; @@ -5766,6 +5786,8 @@ static void rtw89_btc_ntfy_wl_sta_iter(void *data, struct ieee80211_sta *sta) (struct rtw89_btc_wl_sta_iter_data *)data; struct rtw89_dev *rtwdev = iter_data->rtwdev; struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; + const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_link_info *link_info = NULL; struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; @@ -5773,6 +5795,8 @@ static void rtw89_btc_ntfy_wl_sta_iter(void *data, struct ieee80211_sta *sta) struct rtw89_vif *rtwvif = rtwsta->rtwvif; struct rtw89_traffic_stats *stats = &rtwvif->stats; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_btc_wl_role_info *r; + struct rtw89_btc_wl_role_info_v1 *r1; u32 last_tx_rate, last_rx_rate; u16 last_tx_lvl, last_rx_lvl; u8 port = rtwvif->port; @@ -5849,10 +5873,33 @@ static void rtw89_btc_ntfy_wl_sta_iter(void *data, struct ieee80211_sta *sta) link_info_t->tx_rate = rtwsta->ra_report.hw_rate; link_info_t->rx_rate = rtwsta->rx_hw_rate; - wl->role_info.active_role[port].tx_lvl = (u16)stats->tx_tfc_lv; - wl->role_info.active_role[port].rx_lvl = (u16)stats->rx_tfc_lv; - wl->role_info.active_role[port].tx_rate = rtwsta->ra_report.hw_rate; - wl->role_info.active_role[port].rx_rate = rtwsta->rx_hw_rate; + if (link_info->role == RTW89_WIFI_ROLE_STATION || + link_info->role == RTW89_WIFI_ROLE_P2P_CLIENT) { + dm->trx_info.tx_rate = link_info_t->tx_rate; + dm->trx_info.rx_rate = link_info_t->rx_rate; + } + + if (ver->fwlrole == 0) { + r = &wl->role_info; + r->active_role[port].tx_lvl = stats->tx_tfc_lv; + r->active_role[port].rx_lvl = stats->rx_tfc_lv; + r->active_role[port].tx_rate = rtwsta->ra_report.hw_rate; + r->active_role[port].rx_rate = rtwsta->rx_hw_rate; + } else if (ver->fwlrole == 1) { + r1 = &wl->role_info_v1; + r1->active_role_v1[port].tx_lvl = stats->tx_tfc_lv; + r1->active_role_v1[port].rx_lvl = stats->rx_tfc_lv; + r1->active_role_v1[port].tx_rate = rtwsta->ra_report.hw_rate; + r1->active_role_v1[port].rx_rate = rtwsta->rx_hw_rate; + } else if (ver->fwlrole == 2) { + dm->trx_info.tx_lvl = stats->tx_tfc_lv; + dm->trx_info.rx_lvl = stats->rx_tfc_lv; + dm->trx_info.tx_rate = rtwsta->ra_report.hw_rate; + dm->trx_info.rx_rate = rtwsta->rx_hw_rate; + } + + dm->trx_info.tx_tp = link_info_t->tx_throughput; + dm->trx_info.rx_tp = link_info_t->rx_throughput; if (is_sta_change) iter_data->is_sta_change = true; @@ -5866,6 +5913,7 @@ static void rtw89_btc_ntfy_wl_sta_iter(void *data, struct ieee80211_sta *sta) void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_sta_iter_data data = {.rtwdev = rtwdev}; u8 i; @@ -5884,6 +5932,9 @@ void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev) } } + if (dm->trx_info.wl_rssi != wl->rssi_level) + dm->trx_info.wl_rssi = wl->rssi_level; + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): busy=%d\n", __func__, !!wl->status.map.busy); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f51cd8665192..024441d45a66 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1367,6 +1367,22 @@ struct rtw89_btc_rf_para { u32 rx_gain_perpkt; }; +struct rtw89_btc_wl_nhm { + u8 instant_wl_nhm_dbm; + u8 instant_wl_nhm_per_mhz; + u16 valid_record_times; + s8 record_pwr[16]; + u8 record_ratio[16]; + s8 pwr; /* dbm_per_MHz */ + u8 ratio; + u8 current_status; + u8 refresh; + bool start_flag; + u8 last_ccx_rpt_stamp; + s8 pwr_max; + s8 pwr_min; +}; + struct rtw89_btc_wl_info { struct rtw89_btc_wl_link_info link_info[RTW89_PORT_NUM]; struct rtw89_btc_wl_rfk_info rfk_info; @@ -1378,10 +1394,12 @@ struct rtw89_btc_wl_info { struct rtw89_btc_wl_scan_info scan_info; struct rtw89_btc_wl_dbcc_info dbcc_info; struct rtw89_btc_rf_para rf_para; + struct rtw89_btc_wl_nhm nhm; union rtw89_btc_wl_state_map status; u8 port_id[RTW89_WIFI_ROLE_MLME_MAX]; u8 rssi_level; + u8 cn_report; bool scbd_change; u32 scbd; @@ -2019,6 +2037,30 @@ struct rtw89_btc_rf_trx_para { u8 bt_rx_gain; /* LNA constrain level */ }; +struct rtw89_btc_trx_info { + u8 tx_lvl; + u8 rx_lvl; + u8 wl_rssi; + u8 bt_rssi; + + s8 tx_power; /* absolute Tx power (dBm), 0xff-> no BTC control */ + s8 rx_gain; /* rx gain table index (TBD.) */ + s8 bt_tx_power; /* decrease Tx power (dB) */ + s8 bt_rx_gain; /* LNA constrain level */ + + u8 cn; /* condition_num */ + s8 nhm; + u8 bt_profile; + u8 rsvd2; + + u16 tx_rate; + u16 rx_rate; + + u32 tx_tp; + u32 rx_tp; + u32 rx_err_ratio; +}; + struct rtw89_btc_dm { struct rtw89_btc_fbtc_slot slot[CXST_MAX]; struct rtw89_btc_fbtc_slot slot_now[CXST_MAX]; @@ -2030,6 +2072,7 @@ struct rtw89_btc_dm { struct rtw89_btc_wl_tx_limit_para wl_tx_limit; struct rtw89_btc_dm_step dm_step; struct rtw89_btc_wl_scc_ctrl wl_scc; + struct rtw89_btc_trx_info trx_info; union rtw89_btc_dm_error_map error; u32 cnt_dm[BTC_DCNT_NUM]; u32 cnt_notify[BTC_NCNT_NUM]; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 46d9cda2c617..713aefd0cf8d 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2169,6 +2169,62 @@ fail: return ret; } +#define H2C_LEN_CXDRVINFO_TRX (28 + H2C_LEN_CXDRVHDR) +int rtw89_fw_h2c_cxdrv_trx(struct rtw89_dev *rtwdev) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_trx_info *trx = &btc->dm.trx_info; + struct sk_buff *skb; + u8 *cmd; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_LEN_CXDRVINFO_TRX); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_trx\n"); + return -ENOMEM; + } + skb_put(skb, H2C_LEN_CXDRVINFO_TRX); + cmd = skb->data; + + RTW89_SET_FWCMD_CXHDR_TYPE(cmd, CXDRVINFO_TRX); + RTW89_SET_FWCMD_CXHDR_LEN(cmd, H2C_LEN_CXDRVINFO_TRX - H2C_LEN_CXDRVHDR); + + RTW89_SET_FWCMD_CXTRX_TXLV(cmd, trx->tx_lvl); + RTW89_SET_FWCMD_CXTRX_RXLV(cmd, trx->rx_lvl); + RTW89_SET_FWCMD_CXTRX_WLRSSI(cmd, trx->wl_rssi); + RTW89_SET_FWCMD_CXTRX_BTRSSI(cmd, trx->bt_rssi); + RTW89_SET_FWCMD_CXTRX_TXPWR(cmd, trx->tx_power); + RTW89_SET_FWCMD_CXTRX_RXGAIN(cmd, trx->rx_gain); + RTW89_SET_FWCMD_CXTRX_BTTXPWR(cmd, trx->bt_tx_power); + RTW89_SET_FWCMD_CXTRX_BTRXGAIN(cmd, trx->bt_rx_gain); + RTW89_SET_FWCMD_CXTRX_CN(cmd, trx->cn); + RTW89_SET_FWCMD_CXTRX_NHM(cmd, trx->nhm); + RTW89_SET_FWCMD_CXTRX_BTPROFILE(cmd, trx->bt_profile); + RTW89_SET_FWCMD_CXTRX_RSVD2(cmd, trx->rsvd2); + RTW89_SET_FWCMD_CXTRX_TXRATE(cmd, trx->tx_rate); + RTW89_SET_FWCMD_CXTRX_RXRATE(cmd, trx->rx_rate); + RTW89_SET_FWCMD_CXTRX_TXTP(cmd, trx->tx_tp); + RTW89_SET_FWCMD_CXTRX_RXTP(cmd, trx->rx_tp); + RTW89_SET_FWCMD_CXTRX_RXERRRA(cmd, trx->rx_err_ratio); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, BTFC_SET, + SET_DRV_INFO, 0, 0, + H2C_LEN_CXDRVINFO_TRX); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + #define H2C_LEN_CXDRVINFO_RFK (4 + H2C_LEN_CXDRVHDR) int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev) { diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index fa9e786ad7c9..c3c67ddf61a2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2152,6 +2152,7 @@ enum rtw89_btc_cxdrvinfo { CXDRVINFO_RUN, CXDRVINFO_CTRL, CXDRVINFO_SCAN, + CXDRVINFO_TRX, /* WL traffic to WL fw */ CXDRVINFO_MAX, }; @@ -2493,6 +2494,91 @@ static inline void RTW89_SET_FWCMD_CXCTRL_TRACE_STEP(void *cmd, u32 val) le32p_replace_bits((__le32 *)((u8 *)(cmd) + 2), val, GENMASK(18, 3)); } +static inline void RTW89_SET_FWCMD_CXTRX_TXLV(void *cmd, u8 val) +{ + u8p_replace_bits((u8 *)cmd + 2, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_RXLV(void *cmd, u8 val) +{ + u8p_replace_bits((u8 *)cmd + 3, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_WLRSSI(void *cmd, u8 val) +{ + u8p_replace_bits((u8 *)cmd + 4, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_BTRSSI(void *cmd, u8 val) +{ + u8p_replace_bits((u8 *)cmd + 5, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_TXPWR(void *cmd, s8 val) +{ + u8p_replace_bits((u8 *)cmd + 6, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_RXGAIN(void *cmd, s8 val) +{ + u8p_replace_bits((u8 *)cmd + 7, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_BTTXPWR(void *cmd, s8 val) +{ + u8p_replace_bits((u8 *)cmd + 8, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_BTRXGAIN(void *cmd, s8 val) +{ + u8p_replace_bits((u8 *)cmd + 9, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_CN(void *cmd, u8 val) +{ + u8p_replace_bits((u8 *)cmd + 10, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_NHM(void *cmd, s8 val) +{ + u8p_replace_bits((u8 *)cmd + 11, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_BTPROFILE(void *cmd, u8 val) +{ + u8p_replace_bits((u8 *)cmd + 12, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_RSVD2(void *cmd, u8 val) +{ + u8p_replace_bits((u8 *)cmd + 13, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_TXRATE(void *cmd, u16 val) +{ + le16p_replace_bits((__le16 *)((u8 *)cmd + 14), val, GENMASK(15, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_RXRATE(void *cmd, u16 val) +{ + le16p_replace_bits((__le16 *)((u8 *)cmd + 16), val, GENMASK(15, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_TXTP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)cmd + 18), val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_RXTP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)cmd + 22), val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_CXTRX_RXERRRA(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)((u8 *)cmd + 26), val, GENMASK(31, 0)); +} + static inline void RTW89_SET_FWCMD_CXRFK_STATE(void *cmd, u32 val) { le32p_replace_bits((__le32 *)((u8 *)(cmd) + 2), val, GENMASK(1, 0)); @@ -3557,6 +3643,7 @@ int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev); +int rtw89_fw_h2c_cxdrv_trx(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id); int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, -- cgit v1.2.3 From e5e52feb5053a537180cf928428deb8bc697a42e Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 8 Mar 2023 13:32:22 +0800 Subject: wifi: rtw89: coex: Add register monitor report v2 format The v2 firmware report reduce its maximum register numbers from 30 to 20, it can help to save firmware code size. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230308053225.24377-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 148 +++++++++++++++++++++++++++--- drivers/net/wireless/realtek/rtw89/core.h | 17 +++- 2 files changed, 151 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 908ef8bb149a..436d5e35ae49 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -226,7 +226,6 @@ struct rtw89_btc_btf_set_slot_table { u8 buf[]; } __packed; -#define BTF_SET_MON_REG_VER 1 struct rtw89_btc_btf_set_mon_reg { u8 fver; u8 reg_num; @@ -1078,8 +1077,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, break; case BTC_RPT_TYPE_MREG: pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; - pfinfo = &pfwinfo->rpt_fbtc_mregval.finfo; - pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_mregval.finfo); + if (ver->fcxmreg == 1) { + pfinfo = &pfwinfo->rpt_fbtc_mregval.finfo.v1; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_mregval.finfo.v1); + } else if (ver->fcxmreg == 2) { + pfinfo = &pfwinfo->rpt_fbtc_mregval.finfo.v2; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_mregval.finfo.v2); + } else { + goto err; + } pcinfo->req_fver = ver->fcxmreg; break; case BTC_RPT_TYPE_GPIO_DBG: @@ -1709,18 +1715,26 @@ static void rtw89_btc_fw_set_slots(struct rtw89_dev *rtwdev, u8 num, static void btc_fw_set_monreg(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_btc_ver *ver = rtwdev->btc.ver; struct rtw89_btc_btf_set_mon_reg *monreg = NULL; - u8 n, *ptr = NULL, ulen; + u8 n, *ptr = NULL, ulen, cxmreg_max; u16 sz = 0; n = chip->mon_reg_num; - rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): mon_reg_num=%d\n", __func__, n); - if (n > CXMREG_MAX) { + + if (ver->fcxmreg == 1) + cxmreg_max = CXMREG_MAX; + else if (ver->fcxmreg == 2) + cxmreg_max = CXMREG_MAX_V2; + else + return; + + if (n > cxmreg_max) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): mon reg count %d > %d\n", - __func__, n, CXMREG_MAX); + __func__, n, cxmreg_max); return; } @@ -1730,7 +1744,7 @@ static void btc_fw_set_monreg(struct rtw89_dev *rtwdev) if (!monreg) return; - monreg->fver = BTF_SET_MON_REG_VER; + monreg->fver = ver->fcxmreg; monreg->reg_num = n; ptr = &monreg->buf[0]; memcpy(ptr, chip->mon_reg, n * ulen); @@ -7401,13 +7415,13 @@ static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt } } -static void _show_mreg(struct rtw89_dev *rtwdev, struct seq_file *m) +static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; - struct rtw89_btc_fbtc_mreg_val *pmreg = NULL; + struct rtw89_btc_fbtc_mreg_val_v1 *pmreg = NULL; struct rtw89_btc_fbtc_gpio_dbg *gdbg = NULL; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &btc->cx.wl; @@ -7457,7 +7471,7 @@ static void _show_mreg(struct rtw89_dev *rtwdev, struct seq_file *m) return; } - pmreg = &pfwinfo->rpt_fbtc_mregval.finfo; + pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v1; rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): rpt_fbtc_mregval reg_num = %d\n", __func__, pmreg->reg_num); @@ -7486,6 +7500,111 @@ static void _show_mreg(struct rtw89_dev *rtwdev, struct seq_file *m) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): stop due rpt_fbtc_gpio_dbg.cinfo\n", __func__); + seq_puts(m, "\n"); + return; + } + + gdbg = &pfwinfo->rpt_fbtc_gpio_dbg.finfo; + if (!gdbg->en_map) + return; + + seq_printf(m, " %-15s : enable_map:0x%08x", + "[gpio_dbg]", gdbg->en_map); + + for (i = 0; i < BTC_DBG_MAX1; i++) { + if (!(gdbg->en_map & BIT(i))) + continue; + seq_printf(m, ", %d->GPIO%d", (u32)i, gdbg->gpio_map[i]); + } + seq_puts(m, "\n"); +} + +static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; + struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; + struct rtw89_btc_fbtc_mreg_val_v2 *pmreg = NULL; + struct rtw89_btc_fbtc_gpio_dbg *gdbg = NULL; + struct rtw89_btc_cx *cx = &btc->cx; + struct rtw89_btc_wl_info *wl = &btc->cx.wl; + struct rtw89_btc_bt_info *bt = &btc->cx.bt; + struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; + struct rtw89_mac_ax_gnt gnt; + u8 i = 0, type = 0, cnt = 0; + u32 val, offset; + + if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG)) + return; + + seq_puts(m, "========== [HW Status] ==========\n"); + + seq_printf(m, + " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], + cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + + /* To avoid I/O if WL LPS or power-off */ + if (!wl->status.map.lps && !wl->status.map.rf_off) { + btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); + + _get_gnt(rtwdev, &gnt_cfg); + gnt = gnt_cfg.band[0]; + seq_printf(m, + " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ", + "[gnt_status]", + chip->chip_id == RTL8852C ? "HW" : + btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", + gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt); + + gnt = gnt_cfg.band[1]; + seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", + gnt.gnt_wl_sw_en ? "SW" : "HW", + gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", + gnt.gnt_bt); + } + pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; + if (!pcinfo->valid) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n", + __func__); + return; + } + + pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v2; + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): rpt_fbtc_mregval reg_num = %d\n", + __func__, pmreg->reg_num); + + for (i = 0; i < pmreg->reg_num; i++) { + type = (u8)le16_to_cpu(chip->mon_reg[i].type); + offset = le32_to_cpu(chip->mon_reg[i].offset); + val = le32_to_cpu(pmreg->mreg_val[i]); + + if (cnt % 6 == 0) + seq_printf(m, " %-15s : %d_0x%04x=0x%08x", + "[reg]", (u32)type, offset, val); + else + seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type, + offset, val); + if (cnt % 6 == 5) + seq_puts(m, "\n"); + cnt++; + + if (i >= pmreg->reg_num) + seq_puts(m, "\n"); + } + + pcinfo = &pfwinfo->rpt_fbtc_gpio_dbg.cinfo; + if (!pcinfo->valid) { + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC], %s(): stop due rpt_fbtc_gpio_dbg.cinfo\n", + __func__); + seq_puts(m, "\n"); return; } @@ -7868,7 +7987,12 @@ void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m) _show_bt_info(rtwdev, m); _show_dm_info(rtwdev, m); _show_fw_dm_msg(rtwdev, m); - _show_mreg(rtwdev, m); + + if (ver->fcxmreg == 1) + _show_mreg_v1(rtwdev, m); + else if (ver->fcxmreg == 2) + _show_mreg_v2(rtwdev, m); + if (ver->fcxbtcrpt == 1) _show_summary_v1(rtwdev, m); else if (ver->fcxbtcrpt == 4) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 024441d45a66..b0525f258d3c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1524,6 +1524,7 @@ union rtw89_btc_fbtc_tdma_le32 { }; #define CXMREG_MAX 30 +#define CXMREG_MAX_V2 20 #define FCXMAX_STEP 255 /*STEP trace record cnt, Max:65535, default:255*/ #define BTC_CYCLE_SLOT_MAX 48 /* must be even number, non-zero */ @@ -1750,13 +1751,25 @@ struct rtw89_btc_fbtc_gpio_dbg { u8 gpio_map[BTC_DBG_MAX1]; /*the debug signals to GPIO-Position */ } __packed; -struct rtw89_btc_fbtc_mreg_val { +struct rtw89_btc_fbtc_mreg_val_v1 { u8 fver; /* btc_ver::fcxmreg */ u8 reg_num; __le16 rsvd; __le32 mreg_val[CXMREG_MAX]; } __packed; +struct rtw89_btc_fbtc_mreg_val_v2 { + u8 fver; /* btc_ver::fcxmreg */ + u8 reg_num; + __le16 rsvd; + __le32 mreg_val[CXMREG_MAX_V2]; +} __packed; + +union rtw89_btc_fbtc_mreg_val { + struct rtw89_btc_fbtc_mreg_val_v1 v1; + struct rtw89_btc_fbtc_mreg_val_v2 v2; +}; + #define RTW89_DEF_FBTC_MREG(__type, __bytes, __offset) \ { .type = cpu_to_le16(__type), .bytes = cpu_to_le16(__bytes), \ .offset = cpu_to_le32(__offset), } @@ -2203,7 +2216,7 @@ struct rtw89_btc_rpt_fbtc_nullsta { struct rtw89_btc_rpt_fbtc_mreg { struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */ - struct rtw89_btc_fbtc_mreg_val finfo; /* info from fw */ + union rtw89_btc_fbtc_mreg_val finfo; /* info from fw */ }; struct rtw89_btc_rpt_fbtc_gpio_dbg { -- cgit v1.2.3 From 9dfa09e0628d2024ce4574f645344c00fe88a535 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 8 Mar 2023 13:32:23 +0800 Subject: wifi: rtw89: coex: Fix wrong structure assignment at null data report Correct pointer assignment of v1 null data report. It doesn't really change logic at all, but it looks more readable. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230308053225.24377-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 436d5e35ae49..0ca621a9f80a 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -1065,7 +1065,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, case BTC_RPT_TYPE_NULLSTA: pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo; if (ver->fcxnullsta == 1) { - pfinfo = &pfwinfo->rpt_fbtc_nullsta.finfo; + pfinfo = &pfwinfo->rpt_fbtc_nullsta.finfo.v1; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_nullsta.finfo.v1); } else if (ver->fcxnullsta == 2) { pfinfo = &pfwinfo->rpt_fbtc_nullsta.finfo.v2; -- cgit v1.2.3 From 262cc19ea902d2280e2e2a56b153f37466e3349e Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 8 Mar 2023 13:32:24 +0800 Subject: wifi: rtw89: coex: Add v2 Bluetooth scan info Compare to v1 and v2 removed some not usable parameters. Save firmware code size. The information can show how frequent and how long the Bluetooth scan do. It will help to debug coexistence issue. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230308053225.24377-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 67 ++++++++++++++++++++++++++++--- drivers/net/wireless/realtek/rtw89/core.h | 63 ++++++++++++++++++++--------- 2 files changed, 106 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 0ca621a9f80a..b867e54feb89 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -890,13 +890,15 @@ static void _update_bt_report(struct rtw89_dev *rtwdev, u8 rpt_type, u8 *pfinfo) struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; struct rtw89_btc_bt_a2dp_desc *a2dp = &bt_linfo->a2dp_desc; struct rtw89_btc_fbtc_btver *pver = NULL; - struct rtw89_btc_fbtc_btscan *pscan = NULL; + struct rtw89_btc_fbtc_btscan_v1 *pscan_v1; + struct rtw89_btc_fbtc_btscan_v2 *pscan_v2; struct rtw89_btc_fbtc_btafh *pafh_v1 = NULL; struct rtw89_btc_fbtc_btafh_v2 *pafh_v2 = NULL; struct rtw89_btc_fbtc_btdevinfo *pdev = NULL; + bool scan_update = true; + int i; pver = (struct rtw89_btc_fbtc_btver *)pfinfo; - pscan = (struct rtw89_btc_fbtc_btscan *)pfinfo; pdev = (struct rtw89_btc_fbtc_btdevinfo *)pfinfo; rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -910,7 +912,26 @@ static void _update_bt_report(struct rtw89_dev *rtwdev, u8 rpt_type, u8 *pfinfo) bt->feature = le32_to_cpu(pver->feature); break; case BTC_RPT_TYPE_BT_SCAN: - memcpy(bt->scan_info, pscan->scan, BTC_SCAN_MAX1); + if (ver->fcxbtscan == 1) { + pscan_v1 = (struct rtw89_btc_fbtc_btscan_v1 *)pfinfo; + for (i = 0; i < BTC_SCAN_MAX1; i++) { + bt->scan_info_v1[i] = pscan_v1->scan[i]; + if (bt->scan_info_v1[i].win == 0 && + bt->scan_info_v1[i].intvl == 0) + scan_update = false; + } + } else if (ver->fcxbtscan == 2) { + pscan_v2 = (struct rtw89_btc_fbtc_btscan_v2 *)pfinfo; + for (i = 0; i < CXSCAN_MAX; i++) { + bt->scan_info_v2[i] = pscan_v2->para[i]; + if ((pscan_v2->type & BIT(i)) && + pscan_v2->para[i].win == 0 && + pscan_v2->para[i].intvl == 0) + scan_update = false; + } + } + if (scan_update) + bt->scan_info_update = 1; break; case BTC_RPT_TYPE_BT_AFH: if (ver->fcxbtafh == 2) { @@ -1102,8 +1123,13 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, break; case BTC_RPT_TYPE_BT_SCAN: pcinfo = &pfwinfo->rpt_fbtc_btscan.cinfo; - pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo; - pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo); + if (ver->fcxbtscan == 1) { + pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo.v1; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo.v1); + } else if (ver->fcxbtscan == 2) { + pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo.v2; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo.v2); + } pcinfo->req_fver = ver->fcxbtscan; break; case BTC_RPT_TYPE_BT_AFH: @@ -6346,11 +6372,40 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) cx->cnt_bt[BTC_BCNT_INFOSAME]); seq_printf(m, - " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)\n", + " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], cx->cnt_bt[BTC_BCNT_HIPRI_TX], cx->cnt_bt[BTC_BCNT_LOPRI_RX], cx->cnt_bt[BTC_BCNT_LOPRI_TX], cx->cnt_bt[BTC_BCNT_POLUT]); + if (!bt->scan_info_update) { + rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true); + seq_puts(m, "\n"); + } else { + rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, false); + if (ver->fcxbtscan == 1) { + seq_printf(m, + "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); + } else if (ver->fcxbtscan == 2) { + seq_printf(m, + "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); + } + seq_puts(m, "\n"); + } + if (bt->enable.now && bt->ver_info.fw == 0) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true); else diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b0525f258d3c..02fb3c73b137 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1445,14 +1445,6 @@ struct rtw89_btc_wl_tx_limit_para { u16 tx_retry; }; -struct rtw89_btc_bt_scan_info { - u16 win; - u16 intvl; - u32 enable: 1; - u32 interlace: 1; - u32 rsvd: 30; -}; - enum rtw89_btc_bt_scan_type { BTC_SCAN_INQ = 0, BTC_SCAN_PAGE, @@ -1463,9 +1455,50 @@ enum rtw89_btc_bt_scan_type { BTC_SCAN_MAX1, }; +enum rtw89_btc_ble_scan_type { + CXSCAN_BG = 0, + CXSCAN_INIT, + CXSCAN_LE, + CXSCAN_MAX +}; + +#define RTW89_BTC_BTC_SCAN_V1_FLAG_ENABLE BIT(0) +#define RTW89_BTC_BTC_SCAN_V1_FLAG_INTERLACE BIT(1) + +struct rtw89_btc_bt_scan_info_v1 { + __le16 win; + __le16 intvl; + __le32 flags; +} __packed; + +struct rtw89_btc_bt_scan_info_v2 { + __le16 win; + __le16 intvl; +} __packed; + +struct rtw89_btc_fbtc_btscan_v1 { + u8 fver; /* btc_ver::fcxbtscan */ + u8 rsvd; + __le16 rsvd2; + struct rtw89_btc_bt_scan_info_v1 scan[BTC_SCAN_MAX1]; +} __packed; + +struct rtw89_btc_fbtc_btscan_v2 { + u8 fver; /* btc_ver::fcxbtscan */ + u8 type; + __le16 rsvd2; + struct rtw89_btc_bt_scan_info_v2 para[CXSCAN_MAX]; +} __packed; + +union rtw89_btc_fbtc_btscan { + struct rtw89_btc_fbtc_btscan_v1 v1; + struct rtw89_btc_fbtc_btscan_v2 v2; +}; + struct rtw89_btc_bt_info { struct rtw89_btc_bt_link_info link_info; - struct rtw89_btc_bt_scan_info scan_info[BTC_SCAN_MAX1]; + struct rtw89_btc_bt_scan_info_v1 scan_info_v1[BTC_SCAN_MAX1]; + struct rtw89_btc_bt_scan_info_v2 scan_info_v2[CXSCAN_MAX]; struct rtw89_btc_bt_ver_info ver_info; struct rtw89_btc_bool_sta_chg enable; struct rtw89_btc_bool_sta_chg inq_pag; @@ -1488,7 +1521,8 @@ struct rtw89_btc_bt_info { u32 run_patch_code: 1; u32 hi_lna_rx: 1; u32 scan_rx_low_pri: 1; - u32 rsvd: 21; + u32 scan_info_update: 1; + u32 rsvd: 20; }; struct rtw89_btc_cx { @@ -2006,13 +2040,6 @@ struct rtw89_btc_fbtc_btver { __le32 feature; } __packed; -struct rtw89_btc_fbtc_btscan { - u8 fver; /* btc_ver::fcxbtscan */ - u8 rsvd; - __le16 rsvd2; - u8 scan[6]; -} __packed; - struct rtw89_btc_fbtc_btafh { u8 fver; /* btc_ver::fcxbtafh */ u8 rsvd; @@ -2231,7 +2258,7 @@ struct rtw89_btc_rpt_fbtc_btver { struct rtw89_btc_rpt_fbtc_btscan { struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */ - struct rtw89_btc_fbtc_btscan finfo; /* info from fw */ + union rtw89_btc_fbtc_btscan finfo; /* info from fw */ }; struct rtw89_btc_rpt_fbtc_btafh { -- cgit v1.2.3 From 3ab7f9b90cc0a737e0bd8a312dc48814c4682867 Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Wed, 8 Mar 2023 13:32:25 +0800 Subject: wifi: rtw89: coex: Add v5 firmware cycle status report To support v5 version firmware cycle report, apply the related structure and functions. v5 cycle report add a group of status to show how the free-run/TDMA training goes to. It is a firmware mechanism that can auto adjust coexistence mode between TDMA and free run mechanism at 3 antenna solution. v5 version provide more reference data to let the mechanism make decision. Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230308053225.24377-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/coex.c | 194 +++++++++++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/coex.h | 5 + drivers/net/wireless/realtek/rtw89/core.h | 52 ++++++++ 3 files changed, 249 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index b867e54feb89..3dbd4ee14c70 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -987,8 +987,8 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, void *rpt_content = NULL, *pfinfo = NULL; u8 rpt_type = 0; u16 wl_slot_set = 0, wl_slot_real = 0; - u32 trace_step = btc->ctrl.trace_step, rpt_len = 0, diff_t; - u32 cnt_leak_slot = 0, bt_slot_real = 0, cnt_rx_imr = 0; + u32 trace_step = btc->ctrl.trace_step, rpt_len = 0, diff_t = 0; + u32 cnt_leak_slot, bt_slot_real, bt_slot_set, cnt_rx_imr; u8 i; rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -1061,6 +1061,10 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v4; pcysta->v4 = pfwinfo->rpt_fbtc_cysta.finfo.v4; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v4); + } else if (ver->fcxcysta == 5) { + pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v5; + pcysta->v5 = pfwinfo->rpt_fbtc_cysta.finfo.v5; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v5); } else { goto err; } @@ -1406,6 +1410,54 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, le16_to_cpu(pcysta->v4.slot_cnt[CXST_B1])); _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_HANG, le16_to_cpu(pcysta->v4.cycles)); + } else if (ver->fcxcysta == 5) { + if (dm->fddt_train == BTC_FDDT_ENABLE) + break; + cnt_leak_slot = le16_to_cpu(pcysta->v5.slot_cnt[CXST_LK]); + cnt_rx_imr = le32_to_cpu(pcysta->v5.leak_slot.cnt_rximr); + + /* Check Leak-AP */ + if (cnt_leak_slot != 0 && cnt_rx_imr != 0 && + dm->tdma_now.rxflctrl) { + if (le16_to_cpu(pcysta->v5.cycles) >= BTC_CYSTA_CHK_PERIOD && + cnt_leak_slot < BTC_LEAK_AP_TH * cnt_rx_imr) + dm->leak_ap = 1; + } + + /* Check diff time between real WL slot and W1 slot */ + if (dm->tdma_now.type == CXTDMA_OFF) { + wl_slot_set = le16_to_cpu(dm->slot_now[CXST_W1].dur); + wl_slot_real = le16_to_cpu(pcysta->v5.cycle_time.tavg[CXT_WL]); + + if (wl_slot_real > wl_slot_set) + diff_t = wl_slot_real - wl_slot_set; + else + diff_t = wl_slot_set - wl_slot_real; + } + _chk_btc_err(rtwdev, BTC_DCNT_WL_SLOT_DRIFT, diff_t); + + /* Check diff time between real BT slot and EBT/E5G slot */ + bt_slot_set = btc->bt_req_len; + bt_slot_real = le16_to_cpu(pcysta->v5.cycle_time.tavg[CXT_BT]); + diff_t = 0; + if (dm->tdma_now.type == CXTDMA_OFF && + dm->tdma_now.ext_ctrl == CXECTL_EXT && + bt_slot_set != 0) { + if (bt_slot_set > bt_slot_real) + diff_t = bt_slot_set - bt_slot_real; + else + diff_t = bt_slot_real - bt_slot_set; + } + + _chk_btc_err(rtwdev, BTC_DCNT_BT_SLOT_DRIFT, diff_t); + _chk_btc_err(rtwdev, BTC_DCNT_E2G_HANG, + le16_to_cpu(pcysta->v5.slot_cnt[CXST_E2G])); + _chk_btc_err(rtwdev, BTC_DCNT_W1_HANG, + le16_to_cpu(pcysta->v5.slot_cnt[CXST_W1])); + _chk_btc_err(rtwdev, BTC_DCNT_B1_HANG, + le16_to_cpu(pcysta->v5.slot_cnt[CXST_B1])); + _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_HANG, + le16_to_cpu(pcysta->v5.cycles)); } else { goto err; } @@ -5039,6 +5091,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) } dm->cnt_dm[BTC_DCNT_RUN]++; + dm->fddt_train = BTC_FDDT_DISABLE; if (btc->ctrl.always_freerun) { _action_freerun(rtwdev); @@ -6727,6 +6780,10 @@ static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m) pcysta->v4 = pfwinfo->rpt_fbtc_cysta.finfo.v4; except_cnt = pcysta->v4.except_cnt; exception_map = le32_to_cpu(pcysta->v4.except_map); + } else if (ver->fcxcysta == 5) { + pcysta->v5 = pfwinfo->rpt_fbtc_cysta.finfo.v5; + except_cnt = pcysta->v5.except_cnt; + exception_map = le32_to_cpu(pcysta->v5.except_map); } else { return; } @@ -7215,6 +7272,137 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) } } +static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) +{ + struct rtw89_btc *btc = &rtwdev->btc; + struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; + struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; + struct rtw89_btc_dm *dm = &btc->dm; + struct rtw89_btc_fbtc_a2dp_trx_stat_v4 *a2dp_trx; + struct rtw89_btc_fbtc_cysta_v5 *pcysta; + struct rtw89_btc_rpt_cmn_info *pcinfo; + u8 i, cnt = 0, slot_pair, divide_cnt; + u16 cycle, c_begin, c_end, store_index; + + pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; + if (!pcinfo->valid) + return; + + pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v5; + seq_printf(m, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + + for (i = 0; i < CXST_MAX; i++) { + if (!le16_to_cpu(pcysta->slot_cnt[i])) + continue; + + seq_printf(m, ", %s:%d", id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); + } + + if (dm->tdma_now.rxflctrl) + seq_printf(m, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + + if (pcysta->collision_cnt) + seq_printf(m, ", collision:%d", pcysta->collision_cnt); + + if (le16_to_cpu(pcysta->skip_cnt)) + seq_printf(m, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); + + seq_puts(m, "\n"); + + seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + seq_printf(m, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + + cycle = le16_to_cpu(pcysta->cycles); + if (cycle <= 1) + return; + + /* 1 cycle record 1 wl-slot and 1 bt-slot */ + slot_pair = BTC_CYCLE_SLOT_MAX / 2; + + if (cycle <= slot_pair) + c_begin = 1; + else + c_begin = cycle - slot_pair + 1; + + c_end = cycle; + + if (a2dp->exist) + divide_cnt = 3; + else + divide_cnt = BTC_CYCLE_SLOT_MAX / 4; + + if (c_begin > c_end) + return; + + for (cycle = c_begin; cycle <= c_end; cycle++) { + cnt++; + store_index = ((cycle - 1) % slot_pair) * 2; + + if (cnt % divide_cnt == 1) + seq_printf(m, " %-15s : ", "[cycle_step]"); + + seq_printf(m, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); + if (a2dp->exist) { + a2dp_trx = &pcysta->a2dp_trx[store_index]; + seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); + } + seq_printf(m, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + if (a2dp->exist) { + a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; + seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); + } + if (cnt % divide_cnt == 0 || cnt == c_end) + seq_puts(m, "\n"); + } + + if (a2dp->exist) { + seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + + seq_printf(m, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); + + seq_puts(m, "\n"); + } +} + static void _show_fbtc_nullsta(struct rtw89_dev *rtwdev, struct seq_file *m) { struct rtw89_btc *btc = &rtwdev->btc; @@ -7419,6 +7607,8 @@ static void _show_fw_dm_msg(struct rtw89_dev *rtwdev, struct seq_file *m) _show_fbtc_cysta_v3(rtwdev, m); else if (ver->fcxcysta == 4) _show_fbtc_cysta_v4(rtwdev, m); + else if (ver->fcxcysta == 5) + _show_fbtc_cysta_v5(rtwdev, m); _show_fbtc_nullsta(rtwdev, m); diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index 401fb55df82b..38cc53a505c3 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -66,6 +66,11 @@ enum btc_rssi_st { BTC_RSSI_ST_MAX }; +enum btc_fddt_en { + BTC_FDDT_DISABLE, + BTC_FDDT_ENABLE, +}; + #define BTC_RSSI_HIGH(_rssi_) \ ({typeof(_rssi_) __rssi = (_rssi_); \ ((__rssi == BTC_RSSI_ST_HIGH || \ diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 02fb3c73b137..e447bfec508b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1894,6 +1894,11 @@ struct rtw89_btc_fbtc_cycle_time_info { __le16 tmaxdiff[CXT_MAX]; /* max wl-wl bt-bt cycle diff time */ } __packed; +struct rtw89_btc_fbtc_cycle_time_info_v5 { + __le16 tavg[CXT_MAX]; /* avg wl/bt cycle time */ + __le16 tmax[CXT_MAX]; /* max wl/bt cycle time */ +} __packed; + struct rtw89_btc_fbtc_a2dp_trx_stat { u8 empty_cnt; u8 retry_cnt; @@ -1950,6 +1955,21 @@ struct rtw89_btc_fbtc_cycle_fddt_info { #define RTW89_BTC_FDDT_CELL_TRAIN_STATE GENMASK(3, 0) #define RTW89_BTC_FDDT_CELL_TRAIN_PHASE GENMASK(7, 4) +struct rtw89_btc_fbtc_cycle_fddt_info_v5 { + __le16 train_cycle; + __le16 tp; + + s8 tx_power; /* absolute Tx power (dBm), 0xff-> no BTC control */ + s8 bt_tx_power; /* decrease Tx power (dB) */ + s8 bt_rx_gain; /* LNA constrain level */ + u8 no_empty_cnt; + + u8 rssi; /* [7:4] -> bt_rssi_level, [3:0]-> wl_rssi_level */ + u8 cn; /* condition_num */ + u8 train_status; /* [7:4]-> train-state, [3:0]-> train-phase */ + u8 train_result; /* refer to enum btc_fddt_check_map */ +} __packed; + struct rtw89_btc_fbtc_fddt_cell_status { s8 wl_tx_pwr; s8 bt_tx_pwr; @@ -1957,6 +1977,12 @@ struct rtw89_btc_fbtc_fddt_cell_status { u8 state_phase; /* [0:3] train state, [4:7] train phase */ } __packed; +struct rtw89_btc_fbtc_fddt_cell_status_v5 { + s8 wl_tx_pwr; + s8 bt_tx_pwr; + s8 bt_rx_gain; +} __packed; + struct rtw89_btc_fbtc_cysta_v3 { /* statistics for cycles */ u8 fver; u8 rsvd; @@ -2002,10 +2028,35 @@ struct rtw89_btc_fbtc_cysta_v4 { /* statistics for cycles */ __le32 except_map; } __packed; +struct rtw89_btc_fbtc_cysta_v5 { /* statistics for cycles */ + u8 fver; + u8 rsvd; + u8 collision_cnt; /* counter for event/timer occur at the same time */ + u8 except_cnt; + u8 wl_rx_err_ratio[BTC_CYCLE_SLOT_MAX]; + + __le16 skip_cnt; + __le16 cycles; /* total cycle number */ + + __le16 slot_step_time[BTC_CYCLE_SLOT_MAX]; /* record the wl/bt slot time */ + __le16 slot_cnt[CXST_MAX]; /* slot count */ + __le16 bcn_cnt[CXBCN_MAX]; + struct rtw89_btc_fbtc_cycle_time_info_v5 cycle_time; + struct rtw89_btc_fbtc_cycle_leak_info leak_slot; + struct rtw89_btc_fbtc_cycle_a2dp_empty_info a2dp_ept; + struct rtw89_btc_fbtc_a2dp_trx_stat_v4 a2dp_trx[BTC_CYCLE_SLOT_MAX]; + struct rtw89_btc_fbtc_cycle_fddt_info_v5 fddt_trx[BTC_CYCLE_SLOT_MAX]; + struct rtw89_btc_fbtc_fddt_cell_status_v5 fddt_cells[FDD_TRAIN_WL_DIRECTION] + [FDD_TRAIN_WL_RSSI_LEVEL] + [FDD_TRAIN_BT_RSSI_LEVEL]; + __le32 except_map; +} __packed; + union rtw89_btc_fbtc_cysta_info { struct rtw89_btc_fbtc_cysta_v2 v2; struct rtw89_btc_fbtc_cysta_v3 v3; struct rtw89_btc_fbtc_cysta_v4 v4; + struct rtw89_btc_fbtc_cysta_v5 v5; }; struct rtw89_btc_fbtc_cynullsta_v1 { /* cycle null statistics */ @@ -2123,6 +2174,7 @@ struct rtw89_btc_dm { u32 wl_only: 1; u32 wl_fw_cx_offload: 1; u32 freerun: 1; + u32 fddt_train: 1; u32 wl_ps_ctrl: 2; u32 wl_mimo_ps: 1; u32 leak_ap: 1; -- cgit v1.2.3 From 48181d285623198c33bb9698992502687b258efa Mon Sep 17 00:00:00 2001 From: Dongliang Mu Date: Thu, 9 Mar 2023 10:16:36 +0800 Subject: wifi: rtw88: fix memory leak in rtw_usb_probe() drivers/net/wireless/realtek/rtw88/usb.c:876 rtw_usb_probe() warn: 'hw' from ieee80211_alloc_hw() not released on lines: 811 Fix this by modifying return to a goto statement. Signed-off-by: Dongliang Mu Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230309021636.528601-1-dzm91@hust.edu.cn --- drivers/net/wireless/realtek/rtw88/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 2a8336b1847a..68e1b782d199 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -808,7 +808,7 @@ int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ret = rtw_usb_alloc_rx_bufs(rtwusb); if (ret) - return ret; + goto err_release_hw; ret = rtw_core_init(rtwdev); if (ret) -- cgit v1.2.3 From 0da40e018fd034d87c9460123fa7f897b69fdee7 Mon Sep 17 00:00:00 2001 From: Jisoo Jang Date: Thu, 9 Mar 2023 19:44:57 +0900 Subject: wifi: brcmfmac: slab-out-of-bounds read in brcmf_get_assoc_ies() Fix a slab-out-of-bounds read that occurs in kmemdup() called from brcmf_get_assoc_ies(). The bug could occur when assoc_info->req_len, data from a URB provided by a USB device, is bigger than the size of buffer which is defined as WL_EXTRA_BUF_MAX. Add the size check for req_len/resp_len of assoc_info. Found by a modified version of syzkaller. [ 46.592467][ T7] ================================================================== [ 46.594687][ T7] BUG: KASAN: slab-out-of-bounds in kmemdup+0x3e/0x50 [ 46.596572][ T7] Read of size 3014656 at addr ffff888019442000 by task kworker/0:1/7 [ 46.598575][ T7] [ 46.599157][ T7] CPU: 0 PID: 7 Comm: kworker/0:1 Tainted: G O 5.14.0+ #145 [ 46.601333][ T7] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 [ 46.604360][ T7] Workqueue: events brcmf_fweh_event_worker [ 46.605943][ T7] Call Trace: [ 46.606584][ T7] dump_stack_lvl+0x8e/0xd1 [ 46.607446][ T7] print_address_description.constprop.0.cold+0x93/0x334 [ 46.608610][ T7] ? kmemdup+0x3e/0x50 [ 46.609341][ T7] kasan_report.cold+0x79/0xd5 [ 46.610151][ T7] ? kmemdup+0x3e/0x50 [ 46.610796][ T7] kasan_check_range+0x14e/0x1b0 [ 46.611691][ T7] memcpy+0x20/0x60 [ 46.612323][ T7] kmemdup+0x3e/0x50 [ 46.612987][ T7] brcmf_get_assoc_ies+0x967/0xf60 [ 46.613904][ T7] ? brcmf_notify_vif_event+0x3d0/0x3d0 [ 46.614831][ T7] ? lock_chain_count+0x20/0x20 [ 46.615683][ T7] ? mark_lock.part.0+0xfc/0x2770 [ 46.616552][ T7] ? lock_chain_count+0x20/0x20 [ 46.617409][ T7] ? mark_lock.part.0+0xfc/0x2770 [ 46.618244][ T7] ? lock_chain_count+0x20/0x20 [ 46.619024][ T7] brcmf_bss_connect_done.constprop.0+0x241/0x2e0 [ 46.620019][ T7] ? brcmf_parse_configure_security.isra.0+0x2a0/0x2a0 [ 46.620818][ T7] ? __lock_acquire+0x181f/0x5790 [ 46.621462][ T7] brcmf_notify_connect_status+0x448/0x1950 [ 46.622134][ T7] ? rcu_read_lock_bh_held+0xb0/0xb0 [ 46.622736][ T7] ? brcmf_cfg80211_join_ibss+0x7b0/0x7b0 [ 46.623390][ T7] ? find_held_lock+0x2d/0x110 [ 46.623962][ T7] ? brcmf_fweh_event_worker+0x19f/0xc60 [ 46.624603][ T7] ? mark_held_locks+0x9f/0xe0 [ 46.625145][ T7] ? lockdep_hardirqs_on_prepare+0x3e0/0x3e0 [ 46.625871][ T7] ? brcmf_cfg80211_join_ibss+0x7b0/0x7b0 [ 46.626545][ T7] brcmf_fweh_call_event_handler.isra.0+0x90/0x100 [ 46.627338][ T7] brcmf_fweh_event_worker+0x557/0xc60 [ 46.627962][ T7] ? brcmf_fweh_call_event_handler.isra.0+0x100/0x100 [ 46.628736][ T7] ? rcu_read_lock_sched_held+0xa1/0xd0 [ 46.629396][ T7] ? rcu_read_lock_bh_held+0xb0/0xb0 [ 46.629970][ T7] ? lockdep_hardirqs_on_prepare+0x273/0x3e0 [ 46.630649][ T7] process_one_work+0x92b/0x1460 [ 46.631205][ T7] ? pwq_dec_nr_in_flight+0x330/0x330 [ 46.631821][ T7] ? rwlock_bug.part.0+0x90/0x90 [ 46.632347][ T7] worker_thread+0x95/0xe00 [ 46.632832][ T7] ? __kthread_parkme+0x115/0x1e0 [ 46.633393][ T7] ? process_one_work+0x1460/0x1460 [ 46.633957][ T7] kthread+0x3a1/0x480 [ 46.634369][ T7] ? set_kthread_struct+0x120/0x120 [ 46.634933][ T7] ret_from_fork+0x1f/0x30 [ 46.635431][ T7] [ 46.635687][ T7] Allocated by task 7: [ 46.636151][ T7] kasan_save_stack+0x1b/0x40 [ 46.636628][ T7] __kasan_kmalloc+0x7c/0x90 [ 46.637108][ T7] kmem_cache_alloc_trace+0x19e/0x330 [ 46.637696][ T7] brcmf_cfg80211_attach+0x4a0/0x4040 [ 46.638275][ T7] brcmf_attach+0x389/0xd40 [ 46.638739][ T7] brcmf_usb_probe+0x12de/0x1690 [ 46.639279][ T7] usb_probe_interface+0x2aa/0x760 [ 46.639820][ T7] really_probe+0x205/0xb70 [ 46.640342][ T7] __driver_probe_device+0x311/0x4b0 [ 46.640876][ T7] driver_probe_device+0x4e/0x150 [ 46.641445][ T7] __device_attach_driver+0x1cc/0x2a0 [ 46.642000][ T7] bus_for_each_drv+0x156/0x1d0 [ 46.642543][ T7] __device_attach+0x23f/0x3a0 [ 46.643065][ T7] bus_probe_device+0x1da/0x290 [ 46.643644][ T7] device_add+0xb7b/0x1eb0 [ 46.644130][ T7] usb_set_configuration+0xf59/0x16f0 [ 46.644720][ T7] usb_generic_driver_probe+0x82/0xa0 [ 46.645295][ T7] usb_probe_device+0xbb/0x250 [ 46.645786][ T7] really_probe+0x205/0xb70 [ 46.646258][ T7] __driver_probe_device+0x311/0x4b0 [ 46.646804][ T7] driver_probe_device+0x4e/0x150 [ 46.647387][ T7] __device_attach_driver+0x1cc/0x2a0 [ 46.647926][ T7] bus_for_each_drv+0x156/0x1d0 [ 46.648454][ T7] __device_attach+0x23f/0x3a0 [ 46.648939][ T7] bus_probe_device+0x1da/0x290 [ 46.649478][ T7] device_add+0xb7b/0x1eb0 [ 46.649936][ T7] usb_new_device.cold+0x49c/0x1029 [ 46.650526][ T7] hub_event+0x1c98/0x3950 [ 46.650975][ T7] process_one_work+0x92b/0x1460 [ 46.651535][ T7] worker_thread+0x95/0xe00 [ 46.651991][ T7] kthread+0x3a1/0x480 [ 46.652413][ T7] ret_from_fork+0x1f/0x30 [ 46.652885][ T7] [ 46.653131][ T7] The buggy address belongs to the object at ffff888019442000 [ 46.653131][ T7] which belongs to the cache kmalloc-2k of size 2048 [ 46.654669][ T7] The buggy address is located 0 bytes inside of [ 46.654669][ T7] 2048-byte region [ffff888019442000, ffff888019442800) [ 46.656137][ T7] The buggy address belongs to the page: [ 46.656720][ T7] page:ffffea0000651000 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x19440 [ 46.657792][ T7] head:ffffea0000651000 order:3 compound_mapcount:0 compound_pincount:0 [ 46.658673][ T7] flags: 0x100000000010200(slab|head|node=0|zone=1) [ 46.659422][ T7] raw: 0100000000010200 0000000000000000 dead000000000122 ffff888100042000 [ 46.660363][ T7] raw: 0000000000000000 0000000000080008 00000001ffffffff 0000000000000000 [ 46.661236][ T7] page dumped because: kasan: bad access detected [ 46.661956][ T7] page_owner tracks the page as allocated [ 46.662588][ T7] page last allocated via order 3, migratetype Unmovable, gfp_mask 0x52a20(GFP_ATOMIC|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP), pid 7, ts 31136961085, free_ts 0 [ 46.664271][ T7] prep_new_page+0x1aa/0x240 [ 46.664763][ T7] get_page_from_freelist+0x159a/0x27c0 [ 46.665340][ T7] __alloc_pages+0x2da/0x6a0 [ 46.665847][ T7] alloc_pages+0xec/0x1e0 [ 46.666308][ T7] allocate_slab+0x380/0x4e0 [ 46.666770][ T7] ___slab_alloc+0x5bc/0x940 [ 46.667264][ T7] __slab_alloc+0x6d/0x80 [ 46.667712][ T7] kmem_cache_alloc_trace+0x30a/0x330 [ 46.668299][ T7] brcmf_usbdev_qinit.constprop.0+0x50/0x470 [ 46.668885][ T7] brcmf_usb_probe+0xc97/0x1690 [ 46.669438][ T7] usb_probe_interface+0x2aa/0x760 [ 46.669988][ T7] really_probe+0x205/0xb70 [ 46.670487][ T7] __driver_probe_device+0x311/0x4b0 [ 46.671031][ T7] driver_probe_device+0x4e/0x150 [ 46.671604][ T7] __device_attach_driver+0x1cc/0x2a0 [ 46.672192][ T7] bus_for_each_drv+0x156/0x1d0 [ 46.672739][ T7] page_owner free stack trace missing [ 46.673335][ T7] [ 46.673620][ T7] Memory state around the buggy address: [ 46.674213][ T7] ffff888019442700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 46.675083][ T7] ffff888019442780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 46.675994][ T7] >ffff888019442800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 46.676875][ T7] ^ [ 46.677323][ T7] ffff888019442880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 46.678190][ T7] ffff888019442900: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 46.679052][ T7] ================================================================== [ 46.679945][ T7] Disabling lock debugging due to kernel taint [ 46.680725][ T7] Kernel panic - not syncing: Reviewed-by: Arend van Spriel Signed-off-by: Jisoo Jang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230309104457.22628-1-jisoo.jang@yonsei.ac.kr --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 548799fefb4b..de8a2e27f49c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6280,6 +6280,11 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf; req_len = le32_to_cpu(assoc_info->req_len); resp_len = le32_to_cpu(assoc_info->resp_len); + if (req_len > WL_EXTRA_BUF_MAX || resp_len > WL_EXTRA_BUF_MAX) { + bphy_err(drvr, "invalid lengths in assoc info: req %u resp %u\n", + req_len, resp_len); + return -EINVAL; + } if (req_len) { err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies", cfg->extra_buf, -- cgit v1.2.3 From 504ce971f260c178fa625f1278d9a762bc366504 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:07 -0800 Subject: ice: re-order ice_mbx_reset_snapshot function A future change is going to refactor the VF mailbox overflow detection logic, including modifying ice_mbx_reset_snapshot and its callers. To make this change easier to review, first move the ice_mbx_reset_snapshot function higher in the ice_vf_mbx.c file. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_vf_mbx.c | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c index f56fa94ff3d0..2fe9a9504914 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -130,6 +130,30 @@ u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed) */ #define ICE_IGNORE_MAX_MSG_CNT 0xFFFF +/** + * ice_mbx_reset_snapshot - Reset mailbox snapshot structure + * @snap: pointer to mailbox snapshot structure in the ice_hw struct + * + * Reset the mailbox snapshot structure and clear VF counter array. + */ +static void ice_mbx_reset_snapshot(struct ice_mbx_snapshot *snap) +{ + u32 vfcntr_len; + + if (!snap || !snap->mbx_vf.vf_cntr) + return; + + /* Clear VF counters. */ + vfcntr_len = snap->mbx_vf.vfcntr_len; + if (vfcntr_len) + memset(snap->mbx_vf.vf_cntr, 0, + (vfcntr_len * sizeof(*snap->mbx_vf.vf_cntr))); + + /* Reset mailbox snapshot for a new capture. */ + memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); + snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; +} + /** * ice_mbx_traverse - Pass through mailbox snapshot * @hw: pointer to the HW struct @@ -201,30 +225,6 @@ ice_mbx_detect_malvf(struct ice_hw *hw, u16 vf_id, return 0; } -/** - * ice_mbx_reset_snapshot - Reset mailbox snapshot structure - * @snap: pointer to mailbox snapshot structure in the ice_hw struct - * - * Reset the mailbox snapshot structure and clear VF counter array. - */ -static void ice_mbx_reset_snapshot(struct ice_mbx_snapshot *snap) -{ - u32 vfcntr_len; - - if (!snap || !snap->mbx_vf.vf_cntr) - return; - - /* Clear VF counters. */ - vfcntr_len = snap->mbx_vf.vfcntr_len; - if (vfcntr_len) - memset(snap->mbx_vf.vf_cntr, 0, - (vfcntr_len * sizeof(*snap->mbx_vf.vf_cntr))); - - /* Reset mailbox snapshot for a new capture. */ - memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); - snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; -} - /** * ice_mbx_vf_state_handler - Handle states of the overflow algorithm * @hw: pointer to the HW struct -- cgit v1.2.3 From 28756d9ec93e6588b1c3a00cc9123e238a71c709 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:08 -0800 Subject: ice: convert ice_mbx_clear_malvf to void and use WARN The ice_mbx_clear_malvf function checks for a few error conditions before clearing the appropriate data. These error conditions are really warnings that should never occur in a properly initialized driver. Every caller of ice_mbx_clear_malvf just prints a dev_dbg message on failure which will generally be ignored. Convert this function to void and switch the error return values to WARN_ON. This will make any potentially misconfiguration more visible and makes future refactors that involve changing how we store the malicious VF data easier. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 6 ++---- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 12 ++++-------- drivers/net/ethernet/intel/ice/ice_vf_mbx.c | 16 +++++++--------- drivers/net/ethernet/intel/ice/ice_vf_mbx.h | 2 +- 4 files changed, 14 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 96a64c25e2ef..7107c279752a 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -204,10 +204,8 @@ void ice_free_vfs(struct ice_pf *pf) } /* clear malicious info since the VF is getting released */ - if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, - ICE_MAX_SRIOV_VFS, vf->vf_id)) - dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", - vf->vf_id); + ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, + ICE_MAX_SRIOV_VFS, vf->vf_id); mutex_unlock(&vf->cfg_lock); } diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 0e57bd1b85fd..116b43588389 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -496,10 +496,8 @@ void ice_reset_all_vfs(struct ice_pf *pf) /* clear all malicious info if the VFs are getting reset */ ice_for_each_vf(pf, bkt, vf) - if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, - ICE_MAX_SRIOV_VFS, vf->vf_id)) - dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", - vf->vf_id); + ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, + ICE_MAX_SRIOV_VFS, vf->vf_id); /* If VFs have been disabled, there is no need to reset */ if (test_and_set_bit(ICE_VF_DIS, pf->state)) { @@ -705,10 +703,8 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags) ice_eswitch_replay_vf_mac_rule(vf); /* if the VF has been reset allow it to come up again */ - if (ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, - ICE_MAX_SRIOV_VFS, vf->vf_id)) - dev_dbg(dev, "failed to clear malicious VF state for VF %u\n", - vf->vf_id); + ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, + ICE_MAX_SRIOV_VFS, vf->vf_id); out_unlock: if (flags & ICE_VF_RESET_LOCK) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c index 2fe9a9504914..9f6acfeb0fc6 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -392,19 +392,19 @@ ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, * that the new VF loaded is not considered malicious before going * through the overflow detection algorithm. */ -int +void ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, u16 bitmap_len, u16 vf_id) { - if (!snap || !all_malvfs) - return -EINVAL; + if (WARN_ON(!snap || !all_malvfs)) + return; - if (bitmap_len < snap->mbx_vf.vfcntr_len) - return -EINVAL; + if (WARN_ON(bitmap_len < snap->mbx_vf.vfcntr_len)) + return; /* Ensure VF ID value is not larger than bitmap or VF counter length */ - if (vf_id >= bitmap_len || vf_id >= snap->mbx_vf.vfcntr_len) - return -EIO; + if (WARN_ON(vf_id >= bitmap_len || vf_id >= snap->mbx_vf.vfcntr_len)) + return; /* Clear VF ID bit in the bitmap tracking malicious VFs attached to PF */ clear_bit(vf_id, all_malvfs); @@ -416,8 +416,6 @@ ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, * values in the mailbox overflow detection algorithm. */ snap->mbx_vf.vf_cntr[vf_id] = 0; - - return 0; } /** diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h index 582716e6d5f9..be593b951642 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h @@ -22,7 +22,7 @@ u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed); int ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, u16 vf_id, bool *is_mal_vf); -int +void ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, u16 bitmap_len, u16 vf_id); int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count); -- cgit v1.2.3 From e4eaf8938852d092fa447b32adb8ec233621d86a Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:09 -0800 Subject: ice: track malicious VFs in new ice_mbx_vf_info structure Currently the PF tracks malicious VFs in a malvfs bitmap which is used by the ice_mbx_clear_malvf and ice_mbx_report_malvf functions. This bitmap is used to ensure that we only report a VF as malicious once rather than continuously spamming the event log. This mechanism of storage for the malicious indication works well enough for SR-IOV. However, it will not work with Scalable IOV. This is because Scalable IOV VFs can be allocated dynamically and might change VF ID when their underlying VSI changes. To support this, the mailbox overflow logic will need to be refactored. First, introduce a new ice_mbx_vf_info structure which will be used to store data about a VF. Embed this structure in the struct ice_vf, and ensure it gets initialized when a new VF is created. For now this only stores the malicious indicator bit. Pass a pointer to the VF's mbx_info structure instead of using a bitmap to keep track of these bits. A future change will extend this structure and the rest of the logic associated with the overflow detection. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 7 ++- drivers/net/ethernet/intel/ice/ice_type.h | 7 +++ drivers/net/ethernet/intel/ice/ice_vf_lib.c | 10 ++-- drivers/net/ethernet/intel/ice/ice_vf_lib.h | 2 +- drivers/net/ethernet/intel/ice/ice_vf_mbx.c | 71 +++++++++++++---------------- drivers/net/ethernet/intel/ice/ice_vf_mbx.h | 9 ++-- 6 files changed, 53 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 7107c279752a..44b94276df91 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -204,8 +204,8 @@ void ice_free_vfs(struct ice_pf *pf) } /* clear malicious info since the VF is getting released */ - ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, - ICE_MAX_SRIOV_VFS, vf->vf_id); + ice_mbx_clear_malvf(&hw->mbx_snapshot, vf->vf_id, + &vf->mbx_info); mutex_unlock(&vf->cfg_lock); } @@ -1828,8 +1828,7 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, /* if the VF is malicious and we haven't let the user * know about it, then let them know now */ - status = ice_mbx_report_malvf(&pf->hw, pf->vfs.malvfs, - ICE_MAX_SRIOV_VFS, vf_id, + status = ice_mbx_report_malvf(&pf->hw, &vf->mbx_info, &report_vf); if (status) dev_dbg(dev, "Error reporting malicious VF\n"); diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index e3f622cad425..d243a0c59ea4 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -794,6 +794,13 @@ struct ice_mbx_vf_counter { u32 vfcntr_len; }; +/* Structure used to track a single VF's messages on the mailbox: + * 1. malicious: whether this VF has been detected as malicious before + */ +struct ice_mbx_vf_info { + u8 malicious : 1; +}; + /* Structure to hold data relevant to the captured static snapshot * of the PF-VF mailbox. */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 116b43588389..69e89e960950 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -496,8 +496,8 @@ void ice_reset_all_vfs(struct ice_pf *pf) /* clear all malicious info if the VFs are getting reset */ ice_for_each_vf(pf, bkt, vf) - ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, - ICE_MAX_SRIOV_VFS, vf->vf_id); + ice_mbx_clear_malvf(&hw->mbx_snapshot, vf->vf_id, + &vf->mbx_info); /* If VFs have been disabled, there is no need to reset */ if (test_and_set_bit(ICE_VF_DIS, pf->state)) { @@ -703,8 +703,7 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags) ice_eswitch_replay_vf_mac_rule(vf); /* if the VF has been reset allow it to come up again */ - ice_mbx_clear_malvf(&hw->mbx_snapshot, pf->vfs.malvfs, - ICE_MAX_SRIOV_VFS, vf->vf_id); + ice_mbx_clear_malvf(&hw->mbx_snapshot, vf->vf_id, &vf->mbx_info); out_unlock: if (flags & ICE_VF_RESET_LOCK) @@ -760,6 +759,9 @@ void ice_initialize_vf_entry(struct ice_vf *vf) ice_vf_ctrl_invalidate_vsi(vf); ice_vf_fdir_init(vf); + /* Initialize mailbox info for this VF */ + ice_mbx_init_vf_info(&pf->hw, &vf->mbx_info); + mutex_init(&vf->cfg_lock); } diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h index ef30f05b5d02..e3cda6fb71ab 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h @@ -74,7 +74,6 @@ struct ice_vfs { u16 num_qps_per; /* number of queue pairs per VF */ u16 num_msix_per; /* number of MSI-X vectors per VF */ unsigned long last_printed_mdd_jiffies; /* MDD message rate limit */ - DECLARE_BITMAP(malvfs, ICE_MAX_SRIOV_VFS); /* malicious VF indicator */ }; /* VF information structure */ @@ -105,6 +104,7 @@ struct ice_vf { DECLARE_BITMAP(rxq_ena, ICE_MAX_RSS_QS_PER_VF); struct ice_vlan port_vlan_info; /* Port VLAN ID, QoS, and TPID */ struct virtchnl_vlan_caps vlan_v2_caps; + struct ice_mbx_vf_info mbx_info; u8 pf_set_mac:1; /* VF MAC address set by VMM admin */ u8 trusted:1; u8 spoofchk:1; diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c index 9f6acfeb0fc6..2e769bd0bf7e 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -345,35 +345,23 @@ ice_mbx_vf_state_handler(struct ice_hw *hw, /** * ice_mbx_report_malvf - Track and note malicious VF * @hw: pointer to the HW struct - * @all_malvfs: all malicious VFs tracked by PF - * @bitmap_len: length of bitmap in bits - * @vf_id: relative virtual function ID of the malicious VF + * @vf_info: the mailbox tracking info structure for a VF * @report_malvf: boolean to indicate if malicious VF must be reported * - * This function will update a bitmap that keeps track of the malicious - * VFs attached to the PF. A malicious VF must be reported only once if - * discovered between VF resets or loading so the function checks - * the input vf_id against the bitmap to verify if the VF has been - * detected in any previous mailbox iterations. + * This function updates the malicious indicator bit in the VF mailbox + * tracking structure. A malicious VF must be reported only once if discovered + * between VF resets or loading so the function first checks if the VF has + * already been detected in any previous mailbox iterations. */ int -ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id, bool *report_malvf) +ice_mbx_report_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, + bool *report_malvf) { - if (!all_malvfs || !report_malvf) - return -EINVAL; - - *report_malvf = false; - - if (bitmap_len < hw->mbx_snapshot.mbx_vf.vfcntr_len) + if (!report_malvf) return -EINVAL; - if (vf_id >= bitmap_len) - return -EIO; - - /* If the vf_id is found in the bitmap set bit and boolean to true */ - if (!test_and_set_bit(vf_id, all_malvfs)) - *report_malvf = true; + *report_malvf = !vf_info->malicious; + vf_info->malicious = 1; return 0; } @@ -381,33 +369,24 @@ ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, /** * ice_mbx_clear_malvf - Clear VF bitmap and counter for VF ID * @snap: pointer to the mailbox snapshot structure - * @all_malvfs: all malicious VFs tracked by PF - * @bitmap_len: length of bitmap in bits * @vf_id: relative virtual function ID of the malicious VF + * @vf_info: mailbox tracking structure for this VF * - * In case of a VF reset, this function can be called to clear - * the bit corresponding to the VF ID in the bitmap tracking all - * malicious VFs attached to the PF. The function also clears the - * VF counter array at the index of the VF ID. This is to ensure - * that the new VF loaded is not considered malicious before going - * through the overflow detection algorithm. - */ +* In case of a VF reset, this function shall be called to clear the VF's +* current mailbox tracking state. +*/ void -ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id) +ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, u16 vf_id, + struct ice_mbx_vf_info *vf_info) { - if (WARN_ON(!snap || !all_malvfs)) - return; - - if (WARN_ON(bitmap_len < snap->mbx_vf.vfcntr_len)) + if (WARN_ON(!snap)) return; /* Ensure VF ID value is not larger than bitmap or VF counter length */ - if (WARN_ON(vf_id >= bitmap_len || vf_id >= snap->mbx_vf.vfcntr_len)) + if (WARN_ON(vf_id >= snap->mbx_vf.vfcntr_len)) return; - /* Clear VF ID bit in the bitmap tracking malicious VFs attached to PF */ - clear_bit(vf_id, all_malvfs); + vf_info->malicious = 0; /* Clear the VF counter in the mailbox snapshot structure for that VF ID. * This is to ensure that if a VF is unloaded and a new one brought back @@ -418,6 +397,18 @@ ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, snap->mbx_vf.vf_cntr[vf_id] = 0; } +/** + * ice_mbx_init_vf_info - Initialize a new VF mailbox tracking info + * @hw: pointer to the hardware structure + * @vf_info: the mailbox tracking info structure for a VF + * + * Initialize a VF mailbox tracking info structure. + */ +void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info) +{ + vf_info->malicious = 0; +} + /** * ice_mbx_init_snapshot - Initialize mailbox snapshot structure * @hw: pointer to the hardware structure diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h index be593b951642..2613cba61ac7 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h @@ -23,13 +23,14 @@ int ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, u16 vf_id, bool *is_mal_vf); void -ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id); +ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, u16 vf_id, + struct ice_mbx_vf_info *vf_info); +void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info); int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count); void ice_mbx_deinit_snapshot(struct ice_hw *hw); int -ice_mbx_report_malvf(struct ice_hw *hw, unsigned long *all_malvfs, - u16 bitmap_len, u16 vf_id, bool *report_malvf); +ice_mbx_report_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, + bool *report_malvf); #else /* CONFIG_PCI_IOV */ static inline int ice_aq_send_msg_to_vf(struct ice_hw __always_unused *hw, -- cgit v1.2.3 From 8cd8a6b17d275a45e3722d0215f6115b687c8c3e Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:10 -0800 Subject: ice: move VF overflow message count into struct ice_mbx_vf_info The ice driver has some logic in ice_vf_mbx.c used to detect potentially malicious VF behavior with regards to overflowing the PF mailbox. This logic currently stores message counts in struct ice_mbx_vf_counter.vf_cntr as an array. This array is allocated during initialization with ice_mbx_init_snapshot. This logic makes sense for SR-IOV where all VFs are allocated at once up front. However, in the future with Scalable IOV this logic will not work. VFs can be added and removed dynamically. We could try to keep the vf_cntr array for the maximum possible number of VFs, but this is a waste of memory. Use the recently introduced struct ice_mbx_vf_info structure to store the message count. Pass a pointer to the mbx_info for a VF instead of using its VF ID. Replace the array of VF message counts with a linked list that tracks all currently active mailbox tracking info structures. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 9 +- drivers/net/ethernet/intel/ice/ice_type.h | 18 +-- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 7 +- drivers/net/ethernet/intel/ice/ice_vf_mbx.c | 167 +++++++++------------------- drivers/net/ethernet/intel/ice/ice_vf_mbx.h | 8 +- 5 files changed, 69 insertions(+), 140 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 44b94276df91..8820f269bfdf 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -204,8 +204,7 @@ void ice_free_vfs(struct ice_pf *pf) } /* clear malicious info since the VF is getting released */ - ice_mbx_clear_malvf(&hw->mbx_snapshot, vf->vf_id, - &vf->mbx_info); + list_del(&vf->mbx_info.list_entry); mutex_unlock(&vf->cfg_lock); } @@ -1025,9 +1024,7 @@ int ice_sriov_configure(struct pci_dev *pdev, int num_vfs) return -EBUSY; } - err = ice_mbx_init_snapshot(&pf->hw, num_vfs); - if (err) - return err; + ice_mbx_init_snapshot(&pf->hw); err = ice_pci_sriov_ena(pf, num_vfs); if (err) { @@ -1818,7 +1815,7 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, mbxdata.async_watermark_val = ICE_MBX_OVERFLOW_WATERMARK; /* check to see if we have a malicious VF */ - status = ice_mbx_vf_state_handler(&pf->hw, &mbxdata, vf_id, &malvf); + status = ice_mbx_vf_state_handler(&pf->hw, &mbxdata, &vf->mbx_info, &malvf); if (status) goto out_put_vf; diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index d243a0c59ea4..a09556e57803 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -784,20 +784,14 @@ struct ice_mbx_snap_buffer_data { u16 max_num_msgs_mbx; }; -/* Structure to track messages sent by VFs on mailbox: - * 1. vf_cntr: a counter array of VFs to track the number of - * asynchronous messages sent by each VF - * 2. vfcntr_len: number of entries in VF counter array - */ -struct ice_mbx_vf_counter { - u32 *vf_cntr; - u32 vfcntr_len; -}; - /* Structure used to track a single VF's messages on the mailbox: - * 1. malicious: whether this VF has been detected as malicious before + * 1. list_entry: linked list entry node + * 2. msg_count: the number of asynchronous messages sent by this VF + * 3. malicious: whether this VF has been detected as malicious before */ struct ice_mbx_vf_info { + struct list_head list_entry; + u32 msg_count; u8 malicious : 1; }; @@ -806,7 +800,7 @@ struct ice_mbx_vf_info { */ struct ice_mbx_snapshot { struct ice_mbx_snap_buffer_data mbx_buf; - struct ice_mbx_vf_counter mbx_vf; + struct list_head mbx_vf; }; /* Structure to hold data to be used for capturing or updating a diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 69e89e960950..89fd6982df09 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -496,8 +496,7 @@ void ice_reset_all_vfs(struct ice_pf *pf) /* clear all malicious info if the VFs are getting reset */ ice_for_each_vf(pf, bkt, vf) - ice_mbx_clear_malvf(&hw->mbx_snapshot, vf->vf_id, - &vf->mbx_info); + ice_mbx_clear_malvf(&vf->mbx_info); /* If VFs have been disabled, there is no need to reset */ if (test_and_set_bit(ICE_VF_DIS, pf->state)) { @@ -599,12 +598,10 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags) struct ice_pf *pf = vf->pf; struct ice_vsi *vsi; struct device *dev; - struct ice_hw *hw; int err = 0; bool rsd; dev = ice_pf_to_dev(pf); - hw = &pf->hw; if (flags & ICE_VF_RESET_NOTIFY) ice_notify_vf_reset(vf); @@ -703,7 +700,7 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags) ice_eswitch_replay_vf_mac_rule(vf); /* if the VF has been reset allow it to come up again */ - ice_mbx_clear_malvf(&hw->mbx_snapshot, vf->vf_id, &vf->mbx_info); + ice_mbx_clear_malvf(&vf->mbx_info); out_unlock: if (flags & ICE_VF_RESET_LOCK) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c index 2e769bd0bf7e..4bfed5fb3a88 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -93,36 +93,31 @@ u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed) * * 2. When the caller starts processing its mailbox queue in response to an * interrupt, the structure ice_mbx_snapshot is expected to be cleared before - * the algorithm can be run for the first time for that interrupt. This can be - * done via ice_mbx_reset_snapshot(). + * the algorithm can be run for the first time for that interrupt. This + * requires calling ice_mbx_reset_snapshot() as well as calling + * ice_mbx_reset_vf_info() for each VF tracking structure. * * 3. For every message read by the caller from the MBX Queue, the caller must * call the detection algorithm's entry function ice_mbx_vf_state_handler(). * Before every call to ice_mbx_vf_state_handler() the struct ice_mbx_data is * filled as it is required to be passed to the algorithm. * - * 4. Every time a message is read from the MBX queue, a VFId is received which - * is passed to the state handler. The boolean output is_malvf of the state - * handler ice_mbx_vf_state_handler() serves as an indicator to the caller - * whether this VF is malicious or not. + * 4. Every time a message is read from the MBX queue, a tracking structure + * for the VF must be passed to the state handler. The boolean output + * report_malvf from ice_mbx_vf_state_handler() serves as an indicator to the + * caller whether it must report this VF as malicious or not. * * 5. When a VF is identified to be malicious, the caller can send a message - * to the system administrator. The caller can invoke ice_mbx_report_malvf() - * to help determine if a malicious VF is to be reported or not. This function - * requires the caller to maintain a global bitmap to track all malicious VFs - * and pass that to ice_mbx_report_malvf() along with the VFID which was identified - * to be malicious by ice_mbx_vf_state_handler(). + * to the system administrator. * - * 6. The global bitmap maintained by PF can be cleared completely if PF is in - * reset or the bit corresponding to a VF can be cleared if that VF is in reset. - * When a VF is shut down and brought back up, we assume that the new VF - * brought up is not malicious and hence report it if found malicious. + * 6. The PF is responsible for maintaining the struct ice_mbx_vf_info + * structure for each VF. The PF should clear the VF tracking structure if the + * VF is reset. When a VF is shut down and brought back up, we will then + * assume that the new VF is not malicious and may report it again if we + * detect it again. * * 7. The function ice_mbx_reset_snapshot() is called to reset the information * in ice_mbx_snapshot for every new mailbox interrupt handled. - * - * 8. The memory allocated for variables in ice_mbx_snapshot is de-allocated - * when driver is unloaded. */ #define ICE_RQ_DATA_MASK(rq_data) ((rq_data) & PF_MBX_ARQH_ARQH_M) /* Using the highest value for an unsigned 16-bit value 0xFFFF to indicate that @@ -132,26 +127,21 @@ u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed) /** * ice_mbx_reset_snapshot - Reset mailbox snapshot structure - * @snap: pointer to mailbox snapshot structure in the ice_hw struct - * - * Reset the mailbox snapshot structure and clear VF counter array. + * @snap: pointer to the mailbox snapshot */ static void ice_mbx_reset_snapshot(struct ice_mbx_snapshot *snap) { - u32 vfcntr_len; - - if (!snap || !snap->mbx_vf.vf_cntr) - return; + struct ice_mbx_vf_info *vf_info; - /* Clear VF counters. */ - vfcntr_len = snap->mbx_vf.vfcntr_len; - if (vfcntr_len) - memset(snap->mbx_vf.vf_cntr, 0, - (vfcntr_len * sizeof(*snap->mbx_vf.vf_cntr))); - - /* Reset mailbox snapshot for a new capture. */ + /* Clear mbx_buf in the mailbox snaphot structure and setting the + * mailbox snapshot state to a new capture. + */ memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; + + /* Reset message counts for all VFs to zero */ + list_for_each_entry(vf_info, &snap->mbx_vf, list_entry) + vf_info->msg_count = 0; } /** @@ -195,7 +185,7 @@ ice_mbx_traverse(struct ice_hw *hw, /** * ice_mbx_detect_malvf - Detect malicious VF in snapshot * @hw: pointer to the HW struct - * @vf_id: relative virtual function ID + * @vf_info: mailbox tracking structure for a VF * @new_state: new algorithm state * @is_malvf: boolean output to indicate if VF is malicious * @@ -204,19 +194,14 @@ ice_mbx_traverse(struct ice_hw *hw, * the permissible number of messages to send. */ static int -ice_mbx_detect_malvf(struct ice_hw *hw, u16 vf_id, +ice_mbx_detect_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, enum ice_mbx_snapshot_state *new_state, bool *is_malvf) { - struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; + /* increment the message count for this VF */ + vf_info->msg_count++; - if (vf_id >= snap->mbx_vf.vfcntr_len) - return -EIO; - - /* increment the message count in the VF array */ - snap->mbx_vf.vf_cntr[vf_id]++; - - if (snap->mbx_vf.vf_cntr[vf_id] >= ICE_ASYNC_VF_MSG_THRESHOLD) + if (vf_info->msg_count >= ICE_ASYNC_VF_MSG_THRESHOLD) *is_malvf = true; /* continue to iterate through the mailbox snapshot */ @@ -229,7 +214,7 @@ ice_mbx_detect_malvf(struct ice_hw *hw, u16 vf_id, * ice_mbx_vf_state_handler - Handle states of the overflow algorithm * @hw: pointer to the HW struct * @mbx_data: pointer to structure containing mailbox data - * @vf_id: relative virtual function (VF) ID + * @vf_info: mailbox tracking structure for the VF in question * @is_malvf: boolean output to indicate if VF is malicious * * The function serves as an entry point for the malicious VF @@ -250,7 +235,8 @@ ice_mbx_detect_malvf(struct ice_hw *hw, u16 vf_id, */ int ice_mbx_vf_state_handler(struct ice_hw *hw, - struct ice_mbx_data *mbx_data, u16 vf_id, + struct ice_mbx_data *mbx_data, + struct ice_mbx_vf_info *vf_info, bool *is_malvf) { struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; @@ -315,7 +301,8 @@ ice_mbx_vf_state_handler(struct ice_hw *hw, if (snap_buf->num_pending_arq >= mbx_data->async_watermark_val) { new_state = ICE_MAL_VF_DETECT_STATE_DETECT; - status = ice_mbx_detect_malvf(hw, vf_id, &new_state, is_malvf); + status = ice_mbx_detect_malvf(hw, vf_info, &new_state, + is_malvf); } else { new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE; ice_mbx_traverse(hw, &new_state); @@ -329,7 +316,8 @@ ice_mbx_vf_state_handler(struct ice_hw *hw, case ICE_MAL_VF_DETECT_STATE_DETECT: new_state = ICE_MAL_VF_DETECT_STATE_DETECT; - status = ice_mbx_detect_malvf(hw, vf_id, &new_state, is_malvf); + status = ice_mbx_detect_malvf(hw, vf_info, &new_state, + is_malvf); break; default: @@ -367,34 +355,16 @@ ice_mbx_report_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, } /** - * ice_mbx_clear_malvf - Clear VF bitmap and counter for VF ID - * @snap: pointer to the mailbox snapshot structure - * @vf_id: relative virtual function ID of the malicious VF - * @vf_info: mailbox tracking structure for this VF + * ice_mbx_clear_malvf - Clear VF mailbox info + * @vf_info: the mailbox tracking structure for a VF * -* In case of a VF reset, this function shall be called to clear the VF's -* current mailbox tracking state. -*/ -void -ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, u16 vf_id, - struct ice_mbx_vf_info *vf_info) + * In case of a VF reset, this function shall be called to clear the VF's + * current mailbox tracking state. + */ +void ice_mbx_clear_malvf(struct ice_mbx_vf_info *vf_info) { - if (WARN_ON(!snap)) - return; - - /* Ensure VF ID value is not larger than bitmap or VF counter length */ - if (WARN_ON(vf_id >= snap->mbx_vf.vfcntr_len)) - return; - vf_info->malicious = 0; - - /* Clear the VF counter in the mailbox snapshot structure for that VF ID. - * This is to ensure that if a VF is unloaded and a new one brought back - * up with the same VF ID for a snapshot currently in traversal or detect - * state the counter for that VF ID does not increment on top of existing - * values in the mailbox overflow detection algorithm. - */ - snap->mbx_vf.vf_cntr[vf_id] = 0; + vf_info->msg_count = 0; } /** @@ -402,55 +372,32 @@ ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, u16 vf_id, * @hw: pointer to the hardware structure * @vf_info: the mailbox tracking info structure for a VF * - * Initialize a VF mailbox tracking info structure. + * Initialize a VF mailbox tracking info structure and insert it into the + * snapshot list. + * + * If you remove the VF, you must also delete the associated VF info structure + * from the linked list. */ void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info) { - vf_info->malicious = 0; + struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; + + ice_mbx_clear_malvf(vf_info); + list_add(&vf_info->list_entry, &snap->mbx_vf); } /** - * ice_mbx_init_snapshot - Initialize mailbox snapshot structure + * ice_mbx_init_snapshot - Initialize mailbox snapshot data * @hw: pointer to the hardware structure - * @vf_count: number of VFs allocated on a PF - * - * Clear the mailbox snapshot structure and allocate memory - * for the VF counter array based on the number of VFs allocated - * on that PF. * - * Assumption: This function will assume ice_get_caps() has already been - * called to ensure that the vf_count can be compared against the number - * of VFs supported as defined in the functional capabilities of the device. + * Clear the mailbox snapshot structure and initialize the VF mailbox list. */ -int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count) +void ice_mbx_init_snapshot(struct ice_hw *hw) { struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; - /* Ensure that the number of VFs allocated is non-zero and - * is not greater than the number of supported VFs defined in - * the functional capabilities of the PF. - */ - if (!vf_count || vf_count > hw->func_caps.num_allocd_vfs) - return -EINVAL; - - snap->mbx_vf.vf_cntr = devm_kcalloc(ice_hw_to_dev(hw), vf_count, - sizeof(*snap->mbx_vf.vf_cntr), - GFP_KERNEL); - if (!snap->mbx_vf.vf_cntr) - return -ENOMEM; - - /* Setting the VF counter length to the number of allocated - * VFs for given PF's functional capabilities. - */ - snap->mbx_vf.vfcntr_len = vf_count; - - /* Clear mbx_buf in the mailbox snaphot structure and setting the - * mailbox snapshot state to a new capture. - */ - memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); - snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT; - - return 0; + INIT_LIST_HEAD(&snap->mbx_vf); + ice_mbx_reset_snapshot(snap); } /** @@ -463,10 +410,6 @@ void ice_mbx_deinit_snapshot(struct ice_hw *hw) { struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; - /* Free VF counter array and reset VF counter length */ - devm_kfree(ice_hw_to_dev(hw), snap->mbx_vf.vf_cntr); - snap->mbx_vf.vfcntr_len = 0; - /* Clear mbx_buf in the mailbox snaphot structure */ memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); } diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h index 2613cba61ac7..a6d42f467dc5 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h @@ -21,12 +21,10 @@ ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval, u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed); int ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, - u16 vf_id, bool *is_mal_vf); -void -ice_mbx_clear_malvf(struct ice_mbx_snapshot *snap, u16 vf_id, - struct ice_mbx_vf_info *vf_info); + struct ice_mbx_vf_info *vf_info, bool *is_mal_vf); +void ice_mbx_clear_malvf(struct ice_mbx_vf_info *vf_info); void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info); -int ice_mbx_init_snapshot(struct ice_hw *hw, u16 vf_count); +void ice_mbx_init_snapshot(struct ice_hw *hw); void ice_mbx_deinit_snapshot(struct ice_hw *hw); int ice_mbx_report_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, -- cgit v1.2.3 From 4bdf5f258331f049bbff2d770cfcb62f6b789dfe Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:11 -0800 Subject: ice: remove ice_mbx_deinit_snapshot The ice_mbx_deinit_snapshot function's only remaining job is to clear the previous snapshot data. This snapshot data is initialized when SR-IOV adds VFs, so it is not necessary to clear this data when removing VFs. Since no allocation occurs we no longer need to free anything and we can safely remove this function. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 5 +---- drivers/net/ethernet/intel/ice/ice_vf_mbx.c | 14 -------------- drivers/net/ethernet/intel/ice/ice_vf_mbx.h | 1 - 3 files changed, 1 insertion(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 8820f269bfdf..b65025b51526 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1014,7 +1014,6 @@ int ice_sriov_configure(struct pci_dev *pdev, int num_vfs) if (!num_vfs) { if (!pci_vfs_assigned(pdev)) { ice_free_vfs(pf); - ice_mbx_deinit_snapshot(&pf->hw); if (pf->lag) ice_enable_lag(pf->lag); return 0; @@ -1027,10 +1026,8 @@ int ice_sriov_configure(struct pci_dev *pdev, int num_vfs) ice_mbx_init_snapshot(&pf->hw); err = ice_pci_sriov_ena(pf, num_vfs); - if (err) { - ice_mbx_deinit_snapshot(&pf->hw); + if (err) return err; - } if (pf->lag) ice_disable_lag(pf->lag); diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c index 4bfed5fb3a88..1f332ab43b00 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -399,17 +399,3 @@ void ice_mbx_init_snapshot(struct ice_hw *hw) INIT_LIST_HEAD(&snap->mbx_vf); ice_mbx_reset_snapshot(snap); } - -/** - * ice_mbx_deinit_snapshot - Free mailbox snapshot structure - * @hw: pointer to the hardware structure - * - * Clear the mailbox snapshot structure and free the VF counter array. - */ -void ice_mbx_deinit_snapshot(struct ice_hw *hw) -{ - struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; - - /* Clear mbx_buf in the mailbox snaphot structure */ - memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf)); -} diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h index a6d42f467dc5..e4bdd93ccef1 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h @@ -25,7 +25,6 @@ ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, void ice_mbx_clear_malvf(struct ice_mbx_vf_info *vf_info); void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info); void ice_mbx_init_snapshot(struct ice_hw *hw); -void ice_mbx_deinit_snapshot(struct ice_hw *hw); int ice_mbx_report_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, bool *report_malvf); -- cgit v1.2.3 From 07cc1a942216d1f211f1c641af8b6f810bb16699 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:12 -0800 Subject: ice: merge ice_mbx_report_malvf with ice_mbx_vf_state_handler The ice_mbx_report_malvf function is used to update the ice_mbx_vf_info.malicious member after we detect a malicious VF. This is done by calling ice_mbx_report_malvf after ice_mbx_vf_state_handler sets its "is_malvf" return parameter true. Instead of requiring two steps, directly update the malicious bit in the state handler, and remove the need for separately calling ice_mbx_report_malvf. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 34 +++++++------------ drivers/net/ethernet/intel/ice/ice_vf_mbx.c | 51 +++++++++-------------------- drivers/net/ethernet/intel/ice/ice_vf_mbx.h | 5 +-- 3 files changed, 28 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index b65025b51526..6152c90d7286 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1794,7 +1794,7 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, s16 vf_id = le16_to_cpu(event->desc.retval); struct device *dev = ice_pf_to_dev(pf); struct ice_mbx_data mbxdata; - bool malvf = false; + bool report_malvf = false; struct ice_vf *vf; int status; @@ -1811,33 +1811,23 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, #define ICE_MBX_OVERFLOW_WATERMARK 64 mbxdata.async_watermark_val = ICE_MBX_OVERFLOW_WATERMARK; - /* check to see if we have a malicious VF */ - status = ice_mbx_vf_state_handler(&pf->hw, &mbxdata, &vf->mbx_info, &malvf); + /* check to see if we have a newly malicious VF */ + status = ice_mbx_vf_state_handler(&pf->hw, &mbxdata, &vf->mbx_info, + &report_malvf); if (status) goto out_put_vf; - if (malvf) { - bool report_vf = false; + if (report_malvf) { + struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); - /* if the VF is malicious and we haven't let the user - * know about it, then let them know now - */ - status = ice_mbx_report_malvf(&pf->hw, &vf->mbx_info, - &report_vf); - if (status) - dev_dbg(dev, "Error reporting malicious VF\n"); - - if (report_vf) { - struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); - - if (pf_vsi) - dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", - &vf->dev_lan_addr[0], - pf_vsi->netdev->dev_addr); - } + if (pf_vsi) + dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", + &vf->dev_lan_addr[0], + pf_vsi->netdev->dev_addr); } out_put_vf: ice_put_vf(vf); - return malvf; + + return vf->mbx_info.malicious; } diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c index 1f332ab43b00..40cb4ba0789c 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c @@ -215,7 +215,7 @@ ice_mbx_detect_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, * @hw: pointer to the HW struct * @mbx_data: pointer to structure containing mailbox data * @vf_info: mailbox tracking structure for the VF in question - * @is_malvf: boolean output to indicate if VF is malicious + * @report_malvf: boolean output to indicate whether VF should be reported * * The function serves as an entry point for the malicious VF * detection algorithm by handling the different states and state @@ -234,25 +234,24 @@ ice_mbx_detect_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, * the static snapshot and look for a malicious VF. */ int -ice_mbx_vf_state_handler(struct ice_hw *hw, - struct ice_mbx_data *mbx_data, - struct ice_mbx_vf_info *vf_info, - bool *is_malvf) +ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, + struct ice_mbx_vf_info *vf_info, bool *report_malvf) { struct ice_mbx_snapshot *snap = &hw->mbx_snapshot; struct ice_mbx_snap_buffer_data *snap_buf; struct ice_ctl_q_info *cq = &hw->mailboxq; enum ice_mbx_snapshot_state new_state; + bool is_malvf = false; int status = 0; - if (!is_malvf || !mbx_data) + if (!report_malvf || !mbx_data || !vf_info) return -EINVAL; + *report_malvf = false; + /* When entering the mailbox state machine assume that the VF * is not malicious until detected. */ - *is_malvf = false; - /* Checking if max messages allowed to be processed while servicing current * interrupt is not less than the defined AVF message threshold. */ @@ -301,8 +300,7 @@ ice_mbx_vf_state_handler(struct ice_hw *hw, if (snap_buf->num_pending_arq >= mbx_data->async_watermark_val) { new_state = ICE_MAL_VF_DETECT_STATE_DETECT; - status = ice_mbx_detect_malvf(hw, vf_info, &new_state, - is_malvf); + status = ice_mbx_detect_malvf(hw, vf_info, &new_state, &is_malvf); } else { new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE; ice_mbx_traverse(hw, &new_state); @@ -316,8 +314,7 @@ ice_mbx_vf_state_handler(struct ice_hw *hw, case ICE_MAL_VF_DETECT_STATE_DETECT: new_state = ICE_MAL_VF_DETECT_STATE_DETECT; - status = ice_mbx_detect_malvf(hw, vf_info, &new_state, - is_malvf); + status = ice_mbx_detect_malvf(hw, vf_info, &new_state, &is_malvf); break; default: @@ -327,31 +324,13 @@ ice_mbx_vf_state_handler(struct ice_hw *hw, snap_buf->state = new_state; - return status; -} - -/** - * ice_mbx_report_malvf - Track and note malicious VF - * @hw: pointer to the HW struct - * @vf_info: the mailbox tracking info structure for a VF - * @report_malvf: boolean to indicate if malicious VF must be reported - * - * This function updates the malicious indicator bit in the VF mailbox - * tracking structure. A malicious VF must be reported only once if discovered - * between VF resets or loading so the function first checks if the VF has - * already been detected in any previous mailbox iterations. - */ -int -ice_mbx_report_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, - bool *report_malvf) -{ - if (!report_malvf) - return -EINVAL; - - *report_malvf = !vf_info->malicious; - vf_info->malicious = 1; + /* Only report VFs as malicious the first time we detect it */ + if (is_malvf && !vf_info->malicious) { + vf_info->malicious = 1; + *report_malvf = true; + } - return 0; + return status; } /** diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h index e4bdd93ccef1..41250519bc56 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h @@ -21,13 +21,10 @@ ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval, u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed); int ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data, - struct ice_mbx_vf_info *vf_info, bool *is_mal_vf); + struct ice_mbx_vf_info *vf_info, bool *report_malvf); void ice_mbx_clear_malvf(struct ice_mbx_vf_info *vf_info); void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info); void ice_mbx_init_snapshot(struct ice_hw *hw); -int -ice_mbx_report_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info, - bool *report_malvf); #else /* CONFIG_PCI_IOV */ static inline int ice_aq_send_msg_to_vf(struct ice_hw __always_unused *hw, -- cgit v1.2.3 From dde7db637d9981b47da0da575661d0ec83f8b25a Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:13 -0800 Subject: ice: initialize mailbox snapshot earlier in PF init Now that we no longer depend on the number of VFs being allocated, we can move the ice_mbx_init_snapshot function earlier. This will be required by Scalable IOV as we will not be calling ice_sriov_configure for Scalable VFs. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_main.c | 1 + drivers/net/ethernet/intel/ice/ice_sriov.c | 2 -- drivers/net/ethernet/intel/ice/ice_vf_mbx.h | 4 ++++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 567694bf098b..615a731d7afe 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -3891,6 +3891,7 @@ static int ice_init_pf(struct ice_pf *pf) mutex_init(&pf->vfs.table_lock); hash_init(pf->vfs.table); + ice_mbx_init_snapshot(&pf->hw); return 0; } diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 6152c90d7286..79159cbb66ec 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1023,8 +1023,6 @@ int ice_sriov_configure(struct pci_dev *pdev, int num_vfs) return -EBUSY; } - ice_mbx_init_snapshot(&pf->hw); - err = ice_pci_sriov_ena(pf, num_vfs); if (err) return err; diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h index 41250519bc56..44bc030d17e0 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.h @@ -43,5 +43,9 @@ ice_conv_link_speed_to_virtchnl(bool __always_unused adv_link_support, return 0; } +static inline void ice_mbx_init_snapshot(struct ice_hw *hw) +{ +} + #endif /* CONFIG_PCI_IOV */ #endif /* _ICE_VF_MBX_H_ */ -- cgit v1.2.3 From 33b035e70611c10c5aa3864a7517570b25a46ebb Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:14 -0800 Subject: ice: declare ice_vc_process_vf_msg in ice_virtchnl.h The ice_vc_process_vf_msg function is the main entry point for handling virtchnl messages. This function is defined in ice_virtchnl.c but its declaration is still in ice_sriov.c The ice_sriov.c file used to contain all of the virtualization logic until commit bf93bf791cec ("ice: introduce ice_virtchnl.c and ice_virtchnl.h") moved the virtchnl logic to its own file. The ice_vc_process_vf_msg function should have had its declaration moved to ice_virtchnl.h then. Fix this. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.h | 3 --- drivers/net/ethernet/intel/ice/ice_virtchnl.h | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.h b/drivers/net/ethernet/intel/ice/ice_sriov.h index 955ab810a198..1082b0691a3f 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.h +++ b/drivers/net/ethernet/intel/ice/ice_sriov.h @@ -33,7 +33,6 @@ int ice_get_vf_cfg(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi); void ice_free_vfs(struct ice_pf *pf); -void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event); void ice_restore_all_vfs_msi_state(struct pci_dev *pdev); bool ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, @@ -68,8 +67,6 @@ ice_vc_validate_pattern(struct ice_vf *vf, struct virtchnl_proto_hdrs *proto); static inline void ice_process_vflr_event(struct ice_pf *pf) { } static inline void ice_free_vfs(struct ice_pf *pf) { } static inline -void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) { } -static inline void ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event) { } static inline void ice_print_vfs_mdd_events(struct ice_pf *pf) { } static inline void ice_print_vf_rx_mdd_event(struct ice_vf *vf) { } diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.h b/drivers/net/ethernet/intel/ice/ice_virtchnl.h index b454654d7b0c..6d5af29c855e 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.h +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.h @@ -63,6 +63,7 @@ int ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode, enum virtchnl_status_code v_retval, u8 *msg, u16 msglen); bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id); +void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event); #else /* CONFIG_PCI_IOV */ static inline void ice_virtchnl_set_dflt_ops(struct ice_vf *vf) { } static inline void ice_virtchnl_set_repr_ops(struct ice_vf *vf) { } @@ -81,6 +82,11 @@ static inline bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id) { return false; } + +static inline void +ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) +{ +} #endif /* !CONFIG_PCI_IOV */ #endif /* _ICE_VIRTCHNL_H_ */ -- cgit v1.2.3 From 4f0636fef61ad0b22fed4fb06f369d6ba38f807d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:15 -0800 Subject: ice: always report VF overflowing mailbox even without PF VSI In ice_is_malicious_vf we report a message warning the system administrator when a VF is potentially spamming the PF with asynchronous messages that could overflow the PF mailbox. The specific message was requested by our customer support team to include the VF and PF MAC address. In some cases we may not be able to locate the PF VSI to obtain the MAC address for the PF. The current implementation discards the message entirely in this case. Fix this to instead print a zero address in that case so that we always print something here. Note that dev_warn will also include the PCI device information allowing another mechanism for determining on which PF the potentially malicious VF belongs. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 79159cbb66ec..185673afb781 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1817,11 +1817,11 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, if (report_malvf) { struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); + u8 zero_addr[ETH_ALEN] = {}; - if (pf_vsi) - dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", - &vf->dev_lan_addr[0], - pf_vsi->netdev->dev_addr); + dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", + &vf->dev_lan_addr[0], + pf_vsi ? pf_vsi->netdev->dev_addr : zero_addr); } out_put_vf: -- cgit v1.2.3 From 3f22fc3131b814f0d5d1ce6d94fb8239e6df1754 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:16 -0800 Subject: ice: remove unnecessary &array[0] and just use array In ice_is_malicious_vf we print the VF MAC address using %pM by passing the address of the first element of vf->dev_lan_addr. This is equivalent to just passing vf->dev_lan_addr, so do that. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 185673afb781..938be486721e 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1820,7 +1820,7 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, u8 zero_addr[ETH_ALEN] = {}; dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", - &vf->dev_lan_addr[0], + vf->dev_lan_addr, pf_vsi ? pf_vsi->netdev->dev_addr : zero_addr); } -- cgit v1.2.3 From afc24d6584fbd246d98c0feb464b94da67661e3e Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:17 -0800 Subject: ice: pass mbxdata to ice_is_malicious_vf() The ice_is_malicious_vf() function takes information about the current state of the mailbox during a single interrupt. This information includes the number of messages processed so far, as well as the number of pending messages not yet processed. A future refactor is going to make ice_vc_process_vf_msg() call ice_is_malicious_vf() instead of having it called separately in ice_main.c This change will require passing all the necessary arguments into ice_vc_process_vf_msg(). To make this simpler, have the main loop fill in the struct ice_mbx_data and pass that rather than passing in the num_msg_proc and num_msg_pending. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_main.c | 10 +++++++++- drivers/net/ethernet/intel/ice/ice_sriov.c | 14 +++----------- drivers/net/ethernet/intel/ice/ice_sriov.h | 5 ++--- 3 files changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 615a731d7afe..a7e7a186009e 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -1393,6 +1393,8 @@ static void ice_aq_cancel_waiting_tasks(struct ice_pf *pf) wake_up(&pf->aq_wait_queue); } +#define ICE_MBX_OVERFLOW_WATERMARK 64 + /** * __ice_clean_ctrlq - helper function to clean controlq rings * @pf: ptr to struct ice_pf @@ -1483,6 +1485,7 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type) return 0; do { + struct ice_mbx_data data = {}; u16 opcode; int ret; @@ -1509,7 +1512,12 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type) ice_vf_lan_overflow_event(pf, &event); break; case ice_mbx_opc_send_msg_to_pf: - if (!ice_is_malicious_vf(pf, &event, i, pending)) + data.num_msg_proc = i; + data.num_pending_arq = pending; + data.max_num_msgs_mbx = hw->mailboxq.num_rq_entries; + data.async_watermark_val = ICE_MBX_OVERFLOW_WATERMARK; + + if (!ice_is_malicious_vf(pf, &event, &data)) ice_vc_process_vf_msg(pf, &event); break; case ice_aqc_opc_fw_logging: diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 938be486721e..5ae923ea979c 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1782,16 +1782,14 @@ void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) * ice_is_malicious_vf - helper function to detect a malicious VF * @pf: ptr to struct ice_pf * @event: pointer to the AQ event - * @num_msg_proc: the number of messages processed so far - * @num_msg_pending: the number of messages peinding in admin queue + * @mbxdata: data about the state of the mailbox */ bool ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - u16 num_msg_proc, u16 num_msg_pending) + struct ice_mbx_data *mbxdata) { s16 vf_id = le16_to_cpu(event->desc.retval); struct device *dev = ice_pf_to_dev(pf); - struct ice_mbx_data mbxdata; bool report_malvf = false; struct ice_vf *vf; int status; @@ -1803,14 +1801,8 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) goto out_put_vf; - mbxdata.num_msg_proc = num_msg_proc; - mbxdata.num_pending_arq = num_msg_pending; - mbxdata.max_num_msgs_mbx = pf->hw.mailboxq.num_rq_entries; -#define ICE_MBX_OVERFLOW_WATERMARK 64 - mbxdata.async_watermark_val = ICE_MBX_OVERFLOW_WATERMARK; - /* check to see if we have a newly malicious VF */ - status = ice_mbx_vf_state_handler(&pf->hw, &mbxdata, &vf->mbx_info, + status = ice_mbx_vf_state_handler(&pf->hw, mbxdata, &vf->mbx_info, &report_malvf); if (status) goto out_put_vf; diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.h b/drivers/net/ethernet/intel/ice/ice_sriov.h index 1082b0691a3f..8fa61d954fae 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.h +++ b/drivers/net/ethernet/intel/ice/ice_sriov.h @@ -36,7 +36,7 @@ void ice_free_vfs(struct ice_pf *pf); void ice_restore_all_vfs_msi_state(struct pci_dev *pdev); bool ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - u16 num_msg_proc, u16 num_msg_pending); + struct ice_mbx_data *mbxdata); int ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, @@ -75,8 +75,7 @@ static inline void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) { } static inline bool ice_is_malicious_vf(struct ice_pf __always_unused *pf, struct ice_rq_event_info __always_unused *event, - u16 __always_unused num_msg_proc, - u16 __always_unused num_msg_pending) + struct ice_mbx_data *mbxdata) { return false; } -- cgit v1.2.3 From 4508bf02bf8a3de8fb65869f40dfdef74dc1b339 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:18 -0800 Subject: ice: print message if ice_mbx_vf_state_handler returns an error If ice_mbx_vf_state_handler() returns an error, the ice_is_malicious_vf() function just exits without printing anything. Instead, use dev_warn_ratelimited to print a warning that we were unable to check the status for this VF. The _ratelimited variant is used to avoid potentially spamming the log if this function is failing consistently for every single mailbox message. Also we can drop the "goto" as it simply skips over a report_malvf check. That variable should always be false if ice_mbx_vf_state_handler returns non-zero. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 5ae923ea979c..f0daeda236de 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1805,7 +1805,8 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, status = ice_mbx_vf_state_handler(&pf->hw, mbxdata, &vf->mbx_info, &report_malvf); if (status) - goto out_put_vf; + dev_warn_ratelimited(dev, "Unable to check status of mailbox overflow for VF %u MAC %pM, status %d\n", + vf->vf_id, vf->dev_lan_addr, status); if (report_malvf) { struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); -- cgit v1.2.3 From c414463ab1bb098e67f4c1a4ef64f3e97780f087 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:19 -0800 Subject: ice: move ice_is_malicious_vf() to ice_virtchnl.c The ice_is_malicious_vf() function is currently implemented in ice_sriov.c This function is not Single Root specific, and a future change is going to refactor the ice_vc_process_vf_msg() function to call this instead of calling it before ice_vc_process_vf_msg() in the main loop of __ice_clean_ctrlq. To make that change easier to review, first move this function into ice_virtchnl.c but leave the call in __ice_clean_ctrlq() alone. Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_sriov.c | 45 --------------------------- drivers/net/ethernet/intel/ice/ice_sriov.h | 11 ------- drivers/net/ethernet/intel/ice/ice_virtchnl.c | 45 +++++++++++++++++++++++++++ drivers/net/ethernet/intel/ice/ice_virtchnl.h | 11 +++++++ 4 files changed, 56 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index f0daeda236de..6fa62c3cedb0 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -1777,48 +1777,3 @@ void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) } } } - -/** - * ice_is_malicious_vf - helper function to detect a malicious VF - * @pf: ptr to struct ice_pf - * @event: pointer to the AQ event - * @mbxdata: data about the state of the mailbox - */ -bool -ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - struct ice_mbx_data *mbxdata) -{ - s16 vf_id = le16_to_cpu(event->desc.retval); - struct device *dev = ice_pf_to_dev(pf); - bool report_malvf = false; - struct ice_vf *vf; - int status; - - vf = ice_get_vf_by_id(pf, vf_id); - if (!vf) - return false; - - if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) - goto out_put_vf; - - /* check to see if we have a newly malicious VF */ - status = ice_mbx_vf_state_handler(&pf->hw, mbxdata, &vf->mbx_info, - &report_malvf); - if (status) - dev_warn_ratelimited(dev, "Unable to check status of mailbox overflow for VF %u MAC %pM, status %d\n", - vf->vf_id, vf->dev_lan_addr, status); - - if (report_malvf) { - struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); - u8 zero_addr[ETH_ALEN] = {}; - - dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", - vf->dev_lan_addr, - pf_vsi ? pf_vsi->netdev->dev_addr : zero_addr); - } - -out_put_vf: - ice_put_vf(vf); - - return vf->mbx_info.malicious; -} diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.h b/drivers/net/ethernet/intel/ice/ice_sriov.h index 8fa61d954fae..346cb2666f3a 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.h +++ b/drivers/net/ethernet/intel/ice/ice_sriov.h @@ -34,9 +34,6 @@ ice_get_vf_cfg(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi); void ice_free_vfs(struct ice_pf *pf); void ice_restore_all_vfs_msi_state(struct pci_dev *pdev); -bool -ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - struct ice_mbx_data *mbxdata); int ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos, @@ -72,14 +69,6 @@ static inline void ice_print_vfs_mdd_events(struct ice_pf *pf) { } static inline void ice_print_vf_rx_mdd_event(struct ice_vf *vf) { } static inline void ice_restore_all_vfs_msi_state(struct pci_dev *pdev) { } -static inline bool -ice_is_malicious_vf(struct ice_pf __always_unused *pf, - struct ice_rq_event_info __always_unused *event, - struct ice_mbx_data *mbxdata) -{ - return false; -} - static inline int ice_sriov_configure(struct pci_dev __always_unused *pdev, int __always_unused num_vfs) diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index e24e3f5017ca..e0c573d9d1b9 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -3833,6 +3833,51 @@ void ice_virtchnl_set_repr_ops(struct ice_vf *vf) vf->virtchnl_ops = &ice_virtchnl_repr_ops; } +/** + * ice_is_malicious_vf - helper function to detect a malicious VF + * @pf: ptr to struct ice_pf + * @event: pointer to the AQ event + * @mbxdata: data about the state of the mailbox + */ +bool +ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, + struct ice_mbx_data *mbxdata) +{ + s16 vf_id = le16_to_cpu(event->desc.retval); + struct device *dev = ice_pf_to_dev(pf); + bool report_malvf = false; + struct ice_vf *vf; + int status; + + vf = ice_get_vf_by_id(pf, vf_id); + if (!vf) + return false; + + if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) + goto out_put_vf; + + /* check to see if we have a newly malicious VF */ + status = ice_mbx_vf_state_handler(&pf->hw, mbxdata, &vf->mbx_info, + &report_malvf); + if (status) + dev_warn_ratelimited(dev, "Unable to check status of mailbox overflow for VF %u MAC %pM, status %d\n", + vf->vf_id, vf->dev_lan_addr, status); + + if (report_malvf) { + struct ice_vsi *pf_vsi = ice_get_main_vsi(pf); + u8 zero_addr[ETH_ALEN] = {}; + + dev_warn(dev, "VF MAC %pM on PF MAC %pM is generating asynchronous messages and may be overflowing the PF message queue. Please see the Adapter User Guide for more information\n", + vf->dev_lan_addr, + pf_vsi ? pf_vsi->netdev->dev_addr : zero_addr); + } + +out_put_vf: + ice_put_vf(vf); + + return vf->mbx_info.malicious; +} + /** * ice_vc_process_vf_msg - Process request from VF * @pf: pointer to the PF structure diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.h b/drivers/net/ethernet/intel/ice/ice_virtchnl.h index 6d5af29c855e..648a383fad85 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.h +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.h @@ -63,6 +63,9 @@ int ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode, enum virtchnl_status_code v_retval, u8 *msg, u16 msglen); bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id); +bool +ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, + struct ice_mbx_data *mbxdata); void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event); #else /* CONFIG_PCI_IOV */ static inline void ice_virtchnl_set_dflt_ops(struct ice_vf *vf) { } @@ -83,6 +86,14 @@ static inline bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id) return false; } +static inline bool +ice_is_malicious_vf(struct ice_pf __always_unused *pf, + struct ice_rq_event_info __always_unused *event, + struct ice_mbx_data *mbxdata) +{ + return false; +} + static inline void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) { -- cgit v1.2.3 From be96815c616822d3800405b8fbebe3e069d6eed2 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 22 Feb 2023 09:09:20 -0800 Subject: ice: call ice_is_malicious_vf() from ice_vc_process_vf_msg() The main loop in __ice_clean_ctrlq first checks if a VF might be malicious before calling ice_vc_process_vf_msg(). This results in duplicate code in both functions to obtain a reference to the VF, and exports the ice_is_malicious_vf() from ice_virtchnl.c unnecessarily. Refactor ice_is_malicious_vf() to be a static function that takes a pointer to the VF. Call this in ice_vc_process_vf_msg() just after we obtain a reference to the VF by calling ice_get_vf_by_id. Pass the mailbox data from the __ice_clean_ctrlq function into ice_vc_process_vf_msg() instead of calling ice_is_malicious_vf(). This reduces the number of exported functions and avoids the need to obtain the VF reference twice for every mailbox message. Note that the state check for ICE_VF_STATE_DIS is kept in ice_is_malicious_vf() and we call this before checking that state in ice_vc_process_vf_msg. This is intentional, as we stop responding to VF messages from a VF once we detect that it may be overflowing the mailbox. This ensures that we continue to silently ignore the message as before without responding via ice_vc_send_msg_to_vf(). Signed-off-by: Jacob Keller Reviewed-by: Michal Swiatkowski Tested-by: Marek Szlosek Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_main.c | 3 +-- drivers/net/ethernet/intel/ice/ice_virtchnl.c | 36 ++++++++++++++------------- drivers/net/ethernet/intel/ice/ice_virtchnl.h | 17 +++---------- 3 files changed, 24 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index a7e7a186009e..20b3f3e6eda1 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -1517,8 +1517,7 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type) data.max_num_msgs_mbx = hw->mailboxq.num_rq_entries; data.async_watermark_val = ICE_MBX_OVERFLOW_WATERMARK; - if (!ice_is_malicious_vf(pf, &event, &data)) - ice_vc_process_vf_msg(pf, &event); + ice_vc_process_vf_msg(pf, &event, &data); break; case ice_aqc_opc_fw_logging: ice_output_fw_log(hw, &event.desc, event.msg_buf); diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index e0c573d9d1b9..97243c616d5d 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -3834,27 +3834,26 @@ void ice_virtchnl_set_repr_ops(struct ice_vf *vf) } /** - * ice_is_malicious_vf - helper function to detect a malicious VF - * @pf: ptr to struct ice_pf - * @event: pointer to the AQ event + * ice_is_malicious_vf - check if this vf might be overflowing mailbox + * @vf: the VF to check * @mbxdata: data about the state of the mailbox + * + * Detect if a given VF might be malicious and attempting to overflow the PF + * mailbox. If so, log a warning message and ignore this event. */ -bool -ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - struct ice_mbx_data *mbxdata) +static bool +ice_is_malicious_vf(struct ice_vf *vf, struct ice_mbx_data *mbxdata) { - s16 vf_id = le16_to_cpu(event->desc.retval); - struct device *dev = ice_pf_to_dev(pf); bool report_malvf = false; - struct ice_vf *vf; + struct device *dev; + struct ice_pf *pf; int status; - vf = ice_get_vf_by_id(pf, vf_id); - if (!vf) - return false; + pf = vf->pf; + dev = ice_pf_to_dev(pf); if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) - goto out_put_vf; + return vf->mbx_info.malicious; /* check to see if we have a newly malicious VF */ status = ice_mbx_vf_state_handler(&pf->hw, mbxdata, &vf->mbx_info, @@ -3872,9 +3871,6 @@ ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, pf_vsi ? pf_vsi->netdev->dev_addr : zero_addr); } -out_put_vf: - ice_put_vf(vf); - return vf->mbx_info.malicious; } @@ -3882,11 +3878,13 @@ out_put_vf: * ice_vc_process_vf_msg - Process request from VF * @pf: pointer to the PF structure * @event: pointer to the AQ event + * @mbxdata: information used to detect VF attempting mailbox overflow * * called from the common asq/arq handler to * process request from VF */ -void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) +void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event, + struct ice_mbx_data *mbxdata) { u32 v_opcode = le32_to_cpu(event->desc.cookie_high); s16 vf_id = le16_to_cpu(event->desc.retval); @@ -3908,6 +3906,10 @@ void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) mutex_lock(&vf->cfg_lock); + /* Check if the VF is trying to overflow the mailbox */ + if (ice_is_malicious_vf(vf, mbxdata)) + goto finish; + /* Check if VF is disabled. */ if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) { err = -EPERM; diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.h b/drivers/net/ethernet/intel/ice/ice_virtchnl.h index 648a383fad85..cd747718de73 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.h +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.h @@ -63,10 +63,8 @@ int ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode, enum virtchnl_status_code v_retval, u8 *msg, u16 msglen); bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id); -bool -ice_is_malicious_vf(struct ice_pf *pf, struct ice_rq_event_info *event, - struct ice_mbx_data *mbxdata); -void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event); +void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event, + struct ice_mbx_data *mbxdata); #else /* CONFIG_PCI_IOV */ static inline void ice_virtchnl_set_dflt_ops(struct ice_vf *vf) { } static inline void ice_virtchnl_set_repr_ops(struct ice_vf *vf) { } @@ -86,16 +84,9 @@ static inline bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id) return false; } -static inline bool -ice_is_malicious_vf(struct ice_pf __always_unused *pf, - struct ice_rq_event_info __always_unused *event, - struct ice_mbx_data *mbxdata) -{ - return false; -} - static inline void -ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event) +ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event, + struct ice_mbx_data *mbxdata) { } #endif /* !CONFIG_PCI_IOV */ -- cgit v1.2.3 From e4ed8ba08e3f1ef24771eb95f87da129ad752063 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 7 Mar 2023 22:44:02 +0100 Subject: net: phy: micrel: Add support for PTP_PF_PEROUT for lan8841 Lan8841 has 10 GPIOs and it has 2 events(EVENT_A and EVENT_B). It is possible to assigned the 2 events to any of the GPIOs, but a GPIO can have only 1 event at a time. These events are used to generate periodic signals. It is possible to configure the length, the start time and the period of the signal by configuring the event. Currently the SW uses only EVENT_A to generate the perout. These events are generated by comparing the target time with the PHC time. In case the PHC time is changed to a value bigger than the target time + reload time, then it would generate only 1 event and then it would stop because target time + reload time is small than PHC time. Therefore it is required to change also the target time every time when the PHC is changed. The same will apply also when the PHC time is changed to a smaller value. This was tested using: testptp -L 6,2 testptp -p 1000000000 -w 200000000 Signed-off-by: Horatiu Vultur Link: https://lore.kernel.org/r/20230307214402.793057-1-horatiu.vultur@microchip.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/micrel.c | 391 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 389 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 2c84fccef4f6..e5b2af69ac03 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -318,6 +318,7 @@ struct kszphy_ptp_priv { struct ptp_clock_info ptp_clock_info; /* Lock for ptp_clock */ struct mutex ptp_lock; + struct ptp_pin_desc *pin_config; }; struct kszphy_priv { @@ -3658,6 +3659,77 @@ static int lan8841_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; } +#define LAN8841_EVENT_A 0 +#define LAN8841_EVENT_B 1 +#define LAN8841_PTP_LTC_TARGET_SEC_HI(event) ((event) == LAN8841_EVENT_A ? 278 : 288) +#define LAN8841_PTP_LTC_TARGET_SEC_LO(event) ((event) == LAN8841_EVENT_A ? 279 : 289) +#define LAN8841_PTP_LTC_TARGET_NS_HI(event) ((event) == LAN8841_EVENT_A ? 280 : 290) +#define LAN8841_PTP_LTC_TARGET_NS_LO(event) ((event) == LAN8841_EVENT_A ? 281 : 291) + +static int lan8841_ptp_set_target(struct kszphy_ptp_priv *ptp_priv, u8 event, + s64 sec, u32 nsec) +{ + struct phy_device *phydev = ptp_priv->phydev; + int ret; + + ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_SEC_HI(event), + upper_16_bits(sec)); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_SEC_LO(event), + lower_16_bits(sec)); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_NS_HI(event) & 0x3fff, + upper_16_bits(nsec)); + if (ret) + return ret; + + return phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_NS_LO(event), + lower_16_bits(nsec)); +} + +#define LAN8841_BUFFER_TIME 2 + +static int lan8841_ptp_update_target(struct kszphy_ptp_priv *ptp_priv, + const struct timespec64 *ts) +{ + return lan8841_ptp_set_target(ptp_priv, LAN8841_EVENT_A, + ts->tv_sec + LAN8841_BUFFER_TIME, ts->tv_nsec); +} + +#define LAN8841_PTP_LTC_TARGET_RELOAD_SEC_HI(event) ((event) == LAN8841_EVENT_A ? 282 : 292) +#define LAN8841_PTP_LTC_TARGET_RELOAD_SEC_LO(event) ((event) == LAN8841_EVENT_A ? 283 : 293) +#define LAN8841_PTP_LTC_TARGET_RELOAD_NS_HI(event) ((event) == LAN8841_EVENT_A ? 284 : 294) +#define LAN8841_PTP_LTC_TARGET_RELOAD_NS_LO(event) ((event) == LAN8841_EVENT_A ? 285 : 295) + +static int lan8841_ptp_set_reload(struct kszphy_ptp_priv *ptp_priv, u8 event, + s64 sec, u32 nsec) +{ + struct phy_device *phydev = ptp_priv->phydev; + int ret; + + ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_SEC_HI(event), + upper_16_bits(sec)); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_SEC_LO(event), + lower_16_bits(sec)); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_NS_HI(event) & 0x3fff, + upper_16_bits(nsec)); + if (ret) + return ret; + + return phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_NS_LO(event), + lower_16_bits(nsec)); +} + #define LAN8841_PTP_LTC_SET_SEC_HI 262 #define LAN8841_PTP_LTC_SET_SEC_MID 263 #define LAN8841_PTP_LTC_SET_SEC_LO 264 @@ -3671,6 +3743,7 @@ static int lan8841_ptp_settime64(struct ptp_clock_info *ptp, struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, ptp_clock_info); struct phy_device *phydev = ptp_priv->phydev; + int ret; /* Set the value to be stored */ mutex_lock(&ptp_priv->ptp_lock); @@ -3683,9 +3756,10 @@ static int lan8841_ptp_settime64(struct ptp_clock_info *ptp, /* Set the command to load the LTC */ phy_write_mmd(phydev, 2, LAN8841_PTP_CMD_CTL, LAN8841_PTP_CMD_CTL_PTP_LTC_LOAD); + ret = lan8841_ptp_update_target(ptp_priv, ts); mutex_unlock(&ptp_priv->ptp_lock); - return 0; + return ret; } #define LAN8841_PTP_LTC_RD_SEC_HI 358 @@ -3740,6 +3814,7 @@ static int lan8841_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) bool add = true; u32 nsec; s32 sec; + int ret; /* The HW allows up to 15 sec to adjust the time, but here we limit to * 10 sec the adjustment. The reason is, in case the adjustment is 14 @@ -3803,7 +3878,13 @@ static int lan8841_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) } mutex_unlock(&ptp_priv->ptp_lock); - return 0; + /* Update the target clock */ + ptp->gettime64(ptp, &ts); + mutex_lock(&ptp_priv->ptp_lock); + ret = lan8841_ptp_update_target(ptp_priv, &ts); + mutex_unlock(&ptp_priv->ptp_lock); + + return ret; } #define LAN8841_PTP_LTC_RATE_ADJ_HI 269 @@ -3839,6 +3920,292 @@ static int lan8841_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) return 0; } +static int lan8841_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) +{ + switch (func) { + case PTP_PF_NONE: + case PTP_PF_PEROUT: + break; + default: + return -1; + } + + return 0; +} + +#define LAN8841_PTP_GPIO_NUM 10 +#define LAN8841_GPIO_EN 128 +#define LAN8841_GPIO_DIR 129 +#define LAN8841_GPIO_BUF 130 + +static int lan8841_ptp_perout_off(struct kszphy_ptp_priv *ptp_priv, int pin) +{ + struct phy_device *phydev = ptp_priv->phydev; + int ret; + + ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_EN, BIT(pin)); + if (ret) + return ret; + + ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_DIR, BIT(pin)); + if (ret) + return ret; + + return phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_BUF, BIT(pin)); +} + +static int lan8841_ptp_perout_on(struct kszphy_ptp_priv *ptp_priv, int pin) +{ + struct phy_device *phydev = ptp_priv->phydev; + int ret; + + ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_EN, BIT(pin)); + if (ret) + return ret; + + ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_DIR, BIT(pin)); + if (ret) + return ret; + + return phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_BUF, BIT(pin)); +} + +#define LAN8841_GPIO_DATA_SEL1 131 +#define LAN8841_GPIO_DATA_SEL2 132 +#define LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_MASK GENMASK(2, 0) +#define LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_A 1 +#define LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_B 2 +#define LAN8841_PTP_GENERAL_CONFIG 257 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A BIT(1) +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B BIT(3) +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A_MASK GENMASK(7, 4) +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B_MASK GENMASK(11, 8) +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A 4 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B 7 + +static int lan8841_ptp_remove_event(struct kszphy_ptp_priv *ptp_priv, int pin, + u8 event) +{ + struct phy_device *phydev = ptp_priv->phydev; + u16 tmp; + int ret; + + /* Now remove pin from the event. GPIO_DATA_SEL1 contains the GPIO + * pins 0-4 while GPIO_DATA_SEL2 contains GPIO pins 5-9, therefore + * depending on the pin, it requires to read a different register + */ + if (pin < 5) { + tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_MASK << (3 * pin); + ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL1, tmp); + } else { + tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_MASK << (3 * (pin - 5)); + ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL2, tmp); + } + if (ret) + return ret; + + /* Disable the event */ + if (event == LAN8841_EVENT_A) + tmp = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A | + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A_MASK; + else + tmp = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B | + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B_MASK; + return phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_EN, tmp); +} + +static int lan8841_ptp_enable_event(struct kszphy_ptp_priv *ptp_priv, int pin, + u8 event, int pulse_width) +{ + struct phy_device *phydev = ptp_priv->phydev; + u16 tmp; + int ret; + + /* Enable the event */ + if (event == LAN8841_EVENT_A) + ret = phy_modify_mmd(phydev, 2, LAN8841_PTP_GENERAL_CONFIG, + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A | + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A_MASK, + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A | + pulse_width << LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A); + else + ret = phy_modify_mmd(phydev, 2, LAN8841_PTP_GENERAL_CONFIG, + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B | + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B_MASK, + LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B | + pulse_width << LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B); + if (ret) + return ret; + + /* Now connect the pin to the event. GPIO_DATA_SEL1 contains the GPIO + * pins 0-4 while GPIO_DATA_SEL2 contains GPIO pins 5-9, therefore + * depending on the pin, it requires to read a different register + */ + if (event == LAN8841_EVENT_A) + tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_A; + else + tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_B; + + if (pin < 5) + ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL1, + tmp << (3 * pin)); + else + ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL2, + tmp << (3 * (pin - 5))); + + return ret; +} + +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_200MS 13 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100MS 12 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50MS 11 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10MS 10 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5MS 9 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1MS 8 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500US 7 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100US 6 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50US 5 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10US 4 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5US 3 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1US 2 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500NS 1 +#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100NS 0 + +static int lan8841_ptp_perout(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, + ptp_clock_info); + struct phy_device *phydev = ptp_priv->phydev; + struct timespec64 ts_on, ts_period; + s64 on_nsec, period_nsec; + int pulse_width; + int pin; + int ret; + + if (rq->perout.flags & ~PTP_PEROUT_DUTY_CYCLE) + return -EOPNOTSUPP; + + pin = ptp_find_pin(ptp_priv->ptp_clock, PTP_PF_PEROUT, rq->perout.index); + if (pin == -1 || pin >= LAN8841_PTP_GPIO_NUM) + return -EINVAL; + + if (!on) { + ret = lan8841_ptp_perout_off(ptp_priv, pin); + if (ret) + return ret; + + return lan8841_ptp_remove_event(ptp_priv, LAN8841_EVENT_A, pin); + } + + ts_on.tv_sec = rq->perout.on.sec; + ts_on.tv_nsec = rq->perout.on.nsec; + on_nsec = timespec64_to_ns(&ts_on); + + ts_period.tv_sec = rq->perout.period.sec; + ts_period.tv_nsec = rq->perout.period.nsec; + period_nsec = timespec64_to_ns(&ts_period); + + if (period_nsec < 200) { + pr_warn_ratelimited("%s: perout period too small, minimim is 200 nsec\n", + phydev_name(phydev)); + return -EOPNOTSUPP; + } + + if (on_nsec >= period_nsec) { + pr_warn_ratelimited("%s: pulse width must be smaller than period\n", + phydev_name(phydev)); + return -EINVAL; + } + + switch (on_nsec) { + case 200000000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_200MS; + break; + case 100000000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100MS; + break; + case 50000000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50MS; + break; + case 10000000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10MS; + break; + case 5000000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5MS; + break; + case 1000000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1MS; + break; + case 500000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500US; + break; + case 100000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100US; + break; + case 50000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50US; + break; + case 10000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10US; + break; + case 5000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5US; + break; + case 1000: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1US; + break; + case 500: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500NS; + break; + case 100: + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100NS; + break; + default: + pr_warn_ratelimited("%s: Use default duty cycle of 100ns\n", + phydev_name(phydev)); + pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100NS; + break; + } + + mutex_lock(&ptp_priv->ptp_lock); + ret = lan8841_ptp_set_target(ptp_priv, LAN8841_EVENT_A, rq->perout.start.sec, + rq->perout.start.nsec); + mutex_unlock(&ptp_priv->ptp_lock); + if (ret) + return ret; + + ret = lan8841_ptp_set_reload(ptp_priv, LAN8841_EVENT_A, rq->perout.period.sec, + rq->perout.period.nsec); + if (ret) + return ret; + + ret = lan8841_ptp_enable_event(ptp_priv, pin, LAN8841_EVENT_A, + pulse_width); + if (ret) + return ret; + + ret = lan8841_ptp_perout_on(ptp_priv, pin); + if (ret) + lan8841_ptp_remove_event(ptp_priv, pin, LAN8841_EVENT_A); + + return ret; +} + +static int lan8841_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + switch (rq->type) { + case PTP_CLK_REQ_PEROUT: + return lan8841_ptp_perout(ptp, rq, on); + default: + return -EOPNOTSUPP; + } + + return 0; +} + static struct ptp_clock_info lan8841_ptp_clock_info = { .owner = THIS_MODULE, .name = "lan8841 ptp", @@ -3847,6 +4214,10 @@ static struct ptp_clock_info lan8841_ptp_clock_info = { .settime64 = lan8841_ptp_settime64, .adjtime = lan8841_ptp_adjtime, .adjfine = lan8841_ptp_adjfine, + .verify = lan8841_ptp_verify, + .enable = lan8841_ptp_enable, + .n_per_out = LAN8841_PTP_GPIO_NUM, + .n_pins = LAN8841_PTP_GPIO_NUM, }; #define LAN8841_OPERATION_MODE_STRAP_LOW_REGISTER 3 @@ -3874,7 +4245,23 @@ static int lan8841_probe(struct phy_device *phydev) priv = phydev->priv; ptp_priv = &priv->ptp_priv; + ptp_priv->pin_config = devm_kcalloc(&phydev->mdio.dev, + LAN8841_PTP_GPIO_NUM, + sizeof(*ptp_priv->pin_config), + GFP_KERNEL); + if (!ptp_priv->pin_config) + return -ENOMEM; + + for (int i = 0; i < LAN8841_PTP_GPIO_NUM; ++i) { + struct ptp_pin_desc *p = &ptp_priv->pin_config[i]; + + snprintf(p->name, sizeof(p->name), "pin%d", i); + p->index = i; + p->func = PTP_PF_NONE; + } + ptp_priv->ptp_clock_info = lan8841_ptp_clock_info; + ptp_priv->ptp_clock_info.pin_config = ptp_priv->pin_config; ptp_priv->ptp_clock = ptp_clock_register(&ptp_priv->ptp_clock_info, &phydev->mdio.dev); if (IS_ERR(ptp_priv->ptp_clock)) { -- cgit v1.2.3 From c5a8027de26e1b7c0ba4f8e430165b6a4a29a869 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Fri, 10 Mar 2023 08:45:00 +0100 Subject: net: phy: dp83867: Disable IRQs on suspend Before putting the PHY into IEEE power down mode, disable IRQs to prevent accessing the PHY once MDIO has already been shutdown. Signed-off-by: Alexander Stein Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20230310074500.3472858-1-alexander.stein@ew.tq-group.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/dp83867.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 89cd821f1f46..5821f04c69dc 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -693,6 +693,30 @@ static int dp83867_of_init(struct phy_device *phydev) } #endif /* CONFIG_OF_MDIO */ +static int dp83867_suspend(struct phy_device *phydev) +{ + /* Disable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_DISABLED; + dp83867_config_intr(phydev); + } + + return genphy_suspend(phydev); +} + +static int dp83867_resume(struct phy_device *phydev) +{ + /* Enable PHY Interrupts */ + if (phy_interrupt_is_valid(phydev)) { + phydev->interrupts = PHY_INTERRUPT_ENABLED; + dp83867_config_intr(phydev); + } + + genphy_resume(phydev); + + return 0; +} + static int dp83867_probe(struct phy_device *phydev) { struct dp83867_private *dp83867; @@ -968,8 +992,8 @@ static struct phy_driver dp83867_driver[] = { .config_intr = dp83867_config_intr, .handle_interrupt = dp83867_handle_interrupt, - .suspend = genphy_suspend, - .resume = genphy_resume, + .suspend = dp83867_suspend, + .resume = dp83867_resume, .link_change_notify = dp83867_link_change_notify, .set_loopback = dp83867_loopback, -- cgit v1.2.3 From 90c7dd32652bcfcc7716c762bc095a7d49ad85dd Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 8 Mar 2023 21:34:13 +0100 Subject: net: phy: smsc: use device_property_present in smsc_phy_probe Use unified device property API. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/a969f012-1d3b-7a36-51cf-89a5f8f15a9b@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/smsc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index ac951d67edcc..9cfaccce1241 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -269,7 +269,6 @@ static void smsc_get_stats(struct phy_device *phydev, static int smsc_phy_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; - struct device_node *of_node = dev->of_node; struct smsc_phy_priv *priv; struct clk *refclk; @@ -279,7 +278,7 @@ static int smsc_phy_probe(struct phy_device *phydev) priv->energy_enable = true; - if (of_property_read_bool(of_node, "smsc,disable-energy-detect")) + if (device_property_present(dev, "smsc,disable-energy-detect")) priv->energy_enable = false; phydev->priv = priv; -- cgit v1.2.3 From be50da3e9d4ad1958f7b11322d44d94d5c25a4c1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 9 Mar 2023 10:45:59 +0100 Subject: net: virtio_net: implement exact header length guest feature Virtio spec introduced a feature VIRTIO_NET_F_GUEST_HDRLEN which when set implicates that device benefits from knowing the exact size of the header. For compatibility, to signal to the device that the header is reliable driver also needs to set this feature. Without this feature set by driver, device has to figure out the header size itself. Quoting the original virtio spec: "hdr_len is a hint to the device as to how much of the header needs to be kept to copy into each packet" "a hint" might not be clear for the reader what does it mean, if it is "maybe like that" of "exactly like that". This feature just makes it crystal clear and let the device count on the hdr_len being filled up by the exact length of header. Also note the spec already has following note about hdr_len: "Due to various bugs in implementations, this field is not useful as a guarantee of the transport header size." Without this feature the device needs to parse the header in core data path handling. Accurate information helps the device to eliminate such header parsing and directly use the hardware accelerators for GSO operation. virtio_net_hdr_from_skb() fills up hdr_len to skb_headlen(skb). The driver already complies to fill the correct value. Introduce the feature and advertise it. Note that virtio spec also includes following note for device implementation: "Caution should be taken by the implementation so as to prevent a malicious driver from attacking the device by setting an incorrect hdr_len." There is a plan to support this feature in our emulated device. A device of SolidRun offers this feature bit. They claim this feature will save the device a few cycles for every GSO packet. Link: https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-230006x3 Signed-off-by: Jiri Pirko Reviewed-by: Parav Pandit Reviewed-by: Alvaro Karsz Acked-by: Michael S. Tsirkin Acked-by: Willem de Bruijn Link: https://lore.kernel.org/r/20230309094559.917857-1-jiri@resnulli.us Signed-off-by: Jakub Kicinski --- drivers/net/virtio_net.c | 6 ++++-- include/uapi/linux/virtio_net.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index fb5e68ed3ec2..e85b03988733 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -62,7 +62,8 @@ static const unsigned long guest_offloads[] = { VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_USO4, - VIRTIO_NET_F_GUEST_USO6 + VIRTIO_NET_F_GUEST_USO6, + VIRTIO_NET_F_GUEST_HDRLEN }; #define GUEST_OFFLOAD_GRO_HW_MASK ((1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ @@ -4213,7 +4214,8 @@ static struct virtio_device_id id_table[] = { VIRTIO_NET_F_CTRL_MAC_ADDR, \ VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \ VIRTIO_NET_F_SPEED_DUPLEX, VIRTIO_NET_F_STANDBY, \ - VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL + VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL, \ + VIRTIO_NET_F_GUEST_HDRLEN static unsigned int features[] = { VIRTNET_FEATURES, diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h index b4062bed186a..12c1c9699935 100644 --- a/include/uapi/linux/virtio_net.h +++ b/include/uapi/linux/virtio_net.h @@ -61,6 +61,7 @@ #define VIRTIO_NET_F_GUEST_USO6 55 /* Guest can handle USOv6 in. */ #define VIRTIO_NET_F_HOST_USO 56 /* Host can handle USO in. */ #define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */ +#define VIRTIO_NET_F_GUEST_HDRLEN 59 /* Guest provides the exact hdr_len value. */ #define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */ #define VIRTIO_NET_F_RSC_EXT 61 /* extended coalescing info */ #define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device -- cgit v1.2.3 From 7c6dddc239abe660598c49ec95ea0ed6399a4b2a Mon Sep 17 00:00:00 2001 From: Maxim Korotkov Date: Thu, 9 Mar 2023 20:43:47 +0300 Subject: bnxt: avoid overflow in bnxt_get_nvram_directory() The value of an arithmetic expression is subject of possible overflow due to a failure to cast operands to a larger data type before performing arithmetic. Used macro for multiplication instead operator for avoiding overflow. Found by Security Code and Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Maxim Korotkov Reviewed-by: Pavan Chebbi Link: https://lore.kernel.org/r/20230309174347.3515-1-korotkov.maxim.s@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index ec573127b707..696f32dfe41f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2862,7 +2862,7 @@ static int bnxt_get_nvram_directory(struct net_device *dev, u32 len, u8 *data) if (rc) return rc; - buflen = dir_entries * entry_length; + buflen = mul_u32_u32(dir_entries, entry_length); buf = hwrm_req_dma_slice(bp, req, buflen, &dma_handle); if (!buf) { hwrm_req_drop(bp, req); -- cgit v1.2.3 From bcc858689db5f2e5a8d4d6e8bc5bb9736cd80626 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 10 Mar 2023 08:47:16 -0600 Subject: net: Use of_property_present() for testing DT property presence It is preferred to use typed property access functions (i.e. of_property_read_ functions) rather than low-level of_get_property/of_find_property functions for reading properties. As part of this, convert of_get_property/of_find_property calls to the recently added of_property_present() helper when we just want to test for presence of a property and nothing more. Signed-off-by: Rob Herring Reviewed-by: Simon Horman Acked-by: Kalle Valo Link: https://lore.kernel.org/r/20230310144716.1544083-1-robh@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/mdio/of_mdio.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c index 510822d6d0d9..bf10d0688eea 100644 --- a/drivers/net/mdio/of_mdio.c +++ b/drivers/net/mdio/of_mdio.c @@ -131,7 +131,7 @@ bool of_mdiobus_child_is_phy(struct device_node *child) return true; } - if (!of_find_property(child, "compatible", NULL)) + if (!of_property_present(child, "compatible")) return true; return false; @@ -203,7 +203,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) /* auto scan for PHYs with empty reg property */ for_each_available_child_of_node(np, child) { /* Skip PHYs with reg property set */ - if (of_find_property(child, "reg", NULL)) + if (of_property_present(child, "reg")) continue; for (addr = 0; addr < PHY_MAX_ADDR; addr++) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index 52527b61341e..e406e11481a6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -129,7 +129,7 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, sdio->drive_strength = val; /* make sure there are interrupts defined in the node */ - if (!of_find_property(np, "interrupts", NULL)) + if (!of_property_present(np, "interrupts")) return; irq = irq_of_parse_and_map(np, 0); -- cgit v1.2.3 From 68a84a127bb07d2a47e95c808520f742c54781c7 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Sun, 12 Mar 2023 20:51:55 +0100 Subject: net: lan966x: Change lan966x_police_del return type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the function always returns 0 change the return type to be void instead of int. In this way also remove a wrong message in case of error which would never happen. Signed-off-by: Horatiu Vultur Reviewed-by: Alvin Šipraga Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230312195155.1492881-1-horatiu.vultur@microchip.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/lan966x/lan966x_police.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_police.c b/drivers/net/ethernet/microchip/lan966x/lan966x_police.c index 7d66fe75cd3b..7302df2300fd 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_police.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_police.c @@ -49,8 +49,7 @@ static int lan966x_police_add(struct lan966x_port *port, return 0; } -static int lan966x_police_del(struct lan966x_port *port, - u16 pol_idx) +static void lan966x_police_del(struct lan966x_port *port, u16 pol_idx) { struct lan966x *lan966x = port->lan966x; @@ -67,8 +66,6 @@ static int lan966x_police_del(struct lan966x_port *port, lan_wr(ANA_POL_PIR_CFG_PIR_RATE_SET(GENMASK(14, 0)) | ANA_POL_PIR_CFG_PIR_BURST_SET(0), lan966x, ANA_POL_PIR_CFG(pol_idx)); - - return 0; } static int lan966x_police_validate(struct lan966x_port *port, @@ -186,7 +183,6 @@ int lan966x_police_port_del(struct lan966x_port *port, struct netlink_ext_ack *extack) { struct lan966x *lan966x = port->lan966x; - int err; if (port->tc.police_id != police_id) { NL_SET_ERR_MSG_MOD(extack, @@ -194,12 +190,7 @@ int lan966x_police_port_del(struct lan966x_port *port, return -EINVAL; } - err = lan966x_police_del(port, POL_IDX_PORT + port->chip_port); - if (err) { - NL_SET_ERR_MSG_MOD(extack, - "Failed to add policer to port"); - return err; - } + lan966x_police_del(port, POL_IDX_PORT + port->chip_port); lan_rmw(ANA_POL_CFG_PORT_POL_ENA_SET(0) | ANA_POL_CFG_POL_ORDER_SET(POL_ORDER), -- cgit v1.2.3 From b071af523579df7341cabf0f16fc661125e9a13f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 13 Mar 2023 20:17:31 +0000 Subject: neighbour: annotate lockless accesses to n->nud_state We have many lockless accesses to n->nud_state. Before adding another one in the following patch, add annotations to readers and writers. Signed-off-by: Eric Dumazet Reviewed-by: David Ahern Reviewed-by: Martin KaFai Lau Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 4 ++-- include/net/neighbour.h | 2 +- net/bridge/br_arp_nd_proxy.c | 4 ++-- net/bridge/br_netfilter_hooks.c | 3 ++- net/core/filter.c | 4 ++-- net/core/neighbour.c | 28 ++++++++++++++-------------- net/ipv4/arp.c | 8 ++++---- net/ipv4/fib_semantics.c | 4 ++-- net/ipv4/nexthop.c | 4 ++-- net/ipv4/route.c | 2 +- net/ipv6/ip6_output.c | 2 +- net/ipv6/ndisc.c | 4 ++-- net/ipv6/route.c | 2 +- 13 files changed, 36 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index b1b179effe2a..f2c30214cae8 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1863,7 +1863,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) struct vxlan_fdb *f; struct sk_buff *reply; - if (!(n->nud_state & NUD_CONNECTED)) { + if (!(READ_ONCE(n->nud_state) & NUD_CONNECTED)) { neigh_release(n); goto out; } @@ -2027,7 +2027,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) struct vxlan_fdb *f; struct sk_buff *reply; - if (!(n->nud_state & NUD_CONNECTED)) { + if (!(READ_ONCE(n->nud_state) & NUD_CONNECTED)) { neigh_release(n); goto out; } diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 234799ca527e..c8d39bba2a0d 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -464,7 +464,7 @@ static __always_inline int neigh_event_send_probe(struct neighbour *neigh, if (READ_ONCE(neigh->used) != now) WRITE_ONCE(neigh->used, now); - if (!(neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))) + if (!(READ_ONCE(neigh->nud_state) & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))) return __neigh_event_send(neigh, skb, immediate_ok); return 0; } diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index e5e48c6e35d7..b45c00c01dea 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -192,7 +192,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, if (n) { struct net_bridge_fdb_entry *f; - if (!(n->nud_state & NUD_VALID)) { + if (!(READ_ONCE(n->nud_state) & NUD_VALID)) { neigh_release(n); return; } @@ -452,7 +452,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, if (n) { struct net_bridge_fdb_entry *f; - if (!(n->nud_state & NUD_VALID)) { + if (!(READ_ONCE(n->nud_state) & NUD_VALID)) { neigh_release(n); return; } diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 638a4d5359db..3e3065bc0465 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -277,7 +277,8 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb); int ret; - if ((neigh->nud_state & NUD_CONNECTED) && neigh->hh.hh_len) { + if ((READ_ONCE(neigh->nud_state) & NUD_CONNECTED) && + READ_ONCE(neigh->hh.hh_len)) { neigh_hh_bridge(&neigh->hh, skb); skb->dev = nf_bridge->physindev; ret = br_handle_frame_finish(net, sk, skb); diff --git a/net/core/filter.c b/net/core/filter.c index 50f649f1b4a9..d052fac28d02 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5871,7 +5871,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params, else neigh = __ipv6_neigh_lookup_noref_stub(dev, params->ipv6_dst); - if (!neigh || !(neigh->nud_state & NUD_VALID)) + if (!neigh || !(READ_ONCE(neigh->nud_state) & NUD_VALID)) return BPF_FIB_LKUP_RET_NO_NEIGH; memcpy(params->dmac, neigh->ha, ETH_ALEN); memcpy(params->smac, dev->dev_addr, ETH_ALEN); @@ -5992,7 +5992,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params, * not needed here. */ neigh = __ipv6_neigh_lookup_noref_stub(dev, dst); - if (!neigh || !(neigh->nud_state & NUD_VALID)) + if (!neigh || !(READ_ONCE(neigh->nud_state) & NUD_VALID)) return BPF_FIB_LKUP_RET_NO_NEIGH; memcpy(params->dmac, neigh->ha, ETH_ALEN); memcpy(params->smac, dev->dev_addr, ETH_ALEN); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 0116b0ff91a7..90d399b3f980 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1093,13 +1093,13 @@ static void neigh_timer_handler(struct timer_list *t) neigh->used + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { neigh_dbg(2, "neigh %p is delayed\n", neigh); - neigh->nud_state = NUD_DELAY; + WRITE_ONCE(neigh->nud_state, NUD_DELAY); neigh->updated = jiffies; neigh_suspect(neigh); next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME); } else { neigh_dbg(2, "neigh %p is suspected\n", neigh); - neigh->nud_state = NUD_STALE; + WRITE_ONCE(neigh->nud_state, NUD_STALE); neigh->updated = jiffies; neigh_suspect(neigh); notify = 1; @@ -1109,14 +1109,14 @@ static void neigh_timer_handler(struct timer_list *t) neigh->confirmed + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { neigh_dbg(2, "neigh %p is now reachable\n", neigh); - neigh->nud_state = NUD_REACHABLE; + WRITE_ONCE(neigh->nud_state, NUD_REACHABLE); neigh->updated = jiffies; neigh_connect(neigh); notify = 1; next = neigh->confirmed + neigh->parms->reachable_time; } else { neigh_dbg(2, "neigh %p is probed\n", neigh); - neigh->nud_state = NUD_PROBE; + WRITE_ONCE(neigh->nud_state, NUD_PROBE); neigh->updated = jiffies; atomic_set(&neigh->probes, 0); notify = 1; @@ -1130,7 +1130,7 @@ static void neigh_timer_handler(struct timer_list *t) if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) { - neigh->nud_state = NUD_FAILED; + WRITE_ONCE(neigh->nud_state, NUD_FAILED); notify = 1; neigh_invalidate(neigh); goto out; @@ -1179,7 +1179,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, atomic_set(&neigh->probes, NEIGH_VAR(neigh->parms, UCAST_PROBES)); neigh_del_timer(neigh); - neigh->nud_state = NUD_INCOMPLETE; + WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE); neigh->updated = now; if (!immediate_ok) { next = now + 1; @@ -1191,7 +1191,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, } neigh_add_timer(neigh, next); } else { - neigh->nud_state = NUD_FAILED; + WRITE_ONCE(neigh->nud_state, NUD_FAILED); neigh->updated = jiffies; write_unlock_bh(&neigh->lock); @@ -1201,7 +1201,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, } else if (neigh->nud_state & NUD_STALE) { neigh_dbg(2, "neigh %p is delayed\n", neigh); neigh_del_timer(neigh); - neigh->nud_state = NUD_DELAY; + WRITE_ONCE(neigh->nud_state, NUD_DELAY); neigh->updated = jiffies; neigh_add_timer(neigh, jiffies + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME)); @@ -1313,7 +1313,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, neigh_update_flags(neigh, flags, ¬ify, &gc_update, &managed_update); if (flags & (NEIGH_UPDATE_F_USE | NEIGH_UPDATE_F_MANAGED)) { new = old & ~NUD_PERMANENT; - neigh->nud_state = new; + WRITE_ONCE(neigh->nud_state, new); err = 0; goto out; } @@ -1322,7 +1322,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, neigh_del_timer(neigh); if (old & NUD_CONNECTED) neigh_suspect(neigh); - neigh->nud_state = new; + WRITE_ONCE(neigh->nud_state, new); err = 0; notify = old & NUD_VALID; if ((old & (NUD_INCOMPLETE | NUD_PROBE)) && @@ -1401,7 +1401,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, ((new & NUD_REACHABLE) ? neigh->parms->reachable_time : 0))); - neigh->nud_state = new; + WRITE_ONCE(neigh->nud_state, new); notify = 1; } @@ -1488,7 +1488,7 @@ void __neigh_set_probe_once(struct neighbour *neigh) neigh->updated = jiffies; if (!(neigh->nud_state & NUD_FAILED)) return; - neigh->nud_state = NUD_INCOMPLETE; + WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE); atomic_set(&neigh->probes, neigh_max_probes(neigh)); neigh_add_timer(neigh, jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), @@ -3198,7 +3198,7 @@ static struct neighbour *neigh_get_first(struct seq_file *seq) } if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) break; - if (n->nud_state & ~NUD_NOARP) + if (READ_ONCE(n->nud_state) & ~NUD_NOARP) break; next: n = rcu_dereference_bh(n->next); @@ -3240,7 +3240,7 @@ static struct neighbour *neigh_get_next(struct seq_file *seq, if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) break; - if (n->nud_state & ~NUD_NOARP) + if (READ_ONCE(n->nud_state) & ~NUD_NOARP) break; next: n = rcu_dereference_bh(n->next); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 4f7237661afb..9456f5bb35e5 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -375,7 +375,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); if (probes < 0) { - if (!(neigh->nud_state & NUD_VALID)) + if (!(READ_ONCE(neigh->nud_state) & NUD_VALID)) pr_debug("trying to ucast probe in NUD_INVALID\n"); neigh_ha_snapshot(dst_ha, neigh, dev); dst_hw = dst_ha; @@ -1123,7 +1123,7 @@ static int arp_req_get(struct arpreq *r, struct net_device *dev) neigh = neigh_lookup(&arp_tbl, &ip, dev); if (neigh) { - if (!(neigh->nud_state & NUD_NOARP)) { + if (!(READ_ONCE(neigh->nud_state) & NUD_NOARP)) { read_lock_bh(&neigh->lock); memcpy(r->arp_ha.sa_data, neigh->ha, dev->addr_len); r->arp_flags = arp_state_to_flags(neigh); @@ -1144,12 +1144,12 @@ int arp_invalidate(struct net_device *dev, __be32 ip, bool force) struct neigh_table *tbl = &arp_tbl; if (neigh) { - if ((neigh->nud_state & NUD_VALID) && !force) { + if ((READ_ONCE(neigh->nud_state) & NUD_VALID) && !force) { neigh_release(neigh); return 0; } - if (neigh->nud_state & ~NUD_NOARP) + if (READ_ONCE(neigh->nud_state) & ~NUD_NOARP) err = neigh_update(neigh, NULL, NUD_FAILED, NEIGH_UPDATE_F_OVERRIDE| NEIGH_UPDATE_F_ADMIN, 0); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 3bb890a40ed7..574ff450c4d2 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -563,7 +563,7 @@ static int fib_detect_death(struct fib_info *fi, int order, n = NULL; if (n) { - state = n->nud_state; + state = READ_ONCE(n->nud_state); neigh_release(n); } else { return 0; @@ -2202,7 +2202,7 @@ static bool fib_good_nh(const struct fib_nh *nh) else n = NULL; if (n) - state = n->nud_state; + state = READ_ONCE(n->nud_state); rcu_read_unlock_bh(); } diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index d8ef05347fd9..e28a99f1996b 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -1128,7 +1128,7 @@ static bool ipv6_good_nh(const struct fib6_nh *nh) n = __ipv6_neigh_lookup_noref_stub(nh->fib_nh_dev, &nh->fib_nh_gw6); if (n) - state = n->nud_state; + state = READ_ONCE(n->nud_state); rcu_read_unlock_bh(); @@ -1145,7 +1145,7 @@ static bool ipv4_good_nh(const struct fib_nh *nh) n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev, (__force u32)nh->fib_nh_gw4); if (n) - state = n->nud_state; + state = READ_ONCE(n->nud_state); rcu_read_unlock_bh(); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index de6e3515ab4f..232009d216c4 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -784,7 +784,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow if (!n) n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev); if (!IS_ERR(n)) { - if (!(n->nud_state & NUD_VALID)) { + if (!(READ_ONCE(n->nud_state) & NUD_VALID)) { neigh_event_send(n, NULL); } else { if (fib_lookup(net, fl4, &res, 0) == 0) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 4ce3f9d3bc8a..e5ed39a3c65f 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1153,7 +1153,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, rcu_read_lock_bh(); n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr)); - err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0; + err = n && !(READ_ONCE(n->nud_state) & NUD_VALID) ? -EINVAL : 0; rcu_read_unlock_bh(); if (err) { diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index c4be62c99f73..18634ebd20a4 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -745,7 +745,7 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) saddr = &ipv6_hdr(skb)->saddr; probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); if (probes < 0) { - if (!(neigh->nud_state & NUD_VALID)) { + if (!(READ_ONCE(neigh->nud_state) & NUD_VALID)) { ND_PRINTK(1, dbg, "%s: trying to ucast probe in NUD_INVALID: %pI6\n", __func__, target); @@ -1090,7 +1090,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb) u8 old_flags = neigh->flags; struct net *net = dev_net(dev); - if (neigh->nud_state & NUD_FAILED) + if (READ_ONCE(neigh->nud_state) & NUD_FAILED) goto out; /* diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 0fdb03df2287..25c00c6f5131 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -638,7 +638,7 @@ static void rt6_probe(struct fib6_nh *fib6_nh) idev = __in6_dev_get(dev); neigh = __ipv6_neigh_lookup_noref(dev, nh_gw); if (neigh) { - if (neigh->nud_state & NUD_VALID) + if (READ_ONCE(neigh->nud_state) & NUD_VALID) goto out; write_lock(&neigh->lock); -- cgit v1.2.3 From 69444581d0022b8afced2c90c441b7b4d9b8eba9 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 10 Mar 2023 10:08:08 +0100 Subject: net: dsa: microchip: add ksz_setup_tc_mode() function Add ksz_setup_tc_mode() to make queue scheduling and shaping configuration more visible. Signed-off-by: Oleksij Rempel Acked-by: Arun Ramadoss Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_common.c | 20 ++++++++++++-------- drivers/net/dsa/microchip/ksz_common.h | 6 ++---- 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 729b36eeb2c4..11f20501524f 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -32,10 +32,6 @@ #include "ksz9477.h" #include "lan937x.h" -#define KSZ_CBS_ENABLE ((MTI_SCHEDULE_STRICT_PRIO << MTI_SCHEDULE_MODE_S) | \ - (MTI_SHAPING_SRP << MTI_SHAPING_S)) -#define KSZ_CBS_DISABLE ((MTI_SCHEDULE_WRR << MTI_SCHEDULE_MODE_S) |\ - (MTI_SHAPING_OFF << MTI_SHAPING_S)) #define MIB_COUNTER_NUM 0x20 struct ksz_stats_raw { @@ -3091,6 +3087,14 @@ static int cinc_cal(s32 idle_slope, s32 send_slope, u32 *bw) return 0; } +static int ksz_setup_tc_mode(struct ksz_device *dev, int port, u8 scheduler, + u8 shaper) +{ + return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, + FIELD_PREP(MTI_SCHEDULE_MODE_M, scheduler) | + FIELD_PREP(MTI_SHAPING_M, shaper)); +} + static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, struct tc_cbs_qopt_offload *qopt) { @@ -3110,8 +3114,8 @@ static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, return ret; if (!qopt->enable) - return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, - KSZ_CBS_DISABLE); + return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_WRR, + MTI_SHAPING_OFF); /* High Credit */ ret = ksz_pwrite16(dev, port, REG_PORT_MTI_HI_WATER_MARK, @@ -3136,8 +3140,8 @@ static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, return ret; } - return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, - KSZ_CBS_ENABLE); + return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_STRICT_PRIO, + MTI_SHAPING_SRP); } static int ksz_setup_tc(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index d2d5761d58e9..7d87c9a79fb4 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -654,12 +654,10 @@ static inline int is_lan937x(struct ksz_device *dev) #define REG_PORT_MTI_QUEUE_CTRL_0 0x0914 -#define MTI_SCHEDULE_MODE_M 0x3 -#define MTI_SCHEDULE_MODE_S 6 +#define MTI_SCHEDULE_MODE_M GENMASK(7, 6) #define MTI_SCHEDULE_STRICT_PRIO 0 #define MTI_SCHEDULE_WRR 2 -#define MTI_SHAPING_M 0x3 -#define MTI_SHAPING_S 4 +#define MTI_SHAPING_M GENMASK(5, 4) #define MTI_SHAPING_OFF 0 #define MTI_SHAPING_SRP 1 #define MTI_SHAPING_TIME_AWARE 2 -- cgit v1.2.3 From c570f861fa059ea653599415a7c8cc1dfaf16763 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 10 Mar 2023 10:08:09 +0100 Subject: net: dsa: microchip: add ETS Qdisc support for KSZ9477 series Add ETS Qdisc support for KSZ9477 of switches. Current implementation is limited to strict priority mode. Tested on KSZ8563R with following configuration: tc qdisc replace dev lan2 root handle 1: ets strict 4 \ priomap 3 3 2 2 1 1 0 0 ip link add link lan2 name v1 type vlan id 1 \ egress-qos-map 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 and patched iperf3 version: https://github.com/esnet/iperf/pull/1476 iperf3 -c 172.17.0.1 -b100M -l1472 -t100 -u -R --sock-prio 2 Signed-off-by: Oleksij Rempel Acked-by: Arun Ramadoss Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz_common.c | 218 +++++++++++++++++++++++++++++++++ drivers/net/dsa/microchip/ksz_common.h | 12 ++ 2 files changed, 230 insertions(+) (limited to 'drivers') diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 11f20501524f..5a7ce2aede68 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -1085,6 +1085,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 3, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1224,6 +1225,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 4, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1348,6 +1350,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 3, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1375,6 +1378,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 3, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1407,6 +1411,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1433,6 +1438,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1459,6 +1465,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1489,6 +1496,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1519,6 +1527,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -3144,12 +3153,221 @@ static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, MTI_SHAPING_SRP); } +static int ksz_disable_egress_rate_limit(struct ksz_device *dev, int port) +{ + int queue, ret; + + /* Configuration will not take effect until the last Port Queue X + * Egress Limit Control Register is written. + */ + for (queue = 0; queue < dev->info->num_tx_queues; queue++) { + ret = ksz_pwrite8(dev, port, KSZ9477_REG_PORT_OUT_RATE_0 + queue, + KSZ9477_OUT_RATE_NO_LIMIT); + if (ret) + return ret; + } + + return 0; +} + +static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p, + int band) +{ + /* Compared to queues, bands prioritize packets differently. In strict + * priority mode, the lowest priority is assigned to Queue 0 while the + * highest priority is given to Band 0. + */ + return p->bands - 1 - band; +} + +static int ksz_queue_set_strict(struct ksz_device *dev, int port, int queue) +{ + int ret; + + ret = ksz_pwrite32(dev, port, REG_PORT_MTI_QUEUE_INDEX__4, queue); + if (ret) + return ret; + + return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_STRICT_PRIO, + MTI_SHAPING_OFF); +} + +static int ksz_queue_set_wrr(struct ksz_device *dev, int port, int queue, + int weight) +{ + int ret; + + ret = ksz_pwrite32(dev, port, REG_PORT_MTI_QUEUE_INDEX__4, queue); + if (ret) + return ret; + + ret = ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_WRR, + MTI_SHAPING_OFF); + if (ret) + return ret; + + return ksz_pwrite8(dev, port, KSZ9477_PORT_MTI_QUEUE_CTRL_1, weight); +} + +static int ksz_tc_ets_add(struct ksz_device *dev, int port, + struct tc_ets_qopt_offload_replace_params *p) +{ + int ret, band, tc_prio; + u32 queue_map = 0; + + /* In order to ensure proper prioritization, it is necessary to set the + * rate limit for the related queue to zero. Otherwise strict priority + * or WRR mode will not work. This is a hardware limitation. + */ + ret = ksz_disable_egress_rate_limit(dev, port); + if (ret) + return ret; + + /* Configure queue scheduling mode for all bands. Currently only strict + * prio mode is supported. + */ + for (band = 0; band < p->bands; band++) { + int queue = ksz_ets_band_to_queue(p, band); + + ret = ksz_queue_set_strict(dev, port, queue); + if (ret) + return ret; + } + + /* Configure the mapping between traffic classes and queues. Note: + * priomap variable support 16 traffic classes, but the chip can handle + * only 8 classes. + */ + for (tc_prio = 0; tc_prio < ARRAY_SIZE(p->priomap); tc_prio++) { + int queue; + + if (tc_prio > KSZ9477_MAX_TC_PRIO) + break; + + queue = ksz_ets_band_to_queue(p, p->priomap[tc_prio]); + queue_map |= queue << (tc_prio * KSZ9477_PORT_TC_MAP_S); + } + + return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +} + +static int ksz_tc_ets_del(struct ksz_device *dev, int port) +{ + int ret, queue, tc_prio, s; + u32 queue_map = 0; + + /* To restore the default chip configuration, set all queues to use the + * WRR scheduler with a weight of 1. + */ + for (queue = 0; queue < dev->info->num_tx_queues; queue++) { + ret = ksz_queue_set_wrr(dev, port, queue, + KSZ9477_DEFAULT_WRR_WEIGHT); + if (ret) + return ret; + } + + switch (dev->info->num_tx_queues) { + case 2: + s = 2; + break; + case 4: + s = 1; + break; + case 8: + s = 0; + break; + default: + return -EINVAL; + } + + /* Revert the queue mapping for TC-priority to its default setting on + * the chip. + */ + for (tc_prio = 0; tc_prio <= KSZ9477_MAX_TC_PRIO; tc_prio++) { + int queue; + + queue = tc_prio >> s; + queue_map |= queue << (tc_prio * KSZ9477_PORT_TC_MAP_S); + } + + return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +} + +static int ksz_tc_ets_validate(struct ksz_device *dev, int port, + struct tc_ets_qopt_offload_replace_params *p) +{ + int band; + + /* Since it is not feasible to share one port among multiple qdisc, + * the user must configure all available queues appropriately. + */ + if (p->bands != dev->info->num_tx_queues) { + dev_err(dev->dev, "Not supported amount of bands. It should be %d\n", + dev->info->num_tx_queues); + return -EOPNOTSUPP; + } + + for (band = 0; band < p->bands; ++band) { + /* The KSZ switches utilize a weighted round robin configuration + * where a certain number of packets can be transmitted from a + * queue before the next queue is serviced. For more information + * on this, refer to section 5.2.8.4 of the KSZ8565R + * documentation on the Port Transmit Queue Control 1 Register. + * However, the current ETS Qdisc implementation (as of February + * 2023) assigns a weight to each queue based on the number of + * bytes or extrapolated bandwidth in percentages. Since this + * differs from the KSZ switches' method and we don't want to + * fake support by converting bytes to packets, it is better to + * return an error instead. + */ + if (p->quanta[band]) { + dev_err(dev->dev, "Quanta/weights configuration is not supported.\n"); + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port, + struct tc_ets_qopt_offload *qopt) +{ + struct ksz_device *dev = ds->priv; + int ret; + + if (!dev->info->tc_ets_supported) + return -EOPNOTSUPP; + + if (qopt->parent != TC_H_ROOT) { + dev_err(dev->dev, "Parent should be \"root\"\n"); + return -EOPNOTSUPP; + } + + switch (qopt->command) { + case TC_ETS_REPLACE: + ret = ksz_tc_ets_validate(dev, port, &qopt->replace_params); + if (ret) + return ret; + + return ksz_tc_ets_add(dev, port, &qopt->replace_params); + case TC_ETS_DESTROY: + return ksz_tc_ets_del(dev, port); + case TC_ETS_STATS: + case TC_ETS_GRAFT: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + static int ksz_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data) { switch (type) { case TC_SETUP_QDISC_CBS: return ksz_setup_tc_cbs(ds, port, type_data); + case TC_SETUP_QDISC_ETS: + return ksz_tc_setup_qdisc_ets(ds, port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 7d87c9a79fb4..8abecaf6089e 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -51,6 +51,7 @@ struct ksz_chip_data { u8 port_nirqs; u8 num_tx_queues; bool tc_cbs_supported; + bool tc_ets_supported; const struct ksz_dev_ops *ops; bool phy_errata_9477; bool ksz87xx_eee_link_erratum; @@ -649,6 +650,14 @@ static inline int is_lan937x(struct ksz_device *dev) #define KSZ8_LEGAL_PACKET_SIZE 1518 #define KSZ9477_MAX_FRAME_SIZE 9000 +#define KSZ9477_REG_PORT_OUT_RATE_0 0x0420 +#define KSZ9477_OUT_RATE_NO_LIMIT 0 + +#define KSZ9477_PORT_MRI_TC_MAP__4 0x0808 + +#define KSZ9477_PORT_TC_MAP_S 4 +#define KSZ9477_MAX_TC_PRIO 7 + /* CBS related registers */ #define REG_PORT_MTI_QUEUE_INDEX__4 0x0900 @@ -662,6 +671,9 @@ static inline int is_lan937x(struct ksz_device *dev) #define MTI_SHAPING_SRP 1 #define MTI_SHAPING_TIME_AWARE 2 +#define KSZ9477_PORT_MTI_QUEUE_CTRL_1 0x0915 +#define KSZ9477_DEFAULT_WRR_WEIGHT 1 + #define REG_PORT_MTI_HI_WATER_MARK 0x0916 #define REG_PORT_MTI_LO_WATER_MARK 0x0918 -- cgit v1.2.3 From dc54e450a5dd0fd9147dc9fea0684293569d3609 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Mar 2023 22:46:28 +0100 Subject: net: stmmac: qcom: drop of_match_ptr for ID table The driver is specific to ARCH_QCOM which depends on OF thus the driver is OF-only. Its of_device_id table is built unconditionally, thus of_match_ptr() for ID table does not make sense. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 732774645c1a..32763566c214 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -606,7 +606,7 @@ static struct platform_driver qcom_ethqos_driver = { .driver = { .name = "qcom-ethqos", .pm = &stmmac_pltfr_pm_ops, - .of_match_table = of_match_ptr(qcom_ethqos_match), + .of_match_table = qcom_ethqos_match, }, }; module_platform_driver(qcom_ethqos_driver); -- cgit v1.2.3 From e6512465838bf634d147399c8c96612d3cb419b9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Mar 2023 22:46:29 +0100 Subject: net: stmmac: generic: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it is not relevant here). drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c:72:34: error: ‘dwmac_generic_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c index 5e731a72cce8..ef8f3a940938 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c @@ -91,7 +91,7 @@ static struct platform_driver dwmac_generic_driver = { .driver = { .name = STMMAC_RESOURCE_NAME, .pm = &stmmac_pltfr_pm_ops, - .of_match_table = of_match_ptr(dwmac_generic_match), + .of_match_table = dwmac_generic_match, }, }; module_platform_driver(dwmac_generic_driver); -- cgit v1.2.3 From 69df36d524dbb6f0692252c5f9d41bed9a064ff5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Mar 2023 22:46:30 +0100 Subject: net: marvell: pxa168_eth: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it is not relevant here). drivers/net/ethernet/marvell/pxa168_eth.c:1575:34: error: ‘pxa168_eth_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/pxa168_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 87fff539d39d..d5691b6a2bc5 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1586,7 +1586,7 @@ static struct platform_driver pxa168_eth_driver = { .suspend = pxa168_eth_suspend, .driver = { .name = DRIVER_NAME, - .of_match_table = of_match_ptr(pxa168_eth_of_match), + .of_match_table = pxa168_eth_of_match, }, }; -- cgit v1.2.3 From 7f319fe4363c613f8b00b4bfab66c3659c38378b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Mar 2023 22:46:31 +0100 Subject: net: samsung: sxgbe: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it is not relevant here). drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c:220:34: error: ‘sxgbe_dt_ids’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c index 926532466691..4e5526303f07 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c @@ -229,7 +229,7 @@ static struct platform_driver sxgbe_platform_driver = { .driver = { .name = SXGBE_RESOURCE_NAME, .pm = &sxgbe_platform_pm_ops, - .of_match_table = of_match_ptr(sxgbe_dt_ids), + .of_match_table = sxgbe_dt_ids, }, }; -- cgit v1.2.3 From 7e9aa8cad0844b85a6877cd072c95ac2844eb666 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Mar 2023 22:46:32 +0100 Subject: net: ni: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it is not relevant here). drivers/net/ethernet/ni/nixge.c:1253:34: error: ‘nixge_dt_ids’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/ni/nixge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c index 56e02cba0b8a..0fd156286d4d 100644 --- a/drivers/net/ethernet/ni/nixge.c +++ b/drivers/net/ethernet/ni/nixge.c @@ -1422,7 +1422,7 @@ static struct platform_driver nixge_driver = { .remove = nixge_remove, .driver = { .name = "nixge", - .of_match_table = of_match_ptr(nixge_dt_ids), + .of_match_table = nixge_dt_ids, }, }; module_platform_driver(nixge_driver); -- cgit v1.2.3 From a52ed50a04de179cc866c18bdd1b9a2f59d6bdfc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 12:13:28 +0100 Subject: nfc: trf7970a: mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/nfc/trf7970a.c:2232:34: error: ‘trf7970a_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Simon Horman Reviewed-by: Mark Greer Signed-off-by: David S. Miller --- drivers/nfc/trf7970a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index 21d68664fe08..7eb17f46a815 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -2229,7 +2229,7 @@ static const struct dev_pm_ops trf7970a_pm_ops = { trf7970a_pm_runtime_resume, NULL) }; -static const struct of_device_id trf7970a_of_match[] = { +static const struct of_device_id trf7970a_of_match[] __maybe_unused = { {.compatible = "ti,trf7970a",}, {}, }; -- cgit v1.2.3 From 6ea1e67788f30710d1991de42802cbdeb67afec4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:52 +0100 Subject: net: dsa: lantiq_gswip: mark OF related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested with !CONFIG_OF making certain data unused: drivers/net/dsa/lantiq_gswip.c:1888:34: error: ‘xway_gphy_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/net/dsa/lantiq_gswip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 05ecaa007ab1..3c76a1a14aee 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1885,7 +1885,7 @@ static const struct xway_gphy_match_data xrx300_gphy_data = { .ge_firmware_name = "lantiq/xrx300_phy11g_a21.bin", }; -static const struct of_device_id xway_gphy_match[] = { +static const struct of_device_id xway_gphy_match[] __maybe_unused = { { .compatible = "lantiq,xrx200-gphy-fw", .data = NULL }, { .compatible = "lantiq,xrx200a1x-gphy-fw", .data = &xrx200a1x_gphy_data }, { .compatible = "lantiq,xrx200a2x-gphy-fw", .data = &xrx200a2x_gphy_data }, -- cgit v1.2.3 From ced5c5a0a2ea5eec6ed6cf1fcdde719fdafed82c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:53 +0100 Subject: net: dsa: lan9303: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver will match mostly or only by DT table (even thought there is regular ID table) so there is little benefit in of_match_ptr (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/dsa/lan9303_i2c.c:97:34: error: ‘lan9303_i2c_of_match’ defined but not used [-Werror=unused-const-variable=] drivers/net/dsa/lan9303_mdio.c:157:34: error: ‘lan9303_mdio_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303_i2c.c | 2 +- drivers/net/dsa/lan9303_mdio.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index 1cb41c36bd47..e8844820c3a9 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -103,7 +103,7 @@ MODULE_DEVICE_TABLE(of, lan9303_i2c_of_match); static struct i2c_driver lan9303_i2c_driver = { .driver = { .name = "LAN9303_I2C", - .of_match_table = of_match_ptr(lan9303_i2c_of_match), + .of_match_table = lan9303_i2c_of_match, }, .probe_new = lan9303_i2c_probe, .remove = lan9303_i2c_remove, diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index 4f33369a2de5..d8ab2b77d201 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -164,7 +164,7 @@ MODULE_DEVICE_TABLE(of, lan9303_mdio_of_match); static struct mdio_driver lan9303_mdio_driver = { .mdiodrv.driver = { .name = "LAN9303_MDIO", - .of_match_table = of_match_ptr(lan9303_mdio_of_match), + .of_match_table = lan9303_mdio_of_match, }, .probe = lan9303_mdio_probe, .remove = lan9303_mdio_remove, -- cgit v1.2.3 From 1eb8566dd08db9c7402a26b027086eaabc20ff2a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:54 +0100 Subject: net: dsa: seville_vsc9953: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/dsa/ocelot/seville_vsc9953.c:1070:34: error: ‘seville_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/seville_vsc9953.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 563ad338da25..96d4972a62f0 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -1079,7 +1079,7 @@ static struct platform_driver seville_vsc9953_driver = { .shutdown = seville_shutdown, .driver = { .name = "mscc_seville", - .of_match_table = of_match_ptr(seville_of_match), + .of_match_table = seville_of_match, }, }; module_platform_driver(seville_vsc9953_driver); -- cgit v1.2.3 From 00923ff2e1baf6bc9832f12e44f73439de5fcd72 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:55 +0100 Subject: net: dsa: ksz9477: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver will match mostly by DT table (even thought there is regular ID table) so there is little benefit in of_match_ptr (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/dsa/microchip/ksz9477_i2c.c:84:34: error: ‘ksz9477_dt_ids’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/net/dsa/microchip/ksz9477_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index e315f669ec06..97a317263a2f 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); static struct i2c_driver ksz9477_i2c_driver = { .driver = { .name = "ksz9477-switch", - .of_match_table = of_match_ptr(ksz9477_dt_ids), + .of_match_table = ksz9477_dt_ids, }, .probe_new = ksz9477_i2c_probe, .remove = ksz9477_i2c_remove, -- cgit v1.2.3 From 0f17b42827ae63aaf08cad875d9575b1bca1e066 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:56 +0100 Subject: net: dsa: ocelot: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/dsa/ocelot/ocelot_ext.c:143:34: error: ‘ocelot_ext_switch_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Acked-by: Colin Foster Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/ocelot_ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/dsa/ocelot/ocelot_ext.c b/drivers/net/dsa/ocelot/ocelot_ext.c index 063150659816..228737a32080 100644 --- a/drivers/net/dsa/ocelot/ocelot_ext.c +++ b/drivers/net/dsa/ocelot/ocelot_ext.c @@ -149,7 +149,7 @@ MODULE_DEVICE_TABLE(of, ocelot_ext_switch_of_match); static struct platform_driver ocelot_ext_switch_driver = { .driver = { .name = "ocelot-ext-switch", - .of_match_table = of_match_ptr(ocelot_ext_switch_of_match), + .of_match_table = ocelot_ext_switch_of_match, }, .probe = ocelot_ext_probe, .remove = ocelot_ext_remove, -- cgit v1.2.3 From b0b7d1b6260b3acb9f3f3bc7f3ca88a8898e95b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:57 +0100 Subject: net: phy: ks8995: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver will match mostly by DT table (even thought there is regular ID table) so there is little benefit in of_match_ptr (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/phy/spi_ks8995.c:156:34: error: ‘ks8895_spi_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Signed-off-by: David S. Miller --- drivers/net/phy/spi_ks8995.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index d4202d40d47a..7196e927c2cd 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -491,7 +491,7 @@ static void ks8995_remove(struct spi_device *spi) static struct spi_driver ks8995_driver = { .driver = { .name = "spi-ks8995", - .of_match_table = of_match_ptr(ks8895_spi_of_match), + .of_match_table = ks8895_spi_of_match, }, .probe = ks8995_probe, .remove = ks8995_remove, -- cgit v1.2.3 From 3df09beef650af667be5ec7eee88629e6e479a51 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:58 +0100 Subject: net: ieee802154: adf7242: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver will match mostly by DT table (even thought there is regular ID table) so there is little benefit in of_match_ptr (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/ieee802154/adf7242.c:1322:34: error: ‘adf7242_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Acked-by: Michael Hennerich Signed-off-by: David S. Miller --- drivers/net/ieee802154/adf7242.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c index 5cf218c674a5..509acc86001c 100644 --- a/drivers/net/ieee802154/adf7242.c +++ b/drivers/net/ieee802154/adf7242.c @@ -1336,7 +1336,7 @@ MODULE_DEVICE_TABLE(spi, adf7242_device_id); static struct spi_driver adf7242_driver = { .id_table = adf7242_device_id, .driver = { - .of_match_table = of_match_ptr(adf7242_of_match), + .of_match_table = adf7242_of_match, .name = "adf7242", .owner = THIS_MODULE, }, -- cgit v1.2.3 From 3896c40b7824c8874963e257afc0f464200d2d2c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:32:59 +0100 Subject: net: ieee802154: mcr20a: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver will match mostly by DT table (even thought there is regular ID table) so there is little benefit in of_match_ptr (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/ieee802154/mcr20a.c:1340:34: error: ‘mcr20a_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Acked-by: Stefan Schmidt Signed-off-by: David S. Miller --- drivers/net/ieee802154/mcr20a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/mcr20a.c b/drivers/net/ieee802154/mcr20a.c index f53d185e0568..87abe3b46316 100644 --- a/drivers/net/ieee802154/mcr20a.c +++ b/drivers/net/ieee802154/mcr20a.c @@ -1352,7 +1352,7 @@ MODULE_DEVICE_TABLE(spi, mcr20a_device_id); static struct spi_driver mcr20a_driver = { .id_table = mcr20a_device_id, .driver = { - .of_match_table = of_match_ptr(mcr20a_of_match), + .of_match_table = mcr20a_of_match, .name = "mcr20a", }, .probe = mcr20a_probe, -- cgit v1.2.3 From 32b7030681a488186bf1753b29a170f5d4644721 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:33:00 +0100 Subject: net: ieee802154: at86rf230: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver will match mostly by DT table (even thought there is regular ID table) so there is little benefit in of_match_ptr (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/ieee802154/at86rf230.c:1644:34: error: ‘at86rf230_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Acked-by: Stefan Schmidt Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 62b984f84d9f..164c7f605af5 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1662,7 +1662,7 @@ MODULE_DEVICE_TABLE(spi, at86rf230_device_id); static struct spi_driver at86rf230_driver = { .id_table = at86rf230_device_id, .driver = { - .of_match_table = of_match_ptr(at86rf230_of_match), + .of_match_table = at86rf230_of_match, .name = "at86rf230", }, .probe = at86rf230_probe, -- cgit v1.2.3 From cdfe4fc4d946e2a5d589f73985043674cf298ff6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:33:01 +0100 Subject: net: ieee802154: ca8210: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it might not be relevant here). drivers/net/ieee802154/ca8210.c:3174:34: error: ‘ca8210_of_ids’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Acked-by: Stefan Schmidt Signed-off-by: David S. Miller --- drivers/net/ieee802154/ca8210.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index 0b0c6c0764fe..b5def77e37d7 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -3180,7 +3180,7 @@ static struct spi_driver ca8210_spi_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, - .of_match_table = of_match_ptr(ca8210_of_ids), + .of_match_table = ca8210_of_ids, }, .probe = ca8210_probe, .remove = ca8210_remove -- cgit v1.2.3 From 059fa99723405b47833c7e14e062c7ba7066ca3c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:33:02 +0100 Subject: net: ieee802154: adf7242: drop owner from driver Core already sets owner in spi_driver. Signed-off-by: Krzysztof Kozlowski Acked-by: Michael Hennerich Signed-off-by: David S. Miller --- drivers/net/ieee802154/adf7242.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c index 509acc86001c..f9972b8140f9 100644 --- a/drivers/net/ieee802154/adf7242.c +++ b/drivers/net/ieee802154/adf7242.c @@ -1338,7 +1338,6 @@ static struct spi_driver adf7242_driver = { .driver = { .of_match_table = adf7242_of_match, .name = "adf7242", - .owner = THIS_MODULE, }, .probe = adf7242_probe, .remove = adf7242_remove, -- cgit v1.2.3 From 613a3c44a3737c9a2dfd9f70f9b79427459bb745 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2023 18:33:03 +0100 Subject: net: ieee802154: ca8210: drop owner from driver Core already sets owner in spi_driver. Signed-off-by: Krzysztof Kozlowski Acked-by: Stefan Schmidt Signed-off-by: David S. Miller --- drivers/net/ieee802154/ca8210.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index b5def77e37d7..1eada1db8dcf 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -3179,7 +3179,6 @@ MODULE_DEVICE_TABLE(of, ca8210_of_ids); static struct spi_driver ca8210_spi_driver = { .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .of_match_table = ca8210_of_ids, }, .probe = ca8210_probe, -- cgit v1.2.3 From 45ef71d108e6f1545e2981d8af03be13f2e39411 Mon Sep 17 00:00:00 2001 From: Josef Miegl Date: Sun, 12 Mar 2023 17:45:57 +0100 Subject: net: geneve: set IFF_POINTOPOINT with IFLA_GENEVE_INNER_PROTO_INHERIT The GENEVE tunnel used with IFLA_GENEVE_INNER_PROTO_INHERIT is point-to-point, so set IFF_POINTOPOINT to reflect that. Signed-off-by: Josef Miegl Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/geneve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 89ff7f8e8c7e..5c3c7169c0a2 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1426,7 +1426,7 @@ static int geneve_configure(struct net *net, struct net_device *dev, dev->type = ARPHRD_NONE; dev->hard_header_len = 0; dev->addr_len = 0; - dev->flags = IFF_NOARP; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; } err = register_netdevice(dev); -- cgit v1.2.3 From f947568e258038d3c2f8f38a9a7dabaca36643ec Mon Sep 17 00:00:00 2001 From: Stefan Raspl Date: Mon, 13 Mar 2023 11:10:31 +0100 Subject: net/smc: Introduce explicit check for v2 support Previously, v2 support was derived from a very specific format of the SEID as part of the SMC-D codebase. Make this part of the SMC-D device API, so implementers do not need to adhere to a specific SEID format. Signed-off-by: Stefan Raspl Reviewed-and-tested-by: Jan Karcher Reviewed-by: Wenjia Zhang Signed-off-by: Wenjia Zhang Reviewed-by: Tony Lu Signed-off-by: David S. Miller --- drivers/s390/net/ism_drv.c | 7 +++++++ include/net/smc.h | 1 + net/smc/smc_ism.c | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index eb7e13486087..1c73d32966f1 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -842,6 +842,12 @@ static int smcd_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx, return ism_move(smcd->priv, dmb_tok, idx, sf, offset, data, size); } +static int smcd_supports_v2(void) +{ + return SYSTEM_EID.serial_number[0] != '0' || + SYSTEM_EID.type[0] != '0'; +} + static u64 smcd_get_local_gid(struct smcd_dev *smcd) { return ism_get_local_gid(smcd->priv); @@ -869,6 +875,7 @@ static const struct smcd_ops ism_ops = { .reset_vlan_required = smcd_reset_vlan_required, .signal_event = smcd_signal_ieq, .move_data = smcd_move, + .supports_v2 = smcd_supports_v2, .get_system_eid = ism_get_seid, .get_local_gid = smcd_get_local_gid, .get_chid = smcd_get_chid, diff --git a/include/net/smc.h b/include/net/smc.h index 597cb9381182..a002552be29c 100644 --- a/include/net/smc.h +++ b/include/net/smc.h @@ -67,6 +67,7 @@ struct smcd_ops { int (*move_data)(struct smcd_dev *dev, u64 dmb_tok, unsigned int idx, bool sf, unsigned int offset, void *data, unsigned int size); + int (*supports_v2)(void); u8* (*get_system_eid)(void); u64 (*get_local_gid)(struct smcd_dev *dev); u16 (*get_chid)(struct smcd_dev *dev); diff --git a/net/smc/smc_ism.c b/net/smc/smc_ism.c index 3b0b7710c6b0..fbee2493091f 100644 --- a/net/smc/smc_ism.c +++ b/net/smc/smc_ism.c @@ -429,7 +429,7 @@ static void smcd_register_dev(struct ism_dev *ism) u8 *system_eid = NULL; system_eid = smcd->ops->get_system_eid(); - if (system_eid[24] != '0' || system_eid[28] != '0') { + if (smcd->ops->supports_v2()) { smc_ism_v2_capable = true; memcpy(smc_ism_v2_system_eid, system_eid, SMC_MAX_EID_LEN); -- cgit v1.2.3 From 298c91dc40e51395198533433716d0521ed10995 Mon Sep 17 00:00:00 2001 From: Stefan Raspl Date: Mon, 13 Mar 2023 11:10:32 +0100 Subject: net/ism: Remove extra include Signed-off-by: Stefan Raspl Signed-off-by: Wenjia Zhang Reviewed-by: Tony Lu Signed-off-by: David S. Miller --- drivers/s390/net/ism_drv.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index 1c73d32966f1..05749c877990 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From f94d7a3a51070d185e9203b53a3d669e27931e3d Mon Sep 17 00:00:00 2001 From: Bastian Germann Date: Mon, 6 Mar 2023 13:50:40 +0100 Subject: wifi: ath9k: Remove Qwest/Actiontec 802AIN ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The USB device 1668:1200 is Qwest/Actiontec 802AIN which is also correctly claimed to be supported by carl9170. Supposedly, the successor 802AIN2 has an ath9k compatible chip whose USB ID (unknown) could be inserted instead. Drop the ID from the wrong driver. I happened to find this by chance while packaging the ath9k and carl9170 firmware for Debian. Signed-off-by: Bastian Germann Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230306125041.2221-1-bage@debian.org --- drivers/net/wireless/ath/ath9k/hif_usb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index e0130beb304d..27ff1ca2631f 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -42,8 +42,6 @@ static const struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x7015), .driver_info = AR9287_USB }, /* Atheros */ - { USB_DEVICE(0x1668, 0x1200), - .driver_info = AR9287_USB }, /* Verizon */ { USB_DEVICE(0x0cf3, 0x7010), .driver_info = AR9280_USB }, /* Atheros */ -- cgit v1.2.3 From f94557154d9fc77c392844523388edd4661a27a3 Mon Sep 17 00:00:00 2001 From: Vladimir Lypak Date: Sat, 11 Mar 2023 20:36:47 +0530 Subject: wifi: wcn36xx: add support for pronto-v3 Pronto v3 has a different DXE address than prior Pronto versions. This patch changes the macro to return the correct register address based on the pronto version. Signed-off-by: Vladimir Lypak Signed-off-by: Sireesh Kodali Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230311150647.22935-2-sireeshkodali1@gmail.com --- drivers/net/wireless/ath/wcn36xx/dxe.c | 23 ++++++++++++----------- drivers/net/wireless/ath/wcn36xx/dxe.h | 4 ++-- drivers/net/wireless/ath/wcn36xx/main.c | 1 + drivers/net/wireless/ath/wcn36xx/wcn36xx.h | 1 + 4 files changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 4e9e13941c8f..9013f056eecb 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -112,8 +112,8 @@ int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn) wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L; wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H; - wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L; - wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H; + wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L(wcn); + wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H(wcn); wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD; wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD; @@ -165,8 +165,9 @@ void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn) wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch); } -static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn_ch) +static int wcn36xx_dxe_init_descs(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *wcn_ch) { + struct device *dev = wcn->dev; struct wcn36xx_dxe_desc *cur_dxe = NULL; struct wcn36xx_dxe_desc *prev_dxe = NULL; struct wcn36xx_dxe_ctl *cur_ctl = NULL; @@ -190,11 +191,11 @@ static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn switch (wcn_ch->ch_type) { case WCN36XX_DXE_CH_TX_L: cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L; - cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L(wcn); break; case WCN36XX_DXE_CH_TX_H: cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H; - cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H(wcn); break; case WCN36XX_DXE_CH_RX_L: cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; @@ -914,7 +915,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /***************************************/ /* Init descriptors for TX LOW channel */ /***************************************/ - ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch); + ret = wcn36xx_dxe_init_descs(wcn, &wcn->dxe_tx_l_ch); if (ret) { dev_err(wcn->dev, "Error allocating descriptor\n"); return ret; @@ -928,14 +929,14 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /* Program DMA destination addr for TX LOW */ wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_DEST_ADDR_TX_L, - WCN36XX_DXE_WQ_TX_L); + WCN36XX_DXE_WQ_TX_L(wcn)); wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); /***************************************/ /* Init descriptors for TX HIGH channel */ /***************************************/ - ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_h_ch); + ret = wcn36xx_dxe_init_descs(wcn, &wcn->dxe_tx_h_ch); if (ret) { dev_err(wcn->dev, "Error allocating descriptor\n"); goto out_err_txh_ch; @@ -950,14 +951,14 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /* Program DMA destination addr for TX HIGH */ wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_DEST_ADDR_TX_H, - WCN36XX_DXE_WQ_TX_H); + WCN36XX_DXE_WQ_TX_H(wcn)); wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); /***************************************/ /* Init descriptors for RX LOW channel */ /***************************************/ - ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_l_ch); + ret = wcn36xx_dxe_init_descs(wcn, &wcn->dxe_rx_l_ch); if (ret) { dev_err(wcn->dev, "Error allocating descriptor\n"); goto out_err_rxl_ch; @@ -988,7 +989,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) /***************************************/ /* Init descriptors for RX HIGH channel */ /***************************************/ - ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_h_ch); + ret = wcn36xx_dxe_init_descs(wcn, &wcn->dxe_rx_h_ch); if (ret) { dev_err(wcn->dev, "Error allocating descriptor\n"); goto out_err_rxh_ch; diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h index 26a31edf52e9..dd8c684a3ba7 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.h +++ b/drivers/net/wireless/ath/wcn36xx/dxe.h @@ -135,8 +135,8 @@ H2H_TEST_RX_TX = DMA2 WCN36xx_DXE_CTRL_ENDIANNESS) /* TODO This must calculated properly but not hardcoded */ -#define WCN36XX_DXE_WQ_TX_L 0x17 -#define WCN36XX_DXE_WQ_TX_H 0x17 +#define WCN36XX_DXE_WQ_TX_L(wcn) ((wcn)->is_pronto_v3 ? 0x6 : 0x17) +#define WCN36XX_DXE_WQ_TX_H(wcn) ((wcn)->is_pronto_v3 ? 0x6 : 0x17) #define WCN36XX_DXE_WQ_RX_L 0xB #define WCN36XX_DXE_WQ_RX_H 0x4 diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 3b79cc1c7c5b..8dbd115a393c 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1508,6 +1508,7 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, } wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto"); + wcn->is_pronto_v3 = !!of_device_is_compatible(mmio_node, "qcom,pronto-v3-pil"); /* Map the CCU memory */ index = of_property_match_string(mmio_node, "reg-names", "ccu"); diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index 9aa08b636d08..ff4a8e5d7209 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -217,6 +217,7 @@ struct wcn36xx { u8 fw_major; u32 fw_feat_caps[WCN36XX_HAL_CAPS_SIZE]; bool is_pronto; + bool is_pronto_v3; /* extra byte for the NULL termination */ u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1]; -- cgit v1.2.3 From 488d9a484f96eee4f0e8e108aed42a057a1c7295 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Tue, 7 Mar 2023 02:47:06 -0800 Subject: wifi: ath12k: Add missing unwind goto in ath12k_pci_probe() Smatch Warns: drivers/net/wireless/ath/ath12k/pci.c:1198 ath12k_pci_probe() warn: missing unwind goto? Store the error value in ret and use correct label with a goto. Only Compile tested, found with Smatch. Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Reported-by: Dan Carpenter Link: https://lore.kernel.org/all/Y+426q6cfkEdb5Bv@kili/ Suggested-by: Dan Carpenter Signed-off-by: Harshit Mogalapalli Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230307104706.240119-1-harshit.m.mogalapalli@oracle.com --- drivers/net/wireless/ath/ath12k/pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index d32637b0113d..00b0080dbac3 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1223,7 +1223,8 @@ static int ath12k_pci_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "Unknown hardware version found for QCN9274: 0x%x\n", soc_hw_version_major); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto err_pci_free_region; } break; case WCN7850_DEVICE_ID: -- cgit v1.2.3 From 3b1088a09ec9438523c251d8435e78988824bc0d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 7 Mar 2023 16:22:39 -0600 Subject: wifi: ath11k: Replace fake flex-array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zero-length arrays as fake flexible arrays are deprecated and we are moving towards adopting C99 flexible-array members instead. Address 25 of the following warnings found with GCC-13 and -fstrict-flex-arrays=3 enabled: drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c:30:51: warning: array subscript is outside array bounds of ‘const u32[0]’ {aka ‘const unsigned int[]’} [-Warray-bounds=] This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy() and help us make progress towards globally enabling -fstrict-flex-arrays=3 [1]. Link: https://github.com/KSPP/linux/issues/21 Link: https://github.com/KSPP/linux/issues/266 Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Simon Horman Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/ZAe5L5DtmsQxzqRH@work --- .../net/wireless/ath/ath11k/debugfs_htt_stats.h | 73 +++++++++++++--------- 1 file changed, 43 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h index 2b97cbbd28cb..0bbd58a380de 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h @@ -143,7 +143,8 @@ enum htt_tx_pdev_underrun_enum { /* Bytes stored in little endian order */ /* Length should be multiple of DWORD */ struct htt_stats_string_tlv { - u32 data[0]; /* Can be variable length */ + /* Can be variable length */ + DECLARE_FLEX_ARRAY(u32, data); } __packed; #define HTT_STATS_MAC_ID GENMASK(7, 0) @@ -205,27 +206,32 @@ struct htt_tx_pdev_stats_cmn_tlv { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_pdev_stats_urrn_tlv_v { - u32 urrn_stats[0]; /* HTT_TX_PDEV_MAX_URRN_STATS */ + /* HTT_TX_PDEV_MAX_URRN_STATS */ + DECLARE_FLEX_ARRAY(u32, urrn_stats); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_pdev_stats_flush_tlv_v { - u32 flush_errs[0]; /* HTT_TX_PDEV_MAX_FLUSH_REASON_STATS */ + /* HTT_TX_PDEV_MAX_FLUSH_REASON_STATS */ + DECLARE_FLEX_ARRAY(u32, flush_errs); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_pdev_stats_sifs_tlv_v { - u32 sifs_status[0]; /* HTT_TX_PDEV_MAX_SIFS_BURST_STATS */ + /* HTT_TX_PDEV_MAX_SIFS_BURST_STATS */ + DECLARE_FLEX_ARRAY(u32, sifs_status); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_pdev_stats_phy_err_tlv_v { - u32 phy_errs[0]; /* HTT_TX_PDEV_MAX_PHY_ERR_STATS */ + /* HTT_TX_PDEV_MAX_PHY_ERR_STATS */ + DECLARE_FLEX_ARRAY(u32, phy_errs); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_pdev_stats_sifs_hist_tlv_v { - u32 sifs_hist_status[0]; /* HTT_TX_PDEV_SIFS_BURST_HIST_STATS */ + /* HTT_TX_PDEV_SIFS_BURST_HIST_STATS */ + DECLARE_FLEX_ARRAY(u32, sifs_hist_status); }; struct htt_tx_pdev_stats_tx_ppdu_stats_tlv_v { @@ -590,20 +596,20 @@ struct htt_tx_hwq_difs_latency_stats_tlv_v { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_hwq_cmd_result_stats_tlv_v { - /* Histogram of sched cmd result */ - u32 cmd_result[0]; /* HTT_TX_HWQ_MAX_CMD_RESULT_STATS */ + /* Histogram of sched cmd result, HTT_TX_HWQ_MAX_CMD_RESULT_STATS */ + DECLARE_FLEX_ARRAY(u32, cmd_result); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_hwq_cmd_stall_stats_tlv_v { - /* Histogram of various pause conitions */ - u32 cmd_stall_status[0]; /* HTT_TX_HWQ_MAX_CMD_STALL_STATS */ + /* Histogram of various pause conitions, HTT_TX_HWQ_MAX_CMD_STALL_STATS */ + DECLARE_FLEX_ARRAY(u32, cmd_stall_status); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_hwq_fes_result_stats_tlv_v { - /* Histogram of number of user fes result */ - u32 fes_result[0]; /* HTT_TX_HWQ_MAX_FES_RESULT_STATS */ + /* Histogram of number of user fes result, HTT_TX_HWQ_MAX_FES_RESULT_STATS */ + DECLARE_FLEX_ARRAY(u32, fes_result); }; /* NOTE: Variable length TLV, use length spec to infer array size @@ -635,8 +641,8 @@ struct htt_tx_hwq_tried_mpdu_cnt_hist_tlv_v { * #define WAL_TXOP_USED_HISTOGRAM_INTERVAL 1000 ( 1 ms ) */ struct htt_tx_hwq_txop_used_cnt_hist_tlv_v { - /* Histogram of txop used cnt */ - u32 txop_used_cnt_hist[0]; /* HTT_TX_HWQ_TXOP_USED_CNT_HIST */ + /* Histogram of txop used cnt, HTT_TX_HWQ_TXOP_USED_CNT_HIST */ + DECLARE_FLEX_ARRAY(u32, txop_used_cnt_hist); }; /* == TX SELFGEN STATS == */ @@ -804,17 +810,20 @@ struct htt_tx_pdev_mpdu_stats_tlv { /* == TX SCHED STATS == */ /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_sched_txq_cmd_posted_tlv_v { - u32 sched_cmd_posted[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ + /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ + DECLARE_FLEX_ARRAY(u32, sched_cmd_posted); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_sched_txq_cmd_reaped_tlv_v { - u32 sched_cmd_reaped[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ + /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ + DECLARE_FLEX_ARRAY(u32, sched_cmd_reaped); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_sched_txq_sched_order_su_tlv_v { - u32 sched_order_su[0]; /* HTT_TX_PDEV_NUM_SCHED_ORDER_LOG */ + /* HTT_TX_PDEV_NUM_SCHED_ORDER_LOG */ + DECLARE_FLEX_ARRAY(u32, sched_order_su); }; enum htt_sched_txq_sched_ineligibility_tlv_enum { @@ -842,7 +851,7 @@ enum htt_sched_txq_sched_ineligibility_tlv_enum { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_sched_txq_sched_ineligibility_tlv_v { /* indexed by htt_sched_txq_sched_ineligibility_tlv_enum */ - u32 sched_ineligibility[0]; + DECLARE_FLEX_ARRAY(u32, sched_ineligibility); }; #define HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) @@ -888,18 +897,20 @@ struct htt_stats_tx_sched_cmn_tlv { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_tqm_gen_mpdu_stats_tlv_v { - u32 gen_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_GEN_MPDU_END_REASON */ + /* HTT_TX_TQM_MAX_GEN_MPDU_END_REASON */ + DECLARE_FLEX_ARRAY(u32, gen_mpdu_end_reason); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_tqm_list_mpdu_stats_tlv_v { - u32 list_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_LIST_MPDU_END_REASON */ + /* HTT_TX_TQM_MAX_LIST_MPDU_END_REASON */ + DECLARE_FLEX_ARRAY(u32, list_mpdu_end_reason); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_tx_tqm_list_mpdu_cnt_tlv_v { - u32 list_mpdu_cnt_hist[0]; - /* HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS */ + /* HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS */ + DECLARE_FLEX_ARRAY(u32, list_mpdu_cnt_hist); }; struct htt_tx_tqm_pdev_stats_tlv_v { @@ -1098,7 +1109,7 @@ struct htt_tx_de_compl_stats_tlv { * ENTRIES_PER_BIN_COUNT) */ struct htt_tx_de_fw2wbm_ring_full_hist_tlv { - u32 fw2wbm_ring_full_hist[0]; + DECLARE_FLEX_ARRAY(u32, fw2wbm_ring_full_hist); }; struct htt_tx_de_cmn_stats_tlv { @@ -1151,7 +1162,7 @@ struct htt_ring_if_cmn_tlv { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_sfm_client_user_tlv_v { /* Number of DWORDS used per user and per client */ - u32 dwords_used_by_user_n[0]; + DECLARE_FLEX_ARRAY(u32, dwords_used_by_user_n); }; struct htt_sfm_client_tlv { @@ -1436,12 +1447,14 @@ struct htt_rx_soc_fw_stats_tlv { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_rx_soc_fw_refill_ring_empty_tlv_v { - u32 refill_ring_empty_cnt[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ + /* HTT_RX_STATS_REFILL_MAX_RING */ + DECLARE_FLEX_ARRAY(u32, refill_ring_empty_cnt); }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_rx_soc_fw_refill_ring_num_refill_tlv_v { - u32 refill_ring_num_refill[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ + /* HTT_RX_STATS_REFILL_MAX_RING */ + DECLARE_FLEX_ARRAY(u32, refill_ring_num_refill); }; /* RXDMA error code from WBM released packets */ @@ -1473,7 +1486,7 @@ enum htt_rx_rxdma_error_code_enum { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v { - u32 rxdma_err[0]; /* HTT_RX_RXDMA_MAX_ERR_CODE */ + DECLARE_FLEX_ARRAY(u32, rxdma_err); /* HTT_RX_RXDMA_MAX_ERR_CODE */ }; /* REO error code from WBM released packets */ @@ -1505,7 +1518,7 @@ enum htt_rx_reo_error_code_enum { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_rx_soc_fw_refill_ring_num_reo_err_tlv_v { - u32 reo_err[0]; /* HTT_RX_REO_MAX_ERR_CODE */ + DECLARE_FLEX_ARRAY(u32, reo_err); /* HTT_RX_REO_MAX_ERR_CODE */ }; /* == RX PDEV STATS == */ @@ -1622,13 +1635,13 @@ struct htt_rx_pdev_fw_stats_phy_err_tlv { /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_rx_pdev_fw_ring_mpdu_err_tlv_v { /* Num error MPDU for each RxDMA error type */ - u32 fw_ring_mpdu_err[0]; /* HTT_RX_STATS_RXDMA_MAX_ERR */ + DECLARE_FLEX_ARRAY(u32, fw_ring_mpdu_err); /* HTT_RX_STATS_RXDMA_MAX_ERR */ }; /* NOTE: Variable length TLV, use length spec to infer array size */ struct htt_rx_pdev_fw_mpdu_drop_tlv_v { /* Num MPDU dropped */ - u32 fw_mpdu_drop[0]; /* HTT_RX_STATS_FW_DROP_REASON_MAX */ + DECLARE_FLEX_ARRAY(u32, fw_mpdu_drop); /* HTT_RX_STATS_FW_DROP_REASON_MAX */ }; #define HTT_PDEV_CCA_STATS_TX_FRAME_INFO_PRESENT (0x1) -- cgit v1.2.3 From 5a78ac33e3cb8822da64dd1af196e83664b332b0 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Thu, 9 Mar 2023 15:23:08 +0530 Subject: wifi: ath11k: fix deinitialization of firmware resources Currently, in ath11k_ahb_fw_resources_init(), iommu domain mapping is done only for the chipsets having fixed firmware memory. Also, for such chipsets, mapping is done only if it does not have TrustZone support. During deinitialization, only if TrustZone support is not there, iommu is unmapped back. However, for non fixed firmware memory chipsets, TrustZone support is not there and this makes the condition check to true and it tries to unmap the memory which was not mapped during initialization. This leads to the following trace - [ 83.198790] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008 [ 83.259537] Modules linked in: ath11k_ahb ath11k qmi_helpers .. snip .. [ 83.280286] pstate: 20000005 (nzCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 83.287228] pc : __iommu_unmap+0x30/0x140 [ 83.293907] lr : iommu_unmap+0x5c/0xa4 [ 83.298072] sp : ffff80000b3abad0 .. snip .. [ 83.369175] Call trace: [ 83.376282] __iommu_unmap+0x30/0x140 [ 83.378541] iommu_unmap+0x5c/0xa4 [ 83.382360] ath11k_ahb_fw_resource_deinit.part.12+0x2c/0xac [ath11k_ahb] [ 83.385666] ath11k_ahb_free_resources+0x140/0x17c [ath11k_ahb] [ 83.392521] ath11k_ahb_shutdown+0x34/0x40 [ath11k_ahb] [ 83.398248] platform_shutdown+0x20/0x2c [ 83.403455] device_shutdown+0x16c/0x1c4 [ 83.407621] kernel_restart_prepare+0x34/0x3c [ 83.411529] kernel_restart+0x14/0x74 [ 83.415781] __do_sys_reboot+0x1c4/0x22c [ 83.419427] __arm64_sys_reboot+0x1c/0x24 [ 83.423420] invoke_syscall+0x44/0xfc [ 83.427326] el0_svc_common.constprop.3+0xac/0xe8 [ 83.430974] do_el0_svc+0xa0/0xa8 [ 83.435659] el0_svc+0x1c/0x44 [ 83.438957] el0t_64_sync_handler+0x60/0x144 [ 83.441910] el0t_64_sync+0x15c/0x160 [ 83.446343] Code: aa0103f4 f9400001 f90027a1 d2800001 (f94006a0) [ 83.449903] ---[ end trace 0000000000000000 ]--- This can be reproduced by probing an AHB chipset which is not having a fixed memory region. During reboot (or rmmod) trace can be seen. Fix this issue by adding a condition check on firmware fixed memory hw_param as done in the counter initialization function. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 Fixes: f9eec4947add ("ath11k: Add support for targets without trustzone") Signed-off-by: Aditya Kumar Singh Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20230309095308.24937-1-quic_adisi@quicinc.com --- drivers/net/wireless/ath/ath11k/ahb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index b549576d0b51..5cbba9a8b6ba 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1078,6 +1078,12 @@ static int ath11k_ahb_fw_resource_deinit(struct ath11k_base *ab) struct iommu_domain *iommu; size_t unmapped_size; + /* Chipsets not requiring MSA would have not initialized + * MSA resources, return success in such cases. + */ + if (!ab->hw_params.fixed_fw_mem) + return 0; + if (ab_ahb->fw.use_tz) return 0; -- cgit v1.2.3 From 0936998393c7dffd68ba5d73eabdabf8dddbbc41 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Mar 2023 19:49:13 +0200 Subject: wifi: iwlwifi: mvm: avoid sta lookup in queue alloc In FW restart scenarios, we allocate the queues from the iwl_mvm_realloc_queues_after_restart() function, but that is called before we insert the station ID into our map (mvm->fw_id_to_mac_id). However, in all cases where we're actually allocating a queue for a "real" (not bcast, aux, ...) station we have the sta pointer already, so just pass it along to use it instead of looking it up. This fixes an issue where after restart we only allocated a queue of size 16 (due to the ordering issue described above), and thus never got good throughput again since no aggregates could be formed on transmit. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.4d70868003e8.I3476fee5c12f5b1af2be5e2f38a9df7d66d02b62@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 50 +++++++++++++++------------- 1 file changed, 27 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 69634fb82a9b..46af2b422849 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -766,7 +766,25 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, return -ENOSPC; } +static int iwl_mvm_get_queue_size(struct ieee80211_sta *sta) +{ + /* this queue isn't used for traffic (cab_queue) */ + if (!sta) + return IWL_MGMT_QUEUE_SIZE; + + /* support for 1k ba size */ + if (sta->deflink.eht_cap.has_eht) + return IWL_DEFAULT_QUEUE_SIZE_EHT; + + /* support for 256 ba size */ + if (sta->deflink.he_cap.has_he) + return IWL_DEFAULT_QUEUE_SIZE_HE; + + return IWL_DEFAULT_QUEUE_SIZE; +} + static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, u8 sta_id, u8 tid, unsigned int timeout) { int queue, size; @@ -776,22 +794,7 @@ static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, size = max_t(u32, IWL_MGMT_QUEUE_SIZE, mvm->trans->cfg->min_txq_size); } else { - struct ieee80211_sta *sta; - - rcu_read_lock(); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - - /* this queue isn't used for traffic (cab_queue) */ - if (IS_ERR_OR_NULL(sta)) { - size = IWL_MGMT_QUEUE_SIZE; - } else if (sta->deflink.he_cap.has_he) { - /* support for 256 ba size */ - size = IWL_DEFAULT_QUEUE_SIZE_HE; - } else { - size = IWL_DEFAULT_QUEUE_SIZE; - } - - rcu_read_unlock(); + size = iwl_mvm_get_queue_size(sta); } /* take the min with bc tbl entries allowed */ @@ -836,7 +839,8 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm, IWL_DEBUG_TX_QUEUES(mvm, "Allocating queue for sta %d on tid %d\n", mvmsta->sta_id, tid); - queue = iwl_mvm_tvqm_enable_txq(mvm, mvmsta->sta_id, tid, wdg_timeout); + queue = iwl_mvm_tvqm_enable_txq(mvm, sta, mvmsta->sta_id, + tid, wdg_timeout); if (queue < 0) return queue; @@ -1537,7 +1541,8 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, IWL_DEBUG_TX_QUEUES(mvm, "Re-mapping sta %d tid %d\n", mvm_sta->sta_id, i); - txq_id = iwl_mvm_tvqm_enable_txq(mvm, mvm_sta->sta_id, + txq_id = iwl_mvm_tvqm_enable_txq(mvm, sta, + mvm_sta->sta_id, i, wdg); /* * on failures, just set it to IWL_MVM_INVALID_QUEUE @@ -2049,7 +2054,7 @@ static int iwl_mvm_enable_aux_snif_queue_tvqm(struct iwl_mvm *mvm, u8 sta_id) WARN_ON(!iwl_mvm_has_new_tx_api(mvm)); - return iwl_mvm_tvqm_enable_txq(mvm, sta_id, IWL_MAX_TID_COUNT, + return iwl_mvm_tvqm_enable_txq(mvm, NULL, sta_id, IWL_MAX_TID_COUNT, wdg_timeout); } @@ -2233,7 +2238,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * to firmware so enable queue here - after the station was added */ if (iwl_mvm_has_new_tx_api(mvm)) { - queue = iwl_mvm_tvqm_enable_txq(mvm, bsta->sta_id, + queue = iwl_mvm_tvqm_enable_txq(mvm, NULL, bsta->sta_id, IWL_MAX_TID_COUNT, wdg_timeout); if (queue < 0) { @@ -2427,9 +2432,8 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * tfd_queue_mask. */ if (iwl_mvm_has_new_tx_api(mvm)) { - int queue = iwl_mvm_tvqm_enable_txq(mvm, msta->sta_id, - 0, - timeout); + int queue = iwl_mvm_tvqm_enable_txq(mvm, NULL, msta->sta_id, + 0, timeout); if (queue < 0) { ret = queue; goto err; -- cgit v1.2.3 From 51fa8c026e072dac49ca638d66a64a66b29e916f Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Tue, 14 Mar 2023 19:49:14 +0200 Subject: wifi: iwlwifi: yoyo: Add new tlv for dump file name extension Add tlv in dump file for dump file name extension. Signed-off-by: Mukesh Sisodiya Signed-off-by: Greenman, Gregory Link: https://lore.kernel.org/r/20230314194113.ffc28212994e.Ie5f10709548497061f95c1634d942dd2facf72ec@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 32 +++++++++++++++++++++- drivers/net/wireless/intel/iwlwifi/fw/error-dump.h | 17 +++++++++++- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 +++ 3 files changed, 51 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index abf49022edbe..ca97f2fcb693 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2320,6 +2320,34 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, return entry->size; } +static u32 iwl_dump_ini_file_name_info(struct iwl_fw_runtime *fwrt, + struct list_head *list) +{ + struct iwl_fw_ini_dump_entry *entry; + struct iwl_dump_file_name_info *tlv; + u32 len = strnlen(fwrt->trans->dbg.dump_file_name_ext, + IWL_FW_INI_MAX_NAME); + + if (!fwrt->trans->dbg.dump_file_name_ext_valid) + return 0; + + entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + len); + if (!entry) + return 0; + + entry->size = sizeof(*tlv) + len; + + tlv = (void *)entry->data; + tlv->type = cpu_to_le32(IWL_INI_DUMP_NAME_TYPE); + tlv->len = cpu_to_le32(len); + memcpy(tlv->data, fwrt->trans->dbg.dump_file_name_ext, len); + + /* add the dump file name extension tlv to the list */ + list_add_tail(&entry->list, list); + + return entry->size; +} + static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { [IWL_FW_INI_REGION_INVALID] = {}, [IWL_FW_INI_REGION_INTERNAL_BUFFER] = { @@ -2495,8 +2523,10 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, size += iwl_dump_ini_mem(fwrt, list, ®_data, &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); - if (size) + if (size) { + size += iwl_dump_ini_file_name_info(fwrt, list); size += iwl_dump_ini_info(fwrt, trigger, list); + } return size; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index c62576e442bd..f5e08988dc7b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2014, 2018-2021 Intel Corporation + * Copyright (C) 2014, 2018-2022 Intel Corporation * Copyright (C) 2014-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -75,6 +75,18 @@ struct iwl_fw_error_dump_data { __u8 data[]; } __packed; +/** + * struct iwl_dump_file_name_info - data for dump file name addition + * @type: region type with reserved bits + * @len: the length of file name string to be added to dump file + * @data: the string need to be added to dump file + */ +struct iwl_dump_file_name_info { + __le32 type; + __le32 len; + __u8 data[]; +} __packed; + /** * struct iwl_fw_error_dump_file - the layout of the header of the file * @barker: must be %IWL_FW_ERROR_DUMP_BARKER @@ -231,6 +243,9 @@ struct iwl_fw_error_dump_mem { /* Use bit 31 as dump info type to avoid colliding with region types */ #define IWL_INI_DUMP_INFO_TYPE BIT(31) +/* Use bit 31 and bit 24 as dump name type to avoid colliding with region types */ +#define IWL_INI_DUMP_NAME_TYPE (BIT(31) | BIT(24)) + /** * struct iwl_fw_error_dump_data - data for one type * @type: &enum iwl_fw_ini_region_type diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 9aced3e44bc2..dd277a4fa8dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -775,6 +775,8 @@ struct iwl_imr_data { * @periodic_trig_list: periodic triggers list * @domains_bitmap: bitmap of active domains other than &IWL_FW_INI_DOMAIN_ALWAYS_ON * @ucode_preset: preset based on ucode + * @dump_file_name_ext: dump file name extension + * @dump_file_name_ext_valid: dump file name extension if valid or not */ struct iwl_trans_debug { u8 n_dest_reg; @@ -813,6 +815,8 @@ struct iwl_trans_debug { bool restart_required; u32 last_tp_resetfw; struct iwl_imr_data imr_data; + u8 dump_file_name_ext[IWL_FW_INI_MAX_NAME]; + bool dump_file_name_ext_valid; }; struct iwl_dma_ptr { -- cgit v1.2.3 From 834f920ef34b57a51fa892fae6de86830316c353 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Tue, 14 Mar 2023 19:49:15 +0200 Subject: wifi: iwlwifi: yoyo: Add driver defined dump file name Add driver defined dump file name extension for beacon loss and FW Assert case. Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.2a2ee92995e9.I38fff588e32276796cd757309fc811241f827c7a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/22000.c | 2 +- drivers/net/wireless/intel/iwlwifi/fw/dump.c | 58 ++++++++++++++++++++- drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 4 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 5 +- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 61 +++++++++++++++-------- 5 files changed, 101 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 05720352e49f..43fb5cf85f05 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -281,7 +281,7 @@ static const struct iwl_ht_params iwl_gl_a_ht_params = { .trans.gen2 = true, \ .nvm_type = IWL_NVM_EXT, \ .dbgc_supported = true, \ - .min_umac_error_event_table = 0x400000, \ + .min_umac_error_event_table = 0xD0000, \ .d3_debug_data_base_addr = 0x401000, \ .d3_debug_data_length = 60 * 1024, \ .mon_smem_regs = { \ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index 792f7fee1840..59ed321bcc27 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -14,6 +14,13 @@ #include "iwl-csr.h" #include "pnvm.h" +#define FW_ASSERT_LMAC_FATAL 0x70 +#define FW_ASSERT_LMAC2_FATAL 0x72 +#define FW_ASSERT_UMAC_FATAL 0x71 +#define UMAC_RT_NMI_LMAC2_FATAL 0x72 +#define RT_NMI_INTERRUPT_OTHER_LMAC_FATAL 0x73 +#define FW_ASSERT_NMI_UNKNOWN 0x84 + /* * Note: This structure is read from the device with IO accesses, * and the reading already does the endian conversion. As it is @@ -96,6 +103,17 @@ struct iwl_umac_error_event_table { #define ERROR_START_OFFSET (1 * sizeof(u32)) #define ERROR_ELEM_SIZE (7 * sizeof(u32)) +static bool iwl_fwrt_if_errorid_other_cpu(u32 err_id) +{ + err_id &= 0xFF; + + if ((err_id >= FW_ASSERT_LMAC_FATAL && + err_id <= RT_NMI_INTERRUPT_OTHER_LMAC_FATAL) || + err_id == FW_ASSERT_NMI_UNKNOWN) + return true; + return false; +} + static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt) { struct iwl_trans *trans = fwrt->trans; @@ -113,6 +131,13 @@ static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt) if (table.valid) fwrt->dump.umac_err_id = table.error_id; + if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.umac_err_id) && + !fwrt->trans->dbg.dump_file_name_ext_valid) { + fwrt->trans->dbg.dump_file_name_ext_valid = true; + snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, + "0x%x", fwrt->dump.umac_err_id); + } + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { IWL_ERR(trans, "Start IWL Error Log Dump:\n"); IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n", @@ -189,6 +214,13 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu if (table.valid) fwrt->dump.lmac_err_id[lmac_num] = table.error_id; + if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.lmac_err_id[lmac_num]) && + !fwrt->trans->dbg.dump_file_name_ext_valid) { + fwrt->trans->dbg.dump_file_name_ext_valid = true; + snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, + "0x%x", fwrt->dump.lmac_err_id[lmac_num]); + } + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { IWL_ERR(trans, "Start IWL Error Log Dump:\n"); IWL_ERR(trans, "Transport status: 0x%08lX, valid: %d\n", @@ -274,6 +306,16 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx) iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + if (table.valid) + fwrt->dump.tcm_err_id[idx] = table.error_id; + + if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.tcm_err_id[idx]) && + !fwrt->trans->dbg.dump_file_name_ext_valid) { + fwrt->trans->dbg.dump_file_name_ext_valid = true; + snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, + "0x%x", fwrt->dump.tcm_err_id[idx]); + } + IWL_ERR(fwrt, "TCM%d status:\n", idx + 1); IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id); IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2); @@ -337,6 +379,16 @@ static void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx) iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + if (table.valid) + fwrt->dump.rcm_err_id[idx] = table.error_id; + + if (!iwl_fwrt_if_errorid_other_cpu(fwrt->dump.rcm_err_id[idx]) && + !fwrt->trans->dbg.dump_file_name_ext_valid) { + fwrt->trans->dbg.dump_file_name_ext_valid = true; + snprintf(fwrt->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, + "0x%x", fwrt->dump.rcm_err_id[idx]); + } + IWL_ERR(fwrt, "RCM%d status:\n", idx + 1); IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id); IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2); @@ -444,8 +496,10 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt) iwl_fwrt_dump_umac_error_log(fwrt); iwl_fwrt_dump_tcm_error_log(fwrt, 0); iwl_fwrt_dump_rcm_error_log(fwrt, 0); - iwl_fwrt_dump_tcm_error_log(fwrt, 1); - iwl_fwrt_dump_rcm_error_log(fwrt, 1); + if (fwrt->trans->dbg.tcm_error_event_table[1]) + iwl_fwrt_dump_tcm_error_log(fwrt, 1); + if (fwrt->trans->dbg.rcm_error_event_table[1]) + iwl_fwrt_dump_rcm_error_log(fwrt, 1); iwl_fwrt_dump_iml_error_log(fwrt); iwl_fwrt_dump_fseq_regs(fwrt); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index d3cb1ae68a96..a59cf4d9567c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -24,6 +24,8 @@ struct iwl_fw_runtime_ops { }; #define MAX_NUM_LMAC 2 +#define MAX_NUM_TCM 2 +#define MAX_NUM_RCM 2 struct iwl_fwrt_shared_mem_cfg { int num_lmacs; int num_txfifo_entries; @@ -129,6 +131,8 @@ struct iwl_fw_runtime { unsigned long non_collect_ts_start[IWL_FW_INI_TIME_POINT_NUM]; u32 *d3_debug_data; u32 lmac_err_id[MAX_NUM_LMAC]; + u32 tcm_err_id[MAX_NUM_TCM]; + u32 rcm_err_id[MAX_NUM_RCM]; u32 umac_err_id; struct iwl_txf_iter_data txf_iter_data; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 45981e22b2db..3e9e9f13506b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -122,8 +122,6 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, u32 version = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, UCODE_ALIVE_NTFY, 0); u32 i; - struct iwl_trans *trans = mvm->trans; - enum iwl_device_family device_family = trans->trans_cfg->device_family; if (version == 6) { @@ -233,8 +231,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, if (umac_error_table) { if (umac_error_table >= - mvm->trans->cfg->min_umac_error_event_table || - device_family >= IWL_DEVICE_FAMILY_BZ) { + mvm->trans->cfg->min_umac_error_event_table) { iwl_fw_umac_set_alive_err_table(mvm->trans, umac_error_table); } else { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 114c96ba39ee..422550e31bc6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -430,47 +430,55 @@ static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, } } -static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_ctx_cmd *cmd, - const u8 *bssid_override, - u32 action) +static int iwl_mvm_get_mac_type(struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_chanctx_conf *chanctx; - bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION); - u8 cck_ack_rates, ofdm_ack_rates; - const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; - int i; - - cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - cmd->action = cpu_to_le32(action); + u32 mac_type = FW_MAC_TYPE_BSS_STA; switch (vif->type) { case NL80211_IFTYPE_STATION: if (vif->p2p) - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA); + mac_type = FW_MAC_TYPE_P2P_STA; else - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA); + mac_type = FW_MAC_TYPE_BSS_STA; break; case NL80211_IFTYPE_AP: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO); + mac_type = FW_MAC_TYPE_GO; break; case NL80211_IFTYPE_MONITOR: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER); + mac_type = FW_MAC_TYPE_LISTENER; break; case NL80211_IFTYPE_P2P_DEVICE: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE); + mac_type = FW_MAC_TYPE_P2P_DEVICE; break; case NL80211_IFTYPE_ADHOC: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS); + mac_type = FW_MAC_TYPE_IBSS; break; default: WARN_ON_ONCE(1); } + return mac_type; +} + +static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_ctx_cmd *cmd, + const u8 *bssid_override, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *chanctx; + bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION); + u8 cck_ack_rates, ofdm_ack_rates; + const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; + int i; + + cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)); + cmd->action = cpu_to_le32(action); + cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif)); + cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id); memcpy(cmd->node_addr, vif->addr, ETH_ALEN); @@ -1428,6 +1436,7 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, struct ieee80211_vif *vif; u32 id = le32_to_cpu(mb->mac_id); union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt }; + u32 mac_type; IWL_DEBUG_INFO(mvm, "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n", @@ -1443,6 +1452,14 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, if (!vif) goto out; + mac_type = iwl_mvm_get_mac_type(vif); + + IWL_DEBUG_INFO(mvm, "missed beacon mac_type=%u,\n", mac_type); + + mvm->trans->dbg.dump_file_name_ext_valid = true; + snprintf(mvm->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, + "MacId_%d_MacType_%d", id, mac_type); + rx_missed_bcon = le32_to_cpu(mb->consec_missed_beacons); rx_missed_bcon_since_rx = le32_to_cpu(mb->consec_missed_beacons_since_last_rx); -- cgit v1.2.3 From 9c4f15cadcd74becbb7e05f7fccd27180e433b8c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:16 +0200 Subject: wifi: iwlwifi: mvm: Refactor STA_HE_CTXT_CMD sending flow Refactor STA_HE_CTXT_CMD sending flow: 1. As the new MLD API is introduced, there are some common fields in both the old and new APIs. The STA_HE_CTXT_CMD of the non-MLD API has common fields with the link and mac commands of the new MLD API. Put this common parts in functions so it can be used later by the new MLD API. 2. The HE capability which indicates whether the NIC is ack-enabled or not is the same for all bands. No need to take it from the specific band which is currently in use. Take it from the low band - this simplifies the code and doesn't require a phy_ctxt. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.7ca960596953.Ifc3e816461abbd69c6fd87752342afcedfebc293@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 94 ++++++++++++++--------- 1 file changed, 57 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index ab02c6076276..b1f638af4b2c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1996,6 +1996,59 @@ static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext, } } +/* + * This function sets the MU EDCA parameters ans returns whether MU EDCA + * is enabled or not + */ +static bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + struct iwl_he_backoff_conf + *trig_based_txf) +{ + int i; + /* Mark MU EDCA as enabled, unless none detected on some AC */ + bool mu_edca_enabled = true; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + struct ieee80211_he_mu_edca_param_ac_rec *mu_edca = + &mvmvif->queue_params[i].mu_edca_param_rec; + u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); + + if (!mvmvif->queue_params[i].mu_edca) { + mu_edca_enabled = false; + break; + } + + trig_based_txf[ac].cwmin = + cpu_to_le16(mu_edca->ecw_min_max & 0xf); + trig_based_txf[ac].cwmax = + cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4); + trig_based_txf[ac].aifsn = + cpu_to_le16(mu_edca->aifsn & 0xf); + trig_based_txf[ac].mu_time = + cpu_to_le16(mu_edca->mu_edca_timer); + } + + return mu_edca_enabled; +} + +static bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + const struct ieee80211_supported_band *sband; + const struct ieee80211_sta_he_cap *own_he_cap = NULL; + + /* This capability is the same for all bands, + * so take it from one of them. + */ + sband = mvm->hw->wiphy->bands[NL80211_BAND_2GHZ]; + own_he_cap = ieee80211_get_he_iftype_cap(sband, + ieee80211_vif_type_p2p(vif)); + + return (own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] & + IEEE80211_HE_MAC_CAP2_ACK_EN)); +} + static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u8 sta_id) { @@ -2015,9 +2068,6 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta; u32 flags; int i; - const struct ieee80211_sta_he_cap *own_he_cap = NULL; - struct ieee80211_chanctx_conf *chanctx_conf; - const struct ieee80211_supported_band *sband; void *cmd; u8 nominal_padding; @@ -2045,16 +2095,6 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf); - if (WARN_ON(!chanctx_conf)) { - rcu_read_unlock(); - return; - } - - sband = mvm->hw->wiphy->bands[chanctx_conf->def.chan->band]; - own_he_cap = ieee80211_get_he_iftype_cap(sband, - ieee80211_vif_type_p2p(vif)); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_ctxt_cmd.sta_id]); if (IS_ERR_OR_NULL(sta)) { rcu_read_unlock(); @@ -2211,28 +2251,9 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, rcu_read_unlock(); - /* Mark MU EDCA as enabled, unless none detected on some AC */ - flags |= STA_CTXT_HE_MU_EDCA_CW; - for (i = 0; i < IEEE80211_NUM_ACS; i++) { - struct ieee80211_he_mu_edca_param_ac_rec *mu_edca = - &mvmvif->queue_params[i].mu_edca_param_rec; - u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); - - if (!mvmvif->queue_params[i].mu_edca) { - flags &= ~STA_CTXT_HE_MU_EDCA_CW; - break; - } - - sta_ctxt_cmd.trig_based_txf[ac].cwmin = - cpu_to_le16(mu_edca->ecw_min_max & 0xf); - sta_ctxt_cmd.trig_based_txf[ac].cwmax = - cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4); - sta_ctxt_cmd.trig_based_txf[ac].aifsn = - cpu_to_le16(mu_edca->aifsn); - sta_ctxt_cmd.trig_based_txf[ac].mu_time = - cpu_to_le16(mu_edca->mu_edca_timer); - } - + if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif, + &sta_ctxt_cmd.trig_based_txf[0])) + flags |= STA_CTXT_HE_MU_EDCA_CW; if (vif->bss_conf.uora_exists) { flags |= STA_CTXT_HE_TRIG_RND_ALLOC; @@ -2243,8 +2264,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, (vif->bss_conf.uora_ocw_range >> 3) & 0x7; } - if (own_he_cap && !(own_he_cap->he_cap_elem.mac_cap_info[2] & - IEEE80211_HE_MAC_CAP2_ACK_EN)) + if (!iwl_mvm_is_nic_ack_enabled(mvm, vif)) flags |= STA_CTXT_HE_NIC_NOT_ACK_ENABLED; if (vif->bss_conf.nontransmitted) { -- cgit v1.2.3 From af6d168f0ec613d55cdd9c4e0443108172577318 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:17 +0200 Subject: wifi: iwlwifi: mvm: Refactor MAC_CONTEXT_CMD sending flow Refactor MAC_CONTEXT_CMD sending flow: 1. As the new MLD API is introduced, there are some common fields in both the old and new APIs. The MAC_CONTEXT_CMD of the non-MLD API has common fields with the link and mac commands of the new MLD API. Put this common parts in functions so it can be used later by the new MLD API. 2. Use iwl_mvm_mac_ctxt_send_cmd when removing a mac instead of implementing the same functionality once again. 3. Change the debug print when sending the command to be more specific, so it will be easy to distinguish later if the old or new mac command was sent. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.3ab62700db4e.I2e353b308667c215aa456c160e0d90de2b9b85cc@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 320 +++++++++++++--------- 1 file changed, 197 insertions(+), 123 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 422550e31bc6..a30385b85a60 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -396,15 +396,45 @@ static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, *ofdm_rates = ofdm; } -static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_ctx_cmd *cmd) +static void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + __le32 *cck_rates, __le32 *ofdm_rates) +{ + struct ieee80211_chanctx_conf *chanctx; + u8 cck_ack_rates, ofdm_ack_rates; + + rcu_read_lock(); + chanctx = rcu_dereference(vif->bss_conf.chanctx_conf); + iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band + : NL80211_BAND_2GHZ, + &cck_ack_rates, &ofdm_ack_rates); + rcu_read_unlock(); + + *cck_rates = cpu_to_le32((u32)cck_ack_rates); + *ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); +} + +static void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + __le32 *protection_flags, + u32 ht_flag, + u32 tgg_flag) { /* for both sta and ap, ht_operation_mode hold the protection_mode */ u8 protection_mode = vif->bss_conf.ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; - /* The fw does not distinguish between ht and fat */ - u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; + bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION); + + if (vif->bss_conf.use_cts_prot) + *protection_flags |= cpu_to_le32(tgg_flag); + + IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", + vif->bss_conf.use_cts_prot, + vif->bss_conf.ht_operation_mode); + + if (!ht_enabled) + return; IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode); /* @@ -416,12 +446,12 @@ static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, break; case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: - cmd->protection_flags |= cpu_to_le32(ht_flag); + *protection_flags |= cpu_to_le32(ht_flag); break; case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: /* Protect when channel wider than 20MHz */ if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) - cmd->protection_flags |= cpu_to_le32(ht_flag); + *protection_flags |= cpu_to_le32(ht_flag); break; default: IWL_ERR(mvm, "Illegal protection mode %d\n", @@ -430,6 +460,34 @@ static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, } } +static void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_ac_qos *ac, __le32 *qos_flags) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int i; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, i); + u8 ucode_ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); + + ac[ucode_ac].cw_min = + cpu_to_le16(mvmvif->queue_params[i].cw_min); + ac[ucode_ac].cw_max = + cpu_to_le16(mvmvif->queue_params[i].cw_max); + ac[ucode_ac].edca_txop = + cpu_to_le16(mvmvif->queue_params[i].txop * 32); + ac[ucode_ac].aifsn = mvmvif->queue_params[i].aifs; + ac[ucode_ac].fifos_mask = BIT(txf); + } + + if (vif->bss_conf.qos) + *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); + + if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) + *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); +} + static int iwl_mvm_get_mac_type(struct ieee80211_vif *vif) { u32 mac_type = FW_MAC_TYPE_BSS_STA; @@ -456,7 +514,6 @@ static int iwl_mvm_get_mac_type(struct ieee80211_vif *vif) default: WARN_ON_ONCE(1); } - return mac_type; } @@ -467,12 +524,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, u32 action) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_chanctx_conf *chanctx; - bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION); - u8 cck_ack_rates, ofdm_ack_rates; const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; - int i; + u32 ht_flag; cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); @@ -488,15 +541,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, else eth_broadcast_addr(cmd->bssid_addr); - rcu_read_lock(); - chanctx = rcu_dereference(vif->bss_conf.chanctx_conf); - iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band - : NL80211_BAND_2GHZ, - &cck_ack_rates, &ofdm_ack_rates); - rcu_read_unlock(); - - cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates); - cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); + iwl_mvm_set_fw_basic_rates(mvm, vif, &cmd->cck_rates, + &cmd->ofdm_rates); cmd->cck_short_preamble = cpu_to_le32(vif->bss_conf.use_short_preamble ? @@ -507,33 +553,12 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->filter_flags = 0; - for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, i); - u8 ucode_ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); - - cmd->ac[ucode_ac].cw_min = - cpu_to_le16(mvmvif->queue_params[i].cw_min); - cmd->ac[ucode_ac].cw_max = - cpu_to_le16(mvmvif->queue_params[i].cw_max); - cmd->ac[ucode_ac].edca_txop = - cpu_to_le16(mvmvif->queue_params[i].txop * 32); - cmd->ac[ucode_ac].aifsn = mvmvif->queue_params[i].aifs; - cmd->ac[ucode_ac].fifos_mask = BIT(txf); - } - - if (vif->bss_conf.qos) - cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); + iwl_mvm_set_fw_qos_params(mvm, vif, &cmd->ac[0], &cmd->qos_flags); - if (vif->bss_conf.use_cts_prot) - cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - - IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", - vif->bss_conf.use_cts_prot, - vif->bss_conf.ht_operation_mode); - if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) - cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); - if (ht_enabled) - iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); + /* The fw does not distinguish between ht and fat */ + ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; + iwl_mvm_set_fw_protection_flags(mvm, vif, &cmd->protection_flags, + ht_flag, MAC_PROT_FLG_TGG_PROTECT); } static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, @@ -542,11 +567,76 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, sizeof(*cmd), cmd); if (ret) - IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n", + IWL_ERR(mvm, "Failed to send MAC_CONTEXT_CMD (action:%d): %d\n", le32_to_cpu(cmd->action), ret); return ret; } +static void iwl_mvm_set_fw_dtim_tbtt(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + __le64 *dtim_tsf, __le32 *dtim_time, + __le32 *assoc_beacon_arrive_time) +{ + u32 dtim_offs; + + /* + * The DTIM count counts down, so when it is N that means N + * more beacon intervals happen until the DTIM TBTT. Therefore + * add this to the current time. If that ends up being in the + * future, the firmware will handle it. + * + * Also note that the system_timestamp (which we get here as + * "sync_device_ts") and TSF timestamp aren't at exactly the + * same offset in the frame -- the TSF is at the first symbol + * of the TSF, the system timestamp is at signal acquisition + * time. This means there's an offset between them of at most + * a few hundred microseconds (24 * 8 bits + PLCP time gives + * 384us in the longest case), this is currently not relevant + * as the firmware wakes up around 2ms before the TBTT. + */ + dtim_offs = vif->bss_conf.sync_dtim_count * + vif->bss_conf.beacon_int; + /* convert TU to usecs */ + dtim_offs *= 1024; + + *dtim_tsf = + cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); + *dtim_time = + cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); + *assoc_beacon_arrive_time = + cpu_to_le32(vif->bss_conf.sync_device_ts); + + IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", + le64_to_cpu(*dtim_tsf), + le32_to_cpu(*dtim_time), + dtim_offs); +} + +static __le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct ieee80211_p2p_noa_attr *noa = + &vif->bss_conf.p2p_noa_attr; + + return cpu_to_le32(noa->oppps_ctwindow & + IEEE80211_P2P_OPPPS_CTWINDOW_MASK); +} + +static __le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + __le32 twt_policy = cpu_to_le32(0); + + if (vif->bss_conf.twt_requester && IWL_MVM_USE_TWT) + twt_policy |= cpu_to_le32(TWT_SUPPORTED); + if (vif->bss_conf.twt_protected) + twt_policy |= cpu_to_le32(PROTECTED_TWT_SUPPORTED); + if (vif->bss_conf.twt_broadcast) + twt_policy |= cpu_to_le32(BROADCAST_TWT_SUPPORTED); + + return twt_policy; +} + static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 action, bool force_assoc_off, @@ -567,11 +657,9 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, cmd.filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP); if (vif->p2p) { - struct ieee80211_p2p_noa_attr *noa = - &vif->bss_conf.p2p_noa_attr; + cmd.p2p_sta.ctwin = + iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif); - cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); ctxt_sta = &cmd.p2p_sta.sta; } else { ctxt_sta = &cmd.sta; @@ -581,39 +669,10 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, if (vif->cfg.assoc && vif->bss_conf.dtim_period && !force_assoc_off) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 dtim_offs; - /* - * The DTIM count counts down, so when it is N that means N - * more beacon intervals happen until the DTIM TBTT. Therefore - * add this to the current time. If that ends up being in the - * future, the firmware will handle it. - * - * Also note that the system_timestamp (which we get here as - * "sync_device_ts") and TSF timestamp aren't at exactly the - * same offset in the frame -- the TSF is at the first symbol - * of the TSF, the system timestamp is at signal acquisition - * time. This means there's an offset between them of at most - * a few hundred microseconds (24 * 8 bits + PLCP time gives - * 384us in the longest case), this is currently not relevant - * as the firmware wakes up around 2ms before the TBTT. - */ - dtim_offs = vif->bss_conf.sync_dtim_count * - vif->bss_conf.beacon_int; - /* convert TU to usecs */ - dtim_offs *= 1024; - - ctxt_sta->dtim_tsf = - cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); - ctxt_sta->dtim_time = - cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); - ctxt_sta->assoc_beacon_arrive_time = - cpu_to_le32(vif->bss_conf.sync_device_ts); - - IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", - le64_to_cpu(ctxt_sta->dtim_tsf), - le32_to_cpu(ctxt_sta->dtim_time), - dtim_offs); + iwl_mvm_set_fw_dtim_tbtt(mvm, vif, &ctxt_sta->dtim_tsf, + &ctxt_sta->dtim_time, + &ctxt_sta->assoc_beacon_arrive_time); ctxt_sta->is_assoc = cpu_to_le32(1); @@ -643,14 +702,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) { cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX); - if (vif->bss_conf.twt_requester && IWL_MVM_USE_TWT) - ctxt_sta->data_policy |= cpu_to_le32(TWT_SUPPORTED); - if (vif->bss_conf.twt_protected) - ctxt_sta->data_policy |= - cpu_to_le32(PROTECTED_TWT_SUPPORTED); - if (vif->bss_conf.twt_broadcast) - ctxt_sta->data_policy |= - cpu_to_le32(BROADCAST_TWT_SUPPORTED); + ctxt_sta->data_policy |= + iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif); } @@ -732,20 +785,11 @@ static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) data->go_active = true; } -static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) +static __le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { - struct iwl_mac_ctx_cmd cmd = {}; struct iwl_mvm_go_iterator_data data = {}; - WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); - - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - /* Override the filter flags to accept only probe requests */ - cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - /* * This flag should be set to true when the P2P Device is * discoverable and there is at least another active P2P GO. Settings @@ -758,7 +802,25 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, iwl_mvm_go_iterator, &data); - cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0); + return cpu_to_le32(data.go_active ? 1 : 0); +} + +static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_ctx_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); + + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + + cmd.p2p_dev.is_disc_extended = + iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); + + /* Override the filter flags to accept only probe requests */ + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } @@ -1102,6 +1164,30 @@ static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac, data->beacon_int = vif->bss_conf.beacon_int; } +/* + * Fill the filter flags for mac context of type AP or P2P GO. + */ +static void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + __le32 *filter_flags, + int accept_probe_req_flag, + int accept_beacon_flag) +{ + /* + * in AP mode, pass probe requests and beacons from other APs + * (needed for ht protection); when there're no any associated + * station don't ask FW to pass beacons to prevent unnecessary + * wake-ups. + */ + *filter_flags |= cpu_to_le32(accept_probe_req_flag); + if (mvmvif->ap_assoc_sta_count || !mvm->drop_bcn_ap_mode) { + *filter_flags |= cpu_to_le32(accept_beacon_flag); + IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); + } else { + IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); + } +} + /* * Fill the specific data for mac context of type AP of P2P GO */ @@ -1121,19 +1207,10 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST); - /* - * in AP mode, pass probe requests and beacons from other APs - * (needed for ht protection); when there're no any associated - * station don't ask FW to pass beacons to prevent unnecessary - * wake-ups. - */ - cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - if (mvmvif->ap_assoc_sta_count || !mvm->drop_bcn_ap_mode) { - cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); - } else { - IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); - } + iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, + &cmd->filter_flags, + MAC_FILTER_IN_PROBE_REQUEST, + MAC_FILTER_IN_BEACON); ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * @@ -1295,12 +1372,9 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->color)); cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, - sizeof(cmd), &cmd); - if (ret) { - IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret); + ret = iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); + if (ret) return ret; - } mvmvif->uploaded = false; -- cgit v1.2.3 From 9be162a7b670b7e8dbada3c139d77961f457a34d Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:18 +0200 Subject: wifi: iwlwifi: mvm: add support for the new MAC CTXT command As a part of the new MLD FW API changes, we have a new MAC CTXT command. Add structures and enum definitions, along with the functions that sends this command (i.e. add, remove and change mac ctxt). This functions will be in used in the next patches. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.99a41a5bc55f.I310312c829f5f2f69b64fcddce487b1eab80165b@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 109 ++++++++- drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 24 +- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +- drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c | 271 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 18 ++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 7 files changed, 411 insertions(+), 17 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index 712532f17630..f12b2a25cd3d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -1,12 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2019, 2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_mac_cfg_h__ #define __iwl_fw_api_mac_cfg_h__ +#include "mac.h" + /** * enum iwl_mac_conf_subcmd_ids - mac configuration command IDs */ @@ -31,7 +33,10 @@ enum iwl_mac_conf_subcmd_ids { * @CANCEL_CHANNEL_SWITCH_CMD: &struct iwl_cancel_channel_switch_cmd */ CANCEL_CHANNEL_SWITCH_CMD = 0x6, - + /** + * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd + */ + MAC_CONFIG_CMD = 0x8, /** * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif */ @@ -182,4 +187,104 @@ struct iwl_mac_low_latency_cmd { __le16 reserved; } __packed; /* MAC_LOW_LATENCY_API_S_VER_1 */ +/** + * struct iwl_mac_client_data - configuration data for client MAC context + * + * @is_assoc: 1 for associated state, 0 otherwise + * @assoc_id: unique ID assigned by the AP during association + * @data_policy: see &enum iwl_mac_data_policy + * @ctwin: client traffic window in TU (period after TBTT when GO is present). + * 0 indicates that there is no CT window. + */ +struct iwl_mac_client_data { + __le32 is_assoc; + __le32 assoc_id; + __le32 data_policy; + __le32 ctwin; +} __packed; /* MAC_CONTEXT_CONFIG_CLIENT_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_go_ibss_data - configuration data for GO and IBSS MAC context + * + * @beacon_template: beacon template ID + */ +struct iwl_mac_go_ibss_data { + __le32 beacon_template; +} __packed; /* MAC_CONTEXT_CONFIG_GO_IBSS_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_p2p_dev_data - configuration data for P2P device MAC context + * + * @is_disc_extended: if set to true, P2P Device discoverability is enabled on + * other channels as well. This should be to true only in case that the + * device is discoverable and there is an active GO. Note that setting this + * field when not needed, will increase the number of interrupts and have + * effect on the platform power, as this setting opens the Rx filters on + * all macs. + */ +struct iwl_mac_p2p_dev_data { + __le32 is_disc_extended; +} __packed; /* MAC_CONTEXT_CONFIG_P2P_DEV_DATA_API_S_VER_1 */ + +/** + * enum iwl_mac_config_filter_flags - MAC context configuration filter flags + * + * @MAC_CFG_FILTER_PROMISC: accept all data frames + * @MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT: pass all management and + * control frames to the host + * @MAC_CFG_FILTER_ACCEPT_GRP: accept multicast frames + * @MAC_CFG_FILTER_ACCEPT_BEACON: accept beacon frames + * @MAC_CFG_FILTER_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe response + * @MAC_CFG_FILTER_ACCEPT_PROBE_REQ: accept probe requests + */ +enum iwl_mac_config_filter_flags { + MAC_CFG_FILTER_PROMISC = BIT(0), + MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT = BIT(1), + MAC_CFG_FILTER_ACCEPT_GRP = BIT(2), + MAC_CFG_FILTER_ACCEPT_BEACON = BIT(3), + MAC_CFG_FILTER_ACCEPT_BCAST_PROBE_RESP = BIT(4), + MAC_CFG_FILTER_ACCEPT_PROBE_REQ = BIT(5), +}; /* MAC_FILTER_FLAGS_MASK_E_VER_1 */ + +/** + * struct iwl_mac_config_cmd - command structure to configure MAC contexts in + * MLD API + * ( MAC_CONTEXT_CONFIG_CMD = 0x8 ) + * + * @id_and_color: ID and color of the MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @mac_type: one of &enum iwl_mac_types + * @local_mld_addr: mld address + * @reserved_for_local_mld_addr: reserved + * @filter_flags: combination of &enum iwl_mac_config_filter_flags + * @he_support: does this MAC support HE + * @eht_support: does this MAC support EHT. Requires he_support + * @nic_not_ack_enabled: mark that the NIC doesn't support receiving + * ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG). + * If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0 + * len delim to determine if AGG or single. + * @client: client mac data + * @go_ibss: mac data for go or ibss + * @p2p_dev: mac data for p2p device + */ +struct iwl_mac_config_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* MAC_CONTEXT_TYPE_API_E */ + __le32 mac_type; + u8 local_mld_addr[6]; + __le16 reserved_for_local_mld_addr; + __le32 filter_flags; + __le32 he_support; + __le32 eht_support; + __le32 nic_not_ack_enabled; + /* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_1 */ + union { + struct iwl_mac_client_data client; + struct iwl_mac_go_ibss_data go_ibss; + struct iwl_mac_p2p_dev_data p2p_dev; + }; +} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index b28fcf0cf9cf..d71f0a99b7c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -7,7 +7,7 @@ iwlmvm-y += power.o coex.o iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-y += ftm-responder.o ftm-initiator.o iwlmvm-y += rfi.o -iwlmvm-y += mld-key.o +iwlmvm-y += mld-key.o mld-mac.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-$(CONFIG_PM) += d3.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index a30385b85a60..b21327ec4401 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -488,7 +488,7 @@ static void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); } -static int iwl_mvm_get_mac_type(struct ieee80211_vif *vif) +int iwl_mvm_get_mac_type(struct ieee80211_vif *vif) { u32 mac_type = FW_MAC_TYPE_BSS_STA; @@ -612,8 +612,8 @@ static void iwl_mvm_set_fw_dtim_tbtt(struct iwl_mvm *mvm, dtim_offs); } -static __le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +__le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr; @@ -622,8 +622,8 @@ static __le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm, IEEE80211_P2P_OPPPS_CTWINDOW_MASK); } -static __le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +__le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { __le32 twt_policy = cpu_to_le32(0); @@ -785,8 +785,8 @@ static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) data->go_active = true; } -static __le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +__le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { struct iwl_mvm_go_iterator_data data = {}; @@ -1167,11 +1167,11 @@ static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac, /* * Fill the filter flags for mac context of type AP or P2P GO. */ -static void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - __le32 *filter_flags, - int accept_probe_req_flag, - int accept_beacon_flag) +void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + __le32 *filter_flags, + int accept_probe_req_flag, + int accept_beacon_flag) { /* * in AP mode, pass probe requests and beacons from other APs diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index b1f638af4b2c..210b134c735f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2032,8 +2032,7 @@ static bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm, return mu_edca_enabled; } -static bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { const struct ieee80211_supported_band *sband; const struct ieee80211_sta_he_cap *own_he_cap = NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c new file mode 100644 index 000000000000..240f51d2fd53 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include "mvm.h" + +static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + cmd->id_and_color = cpu_to_le32(mvmvif->id); + cmd->action = cpu_to_le32(action); + + cmd->mac_type = cpu_to_le32(iwl_mvm_get_mac_type(vif)); + + memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); + + cmd->filter_flags = cpu_to_le32(0); + cmd->he_support = cpu_to_le32(0); + cmd->eht_support = cpu_to_le32(0); + + cmd->nic_not_ack_enabled = + cpu_to_le32(!iwl_mvm_is_nic_ack_enabled(mvm, vif)); + + if (iwlwifi_mod_params.disable_11ax) + return; + cmd->he_support = cpu_to_le32(vif->bss_conf.he_support); + + if (!iwlwifi_mod_params.disable_11be && cmd->he_support) + cmd->eht_support = cpu_to_le32(vif->bss_conf.eht_support); +} + +static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm, + struct iwl_mac_config_cmd *cmd) +{ + int ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), + 0, sizeof(*cmd), cmd); + if (ret) + IWL_ERR(mvm, "Failed to send MAC_CONFIG_CMD (action:%d): %d\n", + le32_to_cpu(cmd->action), ret); + return ret; +} + +static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action, bool force_assoc_off) +{ + struct iwl_mac_config_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_STATION); + + /* Fill the common data for all mac context types */ + iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + /* + * We always want to hear MCAST frames, if we're not authorized yet, + * we'll drop them. + */ + cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); + + if (vif->p2p) + cmd.client.ctwin = + iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(mvm, vif); + + if (vif->cfg.assoc && vif->bss_conf.dtim_period && + !force_assoc_off) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + cmd.client.is_assoc = cpu_to_le32(1); + + if (!mvmvif->authorized && + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO)) + cmd.client.data_policy |= + cpu_to_le32(COEX_HIGH_PRIORITY_ENABLE); + + } else { + cmd.client.is_assoc = cpu_to_le32(0); + + /* Allow beacons to pass through as long as we are not + * associated, or we do not have dtim period information. + */ + cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); + } + + cmd.client.assoc_id = cpu_to_le32(vif->cfg.aid); + + if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) + cmd.filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + + if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) + cmd.client.data_policy |= + iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(mvm, vif); + + return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_config_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); + + iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_PROMISC | + MAC_FILTER_IN_CONTROL_AND_MGMT | + MAC_CFG_FILTER_ACCEPT_BEACON | + MAC_CFG_FILTER_ACCEPT_PROBE_REQ | + MAC_CFG_FILTER_ACCEPT_GRP); + + return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_config_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); + + iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | + MAC_CFG_FILTER_ACCEPT_PROBE_REQ | + MAC_CFG_FILTER_ACCEPT_GRP); + + /* TODO: Assumes that the beacon id == mac context id */ + cmd.go_ibss.beacon_template = cpu_to_le32(mvmvif->id); + + return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_config_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); + + iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + cmd.p2p_dev.is_disc_extended = + iwl_mac_ctxt_p2p_dev_has_extended_disc(mvm, vif); + + /* Override the filter flags to accept only probe requests */ + cmd.filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + + return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_config_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_AP); + + /* Fill the common data for all mac context types */ + iwl_mvm_mld_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(mvm, mvmvif, + &cmd.filter_flags, + MAC_CFG_FILTER_ACCEPT_PROBE_REQ, + MAC_CFG_FILTER_ACCEPT_BEACON); + + /* TODO: Assume that the beacon id == mac context id */ + cmd.go_ibss.beacon_template = cpu_to_le32(mvmvif->id); + + return iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mld_mac_ctx_send(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action, bool force_assoc_off) +{ + switch (vif->type) { + case NL80211_IFTYPE_STATION: + return iwl_mvm_mld_mac_ctxt_cmd_sta(mvm, vif, action, + force_assoc_off); + case NL80211_IFTYPE_AP: + return iwl_mvm_mld_mac_ctxt_cmd_ap_go(mvm, vif, action); + case NL80211_IFTYPE_MONITOR: + return iwl_mvm_mld_mac_ctxt_cmd_listener(mvm, vif, action); + case NL80211_IFTYPE_P2P_DEVICE: + return iwl_mvm_mld_mac_ctxt_cmd_p2p_device(mvm, vif, action); + case NL80211_IFTYPE_ADHOC: + return iwl_mvm_mld_mac_ctxt_cmd_ibss(mvm, vif, action); + default: + break; + } + + return -EOPNOTSUPP; +} + +int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) + return -EOPNOTSUPP; + + if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", + vif->addr, ieee80211_vif_type_p2p(vif))) + return -EIO; + + ret = iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, + true); + if (ret) + return ret; + + /* will only do anything at resume from D3 time */ + iwl_mvm_set_last_nonqos_seq(mvm, vif); + + mvmvif->uploaded = true; + return 0; +} + +int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool force_assoc_off) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) + return -EOPNOTSUPP; + + if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", + vif->addr, ieee80211_vif_type_p2p(vif))) + return -EIO; + + return iwl_mvm_mld_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, + force_assoc_off); +} + +int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_config_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .id_and_color = cpu_to_le32(mvmvif->id), + }; + int ret; + + if (WARN_ON_ONCE(vif->type == NL80211_IFTYPE_NAN)) + return -EOPNOTSUPP; + + if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", + vif->addr, ieee80211_vif_type_p2p(vif))) + return -EIO; + + ret = iwl_mvm_mld_mac_ctxt_send_cmd(mvm, &cmd); + if (ret) + return ret; + + mvmvif->uploaded = false; + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 6bd1a4c72a12..1287207a34b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1477,6 +1477,7 @@ void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags, struct ieee80211_tx_rate *r); u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx); u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac); +bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif); static inline void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) { @@ -1638,6 +1639,23 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef); u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); /* MAC (virtual interface) programming */ + +__le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + __le32 *filter_flags, + int accept_probe_req_flag, + int accept_beacon_flag); +int iwl_mvm_get_mac_type(struct ieee80211_vif *vif); +__le32 iwl_mvm_mac_ctxt_cmd_p2p_sta_get_oppps_ctwin(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +__le32 iwl_mvm_mac_ctxt_cmd_sta_get_twt_policy(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +int iwl_mvm_mld_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool force_assoc_off); +int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index f4e9446d9dc2..69c390ff2745 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -521,6 +521,7 @@ static const struct iwl_hcmd_names iwl_mvm_system_names[] = { static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { HCMD_NAME(CHANNEL_SWITCH_TIME_EVENT_CMD), HCMD_NAME(SESSION_PROTECTION_CMD), + HCMD_NAME(MAC_CONFIG_CMD), HCMD_NAME(SESSION_PROTECTION_NOTIF), HCMD_NAME(CHANNEL_SWITCH_START_NOTIF), }; -- cgit v1.2.3 From 55eb1c5fa4b260491d8be3299d4546d0b34465f4 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:19 +0200 Subject: wifi: iwlwifi: mvm: add support for the new LINK command As a part of the new MLD FW API changes, we have a new LINK command to add/remove/configure a link. Add structures and enum definitions, along with the functions that sends this command (i.e. add, remove and change mac ctxt). These functions will be in used in the next patches. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.d7808329effb.I13bea2db206b78540bc866bc3ab755ad5be78c53@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 179 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/link.c | 175 ++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 26 ++- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 7 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 20 +++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 7 files changed, 390 insertions(+), 20 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/link.c (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index f12b2a25cd3d..301f34f13523 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -37,6 +37,10 @@ enum iwl_mac_conf_subcmd_ids { * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd */ MAC_CONFIG_CMD = 0x8, + /** + * @LINK_CONFIG_CMD: &struct iwl_link_config_cmd + */ + LINK_CONFIG_CMD = 0x9, /** * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif */ @@ -287,4 +291,179 @@ struct iwl_mac_config_cmd { }; } __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_1 */ +/** + * enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being + * modified in &iwl_link_ctx_cfg_cmd + * + * @LINK_CONTEXT_MODIFY_ACTIVE: covers iwl_link_ctx_cfg_cmd::active + * @LINK_CONTEXT_MODIFY_RATES_INFO: covers iwl_link_ctx_cfg_cmd::cck_rates, + * iwl_link_ctx_cfg_cmd::ofdm_rates, + * iwl_link_ctx_cfg_cmd::cck_short_preamble, + * iwl_link_ctx_cfg_cmd::short_slot + * @LINK_CONTEXT_MODIFY_PROTECT_FLAGS: covers + * iwl_link_ctx_cfg_cmd::protection_flags + * @LINK_CONTEXT_MODIFY_QOS_PARAMS: covers iwl_link_ctx_cfg_cmd::qos_flags, + * iwl_link_ctx_cfg_cmd::ac, + * @LINK_CONTEXT_MODIFY_BEACON_TIMING: covers iwl_link_ctx_cfg_cmd::bi, + * iwl_link_ctx_cfg_cmd::dtim_interval, + * iwl_link_ctx_cfg_cmd::dtim_time, + * iwl_link_ctx_cfg_cmd::dtim_tsf, + * iwl_link_ctx_cfg_cmd::assoc_beacon_arrive_time. + * This flag can be set only once after assoc. + * @LINK_CONTEXT_MODIFY_HE_PARAMS: covers + * iwl_link_ctx_cfg_cmd::htc_trig_based_pkt_ext + * iwl_link_ctx_cfg_cmd::rand_alloc_ecwmin, + * iwl_link_ctx_cfg_cmd::rand_alloc_ecwmax, + * iwl_link_ctx_cfg_cmd::trig_based_txf, + * iwl_link_ctx_cfg_cmd::bss_color, + * iwl_link_ctx_cfg_cmd::ndp_fdbk_buff_th_exp, + * iwl_link_ctx_cfg_cmd::ref_bssid_addr + * iwl_link_ctx_cfg_cmd::bssid_index, + * iwl_link_ctx_cfg_cmd::frame_time_rts_th. + * This flag can be set any time. + * @LINK_CONTEXT_MODIFY_BSS_COLOR_DISABLE: covers + * iwl_link_ctx_cfg_cmd::bss_color_disable + * @LINK_CONTEXT_MODIFY_EHT_PARAMS: covers iwl_link_ctx_cfg_cmd::puncture_mask. + * This flag can be set only if the MAC that this link relates to has + * eht_support set to true. + * @LINK_CONTEXT_MODIFY_ALL: set all above flags + */ +enum iwl_link_ctx_modify_flags { + LINK_CONTEXT_MODIFY_ACTIVE = BIT(0), + LINK_CONTEXT_MODIFY_RATES_INFO = BIT(1), + LINK_CONTEXT_MODIFY_PROTECT_FLAGS = BIT(2), + LINK_CONTEXT_MODIFY_QOS_PARAMS = BIT(3), + LINK_CONTEXT_MODIFY_BEACON_TIMING = BIT(4), + LINK_CONTEXT_MODIFY_HE_PARAMS = BIT(5), + LINK_CONTEXT_MODIFY_BSS_COLOR_DISABLE = BIT(6), + LINK_CONTEXT_MODIFY_EHT_PARAMS = BIT(7), + LINK_CONTEXT_MODIFY_ALL = 0xff, +}; /* LINK_CONTEXT_MODIFY_MASK_E_VER_1 */ + +/** + * enum iwl_link_ctx_protection_flags - link protection flags + * @LINK_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, + * this will require CCK RTS/CTS2self. + * RTS/CTS will protect full burst time. + * @LINK_PROT_FLG_HT_PROT: enable HT protection + * @LINK_PROT_FLG_FAT_PROT: protect 40 MHz transmissions + * @LINK_PROT_FLG_SELF_CTS_EN: allow CTS2self + */ +enum iwl_link_ctx_protection_flags { + LINK_PROT_FLG_TGG_PROTECT = BIT(0), + LINK_PROT_FLG_HT_PROT = BIT(1), + LINK_PROT_FLG_FAT_PROT = BIT(2), + LINK_PROT_FLG_SELF_CTS_EN = BIT(3), +}; /* LINK_PROTECT_FLAGS_E_VER_1 */ + +/** + * enum iwl_link_ctx_flags - link context flags + * + * @LINK_FLG_BSS_COLOR_DIS: BSS color disable, don't use the BSS + * color for RX filter but use MAC header + * enabled AGG, i.e. both BACK and non-BACK frames in a single AGG + * @LINK_FLG_MU_EDCA_CW: indicates that there is an element of MU EDCA + * parameter set, i.e. the backoff counters for trig-based ACs + * @LINK_FLG_RU_2MHZ_BLOCK: indicates that 26-tone RU OFDMA transmission are + * not allowed (as there are OBSS that might classify such transmissions as + * radar pulses). + */ +enum iwl_link_ctx_flags { + LINK_FLG_BSS_COLOR_DIS = BIT(0), + LINK_FLG_MU_EDCA_CW = BIT(1), + LINK_FLG_RU_2MHZ_BLOCK = BIT(2), +}; /* LINK_CONTEXT_FLAG_E_VER_1 */ + +/** + * struct iwl_link_config_cmd - command structure to configure the LINK context + * in MLD API + * ( LINK_CONFIG_CMD =0x9 ) + * + * @action: action to perform, one of FW_CTXT_ACTION_* + * @link_id: the id of the link that this cmd configures + * @mac_id: interface ID. Relevant only if action is FW_CTXT_ACTION_ADD + * @phy_id: PHY index. Can be changed only if the link was inactive + * (and stays inactive). If the link is active (or becomes active), + * this field is ignored. + * @local_link_addr: the links MAC address. Can be changed only if the link was + * inactive (and stays inactive). If the link is active + * (or becomes active), this field is ignored. + * @reserved_for_local_link_addr: reserved + * @modify_mask: from &enum iwl_link_ctx_modify_flags, selects what to change. + * Relevant only if action is FW_CTXT_ACTION_MODIFY + * @active: indicates whether the link is active or not + * @listen_lmac: indicates whether the link should be allocated on the Listen + * Lmac or on the Main Lmac. Cannot be changed on an active Link. + * Relevant only for eSR. + * @cck_rates: basic rates available for CCK + * @ofdm_rates: basic rates available for OFDM + * @cck_short_preamble: 1 for enabling short preamble, 0 otherwise + * @short_slot: 1 for enabling short slots, 0 otherwise + * @protection_flags: combination of &enum iwl_link_ctx_protection_flags + * @qos_flags: from &enum iwl_mac_qos_flags + * @ac: one iwl_mac_qos configuration for each AC + * @htc_trig_based_pkt_ext: default PE in 4us units + * @rand_alloc_ecwmin: random CWmin = 2**ECWmin-1 + * @rand_alloc_ecwmax: random CWmax = 2**ECWmax-1 + * @ndp_fdbk_buff_th_exp: set exponent for the NDP feedback buffered threshold + * @trig_based_txf: MU EDCA Parameter set for the trigger based traffic queues + * @dtim_time: DTIM arrival time in system time + * @dtim_tsf: DTIM arrival time in TSF + * @assoc_beacon_arrive_time: TSF of first beacon after association + * @bi: beacon interval in TU, applicable only when associated + * @dtim_interval: DTIM interval in TU. + * Relevant only for GO, otherwise this is offloaded. + * @beacon_template: beacon template ID. For GO only + * @puncture_mask: puncture mask for EHT + * @frame_time_rts_th: HE duration RTS threshold, in units of 32us + * @flags: a combination from &enum iwl_link_ctx_flags + * @flags_mask: what of %flags have changed. Also &enum iwl_link_ctx_flags + * Below fields are for multi-bssid: + * @ref_bssid_addr: reference BSSID used by the AP + * @reserved_for_ref_bssid_addr: reserved + * @bssid_index: index of the associated VAP + * @bss_color: 11ax AP ID that is used in the HE SIG-A to mark inter BSS frame + * @reserved: alignment + */ +struct iwl_link_config_cmd { + __le32 action; + __le32 link_id; + __le32 mac_id; + __le32 phy_id; + u8 local_link_addr[6]; + __le16 reserved_for_local_link_addr; + __le32 modify_mask; + __le32 active; + __le32 listen_lmac; + __le32 cck_rates; + __le32 ofdm_rates; + __le32 cck_short_preamble; + __le32 short_slot; + __le32 protection_flags; + /* MAC_QOS_PARAM_API_S_VER_1 */ + __le32 qos_flags; + struct iwl_ac_qos ac[AC_NUM + 1]; + u8 htc_trig_based_pkt_ext; + u8 rand_alloc_ecwmin; + u8 rand_alloc_ecwmax; + u8 ndp_fdbk_buff_th_exp; + struct iwl_he_backoff_conf trig_based_txf[AC_NUM]; + __le32 dtim_time; + __le64 dtim_tsf; + __le32 assoc_beacon_arrive_time; + __le32 bi; + __le32 dtim_interval; + __le32 beacon_template; + __le16 puncture_mask; + __le16 frame_time_rts_th; + __le32 flags; + __le32 flags_mask; + /* The below fields are for multi-bssid */ + u8 ref_bssid_addr[6]; + __le16 reserved_for_ref_bssid_addr; + u8 bssid_index; + u8 bss_color; + u8 reserved[2]; +} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index d71f0a99b7c4..ec6346c6c8e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -7,7 +7,7 @@ iwlmvm-y += power.o coex.o iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-y += ftm-responder.o ftm-initiator.o iwlmvm-y += rfi.o -iwlmvm-y += mld-key.o mld-mac.o +iwlmvm-y += mld-key.o mld-mac.o link.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-$(CONFIG_PM) += d3.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/link.c b/drivers/net/wireless/intel/iwlwifi/mvm/link.c new file mode 100644 index 000000000000..2688cb49c951 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/link.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include "mvm.h" + +static int iwl_mvm_link_cmd_send(struct iwl_mvm *mvm, + struct iwl_link_config_cmd *cmd, + enum iwl_ctxt_action action) +{ + int ret; + + cmd->action = cpu_to_le32(action); + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), 0, + sizeof(*cmd), cmd); + if (ret) + IWL_ERR(mvm, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n", + action, ret); + return ret; +} + +int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_link_config_cmd cmd = {}; + + if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) + return -EINVAL; + + /* Update SF - Disable if needed. if this fails, SF might still be on + * while many macs are bound, which is forbidden - so fail the binding. + */ + if (iwl_mvm_sf_update(mvm, vif, false)) + return -EINVAL; + + cmd.link_id = cpu_to_le32(mvmvif->phy_ctxt->id); + cmd.mac_id = cpu_to_le32(mvmvif->id); + cmd.phy_id = cpu_to_le32(mvmvif->phy_ctxt->id); + + memcpy(cmd.local_link_addr, vif->addr, ETH_ALEN); + + return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD); +} + +int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + u32 changes, bool active) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_phy_ctxt *phyctxt = mvmvif->phy_ctxt; + struct iwl_link_config_cmd cmd = {}; + u32 ht_flag, flags = 0, flags_mask = 0; + + if (!phyctxt) + return -EINVAL; + + cmd.link_id = cpu_to_le32(phyctxt->id); + + /* The phy_id, link address and listen_lmac can be modified only until + * the link becomes active, otherwise they will be ignored. + */ + cmd.phy_id = cpu_to_le32(phyctxt->id); + cmd.mac_id = cpu_to_le32(mvmvif->id); + + memcpy(cmd.local_link_addr, vif->addr, ETH_ALEN); + + cmd.active = cpu_to_le32(active); + + /* TODO: set a value to cmd.listen_lmac when system requiremens + * will define it + */ + + iwl_mvm_set_fw_basic_rates(mvm, vif, &cmd.cck_rates, &cmd.ofdm_rates); + + cmd.cck_short_preamble = cpu_to_le32(vif->bss_conf.use_short_preamble); + cmd.short_slot = cpu_to_le32(vif->bss_conf.use_short_slot); + + /* The fw does not distinguish between ht and fat */ + ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT; + iwl_mvm_set_fw_protection_flags(mvm, vif, &cmd.protection_flags, + ht_flag, LINK_PROT_FLG_TGG_PROTECT); + + iwl_mvm_set_fw_qos_params(mvm, vif, &cmd.ac[0], &cmd.qos_flags); + + /* We need the dtim_period to set the MAC as associated */ + if (vif->cfg.assoc && vif->bss_conf.dtim_period) + iwl_mvm_set_fw_dtim_tbtt(mvm, vif, &cmd.dtim_tsf, + &cmd.dtim_time, + &cmd.assoc_beacon_arrive_time); + else + changes &= ~LINK_CONTEXT_MODIFY_BEACON_TIMING; + + cmd.bi = cpu_to_le32(vif->bss_conf.beacon_int); + cmd.dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * + vif->bss_conf.dtim_period); + + /* TODO: Assumes that the beacon id == mac context id */ + cmd.beacon_template = cpu_to_le32(mvmvif->id); + + if (!vif->bss_conf.he_support || iwlwifi_mod_params.disable_11ax || + !vif->cfg.assoc) { + changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS; + goto send_cmd; + } + + cmd.htc_trig_based_pkt_ext = vif->bss_conf.htc_trig_based_pkt_ext; + + if (vif->bss_conf.uora_exists) { + cmd.rand_alloc_ecwmin = + vif->bss_conf.uora_ocw_range & 0x7; + cmd.rand_alloc_ecwmax = + (vif->bss_conf.uora_ocw_range >> 3) & 0x7; + } + + /* TODO how to set ndp_fdbk_buff_th_exp? */ + + if (iwl_mvm_set_fw_mu_edca_params(mvm, mvmvif, + &cmd.trig_based_txf[0])) { + flags |= LINK_FLG_MU_EDCA_CW; + flags_mask |= LINK_FLG_MU_EDCA_CW; + } + + if (vif->bss_conf.eht_puncturing && !iwlwifi_mod_params.disable_11be) + cmd.puncture_mask = cpu_to_le16(vif->bss_conf.eht_puncturing); + else + /* This flag can be set only if the MAC has eht support */ + changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; + + cmd.bss_color = vif->bss_conf.he_bss_color.color; + + if (!vif->bss_conf.he_bss_color.enabled) { + flags |= LINK_FLG_BSS_COLOR_DIS; + flags_mask |= LINK_FLG_BSS_COLOR_DIS; + } + + cmd.frame_time_rts_th = cpu_to_le16(vif->bss_conf.frame_time_rts_th); + + /* Block 26-tone RU OFDMA transmissions */ + if (mvmvif->he_ru_2mhz_block) { + flags |= LINK_FLG_RU_2MHZ_BLOCK; + flags_mask |= LINK_FLG_RU_2MHZ_BLOCK; + } + + if (vif->bss_conf.nontransmitted) { + ether_addr_copy(cmd.ref_bssid_addr, + vif->bss_conf.transmitter_bssid); + cmd.bssid_index = vif->bss_conf.bssid_index; + } + +send_cmd: + cmd.modify_mask = cpu_to_le32(changes); + cmd.flags = cpu_to_le32(flags); + cmd.flags_mask = cpu_to_le32(flags_mask); + + return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_MODIFY); +} + +int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_link_config_cmd cmd = {}; + int ret; + + if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) + return -EINVAL; + + cmd.link_id = cpu_to_le32(mvmvif->phy_ctxt->id); + ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE); + + if (!ret) + if (iwl_mvm_sf_update(mvm, vif, true)) + IWL_ERR(mvm, "Failed to update SF state\n"); + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index b21327ec4401..4d7ad91c53d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -396,9 +396,8 @@ static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, *ofdm_rates = ofdm; } -static void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - __le32 *cck_rates, __le32 *ofdm_rates) +void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + __le32 *cck_rates, __le32 *ofdm_rates) { struct ieee80211_chanctx_conf *chanctx; u8 cck_ack_rates, ofdm_ack_rates; @@ -414,11 +413,10 @@ static void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, *ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); } -static void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - __le32 *protection_flags, - u32 ht_flag, - u32 tgg_flag) +void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + __le32 *protection_flags, u32 ht_flag, + u32 tgg_flag) { /* for both sta and ap, ht_operation_mode hold the protection_mode */ u8 protection_mode = vif->bss_conf.ht_operation_mode & @@ -460,9 +458,8 @@ static void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, } } -static void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_ac_qos *ac, __le32 *qos_flags) +void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct iwl_ac_qos *ac, __le32 *qos_flags) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int i; @@ -572,10 +569,9 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, return ret; } -static void iwl_mvm_set_fw_dtim_tbtt(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - __le64 *dtim_tsf, __le32 *dtim_time, - __le32 *assoc_beacon_arrive_time) +void iwl_mvm_set_fw_dtim_tbtt(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + __le64 *dtim_tsf, __le32 *dtim_time, + __le32 *assoc_beacon_arrive_time) { u32 dtim_offs; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 210b134c735f..cf08cb834cc4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2000,10 +2000,9 @@ static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext, * This function sets the MU EDCA parameters ans returns whether MU EDCA * is enabled or not */ -static bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - struct iwl_he_backoff_conf - *trig_based_txf) +bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + struct iwl_he_backoff_conf *trig_based_txf) { int i; /* Mark MU EDCA as enabled, unless none detected on some AC */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1287207a34b3..4663cb7a0b10 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1640,6 +1640,20 @@ u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); /* MAC (virtual interface) programming */ +void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + __le32 *cck_rates, __le32 *ofdm_rates); +void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + __le32 *protection_flags, u32 ht_flag, + u32 tgg_flag); +void iwl_mvm_set_fw_qos_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct iwl_ac_qos *ac, __le32 *qos_flags); +bool iwl_mvm_set_fw_mu_edca_params(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + struct iwl_he_backoff_conf *trig_based_txf); +void iwl_mvm_set_fw_dtim_tbtt(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + __le64 *dtim_tsf, __le32 *dtim_time, + __le32 *assoc_beacon_arrive_time); __le32 iwl_mac_ctxt_p2p_dev_has_extended_disc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_mac_ctxt_cmd_ap_set_filter_flags(struct iwl_mvm *mvm, @@ -1702,6 +1716,12 @@ void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +/* Links */ +int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + u32 changes, bool active); +int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + /* Quota management */ static inline size_t iwl_mvm_quota_cmd_size(struct iwl_mvm *mvm) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 69c390ff2745..c3767f62026a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -522,6 +522,7 @@ static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { HCMD_NAME(CHANNEL_SWITCH_TIME_EVENT_CMD), HCMD_NAME(SESSION_PROTECTION_CMD), HCMD_NAME(MAC_CONFIG_CMD), + HCMD_NAME(LINK_CONFIG_CMD), HCMD_NAME(SESSION_PROTECTION_NOTIF), HCMD_NAME(CHANNEL_SWITCH_START_NOTIF), }; -- cgit v1.2.3 From 006c152ac9e56ac7871efa995854c3ff8cf6915a Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:20 +0200 Subject: wifi: iwlwifi: mvm: add support for the new STA related commands As a part of the new MLD FW API changes, we have new commands for STA related operations (add/remove/aux/disable tx). Add structures and enum definitions, along with part of the functions that sends this commands. This functions will be in used and more will be added in the next patches. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.132873ce015c.I7b12a77e5be066730762e6ceeeaa7190293c3df1@changeid Signed-off-by: Johannes Berg --- .../net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 128 ++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 281 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 8 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 16 +- 6 files changed, 431 insertions(+), 8 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index 301f34f13523..a111e7366d49 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -41,6 +41,22 @@ enum iwl_mac_conf_subcmd_ids { * @LINK_CONFIG_CMD: &struct iwl_link_config_cmd */ LINK_CONFIG_CMD = 0x9, + /** + * @STA_CONFIG_CMD: &struct iwl_mvm_sta_cfg_cmd + */ + STA_CONFIG_CMD = 0xA, + /** + * @AUX_STA_CMD: &struct iwl_mvm_aux_sta_cmd + */ + AUX_STA_CMD = 0xB, + /** + * @STA_REMOVE_CMD: &struct iwl_mvm_remove_sta_cmd + */ + STA_REMOVE_CMD = 0xC, + /** + * @STA_DISABLE_TX_CMD: &struct iwl_mvm_sta_disable_tx_cmd + */ + STA_DISABLE_TX_CMD = 0xD, /** * @SESSION_PROTECTION_NOTIF: &struct iwl_mvm_session_prot_notif */ @@ -466,4 +482,116 @@ struct iwl_link_config_cmd { u8 reserved[2]; } __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1 */ +/** + * enum iwl_fw_sta_type - FW station types + * @STATION_TYPE_PEER: represents a peer - AP in BSS, a TDLS sta, a client in + * P2P. + * @STATION_TYPE_BCAST_MGMT: The station used to send beacons and + * probe responses. Also used for traffic injection in sniffer mode + * @STATION_TYPE_MCAST: the station used for BCAST / MCAST in GO. Will be + * suspended / resumed at the right timing depending on the clients' + * power save state and the DTIM timing + */ +enum iwl_fw_sta_type { + STATION_TYPE_PEER, + STATION_TYPE_BCAST_MGMT, + STATION_TYPE_MCAST, +}; /* STATION_TYPE_E_VER_1 */ + +/** + * struct iwl_mvm_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's + * station table + * ( STA_CONFIG_CMD = 0xA ) + * + * @sta_id: index of station in uCode's station table + * @link_id: the id of the link that is used to communicate with this sta + * @peer_mld_address: the peers mld address + * @reserved_for_peer_mld_address: reserved + * @peer_link_address: the address of the link that is used to communicate + * with this sta + * @reserved_for_peer_link_address: reserved + * @station_type: type of this station. See &enum iwl_fw_sta_type + * @assoc_id: for GO only + * @beamform_flags: beam forming controls + * @mfp: indicates whether the STA uses management frame protection or not. + * @mimo: indicates whether the sta uses mimo or not + * @mimo_protection: indicates whether the sta uses mimo protection or not + * @ack_enabled: indicates that the AP supports receiving ACK- + * enabled AGG, i.e. both BACK and non-BACK frames in a single AGG + * @trig_rnd_alloc: indicates that trigger based random allocation + * is enabled according to UORA element existence + * @tx_ampdu_spacing: minimum A-MPDU spacing: + * 4 - 2us density, 5 - 4us density, 6 - 8us density, 7 - 16us density + * @tx_ampdu_max_size: maximum A-MPDU length: 0 - 8K, 1 - 16K, 2 - 32K, + * 3 - 64K, 4 - 128K, 5 - 256K, 6 - 512K, 7 - 1024K. + * @sp_length: the size of the SP in actual number of frames + * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver + * enabled ACs. + * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY + * capa + * @htc_flags: which features are supported in HTC + */ +struct iwl_mvm_sta_cfg_cmd { + __le32 sta_id; + __le32 link_id; + u8 peer_mld_address[ETH_ALEN]; + __le16 reserved_for_peer_mld_address; + u8 peer_link_address[ETH_ALEN]; + __le16 reserved_for_peer_link_address; + __le32 station_type; + __le32 assoc_id; + __le32 beamform_flags; + __le32 mfp; + __le32 mimo; + __le32 mimo_protection; + __le32 ack_enabled; + __le32 trig_rnd_alloc; + __le32 tx_ampdu_spacing; + __le32 tx_ampdu_max_size; + __le32 sp_length; + __le32 uapsd_acs; + struct iwl_he_pkt_ext_v2 pkt_ext; + __le32 htc_flags; +} __packed; /* STA_CMD_API_S_VER_1 */ + +/** + * struct iwl_mvm_aux_sta_cmd - command for AUX STA configuration + * ( AUX_STA_CMD = 0xB ) + * + * @sta_id: index of aux sta to configure + * @lmac_id: ? + * @mac_addr: mac addr of the auxilary sta + * @reserved_for_mac_addr: reserved + */ +struct iwl_mvm_aux_sta_cmd { + __le32 sta_id; + __le32 lmac_id; + u8 mac_addr[ETH_ALEN]; + __le16 reserved_for_mac_addr; + +} __packed; /* AUX_STA_CMD_API_S_VER_1 */ + +/** + * struct iwl_mvm_remove_sta_cmd - a cmd structure to remove a sta added by + * STA_CONFIG_CMD or AUX_STA_CONFIG_CMD + * ( STA_REMOVE_CMD = 0xC ) + * + * @sta_id: index of station to remove + */ +struct iwl_mvm_remove_sta_cmd { + __le32 sta_id; +} __packed; /* REMOVE_STA_API_S_VER_1 */ + +/** + * struct iwl_mvm_sta_disable_tx_cmd - disable / re-enable tx to a sta + * ( STA_DISABLE_TX_CMD = 0xD ) + * + * @sta_id: index of the station to disable tx to + * @disable: indicates if to disable or re-enable tx + */ +struct iwl_mvm_sta_disable_tx_cmd { + __le32 sta_id; + __le32 disable; +} __packed; /* STA_DISABLE_TX_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index ec6346c6c8e4..0e9b5381e265 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -7,7 +7,7 @@ iwlmvm-y += power.o coex.o iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-y += ftm-responder.o ftm-initiator.o iwlmvm-y += rfi.o -iwlmvm-y += mld-key.o mld-mac.o link.o +iwlmvm-y += mld-key.o mld-mac.o link.o mld-sta.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-$(CONFIG_PM) += d3.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c new file mode 100644 index 000000000000..cef4fc441afe --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include "mvm.h" + +static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm, + struct iwl_mvm_sta_cfg_cmd *cmd) +{ + int ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), + 0, sizeof(*cmd), cmd); + if (ret) + IWL_ERR(mvm, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); + return ret; +} + +/* + * Add an internal station to the FW table + */ +static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + const u8 *addr, + u16 phy_id) +{ + struct iwl_mvm_sta_cfg_cmd cmd; + + lockdep_assert_held(&mvm->mutex); + + memset(&cmd, 0, sizeof(cmd)); + cmd.sta_id = cpu_to_le32((u8)sta->sta_id); + + cmd.link_id = cpu_to_le32(phy_id); + + cmd.station_type = cpu_to_le32(sta->type); + + if (addr) { + memcpy(cmd.peer_mld_address, addr, ETH_ALEN); + memcpy(cmd.peer_link_address, addr, ETH_ALEN); + } + + return iwl_mvm_mld_send_sta_cmd(mvm, &cmd); +} + +/* + * Remove a station from the FW table. Before sending the command to remove + * the station validate that the station is indeed known to the driver (sanity + * only). + */ +static int iwl_mvm_mld_rm_sta_from_fw(struct iwl_mvm *mvm, u32 sta_id) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_remove_sta_cmd rm_sta_cmd = { + .sta_id = cpu_to_le32(sta_id), + }; + int ret; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + + /* Note: internal stations are marked as error values */ + if (!sta) { + IWL_ERR(mvm, "Invalid station id\n"); + return -EINVAL; + } + + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD), + 0, sizeof(rm_sta_cmd), &rm_sta_cmd); + if (ret) { + IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); + return ret; + } + + return 0; +} + +/* + * Adds an internal sta to the FW table with its queues + */ +static int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + const u8 *addr, int phy_id, + u16 *queue, u8 tid, + unsigned int *_wdg_timeout) +{ + int ret, txq; + unsigned int wdg_timeout = _wdg_timeout ? *_wdg_timeout : + mvm->trans->trans_cfg->base_params->wd_timeout; + + if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA)) + return -ENOSPC; + + ret = iwl_mvm_mld_add_int_sta_to_fw(mvm, sta, addr, phy_id); + if (ret) + return ret; + + /* + * For 22000 firmware and on we cannot add queue to a station unknown + * to firmware so enable queue here - after the station was added + */ + txq = iwl_mvm_tvqm_enable_txq(mvm, NULL, sta->sta_id, tid, + wdg_timeout); + if (txq < 0) { + iwl_mvm_mld_rm_sta_from_fw(mvm, sta->sta_id); + return txq; + } + *queue = txq; + + return 0; +} + +/* + * Adds a new int sta: allocate it in the driver, add it to the FW table, + * and add its queues. + */ +static int iwl_mvm_mld_add_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *int_sta, u16 *queue, + enum nl80211_iftype iftype, + enum iwl_fw_sta_type sta_type, + int phy_id, const u8 *addr, u8 tid, + unsigned int *wdg_timeout) +{ + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* qmask argument is not used in the new tx api, send a don't care */ + ret = iwl_mvm_allocate_int_sta(mvm, int_sta, 0, iftype, + sta_type); + if (ret) + return ret; + + ret = iwl_mvm_mld_add_int_sta_with_queue(mvm, int_sta, addr, phy_id, + queue, tid, wdg_timeout); + if (ret) { + iwl_mvm_dealloc_int_sta(mvm, int_sta); + return ret; + } + + return 0; +} + +/* Allocate a new station entry for the broadcast station to the given vif, + * and send it to the FW. + * Note that each P2P mac should have its own broadcast station. + */ +int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; + static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + const u8 *baddr = _baddr; + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, vif, false, false); + u16 *queue; + + lockdep_assert_held(&mvm->mutex); + + if (vif->type == NL80211_IFTYPE_ADHOC) + baddr = vif->bss_conf.bssid; + + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { + queue = &mvm->probe_queue; + } else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + queue = &mvm->p2p_dev_queue; + } else { + WARN(1, "Missing required TXQ for adding bcast STA\n"); + return -EINVAL; + } + + return iwl_mvm_mld_add_int_sta(mvm, bsta, queue, + ieee80211_vif_type_p2p(vif), + STATION_TYPE_BCAST_MGMT, + mvmvif->phy_ctxt->id, baddr, + IWL_MAX_TID_COUNT, &wdg_timeout); +} + +/* Allocate a new station entry for the sniffer station to the given vif, + * and send it to the FW. + */ +int iwl_mvm_mld_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + lockdep_assert_held(&mvm->mutex); + + return iwl_mvm_mld_add_int_sta(mvm, &mvm->snif_sta, &mvm->snif_queue, + vif->type, STATION_TYPE_BCAST_MGMT, + mvmvif->phy_ctxt->id, NULL, + IWL_MAX_TID_COUNT, NULL); +} + +static int iwl_mvm_mld_disable_txq(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + u16 *queueptr, u8 tid) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + int queue = *queueptr; + int ret = 0; + + if (mvm->sta_remove_requires_queue_remove) { + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD); + struct iwl_scd_queue_cfg_cmd remove_cmd = { + .operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE), + .u.remove.tid = cpu_to_le32(tid), + .u.remove.sta_mask = + cpu_to_le32(BIT(mvmsta->sta_id)), + }; + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, + sizeof(remove_cmd), + &remove_cmd); + } + + iwl_trans_txq_free(mvm->trans, queue); + *queueptr = IWL_MVM_INVALID_QUEUE; + + return ret; +} + +/* Removes a sta from the FW table, disable its queues, and dealloc it + */ +static int iwl_mvm_mld_rm_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *int_sta, + bool flush, u8 tid, u16 *queuptr) +{ + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(int_sta->sta_id == IWL_MVM_INVALID_STA)) + return -EINVAL; + + if (flush) + iwl_mvm_flush_sta(mvm, int_sta, true); + + iwl_mvm_mld_disable_txq(mvm, NULL, queuptr, tid); + + ret = iwl_mvm_mld_rm_sta_from_fw(mvm, int_sta->sta_id); + if (ret) + IWL_WARN(mvm, "Failed sending remove station\n"); + + iwl_mvm_dealloc_int_sta(mvm, int_sta); + + return ret; +} + +int iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u16 *queueptr; + + lockdep_assert_held(&mvm->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + queueptr = &mvm->probe_queue; + break; + case NL80211_IFTYPE_P2P_DEVICE: + queueptr = &mvm->p2p_dev_queue; + break; + default: + WARN(1, "Can't free bcast queue on vif type %d\n", + vif->type); + return -EINVAL; + } + + return iwl_mvm_mld_rm_int_sta(mvm, &mvmvif->bcast_sta, true, + IWL_MAX_TID_COUNT, queueptr); +} + +int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + lockdep_assert_held(&mvm->mutex); + + return iwl_mvm_mld_rm_int_sta(mvm, &mvm->snif_sta, false, + IWL_MAX_TID_COUNT, &mvm->snif_queue); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index c3767f62026a..03da4e53de67 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -523,6 +523,10 @@ static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { HCMD_NAME(SESSION_PROTECTION_CMD), HCMD_NAME(MAC_CONFIG_CMD), HCMD_NAME(LINK_CONFIG_CMD), + HCMD_NAME(STA_CONFIG_CMD), + HCMD_NAME(AUX_STA_CMD), + HCMD_NAME(STA_REMOVE_CMD), + HCMD_NAME(STA_DISABLE_TX_CMD), HCMD_NAME(SESSION_PROTECTION_NOTIF), HCMD_NAME(CHANNEL_SWITCH_START_NOTIF), }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 46af2b422849..313c5416ae5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -783,9 +783,9 @@ static int iwl_mvm_get_queue_size(struct ieee80211_sta *sta) return IWL_DEFAULT_QUEUE_SIZE; } -static int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - u8 sta_id, u8 tid, unsigned int timeout) +int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + u8 sta_id, u8 tid, unsigned int timeout) { int queue, size; @@ -2005,7 +2005,7 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, u32 qmask, enum nl80211_iftype iftype, - enum iwl_sta_type type) + u8 type) { if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || sta->sta_id == IWL_MVM_INVALID_STA) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index f1a4fc3e4038..d11851b47684 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2016 Intel Deutschland GmbH */ @@ -436,7 +436,7 @@ iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) */ struct iwl_mvm_int_sta { u32 sta_id; - enum iwl_sta_type type; + u8 type; u32 tfd_queue_msk; }; @@ -519,7 +519,7 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, u32 qmask, enum nl80211_iftype iftype, - enum iwl_sta_type type); + u8 type); void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta); int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -551,4 +551,14 @@ int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 mac_id); +/* Queues */ +int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + u8 sta_id, u8 tid, unsigned int timeout); + +/* New MLD STA related APIs */ +int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mld_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); #endif /* __sta_h__ */ -- cgit v1.2.3 From 1ab26632332eac61cc24464a74fd9bcf5ec5167b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:21 +0200 Subject: wifi: iwlwifi: mvm: Add an add_interface() callback for mld mode As the MLD mode and its new APIs are introduced, we've decided to add a new ieee80211_ops dedicated for MLD callbacks. Add the MLD add_interface() callback which uses the new MLD APIs added the previous patches. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.6adc29bff39b.I97ed469028009be9392dcc6f7b5ffbe45f4b2c43@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 85 ++++++++++----- .../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 118 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 8 ++ 4 files changed, 184 insertions(+), 29 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index 0e9b5381e265..b82f79ac5378 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -7,7 +7,7 @@ iwlmvm-y += power.o coex.o iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-y += ftm-responder.o ftm-initiator.o iwlmvm-y += rfi.o -iwlmvm-y += mld-key.o mld-mac.o link.o mld-sta.o +iwlmvm-y += mld-key.o mld-mac.o link.o mld-sta.o mld-mac80211.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-$(CONFIG_PM) += d3.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index cf08cb834cc4..73b164aad86d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1216,7 +1216,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) cancel_work_sync(&mvm->async_handlers_wk); } -static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) +struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) { u16 i; @@ -1350,7 +1350,7 @@ static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw, iwl_mvm_post_channel_switch(hw, vif); } -static void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk) +void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk) { struct iwl_mvm_vif *mvmvif; struct ieee80211_vif *vif; @@ -1384,12 +1384,17 @@ iwl_mvm_chandef_get_primary_80(struct cfg80211_chan_def *chandef) return (control_start - data_start) / 80; } -static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +/* + * Returns true if addding the interface is done + * (either with success or failure) + */ +bool iwl_mvm_mac_add_interface_common(struct iwl_mvm *mvm, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int *ret) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; + + lockdep_assert_held(&mvm->mutex); mvmvif->mvm = mvm; RCU_INIT_POINTER(mvmvif->probe_resp_data, NULL); @@ -1400,17 +1405,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, * don't really have to check the types. */ - mutex_lock(&mvm->mutex); - /* make sure that beacon statistics don't go backwards with FW reset */ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) mvmvif->beacon_stats.accu_num_beacons += mvmvif->beacon_stats.num_beacons; /* Allocate resources for the MAC context, and add it to the fw */ - ret = iwl_mvm_mac_ctxt_init(mvm, vif); - if (ret) - goto out_unlock; + *ret = iwl_mvm_mac_ctxt_init(mvm, vif); + if (*ret) + return true; rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif); @@ -1427,27 +1430,48 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, */ if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) { - ret = iwl_mvm_alloc_bcast_sta(mvm, vif); - if (ret) { - IWL_ERR(mvm, "Failed to allocate bcast sta\n"); - goto out_unlock; - } - - /* - * Only queue for this station is the mcast queue, - * which shouldn't be in TFD mask anyway - */ - ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta, - 0, vif->type, - IWL_STA_MULTICAST); - if (ret) - goto out_unlock; - iwl_mvm_vif_dbgfs_register(mvm, vif); - goto out_unlock; + return true; } mvmvif->features |= hw->netdev_features; + return false; +} + +static int iwl_mvm_alloc_bcast_mcast_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_alloc_bcast_sta(mvm, vif); + if (ret) { + IWL_ERR(mvm, "Failed to allocate bcast sta\n"); + return ret; + } + + /* + * Only queue for this station is the mcast queue, + * which shouldn't be in TFD mask anyway + */ + return iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta, 0, vif->type, + IWL_STA_MULTICAST); +} + +static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + mutex_lock(&mvm->mutex); + + /* Common for MLD and non-MLD API */ + if (iwl_mvm_mac_add_interface_common(mvm, hw, vif, &ret)) + goto out; ret = iwl_mvm_mac_ctxt_add(mvm, vif); if (ret) @@ -1516,6 +1540,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, mvm->csme_vif = vif; } +out: + if (!ret && (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC)) + ret = iwl_mvm_alloc_bcast_mcast_sta(mvm, vif); + goto out_unlock; out_unbind: diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c new file mode 100644 index 000000000000..b233bdd68b37 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ +#include "mvm.h" + +static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + mutex_lock(&mvm->mutex); + + /* Common for MLD and non-MLD API */ + if (iwl_mvm_mac_add_interface_common(mvm, hw, vif, &ret)) + goto out_unlock; + + ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); + if (ret) + goto out_unlock; + + ret = iwl_mvm_power_update_mac(mvm); + if (ret) + goto out_remove_mac; + + /* beacon filtering */ + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + if (ret) + goto out_remove_mac; + + if (!mvm->bf_allowed_vif && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { + mvm->bf_allowed_vif = mvmvif; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + } + + /* + * P2P_DEVICE interface does not have a channel context assigned to it, + * so a dedicated PHY context is allocated to it and the corresponding + * MAC context is bound to it at this stage. + */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!mvmvif->phy_ctxt) { + ret = -ENOSPC; + goto out_free_bf; + } + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + ret = iwl_mvm_add_link(mvm, vif); + if (ret) + goto out_unref_phy; + + ret = iwl_mvm_link_changed(mvm, vif, + LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_RATES_INFO, + true); + if (ret) + goto out_remove_link; + + ret = iwl_mvm_mld_add_bcast_sta(mvm, vif); + if (ret) + goto out_remove_link; + + /* Save a pointer to p2p device vif, so it can later be used to + * update the p2p device MAC when a GO is started/stopped + */ + mvm->p2p_device_vif = vif; + } + + iwl_mvm_tcm_add_vif(mvm, vif); + INIT_DELAYED_WORK(&mvmvif->csa_work, + iwl_mvm_channel_switch_disconnect_wk); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + mvm->monitor_on = true; + ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); + } + + iwl_mvm_vif_dbgfs_register(mvm, vif); + + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + !mvm->csme_vif && mvm->mei_registered) { + iwl_mei_set_nic_info(vif->addr, mvm->nvm_data->hw_addr); + iwl_mei_set_netdev(ieee80211_vif_to_wdev(vif)->netdev); + mvm->csme_vif = vif; + } + + goto out_unlock; + + out_remove_link: + /* Link needs to be deactivated before removal */ + iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); + iwl_mvm_remove_link(mvm, vif); + out_unref_phy: + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + out_free_bf: + if (mvm->bf_allowed_vif == mvmvif) { + mvm->bf_allowed_vif = NULL; + vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI); + } + out_remove_mac: + mvmvif->phy_ctxt = NULL; + iwl_mvm_mld_mac_ctxt_remove(mvm, vif); + out_unlock: + mutex_unlock(&mvm->mutex); + + return ret; +} + +const struct ieee80211_ops iwl_mvm_mld_hw_ops = { + .add_interface = iwl_mvm_mld_mac_add_interface, +}; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 4663cb7a0b10..50230a9dbcc8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -72,6 +72,7 @@ #define IWL_MVM_OFFCHANNEL_QUEUE 0 extern const struct ieee80211_ops iwl_mvm_hw_ops; +extern const struct ieee80211_ops iwl_mvm_mld_hw_ops; /** * struct iwl_mvm_mod_params - module parameters for iwlmvm @@ -1624,6 +1625,7 @@ void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); /* MVM PHY */ +struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm); int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic); @@ -1640,6 +1642,9 @@ u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); /* MAC (virtual interface) programming */ +bool iwl_mvm_mac_add_interface_common(struct iwl_mvm *mvm, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int *ret); void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, __le32 *cck_rates, __le32 *ofdm_rates); void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, @@ -2158,6 +2163,9 @@ static inline u8 iwl_mvm_phy_band_from_nl80211(enum nl80211_band band) } } +/* Channel Switch */ +void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk); + /* Channel info utils */ static inline bool iwl_mvm_has_ultra_hb_channel(struct iwl_mvm *mvm) { -- cgit v1.2.3 From 60efeca1c6a75d7f518c963fa4f3694dcfc1147e Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:22 +0200 Subject: wifi: iwlwifi: mvm: Add a remove_interface() callback for mld mode As the MLD mode and its new APIs are introduced, we've decided to add a new ieee80211_ops dedicated for MLD callbacks. Add the MLD remove_interface() callback which uses the new MLD APIs. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.b87c5c0a4b6b.I631173a73d6ffd7232aa539ea8b356a222fac398@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 32 ++++++++++++++++---- .../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 34 ++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 4 +-- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 2 ++ 5 files changed, 66 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 73b164aad86d..bdec5ae5e83a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1579,7 +1579,11 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, } } -static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, +/* This function is doing the common part of removing the interface for + * both - MLD and non-MLD modes. Returns true if removing the interface + * is done + */ +bool iwl_mvm_mac_remove_interface_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -1628,11 +1632,22 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->noa_duration = 0; } #endif - iwl_mvm_dealloc_int_sta(mvm, &mvmvif->mcast_sta); - iwl_mvm_dealloc_bcast_sta(mvm, vif); - goto out_release; + return true; } + iwl_mvm_power_update_mac(mvm); + return false; +} + +static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (iwl_mvm_mac_remove_interface_common(hw, vif)) + goto out; + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { mvm->p2p_device_vif = NULL; iwl_mvm_rm_p2p_bcast_sta(mvm, vif); @@ -1641,7 +1656,6 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvmvif->phy_ctxt = NULL; } - iwl_mvm_power_update_mac(mvm); iwl_mvm_mac_ctxt_remove(mvm, vif); RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); @@ -1649,7 +1663,13 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, if (vif->type == NL80211_IFTYPE_MONITOR) mvm->monitor_on = false; -out_release: +out: + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { + iwl_mvm_dealloc_int_sta(mvm, &mvmvif->mcast_sta); + iwl_mvm_dealloc_bcast_sta(mvm, vif); + } + mutex_unlock(&mvm->mutex); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index b233bdd68b37..8dca72ec55cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -113,6 +113,40 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, return ret; } +static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (iwl_mvm_mac_remove_interface_common(hw, vif)) + goto out; + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + mvm->p2p_device_vif = NULL; + iwl_mvm_mld_rm_bcast_sta(mvm, vif); + /* Link needs to be deactivated before removal */ + iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, + false); + iwl_mvm_remove_link(mvm, vif); + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + mvmvif->phy_ctxt = NULL; + } + + iwl_mvm_mld_mac_ctxt_remove(mvm, vif); + + RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + mvm->monitor_on = false; + __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); + } + +out: + mutex_unlock(&mvm->mutex); +} + const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .add_interface = iwl_mvm_mld_mac_add_interface, + .remove_interface = iwl_mvm_mld_mac_remove_interface, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 50230a9dbcc8..32e0af87b528 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1645,6 +1645,8 @@ u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); bool iwl_mvm_mac_add_interface_common(struct iwl_mvm *mvm, struct ieee80211_hw *hw, struct ieee80211_vif *vif, int *ret); +bool iwl_mvm_mac_remove_interface_common(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); void iwl_mvm_set_fw_basic_rates(struct iwl_mvm *mvm, struct ieee80211_vif *vif, __le32 *cck_rates, __le32 *ofdm_rates); void iwl_mvm_set_fw_protection_flags(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 313c5416ae5e..bbcc7459f3a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2256,8 +2256,8 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return 0; } -static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u16 *queueptr, queue; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index d11851b47684..cad6e879c999 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -510,6 +510,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id); int iwl_mvm_rm_aux_sta(struct iwl_mvm *mvm); int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_add_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -- cgit v1.2.3 From 8a919a78a469bb4e01ced9766bdbb7a03c8a4edf Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:23 +0200 Subject: wifi: iwlwifi: mvm: refactor __iwl_mvm_assign_vif_chanctx() Since parts of the functionality of this function is going to be used also by the MLD version of it, put in a separate function the parts that are common for both MLD and non-MLD modes. The common function will later be used in the MLD ops. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.844755701cac.I1c650718ad2381eabc38f4103c1aac67936a1ffc@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 47 +++++++++++++++++------ 1 file changed, 36 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index bdec5ae5e83a..8123dad3b710 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4405,15 +4405,21 @@ out_unlock: mutex_unlock(&mvm->mutex); } -static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx, - bool switching_chanctx) +/* + * This function executes the common part for MLD and non-MLD modes. + * + * Returns true if we're done assigning the chanctx + * (either on failure or success) + */ +static bool __iwl_mvm_assign_vif_chanctx_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx, + int *ret) { u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; lockdep_assert_held(&mvm->mutex); @@ -4432,19 +4438,32 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, * The AP binding flow is handled as part of the start_ap flow * (in bss_info_changed), similarly for IBSS. */ - ret = 0; - goto out; + *ret = 0; + return true; case NL80211_IFTYPE_STATION: - mvmvif->csa_bcn_pending = false; break; case NL80211_IFTYPE_MONITOR: /* always disable PS when a monitor interface is active */ mvmvif->ps_disabled = true; break; default: - ret = -EINVAL; - goto out; + *ret = -EINVAL; + return true; } + return false; +} + +static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (__iwl_mvm_assign_vif_chanctx_common(mvm, vif, ctx, + switching_chanctx, &ret)) + goto out; ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) @@ -4478,7 +4497,12 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); } - if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) { + if (vif->type == NL80211_IFTYPE_STATION) { + if (!switching_chanctx) { + mvmvif->csa_bcn_pending = false; + goto out; + } + mvmvif->csa_bcn_pending = true; if (!fw_has_capa(&mvm->fw->ucode_capa, @@ -4506,6 +4530,7 @@ out: mvmvif->phy_ctxt = NULL; return ret; } + static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, -- cgit v1.2.3 From 50e81437a4831e32b0d04a828aa18c02356ee23e Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:24 +0200 Subject: wifi: iwlwifi: mvm: add an assign_vif_chanctx() callback for MLD mode This is another patch in the series adding all the ops for the new MLD ieee80211_ops. The callback added here uses the new MLD FW API. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.d3753975e720.I45f89cc81370d2cf8d4f51748ccb3ec675eff1bd@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 9 ++-- .../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 60 ++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 +++ 3 files changed, 70 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 8123dad3b710..d4a3d9259150 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4411,11 +4411,10 @@ out_unlock: * Returns true if we're done assigning the chanctx * (either on failure or success) */ -static bool __iwl_mvm_assign_vif_chanctx_common(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx, - bool switching_chanctx, - int *ret) +bool __iwl_mvm_assign_vif_chanctx_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx, int *ret) { u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 8dca72ec55cb..81f88d1b0feb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -146,7 +146,67 @@ out: mutex_unlock(&mvm->mutex); } +static int __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (__iwl_mvm_assign_vif_chanctx_common(mvm, vif, ctx, + switching_chanctx, &ret)) + goto out; + + ret = iwl_mvm_add_link(mvm, vif); + if (ret) + goto out; + ret = iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, + true); + if (ret) + goto out_remove_link; + + /* + * Power state must be updated before quotas, + * otherwise fw will complain. + */ + iwl_mvm_power_update_mac(mvm); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + ret = iwl_mvm_mld_add_snif_sta(mvm, vif); + if (ret) + goto out_remove_link; + } + + goto out; + +out_remove_link: + /* Link needs to be deactivated before removal */ + iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); + iwl_mvm_remove_link(mvm, vif); + iwl_mvm_power_update_mac(mvm); +out: + if (ret) + mvmvif->phy_ctxt = NULL; + return ret; +} + +static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + ret = __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, ctx, false); + mutex_unlock(&mvm->mutex); + + return ret; +} const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .add_interface = iwl_mvm_mld_mac_add_interface, .remove_interface = iwl_mvm_mld_mac_remove_interface, + .assign_vif_chanctx = iwl_mvm_mld_assign_vif_chanctx, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 32e0af87b528..1221a0717e61 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2168,6 +2168,12 @@ static inline u8 iwl_mvm_phy_band_from_nl80211(enum nl80211_band band) /* Channel Switch */ void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk); +/* Channel Context */ +bool __iwl_mvm_assign_vif_chanctx_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx, int *ret); + /* Channel info utils */ static inline bool iwl_mvm_has_ultra_hb_channel(struct iwl_mvm *mvm) { -- cgit v1.2.3 From daddfae5527164edee09bcca0848c10ab72c5c5b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:25 +0200 Subject: wifi: iwlwifi: mvm: refactor __iwl_mvm_unassign_vif_chanctx() Since parts of the functionality of this function is going to be used also by the MLD version of it, put in a separate function the parts that are common for both MLD and non-MLD modes. The common function will later be used in the MLD ops. Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.fa05929badb9.I2222dc86cf7d7a7bb58c6a2f2529c8089bfe58b4@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 53 +++++++++++++++-------- 1 file changed, 35 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index d4a3d9259150..c006d68a785e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4545,52 +4545,69 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, return ret; } -static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx, - bool switching_chanctx) +/* + * This function executes the common part for MLD and non-MLD modes. + * + * Returns if chanctx unassign chanctx is done + * (either on failure or success) + */ +static bool __iwl_mvm_unassign_vif_chanctx_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool switching_chanctx) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_vif *disabled_vif = NULL; lockdep_assert_held(&mvm->mutex); iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); switch (vif->type) { case NL80211_IFTYPE_ADHOC: - goto out; + return true; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; mvmvif->ps_disabled = false; - iwl_mvm_rm_snif_sta(mvm, vif); break; case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ if (!switching_chanctx || !mvmvif->ap_ibss_active) - goto out; + return true; mvmvif->csa_countdown = false; - /* Set CS bit on all the stations */ - iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); - /* Save blocked iface, the timeout is set on the next beacon */ rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); mvmvif->ap_ibss_active = false; break; - case NL80211_IFTYPE_STATION: - if (!switching_chanctx) - break; + default: + break; + } + return false; +} - disabled_vif = vif; +static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_vif *disabled_vif = NULL; + + if (__iwl_mvm_unassign_vif_chanctx_common(mvm, vif, switching_chanctx)) + goto out; + if (vif->type == NL80211_IFTYPE_MONITOR) + iwl_mvm_rm_snif_sta(mvm, vif); + + if (vif->type == NL80211_IFTYPE_AP) + /* Set CS bit on all the stations */ + iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); + + if (vif->type == NL80211_IFTYPE_STATION && switching_chanctx) { + disabled_vif = vif; if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); - break; - default: - break; } iwl_mvm_update_quotas(mvm, false, disabled_vif); -- cgit v1.2.3 From 6f71e90e65e9bb5d5e12199e19670bb4973a6e39 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 14 Mar 2023 19:49:26 +0200 Subject: wifi: iwlwifi: mvm: add an unassign_vif_chanctx() callback for MLD mode This is another patch in the series adding all the ops for the new MLD ieee80211_ops. The callback added here uses the new MLD FW API Signed-off-by: Miri Korenblit Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.5d4bcd384425.I263eef3aad8efe23a597843fe7c56924038c8fdc@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 6 +-- .../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 41 +++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 58 ++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 ++ drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 7 +++ 5 files changed, 112 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index c006d68a785e..93ca92e8eb98 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4551,9 +4551,9 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, * Returns if chanctx unassign chanctx is done * (either on failure or success) */ -static bool __iwl_mvm_unassign_vif_chanctx_common(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool switching_chanctx) +bool __iwl_mvm_unassign_vif_chanctx_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool switching_chanctx) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 81f88d1b0feb..f402e235525c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -205,8 +205,49 @@ static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw, return ret; } + +static void __iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (__iwl_mvm_unassign_vif_chanctx_common(mvm, vif, switching_chanctx)) + goto out; + + if (vif->type == NL80211_IFTYPE_MONITOR) + iwl_mvm_mld_rm_snif_sta(mvm, vif); + + if (vif->type == NL80211_IFTYPE_AP) + /* Set CS bit on all the stations */ + iwl_mvm_mld_modify_all_sta_disable_tx(mvm, mvmvif, true); + + /* Link needs to be deactivated before removal */ + iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); + iwl_mvm_remove_link(mvm, vif); + +out: + if (switching_chanctx) + return; + mvmvif->phy_ctxt = NULL; + iwl_mvm_power_update_mac(mvm); +} + +static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + __iwl_mvm_mld_unassign_vif_chanctx(mvm, vif, ctx, false); + mutex_unlock(&mvm->mutex); +} const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .add_interface = iwl_mvm_mld_mac_add_interface, .remove_interface = iwl_mvm_mld_mac_remove_interface, .assign_vif_chanctx = iwl_mvm_mld_assign_vif_chanctx, + .unassign_vif_chanctx = iwl_mvm_mld_unassign_vif_chanctx, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index cef4fc441afe..4ee6f9b250d3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -279,3 +279,61 @@ int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return iwl_mvm_mld_rm_int_sta(mvm, &mvm->snif_sta, false, IWL_MAX_TID_COUNT, &mvm->snif_queue); } + +static void iwl_mvm_mld_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + bool disable) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_sta_disable_tx_cmd cmd; + int ret; + + spin_lock_bh(&mvm_sta->lock); + + if (mvm_sta->disable_tx == disable) { + spin_unlock_bh(&mvm_sta->lock); + return; + } + + mvm_sta->disable_tx = disable; + + cmd.sta_id = cpu_to_le32(mvm_sta->sta_id); + cmd.disable = cpu_to_le32(disable); + + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, STA_DISABLE_TX_CMD), + CMD_ASYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, + "Failed to send STA_DISABLE_TX_CMD command (%d)\n", + ret); + + spin_unlock_bh(&mvm_sta->lock); +} + +void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + bool disable) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvm_sta; + int i; + + rcu_read_lock(); + + /* Block/unblock all the stations of the given mvmvif */ + for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) { + sta = rcu_dereference(mvm->fw_id_to_mac_id[i]); + if (IS_ERR_OR_NULL(sta)) + continue; + + mvm_sta = iwl_mvm_sta_from_mac80211(sta); + if (mvm_sta->mac_id_n_color != + FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)) + continue; + + iwl_mvm_mld_sta_modify_disable_tx(mvm, sta, disable); + } + + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1221a0717e61..7f73179e765e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2173,6 +2173,9 @@ bool __iwl_mvm_assign_vif_chanctx_common(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx, bool switching_chanctx, int *ret); +bool __iwl_mvm_unassign_vif_chanctx_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool switching_chanctx); /* Channel info utils */ static inline bool iwl_mvm_has_ultra_hb_channel(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index cad6e879c999..f0d22e9a117b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -545,6 +545,7 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif, bool disable); + void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk); int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -559,8 +560,14 @@ int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, u8 sta_id, u8 tid, unsigned int timeout); /* New MLD STA related APIs */ +/* STA */ int iwl_mvm_mld_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mld_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mld_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mld_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + +/* Queues */ +void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + bool disable); #endif /* __sta_h__ */ -- cgit v1.2.3 From 093e71e26d67c2b0d194b34a7b56432bab049e90 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Tue, 14 Mar 2023 19:49:27 +0200 Subject: wifi: iwlwifi: mvm: remove setting of 'sta' parameter cppcheck reports [drivers/net/wireless/intel/iwlwifi/mvm/rs.c:2686]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it? The setting of the 'sta' parameter is not needed. In the if-check that sets it to NULL, mvm_sta is also set to NULL. Then the next statement checks if mvm_sta is NULL and does an early return. So remove setting sta. Signed-off-by: Tom Rix Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.711fe28cfdd6.I2f723f9d44f65720baaf3e84b72109759350a8f5@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 1f81dff71bc4..7c976b7f1cd7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -2682,7 +2682,6 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, /* if vif isn't initialized mvm doesn't know about * this station, so don't do anything with the it */ - sta = NULL; mvm_sta = NULL; } -- cgit v1.2.3 From e2e76bdcd3fd663bfbddbc09e167e7c1069659cd Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 14 Mar 2023 19:49:28 +0200 Subject: wifi: iwlwifi: fix typos in comment Spelling mistakes (triple letters) in comment. Detected with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.fb2fc470e949.I1f3a347b533bcdb6bcd310f752ab3349800efb49@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/img.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index f878ac508801..f5c4d93d1033 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -182,10 +182,10 @@ struct iwl_dump_exclude { * @enhance_sensitivity_table: device can do enhanced sensitivity. * @init_evtlog_ptr: event log offset for init ucode. * @init_evtlog_size: event log size for init ucode. - * @init_errlog_ptr: error log offfset for init ucode. + * @init_errlog_ptr: error log offset for init ucode. * @inst_evtlog_ptr: event log offset for runtime ucode. * @inst_evtlog_size: event log size for runtime ucode. - * @inst_errlog_ptr: error log offfset for runtime ucode. + * @inst_errlog_ptr: error log offset for runtime ucode. * @type: firmware type (&enum iwl_fw_type) * @human_readable: human readable version * we get the ALIVE from the uCode -- cgit v1.2.3 From d2abe692fe6bea753aaa38c081f322fbed5930e9 Mon Sep 17 00:00:00 2001 From: Solomon Tan Date: Tue, 14 Mar 2023 19:49:29 +0200 Subject: wifi: iwlwifi: Remove prohibited spaces This patch addresses the error from checkpatch.pl regarding the presence of prohibited spaces. Signed-off-by: Solomon Tan Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.557d6841a166.I0957923fe9ea20c1ec9140477033548ccfe93e9a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index aa8e08487b52..923bbfc151dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -81,7 +81,7 @@ static const u16 iwl_nvm_channels[] = { /* 2.4 GHz */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 5 GHz */ - 36, 40, 44 , 48, 52, 56, 60, 64, + 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165 }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 85b99316d029..5790435dc69e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -214,9 +214,9 @@ static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file, int pos; if (!mvm->temperature_test) - pos = scnprintf(buf , sizeof(buf), "disabled\n"); + pos = scnprintf(buf, sizeof(buf), "disabled\n"); else - pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature); + pos = scnprintf(buf, sizeof(buf), "%d\n", mvm->temperature); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } @@ -261,7 +261,7 @@ static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm, mvm->temperature = temperature; } IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n", - mvm->temperature_test ? "En" : "Dis" , + mvm->temperature_test ? "En" : "Dis", mvm->temperature); /* handle the temperature change */ iwl_mvm_tt_handler(mvm); @@ -291,7 +291,7 @@ static ssize_t iwl_dbgfs_nic_temp_read(struct file *file, if (ret) return -EIO; - pos = scnprintf(buf , sizeof(buf), "%d\n", temp); + pos = scnprintf(buf, sizeof(buf), "%d\n", temp); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -- cgit v1.2.3 From 473bc264356297b9df7ee3183b08b954be4a1152 Mon Sep 17 00:00:00 2001 From: Solomon Tan Date: Tue, 14 Mar 2023 19:49:30 +0200 Subject: wifi: iwlwifi: Add required space before open '(' This patch addresses the error from checkpatch.pl that a space is required before an open parenthesis. Signed-off-by: Solomon Tan Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.a21ba1967d94.Iaa52fc6517ea9efb3efc7b1b98f4df2a288de1c3@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 3e9e9f13506b..fe7dfdcd531b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1037,7 +1037,7 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) ret = iwl_read_ppag_table(&mvm->fwrt, &cmd, &cmd_size); /* Not supporting PPAG table is a valid scenario */ - if(ret < 0) + if (ret < 0) return 0; IWL_DEBUG_RADIO(mvm, "Sending PER_PLATFORM_ANT_GAIN_CMD\n"); -- cgit v1.2.3 From f311d0113c7326ec411c9f81dbf77a2cc5ed00ff Mon Sep 17 00:00:00 2001 From: Solomon Tan Date: Tue, 14 Mar 2023 19:49:31 +0200 Subject: wifi: iwlwifi: Replace space with tabs as code indent This patch addresses the checkpatch.pl warning that code indent should use tabs. Signed-off-by: Solomon Tan Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.eaca2a9b08f2.Ic81d60185c13cfc750bf93fbabac57314cb5de13@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 7f73179e765e..7cc3526e78ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1312,7 +1312,7 @@ static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm) { return fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CSUM_SUPPORT) && - !IWL_MVM_HW_CSUM_DISABLE; + !IWL_MVM_HW_CSUM_DISABLE; } static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm) -- cgit v1.2.3 From 71a54f7e74488f234fb56270253bcd08a3357d0e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Mar 2023 19:49:32 +0200 Subject: wifi: iwlwifi: mvm: rs: print BAD_RATE for invalid HT/VHT index If there's a rate->index that maps inside the range but to an uninitialized value, then that's also a bad rate, avoid printing "(nil)" in that case and rather print the "BAD_RATE" string instead as in the else branch. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.06b38d160fc5.I45a9254d3658b1ce796aa4c427193d3cbf638d7e@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 7c976b7f1cd7..bd135b7168cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -512,10 +512,10 @@ static char *rs_pretty_rate(const struct rs_rate *rate) (rate->index <= IWL_RATE_MCS_9_INDEX)) rate_str = ht_vht_rates[rate->index]; else - rate_str = "BAD_RATE"; + rate_str = NULL; sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type), - iwl_rs_pretty_ant(rate->ant), rate_str); + iwl_rs_pretty_ant(rate->ant), rate_str ?: "BAD_RATE"); return buf; } -- cgit v1.2.3 From c2db01752735b522ae150b8698fd4e526efbbc20 Mon Sep 17 00:00:00 2001 From: Mukesh Sisodiya Date: Tue, 14 Mar 2023 19:49:33 +0200 Subject: wifi: iwlwifi: Update configurations for Bnj and Bz devices Add missing rf support for Bz device and B-Step for BnJ device Signed-off-by: Mukesh Sisodiya Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.14c373dcfe15.I96b96a8b149ac181e962f4b82e0d15b172823d0f@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/cfg/22000.c | 36 +++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 3 +++ drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 15 +++++++++++ 3 files changed, 54 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 43fb5cf85f05..bc33d19ca08d 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -57,6 +57,8 @@ #define IWL_BZ_A_MR_A_FW_PRE "iwlwifi-bz-a0-mr-a0-" #define IWL_BZ_A_FM_A_FW_PRE "iwlwifi-bz-a0-fm-a0-" #define IWL_BZ_A_FM4_A_FW_PRE "iwlwifi-bz-a0-fm4-a0-" +#define IWL_BZ_A_FM_B_FW_PRE "iwlwifi-bz-a0-fm-b0-" +#define IWL_BZ_A_FM4_B_FW_PRE "iwlwifi-bz-a0-fm4-b0-" #define IWL_GL_A_FM_A_FW_PRE "iwlwifi-gl-a0-fm-a0-" #define IWL_GL_B_FM_B_FW_PRE "iwlwifi-gl-b0-fm-b0-" #define IWL_BZ_Z_GF_A_FW_PRE "iwlwifi-bz-z0-gf-a0-" @@ -64,6 +66,7 @@ #define IWL_BNJ_A_FM4_A_FW_PRE "iwlwifi-BzBnj-a0-fm4-a0-" #define IWL_BNJ_B_FM4_B_FW_PRE "iwlwifi-BzBnj-b0-fm4-b0-" #define IWL_BNJ_A_GF_A_FW_PRE "iwlwifi-BzBnj-a0-gf-a0-" +#define IWL_BNJ_B_GF_A_FW_PRE "iwlwifi-BzBnj-b0-gf-a0-" #define IWL_BNJ_A_GF4_A_FW_PRE "iwlwifi-BzBnj-a0-gf4-a0-" #define IWL_BNJ_A_HR_B_FW_PRE "iwlwifi-BzBnj-a0-hr-b0-" #define IWL_BNJ_B_FM_B_FW_PRE "iwlwifi-BzBnj-b0-fm-b0-" @@ -125,6 +128,10 @@ IWL_BZ_A_FM_A_FW_PRE __stringify(api) ".ucode" #define IWL_BZ_A_FM4_A_MODULE_FIRMWARE(api) \ IWL_BZ_A_FM4_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BZ_A_FM_B_MODULE_FIRMWARE(api) \ + IWL_BZ_A_FM_B_FW_PRE __stringify(api) ".ucode" +#define IWL_BZ_A_FM4_B_MODULE_FIRMWARE(api) \ + IWL_BZ_A_FM4_B_FW_PRE __stringify(api) ".ucode" #define IWL_GL_A_FM_A_MODULE_FIRMWARE(api) \ IWL_GL_A_FM_A_FW_PRE __stringify(api) ".ucode" #define IWL_GL_B_FM_B_MODULE_FIRMWARE(api) \ @@ -137,6 +144,8 @@ IWL_BNJ_B_FM4_B_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_GF_A_MODULE_FIRMWARE(api) \ IWL_BNJ_A_GF_A_FW_PRE __stringify(api) ".ucode" +#define IWL_BNJ_B_GF_A_MODULE_FIRMWARE(api) \ + IWL_BNJ_B_GF_A_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_GF4_A_MODULE_FIRMWARE(api) \ IWL_BNJ_A_GF4_A_FW_PRE __stringify(api) ".ucode" #define IWL_BNJ_A_HR_B_MODULE_FIRMWARE(api) \ @@ -961,6 +970,22 @@ const struct iwl_cfg iwl_cfg_bz_a0_fm4_a0 = { .num_rbds = IWL_NUM_RBDS_AX210_HE, }; +const struct iwl_cfg iwl_cfg_bz_a0_fm_b0 = { + .fw_name_pre = IWL_BZ_A_FM_B_FW_PRE, + .uhb_supported = true, + IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, + .num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + +const struct iwl_cfg iwl_cfg_bz_a0_fm4_b0 = { + .fw_name_pre = IWL_BZ_A_FM4_B_FW_PRE, + .uhb_supported = true, + IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS_BZ | NETIF_F_RXCSUM, + .num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + const struct iwl_cfg iwl_cfg_gl_a0_fm_a0 = { .fw_name_pre = IWL_GL_A_FM_A_FW_PRE, .uhb_supported = true, @@ -1017,6 +1042,14 @@ const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0 = { .num_rbds = IWL_NUM_RBDS_AX210_HE, }; +const struct iwl_cfg iwl_cfg_bnj_b0_gf_a0 = { + .fw_name_pre = IWL_BNJ_B_GF_A_FW_PRE, + .uhb_supported = true, + IWL_DEVICE_BZ, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .num_rbds = IWL_NUM_RBDS_AX210_HE, +}; + const struct iwl_cfg iwl_cfg_bnj_a0_gf4_a0 = { .fw_name_pre = IWL_BNJ_A_GF4_A_FW_PRE, .uhb_supported = true, @@ -1067,13 +1100,16 @@ MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BZ_A_GF4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BZ_A_MR_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BZ_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BZ_A_FM_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_GL_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_FM_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_FM4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_B_FM4_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_GF_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BNJ_B_GF_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_GF4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BZ_A_FM4_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_BZ_A_FM4_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_GL_B_FM_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BNJ_B_FM_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index eaa0ff2736c5..a4c7fd929cc2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -650,12 +650,15 @@ extern const struct iwl_cfg iwl_cfg_bz_a0_gf4_a0; extern const struct iwl_cfg iwl_cfg_bz_a0_mr_a0; extern const struct iwl_cfg iwl_cfg_bz_a0_fm_a0; extern const struct iwl_cfg iwl_cfg_bz_a0_fm4_a0; +extern const struct iwl_cfg iwl_cfg_bz_a0_fm_b0; +extern const struct iwl_cfg iwl_cfg_bz_a0_fm4_b0; extern const struct iwl_cfg iwl_cfg_gl_a0_fm_a0; extern const struct iwl_cfg iwl_cfg_gl_b0_fm_b0; extern const struct iwl_cfg iwl_cfg_bz_z0_gf_a0; extern const struct iwl_cfg iwl_cfg_bnj_a0_fm_a0; extern const struct iwl_cfg iwl_cfg_bnj_a0_fm4_a0; extern const struct iwl_cfg iwl_cfg_bnj_a0_gf_a0; +extern const struct iwl_cfg iwl_cfg_bnj_b0_gf_a0; extern const struct iwl_cfg iwl_cfg_bnj_a0_gf4_a0; extern const struct iwl_cfg iwl_cfg_bnj_a0_hr_b0; extern const struct iwl_cfg iwl_cfg_bnj_b0_fm_b0; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 8aa8a678475c..077983913acd 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1159,6 +1159,16 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_NO_JACKET, iwl_cfg_bz_a0_fm4_a0, iwl_bz_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET, + iwl_cfg_bz_a0_fm_b0, iwl_bz_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, + IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_CDB, IWL_CFG_IS_JACKET, + iwl_cfg_bz_a0_fm4_b0, iwl_bz_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, SILICON_A_STEP, IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, @@ -1203,6 +1213,11 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET, iwl_cfg_bnj_a0_gf_a0, iwl_bz_name), + _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, + IWL_CFG_MAC_TYPE_GL, SILICON_B_STEP, + IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, + IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, IWL_CFG_IS_JACKET, + iwl_cfg_bnj_b0_gf_a0, iwl_bz_name), _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, -- cgit v1.2.3 From 8f55564c05123b790f25562fa02ca2e1dbf9c4f5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 Mar 2023 19:49:34 +0200 Subject: wifi: iwlwifi: fw: pnvm: fix uefi reduced TX power loading There are a number of issues here: * if trans->reduce_power_loaded is already true, we call iwl_trans_set_reduce_power() with an uninitialized len value * in this case we also clobber a previous load/setting * if iwl_uefi_get_reduced_power() returns an ERR_PTR() we try to kfree() it Move the iwl_trans_set_reduce_power() call into the success case only to fix these issues. Signed-off-by: Johannes Berg Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230314194113.aa2cf2281f5d.I33b4ab3427f1921c184c52fecd0f46781a89dc8a@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index b6d3ac6ed440..c6f2672fdc73 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2020-2021 Intel Corporation + * Copyright(c) 2020-2022 Intel Corporation */ #include "iwl-drv.h" @@ -318,7 +318,6 @@ parse: kfree(data); skip_parse: - data = NULL; /* now try to get the reduce power table, if not loaded yet */ if (!trans->reduce_power_loaded) { data = iwl_uefi_get_reduced_power(trans, &len); @@ -329,19 +328,16 @@ skip_parse: * trying again over and over. */ trans->reduce_power_loaded = true; - - goto skip_reduce_power; + } else { + ret = iwl_trans_set_reduce_power(trans, data, len); + if (ret) + IWL_DEBUG_FW(trans, + "Failed to set reduce power table %d\n", + ret); + kfree(data); } } - ret = iwl_trans_set_reduce_power(trans, data, len); - if (ret) - IWL_DEBUG_FW(trans, - "Failed to set reduce power table %d\n", - ret); - kfree(data); - -skip_reduce_power: iwl_init_notification_wait(notif_wait, &pnvm_wait, ntf_cmds, ARRAY_SIZE(ntf_cmds), iwl_pnvm_complete_fn, trans); -- cgit v1.2.3 From 4eca8cbf7ba83c3291b5841905ce64584036b1ff Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Mar 2023 13:29:20 +0100 Subject: wifi: iwlwifi: suppress printf warnings in tracing This can't really be fixed due to the macro layout of tracepoints (you'd need a special tracepoint macro for when this is needed), so just suppress the warnings. Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c index 999b7c652289..c190ec5effa1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c @@ -12,6 +12,7 @@ #include "iwl-trans.h" #define CREATE_TRACE_POINTS +#pragma GCC diagnostic ignored "-Wsuggest-attribute=format" #include "iwl-devtrace.h" EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); -- cgit v1.2.3 From 543c143dac5d64a8de8db3ccadea6d92c34fd1e6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 14:26:37 +0100 Subject: ptp: ines: drop of_match_ptr for ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can match only via the DT table so the table should be always used and the of_match_ptr does not have any sense (this also allows ACPI matching via PRP0001, even though it might not be relevant here). This also fixes !CONFIG_OF error: drivers/ptp/ptp_ines.c:783:34: error: ‘ines_ptp_ctrl_of_match’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Simon Horman Acked-by: Richard Cochran Link: https://lore.kernel.org/r/20230312132637.352755-1-krzysztof.kozlowski@linaro.org Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_ines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ptp/ptp_ines.c b/drivers/ptp/ptp_ines.c index 61f47fb9d997..ed215b458183 100644 --- a/drivers/ptp/ptp_ines.c +++ b/drivers/ptp/ptp_ines.c @@ -792,7 +792,7 @@ static struct platform_driver ines_ptp_ctrl_driver = { .remove = ines_ptp_ctrl_remove, .driver = { .name = "ines_ptp_ctrl", - .of_match_table = of_match_ptr(ines_ptp_ctrl_of_match), + .of_match_table = ines_ptp_ctrl_of_match, }, }; module_platform_driver(ines_ptp_ctrl_driver); -- cgit v1.2.3 From 83456576a42050cc0675aece14277a3cbdee9cbe Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 14 Mar 2023 13:48:56 +0100 Subject: net: phy: update obsolete comment about PHY_STARTING Commit 899a3cbbf77a ("net: phy: remove states PHY_STARTING and PHY_PENDING") missed to update a comment in phy_probe. Remove superfluous "Description:" prefix while we are here. Signed-off-by: Wolfram Sang Reviewed-by: Heiner Kallweit Link: https://lore.kernel.org/r/20230314124856.44878-1-wsa+renesas@sang-engineering.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phy_device.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 1785f1cead97..c0760cbf534b 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3076,9 +3076,7 @@ EXPORT_SYMBOL_GPL(fwnode_get_phy_node); * phy_probe - probe and init a PHY device * @dev: device to probe and init * - * Description: Take care of setting up the phy_device structure, - * set the state to READY (the driver's init function should - * set it to STARTING if needed). + * Take care of setting up the phy_device structure, set the state to READY. */ static int phy_probe(struct device *dev) { -- cgit v1.2.3 From a57cc54d69d601282c4451010b12f53630834203 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 14 Mar 2023 13:49:27 +0100 Subject: net: phy: micrel: drop superfluous use of temp variable 'temp' was used before commit c0c99d0cd107 ("net: phy: micrel: remove the use of .ack_interrupt()") refactored the code. Now, we can simplify it a little. Signed-off-by: Wolfram Sang Reviewed-by: Andrew Lunn Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230314124928.44948-1-wsa+renesas@sang-engineering.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/micrel.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index e5b2af69ac03..0cf0ef3a96a3 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -436,11 +436,9 @@ static int kszphy_config_intr(struct phy_device *phydev) if (err) return err; - temp = KSZPHY_INTCS_ALL; - err = phy_write(phydev, MII_KSZPHY_INTCS, temp); + err = phy_write(phydev, MII_KSZPHY_INTCS, KSZPHY_INTCS_ALL); } else { - temp = 0; - err = phy_write(phydev, MII_KSZPHY_INTCS, temp); + err = phy_write(phydev, MII_KSZPHY_INTCS, 0); if (err) return err; -- cgit v1.2.3 From c05d145abea138dbba213ce775820af73e893d92 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Mon, 13 Mar 2023 22:42:20 -0700 Subject: net/mlx5: remove redundant clear_bit When shutdown or remove callbacks are called the driver sets the flag MLX5_BREAK_FW_WAIT, to stop waiting for FW as teardown was called. There is no need to clear the bit as once shutdown or remove were called as there is no way back, the driver is going down. Furthermore, if not cleared the flag can be used also in other loops where we may wait while teardown was already called. Use test_bit() instead of test_and_clear_bit() as there is no need to clear the flag. Signed-off-by: Moshe Shemesh Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-2-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 540840e80493..0ff0eb660495 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -191,7 +191,7 @@ static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili, if (!(fw_initializing >> 31)) break; if (time_after(jiffies, end) || - test_and_clear_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state)) { + test_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state)) { err = -EBUSY; break; } -- cgit v1.2.3 From 8ff38e730c3f5ee717f25365ef8aa4739562d567 Mon Sep 17 00:00:00 2001 From: Moshe Shemesh Date: Mon, 13 Mar 2023 22:42:21 -0700 Subject: net/mlx5: Stop waiting for PCI up if teardown was triggered If driver teardown is called while PCI is turned off, there is a race between health recovery and teardown. If health recovery already started it will wait 60 sec trying to see if PCI gets back and it can recover, but actually there is no need to wait anymore once teardown was called. Use the MLX5_BREAK_FW_WAIT flag which is set on driver teardown to break waiting for PCI up. Signed-off-by: Moshe Shemesh Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-3-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/health.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index f9438d4e43ca..016c5f99c470 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -325,6 +325,10 @@ int mlx5_health_wait_pci_up(struct mlx5_core_dev *dev) while (sensor_pci_not_working(dev)) { if (time_after(jiffies, end)) return -ETIMEDOUT; + if (test_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state)) { + mlx5_core_warn(dev, "device is being removed, stop waiting for PCI\n"); + return -ENODEV; + } msleep(100); } return 0; -- cgit v1.2.3 From ceefcfb8a37510f2c2b99b9e8d6c0b3941687b2b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 13 Mar 2023 22:42:22 -0700 Subject: net/mlx5: Add comment to mlx5_devlink_params_register() Add comment to mlx5_devlink_params_register() functions so it is clear that only driver init params should be registered here. Signed-off-by: Jiri Pirko Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-4-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/devlink.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index c5d2fdcabd56..b7784e02c2dd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -805,6 +805,11 @@ int mlx5_devlink_params_register(struct devlink *devlink) { int err; + /* Here only the driver init params should be registered. + * Runtime params should be registered by the code which + * behaviour they configure. + */ + err = devl_params_register(devlink, mlx5_devlink_params, ARRAY_SIZE(mlx5_devlink_params)); if (err) -- cgit v1.2.3 From c1fef618d611b31964ab397aa0bf0611da94bade Mon Sep 17 00:00:00 2001 From: Sandipan Patra Date: Mon, 13 Mar 2023 22:42:23 -0700 Subject: net/mlx5: Implement thermal zone Implement thermal zone support for mlx5 based HW. The NIC uses temperature sensor provided by ASIC to report current temperature to thermal core. Signed-off-by: Sandipan Patra Reviewed-by: Gal Pressman Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-5-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 1 + drivers/net/ethernet/mellanox/mlx5/core/main.c | 6 ++ drivers/net/ethernet/mellanox/mlx5/core/thermal.c | 108 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/thermal.h | 20 ++++ include/linux/mlx5/driver.h | 3 + include/linux/mlx5/mlx5_ifc.h | 26 ++++++ 6 files changed, 164 insertions(+) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/thermal.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/thermal.h (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 8d4e25cc54ea..6c2f1d4a58ab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -77,6 +77,7 @@ mlx5_core-$(CONFIG_MLX5_ESWITCH) += esw/acl/helper.o \ mlx5_core-$(CONFIG_MLX5_BRIDGE) += esw/bridge.o en/rep/bridge.o +mlx5_core-$(CONFIG_THERMAL) += thermal.o mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o mlx5_core-$(CONFIG_VXLAN) += lib/vxlan.o mlx5_core-$(CONFIG_PTP_1588_CLOCK) += lib/clock.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 0ff0eb660495..644c889f9a32 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -52,6 +52,7 @@ #include #include #include "mlx5_core.h" +#include "thermal.h" #include "lib/eq.h" #include "fs_core.h" #include "lib/mpfs.h" @@ -1768,6 +1769,10 @@ static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (err) dev_err(&pdev->dev, "mlx5_crdump_enable failed with error code %d\n", err); + err = mlx5_thermal_init(dev); + if (err) + dev_err(&pdev->dev, "mlx5_thermal_init failed with error code %d\n", err); + pci_save_state(pdev); devlink_register(devlink); return 0; @@ -1796,6 +1801,7 @@ static void remove_one(struct pci_dev *pdev) set_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state); devlink_unregister(devlink); mlx5_sriov_disable(pdev); + mlx5_thermal_uninit(dev); mlx5_crdump_disable(dev); mlx5_drain_health_wq(dev); mlx5_uninit_one(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/thermal.c b/drivers/net/ethernet/mellanox/mlx5/core/thermal.c new file mode 100644 index 000000000000..e47fa6fb836f --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/thermal.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +// Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. + +#include +#include +#include +#include +#include +#include +#include "mlx5_core.h" +#include "thermal.h" + +#define MLX5_THERMAL_POLL_INT_MSEC 1000 +#define MLX5_THERMAL_NUM_TRIPS 0 +#define MLX5_THERMAL_ASIC_SENSOR_INDEX 0 + +/* Bit string indicating the writeablility of trip points if any */ +#define MLX5_THERMAL_TRIP_MASK (BIT(MLX5_THERMAL_NUM_TRIPS) - 1) + +struct mlx5_thermal { + struct mlx5_core_dev *mdev; + struct thermal_zone_device *tzdev; +}; + +static int mlx5_thermal_get_mtmp_temp(struct mlx5_core_dev *mdev, u32 id, int *p_temp) +{ + u32 mtmp_out[MLX5_ST_SZ_DW(mtmp_reg)] = {}; + u32 mtmp_in[MLX5_ST_SZ_DW(mtmp_reg)] = {}; + int err; + + MLX5_SET(mtmp_reg, mtmp_in, sensor_index, id); + + err = mlx5_core_access_reg(mdev, mtmp_in, sizeof(mtmp_in), + mtmp_out, sizeof(mtmp_out), + MLX5_REG_MTMP, 0, 0); + + if (err) + return err; + + *p_temp = MLX5_GET(mtmp_reg, mtmp_out, temperature); + + return 0; +} + +static int mlx5_thermal_get_temp(struct thermal_zone_device *tzdev, + int *p_temp) +{ + struct mlx5_thermal *thermal = tzdev->devdata; + struct mlx5_core_dev *mdev = thermal->mdev; + int err; + + err = mlx5_thermal_get_mtmp_temp(mdev, MLX5_THERMAL_ASIC_SENSOR_INDEX, p_temp); + + if (err) + return err; + + /* The unit of temp returned is in 0.125 C. The thermal + * framework expects the value in 0.001 C. + */ + *p_temp *= 125; + + return 0; +} + +static struct thermal_zone_device_ops mlx5_thermal_ops = { + .get_temp = mlx5_thermal_get_temp, +}; + +int mlx5_thermal_init(struct mlx5_core_dev *mdev) +{ + struct mlx5_thermal *thermal; + struct thermal_zone_device *tzd; + const char *data = "mlx5"; + + tzd = thermal_zone_get_zone_by_name(data); + if (!IS_ERR(tzd)) + return 0; + + thermal = kzalloc(sizeof(*thermal), GFP_KERNEL); + if (!thermal) + return -ENOMEM; + + thermal->mdev = mdev; + thermal->tzdev = thermal_zone_device_register(data, + MLX5_THERMAL_NUM_TRIPS, + MLX5_THERMAL_TRIP_MASK, + thermal, + &mlx5_thermal_ops, + NULL, 0, MLX5_THERMAL_POLL_INT_MSEC); + if (IS_ERR(thermal->tzdev)) { + dev_err(mdev->device, "Failed to register thermal zone device (%s) %ld\n", + data, PTR_ERR(thermal->tzdev)); + kfree(thermal); + return -EINVAL; + } + + mdev->thermal = thermal; + return 0; +} + +void mlx5_thermal_uninit(struct mlx5_core_dev *mdev) +{ + if (!mdev->thermal) + return; + + thermal_zone_device_unregister(mdev->thermal->tzdev); + kfree(mdev->thermal); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/thermal.h b/drivers/net/ethernet/mellanox/mlx5/core/thermal.h new file mode 100644 index 000000000000..7d752c122192 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/thermal.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB + * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. + */ +#ifndef __MLX5_THERMAL_DRIVER_H +#define __MLX5_THERMAL_DRIVER_H + +#if IS_ENABLED(CONFIG_THERMAL) +int mlx5_thermal_init(struct mlx5_core_dev *mdev); +void mlx5_thermal_uninit(struct mlx5_core_dev *mdev); +#else +static inline int mlx5_thermal_init(struct mlx5_core_dev *mdev) +{ + mdev->thermal = NULL; + return 0; +} + +static inline void mlx5_thermal_uninit(struct mlx5_core_dev *mdev) { } +#endif + +#endif /* __MLX5_THERMAL_DRIVER_H */ diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index f33389b42209..7a898113b6b7 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -134,6 +134,7 @@ enum { MLX5_REG_PCAM = 0x507f, MLX5_REG_NODE_DESC = 0x6001, MLX5_REG_HOST_ENDIANNESS = 0x7004, + MLX5_REG_MTMP = 0x900A, MLX5_REG_MCIA = 0x9014, MLX5_REG_MFRL = 0x9028, MLX5_REG_MLCR = 0x902b, @@ -731,6 +732,7 @@ struct mlx5_fw_tracer; struct mlx5_vxlan; struct mlx5_geneve; struct mlx5_hv_vhca; +struct mlx5_thermal; #define MLX5_LOG_SW_ICM_BLOCK_SIZE(dev) (MLX5_CAP_DEV_MEM(dev, log_sw_icm_alloc_granularity)) #define MLX5_SW_ICM_BLOCK_SIZE(dev) (1 << MLX5_LOG_SW_ICM_BLOCK_SIZE(dev)) @@ -808,6 +810,7 @@ struct mlx5_core_dev { struct mlx5_rsc_dump *rsc_dump; u32 vsc_addr; struct mlx5_hv_vhca *hv_vhca; + struct mlx5_thermal *thermal; }; struct mlx5_db { diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 66d76e97a087..d2c164f0778c 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -10869,6 +10869,31 @@ struct mlx5_ifc_mrtc_reg_bits { u8 time_l[0x20]; }; +struct mlx5_ifc_mtmp_reg_bits { + u8 reserved_at_0[0x14]; + u8 sensor_index[0xc]; + + u8 reserved_at_20[0x10]; + u8 temperature[0x10]; + + u8 mte[0x1]; + u8 mtr[0x1]; + u8 reserved_at_42[0xe]; + u8 max_temperature[0x10]; + + u8 tee[0x2]; + u8 reserved_at_62[0xe]; + u8 temp_threshold_hi[0x10]; + + u8 reserved_at_80[0x10]; + u8 temp_threshold_lo[0x10]; + + u8 reserved_at_a0[0x20]; + + u8 sensor_name_hi[0x20]; + u8 sensor_name_lo[0x20]; +}; + union mlx5_ifc_ports_control_registers_document_bits { struct mlx5_ifc_bufferx_reg_bits bufferx_reg; struct mlx5_ifc_eth_2819_cntrs_grp_data_layout_bits eth_2819_cntrs_grp_data_layout; @@ -10931,6 +10956,7 @@ union mlx5_ifc_ports_control_registers_document_bits { struct mlx5_ifc_mfrl_reg_bits mfrl_reg; struct mlx5_ifc_mtutc_reg_bits mtutc_reg; struct mlx5_ifc_mrtc_reg_bits mrtc_reg; + struct mlx5_ifc_mtmp_reg_bits mtmp_reg; u8 reserved_at_0[0x60e0]; }; -- cgit v1.2.3 From aa98d15ea40b9d370871df7882b6d56d08aed692 Mon Sep 17 00:00:00 2001 From: Rahul Rameshbabu Date: Mon, 13 Mar 2023 22:42:24 -0700 Subject: net/mlx5e: Utilize the entire fifo Previous check was comparing against the fifo mask. The mask is size of the fifo (power of two) minus one, so a less than or equal comparator should be used for checking if the fifo has room for the SKB. Signed-off-by: Rahul Rameshbabu Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-6-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h index b9c2f67d3794..816ea83e6413 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h @@ -86,7 +86,7 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq); static inline bool mlx5e_skb_fifo_has_room(struct mlx5e_skb_fifo *fifo) { - return (u16)(*fifo->pc - *fifo->cc) < fifo->mask; + return (u16)(*fifo->pc - *fifo->cc) <= fifo->mask; } static inline bool -- cgit v1.2.3 From 2b5bd5b1611b817970f13b1c4dfdcda0b35e3690 Mon Sep 17 00:00:00 2001 From: Adham Faris Date: Mon, 13 Mar 2023 22:42:25 -0700 Subject: net/mlx5e: Rename RQ/SQ adaptive moderation state flag Dynamic interrupt moderation RQ and SQ feature represented by MLX5E_RQ_STATE_AM and MLX5E_SQ_STATE_AM enums respectively, is not consistent with the feature naming in the driver, and with the formal feature and library names. Hence, change MLX5E_RQ_STATE_AM and MLX5E_SQ_STATE_AM enum type names in core/en.h to MLX5E_RQ_STATE_DIM and MLX5E_SQ_STATE_DIM respectively. Signed-off-by: Adham Faris Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-7-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 4 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 88460b7796e5..66bca3a6a057 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -339,7 +339,7 @@ static inline u8 mlx5e_get_dcb_num_tc(struct mlx5e_params *params) enum { MLX5E_RQ_STATE_ENABLED, MLX5E_RQ_STATE_RECOVERING, - MLX5E_RQ_STATE_AM, + MLX5E_RQ_STATE_DIM, MLX5E_RQ_STATE_NO_CSUM_COMPLETE, MLX5E_RQ_STATE_CSUM_FULL, /* cqe_csum_full hw bit is set */ MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX, /* set when mini_cqe_resp_stride_index cap is used */ @@ -390,7 +390,7 @@ enum { MLX5E_SQ_STATE_MPWQE, MLX5E_SQ_STATE_RECOVERING, MLX5E_SQ_STATE_IPSEC, - MLX5E_SQ_STATE_AM, + MLX5E_SQ_STATE_DIM, MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE, MLX5E_SQ_STATE_PENDING_XSK_TX, MLX5E_SQ_STATE_PENDING_TLS_RX_RESYNC, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 76a9c5194a70..5ca9fcf84586 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1188,7 +1188,7 @@ int mlx5e_open_rq(struct mlx5e_params *params, struct mlx5e_rq_param *param, __set_bit(MLX5E_RQ_STATE_CSUM_FULL, &rq->state); if (params->rx_dim_enabled) - __set_bit(MLX5E_RQ_STATE_AM, &rq->state); + __set_bit(MLX5E_RQ_STATE_DIM, &rq->state); /* We disable csum_complete when XDP is enabled since * XDP programs might manipulate packets which will render @@ -1664,7 +1664,7 @@ int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix, mlx5e_set_sq_maxrate(c->netdev, sq, tx_rate); if (params->tx_dim_enabled) - sq->state |= BIT(MLX5E_SQ_STATE_AM); + sq->state |= BIT(MLX5E_SQ_STATE_DIM); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 9a458a5d9853..a50bfda18e96 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -51,7 +51,7 @@ static void mlx5e_handle_tx_dim(struct mlx5e_txqsq *sq) struct mlx5e_sq_stats *stats = sq->stats; struct dim_sample dim_sample = {}; - if (unlikely(!test_bit(MLX5E_SQ_STATE_AM, &sq->state))) + if (unlikely(!test_bit(MLX5E_SQ_STATE_DIM, &sq->state))) return; dim_update_sample(sq->cq.event_ctr, stats->packets, stats->bytes, &dim_sample); @@ -63,7 +63,7 @@ static void mlx5e_handle_rx_dim(struct mlx5e_rq *rq) struct mlx5e_rq_stats *stats = rq->stats; struct dim_sample dim_sample = {}; - if (unlikely(!test_bit(MLX5E_RQ_STATE_AM, &rq->state))) + if (unlikely(!test_bit(MLX5E_RQ_STATE_DIM, &rq->state))) return; dim_update_sample(rq->cq.event_ctr, stats->packets, stats->bytes, &dim_sample); -- cgit v1.2.3 From 1fe7bc109e3e18f7776ba71953cbc922b7ee9165 Mon Sep 17 00:00:00 2001 From: Adham Faris Date: Mon, 13 Mar 2023 22:42:26 -0700 Subject: net/mlx5e: Stringify RQ SW state in RQ devlink health diagnostics One of the parameters that is retrieved/printed as a response to devlink health diagnostics for rx reporter is the RQ SW state. It's printed as a bitmap decimal number. Printing it as bitmap is problematic and non informative. In addition User can't count on SW state without accessing the kernel sources (mlx5e rq state enum in en.h). This patch prints RQ SW state in a textual representation, as a key: value pairs, where disabled rq states will appear as '0' and enabled ones will appear as '1'. See below the generated output for rx health diagnostics devlink command: $ devlink health diagnose auxiliary/mlx5_core.eth.0/65535 reporter rx Before: ======================================================================= Common config: RQ: type: 2 stride size: 2048 size: 8 ts_format: FRC CQ: stride size: 64 size: 1024 RQs: channel ix: 0 rqn: 4172 HW state: 1 SW state: 37 WQE counter: 7 posted WQEs: 7 cc: 7 CQ: cqn: 1033 HW status: 0 ci: 0 size: 1024 EQ: eqn: 7 irqn: 32 vecidx: 0 ci: 2 size: 2048 ICOSQ: sqn: 4169 HW state: 1 cc: 74 pc: 74 WQE size: 128 CQ: cqn: 1030 cc: 1 size: 128 channel ix: 1 ... . . After: ======================================================================= Common config: RQ: type: 2 stride size: 2048 size: 8 ts_format: FRC CQ: stride size: 64 size: 1024 RQs: channel ix: 0 rqn: 4172 HW state: 1 WQE counter: 7 posted WQEs: 7 cc: 7 SW State: enabled: 1 recovering: 0 am: 1 no_csum_complete: 0 csum_full: 0 mini_cqe_hw_stridx: 1 shampo: 0 mini_cqe_enhanced: 0 CQ: cqn: 1033 HW status: 0 ci: 0 size: 1024 EQ: eqn: 7 irqn: 32 vecidx: 0 ci: 2 size: 2048 ICOSQ: sqn: 4169 HW state: 1 cc: 74 pc: 74 WQE size: 128 CQ: cqn: 1030 cc: 1 size: 128 channel: ix: 1 ... . . Signed-off-by: Adham Faris Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-8-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 6 ++- .../ethernet/mellanox/mlx5/core/en/reporter_rx.c | 49 ++++++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 66bca3a6a057..6c01da3bad74 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -336,8 +336,11 @@ static inline u8 mlx5e_get_dcb_num_tc(struct mlx5e_params *params) params->mqprio.num_tc : 1; } +/* Keep this enum consistent with the corresponding strings array + * declared in en/reporter_rx.c + */ enum { - MLX5E_RQ_STATE_ENABLED, + MLX5E_RQ_STATE_ENABLED = 0, MLX5E_RQ_STATE_RECOVERING, MLX5E_RQ_STATE_DIM, MLX5E_RQ_STATE_NO_CSUM_COMPLETE, @@ -345,6 +348,7 @@ enum { MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX, /* set when mini_cqe_resp_stride_index cap is used */ MLX5E_RQ_STATE_SHAMPO, /* set when SHAMPO cap is used */ MLX5E_RQ_STATE_MINI_CQE_ENHANCED, /* set when enhanced mini_cqe_cap is used */ + MLX5E_NUM_RQ_STATES, /* Must be kept last */ }; struct mlx5e_cq { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c index c462fe76495b..98c87b3df806 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c @@ -8,6 +8,18 @@ #include "ptp.h" #include "lib/tout.h" +/* Keep this string array consistent with the MLX5E_RQ_STATE_* enums in en.h */ +static const char * const rq_sw_state_type_name[] = { + [MLX5E_RQ_STATE_ENABLED] = "enabled", + [MLX5E_RQ_STATE_RECOVERING] = "recovering", + [MLX5E_RQ_STATE_DIM] = "dim", + [MLX5E_RQ_STATE_NO_CSUM_COMPLETE] = "no_csum_complete", + [MLX5E_RQ_STATE_CSUM_FULL] = "csum_full", + [MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX] = "mini_cqe_hw_stridx", + [MLX5E_RQ_STATE_SHAMPO] = "shampo", + [MLX5E_RQ_STATE_MINI_CQE_ENHANCED] = "mini_cqe_enhanced", +}; + static int mlx5e_query_rq_state(struct mlx5_core_dev *dev, u32 rqn, u8 *state) { int outlen = MLX5_ST_SZ_BYTES(query_rq_out); @@ -239,6 +251,35 @@ static int mlx5e_reporter_icosq_diagnose(struct mlx5e_icosq *icosq, u8 hw_state, return mlx5e_health_fmsg_named_obj_nest_end(fmsg); } +static int mlx5e_health_rq_put_sw_state(struct devlink_fmsg *fmsg, struct mlx5e_rq *rq) +{ + int err; + int i; + + BUILD_BUG_ON_MSG(ARRAY_SIZE(rq_sw_state_type_name) != MLX5E_NUM_RQ_STATES, + "rq_sw_state_type_name string array must be consistent with MLX5E_RQ_STATE_* enum in en.h"); + err = devlink_fmsg_obj_nest_start(fmsg); + if (err) + return err; + + err = mlx5e_health_fmsg_named_obj_nest_start(fmsg, "SW State"); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(rq_sw_state_type_name); ++i) { + err = devlink_fmsg_u32_pair_put(fmsg, rq_sw_state_type_name[i], + test_bit(i, &rq->state)); + if (err) + return err; + } + + err = mlx5e_health_fmsg_named_obj_nest_end(fmsg); + if (err) + return err; + + return devlink_fmsg_obj_nest_end(fmsg); +} + static int mlx5e_rx_reporter_build_diagnose_output_rq_common(struct mlx5e_rq *rq, struct devlink_fmsg *fmsg) @@ -265,10 +306,6 @@ mlx5e_rx_reporter_build_diagnose_output_rq_common(struct mlx5e_rq *rq, if (err) return err; - err = devlink_fmsg_u8_pair_put(fmsg, "SW state", rq->state); - if (err) - return err; - err = devlink_fmsg_u32_pair_put(fmsg, "WQE counter", wqe_counter); if (err) return err; @@ -281,6 +318,10 @@ mlx5e_rx_reporter_build_diagnose_output_rq_common(struct mlx5e_rq *rq, if (err) return err; + err = mlx5e_health_rq_put_sw_state(fmsg, rq); + if (err) + return err; + err = mlx5e_health_cq_diag_fmsg(&rq->cq, fmsg); if (err) return err; -- cgit v1.2.3 From fc9d982a2512b294607b2922de3809e7c92810da Mon Sep 17 00:00:00 2001 From: Adham Faris Date: Mon, 13 Mar 2023 22:42:27 -0700 Subject: net/mlx5e: Expose SQ SW state as part of SQ health diagnostics Add SQ SW state textual representation to devlink health diagnostics for tx reporter. SQ SW state can be retrieved by issuing the devlink command below: $ devlink health diagnose auxiliary/mlx5_core.eth.0/65535 reporter tx Output ======================================================================= Common Config: SQ: stride size: 64 size: 1024 ts_format: FRC CQ: stride size: 64 size: 1024 SQs: channel ix: 0 tc: 0 txq ix: 0 sqn: 4170 HW state: 1 stopped: false cc: 0 pc: 0 SW State: enabled: 1 mpwqe: 1 recovering: 0 ipsec: 0 am: 1 vlan_need_l2_inline: 1 pending_xsk_tx: 0 pending_tls_rx_resync: 0 xdp_multibuf: 0 CQ: cqn: 1031 HW status: 0 ci: 0 size: 1024 EQ: eqn: 7 irqn: 32 vecidx: 0 ci: 2 size: 2048 Signed-off-by: Adham Faris Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-9-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 6 ++- .../ethernet/mellanox/mlx5/core/en/reporter_tx.c | 46 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 6c01da3bad74..67f7e24d1f36 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -389,8 +389,11 @@ struct mlx5e_sq_dma { enum mlx5e_dma_map_type type; }; +/* Keep this enum consistent with with the corresponding strings array + * declared in en/reporter_tx.c + */ enum { - MLX5E_SQ_STATE_ENABLED, + MLX5E_SQ_STATE_ENABLED = 0, MLX5E_SQ_STATE_MPWQE, MLX5E_SQ_STATE_RECOVERING, MLX5E_SQ_STATE_IPSEC, @@ -399,6 +402,7 @@ enum { MLX5E_SQ_STATE_PENDING_XSK_TX, MLX5E_SQ_STATE_PENDING_TLS_RX_RESYNC, MLX5E_SQ_STATE_XDP_MULTIBUF, + MLX5E_NUM_SQ_STATES, /* Must be kept last */ }; struct mlx5e_tx_mpwqe { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c index 34666e2b3871..44c1926843a1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c @@ -6,6 +6,19 @@ #include "en/devlink.h" #include "lib/tout.h" +/* Keep this string array consistent with the MLX5E_SQ_STATE_* enums in en.h */ +static const char * const sq_sw_state_type_name[] = { + [MLX5E_SQ_STATE_ENABLED] = "enabled", + [MLX5E_SQ_STATE_MPWQE] = "mpwqe", + [MLX5E_SQ_STATE_RECOVERING] = "recovering", + [MLX5E_SQ_STATE_IPSEC] = "ipsec", + [MLX5E_SQ_STATE_DIM] = "dim", + [MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE] = "vlan_need_l2_inline", + [MLX5E_SQ_STATE_PENDING_XSK_TX] = "pending_xsk_tx", + [MLX5E_SQ_STATE_PENDING_TLS_RX_RESYNC] = "pending_tls_rx_resync", + [MLX5E_SQ_STATE_XDP_MULTIBUF] = "xdp_multibuf", +}; + static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq) { struct mlx5_core_dev *dev = sq->mdev; @@ -37,6 +50,35 @@ static void mlx5e_reset_txqsq_cc_pc(struct mlx5e_txqsq *sq) sq->pc = 0; } +static int mlx5e_health_sq_put_sw_state(struct devlink_fmsg *fmsg, struct mlx5e_txqsq *sq) +{ + int err; + int i; + + BUILD_BUG_ON_MSG(ARRAY_SIZE(sq_sw_state_type_name) != MLX5E_NUM_SQ_STATES, + "sq_sw_state_type_name string array must be consistent with MLX5E_SQ_STATE_* enum in en.h"); + err = devlink_fmsg_obj_nest_start(fmsg); + if (err) + return err; + + err = mlx5e_health_fmsg_named_obj_nest_start(fmsg, "SW State"); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(sq_sw_state_type_name); ++i) { + err = devlink_fmsg_u32_pair_put(fmsg, sq_sw_state_type_name[i], + test_bit(i, &sq->state)); + if (err) + return err; + } + + err = mlx5e_health_fmsg_named_obj_nest_end(fmsg); + if (err) + return err; + + return devlink_fmsg_obj_nest_end(fmsg); +} + static int mlx5e_tx_reporter_err_cqe_recover(void *ctx) { struct mlx5_core_dev *mdev; @@ -190,6 +232,10 @@ mlx5e_tx_reporter_build_diagnose_output_sq_common(struct devlink_fmsg *fmsg, if (err) return err; + err = mlx5e_health_sq_put_sw_state(fmsg, sq); + if (err) + return err; + err = mlx5e_health_cq_diag_fmsg(&sq->cq, fmsg); if (err) return err; -- cgit v1.2.3 From bb76d250e55c72975b77f5d610fd3733306b3031 Mon Sep 17 00:00:00 2001 From: Adham Faris Date: Mon, 13 Mar 2023 22:42:28 -0700 Subject: net/mlx5e: Add XSK RQ state flag for RQ devlink health diagnostics Currently RQ health diagnostics doesn't inform the user whether an RQ is an XSK RQ or not. Address this, by adding XSK state flag to RQ SW state enum in core/en.h. XSK will be '1' if current RQ is an XSK RQ, and it will be '0' if it's not. In this example below, it can be seen that XSK field value is '1' since xdpsock program have been attached to channel 0 before issuing the devlink query command: $ devlink health diagnose auxiliary/mlx5_core.eth.0/65535 reporter rx Output: ======================================================================= Common config: RQ: type: 2 stride size: 4096 size: 16 ts_format: FRC CQ: stride size: 64 size: 1024 RQs: channel ix: 0 rqn: 4236 HW state: 1 WQE counter: 15 posted WQEs: 15 cc: 15 SW State: enabled: 1 recovering: 0 am: 1 no_csum_complete: 1 csum_full: 0 mini_cqe_hw_stridx: 1 shampo: 0 mini_cqe_enhanced: 0 xsk: 1 CQ: cqn: 1085 HW status: 0 ci: 0 size: 1024 EQ: eqn: 7 irqn: 32 vecidx: 0 ci: 5 size: 2048 ICOSQ: sqn: 4229 HW state: 1 cc: 158 pc: 158 WQE size: 2048 CQ: cqn: 1080 cc: 1 size: 2048 Signed-off-by: Adham Faris Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-10-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 + drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c | 10 ++++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 67f7e24d1f36..e4db252d406c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -348,6 +348,7 @@ enum { MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX, /* set when mini_cqe_resp_stride_index cap is used */ MLX5E_RQ_STATE_SHAMPO, /* set when SHAMPO cap is used */ MLX5E_RQ_STATE_MINI_CQE_ENHANCED, /* set when enhanced mini_cqe_cap is used */ + MLX5E_RQ_STATE_XSK, /* set to indicate an xsk rq */ MLX5E_NUM_RQ_STATES, /* Must be kept last */ }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c index 98c87b3df806..b621f735cdc3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c @@ -18,6 +18,7 @@ static const char * const rq_sw_state_type_name[] = { [MLX5E_RQ_STATE_MINI_CQE_HW_STRIDX] = "mini_cqe_hw_stridx", [MLX5E_RQ_STATE_SHAMPO] = "shampo", [MLX5E_RQ_STATE_MINI_CQE_ENHANCED] = "mini_cqe_enhanced", + [MLX5E_RQ_STATE_XSK] = "xsk", }; static int mlx5e_query_rq_state(struct mlx5_core_dev *dev, u32 rqn, u8 *state) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c index 81a567e17264..ed279f450976 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c @@ -93,13 +93,19 @@ static int mlx5e_open_xsk_rq(struct mlx5e_channel *c, struct mlx5e_params *param struct mlx5e_rq_param *rq_params, struct xsk_buff_pool *pool, struct mlx5e_xsk_param *xsk) { + struct mlx5e_rq *xskrq = &c->xskrq; int err; - err = mlx5e_init_xsk_rq(c, params, pool, xsk, &c->xskrq); + err = mlx5e_init_xsk_rq(c, params, pool, xsk, xskrq); if (err) return err; - return mlx5e_open_rq(params, rq_params, xsk, cpu_to_node(c->cpu), &c->xskrq); + err = mlx5e_open_rq(params, rq_params, xsk, cpu_to_node(c->cpu), xskrq); + if (err) + return err; + + __set_bit(MLX5E_RQ_STATE_XSK, &xskrq->state); + return 0; } int mlx5e_open_xsk(struct mlx5e_priv *priv, struct mlx5e_params *params, -- cgit v1.2.3 From 028522e2844393abc44f7bd7477eb4a455f01579 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 13 Mar 2023 22:42:29 -0700 Subject: net/mlx5: Move needed PTYS functions to core layer Downstream patches require devlink params to access the PTYS register, move the needed functions from mlx5e to the core layer. Signed-off-by: Gal Pressman Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-11-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/en/params.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en/port.c | 157 +-------------------- drivers/net/ethernet/mellanox/mlx5/core/en/port.h | 14 -- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 12 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/port.c | 151 ++++++++++++++++++++ include/linux/mlx5/port.h | 16 +++ 8 files changed, 179 insertions(+), 177 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index a21bd1179477..561da78d3b5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -553,7 +553,7 @@ bool slow_pci_heuristic(struct mlx5_core_dev *mdev) u32 link_speed = 0; u32 pci_bw = 0; - mlx5e_port_max_linkspeed(mdev, &link_speed); + mlx5_port_max_linkspeed(mdev, &link_speed); pci_bw = pcie_bandwidth_available(mdev->pdev, NULL, NULL, NULL); mlx5_core_dbg_once(mdev, "Max link speed = %d, PCI BW = %d\n", link_speed, pci_bw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c index 505ba41195b9..dbe2b19a9570 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c @@ -32,101 +32,6 @@ #include "port.h" -/* speed in units of 1Mb */ -static const u32 mlx5e_link_speed[MLX5E_LINK_MODES_NUMBER] = { - [MLX5E_1000BASE_CX_SGMII] = 1000, - [MLX5E_1000BASE_KX] = 1000, - [MLX5E_10GBASE_CX4] = 10000, - [MLX5E_10GBASE_KX4] = 10000, - [MLX5E_10GBASE_KR] = 10000, - [MLX5E_20GBASE_KR2] = 20000, - [MLX5E_40GBASE_CR4] = 40000, - [MLX5E_40GBASE_KR4] = 40000, - [MLX5E_56GBASE_R4] = 56000, - [MLX5E_10GBASE_CR] = 10000, - [MLX5E_10GBASE_SR] = 10000, - [MLX5E_10GBASE_ER] = 10000, - [MLX5E_40GBASE_SR4] = 40000, - [MLX5E_40GBASE_LR4] = 40000, - [MLX5E_50GBASE_SR2] = 50000, - [MLX5E_100GBASE_CR4] = 100000, - [MLX5E_100GBASE_SR4] = 100000, - [MLX5E_100GBASE_KR4] = 100000, - [MLX5E_100GBASE_LR4] = 100000, - [MLX5E_100BASE_TX] = 100, - [MLX5E_1000BASE_T] = 1000, - [MLX5E_10GBASE_T] = 10000, - [MLX5E_25GBASE_CR] = 25000, - [MLX5E_25GBASE_KR] = 25000, - [MLX5E_25GBASE_SR] = 25000, - [MLX5E_50GBASE_CR2] = 50000, - [MLX5E_50GBASE_KR2] = 50000, -}; - -static const u32 mlx5e_ext_link_speed[MLX5E_EXT_LINK_MODES_NUMBER] = { - [MLX5E_SGMII_100M] = 100, - [MLX5E_1000BASE_X_SGMII] = 1000, - [MLX5E_5GBASE_R] = 5000, - [MLX5E_10GBASE_XFI_XAUI_1] = 10000, - [MLX5E_40GBASE_XLAUI_4_XLPPI_4] = 40000, - [MLX5E_25GAUI_1_25GBASE_CR_KR] = 25000, - [MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2] = 50000, - [MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR] = 50000, - [MLX5E_CAUI_4_100GBASE_CR4_KR4] = 100000, - [MLX5E_100GAUI_2_100GBASE_CR2_KR2] = 100000, - [MLX5E_200GAUI_4_200GBASE_CR4_KR4] = 200000, - [MLX5E_400GAUI_8] = 400000, - [MLX5E_100GAUI_1_100GBASE_CR_KR] = 100000, - [MLX5E_200GAUI_2_200GBASE_CR2_KR2] = 200000, - [MLX5E_400GAUI_4_400GBASE_CR4_KR4] = 400000, -}; - -bool mlx5e_ptys_ext_supported(struct mlx5_core_dev *mdev) -{ - struct mlx5e_port_eth_proto eproto; - int err; - - if (MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet)) - return true; - - err = mlx5_port_query_eth_proto(mdev, 1, true, &eproto); - if (err) - return false; - - return !!eproto.cap; -} - -static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev, - const u32 **arr, u32 *size, - bool force_legacy) -{ - bool ext = force_legacy ? false : mlx5e_ptys_ext_supported(mdev); - - *size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) : - ARRAY_SIZE(mlx5e_link_speed); - *arr = ext ? mlx5e_ext_link_speed : mlx5e_link_speed; -} - -int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext, - struct mlx5e_port_eth_proto *eproto) -{ - u32 out[MLX5_ST_SZ_DW(ptys_reg)]; - int err; - - if (!eproto) - return -EINVAL; - - err = mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, port); - if (err) - return err; - - eproto->cap = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, - eth_proto_capability); - eproto->admin = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_admin); - eproto->oper = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_oper); - return 0; -} - void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status, u8 *an_disable_cap, u8 *an_disable_admin) { @@ -172,30 +77,14 @@ int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable, sizeof(out), MLX5_REG_PTYS, 0, 1); } -u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper, - bool force_legacy) -{ - unsigned long temp = eth_proto_oper; - const u32 *table; - u32 speed = 0; - u32 max_size; - int i; - - mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy); - i = find_first_bit(&temp, max_size); - if (i < max_size) - speed = table[i]; - return speed; -} - int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) { - struct mlx5e_port_eth_proto eproto; + struct mlx5_port_eth_proto eproto; bool force_legacy = false; bool ext; int err; - ext = mlx5e_ptys_ext_supported(mdev); + ext = mlx5_ptys_ext_supported(mdev); err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto); if (err) goto out; @@ -205,7 +94,7 @@ int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) if (err) goto out; } - *speed = mlx5e_port_ptys2speed(mdev, eproto.oper, force_legacy); + *speed = mlx5_port_ptys2speed(mdev, eproto.oper, force_legacy); if (!(*speed)) err = -EINVAL; @@ -213,46 +102,6 @@ out: return err; } -int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) -{ - struct mlx5e_port_eth_proto eproto; - u32 max_speed = 0; - const u32 *table; - u32 max_size; - bool ext; - int err; - int i; - - ext = mlx5e_ptys_ext_supported(mdev); - err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto); - if (err) - return err; - - mlx5e_port_get_speed_arr(mdev, &table, &max_size, false); - for (i = 0; i < max_size; ++i) - if (eproto.cap & MLX5E_PROT_MASK(i)) - max_speed = max(max_speed, table[i]); - - *speed = max_speed; - return 0; -} - -u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed, - bool force_legacy) -{ - u32 link_modes = 0; - const u32 *table; - u32 max_size; - int i; - - mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy); - for (i = 0; i < max_size; ++i) { - if (table[i] == speed) - link_modes |= MLX5E_PROT_MASK(i); - } - return link_modes; -} - int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out) { int sz = MLX5_ST_SZ_BYTES(pbmc_reg); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.h b/drivers/net/ethernet/mellanox/mlx5/core/en/port.h index 3f474e370828..d1da225f35da 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.h @@ -36,25 +36,11 @@ #include #include "en.h" -struct mlx5e_port_eth_proto { - u32 cap; - u32 admin; - u32 oper; -}; - -int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext, - struct mlx5e_port_eth_proto *eproto); void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status, u8 *an_disable_cap, u8 *an_disable_admin); int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable, u32 proto_admin, bool ext); -u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper, - bool force_legacy); int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); -int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); -u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed, - bool force_legacy); -bool mlx5e_ptys_ext_supported(struct mlx5_core_dev *mdev); int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out); int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in); int mlx5e_port_query_sbpr(struct mlx5_core_dev *mdev, u32 desc, u8 dir, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 7708acc9b2ab..53c35147f29b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -220,7 +220,7 @@ static void mlx5e_ethtool_get_speed_arr(struct mlx5_core_dev *mdev, struct ptys2ethtool_config **arr, u32 *size) { - bool ext = mlx5e_ptys_ext_supported(mdev); + bool ext = mlx5_ptys_ext_supported(mdev); *arr = ext ? ptys2ext_ethtool_table : ptys2legacy_ethtool_table; *size = ext ? ARRAY_SIZE(ptys2ext_ethtool_table) : @@ -895,7 +895,7 @@ static void get_speed_duplex(struct net_device *netdev, if (!netif_carrier_ok(netdev)) goto out; - speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper, force_legacy); + speed = mlx5_port_ptys2speed(priv->mdev, eth_proto_oper, force_legacy); if (!speed) { if (data_rate_oper) speed = 100 * data_rate_oper; @@ -980,7 +980,7 @@ static void get_lp_advertising(struct mlx5_core_dev *mdev, u32 eth_proto_lp, struct ethtool_link_ksettings *link_ksettings) { unsigned long *lp_advertising = link_ksettings->link_modes.lp_advertising; - bool ext = mlx5e_ptys_ext_supported(mdev); + bool ext = mlx5_ptys_ext_supported(mdev); ptys2ethtool_adver_link(lp_advertising, eth_proto_lp, ext); } @@ -1160,7 +1160,7 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv, const struct ethtool_link_ksettings *link_ksettings) { struct mlx5_core_dev *mdev = priv->mdev; - struct mlx5e_port_eth_proto eproto; + struct mlx5_port_eth_proto eproto; const unsigned long *adver; bool an_changes = false; u8 an_disable_admin; @@ -1180,7 +1180,7 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv, autoneg = link_ksettings->base.autoneg; speed = link_ksettings->base.speed; - ext_supported = mlx5e_ptys_ext_supported(mdev); + ext_supported = mlx5_ptys_ext_supported(mdev); ext = ext_requested(autoneg, adver, ext_supported); if (!ext_supported && ext) return -EOPNOTSUPP; @@ -1194,7 +1194,7 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv, goto out; } link_modes = autoneg == AUTONEG_ENABLE ? ethtool2ptys_adver_func(adver) : - mlx5e_port_speed2linkmodes(mdev, speed, !ext); + mlx5_port_speed2linkmodes(mdev, speed, !ext); err = mlx5e_speed_validate(priv->netdev, ext, link_modes, autoneg); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 70b8d2dfa751..79dd8ad5ede7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1108,7 +1108,7 @@ mlx5e_hairpin_params_init(struct mlx5e_hairpin_params *hairpin_params, hairpin_params->mdev = mdev; /* set hairpin pair per each 50Gbs share of the link */ - mlx5e_port_max_linkspeed(mdev, &link_speed); + mlx5_port_max_linkspeed(mdev, &link_speed); link_speed = max_t(u32, link_speed, 50000); link_speed64 = link_speed; do_div(link_speed64, 50000); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c index 75015d370922..7c79476cc5f9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c @@ -744,7 +744,7 @@ static int esw_qos_devlink_rate_to_mbps(struct mlx5_core_dev *mdev, const char * u64 value; int err; - err = mlx5e_port_max_linkspeed(mdev, &link_speed_max); + err = mlx5_port_max_linkspeed(mdev, &link_speed_max); if (err) { NL_SET_ERR_MSG_MOD(extack, "Failed to get link maximum speed"); return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index a1548e6bfb35..0daeb4b72cca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -1054,3 +1054,154 @@ out: kfree(out); return err; } + +/* speed in units of 1Mb */ +static const u32 mlx5e_link_speed[MLX5E_LINK_MODES_NUMBER] = { + [MLX5E_1000BASE_CX_SGMII] = 1000, + [MLX5E_1000BASE_KX] = 1000, + [MLX5E_10GBASE_CX4] = 10000, + [MLX5E_10GBASE_KX4] = 10000, + [MLX5E_10GBASE_KR] = 10000, + [MLX5E_20GBASE_KR2] = 20000, + [MLX5E_40GBASE_CR4] = 40000, + [MLX5E_40GBASE_KR4] = 40000, + [MLX5E_56GBASE_R4] = 56000, + [MLX5E_10GBASE_CR] = 10000, + [MLX5E_10GBASE_SR] = 10000, + [MLX5E_10GBASE_ER] = 10000, + [MLX5E_40GBASE_SR4] = 40000, + [MLX5E_40GBASE_LR4] = 40000, + [MLX5E_50GBASE_SR2] = 50000, + [MLX5E_100GBASE_CR4] = 100000, + [MLX5E_100GBASE_SR4] = 100000, + [MLX5E_100GBASE_KR4] = 100000, + [MLX5E_100GBASE_LR4] = 100000, + [MLX5E_100BASE_TX] = 100, + [MLX5E_1000BASE_T] = 1000, + [MLX5E_10GBASE_T] = 10000, + [MLX5E_25GBASE_CR] = 25000, + [MLX5E_25GBASE_KR] = 25000, + [MLX5E_25GBASE_SR] = 25000, + [MLX5E_50GBASE_CR2] = 50000, + [MLX5E_50GBASE_KR2] = 50000, +}; + +static const u32 mlx5e_ext_link_speed[MLX5E_EXT_LINK_MODES_NUMBER] = { + [MLX5E_SGMII_100M] = 100, + [MLX5E_1000BASE_X_SGMII] = 1000, + [MLX5E_5GBASE_R] = 5000, + [MLX5E_10GBASE_XFI_XAUI_1] = 10000, + [MLX5E_40GBASE_XLAUI_4_XLPPI_4] = 40000, + [MLX5E_25GAUI_1_25GBASE_CR_KR] = 25000, + [MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2] = 50000, + [MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR] = 50000, + [MLX5E_CAUI_4_100GBASE_CR4_KR4] = 100000, + [MLX5E_100GAUI_2_100GBASE_CR2_KR2] = 100000, + [MLX5E_200GAUI_4_200GBASE_CR4_KR4] = 200000, + [MLX5E_400GAUI_8] = 400000, + [MLX5E_100GAUI_1_100GBASE_CR_KR] = 100000, + [MLX5E_200GAUI_2_200GBASE_CR2_KR2] = 200000, + [MLX5E_400GAUI_4_400GBASE_CR4_KR4] = 400000, +}; + +int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext, + struct mlx5_port_eth_proto *eproto) +{ + u32 out[MLX5_ST_SZ_DW(ptys_reg)]; + int err; + + if (!eproto) + return -EINVAL; + + err = mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, port); + if (err) + return err; + + eproto->cap = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, + eth_proto_capability); + eproto->admin = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_admin); + eproto->oper = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_oper); + return 0; +} + +bool mlx5_ptys_ext_supported(struct mlx5_core_dev *mdev) +{ + struct mlx5_port_eth_proto eproto; + int err; + + if (MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet)) + return true; + + err = mlx5_port_query_eth_proto(mdev, 1, true, &eproto); + if (err) + return false; + + return !!eproto.cap; +} + +static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev, + const u32 **arr, u32 *size, + bool force_legacy) +{ + bool ext = force_legacy ? false : mlx5_ptys_ext_supported(mdev); + + *size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) : + ARRAY_SIZE(mlx5e_link_speed); + *arr = ext ? mlx5e_ext_link_speed : mlx5e_link_speed; +} + +u32 mlx5_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper, + bool force_legacy) +{ + unsigned long temp = eth_proto_oper; + const u32 *table; + u32 speed = 0; + u32 max_size; + int i; + + mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy); + i = find_first_bit(&temp, max_size); + if (i < max_size) + speed = table[i]; + return speed; +} + +u32 mlx5_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed, + bool force_legacy) +{ + u32 link_modes = 0; + const u32 *table; + u32 max_size; + int i; + + mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy); + for (i = 0; i < max_size; ++i) { + if (table[i] == speed) + link_modes |= MLX5E_PROT_MASK(i); + } + return link_modes; +} + +int mlx5_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) +{ + struct mlx5_port_eth_proto eproto; + u32 max_speed = 0; + const u32 *table; + u32 max_size; + bool ext; + int err; + int i; + + ext = mlx5_ptys_ext_supported(mdev); + err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto); + if (err) + return err; + + mlx5e_port_get_speed_arr(mdev, &table, &max_size, false); + for (i = 0; i < max_size; ++i) + if (eproto.cap & MLX5E_PROT_MASK(i)) + max_speed = max(max_speed, table[i]); + + *speed = max_speed; + return 0; +} diff --git a/include/linux/mlx5/port.h b/include/linux/mlx5/port.h index e96ee1e348cb..98b2e1e149f9 100644 --- a/include/linux/mlx5/port.h +++ b/include/linux/mlx5/port.h @@ -141,6 +141,12 @@ enum mlx5_ptys_width { MLX5_PTYS_WIDTH_12X = 1 << 4, }; +struct mlx5_port_eth_proto { + u32 cap; + u32 admin; + u32 oper; +}; + #define MLX5E_PROT_MASK(link_mode) (1U << link_mode) #define MLX5_GET_ETH_PROTO(reg, out, ext, field) \ (ext ? MLX5_GET(reg, out, ext_##field) : \ @@ -218,4 +224,14 @@ int mlx5_set_trust_state(struct mlx5_core_dev *mdev, u8 trust_state); int mlx5_query_trust_state(struct mlx5_core_dev *mdev, u8 *trust_state); int mlx5_set_dscp2prio(struct mlx5_core_dev *mdev, u8 dscp, u8 prio); int mlx5_query_dscp2prio(struct mlx5_core_dev *mdev, u8 *dscp2prio); + +int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext, + struct mlx5_port_eth_proto *eproto); +bool mlx5_ptys_ext_supported(struct mlx5_core_dev *mdev); +u32 mlx5_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper, + bool force_legacy); +u32 mlx5_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed, + bool force_legacy); +int mlx5_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); + #endif /* __MLX5_PORT_H__ */ -- cgit v1.2.3 From 1bffcea42926b26e092045ac398850e80d950bb2 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 13 Mar 2023 22:42:30 -0700 Subject: net/mlx5e: Add devlink hairpin queues parameters We refer to a TC NIC rule that involves forwarding as "hairpin". Hairpin queues are mlx5 hardware specific implementation for hardware forwarding of such packets. Per the discussion in [1], move the hairpin queues control (number and size) from debugfs to devlink. Expose two devlink params: - hairpin_num_queues: control the number of hairpin queues - hairpin_queue_size: control the size (in packets) of the hairpin queues [1] https://lore.kernel.org/all/20230111194608.7f15b9a1@kernel.org/ Signed-off-by: Gal Pressman Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-12-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlx5/devlink.rst | 35 ++++++++++++ Documentation/networking/devlink/mlx5.rst | 12 ++++ drivers/net/ethernet/mellanox/mlx5/core/devlink.c | 66 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/devlink.h | 2 + drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 50 +++++++--------- 5 files changed, 134 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/devlink.rst b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/devlink.rst index 9b5c40ba7f0d..0995e4e5acd7 100644 --- a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/devlink.rst +++ b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5/devlink.rst @@ -122,6 +122,41 @@ users try to enable them. $ devlink dev eswitch set pci/0000:06:00.0 mode switchdev +hairpin_num_queues: Number of hairpin queues +-------------------------------------------- +We refer to a TC NIC rule that involves forwarding as "hairpin". + +Hairpin queues are mlx5 hardware specific implementation for hardware +forwarding of such packets. + +- Show the number of hairpin queues:: + + $ devlink dev param show pci/0000:06:00.0 name hairpin_num_queues + pci/0000:06:00.0: + name hairpin_num_queues type driver-specific + values: + cmode driverinit value 2 + +- Change the number of hairpin queues:: + + $ devlink dev param set pci/0000:06:00.0 name hairpin_num_queues value 4 cmode driverinit + +hairpin_queue_size: Size of the hairpin queues +---------------------------------------------- +Control the size of the hairpin queues. + +- Show the size of the hairpin queues:: + + $ devlink dev param show pci/0000:06:00.0 name hairpin_queue_size + pci/0000:06:00.0: + name hairpin_queue_size type driver-specific + values: + cmode driverinit value 1024 + +- Change the size (in packets) of the hairpin queues:: + + $ devlink dev param set pci/0000:06:00.0 name hairpin_queue_size value 512 cmode driverinit + Health reporters ================ diff --git a/Documentation/networking/devlink/mlx5.rst b/Documentation/networking/devlink/mlx5.rst index 3321117cf605..202798d6501e 100644 --- a/Documentation/networking/devlink/mlx5.rst +++ b/Documentation/networking/devlink/mlx5.rst @@ -72,6 +72,18 @@ parameters. Default: disabled + * - ``hairpin_num_queues`` + - u32 + - driverinit + - We refer to a TC NIC rule that involves forwarding as "hairpin". + Hairpin queues are mlx5 hardware specific implementation for hardware + forwarding of such packets. + + Control the number of hairpin queues. + * - ``hairpin_queue_size`` + - u32 + - driverinit + - Control the size (in packets) of the hairpin queues. The ``mlx5`` driver supports reloading via ``DEVLINK_CMD_RELOAD`` diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index b7784e02c2dd..1ee2a472e1d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -494,6 +494,61 @@ static int mlx5_devlink_eq_depth_validate(struct devlink *devlink, u32 id, return (val.vu32 >= 64 && val.vu32 <= 4096) ? 0 : -EINVAL; } +static int +mlx5_devlink_hairpin_num_queues_validate(struct devlink *devlink, u32 id, + union devlink_param_value val, + struct netlink_ext_ack *extack) +{ + return val.vu32 ? 0 : -EINVAL; +} + +static int +mlx5_devlink_hairpin_queue_size_validate(struct devlink *devlink, u32 id, + union devlink_param_value val, + struct netlink_ext_ack *extack) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + u32 val32 = val.vu32; + + if (!is_power_of_2(val32)) { + NL_SET_ERR_MSG_MOD(extack, "Value is not power of two"); + return -EINVAL; + } + + if (val32 > BIT(MLX5_CAP_GEN(dev, log_max_hairpin_num_packets))) { + NL_SET_ERR_MSG_FMT_MOD( + extack, "Maximum hairpin queue size is %lu", + BIT(MLX5_CAP_GEN(dev, log_max_hairpin_num_packets))); + return -EINVAL; + } + + return 0; +} + +static void mlx5_devlink_hairpin_params_init_values(struct devlink *devlink) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + union devlink_param_value value; + u64 link_speed64; + u32 link_speed; + + /* set hairpin pair per each 50Gbs share of the link */ + mlx5_port_max_linkspeed(dev, &link_speed); + link_speed = max_t(u32, link_speed, 50000); + link_speed64 = link_speed; + do_div(link_speed64, 50000); + + value.vu32 = link_speed64; + devl_param_driverinit_value_set( + devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, value); + + value.vu32 = + BIT(min_t(u32, 16 - MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(dev), + MLX5_CAP_GEN(dev, log_max_hairpin_num_packets))); + devl_param_driverinit_value_set( + devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, value); +} + static const struct devlink_param mlx5_devlink_params[] = { DEVLINK_PARAM_GENERIC(ENABLE_ROCE, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), NULL, NULL, mlx5_devlink_enable_roce_validate), @@ -547,6 +602,14 @@ static void mlx5_devlink_set_params_init_values(struct devlink *devlink) static const struct devlink_param mlx5_devlink_eth_params[] = { DEVLINK_PARAM_GENERIC(ENABLE_ETH, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), NULL, NULL, NULL), + DEVLINK_PARAM_DRIVER(MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, + "hairpin_num_queues", DEVLINK_PARAM_TYPE_U32, + BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), NULL, NULL, + mlx5_devlink_hairpin_num_queues_validate), + DEVLINK_PARAM_DRIVER(MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, + "hairpin_queue_size", DEVLINK_PARAM_TYPE_U32, + BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), NULL, NULL, + mlx5_devlink_hairpin_queue_size_validate), }; static int mlx5_devlink_eth_params_register(struct devlink *devlink) @@ -567,6 +630,9 @@ static int mlx5_devlink_eth_params_register(struct devlink *devlink) devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH, value); + + mlx5_devlink_hairpin_params_init_values(devlink); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h index 212b12424146..5dcfb4d86d8a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h @@ -12,6 +12,8 @@ enum mlx5_devlink_param_id { MLX5_DEVLINK_PARAM_ID_ESW_LARGE_GROUP_NUM, MLX5_DEVLINK_PARAM_ID_ESW_PORT_METADATA, MLX5_DEVLINK_PARAM_ID_ESW_MULTIPORT, + MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, + MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, }; struct mlx5_trap_ctx { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 79dd8ad5ede7..2e6351ef4d9c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -44,6 +44,7 @@ #include #include #include +#include "devlink.h" #include "en.h" #include "en/tc/post_act.h" #include "en/tc/act_stats.h" @@ -73,12 +74,6 @@ #define MLX5E_TC_TABLE_NUM_GROUPS 4 #define MLX5E_TC_TABLE_MAX_GROUP_SIZE BIT(18) -struct mlx5e_hairpin_params { - struct mlx5_core_dev *mdev; - u32 num_queues; - u32 queue_size; -}; - struct mlx5e_tc_table { /* Protects the dynamic assignment of the t parameter * which is the nic tc root table. @@ -101,7 +96,6 @@ struct mlx5e_tc_table { struct mlx5_tc_ct_priv *ct; struct mapping_ctx *mapping; - struct mlx5e_hairpin_params hairpin_params; struct dentry *dfs_root; /* tc action stats */ @@ -1099,33 +1093,15 @@ static void mlx5e_tc_debugfs_init(struct mlx5e_tc_table *tc, &debugfs_hairpin_table_dump_fops); } -static void -mlx5e_hairpin_params_init(struct mlx5e_hairpin_params *hairpin_params, - struct mlx5_core_dev *mdev) -{ - u64 link_speed64; - u32 link_speed; - - hairpin_params->mdev = mdev; - /* set hairpin pair per each 50Gbs share of the link */ - mlx5_port_max_linkspeed(mdev, &link_speed); - link_speed = max_t(u32, link_speed, 50000); - link_speed64 = link_speed; - do_div(link_speed64, 50000); - hairpin_params->num_queues = link_speed64; - - hairpin_params->queue_size = - BIT(min_t(u32, 16 - MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(mdev), - MLX5_CAP_GEN(mdev, log_max_hairpin_num_packets))); -} - static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, struct mlx5e_tc_flow_parse_attr *parse_attr, struct netlink_ext_ack *extack) { struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); + struct devlink *devlink = priv_to_devlink(priv->mdev); int peer_ifindex = parse_attr->mirred_ifindex[0]; + union devlink_param_value val = {}; struct mlx5_hairpin_params params; struct mlx5_core_dev *peer_mdev; struct mlx5e_hairpin_entry *hpe; @@ -1182,7 +1158,14 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, hash_hairpin_info(peer_id, match_prio)); mutex_unlock(&tc->hairpin_tbl_lock); - params.log_num_packets = ilog2(tc->hairpin_params.queue_size); + err = devl_param_driverinit_value_get( + devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, &val); + if (err) { + err = -ENOMEM; + goto out_err; + } + + params.log_num_packets = ilog2(val.vu32); params.log_data_size = clamp_t(u32, params.log_num_packets + @@ -1191,7 +1174,14 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, MLX5_CAP_GEN(priv->mdev, log_max_hairpin_wq_data_sz)); params.q_counter = priv->q_counter; - params.num_channels = tc->hairpin_params.num_queues; + err = devl_param_driverinit_value_get( + devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, &val); + if (err) { + err = -ENOMEM; + goto out_err; + } + + params.num_channels = val.vu32; hp = mlx5e_hairpin_create(priv, ¶ms, peer_ifindex); hpe->hp = hp; @@ -5289,8 +5279,6 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv) tc->ct = mlx5_tc_ct_init(priv, tc->chains, &tc->mod_hdr, MLX5_FLOW_NAMESPACE_KERNEL, tc->post_act); - mlx5e_hairpin_params_init(&tc->hairpin_params, dev); - tc->netdevice_nb.notifier_call = mlx5e_tc_netdev_event; err = register_netdevice_notifier_dev_net(priv->netdev, &tc->netdevice_nb, -- cgit v1.2.3 From 8a0594c0961012ac07112f41800da29dd5632c03 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 13 Mar 2023 22:42:31 -0700 Subject: net/mlx5e: Add more information to hairpin table dump Print the number of hairpin queues and size as part of the hairpin table dump. Signed-off-by: Gal Pressman Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-13-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 2e6351ef4d9c..a139b5e88e2a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -583,6 +583,7 @@ struct mlx5e_hairpin { struct mlx5e_tir direct_tir; int num_channels; + u8 log_num_packets; struct mlx5e_rqt indir_rqt; struct mlx5e_tir indir_tir[MLX5E_NUM_INDIR_TIRS]; struct mlx5_ttc_table *ttc; @@ -929,6 +930,7 @@ mlx5e_hairpin_create(struct mlx5e_priv *priv, struct mlx5_hairpin_params *params hp->func_mdev = func_mdev; hp->func_priv = priv; hp->num_channels = params->num_channels; + hp->log_num_packets = params->log_num_packets; err = mlx5e_hairpin_create_transport(hp); if (err) @@ -1070,9 +1072,11 @@ static int debugfs_hairpin_table_dump_show(struct seq_file *file, void *priv) mutex_lock(&tc->hairpin_tbl_lock); hash_for_each(tc->hairpin_tbl, bkt, hpe, hairpin_hlist) - seq_printf(file, "Hairpin peer_vhca_id %u prio %u refcnt %u\n", + seq_printf(file, + "Hairpin peer_vhca_id %u prio %u refcnt %u num_channels %u num_packets %lu\n", hpe->peer_vhca_id, hpe->prio, - refcount_read(&hpe->refcnt)); + refcount_read(&hpe->refcnt), hpe->hp->num_channels, + BIT(hpe->hp->log_num_packets)); mutex_unlock(&tc->hairpin_tbl_lock); return 0; -- cgit v1.2.3 From 244fd698207f0baec062da4737e31c76b1be2371 Mon Sep 17 00:00:00 2001 From: Maor Dickman Date: Mon, 13 Mar 2023 22:42:32 -0700 Subject: net/mlx5e: TC, Extract indr setup block checks to function In preparation for next patch which will add new check if device block can be setup, extract all existing checks to function to make it more readable and maintainable. Signed-off-by: Maor Dickman Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-14-saeed@kernel.org Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/en/rep/tc.c | 58 ++++++++++++++-------- 1 file changed, 36 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c index 8f7452dc00ee..b4af006dc494 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c @@ -426,39 +426,53 @@ static bool mlx5e_rep_macvlan_mode_supported(const struct net_device *dev) return macvlan->mode == MACVLAN_MODE_PASSTHRU; } -static int -mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch, - struct mlx5e_rep_priv *rpriv, - struct flow_block_offload *f, - flow_setup_cb_t *setup_cb, - void *data, - void (*cleanup)(struct flow_block_cb *block_cb)) +static bool +mlx5e_rep_check_indr_block_supported(struct mlx5e_rep_priv *rpriv, + struct net_device *netdev, + struct flow_block_offload *f) { struct mlx5e_priv *priv = netdev_priv(rpriv->netdev); struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - bool is_ovs_int_port = netif_is_ovs_master(netdev); - struct mlx5e_rep_indr_block_priv *indr_priv; - struct flow_block_cb *block_cb; - if (!mlx5e_tc_tun_device_to_offload(priv, netdev) && - !(is_vlan_dev(netdev) && vlan_dev_real_dev(netdev) == rpriv->netdev) && - !is_ovs_int_port) { - if (!(netif_is_macvlan(netdev) && macvlan_dev_real_dev(netdev) == rpriv->netdev)) - return -EOPNOTSUPP; + if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && + f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) + return false; + + if (mlx5e_tc_tun_device_to_offload(priv, netdev)) + return true; + + if (is_vlan_dev(netdev) && vlan_dev_real_dev(netdev) == rpriv->netdev) + return true; + + if (netif_is_macvlan(netdev)) { if (!mlx5e_rep_macvlan_mode_supported(netdev)) { netdev_warn(netdev, "Offloading ingress filter is supported only with macvlan passthru mode"); - return -EOPNOTSUPP; + return false; } + + if (macvlan_dev_real_dev(netdev) == rpriv->netdev) + return true; } - if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && - f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) - return -EOPNOTSUPP; + if (netif_is_ovs_master(netdev) && f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS && + mlx5e_tc_int_port_supported(esw)) + return true; - if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS && !is_ovs_int_port) - return -EOPNOTSUPP; + return false; +} + +static int +mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch, + struct mlx5e_rep_priv *rpriv, + struct flow_block_offload *f, + flow_setup_cb_t *setup_cb, + void *data, + void (*cleanup)(struct flow_block_cb *block_cb)) +{ + struct mlx5e_rep_indr_block_priv *indr_priv; + struct flow_block_cb *block_cb; - if (is_ovs_int_port && !mlx5e_tc_int_port_supported(esw)) + if (!mlx5e_rep_check_indr_block_supported(rpriv, netdev, f)) return -EOPNOTSUPP; f->unlocked_driver_cb = true; -- cgit v1.2.3 From d5d006bb27ad9d408074eef7e091fbd87335f913 Mon Sep 17 00:00:00 2001 From: Maor Dickman Date: Mon, 13 Mar 2023 22:42:33 -0700 Subject: net/mlx5e: Enable TC offload for ingress MACVLAN over bond Support offloading of TC rules that filter ingress traffic from a MACVLAN device, which is attached to bond device. Signed-off-by: Maor Dickman Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-15-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c index b4af006dc494..19c4a83982ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c @@ -433,6 +433,7 @@ mlx5e_rep_check_indr_block_supported(struct mlx5e_rep_priv *rpriv, { struct mlx5e_priv *priv = netdev_priv(rpriv->netdev); struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct net_device *macvlan_real_dev; if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) @@ -450,7 +451,11 @@ mlx5e_rep_check_indr_block_supported(struct mlx5e_rep_priv *rpriv, return false; } - if (macvlan_dev_real_dev(netdev) == rpriv->netdev) + macvlan_real_dev = macvlan_dev_real_dev(netdev); + + if (macvlan_real_dev == rpriv->netdev) + return true; + if (netif_is_bond_master(macvlan_real_dev)) return true; } -- cgit v1.2.3 From 63b02048f9a781e01e59743cd2d3e0a1f10c971a Mon Sep 17 00:00:00 2001 From: Maor Dickman Date: Mon, 13 Mar 2023 22:42:34 -0700 Subject: net/mlx5e: Enable TC offload for egress MACVLAN over bond Support offloading of TC rules that mirror/redirect egress traffic to a MACVLAN device, which is attached to bond device which master mlx5 devices. Signed-off-by: Maor Dickman Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed Link: https://lore.kernel.org/r/20230314054234.267365-16-saeed@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c index 07cc65596f89..291193f7120d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c @@ -234,6 +234,9 @@ parse_mirred(struct mlx5e_tc_act_parse_state *parse_state, if (mlx5_lag_mpesw_do_mirred(priv->mdev, out_dev, extack)) return -EOPNOTSUPP; + if (netif_is_macvlan(out_dev)) + out_dev = macvlan_dev_real_dev(out_dev); + out_dev = get_fdb_out_dev(uplink_dev, out_dev); if (!out_dev) return -ENODEV; @@ -250,9 +253,6 @@ parse_mirred(struct mlx5e_tc_act_parse_state *parse_state, return err; } - if (netif_is_macvlan(out_dev)) - out_dev = macvlan_dev_real_dev(out_dev); - err = verify_uplink_forwarding(priv, attr, out_dev, extack); if (err) return err; -- cgit v1.2.3 From 8a8db7aeaa6dae4ddae033858bf5b49aecfbbe42 Mon Sep 17 00:00:00 2001 From: Wentao Jia Date: Tue, 14 Mar 2023 08:36:05 +0200 Subject: nfp: flower: add get_flow_act_ct() for ct action CT action is a special case different from other actions, CT clear action is not required when get ct action, but this case is not considered. If CT clear action in the flow rule, skip the CT clear action when get ct action, return the first ct action that is not a CT clear action Signed-off-by: Wentao Jia Acked-by: Simon Horman Signed-off-by: Louis Peens Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/netronome/nfp/flower/conntrack.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index d23830b5bcb8..a54d374788e1 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -1656,6 +1656,22 @@ void nfp_fl_ct_clean_flow_entry(struct nfp_fl_ct_flow_entry *entry) kfree(entry); } +static struct flow_action_entry *get_flow_act_ct(struct flow_rule *rule) +{ + struct flow_action_entry *act; + int i; + + /* More than one ct action may be present in a flow rule, + * Return the first one that is not a CT clear action + */ + flow_action_for_each(i, act, &rule->action) { + if (act->id == FLOW_ACTION_CT && act->ct.action != TCA_CT_ACT_CLEAR) + return act; + } + + return NULL; +} + static struct flow_action_entry *get_flow_act(struct flow_rule *rule, enum flow_action_id act_id) { @@ -1720,7 +1736,7 @@ int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv, struct nfp_fl_ct_zone_entry *zt; int err; - ct_act = get_flow_act(flow->rule, FLOW_ACTION_CT); + ct_act = get_flow_act_ct(flow->rule); if (!ct_act) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: Conntrack action empty in conntrack offload"); -- cgit v1.2.3 From cee7b339d806f71df611087724ddfbaad2d8f8b7 Mon Sep 17 00:00:00 2001 From: Wentao Jia Date: Tue, 14 Mar 2023 08:36:06 +0200 Subject: nfp: flower: refactor function "is_pre_ct_flow" In the scenario of multiple ct zones, ct state key match and ct action is present in one flow rule, the flow rule is classified to post_ct_flow in design. There is no ct state key match for pre ct flow, the judging condition is added to function "is_pre_ct_flow". Chain_index is another field for judging which flows are pre ct flow If chain_index not 0, the flow is not pre ct flow. Signed-off-by: Wentao Jia Acked-by: Simon Horman Signed-off-by: Louis Peens Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/netronome/nfp/flower/conntrack.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index a54d374788e1..e0d6c8754272 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -55,9 +55,21 @@ static void *get_hashentry(struct rhashtable *ht, void *key, bool is_pre_ct_flow(struct flow_cls_offload *flow) { + struct flow_rule *rule = flow_cls_offload_flow_rule(flow); + struct flow_dissector *dissector = rule->match.dissector; struct flow_action_entry *act; + struct flow_match_ct ct; int i; + if (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CT)) { + flow_rule_match_ct(rule, &ct); + if (ct.key->ct_state) + return false; + } + + if (flow->common.chain_index) + return false; + flow_action_for_each(i, act, &flow->rule->action) { if (act->id == FLOW_ACTION_CT) { /* The pre_ct rule only have the ct or ct nat action, cannot -- cgit v1.2.3 From 0b8d953cce26287a7f5eb78ff49496e9836c88ed Mon Sep 17 00:00:00 2001 From: Wentao Jia Date: Tue, 14 Mar 2023 08:36:07 +0200 Subject: nfp: flower: refactor function "is_post_ct_flow" 'ct_clear' action only or no ct action is supported for 'post_ct_flow'. But in scenario of multiple ct zones, one non 'ct_clear' ct action or more ct actions, including 'ct_clear action', may be present in one flow rule. If ct state match key is 'ct_established', the flow rule is still expected to be classified as 'post_ct_flow'. Check ct status first in function "is_post_ct_flow" to achieve this. Signed-off-by: Wentao Jia Acked-by: Simon Horman Signed-off-by: Louis Peens Signed-off-by: Jakub Kicinski --- .../net/ethernet/netronome/nfp/flower/conntrack.c | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index e0d6c8754272..6b90b922bac0 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -94,24 +94,23 @@ bool is_post_ct_flow(struct flow_cls_offload *flow) struct flow_match_ct ct; int i; - /* post ct entry cannot contains any ct action except ct_clear. */ - flow_action_for_each(i, act, &flow->rule->action) { - if (act->id == FLOW_ACTION_CT) { - /* ignore ct clear action. */ - if (act->ct.action == TCA_CT_ACT_CLEAR) { - exist_ct_clear = true; - continue; - } - - return false; - } - } - if (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CT)) { flow_rule_match_ct(rule, &ct); if (ct.key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) return true; } else { + /* post ct entry cannot contains any ct action except ct_clear. */ + flow_action_for_each(i, act, &flow->rule->action) { + if (act->id == FLOW_ACTION_CT) { + /* ignore ct clear action. */ + if (act->ct.action == TCA_CT_ACT_CLEAR) { + exist_ct_clear = true; + continue; + } + + return false; + } + } /* when do nat with ct, the post ct entry ignore the ct status, * will match the nat field(sip/dip) instead. In this situation, * the flow chain index is not zero and contains ct clear action. -- cgit v1.2.3 From 3e44d19934b92398785b3ffc2353b9eba264140e Mon Sep 17 00:00:00 2001 From: Wentao Jia Date: Tue, 14 Mar 2023 08:36:08 +0200 Subject: nfp: flower: add goto_chain_index for ct entry The chain_index has different means in pre ct entry and post ct entry. In pre ct entry, it means chain index, but in post ct entry, it means goto chain index, it is confused. chain_index and goto_chain_index may be present in one flow rule, It cannot be distinguished by one field chain_index, both chain_index and goto_chain_index are required in the follow-up patch to support multiple ct zones Another field goto_chain_index is added to record the goto chain index. If no goto action in post ct entry, goto_chain_index is 0. Signed-off-by: Wentao Jia Acked-by: Simon Horman Signed-off-by: Louis Peens Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/netronome/nfp/flower/conntrack.c | 8 ++++++-- drivers/net/ethernet/netronome/nfp/flower/conntrack.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index 6b90b922bac0..86ea8cbc67a2 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -1254,7 +1254,7 @@ static int nfp_ct_do_tc_merge(struct nfp_fl_ct_zone_entry *zt, /* Checks that the chain_index of the filter matches the * chain_index of the GOTO action. */ - if (post_ct_entry->chain_index != pre_ct_entry->chain_index) + if (post_ct_entry->chain_index != pre_ct_entry->goto_chain_index) return -EINVAL; err = nfp_ct_merge_check(pre_ct_entry, post_ct_entry); @@ -1783,7 +1783,8 @@ int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv, if (IS_ERR(ct_entry)) return PTR_ERR(ct_entry); ct_entry->type = CT_TYPE_PRE_CT; - ct_entry->chain_index = ct_goto->chain_index; + ct_entry->chain_index = flow->common.chain_index; + ct_entry->goto_chain_index = ct_goto->chain_index; list_add(&ct_entry->list_node, &zt->pre_ct_list); zt->pre_ct_count++; @@ -1806,6 +1807,7 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv, struct nfp_fl_ct_zone_entry *zt; bool wildcarded = false; struct flow_match_ct ct; + struct flow_action_entry *ct_goto; flow_rule_match_ct(rule, &ct); if (!ct.mask->ct_zone) { @@ -1830,6 +1832,8 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv, ct_entry->type = CT_TYPE_POST_CT; ct_entry->chain_index = flow->common.chain_index; + ct_goto = get_flow_act(flow->rule, FLOW_ACTION_GOTO); + ct_entry->goto_chain_index = ct_goto ? ct_goto->chain_index : 0; list_add(&ct_entry->list_node, &zt->post_ct_list); zt->post_ct_count++; diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.h b/drivers/net/ethernet/netronome/nfp/flower/conntrack.h index 762c0b36e269..9440ab776ece 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.h +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.h @@ -112,6 +112,7 @@ enum nfp_nfp_layer_name { * @cookie: Flow cookie, same as original TC flow, used as key * @list_node: Used by the list * @chain_index: Chain index of the original flow + * @goto_chain_index: goto chain index of the flow * @netdev: netdev structure. * @type: Type of pre-entry from enum ct_entry_type * @zt: Reference to the zone table this belongs to @@ -125,6 +126,7 @@ struct nfp_fl_ct_flow_entry { unsigned long cookie; struct list_head list_node; u32 chain_index; + u32 goto_chain_index; enum ct_entry_type type; struct net_device *netdev; struct nfp_fl_ct_zone_entry *zt; -- cgit v1.2.3 From 46a83c85b683b647d86e679e2b095494e87f4d7c Mon Sep 17 00:00:00 2001 From: Wentao Jia Date: Tue, 14 Mar 2023 08:36:09 +0200 Subject: nfp: flower: prepare for parameterisation of number of offload rules The fixed number of offload flow rule is only supported scenario of one ct zone, in the scenario of multiple ct zones, dynamic number and more number of offload flow rules are required. In order to support scenario of multiple ct zones, parameter num_rules is added for to offload flow rules Signed-off-by: Wentao Jia Acked-by: Simon Horman Signed-off-by: Louis Peens Signed-off-by: Jakub Kicinski --- .../net/ethernet/netronome/nfp/flower/conntrack.c | 54 +++++++++++----------- 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index 86ea8cbc67a2..ecffb6b0f3a1 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -693,34 +693,34 @@ static void nfp_fl_get_csum_flag(struct flow_action_entry *a_in, u8 ip_proto, u3 static int nfp_fl_merge_actions_offload(struct flow_rule **rules, struct nfp_flower_priv *priv, struct net_device *netdev, - struct nfp_fl_payload *flow_pay) + struct nfp_fl_payload *flow_pay, + int num_rules) { enum flow_action_hw_stats tmp_stats = FLOW_ACTION_HW_STATS_DONT_CARE; struct flow_action_entry *a_in; - int i, j, num_actions, id; + int i, j, id, num_actions = 0; struct flow_rule *a_rule; int err = 0, offset = 0; - num_actions = rules[CT_TYPE_PRE_CT]->action.num_entries + - rules[CT_TYPE_NFT]->action.num_entries + - rules[CT_TYPE_POST_CT]->action.num_entries; + for (i = 0; i < num_rules; i++) + num_actions += rules[i]->action.num_entries; /* Add one action to make sure there is enough room to add an checksum action * when do nat. */ - a_rule = flow_rule_alloc(num_actions + 1); + a_rule = flow_rule_alloc(num_actions + (num_rules / 2)); if (!a_rule) return -ENOMEM; - /* Actions need a BASIC dissector. */ - a_rule->match = rules[CT_TYPE_PRE_CT]->match; /* post_ct entry have one action at least. */ - if (rules[CT_TYPE_POST_CT]->action.num_entries != 0) { - tmp_stats = rules[CT_TYPE_POST_CT]->action.entries[0].hw_stats; - } + if (rules[num_rules - 1]->action.num_entries != 0) + tmp_stats = rules[num_rules - 1]->action.entries[0].hw_stats; + + /* Actions need a BASIC dissector. */ + a_rule->match = rules[0]->match; /* Copy actions */ - for (j = 0; j < _CT_TYPE_MAX; j++) { + for (j = 0; j < num_rules; j++) { u32 csum_updated = 0; u8 ip_proto = 0; @@ -758,8 +758,9 @@ static int nfp_fl_merge_actions_offload(struct flow_rule **rules, /* nft entry is generated by tc ct, which mangle action do not care * the stats, inherit the post entry stats to meet the * flow_action_hw_stats_check. + * nft entry flow rules are at odd array index. */ - if (j == CT_TYPE_NFT) { + if (j & 0x01) { if (a_in->hw_stats == FLOW_ACTION_HW_STATS_DONT_CARE) a_in->hw_stats = tmp_stats; nfp_fl_get_csum_flag(a_in, ip_proto, &csum_updated); @@ -801,6 +802,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) struct nfp_fl_payload *flow_pay; struct flow_rule *rules[_CT_TYPE_MAX]; + int num_rules = _CT_TYPE_MAX; u8 *key, *msk, *kdata, *mdata; struct nfp_port *port = NULL; struct net_device *netdev; @@ -820,7 +822,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) memset(&key_map, 0, sizeof(key_map)); /* Calculate the resultant key layer and size for offload */ - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { err = nfp_flower_calculate_key_layers(priv->app, m_entry->netdev, &tmp_layer, rules[i], @@ -886,7 +888,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) * that the layer is not present. */ if (!qinq_sup) { - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { offset = key_map[FLOW_PAY_META_TCI]; key = kdata + offset; msk = mdata + offset; @@ -900,7 +902,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) offset = key_map[FLOW_PAY_MAC_MPLS]; key = kdata + offset; msk = mdata + offset; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)key, (struct nfp_flower_mac_mpls *)msk, rules[i]); @@ -916,7 +918,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) offset = key_map[FLOW_PAY_IPV4]; key = kdata + offset; msk = mdata + offset; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)key, (struct nfp_flower_ipv4 *)msk, rules[i]); @@ -927,7 +929,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) offset = key_map[FLOW_PAY_IPV6]; key = kdata + offset; msk = mdata + offset; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)key, (struct nfp_flower_ipv6 *)msk, rules[i]); @@ -938,7 +940,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) offset = key_map[FLOW_PAY_L4]; key = kdata + offset; msk = mdata + offset; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_tport((struct nfp_flower_tp_ports *)key, (struct nfp_flower_tp_ports *)msk, rules[i]); @@ -949,7 +951,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) offset = key_map[FLOW_PAY_QINQ]; key = kdata + offset; msk = mdata + offset; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_vlan((struct nfp_flower_vlan *)key, (struct nfp_flower_vlan *)msk, rules[i]); @@ -965,7 +967,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) struct nfp_ipv6_addr_entry *entry; struct in6_addr *dst; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_ipv6_gre_tun((void *)key, (void *)msk, rules[i]); } @@ -982,7 +984,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) } else { __be32 dst; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_ipv4_gre_tun((void *)key, (void *)msk, rules[i]); } @@ -1006,7 +1008,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) struct nfp_ipv6_addr_entry *entry; struct in6_addr *dst; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_ipv6_udp_tun((void *)key, (void *)msk, rules[i]); } @@ -1023,7 +1025,7 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) } else { __be32 dst; - for (i = 0; i < _CT_TYPE_MAX; i++) { + for (i = 0; i < num_rules; i++) { nfp_flower_compile_ipv4_udp_tun((void *)key, (void *)msk, rules[i]); } @@ -1040,13 +1042,13 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) offset = key_map[FLOW_PAY_GENEVE_OPT]; key = kdata + offset; msk = mdata + offset; - for (i = 0; i < _CT_TYPE_MAX; i++) + for (i = 0; i < num_rules; i++) nfp_flower_compile_geneve_opt(key, msk, rules[i]); } } /* Merge actions into flow_pay */ - err = nfp_fl_merge_actions_offload(rules, priv, netdev, flow_pay); + err = nfp_fl_merge_actions_offload(rules, priv, netdev, flow_pay, num_rules); if (err) goto ct_offload_err; -- cgit v1.2.3 From a87ceb3d42afebd86191054314ef1a2f98c4556b Mon Sep 17 00:00:00 2001 From: Wentao Jia Date: Tue, 14 Mar 2023 08:36:10 +0200 Subject: nfp: flower: offload tc flows of multiple conntrack zones If goto_chain action present in the post ct flow rule, merge flow rules in this ct-zone, create a new pre_ct entry as the pre ct flow rule of next ct-zone, but do not offload merged flow rules to firmware. Repeat the process in the next ct-zone until no goto_chain action present in the post ct flow rule in a certain ct-zone, merged all the flow rules. Offload to firmware finally. Signed-off-by: Wentao Jia Acked-by: Simon Horman Signed-off-by: Louis Peens Signed-off-by: Jakub Kicinski --- .../net/ethernet/netronome/nfp/flower/conntrack.c | 145 ++++++++++++++++++--- .../net/ethernet/netronome/nfp/flower/conntrack.h | 30 ++++- .../net/ethernet/netronome/nfp/flower/offload.c | 2 +- 3 files changed, 154 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index ecffb6b0f3a1..73032173ac4e 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -522,6 +522,21 @@ static int nfp_ct_check_vlan_merge(struct flow_action_entry *a_in, return 0; } +/* Extra check for multiple ct-zones merge + * currently surpport nft entries merge check in different zones + */ +static int nfp_ct_merge_extra_check(struct nfp_fl_ct_flow_entry *nft_entry, + struct nfp_fl_ct_tc_merge *tc_m_entry) +{ + struct nfp_fl_nft_tc_merge *prev_nft_m_entry; + struct nfp_fl_ct_flow_entry *pre_ct_entry; + + pre_ct_entry = tc_m_entry->pre_ct_parent; + prev_nft_m_entry = pre_ct_entry->prev_m_entries[pre_ct_entry->num_prev_m_entries - 1]; + + return nfp_ct_merge_check(prev_nft_m_entry->nft_parent, nft_entry); +} + static int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry, struct nfp_fl_ct_flow_entry *post_ct_entry, struct nfp_fl_ct_flow_entry *nft_entry) @@ -796,27 +811,34 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry) { enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE; struct nfp_fl_ct_zone_entry *zt = m_entry->zt; + struct flow_rule *rules[NFP_MAX_ENTRY_RULES]; + struct nfp_fl_ct_flow_entry *pre_ct_entry; struct nfp_fl_key_ls key_layer, tmp_layer; struct nfp_flower_priv *priv = zt->priv; u16 key_map[_FLOW_PAY_LAYERS_MAX]; struct nfp_fl_payload *flow_pay; - - struct flow_rule *rules[_CT_TYPE_MAX]; - int num_rules = _CT_TYPE_MAX; u8 *key, *msk, *kdata, *mdata; struct nfp_port *port = NULL; + int num_rules, err, i, j = 0; struct net_device *netdev; bool qinq_sup; u32 port_id; u16 offset; - int i, err; netdev = m_entry->netdev; qinq_sup = !!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ); - rules[CT_TYPE_PRE_CT] = m_entry->tc_m_parent->pre_ct_parent->rule; - rules[CT_TYPE_NFT] = m_entry->nft_parent->rule; - rules[CT_TYPE_POST_CT] = m_entry->tc_m_parent->post_ct_parent->rule; + pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent; + num_rules = pre_ct_entry->num_prev_m_entries * 2 + _CT_TYPE_MAX; + + for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++) { + rules[j++] = pre_ct_entry->prev_m_entries[i]->tc_m_parent->pre_ct_parent->rule; + rules[j++] = pre_ct_entry->prev_m_entries[i]->nft_parent->rule; + } + + rules[j++] = m_entry->tc_m_parent->pre_ct_parent->rule; + rules[j++] = m_entry->nft_parent->rule; + rules[j++] = m_entry->tc_m_parent->post_ct_parent->rule; memset(&key_layer, 0, sizeof(struct nfp_fl_key_ls)); memset(&key_map, 0, sizeof(key_map)); @@ -1181,6 +1203,12 @@ static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt, if (err) return err; + if (pre_ct_entry->num_prev_m_entries > 0) { + err = nfp_ct_merge_extra_check(nft_entry, tc_m_entry); + if (err) + return err; + } + /* Combine tc_merge and nft cookies for this cookie. */ new_cookie[0] = tc_m_entry->cookie[0]; new_cookie[1] = tc_m_entry->cookie[1]; @@ -1211,11 +1239,6 @@ static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt, list_add(&nft_m_entry->tc_merge_list, &tc_m_entry->children); list_add(&nft_m_entry->nft_flow_list, &nft_entry->children); - /* Generate offload structure and send to nfp */ - err = nfp_fl_ct_add_offload(nft_m_entry); - if (err) - goto err_nft_ct_offload; - err = rhashtable_insert_fast(&zt->nft_merge_tb, &nft_m_entry->hash_node, nfp_nft_ct_merge_params); if (err) @@ -1223,12 +1246,20 @@ static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt, zt->nft_merge_count++; + if (post_ct_entry->goto_chain_index > 0) + return nfp_fl_create_new_pre_ct(nft_m_entry); + + /* Generate offload structure and send to nfp */ + err = nfp_fl_ct_add_offload(nft_m_entry); + if (err) + goto err_nft_ct_offload; + return err; -err_nft_ct_merge_insert: +err_nft_ct_offload: nfp_fl_ct_del_offload(zt->priv->app, nft_m_entry->tc_flower_cookie, nft_m_entry->netdev); -err_nft_ct_offload: +err_nft_ct_merge_insert: list_del(&nft_m_entry->tc_merge_list); list_del(&nft_m_entry->nft_flow_list); kfree(nft_m_entry); @@ -1474,7 +1505,7 @@ nfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt, entry->zt = zt; entry->netdev = netdev; - entry->cookie = flow->cookie; + entry->cookie = flow->cookie > 0 ? flow->cookie : (unsigned long)entry; entry->chain_index = flow->common.chain_index; entry->tun_offset = NFP_FL_CT_NO_TUN; @@ -1514,6 +1545,9 @@ nfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt, INIT_LIST_HEAD(&entry->children); + if (flow->cookie == 0) + return entry; + /* Now add a ct map entry to flower-priv */ map = get_hashentry(&zt->priv->ct_map_table, &flow->cookie, nfp_ct_map_params, sizeof(*map)); @@ -1572,6 +1606,14 @@ static void cleanup_nft_merge_entry(struct nfp_fl_nft_tc_merge *m_entry) list_del(&m_entry->tc_merge_list); list_del(&m_entry->nft_flow_list); + if (m_entry->next_pre_ct_entry) { + struct nfp_fl_ct_map_entry pre_ct_map_ent; + + pre_ct_map_ent.ct_entry = m_entry->next_pre_ct_entry; + pre_ct_map_ent.cookie = 0; + nfp_fl_ct_del_flow(&pre_ct_map_ent); + } + kfree(m_entry); } @@ -1742,7 +1784,8 @@ nfp_ct_merge_nft_with_tc(struct nfp_fl_ct_flow_entry *nft_entry, int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv, struct net_device *netdev, struct flow_cls_offload *flow, - struct netlink_ext_ack *extack) + struct netlink_ext_ack *extack, + struct nfp_fl_nft_tc_merge *m_entry) { struct flow_action_entry *ct_act, *ct_goto; struct nfp_fl_ct_flow_entry *ct_entry; @@ -1787,6 +1830,20 @@ int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv, ct_entry->type = CT_TYPE_PRE_CT; ct_entry->chain_index = flow->common.chain_index; ct_entry->goto_chain_index = ct_goto->chain_index; + + if (m_entry) { + struct nfp_fl_ct_flow_entry *pre_ct_entry; + int i; + + pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent; + for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++) + ct_entry->prev_m_entries[i] = pre_ct_entry->prev_m_entries[i]; + ct_entry->prev_m_entries[i++] = m_entry; + ct_entry->num_prev_m_entries = i; + + m_entry->next_pre_ct_entry = ct_entry; + } + list_add(&ct_entry->list_node, &zt->pre_ct_list); zt->pre_ct_count++; @@ -1864,6 +1921,28 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv, return 0; } +int nfp_fl_create_new_pre_ct(struct nfp_fl_nft_tc_merge *m_entry) +{ + struct nfp_fl_ct_flow_entry *pre_ct_entry, *post_ct_entry; + struct flow_cls_offload new_pre_ct_flow; + int err; + + pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent; + if (pre_ct_entry->num_prev_m_entries >= NFP_MAX_RECIRC_CT_ZONES - 1) + return -1; + + post_ct_entry = m_entry->tc_m_parent->post_ct_parent; + memset(&new_pre_ct_flow, 0, sizeof(struct flow_cls_offload)); + new_pre_ct_flow.rule = post_ct_entry->rule; + new_pre_ct_flow.common.chain_index = post_ct_entry->chain_index; + + err = nfp_fl_ct_handle_pre_ct(pre_ct_entry->zt->priv, + pre_ct_entry->netdev, + &new_pre_ct_flow, NULL, + m_entry); + return err; +} + static void nfp_fl_ct_sub_stats(struct nfp_fl_nft_tc_merge *nft_merge, enum ct_entry_type type, u64 *m_pkts, @@ -1909,6 +1988,32 @@ nfp_fl_ct_sub_stats(struct nfp_fl_nft_tc_merge *nft_merge, 0, priv->stats[ctx_id].used, FLOW_ACTION_HW_STATS_DELAYED); } + + /* Update previous pre_ct/post_ct/nft flow stats */ + if (nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries > 0) { + struct nfp_fl_nft_tc_merge *tmp_nft_merge; + int i; + + for (i = 0; i < nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries; i++) { + tmp_nft_merge = nft_merge->tc_m_parent->pre_ct_parent->prev_m_entries[i]; + flow_stats_update(&tmp_nft_merge->tc_m_parent->pre_ct_parent->stats, + priv->stats[ctx_id].bytes, + priv->stats[ctx_id].pkts, + 0, priv->stats[ctx_id].used, + FLOW_ACTION_HW_STATS_DELAYED); + flow_stats_update(&tmp_nft_merge->tc_m_parent->post_ct_parent->stats, + priv->stats[ctx_id].bytes, + priv->stats[ctx_id].pkts, + 0, priv->stats[ctx_id].used, + FLOW_ACTION_HW_STATS_DELAYED); + flow_stats_update(&tmp_nft_merge->nft_parent->stats, + priv->stats[ctx_id].bytes, + priv->stats[ctx_id].pkts, + 0, priv->stats[ctx_id].used, + FLOW_ACTION_HW_STATS_DELAYED); + } + } + /* Reset stats from the nfp */ priv->stats[ctx_id].pkts = 0; priv->stats[ctx_id].bytes = 0; @@ -2113,10 +2218,12 @@ int nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry *ct_map_ent) switch (ct_entry->type) { case CT_TYPE_PRE_CT: zt->pre_ct_count--; - rhashtable_remove_fast(m_table, &ct_map_ent->hash_node, - nfp_ct_map_params); + if (ct_map_ent->cookie > 0) + rhashtable_remove_fast(m_table, &ct_map_ent->hash_node, + nfp_ct_map_params); nfp_fl_ct_clean_flow_entry(ct_entry); - kfree(ct_map_ent); + if (ct_map_ent->cookie > 0) + kfree(ct_map_ent); if (!zt->pre_ct_count) { zt->nft = NULL; diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.h b/drivers/net/ethernet/netronome/nfp/flower/conntrack.h index 9440ab776ece..c4ec78358033 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.h +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.h @@ -86,6 +86,9 @@ enum ct_entry_type { _CT_TYPE_MAX, }; +#define NFP_MAX_RECIRC_CT_ZONES 4 +#define NFP_MAX_ENTRY_RULES (NFP_MAX_RECIRC_CT_ZONES * 2 + 1) + enum nfp_nfp_layer_name { FLOW_PAY_META_TCI = 0, FLOW_PAY_INPORT, @@ -114,27 +117,31 @@ enum nfp_nfp_layer_name { * @chain_index: Chain index of the original flow * @goto_chain_index: goto chain index of the flow * @netdev: netdev structure. - * @type: Type of pre-entry from enum ct_entry_type * @zt: Reference to the zone table this belongs to * @children: List of tc_merge flows this flow forms part of * @rule: Reference to the original TC flow rule * @stats: Used to cache stats for updating + * @prev_m_entries: Array of all previous nft_tc_merge entries + * @num_prev_m_entries: The number of all previous nft_tc_merge entries * @tun_offset: Used to indicate tunnel action offset in action list * @flags: Used to indicate flow flag like NAT which used by merge. + * @type: Type of ct-entry from enum ct_entry_type */ struct nfp_fl_ct_flow_entry { unsigned long cookie; struct list_head list_node; u32 chain_index; u32 goto_chain_index; - enum ct_entry_type type; struct net_device *netdev; struct nfp_fl_ct_zone_entry *zt; struct list_head children; struct flow_rule *rule; struct flow_stats stats; + struct nfp_fl_nft_tc_merge *prev_m_entries[NFP_MAX_RECIRC_CT_ZONES - 1]; + u8 num_prev_m_entries; u8 tun_offset; // Set to NFP_FL_CT_NO_TUN if no tun u8 flags; + u8 type; }; /** @@ -171,6 +178,7 @@ struct nfp_fl_ct_tc_merge { * @nft_parent: The nft_entry parent * @tc_flower_cookie: The cookie of the flow offloaded to the nfp * @flow_pay: Reference to the offloaded flow struct + * @next_pre_ct_entry: Reference to the next ct zone pre ct entry */ struct nfp_fl_nft_tc_merge { struct net_device *netdev; @@ -183,6 +191,7 @@ struct nfp_fl_nft_tc_merge { struct nfp_fl_ct_flow_entry *nft_parent; unsigned long tc_flower_cookie; struct nfp_fl_payload *flow_pay; + struct nfp_fl_ct_flow_entry *next_pre_ct_entry; }; /** @@ -206,6 +215,7 @@ bool is_post_ct_flow(struct flow_cls_offload *flow); * @netdev: netdev structure. * @flow: TC flower classifier offload structure. * @extack: Extack pointer for errors + * @m_entry:previous nfp_fl_nft_tc_merge entry * * Adds a new entry to the relevant zone table and tries to * merge with other +trk+est entries and offload if possible. @@ -215,7 +225,8 @@ bool is_post_ct_flow(struct flow_cls_offload *flow); int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv, struct net_device *netdev, struct flow_cls_offload *flow, - struct netlink_ext_ack *extack); + struct netlink_ext_ack *extack, + struct nfp_fl_nft_tc_merge *m_entry); /** * nfp_fl_ct_handle_post_ct() - Handles +trk+est conntrack rules * @priv: Pointer to app priv @@ -233,6 +244,19 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv, struct flow_cls_offload *flow, struct netlink_ext_ack *extack); +/** + * nfp_fl_create_new_pre_ct() - create next ct_zone -trk conntrack rules + * @m_entry:previous nfp_fl_nft_tc_merge entry + * + * Create a new pre_ct entry from previous nfp_fl_nft_tc_merge entry + * to the next relevant zone table. Try to merge with other +trk+est + * entries and offload if possible. The created new pre_ct entry is + * linked to the previous nfp_fl_nft_tc_merge entry. + * + * Return: negative value on error, 0 if configured successfully. + */ +int nfp_fl_create_new_pre_ct(struct nfp_fl_nft_tc_merge *m_entry); + /** * nfp_fl_ct_clean_flow_entry() - Free a nfp_fl_ct_flow_entry * @entry: Flow entry to cleanup diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 8593cafa6368..18328eb7f5c3 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -1344,7 +1344,7 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev, port = nfp_port_from_netdev(netdev); if (is_pre_ct_flow(flow)) - return nfp_fl_ct_handle_pre_ct(priv, netdev, flow, extack); + return nfp_fl_ct_handle_pre_ct(priv, netdev, flow, extack, NULL); if (is_post_ct_flow(flow)) return nfp_fl_ct_handle_post_ct(priv, netdev, flow, extack); -- cgit v1.2.3 From 9bdf4489a3950335af088604876fdc3209a9dd8e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 14 Mar 2023 08:23:15 +0000 Subject: net: phy: micrel: Fix spelling mistake "minimim" -> "minimum" There is a spelling mistake in a pr_warn_ratelimited message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20230314082315.26532-1-colin.i.king@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/micrel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 0cf0ef3a96a3..e26c6723caa4 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -4106,7 +4106,7 @@ static int lan8841_ptp_perout(struct ptp_clock_info *ptp, period_nsec = timespec64_to_ns(&ts_period); if (period_nsec < 200) { - pr_warn_ratelimited("%s: perout period too small, minimim is 200 nsec\n", + pr_warn_ratelimited("%s: perout period too small, minimum is 200 nsec\n", phydev_name(phydev)); return -EOPNOTSUPP; } -- cgit v1.2.3 From 499183cc3b52613f06cf4ce70809546971c96ed8 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 15 Mar 2023 20:28:29 -0700 Subject: wifi: iwlwifi: Avoid disabling GCC specific flag with clang Clang errors: drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c:15:32: error: unknown warning group '-Wsuggest-attribute=format', ignored [-Werror,-Wunknown-warning-option] #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" ^ 1 error generated. The warning being disabled by this pragma is GCC specific. Guard its use with CONFIG_CC_IS_GCC so that it is not used with clang to clear up the error. Fixes: 4eca8cbf7ba8 ("wifi: iwlwifi: suppress printf warnings in tracing") Link: https://github.com/ClangBuiltLinux/linux/issues/1818 Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20230315-iwlwifi-fix-pragma-v1-1-ad23f92c4739@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c index c190ec5effa1..e46639b097f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c @@ -12,7 +12,9 @@ #include "iwl-trans.h" #define CREATE_TRACE_POINTS +#ifdef CONFIG_CC_IS_GCC #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" +#endif #include "iwl-devtrace.h" EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); -- cgit v1.2.3 From 0ba13995be9b416ea1d3daaf3ba871a67f45899b Mon Sep 17 00:00:00 2001 From: Xu Liang Date: Wed, 15 Mar 2023 00:30:23 +0800 Subject: net: phy: mxl-gpy: enhance delay time required by loopback disable function GPY2xx devices need 3 seconds to fully switch out of loopback mode before it can safely re-enter loopback mode. Implement timeout mechanism to guarantee 3 seconds waited before re-enter loopback mode. Signed-off-by: Xu Liang Signed-off-by: David S. Miller --- drivers/net/phy/mxl-gpy.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c index e5972b4ef6e8..8e6bb97b5f85 100644 --- a/drivers/net/phy/mxl-gpy.c +++ b/drivers/net/phy/mxl-gpy.c @@ -107,6 +107,13 @@ struct gpy_priv { u8 fw_major; u8 fw_minor; + + /* It takes 3 seconds to fully switch out of loopback mode before + * it can safely re-enter loopback mode. Record the time when + * loopback is disabled. Check and wait if necessary before loopback + * is enabled. + */ + u64 lb_dis_to; }; static const struct { @@ -769,18 +776,34 @@ static void gpy_get_wol(struct phy_device *phydev, static int gpy_loopback(struct phy_device *phydev, bool enable) { + struct gpy_priv *priv = phydev->priv; + u16 set = 0; int ret; - ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, - enable ? BMCR_LOOPBACK : 0); - if (!ret) { - /* It takes some time for PHY device to switch - * into/out-of loopback mode. + if (enable) { + u64 now = get_jiffies_64(); + + /* wait until 3 seconds from last disable */ + if (time_before64(now, priv->lb_dis_to)) + msleep(jiffies64_to_msecs(priv->lb_dis_to - now)); + + set = BMCR_LOOPBACK; + } + + ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, set); + if (ret <= 0) + return ret; + + if (enable) { + /* It takes some time for PHY device to switch into + * loopback mode. */ msleep(100); + } else { + priv->lb_dis_to = get_jiffies_64() + HZ * 3; } - return ret; + return 0; } static int gpy115_loopback(struct phy_device *phydev, bool enable) -- cgit v1.2.3 From 053fdaa841bd1af9fe9c2c30bba81119059aac95 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 14 Mar 2023 15:13:08 -0500 Subject: nfc: mrvl: Move platform_data struct into driver There are no users of nfcmrvl platform_data struct outside of the driver and none will be added, so move it into the driver. Signed-off-by: Rob Herring Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/nfc/nfcmrvl/nfcmrvl.h | 30 ++++++++++++++++++++-- include/linux/platform_data/nfcmrvl.h | 48 ----------------------------------- 2 files changed, 28 insertions(+), 50 deletions(-) delete mode 100644 include/linux/platform_data/nfcmrvl.h (limited to 'drivers') diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index 165bd0a95190..0f22b3233f73 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -8,8 +8,6 @@ #ifndef _NFCMRVL_H_ #define _NFCMRVL_H_ -#include - #include "fw_dnld.h" /* Define private flags: */ @@ -50,6 +48,34 @@ enum nfcmrvl_phy { NFCMRVL_PHY_SPI = 3, }; +struct nfcmrvl_platform_data { + /* + * Generic + */ + + /* GPIO that is wired to RESET_N signal */ + int reset_n_io; + /* Tell if transport is muxed in HCI one */ + unsigned int hci_muxed; + + /* + * UART specific + */ + + /* Tell if UART needs flow control at init */ + unsigned int flow_control; + /* Tell if firmware supports break control for power management */ + unsigned int break_control; + + + /* + * I2C specific + */ + + unsigned int irq; + unsigned int irq_polarity; +}; + struct nfcmrvl_private { unsigned long flags; diff --git a/include/linux/platform_data/nfcmrvl.h b/include/linux/platform_data/nfcmrvl.h deleted file mode 100644 index 9e75ac8d19be..000000000000 --- a/include/linux/platform_data/nfcmrvl.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2015, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available on the worldwide web at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _NFCMRVL_PTF_H_ -#define _NFCMRVL_PTF_H_ - -struct nfcmrvl_platform_data { - /* - * Generic - */ - - /* GPIO that is wired to RESET_N signal */ - int reset_n_io; - /* Tell if transport is muxed in HCI one */ - unsigned int hci_muxed; - - /* - * UART specific - */ - - /* Tell if UART needs flow control at init */ - unsigned int flow_control; - /* Tell if firmware supports break control for power management */ - unsigned int break_control; - - - /* - * I2C specific - */ - - unsigned int irq; - unsigned int irq_polarity; -}; - -#endif /* _NFCMRVL_PTF_H_ */ -- cgit v1.2.3 From cc6d85c1cb5ccc04c594392339c81227abd1667d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 14 Mar 2023 15:13:09 -0500 Subject: nfc: mrvl: Use of_property_read_bool() for boolean properties It is preferred to use typed property access functions (i.e. of_property_read_ functions) rather than low-level of_get_property/of_find_property functions for reading properties. Convert reading boolean properties to of_property_read_bool(). Reviewed-by: Simon Horman Reviewed-by: Krzysztof Kozlowski Signed-off-by: Rob Herring Signed-off-by: David S. Miller --- drivers/nfc/nfcmrvl/i2c.c | 2 +- drivers/nfc/nfcmrvl/main.c | 6 +----- drivers/nfc/nfcmrvl/nfcmrvl.h | 6 +++--- drivers/nfc/nfcmrvl/uart.c | 11 ++--------- 4 files changed, 7 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c index e74342b0b728..164e2ab859fd 100644 --- a/drivers/nfc/nfcmrvl/i2c.c +++ b/drivers/nfc/nfcmrvl/i2c.c @@ -168,7 +168,7 @@ static int nfcmrvl_i2c_parse_dt(struct device_node *node, return ret; } - if (of_find_property(node, "i2c-int-falling", NULL)) + if (of_property_read_bool(node, "i2c-int-falling")) pdata->irq_polarity = IRQF_TRIGGER_FALLING; else pdata->irq_polarity = IRQF_TRIGGER_RISING; diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 1a5284de4341..141bc4b66dcb 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -261,11 +261,7 @@ int nfcmrvl_parse_dt(struct device_node *node, return reset_n_io; } pdata->reset_n_io = reset_n_io; - - if (of_find_property(node, "hci-muxed", NULL)) - pdata->hci_muxed = 1; - else - pdata->hci_muxed = 0; + pdata->hci_muxed = of_property_read_bool(node, "hci-muxed"); return 0; } diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index 0f22b3233f73..f61a99e553db 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h @@ -56,16 +56,16 @@ struct nfcmrvl_platform_data { /* GPIO that is wired to RESET_N signal */ int reset_n_io; /* Tell if transport is muxed in HCI one */ - unsigned int hci_muxed; + bool hci_muxed; /* * UART specific */ /* Tell if UART needs flow control at init */ - unsigned int flow_control; + bool flow_control; /* Tell if firmware supports break control for power management */ - unsigned int break_control; + bool break_control; /* diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c index 9c92cbdc42f0..956ae92f7573 100644 --- a/drivers/nfc/nfcmrvl/uart.c +++ b/drivers/nfc/nfcmrvl/uart.c @@ -76,15 +76,8 @@ static int nfcmrvl_uart_parse_dt(struct device_node *node, return ret; } - if (of_find_property(matched_node, "flow-control", NULL)) - pdata->flow_control = 1; - else - pdata->flow_control = 0; - - if (of_find_property(matched_node, "break-control", NULL)) - pdata->break_control = 1; - else - pdata->break_control = 0; + pdata->flow_control = of_property_read_bool(matched_node, "flow-control"); + pdata->break_control = of_property_read_bool(matched_node, "break-control"); of_node_put(matched_node); -- cgit v1.2.3 From d565263b7d83371e64ef315bdc3428909808b46f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 14 Mar 2023 20:18:24 +0200 Subject: net: dsa: hellcreek: Get rid of custom led_init_default_state_get() LED core provides a helper to parse default state from firmware node. Use it instead of custom implementation. Signed-off-by: Andy Shevchenko Reviewed-by: Kurt Kanzenbach Reviewed-by: Michal Swiatkowski Link: https://lore.kernel.org/r/20230314181824.56881-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/hirschmann/hellcreek_ptp.c | 45 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c index b28baab6d56a..3e44ccb7db84 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_ptp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c @@ -297,7 +297,8 @@ static enum led_brightness hellcreek_led_is_gm_get(struct led_classdev *ldev) static int hellcreek_led_setup(struct hellcreek *hellcreek) { struct device_node *leds, *led = NULL; - const char *label, *state; + enum led_default_state state; + const char *label; int ret = -EINVAL; of_node_get(hellcreek->dev->of_node); @@ -318,16 +319,17 @@ static int hellcreek_led_setup(struct hellcreek *hellcreek) ret = of_property_read_string(led, "label", &label); hellcreek->led_sync_good.name = ret ? "sync_good" : label; - ret = of_property_read_string(led, "default-state", &state); - if (!ret) { - if (!strcmp(state, "on")) - hellcreek->led_sync_good.brightness = 1; - else if (!strcmp(state, "off")) - hellcreek->led_sync_good.brightness = 0; - else if (!strcmp(state, "keep")) - hellcreek->led_sync_good.brightness = - hellcreek_get_brightness(hellcreek, - STATUS_OUT_SYNC_GOOD); + state = led_init_default_state_get(of_fwnode_handle(led)); + switch (state) { + case LEDS_DEFSTATE_ON: + hellcreek->led_sync_good.brightness = 1; + break; + case LEDS_DEFSTATE_KEEP: + hellcreek->led_sync_good.brightness = + hellcreek_get_brightness(hellcreek, STATUS_OUT_SYNC_GOOD); + break; + default: + hellcreek->led_sync_good.brightness = 0; } hellcreek->led_sync_good.max_brightness = 1; @@ -344,16 +346,17 @@ static int hellcreek_led_setup(struct hellcreek *hellcreek) ret = of_property_read_string(led, "label", &label); hellcreek->led_is_gm.name = ret ? "is_gm" : label; - ret = of_property_read_string(led, "default-state", &state); - if (!ret) { - if (!strcmp(state, "on")) - hellcreek->led_is_gm.brightness = 1; - else if (!strcmp(state, "off")) - hellcreek->led_is_gm.brightness = 0; - else if (!strcmp(state, "keep")) - hellcreek->led_is_gm.brightness = - hellcreek_get_brightness(hellcreek, - STATUS_OUT_IS_GM); + state = led_init_default_state_get(of_fwnode_handle(led)); + switch (state) { + case LEDS_DEFSTATE_ON: + hellcreek->led_is_gm.brightness = 1; + break; + case LEDS_DEFSTATE_KEEP: + hellcreek->led_is_gm.brightness = + hellcreek_get_brightness(hellcreek, STATUS_OUT_IS_GM); + break; + default: + hellcreek->led_is_gm.brightness = 0; } hellcreek->led_is_gm.max_brightness = 1; -- cgit v1.2.3 From abc783a7b0ff34d20a799d2fbf0ed0b2b06b72ed Mon Sep 17 00:00:00 2001 From: Durai Manickam KR Date: Wed, 15 Mar 2023 15:20:52 +0530 Subject: net: macb: Add PTP support to GEM for sama7g5 Add PTP capability to the Gigabit Ethernet MAC. Signed-off-by: Durai Manickam KR Reviewed-by: Claudiu Beznea Reviewed-by: Michal Swiatkowski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 6e141a8bbf43..27fc6c903d25 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4844,7 +4844,7 @@ static const struct macb_config mpfs_config = { static const struct macb_config sama7g5_gem_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | - MACB_CAPS_MIIONRGMII, + MACB_CAPS_MIIONRGMII | MACB_CAPS_GEM_HAS_PTP, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, -- cgit v1.2.3 From 9bae0dd05e61009bf45f4a6a8fbde87ed9567166 Mon Sep 17 00:00:00 2001 From: Durai Manickam KR Date: Wed, 15 Mar 2023 15:20:53 +0530 Subject: net: macb: Add PTP support to EMAC for sama7g5 Add PTP capability to the Ethernet MAC. Signed-off-by: Durai Manickam KR Reviewed-by: Claudiu Beznea Reviewed-by: Michal Swiatkowski Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 27fc6c903d25..1dbee16fe90a 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4853,7 +4853,8 @@ static const struct macb_config sama7g5_gem_config = { static const struct macb_config sama7g5_emac_config = { .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | - MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_MIIONRGMII, + MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_MIIONRGMII | + MACB_CAPS_GEM_HAS_PTP, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, -- cgit v1.2.3 From c36a77c33db36277ca33b6a2fa5f802ba93694f7 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Wed, 15 Mar 2023 00:49:22 -0300 Subject: net: dsa: realtek: rtl8365mb: add change_mtu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rtl8365mb was using a fixed MTU size of 1536, which was probably inspired by the rtl8366rb's initial frame size. However, unlike that family, the rtl8365mb family can specify the max frame size in bytes, rather than in fixed steps. DSA calls change_mtu for the CPU port once the max MTU value among the ports changes. As the max frame size is defined globally, the switch is configured only when the call affects the CPU port. The available specifications do not directly define the max supported frame size, but it mentions a 16k limit. This driver will use the 0x3FFF limit as it is used in the vendor API code. However, the switch sets the max frame size to 16368 bytes (0x3FF0) after it resets. change_mtu uses MTU size, or ethernet payload size, while the switch works with frame size. The frame size is calculated considering the ethernet header (14 bytes), a possible 802.1Q tag (4 bytes), the payload size (MTU), and the Ethernet FCS (4 bytes). The CPU tag (8 bytes) is consumed before the switch enforces the limit. During setup, the driver will use the default 1500-byte MTU of DSA to set the maximum frame size. The current sum will be VLAN_ETH_HLEN+1500+ETH_FCS_LEN, which results in 1522 bytes. Although it is lower than the previous initial value of 1536 bytes, the driver will increase the frame size for a larger MTU. However, if something requires more space without increasing the MTU, such as QinQ, we would need to add the extra length to the rtl8365mb_port_change_mtu() formula. MTU was tested up to 2018 (with 802.1Q) as that is as far as mt7620 (where rtl8367s is stacked) can go. The register was manually manipulated byte-by-byte to ensure the MTU to frame size conversion was correct. For frames without 802.1Q tag, the frame size limit will be 4 bytes over the required size. There is a jumbo register, enabled by default at 6k frame size. However, the jumbo settings do not seem to limit nor expand the maximum tested MTU (2018), even when jumbo is disabled. More tests are needed with a device that can handle larger frames. Signed-off-by: Luiz Angelo Daros de Luca Reviewed-by: Alexander Duyck Reviewed-by: Alvin Šipraga Signed-off-by: David S. Miller --- drivers/net/dsa/realtek/rtl8365mb.c | 40 +++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index da31d8b839ac..41ea3b5a42b1 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -98,6 +98,7 @@ #include #include #include +#include #include "realtek.h" @@ -267,6 +268,7 @@ /* Maximum packet length register */ #define RTL8365MB_CFG0_MAX_LEN_REG 0x088C #define RTL8365MB_CFG0_MAX_LEN_MASK 0x3FFF +#define RTL8365MB_CFG0_MAX_LEN_MAX 0x3FFF /* Port learning limit registers */ #define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE 0x0A20 @@ -1135,6 +1137,35 @@ static void rtl8365mb_phylink_mac_link_up(struct dsa_switch *ds, int port, } } +static int rtl8365mb_port_change_mtu(struct dsa_switch *ds, int port, + int new_mtu) +{ + struct realtek_priv *priv = ds->priv; + int frame_size; + + /* When a new MTU is set, DSA always sets the CPU port's MTU to the + * largest MTU of the slave ports. Because the switch only has a global + * RX length register, only allowing CPU port here is enough. + */ + if (!dsa_is_cpu_port(ds, port)) + return 0; + + frame_size = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + + dev_dbg(priv->dev, "changing mtu to %d (frame size: %d)\n", + new_mtu, frame_size); + + return regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, + RTL8365MB_CFG0_MAX_LEN_MASK, + FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, + frame_size)); +} + +static int rtl8365mb_port_max_mtu(struct dsa_switch *ds, int port) +{ + return RTL8365MB_CFG0_MAX_LEN_MAX - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { @@ -1980,10 +2011,7 @@ static int rtl8365mb_setup(struct dsa_switch *ds) p->index = i; } - /* Set maximum packet length to 1536 bytes */ - ret = regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, - RTL8365MB_CFG0_MAX_LEN_MASK, - FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 1536)); + ret = rtl8365mb_port_change_mtu(ds, cpu->trap_port, ETH_DATA_LEN); if (ret) goto out_teardown_irq; @@ -2103,6 +2131,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { .get_eth_mac_stats = rtl8365mb_get_mac_stats, .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, .get_stats64 = rtl8365mb_get_stats64, + .port_change_mtu = rtl8365mb_port_change_mtu, + .port_max_mtu = rtl8365mb_port_max_mtu, }; static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { @@ -2124,6 +2154,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { .get_eth_mac_stats = rtl8365mb_get_mac_stats, .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, .get_stats64 = rtl8365mb_get_stats64, + .port_change_mtu = rtl8365mb_port_change_mtu, + .port_max_mtu = rtl8365mb_port_max_mtu, }; static const struct realtek_ops rtl8365mb_ops = { -- cgit v1.2.3 From 81dc07417f0f82cd8f9733a1a3ebc2a473423e86 Mon Sep 17 00:00:00 2001 From: Mengyuan Lou Date: Wed, 15 Mar 2023 15:43:04 +0800 Subject: net: wangxun: Implement the ndo change mtu interface Add ngbe and txgbe ndo_change_mtu support. Signed-off-by: Mengyuan Lou Signed-off-by: David S. Miller --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 21 ++++++++++++++++++++- drivers/net/ethernet/wangxun/libwx/wx_hw.h | 1 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 2 ++ drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 5 ++++- drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 1 - drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 5 ++++- drivers/net/ethernet/wangxun/txgbe/txgbe_type.h | 1 - 7 files changed, 31 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 7db57f934a91..ca409b4054d0 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -1261,7 +1262,7 @@ static void wx_set_rx_buffer_len(struct wx *wx) struct net_device *netdev = wx->netdev; u32 mhadd, max_frame; - max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN; + max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; /* adjust max frame to be at least the size of a standard frame */ if (max_frame < (ETH_FRAME_LEN + ETH_FCS_LEN)) max_frame = (ETH_FRAME_LEN + ETH_FCS_LEN); @@ -1271,6 +1272,24 @@ static void wx_set_rx_buffer_len(struct wx *wx) wr32(wx, WX_PSR_MAX_SZ, max_frame); } +/** + * wx_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + **/ +int wx_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct wx *wx = netdev_priv(netdev); + + netdev->mtu = new_mtu; + wx_set_rx_buffer_len(wx); + + return 0; +} +EXPORT_SYMBOL(wx_change_mtu); + /* Disable the specified rx queue */ void wx_disable_rx_queue(struct wx *wx, struct wx_ring *ring) { diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h index 44dfd6ea442a..c173c56f0ab5 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h @@ -23,6 +23,7 @@ void wx_flush_sw_mac_table(struct wx *wx); int wx_set_mac(struct net_device *netdev, void *p); void wx_disable_rx(struct wx *wx); void wx_set_rx_mode(struct net_device *netdev); +int wx_change_mtu(struct net_device *netdev, int new_mtu); void wx_disable_rx_queue(struct wx *wx, struct wx_ring *ring); void wx_configure(struct wx *wx); int wx_disable_pcie_master(struct wx *wx); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 77d8d7f1707e..2b9efd13c500 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -300,6 +300,8 @@ #define WX_MAX_RXD 8192 #define WX_MAX_TXD 8192 +#define WX_MAX_JUMBO_FRAME_SIZE 9432 /* max payload 9414 */ + /* Supported Rx Buffer Sizes */ #define WX_RXBUFFER_256 256 /* Used for skb receive header */ #define WX_RXBUFFER_2K 2048 diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 0e4163e1106f..1a004aa2adcb 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "../libwx/wx_type.h" #include "../libwx/wx_hw.h" @@ -469,6 +470,7 @@ static void ngbe_shutdown(struct pci_dev *pdev) static const struct net_device_ops ngbe_netdev_ops = { .ndo_open = ngbe_open, .ndo_stop = ngbe_close, + .ndo_change_mtu = wx_change_mtu, .ndo_start_xmit = wx_xmit_frame, .ndo_set_rx_mode = wx_set_rx_mode, .ndo_validate_addr = eth_validate_addr, @@ -560,7 +562,8 @@ static int ngbe_probe(struct pci_dev *pdev, netdev->priv_flags |= IFF_SUPP_NOFCS; netdev->min_mtu = ETH_MIN_MTU; - netdev->max_mtu = NGBE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); + netdev->max_mtu = WX_MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); wx->bd_number = func_nums; /* setup the private structure */ diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index a2351349785e..373d5af628cd 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -137,7 +137,6 @@ enum NGBE_MSCA_CMD_value { #define NGBE_RX_PB_SIZE 42 #define NGBE_MC_TBL_SIZE 128 #define NGBE_TDB_PB_SZ (20 * 1024) /* 160KB Packet Buffer */ -#define NGBE_MAX_JUMBO_FRAME_SIZE 9432 /* max payload 9414 */ /* TX/RX descriptor defines */ #define NGBE_DEFAULT_TXD 512 /* default ring size */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 859feaafd350..843a88bc416f 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "../libwx/wx_type.h" #include "../libwx/wx_lib.h" @@ -486,6 +487,7 @@ static void txgbe_shutdown(struct pci_dev *pdev) static const struct net_device_ops txgbe_netdev_ops = { .ndo_open = txgbe_open, .ndo_stop = txgbe_close, + .ndo_change_mtu = wx_change_mtu, .ndo_start_xmit = wx_xmit_frame, .ndo_set_rx_mode = wx_set_rx_mode, .ndo_validate_addr = eth_validate_addr, @@ -603,7 +605,8 @@ static int txgbe_probe(struct pci_dev *pdev, netdev->priv_flags |= IFF_SUPP_NOFCS; netdev->min_mtu = ETH_MIN_MTU; - netdev->max_mtu = TXGBE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); + netdev->max_mtu = WX_MAX_JUMBO_FRAME_SIZE - + (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); /* make sure the EEPROM is good */ err = txgbe_validate_eeprom_checksum(wx, NULL); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 563ea51deca6..63a1c733718d 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -79,7 +79,6 @@ #define TXGBE_SP_MC_TBL_SIZE 128 #define TXGBE_SP_RX_PB_SIZE 512 #define TXGBE_SP_TDB_PB_SZ (160 * 1024) /* 160KB Packet Buffer */ -#define TXGBE_MAX_JUMBO_FRAME_SIZE 9432 /* max payload 9414 */ /* TX/RX descriptor defines */ #define TXGBE_DEFAULT_TXD 512 -- cgit v1.2.3 From bd7fc6e1957c2102866f9e464c1f2302e891b7e9 Mon Sep 17 00:00:00 2001 From: Shradha Gupta Date: Wed, 15 Mar 2023 04:55:13 -0700 Subject: net: mana: Add new MANA VF performance counters for easier troubleshooting Extended performance counter stats in 'ethtool -S ' output for MANA VF to facilitate troubleshooting. Tested-on: Ubuntu22 Signed-off-by: Shradha Gupta Signed-off-by: David S. Miller --- drivers/net/ethernet/microsoft/mana/mana_en.c | 62 ++++++++++++++++++++-- drivers/net/ethernet/microsoft/mana/mana_ethtool.c | 52 +++++++++++++++++- include/net/mana/mana.h | 18 +++++++ 3 files changed, 128 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 6120f2b6684f..492474b4d8aa 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -156,6 +156,7 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct mana_txq *txq; struct mana_cq *cq; int err, len; + u16 ihs; if (unlikely(!apc->port_is_up)) goto tx_drop; @@ -166,6 +167,7 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) txq = &apc->tx_qp[txq_idx].txq; gdma_sq = txq->gdma_sq; cq = &apc->tx_qp[txq_idx].tx_cq; + tx_stats = &txq->stats; pkg.tx_oob.s_oob.vcq_num = cq->gdma_id; pkg.tx_oob.s_oob.vsq_frame = txq->vsq_frame; @@ -179,10 +181,17 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) pkg.tx_oob.s_oob.pkt_fmt = pkt_fmt; - if (pkt_fmt == MANA_SHORT_PKT_FMT) + if (pkt_fmt == MANA_SHORT_PKT_FMT) { pkg.wqe_req.inline_oob_size = sizeof(struct mana_tx_short_oob); - else + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->short_pkt_fmt++; + u64_stats_update_end(&tx_stats->syncp); + } else { pkg.wqe_req.inline_oob_size = sizeof(struct mana_tx_oob); + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->long_pkt_fmt++; + u64_stats_update_end(&tx_stats->syncp); + } pkg.wqe_req.inline_oob_data = &pkg.tx_oob; pkg.wqe_req.flags = 0; @@ -232,9 +241,35 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); } + + if (skb->encapsulation) { + ihs = skb_inner_tcp_all_headers(skb); + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->tso_inner_packets++; + tx_stats->tso_inner_bytes += skb->len - ihs; + u64_stats_update_end(&tx_stats->syncp); + } else { + if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) { + ihs = skb_transport_offset(skb) + sizeof(struct udphdr); + } else { + ihs = skb_tcp_all_headers(skb); + if (ipv6_has_hopopt_jumbo(skb)) + ihs -= sizeof(struct hop_jumbo_hdr); + } + + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->tso_packets++; + tx_stats->tso_bytes += skb->len - ihs; + u64_stats_update_end(&tx_stats->syncp); + } + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { csum_type = mana_checksum_info(skb); + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->csum_partial++; + u64_stats_update_end(&tx_stats->syncp); + if (csum_type == IPPROTO_TCP) { pkg.tx_oob.s_oob.is_outer_ipv4 = ipv4; pkg.tx_oob.s_oob.is_outer_ipv6 = ipv6; @@ -254,8 +289,12 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) } } - if (mana_map_skb(skb, apc, &pkg)) + if (mana_map_skb(skb, apc, &pkg)) { + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->mana_map_err++; + u64_stats_update_end(&tx_stats->syncp); goto free_sgl_ptr; + } skb_queue_tail(&txq->pending_skbs, skb); @@ -1038,6 +1077,8 @@ static void mana_poll_tx_cq(struct mana_cq *cq) if (comp_read < 1) return; + apc->eth_stats.tx_cqes = comp_read; + for (i = 0; i < comp_read; i++) { struct mana_tx_comp_oob *cqe_oob; @@ -1064,6 +1105,7 @@ static void mana_poll_tx_cq(struct mana_cq *cq) case CQE_TX_VLAN_TAGGING_VIOLATION: WARN_ONCE(1, "TX: CQE error %d: ignored.\n", cqe_oob->cqe_hdr.cqe_type); + apc->eth_stats.tx_cqe_err++; break; default: @@ -1072,6 +1114,7 @@ static void mana_poll_tx_cq(struct mana_cq *cq) */ WARN_ONCE(1, "TX: Unexpected CQE type %d: HW BUG?\n", cqe_oob->cqe_hdr.cqe_type); + apc->eth_stats.tx_cqe_unknown_type++; return; } @@ -1118,6 +1161,8 @@ static void mana_poll_tx_cq(struct mana_cq *cq) WARN_ON_ONCE(1); cq->work_done = pkt_transmitted; + + apc->eth_stats.tx_cqes -= pkt_transmitted; } static void mana_post_pkt_rxq(struct mana_rxq *rxq) @@ -1252,12 +1297,15 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, struct gdma_context *gc = rxq->gdma_rq->gdma_dev->gdma_context; struct net_device *ndev = rxq->ndev; struct mana_recv_buf_oob *rxbuf_oob; + struct mana_port_context *apc; struct device *dev = gc->dev; void *new_buf, *old_buf; struct page *new_page; u32 curr, pktlen; dma_addr_t da; + apc = netdev_priv(ndev); + switch (oob->cqe_hdr.cqe_type) { case CQE_RX_OKAY: break; @@ -1270,6 +1318,7 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, case CQE_RX_COALESCED_4: netdev_err(ndev, "RX coalescing is unsupported\n"); + apc->eth_stats.rx_coalesced_err++; return; case CQE_RX_OBJECT_FENCE: @@ -1279,6 +1328,7 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, default: netdev_err(ndev, "Unknown RX CQE type = %d\n", oob->cqe_hdr.cqe_type); + apc->eth_stats.rx_cqe_unknown_type++; return; } @@ -1341,11 +1391,15 @@ static void mana_poll_rx_cq(struct mana_cq *cq) { struct gdma_comp *comp = cq->gdma_comp_buf; struct mana_rxq *rxq = cq->rxq; + struct mana_port_context *apc; int comp_read, i; + apc = netdev_priv(rxq->ndev); + comp_read = mana_gd_poll_cq(cq->gdma_cq, comp, CQE_POLLING_BUFFER); WARN_ON_ONCE(comp_read > CQE_POLLING_BUFFER); + apc->eth_stats.rx_cqes = comp_read; rxq->xdp_flush = false; for (i = 0; i < comp_read; i++) { @@ -1357,6 +1411,8 @@ static void mana_poll_rx_cq(struct mana_cq *cq) return; mana_process_rx_cqe(rxq, cq, &comp[i]); + + apc->eth_stats.rx_cqes--; } if (rxq->xdp_flush) diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c index 5b776a33a817..a64c81410dc1 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c @@ -13,6 +13,15 @@ static const struct { } mana_eth_stats[] = { {"stop_queue", offsetof(struct mana_ethtool_stats, stop_queue)}, {"wake_queue", offsetof(struct mana_ethtool_stats, wake_queue)}, + {"tx_cqes", offsetof(struct mana_ethtool_stats, tx_cqes)}, + {"tx_cq_err", offsetof(struct mana_ethtool_stats, tx_cqe_err)}, + {"tx_cqe_unknown_type", offsetof(struct mana_ethtool_stats, + tx_cqe_unknown_type)}, + {"rx_cqes", offsetof(struct mana_ethtool_stats, rx_cqes)}, + {"rx_coalesced_err", offsetof(struct mana_ethtool_stats, + rx_coalesced_err)}, + {"rx_cqe_unknown_type", offsetof(struct mana_ethtool_stats, + rx_cqe_unknown_type)}, }; static int mana_get_sset_count(struct net_device *ndev, int stringset) @@ -23,7 +32,8 @@ static int mana_get_sset_count(struct net_device *ndev, int stringset) if (stringset != ETH_SS_STATS) return -EINVAL; - return ARRAY_SIZE(mana_eth_stats) + num_queues * 8; + return ARRAY_SIZE(mana_eth_stats) + num_queues * + (MANA_STATS_RX_COUNT + MANA_STATS_TX_COUNT); } static void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data) @@ -61,6 +71,22 @@ static void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data) p += ETH_GSTRING_LEN; sprintf(p, "tx_%d_xdp_xmit", i); p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_tso_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_tso_bytes", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_tso_inner_packets", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_tso_inner_bytes", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_long_pkt_fmt", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_short_pkt_fmt", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_csum_partial", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_%d_mana_map_err", i); + p += ETH_GSTRING_LEN; } } @@ -78,6 +104,14 @@ static void mana_get_ethtool_stats(struct net_device *ndev, u64 xdp_xmit; u64 xdp_drop; u64 xdp_tx; + u64 tso_packets; + u64 tso_bytes; + u64 tso_inner_packets; + u64 tso_inner_bytes; + u64 long_pkt_fmt; + u64 short_pkt_fmt; + u64 csum_partial; + u64 mana_map_err; int q, i = 0; if (!apc->port_is_up) @@ -113,11 +147,27 @@ static void mana_get_ethtool_stats(struct net_device *ndev, packets = tx_stats->packets; bytes = tx_stats->bytes; xdp_xmit = tx_stats->xdp_xmit; + tso_packets = tx_stats->tso_packets; + tso_bytes = tx_stats->tso_bytes; + tso_inner_packets = tx_stats->tso_inner_packets; + tso_inner_bytes = tx_stats->tso_inner_bytes; + long_pkt_fmt = tx_stats->long_pkt_fmt; + short_pkt_fmt = tx_stats->short_pkt_fmt; + csum_partial = tx_stats->csum_partial; + mana_map_err = tx_stats->mana_map_err; } while (u64_stats_fetch_retry(&tx_stats->syncp, start)); data[i++] = packets; data[i++] = bytes; data[i++] = xdp_xmit; + data[i++] = tso_packets; + data[i++] = tso_bytes; + data[i++] = tso_inner_packets; + data[i++] = tso_inner_bytes; + data[i++] = long_pkt_fmt; + data[i++] = short_pkt_fmt; + data[i++] = csum_partial; + data[i++] = mana_map_err; } } diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 3bb579962a14..bb11a6535d80 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -48,6 +48,10 @@ enum TRI_STATE { #define MAX_PORTS_IN_MANA_DEV 256 +/* Update this count whenever the respective structures are changed */ +#define MANA_STATS_RX_COUNT 5 +#define MANA_STATS_TX_COUNT 11 + struct mana_stats_rx { u64 packets; u64 bytes; @@ -61,6 +65,14 @@ struct mana_stats_tx { u64 packets; u64 bytes; u64 xdp_xmit; + u64 tso_packets; + u64 tso_bytes; + u64 tso_inner_packets; + u64 tso_inner_bytes; + u64 short_pkt_fmt; + u64 long_pkt_fmt; + u64 csum_partial; + u64 mana_map_err; struct u64_stats_sync syncp; }; @@ -331,6 +343,12 @@ struct mana_tx_qp { struct mana_ethtool_stats { u64 stop_queue; u64 wake_queue; + u64 tx_cqes; + u64 tx_cqe_err; + u64 tx_cqe_unknown_type; + u64 rx_cqes; + u64 rx_coalesced_err; + u64 rx_cqe_unknown_type; }; struct mana_context { -- cgit v1.2.3 From f307c8bf37a346ed3e8b6090b64b4ca8d61e1bcd Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Mar 2023 15:11:49 +0200 Subject: vxlan: Move address helpers to private headers Move the helpers out of the core C file to the private header so that they could be used by the upcoming MDB code. While at it, constify the second argument of vxlan_nla_get_addr(). Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/vxlan/vxlan_core.c | 47 --------------------------------------- drivers/net/vxlan/vxlan_private.h | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index f2c30214cae8..2c65cc5dd55d 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -71,53 +71,6 @@ static inline bool vxlan_collect_metadata(struct vxlan_sock *vs) ip_tunnel_collect_metadata(); } -#if IS_ENABLED(CONFIG_IPV6) -static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) -{ - if (nla_len(nla) >= sizeof(struct in6_addr)) { - ip->sin6.sin6_addr = nla_get_in6_addr(nla); - ip->sa.sa_family = AF_INET6; - return 0; - } else if (nla_len(nla) >= sizeof(__be32)) { - ip->sin.sin_addr.s_addr = nla_get_in_addr(nla); - ip->sa.sa_family = AF_INET; - return 0; - } else { - return -EAFNOSUPPORT; - } -} - -static int vxlan_nla_put_addr(struct sk_buff *skb, int attr, - const union vxlan_addr *ip) -{ - if (ip->sa.sa_family == AF_INET6) - return nla_put_in6_addr(skb, attr, &ip->sin6.sin6_addr); - else - return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); -} - -#else /* !CONFIG_IPV6 */ - -static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) -{ - if (nla_len(nla) >= sizeof(struct in6_addr)) { - return -EAFNOSUPPORT; - } else if (nla_len(nla) >= sizeof(__be32)) { - ip->sin.sin_addr.s_addr = nla_get_in_addr(nla); - ip->sa.sa_family = AF_INET; - return 0; - } else { - return -EAFNOSUPPORT; - } -} - -static int vxlan_nla_put_addr(struct sk_buff *skb, int attr, - const union vxlan_addr *ip) -{ - return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); -} -#endif - /* Find VXLAN socket based on network namespace, address family, UDP port, * enabled unshareable flags and socket device binding (see l3mdev with * non-default VRF). diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index 599c3b4fdd5e..038528f9684a 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -85,6 +85,31 @@ bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b) return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; } +static inline int vxlan_nla_get_addr(union vxlan_addr *ip, + const struct nlattr *nla) +{ + if (nla_len(nla) >= sizeof(struct in6_addr)) { + ip->sin6.sin6_addr = nla_get_in6_addr(nla); + ip->sa.sa_family = AF_INET6; + return 0; + } else if (nla_len(nla) >= sizeof(__be32)) { + ip->sin.sin_addr.s_addr = nla_get_in_addr(nla); + ip->sa.sa_family = AF_INET; + return 0; + } else { + return -EAFNOSUPPORT; + } +} + +static inline int vxlan_nla_put_addr(struct sk_buff *skb, int attr, + const union vxlan_addr *ip) +{ + if (ip->sa.sa_family == AF_INET6) + return nla_put_in6_addr(skb, attr, &ip->sin6.sin6_addr); + else + return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); +} + #else /* !CONFIG_IPV6 */ static inline @@ -93,6 +118,26 @@ bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b) return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; } +static inline int vxlan_nla_get_addr(union vxlan_addr *ip, + const struct nlattr *nla) +{ + if (nla_len(nla) >= sizeof(struct in6_addr)) { + return -EAFNOSUPPORT; + } else if (nla_len(nla) >= sizeof(__be32)) { + ip->sin.sin_addr.s_addr = nla_get_in_addr(nla); + ip->sa.sa_family = AF_INET; + return 0; + } else { + return -EAFNOSUPPORT; + } +} + +static inline int vxlan_nla_put_addr(struct sk_buff *skb, int attr, + const union vxlan_addr *ip) +{ + return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); +} + #endif static inline struct vxlan_vni_node * -- cgit v1.2.3 From 6ab271aaad25351ea8587d67c6837678b875eb2c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Mar 2023 15:11:50 +0200 Subject: vxlan: Expose vxlan_xmit_one() Given a packet and a remote destination, the function will take care of encapsulating the packet and transmitting it to the destination. Expose it so that it could be used in subsequent patches by the MDB code to transmit a packet to the remote destination(s) stored in the MDB entry. It will allow us to keep the MDB code self-contained, not exposing its data structures to the rest of the VXLAN driver. Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/vxlan/vxlan_core.c | 5 ++--- drivers/net/vxlan/vxlan_private.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 2c65cc5dd55d..5de1a20497a6 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2395,9 +2395,8 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, return 0; } -static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - __be32 default_vni, struct vxlan_rdst *rdst, - bool did_rsc) +void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, + __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc) { struct dst_cache *dst_cache; struct ip_tunnel_info *info; diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index 038528f9684a..f4977925cb8a 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -172,6 +172,8 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan, __be16 port, __be32 src_vni, __be32 vni, __u32 ifindex, __u16 ndm_flags, u32 nhid, bool swdev_notify, struct netlink_ext_ack *extack); +void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, + __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc); int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan, struct vxlan_config *conf, __be32 vni); -- cgit v1.2.3 From a3a48de5eade770e911d35291217bdd69ce04ef1 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Mar 2023 15:11:51 +0200 Subject: vxlan: mdb: Add MDB control path support Implement MDB control path support, enabling the creation, deletion, replacement and dumping of MDB entries in a similar fashion to the bridge driver. Unlike the bridge driver, each entry stores a list of remote VTEPs to which matched packets need to be replicated to and not a list of bridge ports. The motivating use case is the installation of MDB entries by a user space control plane in response to received EVPN routes. As such, only allow permanent MDB entries to be installed and do not implement snooping functionality, avoiding a lot of unnecessary complexity. Since entries can only be modified by user space under RTNL, use RTNL as the write lock. Use RCU to ensure that MDB entries and remotes are not freed while being accessed from the data path during transmission. In terms of uAPI, reuse the existing MDB netlink interface, but add a few new attributes to request and response messages: * IP address of the destination VXLAN tunnel endpoint where the multicast receivers reside. * UDP destination port number to use to connect to the remote VXLAN tunnel endpoint. * VXLAN VNI Network Identifier to use to connect to the remote VXLAN tunnel endpoint. Required when Ingress Replication (IR) is used and the remote VTEP is not a member of originating broadcast domain (VLAN/VNI) [1]. * Source VNI Network Identifier the MDB entry belongs to. Used only when the VXLAN device is in external mode. * Interface index of the outgoing interface to reach the remote VXLAN tunnel endpoint. This is required when the underlay destination IP is multicast (P2MP), as the multicast routing tables are not consulted. All the new attributes are added under the 'MDBA_SET_ENTRY_ATTRS' nest which is strictly validated by the bridge driver, thereby automatically rejecting the new attributes. [1] https://datatracker.ietf.org/doc/html/draft-ietf-bess-evpn-irb-mcast#section-3.2.2 Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/vxlan/Makefile | 2 +- drivers/net/vxlan/vxlan_core.c | 8 + drivers/net/vxlan/vxlan_mdb.c | 1341 +++++++++++++++++++++++++++++++++++++ drivers/net/vxlan/vxlan_private.h | 31 + include/net/vxlan.h | 5 + include/uapi/linux/if_bridge.h | 10 + 6 files changed, 1396 insertions(+), 1 deletion(-) create mode 100644 drivers/net/vxlan/vxlan_mdb.c (limited to 'drivers') diff --git a/drivers/net/vxlan/Makefile b/drivers/net/vxlan/Makefile index d4c255499b72..91b8fec8b6cf 100644 --- a/drivers/net/vxlan/Makefile +++ b/drivers/net/vxlan/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_VXLAN) += vxlan.o -vxlan-objs := vxlan_core.o vxlan_multicast.o vxlan_vnifilter.o +vxlan-objs := vxlan_core.o vxlan_multicast.o vxlan_vnifilter.o vxlan_mdb.o diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 5de1a20497a6..a8b26d4f76de 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2878,8 +2878,14 @@ static int vxlan_init(struct net_device *dev) if (err) goto err_free_percpu; + err = vxlan_mdb_init(vxlan); + if (err) + goto err_gro_cells_destroy; + return 0; +err_gro_cells_destroy: + gro_cells_destroy(&vxlan->gro_cells); err_free_percpu: free_percpu(dev->tstats); err_vnigroup_uninit: @@ -2904,6 +2910,8 @@ static void vxlan_uninit(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); + vxlan_mdb_fini(vxlan); + if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) vxlan_vnigroup_uninit(vxlan); diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c new file mode 100644 index 000000000000..129692b3663f --- /dev/null +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -0,0 +1,1341 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vxlan_private.h" + +struct vxlan_mdb_entry_key { + union vxlan_addr src; + union vxlan_addr dst; + __be32 vni; +}; + +struct vxlan_mdb_entry { + struct rhash_head rhnode; + struct list_head remotes; + struct vxlan_mdb_entry_key key; + struct hlist_node mdb_node; + struct rcu_head rcu; +}; + +#define VXLAN_MDB_REMOTE_F_BLOCKED BIT(0) + +struct vxlan_mdb_remote { + struct list_head list; + struct vxlan_rdst __rcu *rd; + u8 flags; + u8 filter_mode; + u8 rt_protocol; + struct hlist_head src_list; + struct rcu_head rcu; +}; + +#define VXLAN_SGRP_F_DELETE BIT(0) + +struct vxlan_mdb_src_entry { + struct hlist_node node; + union vxlan_addr addr; + u8 flags; +}; + +struct vxlan_mdb_dump_ctx { + long reserved; + long entry_idx; + long remote_idx; +}; + +struct vxlan_mdb_config_src_entry { + union vxlan_addr addr; + struct list_head node; +}; + +struct vxlan_mdb_config { + struct vxlan_dev *vxlan; + struct vxlan_mdb_entry_key group; + struct list_head src_list; + union vxlan_addr remote_ip; + u32 remote_ifindex; + __be32 remote_vni; + __be16 remote_port; + u16 nlflags; + u8 flags; + u8 filter_mode; + u8 rt_protocol; +}; + +static const struct rhashtable_params vxlan_mdb_rht_params = { + .head_offset = offsetof(struct vxlan_mdb_entry, rhnode), + .key_offset = offsetof(struct vxlan_mdb_entry, key), + .key_len = sizeof(struct vxlan_mdb_entry_key), + .automatic_shrinking = true, +}; + +static int __vxlan_mdb_add(const struct vxlan_mdb_config *cfg, + struct netlink_ext_ack *extack); +static int __vxlan_mdb_del(const struct vxlan_mdb_config *cfg, + struct netlink_ext_ack *extack); + +static void vxlan_br_mdb_entry_fill(const struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry *mdb_entry, + const struct vxlan_mdb_remote *remote, + struct br_mdb_entry *e) +{ + const union vxlan_addr *dst = &mdb_entry->key.dst; + + memset(e, 0, sizeof(*e)); + e->ifindex = vxlan->dev->ifindex; + e->state = MDB_PERMANENT; + + if (remote->flags & VXLAN_MDB_REMOTE_F_BLOCKED) + e->flags |= MDB_FLAGS_BLOCKED; + + switch (dst->sa.sa_family) { + case AF_INET: + e->addr.u.ip4 = dst->sin.sin_addr.s_addr; + e->addr.proto = htons(ETH_P_IP); + break; +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + e->addr.u.ip6 = dst->sin6.sin6_addr; + e->addr.proto = htons(ETH_P_IPV6); + break; +#endif + } +} + +static int vxlan_mdb_entry_info_fill_srcs(struct sk_buff *skb, + const struct vxlan_mdb_remote *remote) +{ + struct vxlan_mdb_src_entry *ent; + struct nlattr *nest; + + if (hlist_empty(&remote->src_list)) + return 0; + + nest = nla_nest_start(skb, MDBA_MDB_EATTR_SRC_LIST); + if (!nest) + return -EMSGSIZE; + + hlist_for_each_entry(ent, &remote->src_list, node) { + struct nlattr *nest_ent; + + nest_ent = nla_nest_start(skb, MDBA_MDB_SRCLIST_ENTRY); + if (!nest_ent) + goto out_cancel_err; + + if (vxlan_nla_put_addr(skb, MDBA_MDB_SRCATTR_ADDRESS, + &ent->addr) || + nla_put_u32(skb, MDBA_MDB_SRCATTR_TIMER, 0)) + goto out_cancel_err; + + nla_nest_end(skb, nest_ent); + } + + nla_nest_end(skb, nest); + + return 0; + +out_cancel_err: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan, + struct sk_buff *skb, + const struct vxlan_mdb_entry *mdb_entry, + const struct vxlan_mdb_remote *remote) +{ + struct vxlan_rdst *rd = rtnl_dereference(remote->rd); + struct br_mdb_entry e; + struct nlattr *nest; + + nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY_INFO); + if (!nest) + return -EMSGSIZE; + + vxlan_br_mdb_entry_fill(vxlan, mdb_entry, remote, &e); + + if (nla_put_nohdr(skb, sizeof(e), &e) || + nla_put_u32(skb, MDBA_MDB_EATTR_TIMER, 0)) + goto nest_err; + + if (!vxlan_addr_any(&mdb_entry->key.src) && + vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_SOURCE, &mdb_entry->key.src)) + goto nest_err; + + if (nla_put_u8(skb, MDBA_MDB_EATTR_RTPROT, remote->rt_protocol) || + nla_put_u8(skb, MDBA_MDB_EATTR_GROUP_MODE, remote->filter_mode) || + vxlan_mdb_entry_info_fill_srcs(skb, remote) || + vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_DST, &rd->remote_ip)) + goto nest_err; + + if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port && + nla_put_u16(skb, MDBA_MDB_EATTR_DST_PORT, + be16_to_cpu(rd->remote_port))) + goto nest_err; + + if (rd->remote_vni != vxlan->default_dst.remote_vni && + nla_put_u32(skb, MDBA_MDB_EATTR_VNI, be32_to_cpu(rd->remote_vni))) + goto nest_err; + + if (rd->remote_ifindex && + nla_put_u32(skb, MDBA_MDB_EATTR_IFINDEX, rd->remote_ifindex)) + goto nest_err; + + if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && + mdb_entry->key.vni && nla_put_u32(skb, MDBA_MDB_EATTR_SRC_VNI, + be32_to_cpu(mdb_entry->key.vni))) + goto nest_err; + + nla_nest_end(skb, nest); + + return 0; + +nest_err: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int vxlan_mdb_entry_fill(const struct vxlan_dev *vxlan, + struct sk_buff *skb, + struct vxlan_mdb_dump_ctx *ctx, + const struct vxlan_mdb_entry *mdb_entry) +{ + int remote_idx = 0, s_remote_idx = ctx->remote_idx; + struct vxlan_mdb_remote *remote; + struct nlattr *nest; + int err = 0; + + nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY); + if (!nest) + return -EMSGSIZE; + + list_for_each_entry(remote, &mdb_entry->remotes, list) { + if (remote_idx < s_remote_idx) + goto skip; + + err = vxlan_mdb_entry_info_fill(vxlan, skb, mdb_entry, remote); + if (err) + break; +skip: + remote_idx++; + } + + ctx->remote_idx = err ? remote_idx : 0; + nla_nest_end(skb, nest); + return err; +} + +static int vxlan_mdb_fill(const struct vxlan_dev *vxlan, struct sk_buff *skb, + struct vxlan_mdb_dump_ctx *ctx) +{ + int entry_idx = 0, s_entry_idx = ctx->entry_idx; + struct vxlan_mdb_entry *mdb_entry; + struct nlattr *nest; + int err = 0; + + nest = nla_nest_start_noflag(skb, MDBA_MDB); + if (!nest) + return -EMSGSIZE; + + hlist_for_each_entry(mdb_entry, &vxlan->mdb_list, mdb_node) { + if (entry_idx < s_entry_idx) + goto skip; + + err = vxlan_mdb_entry_fill(vxlan, skb, ctx, mdb_entry); + if (err) + break; +skip: + entry_idx++; + } + + ctx->entry_idx = err ? entry_idx : 0; + nla_nest_end(skb, nest); + return err; +} + +int vxlan_mdb_dump(struct net_device *dev, struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct vxlan_mdb_dump_ctx *ctx = (void *)cb->ctx; + struct vxlan_dev *vxlan = netdev_priv(dev); + struct br_port_msg *bpm; + struct nlmsghdr *nlh; + int err; + + ASSERT_RTNL(); + + NL_ASSERT_DUMP_CTX_FITS(struct vxlan_mdb_dump_ctx); + + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, RTM_NEWMDB, sizeof(*bpm), + NLM_F_MULTI); + if (!nlh) + return -EMSGSIZE; + + bpm = nlmsg_data(nlh); + memset(bpm, 0, sizeof(*bpm)); + bpm->family = AF_BRIDGE; + bpm->ifindex = dev->ifindex; + + err = vxlan_mdb_fill(vxlan, skb, ctx); + + nlmsg_end(skb, nlh); + + cb->seq = vxlan->mdb_seq; + nl_dump_check_consistent(cb, nlh); + + return err; +} + +static const struct nla_policy +vxlan_mdbe_src_list_entry_pol[MDBE_SRCATTR_MAX + 1] = { + [MDBE_SRCATTR_ADDRESS] = NLA_POLICY_RANGE(NLA_BINARY, + sizeof(struct in_addr), + sizeof(struct in6_addr)), +}; + +static const struct nla_policy +vxlan_mdbe_src_list_pol[MDBE_SRC_LIST_MAX + 1] = { + [MDBE_SRC_LIST_ENTRY] = NLA_POLICY_NESTED(vxlan_mdbe_src_list_entry_pol), +}; + +static struct netlink_range_validation vni_range = { + .max = VXLAN_N_VID - 1, +}; + +static const struct nla_policy vxlan_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = { + [MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY, + sizeof(struct in_addr), + sizeof(struct in6_addr)), + [MDBE_ATTR_GROUP_MODE] = NLA_POLICY_RANGE(NLA_U8, MCAST_EXCLUDE, + MCAST_INCLUDE), + [MDBE_ATTR_SRC_LIST] = NLA_POLICY_NESTED(vxlan_mdbe_src_list_pol), + [MDBE_ATTR_RTPROT] = NLA_POLICY_MIN(NLA_U8, RTPROT_STATIC), + [MDBE_ATTR_DST] = NLA_POLICY_RANGE(NLA_BINARY, + sizeof(struct in_addr), + sizeof(struct in6_addr)), + [MDBE_ATTR_DST_PORT] = { .type = NLA_U16 }, + [MDBE_ATTR_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range), + [MDBE_ATTR_IFINDEX] = NLA_POLICY_MIN(NLA_S32, 1), + [MDBE_ATTR_SRC_VNI] = NLA_POLICY_FULL_RANGE(NLA_U32, &vni_range), +}; + +static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto, + struct netlink_ext_ack *extack) +{ + switch (proto) { + case htons(ETH_P_IP): + if (nla_len(attr) != sizeof(struct in_addr)) { + NL_SET_ERR_MSG_MOD(extack, "IPv4 invalid source address length"); + return false; + } + if (ipv4_is_multicast(nla_get_in_addr(attr))) { + NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed"); + return false; + } + break; +#if IS_ENABLED(CONFIG_IPV6) + case htons(ETH_P_IPV6): { + struct in6_addr src; + + if (nla_len(attr) != sizeof(struct in6_addr)) { + NL_SET_ERR_MSG_MOD(extack, "IPv6 invalid source address length"); + return false; + } + src = nla_get_in6_addr(attr); + if (ipv6_addr_is_multicast(&src)) { + NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed"); + return false; + } + break; + } +#endif + default: + NL_SET_ERR_MSG_MOD(extack, "Invalid protocol used with source address"); + return false; + } + + return true; +} + +static void vxlan_mdb_config_group_set(struct vxlan_mdb_config *cfg, + const struct br_mdb_entry *entry, + const struct nlattr *source_attr) +{ + struct vxlan_mdb_entry_key *group = &cfg->group; + + switch (entry->addr.proto) { + case htons(ETH_P_IP): + group->dst.sa.sa_family = AF_INET; + group->dst.sin.sin_addr.s_addr = entry->addr.u.ip4; + break; +#if IS_ENABLED(CONFIG_IPV6) + case htons(ETH_P_IPV6): + group->dst.sa.sa_family = AF_INET6; + group->dst.sin6.sin6_addr = entry->addr.u.ip6; + break; +#endif + } + + if (source_attr) + vxlan_nla_get_addr(&group->src, source_attr); +} + +static bool vxlan_mdb_is_star_g(const struct vxlan_mdb_entry_key *group) +{ + return !vxlan_addr_any(&group->dst) && vxlan_addr_any(&group->src); +} + +static bool vxlan_mdb_is_sg(const struct vxlan_mdb_entry_key *group) +{ + return !vxlan_addr_any(&group->dst) && !vxlan_addr_any(&group->src); +} + +static int vxlan_mdb_config_src_entry_init(struct vxlan_mdb_config *cfg, + __be16 proto, + const struct nlattr *src_entry, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[MDBE_SRCATTR_MAX + 1]; + struct vxlan_mdb_config_src_entry *src; + int err; + + err = nla_parse_nested(tb, MDBE_SRCATTR_MAX, src_entry, + vxlan_mdbe_src_list_entry_pol, extack); + if (err) + return err; + + if (NL_REQ_ATTR_CHECK(extack, src_entry, tb, MDBE_SRCATTR_ADDRESS)) + return -EINVAL; + + if (!vxlan_mdb_is_valid_source(tb[MDBE_SRCATTR_ADDRESS], proto, + extack)) + return -EINVAL; + + src = kzalloc(sizeof(*src), GFP_KERNEL); + if (!src) + return -ENOMEM; + + err = vxlan_nla_get_addr(&src->addr, tb[MDBE_SRCATTR_ADDRESS]); + if (err) + goto err_free_src; + + list_add_tail(&src->node, &cfg->src_list); + + return 0; + +err_free_src: + kfree(src); + return err; +} + +static void +vxlan_mdb_config_src_entry_fini(struct vxlan_mdb_config_src_entry *src) +{ + list_del(&src->node); + kfree(src); +} + +static int vxlan_mdb_config_src_list_init(struct vxlan_mdb_config *cfg, + __be16 proto, + const struct nlattr *src_list, + struct netlink_ext_ack *extack) +{ + struct vxlan_mdb_config_src_entry *src, *tmp; + struct nlattr *src_entry; + int rem, err; + + nla_for_each_nested(src_entry, src_list, rem) { + err = vxlan_mdb_config_src_entry_init(cfg, proto, src_entry, + extack); + if (err) + goto err_src_entry_init; + } + + return 0; + +err_src_entry_init: + list_for_each_entry_safe_reverse(src, tmp, &cfg->src_list, node) + vxlan_mdb_config_src_entry_fini(src); + return err; +} + +static void vxlan_mdb_config_src_list_fini(struct vxlan_mdb_config *cfg) +{ + struct vxlan_mdb_config_src_entry *src, *tmp; + + list_for_each_entry_safe_reverse(src, tmp, &cfg->src_list, node) + vxlan_mdb_config_src_entry_fini(src); +} + +static int vxlan_mdb_config_attrs_init(struct vxlan_mdb_config *cfg, + const struct br_mdb_entry *entry, + const struct nlattr *set_attrs, + struct netlink_ext_ack *extack) +{ + struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1]; + int err; + + err = nla_parse_nested(mdbe_attrs, MDBE_ATTR_MAX, set_attrs, + vxlan_mdbe_attrs_pol, extack); + if (err) + return err; + + if (NL_REQ_ATTR_CHECK(extack, set_attrs, mdbe_attrs, MDBE_ATTR_DST)) { + NL_SET_ERR_MSG_MOD(extack, "Missing remote destination IP address"); + return -EINVAL; + } + + if (mdbe_attrs[MDBE_ATTR_SOURCE] && + !vxlan_mdb_is_valid_source(mdbe_attrs[MDBE_ATTR_SOURCE], + entry->addr.proto, extack)) + return -EINVAL; + + vxlan_mdb_config_group_set(cfg, entry, mdbe_attrs[MDBE_ATTR_SOURCE]); + + /* rtnetlink code only validates that IPv4 group address is + * multicast. + */ + if (!vxlan_addr_is_multicast(&cfg->group.dst) && + !vxlan_addr_any(&cfg->group.dst)) { + NL_SET_ERR_MSG_MOD(extack, "Group address is not multicast"); + return -EINVAL; + } + + if (vxlan_addr_any(&cfg->group.dst) && + mdbe_attrs[MDBE_ATTR_SOURCE]) { + NL_SET_ERR_MSG_MOD(extack, "Source cannot be specified for the all-zeros entry"); + return -EINVAL; + } + + if (vxlan_mdb_is_sg(&cfg->group)) + cfg->filter_mode = MCAST_INCLUDE; + + if (mdbe_attrs[MDBE_ATTR_GROUP_MODE]) { + if (!vxlan_mdb_is_star_g(&cfg->group)) { + NL_SET_ERR_MSG_MOD(extack, "Filter mode can only be set for (*, G) entries"); + return -EINVAL; + } + cfg->filter_mode = nla_get_u8(mdbe_attrs[MDBE_ATTR_GROUP_MODE]); + } + + if (mdbe_attrs[MDBE_ATTR_SRC_LIST]) { + if (!vxlan_mdb_is_star_g(&cfg->group)) { + NL_SET_ERR_MSG_MOD(extack, "Source list can only be set for (*, G) entries"); + return -EINVAL; + } + if (!mdbe_attrs[MDBE_ATTR_GROUP_MODE]) { + NL_SET_ERR_MSG_MOD(extack, "Source list cannot be set without filter mode"); + return -EINVAL; + } + err = vxlan_mdb_config_src_list_init(cfg, entry->addr.proto, + mdbe_attrs[MDBE_ATTR_SRC_LIST], + extack); + if (err) + return err; + } + + if (vxlan_mdb_is_star_g(&cfg->group) && list_empty(&cfg->src_list) && + cfg->filter_mode == MCAST_INCLUDE) { + NL_SET_ERR_MSG_MOD(extack, "Cannot add (*, G) INCLUDE with an empty source list"); + return -EINVAL; + } + + if (mdbe_attrs[MDBE_ATTR_RTPROT]) + cfg->rt_protocol = nla_get_u8(mdbe_attrs[MDBE_ATTR_RTPROT]); + + err = vxlan_nla_get_addr(&cfg->remote_ip, mdbe_attrs[MDBE_ATTR_DST]); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Invalid remote destination address"); + goto err_src_list_fini; + } + + if (mdbe_attrs[MDBE_ATTR_DST_PORT]) + cfg->remote_port = + cpu_to_be16(nla_get_u16(mdbe_attrs[MDBE_ATTR_DST_PORT])); + + if (mdbe_attrs[MDBE_ATTR_VNI]) + cfg->remote_vni = + cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_VNI])); + + if (mdbe_attrs[MDBE_ATTR_IFINDEX]) { + cfg->remote_ifindex = + nla_get_s32(mdbe_attrs[MDBE_ATTR_IFINDEX]); + if (!__dev_get_by_index(cfg->vxlan->net, cfg->remote_ifindex)) { + NL_SET_ERR_MSG_MOD(extack, "Outgoing interface not found"); + err = -EINVAL; + goto err_src_list_fini; + } + } + + if (mdbe_attrs[MDBE_ATTR_SRC_VNI]) + cfg->group.vni = + cpu_to_be32(nla_get_u32(mdbe_attrs[MDBE_ATTR_SRC_VNI])); + + return 0; + +err_src_list_fini: + vxlan_mdb_config_src_list_fini(cfg); + return err; +} + +static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg, + struct net_device *dev, struct nlattr *tb[], + u16 nlmsg_flags, + struct netlink_ext_ack *extack) +{ + struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]); + struct vxlan_dev *vxlan = netdev_priv(dev); + + memset(cfg, 0, sizeof(*cfg)); + cfg->vxlan = vxlan; + cfg->group.vni = vxlan->default_dst.remote_vni; + INIT_LIST_HEAD(&cfg->src_list); + cfg->nlflags = nlmsg_flags; + cfg->filter_mode = MCAST_EXCLUDE; + cfg->rt_protocol = RTPROT_STATIC; + cfg->remote_vni = vxlan->default_dst.remote_vni; + cfg->remote_port = vxlan->cfg.dst_port; + + if (entry->ifindex != dev->ifindex) { + NL_SET_ERR_MSG_MOD(extack, "Port net device must be the VXLAN net device"); + return -EINVAL; + } + + /* State is not part of the entry key and can be ignored on deletion + * requests. + */ + if ((nlmsg_flags & (NLM_F_CREATE | NLM_F_REPLACE)) && + entry->state != MDB_PERMANENT) { + NL_SET_ERR_MSG_MOD(extack, "MDB entry must be permanent"); + return -EINVAL; + } + + if (entry->flags) { + NL_SET_ERR_MSG_MOD(extack, "Invalid MDB entry flags"); + return -EINVAL; + } + + if (entry->vid) { + NL_SET_ERR_MSG_MOD(extack, "VID must not be specified"); + return -EINVAL; + } + + if (entry->addr.proto != htons(ETH_P_IP) && + entry->addr.proto != htons(ETH_P_IPV6)) { + NL_SET_ERR_MSG_MOD(extack, "Group address must be an IPv4 / IPv6 address"); + return -EINVAL; + } + + if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY_ATTRS)) { + NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY_ATTRS attribute"); + return -EINVAL; + } + + return vxlan_mdb_config_attrs_init(cfg, entry, tb[MDBA_SET_ENTRY_ATTRS], + extack); +} + +static void vxlan_mdb_config_fini(struct vxlan_mdb_config *cfg) +{ + vxlan_mdb_config_src_list_fini(cfg); +} + +static struct vxlan_mdb_entry * +vxlan_mdb_entry_lookup(struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry_key *group) +{ + return rhashtable_lookup_fast(&vxlan->mdb_tbl, group, + vxlan_mdb_rht_params); +} + +static struct vxlan_mdb_remote * +vxlan_mdb_remote_lookup(const struct vxlan_mdb_entry *mdb_entry, + const union vxlan_addr *addr) +{ + struct vxlan_mdb_remote *remote; + + list_for_each_entry(remote, &mdb_entry->remotes, list) { + struct vxlan_rdst *rd = rtnl_dereference(remote->rd); + + if (vxlan_addr_equal(addr, &rd->remote_ip)) + return remote; + } + + return NULL; +} + +static void vxlan_mdb_rdst_free(struct rcu_head *head) +{ + struct vxlan_rdst *rd = container_of(head, struct vxlan_rdst, rcu); + + dst_cache_destroy(&rd->dst_cache); + kfree(rd); +} + +static int vxlan_mdb_remote_rdst_init(const struct vxlan_mdb_config *cfg, + struct vxlan_mdb_remote *remote) +{ + struct vxlan_rdst *rd; + int err; + + rd = kzalloc(sizeof(*rd), GFP_KERNEL); + if (!rd) + return -ENOMEM; + + err = dst_cache_init(&rd->dst_cache, GFP_KERNEL); + if (err) + goto err_free_rdst; + + rd->remote_ip = cfg->remote_ip; + rd->remote_port = cfg->remote_port; + rd->remote_vni = cfg->remote_vni; + rd->remote_ifindex = cfg->remote_ifindex; + rcu_assign_pointer(remote->rd, rd); + + return 0; + +err_free_rdst: + kfree(rd); + return err; +} + +static void vxlan_mdb_remote_rdst_fini(struct vxlan_rdst *rd) +{ + call_rcu(&rd->rcu, vxlan_mdb_rdst_free); +} + +static int vxlan_mdb_remote_init(const struct vxlan_mdb_config *cfg, + struct vxlan_mdb_remote *remote) +{ + int err; + + err = vxlan_mdb_remote_rdst_init(cfg, remote); + if (err) + return err; + + remote->flags = cfg->flags; + remote->filter_mode = cfg->filter_mode; + remote->rt_protocol = cfg->rt_protocol; + INIT_HLIST_HEAD(&remote->src_list); + + return 0; +} + +static void vxlan_mdb_remote_fini(struct vxlan_dev *vxlan, + struct vxlan_mdb_remote *remote) +{ + WARN_ON_ONCE(!hlist_empty(&remote->src_list)); + vxlan_mdb_remote_rdst_fini(rtnl_dereference(remote->rd)); +} + +static struct vxlan_mdb_src_entry * +vxlan_mdb_remote_src_entry_lookup(const struct vxlan_mdb_remote *remote, + const union vxlan_addr *addr) +{ + struct vxlan_mdb_src_entry *ent; + + hlist_for_each_entry(ent, &remote->src_list, node) { + if (vxlan_addr_equal(&ent->addr, addr)) + return ent; + } + + return NULL; +} + +static struct vxlan_mdb_src_entry * +vxlan_mdb_remote_src_entry_add(struct vxlan_mdb_remote *remote, + const union vxlan_addr *addr) +{ + struct vxlan_mdb_src_entry *ent; + + ent = kzalloc(sizeof(*ent), GFP_KERNEL); + if (!ent) + return NULL; + + ent->addr = *addr; + hlist_add_head(&ent->node, &remote->src_list); + + return ent; +} + +static void +vxlan_mdb_remote_src_entry_del(struct vxlan_mdb_src_entry *ent) +{ + hlist_del(&ent->node); + kfree(ent); +} + +static int +vxlan_mdb_remote_src_fwd_add(const struct vxlan_mdb_config *cfg, + const union vxlan_addr *addr, + struct netlink_ext_ack *extack) +{ + struct vxlan_mdb_config sg_cfg; + + memset(&sg_cfg, 0, sizeof(sg_cfg)); + sg_cfg.vxlan = cfg->vxlan; + sg_cfg.group.src = *addr; + sg_cfg.group.dst = cfg->group.dst; + sg_cfg.group.vni = cfg->group.vni; + INIT_LIST_HEAD(&sg_cfg.src_list); + sg_cfg.remote_ip = cfg->remote_ip; + sg_cfg.remote_ifindex = cfg->remote_ifindex; + sg_cfg.remote_vni = cfg->remote_vni; + sg_cfg.remote_port = cfg->remote_port; + sg_cfg.nlflags = cfg->nlflags; + sg_cfg.filter_mode = MCAST_INCLUDE; + if (cfg->filter_mode == MCAST_EXCLUDE) + sg_cfg.flags = VXLAN_MDB_REMOTE_F_BLOCKED; + sg_cfg.rt_protocol = cfg->rt_protocol; + + return __vxlan_mdb_add(&sg_cfg, extack); +} + +static void +vxlan_mdb_remote_src_fwd_del(struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry_key *group, + const struct vxlan_mdb_remote *remote, + const union vxlan_addr *addr) +{ + struct vxlan_rdst *rd = rtnl_dereference(remote->rd); + struct vxlan_mdb_config sg_cfg; + + memset(&sg_cfg, 0, sizeof(sg_cfg)); + sg_cfg.vxlan = vxlan; + sg_cfg.group.src = *addr; + sg_cfg.group.dst = group->dst; + sg_cfg.group.vni = group->vni; + INIT_LIST_HEAD(&sg_cfg.src_list); + sg_cfg.remote_ip = rd->remote_ip; + + __vxlan_mdb_del(&sg_cfg, NULL); +} + +static int +vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg, + struct vxlan_mdb_remote *remote, + const struct vxlan_mdb_config_src_entry *src, + struct netlink_ext_ack *extack) +{ + struct vxlan_mdb_src_entry *ent; + int err; + + ent = vxlan_mdb_remote_src_entry_lookup(remote, &src->addr); + if (!ent) { + ent = vxlan_mdb_remote_src_entry_add(remote, &src->addr); + if (!ent) + return -ENOMEM; + } else if (!(cfg->nlflags & NLM_F_REPLACE)) { + NL_SET_ERR_MSG_MOD(extack, "Source entry already exists"); + return -EEXIST; + } + + err = vxlan_mdb_remote_src_fwd_add(cfg, &ent->addr, extack); + if (err) + goto err_src_del; + + /* Clear flags in case source entry was marked for deletion as part of + * replace flow. + */ + ent->flags = 0; + + return 0; + +err_src_del: + vxlan_mdb_remote_src_entry_del(ent); + return err; +} + +static void vxlan_mdb_remote_src_del(struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry_key *group, + const struct vxlan_mdb_remote *remote, + struct vxlan_mdb_src_entry *ent) +{ + vxlan_mdb_remote_src_fwd_del(vxlan, group, remote, &ent->addr); + vxlan_mdb_remote_src_entry_del(ent); +} + +static int vxlan_mdb_remote_srcs_add(const struct vxlan_mdb_config *cfg, + struct vxlan_mdb_remote *remote, + struct netlink_ext_ack *extack) +{ + struct vxlan_mdb_config_src_entry *src; + struct vxlan_mdb_src_entry *ent; + struct hlist_node *tmp; + int err; + + list_for_each_entry(src, &cfg->src_list, node) { + err = vxlan_mdb_remote_src_add(cfg, remote, src, extack); + if (err) + goto err_src_del; + } + + return 0; + +err_src_del: + hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) + vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, ent); + return err; +} + +static void vxlan_mdb_remote_srcs_del(struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry_key *group, + struct vxlan_mdb_remote *remote) +{ + struct vxlan_mdb_src_entry *ent; + struct hlist_node *tmp; + + hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) + vxlan_mdb_remote_src_del(vxlan, group, remote, ent); +} + +static size_t +vxlan_mdb_nlmsg_src_list_size(const struct vxlan_mdb_entry_key *group, + const struct vxlan_mdb_remote *remote) +{ + struct vxlan_mdb_src_entry *ent; + size_t nlmsg_size; + + if (hlist_empty(&remote->src_list)) + return 0; + + /* MDBA_MDB_EATTR_SRC_LIST */ + nlmsg_size = nla_total_size(0); + + hlist_for_each_entry(ent, &remote->src_list, node) { + /* MDBA_MDB_SRCLIST_ENTRY */ + nlmsg_size += nla_total_size(0) + + /* MDBA_MDB_SRCATTR_ADDRESS */ + nla_total_size(vxlan_addr_size(&group->dst)) + + /* MDBA_MDB_SRCATTR_TIMER */ + nla_total_size(sizeof(u8)); + } + + return nlmsg_size; +} + +static size_t vxlan_mdb_nlmsg_size(const struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry *mdb_entry, + const struct vxlan_mdb_remote *remote) +{ + const struct vxlan_mdb_entry_key *group = &mdb_entry->key; + struct vxlan_rdst *rd = rtnl_dereference(remote->rd); + size_t nlmsg_size; + + nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) + + /* MDBA_MDB */ + nla_total_size(0) + + /* MDBA_MDB_ENTRY */ + nla_total_size(0) + + /* MDBA_MDB_ENTRY_INFO */ + nla_total_size(sizeof(struct br_mdb_entry)) + + /* MDBA_MDB_EATTR_TIMER */ + nla_total_size(sizeof(u32)); + /* MDBA_MDB_EATTR_SOURCE */ + if (vxlan_mdb_is_sg(group)) + nlmsg_size += nla_total_size(vxlan_addr_size(&group->dst)); + /* MDBA_MDB_EATTR_RTPROT */ + nlmsg_size += nla_total_size(sizeof(u8)); + /* MDBA_MDB_EATTR_SRC_LIST */ + nlmsg_size += vxlan_mdb_nlmsg_src_list_size(group, remote); + /* MDBA_MDB_EATTR_GROUP_MODE */ + nlmsg_size += nla_total_size(sizeof(u8)); + /* MDBA_MDB_EATTR_DST */ + nlmsg_size += nla_total_size(vxlan_addr_size(&rd->remote_ip)); + /* MDBA_MDB_EATTR_DST_PORT */ + if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port) + nlmsg_size += nla_total_size(sizeof(u16)); + /* MDBA_MDB_EATTR_VNI */ + if (rd->remote_vni != vxlan->default_dst.remote_vni) + nlmsg_size += nla_total_size(sizeof(u32)); + /* MDBA_MDB_EATTR_IFINDEX */ + if (rd->remote_ifindex) + nlmsg_size += nla_total_size(sizeof(u32)); + /* MDBA_MDB_EATTR_SRC_VNI */ + if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && group->vni) + nlmsg_size += nla_total_size(sizeof(u32)); + + return nlmsg_size; +} + +static int vxlan_mdb_nlmsg_fill(const struct vxlan_dev *vxlan, + struct sk_buff *skb, + const struct vxlan_mdb_entry *mdb_entry, + const struct vxlan_mdb_remote *remote, + int type) +{ + struct nlattr *mdb_nest, *mdb_entry_nest; + struct br_port_msg *bpm; + struct nlmsghdr *nlh; + + nlh = nlmsg_put(skb, 0, 0, type, sizeof(*bpm), 0); + if (!nlh) + return -EMSGSIZE; + + bpm = nlmsg_data(nlh); + memset(bpm, 0, sizeof(*bpm)); + bpm->family = AF_BRIDGE; + bpm->ifindex = vxlan->dev->ifindex; + + mdb_nest = nla_nest_start_noflag(skb, MDBA_MDB); + if (!mdb_nest) + goto cancel; + mdb_entry_nest = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY); + if (!mdb_entry_nest) + goto cancel; + + if (vxlan_mdb_entry_info_fill(vxlan, skb, mdb_entry, remote)) + goto cancel; + + nla_nest_end(skb, mdb_entry_nest); + nla_nest_end(skb, mdb_nest); + nlmsg_end(skb, nlh); + + return 0; + +cancel: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static void vxlan_mdb_remote_notify(const struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry *mdb_entry, + const struct vxlan_mdb_remote *remote, + int type) +{ + struct net *net = dev_net(vxlan->dev); + struct sk_buff *skb; + int err = -ENOBUFS; + + skb = nlmsg_new(vxlan_mdb_nlmsg_size(vxlan, mdb_entry, remote), + GFP_KERNEL); + if (!skb) + goto errout; + + err = vxlan_mdb_nlmsg_fill(vxlan, skb, mdb_entry, remote, type); + if (err) { + kfree_skb(skb); + goto errout; + } + + rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_KERNEL); + return; +errout: + rtnl_set_sk_err(net, RTNLGRP_MDB, err); +} + +static int +vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg, + const struct vxlan_mdb_entry *mdb_entry, + struct vxlan_mdb_remote *remote, + struct netlink_ext_ack *extack) +{ + struct vxlan_dev *vxlan = cfg->vxlan; + struct vxlan_mdb_src_entry *ent; + struct hlist_node *tmp; + int err; + + hlist_for_each_entry(ent, &remote->src_list, node) + ent->flags |= VXLAN_SGRP_F_DELETE; + + err = vxlan_mdb_remote_srcs_add(cfg, remote, extack); + if (err) + goto err_clear_delete; + + hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) { + if (ent->flags & VXLAN_SGRP_F_DELETE) + vxlan_mdb_remote_src_del(vxlan, &mdb_entry->key, remote, + ent); + } + + return 0; + +err_clear_delete: + hlist_for_each_entry(ent, &remote->src_list, node) + ent->flags &= ~VXLAN_SGRP_F_DELETE; + return err; +} + +static int vxlan_mdb_remote_replace(const struct vxlan_mdb_config *cfg, + const struct vxlan_mdb_entry *mdb_entry, + struct vxlan_mdb_remote *remote, + struct netlink_ext_ack *extack) +{ + struct vxlan_rdst *new_rd, *old_rd = rtnl_dereference(remote->rd); + struct vxlan_dev *vxlan = cfg->vxlan; + int err; + + err = vxlan_mdb_remote_rdst_init(cfg, remote); + if (err) + return err; + new_rd = rtnl_dereference(remote->rd); + + err = vxlan_mdb_remote_srcs_replace(cfg, mdb_entry, remote, extack); + if (err) + goto err_rdst_reset; + + WRITE_ONCE(remote->flags, cfg->flags); + WRITE_ONCE(remote->filter_mode, cfg->filter_mode); + remote->rt_protocol = cfg->rt_protocol; + vxlan_mdb_remote_notify(vxlan, mdb_entry, remote, RTM_NEWMDB); + + vxlan_mdb_remote_rdst_fini(old_rd); + + return 0; + +err_rdst_reset: + rcu_assign_pointer(remote->rd, old_rd); + vxlan_mdb_remote_rdst_fini(new_rd); + return err; +} + +static int vxlan_mdb_remote_add(const struct vxlan_mdb_config *cfg, + struct vxlan_mdb_entry *mdb_entry, + struct netlink_ext_ack *extack) +{ + struct vxlan_mdb_remote *remote; + int err; + + remote = vxlan_mdb_remote_lookup(mdb_entry, &cfg->remote_ip); + if (remote) { + if (!(cfg->nlflags & NLM_F_REPLACE)) { + NL_SET_ERR_MSG_MOD(extack, "Replace not specified and MDB remote entry already exists"); + return -EEXIST; + } + return vxlan_mdb_remote_replace(cfg, mdb_entry, remote, extack); + } + + if (!(cfg->nlflags & NLM_F_CREATE)) { + NL_SET_ERR_MSG_MOD(extack, "Create not specified and entry does not exist"); + return -ENOENT; + } + + remote = kzalloc(sizeof(*remote), GFP_KERNEL); + if (!remote) + return -ENOMEM; + + err = vxlan_mdb_remote_init(cfg, remote); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to initialize remote MDB entry"); + goto err_free_remote; + } + + err = vxlan_mdb_remote_srcs_add(cfg, remote, extack); + if (err) + goto err_remote_fini; + + list_add_rcu(&remote->list, &mdb_entry->remotes); + vxlan_mdb_remote_notify(cfg->vxlan, mdb_entry, remote, RTM_NEWMDB); + + return 0; + +err_remote_fini: + vxlan_mdb_remote_fini(cfg->vxlan, remote); +err_free_remote: + kfree(remote); + return err; +} + +static void vxlan_mdb_remote_del(struct vxlan_dev *vxlan, + struct vxlan_mdb_entry *mdb_entry, + struct vxlan_mdb_remote *remote) +{ + vxlan_mdb_remote_notify(vxlan, mdb_entry, remote, RTM_DELMDB); + list_del_rcu(&remote->list); + vxlan_mdb_remote_srcs_del(vxlan, &mdb_entry->key, remote); + vxlan_mdb_remote_fini(vxlan, remote); + kfree_rcu(remote, rcu); +} + +static struct vxlan_mdb_entry * +vxlan_mdb_entry_get(struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry_key *group) +{ + struct vxlan_mdb_entry *mdb_entry; + int err; + + mdb_entry = vxlan_mdb_entry_lookup(vxlan, group); + if (mdb_entry) + return mdb_entry; + + mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL); + if (!mdb_entry) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&mdb_entry->remotes); + memcpy(&mdb_entry->key, group, sizeof(mdb_entry->key)); + hlist_add_head(&mdb_entry->mdb_node, &vxlan->mdb_list); + + err = rhashtable_lookup_insert_fast(&vxlan->mdb_tbl, + &mdb_entry->rhnode, + vxlan_mdb_rht_params); + if (err) + goto err_free_entry; + + return mdb_entry; + +err_free_entry: + hlist_del(&mdb_entry->mdb_node); + kfree(mdb_entry); + return ERR_PTR(err); +} + +static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan, + struct vxlan_mdb_entry *mdb_entry) +{ + if (!list_empty(&mdb_entry->remotes)) + return; + + rhashtable_remove_fast(&vxlan->mdb_tbl, &mdb_entry->rhnode, + vxlan_mdb_rht_params); + hlist_del(&mdb_entry->mdb_node); + kfree_rcu(mdb_entry, rcu); +} + +static int __vxlan_mdb_add(const struct vxlan_mdb_config *cfg, + struct netlink_ext_ack *extack) +{ + struct vxlan_dev *vxlan = cfg->vxlan; + struct vxlan_mdb_entry *mdb_entry; + int err; + + mdb_entry = vxlan_mdb_entry_get(vxlan, &cfg->group); + if (IS_ERR(mdb_entry)) + return PTR_ERR(mdb_entry); + + err = vxlan_mdb_remote_add(cfg, mdb_entry, extack); + if (err) + goto err_entry_put; + + vxlan->mdb_seq++; + + return 0; + +err_entry_put: + vxlan_mdb_entry_put(vxlan, mdb_entry); + return err; +} + +static int __vxlan_mdb_del(const struct vxlan_mdb_config *cfg, + struct netlink_ext_ack *extack) +{ + struct vxlan_dev *vxlan = cfg->vxlan; + struct vxlan_mdb_entry *mdb_entry; + struct vxlan_mdb_remote *remote; + + mdb_entry = vxlan_mdb_entry_lookup(vxlan, &cfg->group); + if (!mdb_entry) { + NL_SET_ERR_MSG_MOD(extack, "Did not find MDB entry"); + return -ENOENT; + } + + remote = vxlan_mdb_remote_lookup(mdb_entry, &cfg->remote_ip); + if (!remote) { + NL_SET_ERR_MSG_MOD(extack, "Did not find MDB remote entry"); + return -ENOENT; + } + + vxlan_mdb_remote_del(vxlan, mdb_entry, remote); + vxlan_mdb_entry_put(vxlan, mdb_entry); + + vxlan->mdb_seq++; + + return 0; +} + +int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags, + struct netlink_ext_ack *extack) +{ + struct vxlan_mdb_config cfg; + int err; + + ASSERT_RTNL(); + + err = vxlan_mdb_config_init(&cfg, dev, tb, nlmsg_flags, extack); + if (err) + return err; + + err = __vxlan_mdb_add(&cfg, extack); + + vxlan_mdb_config_fini(&cfg); + return err; +} + +int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[], + struct netlink_ext_ack *extack) +{ + struct vxlan_mdb_config cfg; + int err; + + ASSERT_RTNL(); + + err = vxlan_mdb_config_init(&cfg, dev, tb, 0, extack); + if (err) + return err; + + err = __vxlan_mdb_del(&cfg, extack); + + vxlan_mdb_config_fini(&cfg); + return err; +} + +static void vxlan_mdb_check_empty(void *ptr, void *arg) +{ + WARN_ON_ONCE(1); +} + +static void vxlan_mdb_remotes_flush(struct vxlan_dev *vxlan, + struct vxlan_mdb_entry *mdb_entry) +{ + struct vxlan_mdb_remote *remote, *tmp; + + list_for_each_entry_safe(remote, tmp, &mdb_entry->remotes, list) + vxlan_mdb_remote_del(vxlan, mdb_entry, remote); +} + +static void vxlan_mdb_entries_flush(struct vxlan_dev *vxlan) +{ + struct vxlan_mdb_entry *mdb_entry; + struct hlist_node *tmp; + + /* The removal of an entry cannot trigger the removal of another entry + * since entries are always added to the head of the list. + */ + hlist_for_each_entry_safe(mdb_entry, tmp, &vxlan->mdb_list, mdb_node) { + vxlan_mdb_remotes_flush(vxlan, mdb_entry); + vxlan_mdb_entry_put(vxlan, mdb_entry); + } +} + +int vxlan_mdb_init(struct vxlan_dev *vxlan) +{ + int err; + + err = rhashtable_init(&vxlan->mdb_tbl, &vxlan_mdb_rht_params); + if (err) + return err; + + INIT_HLIST_HEAD(&vxlan->mdb_list); + + return 0; +} + +void vxlan_mdb_fini(struct vxlan_dev *vxlan) +{ + vxlan_mdb_entries_flush(vxlan); + rhashtable_free_and_destroy(&vxlan->mdb_tbl, vxlan_mdb_check_empty, + NULL); +} diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index f4977925cb8a..7bcc38faae27 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -110,6 +110,14 @@ static inline int vxlan_nla_put_addr(struct sk_buff *skb, int attr, return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); } +static inline bool vxlan_addr_is_multicast(const union vxlan_addr *ip) +{ + if (ip->sa.sa_family == AF_INET6) + return ipv6_addr_is_multicast(&ip->sin6.sin6_addr); + else + return ipv4_is_multicast(ip->sin.sin_addr.s_addr); +} + #else /* !CONFIG_IPV6 */ static inline @@ -138,8 +146,21 @@ static inline int vxlan_nla_put_addr(struct sk_buff *skb, int attr, return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr); } +static inline bool vxlan_addr_is_multicast(const union vxlan_addr *ip) +{ + return ipv4_is_multicast(ip->sin.sin_addr.s_addr); +} + #endif +static inline size_t vxlan_addr_size(const union vxlan_addr *ip) +{ + if (ip->sa.sa_family == AF_INET6) + return sizeof(struct in6_addr); + else + return sizeof(__be32); +} + static inline struct vxlan_vni_node * vxlan_vnifilter_lookup(struct vxlan_dev *vxlan, __be32 vni) { @@ -206,4 +227,14 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, int rifindex); int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, int rifindex); + +/* vxlan_mdb.c */ +int vxlan_mdb_dump(struct net_device *dev, struct sk_buff *skb, + struct netlink_callback *cb); +int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags, + struct netlink_ext_ack *extack); +int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[], + struct netlink_ext_ack *extack); +int vxlan_mdb_init(struct vxlan_dev *vxlan); +void vxlan_mdb_fini(struct vxlan_dev *vxlan); #endif diff --git a/include/net/vxlan.h b/include/net/vxlan.h index bca5b01af247..110b703d8978 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -3,6 +3,7 @@ #define __NET_VXLAN_H 1 #include +#include #include #include #include @@ -302,6 +303,10 @@ struct vxlan_dev { struct vxlan_vni_group __rcu *vnigrp; struct hlist_head fdb_head[FDB_HASH_SIZE]; + + struct rhashtable mdb_tbl; + struct hlist_head mdb_list; + unsigned int mdb_seq; }; #define VXLAN_F_LEARN 0x01 diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index d60c456710b3..c9d624f528c5 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -633,6 +633,11 @@ enum { MDBA_MDB_EATTR_GROUP_MODE, MDBA_MDB_EATTR_SOURCE, MDBA_MDB_EATTR_RTPROT, + MDBA_MDB_EATTR_DST, + MDBA_MDB_EATTR_DST_PORT, + MDBA_MDB_EATTR_VNI, + MDBA_MDB_EATTR_IFINDEX, + MDBA_MDB_EATTR_SRC_VNI, __MDBA_MDB_EATTR_MAX }; #define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1) @@ -728,6 +733,11 @@ enum { MDBE_ATTR_SRC_LIST, MDBE_ATTR_GROUP_MODE, MDBE_ATTR_RTPROT, + MDBE_ATTR_DST, + MDBE_ATTR_DST_PORT, + MDBE_ATTR_VNI, + MDBE_ATTR_IFINDEX, + MDBE_ATTR_SRC_VNI, __MDBE_ATTR_MAX, }; #define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1) -- cgit v1.2.3 From bc6c6b013ffee36eb555cc0a68aa3d9608e1fad2 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Mar 2023 15:11:52 +0200 Subject: vxlan: mdb: Add an internal flag to indicate MDB usage Add an internal flag to indicate whether MDB entries are configured or not. Set the flag after installing the first MDB entry and clear it before deleting the last one. The flag will be consulted by the data path which will only perform an MDB lookup if the flag is set, thereby keeping the MDB overhead to a minimum when the MDB is not used. Another option would have been to use a static key, but it is global and not per-device, unlike the current approach. Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/vxlan/vxlan_mdb.c | 7 +++++++ include/net/vxlan.h | 1 + 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 129692b3663f..b32b1fb4a74a 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -1185,6 +1185,9 @@ vxlan_mdb_entry_get(struct vxlan_dev *vxlan, if (err) goto err_free_entry; + if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list)) + vxlan->cfg.flags |= VXLAN_F_MDB; + return mdb_entry; err_free_entry: @@ -1199,6 +1202,9 @@ static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan, if (!list_empty(&mdb_entry->remotes)) return; + if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list)) + vxlan->cfg.flags &= ~VXLAN_F_MDB; + rhashtable_remove_fast(&vxlan->mdb_tbl, &mdb_entry->rhnode, vxlan_mdb_rht_params); hlist_del(&mdb_entry->mdb_node); @@ -1336,6 +1342,7 @@ int vxlan_mdb_init(struct vxlan_dev *vxlan) void vxlan_mdb_fini(struct vxlan_dev *vxlan) { vxlan_mdb_entries_flush(vxlan); + WARN_ON_ONCE(vxlan->cfg.flags & VXLAN_F_MDB); rhashtable_free_and_destroy(&vxlan->mdb_tbl, vxlan_mdb_check_empty, NULL); } diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 110b703d8978..b7b2e9abfb37 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -327,6 +327,7 @@ struct vxlan_dev { #define VXLAN_F_IPV6_LINKLOCAL 0x8000 #define VXLAN_F_TTL_INHERIT 0x10000 #define VXLAN_F_VNIFILTER 0x20000 +#define VXLAN_F_MDB 0x40000 /* Flags that are used in the receive path. These flags must match in * order for a socket to be shareable -- cgit v1.2.3 From 0f83e69f44bf8dc8ab48ff0196b3475c1f0f6c07 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Mar 2023 15:11:53 +0200 Subject: vxlan: Add MDB data path support Integrate MDB support into the Tx path of the VXLAN driver, allowing it to selectively forward IP multicast traffic according to the matched MDB entry. If MDB entries are configured (i.e., 'VXLAN_F_MDB' is set) and the packet is an IP multicast packet, perform up to three different lookups according to the following priority: 1. For an (S, G) entry, using {Source VNI, Source IP, Destination IP}. 2. For a (*, G) entry, using {Source VNI, Destination IP}. 3. For the catchall MDB entry (0.0.0.0 or ::), using the source VNI. The catchall MDB entry is similar to the catchall FDB entry (00:00:00:00:00:00) that is currently used to transmit BUM (broadcast, unknown unicast and multicast) traffic. However, unlike the catchall FDB entry, this entry is only used to transmit unregistered IP multicast traffic that is not link-local. Therefore, when configured, the catchall FDB entry will only transmit BULL (broadcast, unknown unicast, link-local multicast) traffic. The catchall MDB entry is useful in deployments where inter-subnet multicast forwarding is used and not all the VTEPs in a tenant domain are members in all the broadcast domains. In such deployments it is advantageous to transmit BULL (broadcast, unknown unicast and link-local multicast) and unregistered IP multicast traffic on different tunnels. If the same tunnel was used, a VTEP only interested in IP multicast traffic would also pull all the BULL traffic and drop it as it is not a member in the originating broadcast domain [1]. If the packet did not match an MDB entry (or if the packet is not an IP multicast packet), return it to the Tx path, allowing it to be forwarded according to the FDB. If the packet did match an MDB entry, forward it to the associated remote VTEPs. However, if the entry is a (*, G) entry and the associated remote is in INCLUDE mode, then skip over it as the source IP is not in its source list (otherwise the packet would have matched on an (S, G) entry). Similarly, if the associated remote is marked as BLOCKED (can only be set on (S, G) entries), then skip over it as well as the remote is in EXCLUDE mode and the source IP is in its source list. [1] https://datatracker.ietf.org/doc/html/draft-ietf-bess-evpn-irb-mcast#section-2.6 Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/vxlan/vxlan_core.c | 15 +++++ drivers/net/vxlan/vxlan_mdb.c | 114 ++++++++++++++++++++++++++++++++++++++ drivers/net/vxlan/vxlan_private.h | 6 ++ 3 files changed, 135 insertions(+) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index a8b26d4f76de..8450768d2300 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2743,6 +2743,21 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) #endif } + if (vxlan->cfg.flags & VXLAN_F_MDB) { + struct vxlan_mdb_entry *mdb_entry; + + rcu_read_lock(); + mdb_entry = vxlan_mdb_entry_skb_get(vxlan, skb, vni); + if (mdb_entry) { + netdev_tx_t ret; + + ret = vxlan_mdb_xmit(vxlan, mdb_entry, skb); + rcu_read_unlock(); + return ret; + } + rcu_read_unlock(); + } + eth = eth_hdr(skb); f = vxlan_find_mac(vxlan, eth->h_dest, vni); did_rsc = false; diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index b32b1fb4a74a..5e041622261a 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -1298,6 +1298,120 @@ int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[], return err; } +struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, + struct sk_buff *skb, + __be32 src_vni) +{ + struct vxlan_mdb_entry *mdb_entry; + struct vxlan_mdb_entry_key group; + + if (!is_multicast_ether_addr(eth_hdr(skb)->h_dest) || + is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) + return NULL; + + /* When not in collect metadata mode, 'src_vni' is zero, but MDB + * entries are stored with the VNI of the VXLAN device. + */ + if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)) + src_vni = vxlan->default_dst.remote_vni; + + memset(&group, 0, sizeof(group)); + group.vni = src_vni; + + switch (skb->protocol) { + case htons(ETH_P_IP): + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + return NULL; + group.dst.sa.sa_family = AF_INET; + group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr; + group.src.sa.sa_family = AF_INET; + group.src.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + break; +#if IS_ENABLED(CONFIG_IPV6) + case htons(ETH_P_IPV6): + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + return NULL; + group.dst.sa.sa_family = AF_INET6; + group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr; + group.src.sa.sa_family = AF_INET6; + group.src.sin6.sin6_addr = ipv6_hdr(skb)->saddr; + break; +#endif + default: + return NULL; + } + + mdb_entry = vxlan_mdb_entry_lookup(vxlan, &group); + if (mdb_entry) + return mdb_entry; + + memset(&group.src, 0, sizeof(group.src)); + mdb_entry = vxlan_mdb_entry_lookup(vxlan, &group); + if (mdb_entry) + return mdb_entry; + + /* No (S, G) or (*, G) found. Look up the all-zeros entry, but only if + * the destination IP address is not link-local multicast since we want + * to transmit such traffic together with broadcast and unknown unicast + * traffic. + */ + switch (skb->protocol) { + case htons(ETH_P_IP): + if (ipv4_is_local_multicast(group.dst.sin.sin_addr.s_addr)) + return NULL; + group.dst.sin.sin_addr.s_addr = 0; + break; +#if IS_ENABLED(CONFIG_IPV6) + case htons(ETH_P_IPV6): + if (ipv6_addr_type(&group.dst.sin6.sin6_addr) & + IPV6_ADDR_LINKLOCAL) + return NULL; + memset(&group.dst.sin6.sin6_addr, 0, + sizeof(group.dst.sin6.sin6_addr)); + break; +#endif + default: + return NULL; + } + + return vxlan_mdb_entry_lookup(vxlan, &group); +} + +netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry *mdb_entry, + struct sk_buff *skb) +{ + struct vxlan_mdb_remote *remote, *fremote = NULL; + __be32 src_vni = mdb_entry->key.vni; + + list_for_each_entry_rcu(remote, &mdb_entry->remotes, list) { + struct sk_buff *skb1; + + if ((vxlan_mdb_is_star_g(&mdb_entry->key) && + READ_ONCE(remote->filter_mode) == MCAST_INCLUDE) || + (READ_ONCE(remote->flags) & VXLAN_MDB_REMOTE_F_BLOCKED)) + continue; + + if (!fremote) { + fremote = remote; + continue; + } + + skb1 = skb_clone(skb, GFP_ATOMIC); + if (skb1) + vxlan_xmit_one(skb1, vxlan->dev, src_vni, + rcu_dereference(remote->rd), false); + } + + if (fremote) + vxlan_xmit_one(skb, vxlan->dev, src_vni, + rcu_dereference(fremote->rd), false); + else + kfree_skb(skb); + + return NETDEV_TX_OK; +} + static void vxlan_mdb_check_empty(void *ptr, void *arg) { WARN_ON_ONCE(1); diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h index 7bcc38faae27..817fa3075842 100644 --- a/drivers/net/vxlan/vxlan_private.h +++ b/drivers/net/vxlan/vxlan_private.h @@ -235,6 +235,12 @@ int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags, struct netlink_ext_ack *extack); int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[], struct netlink_ext_ack *extack); +struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, + struct sk_buff *skb, + __be32 src_vni); +netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan, + const struct vxlan_mdb_entry *mdb_entry, + struct sk_buff *skb); int vxlan_mdb_init(struct vxlan_dev *vxlan); void vxlan_mdb_fini(struct vxlan_dev *vxlan); #endif -- cgit v1.2.3 From 08f876a7d79ed235f90af0373d1e548a71c1f4f6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 15 Mar 2023 15:11:54 +0200 Subject: vxlan: Enable MDB support Now that the VXLAN MDB control and data paths are in place we can expose the VXLAN MDB functionality to user space. Set the VXLAN MDB net device operations to the appropriate functions, thereby allowing the rtnetlink code to reach the VXLAN driver. Signed-off-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/vxlan/vxlan_core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 8450768d2300..e2e5f5dac7e6 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -3083,6 +3083,9 @@ static const struct net_device_ops vxlan_netdev_ether_ops = { .ndo_fdb_del = vxlan_fdb_delete, .ndo_fdb_dump = vxlan_fdb_dump, .ndo_fdb_get = vxlan_fdb_get, + .ndo_mdb_add = vxlan_mdb_add, + .ndo_mdb_del = vxlan_mdb_del, + .ndo_mdb_dump = vxlan_mdb_dump, .ndo_fill_metadata_dst = vxlan_fill_metadata_dst, }; -- cgit v1.2.3 From ef63461caf427a77a04620d74ba90035a712af9c Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 15 Mar 2023 14:46:43 +0000 Subject: net: pcs: xpcs: remove double-read of link state when using AN Phylink does not want the current state of the link when reading the PCS link state - it wants the latched state. Don't double-read the MII status register. Phylink will re-read as necessary to capture transient link-down events as of dbae3388ea9c ("net: phylink: Force retrigger in case of latched link-fail indicator"). The above referenced commit is a dependency for this change, and thus this change should not be backported to any kernel that does not contain the above referenced commit. Fixes: fcb26bd2b6ca ("net: phy: Add Synopsys DesignWare XPCS MDIO module") Signed-off-by: Russell King (Oracle) Signed-off-by: David S. Miller --- drivers/net/pcs/pcs-xpcs.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index bc428a816719..04a685353041 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -321,7 +321,7 @@ static int xpcs_read_fault_c73(struct dw_xpcs *xpcs, return 0; } -static int xpcs_read_link_c73(struct dw_xpcs *xpcs, bool an) +static int xpcs_read_link_c73(struct dw_xpcs *xpcs) { bool link = true; int ret; @@ -333,15 +333,6 @@ static int xpcs_read_link_c73(struct dw_xpcs *xpcs, bool an) if (!(ret & MDIO_STAT1_LSTATUS)) link = false; - if (an) { - ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); - if (ret < 0) - return ret; - - if (!(ret & MDIO_STAT1_LSTATUS)) - link = false; - } - return link; } @@ -935,7 +926,7 @@ static int xpcs_get_state_c73(struct dw_xpcs *xpcs, int ret; /* Link needs to be read first ... */ - state->link = xpcs_read_link_c73(xpcs, state->an_enabled) > 0 ? 1 : 0; + state->link = xpcs_read_link_c73(xpcs) > 0 ? 1 : 0; /* ... and then we check the faults. */ ret = xpcs_read_fault_c73(xpcs, state); -- cgit v1.2.3 From ecec0ebbc6381a5a375f1cf10c4858f24e91e2ef Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 15 Mar 2023 14:46:49 +0000 Subject: net: pcs: lynx: don't print an_enabled in pcs_get_state() an_enabled will be going away, and in any case, pcs_get_state() should not be updating this member. Remove the print. Signed-off-by: Russell King (Oracle) Reviewed-by: Steen Hegelund Signed-off-by: David S. Miller --- drivers/net/pcs/pcs-lynx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c index 3903f3baba2b..622c3de3f3a8 100644 --- a/drivers/net/pcs/pcs-lynx.c +++ b/drivers/net/pcs/pcs-lynx.c @@ -112,11 +112,11 @@ static void lynx_pcs_get_state(struct phylink_pcs *pcs, } dev_dbg(&lynx->mdio->dev, - "mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n", + "mode=%s/%s/%s link=%u an_complete=%u\n", phy_modes(state->interface), phy_speed_to_str(state->speed), phy_duplex_to_str(state->duplex), - state->link, state->an_enabled, state->an_complete); + state->link, state->an_complete); } static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode, -- cgit v1.2.3 From 2e80aeae9f807ac7e967dea7633abea5829c6531 Mon Sep 17 00:00:00 2001 From: Praveen Kaligineedi Date: Wed, 15 Mar 2023 16:33:08 -0700 Subject: gve: XDP support GQI-QPL: helper function changes This patch adds/modifies helper functions needed to add XDP support. Signed-off-by: Praveen Kaligineedi Reviewed-by: Jeroen de Borst Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve.h | 5 ++++ drivers/net/ethernet/google/gve/gve_ethtool.c | 26 ++++++++++------ drivers/net/ethernet/google/gve/gve_main.c | 27 ++++++++++------- drivers/net/ethernet/google/gve/gve_rx.c | 2 +- drivers/net/ethernet/google/gve/gve_rx_dqo.c | 2 +- drivers/net/ethernet/google/gve/gve_tx.c | 43 ++++++++++++++++----------- drivers/net/ethernet/google/gve/gve_utils.c | 6 ++-- drivers/net/ethernet/google/gve/gve_utils.h | 3 +- 8 files changed, 70 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 64eb0442c82f..f52f23198278 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -855,6 +855,11 @@ static inline bool gve_is_gqi(struct gve_priv *priv) priv->queue_format == GVE_GQI_QPL_FORMAT; } +static inline u32 gve_num_tx_queues(struct gve_priv *priv) +{ + return priv->tx_cfg.num_queues; +} + /* buffers */ int gve_alloc_page(struct gve_priv *priv, struct device *dev, struct page **page, dma_addr_t *dma, diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index ce574d097e28..5b6e31812fae 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -81,8 +81,10 @@ static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { struct gve_priv *priv = netdev_priv(netdev); char *s = (char *)data; + int num_tx_queues; int i, j; + num_tx_queues = gve_num_tx_queues(priv); switch (stringset) { case ETH_SS_STATS: memcpy(s, *gve_gstrings_main_stats, @@ -97,7 +99,7 @@ static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } } - for (i = 0; i < priv->tx_cfg.num_queues; i++) { + for (i = 0; i < num_tx_queues; i++) { for (j = 0; j < NUM_GVE_TX_CNTS; j++) { snprintf(s, ETH_GSTRING_LEN, gve_gstrings_tx_stats[j], i); @@ -124,12 +126,14 @@ static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data) static int gve_get_sset_count(struct net_device *netdev, int sset) { struct gve_priv *priv = netdev_priv(netdev); + int num_tx_queues; + num_tx_queues = gve_num_tx_queues(priv); switch (sset) { case ETH_SS_STATS: return GVE_MAIN_STATS_LEN + GVE_ADMINQ_STATS_LEN + (priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS) + - (priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS); + (num_tx_queues * NUM_GVE_TX_CNTS); case ETH_SS_PRIV_FLAGS: return GVE_PRIV_FLAGS_STR_LEN; default: @@ -153,18 +157,20 @@ gve_get_ethtool_stats(struct net_device *netdev, struct gve_priv *priv; bool skip_nic_stats; unsigned int start; + int num_tx_queues; int ring; int i, j; ASSERT_RTNL(); priv = netdev_priv(netdev); + num_tx_queues = gve_num_tx_queues(priv); report_stats = priv->stats_report->stats; rx_qid_to_stats_idx = kmalloc_array(priv->rx_cfg.num_queues, sizeof(int), GFP_KERNEL); if (!rx_qid_to_stats_idx) return; - tx_qid_to_stats_idx = kmalloc_array(priv->tx_cfg.num_queues, + tx_qid_to_stats_idx = kmalloc_array(num_tx_queues, sizeof(int), GFP_KERNEL); if (!tx_qid_to_stats_idx) { kfree(rx_qid_to_stats_idx); @@ -195,7 +201,7 @@ gve_get_ethtool_stats(struct net_device *netdev, } } for (tx_pkts = 0, tx_bytes = 0, tx_dropped = 0, ring = 0; - ring < priv->tx_cfg.num_queues; ring++) { + ring < num_tx_queues; ring++) { if (priv->tx) { do { start = @@ -232,7 +238,7 @@ gve_get_ethtool_stats(struct net_device *netdev, i = GVE_MAIN_STATS_LEN; /* For rx cross-reporting stats, start from nic rx stats in report */ - base_stats_idx = GVE_TX_STATS_REPORT_NUM * priv->tx_cfg.num_queues + + base_stats_idx = GVE_TX_STATS_REPORT_NUM * num_tx_queues + GVE_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues; max_stats_idx = NIC_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues + base_stats_idx; @@ -298,7 +304,7 @@ gve_get_ethtool_stats(struct net_device *netdev, /* For tx cross-reporting stats, start from nic tx stats in report */ base_stats_idx = max_stats_idx; - max_stats_idx = NIC_TX_STATS_REPORT_NUM * priv->tx_cfg.num_queues + + max_stats_idx = NIC_TX_STATS_REPORT_NUM * num_tx_queues + max_stats_idx; /* Preprocess the stats report for tx, map queue id to start index */ skip_nic_stats = false; @@ -316,7 +322,7 @@ gve_get_ethtool_stats(struct net_device *netdev, } /* walk TX rings */ if (priv->tx) { - for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) { + for (ring = 0; ring < num_tx_queues; ring++) { struct gve_tx_ring *tx = &priv->tx[ring]; if (gve_is_gqi(priv)) { @@ -355,7 +361,7 @@ gve_get_ethtool_stats(struct net_device *netdev, } } } else { - i += priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS; + i += num_tx_queues * NUM_GVE_TX_CNTS; } kfree(rx_qid_to_stats_idx); @@ -502,7 +508,9 @@ static int gve_set_priv_flags(struct net_device *netdev, u32 flags) { struct gve_priv *priv = netdev_priv(netdev); u64 ori_flags, new_flags; + int num_tx_queues; + num_tx_queues = gve_num_tx_queues(priv); ori_flags = READ_ONCE(priv->ethtool_flags); new_flags = ori_flags; @@ -522,7 +530,7 @@ static int gve_set_priv_flags(struct net_device *netdev, u32 flags) /* delete report stats timer. */ if (!(flags & BIT(0)) && (ori_flags & BIT(0))) { int tx_stats_num = GVE_TX_STATS_REPORT_NUM * - priv->tx_cfg.num_queues; + num_tx_queues; int rx_stats_num = GVE_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 07111c241e0e..3cfdeeb74f60 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -90,8 +90,10 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s) struct gve_priv *priv = netdev_priv(dev); unsigned int start; u64 packets, bytes; + int num_tx_queues; int ring; + num_tx_queues = gve_num_tx_queues(priv); if (priv->rx) { for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) { do { @@ -106,7 +108,7 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s) } } if (priv->tx) { - for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) { + for (ring = 0; ring < num_tx_queues; ring++) { do { start = u64_stats_fetch_begin(&priv->tx[ring].statss); @@ -180,7 +182,7 @@ static int gve_alloc_stats_report(struct gve_priv *priv) int tx_stats_num, rx_stats_num; tx_stats_num = (GVE_TX_STATS_REPORT_NUM + NIC_TX_STATS_REPORT_NUM) * - priv->tx_cfg.num_queues; + gve_num_tx_queues(priv); rx_stats_num = (GVE_RX_STATS_REPORT_NUM + NIC_RX_STATS_REPORT_NUM) * priv->rx_cfg.num_queues; priv->stats_report_len = struct_size(priv->stats_report, stats, @@ -622,20 +624,21 @@ static int gve_unregister_qpls(struct gve_priv *priv) static int gve_create_rings(struct gve_priv *priv) { + int num_tx_queues = gve_num_tx_queues(priv); int err; int i; - err = gve_adminq_create_tx_queues(priv, priv->tx_cfg.num_queues); + err = gve_adminq_create_tx_queues(priv, num_tx_queues); if (err) { netif_err(priv, drv, priv->dev, "failed to create %d tx queues\n", - priv->tx_cfg.num_queues); + num_tx_queues); /* This failure will trigger a reset - no need to clean * up */ return err; } netif_dbg(priv, drv, priv->dev, "created %d tx queues\n", - priv->tx_cfg.num_queues); + num_tx_queues); err = gve_adminq_create_rx_queues(priv, priv->rx_cfg.num_queues); if (err) { @@ -675,7 +678,7 @@ static void add_napi_init_sync_stats(struct gve_priv *priv, int i; /* Add tx napi & init sync stats*/ - for (i = 0; i < priv->tx_cfg.num_queues; i++) { + for (i = 0; i < gve_num_tx_queues(priv); i++) { int ntfy_idx = gve_tx_idx_to_ntfy(priv, i); u64_stats_init(&priv->tx[i].statss); @@ -753,9 +756,10 @@ free_tx: static int gve_destroy_rings(struct gve_priv *priv) { + int num_tx_queues = gve_num_tx_queues(priv); int err; - err = gve_adminq_destroy_tx_queues(priv, priv->tx_cfg.num_queues); + err = gve_adminq_destroy_tx_queues(priv, num_tx_queues); if (err) { netif_err(priv, drv, priv->dev, "failed to destroy tx queues\n"); @@ -784,11 +788,12 @@ static void gve_rx_free_rings(struct gve_priv *priv) static void gve_free_rings(struct gve_priv *priv) { + int num_tx_queues = gve_num_tx_queues(priv); int ntfy_idx; int i; if (priv->tx) { - for (i = 0; i < priv->tx_cfg.num_queues; i++) { + for (i = 0; i < num_tx_queues; i++) { ntfy_idx = gve_tx_idx_to_ntfy(priv, i); gve_remove_napi(priv, ntfy_idx); } @@ -1118,7 +1123,7 @@ static void gve_turndown(struct gve_priv *priv) return; /* Disable napi to prevent more work from coming in */ - for (idx = 0; idx < priv->tx_cfg.num_queues; idx++) { + for (idx = 0; idx < gve_num_tx_queues(priv); idx++) { int ntfy_idx = gve_tx_idx_to_ntfy(priv, idx); struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; @@ -1146,7 +1151,7 @@ static void gve_turnup(struct gve_priv *priv) netif_tx_start_all_queues(priv->dev); /* Enable napi and unmask interrupts for all queues */ - for (idx = 0; idx < priv->tx_cfg.num_queues; idx++) { + for (idx = 0; idx < gve_num_tx_queues(priv); idx++) { int ntfy_idx = gve_tx_idx_to_ntfy(priv, idx); struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; @@ -1306,7 +1311,7 @@ void gve_handle_report_stats(struct gve_priv *priv) be64_add_cpu(&priv->stats_report->written_count, 1); /* tx stats */ if (priv->tx) { - for (idx = 0; idx < priv->tx_cfg.num_queues; idx++) { + for (idx = 0; idx < gve_num_tx_queues(priv); idx++) { u32 last_completion = 0; u32 tx_frames = 0; diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index 1f55137722b0..db1c74b1d7d3 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -556,7 +556,7 @@ static struct sk_buff *gve_rx_skb(struct gve_priv *priv, struct gve_rx_ring *rx, if (len <= priv->rx_copybreak && is_only_frag) { /* Just copy small packets */ - skb = gve_rx_copy(netdev, napi, page_info, len, GVE_RX_PAD); + skb = gve_rx_copy(netdev, napi, page_info, len); if (skb) { u64_stats_update_begin(&rx->statss); rx->rx_copied_pkt++; diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 630f42a3037b..e57b73eb70f6 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -568,7 +568,7 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, if (eop && buf_len <= priv->rx_copybreak) { rx->ctx.skb_head = gve_rx_copy(priv->dev, napi, - &buf_state->page_info, buf_len, 0); + &buf_state->page_info, buf_len); if (unlikely(!rx->ctx.skb_head)) goto error; rx->ctx.skb_tail = rx->ctx.skb_head; diff --git a/drivers/net/ethernet/google/gve/gve_tx.c b/drivers/net/ethernet/google/gve/gve_tx.c index 4888bf05fbed..0fb052ce9e0b 100644 --- a/drivers/net/ethernet/google/gve/gve_tx.c +++ b/drivers/net/ethernet/google/gve/gve_tx.c @@ -374,18 +374,18 @@ static int gve_maybe_stop_tx(struct gve_priv *priv, struct gve_tx_ring *tx, } static void gve_tx_fill_pkt_desc(union gve_tx_desc *pkt_desc, - struct sk_buff *skb, bool is_gso, + u16 csum_offset, u8 ip_summed, bool is_gso, int l4_hdr_offset, u32 desc_cnt, - u16 hlen, u64 addr) + u16 hlen, u64 addr, u16 pkt_len) { /* l4_hdr_offset and csum_offset are in units of 16-bit words */ if (is_gso) { pkt_desc->pkt.type_flags = GVE_TXD_TSO | GVE_TXF_L4CSUM; - pkt_desc->pkt.l4_csum_offset = skb->csum_offset >> 1; + pkt_desc->pkt.l4_csum_offset = csum_offset >> 1; pkt_desc->pkt.l4_hdr_offset = l4_hdr_offset >> 1; - } else if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + } else if (likely(ip_summed == CHECKSUM_PARTIAL)) { pkt_desc->pkt.type_flags = GVE_TXD_STD | GVE_TXF_L4CSUM; - pkt_desc->pkt.l4_csum_offset = skb->csum_offset >> 1; + pkt_desc->pkt.l4_csum_offset = csum_offset >> 1; pkt_desc->pkt.l4_hdr_offset = l4_hdr_offset >> 1; } else { pkt_desc->pkt.type_flags = GVE_TXD_STD; @@ -393,7 +393,7 @@ static void gve_tx_fill_pkt_desc(union gve_tx_desc *pkt_desc, pkt_desc->pkt.l4_hdr_offset = 0; } pkt_desc->pkt.desc_cnt = desc_cnt; - pkt_desc->pkt.len = cpu_to_be16(skb->len); + pkt_desc->pkt.len = cpu_to_be16(pkt_len); pkt_desc->pkt.seg_len = cpu_to_be16(hlen); pkt_desc->pkt.seg_addr = cpu_to_be64(addr); } @@ -412,15 +412,16 @@ static void gve_tx_fill_mtd_desc(union gve_tx_desc *mtd_desc, } static void gve_tx_fill_seg_desc(union gve_tx_desc *seg_desc, - struct sk_buff *skb, bool is_gso, + u16 l3_offset, u16 gso_size, + bool is_gso_v6, bool is_gso, u16 len, u64 addr) { seg_desc->seg.type_flags = GVE_TXD_SEG; if (is_gso) { - if (skb_is_gso_v6(skb)) + if (is_gso_v6) seg_desc->seg.type_flags |= GVE_TXSF_IPV6; - seg_desc->seg.l3_offset = skb_network_offset(skb) >> 1; - seg_desc->seg.mss = cpu_to_be16(skb_shinfo(skb)->gso_size); + seg_desc->seg.l3_offset = l3_offset >> 1; + seg_desc->seg.mss = cpu_to_be16(gso_size); } seg_desc->seg.seg_len = cpu_to_be16(len); seg_desc->seg.seg_addr = cpu_to_be64(addr); @@ -473,9 +474,10 @@ static int gve_tx_add_skb_copy(struct gve_priv *priv, struct gve_tx_ring *tx, st payload_nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, skb->len - hlen, &info->iov[payload_iov]); - gve_tx_fill_pkt_desc(pkt_desc, skb, is_gso, l4_hdr_offset, + gve_tx_fill_pkt_desc(pkt_desc, skb->csum_offset, skb->ip_summed, + is_gso, l4_hdr_offset, 1 + mtd_desc_nr + payload_nfrags, hlen, - info->iov[hdr_nfrags - 1].iov_offset); + info->iov[hdr_nfrags - 1].iov_offset, skb->len); skb_copy_bits(skb, 0, tx->tx_fifo.base + info->iov[hdr_nfrags - 1].iov_offset, @@ -494,7 +496,9 @@ static int gve_tx_add_skb_copy(struct gve_priv *priv, struct gve_tx_ring *tx, st next_idx = (tx->req + 1 + mtd_desc_nr + i - payload_iov) & tx->mask; seg_desc = &tx->desc[next_idx]; - gve_tx_fill_seg_desc(seg_desc, skb, is_gso, + gve_tx_fill_seg_desc(seg_desc, skb_network_offset(skb), + skb_shinfo(skb)->gso_size, + skb_is_gso_v6(skb), is_gso, info->iov[i].iov_len, info->iov[i].iov_offset); @@ -552,8 +556,9 @@ static int gve_tx_add_skb_no_copy(struct gve_priv *priv, struct gve_tx_ring *tx, if (mtd_desc_nr) num_descriptors++; - gve_tx_fill_pkt_desc(pkt_desc, skb, is_gso, l4_hdr_offset, - num_descriptors, hlen, addr); + gve_tx_fill_pkt_desc(pkt_desc, skb->csum_offset, skb->ip_summed, + is_gso, l4_hdr_offset, + num_descriptors, hlen, addr, skb->len); if (mtd_desc_nr) { idx = (idx + 1) & tx->mask; @@ -569,7 +574,9 @@ static int gve_tx_add_skb_no_copy(struct gve_priv *priv, struct gve_tx_ring *tx, addr += hlen; idx = (idx + 1) & tx->mask; seg_desc = &tx->desc[idx]; - gve_tx_fill_seg_desc(seg_desc, skb, is_gso, len, addr); + gve_tx_fill_seg_desc(seg_desc, skb_network_offset(skb), + skb_shinfo(skb)->gso_size, + skb_is_gso_v6(skb), is_gso, len, addr); } for (i = 0; i < shinfo->nr_frags; i++) { @@ -587,7 +594,9 @@ static int gve_tx_add_skb_no_copy(struct gve_priv *priv, struct gve_tx_ring *tx, dma_unmap_len_set(&tx->info[idx], len, len); dma_unmap_addr_set(&tx->info[idx], dma, addr); - gve_tx_fill_seg_desc(seg_desc, skb, is_gso, len, addr); + gve_tx_fill_seg_desc(seg_desc, skb_network_offset(skb), + skb_shinfo(skb)->gso_size, + skb_is_gso_v6(skb), is_gso, len, addr); } return num_descriptors; diff --git a/drivers/net/ethernet/google/gve/gve_utils.c b/drivers/net/ethernet/google/gve/gve_utils.c index 6ba46adaaee3..26e08d753270 100644 --- a/drivers/net/ethernet/google/gve/gve_utils.c +++ b/drivers/net/ethernet/google/gve/gve_utils.c @@ -49,10 +49,10 @@ void gve_rx_add_to_block(struct gve_priv *priv, int queue_idx) } struct sk_buff *gve_rx_copy(struct net_device *dev, struct napi_struct *napi, - struct gve_rx_slot_page_info *page_info, u16 len, - u16 padding) + struct gve_rx_slot_page_info *page_info, u16 len) { - void *va = page_info->page_address + padding + page_info->page_offset; + void *va = page_info->page_address + page_info->page_offset + + page_info->pad; struct sk_buff *skb; skb = napi_alloc_skb(napi, len); diff --git a/drivers/net/ethernet/google/gve/gve_utils.h b/drivers/net/ethernet/google/gve/gve_utils.h index 79595940b351..324fd98a6112 100644 --- a/drivers/net/ethernet/google/gve/gve_utils.h +++ b/drivers/net/ethernet/google/gve/gve_utils.h @@ -18,8 +18,7 @@ void gve_rx_remove_from_block(struct gve_priv *priv, int queue_idx); void gve_rx_add_to_block(struct gve_priv *priv, int queue_idx); struct sk_buff *gve_rx_copy(struct net_device *dev, struct napi_struct *napi, - struct gve_rx_slot_page_info *page_info, u16 len, - u16 pad); + struct gve_rx_slot_page_info *page_info, u16 len); /* Decrement pagecnt_bias. Set it back to INT_MAX if it reached zero. */ void gve_dec_pagecnt_bias(struct gve_rx_slot_page_info *page_info); -- cgit v1.2.3 From 7fc2bf78a430f975e53f2b1e87e99d8f83cafd11 Mon Sep 17 00:00:00 2001 From: Praveen Kaligineedi Date: Wed, 15 Mar 2023 16:33:09 -0700 Subject: gve: Changes to add new TX queues Changes to enable adding and removing TX queues without calling gve_close() and gve_open(). Made the following changes: 1) priv->tx, priv->rx and priv->qpls arrays are allocated based on max tx queues and max rx queues 2) Changed gve_adminq_create_tx_queues(), gve_adminq_destroy_tx_queues(), gve_tx_alloc_rings() and gve_tx_free_rings() functions to add/remove a subset of TX queues rather than all the TX queues. Signed-off-by: Praveen Kaligineedi Reviewed-by: Jeroen de Borst Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve.h | 45 ++++++++++----- drivers/net/ethernet/google/gve/gve_adminq.c | 8 +-- drivers/net/ethernet/google/gve/gve_adminq.h | 4 +- drivers/net/ethernet/google/gve/gve_main.c | 83 ++++++++++++++++++++-------- drivers/net/ethernet/google/gve/gve_rx.c | 2 +- drivers/net/ethernet/google/gve/gve_tx.c | 12 ++-- 6 files changed, 104 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index f52f23198278..f354a6448c25 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -798,16 +798,35 @@ static inline u32 gve_num_rx_qpls(struct gve_priv *priv) return priv->rx_cfg.num_queues; } +static inline u32 gve_tx_qpl_id(struct gve_priv *priv, int tx_qid) +{ + return tx_qid; +} + +static inline u32 gve_rx_qpl_id(struct gve_priv *priv, int rx_qid) +{ + return priv->tx_cfg.max_queues + rx_qid; +} + +static inline u32 gve_tx_start_qpl_id(struct gve_priv *priv) +{ + return gve_tx_qpl_id(priv, 0); +} + +static inline u32 gve_rx_start_qpl_id(struct gve_priv *priv) +{ + return gve_rx_qpl_id(priv, 0); +} + /* Returns a pointer to the next available tx qpl in the list of qpls */ static inline -struct gve_queue_page_list *gve_assign_tx_qpl(struct gve_priv *priv) +struct gve_queue_page_list *gve_assign_tx_qpl(struct gve_priv *priv, int tx_qid) { - int id = find_first_zero_bit(priv->qpl_cfg.qpl_id_map, - priv->qpl_cfg.qpl_map_size); + int id = gve_tx_qpl_id(priv, tx_qid); - /* we are out of tx qpls */ - if (id >= gve_num_tx_qpls(priv)) + /* QPL already in use */ + if (test_bit(id, priv->qpl_cfg.qpl_id_map)) return NULL; set_bit(id, priv->qpl_cfg.qpl_id_map); @@ -817,14 +836,12 @@ struct gve_queue_page_list *gve_assign_tx_qpl(struct gve_priv *priv) /* Returns a pointer to the next available rx qpl in the list of qpls */ static inline -struct gve_queue_page_list *gve_assign_rx_qpl(struct gve_priv *priv) +struct gve_queue_page_list *gve_assign_rx_qpl(struct gve_priv *priv, int rx_qid) { - int id = find_next_zero_bit(priv->qpl_cfg.qpl_id_map, - priv->qpl_cfg.qpl_map_size, - gve_num_tx_qpls(priv)); + int id = gve_rx_qpl_id(priv, rx_qid); - /* we are out of rx qpls */ - if (id == gve_num_tx_qpls(priv) + gve_num_rx_qpls(priv)) + /* QPL already in use */ + if (test_bit(id, priv->qpl_cfg.qpl_id_map)) return NULL; set_bit(id, priv->qpl_cfg.qpl_id_map); @@ -843,7 +860,7 @@ static inline void gve_unassign_qpl(struct gve_priv *priv, int id) static inline enum dma_data_direction gve_qpl_dma_dir(struct gve_priv *priv, int id) { - if (id < gve_num_tx_qpls(priv)) + if (id < gve_rx_start_qpl_id(priv)) return DMA_TO_DEVICE; else return DMA_FROM_DEVICE; @@ -869,8 +886,8 @@ void gve_free_page(struct device *dev, struct page *page, dma_addr_t dma, /* tx handling */ netdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev); bool gve_tx_poll(struct gve_notify_block *block, int budget); -int gve_tx_alloc_rings(struct gve_priv *priv); -void gve_tx_free_rings_gqi(struct gve_priv *priv); +int gve_tx_alloc_rings(struct gve_priv *priv, int start_id, int num_rings); +void gve_tx_free_rings_gqi(struct gve_priv *priv, int start_id, int num_rings); u32 gve_tx_load_event_counter(struct gve_priv *priv, struct gve_tx_ring *tx); bool gve_tx_clean_pending(struct gve_priv *priv, struct gve_tx_ring *tx); diff --git a/drivers/net/ethernet/google/gve/gve_adminq.c b/drivers/net/ethernet/google/gve/gve_adminq.c index 60061288ad9d..252974202a3f 100644 --- a/drivers/net/ethernet/google/gve/gve_adminq.c +++ b/drivers/net/ethernet/google/gve/gve_adminq.c @@ -516,12 +516,12 @@ static int gve_adminq_create_tx_queue(struct gve_priv *priv, u32 queue_index) return gve_adminq_issue_cmd(priv, &cmd); } -int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 num_queues) +int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues) { int err; int i; - for (i = 0; i < num_queues; i++) { + for (i = start_id; i < start_id + num_queues; i++) { err = gve_adminq_create_tx_queue(priv, i); if (err) return err; @@ -604,12 +604,12 @@ static int gve_adminq_destroy_tx_queue(struct gve_priv *priv, u32 queue_index) return 0; } -int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 num_queues) +int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues) { int err; int i; - for (i = 0; i < num_queues; i++) { + for (i = start_id; i < start_id + num_queues; i++) { err = gve_adminq_destroy_tx_queue(priv, i); if (err) return err; diff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h index cf29662e6ad1..f894beb3deaf 100644 --- a/drivers/net/ethernet/google/gve/gve_adminq.h +++ b/drivers/net/ethernet/google/gve/gve_adminq.h @@ -410,8 +410,8 @@ int gve_adminq_configure_device_resources(struct gve_priv *priv, dma_addr_t db_array_bus_addr, u32 num_ntfy_blks); int gve_adminq_deconfigure_device_resources(struct gve_priv *priv); -int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 num_queues); -int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 queue_id); +int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues); +int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues); int gve_adminq_create_rx_queues(struct gve_priv *priv, u32 num_queues); int gve_adminq_destroy_rx_queues(struct gve_priv *priv, u32 queue_id); int gve_adminq_register_page_list(struct gve_priv *priv, diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 3cfdeeb74f60..160ca77c2751 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -584,11 +584,26 @@ static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) static int gve_register_qpls(struct gve_priv *priv) { - int num_qpls = gve_num_tx_qpls(priv) + gve_num_rx_qpls(priv); + int start_id; int err; int i; - for (i = 0; i < num_qpls; i++) { + start_id = gve_tx_start_qpl_id(priv); + for (i = start_id; i < start_id + gve_num_tx_qpls(priv); i++) { + err = gve_adminq_register_page_list(priv, &priv->qpls[i]); + if (err) { + netif_err(priv, drv, priv->dev, + "failed to register queue page list %d\n", + priv->qpls[i].id); + /* This failure will trigger a reset - no need to clean + * up + */ + return err; + } + } + + start_id = gve_rx_start_qpl_id(priv); + for (i = start_id; i < start_id + gve_num_rx_qpls(priv); i++) { err = gve_adminq_register_page_list(priv, &priv->qpls[i]); if (err) { netif_err(priv, drv, priv->dev, @@ -605,11 +620,24 @@ static int gve_register_qpls(struct gve_priv *priv) static int gve_unregister_qpls(struct gve_priv *priv) { - int num_qpls = gve_num_tx_qpls(priv) + gve_num_rx_qpls(priv); + int start_id; int err; int i; - for (i = 0; i < num_qpls; i++) { + start_id = gve_tx_start_qpl_id(priv); + for (i = start_id; i < start_id + gve_num_tx_qpls(priv); i++) { + err = gve_adminq_unregister_page_list(priv, priv->qpls[i].id); + /* This failure will trigger a reset - no need to clean up */ + if (err) { + netif_err(priv, drv, priv->dev, + "Failed to unregister queue page list %d\n", + priv->qpls[i].id); + return err; + } + } + + start_id = gve_rx_start_qpl_id(priv); + for (i = start_id; i < start_id + gve_num_rx_qpls(priv); i++) { err = gve_adminq_unregister_page_list(priv, priv->qpls[i].id); /* This failure will trigger a reset - no need to clean up */ if (err) { @@ -628,7 +656,7 @@ static int gve_create_rings(struct gve_priv *priv) int err; int i; - err = gve_adminq_create_tx_queues(priv, num_tx_queues); + err = gve_adminq_create_tx_queues(priv, 0, num_tx_queues); if (err) { netif_err(priv, drv, priv->dev, "failed to create %d tx queues\n", num_tx_queues); @@ -695,10 +723,10 @@ static void add_napi_init_sync_stats(struct gve_priv *priv, } } -static void gve_tx_free_rings(struct gve_priv *priv) +static void gve_tx_free_rings(struct gve_priv *priv, int start_id, int num_rings) { if (gve_is_gqi(priv)) { - gve_tx_free_rings_gqi(priv); + gve_tx_free_rings_gqi(priv, start_id, num_rings); } else { gve_tx_free_rings_dqo(priv); } @@ -709,20 +737,20 @@ static int gve_alloc_rings(struct gve_priv *priv) int err; /* Setup tx rings */ - priv->tx = kvcalloc(priv->tx_cfg.num_queues, sizeof(*priv->tx), + priv->tx = kvcalloc(priv->tx_cfg.max_queues, sizeof(*priv->tx), GFP_KERNEL); if (!priv->tx) return -ENOMEM; if (gve_is_gqi(priv)) - err = gve_tx_alloc_rings(priv); + err = gve_tx_alloc_rings(priv, 0, gve_num_tx_queues(priv)); else err = gve_tx_alloc_rings_dqo(priv); if (err) goto free_tx; /* Setup rx rings */ - priv->rx = kvcalloc(priv->rx_cfg.num_queues, sizeof(*priv->rx), + priv->rx = kvcalloc(priv->rx_cfg.max_queues, sizeof(*priv->rx), GFP_KERNEL); if (!priv->rx) { err = -ENOMEM; @@ -747,7 +775,7 @@ free_rx: kvfree(priv->rx); priv->rx = NULL; free_tx_queue: - gve_tx_free_rings(priv); + gve_tx_free_rings(priv, 0, gve_num_tx_queues(priv)); free_tx: kvfree(priv->tx); priv->tx = NULL; @@ -759,7 +787,7 @@ static int gve_destroy_rings(struct gve_priv *priv) int num_tx_queues = gve_num_tx_queues(priv); int err; - err = gve_adminq_destroy_tx_queues(priv, num_tx_queues); + err = gve_adminq_destroy_tx_queues(priv, 0, num_tx_queues); if (err) { netif_err(priv, drv, priv->dev, "failed to destroy tx queues\n"); @@ -797,7 +825,7 @@ static void gve_free_rings(struct gve_priv *priv) ntfy_idx = gve_tx_idx_to_ntfy(priv, i); gve_remove_napi(priv, ntfy_idx); } - gve_tx_free_rings(priv); + gve_tx_free_rings(priv, 0, num_tx_queues); kvfree(priv->tx); priv->tx = NULL; } @@ -894,40 +922,46 @@ static void gve_free_queue_page_list(struct gve_priv *priv, u32 id) qpl->page_buses[i], gve_qpl_dma_dir(priv, id)); kvfree(qpl->page_buses); + qpl->page_buses = NULL; free_pages: kvfree(qpl->pages); + qpl->pages = NULL; priv->num_registered_pages -= qpl->num_entries; } static int gve_alloc_qpls(struct gve_priv *priv) { - int num_qpls = gve_num_tx_qpls(priv) + gve_num_rx_qpls(priv); + int max_queues = priv->tx_cfg.max_queues + priv->rx_cfg.max_queues; + int start_id; int i, j; int err; - if (num_qpls == 0) + if (priv->queue_format != GVE_GQI_QPL_FORMAT) return 0; - priv->qpls = kvcalloc(num_qpls, sizeof(*priv->qpls), GFP_KERNEL); + priv->qpls = kvcalloc(max_queues, sizeof(*priv->qpls), GFP_KERNEL); if (!priv->qpls) return -ENOMEM; - for (i = 0; i < gve_num_tx_qpls(priv); i++) { + start_id = gve_tx_start_qpl_id(priv); + for (i = start_id; i < start_id + gve_num_tx_qpls(priv); i++) { err = gve_alloc_queue_page_list(priv, i, priv->tx_pages_per_qpl); if (err) goto free_qpls; } - for (; i < num_qpls; i++) { + + start_id = gve_rx_start_qpl_id(priv); + for (i = start_id; i < start_id + gve_num_rx_qpls(priv); i++) { err = gve_alloc_queue_page_list(priv, i, priv->rx_data_slot_cnt); if (err) goto free_qpls; } - priv->qpl_cfg.qpl_map_size = BITS_TO_LONGS(num_qpls) * + priv->qpl_cfg.qpl_map_size = BITS_TO_LONGS(max_queues) * sizeof(unsigned long) * BITS_PER_BYTE; - priv->qpl_cfg.qpl_id_map = kvcalloc(BITS_TO_LONGS(num_qpls), + priv->qpl_cfg.qpl_id_map = kvcalloc(BITS_TO_LONGS(max_queues), sizeof(unsigned long), GFP_KERNEL); if (!priv->qpl_cfg.qpl_id_map) { err = -ENOMEM; @@ -940,23 +974,26 @@ free_qpls: for (j = 0; j <= i; j++) gve_free_queue_page_list(priv, j); kvfree(priv->qpls); + priv->qpls = NULL; return err; } static void gve_free_qpls(struct gve_priv *priv) { - int num_qpls = gve_num_tx_qpls(priv) + gve_num_rx_qpls(priv); + int max_queues = priv->tx_cfg.max_queues + priv->rx_cfg.max_queues; int i; - if (num_qpls == 0) + if (!priv->qpls) return; kvfree(priv->qpl_cfg.qpl_id_map); + priv->qpl_cfg.qpl_id_map = NULL; - for (i = 0; i < num_qpls; i++) + for (i = 0; i < max_queues; i++) gve_free_queue_page_list(priv, i); kvfree(priv->qpls); + priv->qpls = NULL; } /* Use this to schedule a reset when the device is capable of continuing diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index db1c74b1d7d3..051a15e4f1af 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -124,7 +124,7 @@ static int gve_prefill_rx_pages(struct gve_rx_ring *rx) return -ENOMEM; if (!rx->data.raw_addressing) { - rx->data.qpl = gve_assign_rx_qpl(priv); + rx->data.qpl = gve_assign_rx_qpl(priv, rx->q_num); if (!rx->data.qpl) { kvfree(rx->data.page_info); rx->data.page_info = NULL; diff --git a/drivers/net/ethernet/google/gve/gve_tx.c b/drivers/net/ethernet/google/gve/gve_tx.c index 0fb052ce9e0b..e24e73e74e33 100644 --- a/drivers/net/ethernet/google/gve/gve_tx.c +++ b/drivers/net/ethernet/google/gve/gve_tx.c @@ -195,7 +195,7 @@ static int gve_tx_alloc_ring(struct gve_priv *priv, int idx) tx->raw_addressing = priv->queue_format == GVE_GQI_RDA_FORMAT; tx->dev = &priv->pdev->dev; if (!tx->raw_addressing) { - tx->tx_fifo.qpl = gve_assign_tx_qpl(priv); + tx->tx_fifo.qpl = gve_assign_tx_qpl(priv, idx); if (!tx->tx_fifo.qpl) goto abort_with_desc; /* map Tx FIFO */ @@ -233,12 +233,12 @@ abort_with_info: return -ENOMEM; } -int gve_tx_alloc_rings(struct gve_priv *priv) +int gve_tx_alloc_rings(struct gve_priv *priv, int start_id, int num_rings) { int err = 0; int i; - for (i = 0; i < priv->tx_cfg.num_queues; i++) { + for (i = start_id; i < start_id + num_rings; i++) { err = gve_tx_alloc_ring(priv, i); if (err) { netif_err(priv, drv, priv->dev, @@ -251,17 +251,17 @@ int gve_tx_alloc_rings(struct gve_priv *priv) if (err) { int j; - for (j = 0; j < i; j++) + for (j = start_id; j < i; j++) gve_tx_free_ring(priv, j); } return err; } -void gve_tx_free_rings_gqi(struct gve_priv *priv) +void gve_tx_free_rings_gqi(struct gve_priv *priv, int start_id, int num_rings) { int i; - for (i = 0; i < priv->tx_cfg.num_queues; i++) + for (i = start_id; i < start_id + num_rings; i++) gve_tx_free_ring(priv, i); } -- cgit v1.2.3 From 75eaae158b1b7d8d5bde2bafc0bcf778423071d3 Mon Sep 17 00:00:00 2001 From: Praveen Kaligineedi Date: Wed, 15 Mar 2023 16:33:10 -0700 Subject: gve: Add XDP DROP and TX support for GQI-QPL format Add support for XDP PASS, DROP and TX actions. This patch contains the following changes: 1) Support installing/uninstalling XDP program 2) Add dedicated XDP TX queues 3) Add support for XDP DROP action 4) Add support for XDP TX action Signed-off-by: Praveen Kaligineedi Reviewed-by: Jeroen de Borst Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve.h | 44 ++- drivers/net/ethernet/google/gve/gve_ethtool.c | 37 ++- drivers/net/ethernet/google/gve/gve_main.c | 422 ++++++++++++++++++++++++-- drivers/net/ethernet/google/gve/gve_rx.c | 74 ++++- drivers/net/ethernet/google/gve/gve_tx.c | 149 ++++++++- 5 files changed, 687 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index f354a6448c25..8d5234d4ba67 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -47,6 +47,10 @@ #define GVE_RX_BUFFER_SIZE_DQO 2048 +#define GVE_XDP_ACTIONS 5 + +#define GVE_TX_MAX_HEADER_SIZE 182 + /* Each slot in the desc ring has a 1:1 mapping to a slot in the data ring */ struct gve_rx_desc_queue { struct gve_rx_desc *desc_ring; /* the descriptor ring */ @@ -230,7 +234,9 @@ struct gve_rx_ring { u64 rx_frag_flip_cnt; /* free-running count of rx segments where page_flip was used */ u64 rx_frag_copy_cnt; /* free-running count of rx segments copied */ u64 rx_frag_alloc_cnt; /* free-running count of rx page allocations */ - + u64 xdp_tx_errors; + u64 xdp_redirect_errors; + u64 xdp_actions[GVE_XDP_ACTIONS]; u32 q_num; /* queue index */ u32 ntfy_id; /* notification block index */ struct gve_queue_resources *q_resources; /* head and tail pointer idx */ @@ -238,6 +244,9 @@ struct gve_rx_ring { struct u64_stats_sync statss; /* sync stats for 32bit archs */ struct gve_rx_ctx ctx; /* Info for packet currently being processed in this ring. */ + + /* XDP stuff */ + struct xdp_rxq_info xdp_rxq; }; /* A TX desc ring entry */ @@ -259,6 +268,9 @@ struct gve_tx_iovec { */ struct gve_tx_buffer_state { struct sk_buff *skb; /* skb for this pkt */ + struct { + u16 size; /* size of xmitted xdp pkt */ + } xdp; union { struct gve_tx_iovec iov[GVE_TX_MAX_IOVEC]; /* segments of this pkt */ struct { @@ -526,9 +538,11 @@ struct gve_priv { u16 rx_data_slot_cnt; /* rx buffer length */ u64 max_registered_pages; u64 num_registered_pages; /* num pages registered with NIC */ + struct bpf_prog *xdp_prog; /* XDP BPF program */ u32 rx_copybreak; /* copy packets smaller than this */ u16 default_num_queues; /* default num queues to set up */ + u16 num_xdp_queues; struct gve_queue_config tx_cfg; struct gve_queue_config rx_cfg; struct gve_qpl_config qpl_cfg; /* map used QPL ids */ @@ -785,7 +799,17 @@ static inline u32 gve_num_tx_qpls(struct gve_priv *priv) if (priv->queue_format != GVE_GQI_QPL_FORMAT) return 0; - return priv->tx_cfg.num_queues; + return priv->tx_cfg.num_queues + priv->num_xdp_queues; +} + +/* Returns the number of XDP tx queue page lists + */ +static inline u32 gve_num_xdp_qpls(struct gve_priv *priv) +{ + if (priv->queue_format != GVE_GQI_QPL_FORMAT) + return 0; + + return priv->num_xdp_queues; } /* Returns the number of rx queue page lists @@ -874,7 +898,17 @@ static inline bool gve_is_gqi(struct gve_priv *priv) static inline u32 gve_num_tx_queues(struct gve_priv *priv) { - return priv->tx_cfg.num_queues; + return priv->tx_cfg.num_queues + priv->num_xdp_queues; +} + +static inline u32 gve_xdp_tx_queue_id(struct gve_priv *priv, u32 queue_id) +{ + return priv->tx_cfg.num_queues + queue_id; +} + +static inline u32 gve_xdp_tx_start_queue_id(struct gve_priv *priv) +{ + return gve_xdp_tx_queue_id(priv, 0); } /* buffers */ @@ -885,7 +919,11 @@ void gve_free_page(struct device *dev, struct page *page, dma_addr_t dma, enum dma_data_direction); /* tx handling */ netdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev); +int gve_xdp_xmit_one(struct gve_priv *priv, struct gve_tx_ring *tx, + void *data, int len); +void gve_xdp_tx_flush(struct gve_priv *priv, u32 xdp_qid); bool gve_tx_poll(struct gve_notify_block *block, int budget); +bool gve_xdp_poll(struct gve_notify_block *block, int budget); int gve_tx_alloc_rings(struct gve_priv *priv, int start_id, int num_rings); void gve_tx_free_rings_gqi(struct gve_priv *priv, int start_id, int num_rings); u32 gve_tx_load_event_counter(struct gve_priv *priv, diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index 5b6e31812fae..067b393ccf9d 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -34,6 +34,11 @@ static u32 gve_get_msglevel(struct net_device *netdev) return priv->msg_enable; } +/* For the following stats column string names, make sure the order + * matches how it is filled in the code. For xdp_aborted, xdp_drop, + * xdp_pass, xdp_tx, xdp_redirect, make sure it also matches the order + * as declared in enum xdp_action inside file uapi/linux/bpf.h . + */ static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = { "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_dropped", "tx_dropped", "tx_timeouts", @@ -49,6 +54,9 @@ static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = { "rx_dropped_pkt[%u]", "rx_copybreak_pkt[%u]", "rx_copied_pkt[%u]", "rx_queue_drop_cnt[%u]", "rx_no_buffers_posted[%u]", "rx_drops_packet_over_mru[%u]", "rx_drops_invalid_checksum[%u]", + "rx_xdp_aborted[%u]", "rx_xdp_drop[%u]", "rx_xdp_pass[%u]", + "rx_xdp_tx[%u]", "rx_xdp_redirect[%u]", + "rx_xdp_tx_errors[%u]", "rx_xdp_redirect_errors[%u]", }; static const char gve_gstrings_tx_stats[][ETH_GSTRING_LEN] = { @@ -289,14 +297,25 @@ gve_get_ethtool_stats(struct net_device *netdev, if (skip_nic_stats) { /* skip NIC rx stats */ i += NIC_RX_STATS_REPORT_NUM; - continue; - } - for (j = 0; j < NIC_RX_STATS_REPORT_NUM; j++) { - u64 value = - be64_to_cpu(report_stats[rx_qid_to_stats_idx[ring] + j].value); + } else { + stats_idx = rx_qid_to_stats_idx[ring]; + for (j = 0; j < NIC_RX_STATS_REPORT_NUM; j++) { + u64 value = + be64_to_cpu(report_stats[stats_idx + j].value); - data[i++] = value; + data[i++] = value; + } } + /* XDP rx counters */ + do { + start = u64_stats_fetch_begin(&priv->rx[ring].statss); + for (j = 0; j < GVE_XDP_ACTIONS; j++) + data[i + j] = rx->xdp_actions[j]; + data[i + j++] = rx->xdp_tx_errors; + data[i + j++] = rx->xdp_redirect_errors; + } while (u64_stats_fetch_retry(&priv->rx[ring].statss, + start)); + i += GVE_XDP_ACTIONS + 2; /* XDP rx counters */ } } else { i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS; @@ -418,6 +437,12 @@ static int gve_set_channels(struct net_device *netdev, if (!new_rx || !new_tx) return -EINVAL; + if (priv->num_xdp_queues && + (new_tx != new_rx || (2 * new_tx > priv->tx_cfg.max_queues))) { + dev_err(&priv->pdev->dev, "XDP load failed: The number of configured RX queues should be equal to the number of configured TX queues and the number of configured RX/TX queues should be less than or equal to half the maximum number of RX/TX queues"); + return -EINVAL; + } + if (!netif_carrier_ok(netdev)) { priv->tx_cfg.num_queues = new_tx; priv->rx_cfg.num_queues = new_rx; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 160ca77c2751..f49398857921 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -4,8 +4,10 @@ * Copyright (C) 2015-2021 Google, Inc. */ +#include #include #include +#include #include #include #include @@ -247,8 +249,13 @@ static int gve_napi_poll(struct napi_struct *napi, int budget) block = container_of(napi, struct gve_notify_block, napi); priv = block->priv; - if (block->tx) - reschedule |= gve_tx_poll(block, budget); + if (block->tx) { + if (block->tx->q_num < priv->tx_cfg.num_queues) + reschedule |= gve_tx_poll(block, budget); + else + reschedule |= gve_xdp_poll(block, budget); + } + if (block->rx) { work_done = gve_rx_poll(block, budget); reschedule |= work_done == budget; @@ -582,6 +589,28 @@ static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) netif_napi_del(&block->napi); } +static int gve_register_xdp_qpls(struct gve_priv *priv) +{ + int start_id; + int err; + int i; + + start_id = gve_tx_qpl_id(priv, gve_xdp_tx_start_queue_id(priv)); + for (i = start_id; i < start_id + gve_num_xdp_qpls(priv); i++) { + err = gve_adminq_register_page_list(priv, &priv->qpls[i]); + if (err) { + netif_err(priv, drv, priv->dev, + "failed to register queue page list %d\n", + priv->qpls[i].id); + /* This failure will trigger a reset - no need to clean + * up + */ + return err; + } + } + return 0; +} + static int gve_register_qpls(struct gve_priv *priv) { int start_id; @@ -618,6 +647,26 @@ static int gve_register_qpls(struct gve_priv *priv) return 0; } +static int gve_unregister_xdp_qpls(struct gve_priv *priv) +{ + int start_id; + int err; + int i; + + start_id = gve_tx_qpl_id(priv, gve_xdp_tx_start_queue_id(priv)); + for (i = start_id; i < start_id + gve_num_xdp_qpls(priv); i++) { + err = gve_adminq_unregister_page_list(priv, priv->qpls[i].id); + /* This failure will trigger a reset - no need to clean up */ + if (err) { + netif_err(priv, drv, priv->dev, + "Failed to unregister queue page list %d\n", + priv->qpls[i].id); + return err; + } + } + return 0; +} + static int gve_unregister_qpls(struct gve_priv *priv) { int start_id; @@ -650,6 +699,27 @@ static int gve_unregister_qpls(struct gve_priv *priv) return 0; } +static int gve_create_xdp_rings(struct gve_priv *priv) +{ + int err; + + err = gve_adminq_create_tx_queues(priv, + gve_xdp_tx_start_queue_id(priv), + priv->num_xdp_queues); + if (err) { + netif_err(priv, drv, priv->dev, "failed to create %d XDP tx queues\n", + priv->num_xdp_queues); + /* This failure will trigger a reset - no need to clean + * up + */ + return err; + } + netif_dbg(priv, drv, priv->dev, "created %d XDP tx queues\n", + priv->num_xdp_queues); + + return 0; +} + static int gve_create_rings(struct gve_priv *priv) { int num_tx_queues = gve_num_tx_queues(priv); @@ -699,6 +769,23 @@ static int gve_create_rings(struct gve_priv *priv) return 0; } +static void add_napi_init_xdp_sync_stats(struct gve_priv *priv, + int (*napi_poll)(struct napi_struct *napi, + int budget)) +{ + int start_id = gve_xdp_tx_start_queue_id(priv); + int i; + + /* Add xdp tx napi & init sync stats*/ + for (i = start_id; i < start_id + priv->num_xdp_queues; i++) { + int ntfy_idx = gve_tx_idx_to_ntfy(priv, i); + + u64_stats_init(&priv->tx[i].statss); + priv->tx[i].ntfy_id = ntfy_idx; + gve_add_napi(priv, ntfy_idx, napi_poll); + } +} + static void add_napi_init_sync_stats(struct gve_priv *priv, int (*napi_poll)(struct napi_struct *napi, int budget)) @@ -732,6 +819,23 @@ static void gve_tx_free_rings(struct gve_priv *priv, int start_id, int num_rings } } +static int gve_alloc_xdp_rings(struct gve_priv *priv) +{ + int start_id; + int err = 0; + + if (!priv->num_xdp_queues) + return 0; + + start_id = gve_xdp_tx_start_queue_id(priv); + err = gve_tx_alloc_rings(priv, start_id, priv->num_xdp_queues); + if (err) + return err; + add_napi_init_xdp_sync_stats(priv, gve_napi_poll); + + return 0; +} + static int gve_alloc_rings(struct gve_priv *priv) { int err; @@ -782,6 +886,26 @@ free_tx: return err; } +static int gve_destroy_xdp_rings(struct gve_priv *priv) +{ + int start_id; + int err; + + start_id = gve_xdp_tx_start_queue_id(priv); + err = gve_adminq_destroy_tx_queues(priv, + start_id, + priv->num_xdp_queues); + if (err) { + netif_err(priv, drv, priv->dev, + "failed to destroy XDP queues\n"); + /* This failure will trigger a reset - no need to clean up */ + return err; + } + netif_dbg(priv, drv, priv->dev, "destroyed XDP queues\n"); + + return 0; +} + static int gve_destroy_rings(struct gve_priv *priv) { int num_tx_queues = gve_num_tx_queues(priv); @@ -814,6 +938,21 @@ static void gve_rx_free_rings(struct gve_priv *priv) gve_rx_free_rings_dqo(priv); } +static void gve_free_xdp_rings(struct gve_priv *priv) +{ + int ntfy_idx, start_id; + int i; + + start_id = gve_xdp_tx_start_queue_id(priv); + if (priv->tx) { + for (i = start_id; i < start_id + priv->num_xdp_queues; i++) { + ntfy_idx = gve_tx_idx_to_ntfy(priv, i); + gve_remove_napi(priv, ntfy_idx); + } + gve_tx_free_rings(priv, start_id, priv->num_xdp_queues); + } +} + static void gve_free_rings(struct gve_priv *priv) { int num_tx_queues = gve_num_tx_queues(priv); @@ -929,6 +1068,28 @@ free_pages: priv->num_registered_pages -= qpl->num_entries; } +static int gve_alloc_xdp_qpls(struct gve_priv *priv) +{ + int start_id; + int i, j; + int err; + + start_id = gve_tx_qpl_id(priv, gve_xdp_tx_start_queue_id(priv)); + for (i = start_id; i < start_id + gve_num_xdp_qpls(priv); i++) { + err = gve_alloc_queue_page_list(priv, i, + priv->tx_pages_per_qpl); + if (err) + goto free_qpls; + } + + return 0; + +free_qpls: + for (j = start_id; j <= i; j++) + gve_free_queue_page_list(priv, j); + return err; +} + static int gve_alloc_qpls(struct gve_priv *priv) { int max_queues = priv->tx_cfg.max_queues + priv->rx_cfg.max_queues; @@ -978,6 +1139,16 @@ free_qpls: return err; } +static void gve_free_xdp_qpls(struct gve_priv *priv) +{ + int start_id; + int i; + + start_id = gve_tx_qpl_id(priv, gve_xdp_tx_start_queue_id(priv)); + for (i = start_id; i < start_id + gve_num_xdp_qpls(priv); i++) + gve_free_queue_page_list(priv, i); +} + static void gve_free_qpls(struct gve_priv *priv) { int max_queues = priv->tx_cfg.max_queues + priv->rx_cfg.max_queues; @@ -1011,11 +1182,64 @@ static int gve_reset_recovery(struct gve_priv *priv, bool was_up); static void gve_turndown(struct gve_priv *priv); static void gve_turnup(struct gve_priv *priv); +static int gve_reg_xdp_info(struct gve_priv *priv, struct net_device *dev) +{ + struct napi_struct *napi; + struct gve_rx_ring *rx; + int err = 0; + int i, j; + + if (!priv->num_xdp_queues) + return 0; + + for (i = 0; i < priv->rx_cfg.num_queues; i++) { + rx = &priv->rx[i]; + napi = &priv->ntfy_blocks[rx->ntfy_id].napi; + + err = xdp_rxq_info_reg(&rx->xdp_rxq, dev, i, + napi->napi_id); + if (err) + goto err; + err = xdp_rxq_info_reg_mem_model(&rx->xdp_rxq, + MEM_TYPE_PAGE_SHARED, NULL); + if (err) + goto err; + } + return 0; + +err: + for (j = i; j >= 0; j--) { + rx = &priv->rx[j]; + if (xdp_rxq_info_is_reg(&rx->xdp_rxq)) + xdp_rxq_info_unreg(&rx->xdp_rxq); + } + return err; +} + +static void gve_unreg_xdp_info(struct gve_priv *priv) +{ + int i; + + if (!priv->num_xdp_queues) + return; + + for (i = 0; i < priv->rx_cfg.num_queues; i++) { + struct gve_rx_ring *rx = &priv->rx[i]; + + xdp_rxq_info_unreg(&rx->xdp_rxq); + } +} + static int gve_open(struct net_device *dev) { struct gve_priv *priv = netdev_priv(dev); int err; + if (priv->xdp_prog) + priv->num_xdp_queues = priv->rx_cfg.num_queues; + else + priv->num_xdp_queues = 0; + err = gve_alloc_qpls(priv); if (err) return err; @@ -1031,6 +1255,10 @@ static int gve_open(struct net_device *dev) if (err) goto free_rings; + err = gve_reg_xdp_info(priv, dev); + if (err) + goto free_rings; + err = gve_register_qpls(priv); if (err) goto reset; @@ -1095,6 +1323,7 @@ static int gve_close(struct net_device *dev) } del_timer_sync(&priv->stats_report_timer); + gve_unreg_xdp_info(priv); gve_free_rings(priv); gve_free_qpls(priv); priv->interface_down_cnt++; @@ -1111,6 +1340,167 @@ err: return gve_reset_recovery(priv, false); } +static int gve_remove_xdp_queues(struct gve_priv *priv) +{ + int err; + + err = gve_destroy_xdp_rings(priv); + if (err) + return err; + + err = gve_unregister_xdp_qpls(priv); + if (err) + return err; + + gve_unreg_xdp_info(priv); + gve_free_xdp_rings(priv); + gve_free_xdp_qpls(priv); + priv->num_xdp_queues = 0; + return 0; +} + +static int gve_add_xdp_queues(struct gve_priv *priv) +{ + int err; + + priv->num_xdp_queues = priv->tx_cfg.num_queues; + + err = gve_alloc_xdp_qpls(priv); + if (err) + goto err; + + err = gve_alloc_xdp_rings(priv); + if (err) + goto free_xdp_qpls; + + err = gve_reg_xdp_info(priv, priv->dev); + if (err) + goto free_xdp_rings; + + err = gve_register_xdp_qpls(priv); + if (err) + goto free_xdp_rings; + + err = gve_create_xdp_rings(priv); + if (err) + goto free_xdp_rings; + + return 0; + +free_xdp_rings: + gve_free_xdp_rings(priv); +free_xdp_qpls: + gve_free_xdp_qpls(priv); +err: + priv->num_xdp_queues = 0; + return err; +} + +static void gve_handle_link_status(struct gve_priv *priv, bool link_status) +{ + if (!gve_get_napi_enabled(priv)) + return; + + if (link_status == netif_carrier_ok(priv->dev)) + return; + + if (link_status) { + netdev_info(priv->dev, "Device link is up.\n"); + netif_carrier_on(priv->dev); + } else { + netdev_info(priv->dev, "Device link is down.\n"); + netif_carrier_off(priv->dev); + } +} + +static int gve_set_xdp(struct gve_priv *priv, struct bpf_prog *prog, + struct netlink_ext_ack *extack) +{ + struct bpf_prog *old_prog; + int err = 0; + u32 status; + + old_prog = READ_ONCE(priv->xdp_prog); + if (!netif_carrier_ok(priv->dev)) { + WRITE_ONCE(priv->xdp_prog, prog); + if (old_prog) + bpf_prog_put(old_prog); + return 0; + } + + gve_turndown(priv); + if (!old_prog && prog) { + // Allocate XDP TX queues if an XDP program is + // being installed + err = gve_add_xdp_queues(priv); + if (err) + goto out; + } else if (old_prog && !prog) { + // Remove XDP TX queues if an XDP program is + // being uninstalled + err = gve_remove_xdp_queues(priv); + if (err) + goto out; + } + WRITE_ONCE(priv->xdp_prog, prog); + if (old_prog) + bpf_prog_put(old_prog); + +out: + gve_turnup(priv); + status = ioread32be(&priv->reg_bar0->device_status); + gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status); + return err; +} + +static int verify_xdp_configuration(struct net_device *dev) +{ + struct gve_priv *priv = netdev_priv(dev); + + if (dev->features & NETIF_F_LRO) { + netdev_warn(dev, "XDP is not supported when LRO is on.\n"); + return -EOPNOTSUPP; + } + + if (priv->queue_format != GVE_GQI_QPL_FORMAT) { + netdev_warn(dev, "XDP is not supported in mode %d.\n", + priv->queue_format); + return -EOPNOTSUPP; + } + + if (dev->mtu > (PAGE_SIZE / 2) - sizeof(struct ethhdr) - GVE_RX_PAD) { + netdev_warn(dev, "XDP is not supported for mtu %d.\n", + dev->mtu); + return -EOPNOTSUPP; + } + + if (priv->rx_cfg.num_queues != priv->tx_cfg.num_queues || + (2 * priv->tx_cfg.num_queues > priv->tx_cfg.max_queues)) { + netdev_warn(dev, "XDP load failed: The number of configured RX queues %d should be equal to the number of configured TX queues %d and the number of configured RX/TX queues should be less than or equal to half the maximum number of RX/TX queues %d", + priv->rx_cfg.num_queues, + priv->tx_cfg.num_queues, + priv->tx_cfg.max_queues); + return -EINVAL; + } + return 0; +} + +static int gve_xdp(struct net_device *dev, struct netdev_bpf *xdp) +{ + struct gve_priv *priv = netdev_priv(dev); + int err; + + err = verify_xdp_configuration(dev); + if (err) + return err; + switch (xdp->command) { + case XDP_SETUP_PROG: + return gve_set_xdp(priv, xdp->prog, xdp->extack); + default: + return -EINVAL; + } +} + int gve_adjust_queues(struct gve_priv *priv, struct gve_queue_config new_rx_config, struct gve_queue_config new_tx_config) @@ -1305,6 +1695,7 @@ static const struct net_device_ops gve_netdev_ops = { .ndo_get_stats64 = gve_get_stats, .ndo_tx_timeout = gve_tx_timeout, .ndo_set_features = gve_set_features, + .ndo_bpf = gve_xdp, }; static void gve_handle_status(struct gve_priv *priv, u32 status) @@ -1411,23 +1802,6 @@ void gve_handle_report_stats(struct gve_priv *priv) } } -static void gve_handle_link_status(struct gve_priv *priv, bool link_status) -{ - if (!gve_get_napi_enabled(priv)) - return; - - if (link_status == netif_carrier_ok(priv->dev)) - return; - - if (link_status) { - netdev_info(priv->dev, "Device link is up.\n"); - netif_carrier_on(priv->dev); - } else { - netdev_info(priv->dev, "Device link is down.\n"); - netif_carrier_off(priv->dev); - } -} - /* Handle NIC status register changes, reset requests and report stats */ static void gve_service_task(struct work_struct *work) { @@ -1441,6 +1815,15 @@ static void gve_service_task(struct work_struct *work) gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status); } +static void gve_set_netdev_xdp_features(struct gve_priv *priv) +{ + if (priv->queue_format == GVE_GQI_QPL_FORMAT) { + priv->dev->xdp_features = NETDEV_XDP_ACT_BASIC; + } else { + priv->dev->xdp_features = 0; + } +} + static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device) { int num_ntfy; @@ -1519,6 +1902,7 @@ static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device) } setup_device: + gve_set_netdev_xdp_features(priv); err = gve_setup_device_resources(priv); if (!err) return 0; diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index 051a15e4f1af..3241f6ea29be 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -8,6 +8,8 @@ #include "gve_adminq.h" #include "gve_utils.h" #include +#include +#include static void gve_rx_free_buffer(struct device *dev, struct gve_rx_slot_page_info *page_info, @@ -591,6 +593,43 @@ static struct sk_buff *gve_rx_skb(struct gve_priv *priv, struct gve_rx_ring *rx, return skb; } +static void gve_xdp_done(struct gve_priv *priv, struct gve_rx_ring *rx, + struct xdp_buff *xdp, struct bpf_prog *xprog, + int xdp_act) +{ + struct gve_tx_ring *tx; + int tx_qid; + int err; + + switch (xdp_act) { + case XDP_ABORTED: + case XDP_DROP: + default: + break; + case XDP_TX: + tx_qid = gve_xdp_tx_queue_id(priv, rx->q_num); + tx = &priv->tx[tx_qid]; + err = gve_xdp_xmit_one(priv, tx, xdp->data, + xdp->data_end - xdp->data); + + if (unlikely(err)) { + u64_stats_update_begin(&rx->statss); + rx->xdp_tx_errors++; + u64_stats_update_end(&rx->statss); + } + break; + case XDP_REDIRECT: + u64_stats_update_begin(&rx->statss); + rx->xdp_redirect_errors++; + u64_stats_update_end(&rx->statss); + break; + } + u64_stats_update_begin(&rx->statss); + if ((u32)xdp_act < GVE_XDP_ACTIONS) + rx->xdp_actions[xdp_act]++; + u64_stats_update_end(&rx->statss); +} + #define GVE_PKTCONT_BIT_IS_SET(x) (GVE_RXF_PKT_CONT & (x)) static void gve_rx(struct gve_rx_ring *rx, netdev_features_t feat, struct gve_rx_desc *desc, u32 idx, @@ -603,9 +642,12 @@ static void gve_rx(struct gve_rx_ring *rx, netdev_features_t feat, union gve_rx_data_slot *data_slot; struct gve_priv *priv = rx->gve; struct sk_buff *skb = NULL; + struct bpf_prog *xprog; + struct xdp_buff xdp; dma_addr_t page_bus; void *va; + u16 len = frag_size; struct napi_struct *napi = &priv->ntfy_blocks[rx->ntfy_id].napi; bool is_first_frag = ctx->frag_cnt == 0; @@ -645,9 +687,35 @@ static void gve_rx(struct gve_rx_ring *rx, netdev_features_t feat, dma_sync_single_for_cpu(&priv->pdev->dev, page_bus, PAGE_SIZE, DMA_FROM_DEVICE); page_info->pad = is_first_frag ? GVE_RX_PAD : 0; + len -= page_info->pad; frag_size -= page_info->pad; - skb = gve_rx_skb(priv, rx, page_info, napi, frag_size, + xprog = READ_ONCE(priv->xdp_prog); + if (xprog && is_only_frag) { + void *old_data; + int xdp_act; + + xdp_init_buff(&xdp, rx->packet_buffer_size, &rx->xdp_rxq); + xdp_prepare_buff(&xdp, page_info->page_address + + page_info->page_offset, GVE_RX_PAD, + len, false); + old_data = xdp.data; + xdp_act = bpf_prog_run_xdp(xprog, &xdp); + if (xdp_act != XDP_PASS) { + gve_xdp_done(priv, rx, &xdp, xprog, xdp_act); + ctx->total_size += frag_size; + goto finish_ok_pkt; + } + + page_info->pad += xdp.data - old_data; + len = xdp.data_end - xdp.data; + + u64_stats_update_begin(&rx->statss); + rx->xdp_actions[XDP_PASS]++; + u64_stats_update_end(&rx->statss); + } + + skb = gve_rx_skb(priv, rx, page_info, napi, len, data_slot, is_only_frag); if (!skb) { u64_stats_update_begin(&rx->statss); @@ -773,6 +841,7 @@ static bool gve_rx_refill_buffers(struct gve_priv *priv, struct gve_rx_ring *rx) static int gve_clean_rx_done(struct gve_rx_ring *rx, int budget, netdev_features_t feat) { + u64 xdp_txs = rx->xdp_actions[XDP_TX]; struct gve_rx_ctx *ctx = &rx->ctx; struct gve_priv *priv = rx->gve; struct gve_rx_cnts cnts = {0}; @@ -820,6 +889,9 @@ static int gve_clean_rx_done(struct gve_rx_ring *rx, int budget, u64_stats_update_end(&rx->statss); } + if (xdp_txs != rx->xdp_actions[XDP_TX]) + gve_xdp_tx_flush(priv, rx->q_num); + /* restock ring slots */ if (!rx->data.raw_addressing) { /* In QPL mode buffs are refilled as the desc are processed */ diff --git a/drivers/net/ethernet/google/gve/gve_tx.c b/drivers/net/ethernet/google/gve/gve_tx.c index e24e73e74e33..3e96ee7537ce 100644 --- a/drivers/net/ethernet/google/gve/gve_tx.c +++ b/drivers/net/ethernet/google/gve/gve_tx.c @@ -19,6 +19,14 @@ static inline void gve_tx_put_doorbell(struct gve_priv *priv, iowrite32be(val, &priv->db_bar2[be32_to_cpu(q_resources->db_index)]); } +void gve_xdp_tx_flush(struct gve_priv *priv, u32 xdp_qid) +{ + u32 tx_qid = gve_xdp_tx_queue_id(priv, xdp_qid); + struct gve_tx_ring *tx = &priv->tx[tx_qid]; + + gve_tx_put_doorbell(priv, tx->q_resources, tx->req); +} + /* gvnic can only transmit from a Registered Segment. * We copy skb payloads into the registered segment before writing Tx * descriptors and ringing the Tx doorbell. @@ -132,6 +140,50 @@ static void gve_tx_free_fifo(struct gve_tx_fifo *fifo, size_t bytes) atomic_add(bytes, &fifo->available); } +static size_t gve_tx_clear_buffer_state(struct gve_tx_buffer_state *info) +{ + size_t space_freed = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(info->iov); i++) { + space_freed += info->iov[i].iov_len + info->iov[i].iov_padding; + info->iov[i].iov_len = 0; + info->iov[i].iov_padding = 0; + } + return space_freed; +} + +static int gve_clean_xdp_done(struct gve_priv *priv, struct gve_tx_ring *tx, + u32 to_do) +{ + struct gve_tx_buffer_state *info; + u32 clean_end = tx->done + to_do; + u64 pkts = 0, bytes = 0; + size_t space_freed = 0; + u32 idx; + + for (; tx->done < clean_end; tx->done++) { + idx = tx->done & tx->mask; + info = &tx->info[idx]; + + if (unlikely(!info->xdp.size)) + continue; + + bytes += info->xdp.size; + pkts++; + + info->xdp.size = 0; + space_freed += gve_tx_clear_buffer_state(info); + } + + gve_tx_free_fifo(&tx->tx_fifo, space_freed); + u64_stats_update_begin(&tx->statss); + tx->bytes_done += bytes; + tx->pkt_done += pkts; + u64_stats_update_end(&tx->statss); + return pkts; +} + static int gve_clean_tx_done(struct gve_priv *priv, struct gve_tx_ring *tx, u32 to_do, bool try_to_wake); @@ -144,8 +196,12 @@ static void gve_tx_free_ring(struct gve_priv *priv, int idx) gve_tx_remove_from_block(priv, idx); slots = tx->mask + 1; - gve_clean_tx_done(priv, tx, priv->tx_desc_cnt, false); - netdev_tx_reset_queue(tx->netdev_txq); + if (tx->q_num < priv->tx_cfg.num_queues) { + gve_clean_tx_done(priv, tx, priv->tx_desc_cnt, false); + netdev_tx_reset_queue(tx->netdev_txq); + } else { + gve_clean_xdp_done(priv, tx, priv->tx_desc_cnt); + } dma_free_coherent(hdev, sizeof(*tx->q_resources), tx->q_resources, tx->q_resources_bus); @@ -213,7 +269,8 @@ static int gve_tx_alloc_ring(struct gve_priv *priv, int idx) netif_dbg(priv, drv, priv->dev, "tx[%d]->bus=%lx\n", idx, (unsigned long)tx->bus); - tx->netdev_txq = netdev_get_tx_queue(priv->dev, idx); + if (idx < priv->tx_cfg.num_queues) + tx->netdev_txq = netdev_get_tx_queue(priv->dev, idx); gve_tx_add_to_block(priv, idx); return 0; @@ -657,6 +714,65 @@ netdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static int gve_tx_fill_xdp(struct gve_priv *priv, struct gve_tx_ring *tx, + void *data, int len) +{ + int pad, nfrags, ndescs, iovi, offset; + struct gve_tx_buffer_state *info; + u32 reqi = tx->req; + + pad = gve_tx_fifo_pad_alloc_one_frag(&tx->tx_fifo, len); + if (pad >= GVE_TX_MAX_HEADER_SIZE) + pad = 0; + info = &tx->info[reqi & tx->mask]; + info->xdp.size = len; + + nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, pad + len, + &info->iov[0]); + iovi = pad > 0; + ndescs = nfrags - iovi; + offset = 0; + + while (iovi < nfrags) { + if (!offset) + gve_tx_fill_pkt_desc(&tx->desc[reqi & tx->mask], 0, + CHECKSUM_NONE, false, 0, ndescs, + info->iov[iovi].iov_len, + info->iov[iovi].iov_offset, len); + else + gve_tx_fill_seg_desc(&tx->desc[reqi & tx->mask], + 0, 0, false, false, + info->iov[iovi].iov_len, + info->iov[iovi].iov_offset); + + memcpy(tx->tx_fifo.base + info->iov[iovi].iov_offset, + data + offset, info->iov[iovi].iov_len); + gve_dma_sync_for_device(&priv->pdev->dev, + tx->tx_fifo.qpl->page_buses, + info->iov[iovi].iov_offset, + info->iov[iovi].iov_len); + offset += info->iov[iovi].iov_len; + iovi++; + reqi++; + } + + return ndescs; +} + +int gve_xdp_xmit_one(struct gve_priv *priv, struct gve_tx_ring *tx, + void *data, int len) +{ + int nsegs; + + if (!gve_can_tx(tx, len + GVE_TX_MAX_HEADER_SIZE - 1)) + return -EBUSY; + + nsegs = gve_tx_fill_xdp(priv, tx, data, len); + tx->req += nsegs; + + return 0; +} + #define GVE_TX_START_THRESH PAGE_SIZE static int gve_clean_tx_done(struct gve_priv *priv, struct gve_tx_ring *tx, @@ -666,8 +782,8 @@ static int gve_clean_tx_done(struct gve_priv *priv, struct gve_tx_ring *tx, u64 pkts = 0, bytes = 0; size_t space_freed = 0; struct sk_buff *skb; - int i, j; u32 idx; + int j; for (j = 0; j < to_do; j++) { idx = tx->done & tx->mask; @@ -689,12 +805,7 @@ static int gve_clean_tx_done(struct gve_priv *priv, struct gve_tx_ring *tx, dev_consume_skb_any(skb); if (tx->raw_addressing) continue; - /* FIFO free */ - for (i = 0; i < ARRAY_SIZE(info->iov); i++) { - space_freed += info->iov[i].iov_len + info->iov[i].iov_padding; - info->iov[i].iov_len = 0; - info->iov[i].iov_padding = 0; - } + space_freed += gve_tx_clear_buffer_state(info); } } @@ -729,6 +840,24 @@ u32 gve_tx_load_event_counter(struct gve_priv *priv, return be32_to_cpu(counter); } +bool gve_xdp_poll(struct gve_notify_block *block, int budget) +{ + struct gve_priv *priv = block->priv; + struct gve_tx_ring *tx = block->tx; + u32 nic_done; + u32 to_do; + + /* If budget is 0, do all the work */ + if (budget == 0) + budget = INT_MAX; + + /* Find out how much work there is to be done */ + nic_done = gve_tx_load_event_counter(priv, tx); + to_do = min_t(u32, (nic_done - tx->done), budget); + gve_clean_xdp_done(priv, tx, to_do); + return nic_done != tx->done; +} + bool gve_tx_poll(struct gve_notify_block *block, int budget) { struct gve_priv *priv = block->priv; -- cgit v1.2.3 From 39a7f4aa3e4a7947614cf1d5c27abba3300adb1e Mon Sep 17 00:00:00 2001 From: Praveen Kaligineedi Date: Wed, 15 Mar 2023 16:33:11 -0700 Subject: gve: Add XDP REDIRECT support for GQI-QPL format This patch contains the following changes: 1) Support for XDP REDIRECT action on rx 2) ndo_xdp_xmit callback support In GQI-QPL queue format, the driver needs to allocate a fixed size memory, the size specified by vNIC device, for RX/TX and register this memory as a bounce buffer with the vNIC device when a queue is created. The number of pages in the bounce buffer is limited and the pages need to be made available to the vNIC by copying the RX data out to prevent head-of-line blocking. The XDP_REDIRECT packets are therefore immediately copied to a newly allocated page. Signed-off-by: Praveen Kaligineedi Reviewed-by: Jeroen de Borst Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve.h | 15 +++++++-- drivers/net/ethernet/google/gve/gve_ethtool.c | 26 ++++++++++----- drivers/net/ethernet/google/gve/gve_main.c | 19 +++++++++++ drivers/net/ethernet/google/gve/gve_rx.c | 47 +++++++++++++++++++++++--- drivers/net/ethernet/google/gve/gve_tx.c | 48 +++++++++++++++++++++++++-- 5 files changed, 138 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 8d5234d4ba67..a3b2aec2c575 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -236,6 +236,7 @@ struct gve_rx_ring { u64 rx_frag_alloc_cnt; /* free-running count of rx page allocations */ u64 xdp_tx_errors; u64 xdp_redirect_errors; + u64 xdp_alloc_fails; u64 xdp_actions[GVE_XDP_ACTIONS]; u32 q_num; /* queue index */ u32 ntfy_id; /* notification block index */ @@ -247,6 +248,7 @@ struct gve_rx_ring { /* XDP stuff */ struct xdp_rxq_info xdp_rxq; + struct page_frag_cache page_cache; /* Page cache to allocate XDP frames */ }; /* A TX desc ring entry */ @@ -267,7 +269,10 @@ struct gve_tx_iovec { * ring entry but only used for a pkt_desc not a seg_desc */ struct gve_tx_buffer_state { - struct sk_buff *skb; /* skb for this pkt */ + union { + struct sk_buff *skb; /* skb for this pkt */ + struct xdp_frame *xdp_frame; /* xdp_frame */ + }; struct { u16 size; /* size of xmitted xdp pkt */ } xdp; @@ -385,6 +390,8 @@ struct gve_tx_ring { struct { /* Spinlock for when cleanup in progress */ spinlock_t clean_lock; + /* Spinlock for XDP tx traffic */ + spinlock_t xdp_lock; }; /* DQO fields. */ @@ -462,6 +469,8 @@ struct gve_tx_ring { dma_addr_t q_resources_bus; /* dma address of the queue resources */ dma_addr_t complq_bus_dqo; /* dma address of the dqo.compl_ring */ struct u64_stats_sync statss; /* sync stats for 32bit archs */ + u64 xdp_xmit; + u64 xdp_xmit_errors; } ____cacheline_aligned; /* Wraps the info for one irq including the napi struct and the queues @@ -919,8 +928,10 @@ void gve_free_page(struct device *dev, struct page *page, dma_addr_t dma, enum dma_data_direction); /* tx handling */ netdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev); +int gve_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, + u32 flags); int gve_xdp_xmit_one(struct gve_priv *priv, struct gve_tx_ring *tx, - void *data, int len); + void *data, int len, void *frame_p); void gve_xdp_tx_flush(struct gve_priv *priv, u32 xdp_qid); bool gve_tx_poll(struct gve_notify_block *block, int budget); bool gve_xdp_poll(struct gve_notify_block *block, int budget); diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index 067b393ccf9d..23db0f3534a8 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -56,13 +56,14 @@ static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = { "rx_drops_packet_over_mru[%u]", "rx_drops_invalid_checksum[%u]", "rx_xdp_aborted[%u]", "rx_xdp_drop[%u]", "rx_xdp_pass[%u]", "rx_xdp_tx[%u]", "rx_xdp_redirect[%u]", - "rx_xdp_tx_errors[%u]", "rx_xdp_redirect_errors[%u]", + "rx_xdp_tx_errors[%u]", "rx_xdp_redirect_errors[%u]", "rx_xdp_alloc_fails[%u]", }; static const char gve_gstrings_tx_stats[][ETH_GSTRING_LEN] = { "tx_posted_desc[%u]", "tx_completed_desc[%u]", "tx_consumed_desc[%u]", "tx_bytes[%u]", "tx_wake[%u]", "tx_stop[%u]", "tx_event_counter[%u]", "tx_dma_mapping_error[%u]", + "tx_xdp_xmit[%u]", "tx_xdp_xmit_errors[%u]" }; static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = { @@ -313,9 +314,10 @@ gve_get_ethtool_stats(struct net_device *netdev, data[i + j] = rx->xdp_actions[j]; data[i + j++] = rx->xdp_tx_errors; data[i + j++] = rx->xdp_redirect_errors; + data[i + j++] = rx->xdp_alloc_fails; } while (u64_stats_fetch_retry(&priv->rx[ring].statss, start)); - i += GVE_XDP_ACTIONS + 2; /* XDP rx counters */ + i += GVE_XDP_ACTIONS + 3; /* XDP rx counters */ } } else { i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS; @@ -371,13 +373,21 @@ gve_get_ethtool_stats(struct net_device *netdev, if (skip_nic_stats) { /* skip NIC tx stats */ i += NIC_TX_STATS_REPORT_NUM; - continue; - } - for (j = 0; j < NIC_TX_STATS_REPORT_NUM; j++) { - u64 value = - be64_to_cpu(report_stats[tx_qid_to_stats_idx[ring] + j].value); - data[i++] = value; + } else { + stats_idx = tx_qid_to_stats_idx[ring]; + for (j = 0; j < NIC_TX_STATS_REPORT_NUM; j++) { + u64 value = + be64_to_cpu(report_stats[stats_idx + j].value); + data[i++] = value; + } } + do { + start = u64_stats_fetch_begin(&priv->tx[ring].statss); + data[i] = tx->xdp_xmit; + data[i + 1] = tx->xdp_xmit_errors; + } while (u64_stats_fetch_retry(&priv->tx[ring].statss, + start)); + i += 2; /* XDP tx counters */ } } else { i += num_tx_queues * NUM_GVE_TX_CNTS; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index f49398857921..2e8ea4dd71e8 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -1230,6 +1230,21 @@ static void gve_unreg_xdp_info(struct gve_priv *priv) } } +static void gve_drain_page_cache(struct gve_priv *priv) +{ + struct page_frag_cache *nc; + int i; + + for (i = 0; i < priv->rx_cfg.num_queues; i++) { + nc = &priv->rx[i].page_cache; + if (nc->va) { + __page_frag_cache_drain(virt_to_page(nc->va), + nc->pagecnt_bias); + nc->va = NULL; + } + } +} + static int gve_open(struct net_device *dev) { struct gve_priv *priv = netdev_priv(dev); @@ -1313,6 +1328,7 @@ static int gve_close(struct net_device *dev) netif_carrier_off(dev); if (gve_get_device_rings_ok(priv)) { gve_turndown(priv); + gve_drain_page_cache(priv); err = gve_destroy_rings(priv); if (err) goto err; @@ -1696,6 +1712,7 @@ static const struct net_device_ops gve_netdev_ops = { .ndo_tx_timeout = gve_tx_timeout, .ndo_set_features = gve_set_features, .ndo_bpf = gve_xdp, + .ndo_xdp_xmit = gve_xdp_xmit, }; static void gve_handle_status(struct gve_priv *priv, u32 status) @@ -1819,6 +1836,8 @@ static void gve_set_netdev_xdp_features(struct gve_priv *priv) { if (priv->queue_format == GVE_GQI_QPL_FORMAT) { priv->dev->xdp_features = NETDEV_XDP_ACT_BASIC; + priv->dev->xdp_features |= NETDEV_XDP_ACT_REDIRECT; + priv->dev->xdp_features |= NETDEV_XDP_ACT_NDO_XMIT; } else { priv->dev->xdp_features = 0; } diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index 3241f6ea29be..ed4b5a540e6d 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -593,6 +593,35 @@ static struct sk_buff *gve_rx_skb(struct gve_priv *priv, struct gve_rx_ring *rx, return skb; } +static int gve_xdp_redirect(struct net_device *dev, struct gve_rx_ring *rx, + struct xdp_buff *orig, struct bpf_prog *xdp_prog) +{ + int total_len, len = orig->data_end - orig->data; + int headroom = XDP_PACKET_HEADROOM; + struct xdp_buff new; + void *frame; + int err; + + total_len = headroom + SKB_DATA_ALIGN(len) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + frame = page_frag_alloc(&rx->page_cache, total_len, GFP_ATOMIC); + if (!frame) { + u64_stats_update_begin(&rx->statss); + rx->xdp_alloc_fails++; + u64_stats_update_end(&rx->statss); + return -ENOMEM; + } + xdp_init_buff(&new, total_len, &rx->xdp_rxq); + xdp_prepare_buff(&new, frame, headroom, len, false); + memcpy(new.data, orig->data, len); + + err = xdp_do_redirect(dev, &new, xdp_prog); + if (err) + page_frag_free(frame); + + return err; +} + static void gve_xdp_done(struct gve_priv *priv, struct gve_rx_ring *rx, struct xdp_buff *xdp, struct bpf_prog *xprog, int xdp_act) @@ -609,8 +638,10 @@ static void gve_xdp_done(struct gve_priv *priv, struct gve_rx_ring *rx, case XDP_TX: tx_qid = gve_xdp_tx_queue_id(priv, rx->q_num); tx = &priv->tx[tx_qid]; + spin_lock(&tx->xdp_lock); err = gve_xdp_xmit_one(priv, tx, xdp->data, - xdp->data_end - xdp->data); + xdp->data_end - xdp->data, NULL); + spin_unlock(&tx->xdp_lock); if (unlikely(err)) { u64_stats_update_begin(&rx->statss); @@ -619,9 +650,13 @@ static void gve_xdp_done(struct gve_priv *priv, struct gve_rx_ring *rx, } break; case XDP_REDIRECT: - u64_stats_update_begin(&rx->statss); - rx->xdp_redirect_errors++; - u64_stats_update_end(&rx->statss); + err = gve_xdp_redirect(priv->dev, rx, xdp, xprog); + + if (unlikely(err)) { + u64_stats_update_begin(&rx->statss); + rx->xdp_redirect_errors++; + u64_stats_update_end(&rx->statss); + } break; } u64_stats_update_begin(&rx->statss); @@ -841,6 +876,7 @@ static bool gve_rx_refill_buffers(struct gve_priv *priv, struct gve_rx_ring *rx) static int gve_clean_rx_done(struct gve_rx_ring *rx, int budget, netdev_features_t feat) { + u64 xdp_redirects = rx->xdp_actions[XDP_REDIRECT]; u64 xdp_txs = rx->xdp_actions[XDP_TX]; struct gve_rx_ctx *ctx = &rx->ctx; struct gve_priv *priv = rx->gve; @@ -892,6 +928,9 @@ static int gve_clean_rx_done(struct gve_rx_ring *rx, int budget, if (xdp_txs != rx->xdp_actions[XDP_TX]) gve_xdp_tx_flush(priv, rx->q_num); + if (xdp_redirects != rx->xdp_actions[XDP_REDIRECT]) + xdp_do_flush(); + /* restock ring slots */ if (!rx->data.raw_addressing) { /* In QPL mode buffs are refilled as the desc are processed */ diff --git a/drivers/net/ethernet/google/gve/gve_tx.c b/drivers/net/ethernet/google/gve/gve_tx.c index 3e96ee7537ce..d928c3c79618 100644 --- a/drivers/net/ethernet/google/gve/gve_tx.c +++ b/drivers/net/ethernet/google/gve/gve_tx.c @@ -173,6 +173,10 @@ static int gve_clean_xdp_done(struct gve_priv *priv, struct gve_tx_ring *tx, pkts++; info->xdp.size = 0; + if (info->xdp_frame) { + xdp_return_frame(info->xdp_frame); + info->xdp_frame = NULL; + } space_freed += gve_tx_clear_buffer_state(info); } @@ -233,6 +237,7 @@ static int gve_tx_alloc_ring(struct gve_priv *priv, int idx) /* Make sure everything is zeroed to start */ memset(tx, 0, sizeof(*tx)); spin_lock_init(&tx->clean_lock); + spin_lock_init(&tx->xdp_lock); tx->q_num = idx; tx->mask = slots - 1; @@ -715,7 +720,7 @@ netdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev) } static int gve_tx_fill_xdp(struct gve_priv *priv, struct gve_tx_ring *tx, - void *data, int len) + void *data, int len, void *frame_p) { int pad, nfrags, ndescs, iovi, offset; struct gve_tx_buffer_state *info; @@ -725,6 +730,7 @@ static int gve_tx_fill_xdp(struct gve_priv *priv, struct gve_tx_ring *tx, if (pad >= GVE_TX_MAX_HEADER_SIZE) pad = 0; info = &tx->info[reqi & tx->mask]; + info->xdp_frame = frame_p; info->xdp.size = len; nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, pad + len, @@ -759,15 +765,51 @@ static int gve_tx_fill_xdp(struct gve_priv *priv, struct gve_tx_ring *tx, return ndescs; } +int gve_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, + u32 flags) +{ + struct gve_priv *priv = netdev_priv(dev); + struct gve_tx_ring *tx; + int i, err = 0, qid; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + qid = gve_xdp_tx_queue_id(priv, + smp_processor_id() % priv->num_xdp_queues); + + tx = &priv->tx[qid]; + + spin_lock(&tx->xdp_lock); + for (i = 0; i < n; i++) { + err = gve_xdp_xmit_one(priv, tx, frames[i]->data, + frames[i]->len, frames[i]); + if (err) + break; + } + + if (flags & XDP_XMIT_FLUSH) + gve_tx_put_doorbell(priv, tx->q_resources, tx->req); + + spin_unlock(&tx->xdp_lock); + + u64_stats_update_begin(&tx->statss); + tx->xdp_xmit += n; + tx->xdp_xmit_errors += n - i; + u64_stats_update_end(&tx->statss); + + return i ? i : err; +} + int gve_xdp_xmit_one(struct gve_priv *priv, struct gve_tx_ring *tx, - void *data, int len) + void *data, int len, void *frame_p) { int nsegs; if (!gve_can_tx(tx, len + GVE_TX_MAX_HEADER_SIZE - 1)) return -EBUSY; - nsegs = gve_tx_fill_xdp(priv, tx, data, len); + nsegs = gve_tx_fill_xdp(priv, tx, data, len, frame_p); tx->req += nsegs; return 0; -- cgit v1.2.3 From fd8e40321a12391e6f554cc637d0c4b6109682a9 Mon Sep 17 00:00:00 2001 From: Praveen Kaligineedi Date: Wed, 15 Mar 2023 16:33:12 -0700 Subject: gve: Add AF_XDP zero-copy support for GQI-QPL format Adding AF_XDP zero-copy support. Note: Although these changes support AF_XDP socket in zero-copy mode, there is still a copy happening within the driver between XSK buffer pool and QPL bounce buffers in GQI-QPL format. In GQI-QPL queue format, the driver needs to allocate a fixed size memory, the size specified by vNIC device, for RX/TX and register this memory as a bounce buffer with the vNIC device when a queue is created. The number of pages in the bounce buffer is limited and the pages need to be made available to the vNIC by copying the RX data out to prevent head-of-line blocking. Therefore, we cannot pass the XSK buffer pool to the vNIC. The number of copies on RX path from the bounce buffer to XSK buffer is 2 for AF_XDP copy mode (bounce buffer -> allocated page frag -> XSK buffer) and 1 for AF_XDP zero-copy mode (bounce buffer -> XSK buffer). This patch contains the following changes: 1) Enable and disable XSK buffer pool 2) Copy XDP packets from QPL bounce buffers to XSK buffer on rx 3) Copy XDP packets from XSK buffer to QPL bounce buffers and ring the doorbell as part of XDP TX napi poll 4) ndo_xsk_wakeup callback support Signed-off-by: Praveen Kaligineedi Reviewed-by: Jeroen de Borst Signed-off-by: David S. Miller --- drivers/net/ethernet/google/gve/gve.h | 7 ++ drivers/net/ethernet/google/gve/gve_ethtool.c | 14 ++- drivers/net/ethernet/google/gve/gve_main.c | 174 +++++++++++++++++++++++++- drivers/net/ethernet/google/gve/gve_rx.c | 30 +++++ drivers/net/ethernet/google/gve/gve_tx.c | 58 ++++++++- 5 files changed, 274 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index a3b2aec2c575..e214b51d3c8b 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -248,6 +248,8 @@ struct gve_rx_ring { /* XDP stuff */ struct xdp_rxq_info xdp_rxq; + struct xdp_rxq_info xsk_rxq; + struct xsk_buff_pool *xsk_pool; struct page_frag_cache page_cache; /* Page cache to allocate XDP frames */ }; @@ -275,6 +277,7 @@ struct gve_tx_buffer_state { }; struct { u16 size; /* size of xmitted xdp pkt */ + u8 is_xsk; /* xsk buff */ } xdp; union { struct gve_tx_iovec iov[GVE_TX_MAX_IOVEC]; /* segments of this pkt */ @@ -469,6 +472,10 @@ struct gve_tx_ring { dma_addr_t q_resources_bus; /* dma address of the queue resources */ dma_addr_t complq_bus_dqo; /* dma address of the dqo.compl_ring */ struct u64_stats_sync statss; /* sync stats for 32bit archs */ + struct xsk_buff_pool *xsk_pool; + u32 xdp_xsk_wakeup; + u32 xdp_xsk_done; + u64 xdp_xsk_sent; u64 xdp_xmit; u64 xdp_xmit_errors; } ____cacheline_aligned; diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index 23db0f3534a8..b18804e934d3 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -62,8 +62,8 @@ static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = { static const char gve_gstrings_tx_stats[][ETH_GSTRING_LEN] = { "tx_posted_desc[%u]", "tx_completed_desc[%u]", "tx_consumed_desc[%u]", "tx_bytes[%u]", "tx_wake[%u]", "tx_stop[%u]", "tx_event_counter[%u]", - "tx_dma_mapping_error[%u]", - "tx_xdp_xmit[%u]", "tx_xdp_xmit_errors[%u]" + "tx_dma_mapping_error[%u]", "tx_xsk_wakeup[%u]", + "tx_xsk_done[%u]", "tx_xsk_sent[%u]", "tx_xdp_xmit[%u]", "tx_xdp_xmit_errors[%u]" }; static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = { @@ -381,13 +381,17 @@ gve_get_ethtool_stats(struct net_device *netdev, data[i++] = value; } } + /* XDP xsk counters */ + data[i++] = tx->xdp_xsk_wakeup; + data[i++] = tx->xdp_xsk_done; do { start = u64_stats_fetch_begin(&priv->tx[ring].statss); - data[i] = tx->xdp_xmit; - data[i + 1] = tx->xdp_xmit_errors; + data[i] = tx->xdp_xsk_sent; + data[i + 1] = tx->xdp_xmit; + data[i + 2] = tx->xdp_xmit_errors; } while (u64_stats_fetch_retry(&priv->tx[ring].statss, start)); - i += 2; /* XDP tx counters */ + i += 3; /* XDP tx counters */ } } else { i += num_tx_queues * NUM_GVE_TX_CNTS; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 2e8ea4dd71e8..57ce74315eba 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "gve.h" #include "gve_dqo.h" #include "gve_adminq.h" @@ -1188,6 +1189,7 @@ static int gve_reg_xdp_info(struct gve_priv *priv, struct net_device *dev) struct gve_rx_ring *rx; int err = 0; int i, j; + u32 tx_qid; if (!priv->num_xdp_queues) return 0; @@ -1204,6 +1206,24 @@ static int gve_reg_xdp_info(struct gve_priv *priv, struct net_device *dev) MEM_TYPE_PAGE_SHARED, NULL); if (err) goto err; + rx->xsk_pool = xsk_get_pool_from_qid(dev, i); + if (rx->xsk_pool) { + err = xdp_rxq_info_reg(&rx->xsk_rxq, dev, i, + napi->napi_id); + if (err) + goto err; + err = xdp_rxq_info_reg_mem_model(&rx->xsk_rxq, + MEM_TYPE_XSK_BUFF_POOL, NULL); + if (err) + goto err; + xsk_pool_set_rxq_info(rx->xsk_pool, + &rx->xsk_rxq); + } + } + + for (i = 0; i < priv->num_xdp_queues; i++) { + tx_qid = gve_xdp_tx_queue_id(priv, i); + priv->tx[tx_qid].xsk_pool = xsk_get_pool_from_qid(dev, i); } return 0; @@ -1212,13 +1232,15 @@ err: rx = &priv->rx[j]; if (xdp_rxq_info_is_reg(&rx->xdp_rxq)) xdp_rxq_info_unreg(&rx->xdp_rxq); + if (xdp_rxq_info_is_reg(&rx->xsk_rxq)) + xdp_rxq_info_unreg(&rx->xsk_rxq); } return err; } static void gve_unreg_xdp_info(struct gve_priv *priv) { - int i; + int i, tx_qid; if (!priv->num_xdp_queues) return; @@ -1227,6 +1249,15 @@ static void gve_unreg_xdp_info(struct gve_priv *priv) struct gve_rx_ring *rx = &priv->rx[i]; xdp_rxq_info_unreg(&rx->xdp_rxq); + if (rx->xsk_pool) { + xdp_rxq_info_unreg(&rx->xsk_rxq); + rx->xsk_pool = NULL; + } + } + + for (i = 0; i < priv->num_xdp_queues; i++) { + tx_qid = gve_xdp_tx_queue_id(priv, i); + priv->tx[tx_qid].xsk_pool = NULL; } } @@ -1469,6 +1500,140 @@ out: return err; } +static int gve_xsk_pool_enable(struct net_device *dev, + struct xsk_buff_pool *pool, + u16 qid) +{ + struct gve_priv *priv = netdev_priv(dev); + struct napi_struct *napi; + struct gve_rx_ring *rx; + int tx_qid; + int err; + + if (qid >= priv->rx_cfg.num_queues) { + dev_err(&priv->pdev->dev, "xsk pool invalid qid %d", qid); + return -EINVAL; + } + if (xsk_pool_get_rx_frame_size(pool) < + priv->dev->max_mtu + sizeof(struct ethhdr)) { + dev_err(&priv->pdev->dev, "xsk pool frame_len too small"); + return -EINVAL; + } + + err = xsk_pool_dma_map(pool, &priv->pdev->dev, + DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING); + if (err) + return err; + + /* If XDP prog is not installed, return */ + if (!priv->xdp_prog) + return 0; + + rx = &priv->rx[qid]; + napi = &priv->ntfy_blocks[rx->ntfy_id].napi; + err = xdp_rxq_info_reg(&rx->xsk_rxq, dev, qid, napi->napi_id); + if (err) + goto err; + + err = xdp_rxq_info_reg_mem_model(&rx->xsk_rxq, + MEM_TYPE_XSK_BUFF_POOL, NULL); + if (err) + goto err; + + xsk_pool_set_rxq_info(pool, &rx->xsk_rxq); + rx->xsk_pool = pool; + + tx_qid = gve_xdp_tx_queue_id(priv, qid); + priv->tx[tx_qid].xsk_pool = pool; + + return 0; +err: + if (xdp_rxq_info_is_reg(&rx->xsk_rxq)) + xdp_rxq_info_unreg(&rx->xsk_rxq); + + xsk_pool_dma_unmap(pool, + DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING); + return err; +} + +static int gve_xsk_pool_disable(struct net_device *dev, + u16 qid) +{ + struct gve_priv *priv = netdev_priv(dev); + struct napi_struct *napi_rx; + struct napi_struct *napi_tx; + struct xsk_buff_pool *pool; + int tx_qid; + + pool = xsk_get_pool_from_qid(dev, qid); + if (!pool) + return -EINVAL; + if (qid >= priv->rx_cfg.num_queues) + return -EINVAL; + + /* If XDP prog is not installed, unmap DMA and return */ + if (!priv->xdp_prog) + goto done; + + tx_qid = gve_xdp_tx_queue_id(priv, qid); + if (!netif_running(dev)) { + priv->rx[qid].xsk_pool = NULL; + xdp_rxq_info_unreg(&priv->rx[qid].xsk_rxq); + priv->tx[tx_qid].xsk_pool = NULL; + goto done; + } + + napi_rx = &priv->ntfy_blocks[priv->rx[qid].ntfy_id].napi; + napi_disable(napi_rx); /* make sure current rx poll is done */ + + napi_tx = &priv->ntfy_blocks[priv->tx[tx_qid].ntfy_id].napi; + napi_disable(napi_tx); /* make sure current tx poll is done */ + + priv->rx[qid].xsk_pool = NULL; + xdp_rxq_info_unreg(&priv->rx[qid].xsk_rxq); + priv->tx[tx_qid].xsk_pool = NULL; + smp_mb(); /* Make sure it is visible to the workers on datapath */ + + napi_enable(napi_rx); + if (gve_rx_work_pending(&priv->rx[qid])) + napi_schedule(napi_rx); + + napi_enable(napi_tx); + if (gve_tx_clean_pending(priv, &priv->tx[tx_qid])) + napi_schedule(napi_tx); + +done: + xsk_pool_dma_unmap(pool, + DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING); + return 0; +} + +static int gve_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags) +{ + struct gve_priv *priv = netdev_priv(dev); + int tx_queue_id = gve_xdp_tx_queue_id(priv, queue_id); + + if (queue_id >= priv->rx_cfg.num_queues || !priv->xdp_prog) + return -EINVAL; + + if (flags & XDP_WAKEUP_TX) { + struct gve_tx_ring *tx = &priv->tx[tx_queue_id]; + struct napi_struct *napi = + &priv->ntfy_blocks[tx->ntfy_id].napi; + + if (!napi_if_scheduled_mark_missed(napi)) { + /* Call local_bh_enable to trigger SoftIRQ processing */ + local_bh_disable(); + napi_schedule(napi); + local_bh_enable(); + } + + tx->xdp_xsk_wakeup++; + } + + return 0; +} + static int verify_xdp_configuration(struct net_device *dev) { struct gve_priv *priv = netdev_priv(dev); @@ -1512,6 +1677,11 @@ static int gve_xdp(struct net_device *dev, struct netdev_bpf *xdp) switch (xdp->command) { case XDP_SETUP_PROG: return gve_set_xdp(priv, xdp->prog, xdp->extack); + case XDP_SETUP_XSK_POOL: + if (xdp->xsk.pool) + return gve_xsk_pool_enable(dev, xdp->xsk.pool, xdp->xsk.queue_id); + else + return gve_xsk_pool_disable(dev, xdp->xsk.queue_id); default: return -EINVAL; } @@ -1713,6 +1883,7 @@ static const struct net_device_ops gve_netdev_ops = { .ndo_set_features = gve_set_features, .ndo_bpf = gve_xdp, .ndo_xdp_xmit = gve_xdp_xmit, + .ndo_xsk_wakeup = gve_xsk_wakeup, }; static void gve_handle_status(struct gve_priv *priv, u32 status) @@ -1838,6 +2009,7 @@ static void gve_set_netdev_xdp_features(struct gve_priv *priv) priv->dev->xdp_features = NETDEV_XDP_ACT_BASIC; priv->dev->xdp_features |= NETDEV_XDP_ACT_REDIRECT; priv->dev->xdp_features |= NETDEV_XDP_ACT_NDO_XMIT; + priv->dev->xdp_features |= NETDEV_XDP_ACT_XSK_ZEROCOPY; } else { priv->dev->xdp_features = 0; } diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index ed4b5a540e6d..d1da7413dc4d 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -10,6 +10,7 @@ #include #include #include +#include static void gve_rx_free_buffer(struct device *dev, struct gve_rx_slot_page_info *page_info, @@ -593,6 +594,31 @@ static struct sk_buff *gve_rx_skb(struct gve_priv *priv, struct gve_rx_ring *rx, return skb; } +static int gve_xsk_pool_redirect(struct net_device *dev, + struct gve_rx_ring *rx, + void *data, int len, + struct bpf_prog *xdp_prog) +{ + struct xdp_buff *xdp; + int err; + + if (rx->xsk_pool->frame_len < len) + return -E2BIG; + xdp = xsk_buff_alloc(rx->xsk_pool); + if (!xdp) { + u64_stats_update_begin(&rx->statss); + rx->xdp_alloc_fails++; + u64_stats_update_end(&rx->statss); + return -ENOMEM; + } + xdp->data_end = xdp->data + len; + memcpy(xdp->data, data, len); + err = xdp_do_redirect(dev, xdp, xdp_prog); + if (err) + xsk_buff_free(xdp); + return err; +} + static int gve_xdp_redirect(struct net_device *dev, struct gve_rx_ring *rx, struct xdp_buff *orig, struct bpf_prog *xdp_prog) { @@ -602,6 +628,10 @@ static int gve_xdp_redirect(struct net_device *dev, struct gve_rx_ring *rx, void *frame; int err; + if (rx->xsk_pool) + return gve_xsk_pool_redirect(dev, rx, orig->data, + len, xdp_prog); + total_len = headroom + SKB_DATA_ALIGN(len) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); frame = page_frag_alloc(&rx->page_cache, total_len, GFP_ATOMIC); diff --git a/drivers/net/ethernet/google/gve/gve_tx.c b/drivers/net/ethernet/google/gve/gve_tx.c index d928c3c79618..e50510b8e784 100644 --- a/drivers/net/ethernet/google/gve/gve_tx.c +++ b/drivers/net/ethernet/google/gve/gve_tx.c @@ -11,6 +11,7 @@ #include #include #include +#include static inline void gve_tx_put_doorbell(struct gve_priv *priv, struct gve_queue_resources *q_resources, @@ -160,6 +161,7 @@ static int gve_clean_xdp_done(struct gve_priv *priv, struct gve_tx_ring *tx, u32 clean_end = tx->done + to_do; u64 pkts = 0, bytes = 0; size_t space_freed = 0; + u32 xsk_complete = 0; u32 idx; for (; tx->done < clean_end; tx->done++) { @@ -171,6 +173,7 @@ static int gve_clean_xdp_done(struct gve_priv *priv, struct gve_tx_ring *tx, bytes += info->xdp.size; pkts++; + xsk_complete += info->xdp.is_xsk; info->xdp.size = 0; if (info->xdp_frame) { @@ -181,6 +184,8 @@ static int gve_clean_xdp_done(struct gve_priv *priv, struct gve_tx_ring *tx, } gve_tx_free_fifo(&tx->tx_fifo, space_freed); + if (xsk_complete > 0 && tx->xsk_pool) + xsk_tx_completed(tx->xsk_pool, xsk_complete); u64_stats_update_begin(&tx->statss); tx->bytes_done += bytes; tx->pkt_done += pkts; @@ -720,7 +725,7 @@ netdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev) } static int gve_tx_fill_xdp(struct gve_priv *priv, struct gve_tx_ring *tx, - void *data, int len, void *frame_p) + void *data, int len, void *frame_p, bool is_xsk) { int pad, nfrags, ndescs, iovi, offset; struct gve_tx_buffer_state *info; @@ -732,6 +737,7 @@ static int gve_tx_fill_xdp(struct gve_priv *priv, struct gve_tx_ring *tx, info = &tx->info[reqi & tx->mask]; info->xdp_frame = frame_p; info->xdp.size = len; + info->xdp.is_xsk = is_xsk; nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, pad + len, &info->iov[0]); @@ -809,7 +815,7 @@ int gve_xdp_xmit_one(struct gve_priv *priv, struct gve_tx_ring *tx, if (!gve_can_tx(tx, len + GVE_TX_MAX_HEADER_SIZE - 1)) return -EBUSY; - nsegs = gve_tx_fill_xdp(priv, tx, data, len, frame_p); + nsegs = gve_tx_fill_xdp(priv, tx, data, len, frame_p, false); tx->req += nsegs; return 0; @@ -882,11 +888,43 @@ u32 gve_tx_load_event_counter(struct gve_priv *priv, return be32_to_cpu(counter); } +static int gve_xsk_tx(struct gve_priv *priv, struct gve_tx_ring *tx, + int budget) +{ + struct xdp_desc desc; + int sent = 0, nsegs; + void *data; + + spin_lock(&tx->xdp_lock); + while (sent < budget) { + if (!gve_can_tx(tx, GVE_TX_START_THRESH)) + goto out; + + if (!xsk_tx_peek_desc(tx->xsk_pool, &desc)) { + tx->xdp_xsk_done = tx->xdp_xsk_wakeup; + goto out; + } + + data = xsk_buff_raw_get_data(tx->xsk_pool, desc.addr); + nsegs = gve_tx_fill_xdp(priv, tx, data, desc.len, NULL, true); + tx->req += nsegs; + sent++; + } +out: + if (sent > 0) { + gve_tx_put_doorbell(priv, tx->q_resources, tx->req); + xsk_tx_release(tx->xsk_pool); + } + spin_unlock(&tx->xdp_lock); + return sent; +} + bool gve_xdp_poll(struct gve_notify_block *block, int budget) { struct gve_priv *priv = block->priv; struct gve_tx_ring *tx = block->tx; u32 nic_done; + bool repoll; u32 to_do; /* If budget is 0, do all the work */ @@ -897,7 +935,21 @@ bool gve_xdp_poll(struct gve_notify_block *block, int budget) nic_done = gve_tx_load_event_counter(priv, tx); to_do = min_t(u32, (nic_done - tx->done), budget); gve_clean_xdp_done(priv, tx, to_do); - return nic_done != tx->done; + repoll = nic_done != tx->done; + + if (tx->xsk_pool) { + int sent = gve_xsk_tx(priv, tx, budget); + + u64_stats_update_begin(&tx->statss); + tx->xdp_xsk_sent += sent; + u64_stats_update_end(&tx->statss); + repoll |= (sent == budget); + if (xsk_uses_need_wakeup(tx->xsk_pool)) + xsk_set_tx_need_wakeup(tx->xsk_pool); + } + + /* If we still have work we want to repoll */ + return repoll; } bool gve_tx_poll(struct gve_notify_block *block, int budget) -- cgit v1.2.3 From 6365ba64b4dbe8b59ddaeaa724b281f3787715d5 Mon Sep 17 00:00:00 2001 From: Jeremi Piotrowski Date: Wed, 8 Mar 2023 15:05:31 +0000 Subject: ptp: kvm: Use decrypted memory in confidential guest on x86 KVM_HC_CLOCK_PAIRING currently fails inside SEV-SNP guests because the guest passes an address to static data to the host. In confidential computing the host can't access arbitrary guest memory so handling the hypercall runs into an "rmpfault". To make the hypercall work, the guest needs to explicitly mark the memory as decrypted. Do that in kvm_arch_ptp_init(), but retain the previous behavior for non-confidential guests to save us from having to allocate memory. Add a new arch-specific function (kvm_arch_ptp_exit()) to free the allocation and mark the memory as encrypted again. Signed-off-by: Jeremi Piotrowski Link: https://lore.kernel.org/r/20230308150531.477741-1-jpiotrowski@linux.microsoft.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_kvm_arm.c | 4 +++ drivers/ptp/ptp_kvm_common.c | 1 + drivers/ptp/ptp_kvm_x86.c | 59 +++++++++++++++++++++++++++++++++++--------- include/linux/ptp_kvm.h | 1 + 4 files changed, 54 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/ptp/ptp_kvm_arm.c b/drivers/ptp/ptp_kvm_arm.c index b7d28c8dfb84..e68e6943167b 100644 --- a/drivers/ptp/ptp_kvm_arm.c +++ b/drivers/ptp/ptp_kvm_arm.c @@ -22,6 +22,10 @@ int kvm_arch_ptp_init(void) return 0; } +void kvm_arch_ptp_exit(void) +{ +} + int kvm_arch_ptp_get_clock(struct timespec64 *ts) { return kvm_arch_ptp_get_crosststamp(NULL, ts, NULL); diff --git a/drivers/ptp/ptp_kvm_common.c b/drivers/ptp/ptp_kvm_common.c index 9141162c4237..2418977989be 100644 --- a/drivers/ptp/ptp_kvm_common.c +++ b/drivers/ptp/ptp_kvm_common.c @@ -130,6 +130,7 @@ static struct kvm_ptp_clock kvm_ptp_clock; static void __exit ptp_kvm_exit(void) { ptp_clock_unregister(kvm_ptp_clock.ptp_clock); + kvm_arch_ptp_exit(); } static int __init ptp_kvm_init(void) diff --git a/drivers/ptp/ptp_kvm_x86.c b/drivers/ptp/ptp_kvm_x86.c index 4991054a2135..902844cc1a17 100644 --- a/drivers/ptp/ptp_kvm_x86.c +++ b/drivers/ptp/ptp_kvm_x86.c @@ -14,27 +14,64 @@ #include #include #include +#include static phys_addr_t clock_pair_gpa; -static struct kvm_clock_pairing clock_pair; +static struct kvm_clock_pairing clock_pair_glbl; +static struct kvm_clock_pairing *clock_pair; int kvm_arch_ptp_init(void) { + struct page *p; long ret; if (!kvm_para_available()) return -ENODEV; - clock_pair_gpa = slow_virt_to_phys(&clock_pair); - if (!pvclock_get_pvti_cpu0_va()) - return -ENODEV; + if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { + p = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!p) + return -ENOMEM; + + clock_pair = page_address(p); + ret = set_memory_decrypted((unsigned long)clock_pair, 1); + if (ret) { + __free_page(p); + clock_pair = NULL; + goto nofree; + } + } else { + clock_pair = &clock_pair_glbl; + } + + clock_pair_gpa = slow_virt_to_phys(clock_pair); + if (!pvclock_get_pvti_cpu0_va()) { + ret = -ENODEV; + goto err; + } ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, KVM_CLOCK_PAIRING_WALLCLOCK); - if (ret == -KVM_ENOSYS) - return -ENODEV; + if (ret == -KVM_ENOSYS) { + ret = -ENODEV; + goto err; + } return ret; + +err: + kvm_arch_ptp_exit(); +nofree: + return ret; +} + +void kvm_arch_ptp_exit(void) +{ + if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { + WARN_ON(set_memory_encrypted((unsigned long)clock_pair, 1)); + free_page((unsigned long)clock_pair); + clock_pair = NULL; + } } int kvm_arch_ptp_get_clock(struct timespec64 *ts) @@ -49,8 +86,8 @@ int kvm_arch_ptp_get_clock(struct timespec64 *ts) return -EOPNOTSUPP; } - ts->tv_sec = clock_pair.sec; - ts->tv_nsec = clock_pair.nsec; + ts->tv_sec = clock_pair->sec; + ts->tv_nsec = clock_pair->nsec; return 0; } @@ -81,9 +118,9 @@ int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *tspec, pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret); return -EOPNOTSUPP; } - tspec->tv_sec = clock_pair.sec; - tspec->tv_nsec = clock_pair.nsec; - *cycle = __pvclock_read_cycles(src, clock_pair.tsc); + tspec->tv_sec = clock_pair->sec; + tspec->tv_nsec = clock_pair->nsec; + *cycle = __pvclock_read_cycles(src, clock_pair->tsc); } while (pvclock_read_retry(src, version)); *cs = &kvm_clock; diff --git a/include/linux/ptp_kvm.h b/include/linux/ptp_kvm.h index c2e28deef33a..746fd67c3480 100644 --- a/include/linux/ptp_kvm.h +++ b/include/linux/ptp_kvm.h @@ -14,6 +14,7 @@ struct timespec64; struct clocksource; int kvm_arch_ptp_init(void); +void kvm_arch_ptp_exit(void); int kvm_arch_ptp_get_clock(struct timespec64 *ts); int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *tspec, struct clocksource **cs); -- cgit v1.2.3 From 4dd2744fae6dc705c5d2298fe0f0812d34f5b64e Mon Sep 17 00:00:00 2001 From: mengyuanlou Date: Wed, 15 Mar 2023 17:18:46 +0800 Subject: net: wangxun: Remove macro that is redefined Remove PCI_VENDOR_ID_WANGXUN which is redefined in drivers/pci/quirks. Signed-off-by: mengyuanlou Link: https://lore.kernel.org/r/20230315091846.17314-1-mengyuanlou@net-swift.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/libwx/wx_type.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 2b9efd13c500..2bec5b1bc196 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -7,11 +7,6 @@ #include #include -/* Vendor ID */ -#ifndef PCI_VENDOR_ID_WANGXUN -#define PCI_VENDOR_ID_WANGXUN 0x8088 -#endif - #define WX_NCSI_SUP 0x8000 #define WX_NCSI_MASK 0x8000 #define WX_WOL_SUP 0x4000 -- cgit v1.2.3 From b1a2de9ccfe63b50e493ebb57b85beb5785200c7 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 15 Mar 2023 17:38:43 +0100 Subject: net: dsa: mv88e6xxx: don't dispose of Global2 IRQ mappings from mdiobus code irq_find_mapping() does not need irq_dispose_mapping(), only irq_create_mapping() does. Calling irq_dispose_mapping() from mv88e6xxx_g2_irq_mdio_free() and from the error path of mv88e6xxx_g2_irq_mdio_setup() effectively means that the mdiobus logic (for internal PHY interrupts) is disposing of a hwirq->virq mapping which it is not responsible of (but instead, the function pair mv88e6xxx_g2_irq_setup() + mv88e6xxx_g2_irq_free() is). With the current code structure, this isn't such a huge problem, because mv88e6xxx_g2_irq_mdio_free() is called relatively close to the real owner of the IRQ mappings: mv88e6xxx_remove() -> mv88e6xxx_unregister_switch() -> mv88e6xxx_mdios_unregister() -> mv88e6xxx_g2_irq_mdio_free() -> mv88e6xxx_g2_irq_free() and the switch isn't 'live' in any way such that it would be able of generating interrupts at this point (mv88e6xxx_unregister_switch() has been called). However, there is a desire to split mv88e6xxx_mdios_unregister() and mv88e6xxx_g2_irq_free() such that mv88e6xxx_mdios_unregister() only gets called from mv88e6xxx_teardown(). This is much more problematic, as can be seen below. In a cross-chip scenario (say 3 switches d0032004.mdio-mii:10, d0032004.mdio-mii:11 and d0032004.mdio-mii:12 which form a single DSA tree), it is possible to unbind the device driver from a single switch (say d0032004.mdio-mii:10). When that happens, mv88e6xxx_remove() will be called for just that one switch, and this will call mv88e6xxx_unregister_switch() which will tear down the entire tree (calling mv88e6xxx_teardown() for all 3 switches). Assuming mv88e6xxx_mdios_unregister() was moved to mv88e6xxx_teardown(), at this stage, all 3 switches will have called irq_dispose_mapping() on their mdiobus virqs. When we bind again the device driver to d0032004.mdio-mii:10, mv88e6xxx_probe() is called for it, which calls dsa_register_switch(). The DSA tree is now complete again, and mv88e6xxx_setup() is called for all 3 switches. Also assuming that mv88e6xxx_mdios_register() is moved to mv88e6xxx_setup() (the 2 assumptions go together), at this point, d0032004.mdio-mii:11 and d0032004.mdio-mii:12 don't have an IRQ mapping for the internal PHYs anymore, as they've disposed of it in mv88e6xxx_teardown(). Whereas switch d0032004.mdio-mii:10 has re-created it, because its code path comes from mv88e6xxx_probe(). Simply put, this change prepares the driver to handle the movement of mv88e6xxx_mdios_register() to mv88e6xxx_setup() for cross-chip DSA trees. Also, the code being deleted was partially wrong anyway (in a way which may have hidden this other issue). mv88e6xxx_g2_irq_mdio_setup() populates bus->irq[] starting with offset chip->info->phy_base_addr, but the teardown path doesn't apply that offset too. So it disposes of virq 0 for phy = [ 0, phy_base_addr ). All switch families have phy_base_addr = 0, except for MV88E6141 and MV88E6341 which have it as 0x10. I guess those families would have happened to work by mistake in cross-chip scenarios too. I'm deleting the body of mv88e6xxx_g2_irq_mdio_free() but leaving its call sites and prototype in place. This is because, if we ever need to add back some teardown procedure in the future, it will be perhaps error-prone to deduce the proper call sites again. Whereas like this, no extra code should get generated, it shouldn't bother anybody. Signed-off-by: Vladimir Oltean Signed-off-by: Klaus Kudielka Tested-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/global2.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index ed3b2f88e783..a26546d3d7b5 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -1176,31 +1176,19 @@ out: int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip, struct mii_bus *bus) { - int phy, irq, err, err_phy; + int phy, irq; for (phy = 0; phy < chip->info->num_internal_phys; phy++) { irq = irq_find_mapping(chip->g2_irq.domain, phy); - if (irq < 0) { - err = irq; - goto out; - } + if (irq < 0) + return irq; + bus->irq[chip->info->phy_base_addr + phy] = irq; } return 0; -out: - err_phy = phy; - - for (phy = 0; phy < err_phy; phy++) - irq_dispose_mapping(bus->irq[phy]); - - return err; } void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip, struct mii_bus *bus) { - int phy; - - for (phy = 0; phy < chip->info->num_internal_phys; phy++) - irq_dispose_mapping(bus->irq[phy]); } -- cgit v1.2.3 From f1bee740fa821c28582bb17c42e2242041e598b8 Mon Sep 17 00:00:00 2001 From: Klaus Kudielka Date: Wed, 15 Mar 2023 17:38:44 +0100 Subject: net: dsa: mv88e6xxx: re-order functions Move mv88e6xxx_setup() below mv88e6xxx_mdios_register(), so that we are able to call the latter one from here. Do the same thing for the inverse functions. Signed-off-by: Klaus Kudielka Reviewed-by: Andrew Lunn Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 358 +++++++++++++++++++-------------------- 1 file changed, 179 insertions(+), 179 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 30383c4f8fd0..184c116dfc8f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3680,185 +3680,6 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip) return mv88e6xxx_software_reset(chip); } -static void mv88e6xxx_teardown(struct dsa_switch *ds) -{ - mv88e6xxx_teardown_devlink_params(ds); - dsa_devlink_resources_unregister(ds); - mv88e6xxx_teardown_devlink_regions_global(ds); -} - -static int mv88e6xxx_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_chip *chip = ds->priv; - u8 cmode; - int err; - int i; - - chip->ds = ds; - ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); - - /* Since virtual bridges are mapped in the PVT, the number we support - * depends on the physical switch topology. We need to let DSA figure - * that out and therefore we cannot set this at dsa_register_switch() - * time. - */ - if (mv88e6xxx_has_pvt(chip)) - ds->max_num_bridges = MV88E6XXX_MAX_PVT_SWITCHES - - ds->dst->last_switch - 1; - - mv88e6xxx_reg_lock(chip); - - if (chip->info->ops->setup_errata) { - err = chip->info->ops->setup_errata(chip); - if (err) - goto unlock; - } - - /* Cache the cmode of each port. */ - for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { - if (chip->info->ops->port_get_cmode) { - err = chip->info->ops->port_get_cmode(chip, i, &cmode); - if (err) - goto unlock; - - chip->ports[i].cmode = cmode; - } - } - - err = mv88e6xxx_vtu_setup(chip); - if (err) - goto unlock; - - /* Must be called after mv88e6xxx_vtu_setup (which flushes the - * VTU, thereby also flushing the STU). - */ - err = mv88e6xxx_stu_setup(chip); - if (err) - goto unlock; - - /* Setup Switch Port Registers */ - for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { - if (dsa_is_unused_port(ds, i)) - continue; - - /* Prevent the use of an invalid port. */ - if (mv88e6xxx_is_invalid_port(chip, i)) { - dev_err(chip->dev, "port %d is invalid\n", i); - err = -EINVAL; - goto unlock; - } - - err = mv88e6xxx_setup_port(chip, i); - if (err) - goto unlock; - } - - err = mv88e6xxx_irl_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_mac_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_phy_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_pvt_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_atu_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_broadcast_setup(chip, 0); - if (err) - goto unlock; - - err = mv88e6xxx_pot_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_rmu_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_rsvd2cpu_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_trunk_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_devmap_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_pri_setup(chip); - if (err) - goto unlock; - - /* Setup PTP Hardware Clock and timestamping */ - if (chip->info->ptp_support) { - err = mv88e6xxx_ptp_setup(chip); - if (err) - goto unlock; - - err = mv88e6xxx_hwtstamp_setup(chip); - if (err) - goto unlock; - } - - err = mv88e6xxx_stats_setup(chip); - if (err) - goto unlock; - -unlock: - mv88e6xxx_reg_unlock(chip); - - if (err) - return err; - - /* Have to be called without holding the register lock, since - * they take the devlink lock, and we later take the locks in - * the reverse order when getting/setting parameters or - * resource occupancy. - */ - err = mv88e6xxx_setup_devlink_resources(ds); - if (err) - return err; - - err = mv88e6xxx_setup_devlink_params(ds); - if (err) - goto out_resources; - - err = mv88e6xxx_setup_devlink_regions_global(ds); - if (err) - goto out_params; - - return 0; - -out_params: - mv88e6xxx_teardown_devlink_params(ds); -out_resources: - dsa_devlink_resources_unregister(ds); - - return err; -} - -static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port) -{ - return mv88e6xxx_setup_devlink_regions_port(ds, port); -} - -static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port) -{ - mv88e6xxx_teardown_devlink_regions_port(ds, port); -} - /* prod_id for switch families which do not have a PHY model number */ static const u16 family_prod_id_table[] = { [MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, @@ -4062,6 +3883,185 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, return 0; } +static void mv88e6xxx_teardown(struct dsa_switch *ds) +{ + mv88e6xxx_teardown_devlink_params(ds); + dsa_devlink_resources_unregister(ds); + mv88e6xxx_teardown_devlink_regions_global(ds); +} + +static int mv88e6xxx_setup(struct dsa_switch *ds) +{ + struct mv88e6xxx_chip *chip = ds->priv; + u8 cmode; + int err; + int i; + + chip->ds = ds; + ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); + + /* Since virtual bridges are mapped in the PVT, the number we support + * depends on the physical switch topology. We need to let DSA figure + * that out and therefore we cannot set this at dsa_register_switch() + * time. + */ + if (mv88e6xxx_has_pvt(chip)) + ds->max_num_bridges = MV88E6XXX_MAX_PVT_SWITCHES - + ds->dst->last_switch - 1; + + mv88e6xxx_reg_lock(chip); + + if (chip->info->ops->setup_errata) { + err = chip->info->ops->setup_errata(chip); + if (err) + goto unlock; + } + + /* Cache the cmode of each port. */ + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { + if (chip->info->ops->port_get_cmode) { + err = chip->info->ops->port_get_cmode(chip, i, &cmode); + if (err) + goto unlock; + + chip->ports[i].cmode = cmode; + } + } + + err = mv88e6xxx_vtu_setup(chip); + if (err) + goto unlock; + + /* Must be called after mv88e6xxx_vtu_setup (which flushes the + * VTU, thereby also flushing the STU). + */ + err = mv88e6xxx_stu_setup(chip); + if (err) + goto unlock; + + /* Setup Switch Port Registers */ + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { + if (dsa_is_unused_port(ds, i)) + continue; + + /* Prevent the use of an invalid port. */ + if (mv88e6xxx_is_invalid_port(chip, i)) { + dev_err(chip->dev, "port %d is invalid\n", i); + err = -EINVAL; + goto unlock; + } + + err = mv88e6xxx_setup_port(chip, i); + if (err) + goto unlock; + } + + err = mv88e6xxx_irl_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_mac_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_phy_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_pvt_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_atu_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_broadcast_setup(chip, 0); + if (err) + goto unlock; + + err = mv88e6xxx_pot_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_rmu_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_rsvd2cpu_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_trunk_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_devmap_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_pri_setup(chip); + if (err) + goto unlock; + + /* Setup PTP Hardware Clock and timestamping */ + if (chip->info->ptp_support) { + err = mv88e6xxx_ptp_setup(chip); + if (err) + goto unlock; + + err = mv88e6xxx_hwtstamp_setup(chip); + if (err) + goto unlock; + } + + err = mv88e6xxx_stats_setup(chip); + if (err) + goto unlock; + +unlock: + mv88e6xxx_reg_unlock(chip); + + if (err) + return err; + + /* Have to be called without holding the register lock, since + * they take the devlink lock, and we later take the locks in + * the reverse order when getting/setting parameters or + * resource occupancy. + */ + err = mv88e6xxx_setup_devlink_resources(ds); + if (err) + return err; + + err = mv88e6xxx_setup_devlink_params(ds); + if (err) + goto out_resources; + + err = mv88e6xxx_setup_devlink_regions_global(ds); + if (err) + goto out_params; + + return 0; + +out_params: + mv88e6xxx_teardown_devlink_params(ds); +out_resources: + dsa_devlink_resources_unregister(ds); + + return err; +} + +static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port) +{ + return mv88e6xxx_setup_devlink_regions_port(ds, port); +} + +static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port) +{ + mv88e6xxx_teardown_devlink_regions_port(ds, port); +} + static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; -- cgit v1.2.3 From 2cb0658d4f88608a19b3833789ac0ac8a4214f0b Mon Sep 17 00:00:00 2001 From: Klaus Kudielka Date: Wed, 15 Mar 2023 17:38:45 +0100 Subject: net: dsa: mv88e6xxx: move call to mv88e6xxx_mdios_register() Call the rather expensive mv88e6xxx_mdios_register() at the beginning of mv88e6xxx_setup(). This avoids the double call via mv88e6xxx_probe() during boot. For symmetry, call mv88e6xxx_mdios_unregister() at the end of mv88e6xxx_teardown(). Link: https://lore.kernel.org/lkml/449bde236c08d5ab5e54abd73b645d8b29955894.camel@gmail.com/ Suggested-by: Andrew Lunn Signed-off-by: Klaus Kudielka Reviewed-by: Andrew Lunn Reviewed-by: Vladimir Oltean Tested-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 184c116dfc8f..de25f755725e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3848,9 +3848,9 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) } } -static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, - struct device_node *np) +static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip) { + struct device_node *np = chip->dev->of_node; struct device_node *child; int err; @@ -3885,9 +3885,12 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, static void mv88e6xxx_teardown(struct dsa_switch *ds) { + struct mv88e6xxx_chip *chip = ds->priv; + mv88e6xxx_teardown_devlink_params(ds); dsa_devlink_resources_unregister(ds); mv88e6xxx_teardown_devlink_regions_global(ds); + mv88e6xxx_mdios_unregister(chip); } static int mv88e6xxx_setup(struct dsa_switch *ds) @@ -3897,6 +3900,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) int err; int i; + err = mv88e6xxx_mdios_register(chip); + if (err) + return err; + chip->ds = ds; ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); @@ -4023,7 +4030,7 @@ unlock: mv88e6xxx_reg_unlock(chip); if (err) - return err; + goto out_mdios; /* Have to be called without holding the register lock, since * they take the devlink lock, and we later take the locks in @@ -4032,7 +4039,7 @@ unlock: */ err = mv88e6xxx_setup_devlink_resources(ds); if (err) - return err; + goto out_mdios; err = mv88e6xxx_setup_devlink_params(ds); if (err) @@ -4048,6 +4055,8 @@ out_params: mv88e6xxx_teardown_devlink_params(ds); out_resources: dsa_devlink_resources_unregister(ds); +out_mdios: + mv88e6xxx_mdios_unregister(chip); return err; } @@ -7228,18 +7237,12 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) if (err) goto out_g1_atu_prob_irq; - err = mv88e6xxx_mdios_register(chip, np); - if (err) - goto out_g1_vtu_prob_irq; - err = mv88e6xxx_register_switch(chip); if (err) - goto out_mdio; + goto out_g1_vtu_prob_irq; return 0; -out_mdio: - mv88e6xxx_mdios_unregister(chip); out_g1_vtu_prob_irq: mv88e6xxx_g1_vtu_prob_irq_free(chip); out_g1_atu_prob_irq: @@ -7276,7 +7279,6 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) mv88e6xxx_phy_destroy(chip); mv88e6xxx_unregister_switch(chip); - mv88e6xxx_mdios_unregister(chip); mv88e6xxx_g1_vtu_prob_irq_free(chip); mv88e6xxx_g1_atu_prob_irq_free(chip); -- cgit v1.2.3 From 2c7e46edbd0308c12b8232ff4270d0894cbdce44 Mon Sep 17 00:00:00 2001 From: Klaus Kudielka Date: Wed, 15 Mar 2023 17:38:46 +0100 Subject: net: dsa: mv88e6xxx: mask apparently non-existing phys during probing To avoid excessive mdio bus transactions during probing, mask all phy addresses that do not exist (there is a 1:1 mapping between switch port number and phy address). Suggested-by: Andrew Lunn Signed-off-by: Klaus Kudielka Reviewed-by: Andrew Lunn Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index de25f755725e..260e38c5c6e6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3805,6 +3805,7 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, bus->read_c45 = mv88e6xxx_mdio_read_c45; bus->write_c45 = mv88e6xxx_mdio_write_c45; bus->parent = chip->dev; + bus->phy_mask = GENMASK(31, mv88e6xxx_num_ports(chip)); if (!external) { err = mv88e6xxx_g2_irq_mdio_setup(chip, bus); -- cgit v1.2.3 From ed0578a46c5f08647f5ecb0e3c735a53267426f7 Mon Sep 17 00:00:00 2001 From: Harini Katakam Date: Thu, 16 Mar 2023 14:00:50 +0530 Subject: net: macb: Increase halt timeout to accommodate 10Mbps link Increase halt timeout to accommodate for 16K SRAM at 10Mbps rounded. Signed-off-by: Harini Katakam Signed-off-by: Michal Simek Signed-off-by: Radhey Shyam Pandey Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20230316083050.2108-1-harini.katakam@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 26f41243743b..c4edd20c1c66 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -94,8 +94,7 @@ struct sifive_fu540_macb_mgmt { /* Graceful stop timeouts in us. We should allow up to * 1 frame time (10 Mbits/s, full-duplex, ignoring collisions) */ -#define MACB_HALT_TIMEOUT 1230 - +#define MACB_HALT_TIMEOUT 14000 #define MACB_PM_TIMEOUT 100 /* ms */ #define MACB_MDIO_TIMEOUT 1000000 /* in usecs */ -- cgit v1.2.3 From 36bd28c1cb0dbf48645cfe43159907fb3253b33a Mon Sep 17 00:00:00 2001 From: haozhe chang Date: Thu, 16 Mar 2023 17:58:20 +0800 Subject: wwan: core: Support slicing in port TX flow of WWAN subsystem wwan_port_fops_write inputs the SKB parameter to the TX callback of the WWAN device driver. However, the WWAN device (e.g., t7xx) may have an MTU less than the size of SKB, causing the TX buffer to be sliced and copied once more in the WWAN device driver. This patch implements the slicing in the WWAN subsystem and gives the WWAN devices driver the option to slice(by frag_len) or not. By doing so, the additional memory copy is reduced. Meanwhile, this patch gives WWAN devices driver the option to reserve headroom in fragments for the device-specific metadata. Signed-off-by: haozhe chang Reviewed-by: Loic Poulain Link: https://lore.kernel.org/r/20230316095826.181904-1-haozhe.chang@mediatek.com Signed-off-by: Jakub Kicinski --- drivers/net/wwan/iosm/iosm_ipc_port.c | 3 +- drivers/net/wwan/mhi_wwan_ctrl.c | 2 +- drivers/net/wwan/rpmsg_wwan_ctrl.c | 2 +- drivers/net/wwan/t7xx/t7xx_port_wwan.c | 36 +++++++++++---------- drivers/net/wwan/wwan_core.c | 58 ++++++++++++++++++++++++++-------- drivers/net/wwan/wwan_hwsim.c | 2 +- drivers/usb/class/cdc-wdm.c | 3 +- include/linux/wwan.h | 11 +++++++ 8 files changed, 81 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wwan/iosm/iosm_ipc_port.c b/drivers/net/wwan/iosm/iosm_ipc_port.c index b6d81c627277..5d5b4183e14a 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_port.c +++ b/drivers/net/wwan/iosm/iosm_ipc_port.c @@ -63,7 +63,8 @@ struct iosm_cdev *ipc_port_init(struct iosm_imem *ipc_imem, ipc_port->ipc_imem = ipc_imem; ipc_port->iosm_port = wwan_create_port(ipc_port->dev, port_type, - &ipc_wwan_ctrl_ops, ipc_port); + &ipc_wwan_ctrl_ops, NULL, + ipc_port); return ipc_port; } diff --git a/drivers/net/wwan/mhi_wwan_ctrl.c b/drivers/net/wwan/mhi_wwan_ctrl.c index f7ca52353f40..e9f979d2d851 100644 --- a/drivers/net/wwan/mhi_wwan_ctrl.c +++ b/drivers/net/wwan/mhi_wwan_ctrl.c @@ -237,7 +237,7 @@ static int mhi_wwan_ctrl_probe(struct mhi_device *mhi_dev, /* Register as a wwan port, id->driver_data contains wwan port type */ port = wwan_create_port(&cntrl->mhi_dev->dev, id->driver_data, - &wwan_pops, mhiwwan); + &wwan_pops, NULL, mhiwwan); if (IS_ERR(port)) { kfree(mhiwwan); return PTR_ERR(port); diff --git a/drivers/net/wwan/rpmsg_wwan_ctrl.c b/drivers/net/wwan/rpmsg_wwan_ctrl.c index 31c24420ab2e..06f4b02f1552 100644 --- a/drivers/net/wwan/rpmsg_wwan_ctrl.c +++ b/drivers/net/wwan/rpmsg_wwan_ctrl.c @@ -129,7 +129,7 @@ static int rpmsg_wwan_ctrl_probe(struct rpmsg_device *rpdev) /* Register as a wwan port, id.driver_data contains wwan port type */ port = wwan_create_port(parent, rpdev->id.driver_data, - &rpmsg_wwan_pops, rpwwan); + &rpmsg_wwan_pops, NULL, rpwwan); if (IS_ERR(port)) return PTR_ERR(port); diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c index 24bd21942403..17389c8f6600 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c @@ -54,13 +54,13 @@ static void t7xx_port_ctrl_stop(struct wwan_port *port) static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) { struct t7xx_port *port_private = wwan_port_get_drvdata(port); - size_t len, offset, chunk_len = 0, txq_mtu = CLDMA_MTU; const struct t7xx_port_conf *port_conf; + struct sk_buff *cur = skb, *cloned; struct t7xx_fsm_ctl *ctl; enum md_state md_state; + int cnt = 0, ret; - len = skb->len; - if (!len || !port_private->chan_enable) + if (!port_private->chan_enable) return -EINVAL; port_conf = port_private->port_conf; @@ -72,23 +72,21 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb) return -ENODEV; } - for (offset = 0; offset < len; offset += chunk_len) { - struct sk_buff *skb_ccci; - int ret; - - chunk_len = min(len - offset, txq_mtu - sizeof(struct ccci_header)); - skb_ccci = t7xx_port_alloc_skb(chunk_len); - if (!skb_ccci) - return -ENOMEM; - - skb_put_data(skb_ccci, skb->data + offset, chunk_len); - ret = t7xx_port_send_skb(port_private, skb_ccci, 0, 0); + while (cur) { + cloned = skb_clone(cur, GFP_KERNEL); + cloned->len = skb_headlen(cur); + ret = t7xx_port_send_skb(port_private, cloned, 0, 0); if (ret) { - dev_kfree_skb_any(skb_ccci); + dev_kfree_skb(cloned); dev_err(port_private->dev, "Write error on %s port, %d\n", port_conf->name, ret); - return ret; + return cnt ? cnt + ret : ret; } + cnt += cur->len; + if (cur == skb) + cur = skb_shinfo(skb)->frag_list; + else + cur = cur->next; } dev_kfree_skb(skb); @@ -154,13 +152,17 @@ static int t7xx_port_wwan_disable_chl(struct t7xx_port *port) static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int state) { const struct t7xx_port_conf *port_conf = port->port_conf; + unsigned int header_len = sizeof(struct ccci_header); + struct wwan_port_caps caps; if (state != MD_STATE_READY) return; if (!port->wwan.wwan_port) { + caps.frag_len = CLDMA_MTU - header_len; + caps.headroom_len = header_len; port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type, - &wwan_ops, port); + &wwan_ops, &caps, port); if (IS_ERR(port->wwan.wwan_port)) dev_err(port->dev, "Unable to create WWWAN port %s", port_conf->name); } diff --git a/drivers/net/wwan/wwan_core.c b/drivers/net/wwan/wwan_core.c index 966d0ccd2276..2e1c01cf00a9 100644 --- a/drivers/net/wwan/wwan_core.c +++ b/drivers/net/wwan/wwan_core.c @@ -67,6 +67,8 @@ struct wwan_device { * @rxq: Buffer inbound queue * @waitqueue: The waitqueue for port fops (read/write/poll) * @data_lock: Port specific data access serialization + * @headroom_len: SKB reserved headroom size + * @frag_len: Length to fragment packet * @at_data: AT port specific data */ struct wwan_port { @@ -79,6 +81,8 @@ struct wwan_port { struct sk_buff_head rxq; wait_queue_head_t waitqueue; struct mutex data_lock; /* Port specific data access serialization */ + size_t headroom_len; + size_t frag_len; union { struct { struct ktermios termios; @@ -426,6 +430,7 @@ static int __wwan_port_dev_assign_name(struct wwan_port *port, const char *fmt) struct wwan_port *wwan_create_port(struct device *parent, enum wwan_port_type type, const struct wwan_port_ops *ops, + struct wwan_port_caps *caps, void *drvdata) { struct wwan_device *wwandev; @@ -459,6 +464,8 @@ struct wwan_port *wwan_create_port(struct device *parent, port->type = type; port->ops = ops; + port->frag_len = caps ? caps->frag_len : SIZE_MAX; + port->headroom_len = caps ? caps->headroom_len : 0; mutex_init(&port->ops_lock); skb_queue_head_init(&port->rxq); init_waitqueue_head(&port->waitqueue); @@ -702,30 +709,53 @@ static ssize_t wwan_port_fops_read(struct file *filp, char __user *buf, static ssize_t wwan_port_fops_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { + struct sk_buff *skb, *head = NULL, *tail = NULL; struct wwan_port *port = filp->private_data; - struct sk_buff *skb; + size_t frag_len, remain = count; int ret; ret = wwan_wait_tx(port, !!(filp->f_flags & O_NONBLOCK)); if (ret) return ret; - skb = alloc_skb(count, GFP_KERNEL); - if (!skb) - return -ENOMEM; + do { + frag_len = min(remain, port->frag_len); + skb = alloc_skb(frag_len + port->headroom_len, GFP_KERNEL); + if (!skb) { + ret = -ENOMEM; + goto freeskb; + } + skb_reserve(skb, port->headroom_len); + + if (!head) { + head = skb; + } else if (!tail) { + skb_shinfo(head)->frag_list = skb; + tail = skb; + } else { + tail->next = skb; + tail = skb; + } - if (copy_from_user(skb_put(skb, count), buf, count)) { - kfree_skb(skb); - return -EFAULT; - } + if (copy_from_user(skb_put(skb, frag_len), buf + count - remain, frag_len)) { + ret = -EFAULT; + goto freeskb; + } - ret = wwan_port_op_tx(port, skb, !!(filp->f_flags & O_NONBLOCK)); - if (ret) { - kfree_skb(skb); - return ret; - } + if (skb != head) { + head->data_len += skb->len; + head->len += skb->len; + head->truesize += skb->truesize; + } + } while (remain -= frag_len); + + ret = wwan_port_op_tx(port, head, !!(filp->f_flags & O_NONBLOCK)); + if (!ret) + return count; - return count; +freeskb: + kfree_skb(head); + return ret; } static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait) diff --git a/drivers/net/wwan/wwan_hwsim.c b/drivers/net/wwan/wwan_hwsim.c index 2397a903d8f5..dfbdaa259a3f 100644 --- a/drivers/net/wwan/wwan_hwsim.c +++ b/drivers/net/wwan/wwan_hwsim.c @@ -205,7 +205,7 @@ static struct wwan_hwsim_port *wwan_hwsim_port_new(struct wwan_hwsim_dev *dev) port->wwan = wwan_create_port(&dev->dev, WWAN_PORT_AT, &wwan_hwsim_port_ops, - port); + NULL, port); if (IS_ERR(port->wwan)) { err = PTR_ERR(port->wwan); goto err_free_port; diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 1f0951be15ab..c553decb5461 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -929,7 +929,8 @@ static void wdm_wwan_init(struct wdm_device *desc) return; } - port = wwan_create_port(&intf->dev, desc->wwanp_type, &wdm_wwan_port_ops, desc); + port = wwan_create_port(&intf->dev, desc->wwanp_type, &wdm_wwan_port_ops, + NULL, desc); if (IS_ERR(port)) { dev_err(&intf->dev, "%s: Unable to create WWAN port\n", dev_name(intf->usb_dev)); diff --git a/include/linux/wwan.h b/include/linux/wwan.h index 24d76500b1cc..01fa15506286 100644 --- a/include/linux/wwan.h +++ b/include/linux/wwan.h @@ -64,11 +64,21 @@ struct wwan_port_ops { poll_table *wait); }; +/** struct wwan_port_caps - The WWAN port capbilities + * @frag_len: WWAN port TX fragments length + * @headroom_len: WWAN port TX fragments reserved headroom length + */ +struct wwan_port_caps { + size_t frag_len; + unsigned int headroom_len; +}; + /** * wwan_create_port - Add a new WWAN port * @parent: Device to use as parent and shared by all WWAN ports * @type: WWAN port type * @ops: WWAN port operations + * @caps: WWAN port capabilities * @drvdata: Pointer to caller driver data * * Allocate and register a new WWAN port. The port will be automatically exposed @@ -86,6 +96,7 @@ struct wwan_port_ops { struct wwan_port *wwan_create_port(struct device *parent, enum wwan_port_type type, const struct wwan_port_ops *ops, + struct wwan_port_caps *caps, void *drvdata); /** -- cgit v1.2.3 From df5e87f16c338aa4f62ed3353e73e39e68af965e Mon Sep 17 00:00:00 2001 From: Gavin Li Date: Thu, 16 Mar 2023 09:07:54 +0200 Subject: vxlan: Remove unused argument from vxlan_build_gbp_hdr( ) and vxlan_build_gpe_hdr( ) Remove unused argument (i.e. u32 vxflags) in vxlan_build_gbp_hdr( ) and vxlan_build_gpe_hdr( ) function arguments. Signed-off-by: Gavin Li Reviewed-by: Simon Horman Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index e2e5f5dac7e6..8cc147c0d069 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2093,8 +2093,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } -static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags, - struct vxlan_metadata *md) +static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, struct vxlan_metadata *md) { struct vxlanhdr_gbp *gbp; @@ -2113,8 +2112,7 @@ static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags, gbp->policy_id = htons(md->gbp & VXLAN_GBP_ID_MASK); } -static int vxlan_build_gpe_hdr(struct vxlanhdr *vxh, u32 vxflags, - __be16 protocol) +static int vxlan_build_gpe_hdr(struct vxlanhdr *vxh, __be16 protocol) { struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)vxh; @@ -2177,9 +2175,9 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst, } if (vxflags & VXLAN_F_GBP) - vxlan_build_gbp_hdr(vxh, vxflags, md); + vxlan_build_gbp_hdr(vxh, md); if (vxflags & VXLAN_F_GPE) { - err = vxlan_build_gpe_hdr(vxh, vxflags, skb->protocol); + err = vxlan_build_gpe_hdr(vxh, skb->protocol); if (err < 0) return err; inner_protocol = skb->protocol; -- cgit v1.2.3 From c641e9279f3530aa2fe4bcb250477b555b75104a Mon Sep 17 00:00:00 2001 From: Gavin Li Date: Thu, 16 Mar 2023 09:07:55 +0200 Subject: vxlan: Expose helper vxlan_build_gbp_hdr The function vxlan_build_gbp_hdr will be used by other modules to build gbp option in vxlan header according to gbp flags. Signed-off-by: Gavin Li Reviewed-by: Gavi Teitz Reviewed-by: Roi Dayan Reviewed-by: Maor Dickman Acked-by: Saeed Mahameed Reviewed-by: Simon Horman Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 19 ------------------- include/net/vxlan.h | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 8cc147c0d069..561fe1b314f5 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2093,25 +2093,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } -static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, struct vxlan_metadata *md) -{ - struct vxlanhdr_gbp *gbp; - - if (!md->gbp) - return; - - gbp = (struct vxlanhdr_gbp *)vxh; - vxh->vx_flags |= VXLAN_HF_GBP; - - if (md->gbp & VXLAN_GBP_DONT_LEARN) - gbp->dont_learn = 1; - - if (md->gbp & VXLAN_GBP_POLICY_APPLIED) - gbp->policy_applied = 1; - - gbp->policy_id = htons(md->gbp & VXLAN_GBP_ID_MASK); -} - static int vxlan_build_gpe_hdr(struct vxlanhdr *vxh, __be16 protocol) { struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)vxh; diff --git a/include/net/vxlan.h b/include/net/vxlan.h index b7b2e9abfb37..20bd7d893e10 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -572,4 +572,23 @@ static inline bool vxlan_fdb_nh_path_select(struct nexthop *nh, return true; } +static inline void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, const struct vxlan_metadata *md) +{ + struct vxlanhdr_gbp *gbp; + + if (!md->gbp) + return; + + gbp = (struct vxlanhdr_gbp *)vxh; + vxh->vx_flags |= VXLAN_HF_GBP; + + if (md->gbp & VXLAN_GBP_DONT_LEARN) + gbp->dont_learn = 1; + + if (md->gbp & VXLAN_GBP_POLICY_APPLIED) + gbp->policy_applied = 1; + + gbp->policy_id = htons(md->gbp & VXLAN_GBP_ID_MASK); +} + #endif -- cgit v1.2.3 From 58de53c1025836529af9e22e96375d0874e2baca Mon Sep 17 00:00:00 2001 From: Gavin Li Date: Thu, 16 Mar 2023 09:07:56 +0200 Subject: net/mlx5e: Add helper for encap_info_equal for tunnels with options For tunnels with options, eg, geneve and vxlan with gbp, they share the same way to compare the headers and options. Extract the code as a common function for them. Signed-off-by: Gavin Li Reviewed-by: Gavi Teitz Reviewed-by: Roi Dayan Reviewed-by: Maor Dickman Acked-by: Saeed Mahameed Signed-off-by: Jakub Kicinski --- .../net/ethernet/mellanox/mlx5/core/en/tc_tun.h | 3 ++ .../ethernet/mellanox/mlx5/core/en/tc_tun_encap.c | 32 ++++++++++++++++++++++ .../ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c | 24 +--------------- 3 files changed, 36 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h index b38f693bbb52..92065568bb19 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h @@ -115,6 +115,9 @@ int mlx5e_tc_tun_parse_udp_ports(struct mlx5e_priv *priv, bool mlx5e_tc_tun_encap_info_equal_generic(struct mlx5e_encap_key *a, struct mlx5e_encap_key *b); +bool mlx5e_tc_tun_encap_info_equal_options(struct mlx5e_encap_key *a, + struct mlx5e_encap_key *b, + __be16 tun_flags); #endif /* CONFIG_MLX5_ESWITCH */ #endif //__MLX5_EN_TC_TUNNEL_H__ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c index 780224fd67a1..a108e73c9f66 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c @@ -3,6 +3,7 @@ #include #include +#include #include "tc_tun_encap.h" #include "en_tc.h" #include "tc_tun.h" @@ -571,6 +572,37 @@ bool mlx5e_tc_tun_encap_info_equal_generic(struct mlx5e_encap_key *a, a->tc_tunnel->tunnel_type == b->tc_tunnel->tunnel_type; } +bool mlx5e_tc_tun_encap_info_equal_options(struct mlx5e_encap_key *a, + struct mlx5e_encap_key *b, + __be16 tun_flags) +{ + struct ip_tunnel_info *a_info; + struct ip_tunnel_info *b_info; + bool a_has_opts, b_has_opts; + + if (!mlx5e_tc_tun_encap_info_equal_generic(a, b)) + return false; + + a_has_opts = !!(a->ip_tun_key->tun_flags & tun_flags); + b_has_opts = !!(b->ip_tun_key->tun_flags & tun_flags); + + /* keys are equal when both don't have any options attached */ + if (!a_has_opts && !b_has_opts) + return true; + + if (a_has_opts != b_has_opts) + return false; + + /* options stored in memory next to ip_tunnel_info struct */ + a_info = container_of(a->ip_tun_key, struct ip_tunnel_info, key); + b_info = container_of(b->ip_tun_key, struct ip_tunnel_info, key); + + return a_info->options_len == b_info->options_len && + !memcmp(ip_tunnel_info_opts(a_info), + ip_tunnel_info_opts(b_info), + a_info->options_len); +} + static int cmp_decap_info(struct mlx5e_decap_key *a, struct mlx5e_decap_key *b) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c index 054d80c4e65c..2bcd10b6d653 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c @@ -337,29 +337,7 @@ static int mlx5e_tc_tun_parse_geneve(struct mlx5e_priv *priv, static bool mlx5e_tc_tun_encap_info_equal_geneve(struct mlx5e_encap_key *a, struct mlx5e_encap_key *b) { - struct ip_tunnel_info *a_info; - struct ip_tunnel_info *b_info; - bool a_has_opts, b_has_opts; - - if (!mlx5e_tc_tun_encap_info_equal_generic(a, b)) - return false; - - a_has_opts = !!(a->ip_tun_key->tun_flags & TUNNEL_GENEVE_OPT); - b_has_opts = !!(b->ip_tun_key->tun_flags & TUNNEL_GENEVE_OPT); - - /* keys are equal when both don't have any options attached */ - if (!a_has_opts && !b_has_opts) - return true; - - if (a_has_opts != b_has_opts) - return false; - - /* geneve options stored in memory next to ip_tunnel_info struct */ - a_info = container_of(a->ip_tun_key, struct ip_tunnel_info, key); - b_info = container_of(b->ip_tun_key, struct ip_tunnel_info, key); - - return a_info->options_len == b_info->options_len && - memcmp(a_info + 1, b_info + 1, a_info->options_len) == 0; + return mlx5e_tc_tun_encap_info_equal_options(a, b, TUNNEL_GENEVE_OPT); } struct mlx5e_tc_tunnel geneve_tunnel = { -- cgit v1.2.3 From 6ee44c518159c364e5d30ed85d357fe1d8e2c141 Mon Sep 17 00:00:00 2001 From: Gavin Li Date: Thu, 16 Mar 2023 09:07:58 +0200 Subject: net/mlx5e: TC, Add support for VxLAN GBP encap/decap flows offload Add HW offloading support for TC flows with VxLAN GBP encap/decap. Example of encap rule: tc filter add dev eth0 protocol ip ingress flower \ action tunnel_key set id 42 vxlan_opts 512 \ action mirred egress redirect dev vxlan1 Example of decap rule: tc filter add dev vxlan1 protocol ip ingress flower \ enc_key_id 42 enc_dst_port 4789 vxlan_opts 1024 \ action tunnel_key unset action mirred egress redirect dev eth0 Signed-off-by: Gavin Li Reviewed-by: Gavi Teitz Reviewed-by: Roi Dayan Reviewed-by: Maor Dickman Acked-by: Saeed Mahameed Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c | 72 +++++++++++++++++++++- include/linux/mlx5/device.h | 6 ++ include/linux/mlx5/mlx5_ifc.h | 13 +++- 3 files changed, 87 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c index 1f62c702b625..a184d739d5f8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2018 Mellanox Technologies. */ +#include #include #include "lib/vxlan.h" #include "en/tc_tun.h" @@ -86,9 +87,11 @@ static int mlx5e_gen_ip_tunnel_header_vxlan(char buf[], const struct ip_tunnel_key *tun_key = &e->tun_info->key; __be32 tun_id = tunnel_id_to_key32(tun_key->tun_id); struct udphdr *udp = (struct udphdr *)(buf); + const struct vxlan_metadata *md; struct vxlanhdr *vxh; - if (tun_key->tun_flags & TUNNEL_VXLAN_OPT) + if ((tun_key->tun_flags & TUNNEL_VXLAN_OPT) && + e->tun_info->options_len != sizeof(*md)) return -EOPNOTSUPP; vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr)); *ip_proto = IPPROTO_UDP; @@ -96,6 +99,57 @@ static int mlx5e_gen_ip_tunnel_header_vxlan(char buf[], udp->dest = tun_key->tp_dst; vxh->vx_flags = VXLAN_HF_VNI; vxh->vx_vni = vxlan_vni_field(tun_id); + if (tun_key->tun_flags & TUNNEL_VXLAN_OPT) { + md = ip_tunnel_info_opts(e->tun_info); + vxlan_build_gbp_hdr(vxh, md); + } + + return 0; +} + +static int mlx5e_tc_tun_parse_vxlan_gbp_option(struct mlx5e_priv *priv, + struct mlx5_flow_spec *spec, + struct flow_cls_offload *f) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(f); + struct netlink_ext_ack *extack = f->common.extack; + struct flow_match_enc_opts enc_opts; + void *misc5_c, *misc5_v; + u32 *gbp, *gbp_mask; + + flow_rule_match_enc_opts(rule, &enc_opts); + + if (memchr_inv(&enc_opts.mask->data, 0, sizeof(enc_opts.mask->data)) && + !MLX5_CAP_ESW_FT_FIELD_SUPPORT_2(priv->mdev, tunnel_header_0_1)) { + NL_SET_ERR_MSG_MOD(extack, "Matching on VxLAN GBP is not supported"); + return -EOPNOTSUPP; + } + + if (enc_opts.key->dst_opt_type != TUNNEL_VXLAN_OPT) { + NL_SET_ERR_MSG_MOD(extack, "Wrong VxLAN option type: not GBP"); + return -EOPNOTSUPP; + } + + if (enc_opts.key->len != sizeof(*gbp) || + enc_opts.mask->len != sizeof(*gbp_mask)) { + NL_SET_ERR_MSG_MOD(extack, "VxLAN GBP option/mask len is not 32 bits"); + return -EINVAL; + } + + gbp = (u32 *)&enc_opts.key->data[0]; + gbp_mask = (u32 *)&enc_opts.mask->data[0]; + + if (*gbp_mask & ~VXLAN_GBP_MASK) { + NL_SET_ERR_MSG_FMT_MOD(extack, "Wrong VxLAN GBP mask(0x%08X)\n", *gbp_mask); + return -EINVAL; + } + + misc5_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_5); + misc5_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters_5); + MLX5_SET(fte_match_set_misc5, misc5_c, tunnel_header_0, *gbp_mask); + MLX5_SET(fte_match_set_misc5, misc5_v, tunnel_header_0, *gbp); + + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_5; return 0; } @@ -122,6 +176,14 @@ static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv, if (!enc_keyid.mask->keyid) return 0; + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_OPTS)) { + int err; + + err = mlx5e_tc_tun_parse_vxlan_gbp_option(priv, spec, f); + if (err) + return err; + } + /* match on VNI is required */ if (!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, @@ -143,6 +205,12 @@ static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv, return 0; } +static bool mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key *a, + struct mlx5e_encap_key *b) +{ + return mlx5e_tc_tun_encap_info_equal_options(a, b, TUNNEL_VXLAN_OPT); +} + static int mlx5e_tc_tun_get_remote_ifindex(struct net_device *mirred_dev) { const struct vxlan_dev *vxlan = netdev_priv(mirred_dev); @@ -160,6 +228,6 @@ struct mlx5e_tc_tunnel vxlan_tunnel = { .generate_ip_tun_hdr = mlx5e_gen_ip_tunnel_header_vxlan, .parse_udp_ports = mlx5e_tc_tun_parse_udp_ports_vxlan, .parse_tunnel = mlx5e_tc_tun_parse_vxlan, - .encap_info_equal = mlx5e_tc_tun_encap_info_equal_generic, + .encap_info_equal = mlx5e_tc_tun_encap_info_equal_vxlan, .get_remote_ifindex = mlx5e_tc_tun_get_remote_ifindex, }; diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 71b06ebad402..af4dd536a52c 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1357,6 +1357,12 @@ enum mlx5_qcam_feature_groups { #define MLX5_CAP_ESW_INGRESS_ACL_MAX(mdev, cap) \ MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, flow_table_properties_esw_acl_ingress.cap) +#define MLX5_CAP_ESW_FT_FIELD_SUPPORT_2(mdev, cap) \ + MLX5_CAP_ESW_FLOWTABLE(mdev, ft_field_support_2_esw_fdb.cap) + +#define MLX5_CAP_ESW_FT_FIELD_SUPPORT_2_MAX(mdev, cap) \ + MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, ft_field_support_2_esw_fdb.cap) + #define MLX5_CAP_ESW(mdev, cap) \ MLX5_GET(e_switch_cap, \ mdev->caps.hca[MLX5_CAP_ESWITCH]->cur, cap) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index d2c164f0778c..e47d6c58da35 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -404,10 +404,13 @@ struct mlx5_ifc_flow_table_fields_supported_bits { u8 metadata_reg_c_0[0x1]; }; +/* Table 2170 - Flow Table Fields Supported 2 Format */ struct mlx5_ifc_flow_table_fields_supported_2_bits { u8 reserved_at_0[0xe]; u8 bth_opcode[0x1]; - u8 reserved_at_f[0x11]; + u8 reserved_at_f[0x1]; + u8 tunnel_header_0_1[0x1]; + u8 reserved_at_11[0xf]; u8 reserved_at_20[0x60]; }; @@ -895,7 +898,13 @@ struct mlx5_ifc_flow_table_eswitch_cap_bits { struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_esw_acl_egress; - u8 reserved_at_800[0x1000]; + u8 reserved_at_800[0xC00]; + + struct mlx5_ifc_flow_table_fields_supported_2_bits ft_field_support_2_esw_fdb; + + struct mlx5_ifc_flow_table_fields_supported_2_bits ft_field_bitmask_support_2_esw_fdb; + + u8 reserved_at_1500[0x300]; u8 sw_steering_fdb_action_drop_icm_address_rx[0x40]; -- cgit v1.2.3 From b31587feaa0133b19a1900a26bce07a3c6d21af6 Mon Sep 17 00:00:00 2001 From: Bartosz Wawrzyniak Date: Thu, 16 Mar 2023 10:03:39 +0000 Subject: net: macb: Set MDIO clock divisor for pclk higher than 160MHz Currently macb sets clock divisor for pclk up to 160 MHz. Function gem_mdc_clk_div was updated to enable divisor for higher values of pclk. Signed-off-by: Bartosz Wawrzyniak Reviewed-by: Michal Kubiak Acked-by: Nicolas Ferre Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.h | 2 ++ drivers/net/ethernet/cadence/macb_main.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 14dfec4db8f9..c1fc91c97cee 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -692,6 +692,8 @@ #define GEM_CLK_DIV48 3 #define GEM_CLK_DIV64 4 #define GEM_CLK_DIV96 5 +#define GEM_CLK_DIV128 6 +#define GEM_CLK_DIV224 7 /* Constants for MAN register */ #define MACB_MAN_C22_SOF 1 diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index c4edd20c1c66..7698719b1909 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2640,8 +2640,12 @@ static u32 gem_mdc_clk_div(struct macb *bp) config = GEM_BF(CLK, GEM_CLK_DIV48); else if (pclk_hz <= 160000000) config = GEM_BF(CLK, GEM_CLK_DIV64); - else + else if (pclk_hz <= 240000000) config = GEM_BF(CLK, GEM_CLK_DIV96); + else if (pclk_hz <= 320000000) + config = GEM_BF(CLK, GEM_CLK_DIV128); + else + config = GEM_BF(CLK, GEM_CLK_DIV224); return config; } -- cgit v1.2.3 From c0e906a953f03c56d8029b0207195ee202fd646d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 16 Mar 2023 14:04:19 +0200 Subject: net: smc91x: Replace of_gpio.h with what indeed is used of_gpio.h in this driver is solely used as a proxy to other headers. This is incorrect usage of the of_gpio.h. Replace it .h with what indeed is used in the code. Signed-off-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smc91x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 35e99bf0c401..032eccf8eb42 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -57,6 +57,7 @@ static const char version[] = #include #include #include +#include #include #include #include @@ -69,7 +70,6 @@ static const char version[] = #include #include #include -#include #include #include -- cgit v1.2.3 From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 16 Mar 2023 14:08:26 +0200 Subject: net: phy: at803x: Replace of_gpio.h with what indeed is used of_gpio.h in this driver is solely used as a proxy to other headers. This is incorrect usage of the of_gpio.h. Replace it .h with what indeed is used in the code. Signed-off-by: Andy Shevchenko Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/at803x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 22f4458274aa..656136628ffd 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -13,12 +13,11 @@ #include #include #include -#include #include -#include #include #include #include +#include #include #include #include -- cgit v1.2.3 From e485f3a6eae0849f83b94936778a2325f72a0c89 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Fri, 17 Mar 2023 13:09:03 -0700 Subject: ixgb: Remove ixgb driver There are likely no users of this driver as the hardware has been discontinued since 2010. Remove the driver and all references to it in documentation. Suggested-by: Jakub Kicinski Signed-off-by: Tony Nguyen Acked-by: Jesse Brandeburg Signed-off-by: David S. Miller --- Documentation/PCI/pci-error-recovery.rst | 1 - .../networking/device_drivers/ethernet/index.rst | 1 - .../device_drivers/ethernet/intel/ixgb.rst | 468 ---- arch/loongarch/configs/loongson3_defconfig | 1 - arch/mips/configs/loongson2k_defconfig | 1 - arch/mips/configs/loongson3_defconfig | 1 - arch/mips/configs/mtx1_defconfig | 1 - arch/powerpc/configs/powernv_defconfig | 1 - arch/powerpc/configs/ppc64_defconfig | 1 - arch/powerpc/configs/ppc64e_defconfig | 1 - arch/powerpc/configs/ppc6xx_defconfig | 1 - arch/powerpc/configs/pseries_defconfig | 1 - arch/powerpc/configs/skiroot_defconfig | 1 - drivers/net/ethernet/intel/Kconfig | 17 - drivers/net/ethernet/intel/Makefile | 1 - drivers/net/ethernet/intel/ixgb/Makefile | 9 - drivers/net/ethernet/intel/ixgb/ixgb.h | 179 -- drivers/net/ethernet/intel/ixgb/ixgb_ee.c | 580 ----- drivers/net/ethernet/intel/ixgb/ixgb_ee.h | 79 - drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c | 642 ------ drivers/net/ethernet/intel/ixgb/ixgb_hw.c | 1229 ----------- drivers/net/ethernet/intel/ixgb/ixgb_hw.h | 767 ------- drivers/net/ethernet/intel/ixgb/ixgb_ids.h | 23 - drivers/net/ethernet/intel/ixgb/ixgb_main.c | 2285 -------------------- drivers/net/ethernet/intel/ixgb/ixgb_osdep.h | 39 - drivers/net/ethernet/intel/ixgb/ixgb_param.c | 442 ---- 26 files changed, 6772 deletions(-) delete mode 100644 Documentation/networking/device_drivers/ethernet/intel/ixgb.rst delete mode 100644 drivers/net/ethernet/intel/ixgb/Makefile delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb.h delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_ee.c delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_ee.h delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_hw.c delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_hw.h delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_ids.h delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_main.c delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_osdep.h delete mode 100644 drivers/net/ethernet/intel/ixgb/ixgb_param.c (limited to 'drivers') diff --git a/Documentation/PCI/pci-error-recovery.rst b/Documentation/PCI/pci-error-recovery.rst index bdafeb4b66dc..9981d330da8f 100644 --- a/Documentation/PCI/pci-error-recovery.rst +++ b/Documentation/PCI/pci-error-recovery.rst @@ -418,7 +418,6 @@ That is, the recovery API only requires that: - drivers/next/e100.c - drivers/net/e1000 - drivers/net/e1000e - - drivers/net/ixgb - drivers/net/ixgbe - drivers/net/cxgb3 - drivers/net/s2io.c diff --git a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst index 392969ac88ad..6e9e7012d000 100644 --- a/Documentation/networking/device_drivers/ethernet/index.rst +++ b/Documentation/networking/device_drivers/ethernet/index.rst @@ -31,7 +31,6 @@ Contents: intel/fm10k intel/igb intel/igbvf - intel/ixgb intel/ixgbe intel/ixgbevf intel/i40e diff --git a/Documentation/networking/device_drivers/ethernet/intel/ixgb.rst b/Documentation/networking/device_drivers/ethernet/intel/ixgb.rst deleted file mode 100644 index c6a233e68ad6..000000000000 --- a/Documentation/networking/device_drivers/ethernet/intel/ixgb.rst +++ /dev/null @@ -1,468 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -===================================================================== -Linux Base Driver for 10 Gigabit Intel(R) Ethernet Network Connection -===================================================================== - -October 1, 2018 - - -Contents -======== - -- In This Release -- Identifying Your Adapter -- Command Line Parameters -- Improving Performance -- Additional Configurations -- Known Issues/Troubleshooting -- Support - - - -In This Release -=============== - -This file describes the ixgb Linux Base Driver for the 10 Gigabit Intel(R) -Network Connection. This driver includes support for Itanium(R)2-based -systems. - -For questions related to hardware requirements, refer to the documentation -supplied with your 10 Gigabit adapter. All hardware requirements listed apply -to use with Linux. - -The following features are available in this kernel: - - Native VLANs - - Channel Bonding (teaming) - - SNMP - -Channel Bonding documentation can be found in the Linux kernel source: -/Documentation/networking/bonding.rst - -The driver information previously displayed in the /proc filesystem is not -supported in this release. Alternatively, you can use ethtool (version 1.6 -or later), lspci, and iproute2 to obtain the same information. - -Instructions on updating ethtool can be found in the section "Additional -Configurations" later in this document. - - -Identifying Your Adapter -======================== - -The following Intel network adapters are compatible with the drivers in this -release: - -+------------+------------------------------+----------------------------------+ -| Controller | Adapter Name | Physical Layer | -+============+==============================+==================================+ -| 82597EX | Intel(R) PRO/10GbE LR/SR/CX4 | - 10G Base-LR (fiber) | -| | Server Adapters | - 10G Base-SR (fiber) | -| | | - 10G Base-CX4 (copper) | -+------------+------------------------------+----------------------------------+ - -For more information on how to identify your adapter, go to the Adapter & -Driver ID Guide at: - - https://support.intel.com - - -Command Line Parameters -======================= - -If the driver is built as a module, the following optional parameters are -used by entering them on the command line with the modprobe command using -this syntax:: - - modprobe ixgb [