From: Sasha Levin Date: Fri, 23 Sep 2022 16:54:11 +0000 (-0400) Subject: Fixes for 5.10 X-Git-Tag: v4.9.330~98 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a4ff282bf2e0d0f8c08e388653569bbda1f94afb;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.10 Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/arm64-bti-disable-in-kernel-bti-when-cross-section-t.patch b/queue-5.10/arm64-bti-disable-in-kernel-bti-when-cross-section-t.patch new file mode 100644 index 00000000000..17cabc544b7 --- /dev/null +++ b/queue-5.10/arm64-bti-disable-in-kernel-bti-when-cross-section-t.patch @@ -0,0 +1,53 @@ +From f42e94f5739b6ae0eb6e63bed2d753f2aae79041 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 5 Sep 2022 15:22:55 +0100 +Subject: arm64/bti: Disable in kernel BTI when cross section thunks are broken + +From: Mark Brown + +[ Upstream commit c0a454b9044fdc99486853aa424e5b3be2107078 ] + +GCC does not insert a `bti c` instruction at the beginning of a function +when it believes that all callers reach the function through a direct +branch[1]. Unfortunately the logic it uses to determine this is not +sufficiently robust, for example not taking account of functions being +placed in different sections which may be loaded separately, so we may +still see thunks being generated to these functions. If that happens, +the first instruction in the callee function will result in a Branch +Target Exception due to the missing landing pad. + +While this has currently only been observed in the case of modules +having their main code loaded sufficiently far from their init section +to require thunks it could potentially happen for other cases so the +safest thing is to disable BTI for the kernel when building with an +affected toolchain. + +[1]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106671 + +Reported-by: D Scott Phillips +[Bits of the commit message are lifted from his report & workaround] +Signed-off-by: Mark Brown +Link: https://lore.kernel.org/r/20220905142255.591990-1-broonie@kernel.org +Cc: # v5.10+ +Signed-off-by: Will Deacon +Signed-off-by: Sasha Levin +--- + arch/arm64/Kconfig | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 662311a513f0..af65ab83e63d 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -1654,6 +1654,8 @@ config ARM64_BTI_KERNEL + depends on CC_HAS_BRANCH_PROT_PAC_RET_BTI + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94697 + depends on !CC_IS_GCC || GCC_VERSION >= 100100 ++ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106671 ++ depends on !CC_IS_GCC + # https://github.com/llvm/llvm-project/commit/a88c722e687e6780dcd6a58718350dc76fcc4cc9 + depends on !CC_IS_CLANG || CLANG_VERSION >= 120000 + depends on (!FUNCTION_GRAPH_TRACER || DYNAMIC_FTRACE_WITH_REGS) +-- +2.35.1 + diff --git a/queue-5.10/arm64-restrict-arm64_bti_kernel-to-clang-12.0.0-and-.patch b/queue-5.10/arm64-restrict-arm64_bti_kernel-to-clang-12.0.0-and-.patch new file mode 100644 index 00000000000..fc21e5733c1 --- /dev/null +++ b/queue-5.10/arm64-restrict-arm64_bti_kernel-to-clang-12.0.0-and-.patch @@ -0,0 +1,67 @@ +From 35c4cf7e6d21f5ed5b6c2030f6e51483dacc49a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 12 Jul 2021 14:46:37 -0700 +Subject: arm64: Restrict ARM64_BTI_KERNEL to clang 12.0.0 and newer + +From: Nathan Chancellor + +[ Upstream commit 8cdd23c23c3d481a43b4aa03dcb5738812831115 ] + +Commit 97fed779f2a6 ("arm64: bti: Provide Kconfig for kernel mode BTI") +disabled CONFIG_ARM64_BTI_KERNEL when CONFIG_GCOV_KERNEL was enabled and +compiling with clang because of warnings that were seen with +allmodconfig because LLVM was not emitting PAC/BTI instructions for +compiler generated functions: + + | warning: some functions compiled with BTI and some compiled without BTI + | warning: not setting BTI in feature flags + +This dependency was fine for avoiding the warnings with allmodconfig +until commit 51c2ee6d121c ("Kconfig: Introduce ARCH_WANTS_NO_INSTR and +CC_HAS_NO_PROFILE_FN_ATTR"), which prevents CONFIG_GCOV_KERNEL from +being enabled with clang 12.0.0 or older because those versions do not +support the no_profile_instrument_function attribute. + +As a result, CONFIG_ARM64_BTI_KERNEL gets enabled with allmodconfig and +there are more warnings like the ones above due to CONFIG_KASAN, which +suffers from the same problem as CONFIG_GCOV_KERNEL. This was most +likely not noticed at the time because allmodconfig + +CONFIG_GCOV_KERNEL=n was not tested. defconfig + CONFIG_KASAN=y is +enough to reproduce the same warnings as above. + +The root cause of the warnings was resolved in LLVM during the 12.0.0 +release so rather than play whack-a-mole with the dependencies, just +update CONFIG_ARM64_BTI_KERNEL to require clang 12.0.0, which will have +all of the issues ironed out. + +Link: https://github.com/ClangBuiltLinux/linux/issues/1428 +Link: https://github.com/ClangBuiltLinux/continuous-integration2/runs/3010034706?check_suite_focus=true +Link: https://github.com/ClangBuiltLinux/continuous-integration2/runs/3010035725?check_suite_focus=true +Link: https://github.com/llvm/llvm-project/commit/a88c722e687e6780dcd6a58718350dc76fcc4cc9 +Signed-off-by: Nathan Chancellor +Reviewed-by: Nick Desaulniers +Link: https://lore.kernel.org/r/20210712214636.3134425-1-nathan@kernel.org +Signed-off-by: Will Deacon +Stable-dep-of: c0a454b9044f ("arm64/bti: Disable in kernel BTI when cross section thunks are broken") +Signed-off-by: Sasha Levin +--- + arch/arm64/Kconfig | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 1116a8d092c0..662311a513f0 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -1654,7 +1654,8 @@ config ARM64_BTI_KERNEL + depends on CC_HAS_BRANCH_PROT_PAC_RET_BTI + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94697 + depends on !CC_IS_GCC || GCC_VERSION >= 100100 +- depends on !(CC_IS_CLANG && GCOV_KERNEL) ++ # https://github.com/llvm/llvm-project/commit/a88c722e687e6780dcd6a58718350dc76fcc4cc9 ++ depends on !CC_IS_CLANG || CLANG_VERSION >= 120000 + depends on (!FUNCTION_GRAPH_TRACER || DYNAMIC_FTRACE_WITH_REGS) + help + Build the kernel with Branch Target Identification annotations +-- +2.35.1 + diff --git a/queue-5.10/drm-amdgpu-indirect-register-access-for-nv12-sriov.patch b/queue-5.10/drm-amdgpu-indirect-register-access-for-nv12-sriov.patch new file mode 100644 index 00000000000..2c8a3cce7c0 --- /dev/null +++ b/queue-5.10/drm-amdgpu-indirect-register-access-for-nv12-sriov.patch @@ -0,0 +1,59 @@ +From 0d2397bd629a7d3b91f60c176216e64970a30921 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 29 Mar 2021 15:47:20 +0800 +Subject: drm/amdgpu: indirect register access for nv12 sriov + +From: Peng Ju Zhou + +[ Upstream commit 8b8a162da820d48bb94261ae4684f2c839ce148c ] + +unify host driver and guest driver indirect access +control bits names + +Signed-off-by: Peng Ju Zhou +Reviewed-by: Emily.Deng +Signed-off-by: Alex Deucher +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 5 +++++ + drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c | 8 ++++++++ + 2 files changed, 13 insertions(+) + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +index f262c4e7a48a..a5f9f51cf583 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -2047,6 +2047,11 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) + amdgpu_vf_error_put(adev, AMDGIM_ERROR_VF_ATOMBIOS_INIT_FAIL, 0, 0); + return r; + } ++ ++ /*get pf2vf msg info at it's earliest time*/ ++ if (amdgpu_sriov_vf(adev)) ++ amdgpu_virt_init_data_exchange(adev); ++ + } + } + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +index e7678ba8fdcf..d17bd0140bf6 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +@@ -615,6 +615,14 @@ void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev) + if (adev->virt.ras_init_done) + amdgpu_virt_add_bad_page(adev, bp_block_offset, bp_block_size); + } ++ } else if (adev->bios != NULL) { ++ adev->virt.fw_reserve.p_pf2vf = ++ (struct amd_sriov_msg_pf2vf_info_header *) ++ (adev->bios + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB << 10)); ++ ++ amdgpu_virt_read_pf2vf_data(adev); ++ ++ return; + } + + if (adev->virt.vf2pf_update_interval_ms != 0) { +-- +2.35.1 + diff --git a/queue-5.10/drm-amdgpu-make-sure-to-init-common-ip-before-gmc.patch b/queue-5.10/drm-amdgpu-make-sure-to-init-common-ip-before-gmc.patch new file mode 100644 index 00000000000..b42049eef2c --- /dev/null +++ b/queue-5.10/drm-amdgpu-make-sure-to-init-common-ip-before-gmc.patch @@ -0,0 +1,73 @@ +From d3d1d1e1ae4faad5acf160fdaaee95bfaa7e5b8b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 30 Aug 2022 10:59:49 -0400 +Subject: drm/amdgpu: make sure to init common IP before gmc +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Alex Deucher + +[ Upstream commit a8671493d2074950553da3cf07d1be43185ef6c6 ] + +Move common IP init before GMC init so that HDP gets +remapped before GMC init which uses it. + +This fixes the Unsupported Request error reported through +AER during driver load. The error happens as a write happens +to the remap offset before real remapping is done. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=216373 + +The error was unnoticed before and got visible because of the commit +referenced below. This doesn't fix anything in the commit below, rather +fixes the issue in amdgpu exposed by the commit. The reference is only +to associate this commit with below one so that both go together. + +Fixes: 8795e182b02d ("PCI/portdrv: Don't disable AER reporting in get_port_device_capability()") + +Acked-by: Christian König +Reviewed-by: Lijo Lazar +Signed-off-by: Alex Deucher +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +index 9ccc8c82353b..f44ab44abd64 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -2179,8 +2179,16 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev) + } + adev->ip_blocks[i].status.sw = true; + +- /* need to do gmc hw init early so we can allocate gpu mem */ +- if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) { ++ if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_COMMON) { ++ /* need to do common hw init early so everything is set up for gmc */ ++ r = adev->ip_blocks[i].version->funcs->hw_init((void *)adev); ++ if (r) { ++ DRM_ERROR("hw_init %d failed %d\n", i, r); ++ goto init_failed; ++ } ++ adev->ip_blocks[i].status.hw = true; ++ } else if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) { ++ /* need to do gmc hw init early so we can allocate gpu mem */ + /* Try to reserve bad pages early */ + if (amdgpu_sriov_vf(adev)) + amdgpu_virt_exchange_data(adev); +@@ -2762,8 +2770,8 @@ static int amdgpu_device_ip_reinit_early_sriov(struct amdgpu_device *adev) + int i, r; + + static enum amd_ip_block_type ip_order[] = { +- AMD_IP_BLOCK_TYPE_GMC, + AMD_IP_BLOCK_TYPE_COMMON, ++ AMD_IP_BLOCK_TYPE_GMC, + AMD_IP_BLOCK_TYPE_PSP, + AMD_IP_BLOCK_TYPE_IH, + }; +-- +2.35.1 + diff --git a/queue-5.10/drm-amdgpu-move-nbio-sdma_doorbell_range-into-sdma-c.patch b/queue-5.10/drm-amdgpu-move-nbio-sdma_doorbell_range-into-sdma-c.patch new file mode 100644 index 00000000000..2cd2a9c0b76 --- /dev/null +++ b/queue-5.10/drm-amdgpu-move-nbio-sdma_doorbell_range-into-sdma-c.patch @@ -0,0 +1,106 @@ +From 8fbd6e01fdf7ce2f24d327ee9d4bea7348adbd32 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 9 Sep 2022 11:53:27 -0400 +Subject: drm/amdgpu: move nbio sdma_doorbell_range() into sdma code for vega +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Alex Deucher + +[ Upstream commit e3163bc8ffdfdb405e10530b140135b2ee487f89 ] + +This mirrors what we do for other asics and this way we are +sure the sdma doorbell range is properly initialized. + +There is a comment about the way doorbells on gfx9 work that +requires that they are initialized for other IPs before GFX +is initialized. However, the statement says that it applies to +multimedia as well, but the VCN code currently initializes +doorbells after GFX and there are no known issues there. In my +testing at least I don't see any problems on SDMA. + +This is a prerequisite for fixing the Unsupported Request error +reported through AER during driver load. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=216373 + +The error was unnoticed before and got visible because of the commit +referenced below. This doesn't fix anything in the commit below, rather +fixes the issue in amdgpu exposed by the commit. The reference is only +to associate this commit with below one so that both go together. + +Fixes: 8795e182b02d ("PCI/portdrv: Don't disable AER reporting in get_port_device_capability()") + +Acked-by: Christian König +Reviewed-by: Lijo Lazar +Signed-off-by: Alex Deucher +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c | 5 +++++ + drivers/gpu/drm/amd/amdgpu/soc15.c | 25 ------------------------- + 2 files changed, 5 insertions(+), 25 deletions(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +index 1f2e2460e121..a1a8e026b9fa 100644 +--- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_0.c +@@ -1475,6 +1475,11 @@ static int sdma_v4_0_start(struct amdgpu_device *adev) + WREG32_SDMA(i, mmSDMA0_CNTL, temp); + + if (!amdgpu_sriov_vf(adev)) { ++ ring = &adev->sdma.instance[i].ring; ++ adev->nbio.funcs->sdma_doorbell_range(adev, i, ++ ring->use_doorbell, ring->doorbell_index, ++ adev->doorbell_index.sdma_doorbell_range); ++ + /* unhalt engine */ + temp = RREG32_SDMA(i, mmSDMA0_F32_CNTL); + temp = REG_SET_FIELD(temp, SDMA0_F32_CNTL, HALT, 0); +diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c +index 7212b9900e0a..abd649285a22 100644 +--- a/drivers/gpu/drm/amd/amdgpu/soc15.c ++++ b/drivers/gpu/drm/amd/amdgpu/soc15.c +@@ -1332,25 +1332,6 @@ static int soc15_common_sw_fini(void *handle) + return 0; + } + +-static void soc15_doorbell_range_init(struct amdgpu_device *adev) +-{ +- int i; +- struct amdgpu_ring *ring; +- +- /* sdma/ih doorbell range are programed by hypervisor */ +- if (!amdgpu_sriov_vf(adev)) { +- for (i = 0; i < adev->sdma.num_instances; i++) { +- ring = &adev->sdma.instance[i].ring; +- adev->nbio.funcs->sdma_doorbell_range(adev, i, +- ring->use_doorbell, ring->doorbell_index, +- adev->doorbell_index.sdma_doorbell_range); +- } +- +- adev->nbio.funcs->ih_doorbell_range(adev, adev->irq.ih.use_doorbell, +- adev->irq.ih.doorbell_index); +- } +-} +- + static int soc15_common_hw_init(void *handle) + { + struct amdgpu_device *adev = (struct amdgpu_device *)handle; +@@ -1370,12 +1351,6 @@ static int soc15_common_hw_init(void *handle) + + /* enable the doorbell aperture */ + soc15_enable_doorbell_aperture(adev, true); +- /* HW doorbell routing policy: doorbell writing not +- * in SDMA/IH/MM/ACV range will be routed to CP. So +- * we need to init SDMA/IH/MM/ACV doorbell range prior +- * to CP ip block init and ring test. +- */ +- soc15_doorbell_range_init(adev); + + return 0; + } +-- +2.35.1 + diff --git a/queue-5.10/drm-amdgpu-separate-vf2pf-work-item-init-from-virt-d.patch b/queue-5.10/drm-amdgpu-separate-vf2pf-work-item-init-from-virt-d.patch new file mode 100644 index 00000000000..02671b67486 --- /dev/null +++ b/queue-5.10/drm-amdgpu-separate-vf2pf-work-item-init-from-virt-d.patch @@ -0,0 +1,133 @@ +From 5550d0c45ffb49b862daa3bc3ef827185a23a76e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Dec 2021 17:01:45 +0000 +Subject: drm/amdgpu: Separate vf2pf work item init from virt data exchange + +From: Victor Skvortsov + +[ Upstream commit 892deb48269c65376f3eeb5b4c032ff2c2979bd7 ] + +We want to be able to call virt data exchange conditionally +after gmc sw init to reserve bad pages as early as possible. +Since this is a conditional call, we will need +to call it again unconditionally later in the init sequence. + +Refactor the data exchange function so it can be +called multiple times without re-initializing the work item. + +v2: Cleaned up the code. Kept the original call to init_exchange_data() +inside early init to initialize the work item, afterwards call +exchange_data() when needed. + +Signed-off-by: Victor Skvortsov +Reviewed By: Shaoyun.liu +Signed-off-by: Alex Deucher +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 6 +++- + drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c | 36 ++++++++++++++-------- + drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h | 1 + + 3 files changed, 30 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +index a5f9f51cf583..9ccc8c82353b 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -2181,6 +2181,10 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev) + + /* need to do gmc hw init early so we can allocate gpu mem */ + if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) { ++ /* Try to reserve bad pages early */ ++ if (amdgpu_sriov_vf(adev)) ++ amdgpu_virt_exchange_data(adev); ++ + r = amdgpu_device_vram_scratch_init(adev); + if (r) { + DRM_ERROR("amdgpu_vram_scratch_init failed %d\n", r); +@@ -2212,7 +2216,7 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev) + } + + if (amdgpu_sriov_vf(adev)) +- amdgpu_virt_init_data_exchange(adev); ++ amdgpu_virt_exchange_data(adev); + + r = amdgpu_ib_pool_init(adev); + if (r) { +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +index d17bd0140bf6..5217eadd7214 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +@@ -580,17 +580,35 @@ void amdgpu_virt_fini_data_exchange(struct amdgpu_device *adev) + + void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev) + { +- uint64_t bp_block_offset = 0; +- uint32_t bp_block_size = 0; +- struct amd_sriov_msg_pf2vf_info *pf2vf_v2 = NULL; +- + adev->virt.fw_reserve.p_pf2vf = NULL; + adev->virt.fw_reserve.p_vf2pf = NULL; + adev->virt.vf2pf_update_interval_ms = 0; + +- if (adev->mman.fw_vram_usage_va != NULL) { ++ if (adev->bios != NULL) { + adev->virt.vf2pf_update_interval_ms = 2000; + ++ adev->virt.fw_reserve.p_pf2vf = ++ (struct amd_sriov_msg_pf2vf_info_header *) ++ (adev->bios + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB << 10)); ++ ++ amdgpu_virt_read_pf2vf_data(adev); ++ } ++ ++ if (adev->virt.vf2pf_update_interval_ms != 0) { ++ INIT_DELAYED_WORK(&adev->virt.vf2pf_work, amdgpu_virt_update_vf2pf_work_item); ++ schedule_delayed_work(&(adev->virt.vf2pf_work), msecs_to_jiffies(adev->virt.vf2pf_update_interval_ms)); ++ } ++} ++ ++ ++void amdgpu_virt_exchange_data(struct amdgpu_device *adev) ++{ ++ uint64_t bp_block_offset = 0; ++ uint32_t bp_block_size = 0; ++ struct amd_sriov_msg_pf2vf_info *pf2vf_v2 = NULL; ++ ++ if (adev->mman.fw_vram_usage_va != NULL) { ++ + adev->virt.fw_reserve.p_pf2vf = + (struct amd_sriov_msg_pf2vf_info_header *) + (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB << 10)); +@@ -621,16 +639,10 @@ void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev) + (adev->bios + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB << 10)); + + amdgpu_virt_read_pf2vf_data(adev); +- +- return; +- } +- +- if (adev->virt.vf2pf_update_interval_ms != 0) { +- INIT_DELAYED_WORK(&adev->virt.vf2pf_work, amdgpu_virt_update_vf2pf_work_item); +- schedule_delayed_work(&(adev->virt.vf2pf_work), adev->virt.vf2pf_update_interval_ms); + } + } + ++ + void amdgpu_detect_virtualization(struct amdgpu_device *adev) + { + uint32_t reg; +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h +index 8dd624c20f89..77b9d37bfa1b 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h +@@ -271,6 +271,7 @@ int amdgpu_virt_alloc_mm_table(struct amdgpu_device *adev); + void amdgpu_virt_free_mm_table(struct amdgpu_device *adev); + void amdgpu_virt_release_ras_err_handler_data(struct amdgpu_device *adev); + void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev); ++void amdgpu_virt_exchange_data(struct amdgpu_device *adev); + void amdgpu_virt_fini_data_exchange(struct amdgpu_device *adev); + void amdgpu_detect_virtualization(struct amdgpu_device *adev); + +-- +2.35.1 + diff --git a/queue-5.10/iio-adc-mcp3911-correct-microchip-device-addr-proper.patch b/queue-5.10/iio-adc-mcp3911-correct-microchip-device-addr-proper.patch new file mode 100644 index 00000000000..fee7e982536 --- /dev/null +++ b/queue-5.10/iio-adc-mcp3911-correct-microchip-device-addr-proper.patch @@ -0,0 +1,45 @@ +From cb169726751dc4d3ac964e476db7f7329c4e12b5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 Jul 2022 15:07:19 +0200 +Subject: iio: adc: mcp3911: correct "microchip,device-addr" property + +From: Marcus Folkesson + +[ Upstream commit cfbd76d5c9c449739bb74288d982bccf9ff822f4 ] + +Go for the right property name that is documented in the bindings. + +Fixes: 3a89b289df5d ("iio: adc: add support for mcp3911") +Signed-off-by: Marcus Folkesson +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20220722130726.7627-3-marcus.folkesson@gmail.com +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +--- + drivers/iio/adc/mcp3911.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c +index 608842632925..7eecbfd491a4 100644 +--- a/drivers/iio/adc/mcp3911.c ++++ b/drivers/iio/adc/mcp3911.c +@@ -217,7 +217,14 @@ static int mcp3911_config(struct mcp3911 *adc) + u32 configreg; + int ret; + +- device_property_read_u32(dev, "device-addr", &adc->dev_addr); ++ ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr); ++ ++ /* ++ * Fallback to "device-addr" due to historical mismatch between ++ * dt-bindings and implementation ++ */ ++ if (ret) ++ device_property_read_u32(dev, "device-addr", &adc->dev_addr); + if (adc->dev_addr > 3) { + dev_err(&adc->spi->dev, + "invalid device address (%i). Must be in range 0-3.\n", +-- +2.35.1 + diff --git a/queue-5.10/iio-adc-mcp3911-switch-to-generic-firmware-propertie.patch b/queue-5.10/iio-adc-mcp3911-switch-to-generic-firmware-propertie.patch new file mode 100644 index 00000000000..0f691058b62 --- /dev/null +++ b/queue-5.10/iio-adc-mcp3911-switch-to-generic-firmware-propertie.patch @@ -0,0 +1,69 @@ +From e86692c086eb19c81a5bf3028e1e20afb4f6734a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 5 Dec 2021 17:01:36 +0000 +Subject: iio:adc:mcp3911: Switch to generic firmware properties. + +From: Jonathan Cameron + +[ Upstream commit 4efc1c614d334883cce09c38aa3fe74d3fb0bbf0 ] + +This allows use of the driver with other types of firmware such as ACPI +PRP0001 based probing. + +Also part of a general attempt to remove direct use of of_ specific +accessors from IIO. + +Added an include for mod_devicetable.h whilst here to cover the +struct of_device_id definition. + +Signed-off-by: Jonathan Cameron +Reviewed-by: Andy Shevchenko +Cc: Kent Gustavsson +Reviewed-by: Marcus Folkesson +Stable-dep-of: cfbd76d5c9c4 ("iio: adc: mcp3911: correct "microchip,device-addr" property") +Signed-off-by: Sasha Levin +--- + drivers/iio/adc/mcp3911.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c +index 65278270a75c..608842632925 100644 +--- a/drivers/iio/adc/mcp3911.c ++++ b/drivers/iio/adc/mcp3911.c +@@ -10,6 +10,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + +@@ -209,12 +211,13 @@ static const struct iio_info mcp3911_info = { + .write_raw = mcp3911_write_raw, + }; + +-static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node) ++static int mcp3911_config(struct mcp3911 *adc) + { ++ struct device *dev = &adc->spi->dev; + u32 configreg; + int ret; + +- of_property_read_u32(of_node, "device-addr", &adc->dev_addr); ++ device_property_read_u32(dev, "device-addr", &adc->dev_addr); + if (adc->dev_addr > 3) { + dev_err(&adc->spi->dev, + "invalid device address (%i). Must be in range 0-3.\n", +@@ -298,7 +301,7 @@ static int mcp3911_probe(struct spi_device *spi) + } + } + +- ret = mcp3911_config(adc, spi->dev.of_node); ++ ret = mcp3911_config(adc); + if (ret) + goto clk_disable; + +-- +2.35.1 + diff --git a/queue-5.10/mmc-core-fix-inconsistent-sd3_bus_mode-at-uhs-i-sd-v.patch b/queue-5.10/mmc-core-fix-inconsistent-sd3_bus_mode-at-uhs-i-sd-v.patch new file mode 100644 index 00000000000..c6a65fe3704 --- /dev/null +++ b/queue-5.10/mmc-core-fix-inconsistent-sd3_bus_mode-at-uhs-i-sd-v.patch @@ -0,0 +1,101 @@ +From 4eee75815be102dbe98bf33711acd23769cee62e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 15 Aug 2022 10:33:21 +0300 +Subject: mmc: core: Fix inconsistent sd3_bus_mode at UHS-I SD voltage switch + failure + +From: Adrian Hunter + +[ Upstream commit 63f1560930e4e1c4f6279b8ae715c9841fe1a6d3 ] + +If re-initialization results is a different signal voltage, because the +voltage switch failed previously, but not this time (or vice versa), then +sd3_bus_mode will be inconsistent with the card because the SD_SWITCH +command is done only upon first initialization. + +Fix by always reading SD_SWITCH information during re-initialization, which +also means it does not need to be re-read later for the 1.8V fixup +workaround. + +Note, brief testing showed SD_SWITCH took about 1.8ms to 2ms which added +about 1% to 1.5% to the re-initialization time, so it's not particularly +significant. + +Reported-by: Seunghui Lee +Signed-off-by: Adrian Hunter +Reviewed-by: Seunghui Lee +Tested-by: Seunghui Lee +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/r/20220815073321.63382-3-adrian.hunter@intel.com +Signed-off-by: Ulf Hansson +Signed-off-by: Sasha Levin +--- + drivers/mmc/core/sd.c | 42 ++++++++++++++++-------------------------- + 1 file changed, 16 insertions(+), 26 deletions(-) + +diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c +index 0b09cdaaeb6c..899768ed1688 100644 +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -932,15 +932,16 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + + /* Erase init depends on CSD and SSR */ + mmc_init_erase(card); +- +- /* +- * Fetch switch information from card. +- */ +- err = mmc_read_switch(card); +- if (err) +- return err; + } + ++ /* ++ * Fetch switch information from card. Note, sd3_bus_mode can change if ++ * voltage switch outcome changes, so do this always. ++ */ ++ err = mmc_read_switch(card); ++ if (err) ++ return err; ++ + /* + * For SPI, enable CRC as appropriate. + * This CRC enable is located AFTER the reading of the +@@ -1089,26 +1090,15 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_uhs(host) && + mmc_sd_card_using_v18(card) && + host->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_180) { +- /* +- * Re-read switch information in case it has changed since +- * oldcard was initialized. +- */ +- if (oldcard) { +- err = mmc_read_switch(card); +- if (err) +- goto free_card; +- } +- if (mmc_sd_card_using_v18(card)) { +- if (mmc_host_set_uhs_voltage(host) || +- mmc_sd_init_uhs_card(card)) { +- v18_fixup_failed = true; +- mmc_power_cycle(host, ocr); +- if (!oldcard) +- mmc_remove_card(card); +- goto retry; +- } +- goto cont; ++ if (mmc_host_set_uhs_voltage(host) || ++ mmc_sd_init_uhs_card(card)) { ++ v18_fixup_failed = true; ++ mmc_power_cycle(host, ocr); ++ if (!oldcard) ++ mmc_remove_card(card); ++ goto retry; + } ++ goto cont; + } + + /* Initialization sequence for UHS-I cards */ +-- +2.35.1 + diff --git a/queue-5.10/net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch b/queue-5.10/net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch new file mode 100644 index 00000000000..e89a54708c5 --- /dev/null +++ b/queue-5.10/net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch @@ -0,0 +1,51 @@ +From 9e6d4bd8d3ed68ca8238c1997360cf273e22c4de Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Sep 2022 15:41:11 +0200 +Subject: net: mvpp2: debugfs: fix memory leak when using debugfs_lookup() + +From: Greg Kroah-Hartman + +[ Upstream commit fe2c9c61f668cde28dac2b188028c5299cedcc1e ] + +When calling debugfs_lookup() the result must have dput() called on it, +otherwise the memory will leak over time. Fix this up to be much +simpler logic and only create the root debugfs directory once when the +driver is first accessed. That resolves the memory leak and makes +things more obvious as to what the intent is. + +Cc: Marcin Wojtas +Cc: Russell King +Cc: "David S. Miller" +Cc: Eric Dumazet +Cc: Jakub Kicinski +Cc: Paolo Abeni +Cc: netdev@vger.kernel.org +Cc: stable +Fixes: 21da57a23125 ("net: mvpp2: add a debugfs interface for the Header Parser") +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c +index 4a3baa7e0142..0eec05d905eb 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c +@@ -700,10 +700,10 @@ void mvpp2_dbgfs_cleanup(struct mvpp2 *priv) + + void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name) + { +- struct dentry *mvpp2_dir, *mvpp2_root; ++ static struct dentry *mvpp2_root; ++ struct dentry *mvpp2_dir; + int ret, i; + +- mvpp2_root = debugfs_lookup(MVPP2_DRIVER_NAME, NULL); + if (!mvpp2_root) + mvpp2_root = debugfs_create_dir(MVPP2_DRIVER_NAME, NULL); + +-- +2.35.1 + diff --git a/queue-5.10/revert-usb-add-quirks-for-lenovo-onelink-dock.patch b/queue-5.10/revert-usb-add-quirks-for-lenovo-onelink-dock.patch new file mode 100644 index 00000000000..43bd9071b86 --- /dev/null +++ b/queue-5.10/revert-usb-add-quirks-for-lenovo-onelink-dock.patch @@ -0,0 +1,43 @@ +From ddc33421fc9a0297e87a5dba53c6ce0f1563702e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 31 Aug 2022 10:34:25 +0200 +Subject: Revert "usb: add quirks for Lenovo OneLink+ Dock" + +From: Greg Kroah-Hartman + +[ Upstream commit 58bfe7d8e31014d7ce246788df99c56e3cfe6c68 ] + +This reverts commit 3d5f70949f1b1168fbb17d06eb5c57e984c56c58. + +The quirk does not work properly, more work is needed to determine what +should be done here. + +Reported-by: Oliver Neukum +Cc: Jean-Francois Le Fillatre +Cc: stable +Fixes: 3d5f70949f1b ("usb: add quirks for Lenovo OneLink+ Dock") +Link: https://lore.kernel.org/r/9a17ea86-079f-510d-e919-01bc53a6d09f@gmx.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/core/quirks.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c +index 03473e20e218..f03ee889ecc7 100644 +--- a/drivers/usb/core/quirks.c ++++ b/drivers/usb/core/quirks.c +@@ -438,10 +438,6 @@ static const struct usb_device_id usb_quirk_list[] = { + { USB_DEVICE(0x1532, 0x0116), .driver_info = + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + +- /* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */ +- { USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME }, +- { USB_DEVICE(0x17ef, 0x1019), .driver_info = USB_QUIRK_RESET_RESUME }, +- + /* Lenovo USB-C to Ethernet Adapter RTL8153-04 */ + { USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM }, + +-- +2.35.1 + diff --git a/queue-5.10/revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch b/queue-5.10/revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch new file mode 100644 index 00000000000..45f5ffa832f --- /dev/null +++ b/queue-5.10/revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch @@ -0,0 +1,96 @@ +From 8f70c73bee649ed1bdf917c5138df3a954681d76 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 2 Sep 2022 09:10:08 +0200 +Subject: Revert "usb: gadget: udc-xilinx: replace memcpy with memcpy_toio" + +From: Greg Kroah-Hartman + +[ Upstream commit fe0a2ac7c627b064c479ad0c3b25e531d342e048 ] + +This reverts commit 8cb339f1c1f04baede9d54c1e40ac96247a6393b as it +throws up a bunch of sparse warnings as reported by the kernel test +robot. + +Reported-by: kernel test robot +Link: https://lore.kernel.org/r/202209020044.CX2PfZzM-lkp@intel.com +Fixes: 8cb339f1c1f0 ("usb: gadget: udc-xilinx: replace memcpy with memcpy_toio") +Cc: stable@vger.kernel.org +Cc: Linus Walleij +Cc: Piyush Mehta +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/gadget/udc/udc-xilinx.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c +index 01133dc42340..096f56a09e6a 100644 +--- a/drivers/usb/gadget/udc/udc-xilinx.c ++++ b/drivers/usb/gadget/udc/udc-xilinx.c +@@ -496,11 +496,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req, + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + if (ep->is_in) { +- memcpy_toio(eprambase, bufferptr, bytestosend); ++ memcpy(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF0COUNT_OFFSET, bufferlen); + } else { +- memcpy_toio(bufferptr, eprambase, bytestosend); ++ memcpy(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. +@@ -514,11 +514,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req, + eprambase = (u32 __force *)(udc->addr + ep->rambase + + ep->ep_usb.maxpacket); + if (ep->is_in) { +- memcpy_toio(eprambase, bufferptr, bytestosend); ++ memcpy(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF1COUNT_OFFSET, bufferlen); + } else { +- memcpy_toio(bufferptr, eprambase, bytestosend); ++ memcpy(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. +@@ -1020,7 +1020,7 @@ static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req) + udc->addr); + length = req->usb_req.actual = min_t(u32, length, + EP0_MAX_PACKET); +- memcpy_toio(corebuf, req->usb_req.buf, length); ++ memcpy(corebuf, req->usb_req.buf, length); + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } else { +@@ -1746,7 +1746,7 @@ static void xudc_handle_setup(struct xusb_udc *udc) + + /* Load up the chapter 9 command buffer.*/ + ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET); +- memcpy_toio(&setup, ep0rambase, 8); ++ memcpy(&setup, ep0rambase, 8); + + udc->setup = setup; + udc->setup.wValue = cpu_to_le16(setup.wValue); +@@ -1833,7 +1833,7 @@ static void xudc_ep0_out(struct xusb_udc *udc) + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + bytes_to_rx; +- memcpy_toio(buffer, ep0rambase, bytes_to_rx); ++ memcpy(buffer, ep0rambase, bytes_to_rx); + + if (req->usb_req.length == req->usb_req.actual) { + /* Data transfer completed get ready for Status stage */ +@@ -1909,7 +1909,7 @@ static void xudc_ep0_in(struct xusb_udc *udc) + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + length; +- memcpy_toio(ep0rambase, buffer, length); ++ memcpy(ep0rambase, buffer, length); + } + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); +-- +2.35.1 + diff --git a/queue-5.10/serial-atmel-remove-redundant-assignment-in-rs485_co.patch b/queue-5.10/serial-atmel-remove-redundant-assignment-in-rs485_co.patch new file mode 100644 index 00000000000..c5de29c1e5e --- /dev/null +++ b/queue-5.10/serial-atmel-remove-redundant-assignment-in-rs485_co.patch @@ -0,0 +1,46 @@ +From eda7287e4c786ddcac4f5ed85b6ffc0eb8e2d748 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 Apr 2022 12:46:42 +0200 +Subject: serial: atmel: remove redundant assignment in rs485_config + +From: Lino Sanfilippo + +[ Upstream commit 60efd0513916f195dd85bfbf21653f74f9ab019c ] + +In uart_set_rs485_config() the serial core already assigns the passed +serial_rs485 struct to the uart port. + +So remove the assignment from the drivers rs485_config() function to avoid +redundancy. + +Reviewed-by: Claudiu Beznea +Acked-by: Richard Genoud +Signed-off-by: Lino Sanfilippo +Link: https://lore.kernel.org/r/20220410104642.32195-10-LinoSanfilippo@gmx.de +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 692a8ebcfc24 ("tty: serial: atmel: Preserve previous USART mode if RS485 disabled") +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/atmel_serial.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c +index 602065bfc9bb..e7526060926d 100644 +--- a/drivers/tty/serial/atmel_serial.c ++++ b/drivers/tty/serial/atmel_serial.c +@@ -298,11 +298,9 @@ static int atmel_config_rs485(struct uart_port *port, + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + +- port->rs485 = *rs485conf; +- + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); +- if (port->rs485.flags & SER_RS485_RX_DURING_TX) ++ if (rs485conf->flags & SER_RS485_RX_DURING_TX) + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + else + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; +-- +2.35.1 + diff --git a/queue-5.10/series b/queue-5.10/series new file mode 100644 index 00000000000..3817250dea0 --- /dev/null +++ b/queue-5.10/series @@ -0,0 +1,40 @@ +drm-amdgpu-move-nbio-sdma_doorbell_range-into-sdma-c.patch +drm-amdgpu-indirect-register-access-for-nv12-sriov.patch +drm-amdgpu-separate-vf2pf-work-item-init-from-virt-d.patch +drm-amdgpu-make-sure-to-init-common-ip-before-gmc.patch +staging-r8188eu-introduce-new-os_dep-dir-for-rtl8188.patch +staging-r8188eu-add-firmware-dependency.patch +staging-r8188eu-remove-support-for-devices-with-8188.patch +staging-r8188eu-add-rosewill-usb-n150-nano-to-device.patch +usb-typec-intel_pmc_mux-update-iom-port-status-offse.patch +usb-typec-intel_pmc_mux-add-new-acpi-id-for-meteor-l.patch +usb-dwc3-gadget-avoid-starting-dwc3-gadget-during-ud.patch +usb-dwc3-issue-core-soft-reset-before-enabling-run-s.patch +usb-dwc3-gadget-prevent-repeat-pullup.patch +usb-dwc3-gadget-refactor-pullup.patch +usb-dwc3-gadget-don-t-modify-gevntcount-in-pullup.patch +usb-dwc3-gadget-avoid-duplicate-requests-to-enable-r.patch +usb-xhci-mtk-get-the-microframe-boundary-for-esit.patch +usb-xhci-mtk-add-only-one-extra-cs-for-fs-ls-intr.patch +usb-xhci-mtk-use-sch_tt-to-check-whether-need-do-tt-.patch +usb-xhci-mtk-add-a-function-to-un-load-bandwidth-inf.patch +usb-xhci-mtk-add-some-schedule-error-number.patch +usb-xhci-mtk-allow-multiple-start-split-in-a-microfr.patch +usb-xhci-mtk-relax-tt-periodic-bandwidth-allocation.patch +iio-adc-mcp3911-switch-to-generic-firmware-propertie.patch +iio-adc-mcp3911-correct-microchip-device-addr-proper.patch +mmc-core-fix-inconsistent-sd3_bus_mode-at-uhs-i-sd-v.patch +serial-atmel-remove-redundant-assignment-in-rs485_co.patch +tty-serial-atmel-preserve-previous-usart-mode-if-rs4.patch +usb-add-quirks-for-lenovo-onelink-dock.patch +usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch +usb-cdns3-fix-incorrect-handling-trb_smm-flag-for-is.patch +usb-cdns3-fix-issue-with-rearming-iso-out-endpoint.patch +revert-usb-add-quirks-for-lenovo-onelink-dock.patch +vfio-type1-change-success-value-of-vaddr_get_pfn.patch +vfio-type1-prepare-for-batched-pinning-with-struct-v.patch +vfio-type1-unpin-zero-pages.patch +revert-usb-gadget-udc-xilinx-replace-memcpy-with-mem.patch +net-mvpp2-debugfs-fix-memory-leak-when-using-debugfs.patch +arm64-restrict-arm64_bti_kernel-to-clang-12.0.0-and-.patch +arm64-bti-disable-in-kernel-bti-when-cross-section-t.patch diff --git a/queue-5.10/staging-r8188eu-add-firmware-dependency.patch b/queue-5.10/staging-r8188eu-add-firmware-dependency.patch new file mode 100644 index 00000000000..861da9e4db8 --- /dev/null +++ b/queue-5.10/staging-r8188eu-add-firmware-dependency.patch @@ -0,0 +1,54 @@ +From 9eee5ea6a8786f5a90bd37f0cb89f0cd8fe2d2bd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Aug 2022 19:18:44 +0200 +Subject: staging: r8188eu: add firmware dependency + +From: Grzegorz Szymaszek + +[ Upstream commit b2fa9e13bbf101c662c4cd974608242a0db98cfc ] + +The old rtl8188eu module, removed in commit 55dfa29b43d2 ("staging: +rtl8188eu: remove rtl8188eu driver from staging dir") (Linux kernel +v5.15-rc1), required (through a MODULE_FIRMWARE call()) the +rtlwifi/rtl8188eufw.bin firmware file, which the new r8188eu driver no +longer requires. + +I have tested a few RTL8188EUS-based Wi-Fi cards and, while supported by +both drivers, they do not work when using the new one and the firmware +wasn't manually loaded. According to Larry Finger, the module +maintainer, all such cards need the firmware and the driver should +depend on it (see the linked mails). + +Add a proper MODULE_FIRMWARE() call, like it was done in the old driver. + +Thanks to Greg Kroah-Hartman and Larry Finger for quick responses to my +questions. + +Cc: stable +Link: https://answers.launchpad.net/ubuntu/+source/linux-meta-hwe-5.15/+question/702611 +Link: https://lore.kernel.org/lkml/YukkBu3TNODO3or9@nx64de-df6d00/ +Signed-off-by: Grzegorz Szymaszek +Link: https://lore.kernel.org/r/YulcdKfhA8dPQ78s@nx64de-df6d00 +Acked-by: Phillip Potter +Acked-by: Larry Finger +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/staging/r8188eu/os_dep/os_intfs.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/staging/r8188eu/os_dep/os_intfs.c b/drivers/staging/r8188eu/os_dep/os_intfs.c +index 8c8ef9063b0c..ce128866e3fd 100644 +--- a/drivers/staging/r8188eu/os_dep/os_intfs.c ++++ b/drivers/staging/r8188eu/os_dep/os_intfs.c +@@ -37,6 +37,7 @@ MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("Realtek Wireless Lan Driver"); + MODULE_AUTHOR("Realtek Semiconductor Corp."); + MODULE_VERSION(DRIVERVERSION); ++MODULE_FIRMWARE("rtlwifi/rtl8188eufw.bin"); + + #define CONFIG_BR_EXT_BRNAME "br0" + #define RTW_NOTCH_FILTER 0 /* 0:Disable, 1:Enable, */ +-- +2.35.1 + diff --git a/queue-5.10/staging-r8188eu-add-rosewill-usb-n150-nano-to-device.patch b/queue-5.10/staging-r8188eu-add-rosewill-usb-n150-nano-to-device.patch new file mode 100644 index 00000000000..cd59807ddb3 --- /dev/null +++ b/queue-5.10/staging-r8188eu-add-rosewill-usb-n150-nano-to-device.patch @@ -0,0 +1,38 @@ +From 1cf5307f0e51b8f77e9a17ffc78117bad1789260 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 14 Aug 2022 12:50:27 -0500 +Subject: staging: r8188eu: Add Rosewill USB-N150 Nano to device tables + +From: Larry Finger + +[ Upstream commit e01f5c8d6af231b3b09e23c1fe8a4057cdcc4e42 ] + +This device is reported as using the RTL8188EUS chip. + +It has the improbable USB ID of 0bda:ffef, which normally would belong +to Realtek, but this ID works for the reporter. + +Signed-off-by: Larry Finger +Cc: stable +Link: https://lore.kernel.org/r/20220814175027.2689-1-Larry.Finger@lwfinger.net +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/staging/r8188eu/os_dep/usb_intf.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/staging/r8188eu/os_dep/usb_intf.c b/drivers/staging/r8188eu/os_dep/usb_intf.c +index f708c902c819..3b319c285d13 100644 +--- a/drivers/staging/r8188eu/os_dep/usb_intf.c ++++ b/drivers/staging/r8188eu/os_dep/usb_intf.c +@@ -49,6 +49,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = { + /*=== Realtek demoboard ===*/ + {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8179)}, /* 8188EUS */ + {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x0179)}, /* 8188ETV */ ++ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0xffef)}, /* Rosewill USB-N150 Nano */ + /*=== Customer ID ===*/ + /****** 8188EUS ********/ + {USB_DEVICE(0x07B8, 0x8179)}, /* Abocom - Abocom */ +-- +2.35.1 + diff --git a/queue-5.10/staging-r8188eu-introduce-new-os_dep-dir-for-rtl8188.patch b/queue-5.10/staging-r8188eu-introduce-new-os_dep-dir-for-rtl8188.patch new file mode 100644 index 00000000000..42d9d7b033c --- /dev/null +++ b/queue-5.10/staging-r8188eu-introduce-new-os_dep-dir-for-rtl8188.patch @@ -0,0 +1,12397 @@ +From edd8187237414687007d435ef8ff539862c22acc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 28 Jul 2021 00:22:16 +0100 +Subject: staging: r8188eu: introduce new os_dep dir for RTL8188eu driver + +From: Phillip Potter + +[ Upstream commit 2b42bd58b32155a1be4dd78991845dec05aaef9e ] + +This patchset is split in order to keep the file sizes down. This os_dep +directory is part of the newer/better driver from GitHub modified by +Larry Finger. Import this as the basis for all future work going +forward. + +Suggested-by: Larry Finger +Signed-off-by: Phillip Potter +Link: https://lore.kernel.org/r/20210727232219.2948-4-phil@philpotter.co.uk +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: b2fa9e13bbf1 ("staging: r8188eu: add firmware dependency") +Signed-off-by: Sasha Levin +--- + drivers/staging/r8188eu/os_dep/ioctl_linux.c | 8178 +++++++++++++++++ + drivers/staging/r8188eu/os_dep/mlme_linux.c | 302 + + drivers/staging/r8188eu/os_dep/os_intfs.c | 1283 +++ + .../staging/r8188eu/os_dep/osdep_service.c | 535 ++ + drivers/staging/r8188eu/os_dep/recv_linux.c | 270 + + drivers/staging/r8188eu/os_dep/rtw_android.c | 303 + + drivers/staging/r8188eu/os_dep/usb_intf.c | 863 ++ + .../staging/r8188eu/os_dep/usb_ops_linux.c | 283 + + drivers/staging/r8188eu/os_dep/xmit_linux.c | 282 + + 9 files changed, 12299 insertions(+) + create mode 100644 drivers/staging/r8188eu/os_dep/ioctl_linux.c + create mode 100644 drivers/staging/r8188eu/os_dep/mlme_linux.c + create mode 100644 drivers/staging/r8188eu/os_dep/os_intfs.c + create mode 100644 drivers/staging/r8188eu/os_dep/osdep_service.c + create mode 100644 drivers/staging/r8188eu/os_dep/recv_linux.c + create mode 100644 drivers/staging/r8188eu/os_dep/rtw_android.c + create mode 100644 drivers/staging/r8188eu/os_dep/usb_intf.c + create mode 100644 drivers/staging/r8188eu/os_dep/usb_ops_linux.c + create mode 100644 drivers/staging/r8188eu/os_dep/xmit_linux.c + +diff --git a/drivers/staging/r8188eu/os_dep/ioctl_linux.c b/drivers/staging/r8188eu/os_dep/ioctl_linux.c +new file mode 100644 +index 000000000000..faced0d0af1d +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/ioctl_linux.c +@@ -0,0 +1,8178 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++#define _IOCTL_LINUX_C_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define RTL_IOCTL_WPA_SUPPLICANT (SIOCIWFIRSTPRIV + 30) ++ ++#define SCAN_ITEM_SIZE 768 ++#define MAX_CUSTOM_LEN 64 ++#define RATE_COUNT 4 ++ ++/* combo scan */ ++#define WEXT_CSCAN_AMOUNT 9 ++#define WEXT_CSCAN_BUF_LEN 360 ++#define WEXT_CSCAN_HEADER "CSCAN S\x01\x00\x00S\x00" ++#define WEXT_CSCAN_HEADER_SIZE 12 ++#define WEXT_CSCAN_SSID_SECTION 'S' ++#define WEXT_CSCAN_CHANNEL_SECTION 'C' ++#define WEXT_CSCAN_NPROBE_SECTION 'N' ++#define WEXT_CSCAN_ACTV_DWELL_SECTION 'A' ++#define WEXT_CSCAN_PASV_DWELL_SECTION 'P' ++#define WEXT_CSCAN_HOME_DWELL_SECTION 'H' ++#define WEXT_CSCAN_TYPE_SECTION 'T' ++ ++static struct mp_ioctl_handler mp_ioctl_hdl[] = { ++/*0*/ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_start_test_hdl, OID_RT_PRO_START_TEST) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_stop_test_hdl, OID_RT_PRO_STOP_TEST) ++ ++ GEN_HANDLER(sizeof(struct rwreg_param), rtl8188eu_oid_rt_pro_read_register_hdl, OID_RT_PRO_READ_REGISTER) ++ GEN_HANDLER(sizeof(struct rwreg_param), rtl8188eu_oid_rt_pro_write_register_hdl, OID_RT_PRO_WRITE_REGISTER) ++ GEN_HANDLER(sizeof(struct bb_reg_param), rtl8188eu_oid_rt_pro_read_bb_reg_hdl, OID_RT_PRO_READ_BB_REG) ++/*5*/ GEN_HANDLER(sizeof(struct bb_reg_param), rtl8188eu_oid_rt_pro_write_bb_reg_hdl, OID_RT_PRO_WRITE_BB_REG) ++ GEN_HANDLER(sizeof(struct rf_reg_param), rtl8188eu_oid_rt_pro_read_rf_reg_hdl, OID_RT_PRO_RF_READ_REGISTRY) ++ GEN_HANDLER(sizeof(struct rf_reg_param), rtl8188eu_oid_rt_pro_write_rf_reg_hdl, OID_RT_PRO_RF_WRITE_REGISTRY) ++ ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_set_channel_direct_call_hdl, OID_RT_PRO_SET_CHANNEL_DIRECT_CALL) ++ GEN_HANDLER(sizeof(struct txpower_param), rtl8188eu_oid_rt_pro_set_tx_power_control_hdl, OID_RT_PRO_SET_TX_POWER_CONTROL) ++/*10*/ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_set_data_rate_hdl, OID_RT_PRO_SET_DATA_RATE) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_set_bandwidth_hdl, OID_RT_SET_BANDWIDTH) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_set_antenna_bb_hdl, OID_RT_PRO_SET_ANTENNA_BB) ++ ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_set_continuous_tx_hdl, OID_RT_PRO_SET_CONTINUOUS_TX) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_set_single_carrier_tx_hdl, OID_RT_PRO_SET_SINGLE_CARRIER_TX) ++/*15*/ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_set_carrier_suppression_tx_hdl, OID_RT_PRO_SET_CARRIER_SUPPRESSION_TX) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_pro_set_single_tone_tx_hdl, OID_RT_PRO_SET_SINGLE_TONE_TX) ++ ++ EXT_MP_IOCTL_HANDLER(0, xmit_packet, 0) ++ ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_set_rx_packet_type_hdl, OID_RT_SET_RX_PACKET_TYPE) ++ GEN_HANDLER(0, rtl8188eu_oid_rt_reset_phy_rx_packet_count_hdl, OID_RT_RESET_PHY_RX_PACKET_COUNT) ++/*20*/ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_get_phy_rx_packet_received_hdl, OID_RT_GET_PHY_RX_PACKET_RECEIVED) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_get_phy_rx_packet_crc32_error_hdl, OID_RT_GET_PHY_RX_PACKET_CRC32_ERROR) ++ ++ GEN_HANDLER(sizeof(struct eeprom_rw_param), NULL, 0) ++ GEN_HANDLER(sizeof(struct eeprom_rw_param), NULL, 0) ++ GEN_HANDLER(sizeof(struct efuse_access_struct), rtl8188eu_oid_rt_pro_efuse_hdl, OID_RT_PRO_EFUSE) ++/*25*/ GEN_HANDLER(0, rtl8188eu_oid_rt_pro_efuse_map_hdl, OID_RT_PRO_EFUSE_MAP) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_get_efuse_max_size_hdl, OID_RT_GET_EFUSE_MAX_SIZE) ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_get_efuse_current_size_hdl, OID_RT_GET_EFUSE_CURRENT_SIZE) ++ ++ GEN_HANDLER(sizeof(u32), rtl8188eu_oid_rt_get_thermal_meter_hdl, OID_RT_PRO_GET_THERMAL_METER) ++ GEN_HANDLER(sizeof(u8), rtl8188eu_oid_rt_pro_set_power_tracking_hdl, OID_RT_PRO_SET_POWER_TRACKING) ++/*30*/ GEN_HANDLER(sizeof(u8), rtl8188eu_oid_rt_set_power_down_hdl, OID_RT_SET_POWER_DOWN) ++/*31*/ GEN_HANDLER(0, rtl8188eu_oid_rt_pro_trigger_gpio_hdl, 0) ++}; ++ ++static u32 rtw_rates[] = {1000000, 2000000, 5500000, 11000000, ++ 6000000, 9000000, 12000000, 18000000, 24000000, 36000000, ++ 48000000, 54000000}; ++ ++static const char * const iw_operation_mode[] = { ++ "Auto", "Ad-Hoc", "Managed", "Master", "Repeater", ++ "Secondary", "Monitor" ++}; ++ ++static int hex2num_i(char c) ++{ ++ if (c >= '0' && c <= '9') ++ return c - '0'; ++ if (c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if (c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return -1; ++} ++ ++/** ++ * hwaddr_aton - Convert ASCII string to MAC address ++ * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") ++ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) ++ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) ++ */ ++static int hwaddr_aton_i(const char *txt, u8 *addr) ++{ ++ int i; ++ ++ for (i = 0; i < 6; i++) { ++ int a, b; ++ ++ a = hex2num_i(*txt++); ++ if (a < 0) ++ return -1; ++ b = hex2num_i(*txt++); ++ if (b < 0) ++ return -1; ++ *addr++ = (a << 4) | b; ++ if (i < 5 && *txt++ != ':') ++ return -1; ++ } ++ ++ return 0; ++} ++ ++void indicate_wx_scan_complete_event(struct adapter *padapter) ++{ ++ union iwreq_data wrqu; ++ ++ memset(&wrqu, 0, sizeof(union iwreq_data)); ++ wireless_send_event(padapter->pnetdev, SIOCGIWSCAN, &wrqu, NULL); ++} ++ ++void rtw_indicate_wx_assoc_event(struct adapter *padapter) ++{ ++ union iwreq_data wrqu; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ ++ memset(&wrqu, 0, sizeof(union iwreq_data)); ++ ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ ++ memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress, ETH_ALEN); ++ ++ DBG_88E_LEVEL(_drv_always_, "assoc success\n"); ++ wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL); ++} ++ ++void rtw_indicate_wx_disassoc_event(struct adapter *padapter) ++{ ++ union iwreq_data wrqu; ++ ++ memset(&wrqu, 0, sizeof(union iwreq_data)); ++ ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); ++ ++ DBG_88E_LEVEL(_drv_always_, "indicate disassoc\n"); ++ wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL); ++} ++ ++static char *translate_scan(struct adapter *padapter, ++ struct iw_request_info *info, ++ struct wlan_network *pnetwork, ++ char *start, char *stop) ++{ ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct iw_event iwe; ++ u16 cap; ++ __le16 le_tmp; ++ u32 ht_ielen = 0; ++ char *custom; ++ char *p; ++ u16 max_rate = 0, rate, ht_cap = false; ++ u32 i = 0; ++ u8 bw_40MHz = 0, short_GI = 0; ++ u16 mcs_rate = 0; ++ u8 ss, sq; ++#ifdef CONFIG_88EU_P2P ++ struct wifidirect_info *pwdinfo = &padapter->wdinfo; ++ ++ if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) { ++ u32 blnGotP2PIE = false; ++ ++ /* User is doing the P2P device discovery */ ++ /* The prefix of SSID should be "DIRECT-" and the IE should contains the P2P IE. */ ++ /* If not, the driver should ignore this AP and go to the next AP. */ ++ ++ /* Verifying the SSID */ ++ if (!memcmp(pnetwork->network.Ssid.Ssid, pwdinfo->p2p_wildcard_ssid, P2P_WILDCARD_SSID_LEN)) { ++ u32 p2pielen = 0; ++ ++ if (pnetwork->network.Reserved[0] == 2) {/* Probe Request */ ++ /* Verifying the P2P IE */ ++ if (rtw_get_p2p_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &p2pielen)) ++ blnGotP2PIE = true; ++ } else {/* Beacon or Probe Respones */ ++ /* Verifying the P2P IE */ ++ if (rtw_get_p2p_ie(&pnetwork->network.IEs[12], pnetwork->network.IELength - 12, NULL, &p2pielen)) ++ blnGotP2PIE = true; ++ } ++ } ++ ++ if (!blnGotP2PIE) ++ return start; ++ } ++#endif /* CONFIG_88EU_P2P */ ++ ++ /* AP MAC address */ ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ ++ memcpy(iwe.u.ap_addr.sa_data, pnetwork->network.MacAddress, ETH_ALEN); ++ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); ++ ++ /* Add the ESSID */ ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.flags = 1; ++ iwe.u.data.length = min_t(u16, pnetwork->network.Ssid.SsidLength, 32); ++ start = iwe_stream_add_point(info, start, stop, &iwe, pnetwork->network.Ssid.Ssid); ++ ++ /* parsing HT_CAP_IE */ ++ p = rtw_get_ie(&pnetwork->network.IEs[12], _HT_CAPABILITY_IE_, &ht_ielen, pnetwork->network.IELength-12); ++ ++ if (p && ht_ielen > 0) { ++ struct ieee80211_ht_cap *pht_capie; ++ ++ ht_cap = true; ++ pht_capie = (struct ieee80211_ht_cap *)(p+2); ++ memcpy(&mcs_rate, pht_capie->mcs.rx_mask, 2); ++ bw_40MHz = (le16_to_cpu(pht_capie->cap_info) & ++ IEEE80211_HT_CAP_SUP_WIDTH) ? 1 : 0; ++ short_GI = (le16_to_cpu(pht_capie->cap_info) & ++ (IEEE80211_HT_CAP_SGI_20|IEEE80211_HT_CAP_SGI_40)) ? 1 : 0; ++ } ++ ++ /* Add the protocol name */ ++ iwe.cmd = SIOCGIWNAME; ++ if ((rtw_is_cckratesonly_included((u8 *)&pnetwork->network.SupportedRates))) { ++ if (ht_cap) ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bn"); ++ else ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b"); ++ } else if ((rtw_is_cckrates_included((u8 *)&pnetwork->network.SupportedRates))) { ++ if (ht_cap) ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bgn"); ++ else ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg"); ++ } else { ++ if (pnetwork->network.Configuration.DSConfig > 14) { ++ if (ht_cap) ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11an"); ++ else ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11a"); ++ } else { ++ if (ht_cap) ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11gn"); ++ else ++ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11g"); ++ } ++ } ++ ++ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); ++ ++ /* Add mode */ ++ iwe.cmd = SIOCGIWMODE; ++ memcpy(&le_tmp, rtw_get_capability_from_ie(pnetwork->network.IEs), 2); ++ ++ cap = le16_to_cpu(le_tmp); ++ ++ if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_BSS)) { ++ if (cap & WLAN_CAPABILITY_BSS) ++ iwe.u.mode = IW_MODE_MASTER; ++ else ++ iwe.u.mode = IW_MODE_ADHOC; ++ ++ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); ++ } ++ ++ if (pnetwork->network.Configuration.DSConfig < 1) ++ pnetwork->network.Configuration.DSConfig = 1; ++ ++ /* Add frequency/channel */ ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = rtw_ch2freq(pnetwork->network.Configuration.DSConfig) * 100000; ++ iwe.u.freq.e = 1; ++ iwe.u.freq.i = pnetwork->network.Configuration.DSConfig; ++ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); ++ ++ /* Add encryption capability */ ++ iwe.cmd = SIOCGIWENCODE; ++ if (cap & WLAN_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; ++ start = iwe_stream_add_point(info, start, stop, &iwe, pnetwork->network.Ssid.Ssid); ++ ++ /*Add basic and extended rates */ ++ max_rate = 0; ++ custom = kzalloc(MAX_CUSTOM_LEN, GFP_ATOMIC); ++ if (!custom) ++ return start; ++ p = custom; ++ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); ++ while (pnetwork->network.SupportedRates[i] != 0) { ++ rate = pnetwork->network.SupportedRates[i]&0x7F; ++ if (rate > max_rate) ++ max_rate = rate; ++ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), ++ "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); ++ i++; ++ } ++ ++ if (ht_cap) { ++ if (mcs_rate&0x8000)/* MCS15 */ ++ max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130); ++ else if (mcs_rate&0x0080)/* MCS7 */ ++ ; ++ else/* default MCS7 */ ++ max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65); ++ ++ max_rate = max_rate*2;/* Mbps/2; */ ++ } ++ ++ iwe.cmd = SIOCGIWRATE; ++ iwe.u.bitrate.fixed = 0; ++ iwe.u.bitrate.disabled = 0; ++ iwe.u.bitrate.value = max_rate * 500000; ++ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_PARAM_LEN); ++ ++ /* parsing WPA/WPA2 IE */ ++ { ++ u8 *buf; ++ u8 *wpa_ie, *rsn_ie; ++ u16 wpa_len = 0, rsn_len = 0; ++ u8 *p; ++ ++ buf = kzalloc(MAX_WPA_IE_LEN, GFP_ATOMIC); ++ if (!buf) ++ goto exit; ++ wpa_ie = kzalloc(255, GFP_ATOMIC); ++ if (!wpa_ie) { ++ kfree(buf); ++ goto exit; ++ } ++ rsn_ie = kzalloc(255, GFP_ATOMIC); ++ if (!rsn_ie) { ++ kfree(buf); ++ kfree(wpa_ie); ++ goto exit; ++ } ++ rtw_get_sec_ie(pnetwork->network.IEs, pnetwork->network.IELength, rsn_ie, &rsn_len, wpa_ie, &wpa_len); ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan: ssid =%s\n", pnetwork->network.Ssid.Ssid)); ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len)); ++ ++ if (wpa_len > 0) { ++ p = buf; ++ memset(buf, 0, MAX_WPA_IE_LEN); ++ p += sprintf(p, "wpa_ie ="); ++ for (i = 0; i < wpa_len; i++) ++ p += sprintf(p, "%02x", wpa_ie[i]); ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ iwe.u.data.length = strlen(buf); ++ start = iwe_stream_add_point(info, start, stop, &iwe, buf); ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = wpa_len; ++ start = iwe_stream_add_point(info, start, stop, &iwe, wpa_ie); ++ } ++ if (rsn_len > 0) { ++ p = buf; ++ memset(buf, 0, MAX_WPA_IE_LEN); ++ p += sprintf(p, "rsn_ie ="); ++ for (i = 0; i < rsn_len; i++) ++ p += sprintf(p, "%02x", rsn_ie[i]); ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ iwe.u.data.length = strlen(buf); ++ start = iwe_stream_add_point(info, start, stop, &iwe, buf); ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = rsn_len; ++ start = iwe_stream_add_point(info, start, stop, &iwe, rsn_ie); ++ } ++ kfree(buf); ++ kfree(wpa_ie); ++ kfree(rsn_ie); ++ } ++ ++ {/* parsing WPS IE */ ++ uint cnt = 0, total_ielen; ++ u8 *wpsie_ptr = NULL; ++ uint wps_ielen = 0; ++ ++ u8 *ie_ptr = pnetwork->network.IEs + _FIXED_IE_LENGTH_; ++ total_ielen = pnetwork->network.IELength - _FIXED_IE_LENGTH_; ++ ++ while (cnt < total_ielen) { ++ if (rtw_is_wps_ie(&ie_ptr[cnt], &wps_ielen) && (wps_ielen > 2)) { ++ wpsie_ptr = &ie_ptr[cnt]; ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = (u16)wps_ielen; ++ start = iwe_stream_add_point(info, start, stop, &iwe, wpsie_ptr); ++ } ++ cnt += ie_ptr[cnt+1]+2; /* goto next */ ++ } ++ } ++ ++ /* Add quality statistics */ ++ iwe.cmd = IWEVQUAL; ++ iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID; ++ ++ if (check_fwstate(pmlmepriv, _FW_LINKED) == true && ++ is_same_network(&pmlmepriv->cur_network.network, &pnetwork->network)) { ++ ss = padapter->recvpriv.signal_strength; ++ sq = padapter->recvpriv.signal_qual; ++ } else { ++ ss = pnetwork->network.PhyInfo.SignalStrength; ++ sq = pnetwork->network.PhyInfo.SignalQuality; ++ } ++ ++ iwe.u.qual.level = (u8)ss; ++ iwe.u.qual.qual = (u8)sq; /* signal quality */ ++ iwe.u.qual.noise = 0; /* noise level */ ++ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); ++exit: ++ kfree(custom); ++ return start; ++} ++ ++static int wpa_set_auth_algs(struct net_device *dev, u32 value) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ int ret = 0; ++ ++ if ((value & AUTH_ALG_SHARED_KEY) && (value & AUTH_ALG_OPEN_SYSTEM)) { ++ DBG_88E("wpa_set_auth_algs, AUTH_ALG_SHARED_KEY and AUTH_ALG_OPEN_SYSTEM [value:0x%x]\n", value); ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeAutoSwitch; ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Auto; ++ } else if (value & AUTH_ALG_SHARED_KEY) { ++ DBG_88E("wpa_set_auth_algs, AUTH_ALG_SHARED_KEY [value:0x%x]\n", value); ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeShared; ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Shared; ++ } else if (value & AUTH_ALG_OPEN_SYSTEM) { ++ DBG_88E("wpa_set_auth_algs, AUTH_ALG_OPEN_SYSTEM\n"); ++ if (padapter->securitypriv.ndisauthtype < Ndis802_11AuthModeWPAPSK) { ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen; ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; ++ } ++ } else if (value & AUTH_ALG_LEAP) { ++ DBG_88E("wpa_set_auth_algs, AUTH_ALG_LEAP\n"); ++ } else { ++ DBG_88E("wpa_set_auth_algs, error!\n"); ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, u32 param_len) ++{ ++ int ret = 0; ++ u32 wep_key_idx, wep_key_len, wep_total_len; ++ struct ndis_802_11_wep *pwep = NULL; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct security_priv *psecuritypriv = &padapter->securitypriv; ++#ifdef CONFIG_88EU_P2P ++ struct wifidirect_info *pwdinfo = &padapter->wdinfo; ++#endif /* CONFIG_88EU_P2P */ ++ ++ param->u.crypt.err = 0; ++ param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0'; ++ ++ if (param_len < (u32) ((u8 *)param->u.crypt.key - (u8 *)param) + param->u.crypt.key_len) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { ++ if (param->u.crypt.idx >= WEP_KEYS) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ } else { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ if (strcmp(param->u.crypt.alg, "WEP") == 0) { ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("wpa_set_encryption, crypt.alg = WEP\n")); ++ DBG_88E("wpa_set_encryption, crypt.alg = WEP\n"); ++ ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_; ++ padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_; ++ ++ wep_key_idx = param->u.crypt.idx; ++ wep_key_len = param->u.crypt.key_len; ++ ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("(1)wep_key_idx =%d\n", wep_key_idx)); ++ DBG_88E("(1)wep_key_idx =%d\n", wep_key_idx); ++ ++ if (wep_key_idx > WEP_KEYS) ++ return -EINVAL; ++ ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("(2)wep_key_idx =%d\n", wep_key_idx)); ++ ++ if (wep_key_len > 0) { ++ wep_key_len = wep_key_len <= 5 ? 5 : 13; ++ wep_total_len = wep_key_len + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial); ++ pwep = (struct ndis_802_11_wep *)rtw_malloc(wep_total_len); ++ if (pwep == NULL) { ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, (" wpa_set_encryption: pwep allocate fail !!!\n")); ++ goto exit; ++ } ++ memset(pwep, 0, wep_total_len); ++ pwep->KeyLength = wep_key_len; ++ pwep->Length = wep_total_len; ++ if (wep_key_len == 13) { ++ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_; ++ padapter->securitypriv.dot118021XGrpPrivacy = _WEP104_; ++ } ++ } else { ++ ret = -EINVAL; ++ goto exit; ++ } ++ pwep->KeyIndex = wep_key_idx; ++ pwep->KeyIndex |= 0x80000000; ++ memcpy(pwep->KeyMaterial, param->u.crypt.key, pwep->KeyLength); ++ if (param->u.crypt.set_tx) { ++ DBG_88E("wep, set_tx = 1\n"); ++ if (rtw_set_802_11_add_wep(padapter, pwep) == (u8)_FAIL) ++ ret = -EOPNOTSUPP; ++ } else { ++ DBG_88E("wep, set_tx = 0\n"); ++ if (wep_key_idx >= WEP_KEYS) { ++ ret = -EOPNOTSUPP; ++ goto exit; ++ } ++ memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength); ++ psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength; ++ rtw_set_key(padapter, psecuritypriv, wep_key_idx, 0); ++ } ++ goto exit; ++ } ++ ++ if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) { /* 802_1x */ ++ struct sta_info *psta, *pbcmc_sta; ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ ++ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE)) { /* sta mode */ ++ psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv)); ++ if (psta == NULL) { ++ ; ++ } else { ++ if (strcmp(param->u.crypt.alg, "none") != 0) ++ psta->ieee8021x_blocked = false; ++ ++ if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || ++ (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) ++ psta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; ++ ++ if (param->u.crypt.set_tx == 1) { /* pairwise key */ ++ memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ ++ if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */ ++ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8); ++ memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8); ++ padapter->securitypriv.busetkipkey = false; ++ } ++ ++ DBG_88E(" ~~~~set sta key:unicastkey\n"); ++ ++ rtw_setstakey_cmd(padapter, (unsigned char *)psta, true); ++ } else { /* group key */ ++ memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8); ++ memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8); ++ padapter->securitypriv.binstallGrpkey = true; ++ DBG_88E(" ~~~~set sta key:groupkey\n"); ++ ++ padapter->securitypriv.dot118021XGrpKeyid = param->u.crypt.idx; ++ ++ rtw_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx, 1); ++#ifdef CONFIG_88EU_P2P ++ if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_PROVISIONING_ING)) ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_PROVISIONING_DONE); ++#endif /* CONFIG_88EU_P2P */ ++ } ++ } ++ pbcmc_sta = rtw_get_bcmc_stainfo(padapter); ++ if (pbcmc_sta == NULL) { ++ ; ++ } else { ++ /* Jeff: don't disable ieee8021x_blocked while clearing key */ ++ if (strcmp(param->u.crypt.alg, "none") != 0) ++ pbcmc_sta->ieee8021x_blocked = false; ++ ++ if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || ++ (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) ++ pbcmc_sta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; ++ } ++ } ++ } ++ ++exit: ++ ++ kfree(pwep); ++ ++ return ret; ++} ++ ++static int rtw_set_wpa_ie(struct adapter *padapter, char *pie, unsigned short ielen) ++{ ++ u8 *buf = NULL; ++ int group_cipher = 0, pairwise_cipher = 0; ++ int ret = 0; ++#ifdef CONFIG_88EU_P2P ++ struct wifidirect_info *pwdinfo = &padapter->wdinfo; ++#endif /* CONFIG_88EU_P2P */ ++ ++ if ((ielen > MAX_WPA_IE_LEN) || (pie == NULL)) { ++ _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS); ++ if (pie == NULL) ++ return ret; ++ else ++ return -EINVAL; ++ } ++ ++ if (ielen) { ++ buf = rtw_zmalloc(ielen); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ memcpy(buf, pie, ielen); ++ ++ /* dump */ ++ { ++ int i; ++ DBG_88E("\n wpa_ie(length:%d):\n", ielen); ++ for (i = 0; i < ielen; i += 8) ++ DBG_88E("0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7]); ++ } ++ ++ if (ielen < RSN_HEADER_LEN) { ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("Ie len too short %d\n", ielen)); ++ ret = -1; ++ goto exit; ++ } ++ ++ if (rtw_parse_wpa_ie(buf, ielen, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; ++ memcpy(padapter->securitypriv.supplicant_ie, &buf[0], ielen); ++ } ++ ++ if (rtw_parse_wpa2_ie(buf, ielen, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; ++ memcpy(padapter->securitypriv.supplicant_ie, &buf[0], ielen); ++ } ++ ++ switch (group_cipher) { ++ case WPA_CIPHER_NONE: ++ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; ++ break; ++ case WPA_CIPHER_WEP40: ++ padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ break; ++ case WPA_CIPHER_TKIP: ++ padapter->securitypriv.dot118021XGrpPrivacy = _TKIP_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; ++ break; ++ case WPA_CIPHER_CCMP: ++ padapter->securitypriv.dot118021XGrpPrivacy = _AES_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled; ++ break; ++ case WPA_CIPHER_WEP104: ++ padapter->securitypriv.dot118021XGrpPrivacy = _WEP104_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ break; ++ } ++ ++ switch (pairwise_cipher) { ++ case WPA_CIPHER_NONE: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; ++ break; ++ case WPA_CIPHER_WEP40: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ break; ++ case WPA_CIPHER_TKIP: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _TKIP_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; ++ break; ++ case WPA_CIPHER_CCMP: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _AES_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled; ++ break; ++ case WPA_CIPHER_WEP104: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_; ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ break; ++ } ++ ++ _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS); ++ {/* set wps_ie */ ++ u16 cnt = 0; ++ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; ++ ++ while (cnt < ielen) { ++ eid = buf[cnt]; ++ if ((eid == _VENDOR_SPECIFIC_IE_) && (!memcmp(&buf[cnt+2], wps_oui, 4))) { ++ DBG_88E("SET WPS_IE\n"); ++ ++ padapter->securitypriv.wps_ie_len = ((buf[cnt+1]+2) < (MAX_WPA_IE_LEN<<2)) ? (buf[cnt+1]+2) : (MAX_WPA_IE_LEN<<2); ++ ++ memcpy(padapter->securitypriv.wps_ie, &buf[cnt], padapter->securitypriv.wps_ie_len); ++ ++ set_fwstate(&padapter->mlmepriv, WIFI_UNDER_WPS); ++#ifdef CONFIG_88EU_P2P ++ if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_GONEGO_OK)) ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_PROVISIONING_ING); ++#endif /* CONFIG_88EU_P2P */ ++ cnt += buf[cnt+1]+2; ++ break; ++ } else { ++ cnt += buf[cnt+1]+2; /* goto next */ ++ } ++ } ++ } ++ } ++ ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ++ ("rtw_set_wpa_ie: pairwise_cipher = 0x%08x padapter->securitypriv.ndisencryptstatus =%d padapter->securitypriv.ndisauthtype =%d\n", ++ pairwise_cipher, padapter->securitypriv.ndisencryptstatus, padapter->securitypriv.ndisauthtype)); ++exit: ++ kfree(buf); ++ return ret; ++} ++ ++typedef unsigned char NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; ++ ++static int rtw_wx_get_name(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u32 ht_ielen = 0; ++ char *p; ++ u8 ht_cap = false; ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; ++ NDIS_802_11_RATES_EX *prates = NULL; ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("cmd_code =%x\n", info->cmd)); ++ ++ ++ ++ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == true) { ++ /* parsing HT_CAP_IE */ ++ p = rtw_get_ie(&pcur_bss->IEs[12], _HT_CAPABILITY_IE_, &ht_ielen, pcur_bss->IELength-12); ++ if (p && ht_ielen > 0) ++ ht_cap = true; ++ ++ prates = &pcur_bss->SupportedRates; ++ ++ if (rtw_is_cckratesonly_included((u8 *)prates) == true) { ++ if (ht_cap) ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bn"); ++ else ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b"); ++ } else if ((rtw_is_cckrates_included((u8 *)prates)) == true) { ++ if (ht_cap) ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bgn"); ++ else ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bg"); ++ } else { ++ if (pcur_bss->Configuration.DSConfig > 14) { ++ if (ht_cap) ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11an"); ++ else ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11a"); ++ } else { ++ if (ht_cap) ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11gn"); ++ else ++ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11g"); ++ } ++ } ++ } else { ++ snprintf(wrqu->name, IFNAMSIZ, "unassociated"); ++ } ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_set_freq(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+rtw_wx_set_freq\n")); ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_get_freq(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; ++ ++ if (check_fwstate(pmlmepriv, _FW_LINKED)) { ++ /* wrqu->freq.m = ieee80211_wlan_frequencies[pcur_bss->Configuration.DSConfig-1] * 100000; */ ++ wrqu->freq.m = rtw_ch2freq(pcur_bss->Configuration.DSConfig) * 100000; ++ wrqu->freq.e = 1; ++ wrqu->freq.i = pcur_bss->Configuration.DSConfig; ++ } else { ++ wrqu->freq.m = rtw_ch2freq(padapter->mlmeextpriv.cur_channel) * 100000; ++ wrqu->freq.e = 1; ++ wrqu->freq.i = padapter->mlmeextpriv.cur_channel; ++ } ++ ++ return 0; ++} ++ ++static int rtw_wx_set_mode(struct net_device *dev, struct iw_request_info *a, ++ union iwreq_data *wrqu, char *b) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ enum ndis_802_11_network_infra networkType; ++ int ret = 0; ++ ++ ++ ++ if (_FAIL == rtw_pwr_wakeup(padapter)) { ++ ret = -EPERM; ++ goto exit; ++ } ++ ++ if (!padapter->hw_init_completed) { ++ ret = -EPERM; ++ goto exit; ++ } ++ ++ switch (wrqu->mode) { ++ case IW_MODE_AUTO: ++ networkType = Ndis802_11AutoUnknown; ++ DBG_88E("set_mode = IW_MODE_AUTO\n"); ++ break; ++ case IW_MODE_ADHOC: ++ networkType = Ndis802_11IBSS; ++ DBG_88E("set_mode = IW_MODE_ADHOC\n"); ++ break; ++ case IW_MODE_MASTER: ++ networkType = Ndis802_11APMode; ++ DBG_88E("set_mode = IW_MODE_MASTER\n"); ++ break; ++ case IW_MODE_INFRA: ++ networkType = Ndis802_11Infrastructure; ++ DBG_88E("set_mode = IW_MODE_INFRA\n"); ++ break; ++ default: ++ ret = -EINVAL; ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("\n Mode: %s is not supported\n", iw_operation_mode[wrqu->mode])); ++ goto exit; ++ } ++ if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) { ++ ret = -EPERM; ++ goto exit; ++ } ++ rtw_setopmode_cmd(padapter, networkType); ++exit: ++ ++ return ret; ++} ++ ++static int rtw_wx_get_mode(struct net_device *dev, struct iw_request_info *a, ++ union iwreq_data *wrqu, char *b) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, (" rtw_wx_get_mode\n")); ++ ++ ++ ++ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) ++ wrqu->mode = IW_MODE_INFRA; ++ else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) || ++ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE))) ++ wrqu->mode = IW_MODE_ADHOC; ++ else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) ++ wrqu->mode = IW_MODE_MASTER; ++ else ++ wrqu->mode = IW_MODE_AUTO; ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_set_pmkid(struct net_device *dev, ++ struct iw_request_info *a, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u8 j, blInserted = false; ++ int ret = false; ++ struct security_priv *psecuritypriv = &padapter->securitypriv; ++ struct iw_pmksa *pPMK = (struct iw_pmksa *)extra; ++ u8 strZeroMacAddress[ETH_ALEN] = {0x00}; ++ u8 strIssueBssid[ETH_ALEN] = {0x00}; ++ ++ memcpy(strIssueBssid, pPMK->bssid.sa_data, ETH_ALEN); ++ if (pPMK->cmd == IW_PMKSA_ADD) { ++ DBG_88E("[rtw_wx_set_pmkid] IW_PMKSA_ADD!\n"); ++ if (!memcmp(strIssueBssid, strZeroMacAddress, ETH_ALEN)) ++ return ret; ++ else ++ ret = true; ++ blInserted = false; ++ ++ /* overwrite PMKID */ ++ for (j = 0; j < NUM_PMKID_CACHE; j++) { ++ if (!memcmp(psecuritypriv->PMKIDList[j].Bssid, strIssueBssid, ETH_ALEN)) { ++ /* BSSID is matched, the same AP => rewrite with new PMKID. */ ++ DBG_88E("[rtw_wx_set_pmkid] BSSID exists in the PMKList.\n"); ++ memcpy(psecuritypriv->PMKIDList[j].PMKID, pPMK->pmkid, IW_PMKID_LEN); ++ psecuritypriv->PMKIDList[j].bUsed = true; ++ psecuritypriv->PMKIDIndex = j+1; ++ blInserted = true; ++ break; ++ } ++ } ++ ++ if (!blInserted) { ++ /* Find a new entry */ ++ DBG_88E("[rtw_wx_set_pmkid] Use the new entry index = %d for this PMKID.\n", ++ psecuritypriv->PMKIDIndex); ++ ++ memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].Bssid, strIssueBssid, ETH_ALEN); ++ memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].PMKID, pPMK->pmkid, IW_PMKID_LEN); ++ ++ psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].bUsed = true; ++ psecuritypriv->PMKIDIndex++; ++ if (psecuritypriv->PMKIDIndex == 16) ++ psecuritypriv->PMKIDIndex = 0; ++ } ++ } else if (pPMK->cmd == IW_PMKSA_REMOVE) { ++ DBG_88E("[rtw_wx_set_pmkid] IW_PMKSA_REMOVE!\n"); ++ ret = true; ++ for (j = 0; j < NUM_PMKID_CACHE; j++) { ++ if (!memcmp(psecuritypriv->PMKIDList[j].Bssid, strIssueBssid, ETH_ALEN)) { ++ /* BSSID is matched, the same AP => Remove this PMKID information and reset it. */ ++ memset(psecuritypriv->PMKIDList[j].Bssid, 0x00, ETH_ALEN); ++ psecuritypriv->PMKIDList[j].bUsed = false; ++ break; ++ } ++ } ++ } else if (pPMK->cmd == IW_PMKSA_FLUSH) { ++ DBG_88E("[rtw_wx_set_pmkid] IW_PMKSA_FLUSH!\n"); ++ memset(&psecuritypriv->PMKIDList[0], 0x00, sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE); ++ psecuritypriv->PMKIDIndex = 0; ++ ret = true; ++ } ++ return ret; ++} ++ ++static int rtw_wx_get_sens(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ wrqu->sens.value = 0; ++ wrqu->sens.fixed = 0; /* no auto select */ ++ wrqu->sens.disabled = 1; ++ return 0; ++} ++ ++static int rtw_wx_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; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; ++ ++ u16 val; ++ int i; ++ ++ ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_range. cmd_code =%x\n", info->cmd)); ++ ++ wrqu->data.length = sizeof(*range); ++ memset(range, 0, sizeof(*range)); ++ ++ /* Let's try to keep this struct in the same order as in ++ * linux/include/wireless.h ++ */ ++ ++ /* TODO: See what values we can set, and remove the ones we can't ++ * set, or fill them with some default data. ++ */ ++ ++ /* ~5 Mb/s real (802.11b) */ ++ range->throughput = 5 * 1000 * 1000; ++ ++ /* signal level threshold range */ ++ ++ /* percent values between 0 and 100. */ ++ range->max_qual.qual = 100; ++ range->max_qual.level = 100; ++ range->max_qual.noise = 100; ++ range->max_qual.updated = 7; /* Updated all three */ ++ ++ range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */ ++ /* TODO: Find real 'good' to 'bad' threshol value for RSSI */ ++ range->avg_qual.level = 178; /* -78 dBm */ ++ range->avg_qual.noise = 0; ++ range->avg_qual.updated = 7; /* Updated all three */ ++ ++ range->num_bitrates = RATE_COUNT; ++ ++ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) ++ range->bitrate[i] = rtw_rates[i]; ++ ++ range->min_frag = MIN_FRAG_THRESHOLD; ++ range->max_frag = MAX_FRAG_THRESHOLD; ++ ++ range->pm_capa = 0; ++ ++ range->we_version_compiled = WIRELESS_EXT; ++ range->we_version_source = 16; ++ ++ for (i = 0, val = 0; i < MAX_CHANNEL_NUM; i++) { ++ /* Include only legal frequencies for some countries */ ++ if (pmlmeext->channel_set[i].ChannelNum != 0) { ++ range->freq[val].i = pmlmeext->channel_set[i].ChannelNum; ++ range->freq[val].m = rtw_ch2freq(pmlmeext->channel_set[i].ChannelNum) * 100000; ++ range->freq[val].e = 1; ++ val++; ++ } ++ ++ if (val == IW_MAX_FREQUENCIES) ++ break; ++ } ++ ++ range->num_channels = val; ++ range->num_frequency = val; ++ ++/* The following code will proivde the security capability to network manager. */ ++/* If the driver doesn't provide this capability to network manager, */ ++/* the WPA/WPA2 routers can't be chosen in the network manager. */ ++ ++/* ++#define IW_SCAN_CAPA_NONE 0x00 ++#define IW_SCAN_CAPA_ESSID 0x01 ++#define IW_SCAN_CAPA_BSSID 0x02 ++#define IW_SCAN_CAPA_CHANNEL 0x04 ++#define IW_SCAN_CAPA_MODE 0x08 ++#define IW_SCAN_CAPA_RATE 0x10 ++#define IW_SCAN_CAPA_TYPE 0x20 ++#define IW_SCAN_CAPA_TIME 0x40 ++*/ ++ ++ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | ++ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; ++ ++ range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE | ++ IW_SCAN_CAPA_BSSID | IW_SCAN_CAPA_CHANNEL | ++ IW_SCAN_CAPA_MODE | IW_SCAN_CAPA_RATE; ++ ++ ++ return 0; ++} ++ ++/* set bssid flow */ ++/* s1. rtw_set_802_11_infrastructure_mode() */ ++/* s2. rtw_set_802_11_authentication_mode() */ ++/* s3. set_802_11_encryption_mode() */ ++/* s4. rtw_set_802_11_bssid() */ ++static int rtw_wx_set_wap(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *awrq, ++ char *extra) ++{ ++ uint ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct sockaddr *temp = (struct sockaddr *)awrq; ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct list_head *phead; ++ u8 *dst_bssid, *src_bssid; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ enum ndis_802_11_auth_mode authmode; ++ ++ ++ ++ if (_FAIL == rtw_pwr_wakeup(padapter)) { ++ ret = -1; ++ goto exit; ++ } ++ ++ if (!padapter->bup) { ++ ret = -1; ++ goto exit; ++ } ++ ++ if (temp->sa_family != ARPHRD_ETHER) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ authmode = padapter->securitypriv.ndisauthtype; ++ spin_lock_bh(&queue->lock); ++ phead = get_list_head(queue); ++ pmlmepriv->pscanned = phead->next; ++ ++ while (phead != pmlmepriv->pscanned) { ++ ++ pnetwork = container_of(pmlmepriv->pscanned, struct wlan_network, list); ++ ++ pmlmepriv->pscanned = pmlmepriv->pscanned->next; ++ ++ dst_bssid = pnetwork->network.MacAddress; ++ ++ src_bssid = temp->sa_data; ++ ++ if ((!memcmp(dst_bssid, src_bssid, ETH_ALEN))) { ++ if (!rtw_set_802_11_infrastructure_mode(padapter, pnetwork->network.InfrastructureMode)) { ++ ret = -1; ++ spin_unlock_bh(&queue->lock); ++ goto exit; ++ } ++ ++ break; ++ } ++ } ++ spin_unlock_bh(&queue->lock); ++ ++ rtw_set_802_11_authentication_mode(padapter, authmode); ++ /* set_802_11_encryption_mode(padapter, padapter->securitypriv.ndisencryptstatus); */ ++ if (rtw_set_802_11_bssid(padapter, temp->sa_data) == false) { ++ ret = -1; ++ goto exit; ++ } ++ ++exit: ++ ++ ++ ++ return ret; ++} ++ ++static int rtw_wx_get_wap(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; ++ ++ wrqu->ap_addr.sa_family = ARPHRD_ETHER; ++ ++ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_wap\n")); ++ ++ ++ ++ if (((check_fwstate(pmlmepriv, _FW_LINKED)) == true) || ++ ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) == true) || ++ ((check_fwstate(pmlmepriv, WIFI_AP_STATE)) == true)) ++ memcpy(wrqu->ap_addr.sa_data, pcur_bss->MacAddress, ETH_ALEN); ++ else ++ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_set_mlme(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ u16 reason; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct iw_mlme *mlme = (struct iw_mlme *)extra; ++ ++ if (mlme == NULL) ++ return -1; ++ ++ DBG_88E("%s\n", __func__); ++ ++ reason = mlme->reason_code; ++ ++ DBG_88E("%s, cmd =%d, reason =%d\n", __func__, mlme->cmd, reason); ++ ++ switch (mlme->cmd) { ++ case IW_MLME_DEAUTH: ++ if (!rtw_set_802_11_disassociate(padapter)) ++ ret = -1; ++ break; ++ case IW_MLME_DISASSOC: ++ if (!rtw_set_802_11_disassociate(padapter)) ++ ret = -1; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ return ret; ++} ++ ++static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a, ++ union iwreq_data *wrqu, char *extra) ++{ ++ u8 _status = false; ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct ndis_802_11_ssid ssid[RTW_SSID_SCAN_AMOUNT]; ++#ifdef CONFIG_88EU_P2P ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++#endif /* CONFIG_88EU_P2P */ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_set_scan\n")); ++ ++ if (padapter->registrypriv.mp_mode == 1) { ++ if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { ++ ret = -1; ++ goto exit; ++ } ++ } ++ if (_FAIL == rtw_pwr_wakeup(padapter)) { ++ ret = -1; ++ goto exit; ++ } ++ ++ if (padapter->bDriverStopped) { ++ DBG_88E("bDriverStopped =%d\n", padapter->bDriverStopped); ++ ret = -1; ++ goto exit; ++ } ++ ++ if (!padapter->bup) { ++ ret = -1; ++ goto exit; ++ } ++ ++ if (!padapter->hw_init_completed) { ++ ret = -1; ++ goto exit; ++ } ++ ++ /* When Busy Traffic, driver do not site survey. So driver return success. */ ++ /* wpa_supplicant will not issue SIOCSIWSCAN cmd again after scan timeout. */ ++ /* modify by thomas 2011-02-22. */ ++ if (pmlmepriv->LinkDetectInfo.bBusyTraffic) { ++ indicate_wx_scan_complete_event(padapter); ++ goto exit; ++ } ++ ++ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) { ++ indicate_wx_scan_complete_event(padapter); ++ goto exit; ++ } ++ ++/* For the DMP WiFi Display project, the driver won't to scan because */ ++/* the pmlmepriv->scan_interval is always equal to 3. */ ++/* So, the wpa_supplicant won't find out the WPS SoftAP. */ ++ ++#ifdef CONFIG_88EU_P2P ++ if (pwdinfo->p2p_state != P2P_STATE_NONE) { ++ rtw_p2p_set_pre_state(pwdinfo, rtw_p2p_state(pwdinfo)); ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_FIND_PHASE_SEARCH); ++ rtw_p2p_findphase_ex_set(pwdinfo, P2P_FINDPHASE_EX_FULL); ++ rtw_free_network_queue(padapter, true); ++ } ++#endif /* CONFIG_88EU_P2P */ ++ ++ memset(ssid, 0, sizeof(struct ndis_802_11_ssid)*RTW_SSID_SCAN_AMOUNT); ++ ++ if (wrqu->data.length == sizeof(struct iw_scan_req)) { ++ struct iw_scan_req *req = (struct iw_scan_req *)extra; ++ ++ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { ++ int len = min((int)req->essid_len, IW_ESSID_MAX_SIZE); ++ ++ memcpy(ssid[0].Ssid, req->essid, len); ++ ssid[0].SsidLength = len; ++ ++ DBG_88E("IW_SCAN_THIS_ESSID, ssid =%s, len =%d\n", req->essid, req->essid_len); ++ ++ spin_lock_bh(&pmlmepriv->lock); ++ ++ _status = rtw_sitesurvey_cmd(padapter, ssid, 1, NULL, 0); ++ ++ spin_unlock_bh(&pmlmepriv->lock); ++ } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) { ++ DBG_88E("rtw_wx_set_scan, req->scan_type == IW_SCAN_TYPE_PASSIVE\n"); ++ } ++ } else { ++ if (wrqu->data.length >= WEXT_CSCAN_HEADER_SIZE && ++ !memcmp(extra, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE)) { ++ int len = wrqu->data.length - WEXT_CSCAN_HEADER_SIZE; ++ char *pos = extra+WEXT_CSCAN_HEADER_SIZE; ++ char section; ++ char sec_len; ++ int ssid_index = 0; ++ ++ while (len >= 1) { ++ section = *(pos++); ++ len -= 1; ++ ++ switch (section) { ++ case WEXT_CSCAN_SSID_SECTION: ++ if (len < 1) { ++ len = 0; ++ break; ++ } ++ sec_len = *(pos++); len -= 1; ++ if (sec_len > 0 && sec_len <= len) { ++ ssid[ssid_index].SsidLength = sec_len; ++ memcpy(ssid[ssid_index].Ssid, pos, ssid[ssid_index].SsidLength); ++ ssid_index++; ++ } ++ pos += sec_len; ++ len -= sec_len; ++ break; ++ case WEXT_CSCAN_TYPE_SECTION: ++ case WEXT_CSCAN_CHANNEL_SECTION: ++ pos += 1; ++ len -= 1; ++ break; ++ case WEXT_CSCAN_PASV_DWELL_SECTION: ++ case WEXT_CSCAN_HOME_DWELL_SECTION: ++ case WEXT_CSCAN_ACTV_DWELL_SECTION: ++ pos += 2; ++ len -= 2; ++ break; ++ default: ++ len = 0; /* stop parsing */ ++ } ++ } ++ ++ /* it has still some scan parameter to parse, we only do this now... */ ++ _status = rtw_set_802_11_bssid_list_scan(padapter, ssid, RTW_SSID_SCAN_AMOUNT); ++ } else { ++ _status = rtw_set_802_11_bssid_list_scan(padapter, NULL, 0); ++ } ++ } ++ ++ if (!_status) ++ ret = -1; ++ ++exit: ++ ++ return ret; ++} ++ ++static int rtw_wx_get_scan(struct net_device *dev, struct iw_request_info *a, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct list_head *plist, *phead; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ char *ev = extra; ++ char *stop = ev + wrqu->data.length; ++ u32 ret = 0; ++ u32 cnt = 0; ++ u32 wait_for_surveydone; ++ int wait_status; ++#ifdef CONFIG_88EU_P2P ++ struct wifidirect_info *pwdinfo = &padapter->wdinfo; ++#endif /* CONFIG_88EU_P2P */ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan\n")); ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, (" Start of Query SIOCGIWSCAN .\n")); ++ ++ ++ ++ if (padapter->pwrctrlpriv.brfoffbyhw && padapter->bDriverStopped) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++#ifdef CONFIG_88EU_P2P ++ if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) { ++ /* P2P is enabled */ ++ wait_for_surveydone = 200; ++ } else { ++ /* P2P is disabled */ ++ wait_for_surveydone = 100; ++ } ++#else ++ { ++ wait_for_surveydone = 100; ++ } ++#endif /* CONFIG_88EU_P2P */ ++ ++ wait_status = _FW_UNDER_SURVEY | _FW_UNDER_LINKING; ++ ++ while (check_fwstate(pmlmepriv, wait_status)) { ++ rtw_msleep_os(30); ++ cnt++; ++ if (cnt > wait_for_surveydone) ++ break; ++ } ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ if ((stop - ev) < SCAN_ITEM_SIZE) { ++ ret = -E2BIG; ++ break; ++ } ++ ++ pnetwork = container_of(plist, struct wlan_network, list); ++ ++ /* report network only if the current channel set contains the channel to which this network belongs */ ++ if (rtw_ch_set_search_ch(padapter->mlmeextpriv.channel_set, pnetwork->network.Configuration.DSConfig) >= 0) ++ ev = translate_scan(padapter, a, pnetwork, ev, stop); ++ ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ wrqu->data.length = ev-extra; ++ wrqu->data.flags = 0; ++ ++exit: ++ ++ return ret; ++} ++ ++/* set ssid flow */ ++/* s1. rtw_set_802_11_infrastructure_mode() */ ++/* s2. set_802_11_authenticaion_mode() */ ++/* s3. set_802_11_encryption_mode() */ ++/* s4. rtw_set_802_11_ssid() */ ++static int rtw_wx_set_essid(struct net_device *dev, ++ struct iw_request_info *a, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct __queue *queue = &pmlmepriv->scanned_queue; ++ struct list_head *phead; ++ struct wlan_network *pnetwork = NULL; ++ enum ndis_802_11_auth_mode authmode; ++ struct ndis_802_11_ssid ndis_ssid; ++ u8 *dst_ssid, *src_ssid; ++ ++ uint ret = 0, len; ++ ++ ++ ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ++ ("+rtw_wx_set_essid: fw_state = 0x%08x\n", get_fwstate(pmlmepriv))); ++ if (_FAIL == rtw_pwr_wakeup(padapter)) { ++ ret = -1; ++ goto exit; ++ } ++ ++ if (!padapter->bup) { ++ ret = -1; ++ goto exit; ++ } ++ ++ if (wrqu->essid.length > IW_ESSID_MAX_SIZE) { ++ ret = -E2BIG; ++ goto exit; ++ } ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { ++ ret = -1; ++ goto exit; ++ } ++ ++ authmode = padapter->securitypriv.ndisauthtype; ++ DBG_88E("=>%s\n", __func__); ++ if (wrqu->essid.flags && wrqu->essid.length) { ++ len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? wrqu->essid.length : IW_ESSID_MAX_SIZE; ++ ++ if (wrqu->essid.length != 33) ++ DBG_88E("ssid =%s, len =%d\n", extra, wrqu->essid.length); ++ ++ memset(&ndis_ssid, 0, sizeof(struct ndis_802_11_ssid)); ++ ndis_ssid.SsidLength = len; ++ memcpy(ndis_ssid.Ssid, extra, len); ++ src_ssid = ndis_ssid.Ssid; ++ ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("rtw_wx_set_essid: ssid =[%s]\n", src_ssid)); ++ spin_lock_bh(&queue->lock); ++ phead = get_list_head(queue); ++ pmlmepriv->pscanned = phead->next; ++ ++ while (phead != pmlmepriv->pscanned) { ++ pnetwork = container_of(pmlmepriv->pscanned, struct wlan_network, list); ++ ++ pmlmepriv->pscanned = pmlmepriv->pscanned->next; ++ ++ dst_ssid = pnetwork->network.Ssid.Ssid; ++ ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ++ ("rtw_wx_set_essid: dst_ssid =%s\n", ++ pnetwork->network.Ssid.Ssid)); ++ ++ if ((!memcmp(dst_ssid, src_ssid, ndis_ssid.SsidLength)) && ++ (pnetwork->network.Ssid.SsidLength == ndis_ssid.SsidLength)) { ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ++ ("rtw_wx_set_essid: find match, set infra mode\n")); ++ ++ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) { ++ if (pnetwork->network.InfrastructureMode != pmlmepriv->cur_network.network.InfrastructureMode) ++ continue; ++ } ++ ++ if (!rtw_set_802_11_infrastructure_mode(padapter, pnetwork->network.InfrastructureMode)) { ++ ret = -1; ++ spin_unlock_bh(&queue->lock); ++ goto exit; ++ } ++ ++ break; ++ } ++ } ++ spin_unlock_bh(&queue->lock); ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ++ ("set ssid: set_802_11_auth. mode =%d\n", authmode)); ++ rtw_set_802_11_authentication_mode(padapter, authmode); ++ if (rtw_set_802_11_ssid(padapter, &ndis_ssid) == false) { ++ ret = -1; ++ goto exit; ++ } ++ } ++ ++exit: ++ ++ DBG_88E("<=%s, ret %d\n", __func__, ret); ++ ++ ++ ++ return ret; ++} ++ ++static int rtw_wx_get_essid(struct net_device *dev, ++ struct iw_request_info *a, ++ union iwreq_data *wrqu, char *extra) ++{ ++ u32 len, ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_essid\n")); ++ ++ ++ ++ if ((check_fwstate(pmlmepriv, _FW_LINKED)) || ++ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE))) { ++ len = pcur_bss->Ssid.SsidLength; ++ memcpy(extra, pcur_bss->Ssid.Ssid, len); ++ } else { ++ len = 0; ++ *extra = 0; ++ } ++ wrqu->essid.length = len; ++ wrqu->essid.flags = 1; ++ ++exit: ++ return ret; ++} ++ ++static int rtw_wx_set_rate(struct net_device *dev, ++ struct iw_request_info *a, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int i, ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u8 datarates[NumRates]; ++ u32 target_rate = wrqu->bitrate.value; ++ u32 fixed = wrqu->bitrate.fixed; ++ u32 ratevalue = 0; ++ u8 mpdatarate[NumRates] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0xff}; ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, (" rtw_wx_set_rate\n")); ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("target_rate = %d, fixed = %d\n", target_rate, fixed)); ++ ++ if (target_rate == -1) { ++ ratevalue = 11; ++ goto set_rate; ++ } ++ target_rate = target_rate/100000; ++ ++ switch (target_rate) { ++ case 10: ++ ratevalue = 0; ++ break; ++ case 20: ++ ratevalue = 1; ++ break; ++ case 55: ++ ratevalue = 2; ++ break; ++ case 60: ++ ratevalue = 3; ++ break; ++ case 90: ++ ratevalue = 4; ++ break; ++ case 110: ++ ratevalue = 5; ++ break; ++ case 120: ++ ratevalue = 6; ++ break; ++ case 180: ++ ratevalue = 7; ++ break; ++ case 240: ++ ratevalue = 8; ++ break; ++ case 360: ++ ratevalue = 9; ++ break; ++ case 480: ++ ratevalue = 10; ++ break; ++ case 540: ++ ratevalue = 11; ++ break; ++ default: ++ ratevalue = 11; ++ break; ++ } ++ ++set_rate: ++ ++ for (i = 0; i < NumRates; i++) { ++ if (ratevalue == mpdatarate[i]) { ++ datarates[i] = mpdatarate[i]; ++ if (fixed == 0) ++ break; ++ } else { ++ datarates[i] = 0xff; ++ } ++ ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("datarate_inx =%d\n", datarates[i])); ++ } ++ ++ if (rtw_setdatarate_cmd(padapter, datarates) != _SUCCESS) { ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("rtw_wx_set_rate Fail!!!\n")); ++ ret = -1; ++ } ++ ++ return ret; ++} ++ ++static int rtw_wx_get_rate(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ u16 max_rate = 0; ++ ++ max_rate = rtw_get_cur_max_rate((struct adapter *)rtw_netdev_priv(dev)); ++ ++ if (max_rate == 0) ++ return -EPERM; ++ ++ wrqu->bitrate.fixed = 0; /* no auto select */ ++ wrqu->bitrate.value = max_rate * 100000; ++ ++ return 0; ++} ++ ++static int rtw_wx_set_rts(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ ++ ++ if (wrqu->rts.disabled) { ++ padapter->registrypriv.rts_thresh = 2347; ++ } else { ++ if (wrqu->rts.value < 0 || ++ wrqu->rts.value > 2347) ++ return -EINVAL; ++ ++ padapter->registrypriv.rts_thresh = wrqu->rts.value; ++ } ++ ++ DBG_88E("%s, rts_thresh =%d\n", __func__, padapter->registrypriv.rts_thresh); ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_get_rts(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ ++ ++ DBG_88E("%s, rts_thresh =%d\n", __func__, padapter->registrypriv.rts_thresh); ++ ++ wrqu->rts.value = padapter->registrypriv.rts_thresh; ++ wrqu->rts.fixed = 0; /* no auto select */ ++ /* wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); */ ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_set_frag(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ ++ ++ if (wrqu->frag.disabled) { ++ padapter->xmitpriv.frag_len = MAX_FRAG_THRESHOLD; ++ } else { ++ if (wrqu->frag.value < MIN_FRAG_THRESHOLD || ++ wrqu->frag.value > MAX_FRAG_THRESHOLD) ++ return -EINVAL; ++ ++ padapter->xmitpriv.frag_len = wrqu->frag.value & ~0x1; ++ } ++ ++ DBG_88E("%s, frag_len =%d\n", __func__, padapter->xmitpriv.frag_len); ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_get_frag(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ ++ ++ DBG_88E("%s, frag_len =%d\n", __func__, padapter->xmitpriv.frag_len); ++ ++ wrqu->frag.value = padapter->xmitpriv.frag_len; ++ wrqu->frag.fixed = 0; /* no auto select */ ++ ++ ++ ++ return 0; ++} ++ ++static int rtw_wx_get_retry(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ wrqu->retry.value = 7; ++ wrqu->retry.fixed = 0; /* no auto select */ ++ wrqu->retry.disabled = 1; ++ ++ return 0; ++} ++ ++static int rtw_wx_set_enc(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *keybuf) ++{ ++ u32 key, ret = 0; ++ u32 keyindex_provided; ++ struct ndis_802_11_wep wep; ++ enum ndis_802_11_auth_mode authmode; ++ ++ struct iw_point *erq = &(wrqu->encoding); ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; ++ DBG_88E("+rtw_wx_set_enc, flags = 0x%x\n", erq->flags); ++ ++ memset(&wep, 0, sizeof(struct ndis_802_11_wep)); ++ ++ key = erq->flags & IW_ENCODE_INDEX; ++ ++ ++ ++ if (erq->flags & IW_ENCODE_DISABLED) { ++ DBG_88E("EncryptionDisabled\n"); ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; ++ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ ++ authmode = Ndis802_11AuthModeOpen; ++ padapter->securitypriv.ndisauthtype = authmode; ++ ++ goto exit; ++ } ++ ++ if (key) { ++ if (key > WEP_KEYS) ++ return -EINVAL; ++ key--; ++ keyindex_provided = 1; ++ } else { ++ keyindex_provided = 0; ++ key = padapter->securitypriv.dot11PrivacyKeyIndex; ++ DBG_88E("rtw_wx_set_enc, key =%d\n", key); ++ } ++ ++ /* set authentication mode */ ++ if (erq->flags & IW_ENCODE_OPEN) { ++ DBG_88E("rtw_wx_set_enc():IW_ENCODE_OPEN\n"); ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;/* Ndis802_11EncryptionDisabled; */ ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; ++ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; ++ authmode = Ndis802_11AuthModeOpen; ++ padapter->securitypriv.ndisauthtype = authmode; ++ } else if (erq->flags & IW_ENCODE_RESTRICTED) { ++ DBG_88E("rtw_wx_set_enc():IW_ENCODE_RESTRICTED\n"); ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Shared; ++ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_; ++ padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_; ++ authmode = Ndis802_11AuthModeShared; ++ padapter->securitypriv.ndisauthtype = authmode; ++ } else { ++ DBG_88E("rtw_wx_set_enc():erq->flags = 0x%x\n", erq->flags); ++ ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;/* Ndis802_11EncryptionDisabled; */ ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ ++ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; ++ authmode = Ndis802_11AuthModeOpen; ++ padapter->securitypriv.ndisauthtype = authmode; ++ } ++ ++ wep.KeyIndex = key; ++ if (erq->length > 0) { ++ wep.KeyLength = erq->length <= 5 ? 5 : 13; ++ ++ wep.Length = wep.KeyLength + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial); ++ } else { ++ wep.KeyLength = 0; ++ ++ if (keyindex_provided == 1) { ++ /* set key_id only, no given KeyMaterial(erq->length == 0). */ ++ padapter->securitypriv.dot11PrivacyKeyIndex = key; ++ ++ DBG_88E("(keyindex_provided == 1), keyid =%d, key_len =%d\n", key, padapter->securitypriv.dot11DefKeylen[key]); ++ ++ switch (padapter->securitypriv.dot11DefKeylen[key]) { ++ case 5: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_; ++ break; ++ case 13: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_; ++ break; ++ default: ++ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ break; ++ } ++ ++ goto exit; ++ } ++ } ++ ++ wep.KeyIndex |= 0x80000000; ++ ++ memcpy(wep.KeyMaterial, keybuf, wep.KeyLength); ++ ++ if (rtw_set_802_11_add_wep(padapter, &wep) == false) { ++ if (rf_on == pwrpriv->rf_pwrstate) ++ ret = -EOPNOTSUPP; ++ goto exit; ++ } ++ ++exit: ++ ++ ++ ++ return ret; ++} ++ ++static int rtw_wx_get_enc(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *keybuf) ++{ ++ uint key, ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct iw_point *erq = &(wrqu->encoding); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ ++ ++ ++ if (check_fwstate(pmlmepriv, _FW_LINKED) != true) { ++ if (!check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) { ++ erq->length = 0; ++ erq->flags |= IW_ENCODE_DISABLED; ++ return 0; ++ } ++ } ++ ++ key = erq->flags & IW_ENCODE_INDEX; ++ ++ if (key) { ++ if (key > WEP_KEYS) ++ return -EINVAL; ++ key--; ++ } else { ++ key = padapter->securitypriv.dot11PrivacyKeyIndex; ++ } ++ ++ erq->flags = key + 1; ++ ++ switch (padapter->securitypriv.ndisencryptstatus) { ++ case Ndis802_11EncryptionNotSupported: ++ case Ndis802_11EncryptionDisabled: ++ erq->length = 0; ++ erq->flags |= IW_ENCODE_DISABLED; ++ break; ++ case Ndis802_11Encryption1Enabled: ++ erq->length = padapter->securitypriv.dot11DefKeylen[key]; ++ if (erq->length) { ++ memcpy(keybuf, padapter->securitypriv.dot11DefKey[key].skey, padapter->securitypriv.dot11DefKeylen[key]); ++ ++ erq->flags |= IW_ENCODE_ENABLED; ++ ++ if (padapter->securitypriv.ndisauthtype == Ndis802_11AuthModeOpen) ++ erq->flags |= IW_ENCODE_OPEN; ++ else if (padapter->securitypriv.ndisauthtype == Ndis802_11AuthModeShared) ++ erq->flags |= IW_ENCODE_RESTRICTED; ++ } else { ++ erq->length = 0; ++ erq->flags |= IW_ENCODE_DISABLED; ++ } ++ break; ++ case Ndis802_11Encryption2Enabled: ++ case Ndis802_11Encryption3Enabled: ++ erq->length = 16; ++ erq->flags |= (IW_ENCODE_ENABLED | IW_ENCODE_OPEN | IW_ENCODE_NOKEY); ++ break; ++ default: ++ erq->length = 0; ++ erq->flags |= IW_ENCODE_DISABLED; ++ break; ++ } ++ ++ ++ return ret; ++} ++ ++static int rtw_wx_get_power(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ wrqu->power.value = 0; ++ wrqu->power.fixed = 0; /* no auto select */ ++ wrqu->power.disabled = 1; ++ ++ return 0; ++} ++ ++static int rtw_wx_set_gen_ie(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ ret = rtw_set_wpa_ie(padapter, extra, wrqu->data.length); ++ return ret; ++} ++ ++static int rtw_wx_set_auth(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct iw_param *param = (struct iw_param *)&(wrqu->param); ++ int ret = 0; ++ ++ switch (param->flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ break; ++ case IW_AUTH_CIPHER_PAIRWISE: ++ ++ break; ++ case IW_AUTH_CIPHER_GROUP: ++ ++ break; ++ case IW_AUTH_KEY_MGMT: ++ /* ++ * ??? does not use these parameters ++ */ ++ break; ++ case IW_AUTH_TKIP_COUNTERMEASURES: ++ if (param->value) { ++ /* wpa_supplicant is enabling the tkip countermeasure. */ ++ padapter->securitypriv.btkip_countermeasure = true; ++ } else { ++ /* wpa_supplicant is disabling the tkip countermeasure. */ ++ padapter->securitypriv.btkip_countermeasure = false; ++ } ++ break; ++ case IW_AUTH_DROP_UNENCRYPTED: ++ /* HACK: ++ * ++ * wpa_supplicant calls set_wpa_enabled when the driver ++ * is loaded and unloaded, regardless of if WPA is being ++ * used. No other calls are made which can be used to ++ * determine if encryption will be used or not prior to ++ * association being expected. If encryption is not being ++ * used, drop_unencrypted is set to false, else true -- we ++ * can use this to determine if the CAP_PRIVACY_ON bit should ++ * be set. ++ */ ++ ++ if (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption1Enabled) ++ break;/* it means init value, or using wep, ndisencryptstatus = Ndis802_11Encryption1Enabled, */ ++ /* then it needn't reset it; */ ++ ++ if (param->value) { ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; ++ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen; ++ } ++ ++ break; ++ case IW_AUTH_80211_AUTH_ALG: ++ /* ++ * It's the starting point of a link layer connection using wpa_supplicant ++ */ ++ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) { ++ LeaveAllPowerSaveMode(padapter); ++ rtw_disassoc_cmd(padapter, 500, false); ++ DBG_88E("%s...call rtw_indicate_disconnect\n ", __func__); ++ rtw_indicate_disconnect(padapter); ++ rtw_free_assoc_resources(padapter, 1); ++ } ++ ret = wpa_set_auth_algs(dev, (u32)param->value); ++ break; ++ case IW_AUTH_WPA_ENABLED: ++ break; ++ case IW_AUTH_RX_UNENCRYPTED_EAPOL: ++ break; ++ case IW_AUTH_PRIVACY_INVOKED: ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return ret; ++} ++ ++static int rtw_wx_set_enc_ext(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ char *alg_name; ++ u32 param_len; ++ struct ieee_param *param = NULL; ++ struct iw_point *pencoding = &wrqu->encoding; ++ struct iw_encode_ext *pext = (struct iw_encode_ext *)extra; ++ int ret = 0; ++ ++ param_len = sizeof(struct ieee_param) + pext->key_len; ++ param = (struct ieee_param *)rtw_malloc(param_len); ++ if (param == NULL) ++ return -1; ++ ++ memset(param, 0, param_len); ++ ++ param->cmd = IEEE_CMD_SET_ENCRYPTION; ++ memset(param->sta_addr, 0xff, ETH_ALEN); ++ ++ switch (pext->alg) { ++ case IW_ENCODE_ALG_NONE: ++ /* todo: remove key */ ++ /* remove = 1; */ ++ alg_name = "none"; ++ break; ++ case IW_ENCODE_ALG_WEP: ++ alg_name = "WEP"; ++ break; ++ case IW_ENCODE_ALG_TKIP: ++ alg_name = "TKIP"; ++ break; ++ case IW_ENCODE_ALG_CCMP: ++ alg_name = "CCMP"; ++ break; ++ default: ++ return -1; ++ } ++ ++ strncpy((char *)param->u.crypt.alg, alg_name, IEEE_CRYPT_ALG_NAME_LEN); ++ ++ if (pext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) ++ param->u.crypt.set_tx = 1; ++ ++ /* cliW: WEP does not have group key ++ * just not checking GROUP key setting ++ */ ++ if ((pext->alg != IW_ENCODE_ALG_WEP) && ++ (pext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)) ++ param->u.crypt.set_tx = 0; ++ ++ param->u.crypt.idx = (pencoding->flags&0x00FF) - 1; ++ ++ if (pext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) ++ memcpy(param->u.crypt.seq, pext->rx_seq, 8); ++ ++ if (pext->key_len) { ++ param->u.crypt.key_len = pext->key_len; ++ memcpy(param->u.crypt.key, pext + 1, pext->key_len); ++ } ++ ++ ret = wpa_set_encryption(dev, param, param_len); ++ ++ kfree(param); ++ return ret; ++} ++ ++static int rtw_wx_get_nick(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ if (extra) { ++ wrqu->data.length = 14; ++ wrqu->data.flags = 1; ++ memcpy(extra, "", 14); ++ } ++ ++ /* dump debug info here */ ++ return 0; ++} ++ ++static int rtw_wx_read32(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter; ++ struct iw_point *p; ++ u16 len; ++ u32 addr; ++ u32 data32; ++ u32 bytes; ++ u8 *ptmp; ++ ++ padapter = (struct adapter *)rtw_netdev_priv(dev); ++ p = &wrqu->data; ++ len = p->length; ++ ptmp = (u8 *)rtw_malloc(len); ++ if (NULL == ptmp) ++ return -ENOMEM; ++ ++ if (copy_from_user(ptmp, p->pointer, len)) { ++ kfree(ptmp); ++ return -EFAULT; ++ } ++ ++ bytes = 0; ++ addr = 0; ++ sscanf(ptmp, "%d,%x", &bytes, &addr); ++ ++ switch (bytes) { ++ case 1: ++ data32 = rtw_read8(padapter, addr); ++ sprintf(extra, "0x%02X", data32); ++ break; ++ case 2: ++ data32 = rtw_read16(padapter, addr); ++ sprintf(extra, "0x%04X", data32); ++ break; ++ case 4: ++ data32 = rtw_read32(padapter, addr); ++ sprintf(extra, "0x%08X", data32); ++ break; ++ default: ++ DBG_88E(KERN_INFO "%s: usage> read [bytes],[address(hex)]\n", __func__); ++ return -EINVAL; ++ } ++ DBG_88E(KERN_INFO "%s: addr = 0x%08X data =%s\n", __func__, addr, extra); ++ ++ kfree(ptmp); ++ return 0; ++} ++ ++static int rtw_wx_write32(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ u32 addr; ++ u32 data32; ++ u32 bytes; ++ ++ bytes = 0; ++ addr = 0; ++ data32 = 0; ++ sscanf(extra, "%d,%x,%x", &bytes, &addr, &data32); ++ ++ switch (bytes) { ++ case 1: ++ rtw_write8(padapter, addr, (u8)data32); ++ DBG_88E(KERN_INFO "%s: addr = 0x%08X data = 0x%02X\n", __func__, addr, (u8)data32); ++ break; ++ case 2: ++ rtw_write16(padapter, addr, (u16)data32); ++ DBG_88E(KERN_INFO "%s: addr = 0x%08X data = 0x%04X\n", __func__, addr, (u16)data32); ++ break; ++ case 4: ++ rtw_write32(padapter, addr, data32); ++ DBG_88E(KERN_INFO "%s: addr = 0x%08X data = 0x%08X\n", __func__, addr, data32); ++ break; ++ default: ++ DBG_88E(KERN_INFO "%s: usage> write [bytes],[address(hex)],[data(hex)]\n", __func__); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rtw_wx_read_rf(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u32 path, addr, data32; ++ ++ path = *(u32 *)extra; ++ addr = *((u32 *)extra + 1); ++ data32 = rtw_hal_read_rfreg(padapter, path, addr, 0xFFFFF); ++ /* ++ * IMPORTANT!! ++ * Only when wireless private ioctl is at odd order, ++ * "extra" would be copied to user space. ++ */ ++ sprintf(extra, "0x%05x", data32); ++ ++ return 0; ++} ++ ++static int rtw_wx_write_rf(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u32 path, addr, data32; ++ ++ path = *(u32 *)extra; ++ addr = *((u32 *)extra + 1); ++ data32 = *((u32 *)extra + 2); ++ rtw_hal_write_rfreg(padapter, path, addr, 0xFFFFF, data32); ++ ++ return 0; ++} ++ ++static int rtw_wx_priv_null(struct net_device *dev, struct iw_request_info *a, ++ union iwreq_data *wrqu, char *b) ++{ ++ return -1; ++} ++ ++static int dummy(struct net_device *dev, struct iw_request_info *a, ++ union iwreq_data *wrqu, char *b) ++{ ++ return -1; ++} ++ ++static int rtw_wx_set_channel_plan(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ u8 channel_plan_req = (u8) (*((int *)wrqu)); ++ ++ if (_SUCCESS == rtw_set_chplan_cmd(padapter, channel_plan_req, 1)) ++ DBG_88E("%s set channel_plan = 0x%02X\n", __func__, pmlmepriv->ChannelPlan); ++ else ++ return -EPERM; ++ ++ return 0; ++} ++ ++static int rtw_wx_set_mtk_wps_probe_ie(struct net_device *dev, ++ struct iw_request_info *a, ++ union iwreq_data *wrqu, char *b) ++{ ++ return 0; ++} ++ ++static int rtw_wx_get_sensitivity(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *buf) ++{ ++ return 0; ++} ++ ++static int rtw_wx_set_mtk_wps_ie(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ return 0; ++} ++ ++/* ++ * For all data larger than 16 octets, we need to use a ++ * pointer to memory allocated in user space. ++ */ ++static int rtw_drvext_hdl(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ return 0; ++} ++ ++static void rtw_dbg_mode_hdl(struct adapter *padapter, u32 id, u8 *pdata, u32 len) ++{ ++ struct mp_rw_reg *RegRWStruct; ++ struct rf_reg_param *prfreg; ++ u8 path; ++ u8 offset; ++ u32 value; ++ ++ DBG_88E("%s\n", __func__); ++ ++ switch (id) { ++ case GEN_MP_IOCTL_SUBCODE(MP_START): ++ DBG_88E("871x_driver is only for normal mode, can't enter mp mode\n"); ++ break; ++ case GEN_MP_IOCTL_SUBCODE(READ_REG): ++ RegRWStruct = (struct mp_rw_reg *)pdata; ++ switch (RegRWStruct->width) { ++ case 1: ++ RegRWStruct->value = rtw_read8(padapter, RegRWStruct->offset); ++ break; ++ case 2: ++ RegRWStruct->value = rtw_read16(padapter, RegRWStruct->offset); ++ break; ++ case 4: ++ RegRWStruct->value = rtw_read32(padapter, RegRWStruct->offset); ++ break; ++ default: ++ break; ++ } ++ ++ break; ++ case GEN_MP_IOCTL_SUBCODE(WRITE_REG): ++ RegRWStruct = (struct mp_rw_reg *)pdata; ++ switch (RegRWStruct->width) { ++ case 1: ++ rtw_write8(padapter, RegRWStruct->offset, (u8)RegRWStruct->value); ++ break; ++ case 2: ++ rtw_write16(padapter, RegRWStruct->offset, (u16)RegRWStruct->value); ++ break; ++ case 4: ++ rtw_write32(padapter, RegRWStruct->offset, (u32)RegRWStruct->value); ++ break; ++ default: ++ break; ++ } ++ ++ break; ++ case GEN_MP_IOCTL_SUBCODE(READ_RF_REG): ++ ++ prfreg = (struct rf_reg_param *)pdata; ++ ++ path = (u8)prfreg->path; ++ offset = (u8)prfreg->offset; ++ ++ value = rtw_hal_read_rfreg(padapter, path, offset, 0xffffffff); ++ ++ prfreg->value = value; ++ ++ break; ++ case GEN_MP_IOCTL_SUBCODE(WRITE_RF_REG): ++ ++ prfreg = (struct rf_reg_param *)pdata; ++ ++ path = (u8)prfreg->path; ++ offset = (u8)prfreg->offset; ++ value = prfreg->value; ++ ++ rtw_hal_write_rfreg(padapter, path, offset, 0xffffffff, value); ++ ++ break; ++ case GEN_MP_IOCTL_SUBCODE(TRIGGER_GPIO): ++ DBG_88E("==> trigger gpio 0\n"); ++ rtw_hal_set_hwreg(padapter, HW_VAR_TRIGGER_GPIO_0, NULL); ++ break; ++ case GEN_MP_IOCTL_SUBCODE(GET_WIFI_STATUS): ++ *pdata = rtw_hal_sreset_get_wifi_status(padapter); ++ break; ++ default: ++ break; ++ } ++} ++ ++static int rtw_mp_ioctl_hdl(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ u32 BytesRead, BytesWritten, BytesNeeded; ++ struct oid_par_priv oid_par; ++ struct mp_ioctl_handler *phandler; ++ struct mp_ioctl_param *poidparam; ++ uint status = 0; ++ u16 len; ++ u8 *pparmbuf = NULL, bset; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct iw_point *p = &wrqu->data; ++ ++ if ((!p->length) || (!p->pointer)) { ++ ret = -EINVAL; ++ goto _rtw_mp_ioctl_hdl_exit; ++ } ++ pparmbuf = NULL; ++ bset = (u8)(p->flags & 0xFFFF); ++ len = p->length; ++ pparmbuf = (u8 *)rtw_malloc(len); ++ if (pparmbuf == NULL) { ++ ret = -ENOMEM; ++ goto _rtw_mp_ioctl_hdl_exit; ++ } ++ ++ if (copy_from_user(pparmbuf, p->pointer, len)) { ++ ret = -EFAULT; ++ goto _rtw_mp_ioctl_hdl_exit; ++ } ++ ++ poidparam = (struct mp_ioctl_param *)pparmbuf; ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ++ ("rtw_mp_ioctl_hdl: subcode [%d], len[%d], buffer_len[%d]\r\n", ++ poidparam->subcode, poidparam->len, len)); ++ ++ if (poidparam->subcode >= MAX_MP_IOCTL_SUBCODE) { ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("no matching drvext subcodes\r\n")); ++ ret = -EINVAL; ++ goto _rtw_mp_ioctl_hdl_exit; ++ } ++ ++ if (padapter->registrypriv.mp_mode == 1) { ++ phandler = mp_ioctl_hdl + poidparam->subcode; ++ ++ if ((phandler->paramsize != 0) && (poidparam->len < phandler->paramsize)) { ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ++ ("no matching drvext param size %d vs %d\r\n", ++ poidparam->len, phandler->paramsize)); ++ ret = -EINVAL; ++ goto _rtw_mp_ioctl_hdl_exit; ++ } ++ ++ if (phandler->handler) { ++ oid_par.adapter_context = padapter; ++ oid_par.oid = phandler->oid; ++ oid_par.information_buf = poidparam->data; ++ oid_par.information_buf_len = poidparam->len; ++ oid_par.dbg = 0; ++ ++ BytesWritten = 0; ++ BytesNeeded = 0; ++ ++ if (bset) { ++ oid_par.bytes_rw = &BytesRead; ++ oid_par.bytes_needed = &BytesNeeded; ++ oid_par.type_of_oid = SET_OID; ++ } else { ++ oid_par.bytes_rw = &BytesWritten; ++ oid_par.bytes_needed = &BytesNeeded; ++ oid_par.type_of_oid = QUERY_OID; ++ } ++ ++ status = phandler->handler(&oid_par); ++ } else { ++ DBG_88E("rtw_mp_ioctl_hdl(): err!, subcode =%d, oid =%d, handler =%p\n", ++ poidparam->subcode, phandler->oid, phandler->handler); ++ ret = -EFAULT; ++ goto _rtw_mp_ioctl_hdl_exit; ++ } ++ } else { ++ rtw_dbg_mode_hdl(padapter, poidparam->subcode, poidparam->data, poidparam->len); ++ } ++ ++ if (bset == 0x00) {/* query info */ ++ if (copy_to_user(p->pointer, pparmbuf, len)) ++ ret = -EFAULT; ++ } ++ ++ if (status) { ++ ret = -EFAULT; ++ goto _rtw_mp_ioctl_hdl_exit; ++ } ++ ++_rtw_mp_ioctl_hdl_exit: ++ ++ kfree(pparmbuf); ++ return ret; ++} ++ ++static int rtw_get_ap_info(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ u32 cnt = 0, wpa_ielen; ++ struct list_head *plist, *phead; ++ unsigned char *pbuf; ++ u8 bssid[ETH_ALEN]; ++ char data[32]; ++ struct wlan_network *pnetwork = NULL; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct iw_point *pdata = &wrqu->data; ++ ++ DBG_88E("+rtw_get_aplist_info\n"); ++ ++ if ((padapter->bDriverStopped) || (pdata == NULL)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ while ((check_fwstate(pmlmepriv, (_FW_UNDER_SURVEY|_FW_UNDER_LINKING)))) { ++ rtw_msleep_os(30); ++ cnt++; ++ if (cnt > 100) ++ break; ++ } ++ pdata->flags = 0; ++ if (pdata->length >= 32) { ++ if (copy_from_user(data, pdata->pointer, 32)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ } else { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ ++ if (hwaddr_aton_i(data, bssid)) { ++ DBG_88E("Invalid BSSID '%s'.\n", (u8 *)data); ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ return -EINVAL; ++ } ++ ++ if (!memcmp(bssid, pnetwork->network.MacAddress, ETH_ALEN)) { ++ /* BSSID match, then check if supporting wpa/wpa2 */ ++ DBG_88E("BSSID:%pM\n", (bssid)); ++ ++ pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12); ++ if (pbuf && (wpa_ielen > 0)) { ++ pdata->flags = 1; ++ break; ++ } ++ ++ pbuf = rtw_get_wpa2_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12); ++ if (pbuf && (wpa_ielen > 0)) { ++ pdata->flags = 2; ++ break; ++ } ++ } ++ ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (pdata->length >= 34) { ++ if (copy_to_user(pdata->pointer+32, (u8 *)&pdata->flags, 1)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ } ++ ++exit: ++ ++ return ret; ++} ++ ++static int rtw_set_pid(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ int *pdata = (int *)wrqu; ++ int selector; ++ ++ if ((padapter->bDriverStopped) || (pdata == NULL)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ selector = *pdata; ++ if (selector < 3 && selector >= 0) { ++ padapter->pid[selector] = *(pdata+1); ++ ui_pid[selector] = *(pdata+1); ++ DBG_88E("%s set pid[%d] =%d\n", __func__, selector, padapter->pid[selector]); ++ } else { ++ DBG_88E("%s selector %d error\n", __func__, selector); ++ } ++exit: ++ return ret; ++} ++ ++static int rtw_wps_start(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct iw_point *pdata = &wrqu->data; ++ u32 u32wps_start = 0; ++ ++ ret = copy_from_user((void *)&u32wps_start, pdata->pointer, 4); ++ if (ret) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ if ((padapter->bDriverStopped) || (pdata == NULL)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ if (u32wps_start == 0) ++ u32wps_start = *extra; ++ ++ DBG_88E("[%s] wps_start = %d\n", __func__, u32wps_start); ++ ++ if (u32wps_start == 1) /* WPS Start */ ++ rtw_led_control(padapter, LED_CTL_START_WPS); ++ else if (u32wps_start == 2) /* WPS Stop because of wps success */ ++ rtw_led_control(padapter, LED_CTL_STOP_WPS); ++ else if (u32wps_start == 3) /* WPS Stop because of wps fail */ ++ rtw_led_control(padapter, LED_CTL_STOP_WPS_FAIL); ++ ++exit: ++ return ret; ++} ++ ++#ifdef CONFIG_88EU_P2P ++static int rtw_wext_p2p_enable(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; ++ enum P2P_ROLE init_role = P2P_ROLE_DISABLE; ++ ++ if (*extra == '0') ++ init_role = P2P_ROLE_DISABLE; ++ else if (*extra == '1') ++ init_role = P2P_ROLE_DEVICE; ++ else if (*extra == '2') ++ init_role = P2P_ROLE_CLIENT; ++ else if (*extra == '3') ++ init_role = P2P_ROLE_GO; ++ ++ if (_FAIL == rtw_p2p_enable(padapter, init_role)) { ++ ret = -EFAULT; ++ goto exit; ++ } ++ ++ /* set channel/bandwidth */ ++ if (init_role != P2P_ROLE_DISABLE) { ++ u8 channel, ch_offset; ++ u16 bwmode; ++ ++ if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_LISTEN)) { ++ /* Stay at the listen state and wait for discovery. */ ++ channel = pwdinfo->listen_channel; ++ pwdinfo->operating_channel = pwdinfo->listen_channel; ++ ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE; ++ bwmode = HT_CHANNEL_WIDTH_20; ++ } else { ++ pwdinfo->operating_channel = pmlmeext->cur_channel; ++ ++ channel = pwdinfo->operating_channel; ++ ch_offset = pmlmeext->cur_ch_offset; ++ bwmode = pmlmeext->cur_bwmode; ++ } ++ ++ set_channel_bwmode(padapter, channel, ch_offset, bwmode); ++ } ++ ++exit: ++ return ret; ++} ++ ++static int rtw_p2p_set_go_nego_ssid(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] ssid = %s, len = %zu\n", __func__, extra, strlen(extra)); ++ memcpy(pwdinfo->nego_ssid, extra, strlen(extra)); ++ pwdinfo->nego_ssidlen = strlen(extra); ++ ++ return ret; ++} ++ ++static int rtw_p2p_set_intent(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ u8 intent = pwdinfo->intent; ++ ++ switch (wrqu->data.length) { ++ case 1: ++ intent = extra[0] - '0'; ++ break; ++ case 2: ++ intent = str_2char2num(extra[0], extra[1]); ++ break; ++ } ++ if (intent <= 15) ++ pwdinfo->intent = intent; ++ else ++ ret = -1; ++ DBG_88E("[%s] intent = %d\n", __func__, intent); ++ return ret; ++} ++ ++static int rtw_p2p_set_listen_ch(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ u8 listen_ch = pwdinfo->listen_channel; /* Listen channel number */ ++ ++ switch (wrqu->data.length) { ++ case 1: ++ listen_ch = extra[0] - '0'; ++ break; ++ case 2: ++ listen_ch = str_2char2num(extra[0], extra[1]); ++ break; ++ } ++ ++ if ((listen_ch == 1) || (listen_ch == 6) || (listen_ch == 11)) { ++ pwdinfo->listen_channel = listen_ch; ++ set_channel_bwmode(padapter, pwdinfo->listen_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); ++ } else { ++ ret = -1; ++ } ++ ++ DBG_88E("[%s] listen_ch = %d\n", __func__, pwdinfo->listen_channel); ++ ++ return ret; ++} ++ ++static int rtw_p2p_set_op_ch(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++/* Commented by Albert 20110524 */ ++/* This function is used to set the operating channel if the driver will become the group owner */ ++ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ u8 op_ch = pwdinfo->operating_channel; /* Operating channel number */ ++ ++ switch (wrqu->data.length) { ++ case 1: ++ op_ch = extra[0] - '0'; ++ break; ++ case 2: ++ op_ch = str_2char2num(extra[0], extra[1]); ++ break; ++ } ++ ++ if (op_ch > 0) ++ pwdinfo->operating_channel = op_ch; ++ else ++ ret = -1; ++ ++ DBG_88E("[%s] op_ch = %d\n", __func__, pwdinfo->operating_channel); ++ ++ return ret; ++} ++ ++static int rtw_p2p_profilefound(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ /* Comment by Albert 2010/10/13 */ ++ /* Input data format: */ ++ /* Ex: 0 */ ++ /* Ex: 1XX:XX:XX:XX:XX:XXYYSSID */ ++ /* 0 => Reflush the profile record list. */ ++ /* 1 => Add the profile list */ ++ /* XX:XX:XX:XX:XX:XX => peer's MAC Address (ex: 00:E0:4C:00:00:01) */ ++ /* YY => SSID Length */ ++ /* SSID => SSID for persistence group */ ++ ++ DBG_88E("[%s] In value = %s, len = %d\n", __func__, extra, wrqu->data.length - 1); ++ ++ /* The upper application should pass the SSID to driver by using this rtw_p2p_profilefound function. */ ++ if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) { ++ if (extra[0] == '0') { ++ /* Remove all the profile information of wifidirect_info structure. */ ++ memset(&pwdinfo->profileinfo[0], 0x00, sizeof(struct profile_info) * P2P_MAX_PERSISTENT_GROUP_NUM); ++ pwdinfo->profileindex = 0; ++ } else { ++ if (pwdinfo->profileindex >= P2P_MAX_PERSISTENT_GROUP_NUM) { ++ ret = -1; ++ } else { ++ int jj, kk; ++ ++ /* Add this profile information into pwdinfo->profileinfo */ ++ /* Ex: 1XX:XX:XX:XX:XX:XXYYSSID */ ++ for (jj = 0, kk = 1; jj < ETH_ALEN; jj++, kk += 3) ++ pwdinfo->profileinfo[pwdinfo->profileindex].peermac[jj] = key_2char2num(extra[kk], extra[kk + 1]); ++ ++ pwdinfo->profileinfo[pwdinfo->profileindex].ssidlen = (extra[18] - '0') * 10 + (extra[19] - '0'); ++ memcpy(pwdinfo->profileinfo[pwdinfo->profileindex].ssid, &extra[20], pwdinfo->profileinfo[pwdinfo->profileindex].ssidlen); ++ pwdinfo->profileindex++; ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++static int rtw_p2p_setDN(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] %s %d\n", __func__, extra, wrqu->data.length - 1); ++ memset(pwdinfo->device_name, 0x00, WPS_MAX_DEVICE_NAME_LEN); ++ memcpy(pwdinfo->device_name, extra, wrqu->data.length - 1); ++ pwdinfo->device_name_len = wrqu->data.length - 1; ++ ++ return ret; ++} ++ ++static int rtw_p2p_get_status(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ if (padapter->bShowGetP2PState) ++ DBG_88E("[%s] Role = %d, Status = %d, peer addr = %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n", __func__, rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo), ++ pwdinfo->p2p_peer_interface_addr[0], pwdinfo->p2p_peer_interface_addr[1], pwdinfo->p2p_peer_interface_addr[2], ++ pwdinfo->p2p_peer_interface_addr[3], pwdinfo->p2p_peer_interface_addr[4], pwdinfo->p2p_peer_interface_addr[5]); ++ ++ /* Commented by Albert 2010/10/12 */ ++ /* Because of the output size limitation, I had removed the "Role" information. */ ++ /* About the "Role" information, we will use the new private IOCTL to get the "Role" information. */ ++ sprintf(extra, "\n\nStatus =%.2d\n", rtw_p2p_state(pwdinfo)); ++ wrqu->data.length = strlen(extra); ++ ++ return ret; ++} ++ ++/* Commented by Albert 20110520 */ ++/* This function will return the config method description */ ++/* This config method description will show us which config method the remote P2P device is intended to use */ ++/* by sending the provisioning discovery request frame. */ ++ ++static int rtw_p2p_get_req_cm(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ sprintf(extra, "\n\nCM =%s\n", pwdinfo->rx_prov_disc_info.strconfig_method_desc_of_prov_disc_req); ++ wrqu->data.length = strlen(extra); ++ return ret; ++} ++ ++static int rtw_p2p_get_role(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] Role = %d, Status = %d, peer addr = %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n", __func__, rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo), ++ pwdinfo->p2p_peer_interface_addr[0], pwdinfo->p2p_peer_interface_addr[1], pwdinfo->p2p_peer_interface_addr[2], ++ pwdinfo->p2p_peer_interface_addr[3], pwdinfo->p2p_peer_interface_addr[4], pwdinfo->p2p_peer_interface_addr[5]); ++ ++ sprintf(extra, "\n\nRole =%.2d\n", rtw_p2p_role(pwdinfo)); ++ wrqu->data.length = strlen(extra); ++ return ret; ++} ++ ++static int rtw_p2p_get_peer_ifaddr(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] Role = %d, Status = %d, peer addr = %pM\n", __func__, ++ rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo), ++ pwdinfo->p2p_peer_interface_addr); ++ sprintf(extra, "\nMAC %pM", ++ pwdinfo->p2p_peer_interface_addr); ++ wrqu->data.length = strlen(extra); ++ return ret; ++} ++ ++static int rtw_p2p_get_peer_devaddr(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++ ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] Role = %d, Status = %d, peer addr = %pM\n", __func__, ++ rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo), ++ pwdinfo->rx_prov_disc_info.peerDevAddr); ++ sprintf(extra, "\n%pM", ++ pwdinfo->rx_prov_disc_info.peerDevAddr); ++ wrqu->data.length = strlen(extra); ++ return ret; ++} ++ ++static int rtw_p2p_get_peer_devaddr_by_invitation(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++ ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] Role = %d, Status = %d, peer addr = %pM\n", ++ __func__, rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo), ++ pwdinfo->p2p_peer_device_addr); ++ sprintf(extra, "\nMAC %pM", ++ pwdinfo->p2p_peer_device_addr); ++ wrqu->data.length = strlen(extra); ++ return ret; ++} ++ ++static int rtw_p2p_get_groupid(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++ ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ sprintf(extra, "\n%.2X:%.2X:%.2X:%.2X:%.2X:%.2X %s", ++ pwdinfo->groupid_info.go_device_addr[0], pwdinfo->groupid_info.go_device_addr[1], ++ pwdinfo->groupid_info.go_device_addr[2], pwdinfo->groupid_info.go_device_addr[3], ++ pwdinfo->groupid_info.go_device_addr[4], pwdinfo->groupid_info.go_device_addr[5], ++ pwdinfo->groupid_info.ssid); ++ wrqu->data.length = strlen(extra); ++ return ret; ++} ++ ++static int rtw_p2p_get_op_ch(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++ ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] Op_ch = %02x\n", __func__, pwdinfo->operating_channel); ++ ++ sprintf(extra, "\n\nOp_ch =%.2d\n", pwdinfo->operating_channel); ++ wrqu->data.length = strlen(extra); ++ return ret; ++} ++ ++static int rtw_p2p_get_wps_configmethod(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u8 peerMAC[ETH_ALEN] = {0x00}; ++ int jj, kk; ++ u8 peerMACStr[17] = {0x00}; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ u8 blnMatch = 0; ++ u16 attr_content = 0; ++ uint attr_contentlen = 0; ++ /* 6 is the string "wpsCM =", 17 is the MAC addr, we have to clear it at wrqu->data.pointer */ ++ u8 attr_content_str[6 + 17] = {0x00}; ++ ++ /* Commented by Albert 20110727 */ ++ /* The input data is the MAC address which the application wants to know its WPS config method. */ ++ /* After knowing its WPS config method, the application can decide the config method for provisioning discovery. */ ++ /* Format: iwpriv wlanx p2p_get_wpsCM 00:E0:4C:00:00:05 */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, (char *)extra); ++ if (copy_from_user(peerMACStr, wrqu->data.pointer + 6, 17)) ++ return -EFAULT; ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ peerMAC[jj] = key_2char2num(peerMACStr[kk], peerMACStr[kk + 1]); ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ if (!memcmp(pnetwork->network.MacAddress, peerMAC, ETH_ALEN)) { ++ u8 *wpsie; ++ uint wpsie_len = 0; ++ __be16 be_tmp; ++ ++ /* The mac address is matched. */ ++ wpsie = rtw_get_wps_ie(&pnetwork->network.IEs[12], pnetwork->network.IELength - 12, NULL, &wpsie_len); ++ if (wpsie) { ++ rtw_get_wps_attr_content(wpsie, wpsie_len, WPS_ATTR_CONF_METHOD, (u8 *) &be_tmp, &attr_contentlen); ++ if (attr_contentlen) { ++ attr_content = be16_to_cpu(be_tmp); ++ sprintf(attr_content_str, "\n\nM =%.4d", attr_content); ++ blnMatch = 1; ++ } ++ } ++ break; ++ } ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (!blnMatch) ++ sprintf(attr_content_str, "\n\nM = 0000"); ++ ++ if (copy_to_user(wrqu->data.pointer, attr_content_str, 6 + 17)) ++ return -EFAULT; ++ return ret; ++} ++ ++static int rtw_p2p_get_go_device_address(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u8 peerMAC[ETH_ALEN] = {0x00}; ++ int jj, kk; ++ u8 peerMACStr[17] = {0x00}; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ u8 blnMatch = 0; ++ u8 *p2pie; ++ uint p2pielen = 0, attr_contentlen = 0; ++ u8 attr_content[100] = {0x00}; ++ ++ u8 go_devadd_str[100 + 10] = {0x00}; ++ /* +10 is for the str "go_devadd =", we have to clear it at wrqu->data.pointer */ ++ ++ /* Commented by Albert 20121209 */ ++ /* The input data is the GO's interface address which the application wants to know its device address. */ ++ /* Format: iwpriv wlanx p2p_get2 go_devadd = 00:E0:4C:00:00:05 */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, (char *)extra); ++ if (copy_from_user(peerMACStr, wrqu->data.pointer + 10, 17)) ++ return -EFAULT; ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ peerMAC[jj] = key_2char2num(peerMACStr[kk], peerMACStr[kk + 1]); ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ if (!memcmp(pnetwork->network.MacAddress, peerMAC, ETH_ALEN)) { ++ /* Commented by Albert 2011/05/18 */ ++ /* Match the device address located in the P2P IE */ ++ /* This is for the case that the P2P device address is not the same as the P2P interface address. */ ++ ++ p2pie = rtw_get_p2p_ie(&pnetwork->network.IEs[12], pnetwork->network.IELength - 12, NULL, &p2pielen); ++ if (p2pie) { ++ while (p2pie) { ++ /* The P2P Device ID attribute is included in the Beacon frame. */ ++ /* The P2P Device Info attribute is included in the probe response frame. */ ++ ++ memset(attr_content, 0x00, 100); ++ if (rtw_get_p2p_attr_content(p2pie, p2pielen, P2P_ATTR_DEVICE_ID, attr_content, &attr_contentlen)) { ++ /* Handle the P2P Device ID attribute of Beacon first */ ++ blnMatch = 1; ++ break; ++ } else if (rtw_get_p2p_attr_content(p2pie, p2pielen, P2P_ATTR_DEVICE_INFO, attr_content, &attr_contentlen)) { ++ /* Handle the P2P Device Info attribute of probe response */ ++ blnMatch = 1; ++ break; ++ } ++ ++ /* Get the next P2P IE */ ++ p2pie = rtw_get_p2p_ie(p2pie+p2pielen, pnetwork->network.IELength - 12 - (p2pie - &pnetwork->network.IEs[12] + p2pielen), NULL, &p2pielen); ++ } ++ } ++ } ++ ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (!blnMatch) ++ sprintf(go_devadd_str, "\n\ndev_add = NULL"); ++ else ++ sprintf(go_devadd_str, "\ndev_add =%.2X:%.2X:%.2X:%.2X:%.2X:%.2X", ++ attr_content[0], attr_content[1], attr_content[2], attr_content[3], attr_content[4], attr_content[5]); ++ ++ if (copy_to_user(wrqu->data.pointer, go_devadd_str, 10 + 17)) ++ return -EFAULT; ++ return ret; ++} ++ ++static int rtw_p2p_get_device_type(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u8 peerMAC[ETH_ALEN] = {0x00}; ++ int jj, kk; ++ u8 peerMACStr[17] = {0x00}; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ u8 blnMatch = 0; ++ u8 dev_type[8] = {0x00}; ++ uint dev_type_len = 0; ++ u8 dev_type_str[17 + 9] = {0x00}; /* +9 is for the str "dev_type =", we have to clear it at wrqu->data.pointer */ ++ ++ /* Commented by Albert 20121209 */ ++ /* The input data is the MAC address which the application wants to know its device type. */ ++ /* Such user interface could know the device type. */ ++ /* Format: iwpriv wlanx p2p_get2 dev_type = 00:E0:4C:00:00:05 */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, (char *)extra); ++ if (copy_from_user(peerMACStr, wrqu->data.pointer + 9, 17)) ++ return -EFAULT; ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ peerMAC[jj] = key_2char2num(peerMACStr[kk], peerMACStr[kk + 1]); ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ if (!memcmp(pnetwork->network.MacAddress, peerMAC, ETH_ALEN)) { ++ u8 *wpsie; ++ uint wpsie_len = 0; ++ ++ /* The mac address is matched. */ ++ ++ wpsie = rtw_get_wps_ie(&pnetwork->network.IEs[12], ++ pnetwork->network.IELength - 12, ++ NULL, &wpsie_len); ++ if (wpsie) { ++ rtw_get_wps_attr_content(wpsie, wpsie_len, WPS_ATTR_PRIMARY_DEV_TYPE, dev_type, &dev_type_len); ++ if (dev_type_len) { ++ u16 type = 0; ++ __be16 be_tmp; ++ ++ memcpy(&be_tmp, dev_type, 2); ++ type = be16_to_cpu(be_tmp); ++ sprintf(dev_type_str, "\n\nN =%.2d", type); ++ blnMatch = 1; ++ } ++ } ++ break; ++ } ++ ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (!blnMatch) ++ sprintf(dev_type_str, "\n\nN = 00"); ++ ++ if (copy_to_user(wrqu->data.pointer, dev_type_str, 9 + 17)) { ++ return -EFAULT; ++ } ++ ++ return ret; ++} ++ ++static int rtw_p2p_get_device_name(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u8 peerMAC[ETH_ALEN] = {0x00}; ++ int jj, kk; ++ u8 peerMACStr[17] = {0x00}; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ u8 blnMatch = 0; ++ u8 dev_name[WPS_MAX_DEVICE_NAME_LEN] = {0x00}; ++ uint dev_len = 0; ++ u8 dev_name_str[WPS_MAX_DEVICE_NAME_LEN + 5] = {0x00}; /* +5 is for the str "devN =", we have to clear it at wrqu->data.pointer */ ++ ++ /* Commented by Albert 20121225 */ ++ /* The input data is the MAC address which the application wants to know its device name. */ ++ /* Such user interface could show peer device's device name instead of ssid. */ ++ /* Format: iwpriv wlanx p2p_get2 devN = 00:E0:4C:00:00:05 */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, (char *)extra); ++ if (copy_from_user(peerMACStr, wrqu->data.pointer + 5, 17)) ++ return -EFAULT; ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ peerMAC[jj] = key_2char2num(peerMACStr[kk], peerMACStr[kk + 1]); ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ if (!memcmp(pnetwork->network.MacAddress, peerMAC, ETH_ALEN)) { ++ u8 *wpsie; ++ uint wpsie_len = 0; ++ ++ /* The mac address is matched. */ ++ wpsie = rtw_get_wps_ie(&pnetwork->network.IEs[12], pnetwork->network.IELength - 12, NULL, &wpsie_len); ++ if (wpsie) { ++ rtw_get_wps_attr_content(wpsie, wpsie_len, WPS_ATTR_DEVICE_NAME, dev_name, &dev_len); ++ if (dev_len) { ++ sprintf(dev_name_str, "\n\nN =%s", dev_name); ++ blnMatch = 1; ++ } ++ } ++ break; ++ } ++ ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (!blnMatch) ++ sprintf(dev_name_str, "\n\nN = 0000"); ++ ++ if (copy_to_user(wrqu->data.pointer, dev_name_str, 5 + ((dev_len > 17) ? dev_len : 17))) ++ return -EFAULT; ++ return ret; ++} ++ ++static int rtw_p2p_get_invitation_procedure(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ u8 peerMAC[ETH_ALEN] = {0x00}; ++ int jj, kk; ++ u8 peerMACStr[17] = {0x00}; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ u8 blnMatch = 0; ++ u8 *p2pie; ++ uint p2pielen = 0, attr_contentlen = 0; ++ u8 attr_content[2] = {0x00}; ++ ++ u8 inv_proc_str[17 + 8] = {0x00}; ++ /* +8 is for the str "InvProc =", we have to clear it at wrqu->data.pointer */ ++ ++ /* Commented by Ouden 20121226 */ ++ /* The application wants to know P2P initiation procedure is supported or not. */ ++ /* Format: iwpriv wlanx p2p_get2 InvProc = 00:E0:4C:00:00:05 */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, (char *)extra); ++ if (copy_from_user(peerMACStr, wrqu->data.pointer + 8, 17)) ++ return -EFAULT; ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ peerMAC[jj] = key_2char2num(peerMACStr[kk], peerMACStr[kk + 1]); ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ if (!memcmp(pnetwork->network.MacAddress, peerMAC, ETH_ALEN)) { ++ /* Commented by Albert 20121226 */ ++ /* Match the device address located in the P2P IE */ ++ /* This is for the case that the P2P device address is not the same as the P2P interface address. */ ++ ++ p2pie = rtw_get_p2p_ie(&pnetwork->network.IEs[12], pnetwork->network.IELength - 12, NULL, &p2pielen); ++ if (p2pie) { ++ while (p2pie) { ++ if (rtw_get_p2p_attr_content(p2pie, p2pielen, P2P_ATTR_CAPABILITY, attr_content, &attr_contentlen)) { ++ /* Handle the P2P capability attribute */ ++ blnMatch = 1; ++ break; ++ } ++ ++ /* Get the next P2P IE */ ++ p2pie = rtw_get_p2p_ie(p2pie+p2pielen, pnetwork->network.IELength - 12 - (p2pie - &pnetwork->network.IEs[12] + p2pielen), NULL, &p2pielen); ++ } ++ } ++ } ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (!blnMatch) { ++ sprintf(inv_proc_str, "\nIP =-1"); ++ } else { ++ if (attr_content[0] & 0x20) ++ sprintf(inv_proc_str, "\nIP = 1"); ++ else ++ sprintf(inv_proc_str, "\nIP = 0"); ++ } ++ if (copy_to_user(wrqu->data.pointer, inv_proc_str, 8 + 17)) ++ return -EFAULT; ++ return ret; ++} ++ ++static int rtw_p2p_connect(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ u8 peerMAC[ETH_ALEN] = {0x00}; ++ int jj, kk; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ uint uintPeerChannel = 0; ++ ++ /* Commented by Albert 20110304 */ ++ /* The input data contains two informations. */ ++ /* 1. First information is the MAC address which wants to formate with */ ++ /* 2. Second information is the WPS PINCode or "pbc" string for push button method */ ++ /* Format: 00:E0:4C:00:00:05 */ ++ /* Format: 00:E0:4C:00:00:05 */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, extra); ++ ++ if (pwdinfo->p2p_state == P2P_STATE_NONE) { ++ DBG_88E("[%s] WiFi Direct is disable!\n", __func__); ++ return ret; ++ } ++ ++ if (pwdinfo->ui_got_wps_info == P2P_NO_WPSINFO) ++ return -1; ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ peerMAC[jj] = key_2char2num(extra[kk], extra[kk + 1]); ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ if (!memcmp(pnetwork->network.MacAddress, peerMAC, ETH_ALEN)) { ++ uintPeerChannel = pnetwork->network.Configuration.DSConfig; ++ break; ++ } ++ ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (uintPeerChannel) { ++ memset(&pwdinfo->nego_req_info, 0x00, sizeof(struct tx_nego_req_info)); ++ memset(&pwdinfo->groupid_info, 0x00, sizeof(struct group_id_info)); ++ ++ pwdinfo->nego_req_info.peer_channel_num[0] = uintPeerChannel; ++ memcpy(pwdinfo->nego_req_info.peerDevAddr, pnetwork->network.MacAddress, ETH_ALEN); ++ pwdinfo->nego_req_info.benable = true; ++ ++ _cancel_timer_ex(&pwdinfo->restore_p2p_state_timer); ++ if (rtw_p2p_state(pwdinfo) != P2P_STATE_GONEGO_OK) { ++ /* Restore to the listen state if the current p2p state is not nego OK */ ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_LISTEN); ++ } ++ ++ rtw_p2p_set_pre_state(pwdinfo, rtw_p2p_state(pwdinfo)); ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_GONEGO_ING); ++ ++ DBG_88E("[%s] Start PreTx Procedure!\n", __func__); ++ _set_timer(&pwdinfo->pre_tx_scan_timer, P2P_TX_PRESCAN_TIMEOUT); ++ _set_timer(&pwdinfo->restore_p2p_state_timer, P2P_GO_NEGO_TIMEOUT); ++ } else { ++ DBG_88E("[%s] Not Found in Scanning Queue~\n", __func__); ++ ret = -1; ++ } ++ return ret; ++} ++ ++static int rtw_p2p_invite_req(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ int jj, kk; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ uint uintPeerChannel = 0; ++ u8 attr_content[50] = {0x00}; ++ u8 *p2pie; ++ uint p2pielen = 0, attr_contentlen = 0; ++ struct tx_invite_req_info *pinvite_req_info = &pwdinfo->invitereq_info; ++ ++ /* The input data contains two informations. */ ++ /* 1. First information is the P2P device address which you want to send to. */ ++ /* 2. Second information is the group id which combines with GO's mac address, space and GO's ssid. */ ++ /* Command line sample: iwpriv wlan0 p2p_set invite ="00:11:22:33:44:55 00:E0:4C:00:00:05 DIRECT-xy" */ ++ /* Format: 00:11:22:33:44:55 00:E0:4C:00:00:05 DIRECT-xy */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, extra); ++ ++ if (wrqu->data.length <= 37) { ++ DBG_88E("[%s] Wrong format!\n", __func__); ++ return ret; ++ } ++ ++ if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) { ++ DBG_88E("[%s] WiFi Direct is disable!\n", __func__); ++ return ret; ++ } else { ++ /* Reset the content of struct tx_invite_req_info */ ++ pinvite_req_info->benable = false; ++ memset(pinvite_req_info->go_bssid, 0x00, ETH_ALEN); ++ memset(pinvite_req_info->go_ssid, 0x00, WLAN_SSID_MAXLEN); ++ pinvite_req_info->ssidlen = 0x00; ++ pinvite_req_info->operating_ch = pwdinfo->operating_channel; ++ memset(pinvite_req_info->peer_macaddr, 0x00, ETH_ALEN); ++ pinvite_req_info->token = 3; ++ } ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ pinvite_req_info->peer_macaddr[jj] = key_2char2num(extra[kk], extra[kk + 1]); ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ pnetwork = container_of(plist, struct wlan_network, list); ++ ++ /* Commented by Albert 2011/05/18 */ ++ /* Match the device address located in the P2P IE */ ++ /* This is for the case that the P2P device address is not the same as the P2P interface address. */ ++ ++ p2pie = rtw_get_p2p_ie(&pnetwork->network.IEs[12], pnetwork->network.IELength - 12, NULL, &p2pielen); ++ if (p2pie) { ++ /* The P2P Device ID attribute is included in the Beacon frame. */ ++ /* The P2P Device Info attribute is included in the probe response frame. */ ++ ++ if (rtw_get_p2p_attr_content(p2pie, p2pielen, P2P_ATTR_DEVICE_ID, attr_content, &attr_contentlen)) { ++ /* Handle the P2P Device ID attribute of Beacon first */ ++ if (!memcmp(attr_content, pinvite_req_info->peer_macaddr, ETH_ALEN)) { ++ uintPeerChannel = pnetwork->network.Configuration.DSConfig; ++ break; ++ } ++ } else if (rtw_get_p2p_attr_content(p2pie, p2pielen, P2P_ATTR_DEVICE_INFO, attr_content, &attr_contentlen)) { ++ /* Handle the P2P Device Info attribute of probe response */ ++ if (!memcmp(attr_content, pinvite_req_info->peer_macaddr, ETH_ALEN)) { ++ uintPeerChannel = pnetwork->network.Configuration.DSConfig; ++ break; ++ } ++ } ++ } ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (uintPeerChannel) { ++ /* Store the GO's bssid */ ++ for (jj = 0, kk = 18; jj < ETH_ALEN; jj++, kk += 3) ++ pinvite_req_info->go_bssid[jj] = key_2char2num(extra[kk], extra[kk + 1]); ++ ++ /* Store the GO's ssid */ ++ pinvite_req_info->ssidlen = wrqu->data.length - 36; ++ memcpy(pinvite_req_info->go_ssid, &extra[36], (u32) pinvite_req_info->ssidlen); ++ pinvite_req_info->benable = true; ++ pinvite_req_info->peer_ch = uintPeerChannel; ++ ++ rtw_p2p_set_pre_state(pwdinfo, rtw_p2p_state(pwdinfo)); ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_TX_INVITE_REQ); ++ ++ set_channel_bwmode(padapter, uintPeerChannel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); ++ ++ _set_timer(&pwdinfo->pre_tx_scan_timer, P2P_TX_PRESCAN_TIMEOUT); ++ ++ _set_timer(&pwdinfo->restore_p2p_state_timer, P2P_INVITE_TIMEOUT); ++ } else { ++ DBG_88E("[%s] NOT Found in the Scanning Queue!\n", __func__); ++ } ++ return ret; ++} ++ ++static int rtw_p2p_set_persistent(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ /* The input data is 0 or 1 */ ++ /* 0: disable persistent group functionality */ ++ /* 1: enable persistent group founctionality */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, extra); ++ ++ if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) { ++ DBG_88E("[%s] WiFi Direct is disable!\n", __func__); ++ return ret; ++ } else { ++ if (extra[0] == '0') /* Disable the persistent group function. */ ++ pwdinfo->persistent_supported = false; ++ else if (extra[0] == '1') /* Enable the persistent group function. */ ++ pwdinfo->persistent_supported = true; ++ else ++ pwdinfo->persistent_supported = false; ++ } ++ pr_info("[%s] persistent_supported = %d\n", __func__, pwdinfo->persistent_supported); ++ return ret; ++} ++ ++static int rtw_p2p_prov_disc(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ u8 peerMAC[ETH_ALEN] = {0x00}; ++ int jj, kk; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct list_head *plist, *phead; ++ struct __queue *queue = &(pmlmepriv->scanned_queue); ++ struct wlan_network *pnetwork = NULL; ++ uint uintPeerChannel = 0; ++ u8 attr_content[100] = {0x00}; ++ u8 *p2pie; ++ uint p2pielen = 0, attr_contentlen = 0; ++ ++ /* The input data contains two informations. */ ++ /* 1. First information is the MAC address which wants to issue the provisioning discovery request frame. */ ++ /* 2. Second information is the WPS configuration method which wants to discovery */ ++ /* Format: 00:E0:4C:00:00:05_display */ ++ /* Format: 00:E0:4C:00:00:05_keypad */ ++ /* Format: 00:E0:4C:00:00:05_pbc */ ++ /* Format: 00:E0:4C:00:00:05_label */ ++ ++ DBG_88E("[%s] data = %s\n", __func__, extra); ++ ++ if (pwdinfo->p2p_state == P2P_STATE_NONE) { ++ DBG_88E("[%s] WiFi Direct is disable!\n", __func__); ++ return ret; ++ } else { ++ /* Reset the content of struct tx_provdisc_req_info excluded the wps_config_method_request. */ ++ memset(pwdinfo->tx_prov_disc_info.peerDevAddr, 0x00, ETH_ALEN); ++ memset(pwdinfo->tx_prov_disc_info.peerIFAddr, 0x00, ETH_ALEN); ++ memset(&pwdinfo->tx_prov_disc_info.ssid, 0x00, sizeof(struct ndis_802_11_ssid)); ++ pwdinfo->tx_prov_disc_info.peer_channel_num[0] = 0; ++ pwdinfo->tx_prov_disc_info.peer_channel_num[1] = 0; ++ pwdinfo->tx_prov_disc_info.benable = false; ++ } ++ ++ for (jj = 0, kk = 0; jj < ETH_ALEN; jj++, kk += 3) ++ peerMAC[jj] = key_2char2num(extra[kk], extra[kk + 1]); ++ ++ if (!memcmp(&extra[18], "display", 7)) { ++ pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_DISPLYA; ++ } else if (!memcmp(&extra[18], "keypad", 7)) { ++ pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_KEYPAD; ++ } else if (!memcmp(&extra[18], "pbc", 3)) { ++ pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_PUSH_BUTTON; ++ } else if (!memcmp(&extra[18], "label", 5)) { ++ pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_LABEL; ++ } else { ++ DBG_88E("[%s] Unknown WPS config methodn", __func__); ++ return ret; ++ } ++ ++ spin_lock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ phead = get_list_head(queue); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ if (uintPeerChannel != 0) ++ break; ++ ++ pnetwork = container_of(plist, struct wlan_network, list); ++ ++ /* Commented by Albert 2011/05/18 */ ++ /* Match the device address located in the P2P IE */ ++ /* This is for the case that the P2P device address is not the same as the P2P interface address. */ ++ ++ p2pie = rtw_get_p2p_ie(&pnetwork->network.IEs[12], pnetwork->network.IELength - 12, NULL, &p2pielen); ++ if (p2pie) { ++ while (p2pie) { ++ /* The P2P Device ID attribute is included in the Beacon frame. */ ++ /* The P2P Device Info attribute is included in the probe response frame. */ ++ ++ if (rtw_get_p2p_attr_content(p2pie, p2pielen, P2P_ATTR_DEVICE_ID, attr_content, &attr_contentlen)) { ++ /* Handle the P2P Device ID attribute of Beacon first */ ++ if (!memcmp(attr_content, peerMAC, ETH_ALEN)) { ++ uintPeerChannel = pnetwork->network.Configuration.DSConfig; ++ break; ++ } ++ } else if (rtw_get_p2p_attr_content(p2pie, p2pielen, P2P_ATTR_DEVICE_INFO, attr_content, &attr_contentlen)) { ++ /* Handle the P2P Device Info attribute of probe response */ ++ if (!memcmp(attr_content, peerMAC, ETH_ALEN)) { ++ uintPeerChannel = pnetwork->network.Configuration.DSConfig; ++ break; ++ } ++ } ++ ++ /* Get the next P2P IE */ ++ p2pie = rtw_get_p2p_ie(p2pie+p2pielen, pnetwork->network.IELength - 12 - (p2pie - &pnetwork->network.IEs[12] + p2pielen), NULL, &p2pielen); ++ } ++ } ++ ++ plist = plist->next; ++ } ++ ++ spin_unlock_bh(&pmlmepriv->scanned_queue.lock); ++ ++ if (uintPeerChannel) { ++ DBG_88E("[%s] peer channel: %d!\n", __func__, uintPeerChannel); ++ memcpy(pwdinfo->tx_prov_disc_info.peerIFAddr, pnetwork->network.MacAddress, ETH_ALEN); ++ memcpy(pwdinfo->tx_prov_disc_info.peerDevAddr, peerMAC, ETH_ALEN); ++ pwdinfo->tx_prov_disc_info.peer_channel_num[0] = (u16) uintPeerChannel; ++ pwdinfo->tx_prov_disc_info.benable = true; ++ rtw_p2p_set_pre_state(pwdinfo, rtw_p2p_state(pwdinfo)); ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_TX_PROVISION_DIS_REQ); ++ ++ if (rtw_p2p_chk_role(pwdinfo, P2P_ROLE_CLIENT)) { ++ memcpy(&pwdinfo->tx_prov_disc_info.ssid, &pnetwork->network.Ssid, sizeof(struct ndis_802_11_ssid)); ++ } else if (rtw_p2p_chk_role(pwdinfo, P2P_ROLE_DEVICE) || rtw_p2p_chk_role(pwdinfo, P2P_ROLE_GO)) { ++ memcpy(pwdinfo->tx_prov_disc_info.ssid.Ssid, pwdinfo->p2p_wildcard_ssid, P2P_WILDCARD_SSID_LEN); ++ pwdinfo->tx_prov_disc_info.ssid.SsidLength = P2P_WILDCARD_SSID_LEN; ++ } ++ ++ set_channel_bwmode(padapter, uintPeerChannel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); ++ ++ _set_timer(&pwdinfo->pre_tx_scan_timer, P2P_TX_PRESCAN_TIMEOUT); ++ ++ _set_timer(&pwdinfo->restore_p2p_state_timer, P2P_PROVISION_TIMEOUT); ++ } else { ++ DBG_88E("[%s] NOT Found in the Scanning Queue!\n", __func__); ++ } ++ return ret; ++} ++ ++/* This function is used to inform the driver the user had specified the pin code value or pbc */ ++/* to application. */ ++ ++static int rtw_p2p_got_wpsinfo(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct wifidirect_info *pwdinfo = &(padapter->wdinfo); ++ ++ DBG_88E("[%s] data = %s\n", __func__, extra); ++ /* Added by Albert 20110328 */ ++ /* if the input data is P2P_NO_WPSINFO -> reset the wpsinfo */ ++ /* if the input data is P2P_GOT_WPSINFO_PEER_DISPLAY_PIN -> the utility just input the PIN code got from the peer P2P device. */ ++ /* if the input data is P2P_GOT_WPSINFO_SELF_DISPLAY_PIN -> the utility just got the PIN code from itself. */ ++ /* if the input data is P2P_GOT_WPSINFO_PBC -> the utility just determine to use the PBC */ ++ ++ if (*extra == '0') ++ pwdinfo->ui_got_wps_info = P2P_NO_WPSINFO; ++ else if (*extra == '1') ++ pwdinfo->ui_got_wps_info = P2P_GOT_WPSINFO_PEER_DISPLAY_PIN; ++ else if (*extra == '2') ++ pwdinfo->ui_got_wps_info = P2P_GOT_WPSINFO_SELF_DISPLAY_PIN; ++ else if (*extra == '3') ++ pwdinfo->ui_got_wps_info = P2P_GOT_WPSINFO_PBC; ++ else ++ pwdinfo->ui_got_wps_info = P2P_NO_WPSINFO; ++ return ret; ++} ++ ++#endif /* CONFIG_88EU_P2P */ ++ ++static int rtw_p2p_set(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ ++#ifdef CONFIG_88EU_P2P ++ DBG_88E("[%s] extra = %s\n", __func__, extra); ++ if (!memcmp(extra, "enable =", 7)) { ++ rtw_wext_p2p_enable(dev, info, wrqu, &extra[7]); ++ } else if (!memcmp(extra, "setDN =", 6)) { ++ wrqu->data.length -= 6; ++ rtw_p2p_setDN(dev, info, wrqu, &extra[6]); ++ } else if (!memcmp(extra, "profilefound =", 13)) { ++ wrqu->data.length -= 13; ++ rtw_p2p_profilefound(dev, info, wrqu, &extra[13]); ++ } else if (!memcmp(extra, "prov_disc =", 10)) { ++ wrqu->data.length -= 10; ++ rtw_p2p_prov_disc(dev, info, wrqu, &extra[10]); ++ } else if (!memcmp(extra, "nego =", 5)) { ++ wrqu->data.length -= 5; ++ rtw_p2p_connect(dev, info, wrqu, &extra[5]); ++ } else if (!memcmp(extra, "intent =", 7)) { ++ /* Commented by Albert 2011/03/23 */ ++ /* The wrqu->data.length will include the null character */ ++ /* So, we will decrease 7 + 1 */ ++ wrqu->data.length -= 8; ++ rtw_p2p_set_intent(dev, info, wrqu, &extra[7]); ++ } else if (!memcmp(extra, "ssid =", 5)) { ++ wrqu->data.length -= 5; ++ rtw_p2p_set_go_nego_ssid(dev, info, wrqu, &extra[5]); ++ } else if (!memcmp(extra, "got_wpsinfo =", 12)) { ++ wrqu->data.length -= 12; ++ rtw_p2p_got_wpsinfo(dev, info, wrqu, &extra[12]); ++ } else if (!memcmp(extra, "listen_ch =", 10)) { ++ /* Commented by Albert 2011/05/24 */ ++ /* The wrqu->data.length will include the null character */ ++ /* So, we will decrease (10 + 1) */ ++ wrqu->data.length -= 11; ++ rtw_p2p_set_listen_ch(dev, info, wrqu, &extra[10]); ++ } else if (!memcmp(extra, "op_ch =", 6)) { ++ /* Commented by Albert 2011/05/24 */ ++ /* The wrqu->data.length will include the null character */ ++ /* So, we will decrease (6 + 1) */ ++ wrqu->data.length -= 7; ++ rtw_p2p_set_op_ch(dev, info, wrqu, &extra[6]); ++ } else if (!memcmp(extra, "invite =", 7)) { ++ wrqu->data.length -= 8; ++ rtw_p2p_invite_req(dev, info, wrqu, &extra[7]); ++ } else if (!memcmp(extra, "persistent =", 11)) { ++ wrqu->data.length -= 11; ++ rtw_p2p_set_persistent(dev, info, wrqu, &extra[11]); ++ } ++#endif /* CONFIG_88EU_P2P */ ++ ++ return ret; ++} ++ ++static int rtw_p2p_get(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ ++#ifdef CONFIG_88EU_P2P ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ if (padapter->bShowGetP2PState) ++ DBG_88E("[%s] extra = %s\n", __func__, (char *)wrqu->data.pointer); ++ if (!memcmp(wrqu->data.pointer, "status", 6)) { ++ rtw_p2p_get_status(dev, info, wrqu, extra); ++ } else if (!memcmp(wrqu->data.pointer, "role", 4)) { ++ rtw_p2p_get_role(dev, info, wrqu, extra); ++ } else if (!memcmp(wrqu->data.pointer, "peer_ifa", 8)) { ++ rtw_p2p_get_peer_ifaddr(dev, info, wrqu, extra); ++ } else if (!memcmp(wrqu->data.pointer, "req_cm", 6)) { ++ rtw_p2p_get_req_cm(dev, info, wrqu, extra); ++ } else if (!memcmp(wrqu->data.pointer, "peer_deva", 9)) { ++ /* Get the P2P device address when receiving the provision discovery request frame. */ ++ rtw_p2p_get_peer_devaddr(dev, info, wrqu, extra); ++ } else if (!memcmp(wrqu->data.pointer, "group_id", 8)) { ++ rtw_p2p_get_groupid(dev, info, wrqu, extra); ++ } else if (!memcmp(wrqu->data.pointer, "peer_deva_inv", 9)) { ++ /* Get the P2P device address when receiving the P2P Invitation request frame. */ ++ rtw_p2p_get_peer_devaddr_by_invitation(dev, info, wrqu, extra); ++ } else if (!memcmp(wrqu->data.pointer, "op_ch", 5)) { ++ rtw_p2p_get_op_ch(dev, info, wrqu, extra); ++ } ++#endif /* CONFIG_88EU_P2P */ ++ return ret; ++} ++ ++static int rtw_p2p_get2(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ ++#ifdef CONFIG_88EU_P2P ++ DBG_88E("[%s] extra = %s\n", __func__, (char *)wrqu->data.pointer); ++ if (!memcmp(extra, "wpsCM =", 6)) { ++ wrqu->data.length -= 6; ++ rtw_p2p_get_wps_configmethod(dev, info, wrqu, &extra[6]); ++ } else if (!memcmp(extra, "devN =", 5)) { ++ wrqu->data.length -= 5; ++ rtw_p2p_get_device_name(dev, info, wrqu, &extra[5]); ++ } else if (!memcmp(extra, "dev_type =", 9)) { ++ wrqu->data.length -= 9; ++ rtw_p2p_get_device_type(dev, info, wrqu, &extra[9]); ++ } else if (!memcmp(extra, "go_devadd =", 10)) { ++ wrqu->data.length -= 10; ++ rtw_p2p_get_go_device_address(dev, info, wrqu, &extra[10]); ++ } else if (!memcmp(extra, "InvProc =", 8)) { ++ wrqu->data.length -= 8; ++ rtw_p2p_get_invitation_procedure(dev, info, wrqu, &extra[8]); ++ } ++ ++#endif /* CONFIG_88EU_P2P */ ++ ++ return ret; ++} ++ ++static int rtw_cta_test_start(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ DBG_88E("%s %s\n", __func__, extra); ++ if (!strcmp(extra, "1")) ++ padapter->in_cta_test = 1; ++ else ++ padapter->in_cta_test = 0; ++ ++ if (padapter->in_cta_test) { ++ u32 v = rtw_read32(padapter, REG_RCR); ++ v &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN);/* RCR_ADF */ ++ rtw_write32(padapter, REG_RCR, v); ++ DBG_88E("enable RCR_ADF\n"); ++ } else { ++ u32 v = rtw_read32(padapter, REG_RCR); ++ v |= RCR_CBSSID_DATA | RCR_CBSSID_BCN;/* RCR_ADF */ ++ rtw_write32(padapter, REG_RCR, v); ++ DBG_88E("disable RCR_ADF\n"); ++ } ++ return ret; ++} ++ ++static int rtw_rereg_nd_name(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ struct rereg_nd_name_data *rereg_priv = &padapter->rereg_nd_name_priv; ++ char new_ifname[IFNAMSIZ]; ++ ++ if (rereg_priv->old_ifname[0] == 0) { ++ char *reg_ifname; ++ reg_ifname = padapter->registrypriv.if2name; ++ ++ strncpy(rereg_priv->old_ifname, reg_ifname, IFNAMSIZ); ++ rereg_priv->old_ifname[IFNAMSIZ-1] = 0; ++ } ++ ++ if (wrqu->data.length > IFNAMSIZ) ++ return -EFAULT; ++ ++ if (copy_from_user(new_ifname, wrqu->data.pointer, IFNAMSIZ)) ++ return -EFAULT; ++ ++ if (0 == strcmp(rereg_priv->old_ifname, new_ifname)) ++ return ret; ++ ++ DBG_88E("%s new_ifname:%s\n", __func__, new_ifname); ++ ret = rtw_change_ifname(padapter, new_ifname); ++ if (0 != ret) ++ goto exit; ++ ++ if (!memcmp(rereg_priv->old_ifname, "disable%d", 9)) { ++ padapter->ledpriv.bRegUseLed = rereg_priv->old_bRegUseLed; ++ rtw_hal_sw_led_init(padapter); ++ rtw_ips_mode_req(&padapter->pwrctrlpriv, rereg_priv->old_ips_mode); ++ } ++ ++ strncpy(rereg_priv->old_ifname, new_ifname, IFNAMSIZ); ++ rereg_priv->old_ifname[IFNAMSIZ-1] = 0; ++ ++ if (!memcmp(new_ifname, "disable%d", 9)) { ++ DBG_88E("%s disable\n", __func__); ++ /* free network queue for Android's timming issue */ ++ rtw_free_network_queue(padapter, true); ++ ++ /* close led */ ++ rtw_led_control(padapter, LED_CTL_POWER_OFF); ++ rereg_priv->old_bRegUseLed = padapter->ledpriv.bRegUseLed; ++ padapter->ledpriv.bRegUseLed = false; ++ rtw_hal_sw_led_deinit(padapter); ++ ++ /* the interface is being "disabled", we can do deeper IPS */ ++ rereg_priv->old_ips_mode = rtw_get_ips_mode_req(&padapter->pwrctrlpriv); ++ rtw_ips_mode_req(&padapter->pwrctrlpriv, IPS_NORMAL); ++ } ++exit: ++ return ret; ++} ++ ++static void mac_reg_dump(struct adapter *padapter) ++{ ++ int i, j = 1; ++ pr_info("\n ======= MAC REG =======\n"); ++ for (i = 0x0; i < 0x300; i += 4) { ++ if (j%4 == 1) ++ pr_info("0x%02x", i); ++ pr_info(" 0x%08x ", rtw_read32(padapter, i)); ++ if ((j++)%4 == 0) ++ pr_info("\n"); ++ } ++ for (i = 0x400; i < 0x800; i += 4) { ++ if (j%4 == 1) ++ pr_info("0x%02x", i); ++ pr_info(" 0x%08x ", rtw_read32(padapter, i)); ++ if ((j++)%4 == 0) ++ pr_info("\n"); ++ } ++} ++ ++static void bb_reg_dump(struct adapter *padapter) ++{ ++ int i, j = 1; ++ pr_info("\n ======= BB REG =======\n"); ++ for (i = 0x800; i < 0x1000; i += 4) { ++ if (j%4 == 1) ++ pr_info("0x%02x", i); ++ ++ pr_info(" 0x%08x ", rtw_read32(padapter, i)); ++ if ((j++)%4 == 0) ++ pr_info("\n"); ++ } ++} ++ ++static void rf_reg_dump(struct adapter *padapter) ++{ ++ int i, j = 1, path; ++ u32 value; ++ u8 rf_type, path_nums = 0; ++ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type)); ++ ++ pr_info("\n ======= RF REG =======\n"); ++ if ((RF_1T2R == rf_type) || (RF_1T1R == rf_type)) ++ path_nums = 1; ++ else ++ path_nums = 2; ++ ++ for (path = 0; path < path_nums; path++) { ++ pr_info("\nRF_Path(%x)\n", path); ++ for (i = 0; i < 0x100; i++) { ++ value = rtw_hal_read_rfreg(padapter, path, i, 0xffffffff); ++ if (j%4 == 1) ++ pr_info("0x%02x ", i); ++ pr_info(" 0x%08x ", value); ++ if ((j++)%4 == 0) ++ pr_info("\n"); ++ } ++ } ++} ++ ++static int rtw_dbg_port(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ u8 major_cmd, minor_cmd; ++ u16 arg; ++ s32 extra_arg; ++ u32 *pdata, val32; ++ struct sta_info *psta; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; ++ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); ++ struct security_priv *psecuritypriv = &padapter->securitypriv; ++ struct wlan_network *cur_network = &(pmlmepriv->cur_network); ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ ++ pdata = (u32 *)&wrqu->data; ++ ++ val32 = *pdata; ++ arg = (u16)(val32 & 0x0000ffff); ++ major_cmd = (u8)(val32 >> 24); ++ minor_cmd = (u8)((val32 >> 16) & 0x00ff); ++ ++ extra_arg = *(pdata+1); ++ ++ switch (major_cmd) { ++ case 0x70:/* read_reg */ ++ switch (minor_cmd) { ++ case 1: ++ DBG_88E("rtw_read8(0x%x) = 0x%02x\n", arg, rtw_read8(padapter, arg)); ++ break; ++ case 2: ++ DBG_88E("rtw_read16(0x%x) = 0x%04x\n", arg, rtw_read16(padapter, arg)); ++ break; ++ case 4: ++ DBG_88E("rtw_read32(0x%x) = 0x%08x\n", arg, rtw_read32(padapter, arg)); ++ break; ++ } ++ break; ++ case 0x71:/* write_reg */ ++ switch (minor_cmd) { ++ case 1: ++ rtw_write8(padapter, arg, extra_arg); ++ DBG_88E("rtw_write8(0x%x) = 0x%02x\n", arg, rtw_read8(padapter, arg)); ++ break; ++ case 2: ++ rtw_write16(padapter, arg, extra_arg); ++ DBG_88E("rtw_write16(0x%x) = 0x%04x\n", arg, rtw_read16(padapter, arg)); ++ break; ++ case 4: ++ rtw_write32(padapter, arg, extra_arg); ++ DBG_88E("rtw_write32(0x%x) = 0x%08x\n", arg, rtw_read32(padapter, arg)); ++ break; ++ } ++ break; ++ case 0x72:/* read_bb */ ++ DBG_88E("read_bbreg(0x%x) = 0x%x\n", arg, rtw_hal_read_bbreg(padapter, arg, 0xffffffff)); ++ break; ++ case 0x73:/* write_bb */ ++ rtw_hal_write_bbreg(padapter, arg, 0xffffffff, extra_arg); ++ DBG_88E("write_bbreg(0x%x) = 0x%x\n", arg, rtw_hal_read_bbreg(padapter, arg, 0xffffffff)); ++ break; ++ case 0x74:/* read_rf */ ++ DBG_88E("read RF_reg path(0x%02x), offset(0x%x), value(0x%08x)\n", minor_cmd, arg, rtw_hal_read_rfreg(padapter, minor_cmd, arg, 0xffffffff)); ++ break; ++ case 0x75:/* write_rf */ ++ rtw_hal_write_rfreg(padapter, minor_cmd, arg, 0xffffffff, extra_arg); ++ DBG_88E("write RF_reg path(0x%02x), offset(0x%x), value(0x%08x)\n", minor_cmd, arg, rtw_hal_read_rfreg(padapter, minor_cmd, arg, 0xffffffff)); ++ break; ++ ++ case 0x76: ++ switch (minor_cmd) { ++ case 0x00: /* normal mode, */ ++ padapter->recvpriv.is_signal_dbg = 0; ++ break; ++ case 0x01: /* dbg mode */ ++ padapter->recvpriv.is_signal_dbg = 1; ++ extra_arg = extra_arg > 100 ? 100 : extra_arg; ++ extra_arg = extra_arg < 0 ? 0 : extra_arg; ++ padapter->recvpriv.signal_strength_dbg = extra_arg; ++ break; ++ } ++ break; ++ case 0x78: /* IOL test */ ++ switch (minor_cmd) { ++ case 0x04: /* LLT table initialization test */ ++ { ++ u8 page_boundary = 0xf9; ++ struct xmit_frame *xmit_frame; ++ ++ xmit_frame = rtw_IOL_accquire_xmit_frame(padapter); ++ if (xmit_frame == NULL) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ rtw_IOL_append_LLT_cmd(xmit_frame, page_boundary); ++ ++ if (_SUCCESS != rtw_IOL_exec_cmds_sync(padapter, xmit_frame, 500, 0)) ++ ret = -EPERM; ++ } ++ break; ++ case 0x05: /* blink LED test */ ++ { ++ u16 reg = 0x4c; ++ u32 blink_num = 50; ++ u32 blink_delay_ms = 200; ++ int i; ++ struct xmit_frame *xmit_frame; ++ ++ xmit_frame = rtw_IOL_accquire_xmit_frame(padapter); ++ if (xmit_frame == NULL) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ for (i = 0; i < blink_num; i++) { ++ rtw_IOL_append_WB_cmd(xmit_frame, reg, 0x00, 0xff); ++ rtw_IOL_append_DELAY_MS_cmd(xmit_frame, blink_delay_ms); ++ rtw_IOL_append_WB_cmd(xmit_frame, reg, 0x08, 0xff); ++ rtw_IOL_append_DELAY_MS_cmd(xmit_frame, blink_delay_ms); ++ } ++ if (_SUCCESS != rtw_IOL_exec_cmds_sync(padapter, xmit_frame, (blink_delay_ms*blink_num*2)+200, 0)) ++ ret = -EPERM; ++ } ++ break; ++ ++ case 0x06: /* continuous write byte test */ ++ { ++ u16 reg = arg; ++ u16 start_value = 0; ++ u32 write_num = extra_arg; ++ int i; ++ u8 final; ++ struct xmit_frame *xmit_frame; ++ ++ xmit_frame = rtw_IOL_accquire_xmit_frame(padapter); ++ if (xmit_frame == NULL) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ for (i = 0; i < write_num; i++) ++ rtw_IOL_append_WB_cmd(xmit_frame, reg, i+start_value, 0xFF); ++ if (_SUCCESS != rtw_IOL_exec_cmds_sync(padapter, xmit_frame, 5000, 0)) ++ ret = -EPERM; ++ ++ final = rtw_read8(padapter, reg); ++ if (start_value+write_num-1 == final) ++ DBG_88E("continuous IOL_CMD_WB_REG to 0x%x %u times Success, start:%u, final:%u\n", reg, write_num, start_value, final); ++ else ++ DBG_88E("continuous IOL_CMD_WB_REG to 0x%x %u times Fail, start:%u, final:%u\n", reg, write_num, start_value, final); ++ } ++ break; ++ ++ case 0x07: /* continuous write word test */ ++ { ++ u16 reg = arg; ++ u16 start_value = 200; ++ u32 write_num = extra_arg; ++ ++ int i; ++ u16 final; ++ struct xmit_frame *xmit_frame; ++ ++ xmit_frame = rtw_IOL_accquire_xmit_frame(padapter); ++ if (xmit_frame == NULL) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ for (i = 0; i < write_num; i++) ++ rtw_IOL_append_WW_cmd(xmit_frame, reg, i+start_value, 0xFFFF); ++ if (_SUCCESS != rtw_IOL_exec_cmds_sync(padapter, xmit_frame, 5000, 0)) ++ ret = -EPERM; ++ ++ final = rtw_read16(padapter, reg); ++ if (start_value+write_num-1 == final) ++ DBG_88E("continuous IOL_CMD_WW_REG to 0x%x %u times Success, start:%u, final:%u\n", reg, write_num, start_value, final); ++ else ++ DBG_88E("continuous IOL_CMD_WW_REG to 0x%x %u times Fail, start:%u, final:%u\n", reg, write_num, start_value, final); ++ } ++ break; ++ case 0x08: /* continuous write dword test */ ++ { ++ u16 reg = arg; ++ u32 start_value = 0x110000c7; ++ u32 write_num = extra_arg; ++ ++ int i; ++ u32 final; ++ struct xmit_frame *xmit_frame; ++ ++ xmit_frame = rtw_IOL_accquire_xmit_frame(padapter); ++ if (xmit_frame == NULL) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ for (i = 0; i < write_num; i++) ++ rtw_IOL_append_WD_cmd(xmit_frame, reg, i+start_value, 0xFFFFFFFF); ++ if (_SUCCESS != rtw_IOL_exec_cmds_sync(padapter, xmit_frame, 5000, 0)) ++ ret = -EPERM; ++ ++ final = rtw_read32(padapter, reg); ++ if (start_value+write_num-1 == final) ++ DBG_88E("continuous IOL_CMD_WD_REG to 0x%x %u times Success, start:%u, final:%u\n", ++ reg, write_num, start_value, final); ++ else ++ DBG_88E("continuous IOL_CMD_WD_REG to 0x%x %u times Fail, start:%u, final:%u\n", ++ reg, write_num, start_value, final); ++ } ++ break; ++ } ++ break; ++ case 0x79: ++ { ++ /* ++ * dbg 0x79000000 [value], set RESP_TXAGC to + value, value:0~15 ++ * dbg 0x79010000 [value], set RESP_TXAGC to - value, value:0~15 ++ */ ++ u8 value = extra_arg & 0x0f; ++ u8 sign = minor_cmd; ++ u16 write_value = 0; ++ ++ DBG_88E("%s set RESP_TXAGC to %s %u\n", __func__, sign ? "minus" : "plus", value); ++ ++ if (sign) ++ value = value | 0x10; ++ ++ write_value = value | (value << 5); ++ rtw_write16(padapter, 0x6d9, write_value); ++ } ++ break; ++ case 0x7a: ++ receive_disconnect(padapter, pmlmeinfo->network.MacAddress ++ , WLAN_REASON_EXPIRATION_CHK); ++ break; ++ case 0x7F: ++ switch (minor_cmd) { ++ case 0x0: ++ DBG_88E("fwstate = 0x%x\n", get_fwstate(pmlmepriv)); ++ break; ++ case 0x01: ++ DBG_88E("auth_alg = 0x%x, enc_alg = 0x%x, auth_type = 0x%x, enc_type = 0x%x\n", ++ psecuritypriv->dot11AuthAlgrthm, psecuritypriv->dot11PrivacyAlgrthm, ++ psecuritypriv->ndisauthtype, psecuritypriv->ndisencryptstatus); ++ break; ++ case 0x02: ++ DBG_88E("pmlmeinfo->state = 0x%x\n", pmlmeinfo->state); ++ break; ++ case 0x03: ++ DBG_88E("qos_option =%d\n", pmlmepriv->qospriv.qos_option); ++ DBG_88E("ht_option =%d\n", pmlmepriv->htpriv.ht_option); ++ break; ++ case 0x04: ++ DBG_88E("cur_ch =%d\n", pmlmeext->cur_channel); ++ DBG_88E("cur_bw =%d\n", pmlmeext->cur_bwmode); ++ DBG_88E("cur_ch_off =%d\n", pmlmeext->cur_ch_offset); ++ break; ++ case 0x05: ++ psta = rtw_get_stainfo(pstapriv, cur_network->network.MacAddress); ++ if (psta) { ++ int i; ++ struct recv_reorder_ctrl *preorder_ctrl; ++ ++ DBG_88E("SSID =%s\n", cur_network->network.Ssid.Ssid); ++ DBG_88E("sta's macaddr: %pM\n", psta->hwaddr); ++ DBG_88E("cur_channel =%d, cur_bwmode =%d, cur_ch_offset =%d\n", pmlmeext->cur_channel, pmlmeext->cur_bwmode, pmlmeext->cur_ch_offset); ++ DBG_88E("rtsen =%d, cts2slef =%d\n", psta->rtsen, psta->cts2self); ++ DBG_88E("state = 0x%x, aid =%d, macid =%d, raid =%d\n", psta->state, psta->aid, psta->mac_id, psta->raid); ++ DBG_88E("qos_en =%d, ht_en =%d, init_rate =%d\n", psta->qos_option, psta->htpriv.ht_option, psta->init_rate); ++ DBG_88E("bwmode =%d, ch_offset =%d, sgi =%d\n", psta->htpriv.bwmode, psta->htpriv.ch_offset, psta->htpriv.sgi); ++ DBG_88E("ampdu_enable = %d\n", psta->htpriv.ampdu_enable); ++ DBG_88E("agg_enable_bitmap =%x, candidate_tid_bitmap =%x\n", psta->htpriv.agg_enable_bitmap, psta->htpriv.candidate_tid_bitmap); ++ for (i = 0; i < 16; i++) { ++ preorder_ctrl = &psta->recvreorder_ctrl[i]; ++ if (preorder_ctrl->enable) ++ DBG_88E("tid =%d, indicate_seq =%d\n", i, preorder_ctrl->indicate_seq); ++ } ++ } else { ++ DBG_88E("can't get sta's macaddr, cur_network's macaddr:%pM\n", (cur_network->network.MacAddress)); ++ } ++ break; ++ case 0x06: ++ { ++ u32 ODMFlag; ++ rtw_hal_get_hwreg(padapter, HW_VAR_DM_FLAG, (u8 *)(&ODMFlag)); ++ DBG_88E("(B)DMFlag = 0x%x, arg = 0x%x\n", ODMFlag, arg); ++ ODMFlag = (u32)(0x0f&arg); ++ DBG_88E("(A)DMFlag = 0x%x\n", ODMFlag); ++ rtw_hal_set_hwreg(padapter, HW_VAR_DM_FLAG, (u8 *)(&ODMFlag)); ++ } ++ break; ++ case 0x07: ++ DBG_88E("bSurpriseRemoved =%d, bDriverStopped =%d\n", ++ padapter->bSurpriseRemoved, padapter->bDriverStopped); ++ break; ++ case 0x08: ++ { ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ struct recv_priv *precvpriv = &padapter->recvpriv; ++ ++ DBG_88E("free_xmitbuf_cnt =%d, free_xmitframe_cnt =%d, free_xmit_extbuf_cnt =%d\n", ++ pxmitpriv->free_xmitbuf_cnt, pxmitpriv->free_xmitframe_cnt, pxmitpriv->free_xmit_extbuf_cnt); ++ DBG_88E("rx_urb_pending_cn =%d\n", precvpriv->rx_pending_cnt); ++ } ++ break; ++ case 0x09: ++ { ++ int i, j; ++ struct list_head *plist, *phead; ++ struct recv_reorder_ctrl *preorder_ctrl; ++ ++#ifdef CONFIG_88EU_AP_MODE ++ DBG_88E("sta_dz_bitmap = 0x%x, tim_bitmap = 0x%x\n", pstapriv->sta_dz_bitmap, pstapriv->tim_bitmap); ++#endif ++ spin_lock_bh(&pstapriv->sta_hash_lock); ++ ++ for (i = 0; i < NUM_STA; i++) { ++ phead = &(pstapriv->sta_hash[i]); ++ plist = phead->next; ++ ++ while (phead != plist) { ++ psta = container_of(plist, struct sta_info, hash_list); ++ ++ plist = plist->next; ++ ++ if (extra_arg == psta->aid) { ++ DBG_88E("sta's macaddr:%pM\n", (psta->hwaddr)); ++ DBG_88E("rtsen =%d, cts2slef =%d\n", psta->rtsen, psta->cts2self); ++ DBG_88E("state = 0x%x, aid =%d, macid =%d, raid =%d\n", psta->state, psta->aid, psta->mac_id, psta->raid); ++ DBG_88E("qos_en =%d, ht_en =%d, init_rate =%d\n", psta->qos_option, psta->htpriv.ht_option, psta->init_rate); ++ DBG_88E("bwmode =%d, ch_offset =%d, sgi =%d\n", psta->htpriv.bwmode, psta->htpriv.ch_offset, psta->htpriv.sgi); ++ DBG_88E("ampdu_enable = %d\n", psta->htpriv.ampdu_enable); ++ DBG_88E("agg_enable_bitmap =%x, candidate_tid_bitmap =%x\n", psta->htpriv.agg_enable_bitmap, psta->htpriv.candidate_tid_bitmap); ++ ++#ifdef CONFIG_88EU_AP_MODE ++ DBG_88E("capability = 0x%x\n", psta->capability); ++ DBG_88E("flags = 0x%x\n", psta->flags); ++ DBG_88E("wpa_psk = 0x%x\n", psta->wpa_psk); ++ DBG_88E("wpa2_group_cipher = 0x%x\n", psta->wpa2_group_cipher); ++ DBG_88E("wpa2_pairwise_cipher = 0x%x\n", psta->wpa2_pairwise_cipher); ++ DBG_88E("qos_info = 0x%x\n", psta->qos_info); ++#endif ++ DBG_88E("dot118021XPrivacy = 0x%x\n", psta->dot118021XPrivacy); ++ ++ for (j = 0; j < 16; j++) { ++ preorder_ctrl = &psta->recvreorder_ctrl[j]; ++ if (preorder_ctrl->enable) ++ DBG_88E("tid =%d, indicate_seq =%d\n", j, preorder_ctrl->indicate_seq); ++ } ++ } ++ } ++ } ++ spin_unlock_bh(&pstapriv->sta_hash_lock); ++ } ++ break; ++ case 0x0c:/* dump rx/tx packet */ ++ if (arg == 0) { ++ DBG_88E("dump rx packet (%d)\n", extra_arg); ++ rtw_hal_set_def_var(padapter, HAL_DEF_DBG_DUMP_RXPKT, &(extra_arg)); ++ } else if (arg == 1) { ++ DBG_88E("dump tx packet (%d)\n", extra_arg); ++ rtw_hal_set_def_var(padapter, HAL_DEF_DBG_DUMP_TXPKT, &(extra_arg)); ++ } ++ break; ++ case 0x0f: ++ if (extra_arg == 0) { ++ DBG_88E("###### silent reset test.......#####\n"); ++ rtw_hal_sreset_reset(padapter); ++ } ++ break; ++ case 0x15: ++ { ++ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; ++ DBG_88E("==>silent resete cnts:%d\n", pwrpriv->ips_enter_cnts); ++ } ++ break; ++ case 0x10:/* driver version display */ ++ DBG_88E("rtw driver version =%s\n", DRIVERVERSION); ++ break; ++ case 0x11: ++ DBG_88E("turn %s Rx RSSI display function\n", (extra_arg == 1) ? "on" : "off"); ++ padapter->bRxRSSIDisplay = extra_arg; ++ rtw_hal_set_def_var(padapter, HW_DEF_FA_CNT_DUMP, &extra_arg); ++ break; ++ case 0x12: /* set rx_stbc */ ++ { ++ struct registry_priv *pregpriv = &padapter->registrypriv; ++ /* 0: disable, bit(0):enable 2.4g, bit(1):enable 5g, 0x3: enable both 2.4g and 5g */ ++ /* default is set to enable 2.4GHZ for IOT issue with bufflao's AP at 5GHZ */ ++ if (pregpriv && ++ (extra_arg == 0 || ++ extra_arg == 1 || ++ extra_arg == 2 || ++ extra_arg == 3)) { ++ pregpriv->rx_stbc = extra_arg; ++ DBG_88E("set rx_stbc =%d\n", pregpriv->rx_stbc); ++ } else { ++ DBG_88E("get rx_stbc =%d\n", pregpriv->rx_stbc); ++ } ++ } ++ break; ++ case 0x13: /* set ampdu_enable */ ++ { ++ struct registry_priv *pregpriv = &padapter->registrypriv; ++ /* 0: disable, 0x1:enable (but wifi_spec should be 0), 0x2: force enable (don't care wifi_spec) */ ++ if (pregpriv && extra_arg >= 0 && extra_arg < 3) { ++ pregpriv->ampdu_enable = extra_arg; ++ DBG_88E("set ampdu_enable =%d\n", pregpriv->ampdu_enable); ++ } else { ++ DBG_88E("get ampdu_enable =%d\n", pregpriv->ampdu_enable); ++ } ++ } ++ break; ++ case 0x14: /* get wifi_spec */ ++ { ++ struct registry_priv *pregpriv = &padapter->registrypriv; ++ DBG_88E("get wifi_spec =%d\n", pregpriv->wifi_spec); ++ } ++ break; ++ case 0x16: ++ if (arg == 0xff) { ++ pr_info("ODM_COMP_DIG\t\tBIT0\n"); ++ pr_info("ODM_COMP_RA_MASK\t\tBIT1\n"); ++ pr_info("ODM_COMP_DYNAMIC_TXPWR\tBIT2\n"); ++ pr_info("ODM_COMP_FA_CNT\t\tBIT3\n"); ++ pr_info("ODM_COMP_RSSI_MONITOR\tBIT4\n"); ++ pr_info("ODM_COMP_CCK_PD\t\tBIT5\n"); ++ pr_info("ODM_COMP_ANT_DIV\t\tBIT6\n"); ++ pr_info("ODM_COMP_PWR_SAVE\t\tBIT7\n"); ++ pr_info("ODM_COMP_PWR_TRAIN\tBIT8\n"); ++ pr_info("ODM_COMP_RATE_ADAPTIVE\tBIT9\n"); ++ pr_info("ODM_COMP_PATH_DIV\t\tBIT10\n"); ++ pr_info("ODM_COMP_PSD \tBIT11\n"); ++ pr_info("ODM_COMP_DYNAMIC_PRICCA\tBIT12\n"); ++ pr_info("ODM_COMP_RXHP\t\tBIT13\n"); ++ pr_info("ODM_COMP_EDCA_TURBO\tBIT16\n"); ++ pr_info("ODM_COMP_EARLY_MODE\tBIT17\n"); ++ pr_info("ODM_COMP_TX_PWR_TRACK\tBIT24\n"); ++ pr_info("ODM_COMP_RX_GAIN_TRACK\tBIT25\n"); ++ pr_info("ODM_COMP_CALIBRATION\tBIT26\n"); ++ rtw_hal_get_def_var(padapter, HW_DEF_ODM_DBG_FLAG, &extra_arg); ++ } else { ++ rtw_hal_set_def_var(padapter, HW_DEF_ODM_DBG_FLAG, &extra_arg); ++ } ++ break; ++ case 0x23: ++ DBG_88E("turn %s the bNotifyChannelChange Variable\n", (extra_arg == 1) ? "on" : "off"); ++ padapter->bNotifyChannelChange = extra_arg; ++ break; ++ case 0x24: ++#ifdef CONFIG_88EU_P2P ++ DBG_88E("turn %s the bShowGetP2PState Variable\n", (extra_arg == 1) ? "on" : "off"); ++ padapter->bShowGetP2PState = extra_arg; ++#endif /* CONFIG_88EU_P2P */ ++ break; ++ case 0xaa: ++ if (extra_arg > 0x13) ++ extra_arg = 0xFF; ++ DBG_88E("chang data rate to :0x%02x\n", extra_arg); ++ padapter->fix_rate = extra_arg; ++ break; ++ case 0xdd:/* registers dump, 0 for mac reg, 1 for bb reg, 2 for rf reg */ ++ if (extra_arg == 0) ++ mac_reg_dump(padapter); ++ else if (extra_arg == 1) ++ bb_reg_dump(padapter); ++ else if (extra_arg == 2) ++ rf_reg_dump(padapter); ++ break; ++ case 0xee:/* turn on/off dynamic funcs */ ++ { ++ u32 odm_flag; ++ ++ if (0xf == extra_arg) { ++ rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DM_FUNC, &odm_flag); ++ DBG_88E(" === DMFlag(0x%08x) ===\n", odm_flag); ++ DBG_88E("extra_arg = 0 - disable all dynamic func\n"); ++ DBG_88E("extra_arg = 1 - disable DIG- BIT(0)\n"); ++ DBG_88E("extra_arg = 2 - disable High power - BIT(1)\n"); ++ DBG_88E("extra_arg = 3 - disable tx power tracking - BIT(2)\n"); ++ DBG_88E("extra_arg = 4 - disable BT coexistence - BIT(3)\n"); ++ DBG_88E("extra_arg = 5 - disable antenna diversity - BIT(4)\n"); ++ DBG_88E("extra_arg = 6 - enable all dynamic func\n"); ++ } else { ++ /* extra_arg = 0 - disable all dynamic func ++ extra_arg = 1 - disable DIG ++ extra_arg = 2 - disable tx power tracking ++ extra_arg = 3 - turn on all dynamic func ++ */ ++ rtw_hal_set_def_var(padapter, HAL_DEF_DBG_DM_FUNC, &(extra_arg)); ++ rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DM_FUNC, &odm_flag); ++ DBG_88E(" === DMFlag(0x%08x) ===\n", odm_flag); ++ } ++ } ++ break; ++ ++ case 0xfd: ++ rtw_write8(padapter, 0xc50, arg); ++ DBG_88E("wr(0xc50) = 0x%x\n", rtw_read8(padapter, 0xc50)); ++ rtw_write8(padapter, 0xc58, arg); ++ DBG_88E("wr(0xc58) = 0x%x\n", rtw_read8(padapter, 0xc58)); ++ break; ++ case 0xfe: ++ DBG_88E("rd(0xc50) = 0x%x\n", rtw_read8(padapter, 0xc50)); ++ DBG_88E("rd(0xc58) = 0x%x\n", rtw_read8(padapter, 0xc58)); ++ break; ++ case 0xff: ++ DBG_88E("dbg(0x210) = 0x%x\n", rtw_read32(padapter, 0x210)); ++ DBG_88E("dbg(0x608) = 0x%x\n", rtw_read32(padapter, 0x608)); ++ DBG_88E("dbg(0x280) = 0x%x\n", rtw_read32(padapter, 0x280)); ++ DBG_88E("dbg(0x284) = 0x%x\n", rtw_read32(padapter, 0x284)); ++ DBG_88E("dbg(0x288) = 0x%x\n", rtw_read32(padapter, 0x288)); ++ ++ DBG_88E("dbg(0x664) = 0x%x\n", rtw_read32(padapter, 0x664)); ++ ++ DBG_88E("\n"); ++ ++ DBG_88E("dbg(0x430) = 0x%x\n", rtw_read32(padapter, 0x430)); ++ DBG_88E("dbg(0x438) = 0x%x\n", rtw_read32(padapter, 0x438)); ++ ++ DBG_88E("dbg(0x440) = 0x%x\n", rtw_read32(padapter, 0x440)); ++ ++ DBG_88E("dbg(0x458) = 0x%x\n", rtw_read32(padapter, 0x458)); ++ ++ DBG_88E("dbg(0x484) = 0x%x\n", rtw_read32(padapter, 0x484)); ++ DBG_88E("dbg(0x488) = 0x%x\n", rtw_read32(padapter, 0x488)); ++ ++ DBG_88E("dbg(0x444) = 0x%x\n", rtw_read32(padapter, 0x444)); ++ DBG_88E("dbg(0x448) = 0x%x\n", rtw_read32(padapter, 0x448)); ++ DBG_88E("dbg(0x44c) = 0x%x\n", rtw_read32(padapter, 0x44c)); ++ DBG_88E("dbg(0x450) = 0x%x\n", rtw_read32(padapter, 0x450)); ++ break; ++ } ++ break; ++ default: ++ DBG_88E("error dbg cmd!\n"); ++ break; ++ } ++ return ret; ++} ++ ++static int wpa_set_param(struct net_device *dev, u8 name, u32 value) ++{ ++ uint ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ switch (name) { ++ case IEEE_PARAM_WPA_ENABLED: ++ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; /* 802.1x */ ++ switch ((value)&0xff) { ++ case 1: /* WPA */ ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; /* WPA_PSK */ ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; ++ break; ++ case 2: /* WPA2 */ ++ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; /* WPA2_PSK */ ++ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled; ++ break; ++ } ++ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ++ ("wpa_set_param:padapter->securitypriv.ndisauthtype =%d\n", padapter->securitypriv.ndisauthtype)); ++ break; ++ case IEEE_PARAM_TKIP_COUNTERMEASURES: ++ break; ++ case IEEE_PARAM_DROP_UNENCRYPTED: { ++ /* HACK: ++ * ++ * wpa_supplicant calls set_wpa_enabled when the driver ++ * is loaded and unloaded, regardless of if WPA is being ++ * used. No other calls are made which can be used to ++ * determine if encryption will be used or not prior to ++ * association being expected. If encryption is not being ++ * used, drop_unencrypted is set to false, else true -- we ++ * can use this to determine if the CAP_PRIVACY_ON bit should ++ * be set. ++ */ ++ ++ break; ++ } ++ case IEEE_PARAM_PRIVACY_INVOKED: ++ break; ++ ++ case IEEE_PARAM_AUTH_ALGS: ++ ret = wpa_set_auth_algs(dev, value); ++ break; ++ case IEEE_PARAM_IEEE_802_1X: ++ break; ++ case IEEE_PARAM_WPAX_SELECT: ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ return ret; ++} ++ ++static int wpa_mlme(struct net_device *dev, u32 command, u32 reason) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ switch (command) { ++ case IEEE_MLME_STA_DEAUTH: ++ if (!rtw_set_802_11_disassociate(padapter)) ++ ret = -1; ++ break; ++ case IEEE_MLME_STA_DISASSOC: ++ if (!rtw_set_802_11_disassociate(padapter)) ++ ret = -1; ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p) ++{ ++ struct ieee_param *param; ++ uint ret = 0; ++ ++ if (p->length < sizeof(struct ieee_param) || !p->pointer) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ param = (struct ieee_param *)rtw_malloc(p->length); ++ if (param == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (copy_from_user(param, p->pointer, p->length)) { ++ kfree(param); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ switch (param->cmd) { ++ case IEEE_CMD_SET_WPA_PARAM: ++ ret = wpa_set_param(dev, param->u.wpa_param.name, param->u.wpa_param.value); ++ break; ++ ++ case IEEE_CMD_SET_WPA_IE: ++ ret = rtw_set_wpa_ie((struct adapter *)rtw_netdev_priv(dev), ++ (char *)param->u.wpa_ie.data, (u16)param->u.wpa_ie.len); ++ break; ++ ++ case IEEE_CMD_SET_ENCRYPTION: ++ ret = wpa_set_encryption(dev, param, p->length); ++ break; ++ ++ case IEEE_CMD_MLME: ++ ret = wpa_mlme(dev, param->u.mlme.command, param->u.mlme.reason_code); ++ break; ++ ++ default: ++ DBG_88E("Unknown WPA supplicant request: %d\n", param->cmd); ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ if (ret == 0 && copy_to_user(p->pointer, param, p->length)) ++ ret = -EFAULT; ++ ++ kfree(param); ++ ++out: ++ ++ return ret; ++} ++ ++#ifdef CONFIG_88EU_AP_MODE ++static u8 set_pairwise_key(struct adapter *padapter, struct sta_info *psta) ++{ ++ struct cmd_obj *ph2c; ++ struct set_stakey_parm *psetstakey_para; ++ struct cmd_priv *pcmdpriv = &padapter->cmdpriv; ++ u8 res = _SUCCESS; ++ ++ ph2c = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj)); ++ if (ph2c == NULL) { ++ res = _FAIL; ++ goto exit; ++ } ++ ++ psetstakey_para = (struct set_stakey_parm *)rtw_zmalloc(sizeof(struct set_stakey_parm)); ++ if (psetstakey_para == NULL) { ++ kfree(ph2c); ++ res = _FAIL; ++ goto exit; ++ } ++ ++ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_); ++ ++ psetstakey_para->algorithm = (u8)psta->dot118021XPrivacy; ++ ++ memcpy(psetstakey_para->addr, psta->hwaddr, ETH_ALEN); ++ ++ memcpy(psetstakey_para->key, &psta->dot118021x_UncstKey, 16); ++ ++ res = rtw_enqueue_cmd(pcmdpriv, ph2c); ++ ++exit: ++ ++ return res; ++} ++ ++static int set_group_key(struct adapter *padapter, u8 *key, u8 alg, int keyid) ++{ ++ u8 keylen; ++ struct cmd_obj *pcmd; ++ struct setkey_parm *psetkeyparm; ++ struct cmd_priv *pcmdpriv = &(padapter->cmdpriv); ++ int res = _SUCCESS; ++ ++ DBG_88E("%s\n", __func__); ++ ++ pcmd = (struct cmd_obj *)rtw_zmalloc(sizeof(struct cmd_obj)); ++ if (pcmd == NULL) { ++ res = _FAIL; ++ goto exit; ++ } ++ psetkeyparm = (struct setkey_parm *)rtw_zmalloc(sizeof(struct setkey_parm)); ++ if (psetkeyparm == NULL) { ++ kfree(pcmd); ++ res = _FAIL; ++ goto exit; ++ } ++ ++ memset(psetkeyparm, 0, sizeof(struct setkey_parm)); ++ ++ psetkeyparm->keyid = (u8)keyid; ++ ++ psetkeyparm->algorithm = alg; ++ ++ psetkeyparm->set_tx = 1; ++ ++ switch (alg) { ++ case _WEP40_: ++ keylen = 5; ++ break; ++ case _WEP104_: ++ keylen = 13; ++ break; ++ case _TKIP_: ++ case _TKIP_WTMIC_: ++ case _AES_: ++ default: ++ keylen = 16; ++ } ++ ++ memcpy(&(psetkeyparm->key[0]), key, keylen); ++ ++ pcmd->cmdcode = _SetKey_CMD_; ++ pcmd->parmbuf = (u8 *)psetkeyparm; ++ pcmd->cmdsz = (sizeof(struct setkey_parm)); ++ pcmd->rsp = NULL; ++ pcmd->rspsz = 0; ++ ++ INIT_LIST_HEAD(&pcmd->list); ++ ++ res = rtw_enqueue_cmd(pcmdpriv, pcmd); ++ ++exit: ++ ++ return res; ++} ++ ++static int set_wep_key(struct adapter *padapter, u8 *key, u8 keylen, int keyid) ++{ ++ u8 alg; ++ ++ switch (keylen) { ++ case 5: ++ alg = _WEP40_; ++ break; ++ case 13: ++ alg = _WEP104_; ++ break; ++ default: ++ alg = _NO_PRIVACY_; ++ } ++ ++ return set_group_key(padapter, key, alg, keyid); ++} ++ ++static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, u32 param_len) ++{ ++ int ret = 0; ++ u32 wep_key_idx, wep_key_len, wep_total_len; ++ struct ndis_802_11_wep *pwep = NULL; ++ struct sta_info *psta = NULL, *pbcmc_sta = NULL; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct security_priv *psecuritypriv = &(padapter->securitypriv); ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ ++ DBG_88E("%s\n", __func__); ++ param->u.crypt.err = 0; ++ param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0'; ++ if (param_len != sizeof(struct ieee_param) + param->u.crypt.key_len) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { ++ if (param->u.crypt.idx >= WEP_KEYS) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ } else { ++ psta = rtw_get_stainfo(pstapriv, param->sta_addr); ++ if (!psta) { ++ DBG_88E("rtw_set_encryption(), sta has already been removed or never been added\n"); ++ goto exit; ++ } ++ } ++ ++ if (strcmp(param->u.crypt.alg, "none") == 0 && (psta == NULL)) { ++ /* todo:clear default encryption keys */ ++ ++ DBG_88E("clear default encryption keys, keyid =%d\n", param->u.crypt.idx); ++ goto exit; ++ } ++ if (strcmp(param->u.crypt.alg, "WEP") == 0 && (psta == NULL)) { ++ DBG_88E("r871x_set_encryption, crypt.alg = WEP\n"); ++ wep_key_idx = param->u.crypt.idx; ++ wep_key_len = param->u.crypt.key_len; ++ DBG_88E("r871x_set_encryption, wep_key_idx=%d, len=%d\n", wep_key_idx, wep_key_len); ++ if ((wep_key_idx >= WEP_KEYS) || (wep_key_len <= 0)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ if (wep_key_len > 0) { ++ wep_key_len = wep_key_len <= 5 ? 5 : 13; ++ wep_total_len = wep_key_len + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial); ++ pwep = (struct ndis_802_11_wep *)rtw_malloc(wep_total_len); ++ if (pwep == NULL) { ++ DBG_88E(" r871x_set_encryption: pwep allocate fail !!!\n"); ++ goto exit; ++ } ++ ++ memset(pwep, 0, wep_total_len); ++ ++ pwep->KeyLength = wep_key_len; ++ pwep->Length = wep_total_len; ++ } ++ ++ pwep->KeyIndex = wep_key_idx; ++ ++ memcpy(pwep->KeyMaterial, param->u.crypt.key, pwep->KeyLength); ++ ++ if (param->u.crypt.set_tx) { ++ DBG_88E("wep, set_tx = 1\n"); ++ ++ psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled; ++ psecuritypriv->dot11PrivacyAlgrthm = _WEP40_; ++ psecuritypriv->dot118021XGrpPrivacy = _WEP40_; ++ ++ if (pwep->KeyLength == 13) { ++ psecuritypriv->dot11PrivacyAlgrthm = _WEP104_; ++ psecuritypriv->dot118021XGrpPrivacy = _WEP104_; ++ } ++ ++ psecuritypriv->dot11PrivacyKeyIndex = wep_key_idx; ++ ++ memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength); ++ ++ psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength; ++ ++ set_wep_key(padapter, pwep->KeyMaterial, pwep->KeyLength, wep_key_idx); ++ } else { ++ DBG_88E("wep, set_tx = 0\n"); ++ ++ /* don't update "psecuritypriv->dot11PrivacyAlgrthm" and */ ++ /* psecuritypriv->dot11PrivacyKeyIndex = keyid", but can rtw_set_key to cam */ ++ ++ memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength); ++ ++ psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength; ++ ++ set_wep_key(padapter, pwep->KeyMaterial, pwep->KeyLength, wep_key_idx); ++ } ++ ++ goto exit; ++ } ++ ++ if (!psta && check_fwstate(pmlmepriv, WIFI_AP_STATE)) { /* group key */ ++ if (param->u.crypt.set_tx == 1) { ++ if (strcmp(param->u.crypt.alg, "WEP") == 0) { ++ DBG_88E("%s, set group_key, WEP\n", __func__); ++ ++ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, ++ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ ++ psecuritypriv->dot118021XGrpPrivacy = _WEP40_; ++ if (param->u.crypt.key_len == 13) ++ psecuritypriv->dot118021XGrpPrivacy = _WEP104_; ++ } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { ++ DBG_88E("%s, set group_key, TKIP\n", __func__); ++ psecuritypriv->dot118021XGrpPrivacy = _TKIP_; ++ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, ++ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ /* set mic key */ ++ memcpy(psecuritypriv->dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8); ++ memcpy(psecuritypriv->dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8); ++ ++ psecuritypriv->busetkipkey = true; ++ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { ++ DBG_88E("%s, set group_key, CCMP\n", __func__); ++ psecuritypriv->dot118021XGrpPrivacy = _AES_; ++ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, ++ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ } else { ++ DBG_88E("%s, set group_key, none\n", __func__); ++ psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_; ++ } ++ psecuritypriv->dot118021XGrpKeyid = param->u.crypt.idx; ++ psecuritypriv->binstallGrpkey = true; ++ psecuritypriv->dot11PrivacyAlgrthm = psecuritypriv->dot118021XGrpPrivacy;/* */ ++ set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx); ++ pbcmc_sta = rtw_get_bcmc_stainfo(padapter); ++ if (pbcmc_sta) { ++ pbcmc_sta->ieee8021x_blocked = false; ++ pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy;/* rx will use bmc_sta's dot118021XPrivacy */ ++ } ++ } ++ goto exit; ++ } ++ ++ if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X && psta) { /* psk/802_1x */ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { ++ if (param->u.crypt.set_tx == 1) { ++ memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ ++ if (strcmp(param->u.crypt.alg, "WEP") == 0) { ++ DBG_88E("%s, set pairwise key, WEP\n", __func__); ++ ++ psta->dot118021XPrivacy = _WEP40_; ++ if (param->u.crypt.key_len == 13) ++ psta->dot118021XPrivacy = _WEP104_; ++ } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { ++ DBG_88E("%s, set pairwise key, TKIP\n", __func__); ++ ++ psta->dot118021XPrivacy = _TKIP_; ++ ++ /* set mic key */ ++ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8); ++ memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8); ++ ++ psecuritypriv->busetkipkey = true; ++ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { ++ DBG_88E("%s, set pairwise key, CCMP\n", __func__); ++ ++ psta->dot118021XPrivacy = _AES_; ++ } else { ++ DBG_88E("%s, set pairwise key, none\n", __func__); ++ ++ psta->dot118021XPrivacy = _NO_PRIVACY_; ++ } ++ ++ set_pairwise_key(padapter, psta); ++ ++ psta->ieee8021x_blocked = false; ++ } else { /* group key??? */ ++ if (strcmp(param->u.crypt.alg, "WEP") == 0) { ++ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, ++ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ psecuritypriv->dot118021XGrpPrivacy = _WEP40_; ++ if (param->u.crypt.key_len == 13) ++ psecuritypriv->dot118021XGrpPrivacy = _WEP104_; ++ } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { ++ psecuritypriv->dot118021XGrpPrivacy = _TKIP_; ++ ++ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, ++ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ ++ /* set mic key */ ++ memcpy(psecuritypriv->dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8); ++ memcpy(psecuritypriv->dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8); ++ ++ psecuritypriv->busetkipkey = true; ++ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { ++ psecuritypriv->dot118021XGrpPrivacy = _AES_; ++ ++ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, ++ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); ++ } else { ++ psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_; ++ } ++ ++ psecuritypriv->dot118021XGrpKeyid = param->u.crypt.idx; ++ ++ psecuritypriv->binstallGrpkey = true; ++ ++ psecuritypriv->dot11PrivacyAlgrthm = psecuritypriv->dot118021XGrpPrivacy;/* */ ++ ++ set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx); ++ ++ pbcmc_sta = rtw_get_bcmc_stainfo(padapter); ++ if (pbcmc_sta) { ++ pbcmc_sta->ieee8021x_blocked = false; ++ pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy;/* rx will use bmc_sta's dot118021XPrivacy */ ++ } ++ } ++ } ++ } ++ ++exit: ++ ++ kfree(pwep); ++ ++ return ret; ++} ++ ++static int rtw_set_beacon(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ unsigned char *pbuf = param->u.bcn_ie.buf; ++ ++ DBG_88E("%s, len =%d\n", __func__, len); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ memcpy(&pstapriv->max_num_sta, param->u.bcn_ie.reserved, 2); ++ ++ if ((pstapriv->max_num_sta > NUM_STA) || (pstapriv->max_num_sta <= 0)) ++ pstapriv->max_num_sta = NUM_STA; ++ ++ if (rtw_check_beacon_data(padapter, pbuf, (len-12-2)) == _SUCCESS)/* 12 = param header, 2:no packed */ ++ ret = 0; ++ else ++ ret = -EINVAL; ++ ++ return ret; ++} ++ ++static int rtw_hostapd_sta_flush(struct net_device *dev) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ DBG_88E("%s\n", __func__); ++ ++ flush_all_cam_entry(padapter); /* clear CAM */ ++ ++ ret = rtw_sta_flush(padapter); ++ ++ return ret; ++} ++ ++static int rtw_add_sta(struct net_device *dev, struct ieee_param *param) ++{ ++ int ret = 0; ++ struct sta_info *psta = NULL; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ ++ DBG_88E("rtw_add_sta(aid =%d) =%pM\n", param->u.add_sta.aid, (param->sta_addr)); ++ ++ if (!check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE))) ++ return -EINVAL; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) ++ return -EINVAL; ++ ++ psta = rtw_get_stainfo(pstapriv, param->sta_addr); ++ if (psta) { ++ int flags = param->u.add_sta.flags; ++ ++ psta->aid = param->u.add_sta.aid;/* aid = 1~2007 */ ++ ++ memcpy(psta->bssrateset, param->u.add_sta.tx_supp_rates, 16); ++ ++ /* check wmm cap. */ ++ if (WLAN_STA_WME&flags) ++ psta->qos_option = 1; ++ else ++ psta->qos_option = 0; ++ ++ if (pmlmepriv->qospriv.qos_option == 0) ++ psta->qos_option = 0; ++ ++ /* chec 802.11n ht cap. */ ++ if (WLAN_STA_HT&flags) { ++ psta->htpriv.ht_option = true; ++ psta->qos_option = 1; ++ memcpy((void *)&psta->htpriv.ht_cap, (void *)¶m->u.add_sta.ht_cap, sizeof(struct ieee80211_ht_cap)); ++ } else { ++ psta->htpriv.ht_option = false; ++ } ++ ++ if (pmlmepriv->htpriv.ht_option == false) ++ psta->htpriv.ht_option = false; ++ ++ update_sta_info_apmode(padapter, psta); ++ } else { ++ ret = -ENOMEM; ++ } ++ ++ return ret; ++} ++ ++static int rtw_del_sta(struct net_device *dev, struct ieee_param *param) ++{ ++ int ret = 0; ++ struct sta_info *psta = NULL; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ int updated = 0; ++ ++ DBG_88E("rtw_del_sta =%pM\n", (param->sta_addr)); ++ ++ if (check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE)) != true) ++ return -EINVAL; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) ++ return -EINVAL; ++ ++ psta = rtw_get_stainfo(pstapriv, param->sta_addr); ++ if (psta) { ++ spin_lock_bh(&pstapriv->asoc_list_lock); ++ if (!list_empty(&psta->asoc_list)) { ++ list_del_init(&psta->asoc_list); ++ pstapriv->asoc_list_cnt--; ++ updated = ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING); ++ } ++ spin_unlock_bh(&pstapriv->asoc_list_lock); ++ associated_clients_update(padapter, updated); ++ psta = NULL; ++ } else { ++ DBG_88E("rtw_del_sta(), sta has already been removed or never been added\n"); ++ } ++ ++ return ret; ++} ++ ++static int rtw_ioctl_get_sta_data(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct sta_info *psta = NULL; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ struct ieee_param_ex *param_ex = (struct ieee_param_ex *)param; ++ struct sta_data *psta_data = (struct sta_data *)param_ex->data; ++ ++ DBG_88E("rtw_ioctl_get_sta_info, sta_addr: %pM\n", (param_ex->sta_addr)); ++ ++ if (check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE)) != true) ++ return -EINVAL; ++ ++ if (param_ex->sta_addr[0] == 0xff && param_ex->sta_addr[1] == 0xff && ++ param_ex->sta_addr[2] == 0xff && param_ex->sta_addr[3] == 0xff && ++ param_ex->sta_addr[4] == 0xff && param_ex->sta_addr[5] == 0xff) ++ return -EINVAL; ++ ++ psta = rtw_get_stainfo(pstapriv, param_ex->sta_addr); ++ if (psta) { ++ psta_data->aid = (u16)psta->aid; ++ psta_data->capability = psta->capability; ++ psta_data->flags = psta->flags; ++ ++/* ++ nonerp_set : BIT(0) ++ no_short_slot_time_set : BIT(1) ++ no_short_preamble_set : BIT(2) ++ no_ht_gf_set : BIT(3) ++ no_ht_set : BIT(4) ++ ht_20mhz_set : BIT(5) ++*/ ++ ++ psta_data->sta_set = ((psta->nonerp_set) | ++ (psta->no_short_slot_time_set << 1) | ++ (psta->no_short_preamble_set << 2) | ++ (psta->no_ht_gf_set << 3) | ++ (psta->no_ht_set << 4) | ++ (psta->ht_20mhz_set << 5)); ++ psta_data->tx_supp_rates_len = psta->bssratelen; ++ memcpy(psta_data->tx_supp_rates, psta->bssrateset, psta->bssratelen); ++ memcpy(&psta_data->ht_cap, &psta->htpriv.ht_cap, sizeof(struct ieee80211_ht_cap)); ++ psta_data->rx_pkts = psta->sta_stats.rx_data_pkts; ++ psta_data->rx_bytes = psta->sta_stats.rx_bytes; ++ psta_data->rx_drops = psta->sta_stats.rx_drops; ++ psta_data->tx_pkts = psta->sta_stats.tx_pkts; ++ psta_data->tx_bytes = psta->sta_stats.tx_bytes; ++ psta_data->tx_drops = psta->sta_stats.tx_drops; ++ } else { ++ ret = -1; ++ } ++ ++ return ret; ++} ++ ++static int rtw_get_sta_wpaie(struct net_device *dev, struct ieee_param *param) ++{ ++ int ret = 0; ++ struct sta_info *psta = NULL; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ ++ DBG_88E("rtw_get_sta_wpaie, sta_addr: %pM\n", (param->sta_addr)); ++ ++ if (check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE)) != true) ++ return -EINVAL; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) ++ return -EINVAL; ++ ++ psta = rtw_get_stainfo(pstapriv, param->sta_addr); ++ if (psta) { ++ if ((psta->wpa_ie[0] == WLAN_EID_RSN) || (psta->wpa_ie[0] == WLAN_EID_GENERIC)) { ++ int wpa_ie_len; ++ int copy_len; ++ ++ wpa_ie_len = psta->wpa_ie[1]; ++ copy_len = ((wpa_ie_len+2) > sizeof(psta->wpa_ie)) ? (sizeof(psta->wpa_ie)) : (wpa_ie_len+2); ++ param->u.wpa_ie.len = copy_len; ++ memcpy(param->u.wpa_ie.reserved, psta->wpa_ie, copy_len); ++ } else { ++ DBG_88E("sta's wpa_ie is NONE\n"); ++ } ++ } else { ++ ret = -1; ++ } ++ ++ return ret; ++} ++ ++static int rtw_set_wps_beacon(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ unsigned char wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); ++ int ie_len; ++ ++ DBG_88E("%s, len =%d\n", __func__, len); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ ie_len = len-12-2;/* 12 = param header, 2:no packed */ ++ ++ if (pmlmepriv->wps_beacon_ie) { ++ kfree(pmlmepriv->wps_beacon_ie); ++ pmlmepriv->wps_beacon_ie = NULL; ++ } ++ ++ if (ie_len > 0) { ++ pmlmepriv->wps_beacon_ie = rtw_malloc(ie_len); ++ pmlmepriv->wps_beacon_ie_len = ie_len; ++ if (pmlmepriv->wps_beacon_ie == NULL) { ++ DBG_88E("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__); ++ return -EINVAL; ++ } ++ ++ memcpy(pmlmepriv->wps_beacon_ie, param->u.bcn_ie.buf, ie_len); ++ ++ update_beacon(padapter, _VENDOR_SPECIFIC_IE_, wps_oui, true); ++ ++ pmlmeext->bstart_bss = true; ++ } ++ ++ return ret; ++} ++ ++static int rtw_set_wps_probe_resp(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ int ie_len; ++ ++ DBG_88E("%s, len =%d\n", __func__, len); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ ie_len = len-12-2;/* 12 = param header, 2:no packed */ ++ ++ if (pmlmepriv->wps_probe_resp_ie) { ++ kfree(pmlmepriv->wps_probe_resp_ie); ++ pmlmepriv->wps_probe_resp_ie = NULL; ++ } ++ ++ if (ie_len > 0) { ++ pmlmepriv->wps_probe_resp_ie = rtw_malloc(ie_len); ++ pmlmepriv->wps_probe_resp_ie_len = ie_len; ++ if (pmlmepriv->wps_probe_resp_ie == NULL) { ++ DBG_88E("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__); ++ return -EINVAL; ++ } ++ memcpy(pmlmepriv->wps_probe_resp_ie, param->u.bcn_ie.buf, ie_len); ++ } ++ ++ return ret; ++} ++ ++static int rtw_set_wps_assoc_resp(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ int ie_len; ++ ++ DBG_88E("%s, len =%d\n", __func__, len); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ ie_len = len-12-2;/* 12 = param header, 2:no packed */ ++ ++ if (pmlmepriv->wps_assoc_resp_ie) { ++ kfree(pmlmepriv->wps_assoc_resp_ie); ++ pmlmepriv->wps_assoc_resp_ie = NULL; ++ } ++ ++ if (ie_len > 0) { ++ pmlmepriv->wps_assoc_resp_ie = rtw_malloc(ie_len); ++ pmlmepriv->wps_assoc_resp_ie_len = ie_len; ++ if (pmlmepriv->wps_assoc_resp_ie == NULL) { ++ DBG_88E("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__); ++ return -EINVAL; ++ } ++ ++ memcpy(pmlmepriv->wps_assoc_resp_ie, param->u.bcn_ie.buf, ie_len); ++ } ++ ++ return ret; ++} ++ ++static int rtw_set_hidden_ssid(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); ++ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); ++ ++ u8 value; ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ if (param->u.wpa_param.name != 0) /* dummy test... */ ++ DBG_88E("%s name(%u) != 0\n", __func__, param->u.wpa_param.name); ++ value = param->u.wpa_param.value; ++ ++ /* use the same definition of hostapd's ignore_broadcast_ssid */ ++ if (value != 1 && value != 2) ++ value = 0; ++ DBG_88E("%s value(%u)\n", __func__, value); ++ pmlmeinfo->hidden_ssid_mode = value; ++ return ret; ++} ++ ++static int rtw_ioctl_acl_remove_sta(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) ++ return -EINVAL; ++ ret = rtw_acl_remove_sta(padapter, param->sta_addr); ++ return ret; ++} ++ ++static int rtw_ioctl_acl_add_sta(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) ++ return -EINVAL; ++ ret = rtw_acl_add_sta(padapter, param->sta_addr); ++ return ret; ++} ++ ++static int rtw_ioctl_set_macaddr_acl(struct net_device *dev, struct ieee_param *param, int len) ++{ ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) ++ return -EINVAL; ++ ++ rtw_set_macaddr_acl(padapter, param->u.mlme.command); ++ ++ return ret; ++} ++ ++static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p) ++{ ++ struct ieee_param *param; ++ int ret = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ /* ++ * this function is expect to call in master mode, which allows no power saving ++ * so, we just check hw_init_completed ++ */ ++ ++ if (!padapter->hw_init_completed) { ++ ret = -EPERM; ++ goto out; ++ } ++ ++ if (!p->pointer || p->length > (sizeof(struct ieee_param) + 100)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ param = (struct ieee_param *)rtw_malloc(p->length); ++ if (param == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (copy_from_user(param, p->pointer, p->length)) { ++ kfree(param); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ switch (param->cmd) { ++ case RTL871X_HOSTAPD_FLUSH: ++ ret = rtw_hostapd_sta_flush(dev); ++ break; ++ case RTL871X_HOSTAPD_ADD_STA: ++ ret = rtw_add_sta(dev, param); ++ break; ++ case RTL871X_HOSTAPD_REMOVE_STA: ++ ret = rtw_del_sta(dev, param); ++ break; ++ case RTL871X_HOSTAPD_SET_BEACON: ++ ret = rtw_set_beacon(dev, param, p->length); ++ break; ++ case RTL871X_SET_ENCRYPTION: ++ ret = rtw_set_encryption(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_GET_WPAIE_STA: ++ ret = rtw_get_sta_wpaie(dev, param); ++ break; ++ case RTL871X_HOSTAPD_SET_WPS_BEACON: ++ ret = rtw_set_wps_beacon(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_SET_WPS_PROBE_RESP: ++ ret = rtw_set_wps_probe_resp(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_SET_WPS_ASSOC_RESP: ++ ret = rtw_set_wps_assoc_resp(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_SET_HIDDEN_SSID: ++ ret = rtw_set_hidden_ssid(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_GET_INFO_STA: ++ ret = rtw_ioctl_get_sta_data(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_SET_MACADDR_ACL: ++ ret = rtw_ioctl_set_macaddr_acl(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_ACL_ADD_STA: ++ ret = rtw_ioctl_acl_add_sta(dev, param, p->length); ++ break; ++ case RTL871X_HOSTAPD_ACL_REMOVE_STA: ++ ret = rtw_ioctl_acl_remove_sta(dev, param, p->length); ++ break; ++ default: ++ pr_info("Unknown hostapd request: %d\n", param->cmd); ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ if (ret == 0 && copy_to_user(p->pointer, param, p->length)) ++ ret = -EFAULT; ++ kfree(param); ++out: ++ return ret; ++} ++#endif ++ ++#include ++static int rtw_wx_set_priv(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *awrq, ++ char *extra) ++{ ++ int ret = 0; ++ int len = 0; ++ char *ext; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct iw_point *dwrq = (struct iw_point *)awrq; ++ ++ if (dwrq->length == 0) ++ return -EFAULT; ++ ++ len = dwrq->length; ++ ext = rtw_vmalloc(len); ++ if (!ext) ++ return -ENOMEM; ++ ++ if (copy_from_user(ext, dwrq->pointer, len)) { ++ rtw_vmfree(ext, len); ++ return -EFAULT; ++ } ++ ++ /* added for wps2.0 @20110524 */ ++ if (dwrq->flags == 0x8766 && len > 8) { ++ u32 cp_sz; ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ u8 *probereq_wpsie = ext; ++ int probereq_wpsie_len = len; ++ u8 wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; ++ ++ if ((_VENDOR_SPECIFIC_IE_ == probereq_wpsie[0]) && ++ (!memcmp(&probereq_wpsie[2], wps_oui, 4))) { ++ cp_sz = probereq_wpsie_len > MAX_WPS_IE_LEN ? MAX_WPS_IE_LEN : probereq_wpsie_len; ++ ++ pmlmepriv->wps_probe_req_ie_len = 0; ++ kfree(pmlmepriv->wps_probe_req_ie); ++ pmlmepriv->wps_probe_req_ie = NULL; ++ ++ pmlmepriv->wps_probe_req_ie = rtw_malloc(cp_sz); ++ if (pmlmepriv->wps_probe_req_ie == NULL) { ++ pr_info("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__); ++ ret = -EINVAL; ++ goto FREE_EXT; ++ } ++ memcpy(pmlmepriv->wps_probe_req_ie, probereq_wpsie, cp_sz); ++ pmlmepriv->wps_probe_req_ie_len = cp_sz; ++ } ++ goto FREE_EXT; ++ } ++ ++ if (len >= WEXT_CSCAN_HEADER_SIZE && ++ !memcmp(ext, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE)) { ++ ret = rtw_wx_set_scan(dev, info, awrq, ext); ++ goto FREE_EXT; ++ } ++ ++FREE_EXT: ++ ++ rtw_vmfree(ext, len); ++ ++ return ret; ++} ++ ++static int rtw_pm_set(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int ret = 0; ++ unsigned mode = 0; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ ++ DBG_88E("[%s] extra = %s\n", __func__, extra); ++ ++ if (!memcmp(extra, "lps =", 4)) { ++ sscanf(extra+4, "%u", &mode); ++ ret = rtw_pm_set_lps(padapter, mode); ++ } else if (!memcmp(extra, "ips =", 4)) { ++ sscanf(extra+4, "%u", &mode); ++ ret = rtw_pm_set_ips(padapter, mode); ++ } else { ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int rtw_mp_efuse_get(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wdata, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); ++ struct hal_data_8188e *haldata = GET_HAL_DATA(padapter); ++ struct efuse_hal *pEfuseHal; ++ struct iw_point *wrqu; ++ ++ u8 *PROMContent = pEEPROM->efuse_eeprom_data; ++ u8 ips_mode = 0, lps_mode = 0; ++ struct pwrctrl_priv *pwrctrlpriv; ++ u8 *data = NULL; ++ u8 *rawdata = NULL; ++ char *pch, *ptmp, *token, *tmp[3] = {NULL, NULL, NULL}; ++ u16 i = 0, j = 0, mapLen = 0, addr = 0, cnts = 0; ++ u16 max_available_size = 0, raw_cursize = 0, raw_maxsize = 0; ++ int err; ++ u8 org_fw_iol = padapter->registrypriv.fw_iol;/* 0:Disable, 1:enable, 2:by usb speed */ ++ ++ wrqu = (struct iw_point *)wdata; ++ pwrctrlpriv = &padapter->pwrctrlpriv; ++ pEfuseHal = &haldata->EfuseHal; ++ ++ err = 0; ++ data = _rtw_zmalloc(EFUSE_BT_MAX_MAP_LEN); ++ if (data == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ rawdata = _rtw_zmalloc(EFUSE_BT_MAX_MAP_LEN); ++ if (rawdata == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ if (copy_from_user(extra, wrqu->pointer, wrqu->length)) { ++ err = -EFAULT; ++ goto exit; ++ } ++ lps_mode = pwrctrlpriv->power_mgnt;/* keep org value */ ++ rtw_pm_set_lps(padapter, PS_MODE_ACTIVE); ++ ++ ips_mode = pwrctrlpriv->ips_mode;/* keep org value */ ++ rtw_pm_set_ips(padapter, IPS_NONE); ++ ++ pch = extra; ++ DBG_88E("%s: in =%s\n", __func__, extra); ++ ++ i = 0; ++ /* mac 16 "00e04c871200" rmap, 00, 2 */ ++ while ((token = strsep(&pch, ",")) != NULL) { ++ if (i > 2) ++ break; ++ tmp[i] = token; ++ i++; ++ } ++ padapter->registrypriv.fw_iol = 0;/* 0:Disable, 1:enable, 2:by usb speed */ ++ ++ if (strcmp(tmp[0], "status") == 0) { ++ sprintf(extra, "Load File efuse =%s, Load File MAC =%s", (pEEPROM->bloadfile_fail_flag ? "FAIL" : "OK"), (pEEPROM->bloadmac_fail_flag ? "FAIL" : "OK")); ++ ++ goto exit; ++ } else if (strcmp(tmp[0], "filemap") == 0) { ++ mapLen = EFUSE_MAP_SIZE; ++ ++ sprintf(extra, "\n"); ++ for (i = 0; i < EFUSE_MAP_SIZE; i += 16) { ++ sprintf(extra + strlen(extra), "0x%02x\t", i); ++ for (j = 0; j < 8; j++) ++ sprintf(extra + strlen(extra), "%02X ", PROMContent[i+j]); ++ sprintf(extra + strlen(extra), "\t"); ++ for (; j < 16; j++) ++ sprintf(extra + strlen(extra), "%02X ", PROMContent[i+j]); ++ sprintf(extra + strlen(extra), "\n"); ++ } ++ } else if (strcmp(tmp[0], "realmap") == 0) { ++ mapLen = EFUSE_MAP_SIZE; ++ if (rtw_efuse_map_read(padapter, 0, mapLen, pEfuseHal->fakeEfuseInitMap) == _FAIL) { ++ DBG_88E("%s: read realmap Fail!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ sprintf(extra, "\n"); ++ for (i = 0; i < EFUSE_MAP_SIZE; i += 16) { ++ sprintf(extra + strlen(extra), "0x%02x\t", i); ++ for (j = 0; j < 8; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->fakeEfuseInitMap[i+j]); ++ sprintf(extra + strlen(extra), "\t"); ++ for (; j < 16; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->fakeEfuseInitMap[i+j]); ++ sprintf(extra + strlen(extra), "\n"); ++ } ++ } else if (strcmp(tmp[0], "rmap") == 0) { ++ if ((tmp[1] == NULL) || (tmp[2] == NULL)) { ++ DBG_88E("%s: rmap Fail!! Parameters error!\n", __func__); ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ /* rmap addr cnts */ ++ addr = simple_strtoul(tmp[1], &ptmp, 16); ++ DBG_88E("%s: addr =%x\n", __func__, addr); ++ ++ cnts = simple_strtoul(tmp[2], &ptmp, 10); ++ if (cnts == 0) { ++ DBG_88E("%s: rmap Fail!! cnts error!\n", __func__); ++ err = -EINVAL; ++ goto exit; ++ } ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if ((addr + cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%X)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ if (rtw_efuse_map_read(padapter, addr, cnts, data) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_map_read error!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ *extra = 0; ++ for (i = 0; i < cnts; i++) ++ sprintf(extra + strlen(extra), "0x%02X ", data[i]); ++ } else if (strcmp(tmp[0], "realraw") == 0) { ++ addr = 0; ++ mapLen = EFUSE_MAX_SIZE; ++ if (rtw_efuse_access(padapter, false, addr, mapLen, rawdata) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_access Fail!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ sprintf(extra, "\n"); ++ for (i = 0; i < mapLen; i++) { ++ sprintf(extra + strlen(extra), "%02X", rawdata[i]); ++ ++ if ((i & 0xF) == 0xF) ++ sprintf(extra + strlen(extra), "\n"); ++ else if ((i & 0x7) == 0x7) ++ sprintf(extra + strlen(extra), "\t"); ++ else ++ sprintf(extra + strlen(extra), " "); ++ } ++ } else if (strcmp(tmp[0], "mac") == 0) { ++ cnts = 6; ++ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if ((addr + cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%02x)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_efuse_map_read(padapter, addr, cnts, data) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_map_read error!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ *extra = 0; ++ for (i = 0; i < cnts; i++) { ++ sprintf(extra + strlen(extra), "%02X", data[i]); ++ if (i != (cnts-1)) ++ sprintf(extra + strlen(extra), ":"); ++ } ++ } else if (strcmp(tmp[0], "vidpid") == 0) { ++ cnts = 4; ++ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if ((addr + cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%02x)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EFAULT; ++ goto exit; ++ } ++ if (rtw_efuse_map_read(padapter, addr, cnts, data) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_access error!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ *extra = 0; ++ for (i = 0; i < cnts; i++) { ++ sprintf(extra + strlen(extra), "0x%02X", data[i]); ++ if (i != (cnts-1)) ++ sprintf(extra + strlen(extra), ","); ++ } ++ } else if (strcmp(tmp[0], "ableraw") == 0) { ++ efuse_GetCurrentSize(padapter, &raw_cursize); ++ raw_maxsize = efuse_GetMaxSize(padapter); ++ sprintf(extra, "[available raw size] = %d bytes", raw_maxsize-raw_cursize); ++ } else if (strcmp(tmp[0], "btfmap") == 0) { ++ mapLen = EFUSE_BT_MAX_MAP_LEN; ++ if (rtw_BT_efuse_map_read(padapter, 0, mapLen, pEfuseHal->BTEfuseInitMap) == _FAIL) { ++ DBG_88E("%s: rtw_BT_efuse_map_read Fail!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ sprintf(extra, "\n"); ++ for (i = 0; i < 512; i += 16) { ++ /* set 512 because the iwpriv's extra size have limit 0x7FF */ ++ sprintf(extra + strlen(extra), "0x%03x\t", i); ++ for (j = 0; j < 8; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->BTEfuseInitMap[i+j]); ++ sprintf(extra + strlen(extra), "\t"); ++ for (; j < 16; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->BTEfuseInitMap[i+j]); ++ sprintf(extra + strlen(extra), "\n"); ++ } ++ } else if (strcmp(tmp[0], "btbmap") == 0) { ++ mapLen = EFUSE_BT_MAX_MAP_LEN; ++ if (rtw_BT_efuse_map_read(padapter, 0, mapLen, pEfuseHal->BTEfuseInitMap) == _FAIL) { ++ DBG_88E("%s: rtw_BT_efuse_map_read Fail!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ sprintf(extra, "\n"); ++ for (i = 512; i < 1024; i += 16) { ++ sprintf(extra + strlen(extra), "0x%03x\t", i); ++ for (j = 0; j < 8; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->BTEfuseInitMap[i+j]); ++ sprintf(extra + strlen(extra), "\t"); ++ for (; j < 16; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->BTEfuseInitMap[i+j]); ++ sprintf(extra + strlen(extra), "\n"); ++ } ++ } else if (strcmp(tmp[0], "btrmap") == 0) { ++ if ((tmp[1] == NULL) || (tmp[2] == NULL)) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ /* rmap addr cnts */ ++ addr = simple_strtoul(tmp[1], &ptmp, 16); ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ ++ cnts = simple_strtoul(tmp[2], &ptmp, 10); ++ if (cnts == 0) { ++ DBG_88E("%s: btrmap Fail!! cnts error!\n", __func__); ++ err = -EINVAL; ++ goto exit; ++ } ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_BT, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if ((addr + cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%X)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_BT_efuse_map_read(padapter, addr, cnts, data) == _FAIL) { ++ DBG_88E("%s: rtw_BT_efuse_map_read error!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ *extra = 0; ++ for (i = 0; i < cnts; i++) ++ sprintf(extra + strlen(extra), " 0x%02X ", data[i]); ++ } else if (strcmp(tmp[0], "btffake") == 0) { ++ sprintf(extra, "\n"); ++ for (i = 0; i < 512; i += 16) { ++ sprintf(extra + strlen(extra), "0x%03x\t", i); ++ for (j = 0; j < 8; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->fakeBTEfuseModifiedMap[i+j]); ++ sprintf(extra + strlen(extra), "\t"); ++ for (; j < 16; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->fakeBTEfuseModifiedMap[i+j]); ++ sprintf(extra + strlen(extra), "\n"); ++ } ++ } else if (strcmp(tmp[0], "btbfake") == 0) { ++ sprintf(extra, "\n"); ++ for (i = 512; i < 1024; i += 16) { ++ sprintf(extra + strlen(extra), "0x%03x\t", i); ++ for (j = 0; j < 8; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->fakeBTEfuseModifiedMap[i+j]); ++ sprintf(extra + strlen(extra), "\t"); ++ for (; j < 16; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->fakeBTEfuseModifiedMap[i+j]); ++ sprintf(extra + strlen(extra), "\n"); ++ } ++ } else if (strcmp(tmp[0], "wlrfkmap") == 0) { ++ sprintf(extra, "\n"); ++ for (i = 0; i < EFUSE_MAP_SIZE; i += 16) { ++ sprintf(extra + strlen(extra), "0x%02x\t", i); ++ for (j = 0; j < 8; j++) ++ sprintf(extra + strlen(extra), "%02X ", pEfuseHal->fakeEfuseModifiedMap[i+j]); ++ sprintf(extra + strlen(extra), "\t"); ++ for (; j < 16; j++) ++ sprintf(extra + strlen(extra), " %02X", pEfuseHal->fakeEfuseModifiedMap[i+j]); ++ sprintf(extra + strlen(extra), "\n"); ++ } ++ } else { ++ sprintf(extra, "Command not found!"); ++ } ++ ++exit: ++ kfree(data); ++ kfree(rawdata); ++ if (!err) ++ wrqu->length = strlen(extra); ++ ++ rtw_pm_set_ips(padapter, ips_mode); ++ rtw_pm_set_lps(padapter, lps_mode); ++ padapter->registrypriv.fw_iol = org_fw_iol;/* 0:Disable, 1:enable, 2:by usb speed */ ++ return err; ++} ++ ++static int rtw_mp_efuse_set(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wdata, char *extra) ++{ ++ struct adapter *padapter; ++ struct pwrctrl_priv *pwrctrlpriv; ++ struct hal_data_8188e *haldata; ++ struct efuse_hal *pEfuseHal; ++ ++ u8 ips_mode = 0, lps_mode = 0; ++ u32 i, jj, kk; ++ u8 *setdata = NULL; ++ u8 *ShadowMapBT = NULL; ++ u8 *ShadowMapWiFi = NULL; ++ u8 *setrawdata = NULL; ++ char *pch, *ptmp, *token, *tmp[3] = {NULL, NULL, NULL}; ++ u16 addr = 0, cnts = 0, max_available_size = 0; ++ int err; ++ ++ padapter = rtw_netdev_priv(dev); ++ pwrctrlpriv = &padapter->pwrctrlpriv; ++ haldata = GET_HAL_DATA(padapter); ++ pEfuseHal = &haldata->EfuseHal; ++ err = 0; ++ setdata = _rtw_zmalloc(1024); ++ if (setdata == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ShadowMapBT = _rtw_malloc(EFUSE_BT_MAX_MAP_LEN); ++ if (ShadowMapBT == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ShadowMapWiFi = _rtw_malloc(EFUSE_MAP_SIZE); ++ if (ShadowMapWiFi == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ setrawdata = _rtw_malloc(EFUSE_MAX_SIZE); ++ if (setrawdata == NULL) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ lps_mode = pwrctrlpriv->power_mgnt;/* keep org value */ ++ rtw_pm_set_lps(padapter, PS_MODE_ACTIVE); ++ ++ ips_mode = pwrctrlpriv->ips_mode;/* keep org value */ ++ rtw_pm_set_ips(padapter, IPS_NONE); ++ ++ pch = extra; ++ DBG_88E("%s: in =%s\n", __func__, extra); ++ ++ i = 0; ++ while ((token = strsep(&pch, ",")) != NULL) { ++ if (i > 2) ++ break; ++ tmp[i] = token; ++ i++; ++ } ++ ++ /* tmp[0],[1],[2] */ ++ /* wmap, addr, 00e04c871200 */ ++ if (strcmp(tmp[0], "wmap") == 0) { ++ if ((tmp[1] == NULL) || (tmp[2] == NULL)) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ addr = simple_strtoul(tmp[1], &ptmp, 16); ++ addr &= 0xFFF; ++ ++ cnts = strlen(tmp[2]); ++ if (cnts%2) { ++ err = -EINVAL; ++ goto exit; ++ } ++ cnts /= 2; ++ if (cnts == 0) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ DBG_88E("%s: map data =%s\n", __func__, tmp[2]); ++ ++ for (jj = 0, kk = 0; jj < cnts; jj++, kk += 2) ++ setdata[jj] = key_2char2num(tmp[2][kk], tmp[2][kk + 1]); ++ /* Change to check TYPE_EFUSE_MAP_LEN, because 8188E raw 256, logic map over 256. */ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_MAP_LEN, (void *)&max_available_size, false); ++ if ((addr+cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%X)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_efuse_map_write(padapter, addr, cnts, setdata) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_map_write error!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ } else if (strcmp(tmp[0], "wraw") == 0) { ++ if ((tmp[1] == NULL) || (tmp[2] == NULL)) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ addr = simple_strtoul(tmp[1], &ptmp, 16); ++ addr &= 0xFFF; ++ ++ cnts = strlen(tmp[2]); ++ if (cnts%2) { ++ err = -EINVAL; ++ goto exit; ++ } ++ cnts /= 2; ++ if (cnts == 0) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ DBG_88E("%s: raw data =%s\n", __func__, tmp[2]); ++ ++ for (jj = 0, kk = 0; jj < cnts; jj++, kk += 2) ++ setrawdata[jj] = key_2char2num(tmp[2][kk], tmp[2][kk + 1]); ++ ++ if (rtw_efuse_access(padapter, true, addr, cnts, setrawdata) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_access error!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ } else if (strcmp(tmp[0], "mac") == 0) { ++ if (tmp[1] == NULL) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ /* mac, 00e04c871200 */ ++ addr = EEPROM_MAC_ADDR_88EU; ++ cnts = strlen(tmp[1]); ++ if (cnts%2) { ++ err = -EINVAL; ++ goto exit; ++ } ++ cnts /= 2; ++ if (cnts == 0) { ++ err = -EINVAL; ++ goto exit; ++ } ++ if (cnts > 6) { ++ DBG_88E("%s: error data for mac addr =\"%s\"\n", __func__, tmp[1]); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ DBG_88E("%s: MAC address =%s\n", __func__, tmp[1]); ++ ++ for (jj = 0, kk = 0; jj < cnts; jj++, kk += 2) ++ setdata[jj] = key_2char2num(tmp[1][kk], tmp[1][kk + 1]); ++ /* Change to check TYPE_EFUSE_MAP_LEN, because 8188E raw 256, logic map over 256. */ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_MAP_LEN, (void *)&max_available_size, false); ++ if ((addr+cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%X)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_efuse_map_write(padapter, addr, cnts, setdata) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_map_write error!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ } else if (strcmp(tmp[0], "vidpid") == 0) { ++ if (tmp[1] == NULL) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ /* pidvid, da0b7881 */ ++ addr = EEPROM_VID_88EE; ++ cnts = strlen(tmp[1]); ++ if (cnts%2) { ++ err = -EINVAL; ++ goto exit; ++ } ++ cnts /= 2; ++ if (cnts == 0) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ DBG_88E("%s: VID/PID =%s\n", __func__, tmp[1]); ++ ++ for (jj = 0, kk = 0; jj < cnts; jj++, kk += 2) ++ setdata[jj] = key_2char2num(tmp[1][kk], tmp[1][kk + 1]); ++ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if ((addr+cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%X)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_efuse_map_write(padapter, addr, cnts, setdata) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_map_write error!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ } else if (strcmp(tmp[0], "btwmap") == 0) { ++ if ((tmp[1] == NULL) || (tmp[2] == NULL)) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ addr = simple_strtoul(tmp[1], &ptmp, 16); ++ addr &= 0xFFF; ++ ++ cnts = strlen(tmp[2]); ++ if (cnts%2) { ++ err = -EINVAL; ++ goto exit; ++ } ++ cnts /= 2; ++ if (cnts == 0) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ DBG_88E("%s: BT data =%s\n", __func__, tmp[2]); ++ ++ for (jj = 0, kk = 0; jj < cnts; jj++, kk += 2) ++ setdata[jj] = key_2char2num(tmp[2][kk], tmp[2][kk + 1]); ++ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_BT, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if ((addr+cnts) > max_available_size) { ++ DBG_88E("%s: addr(0x%X)+cnts(%d) parameter error!\n", __func__, addr, cnts); ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_BT_efuse_map_write(padapter, addr, cnts, setdata) == _FAIL) { ++ DBG_88E("%s: rtw_BT_efuse_map_write error!!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ } else if (strcmp(tmp[0], "btwfake") == 0) { ++ if ((tmp[1] == NULL) || (tmp[2] == NULL)) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ addr = simple_strtoul(tmp[1], &ptmp, 16); ++ addr &= 0xFFF; ++ ++ cnts = strlen(tmp[2]); ++ if (cnts%2) { ++ err = -EINVAL; ++ goto exit; ++ } ++ cnts /= 2; ++ if (cnts == 0) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ DBG_88E("%s: BT tmp data =%s\n", __func__, tmp[2]); ++ ++ for (jj = 0, kk = 0; jj < cnts; jj++, kk += 2) ++ pEfuseHal->fakeBTEfuseModifiedMap[addr+jj] = key_2char2num(tmp[2][kk], tmp[2][kk + 1]); ++ } else if (strcmp(tmp[0], "btdumpfake") == 0) { ++ if (rtw_BT_efuse_map_read(padapter, 0, EFUSE_BT_MAX_MAP_LEN, pEfuseHal->fakeBTEfuseModifiedMap) == _SUCCESS) { ++ DBG_88E("%s: BT read all map success\n", __func__); ++ } else { ++ DBG_88E("%s: BT read all map Fail!\n", __func__); ++ err = -EFAULT; ++ } ++ } else if (strcmp(tmp[0], "wldumpfake") == 0) { ++ if (rtw_efuse_map_read(padapter, 0, EFUSE_BT_MAX_MAP_LEN, pEfuseHal->fakeEfuseModifiedMap) == _SUCCESS) { ++ DBG_88E("%s: BT read all map success\n", __func__); ++ } else { ++ DBG_88E("%s: BT read all map Fail\n", __func__); ++ err = -EFAULT; ++ } ++ } else if (strcmp(tmp[0], "btfk2map") == 0) { ++ memcpy(pEfuseHal->BTEfuseModifiedMap, pEfuseHal->fakeBTEfuseModifiedMap, EFUSE_BT_MAX_MAP_LEN); ++ ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_BT, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if (max_available_size < 1) { ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_BT_efuse_map_write(padapter, 0x00, EFUSE_BT_MAX_MAP_LEN, pEfuseHal->fakeBTEfuseModifiedMap) == _FAIL) { ++ DBG_88E("%s: rtw_BT_efuse_map_write error!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ } else if (strcmp(tmp[0], "wlfk2map") == 0) { ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if (max_available_size < 1) { ++ err = -EFAULT; ++ goto exit; ++ } ++ ++ if (rtw_efuse_map_write(padapter, 0x00, EFUSE_MAX_MAP_LEN, pEfuseHal->fakeEfuseModifiedMap) == _FAIL) { ++ DBG_88E("%s: rtw_efuse_map_write error!\n", __func__); ++ err = -EFAULT; ++ goto exit; ++ } ++ } else if (strcmp(tmp[0], "wlwfake") == 0) { ++ if ((tmp[1] == NULL) || (tmp[2] == NULL)) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ addr = simple_strtoul(tmp[1], &ptmp, 16); ++ addr &= 0xFFF; ++ ++ cnts = strlen(tmp[2]); ++ if (cnts%2) { ++ err = -EINVAL; ++ goto exit; ++ } ++ cnts /= 2; ++ if (cnts == 0) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ DBG_88E("%s: addr = 0x%X\n", __func__, addr); ++ DBG_88E("%s: cnts =%d\n", __func__, cnts); ++ DBG_88E("%s: map tmp data =%s\n", __func__, tmp[2]); ++ ++ for (jj = 0, kk = 0; jj < cnts; jj++, kk += 2) ++ pEfuseHal->fakeEfuseModifiedMap[addr+jj] = key_2char2num(tmp[2][kk], tmp[2][kk + 1]); ++ } ++ ++exit: ++ kfree(setdata); ++ kfree(ShadowMapBT); ++ kfree(ShadowMapWiFi); ++ kfree(setrawdata); ++ ++ rtw_pm_set_ips(padapter, ips_mode); ++ rtw_pm_set_lps(padapter, lps_mode); ++ ++ return err; ++} ++ ++/* ++ * Input Format: %s,%d,%d ++ * %s is width, could be ++ * "b" for 1 byte ++ * "w" for WORD (2 bytes) ++ * "dw" for DWORD (4 bytes) ++ * 1st %d is address(offset) ++ * 2st %d is data to write ++ */ ++static int rtw_mp_write_reg(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ char *pch, *pnext, *ptmp; ++ char *width_str; ++ char width; ++ u32 addr, data; ++ int ret; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ pch = extra; ++ pnext = strpbrk(pch, ",.-"); ++ if (pnext == NULL) ++ return -EINVAL; ++ *pnext = 0; ++ width_str = pch; ++ ++ pch = pnext + 1; ++ pnext = strpbrk(pch, ",.-"); ++ if (pnext == NULL) ++ return -EINVAL; ++ *pnext = 0; ++ addr = simple_strtoul(pch, &ptmp, 16); ++ if (addr > 0x3FFF) ++ return -EINVAL; ++ ++ pch = pnext + 1; ++ if ((pch - extra) >= wrqu->length) ++ return -EINVAL; ++ data = simple_strtoul(pch, &ptmp, 16); ++ ++ ret = 0; ++ width = width_str[0]; ++ switch (width) { ++ case 'b': ++ /* 1 byte */ ++ if (data > 0xFF) { ++ ret = -EINVAL; ++ break; ++ } ++ rtw_write8(padapter, addr, data); ++ break; ++ case 'w': ++ /* 2 bytes */ ++ if (data > 0xFFFF) { ++ ret = -EINVAL; ++ break; ++ } ++ rtw_write16(padapter, addr, data); ++ break; ++ case 'd': ++ /* 4 bytes */ ++ rtw_write32(padapter, addr, data); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Input Format: %s,%d ++ * %s is width, could be ++ * "b" for 1 byte ++ * "w" for WORD (2 bytes) ++ * "dw" for DWORD (4 bytes) ++ * %d is address(offset) ++ * ++ * Return: ++ * %d for data readed ++ */ ++static int rtw_mp_read_reg(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ char *pch, *pnext, *ptmp; ++ char *width_str; ++ char width; ++ char data[20], tmp[20]; ++ u32 addr; ++ u32 ret, i = 0, j = 0, strtout = 0; ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ memset(data, 0, 20); ++ memset(tmp, 0, 20); ++ memset(extra, 0, wrqu->length); ++ ++ pch = input; ++ pnext = strpbrk(pch, ",.-"); ++ if (pnext == NULL) { ++ kfree(input); ++ return -EINVAL; ++ } ++ *pnext = 0; ++ width_str = pch; ++ ++ pch = pnext + 1; ++ if ((pch - input) >= wrqu->length) { ++ kfree(input); ++ return -EINVAL; ++ } ++ kfree(input); ++ addr = simple_strtoul(pch, &ptmp, 16); ++ if (addr > 0x3FFF) ++ return -EINVAL; ++ ++ ret = 0; ++ width = width_str[0]; ++ switch (width) { ++ case 'b': ++ /* 1 byte */ ++ sprintf(extra, "%d\n", rtw_read8(padapter, addr)); ++ wrqu->length = strlen(extra); ++ break; ++ case 'w': ++ /* 2 bytes */ ++ sprintf(data, "%04x\n", rtw_read16(padapter, addr)); ++ for (i = 0; i <= strlen(data); i++) { ++ if (i%2 == 0) { ++ tmp[j] = ' '; ++ j++; ++ } ++ if (data[i] != '\0') ++ tmp[j] = data[i]; ++ j++; ++ } ++ pch = tmp; ++ DBG_88E("pch =%s", pch); ++ ++ while (*pch != '\0') { ++ pnext = strpbrk(pch, " "); ++ if (!pnext) ++ break; ++ ++ pnext++; ++ if (*pnext != '\0') { ++ strtout = simple_strtoul(pnext, &ptmp, 16); ++ sprintf(extra, "%s %d", extra, strtout); ++ } else { ++ break; ++ } ++ pch = pnext; ++ } ++ wrqu->length = 6; ++ break; ++ case 'd': ++ /* 4 bytes */ ++ sprintf(data, "%08x", rtw_read32(padapter, addr)); ++ /* add read data format blank */ ++ for (i = 0; i <= strlen(data); i++) { ++ if (i%2 == 0) { ++ tmp[j] = ' '; ++ j++; ++ } ++ if (data[i] != '\0') ++ tmp[j] = data[i]; ++ ++ j++; ++ } ++ pch = tmp; ++ DBG_88E("pch =%s", pch); ++ ++ while (*pch != '\0') { ++ pnext = strpbrk(pch, " "); ++ if (!pnext) ++ break; ++ pnext++; ++ if (*pnext != '\0') { ++ strtout = simple_strtoul(pnext, &ptmp, 16); ++ sprintf(extra, "%s %d", extra, strtout); ++ } else { ++ break; ++ } ++ pch = pnext; ++ } ++ wrqu->length = strlen(extra); ++ break; ++ default: ++ wrqu->length = 0; ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Input Format: %d,%x,%x ++ * %d is RF path, should be smaller than RF_PATH_MAX ++ * 1st %x is address(offset) ++ * 2st %x is data to write ++ */ ++ static int rtw_mp_write_rf(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u32 path, addr, data; ++ int ret; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ ret = sscanf(extra, "%d,%x,%x", &path, &addr, &data); ++ if (ret < 3) ++ return -EINVAL; ++ ++ if (path >= RF_PATH_MAX) ++ return -EINVAL; ++ if (addr > 0xFF) ++ return -EINVAL; ++ if (data > 0xFFFFF) ++ return -EINVAL; ++ ++ memset(extra, 0, wrqu->length); ++ ++ write_rfreg(padapter, path, addr, data); ++ ++ sprintf(extra, "write_rf completed\n"); ++ wrqu->length = strlen(extra); ++ ++ return 0; ++} ++ ++/* ++ * Input Format: %d,%x ++ * %d is RF path, should be smaller than RF_PATH_MAX ++ * %x is address(offset) ++ * ++ * Return: ++ * %d for data readed ++ */ ++static int rtw_mp_read_rf(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ char *pch, *pnext, *ptmp; ++ char data[20], tmp[20]; ++ u32 path, addr; ++ u32 ret, i = 0, j = 0, strtou = 0; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ ret = sscanf(input, "%d,%x", &path, &addr); ++ kfree(input); ++ if (ret < 2) ++ return -EINVAL; ++ ++ if (path >= RF_PATH_MAX) ++ return -EINVAL; ++ if (addr > 0xFF) ++ return -EINVAL; ++ ++ memset(extra, 0, wrqu->length); ++ ++ sprintf(data, "%08x", read_rfreg(padapter, path, addr)); ++ /* add read data format blank */ ++ for (i = 0; i <= strlen(data); i++) { ++ if (i%2 == 0) { ++ tmp[j] = ' '; ++ j++; ++ } ++ tmp[j] = data[i]; ++ j++; ++ } ++ pch = tmp; ++ DBG_88E("pch =%s", pch); ++ ++ while (*pch != '\0') { ++ pnext = strpbrk(pch, " "); ++ pnext++; ++ if (*pnext != '\0') { ++ strtou = simple_strtoul(pnext, &ptmp, 16); ++ sprintf(extra, "%s %d", extra, strtou); ++ } else { ++ break; ++ } ++ pch = pnext; ++ } ++ wrqu->length = strlen(extra); ++ return 0; ++} ++ ++static int rtw_mp_start(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (padapter->registrypriv.mp_mode == 0) { ++ padapter->registrypriv.mp_mode = 1; ++ ++ rtw_pm_set_ips(padapter, IPS_NONE); ++ LeaveAllPowerSaveMode(padapter); ++ ++ MPT_InitializeAdapter(padapter, 1); ++ } ++ if (padapter->registrypriv.mp_mode == 0) ++ return -EPERM; ++ if (padapter->mppriv.mode == MP_OFF) { ++ if (mp_start_test(padapter) == _FAIL) ++ return -EPERM; ++ padapter->mppriv.mode = MP_ON; ++ } ++ return 0; ++} ++ ++static int rtw_mp_stop(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (padapter->registrypriv.mp_mode == 1) { ++ MPT_DeInitAdapter(padapter); ++ padapter->registrypriv.mp_mode = 0; ++ } ++ ++ if (padapter->mppriv.mode != MP_OFF) { ++ mp_stop_test(padapter); ++ padapter->mppriv.mode = MP_OFF; ++ } ++ ++ return 0; ++} ++ ++extern int wifirate2_ratetbl_inx(unsigned char rate); ++ ++static int rtw_mp_rate(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u32 rate = MPT_RATE_1M; ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ rate = rtw_atoi(input); ++ sprintf(extra, "Set data rate to %d", rate); ++ kfree(input); ++ if (rate <= 0x7f) ++ rate = wifirate2_ratetbl_inx((u8)rate); ++ else ++ rate = (rate-0x80+MPT_RATE_MCS0); ++ ++ if (rate >= MPT_RATE_LAST) ++ return -EINVAL; ++ ++ padapter->mppriv.rateidx = rate; ++ Hal_SetDataRate(padapter); ++ ++ wrqu->length = strlen(extra) + 1; ++ return 0; ++} ++ ++static int rtw_mp_channel(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ u32 channel = 1; ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ channel = rtw_atoi(input); ++ sprintf(extra, "Change channel %d to channel %d", padapter->mppriv.channel, channel); ++ ++ padapter->mppriv.channel = channel; ++ Hal_SetChannel(padapter); ++ ++ wrqu->length = strlen(extra) + 1; ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_bandwidth(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u32 bandwidth = 0, sg = 0; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ sscanf(extra, "40M =%d, shortGI =%d", &bandwidth, &sg); ++ ++ if (bandwidth != HT_CHANNEL_WIDTH_40) ++ bandwidth = HT_CHANNEL_WIDTH_20; ++ ++ padapter->mppriv.bandwidth = (u8)bandwidth; ++ padapter->mppriv.preamble = sg; ++ ++ SetBandwidth(padapter); ++ ++ return 0; ++} ++ ++static int rtw_mp_txpower(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u32 idx_a = 0, idx_b = 0; ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ sscanf(input, "patha =%d, pathb =%d", &idx_a, &idx_b); ++ ++ sprintf(extra, "Set power level path_A:%d path_B:%d", idx_a, idx_b); ++ padapter->mppriv.txpoweridx = (u8)idx_a; ++ padapter->mppriv.txpoweridx_b = (u8)idx_b; ++ padapter->mppriv.bSetTxPower = 1; ++ Hal_SetAntennaPathPower(padapter); ++ ++ wrqu->length = strlen(extra) + 1; ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_ant_tx(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u8 i; ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ u16 antenna = 0; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ ++ sprintf(extra, "switch Tx antenna to %s", input); ++ ++ for (i = 0; i < strlen(input); i++) { ++ switch (input[i]) { ++ case 'a': ++ antenna |= ANTENNA_A; ++ break; ++ case 'b': ++ antenna |= ANTENNA_B; ++ break; ++ } ++ } ++ padapter->mppriv.antenna_tx = antenna; ++ ++ Hal_SetAntenna(padapter); ++ ++ wrqu->length = strlen(extra) + 1; ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_ant_rx(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u8 i; ++ u16 antenna = 0; ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ memset(extra, 0, wrqu->length); ++ ++ sprintf(extra, "switch Rx antenna to %s", input); ++ ++ for (i = 0; i < strlen(input); i++) { ++ switch (input[i]) { ++ case 'a': ++ antenna |= ANTENNA_A; ++ break; ++ case 'b': ++ antenna |= ANTENNA_B; ++ break; ++ } ++ } ++ ++ padapter->mppriv.antenna_rx = antenna; ++ Hal_SetAntenna(padapter); ++ wrqu->length = strlen(extra); ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_ctx(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u32 pkTx = 1, countPkTx = 1, cotuTx = 1, CarrSprTx = 1, scTx = 1, sgleTx = 1, stop = 1; ++ u32 bStartTest = 1; ++ u32 count = 0; ++ struct mp_priv *pmp_priv; ++ struct pkt_attrib *pattrib; ++ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ pmp_priv = &padapter->mppriv; ++ ++ if (copy_from_user(extra, wrqu->pointer, wrqu->length)) ++ return -EFAULT; ++ ++ DBG_88E("%s: in =%s\n", __func__, extra); ++ ++ countPkTx = strncmp(extra, "count =", 5); /* strncmp true is 0 */ ++ cotuTx = strncmp(extra, "background", 20); ++ CarrSprTx = strncmp(extra, "background, cs", 20); ++ scTx = strncmp(extra, "background, sc", 20); ++ sgleTx = strncmp(extra, "background, stone", 20); ++ pkTx = strncmp(extra, "background, pkt", 20); ++ stop = strncmp(extra, "stop", 4); ++ sscanf(extra, "count =%d, pkt", &count); ++ ++ memset(extra, '\0', sizeof(*extra)); ++ ++ if (stop == 0) { ++ bStartTest = 0; /* To set Stop */ ++ pmp_priv->tx.stop = 1; ++ sprintf(extra, "Stop continuous Tx"); ++ } else { ++ bStartTest = 1; ++ if (pmp_priv->mode != MP_ON) { ++ if (pmp_priv->tx.stop != 1) { ++ DBG_88E("%s: MP_MODE != ON %d\n", __func__, pmp_priv->mode); ++ return -EFAULT; ++ } ++ } ++ } ++ ++ if (pkTx == 0 || countPkTx == 0) ++ pmp_priv->mode = MP_PACKET_TX; ++ if (sgleTx == 0) ++ pmp_priv->mode = MP_SINGLE_TONE_TX; ++ if (cotuTx == 0) ++ pmp_priv->mode = MP_CONTINUOUS_TX; ++ if (CarrSprTx == 0) ++ pmp_priv->mode = MP_CARRIER_SUPPRISSION_TX; ++ if (scTx == 0) ++ pmp_priv->mode = MP_SINGLE_CARRIER_TX; ++ ++ switch (pmp_priv->mode) { ++ case MP_PACKET_TX: ++ if (bStartTest == 0) { ++ pmp_priv->tx.stop = 1; ++ pmp_priv->mode = MP_ON; ++ sprintf(extra, "Stop continuous Tx"); ++ } else if (pmp_priv->tx.stop == 1) { ++ sprintf(extra, "Start continuous DA = ffffffffffff len = 1500 count =%u,\n", count); ++ pmp_priv->tx.stop = 0; ++ pmp_priv->tx.count = count; ++ pmp_priv->tx.payload = 2; ++ pattrib = &pmp_priv->tx.attrib; ++ pattrib->pktlen = 1500; ++ memset(pattrib->dst, 0xFF, ETH_ALEN); ++ SetPacketTx(padapter); ++ } else { ++ return -EFAULT; ++ } ++ wrqu->length = strlen(extra); ++ return 0; ++ case MP_SINGLE_TONE_TX: ++ if (bStartTest != 0) ++ sprintf(extra, "Start continuous DA = ffffffffffff len = 1500\n infinite = yes."); ++ Hal_SetSingleToneTx(padapter, (u8)bStartTest); ++ break; ++ case MP_CONTINUOUS_TX: ++ if (bStartTest != 0) ++ sprintf(extra, "Start continuous DA = ffffffffffff len = 1500\n infinite = yes."); ++ Hal_SetContinuousTx(padapter, (u8)bStartTest); ++ break; ++ case MP_CARRIER_SUPPRISSION_TX: ++ if (bStartTest != 0) { ++ if (pmp_priv->rateidx <= MPT_RATE_11M) { ++ sprintf(extra, "Start continuous DA = ffffffffffff len = 1500\n infinite = yes."); ++ Hal_SetCarrierSuppressionTx(padapter, (u8)bStartTest); ++ } else { ++ sprintf(extra, "Specify carrier suppression but not CCK rate"); ++ } ++ } ++ break; ++ case MP_SINGLE_CARRIER_TX: ++ if (bStartTest != 0) ++ sprintf(extra, "Start continuous DA = ffffffffffff len = 1500\n infinite = yes."); ++ Hal_SetSingleCarrierTx(padapter, (u8)bStartTest); ++ break; ++ default: ++ sprintf(extra, "Error! Continuous-Tx is not on-going."); ++ return -EFAULT; ++ } ++ ++ if (bStartTest == 1 && pmp_priv->mode != MP_ON) { ++ struct mp_priv *pmp_priv = &padapter->mppriv; ++ if (pmp_priv->tx.stop == 0) { ++ pmp_priv->tx.stop = 1; ++ rtw_msleep_os(5); ++ } ++ pmp_priv->tx.stop = 0; ++ pmp_priv->tx.count = 1; ++ SetPacketTx(padapter); ++ } else { ++ pmp_priv->mode = MP_ON; ++ } ++ ++ wrqu->length = strlen(extra); ++ return 0; ++} ++ ++static int rtw_mp_arx(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u8 bStartRx = 0, bStopRx = 0, bQueryPhy; ++ u32 cckok = 0, cckcrc = 0, ofdmok = 0, ofdmcrc = 0, htok = 0, htcrc = 0, OFDM_FA = 0, CCK_FA = 0; ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (!input) ++ return -ENOMEM; ++ ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ DBG_88E("%s: %s\n", __func__, input); ++ ++ bStartRx = (strncmp(input, "start", 5) == 0) ? 1 : 0; /* strncmp true is 0 */ ++ bStopRx = (strncmp(input, "stop", 5) == 0) ? 1 : 0; /* strncmp true is 0 */ ++ bQueryPhy = (strncmp(input, "phy", 3) == 0) ? 1 : 0; /* strncmp true is 0 */ ++ ++ if (bStartRx) { ++ sprintf(extra, "start"); ++ SetPacketRx(padapter, bStartRx); ++ } else if (bStopRx) { ++ SetPacketRx(padapter, 0); ++ sprintf(extra, "Received packet OK:%d CRC error:%d", padapter->mppriv.rx_pktcount, padapter->mppriv.rx_crcerrpktcount); ++ } else if (bQueryPhy) { ++ /* ++ OFDM FA ++ RegCF0[15:0] ++ RegCF2[31:16] ++ RegDA0[31:16] ++ RegDA4[15:0] ++ RegDA4[31:16] ++ RegDA8[15:0] ++ CCK FA ++ (RegA5B<<8) | RegA5C ++ */ ++ cckok = read_bbreg(padapter, 0xf88, 0xffffffff); ++ cckcrc = read_bbreg(padapter, 0xf84, 0xffffffff); ++ ofdmok = read_bbreg(padapter, 0xf94, 0x0000FFFF); ++ ofdmcrc = read_bbreg(padapter, 0xf94, 0xFFFF0000); ++ htok = read_bbreg(padapter, 0xf90, 0x0000FFFF); ++ htcrc = read_bbreg(padapter, 0xf90, 0xFFFF0000); ++ ++ OFDM_FA = read_bbreg(padapter, 0xcf0, 0x0000FFFF); ++ OFDM_FA = read_bbreg(padapter, 0xcf2, 0xFFFF0000); ++ OFDM_FA = read_bbreg(padapter, 0xda0, 0xFFFF0000); ++ OFDM_FA = read_bbreg(padapter, 0xda4, 0x0000FFFF); ++ OFDM_FA = read_bbreg(padapter, 0xda4, 0xFFFF0000); ++ OFDM_FA = read_bbreg(padapter, 0xda8, 0x0000FFFF); ++ CCK_FA = (rtw_read8(padapter, 0xa5b)<<8) | (rtw_read8(padapter, 0xa5c)); ++ ++ sprintf(extra, "Phy Received packet OK:%d CRC error:%d FA Counter: %d", cckok+ofdmok+htok, cckcrc+ofdmcrc+htcrc, OFDM_FA+CCK_FA); ++ } ++ wrqu->length = strlen(extra) + 1; ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_trx_query(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u32 txok, txfail, rxok, rxfail; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ txok = padapter->mppriv.tx.sended; ++ txfail = 0; ++ rxok = padapter->mppriv.rx_pktcount; ++ rxfail = padapter->mppriv.rx_crcerrpktcount; ++ ++ memset(extra, '\0', 128); ++ ++ sprintf(extra, "Tx OK:%d, Tx Fail:%d, Rx OK:%d, CRC error:%d ", txok, txfail, rxok, rxfail); ++ ++ wrqu->length = strlen(extra)+1; ++ ++ return 0; ++} ++ ++static int rtw_mp_pwrtrk(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u8 enable; ++ u32 thermal; ++ s32 ret; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ memset(extra, 0, wrqu->length); ++ ++ enable = 1; ++ if (wrqu->length > 1) {/* not empty string */ ++ if (strncmp(input, "stop", 4) == 0) { ++ enable = 0; ++ sprintf(extra, "mp tx power tracking stop"); ++ } else if (sscanf(input, "ther =%d", &thermal)) { ++ ret = Hal_SetThermalMeter(padapter, (u8)thermal); ++ if (ret == _FAIL) ++ return -EPERM; ++ sprintf(extra, "mp tx power tracking start, target value =%d ok ", thermal); ++ } else { ++ kfree(input); ++ return -EINVAL; ++ } ++ } ++ ++ kfree(input); ++ ret = Hal_SetPowerTracking(padapter, enable); ++ if (ret == _FAIL) ++ return -EPERM; ++ ++ wrqu->length = strlen(extra); ++ return 0; ++} ++ ++static int rtw_mp_psd(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ ++ strcpy(extra, input); ++ ++ wrqu->length = mp_query_psd(padapter, extra); ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_thermal(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u8 val; ++ u16 bwrite = 1; ++ u16 addr = EEPROM_THERMAL_METER_88E; ++ ++ u16 cnt = 1; ++ u16 max_available_size = 0; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (copy_from_user(extra, wrqu->pointer, wrqu->length)) ++ return -EFAULT; ++ ++ bwrite = strncmp(extra, "write", 6); /* strncmp true is 0 */ ++ ++ Hal_GetThermalMeter(padapter, &val); ++ ++ if (bwrite == 0) { ++ EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); ++ if (2 > max_available_size) { ++ DBG_88E("no available efuse!\n"); ++ return -EFAULT; ++ } ++ if (rtw_efuse_map_write(padapter, addr, cnt, &val) == _FAIL) { ++ DBG_88E("rtw_efuse_map_write error\n"); ++ return -EFAULT; ++ } else { ++ sprintf(extra, " efuse write ok :%d", val); ++ } ++ } else { ++ sprintf(extra, "%d", val); ++ } ++ wrqu->length = strlen(extra); ++ ++ return 0; ++} ++ ++static int rtw_mp_reset_stats(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ struct mp_priv *pmp_priv; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ pmp_priv = &padapter->mppriv; ++ ++ pmp_priv->tx.sended = 0; ++ pmp_priv->tx_pktcount = 0; ++ pmp_priv->rx_pktcount = 0; ++ pmp_priv->rx_crcerrpktcount = 0; ++ ++ /* reset phy counter */ ++ write_bbreg(padapter, 0xf14, BIT16, 0x1); ++ rtw_msleep_os(10); ++ write_bbreg(padapter, 0xf14, BIT16, 0x0); ++ ++ return 0; ++} ++ ++static int rtw_mp_dump(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ u32 value; ++ u8 rf_type, path_nums = 0; ++ u32 i, j = 1, path; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (strncmp(extra, "all", 4) == 0) { ++ DBG_88E("\n ======= MAC REG =======\n"); ++ for (i = 0x0; i < 0x300; i += 4) { ++ if (j%4 == 1) ++ DBG_88E("0x%02x", i); ++ DBG_88E(" 0x%08x ", rtw_read32(padapter, i)); ++ if ((j++)%4 == 0) ++ DBG_88E("\n"); ++ } ++ for (i = 0x400; i < 0x1000; i += 4) { ++ if (j%4 == 1) ++ DBG_88E("0x%02x", i); ++ DBG_88E(" 0x%08x ", rtw_read32(padapter, i)); ++ if ((j++)%4 == 0) ++ DBG_88E("\n"); ++ } ++ ++ j = 1; ++ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type)); ++ ++ DBG_88E("\n ======= RF REG =======\n"); ++ if ((RF_1T2R == rf_type) || (RF_1T1R == rf_type)) ++ path_nums = 1; ++ else ++ path_nums = 2; ++ ++ for (path = 0; path < path_nums; path++) { ++ for (i = 0; i < 0x34; i++) { ++ value = rtw_hal_read_rfreg(padapter, path, i, 0xffffffff); ++ if (j%4 == 1) ++ DBG_88E("0x%02x ", i); ++ DBG_88E(" 0x%08x ", value); ++ if ((j++)%4 == 0) ++ DBG_88E("\n"); ++ } ++ } ++ } ++ return 0; ++} ++ ++static int rtw_mp_phypara(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *wrqu, char *extra) ++{ ++ char *input = kmalloc(wrqu->length, GFP_KERNEL); ++ u32 valxcap; ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->pointer, wrqu->length)) { ++ kfree(input); ++ return -EFAULT; ++ } ++ ++ DBG_88E("%s:iwpriv in =%s\n", __func__, input); ++ ++ sscanf(input, "xcap =%d", &valxcap); ++ ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_SetRFPath(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ char *input = kmalloc(wrqu->data.length, GFP_KERNEL); ++ u8 bMain = 1, bTurnoff = 1; ++ ++ if (!input) ++ return -ENOMEM; ++ if (copy_from_user(input, wrqu->data.pointer, wrqu->data.length)) ++ return -EFAULT; ++ DBG_88E("%s:iwpriv in =%s\n", __func__, input); ++ ++ bMain = strncmp(input, "1", 2); /* strncmp true is 0 */ ++ bTurnoff = strncmp(input, "0", 3); /* strncmp true is 0 */ ++ ++ if (bMain == 0) { ++ MP_PHY_SetRFPathSwitch(padapter, true); ++ DBG_88E("%s:PHY_SetRFPathSwitch = true\n", __func__); ++ } else if (bTurnoff == 0) { ++ MP_PHY_SetRFPathSwitch(padapter, false); ++ DBG_88E("%s:PHY_SetRFPathSwitch = false\n", __func__); ++ } ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_QueryDrv(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ char *input = kmalloc(wrqu->data.length, GFP_KERNEL); ++ u8 qAutoLoad = 1; ++ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); ++ ++ if (!input) ++ return -ENOMEM; ++ ++ if (copy_from_user(input, wrqu->data.pointer, wrqu->data.length)) ++ return -EFAULT; ++ DBG_88E("%s:iwpriv in =%s\n", __func__, input); ++ ++ qAutoLoad = strncmp(input, "autoload", 8); /* strncmp true is 0 */ ++ ++ if (qAutoLoad == 0) { ++ DBG_88E("%s:qAutoLoad\n", __func__); ++ ++ if (pEEPROM->bautoload_fail_flag) ++ sprintf(extra, "fail"); ++ else ++ sprintf(extra, "ok"); ++ } ++ wrqu->data.length = strlen(extra) + 1; ++ kfree(input); ++ return 0; ++} ++ ++static int rtw_mp_set(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wdata, char *extra) ++{ ++ struct iw_point *wrqu = (struct iw_point *)wdata; ++ u32 subcmd = wrqu->flags; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (padapter == NULL) ++ return -ENETDOWN; ++ ++ if (extra == NULL) { ++ wrqu->length = 0; ++ return -EIO; ++ } ++ ++ switch (subcmd) { ++ case MP_START: ++ DBG_88E("set case mp_start\n"); ++ rtw_mp_start(dev, info, wrqu, extra); ++ break; ++ case MP_STOP: ++ DBG_88E("set case mp_stop\n"); ++ rtw_mp_stop(dev, info, wrqu, extra); ++ break; ++ case MP_BANDWIDTH: ++ DBG_88E("set case mp_bandwidth\n"); ++ rtw_mp_bandwidth(dev, info, wrqu, extra); ++ break; ++ case MP_RESET_STATS: ++ DBG_88E("set case MP_RESET_STATS\n"); ++ rtw_mp_reset_stats(dev, info, wrqu, extra); ++ break; ++ case MP_SetRFPathSwh: ++ DBG_88E("set MP_SetRFPathSwitch\n"); ++ rtw_mp_SetRFPath(dev, info, wdata, extra); ++ break; ++ case CTA_TEST: ++ DBG_88E("set CTA_TEST\n"); ++ rtw_cta_test_start(dev, info, wdata, extra); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int rtw_mp_get(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wdata, char *extra) ++{ ++ struct iw_point *wrqu = (struct iw_point *)wdata; ++ u32 subcmd = wrqu->flags; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ ++ if (padapter == NULL) ++ return -ENETDOWN; ++ if (extra == NULL) { ++ wrqu->length = 0; ++ return -EIO; ++ } ++ ++ switch (subcmd) { ++ case WRITE_REG: ++ rtw_mp_write_reg(dev, info, wrqu, extra); ++ break; ++ case WRITE_RF: ++ rtw_mp_write_rf(dev, info, wrqu, extra); ++ break; ++ case MP_PHYPARA: ++ DBG_88E("mp_get MP_PHYPARA\n"); ++ rtw_mp_phypara(dev, info, wrqu, extra); ++ break; ++ case MP_CHANNEL: ++ DBG_88E("set case mp_channel\n"); ++ rtw_mp_channel(dev, info, wrqu, extra); ++ break; ++ case READ_REG: ++ DBG_88E("mp_get READ_REG\n"); ++ rtw_mp_read_reg(dev, info, wrqu, extra); ++ break; ++ case READ_RF: ++ DBG_88E("mp_get READ_RF\n"); ++ rtw_mp_read_rf(dev, info, wrqu, extra); ++ break; ++ case MP_RATE: ++ DBG_88E("set case mp_rate\n"); ++ rtw_mp_rate(dev, info, wrqu, extra); ++ break; ++ case MP_TXPOWER: ++ DBG_88E("set case MP_TXPOWER\n"); ++ rtw_mp_txpower(dev, info, wrqu, extra); ++ break; ++ case MP_ANT_TX: ++ DBG_88E("set case MP_ANT_TX\n"); ++ rtw_mp_ant_tx(dev, info, wrqu, extra); ++ break; ++ case MP_ANT_RX: ++ DBG_88E("set case MP_ANT_RX\n"); ++ rtw_mp_ant_rx(dev, info, wrqu, extra); ++ break; ++ case MP_QUERY: ++ rtw_mp_trx_query(dev, info, wrqu, extra); ++ break; ++ case MP_CTX: ++ DBG_88E("set case MP_CTX\n"); ++ rtw_mp_ctx(dev, info, wrqu, extra); ++ break; ++ case MP_ARX: ++ DBG_88E("set case MP_ARX\n"); ++ rtw_mp_arx(dev, info, wrqu, extra); ++ break; ++ case EFUSE_GET: ++ DBG_88E("efuse get EFUSE_GET\n"); ++ rtw_mp_efuse_get(dev, info, wdata, extra); ++ break; ++ case MP_DUMP: ++ DBG_88E("set case MP_DUMP\n"); ++ rtw_mp_dump(dev, info, wrqu, extra); ++ break; ++ case MP_PSD: ++ DBG_88E("set case MP_PSD\n"); ++ rtw_mp_psd(dev, info, wrqu, extra); ++ break; ++ case MP_THER: ++ DBG_88E("set case MP_THER\n"); ++ rtw_mp_thermal(dev, info, wrqu, extra); ++ break; ++ case MP_QueryDrvStats: ++ DBG_88E("mp_get MP_QueryDrvStats\n"); ++ rtw_mp_QueryDrv (dev, info, wdata, extra); ++ break; ++ case MP_PWRTRK: ++ DBG_88E("set case MP_PWRTRK\n"); ++ rtw_mp_pwrtrk(dev, info, wrqu, extra); ++ break; ++ case EFUSE_SET: ++ DBG_88E("set case efuse set\n"); ++ rtw_mp_efuse_set(dev, info, wdata, extra); ++ break; ++ } ++ ++ rtw_msleep_os(10); /* delay 5ms for sending pkt before exit adb shell operation */ ++ return 0; ++} ++ ++static int rtw_tdls(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ return 0; ++} ++ ++static int rtw_tdls_get(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ return 0; ++} ++ ++static int rtw_test( ++ struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ u32 len; ++ u8 *pbuf, *pch; ++ char *ptmp; ++ u8 *delim = ","; ++ ++ DBG_88E("+%s\n", __func__); ++ len = wrqu->data.length; ++ ++ pbuf = (u8 *)rtw_zmalloc(len); ++ if (pbuf == NULL) { ++ DBG_88E("%s: no memory!\n", __func__); ++ return -ENOMEM; ++ } ++ ++ if (copy_from_user(pbuf, wrqu->data.pointer, len)) { ++ kfree(pbuf); ++ DBG_88E("%s: copy from user fail!\n", __func__); ++ return -EFAULT; ++ } ++ DBG_88E("%s: string =\"%s\"\n", __func__, pbuf); ++ ++ ptmp = (char *)pbuf; ++ pch = strsep(&ptmp, delim); ++ if ((pch == NULL) || (strlen(pch) == 0)) { ++ kfree(pbuf); ++ DBG_88E("%s: parameter error(level 1)!\n", __func__); ++ return -EFAULT; ++ } ++ kfree(pbuf); ++ return 0; ++} ++ ++static iw_handler rtw_handlers[] = { ++ NULL, /* SIOCSIWCOMMIT */ ++ rtw_wx_get_name, /* SIOCGIWNAME */ ++ dummy, /* SIOCSIWNWID */ ++ dummy, /* SIOCGIWNWID */ ++ rtw_wx_set_freq, /* SIOCSIWFREQ */ ++ rtw_wx_get_freq, /* SIOCGIWFREQ */ ++ rtw_wx_set_mode, /* SIOCSIWMODE */ ++ rtw_wx_get_mode, /* SIOCGIWMODE */ ++ dummy, /* SIOCSIWSENS */ ++ rtw_wx_get_sens, /* SIOCGIWSENS */ ++ NULL, /* SIOCSIWRANGE */ ++ rtw_wx_get_range, /* SIOCGIWRANGE */ ++ rtw_wx_set_priv, /* SIOCSIWPRIV */ ++ NULL, /* SIOCGIWPRIV */ ++ NULL, /* SIOCSIWSTATS */ ++ NULL, /* SIOCGIWSTATS */ ++ dummy, /* SIOCSIWSPY */ ++ dummy, /* SIOCGIWSPY */ ++ NULL, /* SIOCGIWTHRSPY */ ++ NULL, /* SIOCWIWTHRSPY */ ++ rtw_wx_set_wap, /* SIOCSIWAP */ ++ rtw_wx_get_wap, /* SIOCGIWAP */ ++ rtw_wx_set_mlme, /* request MLME operation; uses struct iw_mlme */ ++ dummy, /* SIOCGIWAPLIST -- depricated */ ++ rtw_wx_set_scan, /* SIOCSIWSCAN */ ++ rtw_wx_get_scan, /* SIOCGIWSCAN */ ++ rtw_wx_set_essid, /* SIOCSIWESSID */ ++ rtw_wx_get_essid, /* SIOCGIWESSID */ ++ dummy, /* SIOCSIWNICKN */ ++ rtw_wx_get_nick, /* SIOCGIWNICKN */ ++ NULL, /* -- hole -- */ ++ NULL, /* -- hole -- */ ++ rtw_wx_set_rate, /* SIOCSIWRATE */ ++ rtw_wx_get_rate, /* SIOCGIWRATE */ ++ rtw_wx_set_rts, /* SIOCSIWRTS */ ++ rtw_wx_get_rts, /* SIOCGIWRTS */ ++ rtw_wx_set_frag, /* SIOCSIWFRAG */ ++ rtw_wx_get_frag, /* SIOCGIWFRAG */ ++ dummy, /* SIOCSIWTXPOW */ ++ dummy, /* SIOCGIWTXPOW */ ++ dummy, /* SIOCSIWRETRY */ ++ rtw_wx_get_retry, /* SIOCGIWRETRY */ ++ rtw_wx_set_enc, /* SIOCSIWENCODE */ ++ rtw_wx_get_enc, /* SIOCGIWENCODE */ ++ dummy, /* SIOCSIWPOWER */ ++ rtw_wx_get_power, /* SIOCGIWPOWER */ ++ NULL, /*---hole---*/ ++ NULL, /*---hole---*/ ++ rtw_wx_set_gen_ie, /* SIOCSIWGENIE */ ++ NULL, /* SIOCGWGENIE */ ++ rtw_wx_set_auth, /* SIOCSIWAUTH */ ++ NULL, /* SIOCGIWAUTH */ ++ rtw_wx_set_enc_ext, /* SIOCSIWENCODEEXT */ ++ NULL, /* SIOCGIWENCODEEXT */ ++ rtw_wx_set_pmkid, /* SIOCSIWPMKSA */ ++ NULL, /*---hole---*/ ++}; ++ ++static const struct iw_priv_args rtw_private_args[] = { ++ { ++ SIOCIWFIRSTPRIV + 0x0, ++ IW_PRIV_TYPE_CHAR | 0x7FF, 0, "write" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x1, ++ IW_PRIV_TYPE_CHAR | 0x7FF, ++ IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "read" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x2, 0, 0, "driver_ext" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x3, 0, 0, "mp_ioctl" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x4, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "apinfo" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x5, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setpid" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x6, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wps_start" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x7, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "get_sensitivity" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x8, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wps_prob_req_ie" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x9, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wps_assoc_req_ie" ++ }, ++ ++ { ++ SIOCIWFIRSTPRIV + 0xA, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "channel_plan" ++ }, ++ ++ { ++ SIOCIWFIRSTPRIV + 0xB, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "dbg" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0xC, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, "rfw" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0xD, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "rfr" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x10, ++ IW_PRIV_TYPE_CHAR | P2P_PRIVATE_IOCTL_SET_LEN, 0, "p2p_set" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x11, ++ IW_PRIV_TYPE_CHAR | P2P_PRIVATE_IOCTL_SET_LEN, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | P2P_PRIVATE_IOCTL_SET_LEN, "p2p_get" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x12, ++ IW_PRIV_TYPE_CHAR | P2P_PRIVATE_IOCTL_SET_LEN, IW_PRIV_TYPE_CHAR | IFNAMSIZ, "p2p_get2" ++ }, ++ {SIOCIWFIRSTPRIV + 0x13, IW_PRIV_TYPE_CHAR | 128, 0, "NULL"}, ++ { ++ SIOCIWFIRSTPRIV + 0x14, ++ IW_PRIV_TYPE_CHAR | 64, 0, "tdls" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x15, ++ IW_PRIV_TYPE_CHAR | P2P_PRIVATE_IOCTL_SET_LEN, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | P2P_PRIVATE_IOCTL_SET_LEN, "tdls_get" ++ }, ++ { ++ SIOCIWFIRSTPRIV + 0x16, ++ IW_PRIV_TYPE_CHAR | 64, 0, "pm_set" ++ }, ++ ++ {SIOCIWFIRSTPRIV + 0x18, IW_PRIV_TYPE_CHAR | IFNAMSIZ, 0, "rereg_nd_name"}, ++ ++ {SIOCIWFIRSTPRIV + 0x1A, IW_PRIV_TYPE_CHAR | 1024, 0, "efuse_set"}, ++ {SIOCIWFIRSTPRIV + 0x1B, IW_PRIV_TYPE_CHAR | 128, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "efuse_get"}, ++ {SIOCIWFIRSTPRIV + 0x1D, IW_PRIV_TYPE_CHAR | 40, IW_PRIV_TYPE_CHAR | 0x7FF, "test" ++ }, ++ ++ {SIOCIWFIRSTPRIV + 0x0E, IW_PRIV_TYPE_CHAR | 1024, 0, ""}, /* set */ ++ {SIOCIWFIRSTPRIV + 0x0F, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, ""},/* get */ ++/* --- sub-ioctls definitions --- */ ++ ++ {MP_START, IW_PRIV_TYPE_CHAR | 1024, 0, "mp_start"}, /* set */ ++ {MP_PHYPARA, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_phypara"},/* get */ ++ {MP_STOP, IW_PRIV_TYPE_CHAR | 1024, 0, "mp_stop"}, /* set */ ++ {MP_CHANNEL, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_channel"},/* get */ ++ {MP_BANDWIDTH, IW_PRIV_TYPE_CHAR | 1024, 0, "mp_bandwidth"}, /* set */ ++ {MP_RATE, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_rate"},/* get */ ++ {MP_RESET_STATS, IW_PRIV_TYPE_CHAR | 1024, 0, "mp_reset_stats"}, ++ {MP_QUERY, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_query"}, /* get */ ++ {READ_REG, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "read_reg"}, ++ {MP_RATE, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_rate"}, ++ {READ_RF, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "read_rf"}, ++ {MP_PSD, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_psd"}, ++ {MP_DUMP, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_dump"}, ++ {MP_TXPOWER, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_txpower"}, ++ {MP_ANT_TX, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_ant_tx"}, ++ {MP_ANT_RX, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_ant_rx"}, ++ {WRITE_REG, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "write_reg"}, ++ {WRITE_RF, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "write_rf"}, ++ {MP_CTX, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_ctx"}, ++ {MP_ARX, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_arx"}, ++ {MP_THER, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_ther"}, ++ {EFUSE_SET, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "efuse_set"}, ++ {EFUSE_GET, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "efuse_get"}, ++ {MP_PWRTRK, IW_PRIV_TYPE_CHAR | 1024, 0, "mp_pwrtrk"}, ++ {MP_QueryDrvStats, IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_MASK, "mp_drvquery"}, ++ {MP_IOCTL, IW_PRIV_TYPE_CHAR | 1024, 0, "mp_ioctl"}, /* mp_ioctl */ ++ {MP_SetRFPathSwh, IW_PRIV_TYPE_CHAR | 1024, 0, "mp_setrfpath"}, ++ {CTA_TEST, IW_PRIV_TYPE_CHAR | 1024, 0, "cta_test"}, ++}; ++ ++static iw_handler rtw_private_handler[] = { ++rtw_wx_write32, /* 0x00 */ ++rtw_wx_read32, /* 0x01 */ ++rtw_drvext_hdl, /* 0x02 */ ++rtw_mp_ioctl_hdl, /* 0x03 */ ++ ++/* for MM DTV platform */ ++ rtw_get_ap_info, /* 0x04 */ ++ ++ rtw_set_pid, /* 0x05 */ ++ rtw_wps_start, /* 0x06 */ ++ ++ rtw_wx_get_sensitivity, /* 0x07 */ ++ rtw_wx_set_mtk_wps_probe_ie, /* 0x08 */ ++ rtw_wx_set_mtk_wps_ie, /* 0x09 */ ++ ++/* Set Channel depend on the country code */ ++ rtw_wx_set_channel_plan, /* 0x0A */ ++ ++ rtw_dbg_port, /* 0x0B */ ++ rtw_wx_write_rf, /* 0x0C */ ++ rtw_wx_read_rf, /* 0x0D */ ++ ++ rtw_mp_set, /* 0x0E */ ++ rtw_mp_get, /* 0x0F */ ++ rtw_p2p_set, /* 0x10 */ ++ rtw_p2p_get, /* 0x11 */ ++ rtw_p2p_get2, /* 0x12 */ ++ ++ NULL, /* 0x13 */ ++ rtw_tdls, /* 0x14 */ ++ rtw_tdls_get, /* 0x15 */ ++ ++ rtw_pm_set, /* 0x16 */ ++ rtw_wx_priv_null, /* 0x17 */ ++ rtw_rereg_nd_name, /* 0x18 */ ++ rtw_wx_priv_null, /* 0x19 */ ++ ++ rtw_mp_efuse_set, /* 0x1A */ ++ rtw_mp_efuse_get, /* 0x1B */ ++ NULL, /* 0x1C is reserved for hostapd */ ++ rtw_test, /* 0x1D */ ++}; ++ ++static struct iw_statistics *rtw_get_wireless_stats(struct net_device *dev) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); ++ struct iw_statistics *piwstats = &padapter->iwstats; ++ int tmp_noise = 0; ++ int tmp; ++ ++ if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED)) { ++ piwstats->qual.qual = 0; ++ piwstats->qual.level = 0; ++ piwstats->qual.noise = 0; ++ } else { ++ tmp_noise = padapter->recvpriv.noise; ++ ++ piwstats->qual.level = padapter->signal_strength; ++ tmp = 219 + 3 * padapter->signal_strength; ++ tmp = min(100, tmp); ++ tmp = max(0, tmp); ++ piwstats->qual.qual = tmp; ++ piwstats->qual.noise = tmp_noise; ++ } ++ piwstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; ++ return &padapter->iwstats; ++} ++ ++struct iw_handler_def rtw_handlers_def = { ++ .standard = rtw_handlers, ++ .num_standard = sizeof(rtw_handlers) / sizeof(iw_handler), ++ .private = rtw_private_handler, ++ .private_args = (struct iw_priv_args *)rtw_private_args, ++ .num_private = sizeof(rtw_private_handler) / sizeof(iw_handler), ++ .num_private_args = sizeof(rtw_private_args) / sizeof(struct iw_priv_args), ++ .get_wireless_stats = rtw_get_wireless_stats, ++}; ++ ++/* copy from net/wireless/wext.c start */ ++/* ---------------------------------------------------------------- */ ++/* ++ * Calculate size of private arguments ++ */ ++static const char iw_priv_type_size[] = { ++ 0, /* IW_PRIV_TYPE_NONE */ ++ 1, /* IW_PRIV_TYPE_BYTE */ ++ 1, /* IW_PRIV_TYPE_CHAR */ ++ 0, /* Not defined */ ++ sizeof(__u32), /* IW_PRIV_TYPE_INT */ ++ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ ++ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ ++ 0, /* Not defined */ ++}; ++ ++static int get_priv_size(__u16 args) ++{ ++ int num = args & IW_PRIV_SIZE_MASK; ++ int type = (args & IW_PRIV_TYPE_MASK) >> 12; ++ ++ return num * iw_priv_type_size[type]; ++} ++/* copy from net/wireless/wext.c end */ ++ ++static int rtw_ioctl_wext_private(struct net_device *dev, union iwreq_data *wrq_data) ++{ ++ int err = 0; ++ u8 *input = NULL; ++ u32 input_len = 0; ++ const char delim[] = " "; ++ u8 *output = NULL; ++ u32 output_len = 0; ++ u32 count = 0; ++ u8 *buffer = NULL; ++ u32 buffer_len = 0; ++ char *ptr = NULL; ++ u8 cmdname[17] = {0}; /* IFNAMSIZ+1 */ ++ u32 cmdlen; ++ s32 len; ++ u8 *extra = NULL; ++ u32 extra_size = 0; ++ ++ s32 k; ++ const iw_handler *priv; /* Private ioctl */ ++ const struct iw_priv_args *priv_args; /* Private ioctl description */ ++ u32 num_priv_args; /* Number of descriptions */ ++ iw_handler handler; ++ int temp; ++ int subcmd = 0; /* sub-ioctl index */ ++ int offset = 0; /* Space for sub-ioctl index */ ++ ++ union iwreq_data wdata; ++ ++ memcpy(&wdata, wrq_data, sizeof(wdata)); ++ ++ input_len = wdata.data.length; ++ if (input_len == 0) ++ return -EFAULT; ++ input = rtw_zmalloc(input_len); ++ if (NULL == input) ++ return -ENOMEM; ++ if (copy_from_user(input, wdata.data.pointer, input_len)) { ++ err = -EFAULT; ++ goto exit; ++ } ++ ptr = input; ++ len = input_len; ++ ++ sscanf(ptr, "%16s", cmdname); ++ cmdlen = strlen(cmdname); ++ DBG_88E("%s: cmd =%s\n", __func__, cmdname); ++ ++ /* skip command string */ ++ if (cmdlen > 0) ++ cmdlen += 1; /* skip one space */ ++ ptr += cmdlen; ++ len -= cmdlen; ++ DBG_88E("%s: parameters =%s\n", __func__, ptr); ++ ++ priv = rtw_private_handler; ++ priv_args = rtw_private_args; ++ num_priv_args = sizeof(rtw_private_args) / sizeof(struct iw_priv_args); ++ ++ if (num_priv_args == 0) { ++ err = -EOPNOTSUPP; ++ goto exit; ++ } ++ ++ /* Search the correct ioctl */ ++ k = -1; ++ while ((++k < num_priv_args) && strcmp(priv_args[k].name, cmdname)); ++ ++ /* If not found... */ ++ if (k == num_priv_args) { ++ err = -EOPNOTSUPP; ++ goto exit; ++ } ++ ++ /* Watch out for sub-ioctls ! */ ++ if (priv_args[k].cmd < SIOCDEVPRIVATE) { ++ int j = -1; ++ ++ /* Find the matching *real* ioctl */ ++ while ((++j < num_priv_args) && ((priv_args[j].name[0] != '\0') || ++ (priv_args[j].set_args != priv_args[k].set_args) || ++ (priv_args[j].get_args != priv_args[k].get_args))); ++ ++ /* If not found... */ ++ if (j == num_priv_args) { ++ err = -EINVAL; ++ goto exit; ++ } ++ ++ /* Save sub-ioctl number */ ++ subcmd = priv_args[k].cmd; ++ /* Reserve one int (simplify alignment issues) */ ++ offset = sizeof(__u32); ++ /* Use real ioctl definition from now on */ ++ k = j; ++ } ++ ++ buffer = rtw_zmalloc(4096); ++ if (NULL == buffer) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ /* If we have to set some data */ ++ if ((priv_args[k].set_args & IW_PRIV_TYPE_MASK) && ++ (priv_args[k].set_args & IW_PRIV_SIZE_MASK)) { ++ u8 *str; ++ ++ switch (priv_args[k].set_args & IW_PRIV_TYPE_MASK) { ++ case IW_PRIV_TYPE_BYTE: ++ /* Fetch args */ ++ count = 0; ++ do { ++ str = strsep(&ptr, delim); ++ if (NULL == str) ++ break; ++ sscanf(str, "%i", &temp); ++ buffer[count++] = (u8)temp; ++ } while (1); ++ buffer_len = count; ++ /* Number of args to fetch */ ++ wdata.data.length = count; ++ if (wdata.data.length > (priv_args[k].set_args & IW_PRIV_SIZE_MASK)) ++ wdata.data.length = priv_args[k].set_args & IW_PRIV_SIZE_MASK; ++ break; ++ case IW_PRIV_TYPE_INT: ++ /* Fetch args */ ++ count = 0; ++ do { ++ str = strsep(&ptr, delim); ++ if (NULL == str) ++ break; ++ sscanf(str, "%i", &temp); ++ ((s32 *)buffer)[count++] = (s32)temp; ++ } while (1); ++ buffer_len = count * sizeof(s32); ++ /* Number of args to fetch */ ++ wdata.data.length = count; ++ if (wdata.data.length > (priv_args[k].set_args & IW_PRIV_SIZE_MASK)) ++ wdata.data.length = priv_args[k].set_args & IW_PRIV_SIZE_MASK; ++ break; ++ case IW_PRIV_TYPE_CHAR: ++ if (len > 0) { ++ /* Size of the string to fetch */ ++ wdata.data.length = len; ++ if (wdata.data.length > (priv_args[k].set_args & IW_PRIV_SIZE_MASK)) ++ wdata.data.length = priv_args[k].set_args & IW_PRIV_SIZE_MASK; ++ ++ /* Fetch string */ ++ memcpy(buffer, ptr, wdata.data.length); ++ } else { ++ wdata.data.length = 1; ++ buffer[0] = '\0'; ++ } ++ buffer_len = wdata.data.length; ++ break; ++ default: ++ DBG_88E("%s: Not yet implemented...\n", __func__); ++ err = -1; ++ goto exit; ++ } ++ ++ if ((priv_args[k].set_args & IW_PRIV_SIZE_FIXED) && ++ (wdata.data.length != (priv_args[k].set_args & IW_PRIV_SIZE_MASK))) { ++ DBG_88E("%s: The command %s needs exactly %d argument(s)...\n", ++ __func__, cmdname, priv_args[k].set_args & IW_PRIV_SIZE_MASK); ++ err = -EINVAL; ++ goto exit; ++ } ++ } else { ++ /* if args to set */ ++ wdata.data.length = 0L; ++ } ++ ++ /* Those two tests are important. They define how the driver ++ * will have to handle the data */ ++ if ((priv_args[k].set_args & IW_PRIV_SIZE_FIXED) && ++ ((get_priv_size(priv_args[k].set_args) + offset) <= IFNAMSIZ)) { ++ /* First case : all SET args fit within wrq */ ++ if (offset) ++ wdata.mode = subcmd; ++ memcpy(wdata.name + offset, buffer, IFNAMSIZ - offset); ++ } else { ++ if ((priv_args[k].set_args == 0) && ++ (priv_args[k].get_args & IW_PRIV_SIZE_FIXED) && ++ (get_priv_size(priv_args[k].get_args) <= IFNAMSIZ)) { ++ /* Second case : no SET args, GET args fit within wrq */ ++ if (offset) ++ wdata.mode = subcmd; ++ } else { ++ /* Third case : args won't fit in wrq, or variable number of args */ ++ if (copy_to_user(wdata.data.pointer, buffer, buffer_len)) { ++ err = -EFAULT; ++ goto exit; ++ } ++ wdata.data.flags = subcmd; ++ } ++ } ++ ++ kfree(input); ++ input = NULL; ++ ++ extra_size = 0; ++ if (IW_IS_SET(priv_args[k].cmd)) { ++ /* Size of set arguments */ ++ extra_size = get_priv_size(priv_args[k].set_args); ++ ++ /* Does it fits in iwr ? */ ++ if ((priv_args[k].set_args & IW_PRIV_SIZE_FIXED) && ++ ((extra_size + offset) <= IFNAMSIZ)) ++ extra_size = 0; ++ } else { ++ /* Size of get arguments */ ++ extra_size = get_priv_size(priv_args[k].get_args); ++ ++ /* Does it fits in iwr ? */ ++ if ((priv_args[k].get_args & IW_PRIV_SIZE_FIXED) && ++ (extra_size <= IFNAMSIZ)) ++ extra_size = 0; ++ } ++ ++ if (extra_size == 0) { ++ extra = (u8 *)&wdata; ++ kfree(buffer); ++ buffer = NULL; ++ } else { ++ extra = buffer; ++ } ++ ++ handler = priv[priv_args[k].cmd - SIOCIWFIRSTPRIV]; ++ err = handler(dev, NULL, &wdata, extra); ++ ++ /* If we have to get some data */ ++ if ((priv_args[k].get_args & IW_PRIV_TYPE_MASK) && ++ (priv_args[k].get_args & IW_PRIV_SIZE_MASK)) { ++ int j; ++ int n = 0; /* number of args */ ++ u8 str[20] = {0}; ++ ++ /* Check where is the returned data */ ++ if ((priv_args[k].get_args & IW_PRIV_SIZE_FIXED) && ++ (get_priv_size(priv_args[k].get_args) <= IFNAMSIZ)) ++ n = priv_args[k].get_args & IW_PRIV_SIZE_MASK; ++ else ++ n = wdata.data.length; ++ ++ output = rtw_zmalloc(4096); ++ if (NULL == output) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ switch (priv_args[k].get_args & IW_PRIV_TYPE_MASK) { ++ case IW_PRIV_TYPE_BYTE: ++ /* Display args */ ++ for (j = 0; j < n; j++) { ++ sprintf(str, "%d ", extra[j]); ++ len = strlen(str); ++ output_len = strlen(output); ++ if ((output_len + len + 1) > 4096) { ++ err = -E2BIG; ++ goto exit; ++ } ++ memcpy(output+output_len, str, len); ++ } ++ break; ++ case IW_PRIV_TYPE_INT: ++ /* Display args */ ++ for (j = 0; j < n; j++) { ++ sprintf(str, "%d ", ((__s32 *)extra)[j]); ++ len = strlen(str); ++ output_len = strlen(output); ++ if ((output_len + len + 1) > 4096) { ++ err = -E2BIG; ++ goto exit; ++ } ++ memcpy(output+output_len, str, len); ++ } ++ break; ++ case IW_PRIV_TYPE_CHAR: ++ /* Display args */ ++ memcpy(output, extra, n); ++ break; ++ default: ++ DBG_88E("%s: Not yet implemented...\n", __func__); ++ err = -1; ++ goto exit; ++ } ++ ++ output_len = strlen(output) + 1; ++ wrq_data->data.length = output_len; ++ if (copy_to_user(wrq_data->data.pointer, output, output_len)) { ++ err = -EFAULT; ++ goto exit; ++ } ++ } else { ++ /* if args to set */ ++ wrq_data->data.length = 0; ++ } ++ ++exit: ++ kfree(input); ++ kfree(buffer); ++ kfree(output); ++ return err; ++} ++ ++#include ++int rtw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct iwreq *wrq = (struct iwreq *)rq; ++ int ret = 0; ++ ++ switch (cmd) { ++ case RTL_IOCTL_WPA_SUPPLICANT: ++ ret = wpa_supplicant_ioctl(dev, &wrq->u.data); ++ break; ++#ifdef CONFIG_88EU_AP_MODE ++ case RTL_IOCTL_HOSTAPD: ++ ret = rtw_hostapd_ioctl(dev, &wrq->u.data); ++ break; ++#endif /* CONFIG_88EU_AP_MODE */ ++ case SIOCDEVPRIVATE: ++ ret = rtw_ioctl_wext_private(dev, &wrq->u); ++ break; ++ case (SIOCDEVPRIVATE+1): ++ ret = rtw_android_priv_cmd(dev, rq, cmd); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ return ret; ++} +diff --git a/drivers/staging/r8188eu/os_dep/mlme_linux.c b/drivers/staging/r8188eu/os_dep/mlme_linux.c +new file mode 100644 +index 000000000000..97c2b62f8fd4 +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/mlme_linux.c +@@ -0,0 +1,302 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++ ++#define _MLME_OSDEP_C_ ++ ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++void rtw_join_timeout_handler (void *FunctionContext) ++#else ++void rtw_join_timeout_handler (struct timer_list *t) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ struct adapter *adapter = (struct adapter *)FunctionContext; ++#else ++ struct adapter *adapter = from_timer(adapter, t, mlmepriv.assoc_timer); ++#endif ++ ++ _rtw_join_timeout_handler(adapter); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++void _rtw_scan_timeout_handler (void *FunctionContext) ++#else ++void _rtw_scan_timeout_handler (struct timer_list *t) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ struct adapter *adapter = (struct adapter *)FunctionContext; ++#else ++ struct adapter *adapter = from_timer(adapter, t, mlmepriv.scan_to_timer); ++#endif ++ ++ rtw_scan_timeout_handler(adapter); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++static void _dynamic_check_timer_handlder(void *FunctionContext) ++#else ++static void _dynamic_check_timer_handlder(struct timer_list *t) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ struct adapter *adapter = (struct adapter *)FunctionContext; ++#else ++ struct adapter *adapter = from_timer(adapter, t, mlmepriv.dynamic_chk_timer); ++#endif ++ ++ if (adapter->registrypriv.mp_mode == 1) ++ return; ++ rtw_dynamic_check_timer_handlder(adapter); ++ _set_timer(&adapter->mlmepriv.dynamic_chk_timer, 2000); ++} ++ ++void rtw_init_mlme_timer(struct adapter *padapter) ++{ ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ _init_timer(&(pmlmepriv->assoc_timer), padapter->pnetdev, rtw_join_timeout_handler, padapter); ++ _init_timer(&(pmlmepriv->scan_to_timer), padapter->pnetdev, _rtw_scan_timeout_handler, padapter); ++ _init_timer(&(pmlmepriv->dynamic_chk_timer), padapter->pnetdev, _dynamic_check_timer_handlder, padapter); ++#else ++ timer_setup(&pmlmepriv->assoc_timer, rtw_join_timeout_handler, 0); ++ timer_setup(&pmlmepriv->scan_to_timer, _rtw_scan_timeout_handler, 0); ++ timer_setup(&pmlmepriv->dynamic_chk_timer, _dynamic_check_timer_handlder, 0); ++#endif ++} ++ ++void rtw_os_indicate_connect(struct adapter *adapter) ++{ ++ ++ rtw_indicate_wx_assoc_event(adapter); ++ netif_carrier_on(adapter->pnetdev); ++ if (adapter->pid[2] != 0) ++ rtw_signal_process(adapter->pid[2], SIGALRM); ++ ++} ++ ++void rtw_os_indicate_scan_done(struct adapter *padapter, bool aborted) ++{ ++ indicate_wx_scan_complete_event(padapter); ++} ++ ++static struct rt_pmkid_list backup_pmkid[NUM_PMKID_CACHE]; ++ ++void rtw_reset_securitypriv(struct adapter *adapter) ++{ ++ u8 backup_index = 0; ++ u8 backup_counter = 0x00; ++ u32 backup_time = 0; ++ ++ if (adapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) { ++ /* 802.1x */ ++ /* We have to backup the PMK information for WiFi PMK Caching test item. */ ++ /* Backup the btkip_countermeasure information. */ ++ /* When the countermeasure is trigger, the driver have to disconnect with AP for 60 seconds. */ ++ memset(&backup_pmkid[0], 0x00, sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE); ++ memcpy(&backup_pmkid[0], &adapter->securitypriv.PMKIDList[0], sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE); ++ backup_index = adapter->securitypriv.PMKIDIndex; ++ backup_counter = adapter->securitypriv.btkip_countermeasure; ++ backup_time = adapter->securitypriv.btkip_countermeasure_time; ++ memset((unsigned char *)&adapter->securitypriv, 0, sizeof(struct security_priv)); ++ ++ /* Restore the PMK information to securitypriv structure for the following connection. */ ++ memcpy(&adapter->securitypriv.PMKIDList[0], ++ &backup_pmkid[0], ++ sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE); ++ adapter->securitypriv.PMKIDIndex = backup_index; ++ adapter->securitypriv.btkip_countermeasure = backup_counter; ++ adapter->securitypriv.btkip_countermeasure_time = backup_time; ++ adapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen; ++ adapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled; ++ } else { ++ /* reset values in securitypriv */ ++ struct security_priv *psec_priv = &adapter->securitypriv; ++ ++ psec_priv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ ++ psec_priv->dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ psec_priv->dot11PrivacyKeyIndex = 0; ++ psec_priv->dot118021XGrpPrivacy = _NO_PRIVACY_; ++ psec_priv->dot118021XGrpKeyid = 1; ++ psec_priv->ndisauthtype = Ndis802_11AuthModeOpen; ++ psec_priv->ndisencryptstatus = Ndis802_11WEPDisabled; ++ } ++} ++ ++void rtw_os_indicate_disconnect(struct adapter *adapter) ++{ ++ ++ netif_carrier_off(adapter->pnetdev); /* Do it first for tx broadcast pkt after disconnection issue! */ ++ rtw_indicate_wx_disassoc_event(adapter); ++ rtw_reset_securitypriv(adapter); ++ ++} ++ ++void rtw_report_sec_ie(struct adapter *adapter, u8 authmode, u8 *sec_ie) ++{ ++ uint len; ++ u8 *buff, *p, i; ++ union iwreq_data wrqu; ++ ++ RT_TRACE(_module_mlme_osdep_c_, _drv_info_, ++ ("+rtw_report_sec_ie, authmode=%d\n", authmode)); ++ buff = NULL; ++ if (authmode == _WPA_IE_ID_) { ++ RT_TRACE(_module_mlme_osdep_c_, _drv_info_, ++ ("rtw_report_sec_ie, authmode=%d\n", authmode)); ++ buff = rtw_malloc(IW_CUSTOM_MAX); ++ if (!buff) ++ return; ++ memset(buff, 0, IW_CUSTOM_MAX); ++ p = buff; ++ p += sprintf(p, "ASSOCINFO(ReqIEs ="); ++ len = sec_ie[1]+2; ++ len = (len < IW_CUSTOM_MAX) ? len : IW_CUSTOM_MAX; ++ for (i = 0; i < len; i++) ++ p += sprintf(p, "%02x", sec_ie[i]); ++ p += sprintf(p, ")"); ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wrqu.data.length = p-buff; ++ wrqu.data.length = (wrqu.data.length < IW_CUSTOM_MAX) ? ++ wrqu.data.length : IW_CUSTOM_MAX; ++ wireless_send_event(adapter->pnetdev, IWEVCUSTOM, &wrqu, buff); ++ kfree(buff); ++ } ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++static void _survey_timer_hdl(void *FunctionContext) ++#else ++static void _survey_timer_hdl(struct timer_list *t) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ struct adapter *padapter = (struct adapter *)FunctionContext; ++#else ++ struct adapter *padapter = from_timer(padapter, t, mlmeextpriv.survey_timer); ++#endif ++ ++ survey_timer_hdl(padapter); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++static void _link_timer_hdl(void *FunctionContext) ++#else ++static void _link_timer_hdl(struct timer_list *t) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ struct adapter *padapter = (struct adapter *)FunctionContext; ++#else ++ struct adapter *padapter = from_timer(padapter, t, mlmeextpriv.link_timer); ++#endif ++ link_timer_hdl(padapter); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++static void _addba_timer_hdl(void *FunctionContext) ++#else ++static void _addba_timer_hdl(struct timer_list *t) ++#endif ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ struct sta_info *psta = (struct sta_info *)FunctionContext; ++#else ++ struct sta_info *psta = from_timer(psta, t, addba_retry_timer); ++#endif ++ addba_timer_hdl(psta); ++} ++ ++void init_addba_retry_timer(struct adapter *padapter, struct sta_info *psta) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ _init_timer(&psta->addba_retry_timer, padapter->pnetdev, _addba_timer_hdl, psta); ++#else ++ timer_setup(&psta->addba_retry_timer, _addba_timer_hdl, 0); ++#endif ++} ++ ++void init_mlme_ext_timer(struct adapter *padapter) ++{ ++ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ _init_timer(&pmlmeext->survey_timer, padapter->pnetdev, _survey_timer_hdl, padapter); ++ _init_timer(&pmlmeext->link_timer, padapter->pnetdev, _link_timer_hdl, padapter); ++#else ++ timer_setup(&pmlmeext->survey_timer, _survey_timer_hdl, 0); ++ timer_setup(&pmlmeext->link_timer, _link_timer_hdl, 0); ++#endif ++} ++ ++#ifdef CONFIG_88EU_AP_MODE ++ ++void rtw_indicate_sta_assoc_event(struct adapter *padapter, struct sta_info *psta) ++{ ++ union iwreq_data wrqu; ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ ++ if (psta == NULL) ++ return; ++ ++ if (psta->aid > NUM_STA) ++ return; ++ ++ if (pstapriv->sta_aid[psta->aid - 1] != psta) ++ return; ++ ++ wrqu.addr.sa_family = ARPHRD_ETHER; ++ ++ memcpy(wrqu.addr.sa_data, psta->hwaddr, ETH_ALEN); ++ ++ DBG_88E("+rtw_indicate_sta_assoc_event\n"); ++ ++ wireless_send_event(padapter->pnetdev, IWEVREGISTERED, &wrqu, NULL); ++} ++ ++void rtw_indicate_sta_disassoc_event(struct adapter *padapter, struct sta_info *psta) ++{ ++ union iwreq_data wrqu; ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ ++ if (psta == NULL) ++ return; ++ ++ if (psta->aid > NUM_STA) ++ return; ++ ++ if (pstapriv->sta_aid[psta->aid - 1] != psta) ++ return; ++ ++ wrqu.addr.sa_family = ARPHRD_ETHER; ++ ++ memcpy(wrqu.addr.sa_data, psta->hwaddr, ETH_ALEN); ++ ++ DBG_88E("+rtw_indicate_sta_disassoc_event\n"); ++ ++ wireless_send_event(padapter->pnetdev, IWEVEXPIRED, &wrqu, NULL); ++} ++ ++#endif +diff --git a/drivers/staging/r8188eu/os_dep/os_intfs.c b/drivers/staging/r8188eu/os_dep/os_intfs.c +new file mode 100644 +index 000000000000..8c8ef9063b0c +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/os_intfs.c +@@ -0,0 +1,1283 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++#define _OS_INTFS_C_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Realtek Wireless Lan Driver"); ++MODULE_AUTHOR("Realtek Semiconductor Corp."); ++MODULE_VERSION(DRIVERVERSION); ++ ++#define CONFIG_BR_EXT_BRNAME "br0" ++#define RTW_NOTCH_FILTER 0 /* 0:Disable, 1:Enable, */ ++ ++/* module param defaults */ ++static int rtw_chip_version = 0x00; ++static int rtw_rfintfs = HWPI; ++static int rtw_lbkmode;/* RTL8712_AIR_TRX; */ ++static int rtw_network_mode = Ndis802_11IBSS;/* Ndis802_11Infrastructure; infra, ad-hoc, auto */ ++static int rtw_channel = 1;/* ad-hoc support requirement */ ++static int rtw_wireless_mode = WIRELESS_11BG_24N; ++static int rtw_vrtl_carrier_sense = AUTO_VCS; ++static int rtw_vcs_type = RTS_CTS;/* */ ++static int rtw_rts_thresh = 2347;/* */ ++static int rtw_frag_thresh = 2346;/* */ ++static int rtw_preamble = PREAMBLE_LONG;/* long, short, auto */ ++static int rtw_scan_mode = 1;/* active, passive */ ++static int rtw_adhoc_tx_pwr = 1; ++static int rtw_soft_ap; ++static int rtw_power_mgnt = 1; ++static int rtw_ips_mode = IPS_NORMAL; ++ ++static int rtw_smart_ps = 2; ++ ++module_param(rtw_ips_mode, int, 0644); ++MODULE_PARM_DESC(rtw_ips_mode, "The default IPS mode"); ++ ++static int rtw_debug = 1; ++static int rtw_radio_enable = 1; ++static int rtw_long_retry_lmt = 7; ++static int rtw_short_retry_lmt = 7; ++static int rtw_busy_thresh = 40; ++static int rtw_ack_policy = NORMAL_ACK; ++ ++static int rtw_mp_mode; ++ ++static int rtw_software_encrypt; ++static int rtw_software_decrypt; ++ ++static int rtw_acm_method;/* 0:By SW 1:By HW. */ ++ ++static int rtw_wmm_enable = 1;/* default is set to enable the wmm. */ ++static int rtw_uapsd_enable; ++static int rtw_uapsd_max_sp = NO_LIMIT; ++static int rtw_uapsd_acbk_en; ++static int rtw_uapsd_acbe_en; ++static int rtw_uapsd_acvi_en; ++static int rtw_uapsd_acvo_en; ++ ++static int rtw_led_enable = 1; ++ ++int rtw_ht_enable = 1; ++int rtw_cbw40_enable = 3; /* 0 :disable, bit(0): enable 2.4g, bit(1): enable 5g */ ++int rtw_ampdu_enable = 1;/* for enable tx_ampdu */ ++static int rtw_rx_stbc = 1;/* 0: disable, bit(0):enable 2.4g, bit(1):enable 5g, default is set to enable 2.4GHZ for IOT issue with bufflao's AP at 5GHZ */ ++static int rtw_ampdu_amsdu;/* 0: disabled, 1:enabled, 2:auto */ ++ ++static int rtw_lowrate_two_xmit = 1;/* Use 2 path Tx to transmit MCS0~7 and legacy mode */ ++ ++static int rtw_rf_config = RF_819X_MAX_TYPE; /* auto */ ++static int rtw_low_power; ++static int rtw_wifi_spec; ++static int rtw_channel_plan = RT_CHANNEL_DOMAIN_MAX; ++static int rtw_AcceptAddbaReq = true;/* 0:Reject AP's Add BA req, 1:Accept AP's Add BA req. */ ++ ++static int rtw_antdiv_cfg = 2; /* 0:OFF , 1:ON, 2:decide by Efuse config */ ++static int rtw_antdiv_type; /* 0:decide by efuse 1: for 88EE, 1Tx and 1RxCG are diversity.(2 Ant with SPDT), 2: for 88EE, 1Tx and 2Rx are diversity.(2 Ant, Tx and RxCG are both on aux port, RxCS is on main port), 3: for 88EE, 1Tx and 1RxCG are fixed.(1Ant, Tx and RxCG are both on aux port) */ ++ ++static int rtw_enusbss;/* 0:disable, 1:enable */ ++ ++static int rtw_hwpdn_mode = 2;/* 0:disable, 1:enable, 2: by EFUSE config */ ++ ++static int rtw_hwpwrp_detect; /* HW power ping detect 0:disable , 1:enable */ ++ ++static int rtw_hw_wps_pbc = 1; ++ ++int rtw_mc2u_disable; ++ ++static int rtw_80211d; ++ ++static char *ifname = "wlan%d"; ++module_param(ifname, charp, 0644); ++MODULE_PARM_DESC(ifname, "The default name to allocate for first interface"); ++ ++static char *if2name = "wlan%d"; ++module_param(if2name, charp, 0644); ++MODULE_PARM_DESC(if2name, "The default name to allocate for second interface"); ++ ++char *rtw_initmac; /* temp mac address if users want to use instead of the mac address in Efuse */ ++ ++module_param(rtw_initmac, charp, 0644); ++module_param(rtw_channel_plan, int, 0644); ++module_param(rtw_chip_version, int, 0644); ++module_param(rtw_rfintfs, int, 0644); ++module_param(rtw_lbkmode, int, 0644); ++module_param(rtw_network_mode, int, 0644); ++module_param(rtw_channel, int, 0644); ++module_param(rtw_mp_mode, int, 0644); ++module_param(rtw_wmm_enable, int, 0644); ++module_param(rtw_vrtl_carrier_sense, int, 0644); ++module_param(rtw_vcs_type, int, 0644); ++module_param(rtw_busy_thresh, int, 0644); ++module_param(rtw_led_enable, int, 0644); ++module_param(rtw_ht_enable, int, 0644); ++module_param(rtw_cbw40_enable, int, 0644); ++module_param(rtw_ampdu_enable, int, 0644); ++module_param(rtw_rx_stbc, int, 0644); ++module_param(rtw_ampdu_amsdu, int, 0644); ++module_param(rtw_lowrate_two_xmit, int, 0644); ++module_param(rtw_rf_config, int, 0644); ++module_param(rtw_power_mgnt, int, 0644); ++module_param(rtw_smart_ps, int, 0644); ++module_param(rtw_low_power, int, 0644); ++module_param(rtw_wifi_spec, int, 0644); ++module_param(rtw_antdiv_cfg, int, 0644); ++module_param(rtw_antdiv_type, int, 0644); ++module_param(rtw_enusbss, int, 0644); ++module_param(rtw_hwpdn_mode, int, 0644); ++module_param(rtw_hwpwrp_detect, int, 0644); ++module_param(rtw_hw_wps_pbc, int, 0644); ++ ++static uint rtw_max_roaming_times = 2; ++module_param(rtw_max_roaming_times, uint, 0644); ++MODULE_PARM_DESC(rtw_max_roaming_times, "The max roaming times to try"); ++ ++static int rtw_fw_iol = 1;/* 0:Disable, 1:enable, 2:by usb speed */ ++module_param(rtw_fw_iol, int, 0644); ++MODULE_PARM_DESC(rtw_fw_iol, "FW IOL"); ++ ++module_param(rtw_mc2u_disable, int, 0644); ++ ++module_param(rtw_80211d, int, 0644); ++MODULE_PARM_DESC(rtw_80211d, "Enable 802.11d mechanism"); ++ ++static uint rtw_notch_filter = RTW_NOTCH_FILTER; ++module_param(rtw_notch_filter, uint, 0644); ++MODULE_PARM_DESC(rtw_notch_filter, "0:Disable, 1:Enable, 2:Enable only for P2P"); ++module_param_named(debug, rtw_debug, int, 0444); ++MODULE_PARM_DESC(debug, "Set debug level (1-9) (default 1)"); ++ ++/* dummy routines */ ++void rtw_proc_remove_one(struct net_device *dev) ++{ ++} ++ ++void rtw_proc_init_one(struct net_device *dev) ++{ ++} ++ ++#if 0 /* TODO: Convert these to /sys */ ++void rtw_proc_init_one(struct net_device *dev) ++{ ++ struct proc_dir_entry *dir_dev = NULL; ++ struct proc_dir_entry *entry = NULL; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ u8 rf_type; ++ ++ if (rtw_proc == NULL) { ++ memcpy(rtw_proc_name, DRV_NAME, sizeof(DRV_NAME)); ++ ++ rtw_proc = create_proc_entry(rtw_proc_name, S_IFDIR, init_net.proc_net); ++ if (rtw_proc == NULL) { ++ DBG_88E(KERN_ERR "Unable to create rtw_proc directory\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("ver_info", S_IFREG | S_IRUGO, rtw_proc, proc_get_drv_version, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ } ++ ++ if (padapter->dir_dev == NULL) { ++ padapter->dir_dev = create_proc_entry(dev->name, ++ S_IFDIR | S_IRUGO | S_IXUGO, ++ rtw_proc); ++ dir_dev = padapter->dir_dev; ++ if (dir_dev == NULL) { ++ if (rtw_proc_cnt == 0) { ++ if (rtw_proc) { ++ remove_proc_entry(rtw_proc_name, init_net.proc_net); ++ rtw_proc = NULL; ++ } ++ } ++ ++ pr_info("Unable to create dir_dev directory\n"); ++ return; ++ } ++ } else { ++ return; ++ } ++ ++ rtw_proc_cnt++; ++ ++ entry = create_proc_read_entry("write_reg", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_write_reg, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_write_reg; ++ ++ entry = create_proc_read_entry("read_reg", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_read_reg, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_read_reg; ++ ++ entry = create_proc_read_entry("fwstate", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_fwstate, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("sec_info", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_sec_info, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("mlmext_state", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_mlmext_state, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("qos_option", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_qos_option, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("ht_option", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_ht_option, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("rf_info", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rf_info, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("ap_info", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_ap_info, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("adapter_state", S_IFREG | S_IRUGO, ++ dir_dev, proc_getstruct adapter_state, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("trx_info", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_trx_info, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("mac_reg_dump1", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_mac_reg_dump1, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("mac_reg_dump2", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_mac_reg_dump2, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("mac_reg_dump3", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_mac_reg_dump3, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("bb_reg_dump1", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_bb_reg_dump1, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("bb_reg_dump2", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_bb_reg_dump2, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("bb_reg_dump3", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_bb_reg_dump3, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("rf_reg_dump1", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rf_reg_dump1, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("rf_reg_dump2", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rf_reg_dump2, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type)); ++ if ((RF_1T2R == rf_type) || (RF_1T1R == rf_type)) { ++ entry = create_proc_read_entry("rf_reg_dump3", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rf_reg_dump3, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("rf_reg_dump4", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rf_reg_dump4, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ } ++ ++#ifdef CONFIG_88EU_AP_MODE ++ ++ entry = create_proc_read_entry("all_sta_info", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_all_sta_info, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++#endif ++ ++ entry = create_proc_read_entry("best_channel", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_best_channel, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ ++ entry = create_proc_read_entry("rx_signal", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rx_signal, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_rx_signal; ++ entry = create_proc_read_entry("ht_enable", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_ht_enable, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_ht_enable; ++ ++ entry = create_proc_read_entry("cbw40_enable", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_cbw40_enable, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_cbw40_enable; ++ ++ entry = create_proc_read_entry("ampdu_enable", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_ampdu_enable, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_ampdu_enable; ++ ++ entry = create_proc_read_entry("rx_stbc", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rx_stbc, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_rx_stbc; ++ ++ entry = create_proc_read_entry("path_rssi", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_two_path_rssi, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry = create_proc_read_entry("rssi_disp", S_IFREG | S_IRUGO, ++ dir_dev, proc_get_rssi_disp, dev); ++ if (!entry) { ++ pr_info("Unable to create_proc_read_entry!\n"); ++ return; ++ } ++ entry->write_proc = proc_set_rssi_disp; ++} ++ ++void rtw_proc_remove_one(struct net_device *dev) ++{ ++ struct proc_dir_entry *dir_dev = NULL; ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ u8 rf_type; ++ ++ dir_dev = padapter->dir_dev; ++ padapter->dir_dev = NULL; ++ ++ if (dir_dev) { ++ remove_proc_entry("write_reg", dir_dev); ++ remove_proc_entry("read_reg", dir_dev); ++ remove_proc_entry("fwstate", dir_dev); ++ remove_proc_entry("sec_info", dir_dev); ++ remove_proc_entry("mlmext_state", dir_dev); ++ remove_proc_entry("qos_option", dir_dev); ++ remove_proc_entry("ht_option", dir_dev); ++ remove_proc_entry("rf_info", dir_dev); ++ remove_proc_entry("ap_info", dir_dev); ++ remove_proc_entry("adapter_state", dir_dev); ++ remove_proc_entry("trx_info", dir_dev); ++ remove_proc_entry("mac_reg_dump1", dir_dev); ++ remove_proc_entry("mac_reg_dump2", dir_dev); ++ remove_proc_entry("mac_reg_dump3", dir_dev); ++ remove_proc_entry("bb_reg_dump1", dir_dev); ++ remove_proc_entry("bb_reg_dump2", dir_dev); ++ remove_proc_entry("bb_reg_dump3", dir_dev); ++ remove_proc_entry("rf_reg_dump1", dir_dev); ++ remove_proc_entry("rf_reg_dump2", dir_dev); ++ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type)); ++ if ((RF_1T2R == rf_type) || (RF_1T1R == rf_type)) { ++ remove_proc_entry("rf_reg_dump3", dir_dev); ++ remove_proc_entry("rf_reg_dump4", dir_dev); ++ } ++#ifdef CONFIG_88EU_AP_MODE ++ remove_proc_entry("all_sta_info", dir_dev); ++#endif ++ ++ remove_proc_entry("best_channel", dir_dev); ++ remove_proc_entry("rx_signal", dir_dev); ++ remove_proc_entry("cbw40_enable", dir_dev); ++ remove_proc_entry("ht_enable", dir_dev); ++ remove_proc_entry("ampdu_enable", dir_dev); ++ remove_proc_entry("rx_stbc", dir_dev); ++ remove_proc_entry("path_rssi", dir_dev); ++ remove_proc_entry("rssi_disp", dir_dev); ++ remove_proc_entry(dev->name, rtw_proc); ++ dir_dev = NULL; ++ } else { ++ return; ++ } ++ rtw_proc_cnt--; ++ ++ if (rtw_proc_cnt == 0) { ++ if (rtw_proc) { ++ remove_proc_entry("ver_info", rtw_proc); ++ ++ remove_proc_entry(rtw_proc_name, init_net.proc_net); ++ rtw_proc = NULL; ++ } ++ } ++} ++#endif ++ ++static uint loadparam(struct adapter *padapter, struct net_device *pnetdev) ++{ ++ uint status = _SUCCESS; ++ struct registry_priv *registry_par = &padapter->registrypriv; ++ ++ GlobalDebugLevel = rtw_debug; ++ registry_par->chip_version = (u8)rtw_chip_version; ++ registry_par->rfintfs = (u8)rtw_rfintfs; ++ registry_par->lbkmode = (u8)rtw_lbkmode; ++ registry_par->network_mode = (u8)rtw_network_mode; ++ ++ memcpy(registry_par->ssid.Ssid, "ANY", 3); ++ registry_par->ssid.SsidLength = 3; ++ ++ registry_par->channel = (u8)rtw_channel; ++ registry_par->wireless_mode = (u8)rtw_wireless_mode; ++ registry_par->vrtl_carrier_sense = (u8)rtw_vrtl_carrier_sense ; ++ registry_par->vcs_type = (u8)rtw_vcs_type; ++ registry_par->rts_thresh = (u16)rtw_rts_thresh; ++ registry_par->frag_thresh = (u16)rtw_frag_thresh; ++ registry_par->preamble = (u8)rtw_preamble; ++ registry_par->scan_mode = (u8)rtw_scan_mode; ++ registry_par->adhoc_tx_pwr = (u8)rtw_adhoc_tx_pwr; ++ registry_par->soft_ap = (u8)rtw_soft_ap; ++ registry_par->smart_ps = (u8)rtw_smart_ps; ++ registry_par->power_mgnt = (u8)rtw_power_mgnt; ++ registry_par->ips_mode = (u8)rtw_ips_mode; ++ registry_par->radio_enable = (u8)rtw_radio_enable; ++ registry_par->long_retry_lmt = (u8)rtw_long_retry_lmt; ++ registry_par->short_retry_lmt = (u8)rtw_short_retry_lmt; ++ registry_par->busy_thresh = (u16)rtw_busy_thresh; ++ registry_par->ack_policy = (u8)rtw_ack_policy; ++ registry_par->mp_mode = (u8)rtw_mp_mode; ++ registry_par->software_encrypt = (u8)rtw_software_encrypt; ++ registry_par->software_decrypt = (u8)rtw_software_decrypt; ++ registry_par->acm_method = (u8)rtw_acm_method; ++ ++ /* UAPSD */ ++ registry_par->wmm_enable = (u8)rtw_wmm_enable; ++ registry_par->uapsd_enable = (u8)rtw_uapsd_enable; ++ registry_par->uapsd_max_sp = (u8)rtw_uapsd_max_sp; ++ registry_par->uapsd_acbk_en = (u8)rtw_uapsd_acbk_en; ++ registry_par->uapsd_acbe_en = (u8)rtw_uapsd_acbe_en; ++ registry_par->uapsd_acvi_en = (u8)rtw_uapsd_acvi_en; ++ registry_par->uapsd_acvo_en = (u8)rtw_uapsd_acvo_en; ++ ++ registry_par->led_enable = (u8)rtw_led_enable; ++ ++ registry_par->ht_enable = (u8)rtw_ht_enable; ++ registry_par->cbw40_enable = (u8)rtw_cbw40_enable; ++ registry_par->ampdu_enable = (u8)rtw_ampdu_enable; ++ registry_par->rx_stbc = (u8)rtw_rx_stbc; ++ registry_par->ampdu_amsdu = (u8)rtw_ampdu_amsdu; ++ registry_par->lowrate_two_xmit = (u8)rtw_lowrate_two_xmit; ++ registry_par->rf_config = (u8)rtw_rf_config; ++ registry_par->low_power = (u8)rtw_low_power; ++ registry_par->wifi_spec = (u8)rtw_wifi_spec; ++ registry_par->channel_plan = (u8)rtw_channel_plan; ++ registry_par->bAcceptAddbaReq = (u8)rtw_AcceptAddbaReq; ++ registry_par->antdiv_cfg = (u8)rtw_antdiv_cfg; ++ registry_par->antdiv_type = (u8)rtw_antdiv_type; ++ registry_par->hwpdn_mode = (u8)rtw_hwpdn_mode;/* 0:disable, 1:enable, 2:by EFUSE config */ ++ registry_par->hwpwrp_detect = (u8)rtw_hwpwrp_detect;/* 0:disable, 1:enable */ ++ registry_par->hw_wps_pbc = (u8)rtw_hw_wps_pbc; ++ ++ registry_par->max_roaming_times = (u8)rtw_max_roaming_times; ++ ++ registry_par->fw_iol = rtw_fw_iol; ++ ++ registry_par->enable80211d = (u8)rtw_80211d; ++ snprintf(registry_par->ifname, 16, "%s", ifname); ++ snprintf(registry_par->if2name, 16, "%s", if2name); ++ registry_par->notch_filter = (u8)rtw_notch_filter; ++ ++ return status; ++} ++ ++static int rtw_net_set_mac_address(struct net_device *pnetdev, void *p) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev); ++ struct sockaddr *addr = p; ++ ++ if (!padapter->bup) ++ memcpy(padapter->eeprompriv.mac_addr, addr->sa_data, ETH_ALEN); ++ ++ return 0; ++} ++ ++static struct net_device_stats *rtw_net_get_stats(struct net_device *pnetdev) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev); ++ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv); ++ struct recv_priv *precvpriv = &(padapter->recvpriv); ++ ++ padapter->stats.tx_packets = pxmitpriv->tx_pkts;/* pxmitpriv->tx_pkts++; */ ++ padapter->stats.rx_packets = precvpriv->rx_pkts;/* precvpriv->rx_pkts++; */ ++ padapter->stats.tx_dropped = pxmitpriv->tx_drop; ++ padapter->stats.rx_dropped = precvpriv->rx_drop; ++ padapter->stats.tx_bytes = pxmitpriv->tx_bytes; ++ padapter->stats.rx_bytes = precvpriv->rx_bytes; ++ return &padapter->stats; ++} ++ ++/* ++ * AC to queue mapping ++ * ++ * AC_VO -> queue 0 ++ * AC_VI -> queue 1 ++ * AC_BE -> queue 2 ++ * AC_BK -> queue 3 ++ */ ++static const u16 rtw_1d_to_queue[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; ++ ++/* Given a data frame determine the 802.1p/1d tag to use. */ ++static unsigned int rtw_classify8021d(struct sk_buff *skb) ++{ ++ unsigned int dscp; ++ ++ /* skb->priority values from 256->263 are magic values to ++ * directly indicate a specific 802.1d priority. This is used ++ * to allow 802.1d priority to be passed directly in from VLAN ++ * tags, etc. ++ */ ++ if (skb->priority >= 256 && skb->priority <= 263) ++ return skb->priority - 256; ++ ++ switch (skb->protocol) { ++ case htons(ETH_P_IP): ++ dscp = ip_hdr(skb)->tos & 0xfc; ++ break; ++ default: ++ return 0; ++ } ++ ++ return dscp >> 5; ++} ++ ++static u16 rtw_select_queue(struct net_device *dev, struct sk_buff *skb ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)) ++ ,struct net_device *sb_dev ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) ++ ,struct net_device *sb_dev ++ ,select_queue_fallback_t fallback ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) ++ ,void *unused ++ ,select_queue_fallback_t fallback ++#elif (LINUX_VERSION_CODE == KERNEL_VERSION(3, 13, 0)) ++ , void *accel ++#endif ++) ++{ ++ struct adapter *padapter = rtw_netdev_priv(dev); ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ ++ skb->priority = rtw_classify8021d(skb); ++ ++ if (pmlmepriv->acm_mask != 0) ++ skb->priority = qos_acm(pmlmepriv->acm_mask, skb->priority); ++ ++ return rtw_1d_to_queue[skb->priority]; ++} ++ ++u16 rtw_recv_select_queue(struct sk_buff *skb) ++{ ++ struct iphdr *piphdr; ++ unsigned int dscp; ++ __be16 eth_type; ++ u32 priority; ++ u8 *pdata = skb->data; ++ ++ memcpy(ð_type, pdata+(ETH_ALEN<<1), 2); ++ ++ switch (eth_type) { ++ case htons(ETH_P_IP): ++ piphdr = (struct iphdr *)(pdata+ETH_HLEN); ++ dscp = piphdr->tos & 0xfc; ++ priority = dscp >> 5; ++ break; ++ default: ++ priority = 0; ++ } ++ ++ return rtw_1d_to_queue[priority]; ++} ++ ++static const struct net_device_ops rtw_netdev_ops = { ++ .ndo_open = netdev_open, ++ .ndo_stop = netdev_close, ++ .ndo_start_xmit = rtw_xmit_entry, ++ .ndo_select_queue = rtw_select_queue, ++ .ndo_set_mac_address = rtw_net_set_mac_address, ++ .ndo_get_stats = rtw_net_get_stats, ++ .ndo_do_ioctl = rtw_ioctl, ++}; ++ ++int rtw_init_netdev_name(struct net_device *pnetdev, const char *ifname) ++{ ++ if (dev_alloc_name(pnetdev, ifname) < 0) ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("dev_alloc_name, fail!\n")); ++ ++ netif_carrier_off(pnetdev); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,39) ++static const struct device_type wlan_type = { ++ .name = "wlan", ++}; ++#else ++static struct device_type wlan_type = { ++ .name = "wlan", ++}; ++#endif ++ ++struct net_device *rtw_init_netdev(struct adapter *old_padapter) ++{ ++ struct adapter *padapter; ++ struct net_device *pnetdev; ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+init_net_dev\n")); ++ ++ if (old_padapter != NULL) ++ pnetdev = rtw_alloc_etherdev_with_old_priv(sizeof(struct adapter), (void *)old_padapter); ++ else ++ pnetdev = rtw_alloc_etherdev(sizeof(struct adapter)); ++ ++ if (!pnetdev) ++ return NULL; ++ ++ pnetdev->dev.type = &wlan_type; ++ padapter = rtw_netdev_priv(pnetdev); ++ padapter->pnetdev = pnetdev; ++ DBG_88E("register rtw_netdev_ops to netdev_ops\n"); ++ pnetdev->netdev_ops = &rtw_netdev_ops; ++ pnetdev->watchdog_timeo = HZ*3; /* 3 second timeout */ ++ pnetdev->wireless_handlers = (struct iw_handler_def *)&rtw_handlers_def; ++ ++ /* step 2. */ ++ loadparam(padapter, pnetdev); ++ ++ return pnetdev; ++} ++ ++u32 rtw_start_drv_threads(struct adapter *padapter) ++{ ++ u32 _status = _SUCCESS; ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_start_drv_threads\n")); ++ ++ padapter->cmdThread = kthread_run(rtw_cmd_thread, padapter, "RTW_CMD_THREAD"); ++ if (IS_ERR(padapter->cmdThread)) ++ _status = _FAIL; ++ else ++ _rtw_down_sema(&padapter->cmdpriv.terminate_cmdthread_sema); /* wait for cmd_thread to run */ ++ ++ rtw_hal_start_thread(padapter); ++ return _status; ++} ++ ++void rtw_stop_drv_threads(struct adapter *padapter) ++{ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_stop_drv_threads\n")); ++ ++ /* Below is to termindate rtw_cmd_thread & event_thread... */ ++ up(&padapter->cmdpriv.cmd_queue_sema); ++ if (padapter->cmdThread) ++ _rtw_down_sema(&padapter->cmdpriv.terminate_cmdthread_sema); ++ ++ rtw_hal_stop_thread(padapter); ++} ++ ++static u8 rtw_init_default_value(struct adapter *padapter) ++{ ++ u8 ret = _SUCCESS; ++ struct registry_priv *pregistrypriv = &padapter->registrypriv; ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct security_priv *psecuritypriv = &padapter->securitypriv; ++ ++ /* xmit_priv */ ++ pxmitpriv->vcs_setting = pregistrypriv->vrtl_carrier_sense; ++ pxmitpriv->vcs = pregistrypriv->vcs_type; ++ pxmitpriv->vcs_type = pregistrypriv->vcs_type; ++ pxmitpriv->frag_len = pregistrypriv->frag_thresh; ++ ++ /* mlme_priv */ ++ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */ ++ pmlmepriv->scan_mode = SCAN_ACTIVE; ++ ++ /* ht_priv */ ++ pmlmepriv->htpriv.ampdu_enable = false;/* set to disabled */ ++ ++ /* security_priv */ ++ psecuritypriv->binstallGrpkey = _FAIL; ++ psecuritypriv->sw_encrypt = pregistrypriv->software_encrypt; ++ psecuritypriv->sw_decrypt = pregistrypriv->software_decrypt; ++ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ ++ psecuritypriv->dot11PrivacyAlgrthm = _NO_PRIVACY_; ++ psecuritypriv->dot11PrivacyKeyIndex = 0; ++ psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_; ++ psecuritypriv->dot118021XGrpKeyid = 1; ++ psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen; ++ psecuritypriv->ndisencryptstatus = Ndis802_11WEPDisabled; ++ ++ /* registry_priv */ ++ rtw_init_registrypriv_dev_network(padapter); ++ rtw_update_registrypriv_dev_network(padapter); ++ ++ /* hal_priv */ ++ rtw_hal_def_value_init(padapter); ++ ++ /* misc. */ ++ padapter->bReadPortCancel = false; ++ padapter->bWritePortCancel = false; ++ padapter->bRxRSSIDisplay = 0; ++ padapter->bNotifyChannelChange = 0; ++#ifdef CONFIG_88EU_P2P ++ padapter->bShowGetP2PState = 1; ++#endif ++ return ret; ++} ++ ++u8 rtw_reset_drv_sw(struct adapter *padapter) ++{ ++ u8 ret8 = _SUCCESS; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv; ++ ++ /* hal_priv */ ++ rtw_hal_def_value_init(padapter); ++ padapter->bReadPortCancel = false; ++ padapter->bWritePortCancel = false; ++ padapter->bRxRSSIDisplay = 0; ++ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */ ++ ++ padapter->xmitpriv.tx_pkts = 0; ++ padapter->recvpriv.rx_pkts = 0; ++ ++ pmlmepriv->LinkDetectInfo.bBusyTraffic = false; ++ ++ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING); ++ ++ rtw_hal_sreset_reset_value(padapter); ++ pwrctrlpriv->pwr_state_check_cnts = 0; ++ ++ /* mlmeextpriv */ ++ padapter->mlmeextpriv.sitesurvey_res.state = SCAN_DISABLE; ++ ++ rtw_set_signal_stat_timer(&padapter->recvpriv); ++ ++ return ret8; ++} ++ ++u8 rtw_init_drv_sw(struct adapter *padapter) ++{ ++ u8 ret8 = _SUCCESS; ++ unsigned long flags; ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_init_drv_sw\n")); ++ ++ if ((rtw_init_cmd_priv(&padapter->cmdpriv)) == _FAIL) { ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("\n Can't init cmd_priv\n")); ++ ret8 = _FAIL; ++ goto exit; ++ } ++ ++ padapter->cmdpriv.padapter = padapter; ++ ++ if ((rtw_init_evt_priv(&padapter->evtpriv)) == _FAIL) { ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("\n Can't init evt_priv\n")); ++ ret8 = _FAIL; ++ goto exit; ++ } ++ ++ if (rtw_init_mlme_priv(padapter) == _FAIL) { ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("\n Can't init mlme_priv\n")); ++ ret8 = _FAIL; ++ goto exit; ++ } ++ ++#ifdef CONFIG_88EU_P2P ++ rtw_init_wifidirect_timers(padapter); ++ init_wifidirect_info(padapter, P2P_ROLE_DISABLE); ++ reset_global_wifidirect_info(padapter); ++#endif /* CONFIG_88EU_P2P */ ++ ++ if (init_mlme_ext_priv(padapter) == _FAIL) { ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("\n Can't init mlme_ext_priv\n")); ++ ret8 = _FAIL; ++ goto exit; ++ } ++ ++ if (_rtw_init_xmit_priv(&padapter->xmitpriv, padapter) == _FAIL) { ++ DBG_88E("Can't _rtw_init_xmit_priv\n"); ++ ret8 = _FAIL; ++ goto exit; ++ } ++ ++ if (_rtw_init_recv_priv(&padapter->recvpriv, padapter) == _FAIL) { ++ DBG_88E("Can't _rtw_init_recv_priv\n"); ++ ret8 = _FAIL; ++ goto exit; ++ } ++ ++ if (_rtw_init_sta_priv(&padapter->stapriv) == _FAIL) { ++ DBG_88E("Can't _rtw_init_sta_priv\n"); ++ ret8 = _FAIL; ++ goto exit; ++ } ++ ++ padapter->stapriv.padapter = padapter; ++ ++ rtw_init_bcmc_stainfo(padapter); ++ ++ rtw_init_pwrctrl_priv(padapter); ++ ++ if (init_mp_priv(padapter) == _FAIL) ++ DBG_88E("%s: initialize MP private data Fail!\n", __func__); ++ ++ ret8 = rtw_init_default_value(padapter); ++ ++ rtw_hal_dm_init(padapter); ++ rtw_hal_sw_led_init(padapter); ++ ++ rtw_hal_sreset_init(padapter); ++ ++ spin_lock_init(&padapter->br_ext_lock); ++ ++exit: ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-rtw_init_drv_sw\n")); ++ ++ ++ ++ return ret8; ++} ++ ++void rtw_cancel_all_timer(struct adapter *padapter) ++{ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_cancel_all_timer\n")); ++ ++ _cancel_timer_ex(&padapter->mlmepriv.assoc_timer); ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel association timer complete!\n")); ++ ++ _cancel_timer_ex(&padapter->mlmepriv.scan_to_timer); ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel scan_to_timer!\n")); ++ ++ _cancel_timer_ex(&padapter->mlmepriv.dynamic_chk_timer); ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel dynamic_chk_timer!\n")); ++ ++ /* cancel sw led timer */ ++ rtw_hal_sw_led_deinit(padapter); ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel DeInitSwLeds!\n")); ++ ++ _cancel_timer_ex(&padapter->pwrctrlpriv.pwr_state_check_timer); ++ ++ _cancel_timer_ex(&padapter->recvpriv.signal_stat_timer); ++ /* cancel dm timer */ ++ rtw_hal_dm_deinit(padapter); ++} ++ ++u8 rtw_free_drv_sw(struct adapter *padapter) ++{ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("==>rtw_free_drv_sw")); ++ ++ /* we can call rtw_p2p_enable here, but: */ ++ /* 1. rtw_p2p_enable may have IO operation */ ++ /* 2. rtw_p2p_enable is bundled with wext interface */ ++ #ifdef CONFIG_88EU_P2P ++ { ++ struct wifidirect_info *pwdinfo = &padapter->wdinfo; ++ if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) { ++ _cancel_timer_ex(&pwdinfo->find_phase_timer); ++ _cancel_timer_ex(&pwdinfo->restore_p2p_state_timer); ++ _cancel_timer_ex(&pwdinfo->pre_tx_scan_timer); ++ rtw_p2p_set_state(pwdinfo, P2P_STATE_NONE); ++ } ++ } ++ #endif ++ ++ _rtw_spinlock_free(&padapter->br_ext_lock); ++ ++ free_mlme_ext_priv(&padapter->mlmeextpriv); ++ ++ rtw_free_cmd_priv(&padapter->cmdpriv); ++ ++ rtw_free_evt_priv(&padapter->evtpriv); ++ ++ rtw_free_mlme_priv(&padapter->mlmepriv); ++ _rtw_free_xmit_priv(&padapter->xmitpriv); ++ ++ _rtw_free_sta_priv(&padapter->stapriv); /* will free bcmc_stainfo here */ ++ ++ _rtw_free_recv_priv(&padapter->recvpriv); ++ ++ rtw_free_pwrctrl_priv(padapter); ++ ++ rtw_hal_free_data(padapter); ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("<== rtw_free_drv_sw\n")); ++ ++ /* free the old_pnetdev */ ++ if (padapter->rereg_nd_name_priv.old_pnetdev) { ++ free_netdev(padapter->rereg_nd_name_priv.old_pnetdev); ++ padapter->rereg_nd_name_priv.old_pnetdev = NULL; ++ } ++ ++ /* clear pbuddystruct adapter to avoid access wrong pointer. */ ++ if (padapter->pbuddy_adapter != NULL) ++ padapter->pbuddy_adapter->pbuddy_adapter = NULL; ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-rtw_free_drv_sw\n")); ++ ++ return _SUCCESS; ++} ++ ++void netdev_br_init(struct net_device *netdev) ++{ ++ struct adapter *adapter = (struct adapter *)rtw_netdev_priv(netdev); ++ ++ rcu_read_lock(); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++ if (rcu_dereference(adapter->pnetdev->rx_handler_data)) { ++#else ++ if (rcu_dereference(adapter->pnetdev->br_port)) { ++#endif ++ struct net_device *br_netdev; ++ struct net *devnet = NULL; ++ ++ devnet = dev_net(netdev); ++ br_netdev = dev_get_by_name(devnet, CONFIG_BR_EXT_BRNAME); ++ if (br_netdev) { ++ memcpy(adapter->br_mac, br_netdev->dev_addr, ETH_ALEN); ++ dev_put(br_netdev); ++ } else { ++ pr_info("%s()-%d: dev_get_by_name(%s) failed!", ++ __func__, __LINE__, CONFIG_BR_EXT_BRNAME); ++ } ++ } ++ adapter->ethBrExtInfo.addPPPoETag = 1; ++ ++ rcu_read_unlock(); ++} ++ ++int _netdev_open(struct net_device *pnetdev) ++{ ++ uint status; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev); ++ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv; ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+88eu_drv - dev_open\n")); ++ DBG_88E("+88eu_drv - drv_open, bup =%d\n", padapter->bup); ++ ++ if (pwrctrlpriv->ps_flag) { ++ padapter->net_closed = false; ++ goto netdev_open_normal_process; ++ } ++ ++ if (!padapter->bup) { ++ padapter->bDriverStopped = false; ++ padapter->bSurpriseRemoved = false; ++ padapter->bCardDisableWOHSM = false; ++ ++ status = rtw_hal_init(padapter); ++ if (status == _FAIL) { ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("rtl88eu_hal_init(): Can't init h/w!\n")); ++ goto netdev_open_error; ++ } ++ ++ pr_info("MAC Address = %pM\n", pnetdev->dev_addr); ++ ++ status = rtw_start_drv_threads(padapter); ++ if (status == _FAIL) { ++ pr_info("Initialize driver software resource Failed!\n"); ++ goto netdev_open_error; ++ } ++ ++ if (init_hw_mlme_ext(padapter) == _FAIL) { ++ pr_info("can't init mlme_ext_priv\n"); ++ goto netdev_open_error; ++ } ++ if (padapter->intf_start) ++ padapter->intf_start(padapter); ++ rtw_proc_init_one(pnetdev); ++ ++ rtw_led_control(padapter, LED_CTL_NO_LINK); ++ ++ padapter->bup = true; ++ } ++ padapter->net_closed = false; ++ ++ _set_timer(&padapter->mlmepriv.dynamic_chk_timer, 2000); ++ ++ padapter->pwrctrlpriv.bips_processing = false; ++ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv); ++ ++ if (!rtw_netif_queue_stopped(pnetdev)) ++ rtw_netif_start_queue(pnetdev); ++ else ++ rtw_netif_wake_queue(pnetdev); ++ ++ netdev_br_init(pnetdev); ++ ++netdev_open_normal_process: ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-88eu_drv - dev_open\n")); ++ DBG_88E("-88eu_drv - drv_open, bup =%d\n", padapter->bup); ++ return 0; ++ ++netdev_open_error: ++ padapter->bup = false; ++ netif_carrier_off(pnetdev); ++ rtw_netif_stop_queue(pnetdev); ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("-88eu_drv - dev_open, fail!\n")); ++ DBG_88E("-88eu_drv - drv_open fail, bup =%d\n", padapter->bup); ++ return -1; ++} ++ ++int netdev_open(struct net_device *pnetdev) ++{ ++ int ret; ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev); ++ ++ _enter_critical_mutex(padapter->hw_init_mutex, NULL); ++ ret = _netdev_open(pnetdev); ++ _exit_critical_mutex(padapter->hw_init_mutex, NULL); ++ return ret; ++} ++ ++static int ips_netdrv_open(struct adapter *padapter) ++{ ++ int status = _SUCCESS; ++ padapter->net_closed = false; ++ DBG_88E("===> %s.........\n", __func__); ++ ++ padapter->bDriverStopped = false; ++ padapter->bSurpriseRemoved = false; ++ padapter->bCardDisableWOHSM = false; ++ ++ status = rtw_hal_init(padapter); ++ if (status == _FAIL) { ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("ips_netdrv_open(): Can't init h/w!\n")); ++ goto netdev_open_error; ++ } ++ ++ if (padapter->intf_start) ++ padapter->intf_start(padapter); ++ ++ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv); ++ _set_timer(&padapter->mlmepriv.dynamic_chk_timer, 5000); ++ ++ return _SUCCESS; ++ ++netdev_open_error: ++ DBG_88E("-ips_netdrv_open - drv_open failure, bup =%d\n", padapter->bup); ++ ++ return _FAIL; ++} ++ ++int rtw_ips_pwr_up(struct adapter *padapter) ++{ ++ int result; ++ u32 start_time = jiffies; ++ DBG_88E("===> rtw_ips_pwr_up..............\n"); ++ rtw_reset_drv_sw(padapter); ++ ++ result = ips_netdrv_open(padapter); ++ ++ rtw_led_control(padapter, LED_CTL_NO_LINK); ++ ++ DBG_88E("<=== rtw_ips_pwr_up.............. in %dms\n", rtw_get_passing_time_ms(start_time)); ++ return result; ++} ++ ++void rtw_ips_pwr_down(struct adapter *padapter) ++{ ++ u32 start_time = jiffies; ++ DBG_88E("===> rtw_ips_pwr_down...................\n"); ++ ++ padapter->bCardDisableWOHSM = true; ++ padapter->net_closed = true; ++ ++ rtw_led_control(padapter, LED_CTL_POWER_OFF); ++ ++ rtw_ips_dev_unload(padapter); ++ padapter->bCardDisableWOHSM = false; ++ DBG_88E("<=== rtw_ips_pwr_down..................... in %dms\n", rtw_get_passing_time_ms(start_time)); ++} ++ ++void rtw_ips_dev_unload(struct adapter *padapter) ++{ ++ DBG_88E("====> %s...\n", __func__); ++ ++ rtw_hal_set_hwreg(padapter, HW_VAR_FIFO_CLEARN_UP, NULL); ++ ++ if (padapter->intf_stop) ++ padapter->intf_stop(padapter); ++ ++ /* s5. */ ++ if (!padapter->bSurpriseRemoved) ++ rtw_hal_deinit(padapter); ++} ++ ++int pm_netdev_open(struct net_device *pnetdev, u8 bnormal) ++{ ++ int status; ++ ++ if (bnormal) ++ status = netdev_open(pnetdev); ++ else ++ status = (_SUCCESS == ips_netdrv_open((struct adapter *)rtw_netdev_priv(pnetdev))) ? (0) : (-1); ++ return status; ++} ++ ++int netdev_close(struct net_device *pnetdev) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev); ++ struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+88eu_drv - drv_close\n")); ++ ++ if (padapter->pwrctrlpriv.bInternalAutoSuspend) { ++ if (padapter->pwrctrlpriv.rf_pwrstate == rf_off) ++ padapter->pwrctrlpriv.ps_flag = true; ++ } ++ padapter->net_closed = true; ++ ++ if (padapter->pwrctrlpriv.rf_pwrstate == rf_on) { ++ DBG_88E("(2)88eu_drv - drv_close, bup =%d, hw_init_completed =%d\n", ++ padapter->bup, padapter->hw_init_completed); ++ ++ /* s1. */ ++ if (pnetdev) { ++ if (!rtw_netif_queue_stopped(pnetdev)) ++ rtw_netif_stop_queue(pnetdev); ++ } ++ ++ /* s2. */ ++ LeaveAllPowerSaveMode(padapter); ++ rtw_disassoc_cmd(padapter, 500, false); ++ /* s2-2. indicate disconnect to os */ ++ rtw_indicate_disconnect(padapter); ++ /* s2-3. */ ++ rtw_free_assoc_resources(padapter, 1); ++ /* s2-4. */ ++ rtw_free_network_queue(padapter, true); ++ /* Close LED */ ++ rtw_led_control(padapter, LED_CTL_POWER_OFF); ++ } ++ ++ nat25_db_cleanup(padapter); ++ ++#ifdef CONFIG_88EU_P2P ++ rtw_p2p_enable(padapter, P2P_ROLE_DISABLE); ++#endif /* CONFIG_88EU_P2P */ ++ ++ kfree(dvobj->firmware.szFwBuffer); ++ dvobj->firmware.szFwBuffer = NULL; ++ ++ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-88eu_drv - drv_close\n")); ++ DBG_88E("-88eu_drv - drv_close, bup =%d\n", padapter->bup); ++ return 0; ++} +diff --git a/drivers/staging/r8188eu/os_dep/osdep_service.c b/drivers/staging/r8188eu/os_dep/osdep_service.c +new file mode 100644 +index 000000000000..541af56515ac +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/osdep_service.c +@@ -0,0 +1,535 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++ ++#define _OSDEP_SERVICE_C_ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++* Translate the OS dependent @param error_code to OS independent RTW_STATUS_CODE ++* @return: one of RTW_STATUS_CODE ++*/ ++inline int RTW_STATUS_CODE(int error_code) ++{ ++ if (error_code >= 0) ++ return _SUCCESS; ++ return _FAIL; ++} ++ ++u32 rtw_atoi(u8 *s) ++{ ++ int num = 0, flag = 0; ++ int i; ++ for (i = 0; i <= strlen(s); i++) { ++ if (s[i] >= '0' && s[i] <= '9') ++ num = num * 10 + s[i] - '0'; ++ else if (s[0] == '-' && i == 0) ++ flag = 1; ++ else ++ break; ++ } ++ if (flag == 1) ++ num = num * -1; ++ return num; ++} ++ ++inline u8 *_rtw_vmalloc(u32 sz) ++{ ++ u8 *pbuf; ++ pbuf = vmalloc(sz); ++ return pbuf; ++} ++ ++inline u8 *_rtw_zvmalloc(u32 sz) ++{ ++ u8 *pbuf; ++ pbuf = _rtw_vmalloc(sz); ++ if (pbuf != NULL) ++ memset(pbuf, 0, sz); ++ return pbuf; ++} ++ ++inline void _rtw_vmfree(u8 *pbuf, u32 sz) ++{ ++ vfree(pbuf); ++} ++ ++u8 *_rtw_malloc(u32 sz) ++{ ++ u8 *pbuf = NULL; ++ ++ pbuf = kmalloc(sz, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); ++ return pbuf; ++} ++ ++u8 *_rtw_zmalloc(u32 sz) ++{ ++ u8 *pbuf = _rtw_malloc(sz); ++ ++ if (pbuf != NULL) ++ memset(pbuf, 0, sz); ++ return pbuf; ++} ++ ++void *rtw_malloc2d(int h, int w, int size) ++{ ++ int j; ++ ++ void **a = (void **)rtw_zmalloc(h*sizeof(void *) + h*w*size); ++ if (a == NULL) { ++ pr_info("%s: alloc memory fail!\n", __func__); ++ return NULL; ++ } ++ ++ for (j = 0; j < h; j++) ++ a[j] = ((char *)(a+h)) + j*w*size; ++ ++ return a; ++} ++ ++void rtw_mfree2d(void *pbuf, int h, int w, int size) ++{ ++ kfree(pbuf); ++} ++ ++/* ++For the following list_xxx operations, ++caller must guarantee the atomic context. ++Otherwise, there will be racing condition. ++*/ ++/* ++Caller must check if the list is empty before calling rtw_list_delete ++*/ ++ ++u32 _rtw_down_sema(struct semaphore *sema) ++{ ++ if (down_interruptible(sema)) ++ return _FAIL; ++ else ++ return _SUCCESS; ++} ++ ++void _rtw_mutex_init(struct mutex *pmutex) ++{ ++ mutex_init(pmutex); ++} ++ ++void _rtw_mutex_free(struct mutex *pmutex) ++{ ++ mutex_destroy(pmutex); ++} ++ ++void _rtw_spinlock_free(spinlock_t *plock) ++{ ++} ++ ++void _rtw_init_queue(struct __queue *pqueue) ++{ ++ INIT_LIST_HEAD(&(pqueue->queue)); ++ spin_lock_init(&(pqueue->lock)); ++} ++ ++inline u32 rtw_systime_to_ms(u32 systime) ++{ ++ return systime * 1000 / HZ; ++} ++ ++inline u32 rtw_ms_to_systime(u32 ms) ++{ ++ return ms * HZ / 1000; ++} ++ ++/* the input parameter start use the same unit as jiffies */ ++inline s32 rtw_get_passing_time_ms(u32 start) ++{ ++ return rtw_systime_to_ms(jiffies-start); ++} ++ ++inline s32 rtw_get_time_interval_ms(u32 start, u32 end) ++{ ++ return rtw_systime_to_ms(end-start); ++} ++ ++void rtw_sleep_schedulable(int ms) ++{ ++ u32 delta; ++ ++ delta = (ms * HZ)/1000;/* ms) */ ++ if (delta == 0) ++ delta = 1;/* 1 ms */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (schedule_timeout(delta) != 0) ++ return; ++} ++ ++void rtw_msleep_os(int ms) ++{ ++ msleep((unsigned int)ms); ++} ++ ++void rtw_usleep_os(int us) ++{ ++ if (1 < (us/1000)) ++ msleep(1); ++ else ++ msleep((us/1000) + 1); ++} ++ ++void rtw_mdelay_os(int ms) ++{ ++ mdelay((unsigned long)ms); ++} ++ ++void rtw_udelay_os(int us) ++{ ++ udelay((unsigned long)us); ++} ++ ++void rtw_yield_os(void) ++{ ++ yield(); ++} ++ ++#define RTW_SUSPEND_LOCK_NAME "rtw_wifi" ++ ++inline void rtw_suspend_lock_init(void) ++{ ++} ++ ++inline void rtw_suspend_lock_uninit(void) ++{ ++} ++ ++inline void rtw_lock_suspend(void) ++{ ++} ++ ++inline void rtw_unlock_suspend(void) ++{ ++} ++ ++inline void ATOMIC_SET(ATOMIC_T *v, int i) ++{ ++ atomic_set(v, i); ++} ++ ++inline int ATOMIC_READ(ATOMIC_T *v) ++{ ++ return atomic_read(v); ++} ++ ++inline void ATOMIC_ADD(ATOMIC_T *v, int i) ++{ ++ atomic_add(i, v); ++} ++ ++inline void ATOMIC_SUB(ATOMIC_T *v, int i) ++{ ++ atomic_sub(i, v); ++} ++ ++inline void ATOMIC_INC(ATOMIC_T *v) ++{ ++ atomic_inc(v); ++} ++ ++inline void ATOMIC_DEC(ATOMIC_T *v) ++{ ++ atomic_dec(v); ++} ++ ++inline int ATOMIC_ADD_RETURN(ATOMIC_T *v, int i) ++{ ++ return atomic_add_return(i, v); ++} ++ ++inline int ATOMIC_SUB_RETURN(ATOMIC_T *v, int i) ++{ ++ return atomic_sub_return(i, v); ++} ++ ++inline int ATOMIC_INC_RETURN(ATOMIC_T *v) ++{ ++ return atomic_inc_return(v); ++} ++ ++inline int ATOMIC_DEC_RETURN(ATOMIC_T *v) ++{ ++ return atomic_dec_return(v); ++} ++ ++static const struct device_type wlan_type = { ++ .name = "wlan", ++}; ++ ++struct net_device *rtw_alloc_etherdev_with_old_priv(int sizeof_priv, ++ void *old_priv) ++{ ++ struct net_device *pnetdev; ++ struct rtw_netdev_priv_indicator *pnpi; ++ ++ pnetdev = alloc_etherdev_mq(sizeof(struct rtw_netdev_priv_indicator), 4); ++ if (!pnetdev) ++ goto RETURN; ++ ++ pnetdev->dev.type = &wlan_type; ++ pnpi = netdev_priv(pnetdev); ++ pnpi->priv = old_priv; ++ pnpi->sizeof_priv = sizeof_priv; ++ ++RETURN: ++ return pnetdev; ++} ++ ++struct net_device *rtw_alloc_etherdev(int sizeof_priv) ++{ ++ struct net_device *pnetdev; ++ struct rtw_netdev_priv_indicator *pnpi; ++ ++ pnetdev = alloc_etherdev_mq(sizeof(struct rtw_netdev_priv_indicator), 4); ++ if (!pnetdev) ++ goto RETURN; ++ ++ pnpi = netdev_priv(pnetdev); ++ ++ pnpi->priv = rtw_zvmalloc(sizeof_priv); ++ if (!pnpi->priv) { ++ free_netdev(pnetdev); ++ pnetdev = NULL; ++ goto RETURN; ++ } ++ ++ pnpi->sizeof_priv = sizeof_priv; ++RETURN: ++ return pnetdev; ++} ++ ++void rtw_free_netdev(struct net_device *netdev) ++{ ++ struct rtw_netdev_priv_indicator *pnpi; ++ ++ if (!netdev) ++ goto RETURN; ++ ++ pnpi = netdev_priv(netdev); ++ ++ if (!pnpi->priv) ++ goto RETURN; ++ ++ rtw_vmfree(pnpi->priv, pnpi->sizeof_priv); ++ free_netdev(netdev); ++ ++RETURN: ++ return; ++} ++ ++int rtw_change_ifname(struct adapter *padapter, const char *ifname) ++{ ++ struct net_device *pnetdev; ++ struct net_device *cur_pnetdev; ++ struct rereg_nd_name_data *rereg_priv; ++ int ret; ++ ++ if (!padapter) ++ goto error; ++ ++ cur_pnetdev = padapter->pnetdev; ++ rereg_priv = &padapter->rereg_nd_name_priv; ++ ++ /* free the old_pnetdev */ ++ if (rereg_priv->old_pnetdev) { ++ free_netdev(rereg_priv->old_pnetdev); ++ rereg_priv->old_pnetdev = NULL; ++ } ++ ++ if (!rtnl_is_locked()) ++ unregister_netdev(cur_pnetdev); ++ else ++ unregister_netdevice(cur_pnetdev); ++ ++ rtw_proc_remove_one(cur_pnetdev); ++ ++ rereg_priv->old_pnetdev = cur_pnetdev; ++ ++ pnetdev = rtw_init_netdev(padapter); ++ if (!pnetdev) { ++ ret = -1; ++ goto error; ++ } ++ ++ SET_NETDEV_DEV(pnetdev, dvobj_to_dev(adapter_to_dvobj(padapter))); ++ ++ rtw_init_netdev_name(pnetdev, ifname); ++ ++ memcpy(pnetdev->dev_addr, padapter->eeprompriv.mac_addr, ETH_ALEN); ++ ++ if (!rtnl_is_locked()) ++ ret = register_netdev(pnetdev); ++ else ++ ret = register_netdevice(pnetdev); ++ if (ret != 0) { ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ++ ("register_netdev() failed\n")); ++ goto error; ++ } ++ rtw_proc_init_one(pnetdev); ++ return 0; ++error: ++ return -1; ++} ++ ++u64 rtw_modular64(u64 x, u64 y) ++{ ++ return do_div(x, y); ++} ++ ++u64 rtw_division64(u64 x, u64 y) ++{ ++ do_div(x, y); ++ return x; ++} ++ ++void rtw_buf_free(u8 **buf, u32 *buf_len) ++{ ++ *buf_len = 0; ++ kfree(*buf); ++ *buf = NULL; ++} ++ ++void rtw_buf_update(u8 **buf, u32 *buf_len, u8 *src, u32 src_len) ++{ ++ u32 ori_len = 0, dup_len = 0; ++ u8 *ori = NULL; ++ u8 *dup = NULL; ++ ++ if (!buf || !buf_len) ++ return; ++ ++ if (!src || !src_len) ++ goto keep_ori; ++ ++ /* duplicate src */ ++ dup = rtw_malloc(src_len); ++ if (dup) { ++ dup_len = src_len; ++ memcpy(dup, src, dup_len); ++ } ++ ++keep_ori: ++ ori = *buf; ++ ori_len = *buf_len; ++ ++ /* replace buf with dup */ ++ *buf_len = 0; ++ *buf = dup; ++ *buf_len = dup_len; ++ ++ /* free ori */ ++ kfree(ori); ++} ++ ++/** ++ * rtw_cbuf_full - test if cbuf is full ++ * @cbuf: pointer of struct rtw_cbuf ++ * ++ * Returns: true if cbuf is full ++ */ ++inline bool rtw_cbuf_full(struct rtw_cbuf *cbuf) ++{ ++ return (cbuf->write == cbuf->read-1) ? true : false; ++} ++ ++/** ++ * rtw_cbuf_empty - test if cbuf is empty ++ * @cbuf: pointer of struct rtw_cbuf ++ * ++ * Returns: true if cbuf is empty ++ */ ++inline bool rtw_cbuf_empty(struct rtw_cbuf *cbuf) ++{ ++ return (cbuf->write == cbuf->read) ? true : false; ++} ++ ++/** ++ * rtw_cbuf_push - push a pointer into cbuf ++ * @cbuf: pointer of struct rtw_cbuf ++ * @buf: pointer to push in ++ * ++ * Lock free operation, be careful of the use scheme ++ * Returns: true push success ++ */ ++bool rtw_cbuf_push(struct rtw_cbuf *cbuf, void *buf) ++{ ++ if (rtw_cbuf_full(cbuf)) ++ return _FAIL; ++ ++ if (0) ++ DBG_88E("%s on %u\n", __func__, cbuf->write); ++ cbuf->bufs[cbuf->write] = buf; ++ cbuf->write = (cbuf->write+1)%cbuf->size; ++ ++ return _SUCCESS; ++} ++ ++/** ++ * rtw_cbuf_pop - pop a pointer from cbuf ++ * @cbuf: pointer of struct rtw_cbuf ++ * ++ * Lock free operation, be careful of the use scheme ++ * Returns: pointer popped out ++ */ ++void *rtw_cbuf_pop(struct rtw_cbuf *cbuf) ++{ ++ void *buf; ++ if (rtw_cbuf_empty(cbuf)) ++ return NULL; ++ ++ if (0) ++ DBG_88E("%s on %u\n", __func__, cbuf->read); ++ buf = cbuf->bufs[cbuf->read]; ++ cbuf->read = (cbuf->read+1)%cbuf->size; ++ ++ return buf; ++} ++ ++/** ++ * rtw_cbuf_alloc - allocate a rtw_cbuf with given size and do initialization ++ * @size: size of pointer ++ * ++ * Returns: pointer of srtuct rtw_cbuf, NULL for allocation failure ++ */ ++struct rtw_cbuf *rtw_cbuf_alloc(u32 size) ++{ ++ struct rtw_cbuf *cbuf; ++ ++ cbuf = (struct rtw_cbuf *)rtw_malloc(sizeof(*cbuf) + ++ sizeof(void *)*size); ++ ++ if (cbuf) { ++ cbuf->write = 0; ++ cbuf->read = 0; ++ cbuf->size = size; ++ } ++ return cbuf; ++} +diff --git a/drivers/staging/r8188eu/os_dep/recv_linux.c b/drivers/staging/r8188eu/os_dep/recv_linux.c +new file mode 100644 +index 000000000000..44c5fcbafa6a +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/recv_linux.c +@@ -0,0 +1,270 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++#define _RECV_OSDEP_C_ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* init os related resource in struct recv_priv */ ++int rtw_os_recv_resource_init(struct recv_priv *precvpriv, ++ struct adapter *padapter) ++{ ++ return _SUCCESS; ++} ++ ++/* alloc os related resource in struct recv_frame */ ++int rtw_os_recv_resource_alloc(struct adapter *padapter, ++ struct recv_frame *precvframe) ++{ ++ precvframe->pkt_newalloc = NULL; ++ precvframe->pkt = NULL; ++ return _SUCCESS; ++} ++ ++/* free os related resource in struct recv_frame */ ++void rtw_os_recv_resource_free(struct recv_priv *precvpriv) ++{ ++} ++ ++/* alloc os related resource in struct recv_buf */ ++int rtw_os_recvbuf_resource_alloc(struct adapter *padapter, ++ struct recv_buf *precvbuf) ++{ ++ int res = _SUCCESS; ++ ++ precvbuf->irp_pending = false; ++ precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); ++ if (precvbuf->purb == NULL) ++ res = _FAIL; ++ precvbuf->pskb = NULL; ++ precvbuf->reuse = false; ++ precvbuf->pallocated_buf = NULL; ++ precvbuf->pbuf = NULL; ++ precvbuf->pdata = NULL; ++ precvbuf->phead = NULL; ++ precvbuf->ptail = NULL; ++ precvbuf->pend = NULL; ++ precvbuf->transfer_len = 0; ++ precvbuf->len = 0; ++ return res; ++} ++ ++/* free os related resource in struct recv_buf */ ++int rtw_os_recvbuf_resource_free(struct adapter *padapter, ++ struct recv_buf *precvbuf) ++{ ++ usb_free_urb(precvbuf->purb); ++ return _SUCCESS; ++} ++ ++void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup) ++{ ++ union iwreq_data wrqu; ++ struct iw_michaelmicfailure ev; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct security_priv *psecuritypriv = &padapter->securitypriv; ++ u32 cur_time = 0; ++ ++ if (psecuritypriv->last_mic_err_time == 0) { ++ psecuritypriv->last_mic_err_time = jiffies; ++ } else { ++ cur_time = jiffies; ++ ++ if (cur_time - psecuritypriv->last_mic_err_time < 60*HZ) { ++ psecuritypriv->btkip_countermeasure = true; ++ psecuritypriv->last_mic_err_time = 0; ++ psecuritypriv->btkip_countermeasure_time = cur_time; ++ } else { ++ psecuritypriv->last_mic_err_time = jiffies; ++ } ++ } ++ ++ memset(&ev, 0x00, sizeof(ev)); ++ if (bgroup) ++ ev.flags |= IW_MICFAILURE_GROUP; ++ else ++ ev.flags |= IW_MICFAILURE_PAIRWISE; ++ ++ ev.src_addr.sa_family = ARPHRD_ETHER; ++ memcpy(ev.src_addr.sa_data, &pmlmepriv->assoc_bssid[0], ETH_ALEN); ++ memset(&wrqu, 0x00, sizeof(wrqu)); ++ wrqu.data.length = sizeof(ev); ++ wireless_send_event(padapter->pnetdev, IWEVMICHAELMICFAILURE, ++ &wrqu, (char *)&ev); ++} ++ ++void rtw_hostapd_mlme_rx(struct adapter *padapter, ++ struct recv_frame *precv_frame) ++{ ++} ++ ++int rtw_recv_indicatepkt(struct adapter *padapter, ++ struct recv_frame *precv_frame) ++{ ++ struct recv_priv *precvpriv; ++ struct __queue *pfree_recv_queue; ++ struct sk_buff *skb; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ ++ precvpriv = &(padapter->recvpriv); ++ pfree_recv_queue = &(precvpriv->free_recv_queue); ++ ++ skb = precv_frame->pkt; ++ if (skb == NULL) { ++ RT_TRACE(_module_recv_osdep_c_, _drv_err_, ++ ("rtw_recv_indicatepkt():skb == NULL something wrong!!!!\n")); ++ goto _recv_indicatepkt_drop; ++ } ++ ++ RT_TRACE(_module_recv_osdep_c_, _drv_info_, ++ ("rtw_recv_indicatepkt():skb != NULL !!!\n")); ++ RT_TRACE(_module_recv_osdep_c_, _drv_info_, ++ ("rtw_recv_indicatepkt():precv_frame->rx_head =%p precv_frame->hdr.rx_data =%p\n", ++ precv_frame->rx_head, precv_frame->rx_data)); ++ RT_TRACE(_module_recv_osdep_c_, _drv_info_, ++ ("precv_frame->hdr.rx_tail =%p precv_frame->rx_end =%p precv_frame->hdr.len =%d\n", ++ precv_frame->rx_tail, precv_frame->rx_end, ++ precv_frame->len)); ++ ++ skb->data = precv_frame->rx_data; ++ ++ skb_set_tail_pointer(skb, precv_frame->len); ++ ++ skb->len = precv_frame->len; ++ ++ RT_TRACE(_module_recv_osdep_c_, _drv_info_, ++ ("skb->head =%p skb->data =%p skb->tail =%p skb->end =%p skb->len =%d\n", ++ skb->head, skb->data, skb_tail_pointer(skb), ++ skb_end_pointer(skb), skb->len)); ++ ++ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { ++ struct sk_buff *pskb2 = NULL; ++ struct sta_info *psta = NULL; ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ struct rx_pkt_attrib *pattrib = &precv_frame->attrib; ++ int bmcast = IS_MCAST(pattrib->dst); ++ ++ if (memcmp(pattrib->dst, myid(&padapter->eeprompriv), ++ ETH_ALEN)) { ++ if (bmcast) { ++ psta = rtw_get_bcmc_stainfo(padapter); ++ pskb2 = skb_clone(skb, GFP_ATOMIC); ++ } else { ++ psta = rtw_get_stainfo(pstapriv, pattrib->dst); ++ } ++ ++ if (psta) { ++ struct net_device *pnetdev; ++ ++ pnetdev = (struct net_device *)padapter->pnetdev; ++ skb->dev = pnetdev; ++ skb_set_queue_mapping(skb, rtw_recv_select_queue(skb)); ++ ++ rtw_xmit_entry(skb, pnetdev); ++ ++ if (bmcast) ++ skb = pskb2; ++ else ++ goto _recv_indicatepkt_end; ++ } ++ } ++ } ++ ++ rcu_read_lock(); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++ rcu_dereference(padapter->pnetdev->rx_handler_data); ++#else ++ rcu_dereference(padapter->pnetdev->br_port); ++#endif ++ rcu_read_unlock(); ++ ++ skb->ip_summed = CHECKSUM_NONE; ++ skb->dev = padapter->pnetdev; ++ skb->protocol = eth_type_trans(skb, padapter->pnetdev); ++ ++ netif_rx(skb); ++ ++_recv_indicatepkt_end: ++ ++ /* pointers to NULL before rtw_free_recvframe() */ ++ precv_frame->pkt = NULL; ++ ++ rtw_free_recvframe(precv_frame, pfree_recv_queue); ++ ++ RT_TRACE(_module_recv_osdep_c_, _drv_info_, ++ ("\n rtw_recv_indicatepkt :after netif_rx!!!!\n")); ++ ++ return _SUCCESS; ++ ++_recv_indicatepkt_drop: ++ ++ /* enqueue back to free_recv_queue */ ++ rtw_free_recvframe(precv_frame, pfree_recv_queue); ++ ++ return _FAIL; ++} ++ ++void rtw_os_read_port(struct adapter *padapter, struct recv_buf *precvbuf) ++{ ++ struct recv_priv *precvpriv = &padapter->recvpriv; ++ ++ precvbuf->ref_cnt--; ++ /* free skb in recv_buf */ ++ dev_kfree_skb_any(precvbuf->pskb); ++ precvbuf->pskb = NULL; ++ precvbuf->reuse = false; ++ if (!precvbuf->irp_pending) ++ rtw_read_port(padapter, precvpriv->ff_hwaddr, 0, ++ (unsigned char *)precvbuf); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++static void _rtw_reordering_ctrl_timeout_handler(void *func_context) ++#else ++static void _rtw_reordering_ctrl_timeout_handler(struct timer_list *t) ++#endif ++{ ++ struct recv_reorder_ctrl *preorder_ctrl; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ preorder_ctrl = (struct recv_reorder_ctrl *)func_context; ++#else ++ preorder_ctrl = from_timer(preorder_ctrl, t, reordering_ctrl_timer); ++#endif ++ rtw_reordering_ctrl_timeout_handler(preorder_ctrl); ++} ++ ++void rtw_init_recv_timer(struct recv_reorder_ctrl *preorder_ctrl) ++{ ++ struct adapter *padapter = preorder_ctrl->padapter; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) ++ _init_timer(&(preorder_ctrl->reordering_ctrl_timer), padapter->pnetdev, _rtw_reordering_ctrl_timeout_handler, preorder_ctrl); ++#else ++ timer_setup(&preorder_ctrl->reordering_ctrl_timer, _rtw_reordering_ctrl_timeout_handler, 0); ++#endif ++} +diff --git a/drivers/staging/r8188eu/os_dep/rtw_android.c b/drivers/staging/r8188eu/os_dep/rtw_android.c +new file mode 100644 +index 000000000000..a20d900d85ca +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/rtw_android.c +@@ -0,0 +1,303 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++static const char *android_wifi_cmd_str[ANDROID_WIFI_CMD_MAX] = { ++ "START", ++ "STOP", ++ "SCAN-ACTIVE", ++ "SCAN-PASSIVE", ++ "RSSI", ++ "LINKSPEED", ++ "RXFILTER-START", ++ "RXFILTER-STOP", ++ "RXFILTER-ADD", ++ "RXFILTER-REMOVE", ++ "BTCOEXSCAN-START", ++ "BTCOEXSCAN-STOP", ++ "BTCOEXMODE", ++ "SETSUSPENDOPT", ++ "P2P_DEV_ADDR", ++ "SETFWPATH", ++ "SETBAND", ++ "GETBAND", ++ "COUNTRY", ++ "P2P_SET_NOA", ++ "P2P_GET_NOA", ++ "P2P_SET_PS", ++ "SET_AP_WPS_P2P_IE", ++ "MACADDR", ++ "BLOCK", ++ "WFD-ENABLE", ++ "WFD-DISABLE", ++ "WFD-SET-TCPPORT", ++ "WFD-SET-MAXTPUT", ++ "WFD-SET-DEVTYPE", ++}; ++ ++struct android_wifi_priv_cmd { ++ const char __user *buf; ++ int used_len; ++ int total_len; ++}; ++ ++/** ++ * Local (static) functions and variables ++ */ ++ ++/* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first ++ * time (only) in dhd_open, subsequential wifi on will be handled by ++ * wl_android_wifi_on ++ */ ++static int g_wifi_on = true; ++ ++int rtw_android_cmdstr_to_num(char *cmdstr) ++{ ++ int cmd_num; ++ for(cmd_num=0; cmd_num < ANDROID_WIFI_CMD_MAX; cmd_num++) ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) ++ if (!strncasecmp(cmdstr, android_wifi_cmd_str[cmd_num], ++ strlen(android_wifi_cmd_str[cmd_num]))) ++#else ++ if(0 == strnicmp(cmdstr, android_wifi_cmd_str[cmd_num], ++ strlen(android_wifi_cmd_str[cmd_num]))) ++#endif ++ break; ++ return cmd_num; ++} ++ ++static int rtw_android_get_rssi(struct net_device *net, char *command, ++ int total_len) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(net); ++ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); ++ struct wlan_network *pcur_network = &pmlmepriv->cur_network; ++ int bytes_written = 0; ++ ++ if (check_fwstate(pmlmepriv, _FW_LINKED)) { ++ bytes_written += snprintf(&command[bytes_written], total_len, ++ "%s rssi %d", ++ pcur_network->network.Ssid.Ssid, ++ padapter->recvpriv.rssi); ++ } ++ return bytes_written; ++} ++ ++static int rtw_android_get_link_speed(struct net_device *net, char *command, ++ int total_len) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(net); ++ int bytes_written; ++ u16 link_speed; ++ ++ link_speed = rtw_get_cur_max_rate(padapter) / 10; ++ bytes_written = snprintf(command, total_len, "LinkSpeed %d", ++ link_speed); ++ return bytes_written; ++} ++ ++static int rtw_android_get_macaddr(struct net_device *net, char *command, ++ int total_len) ++{ ++ int bytes_written; ++ ++ bytes_written = snprintf(command, total_len, "Macaddr = %pM", ++ net->dev_addr); ++ return bytes_written; ++} ++ ++static int android_set_cntry(struct net_device *net, char *command, ++ int total_len) ++{ ++ struct adapter *adapter = (struct adapter *)rtw_netdev_priv(net); ++ char *country_code = command + strlen(android_wifi_cmd_str[ANDROID_WIFI_CMD_COUNTRY]) + 1; ++ int ret; ++ ++ ret = rtw_set_country(adapter, country_code); ++ return (ret == _SUCCESS) ? 0 : -1; ++} ++ ++static int android_get_p2p_addr(struct net_device *net, char *command, ++ int total_len) ++{ ++ /* We use the same address as our HW MAC address */ ++ memcpy(command, net->dev_addr, ETH_ALEN); ++ return ETH_ALEN; ++} ++ ++static int rtw_android_set_block(struct net_device *net, char *command, ++ int total_len) ++{ ++ return 0; ++} ++ ++int rtw_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) ++{ ++ int ret = 0; ++ char *command = NULL; ++ int cmd_num; ++ int bytes_written = 0; ++ struct android_wifi_priv_cmd priv_cmd; ++ ++ rtw_lock_suspend(); ++ if (!ifr->ifr_data) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ if (copy_from_user(&priv_cmd, ifr->ifr_data, ++ sizeof(struct android_wifi_priv_cmd))) { ++ ret = -EFAULT; ++ goto exit; ++ } ++ command = kmalloc(priv_cmd.total_len, GFP_KERNEL); ++ if (!command) { ++ DBG_88E("%s: failed to allocate memory\n", __func__); ++ ret = -ENOMEM; ++ goto exit; ++ } ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) ++ if (!access_ok(priv_cmd.buf, priv_cmd.total_len)) { ++#else ++ if (!access_ok(VERIFY_READ, priv_cmd.buf, priv_cmd.total_len)) { ++#endif ++ DBG_88E("%s: failed to access memory\n", __func__); ++ ret = -EFAULT; ++ goto exit; ++ } ++ if (copy_from_user(command, (char __user *)priv_cmd.buf, ++ priv_cmd.total_len)) { ++ ret = -EFAULT; ++ goto exit; ++ } ++ DBG_88E("%s: Android private cmd \"%s\" on %s\n", ++ __func__, command, ifr->ifr_name); ++ cmd_num = rtw_android_cmdstr_to_num(command); ++ switch (cmd_num) { ++ case ANDROID_WIFI_CMD_START: ++ goto response; ++ case ANDROID_WIFI_CMD_SETFWPATH: ++ goto response; ++ } ++ if (!g_wifi_on) { ++ DBG_88E("%s: Ignore private cmd \"%s\" - iface %s is down\n", ++ __func__, command, ifr->ifr_name); ++ ret = 0; ++ goto exit; ++ } ++ switch (cmd_num) { ++ case ANDROID_WIFI_CMD_STOP: ++ break; ++ case ANDROID_WIFI_CMD_SCAN_ACTIVE: ++ break; ++ case ANDROID_WIFI_CMD_SCAN_PASSIVE: ++ break; ++ case ANDROID_WIFI_CMD_RSSI: ++ bytes_written = rtw_android_get_rssi(net, command, ++ priv_cmd.total_len); ++ break; ++ case ANDROID_WIFI_CMD_LINKSPEED: ++ bytes_written = rtw_android_get_link_speed(net, command, ++ priv_cmd.total_len); ++ break; ++ case ANDROID_WIFI_CMD_MACADDR: ++ bytes_written = rtw_android_get_macaddr(net, command, ++ priv_cmd.total_len); ++ break; ++ case ANDROID_WIFI_CMD_BLOCK: ++ bytes_written = rtw_android_set_block(net, command, ++ priv_cmd.total_len); ++ break; ++ case ANDROID_WIFI_CMD_RXFILTER_START: ++ break; ++ case ANDROID_WIFI_CMD_RXFILTER_STOP: ++ break; ++ case ANDROID_WIFI_CMD_RXFILTER_ADD: ++ break; ++ case ANDROID_WIFI_CMD_RXFILTER_REMOVE: ++ break; ++ case ANDROID_WIFI_CMD_BTCOEXSCAN_START: ++ /* TBD: BTCOEXSCAN-START */ ++ break; ++ case ANDROID_WIFI_CMD_BTCOEXSCAN_STOP: ++ /* TBD: BTCOEXSCAN-STOP */ ++ break; ++ case ANDROID_WIFI_CMD_BTCOEXMODE: ++ break; ++ case ANDROID_WIFI_CMD_SETSUSPENDOPT: ++ break; ++ case ANDROID_WIFI_CMD_SETBAND: ++ break; ++ case ANDROID_WIFI_CMD_GETBAND: ++ break; ++ case ANDROID_WIFI_CMD_COUNTRY: ++ bytes_written = android_set_cntry(net, command, ++ priv_cmd.total_len); ++ break; ++ case ANDROID_WIFI_CMD_P2P_DEV_ADDR: ++ bytes_written = android_get_p2p_addr(net, command, ++ priv_cmd.total_len); ++ break; ++ case ANDROID_WIFI_CMD_P2P_SET_NOA: ++ break; ++ case ANDROID_WIFI_CMD_P2P_GET_NOA: ++ break; ++ case ANDROID_WIFI_CMD_P2P_SET_PS: ++ break; ++ default: ++ DBG_88E("Unknown PRIVATE command %s - ignored\n", command); ++ snprintf(command, 3, "OK"); ++ bytes_written = strlen("OK"); ++ } ++ ++response: ++ if (bytes_written >= 0) { ++ if ((bytes_written == 0) && (priv_cmd.total_len > 0)) ++ command[0] = '\0'; ++ if (bytes_written >= priv_cmd.total_len) { ++ DBG_88E("%s: bytes_written = %d\n", __func__, ++ bytes_written); ++ bytes_written = priv_cmd.total_len; ++ } else { ++ bytes_written++; ++ } ++ priv_cmd.used_len = bytes_written; ++ if (copy_to_user((char __user *)priv_cmd.buf, command, ++ bytes_written)) { ++ DBG_88E("%s: failed to copy data to user buffer\n", ++ __func__); ++ ret = -EFAULT; ++ } ++ } else { ++ ret = bytes_written; ++ } ++exit: ++ rtw_unlock_suspend(); ++ kfree(command); ++ return ret; ++} +diff --git a/drivers/staging/r8188eu/os_dep/usb_intf.c b/drivers/staging/r8188eu/os_dep/usb_intf.c +new file mode 100644 +index 000000000000..fd16afa1de33 +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/usb_intf.c +@@ -0,0 +1,863 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++#define _HCI_INTF_C_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++int ui_pid[3] = {0, 0, 0}; ++ ++static int rtw_suspend(struct usb_interface *intf, pm_message_t message); ++static int rtw_resume(struct usb_interface *intf); ++ ++static int rtw_drv_init(struct usb_interface *pusb_intf, const struct usb_device_id *pdid); ++static void rtw_dev_remove(struct usb_interface *pusb_intf); ++ ++#define USB_VENDER_ID_REALTEK 0x0bda ++ ++/* DID_USB_v916_20130116 */ ++static struct usb_device_id rtw_usb_id_tbl[] = { ++ /*=== Realtek demoboard ===*/ ++ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8179)}, /* 8188EUS */ ++ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x0179)}, /* 8188ETV */ ++ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0xf179)}, /* 8188FU */ ++ /*=== Customer ID ===*/ ++ /****** 8188EUS ********/ ++ {USB_DEVICE(0x07B8, 0x8179)}, /* Abocom - Abocom */ ++ {USB_DEVICE(0x0DF6, 0x0076)}, /* Sitecom N150 v2 */ ++ {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */ ++ {USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */ ++ {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */ ++ {USB_DEVICE(0x2001, 0x331B)}, /* D-Link DWA-121 rev B1 */ ++ {USB_DEVICE(0x056E, 0x4008)}, /* Elecom WDC-150SU2M */ ++ {USB_DEVICE(0x2357, 0x010c)}, /* TP-Link TL-WN722N v2 */ ++ {USB_DEVICE(0x2357, 0x0111)}, /* TP-Link TL-WN727N v5.21 */ ++ {USB_DEVICE(0x2C4E, 0x0102)}, /* MERCUSYS MW150US v2 */ ++ {USB_DEVICE(0x0B05, 0x18F0)}, /* ASUS USB-N10 Nano B1 */ ++ {USB_DEVICE(0x7392, 0xb811)}, /* Edimax EW-7811Un V2 */ ++ {} /* Terminating entry */ ++}; ++ ++MODULE_DEVICE_TABLE(usb, rtw_usb_id_tbl); ++ ++static struct specific_device_id specific_device_id_tbl[] = { ++ {} /* empty table for now */ ++}; ++ ++struct rtw_usb_drv { ++ struct usb_driver usbdrv; ++ int drv_registered; ++ struct mutex hw_init_mutex; ++}; ++ ++static struct rtw_usb_drv rtl8188e_usb_drv = { ++ .usbdrv.name = (char *)"r8188eu", ++ .usbdrv.probe = rtw_drv_init, ++ .usbdrv.disconnect = rtw_dev_remove, ++ .usbdrv.id_table = rtw_usb_id_tbl, ++ .usbdrv.suspend = rtw_suspend, ++ .usbdrv.resume = rtw_resume, ++ .usbdrv.reset_resume = rtw_resume, ++}; ++ ++static struct rtw_usb_drv *usb_drv = &rtl8188e_usb_drv; ++ ++static inline int RT_usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) ++{ ++ return (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN; ++} ++ ++static inline int RT_usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd) ++{ ++ return (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT; ++} ++ ++static inline int RT_usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd) ++{ ++ return (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT; ++} ++ ++static inline int RT_usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd) ++{ ++ return (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK; ++} ++ ++static inline int RT_usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd) ++{ ++ return RT_usb_endpoint_xfer_bulk(epd) && RT_usb_endpoint_dir_in(epd); ++} ++ ++static inline int RT_usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd) ++{ ++ return RT_usb_endpoint_xfer_bulk(epd) && RT_usb_endpoint_dir_out(epd); ++} ++ ++static inline int usb_endpoint_is_int(const struct usb_endpoint_descriptor *epd) ++{ ++ return RT_usb_endpoint_xfer_int(epd) && RT_usb_endpoint_dir_in(epd); ++} ++ ++static inline int RT_usb_endpoint_num(const struct usb_endpoint_descriptor *epd) ++{ ++ return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; ++} ++ ++static u8 rtw_init_intf_priv(struct dvobj_priv *dvobj) ++{ ++ u8 rst = _SUCCESS; ++ ++ _rtw_mutex_init(&dvobj->usb_vendor_req_mutex); ++ ++ dvobj->usb_alloc_vendor_req_buf = rtw_zmalloc(MAX_USB_IO_CTL_SIZE); ++ if (dvobj->usb_alloc_vendor_req_buf == NULL) { ++ DBG_88E("alloc usb_vendor_req_buf failed... /n"); ++ rst = _FAIL; ++ goto exit; ++ } ++ dvobj->usb_vendor_req_buf = (u8 *)N_BYTE_ALIGMENT((size_t)(dvobj->usb_alloc_vendor_req_buf), ALIGNMENT_UNIT); ++exit: ++ return rst; ++} ++ ++static u8 rtw_deinit_intf_priv(struct dvobj_priv *dvobj) ++{ ++ u8 rst = _SUCCESS; ++ ++ kfree(dvobj->usb_alloc_vendor_req_buf); ++ _rtw_mutex_free(&dvobj->usb_vendor_req_mutex); ++ return rst; ++} ++ ++static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf) ++{ ++ int i; ++ int status = _FAIL; ++ struct dvobj_priv *pdvobjpriv; ++ struct usb_host_config *phost_conf; ++ struct usb_config_descriptor *pconf_desc; ++ struct usb_host_interface *phost_iface; ++ struct usb_interface_descriptor *piface_desc; ++ struct usb_endpoint_descriptor *pendp_desc; ++ struct usb_device *pusbd; ++ ++ pdvobjpriv = (struct dvobj_priv *)rtw_zmalloc(sizeof(*pdvobjpriv)); ++ if (pdvobjpriv == NULL) ++ goto exit; ++ ++ pdvobjpriv->pusbintf = usb_intf; ++ pusbd = interface_to_usbdev(usb_intf); ++ pdvobjpriv->pusbdev = pusbd; ++ usb_set_intfdata(usb_intf, pdvobjpriv); ++ ++ pdvobjpriv->RtNumInPipes = 0; ++ pdvobjpriv->RtNumOutPipes = 0; ++ ++ phost_conf = pusbd->actconfig; ++ pconf_desc = &phost_conf->desc; ++ ++ phost_iface = &usb_intf->altsetting[0]; ++ piface_desc = &phost_iface->desc; ++ ++ pdvobjpriv->NumInterfaces = pconf_desc->bNumInterfaces; ++ pdvobjpriv->InterfaceNumber = piface_desc->bInterfaceNumber; ++ pdvobjpriv->nr_endpoint = piface_desc->bNumEndpoints; ++ ++ for (i = 0; i < pdvobjpriv->nr_endpoint; i++) { ++ int ep_num; ++ pendp_desc = &phost_iface->endpoint[i].desc; ++ ++ ep_num = usb_endpoint_num(pendp_desc); ++ ++ if (usb_endpoint_is_bulk_in(pendp_desc)) { ++ pdvobjpriv->RtInPipe[pdvobjpriv->RtNumInPipes] = ep_num; ++ pdvobjpriv->RtNumInPipes++; ++ } else if (usb_endpoint_is_int_in(pendp_desc)) { ++ pdvobjpriv->RtInPipe[pdvobjpriv->RtNumInPipes] = ep_num; ++ pdvobjpriv->RtNumInPipes++; ++ } else if (usb_endpoint_is_bulk_out(pendp_desc)) { ++ pdvobjpriv->RtOutPipe[pdvobjpriv->RtNumOutPipes] = ++ ep_num; ++ pdvobjpriv->RtNumOutPipes++; ++ } ++ pdvobjpriv->ep_num[i] = ep_num; ++ } ++ ++ if (pusbd->speed == USB_SPEED_HIGH) { ++ pdvobjpriv->ishighspeed = true; ++ DBG_88E("USB_SPEED_HIGH\n"); ++ } else { ++ pdvobjpriv->ishighspeed = false; ++ DBG_88E("NON USB_SPEED_HIGH\n"); ++ } ++ ++ if (rtw_init_intf_priv(pdvobjpriv) == _FAIL) { ++ RT_TRACE(_module_os_intfs_c_, _drv_err_, ++ ("\n Can't INIT rtw_init_intf_priv\n")); ++ goto free_dvobj; ++ } ++ ++ /* 3 misc */ ++ sema_init(&(pdvobjpriv->usb_suspend_sema), 0); ++ rtw_reset_continual_urb_error(pdvobjpriv); ++ ++ usb_get_dev(pusbd); ++ ++ status = _SUCCESS; ++ ++free_dvobj: ++ if (status != _SUCCESS && pdvobjpriv) { ++ usb_set_intfdata(usb_intf, NULL); ++ kfree(pdvobjpriv); ++ pdvobjpriv = NULL; ++ } ++exit: ++ return pdvobjpriv; ++} ++ ++static void usb_dvobj_deinit(struct usb_interface *usb_intf) ++{ ++ struct dvobj_priv *dvobj = usb_get_intfdata(usb_intf); ++ ++ usb_set_intfdata(usb_intf, NULL); ++ if (dvobj) { ++ /* Modify condition for 92DU DMDP 2010.11.18, by Thomas */ ++ if ((dvobj->NumInterfaces != 2 && ++ dvobj->NumInterfaces != 3) || ++ (dvobj->InterfaceNumber == 1)) { ++ if (interface_to_usbdev(usb_intf)->state != ++ USB_STATE_NOTATTACHED) { ++ /* If we didn't unplug usb dongle and ++ * remove/insert module, driver fails ++ * on sitesurvey for the first time when ++ * device is up . Reset usb port for sitesurvey ++ * fail issue. */ ++ DBG_88E("usb attached..., try to reset usb device\n"); ++ usb_reset_device(interface_to_usbdev(usb_intf)); ++ } ++ } ++ rtw_deinit_intf_priv(dvobj); ++ kfree(dvobj); ++ } ++ ++ usb_put_dev(interface_to_usbdev(usb_intf)); ++ ++} ++ ++static void chip_by_usb_id(struct adapter *padapter, ++ const struct usb_device_id *pdid) ++{ ++ padapter->chip_type = NULL_CHIP_TYPE; ++ hal_set_hw_type(padapter); ++} ++ ++static void usb_intf_start(struct adapter *padapter) ++{ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+usb_intf_start\n")); ++ ++ rtw_hal_inirp_init(padapter); ++ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-usb_intf_start\n")); ++} ++ ++static void usb_intf_stop(struct adapter *padapter) ++{ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+usb_intf_stop\n")); ++ ++ /* disabel_hw_interrupt */ ++ if (!padapter->bSurpriseRemoved) { ++ /* device still exists, so driver can do i/o operation */ ++ /* TODO: */ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ++ ("SurpriseRemoved == false\n")); ++ } ++ ++ /* cancel in irp */ ++ rtw_hal_inirp_deinit(padapter); ++ ++ /* cancel out irp */ ++ rtw_write_port_cancel(padapter); ++ ++ /* todo:cancel other irps */ ++ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-usb_intf_stop\n")); ++} ++ ++static void rtw_dev_unload(struct adapter *padapter) ++{ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+rtw_dev_unload\n")); ++ ++ if (padapter->bup) { ++ DBG_88E("===> rtw_dev_unload\n"); ++ padapter->bDriverStopped = true; ++ if (padapter->xmitpriv.ack_tx) ++ rtw_ack_tx_done(&padapter->xmitpriv, RTW_SCTX_DONE_DRV_STOP); ++ /* s3. */ ++ if (padapter->intf_stop) ++ padapter->intf_stop(padapter); ++ /* s4. */ ++ if (!padapter->pwrctrlpriv.bInternalAutoSuspend) ++ rtw_stop_drv_threads(padapter); ++ ++ /* s5. */ ++ if (!padapter->bSurpriseRemoved) { ++ rtw_hal_deinit(padapter); ++ padapter->bSurpriseRemoved = true; ++ } ++ ++ padapter->bup = false; ++ } else { ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ++ ("r871x_dev_unload():padapter->bup == false\n")); ++ } ++ ++ DBG_88E("<=== rtw_dev_unload\n"); ++ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-rtw_dev_unload\n")); ++} ++ ++static void process_spec_devid(const struct usb_device_id *pdid) ++{ ++ u16 vid, pid; ++ u32 flags; ++ int i; ++ int num = sizeof(specific_device_id_tbl) / ++ sizeof(struct specific_device_id); ++ ++ for (i = 0; i < num; i++) { ++ vid = specific_device_id_tbl[i].idVendor; ++ pid = specific_device_id_tbl[i].idProduct; ++ flags = specific_device_id_tbl[i].flags; ++ ++ if ((pdid->idVendor == vid) && (pdid->idProduct == pid) && ++ (flags&SPEC_DEV_ID_DISABLE_HT)) { ++ rtw_ht_enable = 0; ++ rtw_cbw40_enable = 0; ++ rtw_ampdu_enable = 0; ++ } ++ } ++} ++ ++int rtw_hw_suspend(struct adapter *padapter) ++{ ++ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; ++ struct net_device *pnetdev = padapter->pnetdev; ++ ++ ++ if ((!padapter->bup) || (padapter->bDriverStopped) || ++ (padapter->bSurpriseRemoved)) { ++ DBG_88E("padapter->bup=%d bDriverStopped=%d bSurpriseRemoved = %d\n", ++ padapter->bup, padapter->bDriverStopped, ++ padapter->bSurpriseRemoved); ++ goto error_exit; ++ } ++ ++ if (padapter) { /* system suspend */ ++ LeaveAllPowerSaveMode(padapter); ++ ++ DBG_88E("==> rtw_hw_suspend\n"); ++ _enter_pwrlock(&pwrpriv->lock); ++ pwrpriv->bips_processing = true; ++ /* s1. */ ++ if (pnetdev) { ++ netif_carrier_off(pnetdev); ++ rtw_netif_stop_queue(pnetdev); ++ } ++ ++ /* s2. */ ++ rtw_disassoc_cmd(padapter, 500, false); ++ ++ /* s2-2. indicate disconnect to os */ ++ { ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ ++ if (check_fwstate(pmlmepriv, _FW_LINKED)) { ++ _clr_fwstate_(pmlmepriv, _FW_LINKED); ++ ++ rtw_led_control(padapter, LED_CTL_NO_LINK); ++ ++ rtw_os_indicate_disconnect(padapter); ++ ++ /* donnot enqueue cmd */ ++ rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_DISCONNECT, 0); ++ } ++ } ++ /* s2-3. */ ++ rtw_free_assoc_resources(padapter, 1); ++ ++ /* s2-4. */ ++ rtw_free_network_queue(padapter, true); ++ rtw_ips_dev_unload(padapter); ++ pwrpriv->rf_pwrstate = rf_off; ++ pwrpriv->bips_processing = false; ++ ++ _exit_pwrlock(&pwrpriv->lock); ++ } else { ++ goto error_exit; ++ } ++ return 0; ++ ++error_exit: ++ DBG_88E("%s, failed\n", __func__); ++ return -1; ++} ++ ++int rtw_hw_resume(struct adapter *padapter) ++{ ++ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; ++ struct net_device *pnetdev = padapter->pnetdev; ++ ++ ++ if (padapter) { /* system resume */ ++ DBG_88E("==> rtw_hw_resume\n"); ++ _enter_pwrlock(&pwrpriv->lock); ++ pwrpriv->bips_processing = true; ++ rtw_reset_drv_sw(padapter); ++ ++ if (pm_netdev_open(pnetdev, false) != 0) { ++ _exit_pwrlock(&pwrpriv->lock); ++ goto error_exit; ++ } ++ ++ netif_device_attach(pnetdev); ++ netif_carrier_on(pnetdev); ++ ++ if (!netif_queue_stopped(pnetdev)) ++ netif_start_queue(pnetdev); ++ else ++ netif_wake_queue(pnetdev); ++ ++ pwrpriv->bkeepfwalive = false; ++ pwrpriv->brfoffbyhw = false; ++ ++ pwrpriv->rf_pwrstate = rf_on; ++ pwrpriv->bips_processing = false; ++ ++ _exit_pwrlock(&pwrpriv->lock); ++ } else { ++ goto error_exit; ++ } ++ ++ ++ return 0; ++error_exit: ++ DBG_88E("%s, Open net dev failed\n", __func__); ++ return -1; ++} ++ ++static int rtw_suspend(struct usb_interface *pusb_intf, pm_message_t message) ++{ ++ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf); ++ struct adapter *padapter = dvobj->if1; ++ struct net_device *pnetdev = padapter->pnetdev; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; ++ ++ int ret = 0; ++ u32 start_time = jiffies; ++ ++ ++ DBG_88E("==> %s (%s:%d)\n", __func__, current->comm, current->pid); ++ ++ if ((!padapter->bup) || (padapter->bDriverStopped) || ++ (padapter->bSurpriseRemoved)) { ++ DBG_88E("padapter->bup=%d bDriverStopped=%d bSurpriseRemoved = %d\n", ++ padapter->bup, padapter->bDriverStopped, ++ padapter->bSurpriseRemoved); ++ goto exit; ++ } ++ ++ pwrpriv->bInSuspend = true; ++ rtw_cancel_all_timer(padapter); ++ LeaveAllPowerSaveMode(padapter); ++ ++ _enter_pwrlock(&pwrpriv->lock); ++ /* s1. */ ++ if (pnetdev) { ++ netif_carrier_off(pnetdev); ++ rtw_netif_stop_queue(pnetdev); ++ } ++ ++ /* s2. */ ++ rtw_disassoc_cmd(padapter, 0, false); ++ ++ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) && ++ check_fwstate(pmlmepriv, _FW_LINKED)) { ++ DBG_88E("%s:%d %s(%pM), length:%d assoc_ssid.length:%d\n", ++ __func__, __LINE__, ++ pmlmepriv->cur_network.network.Ssid.Ssid, ++ pmlmepriv->cur_network.network.MacAddress, ++ pmlmepriv->cur_network.network.Ssid.SsidLength, ++ pmlmepriv->assoc_ssid.SsidLength); ++ ++ pmlmepriv->to_roaming = 1; ++ } ++ /* s2-2. indicate disconnect to os */ ++ rtw_indicate_disconnect(padapter); ++ /* s2-3. */ ++ rtw_free_assoc_resources(padapter, 1); ++ /* s2-4. */ ++ rtw_free_network_queue(padapter, true); ++ ++ rtw_dev_unload(padapter); ++ _exit_pwrlock(&pwrpriv->lock); ++ ++ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) ++ rtw_indicate_scan_done(padapter, 1); ++ ++ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) ++ rtw_indicate_disconnect(padapter); ++ ++exit: ++ DBG_88E("<=== %s return %d.............. in %dms\n", __func__ ++ , ret, rtw_get_passing_time_ms(start_time)); ++ ++ return ret; ++} ++ ++static int rtw_resume(struct usb_interface *pusb_intf) ++{ ++ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf); ++ struct adapter *padapter = dvobj->if1; ++ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv; ++ int ret = 0; ++ ++ if (pwrpriv->bInternalAutoSuspend) ++ ret = rtw_resume_process(padapter); ++ else ++ ret = rtw_resume_process(padapter); ++ return ret; ++} ++ ++int rtw_resume_process(struct adapter *padapter) ++{ ++ struct net_device *pnetdev; ++ struct pwrctrl_priv *pwrpriv = NULL; ++ int ret = -1; ++ u32 start_time = jiffies; ++ ++ DBG_88E("==> %s (%s:%d)\n", __func__, current->comm, current->pid); ++ ++ if (padapter) { ++ pnetdev = padapter->pnetdev; ++ pwrpriv = &padapter->pwrctrlpriv; ++ } else { ++ goto exit; ++ } ++ ++ _enter_pwrlock(&pwrpriv->lock); ++ rtw_reset_drv_sw(padapter); ++ if (pwrpriv) ++ pwrpriv->bkeepfwalive = false; ++ ++ DBG_88E("bkeepfwalive(%x)\n", pwrpriv->bkeepfwalive); ++ if (pm_netdev_open(pnetdev, true) != 0) ++ goto exit; ++ ++ netif_device_attach(pnetdev); ++ netif_carrier_on(pnetdev); ++ ++ _exit_pwrlock(&pwrpriv->lock); ++ ++ if (padapter->pid[1] != 0) { ++ DBG_88E("pid[1]:%d\n", padapter->pid[1]); ++ rtw_signal_process(padapter->pid[1], SIGUSR2); ++ } ++ ++ rtw_roaming(padapter, NULL); ++ ++ ret = 0; ++exit: ++ if (pwrpriv) ++ pwrpriv->bInSuspend = false; ++ DBG_88E("<=== %s return %d.............. in %dms\n", __func__, ++ ret, rtw_get_passing_time_ms(start_time)); ++ ++ ++ return ret; ++} ++ ++/* ++ * drv_init() - a device potentially for us ++ * ++ * notes: drv_init() is called when the bus driver has located ++ * a card for us to support. ++ * We accept the new device by returning 0. ++ */ ++ ++static struct adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj, ++ struct usb_interface *pusb_intf, const struct usb_device_id *pdid) ++{ ++ struct adapter *padapter = NULL; ++ struct net_device *pnetdev = NULL; ++ int status = _FAIL; ++ ++ padapter = (struct adapter *)rtw_zvmalloc(sizeof(*padapter)); ++ if (padapter == NULL) ++ goto exit; ++ padapter->dvobj = dvobj; ++ dvobj->if1 = padapter; ++ ++ padapter->bDriverStopped = true; ++ ++ padapter->hw_init_mutex = &usb_drv->hw_init_mutex; ++ ++ /* step 1-1., decide the chip_type via vid/pid */ ++ padapter->interface_type = RTW_USB; ++ chip_by_usb_id(padapter, pdid); ++ ++ if (rtw_handle_dualmac(padapter, 1) != _SUCCESS) ++ goto free_adapter; ++ ++ pnetdev = rtw_init_netdev(padapter); ++ if (pnetdev == NULL) ++ goto handle_dualmac; ++ SET_NETDEV_DEV(pnetdev, dvobj_to_dev(dvobj)); ++ padapter = rtw_netdev_priv(pnetdev); ++ ++ /* step 2. hook HalFunc, allocate HalData */ ++ hal_set_hal_ops(padapter); ++ ++ padapter->intf_start = &usb_intf_start; ++ padapter->intf_stop = &usb_intf_stop; ++ ++ /* step init_io_priv */ ++ rtw_init_io_priv(padapter, usb_set_intf_ops); ++ ++ /* step read_chip_version */ ++ rtw_hal_read_chip_version(padapter); ++ ++ /* step usb endpoint mapping */ ++ rtw_hal_chip_configure(padapter); ++ ++ /* step read efuse/eeprom data and get mac_addr */ ++ rtw_hal_read_chip_info(padapter); ++ ++ /* step 5. */ ++ if (rtw_init_drv_sw(padapter) == _FAIL) { ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ++ ("Initialize driver software resource Failed!\n")); ++ goto free_hal_data; ++ } ++ ++#ifdef CONFIG_PM ++ if (padapter->pwrctrlpriv.bSupportRemoteWakeup) { ++ dvobj->pusbdev->do_remote_wakeup = 1; ++ pusb_intf->needs_remote_wakeup = 1; ++ device_init_wakeup(&pusb_intf->dev, 1); ++ DBG_88E("\n padapter->pwrctrlpriv.bSupportRemoteWakeup~~~~~~\n"); ++ DBG_88E("\n padapter->pwrctrlpriv.bSupportRemoteWakeup~~~[%d]~~~\n", ++ device_may_wakeup(&pusb_intf->dev)); ++ } ++#endif ++ ++ /* 2012-07-11 Move here to prevent the 8723AS-VAU BT auto ++ * suspend influence */ ++ if (usb_autopm_get_interface(pusb_intf) < 0) ++ DBG_88E("can't get autopm:\n"); ++ ++ /* alloc dev name after read efuse. */ ++ rtw_init_netdev_name(pnetdev, padapter->registrypriv.ifname); ++ rtw_macaddr_cfg(padapter->eeprompriv.mac_addr); ++#ifdef CONFIG_88EU_P2P ++ rtw_init_wifidirect_addrs(padapter, padapter->eeprompriv.mac_addr, ++ padapter->eeprompriv.mac_addr); ++#endif ++ memcpy(pnetdev->dev_addr, padapter->eeprompriv.mac_addr, ETH_ALEN); ++ DBG_88E("MAC Address from pnetdev->dev_addr = %pM\n", ++ pnetdev->dev_addr); ++ ++ /* step 6. Tell the network stack we exist */ ++ if (register_netdev(pnetdev) != 0) { ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("register_netdev() failed\n")); ++ goto free_hal_data; ++ } ++ ++ DBG_88E("bDriverStopped:%d, bSurpriseRemoved:%d, bup:%d, hw_init_completed:%d\n" ++ , padapter->bDriverStopped ++ , padapter->bSurpriseRemoved ++ , padapter->bup ++ , padapter->hw_init_completed ++ ); ++ ++ status = _SUCCESS; ++ ++free_hal_data: ++ if (status != _SUCCESS) ++ kfree(padapter->HalData); ++handle_dualmac: ++ if (status != _SUCCESS) ++ rtw_handle_dualmac(padapter, 0); ++free_adapter: ++ if (status != _SUCCESS) { ++ if (pnetdev) ++ rtw_free_netdev(pnetdev); ++ else if (padapter) ++ rtw_vmfree((u8 *)padapter, sizeof(*padapter)); ++ padapter = NULL; ++ } ++exit: ++ return padapter; ++} ++ ++static void rtw_usb_if1_deinit(struct adapter *if1) ++{ ++ struct net_device *pnetdev = if1->pnetdev; ++ struct mlme_priv *pmlmepriv = &if1->mlmepriv; ++ ++ if (check_fwstate(pmlmepriv, _FW_LINKED)) ++ rtw_disassoc_cmd(if1, 0, false); ++ ++#ifdef CONFIG_88EU_AP_MODE ++ free_mlme_ap_info(if1); ++#endif ++ ++ if (if1->DriverState != DRIVER_DISAPPEAR) { ++ if (pnetdev) { ++ /* will call netdev_close() */ ++ unregister_netdev(pnetdev); ++ rtw_proc_remove_one(pnetdev); ++ } ++ } ++ rtw_cancel_all_timer(if1); ++ ++ rtw_dev_unload(if1); ++ DBG_88E("+r871xu_dev_remove, hw_init_completed=%d\n", ++ if1->hw_init_completed); ++ rtw_handle_dualmac(if1, 0); ++ rtw_free_drv_sw(if1); ++ if (pnetdev) ++ rtw_free_netdev(pnetdev); ++} ++ ++static int rtw_drv_init(struct usb_interface *pusb_intf, const struct usb_device_id *pdid) ++{ ++ struct adapter *if1 = NULL; ++ int status; ++ struct dvobj_priv *dvobj; ++ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+rtw_drv_init\n")); ++ ++ /* step 0. */ ++ process_spec_devid(pdid); ++ ++ /* Initialize dvobj_priv */ ++ dvobj = usb_dvobj_init(pusb_intf); ++ if (dvobj == NULL) { ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ++ ("initialize device object priv Failed!\n")); ++ goto exit; ++ } ++ ++ if1 = rtw_usb_if1_init(dvobj, pusb_intf, pdid); ++ if (if1 == NULL) { ++ DBG_88E("rtw_init_primarystruct adapter Failed!\n"); ++ goto free_dvobj; ++ } ++ ++ if (ui_pid[1] != 0) { ++ DBG_88E("ui_pid[1]:%d\n", ui_pid[1]); ++ rtw_signal_process(ui_pid[1], SIGUSR2); ++ } ++ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-871x_drv - drv_init, success!\n")); ++ ++ status = _SUCCESS; ++ ++ if (status != _SUCCESS && if1) ++ rtw_usb_if1_deinit(if1); ++free_dvobj: ++ if (status != _SUCCESS) ++ usb_dvobj_deinit(pusb_intf); ++exit: ++ return status == _SUCCESS ? 0 : -ENODEV; ++} ++ ++/* ++ * dev_remove() - our device is being removed ++*/ ++/* rmmod module & unplug(SurpriseRemoved) will call r871xu_dev_remove() => how to recognize both */ ++static void rtw_dev_remove(struct usb_interface *pusb_intf) ++{ ++ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf); ++ struct adapter *padapter = dvobj->if1; ++ ++ DBG_88E("+rtw_dev_remove\n"); ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+dev_remove()\n")); ++ ++ if (usb_drv->drv_registered) ++ padapter->bSurpriseRemoved = true; ++ ++ rtw_pm_set_ips(padapter, IPS_NONE); ++ rtw_pm_set_lps(padapter, PS_MODE_ACTIVE); ++ ++ LeaveAllPowerSaveMode(padapter); ++ ++ rtw_usb_if1_deinit(padapter); ++ ++ usb_dvobj_deinit(pusb_intf); ++ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-dev_remove()\n")); ++ DBG_88E("-r871xu_dev_remove, done\n"); ++ ++ return; ++} ++ ++static int __init rtw_drv_entry(void) ++{ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+rtw_drv_entry\n")); ++ ++ DBG_88E(DRV_NAME " driver version=%s\n", DRIVERVERSION); ++ ++ rtw_suspend_lock_init(); ++ ++ _rtw_mutex_init(&usb_drv->hw_init_mutex); ++ ++ usb_drv->drv_registered = true; ++ return usb_register(&usb_drv->usbdrv); ++} ++ ++static void __exit rtw_drv_halt(void) ++{ ++ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+rtw_drv_halt\n")); ++ DBG_88E("+rtw_drv_halt\n"); ++ ++ rtw_suspend_lock_uninit(); ++ ++ usb_drv->drv_registered = false; ++ usb_deregister(&usb_drv->usbdrv); ++ ++ _rtw_mutex_free(&usb_drv->hw_init_mutex); ++ DBG_88E("-rtw_drv_halt\n"); ++} ++ ++module_init(rtw_drv_entry); ++module_exit(rtw_drv_halt); +diff --git a/drivers/staging/r8188eu/os_dep/usb_ops_linux.c b/drivers/staging/r8188eu/os_dep/usb_ops_linux.c +new file mode 100644 +index 000000000000..9cce79d412b9 +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/usb_ops_linux.c +@@ -0,0 +1,283 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ ******************************************************************************/ ++#define _USB_OPS_LINUX_C_ ++ ++#include ++#include ++#include ++ ++unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr) ++{ ++ unsigned int pipe = 0, ep_num = 0; ++ struct usb_device *pusbd = pdvobj->pusbdev; ++ ++ if (addr == RECV_BULK_IN_ADDR) { ++ pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[0]); ++ } else if (addr == RECV_INT_IN_ADDR) { ++ pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[1]); ++ } else if (addr < HW_QUEUE_ENTRY) { ++ ep_num = pdvobj->Queue2Pipe[addr]; ++ pipe = usb_sndbulkpipe(pusbd, ep_num); ++ } ++ ++ return pipe; ++} ++ ++struct zero_bulkout_context { ++ void *pbuf; ++ void *purb; ++ void *pirp; ++ void *padapter; ++}; ++ ++void usb_read_mem(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem) ++{ ++} ++ ++void usb_write_mem(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem) ++{ ++} ++ ++void usb_read_port_cancel(struct intf_hdl *pintfhdl) ++{ ++ int i; ++ struct recv_buf *precvbuf; ++ struct adapter *padapter = pintfhdl->padapter; ++ precvbuf = (struct recv_buf *)padapter->recvpriv.precv_buf; ++ ++ DBG_88E("%s\n", __func__); ++ ++ padapter->bReadPortCancel = true; ++ ++ for (i = 0; i < NR_RECVBUFF; i++) { ++ precvbuf->reuse = true; ++ if (precvbuf->purb) ++ usb_kill_urb(precvbuf->purb); ++ precvbuf++; ++ } ++} ++ ++static void usb_write_port_complete(struct urb *purb, struct pt_regs *regs) ++{ ++ struct xmit_buf *pxmitbuf = (struct xmit_buf *)purb->context; ++ struct adapter *padapter = pxmitbuf->padapter; ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ struct hal_data_8188e *haldata; ++ ++ switch (pxmitbuf->flags) { ++ case VO_QUEUE_INX: ++ pxmitpriv->voq_cnt--; ++ break; ++ case VI_QUEUE_INX: ++ pxmitpriv->viq_cnt--; ++ break; ++ case BE_QUEUE_INX: ++ pxmitpriv->beq_cnt--; ++ break; ++ case BK_QUEUE_INX: ++ pxmitpriv->bkq_cnt--; ++ break; ++ case HIGH_QUEUE_INX: ++#ifdef CONFIG_88EU_AP_MODE ++ rtw_chk_hi_queue_cmd(padapter); ++#endif ++ break; ++ default: ++ break; ++ } ++ ++ if (padapter->bSurpriseRemoved || padapter->bDriverStopped || ++ padapter->bWritePortCancel) { ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ++ ("usb_write_port_complete:bDriverStopped(%d) OR bSurpriseRemoved(%d)", ++ padapter->bDriverStopped, padapter->bSurpriseRemoved)); ++ DBG_88E("%s(): TX Warning! bDriverStopped(%d) OR bSurpriseRemoved(%d) bWritePortCancel(%d) pxmitbuf->ext_tag(%x)\n", ++ __func__, padapter->bDriverStopped, ++ padapter->bSurpriseRemoved, padapter->bReadPortCancel, ++ pxmitbuf->ext_tag); ++ ++ goto check_completion; ++ } ++ ++ if (purb->status) { ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete : purb->status(%d) != 0\n", purb->status)); ++ DBG_88E("###=> urb_write_port_complete status(%d)\n", purb->status); ++ if ((purb->status == -EPIPE) || (purb->status == -EPROTO)) { ++ sreset_set_wifi_error_status(padapter, USB_WRITE_PORT_FAIL); ++ } else if (purb->status == -EINPROGRESS) { ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete: EINPROGESS\n")); ++ goto check_completion; ++ } else if (purb->status == -ENOENT) { ++ DBG_88E("%s: -ENOENT\n", __func__); ++ goto check_completion; ++ } else if (purb->status == -ECONNRESET) { ++ DBG_88E("%s: -ECONNRESET\n", __func__); ++ goto check_completion; ++ } else if (purb->status == -ESHUTDOWN) { ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete: ESHUTDOWN\n")); ++ padapter->bDriverStopped = true; ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete:bDriverStopped = true\n")); ++ goto check_completion; ++ } else { ++ padapter->bSurpriseRemoved = true; ++ DBG_88E("bSurpriseRemoved = true\n"); ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete:bSurpriseRemoved = true\n")); ++ ++ goto check_completion; ++ } ++ } ++ ++ haldata = GET_HAL_DATA(padapter); ++ haldata->srestpriv.last_tx_complete_time = jiffies; ++ ++check_completion: ++ rtw_sctx_done_err(&pxmitbuf->sctx, ++ purb->status ? RTW_SCTX_DONE_WRITE_PORT_ERR : ++ RTW_SCTX_DONE_SUCCESS); ++ ++ rtw_free_xmitbuf(pxmitpriv, pxmitbuf); ++ ++ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet); ++ ++} ++ ++u32 usb_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem) ++{ ++ unsigned long irqL; ++ unsigned int pipe; ++ int status; ++ u32 ret = _FAIL; ++ struct urb *purb = NULL; ++ struct adapter *padapter = (struct adapter *)pintfhdl->padapter; ++ struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter); ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ struct xmit_buf *pxmitbuf = (struct xmit_buf *)wmem; ++ struct xmit_frame *pxmitframe = (struct xmit_frame *)pxmitbuf->priv_data; ++ struct usb_device *pusbd = pdvobj->pusbdev; ++ ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("+usb_write_port\n")); ++ ++ if ((padapter->bDriverStopped) || (padapter->bSurpriseRemoved) || ++ (padapter->pwrctrlpriv.pnp_bstop_trx)) { ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ++ ("usb_write_port:( padapter->bDriverStopped ||padapter->bSurpriseRemoved ||adapter->pwrctrlpriv.pnp_bstop_trx)!!!\n")); ++ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_TX_DENY); ++ goto exit; ++ } ++ ++ spin_lock_irqsave(&pxmitpriv->lock, irqL); ++ ++ switch (addr) { ++ case VO_QUEUE_INX: ++ pxmitpriv->voq_cnt++; ++ pxmitbuf->flags = VO_QUEUE_INX; ++ break; ++ case VI_QUEUE_INX: ++ pxmitpriv->viq_cnt++; ++ pxmitbuf->flags = VI_QUEUE_INX; ++ break; ++ case BE_QUEUE_INX: ++ pxmitpriv->beq_cnt++; ++ pxmitbuf->flags = BE_QUEUE_INX; ++ break; ++ case BK_QUEUE_INX: ++ pxmitpriv->bkq_cnt++; ++ pxmitbuf->flags = BK_QUEUE_INX; ++ break; ++ case HIGH_QUEUE_INX: ++ pxmitbuf->flags = HIGH_QUEUE_INX; ++ break; ++ default: ++ pxmitbuf->flags = MGT_QUEUE_INX; ++ break; ++ } ++ ++ spin_unlock_irqrestore(&pxmitpriv->lock, irqL); ++ ++ purb = pxmitbuf->pxmit_urb[0]; ++ ++ /* translate DMA FIFO addr to pipehandle */ ++ pipe = ffaddr2pipehdl(pdvobj, addr); ++ ++ usb_fill_bulk_urb(purb, pusbd, pipe, ++ pxmitframe->buf_addr, /* pxmitbuf->pbuf */ ++ cnt, ++ usb_write_port_complete, ++ pxmitbuf);/* context is pxmitbuf */ ++ ++ status = usb_submit_urb(purb, GFP_ATOMIC); ++ if (!status) { ++ struct hal_data_8188e *haldata = GET_HAL_DATA(padapter); ++ ++ haldata->srestpriv.last_tx_time = jiffies; ++ } else { ++ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_WRITE_PORT_ERR); ++ DBG_88E("usb_write_port, status =%d\n", status); ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port(): usb_submit_urb, status =%x\n", status)); ++ ++ switch (status) { ++ case -ENODEV: ++ padapter->bDriverStopped = true; ++ break; ++ default: ++ break; ++ } ++ goto exit; ++ } ++ ++ ret = _SUCCESS; ++ ++/* We add the URB_ZERO_PACKET flag to urb so that the host will send the zero packet automatically. */ ++ ++ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("-usb_write_port\n")); ++ ++exit: ++ if (ret != _SUCCESS) ++ rtw_free_xmitbuf(pxmitpriv, pxmitbuf); ++ ++ return ret; ++} ++ ++void usb_write_port_cancel(struct intf_hdl *pintfhdl) ++{ ++ int i, j; ++ struct adapter *padapter = pintfhdl->padapter; ++ struct xmit_buf *pxmitbuf = (struct xmit_buf *)padapter->xmitpriv.pxmitbuf; ++ ++ DBG_88E("%s\n", __func__); ++ ++ padapter->bWritePortCancel = true; ++ ++ for (i = 0; i < NR_XMITBUFF; i++) { ++ for (j = 0; j < 8; j++) { ++ if (pxmitbuf->pxmit_urb[j]) ++ usb_kill_urb(pxmitbuf->pxmit_urb[j]); ++ } ++ pxmitbuf++; ++ } ++ ++ pxmitbuf = (struct xmit_buf *)padapter->xmitpriv.pxmit_extbuf; ++ for (i = 0; i < NR_XMIT_EXTBUFF; i++) { ++ for (j = 0; j < 8; j++) { ++ if (pxmitbuf->pxmit_urb[j]) ++ usb_kill_urb(pxmitbuf->pxmit_urb[j]); ++ } ++ pxmitbuf++; ++ } ++} +diff --git a/drivers/staging/r8188eu/os_dep/xmit_linux.c b/drivers/staging/r8188eu/os_dep/xmit_linux.c +new file mode 100644 +index 000000000000..c6c5b2aabefb +--- /dev/null ++++ b/drivers/staging/r8188eu/os_dep/xmit_linux.c +@@ -0,0 +1,282 @@ ++/****************************************************************************** ++ * ++ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA ++ * ++ * ++ ******************************************************************************/ ++#define _XMIT_OSDEP_C_ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++uint rtw_remainder_len(struct pkt_file *pfile) ++{ ++ return pfile->buf_len - ((size_t)(pfile->cur_addr) - ++ (size_t)(pfile->buf_start)); ++} ++ ++void _rtw_open_pktfile(struct sk_buff *pktptr, struct pkt_file *pfile) ++{ ++ ++ if (!pktptr) { ++ pr_err("8188eu: pktptr is NULL\n"); ++ return; ++ } ++ if (!pfile) { ++ pr_err("8188eu: pfile is NULL\n"); ++ return; ++ } ++ pfile->pkt = pktptr; ++ pfile->cur_addr = pktptr->data; ++ pfile->buf_start = pktptr->data; ++ pfile->pkt_len = pktptr->len; ++ pfile->buf_len = pktptr->len; ++ ++ pfile->cur_buffer = pfile->buf_start; ++ ++} ++ ++uint _rtw_pktfile_read (struct pkt_file *pfile, u8 *rmem, uint rlen) ++{ ++ uint len = 0; ++ ++ len = rtw_remainder_len(pfile); ++ len = (rlen > len) ? len : rlen; ++ ++ if (rmem) ++ skb_copy_bits(pfile->pkt, pfile->buf_len-pfile->pkt_len, rmem, len); ++ ++ pfile->cur_addr += len; ++ pfile->pkt_len -= len; ++ ++ return len; ++} ++ ++int rtw_endofpktfile(struct pkt_file *pfile) ++{ ++ ++ if (pfile->pkt_len == 0) { ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++void rtw_set_tx_chksum_offload(struct sk_buff *pkt, struct pkt_attrib *pattrib) ++{ ++} ++ ++int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz) ++{ ++ int i; ++ ++ pxmitbuf->pallocated_buf = rtw_zmalloc(alloc_sz); ++ if (pxmitbuf->pallocated_buf == NULL) ++ return _FAIL; ++ ++ pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ); ++ pxmitbuf->dma_transfer_addr = 0; ++ ++ for (i = 0; i < 8; i++) { ++ pxmitbuf->pxmit_urb[i] = usb_alloc_urb(0, GFP_KERNEL); ++ if (pxmitbuf->pxmit_urb[i] == NULL) { ++ DBG_88E("pxmitbuf->pxmit_urb[i]==NULL"); ++ return _FAIL; ++ } ++ } ++ return _SUCCESS; ++} ++ ++void rtw_os_xmit_resource_free(struct adapter *padapter, ++ struct xmit_buf *pxmitbuf, u32 free_sz) ++{ ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ usb_free_urb(pxmitbuf->pxmit_urb[i]); ++ ++ kfree(pxmitbuf->pallocated_buf); ++} ++ ++#define WMM_XMIT_THRESHOLD (NR_XMITFRAME*2/5) ++ ++void rtw_os_pkt_complete(struct adapter *padapter, struct sk_buff *pkt) ++{ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) ++ u16 queue; ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ ++ queue = skb_get_queue_mapping(pkt); ++ if (padapter->registrypriv.wifi_spec) { ++ if (__netif_subqueue_stopped(padapter->pnetdev, queue) && ++ (pxmitpriv->hwxmits[queue].accnt < WMM_XMIT_THRESHOLD)) ++ netif_wake_subqueue(padapter->pnetdev, queue); ++ } else { ++ if (__netif_subqueue_stopped(padapter->pnetdev, queue)) ++ netif_wake_subqueue(padapter->pnetdev, queue); ++ } ++#else ++ if (netif_queue_stopped(padapter->pnetdev)) ++ netif_wake_queue(padapter->pnetdev); ++#endif ++ ++ dev_kfree_skb_any(pkt); ++} ++ ++void rtw_os_xmit_complete(struct adapter *padapter, struct xmit_frame *pxframe) ++{ ++ if (pxframe->pkt) ++ rtw_os_pkt_complete(padapter, pxframe->pkt); ++ pxframe->pkt = NULL; ++} ++ ++void rtw_os_xmit_schedule(struct adapter *padapter) ++{ ++ struct xmit_priv *pxmitpriv; ++ ++ if (!padapter) ++ return; ++ ++ pxmitpriv = &padapter->xmitpriv; ++ ++ spin_lock_bh(&pxmitpriv->lock); ++ ++ if (rtw_txframes_pending(padapter)) ++ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet); ++ ++ spin_unlock_bh(&pxmitpriv->lock); ++} ++ ++static void rtw_check_xmit_resource(struct adapter *padapter, struct sk_buff *pkt) ++{ ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ u16 queue; ++ ++ queue = skb_get_queue_mapping(pkt); ++ if (padapter->registrypriv.wifi_spec) { ++ /* No free space for Tx, tx_worker is too slow */ ++ if (pxmitpriv->hwxmits[queue].accnt > WMM_XMIT_THRESHOLD) ++ netif_stop_subqueue(padapter->pnetdev, queue); ++ } else { ++ if (pxmitpriv->free_xmitframe_cnt <= 4) { ++ if (!netif_tx_queue_stopped(netdev_get_tx_queue(padapter->pnetdev, queue))) ++ netif_stop_subqueue(padapter->pnetdev, queue); ++ } ++ } ++} ++ ++static int rtw_mlcst2unicst(struct adapter *padapter, struct sk_buff *skb) ++{ ++ struct sta_priv *pstapriv = &padapter->stapriv; ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ struct list_head *phead, *plist; ++ struct sk_buff *newskb; ++ struct sta_info *psta = NULL; ++ s32 res; ++ ++ spin_lock_bh(&pstapriv->asoc_list_lock); ++ phead = &pstapriv->asoc_list; ++ plist = phead->next; ++ ++ /* free sta asoc_queue */ ++ while (phead != plist) { ++ psta = container_of(plist, struct sta_info, asoc_list); ++ ++ plist = plist->next; ++ ++ /* avoid come from STA1 and send back STA1 */ ++ if (!memcmp(psta->hwaddr, &skb->data[6], 6)) ++ continue; ++ ++ newskb = skb_copy(skb, GFP_ATOMIC); ++ ++ if (newskb) { ++ memcpy(newskb->data, psta->hwaddr, 6); ++ res = rtw_xmit(padapter, &newskb); ++ if (res < 0) { ++ DBG_88E("%s()-%d: rtw_xmit() return error!\n", __func__, __LINE__); ++ pxmitpriv->tx_drop++; ++ dev_kfree_skb_any(newskb); ++ } else { ++ pxmitpriv->tx_pkts++; ++ } ++ } else { ++ DBG_88E("%s-%d: skb_copy() failed!\n", __func__, __LINE__); ++ pxmitpriv->tx_drop++; ++ ++ spin_unlock_bh(&pstapriv->asoc_list_lock); ++ return false; /* Caller shall tx this multicast frame via normal way. */ ++ } ++ } ++ ++ spin_unlock_bh(&pstapriv->asoc_list_lock); ++ dev_kfree_skb_any(skb); ++ return true; ++} ++ ++int rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetdev) ++{ ++ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev); ++ struct xmit_priv *pxmitpriv = &padapter->xmitpriv; ++ struct mlme_priv *pmlmepriv = &padapter->mlmepriv; ++ s32 res = 0; ++ ++ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("+xmit_enry\n")); ++ ++ if (rtw_if_up(padapter) == false) { ++ RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit_entry: rtw_if_up fail\n")); ++ goto drop_packet; ++ } ++ ++ rtw_check_xmit_resource(padapter, pkt); ++ ++ if (!rtw_mc2u_disable && check_fwstate(pmlmepriv, WIFI_AP_STATE) && ++ (IP_MCAST_MAC(pkt->data) || ICMPV6_MCAST_MAC(pkt->data)) && ++ (padapter->registrypriv.wifi_spec == 0)) { ++ if (pxmitpriv->free_xmitframe_cnt > (NR_XMITFRAME/4)) { ++ res = rtw_mlcst2unicst(padapter, pkt); ++ if (res) ++ goto exit; ++ } ++ } ++ ++ res = rtw_xmit(padapter, &pkt); ++ if (res < 0) ++ goto drop_packet; ++ ++ pxmitpriv->tx_pkts++; ++ RT_TRACE(_module_xmit_osdep_c_, _drv_info_, ("rtw_xmit_entry: tx_pkts=%d\n", (u32)pxmitpriv->tx_pkts)); ++ goto exit; ++ ++drop_packet: ++ pxmitpriv->tx_drop++; ++ dev_kfree_skb_any(pkt); ++ RT_TRACE(_module_xmit_osdep_c_, _drv_notice_, ("rtw_xmit_entry: drop, tx_drop=%d\n", (u32)pxmitpriv->tx_drop)); ++ ++exit: ++ ++ return 0; ++} +-- +2.35.1 + diff --git a/queue-5.10/staging-r8188eu-remove-support-for-devices-with-8188.patch b/queue-5.10/staging-r8188eu-remove-support-for-devices-with-8188.patch new file mode 100644 index 00000000000..ec1c52cb893 --- /dev/null +++ b/queue-5.10/staging-r8188eu-remove-support-for-devices-with-8188.patch @@ -0,0 +1,44 @@ +From e278f2899054cd231837b57c36a5efce3da37ca9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Nov 2021 23:37:34 +0700 +Subject: staging: r8188eu: Remove support for devices with 8188FU chipset + (0bda:f179) + +From: Candy Febriyanto + +[ Upstream commit 6723b283c44a3fdf9f922ae9788aab38bd909211 ] + +The new r8188eu driver doesn't actually support devices with vendor ID 0bda +and product ID f179[0][1][2], remove the ID so owners of these devices +don't have to blacklist the staging driver. + +[0] https://github.com/lwfinger/rtl8188eu/issues/366#issuecomment-888511731 +[1] https://github.com/lwfinger/rtl8188eu/issues/385 +[2] https://github.com/lwfinger/rtl8188eu/issues/385#issuecomment-973013539 + +Cc: Larry Finger +CC: Phillip Potter +Signed-off-by: Candy Febriyanto +Link: https://lore.kernel.org/r/YZaBTq9vlMaJDFz2@mainframe.localdomain +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: e01f5c8d6af2 ("staging: r8188eu: Add Rosewill USB-N150 Nano to device tables") +Signed-off-by: Sasha Levin +--- + drivers/staging/r8188eu/os_dep/usb_intf.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/staging/r8188eu/os_dep/usb_intf.c b/drivers/staging/r8188eu/os_dep/usb_intf.c +index fd16afa1de33..f708c902c819 100644 +--- a/drivers/staging/r8188eu/os_dep/usb_intf.c ++++ b/drivers/staging/r8188eu/os_dep/usb_intf.c +@@ -49,7 +49,6 @@ static struct usb_device_id rtw_usb_id_tbl[] = { + /*=== Realtek demoboard ===*/ + {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8179)}, /* 8188EUS */ + {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x0179)}, /* 8188ETV */ +- {USB_DEVICE(USB_VENDER_ID_REALTEK, 0xf179)}, /* 8188FU */ + /*=== Customer ID ===*/ + /****** 8188EUS ********/ + {USB_DEVICE(0x07B8, 0x8179)}, /* Abocom - Abocom */ +-- +2.35.1 + diff --git a/queue-5.10/tty-serial-atmel-preserve-previous-usart-mode-if-rs4.patch b/queue-5.10/tty-serial-atmel-preserve-previous-usart-mode-if-rs4.patch new file mode 100644 index 00000000000..e06fbaa259b --- /dev/null +++ b/queue-5.10/tty-serial-atmel-preserve-previous-usart-mode-if-rs4.patch @@ -0,0 +1,74 @@ +From f6ad96aaaa384194ca2db76e4b5864a21d64a9ff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Aug 2022 17:29:03 +0300 +Subject: tty: serial: atmel: Preserve previous USART mode if RS485 disabled +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Sergiu Moga + +[ Upstream commit 692a8ebcfc24f4a5bea0eb2967e450f584193da6 ] + +Whenever the atmel_rs485_config() driver method would be called, +the USART mode is reset to normal mode before even checking if +RS485 flag is set, thus resulting in losing the previous USART +mode in the case where the checking fails. + +Some tools, such as `linux-serial-test`, lead to the driver calling +this method when doing the setup of the serial port: after setting the +port mode (Hardware Flow Control, Normal Mode, RS485 Mode, etc.), +`linux-serial-test` tries to enable/disable RS485 depending on +the commandline arguments that were passed. + +Example of how this issue could reveal itself: +When doing a serial communication with Hardware Flow Control through +`linux-serial-test`, the tool would lead to the driver roughly doing +the following: +- set the corresponding bit to 1 (ATMEL_US_USMODE_HWHS bit in the +ATMEL_US_MR register) through the atmel_set_termios() to enable +Hardware Flow Control +- disable RS485 through the atmel_config_rs485() method +Thus, when the latter is called, the mode will be reset and the +previously set bit is unset, leaving USART in normal mode instead of +the expected Hardware Flow Control mode. + +This fix ensures that this reset is only done if the checking for +RS485 succeeds and that the previous mode is preserved otherwise. + +Fixes: e8faff7330a35 ("ARM: 6092/1: atmel_serial: support for RS485 communications") +Cc: stable +Reviewed-by: Ilpo Järvinen +Signed-off-by: Sergiu Moga +Link: https://lore.kernel.org/r/20220824142902.502596-1-sergiu.moga@microchip.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/atmel_serial.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c +index e7526060926d..b7872ad3e762 100644 +--- a/drivers/tty/serial/atmel_serial.c ++++ b/drivers/tty/serial/atmel_serial.c +@@ -295,9 +295,6 @@ static int atmel_config_rs485(struct uart_port *port, + + mode = atmel_uart_readl(port, ATMEL_US_MR); + +- /* Resetting serial mode to RS232 (0x0) */ +- mode &= ~ATMEL_US_USMODE; +- + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (rs485conf->flags & SER_RS485_RX_DURING_TX) +@@ -307,6 +304,7 @@ static int atmel_config_rs485(struct uart_port *port, + + atmel_uart_writel(port, ATMEL_US_TTGR, + rs485conf->delay_rts_after_send); ++ mode &= ~ATMEL_US_USMODE; + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); +-- +2.35.1 + diff --git a/queue-5.10/usb-add-quirks-for-lenovo-onelink-dock.patch b/queue-5.10/usb-add-quirks-for-lenovo-onelink-dock.patch new file mode 100644 index 00000000000..f71b5ed6249 --- /dev/null +++ b/queue-5.10/usb-add-quirks-for-lenovo-onelink-dock.patch @@ -0,0 +1,51 @@ +From 2106ab3e2506deb40cf604ba28436dd0fa81a5ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Aug 2022 21:13:21 +0200 +Subject: usb: add quirks for Lenovo OneLink+ Dock + +From: Jean-Francois Le Fillatre + +[ Upstream commit 3d5f70949f1b1168fbb17d06eb5c57e984c56c58 ] + +The Lenovo OneLink+ Dock contains two VL812 USB3.0 controllers: +17ef:1018 upstream +17ef:1019 downstream + +Those two controllers both have problems with some USB3.0 devices, +particularly self-powered ones. Typical error messages include: + + Timeout while waiting for setup device command + device not accepting address X, error -62 + unable to enumerate USB device + +By process of elimination the controllers themselves were identified as +the cause of the problem. Through trial and error the issue was solved +by using USB_QUIRK_RESET_RESUME for both chips. + +Signed-off-by: Jean-Francois Le Fillatre +Cc: stable +Link: https://lore.kernel.org/r/20220824191320.17883-1-jflf_kernel@gmx.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/core/quirks.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c +index f03ee889ecc7..03473e20e218 100644 +--- a/drivers/usb/core/quirks.c ++++ b/drivers/usb/core/quirks.c +@@ -438,6 +438,10 @@ static const struct usb_device_id usb_quirk_list[] = { + { USB_DEVICE(0x1532, 0x0116), .driver_info = + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + ++ /* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */ ++ { USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME }, ++ { USB_DEVICE(0x17ef, 0x1019), .driver_info = USB_QUIRK_RESET_RESUME }, ++ + /* Lenovo USB-C to Ethernet Adapter RTL8153-04 */ + { USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM }, + +-- +2.35.1 + diff --git a/queue-5.10/usb-cdns3-fix-incorrect-handling-trb_smm-flag-for-is.patch b/queue-5.10/usb-cdns3-fix-incorrect-handling-trb_smm-flag-for-is.patch new file mode 100644 index 00000000000..2e7eff94142 --- /dev/null +++ b/queue-5.10/usb-cdns3-fix-incorrect-handling-trb_smm-flag-for-is.patch @@ -0,0 +1,50 @@ +From 15d796a79456210a064eec2b6ec50916b0fde0a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 25 Aug 2022 08:22:07 +0200 +Subject: usb: cdns3: fix incorrect handling TRB_SMM flag for ISOC transfer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Pawel Laszczak + +[ Upstream commit d5dcc33677d7415c5f23b3c052f9e80cbab9ea4e ] + +The TRB_SMM flag indicates that DMA has completed the TD service with +this TRB. Usually it’s a last TRB in TD. In case of ISOC transfer for +bInterval > 1 each ISOC transfer contains more than one TD associated +with usb request (one TD per ITP). In such case the TRB_SMM flag will +be set in every TD and driver will recognize the end of transfer after +processing the first TD with TRB_SMM. In result driver stops updating +request->actual and returns incorrect actual length. +To fix this issue driver additionally must check TRB_CHAIN which is not +used for isochronous transfers. + +Fixes: 249f0a25e8be ("usb: cdns3: gadget: handle sg list use case at completion correctly") +cc: +Acked-by: Peter Chen +Signed-off-by: Pawel Laszczak +Link: https://lore.kernel.org/r/20220825062207.5824-1-pawell@cadence.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/cdns3/gadget.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c +index c6fc14b169da..d0d4de80680f 100644 +--- a/drivers/usb/cdns3/gadget.c ++++ b/drivers/usb/cdns3/gadget.c +@@ -1531,7 +1531,8 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, + TRB_LEN(le32_to_cpu(trb->length)); + + if (priv_req->num_of_trb > 1 && +- le32_to_cpu(trb->control) & TRB_SMM) ++ le32_to_cpu(trb->control) & TRB_SMM && ++ le32_to_cpu(trb->control) & TRB_CHAIN) + transfer_end = true; + + cdns3_ep_inc_deq(priv_ep); +-- +2.35.1 + diff --git a/queue-5.10/usb-cdns3-fix-issue-with-rearming-iso-out-endpoint.patch b/queue-5.10/usb-cdns3-fix-issue-with-rearming-iso-out-endpoint.patch new file mode 100644 index 00000000000..5978d90f9cc --- /dev/null +++ b/queue-5.10/usb-cdns3-fix-issue-with-rearming-iso-out-endpoint.patch @@ -0,0 +1,42 @@ +From 4918dd1fec79023e236e005473cf8c6340c64808 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 25 Aug 2022 08:21:37 +0200 +Subject: usb: cdns3: fix issue with rearming ISO OUT endpoint + +From: Pawel Laszczak + +[ Upstream commit b46a6b09fa056042a302b181a1941f0056944603 ] + +ISO OUT endpoint is enabled during queuing first usb request +in transfer ring and disabled when TRBERR is reported by controller. +After TRBERR and before next transfer added to TR driver must again +reenable endpoint but does not. +To solve this issue during processing TRBERR event driver must +set the flag EP_UPDATE_EP_TRBADDR in priv_ep->flags field. + +Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") +cc: +Acked-by: Peter Chen +Signed-off-by: Pawel Laszczak +Link: https://lore.kernel.org/r/20220825062137.5766-1-pawell@cadence.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/cdns3/gadget.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c +index d0d4de80680f..e3a8b6c71aa1 100644 +--- a/drivers/usb/cdns3/gadget.c ++++ b/drivers/usb/cdns3/gadget.c +@@ -1692,6 +1692,7 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) + ep_cfg &= ~EP_CFG_ENABLE; + writel(ep_cfg, &priv_dev->regs->ep_cfg); + priv_ep->flags &= ~EP_QUIRK_ISO_OUT_EN; ++ priv_ep->flags |= EP_UPDATE_EP_TRBADDR; + } + cdns3_transfer_completed(priv_dev, priv_ep); + } else if (!(priv_ep->flags & EP_STALLED) && +-- +2.35.1 + diff --git a/queue-5.10/usb-dwc3-gadget-avoid-duplicate-requests-to-enable-r.patch b/queue-5.10/usb-dwc3-gadget-avoid-duplicate-requests-to-enable-r.patch new file mode 100644 index 00000000000..d90ee0203e8 --- /dev/null +++ b/queue-5.10/usb-dwc3-gadget-avoid-duplicate-requests-to-enable-r.patch @@ -0,0 +1,57 @@ +From 5fc6b4961f500778ceceaf1ec89bd9dfe3fde4cc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 27 Jul 2022 19:06:47 -0700 +Subject: usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop + +From: Wesley Cheng + +[ Upstream commit 040f2dbd2010c43f33ad27249e6dac48456f4d99 ] + +Relocate the pullups_connected check until after it is ensured that there +are no runtime PM transitions. If another context triggered the DWC3 +core's runtime resume, it may have already enabled the Run/Stop. Do not +re-run the entire pullup sequence again, as it may issue a core soft +reset while Run/Stop is already set. + +This patch depends on + commit 69e131d1ac4e ("usb: dwc3: gadget: Prevent repeat pullup()") + +Fixes: 77adb8bdf422 ("usb: dwc3: gadget: Allow runtime suspend if UDC unbinded") +Cc: stable +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20220728020647.9377-1-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/dwc3/gadget.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index 818a70e56d89..41ed2f6f8a8d 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2155,9 +2155,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + + is_on = !!is_on; + +- if (dwc->pullups_connected == is_on) +- return 0; +- + dwc->softconnect = is_on; + /* + * Per databook, when we want to stop the gadget, if a control transfer +@@ -2194,6 +2191,11 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + return 0; + } + ++ if (dwc->pullups_connected == is_on) { ++ pm_runtime_put(dwc->dev); ++ return 0; ++ } ++ + if (!is_on) { + ret = dwc3_gadget_soft_disconnect(dwc); + } else { +-- +2.35.1 + diff --git a/queue-5.10/usb-dwc3-gadget-avoid-starting-dwc3-gadget-during-ud.patch b/queue-5.10/usb-dwc3-gadget-avoid-starting-dwc3-gadget-during-ud.patch new file mode 100644 index 00000000000..0276b3904af --- /dev/null +++ b/queue-5.10/usb-dwc3-gadget-avoid-starting-dwc3-gadget-during-ud.patch @@ -0,0 +1,90 @@ +From 0b166dd48d1dbd264075f8daeda224ee299e32a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Sep 2021 19:18:52 -0700 +Subject: usb: dwc3: gadget: Avoid starting DWC3 gadget during UDC unbind + +From: Wesley Cheng + +[ Upstream commit 8217f07a50236779880f13e87f99224cd9117f83 ] + +There is a race present where the DWC3 runtime resume runs in parallel +to the UDC unbind sequence. This will eventually lead to a possible +scenario where we are enabling the run/stop bit, without a valid +composition defined. + +Thread#1 (handling UDC unbind): +usb_gadget_remove_driver() +-->usb_gadget_disconnect() + -->dwc3_gadget_pullup(0) +--> continue UDC unbind sequence +-->Thread#2 is running in parallel here + +Thread#2 (handing next cable connect) +__dwc3_set_mode() + -->pm_runtime_get_sync() + -->dwc3_gadget_resume() + -->dwc->gadget_driver is NOT NULL yet + -->dwc3_gadget_run_stop(1) + --> _dwc3gadget_start() +... + +Fix this by tracking the pullup disable routine, and avoiding resuming +of the DWC3 gadget. Once the UDC is re-binded, that will trigger the +pullup enable routine, which would handle enabling the DWC3 gadget. + +Acked-by: Felipe Balbi +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20210917021852.2037-1-wcheng@codeaurora.org +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 040f2dbd2010 ("usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop") +Signed-off-by: Sasha Levin +--- + drivers/usb/dwc3/core.h | 2 ++ + drivers/usb/dwc3/gadget.c | 4 ++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +index 79e1b82e5e05..1cb1601a6d98 100644 +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -1010,6 +1010,7 @@ struct dwc3_scratchpad_array { + * @tx_max_burst_prd: max periodic ESS transmit burst size + * @hsphy_interface: "utmi" or "ulpi" + * @connected: true when we're connected to a host, false otherwise ++ * @softconnect: true when gadget connect is called, false when disconnect runs + * @delayed_status: true when gadget driver asks for delayed status + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer +@@ -1218,6 +1219,7 @@ struct dwc3 { + const char *hsphy_interface; + + unsigned connected:1; ++ unsigned softconnect:1; + unsigned delayed_status:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index a2a10c05ef3f..85a0159f12ec 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2127,7 +2127,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + int ret; + + is_on = !!is_on; +- ++ dwc->softconnect = is_on; + /* + * Per databook, when we want to stop the gadget, if a control transfer + * is still in process, complete it and get the core into setup phase. +@@ -4048,7 +4048,7 @@ int dwc3_gadget_resume(struct dwc3 *dwc) + { + int ret; + +- if (!dwc->gadget_driver) ++ if (!dwc->gadget_driver || !dwc->softconnect) + return 0; + + ret = __dwc3_gadget_start(dwc); +-- +2.35.1 + diff --git a/queue-5.10/usb-dwc3-gadget-don-t-modify-gevntcount-in-pullup.patch b/queue-5.10/usb-dwc3-gadget-don-t-modify-gevntcount-in-pullup.patch new file mode 100644 index 00000000000..8a0749a3411 --- /dev/null +++ b/queue-5.10/usb-dwc3-gadget-don-t-modify-gevntcount-in-pullup.patch @@ -0,0 +1,111 @@ +From 2f906b6c68e37e4ddb75512dd075cba681655479 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Apr 2022 19:22:44 -0700 +Subject: usb: dwc3: gadget: Don't modify GEVNTCOUNT in pullup() + +From: Thinh Nguyen + +[ Upstream commit 8f8034f493b5eb1ad21ff392fd30c0cf9e71f73f ] + +If the GEVNTCOUNT indicates events in the event buffer, the driver needs +to acknowledge them before the controller can halt. Simply let the +interrupt handler acknowledges the remaining event generated by the +controller while polling for DSTS.DEVCTLHLT. This avoids disabling irq +and taking care of race condition between the interrupt handlers and +pullup(). + +Signed-off-by: Thinh Nguyen +Link: https://lore.kernel.org/r/ea306ec93c41ccafbdb5d16404ff3b6eca299613.1650593829.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 040f2dbd2010 ("usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop") +Signed-off-by: Sasha Levin +--- + drivers/usb/dwc3/gadget.c | 35 ++++++++--------------------------- + 1 file changed, 8 insertions(+), 27 deletions(-) + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index bd1050f75558..818a70e56d89 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2122,8 +2122,9 @@ static int __dwc3_gadget_start(struct dwc3 *dwc); + + static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) + { +- u32 count; ++ unsigned long flags; + ++ spin_lock_irqsave(&dwc->lock, flags); + dwc->connected = false; + + /* +@@ -2135,29 +2136,21 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) + */ + dwc3_stop_active_transfers(dwc); + __dwc3_gadget_stop(dwc); ++ spin_unlock_irqrestore(&dwc->lock, flags); + + /* +- * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a +- * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the +- * "software needs to acknowledge the events that are generated +- * (by writing to GEVNTCOUNTn) while it is waiting for this bit +- * to be set to '1'." ++ * Note: if the GEVNTCOUNT indicates events in the event buffer, the ++ * driver needs to acknowledge them before the controller can halt. ++ * Simply let the interrupt handler acknowledges and handle the ++ * remaining event generated by the controller while polling for ++ * DSTS.DEVCTLHLT. + */ +- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); +- count &= DWC3_GEVNTCOUNT_MASK; +- if (count > 0) { +- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); +- dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) % +- dwc->ev_buf->length; +- } +- + return dwc3_gadget_run_stop(dwc, false, false); + } + + static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + { + struct dwc3 *dwc = gadget_to_dwc(g); +- unsigned long flags; + int ret; + + is_on = !!is_on; +@@ -2201,14 +2194,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + return 0; + } + +- /* +- * Synchronize and disable any further event handling while controller +- * is being enabled/disabled. +- */ +- disable_irq(dwc->irq_gadget); +- +- spin_lock_irqsave(&dwc->lock, flags); +- + if (!is_on) { + ret = dwc3_gadget_soft_disconnect(dwc); + } else { +@@ -2218,16 +2203,12 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + * device-initiated disconnect requires a core soft reset + * (DCTL.CSftRst) before enabling the run/stop bit. + */ +- spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_core_soft_reset(dwc); +- spin_lock_irqsave(&dwc->lock, flags); + + dwc3_event_buffers_setup(dwc); + __dwc3_gadget_start(dwc); + ret = dwc3_gadget_run_stop(dwc, true, false); + } +- spin_unlock_irqrestore(&dwc->lock, flags); +- enable_irq(dwc->irq_gadget); + + pm_runtime_put(dwc->dev); + +-- +2.35.1 + diff --git a/queue-5.10/usb-dwc3-gadget-prevent-repeat-pullup.patch b/queue-5.10/usb-dwc3-gadget-prevent-repeat-pullup.patch new file mode 100644 index 00000000000..aaa08a3ae18 --- /dev/null +++ b/queue-5.10/usb-dwc3-gadget-prevent-repeat-pullup.patch @@ -0,0 +1,45 @@ +From b30e072d8ab7471a25c0e7191911a2775c227ec7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Apr 2022 19:22:31 -0700 +Subject: usb: dwc3: gadget: Prevent repeat pullup() + +From: Thinh Nguyen + +[ Upstream commit 69e131d1ac4e52a59ec181ab4f8aa8c48cd8fb64 ] + +Don't do soft-disconnect if it's previously done. Likewise, don't do +soft-connect if the device is currently connected and running. It would +break normal operation. + +Currently the caller of pullup() (udc's sysfs soft_connect) only checks +if it had initiated disconnect to prevent repeating soft-disconnect. It +doesn't check for soft-connect. To be safe, let's keep the check here +regardless whether the udc core is fixed. + +Signed-off-by: Thinh Nguyen +Link: https://lore.kernel.org/r/1c1345bd66c97a9d32f77d63aaadd04b7b037143.1650593829.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 040f2dbd2010 ("usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop") +Signed-off-by: Sasha Levin +--- + drivers/usb/dwc3/gadget.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index e7ede868ffb3..3820dff0387a 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2127,6 +2127,10 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + int ret; + + is_on = !!is_on; ++ ++ if (dwc->pullups_connected == is_on) ++ return 0; ++ + dwc->softconnect = is_on; + /* + * Per databook, when we want to stop the gadget, if a control transfer +-- +2.35.1 + diff --git a/queue-5.10/usb-dwc3-gadget-refactor-pullup.patch b/queue-5.10/usb-dwc3-gadget-refactor-pullup.patch new file mode 100644 index 00000000000..c263cb4a563 --- /dev/null +++ b/queue-5.10/usb-dwc3-gadget-refactor-pullup.patch @@ -0,0 +1,115 @@ +From 3731feb8a3376f496d94f298da8fcd04a5e1e920 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Apr 2022 19:22:38 -0700 +Subject: usb: dwc3: gadget: Refactor pullup() + +From: Thinh Nguyen + +[ Upstream commit 861c010a2ee1bc4a66d23f0da4aa22e75d8eaa24 ] + +Move soft-disconnect sequence out of dwc3_gadget_pullup(). No +functional change here. + +Signed-off-by: Thinh Nguyen +Link: https://lore.kernel.org/r/4c0f259b17d95acaaa931f90276683a48a32fe22.1650593829.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 040f2dbd2010 ("usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop") +Signed-off-by: Sasha Levin +--- + drivers/usb/dwc3/gadget.c | 65 ++++++++++++++++++++++----------------- + 1 file changed, 36 insertions(+), 29 deletions(-) + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index 3820dff0387a..bd1050f75558 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2120,6 +2120,40 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc); + static void __dwc3_gadget_stop(struct dwc3 *dwc); + static int __dwc3_gadget_start(struct dwc3 *dwc); + ++static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) ++{ ++ u32 count; ++ ++ dwc->connected = false; ++ ++ /* ++ * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a ++ * Section 4.1.8 Table 4-7, it states that for a device-initiated ++ * disconnect, the SW needs to ensure that it sends "a DEPENDXFER ++ * command for any active transfers" before clearing the RunStop ++ * bit. ++ */ ++ dwc3_stop_active_transfers(dwc); ++ __dwc3_gadget_stop(dwc); ++ ++ /* ++ * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a ++ * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the ++ * "software needs to acknowledge the events that are generated ++ * (by writing to GEVNTCOUNTn) while it is waiting for this bit ++ * to be set to '1'." ++ */ ++ count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); ++ count &= DWC3_GEVNTCOUNT_MASK; ++ if (count > 0) { ++ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); ++ dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) % ++ dwc->ev_buf->length; ++ } ++ ++ return dwc3_gadget_run_stop(dwc, false, false); ++} ++ + static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + { + struct dwc3 *dwc = gadget_to_dwc(g); +@@ -2176,33 +2210,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + spin_lock_irqsave(&dwc->lock, flags); + + if (!is_on) { +- u32 count; +- +- dwc->connected = false; +- /* +- * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a +- * Section 4.1.8 Table 4-7, it states that for a device-initiated +- * disconnect, the SW needs to ensure that it sends "a DEPENDXFER +- * command for any active transfers" before clearing the RunStop +- * bit. +- */ +- dwc3_stop_active_transfers(dwc); +- __dwc3_gadget_stop(dwc); +- +- /* +- * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a +- * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the +- * "software needs to acknowledge the events that are generated +- * (by writing to GEVNTCOUNTn) while it is waiting for this bit +- * to be set to '1'." +- */ +- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); +- count &= DWC3_GEVNTCOUNT_MASK; +- if (count > 0) { +- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); +- dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) % +- dwc->ev_buf->length; +- } ++ ret = dwc3_gadget_soft_disconnect(dwc); + } else { + /* + * In the Synopsys DWC_usb31 1.90a programming guide section +@@ -2216,9 +2224,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + + dwc3_event_buffers_setup(dwc); + __dwc3_gadget_start(dwc); ++ ret = dwc3_gadget_run_stop(dwc, true, false); + } +- +- ret = dwc3_gadget_run_stop(dwc, is_on, false); + spin_unlock_irqrestore(&dwc->lock, flags); + enable_irq(dwc->irq_gadget); + +-- +2.35.1 + diff --git a/queue-5.10/usb-dwc3-issue-core-soft-reset-before-enabling-run-s.patch b/queue-5.10/usb-dwc3-issue-core-soft-reset-before-enabling-run-s.patch new file mode 100644 index 00000000000..8636e5ba7f4 --- /dev/null +++ b/queue-5.10/usb-dwc3-issue-core-soft-reset-before-enabling-run-s.patch @@ -0,0 +1,86 @@ +From 53c9b87410d189a712074bcb9963e4cc073b92f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 15 Mar 2022 18:13:58 -0700 +Subject: usb: dwc3: Issue core soft reset before enabling run/stop + +From: Wesley Cheng + +[ Upstream commit 0066472de157439d58454f4a55786f1045ea5681 ] + +It is recommended by the Synopsis databook to issue a DCTL.CSftReset +when reconnecting from a device-initiated disconnect routine. This +resolves issues with enumeration during fast composition switching +cases, which result in an unknown device on the host. + +Reviewed-by: Thinh Nguyen +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20220316011358.3057-1-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 040f2dbd2010 ("usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop") +Signed-off-by: Sasha Levin +--- + drivers/usb/dwc3/core.c | 4 +--- + drivers/usb/dwc3/core.h | 2 ++ + drivers/usb/dwc3/gadget.c | 11 +++++++++++ + 3 files changed, 14 insertions(+), 3 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 5aae7504f78a..4a0eec176511 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -114,8 +114,6 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) + dwc->current_dr_role = mode; + } + +-static int dwc3_core_soft_reset(struct dwc3 *dwc); +- + static void __dwc3_set_mode(struct work_struct *work) + { + struct dwc3 *dwc = work_to_dwc(work); +@@ -265,7 +263,7 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) + * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +-static int dwc3_core_soft_reset(struct dwc3 *dwc) ++int dwc3_core_soft_reset(struct dwc3 *dwc) + { + u32 reg; + int retries = 1000; +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +index 1cb1601a6d98..cbebe541f7e8 100644 +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -1458,6 +1458,8 @@ bool dwc3_has_imod(struct dwc3 *dwc); + int dwc3_event_buffers_setup(struct dwc3 *dwc); + void dwc3_event_buffers_cleanup(struct dwc3 *dwc); + ++int dwc3_core_soft_reset(struct dwc3 *dwc); ++ + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) + int dwc3_host_init(struct dwc3 *dwc); + void dwc3_host_exit(struct dwc3 *dwc); +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index 85a0159f12ec..e7ede868ffb3 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -2200,6 +2200,17 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + dwc->ev_buf->length; + } + } else { ++ /* ++ * In the Synopsys DWC_usb31 1.90a programming guide section ++ * 4.1.9, it specifies that for a reconnect after a ++ * device-initiated disconnect requires a core soft reset ++ * (DCTL.CSftRst) before enabling the run/stop bit. ++ */ ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ dwc3_core_soft_reset(dwc); ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ dwc3_event_buffers_setup(dwc); + __dwc3_gadget_start(dwc); + } + +-- +2.35.1 + diff --git a/queue-5.10/usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch b/queue-5.10/usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch new file mode 100644 index 00000000000..cc0e4cd72ee --- /dev/null +++ b/queue-5.10/usb-gadget-udc-xilinx-replace-memcpy-with-memcpy_toi.patch @@ -0,0 +1,156 @@ +From afa9bb5ccab37a681d399ca5b1047e1a43fefb77 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 24 Aug 2022 12:42:53 +0530 +Subject: usb: gadget: udc-xilinx: replace memcpy with memcpy_toio + +From: Piyush Mehta + +[ Upstream commit 8cb339f1c1f04baede9d54c1e40ac96247a6393b ] + +For ARM processor, unaligned access to device memory is not allowed. +Method memcpy does not take care of alignment. + +USB detection failure with the unaligned address of memory access, with +below kernel crash. To fix the unaligned address the kernel panic issue, +replace memcpy with memcpy_toio method. + +Kernel crash: +Unable to handle kernel paging request at virtual address ffff80000c05008a +Mem abort info: + ESR = 0x96000061 + EC = 0x25: DABT (current EL), IL = 32 bits + SET = 0, FnV = 0 + EA = 0, S1PTW = 0 + FSC = 0x21: alignment fault +Data abort info: + ISV = 0, ISS = 0x00000061 + CM = 0, WnR = 1 +swapper pgtable: 4k pages, 48-bit VAs, pgdp=000000000143b000 +[ffff80000c05008a] pgd=100000087ffff003, p4d=100000087ffff003, +pud=100000087fffe003, pmd=1000000800bcc003, pte=00680000a0010713 +Internal error: Oops: 96000061 [#1] SMP +Modules linked in: +CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.15.19-xilinx-v2022.1 #1 +Hardware name: ZynqMP ZCU102 Rev1.0 (DT) +pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) +pc : __memcpy+0x30/0x260 +lr : __xudc_ep0_queue+0xf0/0x110 +sp : ffff800008003d00 +x29: ffff800008003d00 x28: ffff800009474e80 x27: 00000000000000a0 +x26: 0000000000000100 x25: 0000000000000012 x24: ffff000800bc8080 +x23: 0000000000000001 x22: 0000000000000012 x21: ffff000800bc8080 +x20: 0000000000000012 x19: ffff000800bc8080 x18: 0000000000000000 +x17: ffff800876482000 x16: ffff800008004000 x15: 0000000000004000 +x14: 00001f09785d0400 x13: 0103020101005567 x12: 0781400000000200 +x11: 00000000c5672a10 x10: 00000000000008d0 x9 : ffff800009463cf0 +x8 : ffff8000094757b0 x7 : 0201010055670781 x6 : 4000000002000112 +x5 : ffff80000c05009a x4 : ffff000800a15012 x3 : ffff00080362ad80 +x2 : 0000000000000012 x1 : ffff000800a15000 x0 : ffff80000c050088 +Call trace: + __memcpy+0x30/0x260 + xudc_ep0_queue+0x3c/0x60 + usb_ep_queue+0x38/0x44 + composite_ep0_queue.constprop.0+0x2c/0xc0 + composite_setup+0x8d0/0x185c + configfs_composite_setup+0x74/0xb0 + xudc_irq+0x570/0xa40 + __handle_irq_event_percpu+0x58/0x170 + handle_irq_event+0x60/0x120 + handle_fasteoi_irq+0xc0/0x220 + handle_domain_irq+0x60/0x90 + gic_handle_irq+0x74/0xa0 + call_on_irq_stack+0x2c/0x60 + do_interrupt_handler+0x54/0x60 + el1_interrupt+0x30/0x50 + el1h_64_irq_handler+0x18/0x24 + el1h_64_irq+0x78/0x7c + arch_cpu_idle+0x18/0x2c + do_idle+0xdc/0x15c + cpu_startup_entry+0x28/0x60 + rest_init+0xc8/0xe0 + arch_call_rest_init+0x10/0x1c + start_kernel+0x694/0x6d4 + __primary_switched+0xa4/0xac + +Fixes: 1f7c51660034 ("usb: gadget: Add xilinx usb2 device support") +Cc: stable@vger.kernel.org +Reviewed-by: Linus Walleij +Signed-off-by: Piyush Mehta +Link: https://lore.kernel.org/r/20220824071253.1261096-1-piyush.mehta@amd.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/gadget/udc/udc-xilinx.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c +index 096f56a09e6a..01133dc42340 100644 +--- a/drivers/usb/gadget/udc/udc-xilinx.c ++++ b/drivers/usb/gadget/udc/udc-xilinx.c +@@ -496,11 +496,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req, + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + if (ep->is_in) { +- memcpy(eprambase, bufferptr, bytestosend); ++ memcpy_toio(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF0COUNT_OFFSET, bufferlen); + } else { +- memcpy(bufferptr, eprambase, bytestosend); ++ memcpy_toio(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. +@@ -514,11 +514,11 @@ static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req, + eprambase = (u32 __force *)(udc->addr + ep->rambase + + ep->ep_usb.maxpacket); + if (ep->is_in) { +- memcpy(eprambase, bufferptr, bytestosend); ++ memcpy_toio(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF1COUNT_OFFSET, bufferlen); + } else { +- memcpy(bufferptr, eprambase, bytestosend); ++ memcpy_toio(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. +@@ -1020,7 +1020,7 @@ static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req) + udc->addr); + length = req->usb_req.actual = min_t(u32, length, + EP0_MAX_PACKET); +- memcpy(corebuf, req->usb_req.buf, length); ++ memcpy_toio(corebuf, req->usb_req.buf, length); + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } else { +@@ -1746,7 +1746,7 @@ static void xudc_handle_setup(struct xusb_udc *udc) + + /* Load up the chapter 9 command buffer.*/ + ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET); +- memcpy(&setup, ep0rambase, 8); ++ memcpy_toio(&setup, ep0rambase, 8); + + udc->setup = setup; + udc->setup.wValue = cpu_to_le16(setup.wValue); +@@ -1833,7 +1833,7 @@ static void xudc_ep0_out(struct xusb_udc *udc) + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + bytes_to_rx; +- memcpy(buffer, ep0rambase, bytes_to_rx); ++ memcpy_toio(buffer, ep0rambase, bytes_to_rx); + + if (req->usb_req.length == req->usb_req.actual) { + /* Data transfer completed get ready for Status stage */ +@@ -1909,7 +1909,7 @@ static void xudc_ep0_in(struct xusb_udc *udc) + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + length; +- memcpy(ep0rambase, buffer, length); ++ memcpy_toio(ep0rambase, buffer, length); + } + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); +-- +2.35.1 + diff --git a/queue-5.10/usb-typec-intel_pmc_mux-add-new-acpi-id-for-meteor-l.patch b/queue-5.10/usb-typec-intel_pmc_mux-add-new-acpi-id-for-meteor-l.patch new file mode 100644 index 00000000000..003928b7a9b --- /dev/null +++ b/queue-5.10/usb-typec-intel_pmc_mux-add-new-acpi-id-for-meteor-l.patch @@ -0,0 +1,70 @@ +From c8441aca6d006f0c63ecad32e4d605bed735ed53 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Aug 2022 13:16:24 +0300 +Subject: usb: typec: intel_pmc_mux: Add new ACPI ID for Meteor Lake IOM device + +From: Utkarsh Patel + +[ Upstream commit 1b1b672cc1d4fb3065dac79efb8901bd6244ef69 ] + +This adds the necessary ACPI ID for Intel Meteor Lake +IOM devices. + +The callback function is_memory() is modified so that it +also checks if the resource descriptor passed to it is a +memory type "Address Space Resource Descriptor". + +On Intel Meteor Lake the ACPI memory resource is not +described using the "32-bit Memory Range Descriptor" because +the memory is outside of the 32-bit address space. The +memory resource is described using the "Address Space +Resource Descriptor" instead. + +Intel Meteor Lake is the first platform to describe the +memory resource for this device with Address Space Resource +Descriptor, but it most likely will not be the last. +Therefore the change to the is_memory() callback function +is made generic. + +Signed-off-by: Utkarsh Patel +Cc: stable@vger.kernel.org +[ heikki: Rewrote the commit message. ] +Signed-off-by: Heikki Krogerus +Link: https://lore.kernel.org/r/20220816101629.69054-2-heikki.krogerus@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/typec/mux/intel_pmc_mux.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c +index ea1333ad4b2b..80daa70e288b 100644 +--- a/drivers/usb/typec/mux/intel_pmc_mux.c ++++ b/drivers/usb/typec/mux/intel_pmc_mux.c +@@ -541,9 +541,11 @@ static int pmc_usb_register_port(struct pmc_usb *pmc, int index, + + static int is_memory(struct acpi_resource *res, void *data) + { +- struct resource r; ++ struct resource_win win = {}; ++ struct resource *r = &win.res; + +- return !acpi_dev_resource_memory(res, &r); ++ return !(acpi_dev_resource_memory(res, r) || ++ acpi_dev_resource_address_space(res, &win)); + } + + /* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */ +@@ -553,6 +555,9 @@ static const struct acpi_device_id iom_acpi_ids[] = { + + /* AlderLake */ + { "INTC1079", 0x160, }, ++ ++ /* Meteor Lake */ ++ { "INTC107A", 0x160, }, + {} + }; + +-- +2.35.1 + diff --git a/queue-5.10/usb-typec-intel_pmc_mux-update-iom-port-status-offse.patch b/queue-5.10/usb-typec-intel_pmc_mux-update-iom-port-status-offse.patch new file mode 100644 index 00000000000..fecbd40644a --- /dev/null +++ b/queue-5.10/usb-typec-intel_pmc_mux-update-iom-port-status-offse.patch @@ -0,0 +1,94 @@ +From e4b28a3a3f3701edba2b440fba0b7970d3463be7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 31 May 2021 20:58:43 -0700 +Subject: usb: typec: intel_pmc_mux: Update IOM port status offset for + AlderLake + +From: Azhar Shaikh + +[ Upstream commit ca5ce82529104e96ccc5e1888979258e233e1644 ] + +Intel AlderLake(ADL) IOM has a different IOM port status offset than +Intel TigerLake. +Add a new ACPI ID for ADL and use the IOM port status offset as per +the platform. + +Acked-by: Heikki Krogerus +Signed-off-by: Azhar Shaikh +Link: https://lore.kernel.org/r/20210601035843.71150-1-azhar.shaikh@intel.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 1b1b672cc1d4 ("usb: typec: intel_pmc_mux: Add new ACPI ID for Meteor Lake IOM device") +Signed-off-by: Sasha Levin +--- + drivers/usb/typec/mux/intel_pmc_mux.c | 28 ++++++++++++++++++++++----- + 1 file changed, 23 insertions(+), 5 deletions(-) + +diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c +index acdef6fbb85e..ea1333ad4b2b 100644 +--- a/drivers/usb/typec/mux/intel_pmc_mux.c ++++ b/drivers/usb/typec/mux/intel_pmc_mux.c +@@ -83,8 +83,6 @@ enum { + /* + * Input Output Manager (IOM) PORT STATUS + */ +-#define IOM_PORT_STATUS_OFFSET 0x560 +- + #define IOM_PORT_STATUS_ACTIVITY_TYPE_MASK GENMASK(9, 6) + #define IOM_PORT_STATUS_ACTIVITY_TYPE_SHIFT 6 + #define IOM_PORT_STATUS_ACTIVITY_TYPE_USB 0x03 +@@ -144,6 +142,7 @@ struct pmc_usb { + struct pmc_usb_port *port; + struct acpi_device *iom_adev; + void __iomem *iom_base; ++ u32 iom_port_status_offset; + }; + + static void update_port_status(struct pmc_usb_port *port) +@@ -153,7 +152,8 @@ static void update_port_status(struct pmc_usb_port *port) + /* SoC expects the USB Type-C port numbers to start with 0 */ + port_num = port->usb3_port - 1; + +- port->iom_status = readl(port->pmc->iom_base + IOM_PORT_STATUS_OFFSET + ++ port->iom_status = readl(port->pmc->iom_base + ++ port->pmc->iom_port_status_offset + + port_num * sizeof(u32)); + } + +@@ -546,14 +546,32 @@ static int is_memory(struct acpi_resource *res, void *data) + return !acpi_dev_resource_memory(res, &r); + } + ++/* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */ ++static const struct acpi_device_id iom_acpi_ids[] = { ++ /* TigerLake */ ++ { "INTC1072", 0x560, }, ++ ++ /* AlderLake */ ++ { "INTC1079", 0x160, }, ++ {} ++}; ++ + static int pmc_usb_probe_iom(struct pmc_usb *pmc) + { + struct list_head resource_list; + struct resource_entry *rentry; +- struct acpi_device *adev; ++ static const struct acpi_device_id *dev_id; ++ struct acpi_device *adev = NULL; + int ret; + +- adev = acpi_dev_get_first_match_dev("INTC1072", NULL, -1); ++ for (dev_id = &iom_acpi_ids[0]; dev_id->id[0]; dev_id++) { ++ if (acpi_dev_present(dev_id->id, NULL, -1)) { ++ pmc->iom_port_status_offset = (u32)dev_id->driver_data; ++ adev = acpi_dev_get_first_match_dev(dev_id->id, NULL, -1); ++ break; ++ } ++ } ++ + if (!adev) + return -ENODEV; + +-- +2.35.1 + diff --git a/queue-5.10/usb-xhci-mtk-add-a-function-to-un-load-bandwidth-inf.patch b/queue-5.10/usb-xhci-mtk-add-a-function-to-un-load-bandwidth-inf.patch new file mode 100644 index 00000000000..a51429afd38 --- /dev/null +++ b/queue-5.10/usb-xhci-mtk-add-a-function-to-un-load-bandwidth-inf.patch @@ -0,0 +1,106 @@ +From d0aee23505dc3bbdf30719d98caa287b4a82369a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Mar 2021 10:51:55 +0800 +Subject: usb: xhci-mtk: add a function to (un)load bandwidth info + +From: Chunfeng Yun + +[ Upstream commit 338af695fffb12a9407c376ce0cebce896c15050 ] + +Extract a function to load/unload bandwidth info, and remove +a dummy check of TT offset. + +Signed-off-by: Chunfeng Yun +Link: https://lore.kernel.org/r/6fbc000756a4a4a7efbce651b785fee7561becb6.1615170625.git.chunfeng.yun@mediatek.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 548011957d1d ("usb: xhci-mtk: relax TT periodic bandwidth allocation") +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci-mtk-sch.c | 37 ++++++++++++++------------------- + 1 file changed, 16 insertions(+), 21 deletions(-) + +diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c +index b1da3cb077c9..9a9685f74940 100644 +--- a/drivers/usb/host/xhci-mtk-sch.c ++++ b/drivers/usb/host/xhci-mtk-sch.c +@@ -375,7 +375,6 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, + sch_ep->bw_budget_table[j]; + } + } +- sch_ep->allocated = used; + } + + static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset) +@@ -509,6 +508,19 @@ static void update_sch_tt(struct usb_device *udev, + list_del(&sch_ep->tt_endpoint); + } + ++static int load_ep_bw(struct usb_device *udev, struct mu3h_sch_bw_info *sch_bw, ++ struct mu3h_sch_ep_info *sch_ep, bool loaded) ++{ ++ if (sch_ep->sch_tt) ++ update_sch_tt(udev, sch_ep, loaded); ++ ++ /* update bus bandwidth info */ ++ update_bus_bw(sch_bw, sch_ep, loaded); ++ sch_ep->allocated = loaded; ++ ++ return 0; ++} ++ + static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep) + { + u32 boundary = sch_ep->esit; +@@ -535,7 +547,6 @@ static int check_sch_bw(struct usb_device *udev, + u32 esit_boundary; + u32 min_num_budget; + u32 min_cs_count; +- bool tt_offset_ok = false; + int ret; + + /* +@@ -552,8 +563,6 @@ static int check_sch_bw(struct usb_device *udev, + ret = check_sch_tt(udev, sch_ep, offset); + if (ret) + continue; +- else +- tt_offset_ok = true; + } + + if ((offset + sch_ep->num_budget_microframes) > esit_boundary) +@@ -585,29 +594,15 @@ static int check_sch_bw(struct usb_device *udev, + sch_ep->cs_count = min_cs_count; + sch_ep->num_budget_microframes = min_num_budget; + +- if (sch_ep->sch_tt) { +- /* all offset for tt is not ok*/ +- if (!tt_offset_ok) +- return -ERANGE; +- +- update_sch_tt(udev, sch_ep, 1); +- } +- +- /* update bus bandwidth info */ +- update_bus_bw(sch_bw, sch_ep, 1); +- +- return 0; ++ return load_ep_bw(udev, sch_bw, sch_ep, true); + } + + static void destroy_sch_ep(struct usb_device *udev, + struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) + { + /* only release ep bw check passed by check_sch_bw() */ +- if (sch_ep->allocated) { +- update_bus_bw(sch_bw, sch_ep, 0); +- if (sch_ep->sch_tt) +- update_sch_tt(udev, sch_ep, 0); +- } ++ if (sch_ep->allocated) ++ load_ep_bw(udev, sch_bw, sch_ep, false); + + if (sch_ep->sch_tt) + drop_tt(udev); +-- +2.35.1 + diff --git a/queue-5.10/usb-xhci-mtk-add-only-one-extra-cs-for-fs-ls-intr.patch b/queue-5.10/usb-xhci-mtk-add-only-one-extra-cs-for-fs-ls-intr.patch new file mode 100644 index 00000000000..4da49bd3f8d --- /dev/null +++ b/queue-5.10/usb-xhci-mtk-add-only-one-extra-cs-for-fs-ls-intr.patch @@ -0,0 +1,73 @@ +From af6a362dde99860f25f262a923a9b038ee5e6ef6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Mar 2021 10:51:53 +0800 +Subject: usb: xhci-mtk: add only one extra CS for FS/LS INTR + +From: Chunfeng Yun + +[ Upstream commit 1bf661daf6b084bc4d753f55b54f35dc98709685 ] + +In USB2 Spec: +"11.18.5 TT Response Generation +In general, there will be two (or more) complete-split +transactions scheduled for a periodic endpoint. +However, for interrupt endpoints, the maximum size of +the full-/low-speed transaction guarantees that it can +never require more than two complete-split transactions. +Two complete-split transactions are only required +when the transaction spans a microframe boundary." + +Due to the maxp is 64, and less then 188 (at most in one +microframe), seems never span boundary, so use only one CS +for FS/LS interrupt transfer, this will save some bandwidth. + +Signed-off-by: Chunfeng Yun +Link: https://lore.kernel.org/r/5b9ff09f53d23cf9e5c5437db4ffc18b798bf60c.1615170625.git.chunfeng.yun@mediatek.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 548011957d1d ("usb: xhci-mtk: relax TT periodic bandwidth allocation") +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci-mtk-sch.c | 10 +++------- + 1 file changed, 3 insertions(+), 7 deletions(-) + +diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c +index 450fa22b7dc7..59ba25ca018d 100644 +--- a/drivers/usb/host/xhci-mtk-sch.c ++++ b/drivers/usb/host/xhci-mtk-sch.c +@@ -408,13 +408,11 @@ static int check_sch_tt(struct usb_device *udev, + { + struct mu3h_sch_tt *tt = sch_ep->sch_tt; + u32 extra_cs_count; +- u32 fs_budget_start; + u32 start_ss, last_ss; + u32 start_cs, last_cs; + int i; + + start_ss = offset % 8; +- fs_budget_start = (start_ss + 1) % 8; + + if (sch_ep->ep_type == ISOC_OUT_EP) { + last_ss = start_ss + sch_ep->cs_count - 1; +@@ -450,16 +448,14 @@ static int check_sch_tt(struct usb_device *udev, + if (sch_ep->ep_type == ISOC_IN_EP) + extra_cs_count = (last_cs == 7) ? 1 : 2; + else /* ep_type : INTR IN / INTR OUT */ +- extra_cs_count = (fs_budget_start == 6) ? 1 : 2; ++ extra_cs_count = 1; + + cs_count += extra_cs_count; + if (cs_count > 7) + cs_count = 7; /* HW limit */ + +- for (i = 0; i < cs_count + 2; i++) { +- if (test_bit(offset + i, tt->ss_bit_map)) +- return -ERANGE; +- } ++ if (test_bit(offset, tt->ss_bit_map)) ++ return -ERANGE; + + sch_ep->cs_count = cs_count; + /* one for ss, the other for idle */ +-- +2.35.1 + diff --git a/queue-5.10/usb-xhci-mtk-add-some-schedule-error-number.patch b/queue-5.10/usb-xhci-mtk-add-some-schedule-error-number.patch new file mode 100644 index 00000000000..6b5a9016ca8 --- /dev/null +++ b/queue-5.10/usb-xhci-mtk-add-some-schedule-error-number.patch @@ -0,0 +1,144 @@ +From db2c0fd011227cb73028d73e62ebbd5355602506 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Mar 2021 10:52:02 +0800 +Subject: usb: xhci-mtk: add some schedule error number + +From: Chunfeng Yun + +[ Upstream commit ccda8c224c0701caac007311d06a2de9543a7590 ] + +This is used to provide more information about which case +causes bandwidth schedule failure. + +Signed-off-by: Chunfeng Yun +Link: https://lore.kernel.org/r/9771f44093053b581e9c4be4b7fb68d9fcecad08.1615170625.git.chunfeng.yun@mediatek.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 548011957d1d ("usb: xhci-mtk: relax TT periodic bandwidth allocation") +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci-mtk-sch.c | 44 ++++++++++++++++++++++++++------- + 1 file changed, 35 insertions(+), 9 deletions(-) + +diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c +index 9a9685f74940..a6ec75bf2def 100644 +--- a/drivers/usb/host/xhci-mtk-sch.c ++++ b/drivers/usb/host/xhci-mtk-sch.c +@@ -25,6 +25,13 @@ + */ + #define TT_MICROFRAMES_MAX 9 + ++/* schedule error type */ ++#define ESCH_SS_Y6 1001 ++#define ESCH_SS_OVERLAP 1002 ++#define ESCH_CS_OVERFLOW 1003 ++#define ESCH_BW_OVERFLOW 1004 ++#define ESCH_FIXME 1005 ++ + /* mtk scheduler bitmasks */ + #define EP_BPKTS(p) ((p) & 0x7f) + #define EP_BCSCOUNT(p) (((p) & 0x7) << 8) +@@ -32,6 +39,24 @@ + #define EP_BOFFSET(p) ((p) & 0x3fff) + #define EP_BREPEAT(p) (((p) & 0x7fff) << 16) + ++static char *sch_error_string(int err_num) ++{ ++ switch (err_num) { ++ case ESCH_SS_Y6: ++ return "Can't schedule Start-Split in Y6"; ++ case ESCH_SS_OVERLAP: ++ return "Can't find a suitable Start-Split location"; ++ case ESCH_CS_OVERFLOW: ++ return "The last Complete-Split is greater than 7"; ++ case ESCH_BW_OVERFLOW: ++ return "Bandwidth exceeds the maximum limit"; ++ case ESCH_FIXME: ++ return "FIXME, to be resolved"; ++ default: ++ return "Unknown"; ++ } ++} ++ + static int is_fs_or_ls(enum usb_device_speed speed) + { + return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW; +@@ -395,7 +420,7 @@ static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset) + for (j = 0; j < sch_ep->cs_count; j++) { + tmp = tt->fs_bus_bw[base + j] + sch_ep->bw_cost_per_microframe; + if (tmp > FS_PAYLOAD_MAX) +- return -ERANGE; ++ return -ESCH_BW_OVERFLOW; + } + } + +@@ -421,11 +446,11 @@ static int check_sch_tt(struct usb_device *udev, + * must never schedule Start-Split in Y6 + */ + if (!(start_ss == 7 || last_ss < 6)) +- return -ERANGE; ++ return -ESCH_SS_Y6; + + for (i = 0; i < sch_ep->cs_count; i++) + if (test_bit(offset + i, tt->ss_bit_map)) +- return -ERANGE; ++ return -ESCH_SS_OVERLAP; + + } else { + u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); +@@ -435,14 +460,14 @@ static int check_sch_tt(struct usb_device *udev, + * must never schedule Start-Split in Y6 + */ + if (start_ss == 6) +- return -ERANGE; ++ return -ESCH_SS_Y6; + + /* one uframe for ss + one uframe for idle */ + start_cs = (start_ss + 2) % 8; + last_cs = start_cs + cs_count - 1; + + if (last_cs > 7) +- return -ERANGE; ++ return -ESCH_CS_OVERFLOW; + + if (sch_ep->ep_type == ISOC_IN_EP) + extra_cs_count = (last_cs == 7) ? 1 : 2; +@@ -454,7 +479,7 @@ static int check_sch_tt(struct usb_device *udev, + cs_count = 7; /* HW limit */ + + if (test_bit(offset, tt->ss_bit_map)) +- return -ERANGE; ++ return -ESCH_SS_OVERLAP; + + sch_ep->cs_count = cs_count; + /* one for ss, the other for idle */ +@@ -547,7 +572,7 @@ static int check_sch_bw(struct usb_device *udev, + u32 esit_boundary; + u32 min_num_budget; + u32 min_cs_count; +- int ret; ++ int ret = 0; + + /* + * Search through all possible schedule microframes. +@@ -588,7 +613,7 @@ static int check_sch_bw(struct usb_device *udev, + + /* check bandwidth */ + if (min_bw > bw_boundary) +- return -ERANGE; ++ return ret ? ret : -ESCH_BW_OVERFLOW; + + sch_ep->offset = min_index; + sch_ep->cs_count = min_cs_count; +@@ -765,7 +790,8 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) + + ret = check_sch_bw(udev, sch_bw, sch_ep); + if (ret) { +- xhci_err(xhci, "Not enough bandwidth!\n"); ++ xhci_err(xhci, "Not enough bandwidth! (%s)\n", ++ sch_error_string(-ret)); + return -ENOSPC; + } + } +-- +2.35.1 + diff --git a/queue-5.10/usb-xhci-mtk-allow-multiple-start-split-in-a-microfr.patch b/queue-5.10/usb-xhci-mtk-allow-multiple-start-split-in-a-microfr.patch new file mode 100644 index 00000000000..c21444028a3 --- /dev/null +++ b/queue-5.10/usb-xhci-mtk-allow-multiple-start-split-in-a-microfr.patch @@ -0,0 +1,108 @@ +From d2efce5f881287e8187b04a3f20a61a2608358d1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 18 Jun 2021 13:46:05 +0800 +Subject: usb: xhci-mtk: allow multiple Start-Split in a microframe + +From: Chunfeng Yun + +[ Upstream commit d3997fce189fc4423169c51a81ba5ca01144d886 ] + +This patch is used to relax bandwidth schedule by allowing multiple +Start-Split in the same microframe. + +Reviewed-and-Tested-by: Ikjoon Jang +Signed-off-by: Chunfeng Yun +Link: https://lore.kernel.org/r/1623995165-25759-1-git-send-email-chunfeng.yun@mediatek.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 548011957d1d ("usb: xhci-mtk: relax TT periodic bandwidth allocation") +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci-mtk-sch.c | 18 ------------------ + drivers/usb/host/xhci-mtk.h | 2 -- + 2 files changed, 20 deletions(-) + +diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c +index a6ec75bf2def..f048af9c5335 100644 +--- a/drivers/usb/host/xhci-mtk-sch.c ++++ b/drivers/usb/host/xhci-mtk-sch.c +@@ -430,11 +430,9 @@ static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset) + static int check_sch_tt(struct usb_device *udev, + struct mu3h_sch_ep_info *sch_ep, u32 offset) + { +- struct mu3h_sch_tt *tt = sch_ep->sch_tt; + u32 extra_cs_count; + u32 start_ss, last_ss; + u32 start_cs, last_cs; +- int i; + + start_ss = offset % 8; + +@@ -448,10 +446,6 @@ static int check_sch_tt(struct usb_device *udev, + if (!(start_ss == 7 || last_ss < 6)) + return -ESCH_SS_Y6; + +- for (i = 0; i < sch_ep->cs_count; i++) +- if (test_bit(offset + i, tt->ss_bit_map)) +- return -ESCH_SS_OVERLAP; +- + } else { + u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); + +@@ -478,9 +472,6 @@ static int check_sch_tt(struct usb_device *udev, + if (cs_count > 7) + cs_count = 7; /* HW limit */ + +- if (test_bit(offset, tt->ss_bit_map)) +- return -ESCH_SS_OVERLAP; +- + sch_ep->cs_count = cs_count; + /* one for ss, the other for idle */ + sch_ep->num_budget_microframes = cs_count + 2; +@@ -502,11 +493,9 @@ static void update_sch_tt(struct usb_device *udev, + struct mu3h_sch_tt *tt = sch_ep->sch_tt; + u32 base, num_esit; + int bw_updated; +- int bits; + int i, j; + + num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; +- bits = (sch_ep->ep_type == ISOC_OUT_EP) ? sch_ep->cs_count : 1; + + if (used) + bw_updated = sch_ep->bw_cost_per_microframe; +@@ -516,13 +505,6 @@ static void update_sch_tt(struct usb_device *udev, + for (i = 0; i < num_esit; i++) { + base = sch_ep->offset + i * sch_ep->esit; + +- for (j = 0; j < bits; j++) { +- if (used) +- set_bit(base + j, tt->ss_bit_map); +- else +- clear_bit(base + j, tt->ss_bit_map); +- } +- + for (j = 0; j < sch_ep->cs_count; j++) + tt->fs_bus_bw[base + j] += bw_updated; + } +diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h +index 2fc0568ba054..3e2c607b5d64 100644 +--- a/drivers/usb/host/xhci-mtk.h ++++ b/drivers/usb/host/xhci-mtk.h +@@ -20,14 +20,12 @@ + #define XHCI_MTK_MAX_ESIT 64 + + /** +- * @ss_bit_map: used to avoid start split microframes overlay + * @fs_bus_bw: array to keep track of bandwidth already used for FS + * @ep_list: Endpoints using this TT + * @usb_tt: usb TT related + * @tt_port: TT port number + */ + struct mu3h_sch_tt { +- DECLARE_BITMAP(ss_bit_map, XHCI_MTK_MAX_ESIT); + u32 fs_bus_bw[XHCI_MTK_MAX_ESIT]; + struct list_head ep_list; + struct usb_tt *usb_tt; +-- +2.35.1 + diff --git a/queue-5.10/usb-xhci-mtk-get-the-microframe-boundary-for-esit.patch b/queue-5.10/usb-xhci-mtk-get-the-microframe-boundary-for-esit.patch new file mode 100644 index 00000000000..8fe7e8ba8a3 --- /dev/null +++ b/queue-5.10/usb-xhci-mtk-get-the-microframe-boundary-for-esit.patch @@ -0,0 +1,88 @@ +From e20d091703fab4db04b69b71425abb66396e2303 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Mar 2021 10:51:52 +0800 +Subject: usb: xhci-mtk: get the microframe boundary for ESIT + +From: Chunfeng Yun + +[ Upstream commit 7c986fbc16ae6b2f914a3ebf06a3a4a8d9bb0b7c ] + +Tune the boundary for FS/LS ESIT due to CS: +For ISOC out-ep, the controller starts transfer data after +the first SS; for others, the data is already transferred +before the last CS. + +Signed-off-by: Chunfeng Yun +Link: https://lore.kernel.org/r/49e5a269a47984f3126a70c3fb471b0c2874b8c2.1615170625.git.chunfeng.yun@mediatek.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 548011957d1d ("usb: xhci-mtk: relax TT periodic bandwidth allocation") +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci-mtk-sch.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c +index 8950d1f10a7f..450fa22b7dc7 100644 +--- a/drivers/usb/host/xhci-mtk-sch.c ++++ b/drivers/usb/host/xhci-mtk-sch.c +@@ -513,22 +513,35 @@ static void update_sch_tt(struct usb_device *udev, + list_del(&sch_ep->tt_endpoint); + } + ++static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep) ++{ ++ u32 boundary = sch_ep->esit; ++ ++ if (sch_ep->sch_tt) { /* LS/FS with TT */ ++ /* tune for CS */ ++ if (sch_ep->ep_type != ISOC_OUT_EP) ++ boundary++; ++ else if (boundary > 1) /* normally esit >= 8 for FS/LS */ ++ boundary--; ++ } ++ ++ return boundary; ++} ++ + static int check_sch_bw(struct usb_device *udev, + struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) + { + u32 offset; +- u32 esit; + u32 min_bw; + u32 min_index; + u32 worst_bw; + u32 bw_boundary; ++ u32 esit_boundary; + u32 min_num_budget; + u32 min_cs_count; + bool tt_offset_ok = false; + int ret; + +- esit = sch_ep->esit; +- + /* + * Search through all possible schedule microframes. + * and find a microframe where its worst bandwidth is minimum. +@@ -537,7 +550,8 @@ static int check_sch_bw(struct usb_device *udev, + min_index = 0; + min_cs_count = sch_ep->cs_count; + min_num_budget = sch_ep->num_budget_microframes; +- for (offset = 0; offset < esit; offset++) { ++ esit_boundary = get_esit_boundary(sch_ep); ++ for (offset = 0; offset < sch_ep->esit; offset++) { + if (is_fs_or_ls(udev->speed)) { + ret = check_sch_tt(udev, sch_ep, offset); + if (ret) +@@ -546,7 +560,7 @@ static int check_sch_bw(struct usb_device *udev, + tt_offset_ok = true; + } + +- if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) ++ if ((offset + sch_ep->num_budget_microframes) > esit_boundary) + break; + + worst_bw = get_max_bw(sch_bw, sch_ep, offset); +-- +2.35.1 + diff --git a/queue-5.10/usb-xhci-mtk-relax-tt-periodic-bandwidth-allocation.patch b/queue-5.10/usb-xhci-mtk-relax-tt-periodic-bandwidth-allocation.patch new file mode 100644 index 00000000000..e11277b57fb --- /dev/null +++ b/queue-5.10/usb-xhci-mtk-relax-tt-periodic-bandwidth-allocation.patch @@ -0,0 +1,90 @@ +From 330754e23a0ea0bbb80e296c01f83f5ef0ecb633 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Aug 2021 13:39:57 +0800 +Subject: usb: xhci-mtk: relax TT periodic bandwidth allocation + +From: Ikjoon Jang + +[ Upstream commit 548011957d1d72e0b662300c8b32b81d593b796e ] + +Currently xhci-mtk needs software-managed bandwidth allocation for +periodic endpoints, it allocates the microframe index for the first +start-split packet for each endpoint. As this index allocation logic +should avoid the conflicts with other full/low-speed periodic endpoints, +it uses the worst case byte budgets on high-speed bus bandwidth +For example, for an isochronos IN endpoint with 192 bytes budget, +it will consume the whole 4 u-frames(188 * 4) while the actual +full-speed bus budget should be just 192bytes. + +This patch changes the low/full-speed bandwidth allocation logic +to use "approximate" best case budget for lower speed bandwidth +management. For the same endpoint from the above example, the +approximate best case budget is now reduced to (188 * 2) bytes. + +Without this patch, many usb audio headsets with 3 interfaces +(audio input, audio output, and HID) cannot be configured +on xhci-mtk. + +Signed-off-by: Ikjoon Jang +Link: https://lore.kernel.org/r/20210805133937.1.Ia8174b875bc926c12ce427a5a1415dea31cc35ae@changeid +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci-mtk-sch.c | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c +index f048af9c5335..4a7b200674ea 100644 +--- a/drivers/usb/host/xhci-mtk-sch.c ++++ b/drivers/usb/host/xhci-mtk-sch.c +@@ -408,16 +408,17 @@ static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset) + u32 num_esit, tmp; + int base; + int i, j; ++ u8 uframes = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); + + num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; ++ ++ if (sch_ep->ep_type == INT_IN_EP || sch_ep->ep_type == ISOC_IN_EP) ++ offset++; ++ + for (i = 0; i < num_esit; i++) { + base = offset + i * sch_ep->esit; + +- /* +- * Compared with hs bus, no matter what ep type, +- * the hub will always delay one uframe to send data +- */ +- for (j = 0; j < sch_ep->cs_count; j++) { ++ for (j = 0; j < uframes; j++) { + tmp = tt->fs_bus_bw[base + j] + sch_ep->bw_cost_per_microframe; + if (tmp > FS_PAYLOAD_MAX) + return -ESCH_BW_OVERFLOW; +@@ -494,6 +495,8 @@ static void update_sch_tt(struct usb_device *udev, + u32 base, num_esit; + int bw_updated; + int i, j; ++ int offset = sch_ep->offset; ++ u8 uframes = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); + + num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; + +@@ -502,10 +505,13 @@ static void update_sch_tt(struct usb_device *udev, + else + bw_updated = -sch_ep->bw_cost_per_microframe; + ++ if (sch_ep->ep_type == INT_IN_EP || sch_ep->ep_type == ISOC_IN_EP) ++ offset++; ++ + for (i = 0; i < num_esit; i++) { +- base = sch_ep->offset + i * sch_ep->esit; ++ base = offset + i * sch_ep->esit; + +- for (j = 0; j < sch_ep->cs_count; j++) ++ for (j = 0; j < uframes; j++) + tt->fs_bus_bw[base + j] += bw_updated; + } + +-- +2.35.1 + diff --git a/queue-5.10/usb-xhci-mtk-use-sch_tt-to-check-whether-need-do-tt-.patch b/queue-5.10/usb-xhci-mtk-use-sch_tt-to-check-whether-need-do-tt-.patch new file mode 100644 index 00000000000..271e002b192 --- /dev/null +++ b/queue-5.10/usb-xhci-mtk-use-sch_tt-to-check-whether-need-do-tt-.patch @@ -0,0 +1,46 @@ +From ecf1252e2c9e2fadf1a784cd4b3a0a993deea923 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Mar 2021 10:51:54 +0800 +Subject: usb: xhci-mtk: use @sch_tt to check whether need do TT schedule + +From: Chunfeng Yun + +[ Upstream commit 4a56adf4fafbc41ceffce0c3f385f59d4fc3c16a ] + +It's clearer to use @sch_tt to check whether need do TT schedule, +no function is changed. + +Signed-off-by: Chunfeng Yun +Link: https://lore.kernel.org/r/324a76782ccaf857a8f01f67aee435e8ec7d0e28.1615170625.git.chunfeng.yun@mediatek.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 548011957d1d ("usb: xhci-mtk: relax TT periodic bandwidth allocation") +Signed-off-by: Sasha Levin +--- + drivers/usb/host/xhci-mtk-sch.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c +index 59ba25ca018d..b1da3cb077c9 100644 +--- a/drivers/usb/host/xhci-mtk-sch.c ++++ b/drivers/usb/host/xhci-mtk-sch.c +@@ -548,7 +548,7 @@ static int check_sch_bw(struct usb_device *udev, + min_num_budget = sch_ep->num_budget_microframes; + esit_boundary = get_esit_boundary(sch_ep); + for (offset = 0; offset < sch_ep->esit; offset++) { +- if (is_fs_or_ls(udev->speed)) { ++ if (sch_ep->sch_tt) { + ret = check_sch_tt(udev, sch_ep, offset); + if (ret) + continue; +@@ -585,7 +585,7 @@ static int check_sch_bw(struct usb_device *udev, + sch_ep->cs_count = min_cs_count; + sch_ep->num_budget_microframes = min_num_budget; + +- if (is_fs_or_ls(udev->speed)) { ++ if (sch_ep->sch_tt) { + /* all offset for tt is not ok*/ + if (!tt_offset_ok) + return -ERANGE; +-- +2.35.1 + diff --git a/queue-5.10/vfio-type1-change-success-value-of-vaddr_get_pfn.patch b/queue-5.10/vfio-type1-change-success-value-of-vaddr_get_pfn.patch new file mode 100644 index 00000000000..4adaa17d6b9 --- /dev/null +++ b/queue-5.10/vfio-type1-change-success-value-of-vaddr_get_pfn.patch @@ -0,0 +1,101 @@ +From 20bfdcd0744517441b99b14570d7ee4d9deb16e6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Feb 2021 11:13:03 -0500 +Subject: vfio/type1: Change success value of vaddr_get_pfn() + +From: Daniel Jordan + +[ Upstream commit be16c1fd99f41abebc0bf965d5d29cd18c9d271e ] + +vaddr_get_pfn() simply returns 0 on success. Have it report the number +of pfns successfully gotten instead, whether from page pinning or +follow_fault_pfn(), which will be used later when batching pinning. + +Change the last check in vfio_pin_pages_remote() for consistency with +the other two. + +Signed-off-by: Daniel Jordan +Signed-off-by: Alex Williamson +Stable-dep-of: 873aefb376bb ("vfio/type1: Unpin zero pages") +Signed-off-by: Sasha Levin +--- + drivers/vfio/vfio_iommu_type1.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c +index fbd438e9b9b0..2d26244f9c32 100644 +--- a/drivers/vfio/vfio_iommu_type1.c ++++ b/drivers/vfio/vfio_iommu_type1.c +@@ -464,6 +464,10 @@ static int follow_fault_pfn(struct vm_area_struct *vma, struct mm_struct *mm, + return ret; + } + ++/* ++ * Returns the positive number of pfns successfully obtained or a negative ++ * error code. ++ */ + static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, + int prot, unsigned long *pfn) + { +@@ -480,7 +484,6 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, + page, NULL, NULL); + if (ret == 1) { + *pfn = page_to_pfn(page[0]); +- ret = 0; + goto done; + } + +@@ -494,8 +497,12 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, + if (ret == -EAGAIN) + goto retry; + +- if (!ret && !is_invalid_reserved_pfn(*pfn)) +- ret = -EFAULT; ++ if (!ret) { ++ if (is_invalid_reserved_pfn(*pfn)) ++ ret = 1; ++ else ++ ret = -EFAULT; ++ } + } + done: + mmap_read_unlock(mm); +@@ -521,7 +528,7 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, + return -ENODEV; + + ret = vaddr_get_pfn(current->mm, vaddr, dma->prot, pfn_base); +- if (ret) ++ if (ret < 0) + return ret; + + pinned++; +@@ -548,7 +555,7 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, + for (vaddr += PAGE_SIZE, iova += PAGE_SIZE; pinned < npage; + pinned++, vaddr += PAGE_SIZE, iova += PAGE_SIZE) { + ret = vaddr_get_pfn(current->mm, vaddr, dma->prot, &pfn); +- if (ret) ++ if (ret < 0) + break; + + if (pfn != *pfn_base + pinned || +@@ -574,7 +581,7 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, + ret = vfio_lock_acct(dma, lock_acct, false); + + unpin_out: +- if (ret) { ++ if (ret < 0) { + if (!rsvd) { + for (pfn = *pfn_base ; pinned ; pfn++, pinned--) + put_pfn(pfn, dma->prot); +@@ -618,7 +625,7 @@ static int vfio_pin_page_external(struct vfio_dma *dma, unsigned long vaddr, + return -ENODEV; + + ret = vaddr_get_pfn(mm, vaddr, dma->prot, pfn_base); +- if (!ret && do_accounting && !is_invalid_reserved_pfn(*pfn_base)) { ++ if (ret == 1 && do_accounting && !is_invalid_reserved_pfn(*pfn_base)) { + ret = vfio_lock_acct(dma, 1, true); + if (ret) { + put_pfn(*pfn_base, dma->prot); +-- +2.35.1 + diff --git a/queue-5.10/vfio-type1-prepare-for-batched-pinning-with-struct-v.patch b/queue-5.10/vfio-type1-prepare-for-batched-pinning-with-struct-v.patch new file mode 100644 index 00000000000..3881ef925cc --- /dev/null +++ b/queue-5.10/vfio-type1-prepare-for-batched-pinning-with-struct-v.patch @@ -0,0 +1,227 @@ +From 50a5be8af5e4cad083f350e90abe6477bc28a963 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 19 Feb 2021 11:13:04 -0500 +Subject: vfio/type1: Prepare for batched pinning with struct vfio_batch + +From: Daniel Jordan + +[ Upstream commit 4b6c33b3229678e38a6b0bbd4367d4b91366b523 ] + +Get ready to pin more pages at once with struct vfio_batch, which +represents a batch of pinned pages. + +The struct has a fallback page pointer to avoid two unlikely scenarios: +pointlessly allocating a page if disable_hugepages is enabled or failing +the whole pinning operation if the kernel can't allocate memory. + +vaddr_get_pfn() becomes vaddr_get_pfns() to prepare for handling +multiple pages, though for now only one page is stored in the pages +array. + +Signed-off-by: Daniel Jordan +Signed-off-by: Alex Williamson +Stable-dep-of: 873aefb376bb ("vfio/type1: Unpin zero pages") +Signed-off-by: Sasha Levin +--- + drivers/vfio/vfio_iommu_type1.c | 71 +++++++++++++++++++++++++++------ + 1 file changed, 58 insertions(+), 13 deletions(-) + +diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c +index 2d26244f9c32..0c15cffd5ef1 100644 +--- a/drivers/vfio/vfio_iommu_type1.c ++++ b/drivers/vfio/vfio_iommu_type1.c +@@ -98,6 +98,12 @@ struct vfio_dma { + unsigned long *bitmap; + }; + ++struct vfio_batch { ++ struct page **pages; /* for pin_user_pages_remote */ ++ struct page *fallback_page; /* if pages alloc fails */ ++ int capacity; /* length of pages array */ ++}; ++ + struct vfio_group { + struct iommu_group *iommu_group; + struct list_head next; +@@ -428,6 +434,31 @@ static int put_pfn(unsigned long pfn, int prot) + return 0; + } + ++#define VFIO_BATCH_MAX_CAPACITY (PAGE_SIZE / sizeof(struct page *)) ++ ++static void vfio_batch_init(struct vfio_batch *batch) ++{ ++ if (unlikely(disable_hugepages)) ++ goto fallback; ++ ++ batch->pages = (struct page **) __get_free_page(GFP_KERNEL); ++ if (!batch->pages) ++ goto fallback; ++ ++ batch->capacity = VFIO_BATCH_MAX_CAPACITY; ++ return; ++ ++fallback: ++ batch->pages = &batch->fallback_page; ++ batch->capacity = 1; ++} ++ ++static void vfio_batch_fini(struct vfio_batch *batch) ++{ ++ if (batch->capacity == VFIO_BATCH_MAX_CAPACITY) ++ free_page((unsigned long)batch->pages); ++} ++ + static int follow_fault_pfn(struct vm_area_struct *vma, struct mm_struct *mm, + unsigned long vaddr, unsigned long *pfn, + bool write_fault) +@@ -468,10 +499,10 @@ static int follow_fault_pfn(struct vm_area_struct *vma, struct mm_struct *mm, + * Returns the positive number of pfns successfully obtained or a negative + * error code. + */ +-static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, +- int prot, unsigned long *pfn) ++static int vaddr_get_pfns(struct mm_struct *mm, unsigned long vaddr, ++ long npages, int prot, unsigned long *pfn, ++ struct page **pages) + { +- struct page *page[1]; + struct vm_area_struct *vma; + unsigned int flags = 0; + int ret; +@@ -480,10 +511,10 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, + flags |= FOLL_WRITE; + + mmap_read_lock(mm); +- ret = pin_user_pages_remote(mm, vaddr, 1, flags | FOLL_LONGTERM, +- page, NULL, NULL); +- if (ret == 1) { +- *pfn = page_to_pfn(page[0]); ++ ret = pin_user_pages_remote(mm, vaddr, npages, flags | FOLL_LONGTERM, ++ pages, NULL, NULL); ++ if (ret > 0) { ++ *pfn = page_to_pfn(pages[0]); + goto done; + } + +@@ -516,7 +547,7 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr, + */ + static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, + long npage, unsigned long *pfn_base, +- unsigned long limit) ++ unsigned long limit, struct vfio_batch *batch) + { + unsigned long pfn = 0; + long ret, pinned = 0, lock_acct = 0; +@@ -527,7 +558,8 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, + if (!current->mm) + return -ENODEV; + +- ret = vaddr_get_pfn(current->mm, vaddr, dma->prot, pfn_base); ++ ret = vaddr_get_pfns(current->mm, vaddr, 1, dma->prot, pfn_base, ++ batch->pages); + if (ret < 0) + return ret; + +@@ -554,7 +586,8 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr, + /* Lock all the consecutive pages from pfn_base */ + for (vaddr += PAGE_SIZE, iova += PAGE_SIZE; pinned < npage; + pinned++, vaddr += PAGE_SIZE, iova += PAGE_SIZE) { +- ret = vaddr_get_pfn(current->mm, vaddr, dma->prot, &pfn); ++ ret = vaddr_get_pfns(current->mm, vaddr, 1, dma->prot, &pfn, ++ batch->pages); + if (ret < 0) + break; + +@@ -617,6 +650,7 @@ static long vfio_unpin_pages_remote(struct vfio_dma *dma, dma_addr_t iova, + static int vfio_pin_page_external(struct vfio_dma *dma, unsigned long vaddr, + unsigned long *pfn_base, bool do_accounting) + { ++ struct page *pages[1]; + struct mm_struct *mm; + int ret; + +@@ -624,7 +658,7 @@ static int vfio_pin_page_external(struct vfio_dma *dma, unsigned long vaddr, + if (!mm) + return -ENODEV; + +- ret = vaddr_get_pfn(mm, vaddr, dma->prot, pfn_base); ++ ret = vaddr_get_pfns(mm, vaddr, 1, dma->prot, pfn_base, pages); + if (ret == 1 && do_accounting && !is_invalid_reserved_pfn(*pfn_base)) { + ret = vfio_lock_acct(dma, 1, true); + if (ret) { +@@ -1270,15 +1304,19 @@ static int vfio_pin_map_dma(struct vfio_iommu *iommu, struct vfio_dma *dma, + { + dma_addr_t iova = dma->iova; + unsigned long vaddr = dma->vaddr; ++ struct vfio_batch batch; + size_t size = map_size; + long npage; + unsigned long pfn, limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + int ret = 0; + ++ vfio_batch_init(&batch); ++ + while (size) { + /* Pin a contiguous chunk of memory */ + npage = vfio_pin_pages_remote(dma, vaddr + dma->size, +- size >> PAGE_SHIFT, &pfn, limit); ++ size >> PAGE_SHIFT, &pfn, limit, ++ &batch); + if (npage <= 0) { + WARN_ON(!npage); + ret = (int)npage; +@@ -1298,6 +1336,7 @@ static int vfio_pin_map_dma(struct vfio_iommu *iommu, struct vfio_dma *dma, + dma->size += npage << PAGE_SHIFT; + } + ++ vfio_batch_fini(&batch); + dma->iommu_mapped = true; + + if (ret) +@@ -1456,6 +1495,7 @@ static int vfio_bus_type(struct device *dev, void *data) + static int vfio_iommu_replay(struct vfio_iommu *iommu, + struct vfio_domain *domain) + { ++ struct vfio_batch batch; + struct vfio_domain *d = NULL; + struct rb_node *n; + unsigned long limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; +@@ -1466,6 +1506,8 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, + d = list_first_entry(&iommu->domain_list, + struct vfio_domain, next); + ++ vfio_batch_init(&batch); ++ + n = rb_first(&iommu->dma_list); + + for (; n; n = rb_next(n)) { +@@ -1513,7 +1555,8 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, + + npage = vfio_pin_pages_remote(dma, vaddr, + n >> PAGE_SHIFT, +- &pfn, limit); ++ &pfn, limit, ++ &batch); + if (npage <= 0) { + WARN_ON(!npage); + ret = (int)npage; +@@ -1546,6 +1589,7 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, + dma->iommu_mapped = true; + } + ++ vfio_batch_fini(&batch); + return 0; + + unwind: +@@ -1586,6 +1630,7 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu, + } + } + ++ vfio_batch_fini(&batch); + return ret; + } + +-- +2.35.1 + diff --git a/queue-5.10/vfio-type1-unpin-zero-pages.patch b/queue-5.10/vfio-type1-unpin-zero-pages.patch new file mode 100644 index 00000000000..38d55c74ad8 --- /dev/null +++ b/queue-5.10/vfio-type1-unpin-zero-pages.patch @@ -0,0 +1,58 @@ +From 3911a750c69d70d18478e8c952c73463d7fbac64 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 29 Aug 2022 21:05:40 -0600 +Subject: vfio/type1: Unpin zero pages + +From: Alex Williamson + +[ Upstream commit 873aefb376bbc0ed1dd2381ea1d6ec88106fdbd4 ] + +There's currently a reference count leak on the zero page. We increment +the reference via pin_user_pages_remote(), but the page is later handled +as an invalid/reserved page, therefore it's not accounted against the +user and not unpinned by our put_pfn(). + +Introducing special zero page handling in put_pfn() would resolve the +leak, but without accounting of the zero page, a single user could +still create enough mappings to generate a reference count overflow. + +The zero page is always resident, so for our purposes there's no reason +to keep it pinned. Therefore, add a loop to walk pages returned from +pin_user_pages_remote() and unpin any zero pages. + +Cc: stable@vger.kernel.org +Reported-by: Luboslav Pivarc +Reviewed-by: David Hildenbrand +Link: https://lore.kernel.org/r/166182871735.3518559.8884121293045337358.stgit@omen +Signed-off-by: Alex Williamson +Signed-off-by: Sasha Levin +--- + drivers/vfio/vfio_iommu_type1.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c +index 0c15cffd5ef1..cd5c8b49d763 100644 +--- a/drivers/vfio/vfio_iommu_type1.c ++++ b/drivers/vfio/vfio_iommu_type1.c +@@ -514,6 +514,18 @@ static int vaddr_get_pfns(struct mm_struct *mm, unsigned long vaddr, + ret = pin_user_pages_remote(mm, vaddr, npages, flags | FOLL_LONGTERM, + pages, NULL, NULL); + if (ret > 0) { ++ int i; ++ ++ /* ++ * The zero page is always resident, we don't need to pin it ++ * and it falls into our invalid/reserved test so we don't ++ * unpin in put_pfn(). Unpin all zero pages in the batch here. ++ */ ++ for (i = 0 ; i < ret; i++) { ++ if (unlikely(is_zero_pfn(page_to_pfn(pages[i])))) ++ unpin_user_page(pages[i]); ++ } ++ + *pfn = page_to_pfn(pages[0]); + goto done; + } +-- +2.35.1 +