diff options
30 files changed, 678 insertions, 48 deletions
diff --git a/Documentation/devicetree/bindings/spi/spi_qsd.txt b/Documentation/devicetree/bindings/spi/spi_qsd.txt index 1edf0820398a..b3007eb9ceea 100644 --- a/Documentation/devicetree/bindings/spi/spi_qsd.txt +++ b/Documentation/devicetree/bindings/spi/spi_qsd.txt @@ -1,7 +1,8 @@ Qualcomm Serial Peripheral Interface (SPI) Required properties: -- compatible : Should be "qcom,spi-qup-v2". +- compatible : Should be "qcom,spi-qup-v2". Should "qcom,qup-v26" for + controllers that support spi slave mode. - reg : Offset and length of the register regions for the device - reg-names : Register region names referenced in reg above. Required register resource entries are: @@ -72,6 +73,7 @@ the following properties. clock phase (CPHA) mode - spi-cs-high : (optional) Empty property indicating device requires chip select active high +- qcom,slv-ctrl : Set this flag to configure QUP as SPI slave controller. Example: aliases { diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index 64c1e4550d14..9694490a39a2 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -2289,6 +2289,13 @@ qcom,reset-ep-after-lpm-resume; }; + usb_audio_qmi_dev { + compatible = "qcom,usb-audio-qmi-dev"; + iommus = <&lpass_q6_smmu 12>; + qcom,usb-audio-stream-id = <12>; + qcom,usb-audio-intr-num = <1>; + }; + qcom,lpass@9300000 { compatible = "qcom,pil-tz-generic"; reg = <0x9300000 0x00100>; diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi index 08aa412f1ff4..7815399f23b1 100644 --- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi @@ -24,11 +24,8 @@ }; psci { - compatible = "arm,psci"; + compatible = "arm,psci-1.0"; method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; }; soc: soc { }; diff --git a/arch/arm64/configs/msm-auto-gvm-perf_defconfig b/arch/arm64/configs/msm-auto-gvm-perf_defconfig index 1c4e19d9b859..70673d4959eb 100644 --- a/arch/arm64/configs/msm-auto-gvm-perf_defconfig +++ b/arch/arm64/configs/msm-auto-gvm-perf_defconfig @@ -44,6 +44,9 @@ CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm64/configs/msm-auto-gvm_defconfig b/arch/arm64/configs/msm-auto-gvm_defconfig index 05ade778b2ae..455c7581f51f 100644 --- a/arch/arm64/configs/msm-auto-gvm_defconfig +++ b/arch/arm64/configs/msm-auto-gvm_defconfig @@ -42,6 +42,10 @@ CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +CONFIG_PM_DEBUG=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 5df8e1234505..071c94457d34 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -209,13 +209,27 @@ static int ath3k_load_firmware(struct usb_device *udev, { u8 *send_buf; int err, pipe, len, size, sent = 0; - int count = firmware->size; + int count; BT_DBG("udev %p", udev); + if (!firmware || !firmware->data || firmware->size <= 0) { + err = -EINVAL; + BT_ERR("Not a valid FW file"); + return err; + } + + count = firmware->size; + + if (count < FW_HDR_SIZE) { + err = -EINVAL; + BT_ERR("ath3k loading invalid size of file"); + return err; + } + pipe = usb_sndctrlpipe(udev, 0); - send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); + send_buf = kzalloc(BULK_SIZE, GFP_KERNEL); if (!send_buf) { BT_ERR("Can't allocate memory chunk for firmware"); return -ENOMEM; diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 4a6208168850..0db4a00e7641 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -27,6 +27,9 @@ #define VERSION "0.1" +#define MAX_PATCH_FILE_SIZE (100*1024) +#define MAX_NVM_FILE_SIZE (10*1024) + static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version) { struct sk_buff *skb; @@ -285,27 +288,59 @@ static int rome_download_firmware(struct hci_dev *hdev, struct rome_config *config) { const struct firmware *fw; + u32 type_len, length; + struct tlv_type_hdr *tlv; int ret; - BT_INFO("%s: ROME Downloading %s", hdev->name, config->fwname); - + BT_INFO("%s: ROME Downloading file: %s", hdev->name, config->fwname); ret = request_firmware(&fw, config->fwname, &hdev->dev); - if (ret) { - BT_ERR("%s: Failed to request file: %s (%d)", hdev->name, - config->fwname, ret); + + if (ret || !fw || !fw->data || fw->size <= 0) { + BT_ERR("Failed to request file: err = (%d)", ret); + ret = ret ? ret : -EINVAL; return ret; } + if (config->type == TLV_TYPE_PATCH && + (fw->size > MAX_PATCH_FILE_SIZE)) { + ret = -EINVAL; + BT_ERR("TLV_PATCH dload: wrong patch file sizes"); + goto exit; + } else if (config->type == TLV_TYPE_NVM && + (fw->size > MAX_NVM_FILE_SIZE)) { + ret = -EINVAL; + BT_ERR("TLV_NVM dload: wrong NVM file sizes"); + goto exit; + } else { + ret = -EINVAL; + BT_ERR("TLV_NVM dload: wrong config type selected"); + goto exit; + } - rome_tlv_check_data(config, fw); + if (fw->size < sizeof(struct tlv_type_hdr)) { + ret = -EINVAL; + BT_ERR("Firware size smaller to fit minimum value"); + goto exit; + } + tlv = (struct tlv_type_hdr *)fw->data; + type_len = le32_to_cpu(tlv->type_len); + length = (type_len >> 8) & 0x00ffffff; + + if (fw->size - 4 != length) { + ret = -EINVAL; + BT_ERR("Requested size not matching size in header"); + goto exit; + } + + rome_tlv_check_data(config, fw); ret = rome_tlv_download_request(hdev, fw); + if (ret) { - BT_ERR("%s: Failed to download file: %s (%d)", hdev->name, - config->fwname, ret); + BT_ERR("Failed to download FW: error = (%d)", ret); } +exit: release_firmware(fw); - return ret; } @@ -316,8 +351,9 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) int err; cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD; - cmd[1] = 0x02; /* TAG ID */ - cmd[2] = sizeof(bdaddr_t); /* size */ + /* Set the TAG ID of 0x02 for NVM set and size of tag */ + cmd[1] = 0x02; + cmd[2] = sizeof(bdaddr_t); memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t)); skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 194a6583103e..6a969401e950 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -35,6 +35,7 @@ static struct v4l2_device *msm_v4l2_dev; static struct list_head ordered_sd_list; static struct mutex ordered_sd_mtx; +static struct mutex v4l2_event_mtx; static struct pm_qos_request msm_v4l2_pm_qos_request; @@ -852,13 +853,25 @@ static long msm_private_ioctl(struct file *file, void *fh, static int msm_unsubscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { - return v4l2_event_unsubscribe(fh, sub); + int rc; + + mutex_lock(&v4l2_event_mtx); + rc = v4l2_event_unsubscribe(fh, sub); + mutex_unlock(&v4l2_event_mtx); + + return rc; } static int msm_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { - return v4l2_event_subscribe(fh, sub, 5, NULL); + int rc; + + mutex_lock(&v4l2_event_mtx); + rc = v4l2_event_subscribe(fh, sub, 5, NULL); + mutex_unlock(&v4l2_event_mtx); + + return rc; } static const struct v4l2_ioctl_ops g_msm_ioctl_ops = { @@ -1376,6 +1389,7 @@ static int msm_probe(struct platform_device *pdev) spin_lock_init(&msm_eventq_lock); spin_lock_init(&msm_pid_lock); mutex_init(&ordered_sd_mtx); + mutex_init(&v4l2_event_mtx); INIT_LIST_HEAD(&ordered_sd_list); cam_debugfs_root = debugfs_create_dir(MSM_CAM_LOGSYNC_FILE_BASEDIR, diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index ffcf30756b9e..f194d434b97c 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -24,6 +24,7 @@ #include <linux/pci.h> #include <linux/uuid.h> #include <linux/time.h> +#include <linux/inetdevice.h> #include <soc/qcom/socinfo.h> #include "htt.h" @@ -430,6 +431,8 @@ struct ath10k_vif { struct work_struct ap_csa_work; struct delayed_work connection_loss_work; struct cfg80211_bitrate_mask bitrate_mask; + struct wmi_ns_arp_offload_req arp_offload; + struct wmi_ns_arp_offload_req ns_offload; }; struct ath10k_vif_iter { diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 1147dee4051e..9fcc2866d830 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1387,6 +1387,10 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, lockdep_assert_held(&ar->conf_mutex); + /* Clear arp and ns offload cache */ + memset(&arvif->arp_offload, 0, sizeof(arvif->arp_offload)); + memset(&arvif->ns_offload, 0, sizeof(arvif->ns_offload)); + reinit_completion(&ar->vdev_setup_done); reinit_completion(&ar->vdev_delete_done); diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 8bb01c1a35f7..dcb0da51530a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -160,6 +160,8 @@ struct wmi_ops { u32 num_ac); struct sk_buff *(*gen_sta_keepalive)(struct ath10k *ar, const struct wmi_sta_keepalive_arg *arg); + struct sk_buff *(*gen_set_arp_ns_offload)(struct ath10k *ar, + struct ath10k_vif *arvif); struct sk_buff *(*gen_wow_enable)(struct ath10k *ar); struct sk_buff *(*gen_wow_add_wakeup_event)(struct ath10k *ar, u32 vdev_id, enum wmi_wow_wakeup_event event, @@ -1187,6 +1189,23 @@ ath10k_wmi_sta_keepalive(struct ath10k *ar, } static inline int +ath10k_wmi_set_arp_ns_offload(struct ath10k *ar, struct ath10k_vif *arvif) +{ + struct sk_buff *skb; + u32 cmd_id; + + if (!ar->wmi.ops->gen_set_arp_ns_offload) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_set_arp_ns_offload(ar, arvif); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->set_arp_ns_offload_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +static inline int ath10k_wmi_wow_enable(struct ath10k *ar) { struct sk_buff *skb; diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 95bd588e0d2c..b904a0d9d391 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3010,6 +3010,82 @@ ath10k_wmi_tlv_op_gen_tdls_peer_update(struct ath10k *ar, } static struct sk_buff * +ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar, + struct ath10k_vif *arvif) +{ + struct wmi_tlv_arp_ns_offload_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + int i; + struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload; + struct wmi_ns_offload *ns_tuple[WMI_MAX_NS_OFFLOADS]; + struct wmi_arp_offload *arp_tuple[WMI_MAX_ARP_OFFLOADS]; + + len = sizeof(*cmd) + sizeof(*tlv) + + sizeof(*tlv) + WMI_MAX_NS_OFFLOADS * + (sizeof(struct wmi_ns_offload) + sizeof(*tlv)) + + sizeof(*tlv) + WMI_MAX_ARP_OFFLOADS * + (sizeof(struct wmi_arp_offload) + sizeof(*tlv)); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SET_ARP_NS_OFFLOAD_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (struct wmi_tlv_arp_ns_offload_cmd *)tlv->value; + cmd->flags = __cpu_to_le32(0); + cmd->vdev_id = __cpu_to_le32(arvif->vdev_id); + + ptr += (sizeof(*tlv) + sizeof(*cmd)); + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = __cpu_to_le16(WMI_MAX_NS_OFFLOADS * + (sizeof(struct wmi_ns_offload) + sizeof(*tlv))); + ptr += sizeof(*tlv); + tlv = ptr; + + for (i = 0; i < WMI_MAX_NS_OFFLOADS; i++) { + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE); + tlv->len = __cpu_to_le16(sizeof(struct wmi_ns_offload)); + ns_tuple[i] = (struct wmi_ns_offload *)tlv->value; + ns_tuple[i]->flags |= __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE); + ptr += (sizeof(*tlv) + sizeof(struct wmi_ns_offload)); + tlv = ptr; + } + + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = __cpu_to_le16(WMI_MAX_ARP_OFFLOADS * + (sizeof(struct wmi_arp_offload) + sizeof(*tlv))); + ptr += sizeof(*tlv); + tlv = ptr; + + for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) { + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE); + tlv->len = __cpu_to_le16(sizeof(struct wmi_arp_offload)); + arp_tuple[i] = (struct wmi_arp_offload *)tlv->value; + if (arp->enable_offload && (i == 0)) { + arp_tuple[i]->flags |= + __cpu_to_le32(WMI_ARPOFF_FLAGS_VALID); + memcpy(&arp_tuple[i]->target_ipaddr, + &arp->params.ipv4_addr, 4); + } else { + arp_tuple[i]->flags |= + __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE); + } + ptr += (sizeof(*tlv) + sizeof(struct wmi_arp_offload)); + tlv = ptr; + } + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set arp ns offload\n"); + return skb; +} + +static struct sk_buff * ath10k_wmi_tlv_op_gen_wow_enable(struct ath10k *ar) { struct wmi_tlv_wow_enable_cmd *cmd; @@ -3695,6 +3771,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_p2p_go_bcn_ie = ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie, .gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd, .gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive, + .gen_set_arp_ns_offload = ath10k_wmi_tlv_op_gen_set_arp_ns_offload, .gen_wow_enable = ath10k_wmi_tlv_op_gen_wow_enable, .gen_wow_add_wakeup_event = ath10k_wmi_tlv_op_gen_wow_add_wakeup_event, .gen_wow_host_wakeup_ind = ath10k_wmi_tlv_gen_wow_host_wakeup_ind, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 7140e5e23307..1d4aed14fbb4 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -1557,6 +1557,12 @@ struct wmi_tlv_wow_enable_cmd { __le32 pause_iface_config; } __packed; +struct wmi_tlv_arp_ns_offload_cmd { + __le32 flags; + __le32 vdev_id; + __le32 num_ns_ext_tuples; +} __packed; + struct wmi_tlv_wow_host_wakeup_ind { __le32 reserved; } __packed; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index f59e5f86708b..9b17ef150303 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -20,6 +20,8 @@ #include <linux/types.h> #include <net/mac80211.h> +#include <linux/ipv6.h> +#include <linux/in.h> /* * This file specifies the WMI interface for the Unified Software @@ -2884,6 +2886,53 @@ struct wmi_start_scan_common { __le32 scan_ctrl_flags; } __packed; +/* ARP-NS offload data structure */ +#define WMI_NSOFF_MAX_TARGET_IPS 2 +#define WMI_MAX_NS_OFFLOADS 2 +#define WMI_MAX_ARP_OFFLOADS 2 +#define WMI_ARPOFF_FLAGS_VALID BIT(0) +#define WMI_IPV4_ARP_REPLY_OFFLOAD 0 +#define WMI_ARP_NS_OFFLOAD_DISABLE 0 +#define WMI_ARP_NS_OFFLOAD_ENABLE 1 + +struct wmi_ns_offload_info { + struct in6_addr src_addr; + struct in6_addr self_addr[TARGET_NUM_STATIONS]; + struct in6_addr target_addr[TARGET_NUM_STATIONS]; + struct wmi_mac_addr self_macaddr; + u8 src_ipv6_addr_valid; + struct in6_addr target_addr_valid; + struct in6_addr target_addr_ac_type; + u8 slot_idx; +} __packed; + +struct wmi_ns_arp_offload_req { + u8 offload_type; + u8 enable_offload; + __le32 num_ns_offload_count; + union { + struct in_addr ipv4_addr; + struct in6_addr ipv6_addr; + } params; + struct wmi_ns_offload_info offload_info; + struct wmi_mac_addr bssid; +} __packed; + +struct wmi_ns_offload { + __le32 flags; + struct in6_addr target_ipaddr[WMI_NSOFF_MAX_TARGET_IPS]; + struct in6_addr solicitation_ipaddr; + struct in6_addr remote_ipaddr; + struct wmi_mac_addr target_mac; +} __packed; + +struct wmi_arp_offload { + __le32 flags; + struct in_addr target_ipaddr; + struct in_addr remote_ipaddr; + struct wmi_mac_addr target_mac; +} __packed; + struct wmi_start_scan_tlvs { /* TLV parameters. These includes channel list, ssid list, bssid list, * extra ies. diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index de678fd3b801..1a43375c3264 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -224,6 +224,76 @@ static int ath10k_wow_wakeup(struct ath10k *ar) return 0; } +static int +ath10k_wow_fill_vdev_arp_offload_struct(struct ath10k_vif *arvif, + bool enable_offload) +{ + struct in_device *in_dev; + struct in_ifaddr *ifa; + bool offload_params_found = false; + struct wireless_dev *wdev = ieee80211_vif_to_wdev(arvif->vif); + struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload; + + if (!enable_offload) { + arp->offload_type = __cpu_to_le16(WMI_IPV4_ARP_REPLY_OFFLOAD); + arp->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_DISABLE); + return 0; + } + + if (!wdev) + return -ENODEV; + if (!wdev->netdev) + return -ENODEV; + in_dev = __in_dev_get_rtnl(wdev->netdev); + if (!in_dev) + return -ENODEV; + + arp->offload_type = __cpu_to_le16(WMI_IPV4_ARP_REPLY_OFFLOAD); + arp->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_ENABLE); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + if (!strcmp(ifa->ifa_label, wdev->netdev->name)) { + offload_params_found = true; + break; + } + } + + if (!offload_params_found) + return -ENODEV; + + memcpy(&arp->params.ipv4_addr, &ifa->ifa_local, 4); + return 0; +} + +static int ath10k_wow_enable_ns_arp_offload(struct ath10k *ar, bool offload) +{ + struct ath10k_vif *arvif; + int ret; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + if (!arvif->is_up) + continue; + + ret = ath10k_wow_fill_vdev_arp_offload_struct(arvif, offload); + if (ret) { + ath10k_err(ar, "ARP-offload config failed, vdev: %d\n", + arvif->vdev_id); + return ret; + } + + ret = ath10k_wmi_set_arp_ns_offload(ar, arvif); + if (ret) { + ath10k_err(ar, "failed to send offload cmd, vdev: %d\n", + arvif->vdev_id); + return ret; + } + } + + return 0; +} + static int ath10k_config_wow_listen_interval(struct ath10k *ar) { int ret; @@ -263,11 +333,17 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw, goto exit; } + ret = ath10k_wow_enable_ns_arp_offload(ar, true); + if (ret) { + ath10k_warn(ar, "failed to enable ARP-NS offload: %d\n", ret); + goto exit; + } + ret = ath10k_wow_cleanup(ar); if (ret) { ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", ret); - goto exit; + goto disable_ns_arp_offload; } ret = ath10k_wow_set_wakeups(ar, wowlan); @@ -304,6 +380,9 @@ wakeup: cleanup: ath10k_wow_cleanup(ar); +disable_ns_arp_offload: + ath10k_wow_enable_ns_arp_offload(ar, false); + exit: mutex_unlock(&ar->conf_mutex); return ret ? 1 : 0; @@ -341,8 +420,14 @@ int ath10k_wow_op_resume(struct ieee80211_hw *hw) } ret = ath10k_wow_wakeup(ar); - if (ret) + if (ret) { ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); + goto exit; + } + + ret = ath10k_wow_enable_ns_arp_offload(ar, false); + if (ret) + ath10k_warn(ar, "failed to disable ARP-NS offload: %d\n", ret); exit: if (ret) { diff --git a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c index a876484859eb..ba1adb8acea7 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is Mree software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,6 +27,7 @@ struct msm_bus_floor_client_type { }; static struct class *bus_floor_class; +static DEFINE_RT_MUTEX(msm_bus_floor_vote_lock); #define MAX_VOTER_NAME (50) #define DEFAULT_NODE_WIDTH (8) #define DBG_NAME(s) (strnstr(s, "-", 7) + 1) @@ -64,18 +65,22 @@ static ssize_t bus_floor_active_only_store(struct device *dev, { struct msm_bus_floor_client_type *cl; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (sscanf(buf, "%d", &cl->active_only) != 1) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } @@ -100,20 +105,24 @@ static ssize_t bus_floor_vote_store(struct device *dev, struct msm_bus_floor_client_type *cl; int ret = 0; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (sscanf(buf, "%llu", &cl->cur_vote_hz) != 1) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } ret = msm_bus_floor_vote_context(dev_name(dev), cl->cur_vote_hz, cl->active_only); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } @@ -126,15 +135,18 @@ static ssize_t bus_floor_vote_store_api(struct device *dev, char name[10]; u64 vote_khz = 0; + rt_mutex_lock(&msm_bus_floor_vote_lock); cl = dev_get_drvdata(dev); if (!cl) { pr_err("%s: Can't find cl", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return 0; } if (sscanf(buf, "%9s %llu", name, &vote_khz) != 2) { pr_err("%s:return error", __func__); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return -EINVAL; } @@ -142,6 +154,7 @@ static ssize_t bus_floor_vote_store_api(struct device *dev, __func__, name, vote_khz); ret = msm_bus_floor_vote(name, vote_khz); + rt_mutex_unlock(&msm_bus_floor_vote_lock); return n; } diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index da58f19dd6e6..73396072a052 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -33,6 +33,7 @@ #include <linux/debugfs.h> #include <linux/gpio.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/dma-mapping.h> #include <linux/sched.h> @@ -52,6 +53,7 @@ static int msm_spi_pm_suspend_runtime(struct device *device); static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd); static int get_local_resources(struct msm_spi *dd); static void put_local_resources(struct msm_spi *dd); +static void msm_spi_slv_setup(struct msm_spi *dd); static inline int msm_spi_configure_gsbi(struct msm_spi *dd, struct platform_device *pdev) @@ -84,7 +86,10 @@ static inline int msm_spi_configure_gsbi(struct msm_spi *dd, static inline void msm_spi_register_init(struct msm_spi *dd) { - writel_relaxed(0x00000001, dd->base + SPI_SW_RESET); + if (dd->pdata->is_slv_ctrl) + writel_relaxed(0x00000002, dd->base + SPI_SW_RESET); + else + writel_relaxed(0x00000001, dd->base + SPI_SW_RESET); msm_spi_set_state(dd, SPI_OP_STATE_RESET); writel_relaxed(0x00000000, dd->base + SPI_OPERATIONAL); writel_relaxed(0x00000000, dd->base + SPI_CONFIG); @@ -932,6 +937,7 @@ static inline void msm_spi_ack_transfer(struct msm_spi *dd) static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id) { u32 op, ret = IRQ_NONE; + u32 slv; struct msm_spi *dd = dev_id; if (pm_runtime_suspended(dd->dev)) { @@ -945,7 +951,9 @@ static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id) } op = readl_relaxed(dd->base + SPI_OPERATIONAL); + slv = readl_relaxed(dd->base + SPI_SLAVE_IRQ_STATUS); writel_relaxed(op, dd->base + SPI_OPERATIONAL); + writel_relaxed(slv, dd->base + SPI_SLAVE_IRQ_STATUS); /* * Ensure service flag was cleared before further * processing of interrupt. @@ -1234,7 +1242,10 @@ msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw) static void msm_spi_set_transfer_mode(struct msm_spi *dd, u8 bpw, u32 read_count) { - if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) { + if (dd->pdata->is_slv_ctrl) { + dd->tx_mode = SPI_BAM_MODE; + dd->rx_mode = SPI_BAM_MODE; + } else if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) { dd->tx_mode = SPI_BAM_MODE; dd->rx_mode = SPI_BAM_MODE; } else { @@ -1353,7 +1364,7 @@ static void get_transfer_length(struct msm_spi *dd) static int msm_spi_process_transfer(struct msm_spi *dd) { u8 bpw; - u32 max_speed; + u32 max_speed = 0; u32 read_count; u32 timeout; u32 spi_ioc; @@ -1392,7 +1403,7 @@ static int msm_spi_process_transfer(struct msm_spi *dd) DIV_ROUND_UP(max_speed, MSEC_PER_SEC))); read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word); - if (dd->spi->mode & SPI_LOOP) + if (dd->spi->mode & SPI_LOOP && !dd->pdata->is_slv_ctrl) int_loopback = 1; if (msm_spi_set_state(dd, SPI_OP_STATE_RESET)) @@ -1414,8 +1425,11 @@ static int msm_spi_process_transfer(struct msm_spi *dd) msm_spi_set_qup_io_modes(dd); msm_spi_set_spi_config(dd, bpw); msm_spi_set_qup_config(dd, bpw); - spi_ioc = msm_spi_set_spi_io_control(dd); + if (!dd->pdata->is_slv_ctrl) + spi_ioc = msm_spi_set_spi_io_control(dd); msm_spi_set_qup_op_mask(dd); + if (dd->pdata->is_slv_ctrl) + msm_spi_slv_setup(dd); /* The output fifo interrupt handler will handle all writes after the first. Restricting this to one write avoids contention @@ -1482,12 +1496,20 @@ transfer_end: dd->rx_mode = SPI_MODE_NONE; msm_spi_set_state(dd, SPI_OP_STATE_RESET); - if (!dd->cur_transfer->cs_change) + if (!dd->cur_transfer->cs_change && !dd->pdata->is_slv_ctrl) writel_relaxed(spi_ioc & ~SPI_IO_C_MX_CS_MODE, dd->base + SPI_IO_CONTROL); return status; } +static int msm_spi_slv_abort(struct spi_master *spi) +{ + struct msm_spi *dd = spi_master_get_devdata(spi); + + complete_all(&dd->tx_transfer_complete); + complete_all(&dd->rx_transfer_complete); + return 0; +} static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag) { @@ -1753,6 +1775,32 @@ static int msm_spi_unprepare_transfer_hardware(struct spi_master *master) return 0; } +static void msm_spi_slv_setup(struct msm_spi *dd) +{ + u32 spi_config = readl_relaxed(dd->base + SPI_CONFIG); + u32 qup_config = readl_relaxed(dd->base + QUP_CONFIG); + u32 irq_en = GENMASK(6, 0); + + qup_config &= ~QUP_CFG_MODE; + qup_config |= QUP_CONFIG_SPI_SLAVE; + qup_config |= (SPI_EN_EXT_OUT_FLAG | APP_CLK_ON_EN | CORE_CLK_ON_EN + | FIFO_CLK_ON_EN | CORE_EX_CLK_ON_EN); + spi_config |= SPI_CFG_SLAVE_OP; + writel_relaxed(qup_config, dd->base + QUP_CONFIG); + writel_relaxed(spi_config, dd->base + SPI_CONFIG); + writel_relaxed(irq_en, (dd->base + SPI_SLAVE_IRQ_EN)); + if (dd->read_buf && !dd->write_buf) { + u32 slv_cfg = + readl_relaxed(dd->base + SPI_SLAVE_CONFIG); + slv_cfg |= (RX_UNBALANCED_MASK | SPI_S_CGC_EN); + writel_relaxed(slv_cfg, (dd->base + SPI_SLAVE_CONFIG)); + } + /* + * Ensure Slave setup completes before returning. + */ + mb(); +} + static int msm_spi_setup(struct spi_device *spi) { struct msm_spi *dd; @@ -2248,6 +2296,8 @@ struct msm_spi_platform_data *msm_spi_dt_to_pdata( &pdata->rt_priority, DT_OPT, DT_BOOL, 0}, {"qcom,shared", &pdata->is_shared, DT_OPT, DT_BOOL, 0}, + {"qcom,slv-ctrl", + &pdata->is_slv_ctrl, DT_OPT, DT_BOOL, 0}, {NULL, NULL, 0, 0, 0}, }; @@ -2451,6 +2501,12 @@ err_clk_get: return rc; } +static const struct of_device_id msm_spi_dt_match[] = { + { .compatible = "qcom,spi-qup-v2", }, + { .compatible = "qcom,qup-v26", }, + {} +}; + static int msm_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -2481,6 +2537,9 @@ static int msm_spi_probe(struct platform_device *pdev) dd = spi_master_get_devdata(master); if (pdev->dev.of_node) { + const struct of_device_id *dev_id; + enum msm_spi_qup_version ver; + dd->qup_ver = SPI_QUP_VERSION_BFAM; master->dev.of_node = pdev->dev.of_node; pdata = msm_spi_dt_to_pdata(pdev, dd); @@ -2495,6 +2554,17 @@ static int msm_spi_probe(struct platform_device *pdev) "using default bus_num %d\n", pdev->id); else master->bus_num = pdev->id = rc; + + dev_id = of_match_device(msm_spi_dt_match, &pdev->dev); + if (dev_id) + ver = SPI_QUP_VERSION_SPI_SLV; + else + ver = SPI_QUP_VERSION_BFAM; + + if (ver >= SPI_QUP_VERSION_SPI_SLV) + dd->slv_support = true; + else + dd->slv_support = false; } else { pdata = pdev->dev.platform_data; dd->qup_ver = SPI_QUP_VERSION_NONE; @@ -2553,6 +2623,21 @@ static int msm_spi_probe(struct platform_device *pdev) } spi_dma_mask(&pdev->dev); + + if (pdata && pdata->is_slv_ctrl) { + if (!dd->slv_support) { + rc = -ENXIO; + dev_err(&pdev->dev, "QUP ver %d, no slv support\n", + dd->qup_ver); + goto err_probe_res; + } + + master->slave = true; + master->set_cs = NULL; + master->setup = NULL; + master->slave_abort = msm_spi_slv_abort; + } + skip_dma_resources: spin_lock_init(&dd->queue_lock); @@ -2753,13 +2838,6 @@ static int msm_spi_remove(struct platform_device *pdev) return 0; } -static struct of_device_id msm_spi_dt_match[] = { - { - .compatible = "qcom,spi-qup-v2", - }, - {} -}; - static const struct dev_pm_ops msm_spi_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(msm_spi_suspend, msm_spi_resume) SET_RUNTIME_PM_OPS(msm_spi_pm_suspend_runtime, diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h index 47d69965f18a..6632fe806e41 100644 --- a/drivers/spi/spi_qsd.h +++ b/drivers/spi/spi_qsd.h @@ -38,6 +38,7 @@ #define QUP_MX_WRITE_CNT_CURRENT 0x0154 #define QUP_CONFIG_SPI_MODE 0x0100 +#define QUP_CONFIG_SPI_SLAVE 0x0400 #endif #define GSBI_CTRL_REG 0x0 @@ -72,17 +73,26 @@ #define SPI_OUTPUT_FIFO QSD_REG(0x0100) QUP_REG(0x0110) #define SPI_INPUT_FIFO QSD_REG(0x0200) QUP_REG(0x0218) #define SPI_STATE QSD_REG(SPI_OPERATIONAL) QUP_REG(0x0004) +#define SPI_SLAVE_IRQ_STATUS (0x0330) +#define SPI_SLAVE_IRQ_EN (0x0334) +#define SPI_SLAVE_CONFIG (0x0338) /* QUP_CONFIG fields */ #define SPI_CFG_N 0x0000001F #define SPI_NO_INPUT 0x00000080 #define SPI_NO_OUTPUT 0x00000040 #define SPI_EN_EXT_OUT_FLAG 0x00010000 +#define QUP_CFG_MODE 0x00000F00 +#define APP_CLK_ON_EN BIT(12) +#define CORE_CLK_ON_EN BIT(13) +#define FIFO_CLK_ON_EN BIT(14) +#define CORE_EX_CLK_ON_EN BIT(15) /* SPI_CONFIG fields */ #define SPI_CFG_LOOPBACK 0x00000100 #define SPI_CFG_INPUT_FIRST 0x00000200 #define SPI_CFG_HS_MODE 0x00000400 +#define SPI_CFG_SLAVE_OP 0x00000020 /* SPI_IO_CONTROL fields */ #define SPI_IO_C_FORCE_CS 0x00000800 @@ -128,6 +138,23 @@ #define SPI_OP_STATE_CLEAR_BITS 0x2 +/* SPI SLAVE IRQ_STATUS/EN fields */ +#define CS_N_ASSERT BIT(0) +#define CS_N_DEASSERT BIT(1) +#define CS_N_ETXT BIT(2) +#define TX_UNDERFLOW BIT(3) +#define RX_OVERFLOW_WAIT_EOT BIT(4) +#define RX_OVERFLOW_NO_EOT BIT(5) +#define CS_N_ERXT BIT(6) + +/* SPI_SLAVE_CONFIG Fields */ +#define RX_N_SHIFT BIT(0) +#define PAUSE_ON_ERR_DIS BIT(1) +#define SPI_S_CGC_EN BIT(2) +#define RX_UNBALANCED_MASK BIT(3) +#define SLAVE_DIS_RESET_ST BIT(4) +#define SLAVE_AUTO_PAUSE_EOT BIT(7) + #define SPI_PINCTRL_STATE_DEFAULT "spi_default" #define SPI_PINCTRL_STATE_SLEEP "spi_sleep" @@ -177,6 +204,7 @@ enum msm_spi_state { enum msm_spi_qup_version { SPI_QUP_VERSION_NONE = 0x0, SPI_QUP_VERSION_BFAM = 0x2, + SPI_QUP_VERSION_SPI_SLV = 0x26, }; enum msm_spi_pipe_direction { @@ -376,6 +404,7 @@ struct msm_spi { struct pinctrl_state *pins_sleep; bool is_init_complete; bool pack_words; + bool slv_support; }; /* Forward declaration */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 5eb17ab27e4c..5cb436261115 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -2223,6 +2223,9 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io, HDMI_DDC_ARBITRATION) & ~(BIT(4))); + /* Set/Reset HDMI max TMDS clock supported by source */ + hdmi_edid_set_max_pclk_rate(data, hdmi_ctrl->max_pclk_khz); + if (!hdmi_ctrl->custom_edid && !hdmi_ctrl->sim_mode) { hdmi_ddc_config(&hdmi_ctrl->ddc_ctrl); diff --git a/include/linux/spi/qcom-spi.h b/include/linux/spi/qcom-spi.h index 598481079383..67ef31c69641 100644 --- a/include/linux/spi/qcom-spi.h +++ b/include/linux/spi/qcom-spi.h @@ -48,9 +48,11 @@ struct msm_spi_platform_data { u32 infinite_mode; bool ver_reg_exists; bool use_bam; + bool slv_test; u32 bam_consumer_pipe_index; u32 bam_producer_pipe_index; bool rt_priority; bool use_pinctrl; bool is_shared; + bool is_slv_ctrl; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 3dd0091f4af3..75050fac06fb 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4904,12 +4904,17 @@ enum nl80211_smps_mode { * change to the channel status. * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is * over, channel becomes usable. + * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this + * non-operating channel is expired and no longer valid. New CAC must + * be done on this channel before starting the operation. This is not + * applicable for ETSI dfs domain where pre-CAC is valid for ever. */ enum nl80211_radar_event { NL80211_RADAR_DETECTED, NL80211_RADAR_CAC_FINISHED, NL80211_RADAR_CAC_ABORTED, NL80211_RADAR_NOP_FINISHED, + NL80211_RADAR_PRE_CAC_EXPIRED, }; /** diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 91d02ac0f42f..547d38356583 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -32,6 +32,11 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, rdev_set_qos_map(rdev, dev, NULL); if (notify) nl80211_send_ap_stopped(wdev); + + /* Should we apply the grace period during beaconing interface + * shutdown also? + */ + cfg80211_sched_dfs_chan_update(rdev); } return err; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index d5ccaeaa76e0..6b5467ea99db 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -456,6 +456,105 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, return (r1 + r2 > 0); } +/* + * Checks if center frequency of chan falls with in the bandwidth + * range of chandef. + */ +bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan) +{ + int width; + u32 cf_offset, freq; + + if (chandef->chan->center_freq == chan->center_freq) + return true; + + width = cfg80211_chandef_get_width(chandef); + if (width <= 20) + return false; + + cf_offset = width / 2 - 10; + + for (freq = chandef->center_freq1 - width / 2 + 10; + freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) { + if (chan->center_freq == freq) + return true; + } + + if (!chandef->center_freq2) + return false; + + for (freq = chandef->center_freq2 - width / 2 + 10; + freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) { + if (chan->center_freq == freq) + return true; + } + + return false; +} + +bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) +{ + bool active = false; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->chandef.chan) + return false; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + active = wdev->beacon_interval != 0; + break; + case NL80211_IFTYPE_ADHOC: + active = wdev->ssid_len != 0; + break; + case NL80211_IFTYPE_MESH_POINT: + active = wdev->mesh_id_len != 0; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_P2P_DEVICE: + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + WARN_ON(1); + } + + return active; +} + +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) +{ + struct wireless_dev *wdev; + + ASSERT_RTNL(); + + if (!(chan->flags & IEEE80211_CHAN_RADAR)) + return false; + + list_for_each_entry(wdev, &wiphy->wdev_list, list) { + wdev_lock(wdev); + if (!cfg80211_beaconing_iface_active(wdev)) { + wdev_unlock(wdev); + continue; + } + + if (cfg80211_is_sub_chan(&wdev->chandef, chan)) { + wdev_unlock(wdev); + return true; + } + wdev_unlock(wdev); + } + + return false; +} static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, u32 center_freq, diff --git a/net/wireless/core.h b/net/wireless/core.h index be5ab8c13a39..55c64d08db31 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -438,6 +438,16 @@ unsigned int cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef); +void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); + +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan); + +bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev); + +bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan); + static inline unsigned int elapsed_jiffies_msecs(unsigned long start) { unsigned long end = jiffies; diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 86005410a22f..499d98745d44 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -1478,9 +1478,9 @@ country VI: DFS-FCC country VN: DFS-FCC (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (5170 - 5250 @ 80), (24) + (5250 - 5330 @ 80), (24), DFS + (5490 - 5730 @ 80), (24), DFS (5735 - 5835 @ 80), (30) # 60 gHz band channels 1-4 (57240 - 65880 @ 2160), (40) diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 4c55fab9b4e4..7da0cd9c5e73 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -186,6 +186,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) if (!nowext) wdev->wext.ibss.ssid_len = 0; #endif + cfg80211_sched_dfs_chan_update(rdev); } void cfg80211_clear_ibss(struct net_device *dev, bool nowext) diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 092300b30c37..dda90a39be40 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -260,6 +260,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, wdev->mesh_id_len = 0; memset(&wdev->chandef, 0, sizeof(wdev->chandef)); rdev_set_qos_map(rdev, dev, NULL); + cfg80211_sched_dfs_chan_update(rdev); } return err; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 2bc6eaa766c7..465e0d31229d 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -743,6 +743,12 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, } EXPORT_SYMBOL(cfg80211_rx_mgmt); +void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev) +{ + cancel_delayed_work(&rdev->dfs_update_channels_wk); + queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0); +} + void cfg80211_dfs_channels_update_work(struct work_struct *work) { struct delayed_work *delayed_work; @@ -753,6 +759,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) struct wiphy *wiphy; bool check_again = false; unsigned long timeout, next_time = 0; + unsigned long time_dfs_update; + enum nl80211_radar_event radar_event; int bandid, i; delayed_work = container_of(work, struct delayed_work, work); @@ -769,11 +777,27 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) for (i = 0; i < sband->n_channels; i++) { c = &sband->channels[i]; - if (c->dfs_state != NL80211_DFS_UNAVAILABLE) + if (!(c->flags & IEEE80211_CHAN_RADAR)) + continue; + + if (c->dfs_state != NL80211_DFS_UNAVAILABLE && + c->dfs_state != NL80211_DFS_AVAILABLE) continue; - timeout = c->dfs_state_entered + msecs_to_jiffies( - IEEE80211_DFS_MIN_NOP_TIME_MS); + if (c->dfs_state == NL80211_DFS_UNAVAILABLE) { + time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS; + radar_event = NL80211_RADAR_NOP_FINISHED; + } else { + if (regulatory_pre_cac_allowed(wiphy) || + cfg80211_any_wiphy_oper_chan(wiphy, c)) + continue; + + time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS; + radar_event = NL80211_RADAR_PRE_CAC_EXPIRED; + } + + timeout = c->dfs_state_entered + + msecs_to_jiffies(time_dfs_update); if (time_after_eq(jiffies, timeout)) { c->dfs_state = NL80211_DFS_USABLE; @@ -783,8 +807,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) NL80211_CHAN_NO_HT); nl80211_radar_notify(rdev, &chandef, - NL80211_RADAR_NOP_FINISHED, - NULL, GFP_ATOMIC); + radar_event, NULL, + GFP_ATOMIC); continue; } @@ -809,7 +833,6 @@ void cfg80211_radar_event(struct wiphy *wiphy, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - unsigned long timeout; trace_cfg80211_radar_event(wiphy, chandef); @@ -819,9 +842,7 @@ void cfg80211_radar_event(struct wiphy *wiphy, */ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); - timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS); - queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, - timeout); + cfg80211_sched_dfs_chan_update(rdev); nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); } @@ -850,6 +871,7 @@ void cfg80211_cac_event(struct net_device *netdev, msecs_to_jiffies(wdev->cac_time_ms); WARN_ON(!time_after_eq(jiffies, timeout)); cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); + cfg80211_sched_dfs_chan_update(rdev); break; case NL80211_RADAR_CAC_ABORTED: break; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 050d7948dd68..26ac0a4808a0 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -3247,6 +3247,34 @@ bool regulatory_indoor_allowed(void) return reg_is_indoor; } +bool regulatory_pre_cac_allowed(struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *regd = NULL; + const struct ieee80211_regdomain *wiphy_regd = NULL; + bool pre_cac_allowed = false; + + rcu_read_lock(); + + regd = rcu_dereference(cfg80211_regdomain); + wiphy_regd = rcu_dereference(wiphy->regd); + if (!wiphy_regd) { + if (regd->dfs_region == NL80211_DFS_ETSI) + pre_cac_allowed = true; + + rcu_read_unlock(); + + return pre_cac_allowed; + } + + if (regd->dfs_region == wiphy_regd->dfs_region && + wiphy_regd->dfs_region == NL80211_DFS_ETSI) + pre_cac_allowed = true; + + rcu_read_unlock(); + + return pre_cac_allowed; +} + int __init regulatory_init(void) { int err = 0; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 9f495d76eca0..7bbe2d138d2a 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -143,4 +143,18 @@ int cfg80211_get_unii(int freq); */ bool regulatory_indoor_allowed(void); +/* + * Grace period to timeout pre-CAC results on the dfs channels. This timeout + * value is used for Non-ETSI domain. + * TODO: May be make this timeout available through regdb? + */ +#define REG_PRE_CAC_EXPIRY_GRACE_MS 2000 + +/** + * regulatory_pre_cac_allowed - if pre-CAC allowed in the current dfs domain + * @wiphy: wiphy for which pre-CAC capability is checked. + + * Pre-CAC is allowed only in ETSI domain. + */ +bool regulatory_pre_cac_allowed(struct wiphy *wiphy); #endif /* __NET_WIRELESS_REG_H */ |
