From f6f9670c6d199bd687b92cd0edc0ab6044355e9c Mon Sep 17 00:00:00 2001 From: ertugerata Date: Wed, 23 Sep 2015 01:05:31 +0300 Subject: [PATCH 1/3] new kernel --- kernel/drivers/module-bbswitch/pspec.xml | 11 +- kernel/drivers/module-broadcom-wl/pspec.xml | 11 +- .../drivers/module-virtualbox-guest/pspec.xml | 13 +- kernel/drivers/module-virtualbox/pspec.xml | 11 +- kernel/drivers/ndiswrapper/pspec.xml | 13 +- .../kernel/files/configs/kernel-x86_64-config | 2 +- .../kernel/files/patches/linux/patch-4.1.8.xz | Bin 0 -> 256048 bytes .../mageia.old/3rd-3rdparty-1.0-tree.patch | 181 + .../mageia.old/3rd-3rdparty-merge.patch | 181 + .../mageia.old/3rd-acerhk-0.5.35.patch | 4212 + .../3rd-acerhk-2.6.36-buildfix.patch | 20 + .../mageia.old/3rd-acerhk-extra-cflags.patch | 33 + ...cerhk-fix-build-with-function-tracer.patch | 22 + .../mageia.old/3rd-acerhk-fix-include.patch | 23 + .../mageia.old/3rd-acerhk-kbuild.patch | 37 + .../3rd-acerhk-proc_dir_entry-owner.patch | 67 + .../mageia.old/3rd-aes2501-kbuild.patch | 47 + .../patches/mageia.old/3rd-aes2501-r19.patch | 2356 + .../3rd-aes2501-rmmod-oops-fix.patch | 21 + .../mageia.old/3rd-ndiswrapper-1.59.patch | 26399 +++ .../3rd-ndiswrapper-4.0-buildfix.patch | 32 + .../mageia.old/3rd-ndiswrapper-Kconfig.patch | 7 + .../3rd-ndiswrapper-Makefile-build-fix.patch | 45 + .../3rd-ndiswrapper-linux-3.14-buildfix.patch | 33 + .../patches/mageia.old/3rd-rfswitch-1.3.patch | 978 + .../3rd-rfswitch-3.0-buildfix.patch | 19 + .../mageia.old/3rd-rfswitch-build-fix.patch | 51 + .../mageia.old/3rd-rtl8723bs-buildfix.patch | 23 + .../mageia.old/3rd-rtl8723bs-updates.patch | 28991 +++ .../patches/mageia.old/3rd-rtl8723bs.patch | 140470 +++++++++++++++ .../patches/mageia.old/3rd-viahss-0.92.patch | 189 + .../3rd-viahss-2.6.35-buildfix.patch | 20 + .../mageia.old/3rd-viahss-3.0-buildfix.patch | 19 + .../mageia.old/3rd-viahss-config.patch | 18 + .../3rd-viahss-module-license.patch | 7 + .../files/patches/mageia.old/3rdparty.series | 3 + ...vdev-do-not-report-errors-form-flush.patch | 64 + ...cc-Enable-autoload-of-pcc-cpufreq-fo.patch | 32 + .../acpi-CLEVO-M360S-disable_acpi_irq.patch | 28 + .../acpi-processor-M720SR-limit-to-C2.patch | 21 + ...over-ide-drivers-when-both-are-built.patch | 33 + ...ake-CFQ-default-to-IOPS-mode-on-SSDs.patch | 58 + ...fix-the-setting-of-IOPS-mode-on-SSDs.patch | 95 + .../block-floppy-disable-pnp-modalias.patch | 21 + .../char-agp-intel-new-Q57-id.patch | 35 + .../patches/mageia.old/dm-raid-aliases.patch | 20 + .../firewire-ieee1394-module-aliases.patch | 29 + ...ert-ext4-remove-block_device_ejected.patch | 53 + .../patches/mageia.old/fs-aufs4-modular.patch | 338 + .../files/patches/mageia.old/fs-aufs4.patch | 34146 ++++ .../mageia.old/gpu-drm-mach64-2.6.31.patch | 100 + .../gpu-drm-mach64-2.6.36-buildfix.patch | 27 + .../gpu-drm-mach64-2.6.37-buildfix.patch | 24 + .../gpu-drm-mach64-3.0-buildfix.patch | 44 + .../gpu-drm-mach64-3.12-buildfix.patch | 17 + .../gpu-drm-mach64-3.17-buildfix.patch | 31 + .../gpu-drm-mach64-3.18-buildfix.patch | 142 + .../gpu-drm-mach64-3.3-buildfix.patch | 47 + .../gpu-drm-mach64-3.6-buildfix.patch | 15 + .../gpu-drm-mach64-3.7-buildfix.patch | 61 + ...drm-mach64-fix-for-changed-drm_ioctl.patch | 20 + ...mach64-fix-for-changed-drm_pci_alloc.patch | 12 + .../mageia.old/gpu-drm-mach64-fixes.patch | 97 + .../gpu-drm-mach64-include-module.h.patch | 10 + .../gpu-drm-mach64-linux-3.14-buildfix.patch | 102 + ...pu-drm-mach64-restore-mach64_PCI_IDS.patch | 38 + .../patches/mageia.old/gpu-drm-mach64.patch | 4144 + ...hid-usbhid-IBM-BladeCenterHS20-quirk.patch | 37 + .../mageia.old/ide-pci-sis5513-965.patch | 23 + .../include-kbuild-export-pci_ids.patch | 19 + ...ks-for-Fujitsu-Lifebook-A544-and-Lif.patch | 47 + ...edia-usb-pwc-lie-in-proc-usb-devices.patch | 13 + .../patches/mageia.old/mpt-vmware-fix.patch | 49 + ...v4-netfilter-ipt_IFWLOG-3.6-buildfix.patch | 33 + ...net-netfilter-IFWLOG-2.6.35-buildfix.patch | 32 + ...net-netfilter-IFWLOG-2.6.37-buildfix.patch | 15 + .../net-netfilter-IFWLOG-3.7-buildfix.patch | 20 + .../mageia.old/net-netfilter-IFWLOG-mdv.patch | 264 + .../mageia.old/net-netfilter-IFWLOG.patch | 269 + .../net-netfilter-psd-2.6.35-buildfix.patch | 11 + .../mageia.old/net-netfilter-psd-mdv.patch | 233 + .../mageia.old/net-netfilter-psd.patch | 420 + .../net-sis190-fix-list-usage.patch | 30 + ...ks-for-Intel-9-series-PCH-root-ports.patch | 28 + ...I-M5229-ide-compatibility-mode-quirk.patch | 60 + .../pci-quirks-drop-devinit-exit.patch | 11 + .../platform-x86-add-shuttle-wmi-driver.patch | 1437 + ...rm-x86-shuttle-wmi-drop-devinit-exit.patch | 38 + ...r-dereference-in-RTPM-of-block-layer.patch | 74 + ...LL-pointer-dereference-in-runtime-PM.patch | 64 + .../scsi-megaraid-new-sysfs-name.patch | 15 + .../mageia.old/scsi-ppscsi-2.6.2.patch | 5083 + .../mageia.old/scsi-ppscsi-3.0-buildfix.patch | 19 + .../mageia.old/scsi-ppscsi-mdvbz45393.patch | 31 + .../scsi-ppscsi-sg-helper-update.patch | 36 + ...i-ppscsi-update-for-scsi_data_buffer.patch | 88 + .../mageia.old/scsi-ppscsi_fixes.patch | 226 + kernel/kernel/files/patches/mageia.old/series | 411 + ...k-setting-from-adav80x_regmap_config.patch | 0 ...ain-settings-of-fll-in-free-run-mode.patch | 0 ...-clock-ok-rather-than-use-interrupts.patch | 0 ...c-rt5640-fix-line-out-no-sound-issue.patch | 0 ...emove-redundant-arndale_audio_remove.patch | 0 ...4-fix-wrong-clock-for-exynos4x12-adc.patch | 0 ...istachio-correct-critical-clock-list.patch | 0 ...of-clk-pll-settings-from-boot-loader.patch | 0 ...xa-fix-core-frequency-reporting-unit.patch | 0 ...om-fix-msm8916-prng-clock-enable-bit.patch | 0 ...et-clk_set_rate_parent-on-ce1-clocks.patch | 0 ...-add-clk_set_rate_parent-to-sclk_mac.patch | 0 ...able-clk-s5pv210-add-missing-call-to.patch | 0 ...y-one-in-clk_sp810_timerclken_of_get.patch | 0 ...-testing-using-the-updated-testusb.c.patch | 0 ...al-link-to-be-configured-on-any-pipe.patch | 0 ...object-as-dirty-when-used-by-the-gpu.patch | 0 ...on-workaround-everywhere-on-pre-gen6.patch | 0 ...check-dp-link-status-on-long-hpd-too.patch | 0 ...s-for-reading-a-split-64bit-register.patch | 0 ...stable-drm-i915-preserve-ssc-earlier.patch | 0 ...m-qxl-validate-monitors-config-modes.patch | 0 ...n-atom-send-out-the-full-aux-address.patch | 0 ...layport-on-hpd-until-we-get-the-dpcd.patch | 0 ...uantization_range-for-pre-dce5-asics.patch | 0 ...native-send-out-the-full-aux-address.patch | 0 ...o-add-inverse-unit-conversion-macros.patch | 0 ...s16400-fix-adis16448-gyroscope-scale.patch | 0 ...able-iio-adis16480-fix-scale-factors.patch | 0 ...nd-iio_triggered_buffer-are-required.patch | 0 ...ative-error-code-from-iio_event_poll.patch | 0 ...fer-fix-iio_buffer_poll-return-value.patch | 0 ...-blocks-should-use-the-read-verifier.patch | 0 ...bit-to-access-vpd-through-function-0.patch | 0 ...n-0-quirk-for-intel-ethernet-devices.patch | 0 ...for-jmicron-multi-function-sata-ahci.patch | 0 ...able-pci-fix-ti816x-class-code-quirk.patch | 0 ...-don-t-return-int-on-__pm_clk_enable.patch | 0 .../stable-s390-sclp-fix-compile-error.patch | 0 ...stable-s390-setup-fix-novx-parameter.patch | 0 ...cpu_active_mask-cpu_online_mask-race.patch | 0 ...up-spi-mode-before-asserting-cs-gpio.patch | 0 ...vers-to-limit-data-i-o-to-word-sizes.patch | 0 ...fix-regression-in-spi-bitbang-txrx.h.patch | 0 ...-for-timeout-error-before-proceeding.patch | 0 ...g-spfi-fix-kbuild-test-robot-warning.patch | 0 ...i-fix-multiple-calls-to-request-gpio.patch | 0 ...x-fifo-size-to-64-word-from-256-word.patch | 0 ...i7x3x-fix-digital-output-on-pci-7230.patch | 0 ...n-t-clobber-ai_timer-in-command-test.patch | 0 ...n-t-clobber-ao_timer-in-command-test.patch | 0 ...fix-race-between-irq-and-set_termios.patch | 0 ...out-transfers-of-more-than-512-bytes.patch | 0 ...d-custom-pid-for-customware-products.patch | 0 ...maxpacketsize-according-to-bandwidth.patch | 0 ...6592-udc-forever-loop-in-set_feature.patch | 0 ...delete-useless-bus_to_hcd-conversion.patch | 0 ...3-fix-baud-rate-divisor-calculations.patch | 0 ...lt4111-lte-ev-do-hspa-gobi-4g-module.patch | 0 ...lserial-use-usb_get_serial_port_data.patch | 0 ...-correct-fpu-emulation-access-to-ldt.patch | 0 ...-ldt-access-in-single-stepping-logic.patch | 0 ...le-x86-ldt-further-fix-fpu-emulation.patch | 0 ...-x86-ldt-make-modify_ldt-synchronous.patch | 0 ...ory-corruption-for-btree-directories.patch | 0 ...fs-fix-xfs_attr_leafblock-definition.patch | 0 ...-unusual_devs-add-id-2.6.37-buildfix.patch | 46 + .../usb-storage-unusual_devs-add-id.patch | 55 + .../mageia.old/video-mageia-logo.patch | 949 + .../x86-boot-video-80x25-if-break.patch | 24 + .../x86-default_poweroff_up_machines.patch | 58 + ...inimum-vmalloc-area-by-64MB-to-192MB.patch | 28 + ...pci-toshiba-equium-a60-assign-busses.patch | 29 + .../arm-rockchip-fix-broken-build.patch | 42 + .../cpu-cacheinfo-Fix-teardown-path.patch | 81 + .../patches/mageia/fs-aufs-CVE-pending.patch | 115 + kernel/kernel/files/patches/mageia/series | 72 +- kernel/kernel/pspec.xml | 15 +- 176 files changed, 255038 insertions(+), 81 deletions(-) create mode 100644 kernel/kernel/files/patches/linux/patch-4.1.8.xz create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-0.92.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-2.6.35-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-3.0-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-config.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-module-license.patch create mode 100644 kernel/kernel/files/patches/mageia.old/3rdparty.series create mode 100644 kernel/kernel/files/patches/mageia.old/Input-evdev-do-not-report-errors-form-flush.patch create mode 100644 kernel/kernel/files/patches/mageia.old/Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch create mode 100644 kernel/kernel/files/patches/mageia.old/acpi-CLEVO-M360S-disable_acpi_irq.patch create mode 100644 kernel/kernel/files/patches/mageia.old/acpi-processor-M720SR-limit-to-C2.patch create mode 100644 kernel/kernel/files/patches/mageia.old/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch create mode 100644 kernel/kernel/files/patches/mageia.old/block-Make-CFQ-default-to-IOPS-mode-on-SSDs.patch create mode 100644 kernel/kernel/files/patches/mageia.old/block-cfq-iosched-fix-the-setting-of-IOPS-mode-on-SSDs.patch create mode 100644 kernel/kernel/files/patches/mageia.old/block-floppy-disable-pnp-modalias.patch create mode 100644 kernel/kernel/files/patches/mageia.old/char-agp-intel-new-Q57-id.patch create mode 100644 kernel/kernel/files/patches/mageia.old/dm-raid-aliases.patch create mode 100644 kernel/kernel/files/patches/mageia.old/firewire-ieee1394-module-aliases.patch create mode 100644 kernel/kernel/files/patches/mageia.old/fs-Revert-ext4-remove-block_device_ejected.patch create mode 100644 kernel/kernel/files/patches/mageia.old/fs-aufs4-modular.patch create mode 100644 kernel/kernel/files/patches/mageia.old/fs-aufs4.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.31.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.36-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.37-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.0-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.12-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.17-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.18-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.3-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.6-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.7-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_ioctl.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_pci_alloc.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fixes.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-include-module.h.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-linux-3.14-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-restore-mach64_PCI_IDS.patch create mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64.patch create mode 100644 kernel/kernel/files/patches/mageia.old/hid-usbhid-IBM-BladeCenterHS20-quirk.patch create mode 100644 kernel/kernel/files/patches/mageia.old/ide-pci-sis5513-965.patch create mode 100644 kernel/kernel/files/patches/mageia.old/include-kbuild-export-pci_ids.patch create mode 100644 kernel/kernel/files/patches/mageia.old/input-i8042-quirks-for-Fujitsu-Lifebook-A544-and-Lif.patch create mode 100644 kernel/kernel/files/patches/mageia.old/media-usb-pwc-lie-in-proc-usb-devices.patch create mode 100644 kernel/kernel/files/patches/mageia.old/mpt-vmware-fix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-ipv4-netfilter-ipt_IFWLOG-3.6-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.35-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.37-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-3.7-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-mdv.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-psd-2.6.35-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-psd-mdv.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-psd.patch create mode 100644 kernel/kernel/files/patches/mageia.old/net-sis190-fix-list-usage.patch create mode 100644 kernel/kernel/files/patches/mageia.old/pci-acs-quirks-for-Intel-9-series-PCH-root-ports.patch create mode 100644 kernel/kernel/files/patches/mageia.old/pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch create mode 100644 kernel/kernel/files/patches/mageia.old/pci-quirks-drop-devinit-exit.patch create mode 100644 kernel/kernel/files/patches/mageia.old/platform-x86-add-shuttle-wmi-driver.patch create mode 100644 kernel/kernel/files/patches/mageia.old/platform-x86-shuttle-wmi-drop-devinit-exit.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-megaraid-new-sysfs-name.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-2.6.2.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-3.0-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-mdvbz45393.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-sg-helper-update.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-update-for-scsi_data_buffer.patch create mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi_fixes.patch create mode 100644 kernel/kernel/files/patches/mageia.old/series rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-asoc-rt5640-fix-line-out-no-sound-issue.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-pistachio-correct-critical-clock-list.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-pxa-fix-core-frequency-reporting-unit.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-s5pv210-add-missing-call-to.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-i915-preserve-ssc-earlier.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-qxl-validate-monitors-config-modes.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-radeon-atom-send-out-the-full-aux-address.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-drm-radeon-native-send-out-the-full-aux-address.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-iio-add-inverse-unit-conversion-macros.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-iio-adis16480-fix-scale-factors.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-pci-fix-ti816x-class-code-quirk.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-s390-sclp-fix-compile-error.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-s390-setup-fix-novx-parameter.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-usb-symbolserial-use-usb_get_serial_port_data.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-x86-ldt-further-fix-fpu-emulation.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-x86-ldt-make-modify_ldt-synchronous.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch (100%) rename kernel/kernel/files/patches/{mageia => mageia.old}/stable-xfs-fix-xfs_attr_leafblock-definition.patch (100%) create mode 100644 kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id-2.6.37-buildfix.patch create mode 100644 kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id.patch create mode 100644 kernel/kernel/files/patches/mageia.old/video-mageia-logo.patch create mode 100644 kernel/kernel/files/patches/mageia.old/x86-boot-video-80x25-if-break.patch create mode 100644 kernel/kernel/files/patches/mageia.old/x86-default_poweroff_up_machines.patch create mode 100644 kernel/kernel/files/patches/mageia.old/x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch create mode 100644 kernel/kernel/files/patches/mageia.old/x86-pci-toshiba-equium-a60-assign-busses.patch create mode 100644 kernel/kernel/files/patches/mageia/arm-rockchip-fix-broken-build.patch create mode 100644 kernel/kernel/files/patches/mageia/cpu-cacheinfo-Fix-teardown-path.patch create mode 100644 kernel/kernel/files/patches/mageia/fs-aufs-CVE-pending.patch diff --git a/kernel/drivers/module-bbswitch/pspec.xml b/kernel/drivers/module-bbswitch/pspec.xml index 922cfc8d..d6bf5948 100644 --- a/kernel/drivers/module-bbswitch/pspec.xml +++ b/kernel/drivers/module-bbswitch/pspec.xml @@ -11,7 +11,7 @@ Kernel module allowing to switch dedicated graphics card on Optimus laptops kernel module allowing to switch dedicated graphics card on Optimus laptops - kernel-module-headers + kernel-module-headers https://github.com/Bumblebee-Project/bbswitch/archive/v0.8.tar.gz @@ -20,7 +20,7 @@ module-bbswitch - kernel + kernel /lib/modules @@ -34,6 +34,13 @@ + + 2015-09-23 + 0.8 + Rebuild for kernel-4.1.8. + Ertuğrul Erata + ertugrulerata@gmail.com + 2015-09-18 0.8 diff --git a/kernel/drivers/module-broadcom-wl/pspec.xml b/kernel/drivers/module-broadcom-wl/pspec.xml index 06dc2432..85888a5a 100644 --- a/kernel/drivers/module-broadcom-wl/pspec.xml +++ b/kernel/drivers/module-broadcom-wl/pspec.xml @@ -15,7 +15,7 @@ http://www.broadcom.com/docs/linux_sta/hybrid-v35-nodebug-pcoem-6_30_223_248.tar.gz http://www.broadcom.com/docs/linux_sta/hybrid-v35_64-nodebug-pcoem-6_30_223_248.tar.gz - kernel-module-headers + kernel-module-headers patch/linux-40.patch @@ -30,7 +30,7 @@ module-broadcom-wl - kernel + kernel module-broadcom-wl-userspace @@ -62,6 +62,13 @@ + + 2015-09-23 + 6.30.223.248 + Rebuild for kernel 4.1.8. + Ertuğrul Erata + ertugrulerata@gmail.com + 2015-09-18 6.30.223.248 diff --git a/kernel/drivers/module-virtualbox-guest/pspec.xml b/kernel/drivers/module-virtualbox-guest/pspec.xml index 734a0576..21fb6d31 100644 --- a/kernel/drivers/module-virtualbox-guest/pspec.xml +++ b/kernel/drivers/module-virtualbox-guest/pspec.xml @@ -14,14 +14,14 @@ This package provides the kernel modules needed for mouse integration and shared folder support between VirtualBox host and guest systems. http://source.pisilinux.org/1.0/module-virtualbox-guest-4.3.28.tar.xz - kernel-module-headers + kernel-module-headers module-virtualbox-guest - kernel + kernel baselayout module-virtualbox-guest-userspace @@ -52,7 +52,14 @@ - + + 2015-08-23 + 4.3.28 + Rebuild for kernel-4.1.8. + Ertuğrul Erata + ertugrulerata@gmail.com + + 2015-08-18 4.3.28 Rebuild for kernel-4.1.7. diff --git a/kernel/drivers/module-virtualbox/pspec.xml b/kernel/drivers/module-virtualbox/pspec.xml index 5b0de905..bf9bdafe 100644 --- a/kernel/drivers/module-virtualbox/pspec.xml +++ b/kernel/drivers/module-virtualbox/pspec.xml @@ -14,14 +14,14 @@ This package provides the kernel support for VirtualBox. http://source.pisilinux.org/1.0/module-virtualbox-4.3.28.tar.xz - kernel-module-headers + kernel-module-headers module-virtualbox - kernel + kernel module-virtualbox-userspace @@ -47,6 +47,13 @@ + + 2015-09-23 + 4.3.28 + Rebuild for kernel-4.1.8. + Ertuğrul Erata + ertugrulerata@gmail.com + 2015-09-18 4.3.28 diff --git a/kernel/drivers/ndiswrapper/pspec.xml b/kernel/drivers/ndiswrapper/pspec.xml index 52c2a0ee..ce9dd73c 100644 --- a/kernel/drivers/ndiswrapper/pspec.xml +++ b/kernel/drivers/ndiswrapper/pspec.xml @@ -15,7 +15,7 @@ module-ndiswrapper allows you to use Windows XP drivers for WLAN cards without proper Linux drivers. http://download.sourceforge.net/ndiswrapper/ndiswrapper-1.59.tar.gz - kernel-module-headers + kernel-module-headers support_kernel-4.0.patch @@ -28,7 +28,7 @@ ndiswrapper - kernel + kernel /lib/modules @@ -44,7 +44,14 @@ - + + 2014-09-23 + 1.59 + Rebuild for kernel-4.1.8. + PisiLinux Community + admins@pisilinux.org + + 2014-08-18 1.59 Rebuild for kernel-4.1.6. diff --git a/kernel/kernel/files/configs/kernel-x86_64-config b/kernel/kernel/files/configs/kernel-x86_64-config index 39d4fc93..e5d999ba 100644 --- a/kernel/kernel/files/configs/kernel-x86_64-config +++ b/kernel/kernel/files/configs/kernel-x86_64-config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 4.1.7 Kernel Configuration +# Linux/x86_64 4.1.8 Kernel Configuration # CONFIG_64BIT=y CONFIG_X86_64=y diff --git a/kernel/kernel/files/patches/linux/patch-4.1.8.xz b/kernel/kernel/files/patches/linux/patch-4.1.8.xz new file mode 100644 index 0000000000000000000000000000000000000000..c2d46113723ec120feb3825a26e45bef81607bbf GIT binary patch literal 256048 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

4(XRGD!7pkiN; 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 literal 0 HcmV?d00001 diff --git a/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch b/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch new file mode 100644 index 00000000..233389b4 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch @@ -0,0 +1,181 @@ + + 3rdparty/mkbuild.pl | 92 +++++++++++++++++++++++++++++++++++++++++++++ + Documentation/3rdparty.txt | 76 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 168 insertions(+) + +diff -Nurp linux-2.6.37/3rdparty/mkbuild.pl linux-2.6.37.3rdparty/3rdparty/mkbuild.pl +--- linux-2.6.37/3rdparty/mkbuild.pl 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/mkbuild.pl 2004-04-23 14:59:03.000000000 +0300 +@@ -0,0 +1,92 @@ ++#!/usr/bin/perl -w ++# ++# Version 1.0 ++# ++# Copyright 2001 Jeff Garzik ++# Copyright 2002 Juan Quintela ++# Copyright 2003 Nicolas Planel ++# ++# This software may be used and distributed according to the terms ++# of the GNU General Public License, incorporated herein by reference. ++# ++# ++# Run "mkbuild.pl" ++# ++# This program generates the following files ++# Makefile ++# Makefile.drivers ++# Config.in ++# using the information in the subdirs of this directory. ++# ++# subdirs need to have: ++# a Config.in file ++# a Makefile with a O_TARGET/L_TARGET targets ++# The config.in should set a CONFIG_ to m/y. ++ ++use strict; ++ ++opendir(THISDIR, "."); ++# get dirs without . and .. garbage ++my (@modules) = grep(!/\.\.?$/,grep(-d, readdir(THISDIR))); ++closedir(THISDIR); ++ ++generate_kconfig(@modules); ++generate_makefile(@modules); ++exit(0); ++ ++########################################################################## ++ ++sub generate_makefile { ++ my (@modules) = @_; ++ ++ local *F; ++ open F, "> Makefile" or die "Cannot create new Makefile: $!\n"; ++ print F <<'EOM'; ++# ++# THIS IS AN AUTOMATICALLY GENERATED FILE. DO NOT EDIT. ++# ++ ++EOM ++ printf F "obj- := 3rdparty.o # Dummy rule to force built-in.o to be made\n"; ++ printf F "obj-\$(%s) += %s\n", to_CONFIG($_), $_ . '/' foreach @modules; ++} ++ ++sub generate_kconfig { ++ my (@modules) = @_; ++ ++ local *F; ++ open F, "> Kconfig" or die "Cannot create Kconfig: $!\n"; ++ print F <<"EOM"; ++# ++# THIS IS AN AUTOMATICALLY GENERATED FILE. DO NOT EDIT. ++# ++ ++menu "Unofficial 3rd party kernel additions" ++ ++EOM ++ ++ foreach (@modules) { ++ die "No Kconfig in $_.\n" if ! -r "$_/Kconfig"; ++ print F "source 3rdparty/$_/Kconfig\n"; ++ } ++ print F "\n\nendmenu\n"; ++} ++ ++sub to_CONFIG { ++ local $_ = $_[0]; ++ tr/a-z/A-Z/; ++ s/[\-\. ]/_/g; ++ "CONFIG_$_"; ++} ++ ++sub find_target { ++ my ($module_dir) = @_; ++ ++ local *F; ++ open(F, "$module_dir/Makefile") or die "$module_dir/Makefile: $!\n"; ++ while () { ++ chomp; ++ return $1 if (/[LO]_TARGET.*:=\s+(\S+)/); ++ } ++} ++ +diff -Nurp linux-2.6.37/Documentation/3rdparty.txt linux-2.6.37.3rdparty/Documentation/3rdparty.txt +--- linux-2.6.37/Documentation/3rdparty.txt 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/Documentation/3rdparty.txt 2003-11-22 01:07:26.000000000 +0200 +@@ -0,0 +1,76 @@ ++ ++Third-Party Kernel Source Module Support, or ++an easy way to add modules to your kernel build. ++ ++ ++ ++Vendors quite often add additional drivers and features to the kernel ++which require nothing more than modifying Kconfig, Makefile, and ++adding one or more files to a sub-directory. As a single discrete task, ++this is not a problem. However, using patches to add modules to the ++kernel very often results in patch conflicts, resulting in needless time ++wastage as developers regenerate an otherwise working kernel patch. ++ ++This is designed as a solution to these problems. It is NOT designed as ++a replacement for the kernel build system, but merely as a tool for ++vendors and system administrators to ease the pain of patch management. ++ ++The key feature of this system is the distinct lack of patches. Drivers ++are installed via unpacking a tarball. ++ ++ ++ ++Adding a directory to the build (usually from a tarball) ++-------------------------------------------------------- ++If a directory exists inside the 3rdparty sub-directory that contains a ++proper Makefile, it can be added to the build. It also needs a ++Kconfig file. ++ ++ cd /usr/src/linux-2.4.3/3rdparty ++ bzcat /tmp/my-driver2.tar.bz2 | tar xf - # creates "my2" dir ++ ++ ++Limitations ++----------- ++There are some limitations to this system. This system is only ++designed to support a very common case. If you find yourself running ++into limitations (kernel build experts can spot them right off), ++then you should probably be patching the kernel instead of using ++mkbuild.pl for that particular module. ++ ++FIXME: actually list the limitations ++ ++ ++ ++Other notes ++----------- ++Link order is controlled by the order of mkbuild.pl executions. ++ ++"make mrproper" will erase Makefile.meta, and empty Kconfig, Makefile, ++and Makefile.drivers. ++ ++IMPORTANT NOTE: Because this feature modifies the kernel's makefiles and ++configuration system, you MUST complete all mkbuild.pl runs before ++running any "make" command. ++ ++Building in the 3rdparty dir ++---------------------------- ++ ++If you use modules that: ++ - are contained in one subdir with the name of the module ++ - has a Makefile ++ - has a Kconfig file ++ ++The system calls the ./mkbuild.pl script. It will search for ++subdirectories, and will try to build each of them as a module. ++Things to note: ++ ++ The dependencies will be done in a module called: ++ ++ 3rdparty// ++ ++depending of CONFIG_. ++ ++ is the value of O_TARGET/L_TARGET. ++ ++ diff --git a/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch b/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch new file mode 100644 index 00000000..8c1f8421 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch @@ -0,0 +1,181 @@ + +Include 3rdparty directory into the main build-system. + +Original author is unknown. +(Was either Juan Quintela or Jeff Garzik) + +Signed-off-by: Luiz Fernando N. Capitulino +Signed-off-by: Herton Ronaldo Krzesinski +Signed-off-by: Thomas Backlund + + Makefile | 2 +- + arch/alpha/Kconfig | 1 + + arch/ia64/Kconfig | 2 ++ + arch/mips/Kconfig | 2 ++ + arch/powerpc/Kconfig | 2 ++ + arch/sparc/Kconfig | 2 ++ + arch/x86/Kconfig | 2 ++ + scripts/kconfig/Makefile | 31 +++++++++++++++++-------------- + 8 files changed, 29 insertions(+), 15 deletions(-) + +diff -Nurp linux-4.1/arch/alpha/Kconfig linux-4.1-3rd/arch/alpha/Kconfig +--- linux-4.1/arch/alpha/Kconfig 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/arch/alpha/Kconfig 2015-06-25 20:44:03.221647317 +0200 +@@ -750,3 +750,4 @@ source "crypto/Kconfig" + + source "lib/Kconfig" + ++source "3rdparty/Kconfig" +diff -Nurp linux-4.1/arch/ia64/Kconfig linux-4.1-3rd/arch/ia64/Kconfig +--- linux-4.1/arch/ia64/Kconfig 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/arch/ia64/Kconfig 2015-06-25 20:44:03.231647227 +0200 +@@ -636,3 +636,5 @@ source "lib/Kconfig" + + config IOMMU_HELPER + def_bool (IA64_HP_ZX1 || IA64_HP_ZX1_SWIOTLB || IA64_GENERIC || SWIOTLB) ++ ++source "3rdparty/Kconfig" +diff -Nurp linux-4.1/arch/mips/Kconfig linux-4.1-3rd/arch/mips/Kconfig +--- linux-4.1/arch/mips/Kconfig 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/arch/mips/Kconfig 2015-06-25 20:44:03.231647227 +0200 +@@ -2893,3 +2893,5 @@ source "crypto/Kconfig" + source "lib/Kconfig" + + source "arch/mips/kvm/Kconfig" ++ ++source "3rdparty/Kconfig" +diff -Nurp linux-4.1/arch/powerpc/Kconfig linux-4.1-3rd/arch/powerpc/Kconfig +--- linux-4.1/arch/powerpc/Kconfig 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/arch/powerpc/Kconfig 2015-06-25 20:44:03.231647227 +0200 +@@ -1090,3 +1090,5 @@ config PPC_LIB_RHEAP + bool + + source "arch/powerpc/kvm/Kconfig" ++ ++source "3rdparty/Kconfig" +diff -Nurp linux-4.1/arch/sparc/Kconfig linux-4.1-3rd/arch/sparc/Kconfig +--- linux-4.1/arch/sparc/Kconfig 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/arch/sparc/Kconfig 2015-06-25 20:44:03.231647227 +0200 +@@ -569,3 +569,5 @@ source "security/Kconfig" + source "crypto/Kconfig" + + source "lib/Kconfig" ++ ++source "3rdparty/Kconfig" +diff -Nurp linux-4.1/arch/x86/Kconfig linux-4.1-3rd/arch/x86/Kconfig +--- linux-4.1/arch/x86/Kconfig 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/arch/x86/Kconfig 2015-06-25 20:44:03.231647227 +0200 +@@ -2594,3 +2594,5 @@ source "crypto/Kconfig" + source "arch/x86/kvm/Kconfig" + + source "lib/Kconfig" ++ ++source "3rdparty/Kconfig" +diff -Nurp linux-4.1/Makefile linux-4.1-3rd/Makefile +--- linux-4.1/Makefile 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/Makefile 2015-06-25 20:44:03.231647227 +0200 +@@ -556,7 +556,7 @@ scripts: scripts_basic include/config/au + + # Objects we will link into vmlinux / subdirs we need to visit + init-y := init/ +-drivers-y := drivers/ sound/ firmware/ ++drivers-y := drivers/ sound/ firmware/ 3rdparty/ + net-y := net/ + libs-y := lib/ + core-y := usr/ +diff -Nurp linux-4.1/scripts/kconfig/Makefile linux-4.1-3rd/scripts/kconfig/Makefile +--- linux-4.1/scripts/kconfig/Makefile 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-3rd/scripts/kconfig/Makefile 2015-06-25 20:47:50.109596717 +0200 +@@ -18,26 +18,26 @@ endif + # We need this, in case the user has it in its environment + unexport CONFIG_ + +-xconfig: $(obj)/qconf ++xconfig: $(obj)/qconf 3rdparty/Makefile + $< $(silent) $(Kconfig) + +-gconfig: $(obj)/gconf ++gconfig: $(obj)/gconf 3rdparty/Makefile + $< $(silent) $(Kconfig) + +-menuconfig: $(obj)/mconf ++menuconfig: $(obj)/mconf 3rdparty/Makefile + $< $(silent) $(Kconfig) + +-config: $(obj)/conf ++config: $(obj)/conf 3rdparty/Makefile + $< $(silent) --oldaskconfig $(Kconfig) + +-nconfig: $(obj)/nconf ++nconfig: $(obj)/nconf 3rdparty/Makefile + $< $(silent) $(Kconfig) + +-silentoldconfig: $(obj)/conf ++silentoldconfig: $(obj)/conf 3rdparty/Makefile + $(Q)mkdir -p include/config include/generated + $< $(silent) --$@ $(Kconfig) + +-localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf ++localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf 3rdparty/Makefile + $(Q)mkdir -p include/config include/generated + $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config + $(Q)if [ -f .config ]; then \ +@@ -80,7 +80,7 @@ simple-targets := oldconfig allnoconfig + alldefconfig randconfig listnewconfig olddefconfig + PHONY += $(simple-targets) + +-$(simple-targets): $(obj)/conf ++$(simple-targets): $(obj)/conf 3rdparty/Makefile + $< $(silent) --$@ $(Kconfig) + + PHONY += oldnoconfig savedefconfig defconfig +@@ -88,12 +88,12 @@ PHONY += oldnoconfig savedefconfig defco + # oldnoconfig is an alias of olddefconfig, because people already are dependent + # on its behavior(sets new symbols to their default value but not 'n') with the + # counter-intuitive name. +-oldnoconfig: olddefconfig ++oldnoconfig: olddefconfig 3rdparty/Makefile + +-savedefconfig: $(obj)/conf ++savedefconfig: $(obj)/conf 3rdparty/Makefile + $< $(silent) --$@=defconfig $(Kconfig) + +-defconfig: $(obj)/conf ++defconfig: $(obj)/conf 3rdparty/Makefile + ifeq ($(KBUILD_DEFCONFIG),) + $< $(silent) --defconfig $(Kconfig) + else +@@ -101,7 +101,7 @@ else + $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) + endif + +-%_defconfig: $(obj)/conf ++%_defconfig: $(obj)/conf 3rdparty/Makefile + $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) + + configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) +@@ -112,12 +112,12 @@ configfiles=$(wildcard $(srctree)/kernel + +$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig + + PHONY += kvmconfig +-kvmconfig: kvm_guest.config ++kvmconfig: kvm_guest.config 3rdparty/Makefile + @: + + PHONY += tinyconfig + tinyconfig: +- $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config ++ $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config 3rdparty/Makefile + + # Help text used by make help + help: +@@ -176,6 +176,9 @@ gconf-objs := gconf.o zconf.tab.o + + hostprogs-y := conf nconf mconf kxgettext qconf gconf + ++3rdparty/Makefile: ++ pushd $(srctree)/3rdparty ; $(PERL) ./mkbuild.pl ; popd ++ + clean-files := qconf.moc .tmp_qtcheck .tmp_gtkcheck + clean-files += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h + clean-files += config.pot linux.pot diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch new file mode 100644 index 00000000..5d61570f --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch @@ -0,0 +1,4212 @@ + 3rdparty/acerhk/AUTHORS | 1 + 3rdparty/acerhk/COPYING | 340 ++++ + 3rdparty/acerhk/INSTALL | 109 + + 3rdparty/acerhk/Makefile | 65 + 3rdparty/acerhk/NEWS | 16 + 3rdparty/acerhk/README | 215 ++ + 3rdparty/acerhk/acerhk.c | 2999 ++++++++++++++++++++++++++++++++++++++++ + 3rdparty/acerhk/acerhk.h | 91 + + 3rdparty/acerhk/doc/FAQ | 150 ++ + 3rdparty/acerhk/doc/IOCTL | 61 + 3rdparty/acerhk/doc/acertm.def | 56 + 3rdparty/acerhk/doc/keycodes | 26 + 3rdparty/acerhk/doc/md95400.def | 14 + 13 files changed, 4143 insertions(+) +diff -Nurp linux-2.6.37/3rdparty/acerhk/acerhk.c linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.c +--- linux-2.6.37/3rdparty/acerhk/acerhk.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.c 2007-02-10 17:46:23.000000000 +0200 +@@ -0,0 +1,2999 @@ ++/********************************************************************* ++ * Filename: acerhk.c ++ * Version: 0.5 ++ * ++ * Copyright (C) 2002-2006, Olaf Tauber (olaf-tauber@versanet.de) ++ * ++ * Description: kernel driver for Acer Travelmate and similar ++ * laptops special keys ++ * Author: Olaf Tauber ++ * Created at: Mon Apr 29 22:16:42 2002 ++ * Modified at: Mon Oct 16 21:36:44 2006 ++ * Modified by: Olaf Tauber ++ * Modified at: Thu Nov 24 13:03:01 2005 ++ * Modified by: Antonio Cuni ++ * Modified at: Wed Oct 27 19:47:11 CEST 2004 ++ * Modified by: Joachim Fenkes ++ * ++ * This program is free software; you can redistribute ++ * it and/or modify it under the terms of the GNU General ++ * Public License as published by the Free Software ++ * Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * 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., 59 Temple Place, ++ * Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ++ */ ++ ++#ifndef AUTOCONF_INCLUDED ++#include ++#endif ++ ++/* This driver is heavily dependent on the architecture, don't let ++ * anyone without an X86 machine use it. On laptops with AMD64 ++ * architecture this driver is only useable in 32 bit mode. ++ */ ++#ifdef CONFIG_X86 ++ ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#define KERNEL26 ++#include ++#else ++#include ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) ++#define STATIC_INPUT_DEV ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "acerhk.h" ++ ++/* #define ACERDEBUG */ ++/* #define DUMMYHW */ ++ ++#define ACERHK_VERSION "0.5.35" ++#define MODULE_NAME "acerhk" ++ ++/* maximum number of polling loops, adjust it if needed to values between ++ * 1 and 32 ++ */ ++#define MAX_POLLING_LOOPS 16U ++ ++/* maximum length for model string */ ++#define ACERHK_MODEL_STRLEN 16 ++/* size of mapped areas */ ++#define AREA_SIZE 0xffff ++/* needed for colussi algorithm */ ++#define XSIZE 20 ++ ++/* Module parameters */ ++static int poll=1; ++static int autowlan; ++static int usedritek=1; ++static int wlan_state=-1; ++static int bluetooth_state=-1; ++static int verbose; ++static unsigned int force_series; ++#ifdef KERNEL26 ++module_param(poll, int, 0444); ++module_param(autowlan, int, 0444); ++module_param(usedritek, int, 0444); ++module_param(verbose, int, 0444); ++module_param(wlan_state, int, 0444); ++module_param(bluetooth_state, int, 0444); ++module_param(force_series, uint, 0444); ++#else ++MODULE_PARM(poll, "i"); ++MODULE_PARM(autowlan, "i"); ++MODULE_PARM(wlan_state, "i"); ++MODULE_PARM(bluetooth_state, "i"); ++MODULE_PARM(usedritek, "i"); ++MODULE_PARM(verbose, "i"); ++MODULE_PARM(force_series, "i"); ++#endif ++MODULE_PARM_DESC(poll, "start polling timer"); ++MODULE_PARM_DESC(autowlan, "automatic switching of wlan hardware"); ++MODULE_PARM_DESC(wlan_state, "(assumed) initial state of WLAN LED/hardware"); ++MODULE_PARM_DESC(bluetooth_state, "(assumed) initial state of Bluetooth LED/hardware"); ++MODULE_PARM_DESC(usedritek, "enable dritek keyboard extension"); ++MODULE_PARM_DESC(verbose, "output additional information"); ++MODULE_PARM_DESC(force_series, "force laptop series, skip autodetection"); ++ ++/* input device */ ++static struct input_dev *acerhk_input_dev_ptr; ++#ifdef STATIC_INPUT_DEV ++static struct input_dev acerhk_input_dev; ++#endif ++ ++/* mapped IO area from 0xf0000 */ ++static void *reg1; ++/* mapped IO area from 0xe0000 */ ++static void *reg2; ++/* Pointer to mapped area at 0x400 on 520 series */ ++static void *preg400; ++/* location of IO routine in mapped area */ ++static unsigned int bios_routine; ++/* index of CMOS port to get key event */ ++static unsigned int cmos_index; ++/* function for bios call */ ++static bios_call call_bios; ++/* address of model string */ ++static char *acerhk_model_addr; ++/* copied string, maximum length 16 ('TravelMate xxx') */ ++static char acerhk_model_string[ACERHK_MODEL_STRLEN]; ++/* type of hardware access */ ++static t_acer_type acerhk_type; ++/* travelmate series */ ++static unsigned int acerhk_series; ++/* supported features for this model */ ++static unsigned int acerhk_model_features; ++/* map of acer key codes to acer key names */ ++static unsigned char acerhk_key2name[0xff]; ++/* map of acer key names to key events */ ++static t_map_name2event acerhk_name2event; ++/* timer for polling key presses */ ++static struct timer_list acerhk_timer_poll; ++/* polling active */ ++static int acerhk_polling_state; ++/* polling delay */ ++static unsigned acerhk_polling_delay = HZ/5; ++/* wlan hardware toggle */ ++static int acerhk_wlan_state; ++/* bluetooth hardware toggle */ ++static int acerhk_bluetooth_state; ++ ++/* bluetooth blinking state; added by Antonio Cuni ++ possible values: ++ -1: blinking disabled (default) ++ 0: blinking enabled, led currently off ++ 1: blinking enabled, led currently on ++*/ ++static int acerhk_blueled_blinking = -1; ++/* delay between two changes of state, in jiffies */ ++static unsigned acerhk_blueled_blinking_delay; ++/* timer for blinking */ ++static struct timer_list acerhk_timer_blinking; ++ ++/* function prototypes */ ++static void start_polling(void); ++static void stop_polling(void); ++ ++/* Added by Antonio Cuni */ ++static void start_blinking(void); ++static void stop_blinking(void); ++ ++/* {{{ Experimental use of dritek keyboard extension */ ++ ++#define EC_STATUS_REG 0x66 /* Status register of EC (R) */ ++#define EC_CNTL_REG 0x66 /* Controller command register of EC (W) */ ++#define EC_DATA_REG 0x62 /* EC data register (R/W) */ ++ ++#ifdef KERNEL26 ++ ++#include ++ ++#define KBD_STATUS_REG 0x64 /* Status register (R) */ ++#define KBD_CNTL_REG 0x64 /* Controller command register (W) */ ++#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */ ++ ++#else ++ ++#ifndef KEY_MEDIA ++#define KEY_MEDIA 226 ++#endif ++ ++#define preempt_disable() do { } while (0) ++#define preempt_enable_no_resched() do { } while (0) ++#define preempt_enable() do { } while (0) ++#define preempt_check_resched() do { } while (0) ++#include ++ ++#endif ++ ++static inline int my_i8042_read_status(void) ++{ ++ return inb(KBD_STATUS_REG); ++} ++static int my_i8042_wait_write(void) ++{ ++ int i = 0; ++ while ((my_i8042_read_status() & 0x02) && (i < 10000)) { ++ udelay(50); ++ i++; ++ } ++ return -(i == 10000); ++} ++static void send_kbd_cmd(unsigned char cmd, unsigned char val) ++{ ++ if (usedritek) { ++ preempt_disable(); ++ if (!my_i8042_wait_write()) ++ outb(cmd, KBD_CNTL_REG); ++ if (!my_i8042_wait_write()) ++ outb(val, KBD_DATA_REG); ++ preempt_enable_no_resched(); ++ } else { ++ printk(KERN_INFO"acerhk: request for accessing EC ignored\n" ++ KERN_INFO"acerhk: Use of dritek keyboard extension not enabled, use module\n" ++ KERN_INFO"acerhk: parameter usedritek=1 to do that (possibly dangerous)\n"); ++ } ++} ++#ifdef ACERDEBUG ++static inline int my_i8042_read_ecstatus(void) ++{ ++ return inb(EC_STATUS_REG); ++} ++static int my_i8042_wait_ecwrite(void) ++{ ++ int i = 0; ++ while ((my_i8042_read_ecstatus() & 0x02) && (i < 10000)) { ++ udelay(50); ++ i++; ++ } ++ return -(i == 10000); ++} ++static void send_ec_cmd(unsigned char cmd, unsigned char val) ++{ ++ if (usedritek) { ++ preempt_disable(); ++ if (!my_i8042_wait_ecwrite()) ++ outb(cmd, EC_CNTL_REG); ++ if (!my_i8042_wait_ecwrite()) ++ outb(val, EC_DATA_REG); ++ preempt_enable_no_resched(); ++ } else { ++ printk(KERN_INFO"acerhk: request for accessing EC ignored\n" ++ KERN_INFO"acerhk: Use of dritek keyboard extension not enabled, use module\n" ++ KERN_INFO"acerhk: parameter usedritek=1 to do that (possibly dangerous)\n"); ++ } ++} ++#endif ++#ifdef ACERDEBUG ++static void enable_mute_led_ec(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling mute led via EC\n"); ++ send_kbd_cmd(0x59, 0x94); ++} ++static void disable_mute_led_ec(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling mute led via EC\n"); ++ send_kbd_cmd(0x59, 0x95); ++} ++static void enable_dmm_function(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 2\n"); ++ send_kbd_cmd(0x45, 0xd3); ++} ++#endif ++static void enable_wlan_ec_1(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 1\n"); ++ send_kbd_cmd(0xe7, 0x01); ++ acerhk_wlan_state = 1; ++} ++static void disable_wlan_ec_1(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling WLAN via EC variant 1\n"); ++ send_kbd_cmd(0xe7, 0x00); ++ acerhk_wlan_state = 0; ++} ++static void enable_bluetooth_ec_1(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling Bluetooth via EC variant 1\n"); ++ send_kbd_cmd(0xe7, 0x03); ++ acerhk_bluetooth_state = 1; ++} ++static void disable_bluetooth_ec_1(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling Bluetooth via EC variant 1\n"); ++ send_kbd_cmd(0xe7, 0x02); ++ acerhk_bluetooth_state = 0; ++} ++static void enable_wlan_ec_2(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 2\n"); ++ send_kbd_cmd(0x45, acerhk_bluetooth_state ? 0xa2 : 0xa0); ++ acerhk_wlan_state = 1; ++} ++static void disable_wlan_ec_2(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling WLAN via EC variant 2\n"); ++ send_kbd_cmd(0x45, acerhk_bluetooth_state ? 0xa1 : 0xa3); ++ acerhk_wlan_state = 0; ++} ++static void enable_bluetooth_ec_2(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling Bluetooth via EC variant 2\n"); ++ send_kbd_cmd(0x45, acerhk_wlan_state ? 0xa2 : 0xa1); ++ acerhk_bluetooth_state = 1; ++} ++static void disable_bluetooth_ec_2(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling Bluetooth via EC variant 2\n"); ++ send_kbd_cmd(0x45, acerhk_wlan_state ? 0xa0 : 0xa3); ++ acerhk_bluetooth_state = 0; ++} ++#ifdef ACERDEBUG ++static void enable_wireless_ec(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling wireless hardware\n"); ++ if (usedritek) { ++ preempt_disable(); ++ if (!my_i8042_wait_ecwrite()) ++ outb(0x4d, EC_CNTL_REG); ++ if (!my_i8042_wait_ecwrite()) ++ outb(0xd2, EC_DATA_REG); ++ if (!my_i8042_wait_ecwrite()) ++ outb(0x01, EC_DATA_REG); ++ preempt_enable_no_resched(); ++ } ++ acerhk_wlan_state = 1; ++} ++static void disable_wireless_ec(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling wireless hardware\n"); ++ if (usedritek) { ++ preempt_disable(); ++ if (!my_i8042_wait_ecwrite()) ++ outb(0x4d, EC_CNTL_REG); ++ if (!my_i8042_wait_ecwrite()) ++ outb(0xd2, EC_DATA_REG); ++ if (!my_i8042_wait_ecwrite()) ++ outb(0x00, EC_DATA_REG); ++ preempt_enable_no_resched(); ++ } ++ acerhk_wlan_state = 0; ++} ++#endif ++static void enable_dritek_keyboard(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling dritek keyboard extension\n"); ++ send_kbd_cmd(0x59, 0x90); ++} ++static void disable_dritek_keyboard(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling dritek keyboard extension\n"); ++ send_kbd_cmd(0x59, 0x91); ++} ++static void enable_mail_led_ec_1(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling mail led via EC variant 1\n"); ++ send_kbd_cmd(0xe8, 0x01); ++} ++static void disable_mail_led_ec_1(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling mail led via EC variant 1\n"); ++ send_kbd_cmd(0xe8, 0x00); ++} ++ ++static void enable_mail_led_ec_2(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling mail led via EC variant 2\n"); ++ send_kbd_cmd(0x59, 0x92); ++} ++static void disable_mail_led_ec_2(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling mail led via EC variant 2\n"); ++ send_kbd_cmd(0x59, 0x93); ++} ++static void enable_mail_led_ec_3(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: enabling mail led via EC variant 3\n"); ++ if (usedritek) { ++ preempt_disable(); ++ if (!my_i8042_wait_write()) ++ outl(0x80008894, 0xCF8); ++ if (!my_i8042_wait_write()) ++ outw(0xC061, 0xCFC); ++ preempt_enable_no_resched(); ++ } ++} ++static void disable_mail_led_ec_3(void) ++{ ++ if (verbose) ++ printk(KERN_INFO "acerhk: disabling mail led via EC variant 3\n"); ++ if (usedritek) { ++ preempt_disable(); ++ if (!my_i8042_wait_write()) ++ outl(0x80008894, 0xCF8); ++ if (!my_i8042_wait_write()) ++ outw(0xC060, 0xCFC); ++ preempt_enable_no_resched(); ++ } ++} ++ ++/* }}} */ ++ ++/* {{{ string search functions */ ++ ++/* This is the Colussi algorithm, the code is taken from ++ http://www-igm.univ-mlv.fr/~lecroq/string ++*/ ++int preColussi(char *x, int m, int *h, int *next, int *shift) ++{ ++ int i, k, nd, q, r, s; ++ int hmax[XSIZE], kmin[XSIZE], nhd0[XSIZE], rmin[XSIZE]; ++ /* Computation of hmax */ ++ i = k = 1; ++ do { ++ while (x[i] == x[i - k]) ++ i++; ++ hmax[k] = i; ++ q = k + 1; ++ while (hmax[q - k] + k < i) { ++ hmax[q] = hmax[q - k] + k; ++ q++; ++ } ++ k = q; ++ if (k == i + 1) ++ i = k; ++ } while (k <= m); /* Computation of kmin */ ++ memset(kmin, 0, m*sizeof(int)); ++ r = 0; ++ for (i = m; i >= 1; --i) ++ if (hmax[i] < m) ++ kmin[hmax[i]] = i; /* Computation of rmin */ ++ for (i = m - 1; i >= 0; --i) { ++ if (hmax[i + 1] == m) ++ r = i + 1; ++ if (kmin[i] == 0) ++ rmin[i] = r; ++ else ++ rmin[i] = 0; ++ } /* Computation of h */ ++ s = -1; ++ r = m; ++ for (i = 0; i < m; ++i) ++ if (kmin[i] == 0) ++ h[--r] = i; ++ else ++ h[++s] = i; ++ nd = s; /* Computation of shift */ ++ for (i = 0; i <= nd; ++i) ++ shift[i] = kmin[h[i]]; ++ for (i = nd + 1; i < m; ++i) ++ shift[i] = rmin[h[i]]; ++ shift[m] = rmin[0]; /* Computation of nhd0 */ ++ s = 0; ++ for (i = 0; i < m; ++i) { ++ nhd0[i] = s; ++ if (kmin[i] > 0) ++ ++s; ++ } /* Computation of next */ ++ for (i = 0; i <= nd; ++i) ++ next[i] = nhd0[h[i] - kmin[h[i]]]; ++ for (i = nd + 1; i < m; ++i) ++ next[i] = nhd0[m - rmin[h[i]]]; ++ next[m] = nhd0[m - rmin[h[m - 1]]]; return(nd); ++} ++ ++int COLUSSI(char *x, int m, char *y, int n) { ++ int i, j, last, nd, ++ h[XSIZE], next[XSIZE], shift[XSIZE]; /* Processing */ ++ int match_pos; /* position of first match */ ++ nd = preColussi(x, m, h, next, shift); /* Searching */ ++ i = j = 0; ++ last = -1; ++ match_pos = -1; ++ while ( (match_pos == -1) ++ && (j <= n - m) ) { ++ while (i < m && last < j + h[i] && ++ x[h[i]] == y[j + h[i]]) ++ i++; ++ if (i >= m || last >= j + h[i]) { ++ /* Match found, bail out */ ++ match_pos = j; ++ i = m; ++ } ++ if (i > nd) ++ last = j + m - 1; ++ j += shift[i]; ++ i = next[i]; ++ } ++ return match_pos; ++} ++ ++/* }}} */ ++ ++/* {{{ hardware access functions */ ++ ++/* call_bios_ ++ * ++ * call request handler in mapped system rom ++ * ++ * the request is handed over via all 6 general purpose registers, results are ++ * taken from them and copied back to buf ++ */ ++static asmlinkage void call_bios_6xx(struct register_buffer *buf) ++{ ++ if (bios_routine) { ++ local_irq_disable(); ++ __asm__ __volatile__( ++ "movl %1,%%edx\n\t" ++ "pusha\n\t" ++ "movl %%edx,%%ebp\n\t" ++ "movl (%%ebp),%%eax\n\t" ++ "movl 4(%%ebp),%%ebx\n\t" ++ "movl 8(%%ebp),%%ecx\n\t" ++ "movl 12(%%ebp),%%edx\n\t" ++ "movl 16(%%ebp),%%edi\n\t" ++ "movl 20(%%ebp),%%esi\n\t" ++ "pushl %%ebp\n\t" ++ "call *%0\n\t" ++ "popl %%ebp\n\t" ++ "movl %%eax, (%%ebp)\n\t" ++ "movl %%ebx, 4(%%ebp)\n\t" ++ "movl %%ecx, 8(%%ebp)\n\t" ++ "movl %%edx, 12(%%ebp)\n\t" ++ "movl %%edi, 16(%%ebp)\n\t" ++ "movl %%esi, 20(%%ebp)\n\t" ++ "popa\n\t" ++ : ++ :"m" (bios_routine), "m" (buf) ++ :"%eax", "%ebx", "%ecx", "%edx", "%edi", "%esi", "%ebp" ++ ); ++ local_irq_enable(); ++ } ++} ++ ++static asmlinkage void call_bios_52x(struct register_buffer *buf) ++{ ++ if (bios_routine) { ++ local_irq_disable(); ++ __asm__ __volatile__( ++ "movl %2,%%edx\n\t" ++ "pusha\n\t" ++ "movl %%edx,%%ebp\n\t" ++ "movl (%%ebp),%%eax\n\t" ++ "movl 4(%%ebp),%%ebx\n\t" ++ "movl 8(%%ebp),%%ecx\n\t" ++ "movl 12(%%ebp),%%edx\n\t" ++ "movl 16(%%ebp),%%edi\n\t" ++ "movl 20(%%ebp),%%esi\n\t" ++ "pushl %%ebp\n\t" ++ "movl %1, %%ebp\n\t" ++ "call *%0\n\t" ++ "popl %%ebp\n\t" ++ "movl %%eax, (%%ebp)\n\t" ++ "movl %%ebx, 4(%%ebp)\n\t" ++ "movl %%ecx, 8(%%ebp)\n\t" ++ "movl %%edx, 12(%%ebp)\n\t" ++ "movl %%edi, 16(%%ebp)\n\t" ++ "movl %%esi, 20(%%ebp)\n\t" ++ "popa\n\t" ++ : ++ :"m" (bios_routine), "m" (preg400), "m" (buf) ++ :"%eax", "%ebx", "%ecx", "%edx", "%edi", "%esi", "%ebp" ++ ); ++ local_irq_enable(); ++ } ++} ++ ++#define PRINT_BUFFER(x) \ ++ printk(KERN_INFO"acerhk: eax=0x%x ebx=0x%x ecx=0x%x edx=0x%x\n" \ ++ "acerhk: edi=0x%x esi=0x%x ebp=0x%x\n", \ ++ x.eax, x.ebx, x.ecx, x.edx, x.edi, x.esi, x.ebp); ++ ++/* get_fnkey_event ++ * ++ * gets the first (oldest) key id from the queue of events ++ * ++ * return value: id of key ++ */ ++static int get_fnkey_event(void) ++{ ++ struct register_buffer regs; ++ regs.eax = 0x9610; ++ regs.ebx = 0x61C; ++ /* clear other registers, some models need this */ ++ regs.ecx = 0; ++ regs.edx = 0; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ return regs.eax & 0xffff; ++} ++ ++/* get_thermal_event ++ * ++ * does what? ++ * ++ * return value: event ? ++ */ ++static int get_thermal_event(void) ++{ ++ struct register_buffer regs; ++ if (acerhk_model_features & TM_F_THERMAL) { ++ regs.eax = 0x9612; ++ regs.ebx = 0x12e; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: thermal event = 0x%x\n", regs.eax); ++ } else { ++ regs.eax = 0x00; ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: thermal event not supported\n"); ++ } ++ return regs.eax & 0xffff; ++} ++ ++#ifdef ACERDEBUG ++/* pbutton_fct ++ * ++ * does what? ++ * ++ * return value: ? ++ */ ++static int pbutton_fct(void) ++{ ++ struct register_buffer regs; ++ if (acerhk_model_features & TM_F_PBUTTON) { ++ regs.eax = 0x9612; ++ regs.ebx = 0x10b; ++ regs.ecx = 0x2; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: pbutton = 0x%x\n", regs.eax); ++ } else { ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: pbutton function not supported\n"); ++ regs.eax = 0x00; ++ } ++ return regs.eax & 0xffff; ++} ++#endif ++ ++/* wbutton_fct_1 ++ * ++ * turn on installed Bluetooth hardware together with the corresponding LED ++ * ++ * val: 0 turns off the LED ++ * 1 turns the LED to green/blue ++ * ++ * return value: ? ++ */ ++static int wbutton_fct_1(int val) ++{ ++ struct register_buffer regs; ++ if (acerhk_model_features & TM_F_WBUTTON) { ++ acerhk_bluetooth_state = val; ++ regs.eax = 0x9610; ++ regs.ebx = ((val & 0xff) << 8) | 0x34; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: wbutton1 = 0x%x\n", regs.eax); ++ } else { ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: wbutton function 1 not supported\n"); ++ regs.eax = 0x00; ++ } ++ return regs.eax & 0xffff; ++} ++ ++/* wbutton_fct_2 ++ * ++ * turn on installed WLAN hardware together with the corresponding LED ++ * ++ * val: 0 turns off the LED ++ * 1 turns the LED to orange ++ * ++ * return value: ? ++ */ ++static int wbutton_fct_2(int val) ++{ ++ struct register_buffer regs; ++ if (acerhk_model_features & TM_F_WBUTTON) { ++ acerhk_wlan_state = val; ++ regs.eax = 0x9610; ++ regs.ebx = ((val & 0xff) << 8) | 0x35; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: wbutton2 = 0x%x\n", regs.eax); ++ } else { ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: wbutton function 2 not supported\n"); ++ regs.eax = 0x00; ++ } ++ return regs.eax & 0xffff; ++} ++ ++/* get_cmos_index ++ * ++ * gets index of CMOS port from ROM. The number of events is monitored ++ * in this port. ++ * ++ * return value: index of CMOS port ++ */ ++static int get_cmos_index(void) ++{ ++ struct register_buffer regs; ++ regs.eax = 0x9610; ++ regs.ebx = 0x51C; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ cmos_index = regs.ecx & 0xff; ++ if (verbose) ++ printk(KERN_INFO"acerhk: cmos index set to 0x%x\n", cmos_index); ++ return cmos_index; ++} ++ ++/* get_nr_events ++ * ++ * gets the number of cached events (keys pressed) in queue. Up to 31 events ++ * are cached. ++ * ++ * return value: number of events in queue ++ */ ++static int get_nr_events(void) ++{ ++ unsigned long flags; ++ unsigned char c = 0; ++ ++ spin_lock_irqsave (&rtc_lock, flags); ++#ifndef DUMMYHW ++ if (cmos_index) ++ c = CMOS_READ(cmos_index); ++ else if (verbose > 3) ++ printk(KERN_INFO"acerhk: get_nr_events - no valid cmos index set\n"); ++#endif ++ spin_unlock_irqrestore (&rtc_lock, flags); ++ return c; ++} ++ ++/* set_mail_led ++ * ++ * change state of mail led ++ * ++ * val: 0 - switch led off ++ * 1 - switch led on (blinking) ++ * ++ * return value: 1 - action succesfull (val valid) ++ * 0 - no action taken (val invalid) ++ */ ++static int set_mail_led(int val) ++{ ++ struct register_buffer regs; ++ if (acerhk_model_features & TM_F_MAIL_LED) { ++ regs.eax = 0x9610; ++ regs.ebx = ((val & 0xff) << 8) | 0x31; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: mail led set to = 0x%x\n", val); ++ } else if (acerhk_model_features & TM_F_MAIL_LED_EC) { ++ if (val == 1) ++ enable_mail_led_ec_1(); ++ else if (val == 0) ++ disable_mail_led_ec_1(); ++ } else if (acerhk_model_features & TM_F_MAIL_LED_EC2) { ++ if (val == 1) ++ enable_mail_led_ec_2(); ++ else if (val == 0) ++ disable_mail_led_ec_2(); ++ } else if (acerhk_model_features & TM_F_MAIL_LED_EC3) { ++ if (val == 1) ++ enable_mail_led_ec_3(); ++ else if (val == 0) ++ disable_mail_led_ec_3(); ++ } else { ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: mail led not supported\n"); ++ regs.eax = 0x00; ++ } ++ return regs.eax & 0xffff; ++} ++ ++/* launch_connect ++ * ++ * does what? ++ * val: 1 - only known value from windows driver ++ */ ++static int launch_connect(int val) ++{ ++ struct register_buffer regs; ++ if (acerhk_model_features & TM_F_CONNECT) { ++ regs.eax = 0x9610; ++ regs.ebx = ((val & 0xff) << 8) | 0x2e; ++ preempt_disable(); ++ call_bios(®s); ++ preempt_enable_no_resched(); ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: connect(%d) = 0x%x\n", val, regs.eax); ++ } else { ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: connect not supported\n"); ++ regs.eax = 0x00; ++ } ++ return regs.eax & 0xffff; ++} ++ ++/* }}} */ ++ ++/* {{{ hardware probing */ ++ ++static struct proc_dir_entry *proc_acer_dir; ++ ++static unsigned int __init find_hk_area(void) ++{ ++ int offset, sig; ++ unsigned int fkt; ++ fkt = 0; ++ sig = -1; /* offset to signature in io area */ ++ /* Look for signature, start at 0xf0000, search until 0xffff0 */ ++ for (offset = 0;offset < 0xfffd; offset += 16) { ++ if (readl(reg1 + offset) == 0x30552142) { ++ sig = offset; ++ offset = 0xffff; ++ } ++ } ++ if (sig < 0) ++ printk(KERN_WARNING"acerhk: could not find request handler, possibly not all functions available\n"); ++ else { ++ /* compute location of bios routine */ ++ fkt = readl(reg1 + sig + 5); ++ /* adjust fkt to address of mapped IO area */ ++ if (fkt >= 0xf0000) ++ fkt = (unsigned int)reg1 + fkt - 0xf0000; ++ else if (fkt >= 0xe0000) ++ fkt = (unsigned int)reg1 + fkt - 0xe0000; ++ else ++ fkt = 0; ++ } ++ return fkt; ++} ++ ++static void print_features(void) ++{ ++ int i; ++ printk(KERN_INFO"acerhk: supported keys:"); ++ for (i = 0; i < 255; i++) { ++ switch (acerhk_key2name[i]) { ++ case k_help: printk(" help"); break; ++ case k_setup: printk(" setup"); break; ++ case k_p1: printk(" p1"); break; ++ case k_p2: printk(" p2"); break; ++ case k_p3: printk(" p3"); break; ++ case k_www: printk(" www"); break; ++ case k_mail: printk(" mail"); break; ++ case k_wireless: printk(" wireless"); break; ++ case k_power: printk(" power"); break; ++ case k_mute: printk(" mute"); break; ++ case k_volup: printk(" volup"); break; ++ case k_voldn: printk(" voldn"); break; ++ case k_res: printk(" res"); break; ++ case k_close: printk(" close"); break; ++ case k_open: printk(" open"); break; ++ case k_wireless2: printk(" wireless2"); break; ++ case k_play: printk(" play"); break; ++ case k_stop: printk(" stop"); break; ++ case k_prev: printk(" prev"); break; ++ case k_next: printk(" next"); break; ++ case k_display: printk(" display"); break; ++ default: break; ++ } ++ } ++ printk("\n"); ++ if (acerhk_model_features & TM_F_MUTE_LED_EC) ++ printk(KERN_INFO"acerhk: mute led is supported\n"); ++ if (acerhk_model_features & TM_F_MAIL_LED) ++ printk(KERN_INFO"acerhk: mail led is supported\n"); ++ else if (acerhk_model_features & TM_F_MAIL_LED_EC) ++ printk(KERN_INFO"acerhk: mail led (EC) is supported\n"); ++ else if (acerhk_model_features & TM_F_MAIL_LED_EC2) ++ printk(KERN_INFO"acerhk: mail led (EC2) is supported\n"); ++ else if (acerhk_model_features & TM_F_MAIL_LED_EC3) ++ printk(KERN_INFO"acerhk: mail led (EC3) is supported\n"); ++ if (acerhk_model_features & TM_F_WLAN_EC1) ++ printk(KERN_INFO"acerhk: wlan control (EC1) is supported\n"); ++ else if (acerhk_model_features & TM_F_WLAN_EC2) ++ printk(KERN_INFO"acerhk: wlan control (EC2) is supported\n"); ++ if (acerhk_model_features & TM_F_BLUE_EC1) ++ printk(KERN_INFO"acerhk: bluetooth control (EC1) is supported\n"); ++ else if (acerhk_model_features & TM_F_BLUE_EC2) ++ printk(KERN_INFO"acerhk: bluetooth control (EC2) is supported\n"); ++ printk(KERN_INFO"acerhk: supported functions:"); ++ if (acerhk_model_features & TM_F_CONNECT) ++ printk(" connect"); ++ if (acerhk_model_features & TM_F_THERMAL) ++ printk(" thermal"); ++ if (acerhk_model_features & TM_F_PBUTTON) ++ printk(" pbutton"); ++ if (acerhk_model_features & TM_F_WBUTTON) ++ printk(" wbutton"); ++ printk("\n"); ++} ++ ++static void __init setup_keymap_model(unsigned int series) ++{ ++ /* clear mapping keycode -> keyname, */ ++ memset(&acerhk_key2name[0], k_none, sizeof(acerhk_key2name)); ++ /* first set the common keys, namely FnF1 and FnF2, */ ++ acerhk_key2name[1] = k_help; ++ acerhk_key2name[2] = k_setup; ++ /* then set known keycodes according to model */ ++ switch (series) { ++ case 110: ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[3] = k_power; ++ acerhk_key2name[8] = k_mute; ++ acerhk_key2name[32] = k_volup; ++ acerhk_key2name[33] = k_voldn; ++ /* C110 generates 2 extra codes when opening/closing the lid */ ++ acerhk_key2name[74] = k_close; ++ acerhk_key2name[75] = k_open; ++ break; ++ case 300: /* treat C300 like C100 with Bluetooth button */ ++ acerhk_key2name[68] = k_wireless2; ++ case 100: ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[49] = k_www; ++ acerhk_key2name[54] = k_mail; ++ acerhk_key2name[3] = k_power; ++ acerhk_key2name[8] = k_mute; ++ acerhk_key2name[32] = k_volup; ++ acerhk_key2name[33] = k_voldn; ++ break; ++ default: ++ /* only the two common keys are supported */ ++ break; ++ case 210: ++ acerhk_key2name[19] = k_p1; ++ acerhk_key2name[20] = k_p2; ++ acerhk_key2name[17] = k_www; ++ acerhk_key2name[18] = k_mail; ++ break; ++ case 220: ++ case 260: /* 260 with same keys? */ ++ acerhk_key2name[49] = k_p1; ++ acerhk_key2name[19] = k_p2; ++ acerhk_key2name[18] = k_www; ++ acerhk_key2name[17] = k_mail; ++ break; ++ case 230: ++ case 280: /* 280 with same keys? */ ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[54] = k_www; ++ break; ++ case 1500: ++ acerhk_key2name[0x49] = k_setup; ++ acerhk_key2name[0x36] = k_www; ++ acerhk_key2name[0x31] = k_mail; ++ acerhk_key2name[0x11] = k_p1; ++ acerhk_key2name[0x12] = k_p2; ++ acerhk_key2name[0x30] = k_wireless; ++ acerhk_key2name[0x44] = k_wireless2; ++ acerhk_key2name[0x03] = k_power; ++ break; ++ case 240: ++ acerhk_key2name[0x31] = k_www; ++ acerhk_key2name[0x36] = k_mail; ++ acerhk_key2name[0x11] = k_p1; ++ acerhk_key2name[0x12] = k_p2; ++ acerhk_key2name[0x44] = k_wireless; ++ acerhk_key2name[0x30] = k_wireless2; ++ acerhk_key2name[0x03] = k_power; ++ acerhk_key2name[0x08] = k_mute; ++ // acerhk_key2name[] = k_volup; ++ // acerhk_key2name[] = k_voldn; ++ break; ++ case 2900: ++ acerhk_key2name[0x31] = k_mail; /* with led */ ++ acerhk_key2name[0x36] = k_www; ++ acerhk_key2name[0x11] = k_p1; ++ acerhk_key2name[0x12] = k_p2; ++ acerhk_key2name[0x30] = k_wireless; /* wireless, with led, related with autowlan=1 */ ++ break; ++ case 250: /* enriqueg@altern.org */ ++ /* TravelMate 254LMi_DT manual common for 240/250 series, but key order ++ differ from 240 already present on acerhk driver */ ++ /* TravelMate 254LMi_DT: 6 buttons: left to right: mail, www, p1, p2, bluetooth, wireless */ ++ acerhk_key2name[0x31] = k_mail; /* with led */ ++ acerhk_key2name[0x36] = k_www; ++ acerhk_key2name[0x11] = k_p1; ++ acerhk_key2name[0x12] = k_p2; ++ acerhk_key2name[0x44] = k_wireless2; /* bluetooth, hw optional */ ++ acerhk_key2name[0x30] = k_wireless; /* wireless, with led, related with autowlan=1 */ ++ acerhk_key2name[0x03] = k_power; /* Fn+F3 */ ++ acerhk_key2name[0x08] = k_mute; /* Fn+F8 */ ++ break; ++ case 380: ++ /* TM 380 has same codes as TM 370, with an additional one */ ++ acerhk_key2name[0x03] = k_power; ++ case 370: ++ acerhk_key2name[0x30] = k_wireless; ++ acerhk_key2name[0x11] = k_p1; ++ acerhk_key2name[0x12] = k_p2; ++ acerhk_key2name[0x13] = k_p3; ++ acerhk_key2name[0x36] = k_www; ++ acerhk_key2name[0x31] = k_mail; ++ break; ++ case 360: ++ /* 360 series has the same layout as 350, with an ++ additional wireless key */ ++ acerhk_key2name[64] = k_wireless; ++ case 350: ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[20] = k_p3; ++ acerhk_key2name[21] = k_www; ++ acerhk_key2name[19] = k_mail; ++ break; ++ case 520: ++ acerhk_key2name[19] = k_p1; ++ acerhk_key2name[20] = k_p2; ++ acerhk_key2name[17] = k_www; ++ acerhk_key2name[18] = k_mail; ++ break; ++ case 610: ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[19] = k_p3; ++ acerhk_key2name[21] = k_www; ++ acerhk_key2name[20] = k_mail; ++ acerhk_key2name[64] = k_wireless; ++ break; ++ case 630: ++ /* 630 has all keys of 620 plus one */ ++ acerhk_key2name[8] = k_mute; ++ case 620: ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[19] = k_p3; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[3] = k_power; ++ acerhk_key2name[32] = k_volup; ++ acerhk_key2name[33] = k_voldn; ++ break; ++ case 290: ++ case 420: ++ case 430: ++ case 530: ++ case 540: ++ case 650: ++ case 660: ++ case 800: ++ case 1450: ++ case 2300: ++ case 2350: ++ case 4000: ++ case 4050: ++ case 6000: ++ case 8000: ++ case 4100: ++ case 4150: ++ case 4500: ++ case 4600: ++ case 4650: ++ case 1680: ++ case 1690: ++ /* keys are handled by dritek EC */ ++ acerhk_key2name[1] = k_none; ++ acerhk_key2name[2] = k_none; ++ break; ++ case 1300: ++ case 1310: ++ case 1350: ++ case 1360: ++ case 1400: ++ case 1700: ++ case 1800: ++ case 2000: ++ case 2010: ++ case 2020: ++ /* Aspire 13xx series laptops use dritek hardware, no ++ acerhk-mapping needed ++ VolUp and VolDown are managed as normal keys ++ 1300/1310 series should have P1, P2, Mail, WWW, Mute buttons ++ 1353 has bluetooth, wifi, p1, p2, www, mail, help, setup, power ++ and mute ++ Aspire 1400/1450/Ferrari use dritek EC, too ++ 1450 should have bluetooth, wifi, p1, p2, www, mail, help, ++ setup, power and mute ++ Aspire 1700 uses dritek EC, too ++ 1700 should have bluetooth, wifi, p1, p2, www, mail, help, ++ setup, power and mute ++ need the MM-buttons Activation? (forward, shuffle, ...) ++ 2000 hast lots of MM buttons ++ 2010 should have bluetooth, wifi, p1, p2, www, mail, help, ++ setup, power and mute ++ */ ++ acerhk_key2name[1] = k_none; ++ acerhk_key2name[2] = k_none; ++ break; ++ case 1600: ++ /* Aspire 1600 has acer keycode 0x49 for FnF2 */ ++ acerhk_key2name[73] = k_setup; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[19] = k_p3; ++ acerhk_key2name[3] = k_power; ++ acerhk_key2name[8] = k_mute; ++ /* VolUp and VolDown keys doesn't seem to be managed as special keys ++ but as normal keys ! */ ++ break; ++ case 5020: /* Aspire 5020 has 0x6a for Fn+F2 */ ++ acerhk_key2name[2] = k_none; ++ acerhk_key2name[106] = k_setup; ++ acerhk_key2name[3] = k_power; ++ acerhk_key2name[5] = k_display; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[68] = k_wireless2; ++ break; ++ case 2410: /* TM 2410 is very similar to Aspire 5020, but has 0x6s for Fn-F3 */ ++ acerhk_key2name[2] = k_none; ++ acerhk_key2name[106] = k_setup; ++ acerhk_key2name[109] = k_power; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[68] = k_wireless2; ++ break; ++ case 40100: ++ /* Medion MD40100, 4 keys */ ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[55] = k_res; ++ break; ++ case 96500: ++ case 95400: ++ /* Medion MD95400, many keys */ ++ acerhk_key2name[49] = k_mail; /* 1 */ ++ acerhk_key2name[54] = k_www; /* 2 */ ++ acerhk_key2name[48] = k_wireless; /* 3 */ ++ acerhk_key2name[68] = k_wireless2; /* 4 (Bluetooth) */ ++ ++ acerhk_key2name[17] = k_p1; /* 5 */ ++ acerhk_key2name[18] = k_p2; /* 6 */ ++ acerhk_key2name[36] = k_play; /* 7 */ ++ acerhk_key2name[37] = k_stop; /* 8 */ ++ acerhk_key2name[34] = k_prev; /* 9 */ ++ acerhk_key2name[35] = k_next; /* 10 */ ++ acerhk_key2name[33] = k_voldn; /* 11 */ ++ acerhk_key2name[32] = k_volup; /* 12 */ ++ acerhk_key2name[38] = k_p3; /* 13 */ ++ acerhk_key2name[8] = k_mute; /* 14 */ ++ ++ acerhk_key2name[1] = k_help; /* FN+F1 (Help) */ ++ acerhk_key2name[5] = k_display; /* FN+F3 (Display switch) */ ++ acerhk_key2name[6] = k_res; /* FN+F4 (Display ein/ausschalten) */ ++ break; ++ case 42200: ++ /* Medion MD42200, 7 keys, no setup */ ++ acerhk_key2name[2] = k_none; ++ acerhk_key2name[5] = k_display; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ break; ++ case 9783: ++ /* Medion MD9783, 6 keys + info, no setup */ ++ acerhk_key2name[2] = k_none; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[19] = k_p3; ++ acerhk_key2name[8] = k_mute; ++ break; ++ case 7400: ++ /* Amilo Pro V2000 does not have Help and Setup key (?) ++ Amilo M 7400 has Help key, disabling only setup ++ */ ++ acerhk_key2name[2] = k_none; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ break; ++ case 1559: ++ acerhk_key2name[6] = k_display; /* FN+F4 (Display ein/ausschalten) */ ++ case 1555: ++ /* AOpen (Ahtec Signal 1555M) is similar to FS Amilo M */ ++ acerhk_key2name[2] = k_none; ++ acerhk_key2name[48] = k_wireless; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[34] = k_prev; ++ acerhk_key2name[35] = k_next; ++ acerhk_key2name[36] = k_play; ++ acerhk_key2name[37] = k_stop; ++ break; ++ case 6800: ++ case 7820: ++ /* Amilo D does not have Setup key */ ++ acerhk_key2name[2] = k_none; ++ acerhk_key2name[49] = k_mail; ++ acerhk_key2name[54] = k_www; ++ acerhk_key2name[17] = k_p1; ++ acerhk_key2name[18] = k_p2; ++ acerhk_key2name[19] = k_p3; ++ acerhk_key2name[8] = k_mute; ++ break; ++ } ++} ++ ++static void __init setup_model_features(unsigned int series) ++{ ++ switch (series) { ++ case 200: ++ case 210: ++ case 520: ++ /* nothing special */ ++ acerhk_model_features = 0; ++ acerhk_type = TM_old; ++ break; ++ case 220: ++ case 230: ++ case 260: ++ case 280: ++ case 360: ++ case 40100: /* Medion MD40100 */ ++ case 95400: /* Medion MD95400 */ ++ case 96500: /* Medion MD96500 */ ++ /* all special functions, no mail led */ ++ acerhk_model_features = 0x00f00000; ++ acerhk_type = TM_new; ++ break; ++ case 42200: /* Medion MD42200 */ ++ /* has WLAN button, should call connect() */ ++ acerhk_model_features = TM_F_WBUTTON | TM_F_CONNECT; ++ acerhk_type = TM_old; ++ break; ++ case 9783: /* Medion MD9783 */ ++ /* only email led */ ++ acerhk_model_features = TM_F_MAIL_LED; ++ acerhk_type = TM_new; ++ break; ++ case 1600: ++ acerhk_type = TM_new; ++ /* Do Aspire 1600 series have special functions or not ? I enable ++ them, perhaps it helps with problems Francois Valenduc has */ ++ acerhk_model_features = 0x00f00000; ++ break; ++ case 300: ++ case 100: ++ case 110: ++ case 240: ++ case 350: ++ case 610: ++ case 620: ++ case 630: ++ /* all special functions, mail led */ ++ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000; ++ acerhk_type = TM_new; ++ break; ++ case 370: ++ case 380: ++ case 2410: ++ case 2900: /* Medion MD2900 */ ++ case 2100: /* TM 2100 uses same driver as 5020 */ ++ case 5020: /* Aspire 5020 is still old hardware */ ++ acerhk_model_features = TM_F_MAIL_LED | TM_F_CONNECT| TM_F_WBUTTON; ++ acerhk_type = TM_new; ++ break; ++ case 7400: ++ case 1555: ++ case 1559: ++ /* all special functions for Fujitsu-Siemens Amilo M7400, Pro V2000; AOpen */ ++ acerhk_model_features = 0x00f00000; ++ acerhk_type = TM_new; ++ break; ++ case 6800: ++ case 7820: ++ /* mail led and all special functions for FS Amilo D */ ++ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000; ++ acerhk_type = TM_new; ++ break; ++ case 2350: ++ case 4050: ++ acerhk_wlan_state = 1; // Default state is on ++ case 290: ++ /* no special functions, wireless hardware controlled by EC */ ++ acerhk_model_features = TM_F_WLAN_EC2 | TM_F_BLUE_EC2; ++ acerhk_type = TM_dritek; ++ break; ++ case 650: ++ case 1300: ++ case 1310: ++ case 1400: ++ case 1700: ++ /* all special functions, wireless hardware can be controlled */ ++ acerhk_model_features = 0x00f00000; ++ acerhk_type = TM_dritek; ++ break; ++ case 4100: ++ case 4600: ++ case 1680: ++ case 1690: /* Aspire 1680/1690 should be similar to TM 4100/4600 */ ++ /* mail led, wireless and bluetooth controlled the old way, but keys are ++ controlled by normal keyboard controller, so mark as dritek and ++ deactivate dritek use */ ++ acerhk_model_features = TM_F_MAIL_LED | TM_F_WBUTTON; ++ acerhk_type = TM_dritek; ++ usedritek=0; ++ break; ++ case 660: ++ case 800: ++ /* all special functions, mail led */ ++ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000; ++ acerhk_type = TM_dritek; ++ break; ++ case 1350: ++ case 1360: ++ /* mail led, handled by EC, wireless HW is not (yet) controllable ? */ ++ acerhk_model_features = TM_F_MAIL_LED_EC|TM_F_WLAN_EC1; ++ acerhk_type = TM_dritek; ++ break; ++ case 1450: ++ /* Bluetooth/Wlan led, Mail led handled by EC (variant 3) */ ++ acerhk_model_features = TM_F_MAIL_LED_EC3|TM_F_WBUTTON; ++ acerhk_type = TM_dritek; ++ break; ++ case 1500: ++ /* Bluetooth/Wlan led */ ++ acerhk_model_features = TM_F_WBUTTON; ++ acerhk_type = TM_new; ++ break; ++ case 420: ++ case 430: ++ /* all functions and dritek EC, mail LED is handled by EC, second ++ variant. An additional led is available, mute. (really?) ++ */ ++ acerhk_type = TM_dritek; ++ acerhk_model_features = TM_F_MUTE_LED_EC|TM_F_MAIL_LED_EC2; ++ break; ++ case 2300: ++ case 4000: ++ case 4500: ++ /* wireless hardware, hopefully under control of my driver */ ++ acerhk_type = TM_dritek; ++ acerhk_model_features = TM_F_BLUE_EC1|TM_F_WLAN_EC1; ++ break; ++ case 3200: ++ /* test, if this model uses old style wlan control */ ++ acerhk_model_features = TM_F_WBUTTON; ++ acerhk_type = TM_dritek; ++ break; ++ case 6000: ++ case 8000: ++ /* 6000 and 8000 have wireless hardware, but I don't know how to handle, ++ so I choose no features */ ++ acerhk_type = TM_dritek; ++ break; ++ case 530: ++ case 540: ++ case 2000: ++ /* No features (?) dritek EC, mail LED is handled by EC but ++ different from other Aspire series */ ++ acerhk_type = TM_dritek; ++ acerhk_model_features = TM_F_MAIL_LED_EC2; ++ break; ++ case 4150: ++ case 4650: ++ /* Dritek EC, bluetooth, wifi, mail */ ++ /* According to Andreas Stumpfl his TM 4652LMi does also work as series ++ 3200, which might mean that the BIOS function accesses the EC */ ++ acerhk_type = TM_dritek; ++ acerhk_model_features = TM_F_MAIL_LED_EC2 | TM_F_WLAN_EC2 | TM_F_BLUE_EC2; ++ break; ++ case 1800: ++ case 2010: ++ case 2020: ++ /* Dritek EC, bluetooth, wifi, mail */ ++ acerhk_type = TM_dritek; ++ acerhk_model_features = TM_F_MAIL_LED_EC2 | TM_F_WLAN_EC2 | TM_F_BLUE_EC2; ++ acerhk_wlan_state = 1; // Default state is on ++ break; ++ case 250: /* enriqueg@altern.org */ ++ /* TravelMate254LMi_DT : mail led, bluetooth (button present, hw optional), wifi (with led) */ ++ acerhk_model_features = TM_F_MAIL_LED| ++ TM_F_WBUTTON ; ++ acerhk_type = TM_new; ++ acerhk_wlan_state = 0; //Initial state is off on 254LMi_DT ++ break; ++ default: ++ /* nothing special */ ++ acerhk_model_features = 0; ++ acerhk_type = TM_unknown; ++ break; ++ } ++ /* set the correct bios call function according to type */ ++ if ((acerhk_type == TM_new) || (acerhk_type == TM_dritek)) { ++ call_bios = call_bios_6xx; ++ if (verbose > 2) ++ printk(KERN_INFO"acerhk: using call_bios_6xx mode\n"); ++ } else { ++ call_bios = call_bios_52x; ++ if (verbose > 2) ++ printk(KERN_INFO"acerhk: using call_bios_52x mode\n"); ++ } ++ /* remove key file on dritek hardware */ ++ if (acerhk_type == TM_dritek) { ++ remove_proc_entry("key", proc_acer_dir); ++ } ++ /* setup available keys */ ++ setup_keymap_model(acerhk_series); ++ if (verbose > 1) ++ print_features(); ++} ++ ++static unsigned int __init determine_laptop_series(char * str) ++{ ++ /* 0 means unknown series, handled like TM 200 */ ++ unsigned int series = 0; ++ if (strncmp(str, "TravelMate ", 11) == 0) { ++ switch (str[11]) { ++ case 'C': ++ if (str[12] == '1') { ++ if (str[13] == '0') { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates TM C100 series\n"); ++ series = 100; ++ } else if (str[13] == '1') { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates TM C110 series\n"); ++ series = 110; ++ } ++ } else if (str[12] == '3') { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates TM C300 series\n"); ++ series = 300; ++ } ++ break; ++ case 'F': ++ if (str[12] == '4') { ++ series = 230; ++ } ++ break; ++ case '2': ++ if (str[14] == '0') { ++ /* newer Travelmate 2xxx series */ ++ switch (str[12]) { ++ case '0': ++ case '5': ++ series = 2000; // 2000 and 2500 are the same ++ break; ++ case '1': ++ if (str[13] == '0') ++ series = 2100; ++ break; ++ case '2': ++ case '7': ++ series = 2200; // 2200 and 2700 are the same ++ break; ++ case '3': ++ if (str[13] == '0') ++ series = 4000; // 2300 is the same as 4000 ++ else if (str[13] == '5') ++ series = 4050; // 2350 is the same as 4050 ++ break; ++ case '4': ++ if (str[13] == '1') ++ series = 2410; ++ break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM 2xxx series\n"); ++ break; ++ } ++ } else { ++ /* older Travelmate 2xx series */ ++ switch (str[12]) { ++ case '0': series = 200; break; ++ case '1': series = 210; break; ++ case '2': series = 220; break; ++ case '4': series = 240; break; ++ case '5': series = 250; break; /* enriqueg@altern.org */ ++ case '6': series = 260; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM 2xx series\n"); ++ break; ++ } ++ } ++ break; ++ case '3': ++ switch (str[12]) { ++ case '0': series = 3200; break; /* TM 3000 works like TM 3200 */ ++ /* Travelmate 3xx series */ ++ case '5': series = 350; break; ++ case '6': series = 360; break; ++ case '7': series = 370; break; ++ case '8': series = 380; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM 3xx series\n"); ++ break; ++ } ++ break; ++ case '4': ++ if ( (strnlen(str, ACERHK_MODEL_STRLEN-1) == 15) && ++ (str[14] == '0') ) { /* Travelmate 4xxx series */ ++ switch (str[12]) { ++ case '0': /* 4000 and 4500 are the same */ ++ case '5': ++ series = 4000; ++ break; ++ case '1': ++ case '6': /* 4100 and 4600 are the same */ ++ series = 4100; ++ break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM 4xxx series\n"); ++ break; ++ } ++ } else { /* Travelmate 4xx series */ ++ switch (str[12]) { ++ case '2': series = 420; break; ++ case '3': series = 430; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM 4xx series\n"); ++ break; ++ } ++ } ++ break; ++ case '5': /* Travelmate 5xx series */ ++ if (str[12] == '2') ++ series = 520; ++ else if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM 5xx series\n"); ++ break; ++ case '6': /* older Travelmate 6xx series */ ++ switch (str[12]) { ++ case '1': series = 610; break; ++ case '2': series = 620; break; ++ case '3': series = 630; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM 6xx series\n"); ++ break; ++ } ++ break; ++ default: ++ printk(KERN_INFO"acerhk: model string indicates unknown TM xxx series\n"); ++ break; ++ } ++ if (series && verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates TM %d series\n", series); ++ } ++ /* newer Travelmate series do not have a space after 'TravelMate' */ ++ else if (strncmp(str, "TravelMate", 10) == 0) { ++ switch (str[10]) { ++ case '2': ++ if (str[11] == '9') { ++ series = 290; ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM2xx series\n"); ++ } ++ break; ++ case '3': ++ if (str[11] == '2' && str[14] == '3') { ++ // TM 3200 uses "TravelMate32003" ++ series = 3200; ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM3xxx series\n"); ++ } ++ break; ++ case '4': ++ switch (str[11]) { ++ case '3': series = 430; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM4xx series\n"); ++ break; ++ } ++ break; ++ case '5': ++ switch (str[11]) { ++ case '3': series = 530; break; ++ case '4': series = 540; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM5xx series\n"); ++ break; ++ } ++ break; ++ case '6': ++ switch (str[11]) { ++ case '5': series = 650; break; ++ case '6': series = 660; break; ++ case '0': ++ if (strncmp(str, "TravelMate60003", 15) == 0) { ++ series = 6000; break; ++ } ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM6xx series\n"); ++ break; ++ } ++ break; ++ case '8': ++ if (strncmp(str, "TravelMate80003", 15) == 0) { ++ series = 8000; ++ } else if (str[11] == '0') { ++ series = 800; ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown TM8xx series\n"); ++ } ++ break; ++ default: ++ printk(KERN_INFO"acerhk: model string indicates unknown TMxxx series\n"); ++ break; ++ } ++ if (series && verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates TM%d series\n", series); ++ } ++ else if (strncmp(str, "Aspire ", 7) == 0) { ++ switch(str[7]) { ++ case '1': /* Aspire 1xxx series */ ++ switch(str[8]) { ++ case '3': /* Aspire 13xx series */ ++ switch (str[9]) { ++ case '0': series = 1300; break; ++ case '1': series = 1310; break; ++ case '5': series = 1350; break; ++ case '6': series = 1360; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 13xx series\n"); ++ break; ++ } ++ break; ++ case '4': /* Aspire 14xx series */ ++ switch (str[9]) { ++ case '0': series = 1400; break; ++ case '5': series = 1450; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 14xx series\n"); ++ break; ++ } ++ break; ++ case '5': series = 1500; break; ++ case '6': /* Aspire 14xx series */ ++ switch (str[9]) { ++ case '0': series = 1600; break; ++ case '8': ++ case '9': series = 1680; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 16xx series\n"); ++ break; ++ } ++ break; ++ case '7': series = 1700; break; ++ case '8': series = 1800; break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 1xxx series\n"); ++ break; ++ } ++ break; ++ case '2': /* Aspire 2xxx series */ ++ if (str[8] == '0') { ++ switch (str[9]) { ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 20xx series\n"); ++ break; ++ case '0': series = 2000; break; ++ case '1': series = 2010; break; ++ case '2': series = 2020; break; ++ } ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 2xxx series\n"); ++ } ++ break; ++ case '3': /* Aspire 3xxx series */ ++ if (str[8] == '0') { ++ switch (str[9]) { ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 30xx series\n"); ++ break; ++ case '2': series = 5020; break; /* Aspire 3020/5020 are identical */ ++ } ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 3xxx series\n"); ++ } ++ break; ++ case '5': /* Aspire 5xxx series */ ++ if (str[8] == '0') { ++ switch (str[9]) { ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 50xx series\n"); ++ break; ++ case '2': series = 5020; break; ++ } ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 5xxx series\n"); ++ } ++ break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Aspire series\n"); ++ break; ++ } ++ if (series && verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates Aspire %d series\n", series); ++ } ++ else if (strncmp(str, "Extensa ", 8) == 0) { ++ /* Extensa series */ ++ switch (str[8]) { ++ case '3': ++ switch (str[9]) { ++ case '0': ++ series = 3000; break; ++ default: break; ++ } ++ break; ++ default: break; ++ } ++ if (series && verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates Extensa %d series\n", series); ++ else if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown Extensa series\n"); ++ } ++ else if (strncmp(str, "Amilo ", 6) == 0) { ++ switch (str[6]) { ++ case 'D': /* complete string is "Amilo D-Series", there seems to be no model number */ ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates FS Amilo D series\n"); ++ /* this is the model number of my Amilo */ ++ series = 7820; ++ break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown FS Amilo XX series\n"); ++ series = 7820; ++ } ++ } ++ else if (strncmp(str, "AMILO ", 6) == 0) { ++ switch (str[6]) { ++ case 'D': /* AMILO D 6800 P4-2000 */ ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates FS AMILO D series\n"); ++ series = 6800; ++ break; ++ case 'M': ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates FS AMILO M(7400) series\n"); ++ series = 7400; ++ break; ++ case 'P': ++ /* it is assumed, that 'AMILO P' appears only on Amilo Pro Series */ ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates FS AMILO Pro (V2000) series\n"); ++ series = 7400; ++ break; ++ default: ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates unknown FS AMILO XX series\n"); ++ series = 6800; ++ } ++ } ++ else if (strncmp(str, "MEDIONPC", 8) == 0) { ++ uint medionmodel; ++ if ((medionmodel = COLUSSI("WIM 2040", 4, reg1, AREA_SIZE)) >= 0) { ++ printk(KERN_INFO"acerhk: found Medion model string:'%s'\n", (char*)reg1+medionmodel); ++ series = 96500; ++ } else { ++ if ((medionmodel = COLUSSI("MD 9", 4, reg1, AREA_SIZE)) >= 0) { ++ printk(KERN_INFO"acerhk: found Medion model string:'%s'\n", (char*)reg1+medionmodel); ++ } ++ series = 95400; ++ } ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates a medion MD %d\n", series); ++ } ++ else if (strncmp(str, "MEDIONNB", 8) == 0) { ++ /* Search for the Product string of the MD9783. */ ++ if (COLUSSI("MD 42200", 8, reg1, AREA_SIZE) >= 0) { ++ if (verbose>1) ++ printk(KERN_INFO"acerhk: model string indicates a Medion MD 42200\n"); ++ series = 42200; ++ } else if (COLUSSI("MD 9783", 7, reg1, AREA_SIZE) >= 0){ ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates a medion MD 9783\n"); ++ series = 9783; ++ } else if (COLUSSI("WIM 2000", 7, reg1, AREA_SIZE) >= 0){ ++ if (verbose>1) ++ printk(KERN_INFO"acerhk: model string indicates a Medion MD 2900\n"); ++ series = 2900; ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates a medion MD40100\n"); ++ series = 40100; ++ } ++ } else if (strncmp(str, "AOpen", 5) == 0) { ++ if (strncmp(str, "AOpen*EzRestore", 15) == 0) { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates a AOpen 1559\n"); ++ series = 1559; ++ } else { ++ /* Unless I know of other models no further differentiation, ++ although there is a second part of the model string */ ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates a AOpen\n"); ++ series = 1555; ++ } ++ } else if (strncmp(str, "CL56", 4) == 0) { ++ /* Unless I know of other models no further differentiation, ++ although there are strings with more numbers ("CL561" on a Compal ++ CL56/Zepto 4200, reported by Stian B. Barmen) ++ It has the same functions as Acer Aspire 2010 ++ */ ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates a Compal CL56 (or similar)\n"); ++ series = 2010; ++ } else if (strncmp(str, "Geneva2", 7) == 0) { ++ /* This might be an Aspire 9110 which is very similar to 4650 */ ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates an Aspire 9110\n"); ++ series = 4650; ++ } else { ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: model string indicates no supported hardware\n"); ++ } ++ return (series); ++} ++ ++static void __init probe_model(void) { ++ int offset; /* offset from beginning of reg1 to Model string */ ++ if (verbose) ++ printk(KERN_INFO"acerhk: start search for model string at %p\n", reg1); ++ /* first we look for Travelmate, if it isn't one we try to identify other ++ laptops, such as Medion or Aspire */ ++ offset = COLUSSI("Travel", 6, reg1, AREA_SIZE); ++ /* Try to detect Aspire laptops */ ++ if (offset < 0) ++ offset = COLUSSI("Aspire", 6, reg1, AREA_SIZE); ++ /* Try to detect Extensa laptops */ ++ if (offset < 0) ++ offset = COLUSSI("Extensa", 7, reg1, AREA_SIZE); ++ /* Try to detect Medion laptops */ ++ if (offset < 0) ++ offset = COLUSSI("MEDION", 6, reg1, AREA_SIZE); ++ /* Try to detect AOpen laptops */ ++ if (offset < 0) ++ offset = COLUSSI("AOpen", 5, reg1, AREA_SIZE); ++ /* Try to detect Fujitsu Siemens Amilo laptops */ ++ if (offset < 0) ++ offset = COLUSSI("Amilo", 5, reg1, AREA_SIZE); ++ if (offset < 0) ++ offset = COLUSSI("AMILO", 5, reg1, AREA_SIZE); ++ /* Try to detect Compal */ ++ if (offset < 0) ++ offset = COLUSSI("CL56", 4, reg1, AREA_SIZE); ++ /* That might be an Aspire 9110 */ ++ if (offset < 0) ++ offset = COLUSSI("Geneva2", 7, reg1, AREA_SIZE); ++ if (offset >= 0) { ++ acerhk_model_addr = reg1 + offset; ++ /* copy the string, but not more than 15 characters */ ++ strncpy(acerhk_model_string, acerhk_model_addr, ACERHK_MODEL_STRLEN-1); ++ if (verbose) ++ printk(KERN_INFO"acerhk: found model string '%s' at %p\n", ++ acerhk_model_string, acerhk_model_addr); ++ if (bios_routine && verbose > 2) ++ printk(KERN_INFO"acerhk: offset from model string to function address: 0x%lx\n", ++ bios_routine - (unsigned long)acerhk_model_addr); ++ acerhk_series = determine_laptop_series(acerhk_model_string); ++ } else { ++ printk(KERN_WARNING"acerhk: Could not find model string, will assume type 200 series\n"); ++ acerhk_series = 200; ++ } ++} ++ ++/* }}} */ ++ ++/* {{{ key polling and translation */ ++ ++static void print_mapping(void) ++{ ++ printk(KERN_INFO"acerhk: key mapping:\n"); ++ printk("acerhk: help 0x%x\n", acerhk_name2event[k_help]); ++ printk("acerhk: setup 0x%x\n", acerhk_name2event[k_setup]); ++ printk("acerhk: p1 0x%x\n", acerhk_name2event[k_p1]); ++ printk("acerhk: p2 0x%x\n", acerhk_name2event[k_p2]); ++ printk("acerhk: p3 0x%x\n", acerhk_name2event[k_p3]); ++ printk("acerhk: www 0x%x\n", acerhk_name2event[k_www]); ++ printk("acerhk: mail 0x%x\n", acerhk_name2event[k_mail]); ++ printk("acerhk: wireless 0x%x\n", acerhk_name2event[k_wireless]); ++ printk("acerhk: power 0x%x\n", acerhk_name2event[k_power]); ++ printk("acerhk: mute 0x%x\n", acerhk_name2event[k_mute]); ++ printk("acerhk: volup 0x%x\n", acerhk_name2event[k_volup]); ++ printk("acerhk: voldn 0x%x\n", acerhk_name2event[k_voldn]); ++ printk("acerhk: res 0x%x\n", acerhk_name2event[k_res]); ++ printk("acerhk: close 0x%x\n", acerhk_name2event[k_close]); ++ printk("acerhk: open 0x%x\n", acerhk_name2event[k_open]); ++ printk("acerhk: wireless2 0x%x\n", acerhk_name2event[k_wireless2]); ++ printk("acerhk: play 0x%x\n", acerhk_name2event[k_play]); ++ printk("acerhk: stop 0x%x\n", acerhk_name2event[k_stop]); ++ printk("acerhk: prev 0x%x\n", acerhk_name2event[k_prev]); ++ printk("acerhk: next 0x%x\n", acerhk_name2event[k_next]); ++ printk("acerhk: display 0x%x\n", acerhk_name2event[k_display]); ++} ++ ++static void set_keymap_name(t_key_names name, unsigned int key) ++{ ++ acerhk_name2event[name] = key; ++} ++ ++static void init_keymap_input(void) ++{ ++ /* these values for input keys are chosen to match the key names on the ++ actual Acer laptop */ ++ set_keymap_name(k_none, KEY_RESERVED); ++ set_keymap_name(k_help, KEY_HELP); ++ set_keymap_name(k_setup, KEY_CONFIG); ++ set_keymap_name(k_p1, KEY_PROG1); ++ set_keymap_name(k_p2, KEY_PROG2); ++ set_keymap_name(k_p3, KEY_PROG3); ++ set_keymap_name(k_www, KEY_WWW); ++ set_keymap_name(k_mail, KEY_MAIL); ++ set_keymap_name(k_wireless, KEY_XFER); ++ set_keymap_name(k_power, KEY_POWER); ++ set_keymap_name(k_mute, KEY_MUTE); ++ set_keymap_name(k_volup, KEY_VOLUMEUP); ++ set_keymap_name(k_voldn, KEY_VOLUMEDOWN); ++ set_keymap_name(k_res, KEY_CONFIG); ++ set_keymap_name(k_close, KEY_CLOSE); ++ set_keymap_name(k_open, KEY_OPEN); ++ /* I am not really happy with the selections for wireless and wireless2, ++ but coffee looks good. Michal Veselenyi proposed this value */ ++ set_keymap_name(k_wireless2, KEY_COFFEE); ++ set_keymap_name(k_play, KEY_PLAYPAUSE); ++ set_keymap_name(k_stop, KEY_STOPCD); ++ set_keymap_name(k_prev, KEY_PREVIOUSSONG); ++ set_keymap_name(k_next, KEY_NEXTSONG); ++ set_keymap_name(k_display, KEY_MEDIA); /* also not happy with this */ ++ if (verbose > 1) ++ print_mapping(); ++} ++ ++static int filter_idle_value(int keycode) ++{ ++ int validkey = 0; ++ if (keycode != 0x0 && ++ keycode != 0x9610 && ++ keycode != 0xc100 && /* Francois Valenduc, Aspire 1601 LC */ ++ keycode != 0x8610 && ++ keycode != 0x861 && ++ keycode != 0x8650 && ++ keycode != 0x865) ++ validkey = keycode; ++ if (verbose > 4 && !validkey) ++ printk(KERN_INFO"acerhk: throw away idle value 0x%x\n", keycode); ++ return validkey; ++} ++ ++static void send_key_event(t_key_names key) ++{ ++ unsigned int input_key; ++ if (key != k_none) { ++ /* convert key name to kernel keycode */ ++ input_key = acerhk_name2event[key]; ++ if (verbose > 2) ++ printk(KERN_INFO"acerhk: translated acer key name 0x%x to input key 0x%x\n", ++ key, input_key); ++ /* send press and release together, as there is no such event from acer as 'release' */ ++ input_report_key(acerhk_input_dev_ptr, input_key, 1); ++ input_report_key(acerhk_input_dev_ptr, input_key, 0); ++ } ++} ++ ++static t_key_names transl8_key_code(int keycode) ++{ ++ t_key_names keyname = k_none; ++ /* first filter out idle values */ ++ if ( (keycode = filter_idle_value(keycode)) ) { ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: received key code 0x%x\n", keycode); ++ /* translate keycode to key name */ ++ if (keycode >= 0 && keycode <= 255) ++ keyname = acerhk_key2name[keycode]; ++ else { ++ if (verbose > 3) ++ printk(KERN_INFO"acerhk: keycode 0x%x too big, will use only 8 bits\n", keycode); ++ /* use only lower 8 bits of value to distinguish keys */ ++ keyname = acerhk_key2name[keycode&0xff]; ++ } ++ /* produce some log information for higher verbosity levels */ ++ if (keyname != k_none && verbose > 2) ++ printk(KERN_INFO"acerhk: translated acer key code 0x%x to key name 0x%x\n", ++ keycode, keyname); ++ else if (keyname == k_none && verbose > 3) ++ printk(KERN_INFO"acerhk: translated acer key code 0x%x to no key\n", ++ keycode); ++ if (autowlan) { ++ /* if automatic switching of wlan hardware is enabled, do it here ++ on wireless key press */ ++ if (keyname == k_wireless2) { ++ if (acerhk_bluetooth_state) ++ wbutton_fct_1(0); ++ else ++ wbutton_fct_1(1); ++ } ++ if (keyname == k_wireless) { ++ if (acerhk_wlan_state) ++ wbutton_fct_2(0); ++ else ++ wbutton_fct_2(1); ++ } ++ } ++ } ++ return keyname; ++} ++ ++/* polling timer handler */ ++static void acerhk_poll_event(unsigned long save_size) ++{ ++#ifndef DUMMYHW ++ unsigned int max = MAX_POLLING_LOOPS; ++ /* make sure not to loop more then 32 times */ ++ if (!max || max > 32) ++ max = 32; ++ if (acerhk_type != TM_dritek) { ++ while (get_nr_events() && max--) { ++ send_key_event(transl8_key_code(get_fnkey_event())); ++ } ++ } else { ++ send_key_event(transl8_key_code(get_fnkey_event())); ++ } ++#endif ++ acerhk_timer_poll.expires = jiffies + acerhk_polling_delay; ++ add_timer(&acerhk_timer_poll); ++} ++ ++/* blinking timer handler; added by Antonio Cuni */ ++static void acerhk_blink_event(unsigned long not_used) ++{ ++ if (acerhk_blueled_blinking != -1) { ++ acerhk_blueled_blinking = !acerhk_blueled_blinking; ++#ifndef DUMMYHW ++ wbutton_fct_1(acerhk_blueled_blinking); ++#endif ++ acerhk_timer_blinking.expires = jiffies + acerhk_blueled_blinking_delay; ++ add_timer(&acerhk_timer_blinking); ++ } ++ else ++ printk(KERN_WARNING "acerhk: blinking event called, but blinking not active\n"); ++} ++ ++static void init_input(void) ++{ ++ int i; ++ ++#ifndef KERNEL26 ++ /* request keyboard input module */ ++ request_module("keybdev"); ++ if (verbose > 3) ++ printk(KERN_INFO"requested keyboard input driver\n"); ++#endif ++ ++#ifndef STATIC_INPUT_DEV ++ /* allocate acerhk input device */ ++ acerhk_input_dev_ptr=input_allocate_device(); ++ /* enter some name */ ++ acerhk_input_dev_ptr->name = "Acer hotkey driver"; ++#else ++ acerhk_input_dev_ptr=&acerhk_input_dev; ++#endif ++ ++ /* some laptops have a mail led, should I announce it here? */ ++ acerhk_input_dev_ptr->evbit[0] = BIT(EV_KEY); ++ /* announce keys to input system ++ * the generated keys can be changed on runtime, ++ * but to publish those changes the device needs to ++ * get reconnected (I dont't know any other way) ++ * Therefore I enable all possible keys */ ++ for (i = KEY_RESERVED; i < BTN_MISC; i++) ++ set_bit(i, acerhk_input_dev_ptr->keybit); ++ /* set mapping keyname -> input event */ ++ init_keymap_input(); ++ if (verbose) ++ printk(KERN_INFO"acerhk: registered input device\n"); ++ input_register_device(acerhk_input_dev_ptr); ++ init_timer(&acerhk_timer_poll); ++ acerhk_polling_state = 0; ++} ++ ++static void stop_polling(void) ++{ ++ if (acerhk_polling_state == 1) { ++ del_timer(&acerhk_timer_poll); ++ if (verbose) ++ printk(KERN_INFO"acerhk: key polling stopped\n"); ++ acerhk_polling_state = 0; ++ } else ++ if (verbose) ++ printk(KERN_INFO"acerhk: key polling not active\n"); ++} ++ ++static void start_polling(void) ++{ ++ if (acerhk_polling_state != 1) { ++ acerhk_timer_poll.function = acerhk_poll_event; ++ acerhk_timer_poll.expires = jiffies + acerhk_polling_delay; ++ acerhk_timer_poll.data = get_nr_events(); ++ add_timer(&acerhk_timer_poll); ++ acerhk_polling_state = 1; ++ if (acerhk_type == TM_dritek) { ++ printk(KERN_INFO"acerhk: Your hardware does not need polling enabled for hotkeys to work, " ++ "you can safely disable polling by using the module parameter poll=0 (unless you " ++ "want to play around with the driver and see if there are buttons which need polling).\n"); ++ } ++ if (verbose) ++ printk(KERN_INFO"acerhk: starting key polling, every %d ms\n", acerhk_polling_delay); ++ } else ++ if (verbose) ++ printk(KERN_INFO"acerhk: key polling already active\n"); ++} ++ ++/* addedd by Antonio Cuni */ ++static void start_blinking(void) ++{ ++ if (acerhk_blueled_blinking == -1) { ++ // blinking was disabled... enable it! ++ acerhk_timer_blinking.function = acerhk_blink_event; ++ acerhk_timer_blinking.expires = jiffies + acerhk_blueled_blinking_delay; ++ acerhk_timer_blinking.data = 0; // not used ++ add_timer(&acerhk_timer_blinking); ++ acerhk_blueled_blinking = 0; ++ if (verbose) ++ printk(KERN_INFO "acerhk: starting blueled blinking\n"); ++ } else ++ if (verbose) ++ printk(KERN_INFO "acerhk: blueled already blinking\n"); ++} ++ ++/* Added by Antonio Cuni */ ++static void stop_blinking(void) ++{ ++ if (acerhk_blueled_blinking != -1) { ++ del_timer(&acerhk_timer_blinking); ++ if (verbose) ++ printk(KERN_INFO "acerhk: blueled blinking stopped\n"); ++ acerhk_blueled_blinking = -1; ++ } ++} ++ ++static void release_input(void) ++{ ++ stop_polling(); ++ input_unregister_device(acerhk_input_dev_ptr); ++} ++ ++/* }}} */ ++ ++/* {{{ procfs functions */ ++ ++#ifndef CONFIG_PROC_FS ++ ++static int acerhk_proc_init(void) ++{ ++ return 1; ++} ++#else ++ ++/* This macro frees the machine specific function from bounds checking and ++ * things like that... */ ++#define PRINT_PROC(fmt,args...) \ ++ do { \ ++ *len += sprintf( buffer+*len, fmt, ##args ); \ ++ if (*begin + *len > offset + size) \ ++ return( 0 ); \ ++ if (*begin + *len < offset) { \ ++ *begin += *len; \ ++ *len = 0; \ ++ } \ ++ } while(0) ++ ++static int pc_proc_infos( char *buffer, int *len, ++ off_t *begin, off_t offset, int size ) ++{ ++ PRINT_PROC( "Acer hotkeys version %s\n", ACERHK_VERSION); ++ PRINT_PROC( "Model(Type)\t: %s(", acerhk_model_string); ++ switch(acerhk_type) { ++ default: ++ PRINT_PROC( "unknown)\n"); ++ break; ++ case TM_old: ++ PRINT_PROC( "old)\n"); ++ break; ++ case TM_new: ++ PRINT_PROC( "new)\n"); ++ break; ++ case TM_dritek: ++ PRINT_PROC( "Dritek)\n"); ++ break; ++ } ++ if (bios_routine != 0) { ++ PRINT_PROC( "request handler\t: 0x%x\n", bios_routine); ++ if (cmos_index) { ++ PRINT_PROC( "CMOS index\t: 0x%x\n", cmos_index); ++ PRINT_PROC( "events pending\t: %u\n", get_nr_events()); ++ } else { ++ PRINT_PROC( "CMOS index\t: not available\n"); ++ } ++ if (acerhk_polling_state == 1) ++ PRINT_PROC( "kernel polling\t: active\n"); ++ else ++ PRINT_PROC( "kernel polling\t: inactive\n"); ++ PRINT_PROC( "autoswitch wlan\t: "); ++ if (autowlan == 1) ++ PRINT_PROC( "enabled\n"); ++ else ++ PRINT_PROC( "disabled\n"); ++ } else { ++ PRINT_PROC( "request handler\t: not found\n"); ++ PRINT_PROC( "kernel polling\t: not possible\n"); ++ } ++ /* model specific infos */ ++ if (acerhk_type == TM_dritek) { ++ PRINT_PROC( "use of Dritek EC: "); ++ if (usedritek) ++ PRINT_PROC( "enabled\n"); ++ else ++ PRINT_PROC( "disabled\n"); ++ } ++ if (acerhk_type == TM_old) ++ PRINT_PROC( "preg400\t\t: 0x%p\n", preg400); ++ return (1); ++} ++ ++static int acerhk_proc_info( char *buffer, char **start, off_t offset, ++ int size, int *eof, void *data ) ++{ ++ int len = 0; ++ off_t begin = 0; ++ ++ *eof = pc_proc_infos( buffer, &len, &begin, offset, size ); ++ ++ if (offset >= begin + len) ++ return( 0 ); ++ *start = buffer + (offset - begin); ++ return( size < begin + len - offset ? size : begin + len - offset ); ++ ++} ++ ++static int acerhk_proc_key( char *buffer, char **start, off_t offset, ++ int size, int *eof, void *data ) ++{ ++ if (size >= 5 && offset == 0) { ++ if (acerhk_type == TM_dritek || acerhk_polling_state == 1) { ++ snprintf(buffer+offset, size, "n/a\n"); ++ } else { ++ snprintf(buffer+offset, size, "0x%02x\n", filter_idle_value(get_fnkey_event())); ++ } ++ *eof = 1; ++ return 5; ++ } ++ *eof = 1; ++ return 0; ++} ++ ++static int acerhk_proc_led(struct file* file, const char* buffer, ++ unsigned long count, void* data) ++{ ++ char str[4]; ++ int len; ++ if (count > 4) ++ len = 4; ++ else ++ len = count; ++ if (copy_from_user(str, buffer, len)) ++ return -EFAULT; ++ str[3] = '\0'; ++ if ( ( (len >= 2) && (!strncmp(str, "on", 2) || !strncmp(str, "an", 2)) ) ++ || str[0] == '1') ++ set_mail_led(1); ++ else ++ set_mail_led(0); ++ return len; ++} ++ ++static int acerhk_proc_wirelessled(struct file* file, const char* buffer, ++ unsigned long count, void* data) ++{ ++ char str[4]; ++ int len; ++ if (count > 4) ++ len = 4; ++ else ++ len = count; ++ if (copy_from_user(str, buffer, len)) ++ return -EFAULT; ++ str[3] = '\0'; ++ if ( ( (len >= 2) && (!strncmp(str, "on", 2) || !strncmp(str, "an", 2)) ) ++ || str[0] == '1') { ++ if (acerhk_model_features & TM_F_WLAN_EC1) ++ enable_wlan_ec_1(); ++ else if (acerhk_model_features & TM_F_WLAN_EC2) ++ enable_wlan_ec_2(); ++ else ++ wbutton_fct_2(1); ++ } ++ else { ++ if (acerhk_model_features & TM_F_WLAN_EC1) ++ disable_wlan_ec_1(); ++ else if (acerhk_model_features & TM_F_WLAN_EC2) ++ disable_wlan_ec_2(); ++ else ++ wbutton_fct_2(0); ++ } ++ return len; ++} ++ ++ ++/* Modified by Antonio Cuni: added support for blinking ++ possible values: ++ - off, 0: led always off ++ - on, an, 1: led alway on ++ - n (a number): led blinking; n is the delay between ++ two changes of state, in jiffies; n must ++ be > 50, to prevent the user from overloading ++ the kernel. ++ ++ */ ++static int acerhk_proc_blueled(struct file* file, const char* buffer, ++ unsigned long count, void* data) ++{ ++ const int MAXLEN=11; ++ char str[MAXLEN]; ++ int len; ++ int isNumber; ++ ++ if (count > MAXLEN) ++ len = MAXLEN; ++ else ++ len = count; ++ if (copy_from_user(str, buffer, len)) ++ return -EFAULT; ++ str[MAXLEN - 1] = '\0'; ++ ++ /* try to parse a number */ ++ isNumber = sscanf(str, "%u", &acerhk_blueled_blinking_delay); ++ /* if the delay is 0, turn off the led */ ++ if (isNumber && acerhk_blueled_blinking_delay != 0 && acerhk_blueled_blinking_delay != 1) { ++ if (acerhk_blueled_blinking_delay < 50) ++ printk(KERN_INFO"acerhk: blinking request rejected. The delay must be > 50.\n"); ++ else { ++ if (verbose) ++ printk(KERN_INFO"acerhk: blinking delay set to %u.\n", acerhk_blueled_blinking_delay); ++ start_blinking(); ++ } ++ } else if (acerhk_blueled_blinking_delay == 1 || !strncmp(str, "on", 2) || !strncmp(str, "an", 2)) { ++ stop_blinking(); ++ if (acerhk_model_features & TM_F_BLUE_EC1) ++ enable_bluetooth_ec_1(); ++ else if (acerhk_model_features & TM_F_BLUE_EC2) ++ enable_bluetooth_ec_2(); ++ else ++ wbutton_fct_1(1); ++ } else { ++ /* it's 0 or everything else */ ++ stop_blinking(); ++ if (acerhk_model_features & TM_F_BLUE_EC1) ++ disable_bluetooth_ec_1(); ++ else if (acerhk_model_features & TM_F_BLUE_EC2) ++ disable_bluetooth_ec_2(); ++ else ++ wbutton_fct_1(0); ++ } ++ return len; ++} ++ ++#ifdef ACERDEBUG ++static void do_debug(const char* buffer, unsigned long len) ++{ ++ unsigned int h, i; ++ switch (buffer[0]) { ++ case 'b': ++ /* test WLAN on TM 4001 */ ++ switch (buffer[1]) { ++ case '0': ++ disable_wlan_ec_1(); ++ break; ++ case '1': ++ default: ++ enable_wlan_ec_1(); ++ } ++ break; ++ case 'B': ++ /* test BLUETOOTH on TM 4001 */ ++ switch (buffer[1]) { ++ case '0': ++ disable_bluetooth_ec_1(); ++ break; ++ case '1': ++ default: ++ enable_bluetooth_ec_1(); ++ } ++ break; ++ case 'D': ++ /* test "DMM Function Enabled" entry of TM 4150/4650 */ ++ enable_dmm_function(); ++ break; ++ case 'i': ++ case '1': ++#ifndef KERNEL26 ++ MOD_INC_USE_COUNT; ++#endif ++ break; ++ case 'e': ++ switch (buffer[1]) { ++ case '1': ++ start_polling(); ++ break; ++ default: ++ stop_polling(); ++ } ++ break; ++ case 'k': ++ for (i = 0; i <= 255;i++) { ++ input_report_key(acerhk_input_dev_ptr, i, 1); ++ input_report_key(acerhk_input_dev_ptr, i, 0); ++ } ++ break; ++ case 'm': ++ /* set mapping key names -> input events */ ++ sscanf(&buffer[2],"%x", &i); ++ h = buffer[1] - '0' + 1; ++ printk("acerhk: key name %x maps to %x\n", h, i); ++ acerhk_name2event[h] = i; ++ break; ++ case 'M': ++ /* test mute LED on dritek hardware */ ++ switch (buffer[1]) { ++ case '0': ++ disable_mute_led_ec(); ++ break; ++ case '1': ++ default: ++ enable_mute_led_ec(); ++ } ++ break; ++ case 'p': ++ printk("acerhk: pbutton = 0x%x\n", pbutton_fct()); ++ break; ++ case 's': ++ /* send key event to test the key translation in input system */ ++ sscanf(&buffer[1],"%x", &h); ++ printk("acerhk: sending key event 0x%x\n", h); ++ input_report_key(acerhk_input_dev_ptr, h, 1); ++ input_report_key(acerhk_input_dev_ptr, h, 0); ++ break; ++ case 'S': ++ /* simulate key codes to test the key translation in acerhk */ ++ sscanf(&buffer[1],"%x", &h); ++ send_key_event(transl8_key_code(h)); ++ break; ++ case 't': ++ printk("acerhk: thermal event = 0x%x\n", get_thermal_event()); ++ break; ++ case 'w': ++ /* test the wbutton functions, someone really needs to have another look ++ at the windows driver */ ++ switch (buffer[1]) { ++ case '2': ++ printk("acerhk: wbutton_2(%d) = 0x%x\n", buffer[2]-'0', wbutton_fct_2(buffer[2]-'0')); ++ break; ++ case '1': ++ default: ++ printk("acerhk: wbutton_1(%d) = 0x%x\n", buffer[2]-'0', wbutton_fct_1(buffer[2]-'0')); ++ } ++ break; ++ case 'W': ++ /* test wireless HW/LED on some models using dritek hardware */ ++ switch (buffer[1]) { ++ case '0': ++ disable_wireless_ec(); ++ break; ++ case '1': ++ default: ++ enable_wireless_ec(); ++ } ++ break; ++ case 'v': ++ verbose = buffer[1]-'0'; ++ printk("acerhk: verbosity level changed to %d\n", verbose); ++ break; ++ case 'd': ++ case '0': ++ default: ++#ifndef KERNEL26 ++ MOD_DEC_USE_COUNT; ++#endif ++ break; ++ } ++} ++ ++static int acerhk_proc_debug(struct file* file, const char* buffer, ++ unsigned long count, void* data) ++{ ++ char str[5]; ++ int len; ++ if (count > 5) ++ len = 5; ++ else ++ len = count; ++ if (copy_from_user(str, buffer, len)) ++ return -EFAULT; ++ str[4] = '\0'; ++ do_debug(str, len); ++ return len; ++} ++#endif ++ ++static int acerhk_proc_init(void) ++{ ++ int retval; ++ struct proc_dir_entry *entry; ++ /* create own directory */ ++ proc_acer_dir = proc_mkdir("driver/acerhk", NULL); ++ if (proc_acer_dir == NULL) { ++ retval = 0; ++ printk(KERN_INFO"acerhk: could not create /proc/driver/acerhk\n"); ++ } ++ else { ++ proc_acer_dir->owner = THIS_MODULE; ++ /* now create several files, first general info ... */ ++ entry = create_proc_read_entry("info", ++ 0444, proc_acer_dir, acerhk_proc_info, NULL); ++ if (entry == NULL) { ++ printk(KERN_INFO"acerhk: cannot create info file\n"); ++ remove_proc_entry("driver/acerhk", NULL); ++ retval = 0; ++ } else { ++ entry->owner = THIS_MODULE; ++ /* ... last pressed key ... */ ++ entry = create_proc_read_entry("key", ++ 0444, proc_acer_dir, acerhk_proc_key, NULL); ++ if (entry == NULL) { ++ printk(KERN_INFO"acerhk: cannot create key file\n"); ++ remove_proc_entry("info", proc_acer_dir); ++ remove_proc_entry("driver/acerhk", NULL); ++ retval = 0; ++ } else { ++ entry->owner = THIS_MODULE; ++ /* ... and led control file */ ++ entry = create_proc_entry("led", 0222, proc_acer_dir); ++ if (entry == NULL) { ++ printk(KERN_INFO"acerhk: cannot create LED file\n"); ++ remove_proc_entry("info", proc_acer_dir); ++ remove_proc_entry("key", proc_acer_dir); ++ remove_proc_entry("driver/acerhk", NULL); ++ retval = 0; ++ } ++ else { ++ entry->write_proc = acerhk_proc_led; ++ entry->owner = THIS_MODULE; ++ /* ... and wireless led controll file */ ++ entry = create_proc_entry("wirelessled", 0222, proc_acer_dir); ++ if (entry == NULL) { ++ printk(KERN_INFO"acerhk: cannot create wirelessled file\n"); ++ remove_proc_entry("info", proc_acer_dir); ++ remove_proc_entry("key", proc_acer_dir); ++ remove_proc_entry("led", proc_acer_dir); ++ remove_proc_entry("driver/acerhk", NULL); ++ retval = 0; ++ } ++ else { ++ entry->write_proc = acerhk_proc_wirelessled; ++ entry->owner = THIS_MODULE; ++ /* ... and bluetooth led controll file */ ++ entry = create_proc_entry("blueled", 0222, proc_acer_dir); ++ if (entry == NULL) { ++ printk(KERN_INFO"acerhk: cannot create blueled file\n"); ++ remove_proc_entry("info", proc_acer_dir); ++ remove_proc_entry("key", proc_acer_dir); ++ remove_proc_entry("led", proc_acer_dir); ++ remove_proc_entry("wirelessled", proc_acer_dir); ++ remove_proc_entry("driver/acerhk", NULL); ++ retval = 0; ++ } else { ++ entry->write_proc = acerhk_proc_blueled; ++ entry->owner = THIS_MODULE; ++ retval = 1; ++#ifdef ACERDEBUG ++ /* add extra file for debugging purposes */ ++ entry = create_proc_entry("debug", 0222, proc_acer_dir); ++ if (entry == NULL) { ++ printk(KERN_INFO"acerhk: cannot create debug file\n"); ++ remove_proc_entry("info", proc_acer_dir); ++ remove_proc_entry("key", proc_acer_dir); ++ remove_proc_entry("led", proc_acer_dir); ++ remove_proc_entry("wirelessled", proc_acer_dir); ++ remove_proc_entry("blueled", proc_acer_dir); ++ remove_proc_entry("driver/acerhk", NULL); ++ retval = 0; ++ } ++ else { ++ entry->write_proc = acerhk_proc_debug; ++ entry->owner = THIS_MODULE; ++ retval = 1; ++ } ++#endif ++ } ++ } ++ } ++ } ++ } ++ } ++ return retval; ++} ++ ++static void acerhk_proc_cleanup(void) ++{ ++ if (proc_acer_dir) { ++ remove_proc_entry("info", proc_acer_dir); ++ /* On dritek type hardware key file is already removed */ ++ if (acerhk_type != TM_dritek) ++ remove_proc_entry("key", proc_acer_dir); ++ remove_proc_entry("led", proc_acer_dir); ++ remove_proc_entry("wirelessled", proc_acer_dir); ++ remove_proc_entry("blueled", proc_acer_dir); ++#ifdef ACERDEBUG ++ remove_proc_entry("debug", proc_acer_dir); ++#endif ++ remove_proc_entry("driver/acerhk", NULL); ++ proc_acer_dir = NULL; ++ } ++} ++ ++#endif /* CONFIG_PROC_FS */ ++ ++/* }}} */ ++ ++/* {{{ file operations */ ++ ++static int acerhk_ioctl( struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg ) ++{ ++ int retval; ++ switch( cmd ) { ++ case ACERHK_GET_KEYCOUNT: ++ { ++ char nr; ++ nr = get_nr_events(); ++ put_user(nr, (char*)arg); ++ retval = 0; ++ break; ++ } ++ case ACERHK_GET_KEYID: ++ { ++ char id; ++ id = get_fnkey_event(); ++ put_user(id, (char*)arg); ++ retval = 0; ++ break; ++ } ++ case ACERHK_CONNECT: ++ launch_connect(1); ++ retval = 0; ++ break; ++ case ACERHK_START_POLLING: ++ start_polling(); ++ retval = 0; ++ break; ++ case ACERHK_STOP_POLLING: ++ stop_polling(); ++ retval = 0; ++ break; ++ case ACERHK_DISCONNECT: ++ launch_connect(0); ++ retval = 0; ++ break; ++ case ACERHK_GET_THERMAL_EVENT: ++ { ++ short event; ++ event = get_thermal_event(); ++ put_user(event, (short*)arg); ++ retval = 0; ++ break; ++ } ++ case ACERHK_MAIL_LED_OFF: ++ set_mail_led(0); ++ retval = 0; ++ break; ++ case ACERHK_MAIL_LED_ON: ++ set_mail_led(1); ++ retval = 0; ++ break; ++ case ACERHK_GET_KEY_MAP: ++ if (copy_to_user((t_map_name2event*)arg, &acerhk_name2event, sizeof(acerhk_name2event))) ++ retval = -EFAULT; ++ else ++ retval = 0; ++ break; ++ case ACERHK_SET_KEY_MAP: ++ if (copy_from_user(&acerhk_name2event, (t_map_name2event*)arg, sizeof(acerhk_name2event))) ++ retval = -EFAULT; ++ else { ++ if (verbose) { ++ printk(KERN_INFO"acerhk: changed key mapping\n"); ++ print_mapping(); ++ } ++ retval = 0; ++ } ++ break; ++ default: ++ retval = -EINVAL; ++ } ++ return retval; ++} ++ ++#ifdef ACERDEBUG ++static ssize_t acerhk_write (struct file* file, const char* buffer, size_t length, loff_t* offset) ++{ ++ if (length) ++ do_debug(buffer, length); ++ return length; ++} ++#endif ++ ++static int acerhk_open( struct inode *inode, struct file *file ) ++{ ++ return 0; ++} ++ ++static int acerhk_release( struct inode *inode, struct file *file ) ++{ ++ return 0; ++} ++ ++static struct file_operations acerhk_fops = { ++ owner: THIS_MODULE, ++ ioctl: acerhk_ioctl, ++ open: acerhk_open, ++#ifdef ACERDEBUG ++ write: acerhk_write, ++#endif ++ release: acerhk_release, ++}; ++ ++static struct miscdevice acerhk_dev = { ++ MISC_DYNAMIC_MINOR, ++ "acerhk", ++ &acerhk_fops ++}; ++ ++/* }}} */ ++ ++static void __init model_init(void) ++{ ++ /* set callroutine, features and keymap for model */ ++ setup_model_features(acerhk_series); ++ /* override initial state of wireless hardware if specified by module options */ ++ if (wlan_state >= 0) acerhk_wlan_state = wlan_state; ++ if (bluetooth_state >= 0) acerhk_bluetooth_state = bluetooth_state; ++ /* Launch connect only if available */ ++ if (acerhk_model_features & TM_F_CONNECT) { ++ if (verbose) ++ printk(KERN_INFO"acerhk: Model type %d, calling launch_connect(1)\n", ++ acerhk_type); ++ launch_connect(1); ++ } ++ if ( acerhk_type != TM_dritek ) { ++ get_cmos_index(); ++ } ++ if ( acerhk_type == TM_dritek ) { ++ enable_dritek_keyboard(); ++ } ++ /* added by Antonio Cuni */ ++ init_timer(&acerhk_timer_blinking); ++} ++ ++ ++static void __exit acerhk_cleanup_module (void); ++static int __init acerhk_init(void) ++{ ++ int ret; ++ ++ ret = misc_register( &acerhk_dev ); ++ if (ret) { ++ printk(KERN_ERR "acerhk: can't misc_register on minor=%d\n", ACERHK_MINOR); ++ ret = -EAGAIN; ++ } ++ else if (!acerhk_proc_init()) { ++ printk(KERN_ERR "acerhk: can't create procfs entries\n"); ++ ret = -ENOMEM; ++ misc_deregister( &acerhk_dev ); ++ } ++ else { ++ reg1 = ioremap(0xf0000, 0xffff); ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: area from 0xf000 to 0xffff mapped to %p\n", reg1); ++ reg2 = ioremap(0xe0000, 0xffff); ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: area from 0xe000 to 0xffff mapped to %p\n", reg2); ++ /* the area 0x400 is used as data area by earlier (520) series */ ++ preg400 = ioremap(0x400, 0xfff); ++ if (verbose > 1) ++ printk(KERN_INFO"acerhk: area from 0x400 to 0x13ff mapped to %p\n", preg400); ++ /* attach to input system */ ++ init_input(); ++ memset(acerhk_model_string, 0x00, ACERHK_MODEL_STRLEN); ++#ifdef DUMMYHW ++ acerhk_model_addr = (void*)0x12345678; ++ /* copy the string, but not more than 15 characters */ ++ strncpy(acerhk_model_string, "TravelmateDummy", ACERHK_MODEL_STRLEN-1); ++ /* set callroutine for model */ ++ if (force_series) ++ acerhk_series = force_series; ++ else ++ acerhk_series = 2000; ++ setup_model_features(acerhk_series); ++ printk(KERN_INFO "Acer Travelmate hotkey driver v" ACERHK_VERSION " dummy\n"); ++ if ( acerhk_type == TM_dritek ) ++ enable_dritek_keyboard(); ++ if (poll) ++ start_polling(); ++ init_timer(&acerhk_timer_blinking); ++#else ++ bios_routine = find_hk_area(); ++ if (!force_series) ++ probe_model(); ++ else { ++ if (verbose) ++ printk(KERN_INFO"acerhk: forced laptop series to %d\n", force_series); ++ acerhk_series = force_series; ++ } ++ /* do model specific initialization */ ++ model_init(); ++ /* Without a bios routine we cannot do anything except on dritek ++ type HW, unload on other types */ ++ if (bios_routine || (acerhk_type == TM_dritek)) { ++ ret = 0; ++ if (verbose && bios_routine) ++ printk(KERN_INFO"acerhk: bios routine found at 0x%x\n", bios_routine); ++ printk(KERN_INFO "Acer Travelmate hotkey driver v" ACERHK_VERSION "\n"); ++ /* If automatic switching of wlan is wanted but polling is disabled, ++ automatically enable it */ ++ if (!poll && autowlan) { ++ printk(KERN_INFO "Automatic switching of wireless hardware needs polling, enabling it\n"); ++ poll = 1; ++ } ++ /* start automatic polling of key presses if wanted and bios routine found */ ++ if (poll && bios_routine) ++ start_polling(); ++ } else { ++ printk(KERN_ERR "acerhk: can't find bios routine, cannot do anything for you, sorry!\n"); ++ ret = -ENOMEM; ++ acerhk_cleanup_module(); ++ } ++#endif ++ } ++ return ret; ++} ++ ++static void __exit acerhk_cleanup_module (void) ++{ ++ acerhk_proc_cleanup(); ++ stop_blinking(); ++ if (reg1) ++ iounmap(reg1); ++ if (reg2) ++ iounmap(reg2); ++ if (preg400) ++ iounmap(preg400); ++ release_input(); ++ misc_deregister( &acerhk_dev ); ++ if ( acerhk_type == TM_dritek ) { ++ disable_dritek_keyboard(); ++ } ++ if (verbose > 2) ++ printk(KERN_INFO "acerhk: unloaded\n"); ++} ++ ++module_init(acerhk_init); ++module_exit(acerhk_cleanup_module); ++ ++MODULE_AUTHOR("Olaf Tauber"); ++MODULE_DESCRIPTION("AcerHotkeys extra buttons keyboard driver"); ++MODULE_LICENSE("GPL"); ++ ++#ifndef KERNEL26 ++EXPORT_NO_SYMBOLS; ++#endif ++ ++#else ++#error This driver is only available for X86 architecture ++#endif ++/* ++ * Local variables: ++ * c-indent-level: 4 ++ * tab-width: 4 ++ * End: ++ */ ++ +diff -Nurp linux-2.6.37/3rdparty/acerhk/acerhk.h linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.h +--- linux-2.6.37/3rdparty/acerhk/acerhk.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.h 2005-06-17 00:10:12.000000000 +0300 +@@ -0,0 +1,91 @@ ++#ifndef __ACERHK_H__ ++#define __ACERHK_H__ ++ ++#include ++ ++#define ACERHK_MINOR MISC_DYNAMIC_MINOR ++ ++#define ACERHK_GET_KEYCOUNT _IOR('p', 0x01, char) /* Get number of cached key presses */ ++#define ACERHK_GET_KEYID _IOR('p', 0x02, char) /* Get first key in queue */ ++#define ACERHK_CONNECT _IO('p', 0x03) /* ? */ ++#define ACERHK_DISCONNECT _IO('p', 0x04) /* ? */ ++#define ACERHK_GET_THERMAL_EVENT _IOR('p', 0x05, short) /* ? */ ++#define ACERHK_MAIL_LED_OFF _IO('p', 0x10) /* switch mail LED off */ ++#define ACERHK_MAIL_LED_ON _IO('p', 0x11) /* switch mail LED on (blinking) */ ++#define ACERHK_START_POLLING _IO('p', 0x12) /* poll keys in kernel, send real key events */ ++#define ACERHK_STOP_POLLING _IO('p', 0x13) /* stop key polling in kernel */ ++#define ACERHK_GET_KEY_MAP _IOR('p', 0x20, int) /* Get mapping of key names to key events, */ ++#define ACERHK_SET_KEY_MAP _IOW('p', 0x21, int) /* Set mapping of key names to key events */ ++ ++/* all possible keys (known to me) */ ++typedef enum e_key_names { ++ k_none = 0, ++ k_help = 1, /* Fn+F1 */ ++ k_setup = 2, /* Fn+F2 */ ++ k_p1 = 3, ++ k_p2 = 4, ++ k_p3 = 5, ++ k_www = 6, ++ k_mail = 7, ++ k_wireless = 8, ++ k_power = 9, /* Fn+F3 */ ++ k_mute = 10, /* Fn+F8 */ ++ k_volup = 11, /* Fn+Up */ ++ k_voldn = 12, /* Fn+Down */ ++ k_res = 13, /* resolution change on Medion MD 40100 */ ++ k_close = 14, /* if lid is closed in tablet mode */ ++ k_open = 15, /* if lid is opend in tablet mode */ ++ k_wireless2 = 16, /* second wireless button on TM 243LC */ ++ k_play = 17, /* Play/Pause found on AOpen */ ++ k_stop = 18, /* Stop/Eject found on AOpen */ ++ k_prev = 19, /* Prev found on AOpen */ ++ k_next = 20, /* Next found on AOpen */ ++ k_display = 21 /* Change internal/external display on MD 42200 */ ++} t_key_names; ++#define NR_KEY_NAMES 22 ++typedef unsigned int t_map_name2event[NR_KEY_NAMES]; ++ ++#ifdef __KERNEL__ ++ ++/* available features */ ++#define TM_F_WLAN_EC1 0x00000010 ++#define TM_F_BLUE_EC1 0x00000020 ++#define TM_F_WLAN_EC2 0x00000040 ++#define TM_F_BLUE_EC2 0x00000080 ++#define TM_F_MUTE_LED_EC 0x00001000 ++#define TM_F_MAIL_LED 0x00010000 ++#define TM_F_MAIL_LED_EC 0x00020000 ++#define TM_F_MAIL_LED_EC2 0x00040000 ++#define TM_F_MAIL_LED_EC3 0x00080000 ++ ++#define TM_F_CONNECT 0x00100000 ++#define TM_F_THERMAL 0x00200000 ++#define TM_F_PBUTTON 0x00400000 ++#define TM_F_WBUTTON 0x00800000 ++ ++typedef enum acer_type { ++ TM_unknown, ++ /* 200, 210, 520, 600 and 730 series, Medion MD42200 */ ++ TM_old, ++ /* C100, C110, 220, 230, 240, 260, 350, 360, 610, 620, 630, 740 series ++ Medion MD40100, Aspire 1600, FS Amilo */ ++ TM_new, ++ /* Aspire 13xx, 14xx, 1700, TM 290, 650, 660, 800 */ ++ TM_dritek ++} t_acer_type; ++ ++struct register_buffer { ++ unsigned int eax; ++ unsigned int ebx; ++ unsigned int ecx; ++ unsigned int edx; ++ unsigned int edi; ++ unsigned int esi; ++ unsigned int ebp; ++}; ++ ++typedef asmlinkage void (*bios_call) (struct register_buffer *); ++ ++#endif ++ ++#endif +diff -Nurp linux-2.6.37/3rdparty/acerhk/AUTHORS linux-2.6.37.3rdparty/3rdparty/acerhk/AUTHORS +--- linux-2.6.37/3rdparty/acerhk/AUTHORS 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/AUTHORS 2006-11-10 00:02:55.000000000 +0200 +@@ -0,0 +1 @@ ++Olaf Tauber +\ Ingen nyrad vid filslut +diff -Nurp linux-2.6.37/3rdparty/acerhk/COPYING linux-2.6.37.3rdparty/3rdparty/acerhk/COPYING +--- linux-2.6.37/3rdparty/acerhk/COPYING 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/COPYING 2002-05-08 22:01:52.000000000 +0300 +@@ -0,0 +1,340 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Library General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++Each version is given a distinguishing version number. If the Program ++specifies a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++REPAIR OR CORRECTION. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGES. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++convey the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ ++ Copyright (C) 19yy ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) 19yy name of author ++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Ty Coon, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Library General ++Public License instead of this License. +diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/acertm.def linux-2.6.37.3rdparty/3rdparty/acerhk/doc/acertm.def +--- linux-2.6.37/3rdparty/acerhk/doc/acertm.def 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/acertm.def 2003-08-24 19:46:37.000000000 +0300 +@@ -0,0 +1,56 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ Konfiguration ++ ++ Mute/Unmute ++ Lauter ++ Leiser ++ ++ Terminal ++ XEmacs ++ Play/Pause ++ Play/Pause ++ ++ ++ ++ +diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/FAQ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/FAQ +--- linux-2.6.37/3rdparty/acerhk/doc/FAQ 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/FAQ 2005-11-10 19:45:33.000000000 +0200 +@@ -0,0 +1,150 @@ ++****************************************************************************** ++ ++Q: I have a (non Acer) notebook which is not recognized by your driver but I ++think it should. What information do you need? ++ ++A: If it is a non-Acer laptop I would like to know, what makes you think that ++your laptop would work with my driver. A windows driver/utility package of the ++name "LaunchManager" or "EasyButton" is a good hint. If it is clear that your ++hardware is okay, I need the string with your model name from BIOS. Please ++don't ask me how to do it. To find out which functions of your laptop the ++driver will support, it is best, if you can tell me where to get this ++"LaunchManager" or "EasyButton" package. ++If it is a model from 2003 or newer then chances are good that it's a dritek ++type one. In this case try the option "force_series=6000" to at least enable ++the keys. ++ ++****************************************************************************** ++ ++Q: Why can't I activate my wireless hardware on Acer TM 420/430/6000/8000 ...? ++ ++A: On many newer laptops (Dritek type) I do not now how to do this, ++sorry. Someone would need to find out how windows does it. Volunteers? ++ ++****************************************************************************** ++ ++Q: I know that wireless hardware is supported on my Aspire 1690/TravelMate4600, ++but I cannot get it to work, why? ++ ++A: On these model (and similar) the wireless hardware is controlled on two ++different levels. One is controlled by acerhk's xxxled file, the other level ++is controlled by the key itself. To actually activate the hardware you need to ++write '1' to the xxled file and you need to press the corresponding button. ++Example: ++echo 1 > /proc/driver/acerhk/wirelessled ++ ++ ++****************************************************************************** ++ ++Q: Why do I always get 0x00 from /proc/driver/acerhk/key? ++ ++A1: Check the type of your laptop if /proc/driver/acerhk/info. If it is ++"Dritek", you cannot read the keys from the driver. Starting from version ++0.5.17 you get "n/a" out of the key file in this case. ++A2: If you have polling enabled (default) you will almost always read 0x00 ++from this file. This is because in every polling cycle it is checked for key ++events and if there is one, it is instantly translated and sent to the kernel ++input system. Starting with version 0.5.20 you get "n/a" out of the key file ++in this case. ++ ++****************************************************************************** ++ ++Q: When I press a key, nothing happens. Why are they not working? ++ ++A: Most keys won't do anything by themself. If nothing happens, that is ++because no programm knows what to do with the new keys. You need to assign ++actions to them, use the hotkey manager of your desktop to do that. If ++using Gnome, you find it under desktop settings - keyboard shortcuts. ++ ++****************************************************************************** ++ ++Q: The driver works for my laptop, but not all of the keys are working. What ++can I do? ++ ++A: There are some keys/key combinations which generate an ACPI event, ++e.g. Fn+F4 on some models or the lid button. ++If you have a different laptop than the one which got detected (or you used ++"force_series=xxx" anyway), then it is possible that the mapping acerhk uses ++to translate the codes from the buttons to key events is wrong. ++In this case, load the driver with "verbose=4" and press the buttons which do ++not work. Then look for messages from acerhk of the form "translated acer key ++code xxx to no key". Note these codes together with the button they belong to ++and send me this list along with the model name of your laptop. ++If you do not see a usable name in /proc/driver/acerhk/info please try the ++tools dmidecode/biosdecode/vpddecode to find this name. ++ ++****************************************************************************** ++Q: I press the wireless key but the hardware doesn't get activated, what's wrong? ++ ++A: The driver is only on older models (non-Dritek type) able to read ++keypresses by itself and toggle the hardware/LED automatically (with option ++autowlan=1). On Dritek type models this must be done by writing the desired ++value to one of the wireless files in the proc filesystem. In most ++cases this would be /proc/drivers/acerhk/wirelessled to control wlan ++hardware (blueled for Bluetooth hardware): ++ ++echo 1 > /proc/driver/acerhk/wirelessled ++ ++But you could use a hotkey manager to do that automatically when you press the ++button. In this case be aware that on some models the button generates ++different key events according to the actual state of the wireless hardware. ++ ++****************************************************************************** ++ ++Q: My keys do not work, I only get kernel messages of the form: ++atkbd.c: Unknown key pressed (translated set 2, code 0xf4 on isa0060/serio0). ++atkbd.c: Use 'setkeycodes e074 ' to make it known. ++ ++A: Press each of the buttons and note the mentioned ++code for it (e074 in this example). You should get a list like this: ++P1 e074 ++P2 e075 ++... ++If you are finished with it, look into /usr/include/linux/input.h, using ++your favourite text viewer/editor. Search for "KEY_STOP". You should see ++the following line in the file: ++ ++#define KEY_STOP 128 ++ ++After it many more lines with equal #defines should be visible. Look for ++key names which best match the names of your buttons, e.g. KEY_PROG1, ++KEY_WWW and so on. Note the numbers assigned to the names, for KEY_STOP ++this would be 128, for KEY_PROG1 148. ++Now you have a list with three items per entry, your button, a code ++from the kernel messages and a corresponding number from the file ++linux.h: ++P1 e074 148 ++P2 e075 149 ++... ++For each line in this list, issue the setkeycodes command as mentioned ++in the kernel message: ++setkeycodes e074 148 ++setkeycodes e075 149 ++... ++After doing that, the keys should be available for your hotkey manager. ++ ++****************************************************************************** ++ ++Q: My WLAN hardware gets activated through /proc/driver/acerhk/wirelessled, ++but the LED is not working. Why not? ++ ++A: Try if adding the option "led=1" to your wireless module helps. For the ++ipw2200 driver it works, as Didier CLERC found out: ++ ++ I have to load the module "ipw2200" with the option "led=1", then ++ the wifi button blinks until a network is detected. ++ ++****************************************************************************** ++ ++Q: I have an unsupported laptop, but the driver works when I force the series ++to a type like 610 or 2100. Only my buttons doesn't get recognized, why? ++ ++A: On models of the type TM_new (like TravelMate 600, 2100 and many other, see ++acerhk.c, function setup_model_features for details) the buttons use different ++codes on different models. Therefore the driver needs to know these codes. You ++can get them if you load the driver with verbose=4, press the buttons and key ++combinations (Fn+xx) you are interested in and look for kernel messages of the ++form "acerhk: translated acer key code 0xnn to ...". Write down the key code ++for each button/key combination and send them to me so I can patch the ++driver. I also need your model string to make autodetection work, so include ++/proc/driver/acerk/info. +\ Ingen nyrad vid filslut +diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/IOCTL linux-2.6.37.3rdparty/3rdparty/acerhk/doc/IOCTL +--- linux-2.6.37/3rdparty/acerhk/doc/IOCTL 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/IOCTL 2004-01-31 22:38:17.000000000 +0200 +@@ -0,0 +1,61 @@ ++Documentation of possible IOCTLs used by acerhk +++++++++++++++++++++++++++++++++++++++++++++++++ ++ ++ACERHK_GET_KEYCOUNT ++ Read the number of unread key presses in queue ++ Parameter: pointer to char ++ ++ACERHK_GET_KEYID ++ Read the code of the first key press (oldest) in queue ++ Parameter: pointer to char ++ ++ACERHK_CONNECT ++ Don't know what it does, used in windows driver ++ ++ACERHK_DISCONNECT ++ Don't know what it does, used in windows driver ++ ++ACERHK_GET_THERMAL_EVENT ++ Don't know what it does, used in windows driver ++ Parameter: pointer to short ++ ++ACERHK_MAIL_LED_OFF ++ Switch off the LED of the mail button(if available) ++ ++ACERHK_MAIL_LED_ON ++ Switch on the LED of the mail button (if available) ++ ++ACERHK_START_POLLING ++ Start polling (and translation to key events) in kernel ++ ++ACERHK_STOP_POLLING ++ Stop polling in kernel ++ ++ACERHK_GET_KEY_MAP ++ Get mapping of key names to key events ++ Parameter: pointer to t_map_name2event ++ ++ACERHK_SET_KEY_MAP ++ Set mapping of key names to key events ++ Parameter: pointer to t_map_name2event ++ ++IOCTLs used by windows driver +++++++++++++++++++++++++++++++ ++device name: ++\DosDevices\HOTKEY ++\Device\HOTKEY ++ ++ ++630 series: ++0x222404 ++ Get CMOS index ++0x222408 ++ ACERHK_GET_KEYCOUNT ++0x22240C ++ ACERHK_GET_KEYID ++0x222410 ++ ACERHK_MAIL_LED_OFF/ACERHK_MAIL_LED_ON ++0x222414 ++ ACERHK_CONNECT/ACERHK_DISCONNECT ++0x222418 ++ ACERHK_GET_THERMAL_EVENT +diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/keycodes linux-2.6.37.3rdparty/3rdparty/acerhk/doc/keycodes +--- linux-2.6.37/3rdparty/acerhk/doc/keycodes 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/keycodes 2004-04-18 20:42:59.000000000 +0300 +@@ -0,0 +1,26 @@ ++standard X keycodes with older (acerhk controlled) hardware from Acer ++********************************************************************* ++help (Fn+F1) 226 ++setup (Fn+F2) 129 ++p1 153 ++p2 144 ++p3 171 ++www 178 ++mail 236 ++wireless 147 ++power (Fn+F3) 222 ++mute (Fn+F8) 166 ++volup (Fn+Up) 158 ++voldn (Fn+Down) 165 ++ ++standard X keycodes with newer (acerhk activated) hardware from Dritek ++********************************************************************** ++help (Fn+F1) 226 ++setup (Fn+F2) 129 ++p1 153 ++p2 144 ++www 178 ++mail 236 ++volup (Fn+Up) 176 ++voldn (Fn+Down) 174 ++mute (Fn+F8) 160 +diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/md95400.def linux-2.6.37.3rdparty/3rdparty/acerhk/doc/md95400.def +--- linux-2.6.37/3rdparty/acerhk/doc/md95400.def 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/md95400.def 2005-03-01 22:46:53.000000000 +0200 +@@ -0,0 +1,14 @@ ++ ++ ++ ++Mail ++Web ++Play/Pause ++Stop ++Previous ++Next ++Leiser ++Lauter ++ ++ ++ +diff -Nurp linux-2.6.37/3rdparty/acerhk/INSTALL linux-2.6.37.3rdparty/3rdparty/acerhk/INSTALL +--- linux-2.6.37/3rdparty/acerhk/INSTALL 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/INSTALL 2005-12-07 14:27:16.000000000 +0200 +@@ -0,0 +1,109 @@ ++Installation ++************ ++ ++1. You need the kernel sources (or kernel headers for your kernel) ++installed to compile the driver. ++ ++2. Your kernel needs loadable module support with version information for ++modules enabled. Usage of procfs is highly recommended. ++If you want the driver to generate regular keyboard events using ++kernel version 2.4 you need the input system of the kernel enabled ++(Input core support AND keyboard support). In kernel version 2.6 all ++needed functionality should be available by default. ++ ++In most cases you can skip the next step, the Makefile tries do determine ++the correct directory on its own. Change KERNELSRC only if the autodetection ++does not work for you. Otherwise proceed directly with step 4. ++ ++3. Before you compile the driver, change KERNELSRC in the makefile to your ++path to the kernel build environment. If you are using a self compiled kernel, ++point it to the root of your sources. If you are using a packaged kernel of ++your distribution, install the package with kernel headers ++(Debian:kernel-headers) and point KERNELSRC to where the headers and config ++files are located. If you are using Debian, this ++would be "/lib/modules//build". ++ ++4. Do: ++ make ++to compile the driver. If you run into problems because of the makefile not ++recognizing your kernel version correctly, try this: ++ make acerhk.o - kernel version 2.4 ++ make acerhk.ko - kernel version 2.6 ++ ++5. Do: ++ make install ++to automatically copy the driver into the kernel module library. If you've ++done so, proceed directly with step 8. If you want to install the module ++binary yourself (because you want a different location), use steps 6 and 7 ++instead. ++ ++6. Copy the created file "acerhk.o" ("acerhk.ko" with version 2.6) to your ++kernel modules path. In Debian this could be ++"/lib/modules//kernel/drivers/extra/". ++ ++7. Update module dependencies: depmod -a ++ ++8. Try loading the module with: ++ insmod/modprobe acerhk ++If it succeeds - congratulations! If you have procfs enabled, you can try the ++following to test the driver: ++ ++Non-Dritek models: ++Press one of the special keys and after that: ++ cat /proc/driver/acerhk/key ++to read the (hexadezimal) code of the key pressed. It should ++be different from 0x00. ++(Note: You mustn't have the polling feature enabled for this to work, so load ++ the module with poll=0) ++ ++Dritek-models: ++Press one of the special keys and look for the generated key with "xev". If ++there is none, then you should see at least kernel messages about using ++setkeycodes. ++ ++If your notebook has a mail led you can try this: ++ echo on > /proc/driver/acerhk/led ++This should sete the mail led to blinking mode. ++ echo off > /proc/driver/acerhk/led ++turns it off again. ++See README for further usage information. ++ ++If the module didn't load look into your kernel messages what went wrong. If ++you see something like the following lines: ++ acerhk: could not find request handler ++ acerhk: can't find ROM area ++ acerhk: unloaded ++then your hardware is not recognized. See README for supported models. If it ++won't work on your notebook, please contact me and I will see if I can fix ++that. ++ ++Integrating the driver into kernel tree version 2.6 ++*************************************************** ++ ++If you want the driver to fully integrate into the kernel tree of version 2.6 ++proceed as follows: ++1. Copy the acerhk directory into the source tree, for instance ++ /usr/src/linux/drivers/misc/acerhk ++2. Include the driver directory in the config files. Add to the Kconfig ++file of the parent directory(/usr/src/linux/drivers/misc/Kconfig): ++ ++config ACERHK ++ tristate "Acerhk driver" ++ depends on EXPERIMENTAL ++ ---help--- ++ This is an experimental acer keyboard driver for ++ acer laptops ++ ++3. Include the acer directory in it's parents ++Makefile(/usr/src/linux/drivers/misc/Makefile): ++ ++obj-$(CONFIG_ACERHK) += acerhk/ ++ ++4. In this case you also need to activate the misc drivers first ++(/usr/src/linux/drivers/Kconfig): ++ ++source "drivers/misc/Kconfig" ++ ++If that's done, you should be able to select the driver from the configuration ++programm and build the module. ++ +diff -Nurp linux-2.6.37/3rdparty/acerhk/Makefile linux-2.6.37.3rdparty/3rdparty/acerhk/Makefile +--- linux-2.6.37/3rdparty/acerhk/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/Makefile 2006-08-16 19:11:08.000000000 +0300 +@@ -0,0 +1,65 @@ ++# change KERNELSRC to the location of your kernel build tree only if ++# autodetection does not work ++#KERNELSRC=/usr/src/linux ++KERNELSRC?=/lib/modules/`uname -r`/build ++# Starting with 2.6.18, the kernel version is in utsrelease.h instead of version.h, accomodate both cases ++KERNELVERSION=$(shell awk -F\" '/REL/ {print $$2}' $(shell grep -s -l REL $(KERNELSRC)/include/linux/version.h $(KERNELSRC)/include/linux/utsrelease.h)) ++KERNELMAJOR=$(shell echo $(KERNELVERSION)|head -c3) ++ ++# next line is for kernel 2.6, if you integrate the driver in the kernel tree ++# /usr/src/linux/drivers/acerhk - or something similar ++# don't forget to add the following line to the parent dir's Makefile: ++# (/usr/src/linux/drivers/Makefile) ++# obj-m += acerhk/ ++CONFIG_ACERHK?=m ++obj-$(CONFIG_ACERHK) += acerhk.o ++ ++CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe ++INCLUDE=-I$(KERNELSRC)/include ++ ++ifeq ($(KERNELMAJOR), 2.6) ++TARGET := acerhk.ko ++else ++TARGET := acerhk.o ++endif ++ ++SOURCE := acerhk.c ++ ++all: $(TARGET) ++ ++help: ++ @echo Possible targets: ++ @echo -e all\\t- default target, builds kernel module ++ @echo -e install\\t- copies module binary to /lib/modules/$(KERNELVERSION)/extra/ ++ @echo -e clean\\t- removes all binaries and temporary files ++ ++# this target is only for me, don't use it yourself (Olaf) ++export: ++ sh export.sh ++ ++acerhk.ko: $(SOURCE) acerhk.h ++ $(MAKE) -C $(KERNELSRC) SUBDIRS=$(PWD) modules ++ ++acerhk.o: $(SOURCE) acerhk.h ++ $(CC) $(INCLUDE) $(CFLAGS) -DMODVERSIONS -DMODULE -D__KERNEL__ -o $(TARGET) $(SOURCE) ++ ++asm: $(SOURCE) ++ifeq ($(KERNELMAJOR), 2.6) ++ $(CC) $(INCLUDE) $(INCLUDE)/asm-i386/mach-default $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) ++else ++ $(CC) $(INCLUDE) $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) ++endif ++ ++clean: ++ rm -f *~ *.o *.s *.ko .acerhk* *.mod.c ++ ++load: $(TARGET) ++ insmod $(TARGET) ++ ++unload: ++ rmmod acerhk ++ ++install: $(TARGET) ++ mkdir -p /lib/modules/$(KERNELVERSION)/extra ++ cp -v $(TARGET) /lib/modules/$(KERNELVERSION)/extra/ ++ depmod -a +diff -Nurp linux-2.6.37/3rdparty/acerhk/NEWS linux-2.6.37.3rdparty/3rdparty/acerhk/NEWS +--- linux-2.6.37/3rdparty/acerhk/NEWS 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/NEWS 2003-02-28 02:47:18.000000000 +0200 +@@ -0,0 +1,16 @@ ++2003-02-28 Version 0.5.0 ++ completely changed key polling/translation ++ support for more series ++2002-07-06 Version 0.4.2 ++ added debug functionality ++ bugfix with 520/210 ++2002-06-24 Version 0.4.1 ++ added support for TM 210 series ++ removed nvram dependency ++2002-06-15 Version 0.4 ++ added model recognition ++ added kernel polling an key event generation ++ added support for 520 series ++ ++2002-04-29 Version 0.3 ++ Initial release +\ Ingen nyrad vid filslut +diff -Nurp linux-2.6.37/3rdparty/acerhk/README linux-2.6.37.3rdparty/3rdparty/acerhk/README +--- linux-2.6.37/3rdparty/acerhk/README 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/acerhk/README 2006-11-10 00:01:46.000000000 +0200 +@@ -0,0 +1,215 @@ ++ ++What is this driver good for? ++***************************** ++ ++This driver will give access to the special keys on notebooks of the ++Acer Travelmate series, which are not handled by the keyboard ++driver. ++It also works on notebooks from other manufacturers (some Medion, ++Fujitsu-Siemens, ...). ++ ++It also has some other related functionality (depending on the model): ++- controlling LEDs (Mail, Wireless) ++- enable/disable wireless hardware ++ ++Usage ++***** ++ ++The driver provides a device /dev/misc/acerhk where you can access all ++functions through IOCTL's, look into acerhk.h for their definitions. ++I use devfs and don't care about the minor number. You may want to change ++ACERHK_MINOR in acerhk.h. ++If you don't use devfs you need to create the device node. The device uses ++major 10 (misc character devices), the minor is chosen by the kernel (if ++ACERHK_MINOR equals MISC_DYNAMIC_MINOR) or by yourself (any other value). ++Use something like ++ mknod /dev/acerhk c 10 123 ++to create the device node. ++You can also use most functions through procfs. You will find some files in ++/proc/driver/acerhk: ++ led - write "on" or "off" to set the mail led state ++ key - read it to get a key press, only useful on non-dritek models ++ and only if polling is disabled ++ This file exists only on non-dritek models ++ info - some information about the driver, including the number of ++ pending keys ++ wirelessled - write "on" or "off" to set the led state and enable ++ wlan hardware ++ On some laptops you need to load the ipw2200 (others ++ too?) with option "led=1" to make the led work. ++ blueled - write "on" or "off" to set the led state and enable ++ bluetooth hardware ++ write a value n >= 50 to let the led blink every n jiffies ++ Do not use blinking mode where not only the led but also the ++ bluetooth hardware is controlled! ++ ++Probably the best way to use the driver is to let it poll for the keys itself ++and generate real key events. this is the normal case when loading the module ++without an additional parameter : ++ ++ modprobe acerhk ++ ++or you can use the ioctl as specified in acerhk.h to control the polling. If ++your kernel has keyboard input support you will get real key events when ++pressing the special keys. ++You can then use whatever software you like to make use of those keys, ++i.e. 'hotkeys' or Linux Easy Access Keyboard 'lineakd'. I prefer hotkeys, just ++in case anyone is interested, you can find a sample configuration for this ++programm in doc/acertm.def. ++Gnome >= 2.4 has built in support for multimedia keys (acme), that's ++what I use now. ++ ++Have a look at ++ http://mmkc.sourceforge.net/ ++ ++Note: If you have one of the newer models using dritek hardware you don't need ++polling (nor the file /proc/driver/acerhk/key) to use your buttons. On this ++hardware the driver only needs to send a special command to the keyboard ++controller to make them work. The actual key presses are then handled by the ++normal keyboard driver of the kernel. You can switch of polling by ++loading the module with poll=0. It saves you precious cpu time. ++ ++ ++Module parameters ++***************** ++name values meaning ++autowlan 0,1 disable(default)/enable automatic switching of wlan hardware ++ on wireless keypress, only useful for older (no ++ dritek-hardware) models, works only will poll=1 ++poll 0,1 disable/enable(default) kernel polling of key events ++verbose 0-4 verbosity level, see below ++usedritek 0,1 disable/enable(default) use of dritek hardware on newer ++ series, needed to activate the keys on such models ++force_series set this to the laptop series number you want the driver to ++ assume, skip autodetection (look at function ++ setup_model_features() for possible values) ++ ++Debugging ++********* ++ ++You can make the the driver be more verbose. To do this, add the following ++parameter when loading the module: ++ ++ verbose= ++ ++n is an integer, 0 (default) means no additional information while increasing ++values provide an increasing amount of information. Currently 3 is maximum, ++bigger values will have the same effect as 3. The existing verbosity levels ++will generate information about: ++ Level Information ++ 1 state changes and variable initialization ++ 2 model probing ++ 3 key translation, only known keys ++ 4 key translation, also unknown keys ++ 5 misc. information (idle values) ++ ++If you have rather serious problems you can activate debugging functionality ++in the driver. To do this, uncomment the '#define ACERDEBUG' in acerhk.c. You ++will find a new file in /proc/driver/acerhk, named 'debug'. You cannot read ++from this file, but you can write commands consisting of up to 4 digits. The ++first one specifies the action, while the latter can give additional ++parameters. Implemented are: ++ 'd' decrement module usage counter ++ 'i' increment module usage counter ++ ++ 'p' call function pbutton_fct() ++ 't' call function get_thermal_event() ++ 'w1x' call function wbutton_fct_1() with parameter x (0-9) ++ 'w2x' call function wbutton_fct_2() with parameter x (0-9) ++ ++ 'vx' change verbosity level to x (0-9) ++ ++ 'mxyy' set mapping of key name x to key event yy (hex) ++ 'sxx' send key event xx (hex) to input system ++ 'Sxx' simulate acer key press with code xx (hex) ++ ++ 'e0' stop kernel polling ++ 'e1' start kernel polling ++ ++Example: ++ ++1) echo d > /proc/driver/acerhk/debug ++ ++will decrement the usage counter, very useful if a program using the driver ++segfaulted. This way you can still unload the driver. ++ ++2) echo v4 > /proc/driver/acerhk/debug ++ ++will set the verbosity level to maximum. ++ ++Keycodes ++******** ++ ++see doc/keycodes ++Also see http://bernd-wurst.de/linux/tm800.php#mmkeys (only in german) ++ ++If you have one of the newer models with the dritek hardware, use kernel 2.6 ++and get (after enabling it) kernel messages of the form: ++ ++ atkbd.c: Unknown key pressed (translated set 2, code 0xf4 on ++ isa0060/serio0). ++ atkbd.c: Use 'setkeycodes e074 ' to make it known. ++ ++then you should do exactly what your told. In this case you could do ++ ++ setkeycodes e074 158 ++ ++to map the button with scancode e074 (hex) to keycode 158 (decimal). To find ++out the scancodes of the buttons either look into the kernel log or into the ++file MMKEYBD.CFG of the windows launch manager package. There you should find ++lines like this: ++ ++Key 1 = 1,E0,74,E0,F4,F500,P1 ++ ** ** ** ++ ++The important information is marked in the example above. The numbers give the ++scancode produced by the button which's name is given last. ++The keycode you give as parameter to setkeycodes is one out of the header file ++linux/input.h, in the example above the one for KEY_BACK. ++Important note: I received mails from a number of people where setkeycodes ++rejected keycodes above a certain value. This is caused by an outdated version ++of setkeycodes, use a more recent one. ++To ease the setup of keymappings for the newer series I will try to include ++setup scripts for the different notebook series. If I have enough time to ++spare I will perhaps expand the driver itself do to that. ++ ++How does it work? ++***************** ++ ++The driver is based on the windows Me for series 610 driver and ++resembles its structure and functionality. ++Key presses are events, which are stored in a FIFO queue with 31 ++entries. You can access the event count via CMOS nvram, but the access to ++the actual queue (and other functionality like switching the mail led) is ++done through calling a system ROM function. ++Upon loading the driver looks for this function, if it cannot find it, ++loading aborts. ++Newer Dritek Hardware: ++Button handling is done entirely by the EC (embbedded/environment ++controller) which behaves like an extended keyboard controller. My ++driver only enables/disables this extension. ++ ++ ++Credits ++******* ++ ++Leif Jensen, whose driver inspired me to do the probing stuff ++http://www.math.columbia.edu/~jensen/linux/acertm/ ++ ++Thanks to all who contributed patches to this driver or who just tried it out ++on their laptops, without them it wouldn't support anything else but my ++TM 613. ++ ++Contact ++******* ++ ++If you have problems with the driver, please include the following information: ++1. name of your laptop (e.g. Acer TM 4001) ++2. content of /proc/driver/acerhk/info, if available ++3. kernel output after loading the module with verbose=2 ++ ++Email: Olaf Tauber ++ ++The latest version can be found here: ++ ++http://www.informatik.hu-berlin.de/~tauber/acerhk diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch new file mode 100644 index 00000000..47a71053 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch @@ -0,0 +1,20 @@ +--- linux-2.6.35/3rdparty/acerhk/acerhk.c.orig 2010-10-03 12:50:33.000000000 +0000 ++++ linux-2.6.35/3rdparty/acerhk/acerhk.c 2010-10-03 13:13:22.093868410 +0000 +@@ -2730,7 +2730,7 @@ static void acerhk_proc_cleanup(void) + + /* {{{ file operations */ + +-static int acerhk_ioctl( struct inode *inode, struct file *file, ++static long acerhk_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg ) + { + int retval; +@@ -2827,7 +2827,7 @@ static int acerhk_release( struct inode + + static struct file_operations acerhk_fops = { + owner: THIS_MODULE, +- ioctl: acerhk_ioctl, ++ unlocked_ioctl: acerhk_ioctl, + open: acerhk_open, + #ifdef ACERDEBUG + write: acerhk_write, diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch new file mode 100644 index 00000000..7971c699 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch @@ -0,0 +1,33 @@ +CFLAGS use isn't allowed anymore with 2.6.24, we must use EXTRA_CFLAGS +in its place + +Signed-off-by: Herton Ronaldo Krzesinski + +--- linux-2.6.23/3rdparty/acerhk/Makefile.orig 2007-11-08 21:32:43.000000000 -0200 ++++ linux-2.6.23/3rdparty/acerhk/Makefile 2007-11-08 21:33:28.000000000 -0200 +@@ -14,7 +14,7 @@ + CONFIG_ACERHK?=m + obj-$(CONFIG_ACERHK) += acerhk.o + +-CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe ++EXTRA_CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe + INCLUDE=-I$(KERNELSRC)/include + + ifeq ($(KERNELMAJOR), 2.6) +@@ -41,13 +41,13 @@ acerhk.ko: $(SOURCE) acerhk.h + $(MAKE) -C $(KERNELSRC) SUBDIRS=$(PWD) modules + + acerhk.o: $(SOURCE) acerhk.h +- $(CC) $(INCLUDE) $(CFLAGS) -DMODVERSIONS -DMODULE -D__KERNEL__ -o $(TARGET) $(SOURCE) ++ $(CC) $(INCLUDE) $(EXTRA_CFLAGS) -DMODVERSIONS -DMODULE -D__KERNEL__ -o $(TARGET) $(SOURCE) + + asm: $(SOURCE) + ifeq ($(KERNELMAJOR), 2.6) +- $(CC) $(INCLUDE) $(INCLUDE)/asm-i386/mach-default $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) ++ $(CC) $(INCLUDE) $(INCLUDE)/asm-i386/mach-default $(EXTRA_CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) + else +- $(CC) $(INCLUDE) $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) ++ $(CC) $(INCLUDE) $(EXTRA_CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) + endif + + clean: diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch new file mode 100644 index 00000000..c8d000b5 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch @@ -0,0 +1,22 @@ +Fix acerhk build with FUNCTION_TRACER enabled. + +Signed-off-by: Herton Ronaldo Krzesinski +--- + 3rdparty/acerhk/Makefile | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff -p -up linux-2.6.31/3rdparty/acerhk/Makefile.orig linux-2.6.31/3rdparty/acerhk/Makefile +--- linux-2.6.31/3rdparty/acerhk/Makefile.orig 2009-10-02 16:39:06.000000000 -0300 ++++ linux-2.6.31/3rdparty/acerhk/Makefile 2009-10-02 16:45:13.000000000 -0300 +@@ -14,7 +14,10 @@ + CONFIG_ACERHK?=m + obj-$(CONFIG_ACERHK) += acerhk.o + +-EXTRA_CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe ++CFLAGS_acerhk.o += -c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe ++ifdef CONFIG_FUNCTION_TRACER ++CFLAGS_REMOVE_acerhk.o = -pg ++endif + INCLUDE=-I$(KERNELSRC)/include + + ifeq ($(KERNELMAJOR), 2.6) diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch new file mode 100644 index 00000000..cba26984 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch @@ -0,0 +1,23 @@ + +AUTOCONF_INCLUDED is not defined anymore in 2.6.38, so old include got exposed. + +Fix by removing the offending bits. + +Signed-off-by: Thomas Backlund + + 3rdparty/acerhk/acerhk.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- linux-2.6.38/3rdparty/acerhk/acerhk.c.orig 2011-03-20 13:57:32.000000000 +0200 ++++ linux-2.6.38/3rdparty/acerhk/acerhk.c 2011-03-20 15:00:45.768659568 +0200 +@@ -35,10 +35,6 @@ + * + */ + +-#ifndef AUTOCONF_INCLUDED +-#include +-#endif +- + /* This driver is heavily dependent on the architecture, don't let + * anyone without an X86 machine use it. On laptops with AMD64 + * architecture this driver is only useable in 32 bit mode. diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch new file mode 100644 index 00000000..f0c419ad --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch @@ -0,0 +1,37 @@ +Fix Makefile and add Kconfig for the acerhk driver +--- + 3rdparty/acerhk/Kconfig | 8 8 + 0 - 0 ! + 3rdparty/acerhk/Makefile | 6 3 + 3 - 0 ! + 2 files changed, 11 insertions(+), 3 deletions(-) + +Index: linux-2.6.21/3rdparty/acerhk/Makefile +=================================================================== +--- linux-2.6.21.orig/3rdparty/acerhk/Makefile 2007-05-15 15:34:48.000000000 +0200 ++++ linux-2.6.21/3rdparty/acerhk/Makefile 2007-05-15 15:37:17.000000000 +0200 +@@ -1,10 +1,10 @@ + # change KERNELSRC to the location of your kernel build tree only if + # autodetection does not work + #KERNELSRC=/usr/src/linux +-KERNELSRC?=/lib/modules/`uname -r`/build ++#KERNELSRC?=/lib/modules/`uname -r`/build + # Starting with 2.6.18, the kernel version is in utsrelease.h instead of version.h, accomodate both cases +-KERNELVERSION=$(shell awk -F\" '/REL/ {print $$2}' $(shell grep -s -l REL $(KERNELSRC)/include/linux/version.h $(KERNELSRC)/include/linux/utsrelease.h)) +-KERNELMAJOR=$(shell echo $(KERNELVERSION)|head -c3) ++#KERNELVERSION=$(shell awk -F\" '/REL/ {print $$2}' $(shell grep -s -l REL $(KERNELSRC)/include/linux/version.h $(KERNELSRC)/include/linux/utsrelease.h)) ++#KERNELMAJOR=$(shell echo $(KERNELVERSION)|head -c3) + + # next line is for kernel 2.6, if you integrate the driver in the kernel tree + # /usr/src/linux/drivers/acerhk - or something similar +Index: linux-2.6.21/3rdparty/acerhk/Kconfig +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.21/3rdparty/acerhk/Kconfig 2007-05-15 15:39:30.000000000 +0200 +@@ -0,0 +1,8 @@ ++config ACERHK ++ tristate "Acerhk driver" ++ depends on EXPERIMENTAL && X86 ++ ---help--- ++ This is an experimental acer keyboard driver for ++ acer laptops. If you have a notebook with a ipw2X00 ++ wireless card, it allows you to turn off the rf_kill ++ diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch new file mode 100644 index 00000000..e5821d46 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch @@ -0,0 +1,67 @@ +Updated for owner removal from struct proc_dir_entry (2.6.30) + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + 3rdparty/acerhk/acerhk.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff -p -up linux-2.6.29/3rdparty/acerhk/acerhk.c.orig linux-2.6.29/3rdparty/acerhk/acerhk.c +--- linux-2.6.29/3rdparty/acerhk/acerhk.c.orig 2009-05-22 02:42:44.000000000 -0300 ++++ linux-2.6.29/3rdparty/acerhk/acerhk.c 2009-05-22 02:43:45.000000000 -0300 +@@ -2626,7 +2626,6 @@ static int acerhk_proc_init(void) + printk(KERN_INFO"acerhk: could not create /proc/driver/acerhk\n"); + } + else { +- proc_acer_dir->owner = THIS_MODULE; + /* now create several files, first general info ... */ + entry = create_proc_read_entry("info", + 0444, proc_acer_dir, acerhk_proc_info, NULL); +@@ -2635,7 +2634,6 @@ static int acerhk_proc_init(void) + remove_proc_entry("driver/acerhk", NULL); + retval = 0; + } else { +- entry->owner = THIS_MODULE; + /* ... last pressed key ... */ + entry = create_proc_read_entry("key", + 0444, proc_acer_dir, acerhk_proc_key, NULL); +@@ -2645,7 +2643,6 @@ static int acerhk_proc_init(void) + remove_proc_entry("driver/acerhk", NULL); + retval = 0; + } else { +- entry->owner = THIS_MODULE; + /* ... and led control file */ + entry = create_proc_entry("led", 0222, proc_acer_dir); + if (entry == NULL) { +@@ -2657,7 +2654,6 @@ static int acerhk_proc_init(void) + } + else { + entry->write_proc = acerhk_proc_led; +- entry->owner = THIS_MODULE; + /* ... and wireless led controll file */ + entry = create_proc_entry("wirelessled", 0222, proc_acer_dir); + if (entry == NULL) { +@@ -2670,7 +2666,6 @@ static int acerhk_proc_init(void) + } + else { + entry->write_proc = acerhk_proc_wirelessled; +- entry->owner = THIS_MODULE; + /* ... and bluetooth led controll file */ + entry = create_proc_entry("blueled", 0222, proc_acer_dir); + if (entry == NULL) { +@@ -2683,7 +2678,6 @@ static int acerhk_proc_init(void) + retval = 0; + } else { + entry->write_proc = acerhk_proc_blueled; +- entry->owner = THIS_MODULE; + retval = 1; + #ifdef ACERDEBUG + /* add extra file for debugging purposes */ +@@ -2700,7 +2694,6 @@ static int acerhk_proc_init(void) + } + else { + entry->write_proc = acerhk_proc_debug; +- entry->owner = THIS_MODULE; + retval = 1; + } + #endif diff --git a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch new file mode 100644 index 00000000..1dcf8fa0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch @@ -0,0 +1,47 @@ +Fix Makefile and add Kconfig for the aes2501 driver. + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + linux-2.6.23/3rdparty/aes2501/Kconfig | 10 ++++++++++ + linux-2.6.23/3rdparty/aes2501/Makefile | 19 +------------------ + 2 files changed, 11 insertions(+), 18 deletions(-) + +diff -p -up linux-2.6.23/3rdparty/aes2501/Kconfig.orig linux-2.6.23/3rdparty/aes2501/Kconfig +--- linux-2.6.23/3rdparty/aes2501/Kconfig.orig 2008-01-13 23:14:47.000000000 -0200 ++++ linux-2.6.23/3rdparty/aes2501/Kconfig 2008-01-13 23:35:21.000000000 -0200 +@@ -0,0 +1,10 @@ ++config AES2501 ++ tristate "AuthenTec AES2501 Fingerprint Sensor Driver" ++ depends on USB ++ default m ++ ---help--- ++ Say Y here if you have a AuthenTec AES2501 Fingerprint ++ Sensor device. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called aes2501. +diff -p -up linux-2.6.23/3rdparty/aes2501/Makefile.orig linux-2.6.23/3rdparty/aes2501/Makefile +--- linux-2.6.23/3rdparty/aes2501/Makefile.orig 2008-01-13 23:14:36.000000000 -0200 ++++ linux-2.6.23/3rdparty/aes2501/Makefile 2008-01-13 23:21:33.000000000 -0200 +@@ -1,19 +1,2 @@ +- +-obj-m := aes2501.o +- +-KERNELDIR ?= /lib/modules/$(shell uname -r)/build +- +-PWD := $(shell pwd) +- +-all: usertest +- $(MAKE) -C $(KERNELDIR) M=$(PWD) +- +-usertest: +- gcc -I. -o usertest usertest.c +- +-clean: +- rm -f *.o *~ aes2501.ko aes2501.mod.c Module.symvers usertest +- rm -rf .tmp_versions +- rm -f .*.cmd +- ++obj-$(CONFIG_AES2501) := aes2501.o + diff --git a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch new file mode 100644 index 00000000..a5f95e79 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch @@ -0,0 +1,2356 @@ + 3rdparty/aes2501/ChangeLog | 95 ++ + 3rdparty/aes2501/Makefile | 19 + 3rdparty/aes2501/aes2501.c | 1629 ++++++++++++++++++++++++++++++++++++++++ + 3rdparty/aes2501/aes2501.h | 40 + 3rdparty/aes2501/aes2501_regs.h | 464 +++++++++++ + 3rdparty/aes2501/usertest.c | 78 + + 6 files changed, 2325 insertions(+) +diff -Nurp linux-2.6.37/3rdparty/aes2501/aes2501.c linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.c +--- linux-2.6.37/3rdparty/aes2501/aes2501.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.c 2008-01-14 03:07:30.000000000 +0200 +@@ -0,0 +1,1629 @@ ++ ++/* ++ * aes2501.c -- AuthenTec AES2501 Fingerprint Sensor Driver for Linux ++ * ++ * Maintainer: Cyrille Bagard ++ * ++ * Copyright (C) 2007 Cyrille Bagard ++ * ++ * This file is part of the AES2501 driver. ++ * ++ * the AES2501 driver is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * the AES2501 driver 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 the AES2501 driver; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include "aes2501.h" ++#include "aes2501_regs.h" ++ ++ ++static int major = 0; ++ ++module_param(major, int, 0); ++MODULE_PARM_DESC(major, "major number"); ++ ++ ++//#define DEBUG ++ ++ ++#define PRINTK_INFO(fmt, arg...) printk(KERN_INFO "aes2501: " fmt, ## arg) ++ ++#define PRINTK_CRIT(fmt, arg...) printk(KERN_CRIT "aes2501: " fmt, ## arg) ++ ++#define PRINTK_DBG(fmt, arg...) printk(KERN_DEBUG "aes2501: " fmt, ## arg) ++ ++ ++static struct workqueue_struct *comm_queue; ++ ++ ++static char *finger_print = NULL; ++static size_t finger_print_len; ++ ++ ++ ++static DECLARE_WAIT_QUEUE_HEAD(wq); ++static int flag = 0; ++ ++ ++ ++struct usb_aes2501 *_dev; ++ ++ ++ ++ ++ ++ ++struct aes2501 { ++ ++ struct usb_device *udev; ++ ++ int temp; ++ ++ unsigned char *int_in_buffer; ++ struct urb *int_in_urb; ++ ++}; ++ ++ ++/* ++#define VENDOR_ID 0x08f7 ++#define PRODUCT_ID 0x0002 ++*/ ++ ++#define VENDOR_ID 0x08ff ++#define PRODUCT_ID 0x2580 ++ ++ ++/* Table of devices that work with this driver */ ++ ++static struct usb_device_id id_table [] = { ++ { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(usb, id_table); ++ ++ ++/* Get a minor range for your devices from the usb maintainer */ ++#define USB_AES2501_MINOR_BASE 192 ++ ++ ++ ++#define WRITES_IN_FLIGHT 8 /* ??? */ ++ ++ ++ ++enum AES2501_Status { ++ ++ AESS_INIT_DONE, ++ AESS_IS_SCANNING, ++ ++ AESS_COUNT ++ ++}; ++ ++ ++#define BULK_OUT_BUFFER_SIZE 32 ++ ++ ++/* Read a register value from a dump sent by the device. */ ++static int read_register_value(const uint8_t *, Aes2501Registers); ++ ++/* Tells if a finger motion is still detected. */ ++static int sum_histogram_values(const uint8_t *, uint8_t); ++ ++ ++/* Structure to hold all device specific stuff */ ++struct usb_aes2501 { ++ ++ struct usb_device *udev; /* the usb device for this device */ ++ struct usb_interface *interface; /* the interface for this device */ ++ struct semaphore limit_sem; /* limiting the number of writes in progress */ ++ unsigned char *bulk_in_buffer; /* the buffer to receive data */ ++ size_t bulk_in_size; /* the size of the receive buffer */ ++ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ ++ uint8_t bulk_out_buffer[BULK_OUT_BUFFER_SIZE]; /* the buffer to send data (32 / 8 = 4) */ ++ size_t bulk_out_buffer_used; /* the quantity of buffered data to send */ ++ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ ++ struct kref kref; ++ struct mutex io_mutex; /* synchronize I/O with disconnect */ ++ ++ DECLARE_BITMAP(status, AESS_COUNT); /* Information about what has been set / is running */ ++ ++ struct work_struct scan_work; ++ uint8_t stop_scan; /* Flag to stop finger detecting during module removal */ ++}; ++ ++ ++#define to_aes2501_dev(r) container_of(r, struct usb_aes2501, kref) ++ ++static struct usb_driver aes2501_driver; ++ ++static void aes2501_delete(struct kref *kref) ++{ ++ struct usb_aes2501 *dev; ++ ++ dev = to_aes2501_dev(kref); ++ ++ usb_put_dev(dev->udev); ++ kfree(dev->bulk_in_buffer); ++ kfree(dev); ++ ++} ++ ++ ++static int aes2501_open(struct inode *inode, struct file *file); ++/* ++struct usb_aes2501 *_dev; ++static int aes2501_open(struct inode *inode, struct file *file) ++{ ++ printk(KERN_INFO "Plop !\n"); ++ ++ ++ ++ return 0; ++} ++*/ ++ ++static ssize_t aes2501_read(struct file *file, char *buffer, size_t count, loff_t *ppos) ++{ ++ int read_len; ++ ++ //printk(KERN_INFO "aes2501_read ! got %d -> need (%d ; %d)\n", finger_print_len, *ppos, count); ++ ++ if (flag == 0) ++ printk(KERN_INFO "Process %i (%s) is going to sleep\n", current->pid, current->comm); ++ ++ wait_event_interruptible(wq, flag == 1); ++ ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ ++ if (finger_print_len == 0) ++ return -ENODATA; ++ ++ if (unlikely((*ppos < 0) || (loff_t)(*ppos + count) < 0)) ++ return -EINVAL; ++ ++ if (*ppos + count > finger_print_len) ++ read_len = finger_print_len - *ppos; ++ else read_len = count; ++ ++ if (read_len > 0) ++ { ++ if (!access_ok(VERIFY_WRITE, buffer, read_len)) ++ return -EFAULT; ++ ++ read_len -= copy_to_user(buffer, finger_print + *ppos, read_len); ++ *ppos += read_len; ++ ++ } ++ ++ /* !!!! */ ++ else flag = 0; ++ ++ return read_len; ++ ++} ++ ++ ++ ++static int detect_finger_on_aes2501(struct usb_aes2501 *dev); ++ ++static void do_scanning(struct work_struct *work); ++ ++ ++// ioctl - I/O control ++static int aes2501_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct usb_aes2501 *dev; ++ int retval; ++ ++ dev = _dev; ++ ++ ++ printk(KERN_INFO "Get IOCtl :: cmd = %d vs %d\n", cmd, (AES2501_IOC_TEST)); ++ ++ switch (cmd) { ++ ++ case AES2501_IOC_TEST: ++ ++#if defined(_arch_um__) || !(LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) ++ INIT_WORK(&dev->scan_work, do_scanning); ++#else ++ INIT_WORK(&dev->scan_work, do_scanning, NULL); ++#endif ++ ++ retval = queue_work(comm_queue, &dev->scan_work); ++ ++ ++ } ++ ++ ++ ++ return 0; ++ ++ ++#if 0 ++ int retval = 0; ++ switch ( cmd ) { ++ case CASE1:/* for writing data to arg */ ++ if (copy_from_user(&data, (int *)arg, sizeof(int))) ++ return -EFAULT; ++ break; ++ case CASE2:/* for reading data from arg */ ++ if (copy_to_user((int *)arg, &data, sizeof(int))) ++ return -EFAULT; ++ break; ++ default: ++ retval = -EINVAL; ++ } ++ return retval; ++#endif ++} ++ ++ ++ ++static const struct file_operations aes2501_fops = { ++ .owner = THIS_MODULE, ++ .read = aes2501_read,/* ++ .write = aes2501_write,*/ ++ .open = aes2501_open,/* ++ .release = skel_release,*/ ++ .ioctl = aes2501_ioctl ++}; ++ ++ ++ ++/* ++static int send_data_to_aes2501(struct usb_aes2501 *dev, unsigned char *buffer, int len) ++{ ++#ifdef DEBUG ++ int i; ++#endif ++ int bytes_written; ++ int ret; ++ ++#ifdef DEBUG ++ printk(KERN_INFO "\n:: OUT :: len=%d\n", len); ++ ++ for (i = 0; i < len; i++) ++ { ++ if (i > 0 && i % 16 == 0) ++ printk("\n"); ++ if (i % 16 == 0) ++ printk(KERN_INFO " %05x: ", i); ++ ++ if (i % 8 == 0) ++ printk(" "); ++ ++ printk("%02x ", buffer[i]); ++ ++ } ++ ++ printk("\n"); ++#endif ++ ++ ret = usb_bulk_msg(dev->udev, ++ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), ++ buffer, ++ len, ++ &bytes_written, 4000); ++ ++ if (ret < 0) ++ PRINTK_CRIT("error while sending data: %d\n", ret); ++ ++ if (bytes_written != len) ++ printk(KERN_INFO "did not write all ! %d vs %d\n", bytes_written, len); ++ ++ return ret; ++ ++} ++*/ ++ ++static int flush_aes2501_bulk_out(struct usb_aes2501 *dev) ++{ ++ int ret; ++ int bytes_written; ++ ++ ret = 0; ++ ++ if (dev->bulk_out_buffer_used > 0) { ++ ++ ret = usb_bulk_msg(dev->udev, ++ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), ++ dev->bulk_out_buffer, ++ dev->bulk_out_buffer_used, ++ &bytes_written, 4000); ++ ++ if (ret < 0) ++ PRINTK_CRIT("error while sending data: %d\n", ret); ++ ++ else if (bytes_written != dev->bulk_out_buffer_used) ++ { ++ printk(KERN_INFO "did not write all ! %d vs %d\n", bytes_written, dev->bulk_out_buffer_used); ++ /*ret = ... */ ++ } ++ ++ dev->bulk_out_buffer_used = 0; ++ ++ } ++ ++ return ret; ++ ++} ++ ++ ++static int write_aes2501_register(struct usb_aes2501 *dev, uint8_t reg, uint8_t data) ++{ ++ int ret; ++ ++ if (dev->bulk_out_buffer_used == BULK_OUT_BUFFER_SIZE) ++ ret = flush_aes2501_bulk_out(dev); ++ else ++ ret = 0; ++ ++ dev->bulk_out_buffer[dev->bulk_out_buffer_used++] = reg; ++ dev->bulk_out_buffer[dev->bulk_out_buffer_used++] = data; ++ ++ return ret; ++ ++} ++ ++ ++static int recv_data_to_aes2501(struct usb_aes2501 *dev, unsigned char *buffer, int len) ++{ ++ int bytes_read; ++ int ret; ++#ifdef DEBUG ++ int i; ++#endif ++ ++ ret = usb_bulk_msg(dev->udev, ++ usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), ++ buffer, ++ len, ++ &bytes_read, 4000); ++ ++ if (ret < 0) ++ PRINTK_CRIT("error while sending data: %d\n", ret); ++ ++ if (bytes_read != len) ++ printk(KERN_INFO "did not read all ! %d vs %d\n", bytes_read, len); ++ ++#ifdef DEBUG ++ if (ret == 0) ++ { ++ printk(KERN_INFO "\n:: IN :: len=%d\n", len); ++ ++ for (i = 0; i < len; i++) ++ { ++ if (i > 0 && i % 16 == 0) ++ printk("\n"); ++ if (i % 16 == 0) ++ printk(KERN_INFO " %05x: ", i); ++ ++ if (i % 8 == 0) ++ printk(" "); ++ ++ printk("%02x ", buffer[i]); ++ ++ } ++ ++ printk("\n"); ++ ++ } ++#endif ++ ++ return ret; ++ ++} ++ ++ ++ ++static int standby_aes2501(struct usb_aes2501 *dev) ++{ ++ ++ //unsigned char cmd[] = "\xac\x01\xad\x1a\x81\x02"; ++ ++ ++ unsigned char buffer[4]; ++ ++ ++ ++ ++ printk(KERN_INFO ">> Standby aes2501 !\n"); ++ ++ ++ //msleep(800); ++ ++ write_aes2501_register(dev, AES2501_REG_TREGC, AES2501_TREGC_ENABLE); ++ write_aes2501_register(dev, AES2501_REG_TREGD, 0x1a); ++ ++ flush_aes2501_bulk_out(dev); ++ ++ /** ++ * TODO: ++ * - lire ici les deux octets. ++ * - voir pour inverser les commandes. ++ */ ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL2, 0x02/* AES2501_CTRL2_READ_REGS ??? */); ++ flush_aes2501_bulk_out(dev); ++ ++ //send_data_to_aes2501(dev, cmd, 6); ++ recv_data_to_aes2501(dev, buffer, 2); ++ ++ ++ ++ printk(KERN_INFO " %02x %02x\n", buffer[0], buffer[1]); ++ ++ ++ if ((buffer[1] & AES2501_STAT_SCAN) == buffer[1]) ++ { ++ PRINTK_DBG("scan state: "); ++ ++ switch (buffer[1]) ++ { ++ case STATE_WAITING_FOR_FINGER: ++ printk("'Waiting for finger'\n"); ++ break; ++ case STATE_FINGER_SETTLING_DELAY: ++ printk("'In Finger settling delay'\n"); ++ break; ++ case STATE_POWER_UP_DELAY: ++ printk("'In power up delay'\n"); ++ break; ++ case STATE_WAITING_TO_START_SCAN: ++ printk("'Waiting to start image scan'\n"); ++ break; ++ case STATE_PRELOADING_SUBARRAY_0: ++ printk("'Pre-loading subarray 0'\n"); ++ break; ++ case STATE_SETUP_FOR_ROW_ADVANCE: ++ printk("'Setup for row advance'\n"); ++ break; ++ case STATE_WAITING_FOR_ROW_ADVANCE: ++ printk("'Waiting for row advance'\n"); ++ break; ++ case STATE_PRELOADING_COL_0: ++ printk("'Pre-loading column 0'\n"); ++ break; ++ case STATE_SETUP_FOR_COL_ADVANCE: ++ printk("'Setup for column advance'\n"); ++ break; ++ case STATE_WAITING_FOR_COL_ADVANCE: ++ printk("'Waiting for column advance'\n"); ++ break; ++ case STATE_WAITING_FOR_SCAN_START: ++ printk("'Waiting for scan start'\n"); ++ break; ++ case STATE_WAITING_FOR_SCAN_END: ++ printk("'Waiting for scan end'\n"); ++ break; ++ case STATE_WAITING_FOR_ROW_SETUP: ++ printk("'Waiting for row setup'\n"); ++ break; ++ case STATE_WAITING_FOR_COL_TIME: ++ printk("'Waiting for one column time (depends on scan rate)'\n"); ++ break; ++ case STATE_WAITING_FOR_QUEUED_DATA: ++ printk("'Waiting for queued data transmission to be completed'\n"); ++ break; ++ case STATE_WAIT_FOR_128_US: ++ printk("'Wait for 128 us'\n"); ++ break; ++ default: ++ printk("none (?!)\n"); ++ break; ++ ++ } ++ ++ } ++ ++ if (buffer[1] & AES2501_STAT_ERROR) ++ PRINTK_DBG("got serial interface framing error\n"); ++ ++ if (buffer[1] & AES2501_STAT_PAUSED) ++ PRINTK_DBG("scan was paused due to input buffer full\n"); ++ ++ if (buffer[1] & AES2501_STAT_RESET) ++ PRINTK_DBG("master reset input has been asserted\n"); ++ ++ ++ ++ ++ return 0; ++ ++ ++} ++ ++ ++ ++ ++static int setup_aes2501(struct usb_aes2501 *dev) ++{ ++ //unsigned char patch_msg[] = "\x80\x01"; ++ ++ ++ //unsigned char cmd[] = "\x80\x01\x81\x02"; ++ ++ //unsigned char cmd_00[] = "\xb0\x27"; ++ //unsigned char cmd_01[] = "\x80\x01\x82\x40"; ++ //unsigned char cmd_02[] = "\xff\x00"; ++ //unsigned char cmd_03[] = "\x80\x01\x82\x40\x83\x00\x88\x02\x89\x10\x8a\x05\x8c\x00\x8e\x13\x91\x44\x92\x34\x95\x16\x96\x16\x97\x18\xa1\x70\xa2\x02\xa7\x00\xac\x01\xad\x1a\x80\x04\x81\x04\xb4\x00";/*42*/ ++ //unsigned char cmd_04[] = "\x80\x01\x82\x40"; ++ ++ //unsigned char cmd2[] = "\x80\x01\xa8\x41\x82\x42\x83\x53\x80\x04\x81\x02";/*12*/ ++ ++ //unsigned char cmd3_00[] = "\xff\x00"; ++ //unsigned char cmd3_01[] = "\x80\x01\xa8\x41\x82\x42\x83\x53\x80\x04\x81\x02";/*12*/ ++ ++ //unsigned char cmd4[] = "\x80\x01\x82\x40\xb0\x27\x94\x0a\x80\x04\x83\x45\xa8\x41";/*14*/ ++ ++ //unsigned char cmd5_00[] = "\xb0\x27"; ++ //unsigned char cmd5_01[] = "\x80\x01\x82\x40"; ++ //unsigned char cmd5_02[] = "\xff\x00"; ++ //unsigned char cmd5_03[] = "\x80\x02"; ++ //unsigned char cmd5_04[] = "\x81\x02"; ++ ++ ++ int i; ++ ++ unsigned char buffer[128]; ++ ++ ++ /* To be sure the device will respond */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ //send_data_to_aes2501(dev, patch_msg, 2); ++ flush_aes2501_bulk_out(dev);/* ... */ ++ ++ printk(KERN_INFO ">> Setting up aes2501 !\n"); ++ ++ /* Part 1 */ ++ ++ printk(KERN_INFO " -- part 1 --\n"); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); ++ flush_aes2501_bulk_out(dev); ++ ++ //send_data_to_aes2501(dev, cmd, 4); ++ recv_data_to_aes2501(dev, buffer, 126); ++ ++ ++ ++ //send_data_to_aes2501(dev, cmd_00, 2); ++ ++ write_aes2501_register(dev, 0xb0, 0x27);/* Reserved */ ++ ++ ++ ++ //send_data_to_aes2501(dev, cmd_01, 4); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ ++ for (i = 0; i <= 10; i++) ++ { ++ //msleep(200); ++ //send_data_to_aes2501(dev, cmd_02, 2); ++ write_aes2501_register(dev, 0xff, 0x00);/* Reserved */ ++ } ++ ++ //msleep(100); ++ ++ ++ //send_data_to_aes2501(dev, cmd_03, 42); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_DETCTRL, AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS); ++ write_aes2501_register(dev, AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US); ++ write_aes2501_register(dev, AES2501_REG_MEASDRV, AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE); ++ write_aes2501_register(dev, AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M); ++ write_aes2501_register(dev, AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE);/* Default */ ++ write_aes2501_register(dev, AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE); ++ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X); ++ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x44); ++ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x34); ++ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x16); ++ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x16); ++ write_aes2501_register(dev, AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_TREG1, 0x70);/* Reserved + 0xXX */ ++ write_aes2501_register(dev, 0xa2, 0x02);/* Reserved */ ++ write_aes2501_register(dev, 0xa7, 0x00);/* Reserved */ ++ write_aes2501_register(dev, AES2501_REG_TREGC, AES2501_TREGC_ENABLE); ++ write_aes2501_register(dev, AES2501_REG_TREGD, 0x1a); ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); ++ write_aes2501_register(dev, AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE); ++ ++ ++ flush_aes2501_bulk_out(dev); ++ ++ recv_data_to_aes2501(dev, buffer, 20); ++ ++ ++ ++ //send_data_to_aes2501(dev, cmd_04, 4); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ ++ /* Part 2 */ ++ ++ printk(KERN_INFO " -- part 2 --\n"); ++ ++ //msleep(100); ++ ++ //send_data_to_aes2501(dev, cmd2, 12); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_AUTOCALOFFSET, 0x41); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x42);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_DETCTRL, 0x53);/* Cumul ??? */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); ++ ++ ++ ++ ++ flush_aes2501_bulk_out(dev); ++ ++ ++ recv_data_to_aes2501(dev, buffer, 126); ++ ++ /* Part 3 */ ++ ++ printk(KERN_INFO " -- part 3 --\n"); ++ ++ i = 0; ++ ++ printk(KERN_INFO " reg 0xaf = 0x%x\n", buffer[0x5f]); ++ while (buffer[0x5f] == 0x6b) ++ { ++ //msleep(200); ++ ++ ++ //send_data_to_aes2501(dev, cmd3_00, 2); ++ write_aes2501_register(dev, 0xff, 0x00);/* Reserved */ ++ ++ //msleep(80); ++ ++ //send_data_to_aes2501(dev, cmd3_01, 12); ++ ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_AUTOCALOFFSET, 0x41); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x42);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_DETCTRL, 0x53);/* Cumul ??? */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); ++ ++ flush_aes2501_bulk_out(dev); ++ ++ ++ ++ ++ recv_data_to_aes2501(dev, buffer, 126); ++ ++ printk(KERN_INFO " +reg 0xaf = 0x%x\n", buffer[0x5f]); ++ ++ if (++i == 13) break; ++ ++ } ++ ++ ++ ++ ++ ++ /* Part 4 */ ++ ++ printk(KERN_INFO " -- part 4 --\n"); ++ ++ //send_data_to_aes2501(dev, cmd4, 14); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ write_aes2501_register(dev, 0xb0, 0x27);/* Reserved */ ++ write_aes2501_register(dev, AES2501_REG_ENDROW, 0x0a); ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); ++ write_aes2501_register(dev, AES2501_REG_DETCTRL, 0x45);/* ???? */ ++ write_aes2501_register(dev, AES2501_REG_AUTOCALOFFSET, 0x41); ++ ++ ++ ++ ++ ++ ++ /* ... */ ++ ++ ++/* ++ for (i = 0; i < 126; i += 10) ++ { ++ printk(KERN_INFO " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", ++ buffer[i + 0], buffer[i + 1], buffer[i + 2], buffer[i + 3], buffer[i + 4], ++ buffer[i + 5], buffer[i + 6], buffer[i + 7], buffer[i + 8], buffer[i + 9]); ++ ++ if (i % 20 == 0) ++ printk(KERN_INFO "\n "); ++ ++ } ++ ++ printk(KERN_INFO "\n"); ++*/ ++ ++ ++ ++ /* Part 5 */ ++ ++ printk(KERN_INFO " -- part 5 --\n"); ++ ++ //send_data_to_aes2501(dev, cmd5_00, 2); ++ ++ write_aes2501_register(dev, 0xb0, 0x27);/* Reserved */ ++ ++ //send_data_to_aes2501(dev, cmd5_01, 4); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ ++ //msleep(200); ++ ++ ++ //send_data_to_aes2501(dev, cmd5_02, 2); ++ //send_data_to_aes2501(dev, cmd5_01, 4); ++ //send_data_to_aes2501(dev, cmd5_01, 4); ++ ++ write_aes2501_register(dev, 0xff, 0x00);/* Reserved */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ ++ ++ ++ //msleep(50); ++ ++ //send_data_to_aes2501(dev, cmd5_01, 4); ++ //send_data_to_aes2501(dev, cmd5_01, 4); ++ //send_data_to_aes2501(dev, cmd5_01, 4); ++ //send_data_to_aes2501(dev, cmd5_03, 2); ++ //send_data_to_aes2501(dev, cmd5_03, 2); ++ //send_data_to_aes2501(dev, cmd5_04, 2); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET); ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); ++ flush_aes2501_bulk_out(dev); ++ ++ ++ recv_data_to_aes2501(dev, buffer, 126); ++ ++ ++ ++ ++ ++ return 0; ++ ++} ++ ++static int detect_finger_on_aes2501(struct usb_aes2501 *dev) ++{ ++ //uint8_t cmd1[] = "\x80\x01\x82\x40"; ++ //uint8_t cmd2[] = "\x80\x01\x82\x40\x83\x00\x88\x02\x89\x10\x8a\x05\x8c\x00\x8e\x13\x91\x44\x92\x34\x95\x16\x96\x16\x97\x18\xa1\x70\xa2\x02\xa7\x00\xac\x01\xad\x1a\x80\x04\x81\x04\xb4\x00";/*42*/ ++ ++ ++ unsigned char buffer[22]; ++ unsigned i, sum; ++ ++ //send_data_to_aes2501(dev, cmd1, 4); ++ ++ //msleep(30); ++ ++ ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* Reserved */ ++ write_aes2501_register(dev, AES2501_REG_DETCTRL, AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS); ++ write_aes2501_register(dev, AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US);/* not the faster ??? */ ++ write_aes2501_register(dev, AES2501_REG_MEASDRV, AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE); ++ write_aes2501_register(dev, AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M); ++ write_aes2501_register(dev, AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE);/* Default */ ++ write_aes2501_register(dev, AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE); ++ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X); ++ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x44); ++ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x34); ++ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x16); ++ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x16); ++ write_aes2501_register(dev, AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08); ++ write_aes2501_register(dev, AES2501_REG_TREG1, 0x70);/* Reserved + 0xXX */ ++ write_aes2501_register(dev, 0xa2, 0x02);/* Reserved */ ++ write_aes2501_register(dev, 0xa7, 0x00);/* Reserved */ ++ write_aes2501_register(dev, AES2501_REG_TREGC, AES2501_TREGC_ENABLE); ++ write_aes2501_register(dev, AES2501_REG_TREGD, 0x1a); ++ ++ flush_aes2501_bulk_out(dev); ++ ++ /** ++ * TODO: ++ * - lire ici les deux octets. ++ * - voir pour inverser les commandes. ++ */ ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); ++ write_aes2501_register(dev, AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE); ++ ++ flush_aes2501_bulk_out(dev); ++ ++ ++ //send_data_to_aes2501(dev, cmd2, 42); ++ ++ recv_data_to_aes2501(dev, buffer, 20); ++ ++ ++ /* One column is returned here but I don't know which one. ++ Maybe an average over the whole sensor area. */ ++ sum = 0; ++ for (i = 1; i != 9; i++) { ++ sum += (buffer[i] & 0xf) + (buffer[i] >> 4); ++ } ++ ++ ++ //printk(KERN_INFO "===finger=== sum = %d vs %d\n", sum, 20); ++ ++ ++ /* ++ return (sum > aes->fingerTh1); ++ */ ++ ++ return sum > 20; ++ ++} ++ ++ ++static unsigned read_fingerprint_on_aes2501(struct usb_aes2501 *dev, void *raw_, unsigned maxstrip) ++{ ++ //unsigned char patch_msg[] = "\x80\x01"; ++ ++ /* 8e xx = set gain */ ++ //uint8_t cmd1_00[] = "0x80\x01\x82\x40"; ++ //uint8_t cmd1_01[] = "0x80\x01\x82\x40\x83\x00\x88\x02\x8c\x7c\x89\x10\x8d\x24\x9b\x00\x9c\x6c\x9d\x09\x9e\x54\x9f\x78\xa2\x02\xa7\x00\xb6\x26\xb7\x1a\x80\x04\x98\x23\x95\x10\x96\x1f\x8e\x00\x91\x70\x92\x20\x81\x04\xb4\x00";/*50*/ ++ ++ //uint8_t cmd2[] = "\x98\x23\x95\x10\x96\x1f\x8e\x03\x91\x70\x92\x20\x81\x04";/*14*/ ++ ++ //uint8_t cmd3[] = "\x98\x22\x95\x00\x96\x2f\x8e\x03\x91\x5b\x92\x20\x81\x04";/*14*/ ++ ++ uint8_t buf[1705]; ++ uint8_t *raw = (uint8_t *) raw_; ++ unsigned nstrips; ++ ++ int threshold; ++ int sum; ++ ++ ++ //send_data_to_aes2501(dev, patch_msg, 2); ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ flush_aes2501_bulk_out(dev); ++ ++ ++ ++ //send_data_to_aes2501(dev, cmd1_00, 4); ++ ++ ++ //send_data_to_aes2501(dev, cmd1_01, 50); ++ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); ++ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* Reserved */ ++ write_aes2501_register(dev, AES2501_REG_DETCTRL, AES2501_DETCTRL_SDELAY_31_MS | AES2501_DETCTRL_DRATE_CONTINUOUS); ++ write_aes2501_register(dev, AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US); ++ write_aes2501_register(dev, AES2501_REG_DEMODPHASE2, 0x7c);/* ... */ ++ write_aes2501_register(dev, AES2501_REG_MEASDRV, AES2501_MEASDRV_MEASURE_SQUARE | AES2501_MEASDRV_MDRIVE_0_325); ++ write_aes2501_register(dev, AES2501_REG_DEMODPHASE1, 0x24);/* ... */ ++ write_aes2501_register(dev, AES2501_REG_CHWORD1, 0x00);/* Challenge Word */ ++ write_aes2501_register(dev, AES2501_REG_CHWORD2, 0x6c);/* Challenge Word */ ++ write_aes2501_register(dev, AES2501_REG_CHWORD3, 0x09);/* Challenge Word */ ++ write_aes2501_register(dev, AES2501_REG_CHWORD4, 0x54);/* Challenge Word */ ++ write_aes2501_register(dev, AES2501_REG_CHWORD5, 0x78);/* Challenge Word */ ++ write_aes2501_register(dev, 0xa2, 0x02);/* Reserved */ ++ write_aes2501_register(dev, 0xa7, 0x00);/* Reserved */ ++ write_aes2501_register(dev, 0xb6, 0x26);/* Reserved */ ++ write_aes2501_register(dev, 0xb7, 0x1a);/* Reserved */ ++ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); ++ write_aes2501_register(dev, AES2501_REG_IMAGCTRL, AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE | AES2501_IMAGCTRL_IMG_DATA_DISABLE); ++ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x10); ++ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x1f); ++ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_2X | AES2501_CHANGAIN_STAGE2_2X); ++ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x70); ++ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x20); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); ++ write_aes2501_register(dev, AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE); ++ ++ ///////recv_data_to_aes2501(dev, buf, 159); ++ /* TODO: calibration e.g setting gain (0x8e xx) */ ++ ++ ++ ++ ++ //send_data_to_aes2501(dev, cmd2, 14); ++ ++ ++ ++ write_aes2501_register(dev, AES2501_REG_IMAGCTRL, AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE | AES2501_IMAGCTRL_IMG_DATA_DISABLE); ++ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x10); ++ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x1f); ++ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X); ++ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x70); ++ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x20); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); ++ ++ flush_aes2501_bulk_out(dev); ++ ++ ++ recv_data_to_aes2501(dev, buf, 159); ++ ++ nstrips = 0; ++ do { ++ printk(KERN_INFO "-start-\n"); ++ /* Timing in this loop is critical. It decides how fast you can move your finger. ++ If one loop takes tl second, the maximum speed is: ++ (16/500 * 25.4) / tl [mm per sec] ++ */ ++ //send_data_to_aes2501(dev, cmd3, 14); ++ ++ write_aes2501_register(dev, AES2501_REG_IMAGCTRL, AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE); ++ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x00); ++ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x2f); ++ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X); ++ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x5b); ++ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x20); ++ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); ++ ++ flush_aes2501_bulk_out(dev); ++ ++ ++ ++ recv_data_to_aes2501(dev, buf, 1705); ++ memcpy(raw, buf+1, 192*8); ++ raw += 192*8; ++ ++ printk(KERN_INFO "-end of copy-\n"); ++ ++ ++ threshold = read_register_value((buf + 1 + 192*8 + 1 + 16*2 + 1 + 8), _AES2501_REG_DATFMT); ++ /*if (threshold < 0) ++ return threshold;*/ ++ ++ threshold &= 0x0f; ++ ++ sum = sum_histogram_values((buf + 1 + 192*8), threshold); ++ /*if (threshold < 0) ++ return threshold;*/ ++ ++ nstrips++; ++ printk(KERN_INFO "-end- %d vs %d\n", nstrips, maxstrip); ++ } while (sum > 0 && nstrips < maxstrip); ++ printk(KERN_INFO "nstrips = %u\n", nstrips); ++ if (nstrips == maxstrip) ++ printk(KERN_INFO "nstrips == %u, swiping the finger too slowly?\n", maxstrip); ++ return nstrips; ++} ++ ++ ++ ++/** ++ * Read a register value from a dump sent by the device. ++ * @data: list of couples . ++ * @target: index of the register to process. ++ * @return: -EILSEQ,-EINVAL if error, value > 0 if a finger is here. ++ */ ++int read_register_value(const uint8_t *data, Aes2501Registers target) ++{ ++ int result; ++ uint8_t offset; ++ ++ if (*data == FIRST_AES2501_REG) ++ { ++ offset = target; ++ ++ if (!(FIRST_AES2501_REG <= offset && offset <= LAST_AES2501_REG)) ++ result = -EINVAL; ++ ++ else ++ { ++ offset -= FIRST_AES2501_REG; ++ offset *= 2; ++ ++ result = data[++offset]; ++ ++ } ++ ++ } ++ else result = -EILSEQ; ++ ++ return result; ++ ++} ++ ++ ++/** ++ * Tells if a finger motion is still detected. ++ * @data: start point of data to handle, preceded by 0xde. ++ * @threshold: index of values set to 1 by the device. ++ * @return: -EILSEQ,-EINVAL if error, value > 0 if a finger is here. ++ * ++ * Histograms always are a 16-uint16_t long message, where ++ * histogram[i] = number of pixels of value i. ++ */ ++int sum_histogram_values(const uint8_t *data, uint8_t threshold) ++{ ++ int result; ++ uint16_t *histogram; ++ uint8_t i; ++ ++ if (*data == 0xde) ++ { ++ if (threshold > 0x0f) ++ result = -EINVAL; ++ ++ else ++ { ++ result = 0; ++ histogram = (uint16_t *)(data + 1); ++ ++ for (i = threshold; i < 16; i++) ++ result += histogram[i]; ++ ++ } ++ ++ } ++ else result = -EILSEQ; ++ ++ return result; ++ ++} ++ ++ ++ ++/********************************************************** ++ ** Image processing ++ **********************************************************/ ++ ++/* This function finds overlapping parts of two frames ++ * Based on calculating normalized hamming distance ++ * between two frames ++ */ ++ ++static unsigned find_overlap(const uint8_t *first_frame, ++ const uint8_t *second_frame, ++ uint32_t frame_height, ++ uint32_t frame_width, ++ uint32_t *min_error) ++{ ++ uint32_t dy, i; ++ uint32_t error, not_overlapped_height = 0; ++ *min_error = 255 * frame_height * frame_width; ++ for (dy = 0; dy < frame_height; dy++) { ++ /* Calculating difference (error) between parts of frames */ ++ error = 0; ++ for (i = 0; i < frame_width * (frame_height - dy); i++) { ++ /* Using ? operator to avoid abs function */ ++ error += first_frame[i] > second_frame[i] ? ++ (first_frame[i] - second_frame[i]) : ++ (second_frame[i] - first_frame[i]); ++ } ++ ++ /* Normalizing error */ ++ error *= 15; ++ error /= i; ++ if (error < *min_error) { ++ *min_error = error; ++ not_overlapped_height = dy; ++ } ++ first_frame += frame_width; ++ } ++ ++ return not_overlapped_height; ++} ++ ++ ++/* This function assembles frames to single image, returns image height ++ * TODO: add Doxygen comments ++ */ ++static unsigned assemble(const uint8_t *input, /* Raw data received from device */ ++ uint8_t *output, /* Output buffer */ ++ uint32_t frame_height, /* Height of each frame */ ++ uint32_t frame_width, /* Width of each frame */ ++ uint32_t frames_count, /* Frames count */ ++ /*int overlap, */ ++ uint8_t reversed, ++ uint32_t *errors_sum ++ ) ++{ ++ uint8_t *assembled = output; ++ uint32_t frame, column, row, image_height = frame_height; ++ uint32_t error, not_overlapped; ++ ++ *errors_sum = 0; ++ ++ if (frames_count < 1) return 0; ++ ++ /* Rotating given data by 90 degrees ++ * Taken from document describing aes2501 image format ++ * TODO: move reversing detection here */ ++ if (reversed) { ++ output += (frames_count - 1) * frame_width * frame_height; ++ } ++ ++ for (frame = 0; frame < frames_count; frame++) { ++ for (column = 0; column < frame_width; column++) { ++ for (row = 0; row < (frame_height / 2); row++) { ++ output[frame_width * ( 2 * row) + column] = *input & 0x0F; ++ output[frame_width * ( 2 * row + 1) + column] = *input >> 4; ++ input++; ++ } ++ } ++ ++ if (reversed) { ++ output -= frame_width * frame_height; ++ } ++ else { ++ output += frame_width * frame_height; ++ } ++ } ++ ++ /* Detecting where frames overlaped */ ++ output = assembled; ++ for (frame = 1; frame < frames_count; frame++) ++ { ++ output += frame_width * frame_height; ++ not_overlapped = find_overlap(assembled, output, frame_height, frame_width, &error); ++ *errors_sum += error; ++ image_height += not_overlapped; ++ assembled += frame_width * not_overlapped; ++ memcpy(assembled, output, frame_width * frame_height); ++ } ++ ++ return image_height; ++} ++ ++ ++ ++static void store_new_aes2501_pnm(const uint8_t *data, unsigned int width, unsigned int height) ++{ ++ char header[20]; ++ __kernel_size_t header_len; ++ int i; ++ char *iter; ++ ++ sprintf(header, "P5\n%u %u\n255\n", width, height); ++ header_len = strlen(header); ++ ++ finger_print_len = header_len + width * height; ++ finger_print = kzalloc(finger_print_len, GFP_KERNEL); ++ ++ memcpy(finger_print, header, header_len); ++ ++ for (i = 0, iter = &finger_print[header_len]; i < width * height; i++, iter++) ++ *iter = (data[i] << 4) + 0x0f; ++ ++} ++ ++ ++ ++static void do_scanning(struct work_struct *work) ++{ ++ struct usb_aes2501 *dev; ++ ++ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ ++ ++ uint8_t *raw, *cooked; ++ unsigned swidth = 192; ++ unsigned sheight = 16; ++ unsigned maxstrip = 150; ++ unsigned nstrips, height, mindiff_sum, mindiff_sum_r; ++ ++ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ ++ ++ dev = container_of(work, struct usb_aes2501, scan_work); ++ ++ ++ ++ ++ ++ ++ setup_aes2501(dev); ++ ++ while (!detect_finger_on_aes2501(dev)) ++ { ++ if (dev->stop_scan) ++ { ++ PRINTK_INFO("aborting scan due to module removal\n"); ++ return; ++ } ++ }; ++ ++ /* We don't need physical continuity of allocated memory */ ++ raw = (uint8_t *)vmalloc((3 * maxstrip * sheight * swidth) / 2); ++ if (raw == NULL) ++ { ++ PRINTK_CRIT("failed to allocate memory\n"); ++ return; ++ } ++ ++ cooked = raw + (maxstrip * sheight * swidth)/2; ++ ++ ++ ++ printk(KERN_INFO "Raw is 0x%p\n", raw); ++ ++ ++ printk(KERN_INFO "Scanning...\n"); ++ nstrips = read_fingerprint_on_aes2501(dev, raw, maxstrip); ++ ++ printk(KERN_INFO "Assembling...\n"); ++ ++ ++ height = assemble(raw, cooked, 16, 192, nstrips, 0, &mindiff_sum); ++ height = assemble(raw, cooked, 16, 192, nstrips, 1, &mindiff_sum_r); ++ ++ ++ printk(KERN_INFO "Mindiff normal: %d, mindiff reversed: %d\n", mindiff_sum, mindiff_sum_r); ++ ++ if (mindiff_sum_r < mindiff_sum) ++ { ++ PRINTK_INFO("reversed image detected\n"); ++ } ++ else ++ { ++ height = assemble(raw, cooked, 16, 192, nstrips, 0, &mindiff_sum); ++ } ++ ++ printk(KERN_INFO "First height :: %d\n", height); ++ ++#if 0 ++ if (height < 100) { ++ /* It was a "touch", not a finger scan. */ ++ printk(KERN_INFO "Was only a 'touch' ;( [%d]\n", height); ++ /*break;*/ ++ } ++ else ++ ++ { ++ //store_new_aes2501_pnm(cooked, swidth, height); ++ height = assemble(raw, cooked, nstrips, 1, sensor_reversed); ++ printk(KERN_INFO "Second height :: %d\n", height); ++ store_new_aes2501_pnm(cooked, swidth, height); ++ } ++#endif ++ store_new_aes2501_pnm(cooked, swidth, height); ++ standby_aes2501(dev); ++ ++ vfree(raw); ++ ++ flag = 1; ++ wake_up_interruptible(&wq); ++ ++ ++} ++ ++ ++static int aes2501_open(struct inode *inode, struct file *file) ++{ ++ struct usb_aes2501 *dev; ++ ++ //return 0; ++ ++ ++ dev = _dev; ++ ++ printk(KERN_INFO "Plop !\n"); ++ ++ ++ return 0; ++} ++ ++ ++ ++ ++ ++ ++/* ++ * usb class driver info in order to get a minor number from the usb core, ++ * and to have the device registered with the driver core ++ */ ++static struct usb_class_driver aes2501_class = { ++ .name = "aes2501%d", ++ .fops = &aes2501_fops, ++ .minor_base = USB_AES2501_MINOR_BASE, ++}; ++ ++static int aes2501_probe(struct usb_interface *interface, const struct usb_device_id *id) ++{ ++ int retval; ++ struct usb_aes2501 *dev; ++ struct usb_host_interface *iface_desc; ++ __u8 i; ++ struct usb_endpoint_descriptor *endpoint; ++ __u16 buffer_size; ++ ++ ++ ++#if 0 ++ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ ++ ++ uint8_t *raw, *cooked; ++ unsigned swidth = 192; ++ unsigned sheight = 16; ++ unsigned maxstrip = 150; ++ unsigned nstrips, height; ++ ++ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ ++ ++ ++ raw = (uint8_t *)__get_free_page(GFP_KERNEL);//kmalloc((3 * maxstrip * sheight * swidth) / 2, GFP_KERNEL); ++ cooked = raw + (maxstrip * sheight * swidth)/2; ++#endif ++ ++ printk(KERN_INFO "aes2501 probe !\n"); ++ ++ ++ retval = -ENOMEM; ++ ++ /* Allocate memory for our device state and initialize it */ ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (dev == NULL) ++ { ++ PRINTK_CRIT("out of memory\n"); ++ goto error; ++ } ++ ++ _dev = dev; ++ ++ dev->stop_scan = 0; ++ ++ kref_init(&dev->kref); ++ sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); ++ mutex_init(&dev->io_mutex); ++ ++ dev->udev = usb_get_dev(interface_to_usbdev(interface)); ++ dev->interface = interface; ++ ++ printk(KERN_INFO "num_altsetting == %d\n", interface->num_altsetting); ++ ++ /** ++ * Set up the endpoint information. ++ * Use only the first bulk-in and bulk-out endpoints. ++ */ ++ ++ iface_desc = interface->cur_altsetting; ++ ++ printk(KERN_INFO "BULK COUNT %d\n", iface_desc->desc.bNumEndpoints); ++ ++ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) ++ { ++ endpoint = &iface_desc->endpoint[i].desc; ++ ++ if (!dev->bulk_in_endpointAddr && /*usb_endpoint_is_bulk_in(endpoint))*/ ++ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ++ == USB_DIR_IN) && ++ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ == USB_ENDPOINT_XFER_BULK)) ++ { ++ /* We found a bulk in endpoint */ ++ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); ++ dev->bulk_in_size = buffer_size; ++ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; ++ dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); ++ ++ printk(KERN_INFO "BULK IN %d ; size = %d\n", i, buffer_size); ++ ++ if (dev->bulk_in_buffer == NULL) ++ { ++ PRINTK_CRIT("could not allocate bulk_in_buffer\n"); ++ goto error; ++ } ++ ++ } ++ ++ if (!dev->bulk_out_endpointAddr && /*usb_endpoint_is_bulk_out(endpoint))*/ ++ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ++ == USB_DIR_OUT) && ++ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ++ == USB_ENDPOINT_XFER_BULK)) ++ { ++ printk(KERN_INFO "BULK OUT %d\n", i); ++ ++ /* We found a bulk out endpoint */ ++ dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; ++ } ++ ++ } ++ ++ if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) ++ { ++ PRINTK_CRIT("could not find both bulk-in and bulk-out endpoints\n"); ++ goto error; ++ } ++ ++/* ++ dev->once_tasklet = kzalloc(sizeof(*dev->once_tasklet), GFP_KERNEL); ++ if (dev->once_tasklet == NULL) ++ { ++ PRINTK_CRIT("out of memory\n"); ++ goto error; ++ } ++*/ ++ /* Save our data pointer in this interface device */ ++ usb_set_intfdata(interface, dev); ++ ++ /* We can register the device now, as it is ready */ ++ retval = usb_register_dev(interface, &aes2501_class); ++ if (retval) { ++ /* something prevented us from registering this driver */ ++ PRINTK_CRIT("not able to get a minor for this device\n"); ++ usb_set_intfdata(interface, NULL); ++ goto error; ++ } ++ ++ /* Let the user know what node this device is now attached to */ ++ PRINTK_INFO("device now attached to aes2501-%d\n", interface->minor); ++ ++#if 0 ++ setup_aes2501(dev); ++ ++ while (!detect_finger_on_aes2501(dev)); ++ ++ ++ printk(KERN_INFO "Raw is 0x%p\n", raw); ++ ++ ++ printk(KERN_INFO "Scanning...\n"); ++ nstrips = read_fingerprint_on_aes2501(dev, raw, maxstrip); ++ ++ printk(KERN_INFO "Assembling...\n"); ++ ++ height = assemble(raw, cooked, nstrips, 1); ++ if (height < 100) { ++ /* It was a "touch", not a finger scan. */ ++ printk(KERN_INFO "Was only a 'touch' ;(\n"); ++ /*break;*/ ++ } ++ else ++ { ++ store_new_aes2501_pnm(cooked, swidth, height); ++ height = assemble(raw, cooked, nstrips, 0); ++ store_new_aes2501_pnm(cooked, swidth, height); ++ } ++ ++ standby_aes2501(dev); ++#endif ++ ++ return 0; ++ ++error: ++ if (dev != NULL) ++ kref_put(&dev->kref, aes2501_delete); ++ ++ return retval; ++ ++} ++ ++ ++static void aes2501_disconnect(struct usb_interface *interface) ++{ ++ int minor; ++ struct usb_aes2501 *dev; ++ ++ minor = interface->minor; ++ ++ /* Prevent aes2501_open() from racing aes2501_disconnect() */ ++ lock_kernel(); ++ ++ dev = usb_get_intfdata(interface); ++ usb_set_intfdata(interface, NULL); ++ ++ /* Give back our minor */ ++ usb_deregister_dev(interface, &aes2501_class); ++ ++ /* Prevent more I/O from starting */ ++ mutex_lock(&dev->io_mutex); ++ dev->interface = NULL; ++ mutex_unlock(&dev->io_mutex); ++ ++ unlock_kernel(); ++ ++ /* Decrement our usage count */ ++ kref_put(&dev->kref, aes2501_delete); ++ ++ PRINTK_INFO("aes2501 device #%d now disconnected\n", minor); ++ ++ ++ ++ ++ printk(KERN_INFO "aes2501 disconnect !\n"); ++ ++ ++} ++ ++ ++static struct usb_driver aes2501_driver = { ++ .name = "aes2501", ++ .probe = aes2501_probe, ++ .disconnect = aes2501_disconnect, ++ .id_table = id_table, ++}; ++ ++ ++static int __init aes2501_init(void) ++{ ++ int retval; ++ ++ comm_queue = create_singlethread_workqueue("aes2501"); ++ if (comm_queue == NULL) { ++ PRINTK_CRIT("could not create work queue\n"); ++ return -ENOMEM; ++ } ++ ++ retval = usb_register(&aes2501_driver); ++ if (retval == -EINVAL) ++ { ++ printk(KERN_ERR "usb_register failed. Error number %d\n", retval); ++ destroy_workqueue(comm_queue); ++ return retval; ++ } ++ ++ return 0; ++ ++} ++ ++static void __exit aes2501_exit(void) ++{ ++ _dev->stop_scan = 1; ++ destroy_workqueue(comm_queue); ++ usb_deregister(&aes2501_driver); ++} ++ ++module_init(aes2501_init); ++module_exit(aes2501_exit); ++ ++MODULE_AUTHOR("Cyrille Bagard"); ++MODULE_DESCRIPTION("AES 2501 fingerprint scanner driver"); ++MODULE_LICENSE("GPL"); +diff -Nurp linux-2.6.37/3rdparty/aes2501/aes2501.h linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.h +--- linux-2.6.37/3rdparty/aes2501/aes2501.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.h 2008-01-14 03:07:30.000000000 +0200 +@@ -0,0 +1,40 @@ ++ ++/* ++ * aes2501.h -- AuthenTec AES2501 Fingerprint Sensor Driver for Linux ++ * ++ * Maintainer: Cyrille Bagard ++ * ++ * Copyright (C) 2007 Cyrille Bagard ++ * ++ * This file is part of the AES2501 driver. ++ * ++ * the AES2501 driver is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * the AES2501 driver 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 the AES2501 driver; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++ ++#ifndef __AES2501_H ++#define __AES2501_H ++ ++ ++#define AES2501_IOC_MAGIC 'N' ++ ++ ++#define AES2501_IOC_TEST _IOW(AES2501_IOC_MAGIC, 1, int) ++ ++ ++ ++ ++#endif /* __AES2501_H */ +diff -Nurp linux-2.6.37/3rdparty/aes2501/aes2501_regs.h linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501_regs.h +--- linux-2.6.37/3rdparty/aes2501/aes2501_regs.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501_regs.h 2008-01-14 03:07:30.000000000 +0200 +@@ -0,0 +1,464 @@ ++ ++/* ++ * aes2501_regs.h -- AuthenTec AES2501 Fingerprint Sensor Driver for Linux ++ * ++ * Maintainer: Cyrille Bagard ++ * ++ * Copyright (C) 2007 Cyrille Bagard ++ * ++ * This file is part of the AES2501 driver. ++ * ++ * the AES2501 driver is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * the AES2501 driver 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 the AES2501 driver; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++ ++#ifndef __AES2501_REGS_H ++#define __AES2501_REGS_H ++ ++ ++ ++/* ++ * AES2501 Control Register 1 (CTRL1) ++ */ ++ ++#define AES2501_REG_CTRL1 0x80 ++ ++#define AES2501_CTRL1_MASTER_RESET 0x01 /* Master reset, same as Power On Reset */ ++#define AES2501_CTRL1_SCAN_RESET 0x02 /* Scan reset: stop and restart the scan sequencer */ ++#define AES2501_CTRL1_REG_UPDATE 0x04 /* 1 = continuously updated, 0 = updated prior to starting a scan */ ++ ++ ++/* ++ * AES2501 Control Register 2 (CTRL2) ++ */ ++ ++#define AES2501_REG_CTRL2 0x81 ++ ++#define AES2501_CTRL2_CONTINUOUS 0x01 /* 1 = continuous scans, 0 = single scans */ ++#define AES2501_CTRL2_READ_REGS 0x02 /* Read the current state of the local registers in the Sensor IC */ ++#define AES2501_CTRL2_SET_ONE_SHOT 0x04 /* Set the one-shot flip-flop */ ++#define AES2501_CTRL2_CLR_ONE_SHOT 0x08 /* Clear the one-shot flip-flop */ ++#define AES2501_CTRL2_READ_ID 0x10 /* Read the ID register of the chip */ ++ ++ ++/* ++ * AES2501 Excitation Common Control Register (EXCITCTRL) ++ */ ++ ++#define AES2501_REG_EXCITCTRL 0x82 ++ ++#define AES2501_EXCITCTRL_LO_PWR 0x01 /* If set, enable detection in sleep/suspend mode */ ++#define AES2501_EXCITCTRL_AUTO_CAL 0x02 /* If set, perform finger detection calibration */ ++#define AES2501_EXCITCTRL_SGC_ENABLE 0x04 /* ??? */ ++#define AES2501_EXCITCTRL_SGC_RESTART 0x08 /* ??? */ ++ ++#define AES2501_EXCITCTRL_EXCIT_SQR 0x20 /* Select the (1=square | 0=sine) wave finger drive signal */ ++#define AES2501_EXCITCTRL_EXCIT_BOOST 0x10 /* TODO: understand this part */ ++ ++ ++/* ++ * AES2501 Detect Control Register (DETCTRL) ++ */ ++ ++#define AES2501_REG_DETCTRL 0x83 ++ ++enum aes2501_detection_rate { ++ ++ AES2501_DETCTRL_DRATE_CONTINUOUS = 0x00, /* Detection cycles occur continuously */ ++ AES2501_DETCTRL_DRATE_16_MS = 0x01, /* Detection cycles occur every 16.62 ms */ ++ AES2501_DETCTRL_DRATE_31_MS = 0x02, /* Detection cycles occur every 31.24 ms */ ++ AES2501_DETCTRL_DRATE_62_MS = 0x03, /* Detection cycles occur every 62.50 ms */ ++ AES2501_DETCTRL_DRATE_125_MS = 0x04, /* Detection cycles occur every 125.0 ms */ ++ AES2501_DETCTRL_DRATE_250_MS = 0x05, /* Detection cycles occur every 250.0 ms */ ++ AES2501_DETCTRL_DRATE_500_MS = 0x06, /* Detection cycles occur every 500.0 ms */ ++ AES2501_DETCTRL_DRATE_1_S = 0x07 /* Detection cycles occur every 1 s */ ++ ++}; ++ ++enum aes2501_settling_delay { ++ ++ AES2501_DETCTRL_SDELAY_31_MS = 0x00, /* 31.25 ms */ ++ AES2501_DETCTRL_SSDELAY_62_MS = 0x10, /* 62.5 ms */ ++ AES2501_DETCTRL_SSDELAY_125_MS = 0x20, /* 125 ms */ ++ AES2501_DETCTRL_SSDELAY_250_MS = 0x30 /* 250 ms */ ++ ++}; ++ ++ ++ ++ ++ ++/* ++ * AES2501 Column Scan Rate Register (COLSCAN) ++ */ ++ ++#define AES2501_REG_COLSCAN 0x88 ++ ++enum aes2501_col_scan_rate { ++ ++ AES2501_COLSCAN_SRATE_32_US = 0x00, /* 32 us */ ++ AES2501_COLSCAN_SRATE_64_US = 0x01, /* 64 us */ ++ AES2501_COLSCAN_SRATE_128_US = 0x02, /* 128 us */ ++ AES2501_COLSCAN_SRATE_256_US = 0x03, /* 256 us */ ++ AES2501_COLSCAN_SRATE_512_US = 0x04, /* 512 us */ ++ AES2501_COLSCAN_SRATE_1024_US = 0x05, /* 1024 us */ ++ AES2501_COLSCAN_SRATE_2048_US = 0x06, /* 2048 us */ ++ ++}; ++ ++ ++/* ++ * AES2501 Measure Drive Register (MEASDRV) ++ */ ++ ++#define AES2501_REG_MEASDRV 0x89 ++ ++enum aes2501_mesure_drive { ++ ++ AES2501_MEASDRV_MDRIVE_0_325 = 0x00, /* 0.325 Vpp */ ++ AES2501_MEASDRV_MDRIVE_0_65 = 0x01, /* 0.65 Vpp */ ++ AES2501_MEASDRV_MDRIVE_1_3 = 0x02, /* 1.3 Vpp */ ++ AES2501_MEASDRV_MDRIVE_2_6 = 0x03 /* 2.6 Vpp */ ++ ++}; ++ ++#define AES2501_MEASDRV_SQUARE 0x20 /* Select (1=square | 0=sine) wave drive during measure */ ++#define AES2501_MEASDRV_MEASURE_SQUARE 0x10 /* 0 = use mesure drive setting, 1 = when sine wave is selected */ ++ ++ ++/* ++ * AES2501 Measure Frequency Register (MEASFREQ) ++ */ ++ ++#define AES2501_REG_MEASFREQ 0x8a ++ ++enum aes2501_measure_freq { ++ ++ AES2501_MEASFREQ_125K = 0x01, /* 125 kHz */ ++ AES2501_MEASFREQ_250K = 0x02, /* 250 kHz */ ++ AES2501_MEASFREQ_500K = 0x03, /* 500 kHz */ ++ AES2501_MEASFREQ_1M = 0x04, /* 1 MHz */ ++ AES2501_MEASFREQ_2M = 0x05 /* 2 MHz */ ++ ++}; ++ ++ ++ ++ ++/* ++ * AES2501 Demod Phase 2 Register (DEMODPHASE2) ++ */ ++ ++#define AES2501_REG_DEMODPHASE2 0x8c ++ ++#define DEMODPHASE_NONE 0x00 ++ ++#define DEMODPHASE_180_00 0x40 /* 180 degrees */ ++#define DEMODPHASE_2_81 0x01 /* 2.8125 degrees */ ++ ++ ++/* ++ * AES2501 Demod Phase 1 Register (DEMODPHASE1) ++ */ ++ ++#define AES2501_REG_DEMODPHASE1 0x8d ++ ++#define DEMODPHASE_1_40 0x40 /* 1.40625 degrees */ ++#define DEMODPHASE_0_02 0x01 /* 0.02197256 degrees */ ++ ++ ++/* ++ * AES2501 Channel Gain Register (CHANGAIN) ++ */ ++ ++#define AES2501_REG_CHANGAIN 0x8e ++ ++enum aes2501_sensor_gain1 { ++ ++ AES2501_CHANGAIN_STAGE1_2X = 0x00, /* 2x */ ++ AES2501_CHANGAIN_STAGE1_4X = 0x01, /* 4x */ ++ AES2501_CHANGAIN_STAGE1_8X = 0x02, /* 8x */ ++ AES2501_CHANGAIN_STAGE1_16X = 0x03 /* 16x */ ++ ++}; ++ ++enum aes2501_sensor_gain2 { ++ ++ AES2501_CHANGAIN_STAGE2_2X = 0x00, /* 2x */ ++ AES2501_CHANGAIN_STAGE2_4X = 0x10, /* 4x */ ++ AES2501_CHANGAIN_STAGE2_8X = 0x20, /* 8x */ ++ AES2501_CHANGAIN_STAGE2_16X = 0x30 /* 16x */ ++ ++}; ++ ++ ++ ++/* ++ * AES2501 A/D Reference High Register (ADREFHI) ++ */ ++ ++#define AES2501_REG_ADREFHI 0x91 ++ ++ ++/* ++ * AES2501 A/D Reference Low Register (ADREFLO) ++ */ ++ ++#define AES2501_REG_ADREFLO 0x92 ++ ++ ++/* ++ * AES2501 Start Row Register (STRTROW) ++ */ ++ ++#define AES2501_REG_STRTROW 0x93 ++ ++ ++/* ++ * AES2501 End Row Register (ENDROW) ++ */ ++ ++#define AES2501_REG_ENDROW 0x94 ++ ++ ++/* ++ * AES2501 Start Column Register (STRTCOL) ++ */ ++ ++#define AES2501_REG_STRTCOL 0x95 ++ ++ ++/* ++ * AES2501 End Column Register (ENDCOL) ++ */ ++ ++#define AES2501_REG_ENDCOL 0x96 ++ ++ ++/* ++ * AES2501 Data Format Register (DATFMT) ++ */ ++ ++#define AES2501_REG_DATFMT 0x97 ++ ++#define AES2501_DATFMT_EIGHT 0x40 /* 1 = 8-bit data, 0 = 4-bit data */ ++#define AES2501_DATFMT_LOW_RES 0x20 /* TODO: understand this part */ ++#define AES2501_DATFMT_BIN_IMG 0x10 /* TODO: understand this part */ ++ ++/* TODO: Threshold */ ++ ++ ++/* ++ * AES2501 Image Data Control Register (IMAGCTRL) ++ */ ++ ++#define AES2501_REG_IMAGCTRL 0x98 ++ ++#define AES2501_IMAGCTRL_IMG_DATA_DISABLE 0x01 /* If set, image data message and authentication message are not returned when imaging */ ++#define AES2501_IMAGCTRL_HISTO_DATA_ENABLE 0x02 /* if set, send histogram message when imaging */ ++#define AES2501_IMAGCTRL_HISTO_EACH_ROW 0x04 /* A histo message is sent at the end of (1=each row | 0 = scanning) */ ++#define AES2501_IMAGCTRL_HISTO_FULL_ARRAY 0x08 /* 1 = full image array, 0 = 64x64 center */ ++#define AES2501_IMAGCTRL_REG_FIRST 0x10 /* Registers are returned (1=before | 0=after) image data */ ++#define AES2501_IMAGCTRL_TST_REG_ENABLE 0x20 /* If set, Test Registers are returned with register messages */ ++ ++ ++ ++ ++ ++ ++ ++ ++/* ++ * AES2501 Status Register (STAT) ++ */ ++ ++#define AES2501_REG_STAT 0x9a ++ ++#define AES2501_STAT_SCAN 0x0f /* Scan state */ ++#define AES2501_STAT_ERROR 0x10 /* Framing error */ ++#define AES2501_STAT_PAUSED 0x20 /* Scan paused */ ++#define AES2501_STAT_RESET 0x40 /* Reset occurred */ ++ ++enum aes2501_scan_state { ++ ++ STATE_WAITING_FOR_FINGER = 0x00, /* Waiting for finger */ ++ STATE_FINGER_SETTLING_DELAY = 0x01, /* In Finger settling delay */ ++ STATE_POWER_UP_DELAY = 0x02, /* In power up delay */ ++ STATE_WAITING_TO_START_SCAN = 0x03, /* Waiting to start image scan */ ++ STATE_PRELOADING_SUBARRAY_0 = 0x04, /* Pre-loading subarray 0 */ ++ STATE_SETUP_FOR_ROW_ADVANCE = 0x05, /* Setup for row advance */ ++ STATE_WAITING_FOR_ROW_ADVANCE = 0x06, /* Waiting for row advance */ ++ STATE_PRELOADING_COL_0 = 0x07, /* Pre-loading column 0 */ ++ STATE_SETUP_FOR_COL_ADVANCE = 0x08, /* Setup for column advance */ ++ STATE_WAITING_FOR_COL_ADVANCE = 0x09, /* Waiting for column advance */ ++ STATE_WAITING_FOR_SCAN_START = 0x0a, /* Waiting for scan start */ ++ STATE_WAITING_FOR_SCAN_END = 0x0b, /* Waiting for scan end */ ++ STATE_WAITING_FOR_ROW_SETUP = 0x0c, /* Waiting for row setup */ ++ STATE_WAITING_FOR_COL_TIME = 0x0d, /* Waiting for one column time (depends on scan rate) */ ++ STATE_WAITING_FOR_QUEUED_DATA = 0x0e, /* Waiting for queued data transmission to be completed */ ++ STATE_WAIT_FOR_128_US = 0x0f /* Wait for 128 us */ ++ ++}; ++ ++ ++/* ++ * AES2501 Challenge Word 1 Register (CHWORD1) ++ */ ++ ++#define AES2501_REG_CHWORD1 0x9b ++ ++#define AES2501_CHWORD1_IS_FINGER 0x01 /* If set, finger is present */ ++ ++ ++/* ++ * AES2501 Challenge Word 2 Register (CHWORD2) ++ */ ++ ++#define AES2501_REG_CHWORD2 0x9c ++ ++ ++/* ++ * AES2501 Challenge Word 3 Register (CHWORD3) ++ */ ++ ++#define AES2501_REG_CHWORD3 0x9d ++ ++ ++/* ++ * AES2501 Challenge Word 4 Register (CHWORD4) ++ */ ++ ++#define AES2501_REG_CHWORD4 0x9e ++ ++ ++/* ++ * AES2501 Challenge Word 5 Register (CHWORD5) ++ */ ++ ++#define AES2501_REG_CHWORD5 0x9f ++ ++ ++ ++ ++ ++/* ++ * AES2501 Test Register 1 (TREG1) ++ */ ++ ++#define AES2501_REG_TREG1 0xa1 ++ ++#define AES2501_TREG1_SBIAS_UNLCK 0x10 /* 1 = unlock the controlling of sense amp bias, 0 = sense amp bias changes */ ++ ++enum aes2501_sense_amp_bias { ++ ++ AES2501_TREG1_SAMP_BIAS_2_UA = 0x00, /* 2.5 uA */ ++ AES2501_TREG1_SAMP_BIAS_5_UA = 0x01, /* 5 uA */ ++ AES2501_TREG1_SAMP_BIAS_8_UA = 0x02, /* 8 uA */ ++ AES2501_TREG1_SAMP_BIAS_10_UA = 0x03 /* 10 uA */ ++ ++}; ++ ++ ++ ++ ++/* ++ * AES2501 Auto-Calibration Offset Register (AUTOCALOFFSET) ++ */ ++ ++#define AES2501_REG_AUTOCALOFFSET 0xa8 ++ ++ ++ ++ ++ ++/* ++ * AES2501 Test Register C (TREGC) ++ */ ++ ++#define AES2501_REG_TREGC 0xac ++ ++#define AES2501_TREGC_ENABLE 0x01 /* Enable the reading of the register in TREGD */ ++ ++ ++/* ++ * AES2501 Test Register D (TREGD) ++ */ ++ ++#define AES2501_REG_TREGD 0xad ++ ++ ++ ++ ++ ++/* ++ * AES2501 Low Power Oscillator On Time Register (LPONT) ++ */ ++ ++#define AES2501_REG_LPONT 0xb4 ++ ++/* ++ * This register sets the low power oscillator on time. ++ * Units are roughly equivalent to milliseconds. ++ */ ++ ++#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */ ++#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */ ++ ++ ++ ++#define ENUM_REG(reg) _ ## reg = reg ++ ++typedef enum _Aes2501Registers ++{ ++ ENUM_REG(AES2501_REG_CTRL1), ++ ENUM_REG(AES2501_REG_CTRL2), ++ ENUM_REG(AES2501_REG_EXCITCTRL), ++ ENUM_REG(AES2501_REG_DETCTRL), ++ ENUM_REG(AES2501_REG_COLSCAN), ++ ENUM_REG(AES2501_REG_MEASDRV), ++ ENUM_REG(AES2501_REG_MEASFREQ), ++ ENUM_REG(AES2501_REG_DEMODPHASE2), ++ ENUM_REG(AES2501_REG_DEMODPHASE1), ++ ENUM_REG(AES2501_REG_CHANGAIN), ++ ENUM_REG(AES2501_REG_ADREFHI), ++ ENUM_REG(AES2501_REG_ADREFLO), ++ ENUM_REG(AES2501_REG_STRTROW), ++ ENUM_REG(AES2501_REG_ENDROW), ++ ENUM_REG(AES2501_REG_STRTCOL), ++ ENUM_REG(AES2501_REG_ENDCOL), ++ ENUM_REG(AES2501_REG_DATFMT), ++ ENUM_REG(AES2501_REG_IMAGCTRL), ++ ENUM_REG(AES2501_REG_STAT), ++ ENUM_REG(AES2501_REG_CHWORD1), ++ ENUM_REG(AES2501_REG_CHWORD2), ++ ENUM_REG(AES2501_REG_CHWORD3), ++ ENUM_REG(AES2501_REG_CHWORD4), ++ ENUM_REG(AES2501_REG_CHWORD5), ++ ENUM_REG(AES2501_REG_TREG1), ++ ENUM_REG(AES2501_REG_AUTOCALOFFSET), ++ ENUM_REG(AES2501_REG_TREGC), ++ ENUM_REG(AES2501_REG_TREGD), ++ ENUM_REG(AES2501_REG_LPONT) ++ ++} Aes2501Registers; ++ ++ ++#define FIRST_AES2501_REG 0x80 ++#define LAST_AES2501_REG 0x9f ++ ++ ++ ++#endif /* __AES2501_REGS_H */ +diff -Nurp linux-2.6.37/3rdparty/aes2501/ChangeLog linux-2.6.37.3rdparty/3rdparty/aes2501/ChangeLog +--- linux-2.6.37/3rdparty/aes2501/ChangeLog 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/aes2501/ChangeLog 2008-01-14 03:07:30.000000000 +0200 +@@ -0,0 +1,95 @@ ++2007-11-05 Cyrille Bagard ++ ++ * aes2501.c: ++ Apply Vasily Khoruzhick's new patch: rewrite image processing functions. ++ Thus, there should be no more significant common points with aes2501-wy. ++ ++2007-11-05 Cyrille Bagard ++ ++ * aes2501.c: ++ In order to respect the license of aes2501-wy, on which this driver is ++ based, begin to rewrite some functions. Thus, better handle histograms. ++ One more strip is often processed, but that is more logical like this. ++ ++ * aes2501_regs.h: ++ Enumerate all used registers for convenience. ++ ++2007-11-03 Cyrille Bagard ++ ++ * aes2501.c: ++ Clean the code. Take care of signals when processes are awaking. ++ Apply Vasily Khoruzhick's new patch: autodetect picture reversing needs ++ and fix kernel oops in case of module removal. ++ ++ * Makefile: ++ Remove hidden files and clean the rules. KERNELDIR has to be overrided ++ from the sheel if needed now. ++ ++2007-10-29 Cyrille Bagard ++ ++ * aes2501.c: ++ Apply Vasily Khoruzhick's patch and thus increase the resulting pictures ++ quality. Many thanks to him ! ++ ++ * Makefile: ++ Add two new rules: usertest and clean. ++ ++ * usertest.c: ++ Initial commit. ++ ++2007-08-27 Cyrille Bagard ++ ++ * aes2501.c: ++ Make INIT_WORK macro working with 2.6.20+ kernels. Many thanks to ++ Miguel Gea Milvaques for the patch ! ++ ++2007-06-13 Cyrille Bagard ++ ++ * aes2501.c: ++ * aes2501_regs.h: ++ Complete translation of all binary communications using well documented ++ registers and values (as often as possible). ++ ++2007-06-13 Cyrille Bagard ++ ++ * aes2501.c: ++ Increase communication speed by buffering bulk out. Output data ++ need to be flushed at the end now. ++ ++2007-06-13 Cyrille Bagard ++ ++ * aes2501.c: ++ * aes2501_regs.h: ++ Translate binary data used in finger detection. Some register values ++ still need to be understood. Note: some hex values are reserved bits ++ in registers. ++ ++2007-06-12 Cyrille Bagard ++ ++ * aes2501.c: ++ Begin the binary translation: the "standby" function now uses understood ++ instructions and prints debug status information if needed. CTRL2 register ++ value needs to be checked. ++ ++ * aes2501_regs.h: ++ Define the interesting hex values for the following registers: ++ CTRL2, STAT, TREGC and TREGD. ++ ++2007-06-12 Cyrille Bagard ++ ++ * aes2501.c: ++ Clean the driver a little bit by removing old piece of code to handle ++ a character device in the old way. ++ ++2007-06-12 Cyrille Bagard ++ ++ * aes2501.c: ++ Use an own work queue. Scanning now starts with the AES2501_IOC_TEST ++ ioctl command. Reading the /dev entry blocks until data is available. ++ ++2007-06-12 Cyrille Bagard ++ ++ * aes2501.c: ++ * aes2501.h: ++ * Makefile: ++ Initial [crappy] commit. +diff -Nurp linux-2.6.37/3rdparty/aes2501/Makefile linux-2.6.37.3rdparty/3rdparty/aes2501/Makefile +--- linux-2.6.37/3rdparty/aes2501/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/aes2501/Makefile 2008-01-14 03:07:30.000000000 +0200 +@@ -0,0 +1,19 @@ ++ ++obj-m := aes2501.o ++ ++KERNELDIR ?= /lib/modules/$(shell uname -r)/build ++ ++PWD := $(shell pwd) ++ ++all: usertest ++ $(MAKE) -C $(KERNELDIR) M=$(PWD) ++ ++usertest: ++ gcc -I. -o usertest usertest.c ++ ++clean: ++ rm -f *.o *~ aes2501.ko aes2501.mod.c Module.symvers usertest ++ rm -rf .tmp_versions ++ rm -f .*.cmd ++ ++ +diff -Nurp linux-2.6.37/3rdparty/aes2501/usertest.c linux-2.6.37.3rdparty/3rdparty/aes2501/usertest.c +--- linux-2.6.37/3rdparty/aes2501/usertest.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/aes2501/usertest.c 2008-01-14 03:07:30.000000000 +0200 +@@ -0,0 +1,78 @@ ++/** ++ * AES2501 Device Driver ++ * Userspace test program ++ * ++ * Compile with: ++ * gcc -I. -o usertest usertest.c ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define BUFFER_SIZE 256 ++ ++ ++int main(int argc, char *argv[]) ++{ ++ int fd_in; ++ int fd_out; ++ char buffer[BUFFER_SIZE]; ++ size_t len; ++ ++ fd_in = -1; ++ fd_out = -1; ++ ++ ++ int data, rdata; ++ ++ ++ ++ fd_in = open("/dev/aes2501", O_RDONLY); ++ if (fd_in == -1) { ++ perror("open() -- "); ++ goto err; ++ } ++ ++ fd_out = open("/root/pic.pnm", O_CREAT | O_WRONLY); ++ if (fd_out == -1) { ++ perror("open() -- "); ++ goto err; ++ } ++ ++ ++ ++ data = 0x55555555; ++ ioctl(fd_in, AES2501_IOC_TEST, data); ++ //ioctl(fd_in, CASE2, &rdata); ++ ++ printf("IOCTL test: written: '%x' - received: '%x'\n", data, rdata); ++ ++ ++ ++ ++ /* Write the fingerprint */ ++ while ((len = read(fd_in, buffer, BUFFER_SIZE)) > 0) ++ write(fd_out, buffer, len); ++ ++ close(fd_in); ++ close(fd_out); ++ ++ return EXIT_SUCCESS; ++ ++ err: ++ ++ if (fd_in != -1) ++ close(fd_in); ++ ++ if (fd_out != -1) ++ close(fd_out); ++ ++ return EXIT_FAILURE; ++ ++} diff --git a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch new file mode 100644 index 00000000..f11cc2e5 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch @@ -0,0 +1,21 @@ +Prevent oops on module removal when we don't have the device available. + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + linux-2.6.23/3rdparty/aes2501/aes2501.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff -p -up linux-2.6.23/3rdparty/aes2501/aes2501.c.orig linux-2.6.23/3rdparty/aes2501/aes2501.c +--- linux-2.6.23/3rdparty/aes2501/aes2501.c.orig 2008-01-13 23:37:38.000000000 -0200 ++++ linux-2.6.23/3rdparty/aes2501/aes2501.c 2008-01-13 23:38:26.000000000 -0200 +@@ -1616,7 +1616,8 @@ static int __init aes2501_init(void) + + static void __exit aes2501_exit(void) + { +- _dev->stop_scan = 1; ++ if (_dev) ++ _dev->stop_scan = 1; + destroy_workqueue(comm_queue); + usb_deregister(&aes2501_driver); + } diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch new file mode 100644 index 00000000..63ccc093 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch @@ -0,0 +1,26399 @@ +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/crt.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/crt.c +--- linux-3.12.2/3rdparty/ndiswrapper/crt.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/crt.c 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,589 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ntoskernel.h" ++#include "crt_exports.h" ++ ++#ifdef CONFIG_X86_64 ++/* Windows long is 32-bit, so strip single 'l' in integer formats */ ++static void strip_l_modifier(char *str) ++{ ++ char *ptr = str; ++ int in_format = 0; ++ char *lptr = NULL; ++ char last = 0; ++ char *end_ptr; ++ char *wptr; ++ ++ /* Replace single 'l' inside integer formats with '\0' */ ++ for (ptr = str; *ptr; ptr++) { ++ if (!in_format) { ++ if (*ptr == '%') ++ in_format = 1; ++ last = *ptr; ++ continue; ++ } ++ switch (*ptr) { ++ case 'd': ++ case 'i': ++ case 'o': ++ case 'u': ++ case 'x': ++ case 'X': ++ case 'p': ++ case 'n': ++ case 'm': ++ if (lptr) { ++ *lptr = '\0'; ++ lptr = NULL; ++ } ++ in_format = 0; ++ break; ++ case 'c': ++ case 'C': ++ case 's': ++ case 'S': ++ case 'f': ++ case 'e': ++ case 'E': ++ case 'g': ++ case 'G': ++ case 'a': ++ case 'A': ++ lptr = NULL; ++ in_format = 0; ++ break; ++ case '%': ++ lptr = NULL; ++ if (last == '%') ++ in_format = 0; ++ else ++ in_format = 1; /* ignore previous junk */ ++ break; ++ case 'l': ++ if (last == 'l') ++ lptr = NULL; ++ else ++ lptr = ptr; ++ break; ++ default: ++ break; ++ } ++ last = *ptr; ++ } ++ ++ /* Purge zeroes from the resulting string */ ++ end_ptr = ptr; ++ wptr = str; ++ for (ptr = str; ptr < end_ptr; ptr++) ++ if (*ptr != 0) ++ *(wptr++) = *ptr; ++ *wptr = 0; ++} ++ ++/* ++ * va_list on x86_64 Linux is designed to allow passing arguments in registers ++ * even to variadic functions. va_list is a structure holding pointers to the ++ * register save area, which holds the arguments passed in registers, and to ++ * the stack, which may have the arguments that did not fit the registers. ++ * va_list also holds offsets in the register save area for the next general ++ * purpose and floating point registers that the next va_arg() would fetch. ++ * ++ * Unlike Linux, the Windows va_list is just a pointer to the stack. No ++ * arguments are passed in the registers. That's why we construct the Linux ++ * va_list so that the register save area is never used. For that goal, we set ++ * the offsets to the maximal allowed values, meaning that the arguments passed ++ * in the registers have been exhausted. The values are 48 for general purpose ++ * registers (6 registers, 8 bytes each) and 304 for floating point registers ++ * (16 registers, 16 bytes each, on top of general purpose register). ++ */ ++ ++struct x86_64_va_list { ++ int gp_offset; ++ int fp_offset; ++ void *overflow_arg_area; ++ void *reg_save_area; ++}; ++ ++#define VA_LIST_DECL(_args) \ ++ va_list _args##new; \ ++ struct x86_64_va_list *_args##x; ++#define VA_LIST_PREP(_args) \ ++do { \ ++ _args##x = (struct x86_64_va_list *)&_args##new; \ ++ _args##x->gp_offset = 6 * 8; /* GP registers exhausted */ \ ++ _args##x->fp_offset = 6 * 8 + 16 * 16; /* FP registers exhausted */ \ ++ _args##x->overflow_arg_area = (void *)_args; \ ++ _args##x->reg_save_area = NULL; \ ++} while (0) ++#define VA_LIST_CONV(_args) (_args##new) ++#define VA_LIST_FREE(_args) ++#define FMT_DECL(_fmt) \ ++ char *_fmt##copy; \ ++ int _fmt##len; ++#define FMT_PREP(_fmt) \ ++do { \ ++ _fmt##len = strlen(format) + 1; \ ++ _fmt##copy = kmalloc(_fmt##len, irql_gfp()); \ ++ if (_fmt##copy) { \ ++ memcpy(_fmt##copy, format, _fmt##len); \ ++ strip_l_modifier(_fmt##copy); \ ++ } \ ++} while (0) ++#define FMT_CONV(_fmt) (_fmt##copy ? _fmt##copy : format) ++#define FMT_FREE(_fmt) kfree(_fmt##copy) ++ ++#else /* !CONFIG_X86_64 */ ++ ++#define VA_LIST_DECL(_args) ++#define VA_LIST_PREP(_args) ++#define VA_LIST_CONV(_args) (_args) ++#define VA_LIST_FREE(_args) ++#define FMT_DECL(_fmt) ++#define FMT_PREP(_fmt) ++#define FMT_CONV(_fmt) (format) ++#define FMT_FREE(_fmt) ++ ++#endif /* !CONFIG_X86_64 */ ++ ++__attribute__((format(printf, 2, 3))) ++noregparm INT WIN_FUNC(_win_sprintf,12) ++ (char *buf, const char *format, ...) ++{ ++ va_list args; ++ int res; ++ FMT_DECL(format) ++ ++ FMT_PREP(format); ++ va_start(args, format); ++ res = vsprintf(buf, FMT_CONV(format), args); ++ va_end(args); ++ FMT_FREE(format); ++ ++ TRACE2("buf: %p: %s", buf, buf); ++ return res; ++} ++ ++noregparm INT WIN_FUNC(swprintf,12) ++ (wchar_t *buf, const wchar_t *format, ...) ++{ ++ TODO(); ++ EXIT2(return 0); ++} ++ ++noregparm INT WIN_FUNC(_win_vsprintf,3) ++ (char *str, const char *format, va_list ap) ++{ ++ INT i; ++ VA_LIST_DECL(ap) ++ FMT_DECL(format) ++ ++ VA_LIST_PREP(ap); ++ FMT_PREP(format); ++ ++ i = vsprintf(str, FMT_CONV(format), VA_LIST_CONV(ap)); ++ TRACE2("str: %p: %s", str, str); ++ ++ FMT_FREE(format); ++ VA_LIST_FREE(ap); ++ EXIT2(return i); ++} ++ ++__attribute__((format(printf, 3, 4))) ++noregparm INT WIN_FUNC(_win_snprintf,12) ++ (char *buf, SIZE_T count, const char *format, ...) ++{ ++ va_list args; ++ int res; ++ FMT_DECL(format) ++ ++ FMT_PREP(format); ++ va_start(args, format); ++ res = vsnprintf(buf, count, FMT_CONV(format), args); ++ va_end(args); ++ TRACE2("buf: %p: %s", buf, buf); ++ ++ FMT_FREE(format); ++ return res; ++} ++ ++__attribute__((format(printf, 3, 4))) ++noregparm INT WIN_FUNC(_win__snprintf,12) ++ (char *buf, SIZE_T count, const char *format, ...) ++{ ++ va_list args; ++ int res; ++ FMT_DECL(format) ++ ++ FMT_PREP(format); ++ va_start(args, format); ++ res = vsnprintf(buf, count, FMT_CONV(format), args); ++ va_end(args); ++ TRACE2("buf: %p: %s", buf, buf); ++ ++ FMT_FREE(format); ++ return res; ++} ++ ++noregparm INT WIN_FUNC(_win_vsnprintf,4) ++ (char *str, SIZE_T size, const char *format, va_list ap) ++{ ++ INT i; ++ VA_LIST_DECL(ap) ++ FMT_DECL(format) ++ ++ VA_LIST_PREP(ap); ++ FMT_PREP(format); ++ ++ i = vsnprintf(str, size, FMT_CONV(format), VA_LIST_CONV(ap)); ++ TRACE2("str: %p: %s", str, str); ++ ++ FMT_FREE(format); ++ VA_LIST_FREE(ap); ++ EXIT2(return i); ++} ++ ++noregparm INT WIN_FUNC(_win__vsnprintf,4) ++ (char *str, SIZE_T size, const char *format, va_list ap) ++{ ++ INT i; ++ VA_LIST_DECL(ap) ++ FMT_DECL(format) ++ ++ VA_LIST_PREP(ap); ++ FMT_PREP(format); ++ ++ i = vsnprintf(str, size, FMT_CONV(format), VA_LIST_CONV(ap)); ++ TRACE2("str: %p: %s", str, str); ++ ++ FMT_FREE(format); ++ VA_LIST_FREE(ap); ++ EXIT2(return i); ++} ++ ++noregparm INT WIN_FUNC(_win__vsnwprintf,4) ++ (wchar_t *str, SIZE_T size, const wchar_t *format, va_list ap) ++{ ++ int ret; ++ ++ TODO(); /* format expansion not implemented */ ++ _win_wcsncpy(str, format, size); ++ ret = _win_wcslen(format); ++ if (ret >= size) ++ ret = -1; ++ return ret; ++} ++ ++noregparm char *WIN_FUNC(_win_strncpy,3) ++ (char *dst, char *src, SIZE_T n) ++{ ++ return strncpy(dst, src, n); ++} ++ ++noregparm SIZE_T WIN_FUNC(_win_strlen,1) ++ (const char *s) ++{ ++ return strlen(s); ++} ++ ++noregparm INT WIN_FUNC(_win_strncmp,3) ++ (const char *s1, const char *s2, SIZE_T n) ++{ ++ return strncmp(s1, s2, n); ++} ++ ++noregparm INT WIN_FUNC(_win_strcmp,2) ++ (const char *s1, const char *s2) ++{ ++ return strcmp(s1, s2); ++} ++ ++noregparm INT WIN_FUNC(_win_stricmp,2) ++ (const char *s1, const char *s2) ++{ ++ return stricmp(s1, s2); ++} ++ ++noregparm char *WIN_FUNC(_win_strncat,3) ++ (char *dest, const char *src, SIZE_T n) ++{ ++ return strncat(dest, src, n); ++} ++ ++noregparm INT WIN_FUNC(_win_wcscmp,2) ++ (const wchar_t *s1, const wchar_t *s2) ++{ ++ while (*s1 && *s1 == *s2) { ++ s1++; ++ s2++; ++ } ++ return *s1 - *s2; ++} ++ ++noregparm INT WIN_FUNC(_win_wcsicmp,2) ++ (const wchar_t *s1, const wchar_t *s2) ++{ ++ while (*s1 && tolower((char)*s1) == tolower((char)*s2)) { ++ s1++; ++ s2++; ++ } ++ return tolower((char)*s1) - tolower((char)*s2); ++} ++ ++noregparm SIZE_T WIN_FUNC(_win_wcslen,1) ++ (const wchar_t *s) ++{ ++ const wchar_t *t = s; ++ while (*t) ++ t++; ++ return t - s; ++} ++ ++noregparm wchar_t *WIN_FUNC(_win_wcsncpy,3) ++ (wchar_t *dest, const wchar_t *src, SIZE_T n) ++{ ++ const wchar_t *s; ++ wchar_t *d; ++ s = src + n; ++ d = dest; ++ while (src < s && (*d++ = *src++)) ++ ; ++ if (s > src) ++ memset(d, 0, (s - src) * sizeof(wchar_t)); ++ return dest; ++} ++ ++noregparm wchar_t *WIN_FUNC(_win_wcscpy,2) ++ (wchar_t *dest, const wchar_t *src) ++{ ++ wchar_t *d = dest; ++ while ((*d++ = *src++)) ++ ; ++ return dest; ++} ++ ++noregparm wchar_t *WIN_FUNC(_win_wcscat,2) ++ (wchar_t *dest, const wchar_t *src) ++{ ++ wchar_t *d; ++ d = dest; ++ while (*d) ++ d++; ++ while ((*d++ = *src++)) ++ ; ++ return dest; ++} ++ ++noregparm INT WIN_FUNC(_win_towupper,1) ++ (wchar_t c) ++{ ++ return toupper(c); ++} ++ ++noregparm INT WIN_FUNC(_win_towlower,1) ++ (wchar_t c) ++{ ++ return tolower(c); ++} ++ ++noregparm INT WIN_FUNC(_win_tolower,1) ++ (INT c) ++{ ++ return tolower(c); ++} ++ ++noregparm INT WIN_FUNC(_win_toupper,1) ++ (INT c) ++{ ++ return toupper(c); ++} ++ ++noregparm void *WIN_FUNC(_win_strcpy,2) ++ (void *to, const void *from) ++{ ++ return strcpy(to, from); ++} ++ ++noregparm char *WIN_FUNC(_win_strstr,2) ++ (const char *s1, const char *s2) ++{ ++ return strstr(s1, s2); ++} ++ ++noregparm char *WIN_FUNC(_win_strchr,2) ++ (const char *s, int c) ++{ ++ return strchr(s, c); ++} ++ ++noregparm char *WIN_FUNC(_win_strrchr,2) ++ (const char *s, int c) ++{ ++ return strrchr(s, c); ++} ++ ++noregparm void *WIN_FUNC(_win_memmove,3) ++ (void *to, void *from, SIZE_T count) ++{ ++ return memmove(to, from, count); ++} ++ ++noregparm void *WIN_FUNC(_win_memchr,3) ++ (const void *s, INT c, SIZE_T n) ++{ ++ return memchr(s, c, n); ++} ++ ++noregparm void *WIN_FUNC(_win_memcpy,3) ++ (void *to, const void *from, SIZE_T n) ++{ ++ return memcpy(to, from, n); ++} ++ ++noregparm void *WIN_FUNC(_win_memset,3) ++ (void *s, char c, SIZE_T count) ++{ ++ return memset(s, c, count); ++} ++ ++noregparm int WIN_FUNC(_win_memcmp,3) ++ (void *s1, void *s2, SIZE_T n) ++{ ++ return memcmp(s1, s2, n); ++} ++ ++noregparm void WIN_FUNC(_win_srand,1) ++ (UINT seed) ++{ ++ net_srandom(seed); ++} ++ ++noregparm int WIN_FUNC(rand,0) ++ (void) ++{ ++ char buf[6]; ++ int i, n; ++ ++ get_random_bytes(buf, sizeof(buf)); ++ for (n = i = 0; i < sizeof(buf); i++) ++ n += buf[i]; ++ return n; ++} ++ ++noregparm int WIN_FUNC(_win_atoi,1) ++ (const char *ptr) ++{ ++ int i = simple_strtol(ptr, NULL, 10); ++ return i; ++} ++ ++noregparm int WIN_FUNC(_win_isdigit,1) ++ (int c) ++{ ++ return isdigit(c); ++} ++ ++noregparm int WIN_FUNC(_win_isprint,1) ++ (int c) ++{ ++ return isprint(c); ++} ++ ++wstdcall s64 WIN_FUNC(_alldiv,2) ++ (s64 a, s64 b) ++{ ++ return a / b; ++} ++ ++wstdcall u64 WIN_FUNC(_aulldiv,2) ++ (u64 a, u64 b) ++{ ++ return a / b; ++} ++ ++wstdcall s64 WIN_FUNC(_allmul,2) ++ (s64 a, s64 b) ++{ ++ return a * b; ++} ++ ++wstdcall u64 WIN_FUNC(_aullmul,2) ++ (u64 a, u64 b) ++{ ++ return a * b; ++} ++ ++wstdcall s64 WIN_FUNC(_allrem,2) ++ (s64 a, s64 b) ++{ ++ return a % b; ++} ++ ++wstdcall u64 WIN_FUNC(_aullrem,2) ++ (u64 a, u64 b) ++{ ++ return a % b; ++} ++ ++regparm3 s64 WIN_FUNC(_allshl,2) ++ (s64 a, u8 b) ++{ ++ return a << b; ++} ++ ++regparm3 u64 WIN_FUNC(_aullshl,2) ++ (u64 a, u8 b) ++{ ++ return a << b; ++} ++ ++regparm3 s64 WIN_FUNC(_allshr,2) ++ (s64 a, u8 b) ++{ ++ return a >> b; ++} ++ ++regparm3 u64 WIN_FUNC(_aullshr,2) ++ (u64 a, u8 b) ++{ ++ return a >> b; ++} ++ ++int stricmp(const char *s1, const char *s2) ++{ ++ while (*s1 && tolower(*s1) == tolower(*s2)) { ++ s1++; ++ s2++; ++ } ++ return *s1 - *s2; ++} ++ ++void dump_bytes(const char *ctx, const u8 *from, int len) ++{ ++ int i, j; ++ u8 *buf; ++ ++ buf = kmalloc(len * 3 + 1, irql_gfp()); ++ if (!buf) { ++ ERROR("couldn't allocate memory"); ++ return; ++ } ++ for (i = j = 0; i < len; i++, j += 3) { ++ sprintf(&buf[j], "%02x ", from[i]); ++ } ++ buf[j] = 0; ++ printk(KERN_DEBUG "%s: %p: %s\n", ctx, from, buf); ++ kfree(buf); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/divdi3.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/divdi3.c +--- linux-3.12.2/3rdparty/ndiswrapper/divdi3.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/divdi3.c 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,329 @@ ++/* 64-bit multiplication and division ++ Copyright (C) 1989, 1992-1999, 2000, 2001, 2002, 2003 ++ Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#include ++#include ++ ++#if BITS_PER_LONG != 32 ++#error This is for 32-bit targets only ++#endif ++ ++typedef unsigned int UQItype __attribute__ ((mode (QI))); ++typedef int SItype __attribute__ ((mode (SI))); ++typedef unsigned int USItype __attribute__ ((mode (SI))); ++typedef int DItype __attribute__ ((mode (DI))); ++typedef unsigned int UDItype __attribute__ ((mode (DI))); ++#define Wtype SItype ++#define HWtype SItype ++#define DWtype DItype ++#define UWtype USItype ++#define UHWtype USItype ++#define UDWtype UDItype ++#define W_TYPE_SIZE 32 ++ ++#include "longlong.h" ++ ++#if defined(__BIG_ENDIAN) ++struct DWstruct { Wtype high, low;}; ++#elif defined(__LITTLE_ENDIAN) ++struct DWstruct { Wtype low, high;}; ++#else ++#error Unhandled endianity ++#endif ++typedef union { struct DWstruct s; DWtype ll; } DWunion; ++ ++/* Prototypes of exported functions. */ ++extern DWtype __divdi3 (DWtype u, DWtype v); ++extern DWtype __moddi3 (DWtype u, DWtype v); ++extern UDWtype __udivdi3 (UDWtype u, UDWtype v); ++extern UDWtype __umoddi3 (UDWtype u, UDWtype v); ++ ++static UDWtype ++__udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) ++{ ++ DWunion ww; ++ DWunion nn, dd; ++ DWunion rr; ++ UWtype d0, d1, n0, n1, n2; ++ UWtype q0, q1; ++ UWtype b, bm; ++ ++ nn.ll = n; ++ dd.ll = d; ++ ++ d0 = dd.s.low; ++ d1 = dd.s.high; ++ n0 = nn.s.low; ++ n1 = nn.s.high; ++ ++#if !UDIV_NEEDS_NORMALIZATION ++ if (d1 == 0) ++ { ++ if (d0 > n1) ++ { ++ /* 0q = nn / 0D */ ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ q1 = 0; ++ ++ /* Remainder in n0. */ ++ } ++ else ++ { ++ /* qq = NN / 0d */ ++ ++ if (d0 == 0) ++ d0 = 1 / d0; /* Divide intentionally by zero. */ ++ ++ udiv_qrnnd (q1, n1, 0, n1, d0); ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ ++ /* Remainder in n0. */ ++ } ++ ++ if (rp != NULL) ++ { ++ rr.s.low = n0; ++ rr.s.high = 0; ++ *rp = rr.ll; ++ } ++ } ++ ++#else /* UDIV_NEEDS_NORMALIZATION */ ++ ++ if (d1 == 0) ++ { ++ if (d0 > n1) ++ { ++ /* 0q = nn / 0D */ ++ ++ count_leading_zeros (bm, d0); ++ ++ if (bm != 0) ++ { ++ /* Normalize, i.e. make the most significant bit of the ++ denominator set. */ ++ ++ d0 = d0 << bm; ++ n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); ++ n0 = n0 << bm; ++ } ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ q1 = 0; ++ ++ /* Remainder in n0 >> bm. */ ++ } ++ else ++ { ++ /* qq = NN / 0d */ ++ ++ if (d0 == 0) ++ d0 = 1 / d0; /* Divide intentionally by zero. */ ++ ++ count_leading_zeros (bm, d0); ++ ++ if (bm == 0) ++ { ++ /* From (n1 >= d0) /\ (the most significant bit of d0 is set), ++ conclude (the most significant bit of n1 is set) /\ (the ++ leading quotient digit q1 = 1). ++ ++ This special case is necessary, not an optimization. ++ (Shifts counts of W_TYPE_SIZE are undefined.) */ ++ ++ n1 -= d0; ++ q1 = 1; ++ } ++ else ++ { ++ /* Normalize. */ ++ ++ b = W_TYPE_SIZE - bm; ++ ++ d0 = d0 << bm; ++ n2 = n1 >> b; ++ n1 = (n1 << bm) | (n0 >> b); ++ n0 = n0 << bm; ++ ++ udiv_qrnnd (q1, n1, n2, n1, d0); ++ } ++ ++ /* n1 != d0... */ ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ ++ /* Remainder in n0 >> bm. */ ++ } ++ ++ if (rp != NULL) ++ { ++ rr.s.low = n0 >> bm; ++ rr.s.high = 0; ++ *rp = rr.ll; ++ } ++ } ++#endif /* UDIV_NEEDS_NORMALIZATION */ ++ ++ else ++ { ++ if (d1 > n1) ++ { ++ /* 00 = nn / DD */ ++ ++ q0 = 0; ++ q1 = 0; ++ ++ /* Remainder in n1n0. */ ++ if (rp != NULL) ++ { ++ rr.s.low = n0; ++ rr.s.high = n1; ++ *rp = rr.ll; ++ } ++ } ++ else ++ { ++ /* 0q = NN / dd */ ++ ++ count_leading_zeros (bm, d1); ++ if (bm == 0) ++ { ++ /* From (n1 >= d1) /\ (the most significant bit of d1 is set), ++ conclude (the most significant bit of n1 is set) /\ (the ++ quotient digit q0 = 0 or 1). ++ ++ This special case is necessary, not an optimization. */ ++ ++ /* The condition on the next line takes advantage of that ++ n1 >= d1 (true due to program flow). */ ++ if (n1 > d1 || n0 >= d0) ++ { ++ q0 = 1; ++ sub_ddmmss (n1, n0, n1, n0, d1, d0); ++ } ++ else ++ q0 = 0; ++ ++ q1 = 0; ++ ++ if (rp != NULL) ++ { ++ rr.s.low = n0; ++ rr.s.high = n1; ++ *rp = rr.ll; ++ } ++ } ++ else ++ { ++ UWtype m1, m0; ++ /* Normalize. */ ++ ++ b = W_TYPE_SIZE - bm; ++ ++ d1 = (d1 << bm) | (d0 >> b); ++ d0 = d0 << bm; ++ n2 = n1 >> b; ++ n1 = (n1 << bm) | (n0 >> b); ++ n0 = n0 << bm; ++ ++ udiv_qrnnd (q0, n1, n2, n1, d1); ++ umul_ppmm (m1, m0, q0, d0); ++ ++ if (m1 > n1 || (m1 == n1 && m0 > n0)) ++ { ++ q0--; ++ sub_ddmmss (m1, m0, m1, m0, d1, d0); ++ } ++ ++ q1 = 0; ++ ++ /* Remainder in (n1n0 - m1m0) >> bm. */ ++ if (rp != NULL) ++ { ++ sub_ddmmss (n1, n0, n1, n0, m1, m0); ++ rr.s.low = (n1 << b) | (n0 >> bm); ++ rr.s.high = n1 >> bm; ++ *rp = rr.ll; ++ } ++ } ++ } ++ } ++ ++ ww.s.low = q0; ++ ww.s.high = q1; ++ return ww.ll; ++} ++ ++DWtype ++__divdi3 (DWtype u, DWtype v) ++{ ++ Wtype c = 0; ++ DWtype w; ++ ++ if (u < 0) ++ { ++ c = ~c; ++ u = -u; ++ } ++ if (v < 0) ++ { ++ c = ~c; ++ v = -v; ++ } ++ w = __udivmoddi4 (u, v, NULL); ++ if (c) ++ w = -w; ++ return w; ++} ++ ++DWtype ++__moddi3 (DWtype u, DWtype v) ++{ ++ Wtype c = 0; ++ DWtype w; ++ ++ if (u < 0) ++ { ++ c = ~c; ++ u = -u; ++ } ++ if (v < 0) ++ v = -v; ++ __udivmoddi4 (u, v, &w); ++ if (c) ++ w = -w; ++ return w; ++} ++ ++UDWtype ++__udivdi3 (UDWtype u, UDWtype v) ++{ ++ return __udivmoddi4 (u, v, NULL); ++} ++ ++UDWtype ++__umoddi3 (UDWtype u, UDWtype v) ++{ ++ UDWtype w; ++ ++ __udivmoddi4 (u, v, &w); ++ return w; ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/hal.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/hal.c +--- linux-3.12.2/3rdparty/ndiswrapper/hal.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/hal.c 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,157 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ntoskernel.h" ++#include "hal_exports.h" ++ ++wstdcall void WIN_FUNC(WRITE_PORT_ULONG,2) ++ (ULONG_PTR port, ULONG value) ++{ ++ outl(value, port); ++} ++ ++wstdcall ULONG WIN_FUNC(READ_PORT_ULONG,1) ++ (ULONG_PTR port) ++{ ++ return inl(port); ++} ++ ++wstdcall void WIN_FUNC(WRITE_PORT_USHORT,2) ++ (ULONG_PTR port, USHORT value) ++{ ++ outw(value, port); ++} ++ ++wstdcall USHORT WIN_FUNC(READ_PORT_USHORT,1) ++ (ULONG_PTR port) ++{ ++ return inw(port); ++} ++ ++wstdcall void WIN_FUNC(WRITE_PORT_UCHAR,2) ++ (ULONG_PTR port, UCHAR value) ++{ ++ outb(value, port); ++} ++ ++wstdcall UCHAR WIN_FUNC(READ_PORT_UCHAR,1) ++ (ULONG_PTR port) ++{ ++ return inb(port); ++} ++ ++wstdcall void WIN_FUNC(WRITE_PORT_BUFFER_USHORT,3) ++ (ULONG_PTR port, USHORT *buf, ULONG count) ++{ ++ outsw(port, buf, count); ++} ++ ++wstdcall void WIN_FUNC(READ_PORT_BUFFER_USHORT,3) ++ (ULONG_PTR port, USHORT *buf, ULONG count) ++{ ++ insw(port, buf, count); ++} ++ ++wstdcall void WIN_FUNC(WRITE_PORT_BUFFER_ULONG,3) ++ (ULONG_PTR port, ULONG *buf, ULONG count) ++{ ++ outsl(port, buf, count); ++} ++ ++wstdcall void WIN_FUNC(READ_PORT_BUFFER_ULONG,3) ++ (ULONG_PTR port, ULONG *buf, ULONG count) ++{ ++ insl(port, buf, count); ++} ++ ++wstdcall USHORT WIN_FUNC(READ_REGISTER_USHORT,1) ++ (void __iomem *reg) ++{ ++ return readw(reg); ++} ++ ++wstdcall void WIN_FUNC(WRITE_REGISTER_ULONG,2) ++ (void __iomem *reg, UINT val) ++{ ++ writel(val, reg); ++} ++ ++wstdcall void WIN_FUNC(WRITE_REGISTER_USHORT,2) ++ (void __iomem *reg, USHORT val) ++{ ++ writew(val, reg); ++} ++ ++wstdcall void WIN_FUNC(WRITE_REGISTER_UCHAR,2) ++ (void __iomem *reg, UCHAR val) ++{ ++ writeb(val, reg); ++} ++ ++wstdcall void WIN_FUNC(KeStallExecutionProcessor,1) ++ (ULONG usecs) ++{ ++ udelay(usecs); ++} ++ ++wstdcall KIRQL WIN_FUNC(KeGetCurrentIrql,0) ++ (void) ++{ ++ return current_irql(); ++} ++ ++wfastcall KIRQL WIN_FUNC(KfRaiseIrql,1) ++ (KIRQL newirql) ++{ ++ return raise_irql(newirql); ++} ++ ++wfastcall void WIN_FUNC(KfLowerIrql,1) ++ (KIRQL oldirql) ++{ ++ lower_irql(oldirql); ++} ++ ++wfastcall KIRQL WIN_FUNC(KfAcquireSpinLock,1) ++ (NT_SPIN_LOCK *lock) ++{ ++ return nt_spin_lock_irql(lock, DISPATCH_LEVEL); ++} ++ ++wfastcall void WIN_FUNC(KfReleaseSpinLock,2) ++ (NT_SPIN_LOCK *lock, KIRQL oldirql) ++{ ++ nt_spin_unlock_irql(lock, oldirql); ++} ++ ++wfastcall void WIN_FUNC(KefAcquireSpinLockAtDpcLevel,1) ++ (NT_SPIN_LOCK *lock) ++{ ++#ifdef DEBUG_IRQL ++ if (current_irql() != DISPATCH_LEVEL) ++ ERROR("irql != DISPATCH_LEVEL"); ++#endif ++ nt_spin_lock(lock); ++} ++ ++wfastcall void WIN_FUNC(KefReleaseSpinLockFromDpcLevel,1) ++ (NT_SPIN_LOCK *lock) ++{ ++#ifdef DEBUG_IRQL ++ if (current_irql() != DISPATCH_LEVEL) ++ ERROR("irql != DISPATCH_LEVEL"); ++#endif ++ nt_spin_unlock(lock); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.c +--- linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.c 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,2001 @@ ++ /* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "iw_ndis.h" ++#include "wrapndis.h" ++ ++#ifdef CONFIG_WIRELESS_EXT ++ ++static int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, ++ 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; ++ ++static const char *network_names[] = {"IEEE 802.11FH", "IEEE 802.11b", ++ "IEEE 802.11a", "IEEE 802.11g", "Auto"}; ++ ++static int set_essid(struct ndis_device *wnd, const char *ssid, int ssid_len) ++{ ++ NDIS_STATUS res; ++ struct ndis_essid req; ++ ++ if (ssid_len > NDIS_ESSID_MAX_SIZE) ++ return -EINVAL; ++ ++ memset(&req, 0, sizeof(req)); ++ req.length = ssid_len; ++ if (ssid_len) ++ memcpy(&req.essid, ssid, ssid_len); ++ ++ res = mp_set(wnd, OID_802_11_SSID, &req, sizeof(req)); ++ if (res) { ++ WARNING("setting essid failed (%08X)", res); ++ EXIT2(return -EINVAL); ++ } ++ memcpy(&wnd->essid, &req, sizeof(req)); ++ EXIT2(return 0); ++} ++ ++static int set_iw_auth_mode(struct ndis_device *wnd, int wpa_version, ++ int auth_80211_alg) ++{ ++ NDIS_STATUS res; ++ ULONG auth_mode; ++ ++ ENTER2("%d, %d", wpa_version, auth_80211_alg); ++ if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) { ++ if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X) ++ auth_mode = Ndis802_11AuthModeWPA2; ++ else ++ auth_mode = Ndis802_11AuthModeWPA2PSK; ++ } else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) { ++ if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X) ++ auth_mode = Ndis802_11AuthModeWPA; ++ else if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_PSK) ++ auth_mode = Ndis802_11AuthModeWPAPSK; ++ else ++ auth_mode = Ndis802_11AuthModeWPANone; ++ } else if (auth_80211_alg & IW_AUTH_ALG_SHARED_KEY) { ++ if (auth_80211_alg & IW_AUTH_ALG_OPEN_SYSTEM) ++ auth_mode = Ndis802_11AuthModeAutoSwitch; ++ else ++ auth_mode = Ndis802_11AuthModeShared; ++ } else ++ auth_mode = Ndis802_11AuthModeOpen; ++ ++ res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode); ++ if (res) { ++ WARNING("setting auth mode to %u failed (%08X)", ++ auth_mode, res); ++ if (res == NDIS_STATUS_INVALID_DATA) ++ EXIT2(return -EINVAL); ++ return -EOPNOTSUPP; ++ } ++ wnd->iw_auth_wpa_version = wpa_version; ++ wnd->iw_auth_80211_alg = auth_80211_alg; ++ EXIT2(return 0); ++} ++ ++static int set_auth_mode(struct ndis_device *wnd) ++{ ++ return set_iw_auth_mode(wnd, wnd->iw_auth_wpa_version, ++ wnd->iw_auth_80211_alg); ++} ++ ++static enum ndis_priv_filter ndis_priv_mode(struct ndis_device *wnd) ++{ ++ if (wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2 || ++ wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA) ++ return Ndis802_11PrivFilter8021xWEP; ++ else ++ return Ndis802_11PrivFilterAcceptAll; ++} ++ ++static int set_priv_filter(struct ndis_device *wnd) ++{ ++ NDIS_STATUS res; ++ ULONG flags; ++ ++ flags = ndis_priv_mode(wnd); ++ ENTER2("filter: %d", flags); ++ res = mp_set_int(wnd, OID_802_11_PRIVACY_FILTER, flags); ++ if (res) ++ TRACE2("setting privacy filter to %d failed (%08X)", ++ flags, res); ++ EXIT2(return 0); ++} ++ ++static int set_encr_mode(struct ndis_device *wnd) ++{ ++ return set_iw_encr_mode(wnd, wnd->iw_auth_cipher_pairwise, ++ wnd->iw_auth_cipher_group); ++} ++ ++static int set_assoc_params(struct ndis_device *wnd) ++{ ++ TRACE2("wpa_version=0x%x auth_alg=0x%x key_mgmt=0x%x " ++ "cipher_pairwise=0x%x cipher_group=0x%x", ++ wnd->iw_auth_wpa_version, wnd->iw_auth_80211_alg, ++ wnd->iw_auth_key_mgmt, wnd->iw_auth_cipher_pairwise, ++ wnd->iw_auth_cipher_group); ++ set_auth_mode(wnd); ++ set_priv_filter(wnd); ++ set_encr_mode(wnd); ++ return 0; ++} ++ ++static int iw_set_essid(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ int length; ++ ++ ENTER2(""); ++ /* there is no way to turn off essid other than to set to ++ * random bytes; instead, we use off to mean any */ ++ if (wrqu->essid.flags) { ++ length = wrqu->essid.length; ++ /* Strip '\0' appended by wireless extensions 19 and older */ ++ if (length > 0 && extra[length - 1] == '\0') ++ length--; ++ TRACE2("%d", length); ++ if (length <= 0 || length > NDIS_ESSID_MAX_SIZE) ++ EXIT2(return -EINVAL); ++ } else ++ length = 0; ++ ++ set_assoc_params(wnd); ++ ++ if (set_essid(wnd, extra, length)) ++ EXIT2(return -EINVAL); ++ ++ EXIT2(return 0); ++} ++ ++static int iw_get_essid(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ struct ndis_essid req; ++ ++ ENTER2(""); ++ memset(&req, 0, sizeof(req)); ++ res = mp_query(wnd, OID_802_11_SSID, &req, sizeof(req)); ++ if (res) { ++ WARNING("getting essid failed (%08X)", res); ++ EXIT2(return -EOPNOTSUPP); ++ } ++ memcpy(extra, req.essid, req.length); ++ if (req.length > 0) ++ wrqu->essid.flags = 1; ++ else ++ wrqu->essid.flags = 0; ++ wrqu->essid.length = req.length; ++ EXIT2(return 0); ++} ++ ++/* index must be 0 - N, as per NDIS */ ++static int add_wep_key(struct ndis_device *wnd, char *key, int key_len, ++ int index) ++{ ++ struct ndis_encr_key ndis_key; ++ NDIS_STATUS res; ++ ++ ENTER2("key index: %d, length: %d", index, key_len); ++ if (key_len <= 0 || key_len > NDIS_ENCODING_TOKEN_MAX) { ++ WARNING("invalid key length (%d)", key_len); ++ EXIT2(return -EINVAL); ++ } ++ if (index < 0 || index >= MAX_ENCR_KEYS) { ++ WARNING("invalid key index (%d)", index); ++ EXIT2(return -EINVAL); ++ } ++ ndis_key.struct_size = sizeof(ndis_key); ++ ndis_key.length = key_len; ++ memcpy(&ndis_key.key, key, key_len); ++ ndis_key.index = index; ++ ++ if (index == wnd->encr_info.tx_key_index) { ++ ndis_key.index |= (1 << 31); ++ res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104, ++ IW_AUTH_CIPHER_NONE); ++ if (res) ++ WARNING("encryption couldn't be enabled (%08X)", res); ++ } ++ TRACE2("key %d: " MACSTRSEP, index, MAC2STR(key)); ++ res = mp_set(wnd, OID_802_11_ADD_WEP, &ndis_key, sizeof(ndis_key)); ++ if (res) { ++ WARNING("adding encryption key %d failed (%08X)", ++ index+1, res); ++ EXIT2(return -EINVAL); ++ } ++ ++ /* Atheros driver messes up ndis_key during ADD_WEP, so ++ * don't rely on that; instead use info in key and key_len */ ++ wnd->encr_info.keys[index].length = key_len; ++ memcpy(&wnd->encr_info.keys[index].key, key, key_len); ++ ++ EXIT2(return 0); ++} ++ ++static int set_infra_mode(struct ndis_device *wnd, ++ enum ndis_infrastructure_mode mode) ++{ ++ NDIS_STATUS res; ++ unsigned int i; ++ ++ ENTER2("%d", mode); ++ res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, ++ &wnd->infrastructure_mode); ++ if (res != NDIS_STATUS_SUCCESS) { ++ WARNING("getting operating mode failed (%08X)", res); ++ EXIT2(return -EINVAL); ++ } ++ if (wnd->infrastructure_mode == mode) ++ EXIT2(return 0); ++ res = mp_set_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, mode); ++ if (res) { ++ WARNING("setting operating mode to %d failed (%08X)", ++ mode, res); ++ EXIT2(return -EINVAL); ++ } ++ /* NDIS drivers clear keys when infrastructure mode is ++ * changed. But Linux tools assume otherwise. So set the ++ * keys */ ++ if (wnd->iw_auth_key_mgmt == 0 || ++ wnd->iw_auth_key_mgmt == IW_AUTH_KEY_MGMT_802_1X) { ++ for (i = 0; i < MAX_ENCR_KEYS; i++) { ++ if (wnd->encr_info.keys[i].length > 0) ++ add_wep_key(wnd, wnd->encr_info.keys[i].key, ++ wnd->encr_info.keys[i].length, i); ++ } ++ } ++ wnd->infrastructure_mode = mode; ++ EXIT2(return 0); ++} ++ ++static int iw_set_infra_mode(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ enum ndis_infrastructure_mode ndis_mode; ++ ++ ENTER2("%d", wrqu->mode); ++ switch (wrqu->mode) { ++ case IW_MODE_ADHOC: ++ ndis_mode = Ndis802_11IBSS; ++ break; ++ case IW_MODE_INFRA: ++ ndis_mode = Ndis802_11Infrastructure; ++ break; ++ case IW_MODE_AUTO: ++ ndis_mode = Ndis802_11AutoUnknown; ++ break; ++ default: ++ EXIT2(return -EINVAL); ++ } ++ ++ if (set_infra_mode(wnd, ndis_mode)) ++ EXIT2(return -EINVAL); ++ ++ EXIT2(return 0); ++} ++ ++static int iw_get_infra_mode(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ int ndis_mode, iw_mode; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &ndis_mode); ++ if (res) { ++ WARNING("getting operating mode failed (%08X)", res); ++ EXIT2(return -EOPNOTSUPP); ++ } ++ ++ switch (ndis_mode) { ++ case Ndis802_11IBSS: ++ iw_mode = IW_MODE_ADHOC; ++ break; ++ case Ndis802_11Infrastructure: ++ iw_mode = IW_MODE_INFRA; ++ break; ++ case Ndis802_11AutoUnknown: ++ iw_mode = IW_MODE_AUTO; ++ break; ++ default: ++ ERROR("invalid operating mode (%u)", ndis_mode); ++ EXIT2(return -EINVAL); ++ } ++ wrqu->mode = iw_mode; ++ EXIT2(return 0); ++} ++ ++static const char *network_type_to_name(int net_type) ++{ ++ if (net_type >= 0 && net_type < ARRAY_SIZE(network_names)) ++ return network_names[net_type]; ++ else ++ return network_names[ARRAY_SIZE(network_names) - 1]; ++} ++ ++static int iw_get_network_type(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ unsigned int network_type; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ res = mp_query_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, ++ &network_type); ++ if (res) { ++ WARNING("getting network type failed: %08X", res); ++ network_type = -1; ++ } ++ strncpy(wrqu->name, network_type_to_name(network_type), ++ sizeof(wrqu->name) - 1); ++ wrqu->name[sizeof(wrqu->name)-1] = 0; ++ return 0; ++} ++ ++static int iw_get_freq(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ struct ndis_configuration req; ++ ++ ENTER2(""); ++ memset(&req, 0, sizeof(req)); ++ res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); ++ if (res) { ++ WARNING("getting configuration failed (%08X)", res); ++ EXIT2(return -EOPNOTSUPP); ++ } ++ ++ memset(&(wrqu->freq), 0, sizeof(struct iw_freq)); ++ ++ /* see comment in wireless.h above the "struct iw_freq" ++ definition for an explanation of this if ++ NOTE: 1000000 is due to the kHz ++ */ ++ if (req.ds_config > 1000000) { ++ wrqu->freq.m = req.ds_config / 10; ++ wrqu->freq.e = 1; ++ } ++ else ++ wrqu->freq.m = req.ds_config; ++ ++ /* convert from kHz to Hz */ ++ wrqu->freq.e += 3; ++ ++ return 0; ++} ++ ++static int iw_set_freq(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ struct ndis_configuration req; ++ ++ ENTER2(""); ++ /* this OID is valid only when not associated */ ++ if (netif_carrier_ok(wnd->net_dev)) ++ EXIT2(return 0); ++ memset(&req, 0, sizeof(req)); ++ res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); ++ if (res) { ++ WARNING("getting configuration failed (%08X)", res); ++ EXIT2(return 0); ++ } ++ ++ if (wrqu->freq.m < 1000 && wrqu->freq.e == 0) { ++ if (wrqu->freq.m >= 1 && wrqu->freq.m <= ARRAY_SIZE(freq_chan)) ++ req.ds_config = freq_chan[wrqu->freq.m - 1] * 1000; ++ else ++ return -EINVAL; ++ } else { ++ int i; ++ req.ds_config = wrqu->freq.m; ++ for (i = wrqu->freq.e; i > 0; i--) ++ req.ds_config *= 10; ++ req.ds_config /= 1000; ++ } ++ res = mp_set(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); ++ if (res) ++ WARNING("setting configuration failed (%08X)", res); ++ return 0; ++} ++ ++static int iw_get_tx_power(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ndis_tx_power_level ndis_power; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, ++ &ndis_power, sizeof(ndis_power)); ++ if (res) ++ return -EOPNOTSUPP; ++ wrqu->txpower.flags = IW_TXPOW_MWATT; ++ wrqu->txpower.disabled = 0; ++ wrqu->txpower.fixed = 0; ++ wrqu->txpower.value = ndis_power; ++ return 0; ++} ++ ++static int iw_set_tx_power(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ndis_tx_power_level ndis_power; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ if (wrqu->txpower.disabled) ++ ndis_power = 0; ++ else { ++ if (wrqu->txpower.flags == IW_TXPOW_MWATT) ++ ndis_power = wrqu->txpower.value; ++ else { // wrqu->txpower.flags == IW_TXPOW_DBM ++ if (wrqu->txpower.value > 20) ++ ndis_power = 128; ++ else if (wrqu->txpower.value < -43) ++ ndis_power = 127; ++ else { ++ signed char tmp; ++ tmp = wrqu->txpower.value; ++ tmp = -12 - tmp; ++ tmp <<= 2; ++ ndis_power = (unsigned char)tmp; ++ } ++ } ++ } ++ TRACE2("%d", ndis_power); ++ res = mp_set(wnd, OID_802_11_TX_POWER_LEVEL, ++ &ndis_power, sizeof(ndis_power)); ++ if (res) ++ EXIT2(return -EOPNOTSUPP); ++ if (ndis_power == 0) ++ res = disassociate(wnd, 0); ++ EXIT2(return 0); ++} ++ ++static int iw_get_bitrate(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ULONG ndis_rate; ++ int res; ++ ++ ENTER2(""); ++ res = mp_query(wnd, OID_GEN_LINK_SPEED, &ndis_rate, sizeof(ndis_rate)); ++ if (res) { ++ WARNING("getting bitrate failed (%08X)", res); ++ ndis_rate = 0; ++ } ++ ++ wrqu->bitrate.value = ndis_rate * 100; ++ return 0; ++} ++ ++static int iw_set_bitrate(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ int i, n; ++ NDIS_STATUS res; ++ UCHAR rates[NDIS_MAX_RATES_EX]; ++ ++ ENTER2(""); ++ if (wrqu->bitrate.fixed == 0) ++ EXIT2(return 0); ++ ++ res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES, &rates, ++ sizeof(rates), &n, NULL); ++ if (res) { ++ WARNING("getting bit rate failed (%08X)", res); ++ EXIT2(return 0); ++ } ++ for (i = 0; i < n; i++) { ++ if (rates[i] & 0x80) ++ continue; ++ if ((rates[i] & 0x7f) * 500000 > wrqu->bitrate.value) { ++ TRACE2("setting rate %d to 0", ++ (rates[i] & 0x7f) * 500000); ++ rates[i] = 0; ++ } ++ } ++ ++ res = mp_set(wnd, OID_802_11_DESIRED_RATES, &rates, n); ++ if (res) { ++ WARNING("setting bit rate failed (%08X)", res); ++ EXIT2(return 0); ++ } ++ ++ return 0; ++} ++ ++static int iw_set_dummy(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ /* Do nothing. Used for ioctls that are not implemented. */ ++ return 0; ++} ++ ++static int iw_get_rts_threshold(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ndis_rts_threshold threshold; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ res = mp_query(wnd, OID_802_11_RTS_THRESHOLD, ++ &threshold, sizeof(threshold)); ++ if (res) ++ return -EOPNOTSUPP; ++ ++ wrqu->rts.value = threshold; ++ return 0; ++} ++ ++static int iw_set_rts_threshold(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ndis_rts_threshold threshold; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ threshold = wrqu->rts.value; ++ res = mp_set(wnd, OID_802_11_RTS_THRESHOLD, ++ &threshold, sizeof(threshold)); ++ if (res == NDIS_STATUS_INVALID_DATA) ++ return -EINVAL; ++ if (res) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static int iw_get_frag_threshold(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ndis_fragmentation_threshold frag_threshold; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, ++ &frag_threshold, sizeof(frag_threshold)); ++ if (res) ++ return -ENOTSUPP; ++ ++ wrqu->frag.value = frag_threshold; ++ return 0; ++} ++ ++static int iw_set_frag_threshold(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ndis_rts_threshold threshold; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ threshold = wrqu->frag.value; ++ res = mp_set(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, ++ &threshold, sizeof(threshold)); ++ if (res == NDIS_STATUS_INVALID_DATA) ++ return -EINVAL; ++ if (res) ++ return -EOPNOTSUPP; ++ return 0; ++} ++ ++int get_ap_address(struct ndis_device *wnd, mac_address ap_addr) ++{ ++ NDIS_STATUS res; ++ ++ res = mp_query(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN); ++ TRACE2(MACSTRSEP, MAC2STR(ap_addr)); ++ if (res) { ++ TRACE2("res: %08X", res); ++ memset(ap_addr, 0x0, ETH_ALEN); ++ EXIT2(return -EOPNOTSUPP); ++ } ++ EXIT2(return 0); ++} ++ ++static int iw_get_ap_address(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ mac_address ap_addr; ++ ++ ENTER2(""); ++ get_ap_address(wnd, ap_addr); ++ memcpy(wrqu->ap_addr.sa_data, ap_addr, ETH_ALEN); ++ wrqu->ap_addr.sa_family = ARPHRD_ETHER; ++ EXIT2(return 0); ++} ++ ++static int iw_set_ap_address(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ mac_address ap_addr; ++ ++ ENTER2(""); ++ memcpy(ap_addr, wrqu->ap_addr.sa_data, ETH_ALEN); ++ TRACE2(MACSTRSEP, MAC2STR(ap_addr)); ++ res = mp_set(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN); ++ /* user apps may set ap's mac address, which is not required; ++ * they may fail to work if this function fails, so return ++ * success */ ++ if (res) ++ WARNING("setting AP mac address failed (%08X)", res); ++ ++ EXIT2(return 0); ++} ++ ++int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode) ++{ ++ NDIS_STATUS res; ++ ++ ENTER2("%d", auth_mode); ++ res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode); ++ if (res) { ++ WARNING("setting auth mode to %u failed (%08X)", ++ auth_mode, res); ++ if (res == NDIS_STATUS_INVALID_DATA) ++ EXIT2(return -EINVAL); ++ return -EOPNOTSUPP; ++ } ++ switch (auth_mode) { ++ case Ndis802_11AuthModeWPA: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA; ++ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X; ++ break; ++ case Ndis802_11AuthModeWPAPSK: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA; ++ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; ++ case Ndis802_11AuthModeWPANone: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; ++ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; ++ break; ++ case Ndis802_11AuthModeWPA2: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2; ++ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X; ++ break; ++ case Ndis802_11AuthModeWPA2PSK: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2; ++ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; ++ break; ++ case Ndis802_11AuthModeOpen: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; ++ wnd->iw_auth_80211_alg = IW_AUTH_ALG_OPEN_SYSTEM; ++ break; ++ case Ndis802_11AuthModeShared: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; ++ wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY; ++ break; ++ case Ndis802_11AuthModeAutoSwitch: ++ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; ++ wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY; ++ wnd->iw_auth_80211_alg |= IW_AUTH_ALG_OPEN_SYSTEM; ++ break; ++ default: ++ WARNING("invalid authentication algorithm: %d", auth_mode); ++ break; ++ } ++ EXIT2(return 0); ++} ++ ++int get_ndis_auth_mode(struct ndis_device *wnd) ++{ ++ ULONG mode; ++ NDIS_STATUS res; ++ ++ res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &mode); ++ if (res) { ++ WARNING("getting authentication mode failed (%08X)", res); ++ EXIT2(return -EOPNOTSUPP); ++ } ++ TRACE2("%d", mode); ++ return mode; ++} ++ ++int set_iw_encr_mode(struct ndis_device *wnd, int cipher_pairwise, ++ int cipher_groupwise) ++{ ++ NDIS_STATUS res; ++ ULONG ndis_mode; ++ ++ ENTER2("%d, %d", cipher_pairwise, cipher_groupwise); ++ if (cipher_pairwise & IW_AUTH_CIPHER_CCMP) ++ ndis_mode = Ndis802_11Encryption3Enabled; ++ else if (cipher_pairwise & IW_AUTH_CIPHER_TKIP) ++ ndis_mode = Ndis802_11Encryption2Enabled; ++ else if (cipher_pairwise & ++ (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) ++ ndis_mode = Ndis802_11Encryption1Enabled; ++ else if (cipher_groupwise & IW_AUTH_CIPHER_CCMP) ++ ndis_mode = Ndis802_11Encryption3Enabled; ++ else if (cipher_groupwise & IW_AUTH_CIPHER_TKIP) ++ ndis_mode = Ndis802_11Encryption2Enabled; ++ else ++ ndis_mode = Ndis802_11EncryptionDisabled; ++ ++ res = mp_set_int(wnd, OID_802_11_ENCRYPTION_STATUS, ndis_mode); ++ if (res) { ++ WARNING("setting encryption mode to %u failed (%08X)", ++ ndis_mode, res); ++ if (res == NDIS_STATUS_INVALID_DATA) ++ EXIT2(return -EINVAL); ++ return -EOPNOTSUPP; ++ } ++ wnd->iw_auth_cipher_pairwise = cipher_pairwise; ++ wnd->iw_auth_cipher_group = cipher_groupwise; ++ EXIT2(return 0); ++} ++ ++int get_ndis_encr_mode(struct ndis_device *wnd) ++{ ++ ULONG mode; ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &mode); ++ if (res) { ++ WARNING("getting encryption status failed (%08X)", res); ++ EXIT2(return -EOPNOTSUPP); ++ } else ++ EXIT2(return mode); ++} ++ ++static int iw_get_encr(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ int index, mode; ++ struct encr_info *encr_info = &wnd->encr_info; ++ ++ ENTER2("wnd = %p", wnd); ++ wrqu->data.length = 0; ++ extra[0] = 0; ++ ++ index = (wrqu->encoding.flags & IW_ENCODE_INDEX); ++ TRACE2("index = %u", index); ++ if (index > 0) ++ index--; ++ else ++ index = encr_info->tx_key_index; ++ ++ if (index < 0 || index >= MAX_ENCR_KEYS) { ++ WARNING("encryption index out of range (%u)", index); ++ EXIT2(return -EINVAL); ++ } ++ ++ if (index != encr_info->tx_key_index) { ++ if (encr_info->keys[index].length > 0) { ++ wrqu->data.flags |= IW_ENCODE_ENABLED; ++ wrqu->data.length = encr_info->keys[index].length; ++ memcpy(extra, encr_info->keys[index].key, ++ encr_info->keys[index].length); ++ } ++ else ++ wrqu->data.flags |= IW_ENCODE_DISABLED; ++ ++ EXIT2(return 0); ++ } ++ ++ /* transmit key */ ++ mode = get_ndis_encr_mode(wnd); ++ if (mode < 0) ++ EXIT2(return -EOPNOTSUPP); ++ ++ if (mode == Ndis802_11EncryptionDisabled || ++ mode == Ndis802_11EncryptionNotSupported) ++ wrqu->data.flags |= IW_ENCODE_DISABLED; ++ else { ++ if (mode == Ndis802_11Encryption1KeyAbsent || ++ mode == Ndis802_11Encryption2KeyAbsent || ++ mode == Ndis802_11Encryption3KeyAbsent) ++ wrqu->data.flags |= IW_ENCODE_NOKEY; ++ else { ++ wrqu->data.flags |= IW_ENCODE_ENABLED; ++ wrqu->encoding.flags |= index+1; ++ wrqu->data.length = encr_info->keys[index].length; ++ memcpy(extra, encr_info->keys[index].key, ++ encr_info->keys[index].length); ++ } ++ } ++ mode = get_ndis_auth_mode(wnd); ++ if (mode < 0) ++ EXIT2(return -EOPNOTSUPP); ++ ++ if (mode == Ndis802_11AuthModeOpen) ++ wrqu->data.flags |= IW_ENCODE_OPEN; ++ else if (mode == Ndis802_11AuthModeAutoSwitch) ++ wrqu->data.flags |= IW_ENCODE_RESTRICTED; ++ else // Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA etc. ++ wrqu->data.flags |= IW_ENCODE_RESTRICTED; ++ ++ EXIT2(return 0); ++} ++ ++/* remove_key is for both wep and wpa */ ++static int remove_key(struct ndis_device *wnd, int index, ++ mac_address bssid) ++{ ++ NDIS_STATUS res; ++ if (wnd->encr_info.keys[index].length == 0) ++ EXIT2(return 0); ++ wnd->encr_info.keys[index].length = 0; ++ memset(&wnd->encr_info.keys[index].key, 0, ++ sizeof(wnd->encr_info.keys[index].length)); ++ if (wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_TKIP || ++ wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_CCMP || ++ wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_TKIP || ++ wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_CCMP) { ++ struct ndis_remove_key rmkey; ++ rmkey.struct_size = sizeof(rmkey); ++ rmkey.index = index; ++ if (bssid) { ++ /* pairwise key */ ++ if (memcmp(bssid, "\xff\xff\xff\xff\xff\xff", ++ ETH_ALEN) != 0) ++ rmkey.index |= (1 << 30); ++ memcpy(rmkey.bssid, bssid, sizeof(rmkey.bssid)); ++ } else ++ memset(rmkey.bssid, 0xff, sizeof(rmkey.bssid)); ++ if (mp_set(wnd, OID_802_11_REMOVE_KEY, &rmkey, sizeof(rmkey))) ++ EXIT2(return -EINVAL); ++ } else { ++ ndis_key_index keyindex = index; ++ res = mp_set_int(wnd, OID_802_11_REMOVE_WEP, keyindex); ++ if (res) { ++ WARNING("removing encryption key %d failed (%08X)", ++ keyindex, res); ++ EXIT2(return -EINVAL); ++ } ++ } ++ /* if it is transmit key, disable encryption */ ++ if (index == wnd->encr_info.tx_key_index) { ++ res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE, ++ IW_AUTH_CIPHER_NONE); ++ if (res) ++ WARNING("changing encr status failed (%08X)", res); ++ } ++ TRACE2("key %d removed", index); ++ EXIT2(return 0); ++} ++ ++static int iw_set_wep(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ unsigned int index, key_len; ++ struct encr_info *encr_info = &wnd->encr_info; ++ unsigned char *key; ++ ++ ENTER2(""); ++ index = (wrqu->encoding.flags & IW_ENCODE_INDEX); ++ TRACE2("index = %u", index); ++ ++ /* iwconfig gives index as 1 - N */ ++ if (index > 0) ++ index--; ++ else ++ index = encr_info->tx_key_index; ++ ++ if (index >= MAX_ENCR_KEYS) { ++ WARNING("encryption index out of range (%u)", index); ++ EXIT2(return -EINVAL); ++ } ++ ++ /* remove key if disabled */ ++ if (wrqu->data.flags & IW_ENCODE_DISABLED) { ++ if (remove_key(wnd, index, NULL)) ++ EXIT2(return -EINVAL); ++ else ++ EXIT2(return 0); ++ } ++ ++ /* global encryption state (for all keys) */ ++ if (wrqu->data.flags & IW_ENCODE_OPEN) ++ res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen); ++ else // if (wrqu->data.flags & IW_ENCODE_RESTRICTED) ++ res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeShared); ++ if (res) { ++ WARNING("setting authentication mode failed (%08X)", res); ++ EXIT2(return -EINVAL); ++ } ++ ++ TRACE2("key length: %d", wrqu->data.length); ++ ++ if (wrqu->data.length > 0) { ++ key_len = wrqu->data.length; ++ key = extra; ++ } else { // must be set as tx key ++ if (encr_info->keys[index].length == 0) { ++ WARNING("key %d is not set", index+1); ++ EXIT2(return -EINVAL); ++ } ++ key_len = encr_info->keys[index].length; ++ key = encr_info->keys[index].key; ++ encr_info->tx_key_index = index; ++ } ++ ++ if (add_wep_key(wnd, key, key_len, index)) ++ EXIT2(return -EINVAL); ++ ++ if (index == encr_info->tx_key_index) { ++ /* if transmit key is at index other than 0, some ++ * drivers, at least Atheros and TI, want another ++ * (global) non-transmit key to be set; don't know why */ ++ if (index != 0) { ++ int i; ++ for (i = 0; i < MAX_ENCR_KEYS; i++) ++ if (i != index && ++ encr_info->keys[i].length != 0) ++ break; ++ if (i == MAX_ENCR_KEYS) { ++ if (index == 0) ++ i = index + 1; ++ else ++ i = index - 1; ++ if (add_wep_key(wnd, key, key_len, i)) ++ WARNING("couldn't add broadcast key" ++ " at %d", i); ++ } ++ } ++ /* ndis drivers want essid to be set after setting encr */ ++ set_essid(wnd, wnd->essid.essid, wnd->essid.length); ++ } ++ EXIT2(return 0); ++} ++ ++static int iw_set_nick(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ if (wrqu->data.length >= sizeof(wnd->nick)) ++ return -EINVAL; ++ memcpy(wnd->nick, extra, wrqu->data.length); ++ wnd->nick[wrqu->data.length] = 0; ++ return 0; ++} ++ ++static int iw_get_nick(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ wrqu->data.length = strlen(wnd->nick); ++ memcpy(extra, wnd->nick, wrqu->data.length); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) && !defined(IW_REQUEST_FLAG_COMPAT) ++#define iwe_stream_add_event(a, b, c, d, e) iwe_stream_add_event(b, c, d, e) ++#define iwe_stream_add_point(a, b, c, d, e) iwe_stream_add_point(b, c, d, e) ++#define iwe_stream_add_value(a, b, c, d, e, f) \ ++ iwe_stream_add_value(b, c, d, e, f) ++#define iwe_stream_lcp_len(a) IW_EV_LCP_LEN ++#endif ++ ++static char *ndis_translate_scan(struct net_device *dev, ++ struct iw_request_info *info, char *event, ++ char *end_buf, void *item) ++{ ++ struct iw_event iwe; ++ char *current_val; ++ char *ret; ++ int i, nrates; ++ unsigned char custom_str[64]; ++ struct ndis_wlan_bssid *bssid; ++ struct ndis_wlan_bssid_ex *bssid_ex; ++ int extended; ++ ++ ENTER2("%p, %p", event, item); ++ bssid = item; ++ bssid_ex = item; ++ extended = (bssid->length > offsetof(struct ndis_wlan_bssid_ex, var)); ++ ++ /* add mac address */ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ iwe.len = IW_EV_ADDR_LEN; ++ memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN); ++ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_ADDR_LEN); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ /* add essid */ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.length = bssid->ssid.length; ++ if (iwe.u.data.length > IW_ESSID_MAX_SIZE) ++ iwe.u.data.length = IW_ESSID_MAX_SIZE; ++ iwe.u.data.flags = 1; ++ iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; ++ ret = iwe_stream_add_point(info, event, end_buf, &iwe, ++ bssid->ssid.essid); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ /* add protocol name */ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWNAME; ++ strncpy(iwe.u.name, network_type_to_name(bssid->net_type), IFNAMSIZ); ++ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_CHAR_LEN); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ /* add mode */ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWMODE; ++ if (bssid->mode == Ndis802_11IBSS) ++ iwe.u.mode = IW_MODE_ADHOC; ++ else if (bssid->mode == Ndis802_11Infrastructure) ++ iwe.u.mode = IW_MODE_MASTER; ++ else // if (bssid->mode == Ndis802_11AutoUnknown) ++ iwe.u.mode = IW_MODE_AUTO; ++ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_UINT_LEN); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ /* add freq */ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = bssid->config.ds_config; ++ if (bssid->config.ds_config > 1000000) { ++ iwe.u.freq.m = bssid->config.ds_config / 10; ++ iwe.u.freq.e = 1; ++ } ++ else ++ iwe.u.freq.m = bssid->config.ds_config; ++ /* convert from kHz to Hz */ ++ iwe.u.freq.e += 3; ++ iwe.len = IW_EV_FREQ_LEN; ++ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_FREQ_LEN); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ /* add qual */ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVQUAL; ++ i = 100 * (bssid->rssi - WL_NOISE) / (WL_SIGMAX - WL_NOISE); ++ if (i < 0) ++ i = 0; ++ else if (i > 100) ++ i = 100; ++ iwe.u.qual.level = bssid->rssi; ++ iwe.u.qual.noise = WL_NOISE; ++ iwe.u.qual.qual = i; ++ iwe.len = IW_EV_QUAL_LEN; ++ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_QUAL_LEN); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ /* add key info */ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWENCODE; ++ if (bssid->privacy == Ndis802_11PrivFilterAcceptAll) ++ iwe.u.data.flags = IW_ENCODE_DISABLED; ++ else ++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; ++ iwe.u.data.length = 0; ++ iwe.len = IW_EV_POINT_LEN; ++ ret = iwe_stream_add_point(info, event, end_buf, &iwe, ++ bssid->ssid.essid); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ /* add rate */ ++ memset(&iwe, 0, sizeof(iwe)); ++ current_val = event + iwe_stream_lcp_len(info); ++ iwe.cmd = SIOCGIWRATE; ++ if (extended) ++ nrates = ARRAY_SIZE(bssid->rates); ++ else ++ nrates = ARRAY_SIZE(bssid_ex->rates_ex); ++ for (i = 0; i < nrates; i++) { ++ if (bssid_ex->rates_ex[i] & 0x7f) { ++ iwe.u.bitrate.value = ((bssid->rates[i] & 0x7f) * ++ 500000); ++ ret = iwe_stream_add_value(info, event, current_val, ++ end_buf, &iwe, ++ IW_EV_PARAM_LEN); ++ if (ret == current_val) ++ return NULL; ++ current_val = ret; ++ } ++ } ++ ++ if ((current_val - event) > iwe_stream_lcp_len(info)) ++ event = current_val; ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ sprintf(custom_str, "bcn_int=%d", bssid->config.beacon_period); ++ iwe.u.data.length = strlen(custom_str); ++ ret = iwe_stream_add_point(info, event, end_buf, &iwe, custom_str); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ sprintf(custom_str, "atim=%u", bssid->config.atim_window); ++ iwe.u.data.length = strlen(custom_str); ++ ret = iwe_stream_add_point(info, event, end_buf, &iwe, custom_str); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ ++ TRACE2("%d, %zu", bssid->length, sizeof(*bssid)); ++ if (extended) { ++ struct ndis_variable_ies *iep = bssid_ex->var; ++ unsigned char *end = (unsigned char *)&bssid_ex->fixed + ++ bssid_ex->ie_length; ++ ++ while (&iep->length < end && &iep->data[iep->length] <= end) { ++ unsigned char ielen = iep->length + 2; ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = ielen; ++ ret = iwe_stream_add_point(info, event, end_buf, &iwe, ++ (char *)iep); ++ if (ret == event) ++ return NULL; ++ event = ret; ++ iep = (typeof(iep))&iep->data[iep->length]; ++ } ++ } ++ TRACE2("event = %p, current_val = %p", event, current_val); ++ EXIT2(return event); ++} ++ ++static int set_scan(struct ndis_device *wnd) ++{ ++ NDIS_STATUS res; ++ ++ ENTER2(""); ++ res = mp_set(wnd, OID_802_11_BSSID_LIST_SCAN, NULL, 0); ++ if (res) { ++ WARNING("scanning failed (%08X)", res); ++ EXIT2(return -EOPNOTSUPP); ++ } ++ wnd->scan_timestamp = jiffies; ++ EXIT2(return 0); ++} ++ ++static int iw_set_scan(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ return set_scan(wnd); ++} ++ ++static int iw_get_scan(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ unsigned int i, buf_len, needed, data_len; ++ NDIS_STATUS res; ++ struct ndis_bssid_list *bssid_list = NULL; ++ char *event = extra; ++ struct ndis_wlan_bssid *cur_item; ++ ++ ENTER2(""); ++ if (time_before(jiffies, wnd->scan_timestamp + 3 * HZ)) ++ return -EAGAIN; ++ /* try with space for a few scan items */ ++ buf_len = sizeof(ULONG) + offsetof(struct ndis_wlan_bssid_ex, var) * 8; ++ ++ /* Try many times, as the needed space may grow between queries */ ++ for (i = 0; i < 10; i++) { ++ bssid_list = kzalloc(buf_len, GFP_KERNEL); ++ if (!bssid_list) { ++ ERROR("couldn't allocate %u bytes for scan results", ++ buf_len); ++ return -ENOMEM; ++ } ++ ++ needed = 0; ++ data_len = 0; ++ res = mp_query_info(wnd, OID_802_11_BSSID_LIST, bssid_list, ++ buf_len, &data_len, &needed); ++ TRACE2("try %d: given %d bytes, needed %d, written %d", ++ i, buf_len, needed, data_len); ++ if (needed <= buf_len) ++ break; ++ kfree(bssid_list); ++ buf_len = needed; ++ } ++ if (res) { ++ WARNING("getting BSSID list failed (%08X)", res); ++ kfree(bssid_list); ++ EXIT2(return -EOPNOTSUPP); ++ } ++ ++ /* some drivers don't set bssid_list->num_items to 0 if ++ OID_802_11_BSSID_LIST returns no items (prism54 driver, e.g.,) */ ++ TRACE2("items: %d", bssid_list->num_items); ++ cur_item = &bssid_list->bssid[0]; ++ for (i = 0; i < bssid_list->num_items; i++) { ++ TRACE2("item %d: len %d, remaining data %d", ++ i, cur_item->length, data_len); ++ /* drop truncated items */ ++ if (cur_item->length > data_len) ++ break; ++ event = ndis_translate_scan(dev, info, event, ++ extra + wrqu->data.length, ++ cur_item); ++ if (!event) { ++ kfree(bssid_list); ++ return -E2BIG; ++ } ++ data_len -= cur_item->length; ++ cur_item = (struct ndis_wlan_bssid *)((char *)cur_item + ++ cur_item->length); ++ } ++ wrqu->data.length = event - extra; ++ wrqu->data.flags = 0; ++ kfree(bssid_list); ++ EXIT2(return 0); ++} ++ ++static int iw_set_power_mode(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ enum ndis_power power_mode; ++ ++ if (wrqu->power.disabled == 1) ++ power_mode = NDIS_POWER_OFF; ++ else if (wrqu->power.flags & IW_POWER_MIN) ++ power_mode = NDIS_POWER_MIN; ++ else // if (wrqu->power.flags & IW_POWER_MAX) ++ power_mode = NDIS_POWER_MAX; ++ ++ TRACE2("%d", power_mode); ++ res = mp_set(wnd, OID_802_11_POWER_MODE, ++ &power_mode, sizeof(power_mode)); ++ if (res) ++ WARNING("setting power mode failed (%08X)", res); ++ return 0; ++} ++ ++static int iw_get_power_mode(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ enum ndis_power power_mode; ++ ++ ENTER2(""); ++ res = mp_query(wnd, OID_802_11_POWER_MODE, ++ &power_mode, sizeof(power_mode)); ++ if (res) ++ return -ENOTSUPP; ++ ++ if (power_mode == NDIS_POWER_OFF) ++ wrqu->power.disabled = 1; ++ else { ++ if (wrqu->power.flags != 0) ++ return 0; ++ wrqu->power.flags |= IW_POWER_ALL_R; ++ wrqu->power.flags |= IW_POWER_TIMEOUT; ++ wrqu->power.value = 0; ++ wrqu->power.disabled = 0; ++ ++ if (power_mode == NDIS_POWER_MIN) ++ wrqu->power.flags |= IW_POWER_MIN; ++ else // if (power_mode == NDIS_POWER_MAX) ++ wrqu->power.flags |= IW_POWER_MAX; ++ } ++ return 0; ++} ++ ++static int iw_get_sensitivity(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ ndis_rssi rssi_trigger; ++ ++ ENTER2(""); ++ res = mp_query(wnd, OID_802_11_RSSI_TRIGGER, ++ &rssi_trigger, sizeof(rssi_trigger)); ++ if (res) ++ return -EOPNOTSUPP; ++ wrqu->param.value = rssi_trigger; ++ wrqu->param.disabled = (rssi_trigger == 0); ++ wrqu->param.fixed = 1; ++ return 0; ++} ++ ++static int iw_set_sensitivity(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ ndis_rssi rssi_trigger; ++ ++ ENTER2(""); ++ if (wrqu->param.disabled) ++ rssi_trigger = 0; ++ else ++ rssi_trigger = wrqu->param.value; ++ res = mp_set(wnd, OID_802_11_RSSI_TRIGGER, ++ &rssi_trigger, sizeof(rssi_trigger)); ++ if (res == NDIS_STATUS_INVALID_DATA) ++ return -EINVAL; ++ if (res) ++ return -EOPNOTSUPP; ++ return 0; ++} ++ ++static int iw_get_ndis_stats(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ struct iw_statistics *stats = &wnd->iw_stats; ++ memcpy(&wrqu->qual, &stats->qual, sizeof(stats->qual)); ++ return 0; ++} ++ ++static int iw_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 iw_point *data = &wrqu->data; ++ struct ndis_device *wnd = netdev_priv(dev); ++ unsigned int i, n; ++ NDIS_STATUS res; ++ UCHAR rates[NDIS_MAX_RATES_EX]; ++ ndis_tx_power_level tx_power; ++ ++ ENTER2(""); ++ data->length = sizeof(struct iw_range); ++ memset(range, 0, sizeof(struct iw_range)); ++ ++ range->txpower_capa = IW_TXPOW_MWATT; ++ range->num_txpower = 0; ++ ++ res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, ++ &tx_power, sizeof(tx_power)); ++ if (!res) { ++ range->num_txpower = 1; ++ range->txpower[0] = tx_power; ++ } ++ ++ range->we_version_compiled = WIRELESS_EXT; ++ range->we_version_source = 19; ++ ++ range->retry_capa = IW_RETRY_LIMIT; ++ range->retry_flags = IW_RETRY_LIMIT; ++ range->min_retry = 0; ++ range->max_retry = 255; ++ ++ range->num_channels = 1; ++ ++ range->max_qual.qual = 100; ++ range->max_qual.level = 154; ++ range->max_qual.noise = 154; ++ range->sensitivity = 3; ++ ++ range->max_encoding_tokens = 4; ++ range->num_encoding_sizes = 2; ++ range->encoding_size[0] = 5; ++ range->encoding_size[1] = 13; ++ ++ range->num_bitrates = 0; ++ memset(&rates, 0, sizeof(rates)); ++ res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES, ++ &rates, sizeof(rates), &n, NULL); ++ if (res) ++ WARNING("getting bit rates failed: %08X", res); ++ else { ++ for (i = 0; i < n && range->num_bitrates < IW_MAX_BITRATES; i++) ++ if (rates[i] & 0x80) ++ continue; ++ else if (rates[i] & 0x7f) { ++ range->bitrate[range->num_bitrates] = ++ (rates[i] & 0x7f) * 500000; ++ range->num_bitrates++; ++ } ++ } ++ ++ range->num_channels = ARRAY_SIZE(freq_chan); ++ ++ for (i = 0; i < ARRAY_SIZE(freq_chan) && i < IW_MAX_FREQUENCIES; i++) { ++ range->freq[i].i = i + 1; ++ range->freq[i].m = freq_chan[i] * 100000; ++ range->freq[i].e = 1; ++ } ++ range->num_frequency = i; ++ ++ range->min_rts = 0; ++ range->max_rts = 2347; ++ range->min_frag = 256; ++ range->max_frag = 2346; ++ ++ /* Event capability (kernel + driver) */ ++ range->event_capa[0] = (IW_EVENT_CAPA_K_0 | ++ IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | ++ IW_EVENT_CAPA_MASK(SIOCGIWAP) | ++ IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); ++ range->event_capa[1] = IW_EVENT_CAPA_K_1; ++ range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | ++ IW_EVENT_CAPA_MASK(IWEVCUSTOM) | ++ IW_EVENT_CAPA_MASK(IWEVREGISTERED) | ++ IW_EVENT_CAPA_MASK(IWEVEXPIRED)); ++ ++ range->enc_capa = 0; ++ ++ if (test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr)) ++ range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; ++ if (test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr)) ++ range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; ++ ++ if (test_bit(Ndis802_11AuthModeWPA, &wnd->capa.auth) || ++ test_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.auth)) ++ range->enc_capa |= IW_ENC_CAPA_WPA; ++ if (test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) || ++ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth)) ++ range->enc_capa |= IW_ENC_CAPA_WPA2; ++ ++ return 0; ++} ++ ++void set_default_iw_params(struct ndis_device *wnd) ++{ ++ wnd->iw_auth_key_mgmt = 0; ++ wnd->iw_auth_wpa_version = 0; ++ set_infra_mode(wnd, Ndis802_11Infrastructure); ++ set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen); ++ set_priv_filter(wnd); ++ set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE); ++} ++ ++static int deauthenticate(struct ndis_device *wnd) ++{ ++ int ret; ++ ++ ENTER2(""); ++ ret = disassociate(wnd, 1); ++ set_default_iw_params(wnd); ++ EXIT2(return ret); ++} ++ ++NDIS_STATUS disassociate(struct ndis_device *wnd, int reset_ssid) ++{ ++ NDIS_STATUS res; ++ u8 buf[NDIS_ESSID_MAX_SIZE]; ++ int i; ++ ++ TRACE2(""); ++ res = mp_set(wnd, OID_802_11_DISASSOCIATE, NULL, 0); ++ /* disassociate causes radio to be turned off; if reset_ssid ++ * is given, set ssid to random to enable radio */ ++ if (reset_ssid) { ++ get_random_bytes(buf, sizeof(buf)); ++ for (i = 0; i < sizeof(buf); i++) ++ buf[i] = 'a' + (buf[i] % 26); ++ set_essid(wnd, buf, sizeof(buf)); ++ } ++ return res; ++} ++ ++static int iw_set_mlme(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ struct iw_mlme *mlme = (struct iw_mlme *)extra; ++ ++ ENTER2(""); ++ switch (mlme->cmd) { ++ case IW_MLME_DEAUTH: ++ return deauthenticate(wnd); ++ case IW_MLME_DISASSOC: ++ TRACE2("cmd=%d reason_code=%d", mlme->cmd, mlme->reason_code); ++ return disassociate(wnd, 1); ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int iw_set_genie(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ /* ++ * NDIS drivers do not allow IEs to be configured; this is ++ * done by the driver based on other configuration. Return 0 ++ * to avoid causing issues with user space programs that ++ * expect this function to succeed. ++ */ ++ return 0; ++} ++ ++static int iw_set_auth(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ TRACE2("index=%d value=%d", wrqu->param.flags & IW_AUTH_INDEX, ++ wrqu->param.value); ++ switch (wrqu->param.flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ wnd->iw_auth_wpa_version = wrqu->param.value; ++ break; ++ case IW_AUTH_CIPHER_PAIRWISE: ++ wnd->iw_auth_cipher_pairwise = wrqu->param.value; ++ break; ++ case IW_AUTH_CIPHER_GROUP: ++ wnd->iw_auth_cipher_group = wrqu->param.value; ++ break; ++ case IW_AUTH_KEY_MGMT: ++ wnd->iw_auth_key_mgmt = wrqu->param.value; ++ break; ++ case IW_AUTH_80211_AUTH_ALG: ++ wnd->iw_auth_80211_alg = wrqu->param.value; ++ break; ++ case IW_AUTH_WPA_ENABLED: ++ if (wrqu->param.value) ++ deauthenticate(wnd); ++ break; ++#ifdef IW_AUTH_MFP ++ case IW_AUTH_MFP: ++ if (wrqu->param.value == IW_AUTH_MFP_DISABLED || ++ wrqu->param.value == IW_AUTH_MFP_OPTIONAL) ++ break; ++ WARNING("MFP not implemented"); ++ return -EOPNOTSUPP; ++#endif ++ case IW_AUTH_TKIP_COUNTERMEASURES: ++ case IW_AUTH_DROP_UNENCRYPTED: ++ case IW_AUTH_RX_UNENCRYPTED_EAPOL: ++ case IW_AUTH_PRIVACY_INVOKED: ++ TRACE2("%d not implemented: %d", ++ wrqu->param.flags & IW_AUTH_INDEX, wrqu->param.value); ++ break; ++ default: ++ WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX); ++ return -EOPNOTSUPP; ++ } ++ return 0; ++} ++ ++static int iw_get_auth(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ ENTER2("index=%d", wrqu->param.flags & IW_AUTH_INDEX); ++ switch (wrqu->param.flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ wrqu->param.value = wnd->iw_auth_wpa_version; ++ break; ++ case IW_AUTH_CIPHER_PAIRWISE: ++ wrqu->param.value = wnd->iw_auth_cipher_pairwise; ++ break; ++ case IW_AUTH_CIPHER_GROUP: ++ wrqu->param.value = wnd->iw_auth_cipher_group; ++ break; ++ case IW_AUTH_KEY_MGMT: ++ wrqu->param.value = wnd->iw_auth_key_mgmt; ++ break; ++ case IW_AUTH_80211_AUTH_ALG: ++ wrqu->param.value = wnd->iw_auth_80211_alg; ++ break; ++ default: ++ WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX); ++ return -EOPNOTSUPP; ++ } ++ return 0; ++} ++ ++static int iw_set_encodeext(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; ++ struct ndis_device *wnd = netdev_priv(dev); ++ struct ndis_add_key ndis_key; ++ int i, keyidx; ++ NDIS_STATUS res; ++ u8 *addr; ++ ++ keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX; ++ ENTER2("%d", keyidx); ++ if (keyidx) ++ keyidx--; ++ else ++ keyidx = wnd->encr_info.tx_key_index; ++ ++ if (keyidx < 0 || keyidx >= MAX_ENCR_KEYS) ++ return -EINVAL; ++ ++ if (ext->alg == WPA_ALG_WEP) { ++ if (!test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr)) ++ EXIT2(return -1); ++ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) ++ wnd->encr_info.tx_key_index = keyidx; ++ if (add_wep_key(wnd, ext->key, ext->key_len, keyidx)) ++ EXIT2(return -1); ++ else ++ EXIT2(return 0); ++ } ++ if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) || ++ ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0) ++ EXIT2(return remove_key(wnd, keyidx, ndis_key.bssid)); ++ ++ if (ext->key_len > sizeof(ndis_key.key)) { ++ TRACE2("incorrect key length (%u)", ext->key_len); ++ EXIT2(return -1); ++ } ++ ++ memset(&ndis_key, 0, sizeof(ndis_key)); ++ ++ ndis_key.struct_size = ++ sizeof(ndis_key) - sizeof(ndis_key.key) + ext->key_len; ++ ndis_key.length = ext->key_len; ++ ndis_key.index = keyidx; ++ ++ if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { ++ for (i = 0; i < 6; i++) ++ ndis_key.rsc |= (((u64)ext->rx_seq[i]) << (i * 8)); ++ TRACE2("0x%llx", ndis_key.rsc); ++ ndis_key.index |= 1 << 29; ++ } ++ ++ addr = ext->addr.sa_data; ++ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { ++ /* group key */ ++ if (wnd->infrastructure_mode == Ndis802_11IBSS) ++ memset(ndis_key.bssid, 0xff, ETH_ALEN); ++ else ++ get_ap_address(wnd, ndis_key.bssid); ++ } else { ++ /* pairwise key */ ++ ndis_key.index |= (1 << 30); ++ memcpy(ndis_key.bssid, addr, ETH_ALEN); ++ } ++ TRACE2(MACSTRSEP, MAC2STR(ndis_key.bssid)); ++ ++ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) ++ ndis_key.index |= (1 << 31); ++ ++ if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) { ++ /* wpa_supplicant gives us the Michael MIC RX/TX keys in ++ * different order than NDIS spec, so swap the order here. */ ++ memcpy(ndis_key.key, ext->key, 16); ++ memcpy(ndis_key.key + 16, ext->key + 24, 8); ++ memcpy(ndis_key.key + 24, ext->key + 16, 8); ++ } else ++ memcpy(ndis_key.key, ext->key, ext->key_len); ++ ++ res = mp_set(wnd, OID_802_11_ADD_KEY, &ndis_key, ndis_key.struct_size); ++ if (res) { ++ TRACE2("adding key failed (%08X), %u", ++ res, ndis_key.struct_size); ++ EXIT2(return -1); ++ } ++ wnd->encr_info.keys[keyidx].length = ext->key_len; ++ memcpy(&wnd->encr_info.keys[keyidx].key, ndis_key.key, ext->key_len); ++ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) ++ wnd->encr_info.tx_key_index = keyidx; ++ TRACE2("key %d added", keyidx); ++ ++ EXIT2(return 0); ++} ++ ++static int iw_get_encodeext(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ /* struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; */ ++ /* TODO */ ++ ENTER2(""); ++ return 0; ++} ++ ++static int iw_set_pmksa(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; ++ struct ndis_pmkid pmkid; ++ NDIS_STATUS res; ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ /* TODO: must keep local list of PMKIDs since NDIS drivers ++ * expect that all PMKID entries are included whenever a new ++ * one is added. */ ++ ++ ENTER2("%d", pmksa->cmd); ++ if ((pmksa->cmd == IW_PMKSA_ADD || pmksa->cmd == IW_PMKSA_REMOVE) && ++ (!(wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2))) ++ EXIT2(return -EOPNOTSUPP); ++ ++ memset(&pmkid, 0, sizeof(pmkid)); ++ if (pmksa->cmd == IW_PMKSA_ADD) { ++ pmkid.bssid_info_count = 1; ++ memcpy(pmkid.bssid_info[0].bssid, pmksa->bssid.sa_data, ++ ETH_ALEN); ++ memcpy(pmkid.bssid_info[0].pmkid, pmksa->pmkid, IW_PMKID_LEN); ++ } ++ pmkid.length = sizeof(pmkid); ++ ++ res = mp_set(wnd, OID_802_11_PMKID, &pmkid, pmkid.length); ++ if (res == NDIS_STATUS_FAILURE) ++ EXIT2(return -EOPNOTSUPP); ++ TRACE2("OID_802_11_PMKID -> %d", res); ++ if (res) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++#define WEXT(id) [id - SIOCIWFIRST] ++ ++static const iw_handler ndis_handler[] = { ++ WEXT(SIOCGIWNAME) = iw_get_network_type, ++ WEXT(SIOCSIWESSID) = iw_set_essid, ++ WEXT(SIOCGIWESSID) = iw_get_essid, ++ WEXT(SIOCSIWMODE) = iw_set_infra_mode, ++ WEXT(SIOCGIWMODE) = iw_get_infra_mode, ++ WEXT(SIOCGIWFREQ) = iw_get_freq, ++ WEXT(SIOCSIWFREQ) = iw_set_freq, ++ WEXT(SIOCGIWTXPOW) = iw_get_tx_power, ++ WEXT(SIOCSIWTXPOW) = iw_set_tx_power, ++ WEXT(SIOCGIWRATE) = iw_get_bitrate, ++ WEXT(SIOCSIWRATE) = iw_set_bitrate, ++ WEXT(SIOCGIWRTS) = iw_get_rts_threshold, ++ WEXT(SIOCSIWRTS) = iw_set_rts_threshold, ++ WEXT(SIOCGIWFRAG) = iw_get_frag_threshold, ++ WEXT(SIOCSIWFRAG) = iw_set_frag_threshold, ++ WEXT(SIOCGIWAP) = iw_get_ap_address, ++ WEXT(SIOCSIWAP) = iw_set_ap_address, ++ WEXT(SIOCSIWENCODE) = iw_set_wep, ++ WEXT(SIOCGIWENCODE) = iw_get_encr, ++ WEXT(SIOCSIWSCAN) = iw_set_scan, ++ WEXT(SIOCGIWSCAN) = iw_get_scan, ++ WEXT(SIOCGIWPOWER) = iw_get_power_mode, ++ WEXT(SIOCSIWPOWER) = iw_set_power_mode, ++ WEXT(SIOCGIWRANGE) = iw_get_range, ++ WEXT(SIOCGIWSTATS) = iw_get_ndis_stats, ++ WEXT(SIOCGIWSENS) = iw_get_sensitivity, ++ WEXT(SIOCSIWSENS) = iw_set_sensitivity, ++ WEXT(SIOCGIWNICKN) = iw_get_nick, ++ WEXT(SIOCSIWNICKN) = iw_set_nick, ++ WEXT(SIOCSIWCOMMIT) = iw_set_dummy, ++ WEXT(SIOCSIWMLME) = iw_set_mlme, ++ WEXT(SIOCSIWGENIE) = iw_set_genie, ++ WEXT(SIOCSIWAUTH) = iw_set_auth, ++ WEXT(SIOCGIWAUTH) = iw_get_auth, ++ WEXT(SIOCSIWENCODEEXT) = iw_set_encodeext, ++ WEXT(SIOCGIWENCODEEXT) = iw_get_encodeext, ++ WEXT(SIOCSIWPMKSA) = iw_set_pmksa, ++}; ++ ++/* private ioctl's */ ++ ++static int priv_reset(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int res; ++ ENTER2(""); ++ res = mp_reset(netdev_priv(dev)); ++ if (res) { ++ WARNING("reset failed: %08X", res); ++ return -EOPNOTSUPP; ++ } ++ return 0; ++} ++ ++static int priv_deauthenticate(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ int res; ++ ENTER2(""); ++ res = deauthenticate(netdev_priv(dev)); ++ return res; ++} ++ ++static int priv_power_profile(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ struct miniport *mp; ++ ULONG profile_inf; ++ ++ ENTER2(""); ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ if (!mp->pnp_event_notify) ++ EXIT2(return -EOPNOTSUPP); ++ ++ /* 1 for AC and 0 for Battery */ ++ if (wrqu->param.value) ++ profile_inf = NdisPowerProfileAcOnLine; ++ else ++ profile_inf = NdisPowerProfileBattery; ++ ++ LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx, ++ NdisDevicePnPEventPowerProfileChanged, ++ &profile_inf, sizeof(profile_inf)); ++ EXIT2(return 0); ++} ++ ++static int priv_network_type(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ enum network_type network_type; ++ NDIS_STATUS res; ++ char type; ++ ++ ENTER2(""); ++ type = wrqu->param.value; ++ if (type == 'f') ++ network_type = Ndis802_11FH; ++ else if (type == 'b') ++ network_type = Ndis802_11DS; ++ else if (type == 'a') ++ network_type = Ndis802_11OFDM5; ++ else if (type == 'g' || type == 'n') ++ network_type = Ndis802_11OFDM24; ++ else ++ network_type = Ndis802_11Automode; ++ ++ res = mp_set_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, network_type); ++ if (res) { ++ WARNING("setting network type to %d failed (%08X)", ++ network_type, res); ++ EXIT2(return -EINVAL); ++ } ++ ++ EXIT2(return 0); ++} ++ ++static int priv_media_stream_mode(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ NDIS_STATUS res; ++ int mode; ++ ++ ENTER2(""); ++ if (wrqu->param.value > 0) ++ mode = Ndis802_11MediaStreamOn; ++ else ++ mode = Ndis802_11MediaStreamOff; ++ res = mp_set_int(wnd, OID_802_11_MEDIA_STREAM_MODE, mode); ++ if (res) { ++ WARNING("oid failed (%08X)", res); ++ EXIT2(return -EINVAL); ++ } ++ EXIT2(return 0); ++} ++ ++static int priv_reload_defaults(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ int res; ++ ENTER2(""); ++ res = mp_set_int(wnd, OID_802_11_RELOAD_DEFAULTS, ++ Ndis802_11ReloadWEPKeys); ++ if (res) { ++ WARNING("reloading defaults failed: %08X", res); ++ return -EOPNOTSUPP; ++ } ++ return 0; ++} ++ ++static const struct iw_priv_args priv_args[] = { ++ {PRIV_RESET, 0, 0, "ndis_reset"}, ++ {PRIV_POWER_PROFILE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ++ "power_profile"}, ++ {PRIV_DEAUTHENTICATE, 0, 0, "deauthenticate"}, ++ {PRIV_NETWORK_TYPE, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, ++ "network_type"}, ++ {PRIV_MEDIA_STREAM_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ++ "media_stream"}, ++ ++ {PRIV_RELOAD_DEFAULTS, 0, 0, "reload_defaults"}, ++}; ++ ++#define WEPRIV(id) [id - SIOCIWFIRSTPRIV] ++ ++static const iw_handler priv_handler[] = { ++ WEPRIV(PRIV_RESET) = priv_reset, ++ WEPRIV(PRIV_POWER_PROFILE) = priv_power_profile, ++ WEPRIV(PRIV_DEAUTHENTICATE) = priv_deauthenticate, ++ WEPRIV(PRIV_NETWORK_TYPE) = priv_network_type, ++ WEPRIV(PRIV_MEDIA_STREAM_MODE) = priv_media_stream_mode, ++ WEPRIV(PRIV_RELOAD_DEFAULTS) = priv_reload_defaults, ++}; ++ ++const struct iw_handler_def ndis_handler_def = { ++ .num_standard = ARRAY_SIZE(ndis_handler), ++ .num_private = ARRAY_SIZE(priv_handler), ++ .num_private_args = ARRAY_SIZE(priv_args), ++ ++ .standard = (iw_handler *)ndis_handler, ++ .private = (iw_handler *)priv_handler, ++ .private_args = (struct iw_priv_args *)priv_args, ++ .get_wireless_stats = get_iw_stats, ++}; ++ ++#endif +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.h +--- linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.h 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,194 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _IW_NDIS_H_ ++#define _IW_NDIS_H_ ++ ++#include "ndis.h" ++ ++#define WL_NOISE -96 /* typical noise level in dBm */ ++#define WL_SIGMAX -32 /* typical maximum signal level in dBm */ ++ ++struct ndis_encr_key { ++ ULONG struct_size; ++ ULONG index; ++ ULONG length; ++ UCHAR key[NDIS_ENCODING_TOKEN_MAX]; ++}; ++ ++struct ndis_add_key { ++ ULONG struct_size; ++ ndis_key_index index; ++ ULONG length; ++ mac_address bssid; ++ UCHAR pad[6]; ++ ndis_key_rsc rsc; ++ UCHAR key[NDIS_ENCODING_TOKEN_MAX]; ++}; ++ ++struct ndis_remove_key { ++ ULONG struct_size; ++ ndis_key_index index; ++ mac_address bssid; ++}; ++ ++struct ndis_fixed_ies { ++ UCHAR time_stamp[8]; ++ USHORT beacon_interval; ++ USHORT capa; ++}; ++ ++struct ndis_variable_ies { ++ UCHAR elem_id; ++ UCHAR length; ++ UCHAR data[]; ++}; ++ ++enum ndis_reload_defaults { Ndis802_11ReloadWEPKeys }; ++ ++struct ndis_assoc_info { ++ ULONG length; ++ USHORT req_ies; ++ struct req_ie { ++ USHORT capa; ++ USHORT listen_interval; ++ mac_address cur_ap_address; ++ } req_ie; ++ ULONG req_ie_length; ++ ULONG offset_req_ies; ++ USHORT resp_ies; ++ struct resp_ie { ++ USHORT capa; ++ USHORT status_code; ++ USHORT assoc_id; ++ } resp_ie; ++ ULONG resp_ie_length; ++ ULONG offset_resp_ies; ++}; ++ ++struct ndis_configuration_fh { ++ ULONG length; ++ ULONG hop_pattern; ++ ULONG hop_set; ++ ULONG dwell_time; ++}; ++ ++struct ndis_configuration { ++ ULONG length; ++ ULONG beacon_period; ++ ULONG atim_window; ++ ULONG ds_config; ++ struct ndis_configuration_fh fh_config; ++}; ++ ++struct ndis_wlan_bssid { ++ ULONG length; ++ mac_address mac; ++ UCHAR reserved[2]; ++ struct ndis_essid ssid; ++ ULONG privacy; ++ ndis_rssi rssi; ++ UINT net_type; ++ struct ndis_configuration config; ++ UINT mode; ++ UCHAR rates[NDIS_MAX_RATES]; ++}; ++ ++struct ndis_wlan_bssid_ex { ++ ULONG length; ++ mac_address mac; ++ UCHAR reserved[2]; ++ struct ndis_essid ssid; ++ ULONG privacy; ++ ndis_rssi rssi; ++ UINT net_type; ++ struct ndis_configuration config; ++ UINT mode; ++ UCHAR rates_ex[NDIS_MAX_RATES_EX]; ++ ULONG ie_length; ++ struct ndis_fixed_ies fixed; ++ struct ndis_variable_ies var[]; ++}; ++ ++/* we use bssid_list as bssid_list_ex also */ ++struct ndis_bssid_list { ++ ULONG num_items; ++ struct ndis_wlan_bssid bssid[1]; ++}; ++ ++enum ndis_priv_filter { ++ Ndis802_11PrivFilterAcceptAll, Ndis802_11PrivFilter8021xWEP ++}; ++ ++enum network_type { ++ Ndis802_11FH, Ndis802_11DS, Ndis802_11OFDM5, Ndis802_11OFDM24, ++ /* MSDN site uses Ndis802_11Automode, which is not mentioned ++ * in DDK, so add one and assign it to ++ * Ndis802_11NetworkTypeMax */ ++ Ndis802_11Automode, Ndis802_11NetworkTypeMax = Ndis802_11Automode ++}; ++ ++struct network_type_list { ++ ULONG num; ++ enum network_type types[1]; ++}; ++ ++enum ndis_power { ++ NDIS_POWER_OFF = 0, NDIS_POWER_MAX, NDIS_POWER_MIN, ++}; ++ ++struct ndis_auth_req { ++ ULONG length; ++ mac_address bssid; ++ ULONG flags; ++}; ++ ++struct ndis_bssid_info { ++ mac_address bssid; ++ UCHAR pmkid[IW_PMKID_LEN]; ++}; ++ ++struct ndis_pmkid { ++ ULONG length; ++ ULONG bssid_info_count; ++ struct ndis_bssid_info bssid_info[1]; ++}; ++ ++int get_ap_address(struct ndis_device *wnd, mac_address mac); ++int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode); ++int get_ndis_encr_mode(struct ndis_device *wnd); ++int set_iw_encr_mode(struct ndis_device *wnd, int cipher_pairwise, ++ int cipher_groupwise); ++int get_ndis_auth_mode(struct ndis_device *wnd); ++NDIS_STATUS disassociate(struct ndis_device *wnd, int reset_ssid); ++void set_default_iw_params(struct ndis_device *wnd); ++extern const struct iw_handler_def ndis_handler_def; ++ ++#define PRIV_RESET SIOCIWFIRSTPRIV+16 ++#define PRIV_POWER_PROFILE SIOCIWFIRSTPRIV+17 ++#define PRIV_NETWORK_TYPE SIOCIWFIRSTPRIV+18 ++#define PRIV_DEAUTHENTICATE SIOCIWFIRSTPRIV+19 ++#define PRIV_MEDIA_STREAM_MODE SIOCIWFIRSTPRIV+20 ++#define PRIV_RELOAD_DEFAULTS SIOCIWFIRSTPRIV+23 ++ ++/* these have to match what is in wpa_supplicant */ ++ ++enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP }; ++enum wpa_cipher { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, ++ CIPHER_WEP104 }; ++enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, ++ KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE }; ++ ++#endif // IW_NDIS_H +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.h +--- linux-3.12.2/3rdparty/ndiswrapper/lin2win.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.h 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,111 @@ ++/* ++ * Copyright (C) 2006 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifdef CONFIG_X86_64 ++ ++u64 lin2win0(void *func); ++u64 lin2win1(void *func, u64 arg1); ++u64 lin2win2(void *func, u64 arg1, u64 arg2); ++u64 lin2win3(void *func, u64 arg1, u64 arg2, u64 arg3); ++u64 lin2win4(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4); ++u64 lin2win5(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5); ++u64 lin2win6(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, ++ u64 arg6); ++ ++#define LIN2WIN0(func) \ ++({ \ ++ if (0) \ ++ func(); \ ++ lin2win0(func); \ ++}) ++ ++#define LIN2WIN1(func, arg1) \ ++({ \ ++ if (0) \ ++ func(arg1); \ ++ lin2win1(func, (u64)arg1); \ ++}) ++ ++#define LIN2WIN2(func, arg1, arg2) \ ++({ \ ++ if (0) \ ++ func(arg1, arg2); \ ++ lin2win2(func, (u64)arg1, (u64)arg2); \ ++}) ++ ++#define LIN2WIN3(func, arg1, arg2, arg3) \ ++({ \ ++ if (0) \ ++ func(arg1, arg2, arg3); \ ++ lin2win3(func, (u64)arg1, (u64)arg2, (u64)arg3); \ ++}) ++ ++#define LIN2WIN4(func, arg1, arg2, arg3, arg4) \ ++({ \ ++ if (0) \ ++ func(arg1, arg2, arg3, arg4); \ ++ lin2win4(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4); \ ++}) ++ ++#define LIN2WIN5(func, arg1, arg2, arg3, arg4, arg5) \ ++({ \ ++ if (0) \ ++ func(arg1, arg2, arg3, arg4, arg5); \ ++ lin2win5(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4, \ ++ (u64)arg5); \ ++}) ++ ++#define LIN2WIN6(func, arg1, arg2, arg3, arg4, arg5, arg6) \ ++({ \ ++ if (0) \ ++ func(arg1, arg2, arg3, arg4, arg5, arg6); \ ++ lin2win6(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4, \ ++ (u64)arg5, (u64)arg6); \ ++}) ++ ++#else // CONFIG_X86_64 ++ ++#define LIN2WIN1(func, arg1) \ ++({ \ ++ TRACE6("calling %p", func); \ ++ func(arg1); \ ++}) ++#define LIN2WIN2(func, arg1, arg2) \ ++({ \ ++ TRACE6("calling %p", func); \ ++ func(arg1, arg2); \ ++}) ++#define LIN2WIN3(func, arg1, arg2, arg3) \ ++({ \ ++ TRACE6("calling %p", func); \ ++ func(arg1, arg2, arg3); \ ++}) ++#define LIN2WIN4(func, arg1, arg2, arg3, arg4) \ ++({ \ ++ TRACE6("calling %p", func); \ ++ func(arg1, arg2, arg3, arg4); \ ++}) ++#define LIN2WIN5(func, arg1, arg2, arg3, arg4, arg5) \ ++({ \ ++ TRACE6("calling %p", func); \ ++ func(arg1, arg2, arg3, arg4, arg5); \ ++}) ++#define LIN2WIN6(func, arg1, arg2, arg3, arg4, arg5, arg6) \ ++({ \ ++ TRACE6("calling %p", func); \ ++ func(arg1, arg2, arg3, arg4, arg5, arg6); \ ++}) ++ ++#endif // CONFIG_X86_64 +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.S linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.S +--- linux-3.12.2/3rdparty/ndiswrapper/lin2win.S 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.S 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,138 @@ ++/* ++ * Copyright (C) 2011 Pavel Roskin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include ++ ++ .text ++ ++#define WORD_BYTES 8 ++#define LINUX_REG_ARGS 6 ++#define WINDOWS_REG_ARGS 4 ++ ++/* %rbp is saved to create a stack frame, which can help with debugging */ ++#define SAVED_REGS 1 ++ ++/* ++ * When calling a Windows function, stack space is allocated for at least 4 ++ * arguments even if the number of arguments is less than 4. The value of ++ * true is -1 in assembler, so we multiply it by another true value. ++ */ ++#define stack_args(argc) \ ++ (WINDOWS_REG_ARGS + \ ++ (0 < 1) * (argc > WINDOWS_REG_ARGS) * (argc - WINDOWS_REG_ARGS)) ++ ++/* Full required change of stack pointer, in words */ ++#define stack_words_raw(argc) (stack_args(argc) + SAVED_REGS + 1) ++ ++/* Full actual change of stack pointer, in words (must be even) */ ++#define stack_words_aligned(argc) ((stack_words_raw(argc) + 1) & ~1) ++ ++/* Space allocated for Linux arguments on stack */ ++#define stack_space(argc) \ ++ ((stack_words_aligned(argc) - SAVED_REGS - 1) * WORD_BYTES) ++ ++/* ++ * lin2win_win_arg(N) gives the address of the Nth Windows argument on our ++ * stack frame. %rsp points to the first argument. The Nth argument is ++ * therefore at ((N - 1) * 8)(%rsp). ++ * ++ * Don't call with N less than 5! ++ */ ++#define lin2win_win_arg(n) ((n - 1) * WORD_BYTES)(%rsp) ++ ++/* ++ * lin2win_lin_arg(N, ARGC) gives the address of the Nth Linux argument after ++ * the stack has been prepared for a Windows function call with ARGC arguments. ++ * ++ * When called from Linux, the Nth argument is at ((N - 6) * 8)(%rsp). We add ++ * the allocated stack space and saved registers to compensate for %rsp change. ++ * ++ * Don't call with N less than 7! ++ */ ++#define lin2win_lin_arg(n, argc) \ ++ (stack_space(argc) + \ ++ (SAVED_REGS + n - LINUX_REG_ARGS) * WORD_BYTES)(%rsp) ++ ++/* ++ * lin2win(func, winarg1, winarg2, ...) ++ * Call Windows FUNC function with ARGC arguments WINARG1, WINARG2, ... ++ * We get (ARGC + 1) arguments. ++ */ ++.macro lin2win name, argc ++ .type \name, @function ++ ENTRY(\name) ++ ++ /* Create a call frame - it's optional, but good for debugging */ ++ .cfi_startproc ++ push %rbp ++ .cfi_def_cfa %rsp, 2 * WORD_BYTES ++ .cfi_offset %rbp, -2 * WORD_BYTES ++ mov %rsp, %rbp ++ .cfi_def_cfa %rbp, 2 * WORD_BYTES ++ ++ /* Allocate space for Windows arguments */ ++ sub $stack_space(\argc), %rsp ++ ++ /* arg7 to winarg6 */ ++ .if (\argc >= 6) ++ mov lin2win_lin_arg(7, \argc), %r11 ++ mov %r11, lin2win_win_arg(6) ++ .endif ++ ++ /* arg6 to winarg5 */ ++ .if (\argc >= 5) ++ mov %r9, lin2win_win_arg(5) ++ .endif ++ ++ /* arg5 to winarg4 */ ++ .if (\argc >= 4) ++ mov %r8, %r9 ++ .endif ++ ++ /* arg4 to winarg3 */ ++ .if (\argc >= 3) ++ mov %rcx, %r8 ++ .endif ++ ++ /* arg3 to winarg2 - nothing needed, both are in %rdx */ ++ ++ /* arg2 to winarg1 */ ++ .if (\argc >= 1) ++ mov %rsi, %rcx ++ .endif ++ ++ /* Call function (arg1) */ ++ call *%rdi ++ ++ /* Reclaim space for Windows arguments */ ++ add $stack_space(\argc), %rsp ++ ++ /* Return to the caller */ ++ leave ++ .cfi_def_cfa %rsp, WORD_BYTES ++ .cfi_restore %rbp ++ ret ++ .cfi_endproc ++ .size \name, (. - \name) ++.endm ++ ++/* Define lin2winN functions */ ++lin2win lin2win0, 0 ++lin2win lin2win1, 1 ++lin2win lin2win2, 2 ++lin2win lin2win3, 3 ++lin2win lin2win4, 4 ++lin2win lin2win5, 5 ++lin2win lin2win6, 6 +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.c +--- linux-3.12.2/3rdparty/ndiswrapper/loader.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.c 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,967 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ndis.h" ++#include "loader.h" ++#include "wrapndis.h" ++#include "pnp.h" ++ ++#include ++#include ++#include ++#include ++ ++/* ++ Network adapter: ClassGuid = {4d36e972-e325-11ce-bfc1-08002be10318} ++ Network client: ClassGuid = {4d36e973-e325-11ce-bfc1-08002be10318} ++ PCMCIA adapter: ClassGuid = {4d36e977-e325-11ce-bfc1-08002be10318} ++ USB: ClassGuid = {36fc9e60-c465-11cf-8056-444553540000} ++*/ ++ ++/* the indices used here must match macros WRAP_NDIS_DEVICE etc. */ ++static struct guid class_guids[] = { ++ /* Network */ ++ { .data1 = 0x4d36e972, .data2 = 0xe325, .data3 = 0x11ce }, ++ /* USB WDM */ ++ { .data1 = 0x36fc9e60, .data2 = 0xc465, .data3 = 0x11cf }, ++ /* Bluetooth */ ++ { .data1 = 0xe0cbf06c, .data2 = 0xcd8b, .data3 = 0x4647 }, ++ /* ivtcorporation.com's bluetooth device claims this is ++ * bluetooth guid */ ++ { .data1 = 0xf12d3cf8, .data2 = 0xb11d, .data3 = 0x457e}, ++}; ++ ++struct mutex loader_mutex; ++static struct completion loader_complete; ++ ++static struct nt_list wrap_devices; ++static struct nt_list wrap_drivers; ++ ++static int wrap_device_type(int data1) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(class_guids); i++) ++ if (data1 == class_guids[i].data1) ++ return i; ++ ERROR("unknown device: 0x%x\n", data1); ++ return -1; ++} ++ ++/* load driver for given device, if not already loaded */ ++struct wrap_driver *load_wrap_driver(struct wrap_device *wd) ++{ ++ int ret; ++ struct nt_list *cur; ++ struct wrap_driver *wrap_driver; ++ ++ ENTER1("device: %04X:%04X:%04X:%04X", wd->vendor, wd->device, ++ wd->subvendor, wd->subdevice); ++ mutex_lock(&loader_mutex); ++ wrap_driver = NULL; ++ nt_list_for_each(cur, &wrap_drivers) { ++ wrap_driver = container_of(cur, struct wrap_driver, list); ++ if (!stricmp(wrap_driver->name, wd->driver_name)) { ++ TRACE1("driver %s already loaded", wrap_driver->name); ++ break; ++ } else ++ wrap_driver = NULL; ++ } ++ mutex_unlock(&loader_mutex); ++ ++ if (!wrap_driver) { ++ char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DRIVER, ++#if DEBUG >= 1 ++ "1", ++#else ++ "0", ++#endif ++ UTILS_VERSION, wd->driver_name, ++ wd->conf_file_name, NULL}; ++ char *env[] = {NULL}; ++ ++ TRACE1("loading driver %s", wd->driver_name); ++ mutex_lock(&loader_mutex); ++ reinit_completion(&loader_complete); ++ ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, ++ UMH_WAIT_PROC); ++ if (ret) { ++ mutex_unlock(&loader_mutex); ++ ERROR("couldn't load driver %s; check system log " ++ "for messages from 'loadndisdriver'", ++ wd->driver_name); ++ EXIT1(return NULL); ++ } ++ wait_for_completion(&loader_complete); ++ TRACE1("%s", wd->driver_name); ++ wrap_driver = NULL; ++ nt_list_for_each(cur, &wrap_drivers) { ++ wrap_driver = container_of(cur, struct wrap_driver, ++ list); ++ if (!stricmp(wrap_driver->name, wd->driver_name)) { ++ wd->driver = wrap_driver; ++ break; ++ } else ++ wrap_driver = NULL; ++ } ++ mutex_unlock(&loader_mutex); ++ if (wrap_driver) ++ TRACE1("driver %s is loaded", wrap_driver->name); ++ else ++ ERROR("couldn't load driver '%s'", wd->driver_name); ++ } ++ EXIT1(return wrap_driver); ++} ++ ++/* load the driver files from userspace. */ ++static int load_sys_files(struct wrap_driver *driver, ++ struct load_driver *load_driver) ++{ ++ int i, err; ++ ++ TRACE1("num_pe_images = %d", load_driver->num_sys_files); ++ TRACE1("loading driver: %s", load_driver->name); ++ strncpy(driver->name, load_driver->name, sizeof(driver->name)); ++ driver->name[sizeof(driver->name)-1] = 0; ++ TRACE1("driver: %s", driver->name); ++ err = 0; ++ driver->num_pe_images = 0; ++ for (i = 0; i < load_driver->num_sys_files; i++) { ++ struct pe_image *pe_image; ++ pe_image = &driver->pe_images[driver->num_pe_images]; ++ ++ strncpy(pe_image->name, load_driver->sys_files[i].name, ++ sizeof(pe_image->name)); ++ pe_image->name[sizeof(pe_image->name)-1] = 0; ++ TRACE1("image size: %zu bytes", load_driver->sys_files[i].size); ++ ++#ifdef CONFIG_X86_64 ++#ifdef PAGE_KERNEL_EXECUTABLE ++ pe_image->image = ++ __vmalloc(load_driver->sys_files[i].size, ++ GFP_KERNEL | __GFP_HIGHMEM, ++ PAGE_KERNEL_EXECUTABLE); ++#elif defined PAGE_KERNEL_EXEC ++ pe_image->image = ++ __vmalloc(load_driver->sys_files[i].size, ++ GFP_KERNEL | __GFP_HIGHMEM, ++ PAGE_KERNEL_EXEC); ++#else ++#error x86_64 should have either PAGE_KERNEL_EXECUTABLE or PAGE_KERNEL_EXEC ++#endif ++#else ++ /* hate to play with kernel macros, but PAGE_KERNEL_EXEC is ++ * not available to modules! */ ++#ifdef cpu_has_nx ++ if (cpu_has_nx) ++ pe_image->image = ++ __vmalloc(load_driver->sys_files[i].size, ++ GFP_KERNEL | __GFP_HIGHMEM, ++ __pgprot(__PAGE_KERNEL & ~_PAGE_NX)); ++ else ++ pe_image->image = ++ vmalloc(load_driver->sys_files[i].size); ++#else ++ pe_image->image = ++ vmalloc(load_driver->sys_files[i].size); ++#endif ++#endif ++ if (!pe_image->image) { ++ ERROR("couldn't allocate memory"); ++ err = -ENOMEM; ++ break; ++ } ++ TRACE1("image is at %p", pe_image->image); ++ ++ if (copy_from_user(pe_image->image, ++ load_driver->sys_files[i].data, ++ load_driver->sys_files[i].size)) { ++ ERROR("couldn't load file %s", ++ load_driver->sys_files[i].name); ++ err = -EFAULT; ++ break; ++ } ++ pe_image->size = load_driver->sys_files[i].size; ++ driver->num_pe_images++; ++ } ++ ++ if (!err && link_pe_images(driver->pe_images, driver->num_pe_images)) { ++ ERROR("couldn't prepare driver '%s'", load_driver->name); ++ err = -EINVAL; ++ } ++ ++ if (driver->num_pe_images < load_driver->num_sys_files || err) { ++ for (i = 0; i < driver->num_pe_images; i++) ++ if (driver->pe_images[i].image) ++ vfree(driver->pe_images[i].image); ++ driver->num_pe_images = 0; ++ EXIT1(return err); ++ } else ++ EXIT1(return 0); ++} ++ ++struct wrap_bin_file *get_bin_file(char *bin_file_name) ++{ ++ int i = 0; ++ struct wrap_driver *driver, *cur; ++ ++ ENTER1("%s", bin_file_name); ++ mutex_lock(&loader_mutex); ++ driver = NULL; ++ nt_list_for_each_entry(cur, &wrap_drivers, list) { ++ for (i = 0; i < cur->num_bin_files; i++) ++ if (!stricmp(cur->bin_files[i].name, bin_file_name)) { ++ driver = cur; ++ break; ++ } ++ if (driver) ++ break; ++ } ++ mutex_unlock(&loader_mutex); ++ if (!driver) { ++ TRACE1("couldn't find bin file '%s'", bin_file_name); ++ return NULL; ++ } ++ ++ if (!driver->bin_files[i].data) { ++ char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_BIN_FILE, ++#if DEBUG >= 1 ++ "1", ++#else ++ "0", ++#endif ++ UTILS_VERSION, driver->name, ++ driver->bin_files[i].name, NULL}; ++ char *env[] = {NULL}; ++ int ret; ++ ++ TRACE1("loading bin file %s/%s", driver->name, ++ driver->bin_files[i].name); ++ mutex_lock(&loader_mutex); ++ reinit_completion(&loader_complete); ++ ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, ++ UMH_WAIT_PROC); ++ if (ret) { ++ mutex_unlock(&loader_mutex); ++ ERROR("couldn't load file %s/%s; check system log " ++ "for messages from 'loadndisdriver' (%d)", ++ driver->name, driver->bin_files[i].name, ret); ++ EXIT1(return NULL); ++ } ++ wait_for_completion(&loader_complete); ++ mutex_unlock(&loader_mutex); ++ if (!driver->bin_files[i].data) { ++ WARNING("couldn't load binary file %s", ++ driver->bin_files[i].name); ++ EXIT1(return NULL); ++ } ++ } ++ EXIT2(return &(driver->bin_files[i])); ++} ++ ++/* called with loader_mutex down */ ++static int add_bin_file(struct load_driver_file *driver_file) ++{ ++ struct wrap_driver *driver, *cur; ++ struct wrap_bin_file *bin_file; ++ int i = 0; ++ ++ driver = NULL; ++ nt_list_for_each_entry(cur, &wrap_drivers, list) { ++ for (i = 0; i < cur->num_bin_files; i++) ++ if (!stricmp(cur->bin_files[i].name, ++ driver_file->name)) { ++ driver = cur; ++ break; ++ } ++ if (driver) ++ break; ++ } ++ if (!driver) { ++ ERROR("couldn't find %s", driver_file->name); ++ return -EINVAL; ++ } ++ bin_file = &driver->bin_files[i]; ++ strncpy(bin_file->name, driver_file->name, sizeof(bin_file->name)); ++ bin_file->name[sizeof(bin_file->name)-1] = 0; ++ bin_file->data = vmalloc(driver_file->size); ++ if (!bin_file->data) { ++ ERROR("couldn't allocate memory"); ++ return -ENOMEM; ++ } ++ bin_file->size = driver_file->size; ++ if (copy_from_user(bin_file->data, driver_file->data, bin_file->size)) { ++ ERROR("couldn't copy data"); ++ free_bin_file(bin_file); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++void free_bin_file(struct wrap_bin_file *bin_file) ++{ ++ TRACE2("unloading %s", bin_file->name); ++ if (bin_file->data) ++ vfree(bin_file->data); ++ bin_file->data = NULL; ++ bin_file->size = 0; ++ EXIT2(return); ++} ++ ++/* load firmware files from userspace */ ++static int load_bin_files_info(struct wrap_driver *driver, ++ struct load_driver *load_driver) ++{ ++ struct wrap_bin_file *bin_files; ++ int i; ++ ++ ENTER1("%s, %d", load_driver->name, load_driver->num_bin_files); ++ driver->num_bin_files = 0; ++ driver->bin_files = NULL; ++ if (load_driver->num_bin_files == 0) ++ EXIT1(return 0); ++ bin_files = kzalloc(load_driver->num_bin_files * sizeof(*bin_files), ++ GFP_KERNEL); ++ if (!bin_files) { ++ ERROR("couldn't allocate memory"); ++ EXIT1(return -ENOMEM); ++ } ++ ++ for (i = 0; i < load_driver->num_bin_files; i++) { ++ strncpy(bin_files[i].name, load_driver->bin_files[i].name, ++ sizeof(bin_files[i].name)); ++ bin_files[i].name[sizeof(bin_files[i].name)-1] = 0; ++ TRACE2("loaded bin file %s", bin_files[i].name); ++ } ++ driver->num_bin_files = load_driver->num_bin_files; ++ driver->bin_files = bin_files; ++ EXIT1(return 0); ++} ++ ++/* load settings for a device. called with loader_mutex down */ ++static int load_settings(struct wrap_driver *wrap_driver, ++ struct load_driver *load_driver) ++{ ++ int i, num_settings; ++ ++ ENTER1("%p, %p", wrap_driver, load_driver); ++ ++ num_settings = 0; ++ for (i = 0; i < load_driver->num_settings; i++) { ++ struct load_device_setting *load_setting = ++ &load_driver->settings[i]; ++ struct wrap_device_setting *setting; ++ ULONG data1; ++ ++ setting = kzalloc(sizeof(*setting), GFP_KERNEL); ++ if (!setting) { ++ ERROR("couldn't allocate memory"); ++ break; ++ } ++ strncpy(setting->name, load_setting->name, ++ sizeof(setting->name)); ++ setting->name[sizeof(setting->name)-1] = 0; ++ strncpy(setting->value, load_setting->value, ++ sizeof(setting->value)); ++ setting->value[sizeof(setting->value)-1] = 0; ++ TRACE2("%p: %s=%s", setting, setting->name, setting->value); ++ ++ if (strcmp(setting->name, "driver_version") == 0) { ++ strncpy(wrap_driver->version, setting->value, ++ sizeof(wrap_driver->version)); ++ wrap_driver->version[sizeof(wrap_driver->version)-1] = 0; ++ } else if (strcmp(setting->name, "class_guid") == 0 && ++ sscanf(setting->value, "%x", &data1) == 1) { ++ wrap_driver->dev_type = wrap_device_type(data1); ++ if (wrap_driver->dev_type < 0) { ++ WARNING("unknown guid: %x", data1); ++ wrap_driver->dev_type = 0; ++ } ++ } ++ InsertTailList(&wrap_driver->settings, &setting->list); ++ num_settings++; ++ } ++ /* it is not a fatal error if some settings couldn't be loaded */ ++ if (num_settings > 0) ++ EXIT1(return 0); ++ else ++ EXIT1(return -EINVAL); ++} ++ ++void unload_wrap_device(struct wrap_device *wd) ++{ ++ struct nt_list *cur; ++ ENTER1("unloading device %p (%04X:%04X:%04X:%04X), driver %s", wd, ++ wd->vendor, wd->device, wd->subvendor, wd->subdevice, ++ wd->driver_name); ++ mutex_lock(&loader_mutex); ++ while ((cur = RemoveHeadList(&wd->settings))) { ++ struct wrap_device_setting *setting; ++ setting = container_of(cur, struct wrap_device_setting, list); ++ kfree(setting); ++ } ++ RemoveEntryList(&wd->list); ++ mutex_unlock(&loader_mutex); ++ kfree(wd); ++ EXIT1(return); ++} ++ ++/* should be called with loader_mutex down */ ++void unload_wrap_driver(struct wrap_driver *driver) ++{ ++ int i; ++ struct driver_object *drv_obj; ++ struct nt_list *cur, *next; ++ ++ ENTER1("unloading driver: %s (%p)", driver->name, driver); ++ TRACE1("freeing %d images", driver->num_pe_images); ++ drv_obj = driver->drv_obj; ++ for (i = 0; i < driver->num_pe_images; i++) ++ if (driver->pe_images[i].image) { ++ TRACE1("freeing image at %p", ++ driver->pe_images[i].image); ++ vfree(driver->pe_images[i].image); ++ } ++ ++ TRACE1("freeing %d bin files", driver->num_bin_files); ++ for (i = 0; i < driver->num_bin_files; i++) { ++ TRACE1("freeing image at %p", driver->bin_files[i].data); ++ if (driver->bin_files[i].data) ++ vfree(driver->bin_files[i].data); ++ } ++ kfree(driver->bin_files); ++ RtlFreeUnicodeString(&drv_obj->name); ++ RemoveEntryList(&driver->list); ++ nt_list_for_each_safe(cur, next, &driver->settings) { ++ struct wrap_device_setting *setting; ++ struct ndis_configuration_parameter *param; ++ ++ setting = container_of(cur, struct wrap_device_setting, list); ++ TRACE2("%p", setting); ++ param = setting->encoded; ++ if (param) { ++ TRACE2("%p", param); ++ if (param->type == NdisParameterString) ++ RtlFreeUnicodeString(¶m->data.string); ++ ExFreePool(param); ++ } ++ kfree(setting); ++ } ++ /* this frees driver */ ++ free_custom_extensions(drv_obj->drv_ext); ++ kfree(drv_obj->drv_ext); ++ TRACE1("drv_obj: %p", drv_obj); ++ ++ EXIT1(return); ++} ++ ++/* call the entry point of the driver */ ++static int start_wrap_driver(struct wrap_driver *driver) ++{ ++ int i; ++ NTSTATUS ret, res; ++ struct driver_object *drv_obj; ++ typeof(driver->pe_images[0].entry) entry; ++ ++ ENTER1("%s", driver->name); ++ drv_obj = driver->drv_obj; ++ for (ret = res = 0, i = 0; i < driver->num_pe_images; i++) ++ /* dlls are already started by loader */ ++ if (driver->pe_images[i].type == IMAGE_FILE_EXECUTABLE_IMAGE) { ++ entry = driver->pe_images[i].entry; ++ drv_obj->start = driver->pe_images[i].entry; ++ drv_obj->driver_size = driver->pe_images[i].size; ++ TRACE1("entry: %p, %p, drv_obj: %p", ++ entry, *entry, drv_obj); ++ res = LIN2WIN2(entry, drv_obj, &drv_obj->name); ++ ret |= res; ++ TRACE1("entry returns %08X", res); ++ break; ++ } ++ if (ret) { ++ ERROR("driver initialization failed: %08X", ret); ++ RtlFreeUnicodeString(&drv_obj->name); ++ /* this frees ndis_driver */ ++ free_custom_extensions(drv_obj->drv_ext); ++ kfree(drv_obj->drv_ext); ++ TRACE1("drv_obj: %p", drv_obj); ++ ObDereferenceObject(drv_obj); ++ EXIT1(return -EINVAL); ++ } ++ EXIT1(return 0); ++} ++ ++/* ++ * add driver to list of loaded driver but make sure this driver is ++ * not loaded before. called with loader_mutex down ++ */ ++static int add_wrap_driver(struct wrap_driver *driver) ++{ ++ struct wrap_driver *tmp; ++ ++ ENTER1("name: %s", driver->name); ++ nt_list_for_each_entry(tmp, &wrap_drivers, list) { ++ if (stricmp(tmp->name, driver->name) == 0) { ++ ERROR("cannot add duplicate driver"); ++ EXIT1(return -EBUSY); ++ } ++ } ++ InsertHeadList(&wrap_drivers, &driver->list); ++ EXIT1(return 0); ++} ++ ++/* load a driver from userspace and initialize it. called with ++ * loader_mutex down */ ++static int load_user_space_driver(struct load_driver *load_driver) ++{ ++ struct driver_object *drv_obj; ++ struct ansi_string ansi_reg; ++ struct wrap_driver *wrap_driver = NULL; ++ ++ ENTER1("%p", load_driver); ++ drv_obj = allocate_object(sizeof(*drv_obj), OBJECT_TYPE_DRIVER, NULL); ++ if (!drv_obj) { ++ ERROR("couldn't allocate memory"); ++ EXIT1(return -ENOMEM); ++ } ++ TRACE1("drv_obj: %p", drv_obj); ++ drv_obj->drv_ext = kzalloc(sizeof(*(drv_obj->drv_ext)), GFP_KERNEL); ++ if (!drv_obj->drv_ext) { ++ ERROR("couldn't allocate memory"); ++ ObDereferenceObject(drv_obj); ++ EXIT1(return -ENOMEM); ++ } ++ InitializeListHead(&drv_obj->drv_ext->custom_ext); ++ if (IoAllocateDriverObjectExtension(drv_obj, ++ (void *)WRAP_DRIVER_CLIENT_ID, ++ sizeof(*wrap_driver), ++ (void **)&wrap_driver) != ++ STATUS_SUCCESS) ++ EXIT1(return -ENOMEM); ++ TRACE1("driver: %p", wrap_driver); ++ memset(wrap_driver, 0, sizeof(*wrap_driver)); ++ InitializeListHead(&wrap_driver->list); ++ InitializeListHead(&wrap_driver->settings); ++ wrap_driver->drv_obj = drv_obj; ++ RtlInitAnsiString(&ansi_reg, "/tmp"); ++ if (RtlAnsiStringToUnicodeString(&drv_obj->name, &ansi_reg, TRUE) != ++ STATUS_SUCCESS) { ++ ERROR("couldn't initialize registry path"); ++ free_custom_extensions(drv_obj->drv_ext); ++ kfree(drv_obj->drv_ext); ++ TRACE1("drv_obj: %p", drv_obj); ++ ObDereferenceObject(drv_obj); ++ EXIT1(return -EINVAL); ++ } ++ strncpy(wrap_driver->name, load_driver->name, sizeof(wrap_driver->name)); ++ wrap_driver->name[sizeof(wrap_driver->name)-1] = 0; ++ if (load_sys_files(wrap_driver, load_driver) || ++ load_bin_files_info(wrap_driver, load_driver) || ++ load_settings(wrap_driver, load_driver) || ++ start_wrap_driver(wrap_driver) || ++ add_wrap_driver(wrap_driver)) { ++ unload_wrap_driver(wrap_driver); ++ ObDereferenceObject(drv_obj); ++ EXIT1(return -EINVAL); ++ } else { ++ printk(KERN_INFO "%s: driver %s (%s) loaded\n", ++ DRIVER_NAME, wrap_driver->name, wrap_driver->version); ++ add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); ++ EXIT1(return 0); ++ } ++} ++ ++static struct pci_device_id wrap_pci_id_table[] = { ++ { ++ .vendor = PCI_ANY_ID, ++ .device = PCI_ANY_ID, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .class = 0, ++ .class_mask = 0, ++ .driver_data = 0 ++ } ++}; ++ ++static struct pci_driver wrap_pci_driver = { ++ .name = DRIVER_NAME, ++ .id_table = wrap_pci_id_table, ++ .probe = wrap_pnp_start_pci_device, ++ .remove = wrap_pnp_remove_pci_device, ++ .suspend = wrap_pnp_suspend_pci_device, ++ .resume = wrap_pnp_resume_pci_device, ++}; ++ ++#ifdef ENABLE_USB ++static struct usb_device_id wrap_usb_id_table[] = { ++ { ++ .driver_info = 1 ++ }, ++}; ++ ++static struct usb_driver wrap_usb_driver = { ++ .name = DRIVER_NAME, ++ .id_table = wrap_usb_id_table, ++ .probe = wrap_pnp_start_usb_device, ++ .disconnect = wrap_pnp_remove_usb_device, ++ .suspend = wrap_pnp_suspend_usb_device, ++ .resume = wrap_pnp_resume_usb_device, ++}; ++#endif ++ ++/* register drivers for pci and usb */ ++static void register_devices(void) ++{ ++ int res; ++ ++ res = pci_register_driver(&wrap_pci_driver); ++ if (res < 0) { ++ ERROR("couldn't register pci driver: %d", res); ++ wrap_pci_driver.name = NULL; ++ } ++ ++#ifdef ENABLE_USB ++ res = usb_register(&wrap_usb_driver); ++ if (res < 0) { ++ ERROR("couldn't register usb driver: %d", res); ++ wrap_usb_driver.name = NULL; ++ } ++#endif ++ EXIT1(return); ++} ++ ++static void unregister_devices(void) ++{ ++ struct nt_list *cur, *next; ++ ++ mutex_lock(&loader_mutex); ++ nt_list_for_each_safe(cur, next, &wrap_devices) { ++ struct wrap_device *wd; ++ wd = container_of(cur, struct wrap_device, list); ++ set_bit(HW_DISABLED, &wd->hw_status); ++ } ++ mutex_unlock(&loader_mutex); ++ ++ if (wrap_pci_driver.name) ++ pci_unregister_driver(&wrap_pci_driver); ++#ifdef ENABLE_USB ++ if (wrap_usb_driver.name) ++ usb_deregister(&wrap_usb_driver); ++#endif ++} ++ ++struct wrap_device *load_wrap_device(struct load_device *load_device) ++{ ++ int ret; ++ struct nt_list *cur; ++ struct wrap_device *wd = NULL; ++ char vendor[5], device[5], subvendor[5], subdevice[5], bus[5]; ++ ++ ENTER1("%04x, %04x, %04x, %04x", load_device->vendor, ++ load_device->device, load_device->subvendor, ++ load_device->subdevice); ++ if (sprintf(vendor, "%04x", load_device->vendor) == 4 && ++ sprintf(device, "%04x", load_device->device) == 4 && ++ sprintf(subvendor, "%04x", load_device->subvendor) == 4 && ++ sprintf(subdevice, "%04x", load_device->subdevice) == 4 && ++ sprintf(bus, "%04x", load_device->bus) == 4) { ++ char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DEVICE, ++#if DEBUG >= 1 ++ "1", ++#else ++ "0", ++#endif ++ UTILS_VERSION, vendor, device, ++ subvendor, subdevice, bus, NULL}; ++ char *env[] = {NULL}; ++ TRACE2("%s, %s, %s, %s, %s", vendor, device, ++ subvendor, subdevice, bus); ++ mutex_lock(&loader_mutex); ++ reinit_completion(&loader_complete); ++ ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, ++ UMH_WAIT_PROC); ++ if (ret) { ++ mutex_unlock(&loader_mutex); ++ TRACE1("couldn't load device %04x:%04x; check system " ++ "log for messages from 'loadndisdriver'", ++ load_device->vendor, load_device->device); ++ EXIT1(return NULL); ++ } ++ wait_for_completion(&loader_complete); ++ wd = NULL; ++ nt_list_for_each(cur, &wrap_devices) { ++ wd = container_of(cur, struct wrap_device, list); ++ TRACE2("%p, %04x, %04x, %04x, %04x", wd, wd->vendor, ++ wd->device, wd->subvendor, wd->subdevice); ++ if (wd->vendor == load_device->vendor && ++ wd->device == load_device->device) ++ break; ++ else ++ wd = NULL; ++ } ++ mutex_unlock(&loader_mutex); ++ } else ++ wd = NULL; ++ EXIT1(return wd); ++} ++ ++struct wrap_device *get_wrap_device(void *dev, int bus) ++{ ++ struct nt_list *cur; ++ struct wrap_device *wd; ++ ++ mutex_lock(&loader_mutex); ++ wd = NULL; ++ nt_list_for_each(cur, &wrap_devices) { ++ wd = container_of(cur, struct wrap_device, list); ++ if (bus == WRAP_PCI_BUS && ++ wrap_is_pci_bus(wd->dev_bus) && wd->pci.pdev == dev) ++ break; ++ else if (bus == WRAP_USB_BUS && ++ wrap_is_usb_bus(wd->dev_bus) && wd->usb.udev == dev) ++ break; ++ else ++ wd = NULL; ++ } ++ mutex_unlock(&loader_mutex); ++ return wd; ++} ++ ++/* called with loader_mutex is down */ ++static long wrapper_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct load_driver *load_driver; ++ struct load_device load_device; ++ struct load_driver_file load_bin_file; ++ int ret; ++ void __user *addr = (void __user *)arg; ++ ++ ENTER1("cmd: 0x%x", cmd); ++ ++ ret = 0; ++ switch (cmd) { ++ case WRAP_IOCTL_LOAD_DEVICE: ++ if (copy_from_user(&load_device, addr, sizeof(load_device))) { ++ ret = -EFAULT; ++ break; ++ } ++ TRACE2("%04x, %04x, %04x, %04x", load_device.vendor, ++ load_device.device, load_device.subvendor, ++ load_device.subdevice); ++ if (load_device.vendor) { ++ struct wrap_device *wd; ++ wd = kzalloc(sizeof(*wd), GFP_KERNEL); ++ if (!wd) { ++ ret = -ENOMEM; ++ break; ++ } ++ InitializeListHead(&wd->settings); ++ wd->dev_bus = WRAP_BUS(load_device.bus); ++ wd->vendor = load_device.vendor; ++ wd->device = load_device.device; ++ wd->subvendor = load_device.subvendor; ++ wd->subdevice = load_device.subdevice; ++ strncpy(wd->conf_file_name, load_device.conf_file_name, ++ sizeof(wd->conf_file_name)); ++ wd->conf_file_name[sizeof(wd->conf_file_name)-1] = 0; ++ strncpy(wd->driver_name, load_device.driver_name, ++ sizeof(wd->driver_name)); ++ wd->driver_name[sizeof(wd->driver_name)-1] = 0; ++ InsertHeadList(&wrap_devices, &wd->list); ++ ret = 0; ++ } else ++ ret = -EINVAL; ++ break; ++ case WRAP_IOCTL_LOAD_DRIVER: ++ TRACE1("loading driver at %p", addr); ++ load_driver = vmalloc(sizeof(*load_driver)); ++ if (!load_driver) { ++ ret = -ENOMEM; ++ break; ++ } ++ if (copy_from_user(load_driver, addr, sizeof(*load_driver))) ++ ret = -EFAULT; ++ else ++ ret = load_user_space_driver(load_driver); ++ vfree(load_driver); ++ break; ++ case WRAP_IOCTL_LOAD_BIN_FILE: ++ if (copy_from_user(&load_bin_file, addr, sizeof(load_bin_file))) ++ ret = -EFAULT; ++ else ++ ret = add_bin_file(&load_bin_file); ++ break; ++ default: ++ ERROR("unknown ioctl 0x%x", cmd); ++ ret = -EINVAL; ++ break; ++ } ++ complete(&loader_complete); ++ EXIT1(return ret); ++} ++ ++#ifdef CONFIG_COMPAT ++static int copy_load_driver_file32(struct load_driver_file *k, ++ struct load_driver_file32 __user *u) ++{ ++ u32 data; ++ ++ if (copy_from_user(&k->driver_name, &u->driver_name, ++ sizeof(u->driver_name) + sizeof(u->name))) ++ return -EFAULT; ++ ++ if (get_user(k->size, &u->size)) ++ return -EFAULT; ++ if (get_user(data, &u->data)) ++ return -EFAULT; ++ ++ k->data = (void __user *)(unsigned long)data; ++ return 0; ++} ++ ++static int copy_load_driver32(struct load_driver *k, ++ struct load_driver32 __user *u) ++{ ++ int i; ++ ++ if (copy_from_user(&k->name, &u->name, ++ sizeof(u->name) + sizeof(u->conf_file_name))) ++ return -EFAULT; ++ ++ if (get_user(k->num_sys_files, &u->num_sys_files)) ++ return -EFAULT; ++ ++ for (i = 0; i < k->num_sys_files; i++) ++ if (copy_load_driver_file32(&k->sys_files[i], &u->sys_files[i])) ++ return -EFAULT; ++ ++ if (get_user(k->num_settings, &u->num_settings)) ++ return -EFAULT; ++ ++ if (copy_from_user(&k->settings, &u->settings, ++ sizeof(u->settings[0]) * k->num_settings)) ++ return -EFAULT; ++ ++ if (get_user(k->num_bin_files, &u->num_bin_files)) ++ return -EFAULT; ++ ++ for (i = 0; i < k->num_bin_files; i++) ++ if (copy_load_driver_file32(&k->bin_files[i], &u->bin_files[i])) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static long wrapper_ioctl_compat(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret = 0; ++ void __user *addr = (void __user *)arg; ++ struct load_driver *kdriver; ++ struct load_driver32 __user *udriver = addr; ++ struct load_driver_file kfile; ++ struct load_driver_file32 __user *ufile = addr; ++ ++ ENTER1("cmd: 0x%x", cmd); ++ ++ switch (cmd) { ++ case WRAP_IOCTL_LOAD_DEVICE32: ++ return wrapper_ioctl(file, WRAP_IOCTL_LOAD_DEVICE, arg); ++ case WRAP_IOCTL_LOAD_DRIVER32: ++ TRACE1("loading driver at %p", addr); ++ kdriver = vmalloc(sizeof(*kdriver)); ++ if (!kdriver) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ ret = copy_load_driver32(kdriver, udriver); ++ if (!ret) ++ ret = load_user_space_driver(kdriver); ++ ++ vfree(kdriver); ++ break; ++ case WRAP_IOCTL_LOAD_BIN_FILE32: ++ ret = copy_load_driver_file32(&kfile, ufile); ++ if (ret) ++ break; ++ ++ ret = add_bin_file(&kfile); ++ break; ++ default: ++ ERROR("unknown ioctl 0x%x", cmd); ++ ret = -EINVAL; ++ break; ++ } ++ complete(&loader_complete); ++ EXIT1(return ret); ++} ++#endif ++ ++static int wrapper_ioctl_release(struct inode *inode, struct file *file) ++{ ++ ENTER1(""); ++ complete(&loader_complete); ++ return 0; ++} ++ ++static struct file_operations wrapper_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = wrapper_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = wrapper_ioctl_compat, ++#endif ++ .release = wrapper_ioctl_release, ++}; ++ ++static struct miscdevice wrapper_misc = { ++ .name = DRIVER_NAME, ++ .minor = MISC_DYNAMIC_MINOR, ++ .fops = &wrapper_fops ++}; ++ ++int loader_init(void) ++{ ++ int err; ++ ++ InitializeListHead(&wrap_drivers); ++ InitializeListHead(&wrap_devices); ++ mutex_init(&loader_mutex); ++ init_completion(&loader_complete); ++ if ((err = misc_register(&wrapper_misc)) < 0) { ++ ERROR("couldn't register module (%d)", err); ++ unregister_devices(); ++ EXIT1(return err); ++ } ++ register_devices(); ++ EXIT1(return 0); ++} ++ ++void loader_exit(void) ++{ ++ struct nt_list *cur, *next; ++ ++ ENTER1(""); ++ misc_deregister(&wrapper_misc); ++ unregister_devices(); ++ mutex_lock(&loader_mutex); ++ nt_list_for_each_safe(cur, next, &wrap_drivers) { ++ struct wrap_driver *driver; ++ driver = container_of(cur, struct wrap_driver, list); ++ unload_wrap_driver(driver); ++ } ++ mutex_unlock(&loader_mutex); ++ EXIT1(return); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.h +--- linux-3.12.2/3rdparty/ndiswrapper/loader.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.h 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,108 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _LOADER_H_ ++#define _LOADER_H_ ++ ++#include "ndiswrapper.h" ++ ++#ifndef __KERNEL__ ++#define __user ++#endif ++ ++struct load_driver_file { ++ char driver_name[MAX_DRIVER_NAME_LEN]; ++ char name[MAX_DRIVER_NAME_LEN]; ++ size_t size; ++ void __user *data; ++}; ++ ++struct load_device_setting { ++ char name[MAX_SETTING_NAME_LEN]; ++ char value[MAX_SETTING_VALUE_LEN]; ++}; ++ ++struct load_device { ++ int bus; ++ int vendor; ++ int device; ++ int subvendor; ++ int subdevice; ++ char conf_file_name[MAX_DRIVER_NAME_LEN]; ++ char driver_name[MAX_DRIVER_NAME_LEN]; ++}; ++ ++struct load_driver { ++ char name[MAX_DRIVER_NAME_LEN]; ++ char conf_file_name[MAX_DRIVER_NAME_LEN]; ++ unsigned int num_sys_files; ++ struct load_driver_file sys_files[MAX_DRIVER_PE_IMAGES]; ++ unsigned int num_settings; ++ struct load_device_setting settings[MAX_DEVICE_SETTINGS]; ++ unsigned int num_bin_files; ++ struct load_driver_file bin_files[MAX_DRIVER_BIN_FILES]; ++}; ++ ++#define WRAP_IOCTL_LOAD_DEVICE _IOW(('N' + 'd' + 'i' + 'S'), 0, \ ++ struct load_device *) ++#define WRAP_IOCTL_LOAD_DRIVER _IOW(('N' + 'd' + 'i' + 'S'), 1, \ ++ struct load_driver *) ++#define WRAP_IOCTL_LOAD_BIN_FILE _IOW(('N' + 'd' + 'i' + 'S'), 2, \ ++ struct load_driver_file *) ++ ++#ifdef CONFIG_COMPAT ++struct load_driver_file32 { ++ char driver_name[MAX_DRIVER_NAME_LEN]; ++ char name[MAX_DRIVER_NAME_LEN]; ++ u32 size; ++ u32 data; ++}; ++ ++struct load_driver32 { ++ char name[MAX_DRIVER_NAME_LEN]; ++ char conf_file_name[MAX_DRIVER_NAME_LEN]; ++ u32 num_sys_files; ++ struct load_driver_file32 sys_files[MAX_DRIVER_PE_IMAGES]; ++ u32 num_settings; ++ struct load_device_setting settings[MAX_DEVICE_SETTINGS]; ++ u32 num_bin_files; ++ struct load_driver_file32 bin_files[MAX_DRIVER_BIN_FILES]; ++} __packed; ++ ++#define WRAP_IOCTL_LOAD_DEVICE32 _IOW(('N' + 'd' + 'i' + 'S'), 0, u32) ++#define WRAP_IOCTL_LOAD_DRIVER32 _IOW(('N' + 'd' + 'i' + 'S'), 1, u32) ++#define WRAP_IOCTL_LOAD_BIN_FILE32 _IOW(('N' + 'd' + 'i' + 'S'), 2, u32) ++#endif ++ ++#define WRAP_CMD_LOAD_DEVICE "load_device" ++#define WRAP_CMD_LOAD_DRIVER "load_driver" ++#define WRAP_CMD_LOAD_BIN_FILE "load_bin_file" ++ ++int loader_init(void); ++void loader_exit(void); ++ ++#ifdef __KERNEL__ ++struct wrap_device *load_wrap_device(struct load_device *load_device); ++struct wrap_driver *load_wrap_driver(struct wrap_device *device); ++struct wrap_bin_file *get_bin_file(char *bin_file_name); ++void free_bin_file(struct wrap_bin_file *bin_file); ++void unload_wrap_driver(struct wrap_driver *driver); ++void unload_wrap_device(struct wrap_device *wd); ++struct wrap_device *get_wrap_device(void *dev, int bus_type); ++ ++extern struct mutex loader_mutex; ++#endif ++ ++#endif /* LOADER_H */ +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/longlong.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/longlong.h +--- linux-3.12.2/3rdparty/ndiswrapper/longlong.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/longlong.h 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,1333 @@ ++/* longlong.h -- definitions for mixed size 32/64 bit arithmetic. ++ Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000 ++ Free Software Foundation, Inc. ++ ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++/* You have to define the following before including this file: ++ ++ UWtype -- An unsigned type, default type for operations (typically a "word") ++ UHWtype -- An unsigned type, at least half the size of UWtype. ++ UDWtype -- An unsigned type, at least twice as large a UWtype ++ W_TYPE_SIZE -- size in bits of UWtype ++ ++ UQItype -- Unsigned 8 bit type. ++ SItype, USItype -- Signed and unsigned 32 bit types. ++ DItype, UDItype -- Signed and unsigned 64 bit types. ++ ++ On a 32 bit machine UWtype should typically be USItype; ++ on a 64 bit machine, UWtype should typically be UDItype. ++*/ ++ ++#define __BITS4 (W_TYPE_SIZE / 4) ++#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) ++#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1)) ++#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2)) ++ ++#ifndef W_TYPE_SIZE ++#define W_TYPE_SIZE 32 ++#define UWtype USItype ++#define UHWtype USItype ++#define UDWtype UDItype ++#endif ++ ++/* Define auxiliary asm macros. ++ ++ 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two ++ UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype ++ word product in HIGH_PROD and LOW_PROD. ++ ++ 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a ++ UDWtype product. This is just a variant of umul_ppmm. ++ ++ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, ++ denominator) divides a UDWtype, composed by the UWtype integers ++ HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient ++ in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less ++ than DENOMINATOR for correct operation. If, in addition, the most ++ significant bit of DENOMINATOR must be 1, then the pre-processor symbol ++ UDIV_NEEDS_NORMALIZATION is defined to 1. ++ ++ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, ++ denominator). Like udiv_qrnnd but the numbers are signed. The quotient ++ is rounded towards 0. ++ ++ 5) count_leading_zeros(count, x) counts the number of zero-bits from the ++ msb to the first nonzero bit in the UWtype X. This is the number of ++ steps X needs to be shifted left to set the msb. Undefined for X == 0, ++ unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value. ++ ++ 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts ++ from the least significant end. ++ ++ 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, ++ high_addend_2, low_addend_2) adds two UWtype integers, composed by ++ HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 ++ respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow ++ (i.e. carry out) is not stored anywhere, and is lost. ++ ++ 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, ++ high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, ++ composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and ++ LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE ++ and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, ++ and is lost. ++ ++ If any of these macros are left undefined for a particular CPU, ++ C macros are used. */ ++ ++/* The CPUs come in alphabetical order below. ++ ++ Please add support for more CPUs here, or improve the current support ++ for the CPUs below! ++ (E.g. WE32100, IBM360.) */ ++ ++#if defined (__GNUC__) && !defined (NO_ASM) ++ ++/* We sometimes need to clobber "cc" with gcc2, but that would not be ++ understood by gcc1. Use cpp to avoid major code duplication. */ ++#if __GNUC__ < 2 ++#define __CLOBBER_CC ++#define __AND_CLOBBER_CC ++#else /* __GNUC__ >= 2 */ ++#define __CLOBBER_CC : "cc" ++#define __AND_CLOBBER_CC , "cc" ++#endif /* __GNUC__ < 2 */ ++ ++#if defined (__alpha) && W_TYPE_SIZE == 64 ++#define umul_ppmm(ph, pl, m0, m1) \ ++ do { \ ++ UDItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("umulh %r1,%2,%0" \ ++ : "=r" ((UDItype) ph) \ ++ : "%rJ" (__m0), \ ++ "rI" (__m1)); \ ++ (pl) = __m0 * __m1; \ ++ } while (0) ++#define UMUL_TIME 46 ++#ifndef LONGLONG_STANDALONE ++#define udiv_qrnnd(q, r, n1, n0, d) \ ++ do { UDItype __r; \ ++ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \ ++ (r) = __r; \ ++ } while (0) ++extern UDItype __udiv_qrnnd (UDItype *, UDItype, UDItype, UDItype); ++#define UDIV_TIME 220 ++#endif /* LONGLONG_STANDALONE */ ++#ifdef __alpha_cix__ ++#define count_leading_zeros(COUNT,X) \ ++ __asm__("ctlz %1,%0" : "=r"(COUNT) : "r"(X)) ++#define count_trailing_zeros(COUNT,X) \ ++ __asm__("cttz %1,%0" : "=r"(COUNT) : "r"(X)) ++#define COUNT_LEADING_ZEROS_0 64 ++#else ++extern const UQItype __clz_tab[]; ++#define count_leading_zeros(COUNT,X) \ ++ do { \ ++ UDItype __xr = (X), __t, __a; \ ++ __asm__("cmpbge $31,%1,%0" : "=r"(__t) : "r"(__xr)); \ ++ __a = __clz_tab[__t ^ 0xff] - 1; \ ++ __asm__("extbl %1,%2,%0" : "=r"(__t) : "r"(__xr), "r"(__a)); \ ++ (COUNT) = 64 - (__clz_tab[__t] + __a*8); \ ++ } while (0) ++#define count_trailing_zeros(COUNT,X) \ ++ do { \ ++ UDItype __xr = (X), __t, __a; \ ++ __asm__("cmpbge $31,%1,%0" : "=r"(__t) : "r"(__xr)); \ ++ __t = ~__t & -~__t; \ ++ __a = ((__t & 0xCC) != 0) * 2; \ ++ __a += ((__t & 0xF0) != 0) * 4; \ ++ __a += ((__t & 0xAA) != 0); \ ++ __asm__("extbl %1,%2,%0" : "=r"(__t) : "r"(__xr), "r"(__a)); \ ++ __a <<= 3; \ ++ __t &= -__t; \ ++ __a += ((__t & 0xCC) != 0) * 2; \ ++ __a += ((__t & 0xF0) != 0) * 4; \ ++ __a += ((__t & 0xAA) != 0); \ ++ (COUNT) = __a; \ ++ } while (0) ++#endif /* __alpha_cix__ */ ++#endif /* __alpha */ ++ ++#if defined (__arc__) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("add.f %1, %4, %5\n\tadc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%r" ((USItype) (ah)), \ ++ "rIJ" ((USItype) (bh)), \ ++ "%r" ((USItype) (al)), \ ++ "rIJ" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("sub.f %1, %4, %5\n\tsbc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "r" ((USItype) (ah)), \ ++ "rIJ" ((USItype) (bh)), \ ++ "r" ((USItype) (al)), \ ++ "rIJ" ((USItype) (bl))) ++/* Call libgcc routine. */ ++#define umul_ppmm(w1, w0, u, v) \ ++do { \ ++ DWunion __w; \ ++ __w.ll = __umulsidi3 (u, v); \ ++ w1 = __w.s.high; \ ++ w0 = __w.s.low; \ ++} while (0) ++#define __umulsidi3 __umulsidi3 ++UDItype __umulsidi3 (USItype, USItype); ++#endif ++ ++#if defined (__arm__) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("adds %1, %4, %5\n\tadc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%r" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "%r" ((USItype) (al)), \ ++ "rI" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subs %1, %4, %5\n\tsbc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "r" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "r" ((USItype) (al)), \ ++ "rI" ((USItype) (bl))) ++#define umul_ppmm(xh, xl, a, b) \ ++{register USItype __t0, __t1, __t2; \ ++ __asm__ ("%@ Inlined umul_ppmm\n" \ ++ " mov %2, %5, lsr #16\n" \ ++ " mov %0, %6, lsr #16\n" \ ++ " bic %3, %5, %2, lsl #16\n" \ ++ " bic %4, %6, %0, lsl #16\n" \ ++ " mul %1, %3, %4\n" \ ++ " mul %4, %2, %4\n" \ ++ " mul %3, %0, %3\n" \ ++ " mul %0, %2, %0\n" \ ++ " adds %3, %4, %3\n" \ ++ " addcs %0, %0, #65536\n" \ ++ " adds %1, %1, %3, lsl #16\n" \ ++ " adc %0, %0, %3, lsr #16" \ ++ : "=&r" ((USItype) (xh)), \ ++ "=r" ((USItype) (xl)), \ ++ "=&r" (__t0), "=&r" (__t1), "=r" (__t2) \ ++ : "r" ((USItype) (a)), \ ++ "r" ((USItype) (b)));} ++#define UMUL_TIME 20 ++#define UDIV_TIME 100 ++#endif /* __arm__ */ ++ ++#if defined (__hppa) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("add %4,%5,%1\n\taddc %2,%3,%0" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%rM" ((USItype) (ah)), \ ++ "rM" ((USItype) (bh)), \ ++ "%rM" ((USItype) (al)), \ ++ "rM" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("sub %4,%5,%1\n\tsubb %2,%3,%0" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "rM" ((USItype) (ah)), \ ++ "rM" ((USItype) (bh)), \ ++ "rM" ((USItype) (al)), \ ++ "rM" ((USItype) (bl))) ++#if defined (_PA_RISC1_1) ++#define umul_ppmm(w1, w0, u, v) \ ++ do { \ ++ union \ ++ { \ ++ UDItype __f; \ ++ struct {USItype __w1, __w0;} __w1w0; \ ++ } __t; \ ++ __asm__ ("xmpyu %1,%2,%0" \ ++ : "=x" (__t.__f) \ ++ : "x" ((USItype) (u)), \ ++ "x" ((USItype) (v))); \ ++ (w1) = __t.__w1w0.__w1; \ ++ (w0) = __t.__w1w0.__w0; \ ++ } while (0) ++#define UMUL_TIME 8 ++#else ++#define UMUL_TIME 30 ++#endif ++#define UDIV_TIME 40 ++#define count_leading_zeros(count, x) \ ++ do { \ ++ USItype __tmp; \ ++ __asm__ ( \ ++ "ldi 1,%0\n" \ ++" extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \ ++" extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n"\ ++" ldo 16(%0),%0 ; Yes. Perform add.\n" \ ++" extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \ ++" extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n"\ ++" ldo 8(%0),%0 ; Yes. Perform add.\n" \ ++" extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \ ++" extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n"\ ++" ldo 4(%0),%0 ; Yes. Perform add.\n" \ ++" extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \ ++" extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n"\ ++" ldo 2(%0),%0 ; Yes. Perform add.\n" \ ++" extru %1,30,1,%1 ; Extract bit 1.\n" \ ++" sub %0,%1,%0 ; Subtract it.\n" \ ++ : "=r" (count), "=r" (__tmp) : "1" (x)); \ ++ } while (0) ++#endif ++ ++#if (defined (__i370__) || defined (__mvs__)) && W_TYPE_SIZE == 32 ++#define umul_ppmm(xh, xl, m0, m1) \ ++ do { \ ++ union {UDItype __ll; \ ++ struct {USItype __h, __l;} __i; \ ++ } __xx; \ ++ USItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("mr %0,%3" \ ++ : "=r" (__xx.__i.__h), \ ++ "=r" (__xx.__i.__l) \ ++ : "%1" (__m0), \ ++ "r" (__m1)); \ ++ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \ ++ (xh) += ((((SItype) __m0 >> 31) & __m1) \ ++ + (((SItype) __m1 >> 31) & __m0)); \ ++ } while (0) ++#define smul_ppmm(xh, xl, m0, m1) \ ++ do { \ ++ union {DItype __ll; \ ++ struct {USItype __h, __l;} __i; \ ++ } __xx; \ ++ __asm__ ("mr %0,%3" \ ++ : "=r" (__xx.__i.__h), \ ++ "=r" (__xx.__i.__l) \ ++ : "%1" (m0), \ ++ "r" (m1)); \ ++ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \ ++ } while (0) ++#define sdiv_qrnnd(q, r, n1, n0, d) \ ++ do { \ ++ union {DItype __ll; \ ++ struct {USItype __h, __l;} __i; \ ++ } __xx; \ ++ __xx.__i.__h = n1; __xx.__i.__l = n0; \ ++ __asm__ ("dr %0,%2" \ ++ : "=r" (__xx.__ll) \ ++ : "0" (__xx.__ll), "r" (d)); \ ++ (q) = __xx.__i.__l; (r) = __xx.__i.__h; \ ++ } while (0) ++#endif ++ ++#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("addl %5,%1\n\tadcl %3,%0" \ ++ : "=r" (sh), \ ++ "=&r" (sl) \ ++ : "%0" ((USItype) (ah)), \ ++ "g" ((USItype) (bh)), \ ++ "%1" ((USItype) (al)), \ ++ "g" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subl %5,%1\n\tsbbl %3,%0" \ ++ : "=r" (sh), \ ++ "=&r" (sl) \ ++ : "0" ((USItype) (ah)), \ ++ "g" ((USItype) (bh)), \ ++ "1" ((USItype) (al)), \ ++ "g" ((USItype) (bl))) ++#define umul_ppmm(w1, w0, u, v) \ ++ __asm__ ("mull %3" \ ++ : "=a" (w0), \ ++ "=d" (w1) \ ++ : "%0" ((USItype) (u)), \ ++ "rm" ((USItype) (v))) ++#define udiv_qrnnd(q, r, n1, n0, dv) \ ++ __asm__ ("divl %4" \ ++ : "=a" (q), \ ++ "=d" (r) \ ++ : "0" ((USItype) (n0)), \ ++ "1" ((USItype) (n1)), \ ++ "rm" ((USItype) (dv))) ++#define count_leading_zeros(count, x) \ ++ do { \ ++ USItype __cbtmp; \ ++ __asm__ ("bsrl %1,%0" \ ++ : "=r" (__cbtmp) : "rm" ((USItype) (x))); \ ++ (count) = __cbtmp ^ 31; \ ++ } while (0) ++#define count_trailing_zeros(count, x) \ ++ __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x))) ++#define UMUL_TIME 40 ++#define UDIV_TIME 40 ++#endif /* 80x86 */ ++ ++#if defined (__i960__) && W_TYPE_SIZE == 32 ++#define umul_ppmm(w1, w0, u, v) \ ++ ({union {UDItype __ll; \ ++ struct {USItype __l, __h;} __i; \ ++ } __xx; \ ++ __asm__ ("emul %2,%1,%0" \ ++ : "=d" (__xx.__ll) \ ++ : "%dI" ((USItype) (u)), \ ++ "dI" ((USItype) (v))); \ ++ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;}) ++#define __umulsidi3(u, v) \ ++ ({UDItype __w; \ ++ __asm__ ("emul %2,%1,%0" \ ++ : "=d" (__w) \ ++ : "%dI" ((USItype) (u)), \ ++ "dI" ((USItype) (v))); \ ++ __w; }) ++#endif /* __i960__ */ ++ ++#if defined (__M32R__) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ /* The cmp clears the condition bit. */ \ ++ __asm__ ("cmp %0,%0\n\taddx %%5,%1\n\taddx %%3,%0" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%0" ((USItype) (ah)), \ ++ "r" ((USItype) (bh)), \ ++ "%1" ((USItype) (al)), \ ++ "r" ((USItype) (bl)) \ ++ : "cbit") ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ /* The cmp clears the condition bit. */ \ ++ __asm__ ("cmp %0,%0\n\tsubx %5,%1\n\tsubx %3,%0" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "0" ((USItype) (ah)), \ ++ "r" ((USItype) (bh)), \ ++ "1" ((USItype) (al)), \ ++ "r" ((USItype) (bl)) \ ++ : "cbit") ++#endif /* __M32R__ */ ++ ++#if defined (__mc68000__) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \ ++ : "=d" ((USItype) (sh)), \ ++ "=&d" ((USItype) (sl)) \ ++ : "%0" ((USItype) (ah)), \ ++ "d" ((USItype) (bh)), \ ++ "%1" ((USItype) (al)), \ ++ "g" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0" \ ++ : "=d" ((USItype) (sh)), \ ++ "=&d" ((USItype) (sl)) \ ++ : "0" ((USItype) (ah)), \ ++ "d" ((USItype) (bh)), \ ++ "1" ((USItype) (al)), \ ++ "g" ((USItype) (bl))) ++ ++/* The '020, '030, '040 and CPU32 have 32x32->64 and 64/32->32q-32r. */ ++#if defined (__mc68020__) || defined(mc68020) \ ++ || defined(__mc68030__) || defined(mc68030) \ ++ || defined(__mc68040__) || defined(mc68040) \ ++ || defined(__mcpu32__) || defined(mcpu32) ++#define umul_ppmm(w1, w0, u, v) \ ++ __asm__ ("mulu%.l %3,%1:%0" \ ++ : "=d" ((USItype) (w0)), \ ++ "=d" ((USItype) (w1)) \ ++ : "%0" ((USItype) (u)), \ ++ "dmi" ((USItype) (v))) ++#define UMUL_TIME 45 ++#define udiv_qrnnd(q, r, n1, n0, d) \ ++ __asm__ ("divu%.l %4,%1:%0" \ ++ : "=d" ((USItype) (q)), \ ++ "=d" ((USItype) (r)) \ ++ : "0" ((USItype) (n0)), \ ++ "1" ((USItype) (n1)), \ ++ "dmi" ((USItype) (d))) ++#define UDIV_TIME 90 ++#define sdiv_qrnnd(q, r, n1, n0, d) \ ++ __asm__ ("divs%.l %4,%1:%0" \ ++ : "=d" ((USItype) (q)), \ ++ "=d" ((USItype) (r)) \ ++ : "0" ((USItype) (n0)), \ ++ "1" ((USItype) (n1)), \ ++ "dmi" ((USItype) (d))) ++ ++#else /* not mc68020 */ ++#if !defined(__mcf5200__) ++/* %/ inserts REGISTER_PREFIX, %# inserts IMMEDIATE_PREFIX. */ ++#define umul_ppmm(xh, xl, a, b) \ ++ __asm__ ("| Inlined umul_ppmm\n" \ ++ " move%.l %2,%/d0\n" \ ++ " move%.l %3,%/d1\n" \ ++ " move%.l %/d0,%/d2\n" \ ++ " swap %/d0\n" \ ++ " move%.l %/d1,%/d3\n" \ ++ " swap %/d1\n" \ ++ " move%.w %/d2,%/d4\n" \ ++ " mulu %/d3,%/d4\n" \ ++ " mulu %/d1,%/d2\n" \ ++ " mulu %/d0,%/d3\n" \ ++ " mulu %/d0,%/d1\n" \ ++ " move%.l %/d4,%/d0\n" \ ++ " eor%.w %/d0,%/d0\n" \ ++ " swap %/d0\n" \ ++ " add%.l %/d0,%/d2\n" \ ++ " add%.l %/d3,%/d2\n" \ ++ " jcc 1f\n" \ ++ " add%.l %#65536,%/d1\n" \ ++ "1: swap %/d2\n" \ ++ " moveq %#0,%/d0\n" \ ++ " move%.w %/d2,%/d0\n" \ ++ " move%.w %/d4,%/d2\n" \ ++ " move%.l %/d2,%1\n" \ ++ " add%.l %/d1,%/d0\n" \ ++ " move%.l %/d0,%0" \ ++ : "=g" ((USItype) (xh)), \ ++ "=g" ((USItype) (xl)) \ ++ : "g" ((USItype) (a)), \ ++ "g" ((USItype) (b)) \ ++ : "d0", "d1", "d2", "d3", "d4") ++#define UMUL_TIME 100 ++#define UDIV_TIME 400 ++#endif /* not mcf5200 */ ++#endif /* not mc68020 */ ++ ++/* The '020, '030, '040 and '060 have bitfield insns. */ ++#if defined (__mc68020__) || defined(mc68020) \ ++ || defined(__mc68030__) || defined(mc68030) \ ++ || defined(__mc68040__) || defined(mc68040) \ ++ || defined(__mc68060__) || defined(mc68060) ++#define count_leading_zeros(count, x) \ ++ __asm__ ("bfffo %1{%b2:%b2},%0" \ ++ : "=d" ((USItype) (count)) \ ++ : "od" ((USItype) (x)), "n" (0)) ++#endif ++#endif /* mc68000 */ ++ ++#if defined (__m88000__) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%rJ" ((USItype) (ah)), \ ++ "rJ" ((USItype) (bh)), \ ++ "%rJ" ((USItype) (al)), \ ++ "rJ" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "rJ" ((USItype) (ah)), \ ++ "rJ" ((USItype) (bh)), \ ++ "rJ" ((USItype) (al)), \ ++ "rJ" ((USItype) (bl))) ++#define count_leading_zeros(count, x) \ ++ do { \ ++ USItype __cbtmp; \ ++ __asm__ ("ff1 %0,%1" \ ++ : "=r" (__cbtmp) \ ++ : "r" ((USItype) (x))); \ ++ (count) = __cbtmp ^ 31; \ ++ } while (0) ++#define COUNT_LEADING_ZEROS_0 63 /* sic */ ++#if defined (__mc88110__) ++#define umul_ppmm(wh, wl, u, v) \ ++ do { \ ++ union {UDItype __ll; \ ++ struct {USItype __h, __l;} __i; \ ++ } __xx; \ ++ __asm__ ("mulu.d %0,%1,%2" \ ++ : "=r" (__xx.__ll) \ ++ : "r" ((USItype) (u)), \ ++ "r" ((USItype) (v))); \ ++ (wh) = __xx.__i.__h; \ ++ (wl) = __xx.__i.__l; \ ++ } while (0) ++#define udiv_qrnnd(q, r, n1, n0, d) \ ++ ({union {UDItype __ll; \ ++ struct {USItype __h, __l;} __i; \ ++ } __xx; \ ++ USItype __q; \ ++ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \ ++ __asm__ ("divu.d %0,%1,%2" \ ++ : "=r" (__q) \ ++ : "r" (__xx.__ll), \ ++ "r" ((USItype) (d))); \ ++ (r) = (n0) - __q * (d); (q) = __q; }) ++#define UMUL_TIME 5 ++#define UDIV_TIME 25 ++#else ++#define UMUL_TIME 17 ++#define UDIV_TIME 150 ++#endif /* __mc88110__ */ ++#endif /* __m88000__ */ ++ ++#if defined (__mips__) && W_TYPE_SIZE == 32 ++#define umul_ppmm(w1, w0, u, v) \ ++ __asm__ ("multu %2,%3" \ ++ : "=l" ((USItype) (w0)), \ ++ "=h" ((USItype) (w1)) \ ++ : "d" ((USItype) (u)), \ ++ "d" ((USItype) (v))) ++#define UMUL_TIME 10 ++#define UDIV_TIME 100 ++#endif /* __mips__ */ ++ ++#if defined (__ns32000__) && W_TYPE_SIZE == 32 ++#define umul_ppmm(w1, w0, u, v) \ ++ ({union {UDItype __ll; \ ++ struct {USItype __l, __h;} __i; \ ++ } __xx; \ ++ __asm__ ("meid %2,%0" \ ++ : "=g" (__xx.__ll) \ ++ : "%0" ((USItype) (u)), \ ++ "g" ((USItype) (v))); \ ++ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;}) ++#define __umulsidi3(u, v) \ ++ ({UDItype __w; \ ++ __asm__ ("meid %2,%0" \ ++ : "=g" (__w) \ ++ : "%0" ((USItype) (u)), \ ++ "g" ((USItype) (v))); \ ++ __w; }) ++#define udiv_qrnnd(q, r, n1, n0, d) \ ++ ({union {UDItype __ll; \ ++ struct {USItype __l, __h;} __i; \ ++ } __xx; \ ++ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \ ++ __asm__ ("deid %2,%0" \ ++ : "=g" (__xx.__ll) \ ++ : "0" (__xx.__ll), \ ++ "g" ((USItype) (d))); \ ++ (r) = __xx.__i.__l; (q) = __xx.__i.__h; }) ++#define count_trailing_zeros(count,x) \ ++ do { \ ++ __asm__ ("ffsd %2,%0" \ ++ : "=r" ((USItype) (count)) \ ++ : "0" ((USItype) 0), \ ++ "r" ((USItype) (x))); \ ++ } while (0) ++#endif /* __ns32000__ */ ++ ++/* FIXME: We should test _IBMR2 here when we add assembly support for the ++ system vendor compilers. ++ FIXME: What's needed for gcc PowerPC VxWorks? __vxworks__ is not good ++ enough, since that hits ARM and m68k too. */ ++#if (defined (_ARCH_PPC) /* AIX */ \ ++ || defined (_ARCH_PWR) /* AIX */ \ ++ || defined (_ARCH_COM) /* AIX */ \ ++ || defined (__powerpc__) /* gcc */ \ ++ || defined (__POWERPC__) /* BEOS */ \ ++ || defined (__ppc__) /* Darwin */ \ ++ || defined (PPC) /* GNU/Linux, SysV */ \ ++ ) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ do { \ ++ if (__builtin_constant_p (bh) && (bh) == 0) \ ++ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ ++ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ ++ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ ++ else \ ++ __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \ ++ : "=r" (sh), "=&r" (sl) \ ++ : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ ++ } while (0) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ do { \ ++ if (__builtin_constant_p (ah) && (ah) == 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ ++ else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ ++ else if (__builtin_constant_p (bh) && (bh) == 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ ++ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ ++ else \ ++ __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \ ++ : "=r" (sh), "=&r" (sl) \ ++ : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ ++ } while (0) ++#define count_leading_zeros(count, x) \ ++ __asm__ ("{cntlz|cntlzw} %0,%1" : "=r" (count) : "r" (x)) ++#define COUNT_LEADING_ZEROS_0 32 ++#if defined (_ARCH_PPC) || defined (__powerpc__) || defined (__POWERPC__) \ ++ || defined (__ppc__) || defined (PPC) || defined (__vxworks__) ++#define umul_ppmm(ph, pl, m0, m1) \ ++ do { \ ++ USItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ ++ (pl) = __m0 * __m1; \ ++ } while (0) ++#define UMUL_TIME 15 ++#define smul_ppmm(ph, pl, m0, m1) \ ++ do { \ ++ SItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ ++ (pl) = __m0 * __m1; \ ++ } while (0) ++#define SMUL_TIME 14 ++#define UDIV_TIME 120 ++#elif defined (_ARCH_PWR) ++#define UMUL_TIME 8 ++#define smul_ppmm(xh, xl, m0, m1) \ ++ __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1)) ++#define SMUL_TIME 4 ++#define sdiv_qrnnd(q, r, nh, nl, d) \ ++ __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d)) ++#define UDIV_TIME 100 ++#endif ++#endif /* 32-bit POWER architecture variants. */ ++ ++/* We should test _IBMR2 here when we add assembly support for the system ++ vendor compilers. */ ++#if (defined (_ARCH_PPC64) || defined (__powerpc64__)) && W_TYPE_SIZE == 64 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ do { \ ++ if (__builtin_constant_p (bh) && (bh) == 0) \ ++ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ ++ else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ ++ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ ++ else \ ++ __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \ ++ : "=r" (sh), "=&r" (sl) \ ++ : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ ++ } while (0) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ do { \ ++ if (__builtin_constant_p (ah) && (ah) == 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ ++ else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ ++ else if (__builtin_constant_p (bh) && (bh) == 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ ++ else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ ++ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \ ++ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ ++ else \ ++ __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \ ++ : "=r" (sh), "=&r" (sl) \ ++ : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ ++ } while (0) ++#define count_leading_zeros(count, x) \ ++ __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x)) ++#define COUNT_LEADING_ZEROS_0 64 ++#define umul_ppmm(ph, pl, m0, m1) \ ++ do { \ ++ UDItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ ++ (pl) = __m0 * __m1; \ ++ } while (0) ++#define UMUL_TIME 15 ++#define smul_ppmm(ph, pl, m0, m1) \ ++ do { \ ++ DItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ ++ (pl) = __m0 * __m1; \ ++ } while (0) ++#define SMUL_TIME 14 /* ??? */ ++#define UDIV_TIME 120 /* ??? */ ++#endif /* 64-bit PowerPC. */ ++ ++#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("a %1,%5\n\tae %0,%3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%0" ((USItype) (ah)), \ ++ "r" ((USItype) (bh)), \ ++ "%1" ((USItype) (al)), \ ++ "r" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("s %1,%5\n\tse %0,%3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "0" ((USItype) (ah)), \ ++ "r" ((USItype) (bh)), \ ++ "1" ((USItype) (al)), \ ++ "r" ((USItype) (bl))) ++#define umul_ppmm(ph, pl, m0, m1) \ ++ do { \ ++ USItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ( \ ++ "s r2,r2\n" \ ++" mts r10,%2\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" m r2,%3\n" \ ++" cas %0,r2,r0\n" \ ++" mfs r10,%1" \ ++ : "=r" ((USItype) (ph)), \ ++ "=r" ((USItype) (pl)) \ ++ : "%r" (__m0), \ ++ "r" (__m1) \ ++ : "r2"); \ ++ (ph) += ((((SItype) __m0 >> 31) & __m1) \ ++ + (((SItype) __m1 >> 31) & __m0)); \ ++ } while (0) ++#define UMUL_TIME 20 ++#define UDIV_TIME 200 ++#define count_leading_zeros(count, x) \ ++ do { \ ++ if ((x) >= 0x10000) \ ++ __asm__ ("clz %0,%1" \ ++ : "=r" ((USItype) (count)) \ ++ : "r" ((USItype) (x) >> 16)); \ ++ else \ ++ { \ ++ __asm__ ("clz %0,%1" \ ++ : "=r" ((USItype) (count)) \ ++ : "r" ((USItype) (x))); \ ++ (count) += 16; \ ++ } \ ++ } while (0) ++#endif ++ ++#if defined (__sh2__) && W_TYPE_SIZE == 32 ++#define umul_ppmm(w1, w0, u, v) \ ++ __asm__ ( \ ++ "dmulu.l %2,%3\n\tsts macl,%1\n\tsts mach,%0" \ ++ : "=r" ((USItype)(w1)), \ ++ "=r" ((USItype)(w0)) \ ++ : "r" ((USItype)(u)), \ ++ "r" ((USItype)(v)) \ ++ : "macl", "mach") ++#define UMUL_TIME 5 ++#endif ++ ++#if defined (__SH5__) && __SHMEDIA__ && W_TYPE_SIZE == 32 ++#define __umulsidi3(u,v) ((UDItype)(USItype)u*(USItype)v) ++#define count_leading_zeros(count, x) \ ++ do \ ++ { \ ++ UDItype x_ = (USItype)(x); \ ++ SItype c_; \ ++ \ ++ __asm__ ("nsb %1, %0" : "=r" (c_) : "r" (x_)); \ ++ (count) = c_ - 31; \ ++ } \ ++ while (0) ++#define COUNT_LEADING_ZEROS_0 32 ++#endif ++ ++#if defined (__sparc__) && !defined (__arch64__) && !defined (__sparcv9) \ ++ && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%rJ" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "%rJ" ((USItype) (al)), \ ++ "rI" ((USItype) (bl)) \ ++ __CLOBBER_CC) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "rJ" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "rJ" ((USItype) (al)), \ ++ "rI" ((USItype) (bl)) \ ++ __CLOBBER_CC) ++#if defined (__sparc_v8__) ++#define umul_ppmm(w1, w0, u, v) \ ++ __asm__ ("umul %2,%3,%1;rd %%y,%0" \ ++ : "=r" ((USItype) (w1)), \ ++ "=r" ((USItype) (w0)) \ ++ : "r" ((USItype) (u)), \ ++ "r" ((USItype) (v))) ++#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \ ++ __asm__ ("mov %2,%%y;nop;nop;nop;udiv %3,%4,%0;umul %0,%4,%1;sub %3,%1,%1"\ ++ : "=&r" ((USItype) (__q)), \ ++ "=&r" ((USItype) (__r)) \ ++ : "r" ((USItype) (__n1)), \ ++ "r" ((USItype) (__n0)), \ ++ "r" ((USItype) (__d))) ++#else ++#if defined (__sparclite__) ++/* This has hardware multiply but not divide. It also has two additional ++ instructions scan (ffs from high bit) and divscc. */ ++#define umul_ppmm(w1, w0, u, v) \ ++ __asm__ ("umul %2,%3,%1;rd %%y,%0" \ ++ : "=r" ((USItype) (w1)), \ ++ "=r" ((USItype) (w0)) \ ++ : "r" ((USItype) (u)), \ ++ "r" ((USItype) (v))) ++#define udiv_qrnnd(q, r, n1, n0, d) \ ++ __asm__ ("! Inlined udiv_qrnnd\n" \ ++" wr %%g0,%2,%%y ! Not a delayed write for sparclite\n" \ ++" tst %%g0\n" \ ++" divscc %3,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%%g1\n" \ ++" divscc %%g1,%4,%0\n" \ ++" rd %%y,%1\n" \ ++" bl,a 1f\n" \ ++" add %1,%4,%1\n" \ ++"1: ! End of inline udiv_qrnnd" \ ++ : "=r" ((USItype) (q)), \ ++ "=r" ((USItype) (r)) \ ++ : "r" ((USItype) (n1)), \ ++ "r" ((USItype) (n0)), \ ++ "rI" ((USItype) (d)) \ ++ : "g1" __AND_CLOBBER_CC) ++#define UDIV_TIME 37 ++#define count_leading_zeros(count, x) \ ++ do { \ ++ __asm__ ("scan %1,1,%0" \ ++ : "=r" ((USItype) (count)) \ ++ : "r" ((USItype) (x))); \ ++ } while (0) ++/* Early sparclites return 63 for an argument of 0, but they warn that future ++ implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0 ++ undefined. */ ++#else ++/* SPARC without integer multiplication and divide instructions. ++ (i.e. at least Sun4/20,40,60,65,75,110,260,280,330,360,380,470,490) */ ++#define umul_ppmm(w1, w0, u, v) \ ++ __asm__ ("! Inlined umul_ppmm\n" \ ++" wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr\n"\ ++" sra %3,31,%%o5 ! Don't move this insn\n" \ ++" and %2,%%o5,%%o5 ! Don't move this insn\n" \ ++" andcc %%g0,0,%%g1 ! Don't move this insn\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,%3,%%g1\n" \ ++" mulscc %%g1,0,%%g1\n" \ ++" add %%g1,%%o5,%0\n" \ ++" rd %%y,%1" \ ++ : "=r" ((USItype) (w1)), \ ++ "=r" ((USItype) (w0)) \ ++ : "%rI" ((USItype) (u)), \ ++ "r" ((USItype) (v)) \ ++ : "g1", "o5" __AND_CLOBBER_CC) ++#define UMUL_TIME 39 /* 39 instructions */ ++/* It's quite necessary to add this much assembler for the sparc. ++ The default udiv_qrnnd (in C) is more than 10 times slower! */ ++#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \ ++ __asm__ ("! Inlined udiv_qrnnd\n" \ ++" mov 32,%%g1\n" \ ++" subcc %1,%2,%%g0\n" \ ++"1: bcs 5f\n" \ ++" addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n" \ ++" sub %1,%2,%1 ! this kills msb of n\n" \ ++" addx %1,%1,%1 ! so this can't give carry\n" \ ++" subcc %%g1,1,%%g1\n" \ ++"2: bne 1b\n" \ ++" subcc %1,%2,%%g0\n" \ ++" bcs 3f\n" \ ++" addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n" \ ++" b 3f\n" \ ++" sub %1,%2,%1 ! this kills msb of n\n" \ ++"4: sub %1,%2,%1\n" \ ++"5: addxcc %1,%1,%1\n" \ ++" bcc 2b\n" \ ++" subcc %%g1,1,%%g1\n" \ ++"! Got carry from n. Subtract next step to cancel this carry.\n" \ ++" bne 4b\n" \ ++" addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb\n" \ ++" sub %1,%2,%1\n" \ ++"3: xnor %0,0,%0\n" \ ++" ! End of inline udiv_qrnnd" \ ++ : "=&r" ((USItype) (__q)), \ ++ "=&r" ((USItype) (__r)) \ ++ : "r" ((USItype) (__d)), \ ++ "1" ((USItype) (__n1)), \ ++ "0" ((USItype) (__n0)) : "g1" __AND_CLOBBER_CC) ++#define UDIV_TIME (3+7*32) /* 7 instructions/iteration. 32 iterations. */ ++#endif /* __sparclite__ */ ++#endif /* __sparc_v8__ */ ++#endif /* sparc32 */ ++ ++#if ((defined (__sparc__) && defined (__arch64__)) || defined (__sparcv9)) \ ++ && W_TYPE_SIZE == 64 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("addcc %r4,%5,%1\n\t" \ ++ "add %r2,%3,%0\n\t" \ ++ "bcs,a,pn %%xcc, 1f\n\t" \ ++ "add %0, 1, %0\n" \ ++ "1:" \ ++ : "=r" ((UDItype)(sh)), \ ++ "=&r" ((UDItype)(sl)) \ ++ : "%rJ" ((UDItype)(ah)), \ ++ "rI" ((UDItype)(bh)), \ ++ "%rJ" ((UDItype)(al)), \ ++ "rI" ((UDItype)(bl)) \ ++ __CLOBBER_CC) ++ ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subcc %r4,%5,%1\n\t" \ ++ "sub %r2,%3,%0\n\t" \ ++ "bcs,a,pn %%xcc, 1f\n\t" \ ++ "sub %0, 1, %0\n\t" \ ++ "1:" \ ++ : "=r" ((UDItype)(sh)), \ ++ "=&r" ((UDItype)(sl)) \ ++ : "rJ" ((UDItype)(ah)), \ ++ "rI" ((UDItype)(bh)), \ ++ "rJ" ((UDItype)(al)), \ ++ "rI" ((UDItype)(bl)) \ ++ __CLOBBER_CC) ++ ++#define umul_ppmm(wh, wl, u, v) \ ++ do { \ ++ UDItype tmp1, tmp2, tmp3, tmp4; \ ++ __asm__ __volatile__ ( \ ++ "srl %7,0,%3\n\t" \ ++ "mulx %3,%6,%1\n\t" \ ++ "srlx %6,32,%2\n\t" \ ++ "mulx %2,%3,%4\n\t" \ ++ "sllx %4,32,%5\n\t" \ ++ "srl %6,0,%3\n\t" \ ++ "sub %1,%5,%5\n\t" \ ++ "srlx %5,32,%5\n\t" \ ++ "addcc %4,%5,%4\n\t" \ ++ "srlx %7,32,%5\n\t" \ ++ "mulx %3,%5,%3\n\t" \ ++ "mulx %2,%5,%5\n\t" \ ++ "sethi %%hi(0x80000000),%2\n\t" \ ++ "addcc %4,%3,%4\n\t" \ ++ "srlx %4,32,%4\n\t" \ ++ "add %2,%2,%2\n\t" \ ++ "movcc %%xcc,%%g0,%2\n\t" \ ++ "addcc %5,%4,%5\n\t" \ ++ "sllx %3,32,%3\n\t" \ ++ "add %1,%3,%1\n\t" \ ++ "add %5,%2,%0" \ ++ : "=r" ((UDItype)(wh)), \ ++ "=&r" ((UDItype)(wl)), \ ++ "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3), "=&r" (tmp4) \ ++ : "r" ((UDItype)(u)), \ ++ "r" ((UDItype)(v)) \ ++ __CLOBBER_CC); \ ++ } while (0) ++#define UMUL_TIME 96 ++#define UDIV_TIME 230 ++#endif /* sparc64 */ ++ ++#if defined (__vax__) && W_TYPE_SIZE == 32 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("addl2 %5,%1\n\tadwc %3,%0" \ ++ : "=g" ((USItype) (sh)), \ ++ "=&g" ((USItype) (sl)) \ ++ : "%0" ((USItype) (ah)), \ ++ "g" ((USItype) (bh)), \ ++ "%1" ((USItype) (al)), \ ++ "g" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subl2 %5,%1\n\tsbwc %3,%0" \ ++ : "=g" ((USItype) (sh)), \ ++ "=&g" ((USItype) (sl)) \ ++ : "0" ((USItype) (ah)), \ ++ "g" ((USItype) (bh)), \ ++ "1" ((USItype) (al)), \ ++ "g" ((USItype) (bl))) ++#define umul_ppmm(xh, xl, m0, m1) \ ++ do { \ ++ union { \ ++ UDItype __ll; \ ++ struct {USItype __l, __h;} __i; \ ++ } __xx; \ ++ USItype __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("emul %1,%2,$0,%0" \ ++ : "=r" (__xx.__ll) \ ++ : "g" (__m0), \ ++ "g" (__m1)); \ ++ (xh) = __xx.__i.__h; \ ++ (xl) = __xx.__i.__l; \ ++ (xh) += ((((SItype) __m0 >> 31) & __m1) \ ++ + (((SItype) __m1 >> 31) & __m0)); \ ++ } while (0) ++#define sdiv_qrnnd(q, r, n1, n0, d) \ ++ do { \ ++ union {DItype __ll; \ ++ struct {SItype __l, __h;} __i; \ ++ } __xx; \ ++ __xx.__i.__h = n1; __xx.__i.__l = n0; \ ++ __asm__ ("ediv %3,%2,%0,%1" \ ++ : "=g" (q), "=g" (r) \ ++ : "g" (__xx.__ll), "g" (d)); \ ++ } while (0) ++#endif /* __vax__ */ ++ ++#if defined (__z8000__) && W_TYPE_SIZE == 16 ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \ ++ : "=r" ((unsigned int)(sh)), \ ++ "=&r" ((unsigned int)(sl)) \ ++ : "%0" ((unsigned int)(ah)), \ ++ "r" ((unsigned int)(bh)), \ ++ "%1" ((unsigned int)(al)), \ ++ "rQR" ((unsigned int)(bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \ ++ : "=r" ((unsigned int)(sh)), \ ++ "=&r" ((unsigned int)(sl)) \ ++ : "0" ((unsigned int)(ah)), \ ++ "r" ((unsigned int)(bh)), \ ++ "1" ((unsigned int)(al)), \ ++ "rQR" ((unsigned int)(bl))) ++#define umul_ppmm(xh, xl, m0, m1) \ ++ do { \ ++ union {long int __ll; \ ++ struct {unsigned int __h, __l;} __i; \ ++ } __xx; \ ++ unsigned int __m0 = (m0), __m1 = (m1); \ ++ __asm__ ("mult %S0,%H3" \ ++ : "=r" (__xx.__i.__h), \ ++ "=r" (__xx.__i.__l) \ ++ : "%1" (__m0), \ ++ "rQR" (__m1)); \ ++ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \ ++ (xh) += ((((signed int) __m0 >> 15) & __m1) \ ++ + (((signed int) __m1 >> 15) & __m0)); \ ++ } while (0) ++#endif /* __z8000__ */ ++ ++#endif /* __GNUC__ */ ++ ++/* If this machine has no inline assembler, use C macros. */ ++ ++#if !defined (add_ssaaaa) ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ do { \ ++ UWtype __x; \ ++ __x = (al) + (bl); \ ++ (sh) = (ah) + (bh) + (__x < (al)); \ ++ (sl) = __x; \ ++ } while (0) ++#endif ++ ++#if !defined (sub_ddmmss) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ do { \ ++ UWtype __x; \ ++ __x = (al) - (bl); \ ++ (sh) = (ah) - (bh) - (__x > (al)); \ ++ (sl) = __x; \ ++ } while (0) ++#endif ++ ++#if !defined (umul_ppmm) ++#define umul_ppmm(w1, w0, u, v) \ ++ do { \ ++ UWtype __x0, __x1, __x2, __x3; \ ++ UHWtype __ul, __vl, __uh, __vh; \ ++ \ ++ __ul = __ll_lowpart (u); \ ++ __uh = __ll_highpart (u); \ ++ __vl = __ll_lowpart (v); \ ++ __vh = __ll_highpart (v); \ ++ \ ++ __x0 = (UWtype) __ul * __vl; \ ++ __x1 = (UWtype) __ul * __vh; \ ++ __x2 = (UWtype) __uh * __vl; \ ++ __x3 = (UWtype) __uh * __vh; \ ++ \ ++ __x1 += __ll_highpart (__x0);/* this can't give carry */ \ ++ __x1 += __x2; /* but this indeed can */ \ ++ if (__x1 < __x2) /* did we get it? */ \ ++ __x3 += __ll_B; /* yes, add it in the proper pos. */ \ ++ \ ++ (w1) = __x3 + __ll_highpart (__x1); \ ++ (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \ ++ } while (0) ++#endif ++ ++#if !defined (__umulsidi3) ++#define __umulsidi3(u, v) \ ++ ({DWunion __w; \ ++ umul_ppmm (__w.s.high, __w.s.low, u, v); \ ++ __w.ll; }) ++#endif ++ ++/* Define this unconditionally, so it can be used for debugging. */ ++#define __udiv_qrnnd_c(q, r, n1, n0, d) \ ++ do { \ ++ UWtype __d1, __d0, __q1, __q0; \ ++ UWtype __r1, __r0, __m; \ ++ __d1 = __ll_highpart (d); \ ++ __d0 = __ll_lowpart (d); \ ++ \ ++ __r1 = (n1) % __d1; \ ++ __q1 = (n1) / __d1; \ ++ __m = (UWtype) __q1 * __d0; \ ++ __r1 = __r1 * __ll_B | __ll_highpart (n0); \ ++ if (__r1 < __m) \ ++ { \ ++ __q1--, __r1 += (d); \ ++ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\ ++ if (__r1 < __m) \ ++ __q1--, __r1 += (d); \ ++ } \ ++ __r1 -= __m; \ ++ \ ++ __r0 = __r1 % __d1; \ ++ __q0 = __r1 / __d1; \ ++ __m = (UWtype) __q0 * __d0; \ ++ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ ++ if (__r0 < __m) \ ++ { \ ++ __q0--, __r0 += (d); \ ++ if (__r0 >= (d)) \ ++ if (__r0 < __m) \ ++ __q0--, __r0 += (d); \ ++ } \ ++ __r0 -= __m; \ ++ \ ++ (q) = (UWtype) __q1 * __ll_B | __q0; \ ++ (r) = __r0; \ ++ } while (0) ++ ++/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through ++ __udiv_w_sdiv (defined in libgcc or elsewhere). */ ++#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd) ++#define udiv_qrnnd(q, r, nh, nl, d) \ ++ do { \ ++ USItype __r; \ ++ (q) = __udiv_w_sdiv (&__r, nh, nl, d); \ ++ (r) = __r; \ ++ } while (0) ++#endif ++ ++/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */ ++#if !defined (udiv_qrnnd) ++#define UDIV_NEEDS_NORMALIZATION 1 ++#define udiv_qrnnd __udiv_qrnnd_c ++#endif ++ ++#if !defined (count_leading_zeros) ++extern const UQItype __clz_tab[]; ++#define count_leading_zeros(count, x) \ ++ do { \ ++ UWtype __xr = (x); \ ++ UWtype __a; \ ++ \ ++ if (W_TYPE_SIZE <= 32) \ ++ { \ ++ __a = __xr < ((UWtype)1<<2*__BITS4) \ ++ ? (__xr < ((UWtype)1<<__BITS4) ? 0 : __BITS4) \ ++ : (__xr < ((UWtype)1<<3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \ ++ } \ ++ else \ ++ { \ ++ for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \ ++ if (((__xr >> __a) & 0xff) != 0) \ ++ break; \ ++ } \ ++ \ ++ (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \ ++ } while (0) ++#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE ++#endif ++ ++#if !defined (count_trailing_zeros) ++/* Define count_trailing_zeros using count_leading_zeros. The latter might be ++ defined in asm, but if it is not, the C version above is good enough. */ ++#define count_trailing_zeros(count, x) \ ++ do { \ ++ UWtype __ctz_x = (x); \ ++ UWtype __ctz_c; \ ++ count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \ ++ (count) = W_TYPE_SIZE - 1 - __ctz_c; \ ++ } while (0) ++#endif ++ ++#ifndef UDIV_NEEDS_NORMALIZATION ++#define UDIV_NEEDS_NORMALIZATION 0 ++#endif +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/Makefile linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile +--- linux-3.12.2/3rdparty/ndiswrapper/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile 2013-11-28 21:42:10.000000000 +0200 +@@ -0,0 +1,213 @@ ++# Name of the module ++MODNAME = ndiswrapper ++ ++DISTFILES = \ ++ Makefile crt.c divdi3.c hal.c iw_ndis.c iw_ndis.h lin2win.S lin2win.h \ ++ loader.c loader.h longlong.h mkexport.sh mkstubs.sh ndis.c ndis.h \ ++ ndiswrapper.h ntoskernel.c ntoskernel.h ntoskernel_io.c pe_linker.c \ ++ pe_linker.h pnp.c pnp.h proc.c rtl.c usb.c usb.h win2lin_stubs.S \ ++ winnt_types.h workqueue.c wrapmem.c wrapmem.h wrapndis.c wrapndis.h \ ++ wrapper.c wrapper.h ++ ++# By default, we try to compile the modules for the currently running ++# kernel. But it's the first approximation, as we will re-read the ++# version from the kernel sources. ++KVERS_UNAME ?= $(shell uname -r) ++ ++# KBUILD is the path to the Linux kernel build tree. It is usually the ++# same as the kernel source tree, except when the kernel was compiled in ++# a separate directory. ++KBUILD ?= $(shell readlink -f /lib/modules/$(KVERS_UNAME)/build) ++ ++ifeq (,$(KBUILD)) ++$(error Kernel build tree not found - please set KBUILD to configured kernel) ++endif ++ ++KCONFIG := $(KBUILD)/.config ++ifeq (,$(wildcard $(KCONFIG))) ++$(error No .config found in $(KBUILD), please set KBUILD to configured kernel) ++endif ++ ++ifneq (,$(wildcard $(KBUILD)/include/linux/version.h)) ++ifneq (,$(wildcard $(KBUILD)/include/generated/uapi/linux/version.h)) ++$(error Multiple copies of version.h found, please clean your build tree) ++endif ++endif ++ ++# Kernel Makefile doesn't always know the exact kernel version, so we ++# get it from the kernel headers instead and pass it to make. ++VERSION_H := $(KBUILD)/include/generated/utsrelease.h ++ifeq (,$(wildcard $(VERSION_H))) ++VERSION_H := $(KBUILD)/include/linux/utsrelease.h ++endif ++ifeq (,$(wildcard $(VERSION_H))) ++VERSION_H := $(KBUILD)/include/linux/version.h ++endif ++ifeq (,$(wildcard $(VERSION_H))) ++$(error Please run 'make modules_prepare' in $(KBUILD)) ++endif ++ ++KVERS := $(shell sed -ne 's/"//g;s/^\#define UTS_RELEASE //p' $(VERSION_H)) ++ ++ifeq (,$(KVERS)) ++$(error Cannot find UTS_RELEASE in $(VERSION_H), please report) ++endif ++ ++INST_DIR = /lib/modules/$(KVERS)/misc ++ ++SRC_DIR=$(shell pwd) ++ ++include $(KCONFIG) ++ ++# returns of structs and unions in registers when possible, like Windows ++EXTRA_CFLAGS += -freg-struct-return ++ ++# to produce debug trace, add option "DEBUG=" where is 1 to 6 ++ifdef DEBUG ++EXTRA_CFLAGS += -DDEBUG=$(DEBUG) -g ++endif ++ ++# to debug timers, add option "TIMER_DEBUG=1" ++ifdef TIMER_DEBUG ++EXTRA_CFLAGS += -DTIMER_DEBUG ++endif ++ ++# to debug event layer, add option "EVENT_DEBUG=1" ++ifdef EVENT_DEBUG ++EXTRA_CFLAGS += -DEVENT_DEBUG ++endif ++ ++# to debug USB layer, add option "USB_DEBUG=1" ++ifdef USB_DEBUG ++EXTRA_CFLAGS += -DUSB_DEBUG ++endif ++ ++# to debug I/O layer, add option "IO_DEBUG=1" ++ifdef IO_DEBUG ++EXTRA_CFLAGS += -DIO_DEBUG ++endif ++ ++# to debug worker threads, add option "WORK_DEBUG=1" ++ifdef WORK_DEBUG ++EXTRA_CFLAGS += -DWORK_DEBUG ++endif ++ ++# to debug memory allocation, add option "ALLOC_DEBUG=" where is 1 or 2 ++ifdef ALLOC_DEBUG ++EXTRA_CFLAGS += -DALLOC_DEBUG=$(ALLOC_DEBUG) ++endif ++ ++OBJS = crt.o hal.o iw_ndis.o loader.o ndis.o ntoskernel.o ntoskernel_io.o \ ++ pe_linker.o pnp.o proc.o rtl.o wrapmem.o wrapndis.o wrapper.o ++ ++EXPORT_SRCS = crt.c hal.c ndis.c ntoskernel.c ntoskernel_io.c rtl.c ++ ++STUB_SRCS = crt.c hal.c ndis.c ntoskernel.c ntoskernel_io.c \ ++ pnp.c rtl.c wrapndis.c ++ ++ ++# By default, USB layer is compiled in if USB support is in kernel; ++# to disable USB support in ndiswrapper even if USB support is in kernel, ++# add option "DISABLE_USB=1" ++ifndef DISABLE_USB ++ifeq ($(CONFIG_USB),y) ++ENABLE_USB = 1 ++endif ++ifeq ($(CONFIG_USB),m) ++ENABLE_USB = 1 ++endif ++endif ++ ++ifdef ENABLE_USB ++EXPORT_SRCS += usb.c ++STUB_SRCS += usb.c ++OBJS += usb.o ++EXTRA_CFLAGS += -DENABLE_USB ++endif ++ ++ifdef WRAP_WQ ++EXTRA_CFLAGS += -DWRAP_WQ ++OBJS += workqueue.o ++endif ++ ++ ++all: config_check modules ++ ++# generate exports symbol table from C files ++quiet_cmd_mkexport = MKEXPORT $@ ++cmd_mkexport = $(SHELL) $(obj)/mkexport.sh $< $@ ++ ++extra-y += $(EXPORT_SRCS:.c=_exports.h) ++%_exports.h: %.c $(obj)/mkexport.sh FORCE ++ $(call if_changed,mkexport) ++ ++$(addprefix $(obj)/,$(EXPORT_SRCS:.c=.o)): %.o: %_exports.h ++ ++ifeq ($(CONFIG_X86_64),y) ++quiet_cmd_mkstubs = MKSTUBS $@ ++cmd_mkstubs = $(SHELL) $(obj)/mkstubs.sh $(addprefix $(src)/,$(STUB_SRCS)) >$@ ++ ++extra-y += win2lin_stubs.h ++$(obj)/win2lin_stubs.h: $(addprefix $(src)/,$(STUB_SRCS)) FORCE ++ $(call if_changed,mkstubs) ++ ++$(obj)/win2lin_stubs.o: $(obj)/win2lin_stubs.h ++OBJS += win2lin_stubs.o lin2win.o ++else ++OBJS += divdi3.o ++endif ++ ++MODULE := $(MODNAME).ko ++obj-m := $(MODNAME).o ++ ++$(MODNAME)-objs := $(OBJS) ++ ++ ++config_check: ++ @if [ -z "$(CONFIG_WIRELESS_EXT)$(CONFIG_NET_RADIO)" ]; then \ ++ echo; echo; \ ++ echo "*** WARNING: This kernel lacks wireless extensions."; \ ++ echo "Wireless drivers will not work properly."; \ ++ echo; echo; \ ++ fi ++ @if [ -z "$(CONFIG_X86_64)" ] && [ -n "$(CONFIG_4KSTACKS)" ]; then \ ++ echo; echo; \ ++ echo "*** WARNING: This kernel uses 4K stack size option"; \ ++ echo "(CONFIG_4KSTACKS); many Windows drivers will not work"; \ ++ echo "with this option enabled. Disable CONFIG_4KSTACKS"; \ ++ echo "in kernel's .config file, recompile and install kernel"; \ ++ echo; echo; \ ++ fi ++ ++modules: ++ $(MAKE) -C $(KBUILD) M=$(SRC_DIR) ++ ++$(MODULE): ++ $(MAKE) modules ++ ++clean: ++ rm -f *.o *.ko .*.cmd *.mod.c *.symvers modules.order *~ .\#* ++ rm -f *_exports.h win2lin_stubs.h ++ rm -rf .tmp_versions ++ ++install: config_check $(MODULE) ++ @/sbin/modinfo $(MODULE) | grep -q "^vermagic: *$(KVERS) " || \ ++ { echo "$(MODULE)" is not for Linux $(KVERS); exit 1; } ++ mkdir -p -m 755 $(DESTDIR)$(INST_DIR) ++ install -m 0644 $(MODULE) $(DESTDIR)$(INST_DIR) ++ifndef DESTDIR ++ -/sbin/depmod -a $(KVERS) ++endif ++ ++uninstall: ++ rm -f $(DESTDIR)$(INST_DIR)/$(MODULE) ++ifndef DESTDIR ++ -/sbin/depmod -a $(KVERS) ++endif ++ ++dist: ++ @for file in $(DISTFILES); do \ ++ cp $$file $(distdir)/$$file || exit 1; \ ++ done ++ ++.PHONY: all modules clean install config_check dist +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkexport.sh linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkexport.sh +--- linux-3.12.2/3rdparty/ndiswrapper/mkexport.sh 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkexport.sh 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,42 @@ ++#! /bin/sh ++ ++# Generate exports symbol table from C files ++ ++input="$1" ++output="$2" ++exports=$(basename "$output" .h) ++exec >"$output" ++ ++echo "/* automatically generated from src */"; ++ ++sed -n -e '/^\(wstdcall\|wfastcall\|noregparm\|regparm3\|__attribute__\)/{ ++:more ++N ++s/\([^{]\)$/\1/ ++t more ++s/\n{$/;/ ++p ++}' $input ++ ++echo "#ifdef CONFIG_X86_64"; ++ ++sed -n \ ++ -e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ ++'WIN_FUNC_DECL(\1, \2)/p' \ ++ -e 's/.*WIN_FUNC_PTR(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ ++'WIN_FUNC_DECL(\1, \2)/p' $input | sort -u ++ ++echo "#endif" ++echo "extern struct wrap_export $exports[];" ++echo "struct wrap_export $exports[] = {" ++ ++sed -n \ ++ -e 's/.*WIN_FUNC(_win_\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ ++' WIN_WIN_SYMBOL(\1, \2),/p' \ ++ -e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ ++' WIN_SYMBOL(\1, \2),/p' \ ++ -e 's/.*WIN_SYMBOL_MAP(\("[^"]\+"\)[ ,\n]\+\([^)]\+\)).*/'\ ++' {\1, (generic_func)\2},/p' $input | sort -u ++ ++echo " {NULL, NULL}" ++echo "};" +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkstubs.sh linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkstubs.sh +--- linux-3.12.2/3rdparty/ndiswrapper/mkstubs.sh 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkstubs.sh 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,12 @@ ++#! /bin/sh ++ ++for file in "$@"; do ++ echo ++ echo "# automatically generated from $file" ++ sed -n \ ++ -e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/\ ++ win2lin(\1, \2)/p' \ ++ -e 's/.*WIN_FUNC_PTR(\([^\,]\+\) *\, *\([0-9]\+\)).*/\ ++ win2lin(\1, \2)/p' \ ++ $file | sed -e 's/[ \t ]\+//' | sort -u; \ ++done +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.c +--- linux-3.12.2/3rdparty/ndiswrapper/ndis.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,3025 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ndis.h" ++#include "iw_ndis.h" ++#include "wrapndis.h" ++#include "pnp.h" ++#include "loader.h" ++#include ++#include ++#include "ndis_exports.h" ++ ++#define MAX_ALLOCATED_NDIS_PACKETS TX_RING_SIZE ++#define MAX_ALLOCATED_NDIS_BUFFERS TX_RING_SIZE ++ ++static struct work_struct ndis_work; ++static struct nt_list ndis_work_list; ++static spinlock_t ndis_work_list_lock; ++ ++struct workqueue_struct *ndis_wq; ++ ++static void *ndis_get_routine_address(char *name); ++ ++wstdcall void WIN_FUNC(NdisInitializeWrapper,4) ++ (void **driver_handle, struct driver_object *driver, ++ struct unicode_string *reg_path, void *unused) ++{ ++ ENTER1("handle: %p, driver: %p", driver_handle, driver); ++ *driver_handle = driver; ++ EXIT1(return); ++} ++ ++wstdcall void WIN_FUNC(NdisTerminateWrapper,2) ++ (struct device_object *dev_obj, void *system_specific) ++{ ++ EXIT1(return); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterMiniport,3) ++ (struct driver_object *drv_obj, struct miniport *mp, UINT length) ++{ ++ int min_length; ++ struct wrap_driver *wrap_driver; ++ struct ndis_driver *ndis_driver; ++ ++ min_length = ((char *)&mp->co_create_vc) - ((char *)mp); ++ ++ ENTER1("%p %p %d", drv_obj, mp, length); ++ ++ if (mp->major_version < 4) { ++ ERROR("Driver is using ndis version %d which is too old.", ++ mp->major_version); ++ EXIT1(return NDIS_STATUS_BAD_VERSION); ++ } ++ ++ if (length < min_length) { ++ ERROR("Characteristics length %d is too small", length); ++ EXIT1(return NDIS_STATUS_BAD_CHARACTERISTICS); ++ } ++ ++ TRACE1("%d.%d, %d, %u", mp->major_version, mp->minor_version, length, ++ (u32)sizeof(struct miniport)); ++ wrap_driver = IoGetDriverObjectExtension(drv_obj, ++ (void *)WRAP_DRIVER_CLIENT_ID); ++ if (!wrap_driver) { ++ ERROR("couldn't get wrap_driver"); ++ EXIT1(return NDIS_STATUS_RESOURCES); ++ } ++ if (IoAllocateDriverObjectExtension( ++ drv_obj, (void *)NDIS_DRIVER_CLIENT_ID, ++ sizeof(*ndis_driver), (void **)&ndis_driver) != ++ STATUS_SUCCESS) ++ EXIT1(return NDIS_STATUS_RESOURCES); ++ wrap_driver->ndis_driver = ndis_driver; ++ TRACE1("driver: %p", ndis_driver); ++ memcpy(&ndis_driver->mp, mp, min_t(int, sizeof(*mp), length)); ++ ++ DBG_BLOCK(2) { ++ int i; ++ void **func; ++ char *mp_funcs[] = { ++ "queryinfo", "reconfig", "reset", "send", "setinfo", ++ "tx_data", "return_packet", "send_packets", ++ "alloc_complete", "co_create_vc", "co_delete_vc", ++ "co_activate_vc", "co_deactivate_vc", ++ "co_send_packets", "co_request", "cancel_send_packets", ++ "pnp_event_notify", "shutdown", ++ }; ++ func = (void **)&ndis_driver->mp.queryinfo; ++ for (i = 0; i < ARRAY_SIZE(mp_funcs); i++) ++ TRACE0("function '%s' is at %p", mp_funcs[i], func[i]); ++ } ++ EXIT1(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterDevice,6) ++ (struct driver_object *drv_obj, struct unicode_string *dev_name, ++ struct unicode_string *link, void **funcs, ++ struct device_object **dev_obj, void **dev_obj_handle) ++{ ++ NTSTATUS status; ++ struct device_object *tmp; ++ int i; ++ ++ ENTER1("%p, %p, %p", drv_obj, dev_name, link); ++ status = IoCreateDevice(drv_obj, 0, dev_name, FILE_DEVICE_NETWORK, 0, ++ FALSE, &tmp); ++ ++ if (status != STATUS_SUCCESS) ++ EXIT1(return NDIS_STATUS_RESOURCES); ++ if (link) ++ status = IoCreateSymbolicLink(link, dev_name); ++ if (status != STATUS_SUCCESS) { ++ IoDeleteDevice(tmp); ++ EXIT1(return NDIS_STATUS_RESOURCES); ++ } ++ ++ *dev_obj = tmp; ++ *dev_obj_handle = *dev_obj; ++ for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) ++ if (funcs[i] && i != IRP_MJ_PNP && i != IRP_MJ_POWER) { ++ drv_obj->major_func[i] = funcs[i]; ++ TRACE1("mj_fn for 0x%x is at %p", i, funcs[i]); ++ } ++ EXIT1(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMDeregisterDevice,1) ++ (struct device_object *dev_obj) ++{ ++ ENTER2("%p", dev_obj); ++ IoDeleteDevice(dev_obj); ++ return NDIS_STATUS_SUCCESS; ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisAllocateMemoryWithTag,3) ++ (void **dest, UINT length, ULONG tag) ++{ ++ void *addr; ++ ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ addr = ExAllocatePoolWithTag(NonPagedPool, length, tag); ++ TRACE4("%p", addr); ++ if (addr) { ++ *dest = addr; ++ EXIT4(return NDIS_STATUS_SUCCESS); ++ } else ++ EXIT4(return NDIS_STATUS_FAILURE); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisAllocateMemory,4) ++ (void **dest, UINT length, UINT flags, NDIS_PHY_ADDRESS highest_address) ++{ ++ return NdisAllocateMemoryWithTag(dest, length, 0); ++} ++ ++/* length_tag is either length or tag, depending on if ++ * NdisAllocateMemory or NdisAllocateMemoryTag is used to allocate ++ * memory */ ++wstdcall void WIN_FUNC(NdisFreeMemory,3) ++ (void *addr, UINT length_tag, UINT flags) ++{ ++ TRACE4("%p", addr); ++ ExFreePool(addr); ++} ++ ++noregparm void WIN_FUNC(NdisWriteErrorLogEntry,12) ++ (struct driver_object *drv_obj, ULONG error, ULONG count, ...) ++{ ++ va_list args; ++ int i; ++ ULONG code; ++ ++ va_start(args, count); ++ ERROR("log: %08X, count: %d, return_address: %p", ++ error, count, __builtin_return_address(0)); ++ for (i = 0; i < count; i++) { ++ code = va_arg(args, ULONG); ++ ERROR("code: 0x%x", code); ++ } ++ va_end(args); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisOpenConfiguration,3) ++ (NDIS_STATUS *status, struct ndis_mp_block **conf_handle, ++ struct ndis_mp_block *handle) ++{ ++ ENTER2("%p", conf_handle); ++ *conf_handle = handle; ++ *status = NDIS_STATUS_SUCCESS; ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisOpenProtocolConfiguration,3) ++ (NDIS_STATUS *status, void **confhandle, ++ struct unicode_string *section) ++{ ++ ENTER2("%p", confhandle); ++ *status = NDIS_STATUS_SUCCESS; ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisOpenConfigurationKeyByName,4) ++ (NDIS_STATUS *status, void *handle, ++ struct unicode_string *key, void **subkeyhandle) ++{ ++ struct ansi_string ansi; ++ ENTER2(""); ++ if (RtlUnicodeStringToAnsiString(&ansi, key, TRUE) == STATUS_SUCCESS) { ++ TRACE2("%s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ *subkeyhandle = handle; ++ *status = NDIS_STATUS_SUCCESS; ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisOpenConfigurationKeyByIndex,5) ++ (NDIS_STATUS *status, void *handle, ULONG index, ++ struct unicode_string *key, void **subkeyhandle) ++{ ++ ENTER2("%u", index); ++// *subkeyhandle = handle; ++ *status = NDIS_STATUS_FAILURE; ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisCloseConfiguration,1) ++ (void *handle) ++{ ++ /* instead of freeing all configuration parameters as we are ++ * supposed to do here, we free them when the device is ++ * removed */ ++ ENTER2("%p", handle); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisOpenFile,5) ++ (NDIS_STATUS *status, struct wrap_bin_file **file, ++ UINT *filelength, struct unicode_string *filename, ++ NDIS_PHY_ADDRESS highest_address) ++{ ++ struct ansi_string ansi; ++ struct wrap_bin_file *bin_file; ++ ++ ENTER2("%p, %d, %llx, %p", status, *filelength, highest_address, *file); ++ if (RtlUnicodeStringToAnsiString(&ansi, filename, TRUE) != ++ STATUS_SUCCESS) { ++ *status = NDIS_STATUS_RESOURCES; ++ EXIT2(return); ++ } ++ TRACE2("%s", ansi.buf); ++ bin_file = get_bin_file(ansi.buf); ++ if (bin_file) { ++ *file = bin_file; ++ *filelength = bin_file->size; ++ *status = NDIS_STATUS_SUCCESS; ++ } else ++ *status = NDIS_STATUS_FILE_NOT_FOUND; ++ ++ RtlFreeAnsiString(&ansi); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMapFile,3) ++ (NDIS_STATUS *status, void **mappedbuffer, struct wrap_bin_file *file) ++{ ++ ENTER2("%p", file); ++ ++ if (!file) { ++ *status = NDIS_STATUS_ALREADY_MAPPED; ++ EXIT2(return); ++ } ++ ++ *status = NDIS_STATUS_SUCCESS; ++ *mappedbuffer = file->data; ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisUnmapFile,1) ++ (struct wrap_bin_file *file) ++{ ++ ENTER2("%p", file); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisCloseFile,1) ++ (struct wrap_bin_file *file) ++{ ++ ENTER2("%p", file); ++ free_bin_file(file); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisGetSystemUpTime,1) ++ (ULONG *ms) ++{ ++ *ms = 1000 * jiffies / HZ; ++ EXIT5(return); ++} ++ ++wstdcall ULONG WIN_FUNC(NDIS_BUFFER_TO_SPAN_PAGES,1) ++ (ndis_buffer *buffer) ++{ ++ ULONG n, length; ++ ++ if (buffer == NULL) ++ EXIT2(return 0); ++ if (MmGetMdlByteCount(buffer) == 0) ++ EXIT2(return 1); ++ ++ length = MmGetMdlByteCount(buffer); ++ n = SPAN_PAGES(MmGetMdlVirtualAddress(buffer), length); ++ TRACE4("%p, %p, %d, %d", buffer->startva, buffer->mappedsystemva, ++ length, n); ++ EXIT3(return n); ++} ++ ++wstdcall void WIN_FUNC(NdisGetBufferPhysicalArraySize,2) ++ (ndis_buffer *buffer, UINT *arraysize) ++{ ++ ENTER3("%p", buffer); ++ *arraysize = NDIS_BUFFER_TO_SPAN_PAGES(buffer); ++ EXIT3(return); ++} ++ ++static struct ndis_configuration_parameter * ++ndis_encode_setting(struct wrap_device_setting *setting, ++ enum ndis_parameter_type type) ++{ ++ struct ansi_string ansi; ++ struct ndis_configuration_parameter *param; ++ ++ param = setting->encoded; ++ if (param) { ++ if (param->type == type) ++ EXIT2(return param); ++ if (param->type == NdisParameterString) ++ RtlFreeUnicodeString(¶m->data.string); ++ setting->encoded = NULL; ++ } else ++ param = ExAllocatePoolWithTag(NonPagedPool, sizeof(*param), 0); ++ if (!param) { ++ ERROR("couldn't allocate memory"); ++ return NULL; ++ } ++ switch (type) { ++ case NdisParameterInteger: ++ param->data.integer = simple_strtol(setting->value, NULL, 0); ++ TRACE2("0x%x", param->data.integer); ++ break; ++ case NdisParameterHexInteger: ++ param->data.integer = simple_strtol(setting->value, NULL, 16); ++ TRACE2("0x%x", param->data.integer); ++ break; ++ case NdisParameterString: ++ RtlInitAnsiString(&ansi, setting->value); ++ TRACE2("'%s'", ansi.buf); ++ if (RtlAnsiStringToUnicodeString(¶m->data.string, ++ &ansi, TRUE)) { ++ ExFreePool(param); ++ EXIT2(return NULL); ++ } ++ break; ++ case NdisParameterBinary: ++ param->data.integer = simple_strtol(setting->value, NULL, 2); ++ TRACE2("0x%x", param->data.integer); ++ break; ++ default: ++ ERROR("unknown type: %d", type); ++ ExFreePool(param); ++ return NULL; ++ } ++ param->type = type; ++ setting->encoded = param; ++ EXIT2(return param); ++} ++ ++static int ndis_decode_setting(struct wrap_device_setting *setting, ++ struct ndis_configuration_parameter *param) ++{ ++ struct ansi_string ansi; ++ struct ndis_configuration_parameter *prev; ++ ++ ENTER2("%p, %p", setting, param); ++ prev = setting->encoded; ++ if (prev && prev->type == NdisParameterString) { ++ RtlFreeUnicodeString(&prev->data.string); ++ setting->encoded = NULL; ++ } ++ switch (param->type) { ++ case NdisParameterInteger: ++ snprintf(setting->value, MAX_SETTING_VALUE_LEN, "%u", ++ param->data.integer); ++ break; ++ case NdisParameterHexInteger: ++ snprintf(setting->value, MAX_SETTING_VALUE_LEN, "%x", ++ param->data.integer); ++ break; ++ case NdisParameterString: ++ ansi.buf = setting->value; ++ ansi.max_length = MAX_SETTING_VALUE_LEN; ++ if ((RtlUnicodeStringToAnsiString(&ansi, ¶m->data.string, ++ FALSE) != STATUS_SUCCESS) ++ || ansi.length >= MAX_SETTING_VALUE_LEN) { ++ EXIT1(return -1); ++ } ++ if (ansi.length == ansi.max_length) ++ ansi.length--; ++ setting->value[ansi.length] = 0; ++ break; ++ default: ++ TRACE2("unknown setting type: %d", param->type); ++ return -1; ++ } ++ TRACE2("setting changed %s='%s', %d", setting->name, setting->value, ++ ansi.length); ++ return 0; ++} ++ ++static int read_setting(struct nt_list *setting_list, char *keyname, int length, ++ struct ndis_configuration_parameter **param, ++ enum ndis_parameter_type type) ++{ ++ struct wrap_device_setting *setting; ++ mutex_lock(&loader_mutex); ++ nt_list_for_each_entry(setting, setting_list, list) { ++ if (strnicmp(keyname, setting->name, length) == 0) { ++ TRACE2("setting %s='%s'", keyname, setting->value); ++ mutex_unlock(&loader_mutex); ++ *param = ndis_encode_setting(setting, type); ++ if (*param) ++ EXIT2(return 0); ++ else ++ EXIT2(return -1); ++ } ++ } ++ mutex_unlock(&loader_mutex); ++ EXIT2(return -1); ++} ++ ++wstdcall void WIN_FUNC(NdisReadConfiguration,5) ++ (NDIS_STATUS *status, struct ndis_configuration_parameter **param, ++ struct ndis_mp_block *nmb, struct unicode_string *key, ++ enum ndis_parameter_type type) ++{ ++ struct ansi_string ansi; ++ int ret; ++ ++ ENTER2("nmb: %p", nmb); ++ ret = RtlUnicodeStringToAnsiString(&ansi, key, TRUE); ++ if (ret != STATUS_SUCCESS || ansi.buf == NULL) { ++ *param = NULL; ++ *status = NDIS_STATUS_FAILURE; ++ RtlFreeAnsiString(&ansi); ++ EXIT2(return); ++ } ++ TRACE2("%d, %s", type, ansi.buf); ++ ++ if (read_setting(&nmb->wnd->wd->settings, ansi.buf, ++ ansi.length, param, type) == 0 || ++ read_setting(&nmb->wnd->wd->driver->settings, ansi.buf, ++ ansi.length, param, type) == 0) ++ *status = NDIS_STATUS_SUCCESS; ++ else { ++ TRACE2("setting %s not found (type:%d)", ansi.buf, type); ++ *status = NDIS_STATUS_FAILURE; ++ } ++ RtlFreeAnsiString(&ansi); ++ EXIT2(return); ++ ++} ++ ++wstdcall void WIN_FUNC(NdisWriteConfiguration,4) ++ (NDIS_STATUS *status, struct ndis_mp_block *nmb, ++ struct unicode_string *key, struct ndis_configuration_parameter *param) ++{ ++ struct ansi_string ansi; ++ char *keyname; ++ struct wrap_device_setting *setting; ++ ++ ENTER2("nmb: %p", nmb); ++ if (RtlUnicodeStringToAnsiString(&ansi, key, TRUE)) { ++ *status = NDIS_STATUS_FAILURE; ++ EXIT2(return); ++ } ++ keyname = ansi.buf; ++ TRACE2("%s", keyname); ++ ++ mutex_lock(&loader_mutex); ++ nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) { ++ if (strnicmp(keyname, setting->name, ansi.length) == 0) { ++ mutex_unlock(&loader_mutex); ++ if (ndis_decode_setting(setting, param)) ++ *status = NDIS_STATUS_FAILURE; ++ else ++ *status = NDIS_STATUS_SUCCESS; ++ RtlFreeAnsiString(&ansi); ++ EXIT2(return); ++ } ++ } ++ mutex_unlock(&loader_mutex); ++ setting = kzalloc(sizeof(*setting), GFP_KERNEL); ++ if (setting) { ++ if (ansi.length == ansi.max_length) ++ ansi.length--; ++ memcpy(setting->name, keyname, ansi.length); ++ setting->name[ansi.length] = 0; ++ if (ndis_decode_setting(setting, param)) ++ *status = NDIS_STATUS_FAILURE; ++ else { ++ *status = NDIS_STATUS_SUCCESS; ++ mutex_lock(&loader_mutex); ++ InsertTailList(&nmb->wnd->wd->settings, &setting->list); ++ mutex_unlock(&loader_mutex); ++ } ++ } else ++ *status = NDIS_STATUS_RESOURCES; ++ ++ RtlFreeAnsiString(&ansi); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisReadNetworkAddress,4) ++ (NDIS_STATUS *status, void **addr, UINT *len, ++ struct ndis_mp_block *nmb) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ struct ndis_configuration_parameter *param; ++ struct unicode_string key; ++ struct ansi_string ansi; ++ typeof(wnd->mac) mac; ++ int i, ret; ++ ++ ENTER2("%p", nmb); ++ RtlInitAnsiString(&ansi, "NetworkAddress"); ++ *status = NDIS_STATUS_FAILURE; ++ if (RtlAnsiStringToUnicodeString(&key, &ansi, TRUE) != STATUS_SUCCESS) ++ EXIT1(return); ++ ++ NdisReadConfiguration(&ret, ¶m, nmb, &key, NdisParameterString); ++ RtlFreeUnicodeString(&key); ++ if (ret != NDIS_STATUS_SUCCESS) ++ EXIT1(return); ++ ret = RtlUnicodeStringToAnsiString(&ansi, ¶m->data.string, TRUE); ++ if (ret != STATUS_SUCCESS) ++ EXIT1(return); ++ ++ i = 0; ++ if (ansi.length >= 2 * sizeof(mac)) { ++ for (i = 0; i < sizeof(mac); i++) { ++ char c[3]; ++ int x; ++ c[0] = ansi.buf[i*2]; ++ c[1] = ansi.buf[i*2+1]; ++ c[2] = 0; ++ ret = sscanf(c, "%x", &x); ++ if (ret != 1) ++ break; ++ mac[i] = x; ++ } ++ } ++ TRACE2("%s, %d, " MACSTR, ansi.buf, i, MAC2STR(mac)); ++ RtlFreeAnsiString(&ansi); ++ if (i == sizeof(mac)) { ++ memcpy(wnd->mac, mac, sizeof(wnd->mac)); ++ *len = sizeof(mac); ++ *addr = wnd->mac; ++ *status = NDIS_STATUS_SUCCESS; ++ } ++ EXIT1(return); ++} ++ ++wstdcall void WIN_FUNC(NdisInitializeString,2) ++ (struct unicode_string *dest, UCHAR *src) ++{ ++ struct ansi_string ansi; ++ ++ ENTER2(""); ++ if (src == NULL) { ++ dest->length = dest->max_length = 0; ++ dest->buf = NULL; ++ } else { ++ RtlInitAnsiString(&ansi, src); ++ /* the string is freed with NdisFreeMemory */ ++ RtlAnsiStringToUnicodeString(dest, &ansi, TRUE); ++ } ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisInitAnsiString,2) ++ (struct ansi_string *dst, CHAR *src) ++{ ++ RtlInitAnsiString(dst, src); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisInitUnicodeString,2) ++ (struct unicode_string *dest, const wchar_t *src) ++{ ++ RtlInitUnicodeString(dest, src); ++ return; ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisAnsiStringToUnicodeString,2) ++ (struct unicode_string *dst, struct ansi_string *src) ++{ ++ ENTER2(""); ++ if (dst == NULL || src == NULL) ++ EXIT2(return NDIS_STATUS_FAILURE); ++ if (RtlAnsiStringToUnicodeString(dst, src, FALSE) == STATUS_SUCCESS) ++ return NDIS_STATUS_SUCCESS; ++ else ++ return NDIS_STATUS_FAILURE; ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisUnicodeStringToAnsiString,2) ++ (struct ansi_string *dst, struct unicode_string *src) ++{ ++ ENTER2(""); ++ if (dst == NULL || src == NULL) ++ EXIT2(return NDIS_STATUS_FAILURE); ++ if (RtlUnicodeStringToAnsiString(dst, src, FALSE) == STATUS_SUCCESS) ++ return NDIS_STATUS_SUCCESS; ++ else ++ return NDIS_STATUS_FAILURE; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(NdisUpcaseUnicodeString,2) ++ (struct unicode_string *dst, struct unicode_string *src) ++{ ++ EXIT2(return RtlUpcaseUnicodeString(dst, src, FALSE)); ++} ++ ++wstdcall void WIN_FUNC(NdisMSetAttributesEx,5) ++ (struct ndis_mp_block *nmb, void *mp_ctx, ++ UINT hangcheck_interval, UINT attributes, ULONG adaptertype) ++{ ++ struct ndis_device *wnd; ++ ++ ENTER1("%p, %p, %d, %08x, %d", nmb, mp_ctx, hangcheck_interval, ++ attributes, adaptertype); ++ wnd = nmb->wnd; ++ nmb->mp_ctx = mp_ctx; ++ wnd->attributes = attributes; ++ ++ if ((attributes & NDIS_ATTRIBUTE_BUS_MASTER) && ++ wrap_is_pci_bus(wnd->wd->dev_bus)) ++ pci_set_master(wnd->wd->pci.pdev); ++ ++ if (hangcheck_interval > 0) ++ wnd->hangcheck_interval = 2 * hangcheck_interval * HZ; ++ else ++ wnd->hangcheck_interval = 2 * HZ; ++ ++ EXIT1(return); ++} ++ ++wstdcall ULONG WIN_FUNC(NdisReadPciSlotInformation,5) ++ (struct ndis_mp_block *nmb, ULONG slot, ++ ULONG offset, char *buf, ULONG len) ++{ ++ struct wrap_device *wd = nmb->wnd->wd; ++ ULONG i; ++ if (!wrap_is_pci_bus(wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return 0; ++ } ++ for (i = 0; i < len; i++) ++ if (pci_read_config_byte(wd->pci.pdev, offset + i, &buf[i]) != ++ PCIBIOS_SUCCESSFUL) ++ break; ++ DBG_BLOCK(2) { ++ if (i != len) ++ WARNING("%u, %u", i, len); ++ } ++ return i; ++} ++ ++wstdcall ULONG WIN_FUNC(NdisImmediateReadPciSlotInformation,5) ++ (struct ndis_mp_block *nmb, ULONG slot, ++ ULONG offset, char *buf, ULONG len) ++{ ++ return NdisReadPciSlotInformation(nmb, slot, offset, buf, len); ++} ++ ++wstdcall ULONG WIN_FUNC(NdisWritePciSlotInformation,5) ++ (struct ndis_mp_block *nmb, ULONG slot, ++ ULONG offset, char *buf, ULONG len) ++{ ++ struct wrap_device *wd = nmb->wnd->wd; ++ ULONG i; ++ if (!wrap_is_pci_bus(wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return 0; ++ } ++ for (i = 0; i < len; i++) ++ if (pci_write_config_byte(wd->pci.pdev, offset + i, buf[i]) != ++ PCIBIOS_SUCCESSFUL) ++ break; ++ DBG_BLOCK(2) { ++ if (i != len) ++ WARNING("%u, %u", i, len); ++ } ++ return i; ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterIoPortRange,4) ++ (void **virt, struct ndis_mp_block *nmb, UINT start, UINT len) ++{ ++ ENTER3("%08x %08x", start, len); ++ *virt = (void *)(ULONG_PTR)start; ++ return NDIS_STATUS_SUCCESS; ++} ++ ++wstdcall void WIN_FUNC(NdisMDeregisterIoPortRange,4) ++ (struct ndis_mp_block *nmb, UINT start, UINT len, void* virt) ++{ ++ ENTER1("%08x %08x", start, len); ++} ++ ++wstdcall void WIN_FUNC(NdisReadPortUchar,3) ++ (struct ndis_mp_block *nmb, ULONG port, char *data) ++{ ++ *data = inb(port); ++} ++ ++wstdcall void WIN_FUNC(NdisImmediateReadPortUchar,3) ++ (struct ndis_mp_block *nmb, ULONG port, char *data) ++{ ++ *data = inb(port); ++} ++ ++wstdcall void WIN_FUNC(NdisWritePortUchar,3) ++ (struct ndis_mp_block *nmb, ULONG port, char data) ++{ ++ outb(data, port); ++} ++ ++wstdcall void WIN_FUNC(NdisImmediateWritePortUchar,3) ++ (struct ndis_mp_block *nmb, ULONG port, char data) ++{ ++ outb(data, port); ++} ++ ++wstdcall void WIN_FUNC(NdisMQueryAdapterResources,4) ++ (NDIS_STATUS *status, struct ndis_mp_block *nmb, ++ NDIS_RESOURCE_LIST *resource_list, UINT *size) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ NDIS_RESOURCE_LIST *list; ++ UINT resource_length; ++ ++ list = &wnd->wd->resource_list->list->partial_resource_list; ++ resource_length = sizeof(struct cm_partial_resource_list) + ++ sizeof(struct cm_partial_resource_descriptor) * ++ (list->count - 1); ++ TRACE2("%p, %p,%d (%d), %p %d %d", wnd, resource_list, *size, ++ resource_length, &list->partial_descriptors[list->count-1], ++ list->partial_descriptors[list->count-1].u.interrupt.level, ++ list->partial_descriptors[list->count-1].u.interrupt.vector); ++ if (*size < sizeof(*list)) { ++ *size = resource_length; ++ *status = NDIS_STATUS_BUFFER_TOO_SHORT; ++ } else { ++ ULONG count; ++ if (*size >= resource_length) { ++ *size = resource_length; ++ count = list->count; ++ } else { ++ UINT n = sizeof(*list); ++ count = 1; ++ while (count++ < list->count && n < *size) ++ n += sizeof(list->partial_descriptors); ++ *size = n; ++ } ++ memcpy(resource_list, list, *size); ++ resource_list->count = count; ++ *status = NDIS_STATUS_SUCCESS; ++ } ++ EXIT2(return); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMPciAssignResources,3) ++ (struct ndis_mp_block *nmb, ULONG slot_number, ++ NDIS_RESOURCE_LIST **resources) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ++ ENTER2("%p, %p", wnd, wnd->wd->resource_list); ++ *resources = &wnd->wd->resource_list->list->partial_resource_list; ++ EXIT2(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMMapIoSpace,4) ++ (void __iomem **virt, struct ndis_mp_block *nmb, ++ NDIS_PHY_ADDRESS phy_addr, UINT len) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ++ ENTER2("%llx, %d", phy_addr, len); ++ *virt = MmMapIoSpace(phy_addr, len, MmCached); ++ if (*virt == NULL) { ++ ERROR("ioremap failed"); ++ EXIT2(return NDIS_STATUS_FAILURE); ++ } ++ wnd->mem_start = phy_addr; ++ wnd->mem_end = phy_addr + len; ++ TRACE2("%p", *virt); ++ EXIT2(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(NdisMUnmapIoSpace,3) ++ (struct ndis_mp_block *nmb, void __iomem *virt, UINT len) ++{ ++ ENTER2("%p, %d", virt, len); ++ MmUnmapIoSpace(virt, len); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisAllocateSpinLock,1) ++ (struct ndis_spinlock *lock) ++{ ++ TRACE4("lock %p, %p", lock, &lock->klock); ++ KeInitializeSpinLock(&lock->klock); ++ lock->irql = PASSIVE_LEVEL; ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisFreeSpinLock,1) ++ (struct ndis_spinlock *lock) ++{ ++ TRACE4("lock %p, %p", lock, &lock->klock); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisAcquireSpinLock,1) ++ (struct ndis_spinlock *lock) ++{ ++ ENTER6("lock %p, %p", lock, &lock->klock); ++// assert_irql(_irql_ <= DISPATCH_LEVEL); ++ lock->irql = nt_spin_lock_irql(&lock->klock, DISPATCH_LEVEL); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisReleaseSpinLock,1) ++ (struct ndis_spinlock *lock) ++{ ++ ENTER6("lock %p, %p", lock, &lock->klock); ++// assert_irql(_irql_ == DISPATCH_LEVEL); ++ nt_spin_unlock_irql(&lock->klock, lock->irql); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisDprAcquireSpinLock,1) ++ (struct ndis_spinlock *lock) ++{ ++ ENTER6("lock %p", &lock->klock); ++// assert_irql(_irql_ == DISPATCH_LEVEL); ++ nt_spin_lock(&lock->klock); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisDprReleaseSpinLock,1) ++ (struct ndis_spinlock *lock) ++{ ++ ENTER6("lock %p", &lock->klock); ++// assert_irql(_irql_ == DISPATCH_LEVEL); ++ nt_spin_unlock(&lock->klock); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisInitializeReadWriteLock,1) ++ (struct ndis_rw_lock *rw_lock) ++{ ++ ENTER3("%p", rw_lock); ++ memset(rw_lock, 0, sizeof(*rw_lock)); ++ KeInitializeSpinLock(&rw_lock->klock); ++ return; ++} ++ ++/* read/write locks are implemented in a rather simplistic way - we ++ * should probably use Linux's rw_lock implementation */ ++ ++wstdcall void WIN_FUNC(NdisAcquireReadWriteLock,3) ++ (struct ndis_rw_lock *rw_lock, BOOLEAN write, ++ struct lock_state *lock_state) ++{ ++ if (write) { ++ while (1) { ++ if (cmpxchg(&rw_lock->count, 0, -1) == 0) ++ return; ++ while (rw_lock->count) ++ cpu_relax(); ++ } ++ return; ++ } ++ while (1) { ++ typeof(rw_lock->count) count; ++ while ((count = rw_lock->count) < 0) ++ cpu_relax(); ++ if (cmpxchg(&rw_lock->count, count, count + 1) == count) ++ return; ++ } ++} ++ ++wstdcall void WIN_FUNC(NdisReleaseReadWriteLock,2) ++ (struct ndis_rw_lock *rw_lock, struct lock_state *lock_state) ++{ ++ if (rw_lock->count > 0) ++ pre_atomic_add(rw_lock->count, -1); ++ else if (rw_lock->count == -1) ++ rw_lock->count = 0; ++ else ++ WARNING("invalid state: %d", rw_lock->count); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMAllocateMapRegisters,5) ++ (struct ndis_mp_block *nmb, UINT dmachan, ++ NDIS_DMA_SIZE dmasize, ULONG basemap, ULONG max_buf_size) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ++ ENTER2("%p, %d %d %d %d", wnd, dmachan, dmasize, basemap, max_buf_size); ++ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return NDIS_STATUS_NOT_SUPPORTED; ++ } ++ if (wnd->dma_map_count > 0) { ++ WARNING("%s: map registers already allocated: %u", ++ wnd->net_dev->name, wnd->dma_map_count); ++ EXIT2(return NDIS_STATUS_RESOURCES); ++ } ++ if (dmasize == NDIS_DMA_24BITS) { ++ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(24)) || ++ pci_set_consistent_dma_mask(wnd->wd->pci.pdev, ++ DMA_BIT_MASK(24))) ++ WARNING("setting dma mask failed"); ++ } else if (dmasize == NDIS_DMA_32BITS) { ++ /* consistent dma is in low 32-bits by default */ ++ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(32))) ++ WARNING("setting dma mask failed"); ++#ifdef CONFIG_X86_64 ++ } else if (dmasize == NDIS_DMA_64BITS) { ++ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(64)) || ++ pci_set_consistent_dma_mask(wnd->wd->pci.pdev, ++ DMA_BIT_MASK(64))) ++ WARNING("setting dma mask failed"); ++ else ++ wnd->net_dev->features |= NETIF_F_HIGHDMA; ++#endif ++ } else { ++ ERROR("dmasize %d not supported", dmasize); ++ EXIT2(return NDIS_STATUS_NOT_SUPPORTED); ++ } ++ /* since memory for buffer is allocated with kmalloc, buffer ++ * is physically contiguous, so entire map will fit in one ++ * register */ ++ if (basemap > 64) { ++ WARNING("Windows driver %s requesting too many (%u) " ++ "map registers", wnd->wd->driver->name, basemap); ++ /* As per NDIS, NDIS_STATUS_RESOURCES should be ++ * returned, but with that Atheros PCI driver fails - ++ * for now tolerate it */ ++// EXIT2(return NDIS_STATUS_RESOURCES); ++ } ++ ++ wnd->dma_map_addr = kzalloc(basemap * sizeof(*(wnd->dma_map_addr)), ++ GFP_KERNEL); ++ if (!wnd->dma_map_addr) ++ EXIT2(return NDIS_STATUS_RESOURCES); ++ wnd->dma_map_count = basemap; ++ TRACE2("%u", wnd->dma_map_count); ++ EXIT2(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(NdisMFreeMapRegisters,1) ++ (struct ndis_mp_block *nmb) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ int i; ++ ++ ENTER2("wnd: %p", wnd); ++ if (wnd->dma_map_addr) { ++ for (i = 0; i < wnd->dma_map_count; i++) { ++ if (wnd->dma_map_addr[i]) ++ WARNING("%s: dma addr 0x%llx not freed by " ++ "Windows driver", wnd->net_dev->name, ++ (unsigned long long)wnd->dma_map_addr[i]); ++ } ++ kfree(wnd->dma_map_addr); ++ wnd->dma_map_addr = NULL; ++ } else ++ WARNING("map registers already freed?"); ++ wnd->dma_map_count = 0; ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMStartBufferPhysicalMapping,6) ++ (struct ndis_mp_block *nmb, ndis_buffer *buf, ++ ULONG index, BOOLEAN write_to_dev, ++ struct ndis_phy_addr_unit *phy_addr_array, UINT *array_size) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ++ ENTER3("%p, %p, %u, %u", wnd, buf, index, wnd->dma_map_count); ++ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return; ++ } ++ if (unlikely(wnd->sg_dma_size || !write_to_dev || ++ index >= wnd->dma_map_count)) { ++ WARNING("invalid request: %d, %d, %d, %d", wnd->sg_dma_size, ++ write_to_dev, index, wnd->dma_map_count); ++ phy_addr_array[0].phy_addr = 0; ++ phy_addr_array[0].length = 0; ++ *array_size = 0; ++ return; ++ } ++ if (wnd->dma_map_addr[index]) { ++ TRACE2("buffer %p at %d is already mapped: %llx", buf, index, ++ (unsigned long long)wnd->dma_map_addr[index]); ++// *array_size = 1; ++ return; ++ } ++ TRACE3("%p, %p, %u", buf, MmGetSystemAddressForMdl(buf), ++ MmGetMdlByteCount(buf)); ++ DBG_BLOCK(4) { ++ dump_bytes(__func__, MmGetSystemAddressForMdl(buf), ++ MmGetMdlByteCount(buf)); ++ } ++ wnd->dma_map_addr[index] = ++ PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, ++ MmGetSystemAddressForMdl(buf), ++ MmGetMdlByteCount(buf), PCI_DMA_TODEVICE); ++ phy_addr_array[0].phy_addr = wnd->dma_map_addr[index]; ++ phy_addr_array[0].length = MmGetMdlByteCount(buf); ++ TRACE4("%llx, %d, %d", phy_addr_array[0].phy_addr, ++ phy_addr_array[0].length, index); ++ *array_size = 1; ++} ++ ++wstdcall void WIN_FUNC(NdisMCompleteBufferPhysicalMapping,3) ++ (struct ndis_mp_block *nmb, ndis_buffer *buf, ULONG index) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ++ ENTER3("%p, %p %u (%u)", wnd, buf, index, wnd->dma_map_count); ++ ++ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return; ++ } ++ if (unlikely(wnd->sg_dma_size)) ++ WARNING("buffer %p may have been unmapped already", buf); ++ if (index >= wnd->dma_map_count) { ++ ERROR("invalid map register (%u >= %u)", ++ index, wnd->dma_map_count); ++ return; ++ } ++ TRACE4("%llx", (unsigned long long)wnd->dma_map_addr[index]); ++ if (wnd->dma_map_addr[index]) { ++ PCI_DMA_UNMAP_SINGLE(wnd->wd->pci.pdev, wnd->dma_map_addr[index], ++ MmGetMdlByteCount(buf), PCI_DMA_TODEVICE); ++ wnd->dma_map_addr[index] = 0; ++ } else ++ WARNING("map registers at %u not used", index); ++} ++ ++wstdcall void WIN_FUNC(NdisMAllocateSharedMemory,5) ++ (struct ndis_mp_block *nmb, ULONG size, ++ BOOLEAN cached, void **virt, NDIS_PHY_ADDRESS *phys) ++{ ++ dma_addr_t dma_addr; ++ struct wrap_device *wd = nmb->wnd->wd; ++ ++ ENTER3("size: %u, cached: %d", size, cached); ++ if (!wrap_is_pci_bus(wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return; ++ } ++ *virt = PCI_DMA_ALLOC_COHERENT(wd->pci.pdev, size, &dma_addr); ++ if (*virt) ++ *phys = dma_addr; ++ else ++ WARNING("couldn't allocate %d bytes of %scached DMA memory", ++ size, cached ? "" : "un-"); ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMFreeSharedMemory,5) ++ (struct ndis_mp_block *nmb, ULONG size, BOOLEAN cached, ++ void *virt, NDIS_PHY_ADDRESS addr) ++{ ++ struct wrap_device *wd = nmb->wnd->wd; ++ ENTER3("%p, %llx, %u", virt, addr, size); ++ if (!wrap_is_pci_bus(wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return; ++ } ++ PCI_DMA_FREE_COHERENT(wd->pci.pdev, size, virt, addr); ++ EXIT3(return); ++} ++ ++wstdcall void alloc_shared_memory_async(void *arg1, void *arg2) ++{ ++ struct ndis_device *wnd; ++ struct alloc_shared_mem *alloc_shared_mem; ++ struct miniport *mp; ++ void *virt; ++ NDIS_PHY_ADDRESS phys; ++ KIRQL irql; ++ ++ wnd = arg1; ++ alloc_shared_mem = arg2; ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ NdisMAllocateSharedMemory(wnd->nmb, alloc_shared_mem->size, ++ alloc_shared_mem->cached, &virt, &phys); ++ irql = serialize_lock_irql(wnd); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ LIN2WIN5(mp->alloc_complete, wnd->nmb, virt, ++ &phys, alloc_shared_mem->size, alloc_shared_mem->ctx); ++ serialize_unlock_irql(wnd, irql); ++ kfree(alloc_shared_mem); ++} ++WIN_FUNC_DECL(alloc_shared_memory_async,2) ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMAllocateSharedMemoryAsync,4) ++ (struct ndis_mp_block *nmb, ULONG size, BOOLEAN cached, void *ctx) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ struct alloc_shared_mem *alloc_shared_mem; ++ ++ ENTER3("wnd: %p", wnd); ++ alloc_shared_mem = kmalloc(sizeof(*alloc_shared_mem), irql_gfp()); ++ if (!alloc_shared_mem) { ++ WARNING("couldn't allocate memory"); ++ return NDIS_STATUS_FAILURE; ++ } ++ ++ alloc_shared_mem->size = size; ++ alloc_shared_mem->cached = cached; ++ alloc_shared_mem->ctx = ctx; ++ if (schedule_ntos_work_item(WIN_FUNC_PTR(alloc_shared_memory_async,2), ++ wnd, alloc_shared_mem)) ++ EXIT3(return NDIS_STATUS_FAILURE); ++ EXIT3(return NDIS_STATUS_PENDING); ++} ++ ++/* Some drivers allocate NDIS_BUFFER (aka MDL) very often; instead of ++ * allocating and freeing with kernel functions, we chain them into ++ * ndis_buffer_pool. When an MDL is freed, it is added to the list of ++ * free MDLs. When allocated, we first check if there is one in free ++ * list and if so just return it; otherwise, we allocate a new one and ++ * return that. This reduces memory fragmentation. Windows DDK says ++ * that the driver itself shouldn't check what is returned in ++ * pool_handle, presumably because buffer pools are not used in ++ * XP. However, as long as driver follows rest of the semantics - that ++ * it should indicate maximum number of MDLs used with num_descr and ++ * pass the same pool_handle in other buffer functions, this should ++ * work. Sadly, though, NdisFreeBuffer doesn't pass the pool_handle, ++ * so we use 'process' field of MDL to store pool_handle. */ ++ ++wstdcall void WIN_FUNC(NdisAllocateBufferPool,3) ++ (NDIS_STATUS *status, struct ndis_buffer_pool **pool_handle, ++ UINT num_descr) ++{ ++ struct ndis_buffer_pool *pool; ++ ++ ENTER1("buffers: %d", num_descr); ++ pool = kmalloc(sizeof(*pool), irql_gfp()); ++ if (!pool) { ++ *status = NDIS_STATUS_RESOURCES; ++ EXIT3(return); ++ } ++ spin_lock_init(&pool->lock); ++ pool->max_descr = num_descr; ++ pool->num_allocated_descr = 0; ++ pool->free_descr = NULL; ++ *pool_handle = pool; ++ *status = NDIS_STATUS_SUCCESS; ++ TRACE1("pool: %p, num_descr: %d", pool, num_descr); ++ EXIT1(return); ++} ++ ++wstdcall void WIN_FUNC(NdisAllocateBuffer,5) ++ (NDIS_STATUS *status, ndis_buffer **buffer, ++ struct ndis_buffer_pool *pool, void *virt, UINT length) ++{ ++ ndis_buffer *descr; ++ ++ ENTER4("pool: %p (%d)", pool, pool->num_allocated_descr); ++ /* NDIS drivers should call this at DISPATCH_LEVEL, but ++ * alloc_tx_packet calls at SOFT_IRQL */ ++ assert_irql(_irql_ <= SOFT_LEVEL); ++ if (!pool) { ++ *status = NDIS_STATUS_FAILURE; ++ *buffer = NULL; ++ EXIT4(return); ++ } ++ spin_lock_bh(&pool->lock); ++ if ((descr = pool->free_descr)) ++ pool->free_descr = descr->next; ++ spin_unlock_bh(&pool->lock); ++ if (descr) { ++ typeof(descr->flags) flags; ++ flags = descr->flags; ++ memset(descr, 0, sizeof(*descr)); ++ MmInitializeMdl(descr, virt, length); ++ if (flags & MDL_CACHE_ALLOCATED) ++ descr->flags |= MDL_CACHE_ALLOCATED; ++ } else { ++ if (pool->num_allocated_descr > pool->max_descr) { ++ TRACE2("pool %p is full: %d(%d)", pool, ++ pool->num_allocated_descr, pool->max_descr); ++#ifndef ALLOW_POOL_OVERFLOW ++ *status = NDIS_STATUS_FAILURE; ++ *buffer = NULL; ++ return; ++#endif ++ } ++ descr = allocate_init_mdl(virt, length); ++ if (!descr) { ++ WARNING("couldn't allocate buffer"); ++ *status = NDIS_STATUS_FAILURE; ++ *buffer = NULL; ++ EXIT4(return); ++ } ++ TRACE4("buffer %p for %p, %d", descr, virt, length); ++ atomic_inc_var(pool->num_allocated_descr); ++ } ++ /* TODO: make sure this mdl can map given buffer */ ++ MmBuildMdlForNonPagedPool(descr); ++// descr->flags |= MDL_ALLOCATED_FIXED_SIZE | ++// MDL_MAPPED_TO_SYSTEM_VA | MDL_PAGES_LOCKED; ++ descr->pool = pool; ++ *buffer = descr; ++ *status = NDIS_STATUS_SUCCESS; ++ TRACE4("buffer: %p", descr); ++ EXIT4(return); ++} ++ ++wstdcall void WIN_FUNC(NdisFreeBuffer,1) ++ (ndis_buffer *buffer) ++{ ++ struct ndis_buffer_pool *pool; ++ ++ ENTER4("%p", buffer); ++ if (!buffer || !buffer->pool) { ++ ERROR("invalid buffer"); ++ EXIT4(return); ++ } ++ pool = buffer->pool; ++ if (pool->num_allocated_descr > MAX_ALLOCATED_NDIS_BUFFERS) { ++ /* NB NB NB: set mdl's 'pool' field to NULL before ++ * calling free_mdl; otherwise free_mdl calls ++ * NdisFreeBuffer back */ ++ atomic_dec_var(pool->num_allocated_descr); ++ buffer->pool = NULL; ++ free_mdl(buffer); ++ } else { ++ spin_lock_bh(&pool->lock); ++ buffer->next = pool->free_descr; ++ pool->free_descr = buffer; ++ spin_unlock_bh(&pool->lock); ++ } ++ EXIT4(return); ++} ++ ++wstdcall void WIN_FUNC(NdisFreeBufferPool,1) ++ (struct ndis_buffer_pool *pool) ++{ ++ ndis_buffer *cur, *next; ++ ++ TRACE3("pool: %p", pool); ++ if (!pool) { ++ WARNING("invalid pool"); ++ EXIT3(return); ++ } ++ spin_lock_bh(&pool->lock); ++ cur = pool->free_descr; ++ while (cur) { ++ next = cur->next; ++ cur->pool = NULL; ++ free_mdl(cur); ++ cur = next; ++ } ++ spin_unlock_bh(&pool->lock); ++ kfree(pool); ++ pool = NULL; ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisAdjustBufferLength,2) ++ (ndis_buffer *buffer, UINT length) ++{ ++ ENTER4("%p, %d", buffer, length); ++ buffer->bytecount = length; ++} ++ ++wstdcall void WIN_FUNC(NdisQueryBuffer,3) ++ (ndis_buffer *buffer, void **virt, UINT *length) ++{ ++ ENTER4("buffer: %p", buffer); ++ if (virt) ++ *virt = MmGetSystemAddressForMdl(buffer); ++ *length = MmGetMdlByteCount(buffer); ++ TRACE4("%p, %u", virt ? *virt : NULL, *length); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisQueryBufferSafe,4) ++ (ndis_buffer *buffer, void **virt, UINT *length, ++ enum mm_page_priority priority) ++{ ++ ENTER4("%p, %p, %p, %d", buffer, virt, length, priority); ++ if (virt) ++ *virt = MmGetSystemAddressForMdlSafe(buffer, priority); ++ *length = MmGetMdlByteCount(buffer); ++ TRACE4("%p, %u", virt ? *virt : NULL, *length); ++} ++ ++wstdcall void *WIN_FUNC(NdisBufferVirtualAddress,1) ++ (ndis_buffer *buffer) ++{ ++ ENTER3("%p", buffer); ++ return MmGetSystemAddressForMdl(buffer); ++} ++ ++wstdcall ULONG WIN_FUNC(NdisBufferLength,1) ++ (ndis_buffer *buffer) ++{ ++ ENTER3("%p", buffer); ++ return MmGetMdlByteCount(buffer); ++} ++ ++wstdcall void WIN_FUNC(NdisQueryBufferOffset,3) ++ (ndis_buffer *buffer, UINT *offset, UINT *length) ++{ ++ ENTER3("%p", buffer); ++ *offset = MmGetMdlByteOffset(buffer); ++ *length = MmGetMdlByteCount(buffer); ++ TRACE3("%d, %d", *offset, *length); ++} ++ ++wstdcall void WIN_FUNC(NdisUnchainBufferAtBack,2) ++ (struct ndis_packet *packet, ndis_buffer **buffer) ++{ ++ ndis_buffer *b, *btail; ++ ++ ENTER3("%p", packet); ++ b = packet->private.buffer_head; ++ if (!b) { ++ /* no buffer in packet */ ++ *buffer = NULL; ++ EXIT3(return); ++ } ++ btail = packet->private.buffer_tail; ++ *buffer = btail; ++ if (b == btail) { ++ /* one buffer in packet */ ++ packet->private.buffer_head = NULL; ++ packet->private.buffer_tail = NULL; ++ } else { ++ while (b->next != btail) ++ b = b->next; ++ packet->private.buffer_tail = b; ++ b->next = NULL; ++ } ++ packet->private.valid_counts = FALSE; ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisUnchainBufferAtFront,2) ++ (struct ndis_packet *packet, ndis_buffer **buffer) ++{ ++ ENTER3("%p", packet); ++ if (packet->private.buffer_head == NULL) { ++ /* no buffer in packet */ ++ *buffer = NULL; ++ EXIT3(return); ++ } ++ ++ *buffer = packet->private.buffer_head; ++ if (packet->private.buffer_head == packet->private.buffer_tail) { ++ /* one buffer in packet */ ++ packet->private.buffer_head = NULL; ++ packet->private.buffer_tail = NULL; ++ } else ++ packet->private.buffer_head = (*buffer)->next; ++ ++ packet->private.valid_counts = FALSE; ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisGetFirstBufferFromPacketSafe,6) ++ (struct ndis_packet *packet, ndis_buffer **first_buffer, ++ void **first_buffer_va, UINT *first_buffer_length, ++ UINT *total_buffer_length, enum mm_page_priority priority) ++{ ++ ndis_buffer *b = packet->private.buffer_head; ++ ++ ENTER3("%p(%p)", packet, b); ++ *first_buffer = b; ++ if (b) { ++ *first_buffer_va = MmGetSystemAddressForMdlSafe(b, priority); ++ *first_buffer_length = *total_buffer_length = ++ MmGetMdlByteCount(b); ++ for (b = b->next; b; b = b->next) ++ *total_buffer_length += MmGetMdlByteCount(b); ++ } else { ++ *first_buffer_va = NULL; ++ *first_buffer_length = 0; ++ *total_buffer_length = 0; ++ } ++ TRACE3("%p, %d, %d", *first_buffer_va, *first_buffer_length, ++ *total_buffer_length); ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisGetFirstBufferFromPacket,6) ++ (struct ndis_packet *packet, ndis_buffer **first_buffer, ++ void **first_buffer_va, UINT *first_buffer_length, ++ UINT *total_buffer_length, enum mm_page_priority priority) ++{ ++ NdisGetFirstBufferFromPacketSafe(packet, first_buffer, ++ first_buffer_va, first_buffer_length, ++ total_buffer_length, ++ NormalPagePriority); ++} ++ ++wstdcall void WIN_FUNC(NdisAllocatePacketPoolEx,5) ++ (NDIS_STATUS *status, struct ndis_packet_pool **pool_handle, ++ UINT num_descr, UINT overflowsize, UINT proto_rsvd_length) ++{ ++ struct ndis_packet_pool *pool; ++ ++ ENTER3("buffers: %d, length: %d", num_descr, proto_rsvd_length); ++ pool = kzalloc(sizeof(*pool), irql_gfp()); ++ if (!pool) { ++ *status = NDIS_STATUS_RESOURCES; ++ EXIT3(return); ++ } ++ spin_lock_init(&pool->lock); ++ pool->max_descr = num_descr; ++ pool->num_allocated_descr = 0; ++ pool->num_used_descr = 0; ++ pool->free_descr = NULL; ++ pool->proto_rsvd_length = proto_rsvd_length; ++ *pool_handle = pool; ++ *status = NDIS_STATUS_SUCCESS; ++ TRACE3("pool: %p", pool); ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisAllocatePacketPool,4) ++ (NDIS_STATUS *status, struct ndis_packet_pool **pool_handle, ++ UINT num_descr, UINT proto_rsvd_length) ++{ ++ NdisAllocatePacketPoolEx(status, pool_handle, num_descr, 0, ++ proto_rsvd_length); ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisFreePacketPool,1) ++ (struct ndis_packet_pool *pool) ++{ ++ struct ndis_packet *packet, *next; ++ ++ ENTER3("pool: %p", pool); ++ if (!pool) { ++ WARNING("invalid pool"); ++ EXIT3(return); ++ } ++ spin_lock_bh(&pool->lock); ++ packet = pool->free_descr; ++ while (packet) { ++ next = (struct ndis_packet *)packet->reserved[0]; ++ kfree(packet); ++ packet = next; ++ } ++ pool->num_allocated_descr = 0; ++ pool->num_used_descr = 0; ++ pool->free_descr = NULL; ++ spin_unlock_bh(&pool->lock); ++ kfree(pool); ++ EXIT3(return); ++} ++ ++wstdcall UINT WIN_FUNC(NdisPacketPoolUsage,1) ++ (struct ndis_packet_pool *pool) ++{ ++ EXIT4(return pool->num_used_descr); ++} ++ ++wstdcall void WIN_FUNC(NdisAllocatePacket,3) ++ (NDIS_STATUS *status, struct ndis_packet **ndis_packet, ++ struct ndis_packet_pool *pool) ++{ ++ struct ndis_packet *packet; ++ int packet_length; ++ ++ ENTER4("pool: %p", pool); ++ if (!pool) { ++ *status = NDIS_STATUS_RESOURCES; ++ *ndis_packet = NULL; ++ EXIT4(return); ++ } ++ assert_irql(_irql_ <= SOFT_LEVEL); ++ if (pool->num_used_descr > pool->max_descr) { ++ TRACE3("pool %p is full: %d(%d)", pool, ++ pool->num_used_descr, pool->max_descr); ++#ifndef ALLOW_POOL_OVERFLOW ++ *status = NDIS_STATUS_RESOURCES; ++ *ndis_packet = NULL; ++ return; ++#endif ++ } ++ /* packet has space for 1 byte in protocol_reserved field */ ++ packet_length = sizeof(*packet) - 1 + pool->proto_rsvd_length + ++ sizeof(struct ndis_packet_oob_data); ++ spin_lock_bh(&pool->lock); ++ if ((packet = pool->free_descr)) ++ pool->free_descr = (void *)packet->reserved[0]; ++ spin_unlock_bh(&pool->lock); ++ if (!packet) { ++ packet = kmalloc(packet_length, irql_gfp()); ++ if (!packet) { ++ WARNING("couldn't allocate packet"); ++ *status = NDIS_STATUS_RESOURCES; ++ *ndis_packet = NULL; ++ return; ++ } ++ atomic_inc_var(pool->num_allocated_descr); ++ } ++ TRACE4("%p, %p", pool, packet); ++ atomic_inc_var(pool->num_used_descr); ++ memset(packet, 0, packet_length); ++ packet->private.oob_offset = ++ packet_length - sizeof(struct ndis_packet_oob_data); ++ packet->private.packet_flags = fPACKET_ALLOCATED_BY_NDIS; ++ packet->private.pool = pool; ++ *ndis_packet = packet; ++ *status = NDIS_STATUS_SUCCESS; ++ EXIT4(return); ++} ++ ++wstdcall void WIN_FUNC(NdisDprAllocatePacket,3) ++ (NDIS_STATUS *status, struct ndis_packet **packet, ++ struct ndis_packet_pool *pool) ++{ ++ NdisAllocatePacket(status, packet, pool); ++} ++ ++wstdcall void WIN_FUNC(NdisFreePacket,1) ++ (struct ndis_packet *packet) ++{ ++ struct ndis_packet_pool *pool; ++ ++ ENTER4("%p, %p", packet, packet->private.pool); ++ pool = packet->private.pool; ++ if (!pool) { ++ ERROR("invalid pool %p", packet); ++ EXIT4(return); ++ } ++ assert((int)pool->num_used_descr > 0); ++ atomic_dec_var(pool->num_used_descr); ++ if (packet->reserved[1]) { ++ TRACE3("%p, %p", packet, (void *)packet->reserved[1]); ++ kfree((void *)packet->reserved[1]); ++ packet->reserved[1] = 0; ++ } ++ if (pool->num_allocated_descr > MAX_ALLOCATED_NDIS_PACKETS) { ++ TRACE3("%p", pool); ++ atomic_dec_var(pool->num_allocated_descr); ++ kfree(packet); ++ } else { ++ TRACE4("%p, %p, %p", pool, packet, pool->free_descr); ++ spin_lock_bh(&pool->lock); ++ packet->reserved[0] = ++ (typeof(packet->reserved[0]))pool->free_descr; ++ pool->free_descr = packet; ++ spin_unlock_bh(&pool->lock); ++ } ++ EXIT4(return); ++} ++ ++wstdcall struct ndis_packet_stack *WIN_FUNC(NdisIMGetCurrentPacketStack,2) ++ (struct ndis_packet *packet, BOOLEAN *stacks_remain) ++{ ++ struct ndis_packet_stack *stack; ++ ++ if (!packet->reserved[1]) { ++ stack = kzalloc(2 * sizeof(*stack), irql_gfp()); ++ TRACE3("%p, %p", packet, stack); ++ packet->reserved[1] = (typeof(packet->reserved[1]))stack; ++ } else { ++ stack = (void *)packet->reserved[1];; ++ if (xchg(&stack->ndis_reserved[0], 1)) { ++ stack++; ++ if (xchg(&stack->ndis_reserved[0], 1)) ++ stack = NULL; ++ } ++ TRACE3("%p", stack); ++ } ++ if (stack) ++ *stacks_remain = TRUE; ++ else ++ *stacks_remain = FALSE; ++ ++ EXIT3(return stack); ++} ++ ++wstdcall void WIN_FUNC(NdisCopyFromPacketToPacketSafe,7) ++ (struct ndis_packet *dst, UINT dst_offset, UINT num_to_copy, ++ struct ndis_packet *src, UINT src_offset, UINT *num_copied, ++ enum mm_page_priority priority) ++{ ++ UINT dst_n, src_n, n, left; ++ ndis_buffer *dst_buf; ++ ndis_buffer *src_buf; ++ ++ ENTER4(""); ++ if (!dst || !src) { ++ *num_copied = 0; ++ EXIT4(return); ++ } ++ ++ dst_buf = dst->private.buffer_head; ++ src_buf = src->private.buffer_head; ++ ++ if (!dst_buf || !src_buf) { ++ *num_copied = 0; ++ EXIT4(return); ++ } ++ dst_n = MmGetMdlByteCount(dst_buf) - dst_offset; ++ src_n = MmGetMdlByteCount(src_buf) - src_offset; ++ ++ n = min(src_n, dst_n); ++ n = min(n, num_to_copy); ++ memcpy(MmGetSystemAddressForMdl(dst_buf) + dst_offset, ++ MmGetSystemAddressForMdl(src_buf) + src_offset, n); ++ ++ left = num_to_copy - n; ++ while (left > 0) { ++ src_offset += n; ++ dst_offset += n; ++ dst_n -= n; ++ src_n -= n; ++ if (dst_n == 0) { ++ dst_buf = dst_buf->next; ++ if (!dst_buf) ++ break; ++ dst_n = MmGetMdlByteCount(dst_buf); ++ dst_offset = 0; ++ } ++ if (src_n == 0) { ++ src_buf = src_buf->next; ++ if (!src_buf) ++ break; ++ src_n = MmGetMdlByteCount(src_buf); ++ src_offset = 0; ++ } ++ ++ n = min(src_n, dst_n); ++ n = min(n, left); ++ memcpy(MmGetSystemAddressForMdl(dst_buf) + dst_offset, ++ MmGetSystemAddressForMdl(src_buf) + src_offset, n); ++ left -= n; ++ } ++ *num_copied = num_to_copy - left; ++ EXIT4(return); ++} ++ ++wstdcall void WIN_FUNC(NdisCopyFromPacketToPacket,6) ++ (struct ndis_packet *dst, UINT dst_offset, UINT num_to_copy, ++ struct ndis_packet *src, UINT src_offset, UINT *num_copied) ++{ ++ NdisCopyFromPacketToPacketSafe(dst, dst_offset, num_to_copy, ++ src, src_offset, num_copied, ++ NormalPagePriority); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisIMCopySendPerPacketInfo,2) ++ (struct ndis_packet *dst, struct ndis_packet *src) ++{ ++ struct ndis_packet_oob_data *dst_oob, *src_oob; ++ dst_oob = NDIS_PACKET_OOB_DATA(dst); ++ src_oob = NDIS_PACKET_OOB_DATA(src); ++ memcpy(&dst_oob->ext, &src_oob->ext, sizeof(dst_oob->ext)); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisSend,3) ++ (NDIS_STATUS *status, struct ndis_mp_block *nmb, ++ struct ndis_packet *packet) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ struct miniport *mp; ++ KIRQL irql; ++ ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ if (mp->send_packets) { ++ irql = serialize_lock_irql(wnd); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx, &packet, 1); ++ serialize_unlock_irql(wnd, irql); ++ if (deserialized_driver(wnd)) ++ *status = NDIS_STATUS_PENDING; ++ else { ++ struct ndis_packet_oob_data *oob_data; ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ *status = oob_data->status; ++ switch (*status) { ++ case NDIS_STATUS_SUCCESS: ++ free_tx_packet(wnd, packet, *status); ++ break; ++ case NDIS_STATUS_PENDING: ++ break; ++ case NDIS_STATUS_RESOURCES: ++ wnd->tx_ok = 0; ++ break; ++ case NDIS_STATUS_FAILURE: ++ default: ++ free_tx_packet(wnd, packet, *status); ++ break; ++ } ++ } ++ } else { ++ irql = serialize_lock_irql(wnd); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ *status = LIN2WIN3(mp->send, wnd->nmb->mp_ctx, packet, 0); ++ serialize_unlock_irql(wnd, irql); ++ switch (*status) { ++ case NDIS_STATUS_SUCCESS: ++ free_tx_packet(wnd, packet, *status); ++ break; ++ case NDIS_STATUS_PENDING: ++ break; ++ case NDIS_STATUS_RESOURCES: ++ wnd->tx_ok = 0; ++ break; ++ case NDIS_STATUS_FAILURE: ++ default: ++ free_tx_packet(wnd, packet, *status); ++ break; ++ } ++ } ++ EXIT3(return); ++} ++ ++/* called for serialized drivers only */ ++wstdcall void mp_timer_dpc(struct kdpc *kdpc, void *ctx, void *arg1, void *arg2) ++{ ++ struct ndis_mp_timer *timer; ++ struct ndis_mp_block *nmb; ++ ++ timer = ctx; ++ TIMERENTER("%p, %p, %p, %p", timer, timer->func, timer->ctx, timer->nmb); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ nmb = timer->nmb; ++ serialize_lock(nmb->wnd); ++ LIN2WIN4(timer->func, NULL, timer->ctx, NULL, NULL); ++ serialize_unlock(nmb->wnd); ++ TIMEREXIT(return); ++} ++WIN_FUNC_DECL(mp_timer_dpc,4) ++ ++wstdcall void WIN_FUNC(NdisMInitializeTimer,4) ++ (struct ndis_mp_timer *timer, struct ndis_mp_block *nmb, ++ DPC func, void *ctx) ++{ ++ TIMERENTER("%p, %p, %p, %p", timer, func, ctx, nmb); ++ assert_irql(_irql_ == PASSIVE_LEVEL); ++ timer->func = func; ++ timer->ctx = ctx; ++ timer->nmb = nmb; ++ if (deserialized_driver(nmb->wnd)) ++ KeInitializeDpc(&timer->kdpc, func, ctx); ++ else ++ KeInitializeDpc(&timer->kdpc, WIN_FUNC_PTR(mp_timer_dpc,4), ++ timer); ++ wrap_init_timer(&timer->nt_timer, NotificationTimer, nmb); ++ TIMEREXIT(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMSetPeriodicTimer,2) ++ (struct ndis_mp_timer *timer, UINT period_ms) ++{ ++ unsigned long expires = MSEC_TO_HZ(period_ms); ++ ++ TIMERENTER("%p, %u, %ld", timer, period_ms, expires); ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ wrap_set_timer(&timer->nt_timer, expires, expires, &timer->kdpc); ++ TIMEREXIT(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMCancelTimer,2) ++ (struct ndis_mp_timer *timer, BOOLEAN *canceled) ++{ ++ TIMERENTER("%p", timer); ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ *canceled = KeCancelTimer(&timer->nt_timer); ++ TIMERTRACE("%d", *canceled); ++ return; ++} ++ ++wstdcall void WIN_FUNC(NdisInitializeTimer,3) ++ (struct ndis_timer *timer, void *func, void *ctx) ++{ ++ TIMERENTER("%p, %p, %p", timer, func, ctx); ++ assert_irql(_irql_ == PASSIVE_LEVEL); ++ KeInitializeDpc(&timer->kdpc, func, ctx); ++ wrap_init_timer(&timer->nt_timer, NotificationTimer, NULL); ++ TIMEREXIT(return); ++} ++ ++/* NdisMSetTimer is a macro that calls NdisSetTimer with ++ * ndis_mp_timer typecast to ndis_timer */ ++ ++wstdcall void WIN_FUNC(NdisSetTimer,2) ++ (struct ndis_timer *timer, UINT duetime_ms) ++{ ++ unsigned long expires = MSEC_TO_HZ(duetime_ms); ++ ++ TIMERENTER("%p, %p, %u, %ld", timer, timer->nt_timer.wrap_timer, ++ duetime_ms, expires); ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ wrap_set_timer(&timer->nt_timer, expires, 0, &timer->kdpc); ++ TIMEREXIT(return); ++} ++ ++wstdcall void WIN_FUNC(NdisCancelTimer,2) ++ (struct ndis_timer *timer, BOOLEAN *canceled) ++{ ++ TIMERENTER("%p", timer); ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ *canceled = KeCancelTimer(&timer->nt_timer); ++ TIMEREXIT(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMRegisterAdapterShutdownHandler,3) ++ (struct ndis_mp_block *nmb, void *ctx, void *func) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ENTER1("%p", func); ++ wnd->wd->driver->ndis_driver->mp.shutdown = func; ++ wnd->shutdown_ctx = ctx; ++} ++ ++wstdcall void WIN_FUNC(NdisMDeregisterAdapterShutdownHandler,1) ++ (struct ndis_mp_block *nmb) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ wnd->wd->driver->ndis_driver->mp.shutdown = NULL; ++ wnd->shutdown_ctx = NULL; ++} ++ ++/* TODO: rt61 (serialized) driver doesn't want MiniportEnableInterrupt ++ * to be called in irq handler, but mrv800c (deserialized) driver ++ * wants. NDIS is confusing about when to call MiniportEnableInterrupt ++ * For now, handle these cases with two separate irq handlers based on ++ * observation of these two drivers. However, it is likely not ++ * correct. */ ++wstdcall void deserialized_irq_handler(struct kdpc *kdpc, void *ctx, ++ void *arg1, void *arg2) ++{ ++ struct ndis_device *wnd = ctx; ++ ndis_interrupt_handler irq_handler = arg1; ++ struct miniport *mp = arg2; ++ ++ TRACE6("%p", irq_handler); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ LIN2WIN1(irq_handler, wnd->nmb->mp_ctx); ++ if (mp->enable_interrupt) ++ LIN2WIN1(mp->enable_interrupt, wnd->nmb->mp_ctx); ++ EXIT6(return); ++} ++WIN_FUNC_DECL(deserialized_irq_handler,4) ++ ++wstdcall void serialized_irq_handler(struct kdpc *kdpc, void *ctx, ++ void *arg1, void *arg2) ++{ ++ struct ndis_device *wnd = ctx; ++ ndis_interrupt_handler irq_handler = arg1; ++ ++ TRACE6("%p, %p, %p", wnd, irq_handler, arg2); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ serialize_lock(wnd); ++ LIN2WIN1(irq_handler, arg2); ++ serialize_unlock(wnd); ++ EXIT6(return); ++} ++WIN_FUNC_DECL(serialized_irq_handler,4) ++ ++wstdcall BOOLEAN ndis_isr(struct kinterrupt *kinterrupt, void *ctx) ++{ ++ struct ndis_mp_interrupt *mp_interrupt = ctx; ++ struct ndis_device *wnd = mp_interrupt->nmb->wnd; ++ BOOLEAN recognized = TRUE, queue_handler = TRUE; ++ ++ TRACE6("%p", wnd); ++ /* kernel may call ISR when registering interrupt, in ++ * the same context if DEBUG_SHIRQ is enabled */ ++ assert_irql(_irql_ == DIRQL || _irql_ == PASSIVE_LEVEL); ++ if (mp_interrupt->shared) ++ LIN2WIN3(mp_interrupt->isr, &recognized, &queue_handler, ++ wnd->nmb->mp_ctx); ++ else { ++ struct miniport *mp; ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ LIN2WIN1(mp->disable_interrupt, wnd->nmb->mp_ctx); ++ /* it is not shared interrupt, so handler must be called */ ++ recognized = queue_handler = TRUE; ++ } ++ if (recognized) { ++ if (queue_handler) { ++ TRACE5("%p", &wnd->irq_kdpc); ++ queue_kdpc(&wnd->irq_kdpc); ++ } ++ EXIT6(return TRUE); ++ } ++ EXIT6(return FALSE); ++} ++WIN_FUNC_DECL(ndis_isr,2) ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterInterrupt,7) ++ (struct ndis_mp_interrupt *mp_interrupt, ++ struct ndis_mp_block *nmb, UINT vector, UINT level, ++ BOOLEAN req_isr, BOOLEAN shared, enum kinterrupt_mode mode) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ struct miniport *mp; ++ ++ ENTER1("%p, vector:%d, level:%d, req_isr:%d, shared:%d, mode:%d", ++ mp_interrupt, vector, level, req_isr, shared, mode); ++ ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ nt_spin_lock_init(&mp_interrupt->lock); ++ mp_interrupt->irq = vector; ++ mp_interrupt->isr = mp->isr; ++ mp_interrupt->mp_dpc = mp->handle_interrupt; ++ mp_interrupt->nmb = nmb; ++ mp_interrupt->req_isr = req_isr; ++ if (shared && !req_isr) ++ WARNING("shared but dynamic interrupt!"); ++ mp_interrupt->shared = shared; ++ wnd->mp_interrupt = mp_interrupt; ++ if (mp->enable_interrupt) ++ mp_interrupt->enable = TRUE; ++ else ++ mp_interrupt->enable = FALSE; ++ ++ if (deserialized_driver(wnd)) { ++ KeInitializeDpc(&wnd->irq_kdpc, ++ WIN_FUNC_PTR(deserialized_irq_handler,4), ++ nmb->wnd); ++ wnd->irq_kdpc.arg1 = mp->handle_interrupt; ++ wnd->irq_kdpc.arg2 = mp; ++ TRACE2("%p, %p, %p, %p", wnd->irq_kdpc.arg1, wnd->irq_kdpc.arg2, ++ nmb->wnd, nmb->mp_ctx); ++ } else { ++ KeInitializeDpc(&wnd->irq_kdpc, ++ WIN_FUNC_PTR(serialized_irq_handler,4), ++ nmb->wnd); ++ wnd->irq_kdpc.arg1 = mp->handle_interrupt; ++ wnd->irq_kdpc.arg2 = nmb->mp_ctx; ++ TRACE2("%p, %p, %p", wnd->irq_kdpc.arg1, wnd->irq_kdpc.arg2, ++ nmb->wnd); ++ } ++ ++ if (IoConnectInterrupt(&mp_interrupt->kinterrupt, ++ WIN_FUNC_PTR(ndis_isr,2), mp_interrupt, NULL, ++ vector, DIRQL, DIRQL, mode, shared, 0, FALSE) != ++ STATUS_SUCCESS) { ++ printk(KERN_WARNING "%s: request for IRQ %d failed\n", ++ DRIVER_NAME, vector); ++ return NDIS_STATUS_RESOURCES; ++ } ++ printk(KERN_INFO "%s: using IRQ %d\n", DRIVER_NAME, vector); ++ EXIT1(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(NdisMDeregisterInterrupt,1) ++ (struct ndis_mp_interrupt *mp_interrupt) ++{ ++ struct ndis_mp_block *nmb; ++ ++ ENTER1("%p", mp_interrupt); ++ nmb = xchg(&mp_interrupt->nmb, NULL); ++ TRACE1("%p", nmb); ++ if (!nmb) { ++ WARNING("interrupt already freed?"); ++ return; ++ } ++ nmb->wnd->mp_interrupt = NULL; ++ if (dequeue_kdpc(&nmb->wnd->irq_kdpc)) ++ TRACE2("interrupt kdpc was pending"); ++ flush_workqueue(wrapndis_wq); ++ IoDisconnectInterrupt(mp_interrupt->kinterrupt); ++ EXIT1(return); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(NdisMSynchronizeWithInterrupt,3) ++ (struct ndis_mp_interrupt *mp_interrupt, ++ PKSYNCHRONIZE_ROUTINE sync_func, void *ctx) ++{ ++ return KeSynchronizeExecution(mp_interrupt->kinterrupt, sync_func, ctx); ++} ++ ++/* called via function pointer; but 64-bit RNDIS driver calls directly */ ++wstdcall void WIN_FUNC(NdisMIndicateStatus,4) ++ (struct ndis_mp_block *nmb, NDIS_STATUS status, void *buf, UINT len) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ struct ndis_status_indication *si; ++ ++ ENTER2("status=0x%x len=%d", status, len); ++ switch (status) { ++ case NDIS_STATUS_MEDIA_CONNECT: ++ set_media_state(wnd, NdisMediaStateConnected); ++ break; ++ case NDIS_STATUS_MEDIA_DISCONNECT: ++ set_media_state(wnd, NdisMediaStateDisconnected); ++ break; ++ case NDIS_STATUS_MEDIA_SPECIFIC_INDICATION: ++ if (!buf) ++ break; ++ si = buf; ++ TRACE2("status_type=%d", si->status_type); ++ switch (si->status_type) { ++ case Ndis802_11StatusType_MediaStreamMode: ++ break; ++#ifdef CONFIG_WIRELESS_EXT ++ case Ndis802_11StatusType_Authentication: ++ buf = (char *)buf + sizeof(*si); ++ len -= sizeof(*si); ++ while (len > 0) { ++ int pairwise_error = 0, group_error = 0; ++ struct ndis_auth_req *auth_req = ++ (struct ndis_auth_req *)buf; ++ TRACE1(MACSTRSEP, MAC2STR(auth_req->bssid)); ++ if (auth_req->flags & 0x01) ++ TRACE2("reauth request"); ++ if (auth_req->flags & 0x02) ++ TRACE2("key update request"); ++ if (auth_req->flags & 0x06) { ++ pairwise_error = 1; ++ TRACE2("pairwise_error"); ++ } ++ if (auth_req->flags & 0x0E) { ++ group_error = 1; ++ TRACE2("group_error"); ++ } ++ if (pairwise_error || group_error) { ++ union iwreq_data wrqu; ++ struct iw_michaelmicfailure micfailure; ++ ++ memset(&micfailure, 0, sizeof(micfailure)); ++ if (pairwise_error) ++ micfailure.flags |= ++ IW_MICFAILURE_PAIRWISE; ++ if (group_error) ++ micfailure.flags |= ++ IW_MICFAILURE_GROUP; ++ memcpy(micfailure.src_addr.sa_data, ++ auth_req->bssid, ETH_ALEN); ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wrqu.data.length = sizeof(micfailure); ++ wireless_send_event(wnd->net_dev, ++ IWEVMICHAELMICFAILURE, ++ &wrqu, (u8 *)&micfailure); ++ } ++ len -= auth_req->length; ++ buf = (char *)buf + auth_req->length; ++ } ++ break; ++ case Ndis802_11StatusType_PMKID_CandidateList: ++ { ++ u8 *end; ++ unsigned long i; ++ struct ndis_pmkid_candidate_list *cand; ++ ++ cand = buf + sizeof(struct ndis_status_indication); ++ if (len < sizeof(struct ndis_status_indication) + ++ sizeof(struct ndis_pmkid_candidate_list) || ++ cand->version != 1) { ++ WARNING("unrecognized PMKID ignored"); ++ EXIT1(return); ++ } ++ ++ end = (u8 *)buf + len; ++ TRACE2("PMKID ver %d num_cand %d", ++ cand->version, cand->num_candidates); ++ for (i = 0; i < cand->num_candidates; i++) { ++ struct iw_pmkid_cand pcand; ++ union iwreq_data wrqu; ++ struct ndis_pmkid_candidate *c = ++ &cand->candidates[i]; ++ if ((u8 *)(c + 1) > end) { ++ TRACE2("truncated PMKID"); ++ break; ++ } ++ TRACE2("%ld: " MACSTRSEP " 0x%x", ++ i, MAC2STR(c->bssid), c->flags); ++ memset(&pcand, 0, sizeof(pcand)); ++ if (c->flags & 0x01) ++ pcand.flags |= IW_PMKID_CAND_PREAUTH; ++ pcand.index = i; ++ memcpy(pcand.bssid.sa_data, c->bssid, ETH_ALEN); ++ ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wrqu.data.length = sizeof(pcand); ++ wireless_send_event(wnd->net_dev, IWEVPMKIDCAND, ++ &wrqu, (u8 *)&pcand); ++ } ++ break; ++ } ++ case Ndis802_11StatusType_RadioState: ++ { ++ struct ndis_radio_status_indication *radio_status = buf; ++ if (radio_status->radio_state == ++ Ndis802_11RadioStatusOn) ++ INFO("radio is turned on"); ++ else if (radio_status->radio_state == ++ Ndis802_11RadioStatusHardwareOff) ++ INFO("radio is turned off by hardware"); ++ else if (radio_status->radio_state == ++ Ndis802_11RadioStatusSoftwareOff) ++ INFO("radio is turned off by software"); ++ break; ++ } ++#endif ++ default: ++ /* is this RSSI indication? */ ++ TRACE2("unknown indication: %x", si->status_type); ++ break; ++ } ++ break; ++ default: ++ TRACE2("unknown status: %08X", status); ++ break; ++ } ++ ++ EXIT2(return); ++} ++ ++/* called via function pointer; but 64-bit RNDIS driver calls directly */ ++wstdcall void WIN_FUNC(NdisMIndicateStatusComplete,1) ++ (struct ndis_mp_block *nmb) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ENTER2("%p", wnd); ++ if (wnd->tx_ok) ++ queue_work(wrapndis_wq, &wnd->tx_work); ++} ++ ++/* called via function pointer */ ++wstdcall void NdisMSendComplete(struct ndis_mp_block *nmb, ++ struct ndis_packet *packet, NDIS_STATUS status) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ENTER4("%p, %08X", packet, status); ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ if (deserialized_driver(wnd)) ++ free_tx_packet(wnd, packet, status); ++ else { ++ struct ndis_packet_oob_data *oob_data; ++ NDIS_STATUS pkt_status; ++ TRACE3("%p, %08x", packet, status); ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ switch ((pkt_status = xchg(&oob_data->status, status))) { ++ case NDIS_STATUS_NOT_RECOGNIZED: ++ free_tx_packet(wnd, packet, status); ++ break; ++ case NDIS_STATUS_PENDING: ++ case 0: ++ break; ++ default: ++ WARNING("%p: invalid status: %08X", packet, pkt_status); ++ break; ++ } ++ /* In case a serialized driver has earlier requested a ++ * pause by returning NDIS_STATUS_RESOURCES during ++ * MiniportSend(Packets), wakeup tx worker now. ++ */ ++ if (xchg(&wnd->tx_ok, 1) == 0) { ++ TRACE3("%d, %d", wnd->tx_ring_start, wnd->tx_ring_end); ++ queue_work(wrapndis_wq, &wnd->tx_work); ++ } ++ } ++ EXIT3(return); ++} ++ ++/* called via function pointer */ ++wstdcall void NdisMSendResourcesAvailable(struct ndis_mp_block *nmb) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ENTER3("%d, %d", wnd->tx_ring_start, wnd->tx_ring_end); ++ wnd->tx_ok = 1; ++ queue_work(wrapndis_wq, &wnd->tx_work); ++ EXIT3(return); ++} ++ ++wstdcall void return_packet(void *arg1, void *arg2) ++{ ++ struct ndis_device *wnd; ++ struct ndis_packet *packet; ++ struct miniport *mp; ++ KIRQL irql; ++ ++ wnd = arg1; ++ packet = arg2; ++ ENTER4("%p, %p", wnd, packet); ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ irql = serialize_lock_irql(wnd); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ LIN2WIN2(mp->return_packet, wnd->nmb->mp_ctx, packet); ++ serialize_unlock_irql(wnd, irql); ++ EXIT4(return); ++} ++WIN_FUNC_DECL(return_packet,2) ++ ++/* called via function pointer */ ++wstdcall void NdisMIndicateReceivePacket(struct ndis_mp_block *nmb, ++ struct ndis_packet **packets, ++ UINT nr_packets) ++{ ++ struct ndis_device *wnd; ++ ndis_buffer *buffer; ++ struct ndis_packet *packet; ++ struct sk_buff *skb; ++ ULONG i, length, total_length; ++ struct ndis_packet_oob_data *oob_data; ++ void *virt; ++ struct ndis_tcp_ip_checksum_packet_info csum; ++ ++ ENTER3("%p, %d", nmb, nr_packets); ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ wnd = nmb->wnd; ++ for (i = 0; i < nr_packets; i++) { ++ packet = packets[i]; ++ if (!packet) { ++ WARNING("empty packet ignored"); ++ continue; ++ } ++ wnd->net_dev->last_rx = jiffies; ++ /* get total number of bytes in packet */ ++ NdisGetFirstBufferFromPacketSafe(packet, &buffer, &virt, ++ &length, &total_length, ++ NormalPagePriority); ++ TRACE3("%d, %d", length, total_length); ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ TRACE3("0x%x, 0x%x, %llu", packet->private.flags, ++ packet->private.packet_flags, oob_data->time_rxed); ++ skb = dev_alloc_skb(total_length); ++ if (skb) { ++ while (buffer) { ++ memcpy_skb(skb, MmGetSystemAddressForMdl(buffer), ++ MmGetMdlByteCount(buffer)); ++ buffer = buffer->next; ++ } ++ skb->dev = wnd->net_dev; ++ skb->protocol = eth_type_trans(skb, wnd->net_dev); ++ pre_atomic_add(wnd->net_stats.rx_bytes, total_length); ++ atomic_inc_var(wnd->net_stats.rx_packets); ++ csum.value = (typeof(csum.value))(ULONG_PTR) ++ oob_data->ext.info[TcpIpChecksumPacketInfo]; ++ TRACE3("0x%05x", csum.value); ++ if (wnd->rx_csum.value && ++ (csum.rx.tcp_succeeded || csum.rx.udp_succeeded || ++ csum.rx.ip_succeeded)) ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ else ++ skb->ip_summed = CHECKSUM_NONE; ++ ++ if (in_interrupt()) ++ netif_rx(skb); ++ else ++ netif_rx_ni(skb); ++ } else { ++ WARNING("couldn't allocate skb; packet dropped"); ++ atomic_inc_var(wnd->net_stats.rx_dropped); ++ } ++ ++ /* serialized drivers check the status upon return ++ * from this function */ ++ if (!deserialized_driver(wnd)) { ++ oob_data->status = NDIS_STATUS_SUCCESS; ++ continue; ++ } ++ ++ /* if a deserialized driver sets ++ * NDIS_STATUS_RESOURCES, then it reclaims the packet ++ * upon return from this function */ ++ if (oob_data->status == NDIS_STATUS_RESOURCES) ++ continue; ++ ++ assert(oob_data->status == NDIS_STATUS_SUCCESS); ++ /* deserialized driver doesn't check the status upon ++ * return from this function; we need to call ++ * MiniportReturnPacket later for this packet. Calling ++ * MiniportReturnPacket from here is not correct - the ++ * driver doesn't expect it (at least Centrino driver ++ * crashes) */ ++ schedule_ntos_work_item(WIN_FUNC_PTR(return_packet,2), ++ wnd, packet); ++ } ++ EXIT3(return); ++} ++ ++/* called via function pointer (by NdisMEthIndicateReceive macro); the ++ * first argument is nmb->eth_db */ ++wstdcall void EthRxIndicateHandler(struct ndis_mp_block *nmb, void *rx_ctx, ++ char *header1, char *header, UINT header_size, ++ void *look_ahead, UINT look_ahead_size, ++ UINT packet_size) ++{ ++ struct sk_buff *skb = NULL; ++ struct ndis_device *wnd; ++ unsigned int skb_size = 0; ++ KIRQL irql; ++ struct ndis_packet_oob_data *oob_data; ++ ++ ENTER3("nmb = %p, rx_ctx = %p, buf = %p, size = %d, buf = %p, " ++ "size = %d, packet = %d", nmb, rx_ctx, header, header_size, ++ look_ahead, look_ahead_size, packet_size); ++ ++ wnd = nmb->wnd; ++ TRACE3("wnd = %p", wnd); ++ if (!wnd) { ++ ERROR("nmb is NULL"); ++ EXIT3(return); ++ } ++ wnd->net_dev->last_rx = jiffies; ++ ++ if (look_ahead_size < packet_size) { ++ struct ndis_packet *packet; ++ struct miniport *mp; ++ unsigned int bytes_txed; ++ NDIS_STATUS res; ++ ++ NdisAllocatePacket(&res, &packet, wnd->tx_packet_pool); ++ if (res != NDIS_STATUS_SUCCESS) { ++ atomic_inc_var(wnd->net_stats.rx_dropped); ++ EXIT3(return); ++ } ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ irql = serialize_lock_irql(wnd); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ res = LIN2WIN6(mp->tx_data, packet, &bytes_txed, nmb, ++ rx_ctx, look_ahead_size, packet_size); ++ serialize_unlock_irql(wnd, irql); ++ TRACE3("%d, %d, %d", header_size, look_ahead_size, bytes_txed); ++ if (res == NDIS_STATUS_SUCCESS) { ++ ndis_buffer *buffer; ++ struct ndis_tcp_ip_checksum_packet_info csum; ++ skb = dev_alloc_skb(header_size + look_ahead_size + ++ bytes_txed); ++ if (!skb) { ++ ERROR("couldn't allocate skb; packet dropped"); ++ atomic_inc_var(wnd->net_stats.rx_dropped); ++ NdisFreePacket(packet); ++ return; ++ } ++ memcpy_skb(skb, header, header_size); ++ memcpy_skb(skb, look_ahead, look_ahead_size); ++ buffer = packet->private.buffer_head; ++ while (buffer) { ++ memcpy_skb(skb, ++ MmGetSystemAddressForMdl(buffer), ++ MmGetMdlByteCount(buffer)); ++ buffer = buffer->next; ++ } ++ skb_size = header_size + look_ahead_size + bytes_txed; ++ csum.value = (typeof(csum.value))(ULONG_PTR) ++ oob_data->ext.info[TcpIpChecksumPacketInfo]; ++ TRACE3("0x%05x", csum.value); ++ if (wnd->rx_csum.value && ++ (csum.rx.tcp_succeeded || csum.rx.udp_succeeded)) ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ else ++ skb->ip_summed = CHECKSUM_NONE; ++ NdisFreePacket(packet); ++ } else if (res == NDIS_STATUS_PENDING) { ++ /* driver will call td_complete */ ++ oob_data->look_ahead = kmalloc(look_ahead_size, ++ GFP_ATOMIC); ++ if (!oob_data->look_ahead) { ++ NdisFreePacket(packet); ++ ERROR("packet dropped"); ++ atomic_inc_var(wnd->net_stats.rx_dropped); ++ EXIT3(return); ++ } ++ assert(sizeof(oob_data->header) == header_size); ++ memcpy(oob_data->header, header, ++ sizeof(oob_data->header)); ++ memcpy(oob_data->look_ahead, look_ahead, ++ look_ahead_size); ++ oob_data->look_ahead_size = look_ahead_size; ++ EXIT3(return); ++ } else { ++ WARNING("packet dropped: %08X", res); ++ atomic_inc_var(wnd->net_stats.rx_dropped); ++ NdisFreePacket(packet); ++ EXIT3(return); ++ } ++ } else { ++ skb_size = header_size + packet_size; ++ skb = dev_alloc_skb(skb_size); ++ if (skb) { ++ memcpy_skb(skb, header, header_size); ++ memcpy_skb(skb, look_ahead, packet_size); ++ } ++ } ++ ++ if (skb) { ++ skb->dev = wnd->net_dev; ++ skb->protocol = eth_type_trans(skb, wnd->net_dev); ++ pre_atomic_add(wnd->net_stats.rx_bytes, skb_size); ++ atomic_inc_var(wnd->net_stats.rx_packets); ++ if (in_interrupt()) ++ netif_rx(skb); ++ else ++ netif_rx_ni(skb); ++ } ++ ++ EXIT3(return); ++} ++ ++/* called via function pointer */ ++wstdcall void NdisMTransferDataComplete(struct ndis_mp_block *nmb, ++ struct ndis_packet *packet, ++ NDIS_STATUS status, UINT bytes_txed) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ struct sk_buff *skb; ++ unsigned int skb_size; ++ struct ndis_packet_oob_data *oob_data; ++ ndis_buffer *buffer; ++ struct ndis_tcp_ip_checksum_packet_info csum; ++ ++ ENTER3("wnd = %p, packet = %p, bytes_txed = %d", ++ wnd, packet, bytes_txed); ++ if (!packet) { ++ WARNING("illegal packet"); ++ EXIT3(return); ++ } ++ wnd->net_dev->last_rx = jiffies; ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ skb_size = sizeof(oob_data->header) + oob_data->look_ahead_size + ++ bytes_txed; ++ skb = dev_alloc_skb(skb_size); ++ if (!skb) { ++ kfree(oob_data->look_ahead); ++ NdisFreePacket(packet); ++ ERROR("couldn't allocate skb; packet dropped"); ++ atomic_inc_var(wnd->net_stats.rx_dropped); ++ EXIT3(return); ++ } ++ memcpy_skb(skb, oob_data->header, sizeof(oob_data->header)); ++ memcpy_skb(skb, oob_data->look_ahead, oob_data->look_ahead_size); ++ buffer = packet->private.buffer_head; ++ while (buffer) { ++ memcpy_skb(skb, MmGetSystemAddressForMdl(buffer), ++ MmGetMdlByteCount(buffer)); ++ buffer = buffer->next; ++ } ++ kfree(oob_data->look_ahead); ++ NdisFreePacket(packet); ++ skb->dev = wnd->net_dev; ++ skb->protocol = eth_type_trans(skb, wnd->net_dev); ++ pre_atomic_add(wnd->net_stats.rx_bytes, skb_size); ++ atomic_inc_var(wnd->net_stats.rx_packets); ++ ++ csum.value = (typeof(csum.value))(ULONG_PTR) ++ oob_data->ext.info[TcpIpChecksumPacketInfo]; ++ TRACE3("0x%05x", csum.value); ++ if (wnd->rx_csum.value && ++ (csum.rx.tcp_succeeded || csum.rx.udp_succeeded)) ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ else ++ skb->ip_summed = CHECKSUM_NONE; ++ ++ if (in_interrupt()) ++ netif_rx(skb); ++ else ++ netif_rx_ni(skb); ++} ++ ++/* called via function pointer */ ++wstdcall void EthRxComplete(struct ndis_mp_block *nmb) ++{ ++ TRACE3(""); ++} ++ ++/* called via function pointer */ ++wstdcall void NdisMQueryInformationComplete(struct ndis_mp_block *nmb, ++ NDIS_STATUS status) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ typeof(wnd->ndis_req_task) task; ++ ++ ENTER2("nmb: %p, wnd: %p, %08X", nmb, wnd, status); ++ wnd->ndis_req_status = status; ++ wnd->ndis_req_done = 1; ++ if ((task = xchg(&wnd->ndis_req_task, NULL))) ++ wake_up_process(task); ++ else ++ WARNING("invalid task"); ++ EXIT2(return); ++} ++ ++/* called via function pointer */ ++wstdcall void NdisMSetInformationComplete(struct ndis_mp_block *nmb, ++ NDIS_STATUS status) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ typeof(wnd->ndis_req_task) task; ++ ++ ENTER2("status = %08X", status); ++ wnd->ndis_req_status = status; ++ wnd->ndis_req_done = 1; ++ if ((task = xchg(&wnd->ndis_req_task, NULL))) ++ wake_up_process(task); ++ else ++ WARNING("invalid task"); ++ EXIT2(return); ++} ++ ++/* called via function pointer */ ++wstdcall void NdisMResetComplete(struct ndis_mp_block *nmb, ++ NDIS_STATUS status, BOOLEAN address_reset) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ typeof(wnd->ndis_req_task) task; ++ ++ ENTER2("status: %08X, %u", status, address_reset); ++ wnd->ndis_req_status = status; ++ wnd->ndis_req_done = address_reset + 1; ++ if ((task = xchg(&wnd->ndis_req_task, NULL))) ++ wake_up_process(task); ++ else ++ WARNING("invalid task"); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMSleep,1) ++ (ULONG us) ++{ ++ unsigned long delay; ++ ++ ENTER4("%p: us: %u", current, us); ++ delay = USEC_TO_HZ(us); ++ sleep_hz(delay); ++ TRACE4("%p: done", current); ++} ++ ++wstdcall void WIN_FUNC(NdisGetCurrentSystemTime,1) ++ (LARGE_INTEGER *time) ++{ ++ *time = ticks_1601(); ++ TRACE5("%llu, %lu", *time, jiffies); ++} ++ ++wstdcall LONG WIN_FUNC(NdisInterlockedDecrement,1) ++ (LONG *val) ++{ ++ return InterlockedDecrement(val); ++} ++ ++wstdcall LONG WIN_FUNC(NdisInterlockedIncrement,1) ++ (LONG *val) ++{ ++ return InterlockedIncrement(val); ++} ++ ++wstdcall struct nt_list *WIN_FUNC(NdisInterlockedInsertHeadList,3) ++ (struct nt_list *head, struct nt_list *entry, ++ struct ndis_spinlock *lock) ++{ ++ return ExInterlockedInsertHeadList(head, entry, &lock->klock); ++} ++ ++wstdcall struct nt_list *WIN_FUNC(NdisInterlockedInsertTailList,3) ++ (struct nt_list *head, struct nt_list *entry, ++ struct ndis_spinlock *lock) ++{ ++ return ExInterlockedInsertTailList(head, entry, &lock->klock); ++} ++ ++wstdcall struct nt_list *WIN_FUNC(NdisInterlockedRemoveHeadList,2) ++ (struct nt_list *head, struct ndis_spinlock *lock) ++{ ++ return ExInterlockedRemoveHeadList(head, &lock->klock); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMInitializeScatterGatherDma,3) ++ (struct ndis_mp_block *nmb, BOOLEAN dma64_supported, ULONG max_phy_map) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ ENTER2("dma64_supported=%d, maxtransfer=%u", dma64_supported, ++ max_phy_map); ++ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ ERROR("used on a non-PCI device"); ++ return NDIS_STATUS_NOT_SUPPORTED; ++ } ++#ifdef CONFIG_X86_64 ++ if (!dma64_supported) { ++ TRACE1("64-bit DMA size is not supported"); ++ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(32)) || ++ pci_set_consistent_dma_mask(wnd->wd->pci.pdev, ++ DMA_BIT_MASK(32))) ++ WARNING("setting dma mask failed"); ++ } ++#endif ++ if ((wnd->attributes & NDIS_ATTRIBUTE_BUS_MASTER) && ++ wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ wnd->sg_dma_size = max_phy_map; ++ return NDIS_STATUS_SUCCESS; ++ } else ++ EXIT1(return NDIS_STATUS_NOT_SUPPORTED); ++} ++ ++wstdcall ULONG WIN_FUNC(NdisMGetDmaAlignment,1) ++ (struct ndis_mp_block *nmb) ++{ ++ ENTER3(""); ++ return dma_get_cache_alignment(); ++} ++ ++wstdcall CHAR WIN_FUNC(NdisSystemProcessorCount,0) ++ (void) ++{ ++ return num_online_cpus(); ++} ++ ++wstdcall void WIN_FUNC(NdisGetCurrentProcessorCounts,3) ++ (ULONG *idle, ULONG *kernel_user, ULONG *index) ++{ ++ int cpu = smp_processor_id(); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) ++ *idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; ++ *kernel_user = kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM] + ++ kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; ++#else ++ *idle = kstat_cpu(cpu).cpustat.idle; ++ *kernel_user = kstat_cpu(cpu).cpustat.system + ++ kstat_cpu(cpu).cpustat.user; ++#endif ++ *index = cpu; ++} ++ ++wstdcall void WIN_FUNC(NdisInitializeEvent,1) ++ (struct ndis_event *ndis_event) ++{ ++ EVENTENTER("%p", ndis_event); ++ KeInitializeEvent(&ndis_event->nt_event, NotificationEvent, 0); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(NdisWaitEvent,2) ++ (struct ndis_event *ndis_event, UINT ms) ++{ ++ LARGE_INTEGER ticks; ++ NTSTATUS res; ++ ++ EVENTENTER("%p %u", ndis_event, ms); ++ ticks = -((LARGE_INTEGER)ms * TICKSPERMSEC); ++ res = KeWaitForSingleObject(&ndis_event->nt_event, 0, 0, TRUE, ++ ms == 0 ? NULL : &ticks); ++ if (res == STATUS_SUCCESS) ++ EXIT3(return TRUE); ++ else ++ EXIT3(return FALSE); ++} ++ ++wstdcall void WIN_FUNC(NdisSetEvent,1) ++ (struct ndis_event *ndis_event) ++{ ++ EVENTENTER("%p", ndis_event); ++ KeSetEvent(&ndis_event->nt_event, 0, 0); ++} ++ ++wstdcall void WIN_FUNC(NdisResetEvent,1) ++ (struct ndis_event *ndis_event) ++{ ++ EVENTENTER("%p", ndis_event); ++ KeResetEvent(&ndis_event->nt_event); ++} ++ ++static void ndis_worker(struct work_struct *dummy) ++{ ++ struct nt_list *ent; ++ struct ndis_work_item *ndis_work_item; ++ ++ WORKENTER(""); ++ while (1) { ++ spin_lock_bh(&ndis_work_list_lock); ++ ent = RemoveHeadList(&ndis_work_list); ++ spin_unlock_bh(&ndis_work_list_lock); ++ if (!ent) ++ break; ++ ndis_work_item = container_of(ent, struct ndis_work_item, list); ++ WORKTRACE("%p: %p, %p", ndis_work_item, ++ ndis_work_item->func, ndis_work_item->ctx); ++ LIN2WIN2(ndis_work_item->func, ndis_work_item, ++ ndis_work_item->ctx); ++ WORKTRACE("%p done", ndis_work_item); ++ } ++ WORKEXIT(return); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisScheduleWorkItem,1) ++ (struct ndis_work_item *ndis_work_item) ++{ ++ ENTER3("%p", ndis_work_item); ++ spin_lock_bh(&ndis_work_list_lock); ++ InsertTailList(&ndis_work_list, &ndis_work_item->list); ++ spin_unlock_bh(&ndis_work_list_lock); ++ WORKTRACE("scheduling %p", ndis_work_item); ++ queue_work(ndis_wq, &ndis_work); ++ EXIT3(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(NdisMGetDeviceProperty,6) ++ (struct ndis_mp_block *nmb, void **phy_dev, void **func_dev, ++ void **next_dev, void **alloc_res, void**trans_res) ++{ ++ ENTER2("nmb: %p, phy_dev = %p, func_dev = %p, next_dev = %p, " ++ "alloc_res = %p, trans_res = %p", nmb, phy_dev, func_dev, ++ next_dev, alloc_res, trans_res); ++ if (phy_dev) ++ *phy_dev = nmb->pdo; ++ if (func_dev) ++ *func_dev = nmb->fdo; ++ if (next_dev) ++ *next_dev = nmb->next_device; ++} ++ ++wstdcall void WIN_FUNC(NdisMRegisterUnloadHandler,2) ++ (struct driver_object *drv_obj, void *unload) ++{ ++ if (drv_obj) ++ drv_obj->unload = unload; ++ return; ++} ++ ++wstdcall UINT WIN_FUNC(NdisGetVersion,0) ++ (void) ++{ ++ return 0x00050001; ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMQueryAdapterInstanceName,2) ++ (struct unicode_string *name, struct ndis_mp_block *nmb) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ struct ansi_string ansi; ++ ++ if (wrap_is_pci_bus(wnd->wd->dev_bus)) ++ RtlInitAnsiString(&ansi, "PCI Ethernet Adapter"); ++ else ++ RtlInitAnsiString(&ansi, "USB Ethernet Adapter"); ++ ++ if (RtlAnsiStringToUnicodeString(name, &ansi, TRUE)) ++ EXIT2(return NDIS_STATUS_RESOURCES); ++ else ++ EXIT2(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisWriteEventLogEntry,7) ++ (void *handle, NDIS_STATUS code, ULONG value, USHORT n, ++ void *strings, ULONG datasize, void *data) ++{ ++ TRACE1("0x%x, 0x%x, %u, %u", code, value, n, datasize); ++ return NDIS_STATUS_SUCCESS; ++} ++ ++wstdcall void *WIN_FUNC(NdisGetRoutineAddress,1) ++ (struct unicode_string *unicode_string) ++{ ++ struct ansi_string ansi_string; ++ void *address; ++ ++ if (RtlUnicodeStringToAnsiString(&ansi_string, unicode_string, TRUE) != ++ STATUS_SUCCESS) ++ EXIT1(return NULL); ++ INFO("%s", ansi_string.buf); ++ address = ndis_get_routine_address(ansi_string.buf); ++ RtlFreeAnsiString(&ansi_string); ++ return address; ++} ++ ++wstdcall ULONG WIN_FUNC(NdisReadPcmciaAttributeMemory,4) ++ (struct ndis_mp_block *nmb, ULONG offset, void *buffer, ++ ULONG length) ++{ ++ TODO(); ++ return 0; ++} ++ ++wstdcall ULONG WIN_FUNC(NdisWritePcmciaAttributeMemory,4) ++ (struct ndis_mp_block *nmb, ULONG offset, void *buffer, ++ ULONG length) ++{ ++ TODO(); ++ return 0; ++} ++ ++wstdcall void WIN_FUNC(NdisMCoIndicateReceivePacket,3) ++ (struct ndis_mp_block *nmb, struct ndis_packet **packets, ++ UINT nr_packets) ++{ ++ ENTER3("nmb = %p", nmb); ++ NdisMIndicateReceivePacket(nmb, packets, nr_packets); ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMCoSendComplete,3) ++ (NDIS_STATUS status, struct ndis_mp_block *nmb, ++ struct ndis_packet *packet) ++{ ++ ENTER3("%08x", status); ++ NdisMSendComplete(nmb, packet, status); ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(NdisMCoRequestComplete,3) ++ (NDIS_STATUS status, struct ndis_mp_block *nmb, ++ struct ndis_request *ndis_request) ++{ ++ struct ndis_device *wnd = nmb->wnd; ++ typeof(wnd->ndis_req_task) task; ++ ++ ENTER3("%08X", status); ++ wnd->ndis_req_status = status; ++ wnd->ndis_req_done = 1; ++ if ((task = xchg(&wnd->ndis_req_task, NULL))) ++ wake_up_process(task); ++ else ++ WARNING("invalid task"); ++ EXIT3(return); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisIMNotifiyPnPEvent,2) ++ (struct ndis_mp_block *nmb, struct net_pnp_event *event) ++{ ++ ENTER2("%p, %d", nmb, event->code); ++ /* NdisWrapper never calls protocol's pnp event notifier, so ++ * nothing to do here */ ++ EXIT2(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(NdisCompletePnPEvent,3) ++ (NDIS_STATUS status, void *handle, struct net_pnp_event *event) ++{ ++ ENTER2("%d, %p, %d", status, handle, event->code); ++ /* NdisWrapper never calls protocol's pnp event notifier, so ++ * nothing to do here */ ++ EXIT2(return); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMSetMiniportSecondary,2) ++ (struct ndis_mp_block *nmb2, struct ndis_mp_block *nmb1) ++{ ++ ENTER3("%p, %p", nmb1, nmb2); ++ TODO(); ++ EXIT3(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMPromoteMiniport,1) ++ (struct ndis_mp_block *nmb) ++{ ++ ENTER3("%p", nmb); ++ TODO(); ++ EXIT3(return NDIS_STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(NdisMCoActivateVcComplete,3) ++ (NDIS_STATUS status, void *handle, void *params) ++{ ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(NdisMCoDeactivateVcComplete,2) ++ (NDIS_STATUS status, void *handle) ++{ ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(NdisMRemoveMiniport,1) ++ (void *handle) ++{ ++ TODO(); ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMOpenLog,3) ++ (struct ndis_device *wnd, UINT size, void *handle) ++{ ++ if (size >= sizeof(int)) ++ *((int *)handle) = 42; ++ return NDIS_STATUS_SUCCESS; ++} ++ ++wstdcall NDIS_STATUS WIN_FUNC(NdisMWriteLogData,3) ++ (void *handle, char *buffer, UINT buffer_size) ++{ ++ TODO(); ++ return NDIS_STATUS_SUCCESS; ++} ++ ++wstdcall void WIN_FUNC(NdisMFlushLog,1) ++ (void *handle) ++{ ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(NdisMCloseLog,1) ++ (void *handle) ++{ ++ TODO(); ++} ++ ++static void *ndis_get_routine_address(char *name) ++{ ++ int i; ++ ENTER2("%p", name); ++ for (i = 0; i < ARRAY_SIZE(ndis_exports); i++) { ++ if (strcmp(name, ndis_exports[i].name) == 0) { ++ TRACE2("%p", ndis_exports[i].func); ++ return ndis_exports[i].func; ++ } ++ } ++ EXIT2(return NULL); ++} ++ ++/* ndis_init_device is called for each device */ ++int ndis_init_device(struct ndis_device *wnd) ++{ ++ struct ndis_mp_block *nmb = wnd->nmb; ++ ++ KeInitializeSpinLock(&nmb->lock); ++ wnd->mp_interrupt = NULL; ++ wnd->wrap_timer_slist.next = NULL; ++ if (wnd->wd->driver->ndis_driver) ++ wnd->wd->driver->ndis_driver->mp.shutdown = NULL; ++ ++ nmb->filterdbs.eth_db = nmb; ++ nmb->filterdbs.tr_db = nmb; ++ nmb->filterdbs.fddi_db = nmb; ++ nmb->filterdbs.arc_db = nmb; ++ ++ nmb->rx_packet = WIN_FUNC_PTR(NdisMIndicateReceivePacket,3); ++ nmb->send_complete = WIN_FUNC_PTR(NdisMSendComplete,3); ++ nmb->send_resource_avail = WIN_FUNC_PTR(NdisMSendResourcesAvailable,1); ++ nmb->status = WIN_FUNC_PTR(NdisMIndicateStatus,4); ++ nmb->status_complete = WIN_FUNC_PTR(NdisMIndicateStatusComplete,1); ++ nmb->queryinfo_complete = WIN_FUNC_PTR(NdisMQueryInformationComplete,2); ++ nmb->setinfo_complete = WIN_FUNC_PTR(NdisMSetInformationComplete,2); ++ nmb->reset_complete = WIN_FUNC_PTR(NdisMResetComplete,3); ++ nmb->eth_rx_indicate = WIN_FUNC_PTR(EthRxIndicateHandler,8); ++ nmb->eth_rx_complete = WIN_FUNC_PTR(EthRxComplete,1); ++ nmb->td_complete = WIN_FUNC_PTR(NdisMTransferDataComplete,4); ++ return 0; ++} ++ ++/* ndis_exit_device is called for each device */ ++void ndis_exit_device(struct ndis_device *wnd) ++{ ++ struct wrap_device_setting *setting; ++ ENTER2("%p", wnd); ++ mutex_lock(&loader_mutex); ++ nt_list_for_each_entry(setting, &wnd->wd->settings, list) { ++ struct ndis_configuration_parameter *param; ++ param = setting->encoded; ++ if (param) { ++ if (param->type == NdisParameterString) ++ RtlFreeUnicodeString(¶m->data.string); ++ ExFreePool(param); ++ setting->encoded = NULL; ++ } ++ } ++ mutex_unlock(&loader_mutex); ++} ++ ++/* ndis_init is called once when module is loaded */ ++int ndis_init(void) ++{ ++ InitializeListHead(&ndis_work_list); ++ spin_lock_init(&ndis_work_list_lock); ++ INIT_WORK(&ndis_work, ndis_worker); ++ ++ ndis_wq = create_singlethread_workqueue("ndis_wq"); ++ if (!ndis_wq) { ++ WARNING("couldn't create worker thread"); ++ EXIT1(return -ENOMEM); ++ } ++ ++ TRACE1("ndis_wq: %p", ndis_wq); ++ return 0; ++} ++ ++/* ndis_exit is called once when module is removed */ ++void ndis_exit(void) ++{ ++ ENTER1(""); ++ if (ndis_wq) ++ destroy_workqueue(ndis_wq); ++ EXIT1(return); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.h +--- linux-3.12.2/3rdparty/ndiswrapper/ndis.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,1309 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _NDIS_H_ ++#define _NDIS_H_ ++ ++#include "ntoskernel.h" ++ ++//#define ALLOW_POOL_OVERFLOW 1 ++ ++#define NDIS_DMA_24BITS 0 ++#define NDIS_DMA_32BITS 1 ++#define NDIS_DMA_64BITS 2 ++ ++#ifdef CONFIG_X86_64 ++#define MAXIMUM_PROCESSORS 64 ++#else ++#define MAXIMUM_PROCESSORS 32 ++#endif ++ ++typedef UINT NDIS_STATUS; ++typedef UCHAR NDIS_DMA_SIZE; ++typedef LONG ndis_rssi; ++typedef ULONG ndis_key_index; ++typedef ULONG ndis_tx_power_level; ++typedef ULONGULONG ndis_key_rsc; ++typedef UCHAR mac_address[ETH_ALEN]; ++typedef ULONG ndis_fragmentation_threshold; ++typedef ULONG ndis_rts_threshold; ++typedef ULONG ndis_antenna; ++typedef ULONG ndis_oid; ++ ++typedef uint64_t NDIS_PHY_ADDRESS; ++ ++struct ndis_sg_element { ++ PHYSICAL_ADDRESS address; ++ ULONG length; ++ ULONG_PTR reserved; ++}; ++ ++struct ndis_sg_list { ++ ULONG nent; ++ ULONG_PTR reserved; ++ struct ndis_sg_element elements[]; ++}; ++ ++/* when sending packets, ndiswrapper associates exactly one sg element ++ * in sg list */ ++struct wrap_tx_sg_list { ++ ULONG nent; ++ ULONG_PTR reserved; ++ struct ndis_sg_element elements[1]; ++}; ++ ++struct ndis_phy_addr_unit { ++ NDIS_PHY_ADDRESS phy_addr; ++ UINT length; ++}; ++ ++typedef struct mdl ndis_buffer; ++ ++struct ndis_buffer_pool { ++ ndis_buffer *free_descr; ++// NT_SPIN_LOCK lock; ++ spinlock_t lock; ++ UINT max_descr; ++ UINT num_allocated_descr; ++}; ++ ++#define NDIS_PROTOCOL_ID_DEFAULT 0x00 ++#define NDIS_PROTOCOL_ID_TCP_IP 0x02 ++#define NDIS_PROTOCOL_ID_IPX 0x06 ++#define NDIS_PROTOCOL_ID_NBF 0x07 ++#define NDIS_PROTOCOL_ID_MAX 0x0F ++#define NDIS_PROTOCOL_ID_MASK 0x0F ++ ++#define fPACKET_WRAPPER_RESERVED 0x3F ++#define fPACKET_CONTAINS_MEDIA_SPECIFIC_INFO 0x40 ++#define fPACKET_ALLOCATED_BY_NDIS 0x80 ++ ++#define PROTOCOL_RESERVED_SIZE_IN_PACKET (4 * sizeof(void *)) ++ ++struct transport_header_offset { ++ USHORT protocol_type; ++ USHORT header_offset; ++}; ++ ++struct ndis_network_address { ++ USHORT length; ++ USHORT type; ++ UCHAR address[1]; ++}; ++ ++struct ndis_network_address_list { ++ LONG count; ++ USHORT type; ++ struct ndis_network_address address[1]; ++}; ++ ++struct ndis_tcp_ip_checksum_packet_info { ++ union { ++ struct { ++ ULONG v4:1; ++ ULONG v6:1; ++ ULONG tcp:1; ++ ULONG udp:1; ++ ULONG ip:1; ++ } tx; ++ struct { ++ ULONG tcp_failed:1; ++ ULONG udp_failed:1; ++ ULONG ip_failed:1; ++ ULONG tcp_succeeded:1; ++ ULONG udp_succeeded:1; ++ ULONG ip_succeeded:1; ++ ULONG loopback:1; ++ } rx; ++ ULONG value; ++ }; ++}; ++ ++enum ndis_task { ++ TcpIpChecksumNdisTask, IpSecNdisTask, TcpLargeSendNdisTask, MaxNdisTask ++}; ++ ++enum ndis_encapsulation { ++ UNSPECIFIED_Encapsulation, NULL_Encapsulation, ++ IEEE_802_3_Encapsulation, IEEE_802_5_Encapsulation, ++ LLC_SNAP_ROUTED_Encapsulation, LLC_SNAP_BRIDGED_Encapsulation ++}; ++ ++#define NDIS_TASK_OFFLOAD_VERSION 1 ++ ++struct ndis_encapsulation_format { ++ enum ndis_encapsulation encap; ++ struct { ++ ULONG fixed_header_size:1; ++ ULONG reserved:31; ++ } flags; ++ ULONG header_size; ++}; ++ ++struct ndis_task_offload_header { ++ ULONG version; ++ ULONG size; ++ ULONG reserved; ++ ULONG offset_first_task; ++ struct ndis_encapsulation_format encap_format; ++}; ++ ++struct ndis_task_offload { ++ ULONG version; ++ ULONG size; ++ enum ndis_task task; ++ ULONG offset_next_task; ++ ULONG task_buf_length; ++ UCHAR task_buf[1]; ++}; ++ ++struct v4_checksum { ++ union { ++ struct { ++ ULONG ip_opts:1; ++ ULONG tcp_opts:1; ++ ULONG tcp_csum:1; ++ ULONG udp_csum:1; ++ ULONG ip_csum:1; ++ }; ++ ULONG value; ++ }; ++}; ++ ++struct v6_checksum { ++ ULONG ip_supported:1; ++ ULONG tcp_supported:1; ++ ULONG tcp_csum:1; ++ ULONG udp_csum:1; ++}; ++ ++struct ndis_task_tcp_ip_checksum { ++ struct v4_checksum v4_tx; ++ struct v4_checksum v4_rx; ++ struct v6_checksum v6_tx; ++ struct v6_checksum v6_rx; ++}; ++ ++struct ndis_task_tcp_large_send { ++ ULONG version; ++ ULONG max_size; ++ ULONG min_seg_count; ++ BOOLEAN tcp_opts; ++ BOOLEAN ip_opts; ++}; ++ ++struct ndis_packet; ++ ++struct ndis_packet_pool { ++ struct ndis_packet *free_descr; ++// NT_SPIN_LOCK lock; ++ spinlock_t lock; ++ UINT max_descr; ++ UINT num_allocated_descr; ++ UINT num_used_descr; ++ UINT proto_rsvd_length; ++}; ++ ++struct ndis_packet_stack { ++ ULONG_PTR IM_reserved[2]; ++ ULONG_PTR ndis_reserved[4]; ++}; ++ ++enum ndis_per_packet_info { ++ TcpIpChecksumPacketInfo, IpSecPacketInfo, TcpLargeSendPacketInfo, ++ ClassificationHandlePacketInfo, NdisReserved, ++ ScatterGatherListPacketInfo, Ieee8021QInfo, OriginalPacketInfo, ++ PacketCancelId, MaxPerPacketInfo ++}; ++ ++struct ndis_packet_extension { ++ void *info[MaxPerPacketInfo]; ++}; ++ ++struct ndis_packet_private { ++ UINT nr_pages; ++ UINT len; ++ ndis_buffer *buffer_head; ++ ndis_buffer *buffer_tail; ++ struct ndis_packet_pool *pool; ++ UINT count; ++ ULONG flags; ++ BOOLEAN valid_counts; ++ UCHAR packet_flags; ++ USHORT oob_offset; ++}; ++ ++struct ndis_packet { ++ struct ndis_packet_private private; ++ /* for use by miniport */ ++ union { ++ /* for connectionless mininports */ ++ struct { ++ UCHAR miniport_reserved[2 * sizeof(void *)]; ++ UCHAR wrapper_reserved[2 * sizeof(void *)]; ++ } cl_reserved; ++ /* for deserialized miniports */ ++ struct { ++ UCHAR miniport_reserved_ex[3 * sizeof(void *)]; ++ UCHAR wrapper_reserved_ex[sizeof(void *)]; ++ } deserialized_reserved; ++ struct { ++ UCHAR mac_reserved[4 * sizeof(void *)]; ++ } mac_reserved; ++ }; ++ ULONG_PTR reserved[2]; ++ UCHAR protocol_reserved[1]; ++}; ++ ++/* OOB data */ ++struct ndis_packet_oob_data { ++ union { ++ ULONGLONG time_to_tx; ++ ULONGLONG time_txed; ++ }; ++ ULONGLONG time_rxed; ++ UINT header_size; ++ UINT media_size; ++ void *media; ++ NDIS_STATUS status; ++ ++ /* ndiswrapper specific info; extension should be right after ++ * ndis's oob_data */ ++ struct ndis_packet_extension ext; ++ union { ++ /* used for tx only */ ++ struct { ++ struct sk_buff *tx_skb; ++ union { ++ struct wrap_tx_sg_list wrap_tx_sg_list; ++ struct ndis_sg_list *tx_sg_list; ++ }; ++ }; ++ /* used for rx only */ ++ struct { ++ unsigned char header[ETH_HLEN]; ++ unsigned char *look_ahead; ++ UINT look_ahead_size; ++ }; ++ }; ++}; ++ ++#define NDIS_PACKET_OOB_DATA(packet) \ ++ (struct ndis_packet_oob_data *)(((void *)(packet)) + \ ++ (packet)->private.oob_offset) ++ ++enum ndis_device_pnp_event { ++ NdisDevicePnPEventQueryRemoved, NdisDevicePnPEventRemoved, ++ NdisDevicePnPEventSurpriseRemoved, NdisDevicePnPEventQueryStopped, ++ NdisDevicePnPEventStopped, NdisDevicePnPEventPowerProfileChanged, ++ NdisDevicePnPEventMaximum ++}; ++ ++enum ndis_request_type { ++ NdisRequestQueryInformation, NdisRequestSetInformation, ++ NdisRequestQueryStatistics, NdisRequestOpen, NdisRequestClose, ++ NdisRequestSend, NdisRequestTransferData, NdisRequestReset, ++ NdisRequestGeneric1, NdisRequestGeneric2, NdisRequestGeneric3, ++ NdisRequestGeneric4 ++}; ++ ++struct ndis_request { ++ mac_address mac; ++ enum ndis_request_type request_type; ++ union data { ++ struct query_info { ++ UINT oid; ++ void *buf; ++ UINT buf_len; ++ UINT written; ++ UINT needed; ++ } query_info; ++ struct set_info { ++ UINT oid; ++ void *buf; ++ UINT buf_len; ++ UINT written; ++ UINT needed; ++ } set_info; ++ } data; ++}; ++ ++enum ndis_medium { ++ NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumWan, ++ NdisMediumLocalTalk, NdisMediumDix, NdisMediumArcnetRaw, ++ NdisMediumArcnet878_2, NdisMediumAtm, NdisMediumWirelessWan, ++ NdisMediumIrda, NdisMediumBpc, NdisMediumCoWan, ++ NdisMedium1394, NdisMediumMax ++}; ++ ++enum ndis_physical_medium { ++ NdisPhysicalMediumUnspecified, NdisPhysicalMediumWirelessLan, ++ NdisPhysicalMediumCableModem, NdisPhysicalMediumPhoneLine, ++ NdisPhysicalMediumPowerLine, NdisPhysicalMediumDSL, ++ NdisPhysicalMediumFibreChannel, NdisPhysicalMedium1394, ++ NdisPhysicalMediumWirelessWan, NdisPhysicalMediumMax ++}; ++ ++enum ndis_power_state { ++ NdisDeviceStateUnspecified = 0, ++ NdisDeviceStateD0, NdisDeviceStateD1, NdisDeviceStateD2, ++ NdisDeviceStateD3, NdisDeviceStateMaximum ++}; ++ ++enum ndis_power_profile { ++ NdisPowerProfileBattery, NdisPowerProfileAcOnLine ++}; ++ ++struct ndis_pm_wakeup_capabilities { ++ enum ndis_power_state min_magic_packet_wakeup; ++ enum ndis_power_state min_pattern_wakeup; ++ enum ndis_power_state min_link_change_wakeup; ++}; ++ ++#define NDIS_PNP_WAKE_UP_MAGIC_PACKET 0x00000001 ++#define NDIS_PNP_WAKE_UP_PATTERN_MATCH 0x00000002 ++#define NDIS_PNP_WAKE_UP_LINK_CHANGE 0x00000004 ++ ++enum net_pnp_event_code { ++ NetEventSetPower, NetEventQueryPower, NetEventQueryRemoveDevice, ++ NetEventCancelRemoveDevice, NetEventReconfigure, NetEventBindList, ++ NetEventBindsComplete, NetEventPnPCapabilities, NetEventMaximum ++}; ++ ++struct net_pnp_event { ++ enum net_pnp_event_code code; ++ void *buf; ++ ULONG buf_length; ++ ULONG_PTR ndis_reserved[4]; ++ ULONG_PTR transport_reserved[4]; ++ ULONG_PTR tdi_reserved[4]; ++ ULONG_PTR tdi_client_reserved[4]; ++}; ++ ++struct ndis_pnp_capabilities { ++ ULONG flags; ++ struct ndis_pm_wakeup_capabilities wakeup; ++}; ++ ++typedef void (*ndis_isr_handler)(BOOLEAN *recognized, BOOLEAN *queue_handler, ++ void *handle) wstdcall; ++typedef void (*ndis_interrupt_handler)(void *ctx) wstdcall; ++ ++struct miniport { ++ /* NDIS 3.0 */ ++ UCHAR major_version; ++ UCHAR minor_version; ++ USHORT filler; ++ UINT reserved; ++ BOOLEAN (*hangcheck)(void *ctx) wstdcall; ++ void (*disable_interrupt)(void *ctx) wstdcall; ++ void (*enable_interrupt)(void *ctx) wstdcall; ++ void (*mp_halt)(void *ctx) wstdcall; ++ ndis_interrupt_handler handle_interrupt; ++ NDIS_STATUS (*init)(NDIS_STATUS *error_status, UINT *medium_index, ++ enum ndis_medium medium[], UINT medium_array_size, ++ void *handle, void *conf_handle) wstdcall; ++ ndis_isr_handler isr; ++ NDIS_STATUS (*queryinfo)(void *ctx, ndis_oid oid, void *buffer, ++ ULONG buflen, ULONG *written, ++ ULONG *needed) wstdcall; ++ void *reconfig; ++ NDIS_STATUS (*reset)(BOOLEAN *reset_address, void *ctx) wstdcall; ++ NDIS_STATUS (*send)(void *ctx, struct ndis_packet *packet, ++ UINT flags) wstdcall; ++ NDIS_STATUS (*setinfo)(void *ctx, ndis_oid oid, void *buffer, ++ ULONG buflen, ULONG *written, ++ ULONG *needed) wstdcall; ++ NDIS_STATUS (*tx_data)(struct ndis_packet *ndis_packet, ++ UINT *bytes_txed, void *mp_ctx, void *rx_ctx, ++ UINT offset, UINT bytes_to_tx) wstdcall; ++ /* NDIS 4.0 extensions */ ++ void (*return_packet)(void *ctx, void *packet) wstdcall; ++ void (*send_packets)(void *ctx, struct ndis_packet **packets, ++ INT nr_of_packets) wstdcall; ++ void (*alloc_complete)(void *handle, void *virt, ++ NDIS_PHY_ADDRESS *phys, ++ ULONG size, void *ctx) wstdcall; ++ /* NDIS 5.0 extensions */ ++ NDIS_STATUS (*co_create_vc)(void *ctx, void *vc_handle, ++ void *vc_ctx) wstdcall; ++ NDIS_STATUS (*co_delete_vc)(void *vc_ctx) wstdcall; ++ NDIS_STATUS (*co_activate_vc)(void *vc_ctx, void *call_params) wstdcall; ++ NDIS_STATUS (*co_deactivate_vc)(void *vc_ctx) wstdcall; ++ NDIS_STATUS (*co_send_packets)(void *vc_ctx, void **packets, ++ UINT nr_of_packets) wstdcall; ++ NDIS_STATUS (*co_request)(void *ctx, void *vc_ctx, UINT *req) wstdcall; ++ /* NDIS 5.1 extensions */ ++ void (*cancel_send_packets)(void *ctx, void *id) wstdcall; ++ void (*pnp_event_notify)(void *ctx, enum ndis_device_pnp_event event, ++ void *inf_buf, ULONG inf_buf_len) wstdcall; ++ void (*shutdown)(void *ctx) wstdcall; ++ void *reserved1; ++ void *reserved2; ++ void *reserved3; ++ void *reserved4; ++}; ++ ++struct ndis_spinlock { ++ NT_SPIN_LOCK klock; ++ KIRQL irql; ++}; ++ ++union ndis_rw_lock_refcount { ++ UCHAR cache_line[16]; ++}; ++ ++struct ndis_rw_lock { ++ union { ++ struct { ++ NT_SPIN_LOCK klock; ++ void *context; ++ }; ++ UCHAR reserved[16]; ++ }; ++ union { ++ union ndis_rw_lock_refcount ref_count[MAXIMUM_PROCESSORS]; ++ /* ndiswrapper specific */ ++ volatile int count; ++ }; ++}; ++ ++struct lock_state { ++ USHORT state; ++ KIRQL irql; ++}; ++ ++struct ndis_work_item; ++typedef void (*NDIS_PROC)(struct ndis_work_item *, void *) wstdcall; ++ ++struct ndis_work_item { ++ void *ctx; ++ NDIS_PROC func; ++ union { ++ UCHAR reserved[8 * sizeof(void *)]; ++ /* ndiswrapper specific */ ++ struct nt_list list; ++ }; ++}; ++ ++struct alloc_shared_mem { ++ void *ctx; ++ ULONG size; ++ BOOLEAN cached; ++}; ++ ++struct ndis_mp_block; ++ ++/* this is opaque to drivers, so we can use it as we please */ ++struct ndis_mp_interrupt { ++ struct kinterrupt *kinterrupt; ++ NT_SPIN_LOCK lock; ++ union { ++ void *reserved; ++ unsigned int irq; ++ }; ++ ndis_isr_handler isr; ++ ndis_interrupt_handler mp_dpc; ++ struct kdpc intr_dpc; ++ struct ndis_mp_block *nmb; ++ UCHAR dpc_count; ++ BOOLEAN enable; ++ struct nt_event dpc_completed_event; ++ BOOLEAN shared; ++ BOOLEAN req_isr; ++}; ++ ++struct ndis_binary_data { ++ USHORT len; ++ void *buf; ++}; ++ ++enum ndis_parameter_type { ++ NdisParameterInteger, NdisParameterHexInteger, ++ NdisParameterString, NdisParameterMultiString, NdisParameterBinary, ++}; ++ ++typedef struct unicode_string NDIS_STRING; ++ ++struct ndis_configuration_parameter { ++ enum ndis_parameter_type type; ++ union { ++ ULONG integer; ++ NDIS_STRING string; ++ } data; ++}; ++ ++struct ndis_driver { ++ struct miniport mp; ++}; ++ ++/* IDs used to store extensions in driver_object's custom extension */ ++#define NDIS_DRIVER_CLIENT_ID 10 ++ ++struct ndis_wireless_stats { ++ ULONG length; ++ LARGE_INTEGER tx_frag; ++ LARGE_INTEGER tx_multi_frag; ++ LARGE_INTEGER failed; ++ LARGE_INTEGER retry; ++ LARGE_INTEGER multi_retry; ++ LARGE_INTEGER rtss_succ; ++ LARGE_INTEGER rtss_fail; ++ LARGE_INTEGER ack_fail; ++ LARGE_INTEGER frame_dup; ++ LARGE_INTEGER rx_frag; ++ LARGE_INTEGER rx_multi_frag; ++ LARGE_INTEGER fcs_err; ++ LARGE_INTEGER tkip_local_mic_failures; ++ LARGE_INTEGER tkip_icv_errors; ++ LARGE_INTEGER tkip_counter_measures_invoked; ++ LARGE_INTEGER tkip_replays; ++ LARGE_INTEGER ccmp_format_errors; ++ LARGE_INTEGER ccmp_replays; ++ LARGE_INTEGER ccmp_decrypt_errors; ++ LARGE_INTEGER fourway_handshake_failures; ++ LARGE_INTEGER wep_undecryptable_count; ++ LARGE_INTEGER wep_icv_errorcount; ++ LARGE_INTEGER decrypt_success_count; ++ LARGE_INTEGER decrypt_failure_count; ++}; ++ ++enum ndis_status_type { ++ Ndis802_11StatusType_Authentication, ++ Ndis802_11StatusType_MediaStreamMode, ++ Ndis802_11StatusType_PMKID_CandidateList, ++ Ndis802_11StatusType_RadioState, ++}; ++ ++struct ndis_status_indication { ++ enum ndis_status_type status_type; ++}; ++ ++enum ndis_radio_status { ++ Ndis802_11RadioStatusOn, Ndis802_11RadioStatusHardwareOff, ++ Ndis802_11RadioStatusSoftwareOff, ++}; ++ ++struct ndis_radio_status_indication ++{ ++ enum ndis_status_type status_type; ++ enum ndis_radio_status radio_state; ++}; ++ ++enum ndis_media_state { ++ NdisMediaStateConnected, ++ NdisMediaStateDisconnected, ++}; ++ ++enum ndis_media_stream_mode { ++ Ndis802_11MediaStreamOff, Ndis802_11MediaStreamOn ++}; ++ ++enum wrapper_work { ++ LINK_STATUS_OFF, LINK_STATUS_ON, SET_MULTICAST_LIST, COLLECT_IW_STATS, ++ HANGCHECK, NETIF_WAKEQ, ++}; ++ ++struct encr_info { ++ struct encr_key { ++ ULONG length; ++ UCHAR key[NDIS_ENCODING_TOKEN_MAX]; ++ } keys[MAX_ENCR_KEYS]; ++ unsigned short tx_key_index; ++}; ++ ++struct ndis_essid { ++ ULONG length; ++ UCHAR essid[NDIS_ESSID_MAX_SIZE]; ++}; ++ ++enum ndis_infrastructure_mode { ++ Ndis802_11IBSS, Ndis802_11Infrastructure, Ndis802_11AutoUnknown, ++ Ndis802_11InfrastructureMax ++}; ++ ++enum authentication_mode { ++ Ndis802_11AuthModeOpen, Ndis802_11AuthModeShared, ++ Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA, ++ Ndis802_11AuthModeWPAPSK, Ndis802_11AuthModeWPANone, ++ Ndis802_11AuthModeWPA2, Ndis802_11AuthModeWPA2PSK, ++ Ndis802_11AuthModeMax ++}; ++ ++enum encryption_status { ++ Ndis802_11WEPEnabled, ++ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, ++ Ndis802_11WEPDisabled, ++ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, ++ Ndis802_11WEPKeyAbsent, ++ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, ++ Ndis802_11WEPNotSupported, ++ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, ++ Ndis802_11Encryption2Enabled, Ndis802_11Encryption2KeyAbsent, ++ Ndis802_11Encryption3Enabled, Ndis802_11Encryption3KeyAbsent ++}; ++ ++struct ndis_auth_encr_pair { ++ enum authentication_mode auth_mode; ++ enum encryption_status encr_mode; ++}; ++ ++struct ndis_capability { ++ ULONG length; ++ ULONG version; ++ ULONG num_PMKIDs; ++ ULONG num_auth_encr_pair; ++ struct ndis_auth_encr_pair auth_encr_pair[1]; ++}; ++ ++struct ndis_guid { ++ struct guid guid; ++ union { ++ ndis_oid oid; ++ NDIS_STATUS status; ++ }; ++ ULONG size; ++ ULONG flags; ++}; ++ ++struct ndis_timer { ++ struct nt_timer nt_timer; ++ struct kdpc kdpc; ++}; ++ ++struct ndis_mp_timer { ++ struct nt_timer nt_timer; ++ struct kdpc kdpc; ++ DPC func; ++ void *ctx; ++ struct ndis_mp_block *nmb; ++ struct ndis_mp_timer *next; ++}; ++ ++typedef struct cm_partial_resource_list NDIS_RESOURCE_LIST; ++ ++struct ndis_event { ++ struct nt_event nt_event; ++}; ++ ++struct ndis_bind_paths { ++ UINT number; ++ struct unicode_string paths[1]; ++}; ++ ++struct ndis_reference { ++ NT_SPIN_LOCK lock; ++ USHORT ref_count; ++ BOOLEAN closing; ++}; ++ ++struct ndis_filterdbs { ++ union { ++ void *eth_db; ++ void *null_db; ++ }; ++ void *tr_db; ++ void *fddi_db; ++ void *arc_db; ++}; ++ ++enum ndis_interface_type { ++ NdisInterfaceInternal, NdisInterfaceIsa, NdisInterfaceEisa, ++ NdisInterfaceMca, NdisInterfaceTurboChannel, NdisInterfacePci, ++ NdisInterfacePcMcia, ++}; ++ ++struct auth_encr_capa { ++ unsigned long auth; ++ unsigned long encr; ++}; ++ ++struct ndis_pmkid_candidate { ++ mac_address bssid; ++ DWORD flags; ++}; ++ ++struct ndis_pmkid_candidate_list { ++ ULONG version; ++ ULONG num_candidates; ++ struct ndis_pmkid_candidate candidates[1]; ++}; ++ ++/* ++ * This struct contains function pointers that the drivers references ++ * directly via macros, so it's important that they are at the correct ++ * position. ++ */ ++struct ndis_mp_block { ++ void *signature; ++ struct ndis_mp_block *next; ++ struct driver_object *drv_obj; ++ void *mp_ctx; ++ struct unicode_string name; ++ struct ndis_bind_paths *bindpaths; ++ void *openqueue; ++ struct ndis_reference reference; ++ void *device_ctx; ++ UCHAR padding; ++ UCHAR lock_acquired; ++ UCHAR pmode_opens; ++ UCHAR assigned_cpu; ++ NT_SPIN_LOCK lock; ++ enum ndis_request_type *mediarequest; ++ struct ndis_mp_interrupt *interrupt; ++ ULONG flags; ++ ULONG pnp_flags; ++ struct nt_list packet_list; ++ struct ndis_packet *first_pending_tx_packet; ++ struct ndis_packet *return_packet_queue; ++ ULONG request_buffer; ++ void *set_mcast_buffer; ++ struct ndis_mp_block *primary_mp; ++ void *wrapper_ctx; ++ void *bus_data_ctx; ++ ULONG pnp_capa; ++ void *resources; ++ struct ndis_timer wakeup_dpc_timer; ++ struct unicode_string basename; ++ struct unicode_string symlink_name; ++ ULONG ndis_hangcheck_interval; ++ USHORT hangcheck_ticks; ++ USHORT hangcheck_tick; ++ NDIS_STATUS ndis_reset_status; ++ void *resetopen; ++ struct ndis_filterdbs filterdbs; ++ void *rx_packet; ++ void *send_complete; ++ void *send_resource_avail; ++ void *reset_complete; ++ ++ enum ndis_medium media_type; ++ ULONG bus_number; ++ enum ndis_interface_type bus_type; ++ enum ndis_interface_type adapter_type; ++ struct device_object *fdo; ++ struct device_object *pdo; ++ struct device_object *next_device; ++ void *mapreg; ++ void *call_mgraflist; ++ void *mp_thread; ++ void *setinfobuf; ++ USHORT setinfo_buf_len; ++ USHORT max_send_pkts; ++ NDIS_STATUS fake_status; ++ void *lock_handler; ++ struct unicode_string *adapter_instance_name; ++ void *timer_queue; ++ UINT mac_options; ++ void *pending_req; ++ UINT max_long_addrs; ++ UINT max_short_addrs; ++ UINT cur_lookahead; ++ UINT max_lookahead; ++ ++ ndis_interrupt_handler irq_bh; ++ void *disable_intr; ++ void *enable_intr; ++ void *send_pkts; ++ void *deferred_send; ++ void *eth_rx_indicate; ++ void *tr_rx_indicate; ++ void *fddi_rx_indicate; ++ void *eth_rx_complete; ++ void *tr_rx_complete; ++ void *fddi_rx_complete; ++ ++ void *status; ++ void *status_complete; ++ void *td_complete; ++ ++ void *queryinfo_complete; ++ void *setinfo_complete; ++ void *wan_tx_complete; ++ void *wan_rx; ++ void *wan_rx_complete; ++ /* ndiswrapper specific */ ++ struct ndis_device *wnd; ++}; ++ ++struct ndis_device { ++ struct ndis_mp_block *nmb; ++ struct wrap_device *wd; ++ struct net_device *net_dev; ++ void *shutdown_ctx; ++ struct ndis_mp_interrupt *mp_interrupt; ++ struct kdpc irq_kdpc; ++ unsigned long mem_start; ++ unsigned long mem_end; ++ ++ struct net_device_stats net_stats; ++ struct iw_statistics iw_stats; ++ BOOLEAN iw_stats_enabled; ++ struct ndis_wireless_stats ndis_stats; ++ ++ struct work_struct tx_work; ++ struct ndis_packet *tx_ring[TX_RING_SIZE]; ++ u8 tx_ring_start; ++ u8 tx_ring_end; ++ u8 is_tx_ring_full; ++ u8 tx_ok; ++ spinlock_t tx_ring_lock; ++ struct mutex tx_ring_mutex; ++ unsigned int max_tx_packets; ++ struct mutex ndis_req_mutex; ++ struct task_struct *ndis_req_task; ++ int ndis_req_done; ++ NDIS_STATUS ndis_req_status; ++ ULONG packet_filter; ++ ++ ULONG sg_dma_size; ++ ULONG dma_map_count; ++ dma_addr_t *dma_map_addr; ++ ++ int hangcheck_interval; ++ struct timer_list hangcheck_timer; ++ int iw_stats_interval; ++ struct timer_list iw_stats_timer; ++ unsigned long scan_timestamp; ++ struct encr_info encr_info; ++ char nick[IW_ESSID_MAX_SIZE + 1]; ++ struct ndis_essid essid; ++ struct auth_encr_capa capa; ++ enum ndis_infrastructure_mode infrastructure_mode; ++ int max_pmkids; ++ int num_pmkids; ++ struct ndis_pmkid *pmkids; ++ mac_address mac; ++ struct proc_dir_entry *procfs_iface; ++ ++ struct work_struct ndis_work; ++ unsigned long ndis_pending_work; ++ UINT attributes; ++ int iw_auth_wpa_version; ++ int iw_auth_cipher_pairwise; ++ int iw_auth_cipher_group; ++ int iw_auth_key_mgmt; ++ int iw_auth_80211_alg; ++ struct ndis_packet_pool *tx_packet_pool; ++ struct ndis_buffer_pool *tx_buffer_pool; ++ int multicast_size; ++ struct v4_checksum rx_csum; ++ struct v4_checksum tx_csum; ++ enum ndis_physical_medium physical_medium; ++ ULONG ndis_wolopts; ++ struct nt_slist wrap_timer_slist; ++ int drv_ndis_version; ++ struct ndis_pnp_capabilities pnp_capa; ++}; ++ ++BOOLEAN ndis_isr(struct kinterrupt *kinterrupt, void *ctx) wstdcall; ++ ++int ndis_init(void); ++void ndis_exit(void); ++int ndis_init_device(struct ndis_device *wnd); ++void ndis_exit_device(struct ndis_device *wnd); ++ ++int wrap_procfs_add_ndis_device(struct ndis_device *wnd); ++void wrap_procfs_remove_ndis_device(struct ndis_device *wnd); ++ ++void NdisAllocatePacketPoolEx(NDIS_STATUS *status, ++ struct ndis_packet_pool **pool_handle, ++ UINT num_descr, UINT overflowsize, ++ UINT proto_rsvd_length) wstdcall; ++void NdisFreePacketPool(struct ndis_packet_pool *pool) wstdcall; ++void NdisAllocatePacket(NDIS_STATUS *status, struct ndis_packet **packet, ++ struct ndis_packet_pool *pool) wstdcall; ++void NdisFreePacket(struct ndis_packet *descr) wstdcall; ++void NdisAllocateBufferPool(NDIS_STATUS *status, ++ struct ndis_buffer_pool **pool_handle, ++ UINT num_descr) wstdcall; ++void NdisFreeBufferPool(struct ndis_buffer_pool *pool) wstdcall; ++void NdisAllocateBuffer(NDIS_STATUS *status, ndis_buffer **buffer, ++ struct ndis_buffer_pool *pool, void *virt, ++ UINT length) wstdcall; ++void NdisFreeBuffer(ndis_buffer *descr) wstdcall; ++void NdisMIndicateReceivePacket(struct ndis_mp_block *nmb, ++ struct ndis_packet **packets, ++ UINT nr_packets) wstdcall; ++void NdisMSendComplete(struct ndis_mp_block *nmb, struct ndis_packet *packet, ++ NDIS_STATUS status) wstdcall; ++void NdisMSendResourcesAvailable(struct ndis_mp_block *nmb) wstdcall; ++void NdisMIndicateStatus(struct ndis_mp_block *nmb, ++ NDIS_STATUS status, void *buf, UINT len) wstdcall; ++void NdisMIndicateStatusComplete(struct ndis_mp_block *nmb) wstdcall; ++void NdisMQueryInformationComplete(struct ndis_mp_block *nmb, ++ NDIS_STATUS status) wstdcall; ++void NdisMSetInformationComplete(struct ndis_mp_block *nmb, ++ NDIS_STATUS status) wstdcall; ++void NdisMResetComplete(struct ndis_mp_block *nmb, NDIS_STATUS status, ++ BOOLEAN address_reset) wstdcall; ++ULONG NDIS_BUFFER_TO_SPAN_PAGES(ndis_buffer *buffer) wstdcall; ++BOOLEAN NdisWaitEvent(struct ndis_event *event, UINT timeout) wstdcall; ++void NdisSetEvent(struct ndis_event *event) wstdcall; ++void NdisMDeregisterInterrupt(struct ndis_mp_interrupt *mp_interrupt) wstdcall; ++void EthRxIndicateHandler(struct ndis_mp_block *nmb, void *rx_ctx, ++ char *header1, char *header, UINT header_size, ++ void *look_ahead, UINT look_ahead_size, ++ UINT packet_size) wstdcall; ++void EthRxComplete(struct ndis_mp_block *nmb) wstdcall; ++void NdisMTransferDataComplete(struct ndis_mp_block *nmb, ++ struct ndis_packet *packet, NDIS_STATUS status, ++ UINT bytes_txed) wstdcall; ++void NdisWriteConfiguration(NDIS_STATUS *status, struct ndis_mp_block *nmb, ++ struct unicode_string *key, ++ struct ndis_configuration_parameter *param) wstdcall; ++void NdisReadConfiguration(NDIS_STATUS *status, ++ struct ndis_configuration_parameter **param, ++ struct ndis_mp_block *nmb, ++ struct unicode_string *key, ++ enum ndis_parameter_type type) wstdcall; ++ ++/* Required OIDs */ ++#define OID_GEN_SUPPORTED_LIST 0x00010101 ++#define OID_GEN_HARDWARE_STATUS 0x00010102 ++#define OID_GEN_MEDIA_SUPPORTED 0x00010103 ++#define OID_GEN_MEDIA_IN_USE 0x00010104 ++#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 ++#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 ++#define OID_GEN_LINK_SPEED 0x00010107 ++#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 ++#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 ++#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A ++#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B ++#define OID_GEN_VENDOR_ID 0x0001010C ++#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D ++#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E ++#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F ++#define OID_GEN_DRIVER_VERSION 0x00010110 ++#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 ++#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 ++#define OID_GEN_MAC_OPTIONS 0x00010113 ++#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 ++#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 ++#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 ++#define OID_GEN_SUPPORTED_GUIDS 0x00010117 ++#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 /* Set only */ ++#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 /* Set only */ ++#define OID_GEN_MACHINE_NAME 0x0001021A ++#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B /* Set only */ ++#define OID_GEN_VLAN_ID 0x0001021C ++ ++/* Optional OIDs. */ ++#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 ++#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 ++ ++/* Required statistics OIDs. */ ++#define OID_GEN_XMIT_OK 0x00020101 ++#define OID_GEN_RCV_OK 0x00020102 ++#define OID_GEN_XMIT_ERROR 0x00020103 ++#define OID_GEN_RCV_ERROR 0x00020104 ++#define OID_GEN_RCV_NO_BUFFER 0x00020105 ++ ++/* Optional OID statistics */ ++#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 ++#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 ++#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 ++#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 ++#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 ++#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 ++#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 ++#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 ++#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 ++#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A ++#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B ++#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C ++#define OID_GEN_RCV_CRC_ERROR 0x0002020D ++#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E ++#define OID_GEN_GET_TIME_CAPS 0x0002020F ++#define OID_GEN_GET_NETCARD_TIME 0x00020210 ++#define OID_GEN_NETCARD_LOAD 0x00020211 ++#define OID_GEN_DEVICE_PROFILE 0x00020212 ++ ++/* 802.3 (ethernet) OIDs */ ++#define OID_802_3_PERMANENT_ADDRESS 0x01010101 ++#define OID_802_3_CURRENT_ADDRESS 0x01010102 ++#define OID_802_3_MULTICAST_LIST 0x01010103 ++#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 ++#define OID_802_3_MAC_OPTIONS 0x01010105 ++#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 ++#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 ++#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 ++#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 ++#define OID_802_3_XMIT_DEFERRED 0x01020201 ++#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 ++#define OID_802_3_RCV_OVERRUN 0x01020203 ++#define OID_802_3_XMIT_UNDERRUN 0x01020204 ++#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 ++#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 ++#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 ++ ++/* PnP and power management OIDs */ ++#define OID_PNP_CAPABILITIES 0xFD010100 ++#define OID_PNP_SET_POWER 0xFD010101 ++#define OID_PNP_QUERY_POWER 0xFD010102 ++#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 ++#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 ++#define OID_PNP_WAKE_UP_PATTERN_LIST 0xFD010105 ++#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 ++ ++/* PnP/PM Statistics (Optional). */ ++#define OID_PNP_WAKE_UP_OK 0xFD020200 ++#define OID_PNP_WAKE_UP_ERROR 0xFD020201 ++ ++/* The following bits are defined for OID_PNP_ENABLE_WAKE_UP */ ++#define NDIS_PNP_WAKE_UP_MAGIC_PACKET 0x00000001 ++#define NDIS_PNP_WAKE_UP_PATTERN_MATCH 0x00000002 ++#define NDIS_PNP_WAKE_UP_LINK_CHANGE 0x00000004 ++ ++/* 802.11 OIDs */ ++#define OID_802_11_BSSID 0x0D010101 ++#define OID_802_11_SSID 0x0D010102 ++#define OID_802_11_NETWORK_TYPES_SUPPORTED 0x0D010203 ++#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 ++#define OID_802_11_TX_POWER_LEVEL 0x0D010205 ++#define OID_802_11_RSSI 0x0D010206 ++#define OID_802_11_RSSI_TRIGGER 0x0D010207 ++#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 ++#define OID_802_11_FRAGMENTATION_THRESHOLD 0x0D010209 ++#define OID_802_11_RTS_THRESHOLD 0x0D01020A ++#define OID_802_11_NUMBER_OF_ANTENNAS 0x0D01020B ++#define OID_802_11_RX_ANTENNA_SELECTED 0x0D01020C ++#define OID_802_11_TX_ANTENNA_SELECTED 0x0D01020D ++#define OID_802_11_SUPPORTED_RATES 0x0D01020E ++#define OID_802_11_DESIRED_RATES 0x0D010210 ++#define OID_802_11_CONFIGURATION 0x0D010211 ++#define OID_802_11_STATISTICS 0x0D020212 ++#define OID_802_11_ADD_WEP 0x0D010113 ++#define OID_802_11_REMOVE_WEP 0x0D010114 ++#define OID_802_11_DISASSOCIATE 0x0D010115 ++#define OID_802_11_POWER_MODE 0x0D010216 ++#define OID_802_11_BSSID_LIST 0x0D010217 ++#define OID_802_11_AUTHENTICATION_MODE 0x0D010118 ++#define OID_802_11_PRIVACY_FILTER 0x0D010119 ++#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A ++#define OID_802_11_WEP_STATUS 0x0D01011B ++#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS ++#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C ++#define OID_802_11_ADD_KEY 0x0D01011D ++#define OID_802_11_REMOVE_KEY 0x0D01011E ++#define OID_802_11_ASSOCIATION_INFORMATION 0x0D01011F ++#define OID_802_11_TEST 0x0D010120 ++#define OID_802_11_MEDIA_STREAM_MODE 0x0D010121 ++#define OID_802_11_CAPABILITY 0x0D010122 ++#define OID_802_11_PMKID 0x0D010123 ++ ++#define NDIS_STATUS_SUCCESS 0 ++#define NDIS_STATUS_PENDING 0x00000103 ++#define NDIS_STATUS_NOT_RECOGNIZED 0x00010001 ++#define NDIS_STATUS_NOT_COPIED 0x00010002 ++#define NDIS_STATUS_NOT_ACCEPTED 0x00010003 ++#define NDIS_STATUS_CALL_ACTIVE 0x00010007 ++#define NDIS_STATUS_ONLINE 0x40010003 ++#define NDIS_STATUS_RESET_START 0x40010004 ++#define NDIS_STATUS_RESET_END 0x40010005 ++#define NDIS_STATUS_RING_STATUS 0x40010006 ++#define NDIS_STATUS_CLOSED 0x40010007 ++#define NDIS_STATUS_WAN_LINE_UP 0x40010008 ++#define NDIS_STATUS_WAN_LINE_DOWN 0x40010009 ++#define NDIS_STATUS_WAN_FRAGMENT 0x4001000A ++#define NDIS_STATUS_MEDIA_CONNECT 0x4001000B ++#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C ++#define NDIS_STATUS_HARDWARE_LINE_UP 0x4001000D ++#define NDIS_STATUS_HARDWARE_LINE_DOWN 0x4001000E ++#define NDIS_STATUS_INTERFACE_UP 0x4001000F ++#define NDIS_STATUS_INTERFACE_DOWN 0x40010010 ++#define NDIS_STATUS_MEDIA_BUSY 0x40010011 ++#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 ++#define NDIS_STATUS_WW_INDICATION NDIS_STATUS_MEDIA_SPECIFIC_INDICATION ++#define NDIS_STATUS_LINK_SPEED_CHANGE 0x40010013 ++#define NDIS_STATUS_WAN_GET_STATS 0x40010014 ++#define NDIS_STATUS_WAN_CO_FRAGMENT 0x40010015 ++#define NDIS_STATUS_WAN_CO_LINKPARAMS 0x40010016 ++#define NDIS_STATUS_NOT_RESETTABLE 0x80010001 ++#define NDIS_STATUS_SOFT_ERRORS 0x80010003 ++#define NDIS_STATUS_HARD_ERRORS 0x80010004 ++#define NDIS_STATUS_BUFFER_OVERFLOW 0x80000005 ++#define NDIS_STATUS_FAILURE 0xC0000001 ++#define NDIS_STATUS_INVALID_PARAMETER 0xC000000D ++#define NDIS_STATUS_RESOURCES 0xC000009A ++#define NDIS_STATUS_CLOSING 0xC0010002 ++#define NDIS_STATUS_BAD_VERSION 0xC0010004 ++#define NDIS_STATUS_BAD_CHARACTERISTICS 0xC0010005 ++#define NDIS_STATUS_ADAPTER_NOT_FOUND 0xC0010006 ++#define NDIS_STATUS_OPEN_FAILED 0xC0010007 ++#define NDIS_STATUS_DEVICE_FAILED 0xC0010008 ++#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 ++#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A ++#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B ++#define NDIS_STATUS_REQUEST_ABORTED 0xC001000C ++#define NDIS_STATUS_RESET_IN_PROGRESS 0xC001000D ++#define NDIS_STATUS_CLOSING_INDICATING 0xC001000E ++#define NDIS_STATUS_BAD_VERSION 0xC0010004 ++#define NDIS_STATUS_NOT_SUPPORTED 0xC00000BB ++#define NDIS_STATUS_INVALID_PACKET 0xC001000F ++#define NDIS_STATUS_OPEN_LIST_FULL 0xC0010010 ++#define NDIS_STATUS_ADAPTER_NOT_READY 0xC0010011 ++#define NDIS_STATUS_ADAPTER_NOT_OPEN 0xC0010012 ++#define NDIS_STATUS_NOT_INDICATING 0xC0010013 ++#define NDIS_STATUS_INVALID_LENGTH 0xC0010014 ++#define NDIS_STATUS_INVALID_DATA 0xC0010015 ++#define NDIS_STATUS_BUFFER_TOO_SHORT 0xC0010016 ++#define NDIS_STATUS_INVALID_OID 0xC0010017 ++#define NDIS_STATUS_ADAPTER_REMOVED 0xC0010018 ++#define NDIS_STATUS_UNSUPPORTED_MEDIA 0xC0010019 ++#define NDIS_STATUS_GROUP_ADDRESS_IN_USE 0xC001001A ++#define NDIS_STATUS_FILE_NOT_FOUND 0xC001001B ++#define NDIS_STATUS_ERROR_READING_FILE 0xC001001C ++#define NDIS_STATUS_ALREADY_MAPPED 0xC001001D ++#define NDIS_STATUS_RESOURCE_CONFLICT 0xC001001E ++#define NDIS_STATUS_NO_CABLE 0xC001001F ++#define NDIS_STATUS_INVALID_SAP 0xC0010020 ++#define NDIS_STATUS_SAP_IN_USE 0xC0010021 ++#define NDIS_STATUS_INVALID_ADDRESS 0xC0010022 ++#define NDIS_STATUS_VC_NOT_ACTIVATED 0xC0010023 ++#define NDIS_STATUS_DEST_OUT_OF_ORDER 0xC0010024 ++#define NDIS_STATUS_VC_NOT_AVAILABLE 0xC0010025 ++#define NDIS_STATUS_CELLRATE_NOT_AVAILABLE 0xC0010026 ++#define NDIS_STATUS_INCOMPATABLE_QOS 0xC0010027 ++#define NDIS_STATUS_AAL_PARAMS_UNSUPPORTED 0xC0010028 ++#define NDIS_STATUS_NO_ROUTE_TO_DESTINATION 0xC0010029 ++#define NDIS_STATUS_TOKEN_RING_OPEN_ERROR 0xC0011000 ++#define NDIS_STATUS_INVALID_DEVICE_REQUEST 0xC0000010 ++#define NDIS_STATUS_NETWORK_UNREACHABLE 0xC000023C ++ ++/* Event codes */ ++ ++#define EVENT_NDIS_RESOURCE_CONFLICT 0xC0001388 ++#define EVENT_NDIS_OUT_OF_RESOURCE 0xC0001389 ++#define EVENT_NDIS_HARDWARE_FAILURE 0xC000138A ++#define EVENT_NDIS_ADAPTER_NOT_FOUND 0xC000138B ++#define EVENT_NDIS_INTERRUPT_CONNECT 0xC000138C ++#define EVENT_NDIS_DRIVER_FAILURE 0xC000138D ++#define EVENT_NDIS_BAD_VERSION 0xC000138E ++#define EVENT_NDIS_TIMEOUT 0x8000138F ++#define EVENT_NDIS_NETWORK_ADDRESS 0xC0001390 ++#define EVENT_NDIS_UNSUPPORTED_CONFIGURATION 0xC0001391 ++#define EVENT_NDIS_INVALID_VALUE_FROM_ADAPTER 0xC0001392 ++#define EVENT_NDIS_MISSING_CONFIGURATION_PARAMETER 0xC0001393 ++#define EVENT_NDIS_BAD_IO_BASE_ADDRESS 0xC0001394 ++#define EVENT_NDIS_RECEIVE_SPACE_SMALL 0x40001395 ++#define EVENT_NDIS_ADAPTER_DISABLED 0x80001396 ++#define EVENT_NDIS_IO_PORT_CONFLICT 0x80001397 ++#define EVENT_NDIS_PORT_OR_DMA_CONFLICT 0x80001398 ++#define EVENT_NDIS_MEMORY_CONFLICT 0x80001399 ++#define EVENT_NDIS_INTERRUPT_CONFLICT 0x8000139A ++#define EVENT_NDIS_DMA_CONFLICT 0x8000139B ++#define EVENT_NDIS_INVALID_DOWNLOAD_FILE_ERROR 0xC000139C ++#define EVENT_NDIS_MAXRECEIVES_ERROR 0x8000139D ++#define EVENT_NDIS_MAXTRANSMITS_ERROR 0x8000139E ++#define EVENT_NDIS_MAXFRAMESIZE_ERROR 0x8000139F ++#define EVENT_NDIS_MAXINTERNALBUFS_ERROR 0x800013A0 ++#define EVENT_NDIS_MAXMULTICAST_ERROR 0x800013A1 ++#define EVENT_NDIS_PRODUCTID_ERROR 0x800013A2 ++#define EVENT_NDIS_LOBE_FAILUE_ERROR 0x800013A3 ++#define EVENT_NDIS_SIGNAL_LOSS_ERROR 0x800013A4 ++#define EVENT_NDIS_REMOVE_RECEIVED_ERROR 0x800013A5 ++#define EVENT_NDIS_TOKEN_RING_CORRECTION 0x400013A6 ++#define EVENT_NDIS_ADAPTER_CHECK_ERROR 0xC00013A7 ++#define EVENT_NDIS_RESET_FAILURE_ERROR 0x800013A8 ++#define EVENT_NDIS_CABLE_DISCONNECTED_ERROR 0x800013A9 ++#define EVENT_NDIS_RESET_FAILURE_CORRECTION 0x800013AA ++ ++/* packet filter bits used by NDIS_OID_PACKET_FILTER */ ++#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 ++#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 ++#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 ++#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 ++#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 ++#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 ++#define NDIS_PACKET_TYPE_SMT 0x00000040 ++#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 ++#define NDIS_PACKET_TYPE_GROUP 0x00001000 ++#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00002000 ++#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00004000 ++#define NDIS_PACKET_TYPE_MAC_FRAME 0x00008000 ++ ++/* memory allocation flags */ ++#define NDIS_MEMORY_CONTIGUOUS 0x00000001 ++#define NDIS_MEMORY_NONCACHED 0x00000002 ++ ++/* Attribute flags to NdisMSetAtrributesEx */ ++#define NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT 0x00000001 ++#define NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT 0x00000002 ++#define NDIS_ATTRIBUTE_IGNORE_TOKEN_RING_ERRORS 0x00000004 ++#define NDIS_ATTRIBUTE_BUS_MASTER 0x00000008 ++#define NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER 0x00000010 ++#define NDIS_ATTRIBUTE_DESERIALIZE 0x00000020 ++#define NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND 0x00000040 ++#define NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK 0x00000080 ++#define NDIS_ATTRIBUTE_NOT_CO_NDIS 0x00000100 ++#define NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS 0x00000200 ++ ++#define OID_TCP_TASK_OFFLOAD 0xFC010201 ++ ++#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 ++#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 ++#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 ++#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 ++#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 ++#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 ++#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 ++#define NDIS_MAC_OPTION_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00000080 ++#define NDIS_MAC_OPTION_RECEIVE_AT_DPC 0x00000100 ++#define NDIS_MAC_OPTION_8021Q_VLAN 0x00000200 ++#define NDIS_MAC_OPTION_RESERVED 0x80000000 ++ ++#define deserialized_driver(wnd) (wnd->attributes & NDIS_ATTRIBUTE_DESERIALIZE) ++ ++static inline void serialize_lock(struct ndis_device *wnd) ++{ ++ nt_spin_lock(&wnd->nmb->lock); ++} ++ ++static inline void serialize_unlock(struct ndis_device *wnd) ++{ ++ nt_spin_unlock(&wnd->nmb->lock); ++} ++ ++static inline KIRQL serialize_lock_irql(struct ndis_device *wnd) ++{ ++ if (deserialized_driver(wnd)) ++ return raise_irql(DISPATCH_LEVEL); ++ else ++ return nt_spin_lock_irql(&wnd->nmb->lock, DISPATCH_LEVEL); ++} ++ ++static inline void serialize_unlock_irql(struct ndis_device *wnd, ++ KIRQL irql) ++{ ++ if (deserialized_driver(wnd)) ++ lower_irql(irql); ++ else ++ nt_spin_unlock_irql(&wnd->nmb->lock, irql); ++} ++ ++static inline void if_serialize_lock(struct ndis_device *wnd) ++{ ++ if (!deserialized_driver(wnd)) ++ nt_spin_lock(&wnd->nmb->lock); ++} ++ ++static inline void if_serialize_unlock(struct ndis_device *wnd) ++{ ++ if (!deserialized_driver(wnd)) ++ nt_spin_unlock(&wnd->nmb->lock); ++} ++ ++#endif /* NDIS_H */ +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndiswrapper.h +--- linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndiswrapper.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,219 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _NDISWRAPPER_H_ ++#define _NDISWRAPPER_H_ ++ ++#define DRIVER_VERSION "1.59" ++#define UTILS_VERSION "1.9" ++ ++#define DRIVER_NAME "ndiswrapper" ++#define DRIVER_CONFIG_DIR "/etc/ndiswrapper" ++ ++#define NDIS_ESSID_MAX_SIZE 32 ++#define NDIS_ENCODING_TOKEN_MAX 32 ++#define MAX_ENCR_KEYS 4 ++#define TX_RING_SIZE 16 ++#define NDIS_MAX_RATES 8 ++#define NDIS_MAX_RATES_EX 16 ++ ++#define WRAP_PCI_BUS 5 ++#define WRAP_PCMCIA_BUS 8 ++/* some USB devices, e.g., DWL-G120 have BusType as 0 */ ++#define WRAP_INTERNAL_BUS 0 ++/* documentation at msdn says 15 is PNP bus, but inf files from all ++ * vendors say 15 is USB; which is correct? */ ++#define WRAP_USB_BUS 15 ++ ++/* NDIS device must be 0, for compatibility with old versions of ++ * ndiswrapper where device type for NDIS drivers is 0 */ ++#define WRAP_NDIS_DEVICE 0 ++#define WRAP_USB_DEVICE 1 ++#define WRAP_BLUETOOTH_DEVICE1 2 ++#define WRAP_BLUETOOTH_DEVICE2 3 ++ ++#define WRAP_DEVICE_BUS(dev, bus) ((dev) << 8 | (bus)) ++#define WRAP_BUS(dev_bus) ((dev_bus) & 0x000FF) ++#define WRAP_DEVICE(dev_bus) ((dev_bus) >> 8) ++ ++#define MAX_DRIVER_NAME_LEN 32 ++#define MAX_VERSION_STRING_LEN 64 ++#define MAX_SETTING_NAME_LEN 128 ++#define MAX_SETTING_VALUE_LEN 256 ++ ++#define MAX_DRIVER_PE_IMAGES 4 ++#define MAX_DRIVER_BIN_FILES 5 ++#define MAX_DEVICE_SETTINGS 512 ++ ++#define MAX_ALLOCATED_URBS 15 ++ ++#define DEV_ANY_ID -1 ++ ++#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] ++#define MACSTRSEP "%02x:%02x:%02x:%02x:%02x:%02x" ++#define MACSTR "%02x%02x%02x%02x%02x%02x" ++#define MACINTADR(a) (int*)&((a)[0]), (int*)&((a)[1]), (int*)&((a)[2]), \ ++ (int*)&((a)[3]), (int*)&((a)[4]), (int*)&((a)[5]) ++ ++#ifdef __KERNEL__ ++/* DEBUG macros */ ++ ++#define MSG(level, fmt, ...) \ ++ printk(level DRIVER_NAME " (%s:%d): " fmt "\n", \ ++ __func__, __LINE__ , ## __VA_ARGS__) ++ ++#define WARNING(fmt, ...) MSG(KERN_WARNING, fmt, ## __VA_ARGS__) ++#define ERROR(fmt, ...) MSG(KERN_ERR, fmt , ## __VA_ARGS__) ++#define INFO(fmt, ...) MSG(KERN_INFO, fmt , ## __VA_ARGS__) ++#define TODO() WARNING("not fully implemented (yet)") ++ ++#define TRACE(level, fmt, ...) \ ++do { \ ++ if (debug >= level) \ ++ printk(KERN_INFO "%s (%s:%d): " fmt "\n", DRIVER_NAME, \ ++ __func__, __LINE__ , ## __VA_ARGS__); \ ++} while (0) ++#define TRACE0(fmt, ...) TRACE(0, fmt , ## __VA_ARGS__) ++ ++extern int debug; ++ ++#ifndef DEBUG ++#define DEBUG 0 ++#endif ++ ++/* for a block of code */ ++#if DEBUG >= 1 ++#define DBG_BLOCK(level) if (debug >= level) ++#else ++#define DBG_BLOCK(level) while (0) ++#endif ++ ++#if DEBUG >= 1 ++#define TRACE1(fmt, ...) TRACE(1, fmt , ## __VA_ARGS__) ++#else ++#define TRACE1(fmt, ...) do { } while (0) ++#endif ++ ++#if DEBUG >= 2 ++#define TRACE2(fmt, ...) TRACE(2, fmt , ## __VA_ARGS__) ++#else ++#define TRACE2(fmt, ...) do { } while (0) ++#endif ++ ++#if DEBUG >= 3 ++#define TRACE3(fmt, ...) TRACE(3, fmt , ## __VA_ARGS__) ++#else ++#define TRACE3(fmt, ...) do { } while (0) ++#endif ++ ++#if DEBUG >= 4 ++#define TRACE4(fmt, ...) TRACE(4, fmt , ## __VA_ARGS__) ++#else ++#define TRACE4(fmt, ...) do { } while (0) ++#endif ++ ++#if DEBUG >= 5 ++#define TRACE5(fmt, ...) TRACE(5, fmt , ## __VA_ARGS__) ++#else ++#define TRACE5(fmt, ...) do { } while (0) ++#endif ++ ++#if DEBUG >= 6 ++#define TRACE6(fmt, ...) TRACE(6, fmt , ## __VA_ARGS__) ++#else ++#define TRACE6(fmt, ...) do { } while (0) ++#endif ++ ++#define ENTER0(fmt, ...) TRACE0("Enter " fmt , ## __VA_ARGS__) ++#define ENTER1(fmt, ...) TRACE1("Enter " fmt , ## __VA_ARGS__) ++#define ENTER2(fmt, ...) TRACE2("Enter " fmt , ## __VA_ARGS__) ++#define ENTER3(fmt, ...) TRACE3("Enter " fmt , ## __VA_ARGS__) ++#define ENTER4(fmt, ...) TRACE4("Enter " fmt , ## __VA_ARGS__) ++#define ENTER5(fmt, ...) TRACE5("Enter " fmt , ## __VA_ARGS__) ++#define ENTER6(fmt, ...) TRACE6("Enter " fmt , ## __VA_ARGS__) ++ ++#define EXIT0(stmt) do { TRACE0("Exit"); stmt; } while (0) ++#define EXIT1(stmt) do { TRACE1("Exit"); stmt; } while (0) ++#define EXIT2(stmt) do { TRACE2("Exit"); stmt; } while (0) ++#define EXIT3(stmt) do { TRACE3("Exit"); stmt; } while (0) ++#define EXIT4(stmt) do { TRACE4("Exit"); stmt; } while (0) ++#define EXIT5(stmt) do { TRACE5("Exit"); stmt; } while (0) ++#define EXIT6(stmt) do { TRACE6("Exit"); stmt; } while (0) ++ ++#if defined(USB_DEBUG) ++#define USBTRACE TRACE0 ++#define USBENTER ENTER0 ++#define USBEXIT EXIT0 ++#else ++#define USBTRACE(fmt, ...) do { } while (0) ++#define USBENTER(fmt, ...) ++#define USBEXIT(stmt) stmt ++#endif ++ ++#if defined(EVENT_DEBUG) ++#define EVENTTRACE TRACE0 ++#define EVENTENTER ENTER0 ++#define EVENTEXIT EXIT0 ++#else ++#define EVENTTRACE(fmt, ...) do { } while (0) ++#define EVENTENTER(fmt, ...) ++#define EVENTEXIT(stmt) stmt ++#endif ++ ++#if defined(TIMER_DEBUG) ++#define TIMERTRACE TRACE0 ++#define TIMERENTER ENTER0 ++#define TIMEREXIT EXIT0 ++#else ++#define TIMERTRACE(fmt, ...) do { } while (0) ++#define TIMERENTER(fmt, ...) ++#define TIMEREXIT(stmt) stmt ++#endif ++ ++#if defined(IO_DEBUG) ++#define IOTRACE TRACE0 ++#define IOENTER ENTER0 ++#define IOEXIT EXIT0 ++#else ++#define IOTRACE(fmt, ...) do { } while (0) ++#define IOENTER(fmt, ...) ++#define IOEXIT(stmt) stmt ++#endif ++ ++#if defined(WORK_DEBUG) ++#define WORKTRACE TRACE0 ++#define WORKENTER ENTER0 ++#define WORKEXIT EXIT0 ++#else ++#define WORKTRACE(fmt, ...) do { } while (0) ++#define WORKENTER(fmt, ...) ++#define WORKEXIT(stmt) stmt ++#endif ++ ++#if DEBUG >= 1 ++#define assert(expr) \ ++do { \ ++ if (!(expr)) { \ ++ ERROR("assertion '%s' failed", #expr); \ ++ dump_stack(); \ ++ } \ ++} while (0) ++#else ++#define assert(expr) do { } while (0) ++#endif ++ ++#endif // __KERNEL__ ++ ++#endif // NDISWRAPPER_H +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.c +--- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,2677 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ntoskernel.h" ++#include "ndis.h" ++#include "usb.h" ++#include "pnp.h" ++#include "loader.h" ++#include "ntoskernel_exports.h" ++ ++/* MDLs describe a range of virtual address with an array of physical ++ * pages right after the header. For different ranges of virtual ++ * addresses, the number of entries of physical pages may be different ++ * (depending on number of entries required). If we want to allocate ++ * MDLs from a pool, the size has to be constant. So we assume that ++ * maximum range used by a driver is MDL_CACHE_PAGES; if a driver ++ * requests an MDL for a bigger region, we allocate it with kmalloc; ++ * otherwise, we allocate from the pool */ ++ ++#define MDL_CACHE_PAGES 3 ++#define MDL_CACHE_SIZE (sizeof(struct mdl) + \ ++ (sizeof(PFN_NUMBER) * MDL_CACHE_PAGES)) ++struct wrap_mdl { ++ struct nt_list list; ++ struct mdl mdl[0]; ++}; ++ ++/* everything here is for all drivers/devices - not per driver/device */ ++static spinlock_t dispatcher_lock; ++spinlock_t ntoskernel_lock; ++static void *mdl_cache; ++static struct nt_list wrap_mdl_list; ++ ++static struct work_struct kdpc_work; ++static void kdpc_worker(struct work_struct *dummy); ++ ++static struct nt_list kdpc_list; ++static spinlock_t kdpc_list_lock; ++ ++static struct nt_list callback_objects; ++ ++struct nt_list object_list; ++ ++struct bus_driver { ++ struct nt_list list; ++ char name[MAX_DRIVER_NAME_LEN]; ++ struct driver_object drv_obj; ++}; ++ ++static struct nt_list bus_driver_list; ++ ++static struct work_struct ntos_work; ++static struct nt_list ntos_work_list; ++static spinlock_t ntos_work_lock; ++static void ntos_work_worker(struct work_struct *dummy); ++spinlock_t irp_cancel_lock; ++static NT_SPIN_LOCK nt_list_lock; ++static struct nt_slist wrap_timer_slist; ++CCHAR cpu_count; ++ ++/* compute ticks (100ns) since 1601 until when system booted into ++ * wrap_ticks_to_boot */ ++u64 wrap_ticks_to_boot; ++ ++#if defined(CONFIG_X86_64) ++static struct timer_list shared_data_timer; ++struct kuser_shared_data kuser_shared_data; ++static void update_user_shared_data_proc(unsigned long data); ++#endif ++ ++WIN_SYMBOL_MAP("KeTickCount", &jiffies) ++WIN_SYMBOL_MAP("KeNumberProcessors", &cpu_count) ++WIN_SYMBOL_MAP("NlsMbCodePageTag", FALSE) ++ ++struct workqueue_struct *ntos_wq; ++ ++#ifdef WRAP_PREEMPT ++DEFINE_PER_CPU(struct irql_info, irql_info); ++#endif ++ ++#if defined(CONFIG_X86_64) ++static void update_user_shared_data_proc(unsigned long data) ++{ ++ /* timer is supposed to be scheduled every 10ms, but bigger ++ * intervals seem to work (tried up to 50ms) */ ++ *((ULONG64 *)&kuser_shared_data.system_time) = ticks_1601(); ++ *((ULONG64 *)&kuser_shared_data.interrupt_time) = ++ jiffies * TICKSPERSEC / HZ; ++ *((ULONG64 *)&kuser_shared_data.tick) = jiffies; ++ ++ mod_timer(&shared_data_timer, jiffies + MSEC_TO_HZ(30)); ++} ++#endif ++ ++void *allocate_object(ULONG size, enum common_object_type type, ++ struct unicode_string *name) ++{ ++ struct common_object_header *hdr; ++ void *body; ++ ++ /* we pad header as prefix to body */ ++ hdr = ExAllocatePoolWithTag(NonPagedPool, OBJECT_SIZE(size), 0); ++ if (!hdr) { ++ WARNING("couldn't allocate memory"); ++ return NULL; ++ } ++ memset(hdr, 0, OBJECT_SIZE(size)); ++ if (name) { ++ hdr->name.buf = ExAllocatePoolWithTag(NonPagedPool, ++ name->max_length, 0); ++ if (!hdr->name.buf) { ++ ExFreePool(hdr); ++ return NULL; ++ } ++ memcpy(hdr->name.buf, name->buf, name->max_length); ++ hdr->name.length = name->length; ++ hdr->name.max_length = name->max_length; ++ } ++ hdr->type = type; ++ hdr->ref_count = 1; ++ spin_lock_bh(&ntoskernel_lock); ++ /* threads are looked up often (in KeWaitForXXX), so optimize ++ * for fast lookups of threads */ ++ if (type == OBJECT_TYPE_NT_THREAD) ++ InsertHeadList(&object_list, &hdr->list); ++ else ++ InsertTailList(&object_list, &hdr->list); ++ spin_unlock_bh(&ntoskernel_lock); ++ body = HEADER_TO_OBJECT(hdr); ++ TRACE3("allocated hdr: %p, body: %p", hdr, body); ++ return body; ++} ++ ++static void free_object(void *object) ++{ ++ struct common_object_header *hdr; ++ ++ hdr = OBJECT_TO_HEADER(object); ++ spin_lock_bh(&ntoskernel_lock); ++ RemoveEntryList(&hdr->list); ++ spin_unlock_bh(&ntoskernel_lock); ++ TRACE3("freed hdr: %p, body: %p", hdr, object); ++ if (hdr->name.buf) ++ ExFreePool(hdr->name.buf); ++ ExFreePool(hdr); ++} ++ ++static int add_bus_driver(const char *name) ++{ ++ struct bus_driver *bus_driver; ++ ++ bus_driver = kzalloc(sizeof(*bus_driver), GFP_KERNEL); ++ if (!bus_driver) { ++ ERROR("couldn't allocate memory"); ++ return -ENOMEM; ++ } ++ strncpy(bus_driver->name, name, sizeof(bus_driver->name)); ++ bus_driver->name[sizeof(bus_driver->name)-1] = 0; ++ spin_lock_bh(&ntoskernel_lock); ++ InsertTailList(&bus_driver_list, &bus_driver->list); ++ spin_unlock_bh(&ntoskernel_lock); ++ TRACE1("bus driver %s is at %p", name, &bus_driver->drv_obj); ++ return STATUS_SUCCESS; ++} ++ ++struct driver_object *find_bus_driver(const char *name) ++{ ++ struct bus_driver *bus_driver; ++ struct driver_object *drv_obj; ++ ++ spin_lock_bh(&ntoskernel_lock); ++ drv_obj = NULL; ++ nt_list_for_each_entry(bus_driver, &bus_driver_list, list) { ++ if (strcmp(bus_driver->name, name) == 0) { ++ drv_obj = &bus_driver->drv_obj; ++ break; ++ } ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ return drv_obj; ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExfInterlockedInsertHeadList,3) ++ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) ++{ ++ struct nt_list *first; ++ unsigned long flags; ++ ++ ENTER5("head = %p, entry = %p", head, entry); ++ nt_spin_lock_irqsave(lock, flags); ++ first = InsertHeadList(head, entry); ++ nt_spin_unlock_irqrestore(lock, flags); ++ TRACE5("head = %p, old = %p", head, first); ++ return first; ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExInterlockedInsertHeadList,3) ++ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) ++{ ++ ENTER5("%p", head); ++ return ExfInterlockedInsertHeadList(head, entry, lock); ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExfInterlockedInsertTailList,3) ++ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) ++{ ++ struct nt_list *last; ++ unsigned long flags; ++ ++ ENTER5("head = %p, entry = %p", head, entry); ++ nt_spin_lock_irqsave(lock, flags); ++ last = InsertTailList(head, entry); ++ nt_spin_unlock_irqrestore(lock, flags); ++ TRACE5("head = %p, old = %p", head, last); ++ return last; ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExInterlockedInsertTailList,3) ++ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) ++{ ++ ENTER5("%p", head); ++ return ExfInterlockedInsertTailList(head, entry, lock); ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExfInterlockedRemoveHeadList,2) ++ (struct nt_list *head, NT_SPIN_LOCK *lock) ++{ ++ struct nt_list *ret; ++ unsigned long flags; ++ ++ ENTER5("head = %p", head); ++ nt_spin_lock_irqsave(lock, flags); ++ ret = RemoveHeadList(head); ++ nt_spin_unlock_irqrestore(lock, flags); ++ TRACE5("head = %p, ret = %p", head, ret); ++ return ret; ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExInterlockedRemoveHeadList,2) ++ (struct nt_list *head, NT_SPIN_LOCK *lock) ++{ ++ ENTER5("%p", head); ++ return ExfInterlockedRemoveHeadList(head, lock); ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExfInterlockedRemoveTailList,2) ++ (struct nt_list *head, NT_SPIN_LOCK *lock) ++{ ++ struct nt_list *ret; ++ unsigned long flags; ++ ++ ENTER5("head = %p", head); ++ nt_spin_lock_irqsave(lock, flags); ++ ret = RemoveTailList(head); ++ nt_spin_unlock_irqrestore(lock, flags); ++ TRACE5("head = %p, ret = %p", head, ret); ++ return ret; ++} ++ ++wfastcall struct nt_list *WIN_FUNC(ExInterlockedRemoveTailList,2) ++ (struct nt_list *head, NT_SPIN_LOCK *lock) ++{ ++ ENTER5("%p", head); ++ return ExfInterlockedRemoveTailList(head, lock); ++} ++ ++wfastcall void WIN_FUNC(InitializeSListHead,1) ++ (nt_slist_header *head) ++{ ++ memset(head, 0, sizeof(*head)); ++} ++ ++wfastcall struct nt_slist *WIN_FUNC(ExInterlockedPushEntrySList,3) ++ (nt_slist_header *head, struct nt_slist *entry, NT_SPIN_LOCK *lock) ++{ ++ struct nt_slist *ret; ++ ++ ret = PushEntrySList(head, entry, lock); ++ return ret; ++} ++ ++wstdcall struct nt_slist *WIN_FUNC(ExpInterlockedPushEntrySList,2) ++ (nt_slist_header *head, struct nt_slist *entry) ++{ ++ struct nt_slist *ret; ++ ++ ret = PushEntrySList(head, entry, &nt_list_lock); ++ return ret; ++} ++ ++wfastcall struct nt_slist *WIN_FUNC(InterlockedPushEntrySList,2) ++ (nt_slist_header *head, struct nt_slist *entry) ++{ ++ struct nt_slist *ret; ++ ++ ret = PushEntrySList(head, entry, &nt_list_lock); ++ return ret; ++} ++ ++wfastcall struct nt_slist *WIN_FUNC(ExInterlockedPopEntrySList,2) ++ (nt_slist_header *head, NT_SPIN_LOCK *lock) ++{ ++ struct nt_slist *ret; ++ ++ ret = PopEntrySList(head, lock); ++ return ret; ++} ++ ++wstdcall struct nt_slist *WIN_FUNC(ExpInterlockedPopEntrySList,1) ++ (nt_slist_header *head) ++{ ++ struct nt_slist *ret; ++ ++ ret = PopEntrySList(head, &nt_list_lock); ++ return ret; ++} ++ ++wfastcall struct nt_slist *WIN_FUNC(InterlockedPopEntrySList,1) ++ (nt_slist_header *head) ++{ ++ struct nt_slist *ret; ++ ++ ret = PopEntrySList(head, &nt_list_lock); ++ return ret; ++} ++ ++wstdcall USHORT WIN_FUNC(ExQueryDepthSList,1) ++ (nt_slist_header *head) ++{ ++ USHORT depth; ++ ENTER5("%p", head); ++ depth = head->depth; ++ TRACE5("%d, %p", depth, head->next); ++ return depth; ++} ++ ++wfastcall LONG WIN_FUNC(InterlockedIncrement,1) ++ (LONG volatile *val) ++{ ++ return post_atomic_add(*val, 1); ++} ++ ++wfastcall LONG WIN_FUNC(InterlockedDecrement,1) ++ (LONG volatile *val) ++{ ++ return post_atomic_add(*val, -1); ++} ++ ++wfastcall LONG WIN_FUNC(InterlockedExchange,2) ++ (LONG volatile *target, LONG val) ++{ ++ return xchg(target, val); ++} ++ ++wfastcall LONG WIN_FUNC(InterlockedCompareExchange,3) ++ (LONG volatile *dest, LONG new, LONG old) ++{ ++ return cmpxchg(dest, old, new); ++} ++ ++wfastcall void WIN_FUNC(ExInterlockedAddLargeStatistic,2) ++ (LARGE_INTEGER volatile *plint, ULONG n) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++#ifdef CONFIG_X86_64 ++ __asm__ __volatile__( ++ "\n" ++ LOCK_PREFIX "add %1, %0\n\t" ++ : "+m" (*plint) ++ : "r" (n)); ++#else ++ __asm__ __volatile__( ++ "1:\t" ++ " movl %1, %%ebx\n\t" ++ " movl %%edx, %%ecx\n\t" ++ " addl %%eax, %%ebx\n\t" ++ " adcl $0, %%ecx\n\t" ++ LOCK_PREFIX "cmpxchg8b %0\n\t" ++ " jnz 1b\n\t" ++ : "+m" (*plint) ++ : "m" (n), "A" (*plint) ++ : "ebx", "ecx"); ++#endif ++ local_irq_restore(flags); ++} ++ ++static void initialize_object(struct dispatcher_header *dh, enum dh_type type, ++ int state) ++{ ++ memset(dh, 0, sizeof(*dh)); ++ set_object_type(dh, type); ++ dh->signal_state = state; ++ InitializeListHead(&dh->wait_blocks); ++} ++ ++static void timer_proc(unsigned long data) ++{ ++ struct wrap_timer *wrap_timer = (struct wrap_timer *)data; ++ struct nt_timer *nt_timer; ++ struct kdpc *kdpc; ++ ++ nt_timer = wrap_timer->nt_timer; ++ TIMERENTER("%p(%p), %lu", wrap_timer, nt_timer, jiffies); ++#ifdef TIMER_DEBUG ++ BUG_ON(wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC); ++ BUG_ON(nt_timer->wrap_timer_magic != WRAP_TIMER_MAGIC); ++#endif ++ KeSetEvent((struct nt_event *)nt_timer, 0, FALSE); ++ if (wrap_timer->repeat) ++ mod_timer(&wrap_timer->timer, jiffies + wrap_timer->repeat); ++ kdpc = nt_timer->kdpc; ++ if (kdpc) ++ queue_kdpc(kdpc); ++ TIMEREXIT(return); ++} ++ ++void wrap_init_timer(struct nt_timer *nt_timer, enum timer_type type, ++ struct ndis_mp_block *nmb) ++{ ++ struct wrap_timer *wrap_timer; ++ ++ /* TODO: if a timer is initialized more than once, we allocate ++ * memory for wrap_timer more than once for the same nt_timer, ++ * wasting memory. We can check if nt_timer->wrap_timer_magic is ++ * set and not allocate, but it is not guaranteed always to be ++ * safe */ ++ TIMERENTER("%p", nt_timer); ++ /* we allocate memory for wrap_timer behind driver's back and ++ * there is no NDIS/DDK function where this memory can be ++ * freed, so we use slack_kmalloc so it gets freed when driver ++ * is unloaded */ ++ if (nmb) ++ wrap_timer = kzalloc(sizeof(*wrap_timer), irql_gfp()); ++ else ++ wrap_timer = slack_kzalloc(sizeof(*wrap_timer)); ++ if (!wrap_timer) { ++ ERROR("couldn't allocate memory for timer"); ++ return; ++ } ++ ++ init_timer(&wrap_timer->timer); ++ wrap_timer->timer.data = (unsigned long)wrap_timer; ++ wrap_timer->timer.function = timer_proc; ++ wrap_timer->nt_timer = nt_timer; ++#ifdef TIMER_DEBUG ++ wrap_timer->wrap_timer_magic = WRAP_TIMER_MAGIC; ++#endif ++ nt_timer->wrap_timer = wrap_timer; ++ nt_timer->kdpc = NULL; ++ initialize_object(&nt_timer->dh, (enum dh_type)type, 0); ++ nt_timer->wrap_timer_magic = WRAP_TIMER_MAGIC; ++ TIMERTRACE("timer %p (%p)", wrap_timer, nt_timer); ++ spin_lock_bh(&ntoskernel_lock); ++ if (nmb) { ++ wrap_timer->slist.next = nmb->wnd->wrap_timer_slist.next; ++ nmb->wnd->wrap_timer_slist.next = &wrap_timer->slist; ++ } else { ++ wrap_timer->slist.next = wrap_timer_slist.next; ++ wrap_timer_slist.next = &wrap_timer->slist; ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ TIMEREXIT(return); ++} ++ ++wstdcall void WIN_FUNC(KeInitializeTimerEx,2) ++ (struct nt_timer *nt_timer, enum timer_type type) ++{ ++ TIMERENTER("%p", nt_timer); ++ wrap_init_timer(nt_timer, type, NULL); ++} ++ ++wstdcall void WIN_FUNC(KeInitializeTimer,1) ++ (struct nt_timer *nt_timer) ++{ ++ TIMERENTER("%p", nt_timer); ++ wrap_init_timer(nt_timer, NotificationTimer, NULL); ++} ++ ++/* expires and repeat are in HZ */ ++BOOLEAN wrap_set_timer(struct nt_timer *nt_timer, unsigned long expires_hz, ++ unsigned long repeat_hz, struct kdpc *kdpc) ++{ ++ struct wrap_timer *wrap_timer; ++ ++ TIMERENTER("%p, %lu, %lu, %p, %lu", ++ nt_timer, expires_hz, repeat_hz, kdpc, jiffies); ++ ++ wrap_timer = nt_timer->wrap_timer; ++ TIMERTRACE("%p", wrap_timer); ++#ifdef TIMER_DEBUG ++ if (wrap_timer->nt_timer != nt_timer) ++ WARNING("bad timers: %p, %p, %p", wrap_timer, nt_timer, ++ wrap_timer->nt_timer); ++ if (nt_timer->wrap_timer_magic != WRAP_TIMER_MAGIC) { ++ WARNING("buggy Windows timer didn't initialize timer %p", ++ nt_timer); ++ return FALSE; ++ } ++ if (wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC) { ++ WARNING("timer %p is not initialized (%lx)?", ++ wrap_timer, wrap_timer->wrap_timer_magic); ++ wrap_timer->wrap_timer_magic = WRAP_TIMER_MAGIC; ++ } ++#endif ++ KeClearEvent((struct nt_event *)nt_timer); ++ nt_timer->kdpc = kdpc; ++ wrap_timer->repeat = repeat_hz; ++ if (mod_timer(&wrap_timer->timer, jiffies + expires_hz)) ++ TIMEREXIT(return TRUE); ++ else ++ TIMEREXIT(return FALSE); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeSetTimerEx,4) ++ (struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks, ++ LONG period_ms, struct kdpc *kdpc) ++{ ++ unsigned long expires_hz, repeat_hz; ++ ++ TIMERENTER("%p, %lld, %d", nt_timer, duetime_ticks, period_ms); ++ expires_hz = SYSTEM_TIME_TO_HZ(duetime_ticks); ++ repeat_hz = MSEC_TO_HZ(period_ms); ++ return wrap_set_timer(nt_timer, expires_hz, repeat_hz, kdpc); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeSetTimer,3) ++ (struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks, ++ struct kdpc *kdpc) ++{ ++ TIMERENTER("%p, %lld, %p", nt_timer, duetime_ticks, kdpc); ++ return KeSetTimerEx(nt_timer, duetime_ticks, 0, kdpc); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeCancelTimer,1) ++ (struct nt_timer *nt_timer) ++{ ++ struct wrap_timer *wrap_timer; ++ int ret; ++ ++ TIMERENTER("%p", nt_timer); ++ wrap_timer = nt_timer->wrap_timer; ++ if (!wrap_timer) { ++ ERROR("invalid wrap_timer"); ++ return TRUE; ++ } ++#ifdef TIMER_DEBUG ++ BUG_ON(wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC); ++#endif ++ /* disable timer before deleting so if it is periodic timer, it ++ * won't be re-armed after deleting */ ++ wrap_timer->repeat = 0; ++ ret = del_timer_sync(&wrap_timer->timer); ++ /* the documentation for KeCancelTimer suggests the DPC is ++ * deqeued, but actually DPC is left to run */ ++ if (ret) ++ TIMEREXIT(return TRUE); ++ else ++ TIMEREXIT(return FALSE); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeReadStateTimer,1) ++ (struct nt_timer *nt_timer) ++{ ++ if (nt_timer->dh.signal_state) ++ return TRUE; ++ else ++ return FALSE; ++} ++ ++wstdcall void WIN_FUNC(KeInitializeDpc,3) ++ (struct kdpc *kdpc, void *func, void *ctx) ++{ ++ ENTER3("%p, %p, %p", kdpc, func, ctx); ++ memset(kdpc, 0, sizeof(*kdpc)); ++ kdpc->func = func; ++ kdpc->ctx = ctx; ++ InitializeListHead(&kdpc->list); ++} ++ ++static void kdpc_worker(struct work_struct *dummy) ++{ ++ struct nt_list *entry; ++ struct kdpc *kdpc; ++ unsigned long flags; ++ KIRQL irql; ++ ++ WORKENTER(""); ++ irql = raise_irql(DISPATCH_LEVEL); ++ while (1) { ++ spin_lock_irqsave(&kdpc_list_lock, flags); ++ entry = RemoveHeadList(&kdpc_list); ++ if (entry) { ++ kdpc = container_of(entry, struct kdpc, list); ++ assert(kdpc->queued); ++ kdpc->queued = 0; ++ } else ++ kdpc = NULL; ++ spin_unlock_irqrestore(&kdpc_list_lock, flags); ++ if (!kdpc) ++ break; ++ WORKTRACE("%p, %p, %p, %p, %p", kdpc, kdpc->func, kdpc->ctx, ++ kdpc->arg1, kdpc->arg2); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ LIN2WIN4(kdpc->func, kdpc, kdpc->ctx, kdpc->arg1, kdpc->arg2); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ } ++ lower_irql(irql); ++ WORKEXIT(return); ++} ++ ++wstdcall void WIN_FUNC(KeFlushQueuedDpcs,0) ++ (void) ++{ ++ kdpc_worker(NULL); ++} ++ ++BOOLEAN queue_kdpc(struct kdpc *kdpc) ++{ ++ BOOLEAN ret; ++ unsigned long flags; ++ ++ WORKENTER("%p", kdpc); ++ spin_lock_irqsave(&kdpc_list_lock, flags); ++ if (kdpc->queued) ++ ret = FALSE; ++ else { ++ if (unlikely(kdpc->importance == HighImportance)) ++ InsertHeadList(&kdpc_list, &kdpc->list); ++ else ++ InsertTailList(&kdpc_list, &kdpc->list); ++ kdpc->queued = 1; ++ ret = TRUE; ++ } ++ spin_unlock_irqrestore(&kdpc_list_lock, flags); ++ if (ret == TRUE) ++ queue_work(ntos_wq, &kdpc_work); ++ WORKTRACE("%d", ret); ++ return ret; ++} ++ ++BOOLEAN dequeue_kdpc(struct kdpc *kdpc) ++{ ++ BOOLEAN ret; ++ unsigned long flags; ++ ++ WORKENTER("%p", kdpc); ++ spin_lock_irqsave(&kdpc_list_lock, flags); ++ if (kdpc->queued) { ++ RemoveEntryList(&kdpc->list); ++ kdpc->queued = 0; ++ ret = TRUE; ++ } else ++ ret = FALSE; ++ spin_unlock_irqrestore(&kdpc_list_lock, flags); ++ WORKTRACE("%d", ret); ++ return ret; ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeInsertQueueDpc,3) ++ (struct kdpc *kdpc, void *arg1, void *arg2) ++{ ++ WORKENTER("%p, %p, %p", kdpc, arg1, arg2); ++ kdpc->arg1 = arg1; ++ kdpc->arg2 = arg2; ++ return queue_kdpc(kdpc); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeRemoveQueueDpc,1) ++ (struct kdpc *kdpc) ++{ ++ return dequeue_kdpc(kdpc); ++} ++ ++wstdcall void WIN_FUNC(KeSetImportanceDpc,2) ++ (struct kdpc *kdpc, enum kdpc_importance importance) ++{ ++ kdpc->importance = importance; ++} ++ ++static void ntos_work_worker(struct work_struct *dummy) ++{ ++ struct ntos_work_item *ntos_work_item; ++ struct nt_list *cur; ++ ++ while (1) { ++ spin_lock_bh(&ntos_work_lock); ++ cur = RemoveHeadList(&ntos_work_list); ++ spin_unlock_bh(&ntos_work_lock); ++ if (!cur) ++ break; ++ ntos_work_item = container_of(cur, struct ntos_work_item, list); ++ WORKTRACE("%p: executing %p, %p, %p", current, ++ ntos_work_item->func, ntos_work_item->arg1, ++ ntos_work_item->arg2); ++ LIN2WIN2(ntos_work_item->func, ntos_work_item->arg1, ++ ntos_work_item->arg2); ++ kfree(ntos_work_item); ++ } ++ WORKEXIT(return); ++} ++ ++int schedule_ntos_work_item(NTOS_WORK_FUNC func, void *arg1, void *arg2) ++{ ++ struct ntos_work_item *ntos_work_item; ++ ++ WORKENTER("adding work: %p, %p, %p", func, arg1, arg2); ++ ntos_work_item = kmalloc(sizeof(*ntos_work_item), irql_gfp()); ++ if (!ntos_work_item) { ++ ERROR("couldn't allocate memory"); ++ return -ENOMEM; ++ } ++ ntos_work_item->func = func; ++ ntos_work_item->arg1 = arg1; ++ ntos_work_item->arg2 = arg2; ++ spin_lock_bh(&ntos_work_lock); ++ InsertTailList(&ntos_work_list, &ntos_work_item->list); ++ spin_unlock_bh(&ntos_work_lock); ++ queue_work(ntos_wq, &ntos_work); ++ WORKEXIT(return 0); ++} ++ ++wstdcall void WIN_FUNC(KeInitializeSpinLock,1) ++ (NT_SPIN_LOCK *lock) ++{ ++ ENTER6("%p", lock); ++ nt_spin_lock_init(lock); ++} ++ ++wstdcall void WIN_FUNC(KeAcquireSpinLock,2) ++ (NT_SPIN_LOCK *lock, KIRQL *irql) ++{ ++ ENTER6("%p", lock); ++ *irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL); ++} ++ ++wstdcall void WIN_FUNC(KeReleaseSpinLock,2) ++ (NT_SPIN_LOCK *lock, KIRQL oldirql) ++{ ++ ENTER6("%p", lock); ++ nt_spin_unlock_irql(lock, oldirql); ++} ++ ++wstdcall void WIN_FUNC(KeAcquireSpinLockAtDpcLevel,1) ++ (NT_SPIN_LOCK *lock) ++{ ++ ENTER6("%p", lock); ++ nt_spin_lock(lock); ++} ++ ++wstdcall void WIN_FUNC(KeReleaseSpinLockFromDpcLevel,1) ++ (NT_SPIN_LOCK *lock) ++{ ++ ENTER6("%p", lock); ++ nt_spin_unlock(lock); ++} ++ ++wstdcall void WIN_FUNC(KeRaiseIrql,2) ++ (KIRQL newirql, KIRQL *oldirql) ++{ ++ ENTER6("%d", newirql); ++ *oldirql = raise_irql(newirql); ++} ++ ++wstdcall KIRQL WIN_FUNC(KeRaiseIrqlToDpcLevel,0) ++ (void) ++{ ++ return raise_irql(DISPATCH_LEVEL); ++} ++ ++wstdcall void WIN_FUNC(KeLowerIrql,1) ++ (KIRQL irql) ++{ ++ ENTER6("%d", irql); ++ lower_irql(irql); ++} ++ ++wstdcall KIRQL WIN_FUNC(KeAcquireSpinLockRaiseToDpc,1) ++ (NT_SPIN_LOCK *lock) ++{ ++ ENTER6("%p", lock); ++ return nt_spin_lock_irql(lock, DISPATCH_LEVEL); ++} ++ ++wstdcall void *WIN_FUNC(ExAllocatePoolWithTag,3) ++ (enum pool_type pool_type, SIZE_T size, ULONG tag) ++{ ++ void *addr; ++ ++ ENTER4("pool_type: %d, size: %zu, tag: 0x%x", pool_type, size, tag); ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ if (size < PAGE_SIZE) ++ addr = kmalloc(size, irql_gfp()); ++ else { ++ if (irql_gfp() & GFP_ATOMIC) { ++ addr = __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM, ++ PAGE_KERNEL); ++ TRACE1("%p, %zu", addr, size); ++ } else { ++ addr = vmalloc(size); ++ TRACE1("%p, %zu", addr, size); ++ } ++ } ++ DBG_BLOCK(1) { ++ if (addr) ++ TRACE4("addr: %p, %zu", addr, size); ++ else ++ TRACE1("failed: %zu", size); ++ } ++ return addr; ++} ++WIN_FUNC_DECL(ExAllocatePoolWithTag,3) ++ ++wstdcall void WIN_FUNC(ExFreePoolWithTag,2) ++ (void *addr, ULONG tag) ++{ ++ TRACE4("%p", addr); ++ if ((unsigned long)addr < VMALLOC_START || ++ (unsigned long)addr >= VMALLOC_END) ++ kfree(addr); ++ else ++ vfree(addr); ++ ++ EXIT4(return); ++} ++ ++wstdcall void WIN_FUNC(ExFreePool,1) ++ (void *addr) ++{ ++ ExFreePoolWithTag(addr, 0); ++} ++WIN_FUNC_DECL(ExFreePool,1) ++ ++wstdcall void WIN_FUNC(ExInitializeNPagedLookasideList,7) ++ (struct npaged_lookaside_list *lookaside, ++ LOOKASIDE_ALLOC_FUNC *alloc_func, LOOKASIDE_FREE_FUNC *free_func, ++ ULONG flags, SIZE_T size, ULONG tag, USHORT depth) ++{ ++ ENTER3("lookaside: %p, size: %zu, flags: %u, head: %p, " ++ "alloc: %p, free: %p", lookaside, size, flags, ++ lookaside, alloc_func, free_func); ++ ++ memset(lookaside, 0, sizeof(*lookaside)); ++ ++ lookaside->size = size; ++ lookaside->tag = tag; ++ lookaside->depth = 4; ++ lookaside->maxdepth = 256; ++ lookaside->pool_type = NonPagedPool; ++ ++ if (alloc_func) ++ lookaside->alloc_func = alloc_func; ++ else ++ lookaside->alloc_func = WIN_FUNC_PTR(ExAllocatePoolWithTag,3); ++ if (free_func) ++ lookaside->free_func = free_func; ++ else ++ lookaside->free_func = WIN_FUNC_PTR(ExFreePool,1); ++ ++#ifndef CONFIG_X86_64 ++ nt_spin_lock_init(&lookaside->obsolete); ++#endif ++ EXIT3(return); ++} ++ ++wstdcall void WIN_FUNC(ExDeleteNPagedLookasideList,1) ++ (struct npaged_lookaside_list *lookaside) ++{ ++ struct nt_slist *entry; ++ ++ ENTER3("lookaside = %p", lookaside); ++ while ((entry = ExpInterlockedPopEntrySList(&lookaside->head))) ++ LIN2WIN1(lookaside->free_func, entry); ++ EXIT3(return); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ExCreateCallback,4) ++ (struct callback_object **object, struct object_attributes *attributes, ++ BOOLEAN create, BOOLEAN allow_multiple_callbacks) ++{ ++ struct callback_object *obj; ++ ++ ENTER2(""); ++ spin_lock_bh(&ntoskernel_lock); ++ nt_list_for_each_entry(obj, &callback_objects, callback_funcs) { ++ if (obj->attributes == attributes) { ++ spin_unlock_bh(&ntoskernel_lock); ++ *object = obj; ++ return STATUS_SUCCESS; ++ } ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ obj = allocate_object(sizeof(struct callback_object), ++ OBJECT_TYPE_CALLBACK, NULL); ++ if (!obj) ++ EXIT2(return STATUS_INSUFFICIENT_RESOURCES); ++ InitializeListHead(&obj->callback_funcs); ++ nt_spin_lock_init(&obj->lock); ++ obj->allow_multiple_callbacks = allow_multiple_callbacks; ++ obj->attributes = attributes; ++ *object = obj; ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall void *WIN_FUNC(ExRegisterCallback,3) ++ (struct callback_object *object, PCALLBACK_FUNCTION func, void *context) ++{ ++ struct callback_func *callback; ++ KIRQL irql; ++ ++ ENTER2(""); ++ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); ++ if (object->allow_multiple_callbacks == FALSE && ++ !IsListEmpty(&object->callback_funcs)) { ++ nt_spin_unlock_irql(&object->lock, irql); ++ EXIT2(return NULL); ++ } ++ nt_spin_unlock_irql(&object->lock, irql); ++ callback = kmalloc(sizeof(*callback), GFP_KERNEL); ++ if (!callback) { ++ ERROR("couldn't allocate memory"); ++ return NULL; ++ } ++ callback->func = func; ++ callback->context = context; ++ callback->object = object; ++ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); ++ InsertTailList(&object->callback_funcs, &callback->list); ++ nt_spin_unlock_irql(&object->lock, irql); ++ EXIT2(return callback); ++} ++ ++wstdcall void WIN_FUNC(ExUnregisterCallback,1) ++ (struct callback_func *callback) ++{ ++ struct callback_object *object; ++ KIRQL irql; ++ ++ ENTER3("%p", callback); ++ if (!callback) ++ return; ++ object = callback->object; ++ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); ++ RemoveEntryList(&callback->list); ++ nt_spin_unlock_irql(&object->lock, irql); ++ kfree(callback); ++ return; ++} ++ ++wstdcall void WIN_FUNC(ExNotifyCallback,3) ++ (struct callback_object *object, void *arg1, void *arg2) ++{ ++ struct callback_func *callback; ++ KIRQL irql; ++ ++ ENTER3("%p", object); ++ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); ++ nt_list_for_each_entry(callback, &object->callback_funcs, list) { ++ LIN2WIN3(callback->func, callback->context, arg1, arg2); ++ } ++ nt_spin_unlock_irql(&object->lock, irql); ++ return; ++} ++ ++/* check and set signaled state; should be called with dispatcher_lock held */ ++/* @grab indicates if the event should be grabbed or checked ++ * - note that a semaphore may stay in signaled state for multiple ++ * 'grabs' if the count is > 1 */ ++static int grab_object(struct dispatcher_header *dh, ++ struct task_struct *thread, int grab) ++{ ++ EVENTTRACE("%p, %p, %d, %d", dh, thread, grab, dh->signal_state); ++ if (unlikely(is_mutex_object(dh))) { ++ struct nt_mutex *nt_mutex; ++ nt_mutex = container_of(dh, struct nt_mutex, dh); ++ EVENTTRACE("%p, %p, %d, %p, %d", nt_mutex, ++ nt_mutex->owner_thread, dh->signal_state, ++ thread, grab); ++ /* either no thread owns the mutex or this thread owns ++ * it */ ++ assert(dh->signal_state == 1 && nt_mutex->owner_thread == NULL); ++ assert(dh->signal_state < 1 && nt_mutex->owner_thread != NULL); ++ if ((dh->signal_state == 1 && nt_mutex->owner_thread == NULL) || ++ nt_mutex->owner_thread == thread) { ++ if (grab) { ++ dh->signal_state--; ++ nt_mutex->owner_thread = thread; ++ } ++ EVENTEXIT(return 1); ++ } ++ } else if (dh->signal_state > 0) { ++ /* to grab, decrement signal_state for synchronization ++ * or semaphore objects */ ++ if (grab && (is_synch_object(dh) || is_semaphore_object(dh))) ++ dh->signal_state--; ++ EVENTEXIT(return 1); ++ } ++ EVENTEXIT(return 0); ++} ++ ++/* this function should be called holding dispatcher_lock */ ++static void object_signaled(struct dispatcher_header *dh) ++{ ++ struct nt_list *cur, *next; ++ struct wait_block *wb; ++ ++ EVENTENTER("%p", dh); ++ nt_list_for_each_safe(cur, next, &dh->wait_blocks) { ++ wb = container_of(cur, struct wait_block, list); ++ assert(wb->thread != NULL); ++ assert(wb->object == NULL); ++ if (!grab_object(dh, wb->thread, 1)) ++ continue; ++ EVENTTRACE("%p (%p): waking %p", dh, wb, wb->thread); ++ RemoveEntryList(cur); ++ wb->object = dh; ++ *(wb->wait_done) = 1; ++ wake_up_process(wb->thread); ++ } ++ EVENTEXIT(return); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(KeWaitForMultipleObjects,8) ++ (ULONG count, void *object[], enum wait_type wait_type, ++ KWAIT_REASON wait_reason, KPROCESSOR_MODE wait_mode, ++ BOOLEAN alertable, LARGE_INTEGER *timeout, ++ struct wait_block *wait_block_array) ++{ ++ int i, res = 0, wait_count, wait_done; ++ typeof(jiffies) wait_hz = 0; ++ struct wait_block *wb, wb_array[THREAD_WAIT_OBJECTS]; ++ struct dispatcher_header *dh; ++ KIRQL irql = current_irql(); ++ ++ EVENTENTER("%p, %d, %u, %p", current, count, wait_type, timeout); ++ ++ if (count > MAX_WAIT_OBJECTS || ++ (count > THREAD_WAIT_OBJECTS && wait_block_array == NULL)) ++ EVENTEXIT(return STATUS_INVALID_PARAMETER); ++ ++ if (wait_block_array == NULL) ++ wb = wb_array; ++ else ++ wb = wait_block_array; ++ ++ /* If *timeout == 0: In the case of WaitAny, if an object can ++ * be grabbed (object is in signaled state), grab and ++ * return. In the case of WaitAll, we have to first make sure ++ * all objects can be grabbed. If any/some of them can't be ++ * grabbed, either we return STATUS_TIMEOUT or wait for them, ++ * depending on how to satisfy wait. If all of them can be ++ * grabbed, we will grab them in the next loop below */ ++ ++ spin_lock_bh(&dispatcher_lock); ++ for (i = wait_count = 0; i < count; i++) { ++ dh = object[i]; ++ EVENTTRACE("%p: event %p (%d)", current, dh, dh->signal_state); ++ /* wait_type == 1 for WaitAny, 0 for WaitAll */ ++ if (grab_object(dh, current, wait_type)) { ++ if (wait_type == WaitAny) { ++ spin_unlock_bh(&dispatcher_lock); ++ EVENTEXIT(return STATUS_WAIT_0 + i); ++ } ++ } else { ++ EVENTTRACE("%p: wait for %p", current, dh); ++ wait_count++; ++ } ++ } ++ ++ if (timeout && *timeout == 0 && wait_count) { ++ spin_unlock_bh(&dispatcher_lock); ++ EVENTEXIT(return STATUS_TIMEOUT); ++ } ++ ++ /* get the list of objects the thread needs to wait on and add ++ * the thread on the wait list for each such object */ ++ /* if *timeout == 0, this step will grab all the objects */ ++ wait_done = 0; ++ for (i = 0; i < count; i++) { ++ dh = object[i]; ++ EVENTTRACE("%p: event %p (%d)", current, dh, dh->signal_state); ++ wb[i].object = NULL; ++ if (grab_object(dh, current, 1)) { ++ EVENTTRACE("%p: no wait for %p (%d)", ++ current, dh, dh->signal_state); ++ /* mark that we are not waiting on this object */ ++ wb[i].thread = NULL; ++ } else { ++ wb[i].wait_done = &wait_done; ++ wb[i].thread = current; ++ EVENTTRACE("%p: wait for %p", current, dh); ++ InsertTailList(&dh->wait_blocks, &wb[i].list); ++ } ++ } ++ spin_unlock_bh(&dispatcher_lock); ++ if (wait_count == 0) ++ EVENTEXIT(return STATUS_SUCCESS); ++ ++ assert(timeout == NULL || *timeout != 0); ++ if (timeout == NULL) ++ wait_hz = 0; ++ else ++ wait_hz = SYSTEM_TIME_TO_HZ(*timeout); ++ ++ if (irql >= DISPATCH_LEVEL) { ++ WARNING("attempt to wait with irql %d", irql); ++ EVENTEXIT(return STATUS_INVALID_PARAMETER); ++ } ++ EVENTTRACE("%p: sleep for %ld on %p", current, wait_hz, &wait_done); ++ /* we don't honor 'alertable' - according to description for ++ * this, even if waiting in non-alertable state, thread may be ++ * alerted in some circumstances */ ++ while (wait_count) { ++ res = wait_condition(wait_done, wait_hz, TASK_INTERRUPTIBLE); ++ spin_lock_bh(&dispatcher_lock); ++ EVENTTRACE("%p woke up: %d, %d", current, res, wait_done); ++ /* the event may have been set by the time ++ * wrap_wait_event returned and spinlock obtained, so ++ * don't rely on value of 'res' - check event status */ ++ if (!wait_done) { ++ assert(res <= 0); ++ /* timed out or interrupted; remove from wait list */ ++ for (i = 0; i < count; i++) { ++ if (!wb[i].thread) ++ continue; ++ EVENTTRACE("%p: timedout, dequeue %p (%p)", ++ current, object[i], wb[i].object); ++ assert(wb[i].object == NULL); ++ RemoveEntryList(&wb[i].list); ++ } ++ spin_unlock_bh(&dispatcher_lock); ++ if (res < 0) ++ EVENTEXIT(return STATUS_ALERTED); ++ else ++ EVENTEXIT(return STATUS_TIMEOUT); ++ } ++ assert(res > 0); ++ /* woken because object(s) signaled */ ++ for (i = 0; wait_count && i < count; i++) { ++ if (!wb[i].thread || !wb[i].object) ++ continue; ++ DBG_BLOCK(1) { ++ if (wb[i].object != object[i]) { ++ EVENTTRACE("oops %p != %p", ++ wb[i].object, object[i]); ++ continue; ++ } ++ } ++ wait_count--; ++ if (wait_type == WaitAny) { ++ int j; ++ /* done; remove from rest of wait list */ ++ for (j = i + 1; j < count; j++) { ++ if (wb[j].thread && !wb[j].object) ++ RemoveEntryList(&wb[j].list); ++ } ++ spin_unlock_bh(&dispatcher_lock); ++ EVENTEXIT(return STATUS_WAIT_0 + i); ++ } ++ } ++ wait_done = 0; ++ spin_unlock_bh(&dispatcher_lock); ++ if (wait_count == 0) ++ EVENTEXIT(return STATUS_SUCCESS); ++ ++ /* this thread is still waiting for more objects, so ++ * let it wait for remaining time and those objects */ ++ if (timeout) ++ wait_hz = res; ++ else ++ wait_hz = 0; ++ } ++ /* should never reach here, but compiler wants return value */ ++ ERROR("%p: wait_hz: %ld", current, wait_hz); ++ EVENTEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(KeWaitForSingleObject,5) ++ (void *object, KWAIT_REASON wait_reason, KPROCESSOR_MODE wait_mode, ++ BOOLEAN alertable, LARGE_INTEGER *timeout) ++{ ++ return KeWaitForMultipleObjects(1, &object, WaitAny, wait_reason, ++ wait_mode, alertable, timeout, NULL); ++} ++ ++wstdcall void WIN_FUNC(KeInitializeEvent,3) ++ (struct nt_event *nt_event, enum event_type type, BOOLEAN state) ++{ ++ EVENTENTER("event = %p, type = %d, state = %d", nt_event, type, state); ++ initialize_object(&nt_event->dh, (enum dh_type)type, state); ++ EVENTEXIT(return); ++} ++ ++wstdcall LONG WIN_FUNC(KeSetEvent,3) ++ (struct nt_event *nt_event, KPRIORITY incr, BOOLEAN wait) ++{ ++ LONG old_state; ++ ++ EVENTENTER("%p, %d", nt_event, nt_event->dh.type); ++ if (wait == TRUE) ++ WARNING("wait = %d, not yet implemented", wait); ++ spin_lock_bh(&dispatcher_lock); ++ old_state = nt_event->dh.signal_state; ++ nt_event->dh.signal_state = 1; ++ if (old_state == 0) ++ object_signaled(&nt_event->dh); ++ spin_unlock_bh(&dispatcher_lock); ++ EVENTEXIT(return old_state); ++} ++ ++wstdcall void WIN_FUNC(KeClearEvent,1) ++ (struct nt_event *nt_event) ++{ ++ EVENTENTER("%p", nt_event); ++ nt_event->dh.signal_state = 0; ++ EVENTEXIT(return); ++} ++ ++wstdcall LONG WIN_FUNC(KeResetEvent,1) ++ (struct nt_event *nt_event) ++{ ++ LONG old_state; ++ ++ EVENTENTER("%p", nt_event); ++ old_state = xchg(&nt_event->dh.signal_state, 0); ++ EVENTEXIT(return old_state); ++} ++ ++wstdcall LONG WIN_FUNC(KeReadStateEvent,1) ++ (struct nt_event *nt_event) ++{ ++ LONG state; ++ ++ state = nt_event->dh.signal_state; ++ EVENTTRACE("%d", state); ++ return state; ++} ++ ++wstdcall void WIN_FUNC(KeInitializeMutex,2) ++ (struct nt_mutex *mutex, ULONG level) ++{ ++ EVENTENTER("%p", mutex); ++ initialize_object(&mutex->dh, MutexObject, 1); ++ mutex->dh.size = sizeof(*mutex); ++ InitializeListHead(&mutex->list); ++ mutex->abandoned = FALSE; ++ mutex->apc_disable = 1; ++ mutex->owner_thread = NULL; ++ EVENTEXIT(return); ++} ++ ++wstdcall LONG WIN_FUNC(KeReleaseMutex,2) ++ (struct nt_mutex *mutex, BOOLEAN wait) ++{ ++ LONG ret; ++ struct task_struct *thread; ++ ++ EVENTENTER("%p, %d, %p", mutex, wait, current); ++ if (wait == TRUE) ++ WARNING("wait: %d", wait); ++ thread = current; ++ spin_lock_bh(&dispatcher_lock); ++ EVENTTRACE("%p, %p, %p, %d", mutex, thread, mutex->owner_thread, ++ mutex->dh.signal_state); ++ if ((mutex->owner_thread == thread) && (mutex->dh.signal_state <= 0)) { ++ ret = mutex->dh.signal_state++; ++ if (ret == 0) { ++ mutex->owner_thread = NULL; ++ object_signaled(&mutex->dh); ++ } ++ } else { ++ ret = STATUS_MUTANT_NOT_OWNED; ++ WARNING("invalid mutex: %p, %p, %p", mutex, mutex->owner_thread, ++ thread); ++ } ++ EVENTTRACE("%p, %p, %p, %d", mutex, thread, mutex->owner_thread, ++ mutex->dh.signal_state); ++ spin_unlock_bh(&dispatcher_lock); ++ EVENTEXIT(return ret); ++} ++ ++wstdcall void WIN_FUNC(KeInitializeSemaphore,3) ++ (struct nt_semaphore *semaphore, LONG count, LONG limit) ++{ ++ EVENTENTER("%p: %d", semaphore, count); ++ /* if limit > 1, we need to satisfy as many waits (until count ++ * becomes 0); so we keep decrementing count every time a wait ++ * is satisfied */ ++ initialize_object(&semaphore->dh, SemaphoreObject, count); ++ semaphore->dh.size = sizeof(*semaphore); ++ semaphore->limit = limit; ++ EVENTEXIT(return); ++} ++ ++wstdcall LONG WIN_FUNC(KeReleaseSemaphore,4) ++ (struct nt_semaphore *semaphore, KPRIORITY incr, LONG adjustment, ++ BOOLEAN wait) ++{ ++ LONG ret; ++ ++ EVENTENTER("%p", semaphore); ++ spin_lock_bh(&dispatcher_lock); ++ ret = semaphore->dh.signal_state; ++ assert(ret >= 0); ++ if (semaphore->dh.signal_state + adjustment <= semaphore->limit) ++ semaphore->dh.signal_state += adjustment; ++ else { ++ WARNING("releasing %d over limit %d", adjustment, ++ semaphore->limit); ++ semaphore->dh.signal_state = semaphore->limit; ++ } ++ if (semaphore->dh.signal_state > 0) ++ object_signaled(&semaphore->dh); ++ spin_unlock_bh(&dispatcher_lock); ++ EVENTEXIT(return ret); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(KeDelayExecutionThread,3) ++ (KPROCESSOR_MODE wait_mode, BOOLEAN alertable, LARGE_INTEGER *interval) ++{ ++ int res; ++ long timeout; ++ ++ if (wait_mode != 0) ++ ERROR("invalid wait_mode %d", wait_mode); ++ ++ timeout = SYSTEM_TIME_TO_HZ(*interval); ++ EVENTTRACE("%p, %lld, %ld", current, *interval, timeout); ++ if (timeout <= 0) ++ EVENTEXIT(return STATUS_SUCCESS); ++ ++ if (alertable) ++ set_current_state(TASK_INTERRUPTIBLE); ++ else ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ ++ res = schedule_timeout(timeout); ++ EVENTTRACE("%p, %d", current, res); ++ if (res == 0) ++ EVENTEXIT(return STATUS_SUCCESS); ++ else ++ EVENTEXIT(return STATUS_ALERTED); ++} ++ ++wstdcall ULONGLONG WIN_FUNC(KeQueryInterruptTime,0) ++ (void) ++{ ++ EXIT5(return jiffies * TICKSPERJIFFY); ++} ++ ++wstdcall ULONG WIN_FUNC(KeQueryTimeIncrement,0) ++ (void) ++{ ++ EXIT5(return TICKSPERSEC / HZ); ++} ++ ++wstdcall void WIN_FUNC(KeQuerySystemTime,1) ++ (LARGE_INTEGER *time) ++{ ++ *time = ticks_1601(); ++ TRACE5("%llu, %lu", *time, jiffies); ++} ++ ++wstdcall void WIN_FUNC(KeQueryTickCount,1) ++ (LARGE_INTEGER *count) ++{ ++ *count = jiffies; ++} ++ ++wstdcall LARGE_INTEGER WIN_FUNC(KeQueryPerformanceCounter,1) ++ (LARGE_INTEGER *counter) ++{ ++ if (counter) ++ *counter = HZ; ++ return jiffies; ++} ++ ++wstdcall KAFFINITY WIN_FUNC(KeQueryActiveProcessors,0) ++ (void) ++{ ++ int i, n; ++ KAFFINITY bits = 0; ++ n = num_online_cpus(); ++ for (i = 0; i < n; i++) ++ bits = (bits << 1) | 1; ++ return bits; ++} ++ ++struct nt_thread *get_current_nt_thread(void) ++{ ++ struct task_struct *task = current; ++ struct nt_thread *thread; ++ struct common_object_header *header; ++ ++ TRACE6("task: %p", task); ++ thread = NULL; ++ spin_lock_bh(&ntoskernel_lock); ++ nt_list_for_each_entry(header, &object_list, list) { ++ TRACE6("%p, %d", header, header->type); ++ if (header->type != OBJECT_TYPE_NT_THREAD) ++ break; ++ thread = HEADER_TO_OBJECT(header); ++ TRACE6("%p, %p", thread, thread->task); ++ if (thread->task == task) ++ break; ++ else ++ thread = NULL; ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ if (thread == NULL) ++ TRACE4("couldn't find thread for task %p, %d", task, task->pid); ++ TRACE6("%p", thread); ++ return thread; ++} ++ ++static struct task_struct *get_nt_thread_task(struct nt_thread *thread) ++{ ++ struct task_struct *task; ++ struct common_object_header *header; ++ ++ TRACE6("%p", thread); ++ task = NULL; ++ spin_lock_bh(&ntoskernel_lock); ++ nt_list_for_each_entry(header, &object_list, list) { ++ TRACE6("%p, %d", header, header->type); ++ if (header->type != OBJECT_TYPE_NT_THREAD) ++ break; ++ if (thread == HEADER_TO_OBJECT(header)) { ++ task = thread->task; ++ break; ++ } ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ if (task == NULL) ++ TRACE2("%p: couldn't find task for %p", current, thread); ++ return task; ++} ++ ++static struct nt_thread *create_nt_thread(struct task_struct *task) ++{ ++ struct nt_thread *thread; ++ thread = allocate_object(sizeof(*thread), OBJECT_TYPE_NT_THREAD, NULL); ++ if (!thread) { ++ ERROR("couldn't allocate thread object"); ++ EXIT2(return NULL); ++ } ++ thread->task = task; ++ if (task) ++ thread->pid = task->pid; ++ else ++ thread->pid = 0; ++ nt_spin_lock_init(&thread->lock); ++ InitializeListHead(&thread->irps); ++ initialize_object(&thread->dh, ThreadObject, 0); ++ thread->dh.size = sizeof(*thread); ++ thread->prio = LOW_PRIORITY; ++ return thread; ++} ++ ++wstdcall struct nt_thread *WIN_FUNC(KeGetCurrentThread,0) ++ (void) ++{ ++ struct nt_thread *thread = get_current_nt_thread(); ++ TRACE2("%p, %p", thread, current); ++ return thread; ++} ++ ++wstdcall KPRIORITY WIN_FUNC(KeQueryPriorityThread,1) ++ (struct nt_thread *thread) ++{ ++ KPRIORITY prio; ++ struct task_struct *task; ++ ++ TRACE2("%p", thread); ++#ifdef CONFIG_X86_64 ++ /* sis163u driver for amd64 passes 0x1f from thread created by ++ * PsCreateSystemThread - no idea what is 0x1f */ ++ if (thread == (void *)0x1f) ++ thread = get_current_nt_thread(); ++#endif ++ if (!thread) { ++ TRACE2("invalid thread"); ++ EXIT2(return LOW_REALTIME_PRIORITY); ++ } ++ task = get_nt_thread_task(thread); ++ if (!task) { ++ TRACE2("couldn't find task for thread: %p", thread); ++ EXIT2(return LOW_REALTIME_PRIORITY); ++ } ++ ++ prio = thread->prio; ++ ++ TRACE2("%d", prio); ++ return prio; ++} ++ ++wstdcall KPRIORITY WIN_FUNC(KeSetPriorityThread,2) ++ (struct nt_thread *thread, KPRIORITY prio) ++{ ++ KPRIORITY old_prio; ++ struct task_struct *task; ++ ++ TRACE2("thread: %p, priority = %u", thread, prio); ++#ifdef CONFIG_X86_64 ++ if (thread == (void *)0x1f) ++ thread = get_current_nt_thread(); ++#endif ++ if (!thread) { ++ TRACE2("invalid thread"); ++ EXIT2(return LOW_REALTIME_PRIORITY); ++ } ++ task = get_nt_thread_task(thread); ++ if (!task) { ++ TRACE2("couldn't find task for thread: %p", thread); ++ EXIT2(return LOW_REALTIME_PRIORITY); ++ } ++ ++ old_prio = thread->prio; ++ thread->prio = prio; ++ ++ TRACE2("%d, %d", old_prio, thread->prio); ++ return old_prio; ++} ++ ++struct thread_trampoline { ++ void (*func)(void *) wstdcall; ++ void *ctx; ++ struct nt_thread *thread; ++ struct completion started; ++}; ++ ++static int ntdriver_thread(void *data) ++{ ++ struct thread_trampoline *thread_tramp = data; ++ /* yes, a tramp! */ ++ typeof(thread_tramp->func) func = thread_tramp->func; ++ typeof(thread_tramp->ctx) ctx = thread_tramp->ctx; ++ ++ thread_tramp->thread->task = current; ++ thread_tramp->thread->pid = current->pid; ++ TRACE2("thread: %p, task: %p (%d)", thread_tramp->thread, ++ current, current->pid); ++ complete(&thread_tramp->started); ++ ++#ifdef PF_NOFREEZE ++ current->flags |= PF_NOFREEZE; ++#endif ++ strncpy(current->comm, "ntdriver", sizeof(current->comm)); ++ current->comm[sizeof(current->comm)-1] = 0; ++ LIN2WIN1(func, ctx); ++ ERROR("task: %p", current); ++ return 0; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(PsCreateSystemThread,7) ++ (void **handle, ULONG access, void *obj_attr, void *process, ++ void *client_id, void (*func)(void *) wstdcall, void *ctx) ++{ ++ struct thread_trampoline thread_tramp; ++ ++ ENTER2("handle = %p, access = %u, obj_attr = %p, process = %p, " ++ "client_id = %p, func = %p, context = %p", handle, access, ++ obj_attr, process, client_id, func, ctx); ++ ++ thread_tramp.thread = create_nt_thread(NULL); ++ if (!thread_tramp.thread) { ++ ERROR("couldn't allocate thread object"); ++ EXIT2(return STATUS_RESOURCES); ++ } ++ TRACE2("thread: %p", thread_tramp.thread); ++ thread_tramp.func = func; ++ thread_tramp.ctx = ctx; ++ init_completion(&thread_tramp.started); ++ ++ thread_tramp.thread->task = kthread_run(ntdriver_thread, ++ &thread_tramp, "ntdriver"); ++ if (IS_ERR(thread_tramp.thread->task)) { ++ free_object(thread_tramp.thread); ++ EXIT2(return STATUS_FAILURE); ++ } ++ TRACE2("created task: %p", thread_tramp.thread->task); ++ ++ wait_for_completion(&thread_tramp.started); ++ *handle = OBJECT_TO_HEADER(thread_tramp.thread); ++ TRACE2("created thread: %p, %p", thread_tramp.thread, *handle); ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(PsTerminateSystemThread,1) ++ (NTSTATUS status) ++{ ++ struct nt_thread *thread; ++ ++ TRACE2("%p, %08X", current, status); ++ thread = get_current_nt_thread(); ++ TRACE2("%p", thread); ++ if (thread) { ++ KeSetEvent((struct nt_event *)&thread->dh, 0, FALSE); ++ while (1) { ++ struct nt_list *ent; ++ struct irp *irp; ++ KIRQL irql; ++ irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL); ++ ent = RemoveHeadList(&thread->irps); ++ nt_spin_unlock_irql(&thread->lock, irql); ++ if (!ent) ++ break; ++ irp = container_of(ent, struct irp, thread_list); ++ IOTRACE("%p", irp); ++ IoCancelIrp(irp); ++ } ++ /* the driver may later query this status with ++ * ZwQueryInformationThread */ ++ thread->status = status; ++ } else ++ ERROR("couldn't find thread for task: %p", current); ++ ++ complete_and_exit(NULL, status); ++ ERROR("oops: %p, %d", thread->task, thread->pid); ++ return STATUS_FAILURE; ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeRemoveEntryDeviceQueue,2) ++ (struct kdevice_queue *dev_queue, struct kdevice_queue_entry *entry) ++{ ++ struct kdevice_queue_entry *e; ++ KIRQL irql; ++ ++ irql = nt_spin_lock_irql(&dev_queue->lock, DISPATCH_LEVEL); ++ nt_list_for_each_entry(e, &dev_queue->list, list) { ++ if (e == entry) { ++ RemoveEntryList(&e->list); ++ nt_spin_unlock_irql(&dev_queue->lock, irql); ++ return TRUE; ++ } ++ } ++ nt_spin_unlock_irql(&dev_queue->lock, irql); ++ return FALSE; ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeSynchronizeExecution,3) ++ (struct kinterrupt *interrupt, PKSYNCHRONIZE_ROUTINE synch_routine, ++ void *ctx) ++{ ++ BOOLEAN ret; ++ unsigned long flags; ++ ++ nt_spin_lock_irqsave(interrupt->actual_lock, flags); ++ ret = LIN2WIN1(synch_routine, ctx); ++ nt_spin_unlock_irqrestore(interrupt->actual_lock, flags); ++ TRACE6("%d", ret); ++ return ret; ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeRegisterBugCheckReasonCallback,4) ++ (void *callback_record, void *callback_routine, UINT reason, ++ char *component) ++{ ++ TRACE1("callback_record: %p, callback_routine: %p, reason: %d, " ++ "component: %s", callback_record, callback_routine, reason, ++ component); ++ TODO(); ++ return FALSE; ++} ++ ++wstdcall BOOLEAN WIN_FUNC(KeDeregisterBugCheckReasonCallback,1) ++ (void *callback_record) ++{ ++ TRACE1("callback_record: %p", callback_record); ++ TODO(); ++ return TRUE; ++} ++ ++wstdcall void *WIN_FUNC(MmAllocateContiguousMemorySpecifyCache,5) ++ (SIZE_T size, PHYSICAL_ADDRESS lowest, PHYSICAL_ADDRESS highest, ++ PHYSICAL_ADDRESS boundary, enum memory_caching_type cache_type) ++{ ++ void *addr; ++ gfp_t flags; ++ ++ ENTER2("%zu, 0x%llx, 0x%llx, 0x%llx, %d", size, lowest, ++ highest, boundary, cache_type); ++ flags = irql_gfp(); ++ addr = wrap_get_free_pages(flags, size); ++ TRACE2("%p, %zu, 0x%x", addr, size, flags); ++ if (addr && ((virt_to_phys(addr) + size) <= highest)) ++ EXIT2(return addr); ++#ifdef CONFIG_X86_64 ++ /* GFP_DMA is really only 16MB even on x86-64, but there is no ++ * other zone available */ ++ if (highest <= DMA_BIT_MASK(31)) ++ flags |= __GFP_DMA; ++ else if (highest <= DMA_BIT_MASK(32)) ++ flags |= __GFP_DMA32; ++#else ++ if (highest <= DMA_BIT_MASK(24)) ++ flags |= __GFP_DMA; ++ else if (highest > DMA_BIT_MASK(30)) ++ flags |= __GFP_HIGHMEM; ++#endif ++ if (addr) ++ free_pages((unsigned long)addr, get_order(size)); ++ addr = wrap_get_free_pages(flags, size); ++ TRACE2("%p, %zu, 0x%x", addr, size, flags); ++ return addr; ++} ++ ++wstdcall void WIN_FUNC(MmFreeContiguousMemorySpecifyCache,3) ++ (void *base, SIZE_T size, enum memory_caching_type cache_type) ++{ ++ TRACE2("%p, %zu", base, size); ++ free_pages((unsigned long)base, get_order(size)); ++} ++ ++wstdcall PHYSICAL_ADDRESS WIN_FUNC(MmGetPhysicalAddress,1) ++ (void *base) ++{ ++ unsigned long phy = virt_to_phys(base); ++ TRACE2("%p, %p", base, (void *)phy); ++ return phy; ++} ++ ++/* Atheros card with pciid 168C:0014 calls this function with 0xf0000 ++ * and 0xf6ef0 address, and then check for things that seem to be ++ * related to ACPI: "_SM_" and "_DMI_". This may be the hack they do ++ * to check if this card is installed in IBM thinkpads; we can ++ * probably get this device to work if we create a buffer with the ++ * strings as required by the driver and return virtual address for ++ * that address instead */ ++wstdcall void __iomem *WIN_FUNC(MmMapIoSpace,3) ++ (PHYSICAL_ADDRESS phys_addr, SIZE_T size, ++ enum memory_caching_type cache) ++{ ++ void __iomem *virt; ++ ENTER1("cache type: %d", cache); ++ if (cache == MmCached) ++ virt = ioremap(phys_addr, size); ++ else ++ virt = ioremap_nocache(phys_addr, size); ++ TRACE1("%llx, %zu, %p", phys_addr, size, virt); ++ return virt; ++} ++ ++wstdcall void WIN_FUNC(MmUnmapIoSpace,2) ++ (void __iomem *addr, SIZE_T size) ++{ ++ ENTER1("%p, %zu", addr, size); ++ iounmap(addr); ++ return; ++} ++ ++wstdcall ULONG WIN_FUNC(MmSizeOfMdl,2) ++ (void *base, ULONG length) ++{ ++ return sizeof(struct mdl) + ++ (sizeof(PFN_NUMBER) * SPAN_PAGES(base, length)); ++} ++ ++struct mdl *allocate_init_mdl(void *virt, ULONG length) ++{ ++ struct wrap_mdl *wrap_mdl; ++ struct mdl *mdl; ++ int mdl_size = MmSizeOfMdl(virt, length); ++ ++ if (mdl_size <= MDL_CACHE_SIZE) { ++ wrap_mdl = kmem_cache_alloc(mdl_cache, irql_gfp()); ++ if (!wrap_mdl) ++ return NULL; ++ spin_lock_bh(&dispatcher_lock); ++ InsertHeadList(&wrap_mdl_list, &wrap_mdl->list); ++ spin_unlock_bh(&dispatcher_lock); ++ mdl = wrap_mdl->mdl; ++ TRACE3("allocated mdl from cache: %p(%p), %p(%d)", ++ wrap_mdl, mdl, virt, length); ++ memset(mdl, 0, MDL_CACHE_SIZE); ++ MmInitializeMdl(mdl, virt, length); ++ /* mark the MDL as allocated from cache pool so when ++ * it is freed, we free it back to the pool */ ++ mdl->flags = MDL_ALLOCATED_FIXED_SIZE | MDL_CACHE_ALLOCATED; ++ } else { ++ wrap_mdl = ++ kmalloc(sizeof(*wrap_mdl) + mdl_size, irql_gfp()); ++ if (!wrap_mdl) ++ return NULL; ++ mdl = wrap_mdl->mdl; ++ TRACE3("allocated mdl from memory: %p(%p), %p(%d)", ++ wrap_mdl, mdl, virt, length); ++ spin_lock_bh(&dispatcher_lock); ++ InsertHeadList(&wrap_mdl_list, &wrap_mdl->list); ++ spin_unlock_bh(&dispatcher_lock); ++ memset(mdl, 0, mdl_size); ++ MmInitializeMdl(mdl, virt, length); ++ mdl->flags = MDL_ALLOCATED_FIXED_SIZE; ++ } ++ return mdl; ++} ++ ++void free_mdl(struct mdl *mdl) ++{ ++ /* A driver may allocate Mdl with NdisAllocateBuffer and free ++ * with IoFreeMdl (e.g., 64-bit Broadcom). Since we need to ++ * treat buffers allocated with Ndis calls differently, we ++ * must call NdisFreeBuffer if it is allocated with Ndis ++ * function. We set 'pool' field in Ndis functions. */ ++ if (!mdl) ++ return; ++ if (mdl->pool) ++ NdisFreeBuffer(mdl); ++ else { ++ struct wrap_mdl *wrap_mdl = (struct wrap_mdl *) ++ ((char *)mdl - offsetof(struct wrap_mdl, mdl)); ++ spin_lock_bh(&dispatcher_lock); ++ RemoveEntryList(&wrap_mdl->list); ++ spin_unlock_bh(&dispatcher_lock); ++ ++ if (mdl->flags & MDL_CACHE_ALLOCATED) { ++ TRACE3("freeing mdl cache: %p, %p, %p", ++ wrap_mdl, mdl, mdl->mappedsystemva); ++ kmem_cache_free(mdl_cache, wrap_mdl); ++ } else { ++ TRACE3("freeing mdl: %p, %p, %p", ++ wrap_mdl, mdl, mdl->mappedsystemva); ++ kfree(wrap_mdl); ++ } ++ } ++ return; ++} ++ ++wstdcall void WIN_FUNC(IoBuildPartialMdl,4) ++ (struct mdl *source, struct mdl *target, void *virt, ULONG length) ++{ ++ MmInitializeMdl(target, virt, length); ++ target->flags |= MDL_PARTIAL; ++} ++ ++wstdcall void WIN_FUNC(MmBuildMdlForNonPagedPool,1) ++ (struct mdl *mdl) ++{ ++ PFN_NUMBER *mdl_pages; ++ int i, n; ++ ++ ENTER4("%p", mdl); ++ /* already mapped */ ++// mdl->mappedsystemva = MmGetMdlVirtualAddress(mdl); ++ mdl->flags |= MDL_SOURCE_IS_NONPAGED_POOL; ++ TRACE4("%p, %p, %p, %d, %d", mdl, mdl->mappedsystemva, mdl->startva, ++ mdl->byteoffset, mdl->bytecount); ++ n = SPAN_PAGES(MmGetSystemAddressForMdl(mdl), MmGetMdlByteCount(mdl)); ++ if (n > MDL_CACHE_PAGES) ++ WARNING("%p, %d, %d", MmGetSystemAddressForMdl(mdl), ++ MmGetMdlByteCount(mdl), n); ++ mdl_pages = MmGetMdlPfnArray(mdl); ++ for (i = 0; i < n; i++) ++ mdl_pages[i] = (ULONG_PTR)mdl->startva + (i * PAGE_SIZE); ++ EXIT4(return); ++} ++ ++wstdcall void *WIN_FUNC(MmMapLockedPages,2) ++ (struct mdl *mdl, KPROCESSOR_MODE access_mode) ++{ ++ /* already mapped */ ++// mdl->mappedsystemva = MmGetMdlVirtualAddress(mdl); ++ mdl->flags |= MDL_MAPPED_TO_SYSTEM_VA; ++ /* what is the need for MDL_PARTIAL_HAS_BEEN_MAPPED? */ ++ if (mdl->flags & MDL_PARTIAL) ++ mdl->flags |= MDL_PARTIAL_HAS_BEEN_MAPPED; ++ return mdl->mappedsystemva; ++} ++ ++wstdcall void *WIN_FUNC(MmMapLockedPagesSpecifyCache,6) ++ (struct mdl *mdl, KPROCESSOR_MODE access_mode, ++ enum memory_caching_type cache_type, void *base_address, ++ ULONG bug_check, enum mm_page_priority priority) ++{ ++ return MmMapLockedPages(mdl, access_mode); ++} ++ ++wstdcall void WIN_FUNC(MmUnmapLockedPages,2) ++ (void *base, struct mdl *mdl) ++{ ++ mdl->flags &= ~MDL_MAPPED_TO_SYSTEM_VA; ++ return; ++} ++ ++wstdcall void WIN_FUNC(MmProbeAndLockPages,3) ++ (struct mdl *mdl, KPROCESSOR_MODE access_mode, ++ enum lock_operation operation) ++{ ++ /* already locked */ ++ mdl->flags |= MDL_PAGES_LOCKED; ++ return; ++} ++ ++wstdcall void WIN_FUNC(MmUnlockPages,1) ++ (struct mdl *mdl) ++{ ++ mdl->flags &= ~MDL_PAGES_LOCKED; ++ return; ++} ++ ++wstdcall BOOLEAN WIN_FUNC(MmIsAddressValid,1) ++ (void *virt_addr) ++{ ++ if (virt_addr_valid(virt_addr)) ++ return TRUE; ++ else ++ return FALSE; ++} ++ ++wstdcall void *WIN_FUNC(MmLockPagableDataSection,1) ++ (void *address) ++{ ++ return address; ++} ++ ++wstdcall void WIN_FUNC(MmUnlockPagableImageSection,1) ++ (void *handle) ++{ ++ return; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ObReferenceObjectByHandle,6) ++ (void *handle, ACCESS_MASK desired_access, void *obj_type, ++ KPROCESSOR_MODE access_mode, void **object, void *handle_info) ++{ ++ struct common_object_header *hdr; ++ ++ TRACE2("%p", handle); ++ hdr = HANDLE_TO_HEADER(handle); ++ atomic_inc_var(hdr->ref_count); ++ *object = HEADER_TO_OBJECT(hdr); ++ TRACE2("%p, %p, %d, %p", hdr, object, hdr->ref_count, *object); ++ return STATUS_SUCCESS; ++} ++ ++/* DDK doesn't say if return value should be before incrementing or ++ * after incrementing reference count, but according to #reactos ++ * developers, it should be return value after incrementing */ ++wfastcall LONG WIN_FUNC(ObfReferenceObject,1) ++ (void *object) ++{ ++ struct common_object_header *hdr; ++ LONG ret; ++ ++ hdr = OBJECT_TO_HEADER(object); ++ ret = post_atomic_add(hdr->ref_count, 1); ++ TRACE2("%p, %d, %p", hdr, hdr->ref_count, object); ++ return ret; ++} ++ ++static int dereference_object(void *object) ++{ ++ struct common_object_header *hdr; ++ int ref_count; ++ ++ ENTER2("object: %p", object); ++ hdr = OBJECT_TO_HEADER(object); ++ TRACE2("hdr: %p", hdr); ++ ref_count = post_atomic_add(hdr->ref_count, -1); ++ TRACE2("object: %p, %d", object, ref_count); ++ if (ref_count < 0) ++ ERROR("invalid object: %p (%d)", object, ref_count); ++ if (ref_count <= 0) { ++ free_object(object); ++ return 1; ++ } else ++ return 0; ++} ++ ++wfastcall void WIN_FUNC(ObfDereferenceObject,1) ++ (void *object) ++{ ++ TRACE2("%p", object); ++ dereference_object(object); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwCreateFile,11) ++ (void **handle, ACCESS_MASK access_mask, ++ struct object_attributes *obj_attr, struct io_status_block *iosb, ++ LARGE_INTEGER *size, ULONG file_attr, ULONG share_access, ++ ULONG create_disposition, ULONG create_options, void *ea_buffer, ++ ULONG ea_length) ++{ ++ struct common_object_header *coh; ++ struct file_object *fo; ++ struct ansi_string ansi; ++ struct wrap_bin_file *bin_file; ++ char *file_basename; ++ NTSTATUS status; ++ ++ spin_lock_bh(&ntoskernel_lock); ++ nt_list_for_each_entry(coh, &object_list, list) { ++ if (coh->type != OBJECT_TYPE_FILE) ++ continue; ++ /* TODO: check if file is opened in shared mode */ ++ if (!RtlCompareUnicodeString(&coh->name, obj_attr->name, TRUE)) { ++ fo = HEADER_TO_OBJECT(coh); ++ bin_file = fo->wrap_bin_file; ++ *handle = coh; ++ spin_unlock_bh(&ntoskernel_lock); ++ ObReferenceObject(fo); ++ iosb->status = FILE_OPENED; ++ iosb->info = bin_file->size; ++ EXIT2(return STATUS_SUCCESS); ++ } ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ ++ if (RtlUnicodeStringToAnsiString(&ansi, obj_attr->name, TRUE) != ++ STATUS_SUCCESS) ++ EXIT2(return STATUS_INSUFFICIENT_RESOURCES); ++ ++ file_basename = strrchr(ansi.buf, '\\'); ++ if (file_basename) ++ file_basename++; ++ else ++ file_basename = ansi.buf; ++ TRACE2("file: '%s', '%s'", ansi.buf, file_basename); ++ ++ fo = allocate_object(sizeof(struct file_object), OBJECT_TYPE_FILE, ++ obj_attr->name); ++ if (!fo) { ++ RtlFreeAnsiString(&ansi); ++ iosb->status = STATUS_INSUFFICIENT_RESOURCES; ++ iosb->info = 0; ++ EXIT2(return STATUS_FAILURE); ++ } ++ coh = OBJECT_TO_HEADER(fo); ++ bin_file = get_bin_file(file_basename); ++ if (bin_file) { ++ TRACE2("%s, %s", bin_file->name, file_basename); ++ fo->flags = FILE_OPENED; ++ } else if (access_mask & FILE_WRITE_DATA) { ++ bin_file = kzalloc(sizeof(*bin_file), GFP_KERNEL); ++ if (bin_file) { ++ strncpy(bin_file->name, file_basename, ++ sizeof(bin_file->name)); ++ bin_file->name[sizeof(bin_file->name)-1] = 0; ++ bin_file->data = vmalloc(*size); ++ if (bin_file->data) { ++ memset(bin_file->data, 0, *size); ++ bin_file->size = *size; ++ fo->flags = FILE_CREATED; ++ } else { ++ kfree(bin_file); ++ bin_file = NULL; ++ } ++ } ++ } else ++ bin_file = NULL; ++ ++ RtlFreeAnsiString(&ansi); ++ if (!bin_file) { ++ iosb->status = FILE_DOES_NOT_EXIST; ++ iosb->info = 0; ++ free_object(fo); ++ EXIT2(return STATUS_FAILURE); ++ } ++ ++ fo->wrap_bin_file = bin_file; ++ fo->current_byte_offset = 0; ++ if (access_mask & FILE_READ_DATA) ++ fo->read_access = TRUE; ++ if (access_mask & FILE_WRITE_DATA) ++ fo->write_access = TRUE; ++ iosb->status = FILE_OPENED; ++ iosb->info = bin_file->size; ++ *handle = coh; ++ TRACE2("handle: %p", *handle); ++ status = STATUS_SUCCESS; ++ EXIT2(return status); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwOpenFile,6) ++ (void **handle, ACCESS_MASK access_mask, ++ struct object_attributes *obj_attr, struct io_status_block *iosb, ++ ULONG share_access, ULONG open_options) ++{ ++ LARGE_INTEGER size; ++ return ZwCreateFile(handle, access_mask, obj_attr, iosb, &size, 0, ++ share_access, 0, open_options, NULL, 0); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwReadFile,9) ++ (void *handle, struct nt_event *event, void *apc_routine, ++ void *apc_context, struct io_status_block *iosb, void *buffer, ++ ULONG length, LARGE_INTEGER *byte_offset, ULONG *key) ++{ ++ struct file_object *fo; ++ struct common_object_header *coh; ++ ULONG count; ++ size_t offset; ++ struct wrap_bin_file *file; ++ ++ TRACE2("%p", handle); ++ coh = handle; ++ if (coh->type != OBJECT_TYPE_FILE) { ++ ERROR("handle %p is invalid: %d", handle, coh->type); ++ EXIT2(return STATUS_FAILURE); ++ } ++ fo = HANDLE_TO_OBJECT(coh); ++ file = fo->wrap_bin_file; ++ TRACE2("file: %s (%zu)", file->name, file->size); ++ spin_lock_bh(&ntoskernel_lock); ++ if (byte_offset) ++ offset = *byte_offset; ++ else ++ offset = fo->current_byte_offset; ++ count = min((size_t)length, file->size - offset); ++ TRACE2("count: %u, offset: %zu, length: %u", count, offset, length); ++ memcpy(buffer, ((void *)file->data) + offset, count); ++ fo->current_byte_offset = offset + count; ++ spin_unlock_bh(&ntoskernel_lock); ++ iosb->status = STATUS_SUCCESS; ++ iosb->info = count; ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwWriteFile,9) ++ (void *handle, struct nt_event *event, void *apc_routine, ++ void *apc_context, struct io_status_block *iosb, void *buffer, ++ ULONG length, LARGE_INTEGER *byte_offset, ULONG *key) ++{ ++ struct file_object *fo; ++ struct common_object_header *coh; ++ struct wrap_bin_file *file; ++ unsigned long offset; ++ ++ TRACE2("%p", handle); ++ coh = handle; ++ if (coh->type != OBJECT_TYPE_FILE) { ++ ERROR("handle %p is invalid: %d", handle, coh->type); ++ EXIT2(return STATUS_FAILURE); ++ } ++ fo = HANDLE_TO_OBJECT(coh); ++ file = fo->wrap_bin_file; ++ TRACE2("file: %zu, %u", file->size, length); ++ spin_lock_bh(&ntoskernel_lock); ++ if (byte_offset) ++ offset = *byte_offset; ++ else ++ offset = fo->current_byte_offset; ++ if (length + offset > file->size) { ++ WARNING("%lu, %zu", length + offset, file->size); ++ /* TODO: implement writing past end of current size */ ++ iosb->status = STATUS_FAILURE; ++ iosb->info = 0; ++ } else { ++ memcpy(file->data + offset, buffer, length); ++ iosb->status = STATUS_SUCCESS; ++ iosb->info = length; ++ fo->current_byte_offset = offset + length; ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ EXIT2(return iosb->status); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwClose,1) ++ (void *handle) ++{ ++ struct common_object_header *coh; ++ ++ TRACE2("%p", handle); ++ if (handle == NULL) { ++ TRACE1(""); ++ EXIT2(return STATUS_SUCCESS); ++ } ++ coh = handle; ++ if (coh->type == OBJECT_TYPE_FILE) { ++ struct file_object *fo; ++ struct wrap_bin_file *bin_file; ++ typeof(fo->flags) flags; ++ ++ fo = HANDLE_TO_OBJECT(handle); ++ flags = fo->flags; ++ bin_file = fo->wrap_bin_file; ++ if (dereference_object(fo)) { ++ if (flags == FILE_CREATED) { ++ vfree(bin_file->data); ++ kfree(bin_file); ++ } else ++ free_bin_file(bin_file); ++ } ++ } else if (coh->type == OBJECT_TYPE_NT_THREAD) { ++ struct nt_thread *thread = HANDLE_TO_OBJECT(handle); ++ TRACE2("thread: %p (%p)", thread, handle); ++ ObDereferenceObject(thread); ++ } else { ++ /* TODO: can we just dereference object here? */ ++ WARNING("closing handle 0x%x not implemented", coh->type); ++ } ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwQueryInformationFile,5) ++ (void *handle, struct io_status_block *iosb, void *info, ++ ULONG length, enum file_info_class class) ++{ ++ struct file_object *fo; ++ struct file_name_info *fni; ++ struct file_std_info *fsi; ++ struct wrap_bin_file *file; ++ struct common_object_header *coh; ++ ++ ENTER2("%p", handle); ++ coh = handle; ++ if (coh->type != OBJECT_TYPE_FILE) { ++ ERROR("handle %p is invalid: %d", coh, coh->type); ++ EXIT2(return STATUS_FAILURE); ++ } ++ fo = HANDLE_TO_OBJECT(handle); ++ TRACE2("fo: %p, %d", fo, class); ++ switch (class) { ++ case FileNameInformation: ++ fni = info; ++ fni->length = min(length, (typeof(length))coh->name.length); ++ memcpy(fni->name, coh->name.buf, fni->length); ++ iosb->status = STATUS_SUCCESS; ++ iosb->info = fni->length; ++ break; ++ case FileStandardInformation: ++ fsi = info; ++ file = fo->wrap_bin_file; ++ fsi->alloc_size = file->size; ++ fsi->eof = file->size; ++ fsi->num_links = 1; ++ fsi->delete_pending = FALSE; ++ fsi->dir = FALSE; ++ iosb->status = STATUS_SUCCESS; ++ iosb->info = 0; ++ break; ++ default: ++ WARNING("type %d not implemented yet", class); ++ iosb->status = STATUS_FAILURE; ++ iosb->info = 0; ++ break; ++ } ++ EXIT2(return iosb->status); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwOpenSection,3) ++ (void **handle, ACCESS_MASK access, struct object_attributes *obj_attrs) ++{ ++ INFO("%p, 0x%x, %d", obj_attrs, obj_attrs->attributes, access); ++ TODO(); ++ *handle = obj_attrs; ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwMapViewOfSection,10) ++ (void *secn_handle, void *process_handle, void **base_address, ++ ULONG zero_bits, SIZE_T commit_size, LARGE_INTEGER *secn_offset, ++ SIZE_T *view_size, enum section_inherit inherit, ULONG alloc_type, ++ ULONG protect) ++{ ++ INFO("%p, %p, %p", secn_handle, process_handle, base_address); ++ TODO(); ++ *base_address = (void *)0xdeadbeef; ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwUnmapViewOfSection,2) ++ (void *process_handle, void *base_address) ++{ ++ INFO("%p, %p", process_handle, base_address); ++ TODO(); ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwCreateKey,7) ++ (void **handle, ACCESS_MASK desired_access, ++ struct object_attributes *attr, ULONG title_index, ++ struct unicode_string *class, ULONG create_options, ++ ULONG *disposition) ++{ ++ struct ansi_string ansi; ++ if (RtlUnicodeStringToAnsiString(&ansi, attr->name, TRUE) == ++ STATUS_SUCCESS) { ++ TRACE1("key: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ *handle = NULL; ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwOpenKey,3) ++ (void **handle, ACCESS_MASK desired_access, ++ struct object_attributes *attr) ++{ ++ struct ansi_string ansi; ++ if (RtlUnicodeStringToAnsiString(&ansi, attr->name, TRUE) == ++ STATUS_SUCCESS) { ++ TRACE1("key: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ *handle = NULL; ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwSetValueKey,6) ++ (void *handle, struct unicode_string *name, ULONG title_index, ++ ULONG type, void *data, ULONG data_size) ++{ ++ struct ansi_string ansi; ++ if (RtlUnicodeStringToAnsiString(&ansi, name, TRUE) == ++ STATUS_SUCCESS) { ++ TRACE1("key: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwQueryValueKey,6) ++ (void *handle, struct unicode_string *name, ++ enum key_value_information_class class, void *info, ++ ULONG length, ULONG *res_length) ++{ ++ struct ansi_string ansi; ++ if (RtlUnicodeStringToAnsiString(&ansi, name, TRUE) == STATUS_SUCCESS) { ++ TRACE1("key: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ TODO(); ++ return STATUS_INVALID_PARAMETER; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwDeleteKey,1) ++ (void *handle) ++{ ++ ENTER2("%p", handle); ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(ZwPowerInformation,5) ++ (INT info_level, void *in_buf, ULONG in_buf_len, void *out_buf, ++ ULONG out_buf_len) ++{ ++ INFO("%d, %u, %u", info_level, in_buf_len, out_buf_len); ++ TODO(); ++ return STATUS_ACCESS_DENIED; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(WmiSystemControl,4) ++ (struct wmilib_context *info, struct device_object *dev_obj, ++ struct irp *irp, void *irp_disposition) ++{ ++ TODO(); ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(WmiCompleteRequest,5) ++ (struct device_object *dev_obj, struct irp *irp, NTSTATUS status, ++ ULONG buffer_used, CCHAR priority_boost) ++{ ++ TODO(); ++ return STATUS_SUCCESS; ++} ++ ++noregparm NTSTATUS WIN_FUNC(WmiTraceMessage,12) ++ (void *tracehandle, ULONG message_flags, ++ void *message_guid, USHORT message_no, ...) ++{ ++ TODO(); ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(WmiQueryTraceInformation,4) ++ (enum trace_information_class trace_info_class, void *trace_info, ++ ULONG *req_length, void *buf) ++{ ++ TODO(); ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++/* this function can't be wstdcall as it takes variable number of args */ ++__attribute__((format(printf, 1, 2))) ++noregparm ULONG WIN_FUNC(DbgPrint,12) ++ (char *format, ...) ++{ ++#if DEBUG >= 1 ++ va_list args; ++ static char buf[100]; ++ ++ va_start(args, format); ++ vsnprintf(buf, sizeof(buf), format, args); ++ printk(KERN_DEBUG "%s (%s): %s", DRIVER_NAME, __func__, buf); ++ va_end(args); ++#endif ++ return STATUS_SUCCESS; ++} ++ ++__attribute__((format(printf, 3, 4))) ++noregparm ULONG WIN_FUNC(DbgPrintEx,12) ++ (ULONG component_id, ULONG severity, char *format, ...) ++{ ++#if DEBUG >= 1 ++ va_list args; ++ static char buf[100]; ++ ++ va_start(args, format); ++ vsnprintf(buf, sizeof(buf), format, args); ++ TRACE1("component_id: %d, severity: %d\n", component_id, severity); ++ printk(KERN_DEBUG "%s (%s): %s", DRIVER_NAME, __func__, buf); ++ va_end(args); ++#endif ++ return STATUS_SUCCESS; ++} ++ ++wstdcall void WIN_FUNC(KeBugCheck,1) ++ (ULONG code) ++{ ++ ERROR("Unrecoverable error reported by the driver"); ++ ERROR("code: 0x%x\n", code); ++ dump_stack(); ++ return; ++} ++ ++wstdcall void WIN_FUNC(KeBugCheckEx,5) ++ (ULONG code, ULONG_PTR param1, ULONG_PTR param2, ++ ULONG_PTR param3, ULONG_PTR param4) ++{ ++ ERROR("Unrecoverable error reported by the driver"); ++ ERROR("code: 0x%x, params: 0x%lx 0x%lx 0x%lx 0x%lx\n", code, param1, ++ param2, param3, param4); ++ dump_stack(); ++ return; ++} ++ ++wstdcall void WIN_FUNC(ExSystemTimeToLocalTime,2) ++ (LARGE_INTEGER *system_time, LARGE_INTEGER *local_time) ++{ ++ *local_time = *system_time; ++} ++ ++wstdcall ULONG WIN_FUNC(ExSetTimerResolution,2) ++ (ULONG time, BOOLEAN set) ++{ ++ /* why a driver should change system wide timer resolution is ++ * beyond me */ ++ return time; ++} ++ ++wstdcall void WIN_FUNC(DbgBreakPoint,0) ++ (void) ++{ ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(_except_handler3,0) ++ (void) ++{ ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(__C_specific_handler,0) ++ (void) ++{ ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(_purecall,0) ++ (void) ++{ ++ TODO(); ++} ++ ++struct worker_init_struct { ++ struct work_struct work; ++ struct completion completion; ++ struct nt_thread *nt_thread; ++}; ++ ++int ntoskernel_init(void) ++{ ++ struct timeval now; ++ ++ spin_lock_init(&dispatcher_lock); ++ spin_lock_init(&ntoskernel_lock); ++ spin_lock_init(&ntos_work_lock); ++ spin_lock_init(&kdpc_list_lock); ++ spin_lock_init(&irp_cancel_lock); ++ InitializeListHead(&wrap_mdl_list); ++ InitializeListHead(&kdpc_list); ++ InitializeListHead(&callback_objects); ++ InitializeListHead(&bus_driver_list); ++ InitializeListHead(&object_list); ++ InitializeListHead(&ntos_work_list); ++ ++ nt_spin_lock_init(&nt_list_lock); ++ ++ INIT_WORK(&kdpc_work, kdpc_worker); ++ INIT_WORK(&ntos_work, ntos_work_worker); ++ wrap_timer_slist.next = NULL; ++ ++ do_gettimeofday(&now); ++ wrap_ticks_to_boot = TICKS_1601_TO_1970; ++ wrap_ticks_to_boot += (u64)now.tv_sec * TICKSPERSEC; ++ wrap_ticks_to_boot += now.tv_usec * 10; ++ wrap_ticks_to_boot -= jiffies * TICKSPERJIFFY; ++ TRACE2("%llu", wrap_ticks_to_boot); ++ ++ cpu_count = num_online_cpus(); ++ ++#ifdef WRAP_PREEMPT ++ do { ++ int cpu; ++ for_each_possible_cpu(cpu) { ++ struct irql_info *info; ++ info = &per_cpu(irql_info, cpu); ++ mutex_init(&(info->lock)); ++ info->task = NULL; ++ info->count = 0; ++#ifdef CONFIG_SMP ++ cpumask_setall(&info->cpus_allowed); ++#endif ++ } ++ } while (0); ++#endif ++ ++ ntos_wq = create_singlethread_workqueue("ntos_wq"); ++ if (!ntos_wq) { ++ WARNING("couldn't create ntos_wq thread"); ++ return -ENOMEM; ++ } ++ TRACE1("ntos_wq: %p", ntos_wq); ++ ++ if (add_bus_driver("PCI") ++#ifdef ENABLE_USB ++ || add_bus_driver("USB") ++#endif ++ ) { ++ ntoskernel_exit(); ++ return -ENOMEM; ++ } ++ mdl_cache = ++ wrap_kmem_cache_create(DRIVER_NAME "_mdl", ++ sizeof(struct wrap_mdl) + MDL_CACHE_SIZE, ++ 0, 0); ++ TRACE2("%p", mdl_cache); ++ if (!mdl_cache) { ++ ERROR("couldn't allocate MDL cache"); ++ ntoskernel_exit(); ++ return -ENOMEM; ++ } ++ ++#if defined(CONFIG_X86_64) ++ memset(&kuser_shared_data, 0, sizeof(kuser_shared_data)); ++ *((ULONG64 *)&kuser_shared_data.system_time) = ticks_1601(); ++ init_timer(&shared_data_timer); ++ shared_data_timer.function = update_user_shared_data_proc; ++ shared_data_timer.data = 0; ++#endif ++ return 0; ++} ++ ++int ntoskernel_init_device(struct wrap_device *wd) ++{ ++#if defined(CONFIG_X86_64) ++ if (kuser_shared_data.reserved1) ++ mod_timer(&shared_data_timer, jiffies + MSEC_TO_HZ(30)); ++#endif ++ return 0; ++} ++ ++void ntoskernel_exit_device(struct wrap_device *wd) ++{ ++ ENTER2(""); ++ ++ KeFlushQueuedDpcs(); ++ EXIT2(return); ++} ++ ++void ntoskernel_exit(void) ++{ ++ struct nt_list *cur; ++ ++ ENTER2(""); ++ ++ /* free kernel (Ke) timers */ ++ TRACE2("freeing timers"); ++ while (1) { ++ struct wrap_timer *wrap_timer; ++ struct nt_slist *slist; ++ ++ spin_lock_bh(&ntoskernel_lock); ++ if ((slist = wrap_timer_slist.next)) ++ wrap_timer_slist.next = slist->next; ++ spin_unlock_bh(&ntoskernel_lock); ++ TIMERTRACE("%p", slist); ++ if (!slist) ++ break; ++ wrap_timer = container_of(slist, struct wrap_timer, slist); ++ if (del_timer_sync(&wrap_timer->timer)) ++ WARNING("Buggy Windows driver left timer %p running", ++ wrap_timer->nt_timer); ++ memset(wrap_timer, 0, sizeof(*wrap_timer)); ++ slack_kfree(wrap_timer); ++ } ++ ++ TRACE2("freeing MDLs"); ++ if (mdl_cache) { ++ spin_lock_bh(&ntoskernel_lock); ++ if (!IsListEmpty(&wrap_mdl_list)) ++ ERROR("Windows driver didn't free all MDLs; " ++ "freeing them now"); ++ while ((cur = RemoveHeadList(&wrap_mdl_list))) { ++ struct wrap_mdl *wrap_mdl; ++ wrap_mdl = container_of(cur, struct wrap_mdl, list); ++ if (wrap_mdl->mdl->flags & MDL_CACHE_ALLOCATED) ++ kmem_cache_free(mdl_cache, wrap_mdl); ++ else ++ kfree(wrap_mdl); ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ kmem_cache_destroy(mdl_cache); ++ mdl_cache = NULL; ++ } ++ ++ TRACE2("freeing callbacks"); ++ spin_lock_bh(&ntoskernel_lock); ++ while ((cur = RemoveHeadList(&callback_objects))) { ++ struct callback_object *object; ++ struct nt_list *ent; ++ object = container_of(cur, struct callback_object, list); ++ while ((ent = RemoveHeadList(&object->callback_funcs))) { ++ struct callback_func *f; ++ f = container_of(ent, struct callback_func, list); ++ kfree(f); ++ } ++ kfree(object); ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ ++ spin_lock_bh(&ntoskernel_lock); ++ while ((cur = RemoveHeadList(&bus_driver_list))) { ++ struct bus_driver *bus_driver; ++ bus_driver = container_of(cur, struct bus_driver, list); ++ /* TODO: make sure all all drivers are shutdown/removed */ ++ kfree(bus_driver); ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ ++#if defined(CONFIG_X86_64) ++ del_timer_sync(&shared_data_timer); ++#endif ++ if (ntos_wq) ++ destroy_workqueue(ntos_wq); ++ ENTER2("freeing objects"); ++ spin_lock_bh(&ntoskernel_lock); ++ while ((cur = RemoveHeadList(&object_list))) { ++ struct common_object_header *hdr; ++ hdr = container_of(cur, struct common_object_header, list); ++ if (hdr->type == OBJECT_TYPE_NT_THREAD) ++ TRACE1("object %p(%d) was not freed, freeing it now", ++ HEADER_TO_OBJECT(hdr), hdr->type); ++ else ++ WARNING("object %p(%d) was not freed, freeing it now", ++ HEADER_TO_OBJECT(hdr), hdr->type); ++ ExFreePool(hdr); ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ ++ EXIT2(return); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.h +--- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,1083 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _NTOSKERNEL_H_ ++#define _NTOSKERNEL_H_ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if !defined(CONFIG_X86) && !defined(CONFIG_X86_64) ++#error "this module is for x86 or x86_64 architectures only" ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) ++#define gfp_t unsigned int __nocast ++ ++static inline void *_kzalloc(size_t size, gfp_t flags) ++{ ++ void *p = kmalloc(size, flags); ++ if (likely(p != NULL)) ++ memset(p, 0, size); ++ return p; ++} ++ ++#define kzalloc(size, flags) _kzalloc(size, flags) ++#endif ++ ++/* Interrupt backwards compatibility stuff */ ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) ++#ifndef IRQ_HANDLED ++#define IRQ_HANDLED ++#define IRQ_NONE ++#define irqreturn_t void ++#endif ++#endif /* Linux < 2.6.29 */ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) ++#ifndef mutex_init ++#define mutex semaphore ++#define mutex_init(m) sema_init(m, 1) ++#define mutex_lock(m) down(m) ++#define mutex_trylock(m) (!down_trylock(m)) ++#define mutex_unlock(m) up(m) ++#define mutex_is_locked(m) (atomic_read(m.count) == 0) ++#endif ++#endif /* Linux < 2.6.16 */ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) ++#define set_cpus_allowed_ptr(task, mask) set_cpus_allowed(task, *mask) ++#endif /* Linux < 2.6.26 */ ++ ++#ifdef CONFIG_SMP ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++#define cpumask_copy(dst, src) do { *dst = *src; } while (0) ++#define cpumask_equal(mask1, mask2) cpus_equal(*mask1, *mask2) ++#define cpumask_setall(mask) cpus_setall(*mask) ++static cpumask_t cpumasks[NR_CPUS]; ++#define cpumask_of(cpu) \ ++({ \ ++ cpumasks[cpu] = cpumask_of_cpu(cpu); \ ++ &cpumasks[cpu]; \ ++}) ++#endif /* Linux < 2.6.28 */ ++#endif /* CONFIG_SMP */ ++ ++#ifndef tsk_cpus_allowed ++#define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed) ++#endif ++ ++#ifndef __packed ++#define __packed __attribute__((packed)) ++#endif ++ ++/* pci functions in 2.6 kernels have problems allocating dma buffers, ++ * but seem to work fine with dma functions ++ */ ++#include ++ ++#define PCI_DMA_ALLOC_COHERENT(pci_dev,size,dma_handle) \ ++ dma_alloc_coherent(&pci_dev->dev,size,dma_handle, \ ++ GFP_KERNEL | __GFP_REPEAT) ++#define PCI_DMA_FREE_COHERENT(pci_dev,size,cpu_addr,dma_handle) \ ++ dma_free_coherent(&pci_dev->dev,size,cpu_addr,dma_handle) ++#define PCI_DMA_MAP_SINGLE(pci_dev,addr,size,direction) \ ++ dma_map_single(&pci_dev->dev,addr,size,direction) ++#define PCI_DMA_UNMAP_SINGLE(pci_dev,dma_handle,size,direction) \ ++ dma_unmap_single(&pci_dev->dev,dma_handle,size,direction) ++#define MAP_SG(pci_dev, sglist, nents, direction) \ ++ dma_map_sg(&pci_dev->dev, sglist, nents, direction) ++#define UNMAP_SG(pci_dev, sglist, nents, direction) \ ++ dma_unmap_sg(&pci_dev->dev, sglist, nents, direction) ++#define PCI_DMA_MAP_ERROR(dma_addr) dma_mapping_error(dma_addr) ++ ++ ++#if defined(CONFIG_NET_RADIO) && !defined(CONFIG_WIRELESS_EXT) ++#define CONFIG_WIRELESS_EXT ++#endif ++ ++#define prepare_wait_condition(task, var, value) \ ++do { \ ++ var = value; \ ++ task = current; \ ++ barrier(); \ ++} while (0) ++ ++/* Wait in wait_state (e.g., TASK_INTERRUPTIBLE) for condition to ++ * become true; timeout is either jiffies (> 0) to wait or 0 to wait ++ * forever. ++ * When timeout == 0, return value is ++ * > 0 if condition becomes true, or ++ * < 0 if signal is pending on the thread. ++ * When timeout > 0, return value is ++ * > 0 if condition becomes true before timeout, ++ * < 0 if signal is pending on the thread before timeout, or ++ * 0 if timedout (condition may have become true at the same time) ++ */ ++ ++#define wait_condition(condition, timeout, wait_state) \ ++({ \ ++ long ret = timeout ? timeout : 1; \ ++ while (1) { \ ++ if (signal_pending(current)) { \ ++ ret = -ERESTARTSYS; \ ++ break; \ ++ } \ ++ set_current_state(wait_state); \ ++ if (condition) { \ ++ __set_current_state(TASK_RUNNING); \ ++ break; \ ++ } \ ++ if (timeout) { \ ++ ret = schedule_timeout(ret); \ ++ if (!ret) \ ++ break; \ ++ } else \ ++ schedule(); \ ++ } \ ++ ret; \ ++}) ++ ++#ifdef WRAP_WQ ++ ++struct wrap_workqueue_struct; ++ ++struct wrap_work_struct { ++ struct list_head list; ++ void (*func)(struct wrap_work_struct *data); ++ void *data; ++ /* whether/on which thread scheduled */ ++ struct workqueue_thread *thread; ++}; ++ ++#define work_struct wrap_work_struct ++#define workqueue_struct wrap_workqueue_struct ++ ++#undef INIT_WORK ++#define INIT_WORK(work, pfunc) \ ++ do { \ ++ (work)->func = (pfunc); \ ++ (work)->data = (work); \ ++ (work)->thread = NULL; \ ++ } while (0) ++ ++#undef create_singlethread_workqueue ++#define create_singlethread_workqueue(wq) wrap_create_wq(wq, 1, 0) ++#undef create_workqueue ++#define create_workqueue(wq) wrap_create_wq(wq, 0, 0) ++#undef destroy_workqueue ++#define destroy_workqueue(wq) wrap_destroy_wq(wq) ++#undef queue_work ++#define queue_work(wq, work) wrap_queue_work(wq, work) ++#undef flush_workqueue ++#define flush_workqueue(wq) wrap_flush_wq(wq) ++ ++struct workqueue_struct *wrap_create_wq(const char *name, u8 singlethread, ++ u8 freeze); ++void wrap_destroy_wq(struct workqueue_struct *workq); ++int wrap_queue_work(struct workqueue_struct *workq, struct work_struct *work); ++void wrap_cancel_work(struct work_struct *work); ++void wrap_flush_wq(struct workqueue_struct *workq); ++ ++#else // WRAP_WQ ++ ++/* Compatibility for Linux before 2.6.20 where INIT_WORK takes 3 arguments */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && \ ++ !defined(INIT_WORK_NAR) && \ ++ !defined(INIT_DELAYED_WORK_DEFERRABLE) ++typedef void (*compat_work_func_t)(void *work); ++typedef void (*work_func_t)(struct work_struct *work); ++static inline void (INIT_WORK)(struct work_struct *work, work_func_t func) ++{ ++ INIT_WORK(work, (compat_work_func_t)func, work); ++} ++#undef INIT_WORK ++#endif ++ ++#endif // WRAP_WQ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18) ++#define ISR_PT_REGS_PARAM_DECL ++#else ++#define ISR_PT_REGS_PARAM_DECL , struct pt_regs *regs ++#endif ++ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) ++#define for_each_possible_cpu(_cpu) for_each_cpu(_cpu) ++#endif ++ ++#ifndef CHECKSUM_PARTIAL ++#define CHECKSUM_PARTIAL CHECKSUM_HW ++#endif ++ ++#ifndef IRQF_SHARED ++#define IRQF_SHARED SA_SHIRQ ++#endif ++ ++#ifndef UMH_WAIT_PROC ++#define UMH_WAIT_PROC 1 ++#endif ++ ++#define memcpy_skb(skb, from, length) \ ++ memcpy(skb_put(skb, length), from, length) ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ++#ifndef DMA_BIT_MASK ++#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) ++#endif ++#endif ++ ++#ifndef __GFP_DMA32 ++#define __GFP_DMA32 GFP_DMA ++#endif ++ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) ++#define wrap_kmem_cache_create(name, size, align, flags) \ ++ kmem_cache_create(name, size, align, flags, NULL, NULL) ++#else ++#define wrap_kmem_cache_create(name, size, align, flags) \ ++ kmem_cache_create(name, size, align, flags, NULL) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) ++#define netdev_mc_count(dev) ((dev)->mc_count) ++#define usb_alloc_coherent(dev, size, mem_flags, dma) (usb_buffer_alloc((dev), (size), (mem_flags), (dma))) ++#define usb_free_coherent(dev, size, addr, dma) (usb_buffer_free((dev), (size), (addr), (dma))) ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) ++#define daemonize(name, ...) do {} while (0) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) ++#define add_taint(flag, lockdep_ok) add_taint(flag) ++#endif ++ ++#include "winnt_types.h" ++#include "ndiswrapper.h" ++#include "pe_linker.h" ++#include "wrapmem.h" ++#include "lin2win.h" ++#include "loader.h" ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++static inline void netif_tx_lock(struct net_device *dev) ++{ ++ spin_lock(&dev->xmit_lock); ++} ++static inline void netif_tx_unlock(struct net_device *dev) ++{ ++ spin_unlock(&dev->xmit_lock); ++} ++static inline void netif_tx_lock_bh(struct net_device *dev) ++{ ++ spin_lock_bh(&dev->xmit_lock); ++} ++static inline void netif_tx_unlock_bh(struct net_device *dev) ++{ ++ spin_unlock_bh(&dev->xmit_lock); ++} ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++static inline void netif_poll_enable(struct net_device *dev) ++{ ++} ++static inline void netif_poll_disable(struct net_device *dev) ++{ ++} ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++#define proc_net_root init_net.proc_net ++#else ++#define proc_net_root proc_net ++#endif ++ ++#if ((LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) && \ ++ (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0))) || \ ++ (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,42)) ++#ifndef skb_frag_page ++#define skb_frag_page(frag) ((frag)->page) ++#endif ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) ++#define netdev_notifier_info_to_dev(x) ((struct net_device *)(x)) ++#endif ++ ++#ifdef INIT_COMPLETION ++static inline void reinit_completion(struct completion *x) ++{ ++ INIT_COMPLETION(*x); ++} ++#endif ++ ++/* TICK is 100ns */ ++#define TICKSPERSEC 10000000 ++#define TICKSPERMSEC 10000 ++#define SECSPERDAY 86400 ++#define TICKSPERJIFFY ((TICKSPERSEC + HZ - 1) / HZ) ++ ++#define int_div_round(x, y) (((x) + (y - 1)) / (y)) ++ ++/* 1601 to 1970 is 369 years plus 89 leap days */ ++#define SECS_1601_TO_1970 ((369 * 365 + 89) * (u64)SECSPERDAY) ++#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC) ++ ++/* 100ns units to HZ; if sys_time is negative, relative to current ++ * clock, otherwise from year 1601 */ ++#define SYSTEM_TIME_TO_HZ(sys_time) \ ++ (((sys_time) <= 0) ? \ ++ int_div_round(((u64)HZ * (-(sys_time))), TICKSPERSEC) : \ ++ int_div_round(((s64)HZ * ((sys_time) - ticks_1601())), TICKSPERSEC)) ++ ++#define MSEC_TO_HZ(ms) int_div_round((ms * HZ), 1000) ++#define USEC_TO_HZ(us) int_div_round((us * HZ), 1000000) ++ ++extern u64 wrap_ticks_to_boot; ++ ++static inline u64 ticks_1601(void) ++{ ++ return wrap_ticks_to_boot + (u64)jiffies * TICKSPERJIFFY; ++} ++ ++typedef void (*generic_func)(void); ++ ++struct wrap_export { ++ const char *name; ++ generic_func func; ++}; ++ ++#ifdef CONFIG_X86_64 ++ ++#define WIN_SYMBOL(name, argc) \ ++ {#name, (generic_func) win2lin_ ## name ## _ ## argc} ++#define WIN_WIN_SYMBOL(name, argc) \ ++ {#name, (generic_func) win2lin__win_ ## name ## _ ## argc} ++#define WIN_FUNC_DECL(name, argc) \ ++ extern typeof(name) win2lin_ ## name ## _ ## argc; ++#define WIN_FUNC_PTR(name, argc) win2lin_ ## name ## _ ## argc ++ ++#else ++ ++#define WIN_SYMBOL(name, argc) {#name, (generic_func)name} ++#define WIN_WIN_SYMBOL(name, argc) {#name, (generic_func)_win_ ## name} ++#define WIN_FUNC_DECL(name, argc) ++#define WIN_FUNC_PTR(name, argc) name ++ ++#endif ++ ++#define WIN_FUNC(name, argc) (name) ++/* map name s to f - if f is different from s */ ++#define WIN_SYMBOL_MAP(s, f) ++ ++#define POOL_TAG(A, B, C, D) \ ++ ((ULONG)((A) + ((B) << 8) + ((C) << 16) + ((D) << 24))) ++ ++struct pe_image { ++ char name[MAX_DRIVER_NAME_LEN]; ++ UINT (*entry)(struct driver_object *, struct unicode_string *) wstdcall; ++ void *image; ++ int size; ++ int type; ++ ++ IMAGE_NT_HEADERS *nt_hdr; ++ IMAGE_OPTIONAL_HEADER *opt_hdr; ++}; ++ ++struct ndis_mp_block; ++ ++struct wrap_timer { ++ struct nt_slist slist; ++ struct timer_list timer; ++ struct nt_timer *nt_timer; ++ long repeat; ++#ifdef TIMER_DEBUG ++ unsigned long wrap_timer_magic; ++#endif ++}; ++ ++struct ntos_work_item { ++ struct nt_list list; ++ void *arg1; ++ void *arg2; ++ NTOS_WORK_FUNC func; ++}; ++ ++struct wrap_device_setting { ++ struct nt_list list; ++ char name[MAX_SETTING_NAME_LEN]; ++ char value[MAX_SETTING_VALUE_LEN]; ++ void *encoded; ++}; ++ ++struct wrap_bin_file { ++ char name[MAX_DRIVER_NAME_LEN]; ++ size_t size; ++ void *data; ++}; ++ ++#define WRAP_DRIVER_CLIENT_ID 1 ++ ++struct wrap_driver { ++ struct nt_list list; ++ struct driver_object *drv_obj; ++ char name[MAX_DRIVER_NAME_LEN]; ++ char version[MAX_SETTING_VALUE_LEN]; ++ unsigned short num_pe_images; ++ struct pe_image pe_images[MAX_DRIVER_PE_IMAGES]; ++ unsigned short num_bin_files; ++ struct wrap_bin_file *bin_files; ++ struct nt_list settings; ++ int dev_type; ++ struct ndis_driver *ndis_driver; ++}; ++ ++enum hw_status { ++ HW_INITIALIZED = 1, HW_SUSPENDED, HW_HALTED, HW_DISABLED, ++}; ++ ++struct wrap_device { ++ /* first part is (de)initialized once by loader */ ++ struct nt_list list; ++ int dev_bus; ++ int vendor; ++ int device; ++ int subvendor; ++ int subdevice; ++ char conf_file_name[MAX_DRIVER_NAME_LEN]; ++ char driver_name[MAX_DRIVER_NAME_LEN]; ++ struct wrap_driver *driver; ++ struct nt_list settings; ++ ++ /* rest should be (de)initialized when a device is ++ * (un)plugged */ ++ struct cm_resource_list *resource_list; ++ unsigned long hw_status; ++ struct device_object *pdo; ++ union { ++ struct { ++ struct pci_dev *pdev; ++ enum device_power_state wake_state; ++ } pci; ++ struct { ++ struct usb_device *udev; ++ struct usb_interface *intf; ++ int num_alloc_urbs; ++ struct nt_list wrap_urb_list; ++ } usb; ++ }; ++}; ++ ++#define wrap_is_pci_bus(dev_bus) \ ++ (WRAP_BUS(dev_bus) == WRAP_PCI_BUS || \ ++ WRAP_BUS(dev_bus) == WRAP_PCMCIA_BUS) ++#ifdef ENABLE_USB ++/* earlier versions of ndiswrapper used 0 as USB_BUS */ ++#define wrap_is_usb_bus(dev_bus) \ ++ (WRAP_BUS(dev_bus) == WRAP_USB_BUS || \ ++ WRAP_BUS(dev_bus) == WRAP_INTERNAL_BUS) ++#else ++#define wrap_is_usb_bus(dev_bus) 0 ++#endif ++#define wrap_is_bluetooth_device(dev_bus) \ ++ (WRAP_DEVICE(dev_bus) == WRAP_BLUETOOTH_DEVICE1 || \ ++ WRAP_DEVICE(dev_bus) == WRAP_BLUETOOTH_DEVICE2) ++ ++extern struct workqueue_struct *ntos_wq; ++extern struct workqueue_struct *ndis_wq; ++extern struct workqueue_struct *wrapndis_wq; ++ ++#define atomic_unary_op(var, size, oper) \ ++do { \ ++ if (size == 1) \ ++ __asm__ __volatile__( \ ++ LOCK_PREFIX oper "b %b0\n\t" : "+m" (var)); \ ++ else if (size == 2) \ ++ __asm__ __volatile__( \ ++ LOCK_PREFIX oper "w %w0\n\t" : "+m" (var)); \ ++ else if (size == 4) \ ++ __asm__ __volatile__( \ ++ LOCK_PREFIX oper "l %0\n\t" : "+m" (var)); \ ++ else if (size == 8) \ ++ __asm__ __volatile__( \ ++ LOCK_PREFIX oper "q %q0\n\t" : "+m" (var)); \ ++ else { \ ++ extern void _invalid_op_size_(void); \ ++ _invalid_op_size_(); \ ++ } \ ++} while (0) ++ ++#define atomic_inc_var_size(var, size) atomic_unary_op(var, size, "inc") ++ ++#define atomic_inc_var(var) atomic_inc_var_size(var, sizeof(var)) ++ ++#define atomic_dec_var_size(var, size) atomic_unary_op(var, size, "dec") ++ ++#define atomic_dec_var(var) atomic_dec_var_size(var, sizeof(var)) ++ ++#define pre_atomic_add(var, i) \ ++({ \ ++ typeof(var) pre; \ ++ __asm__ __volatile__( \ ++ LOCK_PREFIX "xadd %0, %1\n\t" \ ++ : "=r"(pre), "+m"(var) \ ++ : "0"(i)); \ ++ pre; \ ++}) ++ ++#define post_atomic_add(var, i) (pre_atomic_add(var, i) + i) ++ ++//#define DEBUG_IRQL 1 ++ ++#ifdef DEBUG_IRQL ++#define assert_irql(cond) \ ++do { \ ++ KIRQL _irql_ = current_irql(); \ ++ if (!(cond)) { \ ++ WARNING("assertion '%s' failed: %d", #cond, _irql_); \ ++ DBG_BLOCK(4) { \ ++ dump_stack(); \ ++ } \ ++ } \ ++} while (0) ++#else ++#define assert_irql(cond) do { } while (0) ++#endif ++ ++/* When preempt is enabled, we should preempt_disable to raise IRQL to ++ * DISPATCH_LEVEL, to be consistent with the semantics. However, using ++ * a mutex instead, so that only ndiswrapper threads run one at a time ++ * on a processor when at DISPATCH_LEVEL seems to be enough. So that ++ * is what we will use until we learn otherwise. If ++ * preempt_(en|dis)able is required for some reason, comment out ++ * following #define. */ ++ ++#define WRAP_PREEMPT 1 ++ ++#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PREEMPT_RT) ++#ifndef WRAP_PREEMPT ++#define WRAP_PREEMPT 1 ++#endif ++#endif ++ ++//#undef WRAP_PREEMPT ++ ++#ifdef WRAP_PREEMPT ++ ++struct irql_info { ++ int count; ++ struct mutex lock; ++#ifdef CONFIG_SMP ++ cpumask_t cpus_allowed; ++#endif ++ struct task_struct *task; ++}; ++ ++DECLARE_PER_CPU(struct irql_info, irql_info); ++ ++static inline KIRQL raise_irql(KIRQL newirql) ++{ ++ struct irql_info *info; ++ ++ assert(newirql == DISPATCH_LEVEL); ++ info = &get_cpu_var(irql_info); ++ if (info->task == current) { ++ assert(info->count > 0); ++ assert(mutex_is_locked(&info->lock)); ++#if defined(CONFIG_SMP) && DEBUG >= 1 ++ assert(cpumask_equal(tsk_cpus_allowed(current), ++ cpumask_of(smp_processor_id()))); ++#endif ++ info->count++; ++ put_cpu_var(irql_info); ++ return DISPATCH_LEVEL; ++ } ++ /* TODO: is this enough to pin down to current cpu? */ ++#ifdef CONFIG_SMP ++ assert(task_cpu(current) == smp_processor_id()); ++ cpumask_copy(&info->cpus_allowed, tsk_cpus_allowed(current)); ++ set_cpus_allowed_ptr(current, cpumask_of(smp_processor_id())); ++#endif ++ put_cpu_var(irql_info); ++ mutex_lock(&info->lock); ++ assert(info->count == 0); ++ assert(info->task == NULL); ++ info->count = 1; ++ info->task = current; ++ return PASSIVE_LEVEL; ++} ++ ++static inline void lower_irql(KIRQL oldirql) ++{ ++ struct irql_info *info; ++ ++ assert(oldirql <= DISPATCH_LEVEL); ++ info = &get_cpu_var(irql_info); ++ assert(info->task == current); ++ assert(mutex_is_locked(&info->lock)); ++ assert(info->count > 0); ++ if (--info->count == 0) { ++ info->task = NULL; ++#ifdef CONFIG_SMP ++ set_cpus_allowed_ptr(current, &info->cpus_allowed); ++#endif ++ mutex_unlock(&info->lock); ++ } ++ put_cpu_var(irql_info); ++} ++ ++static inline KIRQL current_irql(void) ++{ ++ int count; ++ if (in_irq() || irqs_disabled()) ++ EXIT4(return DIRQL); ++ if (in_atomic() || in_interrupt()) ++ EXIT4(return SOFT_IRQL); ++ count = get_cpu_var(irql_info).count; ++ put_cpu_var(irql_info); ++ if (count) ++ EXIT6(return DISPATCH_LEVEL); ++ else ++ EXIT6(return PASSIVE_LEVEL); ++} ++ ++#else ++ ++static inline KIRQL current_irql(void) ++{ ++ if (in_irq() || irqs_disabled()) ++ EXIT4(return DIRQL); ++ if (in_interrupt()) ++ EXIT4(return SOFT_IRQL); ++ if (in_atomic()) ++ EXIT6(return DISPATCH_LEVEL); ++ else ++ EXIT6(return PASSIVE_LEVEL); ++} ++ ++static inline KIRQL raise_irql(KIRQL newirql) ++{ ++ KIRQL ret = in_atomic() ? DISPATCH_LEVEL : PASSIVE_LEVEL; ++ assert(newirql == DISPATCH_LEVEL); ++ assert(current_irql() <= DISPATCH_LEVEL); ++ preempt_disable(); ++ return ret; ++} ++ ++static inline void lower_irql(KIRQL oldirql) ++{ ++ assert(current_irql() == DISPATCH_LEVEL); ++ preempt_enable(); ++} ++ ++#endif ++ ++#define irql_gfp() (in_atomic() ? GFP_ATOMIC : GFP_KERNEL) ++ ++/* Windows spinlocks are of type ULONG_PTR which is not big enough to ++ * store Linux spinlocks; so we implement Windows spinlocks using ++ * ULONG_PTR space with our own functions/macros */ ++ ++/* Windows seems to use 0 for unlocked state of spinlock - if Linux ++ * convention of 1 for unlocked state is used, at least prism54 driver ++ * crashes */ ++ ++#define NT_SPIN_LOCK_UNLOCKED 0 ++#define NT_SPIN_LOCK_LOCKED 1 ++ ++static inline void nt_spin_lock_init(NT_SPIN_LOCK *lock) ++{ ++ *lock = NT_SPIN_LOCK_UNLOCKED; ++} ++ ++#ifdef CONFIG_SMP ++ ++static inline void nt_spin_lock(NT_SPIN_LOCK *lock) ++{ ++ while (1) { ++ unsigned long lockval = xchg(lock, NT_SPIN_LOCK_LOCKED); ++ ++ if (likely(lockval == NT_SPIN_LOCK_UNLOCKED)) ++ break; ++ if (unlikely(lockval > NT_SPIN_LOCK_LOCKED)) { ++ ERROR("bad spinlock: 0x%lx at %p", lockval, lock); ++ return; ++ } ++ /* "rep; nop" doesn't change cx register, it's a "pause" */ ++ __asm__ __volatile__("rep; nop"); ++ } ++} ++ ++static inline void nt_spin_unlock(NT_SPIN_LOCK *lock) ++{ ++ unsigned long lockval = xchg(lock, NT_SPIN_LOCK_UNLOCKED); ++ ++ if (likely(lockval == NT_SPIN_LOCK_LOCKED)) ++ return; ++ WARNING("unlocking unlocked spinlock: 0x%lx at %p", lockval, lock); ++} ++ ++#else // CONFIG_SMP ++ ++#define nt_spin_lock(lock) do { } while (0) ++ ++#define nt_spin_unlock(lock) do { } while (0) ++ ++#endif // CONFIG_SMP ++ ++/* When kernel would've disabled preempt (e.g., in interrupt ++ * handlers), we need to fake preempt so driver thinks it is running ++ * at right IRQL */ ++ ++/* raise IRQL to given (higher) IRQL if necessary before locking */ ++static inline KIRQL nt_spin_lock_irql(NT_SPIN_LOCK *lock, KIRQL newirql) ++{ ++ KIRQL oldirql = raise_irql(newirql); ++ nt_spin_lock(lock); ++ return oldirql; ++} ++ ++/* lower IRQL to given (lower) IRQL if necessary after unlocking */ ++static inline void nt_spin_unlock_irql(NT_SPIN_LOCK *lock, KIRQL oldirql) ++{ ++ nt_spin_unlock(lock); ++ lower_irql(oldirql); ++} ++ ++#define nt_spin_lock_irqsave(lock, flags) \ ++do { \ ++ local_irq_save(flags); \ ++ preempt_disable(); \ ++ nt_spin_lock(lock); \ ++} while (0) ++ ++#define nt_spin_unlock_irqrestore(lock, flags) \ ++do { \ ++ nt_spin_unlock(lock); \ ++ preempt_enable_no_resched(); \ ++ local_irq_restore(flags); \ ++ preempt_check_resched(); \ ++} while (0) ++ ++static inline ULONG SPAN_PAGES(void *ptr, SIZE_T length) ++{ ++ return PAGE_ALIGN(((unsigned long)ptr & (PAGE_SIZE - 1)) + length) ++ >> PAGE_SHIFT; ++} ++ ++#ifdef CONFIG_X86_64 ++ ++/* TODO: can these be implemented without using spinlock? */ ++ ++static inline struct nt_slist *PushEntrySList(nt_slist_header *head, ++ struct nt_slist *entry, ++ NT_SPIN_LOCK *lock) ++{ ++ KIRQL irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL); ++ entry->next = head->next; ++ head->next = entry; ++ head->depth++; ++ nt_spin_unlock_irql(lock, irql); ++ TRACE4("%p, %p, %p", head, entry, entry->next); ++ return entry->next; ++} ++ ++static inline struct nt_slist *PopEntrySList(nt_slist_header *head, ++ NT_SPIN_LOCK *lock) ++{ ++ struct nt_slist *entry; ++ KIRQL irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL); ++ entry = head->next; ++ if (entry) { ++ head->next = entry->next; ++ head->depth--; ++ } ++ nt_spin_unlock_irql(lock, irql); ++ TRACE4("%p, %p", head, entry); ++ return entry; ++} ++ ++#else ++ ++#define u64_low_32(x) ((u32)x) ++#define u64_high_32(x) ((u32)(x >> 32)) ++ ++static inline u64 nt_cmpxchg8b(volatile u64 *ptr, u64 old, u64 new) ++{ ++ u64 prev; ++ ++ __asm__ __volatile__( ++ "\n" ++ LOCK_PREFIX "cmpxchg8b %0\n" ++ : "+m" (*ptr), "=A" (prev) ++ : "A" (old), "b" (u64_low_32(new)), "c" (u64_high_32(new))); ++ return prev; ++} ++ ++/* slist routines below update slist atomically - no need for ++ * spinlocks */ ++ ++static inline struct nt_slist *PushEntrySList(nt_slist_header *head, ++ struct nt_slist *entry, ++ NT_SPIN_LOCK *lock) ++{ ++ nt_slist_header old, new; ++ do { ++ old.align = head->align; ++ entry->next = old.next; ++ new.next = entry; ++ new.depth = old.depth + 1; ++ } while (nt_cmpxchg8b(&head->align, old.align, new.align) != old.align); ++ TRACE4("%p, %p, %p", head, entry, old.next); ++ return old.next; ++} ++ ++static inline struct nt_slist *PopEntrySList(nt_slist_header *head, ++ NT_SPIN_LOCK *lock) ++{ ++ struct nt_slist *entry; ++ nt_slist_header old, new; ++ do { ++ old.align = head->align; ++ entry = old.next; ++ if (!entry) ++ break; ++ new.next = entry->next; ++ new.depth = old.depth - 1; ++ } while (nt_cmpxchg8b(&head->align, old.align, new.align) != old.align); ++ TRACE4("%p, %p", head, entry); ++ return entry; ++} ++ ++#endif ++ ++#define sleep_hz(n) \ ++do { \ ++ set_current_state(TASK_INTERRUPTIBLE); \ ++ schedule_timeout(n); \ ++} while (0) ++ ++int ntoskernel_init(void); ++void ntoskernel_exit(void); ++int ntoskernel_init_device(struct wrap_device *wd); ++void ntoskernel_exit_device(struct wrap_device *wd); ++void *allocate_object(ULONG size, enum common_object_type type, ++ struct unicode_string *name); ++ ++#ifdef ENABLE_USB ++int usb_init(void); ++void usb_exit(void); ++#else ++static inline int usb_init(void) { return 0; } ++static inline void usb_exit(void) {} ++#endif ++int usb_init_device(struct wrap_device *wd); ++void usb_exit_device(struct wrap_device *wd); ++ ++int wrap_procfs_init(void); ++void wrap_procfs_remove(void); ++ ++int link_pe_images(struct pe_image *pe_image, unsigned short n); ++ ++int stricmp(const char *s1, const char *s2); ++void dump_bytes(const char *name, const u8 *from, int len); ++struct mdl *allocate_init_mdl(void *virt, ULONG length); ++void free_mdl(struct mdl *mdl); ++struct driver_object *find_bus_driver(const char *name); ++void free_custom_extensions(struct driver_extension *drv_obj_ext); ++struct nt_thread *get_current_nt_thread(void); ++u64 ticks_1601(void); ++int schedule_ntos_work_item(NTOS_WORK_FUNC func, void *arg1, void *arg2); ++void wrap_init_timer(struct nt_timer *nt_timer, enum timer_type type, ++ struct ndis_mp_block *nmb); ++BOOLEAN wrap_set_timer(struct nt_timer *nt_timer, unsigned long expires_hz, ++ unsigned long repeat_hz, struct kdpc *kdpc); ++ ++LONG InterlockedDecrement(LONG volatile *val) wfastcall; ++LONG InterlockedIncrement(LONG volatile *val) wfastcall; ++struct nt_list *ExInterlockedInsertHeadList ++ (struct nt_list *head, struct nt_list *entry, ++ NT_SPIN_LOCK *lock) wfastcall; ++struct nt_list *ExInterlockedInsertTailList ++ (struct nt_list *head, struct nt_list *entry, ++ NT_SPIN_LOCK *lock) wfastcall; ++struct nt_list *ExInterlockedRemoveHeadList ++ (struct nt_list *head, NT_SPIN_LOCK *lock) wfastcall; ++NTSTATUS IofCallDriver(struct device_object *dev_obj, struct irp *irp) wfastcall; ++KIRQL KfRaiseIrql(KIRQL newirql) wfastcall; ++void KfLowerIrql(KIRQL oldirql) wfastcall; ++KIRQL KfAcquireSpinLock(NT_SPIN_LOCK *lock) wfastcall; ++void KfReleaseSpinLock(NT_SPIN_LOCK *lock, KIRQL oldirql) wfastcall; ++void IofCompleteRequest(struct irp *irp, CHAR prio_boost) wfastcall; ++void KefReleaseSpinLockFromDpcLevel(NT_SPIN_LOCK *lock) wfastcall; ++ ++LONG ObfReferenceObject(void *object) wfastcall; ++void ObfDereferenceObject(void *object) wfastcall; ++ ++#define ObReferenceObject(object) ObfReferenceObject(object) ++#define ObDereferenceObject(object) ObfDereferenceObject(object) ++ ++/* prevent expansion of ExAllocatePoolWithTag macro */ ++void *(ExAllocatePoolWithTag)(enum pool_type pool_type, SIZE_T size, ++ ULONG tag) wstdcall; ++ ++void ExFreePool(void *p) wstdcall; ++ULONG MmSizeOfMdl(void *base, ULONG length) wstdcall; ++void __iomem *MmMapIoSpace(PHYSICAL_ADDRESS phys_addr, SIZE_T size, ++ enum memory_caching_type cache) wstdcall; ++void MmUnmapIoSpace(void __iomem *addr, SIZE_T size) wstdcall; ++void MmProbeAndLockPages(struct mdl *mdl, KPROCESSOR_MODE access_mode, ++ enum lock_operation operation) wstdcall; ++void MmUnlockPages(struct mdl *mdl) wstdcall; ++void KeInitializeEvent(struct nt_event *nt_event, ++ enum event_type type, BOOLEAN state) wstdcall; ++LONG KeSetEvent(struct nt_event *nt_event, KPRIORITY incr, ++ BOOLEAN wait) wstdcall; ++LONG KeResetEvent(struct nt_event *nt_event) wstdcall; ++BOOLEAN queue_kdpc(struct kdpc *kdpc); ++BOOLEAN dequeue_kdpc(struct kdpc *kdpc); ++ ++NTSTATUS IoConnectInterrupt(struct kinterrupt **kinterrupt, ++ PKSERVICE_ROUTINE service_routine, ++ void *service_context, NT_SPIN_LOCK *lock, ++ ULONG vector, KIRQL irql, KIRQL synch_irql, ++ enum kinterrupt_mode interrupt_mode, ++ BOOLEAN shareable, KAFFINITY processor_enable_mask, ++ BOOLEAN floating_save) wstdcall; ++void IoDisconnectInterrupt(struct kinterrupt *interrupt) wstdcall; ++BOOLEAN KeSynchronizeExecution(struct kinterrupt *interrupt, ++ PKSYNCHRONIZE_ROUTINE synch_routine, ++ void *ctx) wstdcall; ++ ++NTSTATUS KeWaitForSingleObject(void *object, KWAIT_REASON reason, ++ KPROCESSOR_MODE waitmode, BOOLEAN alertable, ++ LARGE_INTEGER *timeout) wstdcall; ++void MmBuildMdlForNonPagedPool(struct mdl *mdl) wstdcall; ++NTSTATUS IoCreateDevice(struct driver_object *driver, ULONG dev_ext_length, ++ struct unicode_string *dev_name, DEVICE_TYPE dev_type, ++ ULONG dev_chars, BOOLEAN exclusive, ++ struct device_object **dev_obj) wstdcall; ++NTSTATUS IoCreateSymbolicLink(struct unicode_string *link, ++ struct unicode_string *dev_name) wstdcall; ++void IoDeleteDevice(struct device_object *dev) wstdcall; ++void IoDetachDevice(struct device_object *topdev) wstdcall; ++struct device_object *IoGetAttachedDevice(struct device_object *dev) wstdcall; ++struct device_object *IoGetAttachedDeviceReference ++ (struct device_object *dev) wstdcall; ++NTSTATUS IoAllocateDriverObjectExtension ++ (struct driver_object *drv_obj, void *client_id, ULONG extlen, ++ void **ext) wstdcall; ++void *IoGetDriverObjectExtension(struct driver_object *drv, ++ void *client_id) wstdcall; ++struct device_object *IoAttachDeviceToDeviceStack ++ (struct device_object *src, struct device_object *dst) wstdcall; ++BOOLEAN IoCancelIrp(struct irp *irp) wstdcall; ++struct irp *IoBuildSynchronousFsdRequest ++ (ULONG major_func, struct device_object *dev_obj, void *buf, ++ ULONG length, LARGE_INTEGER *offset, struct nt_event *event, ++ struct io_status_block *status) wstdcall; ++ ++NTSTATUS IoPassIrpDown(struct device_object *dev_obj, struct irp *irp) wstdcall; ++WIN_FUNC_DECL(IoPassIrpDown,2); ++NTSTATUS IoSyncForwardIrp(struct device_object *dev_obj, ++ struct irp *irp) wstdcall; ++NTSTATUS IoAsyncForwardIrp(struct device_object *dev_obj, ++ struct irp *irp) wstdcall; ++NTSTATUS IoInvalidDeviceRequest(struct device_object *dev_obj, ++ struct irp *irp) wstdcall; ++ ++void KeInitializeSpinLock(NT_SPIN_LOCK *lock) wstdcall; ++void IoAcquireCancelSpinLock(KIRQL *irql) wstdcall; ++void IoReleaseCancelSpinLock(KIRQL irql) wstdcall; ++ ++NTSTATUS RtlUnicodeStringToAnsiString ++ (struct ansi_string *dst, const struct unicode_string *src, ++ BOOLEAN dup) wstdcall; ++NTSTATUS RtlAnsiStringToUnicodeString ++ (struct unicode_string *dst, const struct ansi_string *src, ++ BOOLEAN dup) wstdcall; ++void RtlInitAnsiString(struct ansi_string *dst, const char *src) wstdcall; ++void RtlInitUnicodeString(struct unicode_string *dest, ++ const wchar_t *src) wstdcall; ++void RtlFreeUnicodeString(struct unicode_string *string) wstdcall; ++void RtlFreeAnsiString(struct ansi_string *string) wstdcall; ++LONG RtlCompareUnicodeString(const struct unicode_string *s1, ++ const struct unicode_string *s2, ++ BOOLEAN case_insensitive) wstdcall; ++NTSTATUS RtlUpcaseUnicodeString(struct unicode_string *dst, ++ struct unicode_string *src, ++ BOOLEAN alloc) wstdcall; ++BOOLEAN KeCancelTimer(struct nt_timer *nt_timer) wstdcall; ++void KeInitializeDpc(struct kdpc *kdpc, void *func, void *ctx) wstdcall; ++ ++extern spinlock_t ntoskernel_lock; ++extern spinlock_t irp_cancel_lock; ++extern struct nt_list object_list; ++extern CCHAR cpu_count; ++#ifdef CONFIG_X86_64 ++extern struct kuser_shared_data kuser_shared_data; ++#endif ++ ++#define IoCompleteRequest(irp, prio) IofCompleteRequest(irp, prio) ++#define IoCallDriver(dev, irp) IofCallDriver(dev, irp) ++ ++#if defined(IO_DEBUG) ++#define DUMP_IRP(_irp) \ ++do { \ ++ struct io_stack_location *_irp_sl; \ ++ _irp_sl = IoGetCurrentIrpStackLocation(_irp); \ ++ IOTRACE("irp: %p, stack size: %d, cl: %d, sl: %p, dev_obj: %p, " \ ++ "mj_fn: %d, minor_fn: %d, nt_urb: %p, event: %p", \ ++ _irp, _irp->stack_count, (_irp)->current_location, \ ++ _irp_sl, _irp_sl->dev_obj, _irp_sl->major_fn, \ ++ _irp_sl->minor_fn, IRP_URB(_irp), \ ++ (_irp)->user_event); \ ++} while (0) ++#else ++#define DUMP_IRP(_irp) do { } while (0) ++#endif ++ ++#endif // _NTOSKERNEL_H_ +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel_io.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel_io.c +--- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel_io.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel_io.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,1161 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ntoskernel.h" ++#include "ndis.h" ++#include "wrapndis.h" ++#include "usb.h" ++#include "loader.h" ++#include "ntoskernel_io_exports.h" ++ ++wstdcall void WIN_FUNC(IoAcquireCancelSpinLock,1) ++ (KIRQL *irql) __acquires(irql) ++{ ++ spin_lock_bh(&irp_cancel_lock); ++ *irql = 0; ++} ++ ++wstdcall void WIN_FUNC(IoReleaseCancelSpinLock,1) ++ (KIRQL irql) __releases(irql) ++{ ++ spin_unlock_bh(&irp_cancel_lock); ++} ++ ++wstdcall int WIN_FUNC(IoIsWdmVersionAvailable,2) ++ (UCHAR major, UCHAR minor) ++{ ++ IOENTER("%d, %x", major, minor); ++ if (major == 1 && ++ (minor == 0x30 || // Windows 2003 ++ minor == 0x20 || // Windows XP ++ minor == 0x10)) // Windows 2000 ++ IOEXIT(return TRUE); ++ IOEXIT(return FALSE); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(IoIs32bitProcess,1) ++ (struct irp *irp) ++{ ++#ifdef CONFIG_X86_64 ++ return FALSE; ++#else ++ return TRUE; ++#endif ++} ++ ++wstdcall void WIN_FUNC(IoInitializeIrp,3) ++ (struct irp *irp, USHORT size, CCHAR stack_count) ++{ ++ IOENTER("irp: %p, %d, %d", irp, size, stack_count); ++ ++ memset(irp, 0, size); ++ irp->size = size; ++ irp->stack_count = stack_count; ++ irp->current_location = stack_count; ++ IoGetCurrentIrpStackLocation(irp) = IRP_SL(irp, stack_count); ++ IOEXIT(return); ++} ++ ++wstdcall void WIN_FUNC(IoReuseIrp,2) ++ (struct irp *irp, NTSTATUS status) ++{ ++ IOENTER("%p, %d", irp, status); ++ if (irp) { ++ UCHAR alloc_flags; ++ ++ alloc_flags = irp->alloc_flags; ++ IoInitializeIrp(irp, irp->size, irp->stack_count); ++ irp->alloc_flags = alloc_flags; ++ irp->io_status.status = status; ++ } ++ IOEXIT(return); ++} ++ ++wstdcall struct irp *WIN_FUNC(IoAllocateIrp,2) ++ (char stack_count, BOOLEAN charge_quota) ++{ ++ struct irp *irp; ++ int irp_size; ++ ++ IOENTER("count: %d", stack_count); ++ stack_count++; ++ irp_size = IoSizeOfIrp(stack_count); ++ irp = kmalloc(irp_size, irql_gfp()); ++ if (irp) ++ IoInitializeIrp(irp, irp_size, stack_count); ++ IOTRACE("irp %p", irp); ++ IOEXIT(return irp); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(IoCancelIrp,1) ++ (struct irp *irp) ++{ ++ typeof(irp->cancel_routine) cancel_routine; ++ ++ /* NB: this function may be called at DISPATCH_LEVEL */ ++ IOTRACE("irp: %p", irp); ++ if (!irp) ++ return FALSE; ++ DUMP_IRP(irp); ++ IoAcquireCancelSpinLock(&irp->cancel_irql); ++ cancel_routine = xchg(&irp->cancel_routine, NULL); ++ IOTRACE("%p", cancel_routine); ++ irp->cancel = TRUE; ++ if (cancel_routine) { ++ struct device_object *dev_obj; ++ ++ if (irp->current_location >= 0 && ++ irp->current_location < irp->stack_count) ++ dev_obj = IoGetCurrentIrpStackLocation(irp)->dev_obj; ++ else ++ dev_obj = NULL; ++ IOTRACE("current_location: %d, dev_obj: %p", ++ irp->current_location, dev_obj); ++ /* cancel_routine will release the spin lock */ ++ __release(irp->cancel_irql); ++ LIN2WIN2(cancel_routine, dev_obj, irp); ++ /* in usb's cancel, irp->cancel is set to indicate ++ * status of cancel */ ++ IOEXIT(return xchg(&irp->cancel, TRUE)); ++ } else { ++ IOTRACE("irp %p already canceled", irp); ++ IoReleaseCancelSpinLock(irp->cancel_irql); ++ IOEXIT(return FALSE); ++ } ++} ++ ++wstdcall void IoQueueThreadIrp(struct irp *irp) ++{ ++ struct nt_thread *thread; ++ KIRQL irql; ++ ++ thread = get_current_nt_thread(); ++ if (thread) { ++ IOTRACE("thread: %p, task: %p", thread, thread->task); ++ irp->flags |= IRP_SYNCHRONOUS_API; ++ irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL); ++ InsertTailList(&thread->irps, &irp->thread_list); ++ IoIrpThread(irp) = thread; ++ nt_spin_unlock_irql(&thread->lock, irql); ++ } else ++ IoIrpThread(irp) = NULL; ++} ++ ++wstdcall void IoDequeueThreadIrp(struct irp *irp) ++{ ++ struct nt_thread *thread; ++ KIRQL irql; ++ ++ thread = IoIrpThread(irp); ++ if (thread) { ++ irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL); ++ RemoveEntryList(&irp->thread_list); ++ nt_spin_unlock_irql(&thread->lock, irql); ++ } ++} ++ ++wstdcall void WIN_FUNC(IoFreeIrp,1) ++ (struct irp *irp) ++{ ++ IOENTER("irp = %p", irp); ++ if (!irp) { ++ WARNING("irp is NULL"); ++ return; ++ } ++ ++ if (irp->flags & IRP_SYNCHRONOUS_API) ++ IoDequeueThreadIrp(irp); ++ IoCancelIrp(irp); ++ kfree(irp); ++ ++ IOEXIT(return); ++} ++ ++wstdcall struct irp *WIN_FUNC(IoBuildAsynchronousFsdRequest,6) ++ (ULONG major_fn, struct device_object *dev_obj, void *buffer, ++ ULONG length, LARGE_INTEGER *offset, ++ struct io_status_block *user_status) ++{ ++ struct irp *irp; ++ struct io_stack_location *irp_sl; ++ ++ IOENTER("%p", dev_obj); ++ if (!dev_obj) ++ IOEXIT(return NULL); ++ irp = IoAllocateIrp(dev_obj->stack_count, FALSE); ++ if (irp == NULL) { ++ WARNING("couldn't allocate irp"); ++ IOEXIT(return NULL); ++ } ++ ++ irp_sl = IoGetNextIrpStackLocation(irp); ++ irp_sl->major_fn = major_fn; ++ IOTRACE("major_fn: %d", major_fn); ++ irp_sl->minor_fn = 0; ++ irp_sl->flags = 0; ++ irp_sl->control = 0; ++ irp_sl->dev_obj = dev_obj; ++ irp_sl->file_obj = NULL; ++ irp_sl->completion_routine = NULL; ++ ++ if (dev_obj->flags & DO_DIRECT_IO) { ++ irp->mdl = IoAllocateMdl(buffer, length, FALSE, FALSE, irp); ++ if (irp->mdl == NULL) { ++ IoFreeIrp(irp); ++ return NULL; ++ } ++ MmProbeAndLockPages(irp->mdl, KernelMode, ++ major_fn == IRP_MJ_WRITE ? ++ IoReadAccess : IoWriteAccess); ++ IOTRACE("mdl: %p", irp->mdl); ++ } else if (dev_obj->flags & DO_BUFFERED_IO) { ++ irp->associated_irp.system_buffer = buffer; ++ irp->flags = IRP_BUFFERED_IO; ++ irp->mdl = NULL; ++ IOTRACE("buffer: %p", buffer); ++ } ++ if (major_fn == IRP_MJ_READ) { ++ irp_sl->params.read.length = length; ++ irp_sl->params.read.byte_offset = *offset; ++ } else if (major_fn == IRP_MJ_WRITE) { ++ irp_sl->params.write.length = length; ++ irp_sl->params.write.byte_offset = *offset; ++ } ++ irp->user_status = user_status; ++ IOTRACE("irp: %p", irp); ++ return irp; ++} ++ ++wstdcall struct irp *WIN_FUNC(IoBuildSynchronousFsdRequest,7) ++ (ULONG major_fn, struct device_object *dev_obj, void *buf, ++ ULONG length, LARGE_INTEGER *offset, struct nt_event *event, ++ struct io_status_block *user_status) ++{ ++ struct irp *irp; ++ ++ irp = IoBuildAsynchronousFsdRequest(major_fn, dev_obj, buf, length, ++ offset, user_status); ++ if (irp == NULL) ++ return NULL; ++ irp->user_event = event; ++ IoQueueThreadIrp(irp); ++ return irp; ++} ++ ++wstdcall struct irp *WIN_FUNC(IoBuildDeviceIoControlRequest,9) ++ (ULONG ioctl, struct device_object *dev_obj, ++ void *input_buf, ULONG input_buf_len, void *output_buf, ++ ULONG output_buf_len, BOOLEAN internal_ioctl, ++ struct nt_event *event, struct io_status_block *io_status) ++{ ++ struct irp *irp; ++ struct io_stack_location *irp_sl; ++ ULONG buf_len; ++ ++ IOENTER("%p, 0x%08x, %d", dev_obj, ioctl, internal_ioctl); ++ if (!dev_obj) ++ IOEXIT(return NULL); ++ irp = IoAllocateIrp(dev_obj->stack_count, FALSE); ++ if (irp == NULL) { ++ WARNING("couldn't allocate irp"); ++ return NULL; ++ } ++ irp_sl = IoGetNextIrpStackLocation(irp); ++ irp_sl->params.dev_ioctl.code = ioctl; ++ irp_sl->params.dev_ioctl.input_buf_len = input_buf_len; ++ irp_sl->params.dev_ioctl.output_buf_len = output_buf_len; ++ irp_sl->major_fn = (internal_ioctl) ? ++ IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL; ++ IOTRACE("%d", IO_METHOD_FROM_CTL_CODE(ioctl)); ++ ++ switch (IO_METHOD_FROM_CTL_CODE(ioctl)) { ++ case METHOD_BUFFERED: ++ buf_len = max(input_buf_len, output_buf_len); ++ if (buf_len) { ++ irp->associated_irp.system_buffer = ++ ExAllocatePoolWithTag(NonPagedPool, buf_len, 0); ++ if (!irp->associated_irp.system_buffer) { ++ IoFreeIrp(irp); ++ IOEXIT(return NULL); ++ } ++ irp->associated_irp.system_buffer = input_buf; ++ if (input_buf) ++ memcpy(irp->associated_irp.system_buffer, ++ input_buf, input_buf_len); ++ irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; ++ if (output_buf) ++ irp->flags = IRP_INPUT_OPERATION; ++ irp->user_buf = output_buf; ++ } else ++ irp->user_buf = NULL; ++ break; ++ case METHOD_IN_DIRECT: ++ case METHOD_OUT_DIRECT: ++ if (input_buf) { ++ irp->associated_irp.system_buffer = ++ ExAllocatePoolWithTag(NonPagedPool, ++ input_buf_len, 0); ++ if (!irp->associated_irp.system_buffer) { ++ IoFreeIrp(irp); ++ IOEXIT(return NULL); ++ } ++ memcpy(irp->associated_irp.system_buffer, ++ input_buf, input_buf_len); ++ irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; ++ } ++ /* TODO: we are supposed to setup MDL, but USB layer ++ * doesn't use MDLs. Moreover, USB layer mirrors ++ * non-DMAable buffers, so no need to allocate ++ * DMAable buffer here */ ++ if (output_buf) { ++ irp->associated_irp.system_buffer = ++ ExAllocatePoolWithTag(NonPagedPool, ++ output_buf_len, 0); ++ if (!irp->associated_irp.system_buffer) { ++ IoFreeIrp(irp); ++ IOEXIT(return NULL); ++ } ++ irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; ++ } ++ break; ++ case METHOD_NEITHER: ++ irp->user_buf = output_buf; ++ irp_sl->params.dev_ioctl.type3_input_buf = input_buf; ++ break; ++ } ++ ++ irp->user_status = io_status; ++ irp->user_event = event; ++ IoQueueThreadIrp(irp); ++ ++ IOTRACE("irp: %p", irp); ++ IOEXIT(return irp); ++} ++ ++wfastcall NTSTATUS WIN_FUNC(IofCallDriver,2) ++ (struct device_object *dev_obj, struct irp *irp) ++{ ++ struct io_stack_location *irp_sl; ++ NTSTATUS status; ++ driver_dispatch_t *major_func; ++ struct driver_object *drv_obj; ++ ++ if (irp->current_location <= 0) { ++ ERROR("invalid irp: %p, %d", irp, irp->current_location); ++ return STATUS_INVALID_PARAMETER; ++ } ++ IOTRACE("%p, %p, %p, %d, %d, %p", dev_obj, irp, dev_obj->drv_obj, ++ irp->current_location, irp->stack_count, ++ IoGetCurrentIrpStackLocation(irp)); ++ IoSetNextIrpStackLocation(irp); ++ DUMP_IRP(irp); ++ irp_sl = IoGetCurrentIrpStackLocation(irp); ++ drv_obj = dev_obj->drv_obj; ++ irp_sl->dev_obj = dev_obj; ++ major_func = drv_obj->major_func[irp_sl->major_fn]; ++ IOTRACE("major_func: %p, dev_obj: %p", major_func, dev_obj); ++ if (major_func) ++ status = LIN2WIN2(major_func, dev_obj, irp); ++ else { ++ ERROR("major_function %d is not implemented", ++ irp_sl->major_fn); ++ status = STATUS_NOT_SUPPORTED; ++ } ++ IOEXIT(return status); ++} ++ ++wfastcall void WIN_FUNC(IofCompleteRequest,2) ++ (struct irp *irp, CHAR prio_boost) ++{ ++ struct io_stack_location *irp_sl; ++ ++#ifdef IO_DEBUG ++ DUMP_IRP(irp); ++ if (irp->io_status.status == STATUS_PENDING) { ++ ERROR("invalid irp: %p, STATUS_PENDING", irp); ++ return; ++ } ++ if (irp->current_location < 0 || ++ irp->current_location >= irp->stack_count) { ++ ERROR("invalid irp: %p, %d", irp, irp->current_location); ++ return; ++ } ++#endif ++ for (irp_sl = IoGetCurrentIrpStackLocation(irp); ++ irp->current_location < irp->stack_count; irp_sl++) { ++ struct device_object *dev_obj; ++ NTSTATUS status; ++ ++ DUMP_IRP(irp); ++ if (irp_sl->control & SL_PENDING_RETURNED) ++ irp->pending_returned = TRUE; ++ ++ /* current_location and dev_obj must be same as when ++ * driver called IoSetCompletionRoutine, which sets ++ * completion routine at next (lower) location, which ++ * is what we are going to call below; so we set ++ * current_location and dev_obj for the previous ++ * (higher) location */ ++ IoSkipCurrentIrpStackLocation(irp); ++ if (irp->current_location < irp->stack_count) ++ dev_obj = IoGetCurrentIrpStackLocation(irp)->dev_obj; ++ else ++ dev_obj = NULL; ++ ++ IOTRACE("%d, %d, %p", irp->current_location, irp->stack_count, ++ dev_obj); ++ if (irp_sl->completion_routine && ++ ((irp->io_status.status == STATUS_SUCCESS && ++ irp_sl->control & SL_INVOKE_ON_SUCCESS) || ++ (irp->io_status.status != STATUS_SUCCESS && ++ irp_sl->control & SL_INVOKE_ON_ERROR) || ++ (irp->cancel == TRUE && ++ irp_sl->control & SL_INVOKE_ON_CANCEL))) { ++ IOTRACE("calling completion_routine at: %p, %p", ++ irp_sl->completion_routine, irp_sl->context); ++ status = LIN2WIN3(irp_sl->completion_routine, ++ dev_obj, irp, irp_sl->context); ++ IOTRACE("status: %08X", status); ++ if (status == STATUS_MORE_PROCESSING_REQUIRED) ++ IOEXIT(return); ++ } else { ++ /* propagate pending status to next irp_sl */ ++ if (irp->pending_returned && ++ irp->current_location < irp->stack_count) ++ IoMarkIrpPending(irp); ++ } ++ } ++ ++ if (irp->user_status) { ++ irp->user_status->status = irp->io_status.status; ++ irp->user_status->info = irp->io_status.info; ++ } ++ ++ if (irp->user_event) { ++ IOTRACE("setting event %p", irp->user_event); ++ KeSetEvent(irp->user_event, prio_boost, FALSE); ++ } ++ ++ if (irp->associated_irp.system_buffer && ++ (irp->flags & IRP_DEALLOCATE_BUFFER)) ++ ExFreePool(irp->associated_irp.system_buffer); ++ else { ++ struct mdl *mdl; ++ while ((mdl = irp->mdl)) { ++ irp->mdl = mdl->next; ++ MmUnlockPages(mdl); ++ IoFreeMdl(mdl); ++ } ++ } ++ IOTRACE("freeing irp %p", irp); ++ IoFreeIrp(irp); ++ IOEXIT(return); ++} ++ ++wstdcall NTSTATUS IoPassIrpDown(struct device_object *dev_obj, struct irp *irp) ++{ ++ IoSkipCurrentIrpStackLocation(irp); ++ IOEXIT(return IoCallDriver(dev_obj, irp)); ++} ++ ++wstdcall NTSTATUS IoIrpSyncComplete(struct device_object *dev_obj, ++ struct irp *irp, void *context) ++{ ++ if (irp->pending_returned == TRUE) ++ KeSetEvent(context, IO_NO_INCREMENT, FALSE); ++ IOEXIT(return STATUS_MORE_PROCESSING_REQUIRED); ++} ++WIN_FUNC_DECL(IoIrpSyncComplete,3) ++ ++wstdcall NTSTATUS IoSyncForwardIrp(struct device_object *dev_obj, ++ struct irp *irp) ++{ ++ struct nt_event event; ++ NTSTATUS status; ++ ++ IoCopyCurrentIrpStackLocationToNext(irp); ++ KeInitializeEvent(&event, SynchronizationEvent, FALSE); ++ /* completion function is called as Windows function */ ++ IoSetCompletionRoutine(irp, WIN_FUNC_PTR(IoIrpSyncComplete,3), &event, ++ TRUE, TRUE, TRUE); ++ status = IoCallDriver(dev_obj, irp); ++ IOTRACE("%08X", status); ++ if (status == STATUS_PENDING) { ++ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, ++ NULL); ++ status = irp->io_status.status; ++ } ++ IOTRACE("%08X", status); ++ IOEXIT(return status); ++} ++WIN_FUNC_DECL(IoSyncForwardIrp,2) ++ ++wstdcall NTSTATUS IoAsyncForwardIrp(struct device_object *dev_obj, ++ struct irp *irp) ++{ ++ NTSTATUS status; ++ ++ IoCopyCurrentIrpStackLocationToNext(irp); ++ status = IoCallDriver(dev_obj, irp); ++ IOEXIT(return status); ++} ++WIN_FUNC_DECL(IoAsyncForwardIrp,2) ++ ++wstdcall NTSTATUS IoInvalidDeviceRequest(struct device_object *dev_obj, ++ struct irp *irp) ++{ ++ struct io_stack_location *irp_sl; ++ NTSTATUS status; ++ ++ irp_sl = IoGetCurrentIrpStackLocation(irp); ++ WARNING("%d:%d not implemented", irp_sl->major_fn, irp_sl->minor_fn); ++ irp->io_status.status = STATUS_SUCCESS; ++ irp->io_status.info = 0; ++ status = irp->io_status.status; ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ IOEXIT(return status); ++} ++WIN_FUNC_DECL(IoInvalidDeviceRequest,2) ++ ++static irqreturn_t io_irq_isr(int irq, void *data ISR_PT_REGS_PARAM_DECL) ++{ ++ struct kinterrupt *interrupt = data; ++ BOOLEAN ret; ++ ++#ifdef CONFIG_DEBUG_SHIRQ ++ if (!interrupt->u.enabled) ++ EXIT1(return IRQ_NONE); ++#endif ++ TRACE6("%p", interrupt); ++ nt_spin_lock(interrupt->actual_lock); ++ ret = LIN2WIN2(interrupt->isr, interrupt, interrupt->isr_ctx); ++ nt_spin_unlock(interrupt->actual_lock); ++ if (ret == TRUE) ++ EXIT6(return IRQ_HANDLED); ++ else ++ EXIT6(return IRQ_NONE); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoConnectInterrupt,11) ++ (struct kinterrupt **kinterrupt, PKSERVICE_ROUTINE isr, void *isr_ctx, ++ NT_SPIN_LOCK *lock, ULONG vector, KIRQL irql, KIRQL synch_irql, ++ enum kinterrupt_mode mode, BOOLEAN shared, KAFFINITY cpu_mask, ++ BOOLEAN save_fp) ++{ ++ struct kinterrupt *interrupt; ++ IOENTER(""); ++ interrupt = kzalloc(sizeof(*interrupt), GFP_KERNEL); ++ if (!interrupt) ++ IOEXIT(return STATUS_INSUFFICIENT_RESOURCES); ++ interrupt->vector = vector; ++ interrupt->cpu_mask = cpu_mask; ++ nt_spin_lock_init(&interrupt->lock); ++ if (lock) ++ interrupt->actual_lock = lock; ++ else ++ interrupt->actual_lock = &interrupt->lock; ++ interrupt->shared = shared; ++ interrupt->save_fp = save_fp; ++ interrupt->isr = isr; ++ interrupt->isr_ctx = isr_ctx; ++ InitializeListHead(&interrupt->list); ++ interrupt->irql = irql; ++ interrupt->synch_irql = synch_irql; ++ interrupt->mode = mode; ++ if (request_irq(vector, io_irq_isr, shared ? IRQF_SHARED : 0, ++ DRIVER_NAME, interrupt)) { ++ WARNING("request for irq %d failed", vector); ++ kfree(interrupt); ++ IOEXIT(return STATUS_INSUFFICIENT_RESOURCES); ++ } ++ *kinterrupt = interrupt; ++#ifdef CONFIG_DEBUG_SHIRQ ++ interrupt->u.enabled = 1; ++#endif ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(IoDisconnectInterrupt,1) ++ (struct kinterrupt *interrupt) ++{ ++#ifdef CONFIG_DEBUG_SHIRQ ++ interrupt->u.enabled = 0; ++#endif ++ free_irq(interrupt->vector, interrupt); ++ kfree(interrupt); ++} ++ ++wstdcall struct mdl *WIN_FUNC(IoAllocateMdl,5) ++ (void *virt, ULONG length, BOOLEAN second_buf, BOOLEAN charge_quota, ++ struct irp *irp) ++{ ++ struct mdl *mdl; ++ mdl = allocate_init_mdl(virt, length); ++ if (!mdl) ++ return NULL; ++ if (irp) { ++ if (second_buf == TRUE) { ++ struct mdl *last; ++ ++ last = irp->mdl; ++ while (last->next) ++ last = last->next; ++ last->next = mdl; ++ } else ++ irp->mdl = mdl; ++ } ++ IOTRACE("%p", mdl); ++ return mdl; ++} ++ ++wstdcall void WIN_FUNC(IoFreeMdl,1) ++ (struct mdl *mdl) ++{ ++ IOTRACE("%p", mdl); ++ free_mdl(mdl); ++} ++ ++wstdcall struct io_workitem *WIN_FUNC(IoAllocateWorkItem,1) ++ (struct device_object *dev_obj) ++{ ++ struct io_workitem *io_workitem; ++ ++ IOENTER("%p", dev_obj); ++ io_workitem = kmalloc(sizeof(*io_workitem), irql_gfp()); ++ if (!io_workitem) ++ IOEXIT(return NULL); ++ io_workitem->dev_obj = dev_obj; ++ IOEXIT(return io_workitem); ++} ++ ++wstdcall void WIN_FUNC(IoFreeWorkItem,1) ++ (struct io_workitem *io_workitem) ++{ ++ kfree(io_workitem); ++ IOEXIT(return); ++} ++ ++wstdcall void WIN_FUNC(IoQueueWorkItem,4) ++ (struct io_workitem *io_workitem, void *func, ++ enum work_queue_type queue_type, void *context) ++{ ++ IOENTER("%p, %p", io_workitem, io_workitem->dev_obj); ++ io_workitem->worker_routine = func; ++ io_workitem->context = context; ++ schedule_ntos_work_item(func, io_workitem->dev_obj, context); ++ IOEXIT(return); ++} ++ ++wstdcall void WIN_FUNC(ExQueueWorkItem,2) ++ (struct io_workitem *io_workitem, enum work_queue_type queue_type) ++{ ++ IOENTER("%p", io_workitem); ++ schedule_ntos_work_item(io_workitem->worker_routine, ++ io_workitem->dev_obj, io_workitem->context); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoAllocateDriverObjectExtension,4) ++ (struct driver_object *drv_obj, void *client_id, ULONG extlen, ++ void **ext) ++{ ++ struct custom_ext *ce; ++ ++ IOENTER("%p, %p", drv_obj, client_id); ++ ce = kmalloc(sizeof(*ce) + extlen, irql_gfp()); ++ if (ce == NULL) ++ return STATUS_INSUFFICIENT_RESOURCES; ++ ++ IOTRACE("custom_ext: %p", ce); ++ ce->client_id = client_id; ++ spin_lock_bh(&ntoskernel_lock); ++ InsertTailList(&drv_obj->drv_ext->custom_ext, &ce->list); ++ spin_unlock_bh(&ntoskernel_lock); ++ ++ *ext = (void *)ce + sizeof(*ce); ++ IOTRACE("ext: %p", *ext); ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall void *WIN_FUNC(IoGetDriverObjectExtension,2) ++ (struct driver_object *drv_obj, void *client_id) ++{ ++ struct custom_ext *ce; ++ void *ret; ++ ++ IOENTER("drv_obj: %p, client_id: %p", drv_obj, client_id); ++ ret = NULL; ++ spin_lock_bh(&ntoskernel_lock); ++ nt_list_for_each_entry(ce, &drv_obj->drv_ext->custom_ext, list) { ++ if (ce->client_id == client_id) { ++ ret = (void *)ce + sizeof(*ce); ++ break; ++ } ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ IOTRACE("ret: %p", ret); ++ return ret; ++} ++ ++void free_custom_extensions(struct driver_extension *drv_ext) ++{ ++ struct nt_list *ent; ++ ++ IOENTER("%p", drv_ext); ++ spin_lock_bh(&ntoskernel_lock); ++ while ((ent = RemoveHeadList(&drv_ext->custom_ext))) ++ kfree(ent); ++ spin_unlock_bh(&ntoskernel_lock); ++ IOEXIT(return); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoCreateDevice,7) ++ (struct driver_object *drv_obj, ULONG dev_ext_length, ++ struct unicode_string *dev_name, DEVICE_TYPE dev_type, ++ ULONG dev_chars, BOOLEAN exclusive, struct device_object **newdev) ++{ ++ struct device_object *dev; ++ struct dev_obj_ext *dev_obj_ext; ++ int size; ++ ++ IOENTER("%p, %u, %p", drv_obj, dev_ext_length, dev_name); ++ ++ size = sizeof(*dev) + dev_ext_length + sizeof(*dev_obj_ext); ++ dev = allocate_object(size, OBJECT_TYPE_DEVICE, dev_name); ++ if (!dev) ++ IOEXIT(return STATUS_INSUFFICIENT_RESOURCES); ++ if (dev_ext_length) ++ dev->dev_ext = dev + 1; ++ else ++ dev->dev_ext = NULL; ++ ++ dev_obj_ext = ((void *)(dev + 1)) + dev_ext_length; ++ dev_obj_ext->dev_obj = dev; ++ dev_obj_ext->size = 0; ++ dev_obj_ext->type = IO_TYPE_DEVICE; ++ dev->dev_obj_ext = dev_obj_ext; ++ ++ dev->type = dev_type; ++ dev->flags = 0; ++ dev->size = sizeof(*dev) + dev_ext_length; ++ dev->ref_count = 1; ++ dev->attached = NULL; ++ dev->stack_count = 1; ++ ++ dev->drv_obj = drv_obj; ++ dev->next = drv_obj->dev_obj; ++ drv_obj->dev_obj = dev; ++ ++ dev->align_req = 1; ++ dev->characteristics = dev_chars; ++ dev->io_timer = NULL; ++ KeInitializeEvent(&dev->lock, SynchronizationEvent, TRUE); ++ dev->vpb = NULL; ++ ++ IOTRACE("dev: %p, ext: %p", dev, dev->dev_ext); ++ *newdev = dev; ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoCreateUnprotectedSymbolicLink,2) ++ (struct unicode_string *link, struct unicode_string *dev_name) ++{ ++ struct ansi_string ansi; ++ ++ IOENTER("%p, %p", dev_name, link); ++ if (dev_name && (RtlUnicodeStringToAnsiString(&ansi, dev_name, TRUE) == ++ STATUS_SUCCESS)) { ++ IOTRACE("dev_name: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ if (link && (RtlUnicodeStringToAnsiString(&ansi, link, TRUE) == ++ STATUS_SUCCESS)) { ++ IOTRACE("link: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++// TODO(); ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoCreateSymbolicLink,2) ++ (struct unicode_string *link, struct unicode_string *dev_name) ++{ ++ IOEXIT(return IoCreateUnprotectedSymbolicLink(link, dev_name)); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoDeleteSymbolicLink,1) ++ (struct unicode_string *link) ++{ ++ struct ansi_string ansi; ++ ++ IOENTER("%p", link); ++ if (link && (RtlUnicodeStringToAnsiString(&ansi, link, TRUE) == ++ STATUS_SUCCESS)) { ++ IOTRACE("dev_name: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(IoDeleteDevice,1) ++ (struct device_object *dev) ++{ ++ IOENTER("%p", dev); ++ if (dev == NULL) ++ IOEXIT(return); ++ IOTRACE("drv_obj: %p", dev->drv_obj); ++ if (dev->drv_obj) { ++ struct device_object *prev; ++ ++ prev = dev->drv_obj->dev_obj; ++ IOTRACE("dev_obj: %p", prev); ++ if (prev == dev) ++ dev->drv_obj->dev_obj = dev->next; ++ else if (prev) { ++ while (prev->next != dev) ++ prev = prev->next; ++ prev->next = dev->next; ++ } ++ } ++ ObDereferenceObject(dev); ++ IOEXIT(return); ++} ++ ++wstdcall void WIN_FUNC(IoDetachDevice,1) ++ (struct device_object *tgt) ++{ ++ struct device_object *tail; ++ ++ IOENTER("%p", tgt); ++ if (!tgt) ++ IOEXIT(return); ++ tail = tgt->attached; ++ if (!tail) ++ IOEXIT(return); ++ IOTRACE("tail: %p", tail); ++ ++ spin_lock_bh(&ntoskernel_lock); ++ tgt->attached = tail->attached; ++ IOTRACE("attached:%p", tgt->attached); ++ for ( ; tail; tail = tail->attached) { ++ IOTRACE("tail:%p", tail); ++ tail->stack_count--; ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ IOEXIT(return); ++} ++ ++wstdcall struct device_object *WIN_FUNC(IoGetAttachedDevice,1) ++ (struct device_object *dev) ++{ ++ IOENTER("%p", dev); ++ if (!dev) ++ IOEXIT(return NULL); ++ spin_lock_bh(&ntoskernel_lock); ++ while (dev->attached) ++ dev = dev->attached; ++ spin_unlock_bh(&ntoskernel_lock); ++ IOEXIT(return dev); ++} ++ ++wstdcall struct device_object *WIN_FUNC(IoGetAttachedDeviceReference,1) ++ (struct device_object *dev) ++{ ++ IOENTER("%p", dev); ++ if (!dev) ++ IOEXIT(return NULL); ++ dev = IoGetAttachedDevice(dev); ++ ObReferenceObject(dev); ++ IOEXIT(return dev); ++} ++ ++wstdcall struct device_object *WIN_FUNC(IoAttachDeviceToDeviceStack,2) ++ (struct device_object *src, struct device_object *tgt) ++{ ++ struct device_object *attached; ++ struct dev_obj_ext *src_dev_ext; ++ ++ IOENTER("%p, %p", src, tgt); ++ attached = IoGetAttachedDevice(tgt); ++ IOTRACE("%p", attached); ++ src_dev_ext = src->dev_obj_ext; ++ spin_lock_bh(&ntoskernel_lock); ++ if (attached) ++ attached->attached = src; ++ src->attached = NULL; ++ src->stack_count = attached->stack_count + 1; ++ src_dev_ext->attached_to = attached; ++ spin_unlock_bh(&ntoskernel_lock); ++ IOTRACE("stack_count: %d -> %d", attached->stack_count, ++ src->stack_count); ++ IOEXIT(return attached); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoGetDeviceProperty,5) ++ (struct device_object *pdo, enum device_registry_property dev_property, ++ ULONG buffer_len, void *buffer, ULONG *result_len) ++{ ++ struct ansi_string ansi; ++ struct unicode_string unicode; ++ struct wrap_device *wd; ++ ULONG need; ++ ++ IOENTER("dev_obj = %p, dev_property = %d, buffer_len = %u, " ++ "buffer = %p, result_len = %p", pdo, dev_property, ++ buffer_len, buffer, result_len); ++ ++ wd = pdo->reserved; ++ switch (dev_property) { ++ case DevicePropertyDeviceDescription: ++ case DevicePropertyFriendlyName: ++ case DevicePropertyDriverKeyName: ++ if (wrap_is_pci_bus(wd->dev_bus)) ++ RtlInitAnsiString(&ansi, "PCI"); ++ else // if (wrap_is_usb_bus(wd->dev_bus)) ++ RtlInitAnsiString(&ansi, "USB"); ++ need = sizeof(wchar_t) * (ansi.max_length + 1); ++ if (buffer_len < need) { ++ *result_len = need; ++ IOEXIT(return STATUS_BUFFER_TOO_SMALL); ++ } ++ unicode.max_length = buffer_len; ++ unicode.buf = buffer; ++ if (RtlAnsiStringToUnicodeString(&unicode, &ansi, ++ FALSE) != STATUS_SUCCESS) { ++ *result_len = unicode.length; ++ IOEXIT(return STATUS_BUFFER_TOO_SMALL); ++ } ++ IOEXIT(return STATUS_SUCCESS); ++ default: ++ WARNING("%d not implemented", dev_property); ++ IOEXIT(return STATUS_INVALID_PARAMETER_2); ++ } ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoGetDeviceObjectPointer,4) ++ (struct unicode_string *name, ACCESS_MASK desired_access, ++ struct file_object *file_obj, struct device_object *dev_obj) ++{ ++ struct common_object_header *coh; ++ ++ dev_obj = NULL; ++ /* TODO: access is not checked and file_obj is filled with zeroes */ ++ spin_lock_bh(&ntoskernel_lock); ++ nt_list_for_each_entry(coh, &object_list, list) { ++ TRACE5("header: %p, type: %d", coh, coh->type); ++ if (coh->type != OBJECT_TYPE_DEVICE) ++ continue; ++ if (!RtlCompareUnicodeString(&coh->name, name, TRUE)) { ++ dev_obj = HEADER_TO_OBJECT(coh); ++ TRACE5("dev_obj: %p", dev_obj); ++ break; ++ } ++ } ++ spin_unlock_bh(&ntoskernel_lock); ++ if (dev_obj) { ++ memset(file_obj, 0, sizeof(*file_obj)); ++ WARNING("file_obj filled with zeroes"); ++ IOEXIT(return STATUS_SUCCESS); ++ } else ++ IOEXIT(return STATUS_OBJECT_NAME_INVALID); ++} ++ ++/* NOTE: Make sure to compile with -freg-struct-return, so gcc will ++ * return union in register, like Windows */ ++wstdcall union power_state WIN_FUNC(PoSetPowerState,3) ++ (struct device_object *dev_obj, enum power_state_type type, ++ union power_state state) ++{ ++ IOEXIT(return state); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(PoCallDriver,2) ++ (struct device_object *dev_obj, struct irp *irp) ++{ ++ return IoCallDriver(dev_obj, irp); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(PoRequestPowerIrp,6) ++ (struct device_object *dev_obj, UCHAR minor_fn, ++ union power_state power_state, void *completion_func, ++ void *context, struct irp **pirp) ++{ ++ struct irp *irp; ++ struct io_stack_location *irp_sl; ++ ++ TRACE1("%p, %d, %p", dev_obj, dev_obj->stack_count, dev_obj->drv_obj); ++ irp = IoAllocateIrp(dev_obj->stack_count, FALSE); ++ if (!irp) ++ return STATUS_INSUFFICIENT_RESOURCES; ++ irp_sl = IoGetNextIrpStackLocation(irp); ++ irp_sl->major_fn = IRP_MJ_POWER; ++ irp_sl->minor_fn = minor_fn; ++ if (minor_fn == IRP_MN_WAIT_WAKE) ++ irp_sl->params.power.type = SystemPowerState; ++ else ++ irp_sl->params.power.type = DevicePowerState; ++ irp_sl->params.power.state = power_state; ++ irp_sl->completion_routine = completion_func; ++ irp->io_status.status = STATUS_NOT_SUPPORTED; ++ *pirp = irp; ++ return PoCallDriver(dev_obj, irp); ++} ++ ++wstdcall void WIN_FUNC(PoStartNextPowerIrp,1) ++ (struct irp *irp) ++{ ++ IOENTER("irp = %p", irp); ++ IOEXIT(return); ++} ++ ++wstdcall void WIN_FUNC(IoInitializeRemoveLockEx,5) ++ (struct io_remove_lock *lock, ULONG alloc_tag, ULONG max_locked_min, ++ ULONG high_mark, ULONG lock_size) ++{ ++ TODO(); ++} ++ ++wstdcall void *WIN_FUNC(IoAllocateErrorLogEntry,2) ++ (void *io_object, UCHAR entry_size) ++{ ++ /* not implemented fully */ ++ void *ret = kmalloc(sizeof(struct io_error_log_packet) + entry_size, ++ irql_gfp()); ++ TRACE2("%p", ret); ++ if (ret) ++ return ret + sizeof(struct io_error_log_packet); ++ else ++ return NULL; ++} ++ ++wstdcall void WIN_FUNC(IoWriteErrorLogEntry,1) ++ (void *entry) ++{ ++ /* TODO: log error with codes and message */ ++ ERROR(""); ++} ++ ++wstdcall void WIN_FUNC(IoFreeErrorLogEntry,1) ++ (void *entry) ++{ ++ TRACE2("%p", entry); ++ kfree(entry - sizeof(struct io_error_log_packet)); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoAcquireRemoveLockEx,5) ++ (struct io_remove_lock *lock, void *tag, char *file, ULONG line, ++ ULONG lock_size) ++{ ++ TODO(); ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoReleaseRemoveLockEx,3) ++ (struct io_remove_lock *lock, void *tag, ULONG lock_size) ++{ ++ TODO(); ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoRegisterDeviceInterface,4) ++ (struct device_object *pdo, struct guid *guid_class, ++ struct unicode_string *reference, struct unicode_string *link) ++{ ++ struct ansi_string ansi; ++ ++ /* TODO: check if pdo is valid */ ++ RtlInitAnsiString(&ansi, "ndis"); ++ ENTER1("pdo: %p, ref: %p, link: %p, %x, %x, %x", pdo, reference, link, ++ guid_class->data1, guid_class->data2, guid_class->data3); ++ return RtlAnsiStringToUnicodeString(link, &ansi, TRUE); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoSetDeviceInterfaceState,2) ++ (struct unicode_string *link, BOOLEAN enable) ++{ ++ ENTER1("link: %p, enable: %d", link, enable); ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoOpenDeviceRegistryKey,4) ++ (struct device_object *dev_obj, ULONG type, ACCESS_MASK mask, ++ void **handle) ++{ ++ ENTER1("dev_obj: %p", dev_obj); ++ *handle = dev_obj; ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoWMIRegistrationControl,2) ++ (struct device_object *dev_obj, ULONG action) ++{ ++ ENTER2("%p, %d", dev_obj, action); ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(IoInvalidateDeviceRelations,2) ++ (struct device_object *dev_obj, enum device_relation_type type) ++{ ++ INFO("%p, %d", dev_obj, type); ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(IoInvalidateDeviceState,1) ++ (struct device_object *pdo) ++{ ++ INFO("%p", pdo); ++ TODO(); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoRegisterPlugPlayNotification,7) ++ (UINT category, ULONG flags, void *data, struct driver_object *object, ++ void *callback, void *context, void **notification_entry) ++{ ++ TRACE2("category: %d, flags 0x%x, data: %p, object: %p, callback: %p, " ++ "context: %p, notification_entry: %p", category, flags, data, ++ object, callback, context, notification_entry); ++ TODO(); ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoUnregisterPlugPlayNotification,1) ++ (void *notification_entry) ++{ ++ TRACE2("%p", notification_entry); ++ TODO(); ++ IOEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoWMIOpenBlock,3) ++ (struct guid *guid, ULONG access, void *object) ++{ ++ TODO(); ++ IOEXIT(return STATUS_NOT_IMPLEMENTED); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(IoWMIQueryAllData,3) ++ (void *data_block_object, UINT *buffer_size, void *buffer) ++{ ++ TODO(); ++ IOEXIT(return STATUS_NOT_IMPLEMENTED); ++} ++ ++wstdcall void WIN_FUNC(IoGetStackLimits,2) ++ (ULONG_PTR *LowLimit, ULONG_PTR *HighLimit) ++{ ++ *LowLimit = (ULONG_PTR)&LowLimit & ~(THREAD_SIZE - 1); ++ *HighLimit = *LowLimit + THREAD_SIZE; ++ IOTRACE("LowLimit: 0x%lx, HighLimit: 0x%lx", *LowLimit, *HighLimit); ++ IOEXIT(return); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.c +--- linux-3.12.2/3rdparty/ndiswrapper/pe_linker.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,600 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifdef TEST_LOADER ++ ++#include "usr_linker.h" ++ ++#else ++ ++#include ++#include ++ ++//#define DEBUGLINKER 2 ++ ++#include "ntoskernel.h" ++ ++#endif ++ ++struct pe_exports { ++ char *dll; ++ char *name; ++ generic_func addr; ++}; ++ ++static struct pe_exports pe_exports[40]; ++static int num_pe_exports; ++ ++#define RVA2VA(image, rva, type) (type)(ULONG_PTR)((void *)image + rva) ++#define CHECK_SZ(a,b) { if (sizeof(a) != b) { \ ++ ERROR("%s is bad, got %zd, expected %d", \ ++ #a , sizeof(a), (b)); return -EINVAL; } } ++ ++#if defined(DEBUGLINKER) && DEBUGLINKER > 0 ++#define DBGLINKER(fmt, ...) printk(KERN_INFO "%s (%s:%d): " fmt "\n", \ ++ DRIVER_NAME, __func__, \ ++ __LINE__ , ## __VA_ARGS__); ++static const char *image_directory_name[] = { ++ "EXPORT", ++ "IMPORT", ++ "RESOURCE", ++ "EXCEPTION", ++ "SECURITY", ++ "BASERELOC", ++ "DEBUG", ++ "COPYRIGHT", ++ "GLOBALPTR", ++ "TLS", ++ "LOAD_CONFIG", ++ "BOUND_IMPORT", ++ "IAT", ++ "DELAY_IMPORT", ++ "COM_DESCRIPTOR" }; ++#else ++#define DBGLINKER(fmt, ...) do { } while (0) ++#endif ++ ++#ifndef TEST_LOADER ++extern struct wrap_export ntoskernel_exports[], ntoskernel_io_exports[], ++ ndis_exports[], crt_exports[], hal_exports[], rtl_exports[]; ++#ifdef ENABLE_USB ++extern struct wrap_export usb_exports[]; ++#endif ++ ++static int get_export(char *name, generic_func *func) ++{ ++ int i, j; ++ ++ struct wrap_export *exports[] = { ++ ntoskernel_exports, ++ ntoskernel_io_exports, ++ ndis_exports, ++ crt_exports, ++ hal_exports, ++ rtl_exports, ++#ifdef ENABLE_USB ++ usb_exports, ++#endif ++ }; ++ ++ for (j = 0; j < ARRAY_SIZE(exports); j++) ++ for (i = 0; exports[j][i].name != NULL; i++) ++ if (strcmp(exports[j][i].name, name) == 0) { ++ *func = exports[j][i].func; ++ return 0; ++ } ++ ++ for (i = 0; i < num_pe_exports; i++) ++ if (strcmp(pe_exports[i].name, name) == 0) { ++ *func = pe_exports[i].addr; ++ return 0; ++ } ++ ++ return -1; ++} ++#endif // TEST_LOADER ++ ++static void *get_dll_init(char *name) ++{ ++ int i; ++ for (i = 0; i < num_pe_exports; i++) ++ if ((strcmp(pe_exports[i].dll, name) == 0) && ++ (strcmp(pe_exports[i].name, "DllInitialize") == 0)) ++ return (void *)pe_exports[i].addr; ++ return NULL; ++} ++ ++/* ++ * Find and validate the coff header ++ * ++ */ ++static int check_nt_hdr(IMAGE_NT_HEADERS *nt_hdr) ++{ ++ int i; ++ WORD attr; ++ PIMAGE_OPTIONAL_HEADER opt_hdr; ++ ++ /* Validate the "PE\0\0" signature */ ++ if (nt_hdr->Signature != IMAGE_NT_SIGNATURE) { ++ ERROR("is this driver file? bad signature %08x", ++ nt_hdr->Signature); ++ return -EINVAL; ++ } ++ ++ opt_hdr = &nt_hdr->OptionalHeader; ++ /* Make sure Image is PE32 or PE32+ */ ++#ifdef CONFIG_X86_64 ++ if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { ++ ERROR("kernel is 64-bit, but Windows driver is not 64-bit;" ++ "bad magic: %04X", opt_hdr->Magic); ++ return -EINVAL; ++ } ++#else ++ if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { ++ ERROR("kernel is 32-bit, but Windows driver is not 32-bit;" ++ "bad magic: %04X", opt_hdr->Magic); ++ return -EINVAL; ++ } ++#endif ++ ++ /* Validate the image for the current architecture. */ ++#ifdef CONFIG_X86_64 ++ if (nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) { ++ ERROR("kernel is 64-bit, but Windows driver is not 64-bit;" ++ " (PE signature is %04X)", nt_hdr->FileHeader.Machine); ++ return -EINVAL; ++ } ++#else ++ if (nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) { ++ ERROR("kernel is 32-bit, but Windows driver is not 32-bit;" ++ " (PE signature is %04X)", nt_hdr->FileHeader.Machine); ++ return -EINVAL; ++ } ++#endif ++ ++ /* Must have attributes */ ++#ifdef CONFIG_X86_64 ++ attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE; ++#else ++ attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE; ++#endif ++ if ((nt_hdr->FileHeader.Characteristics & attr) != attr) ++ return -EINVAL; ++ ++ /* Must be relocatable */ ++ attr = IMAGE_FILE_RELOCS_STRIPPED; ++ if ((nt_hdr->FileHeader.Characteristics & attr)) ++ return -EINVAL; ++ ++ /* Make sure we have at least one section */ ++ if (nt_hdr->FileHeader.NumberOfSections == 0) ++ return -EINVAL; ++ ++ if (opt_hdr->SectionAlignment < opt_hdr->FileAlignment) { ++ ERROR("alignment mismatch: section: 0x%x, file: 0x%x", ++ opt_hdr->SectionAlignment, opt_hdr->FileAlignment); ++ return -EINVAL; ++ } ++ ++ DBGLINKER("number of datadictionary entries %d", ++ opt_hdr->NumberOfRvaAndSizes); ++ for (i = 0; i < opt_hdr->NumberOfRvaAndSizes; i++) { ++ DBGLINKER("datadirectory %s RVA:%X Size:%d", ++ (i <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) ? ++ image_directory_name[i] : "unknown", ++ opt_hdr->DataDirectory[i].VirtualAddress, ++ opt_hdr->DataDirectory[i].Size); ++ } ++ ++ if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) ++ return IMAGE_FILE_EXECUTABLE_IMAGE; ++ if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_DLL)) ++ return IMAGE_FILE_DLL; ++ return -EINVAL; ++} ++ ++static int import(void *image, IMAGE_IMPORT_DESCRIPTOR *dirent, char *dll) ++{ ++ ULONG_PTR *lookup_tbl, *address_tbl; ++ char *symname = NULL; ++ int i; ++ int ret = 0; ++ generic_func adr; ++ ++ lookup_tbl = RVA2VA(image, dirent->u.OriginalFirstThunk, ULONG_PTR *); ++ address_tbl = RVA2VA(image, dirent->FirstThunk, ULONG_PTR *); ++ ++ for (i = 0; lookup_tbl[i]; i++) { ++ if (IMAGE_SNAP_BY_ORDINAL(lookup_tbl[i])) { ++ ERROR("ordinal import not supported: %llu", ++ (uint64_t)lookup_tbl[i]); ++ return -1; ++ } ++ else { ++ symname = RVA2VA(image, ++ ((lookup_tbl[i] & ++ ~IMAGE_ORDINAL_FLAG) + 2), char *); ++ } ++ ++ if (get_export(symname, &adr) < 0) { ++ ERROR("unknown symbol: %s:'%s'", dll, symname); ++ ret = -1; ++ } else { ++ DBGLINKER("found symbol: %s:%s: addr: %p, rva = %llu", ++ dll, symname, adr, (uint64_t)address_tbl[i]); ++ address_tbl[i] = (ULONG_PTR)adr; ++ } ++ } ++ return ret; ++} ++ ++static int read_exports(struct pe_image *pe) ++{ ++ IMAGE_EXPORT_DIRECTORY *export_dir_table; ++ uint32_t *export_addr_table; ++ int i; ++ uint32_t *name_table; ++ PIMAGE_OPTIONAL_HEADER opt_hdr; ++ IMAGE_DATA_DIRECTORY *export_data_dir; ++ ++ opt_hdr = &pe->nt_hdr->OptionalHeader; ++ export_data_dir = ++ &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; ++ ++ if (export_data_dir->Size == 0) { ++ DBGLINKER("no exports"); ++ return 0; ++ } ++ ++ export_dir_table = ++ RVA2VA(pe->image, export_data_dir->VirtualAddress, ++ IMAGE_EXPORT_DIRECTORY *); ++ ++ name_table = (unsigned int *)(pe->image + ++ export_dir_table->AddressOfNames); ++ export_addr_table = (uint32_t *) ++ (pe->image + export_dir_table->AddressOfFunctions); ++ ++ for (i = 0; i < export_dir_table->NumberOfNames; i++) { ++ ++ if (export_data_dir->VirtualAddress <= *export_addr_table || ++ *export_addr_table >= (export_data_dir->VirtualAddress + ++ export_data_dir->Size)) ++ DBGLINKER("forwarder rva"); ++ ++ DBGLINKER("export symbol: %s, at %p", ++ (char *)(pe->image + *name_table), ++ pe->image + *export_addr_table); ++ ++ pe_exports[num_pe_exports].dll = pe->name; ++ pe_exports[num_pe_exports].name = pe->image + *name_table; ++ pe_exports[num_pe_exports].addr = ++ pe->image + *export_addr_table; ++ ++ num_pe_exports++; ++ name_table++; ++ export_addr_table++; ++ } ++ return 0; ++} ++ ++static int fixup_imports(void *image, IMAGE_NT_HEADERS *nt_hdr) ++{ ++ int i; ++ char *name; ++ int ret = 0; ++ IMAGE_IMPORT_DESCRIPTOR *dirent; ++ IMAGE_DATA_DIRECTORY *import_data_dir; ++ PIMAGE_OPTIONAL_HEADER opt_hdr; ++ ++ opt_hdr = &nt_hdr->OptionalHeader; ++ import_data_dir = ++ &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; ++ dirent = RVA2VA(image, import_data_dir->VirtualAddress, ++ IMAGE_IMPORT_DESCRIPTOR *); ++ ++ for (i = 0; dirent[i].Name; i++) { ++ name = RVA2VA(image, dirent[i].Name, char*); ++ ++ DBGLINKER("imports from dll: %s", name); ++ ret += import(image, &dirent[i], name); ++ } ++ return ret; ++} ++ ++static int fixup_reloc(void *image, IMAGE_NT_HEADERS *nt_hdr) ++{ ++ ULONG_PTR base; ++ ULONG_PTR size; ++ IMAGE_BASE_RELOCATION *fixup_block; ++ IMAGE_DATA_DIRECTORY *base_reloc_data_dir; ++ PIMAGE_OPTIONAL_HEADER opt_hdr; ++ ++ opt_hdr = &nt_hdr->OptionalHeader; ++ base = opt_hdr->ImageBase; ++ base_reloc_data_dir = ++ &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; ++ if (base_reloc_data_dir->Size == 0) ++ return 0; ++ ++ fixup_block = RVA2VA(image, base_reloc_data_dir->VirtualAddress, ++ IMAGE_BASE_RELOCATION *); ++ DBGLINKER("fixup_block=%p, image=%p", fixup_block, image); ++ DBGLINKER("fixup_block info: %x %d", ++ fixup_block->VirtualAddress, fixup_block->SizeOfBlock); ++ ++ while (fixup_block->SizeOfBlock) { ++ int i; ++ WORD fixup, offset; ++ ++ size = (fixup_block->SizeOfBlock - ++ sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); ++ DBGLINKER("found %llu relocations in this block", ++ (uint64_t)size); ++ ++ for (i = 0; i < size; i++) { ++ fixup = fixup_block->TypeOffset[i]; ++ offset = fixup & 0xfff; ++ switch ((fixup >> 12) & 0x0f) { ++ case IMAGE_REL_BASED_ABSOLUTE: ++ break; ++ ++ case IMAGE_REL_BASED_HIGHLOW: { ++ uint32_t addr; ++ uint32_t *loc = ++ RVA2VA(image, ++ fixup_block->VirtualAddress + ++ offset, uint32_t *); ++ addr = RVA2VA(image, (*loc - base), uint32_t); ++ DBGLINKER("relocation: *%p (Val:%X)= %X", ++ loc, *loc, addr); ++ *loc = addr; ++ } ++ break; ++ ++ case IMAGE_REL_BASED_DIR64: { ++ uint64_t addr; ++ uint64_t *loc = ++ RVA2VA(image, ++ fixup_block->VirtualAddress + ++ offset, uint64_t *); ++ addr = RVA2VA(image, (*loc - base), uint64_t); ++ DBGLINKER("relocation: *%p (Val:%llX)= %llx", ++ loc, *loc, addr); ++ *loc = addr; ++ } ++ break; ++ ++ default: ++ ERROR("unknown fixup: %08X", ++ (fixup >> 12) & 0x0f); ++ return -EOPNOTSUPP; ++ break; ++ } ++ } ++ DBGLINKER("finished relocating block"); ++ ++ fixup_block = (IMAGE_BASE_RELOCATION *) ++ ((void *)fixup_block + fixup_block->SizeOfBlock); ++ }; ++ DBGLINKER("done relocating all"); ++ ++ return 0; ++} ++ ++/* Expand the image in memory if necessary. The image on disk does not ++ * necessarily maps the image of the driver in memory, so we have to ++ * re-write it in order to fulfill the sections alignments. The ++ * advantage to do that is that rva_to_va becomes a simple ++ * addition. */ ++static int fix_pe_image(struct pe_image *pe) ++{ ++ void *image; ++ IMAGE_SECTION_HEADER *sect_hdr; ++ int i, sections; ++ int image_size; ++ ++ if (pe->size == pe->opt_hdr->SizeOfImage) { ++ /* Nothing to do */ ++ return 0; ++ } ++ ++ image_size = pe->opt_hdr->SizeOfImage; ++#ifdef CONFIG_X86_64 ++#ifdef PAGE_KERNEL_EXECUTABLE ++ image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, ++ PAGE_KERNEL_EXECUTABLE); ++#elif defined PAGE_KERNEL_EXEC ++ image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, ++ PAGE_KERNEL_EXEC); ++#else ++#error x86_64 should have either PAGE_KERNEL_EXECUTABLE or PAGE_KERNEL_EXEC ++#endif ++#else ++#ifdef cpu_has_nx ++ /* hate to play with kernel macros, but PAGE_KERNEL_EXEC is ++ * not available to modules! */ ++ if (cpu_has_nx) ++ image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, ++ __pgprot(__PAGE_KERNEL & ~_PAGE_NX)); ++ else ++ image = vmalloc(image_size); ++#else ++ image = vmalloc(image_size); ++#endif ++#endif ++ if (image == NULL) { ++ ERROR("failed to allocate enough space for new image:" ++ " %d bytes", image_size); ++ return -ENOMEM; ++ } ++ memset(image, 0, image_size); ++ ++ /* Copy all the headers, ie everything before the first section. */ ++ ++ sections = pe->nt_hdr->FileHeader.NumberOfSections; ++ sect_hdr = IMAGE_FIRST_SECTION(pe->nt_hdr); ++ ++ DBGLINKER("copying headers: %u bytes", sect_hdr->PointerToRawData); ++ ++ memcpy(image, pe->image, sect_hdr->PointerToRawData); ++ ++ /* Copy all the sections */ ++ for (i = 0; i < sections; i++) { ++ DBGLINKER("Copy section %s from %x to %x", ++ sect_hdr->Name, sect_hdr->PointerToRawData, ++ sect_hdr->VirtualAddress); ++ if (sect_hdr->VirtualAddress+sect_hdr->SizeOfRawData > ++ image_size) { ++ ERROR("Invalid section %s in driver", sect_hdr->Name); ++ vfree(image); ++ return -EINVAL; ++ } ++ ++ memcpy(image+sect_hdr->VirtualAddress, ++ pe->image + sect_hdr->PointerToRawData, ++ sect_hdr->SizeOfRawData); ++ sect_hdr++; ++ } ++ ++ vfree(pe->image); ++ pe->image = image; ++ pe->size = image_size; ++ ++ /* Update our internal pointers */ ++ pe->nt_hdr = (IMAGE_NT_HEADERS *) ++ (pe->image + ((IMAGE_DOS_HEADER *)pe->image)->e_lfanew); ++ pe->opt_hdr = &pe->nt_hdr->OptionalHeader; ++ ++ DBGLINKER("set nt headers: nt_hdr=%p, opt_hdr=%p, image=%p", ++ pe->nt_hdr, pe->opt_hdr, pe->image); ++ ++ return 0; ++} ++ ++#if defined(CONFIG_X86_64) ++static void fix_user_shared_data_addr(char *driver, unsigned long length) ++{ ++ unsigned long i, n, max_addr, *addr; ++ ++ TRACE1("fixing KI_USER_SHARED_DATA address in the driver"); ++ n = length - sizeof(unsigned long); ++ max_addr = KI_USER_SHARED_DATA + sizeof(kuser_shared_data); ++ for (i = 0; i < n; i++) { ++ addr = (unsigned long *)(driver + i); ++ if (*addr >= KI_USER_SHARED_DATA && *addr < max_addr) { ++ *addr -= KI_USER_SHARED_DATA; ++ *addr += (unsigned long)&kuser_shared_data; ++ kuser_shared_data.reserved1 = 1; ++ } ++ } ++} ++#endif ++ ++int link_pe_images(struct pe_image *pe_image, unsigned short n) ++{ ++ int i; ++ struct pe_image *pe; ++ ++#if DEBUG >= 1 ++ /* Sanity checks */ ++ CHECK_SZ(IMAGE_SECTION_HEADER, IMAGE_SIZEOF_SECTION_HEADER); ++ CHECK_SZ(IMAGE_FILE_HEADER, IMAGE_SIZEOF_FILE_HEADER); ++ CHECK_SZ(IMAGE_OPTIONAL_HEADER, IMAGE_SIZEOF_NT_OPTIONAL_HEADER); ++ CHECK_SZ(IMAGE_NT_HEADERS, 4 + IMAGE_SIZEOF_FILE_HEADER + ++ IMAGE_SIZEOF_NT_OPTIONAL_HEADER); ++ CHECK_SZ(IMAGE_DOS_HEADER, 0x40); ++ CHECK_SZ(IMAGE_EXPORT_DIRECTORY, 40); ++ CHECK_SZ(IMAGE_BASE_RELOCATION, 8); ++ CHECK_SZ(IMAGE_IMPORT_DESCRIPTOR, 20); ++#endif ++ ++ for (i = 0; i < n; i++) { ++ IMAGE_DOS_HEADER *dos_hdr; ++ pe = &pe_image[i]; ++ dos_hdr = pe->image; ++ ++ if (pe->size < sizeof(IMAGE_DOS_HEADER)) { ++ TRACE1("image too small: %d", pe->size); ++ return -EINVAL; ++ } ++ ++ pe->nt_hdr = ++ (IMAGE_NT_HEADERS *)(pe->image + dos_hdr->e_lfanew); ++ pe->opt_hdr = &pe->nt_hdr->OptionalHeader; ++ ++ pe->type = check_nt_hdr(pe->nt_hdr); ++ if (pe->type <= 0) { ++ TRACE1("type <= 0"); ++ return -EINVAL; ++ } ++ ++ if (fix_pe_image(pe)) { ++ TRACE1("bad PE image"); ++ return -EINVAL; ++ } ++ ++ if (read_exports(pe)) { ++ TRACE1("read exports failed"); ++ return -EINVAL; ++ } ++ } ++ ++ for (i = 0; i < n; i++) { ++ pe = &pe_image[i]; ++ ++ if (fixup_reloc(pe->image, pe->nt_hdr)) { ++ TRACE1("fixup reloc failed"); ++ return -EINVAL; ++ } ++ if (fixup_imports(pe->image, pe->nt_hdr)) { ++ TRACE1("fixup imports failed"); ++ return -EINVAL; ++ } ++#if defined(CONFIG_X86_64) ++ fix_user_shared_data_addr(pe_image[i].image, pe_image[i].size); ++#endif ++ flush_icache_range((unsigned long)pe->image, pe->size); ++ ++ pe->entry = ++ RVA2VA(pe->image, ++ pe->opt_hdr->AddressOfEntryPoint, void *); ++ TRACE1("entry is at %p, rva at %08X", pe->entry, ++ pe->opt_hdr->AddressOfEntryPoint); ++ } ++ ++ for (i = 0; i < n; i++) { ++ pe = &pe_image[i]; ++ ++ if (pe->type == IMAGE_FILE_DLL) { ++ struct unicode_string ustring; ++ char *buf = "0/0t0m0p00"; ++ int (*dll_entry)(struct unicode_string *ustring) ++ wstdcall; ++ ++ memset(&ustring, 0, sizeof(ustring)); ++ ustring.buf = (wchar_t *)buf; ++ dll_entry = (void *)get_dll_init(pe->name); ++ ++ TRACE1("calling dll_init at %p", dll_entry); ++ if (!dll_entry || dll_entry(&ustring)) ++ ERROR("DLL initialize failed for %s", ++ pe->name); ++ } ++ else if (pe->type != IMAGE_FILE_EXECUTABLE_IMAGE) ++ ERROR("illegal image type: %d", pe->type); ++ } ++ return 0; ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.h +--- linux-3.12.2/3rdparty/ndiswrapper/pe_linker.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,992 @@ ++/* ++ * This file is an excerpt of winnt.h from WINE, which bears the ++ * following copyright: ++ * ++ * Win32 definitions for Windows NT ++ * ++ * Copyright 1996 Alexandre Julliard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* ++ * File formats definitions ++ */ ++typedef struct _IMAGE_DOS_HEADER { ++ WORD e_magic; /* 00: MZ Header signature */ ++ WORD e_cblp; /* 02: Bytes on last page of file */ ++ WORD e_cp; /* 04: Pages in file */ ++ WORD e_crlc; /* 06: Relocations */ ++ WORD e_cparhdr; /* 08: Size of header in paragraphs */ ++ WORD e_minalloc; /* 0a: Minimum extra paragraphs needed */ ++ WORD e_maxalloc; /* 0c: Maximum extra paragraphs needed */ ++ WORD e_ss; /* 0e: Initial (relative) SS value */ ++ WORD e_sp; /* 10: Initial SP value */ ++ WORD e_csum; /* 12: Checksum */ ++ WORD e_ip; /* 14: Initial IP value */ ++ WORD e_cs; /* 16: Initial (relative) CS value */ ++ WORD e_lfarlc; /* 18: File address of relocation table */ ++ WORD e_ovno; /* 1a: Overlay number */ ++ WORD e_res[4]; /* 1c: Reserved words */ ++ WORD e_oemid; /* 24: OEM identifier (for e_oeminfo) */ ++ WORD e_oeminfo; /* 26: OEM information; e_oemid specific */ ++ WORD e_res2[10]; /* 28: Reserved words */ ++ DWORD e_lfanew; /* 3c: Offset to extended header */ ++} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; ++ ++#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ ++#define IMAGE_OS2_SIGNATURE 0x454E /* NE */ ++#define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */ ++#define IMAGE_OS2_SIGNATURE_LX 0x584C /* LX */ ++#define IMAGE_VXD_SIGNATURE 0x454C /* LE */ ++#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ ++ ++/* ++ * This is the Windows executable (NE) header. ++ * the name IMAGE_OS2_HEADER is misleading, but in the SDK this way. ++ */ ++typedef struct ++{ ++ WORD ne_magic; /* 00 NE signature 'NE' */ ++ BYTE ne_ver; /* 02 Linker version number */ ++ BYTE ne_rev; /* 03 Linker revision number */ ++ WORD ne_enttab; /* 04 Offset to entry table relative to NE */ ++ WORD ne_cbenttab; /* 06 Length of entry table in bytes */ ++ LONG ne_crc; /* 08 Checksum */ ++ WORD ne_flags; /* 0c Flags about segments in this file */ ++ WORD ne_autodata; /* 0e Automatic data segment number */ ++ WORD ne_heap; /* 10 Initial size of local heap */ ++ WORD ne_stack; /* 12 Initial size of stack */ ++ DWORD ne_csip; /* 14 Initial CS:IP */ ++ DWORD ne_sssp; /* 18 Initial SS:SP */ ++ WORD ne_cseg; /* 1c # of entries in segment table */ ++ WORD ne_cmod; /* 1e # of entries in module reference tab. */ ++ WORD ne_cbnrestab; /* 20 Length of nonresident-name table */ ++ WORD ne_segtab; /* 22 Offset to segment table */ ++ WORD ne_rsrctab; /* 24 Offset to resource table */ ++ WORD ne_restab; /* 26 Offset to resident-name table */ ++ WORD ne_modtab; /* 28 Offset to module reference table */ ++ WORD ne_imptab; /* 2a Offset to imported name table */ ++ DWORD ne_nrestab; /* 2c Offset to nonresident-name table */ ++ WORD ne_cmovent; /* 30 # of movable entry points */ ++ WORD ne_align; /* 32 Logical sector alignment shift count */ ++ WORD ne_cres; /* 34 # of resource segments */ ++ BYTE ne_exetyp; /* 36 Flags indicating target OS */ ++ BYTE ne_flagsothers; /* 37 Additional information flags */ ++ WORD ne_pretthunks; /* 38 Offset to return thunks */ ++ WORD ne_psegrefbytes; /* 3a Offset to segment ref. bytes */ ++ WORD ne_swaparea; /* 3c Reserved by Microsoft */ ++ WORD ne_expver; /* 3e Expected Windows version number */ ++} IMAGE_OS2_HEADER, *PIMAGE_OS2_HEADER; ++ ++typedef struct _IMAGE_VXD_HEADER { ++ WORD e32_magic; ++ BYTE e32_border; ++ BYTE e32_worder; ++ DWORD e32_level; ++ WORD e32_cpu; ++ WORD e32_os; ++ DWORD e32_ver; ++ DWORD e32_mflags; ++ DWORD e32_mpages; ++ DWORD e32_startobj; ++ DWORD e32_eip; ++ DWORD e32_stackobj; ++ DWORD e32_esp; ++ DWORD e32_pagesize; ++ DWORD e32_lastpagesize; ++ DWORD e32_fixupsize; ++ DWORD e32_fixupsum; ++ DWORD e32_ldrsize; ++ DWORD e32_ldrsum; ++ DWORD e32_objtab; ++ DWORD e32_objcnt; ++ DWORD e32_objmap; ++ DWORD e32_itermap; ++ DWORD e32_rsrctab; ++ DWORD e32_rsrccnt; ++ DWORD e32_restab; ++ DWORD e32_enttab; ++ DWORD e32_dirtab; ++ DWORD e32_dircnt; ++ DWORD e32_fpagetab; ++ DWORD e32_frectab; ++ DWORD e32_impmod; ++ DWORD e32_impmodcnt; ++ DWORD e32_impproc; ++ DWORD e32_pagesum; ++ DWORD e32_datapage; ++ DWORD e32_preload; ++ DWORD e32_nrestab; ++ DWORD e32_cbnrestab; ++ DWORD e32_nressum; ++ DWORD e32_autodata; ++ DWORD e32_debuginfo; ++ DWORD e32_debuglen; ++ DWORD e32_instpreload; ++ DWORD e32_instdemand; ++ DWORD e32_heapsize; ++ BYTE e32_res3[12]; ++ DWORD e32_winresoff; ++ DWORD e32_winreslen; ++ WORD e32_devid; ++ WORD e32_ddkver; ++} IMAGE_VXD_HEADER, *PIMAGE_VXD_HEADER; ++ ++/* These defines describe the meanings of the bits in the ++ Characteristics field */ ++ ++#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 /* No relocation info */ ++#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 ++#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 ++#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 ++#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 ++#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 ++#define IMAGE_FILE_16BIT_MACHINE 0x0040 ++#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 ++#define IMAGE_FILE_32BIT_MACHINE 0x0100 ++#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 ++#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 ++#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 ++#define IMAGE_FILE_SYSTEM 0x1000 ++#define IMAGE_FILE_DLL 0x2000 ++#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 ++#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 ++ ++/* These are the settings of the Machine field. */ ++#define IMAGE_FILE_MACHINE_UNKNOWN 0 ++#define IMAGE_FILE_MACHINE_I860 0x014d ++#define IMAGE_FILE_MACHINE_I386 0x014c ++#define IMAGE_FILE_MACHINE_R3000 0x0162 ++#define IMAGE_FILE_MACHINE_R4000 0x0166 ++#define IMAGE_FILE_MACHINE_R10000 0x0168 ++#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 ++#define IMAGE_FILE_MACHINE_ALPHA 0x0184 ++#define IMAGE_FILE_MACHINE_SH3 0x01a2 ++#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 ++#define IMAGE_FILE_MACHINE_SH3E 0x01a4 ++#define IMAGE_FILE_MACHINE_SH4 0x01a6 ++#define IMAGE_FILE_MACHINE_SH5 0x01a8 ++#define IMAGE_FILE_MACHINE_ARM 0x01c0 ++#define IMAGE_FILE_MACHINE_THUMB 0x01c2 ++#define IMAGE_FILE_MACHINE_AM33 0x01d3 ++#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 ++#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 ++#define IMAGE_FILE_MACHINE_IA64 0x0200 ++#define IMAGE_FILE_MACHINE_MIPS16 0x0266 ++#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 ++#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 ++#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 ++#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 ++#define IMAGE_FILE_MACHINE_TRICORE 0x0520 ++#define IMAGE_FILE_MACHINE_CEF 0x0cef ++#define IMAGE_FILE_MACHINE_EBC 0x0ebc ++#define IMAGE_FILE_MACHINE_AMD64 0x8664 ++#define IMAGE_FILE_MACHINE_M32R 0x9041 ++#define IMAGE_FILE_MACHINE_CEE 0xc0ee ++ ++#define IMAGE_SIZEOF_FILE_HEADER 20 ++#define IMAGE_SIZEOF_ROM_OPTIONAL_HEADER 56 ++#define IMAGE_SIZEOF_STD_OPTIONAL_HEADER 28 ++#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER32 224 ++#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER64 240 ++#define IMAGE_SIZEOF_SHORT_NAME 8 ++#define IMAGE_SIZEOF_SECTION_HEADER 40 ++#define IMAGE_SIZEOF_SYMBOL 18 ++#define IMAGE_SIZEOF_AUX_SYMBOL 18 ++#define IMAGE_SIZEOF_RELOCATION 10 ++#define IMAGE_SIZEOF_BASE_RELOCATION 8 ++#define IMAGE_SIZEOF_LINENUMBER 6 ++#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 ++ ++/* Possible Magic values */ ++#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x010b ++#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x020b ++#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x0107 ++ ++#ifdef CONFIG_X86_64 ++#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL_HEADER64 ++#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC ++#else ++#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL_HEADER32 ++#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC ++#endif ++ ++/* These are indexes into the DataDirectory array */ ++#define IMAGE_FILE_EXPORT_DIRECTORY 0 ++#define IMAGE_FILE_IMPORT_DIRECTORY 1 ++#define IMAGE_FILE_RESOURCE_DIRECTORY 2 ++#define IMAGE_FILE_EXCEPTION_DIRECTORY 3 ++#define IMAGE_FILE_SECURITY_DIRECTORY 4 ++#define IMAGE_FILE_BASE_RELOCATION_TABLE 5 ++#define IMAGE_FILE_DEBUG_DIRECTORY 6 ++#define IMAGE_FILE_DESCRIPTION_STRING 7 ++#define IMAGE_FILE_MACHINE_VALUE 8 /* Mips */ ++#define IMAGE_FILE_THREAD_LOCAL_STORAGE 9 ++#define IMAGE_FILE_CALLBACK_DIRECTORY 10 ++ ++/* Directory Entries, indices into the DataDirectory array */ ++ ++#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 ++#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 ++#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 ++#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 ++#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 ++#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 ++#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 ++#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 ++#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* (MIPS GP) */ ++#define IMAGE_DIRECTORY_ENTRY_TLS 9 ++#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 ++#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 ++#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ ++#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 ++#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 ++ ++/* Subsystem Values */ ++ ++#define IMAGE_SUBSYSTEM_UNKNOWN 0 ++#define IMAGE_SUBSYSTEM_NATIVE 1 ++#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 /* Windows GUI subsystem */ ++#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 /* Windows character subsystem */ ++#define IMAGE_SUBSYSTEM_OS2_CUI 5 ++#define IMAGE_SUBSYSTEM_POSIX_CUI 7 ++#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 /* native Win9x driver */ ++#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 /* Windows CE subsystem */ ++#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 ++#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 ++#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 ++#define IMAGE_SUBSYSTEM_EFI_ROM 13 ++#define IMAGE_SUBSYSTEM_XBOX 14 ++ ++typedef struct _IMAGE_FILE_HEADER { ++ WORD Machine; ++ WORD NumberOfSections; ++ DWORD TimeDateStamp; ++ DWORD PointerToSymbolTable; ++ DWORD NumberOfSymbols; ++ WORD SizeOfOptionalHeader; ++ WORD Characteristics; ++} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; ++ ++typedef struct _IMAGE_DATA_DIRECTORY { ++ DWORD VirtualAddress; ++ DWORD Size; ++} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; ++ ++#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 ++ ++typedef struct _IMAGE_OPTIONAL_HEADER32 { ++ ++ /* Standard fields */ ++ ++ WORD Magic; ++ BYTE MajorLinkerVersion; ++ BYTE MinorLinkerVersion; ++ DWORD SizeOfCode; ++ DWORD SizeOfInitializedData; ++ DWORD SizeOfUninitializedData; ++ DWORD AddressOfEntryPoint; ++ DWORD BaseOfCode; ++ DWORD BaseOfData; ++ ++ /* NT additional fields */ ++ DWORD ImageBase; ++ DWORD SectionAlignment; ++ DWORD FileAlignment; ++ WORD MajorOperatingSystemVersion; ++ WORD MinorOperatingSystemVersion; ++ WORD MajorImageVersion; ++ WORD MinorImageVersion; ++ WORD MajorSubsystemVersion; ++ WORD MinorSubsystemVersion; ++ DWORD Win32VersionValue; ++ DWORD SizeOfImage; ++ DWORD SizeOfHeaders; ++ DWORD CheckSum; ++ WORD Subsystem; ++ WORD DllCharacteristics; ++ DWORD SizeOfStackReserve; ++ DWORD SizeOfStackCommit; ++ DWORD SizeOfHeapReserve; ++ DWORD SizeOfHeapCommit; ++ DWORD LoaderFlags; ++ DWORD NumberOfRvaAndSizes; ++ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; ++} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; ++ ++typedef struct _IMAGE_OPTIONAL_HEADER64 { ++ ++ /* Standard fields */ ++ ++ WORD Magic; ++ BYTE MajorLinkerVersion; ++ BYTE MinorLinkerVersion; ++ DWORD SizeOfCode; ++ DWORD SizeOfInitializedData; ++ DWORD SizeOfUninitializedData; ++ DWORD AddressOfEntryPoint; ++ DWORD BaseOfCode; ++ ++ /* NT additional fields */ ++ ULONGLONG ImageBase; ++ DWORD SectionAlignment; ++ DWORD FileAlignment; ++ WORD MajorOperatingSystemVersion; ++ WORD MinorOperatingSystemVersion; ++ WORD MajorImageVersion; ++ WORD MinorImageVersion; ++ WORD MajorSubsystemVersion; ++ WORD MinorSubsystemVersion; ++ DWORD Win32VersionValue; ++ DWORD SizeOfImage; ++ DWORD SizeOfHeaders; ++ DWORD CheckSum; ++ WORD Subsystem; ++ WORD DllCharacteristics; ++ ULONGLONG SizeOfStackReserve; ++ ULONGLONG SizeOfStackCommit; ++ ULONGLONG SizeOfHeapReserve; ++ ULONGLONG SizeOfHeapCommit; ++ DWORD LoaderFlags; ++ DWORD NumberOfRvaAndSizes; ++ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; ++} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; ++ ++#ifdef CONFIG_X86_64 ++typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER; ++typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER; ++#else ++typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER; ++typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER; ++#endif ++ ++typedef struct _IMAGE_NT_HEADERS32 { ++ DWORD Signature; /* "PE"\0\0 */ /* 0x00 */ ++ IMAGE_FILE_HEADER FileHeader; /* 0x04 */ ++ IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */ ++} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; ++ ++typedef struct _IMAGE_NT_HEADERS64 { ++ DWORD Signature; /* "PE"\0\0 */ /* 0x00 */ ++ IMAGE_FILE_HEADER FileHeader; /* 0x04 */ ++ IMAGE_OPTIONAL_HEADER64 OptionalHeader; /* 0x18 */ ++} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; ++ ++#ifdef CONFIG_X86_64 ++typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS; ++typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS; ++#else ++typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS; ++typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS; ++#endif ++ ++#define IMAGE_SIZEOF_SHORT_NAME 8 ++ ++typedef struct _IMAGE_SECTION_HEADER { ++ BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; ++ union { ++ DWORD PhysicalAddress; ++ DWORD VirtualSize; ++ } Misc; ++ DWORD VirtualAddress; ++ DWORD SizeOfRawData; ++ DWORD PointerToRawData; ++ DWORD PointerToRelocations; ++ DWORD PointerToLinenumbers; ++ WORD NumberOfRelocations; ++ WORD NumberOfLinenumbers; ++ DWORD Characteristics; ++} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; ++ ++#define IMAGE_SIZEOF_SECTION_HEADER 40 ++ ++#define IMAGE_FIRST_SECTION(ntheader) \ ++((PIMAGE_SECTION_HEADER)((LPBYTE)&((PIMAGE_NT_HEADERS)(ntheader))->OptionalHeader + \ ++((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader)) ++ ++/* These defines are for the Characteristics bitfield. */ ++/* #define IMAGE_SCN_TYPE_REG 0x00000000 - Reserved */ ++/* #define IMAGE_SCN_TYPE_DSECT 0x00000001 - Reserved */ ++/* #define IMAGE_SCN_TYPE_NOLOAD 0x00000002 - Reserved */ ++/* #define IMAGE_SCN_TYPE_GROUP 0x00000004 - Reserved */ ++#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved */ ++/* #define IMAGE_SCN_TYPE_COPY 0x00000010 - Reserved */ ++ ++#define IMAGE_SCN_CNT_CODE 0x00000020 ++#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 ++#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 ++ ++#define IMAGE_SCN_LNK_OTHER 0x00000100 ++#define IMAGE_SCN_LNK_INFO 0x00000200 ++/* #define IMAGE_SCN_TYPE_OVER 0x00000400 - Reserved */ ++#define IMAGE_SCN_LNK_REMOVE 0x00000800 ++#define IMAGE_SCN_LNK_COMDAT 0x00001000 ++ ++/* 0x00002000 - Reserved */ ++/* #define IMAGE_SCN_MEM_PROTECTED 0x00004000 - Obsolete */ ++#define IMAGE_SCN_MEM_FARDATA 0x00008000 ++ ++/* #define IMAGE_SCN_MEM_SYSHEAP 0x00010000 - Obsolete */ ++#define IMAGE_SCN_MEM_PURGEABLE 0x00020000 ++#define IMAGE_SCN_MEM_16BIT 0x00020000 ++#define IMAGE_SCN_MEM_LOCKED 0x00040000 ++#define IMAGE_SCN_MEM_PRELOAD 0x00080000 ++ ++#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 ++#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 ++#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 ++#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 ++#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default */ ++#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 ++#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 ++#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 ++#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 ++#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 ++#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 ++#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 ++#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 ++#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 ++/* 0x00F00000 - Unused */ ++#define IMAGE_SCN_ALIGN_MASK 0x00F00000 ++ ++#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 ++ ++ ++#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 ++#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 ++#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 ++#define IMAGE_SCN_MEM_SHARED 0x10000000 ++#define IMAGE_SCN_MEM_EXECUTE 0x20000000 ++#define IMAGE_SCN_MEM_READ 0x40000000 ++#define IMAGE_SCN_MEM_WRITE 0x80000000 ++ ++typedef struct _IMAGE_SYMBOL { ++ union { ++ BYTE ShortName[8]; ++ struct { ++ DWORD Short; ++ DWORD Long; ++ } Name; ++ DWORD LongName[2]; ++ } N; ++ DWORD Value; ++ SHORT SectionNumber; ++ WORD Type; ++ BYTE StorageClass; ++ BYTE NumberOfAuxSymbols; ++} IMAGE_SYMBOL; ++typedef IMAGE_SYMBOL *PIMAGE_SYMBOL; ++ ++#define IMAGE_SIZEOF_SYMBOL 18 ++ ++typedef struct _IMAGE_LINENUMBER { ++ union { ++ DWORD SymbolTableIndex; ++ DWORD VirtualAddress; ++ } Type; ++ WORD Linenumber; ++} IMAGE_LINENUMBER; ++typedef IMAGE_LINENUMBER *PIMAGE_LINENUMBER; ++ ++#define IMAGE_SIZEOF_LINENUMBER 6 ++ ++typedef union _IMAGE_AUX_SYMBOL { ++ struct { ++ DWORD TagIndex; ++ union { ++ struct { ++ WORD Linenumber; ++ WORD Size; ++ } LnSz; ++ DWORD TotalSize; ++ } Misc; ++ union { ++ struct { ++ DWORD PointerToLinenumber; ++ DWORD PointerToNextFunction; ++ } Function; ++ struct { ++ WORD Dimension[4]; ++ } Array; ++ } FcnAry; ++ WORD TvIndex; ++ } Sym; ++ struct { ++ BYTE Name[IMAGE_SIZEOF_SYMBOL]; ++ } File; ++ struct { ++ DWORD Length; ++ WORD NumberOfRelocations; ++ WORD NumberOfLinenumbers; ++ DWORD CheckSum; ++ SHORT Number; ++ BYTE Selection; ++ } Section; ++} IMAGE_AUX_SYMBOL; ++typedef IMAGE_AUX_SYMBOL *PIMAGE_AUX_SYMBOL; ++ ++#define IMAGE_SIZEOF_AUX_SYMBOL 18 ++ ++#define IMAGE_SYM_UNDEFINED (SHORT)0 ++#define IMAGE_SYM_ABSOLUTE (SHORT)-1 ++#define IMAGE_SYM_DEBUG (SHORT)-2 ++ ++#define IMAGE_SYM_TYPE_NULL 0x0000 ++#define IMAGE_SYM_TYPE_VOID 0x0001 ++#define IMAGE_SYM_TYPE_CHAR 0x0002 ++#define IMAGE_SYM_TYPE_SHORT 0x0003 ++#define IMAGE_SYM_TYPE_INT 0x0004 ++#define IMAGE_SYM_TYPE_LONG 0x0005 ++#define IMAGE_SYM_TYPE_FLOAT 0x0006 ++#define IMAGE_SYM_TYPE_DOUBLE 0x0007 ++#define IMAGE_SYM_TYPE_STRUCT 0x0008 ++#define IMAGE_SYM_TYPE_UNION 0x0009 ++#define IMAGE_SYM_TYPE_ENUM 0x000A ++#define IMAGE_SYM_TYPE_MOE 0x000B ++#define IMAGE_SYM_TYPE_BYTE 0x000C ++#define IMAGE_SYM_TYPE_WORD 0x000D ++#define IMAGE_SYM_TYPE_UINT 0x000E ++#define IMAGE_SYM_TYPE_DWORD 0x000F ++#define IMAGE_SYM_TYPE_PCODE 0x8000 ++ ++#define IMAGE_SYM_DTYPE_NULL 0 ++#define IMAGE_SYM_DTYPE_POINTER 1 ++#define IMAGE_SYM_DTYPE_FUNCTION 2 ++#define IMAGE_SYM_DTYPE_ARRAY 3 ++ ++#define IMAGE_SYM_CLASS_END_OF_FUNCTION (BYTE )-1 ++#define IMAGE_SYM_CLASS_NULL 0x0000 ++#define IMAGE_SYM_CLASS_AUTOMATIC 0x0001 ++#define IMAGE_SYM_CLASS_EXTERNAL 0x0002 ++#define IMAGE_SYM_CLASS_STATIC 0x0003 ++#define IMAGE_SYM_CLASS_REGISTER 0x0004 ++#define IMAGE_SYM_CLASS_EXTERNAL_DEF 0x0005 ++#define IMAGE_SYM_CLASS_LABEL 0x0006 ++#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 0x0007 ++#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 0x0008 ++#define IMAGE_SYM_CLASS_ARGUMENT 0x0009 ++#define IMAGE_SYM_CLASS_STRUCT_TAG 0x000A ++#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 0x000B ++#define IMAGE_SYM_CLASS_UNION_TAG 0x000C ++#define IMAGE_SYM_CLASS_TYPE_DEFINITION 0x000D ++#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 0x000E ++#define IMAGE_SYM_CLASS_ENUM_TAG 0x000F ++#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 0x0010 ++#define IMAGE_SYM_CLASS_REGISTER_PARAM 0x0011 ++#define IMAGE_SYM_CLASS_BIT_FIELD 0x0012 ++ ++#define IMAGE_SYM_CLASS_FAR_EXTERNAL 0x0044 ++#define IMAGE_SYM_CLASS_BLOCK 0x0064 ++#define IMAGE_SYM_CLASS_FUNCTION 0x0065 ++#define IMAGE_SYM_CLASS_END_OF_STRUCT 0x0066 ++#define IMAGE_SYM_CLASS_FILE 0x0067 ++#define IMAGE_SYM_CLASS_SECTION 0x0068 ++#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069 ++ ++#define N_BTMASK 0x000F ++#define N_TMASK 0x0030 ++#define N_TMASK1 0x00C0 ++#define N_TMASK2 0x00F0 ++#define N_BTSHFT 4 ++#define N_TSHIFT 2 ++ ++#define BTYPE(x) ((x) & N_BTMASK) ++ ++#ifndef ISPTR ++#define ISPTR(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_POINTER << N_BTSHFT)) ++#endif ++ ++#ifndef ISFCN ++#define ISFCN(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_FUNCTION << N_BTSHFT)) ++#endif ++ ++#ifndef ISARY ++#define ISARY(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_ARRAY << N_BTSHFT)) ++#endif ++ ++#ifndef ISTAG ++#define ISTAG(x) ((x)==IMAGE_SYM_CLASS_STRUCT_TAG || (x)==IMAGE_SYM_CLASS_UNION_TAG || (x)==IMAGE_SYM_CLASS_ENUM_TAG) ++#endif ++ ++#ifndef INCREF ++#define INCREF(x) ((((x)&~N_BTMASK)<>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK)) ++#endif ++ ++#define IMAGE_COMDAT_SELECT_NODUPLICATES 1 ++#define IMAGE_COMDAT_SELECT_ANY 2 ++#define IMAGE_COMDAT_SELECT_SAME_SIZE 3 ++#define IMAGE_COMDAT_SELECT_EXACT_MATCH 4 ++#define IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 ++#define IMAGE_COMDAT_SELECT_LARGEST 6 ++#define IMAGE_COMDAT_SELECT_NEWEST 7 ++ ++#define IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1 ++#define IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2 ++#define IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3 ++ ++/* Export module directory */ ++ ++typedef struct _IMAGE_EXPORT_DIRECTORY { ++ DWORD Characteristics; ++ DWORD TimeDateStamp; ++ WORD MajorVersion; ++ WORD MinorVersion; ++ DWORD Name; ++ DWORD Base; ++ DWORD NumberOfFunctions; ++ DWORD NumberOfNames; ++ DWORD AddressOfFunctions; ++ DWORD AddressOfNames; ++ DWORD AddressOfNameOrdinals; ++} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY; ++ ++/* Import name entry */ ++typedef struct _IMAGE_IMPORT_BY_NAME { ++ WORD Hint; ++ BYTE Name[1]; ++} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME; ++ ++/* Import thunk */ ++typedef struct _IMAGE_THUNK_DATA32 { ++ union { ++ DWORD ForwarderString; ++ DWORD Function; ++ DWORD Ordinal; ++ DWORD AddressOfData; ++ } u1; ++} IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32; ++ ++typedef struct _IMAGE_THUNK_DATA64 { ++ union { ++ ULONGLONG ForwarderString; ++ ULONGLONG Function; ++ ULONGLONG Ordinal; ++ ULONGLONG AddressOfData; ++ } u1; ++} IMAGE_THUNK_DATA64,*PIMAGE_THUNK_DATA64; ++ ++#ifdef CONFIG_X86_64 ++typedef IMAGE_THUNK_DATA32 IMAGE_THUNK_DATA; ++typedef PIMAGE_THUNK_DATA32 PIMAGE_THUNK_DATA; ++#else ++typedef IMAGE_THUNK_DATA64 IMAGE_THUNK_DATA; ++typedef PIMAGE_THUNK_DATA64 PIMAGE_THUNK_DATA; ++#endif ++ ++/* Import module directory */ ++ ++typedef struct __packed _IMAGE_IMPORT_DESCRIPTOR { ++ union { ++ DWORD Characteristics; /* 0 for terminating null ++ * import descriptor */ ++ DWORD OriginalFirstThunk; /* RVA to original unbound ++ * IAT */ ++ } u; ++ DWORD TimeDateStamp; /* 0 if not bound, ++ * -1 if bound, and real date\time stamp ++ * in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ++ * (new BIND) ++ * otherwise date/time stamp of DLL bound to ++ * (Old BIND) ++ */ ++ DWORD ForwarderChain; /* -1 if no forwarders */ ++ DWORD Name; ++ /* RVA to IAT (if bound this IAT has actual addresses) */ ++ DWORD FirstThunk; ++} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR; ++ ++#define IMAGE_ORDINAL_FLAG32 0x80000000 ++#define IMAGE_ORDINAL_FLAG64 0x8000000000000000UL ++#define IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0) ++#define IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0) ++#define IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) ++ ++#ifdef CONFIG_X86_64 ++#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG64 ++#define IMAGE_SNAP_BY_ORDINAL IMAGE_SNAP_BY_ORDINAL64 ++#else ++#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG32 ++#define IMAGE_SNAP_BY_ORDINAL IMAGE_SNAP_BY_ORDINAL32 ++#endif ++ ++typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR ++{ ++ DWORD TimeDateStamp; ++ WORD OffsetModuleName; ++ WORD NumberOfModuleForwarderRefs; ++/* Array of zero or more IMAGE_BOUND_FORWARDER_REF follows */ ++} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR; ++ ++typedef struct _IMAGE_BOUND_FORWARDER_REF ++{ ++ DWORD TimeDateStamp; ++ WORD OffsetModuleName; ++ WORD Reserved; ++} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF; ++ ++typedef struct _IMAGE_BASE_RELOCATION ++{ ++ DWORD VirtualAddress; ++ DWORD SizeOfBlock; ++ WORD TypeOffset[0]; ++} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; ++ ++typedef struct _IMAGE_RELOCATION ++{ ++ union { ++ DWORD VirtualAddress; ++ DWORD RelocCount; ++ } DUMMYUNIONNAME; ++ DWORD SymbolTableIndex; ++ WORD Type; ++} IMAGE_RELOCATION, *PIMAGE_RELOCATION; ++ ++#define IMAGE_SIZEOF_RELOCATION 10 ++ ++/* generic relocation types */ ++#define IMAGE_REL_BASED_ABSOLUTE 0 ++#define IMAGE_REL_BASED_HIGH 1 ++#define IMAGE_REL_BASED_LOW 2 ++#define IMAGE_REL_BASED_HIGHLOW 3 ++#define IMAGE_REL_BASED_HIGHADJ 4 ++#define IMAGE_REL_BASED_MIPS_JMPADDR 5 ++#define IMAGE_REL_BASED_SECTION 6 ++#define IMAGE_REL_BASED_REL 7 ++#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 ++#define IMAGE_REL_BASED_IA64_IMM64 9 /* yes, 9 too */ ++#define IMAGE_REL_BASED_DIR64 10 ++#define IMAGE_REL_BASED_HIGH3ADJ 11 ++ ++/* I386 relocation types */ ++#define IMAGE_REL_I386_ABSOLUTE 0 ++#define IMAGE_REL_I386_DIR16 1 ++#define IMAGE_REL_I386_REL16 2 ++#define IMAGE_REL_I386_DIR32 6 ++#define IMAGE_REL_I386_DIR32NB 7 ++#define IMAGE_REL_I386_SEG12 9 ++#define IMAGE_REL_I386_SECTION 10 ++#define IMAGE_REL_I386_SECREL 11 ++#define IMAGE_REL_I386_REL32 20 ++ ++/* MIPS relocation types */ ++#define IMAGE_REL_MIPS_ABSOLUTE 0x0000 ++#define IMAGE_REL_MIPS_REFHALF 0x0001 ++#define IMAGE_REL_MIPS_REFWORD 0x0002 ++#define IMAGE_REL_MIPS_JMPADDR 0x0003 ++#define IMAGE_REL_MIPS_REFHI 0x0004 ++#define IMAGE_REL_MIPS_REFLO 0x0005 ++#define IMAGE_REL_MIPS_GPREL 0x0006 ++#define IMAGE_REL_MIPS_LITERAL 0x0007 ++#define IMAGE_REL_MIPS_SECTION 0x000A ++#define IMAGE_REL_MIPS_SECREL 0x000B ++#define IMAGE_REL_MIPS_SECRELLO 0x000C ++#define IMAGE_REL_MIPS_SECRELHI 0x000D ++#define IMAGE_REL_MIPS_JMPADDR16 0x0010 ++#define IMAGE_REL_MIPS_REFWORDNB 0x0022 ++#define IMAGE_REL_MIPS_PAIR 0x0025 ++ ++/* ALPHA relocation types */ ++#define IMAGE_REL_ALPHA_ABSOLUTE 0x0000 ++#define IMAGE_REL_ALPHA_REFLONG 0x0001 ++#define IMAGE_REL_ALPHA_REFQUAD 0x0002 ++#define IMAGE_REL_ALPHA_GPREL 0x0003 ++#define IMAGE_REL_ALPHA_LITERAL 0x0004 ++#define IMAGE_REL_ALPHA_LITUSE 0x0005 ++#define IMAGE_REL_ALPHA_GPDISP 0x0006 ++#define IMAGE_REL_ALPHA_BRADDR 0x0007 ++#define IMAGE_REL_ALPHA_HINT 0x0008 ++#define IMAGE_REL_ALPHA_INLINE_REFLONG 0x0009 ++#define IMAGE_REL_ALPHA_REFHI 0x000A ++#define IMAGE_REL_ALPHA_REFLO 0x000B ++#define IMAGE_REL_ALPHA_PAIR 0x000C ++#define IMAGE_REL_ALPHA_MATCH 0x000D ++#define IMAGE_REL_ALPHA_SECTION 0x000E ++#define IMAGE_REL_ALPHA_SECREL 0x000F ++#define IMAGE_REL_ALPHA_REFLONGNB 0x0010 ++#define IMAGE_REL_ALPHA_SECRELLO 0x0011 ++#define IMAGE_REL_ALPHA_SECRELHI 0x0012 ++#define IMAGE_REL_ALPHA_REFQ3 0x0013 ++#define IMAGE_REL_ALPHA_REFQ2 0x0014 ++#define IMAGE_REL_ALPHA_REFQ1 0x0015 ++#define IMAGE_REL_ALPHA_GPRELLO 0x0016 ++#define IMAGE_REL_ALPHA_GPRELHI 0x0017 ++ ++/* PowerPC relocation types */ ++#define IMAGE_REL_PPC_ABSOLUTE 0x0000 ++#define IMAGE_REL_PPC_ADDR64 0x0001 ++#define IMAGE_REL_PPC_ADDR 0x0002 ++#define IMAGE_REL_PPC_ADDR24 0x0003 ++#define IMAGE_REL_PPC_ADDR16 0x0004 ++#define IMAGE_REL_PPC_ADDR14 0x0005 ++#define IMAGE_REL_PPC_REL24 0x0006 ++#define IMAGE_REL_PPC_REL14 0x0007 ++#define IMAGE_REL_PPC_TOCREL16 0x0008 ++#define IMAGE_REL_PPC_TOCREL14 0x0009 ++#define IMAGE_REL_PPC_ADDR32NB 0x000A ++#define IMAGE_REL_PPC_SECREL 0x000B ++#define IMAGE_REL_PPC_SECTION 0x000C ++#define IMAGE_REL_PPC_IFGLUE 0x000D ++#define IMAGE_REL_PPC_IMGLUE 0x000E ++#define IMAGE_REL_PPC_SECREL16 0x000F ++#define IMAGE_REL_PPC_REFHI 0x0010 ++#define IMAGE_REL_PPC_REFLO 0x0011 ++#define IMAGE_REL_PPC_PAIR 0x0012 ++#define IMAGE_REL_PPC_SECRELLO 0x0013 ++#define IMAGE_REL_PPC_SECRELHI 0x0014 ++#define IMAGE_REL_PPC_GPREL 0x0015 ++#define IMAGE_REL_PPC_TYPEMASK 0x00FF ++/* modifier bits */ ++#define IMAGE_REL_PPC_NEG 0x0100 ++#define IMAGE_REL_PPC_BRTAKEN 0x0200 ++#define IMAGE_REL_PPC_BRNTAKEN 0x0400 ++#define IMAGE_REL_PPC_TOCDEFN 0x0800 ++ ++/* SH3 ? relocation type */ ++#define IMAGE_REL_SH3_ABSOLUTE 0x0000 ++#define IMAGE_REL_SH3_DIRECT16 0x0001 ++#define IMAGE_REL_SH3_DIRECT 0x0002 ++#define IMAGE_REL_SH3_DIRECT8 0x0003 ++#define IMAGE_REL_SH3_DIRECT8_WORD 0x0004 ++#define IMAGE_REL_SH3_DIRECT8_LONG 0x0005 ++#define IMAGE_REL_SH3_DIRECT4 0x0006 ++#define IMAGE_REL_SH3_DIRECT4_WORD 0x0007 ++#define IMAGE_REL_SH3_DIRECT4_LONG 0x0008 ++#define IMAGE_REL_SH3_PCREL8_WORD 0x0009 ++#define IMAGE_REL_SH3_PCREL8_LONG 0x000A ++#define IMAGE_REL_SH3_PCREL12_WORD 0x000B ++#define IMAGE_REL_SH3_STARTOF_SECTION 0x000C ++#define IMAGE_REL_SH3_SIZEOF_SECTION 0x000D ++#define IMAGE_REL_SH3_SECTION 0x000E ++#define IMAGE_REL_SH3_SECREL 0x000F ++#define IMAGE_REL_SH3_DIRECT32_NB 0x0010 ++ ++/* ARM (Archimedes?) relocation types */ ++#define IMAGE_REL_ARM_ABSOLUTE 0x0000 ++#define IMAGE_REL_ARM_ADDR 0x0001 ++#define IMAGE_REL_ARM_ADDR32NB 0x0002 ++#define IMAGE_REL_ARM_BRANCH24 0x0003 ++#define IMAGE_REL_ARM_BRANCH11 0x0004 ++#define IMAGE_REL_ARM_SECTION 0x000E ++#define IMAGE_REL_ARM_SECREL 0x000F ++ ++/* IA64 relocation types */ ++#define IMAGE_REL_IA64_ABSOLUTE 0x0000 ++#define IMAGE_REL_IA64_IMM14 0x0001 ++#define IMAGE_REL_IA64_IMM22 0x0002 ++#define IMAGE_REL_IA64_IMM64 0x0003 ++#define IMAGE_REL_IA64_DIR 0x0004 ++#define IMAGE_REL_IA64_DIR64 0x0005 ++#define IMAGE_REL_IA64_PCREL21B 0x0006 ++#define IMAGE_REL_IA64_PCREL21M 0x0007 ++#define IMAGE_REL_IA64_PCREL21F 0x0008 ++#define IMAGE_REL_IA64_GPREL22 0x0009 ++#define IMAGE_REL_IA64_LTOFF22 0x000A ++#define IMAGE_REL_IA64_SECTION 0x000B ++#define IMAGE_REL_IA64_SECREL22 0x000C ++#define IMAGE_REL_IA64_SECREL64I 0x000D ++#define IMAGE_REL_IA64_SECREL 0x000E ++#define IMAGE_REL_IA64_LTOFF64 0x000F ++#define IMAGE_REL_IA64_DIR32NB 0x0010 ++#define IMAGE_REL_IA64_RESERVED_11 0x0011 ++#define IMAGE_REL_IA64_RESERVED_12 0x0012 ++#define IMAGE_REL_IA64_RESERVED_13 0x0013 ++#define IMAGE_REL_IA64_RESERVED_14 0x0014 ++#define IMAGE_REL_IA64_RESERVED_15 0x0015 ++#define IMAGE_REL_IA64_RESERVED_16 0x0016 ++#define IMAGE_REL_IA64_ADDEND 0x001F ++ ++/* archive format */ ++ ++#define IMAGE_ARCHIVE_START_SIZE 8 ++#define IMAGE_ARCHIVE_START "!\n" ++#define IMAGE_ARCHIVE_END "`\n" ++#define IMAGE_ARCHIVE_PAD "\n" ++#define IMAGE_ARCHIVE_LINKER_MEMBER "/ " ++#define IMAGE_ARCHIVE_LONGNAMES_MEMBER "// " ++ ++typedef struct _IMAGE_ARCHIVE_MEMBER_HEADER ++{ ++ BYTE Name[16]; ++ BYTE Date[12]; ++ BYTE UserID[6]; ++ BYTE GroupID[6]; ++ BYTE Mode[8]; ++ BYTE Size[10]; ++ BYTE EndHeader[2]; ++} IMAGE_ARCHIVE_MEMBER_HEADER, *PIMAGE_ARCHIVE_MEMBER_HEADER; ++ ++#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 ++ ++/* ++ * Resource directory stuff ++ */ ++typedef struct _IMAGE_RESOURCE_DIRECTORY { ++ DWORD Characteristics; ++ DWORD TimeDateStamp; ++ WORD MajorVersion; ++ WORD MinorVersion; ++ WORD NumberOfNamedEntries; ++ WORD NumberOfIdEntries; ++ /* IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; */ ++} IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY; ++ ++#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000 ++#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000 ++ ++typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { ++ union { ++ struct { ++#ifdef BITFIELDS_BIGENDIAN ++ unsigned NameIsString:1; ++ unsigned NameOffset:31; ++#else ++ unsigned NameOffset:31; ++ unsigned NameIsString:1; ++#endif ++ } DUMMYSTRUCTNAME1; ++ DWORD Name; ++ struct { ++#ifdef WORDS_BIGENDIAN ++ WORD __pad; ++ WORD Id; ++#else ++ WORD Id; ++ WORD __pad; ++#endif ++ } DUMMYSTRUCTNAME2; ++ } DUMMYUNIONNAME1; ++ union { ++ DWORD OffsetToData; ++ struct { ++#ifdef BITFIELDS_BIGENDIAN ++ unsigned DataIsDirectory:1; ++ unsigned OffsetToDirectory:31; ++#else ++ unsigned OffsetToDirectory:31; ++ unsigned DataIsDirectory:1; ++#endif ++ } DUMMYSTRUCTNAME3; ++ } DUMMYUNIONNAME2; ++} IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY; ++ ++ ++typedef struct _IMAGE_RESOURCE_DIRECTORY_STRING { ++ WORD Length; ++ CHAR NameString[ 1 ]; ++} IMAGE_RESOURCE_DIRECTORY_STRING,*PIMAGE_RESOURCE_DIRECTORY_STRING; +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.c +--- linux-3.12.2/3rdparty/ndiswrapper/pnp.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,697 @@ ++/* ++ * Copyright (C) 2005 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "usb.h" ++#include "pnp.h" ++#include "wrapndis.h" ++#include "loader.h" ++ ++/* Functions callable from the NDIS driver */ ++wstdcall NTSTATUS pdoDispatchDeviceControl(struct device_object *pdo, ++ struct irp *irp); ++wstdcall NTSTATUS pdoDispatchPnp(struct device_object *pdo, struct irp *irp); ++wstdcall NTSTATUS pdoDispatchPower(struct device_object *pdo, struct irp *irp); ++ ++static NTSTATUS start_pdo(struct device_object *pdo) ++{ ++ int i, ret, count, resources_size; ++ struct wrap_device *wd; ++ struct pci_dev *pdev; ++ struct cm_partial_resource_descriptor *entry; ++ struct cm_partial_resource_list *partial_resource_list; ++ ++ ENTER1("%p, %p", pdo, pdo->reserved); ++ wd = pdo->reserved; ++ if (ntoskernel_init_device(wd)) ++ EXIT1(return STATUS_FAILURE); ++ if (wrap_is_usb_bus(wd->dev_bus)) { ++ if (usb_init_device(wd)) { ++ ntoskernel_exit_device(wd); ++ EXIT1(return STATUS_FAILURE); ++ } ++ EXIT1(return STATUS_SUCCESS); ++ } ++ if (!wrap_is_pci_bus(wd->dev_bus)) ++ EXIT1(return STATUS_SUCCESS); ++ pdev = wd->pci.pdev; ++ ret = pci_enable_device(pdev); ++ if (ret) { ++ ERROR("couldn't enable PCI device: %x", ret); ++ return STATUS_FAILURE; ++ } ++ ret = pci_request_regions(pdev, DRIVER_NAME); ++ if (ret) { ++ ERROR("couldn't request PCI regions: %x", ret); ++ goto err_enable; ++ } ++ pci_set_power_state(pdev, PCI_D0); ++#ifdef CONFIG_X86_64 ++ /* 64-bit broadcom driver doesn't work if DMA is allocated ++ * from over 1GB */ ++ if (wd->vendor == 0x14e4) { ++ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(30)) || ++ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(30))) ++ WARNING("couldn't set DMA mask; this driver " ++ "may not work with more than 1GB RAM"); ++ } ++#endif ++ /* IRQ resource entry is filled in from pdev, instead of ++ * pci_resource macros */ ++ for (i = count = 0; pci_resource_start(pdev, i); i++) ++ if ((pci_resource_flags(pdev, i) & IORESOURCE_MEM) || ++ (pci_resource_flags(pdev, i) & IORESOURCE_IO)) ++ count++; ++ /* space for entry for IRQ is already in ++ * cm_partial_resource_list */ ++ resources_size = sizeof(struct cm_resource_list) + ++ sizeof(struct cm_partial_resource_descriptor) * count; ++ TRACE2("resources: %d, %d", count, resources_size); ++ wd->resource_list = kzalloc(resources_size, GFP_KERNEL); ++ if (!wd->resource_list) { ++ WARNING("couldn't allocate memory"); ++ goto err_regions; ++ } ++ wd->resource_list->count = 1; ++ wd->resource_list->list[0].interface_type = PCIBus; ++ /* bus_number is not used by WDM drivers */ ++ wd->resource_list->list[0].bus_number = pdev->bus->number; ++ ++ partial_resource_list = ++ &wd->resource_list->list->partial_resource_list; ++ partial_resource_list->version = 1; ++ partial_resource_list->revision = 1; ++ partial_resource_list->count = count + 1; ++ ++ for (i = count = 0; pci_resource_start(pdev, i); i++) { ++ entry = &partial_resource_list->partial_descriptors[count]; ++ TRACE2("%d", count); ++ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { ++ entry->type = CmResourceTypeMemory; ++ entry->flags = CM_RESOURCE_MEMORY_READ_WRITE; ++ entry->share = CmResourceShareDeviceExclusive; ++ } else if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { ++ entry->type = CmResourceTypePort; ++ entry->flags = CM_RESOURCE_PORT_IO; ++ entry->share = CmResourceShareDeviceExclusive; ++#if 0 ++ } else if (pci_resource_flags(pdev, i) & IORESOURCE_DMA) { ++ /* it looks like no driver uses this resource */ ++ typeof(pci_resource_flags(pdev, 0)) flags; ++ entry->type = CmResourceTypeDma; ++ flags = pci_resource_flags(pdev, i); ++ if (flags & IORESOURCE_DMA_TYPEA) ++ entry->flags |= CM_RESOURCE_DMA_TYPE_A; ++ else if (flags & IORESOURCE_DMA_TYPEB) ++ entry->flags |= CM_RESOURCE_DMA_TYPE_B; ++ else if (flags & IORESOURCE_DMA_TYPEF) ++ entry->flags |= CM_RESOURCE_DMA_TYPE_F; ++ if (flags & IORESOURCE_DMA_8BIT) ++ entry->flags |= CM_RESOURCE_DMA_8; ++ else if (flags & IORESOURCE_DMA_16BIT) ++ entry->flags |= CM_RESOURCE_DMA_16; ++ /* what about 32bit DMA? */ ++ else if (flags & IORESOURCE_DMA_8AND16BIT) ++ entry->flags |= CM_RESOURCE_DMA_8_AND_16; ++ if (flags & IORESOURCE_DMA_MASTER) ++ entry->flags |= CM_RESOURCE_DMA_BUS_MASTER; ++ entry->u.dma.channel = pci_resource_start(pdev, i); ++ /* what should this be? */ ++ entry->u.dma.port = 1; ++#endif ++ } else ++ continue; ++ /* TODO: Add other resource types? */ ++ entry->u.generic.start = ++ (ULONG_PTR)pci_resource_start(pdev, i); ++ entry->u.generic.length = pci_resource_len(pdev, i); ++ count++; ++ } ++ ++ /* put IRQ resource at the end */ ++ entry = &partial_resource_list->partial_descriptors[count++]; ++ entry->type = CmResourceTypeInterrupt; ++ entry->flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; ++ /* we assume all devices use shared IRQ */ ++ entry->share = CmResourceShareShared; ++ /* as per documentation, interrupt level should be DIRQL, but ++ * examples from DDK as well some drivers, such as AR5211, ++ * RT8180L use interrupt level as interrupt vector also in ++ * NdisMRegisterInterrupt */ ++ entry->u.interrupt.level = pdev->irq; ++ entry->u.interrupt.vector = pdev->irq; ++ entry->u.interrupt.affinity = -1; ++ ++ TRACE2("resource list count %d, irq: %d", ++ partial_resource_list->count, pdev->irq); ++ pci_set_drvdata(pdev, wd); ++ EXIT1(return STATUS_SUCCESS); ++err_regions: ++ pci_release_regions(pdev); ++err_enable: ++ pci_disable_device(pdev); ++ wd->pci.pdev = NULL; ++ wd->pdo = NULL; ++ EXIT1(return STATUS_FAILURE); ++} ++ ++static void remove_pdo(struct device_object *pdo) ++{ ++ struct wrap_device *wd = pdo->reserved; ++ ++ ntoskernel_exit_device(wd); ++ if (wrap_is_pci_bus(wd->dev_bus)) { ++ struct pci_dev *pdev = wd->pci.pdev; ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++ wd->pci.pdev = NULL; ++ pci_set_drvdata(pdev, NULL); ++ } else if (wrap_is_usb_bus(wd->dev_bus)) { ++ usb_exit_device(wd); ++ } ++ kfree(wd->resource_list); ++ wd->resource_list = NULL; ++ return; ++} ++ ++static NTSTATUS IoSendIrpTopDev(struct device_object *dev_obj, ULONG major_fn, ++ ULONG minor_fn, struct io_stack_location *sl) ++{ ++ NTSTATUS status; ++ struct nt_event event; ++ struct irp *irp; ++ struct io_stack_location *irp_sl; ++ struct device_object *top_dev = IoGetAttachedDeviceReference(dev_obj); ++ ++ KeInitializeEvent(&event, NotificationEvent, FALSE); ++ irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, top_dev, NULL, 0, NULL, ++ &event, NULL); ++ irp->io_status.status = STATUS_NOT_IMPLEMENTED; ++ irp->io_status.info = 0; ++ irp_sl = IoGetNextIrpStackLocation(irp); ++ if (sl) ++ memcpy(irp_sl, sl, sizeof(*irp_sl)); ++ irp_sl->major_fn = major_fn; ++ irp_sl->minor_fn = minor_fn; ++ status = IoCallDriver(top_dev, irp); ++ if (status == STATUS_PENDING) { ++ KeWaitForSingleObject(&event, Executive, KernelMode, ++ FALSE, NULL); ++ status = irp->io_status.status; ++ } ++ ObDereferenceObject(top_dev); ++ return status; ++} ++ ++wstdcall NTSTATUS pdoDispatchDeviceControl(struct device_object *pdo, ++ struct irp *irp) ++{ ++ NTSTATUS status; ++ struct wrap_device *wd = pdo->reserved; ++ ++ DUMP_IRP(irp); ++ (void)wd; ++ if (wrap_is_usb_bus(wd->dev_bus)) { ++ status = wrap_submit_irp(pdo, irp); ++ IOTRACE("status: %08X", status); ++ if (status != STATUS_PENDING) ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ } else { ++ status = irp->io_status.status = STATUS_NOT_IMPLEMENTED; ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ } ++ IOEXIT(return status); ++} ++WIN_FUNC_DECL(pdoDispatchDeviceControl,2) ++ ++wstdcall NTSTATUS pdoDispatchPnp(struct device_object *pdo, struct irp *irp) ++{ ++ struct io_stack_location *irp_sl; ++ NTSTATUS status; ++ struct wrap_device *wd = pdo->reserved; ++ ++ irp_sl = IoGetCurrentIrpStackLocation(irp); ++ TRACE2("%p %d:%d", pdo, irp_sl->major_fn, irp_sl->minor_fn); ++ switch (irp_sl->minor_fn) { ++ case IRP_MN_START_DEVICE: ++ status = start_pdo(pdo); ++ break; ++ case IRP_MN_QUERY_STOP_DEVICE: ++ case IRP_MN_STOP_DEVICE: ++ case IRP_MN_QUERY_REMOVE_DEVICE: ++ status = STATUS_SUCCESS; ++ break; ++ case IRP_MN_REMOVE_DEVICE: ++ remove_pdo(pdo); ++ status = STATUS_SUCCESS; ++ break; ++ case IRP_MN_QUERY_INTERFACE: ++ if (wrap_is_usb_bus(wd->dev_bus)) ++ status = usb_query_interface(wd, irp_sl); ++ else ++ status = STATUS_NOT_IMPLEMENTED; ++ break; ++ default: ++ TRACE2("fn %d not implemented", irp_sl->minor_fn); ++ status = STATUS_SUCCESS; ++ break; ++ } ++ irp->io_status.status = status; ++ TRACE2("status: %08X", status); ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ IOEXIT(return status); ++} ++WIN_FUNC_DECL(pdoDispatchPnp,2) ++ ++wstdcall NTSTATUS pdoDispatchPower(struct device_object *pdo, struct irp *irp) ++{ ++ struct io_stack_location *irp_sl; ++ struct wrap_device *wd; ++ union power_state power_state; ++ struct pci_dev *pdev; ++ NTSTATUS status; ++ ++ irp_sl = IoGetCurrentIrpStackLocation(irp); ++ wd = pdo->reserved; ++ TRACE2("pdo: %p, fn: %d:%d, wd: %p", ++ pdo, irp_sl->major_fn, irp_sl->minor_fn, wd); ++ switch (irp_sl->minor_fn) { ++ case IRP_MN_WAIT_WAKE: ++ /* TODO: this is not complete/correct */ ++ TRACE2("state: %d, completion: %p", ++ irp_sl->params.power.state.system_state, ++ irp_sl->completion_routine); ++ IoMarkIrpPending(irp); ++ status = STATUS_PENDING; ++ break; ++ case IRP_MN_SET_POWER: ++ power_state = irp_sl->params.power.state; ++ if (power_state.device_state == PowerDeviceD0) { ++ TRACE2("resuming %p", wd); ++ if (wrap_is_pci_bus(wd->dev_bus)) { ++ pdev = wd->pci.pdev; ++ pci_restore_state(pdev); ++ if (wd->pci.wake_state == PowerDeviceD3) { ++ pci_enable_wake(wd->pci.pdev, ++ PCI_D3hot, 0); ++ pci_enable_wake(wd->pci.pdev, ++ PCI_D3cold, 0); ++ } ++ pci_set_power_state(pdev, PCI_D0); ++ } else if (wrap_is_usb_bus(wd->dev_bus)) { ++ wrap_resume_urbs(wd); ++ } ++ } else { ++ TRACE2("suspending device %p", wd); ++ if (wrap_is_pci_bus(wd->dev_bus)) { ++ pdev = wd->pci.pdev; ++ pci_save_state(pdev); ++ TRACE2("%d", wd->pci.wake_state); ++ if (wd->pci.wake_state == PowerDeviceD3) { ++ pci_enable_wake(wd->pci.pdev, ++ PCI_D3hot, 1); ++ pci_enable_wake(wd->pci.pdev, ++ PCI_D3cold, 1); ++ } ++ pci_set_power_state(pdev, PCI_D3hot); ++ } else if (wrap_is_usb_bus(wd->dev_bus)) { ++ wrap_suspend_urbs(wd); ++ } ++ } ++ status = STATUS_SUCCESS; ++ break; ++ case IRP_MN_QUERY_POWER: ++ status = STATUS_SUCCESS; ++ break; ++ default: ++ TRACE2("fn %d not implemented", irp_sl->minor_fn); ++ status = STATUS_SUCCESS; ++ break; ++ } ++ irp->io_status.status = status; ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ return status; ++} ++WIN_FUNC_DECL(pdoDispatchPower,2) ++ ++static NTSTATUS pnp_set_device_power_state(struct wrap_device *wd, ++ enum device_power_state state) ++{ ++ NTSTATUS status; ++ struct device_object *pdo; ++ struct io_stack_location irp_sl; ++ ++ pdo = wd->pdo; ++ IOTRACE("%p, %p", pdo, IoGetAttachedDevice(pdo)); ++ memset(&irp_sl, 0, sizeof(irp_sl)); ++ irp_sl.params.power.state.device_state = state; ++ irp_sl.params.power.type = DevicePowerState; ++ if (state > PowerDeviceD0) { ++ status = IoSendIrpTopDev(pdo, IRP_MJ_POWER, IRP_MN_QUERY_POWER, ++ &irp_sl); ++ if (status != STATUS_SUCCESS) { ++ TRACE1("query of power to %d returns %08X", ++ state, status); ++ EXIT1(return status); ++ } ++ } ++ status = IoSendIrpTopDev(pdo, IRP_MJ_POWER, IRP_MN_SET_POWER, &irp_sl); ++ if (status != STATUS_SUCCESS) ++ WARNING("setting power to %d failed: %08X", state, status); ++ EXIT1(return status); ++} ++ ++static NTSTATUS pnp_start_device(struct wrap_device *wd) ++{ ++ struct device_object *fdo; ++ struct device_object *pdo; ++ struct io_stack_location irp_sl; ++ NTSTATUS status; ++ ++ pdo = wd->pdo; ++ /* TODO: for now we use same resources for both translated ++ * resources and raw resources */ ++ memset(&irp_sl, 0, sizeof(irp_sl)); ++ irp_sl.params.start_device.allocated_resources = ++ wd->resource_list; ++ irp_sl.params.start_device.allocated_resources_translated = ++ wd->resource_list; ++ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_START_DEVICE, &irp_sl); ++ fdo = IoGetAttachedDevice(pdo); ++ fdo->drv_obj->drv_ext->count++; ++ if (status != STATUS_SUCCESS) ++ WARNING("Windows driver couldn't initialize the device (%08X)", ++ status); ++ EXIT1(return status); ++} ++ ++#if 0 ++static NTSTATUS pnp_stop_device(struct wrap_device *wd) ++{ ++ struct device_object *pdo; ++ NTSTATUS status; ++ ++ pdo = wd->pdo; ++ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_QUERY_STOP_DEVICE, ++ NULL); ++ if (status != STATUS_SUCCESS) ++ WARNING("status: %08X", status); ++ /* for now we ignore query status */ ++ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_STOP_DEVICE, NULL); ++ if (status != STATUS_SUCCESS) ++ WARNING("status: %08X", status); ++ if (status != STATUS_SUCCESS) ++ WARNING("status: %08X", status); ++ EXIT2(return status); ++} ++#endif ++ ++static NTSTATUS pnp_remove_device(struct wrap_device *wd) ++{ ++ struct device_object *pdo, *fdo; ++ struct driver_object *fdo_drv_obj; ++ NTSTATUS status; ++ ++ pdo = wd->pdo; ++ fdo = IoGetAttachedDevice(pdo); ++ fdo_drv_obj = fdo->drv_obj; ++ TRACE2("%p, %p, %p", pdo, fdo, fdo_drv_obj); ++ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_QUERY_REMOVE_DEVICE, ++ NULL); ++ if (status != STATUS_SUCCESS) ++ WARNING("status: %08X", status); ++ ++ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_REMOVE_DEVICE, NULL); ++ if (status != STATUS_SUCCESS) ++ WARNING("status: %08X", status); ++ /* TODO: should we use count in drv_ext or driver's Object ++ * header reference count to keep count of devices associated ++ * with a driver? */ ++ if (status == STATUS_SUCCESS) ++ fdo_drv_obj->drv_ext->count--; ++ TRACE1("count: %d", fdo_drv_obj->drv_ext->count); ++ if ((LONG)fdo_drv_obj->drv_ext->count < 0) ++ WARNING("wrong count: %d", fdo_drv_obj->drv_ext->count); ++ if (fdo_drv_obj->drv_ext->count == 0) { ++ struct wrap_driver *wrap_driver; ++ TRACE1("unloading driver: %p", fdo_drv_obj); ++ wrap_driver = ++ IoGetDriverObjectExtension(fdo_drv_obj, ++ (void *)WRAP_DRIVER_CLIENT_ID); ++ if (fdo_drv_obj->unload) ++ LIN2WIN1(fdo_drv_obj->unload, fdo_drv_obj); ++ if (wrap_driver) { ++ mutex_lock(&loader_mutex); ++ unload_wrap_driver(wrap_driver); ++ mutex_unlock(&loader_mutex); ++ } else ++ ERROR("couldn't get wrap_driver"); ++ ObDereferenceObject(fdo_drv_obj); ++ } ++ IoDeleteDevice(pdo); ++ unload_wrap_device(wd); ++ EXIT1(return status); ++} ++ ++WIN_FUNC_DECL(IoInvalidDeviceRequest,2) ++ ++static struct device_object *alloc_pdo(struct driver_object *drv_obj) ++{ ++ struct device_object *pdo; ++ NTSTATUS status; ++ int i; ++ struct ansi_string ansi_name; ++ struct unicode_string unicode_name; ++ ++ RtlInitAnsiString(&ansi_name, "NDISpdo"); ++ if (RtlAnsiStringToUnicodeString(&unicode_name, &ansi_name, TRUE) == ++ STATUS_SUCCESS) { ++ status = IoCreateDevice(drv_obj, 0, &unicode_name, ++ FILE_DEVICE_UNKNOWN, ++ FILE_AUTOGENERATED_DEVICE_NAME, ++ FALSE, &pdo); ++ RtlFreeUnicodeString(&unicode_name); ++ } else { ++ status = IoCreateDevice(drv_obj, 0, NULL, ++ FILE_DEVICE_UNKNOWN, ++ FILE_AUTOGENERATED_DEVICE_NAME, ++ FALSE, &pdo); ++ } ++ TRACE1("%p, %d, %p", drv_obj, status, pdo); ++ if (status != STATUS_SUCCESS) ++ return NULL; ++ /* dispatch routines are called as Windows functions */ ++ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) ++ drv_obj->major_func[i] = WIN_FUNC_PTR(IoInvalidDeviceRequest,2); ++ drv_obj->major_func[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ++ WIN_FUNC_PTR(pdoDispatchDeviceControl,2); ++ drv_obj->major_func[IRP_MJ_DEVICE_CONTROL] = ++ WIN_FUNC_PTR(pdoDispatchDeviceControl,2); ++ drv_obj->major_func[IRP_MJ_POWER] = WIN_FUNC_PTR(pdoDispatchPower,2); ++ drv_obj->major_func[IRP_MJ_PNP] = WIN_FUNC_PTR(pdoDispatchPnp,2); ++ return pdo; ++} ++ ++static int wrap_pnp_start_device(struct wrap_device *wd) ++{ ++ struct wrap_driver *driver; ++ struct device_object *pdo; ++ struct driver_object *pdo_drv_obj; ++ ++ ENTER1("wd: %p", wd); ++ ++ if (!((wrap_is_pci_bus(wd->dev_bus)) || ++ (wrap_is_usb_bus(wd->dev_bus)))) { ++ ERROR("bus type %d (%d) not supported", ++ WRAP_BUS(wd->dev_bus), wd->dev_bus); ++ EXIT1(return -EINVAL); ++ } ++ driver = load_wrap_driver(wd); ++ if (!driver) ++ return -ENODEV; ++ ++ wd->driver = driver; ++ wd->dev_bus = WRAP_DEVICE_BUS(driver->dev_type, WRAP_BUS(wd->dev_bus)); ++ TRACE1("dev type: %d, bus type: %d, %d", WRAP_DEVICE(wd->dev_bus), ++ WRAP_BUS(wd->dev_bus), wd->dev_bus); ++ TRACE1("%d, %d", driver->dev_type, wrap_is_usb_bus(wd->dev_bus)); ++ /* first create pdo */ ++ if (wrap_is_pci_bus(wd->dev_bus)) ++ pdo_drv_obj = find_bus_driver("PCI"); ++ else // if (wrap_is_usb_bus(wd->dev_bus)) ++ pdo_drv_obj = find_bus_driver("USB"); ++ if (!pdo_drv_obj) ++ return -EINVAL; ++ pdo = alloc_pdo(pdo_drv_obj); ++ if (!pdo) ++ return -ENOMEM; ++ wd->pdo = pdo; ++ pdo->reserved = wd; ++ if (WRAP_DEVICE(wd->dev_bus) == WRAP_NDIS_DEVICE) { ++ if (init_ndis_driver(driver->drv_obj)) { ++ IoDeleteDevice(pdo); ++ return -EINVAL; ++ } ++ } ++ TRACE1("%p", driver->drv_obj->drv_ext->add_device); ++ if (driver->drv_obj->drv_ext->add_device(driver->drv_obj, pdo) != ++ STATUS_SUCCESS) { ++ IoDeleteDevice(pdo); ++ return -ENOMEM; ++ } ++ if (pnp_start_device(wd) != STATUS_SUCCESS) { ++ /* TODO: we need proper cleanup, to deallocate memory, ++ * for example */ ++ pnp_remove_device(wd); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int wrap_pnp_start_pci_device(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct load_device load_device; ++ struct wrap_device *wd; ++ ++ ENTER1("called for %04x:%04x:%04x:%04x", pdev->vendor, pdev->device, ++ pdev->subsystem_vendor, pdev->subsystem_device); ++ ++ load_device.bus = WRAP_PCI_BUS; ++ load_device.vendor = pdev->vendor; ++ load_device.device = pdev->device; ++ load_device.subvendor = pdev->subsystem_vendor; ++ load_device.subdevice = pdev->subsystem_device; ++ wd = load_wrap_device(&load_device); ++ if (!wd) ++ EXIT1(return -ENODEV); ++ wd->pci.pdev = pdev; ++ return wrap_pnp_start_device(wd); ++} ++ ++void wrap_pnp_remove_pci_device(struct pci_dev *pdev) ++{ ++ struct wrap_device *wd; ++ ++ wd = (struct wrap_device *)pci_get_drvdata(pdev); ++ ENTER1("%p, %p", pdev, wd); ++ if (!wd) ++ EXIT1(return); ++ pnp_remove_device(wd); ++} ++ ++int wrap_pnp_suspend_pci_device(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct wrap_device *wd; ++ ++ wd = (struct wrap_device *)pci_get_drvdata(pdev); ++ return pnp_set_device_power_state(wd, PowerDeviceD3); ++} ++ ++int wrap_pnp_resume_pci_device(struct pci_dev *pdev) ++{ ++ struct wrap_device *wd; ++ ++ wd = (struct wrap_device *)pci_get_drvdata(pdev); ++ return pnp_set_device_power_state(wd, PowerDeviceD0); ++} ++ ++#ifdef ENABLE_USB ++int wrap_pnp_start_usb_device(struct usb_interface *intf, ++ const struct usb_device_id *usb_id) ++{ ++ struct wrap_device *wd; ++ int ret; ++ struct usb_device *udev = interface_to_usbdev(intf); ++ ENTER1("%04x, %04x, %04x", udev->descriptor.idVendor, ++ udev->descriptor.idProduct, udev->descriptor.bDeviceClass); ++ ++ /* USB device (e.g., RNDIS) may have multiple interfaces; ++ initialize one interface only (is there a way to know which ++ of these interfaces is for network?) */ ++ ++ if ((wd = get_wrap_device(udev, WRAP_USB_BUS))) { ++ TRACE1("device already initialized: %p", wd); ++ usb_set_intfdata(intf, NULL); ++ ret = 0; ++ } else { ++ struct load_device load_device; ++ ++ load_device.bus = WRAP_USB_BUS; ++ load_device.vendor = le16_to_cpu(udev->descriptor.idVendor); ++ load_device.device = le16_to_cpu(udev->descriptor.idProduct); ++ load_device.subvendor = 0; ++ load_device.subdevice = 0; ++ wd = load_wrap_device(&load_device); ++ TRACE2("%p", wd); ++ if (wd) { ++ /* some devices (e.g., TI 4150, RNDIS) need ++ * full reset */ ++ ret = usb_reset_device(udev); ++ if (ret) ++ WARNING("reset failed: %d", ret); ++ usb_set_intfdata(intf, wd); ++ wd->usb.intf = intf; ++ wd->usb.udev = udev; ++ ret = wrap_pnp_start_device(wd); ++ } else ++ ret = -ENODEV; ++ } ++ ++ TRACE2("ret: %d", ret); ++ if (ret) ++ EXIT1(return ret); ++ else ++ return 0; ++} ++ ++void wrap_pnp_remove_usb_device(struct usb_interface *intf) ++{ ++ struct wrap_device *wd; ++ ++ wd = (struct wrap_device *)usb_get_intfdata(intf); ++ TRACE1("%p, %p", intf, wd); ++ if (wd == NULL) ++ EXIT1(return); ++ usb_set_intfdata(intf, NULL); ++ wd->usb.intf = NULL; ++ pnp_remove_device(wd); ++} ++ ++int wrap_pnp_suspend_usb_device(struct usb_interface *intf, pm_message_t state) ++{ ++ struct wrap_device *wd; ++ ++ wd = usb_get_intfdata(intf); ++ ENTER1("%p, %p", intf, wd); ++ if (!wd) ++ EXIT1(return 0); ++ if (pnp_set_device_power_state(wd, PowerDeviceD3)) ++ return -1; ++ return 0; ++} ++ ++int wrap_pnp_resume_usb_device(struct usb_interface *intf) ++{ ++ struct wrap_device *wd; ++ wd = usb_get_intfdata(intf); ++ ENTER1("%p, %p", intf, wd); ++ if (!wd) ++ EXIT1(return 0); ++ if (pnp_set_device_power_state(wd, PowerDeviceD0)) ++ return -1; ++ return 0; ++} ++ ++#endif // USB +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.h +--- linux-3.12.2/3rdparty/ndiswrapper/pnp.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,36 @@ ++/* ++ * Copyright (C) 2005 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _PNP_H_ ++#define _PNP_H_ ++ ++#include "ntoskernel.h" ++#include "ndis.h" ++#include "wrapndis.h" ++ ++int wrap_pnp_start_pci_device(struct pci_dev *pdev, ++ const struct pci_device_id *ent); ++void wrap_pnp_remove_pci_device(struct pci_dev *pdev); ++int wrap_pnp_suspend_pci_device(struct pci_dev *pdev, pm_message_t state); ++int wrap_pnp_resume_pci_device(struct pci_dev *pdev); ++ ++int wrap_pnp_start_usb_device(struct usb_interface *intf, ++ const struct usb_device_id *usb_id); ++void wrap_pnp_remove_usb_device(struct usb_interface *intf); ++int wrap_pnp_suspend_usb_device(struct usb_interface *intf, ++ pm_message_t state); ++int wrap_pnp_resume_usb_device(struct usb_interface *intf); ++ ++#endif +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/proc.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/proc.c +--- linux-3.12.2/3rdparty/ndiswrapper/proc.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/proc.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,588 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include "ndis.h" ++#include "iw_ndis.h" ++#include "wrapndis.h" ++#include "pnp.h" ++#include "wrapper.h" ++ ++#define MAX_PROC_STR_LEN 32 ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) ++static kuid_t proc_kuid; ++static kgid_t proc_kgid; ++#else ++#define proc_kuid proc_uid ++#define proc_kgid proc_gid ++#define kuid_t uid_t ++#define kgid_t gid_t ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++static inline struct inode *file_inode(struct file *f) ++{ ++ return f->f_dentry->d_inode; ++} ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) ++static inline struct inode *file_inode(struct file *f) ++{ ++ return f->f_path.dentry->d_inode; ++} ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) ++static inline void proc_set_user(struct proc_dir_entry *de, kuid_t uid, ++ kgid_t gid) ++{ ++ de->uid = uid; ++ de->gid = gid; ++} ++ ++static inline void proc_remove(struct proc_dir_entry *de) ++{ ++ if (de) ++ remove_proc_entry(de->name, de->parent); ++} ++ ++static inline void *PDE_DATA(const struct inode *inode) ++{ ++ return PDE(inode)->data; ++} ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) ++static inline struct proc_dir_entry *proc_create_data(const char *name, ++ umode_t mode, struct proc_dir_entry *parent, ++ struct file_operations *fops, void *data) ++{ ++ struct proc_dir_entry *de; ++ ++ de = create_proc_entry(name, mode, parent); ++ if (de) { ++ de->data = data; ++ de->proc_fops = fops; ++ } ++ ++ return de; ++} ++#endif ++ ++static int do_proc_make_entry(const char *name, umode_t mode, ++ struct proc_dir_entry *parent, ++ struct file_operations *fops, kuid_t uid, ++ kgid_t gid, struct ndis_device *wnd) ++{ ++ struct proc_dir_entry *de; ++ ++ de = proc_create_data(name, mode, parent, fops, wnd); ++ if (de == NULL) { ++ ERROR("couldn't create proc entry for '%s'", name); ++ return -ENOMEM; ++ } ++ proc_set_user(de, uid, gid); ++ return 0; ++} ++ ++#define PROC_DECLARE_RO(name) \ ++ static int proc_##name##_open(struct inode *inode, struct file *file) \ ++ { \ ++ return single_open(file, proc_##name##_read, PDE_DATA(inode)); \ ++ } \ ++ static struct file_operations name##_fops = { \ ++ .owner = THIS_MODULE, \ ++ .open = proc_##name##_open, \ ++ .read = seq_read, \ ++ .llseek = seq_lseek, \ ++ .release = single_release, \ ++ }; ++ ++#define PROC_DECLARE_RW(name) \ ++ static int proc_##name##_open(struct inode *inode, struct file *file) \ ++ { \ ++ return single_open(file, proc_##name##_read, PDE_DATA(inode)); \ ++ } \ ++ static struct file_operations name##_fops = { \ ++ .owner = THIS_MODULE, \ ++ .open = proc_##name##_open, \ ++ .read = seq_read, \ ++ .llseek = seq_lseek, \ ++ .release = single_release, \ ++ .write = proc_##name##_write, \ ++ }; ++ ++#define proc_make_entry_ro(name, parent, wnd) \ ++ do_proc_make_entry(#name, S_IFREG | S_IRUSR | S_IRGRP, parent, \ ++ &name##_fops, proc_kuid, proc_kgid, wnd) ++#define proc_make_entry_rw(name, parent, wnd) \ ++ do_proc_make_entry(#name, \ ++ S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, \ ++ parent, &name##_fops, proc_kuid, proc_kgid, wnd) ++ ++#define add_text(fmt, ...) seq_printf(sf, fmt, ##__VA_ARGS__) ++ ++static struct proc_dir_entry *wrap_procfs_entry; ++ ++static int proc_stats_read(struct seq_file *sf, void *v) ++{ ++ struct ndis_device *wnd = (struct ndis_device *)sf->private; ++ struct ndis_wireless_stats stats; ++ NDIS_STATUS res; ++ ndis_rssi rssi; ++ ++ res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi)); ++ if (!res) ++ add_text("signal_level=%d dBm\n", (s32)rssi); ++ ++ res = mp_query(wnd, OID_802_11_STATISTICS, &stats, sizeof(stats)); ++ if (!res) { ++ add_text("tx_frames=%llu\n", stats.tx_frag); ++ add_text("tx_multicast_frames=%llu\n", stats.tx_multi_frag); ++ add_text("tx_failed=%llu\n", stats.failed); ++ add_text("tx_retry=%llu\n", stats.retry); ++ add_text("tx_multi_retry=%llu\n", stats.multi_retry); ++ add_text("tx_rtss_success=%llu\n", stats.rtss_succ); ++ add_text("tx_rtss_fail=%llu\n", stats.rtss_fail); ++ add_text("ack_fail=%llu\n", stats.ack_fail); ++ add_text("frame_duplicates=%llu\n", stats.frame_dup); ++ add_text("rx_frames=%llu\n", stats.rx_frag); ++ add_text("rx_multicast_frames=%llu\n", stats.rx_multi_frag); ++ add_text("fcs_errors=%llu\n", stats.fcs_err); ++ } ++ ++ return 0; ++} ++ ++PROC_DECLARE_RO(stats) ++ ++static int proc_encr_read(struct seq_file *sf, void *v) ++{ ++ struct ndis_device *wnd = (struct ndis_device *)sf->private; ++ int i, encr_status, auth_mode, infra_mode; ++ NDIS_STATUS res; ++ struct ndis_essid essid; ++ mac_address ap_address; ++ ++ res = mp_query(wnd, OID_802_11_BSSID, ++ &ap_address, sizeof(ap_address)); ++ if (res) ++ memset(ap_address, 0, ETH_ALEN); ++ add_text("ap_address=" MACSTRSEP "\n", MAC2STR(ap_address)); ++ ++ res = mp_query(wnd, OID_802_11_SSID, &essid, sizeof(essid)); ++ if (!res) ++ add_text("essid=%.*s\n", essid.length, essid.essid); ++ ++ res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &encr_status); ++ if (!res) { ++ typeof(&wnd->encr_info.keys[0]) tx_key; ++ add_text("tx_key=%u\n", wnd->encr_info.tx_key_index); ++ add_text("key="); ++ tx_key = &wnd->encr_info.keys[wnd->encr_info.tx_key_index]; ++ if (tx_key->length > 0) ++ for (i = 0; i < tx_key->length; i++) ++ add_text("%2.2X", tx_key->key[i]); ++ else ++ add_text("off"); ++ add_text("\n"); ++ add_text("encr_mode=%d\n", encr_status); ++ } ++ res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &auth_mode); ++ if (!res) ++ add_text("auth_mode=%d\n", auth_mode); ++ res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &infra_mode); ++ add_text("mode=%s\n", (infra_mode == Ndis802_11IBSS) ? "adhoc" : ++ (infra_mode == Ndis802_11Infrastructure) ? "managed" : "auto"); ++ ++ return 0; ++} ++ ++PROC_DECLARE_RO(encr) ++ ++static int proc_hw_read(struct seq_file *sf, void *v) ++{ ++ struct ndis_device *wnd = (struct ndis_device *)sf->private; ++ struct ndis_configuration config; ++ enum ndis_power power_mode; ++ NDIS_STATUS res; ++ ndis_tx_power_level tx_power; ++ ULONG bit_rate; ++ ndis_rts_threshold rts_threshold; ++ ndis_fragmentation_threshold frag_threshold; ++ ndis_antenna antenna; ++ ULONG packet_filter; ++ int n; ++ mac_address mac; ++ char *hw_status[] = {"ready", "initializing", "resetting", "closing", ++ "not ready"}; ++ ++ res = mp_query_int(wnd, OID_GEN_HARDWARE_STATUS, &n); ++ if (res == NDIS_STATUS_SUCCESS && n >= 0 && n < ARRAY_SIZE(hw_status)) ++ add_text("status=%s\n", hw_status[n]); ++ ++ res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac)); ++ if (!res) ++ add_text("mac: " MACSTRSEP "\n", MAC2STR(mac)); ++ res = mp_query(wnd, OID_802_11_CONFIGURATION, &config, sizeof(config)); ++ if (!res) { ++ add_text("beacon_period=%u msec\n", config.beacon_period); ++ add_text("atim_window=%u msec\n", config.atim_window); ++ add_text("frequency=%u kHz\n", config.ds_config); ++ add_text("hop_pattern=%u\n", config.fh_config.hop_pattern); ++ add_text("hop_set=%u\n", config.fh_config.hop_set); ++ add_text("dwell_time=%u msec\n", config.fh_config.dwell_time); ++ } ++ ++ res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, ++ &tx_power, sizeof(tx_power)); ++ if (!res) ++ add_text("tx_power=%u mW\n", tx_power); ++ ++ res = mp_query(wnd, OID_GEN_LINK_SPEED, &bit_rate, sizeof(bit_rate)); ++ if (!res) ++ add_text("bit_rate=%u kBps\n", (u32)bit_rate / 10); ++ ++ res = mp_query(wnd, OID_802_11_RTS_THRESHOLD, ++ &rts_threshold, sizeof(rts_threshold)); ++ if (!res) ++ add_text("rts_threshold=%u bytes\n", rts_threshold); ++ ++ res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, ++ &frag_threshold, sizeof(frag_threshold)); ++ if (!res) ++ add_text("frag_threshold=%u bytes\n", frag_threshold); ++ ++ res = mp_query_int(wnd, OID_802_11_POWER_MODE, &power_mode); ++ if (!res) ++ add_text("power_mode=%s\n", ++ (power_mode == NDIS_POWER_OFF) ? "always_on" : ++ (power_mode == NDIS_POWER_MAX) ? "max_savings" : ++ "min_savings"); ++ ++ res = mp_query(wnd, OID_802_11_NUMBER_OF_ANTENNAS, ++ &antenna, sizeof(antenna)); ++ if (!res) ++ add_text("num_antennas=%u\n", antenna); ++ ++ res = mp_query(wnd, OID_802_11_TX_ANTENNA_SELECTED, ++ &antenna, sizeof(antenna)); ++ if (!res) ++ add_text("tx_antenna=%u\n", antenna); ++ ++ res = mp_query(wnd, OID_802_11_RX_ANTENNA_SELECTED, ++ &antenna, sizeof(antenna)); ++ if (!res) ++ add_text("rx_antenna=%u\n", antenna); ++ ++ add_text("encryption_modes=%s%s%s%s%s%s%s\n", ++ test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr) ? ++ "WEP" : "none", ++ test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr) ? ++ "; TKIP with WPA" : "", ++ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? ++ ", WPA2" : "", ++ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? ++ ", WPA2PSK" : "", ++ test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr) ? ++ "; AES/CCMP with WPA" : "", ++ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? ++ ", WPA2" : "", ++ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? ++ ", WPA2PSK" : ""); ++ ++ res = mp_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter); ++ if (!res) { ++ if (packet_filter != wnd->packet_filter) ++ WARNING("wrong packet_filter? 0x%08x, 0x%08x\n", ++ packet_filter, wnd->packet_filter); ++ add_text("packet_filter: 0x%08x\n", packet_filter); ++ } ++ ++ return 0; ++} ++ ++PROC_DECLARE_RO(hw) ++ ++static int proc_settings_read(struct seq_file *sf, void *v) ++{ ++ struct ndis_device *wnd = (struct ndis_device *)sf->private; ++ struct wrap_device_setting *setting; ++ ++ add_text("hangcheck_interval=%d\n", (hangcheck_interval == 0) ? ++ (wnd->hangcheck_interval / HZ) : -1); ++ ++ list_for_each_entry(setting, &wnd->wd->settings, list) { ++ add_text("%s=%s\n", setting->name, setting->value); ++ } ++ ++ list_for_each_entry(setting, &wnd->wd->driver->settings, list) { ++ add_text("%s=%s\n", setting->name, setting->value); ++ } ++ ++ return 0; ++} ++ ++static ssize_t proc_settings_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ndis_device *wnd = PDE_DATA(file_inode(file)); ++ char setting[MAX_PROC_STR_LEN], *p; ++ unsigned int i; ++ NDIS_STATUS res; ++ ++ if (count > MAX_PROC_STR_LEN) ++ return -EINVAL; ++ ++ memset(setting, 0, sizeof(setting)); ++ if (copy_from_user(setting, buf, count)) ++ return -EFAULT; ++ ++ if ((p = strchr(setting, '\n'))) ++ *p = 0; ++ ++ if ((p = strchr(setting, '='))) ++ *p = 0; ++ ++ if (!strcmp(setting, "hangcheck_interval")) { ++ if (!p) ++ return -EINVAL; ++ p++; ++ i = simple_strtol(p, NULL, 10); ++ hangcheck_del(wnd); ++ if (i > 0) { ++ wnd->hangcheck_interval = i * HZ; ++ hangcheck_add(wnd); ++ } ++ } else if (!strcmp(setting, "suspend")) { ++ if (!p) ++ return -EINVAL; ++ p++; ++ i = simple_strtol(p, NULL, 10); ++ if (i <= 0 || i > 3) ++ return -EINVAL; ++ i = -1; ++ if (wrap_is_pci_bus(wnd->wd->dev_bus)) ++ i = wrap_pnp_suspend_pci_device(wnd->wd->pci.pdev, ++ PMSG_SUSPEND); ++ else if (wrap_is_usb_bus(wnd->wd->dev_bus)) ++ i = wrap_pnp_suspend_usb_device(wnd->wd->usb.intf, ++ PMSG_SUSPEND); ++ if (i) ++ return -EINVAL; ++ } else if (!strcmp(setting, "resume")) { ++ i = -1; ++ if (wrap_is_pci_bus(wnd->wd->dev_bus)) ++ i = wrap_pnp_resume_pci_device(wnd->wd->pci.pdev); ++ else if (wrap_is_usb_bus(wnd->wd->dev_bus)) ++ i = wrap_pnp_resume_usb_device(wnd->wd->usb.intf); ++ if (i) ++ return -EINVAL; ++ } else if (!strcmp(setting, "stats_enabled")) { ++ if (!p) ++ return -EINVAL; ++ p++; ++ i = simple_strtol(p, NULL, 10); ++ if (i > 0) ++ wnd->iw_stats_enabled = TRUE; ++ else ++ wnd->iw_stats_enabled = FALSE; ++ } else if (!strcmp(setting, "packet_filter")) { ++ if (!p) ++ return -EINVAL; ++ p++; ++ i = simple_strtol(p, NULL, 10); ++ res = mp_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, i); ++ if (res) ++ WARNING("setting packet_filter failed: %08X", res); ++ } else if (!strcmp(setting, "reinit")) { ++ if (ndis_reinit(wnd) != NDIS_STATUS_SUCCESS) ++ return -EFAULT; ++ } else { ++ struct ndis_configuration_parameter param; ++ struct unicode_string key; ++ struct ansi_string ansi; ++ ++ if (!p) ++ return -EINVAL; ++ p++; ++ RtlInitAnsiString(&ansi, p); ++ if (RtlAnsiStringToUnicodeString(¶m.data.string, &ansi, ++ TRUE) != STATUS_SUCCESS) ++ EXIT1(return -EFAULT); ++ param.type = NdisParameterString; ++ RtlInitAnsiString(&ansi, setting); ++ if (RtlAnsiStringToUnicodeString(&key, &ansi, ++ TRUE) != STATUS_SUCCESS) { ++ RtlFreeUnicodeString(¶m.data.string); ++ EXIT1(return -EINVAL); ++ } ++ NdisWriteConfiguration(&res, wnd->nmb, &key, ¶m); ++ RtlFreeUnicodeString(&key); ++ RtlFreeUnicodeString(¶m.data.string); ++ if (res != NDIS_STATUS_SUCCESS) ++ return -EFAULT; ++ } ++ return count; ++} ++ ++PROC_DECLARE_RW(settings) ++ ++int wrap_procfs_add_ndis_device(struct ndis_device *wnd) ++{ ++ int ret; ++ ++ if (wrap_procfs_entry == NULL) ++ return -ENOMEM; ++ ++ if (wnd->procfs_iface) { ++ ERROR("%s already registered?", wnd->net_dev->name); ++ return -EINVAL; ++ } ++ wnd->procfs_iface = proc_mkdir(wnd->net_dev->name, wrap_procfs_entry); ++ if (wnd->procfs_iface == NULL) { ++ ERROR("couldn't create proc directory"); ++ return -ENOMEM; ++ } ++ proc_set_user(wnd->procfs_iface, proc_kuid, proc_kgid); ++ ++ ret = proc_make_entry_ro(hw, wnd->procfs_iface, wnd); ++ if (ret) ++ goto err_hw; ++ ++ ret = proc_make_entry_ro(stats, wnd->procfs_iface, wnd); ++ if (ret) ++ goto err_stats; ++ ++ ret = proc_make_entry_ro(encr, wnd->procfs_iface, wnd); ++ if (ret) ++ goto err_encr; ++ ++ ret = proc_make_entry_rw(settings, wnd->procfs_iface, wnd); ++ if (ret) ++ goto err_settings; ++ ++ return 0; ++ ++err_settings: ++ remove_proc_entry("encr", wnd->procfs_iface); ++err_encr: ++ remove_proc_entry("stats", wnd->procfs_iface); ++err_stats: ++ remove_proc_entry("hw", wnd->procfs_iface); ++err_hw: ++ proc_remove(wnd->procfs_iface); ++ wnd->procfs_iface = NULL; ++ return -ENOMEM; ++} ++ ++void wrap_procfs_remove_ndis_device(struct ndis_device *wnd) ++{ ++ struct proc_dir_entry *procfs_iface = xchg(&wnd->procfs_iface, NULL); ++ ++ if (procfs_iface == NULL) ++ return; ++ remove_proc_entry("hw", procfs_iface); ++ remove_proc_entry("stats", procfs_iface); ++ remove_proc_entry("encr", procfs_iface); ++ remove_proc_entry("settings", procfs_iface); ++ if (wrap_procfs_entry) ++ proc_remove(procfs_iface); ++} ++ ++static int proc_debug_read(struct seq_file *sf, void *v) ++{ ++#if ALLOC_DEBUG ++ enum alloc_type type; ++#endif ++ ++ add_text("%d\n", debug); ++#if ALLOC_DEBUG ++ for (type = 0; type < ALLOC_TYPE_MAX; type++) ++ add_text("total size of allocations in %s: %d\n", ++ alloc_type_name[type], alloc_size(type)); ++#endif ++ return 0; ++} ++ ++static ssize_t proc_debug_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ int i; ++ char setting[MAX_PROC_STR_LEN], *p; ++ ++ if (count > MAX_PROC_STR_LEN) ++ return -EINVAL; ++ ++ memset(setting, 0, sizeof(setting)); ++ if (copy_from_user(setting, buf, count)) ++ return -EFAULT; ++ ++ if ((p = strchr(setting, '\n'))) ++ *p = 0; ++ ++ if ((p = strchr(setting, '='))) ++ *p = 0; ++ ++ i = simple_strtol(setting, NULL, 10); ++ if (i >= 0 && i < 10) ++ debug = i; ++ else ++ return -EINVAL; ++ return count; ++} ++ ++PROC_DECLARE_RW(debug) ++ ++int wrap_procfs_init(void) ++{ ++ int ret; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) ++ struct user_namespace *ns = current_user_ns(); ++ proc_kuid = make_kuid(ns, proc_uid); ++ if (!uid_valid(proc_kuid)) { ++ ERROR("invalid UID\n"); ++ return -EINVAL; ++ } ++ proc_kgid = make_kgid(ns, proc_gid); ++ if (!gid_valid(proc_kgid)) { ++ ERROR("invalid GID\n"); ++ return -EINVAL; ++ } ++#endif ++ ++ wrap_procfs_entry = proc_mkdir(DRIVER_NAME, proc_net_root); ++ if (wrap_procfs_entry == NULL) { ++ ERROR("couldn't create procfs directory"); ++ return -ENOMEM; ++ } ++ proc_set_user(wrap_procfs_entry, proc_kuid, proc_kgid); ++ ++ ret = proc_make_entry_rw(debug, wrap_procfs_entry, NULL); ++ ++ return ret; ++} ++ ++void wrap_procfs_remove(void) ++{ ++ if (wrap_procfs_entry == NULL) ++ return; ++ remove_proc_entry("debug", wrap_procfs_entry); ++ proc_remove(wrap_procfs_entry); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/rtl.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/rtl.c +--- linux-3.12.2/3rdparty/ndiswrapper/rtl.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/rtl.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,715 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * Copyright (C) 2006-2007 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ntoskernel.h" ++#include "rtl_exports.h" ++ ++wstdcall SIZE_T WIN_FUNC(RtlCompareMemory,3) ++ (const void *a, const void *b, SIZE_T len) ++{ ++ size_t i; ++ char *x, *y; ++ ++ ENTER1("%p %p %zd", a, b, len); ++ x = (char *)a; ++ y = (char *)b; ++ /* MSDN says this should return number of bytes that compare as ++ * equal. This can be interpreted as either all bytes that are ++ * equal in 'len' bytes or that only until the bytes compare as ++ * not equal. Initially we had it the former way, but Realtek driver ++ * doesn't like it that way - it takes many attempts to associate ++ * with WPA. ReactOS returns the number of bytes that are equal ++ * before the first differing byte. ++ * According to lords at #reactos, that is the way it should be ++ * and that msdn is wrong about it! ++ */ ++ for (i = 0; i < len && x[i] == y[i]; i++) ++ ; ++ return i; ++} ++ ++wstdcall void WIN_FUNC(RtlCopyMemory,3) ++ (void *dst, const void *src, SIZE_T length) ++{ ++ memcpy(dst, src, length); ++} ++ ++wstdcall void WIN_FUNC(RtlZeroMemory,2) ++ (void *dst, SIZE_T length) ++{ ++ memset(dst, 0, length); ++} ++ ++wstdcall void WIN_FUNC(RtlSecureZeroMemory,2) ++ (void *dst, SIZE_T length) ++{ ++ memset(dst, 0, length); ++} ++ ++wstdcall void WIN_FUNC(RtlFillMemory,3) ++ (void *dest, SIZE_T length, UCHAR fill) ++{ ++ memset(dest, fill, length); ++} ++ ++wstdcall void WIN_FUNC(RtlMoveMemory,3) ++ (void *dest, const void *src, SIZE_T length) ++{ ++ memmove(dest, src, length); ++} ++ ++wstdcall LONG WIN_FUNC(RtlCompareString,3) ++ (const struct ansi_string *s1, const struct ansi_string *s2, ++ BOOLEAN case_insensitive) ++{ ++ unsigned int len; ++ LONG ret = 0; ++ const char *p1, *p2; ++ ++ ENTER2(""); ++ len = min(s1->length, s2->length); ++ p1 = s1->buf; ++ p2 = s2->buf; ++ if (case_insensitive) ++ while (!ret && len--) ++ ret = toupper(*p1++) - toupper(*p2++); ++ else ++ while (!ret && len--) ++ ret = *p1++ - *p2++; ++ if (!ret) ++ ret = s1->length - s2->length; ++ EXIT2(return ret); ++} ++ ++wstdcall LONG WIN_FUNC(RtlCompareUnicodeString,3) ++ (const struct unicode_string *s1, const struct unicode_string *s2, ++ BOOLEAN case_insensitive) ++{ ++ unsigned int len; ++ LONG ret = 0; ++ const wchar_t *p1, *p2; ++ ++ ENTER2(""); ++ ++ len = min(s1->length, s2->length) / sizeof(wchar_t); ++ p1 = s1->buf; ++ p2 = s2->buf; ++ if (case_insensitive) ++ while (!ret && len--) ++ ret = toupper((u8)*p1++) - toupper((u8)*p2++); ++ else ++ while (!ret && len--) ++ ret = (u8)*p1++ - (u8)*p2++; ++ if (!ret) ++ ret = s1->length - s2->length; ++ TRACE2("len: %d, ret: %d", len, ret); ++ EXIT2(return ret); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(RtlEqualString,3) ++ (const struct ansi_string *s1, const struct ansi_string *s2, ++ BOOLEAN case_insensitive) ++{ ++ ENTER1(""); ++ if (s1->length != s2->length) ++ return FALSE; ++ return !RtlCompareString(s1, s2, case_insensitive); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(RtlEqualUnicodeString,3) ++ (const struct unicode_string *s1, const struct unicode_string *s2, ++ BOOLEAN case_insensitive) ++{ ++ if (s1->length != s2->length) ++ return FALSE; ++ return !RtlCompareUnicodeString(s1, s2, case_insensitive); ++} ++ ++wstdcall void WIN_FUNC(RtlCopyUnicodeString,2) ++ (struct unicode_string *dst, struct unicode_string *src) ++{ ++ ENTER1("%p, %p", dst, src); ++ if (src && src->buf && dst->buf) { ++ dst->length = min(src->length, dst->max_length); ++ memcpy(dst->buf, src->buf, dst->length); ++ if (dst->length < dst->max_length) ++ dst->buf[dst->length / sizeof(dst->buf[0])] = 0; ++ } else ++ dst->length = 0; ++ EXIT1(return); ++} ++ ++wstdcall void WIN_FUNC(RtlCopyString,2) ++ (struct ansi_string *dst, struct ansi_string *src) ++{ ++ ENTER1("%p, %p", dst, src); ++ if (src && src->buf && dst->buf) { ++ dst->length = min(src->length, dst->max_length); ++ memcpy(dst->buf, src->buf, dst->length); ++ if (dst->length < dst->max_length) ++ dst->buf[dst->length] = 0; ++ } else ++ dst->length = 0; ++ EXIT1(return); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlAppendUnicodeToString,2) ++ (struct unicode_string *dst, wchar_t *src) ++{ ++ if (src) { ++ int len; ++ for (len = 0; src[len]; len++) ++ ; ++ if (dst->length + (len * sizeof(dst->buf[0])) > dst->max_length) ++ return STATUS_BUFFER_TOO_SMALL; ++ memcpy(&dst->buf[dst->length], src, len * sizeof(dst->buf[0])); ++ dst->length += len * sizeof(dst->buf[0]); ++ if (dst->max_length > dst->length) ++ dst->buf[dst->length / sizeof(dst->buf[0])] = 0; ++ } ++ return STATUS_SUCCESS; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlAppendUnicodeStringToString,2) ++ (struct unicode_string *dst, struct unicode_string *src) ++{ ++ if (dst->max_length < src->length + dst->length) ++ return STATUS_BUFFER_TOO_SMALL; ++ if (src->length) { ++ memcpy(&dst->buf[dst->length], src->buf, src->length); ++ dst->length += src->length; ++ if (dst->max_length > dst->length) ++ dst->buf[dst->length / sizeof(dst->buf[0])] = 0; ++ } ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall ULONG WIN_FUNC(RtlxAnsiStringToUnicodeSize,1) ++ (const struct ansi_string *string) ++{ ++ int i; ++ ++ for (i = 0; i < string->max_length && string->buf[i]; i++) ++ ; ++ return i * sizeof(wchar_t); ++} ++ ++wstdcall ULONG WIN_FUNC(RtlxUnicodeStringToAnsiSize,1) ++ (const struct unicode_string *string) ++{ ++ int i; ++ ++ for (i = 0; i < string->max_length && string->buf[i]; i++) ++ ; ++ return i; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlAnsiStringToUnicodeString,3) ++ (struct unicode_string *dst, const struct ansi_string *src, ++ BOOLEAN alloc) ++{ ++ int i, n; ++ ++ n = RtlxAnsiStringToUnicodeSize(src); ++ TRACE2("%d, %d, %d, %d, %p", n, dst->max_length, src->length, ++ src->max_length, src->buf); ++ if (alloc == TRUE) { ++#if 0 ++ if (n == 0) { ++ dst->length = dst->max_length = 0; ++ dst->buf = NULL; ++ EXIT2(return STATUS_SUCCESS); ++ } ++#endif ++ dst->max_length = n + sizeof(dst->buf[0]); ++ dst->buf = ExAllocatePoolWithTag(NonPagedPool, ++ dst->max_length, 0); ++ if (!dst->buf) { ++ dst->max_length = dst->length = 0; ++ EXIT2(return STATUS_NO_MEMORY); ++ } ++ } else if (dst->max_length < n) ++ EXIT2(return STATUS_BUFFER_TOO_SMALL); ++ ++ dst->length = n; ++ n /= sizeof(dst->buf[0]); ++ for (i = 0; i < n; i++) ++ dst->buf[i] = src->buf[i]; ++ if (i * sizeof(dst->buf[0]) < dst->max_length) ++ dst->buf[i] = 0; ++ TRACE2("dst: length: %d, max_length: %d, string: %p", ++ dst->length, dst->max_length, src->buf); ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlUnicodeStringToAnsiString,3) ++ (struct ansi_string *dst, const struct unicode_string *src, ++ BOOLEAN alloc) ++{ ++ int i, n; ++ ++ n = RtlxUnicodeStringToAnsiSize(src); ++ TRACE2("%d, %d, %d, %d, %p", n, dst->max_length, src->length, ++ src->max_length, src->buf); ++ if (alloc == TRUE) { ++#if 0 ++ if (n == 0) { ++ dst->length = dst->max_length = 0; ++ dst->buf = NULL; ++ EXIT2(return STATUS_SUCCESS); ++ } ++#endif ++ dst->max_length = n + sizeof(dst->buf[0]); ++ dst->buf = ExAllocatePoolWithTag(NonPagedPool, ++ dst->max_length, 0); ++ if (!dst->buf) { ++ dst->max_length = dst->length = 0; ++ EXIT1(return STATUS_NO_MEMORY); ++ } ++ } else if (dst->max_length < n) ++ EXIT2(return STATUS_BUFFER_TOO_SMALL); ++ ++ dst->length = n; ++ for (i = 0; i < n; i++) ++ dst->buf[i] = src->buf[i]; ++ if (i < dst->max_length) ++ dst->buf[i] = 0; ++ TRACE2("string: %p, len: %d(%d)", dst->buf, dst->length, ++ dst->max_length); ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlUnicodeStringToInteger,3) ++ (struct unicode_string *ustring, ULONG base, ULONG *value) ++{ ++ int i, sign = 1; ++ ULONG res; ++ typeof(ustring->buf) string; ++ ++ if (ustring->length == 0) { ++ *value = 0; ++ return STATUS_SUCCESS; ++ } ++ ++ string = ustring->buf; ++ i = 0; ++ while (i < (ustring->length / sizeof(*string)) && string[i] == ' ') ++ i++; ++ if (string[i] == '+') ++ i++; ++ else if (string[i] == '-') { ++ i++; ++ sign = -1; ++ } ++ if (base == 0) { ++ base = 10; ++ if (i <= ((ustring->length / sizeof(*string)) - 2) && ++ string[i] == '0') { ++ i++; ++ if (string[i] == 'b') { ++ base = 2; ++ i++; ++ } else if (string[i] == 'o') { ++ base = 8; ++ i++; ++ } else if (string[i] == 'x') { ++ base = 16; ++ i++; ++ } ++ } ++ } ++ if (!(base == 2 || base == 8 || base == 10 || base == 16)) ++ EXIT2(return STATUS_INVALID_PARAMETER); ++ ++ for (res = 0; i < (ustring->length / sizeof(*string)); i++) { ++ int v; ++ if (isdigit((char)string[i])) ++ v = string[i] - '0'; ++ else if (isxdigit((char)string[i])) ++ v = tolower((char)string[i]) - 'a' + 10; ++ else ++ v = base; ++ if (v >= base) ++ EXIT2(return STATUS_INVALID_PARAMETER); ++ res = res * base + v; ++ } ++ *value = sign * res; ++ EXIT3(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlCharToInteger,3) ++ (const char *string, ULONG base, ULONG *value) ++{ ++ int sign = 1; ++ ULONG res; ++ ++ if (!string || !value) ++ EXIT2(return STATUS_INVALID_PARAMETER); ++ while (*string == ' ') ++ string++; ++ if (*string == '+') ++ string++; ++ else if (*string == '-') { ++ string++; ++ sign = -1; ++ } ++ if (base == 0) { ++ base = 10; ++ if (*string == '0') { ++ string++; ++ if (*string == 'b') { ++ base = 2; ++ string++; ++ } else if (*string == 'o') { ++ base = 8; ++ string++; ++ } else if (*string == 'x') { ++ base = 16; ++ string++; ++ } ++ } ++ } ++ if (!(base == 2 || base == 8 || base == 10 || base == 16)) ++ EXIT2(return STATUS_INVALID_PARAMETER); ++ ++ for (res = 0; *string; string++) { ++ int v; ++ if (isdigit(*string)) ++ v = *string - '0'; ++ else if (isxdigit(*string)) ++ v = tolower(*string) - 'a' + 10; ++ else ++ v = base; ++ if (v >= base) ++ EXIT2(return STATUS_INVALID_PARAMETER); ++ res = res * base + v; ++ } ++ *value = sign * res; ++ EXIT3(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlIntegerToUnicodeString,3) ++ (ULONG value, ULONG base, struct unicode_string *ustring) ++{ ++ typeof(ustring->buf) buf = ustring->buf; ++ int i; ++ ++ if (base == 0) ++ base = 10; ++ if (!(base == 2 || base == 8 || base == 10 || base == 16)) ++ return STATUS_INVALID_PARAMETER; ++ for (i = 0; value && i < ustring->max_length / sizeof(*buf); i++) { ++ int r; ++ r = value % base; ++ value /= base; ++ if (r < 10) ++ buf[i] = r + '0'; ++ else ++ buf[i] = r + 'a' - 10; ++ } ++ if (value) ++ return STATUS_BUFFER_OVERFLOW; ++ ustring->length = i * sizeof(*buf); ++ return STATUS_SUCCESS; ++} ++ ++wstdcall LARGE_INTEGER WIN_FUNC(RtlConvertUlongToLargeInteger,1) ++ (ULONG ul) ++{ ++ LARGE_INTEGER li = ul; ++ return li; ++} ++ ++wfastcall USHORT WIN_FUNC(RtlUshortByteSwap,1) ++ (USHORT src) ++{ ++ return __swab16(src); ++} ++ ++wfastcall ULONG WIN_FUNC(RtlUlongByteSwap,1) ++ (ULONG src) ++{ ++ /* ULONG is 32 bits for both 32-bit and 64-bit architectures */ ++ return __swab32(src); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlUpcaseUnicodeString,3) ++ (struct unicode_string *dst, struct unicode_string *src, BOOLEAN alloc) ++{ ++ USHORT i, n; ++ ++ if (alloc) { ++ dst->buf = ExAllocatePoolWithTag(NonPagedPool, src->length, 0); ++ if (dst->buf) ++ dst->max_length = src->length; ++ else ++ EXIT2(return STATUS_NO_MEMORY); ++ } else { ++ if (dst->max_length < src->length) ++ EXIT2(return STATUS_BUFFER_OVERFLOW); ++ } ++ ++ n = src->length / sizeof(src->buf[0]); ++ for (i = 0; i < n; i++) ++ dst->buf[i] = toupper(src->buf[i]); ++ ++ dst->length = src->length; ++ EXIT3(return STATUS_SUCCESS); ++} ++ ++wstdcall void WIN_FUNC(RtlInitUnicodeString,2) ++ (struct unicode_string *dst, const wchar_t *src) ++{ ++ ENTER2("%p", dst); ++ if (dst == NULL) ++ EXIT1(return); ++ if (src == NULL) { ++ dst->max_length = dst->length = 0; ++ dst->buf = NULL; ++ } else { ++ int i; ++ for (i = 0; (char)src[i]; i++) ++ ; ++ dst->buf = (typeof(dst->buf))src; ++ dst->length = i * sizeof(dst->buf[0]); ++ dst->max_length = (i + 1) * sizeof(dst->buf[0]); ++ } ++ EXIT1(return); ++} ++ ++wstdcall void WIN_FUNC(RtlInitAnsiString,2) ++ (struct ansi_string *dst, const char *src) ++{ ++ ENTER2("%p", dst); ++ if (dst == NULL) ++ EXIT2(return); ++ if (src == NULL) { ++ dst->max_length = dst->length = 0; ++ dst->buf = NULL; ++ } else { ++ int i; ++ for (i = 0; src[i]; i++) ++ ; ++ dst->buf = (typeof(dst->buf))src; ++ dst->length = i; ++ dst->max_length = i + 1; ++ } ++ TRACE2("%p", dst->buf); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(RtlInitString,2) ++ (struct ansi_string *dst, const char *src) ++{ ++ ENTER2("%p", dst); ++ RtlInitAnsiString(dst, src); ++ EXIT2(return); ++} ++ ++wstdcall void WIN_FUNC(RtlFreeUnicodeString,1) ++ (struct unicode_string *string) ++{ ++ ENTER2("%p", string); ++ if (string == NULL) ++ return; ++ if (string->buf) ++ ExFreePool(string->buf); ++ string->length = string->max_length = 0; ++ string->buf = NULL; ++ return; ++} ++ ++wstdcall void WIN_FUNC(RtlFreeAnsiString,1) ++ (struct ansi_string *string) ++{ ++ ENTER2("%p", string); ++ if (string == NULL) ++ return; ++ if (string->buf) ++ ExFreePool(string->buf); ++ string->length = string->max_length = 0; ++ string->buf = NULL; ++ return; ++} ++ ++/* guid string is of the form: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */ ++wstdcall NTSTATUS WIN_FUNC(RtlGUIDFromString,2) ++ (struct unicode_string *guid_string, struct guid *guid) ++{ ++ struct ansi_string ansi; ++ NTSTATUS ret; ++ int i, j, k, l, m; ++ ++ ret = RtlUnicodeStringToAnsiString(&ansi, guid_string, TRUE); ++ if (ret != STATUS_SUCCESS) ++ return ret; ++ if (ansi.length != 37 || ansi.buf[0] != '{' || ++ ansi.buf[36] != '}' || ansi.buf[9] != '-' || ++ ansi.buf[14] != '-' || ansi.buf[19] != '-' || ++ ansi.buf[24] != '-') { ++ RtlFreeAnsiString(&ansi); ++ EXIT2(return STATUS_INVALID_PARAMETER); ++ } ++ memcpy(&guid->data4, &ansi.buf[29], sizeof(guid->data3)); ++ /* set end of data3 for scanf */ ++ ansi.buf[29] = 0; ++ if (sscanf(&ansi.buf[1], "%x", &i) == 1 && ++ sscanf(&ansi.buf[10], "%x", &j) == 1 && ++ sscanf(&ansi.buf[15], "%x", &k) == 1 && ++ sscanf(&ansi.buf[20], "%x", &l) == 1 && ++ sscanf(&ansi.buf[25], "%x", &m) == 1) { ++ guid->data1 = (i << 16) | (j < 8) | k; ++ guid->data2 = l; ++ guid->data3 = m; ++ ret = STATUS_SUCCESS; ++ } else ++ ret = STATUS_INVALID_PARAMETER; ++ RtlFreeAnsiString(&ansi); ++ return ret; ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlQueryRegistryValues,5) ++ (ULONG relative, wchar_t *path, struct rtl_query_registry_table *tbl, ++ void *context, void *env) ++{ ++ struct ansi_string ansi; ++ struct unicode_string unicode; ++ NTSTATUS status, ret; ++ static int i = 0; ++ ++ ENTER3("%x, %p", relative, tbl); ++// TODO(); ++ ++ RtlInitUnicodeString(&unicode, path); ++ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == ++ STATUS_SUCCESS) { ++ TRACE2("%s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ ret = STATUS_SUCCESS; ++ for (; tbl->name; tbl++) { ++ RtlInitUnicodeString(&unicode, tbl->name); ++ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == ++ STATUS_SUCCESS) { ++ TRACE2("name: %s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ TRACE2("flags: %08X", tbl->flags); ++ if (tbl->flags == RTL_QUERY_REGISTRY_DIRECT) { ++ TRACE2("type: %08X", tbl->def_type); ++ if (tbl->def_type == REG_DWORD) { ++ /* Atheros USB driver needs this, but ++ * don't know where and how to get its ++ * value */ ++ if (tbl->def_data) { ++ TRACE2("def_data: %x", ++ *(int *)tbl->def_data); ++ *(DWORD *)tbl->context = 0x5f292a + i++; ++// *(DWORD *)tbl->def_data; ++ } else ++ *(DWORD *)tbl->context = 0x2345dbe; ++ } ++ } else { ++ void *data; ++ ULONG type, length; ++ ++ if (!tbl->query_func) { ++ ERROR("oops: no query_func"); ++ ret = STATUS_INVALID_PARAMETER; ++ break; ++ } ++ if (tbl->flags & RTL_QUERY_REGISTRY_NOVALUE) { ++ data = NULL; ++ type = REG_NONE; ++ length = 0; ++ } else { ++ data = tbl->def_data; ++ type = tbl->def_type; ++ length = tbl->def_length;; ++ } ++ TRACE2("calling query_func: %p", tbl->query_func); ++ status = LIN2WIN6(tbl->query_func, tbl->name, type, ++ data, length, context, env); ++ TRACE2("status: %08X", status); ++ if (status) { ++ if (status == STATUS_BUFFER_TOO_SMALL) ++ ret = STATUS_BUFFER_TOO_SMALL; ++ else ++ EXIT2(return STATUS_INVALID_PARAMETER); ++ } ++ } ++ } ++ EXIT3(return ret); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlWriteRegistryValue,6) ++ (ULONG relative, wchar_t *path, wchar_t *name, ULONG type, ++ void *data, ULONG length) ++{ ++ struct ansi_string ansi; ++ struct unicode_string unicode; ++ ++ ENTER3("%d", relative); ++ TODO(); ++ ++ RtlInitUnicodeString(&unicode, path); ++ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == ++ STATUS_SUCCESS) { ++ TRACE2("%s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ RtlInitUnicodeString(&unicode, name); ++ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == ++ STATUS_SUCCESS) { ++ TRACE2("%s", ansi.buf); ++ RtlFreeAnsiString(&ansi); ++ } ++ EXIT5(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS WIN_FUNC(RtlDeleteRegistryValue,3) ++ (ULONG relative, wchar_t *path, wchar_t *name) ++{ ++ return STATUS_SUCCESS; ++} ++ ++wstdcall void WIN_FUNC(RtlAssert,4) ++ (char *failed_assertion, char *file_name, ULONG line_num, char *message) ++{ ++ ERROR("assertion '%s' failed at %s line %d%s", ++ failed_assertion, file_name, line_num, message ? message : ""); ++ return; ++} ++ ++wstdcall void WIN_FUNC(RtlUnwind,0) ++ (void) ++{ ++ TODO(); ++} ++ ++wstdcall void WIN_FUNC(RtlRaiseException,1) ++ (void *exception_record) ++{ ++ TODO(); ++} ++ ++wstdcall BOOLEAN WIN_FUNC(RtlIsServicePackVersionInstalled,1) ++ (ULONG version) ++{ ++ /* Assume we have all service packs */ ++ TRACE1("version: %d", version); ++ return TRUE; ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.c +--- linux-3.12.2/3rdparty/ndiswrapper/usb.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,1496 @@ ++/* ++ * Copyright (C) 2004 Jan Kiszka ++ * Copyright (C) 2005 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ndis.h" ++#include "usb.h" ++#include "usb_exports.h" ++ ++#ifdef USB_DEBUG ++static unsigned int urb_id = 0; ++ ++#define DUMP_WRAP_URB(wrap_urb, dir) \ ++ USBTRACE("urb %p (%d) %s: buf: %p, len: %d, pipe: 0x%x, %d", \ ++ (wrap_urb)->urb, (wrap_urb)->id, \ ++ (dir == USB_DIR_OUT) ? "going down" : "coming back", \ ++ (wrap_urb)->urb->transfer_buffer, \ ++ (wrap_urb)->urb->transfer_buffer_length, \ ++ (wrap_urb)->urb->pipe, (wrap_urb)->urb->status) ++ ++#define DUMP_URB_BUFFER(urb, dir) \ ++ while (debug >= 2) { \ ++ int i; \ ++ char msg[20], *t; \ ++ if (!urb->transfer_buffer) \ ++ break; \ ++ if (!((usb_pipein(urb->pipe) && dir == USB_DIR_IN) || \ ++ (usb_pipeout(urb->pipe) && dir == USB_DIR_OUT))) \ ++ break; \ ++ t = msg; \ ++ t += sprintf(t, "%d: ", (urb)->actual_length); \ ++ for (i = 0; i < urb->actual_length && \ ++ t < &msg[sizeof(msg) - 4]; i++) \ ++ t += sprintf(t, "%02X ", \ ++ ((char *)urb->transfer_buffer)[i]); \ ++ *t = 0; \ ++ USBTRACE("%s", msg); \ ++ break; \ ++ } ++ ++#else ++ ++#define DUMP_WRAP_URB(wrap_urb, dir) (void)0 ++#define DUMP_URB_BUFFER(urb, dir) (void)0 ++ ++#endif ++ ++#define CUR_ALT_SETTING(intf) (intf)->cur_altsetting ++ ++#ifndef USB_CTRL_SET_TIMEOUT ++#define USB_CTRL_SET_TIMEOUT 5000 ++#endif ++ ++#ifndef USB_CTRL_GET_TIMEOUT ++#define USB_CTRL_GET_TIMEOUT 5000 ++#endif ++ ++#ifndef URB_NO_TRANSFER_DMA_MAP ++#define URB_NO_TRANSFER_DMA_MAP 0 ++#endif ++ ++/* wrap_urb->flags */ ++/* transfer_buffer for urb is allocated; free it in wrap_free_urb */ ++#define WRAP_URB_COPY_BUFFER 0x01 ++ ++static inline int wrap_cancel_urb(struct wrap_urb *wrap_urb) ++{ ++ int ret; ++ USBTRACE("%p, %p, %d", wrap_urb, wrap_urb->urb, wrap_urb->state); ++ if (wrap_urb->state != URB_SUBMITTED) ++ USBEXIT(return -1); ++ ret = usb_unlink_urb(wrap_urb->urb); ++ USBTRACE("ret: %d", ret); ++ if (ret == -EINPROGRESS) ++ return 0; ++ else { ++ WARNING("unlink failed: %d", ret); ++ return ret; ++ } ++} ++ ++#define URB_STATUS(wrap_urb) (wrap_urb->urb->status) ++ ++static struct nt_list wrap_urb_complete_list; ++static spinlock_t wrap_urb_complete_list_lock; ++ ++static struct work_struct wrap_urb_complete_work; ++static void wrap_urb_complete_worker(struct work_struct *dummy); ++ ++static void kill_all_urbs(struct wrap_device *wd, int complete) ++{ ++ struct nt_list *ent; ++ struct wrap_urb *wrap_urb; ++ KIRQL irql; ++ ++ USBTRACE("%d", wd->usb.num_alloc_urbs); ++ while (1) { ++ IoAcquireCancelSpinLock(&irql); ++ ent = RemoveHeadList(&wd->usb.wrap_urb_list); ++ IoReleaseCancelSpinLock(irql); ++ if (!ent) ++ break; ++ wrap_urb = container_of(ent, struct wrap_urb, list); ++ if (wrap_urb->state == URB_SUBMITTED) { ++ WARNING("Windows driver %s didn't free urb: %p", ++ wd->driver->name, wrap_urb->urb); ++ if (!complete) ++ wrap_urb->urb->complete = NULL; ++ usb_kill_urb(wrap_urb->urb); ++ } ++ USBTRACE("%p, %p", wrap_urb, wrap_urb->urb); ++ usb_free_urb(wrap_urb->urb); ++ kfree(wrap_urb); ++ } ++ wd->usb.num_alloc_urbs = 0; ++} ++ ++/* for a given Linux urb status code, return corresponding NT urb status */ ++static USBD_STATUS wrap_urb_status(int urb_status) ++{ ++ switch (urb_status) { ++ case 0: ++ return USBD_STATUS_SUCCESS; ++ case -EPROTO: ++ return USBD_STATUS_TIMEOUT; ++ case -EILSEQ: ++ return USBD_STATUS_CRC; ++ case -EPIPE: ++ return USBD_STATUS_INVALID_PIPE_HANDLE; ++ case -ECOMM: ++ return USBD_STATUS_DATA_OVERRUN; ++ case -ENOSR: ++ return USBD_STATUS_DATA_UNDERRUN; ++ case -EOVERFLOW: ++ return USBD_STATUS_BABBLE_DETECTED; ++ case -EREMOTEIO: ++ return USBD_STATUS_ERROR_SHORT_TRANSFER;; ++ case -ENODEV: ++ case -ESHUTDOWN: ++ case -ENOENT: ++ return USBD_STATUS_DEVICE_GONE; ++ case -ENOMEM: ++ return USBD_STATUS_NO_MEMORY; ++ case -EINVAL: ++ return USBD_STATUS_REQUEST_FAILED; ++ default: ++ return USBD_STATUS_NOT_SUPPORTED; ++ } ++} ++ ++/* for a given USBD_STATUS, return its corresponding NTSTATUS (for irp) */ ++static NTSTATUS nt_urb_irp_status(USBD_STATUS nt_urb_status) ++{ ++ switch (nt_urb_status) { ++ case USBD_STATUS_SUCCESS: ++ return STATUS_SUCCESS; ++ case USBD_STATUS_DEVICE_GONE: ++ return STATUS_DEVICE_REMOVED; ++ case USBD_STATUS_PENDING: ++ return STATUS_PENDING; ++ case USBD_STATUS_NOT_SUPPORTED: ++ return STATUS_NOT_IMPLEMENTED; ++ case USBD_STATUS_NO_MEMORY: ++ return STATUS_NO_MEMORY; ++ case USBD_STATUS_REQUEST_FAILED: ++ return STATUS_NOT_SUPPORTED; ++ default: ++ return STATUS_FAILURE; ++ } ++} ++ ++static void wrap_free_urb(struct urb *urb) ++{ ++ struct wrap_urb *wrap_urb = urb->context; ++ struct irp *irp = wrap_urb->irp; ++ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); ++ ++ USBTRACE("freeing urb: %p", urb); ++ irp->cancel_routine = NULL; ++ IRP_WRAP_URB(irp) = NULL; ++ if (wrap_urb->flags & WRAP_URB_COPY_BUFFER) { ++ USBTRACE("freeing DMA buffer for URB: %p %p", ++ urb, urb->transfer_buffer); ++ usb_free_coherent(wd->usb.udev, urb->transfer_buffer_length, ++ urb->transfer_buffer, urb->transfer_dma); ++ } ++ kfree(urb->setup_packet); ++ if (wd->usb.num_alloc_urbs > MAX_ALLOCATED_URBS) { ++ IoAcquireCancelSpinLock(&irp->cancel_irql); ++ RemoveEntryList(&wrap_urb->list); ++ wd->usb.num_alloc_urbs--; ++ IoReleaseCancelSpinLock(irp->cancel_irql); ++ usb_free_urb(urb); ++ kfree(wrap_urb); ++ } else { ++ wrap_urb->state = URB_FREE; ++ wrap_urb->flags = 0; ++ wrap_urb->irp = NULL; ++ } ++ return; ++} ++ ++void wrap_suspend_urbs(struct wrap_device *wd) ++{ ++ /* TODO: do we need to cancel urbs? */ ++ USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs); ++} ++ ++void wrap_resume_urbs(struct wrap_device *wd) ++{ ++ /* TODO: do we need to resubmit urbs? */ ++ USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs); ++} ++ ++wstdcall void wrap_cancel_irp(struct device_object *dev_obj, struct irp *irp) ++{ ++ struct urb *urb; ++ struct wrap_urb *wrap_urb = IRP_WRAP_URB(irp); ++ ++ /* NB: this function is called holding Cancel spinlock */ ++ USBENTER("irp: %p", irp); ++ urb = wrap_urb->urb; ++ USBTRACE("canceling urb %p", urb); ++ if (wrap_cancel_urb(IRP_WRAP_URB(irp))) { ++ irp->cancel = FALSE; ++ ERROR("urb %p can't be canceled: %d", urb, wrap_urb->state); ++ } else ++ USBTRACE("urb %p canceled", urb); ++ IoReleaseCancelSpinLock(irp->cancel_irql); ++ return; ++} ++WIN_FUNC_DECL(wrap_cancel_irp,2) ++ ++static struct urb *wrap_alloc_urb(struct irp *irp, unsigned int pipe, ++ void *buf, unsigned int buf_len) ++{ ++ struct urb *urb; ++ gfp_t alloc_flags; ++ struct wrap_urb *wrap_urb; ++ struct wrap_device *wd; ++ ++ USBENTER("irp: %p", irp); ++ wd = IRP_WRAP_DEVICE(irp); ++ ++ /* Don't interfere with URB cleanup by the kernel */ ++ if (test_bit(HW_DISABLED, &wd->hw_status)) ++ return NULL; ++ ++ alloc_flags = irql_gfp(); ++ IoAcquireCancelSpinLock(&irp->cancel_irql); ++ urb = NULL; ++ nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) { ++ if (cmpxchg(&wrap_urb->state, URB_FREE, ++ URB_ALLOCATED) == URB_FREE) { ++ urb = wrap_urb->urb; ++ /* Clean URB but keep the refcount */ ++ memset((char *)urb + sizeof(urb->kref), 0, ++ sizeof(*urb) - sizeof(urb->kref)); ++ break; ++ } ++ } ++ if (!urb) { ++ IoReleaseCancelSpinLock(irp->cancel_irql); ++ wrap_urb = kzalloc(sizeof(*wrap_urb), alloc_flags); ++ if (!wrap_urb) { ++ WARNING("couldn't allocate memory"); ++ return NULL; ++ } ++ urb = usb_alloc_urb(0, alloc_flags); ++ if (!urb) { ++ WARNING("couldn't allocate urb"); ++ kfree(wrap_urb); ++ return NULL; ++ } ++ IoAcquireCancelSpinLock(&irp->cancel_irql); ++ wrap_urb->urb = urb; ++ wrap_urb->state = URB_ALLOCATED; ++ InsertTailList(&wd->usb.wrap_urb_list, &wrap_urb->list); ++ wd->usb.num_alloc_urbs++; ++ } ++ ++#ifdef URB_ASYNC_UNLINK ++ urb->transfer_flags |= URB_ASYNC_UNLINK; ++#elif defined(USB_ASYNC_UNLINK) ++ urb->transfer_flags |= USB_ASYNC_UNLINK; ++#endif ++ urb->context = wrap_urb; ++ wrap_urb->irp = irp; ++ IRP_WRAP_URB(irp) = wrap_urb; ++ /* called as Windows function */ ++ irp->cancel_routine = WIN_FUNC_PTR(wrap_cancel_irp,2); ++ IoReleaseCancelSpinLock(irp->cancel_irql); ++ USBTRACE("urb: %p", urb); ++ ++ urb->transfer_buffer_length = buf_len; ++ if (buf_len && buf && (!virt_addr_valid(buf) ++#if defined(CONFIG_HIGHMEM) || defined(CONFIG_HIGHMEM4G) ++ || PageHighMem(virt_to_page(buf)) ++#endif ++ )) { ++ urb->transfer_buffer = ++ usb_alloc_coherent(wd->usb.udev, buf_len, alloc_flags, ++ &urb->transfer_dma); ++ if (!urb->transfer_buffer) { ++ WARNING("couldn't allocate dma buf"); ++ IoAcquireCancelSpinLock(&irp->cancel_irql); ++ irp->cancel_routine = NULL; ++ wrap_urb->state = URB_FREE; ++ wrap_urb->irp = NULL; ++ IRP_WRAP_URB(irp) = NULL; ++ IoReleaseCancelSpinLock(irp->cancel_irql); ++ return NULL; ++ } ++ if (urb->transfer_dma) ++ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ wrap_urb->flags |= WRAP_URB_COPY_BUFFER; ++ if (usb_pipeout(pipe)) ++ memcpy(urb->transfer_buffer, buf, buf_len); ++ USBTRACE("DMA buf for urb %p: %p", urb, urb->transfer_buffer); ++ } else ++ urb->transfer_buffer = buf; ++ return urb; ++} ++ ++static USBD_STATUS wrap_submit_urb(struct irp *irp) ++{ ++ int ret; ++ struct wrap_urb *wrap_urb = IRP_WRAP_URB(irp); ++ struct urb *urb = wrap_urb->urb; ++ union nt_urb *nt_urb = IRP_URB(irp); ++ ++#ifdef USB_DEBUG ++ if (wrap_urb->state != URB_ALLOCATED) { ++ ERROR("urb %p is in wrong state: %d", ++ urb, wrap_urb->state); ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_REQUEST_FAILED; ++ return NT_URB_STATUS(nt_urb); ++ } ++ wrap_urb->id = pre_atomic_add(urb_id, 1); ++#endif ++ DUMP_WRAP_URB(IRP_WRAP_URB(irp), USB_DIR_OUT); ++ irp->io_status.status = STATUS_PENDING; ++ irp->io_status.info = 0; ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_PENDING; ++ IoMarkIrpPending(irp); ++ DUMP_URB_BUFFER(urb, USB_DIR_OUT); ++ USBTRACE("%p", urb); ++ wrap_urb->state = URB_SUBMITTED; ++ ret = usb_submit_urb(urb, irql_gfp()); ++ if (ret) { ++ USBTRACE("ret: %d", ret); ++ wrap_free_urb(urb); ++ /* we assume that IRP was not in pending state before */ ++ IoUnmarkIrpPending(irp); ++ NT_URB_STATUS(nt_urb) = wrap_urb_status(ret); ++ USBEXIT(return NT_URB_STATUS(nt_urb)); ++ } else ++ USBEXIT(return USBD_STATUS_PENDING); ++} ++ ++static void wrap_urb_complete(struct urb *urb ISR_PT_REGS_PARAM_DECL) ++{ ++ struct irp *irp; ++ struct wrap_urb *wrap_urb; ++ ++ wrap_urb = urb->context; ++ USBTRACE("%p (%p) completed", wrap_urb, urb); ++ irp = wrap_urb->irp; ++ DUMP_WRAP_URB(wrap_urb, USB_DIR_IN); ++ irp->cancel_routine = NULL; ++#ifdef USB_DEBUG ++ if (wrap_urb->state != URB_SUBMITTED) { ++ WARNING("urb %p in wrong state: %d (%d)", urb, wrap_urb->state, ++ urb->status); ++ return; ++ } ++#endif ++ wrap_urb->state = URB_COMPLETED; ++ spin_lock(&wrap_urb_complete_list_lock); ++ InsertTailList(&wrap_urb_complete_list, &wrap_urb->complete_list); ++ spin_unlock(&wrap_urb_complete_list_lock); ++ queue_work(ntos_wq, &wrap_urb_complete_work); ++} ++ ++/* one worker for all devices */ ++static void wrap_urb_complete_worker(struct work_struct *dummy) ++{ ++ struct irp *irp; ++ struct urb *urb; ++ struct usbd_bulk_or_intr_transfer *bulk_int_tx; ++ struct usbd_vendor_or_class_request *vc_req; ++ union nt_urb *nt_urb; ++ struct wrap_urb *wrap_urb; ++ struct nt_list *ent; ++ unsigned long flags; ++ ++ USBENTER(""); ++ while (1) { ++ spin_lock_irqsave(&wrap_urb_complete_list_lock, flags); ++ ent = RemoveHeadList(&wrap_urb_complete_list); ++ spin_unlock_irqrestore(&wrap_urb_complete_list_lock, flags); ++ if (!ent) ++ break; ++ wrap_urb = container_of(ent, struct wrap_urb, complete_list); ++ urb = wrap_urb->urb; ++#ifdef USB_DEBUG ++ if (wrap_urb->state != URB_COMPLETED && ++ wrap_urb->state != URB_INT_UNLINKED) ++ WARNING("urb %p in wrong state: %d", ++ urb, wrap_urb->state); ++#endif ++ irp = wrap_urb->irp; ++ DUMP_IRP(irp); ++ nt_urb = IRP_URB(irp); ++ USBTRACE("urb: %p, nt_urb: %p, status: %d", ++ urb, nt_urb, urb->status); ++ switch (urb->status) { ++ case 0: ++ /* successfully transferred */ ++ irp->io_status.info = urb->actual_length; ++ if (nt_urb->header.function == ++ URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) { ++ bulk_int_tx = &nt_urb->bulk_int_transfer; ++ bulk_int_tx->transfer_buffer_length = ++ urb->actual_length; ++ DUMP_URB_BUFFER(urb, USB_DIR_IN); ++ if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) && ++ usb_pipein(urb->pipe)) ++ memcpy(bulk_int_tx->transfer_buffer, ++ urb->transfer_buffer, ++ urb->actual_length); ++ } else { // vendor or class request ++ vc_req = &nt_urb->vendor_class_request; ++ vc_req->transfer_buffer_length = ++ urb->actual_length; ++ DUMP_URB_BUFFER(urb, USB_DIR_IN); ++ if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) && ++ usb_pipein(urb->pipe)) ++ memcpy(vc_req->transfer_buffer, ++ urb->transfer_buffer, ++ urb->actual_length); ++ } ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS; ++ irp->io_status.status = STATUS_SUCCESS; ++ break; ++ case -ENOENT: ++ case -ECONNRESET: ++ /* urb canceled */ ++ irp->io_status.info = 0; ++ TRACE2("urb %p canceled", urb); ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS; ++ irp->io_status.status = STATUS_CANCELLED; ++ break; ++ default: ++ TRACE2("irp: %p, urb: %p, status: %d/%d", ++ irp, urb, urb->status, wrap_urb->state); ++ irp->io_status.info = 0; ++ NT_URB_STATUS(nt_urb) = wrap_urb_status(urb->status); ++ irp->io_status.status = ++ nt_urb_irp_status(NT_URB_STATUS(nt_urb)); ++ break; ++ } ++ wrap_free_urb(urb); ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ } ++ USBEXIT(return); ++} ++ ++static USBD_STATUS wrap_bulk_or_intr_trans(struct irp *irp) ++{ ++ struct usb_endpoint_descriptor *pipe_handle; ++ struct urb *urb; ++ unsigned int pipe; ++ struct usbd_bulk_or_intr_transfer *bulk_int_tx; ++ USBD_STATUS status; ++ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); ++ struct usb_device *udev = wd->usb.udev; ++ union nt_urb *nt_urb = IRP_URB(irp); ++ ++ bulk_int_tx = &nt_urb->bulk_int_transfer; ++ pipe_handle = bulk_int_tx->pipe_handle; ++ USBTRACE("flags: 0x%x, length: %u, buffer: %p, handle: %p", ++ bulk_int_tx->transfer_flags, ++ bulk_int_tx->transfer_buffer_length, ++ bulk_int_tx->transfer_buffer, pipe_handle); ++ ++ if (USBD_IS_BULK_PIPE(pipe_handle)) { ++ if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN) ++ pipe = usb_rcvbulkpipe(udev, ++ pipe_handle->bEndpointAddress); ++ else ++ pipe = usb_sndbulkpipe(udev, ++ pipe_handle->bEndpointAddress); ++ } else { ++ if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN) ++ pipe = usb_rcvintpipe(udev, ++ pipe_handle->bEndpointAddress); ++ else ++ pipe = usb_sndintpipe(udev, ++ pipe_handle->bEndpointAddress); ++ } ++ ++ DUMP_IRP(irp); ++ urb = wrap_alloc_urb(irp, pipe, bulk_int_tx->transfer_buffer, ++ bulk_int_tx->transfer_buffer_length); ++ if (!urb) { ++ ERROR("couldn't allocate urb"); ++ return USBD_STATUS_NO_MEMORY; ++ } ++ if (usb_pipein(pipe) && ++ (!(bulk_int_tx->transfer_flags & USBD_SHORT_TRANSFER_OK))) { ++ USBTRACE("short not ok"); ++ urb->transfer_flags |= URB_SHORT_NOT_OK; ++ } ++ if (usb_pipebulk(pipe)) { ++ usb_fill_bulk_urb(urb, udev, pipe, urb->transfer_buffer, ++ bulk_int_tx->transfer_buffer_length, ++ wrap_urb_complete, urb->context); ++ USBTRACE("submitting bulk urb %p on pipe 0x%x (ep 0x%x)", ++ urb, urb->pipe, pipe_handle->bEndpointAddress); ++ } else { ++ usb_fill_int_urb(urb, udev, pipe, urb->transfer_buffer, ++ bulk_int_tx->transfer_buffer_length, ++ wrap_urb_complete, urb->context, ++ pipe_handle->bInterval); ++ USBTRACE("submitting interrupt urb %p on pipe 0x%x (ep 0x%x), " ++ "intvl: %d", urb, urb->pipe, ++ pipe_handle->bEndpointAddress, pipe_handle->bInterval); ++ } ++ status = wrap_submit_urb(irp); ++ USBTRACE("status: %08X", status); ++ USBEXIT(return status); ++} ++ ++static USBD_STATUS wrap_vendor_or_class_req(struct irp *irp) ++{ ++ u8 req_type; ++ unsigned int pipe; ++ struct usbd_vendor_or_class_request *vc_req; ++ USBD_STATUS status; ++ struct urb *urb; ++ struct usb_ctrlrequest *dr; ++ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); ++ struct usb_device *udev = wd->usb.udev; ++ union nt_urb *nt_urb = IRP_URB(irp); ++ ++ vc_req = &nt_urb->vendor_class_request; ++ USBTRACE("bits: %x, req: %x, val: %08x, index: %08x, flags: %x," ++ "buf: %p, len: %d", vc_req->reserved_bits, vc_req->request, ++ vc_req->value, vc_req->index, vc_req->transfer_flags, ++ vc_req->transfer_buffer, vc_req->transfer_buffer_length); ++ ++ USBTRACE("%x", nt_urb->header.function); ++ switch (nt_urb->header.function) { ++ case URB_FUNCTION_VENDOR_DEVICE: ++ req_type = USB_TYPE_VENDOR | USB_RECIP_DEVICE; ++ break; ++ case URB_FUNCTION_VENDOR_INTERFACE: ++ req_type = USB_TYPE_VENDOR | USB_RECIP_INTERFACE; ++ break; ++ case URB_FUNCTION_VENDOR_ENDPOINT: ++ req_type = USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; ++ break; ++ case URB_FUNCTION_VENDOR_OTHER: ++ req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER; ++ break; ++ case URB_FUNCTION_CLASS_DEVICE: ++ req_type = USB_TYPE_CLASS | USB_RECIP_DEVICE; ++ break; ++ case URB_FUNCTION_CLASS_INTERFACE: ++ req_type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; ++ break; ++ case URB_FUNCTION_CLASS_ENDPOINT: ++ req_type = USB_TYPE_CLASS | USB_RECIP_ENDPOINT; ++ break; ++ case URB_FUNCTION_CLASS_OTHER: ++ req_type = USB_TYPE_CLASS | USB_RECIP_OTHER; ++ break; ++ default: ++ ERROR("unknown request type: %x", nt_urb->header.function); ++ req_type = 0; ++ break; ++ } ++ ++ req_type |= vc_req->reserved_bits; ++ USBTRACE("req type: %08x", req_type); ++ ++ if (vc_req->transfer_flags & USBD_TRANSFER_DIRECTION_IN) { ++ pipe = usb_rcvctrlpipe(udev, 0); ++ req_type |= USB_DIR_IN; ++ USBTRACE("pipe: %x, dir in", pipe); ++ } else { ++ pipe = usb_sndctrlpipe(udev, 0); ++ req_type |= USB_DIR_OUT; ++ USBTRACE("pipe: %x, dir out", pipe); ++ } ++ urb = wrap_alloc_urb(irp, pipe, vc_req->transfer_buffer, ++ vc_req->transfer_buffer_length); ++ if (!urb) { ++ ERROR("couldn't allocate urb"); ++ return USBD_STATUS_NO_MEMORY; ++ } ++ ++ if (usb_pipein(pipe) && ++ (!(vc_req->transfer_flags & USBD_SHORT_TRANSFER_OK))) { ++ USBTRACE("short not ok"); ++ urb->transfer_flags |= URB_SHORT_NOT_OK; ++ } ++ ++ dr = kzalloc(sizeof(*dr), irql_gfp()); ++ if (!dr) { ++ ERROR("couldn't allocate memory"); ++ wrap_free_urb(urb); ++ return USBD_STATUS_NO_MEMORY; ++ } ++ dr->bRequestType = req_type; ++ dr->bRequest = vc_req->request; ++ dr->wValue = cpu_to_le16(vc_req->value); ++ dr->wIndex = cpu_to_le16((u16)vc_req->index); ++ dr->wLength = cpu_to_le16((u16)urb->transfer_buffer_length); ++ ++ usb_fill_control_urb(urb, udev, pipe, (unsigned char *)dr, ++ urb->transfer_buffer, urb->transfer_buffer_length, ++ wrap_urb_complete, urb->context); ++ status = wrap_submit_urb(irp); ++ USBTRACE("status: %08X", status); ++ USBEXIT(return status); ++} ++ ++static USBD_STATUS wrap_reset_pipe(struct usb_device *udev, struct irp *irp) ++{ ++ int ret; ++ union nt_urb *nt_urb; ++ struct usb_endpoint_descriptor *pipe_handle; ++ unsigned int pipe1, pipe2; ++ ++ nt_urb = IRP_URB(irp); ++ pipe_handle = nt_urb->pipe_req.pipe_handle; ++ /* TODO: not clear if both directions should be cleared? */ ++ if (USBD_IS_BULK_PIPE(pipe_handle)) { ++ pipe1 = usb_rcvbulkpipe(udev, pipe_handle->bEndpointAddress); ++ pipe2 = usb_sndbulkpipe(udev, pipe_handle->bEndpointAddress); ++ } else if (USBD_IS_INT_PIPE(pipe_handle)) { ++ pipe1 = usb_rcvintpipe(udev, pipe_handle->bEndpointAddress); ++ pipe2 = pipe1; ++ } else { ++ WARNING("invalid pipe %d", pipe_handle->bEndpointAddress); ++ return USBD_STATUS_INVALID_PIPE_HANDLE; ++ } ++ USBTRACE("ep: %d, pipe: 0x%x", pipe_handle->bEndpointAddress, pipe1); ++ ret = usb_clear_halt(udev, pipe1); ++ if (ret) ++ USBTRACE("resetting pipe %d failed: %d", pipe1, ret); ++ if (pipe2 != pipe1) { ++ ret = usb_clear_halt(udev, pipe2); ++ if (ret) ++ USBTRACE("resetting pipe %d failed: %d", pipe2, ret); ++ } ++// return wrap_urb_status(ret); ++ return USBD_STATUS_SUCCESS; ++} ++ ++static USBD_STATUS wrap_abort_pipe(struct usb_device *udev, struct irp *irp) ++{ ++ union nt_urb *nt_urb; ++ struct usb_endpoint_descriptor *pipe_handle; ++ struct wrap_urb *wrap_urb; ++ struct wrap_device *wd; ++ KIRQL irql; ++ ++ wd = IRP_WRAP_DEVICE(irp); ++ nt_urb = IRP_URB(irp); ++ pipe_handle = nt_urb->pipe_req.pipe_handle; ++ USBENTER("%p, %x", irp, pipe_handle->bEndpointAddress); ++ IoAcquireCancelSpinLock(&irql); ++ nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) { ++ USBTRACE("%p, %p, %d, %x, %x", wrap_urb, wrap_urb->urb, ++ wrap_urb->state, wrap_urb->urb->pipe, ++ usb_pipeendpoint(wrap_urb->urb->pipe)); ++ /* for WG111T driver, urbs for endpoint 0 should also ++ * be canceled */ ++ if ((usb_pipeendpoint(wrap_urb->urb->pipe) == ++ pipe_handle->bEndpointAddress) || ++ (usb_pipeendpoint(wrap_urb->urb->pipe) == 0)) { ++ if (wrap_cancel_urb(wrap_urb) == 0) ++ USBTRACE("canceled wrap_urb: %p", wrap_urb); ++ } ++ } ++ IoReleaseCancelSpinLock(irql); ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_CANCELED; ++ USBEXIT(return USBD_STATUS_SUCCESS); ++} ++ ++static USBD_STATUS wrap_set_clear_feature(struct usb_device *udev, ++ struct irp *irp) ++{ ++ union nt_urb *nt_urb; ++ struct urb_control_feature_request *feat_req; ++ int ret = 0; ++ __u8 request, type; ++ __u16 feature; ++ ++ nt_urb = IRP_URB(irp); ++ feat_req = &nt_urb->feat_req; ++ feature = feat_req->feature_selector; ++ switch (nt_urb->header.function) { ++ case URB_FUNCTION_SET_FEATURE_TO_DEVICE: ++ request = USB_REQ_SET_FEATURE; ++ type = USB_DT_DEVICE; ++ break; ++ case URB_FUNCTION_SET_FEATURE_TO_INTERFACE: ++ request = USB_REQ_SET_FEATURE; ++ type = USB_DT_INTERFACE; ++ break; ++ case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT: ++ request = USB_REQ_SET_FEATURE; ++ type = USB_DT_ENDPOINT; ++ break; ++ case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE: ++ request = USB_REQ_CLEAR_FEATURE; ++ type = USB_DT_DEVICE; ++ break; ++ case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE: ++ request = USB_REQ_CLEAR_FEATURE; ++ type = USB_DT_INTERFACE; ++ break; ++ case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT: ++ request = USB_REQ_CLEAR_FEATURE; ++ type = USB_DT_ENDPOINT; ++ break; ++ default: ++ WARNING("invalid function: %x", nt_urb->header.function); ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED; ++ return NT_URB_STATUS(nt_urb); ++ } ++ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), request, type, ++ feature, feat_req->index, NULL, 0, 1000); ++ NT_URB_STATUS(nt_urb) = wrap_urb_status(ret); ++ USBEXIT(return NT_URB_STATUS(nt_urb)); ++} ++ ++static USBD_STATUS wrap_get_status_request(struct usb_device *udev, ++ struct irp *irp) ++{ ++ union nt_urb *nt_urb; ++ struct urb_control_get_status_request *status_req; ++ int ret = 0; ++ __u8 type; ++ ++ nt_urb = IRP_URB(irp); ++ status_req = &nt_urb->status_req; ++ switch (nt_urb->header.function) { ++ case URB_FUNCTION_GET_STATUS_FROM_DEVICE: ++ type = USB_RECIP_DEVICE; ++ break; ++ case URB_FUNCTION_GET_STATUS_FROM_INTERFACE: ++ type = USB_RECIP_INTERFACE; ++ break; ++ case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT: ++ type = USB_RECIP_ENDPOINT; ++ break; ++ default: ++ WARNING("invalid function: %x", nt_urb->header.function); ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED; ++ return NT_URB_STATUS(nt_urb); ++ } ++ assert(status_req->transfer_buffer_length == sizeof(u16)); ++ ret = usb_get_status(udev, type, status_req->index, ++ status_req->transfer_buffer); ++ if (ret >= 0) { ++ assert(ret <= status_req->transfer_buffer_length); ++ status_req->transfer_buffer_length = ret; ++ NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS; ++ } else ++ NT_URB_STATUS(nt_urb) = wrap_urb_status(ret); ++ USBEXIT(return NT_URB_STATUS(nt_urb)); ++} ++ ++static void set_intf_pipe_info(struct wrap_device *wd, ++ struct usb_interface *usb_intf, ++ struct usbd_interface_information *intf) ++{ ++ int i; ++ struct usb_endpoint_descriptor *ep; ++ struct usbd_pipe_information *pipe; ++ ++ for (i = 0; i < CUR_ALT_SETTING(usb_intf)->desc.bNumEndpoints; i++) { ++ ep = &(CUR_ALT_SETTING(usb_intf)->endpoint[i]).desc; ++ if (i >= intf->bNumEndpoints) { ++ ERROR("intf %p has only %d endpoints, " ++ "ignoring endpoints above %d", ++ intf, intf->bNumEndpoints, i); ++ break; ++ } ++ pipe = &intf->pipes[i]; ++ ++ if (pipe->flags & USBD_PF_CHANGE_MAX_PACKET) ++ USBTRACE("pkt_sz: %d: %d", pipe->wMaxPacketSize, ++ pipe->max_tx_size); ++ USBTRACE("driver wants max_tx_size to %d", ++ pipe->max_tx_size); ++ ++ pipe->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); ++ pipe->bEndpointAddress = ep->bEndpointAddress; ++ pipe->type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ if (pipe->type == UsbdPipeTypeInterrupt) { ++ /* Windows and Linux differ in how the ++ * bInterval is interpreted */ ++ /* for low speed: ++ interval (Windows) -> frames per ms (Linux) ++ 0 to 15 -> 8 ++ 16 to 35 -> 16 ++ 36 to 255 -> 32 ++ ++ for full speed: interval -> frames per ms ++ 1 -> 1 ++ 2 to 3 -> 2 ++ 4 to 7 -> 4 ++ 8 to 15 -> 8 ++ 16 to 31 -> 16 ++ 32 to 255 -> 32 ++ ++ for high speed: interval -> microframes ++ 1 -> 1 ++ 2 -> 2 ++ 3 -> 4 ++ 4 -> 8 ++ 5 -> 16 ++ 6 -> 32 ++ 7 to 255 -> 32 ++ */ ++ if (wd->usb.udev->speed == USB_SPEED_LOW) ++ pipe->bInterval = ep->bInterval + 5; ++ else if (wd->usb.udev->speed == USB_SPEED_FULL) ++ pipe->bInterval = ep->bInterval; ++ else { ++ int j, k; ++ for (j = k = 1; j < ep->bInterval; k++) ++ j *= 2; ++ pipe->bInterval = k; ++ } ++ } ++ pipe->handle = ep; ++ USBTRACE("%d: ep 0x%x, type %d, pkt_sz %d, intv %d (%d)," ++ "type: %d, handle %p", i, ep->bEndpointAddress, ++ ep->bmAttributes, pipe->wMaxPacketSize, ep->bInterval, ++ pipe->bInterval, pipe->type, pipe->handle); ++ } ++} ++ ++static USBD_STATUS wrap_select_configuration(struct wrap_device *wd, ++ union nt_urb *nt_urb, ++ struct irp *irp) ++{ ++ int i, ret; ++ struct usbd_select_configuration *sel_conf; ++ struct usb_device *udev; ++ struct usbd_interface_information *intf; ++ struct usb_config_descriptor *config; ++ struct usb_interface *usb_intf; ++ ++ udev = wd->usb.udev; ++ sel_conf = &nt_urb->select_conf; ++ config = sel_conf->config; ++ USBTRACE("%p", config); ++ if (config == NULL) { ++ kill_all_urbs(wd, 1); ++ ret = usb_reset_configuration(udev); ++ return wrap_urb_status(ret); ++ } ++ ++ USBTRACE("conf: %d, type: %d, length: %d, numif: %d, attr: %08x", ++ config->bConfigurationValue, config->bDescriptorType, ++ config->wTotalLength, config->bNumInterfaces, ++ config->bmAttributes); ++ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ USB_REQ_SET_CONFIGURATION, 0, ++ config->bConfigurationValue, 0, ++ NULL, 0, USB_CTRL_SET_TIMEOUT); ++ if (ret < 0) { ++ ERROR("ret: %d", ret); ++ return wrap_urb_status(ret); ++ } ++ sel_conf->handle = udev->actconfig; ++ intf = &sel_conf->intf; ++ for (i = 0; i < config->bNumInterfaces && intf->bLength > 0; ++ i++, intf = (((void *)intf) + intf->bLength)) { ++ ++ USBTRACE("intf: %d, alt setting: %d", ++ intf->bInterfaceNumber, intf->bAlternateSetting); ++ ret = usb_set_interface(udev, intf->bInterfaceNumber, ++ intf->bAlternateSetting); ++ if (ret < 0) { ++ ERROR("failed with %d", ret); ++ return wrap_urb_status(ret); ++ } ++ usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber); ++ if (!usb_intf) { ++ ERROR("couldn't obtain ifnum"); ++ return USBD_STATUS_REQUEST_FAILED; ++ } ++ USBTRACE("intf: %p, num ep: %d", intf, intf->bNumEndpoints); ++ set_intf_pipe_info(wd, usb_intf, intf); ++ } ++ return USBD_STATUS_SUCCESS; ++} ++ ++static USBD_STATUS wrap_select_interface(struct wrap_device *wd, ++ union nt_urb *nt_urb, ++ struct irp *irp) ++{ ++ int ret; ++ struct usbd_select_interface *sel_intf; ++ struct usb_device *udev; ++ struct usbd_interface_information *intf; ++ struct usb_interface *usb_intf; ++ ++ udev = wd->usb.udev; ++ sel_intf = &nt_urb->select_intf; ++ intf = &sel_intf->intf; ++ ++ ret = usb_set_interface(udev, intf->bInterfaceNumber, ++ intf->bAlternateSetting); ++ if (ret < 0) { ++ ERROR("failed with %d", ret); ++ return wrap_urb_status(ret); ++ } ++ usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber); ++ if (!usb_intf) { ++ ERROR("couldn't get interface information"); ++ return USBD_STATUS_REQUEST_FAILED; ++ } ++ USBTRACE("intf: %p, num ep: %d", usb_intf, intf->bNumEndpoints); ++ set_intf_pipe_info(wd, usb_intf, intf); ++ return USBD_STATUS_SUCCESS; ++} ++ ++static int wrap_usb_get_string(struct usb_device *udev, unsigned short langid, ++ unsigned char index, void *buf, int size) ++{ ++ int i, ret; ++ /* if langid is 0, return array of languages supported in ++ * buf */ ++ for (i = 0; i < 3; i++) { ++ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), ++ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, ++ (USB_DT_STRING << 8) + index, langid, ++ buf, size, USB_CTRL_GET_TIMEOUT); ++ if (ret > 0 || ret == -EPIPE) ++ break; ++ } ++ return ret; ++} ++ ++static USBD_STATUS wrap_get_descriptor(struct wrap_device *wd, ++ union nt_urb *nt_urb, struct irp *irp) ++{ ++ struct usbd_control_descriptor_request *control_desc; ++ int ret = 0; ++ struct usb_device *udev; ++ ++ udev = wd->usb.udev; ++ control_desc = &nt_urb->control_desc; ++ USBTRACE("desctype = %d, descindex = %d, transfer_buffer = %p," ++ "transfer_buffer_length = %d", control_desc->desc_type, ++ control_desc->index, control_desc->transfer_buffer, ++ control_desc->transfer_buffer_length); ++ ++ if (control_desc->desc_type == USB_DT_STRING) { ++ USBTRACE("langid: %x", control_desc->language_id); ++ ret = wrap_usb_get_string(udev, control_desc->language_id, ++ control_desc->index, ++ control_desc->transfer_buffer, ++ control_desc->transfer_buffer_length); ++ } else { ++ ret = usb_get_descriptor(udev, control_desc->desc_type, ++ control_desc->index, ++ control_desc->transfer_buffer, ++ control_desc->transfer_buffer_length); ++ } ++ if (ret < 0) { ++ USBTRACE("request %d failed: %d", control_desc->desc_type, ret); ++ control_desc->transfer_buffer_length = 0; ++ return wrap_urb_status(ret); ++ } else { ++ USBTRACE("ret: %08x", ret); ++ control_desc->transfer_buffer_length = ret; ++ irp->io_status.info = ret; ++ return USBD_STATUS_SUCCESS; ++ } ++} ++ ++static USBD_STATUS wrap_process_nt_urb(struct irp *irp) ++{ ++ USBD_STATUS status; ++ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); ++ struct usb_device *udev = wd->usb.udev; ++ union nt_urb *nt_urb = IRP_URB(irp); ++ ++ USBENTER("nt_urb = %p, irp = %p, length = %d, function = %x", ++ nt_urb, irp, nt_urb->header.length, nt_urb->header.function); ++ ++ if (test_bit(HW_DISABLED, &wd->hw_status)) { ++ status = USBD_STATUS_DEVICE_GONE; ++ NT_URB_STATUS(nt_urb) = status; ++ return status; ++ } ++ ++ DUMP_IRP(irp); ++ switch (nt_urb->header.function) { ++ /* bulk/int and vendor/class urbs are submitted to ++ * Linux USB core; if the call is successful, urb's ++ * completion worker will return IRP later */ ++ case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: ++ USBTRACE("submitting bulk/int irp: %p", irp); ++ status = wrap_bulk_or_intr_trans(irp); ++ break; ++ ++ case URB_FUNCTION_VENDOR_DEVICE: ++ case URB_FUNCTION_VENDOR_INTERFACE: ++ case URB_FUNCTION_VENDOR_ENDPOINT: ++ case URB_FUNCTION_VENDOR_OTHER: ++ case URB_FUNCTION_CLASS_DEVICE: ++ case URB_FUNCTION_CLASS_INTERFACE: ++ case URB_FUNCTION_CLASS_ENDPOINT: ++ case URB_FUNCTION_CLASS_OTHER: ++ USBTRACE("submitting vendor/class irp: %p", irp); ++ status = wrap_vendor_or_class_req(irp); ++ break; ++ ++ /* rest are synchronous */ ++ case URB_FUNCTION_SELECT_CONFIGURATION: ++ status = wrap_select_configuration(wd, nt_urb, irp); ++ NT_URB_STATUS(nt_urb) = status; ++ break; ++ ++ case URB_FUNCTION_SELECT_INTERFACE: ++ status = wrap_select_interface(wd, nt_urb, irp); ++ NT_URB_STATUS(nt_urb) = status; ++ break; ++ ++ case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: ++ status = wrap_get_descriptor(wd, nt_urb, irp); ++ NT_URB_STATUS(nt_urb) = status; ++ break; ++ ++ case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: ++ status = wrap_reset_pipe(udev, irp); ++ NT_URB_STATUS(nt_urb) = status; ++ break; ++ ++ case URB_FUNCTION_ABORT_PIPE: ++ status = wrap_abort_pipe(udev, irp); ++ break; ++ ++ case URB_FUNCTION_SET_FEATURE_TO_DEVICE: ++ case URB_FUNCTION_SET_FEATURE_TO_INTERFACE: ++ case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT: ++ case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE: ++ case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE: ++ case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT: ++ status = wrap_set_clear_feature(udev, irp); ++ break; ++ ++ case URB_FUNCTION_GET_STATUS_FROM_DEVICE: ++ case URB_FUNCTION_GET_STATUS_FROM_INTERFACE: ++ case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT: ++ status = wrap_get_status_request(udev, irp); ++ break; ++ ++ default: ++ ERROR("function %x not implemented", nt_urb->header.function); ++ status = NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED; ++ break; ++ } ++ USBTRACE("status: %08X", status); ++ return status; ++} ++ ++static USBD_STATUS wrap_reset_port(struct irp *irp) ++{ ++ int ret, lock = 0; ++ struct wrap_device *wd; ++ ++ wd = IRP_WRAP_DEVICE(irp); ++ USBENTER("%p, %p", wd, wd->usb.udev); ++ lock = usb_lock_device_for_reset(wd->usb.udev, wd->usb.intf); ++ if (lock < 0) { ++ WARNING("locking failed: %d", lock); ++// return wrap_urb_status(lock); ++ return USBD_STATUS_SUCCESS; ++ } ++ ret = usb_reset_device(wd->usb.udev); ++ if (ret < 0) ++ USBTRACE("reset failed: %d", ret); ++ /* TODO: should reconfigure? */ ++ if (lock) ++ usb_unlock_device(wd->usb.udev); ++// return wrap_urb_status(ret); ++ return USBD_STATUS_SUCCESS; ++} ++ ++static USBD_STATUS wrap_get_port_status(struct irp *irp) ++{ ++ struct wrap_device *wd; ++ ULONG *status; ++ enum usb_device_state state; ++ ++ wd = IRP_WRAP_DEVICE(irp); ++ USBENTER("%p, %p", wd, wd->usb.udev); ++ status = IoGetCurrentIrpStackLocation(irp)->params.others.arg1; ++ state = wd->usb.udev->state; ++ if (state != USB_STATE_NOTATTACHED && ++ state != USB_STATE_SUSPENDED) { ++ *status |= USBD_PORT_CONNECTED; ++ if (state == USB_STATE_CONFIGURED) ++ *status |= USBD_PORT_ENABLED; ++ } ++ USBTRACE("state: %d, *status: %08X", state, *status); ++ return USBD_STATUS_SUCCESS; ++} ++ ++NTSTATUS wrap_submit_irp(struct device_object *pdo, struct irp *irp) ++{ ++ struct io_stack_location *irp_sl; ++ struct wrap_device *wd; ++ USBD_STATUS status; ++ struct usbd_idle_callback *idle_callback; ++ ++ USBENTER("%p, %p", pdo, irp); ++ wd = pdo->reserved; ++ if (wd->usb.intf == NULL) { ++ USBTRACE("%p", irp); ++ irp->io_status.status = STATUS_DEVICE_REMOVED; ++ irp->io_status.info = 0; ++ USBEXIT(return STATUS_DEVICE_REMOVED); ++ } ++ IRP_WRAP_DEVICE(irp) = wd; ++ irp_sl = IoGetCurrentIrpStackLocation(irp); ++ switch (irp_sl->params.dev_ioctl.code) { ++ case IOCTL_INTERNAL_USB_SUBMIT_URB: ++ status = wrap_process_nt_urb(irp); ++ break; ++ case IOCTL_INTERNAL_USB_RESET_PORT: ++ status = wrap_reset_port(irp); ++ break; ++ case IOCTL_INTERNAL_USB_GET_PORT_STATUS: ++ status = wrap_get_port_status(irp); ++ break; ++ case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: ++ idle_callback = irp_sl->params.dev_ioctl.type3_input_buf; ++ (void)idle_callback; ++ USBTRACE("suspend function: %p", idle_callback->callback); ++ status = USBD_STATUS_NOT_SUPPORTED; ++ break; ++ default: ++ ERROR("ioctl %08X NOT IMPLEMENTED", ++ irp_sl->params.dev_ioctl.code); ++ status = USBD_STATUS_NOT_SUPPORTED; ++ break; ++ } ++ ++ USBTRACE("status: %08X", status); ++ if (status == USBD_STATUS_PENDING) { ++ /* don't touch this IRP - it may have been already ++ * completed/returned */ ++ return STATUS_PENDING; ++ } else { ++ irp->io_status.status = nt_urb_irp_status(status); ++ if (status != USBD_STATUS_SUCCESS) ++ irp->io_status.info = 0; ++ USBEXIT(return irp->io_status.status); ++ } ++} ++ ++/* TODO: The example on msdn in reference section suggests that second ++ * argument should be an array of usbd_interface_information, but ++ * description and examples elsewhere suggest that it should be ++ * usbd_interface_list_entry structre. Which is correct? */ ++ ++wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequestEx,2) ++ (struct usb_config_descriptor *config, ++ struct usbd_interface_list_entry *intf_list) ++{ ++ int size, i, n; ++ struct usbd_interface_information *intf; ++ struct usbd_pipe_information *pipe; ++ struct usb_interface_descriptor *intf_desc; ++ struct usbd_select_configuration *select_conf; ++ ++ USBENTER("config = %p, intf_list = %p", config, intf_list); ++ ++ /* calculate size required; select_conf already has space for ++ * one intf structure */ ++ size = sizeof(*select_conf) - sizeof(*intf); ++ for (n = 0; n < config->bNumInterfaces; n++) { ++ i = intf_list[n].intf_desc->bNumEndpoints; ++ /* intf already has space for one pipe */ ++ size += sizeof(*intf) + (i - 1) * sizeof(*pipe); ++ } ++ /* don't use kmalloc - driver frees it with ExFreePool */ ++ select_conf = ExAllocatePoolWithTag(NonPagedPool, size, ++ POOL_TAG('L', 'U', 'S', 'B')); ++ if (!select_conf) { ++ WARNING("couldn't allocate memory"); ++ return NULL; ++ } ++ memset(select_conf, 0, size); ++ intf = &select_conf->intf; ++ select_conf->handle = config; ++ for (n = 0; n < config->bNumInterfaces && intf_list[n].intf_desc; n++) { ++ /* initialize 'intf' fields in intf_list so they point ++ * to appropriate entry; these may be read/written by ++ * driver after this function returns */ ++ intf_list[n].intf = intf; ++ intf_desc = intf_list[n].intf_desc; ++ ++ i = intf_desc->bNumEndpoints; ++ intf->bLength = sizeof(*intf) + (i - 1) * sizeof(*pipe); ++ ++ intf->bInterfaceNumber = intf_desc->bInterfaceNumber; ++ intf->bAlternateSetting = intf_desc->bAlternateSetting; ++ intf->bInterfaceClass = intf_desc->bInterfaceClass; ++ intf->bInterfaceSubClass = intf_desc->bInterfaceSubClass; ++ intf->bInterfaceProtocol = intf_desc->bInterfaceProtocol; ++ intf->bNumEndpoints = intf_desc->bNumEndpoints; ++ ++ pipe = &intf->pipes[0]; ++ for (i = 0; i < intf->bNumEndpoints; i++) { ++ memset(&pipe[i], 0, sizeof(*pipe)); ++ pipe[i].max_tx_size = ++ USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; ++ } ++ intf->handle = intf_desc; ++ intf = (((void *)intf) + intf->bLength); ++ } ++ select_conf->header.function = URB_FUNCTION_SELECT_CONFIGURATION; ++ select_conf->header.length = size; ++ select_conf->config = config; ++ USBEXIT(return (union nt_urb *)select_conf); ++} ++ ++WIN_SYMBOL_MAP("_USBD_CreateConfigurationRequestEx@8", USBD_CreateConfigurationRequestEx) ++ ++wstdcall struct usb_interface_descriptor * ++WIN_FUNC(USBD_ParseConfigurationDescriptorEx,7) ++ (struct usb_config_descriptor *config, void *start, ++ LONG bInterfaceNumber, LONG bAlternateSetting, LONG bInterfaceClass, ++ LONG bInterfaceSubClass, LONG bInterfaceProtocol) ++{ ++ void *pos; ++ struct usb_interface_descriptor *intf; ++ ++ USBENTER("config = %p, start = %p, ifnum = %d, alt_setting = %d," ++ " class = %d, subclass = %d, proto = %d", config, start, ++ bInterfaceNumber, bAlternateSetting, bInterfaceClass, ++ bInterfaceSubClass, bInterfaceProtocol); ++ ++ for (pos = start; ++ pos < ((void *)config + le16_to_cpu(config->wTotalLength)); ++ pos += intf->bLength) { ++ ++ intf = pos; ++ ++ if ((intf->bDescriptorType == USB_DT_INTERFACE) && ++ ((bInterfaceNumber == -1) || ++ (intf->bInterfaceNumber == bInterfaceNumber)) && ++ ((bAlternateSetting == -1) || ++ (intf->bAlternateSetting == bAlternateSetting)) && ++ ((bInterfaceClass == -1) || ++ (intf->bInterfaceClass == bInterfaceClass)) && ++ ((bInterfaceSubClass == -1) || ++ (intf->bInterfaceSubClass == bInterfaceSubClass)) && ++ ((bInterfaceProtocol == -1) || ++ (intf->bInterfaceProtocol == bInterfaceProtocol))) { ++ USBTRACE("selected interface = %p", intf); ++ USBEXIT(return intf); ++ } ++ } ++ USBEXIT(return NULL); ++} ++ ++WIN_SYMBOL_MAP("_USBD_ParseConfigurationDescriptorEx@28", USBD_ParseConfigurationDescriptorEx) ++ ++wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequest,2) ++ (struct usb_config_descriptor *config, USHORT *size) ++{ ++ union nt_urb *nt_urb; ++ struct usbd_interface_list_entry intf_list[2]; ++ struct usb_interface_descriptor *intf_desc; ++ ++ USBENTER("config = %p, urb_size = %p", config, size); ++ ++ intf_desc = USBD_ParseConfigurationDescriptorEx(config, config, -1, -1, ++ -1, -1, -1); ++ intf_list[0].intf_desc = intf_desc; ++ intf_list[0].intf = NULL; ++ intf_list[1].intf_desc = NULL; ++ intf_list[1].intf = NULL; ++ nt_urb = USBD_CreateConfigurationRequestEx(config, intf_list); ++ if (!nt_urb) ++ return NULL; ++ ++ *size = nt_urb->select_conf.header.length; ++ USBEXIT(return nt_urb); ++} ++ ++wstdcall struct usb_interface_descriptor * ++WIN_FUNC(USBD_ParseConfigurationDescriptor,3) ++ (struct usb_config_descriptor *config, UCHAR bInterfaceNumber, ++ UCHAR bAlternateSetting) ++{ ++ return USBD_ParseConfigurationDescriptorEx(config, config, ++ bInterfaceNumber, ++ bAlternateSetting, ++ -1, -1, -1); ++} ++ ++wstdcall struct usb_descriptor_header *WIN_FUNC(USBD_ParseDescriptors,4) ++ (void *buf, ULONG length, struct usb_descriptor_header *descr, ++ LONG type) ++{ ++ while ((void *)descr < buf + length) { ++ if (descr->bDescriptorType == type) ++ return descr; ++ if (descr->bLength == 0) ++ break; ++ descr = (void *)descr + descr->bLength; ++ } ++ USBEXIT(return NULL); ++} ++ ++WIN_SYMBOL_MAP("_USBD_ParseDescriptors@16", USBD_ParseDescriptors) ++ ++wstdcall void WIN_FUNC(USBD_GetUSBDIVersion,1) ++ (struct usbd_version_info *version_info) ++{ ++ /* this function is obsolete in Windows XP */ ++ if (version_info) { ++ version_info->usbdi_version = USBDI_VERSION_XP; ++ /* TODO: how do we get this correctly? */ ++ version_info->supported_usb_version = 0x110; ++ } ++ USBEXIT(return); ++} ++ ++wstdcall void ++USBD_InterfaceGetUSBDIVersion(void *context, ++ struct usbd_version_info *version_info, ++ ULONG *hcd_capa) ++{ ++ struct wrap_device *wd = context; ++ ++ if (version_info) { ++ version_info->usbdi_version = USBDI_VERSION_XP; ++ if (wd->usb.udev->speed == USB_SPEED_HIGH) ++ version_info->supported_usb_version = 0x200; ++ else ++ version_info->supported_usb_version = 0x110; ++ } ++ *hcd_capa = USB_HCD_CAPS_SUPPORTS_RT_THREADS; ++ USBEXIT(return); ++} ++ ++wstdcall BOOLEAN USBD_InterfaceIsDeviceHighSpeed(void *context) ++{ ++ struct wrap_device *wd = context; ++ ++ USBTRACE("wd: %p", wd); ++ if (wd->usb.udev->speed == USB_SPEED_HIGH) ++ USBEXIT(return TRUE); ++ else ++ USBEXIT(return FALSE); ++} ++ ++wstdcall void USBD_InterfaceReference(void *context) ++{ ++ USBTRACE("%p", context); ++ TODO(); ++} ++ ++wstdcall void USBD_InterfaceDereference(void *context) ++{ ++ USBTRACE("%p", context); ++ TODO(); ++} ++ ++wstdcall NTSTATUS USBD_InterfaceQueryBusTime(void *context, ULONG *frame) ++{ ++ struct wrap_device *wd = context; ++ ++ *frame = usb_get_current_frame_number(wd->usb.udev); ++ USBEXIT(return STATUS_SUCCESS); ++} ++ ++wstdcall NTSTATUS USBD_InterfaceSubmitIsoOutUrb(void *context, ++ union nt_urb *nt_urb) ++{ ++ /* TODO: implement this */ ++ TODO(); ++ USBEXIT(return STATUS_NOT_IMPLEMENTED); ++} ++ ++wstdcall NTSTATUS ++USBD_InterfaceQueryBusInformation(void *context, ULONG level, void *buf, ++ ULONG *buf_length, ULONG *buf_actual_length) ++{ ++#if 0 ++ struct wrap_device *wd = context; ++ struct usb_bus *bus = wd->usb.udev->bus; ++ struct usb_bus_information_level *bus_info = buf; ++#endif ++ ++ TODO(); ++ USBEXIT(return STATUS_NOT_IMPLEMENTED); ++} ++ ++wstdcall NTSTATUS ++USBD_InterfaceLogEntry(void *context, ULONG driver_tag, ULONG enum_tag, ++ ULONG p1, ULONG p2) ++{ ++ ERROR("%p, %x, %x, %x, %x", context, driver_tag, enum_tag, p1, p2); ++ USBEXIT(return STATUS_SUCCESS); ++} ++ ++NTSTATUS ++usb_query_interface(struct wrap_device *wd, struct io_stack_location *irp_sl) ++{ ++ struct usbd_bus_interface_usbdi *intf = ++ (struct usbd_bus_interface_usbdi *) ++ irp_sl->params.query_intf.intf; ++ ++ TRACE2("type: %x, size: %d, version: %d", ++ irp_sl->params.query_intf.type->data1, ++ irp_sl->params.query_intf.size, ++ irp_sl->params.query_intf.version); ++ intf->Context = wd; ++ intf->InterfaceReference = ++ WIN_FUNC_PTR(USBD_InterfaceReference, 1); ++ intf->InterfaceDereference = ++ WIN_FUNC_PTR(USBD_InterfaceDereference, 1); ++ intf->GetUSBDIVersion = ++ WIN_FUNC_PTR(USBD_InterfaceGetUSBDIVersion, 3); ++ intf->QueryBusTime = ++ WIN_FUNC_PTR(USBD_InterfaceQueryBusTime, 2); ++ intf->SubmitIsoOutUrb = ++ WIN_FUNC_PTR(USBD_InterfaceSubmitIsoOutUrb, 2); ++ intf->QueryBusInformation = ++ WIN_FUNC_PTR(USBD_InterfaceQueryBusInformation, 5); ++ if (irp_sl->params.query_intf.version >= USB_BUSIF_USBDI_VERSION_1) ++ intf->IsDeviceHighSpeed = ++ WIN_FUNC_PTR(USBD_InterfaceIsDeviceHighSpeed, 1); ++ if (irp_sl->params.query_intf.version >= USB_BUSIF_USBDI_VERSION_2) ++ intf->LogEntry = WIN_FUNC_PTR(USBD_InterfaceLogEntry, 5); ++ return STATUS_SUCCESS; ++} ++ ++int usb_init(void) ++{ ++ InitializeListHead(&wrap_urb_complete_list); ++ spin_lock_init(&wrap_urb_complete_list_lock); ++ INIT_WORK(&wrap_urb_complete_work, wrap_urb_complete_worker); ++#ifdef USB_DEBUG ++ urb_id = 0; ++#endif ++ return 0; ++} ++ ++void usb_exit(void) ++{ ++ USBEXIT(return); ++} ++ ++int usb_init_device(struct wrap_device *wd) ++{ ++ InitializeListHead(&wd->usb.wrap_urb_list); ++ wd->usb.num_alloc_urbs = 0; ++ USBEXIT(return 0); ++} ++ ++void usb_exit_device(struct wrap_device *wd) ++{ ++ kill_all_urbs(wd, 0); ++ USBEXIT(return); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.h +--- linux-3.12.2/3rdparty/ndiswrapper/usb.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,360 @@ ++/* ++ * Copyright (C) 2004 Jan Kiszka ++ * Copyright (C) 2005 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _USB_H_ ++#define _USB_H_ ++ ++#include "ntoskernel.h" ++ ++#define IOCTL_INTERNAL_USB_SUBMIT_URB 0x00220003 ++#define IOCTL_INTERNAL_USB_RESET_PORT 0x00220007 ++#define IOCTL_INTERNAL_USB_GET_PORT_STATUS 0x00220013 ++#define IOCTL_INTERNAL_USB_CYCLE_PORT 0x0022001F ++#define IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION 0x00220027 ++ ++#define URB_FUNCTION_SELECT_CONFIGURATION 0x0000 ++#define URB_FUNCTION_SELECT_INTERFACE 0x0001 ++#define URB_FUNCTION_ABORT_PIPE 0x0002 ++#define URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL 0x0003 ++#define URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL 0x0004 ++#define URB_FUNCTION_GET_FRAME_LENGTH 0x0005 ++#define URB_FUNCTION_SET_FRAME_LENGTH 0x0006 ++#define URB_FUNCTION_GET_CURRENT_FRAME_NUMBER 0x0007 ++#define URB_FUNCTION_CONTROL_TRANSFER 0x0008 ++#define URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 0x0009 ++#define URB_FUNCTION_ISOCH_TRANSFER 0x000A ++#define URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 0x000B ++#define URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE 0x000C ++#define URB_FUNCTION_SET_FEATURE_TO_DEVICE 0x000D ++#define URB_FUNCTION_SET_FEATURE_TO_INTERFACE 0x000E ++#define URB_FUNCTION_SET_FEATURE_TO_ENDPOINT 0x000F ++#define URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE 0x0010 ++#define URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE 0x0011 ++#define URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT 0x0012 ++#define URB_FUNCTION_GET_STATUS_FROM_DEVICE 0x0013 ++#define URB_FUNCTION_GET_STATUS_FROM_INTERFACE 0x0014 ++#define URB_FUNCTION_GET_STATUS_FROM_ENDPOINT 0x0015 ++#define URB_FUNCTION_RESERVED_0X0016 0x0016 ++#define URB_FUNCTION_VENDOR_DEVICE 0x0017 ++#define URB_FUNCTION_VENDOR_INTERFACE 0x0018 ++#define URB_FUNCTION_VENDOR_ENDPOINT 0x0019 ++#define URB_FUNCTION_CLASS_DEVICE 0x001A ++#define URB_FUNCTION_CLASS_INTERFACE 0x001B ++#define URB_FUNCTION_CLASS_ENDPOINT 0x001C ++#define URB_FUNCTION_RESERVE_0X001D 0x001D ++#define URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL 0x001E ++#define URB_FUNCTION_CLASS_OTHER 0x001F ++#define URB_FUNCTION_VENDOR_OTHER 0x0020 ++#define URB_FUNCTION_GET_STATUS_FROM_OTHER 0x0021 ++#define URB_FUNCTION_CLEAR_FEATURE_TO_OTHER 0x0022 ++#define URB_FUNCTION_SET_FEATURE_TO_OTHER 0x0023 ++#define URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT 0x0024 ++#define URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT 0x0025 ++#define URB_FUNCTION_GET_CONFIGURATION 0x0026 ++#define URB_FUNCTION_GET_INTERFACE 0x0027 ++#define URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE 0x0028 ++#define URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE 0x0029 ++#define URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR 0x002A ++#define URB_FUNCTION_RESERVE_0X002B 0x002B ++#define URB_FUNCTION_RESERVE_0X002C 0x002C ++#define URB_FUNCTION_RESERVE_0X002D 0x002D ++#define URB_FUNCTION_RESERVE_0X002E 0x002E ++#define URB_FUNCTION_RESERVE_0X002F 0x002F ++// USB 2.0 calls start at 0x0030 ++#define URB_FUNCTION_SYNC_RESET_PIPE 0x0030 ++#define URB_FUNCTION_SYNC_CLEAR_STALL 0x0031 ++#define URB_FUNCTION_CONTROL_TRANSFER_EX 0x0032 ++ ++#define USBD_PF_CHANGE_MAX_PACKET 0x00000001 ++ ++#define USBD_TRANSFER_DIRECTION_OUT 0 ++#define USBD_TRANSFER_DIRECTION_IN 1 ++ ++#define USBD_SHORT_TRANSFER_OK 0x00000002 ++#define USBD_START_ISO_TRANSFER_ASAP 0x00000004 ++#define USBD_DEFAULT_PIPE_TRANSFER 0x00000008 ++ ++#define USBD_TRANSFER_DIRECTION(flags) \ ++ ((flags) & USBD_TRANSFER_DIRECTION_IN) ++ ++enum pipe_type {UsbdPipeTypeControl = USB_ENDPOINT_XFER_CONTROL, ++ UsbdPipeTypeIsochronous = USB_ENDPOINT_XFER_ISOC, ++ UsbdPipeTypeBulk = USB_ENDPOINT_XFER_BULK, ++ UsbdPipeTypeInterrupt = USB_ENDPOINT_XFER_INT}; ++ ++#define USBD_IS_BULK_PIPE(pipe_handle) \ ++ (((pipe_handle)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) \ ++ == USB_ENDPOINT_XFER_BULK) ++ ++#define USBD_IS_INT_PIPE(pipe_handle) \ ++ (((pipe_handle)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) \ ++ == USB_ENDPOINT_XFER_INT) ++ ++#define USBD_PORT_ENABLED 0x00000001 ++#define USBD_PORT_CONNECTED 0x00000002 ++ ++typedef LONG USBD_STATUS; ++ ++#define USBD_STATUS_SUCCESS 0x0 ++#define USBD_STATUS_PENDING 0x40000000 ++#define USBD_STATUS_CANCELED 0x00010000 ++ ++#define USBD_STATUS_CRC 0xC0000001 ++#define USBD_STATUS_BTSTUFF 0xC0000002 ++#define USBD_STATUS_DATA_TOGGLE_MISMATCH 0xC0000003 ++#define USBD_STATUS_STALL_PID 0xC0000004 ++#define USBD_STATUS_DEV_NOT_RESPONDING 0xC0000005 ++#define USBD_STATUS_PID_CHECK_FAILURE 0xC0000006 ++#define USBD_STATUS_UNEXPECTED_PID 0xC0000007 ++#define USBD_STATUS_DATA_OVERRUN 0xC0000008 ++#define USBD_STATUS_DATA_UNDERRUN 0xC0000009 ++#define USBD_STATUS_RESERVED1 0xC000000A ++#define USBD_STATUS_RESERVED2 0xC000000B ++#define USBD_STATUS_BUFFER_OVERRUN 0xC000000C ++#define USBD_STATUS_BUFFER_UNDERRUN 0xC000000D ++#define USBD_STATUS_NOT_ACCESSED 0xC000000F ++#define USBD_STATUS_FIFO 0xC0000010 ++#define USBD_STATUS_XACT_ERROR 0xC0000011 ++#define USBD_STATUS_BABBLE_DETECTED 0xC0000012 ++#define USBD_STATUS_DATA_BUFFER_ERROR 0xC0000013 ++ ++#define USBD_STATUS_NOT_SUPPORTED 0xC0000E00 ++#define USBD_STATUS_BUFFER_TOO_SMALL 0xC0003000 ++#define USBD_STATUS_TIMEOUT 0xC0006000 ++#define USBD_STATUS_DEVICE_GONE 0xC0007000 ++ ++#define USBD_STATUS_NO_MEMORY 0x80000100 ++#define USBD_STATUS_INVALID_URB_FUNCTION 0x80000200 ++#define USBD_STATUS_INVALID_PARAMETER 0x80000300 ++#define USBD_STATUS_REQUEST_FAILED 0x80000500 ++#define USBD_STATUS_INVALID_PIPE_HANDLE 0x80000600 ++#define USBD_STATUS_ERROR_SHORT_TRANSFER 0x80000900 ++ ++#define USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE PAGE_SIZE ++ ++struct urb_hcd_area { ++ void *reserved8[8]; ++}; ++ ++struct usbd_pipe_information { ++ USHORT wMaxPacketSize; ++ UCHAR bEndpointAddress; ++ UCHAR bInterval; ++ enum pipe_type type; ++ struct usb_endpoint_descriptor *handle; ++ ULONG max_tx_size; ++ ULONG flags; ++}; ++ ++struct usbd_interface_information { ++ USHORT bLength; ++ UCHAR bInterfaceNumber; ++ UCHAR bAlternateSetting; ++ UCHAR bInterfaceClass; ++ UCHAR bInterfaceSubClass; ++ UCHAR bInterfaceProtocol; ++ UCHAR reserved; ++ void *handle; ++ ULONG bNumEndpoints; ++ struct usbd_pipe_information pipes[1]; ++}; ++ ++struct usbd_interface_list_entry { ++ struct usb_interface_descriptor *intf_desc; ++ struct usbd_interface_information *intf; ++}; ++ ++struct nt_urb_header { ++ USHORT length; ++ USHORT function; ++ USBD_STATUS status; ++ void *usbd_dev_handle; ++ ULONG usbd_flags; ++}; ++ ++struct usbd_select_interface { ++ struct nt_urb_header header; ++ void *handle; ++ struct usbd_interface_information intf; ++}; ++ ++struct usbd_select_configuration { ++ struct nt_urb_header header; ++ struct usb_config_descriptor *config; ++ void *handle; ++ struct usbd_interface_information intf; ++}; ++ ++struct usbd_control_descriptor_request { ++ struct nt_urb_header header; ++ void *reserved; ++ ULONG reserved0; ++ ULONG transfer_buffer_length; ++ void *transfer_buffer; ++ struct mdl *mdl; ++ union nt_urb *urb_link; ++ struct urb_hcd_area hca; ++ USHORT reserved1; ++ UCHAR index; ++ UCHAR desc_type; ++ USHORT language_id; ++ USHORT reserved2; ++}; ++ ++struct usbd_bulk_or_intr_transfer { ++ struct nt_urb_header header; ++ struct usb_endpoint_descriptor *pipe_handle; ++ ULONG transfer_flags; ++ ULONG transfer_buffer_length; ++ void *transfer_buffer; ++ struct mdl *mdl; ++ union nt_urb *urb_link; ++ struct urb_hcd_area hca; ++}; ++ ++struct usbd_pipe_request { ++ struct nt_urb_header header; ++ struct usb_endpoint_descriptor *pipe_handle; ++}; ++ ++struct usbd_vendor_or_class_request { ++ struct nt_urb_header header; ++ void *reserved; ++ ULONG transfer_flags; ++ ULONG transfer_buffer_length; ++ void *transfer_buffer; ++ struct mdl *mdl; ++ union nt_urb *link; ++ struct urb_hcd_area hca; ++ UCHAR reserved_bits; ++ UCHAR request; ++ USHORT value; ++ USHORT index; ++ USHORT reserved1; ++}; ++ ++struct urb_control_feature_request { ++ struct nt_urb_header header; ++ void *reserved; ++ ULONG reserved2; ++ ULONG reserved3; ++ void *reserved4; ++ struct mdl *reserved5; ++ union nt_urb *link; ++ struct urb_hcd_area hca; ++ USHORT reserved0; ++ USHORT feature_selector; ++ USHORT index; ++ USHORT reserved1; ++}; ++ ++struct urb_control_get_status_request { ++ struct nt_urb_header header; ++ void *reserved; ++ ULONG reserved0; ++ ULONG transfer_buffer_length; ++ void *transfer_buffer; ++ struct mdl *mdl; ++ union nt_urb *link; ++ struct urb_hcd_area hca; ++ UCHAR reserved1[4]; ++ USHORT index; ++ USHORT reserved2; ++}; ++ ++struct usbd_iso_packet_desc { ++ ULONG offset; ++ ULONG length; ++ USBD_STATUS status; ++}; ++ ++struct usbd_isochronous_transfer { ++ struct nt_urb_header header; ++ struct usb_endpoint_descriptor *pipe_handle; ++ ULONG transfer_flags; ++ ULONG transfer_buffer_length; ++ void *transfer_buffer; ++ struct mdl *mdl; ++ union nt_urb *urb_link; ++ struct urb_hcd_area hca; ++ ULONG start_frame; ++ ULONG number_of_packets; ++ ULONG error_count; ++ struct usbd_iso_packet_desc iso_packet[1]; ++}; ++ ++union nt_urb { ++ struct nt_urb_header header; ++ struct usbd_select_interface select_intf; ++ struct usbd_select_configuration select_conf; ++ struct usbd_bulk_or_intr_transfer bulk_int_transfer; ++ struct usbd_control_descriptor_request control_desc; ++ struct usbd_vendor_or_class_request vendor_class_request; ++ struct usbd_isochronous_transfer isochronous; ++ struct usbd_pipe_request pipe_req; ++ struct urb_control_feature_request feat_req; ++ struct urb_control_get_status_request status_req; ++}; ++ ++struct usbd_bus_interface_usbdi { ++ USHORT Size; ++ USHORT Version; ++ void *Context; ++ void *InterfaceReference; ++ void *InterfaceDereference; ++ void *GetUSBDIVersion; ++ void *QueryBusTime; ++ void *SubmitIsoOutUrb; ++ void *QueryBusInformation; ++ /* version 1 and above have following field */ ++ void *IsDeviceHighSpeed; ++ /* version 2 (and above) have following field */ ++ void *LogEntry; ++}; ++ ++struct usbd_bus_information_level { ++ ULONG TotalBandwidth; ++ ULONG ConsumedBandwidth; ++ /* level 1 and above have following fields */ ++ ULONG ControllerNameLength; ++ wchar_t ControllerName[1]; ++}; ++ ++#define USBDI_VERSION_XP 0x00000500 // Windows XP ++#define USB_HCD_CAPS_SUPPORTS_RT_THREADS 0x00000001 ++#define USB_BUSIF_USBDI_VERSION_0 0x0000 ++#define USB_BUSIF_USBDI_VERSION_1 0x0001 ++#define USB_BUSIF_USBDI_VERSION_2 0x0002 ++ ++struct usbd_version_info { ++ ULONG usbdi_version; ++ ULONG supported_usb_version; ++}; ++ ++struct usbd_idle_callback { ++ void *callback; ++ void *context; ++}; ++ ++#define NT_URB_STATUS(nt_urb) ((nt_urb)->header.status) ++ ++NTSTATUS wrap_submit_irp(struct device_object *pdo, struct irp *irp); ++void wrap_suspend_urbs(struct wrap_device *wd); ++void wrap_resume_urbs(struct wrap_device *wd); ++NTSTATUS usb_query_interface(struct wrap_device *wd, ++ struct io_stack_location *irp_sl); ++ ++#endif /* USB_H */ +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/win2lin_stubs.S linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/win2lin_stubs.S +--- linux-3.12.2/3rdparty/ndiswrapper/win2lin_stubs.S 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/win2lin_stubs.S 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,253 @@ ++/* ++ * Copyright (C) 2005 Karl Vogel, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include ++ ++#ifdef CONFIG_X86_64 ++ ++/* ++# Windows <---> Linux register usage conversion when calling functions ++# V = Volatile ++# NV = Non Volatile (needs to be saved) ++# ++# Win Lin ++# --------------------------------------- ++# Rax Return V Return V ++# Rbx NV NV ++# Rcx Arg1 V Arg4 V ++# Rdx Arg2 V Arg3 V ++# Rsi NV Arg2 V ++# Rdi NV Arg1 V ++# Rsp NV NV ++# Rbp NV NV ++# R8 Arg3 V Arg5 V ++# R9 Arg4 V Arg6 V ++# R10 V V ++# R11 V V ++# R12 NV NV ++# R13 NV NV ++# R14 NV NV ++# R15 NV NV ++# ++# In addition, Linux uses %rax to indicate number of SSE registers used ++# when variadic functions are called. Since there is no way to obtain this ++# from Windows, for now, we just assume this is 0 (hence %rax is cleared). ++# ++# Windows pushes arguments 5 and higher onto stack in case of integer ++# variables and 4 and higher in case of floating point variables (passed ++# in SSE registers). ++ ++In a windows function, the stackframe/registers look like this: ++ ++# 0x0048 .... ++# 0x0040 arg8 ++# 0x0038 arg7 ++# 0x0030 arg6 ++# 0x0028 arg5 ++# 0x0020 shadow/spill space for arg4 ++# 0x0018 shadow/spill space for arg3 ++# 0x0010 shadow/spill space for arg2 ++# 0x0008 shadow/spill space for arg1 ++# 0x0000 ret ++ ++# register spill space is same irrespective of number of arguments - even ++# if Windows function takes less than 4 arguments, 32 bytes above return ++# address is reserved for the function ++ ++In Linux it should look like: ++ ++# 0x0018 .... ++# 0x0010 arg8 ++# 0x0008 arg7 ++# 0x0000 ret ++ ++*/ ++ ++ .text ++ ++#define LINUX_REG_ARGS 6 ++#define LOOP_THRESHOLD 9 ++#define WORD_BYTES 8 ++ ++/* ++ * %rsi and %rdi must be saved because they are not saved by Linux calls, but ++ * Windows callers expect them to be saved. %rbp is saved to create a stack ++ * frame, which can help with debugging. We need to reserve space for an odd ++ * number of registers anyway to keep 16-bit alignment of the stack (one more ++ * position is used by the return address). ++ */ ++#define SAVED_REGS 3 ++ ++/* ++ * When calling the Linux function, several registers are saved on the stack. ++ * When passing more than 6 arguments, arguments starting with argument 7 are ++ * pushed to the stack as well. ++ * ++ * We also need to allocate an additional word on the stack to keep it aligned ++ * to the 16-bit boundary if the number of saved arguments plus one (for the ++ * return address) is odd. ++ */ ++ ++/* ++ * Number of arguments we pass on stack to the Linux function. ++ * The value of true is -1 in assembler, so we multiply it by another true ++ * value. ++ */ ++#define stack_args(argc) \ ++ ((0 < 1) * (argc > LINUX_REG_ARGS) * (argc - LINUX_REG_ARGS)) ++ ++/* Full required change of stack pointer, in words */ ++#define stack_words_raw(argc) (stack_args(argc) + SAVED_REGS + 1) ++ ++/* Full actual change of stack pointer, in words (must be even) */ ++#define stack_words_aligned(argc) ((stack_words_raw(argc) + 1) & ~1) ++ ++/* Space allocated for Linux arguments on stack */ ++#define stack_space(argc) \ ++ ((stack_words_aligned(argc) - SAVED_REGS - 1) * WORD_BYTES) ++ ++/* ++ * win2lin_win_arg(N, ARGC) gives the address of the Windows argument N out of ++ * total ARGC after the stack has been prepared for the Linux function call. ++ * ++ * When called from Windows, the Nth argument is at (N * 8)(%rsp). We add the ++ * stack space allocated by the Linux function to compensate for %rsp change. ++ * ++ * Don't call with N less than 5! ++ */ ++#define win2lin_win_arg(n, argc) \ ++ ((n + SAVED_REGS) * WORD_BYTES + stack_space(argc))(%rsp) ++ ++/* ++ * win2lin_lin_arg(N) gives the address of the Nth Linux argument on the extra ++ * Linux stack frame. When more than 6 arguments are used, %rsp points to the ++ * 7th argument. The Nth argument is therefore at ((N - 7) * 8)(%rsp). ++ * ++ * Don't call with N less than 7! ++ */ ++#define win2lin_lin_arg(n) ((n - 1 - LINUX_REG_ARGS) * WORD_BYTES)(%rsp) ++ ++/* Declare function LONGNAME, call function SHORTNAME with ARGC arguments */ ++.macro win2linm longname, shortname, argc ++ .type \longname, @function ++ ENTRY(\longname) ++ ++ /* Create a call frame - it's optional, but good for debugging */ ++ .cfi_startproc ++ push %rbp ++ .cfi_def_cfa %rsp, 2 * WORD_BYTES ++ .cfi_offset %rbp, -2 * WORD_BYTES ++ mov %rsp, %rbp ++ .cfi_def_cfa %rbp, 2 * WORD_BYTES ++ ++ /* ++ * Registers %rdi and %rsi are volatile on Linux, but not on Windows, ++ * so save them on the stack. ++ */ ++ push %rsi ++ push %rdi ++ ++ /* Allocate extra stack space for arguments 7 and up */ ++ sub $stack_space(\argc), %rsp ++ ++ /* ++ * Copy arguments 7 and up. We do it early, before %rdi and %rsi ++ * are used for arguments 1 and 2, so we don't have to save them. ++ * We still need to save %rcx if using a string copy. ++ */ ++ .if (\argc < LOOP_THRESHOLD) ++ /* If a few arguments, copy them individually through %r11 */ ++ .if (\argc >= 7) ++ mov win2lin_win_arg(7, \argc), %r11 ++ mov %r11, win2lin_lin_arg(7) ++ .endif ++ .if (\argc >= 8) ++ mov win2lin_win_arg(8, \argc), %r11 ++ mov %r11, win2lin_lin_arg(8) ++ .endif ++ .else ++ /* If there are many arguments, copy them in a loop */ ++ /* Save arg1 to %r11 */ ++ mov %rcx, %r11 ++ /* Source and destination */ ++ lea win2lin_win_arg(LINUX_REG_ARGS + 1, \argc), %rsi ++ lea win2lin_lin_arg(LINUX_REG_ARGS + 1), %rdi ++ /* Number of arguments to copy (%ecx zero-extends to %rcx) */ ++ mov $(\argc - LINUX_REG_ARGS), %ecx ++ rep movsq ++ /* Restore arg1 directly to %rdi */ ++ mov %r11, %rdi ++ .endif ++ ++ /* ++ * Argument 1 - %rcx on Windows, %rdi on Linux ++ * Micro-optimization - if we used loop, arg1 is already in %rdi ++ */ ++ .if (\argc >= 1) && (\argc < LOOP_THRESHOLD) ++ mov %rcx, %rdi ++ .endif ++ ++ /* Argument 2 - %rdx on Windows, %rsi on Linux */ ++ .if (\argc >= 2) ++ mov %rdx, %rsi ++ .endif ++ ++ /* Argument 3 - %r8 on Windows, %rdx on Linux */ ++ .if (\argc >= 3) ++ mov %r8, %rdx ++ .endif ++ ++ /* Argument 4 - %r9 on Windows, %rcx on Linux */ ++ .if (\argc >= 4) ++ mov %r9, %rcx ++ .endif ++ ++ /* Argument 5 - first argument on stack on Windows, %r8 Linux */ ++ .if (\argc >= 5) ++ mov win2lin_win_arg(5, \argc), %r8 ++ .endif ++ ++ /* Argument 6 - second argument on stack on Windows, %r9 Linux */ ++ .if (\argc >= 6) ++ mov win2lin_win_arg(6, \argc), %r9 ++ .endif ++ ++ /* %rax on Linux is the number of arguments in SSE registers (zero) */ ++ xor %rax, %rax ++ ++ /* Call the function */ ++ call \shortname ++ ++ /* Free stack space for arguments 7 and up */ ++ add $stack_space(\argc), %rsp ++ ++ /* Restore saved registers */ ++ pop %rdi ++ pop %rsi ++ ++ /* Return to Windows code */ ++ leave ++ .cfi_def_cfa %rsp, WORD_BYTES ++ .cfi_restore %rbp ++ ret ++ .cfi_endproc ++ .size \longname, (. - \longname) ++.endm ++ ++#define win2lin(name, argc) win2linm win2lin_ ## name ## _ ## argc, name, argc ++ ++#include "win2lin_stubs.h" ++ ++#endif /* CONFIG_X86_64 */ +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/winnt_types.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/winnt_types.h +--- linux-3.12.2/3rdparty/ndiswrapper/winnt_types.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/winnt_types.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,1701 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _WINNT_TYPES_H_ ++#define _WINNT_TYPES_H_ ++ ++#define TRUE 1 ++#define FALSE 0 ++ ++#define PASSIVE_LEVEL 0 ++#define APC_LEVEL 1 ++#define DISPATCH_LEVEL 2 ++#define DEVICE_LEVEL_BASE 4 ++ ++/* soft interrupts / bottom-half's are disabled at SOFT_IRQL */ ++#define SOFT_IRQL (DEVICE_LEVEL_BASE + 1) ++#define DIRQL (DEVICE_LEVEL_BASE + 2) ++ ++#define STATUS_WAIT_0 0 ++#define STATUS_SUCCESS 0 ++#define STATUS_ALERTED 0x00000101 ++#define STATUS_TIMEOUT 0x00000102 ++#define STATUS_PENDING 0x00000103 ++#define STATUS_FAILURE 0xC0000001 ++#define STATUS_NOT_IMPLEMENTED 0xC0000002 ++#define STATUS_INVALID_PARAMETER 0xC000000D ++#define STATUS_INVALID_DEVICE_REQUEST 0xC0000010 ++#define STATUS_MORE_PROCESSING_REQUIRED 0xC0000016 ++#define STATUS_ACCESS_DENIED 0xC0000022 ++#define STATUS_BUFFER_TOO_SMALL 0xC0000023 ++#define STATUS_OBJECT_NAME_INVALID 0xC0000023 ++#define STATUS_MUTANT_NOT_OWNED 0xC0000046 ++#define STATUS_RESOURCES 0xC000009A ++#define STATUS_DELETE_PENDING 0xC0000056 ++#define STATUS_INSUFFICIENT_RESOURCES 0xC000009A ++#define STATUS_NOT_SUPPORTED 0xC00000BB ++#define STATUS_INVALID_PARAMETER_2 0xC00000F0 ++#define STATUS_NO_MEMORY 0xC0000017 ++#define STATUS_CANCELLED 0xC0000120 ++#define STATUS_DEVICE_REMOVED 0xC00002B6 ++#define STATUS_DEVICE_NOT_CONNECTED 0xC000009D ++ ++#define STATUS_BUFFER_OVERFLOW 0x80000005 ++ ++#define SL_PENDING_RETURNED 0x01 ++#define SL_INVOKE_ON_CANCEL 0x20 ++#define SL_INVOKE_ON_SUCCESS 0x40 ++#define SL_INVOKE_ON_ERROR 0x80 ++ ++#define IRP_MJ_CREATE 0x00 ++#define IRP_MJ_CREATE_NAMED_PIPE 0x01 ++#define IRP_MJ_CLOSE 0x02 ++#define IRP_MJ_READ 0x03 ++#define IRP_MJ_WRITE 0x04 ++ ++#define IRP_MJ_DEVICE_CONTROL 0x0E ++#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0F ++#define IRP_MJ_POWER 0x16 ++#define IRP_MJ_SYSTEM_CONTROL 0x0E ++#define IRP_MJ_PNP 0x1b ++#define IRP_MJ_MAXIMUM_FUNCTION 0x1b ++ ++#define IRP_MN_WAIT_WAKE 0x00 ++#define IRP_MN_POWER_SEQUENCE 0x01 ++#define IRP_MN_SET_POWER 0x02 ++#define IRP_MN_QUERY_POWER 0x03 ++ ++#define IRP_MN_REGINFO 0x08 ++#define IRP_MN_REGINFO_EX 0x0b ++ ++#define IRP_MN_START_DEVICE 0x00 ++#define IRP_MN_QUERY_REMOVE_DEVICE 0x01 ++#define IRP_MN_REMOVE_DEVICE 0x02 ++#define IRP_MN_CANCEL_REMOVE_DEVICE 0x03 ++#define IRP_MN_STOP_DEVICE 0x04 ++#define IRP_MN_QUERY_STOP_DEVICE 0x05 ++#define IRP_MN_CANCEL_STOP_DEVICE 0x06 ++#define IRP_MN_QUERY_DEVICE_RELATIONS 0x07 ++#define IRP_MN_QUERY_INTERFACE 0x08 ++ ++#define IRP_BUFFERED_IO 0x00000010 ++#define IRP_DEALLOCATE_BUFFER 0x00000020 ++#define IRP_INPUT_OPERATION 0x00000040 ++ ++#define IRP_DEFFER_IO_COMPLETION 0x00000800 ++ ++#define THREAD_WAIT_OBJECTS 3 ++#define MAX_WAIT_OBJECTS 64 ++ ++#define LOW_PRIORITY 0 ++#define LOW_REALTIME_PRIORITY 16 ++#define HIGH_PRIORITY 31 ++#define MAXIMUM_PRIORITY 32 ++ ++#define PROCESSOR_FEATURE_MAX 64 ++ ++#define IO_NO_INCREMENT 0 ++ ++#define WMIREG_ACTION_REGISTER 1 ++#define WMIREG_ACTION_DEREGISTER 2 ++#define WMIREG_ACTION_REREGISTER 3 ++#define WMIREG_ACTION_UPDATE_GUIDS 4 ++ ++#define WMIREGISTER 0 ++#define WMIUPDATE 1 ++ ++#ifdef CONFIG_X86_64 ++#define wstdcall ++#define wfastcall ++#define noregparm ++#define regparm3 ++ ++#define KI_USER_SHARED_DATA 0xfffff78000000000UL ++ ++#else ++ ++#define noregparm __attribute__((regparm(0))) ++#define regparm3 __attribute__((regparm(3))) ++#define wstdcall __attribute__((__stdcall__, regparm(0))) ++#if defined(__GNUC__) && ((__GNUC__ == 3 && __GNUC_MINOR__ > 3) || __GNUC__ > 3) ++#undef fastcall ++#define wfastcall __attribute__((fastcall)) ++#else ++#error "gcc 3.4 or newer should be used for compiling this module" ++#endif ++ ++#define KI_USER_SHARED_DATA 0xffdf0000 ++ ++#endif ++ ++typedef u8 BOOLEAN; ++typedef u8 BYTE; ++typedef u8 *LPBYTE; ++typedef s8 CHAR; ++typedef u8 UCHAR; ++typedef s16 SHORT; ++typedef u16 USHORT; ++typedef u16 WORD; ++typedef s32 INT; ++typedef u32 UINT; ++typedef u32 DWORD; ++typedef s32 LONG; ++typedef u32 ULONG; ++typedef s64 LONGLONG; ++typedef u64 ULONGLONG; ++typedef u64 ULONGULONG; ++typedef u64 ULONG64; ++ ++typedef CHAR CCHAR; ++typedef USHORT wchar_t; ++typedef SHORT CSHORT; ++typedef LONGLONG LARGE_INTEGER; ++ ++typedef LONG NTSTATUS; ++ ++typedef LONG KPRIORITY; ++typedef LARGE_INTEGER PHYSICAL_ADDRESS; ++typedef UCHAR KIRQL; ++typedef CHAR KPROCESSOR_MODE; ++ ++/* ULONG_PTR is 32 bits on 32-bit platforms and 64 bits on 64-bit ++ * platform, which is same as 'unsigned long' in Linux */ ++typedef unsigned long ULONG_PTR; ++ ++typedef size_t SIZE_T; ++typedef ULONG_PTR KAFFINITY; ++typedef ULONG ACCESS_MASK; ++ ++typedef ULONG_PTR PFN_NUMBER; ++typedef ULONG SECURITY_INFORMATION; ++ ++/* non-negative numbers indicate success */ ++#define NT_SUCCESS(status) ((NTSTATUS)(status) >= 0) ++ ++struct ansi_string { ++ USHORT length; ++ USHORT max_length; ++ char *buf; ++}; ++ ++struct unicode_string { ++ USHORT length; ++ USHORT max_length; ++ wchar_t *buf; ++}; ++ ++struct nt_slist { ++ struct nt_slist *next; ++}; ++ ++#ifdef CONFIG_X86_64 ++/* it is not clear how nt_slist_head is used to store pointer to ++ * slists and depth; here we assume 'align' field is used to store ++ * depth and 'region' field is used to store slist pointers */ ++struct nt_slist_head { ++ union { ++ USHORT depth; ++ ULONGLONG align; ++ }; ++ union { ++ ULONGLONG region; ++ struct nt_slist *next; ++ }; ++} __attribute__((aligned(16))); ++typedef struct nt_slist_head nt_slist_header; ++#else ++union nt_slist_head { ++ ULONGLONG align; ++ struct { ++ struct nt_slist *next; ++ USHORT depth; ++ USHORT sequence; ++ }; ++}; ++typedef union nt_slist_head nt_slist_header; ++#endif ++ ++struct nt_list { ++ struct nt_list *next; ++ struct nt_list *prev; ++}; ++ ++typedef ULONG_PTR NT_SPIN_LOCK; ++ ++enum kdpc_importance {LowImportance, MediumImportance, HighImportance}; ++ ++struct kdpc; ++typedef void (*DPC)(struct kdpc *kdpc, void *ctx, void *arg1, ++ void *arg2) wstdcall; ++struct kdpc { ++ SHORT type; ++ UCHAR nr_cpu; ++ UCHAR importance; ++ struct nt_list list; ++ DPC func; ++ void *ctx; ++ void *arg1; ++ void *arg2; ++ union { ++ NT_SPIN_LOCK *lock; ++ /* 'lock' is not used; 'queued' represents whether ++ * kdpc is queued or not */ ++ int queued; ++ }; ++}; ++ ++enum pool_type { ++ NonPagedPool, PagedPool, NonPagedPoolMustSucceed, DontUseThisType, ++ NonPagedPoolCacheAligned, PagedPoolCacheAligned, ++ NonPagedPoolCacheAlignedMustS, MaxPoolType, ++ NonPagedPoolSession = 32, ++ PagedPoolSession = NonPagedPoolSession + 1, ++ NonPagedPoolMustSucceedSession = PagedPoolSession + 1, ++ DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1, ++ NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1, ++ PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1, ++ NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1 ++}; ++ ++enum memory_caching_type_orig { ++ MmFrameBufferCached = 2 ++}; ++ ++enum memory_caching_type { ++ MmNonCached = FALSE, MmCached = TRUE, ++ MmWriteCombined = MmFrameBufferCached, MmHardwareCoherentCached, ++ MmNonCachedUnordered, MmUSWCCached, MmMaximumCacheType ++}; ++ ++enum lock_operation { ++ IoReadAccess, IoWriteAccess, IoModifyAccess ++}; ++ ++enum mode { ++ KernelMode, UserMode, MaximumMode ++}; ++ ++struct mdl { ++ struct mdl *next; ++ CSHORT size; ++ CSHORT flags; ++ /* NdisFreeBuffer doesn't pass pool, so we store pool in ++ * unused field 'process' */ ++ union { ++ void *process; ++ void *pool; ++ }; ++ void *mappedsystemva; ++ void *startva; ++ ULONG bytecount; ++ ULONG byteoffset; ++}; ++ ++#define MDL_MAPPED_TO_SYSTEM_VA 0x0001 ++#define MDL_PAGES_LOCKED 0x0002 ++#define MDL_SOURCE_IS_NONPAGED_POOL 0x0004 ++#define MDL_ALLOCATED_FIXED_SIZE 0x0008 ++#define MDL_PARTIAL 0x0010 ++#define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020 ++#define MDL_IO_PAGE_READ 0x0040 ++#define MDL_WRITE_OPERATION 0x0080 ++#define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100 ++#define MDL_FREE_EXTRA_PTES 0x0200 ++#define MDL_IO_SPACE 0x0800 ++#define MDL_NETWORK_HEADER 0x1000 ++#define MDL_MAPPING_CAN_FAIL 0x2000 ++#define MDL_ALLOCATED_MUST_SUCCEED 0x4000 ++ ++#define MDL_POOL_ALLOCATED 0x0400 ++#define MDL_CACHE_ALLOCATED 0x8000 ++ ++#define PAGE_START(ptr) ((void *)((ULONG_PTR)(ptr) & ~(PAGE_SIZE - 1))) ++#define BYTE_OFFSET(ptr) ((ULONG)((ULONG_PTR)(ptr) & (PAGE_SIZE - 1))) ++ ++#define MmGetMdlByteCount(mdl) ((mdl)->bytecount) ++#define MmGetMdlVirtualAddress(mdl) ((mdl)->startva + (mdl)->byteoffset) ++#define MmGetMdlByteOffset(mdl) ((mdl)->byteoffset) ++#define MmGetSystemAddressForMdl(mdl) ((mdl)->mappedsystemva) ++#define MmGetSystemAddressForMdlSafe(mdl, priority) ((mdl)->mappedsystemva) ++#define MmGetMdlPfnArray(mdl) ((PFN_NUMBER *)(mdl + 1)) ++#define MmInitializeMdl(mdl, baseva, length) \ ++do { \ ++ (mdl)->next = NULL; \ ++ (mdl)->size = MmSizeOfMdl(baseva, length); \ ++ (mdl)->flags = 0; \ ++ (mdl)->startva = PAGE_START(baseva); \ ++ (mdl)->byteoffset = BYTE_OFFSET(baseva); \ ++ (mdl)->bytecount = length; \ ++ (mdl)->mappedsystemva = baseva; \ ++ TRACE4("%p %p %p %d %d", (mdl), baseva, (mdl)->startva, \ ++ (mdl)->byteoffset, length); \ ++} while (0) ++ ++struct kdevice_queue_entry { ++ struct nt_list list; ++ ULONG sort_key; ++ BOOLEAN inserted; ++}; ++ ++struct kdevice_queue { ++ USHORT type; ++ USHORT size; ++ struct nt_list list; ++ NT_SPIN_LOCK lock; ++ BOOLEAN busy; ++}; ++ ++struct wait_context_block { ++ struct kdevice_queue_entry wait_queue_entry; ++ void *device_routine; ++ void *device_context; ++ ULONG num_regs; ++ void *device_object; ++ void *current_irp; ++ void *buffer_chaining_dpc; ++}; ++ ++struct wait_block { ++ struct nt_list list; ++ struct task_struct *thread; ++ void *object; ++ int *wait_done; ++ USHORT wait_key; ++ USHORT wait_type; ++}; ++ ++struct dispatcher_header { ++ UCHAR type; ++ UCHAR absolute; ++ UCHAR size; ++ UCHAR inserted; ++ LONG signal_state; ++ struct nt_list wait_blocks; ++}; ++ ++enum event_type { ++ NotificationEvent, ++ SynchronizationEvent, ++}; ++ ++enum timer_type { ++ NotificationTimer = NotificationEvent, ++ SynchronizationTimer = SynchronizationEvent, ++}; ++ ++enum dh_type { ++ NotificationObject = NotificationEvent, ++ SynchronizationObject = SynchronizationEvent, ++ MutexObject, ++ SemaphoreObject, ++ ThreadObject, ++}; ++ ++enum wait_type { ++ WaitAll, WaitAny ++}; ++ ++/* objects that use dispatcher_header have it as the first field, so ++ * whenever we need to initialize dispatcher_header, we can convert ++ * that object into a nt_event and access dispatcher_header */ ++struct nt_event { ++ struct dispatcher_header dh; ++}; ++ ++struct wrap_timer; ++ ++#define WRAP_TIMER_MAGIC 47697249 ++ ++struct nt_timer { ++ struct dispatcher_header dh; ++ /* We can't fit Linux timer in this structure. Instead of ++ * padding the nt_timer structure, we replace due_time field ++ * with *wrap_timer and allocate memory for it when nt_timer is ++ * initialized */ ++ union { ++ ULONGLONG due_time; ++ struct wrap_timer *wrap_timer; ++ }; ++ struct nt_list nt_timer_list; ++ struct kdpc *kdpc; ++ union { ++ LONG period; ++ LONG wrap_timer_magic; ++ }; ++}; ++ ++struct nt_mutex { ++ struct dispatcher_header dh; ++ struct nt_list list; ++ struct task_struct *owner_thread; ++ BOOLEAN abandoned; ++ BOOLEAN apc_disable; ++}; ++ ++struct nt_semaphore { ++ struct dispatcher_header dh; ++ LONG limit; ++}; ++ ++struct nt_thread { ++ struct dispatcher_header dh; ++ /* the rest in Windows is a long structure; since this ++ * structure is opaque to drivers, we just define what we ++ * need */ ++ int pid; ++ NTSTATUS status; ++ struct task_struct *task; ++ struct nt_list irps; ++ NT_SPIN_LOCK lock; ++ KPRIORITY prio; ++}; ++ ++#define set_object_type(dh, type) ((dh)->type = (type)) ++#define is_notify_object(dh) ((dh)->type == NotificationObject) ++#define is_synch_object(dh) ((dh)->type == SynchronizationObject) ++#define is_mutex_object(dh) ((dh)->type == MutexObject) ++#define is_semaphore_object(dh) ((dh)->type == SemaphoreObject) ++#define is_nt_thread_object(dh) ((dh)->type == ThreadObject) ++ ++#define IO_TYPE_ADAPTER 1 ++#define IO_TYPE_CONTROLLER 2 ++#define IO_TYPE_DEVICE 3 ++#define IO_TYPE_DRIVER 4 ++#define IO_TYPE_FILE 5 ++#define IO_TYPE_IRP 6 ++#define IO_TYPE_DEVICE_OBJECT_EXTENSION 13 ++ ++struct irp; ++struct dev_obj_ext; ++struct driver_object; ++ ++struct device_object { ++ CSHORT type; ++ USHORT size; ++ LONG ref_count; ++ struct driver_object *drv_obj; ++ struct device_object *next; ++ struct device_object *attached; ++ struct irp *current_irp; ++ void *io_timer; ++ ULONG flags; ++ ULONG characteristics; ++ void *vpb; ++ void *dev_ext; ++ CCHAR stack_count; ++ union { ++ struct nt_list queue_list; ++ struct wait_context_block wcb; ++ } queue; ++ ULONG align_req; ++ struct kdevice_queue dev_queue; ++ struct kdpc dpc; ++ ULONG active_threads; ++ void *security_desc; ++ struct nt_event lock; ++ USHORT sector_size; ++ USHORT spare1; ++ struct dev_obj_ext *dev_obj_ext; ++ void *reserved; ++}; ++ ++struct dev_obj_ext { ++ CSHORT type; ++ CSHORT size; ++ struct device_object *dev_obj; ++ struct device_object *attached_to; ++}; ++ ++struct io_status_block { ++ union { ++ NTSTATUS status; ++ void *pointer; ++ }; ++ ULONG_PTR info; ++}; ++ ++#ifdef CONFIG_X86_64 ++struct io_status_block32 { ++ NTSTATUS status; ++ ULONG info; ++}; ++#endif ++ ++#define DEVICE_TYPE ULONG ++ ++struct driver_extension; ++ ++typedef NTSTATUS driver_dispatch_t(struct device_object *dev_obj, ++ struct irp *irp) wstdcall; ++ ++struct driver_object { ++ CSHORT type; ++ CSHORT size; ++ struct device_object *dev_obj; ++ ULONG flags; ++ void *start; ++ ULONG driver_size; ++ void *section; ++ struct driver_extension *drv_ext; ++ struct unicode_string name; ++ struct unicode_string *hardware_database; ++ void *fast_io_dispatch; ++ void *init; ++ void *start_io; ++ void (*unload)(struct driver_object *driver) wstdcall; ++ driver_dispatch_t *major_func[IRP_MJ_MAXIMUM_FUNCTION + 1]; ++}; ++ ++struct driver_extension { ++ struct driver_object *drv_obj; ++ NTSTATUS (*add_device)(struct driver_object *drv_obj, ++ struct device_object *dev_obj); ++ ULONG count; ++ struct unicode_string service_key_name; ++ struct nt_list custom_ext; ++}; ++ ++struct custom_ext { ++ struct nt_list list; ++ void *client_id; ++}; ++ ++struct wrap_bin_file; ++ ++struct file_object { ++ CSHORT type; ++ CSHORT size; ++ struct device_object *dev_obj; ++ void *volume_parameter_block; ++ void *fs_context; ++ void *fs_context2; ++ void *section_object_pointer; ++ void *private_cache_map; ++ NTSTATUS final_status; ++ union { ++ struct file_object *related_file_object; ++ struct wrap_bin_file *wrap_bin_file; ++ }; ++ BOOLEAN lock_operation; ++ BOOLEAN delete_pending; ++ BOOLEAN read_access; ++ BOOLEAN write_access; ++ BOOLEAN delete_access; ++ BOOLEAN shared_read; ++ BOOLEAN shared_write; ++ BOOLEAN shared_delete; ++ ULONG flags; ++ struct unicode_string _name_; ++ LARGE_INTEGER current_byte_offset; ++ ULONG waiters; ++ ULONG busy; ++ void *last_lock; ++ struct nt_event lock; ++ struct nt_event event; ++ void *completion_context; ++}; ++ ++#ifdef CONFIG_X86_64 ++#define POINTER_ALIGN __attribute__((aligned(8))) ++#else ++#define POINTER_ALIGN ++#endif ++ ++#define CACHE_ALIGN __attribute__((aligned(128))) ++ ++enum system_power_state { ++ PowerSystemUnspecified = 0, ++ PowerSystemWorking, PowerSystemSleeping1, PowerSystemSleeping2, ++ PowerSystemSleeping3, PowerSystemHibernate, PowerSystemShutdown, ++ PowerSystemMaximum, ++}; ++ ++enum device_power_state { ++ PowerDeviceUnspecified = 0, ++ PowerDeviceD0, PowerDeviceD1, PowerDeviceD2, PowerDeviceD3, ++ PowerDeviceMaximum, ++}; ++ ++union power_state { ++ enum system_power_state system_state; ++ enum device_power_state device_state; ++}; ++ ++enum power_state_type { ++ SystemPowerState = 0, DevicePowerState, ++}; ++ ++enum power_action { ++ PowerActionNone = 0, ++ PowerActionReserved, PowerActionSleep, PowerActionHibernate, ++ PowerActionShutdown, PowerActionShutdownReset, PowerActionShutdownOff, ++ PowerActionWarmEject, ++}; ++ ++struct guid { ++ ULONG data1; ++ USHORT data2; ++ USHORT data3; ++ UCHAR data4[8]; ++}; ++ ++struct nt_interface { ++ USHORT size; ++ USHORT version; ++ void *context; ++ void (*reference)(void *context) wstdcall; ++ void (*dereference)(void *context) wstdcall; ++}; ++ ++enum interface_type { ++ InterfaceTypeUndefined = -1, Internal, Isa, Eisa, MicroChannel, ++ TurboChannel, PCIBus, VMEBus, NuBus, PCMCIABus, CBus, MPIBus, ++ MPSABus, ProcessorInternal, InternalPowerBus, PNPISABus, ++ PNPBus, MaximumInterfaceType, ++}; ++ ++#define CmResourceTypeNull 0 ++#define CmResourceTypePort 1 ++#define CmResourceTypeInterrupt 2 ++#define CmResourceTypeMemory 3 ++#define CmResourceTypeDma 4 ++#define CmResourceTypeDeviceSpecific 5 ++#define CmResourceTypeBusNumber 6 ++#define CmResourceTypeMaximum 7 ++ ++#define CmResourceTypeNonArbitrated 128 ++#define CmResourceTypeConfigData 128 ++#define CmResourceTypeDevicePrivate 129 ++#define CmResourceTypePcCardConfig 130 ++#define CmResourceTypeMfCardConfig 131 ++ ++enum cm_share_disposition { ++ CmResourceShareUndetermined = 0, CmResourceShareDeviceExclusive, ++ CmResourceShareDriverExclusive, CmResourceShareShared ++}; ++ ++#define CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE 0 ++#define CM_RESOURCE_INTERRUPT_LATCHED 1 ++#define CM_RESOURCE_MEMORY_READ_WRITE 0x0000 ++#define CM_RESOURCE_MEMORY_READ_ONLY 0x0001 ++#define CM_RESOURCE_MEMORY_WRITE_ONLY 0x0002 ++#define CM_RESOURCE_MEMORY_PREFETCHABLE 0x0004 ++ ++#define CM_RESOURCE_MEMORY_COMBINEDWRITE 0x0008 ++#define CM_RESOURCE_MEMORY_24 0x0010 ++#define CM_RESOURCE_MEMORY_CACHEABLE 0x0020 ++ ++#define CM_RESOURCE_PORT_MEMORY 0x0000 ++#define CM_RESOURCE_PORT_IO 0x0001 ++#define CM_RESOURCE_PORT_10_BIT_DECODE 0x0004 ++#define CM_RESOURCE_PORT_12_BIT_DECODE 0x0008 ++#define CM_RESOURCE_PORT_16_BIT_DECODE 0x0010 ++#define CM_RESOURCE_PORT_POSITIVE_DECODE 0x0020 ++#define CM_RESOURCE_PORT_PASSIVE_DECODE 0x0040 ++#define CM_RESOURCE_PORT_WINDOW_DECODE 0x0080 ++ ++#define CM_RESOURCE_DMA_8 0x0000 ++#define CM_RESOURCE_DMA_16 0x0001 ++#define CM_RESOURCE_DMA_32 0x0002 ++#define CM_RESOURCE_DMA_8_AND_16 0x0004 ++#define CM_RESOURCE_DMA_BUS_MASTER 0x0008 ++#define CM_RESOURCE_DMA_TYPE_A 0x0010 ++#define CM_RESOURCE_DMA_TYPE_B 0x0020 ++#define CM_RESOURCE_DMA_TYPE_F 0x0040 ++ ++#define MAX_RESOURCES 20 ++ ++#pragma pack(push,4) ++struct cm_partial_resource_descriptor { ++ UCHAR type; ++ UCHAR share; ++ USHORT flags; ++ union { ++ struct { ++ PHYSICAL_ADDRESS start; ++ ULONG length; ++ } generic; ++ struct { ++ PHYSICAL_ADDRESS start; ++ ULONG length; ++ } port; ++ struct { ++ ULONG level; ++ ULONG vector; ++ KAFFINITY affinity; ++ } interrupt; ++ struct { ++ PHYSICAL_ADDRESS start; ++ ULONG length; ++ } memory; ++ struct { ++ ULONG channel; ++ ULONG port; ++ ULONG reserved1; ++ } dma; ++ struct { ++ ULONG data[3]; ++ } device_private; ++ struct { ++ ULONG start; ++ ULONG length; ++ ULONG reserved; ++ } bus_number; ++ struct { ++ ULONG data_size; ++ ULONG reserved1; ++ ULONG reserved2; ++ } device_specific_data; ++ } u; ++}; ++#pragma pack(pop) ++ ++struct cm_partial_resource_list { ++ USHORT version; ++ USHORT revision; ++ ULONG count; ++ struct cm_partial_resource_descriptor partial_descriptors[1]; ++}; ++ ++struct cm_full_resource_descriptor { ++ enum interface_type interface_type; ++ ULONG bus_number; ++ struct cm_partial_resource_list partial_resource_list; ++}; ++ ++struct cm_resource_list { ++ ULONG count; ++ struct cm_full_resource_descriptor list[1]; ++}; ++ ++enum file_info_class { ++ FileDirectoryInformation = 1, ++ FileBasicInformation = 4, ++ FileStandardInformation = 5, ++ FileNameInformation = 9, ++ FilePositionInformation = 14, ++ FileAlignmentInformation = 17, ++ FileNetworkOpenInformation = 34, ++ FileAttributeTagInformation = 35, ++ FileMaximumInformation = 41, ++}; ++ ++enum fs_info_class { ++ FileFsVolumeInformation = 1, ++ /* ... */ ++ FileFsMaximumInformation = 9, ++}; ++ ++enum device_relation_type { ++ BusRelations, EjectionRelations, PowerRelations, RemovalRelations, ++ TargetDeviceRelation, SingleBusRelations, ++}; ++ ++enum bus_query_id_type { ++ BusQueryDeviceID = 0, BusQueryHardwareIDs = 1, ++ BusQueryCompatibleIDs = 2, BusQueryInstanceID = 3, ++ BusQueryDeviceSerialNumber = 4, ++}; ++ ++enum device_text_type { ++ DeviceTextDescription = 0, DeviceTextLocationInformation = 1, ++}; ++ ++enum device_usage_notification_type { ++ DeviceUsageTypeUndefined, DeviceUsageTypePaging, ++ DeviceUsageTypeHibernation, DevbiceUsageTypeDumpFile, ++}; ++ ++#define METHOD_BUFFERED 0 ++#define METHOD_IN_DIRECT 1 ++#define METHOD_OUT_DIRECT 2 ++#define METHOD_NEITHER 3 ++ ++#define CTL_CODE(dev_type, func, method, access) \ ++ (((dev_type) << 16) | ((access) << 14) | ((func) << 2) | (method)) ++ ++#define IO_METHOD_FROM_CTL_CODE(code) (code & 0x3) ++ ++#ifndef CONFIG_X86_64 ++#pragma pack(push,4) ++#endif ++struct io_stack_location { ++ UCHAR major_fn; ++ UCHAR minor_fn; ++ UCHAR flags; ++ UCHAR control; ++ union { ++ struct { ++ void *security_context; ++ ULONG options; ++ USHORT POINTER_ALIGN file_attributes; ++ USHORT share_access; ++ ULONG POINTER_ALIGN ea_length; ++ } create; ++ struct { ++ ULONG length; ++ ULONG POINTER_ALIGN key; ++ LARGE_INTEGER byte_offset; ++ } read; ++ struct { ++ ULONG length; ++ ULONG POINTER_ALIGN key; ++ LARGE_INTEGER byte_offset; ++ } write; ++ struct { ++ ULONG length; ++ enum file_info_class POINTER_ALIGN file_info_class; ++ } query_file; ++ struct { ++ ULONG length; ++ enum file_info_class POINTER_ALIGN file_info_class; ++ struct file_object *file_object; ++ union { ++ struct { ++ BOOLEAN replace_if_exists; ++ BOOLEAN advance_only; ++ }; ++ ULONG cluster_count; ++ void *delete_handle; ++ }; ++ } set_file; ++ struct { ++ ULONG length; ++ enum fs_info_class POINTER_ALIGN fs_info_class; ++ } query_volume; ++ struct { ++ ULONG output_buf_len; ++ ULONG POINTER_ALIGN input_buf_len; ++ ULONG POINTER_ALIGN code; ++ void *type3_input_buf; ++ } dev_ioctl; ++ struct { ++ SECURITY_INFORMATION security_info; ++ ULONG POINTER_ALIGN length; ++ } query_security; ++ struct { ++ SECURITY_INFORMATION security_info; ++ void *security_descriptor; ++ } set_security; ++ struct { ++ void *vpb; ++ struct device_object *device_object; ++ } mount_volume; ++ struct { ++ void *vpb; ++ struct device_object *device_object; ++ } verify_volume; ++ struct { ++ void *srb; ++ } scsi; ++ struct { ++ enum device_relation_type type; ++ } query_device_relations; ++ struct { ++ const struct guid *type; ++ USHORT size; ++ USHORT version; ++ struct nt_interface *intf; ++ void *intf_data; ++ } query_intf; ++ struct { ++ void *capabilities; ++ } device_capabilities; ++ struct { ++ void *io_resource_requirement_list; ++ } filter_resource_requirements; ++ struct { ++ ULONG which_space; ++ void *buffer; ++ ULONG offset; ++ ULONG POINTER_ALIGN length; ++ } read_write_config; ++ struct { ++ BOOLEAN lock; ++ } set_lock; ++ struct { ++ enum bus_query_id_type id_type; ++ } query_id; ++ struct { ++ enum device_text_type device_text_type; ++ ULONG POINTER_ALIGN locale_id; ++ } query_device_text; ++ struct { ++ BOOLEAN in_path; ++ BOOLEAN reserved[3]; ++ enum device_usage_notification_type POINTER_ALIGN type; ++ } usage_notification; ++ struct { ++ enum system_power_state power_state; ++ } wait_wake; ++ struct { ++ void *power_sequence; ++ } power_sequence; ++ struct { ++ ULONG sys_context; ++ enum power_state_type POINTER_ALIGN type; ++ union power_state POINTER_ALIGN state; ++ enum power_action POINTER_ALIGN shutdown_type; ++ } power; ++ struct { ++ struct cm_resource_list *allocated_resources; ++ struct cm_resource_list *allocated_resources_translated; ++ } start_device; ++ struct { ++ ULONG_PTR provider_id; ++ void *data_path; ++ ULONG buf_len; ++ void *buf; ++ } wmi; ++ struct { ++ void *arg1; ++ void *arg2; ++ void *arg3; ++ void *arg4; ++ } others; ++ } params; ++ struct device_object *dev_obj; ++ struct file_object *file_obj; ++ NTSTATUS (*completion_routine)(struct device_object *, ++ struct irp *, void *) wstdcall; ++ void *context; ++}; ++#ifndef CONFIG_X86_64 ++#pragma pack(pop) ++#endif ++ ++struct kapc { ++ CSHORT type; ++ CSHORT size; ++ ULONG spare0; ++ struct nt_thread *thread; ++ struct nt_list list; ++ void *kernele_routine; ++ void *rundown_routine; ++ void *normal_routine; ++ void *normal_context; ++ void *sys_arg1; ++ void *sys_arg2; ++ CCHAR apc_state_index; ++ KPROCESSOR_MODE apc_mode; ++ BOOLEAN inserted; ++}; ++ ++#define IRP_NOCACHE 0x00000001 ++#define IRP_SYNCHRONOUS_API 0x00000004 ++#define IRP_ASSOCIATED_IRP 0x00000008 ++ ++enum urb_state { ++ URB_INVALID = 1, URB_ALLOCATED, URB_SUBMITTED, ++ URB_COMPLETED, URB_FREE, URB_SUSPEND, URB_INT_UNLINKED }; ++ ++struct wrap_urb { ++ struct nt_list list; ++ enum urb_state state; ++ struct nt_list complete_list; ++ unsigned int flags; ++ struct urb *urb; ++ struct irp *irp; ++#ifdef USB_DEBUG ++ unsigned int id; ++#endif ++}; ++ ++struct irp { ++ SHORT type; ++ USHORT size; ++ struct mdl *mdl; ++ ULONG flags; ++ union { ++ struct irp *master_irp; ++ LONG irp_count; ++ void *system_buffer; ++ } associated_irp; ++ struct nt_list thread_list; ++ struct io_status_block io_status; ++ KPROCESSOR_MODE requestor_mode; ++ BOOLEAN pending_returned; ++ CHAR stack_count; ++ CHAR current_location; ++ BOOLEAN cancel; ++ KIRQL cancel_irql; ++ CCHAR apc_env; ++ UCHAR alloc_flags; ++ struct io_status_block *user_status; ++ struct nt_event *user_event; ++ union { ++ struct { ++ void *user_apc_routine; ++ void *user_apc_context; ++ } async_params; ++ LARGE_INTEGER alloc_size; ++ } overlay; ++ void (*cancel_routine)(struct device_object *, struct irp *) wstdcall; ++ void *user_buf; ++ union { ++ struct { ++ union { ++ struct kdevice_queue_entry dev_q_entry; ++ struct { ++ void *driver_context[4]; ++ }; ++ }; ++ void *thread; ++ char *aux_buf; ++ struct { ++ struct nt_list list; ++ union { ++ struct io_stack_location *csl; ++ ULONG packet_type; ++ }; ++ }; ++ struct file_object *file_object; ++ } overlay; ++ union { ++ struct kapc apc; ++ /* space for apc is used for ndiswrapper ++ * specific fields */ ++ struct { ++ struct wrap_urb *wrap_urb; ++ struct wrap_device *wrap_device; ++ }; ++ }; ++ void *completion_key; ++ } tail; ++}; ++ ++#define IoSizeOfIrp(stack_count) \ ++ ((USHORT)(sizeof(struct irp) + \ ++ ((stack_count) * sizeof(struct io_stack_location)))) ++#define IoGetCurrentIrpStackLocation(irp) \ ++ (irp)->tail.overlay.csl ++#define IoGetNextIrpStackLocation(irp) \ ++ (IoGetCurrentIrpStackLocation(irp) - 1) ++#define IoGetPreviousIrpStackLocation(irp) \ ++ (IoGetCurrentIrpStackLocation(irp) + 1) ++ ++#define IoSetNextIrpStackLocation(irp) \ ++do { \ ++ KIRQL _irql_; \ ++ IoAcquireCancelSpinLock(&_irql_); \ ++ (irp)->current_location--; \ ++ IoGetCurrentIrpStackLocation(irp)--; \ ++ IoReleaseCancelSpinLock(_irql_); \ ++} while (0) ++ ++#define IoSkipCurrentIrpStackLocation(irp) \ ++do { \ ++ KIRQL _irql_; \ ++ IoAcquireCancelSpinLock(&_irql_); \ ++ (irp)->current_location++; \ ++ IoGetCurrentIrpStackLocation(irp)++; \ ++ IoReleaseCancelSpinLock(_irql_); \ ++} while (0) ++ ++static inline void ++IoCopyCurrentIrpStackLocationToNext(struct irp *irp) ++{ ++ struct io_stack_location *next; ++ next = IoGetNextIrpStackLocation(irp); ++ memcpy(next, IoGetCurrentIrpStackLocation(irp), ++ offsetof(struct io_stack_location, completion_routine)); ++ next->control = 0; ++} ++ ++static inline void ++IoSetCompletionRoutine(struct irp *irp, void *routine, void *context, ++ BOOLEAN success, BOOLEAN error, BOOLEAN cancel) ++{ ++ struct io_stack_location *irp_sl = IoGetNextIrpStackLocation(irp); ++ irp_sl->completion_routine = routine; ++ irp_sl->context = context; ++ irp_sl->control = 0; ++ if (success) ++ irp_sl->control |= SL_INVOKE_ON_SUCCESS; ++ if (error) ++ irp_sl->control |= SL_INVOKE_ON_ERROR; ++ if (cancel) ++ irp_sl->control |= SL_INVOKE_ON_CANCEL; ++} ++ ++#define IoMarkIrpPending(irp) \ ++ (IoGetCurrentIrpStackLocation((irp))->control |= SL_PENDING_RETURNED) ++#define IoUnmarkIrpPending(irp) \ ++ (IoGetCurrentIrpStackLocation((irp))->control &= ~SL_PENDING_RETURNED) ++ ++#define IRP_SL(irp, n) (((struct io_stack_location *)((irp) + 1)) + (n)) ++#define IRP_DRIVER_CONTEXT(irp) (irp)->tail.overlay.driver_context ++#define IoIrpThread(irp) ((irp)->tail.overlay.thread) ++ ++#define IRP_URB(irp) \ ++ (union nt_urb *)(IoGetCurrentIrpStackLocation(irp)->params.others.arg1) ++ ++#define IRP_WRAP_DEVICE(irp) (irp)->tail.wrap_device ++#define IRP_WRAP_URB(irp) (irp)->tail.wrap_urb ++ ++struct wmi_guid_reg_info { ++ struct guid *guid; ++ ULONG instance_count; ++ ULONG flags; ++}; ++ ++struct wmilib_context { ++ ULONG guid_count; ++ struct wmi_guid_reg_info *guid_list; ++ void *query_wmi_reg_info; ++ void *query_wmi_data_block; ++ void *set_wmi_data_block; ++ void *set_wmi_data_item; ++ void *execute_wmi_method; ++ void *wmi_function_control; ++}; ++ ++enum key_value_information_class { ++ KeyValueBasicInformation, KeyValueFullInformation, ++ KeyValuePartialInformation, KeyValueFullInformationAlign64, ++ KeyValuePartialInformationAlign64 ++}; ++ ++struct file_name_info { ++ ULONG length; ++ wchar_t *name; ++}; ++ ++struct file_std_info { ++ LARGE_INTEGER alloc_size; ++ LARGE_INTEGER eof; ++ ULONG num_links; ++ BOOLEAN delete_pending; ++ BOOLEAN dir; ++}; ++ ++enum nt_obj_type { ++ NT_OBJ_EVENT = 10, NT_OBJ_MUTEX, NT_OBJ_THREAD, NT_OBJ_TIMER, ++ NT_OBJ_SEMAPHORE, ++}; ++ ++enum common_object_type { ++ OBJECT_TYPE_NONE, OBJECT_TYPE_DEVICE, OBJECT_TYPE_DRIVER, ++ OBJECT_TYPE_NT_THREAD, OBJECT_TYPE_FILE, OBJECT_TYPE_CALLBACK, ++}; ++ ++struct common_object_header { ++ struct nt_list list; ++ enum common_object_type type; ++ UINT size; ++ UINT ref_count; ++ BOOLEAN close_in_process; ++ BOOLEAN permanent; ++ struct unicode_string name; ++}; ++ ++#define OBJECT_TO_HEADER(object) \ ++ (struct common_object_header *)((void *)(object) - \ ++ sizeof(struct common_object_header)) ++#define OBJECT_SIZE(size) \ ++ ((size) + sizeof(struct common_object_header)) ++#define HEADER_TO_OBJECT(hdr) \ ++ ((void *)(hdr) + sizeof(struct common_object_header)) ++#define HANDLE_TO_OBJECT(handle) HEADER_TO_OBJECT(handle) ++#define HANDLE_TO_HEADER(handle) (handle) ++ ++enum work_queue_type { ++ CriticalWorkQueue, DelayedWorkQueue, HyperCriticalWorkQueue, ++ MaximumWorkQueue ++}; ++ ++typedef void (*NTOS_WORK_FUNC)(void *arg1, void *arg2) wstdcall; ++ ++struct io_workitem { ++ enum work_queue_type type; ++ struct device_object *dev_obj; ++ NTOS_WORK_FUNC worker_routine; ++ void *context; ++}; ++ ++struct io_workitem_entry { ++ struct nt_list list; ++ struct io_workitem *io_workitem; ++}; ++ ++enum mm_page_priority { ++ LowPagePriority, NormalPagePriority = 16, HighPagePriority = 32 ++}; ++ ++enum kinterrupt_mode { ++ LevelSensitive, Latched ++}; ++ ++enum ntos_wait_reason { ++ Executive, FreePage, PageIn, PoolAllocation, DelayExecution, ++ Suspended, UserRequest, WrExecutive, WrFreePage, WrPageIn, ++ WrPoolAllocation, WrDelayExecution, WrSuspended, WrUserRequest, ++ WrEventPair, WrQueue, WrLpcReceive, WrLpcReply, WrVirtualMemory, ++ WrPageOut, WrRendezvous, Spare2, Spare3, Spare4, Spare5, Spare6, ++ WrKernel, MaximumWaitReason ++}; ++ ++typedef enum ntos_wait_reason KWAIT_REASON; ++ ++typedef void *LOOKASIDE_ALLOC_FUNC(enum pool_type pool_type, ++ SIZE_T size, ULONG tag) wstdcall; ++typedef void LOOKASIDE_FREE_FUNC(void *) wstdcall; ++ ++struct npaged_lookaside_list { ++ nt_slist_header head; ++ USHORT depth; ++ USHORT maxdepth; ++ ULONG totalallocs; ++ union { ++ ULONG allocmisses; ++ ULONG allochits; ++ } u1; ++ ULONG totalfrees; ++ union { ++ ULONG freemisses; ++ ULONG freehits; ++ } u2; ++ enum pool_type pool_type; ++ ULONG tag; ++ ULONG size; ++ LOOKASIDE_ALLOC_FUNC *alloc_func; ++ LOOKASIDE_FREE_FUNC *free_func; ++ struct nt_list list; ++ ULONG lasttotallocs; ++ union { ++ ULONG lastallocmisses; ++ ULONG lastallochits; ++ } u3; ++ ULONG pad[2]; ++#ifndef CONFIG_X86_64 ++ NT_SPIN_LOCK obsolete; ++#endif ++} ++#ifdef CONFIG_X86_64 ++CACHE_ALIGN ++#endif ++; ++ ++enum device_registry_property { ++ DevicePropertyDeviceDescription, DevicePropertyHardwareID, ++ DevicePropertyCompatibleIDs, DevicePropertyBootConfiguration, ++ DevicePropertyBootConfigurationTranslated, ++ DevicePropertyClassName, DevicePropertyClassGuid, ++ DevicePropertyDriverKeyName, DevicePropertyManufacturer, ++ DevicePropertyFriendlyName, DevicePropertyLocationInformation, ++ DevicePropertyPhysicalDeviceObjectName, DevicePropertyBusTypeGuid, ++ DevicePropertyLegacyBusType, DevicePropertyBusNumber, ++ DevicePropertyEnumeratorName, DevicePropertyAddress, ++ DevicePropertyUINumber, DevicePropertyInstallState, ++ DevicePropertyRemovalPolicy ++}; ++ ++enum trace_information_class { ++ TraceIdClass, TraceHandleClass, TraceEnableFlagsClass, ++ TraceEnableLevelClass, GlobalLoggerHandleClass, EventLoggerHandleClass, ++ AllLoggerHandlesClass, TraceHandleByNameClass ++}; ++ ++struct kinterrupt; ++typedef BOOLEAN (*PKSERVICE_ROUTINE)(struct kinterrupt *interrupt, ++ void *context) wstdcall; ++typedef BOOLEAN (*PKSYNCHRONIZE_ROUTINE)(void *context) wstdcall; ++ ++struct kinterrupt { ++ ULONG vector; ++ KAFFINITY cpu_mask; ++ NT_SPIN_LOCK lock; ++ NT_SPIN_LOCK *actual_lock; ++ BOOLEAN shared; ++ BOOLEAN save_fp; ++ union { ++ CHAR processor_number; ++#ifdef CONFIG_DEBUG_SHIRQ ++ CHAR enabled; ++#endif ++ } u; ++ PKSERVICE_ROUTINE isr; ++ void *isr_ctx; ++ struct nt_list list; ++ KIRQL irql; ++ KIRQL synch_irql; ++ enum kinterrupt_mode mode; ++}; ++ ++struct time_fields { ++ CSHORT year; ++ CSHORT month; ++ CSHORT day; ++ CSHORT hour; ++ CSHORT minute; ++ CSHORT second; ++ CSHORT milliseconds; ++ CSHORT weekday; ++}; ++ ++struct object_attributes { ++ ULONG length; ++ void *root_dir; ++ struct unicode_string *name; ++ ULONG attributes; ++ void *security_descr; ++ void *security_qos; ++}; ++ ++typedef void (*PCALLBACK_FUNCTION)(void *context, void *arg1, ++ void *arg2) wstdcall; ++ ++struct callback_object; ++struct callback_func { ++ PCALLBACK_FUNCTION func; ++ void *context; ++ struct nt_list list; ++ struct callback_object *object; ++}; ++ ++struct callback_object { ++ NT_SPIN_LOCK lock; ++ struct nt_list list; ++ struct nt_list callback_funcs; ++ BOOLEAN allow_multiple_callbacks; ++ struct object_attributes *attributes; ++}; ++ ++enum section_inherit { ++ ViewShare = 1, ViewUnmap = 2 ++}; ++ ++struct ksystem_time { ++ ULONG low_part; ++ LONG high1_time; ++ LONG high2_time; ++}; ++ ++enum nt_product_type { ++ nt_product_win_nt = 1, nt_product_lan_man_nt, nt_product_server ++}; ++ ++enum alt_arch_type { ++ arch_type_standard, arch_type_nex98x86, end_alternatives ++}; ++ ++struct kuser_shared_data { ++ ULONG tick_count; ++ ULONG tick_count_multiplier; ++ volatile struct ksystem_time interrupt_time; ++ volatile struct ksystem_time system_time; ++ volatile struct ksystem_time time_zone_bias; ++ USHORT image_number_low; ++ USHORT image_number_high; ++ wchar_t nt_system_root[260]; ++ ULONG max_stack_trace_depth; ++ ULONG crypto_exponent; ++ ULONG time_zone_id; ++ ULONG large_page_min; ++ ULONG reserved2[7]; ++ enum nt_product_type nt_product_type; ++ BOOLEAN product_type_is_valid; ++ ULONG nt_major_version; ++ ULONG nt_minor_version; ++ BOOLEAN processor_features[PROCESSOR_FEATURE_MAX]; ++ ULONG reserved1; ++ ULONG reserved3; ++ volatile LONG time_slip; ++ enum alt_arch_type alt_arch_type; ++ LARGE_INTEGER system_expiration_date; ++ ULONG suite_mask; ++ BOOLEAN kdbg_enabled; ++ volatile ULONG active_console; ++ volatile ULONG dismount_count; ++ ULONG com_plus_package; ++ ULONG last_system_rite_event_tick_count; ++ ULONG num_phys_pages; ++ BOOLEAN safe_boot_mode; ++ ULONG trace_log; ++ ULONGLONG fill0; ++ ULONGLONG sys_call[4]; ++ union { ++ volatile struct ksystem_time tick_count; ++ volatile ULONG64 tick_count_quad; ++ } tick; ++}; ++ ++#define REG_NONE (0) ++#define REG_SZ (1) ++#define REG_EXPAND_SZ (2) ++#define REG_BINARY (3) ++#define REG_DWORD (4) ++ ++#define RTL_REGISTRY_ABSOLUTE 0 ++#define RTL_REGISTRY_SERVICES 1 ++#define RTL_REGISTRY_CONTROL 2 ++#define RTL_REGISTRY_WINDOWS_NT 3 ++#define RTL_REGISTRY_DEVICEMAP 4 ++#define RTL_REGISTRY_USER 5 ++#define RTL_REGISTRY_MAXIMUM 6 ++#define RTL_REGISTRY_HANDLE 0x40000000 ++#define RTL_REGISTRY_OPTIONAL 0x80000000 ++ ++#define RTL_QUERY_REGISTRY_SUBKEY 0x00000001 ++#define RTL_QUERY_REGISTRY_TOPKEY 0x00000002 ++#define RTL_QUERY_REGISTRY_REQUIRED 0x00000004 ++#define RTL_QUERY_REGISTRY_NOVALUE 0x00000008 ++#define RTL_QUERY_REGISTRY_NOEXPAND 0x00000010 ++#define RTL_QUERY_REGISTRY_DIRECT 0x00000020 ++#define RTL_QUERY_REGISTRY_DELETE 0x00000040 ++ ++typedef NTSTATUS (*PRTL_QUERY_REGISTRY_ROUTINE)(wchar_t *name, ULONG type, ++ void *data, ULONG length, ++ void *context, ++ void *entry) wstdcall; ++ ++struct rtl_query_registry_table { ++ PRTL_QUERY_REGISTRY_ROUTINE query_func; ++ ULONG flags; ++ wchar_t *name; ++ void *context; ++ ULONG def_type; ++ void *def_data; ++ ULONG def_length; ++}; ++ ++struct io_remove_lock { ++ BOOLEAN removed; ++ BOOLEAN reserved[3]; ++ LONG io_count; ++ struct nt_event remove_event; ++}; ++ ++struct io_error_log_packet { ++ UCHAR major_fn_code; ++ UCHAR retry_count; ++ USHORT dump_data_size; ++ USHORT nr_of_strings; ++ USHORT string_offset; ++ USHORT event_category; ++ NTSTATUS error_code; ++ ULONG unique_error_value; ++ NTSTATUS final_status; ++ ULONG sequence_number; ++ ULONG io_control_code; ++ LARGE_INTEGER device_offset; ++ ULONG dump_data[1]; ++}; ++ ++/* some of the functions below are slightly different from DDK's ++ * implementation; e.g., Insert functions return appropriate ++ * pointer */ ++ ++/* instead of using Linux's lists, we implement list manipulation ++ * functions because nt_list is used by drivers and we don't want to ++ * worry about Linux's list being different from nt_list (right now ++ * they are same, but in future they could be different) */ ++ ++static inline void InitializeListHead(struct nt_list *head) ++{ ++ head->next = head->prev = head; ++} ++ ++static inline BOOLEAN IsListEmpty(struct nt_list *head) ++{ ++ if (head == head->next) ++ return TRUE; ++ else ++ return FALSE; ++} ++ ++static inline void RemoveEntryList(struct nt_list *entry) ++{ ++ entry->prev->next = entry->next; ++ entry->next->prev = entry->prev; ++} ++ ++static inline struct nt_list *RemoveHeadList(struct nt_list *head) ++{ ++ struct nt_list *entry; ++ ++ entry = head->next; ++ if (entry == head) ++ return NULL; ++ else { ++ RemoveEntryList(entry); ++ return entry; ++ } ++} ++ ++static inline struct nt_list *RemoveTailList(struct nt_list *head) ++{ ++ struct nt_list *entry; ++ ++ entry = head->prev; ++ if (entry == head) ++ return NULL; ++ else { ++ RemoveEntryList(entry); ++ return entry; ++ } ++} ++ ++static inline void InsertListEntry(struct nt_list *entry, struct nt_list *prev, ++ struct nt_list *next) ++{ ++ next->prev = entry; ++ entry->next = next; ++ entry->prev = prev; ++ prev->next = entry; ++} ++ ++static inline struct nt_list *InsertHeadList(struct nt_list *head, ++ struct nt_list *entry) ++{ ++ struct nt_list *ret; ++ ++ if (IsListEmpty(head)) ++ ret = NULL; ++ else ++ ret = head->next; ++ ++ InsertListEntry(entry, head, head->next); ++ return ret; ++} ++ ++static inline struct nt_list *InsertTailList(struct nt_list *head, ++ struct nt_list *entry) ++{ ++ struct nt_list *ret; ++ ++ if (IsListEmpty(head)) ++ ret = NULL; ++ else ++ ret = head->prev; ++ ++ InsertListEntry(entry, head->prev, head); ++ return ret; ++} ++ ++#define nt_list_for_each(pos, head) \ ++ for (pos = (head)->next; pos != (head); pos = pos->next) ++ ++#define nt_list_for_each_entry(pos, head, member) \ ++ for (pos = container_of((head)->next, typeof(*pos), member); \ ++ &pos->member != (head); \ ++ pos = container_of(pos->member.next, typeof(*pos), member)) ++ ++#define nt_list_for_each_safe(pos, n, head) \ ++ for (pos = (head)->next, n = pos->next; pos != (head); \ ++ pos = n, n = pos->next) ++ ++/* device object flags */ ++#define DO_VERIFY_VOLUME 0x00000002 ++#define DO_BUFFERED_IO 0x00000004 ++#define DO_EXCLUSIVE 0x00000008 ++#define DO_DIRECT_IO 0x00000010 ++#define DO_MAP_IO_BUFFER 0x00000020 ++#define DO_DEVICE_HAS_NAME 0x00000040 ++#define DO_DEVICE_INITIALIZING 0x00000080 ++#define DO_SYSTEM_BOOT_PARTITION 0x00000100 ++#define DO_LONG_TERM_REQUESTS 0x00000200 ++#define DO_NEVER_LAST_DEVICE 0x00000400 ++#define DO_SHUTDOWN_REGISTERED 0x00000800 ++#define DO_BUS_ENUMERATED_DEVICE 0x00001000 ++#define DO_POWER_PAGABLE 0x00002000 ++#define DO_POWER_INRUSH 0x00004000 ++#define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 ++ ++/* Various supported device types (used with IoCreateDevice()) */ ++ ++#define FILE_DEVICE_BEEP 0x00000001 ++#define FILE_DEVICE_CD_ROM 0x00000002 ++#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 ++#define FILE_DEVICE_CONTROLLER 0x00000004 ++#define FILE_DEVICE_DATALINK 0x00000005 ++#define FILE_DEVICE_DFS 0x00000006 ++#define FILE_DEVICE_DISK 0x00000007 ++#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 ++#define FILE_DEVICE_FILE_SYSTEM 0x00000009 ++#define FILE_DEVICE_INPORT_PORT 0x0000000A ++#define FILE_DEVICE_KEYBOARD 0x0000000B ++#define FILE_DEVICE_MAILSLOT 0x0000000C ++#define FILE_DEVICE_MIDI_IN 0x0000000D ++#define FILE_DEVICE_MIDI_OUT 0x0000000E ++#define FILE_DEVICE_MOUSE 0x0000000F ++#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 ++#define FILE_DEVICE_NAMED_PIPE 0x00000011 ++#define FILE_DEVICE_NETWORK 0x00000012 ++#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 ++#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 ++#define FILE_DEVICE_NULL 0x00000015 ++#define FILE_DEVICE_PARALLEL_PORT 0x00000016 ++#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 ++#define FILE_DEVICE_PRINTER 0x00000018 ++#define FILE_DEVICE_SCANNER 0x00000019 ++#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001A ++#define FILE_DEVICE_SERIAL_PORT 0x0000001B ++#define FILE_DEVICE_SCREEN 0x0000001C ++#define FILE_DEVICE_SOUND 0x0000001D ++#define FILE_DEVICE_STREAMS 0x0000001E ++#define FILE_DEVICE_TAPE 0x0000001F ++#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 ++#define FILE_DEVICE_TRANSPORT 0x00000021 ++#define FILE_DEVICE_UNKNOWN 0x00000022 ++#define FILE_DEVICE_VIDEO 0x00000023 ++#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 ++#define FILE_DEVICE_WAVE_IN 0x00000025 ++#define FILE_DEVICE_WAVE_OUT 0x00000026 ++#define FILE_DEVICE_8042_PORT 0x00000027 ++#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 ++#define FILE_DEVICE_BATTERY 0x00000029 ++#define FILE_DEVICE_BUS_EXTENDER 0x0000002A ++#define FILE_DEVICE_MODEM 0x0000002B ++#define FILE_DEVICE_VDM 0x0000002C ++#define FILE_DEVICE_MASS_STORAGE 0x0000002D ++#define FILE_DEVICE_SMB 0x0000002E ++#define FILE_DEVICE_KS 0x0000002F ++#define FILE_DEVICE_CHANGER 0x00000030 ++#define FILE_DEVICE_SMARTCARD 0x00000031 ++#define FILE_DEVICE_ACPI 0x00000032 ++#define FILE_DEVICE_DVD 0x00000033 ++#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 ++#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 ++#define FILE_DEVICE_DFS_VOLUME 0x00000036 ++#define FILE_DEVICE_SERENUM 0x00000037 ++#define FILE_DEVICE_TERMSRV 0x00000038 ++#define FILE_DEVICE_KSEC 0x00000039 ++#define FILE_DEVICE_FIPS 0x0000003A ++ ++/* Device characteristics */ ++ ++#define FILE_REMOVABLE_MEDIA 0x00000001 ++#define FILE_READ_ONLY_DEVICE 0x00000002 ++#define FILE_FLOPPY_DISKETTE 0x00000004 ++#define FILE_WRITE_ONCE_MEDIA 0x00000008 ++#define FILE_REMOTE_DEVICE 0x00000010 ++#define FILE_DEVICE_IS_MOUNTED 0x00000020 ++#define FILE_VIRTUAL_VOLUME 0x00000040 ++#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 ++#define FILE_DEVICE_SECURE_OPEN 0x00000100 ++ ++#define FILE_READ_DATA 0x0001 ++#define FILE_WRITE_DATA 0x0002 ++ ++#define FILE_SUPERSEDED 0x00000000 ++#define FILE_OPENED 0x00000001 ++#define FILE_CREATED 0x00000002 ++#define FILE_OVERWRITTEN 0x00000003 ++#define FILE_EXISTS 0x00000004 ++#define FILE_DOES_NOT_EXIST 0x00000005 ++ ++ ++#endif /* WINNT_TYPES_H */ +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/workqueue.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/workqueue.c +--- linux-3.12.2/3rdparty/ndiswrapper/workqueue.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/workqueue.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,279 @@ ++/* ++ * Copyright (C) 2006 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ntoskernel.h" ++ ++struct workqueue_thread { ++ spinlock_t lock; ++ struct task_struct *task; ++ struct completion *completion; ++ char name[16]; ++ int pid; ++ /* whether any work_structs pending? <0 implies quit */ ++ s8 pending; ++ /* list of work_structs pending */ ++ struct list_head work_list; ++}; ++ ++struct workq_thread_data { ++ struct workqueue_struct *workq; ++ int index; ++}; ++ ++struct wrap_workqueue_struct { ++ u8 singlethread; ++ u8 qon; ++ int num_cpus; ++ struct workqueue_thread threads[0]; ++}; ++ ++static void wrap_destroy_wq_on(struct workqueue_struct *workq, int cpu); ++ ++static int workq_thread(void *data) ++{ ++ struct workq_thread_data *thread_data = data; ++ struct workqueue_thread *thread; ++ struct workqueue_struct *workq; ++ struct work_struct *work; ++ ++ workq = thread_data->workq; ++ thread = &workq->threads[thread_data->index]; ++ WORKTRACE("%p, %d, %p", workq, thread_data->index, thread); ++ strncpy(thread->name, current->comm, sizeof(thread->name)); ++ ++ daemonize(thread->name); ++ set_user_nice(current, -5); ++ ++ if (thread->task != current) { ++ WARNING("invalid task: %p, %p", thread->task, current); ++ thread->task = current; ++ } ++ thread->pid = current->pid; ++ complete(xchg(&thread->completion, NULL)); ++ WORKTRACE("%s (%d) started", thread->name, thread->pid); ++ while (1) { ++ if (wait_condition(thread->pending, 0, TASK_INTERRUPTIBLE) < 0) { ++ /* TODO: deal with signal */ ++ WARNING("signal not blocked?"); ++ flush_signals(current); ++ continue; ++ } ++ while (1) { ++ struct list_head *entry; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&thread->lock, flags); ++ if (list_empty(&thread->work_list)) { ++ struct completion *completion; ++ if (thread->pending < 0) { ++ spin_unlock_irqrestore(&thread->lock, ++ flags); ++ goto out; ++ } ++ thread->pending = 0; ++ completion = thread->completion; ++ thread->completion = NULL; ++ spin_unlock_irqrestore(&thread->lock, flags); ++ if (completion) ++ complete(completion); ++ break; ++ } ++ entry = thread->work_list.next; ++ work = list_entry(entry, struct work_struct, list); ++ if (xchg(&work->thread, NULL)) ++ list_del(entry); ++ else ++ work = NULL; ++ spin_unlock_irqrestore(&thread->lock, flags); ++ DBG_BLOCK(4) { ++ WORKTRACE("%p, %p", work, thread); ++ } ++ if (work) ++ work->func(work->data); ++ } ++ } ++ ++out: ++ WORKTRACE("%s exiting", thread->name); ++ thread->pid = 0; ++ return 0; ++} ++ ++static int wrap_queue_work_on(struct workqueue_struct *workq, ++ struct work_struct *work, int cpu) ++{ ++ struct workqueue_thread *thread = &workq->threads[cpu]; ++ unsigned long flags; ++ int ret; ++ ++ assert(thread->pid > 0); ++ DBG_BLOCK(4) { ++ WORKTRACE("%p, %d", workq, cpu); ++ } ++ spin_lock_irqsave(&thread->lock, flags); ++ if (work->thread) ++ ret = 0; ++ else { ++ work->thread = thread; ++ list_add_tail(&work->list, &thread->work_list); ++ thread->pending = 1; ++ wake_up_process(thread->task); ++ ret = 1; ++ } ++ spin_unlock_irqrestore(&thread->lock, flags); ++ return ret; ++} ++ ++int wrap_queue_work(struct workqueue_struct *workq, struct work_struct *work) ++{ ++ if (num_online_cpus() == 1 || workq->singlethread) ++ return wrap_queue_work_on(workq, work, 0); ++ else { ++ typeof(workq->qon) qon; ++ /* work is queued on threads in a round-robin fashion */ ++ do { ++ qon = workq->qon % workq->num_cpus; ++ atomic_inc_var(workq->qon); ++ } while (!workq->threads[qon].pid); ++ return wrap_queue_work_on(workq, work, qon); ++ } ++} ++ ++void wrap_cancel_work(struct work_struct *work) ++{ ++ struct workqueue_thread *thread; ++ unsigned long flags; ++ ++ WORKTRACE("%p", work); ++ if ((thread = xchg(&work->thread, NULL))) { ++ WORKTRACE("%p", thread); ++ spin_lock_irqsave(&thread->lock, flags); ++ list_del(&work->list); ++ spin_unlock_irqrestore(&thread->lock, flags); ++ } ++} ++ ++struct workqueue_struct *wrap_create_wq(const char *name, u8 singlethread, ++ u8 freeze) ++{ ++ struct completion started; ++ struct workqueue_struct *workq; ++ int i, n; ++ ++ if (singlethread) ++ n = 1; ++ else ++ n = num_online_cpus(); ++ workq = kzalloc(sizeof(*workq) + n * sizeof(workq->threads[0]), ++ GFP_KERNEL); ++ if (!workq) { ++ WARNING("couldn't allocate memory"); ++ return NULL; ++ } ++ WORKTRACE("%p", workq); ++ workq->singlethread = singlethread; ++ init_completion(&started); ++ for_each_online_cpu(i) { ++ struct workq_thread_data thread_data; ++ spin_lock_init(&workq->threads[i].lock); ++ INIT_LIST_HEAD(&workq->threads[i].work_list); ++ reinit_completion(&started); ++ workq->threads[i].completion = &started; ++ thread_data.workq = workq; ++ thread_data.index = i; ++ WORKTRACE("%p, %d, %p", workq, i, &workq->threads[i]); ++ workq->threads[i].task = ++ kthread_create(workq_thread, &thread_data, ++ "%s/%d", name, i); ++ if (IS_ERR(workq->threads[i].task)) { ++ int j; ++ for (j = 0; j < i; j++) ++ wrap_destroy_wq_on(workq, j); ++ kfree(workq); ++ WARNING("couldn't start thread %s", name); ++ return NULL; ++ } ++#ifdef PF_NOFREEZE ++ if (!freeze) ++ workq->threads[i].task->flags |= PF_NOFREEZE; ++#endif ++ kthread_bind(workq->threads[i].task, i); ++ workq->num_cpus = max(workq->num_cpus, i); ++ wake_up_process(workq->threads[i].task); ++ wait_for_completion(&started); ++ WORKTRACE("%s, %d: %p, %d", name, i, ++ workq, workq->threads[i].pid); ++ if (singlethread) ++ break; ++ } ++ workq->num_cpus++; ++ return workq; ++} ++ ++static void wrap_flush_wq_on(struct workqueue_struct *workq, int cpu) ++{ ++ struct workqueue_thread *thread = &workq->threads[cpu]; ++ struct completion done; ++ ++ WORKTRACE("%p: %d, %s", workq, cpu, thread->name); ++ init_completion(&done); ++ thread->completion = &done; ++ thread->pending = 1; ++ wake_up_process(thread->task); ++ wait_for_completion(&done); ++ return; ++} ++ ++void wrap_flush_wq(struct workqueue_struct *workq) ++{ ++ int i, n; ++ ++ WORKTRACE("%p", workq); ++ if (workq->singlethread) ++ n = 1; ++ else ++ n = num_online_cpus(); ++ for (i = 0; i < n; i++) ++ wrap_flush_wq_on(workq, i); ++} ++ ++static void wrap_destroy_wq_on(struct workqueue_struct *workq, int cpu) ++{ ++ struct workqueue_thread *thread = &workq->threads[cpu]; ++ ++ WORKTRACE("%p: %d, %s", workq, cpu, thread->name); ++ if (!thread->pid) ++ return; ++ thread->pending = -1; ++ wake_up_process(thread->task); ++ while (thread->pid) { ++ WORKTRACE("%d", thread->pid); ++ schedule(); ++ } ++} ++ ++void wrap_destroy_wq(struct workqueue_struct *workq) ++{ ++ int i, n; ++ ++ WORKTRACE("%p", workq); ++ if (workq->singlethread) ++ n = 1; ++ else ++ n = num_online_cpus(); ++ for (i = 0; i < n; i++) ++ wrap_destroy_wq_on(workq, i); ++ kfree(workq); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.c +--- linux-3.12.2/3rdparty/ndiswrapper/wrapmem.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,366 @@ ++/* ++ * Copyright (C) 2006 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#define _WRAPMEM_C_ ++ ++#include "ntoskernel.h" ++#include "wrapmem.h" ++ ++struct slack_alloc_info { ++ struct nt_list list; ++ size_t size; ++}; ++ ++#if ALLOC_DEBUG > 1 ++static struct nt_list allocs; ++#endif ++ ++static struct nt_list slack_allocs; ++static spinlock_t alloc_lock; ++ ++#if ALLOC_DEBUG ++const char *alloc_type_name[ALLOC_TYPE_MAX] = { ++ "kmalloc_atomic", ++ "kmalloc_nonatomic", ++ "vmalloc_atomic", ++ "vmalloc_nonatomic", ++ "kmalloc_slack", ++ "pages" ++}; ++ ++struct alloc_info { ++ enum alloc_type type; ++ size_t size; ++#if ALLOC_DEBUG > 1 ++ struct nt_list list; ++ const char *file; ++ int line; ++ ULONG tag; ++#endif ++}; ++ ++static atomic_t alloc_sizes[ALLOC_TYPE_MAX]; ++#endif ++ ++/* allocate memory and add it to list of allocated pointers; if a ++ * driver doesn't free this memory for any reason (buggy driver or we ++ * allocate space behind driver's back since we need more space than ++ * corresponding Windows structure provides etc.), this gets freed ++ * automatically when module is unloaded ++ */ ++void *slack_kmalloc(size_t size) ++{ ++ struct slack_alloc_info *info; ++ ++ ENTER4("size = %zu", size); ++ info = kmalloc(size + sizeof(*info), irql_gfp()); ++ if (!info) ++ return NULL; ++ info->size = size; ++ spin_lock_bh(&alloc_lock); ++ InsertTailList(&slack_allocs, &info->list); ++ spin_unlock_bh(&alloc_lock); ++#if ALLOC_DEBUG ++ atomic_add(size, &alloc_sizes[ALLOC_TYPE_SLACK]); ++#endif ++ TRACE4("%p, %p", info, info + 1); ++ EXIT4(return info + 1); ++} ++ ++/* free pointer and remove from list of allocated pointers */ ++void slack_kfree(void *ptr) ++{ ++ struct slack_alloc_info *info; ++ ++ ENTER4("%p", ptr); ++ info = ptr - sizeof(*info); ++ spin_lock_bh(&alloc_lock); ++ RemoveEntryList(&info->list); ++ spin_unlock_bh(&alloc_lock); ++#if ALLOC_DEBUG ++ atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]); ++#endif ++ kfree(info); ++ EXIT4(return); ++} ++ ++void *slack_kzalloc(size_t size) ++{ ++ void *ptr = slack_kmalloc(size); ++ if (ptr) ++ memset(ptr, 0, size); ++ return ptr; ++} ++ ++#if ALLOC_DEBUG ++void *wrap_kmalloc(size_t size, gfp_t flags, const char *file, int line) ++{ ++ struct alloc_info *info; ++ ++ info = kmalloc(size + sizeof(*info), flags); ++ if (!info) ++ return NULL; ++ if (flags & GFP_ATOMIC) ++ info->type = ALLOC_TYPE_KMALLOC_ATOMIC; ++ else ++ info->type = ALLOC_TYPE_KMALLOC_NON_ATOMIC; ++ info->size = size; ++ atomic_add(size, &alloc_sizes[info->type]); ++#if ALLOC_DEBUG > 1 ++ info->file = file; ++ info->line = line; ++ info->tag = 0; ++ spin_lock_bh(&alloc_lock); ++ InsertTailList(&allocs, &info->list); ++ spin_unlock_bh(&alloc_lock); ++#endif ++ TRACE4("%p", info + 1); ++ return info + 1; ++} ++ ++void *wrap_kzalloc(size_t size, gfp_t flags, const char *file, int line) ++{ ++ void *ptr = wrap_kmalloc(size, flags, file, line); ++ if (ptr) ++ memset(ptr, 0, size); ++ return ptr; ++} ++ ++void wrap_kfree(void *ptr) ++{ ++ struct alloc_info *info; ++ ++ TRACE4("%p", ptr); ++ if (!ptr) ++ return; ++ info = ptr - sizeof(*info); ++ atomic_sub(info->size, &alloc_sizes[info->type]); ++#if ALLOC_DEBUG > 1 ++ spin_lock_bh(&alloc_lock); ++ RemoveEntryList(&info->list); ++ spin_unlock_bh(&alloc_lock); ++ if (!(info->type == ALLOC_TYPE_KMALLOC_ATOMIC || ++ info->type == ALLOC_TYPE_KMALLOC_NON_ATOMIC)) { ++ WARNING("invalid type: %d", info->type); ++ return; ++ } ++#endif ++ kfree(info); ++} ++ ++void *wrap_vmalloc(unsigned long size, const char *file, int line) ++{ ++ struct alloc_info *info; ++ ++ info = vmalloc(size + sizeof(*info)); ++ if (!info) ++ return NULL; ++ info->type = ALLOC_TYPE_VMALLOC_NON_ATOMIC; ++ info->size = size; ++ atomic_add(size, &alloc_sizes[info->type]); ++#if ALLOC_DEBUG > 1 ++ info->file = file; ++ info->line = line; ++ info->tag = 0; ++ spin_lock_bh(&alloc_lock); ++ InsertTailList(&allocs, &info->list); ++ spin_unlock_bh(&alloc_lock); ++#endif ++ return info + 1; ++} ++ ++void *wrap__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot, ++ const char *file, int line) ++{ ++ struct alloc_info *info; ++ ++ info = __vmalloc(size + sizeof(*info), gfp_mask, prot); ++ if (!info) ++ return NULL; ++ if (gfp_mask & GFP_ATOMIC) ++ info->type = ALLOC_TYPE_VMALLOC_ATOMIC; ++ else ++ info->type = ALLOC_TYPE_VMALLOC_NON_ATOMIC; ++ info->size = size; ++ atomic_add(size, &alloc_sizes[info->type]); ++#if ALLOC_DEBUG > 1 ++ info->file = file; ++ info->line = line; ++ info->tag = 0; ++ spin_lock_bh(&alloc_lock); ++ InsertTailList(&allocs, &info->list); ++ spin_unlock_bh(&alloc_lock); ++#endif ++ return info + 1; ++} ++ ++void wrap_vfree(void *ptr) ++{ ++ struct alloc_info *info; ++ ++ info = ptr - sizeof(*info); ++ atomic_sub(info->size, &alloc_sizes[info->type]); ++#if ALLOC_DEBUG > 1 ++ spin_lock_bh(&alloc_lock); ++ RemoveEntryList(&info->list); ++ spin_unlock_bh(&alloc_lock); ++ if (!(info->type == ALLOC_TYPE_VMALLOC_ATOMIC || ++ info->type == ALLOC_TYPE_VMALLOC_NON_ATOMIC)) { ++ WARNING("invalid type: %d", info->type); ++ return; ++ } ++#endif ++ vfree(info); ++} ++ ++void *wrap_alloc_pages(gfp_t flags, unsigned int size, ++ const char *file, int line) ++{ ++ struct alloc_info *info; ++ ++ size += sizeof(*info); ++ info = (struct alloc_info *)__get_free_pages(flags, get_order(size)); ++ if (!info) ++ return NULL; ++ info->type = ALLOC_TYPE_PAGES; ++ info->size = size; ++ atomic_add(size, &alloc_sizes[info->type]); ++#if ALLOC_DEBUG > 1 ++ info->file = file; ++ info->line = line; ++ info->tag = 0; ++ spin_lock_bh(&alloc_lock); ++ InsertTailList(&allocs, &info->list); ++ spin_unlock_bh(&alloc_lock); ++#endif ++ return info + 1; ++} ++ ++void wrap_free_pages(unsigned long ptr, int order) ++{ ++ struct alloc_info *info; ++ ++ info = (void *)ptr - sizeof(*info); ++ atomic_sub(info->size, &alloc_sizes[info->type]); ++#if ALLOC_DEBUG > 1 ++ spin_lock_bh(&alloc_lock); ++ RemoveEntryList(&info->list); ++ spin_unlock_bh(&alloc_lock); ++ if (info->type != ALLOC_TYPE_PAGES) { ++ WARNING("invalid type: %d", info->type); ++ return; ++ } ++#endif ++ free_pages((unsigned long)info, get_order(info->size)); ++} ++ ++#if ALLOC_DEBUG > 1 ++void *wrap_ExAllocatePoolWithTag(enum pool_type pool_type, SIZE_T size, ++ ULONG tag, const char *file, int line) ++{ ++ void *addr; ++ struct alloc_info *info; ++ ++ ENTER4("pool_type: %d, size: %zu, tag: %u", pool_type, size, tag); ++ addr = (ExAllocatePoolWithTag)(pool_type, size, tag); ++ if (!addr) ++ return NULL; ++ info = addr - sizeof(*info); ++ info->file = file; ++ info->line = line; ++ info->tag = tag; ++ EXIT4(return addr); ++} ++#endif ++ ++int alloc_size(enum alloc_type type) ++{ ++ if ((int)type >= 0 && type < ALLOC_TYPE_MAX) ++ return atomic_read(&alloc_sizes[type]); ++ else ++ return -EINVAL; ++} ++ ++#endif // ALLOC_DEBUG ++ ++int wrapmem_init(void) ++{ ++#if ALLOC_DEBUG > 1 ++ InitializeListHead(&allocs); ++#endif ++ InitializeListHead(&slack_allocs); ++ spin_lock_init(&alloc_lock); ++ return 0; ++} ++ ++void wrapmem_exit(void) ++{ ++#if ALLOC_DEBUG ++ enum alloc_type type; ++#endif ++ struct nt_list *ent; ++ ++ /* free all pointers on the slack list */ ++ while (1) { ++ struct slack_alloc_info *info; ++ spin_lock_bh(&alloc_lock); ++ ent = RemoveHeadList(&slack_allocs); ++ spin_unlock_bh(&alloc_lock); ++ if (!ent) ++ break; ++ info = container_of(ent, struct slack_alloc_info, list); ++#if ALLOC_DEBUG ++ atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]); ++#endif ++ kfree(info); ++ } ++#if ALLOC_DEBUG ++ for (type = 0; type < ALLOC_TYPE_MAX; type++) { ++ int n = atomic_read(&alloc_sizes[type]); ++ if (n) ++ WARNING("%d bytes of memory in %s leaking", n, ++ alloc_type_name[type]); ++ } ++ ++#if ALLOC_DEBUG > 1 ++ while (1) { ++ struct alloc_info *info; ++ ++ spin_lock_bh(&alloc_lock); ++ ent = RemoveHeadList(&allocs); ++ spin_unlock_bh(&alloc_lock); ++ if (!ent) ++ break; ++ info = container_of(ent, struct alloc_info, list); ++ atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]); ++ printk(KERN_DEBUG DRIVER_NAME ++ ": %s:%d leaked %zd bytes at %p (%s, tag 0x%08X)\n", ++ info->file, info->line, info->size, info + 1, ++ alloc_type_name[info->type], info->tag); ++ if (info->type == ALLOC_TYPE_KMALLOC_ATOMIC || ++ info->type == ALLOC_TYPE_KMALLOC_NON_ATOMIC) ++ kfree(info); ++ else if (info->type == ALLOC_TYPE_VMALLOC_ATOMIC || ++ info->type == ALLOC_TYPE_VMALLOC_NON_ATOMIC) ++ vfree(info); ++ else if (info->type == ALLOC_TYPE_PAGES) ++ free_pages((unsigned long)info, get_order(info->size)); ++ else ++ WARNING("invalid type: %d; not freed", info->type); ++ } ++#endif ++#endif ++ return; ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.h +--- linux-3.12.2/3rdparty/ndiswrapper/wrapmem.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,97 @@ ++/* ++ * Copyright (C) 2006 Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _WRAPMEM_H_ ++#define _WRAPMEM_H_ ++ ++/* ++ * Set ALLOC_DEBUG to 1 to show information about memory used by both ++ * ndiswrapper and Windows driver in /proc/net/ndiswrapper/debug ++ * This will also show memory leaks (memory allocated but not freed) when ++ * ndiswrapper module is unloaded. ++ * ++ * Set ALLOC_DEBUG to 2 to see details about every leaking allocation. ++*/ ++ ++#ifndef ALLOC_DEBUG ++#define ALLOC_DEBUG 0 ++#endif ++ ++int wrapmem_init(void); ++void wrapmem_exit(void); ++void *slack_kmalloc(size_t size); ++void *slack_kzalloc(size_t size); ++void slack_kfree(void *ptr); ++ ++#if ALLOC_DEBUG ++enum alloc_type { ALLOC_TYPE_KMALLOC_ATOMIC, ++ ALLOC_TYPE_KMALLOC_NON_ATOMIC, ++ ALLOC_TYPE_VMALLOC_ATOMIC, ALLOC_TYPE_VMALLOC_NON_ATOMIC, ++ ALLOC_TYPE_SLACK, ALLOC_TYPE_PAGES, ALLOC_TYPE_MAX }; ++ ++extern const char *alloc_type_name[ALLOC_TYPE_MAX]; ++ ++void *wrap_kmalloc(size_t size, gfp_t flags, const char *file, int line); ++void *wrap_kzalloc(size_t size, gfp_t flags, const char *file, int line); ++void wrap_kfree(void *ptr); ++void *wrap_vmalloc(unsigned long size, const char *file, int line); ++void *wrap__vmalloc(unsigned long size, gfp_t flags, pgprot_t prot, ++ const char *file, int line); ++void wrap_vfree(void *ptr); ++void *wrap_alloc_pages(gfp_t flags, unsigned int size, ++ const char *file, int line); ++void wrap_free_pages(unsigned long ptr, int order); ++int alloc_size(enum alloc_type type); ++ ++#if ALLOC_DEBUG > 1 ++void *wrap_ExAllocatePoolWithTag(enum pool_type pool_type, SIZE_T size, ++ ULONG tag, const char *file, int line); ++#define ExAllocatePoolWithTag(pool_type, size, tag) \ ++ wrap_ExAllocatePoolWithTag(pool_type, size, tag, __FILE__, __LINE__) ++#endif ++ ++#ifndef _WRAPMEM_C_ ++#undef kmalloc ++#undef kzalloc ++#undef kfree ++#undef vmalloc ++#undef __vmalloc ++#undef vfree ++#define kmalloc(size, flags) \ ++ wrap_kmalloc(size, flags, __FILE__, __LINE__) ++#define kzalloc(size, flags) \ ++ wrap_kzalloc(size, flags, __FILE__, __LINE__) ++#define vmalloc(size) \ ++ wrap_vmalloc(size, __FILE__, __LINE__) ++#define __vmalloc(size, flags, prot) \ ++ wrap__vmalloc(size, flags, prot, __FILE__, __LINE__) ++#define kfree(ptr) wrap_kfree(ptr) ++#define vfree(ptr) wrap_vfree(ptr) ++ ++#define wrap_get_free_pages(flags, size) \ ++ wrap_alloc_pages(flags, size, __FILE__, __LINE__) ++#undef free_pages ++#define free_pages(ptr, order) wrap_free_pages(ptr, order) ++ ++#endif // _WRAPMEM_C_ ++ ++#else ++ ++#define wrap_get_free_pages(flags, size) \ ++ (void *)__get_free_pages(flags, get_order(size)) ++ ++#endif // ALLOC_DEBUG ++ ++#endif +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.c +--- linux-3.12.2/3rdparty/ndiswrapper/wrapndis.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,2186 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "ndis.h" ++#include "iw_ndis.h" ++#include "pnp.h" ++#include "loader.h" ++#include "wrapndis.h" ++#include "wrapper.h" ++ ++/* Functions callable from the NDIS driver */ ++wstdcall NTSTATUS NdisDispatchDeviceControl(struct device_object *fdo, ++ struct irp *irp); ++wstdcall NTSTATUS NdisDispatchPnp(struct device_object *fdo, struct irp *irp); ++wstdcall NTSTATUS NdisDispatchPower(struct device_object *fdo, struct irp *irp); ++ ++struct workqueue_struct *wrapndis_wq; ++ ++static int set_packet_filter(struct ndis_device *wnd, ++ ULONG packet_filter); ++static void add_iw_stats_timer(struct ndis_device *wnd); ++static void del_iw_stats_timer(struct ndis_device *wnd); ++static NDIS_STATUS ndis_start_device(struct ndis_device *wnd); ++static int ndis_remove_device(struct ndis_device *wnd); ++static void set_multicast_list(struct ndis_device *wnd); ++static int ndis_net_dev_open(struct net_device *net_dev); ++static int ndis_net_dev_close(struct net_device *net_dev); ++ ++/* MiniportReset */ ++NDIS_STATUS mp_reset(struct ndis_device *wnd) ++{ ++ NDIS_STATUS res; ++ struct miniport *mp; ++ BOOLEAN reset_address; ++ KIRQL irql; ++ ++ ENTER2("wnd: %p", wnd); ++ mutex_lock(&wnd->tx_ring_mutex); ++ mutex_lock(&wnd->ndis_req_mutex); ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ prepare_wait_condition(wnd->ndis_req_task, wnd->ndis_req_done, 0); ++ WARNING("%s is being reset", wnd->net_dev->name); ++ irql = serialize_lock_irql(wnd); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ res = LIN2WIN2(mp->reset, &reset_address, wnd->nmb->mp_ctx); ++ serialize_unlock_irql(wnd, irql); ++ ++ TRACE2("%08X, %08X", res, reset_address); ++ if (res == NDIS_STATUS_PENDING) { ++ /* wait for NdisMResetComplete */ ++ if (wait_condition((wnd->ndis_req_done > 0), 0, ++ TASK_INTERRUPTIBLE) < 0) ++ res = NDIS_STATUS_FAILURE; ++ else { ++ res = wnd->ndis_req_status; ++ reset_address = wnd->ndis_req_done - 1; ++ } ++ TRACE2("%08X, %08X", res, reset_address); ++ } ++ mutex_unlock(&wnd->ndis_req_mutex); ++ if (res == NDIS_STATUS_SUCCESS && reset_address) { ++ set_packet_filter(wnd, wnd->packet_filter); ++ set_multicast_list(wnd); ++ } ++ mutex_unlock(&wnd->tx_ring_mutex); ++ EXIT3(return res); ++} ++ ++/* MiniportRequest(Query/Set)Information */ ++NDIS_STATUS mp_request(enum ndis_request_type request, ++ struct ndis_device *wnd, ndis_oid oid, ++ void *buf, ULONG buflen, ULONG *written, ULONG *needed) ++{ ++ NDIS_STATUS res; ++ ULONG w, n; ++ struct miniport *mp; ++ KIRQL irql; ++ ++ mutex_lock(&wnd->ndis_req_mutex); ++ if (!written) ++ written = &w; ++ if (!needed) ++ needed = &n; ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ prepare_wait_condition(wnd->ndis_req_task, wnd->ndis_req_done, 0); ++ irql = serialize_lock_irql(wnd); ++ assert_irql(_irql_ == DISPATCH_LEVEL); ++ switch (request) { ++ case NdisRequestQueryInformation: ++ TRACE2("%p, %08X, %p", mp->queryinfo, oid, wnd->nmb->mp_ctx); ++ res = LIN2WIN6(mp->queryinfo, wnd->nmb->mp_ctx, oid, buf, ++ buflen, written, needed); ++ break; ++ case NdisRequestSetInformation: ++ TRACE2("%p, %08X, %p", mp->setinfo, oid, wnd->nmb->mp_ctx); ++ res = LIN2WIN6(mp->setinfo, wnd->nmb->mp_ctx, oid, buf, ++ buflen, written, needed); ++ break; ++ default: ++ WARNING("invalid request %d, %08X", request, oid); ++ res = NDIS_STATUS_NOT_SUPPORTED; ++ break; ++ } ++ serialize_unlock_irql(wnd, irql); ++ TRACE2("%08X, %08X", res, oid); ++ if (res == NDIS_STATUS_PENDING) { ++ /* wait for NdisMQueryInformationComplete */ ++ if (wait_condition((wnd->ndis_req_done > 0), 0, ++ TASK_INTERRUPTIBLE) < 0) ++ res = NDIS_STATUS_FAILURE; ++ else ++ res = wnd->ndis_req_status; ++ TRACE2("%08X, %08X", res, oid); ++ } ++ mutex_unlock(&wnd->ndis_req_mutex); ++ DBG_BLOCK(2) { ++ if (res || needed) ++ TRACE2("%08X, %d, %d, %d", res, buflen, *written, ++ *needed); ++ } ++ EXIT3(return res); ++} ++ ++/* MiniportPnPEventNotify */ ++static NDIS_STATUS mp_pnp_event(struct ndis_device *wnd, ++ enum ndis_device_pnp_event event, ++ ULONG power_profile) ++{ ++ struct miniport *mp; ++ ++ ENTER1("%p, %d", wnd, event); ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ if (!mp->pnp_event_notify) { ++ TRACE1("Windows driver %s doesn't support " ++ "MiniportPnpEventNotify", wnd->wd->driver->name); ++ return NDIS_STATUS_FAILURE; ++ } ++ /* RNDIS driver doesn't like to be notified if device is ++ * already halted */ ++ if (!test_bit(HW_INITIALIZED, &wnd->wd->hw_status)) ++ EXIT1(return NDIS_STATUS_SUCCESS); ++ switch (event) { ++ case NdisDevicePnPEventSurpriseRemoved: ++ TRACE1("%u, %p", ++ (wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK), ++ mp->pnp_event_notify); ++ if ((wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK) && ++ !test_bit(HW_DISABLED, &wnd->wd->hw_status) && ++ mp->pnp_event_notify) { ++ TRACE1("calling surprise_removed"); ++ LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx, ++ NdisDevicePnPEventSurpriseRemoved, NULL, 0); ++ } else ++ TRACE1("Windows driver %s doesn't support " ++ "MiniportPnpEventNotify for safe unplugging", ++ wnd->wd->driver->name); ++ return NDIS_STATUS_SUCCESS; ++ case NdisDevicePnPEventPowerProfileChanged: ++ if (power_profile) ++ power_profile = NdisPowerProfileAcOnLine; ++ LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx, ++ NdisDevicePnPEventPowerProfileChanged, ++ &power_profile, sizeof(power_profile)); ++ return NDIS_STATUS_SUCCESS; ++ default: ++ WARNING("event %d not yet implemented", event); ++ return NDIS_STATUS_SUCCESS; ++ } ++} ++ ++/* MiniportInitialize */ ++static NDIS_STATUS mp_init(struct ndis_device *wnd) ++{ ++ NDIS_STATUS error_status, status; ++ UINT medium_index; ++ enum ndis_medium medium_array[] = {NdisMedium802_3}; ++ struct miniport *mp; ++ ++ ENTER1("irql: %d", current_irql()); ++ if (test_bit(HW_INITIALIZED, &wnd->wd->hw_status)) { ++ WARNING("device %p already initialized!", wnd); ++ return NDIS_STATUS_FAILURE; ++ } ++ ++ if (!wnd->wd->driver->ndis_driver || ++ !wnd->wd->driver->ndis_driver->mp.init) { ++ WARNING("assuming WDM (non-NDIS) driver"); ++ EXIT1(return NDIS_STATUS_NOT_RECOGNIZED); ++ } ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ status = LIN2WIN6(mp->init, &error_status, &medium_index, medium_array, ++ ARRAY_SIZE(medium_array), wnd->nmb, wnd->nmb); ++ TRACE1("init returns: %08X, irql: %d", status, current_irql()); ++ if (status != NDIS_STATUS_SUCCESS) { ++ WARNING("couldn't initialize device: %08X", status); ++ EXIT1(return NDIS_STATUS_FAILURE); ++ } ++ ++ /* Wait a little to let card power up otherwise ifup might ++ * fail after boot */ ++ sleep_hz(HZ / 5); ++ status = mp_pnp_event(wnd, NdisDevicePnPEventPowerProfileChanged, ++ NdisPowerProfileAcOnLine); ++ if (status != NDIS_STATUS_SUCCESS) ++ TRACE1("setting power failed: %08X", status); ++ set_bit(HW_INITIALIZED, &wnd->wd->hw_status); ++ /* the description about NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND is ++ * misleading/confusing */ ++ status = mp_query(wnd, OID_PNP_CAPABILITIES, ++ &wnd->pnp_capa, sizeof(wnd->pnp_capa)); ++ if (status == NDIS_STATUS_SUCCESS) { ++ TRACE1("%d, %d", wnd->pnp_capa.wakeup.min_magic_packet_wakeup, ++ wnd->pnp_capa.wakeup.min_pattern_wakeup); ++ wnd->attributes |= NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; ++ status = mp_query_int(wnd, OID_PNP_ENABLE_WAKE_UP, ++ &wnd->ndis_wolopts); ++ TRACE1("%08X, %x", status, wnd->ndis_wolopts); ++ } else if (status == NDIS_STATUS_NOT_SUPPORTED) ++ wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; ++ TRACE1("%d", wnd->pnp_capa.wakeup.min_magic_packet_wakeup); ++ /* although some NDIS drivers support suspend, Linux kernel ++ * has issues with suspending USB devices */ ++ if (wrap_is_usb_bus(wnd->wd->dev_bus)) { ++ wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; ++ wnd->ndis_wolopts = 0; ++ } ++ mp_set_int(wnd, OID_802_11_POWER_MODE, NDIS_POWER_OFF); ++ EXIT1(return NDIS_STATUS_SUCCESS); ++} ++ ++/* MiniportHalt */ ++static void mp_halt(struct ndis_device *wnd) ++{ ++ struct miniport *mp; ++ ++ ENTER1("%p", wnd); ++ if (!test_and_clear_bit(HW_INITIALIZED, &wnd->wd->hw_status)) { ++ WARNING("device %p is not initialized - not halting", wnd); ++ return; ++ } ++ hangcheck_del(wnd); ++ del_iw_stats_timer(wnd); ++#ifdef CONFIG_WIRELESS_EXT ++ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan && ++ wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ mutex_unlock(&wnd->ndis_req_mutex); ++ disassociate(wnd, 0); ++ mutex_lock(&wnd->ndis_req_mutex); ++ } ++#endif ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ TRACE1("halt: %p", mp->mp_halt); ++ LIN2WIN1(mp->mp_halt, wnd->nmb->mp_ctx); ++ /* if a driver doesn't call NdisMDeregisterInterrupt during ++ * halt, deregister it now */ ++ if (wnd->mp_interrupt) ++ NdisMDeregisterInterrupt(wnd->mp_interrupt); ++ /* cancel any timers left by buggy windows driver; also free ++ * the memory for timers */ ++ while (1) { ++ struct nt_slist *slist; ++ struct wrap_timer *wrap_timer; ++ ++ spin_lock_bh(&ntoskernel_lock); ++ if ((slist = wnd->wrap_timer_slist.next)) ++ wnd->wrap_timer_slist.next = slist->next; ++ spin_unlock_bh(&ntoskernel_lock); ++ TIMERTRACE("%p", slist); ++ if (!slist) ++ break; ++ wrap_timer = container_of(slist, struct wrap_timer, slist); ++ wrap_timer->repeat = 0; ++ /* ktimer that this wrap_timer is associated to can't ++ * be touched, as it may have been freed by the driver ++ * already */ ++ if (del_timer_sync(&wrap_timer->timer)) ++ WARNING("Buggy Windows driver left timer %p " ++ "running", wrap_timer->nt_timer); ++ memset(wrap_timer, 0, sizeof(*wrap_timer)); ++ kfree(wrap_timer); ++ } ++ EXIT1(return); ++} ++ ++static NDIS_STATUS mp_set_power_state(struct ndis_device *wnd, ++ enum ndis_power_state state) ++{ ++ NDIS_STATUS status; ++ ++ TRACE1("%d", state); ++ if (state == NdisDeviceStateD0) { ++ status = NDIS_STATUS_SUCCESS; ++ mutex_unlock(&wnd->ndis_req_mutex); ++ if (test_and_clear_bit(HW_HALTED, &wnd->wd->hw_status)) { ++ status = mp_init(wnd); ++ if (status == NDIS_STATUS_SUCCESS) { ++ set_packet_filter(wnd, wnd->packet_filter); ++ set_multicast_list(wnd); ++ } ++ } else if (test_and_clear_bit(HW_SUSPENDED, ++ &wnd->wd->hw_status)) { ++ status = mp_set_int(wnd, OID_PNP_SET_POWER, state); ++ if (status != NDIS_STATUS_SUCCESS) ++ WARNING("%s: setting power to state %d failed? " ++ "%08X", wnd->net_dev->name, state, ++ status); ++ } else ++ return NDIS_STATUS_FAILURE; ++ ++ if (wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ pci_enable_wake(wnd->wd->pci.pdev, PCI_D3hot, 0); ++ pci_enable_wake(wnd->wd->pci.pdev, PCI_D3cold, 0); ++ } ++ if (status == NDIS_STATUS_SUCCESS) { ++ mutex_unlock(&wnd->tx_ring_mutex); ++ netif_device_attach(wnd->net_dev); ++ hangcheck_add(wnd); ++ add_iw_stats_timer(wnd); ++ } else ++ WARNING("%s: couldn't set power to state %d; device not" ++ " resumed", wnd->net_dev->name, state); ++ EXIT1(return status); ++ } else { ++ mutex_lock(&wnd->tx_ring_mutex); ++ netif_device_detach(wnd->net_dev); ++ hangcheck_del(wnd); ++ del_iw_stats_timer(wnd); ++ status = NDIS_STATUS_NOT_SUPPORTED; ++ if (wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND) { ++ status = mp_set_int(wnd, OID_PNP_ENABLE_WAKE_UP, ++ wnd->ndis_wolopts); ++ TRACE2("0x%x, 0x%x", status, wnd->ndis_wolopts); ++ if (status == NDIS_STATUS_SUCCESS && ++ wrap_is_pci_bus(wnd->wd->dev_bus)) { ++ if (wnd->ndis_wolopts) ++ wnd->wd->pci.wake_state = ++ PowerDeviceD3; ++ else ++ wnd->wd->pci.wake_state = ++ PowerDeviceUnspecified; ++ } else ++ WARNING("couldn't set wake-on-lan options: " ++ "0x%x, %08X", wnd->ndis_wolopts, status); ++ status = mp_set_int(wnd, OID_PNP_SET_POWER, state); ++ if (status == NDIS_STATUS_SUCCESS) ++ set_bit(HW_SUSPENDED, &wnd->wd->hw_status); ++ else ++ WARNING("suspend failed: %08X", status); ++ } ++ if (status != NDIS_STATUS_SUCCESS) { ++ WARNING("%s does not support power management; " ++ "halting the device", wnd->net_dev->name); ++ mp_halt(wnd); ++ set_bit(HW_HALTED, &wnd->wd->hw_status); ++ status = STATUS_SUCCESS; ++ } ++ mutex_lock(&wnd->ndis_req_mutex); ++ EXIT1(return status); ++ } ++} ++ ++static int ndis_set_mac_address(struct net_device *dev, void *p) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ struct sockaddr *addr = p; ++ struct ndis_configuration_parameter param; ++ struct unicode_string key; ++ struct ansi_string ansi; ++ NDIS_STATUS res; ++ unsigned char mac_string[2 * ETH_ALEN + 1]; ++ mac_address mac; ++ ++ memcpy(mac, addr->sa_data, sizeof(mac)); ++ memset(mac_string, 0, sizeof(mac_string)); ++ res = snprintf(mac_string, sizeof(mac_string), MACSTR, MAC2STR(mac)); ++ if (res != (sizeof(mac_string) - 1)) ++ EXIT1(return -EINVAL); ++ TRACE1("new mac: %s", mac_string); ++ ++ RtlInitAnsiString(&ansi, mac_string); ++ if (RtlAnsiStringToUnicodeString(¶m.data.string, &ansi, ++ TRUE) != STATUS_SUCCESS) ++ EXIT1(return -EINVAL); ++ ++ param.type = NdisParameterString; ++ RtlInitAnsiString(&ansi, "NetworkAddress"); ++ if (RtlAnsiStringToUnicodeString(&key, &ansi, TRUE) != STATUS_SUCCESS) { ++ RtlFreeUnicodeString(¶m.data.string); ++ EXIT1(return -EINVAL); ++ } ++ NdisWriteConfiguration(&res, wnd->nmb, &key, ¶m); ++ RtlFreeUnicodeString(&key); ++ RtlFreeUnicodeString(¶m.data.string); ++ ++ if (res != NDIS_STATUS_SUCCESS) ++ EXIT1(return -EFAULT); ++ if (ndis_reinit(wnd) == NDIS_STATUS_SUCCESS) { ++ res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, ++ mac, sizeof(mac)); ++ if (res == NDIS_STATUS_SUCCESS) { ++ TRACE1("mac:" MACSTRSEP, MAC2STR(mac)); ++ memcpy(dev->dev_addr, mac, sizeof(mac)); ++ } else ++ ERROR("couldn't get mac address: %08X", res); ++ } ++ EXIT1(return 0); ++} ++ ++static int setup_tx_sg_list(struct ndis_device *wnd, struct sk_buff *skb, ++ struct ndis_packet_oob_data *oob_data) ++{ ++ struct ndis_sg_element *sg_element; ++ struct ndis_sg_list *sg_list; ++ int i; ++ ++ ENTER3("%p, %d", skb, skb_shinfo(skb)->nr_frags); ++ if (skb_shinfo(skb)->nr_frags <= 1) { ++ sg_element = &oob_data->wrap_tx_sg_list.elements[0]; ++ sg_element->address = ++ PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, skb->data, ++ skb->len, PCI_DMA_TODEVICE); ++ sg_element->length = skb->len; ++ oob_data->wrap_tx_sg_list.nent = 1; ++ oob_data->ext.info[ScatterGatherListPacketInfo] = ++ &oob_data->wrap_tx_sg_list; ++ TRACE3("%llx, %u", sg_element->address, sg_element->length); ++ return 0; ++ } ++ sg_list = kmalloc(sizeof(*sg_list) + ++ (skb_shinfo(skb)->nr_frags + 1) * sizeof(*sg_element), ++ GFP_ATOMIC); ++ if (!sg_list) ++ return -ENOMEM; ++ sg_list->nent = skb_shinfo(skb)->nr_frags + 1; ++ TRACE3("%p, %d", sg_list, sg_list->nent); ++ sg_element = sg_list->elements; ++ sg_element->length = skb_headlen(skb); ++ sg_element->address = ++ PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, skb->data, ++ skb_headlen(skb), PCI_DMA_TODEVICE); ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ sg_element++; ++ sg_element->length = frag->size; ++ sg_element->address = ++ pci_map_page(wnd->wd->pci.pdev, skb_frag_page(frag), ++ frag->page_offset, frag->size, ++ PCI_DMA_TODEVICE); ++ TRACE3("%llx, %u", sg_element->address, sg_element->length); ++ } ++ oob_data->ext.info[ScatterGatherListPacketInfo] = sg_list; ++ return 0; ++} ++ ++static void free_tx_sg_list(struct ndis_device *wnd, ++ struct ndis_packet_oob_data *oob_data) ++{ ++ int i; ++ struct ndis_sg_element *sg_element; ++ struct ndis_sg_list *sg_list = ++ oob_data->ext.info[ScatterGatherListPacketInfo]; ++ sg_element = sg_list->elements; ++ TRACE3("%p, %d", sg_list, sg_list->nent); ++ PCI_DMA_UNMAP_SINGLE(wnd->wd->pci.pdev, sg_element->address, ++ sg_element->length, PCI_DMA_TODEVICE); ++ if (sg_list->nent == 1) ++ EXIT3(return); ++ for (i = 1; i < sg_list->nent; i++, sg_element++) { ++ TRACE3("%llx, %u", sg_element->address, sg_element->length); ++ pci_unmap_page(wnd->wd->pci.pdev, sg_element->address, ++ sg_element->length, PCI_DMA_TODEVICE); ++ } ++ TRACE3("%p", sg_list); ++ kfree(sg_list); ++} ++ ++static struct ndis_packet *alloc_tx_packet(struct ndis_device *wnd, ++ struct sk_buff *skb) ++{ ++ struct ndis_packet *packet; ++ ndis_buffer *buffer; ++ struct ndis_packet_oob_data *oob_data; ++ NDIS_STATUS status; ++ ++ NdisAllocatePacket(&status, &packet, wnd->tx_packet_pool); ++ if (status != NDIS_STATUS_SUCCESS) ++ return NULL; ++ NdisAllocateBuffer(&status, &buffer, wnd->tx_buffer_pool, ++ skb->data, skb->len); ++ if (status != NDIS_STATUS_SUCCESS) { ++ NdisFreePacket(packet); ++ return NULL; ++ } ++ packet->private.buffer_head = buffer; ++ packet->private.buffer_tail = buffer; ++ ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ oob_data->tx_skb = skb; ++ if (wnd->sg_dma_size) { ++ if (setup_tx_sg_list(wnd, skb, oob_data)) { ++ NdisFreeBuffer(buffer); ++ NdisFreePacket(packet); ++ return NULL; ++ } ++ } ++ if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ struct ndis_tcp_ip_checksum_packet_info csum; ++ int protocol; ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21) ++ protocol = ntohs(skb->protocol); ++#else ++ protocol = skb->nh.iph->protocol; ++#endif ++ csum.value = 0; ++ csum.tx.v4 = 1; ++ if (protocol == IPPROTO_TCP) ++ csum.tx.tcp = 1; ++ else if (protocol == IPPROTO_UDP) ++ csum.tx.udp = 1; ++// csum->tx.ip = 1; ++ packet->private.flags |= NDIS_PROTOCOL_ID_TCP_IP; ++ oob_data->ext.info[TcpIpChecksumPacketInfo] = ++ (void *)(ULONG_PTR)csum.value; ++ } ++ DBG_BLOCK(4) { ++ dump_bytes(__func__, skb->data, skb->len); ++ } ++ TRACE4("%p, %p, %p", packet, buffer, skb); ++ return packet; ++} ++ ++void free_tx_packet(struct ndis_device *wnd, struct ndis_packet *packet, ++ NDIS_STATUS status) ++{ ++ ndis_buffer *buffer; ++ struct ndis_packet_oob_data *oob_data; ++ struct sk_buff *skb; ++ struct ndis_packet_pool *pool; ++ ++ assert_irql(_irql_ <= DISPATCH_LEVEL); ++ assert(packet->private.packet_flags); ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ skb = oob_data->tx_skb; ++ buffer = packet->private.buffer_head; ++ TRACE4("%p, %p, %p, %08X", packet, buffer, skb, status); ++ if (status == NDIS_STATUS_SUCCESS) { ++ pre_atomic_add(wnd->net_stats.tx_bytes, packet->private.len); ++ atomic_inc_var(wnd->net_stats.tx_packets); ++ } else { ++ TRACE1("packet dropped: %08X", status); ++ atomic_inc_var(wnd->net_stats.tx_dropped); ++ } ++ if (wnd->sg_dma_size) ++ free_tx_sg_list(wnd, oob_data); ++ NdisFreeBuffer(buffer); ++ dev_kfree_skb_any(skb); ++ pool = packet->private.pool; ++ NdisFreePacket(packet); ++ if (netif_queue_stopped(wnd->net_dev) && ++ ((pool->max_descr - pool->num_used_descr) >= ++ (wnd->max_tx_packets / 4))) { ++ set_bit(NETIF_WAKEQ, &wnd->ndis_pending_work); ++ queue_work(wrapndis_wq, &wnd->ndis_work); ++ } ++ EXIT4(return); ++} ++ ++/* MiniportSend and MiniportSendPackets */ ++/* this function is called holding tx_ring_mutex. start and n are such ++ * that start + n < TX_RING_SIZE; i.e., packets don't wrap around ++ * ring */ ++static u8 mp_tx_packets(struct ndis_device *wnd, u8 start, u8 n) ++{ ++ NDIS_STATUS res; ++ struct miniport *mp; ++ struct ndis_packet *packet; ++ u8 sent; ++ KIRQL irql; ++ ++ ENTER3("%d, %d", start, n); ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ if (mp->send_packets) { ++ if (deserialized_driver(wnd)) { ++ LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx, ++ &wnd->tx_ring[start], n); ++ sent = n; ++ } else { ++ irql = serialize_lock_irql(wnd); ++ LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx, ++ &wnd->tx_ring[start], n); ++ serialize_unlock_irql(wnd, irql); ++ for (sent = 0; sent < n && wnd->tx_ok; sent++) { ++ struct ndis_packet_oob_data *oob_data; ++ packet = wnd->tx_ring[start + sent]; ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ switch ((res = ++ xchg(&oob_data->status, ++ NDIS_STATUS_NOT_RECOGNIZED))) { ++ case NDIS_STATUS_SUCCESS: ++ free_tx_packet(wnd, packet, ++ NDIS_STATUS_SUCCESS); ++ break; ++ case NDIS_STATUS_PENDING: ++ break; ++ case NDIS_STATUS_RESOURCES: ++ wnd->tx_ok = 0; ++ /* resubmit this packet and ++ * the rest when resources ++ * become available */ ++ sent--; ++ break; ++ case NDIS_STATUS_FAILURE: ++ free_tx_packet(wnd, packet, ++ NDIS_STATUS_FAILURE); ++ break; ++ default: ++ ERROR("%p: invalid status: %08X", ++ packet, res); ++ free_tx_packet(wnd, packet, ++ oob_data->status); ++ break; ++ } ++ TRACE3("%p, %d", packet, res); ++ } ++ } ++ TRACE3("sent: %d(%d)", sent, n); ++ } else { ++ for (sent = 0; sent < n && wnd->tx_ok; sent++) { ++ struct ndis_packet_oob_data *oob_data; ++ packet = wnd->tx_ring[start + sent]; ++ oob_data = NDIS_PACKET_OOB_DATA(packet); ++ oob_data->status = NDIS_STATUS_NOT_RECOGNIZED; ++ irql = serialize_lock_irql(wnd); ++ res = LIN2WIN3(mp->send, wnd->nmb->mp_ctx, ++ packet, packet->private.flags); ++ serialize_unlock_irql(wnd, irql); ++ switch (res) { ++ case NDIS_STATUS_SUCCESS: ++ free_tx_packet(wnd, packet, res); ++ break; ++ case NDIS_STATUS_PENDING: ++ break; ++ case NDIS_STATUS_RESOURCES: ++ wnd->tx_ok = 0; ++ /* resend this packet when resources ++ * become available */ ++ sent--; ++ break; ++ case NDIS_STATUS_FAILURE: ++ free_tx_packet(wnd, packet, res); ++ break; ++ default: ++ ERROR("packet %p: invalid status: %08X", ++ packet, res); ++ break; ++ } ++ } ++ } ++ EXIT3(return sent); ++} ++ ++static void tx_worker(struct work_struct *work) ++{ ++ struct ndis_device *wnd; ++ s8 n; ++ ++ wnd = container_of(work, struct ndis_device, tx_work); ++ ENTER3("tx_ok %d", wnd->tx_ok); ++ while (wnd->tx_ok) { ++ mutex_lock(&wnd->tx_ring_mutex); ++ spin_lock_bh(&wnd->tx_ring_lock); ++ n = wnd->tx_ring_end - wnd->tx_ring_start; ++ TRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n); ++ /* end == start if either ring is empty or full; in ++ * the latter case is_tx_ring_full is set */ ++ if (n == 0) { ++ if (wnd->is_tx_ring_full) ++ n = TX_RING_SIZE - wnd->tx_ring_start; ++ else { ++ spin_unlock_bh(&wnd->tx_ring_lock); ++ mutex_unlock(&wnd->tx_ring_mutex); ++ break; ++ } ++ } else if (n < 0) ++ n = TX_RING_SIZE - wnd->tx_ring_start; ++ spin_unlock_bh(&wnd->tx_ring_lock); ++ if (unlikely(n > wnd->max_tx_packets)) ++ n = wnd->max_tx_packets; ++ n = mp_tx_packets(wnd, wnd->tx_ring_start, n); ++ if (n) { ++ wnd->net_dev->trans_start = jiffies; ++ wnd->tx_ring_start = ++ (wnd->tx_ring_start + n) % TX_RING_SIZE; ++ wnd->is_tx_ring_full = 0; ++ } ++ mutex_unlock(&wnd->tx_ring_mutex); ++ TRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n); ++ } ++ EXIT3(return); ++} ++ ++static int tx_skbuff(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ struct ndis_packet *packet; ++ ++ packet = alloc_tx_packet(wnd, skb); ++ if (!packet) { ++ TRACE2("couldn't allocate packet"); ++ netif_tx_lock(dev); ++ netif_stop_queue(dev); ++ netif_tx_unlock(dev); ++ return NETDEV_TX_BUSY; ++ } ++ spin_lock(&wnd->tx_ring_lock); ++ wnd->tx_ring[wnd->tx_ring_end++] = packet; ++ if (wnd->tx_ring_end == TX_RING_SIZE) ++ wnd->tx_ring_end = 0; ++ if (wnd->tx_ring_end == wnd->tx_ring_start) { ++ netif_tx_lock(dev); ++ wnd->is_tx_ring_full = 1; ++ netif_stop_queue(dev); ++ netif_tx_unlock(dev); ++ } ++ spin_unlock(&wnd->tx_ring_lock); ++ TRACE4("ring: %d, %d", wnd->tx_ring_start, wnd->tx_ring_end); ++ queue_work(wrapndis_wq, &wnd->tx_work); ++ return NETDEV_TX_OK; ++} ++ ++static int set_packet_filter(struct ndis_device *wnd, ULONG packet_filter) ++{ ++ NDIS_STATUS res; ++ ++ while (1) { ++ res = mp_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, ++ packet_filter); ++ if (res == NDIS_STATUS_SUCCESS) ++ break; ++ TRACE2("couldn't set filter 0x%08x", packet_filter); ++ /* NDIS_PACKET_TYPE_PROMISCUOUS may not work with 802.11 */ ++ if (packet_filter & NDIS_PACKET_TYPE_PROMISCUOUS) { ++ packet_filter &= ~NDIS_PACKET_TYPE_PROMISCUOUS; ++ continue; ++ } ++ if (packet_filter & NDIS_PACKET_TYPE_ALL_LOCAL) { ++ packet_filter &= ~NDIS_PACKET_TYPE_ALL_LOCAL; ++ continue; ++ } ++ if (packet_filter & NDIS_PACKET_TYPE_ALL_FUNCTIONAL) { ++ packet_filter &= ~NDIS_PACKET_TYPE_ALL_FUNCTIONAL; ++ continue; ++ } ++ if (packet_filter & NDIS_PACKET_TYPE_MULTICAST) { ++ packet_filter &= ~NDIS_PACKET_TYPE_MULTICAST; ++ packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; ++ continue; ++ } ++ if (packet_filter & NDIS_PACKET_TYPE_ALL_MULTICAST) { ++ packet_filter &= ~NDIS_PACKET_TYPE_ALL_MULTICAST; ++ continue; ++ } ++ break; ++ } ++ ++ wnd->packet_filter = packet_filter; ++ res = mp_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter); ++ if (packet_filter != wnd->packet_filter) { ++ WARNING("filter not set: 0x%08x, 0x%08x", ++ packet_filter, wnd->packet_filter); ++ wnd->packet_filter = packet_filter; ++ } ++ if (wnd->packet_filter) ++ EXIT3(return 0); ++ else ++ EXIT3(return -1); ++} ++ ++void set_media_state(struct ndis_device *wnd, enum ndis_media_state state) ++{ ++ struct net_device *net_dev = wnd->net_dev; ++ ++ ENTER2("state: 0x%x, carrier %d", state, netif_carrier_ok(net_dev)); ++ switch (state) { ++ case NdisMediaStateConnected: ++ if (netif_carrier_ok(net_dev)) ++ return; ++ netif_carrier_on(net_dev); ++ wnd->tx_ok = 1; ++ if (netif_queue_stopped(net_dev)) ++ netif_wake_queue(net_dev); ++ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { ++ set_bit(LINK_STATUS_ON, &wnd->ndis_pending_work); ++ queue_work(wrapndis_wq, &wnd->ndis_work); ++ } ++ break; ++ case NdisMediaStateDisconnected: ++ if (!netif_carrier_ok(net_dev)) ++ return; ++ netif_carrier_off(net_dev); ++ netif_stop_queue(net_dev); ++ wnd->tx_ok = 0; ++ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { ++ memset(&wnd->essid, 0, sizeof(wnd->essid)); ++ set_bit(LINK_STATUS_OFF, &wnd->ndis_pending_work); ++ queue_work(wrapndis_wq, &wnd->ndis_work); ++ } ++ break; ++ default: ++ WARNING("invalid media state: 0x%x", state); ++ break; ++ } ++} ++ ++static int ndis_net_dev_init(struct net_device *net_dev) ++{ ++ struct ndis_device *wnd = netdev_priv(net_dev); ++ ++ ENTER1("%p", wnd); ++ wrap_procfs_add_ndis_device(wnd); ++ EXIT1(return 0); ++} ++ ++static void ndis_net_dev_uninit(struct net_device *net_dev) ++{ ++ struct ndis_device *wnd = netdev_priv(net_dev); ++ ++ ENTER1("%p", wnd); ++ wrap_procfs_remove_ndis_device(wnd); ++ EXIT1(return); ++} ++ ++static int ndis_net_dev_open(struct net_device *net_dev) ++{ ++ int status, res; ++ struct ndis_device *wnd = netdev_priv(net_dev); ++ ++ ENTER1("%p", wnd); ++ res = mp_query_int(wnd, OID_GEN_MEDIA_CONNECT_STATUS, &status); ++ if (res == NDIS_STATUS_SUCCESS && status >= NdisMediaStateConnected && ++ status <= NdisMediaStateDisconnected) ++ set_media_state(wnd, status); ++ netif_start_queue(net_dev); ++ netif_poll_enable(net_dev); ++ EXIT1(return 0); ++} ++ ++static int ndis_net_dev_close(struct net_device *net_dev) ++{ ++ ENTER1("%p", netdev_priv(net_dev)); ++ netif_poll_disable(net_dev); ++ netif_tx_disable(net_dev); ++ EXIT1(return 0); ++} ++ ++static int ndis_change_mtu(struct net_device *net_dev, int mtu) ++{ ++ struct ndis_device *wnd = netdev_priv(net_dev); ++ int max; ++ ++ if (mtu < ETH_ZLEN) ++ return -EINVAL; ++ if (mp_query_int(wnd, OID_GEN_MAXIMUM_TOTAL_SIZE, &max) != ++ NDIS_STATUS_SUCCESS) ++ return -EOPNOTSUPP; ++ TRACE1("%d", max); ++ max -= ETH_HLEN; ++ if (max <= ETH_ZLEN) ++ return -EINVAL; ++ if (mtu + ETH_HLEN > max) ++ return -EINVAL; ++ net_dev->mtu = mtu; ++ return 0; ++} ++ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++static void ndis_poll_controller(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ disable_irq(dev->irq); ++ ndis_isr(wnd->mp_interrupt->kinterrupt, wnd->mp_interrupt); ++ enable_irq(dev->irq); ++} ++#endif ++ ++/* called from BH context */ ++static struct net_device_stats *ndis_get_stats(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ return &wnd->net_stats; ++} ++ ++/* called from BH context */ ++static void ndis_set_multicast_list(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ set_bit(SET_MULTICAST_LIST, &wnd->ndis_pending_work); ++ queue_work(wrapndis_wq, &wnd->ndis_work); ++} ++ ++/* called from BH context */ ++struct iw_statistics *get_iw_stats(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ return &wnd->iw_stats; ++} ++ ++static void update_iw_stats(struct ndis_device *wnd) ++{ ++ struct iw_statistics *iw_stats = &wnd->iw_stats; ++ struct ndis_wireless_stats ndis_stats; ++ NDIS_STATUS res; ++ ndis_rssi rssi; ++ int qual; ++ ++ ENTER2("%p", wnd); ++ if (wnd->iw_stats_enabled == FALSE || !netif_carrier_ok(wnd->net_dev)) { ++ memset(iw_stats, 0, sizeof(*iw_stats)); ++ EXIT2(return); ++ } ++ res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi)); ++ if (res == NDIS_STATUS_SUCCESS) ++ iw_stats->qual.level = rssi; ++ ++ qual = 100 * (rssi - WL_NOISE) / (WL_SIGMAX - WL_NOISE); ++ if (qual < 0) ++ qual = 0; ++ else if (qual > 100) ++ qual = 100; ++ ++ iw_stats->qual.noise = WL_NOISE; ++ iw_stats->qual.qual = qual; ++ ++ res = mp_query(wnd, OID_802_11_STATISTICS, ++ &ndis_stats, sizeof(ndis_stats)); ++ if (res != NDIS_STATUS_SUCCESS) ++ EXIT2(return); ++ iw_stats->discard.retries = (unsigned long)ndis_stats.retry + ++ (unsigned long)ndis_stats.multi_retry; ++ iw_stats->discard.misc = (unsigned long)ndis_stats.fcs_err + ++ (unsigned long)ndis_stats.rtss_fail + ++ (unsigned long)ndis_stats.ack_fail + ++ (unsigned long)ndis_stats.frame_dup; ++ ++ EXIT2(return); ++} ++ ++static void set_multicast_list(struct ndis_device *wnd) ++{ ++ struct net_device *net_dev; ++ ULONG packet_filter; ++ NDIS_STATUS res; ++ ++ net_dev = wnd->net_dev; ++ packet_filter = wnd->packet_filter; ++ ++ TRACE2("0x%08x", packet_filter); ++ if (net_dev->flags & IFF_PROMISC) { ++ packet_filter |= NDIS_PACKET_TYPE_PROMISCUOUS | ++ NDIS_PACKET_TYPE_ALL_LOCAL; ++ } else if (net_dev->flags & IFF_ALLMULTI || ++ netdev_mc_count(net_dev) > wnd->multicast_size) { ++ packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; ++ TRACE2("0x%08x", packet_filter); ++ } else if (netdev_mc_count(net_dev) > 0) { ++ int i, size; ++ char *buf; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) ++ struct netdev_hw_addr *ha; ++#else ++ struct dev_mc_list *mclist; ++#endif ++ size = min(wnd->multicast_size, netdev_mc_count(net_dev)); ++ TRACE2("%d, %d", wnd->multicast_size, netdev_mc_count(net_dev)); ++ buf = kmalloc(size * ETH_ALEN, GFP_KERNEL); ++ if (!buf) { ++ WARNING("couldn't allocate memory"); ++ EXIT2(return); ++ } ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) ++ i = 0; ++ netdev_for_each_mc_addr(ha, net_dev) { ++ if (i >= size) ++ break; ++ memcpy(buf + i * ETH_ALEN, ha->addr, ETH_ALEN); ++ TRACE2(MACSTRSEP, MAC2STR(ha->addr)); ++ i++; ++ } ++#else ++ mclist = net_dev->mc_list; ++ for (i = 0; i < size && mclist; mclist = mclist->next) { ++ if (mclist->dmi_addrlen != ETH_ALEN) ++ continue; ++ memcpy(buf + i * ETH_ALEN, mclist->dmi_addr, ETH_ALEN); ++ TRACE2(MACSTRSEP, MAC2STR(mclist->dmi_addr)); ++ i++; ++ } ++#endif ++ res = mp_set(wnd, OID_802_3_MULTICAST_LIST, buf, i * ETH_ALEN); ++ if (res == NDIS_STATUS_SUCCESS && i > 0) ++ packet_filter |= NDIS_PACKET_TYPE_MULTICAST; ++ else ++ packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; ++ kfree(buf); ++ } ++ TRACE2("0x%08x", packet_filter); ++ res = set_packet_filter(wnd, packet_filter); ++ if (res) ++ TRACE1("couldn't set packet filter (%08X)", res); ++ EXIT2(return); ++} ++ ++static void link_status_off(struct ndis_device *wnd) ++{ ++#ifdef CONFIG_WIRELESS_EXT ++ union iwreq_data wrqu; ++ ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ wireless_send_event(wnd->net_dev, SIOCGIWAP, &wrqu, NULL); ++#endif ++ EXIT2(return); ++} ++ ++static void link_status_on(struct ndis_device *wnd) ++{ ++#ifdef CONFIG_WIRELESS_EXT ++ struct ndis_assoc_info *ndis_assoc_info; ++ union iwreq_data wrqu; ++ NDIS_STATUS res; ++ const int assoc_size = sizeof(*ndis_assoc_info) + IW_CUSTOM_MAX + 32; ++#endif ++ ++ ENTER2(""); ++#ifdef CONFIG_WIRELESS_EXT ++ memset(&wrqu, 0, sizeof(wrqu)); ++ ndis_assoc_info = kzalloc(assoc_size, GFP_KERNEL); ++ if (!ndis_assoc_info) { ++ ERROR("couldn't allocate memory"); ++ goto send_assoc_event; ++ } ++ res = mp_query(wnd, OID_802_11_ASSOCIATION_INFORMATION, ++ ndis_assoc_info, assoc_size); ++ if (res) { ++ TRACE2("query assoc_info failed (%08X)", res); ++ kfree(ndis_assoc_info); ++ goto send_assoc_event; ++ } ++ TRACE2("%u, 0x%x, %u, 0x%x, %u", ndis_assoc_info->length, ++ ndis_assoc_info->req_ies, ndis_assoc_info->req_ie_length, ++ ndis_assoc_info->resp_ies, ndis_assoc_info->resp_ie_length); ++ if (ndis_assoc_info->req_ie_length > 0) { ++ wrqu.data.length = ndis_assoc_info->req_ie_length; ++ wireless_send_event(wnd->net_dev, IWEVASSOCREQIE, &wrqu, ++ ((char *)ndis_assoc_info) + ++ ndis_assoc_info->offset_req_ies); ++ } ++ if (ndis_assoc_info->resp_ie_length > 0) { ++ wrqu.data.length = ndis_assoc_info->resp_ie_length; ++ wireless_send_event(wnd->net_dev, IWEVASSOCRESPIE, &wrqu, ++ ((char *)ndis_assoc_info) + ++ ndis_assoc_info->offset_resp_ies); ++ } ++ kfree(ndis_assoc_info); ++ ++send_assoc_event: ++ get_ap_address(wnd, wrqu.ap_addr.sa_data); ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ TRACE2(MACSTRSEP, MAC2STR(wrqu.ap_addr.sa_data)); ++ wireless_send_event(wnd->net_dev, SIOCGIWAP, &wrqu, NULL); ++#endif ++ EXIT2(return); ++} ++ ++static void iw_stats_timer_proc(unsigned long data) ++{ ++ struct ndis_device *wnd = (struct ndis_device *)data; ++ ++ ENTER2("%d", wnd->iw_stats_interval); ++ if (wnd->iw_stats_interval > 0) { ++ set_bit(COLLECT_IW_STATS, &wnd->ndis_pending_work); ++ queue_work(wrapndis_wq, &wnd->ndis_work); ++ } ++ mod_timer(&wnd->iw_stats_timer, jiffies + wnd->iw_stats_interval); ++} ++ ++static void add_iw_stats_timer(struct ndis_device *wnd) ++{ ++ if (wnd->physical_medium != NdisPhysicalMediumWirelessLan) ++ return; ++ if (wnd->iw_stats_interval < 0) ++ wnd->iw_stats_interval *= -1; ++ wnd->iw_stats_timer.data = (unsigned long)wnd; ++ wnd->iw_stats_timer.function = iw_stats_timer_proc; ++ mod_timer(&wnd->iw_stats_timer, jiffies + wnd->iw_stats_interval); ++} ++ ++static void del_iw_stats_timer(struct ndis_device *wnd) ++{ ++ ENTER2("%d", wnd->iw_stats_interval); ++ wnd->iw_stats_interval *= -1; ++ del_timer_sync(&wnd->iw_stats_timer); ++ EXIT2(return); ++} ++ ++static void hangcheck_proc(unsigned long data) ++{ ++ struct ndis_device *wnd = (struct ndis_device *)data; ++ ++ ENTER3("%d", wnd->hangcheck_interval); ++ if (wnd->hangcheck_interval > 0) { ++ set_bit(HANGCHECK, &wnd->ndis_pending_work); ++ queue_work(wrapndis_wq, &wnd->ndis_work); ++ } ++ mod_timer(&wnd->hangcheck_timer, jiffies + wnd->hangcheck_interval); ++ EXIT3(return); ++} ++ ++void hangcheck_add(struct ndis_device *wnd) ++{ ++ if (!wnd->wd->driver->ndis_driver->mp.hangcheck || ++ hangcheck_interval < 0) ++ EXIT2(return); ++ ++ if (hangcheck_interval > 0) ++ wnd->hangcheck_interval = hangcheck_interval * HZ; ++ if (wnd->hangcheck_interval < 0) ++ wnd->hangcheck_interval *= -1; ++ wnd->hangcheck_timer.data = (unsigned long)wnd; ++ wnd->hangcheck_timer.function = hangcheck_proc; ++ mod_timer(&wnd->hangcheck_timer, jiffies + wnd->hangcheck_interval); ++ EXIT2(return); ++} ++ ++void hangcheck_del(struct ndis_device *wnd) ++{ ++ ENTER2("%d", wnd->hangcheck_interval); ++ if (wnd->hangcheck_interval > 0) ++ wnd->hangcheck_interval *= -1; ++ del_timer_sync(&wnd->hangcheck_timer); ++ EXIT2(return); ++} ++ ++/* worker procedure to take care of setting/checking various states */ ++static void wrapndis_worker(struct work_struct *work) ++{ ++ struct ndis_device *wnd; ++ ++ wnd = container_of(work, struct ndis_device, ndis_work); ++ WORKTRACE("0x%lx", wnd->ndis_pending_work); ++ ++ if (test_and_clear_bit(NETIF_WAKEQ, &wnd->ndis_pending_work)) { ++ netif_tx_lock_bh(wnd->net_dev); ++ netif_wake_queue(wnd->net_dev); ++ netif_tx_unlock_bh(wnd->net_dev); ++ } ++ ++ if (test_and_clear_bit(LINK_STATUS_OFF, &wnd->ndis_pending_work)) ++ link_status_off(wnd); ++ ++ if (test_and_clear_bit(LINK_STATUS_ON, &wnd->ndis_pending_work)) ++ link_status_on(wnd); ++ ++ if (test_and_clear_bit(COLLECT_IW_STATS, &wnd->ndis_pending_work)) ++ update_iw_stats(wnd); ++ ++ if (test_and_clear_bit(SET_MULTICAST_LIST, ++ &wnd->ndis_pending_work)) ++ set_multicast_list(wnd); ++ ++ if (test_and_clear_bit(HANGCHECK, &wnd->ndis_pending_work)) { ++ struct miniport *mp; ++ BOOLEAN reset; ++ KIRQL irql; ++ ++ mp = &wnd->wd->driver->ndis_driver->mp; ++ irql = serialize_lock_irql(wnd); ++ reset = LIN2WIN1(mp->hangcheck, wnd->nmb->mp_ctx); ++ serialize_unlock_irql(wnd, irql); ++ if (reset) { ++ TRACE2("%s needs reset", wnd->net_dev->name); ++ mp_reset(wnd); ++ } ++ } ++ WORKEXIT(return); ++} ++ ++NDIS_STATUS ndis_reinit(struct ndis_device *wnd) ++{ ++ NDIS_STATUS status; ++ ++ wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; ++ status = mp_set_power_state(wnd, NdisDeviceStateD3); ++ if (status != NDIS_STATUS_SUCCESS) { ++ ERROR("halting device %s failed: %08X", wnd->net_dev->name, ++ status); ++ return status; ++ } ++ status = mp_set_power_state(wnd, NdisDeviceStateD0); ++ if (status != NDIS_STATUS_SUCCESS) ++ ERROR("starting device %s failed: %08X", wnd->net_dev->name, ++ status); ++ return status; ++} ++ ++#ifdef CONFIG_WIRELESS_EXT ++static void get_encryption_capa(struct ndis_device *wnd, char *buf, ++ const int buf_len) ++{ ++ int i, mode; ++ NDIS_STATUS res; ++ struct ndis_assoc_info ndis_assoc_info; ++ struct ndis_add_key ndis_key; ++ struct ndis_capability *c; ++ ++ ENTER1("%p", wnd); ++ /* set network type to g, b, or a, in that order */ ++ res = mp_query(wnd, OID_802_11_NETWORK_TYPES_SUPPORTED, buf, buf_len); ++ if (res == NDIS_STATUS_SUCCESS) { ++ struct network_type_list *net_types; ++ unsigned long types = 0; ++ net_types = (typeof(net_types))buf; ++ for (i = 0; i < net_types->num; i++) { ++ TRACE2("%d", net_types->types[i]); ++ set_bit(net_types->types[i], &types); ++ } ++ if (test_bit(Ndis802_11OFDM24, &types)) ++ mode = Ndis802_11OFDM24; ++ else if (test_bit(Ndis802_11DS, &types)) ++ mode = Ndis802_11DS; ++ else if (test_bit(Ndis802_11OFDM5, &types)) ++ mode = Ndis802_11OFDM5; ++ else ++ mode = Ndis802_11DS; ++ mp_set_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, mode); ++ } ++ /* check if WEP is supported */ ++ if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104, ++ IW_AUTH_CIPHER_NONE) == 0 && ++ get_ndis_encr_mode(wnd) == Ndis802_11Encryption1KeyAbsent) ++ set_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr); ++ ++ /* check if WPA is supported */ ++ if (set_ndis_auth_mode(wnd, Ndis802_11AuthModeWPA) == 0 && ++ get_ndis_auth_mode(wnd) == Ndis802_11AuthModeWPA) ++ set_bit(Ndis802_11AuthModeWPA, &wnd->capa.encr); ++ else ++ EXIT1(return); ++ ++ if (set_ndis_auth_mode(wnd, Ndis802_11AuthModeWPAPSK) == 0 && ++ get_ndis_auth_mode(wnd) == Ndis802_11AuthModeWPAPSK) ++ set_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.encr); ++ ++ /* check for highest encryption */ ++ mode = 0; ++ if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_CCMP, ++ IW_AUTH_CIPHER_NONE) == 0 && ++ (i = get_ndis_encr_mode(wnd)) > 0 && ++ (i == Ndis802_11Encryption3KeyAbsent || ++ i == Ndis802_11Encryption3Enabled)) ++ mode = Ndis802_11Encryption3Enabled; ++ else if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_TKIP, ++ IW_AUTH_CIPHER_NONE) == 0 && ++ (i = get_ndis_encr_mode(wnd)) > 0 && ++ (i == Ndis802_11Encryption2KeyAbsent || ++ i == Ndis802_11Encryption2Enabled)) ++ mode = Ndis802_11Encryption2Enabled; ++ else if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104, ++ IW_AUTH_CIPHER_NONE) == 0 && ++ (i = get_ndis_encr_mode(wnd)) > 0 && ++ (i == Ndis802_11Encryption1KeyAbsent || ++ i == Ndis802_11Encryption1Enabled)) ++ mode = Ndis802_11Encryption1Enabled; ++ ++ TRACE1("mode: %d", mode); ++ if (mode == 0) ++ EXIT1(return); ++ set_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr); ++ if (mode == Ndis802_11Encryption1Enabled) ++ EXIT1(return); ++ ++ ndis_key.length = 32; ++ ndis_key.index = 0xC0000001; ++ ndis_key.struct_size = sizeof(ndis_key); ++ res = mp_set(wnd, OID_802_11_ADD_KEY, &ndis_key, ndis_key.struct_size); ++ TRACE2("%08X, %zu", res, sizeof(ndis_key)); ++ if (res && res != NDIS_STATUS_INVALID_DATA) ++ EXIT1(return); ++ res = mp_query(wnd, OID_802_11_ASSOCIATION_INFORMATION, ++ &ndis_assoc_info, sizeof(ndis_assoc_info)); ++ TRACE1("%08X", res); ++ if (res == NDIS_STATUS_NOT_SUPPORTED) ++ EXIT1(return); ++ ++ set_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr); ++ if (mode == Ndis802_11Encryption3Enabled) ++ set_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr); ++ /* not all drivers support OID_802_11_CAPABILITY, so we don't ++ * know for sure if driver support WPA or WPAPSK; assume ++ * WPAPSK */ ++ set_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.auth); ++ wnd->max_pmkids = 1; ++ ++ memset(buf, 0, buf_len); ++ c = (struct ndis_capability *)buf; ++ res = mp_query(wnd, OID_802_11_CAPABILITY, buf, buf_len); ++ if (!(res == NDIS_STATUS_SUCCESS && c->version == 2)) ++ EXIT1(return); ++ wnd->max_pmkids = c->num_PMKIDs; ++ ++ for (i = 0; i < c->num_auth_encr_pair; i++) { ++ struct ndis_auth_encr_pair *ae; ++ ++ ae = &c->auth_encr_pair[i]; ++ if ((char *)(ae + 1) > buf + buf_len) ++ break; ++ switch (ae->auth_mode) { ++ case Ndis802_11AuthModeOpen: ++ case Ndis802_11AuthModeShared: ++ case Ndis802_11AuthModeWPA: ++ case Ndis802_11AuthModeWPAPSK: ++ case Ndis802_11AuthModeWPANone: ++ case Ndis802_11AuthModeWPA2: ++ case Ndis802_11AuthModeWPA2PSK: ++ set_bit(ae->auth_mode, &wnd->capa.auth); ++ break; ++ default: ++ WARNING("unknown auth_mode: %d", ae->auth_mode); ++ break; ++ } ++ switch (ae->encr_mode) { ++ case Ndis802_11EncryptionDisabled: ++ case Ndis802_11Encryption1Enabled: ++ case Ndis802_11Encryption2Enabled: ++ case Ndis802_11Encryption3Enabled: ++ set_bit(ae->encr_mode, &wnd->capa.encr); ++ break; ++ default: ++ WARNING("unknown encr_mode: %d", ae->encr_mode); ++ break; ++ } ++ } ++ EXIT1(return); ++} ++#endif ++ ++wstdcall NTSTATUS NdisDispatchDeviceControl(struct device_object *fdo, ++ struct irp *irp) ++{ ++ struct ndis_device *wnd; ++ ++ TRACE3("fdo: %p", fdo); ++ /* for now, we don't have anything interesting here, so pass it ++ * down to bus driver */ ++ wnd = fdo->reserved; ++ return IoPassIrpDown(wnd->nmb->pdo, irp); ++} ++WIN_FUNC_DECL(NdisDispatchDeviceControl,2) ++ ++wstdcall NTSTATUS NdisDispatchPower(struct device_object *fdo, struct irp *irp) ++{ ++ struct io_stack_location *irp_sl; ++ struct ndis_device *wnd; ++ enum ndis_power_state state; ++ NTSTATUS status; ++ NDIS_STATUS ndis_status; ++ ++ irp_sl = IoGetCurrentIrpStackLocation(irp); ++ wnd = fdo->reserved; ++ IOTRACE("fdo: %p, fn: %d:%d, wnd: %p", fdo, irp_sl->major_fn, ++ irp_sl->minor_fn, wnd); ++ if ((irp_sl->params.power.type == SystemPowerState && ++ irp_sl->params.power.state.system_state > PowerSystemWorking) || ++ (irp_sl->params.power.type == DevicePowerState && ++ irp_sl->params.power.state.device_state > PowerDeviceD0)) ++ state = NdisDeviceStateD3; ++ else ++ state = NdisDeviceStateD0; ++ switch (irp_sl->minor_fn) { ++ case IRP_MN_SET_POWER: ++ if (state == NdisDeviceStateD0) { ++ status = IoSyncForwardIrp(wnd->nmb->pdo, irp); ++ if (status != STATUS_SUCCESS) ++ break; ++ ndis_status = mp_set_power_state(wnd, state); ++ if (ndis_status != NDIS_STATUS_SUCCESS) ++ WARNING("couldn't set power to %d: %08X", ++ state, ndis_status); ++ TRACE2("%s: device resumed", wnd->net_dev->name); ++ irp->io_status.status = status = STATUS_SUCCESS; ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ break; ++ } else { ++ ndis_status = mp_set_power_state(wnd, state); ++ /* TODO: handle error case */ ++ if (ndis_status != NDIS_STATUS_SUCCESS) ++ WARNING("setting power to %d failed: %08X", ++ state, ndis_status); ++ status = IoAsyncForwardIrp(wnd->nmb->pdo, irp); ++ } ++ break; ++ case IRP_MN_QUERY_POWER: ++ if (wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND) { ++ ndis_status = mp_query(wnd, OID_PNP_QUERY_POWER, ++ &state, sizeof(state)); ++ TRACE2("%d, %08X", state, ndis_status); ++ /* this OID must always succeed */ ++ if (ndis_status != NDIS_STATUS_SUCCESS) ++ TRACE1("query power returns %08X", ndis_status); ++ irp->io_status.status = STATUS_SUCCESS; ++ } else ++ irp->io_status.status = STATUS_SUCCESS; ++ status = IoPassIrpDown(wnd->nmb->pdo, irp); ++ break; ++ case IRP_MN_WAIT_WAKE: ++ case IRP_MN_POWER_SEQUENCE: ++ /* TODO: implement WAIT_WAKE */ ++ status = IoPassIrpDown(wnd->nmb->pdo, irp); ++ break; ++ default: ++ status = IoPassIrpDown(wnd->nmb->pdo, irp); ++ break; ++ } ++ IOEXIT(return status); ++} ++WIN_FUNC_DECL(NdisDispatchPower,2) ++ ++wstdcall NTSTATUS NdisDispatchPnp(struct device_object *fdo, struct irp *irp) ++{ ++ struct io_stack_location *irp_sl; ++ struct ndis_device *wnd; ++ struct device_object *pdo; ++ NTSTATUS status; ++ ++ IOTRACE("fdo: %p, irp: %p", fdo, irp); ++ irp_sl = IoGetCurrentIrpStackLocation(irp); ++ wnd = fdo->reserved; ++ pdo = wnd->nmb->pdo; ++ switch (irp_sl->minor_fn) { ++ case IRP_MN_START_DEVICE: ++ status = IoSyncForwardIrp(pdo, irp); ++ if (status != STATUS_SUCCESS) ++ break; ++ if (ndis_start_device(wnd) == NDIS_STATUS_SUCCESS) ++ status = STATUS_SUCCESS; ++ else ++ status = STATUS_FAILURE; ++ irp->io_status.status = status; ++ IoCompleteRequest(irp, IO_NO_INCREMENT); ++ break; ++ case IRP_MN_QUERY_STOP_DEVICE: ++ /* TODO: implement in NDIS */ ++ status = IoPassIrpDown(wnd->nmb->pdo, irp); ++ break; ++ case IRP_MN_STOP_DEVICE: ++ mp_halt(wnd); ++ irp->io_status.status = STATUS_SUCCESS; ++ status = IoAsyncForwardIrp(pdo, irp); ++ break; ++ case IRP_MN_REMOVE_DEVICE: ++ TRACE1("%s", wnd->net_dev->name); ++ mp_pnp_event(wnd, NdisDevicePnPEventSurpriseRemoved, 0); ++ if (ndis_remove_device(wnd)) { ++ status = STATUS_FAILURE; ++ break; ++ } ++ /* wnd is already freed */ ++ status = IoAsyncForwardIrp(pdo, irp); ++ IoDetachDevice(fdo); ++ IoDeleteDevice(fdo); ++ break; ++ default: ++ status = IoAsyncForwardIrp(pdo, irp); ++ break; ++ } ++ IOTRACE("status: %08X", status); ++ IOEXIT(return status); ++} ++WIN_FUNC_DECL(NdisDispatchPnp,2) ++ ++static void set_task_offload(struct ndis_device *wnd, void *buf, ++ const int buf_size) ++{ ++ struct ndis_task_offload_header *task_offload_header; ++ struct ndis_task_offload *task_offload; ++ struct ndis_task_tcp_ip_checksum *csum = NULL; ++ struct ndis_task_tcp_large_send *tso = NULL; ++ NDIS_STATUS status; ++ ++ memset(buf, 0, buf_size); ++ task_offload_header = buf; ++ task_offload_header->version = NDIS_TASK_OFFLOAD_VERSION; ++ task_offload_header->size = sizeof(*task_offload_header); ++ task_offload_header->encap_format.flags.fixed_header_size = 1; ++ task_offload_header->encap_format.header_size = sizeof(struct ethhdr); ++ task_offload_header->encap_format.encap = IEEE_802_3_Encapsulation; ++ status = mp_query(wnd, OID_TCP_TASK_OFFLOAD, buf, buf_size); ++ TRACE1("%08X", status); ++ if (status != NDIS_STATUS_SUCCESS) ++ EXIT1(return); ++ if (task_offload_header->offset_first_task == 0) ++ EXIT1(return); ++ task_offload = ((void *)task_offload_header + ++ task_offload_header->offset_first_task); ++ while (1) { ++ TRACE1("%d, %d", task_offload->version, task_offload->task); ++ switch (task_offload->task) { ++ case TcpIpChecksumNdisTask: ++ csum = (void *)task_offload->task_buf; ++ break; ++ case TcpLargeSendNdisTask: ++ tso = (void *)task_offload->task_buf; ++ break; ++ default: ++ TRACE1("%d", task_offload->task); ++ break; ++ } ++ if (task_offload->offset_next_task == 0) ++ break; ++ task_offload = (void *)task_offload + ++ task_offload->offset_next_task; ++ } ++ if (tso) ++ TRACE1("%u, %u, %d, %d", tso->max_size, tso->min_seg_count, ++ tso->tcp_opts, tso->ip_opts); ++ if (!csum) ++ EXIT1(return); ++ TRACE1("%08x, %08x", csum->v4_tx.value, csum->v4_rx.value); ++ task_offload_header->encap_format.flags.fixed_header_size = 1; ++ task_offload_header->encap_format.header_size = sizeof(struct ethhdr); ++ task_offload_header->offset_first_task = sizeof(*task_offload_header); ++ task_offload = ((void *)task_offload_header + ++ task_offload_header->offset_first_task); ++ task_offload->offset_next_task = 0; ++ task_offload->size = sizeof(*task_offload); ++ task_offload->task = TcpIpChecksumNdisTask; ++ memcpy(task_offload->task_buf, csum, sizeof(*csum)); ++ task_offload->task_buf_length = sizeof(*csum); ++ status = mp_set(wnd, OID_TCP_TASK_OFFLOAD, task_offload_header, ++ sizeof(*task_offload_header) + ++ sizeof(*task_offload) + sizeof(*csum)); ++ TRACE1("%08X", status); ++ if (status != NDIS_STATUS_SUCCESS) ++ EXIT2(return); ++ wnd->tx_csum = csum->v4_tx; ++ if (csum->v4_tx.tcp_csum && csum->v4_tx.udp_csum) { ++ if (csum->v4_tx.ip_csum) { ++ wnd->net_dev->features |= NETIF_F_HW_CSUM; ++ TRACE1("hw checksum enabled"); ++ } else { ++ wnd->net_dev->features |= NETIF_F_IP_CSUM; ++ TRACE1("IP checksum enabled"); ++ } ++ if (wnd->sg_dma_size) ++ wnd->net_dev->features |= NETIF_F_SG; ++ } ++ wnd->rx_csum = csum->v4_rx; ++ EXIT1(return); ++} ++ ++static void get_supported_oids(struct ndis_device *wnd) ++{ ++ NDIS_STATUS res; ++ int i, n, needed; ++ ndis_oid *oids; ++ ++ res = mp_query_info(wnd, OID_GEN_SUPPORTED_LIST, NULL, 0, NULL, ++ &needed); ++ if (!(res == NDIS_STATUS_BUFFER_TOO_SHORT || ++ res == NDIS_STATUS_INVALID_LENGTH)) ++ EXIT1(return); ++ oids = kmalloc(needed, GFP_KERNEL); ++ if (!oids) { ++ TRACE1("couldn't allocate memory"); ++ EXIT1(return); ++ } ++ res = mp_query(wnd, OID_GEN_SUPPORTED_LIST, oids, needed); ++ if (res) { ++ TRACE1("failed: %08X", res); ++ kfree(oids); ++ EXIT1(return); ++ } ++ for (i = 0, n = needed / sizeof(*oids); i < n; i++) { ++ TRACE1("oid: %08X", oids[i]); ++ /* if a wireless device didn't say so for ++ * OID_GEN_PHYSICAL_MEDIUM (they should, but in case) */ ++ if (wnd->physical_medium != NdisPhysicalMediumWirelessLan && ++ oids[i] == OID_802_11_SSID) ++ wnd->physical_medium = NdisPhysicalMediumWirelessLan; ++ } ++ kfree(oids); ++ EXIT1(return); ++} ++ ++static void ndis_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 2); ++ strcat(info->driver, "+"); ++ strncat(info->driver, wnd->wd->driver->name, ++ sizeof(info->driver) - strlen(DRIVER_NAME) - 1); ++ strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 2); ++ strcat(info->version, "+"); ++ strncat(info->version, wnd->wd->driver->version, ++ sizeof(info->version) - strlen(DRIVER_VERSION) - 1); ++ if (wrap_is_pci_bus(wnd->wd->dev_bus)) ++ strncpy(info->bus_info, pci_name(wnd->wd->pci.pdev), ++ sizeof(info->bus_info) - 1); ++#ifdef ENABLE_USB ++ else ++ usb_make_path(wnd->wd->usb.udev, info->bus_info, ++ sizeof(info->bus_info) - 1); ++#endif ++ return; ++} ++ ++static u32 ndis_get_link(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ return netif_carrier_ok(wnd->net_dev); ++} ++ ++static void ndis_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ wol->supported = 0; ++ wol->wolopts = 0; ++ if (!(wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND)) ++ EXIT2(return); ++ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) ++ EXIT2(return); ++ /* we always suspend to D3 */ ++ if (wnd->pnp_capa.wakeup.min_magic_packet_wakeup < NdisDeviceStateD3) ++ return; ++ wol->supported |= WAKE_MAGIC; ++ if (wnd->ndis_wolopts & NDIS_PNP_WAKE_UP_MAGIC_PACKET) ++ wol->wolopts |= WAKE_MAGIC; ++ return; ++} ++ ++static int ndis_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ if (!(wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND)) ++ return -EOPNOTSUPP; ++ if (wnd->pnp_capa.wakeup.min_magic_packet_wakeup < NdisDeviceStateD3) ++ EXIT2(return -EOPNOTSUPP); ++ TRACE2("0x%x", wol->wolopts); ++ if (wol->wolopts & WAKE_MAGIC) { ++ wnd->ndis_wolopts |= NDIS_PNP_WAKE_UP_MAGIC_PACKET; ++ if (wol->wolopts != WAKE_MAGIC) ++ WARNING("ignored wake-on-lan options: 0x%x", ++ wol->wolopts & ~WAKE_MAGIC); ++ } else if (!wol->wolopts) ++ wnd->ndis_wolopts = 0; ++ else ++ return -EOPNOTSUPP; ++ TRACE2("0x%x", wnd->ndis_wolopts); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) ++static u32 ndis_get_tx_csum(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ if (wnd->tx_csum.tcp_csum && wnd->tx_csum.udp_csum) ++ return 1; ++ else ++ return 0; ++} ++ ++static u32 ndis_get_rx_csum(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ if (wnd->rx_csum.value) ++ return 1; ++ else ++ return 0; ++} ++ ++static int ndis_set_tx_csum(struct net_device *dev, u32 data) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ if (data && (wnd->tx_csum.value == 0)) ++ return -EOPNOTSUPP; ++ ++ if (wnd->tx_csum.ip_csum) ++ ethtool_op_set_tx_hw_csum(dev, data); ++ else ++ ethtool_op_set_tx_csum(dev, data); ++ return 0; ++} ++ ++static int ndis_set_rx_csum(struct net_device *dev, u32 data) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ ++ if (data && (wnd->tx_csum.value == 0)) ++ return -EOPNOTSUPP; ++ ++ /* TODO: enable/disable rx csum through NDIS */ ++ return 0; ++} ++ ++static u32 ndis_get_sg(struct net_device *dev) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ if (wnd->sg_dma_size) ++ return ethtool_op_get_sg(dev); ++ else ++ return 0; ++} ++ ++static int ndis_set_sg(struct net_device *dev, u32 data) ++{ ++ struct ndis_device *wnd = netdev_priv(dev); ++ if (wnd->sg_dma_size) ++ return ethtool_op_set_sg(dev, data); ++ else ++ return -EOPNOTSUPP; ++} ++#endif ++ ++static struct ethtool_ops ndis_ethtool_ops = { ++ .get_drvinfo = ndis_get_drvinfo, ++ .get_link = ndis_get_link, ++ .get_wol = ndis_get_wol, ++ .set_wol = ndis_set_wol, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) ++ .get_tx_csum = ndis_get_tx_csum, ++ .get_rx_csum = ndis_get_rx_csum, ++ .set_tx_csum = ndis_set_tx_csum, ++ .set_rx_csum = ndis_set_rx_csum, ++ .get_sg = ndis_get_sg, ++ .set_sg = ndis_set_sg, ++#endif ++}; ++ ++static int notifier_event(struct notifier_block *notifier, unsigned long event, ++ void *ptr) ++{ ++ struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); ++ ++ ENTER2("0x%lx", event); ++ if (net_dev->ethtool_ops == &ndis_ethtool_ops ++ && event == NETDEV_CHANGENAME) { ++ struct ndis_device *wnd = netdev_priv(net_dev); ++ ++ /* called with rtnl lock held, so no need to lock */ ++ if (likely(wnd->procfs_iface)) { ++ printk(KERN_INFO "%s: interface renamed to '%s'\n", ++ DRIVER_NAME, net_dev->name); ++ wrap_procfs_remove_ndis_device(wnd); ++ wrap_procfs_add_ndis_device(wnd); ++ } ++ } ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block netdev_notifier = { ++ .notifier_call = notifier_event, ++}; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) ++static const struct net_device_ops ndis_netdev_ops = { ++ .ndo_init = ndis_net_dev_init, ++ .ndo_uninit = ndis_net_dev_uninit, ++ .ndo_open = ndis_net_dev_open, ++ .ndo_stop = ndis_net_dev_close, ++ .ndo_start_xmit = tx_skbuff, ++ .ndo_change_mtu = ndis_change_mtu, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) ++ .ndo_set_rx_mode = ndis_set_multicast_list, ++#else ++ .ndo_set_multicast_list = ndis_set_multicast_list, ++#endif ++ .ndo_set_mac_address = ndis_set_mac_address, ++ .ndo_get_stats = ndis_get_stats, ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ .ndo_poll_controller = ndis_poll_controller, ++#endif ++}; ++#endif ++ ++static NDIS_STATUS ndis_start_device(struct ndis_device *wnd) ++{ ++ struct wrap_device *wd; ++ struct net_device *net_dev; ++ NDIS_STATUS status; ++ char *buf; ++ const int buf_len = 256; ++ mac_address mac; ++ struct transport_header_offset *tx_header_offset; ++ int n; ++ ++ ENTER2("%d", in_atomic()); ++ status = mp_init(wnd); ++ if (status == NDIS_STATUS_NOT_RECOGNIZED) ++ EXIT1(return NDIS_STATUS_SUCCESS); ++ if (status != NDIS_STATUS_SUCCESS) ++ EXIT1(return status); ++ wd = wnd->wd; ++ net_dev = wnd->net_dev; ++ ++ get_supported_oids(wnd); ++ memset(mac, 0, sizeof(mac)); ++ status = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac)); ++ if (memcmp(mac, "\x00\x00\x00\x00\x00\x00", sizeof(mac)) == 0) { ++ status = mp_query(wnd, OID_802_3_PERMANENT_ADDRESS, mac, ++ sizeof(mac)); ++ if (status != NDIS_STATUS_SUCCESS) { ++ ERROR("couldn't get mac address: %08X", status); ++ goto err_start; ++ } ++ } ++ TRACE1("mac:" MACSTRSEP, MAC2STR(mac)); ++ memcpy(net_dev->dev_addr, mac, ETH_ALEN); ++ ++ strncpy(net_dev->name, if_name, IFNAMSIZ - 1); ++ net_dev->name[IFNAMSIZ - 1] = 0; ++ ++ wnd->packet_filter = NDIS_PACKET_TYPE_DIRECTED | ++ NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_MULTICAST; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) ++ net_dev->netdev_ops = &ndis_netdev_ops; ++#else ++ net_dev->init = ndis_net_dev_init; ++ net_dev->uninit = ndis_net_dev_uninit; ++ net_dev->open = ndis_net_dev_open; ++ net_dev->hard_start_xmit = tx_skbuff; ++ net_dev->stop = ndis_net_dev_close; ++ net_dev->get_stats = ndis_get_stats; ++ net_dev->change_mtu = ndis_change_mtu; ++ net_dev->set_multicast_list = ndis_set_multicast_list; ++ net_dev->set_mac_address = ndis_set_mac_address; ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ net_dev->poll_controller = ndis_poll_controller; ++#endif ++#endif ++#ifdef CONFIG_WIRELESS_EXT ++ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { ++ net_dev->wireless_handlers = &ndis_handler_def; ++ } ++#endif ++ net_dev->ethtool_ops = &ndis_ethtool_ops; ++ if (wnd->mp_interrupt) ++ net_dev->irq = wnd->mp_interrupt->irq; ++ net_dev->mem_start = wnd->mem_start; ++ net_dev->mem_end = wnd->mem_end; ++ status = mp_query_int(wnd, OID_802_3_MAXIMUM_LIST_SIZE, ++ &wnd->multicast_size); ++ if (status != NDIS_STATUS_SUCCESS || wnd->multicast_size < 0) ++ wnd->multicast_size = 0; ++ if (wnd->multicast_size > 0) ++ net_dev->flags |= IFF_MULTICAST; ++ else ++ net_dev->flags &= ~IFF_MULTICAST; ++ ++ buf = kmalloc(buf_len, GFP_KERNEL); ++ if (!buf) { ++ WARNING("couldn't allocate memory"); ++ goto err_start; ++ } ++ ++ set_task_offload(wnd, buf, buf_len); ++#ifdef NETIF_F_LLTX ++ net_dev->features |= NETIF_F_LLTX; ++#endif ++ ++ if (register_netdev(net_dev)) { ++ ERROR("cannot register net device %s", net_dev->name); ++ goto err_register; ++ } ++ memset(buf, 0, buf_len); ++ status = mp_query(wnd, OID_GEN_VENDOR_DESCRIPTION, buf, buf_len); ++ if (status != NDIS_STATUS_SUCCESS) { ++ WARNING("couldn't get vendor information: 0x%x", status); ++ buf[0] = 0; ++ } ++ wnd->drv_ndis_version = n = 0; ++ mp_query_int(wnd, OID_GEN_DRIVER_VERSION, &wnd->drv_ndis_version); ++ mp_query_int(wnd, OID_GEN_VENDOR_DRIVER_VERSION, &n); ++ ++ printk(KERN_INFO "%s: ethernet device " MACSTRSEP " using %sNDIS " ++ "driver: %s, version: 0x%x, NDIS version: 0x%x, vendor: '%s', " ++ "%s\n", net_dev->name, MAC2STR(net_dev->dev_addr), ++ deserialized_driver(wnd) ? "" : "serialized ", ++ wd->driver->name, n, wnd->drv_ndis_version, buf, ++ wd->conf_file_name); ++ ++ if (deserialized_driver(wnd)) { ++ /* deserialized drivers don't have a limit, but we ++ * keep max at TX_RING_SIZE */ ++ wnd->max_tx_packets = TX_RING_SIZE; ++ } else { ++ status = mp_query_int(wnd, OID_GEN_MAXIMUM_SEND_PACKETS, ++ &wnd->max_tx_packets); ++ if (status != NDIS_STATUS_SUCCESS) ++ wnd->max_tx_packets = 1; ++ if (wnd->max_tx_packets > TX_RING_SIZE) ++ wnd->max_tx_packets = TX_RING_SIZE; ++ } ++ TRACE2("maximum send packets: %d", wnd->max_tx_packets); ++ NdisAllocatePacketPoolEx(&status, &wnd->tx_packet_pool, ++ wnd->max_tx_packets, 0, ++ PROTOCOL_RESERVED_SIZE_IN_PACKET); ++ if (status != NDIS_STATUS_SUCCESS) { ++ ERROR("couldn't allocate packet pool"); ++ goto packet_pool_err; ++ } ++ NdisAllocateBufferPool(&status, &wnd->tx_buffer_pool, ++ wnd->max_tx_packets + 4); ++ if (status != NDIS_STATUS_SUCCESS) { ++ ERROR("couldn't allocate buffer pool"); ++ goto buffer_pool_err; ++ } ++ TRACE1("pool: %p", wnd->tx_buffer_pool); ++ ++ if (mp_query_int(wnd, OID_GEN_MAXIMUM_TOTAL_SIZE, &n) == ++ NDIS_STATUS_SUCCESS && n > ETH_HLEN) ++ ndis_change_mtu(wnd->net_dev, n - ETH_HLEN); ++ ++ if (mp_query_int(wnd, OID_GEN_MAC_OPTIONS, &n) == NDIS_STATUS_SUCCESS) ++ TRACE2("mac options supported: 0x%x", n); ++ ++ tx_header_offset = (typeof(tx_header_offset))buf; ++ tx_header_offset->protocol_type = NDIS_PROTOCOL_ID_TCP_IP; ++ tx_header_offset->header_offset = sizeof(ETH_HLEN); ++ status = mp_set(wnd, OID_GEN_TRANSPORT_HEADER_OFFSET, ++ tx_header_offset, sizeof(*tx_header_offset)); ++ TRACE2("%08X", status); ++ ++ status = mp_query_int(wnd, OID_GEN_PHYSICAL_MEDIUM, ++ &wnd->physical_medium); ++ if (status != NDIS_STATUS_SUCCESS) ++ wnd->physical_medium = NdisPhysicalMediumUnspecified; ++ ++#ifdef CONFIG_WIRELESS_EXT ++ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { ++ mp_set_int(wnd, OID_802_11_POWER_MODE, NDIS_POWER_OFF); ++ get_encryption_capa(wnd, buf, buf_len); ++ TRACE1("capabilities = %ld", wnd->capa.encr); ++ printk(KERN_INFO "%s: encryption modes supported: " ++ "%s%s%s%s%s%s%s\n", net_dev->name, ++ test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr) ? ++ "WEP" : "none", ++ ++ test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr) ? ++ "; TKIP with WPA" : "", ++ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? ++ ", WPA2" : "", ++ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? ++ ", WPA2-PSK" : "", ++ ++ test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr) ? ++ "; AES/CCMP with WPA" : "", ++ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? ++ ", WPA2" : "", ++ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? ++ ", WPA2-PSK" : ""); ++ ++ set_default_iw_params(wnd); ++ } ++#endif ++ kfree(buf); ++ hangcheck_add(wnd); ++ add_iw_stats_timer(wnd); ++ EXIT1(return NDIS_STATUS_SUCCESS); ++ ++buffer_pool_err: ++ wnd->tx_buffer_pool = NULL; ++ if (wnd->tx_packet_pool) { ++ NdisFreePacketPool(wnd->tx_packet_pool); ++ wnd->tx_packet_pool = NULL; ++ } ++packet_pool_err: ++ unregister_netdev(net_dev); ++ wnd->max_tx_packets = 0; ++err_register: ++ kfree(buf); ++err_start: ++ mp_halt(wnd); ++ EXIT1(return NDIS_STATUS_FAILURE); ++} ++ ++static int ndis_remove_device(struct ndis_device *wnd) ++{ ++ s8 tx_pending; ++ int our_mutex; ++ ++ /* prevent setting essid during disassociation */ ++ memset(&wnd->essid, 0, sizeof(wnd->essid)); ++ wnd->tx_ok = 0; ++ netif_carrier_off(wnd->net_dev); ++ if (wnd->max_tx_packets) ++ unregister_netdev(wnd->net_dev); ++ /* if device is suspended, but resume failed, tx_ring_mutex ++ * may already be locked */ ++ our_mutex = mutex_trylock(&wnd->tx_ring_mutex); ++ if (!our_mutex) ++ WARNING("couldn't obtain tx_ring_mutex"); ++ spin_lock_bh(&wnd->tx_ring_lock); ++ tx_pending = wnd->tx_ring_end - wnd->tx_ring_start; ++ if (tx_pending < 0) ++ tx_pending += TX_RING_SIZE; ++ else if (tx_pending == 0 && wnd->is_tx_ring_full) ++ tx_pending = TX_RING_SIZE - 1; ++ wnd->is_tx_ring_full = 0; ++ /* throw away pending packets */ ++ while (tx_pending-- > 0) { ++ struct ndis_packet *packet; ++ ++ packet = wnd->tx_ring[wnd->tx_ring_start]; ++ free_tx_packet(wnd, packet, NDIS_STATUS_CLOSING); ++ wnd->tx_ring_start = (wnd->tx_ring_start + 1) % TX_RING_SIZE; ++ } ++ spin_unlock_bh(&wnd->tx_ring_lock); ++ if (our_mutex) ++ mutex_unlock(&wnd->tx_ring_mutex); ++ mp_halt(wnd); ++ ndis_exit_device(wnd); ++ ++ if (wnd->tx_packet_pool) { ++ NdisFreePacketPool(wnd->tx_packet_pool); ++ wnd->tx_packet_pool = NULL; ++ } ++ if (wnd->tx_buffer_pool) { ++ NdisFreeBufferPool(wnd->tx_buffer_pool); ++ wnd->tx_buffer_pool = NULL; ++ } ++ kfree(wnd->pmkids); ++ printk(KERN_INFO "%s: device %s removed\n", DRIVER_NAME, ++ wnd->net_dev->name); ++ kfree(wnd->nmb); ++ free_netdev(wnd->net_dev); ++ EXIT2(return 0); ++} ++ ++static NTSTATUS ndis_add_device(struct driver_object *drv_obj, ++ struct device_object *pdo) ++{ ++ struct device_object *fdo; ++ struct ndis_mp_block *nmb; ++ NTSTATUS status; ++ struct ndis_device *wnd; ++ struct net_device *net_dev; ++ struct wrap_device *wd; ++ unsigned long i; ++ ++ ENTER2("%p, %p", drv_obj, pdo); ++ if (strlen(if_name) >= IFNAMSIZ) { ++ ERROR("interface name '%s' is too long", if_name); ++ return STATUS_INVALID_PARAMETER; ++ } ++ net_dev = alloc_etherdev(sizeof(*wnd)); ++ if (!net_dev) { ++ ERROR("couldn't allocate device"); ++ return STATUS_RESOURCES; ++ } ++ wd = pdo->reserved; ++ if (wrap_is_pci_bus(wd->dev_bus)) ++ SET_NETDEV_DEV(net_dev, &wd->pci.pdev->dev); ++ if (wrap_is_usb_bus(wd->dev_bus)) ++ SET_NETDEV_DEV(net_dev, &wd->usb.intf->dev); ++ status = IoCreateDevice(drv_obj, 0, NULL, FILE_DEVICE_UNKNOWN, 0, ++ FALSE, &fdo); ++ if (status != STATUS_SUCCESS) { ++ free_netdev(net_dev); ++ EXIT2(return status); ++ } ++ wnd = netdev_priv(net_dev); ++ TRACE1("wnd: %p", wnd); ++ ++ nmb = kmalloc(sizeof(*nmb), GFP_KERNEL); ++ if (!nmb) { ++ WARNING("couldn't allocate memory"); ++ IoDeleteDevice(fdo); ++ free_netdev(net_dev); ++ return STATUS_RESOURCES; ++ } ++#if DEBUG >= 6 ++ /* poison nmb so if a driver accesses uninitialized pointers, we ++ * know what it is */ ++ for (i = 0; i < sizeof(*nmb) / sizeof(unsigned long); i++) ++ ((unsigned long *)nmb)[i] = i + 0x8a3fc1; ++#endif ++ ++ wnd->nmb = nmb; ++ nmb->wnd = wnd; ++ nmb->pdo = pdo; ++ wnd->wd = wd; ++ wnd->net_dev = net_dev; ++ fdo->reserved = wnd; ++ nmb->fdo = fdo; ++ if (ndis_init_device(wnd)) { ++ IoDeleteDevice(fdo); ++ kfree(nmb); ++ free_netdev(net_dev); ++ EXIT1(return STATUS_RESOURCES); ++ } ++ nmb->next_device = IoAttachDeviceToDeviceStack(fdo, pdo); ++ spin_lock_init(&wnd->tx_ring_lock); ++ mutex_init(&wnd->tx_ring_mutex); ++ mutex_init(&wnd->ndis_req_mutex); ++ wnd->ndis_req_done = 0; ++ INIT_WORK(&wnd->tx_work, tx_worker); ++ wnd->tx_ring_start = 0; ++ wnd->tx_ring_end = 0; ++ wnd->is_tx_ring_full = 0; ++ wnd->capa.encr = 0; ++ wnd->capa.auth = 0; ++ wnd->attributes = 0; ++ wnd->dma_map_count = 0; ++ wnd->dma_map_addr = NULL; ++ wnd->nick[0] = 0; ++ init_timer(&wnd->hangcheck_timer); ++ wnd->scan_timestamp = 0; ++ init_timer(&wnd->iw_stats_timer); ++ wnd->iw_stats_interval = 10 * HZ; ++ wnd->ndis_pending_work = 0; ++ memset(&wnd->essid, 0, sizeof(wnd->essid)); ++ memset(&wnd->encr_info, 0, sizeof(wnd->encr_info)); ++ wnd->infrastructure_mode = Ndis802_11Infrastructure; ++ INIT_WORK(&wnd->ndis_work, wrapndis_worker); ++ wnd->iw_stats_enabled = TRUE; ++ ++ TRACE1("nmb: %p, pdo: %p, fdo: %p, attached: %p, next: %p", ++ nmb, pdo, fdo, fdo->attached, nmb->next_device); ++ ++ /* dispatch routines are called as Windows functions */ ++ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) ++ drv_obj->major_func[i] = WIN_FUNC_PTR(IoPassIrpDown,2); ++ ++ drv_obj->major_func[IRP_MJ_PNP] = WIN_FUNC_PTR(NdisDispatchPnp,2); ++ drv_obj->major_func[IRP_MJ_POWER] = WIN_FUNC_PTR(NdisDispatchPower,2); ++ drv_obj->major_func[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ++ WIN_FUNC_PTR(NdisDispatchDeviceControl,2); ++// drv_obj->major_func[IRP_MJ_DEVICE_CONTROL] = ++// WIN_FUNC_PTR(NdisDispatchDeviceControl,2); ++ EXIT2(return STATUS_SUCCESS); ++} ++ ++int init_ndis_driver(struct driver_object *drv_obj) ++{ ++ ENTER1("%p", drv_obj); ++ drv_obj->drv_ext->add_device = ndis_add_device; ++ return 0; ++} ++ ++int wrapndis_init(void) ++{ ++ wrapndis_wq = create_singlethread_workqueue("wrapndis_wq"); ++ if (!wrapndis_wq) ++ EXIT1(return -ENOMEM); ++ TRACE1("wrapndis_wq: %p", wrapndis_wq); ++ register_netdevice_notifier(&netdev_notifier); ++ return 0; ++} ++ ++void wrapndis_exit(void) ++{ ++ unregister_netdevice_notifier(&netdev_notifier); ++ if (wrapndis_wq) ++ destroy_workqueue(wrapndis_wq); ++} +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.h +--- linux-3.12.2/3rdparty/ndiswrapper/wrapndis.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _WRAPNDIS_H_ ++#define _WRAPNDIS_H_ ++ ++#include "ndis.h" ++#include "pnp.h" ++ ++int wrapndis_init(void); ++void wrapndis_exit(void); ++ ++NDIS_STATUS mp_reset(struct ndis_device *wnd); ++ ++NDIS_STATUS mp_request(enum ndis_request_type request, ++ struct ndis_device *wnd, ndis_oid oid, ++ void *buf, ULONG buflen, ULONG *written, ULONG *needed); ++ ++static inline NDIS_STATUS mp_query_info(struct ndis_device *wnd, ++ ndis_oid oid, void *buf, ULONG buflen, ++ ULONG *written, ULONG *needed) ++{ ++ return mp_request(NdisRequestQueryInformation, wnd, oid, ++ buf, buflen, written, needed); ++} ++ ++static inline NDIS_STATUS mp_set_info(struct ndis_device *wnd, ++ ndis_oid oid, void *buf, ULONG buflen, ++ ULONG *written, ULONG *needed) ++{ ++ return mp_request(NdisRequestSetInformation, wnd, oid, ++ buf, buflen, written, needed); ++} ++ ++static inline NDIS_STATUS mp_query(struct ndis_device *wnd, ndis_oid oid, ++ void *buf, ULONG buflen) ++{ ++ return mp_request(NdisRequestQueryInformation, wnd, oid, ++ buf, buflen, NULL, NULL); ++} ++ ++static inline NDIS_STATUS mp_query_int(struct ndis_device *wnd, ++ ndis_oid oid, ULONG *data) ++{ ++ return mp_request(NdisRequestQueryInformation, wnd, oid, ++ data, sizeof(ULONG), NULL, NULL); ++} ++ ++static inline NDIS_STATUS mp_set(struct ndis_device *wnd, ndis_oid oid, ++ void *buf, ULONG buflen) ++{ ++ return mp_request(NdisRequestSetInformation, wnd, oid, ++ buf, buflen, NULL, NULL); ++} ++ ++static inline NDIS_STATUS mp_set_int(struct ndis_device *wnd, ++ ndis_oid oid, ULONG data) ++{ ++ return mp_request(NdisRequestSetInformation, wnd, oid, ++ &data, sizeof(ULONG), NULL, NULL); ++} ++ ++void free_tx_packet(struct ndis_device *wnd, struct ndis_packet *packet, ++ NDIS_STATUS status); ++int init_ndis_driver(struct driver_object *drv_obj); ++NDIS_STATUS ndis_reinit(struct ndis_device *wnd); ++void set_media_state(struct ndis_device *wnd, enum ndis_media_state state); ++ ++void hangcheck_add(struct ndis_device *wnd); ++void hangcheck_del(struct ndis_device *wnd); ++ ++struct iw_statistics *get_iw_stats(struct net_device *dev); ++ ++#endif +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapper.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.c +--- linux-3.12.2/3rdparty/ndiswrapper/wrapper.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.c 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,111 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#include "ndis.h" ++#include "iw_ndis.h" ++#include "loader.h" ++#include "pnp.h" ++#include "wrapper.h" ++ ++char *if_name = "wlan%d"; ++int proc_uid, proc_gid; ++int hangcheck_interval; ++static char *utils_version = UTILS_VERSION; ++int debug = DEBUG; ++ ++module_param(if_name, charp, 0400); ++MODULE_PARM_DESC(if_name, "Network interface name or template " ++ "(default: wlan%d)"); ++module_param(proc_uid, int, 0600); ++MODULE_PARM_DESC(proc_uid, "The uid of the files created in /proc " ++ "(default: 0)."); ++module_param(proc_gid, int, 0600); ++MODULE_PARM_DESC(proc_gid, "The gid of the files created in /proc " ++ "(default: 0)."); ++module_param(debug, int, 0600); ++MODULE_PARM_DESC(debug, "debug level"); ++ ++/* 0 - default value provided by NDIS driver, ++ * positive value - force hangcheck interval to that many seconds ++ * negative value - disable hangcheck ++ */ ++module_param(hangcheck_interval, int, 0600); ++MODULE_PARM_DESC(hangcheck_interval, "The interval, in seconds, for checking" ++ " if driver is hung. (default: 0)"); ++ ++module_param(utils_version, charp, 0400); ++MODULE_PARM_DESC(utils_version, "Compatible version of utils " ++ "(read only: " UTILS_VERSION ")"); ++ ++MODULE_AUTHOR("ndiswrapper team "); ++#ifdef MODULE_DESCRIPTION ++MODULE_DESCRIPTION("NDIS wrapper driver"); ++#endif ++#ifdef MODULE_VERSION ++MODULE_VERSION(DRIVER_VERSION); ++#endif ++MODULE_LICENSE("GPL"); ++ ++static void module_cleanup(void) ++{ ++ loader_exit(); ++ usb_exit(); ++ wrap_procfs_remove(); ++ wrapndis_exit(); ++ ndis_exit(); ++ ntoskernel_exit(); ++ wrapmem_exit(); ++} ++ ++static int __init wrapper_init(void) ++{ ++#ifdef TAINT_OOT_MODULE ++ add_taint(TAINT_OOT_MODULE, LOCKDEP_NOW_UNRELIABLE); ++#endif ++ printk(KERN_INFO "%s version %s loaded (smp=%s, preempt=%s)\n", ++ DRIVER_NAME, DRIVER_VERSION, ++#ifdef CONFIG_SMP ++ "yes" ++#else ++ "no" ++#endif ++ , ++#ifdef CONFIG_PREEMPT_RT ++ "rt" ++#elif defined(CONFIG_PREEMPT) ++ "yes" ++#else ++ "no" ++#endif ++ ); ++ ++ if (wrapmem_init() || ntoskernel_init() || ndis_init() || ++ wrapndis_init() || usb_init() || wrap_procfs_init() || ++ loader_init()) { ++ module_cleanup(); ++ ERROR("%s: initialization failed", DRIVER_NAME); ++ return -EINVAL; ++ } ++ EXIT1(return 0); ++} ++ ++static void __exit wrapper_exit(void) ++{ ++ ENTER1(""); ++ module_cleanup(); ++} ++ ++module_init(wrapper_init); ++module_exit(wrapper_exit); +diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapper.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.h +--- linux-3.12.2/3rdparty/ndiswrapper/wrapper.h 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.h 2013-11-28 21:42:11.000000000 +0200 +@@ -0,0 +1,24 @@ ++/* ++ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef WRAPPER_H ++#define WRAPPER_H ++ ++extern char *if_name; ++extern int proc_uid; ++extern int proc_gid; ++extern int hangcheck_interval; ++ ++#endif /* WRAPPER_H */ diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch new file mode 100644 index 00000000..52f39680 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch @@ -0,0 +1,32 @@ + +Adapt for strnicmp has been replaced by strncasecmp, +and removed upstream in v4.0-rc1~98: + +From af3cd13501eb04ca61d017ff4406f1cbffafdc04 Mon Sep 17 00:00:00 2001 +From: Rasmus Villemoes +Date: Thu, 12 Feb 2015 15:02:15 -0800 +Subject: lib/string.c: remove strnicmp() + + +Signed-off-by: Thomas Backlund + +--- linux/3rdparty/ndiswrapper/ndis.c.orig ++++ linux/3rdparty/ndiswrapper/ndis.c +@@ -438,7 +438,7 @@ static int read_setting(struct nt_list * + struct wrap_device_setting *setting; + mutex_lock(&loader_mutex); + nt_list_for_each_entry(setting, setting_list, list) { +- if (strnicmp(keyname, setting->name, length) == 0) { ++ if (strncasecmp(keyname, setting->name, length) == 0) { + TRACE2("setting %s='%s'", keyname, setting->value); + mutex_unlock(&loader_mutex); + *param = ndis_encode_setting(setting, type); +@@ -502,7 +502,7 @@ wstdcall void WIN_FUNC(NdisWriteConfigur + + mutex_lock(&loader_mutex); + nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) { +- if (strnicmp(keyname, setting->name, ansi.length) == 0) { ++ if (strncasecmp(keyname, setting->name, ansi.length) == 0) { + mutex_unlock(&loader_mutex); + if (ndis_decode_setting(setting, param)) + *status = NDIS_STATUS_FAILURE; diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch new file mode 100644 index 00000000..f7d26e62 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch @@ -0,0 +1,7 @@ +--- linux-2.6.24.orig/3rdparty/ndiswrapper/Kconfig 1969-12-31 21:00:00.000000000 -0300 ++++ linux-2.6.24/3rdparty/ndiswrapper/Kconfig 2007-11-26 15:11:31.000000000 -0200 +@@ -0,0 +1,4 @@ ++ ++config NDISWRAPPER ++ tristate "NDIS driver wrapper support" ++ depends on PCI && USB && !X86_64_XEN diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch new file mode 100644 index 00000000..b44f4b29 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch @@ -0,0 +1,45 @@ +--- linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile.orig 2013-11-28 21:42:10.000000000 +0200 ++++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile 2013-11-30 21:41:19.550504380 +0200 +@@ -9,24 +9,10 @@ DISTFILES = \ + winnt_types.h workqueue.c wrapmem.c wrapmem.h wrapndis.c wrapndis.h \ + wrapper.c wrapper.h + +-# By default, we try to compile the modules for the currently running +-# kernel. But it's the first approximation, as we will re-read the +-# version from the kernel sources. +-KVERS_UNAME ?= $(shell uname -r) +- + # KBUILD is the path to the Linux kernel build tree. It is usually the + # same as the kernel source tree, except when the kernel was compiled in + # a separate directory. +-KBUILD ?= $(shell readlink -f /lib/modules/$(KVERS_UNAME)/build) +- +-ifeq (,$(KBUILD)) +-$(error Kernel build tree not found - please set KBUILD to configured kernel) +-endif +- +-KCONFIG := $(KBUILD)/.config +-ifeq (,$(wildcard $(KCONFIG))) +-$(error No .config found in $(KBUILD), please set KBUILD to configured kernel) +-endif ++KBUILD ?= $(srctree) + + ifneq (,$(wildcard $(KBUILD)/include/linux/version.h)) + ifneq (,$(wildcard $(KBUILD)/include/generated/uapi/linux/version.h)) +@@ -43,16 +29,9 @@ endif + ifeq (,$(wildcard $(VERSION_H))) + VERSION_H := $(KBUILD)/include/linux/version.h + endif +-ifeq (,$(wildcard $(VERSION_H))) +-$(error Please run 'make modules_prepare' in $(KBUILD)) +-endif + + KVERS := $(shell sed -ne 's/"//g;s/^\#define UTS_RELEASE //p' $(VERSION_H)) + +-ifeq (,$(KVERS)) +-$(error Cannot find UTS_RELEASE in $(VERSION_H), please report) +-endif +- + INST_DIR = /lib/modules/$(KVERS)/misc + + SRC_DIR=$(shell pwd) diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch new file mode 100644 index 00000000..6550b9f2 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch @@ -0,0 +1,33 @@ + +Fix build with kernel-3.14 + +Signed-off-by: Thomas Backlund + + 3rdparty/ndiswrapper/crt.c | 2 +- + 3rdparty/ndiswrapper/ntoskernel.h | 3 +-- + 2 files changed, 2 insertions(+), 3 deletions(-) + +--- linux/3rdparty/ndiswrapper/crt.c.orig 2014-04-26 19:10:55.000000000 +0300 ++++ linux/3rdparty/ndiswrapper/crt.c 2014-04-26 19:25:27.766116082 +0300 +@@ -467,7 +467,7 @@ noregparm int WIN_FUNC(_win_memcmp,3) + noregparm void WIN_FUNC(_win_srand,1) + (UINT seed) + { +- net_srandom(seed); ++ prandom_seed((__force u32) seed); + } + + noregparm int WIN_FUNC(rand,0) +--- linux/3rdparty/ndiswrapper/ntoskernel.h.orig 2014-04-26 19:42:32.000000000 +0300 ++++ linux/3rdparty/ndiswrapper/ntoskernel.h 2014-04-26 19:59:13.662522942 +0300 +@@ -798,9 +798,8 @@ do { \ + #define nt_spin_unlock_irqrestore(lock, flags) \ + do { \ + nt_spin_unlock(lock); \ +- preempt_enable_no_resched(); \ ++ preempt_enable(); \ + local_irq_restore(flags); \ +- preempt_check_resched(); \ + } while (0) + + static inline ULONG SPAN_PAGES(void *ptr, SIZE_T length) diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch new file mode 100644 index 00000000..e87bf7e0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch @@ -0,0 +1,978 @@ + 3rdparty/rfswitch/FILES | 6 + 3rdparty/rfswitch/ISSUES | 28 +++ + 3rdparty/rfswitch/Kconfig | 22 ++ + 3rdparty/rfswitch/LICENSE | 339 +++++++++++++++++++++++++++++++++++++++++++++ + 3rdparty/rfswitch/Makefile | 119 +++++++++++++++ + 3rdparty/rfswitch/README | 44 +++++ + 3rdparty/rfswitch/av5100.c | 174 +++++++++++++++++++++++ + 3rdparty/rfswitch/pbe5.c | 205 +++++++++++++++++++++++++++ + 8 files changed, 937 insertions(+) +diff -Nurp linux-2.6.37/3rdparty/rfswitch/av5100.c linux-2.6.37.3rdparty/3rdparty/rfswitch/av5100.c +--- linux-2.6.37/3rdparty/rfswitch/av5100.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/av5100.c 2008-03-28 05:25:28.000000000 +0200 +@@ -0,0 +1,174 @@ ++/******************************************************************************* ++ ++ Copyright(c) 2003 - 2004 Intel 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., 59 ++ Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ ++ The full GNU General Public License is included in this distribution in the ++ file called LICENSE. ++ ++ Contact Information: ++ James P. Ketrenos ++ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ++ ++*******************************************************************************/ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define DRV_NAME "av5100" ++#define DRV_VERSION "1.3" ++#define DRV_DESCRIPTION "SW RF kill switch for Averatec 5100P" ++#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation" ++ ++static int radio = 1; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ ++MODULE_PARM(radio, "i"); ++ ++#else /* LINUX_VERSION_CODE < 2.6.0 */ ++ ++#include ++module_param(radio, int, 1); ++ ++#endif /* LINUX_VERSION_CODE < 2.6.0 */ ++ ++MODULE_PARM_DESC(radio, "controls state of radio (1=on, 0=off)"); ++ ++MODULE_DESCRIPTION(DRV_DESCRIPTION); ++MODULE_AUTHOR(DRV_COPYRIGHT); ++MODULE_LICENSE("GPL"); ++ ++#define AV5100_RADIO_ON (0xe0) ++#define AV5100_RADIO_OFF (0xe1) ++ ++static int av5100_radio = AV5100_RADIO_OFF; ++ ++static void av5100_set_radio(int state) ++{ ++ printk(KERN_INFO DRV_NAME ": Radio being turned %s\n", ++ (state == AV5100_RADIO_ON) ? "ON" : "OFF"); ++ outl(0x80020800, 0xcf8); ++ outb(0x6f, 0x0072); ++ outl(0x1800ffff, 0x1184); ++ outb(state, 0x00b2); ++ av5100_radio = state; ++} ++ ++ ++/* ++ * proc stuff ++ */ ++static struct proc_dir_entry *dir_base = NULL; ++ ++static int proc_set_radio(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ av5100_set_radio(buffer[0] == '0' ? AV5100_RADIO_OFF : AV5100_RADIO_ON); ++ ++ return count; ++} ++ ++static int proc_get_radio(char *page, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += snprintf(page, count, DRV_NAME ": %d\n", ++ av5100_radio == AV5100_RADIO_OFF ? 0 : 1); ++ ++ *eof = 1; ++ return len; ++} ++ ++ ++static void av5100_proc_cleanup(void) ++{ ++ if (dir_base) { ++ remove_proc_entry("radio", dir_base); ++ remove_proc_entry(DRV_NAME, &proc_root); ++ dir_base = NULL; ++ } ++} ++ ++ ++static int av5100_proc_init(void) ++{ ++ struct proc_dir_entry *ent; ++ int err = 0; ++ ++ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); ++ if (dir_base == NULL) { ++ printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" ++ DRV_NAME "\n"); ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ ++ ent = create_proc_entry("radio", S_IFREG | S_IRUGO | S_IWUSR, ++ dir_base); ++ if (ent) { ++ ent->read_proc = proc_get_radio; ++ ent->write_proc = proc_set_radio; ++ } else { ++ printk(KERN_ERR ++ "Unable to initialize /proc/" DRV_NAME "/radio\n"); ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ return 0; ++ ++ fail: ++ av5100_proc_cleanup(); ++ return err; ++} ++ ++/* ++ * module stuff ++ */ ++static int __init av5100_init(void) ++{ ++ av5100_proc_init(); ++ ++ av5100_set_radio((radio == 1) ? AV5100_RADIO_ON : AV5100_RADIO_OFF); ++ ++ return 0; ++} ++ ++static void __exit av5100_exit(void) ++{ ++ av5100_set_radio(AV5100_RADIO_OFF); ++ ++ av5100_proc_cleanup(); ++} ++ ++module_init(av5100_init); ++module_exit(av5100_exit); ++ ++/* ++ 1 2 3 4 5 6 7 ++12345678901234567890123456789012345678901234567890123456789012345678901234567890 ++*/ +diff -Nurp linux-2.6.37/3rdparty/rfswitch/FILES linux-2.6.37.3rdparty/3rdparty/rfswitch/FILES +--- linux-2.6.37/3rdparty/rfswitch/FILES 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/FILES 2004-07-09 07:44:19.000000000 +0300 +@@ -0,0 +1,6 @@ ++Makefile Used for building on 2.4, 2.6, internally and exteranlly ++ ++FILES This file ++ ++av5100.c Averatec 5100P SW Switch Module ++pbe5.c Packard Bell Easynote SW Switch Module +diff -Nurp linux-2.6.37/3rdparty/rfswitch/ISSUES linux-2.6.37.3rdparty/3rdparty/rfswitch/ISSUES +--- linux-2.6.37/3rdparty/rfswitch/ISSUES 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/ISSUES 2004-07-09 07:43:22.000000000 +0300 +@@ -0,0 +1,28 @@ ++ ++ISSUES ++------------ ----- ----- ---- --- -- - ++ ++No packets! - RF kill switch ++ ++If the module loads, but no packets are transferred you may have a SW based ++radio kill switch. All laptops have some capability to disable the radio ++via a button or switch. On some laptops that switch is physically tied to the ++IPW2100; simply toggling the switch should enable the radio. ++ ++On other laptops, the switch is a button that when pressed requires some ++software driver to send some hardware command to some other piece of hardware ++on the laptop, that then controls the radio. The driver currently has support ++for the Averatec 5100P laptop's SW switch. Support will be added for other ++laptop vendors as we become aware of them and figure out how to enable the ++radio. ++ ++To know if the radio is being disabled via the RF switch, perform the following: ++ ++% cat /proc/net/ipw2100/eth1/state ++Radio is disabled by RF switch ++ ++If it says that, then your RF switch is currently disabling the radio. The ++driver doesn't currently support switching back to the on state if you have a ++physical RF switch (the radio may turn on and packets will work, but the proc ++entry won't be updated) ++ +diff -Nurp linux-2.6.37/3rdparty/rfswitch/Kconfig linux-2.6.37.3rdparty/3rdparty/rfswitch/Kconfig +--- linux-2.6.37/3rdparty/rfswitch/Kconfig 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/Kconfig 2008-05-27 23:39:10.000000000 +0300 +@@ -0,0 +1,22 @@ ++menu "RF Switch" ++ depends on EXPERIMENTAL ++ ++config RFSWITCH ++ tristate "RF Switch" ++ depends on EXPERIMENTAL ++ ++config AVERATEC_5100P ++ tristate "averatec 5100P driver" ++ depends on RFSWITCH ++ ---help--- ++ This is an experimental driver for the wireless switch ++ of some laptops (usefull with some ipw2x00 cards) ++ ++config PACKARDBELL_E5 ++ tristate "pbe5 driver" ++ depends on RFSWITCH ++ ---help--- ++ This is an experimental driver for the wireless switch ++ of some laptops (usefull with some ipw2x00 cards) ++ ++endmenu +diff -Nurp linux-2.6.37/3rdparty/rfswitch/LICENSE linux-2.6.37.3rdparty/3rdparty/rfswitch/LICENSE +--- linux-2.6.37/3rdparty/rfswitch/LICENSE 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/LICENSE 2004-07-09 07:43:22.000000000 +0300 +@@ -0,0 +1,339 @@ ++ ++"This software program is licensed subject to the GNU General Public License ++(GPL). Version 2, June 1991, available at ++" ++ ++GNU General Public License ++ ++Version 2, June 1991 ++ ++Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ++ ++Everyone is permitted to copy and distribute verbatim copies of this license ++document, but changing it is not allowed. ++ ++Preamble ++ ++The licenses for most software are designed to take away your freedom to ++share and change it. By contrast, the GNU General Public License is intended ++to guarantee your freedom to share and change free software--to make sure ++the software is free for all its users. This General Public License applies ++to most of the Free Software Foundation's software and to any other program ++whose authors commit to using it. (Some other Free Software Foundation ++software is covered by the GNU Library General Public License instead.) You ++can apply it to your programs, too. ++ ++When we speak of free software, we are referring to freedom, not price. Our ++General Public Licenses are designed to make sure that you have the freedom ++to distribute copies of free software (and charge for this service if you ++wish), that you receive source code or can get it if you want it, that you ++can change the software or use pieces of it in new free programs; and that ++you know you can do these things. ++ ++To protect your rights, we need to make restrictions that forbid anyone to ++deny you these rights or to ask you to surrender the rights. These ++restrictions translate to certain responsibilities for you if you distribute ++copies of the software, or if you modify it. ++ ++For example, if you distribute copies of such a program, whether gratis or ++for a fee, you must give the recipients all the rights that you have. You ++must make sure that they, too, receive or can get the source code. And you ++must show them these terms so they know their rights. ++ ++We protect your rights with two steps: (1) copyright the software, and (2) ++offer you this license which gives you legal permission to copy, distribute ++and/or modify the software. ++ ++Also, for each author's protection and ours, we want to make certain that ++everyone understands that there is no warranty for this free software. If ++the software is modified by someone else and passed on, we want its ++recipients to know that what they have is not the original, so that any ++problems introduced by others will not reflect on the original authors' ++reputations. ++ ++Finally, any free program is threatened constantly by software patents. We ++wish to avoid the danger that redistributors of a free program will ++individually obtain patent licenses, in effect making the program ++proprietary. To prevent this, we have made it clear that any patent must be ++licensed for everyone's free use or not licensed at all. ++ ++The precise terms and conditions for copying, distribution and modification ++follow. ++ ++TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++0. This License applies to any program or other work which contains a notice ++ placed by the copyright holder saying it may be distributed under the ++ terms of this General Public License. The "Program", below, refers to any ++ such program or work, and a "work based on the Program" means either the ++ Program or any derivative work under copyright law: that is to say, a ++ work containing the Program or a portion of it, either verbatim or with ++ modifications and/or translated into another language. (Hereinafter, ++ translation is included without limitation in the term "modification".) ++ Each licensee is addressed as "you". ++ ++ Activities other than copying, distribution and modification are not ++ covered by this License; they are outside its scope. The act of running ++ the Program is not restricted, and the output from the Program is covered ++ only if its contents constitute a work based on the Program (independent ++ of having been made by running the Program). Whether that is true depends ++ on what the Program does. ++ ++1. You may copy and distribute verbatim copies of the Program's source code ++ as you receive it, in any medium, provided that you conspicuously and ++ appropriately publish on each copy an appropriate copyright notice and ++ disclaimer of warranty; keep intact all the notices that refer to this ++ License and to the absence of any warranty; and give any other recipients ++ of the Program a copy of this License along with the Program. ++ ++ You may charge a fee for the physical act of transferring a copy, and you ++ may at your option offer warranty protection in exchange for a fee. ++ ++2. You may modify your copy or copies of the Program or any portion of it, ++ thus forming a work based on the Program, and copy and distribute such ++ modifications or work under the terms of Section 1 above, provided that ++ you also meet all of these conditions: ++ ++ * a) You must cause the modified files to carry prominent notices stating ++ that you changed the files and the date of any change. ++ ++ * b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any part ++ thereof, to be licensed as a whole at no charge to all third parties ++ under the terms of this License. ++ ++ * c) If the modified program normally reads commands interactively when ++ run, you must cause it, when started running for such interactive ++ use in the most ordinary way, to print or display an announcement ++ including an appropriate copyright notice and a notice that there is ++ no warranty (or else, saying that you provide a warranty) and that ++ users may redistribute the program under these conditions, and ++ telling the user how to view a copy of this License. (Exception: if ++ the Program itself is interactive but does not normally print such ++ an announcement, your work based on the Program is not required to ++ print an announcement.) ++ ++ These requirements apply to the modified work as a whole. If identifiable ++ sections of that work are not derived from the Program, and can be ++ reasonably considered independent and separate works in themselves, then ++ this License, and its terms, do not apply to those sections when you ++ distribute them as separate works. But when you distribute the same ++ sections as part of a whole which is a work based on the Program, the ++ distribution of the whole must be on the terms of this License, whose ++ permissions for other licensees extend to the entire whole, and thus to ++ each and every part regardless of who wrote it. ++ ++ Thus, it is not the intent of this section to claim rights or contest ++ your rights to work written entirely by you; rather, the intent is to ++ exercise the right to control the distribution of derivative or ++ collective works based on the Program. ++ ++ In addition, mere aggregation of another work not based on the Program ++ with the Program (or with a work based on the Program) on a volume of a ++ storage or distribution medium does not bring the other work under the ++ scope of this License. ++ ++3. You may copy and distribute the Program (or a work based on it, under ++ Section 2) in object code or executable form under the terms of Sections ++ 1 and 2 above provided that you also do one of the following: ++ ++ * a) Accompany it with the complete corresponding machine-readable source ++ code, which must be distributed under the terms of Sections 1 and 2 ++ above on a medium customarily used for software interchange; or, ++ ++ * b) Accompany it with a written offer, valid for at least three years, ++ to give any third party, for a charge no more than your cost of ++ physically performing source distribution, a complete machine- ++ readable copy of the corresponding source code, to be distributed ++ under the terms of Sections 1 and 2 above on a medium customarily ++ used for software interchange; or, ++ ++ * c) Accompany it with the information you received as to the offer to ++ distribute corresponding source code. (This alternative is allowed ++ only for noncommercial distribution and only if you received the ++ program in object code or executable form with such an offer, in ++ accord with Subsection b above.) ++ ++ The source code for a work means the preferred form of the work for ++ making modifications to it. For an executable work, complete source code ++ means all the source code for all modules it contains, plus any ++ associated interface definition files, plus the scripts used to control ++ compilation and installation of the executable. However, as a special ++ exception, the source code distributed need not include anything that is ++ normally distributed (in either source or binary form) with the major ++ components (compiler, kernel, and so on) of the operating system on which ++ the executable runs, unless that component itself accompanies the ++ executable. ++ ++ If distribution of executable or object code is made by offering access ++ to copy from a designated place, then offering equivalent access to copy ++ the source code from the same place counts as distribution of the source ++ code, even though third parties are not compelled to copy the source ++ along with the object code. ++ ++4. You may not copy, modify, sublicense, or distribute the Program except as ++ expressly provided under this License. Any attempt otherwise to copy, ++ modify, sublicense or distribute the Program is void, and will ++ automatically terminate your rights under this License. However, parties ++ who have received copies, or rights, from you under this License will not ++ have their licenses terminated so long as such parties remain in full ++ compliance. ++ ++5. You are not required to accept this License, since you have not signed ++ it. However, nothing else grants you permission to modify or distribute ++ the Program or its derivative works. These actions are prohibited by law ++ if you do not accept this License. Therefore, by modifying or ++ distributing the Program (or any work based on the Program), you ++ indicate your acceptance of this License to do so, and all its terms and ++ conditions for copying, distributing or modifying the Program or works ++ based on it. ++ ++6. Each time you redistribute the Program (or any work based on the ++ Program), the recipient automatically receives a license from the ++ original licensor to copy, distribute or modify the Program subject to ++ these terms and conditions. You may not impose any further restrictions ++ on the recipients' exercise of the rights granted herein. You are not ++ responsible for enforcing compliance by third parties to this License. ++ ++7. If, as a consequence of a court judgment or allegation of patent ++ infringement or for any other reason (not limited to patent issues), ++ conditions are imposed on you (whether by court order, agreement or ++ otherwise) that contradict the conditions of this License, they do not ++ excuse you from the conditions of this License. If you cannot distribute ++ so as to satisfy simultaneously your obligations under this License and ++ any other pertinent obligations, then as a consequence you may not ++ distribute the Program at all. For example, if a patent license would ++ not permit royalty-free redistribution of the Program by all those who ++ receive copies directly or indirectly through you, then the only way you ++ could satisfy both it and this License would be to refrain entirely from ++ distribution of the Program. ++ ++ If any portion of this section is held invalid or unenforceable under any ++ particular circumstance, the balance of the section is intended to apply ++ and the section as a whole is intended to apply in other circumstances. ++ ++ It is not the purpose of this section to induce you to infringe any ++ patents or other property right claims or to contest validity of any ++ such claims; this section has the sole purpose of protecting the ++ integrity of the free software distribution system, which is implemented ++ by public license practices. Many people have made generous contributions ++ to the wide range of software distributed through that system in ++ reliance on consistent application of that system; it is up to the ++ author/donor to decide if he or she is willing to distribute software ++ through any other system and a licensee cannot impose that choice. ++ ++ This section is intended to make thoroughly clear what is believed to be ++ a consequence of the rest of this License. ++ ++8. If the distribution and/or use of the Program is restricted in certain ++ countries either by patents or by copyrighted interfaces, the original ++ copyright holder who places the Program under this License may add an ++ explicit geographical distribution limitation excluding those countries, ++ so that distribution is permitted only in or among countries not thus ++ excluded. In such case, this License incorporates the limitation as if ++ written in the body of this License. ++ ++9. The Free Software Foundation may publish revised and/or new versions of ++ the General Public License from time to time. Such new versions will be ++ similar in spirit to the present version, but may differ in detail to ++ address new problems or concerns. ++ ++ Each version is given a distinguishing version number. If the Program ++ specifies a version number of this License which applies to it and "any ++ later version", you have the option of following the terms and ++ conditions either of that version or of any later version published by ++ the Free Software Foundation. If the Program does not specify a version ++ number of this License, you may choose any version ever published by the ++ Free Software Foundation. ++ ++10. If you wish to incorporate parts of the Program into other free programs ++ whose distribution conditions are different, write to the author to ask ++ for permission. For software which is copyrighted by the Free Software ++ Foundation, write to the Free Software Foundation; we sometimes make ++ exceptions for this. Our decision will be guided by the two goals of ++ preserving the free status of all derivatives of our free software and ++ of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER ++ EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ++ ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH ++ YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL ++ NECESSARY SERVICING, REPAIR OR CORRECTION. ++ ++12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR ++ DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL ++ DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM ++ (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED ++ INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF ++ THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR ++ OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ++ ++END OF TERMS AND CONDITIONS ++ ++How to Apply These Terms to Your New Programs ++ ++If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it free ++software which everyone can redistribute and change under these terms. ++ ++To do so, attach the following notices to the program. It is safest to ++attach them to the start of each source file to most effectively convey the ++exclusion of warranty; and each file should have at least the "copyright" ++line and a pointer to where the full notice is found. ++ ++one line to give the program's name and an idea of what it does. ++Copyright (C) yyyy name of author ++ ++This program is free software; you can redistribute it and/or modify it ++under the terms of the GNU General Public License as published by the Free ++Software Foundation; either version 2 of the License, or (at your option) ++any later version. ++ ++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., 59 ++Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this when ++it starts in an interactive mode: ++ ++Gnomovision version 69, Copyright (C) year name of author Gnomovision comes ++with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free ++software, and you are welcome to redistribute it under certain conditions; ++type 'show c' for details. ++ ++The hypothetical commands 'show w' and 'show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may be ++called something other than 'show w' and 'show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++'Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++signature of Ty Coon, 1 April 1989 ++Ty Coon, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Library General Public ++License instead of this License. +diff -Nurp linux-2.6.37/3rdparty/rfswitch/Makefile linux-2.6.37.3rdparty/3rdparty/rfswitch/Makefile +--- linux-2.6.37/3rdparty/rfswitch/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/Makefile 2008-03-28 05:17:16.000000000 +0200 +@@ -0,0 +1,119 @@ ++# ++# Makefile for the SW RF Switch kernel modules ++# ++# NOTE: This make file can serve as both an external Makefile (launched ++# directly by the user), or as the sub-dir Makefile used by the kernel ++# build system. ++ ++ ++CONFIG_AVERATEC_5100P=m ++CONFIG_PACKARDBELL_E5=m ++ ++ ++ ++list-m := ++list-$(CONFIG_AVERATEC_5100P) += av5100 ++list-$(CONFIG_PACKARDBELL_E5) += pbe5 ++ ++ ++obj-$(CONFIG_AVERATEC_5100P) += av5100.o ++obj-$(CONFIG_PACKARDBELL_E5) += pbe5.o ++ ++# ++# Begin dual Makefile mode here. First we provide support for when we ++# are being invoked by the kernel build system ++# ++ifneq ($(KERNELRELEASE),) ++ ++ifneq ($(PATCHLEVEL),6) # If we are not on a 2.6, then do 2.4 specific things ++ ++O_TARGET := rfswitch.o ++ ++include $(TOPDIR)/Rules.make ++ ++endif # End if 2.4 specific settings ++ ++else ++# Here we begin the portion that is executed if the user invoked this Makefile ++# directly. ++ ++# KSRC should be set to the path to your sources ++# modules are installed into KMISC ++KVER := $(shell uname -r) ++KSRC := /lib/modules/$(KVER)/build ++KMISC := /lib/modules/$(KVER)/kernel/drivers/net/wireless/ ++ ++# KSRC_OUTPUT should be overridden if you are using a 2.6 kernel that ++# has it's output sent elsewhere via KBUILD_OUTPUT= or O= ++KSRC_OUTPUT := $(KSRC) ++ ++# If we find Rules.make, we can assume we're using the old 2.4 style building ++OLDMAKE=$(wildcard $(KSRC)/Rules.make) ++PWD=$(shell pwd) ++ ++VERFILE := $(KSRC_OUTPUT)/include/linux/version.h ++KERNELRELEASE := $(shell \ ++ if [ -r $(VERFILE) ]; then \ ++ (cat $(VERFILE); echo UTS_RELEASE) | \ ++ $(CC) -I$(KSRC_OUTPUT) $(CFLAGS) -E - | \ ++ tail -n 1 | \ ++ xargs echo; \ ++ else \ ++ uname -r; \ ++ fi) ++ ++MODPATH := $(DESTDIR)/lib/modules/$(KERNELRELEASE) ++ ++all: modules ++ ++clean: ++ rm -f *.mod.c *.mod *.o *.ko .*.cmd .*.flags Modules.symvers ++ rm -rf $(PWD)/tmp ++ ++ ++ifeq ($(OLDMAKE),) ++ ++TMP=$(PWD)/tmp ++MODVERDIR=$(TMP)/.tmp_versions ++ ++modules: ++ifdef ($(KSRC_OUTPUT)/.tmp_versions) ++ mkdir -p $(MODVERDIR) ++ -cp $(KSRC_OUTPUT)/.tmp_versions/*.mod $(MODVERDIR) ++endif ++ifeq ($(KSRC),$(KSRC_OUTPUT)) # We're not outputting elsewhere ++ifdef ($(KSRC)/.tmp_versions) ++ -cp $(KSRC)/.tmp_versions/*.mod $(MODVERDIR) ++endif ++ $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) MODVERDIR=$(MODVERDIR) modules ++else # We've got a kernel with seperate output, copy the config, and use O= ++ mkdir -p $(TMP) ++ cp $(KSRC_OUTPUT)/.config $(TMP) ++ $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) MODVERDIR=$(MODVERDIR) O=$(PWD)/tmp modules ++endif ++ ++install: modules ++ install -d $(KMISC) ++ install -m 644 -c $(addsuffix .ko,$(list-m)) $(KMISC) ++ /sbin/depmod -a ++ ++ ++else # We're on 2.4, and things are slightly different ++ ++modules: ++ $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) BUILD_DIR=$(PWD) modules ++ ++install: modules ++ install -d $(KMISC) ++ install -m 644 -c $(addsuffix .o,$(list-m)) $(KMISC) ++ /sbin/depmod -a ++ ++endif ++ ++uninstall: ++ cd $(KMISC) ++ rm -rf $(addsuffix .ko,$(list-m)) ++ cd - ++ /sbin/depmod -a ++ ++endif +diff -Nurp linux-2.6.37/3rdparty/rfswitch/pbe5.c linux-2.6.37.3rdparty/3rdparty/rfswitch/pbe5.c +--- linux-2.6.37/3rdparty/rfswitch/pbe5.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/pbe5.c 2008-03-28 05:25:16.000000000 +0200 +@@ -0,0 +1,205 @@ ++/******************************************************************************* ++ ++ 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., 59 ++ Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ ++ The full GNU General Public License is included in this distribution in the ++ file called LICENSE. ++ ++ Author: ++ Pedro Ramalhais ++ ++ Based on: ++ av5100.c from http://ipw2100.sourceforge.net/ ++ ++*******************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRV_NAME "pbe5" ++#define DRV_VERSION "1.3" ++#define DRV_DESCRIPTION "SW RF kill switch for Packard Bell EasyNote E5" ++#define DRV_AUTHOR "Pedro Ramalhais" ++#define DRV_LICENSE "GPL" ++ ++static int radio = 1; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ ++MODULE_PARM(radio, "i"); ++ ++#else /* LINUX_VERSION_CODE < 2.6.0 */ ++ ++#include ++module_param(radio, int, 1); ++ ++#endif /* LINUX_VERSION_CODE < 2.6.0 */ ++ ++MODULE_PARM_DESC(radio, "controls state of radio (1=on, 0=off)"); ++ ++MODULE_DESCRIPTION(DRV_DESCRIPTION); ++MODULE_AUTHOR(DRV_AUTHOR); ++MODULE_LICENSE(DRV_LICENSE); ++ ++/* ++ * NOTE: These values were obtained from disassembling the Icon.exe program ++ * installed in the Packard Bell EasyNote E5 laptop. The names were guessed, ++ * so don't rely on them. ++ */ ++#define PBE5_PORT_TOGGLE 0x0b3 ++#define PBE5_VALUE_TOGGLE_ON 0x01 ++#define PBE5_VALUE_TOGGLE_OFF 0x00 ++#define PBE5_PORT_APPLY 0x0b2 ++#define PBE5_VALUE_APPLY 0xef ++ ++// Some "booleans" =;-) ++#define PBE5_RADIO_OFF 0 ++#define PBE5_RADIO_ON 1 ++ ++static int pbe5_radio_status = PBE5_RADIO_ON; ++ ++unsigned char pbe5_get_radio(void) ++{ ++ unsigned char val = 0x00; ++ ++ val = inb(PBE5_PORT_TOGGLE); ++ ++ return val; ++} ++ ++static void pbe5_set_radio(int state_set) ++{ ++ pbe5_radio_status = pbe5_get_radio(); ++ ++ if (pbe5_radio_status != state_set) { ++ // Set the radio toggle register ++ outb(PBE5_VALUE_TOGGLE_ON, PBE5_PORT_TOGGLE); ++ // Commit the radio toggle register value ++ outb(PBE5_VALUE_APPLY, PBE5_PORT_APPLY); ++ // Update the radio status ++ pbe5_radio_status = pbe5_get_radio(); ++ ++ printk(KERN_INFO DRV_NAME ": Radio turned %s\n", ++ (state_set == PBE5_RADIO_ON) ? "ON" : "OFF"); ++ } else { ++ printk(KERN_INFO DRV_NAME ": Radio already %s\n", ++ (state_set == PBE5_RADIO_ON) ? "ON" : "OFF"); ++ } ++} ++ ++ ++/* ++ * proc stuff ++ */ ++static struct proc_dir_entry *dir_base = NULL; ++ ++static int proc_set_radio(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ pbe5_set_radio(buffer[0] == '0' ? PBE5_RADIO_OFF : PBE5_RADIO_ON); ++ ++ return count; ++} ++ ++static int proc_get_radio(char *page, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += snprintf(page, count, DRV_NAME ": %d\n", ++ pbe5_radio_status == PBE5_RADIO_OFF ? 0 : 1); ++ ++ *eof = 1; ++ return len; ++} ++ ++ ++static void pbe5_proc_cleanup(void) ++{ ++ if (dir_base) { ++ remove_proc_entry("radio", dir_base); ++ remove_proc_entry(DRV_NAME, &proc_root); ++ dir_base = NULL; ++ } ++} ++ ++ ++static int pbe5_proc_init(void) ++{ ++ struct proc_dir_entry *ent; ++ int err = 0; ++ ++ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); ++ if (dir_base == NULL) { ++ printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" ++ DRV_NAME "\n"); ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ ++ ent = create_proc_entry("radio", S_IFREG | S_IRUGO | S_IWUSR, ++ dir_base); ++ if (ent) { ++ ent->read_proc = proc_get_radio; ++ ent->write_proc = proc_set_radio; ++ } else { ++ printk(KERN_ERR ++ "Unable to initialize /proc/" DRV_NAME "/radio\n"); ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ return 0; ++ ++ fail: ++ pbe5_proc_cleanup(); ++ return err; ++} ++ ++/* ++ * module stuff ++ */ ++static int __init pbe5_init(void) ++{ ++ pbe5_proc_init(); ++ ++ pbe5_set_radio((radio == 1) ? PBE5_RADIO_ON : PBE5_RADIO_OFF); ++ ++ return 0; ++} ++ ++static void __exit pbe5_exit(void) ++{ ++ pbe5_set_radio(PBE5_RADIO_OFF); ++ ++ pbe5_proc_cleanup(); ++} ++ ++module_init(pbe5_init); ++module_exit(pbe5_exit); ++ ++/* ++ 1 2 3 4 5 6 7 ++12345678901234567890123456789012345678901234567890123456789012345678901234567890 ++*/ +diff -Nurp linux-2.6.37/3rdparty/rfswitch/README linux-2.6.37.3rdparty/3rdparty/rfswitch/README +--- linux-2.6.37/3rdparty/rfswitch/README 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/rfswitch/README 2004-07-09 17:26:20.000000000 +0300 +@@ -0,0 +1,44 @@ ++Radio Kill Switch ++------------ ----- ----- ---- --- -- - ++Most laptops provide the ability for the user to physically disable the radio. ++Some vendors have implemented this as a physical switch that requires no ++software to turn the radio off and on. On other laptops, however, the switch ++is controlled through a button being pressed and a software driver then making ++calls to turn the radio off and on. This is referred to as a "software based ++RF kill switch" ++ ++Currently this project provides modules for controlling the software RF kill ++switch on the Averatec 5100P and Packard Bell EasyNote E5. The code may work ++on other laptops, but these are the only models on which it has been tested. ++ ++To determine if you have a system that might be compatible with one of the ++provided SW RF Kill switch modules, you can run: ++ ++ ++ To check for the Packard Bell (to use module pbe) -- ++ ++ % dd if=/dev/mem bs=1 skip=983040 count=65535 2>/dev/null | strings | egrep "NEW-PC|Insyde Software MobilePRO BIOS" ++ ++ To check for the Averatec (to use module av5100) -- ++ ++ % dd if=/dev/mem bs=1 skip=983040 count=65535 2>/dev/null | strings | egrep "AVERATEC" ++ ++If you have one of those laptop models you can imply loading the av5100/pbe5 ++module and the radio will be toggled on and off. In addition, you can turn ++the driver on and off by writing either a 1 or 0 to /proc/av5100/radio or ++/proc/pbe5/radio. If you automatically load the av5100/pbe5 module when your ++system boots, you may wish to use the radio module parameter to control the ++state of the radio upon loading: ++ ++ modprobe av5100 radio=0 ++ modprobe pbe5 radio=0 ++ ++results in the module loading with the radio turned off. You can then turn the ++radio on by: ++ ++ echo 1 > /proc/av5100/radio ++ echo 1 > /proc/pbe5/radio ++ ++If you have a SW RF kill switch and can not use one of the above modules, ++please join us on IRC (irc.freenode.net) on channel #ipw2100 and someone may ++be able to help. diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch new file mode 100644 index 00000000..7bfc2c64 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch @@ -0,0 +1,19 @@ + +fix build with 3.0 + +Signed-off-by: Thomas Backlund + + 3rdparty/rfswitch/Makefile | 2 -- + 1 file changed, 2 deletions(-) + +--- a/3rdparty/rfswitch/Makefile.orig 2011-07-15 02:37:27.000000000 +0300 ++++ a/3rdparty/rfswitch/Makefile 2011-07-15 13:47:10.463914526 +0300 +@@ -29,8 +29,6 @@ ifneq ($(PATCHLEVEL),6) # If we are not + + O_TARGET := rfswitch.o + +-include $(TOPDIR)/Rules.make +- + endif # End if 2.4 specific settings + + else diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch new file mode 100644 index 00000000..ab1c279c --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch @@ -0,0 +1,51 @@ +Remove 'proc_root' usage + +Commit c74c120a21d87b0b6925ada5830d8cac21e852d9 (Linus's tree) removed +the export of 'proc_root' variable, because you can create on the proc's +root directory by passing NULL where you would pass 'proc_root'. + +Signed-off-by: Luiz Fernando N. Capitulino +Index: linux-2.6.25/3rdparty/rfswitch/av5100.c +=================================================================== +--- linux-2.6.25.orig/3rdparty/rfswitch/av5100.c ++++ linux-2.6.25/3rdparty/rfswitch/av5100.c +@@ -107,7 +107,7 @@ static void av5100_proc_cleanup(void) + { + if (dir_base) { + remove_proc_entry("radio", dir_base); +- remove_proc_entry(DRV_NAME, &proc_root); ++ remove_proc_entry(DRV_NAME, NULL); + dir_base = NULL; + } + } +@@ -118,7 +118,7 @@ static int av5100_proc_init(void) + struct proc_dir_entry *ent; + int err = 0; + +- dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); ++ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, NULL); + if (dir_base == NULL) { + printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" + DRV_NAME "\n"); +Index: linux-2.6.25/3rdparty/rfswitch/pbe5.c +=================================================================== +--- linux-2.6.25.orig/3rdparty/rfswitch/pbe5.c ++++ linux-2.6.25/3rdparty/rfswitch/pbe5.c +@@ -138,7 +138,7 @@ static void pbe5_proc_cleanup(void) + { + if (dir_base) { + remove_proc_entry("radio", dir_base); +- remove_proc_entry(DRV_NAME, &proc_root); ++ remove_proc_entry(DRV_NAME, NULL); + dir_base = NULL; + } + } +@@ -149,7 +149,7 @@ static int pbe5_proc_init(void) + struct proc_dir_entry *ent; + int err = 0; + +- dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); ++ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, NULL); + if (dir_base == NULL) { + printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" + DRV_NAME "\n"); diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch new file mode 100644 index 00000000..61c96c46 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch @@ -0,0 +1,23 @@ +From 842e61cb85813b1ed8a1646cbe6739950c0c9822 Mon Sep 17 00:00:00 2001 +From: Larry Finger +Date: Thu, 7 May 2015 19:08:17 +0000 +Subject: [PATCH] rtl8723bs: Fix Makefile to allow warnings + +Signed-off-by: Larry Finger +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git linux.old/3rdparty/rtl8723bs/Makefile linux/3rdparty/rtl8723bs/Makefile +index a4d9f45..4da73fa 100644 +--- linux.old/3rdparty/rtl8723bs/Makefile ++++ linux/3rdparty/rtl8723bs/Makefile +@@ -3,7 +3,7 @@ EXTRA_CFLAGS += -O1 + #EXTRA_CFLAGS += -O3 + #EXTRA_CFLAGS += -Wall + #EXTRA_CFLAGS += -Wextra +-EXTRA_CFLAGS += -Werror ++#EXTRA_CFLAGS += -Werror + #EXTRA_CFLAGS += -pedantic + #EXTRA_CFLAGS += -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes + diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch b/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch new file mode 100644 index 00000000..a620e888 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch @@ -0,0 +1,28991 @@ + +Updates / cleanups from: + +git://github.com/hadess/rtl8723bs.git + +Topmost commit: + +commit 8d329c00dba5d8805a738ecea2bddde8eb0e4945 +Author: Bastien Nocera +Date: Tue May 5 18:30:38 2015 +0200 + + ioctl_linux: Fix compiling with Wake-On-LAN support + + + +Signed-off-by: Thomas backlund + +diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_ap.c linux/3rdparty/rtl8723bs/core/rtw_ap.c +--- linux/3rdparty/rtl8723bs.old/core/rtw_ap.c 2015-05-10 14:36:05.663667846 +0300 ++++ linux/3rdparty/rtl8723bs/core/rtw_ap.c 2015-05-07 17:46:55.000000000 +0300 +@@ -21,9 +21,6 @@ + + #include + +- +-#ifdef CONFIG_AP_MODE +- + extern unsigned char RTW_WPA_OUI[]; + extern unsigned char WMM_OUI[]; + extern unsigned char WPS_OUI[]; +@@ -32,7 +29,6 @@ extern unsigned char WFD_OUI[]; + + void init_mlme_ap_info(_adapter *padapter) + { +- struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; + struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); + struct sta_priv *pstapriv = &padapter->stapriv; + struct wlan_acl_pool *pacl_list = &pstapriv->acl_list; +@@ -71,9 +67,9 @@ void free_mlme_ap_info(_adapter *padapte + + //free bc/mc sta_info + psta = rtw_get_bcmc_stainfo(padapter); +- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_lock_bh(&(pstapriv->sta_hash_lock)); + rtw_free_stainfo(padapter, psta); +- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_unlock_bh(&(pstapriv->sta_hash_lock)); + } + + static void update_BCNTIM(_adapter *padapter) +@@ -191,7 +187,7 @@ static void update_BCNTIM(_adapter *pada + { + memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen); + +- rtw_mfree(pbackup_remainder_ie, remainder_ielen); ++ kfree(pbackup_remainder_ie); + } + + offset = (uint)(dst_ie - pie); +@@ -200,122 +196,6 @@ static void update_BCNTIM(_adapter *pada + } + } + +-void rtw_add_bcn_ie(_adapter *padapter, WLAN_BSSID_EX *pnetwork, u8 index, u8 *data, u8 len) +-{ +- PNDIS_802_11_VARIABLE_IEs pIE; +- u8 bmatch = false; +- u8 *pie = pnetwork->IEs; +- u8 *p=NULL, *dst_ie=NULL, *premainder_ie=NULL, *pbackup_remainder_ie=NULL; +- u32 i, offset, ielen, ie_offset, remainder_ielen = 0; +- +- for (i = sizeof(NDIS_802_11_FIXED_IEs); i < pnetwork->IELength;) +- { +- pIE = (PNDIS_802_11_VARIABLE_IEs)(pnetwork->IEs + i); +- +- if (pIE->ElementID > index) +- { +- break; +- } +- else if(pIE->ElementID == index) // already exist the same IE +- { +- p = (u8 *)pIE; +- ielen = pIE->Length; +- bmatch = true; +- break; +- } +- +- p = (u8 *)pIE; +- ielen = pIE->Length; +- i += (pIE->Length + 2); +- } +- +- if (p != NULL && ielen>0) +- { +- ielen += 2; +- +- premainder_ie = p+ielen; +- +- ie_offset = (sint)(p -pie); +- +- remainder_ielen = pnetwork->IELength - ie_offset - ielen; +- +- if(bmatch) +- dst_ie = p; +- else +- dst_ie = (p+ielen); +- } +- +- if(dst_ie == NULL) +- return; +- +- if(remainder_ielen>0) +- { +- pbackup_remainder_ie = rtw_malloc(remainder_ielen); +- if(pbackup_remainder_ie && premainder_ie) +- memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen); +- } +- +- *dst_ie++=index; +- *dst_ie++=len; +- +- memcpy(dst_ie, data, len); +- dst_ie+=len; +- +- //copy remainder IE +- if(pbackup_remainder_ie) +- { +- memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen); +- +- rtw_mfree(pbackup_remainder_ie, remainder_ielen); +- } +- +- offset = (uint)(dst_ie - pie); +- pnetwork->IELength = offset + remainder_ielen; +-} +- +-void rtw_remove_bcn_ie(_adapter *padapter, WLAN_BSSID_EX *pnetwork, u8 index) +-{ +- u8 *p, *dst_ie=NULL, *premainder_ie=NULL, *pbackup_remainder_ie=NULL; +- uint offset, ielen, ie_offset, remainder_ielen = 0; +- u8 *pie = pnetwork->IEs; +- +- p = rtw_get_ie(pie + _FIXED_IE_LENGTH_, index, &ielen, pnetwork->IELength - _FIXED_IE_LENGTH_); +- if (p != NULL && ielen>0) +- { +- ielen += 2; +- +- premainder_ie = p+ielen; +- +- ie_offset = (sint)(p -pie); +- +- remainder_ielen = pnetwork->IELength - ie_offset - ielen; +- +- dst_ie = p; +- } +- else { +- return; +- } +- +- if(remainder_ielen>0) +- { +- pbackup_remainder_ie = rtw_malloc(remainder_ielen); +- if(pbackup_remainder_ie && premainder_ie) +- memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen); +- } +- +- //copy remainder IE +- if(pbackup_remainder_ie) +- { +- memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen); +- +- rtw_mfree(pbackup_remainder_ie, remainder_ielen); +- } +- +- offset = (uint)(dst_ie - pie); +- pnetwork->IELength = offset + remainder_ielen; +-} +- +- + u8 chk_sta_is_alive(struct sta_info *psta); + u8 chk_sta_is_alive(struct sta_info *psta) + { +@@ -349,19 +229,19 @@ void expire_timeout_chk(_adapter *padapt + int i; + + +- _enter_critical_bh(&pstapriv->auth_list_lock, &irqL); ++ spin_lock_bh(&pstapriv->auth_list_lock); + + phead = &pstapriv->auth_list; + plist = get_next(phead); + + //check auth_queue + #ifdef DBG_EXPIRATION_CHK +- if (rtw_end_of_queue_search(phead, plist) == false) { ++ if (phead != plist) { + DBG_871X(FUNC_NDEV_FMT" auth_list, cnt:%u\n" + , FUNC_NDEV_ARG(padapter->pnetdev), pstapriv->auth_list_cnt); + } + #endif +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + psta = LIST_CONTAINOR(plist, struct sta_info, auth_list); + +@@ -372,41 +252,41 @@ void expire_timeout_chk(_adapter *padapt + psta->expire_to--; + if (psta->expire_to == 0) + { +- rtw_list_delete(&psta->auth_list); ++ list_del_init(&psta->auth_list); + pstapriv->auth_list_cnt--; + + DBG_871X("auth expire %02X%02X%02X%02X%02X%02X\n", + psta->hwaddr[0],psta->hwaddr[1],psta->hwaddr[2],psta->hwaddr[3],psta->hwaddr[4],psta->hwaddr[5]); + +- _exit_critical_bh(&pstapriv->auth_list_lock, &irqL); ++ spin_unlock_bh(&pstapriv->auth_list_lock); + +- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_lock_bh(&(pstapriv->sta_hash_lock)); + rtw_free_stainfo(padapter, psta); +- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_unlock_bh(&(pstapriv->sta_hash_lock)); + +- _enter_critical_bh(&pstapriv->auth_list_lock, &irqL); ++ spin_lock_bh(&pstapriv->auth_list_lock); + } + } + + } + +- _exit_critical_bh(&pstapriv->auth_list_lock, &irqL); ++ spin_unlock_bh(&pstapriv->auth_list_lock); + psta = NULL; + + +- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_lock_bh(&pstapriv->asoc_list_lock); + + phead = &pstapriv->asoc_list; + plist = get_next(phead); + + //check asoc_queue + #ifdef DBG_EXPIRATION_CHK +- if (rtw_end_of_queue_search(phead, plist) == false) { ++ if (phead != plist) { + DBG_871X(FUNC_NDEV_FMT" asoc_list, cnt:%u\n" + , FUNC_NDEV_ARG(padapter->pnetdev), pstapriv->asoc_list_cnt); + } + #endif +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); + plist = get_next(plist); +@@ -417,14 +297,13 @@ void expire_timeout_chk(_adapter *padapt + if (chk_sta_is_alive(psta) || !psta->expire_to) { + psta->expire_to = pstapriv->expire_to; + psta->keep_alive_trycnt = 0; +- #ifdef CONFIG_TX_MCAST2UNI + psta->under_exist_checking = 0; +- #endif // CONFIG_TX_MCAST2UNI + } else { +- psta->expire_to--; ++ if (psta->expire_to > 0) ++ psta->expire_to--; + } + +- if (psta->expire_to <= 0) ++ if (psta->expire_to == 0) + { + struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; + +@@ -460,7 +339,7 @@ void expire_timeout_chk(_adapter *padapt + + continue; + } +- rtw_list_delete(&psta->asoc_list); ++ list_del_init(&psta->asoc_list); + pstapriv->asoc_list_cnt--; + DBG_871X("asoc expire "MAC_FMT", state=0x%x\n", MAC_ARG(psta->hwaddr), psta->state); + updated = ap_free_sta(padapter, psta, false, WLAN_REASON_DEAUTH_LEAVING); +@@ -479,7 +358,7 @@ void expire_timeout_chk(_adapter *padapt + } + } + +- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_unlock_bh(&pstapriv->asoc_list_lock); + + if (chk_alive_num) { + +@@ -521,13 +400,13 @@ if (chk_alive_num) { + + psta->keep_alive_trycnt = 0; + DBG_871X("asoc expire "MAC_FMT", state=0x%x\n", MAC_ARG(psta->hwaddr), psta->state); +- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); +- if (rtw_is_list_empty(&psta->asoc_list)==false) { +- rtw_list_delete(&psta->asoc_list); ++ spin_lock_bh(&pstapriv->asoc_list_lock); ++ if (list_empty(&psta->asoc_list)==false) { ++ list_del_init(&psta->asoc_list); + pstapriv->asoc_list_cnt--; + updated = ap_free_sta(padapter, psta, false, WLAN_REASON_DEAUTH_LEAVING); + } +- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_unlock_bh(&pstapriv->asoc_list_lock); + + } + +@@ -544,13 +423,10 @@ void add_RATid(_adapter *padapter, struc + u8 rf_type; + unsigned char sta_band = 0, shortGIrate = false; + unsigned int tx_ra_bitmap=0; +- struct ht_priv *psta_ht = NULL; + struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); + WLAN_BSSID_EX *pcur_network = (WLAN_BSSID_EX *)&pmlmepriv->cur_network.network; + +- if(psta) +- psta_ht = &psta->htpriv; +- else ++ if(!psta) + return; + + if(!(psta->state & _FW_LINKED)) +@@ -672,9 +548,9 @@ void update_bmc_sta(_adapter *padapter) + + rtw_sta_media_status_rpt(padapter, psta, 1); + +- _enter_critical_bh(&psta->lock, &irqL); ++ spin_lock_bh(&psta->lock); + psta->state = _FW_LINKED; +- _exit_critical_bh(&psta->lock, &irqL); ++ spin_unlock_bh(&psta->lock); + + } + else +@@ -815,9 +691,9 @@ void update_sta_info_apmode(_adapter *pa + //add_RATid(padapter, psta);//move to ap_sta_info_defer_update() + + +- _enter_critical_bh(&psta->lock, &irqL); ++ spin_lock_bh(&psta->lock); + psta->state |= _FW_LINKED; +- _exit_critical_bh(&psta->lock, &irqL); ++ spin_unlock_bh(&psta->lock); + + + } +@@ -826,7 +702,6 @@ static void update_ap_info(_adapter *pad + { + struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); + WLAN_BSSID_EX *pnetwork = (WLAN_BSSID_EX *)&pmlmepriv->cur_network.network; +- struct security_priv *psecuritypriv = &padapter->securitypriv; + struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); + struct ht_priv *phtpriv_ap = &pmlmepriv->htpriv; + +@@ -875,7 +750,6 @@ static void update_hw_ht_param(_adapter + { + unsigned char max_AMPDU_len; + unsigned char min_MPDU_spacing; +- struct registry_priv *pregpriv = &padapter->registrypriv; + struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; + struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); + +@@ -933,8 +807,7 @@ void start_bss_network(_adapter *padapte + WLAN_BSSID_EX *pnetwork_mlmeext = &(pmlmeinfo->network); + struct HT_info_element *pht_info=NULL; + u8 cbw40_enable=0; +- u8 change_band = false; +- ++ + //DBG_871X("%s\n", __FUNCTION__); + + bcn_interval = (u16)pnetwork->Configuration.BeaconPeriod; +@@ -1114,15 +987,12 @@ int rtw_check_beacon_data(_adapter *pada + u8 channel, network_type, supportRate[NDIS_802_11_LENGTH_RATES_EX]; + int supportRateNum = 0; + u8 OUI1[] = {0x00, 0x50, 0xf2,0x01}; +- u8 wps_oui[4]={0x0,0x50,0xf2,0x04}; + u8 WMM_PARA_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01}; + struct registry_priv *pregistrypriv = &padapter->registrypriv; + struct security_priv *psecuritypriv = &padapter->securitypriv; + struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); + WLAN_BSSID_EX *pbss_network = (WLAN_BSSID_EX *)&pmlmepriv->cur_network.network; +- struct sta_priv *pstapriv = &padapter->stapriv; + u8 *ie = pbss_network->IEs; +- u8 vht_cap=false; + + /* SSID */ + /* Supported rates */ +@@ -1483,12 +1353,12 @@ int rtw_acl_add_sta(_adapter *padapter, + return (-1); + + +- _enter_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_lock_bh(&(pacl_node_q->lock)); + + phead = get_list_head(pacl_node_q); + plist = get_next(phead); + +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + paclnode = LIST_CONTAINOR(plist, struct rtw_wlan_acl_node, list); + plist = get_next(plist); +@@ -1504,14 +1374,14 @@ int rtw_acl_add_sta(_adapter *padapter, + } + } + +- _exit_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_unlock_bh(&(pacl_node_q->lock)); + + + if(added == true) + return ret; + + +- _enter_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_lock_bh(&(pacl_node_q->lock)); + + for(i=0; i< NUM_ACL; i++) + { +@@ -1519,13 +1389,13 @@ int rtw_acl_add_sta(_adapter *padapter, + + if(paclnode->valid == false) + { +- _rtw_init_listhead(&paclnode->list); ++ INIT_LIST_HEAD(&paclnode->list); + + memcpy(paclnode->addr, addr, ETH_ALEN); + + paclnode->valid = true; + +- rtw_list_insert_tail(&paclnode->list, get_list_head(pacl_node_q)); ++ list_add_tail(&paclnode->list, get_list_head(pacl_node_q)); + + pacl_list->num++; + +@@ -1535,7 +1405,7 @@ int rtw_acl_add_sta(_adapter *padapter, + + DBG_871X("%s, acl_num=%d\n", __func__, pacl_list->num); + +- _exit_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_unlock_bh(&(pacl_node_q->lock)); + + return ret; + } +@@ -1553,12 +1423,12 @@ int rtw_acl_remove_sta(_adapter *padapte + + DBG_871X("%s(acl_num=%d)=" MAC_FMT "\n", __func__, pacl_list->num, MAC_ARG(addr)); + +- _enter_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_lock_bh(&(pacl_node_q->lock)); + + phead = get_list_head(pacl_node_q); + plist = get_next(phead); + +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + paclnode = LIST_CONTAINOR(plist, struct rtw_wlan_acl_node, list); + plist = get_next(plist); +@@ -1569,14 +1439,14 @@ int rtw_acl_remove_sta(_adapter *padapte + { + paclnode->valid = false; + +- rtw_list_delete(&paclnode->list); ++ list_del_init(&paclnode->list); + + pacl_list->num--; + } + } + } + +- _exit_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_unlock_bh(&(pacl_node_q->lock)); + + DBG_871X("%s, acl_num=%d\n", __func__, pacl_list->num); + +@@ -1599,7 +1469,7 @@ u8 rtw_ap_set_pairwise_key(_adapter *pad + + psetstakey_para = (struct set_stakey_parm*)rtw_zmalloc(sizeof(struct set_stakey_parm)); + if(psetstakey_para==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); ++ kfree((u8 *) ph2c); + res=_FAIL; + goto exit; + } +@@ -1639,7 +1509,7 @@ static int rtw_ap_set_key(_adapter *pada + } + psetkeyparm=(struct setkey_parm*)rtw_zmalloc(sizeof(struct setkey_parm)); + if(psetkeyparm==NULL){ +- rtw_mfree((unsigned char *)pcmd, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)pcmd); + res= _FAIL; + goto exit; + } +@@ -1678,7 +1548,7 @@ static int rtw_ap_set_key(_adapter *pada + pcmd->rspsz = 0; + + +- _rtw_init_listhead(&pcmd->list); ++ INIT_LIST_HEAD(&pcmd->list); + + res = rtw_enqueue_cmd(pcmdpriv, pcmd); + +@@ -1715,8 +1585,6 @@ int rtw_ap_set_wep_key(_adapter *padapte + return rtw_ap_set_key(padapter, key, alg, keyid, set_tx); + } + +-#ifdef CONFIG_NATIVEAP_MLME +- + static void update_bcn_fixed_ie(_adapter *padapter) + { + DBG_871X("%s\n", __FUNCTION__); +@@ -1838,7 +1706,7 @@ static void update_bcn_wps_ie(_adapter * + } + + if(pbackup_remainder_ie) +- rtw_mfree(pbackup_remainder_ie, remainder_ielen); ++ kfree(pbackup_remainder_ie); + + // deal with the case without set_tx_beacon_cmd() in update_beacon() + #if defined( CONFIG_INTERRUPT_BASED_TXBCN ) +@@ -1907,7 +1775,7 @@ void update_beacon(_adapter *padapter, u + if(false == pmlmeext->bstart_bss) + return; + +- _enter_critical_bh(&pmlmepriv->bcn_update_lock, &irqL); ++ spin_lock_bh(&pmlmepriv->bcn_update_lock); + + switch(ie_id) + { +@@ -1959,7 +1827,7 @@ void update_beacon(_adapter *padapter, u + + pmlmepriv->update_bcn = true; + +- _exit_critical_bh(&pmlmepriv->bcn_update_lock, &irqL); ++ spin_unlock_bh(&pmlmepriv->bcn_update_lock); + + #ifndef CONFIG_INTERRUPT_BASED_TXBCN + if(tx) +@@ -2064,13 +1932,13 @@ void associated_clients_update(_adapter + struct sta_info *psta=NULL; + struct sta_priv *pstapriv = &padapter->stapriv; + +- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_lock_bh(&pstapriv->asoc_list_lock); + + phead = &pstapriv->asoc_list; + plist = get_next(phead); + + //check asoc_queue +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); + +@@ -2079,7 +1947,7 @@ void associated_clients_update(_adapter + VCS_update(padapter, psta); + } + +- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_unlock_bh(&pstapriv->asoc_list_lock); + + } + +@@ -2332,8 +2200,6 @@ u8 ap_free_sta(_adapter *padapter, struc + { + _irqL irqL; + u8 beacon_updated = false; +- struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); +- struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); + struct sta_priv *pstapriv = &padapter->stapriv; + + if(!psta) +@@ -2360,9 +2226,9 @@ u8 ap_free_sta(_adapter *padapter, struc + rtw_clearstakey_cmd(padapter, psta, true); + + +- _enter_critical_bh(&psta->lock, &irqL); ++ spin_lock_bh(&psta->lock); + psta->state &= ~_FW_LINKED; +- _exit_critical_bh(&psta->lock, &irqL); ++ spin_unlock_bh(&psta->lock); + + rtw_cfg80211_indicate_sta_disassoc(padapter, psta->hwaddr, reason); + +@@ -2370,52 +2236,15 @@ u8 ap_free_sta(_adapter *padapter, struc + + beacon_updated = bss_cap_update_on_sta_leave(padapter, psta); + +- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_lock_bh(&(pstapriv->sta_hash_lock)); + rtw_free_stainfo(padapter, psta); +- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_unlock_bh(&(pstapriv->sta_hash_lock)); + + + return beacon_updated; + + } + +-int rtw_ap_inform_ch_switch(_adapter *padapter, u8 new_ch, u8 ch_offset) +-{ +- _irqL irqL; +- _list *phead, *plist; +- int ret=0; +- struct sta_info *psta = NULL; +- struct sta_priv *pstapriv = &padapter->stapriv; +- struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; +- struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); +- u8 bc_addr[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff}; +- +- if((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE) +- return ret; +- +- DBG_871X(FUNC_NDEV_FMT" with ch:%u, offset:%u\n", +- FUNC_NDEV_ARG(padapter->pnetdev), new_ch, ch_offset); +- +- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); +- phead = &pstapriv->asoc_list; +- plist = get_next(phead); +- +- /* for each sta in asoc_queue */ +- while ((rtw_end_of_queue_search(phead, plist)) == false) +- { +- psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); +- plist = get_next(plist); +- +- issue_action_spct_ch_switch(padapter, psta->hwaddr, new_ch, ch_offset); +- psta->expire_to = ((pstapriv->expire_to * 2) > 5) ? 5 : (pstapriv->expire_to * 2); +- } +- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); +- +- issue_action_spct_ch_switch(padapter, bc_addr, new_ch, ch_offset); +- +- return ret; +-} +- + int rtw_sta_flush(_adapter *padapter) + { + _irqL irqL; +@@ -2433,25 +2262,25 @@ int rtw_sta_flush(_adapter *padapter) + return ret; + + +- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_lock_bh(&pstapriv->asoc_list_lock); + phead = &pstapriv->asoc_list; + plist = get_next(phead); + + //free sta asoc_queue +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); + + plist = get_next(plist); + +- rtw_list_delete(&psta->asoc_list); ++ list_del_init(&psta->asoc_list); + pstapriv->asoc_list_cnt--; + +- //_exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ //spin_unlock_bh(&pstapriv->asoc_list_lock); + ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING); +- //_enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ //spin_lock_bh(&pstapriv->asoc_list_lock); + } +- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_unlock_bh(&pstapriv->asoc_list_lock); + + + issue_deauth(padapter, bc_addr, WLAN_REASON_DEAUTH_LEAVING); +@@ -2516,7 +2345,6 @@ void rtw_ap_restore_network(_adapter *pa + { + struct mlme_priv *mlmepriv = &padapter->mlmepriv; + struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; +- struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); + struct sta_priv * pstapriv = &padapter->stapriv; + struct sta_info *psta; + struct security_priv* psecuritypriv=&(padapter->securitypriv); +@@ -2539,12 +2367,12 @@ void rtw_ap_restore_network(_adapter *pa + rtw_set_key(padapter, psecuritypriv, psecuritypriv->dot118021XGrpKeyid, 0,false); + } + +- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_lock_bh(&pstapriv->asoc_list_lock); + + phead = &pstapriv->asoc_list; + plist = get_next(phead); + +- while ((rtw_end_of_queue_search(phead, plist)) == false) { ++ while (phead != plist) { + int stainfo_offset; + + psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); +@@ -2556,7 +2384,7 @@ void rtw_ap_restore_network(_adapter *pa + } + } + +- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); ++ spin_unlock_bh(&pstapriv->asoc_list_lock); + + for (i = 0; i < chk_alive_num; i++) { + psta = rtw_get_stainfo_by_offset(pstapriv, chk_alive_list[i]); +@@ -2619,12 +2447,12 @@ void start_ap_mode(_adapter *padapter) + + + //for ACL +- _rtw_init_listhead(&(pacl_list->acl_node_q.queue)); ++ INIT_LIST_HEAD(&(pacl_list->acl_node_q.queue)); + pacl_list->num = 0; + pacl_list->mode = 0; + for(i = 0; i < NUM_ACL; i++) + { +- _rtw_init_listhead(&pacl_list->aclnode[i].list); ++ INIT_LIST_HEAD(&pacl_list->aclnode[i].list); + pacl_list->aclnode[i].valid = false; + } + +@@ -2651,10 +2479,10 @@ void stop_ap_mode(_adapter *padapter) + padapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled; + + //for ACL +- _enter_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_lock_bh(&(pacl_node_q->lock)); + phead = get_list_head(pacl_node_q); + plist = get_next(phead); +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + paclnode = LIST_CONTAINOR(plist, struct rtw_wlan_acl_node, list); + plist = get_next(plist); +@@ -2663,12 +2491,12 @@ void stop_ap_mode(_adapter *padapter) + { + paclnode->valid = false; + +- rtw_list_delete(&paclnode->list); ++ list_del_init(&paclnode->list); + + pacl_list->num--; + } + } +- _exit_critical_bh(&(pacl_node_q->lock), &irqL); ++ spin_unlock_bh(&(pacl_node_q->lock)); + + DBG_871X("%s, free acl_node_queue, num=%d\n", __func__, pacl_list->num); + +@@ -2678,9 +2506,9 @@ void stop_ap_mode(_adapter *padapter) + rtw_free_all_stainfo(padapter); + + psta = rtw_get_bcmc_stainfo(padapter); +- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_lock_bh(&(pstapriv->sta_hash_lock)); + rtw_free_stainfo(padapter, psta); +- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); ++ spin_unlock_bh(&(pstapriv->sta_hash_lock)); + + rtw_init_bcmc_stainfo(padapter); + +@@ -2688,8 +2516,3 @@ void stop_ap_mode(_adapter *padapter) + + rtw_btcoex_MediaStatusNotify(padapter, 0); //disconnect + } +- +-#endif //CONFIG_NATIVEAP_MLME +- +-#endif //CONFIG_AP_MODE +- +diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_btcoex.c linux/3rdparty/rtl8723bs/core/rtw_btcoex.c +--- linux/3rdparty/rtl8723bs.old/core/rtw_btcoex.c 2015-05-10 14:36:05.663667846 +0300 ++++ linux/3rdparty/rtl8723bs/core/rtw_btcoex.c 2015-05-07 17:46:55.000000000 +0300 +@@ -108,16 +108,6 @@ void rtw_btcoex_HaltNotify(PADAPTER pada + hal_btcoex_HaltNotify(padapter); + } + +-void rtw_btcoex_SwitchBtTRxMask(PADAPTER padapter) +-{ +- hal_btcoex_SwitchBtTRxMask(padapter); +-} +- +-void rtw_btcoex_Switch(PADAPTER padapter, u8 enable) +-{ +- hal_btcoex_SetBTCoexist(padapter, enable); +-} +- + u8 rtw_btcoex_IsBtDisabled(PADAPTER padapter) + { + return hal_btcoex_IsBtDisabled(padapter); +@@ -137,15 +127,6 @@ s32 rtw_btcoex_IsBTCoexCtrlAMPDUSize(PAD + return coexctrl; + } + +-u32 rtw_btcoex_GetAMPDUSize(PADAPTER padapter) +-{ +- u32 size; +- +- size = hal_btcoex_GetAMPDUSize(padapter); +- +- return size; +-} +- + void rtw_btcoex_SetManualControl(PADAPTER padapter, u8 manual) + { + if (true == manual) +@@ -158,11 +139,6 @@ void rtw_btcoex_SetManualControl(PADAPTE + } + } + +-u8 rtw_btcoex_1Ant(PADAPTER padapter) +-{ +- return hal_btcoex_1Ant(padapter); +-} +- + u8 rtw_btcoex_IsBtControlLps(PADAPTER padapter) + { + return hal_btcoex_IsBtControlLps(padapter); +@@ -198,11 +174,6 @@ void rtw_btcoex_SetPGAntNum(PADAPTER pad + hal_btcoex_SetPgAntNum(padapter, antNum); + } + +-u8 rtw_btcoex_GetPGAntNum(PADAPTER padapter) +-{ +- return hal_btcoex_GetPgAntNum(padapter); +-} +- + void rtw_btcoex_SetSingleAntPath(PADAPTER padapter, u8 singleAntPath) + { + hal_btcoex_SetSingleAntPath(padapter, singleAntPath); +@@ -233,16 +204,6 @@ u32 rtw_btcoex_GetDBG(PADAPTER padapter, + return hal_btcoex_GetDBG(padapter, pStrBuf, bufSize); + } + +-u8 rtw_btcoex_IncreaseScanDeviceNum(PADAPTER padapter) +-{ +- return hal_btcoex_IncreaseScanDeviceNum(padapter); +-} +- +-u8 rtw_btcoex_IsBtLinkExist(PADAPTER padapter) +-{ +- return hal_btcoex_IsBtLinkExist(padapter); +-} +- + // ================================================== + // Below Functions are called by BT-Coex + // ================================================== +diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_cmd.c linux/3rdparty/rtl8723bs/core/rtw_cmd.c +--- linux/3rdparty/rtl8723bs.old/core/rtw_cmd.c 2015-05-10 14:36:05.665667868 +0300 ++++ linux/3rdparty/rtl8723bs/core/rtw_cmd.c 2015-05-07 17:46:55.000000000 +0300 +@@ -255,10 +255,10 @@ _func_enter_; + void *c2h; + if ((c2h = rtw_cbuf_pop(pevtpriv->c2h_queue)) != NULL + && c2h != (void *)pevtpriv) { +- rtw_mfree(c2h, 16); ++ kfree(c2h); + } + } +- rtw_cbuf_free(pevtpriv->c2h_queue); ++ kfree(pevtpriv->c2h_queue); + + RT_TRACE(_module_rtl871x_cmd_c_,_drv_info_,("-_rtw_free_evt_priv \n")); + +@@ -272,10 +272,10 @@ _func_enter_; + + if(pcmdpriv){ + if (pcmdpriv->cmd_allocated_buf) +- rtw_mfree(pcmdpriv->cmd_allocated_buf, MAX_CMDSZ + CMDBUFF_ALIGN_SZ); ++ kfree(pcmdpriv->cmd_allocated_buf); + + if (pcmdpriv->rsp_allocated_buf) +- rtw_mfree(pcmdpriv->rsp_allocated_buf, MAX_RSPSZ + 4); ++ kfree(pcmdpriv->rsp_allocated_buf); + + mutex_destroy(&pcmdpriv->sctx_mutex); + } +@@ -301,13 +301,13 @@ _func_enter_; + if (obj == NULL) + goto exit; + +- //_enter_critical_bh(&queue->lock, &irqL); +- _enter_critical(&queue->lock, &irqL); ++ //spin_lock_bh(&queue->lock); ++ spin_lock_irqsave(&queue->lock, irqL); + +- rtw_list_insert_tail(&obj->list, &queue->queue); ++ list_add_tail(&obj->list, &queue->queue); + +- //_exit_critical_bh(&queue->lock, &irqL); +- _exit_critical(&queue->lock, &irqL); ++ //spin_unlock_bh(&queue->lock); ++ spin_unlock_irqrestore(&queue->lock, irqL); + + exit: + +@@ -323,18 +323,18 @@ struct cmd_obj *_rtw_dequeue_cmd(_queue + + _func_enter_; + +- //_enter_critical_bh(&(queue->lock), &irqL); +- _enter_critical(&queue->lock, &irqL); +- if (rtw_is_list_empty(&(queue->queue))) ++ //spin_lock_bh(&(queue->lock)); ++ spin_lock_irqsave(&queue->lock, irqL); ++ if (list_empty(&(queue->queue))) + obj = NULL; + else + { + obj = LIST_CONTAINOR(get_next(&(queue->queue)), struct cmd_obj, list); +- rtw_list_delete(&obj->list); ++ list_del_init(&obj->list); + } + +- //_exit_critical_bh(&(queue->lock), &irqL); +- _exit_critical(&queue->lock, &irqL); ++ //spin_unlock_bh(&(queue->lock)); ++ spin_unlock_irqrestore(&queue->lock, irqL); + + _func_exit_; + +@@ -442,14 +442,6 @@ _func_exit_; + return cmd_obj; + } + +-void rtw_cmd_clr_isr(struct cmd_priv *pcmdpriv) +-{ +-_func_enter_; +- pcmdpriv->cmd_done_cnt++; +- //up(&(pcmdpriv->cmd_done_sema)); +-_func_exit_; +-} +- + void rtw_free_cmd_obj(struct cmd_obj *pcmd) + { + _func_enter_; +@@ -457,7 +449,7 @@ _func_enter_; + if((pcmd->cmdcode!=_JoinBss_CMD_) &&(pcmd->cmdcode!= _CreateBss_CMD_)) + { + //free parmbuf in cmd_obj +- rtw_mfree((unsigned char*)pcmd->parmbuf, pcmd->cmdsz); ++ kfree((unsigned char*)pcmd->parmbuf); + } + + if(pcmd->rsp!=NULL) +@@ -465,12 +457,12 @@ _func_enter_; + if(pcmd->rspsz!= 0) + { + //free rsp in cmd_obj +- rtw_mfree((unsigned char*)pcmd->rsp, pcmd->rspsz); ++ kfree((unsigned char*)pcmd->rsp); + } + } + + //free cmd_obj +- rtw_mfree((unsigned char*)pcmd, sizeof(struct cmd_obj)); ++ kfree((unsigned char*)pcmd); + + _func_exit_; + } +@@ -533,7 +525,7 @@ _func_enter_; + break; + } + +- if(rtw_is_list_empty(&(pcmdpriv->cmd_queue.queue))) ++ if(list_empty(&(pcmdpriv->cmd_queue.queue))) + { + //DBG_871X("%s: cmd queue is empty!\n", __func__); + continue; +@@ -573,7 +565,7 @@ _next: + + memcpy(pcmdbuf, pcmd->parmbuf, pcmd->cmdsz); + +- if(pcmd->cmdcode < (sizeof(wlancmds) /sizeof(struct cmd_hdl))) ++ if(pcmd->cmdcode < ARRAY_SIZE(wlancmds)) + { + cmd_hdl = wlancmds[pcmd->cmdcode].h2cfuns; + +@@ -594,18 +586,18 @@ _next: + + post_process: + +- _enter_critical_mutex(&(pcmd->padapter->cmdpriv.sctx_mutex), NULL); +- if (pcmd->sctx) { +- DBG_871X_LEVEL(_drv_always_, FUNC_ADPT_FMT" pcmd->sctx\n", +- FUNC_ADPT_ARG(pcmd->padapter)); +- +- if (pcmd->res == H2C_SUCCESS) +- rtw_sctx_done(&pcmd->sctx); +- else +- rtw_sctx_done_err(&pcmd->sctx, RTW_SCTX_DONE_CMD_ERROR); ++ if (mutex_lock_interruptible(&(pcmd->padapter->cmdpriv.sctx_mutex)) == 0) { ++ if (pcmd->sctx) { ++ DBG_871X_LEVEL(_drv_always_, FUNC_ADPT_FMT" pcmd->sctx\n", ++ FUNC_ADPT_ARG(pcmd->padapter)); ++ ++ if (pcmd->res == H2C_SUCCESS) ++ rtw_sctx_done(&pcmd->sctx); ++ else ++ rtw_sctx_done_err(&pcmd->sctx, RTW_SCTX_DONE_CMD_ERROR); ++ } ++ mutex_unlock(&(pcmd->padapter->cmdpriv.sctx_mutex)); + } +- _exit_critical_mutex(&(pcmd->padapter->cmdpriv.sctx_mutex), NULL); +- + + if((cmd_process_time = jiffies_to_msecs(jiffies - cmd_start_time)) > 1000) + { +@@ -627,7 +619,7 @@ post_process: + } + + //call callback function for post-processed +- if(pcmd->cmdcode < (sizeof(rtw_cmd_callback) /sizeof(struct _cmd_callback))) ++ if(pcmd->cmdcode < ARRAY_SIZE(rtw_cmd_callback)) + { + pcmd_callback = rtw_cmd_callback[pcmd->cmdcode].callback; + if(pcmd_callback == NULL) +@@ -666,7 +658,7 @@ post_process: + if (pcmd->cmdcode == GEN_CMD_CODE(_Set_Drv_Extra)) { + extra_parm = (struct drvextra_cmd_parm *)pcmd->parmbuf; + if(extra_parm->pbuf && extra_parm->size > 0) { +- rtw_mfree(extra_parm->pbuf, extra_parm->size); ++ kfree(extra_parm->pbuf); + } + } + +@@ -682,42 +674,6 @@ _func_exit_; + + } + +-u8 rtw_setstandby_cmd(_adapter *padapter, uint action) +-{ +- struct cmd_obj* ph2c; +- struct usb_suspend_parm* psetusbsuspend; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- +- u8 ret = _SUCCESS; +- +-_func_enter_; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if (ph2c == NULL) { +- ret = _FAIL; +- goto exit; +- } +- +- psetusbsuspend = (struct usb_suspend_parm*)rtw_zmalloc(sizeof(struct usb_suspend_parm)); +- if (psetusbsuspend == NULL) { +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- ret = _FAIL; +- goto exit; +- } +- +- psetusbsuspend->action = action; +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, psetusbsuspend, GEN_CMD_CODE(_SetUsbSuspend)); +- +- ret = rtw_enqueue_cmd(pcmdpriv, ph2c); +- +-exit: +- +-_func_exit_; +- +- return ret; +-} +- + /* + rtw_sitesurvey_cmd(~) + ### NOTE:#### (!!!!) +@@ -744,7 +700,7 @@ _func_enter_; + + psurveyPara = (struct sitesurvey_parm*)rtw_zmalloc(sizeof(struct sitesurvey_parm)); + if (psurveyPara == NULL) { +- rtw_mfree((unsigned char*) ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char*) ph2c); + return _FAIL; + } + +@@ -793,8 +749,6 @@ _func_enter_; + + pmlmepriv->scan_start_time = jiffies; + _set_timer(&pmlmepriv->scan_to_timer, SCANNING_TIMEOUT); +- +- rtw_led_control(padapter, LED_CTL_SITE_SURVEY); + } else { + _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY); + } +@@ -821,7 +775,7 @@ _func_enter_; + + pbsetdataratepara = (struct setdatarate_parm*)rtw_zmalloc(sizeof(struct setdatarate_parm)); + if (pbsetdataratepara == NULL) { +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); ++ kfree((u8 *) ph2c); + res = _FAIL; + goto exit; + } +@@ -838,243 +792,17 @@ _func_exit_; + return res; + } + +-u8 rtw_setbasicrate_cmd(_adapter *padapter, u8 *rateset) +-{ +- struct cmd_obj* ph2c; +- struct setbasicrate_parm* pssetbasicratepara; +- struct cmd_priv* pcmdpriv=&padapter->cmdpriv; +- u8 res = _SUCCESS; +- +-_func_enter_; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if (ph2c == NULL) { +- res= _FAIL; +- goto exit; +- } +- pssetbasicratepara = (struct setbasicrate_parm*)rtw_zmalloc(sizeof(struct setbasicrate_parm)); +- +- if (pssetbasicratepara == NULL) { +- rtw_mfree((u8*) ph2c, sizeof(struct cmd_obj)); +- res = _FAIL; +- goto exit; +- } +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, pssetbasicratepara, _SetBasicRate_CMD_); +- +- memcpy(pssetbasicratepara->basicrates, rateset, NumRates); +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +- +-_func_exit_; +- +- return res; +-} +- +- +-/* +-unsigned char rtw_setphy_cmd(unsigned char *adapter) +- +-1. be called only after rtw_update_registrypriv_dev_network( ~) or mp testing program +-2. for AdHoc/Ap mode or mp mode? +- +-*/ +-u8 rtw_setphy_cmd(_adapter *padapter, u8 modem, u8 ch) +-{ +- struct cmd_obj* ph2c; +- struct setphy_parm* psetphypara; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +-// struct mlme_priv *pmlmepriv = &padapter->mlmepriv; +-// struct registry_priv* pregistry_priv = &padapter->registrypriv; +- u8 res=_SUCCESS; +- +-_func_enter_; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res= _FAIL; +- goto exit; +- } +- psetphypara = (struct setphy_parm*)rtw_zmalloc(sizeof(struct setphy_parm)); +- +- if(psetphypara==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, psetphypara, _SetPhy_CMD_); +- +- RT_TRACE(_module_rtl871x_cmd_c_,_drv_info_,("CH=%d, modem=%d", ch, modem)); +- +- psetphypara->modem = modem; +- psetphypara->rfchannel = ch; +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +-_func_exit_; +- return res; +-} +- +-u8 rtw_setbbreg_cmd(_adapter*padapter, u8 offset, u8 val) +-{ +- struct cmd_obj* ph2c; +- struct writeBB_parm* pwritebbparm; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- u8 res=_SUCCESS; +-_func_enter_; +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res= _FAIL; +- goto exit; +- } +- pwritebbparm = (struct writeBB_parm*)rtw_zmalloc(sizeof(struct writeBB_parm)); +- +- if(pwritebbparm==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, pwritebbparm, GEN_CMD_CODE(_SetBBReg)); +- +- pwritebbparm->offset = offset; +- pwritebbparm->value = val; +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +-_func_exit_; +- return res; +-} +- +-u8 rtw_getbbreg_cmd(_adapter *padapter, u8 offset, u8 *pval) +-{ +- struct cmd_obj* ph2c; +- struct readBB_parm* prdbbparm; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- u8 res=_SUCCESS; +- +-_func_enter_; +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res=_FAIL; +- goto exit; +- } +- prdbbparm = (struct readBB_parm*)rtw_zmalloc(sizeof(struct readBB_parm)); +- +- if(prdbbparm ==NULL){ +- rtw_mfree((unsigned char *) ph2c, sizeof(struct cmd_obj)); +- return _FAIL; +- } +- +- _rtw_init_listhead(&ph2c->list); +- ph2c->cmdcode =GEN_CMD_CODE(_GetBBReg); +- ph2c->parmbuf = (unsigned char *)prdbbparm; +- ph2c->cmdsz = sizeof(struct readBB_parm); +- ph2c->rsp = pval; +- ph2c->rspsz = sizeof(struct readBB_rsp); +- +- prdbbparm ->offset = offset; +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +-_func_exit_; +- return res; +-} +- +-u8 rtw_setrfreg_cmd(_adapter *padapter, u8 offset, u32 val) +-{ +- struct cmd_obj* ph2c; +- struct writeRF_parm* pwriterfparm; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- u8 res=_SUCCESS; +-_func_enter_; +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res= _FAIL; +- goto exit; +- } +- pwriterfparm = (struct writeRF_parm*)rtw_zmalloc(sizeof(struct writeRF_parm)); +- +- if(pwriterfparm==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, pwriterfparm, GEN_CMD_CODE(_SetRFReg)); +- +- pwriterfparm->offset = offset; +- pwriterfparm->value = val; +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +-_func_exit_; +- return res; +-} +- +-u8 rtw_getrfreg_cmd(_adapter *padapter, u8 offset, u8 *pval) +-{ +- struct cmd_obj* ph2c; +- struct readRF_parm* prdrfparm; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- u8 res=_SUCCESS; +- +-_func_enter_; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res= _FAIL; +- goto exit; +- } +- +- prdrfparm = (struct readRF_parm*)rtw_zmalloc(sizeof(struct readRF_parm)); +- if(prdrfparm ==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- _rtw_init_listhead(&ph2c->list); +- ph2c->cmdcode =GEN_CMD_CODE(_GetRFReg); +- ph2c->parmbuf = (unsigned char *)prdrfparm; +- ph2c->cmdsz = sizeof(struct readRF_parm); +- ph2c->rsp = pval; +- ph2c->rspsz = sizeof(struct readRF_rsp); +- +- prdrfparm ->offset = offset; +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +- +-exit: +- +-_func_exit_; +- +- return res; +-} +- + void rtw_getbbrfreg_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd) + { + _func_enter_; + + //rtw_free_cmd_obj(pcmd); +- rtw_mfree((unsigned char*) pcmd->parmbuf, pcmd->cmdsz); +- rtw_mfree((unsigned char*) pcmd, sizeof(struct cmd_obj)); ++ kfree((unsigned char*) pcmd->parmbuf); ++ kfree((unsigned char*) pcmd); + + _func_exit_; + } + +-void rtw_readtssi_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd) +-{ +- _func_enter_; +- +- rtw_mfree((unsigned char*) pcmd->parmbuf, pcmd->cmdsz); +- rtw_mfree((unsigned char*) pcmd, sizeof(struct cmd_obj)); +- +-_func_exit_; +-} +- + u8 rtw_createbss_cmd(_adapter *padapter) + { + struct cmd_obj* pcmd; +@@ -1085,8 +813,6 @@ u8 rtw_createbss_cmd(_adapter *padapter + + _func_enter_; + +- rtw_led_control(padapter, LED_CTL_START_TO_LINK); +- + if (pmlmepriv->assoc_ssid.SsidLength == 0){ + RT_TRACE(_module_rtl871x_cmd_c_,_drv_info_,(" createbss for Any SSid:%s\n",pmlmepriv->assoc_ssid.Ssid)); + } else { +@@ -1099,7 +825,7 @@ _func_enter_; + goto exit; + } + +- _rtw_init_listhead(&pcmd->list); ++ INIT_LIST_HEAD(&pcmd->list); + pcmd->cmdcode = _CreateBss_CMD_; + pcmd->parmbuf = (unsigned char *)pdev_network; + pcmd->cmdsz = get_WLAN_BSSID_EX_sz((WLAN_BSSID_EX*)pdev_network); +@@ -1117,36 +843,6 @@ _func_exit_; + return res; + } + +-u8 rtw_createbss_cmd_ex(_adapter *padapter, unsigned char *pbss, unsigned int sz) +-{ +- struct cmd_obj* pcmd; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- u8 res=_SUCCESS; +- +-_func_enter_; +- +- pcmd = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(pcmd==NULL){ +- res= _FAIL; +- goto exit; +- } +- +- _rtw_init_listhead(&pcmd->list); +- pcmd->cmdcode = GEN_CMD_CODE(_CreateBss); +- pcmd->parmbuf = pbss; +- pcmd->cmdsz = sz; +- pcmd->rsp = NULL; +- pcmd->rspsz = 0; +- +- res = rtw_enqueue_cmd(pcmdpriv, pcmd); +- +-exit: +- +-_func_exit_; +- +- return res; +-} +- + u8 rtw_startbss_cmd(_adapter *padapter, int flags) + { + struct cmd_obj* pcmd; +@@ -1167,7 +863,7 @@ _func_enter_; + goto exit; + } + +- _rtw_init_listhead(&pcmd->list); ++ INIT_LIST_HEAD(&pcmd->list); + pcmd->cmdcode = GEN_CMD_CODE(_CreateBss); + pcmd->parmbuf = NULL; + pcmd->cmdsz = 0; +@@ -1183,10 +879,11 @@ _func_enter_; + + if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) { + rtw_sctx_wait(&sctx, __func__); +- _enter_critical_mutex(&pcmdpriv->sctx_mutex, NULL); +- if (sctx.status == RTW_SCTX_SUBMITTED) +- pcmd->sctx = NULL; +- _exit_critical_mutex(&pcmdpriv->sctx_mutex, NULL); ++ if (mutex_lock_interruptible(&pcmdpriv->sctx_mutex) == 0) { ++ if (sctx.status == RTW_SCTX_SUBMITTED) ++ pcmd->sctx = NULL; ++ mutex_unlock(&pcmdpriv->sctx_mutex); ++ } + } + } + +@@ -1216,8 +913,6 @@ u8 rtw_joinbss_cmd(_adapter *padapter, + u8 *ptmp=NULL; + _func_enter_; + +- rtw_led_control(padapter, LED_CTL_START_TO_LINK); +- + if (pmlmepriv->assoc_ssid.SsidLength == 0){ + RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, ("+Join cmd: Any SSid\n")); + } else { +@@ -1267,7 +962,7 @@ _func_enter_; + if(psecnetwork==NULL) + { + if(pcmd !=NULL) +- rtw_mfree((unsigned char *)pcmd, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)pcmd); + + res=_FAIL; + +@@ -1348,7 +1043,7 @@ _func_enter_; + + pcmd->cmdsz = get_WLAN_BSSID_EX_sz(psecnetwork);//get cmdsz before endian conversion + +- _rtw_init_listhead(&pcmd->list); ++ INIT_LIST_HEAD(&pcmd->list); + pcmd->cmdcode = _JoinBss_CMD_;//GEN_CMD_CODE(_JoinBss) + pcmd->parmbuf = (unsigned char *)psecnetwork; + pcmd->rsp = NULL; +@@ -1387,7 +1082,7 @@ _func_enter_; + cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj)); + if (cmdobj == NULL) { + res = _FAIL; +- rtw_mfree((u8 *)param, sizeof(*param)); ++ kfree((u8 *)param); + goto exit; + } + init_h2fwcmd_w_parm_no_rsp(cmdobj, param, _DisConnect_CMD_); +@@ -1396,7 +1091,7 @@ _func_enter_; + /* no need to enqueue, do the cmd hdl directly and free cmd parameter */ + if (H2C_SUCCESS != disconnect_hdl(padapter, (u8 *)param)) + res = _FAIL; +- rtw_mfree((u8 *)param, sizeof(*param)); ++ kfree((u8 *)param); + } + + exit: +@@ -1426,7 +1121,7 @@ _func_enter_; + if(enqueue){ + ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); + if(ph2c==NULL){ +- rtw_mfree((u8 *)psetop, sizeof(*psetop)); ++ kfree((u8 *)psetop); + res= _FAIL; + goto exit; + } +@@ -1436,7 +1131,7 @@ _func_enter_; + } + else{ + setopmode_hdl(padapter, (u8 *)psetop); +- rtw_mfree((u8 *)psetop, sizeof(*psetop)); ++ kfree((u8 *)psetop); + } + exit: + +@@ -1486,15 +1181,15 @@ _func_enter_; + { + ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); + if ( ph2c == NULL){ +- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); ++ kfree((u8 *) psetstakey_para); + res= _FAIL; + goto exit; + } + + psetstakey_rsp = (struct set_stakey_rsp*)rtw_zmalloc(sizeof(struct set_stakey_rsp)); + if(psetstakey_rsp == NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); ++ kfree((u8 *) ph2c); ++ kfree((u8 *) psetstakey_para); + res=_FAIL; + goto exit; + } +@@ -1506,7 +1201,7 @@ _func_enter_; + } + else{ + set_stakey_hdl(padapter, (u8 *)psetstakey_para); +- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); ++ kfree((u8 *) psetstakey_para); + } + exit: + +@@ -1546,15 +1241,15 @@ _func_enter_; + + psetstakey_para = (struct set_stakey_parm*)rtw_zmalloc(sizeof(struct set_stakey_parm)); + if(psetstakey_para==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); ++ kfree((u8 *) ph2c); + res=_FAIL; + goto exit; + } + + psetstakey_rsp = (struct set_stakey_rsp*)rtw_zmalloc(sizeof(struct set_stakey_rsp)); + if(psetstakey_rsp == NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); ++ kfree((u8 *) ph2c); ++ kfree((u8 *) psetstakey_para); + res=_FAIL; + goto exit; + } +@@ -1578,123 +1273,6 @@ _func_exit_; + return res; + } + +-u8 rtw_setrttbl_cmd(_adapter *padapter, struct setratable_parm *prate_table) +-{ +- struct cmd_obj* ph2c; +- struct setratable_parm * psetrttblparm; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- u8 res=_SUCCESS; +-_func_enter_; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res= _FAIL; +- goto exit; +- } +- psetrttblparm = (struct setratable_parm*)rtw_zmalloc(sizeof(struct setratable_parm)); +- +- if(psetrttblparm==NULL){ +- rtw_mfree((unsigned char *) ph2c, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, psetrttblparm, GEN_CMD_CODE(_SetRaTable)); +- +- memcpy(psetrttblparm,prate_table,sizeof(struct setratable_parm)); +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +-_func_exit_; +- return res; +- +-} +- +-u8 rtw_getrttbl_cmd(_adapter *padapter, struct getratable_rsp *pval) +-{ +- struct cmd_obj* ph2c; +- struct getratable_parm * pgetrttblparm; +- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; +- u8 res=_SUCCESS; +-_func_enter_; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res= _FAIL; +- goto exit; +- } +- pgetrttblparm = (struct getratable_parm*)rtw_zmalloc(sizeof(struct getratable_parm)); +- +- if(pgetrttblparm==NULL){ +- rtw_mfree((unsigned char *) ph2c, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +-// init_h2fwcmd_w_parm_no_rsp(ph2c, psetrttblparm, GEN_CMD_CODE(_SetRaTable)); +- +- _rtw_init_listhead(&ph2c->list); +- ph2c->cmdcode =GEN_CMD_CODE(_GetRaTable); +- ph2c->parmbuf = (unsigned char *)pgetrttblparm; +- ph2c->cmdsz = sizeof(struct getratable_parm); +- ph2c->rsp = (u8*)pval; +- ph2c->rspsz = sizeof(struct getratable_rsp); +- +- pgetrttblparm ->rsvd = 0x0; +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +-_func_exit_; +- return res; +- +-} +- +-u8 rtw_setassocsta_cmd(_adapter *padapter, u8 *mac_addr) +-{ +- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; +- struct cmd_obj* ph2c; +- struct set_assocsta_parm *psetassocsta_para; +- struct set_stakey_rsp *psetassocsta_rsp = NULL; +- +- u8 res=_SUCCESS; +- +-_func_enter_; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(ph2c==NULL){ +- res= _FAIL; +- goto exit; +- } +- +- psetassocsta_para = (struct set_assocsta_parm*)rtw_zmalloc(sizeof(struct set_assocsta_parm)); +- if(psetassocsta_para==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- res=_FAIL; +- goto exit; +- } +- +- psetassocsta_rsp = (struct set_stakey_rsp*)rtw_zmalloc(sizeof(struct set_assocsta_rsp)); +- if(psetassocsta_rsp==NULL){ +- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); +- rtw_mfree((u8 *) psetassocsta_para, sizeof(struct set_assocsta_parm)); +- return _FAIL; +- } +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, psetassocsta_para, _SetAssocSta_CMD_); +- ph2c->rsp = (u8 *) psetassocsta_rsp; +- ph2c->rspsz = sizeof(struct set_assocsta_rsp); +- +- memcpy(psetassocsta_para->addr, mac_addr,ETH_ALEN); +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +- +-exit: +- +-_func_exit_; +- +- return res; +- } +- + u8 rtw_addbareq_cmd(_adapter*padapter, u8 tid, u8 *addr) + { + struct cmd_priv *pcmdpriv = &padapter->cmdpriv; +@@ -1713,7 +1291,7 @@ _func_enter_; + + paddbareq_parm = (struct addBaReq_parm*)rtw_zmalloc(sizeof(struct addBaReq_parm)); + if(paddbareq_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -1752,7 +1330,7 @@ _func_enter_; + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -1793,7 +1371,7 @@ _func_enter_; + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -1835,7 +1413,7 @@ _func_enter_; + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -1858,61 +1436,6 @@ _func_exit_; + + } + +-u8 rtw_set_ch_cmd(_adapter*padapter, u8 ch, u8 bw, u8 ch_offset, u8 enqueue) +-{ +- struct cmd_obj *pcmdobj; +- struct set_ch_parm *set_ch_parm; +- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; +- +- u8 res=_SUCCESS; +- +-_func_enter_; +- +- DBG_871X(FUNC_NDEV_FMT" ch:%u, bw:%u, ch_offset:%u\n", +- FUNC_NDEV_ARG(padapter->pnetdev), ch, bw, ch_offset); +- +- /* check input parameter */ +- +- /* prepare cmd parameter */ +- set_ch_parm = (struct set_ch_parm *)rtw_zmalloc(sizeof(*set_ch_parm)); +- if (set_ch_parm == NULL) { +- res= _FAIL; +- goto exit; +- } +- set_ch_parm->ch = ch; +- set_ch_parm->bw = bw; +- set_ch_parm->ch_offset = ch_offset; +- +- if (enqueue) { +- /* need enqueue, prepare cmd_obj and enqueue */ +- pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(pcmdobj == NULL){ +- rtw_mfree((u8 *)set_ch_parm, sizeof(*set_ch_parm)); +- res=_FAIL; +- goto exit; +- } +- +- init_h2fwcmd_w_parm_no_rsp(pcmdobj, set_ch_parm, GEN_CMD_CODE(_SetChannel)); +- res = rtw_enqueue_cmd(pcmdpriv, pcmdobj); +- } else { +- /* no need to enqueue, do the cmd hdl directly and free cmd parameter */ +- if( H2C_SUCCESS !=set_ch_hdl(padapter, (u8 *)set_ch_parm) ) +- res = _FAIL; +- +- rtw_mfree((u8 *)set_ch_parm, sizeof(*set_ch_parm)); +- } +- +- /* do something based on res... */ +- +-exit: +- +- DBG_871X(FUNC_NDEV_FMT" res:%u\n", FUNC_NDEV_ARG(padapter->pnetdev), res); +- +-_func_exit_; +- +- return res; +-} +- + u8 rtw_set_chplan_cmd(_adapter*padapter, u8 chplan, u8 enqueue, u8 swconfig) + { + struct cmd_obj* pcmdobj; +@@ -1951,7 +1474,7 @@ _func_enter_; + //need enqueue, prepare cmd_obj and enqueue + pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); + if(pcmdobj == NULL){ +- rtw_mfree((u8 *)setChannelPlan_param, sizeof(struct SetChannelPlan_param)); ++ kfree((u8 *)setChannelPlan_param); + res=_FAIL; + goto exit; + } +@@ -1965,7 +1488,7 @@ _func_enter_; + if( H2C_SUCCESS !=set_chplan_hdl(padapter, (unsigned char *)setChannelPlan_param) ) + res = _FAIL; + +- rtw_mfree((u8 *)setChannelPlan_param, sizeof(struct SetChannelPlan_param)); ++ kfree((u8 *)setChannelPlan_param); + } + + //do something based on res... +@@ -1979,92 +1502,6 @@ _func_exit_; + return res; + } + +-u8 rtw_led_blink_cmd(_adapter*padapter, void * pLed) +-{ +- struct cmd_obj* pcmdobj; +- struct LedBlink_param *ledBlink_param; +- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; +- +- u8 res=_SUCCESS; +- +-_func_enter_; +- +- RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+rtw_led_blink_cmd\n")); +- +- pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(pcmdobj == NULL){ +- res=_FAIL; +- goto exit; +- } +- +- ledBlink_param = (struct LedBlink_param *)rtw_zmalloc(sizeof(struct LedBlink_param)); +- if(ledBlink_param == NULL) { +- rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- ledBlink_param->pLed=pLed; +- +- init_h2fwcmd_w_parm_no_rsp(pcmdobj, ledBlink_param, GEN_CMD_CODE(_LedBlink)); +- res = rtw_enqueue_cmd(pcmdpriv, pcmdobj); +- +-exit: +- +-_func_exit_; +- +- return res; +-} +- +-u8 rtw_set_csa_cmd(_adapter*padapter, u8 new_ch_no) +-{ +- struct cmd_obj* pcmdobj; +- struct SetChannelSwitch_param*setChannelSwitch_param; +- struct mlme_priv *pmlmepriv = &padapter->mlmepriv; +- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; +- +- u8 res=_SUCCESS; +- +-_func_enter_; +- +- RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+rtw_set_csa_cmd\n")); +- +- pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if(pcmdobj == NULL){ +- res=_FAIL; +- goto exit; +- } +- +- setChannelSwitch_param = (struct SetChannelSwitch_param *)rtw_zmalloc(sizeof(struct SetChannelSwitch_param)); +- if(setChannelSwitch_param == NULL) { +- rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- setChannelSwitch_param->new_ch_no=new_ch_no; +- +- init_h2fwcmd_w_parm_no_rsp(pcmdobj, setChannelSwitch_param, GEN_CMD_CODE(_SetChannelSwitch)); +- res = rtw_enqueue_cmd(pcmdpriv, pcmdobj); +- +-exit: +- +-_func_exit_; +- +- return res; +-} +- +-u8 rtw_tdls_cmd(_adapter *padapter, u8 *addr, u8 option) +-{ +- u8 res=_SUCCESS; +- +-_func_enter_; +- +-_func_exit_; +- +- return res; +-} +- + static void collect_traffic_statistics(_adapter *padapter) + { + struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); +@@ -2150,8 +1587,6 @@ u8 traffic_status_watchdog(_adapter *pad + TRAFFIC_PROTECT_PERIOD_MS, + link_detect->NumTxOkInPeriod, + link_detect->NumRxUnicastOkInPeriod); +- +- rtw_lock_traffic_suspend_timeout(TRAFFIC_PROTECT_PERIOD_MS); + } + #endif + +@@ -2250,12 +1685,10 @@ static void dynamic_chk_wk_hdl(_adapter + struct mlme_priv *pmlmepriv; + pmlmepriv = &(padapter->mlmepriv); + +-#ifdef CONFIG_AP_MODE + if(check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) + { + expire_timeout_chk(padapter); + } +-#endif + + //for debug purpose + _linked_info_dump(padapter); +@@ -2370,7 +1803,7 @@ _func_enter_; + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -2418,7 +1851,7 @@ u8 rtw_dm_in_lps_wk_cmd(_adapter*padapte + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -2470,43 +1903,6 @@ static void rtw_lps_change_dtim_hdl(_ada + up(&pwrpriv->lock); + } + +-u8 rtw_lps_change_dtim_cmd(_adapter*padapter, u8 dtim) +-{ +- struct cmd_obj *ph2c; +- struct drvextra_cmd_parm *pdrvextra_cmd_parm; +- 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; +- } +- +- pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); +- if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); +- res= _FAIL; +- goto exit; +- } +- +- pdrvextra_cmd_parm->ec_id = LPS_CHANGE_DTIM_CID; +- pdrvextra_cmd_parm->type = dtim; +- pdrvextra_cmd_parm->size = 0; +- pdrvextra_cmd_parm->pbuf = NULL; +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +- } +- +-exit: +- +- return res; +- +-} +- + static void rtw_dm_ra_mask_hdl(_adapter *padapter, struct sta_info *psta) + { + if (psta) { +@@ -2530,7 +1926,7 @@ u8 rtw_dm_ra_mask_wk_cmd(_adapter*padapt + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -2583,7 +1979,7 @@ _func_enter_; + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ppscmd, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ppscmd); + res= _FAIL; + goto exit; + } +@@ -2604,8 +2000,6 @@ _func_exit_; + + } + +-#ifdef CONFIG_AP_MODE +- + extern u32 g_wait_hiq_empty; + + static void rtw_chk_hi_queue_hdl(_adapter *padapter) +@@ -2666,7 +2060,7 @@ u8 rtw_chk_hi_queue_cmd(_adapter*padapte + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if(pdrvextra_cmd_parm==NULL){ +- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); ++ kfree((unsigned char *)ph2c); + res= _FAIL; + goto exit; + } +@@ -2685,7 +2079,6 @@ exit: + return res; + + } +-#endif + + struct btinfo { + u8 cid; +@@ -2751,50 +2144,6 @@ static void rtw_btinfo_hdl(_adapter *ada + rtw_btcoex_BtInfoNotify(adapter ,len+1, &buf[1]); + } + +-u8 rtw_btinfo_cmd(_adapter *adapter, u8 *buf, u16 len) +-{ +- struct cmd_obj *ph2c; +- struct drvextra_cmd_parm *pdrvextra_cmd_parm; +- u8 *btinfo; +- struct cmd_priv *pcmdpriv = &adapter->cmdpriv; +- u8 res = _SUCCESS; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if (ph2c == NULL) { +- res = _FAIL; +- goto exit; +- } +- +- pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); +- if (pdrvextra_cmd_parm == NULL) { +- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); +- res = _FAIL; +- goto exit; +- } +- +- btinfo = rtw_zmalloc(len); +- if (btinfo == NULL) { +- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); +- rtw_mfree((u8*)pdrvextra_cmd_parm, sizeof(struct drvextra_cmd_parm)); +- res = _FAIL; +- goto exit; +- } +- +- pdrvextra_cmd_parm->ec_id = BTINFO_WK_CID; +- pdrvextra_cmd_parm->type = 0; +- pdrvextra_cmd_parm->size = len; +- pdrvextra_cmd_parm->pbuf = btinfo; +- +- memcpy(btinfo, buf, len); +- +- init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +- +-exit: +- return res; +-} +- + u8 rtw_c2h_packet_wk_cmd(PADAPTER padapter, u8 *pbuf, u16 length) + { + struct cmd_obj *ph2c; +@@ -2810,7 +2159,7 @@ u8 rtw_c2h_packet_wk_cmd(PADAPTER padapt + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if (pdrvextra_cmd_parm == NULL) { +- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); ++ kfree((u8*)ph2c); + res = _FAIL; + goto exit; + } +@@ -2845,7 +2194,7 @@ u8 rtw_c2h_wk_cmd(PADAPTER padapter, u8 + + pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); + if (pdrvextra_cmd_parm == NULL) { +- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); ++ kfree((u8*)ph2c); + res = _FAIL; + goto exit; + } +@@ -2864,42 +2213,6 @@ exit: + return res; + } + +-u8 rtw_run_in_thread_cmd(PADAPTER padapter, void (*func)(void*), void* context) +-{ +- struct cmd_priv *pcmdpriv; +- struct cmd_obj *ph2c; +- struct RunInThread_param *parm; +- s32 res = _SUCCESS; +- +-_func_enter_; +- +- pcmdpriv = &padapter->cmdpriv; +- +- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); +- if (NULL == ph2c) { +- res = _FAIL; +- goto exit; +- } +- +- parm = (struct RunInThread_param*)rtw_zmalloc(sizeof(struct RunInThread_param)); +- if (NULL == parm) { +- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); +- res = _FAIL; +- goto exit; +- } +- +- parm->func = func; +- parm->context = context; +- init_h2fwcmd_w_parm_no_rsp(ph2c, parm, GEN_CMD_CODE(_RunInThreadCMD)); +- +- res = rtw_enqueue_cmd(pcmdpriv, ph2c); +-exit: +- +-_func_exit_; +- +- return res; +-} +- + static void c2h_wk_callback(_workitem *work) + { + struct evt_priv *evtpriv = container_of(work, struct evt_priv, c2h_wk); +@@ -2916,7 +2229,7 @@ static void c2h_wk_callback(_workitem *w + } else if ((c2h_evt = (u8 *)rtw_malloc(16)) != NULL) { + /* This C2H event is not read, read & clear now */ + if (rtw_hal_c2h_evt_read(adapter, c2h_evt) != _SUCCESS) { +- rtw_mfree(c2h_evt, 16); ++ kfree(c2h_evt); + continue; + } + } +@@ -2926,14 +2239,14 @@ static void c2h_wk_callback(_workitem *w + continue; + + if (!rtw_hal_c2h_valid(adapter, c2h_evt)) { +- rtw_mfree(c2h_evt, 16); ++ kfree(c2h_evt); + continue; + } + + if (ccx_id_filter(c2h_evt) == true) { + /* Handle CCX report here */ + rtw_hal_c2h_handler(adapter, c2h_evt); +- rtw_mfree(c2h_evt, 16); ++ kfree(c2h_evt); + } else { + /* Enqueue into cmd_thread for others */ + rtw_c2h_wk_cmd(adapter, c2h_evt); +@@ -2969,11 +2282,9 @@ u8 rtw_drvextra_cmd_hdl(_adapter *padapt + case LPS_CHANGE_DTIM_CID: + rtw_lps_change_dtim_hdl(padapter, (u8)pdrvextra_cmd->type); + break; +-#ifdef CONFIG_AP_MODE + case CHECK_HIQ_WK_CID: + rtw_chk_hi_queue_hdl(padapter); + break; +-#endif //CONFIG_AP_MODE + #ifdef CONFIG_INTEL_WIDI + case INTEl_WIDI_WK_CID: + intel_widi_wk_hdl(padapter, pdrvextra_cmd->type, pdrvextra_cmd->pbuf); +@@ -3001,7 +2312,7 @@ u8 rtw_drvextra_cmd_hdl(_adapter *padapt + + if (pdrvextra_cmd->pbuf && pdrvextra_cmd->size>0) + { +- rtw_mfree(pdrvextra_cmd->pbuf, pdrvextra_cmd->size); ++ kfree(pdrvextra_cmd->pbuf); + } + + return H2C_SUCCESS; +@@ -3038,9 +2349,9 @@ _func_enter_; + + if (pcmd->res != H2C_SUCCESS) + { +- _enter_critical_bh(&pmlmepriv->lock, &irqL); ++ spin_lock_bh(&pmlmepriv->lock); + set_fwstate(pmlmepriv, _FW_LINKED); +- _exit_critical_bh(&pmlmepriv->lock, &irqL); ++ spin_unlock_bh(&pmlmepriv->lock); + + RT_TRACE(_module_rtl871x_cmd_c_,_drv_err_,("\n ***Error: disconnect_cmd_callback Fail ***\n.")); + +@@ -3100,7 +2411,7 @@ _func_enter_; + + _cancel_timer(&pmlmepriv->assoc_timer, &timer_cancelled); + +- _enter_critical_bh(&pmlmepriv->lock, &irqL); ++ spin_lock_bh(&pmlmepriv->lock); + + + if(check_fwstate(pmlmepriv, WIFI_AP_STATE) ) +@@ -3123,28 +2434,28 @@ _func_enter_; + _irqL irqL; + + pwlan = _rtw_alloc_network(pmlmepriv); +- _enter_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); ++ spin_lock_bh(&(pmlmepriv->scanned_queue.lock)); + if ( pwlan == NULL) + { + pwlan = rtw_get_oldest_wlan_network(&pmlmepriv->scanned_queue); + if( pwlan == NULL) + { + RT_TRACE(_module_rtl871x_cmd_c_,_drv_err_,("\n Error: can't get pwlan in rtw_joinbss_event_callback \n")); +- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); ++ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); + goto createbss_cmd_fail; + } + pwlan->last_scanned = jiffies; + } + else + { +- rtw_list_insert_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); ++ list_add_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); + } + + pnetwork->Length = get_WLAN_BSSID_EX_sz(pnetwork); + memcpy(&(pwlan->network), pnetwork, pnetwork->Length); + //pwlan->fixed = true; + +- //rtw_list_insert_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); ++ //list_add_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); + + // copy pdev_network information to pmlmepriv->cur_network + memcpy(&tgt_network->network, pnetwork, (get_WLAN_BSSID_EX_sz(pnetwork))); +@@ -3154,14 +2465,14 @@ _func_enter_; + + _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); + +- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); ++ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); + // we will set _FW_LINKED when there is one more sat to join us (rtw_stassoc_event_callback) + + } + + createbss_cmd_fail: + +- _exit_critical_bh(&pmlmepriv->lock, &irqL); ++ spin_unlock_bh(&pmlmepriv->lock); + exit: + rtw_free_cmd_obj(pcmd); + +@@ -3214,28 +2525,16 @@ _func_enter_; + + psta->aid = psta->mac_id = passocsta_rsp->cam_id; + +- _enter_critical_bh(&pmlmepriv->lock, &irqL); ++ spin_lock_bh(&pmlmepriv->lock); + + if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) && (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true)) + _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); + + set_fwstate(pmlmepriv, _FW_LINKED); +- _exit_critical_bh(&pmlmepriv->lock, &irqL); ++ spin_unlock_bh(&pmlmepriv->lock); + + exit: + rtw_free_cmd_obj(pcmd); + + _func_exit_; + } +- +-void rtw_getrttbl_cmd_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd); +-void rtw_getrttbl_cmd_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd) +-{ +-_func_enter_; +- +- rtw_free_cmd_obj(pcmd); +- +-_func_exit_; +- +-} +- +diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_debug.c linux/3rdparty/rtl8723bs/core/rtw_debug.c +--- linux/3rdparty/rtl8723bs.old/core/rtw_debug.c 2015-05-10 14:36:05.666667879 +0300 ++++ linux/3rdparty/rtl8723bs/core/rtw_debug.c 2015-05-07 17:46:55.000000000 +0300 +@@ -62,7 +62,7 @@ u32 GlobalDebugLevel = _drv_err_; + + void dump_drv_version(void *sel) + { +- DBG_871X_SEL_NL(sel, "%s %s\n", DRV_NAME, DRIVERVERSION); ++ DBG_871X_SEL_NL(sel, "%s %s\n", "rtl8723bs", DRIVERVERSION); + } + + void dump_log_level(void *sel) +@@ -309,7 +309,6 @@ int proc_get_mlmext_state(struct seq_fil + return 0; + } + +-#ifdef CONFIG_LAYER2_ROAMING + int proc_get_roam_flags(struct seq_file *m, void *v) + { + struct net_device *dev = m->private; +@@ -411,7 +410,6 @@ ssize_t proc_set_roam_tgt_addr(struct fi + + return count; + } +-#endif /* CONFIG_LAYER2_ROAMING */ + + int proc_get_qos_option(struct seq_file *m, void *v) + { +@@ -463,18 +461,19 @@ int proc_get_survey_info(struct seq_file + s16 notify_noise = 0; + u16 index = 0; + +- _enter_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); ++ spin_lock_bh(&(pmlmepriv->scanned_queue.lock)); + phead = get_list_head(queue); ++ plist = phead ? get_next(phead) : NULL; + plist = get_next(phead); + if ((!phead) || (!plist)) { +- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); ++ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); + return 0; + } + + DBG_871X_SEL_NL(m, "%5s %-17s %3s %-3s %-4s %-4s %5s %s\n","index", "bssid", "ch", "RSSI", "SdBm", "Noise", "age", "ssid"); + while(1) + { +- if (rtw_end_of_queue_search(phead,plist)== true) ++ if (phead == plist) + break; + + pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list); +@@ -504,7 +503,7 @@ int proc_get_survey_info(struct seq_file + pnetwork->network.Ssid.Ssid); + plist = get_next(plist); + } +- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); ++ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); + + return 0; + } +@@ -1325,9 +1324,6 @@ ssize_t proc_set_rssi_disp(struct file * + + } + +- +-#ifdef CONFIG_AP_MODE +- + int proc_get_all_sta_info(struct seq_file *m, void *v) + { + struct net_device *dev = m->private; +@@ -1341,14 +1337,14 @@ int proc_get_all_sta_info(struct seq_fil + + DBG_871X_SEL_NL(m, "sta_dz_bitmap=0x%x, tim_bitmap=0x%x\n", pstapriv->sta_dz_bitmap, pstapriv->tim_bitmap); + +- _enter_critical_bh(&pstapriv->sta_hash_lock, &irqL); ++ spin_lock_bh(&pstapriv->sta_hash_lock); + + for(i=0; i< NUM_STA; i++) + { + phead = &(pstapriv->sta_hash[i]); + plist = get_next(phead); + +- while ((rtw_end_of_queue_search(phead, plist)) == false) ++ while (phead != plist) + { + psta = LIST_CONTAINOR(plist, struct sta_info, hash_list); + +@@ -1394,12 +1390,10 @@ int proc_get_all_sta_info(struct seq_fil + + } + +- _exit_critical_bh(&pstapriv->sta_hash_lock, &irqL); ++ spin_unlock_bh(&pstapriv->sta_hash_lock); + + return 0; + } +- +-#endif + + int proc_get_btcoex_dbg(struct seq_file *m, void *v) + { +@@ -1497,7 +1491,7 @@ int proc_get_btcoex_info(struct seq_file + + DBG_871X_SEL(m, "%s\n", pbuf); + +- rtw_mfree(pbuf, bufsize); ++ kfree(pbuf); + + return 0; + } +diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_eeprom.c linux/3rdparty/rtl8723bs/core/rtw_eeprom.c +--- linux/3rdparty/rtl8723bs.old/core/rtw_eeprom.c 2015-05-10 14:36:05.666667879 +0300 ++++ linux/3rdparty/rtl8723bs/core/rtw_eeprom.c 1970-01-01 02:00:00.000000000 +0200 +@@ -1,381 +0,0 @@ +-/****************************************************************************** +- * +- * 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 _RTW_EEPROM_C_ +- +-#include +-#include +-#include +- +-void up_clk(_adapter* padapter, u16 *x) +-{ +-_func_enter_; +- *x = *x | _EESK; +- rtw_write8(padapter, EE_9346CR, (u8)*x); +- udelay(CLOCK_RATE); +- +-_func_exit_; +- +-} +- +-void down_clk(_adapter * padapter, u16 *x ) +-{ +-_func_enter_; +- *x = *x & ~_EESK; +- rtw_write8(padapter, EE_9346CR, (u8)*x); +- udelay(CLOCK_RATE); +-_func_exit_; +-} +- +-void shift_out_bits(_adapter * padapter, u16 data, u16 count) +-{ +- u16 x,mask; +-_func_enter_; +- +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- mask = 0x01 << (count - 1); +- x = rtw_read8(padapter, EE_9346CR); +- +- x &= ~(_EEDO | _EEDI); +- +- do +- { +- x &= ~_EEDI; +- if(data & mask) +- x |= _EEDI; +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- rtw_write8(padapter, EE_9346CR, (u8)x); +- udelay(CLOCK_RATE); +- up_clk(padapter, &x); +- down_clk(padapter, &x); +- mask = mask >> 1; +- } while(mask); +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- x &= ~_EEDI; +- rtw_write8(padapter, EE_9346CR, (u8)x); +-out: +-_func_exit_; +-} +- +-u16 shift_in_bits (_adapter * padapter) +-{ +- u16 x,d=0,i; +-_func_enter_; +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- x = rtw_read8(padapter, EE_9346CR); +- +- x &= ~( _EEDO | _EEDI); +- d = 0; +- +- for(i=0; i<16; i++) +- { +- d = d << 1; +- up_clk(padapter, &x); +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- x = rtw_read8(padapter, EE_9346CR); +- +- x &= ~(_EEDI); +- if(x & _EEDO) +- d |= 1; +- +- down_clk(padapter, &x); +- } +-out: +-_func_exit_; +- +- return d; +-} +- +-void standby(_adapter * padapter ) +-{ +- u8 x; +-_func_enter_; +- x = rtw_read8(padapter, EE_9346CR); +- +- x &= ~(_EECS | _EESK); +- rtw_write8(padapter, EE_9346CR,x); +- +- udelay(CLOCK_RATE); +- x |= _EECS; +- rtw_write8(padapter, EE_9346CR, x); +- udelay(CLOCK_RATE); +-_func_exit_; +-} +- +-u16 wait_eeprom_cmd_done(_adapter* padapter) +-{ +- u8 x; +- u16 i,res=false; +-_func_enter_; +- standby(padapter ); +- for (i=0; i<200; i++) +- { +- x = rtw_read8(padapter, EE_9346CR); +- if (x & _EEDO){ +- res=true; +- goto exit; +- } +- udelay(CLOCK_RATE); +- } +-exit: +-_func_exit_; +- return res; +-} +- +-void eeprom_clean(_adapter * padapter) +-{ +- u16 x; +-_func_enter_; +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- x = rtw_read8(padapter, EE_9346CR); +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- x &= ~(_EECS | _EEDI); +- rtw_write8(padapter, EE_9346CR, (u8)x); +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- up_clk(padapter, &x); +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- down_clk(padapter, &x); +-out: +-_func_exit_; +-} +- +-void eeprom_write16(_adapter * padapter, u16 reg, u16 data) +-{ +- u8 x; +- +-_func_enter_; +- +- x = rtw_read8(padapter, EE_9346CR); +- +- x &= ~(_EEDI | _EEDO | _EESK | _EEM0); +- x |= _EEM1 | _EECS; +- rtw_write8(padapter, EE_9346CR, x); +- +- shift_out_bits(padapter, EEPROM_EWEN_OPCODE, 5); +- +- if(padapter->EepromAddressSize==8) //CF+ and SDIO +- shift_out_bits(padapter, 0, 6); +- else //USB +- shift_out_bits(padapter, 0, 4); +- +- standby( padapter); +- +-// Commented out by rcnjko, 2004.0 +-// // Erase this particular word. Write the erase opcode and register +-// // number in that order. The opcode is 3bits in length; reg is 6 bits long. +-// shift_out_bits(Adapter, EEPROM_ERASE_OPCODE, 3); +-// shift_out_bits(Adapter, reg, Adapter->EepromAddressSize); +-// +-// if (wait_eeprom_cmd_done(Adapter ) == false) +-// { +-// return; +-// } +- +- +- standby(padapter ); +- +- // write the new word to the EEPROM +- +- // send the write opcode the EEPORM +- shift_out_bits(padapter, EEPROM_WRITE_OPCODE, 3); +- +- // select which word in the EEPROM that we are writing to. +- shift_out_bits(padapter, reg, padapter->EepromAddressSize); +- +- // write the data to the selected EEPROM word. +- shift_out_bits(padapter, data, 16); +- +- if (wait_eeprom_cmd_done(padapter ) == false) +- { +- +- goto exit; +- } +- +- standby(padapter ); +- +- shift_out_bits(padapter, EEPROM_EWDS_OPCODE, 5); +- shift_out_bits(padapter, reg, 4); +- +- eeprom_clean(padapter ); +-exit: +-_func_exit_; +- return; +-} +- +-u16 eeprom_read16(_adapter * padapter, u16 reg) //ReadEEprom +-{ +- +- u16 x; +- u16 data=0; +- +-_func_enter_; +- +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- // select EEPROM, reset bits, set _EECS +- x = rtw_read8(padapter, EE_9346CR); +- +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- +- x &= ~(_EEDI | _EEDO | _EESK | _EEM0); +- x |= _EEM1 | _EECS; +- rtw_write8(padapter, EE_9346CR, (unsigned char)x); +- +- // write the read opcode and register number in that order +- // The opcode is 3bits in length, reg is 6 bits long +- shift_out_bits(padapter, EEPROM_READ_OPCODE, 3); +- shift_out_bits(padapter, reg, padapter->EepromAddressSize); +- +- // Now read the data (16 bits) in from the selected EEPROM word +- data = shift_in_bits(padapter); +- +- eeprom_clean(padapter); +-out: +-_func_exit_; +- return data; +- +- +-} +- +- +- +- +-//From even offset +-void eeprom_read_sz(_adapter * padapter, u16 reg, u8* data, u32 sz) +-{ +- +- u16 x, data16; +- u32 i; +-_func_enter_; +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- // select EEPROM, reset bits, set _EECS +- x = rtw_read8(padapter, EE_9346CR); +- +- if(padapter->bSurpriseRemoved==true){ +- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); +- goto out; +- } +- +- x &= ~(_EEDI | _EEDO | _EESK | _EEM0); +- x |= _EEM1 | _EECS; +- rtw_write8(padapter, EE_9346CR, (unsigned char)x); +- +- // write the read opcode and register number in that order +- // The opcode is 3bits in length, reg is 6 bits long +- shift_out_bits(padapter, EEPROM_READ_OPCODE, 3); +- shift_out_bits(padapter, reg, padapter->EepromAddressSize); +- +- +- for(i=0; i>8; +- } +- +- eeprom_clean(padapter); +-out: +-_func_exit_; +- +- +- +-} +- +- +-//addr_off : address offset of the entry in eeprom (not the tuple number of eeprom (reg); that is addr_off !=reg) +-u8 eeprom_read(_adapter * padapter, u32 addr_off, u8 sz, u8* rbuf) +-{ +- u8 quotient, remainder, addr_2align_odd; +- u16 reg, stmp , i=0, idx = 0; +-_func_enter_; +- reg = (u16)(addr_off >> 1); +- addr_2align_odd = (u8)(addr_off & 0x1); +- +- if(addr_2align_odd) //read that start at high part: e.g 1,3,5,7,9,... +- { +- stmp = eeprom_read16(padapter, reg); +- rbuf[idx++] = (u8) ((stmp>>8)&0xff); //return hogh-part of the short +- reg++; sz--; +- } +- +- quotient = sz >> 1; +- remainder = sz & 0x1; +- +- for( i=0 ; i < quotient; i++) +- { +- stmp = eeprom_read16(padapter, reg+i); +- rbuf[idx++] = (u8) (stmp&0xff); +- rbuf[idx++] = (u8) ((stmp>>8)&0xff); +- } +- +- reg = reg+i; +- if(remainder){ //end of read at lower part of short : 0,2,4,6,... +- stmp = eeprom_read16(padapter, reg); +- rbuf[idx] = (u8)(stmp & 0xff); +- } +-_func_exit_; +- return true; +-} +- +- +- +-void read_eeprom_content(_adapter * padapter) +-{ +- +-_func_enter_; +- +- +-_func_exit_; +-} +- +diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_efuse.c linux/3rdparty/rtl8723bs/core/rtw_efuse.c +--- linux/3rdparty/rtl8723bs.old/core/rtw_efuse.c 2015-05-10 14:36:05.666667879 +0300 ++++ linux/3rdparty/rtl8723bs/core/rtw_efuse.c 2015-05-07 17:46:55.000000000 +0300 +@@ -121,18 +121,8 @@ Efuse_PowerSwitch( + pAdapter->HalFunc.EfusePowerSwitch(pAdapter, bWrite, PwrState); + } + +-void +-BTEfuse_PowerSwitch( +- IN PADAPTER pAdapter, +- IN u8 bWrite, +- IN u8 PwrState) +-{ +- if(pAdapter->HalFunc.BTEfusePowerSwitch) +- pAdapter->HalFunc.BTEfusePowerSwitch(pAdapter, bWrite, PwrState); +-} +- + /*----------------------------------------------------------------------------- +- * Function: efuse_GetCurrentSize ++ * Function: Efuse_GetCurrentSize + * + * Overview: Get current efuse size!!! + * +@@ -174,68 +164,6 @@ Efuse_CalculateWordCnts(IN u8 word_en) + + // + // Description: +-// Execute E-Fuse read byte operation. +-// Refered from SD1 Richard. +-// +-// Assumption: +-// 1. Boot from E-Fuse and successfully auto-load. +-// 2. PASSIVE_LEVEL (USB interface) +-// +-// Created by Roger, 2008.10.21. +-// +-void +-ReadEFuseByte( +- PADAPTER Adapter, +- u16 _offset, +- u8 *pbuf, +- IN bool bPseudoTest) +-{ +- u32 value32; +- u8 readbyte; +- u16 retry; +- +- if(bPseudoTest) +- { +- Efuse_Read1ByteFromFakeContent(Adapter, _offset, pbuf); +- return; +- } +- // <20130121, Kordan> For SMIC S55 EFUSE specificatoin. +- //0x34[11]: SW force PGMEN input of efuse to high. (for the bank selected by 0x34[9:8]) +- PHY_SetMacReg(Adapter, EFUSE_TEST, BIT11, 0); +- +- //Write Address +- rtw_write8(Adapter, EFUSE_CTRL+1, (_offset & 0xff)); +- readbyte = rtw_read8(Adapter, EFUSE_CTRL+2); +- rtw_write8(Adapter, EFUSE_CTRL+2, ((_offset >> 8) & 0x03) | (readbyte & 0xfc)); +- +- //Write bit 32 0 +- readbyte = rtw_read8(Adapter, EFUSE_CTRL+3); +- rtw_write8(Adapter, EFUSE_CTRL+3, (readbyte & 0x7f)); +- +- //Check bit 32 read-ready +- retry = 0; +- value32 = rtw_read32(Adapter, EFUSE_CTRL); +- //while(!(((value32 >> 24) & 0xff) & 0x80) && (retry<10)) +- while(!(((value32 >> 24) & 0xff) & 0x80) && (retry<10000)) +- { +- value32 = rtw_read32(Adapter, EFUSE_CTRL); +- retry++; +- } +- +- // 20100205 Joseph: Add delay suggested by SD1 Victor. +- // This fix the problem that Efuse read error in high temperature condition. +- // Designer says that there shall be some delay after ready bit is set, or the +- // result will always stay on last data we read. +- udelay(50); +- value32 = rtw_read32(Adapter, EFUSE_CTRL); +- +- *pbuf = (u8)(value32 & 0xff); +- //DBG_871X("ReadEFuseByte _offset:%08u, in %d ms\n",_offset ,jiffies_to_msecs(jiffies - start)); +- +-} +- +-// +-// Description: + // 1. Execute E-Fuse read byte operation according as map offset and + // save to E-Fuse table. + // 2. Refered from SD1 Richard. +@@ -350,76 +278,6 @@ EFUSE_Read1Byte( + + }/* EFUSE_Read1Byte */ + +-/*----------------------------------------------------------------------------- +- * Function: EFUSE_Write1Byte +- * +- * Overview: Copy from WMAC fot EFUSE write 1 byte. +- * +- * Input: NONE +- * +- * Output: NONE +- * +- * Return: NONE +- * +- * Revised History: +- * When Who Remark +- * 09/23/2008 MHC Copy from WMAC. +- * +- *---------------------------------------------------------------------------*/ +- +-void +-EFUSE_Write1Byte( +- IN PADAPTER Adapter, +- IN u16 Address, +- IN u8 Value); +-void +-EFUSE_Write1Byte( +- IN PADAPTER Adapter, +- IN u16 Address, +- IN u8 Value) +-{ +- u8 Bytetemp = {0x00}; +- u8 temp = {0x00}; +- u32 k=0; +- u16 contentLen=0; +- +- //RT_TRACE(COMP_EFUSE, DBG_LOUD, ("Addr=%x Data =%x\n", Address, Value)); +- EFUSE_GetEfuseDefinition(Adapter, EFUSE_WIFI , TYPE_EFUSE_REAL_CONTENT_LEN, (void *)&contentLen, false); +- +- if( Address < contentLen) //E-fuse 512Byte +- { +- rtw_write8(Adapter, EFUSE_CTRL, Value); +- +- //Write E-fuse Register address bit0~7 +- temp = Address & 0xFF; +- rtw_write8(Adapter, EFUSE_CTRL+1, temp); +- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+2); +- +- //Write E-fuse Register address bit8~9 +- temp = ((Address >> 8) & 0x03) | (Bytetemp & 0xFC); +- rtw_write8(Adapter, EFUSE_CTRL+2, temp); +- +- //Write 0x30[31]=1 +- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+3); +- temp = Bytetemp | 0x80; +- rtw_write8(Adapter, EFUSE_CTRL+3, temp); +- +- //Wait Write-ready (0x30[31]=0) +- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+3); +- while(Bytetemp & 0x80) +- { +- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+3); +- k++; +- if(k==100) +- { +- k=0; +- break; +- } +- } +- } +-}/* EFUSE_Write1Byte */ +- +- + /* 11/16/2008 MH Read one byte from real Efuse. */ + u8 + efuse_OneByteRead( +@@ -642,289 +500,6 @@ Efuse_WordEnableDataWrite( IN PADAPTER p + return ret; + } + +-static u8 efuse_read8(PADAPTER padapter, u16 address, u8 *value) +-{ +- return efuse_OneByteRead(padapter,address, value, false); +-} +- +-static u8 efuse_write8(PADAPTER padapter, u16 address, u8 *value) +-{ +- return efuse_OneByteWrite(padapter,address, *value, false); +-} +- +-/* +- * read/wirte raw efuse data +- */ +-u8 rtw_efuse_access(PADAPTER padapter, u8 bWrite, u16 start_addr, u16 cnts, u8 *data) +-{ +- int i = 0; +- u16 real_content_len = 0, max_available_size = 0; +- u8 res = _FAIL ; +- u8 (*rw8)(PADAPTER, u16, u8*); +- +- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_REAL_CONTENT_LEN, (void *)&real_content_len, false); +- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); +- +- if (start_addr > real_content_len) +- return _FAIL; +- +- if (true == bWrite) { +- if ((start_addr + cnts) > max_available_size) +- return _FAIL; +- rw8 = &efuse_write8; +- } else +- rw8 = &efuse_read8; +- +- Efuse_PowerSwitch(padapter, bWrite, true); +- +- // e-fuse one byte read / write +- for (i = 0; i < cnts; i++) { +- if (start_addr >= real_content_len) { +- res = _FAIL; +- break; +- } +- +- res = rw8(padapter, start_addr++, data++); +- if (_FAIL == res) break; +- } +- +- Efuse_PowerSwitch(padapter, bWrite, false); +- +- return res; +-} +-//------------------------------------------------------------------------------ +-u16 efuse_GetMaxSize(PADAPTER padapter) +-{ +- u16 max_size; +- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI , TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_size, false); +- return max_size; +-} +-//------------------------------------------------------------------------------ +-u8 efuse_GetCurrentSize(PADAPTER padapter, u16 *size) +-{ +- Efuse_PowerSwitch(padapter, false, true); +- *size = Efuse_GetCurrentSize(padapter, EFUSE_WIFI, false); +- Efuse_PowerSwitch(padapter, false, false); +- +- return _SUCCESS; +-} +-//------------------------------------------------------------------------------ +-u8 rtw_efuse_map_read(PADAPTER padapter, u16 addr, u16 cnts, u8 *data) +-{ +- u16 mapLen=0; +- +- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_MAP_LEN, (void *)&mapLen, false); +- +- if ((addr + cnts) > mapLen) +- return _FAIL; +- +- Efuse_PowerSwitch(padapter, false, true); +- +- efuse_ReadEFuse(padapter, EFUSE_WIFI, addr, cnts, data, false); +- +- Efuse_PowerSwitch(padapter, false, false); +- +- return _SUCCESS; +-} +- +-u8 rtw_BT_efuse_map_read(PADAPTER padapter, u16 addr, u16 cnts, u8 *data) +-{ +- u16 mapLen=0; +- +- EFUSE_GetEfuseDefinition(padapter, EFUSE_BT, TYPE_EFUSE_MAP_LEN, (void *)&mapLen, false); +- +- if ((addr + cnts) > mapLen) +- return _FAIL; +- +- Efuse_PowerSwitch(padapter, false, true); +- +- efuse_ReadEFuse(padapter, EFUSE_BT, addr, cnts, data, false); +- +- Efuse_PowerSwitch(padapter, false, false); +- +- return _SUCCESS; +-} +-//------------------------------------------------------------------------------ +-u8 rtw_efuse_map_write(PADAPTER padapter, u16 addr, u16 cnts, u8 *data) +-{ +-#define RT_ASSERT_RET(expr) \ +- if(!(expr)) { \ +- printk( "Assertion failed! %s at ......\n", #expr); \ +- printk( " ......%s,%s,line=%d\n",__FILE__,__FUNCTION__,__LINE__); \ +- return _FAIL; \ +- } +- +- u8 offset, word_en; +- u8 *map; +- u8 newdata[PGPKT_DATA_SIZE]; +- s32 i, j, idx; +- u8 ret = _SUCCESS; +- u16 mapLen=0; +- EEPROM_EFUSE_PRIV *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); +- HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter); +- +- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_MAP_LEN, (void *)&mapLen, false); +- +- if ((addr + cnts) > mapLen) +- return _FAIL; +- +- RT_ASSERT_RET(PGPKT_DATA_SIZE == 8); // have to be 8 byte alignment +- RT_ASSERT_RET((mapLen & 0x7) == 0); // have to be PGPKT_DATA_SIZE alignment for memcpy +- +- map = rtw_zmalloc(mapLen); +- if(map == NULL){ +- return _FAIL; +- } +- +- memset(map, 0xFF, mapLen); +- +- ret = rtw_efuse_map_read(padapter, 0, mapLen, map); +- if (ret == _FAIL) goto exit; +- +- Efuse_PowerSwitch(padapter, true, true); +- +- idx = 0; +- offset = (addr >> 3); +- while (idx < cnts) +- { +- word_en = 0xF; +- j = (addr + idx) & 0x7; +- memcpy(newdata, &map[offset << 3], PGPKT_DATA_SIZE); +- for (i = j; i> 1); +- newdata[i] = data[idx]; +- +- if( addr + idx == 0x8) +- { +- if (IS_C_CUT(pHalData->VersionID) || IS_B_CUT(pHalData->VersionID)) +- { +- if(pEEPROM->adjuseVoltageVal == 6) +- { +- newdata[i] = map[addr + idx]; +- DBG_8192C(" %s ,\n adjuseVoltageVal = %d ,newdata[%d] = %x \n",__func__,pEEPROM->adjuseVoltageVal,i,newdata[i]); +- } +- } +- } +- } +- } +- +- if (word_en != 0xF) { +- ret = Efuse_PgPacketWrite(padapter, offset, word_en, newdata, false); +- DBG_871X("offset=%x \n",offset); +- DBG_871X("word_en=%x \n",word_en); +- +- for(i=0;i mapLen) +- return _FAIL; +- +- RT_ASSERT_RET(PGPKT_DATA_SIZE == 8); // have to be 8 byte alignment +- RT_ASSERT_RET((mapLen & 0x7) == 0); // have to be PGPKT_DATA_SIZE alignment for memcpy +- +- map = rtw_zmalloc(mapLen); +- if(map == NULL){ +- return _FAIL; +- } +- +- ret = rtw_BT_efuse_map_read(padapter, 0, mapLen, map); +- if (ret == _FAIL) goto exit; +- DBG_871X("OFFSET\tVALUE(hex)\n"); +- for (i=0; i<1024; i+=16) // set 512 because the iwpriv's extra size have limit 0x7FF +- { +- DBG_871X("0x%03x\t", i); +- for (j=0; j<8; j++) { +- DBG_871X("%02X ", map[i+j]); +- } +- DBG_871X("\t"); +- for (; j<16; j++) { +- DBG_871X("%02X ", map[i+j]); +- } +- DBG_871X("\n"); +- } +- DBG_871X("\n"); +- Efuse_PowerSwitch(padapter, true, true); +- +- idx = 0; +- offset = (addr >> 3); +- while (idx < cnts) +- { +- word_en = 0xF; +- j = (addr + idx) & 0x7; +- memcpy(newdata, &map[offset << 3], PGPKT_DATA_SIZE); +- for (i = j; i> 1); +- newdata[i] = data[idx]; +- } +- } +- +- if (word_en != 0xF) { +- DBG_871X("offset=%x \n",offset); +- DBG_871X("word_en=%x \n",word_en); +- DBG_871X("%s: data=", __FUNCTION__); +- for(i=0;i

Setting the serial port speed over 115,200bps

++
++If you have motherboard which has VIA VT82C686A or VT82C686B chipset ++you can set serial ports in high speed mode with this kernel module. ++I use module with external ISDN-TA and haven't had any problems ++so far but I cannot guarantee that you won't have buffer overflows if ++you use full 230K or 460K speed all the time (FIFO's are still 16550A size). ++ISDN with two channels maxes out at 128 Kb which means that it doesn't ++really stress serial ports at 230400. Unlike SHSMOD-patches you don't have ++to patch serial driver and module takes only 1KB of memory when it's ++loaded which should leave enough room for other programs. It should ++be also possible to make this work from userspace but accessing ++pci devices is so much easier from kernel. ++ ++

How to use module

++Get the
package ++and compile it using included makefiles. ++

For 2.4

++If you have kernel in some other location than /usr/src/linux edit Makefile ++before compiling. You can also install module with "make install". After ++you have loaded module in kernel (use modprobe or insmod) you can set serial ++ports to use high speed modes with setserial.
++

For 2.5/2.6

++Copy Makefile-2.6 on top of Makefile and do make. After loading module set ++serial speed with setserial. (NOTE: This gives a warning on depracated ++method). ++ ++
++# setserial /dev/ttyS0 spd_cust divisor 0x8002 ++

++which sets COM1: speed to 230400. With 0x8001 you should get 460800 ++but I haven't tested it. If you want to use COM2: use ttyS1 instead of ttyS0. ++ ++After this you should set program which you are using to use 38400 bps ++speed which is now actually 230K or 460K. For more information check ++setserial man page (spd_cust). ++ ++You can use serial port work as normal if you do
++
++# setserial /dev/ttyS0 spd_normal

++After this you can also remove viahss module with rmmod if you need to. Module doesn't intefere with normal serial port usage so you can leave ++it loaded if you don't need that extra 1KB which module uses. ++

Download

++viahss-0.92.tar.gz ++ ++

Acknowledgments

++ ++Thanks to Kimmo Rintala for help with divisor settings.
++I also like to thank Jeff Garzik for help with VIA datasheets.
++Port to 2.5/2.6 by Kingsly John with the help of LWN ++ ++

Version History

++0.90 First release
++0.91 Fixed Makefile
++0.92 Fixed for 2.5/2.6
++ ++

Contact

++ ++You can reach me by email: jrauti@iki.fi ++ ++ +diff -Nurp linux-2.6.37/3rdparty/viahss/viahss.c linux-2.6.37.3rdparty/3rdparty/viahss/viahss.c +--- linux-2.6.37/3rdparty/viahss/viahss.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3rdparty/3rdparty/viahss/viahss.c 2003-07-17 09:49:59.000000000 +0300 +@@ -0,0 +1,83 @@ ++/* ++ * VIA VT 82c686[AB] high speed serial port enabler ++ * Version 0.92 ++ * Copyright (c) 2000-2001 Juhani Rautiainen ++ * ++ * 0.92: ++ * Ported to 2.5/2.6 by Kingsly John ++ * - Corrected locking (no more cli() and sti()) ++ * - New makefile ++ * ++ * Can be freely distributed and used under the terms of the GNU GPL. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++const unsigned short confindex=0x3F0,confdata=0x3F1; ++const unsigned char spcidx=0xEE; ++ ++spinlock_t driver_lock = SPIN_LOCK_UNLOCKED; ++ ++static int __init viahss_init(void) ++{ ++ struct pci_dev *pcidev = NULL; ++ unsigned char confval,val; ++ pcidev = pci_find_device (PCI_VENDOR_ID_VIA,PCI_DEVICE_ID_VIA_82C686,NULL); ++ if (pcidev) { ++ spin_lock_irq(&driver_lock); ++ /* start config */ ++ pci_read_config_byte(pcidev,0x85,&confval); ++ confval |= 0x2; ++ pci_write_config_byte (pcidev, 0x85,confval); ++ /* activate high speed bits */ ++ outb(spcidx,confindex); /* set index */ ++ val = (unsigned char) inb(confdata); ++ val |= 0xC0; /* both ports on high speed*/ ++ outb (spcidx,confindex); ++ outb (val,confdata); ++ /*stop config*/ ++ confval &= ~0x2; ++ pci_write_config_byte(pcidev, 0x85, confval); ++ spin_unlock_irq(&driver_lock); ++ printk (KERN_INFO "VIA VT82C686[AB] serial port high speed enabled\n"); ++ } ++ else { ++ printk (KERN_INFO "Couldn't locate VIA chipset\n"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static void __exit viahss_exit(void) ++{ ++ struct pci_dev *pcidev = NULL; ++ unsigned char confval,val; ++ pcidev = pci_find_device (PCI_VENDOR_ID_VIA,PCI_DEVICE_ID_VIA_82C686,NULL); ++ if (pcidev) { ++ spin_lock_irq(&driver_lock); ++ /* start config */ ++ pci_read_config_byte(pcidev,0x85,&confval); ++ confval |= 0x2; ++ pci_write_config_byte (pcidev, 0x85,confval); ++ /* activate high speed bits */ ++ outb(spcidx,confindex); /* set index */ ++ val = (unsigned char) inb(confdata); ++ val &= ~0xC0; /* both ports off high speed*/ ++ outb (spcidx,confindex); ++ outb (val,confdata); ++ /*stop config*/ ++ confval &= ~0x2; ++ pci_write_config_byte(pcidev, 0x85, confval); ++ spin_unlock_irq(&driver_lock); ++ printk (KERN_INFO "VIA VT82C686[AB] serial port high speed disabled\n"); ++ } ++} ++ ++module_init(viahss_init); ++module_exit(viahss_exit); ++MODULE_DESCRIPTION("VIA VT82C686[AB] high speed serial port enabler"); ++MODULE_AUTHOR("Juhani Rautiainen "); diff --git a/kernel/kernel/files/patches/mageia.old/3rd-viahss-2.6.35-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-viahss-2.6.35-buildfix.patch new file mode 100644 index 00000000..5ae42e92 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-viahss-2.6.35-buildfix.patch @@ -0,0 +1,20 @@ +--- linux-2.6.35-rc6-git-mnb0.1/3rdparty/viahss/viahss.c.orig 2010-07-30 21:17:30.000000000 +0300 ++++ linux-2.6.35-rc6-git-mnb0.1/3rdparty/viahss/viahss.c 2010-07-30 23:10:21.324978822 +0300 +@@ -25,7 +25,7 @@ static int __init viahss_init(void) + { + struct pci_dev *pcidev = NULL; + unsigned char confval,val; +- pcidev = pci_find_device (PCI_VENDOR_ID_VIA,PCI_DEVICE_ID_VIA_82C686,NULL); ++ pcidev = pci_get_device (PCI_VENDOR_ID_VIA,PCI_DEVICE_ID_VIA_82C686,NULL); + if (pcidev) { + spin_lock_irq(&driver_lock); + /* start config */ +@@ -55,7 +55,7 @@ static void __exit viahss_exit(void) + { + struct pci_dev *pcidev = NULL; + unsigned char confval,val; +- pcidev = pci_find_device (PCI_VENDOR_ID_VIA,PCI_DEVICE_ID_VIA_82C686,NULL); ++ pcidev = pci_get_device (PCI_VENDOR_ID_VIA,PCI_DEVICE_ID_VIA_82C686,NULL); + if (pcidev) { + spin_lock_irq(&driver_lock); + /* start config */ diff --git a/kernel/kernel/files/patches/mageia.old/3rd-viahss-3.0-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-viahss-3.0-buildfix.patch new file mode 100644 index 00000000..7db73881 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-viahss-3.0-buildfix.patch @@ -0,0 +1,19 @@ + +fix build with 3.0 + +Signed-off-by: Thomas Backlund + + 3rdparty/viahss/viahss.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/3rdparty/viahss/viahss.c.orig 2011-07-15 02:37:27.000000000 +0300 ++++ a/3rdparty/viahss/viahss.c 2011-07-15 13:48:13.204291256 +0300 +@@ -19,7 +19,7 @@ + const unsigned short confindex=0x3F0,confdata=0x3F1; + const unsigned char spcidx=0xEE; + +-spinlock_t driver_lock = SPIN_LOCK_UNLOCKED; ++DEFINE_SPINLOCK(driver_lock); + + static int __init viahss_init(void) + { diff --git a/kernel/kernel/files/patches/mageia.old/3rd-viahss-config.patch b/kernel/kernel/files/patches/mageia.old/3rd-viahss-config.patch new file mode 100644 index 00000000..0d1bdbd3 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-viahss-config.patch @@ -0,0 +1,18 @@ +linux/config.h is gone in 2.6.21 + +--- + 3rdparty/viahss/viahss.c | 1 0 + 1 - 0 ! + 1 files changed, 1 deletion(-) + +Index: linux-2.6.21/3rdparty/viahss/viahss.c +=================================================================== +--- linux-2.6.21.orig/3rdparty/viahss/viahss.c 2003-07-17 08:49:59.000000000 +0200 ++++ linux-2.6.21/3rdparty/viahss/viahss.c 2007-05-14 17:06:56.000000000 +0200 +@@ -12,7 +12,6 @@ + */ + + #include +-#include + #include + #include + #include diff --git a/kernel/kernel/files/patches/mageia.old/3rd-viahss-module-license.patch b/kernel/kernel/files/patches/mageia.old/3rd-viahss-module-license.patch new file mode 100644 index 00000000..540538f2 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rd-viahss-module-license.patch @@ -0,0 +1,7 @@ +--- linux-2.6.25/3rdparty/viahss/viahss.c.orig 2008-06-28 22:50:05.000000000 +0300 ++++ linux-2.6.25/3rdparty/viahss/viahss.c 2008-06-29 14:26:55.000000000 +0300 +@@ -80,3 +80,4 @@ module_init(viahss_init); + module_exit(viahss_exit); + MODULE_DESCRIPTION("VIA VT82C686[AB] high speed serial port enabler"); + MODULE_AUTHOR("Juhani Rautiainen "); ++MODULE_LICENSE("GPL"); diff --git a/kernel/kernel/files/patches/mageia.old/3rdparty.series b/kernel/kernel/files/patches/mageia.old/3rdparty.series new file mode 100644 index 00000000..17ace736 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/3rdparty.series @@ -0,0 +1,3 @@ +# Mageia SVN does not like binaries in /packages tree +# it can still be used for out ov svn builds + diff --git a/kernel/kernel/files/patches/mageia.old/Input-evdev-do-not-report-errors-form-flush.patch b/kernel/kernel/files/patches/mageia.old/Input-evdev-do-not-report-errors-form-flush.patch new file mode 100644 index 00000000..dc0b728c --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/Input-evdev-do-not-report-errors-form-flush.patch @@ -0,0 +1,64 @@ +From eb38f3a4f6e86f8bb10a3217ebd85ecc5d763aae Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 3 Sep 2015 22:20:00 -0700 +Subject: [PATCH] Input: evdev - do not report errors form flush() + +We've got bug reports showing the old systemd-logind (at least +system-210) aborting unexpectedly, and this turned out to be because +of an invalid error code from close() call to evdev devices. close() +is supposed to return only either EINTR or EBADFD, while the device +returned ENODEV. logind was overreacting to it and decided to kill +itself when an unexpected error code was received. What a tragedy. + +The bad error code comes from flush fops, and actually evdev_flush() +returns ENODEV when device is disconnected or client's access to it is +revoked. But in these cases the fact that flush did not actually happen is +not an error, but rather normal behavior. For non-disconnected devices +result of flush is also not that interesting as there is no potential of +data loss and even if it fails application has no way of handling the +error. Because of that we are better off always returning success from +evdev_flush(). + +Also returning EINTR from flush()/close() is discouraged (as it is not +clear how application should handle this error), so let's stop taking +evdev->mutex interruptibly. + +Bugzilla: http://bugzilla.suse.com/show_bug.cgi?id=939834 +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Dmitry Torokhov +--- + drivers/input/evdev.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c +index 9d35499..08d4964 100644 +--- a/drivers/input/evdev.c ++++ b/drivers/input/evdev.c +@@ -290,19 +290,14 @@ static int evdev_flush(struct file *file, fl_owner_t id) + { + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; +- int retval; + +- retval = mutex_lock_interruptible(&evdev->mutex); +- if (retval) +- return retval; ++ mutex_lock(&evdev->mutex); + +- if (!evdev->exist || client->revoked) +- retval = -ENODEV; +- else +- retval = input_flush_device(&evdev->handle, file); ++ if (evdev->exist && !client->revoked) ++ input_flush_device(&evdev->handle, file); + + mutex_unlock(&evdev->mutex); +- return retval; ++ return 0; + } + + static void evdev_free(struct device *dev) +-- +2.3.8 + diff --git a/kernel/kernel/files/patches/mageia.old/Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch b/kernel/kernel/files/patches/mageia.old/Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch new file mode 100644 index 00000000..c3fffdfa --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch @@ -0,0 +1,32 @@ +From 88299efb05387c2c94120fdc9661aab2ec3a1ad8 Mon Sep 17 00:00:00 2001 +From: Thomas Backlund +Date: Fri, 3 Apr 2015 00:10:34 +0259 +Subject: [PATCH] Revert "cpufreq: pcc: Enable autoload of pcc-cpufreq for ACPI + processors" + +This reverts commit 7e7e8fe69820c6fa31395dbbd8e348e3c69cd2a9. +--- + drivers/cpufreq/pcc-cpufreq.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c +index 2a0d589..4d2c8e8 100644 +--- a/drivers/cpufreq/pcc-cpufreq.c ++++ b/drivers/cpufreq/pcc-cpufreq.c +@@ -603,13 +603,6 @@ static void __exit pcc_cpufreq_exit(void) + free_percpu(pcc_cpu_info); + } + +-static const struct acpi_device_id processor_device_ids[] = { +- {ACPI_PROCESSOR_OBJECT_HID, }, +- {ACPI_PROCESSOR_DEVICE_HID, }, +- {}, +-}; +-MODULE_DEVICE_TABLE(acpi, processor_device_ids); +- + MODULE_AUTHOR("Matthew Garrett, Naga Chumbalkar"); + MODULE_VERSION(PCC_VERSION); + MODULE_DESCRIPTION("Processor Clocking Control interface driver"); +-- +2.3.1 + diff --git a/kernel/kernel/files/patches/mageia.old/acpi-CLEVO-M360S-disable_acpi_irq.patch b/kernel/kernel/files/patches/mageia.old/acpi-CLEVO-M360S-disable_acpi_irq.patch new file mode 100644 index 00000000..1ac3254c --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/acpi-CLEVO-M360S-disable_acpi_irq.patch @@ -0,0 +1,28 @@ +Subject: CLEVO M360S acpi irq workaround + +Some Hardware vendor use CLEVO M360S Laptop OEM. +This acpi IRQ routing is not correct. we use normal IRQ routing for default. + +Signed-off-by: Go Taniguchi + +--- + arch/x86/kernel/acpi/boot.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/x86/kernel/acpi/boot.c ++++ b/arch/x86/kernel/acpi/boot.c +@@ -1615,6 +1615,14 @@ static struct dmi_system_id __initdata a + }, + }, + { ++ .callback = disable_acpi_irq, ++ .ident = "CLEVO M360S", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "M360S"), ++ }, ++ }, ++ { + /* + * Latest BIOS for IBM 600E (1.16) has bad pcinum + * for LPC bridge, which is needed for the PCI diff --git a/kernel/kernel/files/patches/mageia.old/acpi-processor-M720SR-limit-to-C2.patch b/kernel/kernel/files/patches/mageia.old/acpi-processor-M720SR-limit-to-C2.patch new file mode 100644 index 00000000..a3608628 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/acpi-processor-M720SR-limit-to-C2.patch @@ -0,0 +1,21 @@ +Limit Clevo M720SR to C2, going to C3 makes the machine freeze. This +needs more investigation, sis chipset/bios problem may be? + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + drivers/acpi/processor_idle.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- linux-2.6.33-rc8-git4-t2/drivers/acpi/processor_idle.c.orig 2010-02-20 12:45:35.000000000 +0200 ++++ linux-2.6.33-rc8-git4-t2/drivers/acpi/processor_idle.c 2010-02-20 12:53:39.679099592 +0200 +@@ -110,6 +110,9 @@ static struct dmi_system_id __cpuinitdat + DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), + DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")}, + (void *)2}, ++ { set_max_cstate, "Clevo M720SR", { ++ DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), ++ DMI_MATCH(DMI_PRODUCT_NAME,"M720SR")}, (void *)2}, + { set_max_cstate, "Pavilion zv5000", { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")}, diff --git a/kernel/kernel/files/patches/mageia.old/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch b/kernel/kernel/files/patches/mageia.old/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch new file mode 100644 index 00000000..2990886e --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch @@ -0,0 +1,33 @@ +>From 9f04e51293b130474504216a477bb2a73cbf59e1 Mon Sep 17 00:00:00 2001 +From: Anssi Hannula +Date: Thu, 22 Mar 2012 22:29:11 +0200 +Subject: [PATCH] ata: prefer ata drivers over ide drivers when both are built + +Currently the old IDE drivers are preferred over ATA drivers when both +are built, since ide/ is listed first in drivers/Makefile and therefore +the IDE drivers end up before ATA drivers in modules.order which is used +by depmod/modprobe for module ordering. + +Change it so that ATA drivers are preferred over IDE driver by moving +the ide/ entry under ata/ in drivers/Makefile. + +Signed-off-by: Anssi Hannula +--- + drivers/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/Makefile b/drivers/Makefile +index 932e8bf..e8df3d0 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -64,9 +64,9 @@ obj-y += base/ block/ misc/ mfd/ nfc/ + obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/ + obj-$(CONFIG_NUBUS) += nubus/ + obj-y += macintosh/ +-obj-$(CONFIG_IDE) += ide/ + obj-$(CONFIG_SCSI) += scsi/ + obj-$(CONFIG_ATA) += ata/ ++obj-$(CONFIG_IDE) += ide/ + obj-$(CONFIG_TARGET_CORE) += target/ + obj-$(CONFIG_MTD) += mtd/ + obj-$(CONFIG_SPI) += spi/ diff --git a/kernel/kernel/files/patches/mageia.old/block-Make-CFQ-default-to-IOPS-mode-on-SSDs.patch b/kernel/kernel/files/patches/mageia.old/block-Make-CFQ-default-to-IOPS-mode-on-SSDs.patch new file mode 100644 index 00000000..b26bf714 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/block-Make-CFQ-default-to-IOPS-mode-on-SSDs.patch @@ -0,0 +1,58 @@ +From 41c0126b3f22ef36b97b3c38b8f29569848a5ce2 Mon Sep 17 00:00:00 2001 +From: Tahsin Erdogan +Date: Tue, 19 May 2015 13:55:21 -0700 +Subject: block: Make CFQ default to IOPS mode on SSDs + +CFQ idling causes reduced IOPS throughput on non-rotational disks. +Since disk head seeking is not applicable to SSDs, it doesn't really +help performance by anticipating future near-by IO requests. + +By turning off idling (and switching to IOPS mode), we allow other +processes to dispatch IO requests down to the driver and so increase IO +throughput. + +Following FIO benchmark results were taken on a cloud SSD offering with +idling on and off: + +Idling iops avg-lat(ms) stddev bw +------------------------------------------------------ + On 7054 90.107 38.697 28217KB/s + Off 29255 21.836 11.730 117022KB/s + +fio --name=temp --size=100G --time_based --ioengine=libaio \ + --randrepeat=0 --direct=1 --invalidate=1 --verify=0 \ + --verify_fatal=0 --rw=randread --blocksize=4k --group_reporting=1 \ + --filename=/dev/sdb --runtime=10 --iodepth=64 --numjobs=10 + +And the following is from a local SSD run: + +Idling iops avg-lat(ms) stddev bw +------------------------------------------------------ + On 19320 33.043 14.068 77281KB/s + Off 21626 29.465 12.662 86507KB/s + +fio --name=temp --size=5G --time_based --ioengine=libaio \ + --randrepeat=0 --direct=1 --invalidate=1 --verify=0 \ + --verify_fatal=0 --rw=randread --blocksize=4k --group_reporting=1 \ + --filename=/fio_data --runtime=10 --iodepth=64 --numjobs=10 + +Reviewed-by: Nauman Rafique +Signed-off-by: Tahsin Erdogan +Signed-off-by: Jens Axboe + +diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c +index 5da8e6e..402be01 100644 +--- a/block/cfq-iosched.c ++++ b/block/cfq-iosched.c +@@ -4460,7 +4460,7 @@ static int cfq_init_queue(struct request_queue *q, struct elevator_type *e) + cfqd->cfq_slice[1] = cfq_slice_sync; + cfqd->cfq_target_latency = cfq_target_latency; + cfqd->cfq_slice_async_rq = cfq_slice_async_rq; +- cfqd->cfq_slice_idle = cfq_slice_idle; ++ cfqd->cfq_slice_idle = blk_queue_nonrot(q) ? 0 : cfq_slice_idle; + cfqd->cfq_group_idle = cfq_group_idle; + cfqd->cfq_latency = 1; + cfqd->hw_tag = -1; +-- +cgit v0.10.2 + diff --git a/kernel/kernel/files/patches/mageia.old/block-cfq-iosched-fix-the-setting-of-IOPS-mode-on-SSDs.patch b/kernel/kernel/files/patches/mageia.old/block-cfq-iosched-fix-the-setting-of-IOPS-mode-on-SSDs.patch new file mode 100644 index 00000000..7d188d69 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/block-cfq-iosched-fix-the-setting-of-IOPS-mode-on-SSDs.patch @@ -0,0 +1,95 @@ +From 0bb979472a7401022109e81dd89d777adea58710 Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Wed, 10 Jun 2015 08:01:20 -0600 +Subject: cfq-iosched: fix the setting of IOPS mode on SSDs + +A previous commit wanted to make CFQ default to IOPS mode on +non-rotational storage, however it did so when the queue was +initialized and the non-rotational flag is only set later on +in the probe. + +Add an elevator hook that gets called off the add_disk() path, +at that point we know that feature probing has finished, and +we can reliably check for the various flags that drivers can +set. + +Fixes: 41c0126b ("block: Make CFQ default to IOPS mode on SSDs") +Tested-by: Romain Francoise +Signed-off-by: Jens Axboe + +diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c +index c808ad8..d1d0cb2 100644 +--- a/block/cfq-iosched.c ++++ b/block/cfq-iosched.c +@@ -4508,7 +4508,7 @@ static int cfq_init_queue(struct request_queue *q, struct elevator_type *e) + cfqd->cfq_slice[1] = cfq_slice_sync; + cfqd->cfq_target_latency = cfq_target_latency; + cfqd->cfq_slice_async_rq = cfq_slice_async_rq; +- cfqd->cfq_slice_idle = blk_queue_nonrot(q) ? 0 : cfq_slice_idle; ++ cfqd->cfq_slice_idle = cfq_slice_idle; + cfqd->cfq_group_idle = cfq_group_idle; + cfqd->cfq_latency = 1; + cfqd->hw_tag = -1; +@@ -4525,6 +4525,18 @@ out_free: + return ret; + } + ++static void cfq_registered_queue(struct request_queue *q) ++{ ++ struct elevator_queue *e = q->elevator; ++ struct cfq_data *cfqd = e->elevator_data; ++ ++ /* ++ * Default to IOPS mode with no idling for SSDs ++ */ ++ if (blk_queue_nonrot(q)) ++ cfqd->cfq_slice_idle = 0; ++} ++ + /* + * sysfs parts below --> + */ +@@ -4640,6 +4652,7 @@ static struct elevator_type iosched_cfq = { + .elevator_may_queue_fn = cfq_may_queue, + .elevator_init_fn = cfq_init_queue, + .elevator_exit_fn = cfq_exit_queue, ++ .elevator_registered_fn = cfq_registered_queue, + }, + .icq_size = sizeof(struct cfq_io_cq), + .icq_align = __alignof__(struct cfq_io_cq), +diff --git a/block/elevator.c b/block/elevator.c +index 59794d0..5f04527 100644 +--- a/block/elevator.c ++++ b/block/elevator.c +@@ -810,6 +810,8 @@ int elv_register_queue(struct request_queue *q) + } + kobject_uevent(&e->kobj, KOBJ_ADD); + e->registered = 1; ++ if (e->type->ops.elevator_registered_fn) ++ e->type->ops.elevator_registered_fn(q); + } + return error; + } +diff --git a/include/linux/elevator.h b/include/linux/elevator.h +index 45a9147..638b324 100644 +--- a/include/linux/elevator.h ++++ b/include/linux/elevator.h +@@ -39,6 +39,7 @@ typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct reques + typedef int (elevator_init_fn) (struct request_queue *, + struct elevator_type *e); + typedef void (elevator_exit_fn) (struct elevator_queue *); ++typedef void (elevator_registered_fn) (struct request_queue *); + + struct elevator_ops + { +@@ -68,6 +69,7 @@ struct elevator_ops + + elevator_init_fn *elevator_init_fn; + elevator_exit_fn *elevator_exit_fn; ++ elevator_registered_fn *elevator_registered_fn; + }; + + #define ELV_NAME_MAX (16) +-- +cgit v0.10.2 + diff --git a/kernel/kernel/files/patches/mageia.old/block-floppy-disable-pnp-modalias.patch b/kernel/kernel/files/patches/mageia.old/block-floppy-disable-pnp-modalias.patch new file mode 100644 index 00000000..c994472a --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/block-floppy-disable-pnp-modalias.patch @@ -0,0 +1,21 @@ + +Disable floppy autoloading as it makes systems without real hw, +but that has the pnp headers hang. + +See https://bugs.mageia.org/show_bug.cgi?id=4696 + +Signed-off-by: Thomas Backlund + +diff -Nurp linux-3.3.3-rc1/drivers/block/floppy.c.orig linux-3.3.3-rc1/drivers/block/floppy.c +--- linux-3.3.3-rc1/drivers/block/floppy.c.orig 2012-03-19 01:15:34.000000000 +0200 ++++ linux-3.3.3-rc1/drivers/block/floppy.c 2012-04-22 02:58:10.238498303 +0300 +@@ -4621,8 +4621,7 @@ static const struct pnp_device_id floppy + {"PNP0700", 0}, + {} + }; +- +-MODULE_DEVICE_TABLE(pnp, floppy_pnpids); ++/* MODULE_DEVICE_TABLE(pnp, floppy_pnpids); */ + + #else + diff --git a/kernel/kernel/files/patches/mageia.old/char-agp-intel-new-Q57-id.patch b/kernel/kernel/files/patches/mageia.old/char-agp-intel-new-Q57-id.patch new file mode 100644 index 00000000..7093e9c0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/char-agp-intel-new-Q57-id.patch @@ -0,0 +1,35 @@ + +agp/intel: add new host bridge id for Q57 system + +Add new Host Bridge ID found on a Q57 based system from Positivo. I +don't know what abbreviation id for this would be best, may be Q_HB +instead of UNKNOWN currently used would be better. + +Signed-off-by: Herton Ronaldo Krzesinski + +[ rebased for 3.8-rc3 /tmb ] +Signed-off-by: Thomas Backlund + +Removed the parts of the patch that are already applied in the kernel by +the commit 67384fe3fd450536342330f684ea1f7dcaef8130: + "char/agp: add another Ironlake host bridge" + +Rediffed for kernel 3.10.24. + +Signed-off-by: Eugene A. Shatokhin + + drivers/char/agp/intel-gtt.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff -Nurp linux-3.8-rc3/drivers/char/agp/intel-gtt.c linux-3.8-rc3.q57/drivers/char/agp/intel-gtt.c +--- linux-3.8-rc3/drivers/char/agp/intel-gtt.c 2013-01-10 08:17:09.690594184 +0200 ++++ linux-3.8-rc3.q57/drivers/char/agp/intel-gtt.c 2013-01-10 08:38:56.649888839 +0200 +@@ -1303,6 +1303,8 @@ static const struct intel_gtt_driver_des + &g4x_gtt_driver }, + { PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, + "HD Graphics", &ironlake_gtt_driver }, ++ { PCI_DEVICE_ID_INTEL_IRONLAKE_D2_HB, ++ "HD Graphics", &ironlake_gtt_driver }, + { PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, + "HD Graphics", &ironlake_gtt_driver }, + { 0, NULL, NULL } diff --git a/kernel/kernel/files/patches/mageia.old/dm-raid-aliases.patch b/kernel/kernel/files/patches/mageia.old/dm-raid-aliases.patch new file mode 100644 index 00000000..febdb720 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/dm-raid-aliases.patch @@ -0,0 +1,20 @@ + +This patch adds module aliases for matching the old dm-raid45 patch +by heinzm@redhat.com carried by kernel-tmb series in order to make +sure there is an upgrade path. + +Signed-off-by: Thomas Backlund + + drivers/md/dm-raid.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- linux-2.6.38.2-mga2/drivers/md/dm-raid.c.orig 2011-03-15 03:20:32.000000000 +0200 ++++ linux-2.6.38.2-mga2/drivers/md/dm-raid.c 2011-04-02 21:52:21.331302590 +0300 +@@ -693,5 +693,7 @@ MODULE_DESCRIPTION(DM_NAME " raid4/5/6 t + MODULE_ALIAS("dm-raid4"); + MODULE_ALIAS("dm-raid5"); + MODULE_ALIAS("dm-raid6"); ++MODULE_ALIAS("dm-raid45"); ++MODULE_ALIAS("dm-raid4-5"); + MODULE_AUTHOR("Neil Brown "); + MODULE_LICENSE("GPL"); diff --git a/kernel/kernel/files/patches/mageia.old/firewire-ieee1394-module-aliases.patch b/kernel/kernel/files/patches/mageia.old/firewire-ieee1394-module-aliases.patch new file mode 100644 index 00000000..34c4d489 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/firewire-ieee1394-module-aliases.patch @@ -0,0 +1,29 @@ + +This patch adds missing module aliases for the old ieee1394 stack +to make it easier for endusers. + +Note: firewire-sbp2 and firewire-ohci already contains the needed aliases. + +Signed-off-by: Thomas Backlund + +diff -Nurp linux-2.6.38.4/drivers/firewire/core-transaction.c linux-2.6.38.4.fw/drivers/firewire/core-transaction.c +--- linux-2.6.38.4/drivers/firewire/core-transaction.c 2011-03-15 03:20:32.000000000 +0200 ++++ linux-2.6.38.4.fw/drivers/firewire/core-transaction.c 2011-04-22 16:22:28.461386738 +0300 +@@ -1174,6 +1174,9 @@ static struct fw_address_handler registe + MODULE_AUTHOR("Kristian Hoegsberg "); + MODULE_DESCRIPTION("Core IEEE1394 transaction logic"); + MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ieee1394"); ++MODULE_ALIAS("raw1394"); ++MODULE_ALIAS("video1394"); + + static const u32 vendor_textual_descriptor[] = { + /* textual descriptor leaf () */ +diff -Nurp linux-2.6.38.4/drivers/firewire/net.c linux-2.6.38.4.fw/drivers/firewire/net.c +--- linux-2.6.38.4/drivers/firewire/net.c 2011-03-15 03:20:32.000000000 +0200 ++++ linux-2.6.38.4.fw/drivers/firewire/net.c 2011-04-22 16:20:34.008883920 +0300 +@@ -1719,3 +1719,4 @@ MODULE_AUTHOR("Jay Fenlason +Date: Tue, 18 Aug 2015 22:24:50 +0300 +Subject: [PATCH] Revert "ext4: remove block_device_ejected" + +This reverts commit 08439fec266c3cc5702953b4f54bdf5649357de0. + +its reported on stable@ ml to break usb storage + +Signed-off-by: Thomas Backlund +--- + fs/ext4/super.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/fs/ext4/super.c b/fs/ext4/super.c +index ca9d4a2..9829003 100644 +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -324,6 +324,22 @@ static void save_error_info(struct super_block *sb, const char *func, + ext4_commit_super(sb, 1); + } + ++/* ++ * The del_gendisk() function uninitializes the disk-specific data ++ * structures, including the bdi structure, without telling anyone ++ * else. Once this happens, any attempt to call mark_buffer_dirty() ++ * (for example, by ext4_commit_super), will cause a kernel OOPS. ++ * This is a kludge to prevent these oops until we can put in a proper ++ * hook in del_gendisk() to inform the VFS and file system layers. ++ */ ++static int block_device_ejected(struct super_block *sb) ++{ ++ struct inode *bd_inode = sb->s_bdev->bd_inode; ++ struct backing_dev_info *bdi = inode_to_bdi(bd_inode); ++ ++ return bdi->dev == NULL; ++} ++ + static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn) + { + struct super_block *sb = journal->j_private; +@@ -4590,7 +4606,7 @@ static int ext4_commit_super(struct super_block *sb, int sync) + struct buffer_head *sbh = EXT4_SB(sb)->s_sbh; + int error = 0; + +- if (!sbh) ++ if (!sbh || block_device_ejected(sb)) + return error; + if (buffer_write_io_error(sbh)) { + /* +-- +2.4.5 + diff --git a/kernel/kernel/files/patches/mageia.old/fs-aufs4-modular.patch b/kernel/kernel/files/patches/mageia.old/fs-aufs4-modular.patch new file mode 100644 index 00000000..c1fa58a0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/fs-aufs4-modular.patch @@ -0,0 +1,338 @@ + + fs/aufs/Kconfig | 2 +- + fs/dcache.c | 1 + + fs/file_table.c | 2 ++ + fs/inode.c | 2 ++ + fs/namespace.c | 2 ++ + fs/notify/group.c | 4 ++++ + fs/notify/mark.c | 4 ++++ + fs/open.c | 2 ++ + fs/read_write.c | 2 ++ + fs/splice.c | 2 ++ + security/commoncap.c | 2 ++ + security/device_cgroup.c | 2 ++ + security/security.c | 10 ++++++++++ + 13 files changed, 36 insertions(+), 1 deletion(-) + +diff -Nurp linux-4.1-aufs4/fs/aufs/Kconfig linux-4.1-aufs4-mod/fs/aufs/Kconfig +--- linux-4.1-aufs4/fs/aufs/Kconfig 2015-06-25 20:09:18.250319377 +0200 ++++ linux-4.1-aufs4-mod/fs/aufs/Kconfig 2015-06-25 20:11:30.629136790 +0200 +@@ -1,5 +1,5 @@ + config AUFS_FS +- bool "Aufs (Advanced multi layered unification filesystem) support" ++ tristate "Aufs (Advanced multi layered unification filesystem) support" + help + Aufs is a stackable unification filesystem such as Unionfs, + which unifies several directories and provides a merged single +diff -Nurp linux-4.1-aufs4/fs/dcache.c linux-4.1-aufs4-mod/fs/dcache.c +--- linux-4.1-aufs4/fs/dcache.c 2015-06-25 20:09:18.280319109 +0200 ++++ linux-4.1-aufs4-mod/fs/dcache.c 2015-06-25 20:11:30.629136790 +0200 +@@ -1269,6 +1269,7 @@ rename_retry: + seq = 1; + goto again; + } ++EXPORT_SYMBOL_GPL(d_walk); + + /* + * Search for at least 1 mount point in the dentry's subdirs. +diff -Nurp linux-4.1-aufs4/fs/file_table.c linux-4.1-aufs4-mod/fs/file_table.c +--- linux-4.1-aufs4/fs/file_table.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/fs/file_table.c 2015-06-25 23:47:57.011958835 +0200 +@@ -147,6 +147,7 @@ over: + } + return ERR_PTR(-ENFILE); + } ++EXPORT_SYMBOL(get_empty_filp); + + /** + * alloc_file - allocate and initialize a 'struct file' +@@ -308,6 +309,7 @@ void put_filp(struct file *file) + file_free(file); + } + } ++EXPORT_SYMBOL(put_filp); + + void __init files_init(unsigned long mempages) + { +diff -Nurp linux-4.1-aufs4/fs/inode.c linux-4.1-aufs4-mod/fs/inode.c +--- linux-4.1-aufs4/fs/inode.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/fs/inode.c 2015-06-25 22:45:45.000000000 +0200 +@@ -58,6 +58,7 @@ static struct hlist_head *inode_hashtabl + static __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_hash_lock); + + __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_sb_list_lock); ++EXPORT_SYMBOL(inode_sb_list_lock); + + /* + * Empty aops. Can be used for the cases where the user does not +@@ -1575,6 +1576,7 @@ static int update_time(struct inode *ino + + return update_time(inode, time, flags); + } ++EXPORT_SYMBOL(update_time); + + /** + * touch_atime - update the access time +diff -Nurp linux-4.1-aufs4/fs/namespace.c linux-4.1-aufs4-mod/fs/namespace.c +--- linux-4.1-aufs4/fs/namespace.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/fs/namespace.c 2015-06-25 20:11:30.629136790 +0200 +@@ -463,6 +463,7 @@ void __mnt_drop_write(struct vfsmount *m + mnt_dec_writers(real_mount(mnt)); + preempt_enable(); + } ++EXPORT_SYMBOL_GPL(__mnt_drop_write); + + /** + * mnt_drop_write - give up write access to a mount +@@ -1768,6 +1769,7 @@ int iterate_mounts(int (*f)(struct vfsmo + } + return 0; + } ++EXPORT_SYMBOL(iterate_mounts); + + static void cleanup_group_ids(struct mount *mnt, struct mount *end) + { +diff -Nurp linux-4.1-aufs4/fs/notify/group.c linux-4.1-aufs4-mod/fs/notify/group.c +--- linux-4.1-aufs4/fs/notify/group.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/fs/notify/group.c 2015-06-25 20:11:30.629136790 +0200 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + #include "fsnotify.h" +@@ -72,6 +73,7 @@ void fsnotify_get_group(struct fsnotify_ + { + atomic_inc(&group->refcnt); + } ++EXPORT_SYMBOL(fsnotify_get_group); + + /* + * Drop a reference to a group. Free it if it's through. +@@ -81,6 +83,7 @@ void fsnotify_put_group(struct fsnotify_ + if (atomic_dec_and_test(&group->refcnt)) + fsnotify_final_destroy_group(group); + } ++EXPORT_SYMBOL(fsnotify_put_group); + + /* + * Create a new fsnotify_group and hold a reference for the group returned. +@@ -109,6 +112,7 @@ struct fsnotify_group *fsnotify_alloc_gr + + return group; + } ++EXPORT_SYMBOL(fsnotify_alloc_group); + + int fsnotify_fasync(int fd, struct file *file, int on) + { +diff -Nurp linux-4.1-aufs4/fs/notify/mark.c linux-4.1-aufs4-mod/fs/notify/mark.c +--- linux-4.1-aufs4/fs/notify/mark.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/fs/notify/mark.c 2015-06-25 20:11:30.629136790 +0200 +@@ -109,6 +109,7 @@ void fsnotify_put_mark(struct fsnotify_m + mark->free_mark(mark); + } + } ++EXPORT_SYMBOL(fsnotify_put_mark); + + /* Calculate mask of events for a list of marks */ + u32 fsnotify_recalc_mask(struct hlist_head *head) +@@ -202,6 +203,7 @@ void fsnotify_destroy_mark(struct fsnoti + fsnotify_destroy_mark_locked(mark, group); + mutex_unlock(&group->mark_mutex); + } ++EXPORT_SYMBOL(fsnotify_destroy_mark); + + /* + * Destroy all marks in the given list. The marks must be already detached from +@@ -386,6 +388,7 @@ int fsnotify_add_mark(struct fsnotify_ma + mutex_unlock(&group->mark_mutex); + return ret; + } ++EXPORT_SYMBOL(fsnotify_add_mark); + + /* + * Given a list of marks, find the mark associated with given group. If found +@@ -455,6 +458,7 @@ void fsnotify_init_mark(struct fsnotify_ + atomic_set(&mark->refcnt, 1); + mark->free_mark = free_mark; + } ++EXPORT_SYMBOL(fsnotify_init_mark); + + static int fsnotify_mark_destroy(void *ignored) + { +diff -Nurp linux-4.1-aufs4/fs/open.c linux-4.1-aufs4-mod/fs/open.c +--- linux-4.1-aufs4/fs/open.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/fs/open.c 2015-06-25 23:49:02.741362661 +0200 +@@ -62,6 +62,7 @@ int do_truncate(struct dentry *dentry, l + mutex_unlock(&dentry->d_inode->i_mutex); + return ret; + } ++EXPORT_SYMBOL(do_truncate); + + long vfs_truncate(struct path *path, loff_t length) + { +@@ -676,6 +677,7 @@ int open_check_o_direct(struct file *f) + } + return 0; + } ++EXPORT_SYMBOL(open_check_o_direct); + + static int do_dentry_open(struct file *f, + int (*open)(struct inode *, struct file *), +diff -Nurp linux-4.1-aufs4/fs/read_write.c linux-4.1-aufs4-mod/fs/read_write.c +--- linux-4.1-aufs4/fs/read_write.c 2015-06-25 20:09:18.280319109 +0200 ++++ linux-4.1-aufs4-mod/fs/read_write.c 2015-06-25 23:43:56.804138140 +0200 +@@ -504,6 +504,7 @@ vfs_readf_t vfs_readf(struct file *file) + return new_sync_read; + return ERR_PTR(-ENOSYS); + } ++EXPORT_SYMBOL(vfs_readf); + + vfs_writef_t vfs_writef(struct file *file) + { +@@ -515,6 +516,7 @@ vfs_writef_t vfs_writef(struct file *fil + return new_sync_write; + return ERR_PTR(-ENOSYS); + } ++EXPORT_SYMBOL(vfs_writef); + + ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) + { +diff -Nurp linux-4.1-aufs4/fs/splice.c linux-4.1-aufs4-mod/fs/splice.c +--- linux-4.1-aufs4/fs/splice.c 2015-06-25 20:09:18.280319109 +0200 ++++ linux-4.1-aufs4-mod/fs/splice.c 2015-06-25 20:11:30.629136790 +0200 +@@ -1114,6 +1114,7 @@ long do_splice_from(struct pipe_inode_in + + return splice_write(pipe, out, ppos, len, flags); + } ++EXPORT_SYMBOL(do_splice_from); + + /* + * Attempt to initiate a splice from a file to a pipe. +@@ -1140,6 +1141,7 @@ long do_splice_to(struct file *in, loff_ + + return splice_read(in, ppos, pipe, len, flags); + } ++EXPORT_SYMBOL(do_splice_to); + + /** + * splice_direct_to_actor - splices data directly between two non-pipes +diff -Nurp linux-4.1-aufs4/security/commoncap.c linux-4.1-aufs4-mod/security/commoncap.c +--- linux-4.1-aufs4/security/commoncap.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/security/commoncap.c 2015-06-25 20:11:30.629136790 +0200 +@@ -975,9 +975,11 @@ int cap_mmap_addr(unsigned long addr) + } + return ret; + } ++EXPORT_SYMBOL(cap_mmap_addr); + + int cap_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) + { + return 0; + } ++EXPORT_SYMBOL(cap_mmap_file); +diff -Nurp linux-4.1-aufs4/security/device_cgroup.c linux-4.1-aufs4-mod/security/device_cgroup.c +--- linux-4.1-aufs4/security/device_cgroup.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/security/device_cgroup.c 2015-06-25 20:11:30.629136790 +0200 +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -849,6 +850,7 @@ int __devcgroup_inode_permission(struct + return __devcgroup_check_permission(type, imajor(inode), iminor(inode), + access); + } ++EXPORT_SYMBOL(__devcgroup_inode_permission); + + int devcgroup_inode_mknod(int mode, dev_t dev) + { +diff -Nurp linux-4.1-aufs4/security/security.c linux-4.1-aufs4-mod/security/security.c +--- linux-4.1-aufs4/security/security.c 2015-06-22 07:05:43.000000000 +0200 ++++ linux-4.1-aufs4-mod/security/security.c 2015-06-25 20:11:30.629136790 +0200 +@@ -430,6 +430,7 @@ int security_path_rmdir(struct path *dir + return 0; + return security_ops->path_rmdir(dir, dentry); + } ++EXPORT_SYMBOL(security_path_rmdir); + + int security_path_unlink(struct path *dir, struct dentry *dentry) + { +@@ -446,6 +447,7 @@ int security_path_symlink(struct path *d + return 0; + return security_ops->path_symlink(dir, dentry, old_name); + } ++EXPORT_SYMBOL(security_path_symlink); + + int security_path_link(struct dentry *old_dentry, struct path *new_dir, + struct dentry *new_dentry) +@@ -454,6 +456,7 @@ int security_path_link(struct dentry *ol + return 0; + return security_ops->path_link(old_dentry, new_dir, new_dentry); + } ++EXPORT_SYMBOL(security_path_link); + + int security_path_rename(struct path *old_dir, struct dentry *old_dentry, + struct path *new_dir, struct dentry *new_dentry, +@@ -481,6 +484,7 @@ int security_path_truncate(struct path * + return 0; + return security_ops->path_truncate(path); + } ++EXPORT_SYMBOL(security_path_truncate); + + int security_path_chmod(struct path *path, umode_t mode) + { +@@ -488,6 +492,7 @@ int security_path_chmod(struct path *pat + return 0; + return security_ops->path_chmod(path, mode); + } ++EXPORT_SYMBOL(security_path_chmod); + + int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) + { +@@ -495,6 +500,7 @@ int security_path_chown(struct path *pat + return 0; + return security_ops->path_chown(path, uid, gid); + } ++EXPORT_SYMBOL(security_path_chown); + + int security_path_chroot(struct path *path) + { +@@ -580,6 +586,7 @@ int security_inode_readlink(struct dentr + return 0; + return security_ops->inode_readlink(dentry); + } ++EXPORT_SYMBOL(security_inode_readlink); + + int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) + { +@@ -594,6 +601,7 @@ int security_inode_permission(struct ino + return 0; + return security_ops->inode_permission(inode, mask); + } ++EXPORT_SYMBOL(security_inode_permission); + + int security_inode_setattr(struct dentry *dentry, struct iattr *attr) + { +@@ -716,6 +724,7 @@ int security_file_permission(struct file + + return fsnotify_perm(file, mask); + } ++EXPORT_SYMBOL(security_file_permission); + + int security_file_alloc(struct file *file) + { +@@ -775,6 +784,7 @@ int security_mmap_file(struct file *file + return ret; + return ima_file_mmap(file, prot); + } ++EXPORT_SYMBOL(security_mmap_file); + + int security_mmap_addr(unsigned long addr) + { diff --git a/kernel/kernel/files/patches/mageia.old/fs-aufs4.patch b/kernel/kernel/files/patches/mageia.old/fs-aufs4.patch new file mode 100644 index 00000000..0025b6e6 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/fs-aufs4.patch @@ -0,0 +1,34146 @@ + + Documentation/ABI/testing/debugfs-aufs | 50 + + Documentation/ABI/testing/sysfs-aufs | 31 + + Documentation/filesystems/aufs/README | 383 ++++ + Documentation/filesystems/aufs/design/01intro.txt | 157 ++ + Documentation/filesystems/aufs/design/02struct.txt | 245 +++ + .../filesystems/aufs/design/03atomic_open.txt | 72 + + Documentation/filesystems/aufs/design/03lookup.txt | 100 ++ + Documentation/filesystems/aufs/design/04branch.txt | 61 + + .../filesystems/aufs/design/05wbr_policy.txt | 51 + + Documentation/filesystems/aufs/design/06fhsm.txt | 105 ++ + Documentation/filesystems/aufs/design/06mmap.txt | 59 + + Documentation/filesystems/aufs/design/06xattr.txt | 81 + + Documentation/filesystems/aufs/design/07export.txt | 45 + + Documentation/filesystems/aufs/design/08shwh.txt | 39 + + Documentation/filesystems/aufs/design/10dynop.txt | 34 + + MAINTAINERS | 13 + + drivers/block/loop.c | 18 + + fs/Kconfig | 1 + + fs/Makefile | 1 + + fs/aufs/Kconfig | 185 ++ + fs/aufs/Makefile | 36 + + fs/aufs/aufs.h | 46 + + fs/aufs/branch.c | 1401 +++++++++++++++ + fs/aufs/branch.h | 266 +++ + fs/aufs/cpup.c | 1306 ++++++++++++++ + fs/aufs/cpup.h | 81 + + fs/aufs/dbgaufs.c | 419 +++++ + fs/aufs/dbgaufs.h | 35 + + fs/aufs/dcsub.c | 211 +++ + fs/aufs/dcsub.h | 123 ++ + fs/aufs/debug.c | 427 +++++ + fs/aufs/debug.h | 212 +++ + fs/aufs/dentry.c | 1092 ++++++++++++ + fs/aufs/dentry.h | 220 +++ + fs/aufs/dinfo.c | 537 ++++++ + fs/aufs/dir.c | 740 ++++++++ + fs/aufs/dir.h | 118 ++ + fs/aufs/dynop.c | 356 ++++ + fs/aufs/dynop.h | 61 + + fs/aufs/export.c | 819 +++++++++ + fs/aufs/f_op.c | 725 ++++++++ + fs/aufs/fhsm.c | 412 +++++ + fs/aufs/file.c | 828 +++++++++ + fs/aufs/file.h | 278 +++ + fs/aufs/finfo.c | 144 ++ + fs/aufs/fstype.h | 387 +++++ + fs/aufs/hfsnotify.c | 275 +++ + fs/aufs/hfsplus.c | 43 + + fs/aufs/hnotify.c | 697 ++++++++ + fs/aufs/i_op.c | 1434 +++++++++++++++ + fs/aufs/i_op_add.c | 919 ++++++++++ + fs/aufs/i_op_del.c | 497 ++++++ + fs/aufs/i_op_ren.c | 1004 +++++++++++ + fs/aufs/iinfo.c | 264 +++ + fs/aufs/inode.c | 487 ++++++ + fs/aufs/inode.h | 660 +++++++ + fs/aufs/ioctl.c | 206 +++ + fs/aufs/loop.c | 132 ++ + fs/aufs/loop.h | 39 + + fs/aufs/magic.mk | 30 + + fs/aufs/module.c | 196 +++ + fs/aufs/module.h | 91 + + fs/aufs/mvdown.c | 681 ++++++++ + fs/aufs/opts.c | 1822 ++++++++++++++++++++ + fs/aufs/opts.h | 197 +++ + fs/aufs/plink.c | 515 ++++++ + fs/aufs/poll.c | 39 + + fs/aufs/posix_acl.c | 86 + + fs/aufs/procfs.c | 156 ++ + fs/aufs/rdu.c | 375 ++++ + fs/aufs/rwsem.h | 178 ++ + fs/aufs/sbinfo.c | 343 ++++ + fs/aufs/spl.h | 98 ++ + fs/aufs/super.c | 991 +++++++++++ + fs/aufs/super.h | 622 +++++++ + fs/aufs/sysaufs.c | 91 + + fs/aufs/sysaufs.h | 88 + + fs/aufs/sysfs.c | 336 ++++ + fs/aufs/sysrq.c | 144 ++ + fs/aufs/vdir.c | 875 ++++++++++ + fs/aufs/vfsub.c | 835 +++++++++ + fs/aufs/vfsub.h | 273 +++ + fs/aufs/wbr_policy.c | 752 ++++++++ + fs/aufs/whout.c | 1050 +++++++++++ + fs/aufs/whout.h | 72 + + fs/aufs/wkq.c | 200 +++ + fs/aufs/wkq.h | 78 + + fs/aufs/xattr.c | 331 ++++ + fs/aufs/xino.c | 1284 ++++++++++++++ + fs/buffer.c | 2 +- + fs/dcache.c | 2 +- + fs/proc/base.c | 2 +- + fs/proc/nommu.c | 5 +- + fs/proc/task_mmu.c | 7 +- + fs/proc/task_nommu.c | 5 +- + fs/read_write.c | 22 + + fs/splice.c | 10 +- + include/linux/file.h | 1 + + include/linux/fs.h | 6 + + include/linux/mm.h | 22 + + include/linux/mm_types.h | 2 + + include/linux/splice.h | 6 + + include/uapi/linux/Kbuild | 1 + + include/uapi/linux/aufs_type.h | 406 +++++ + kernel/fork.c | 2 +- + mm/Makefile | 2 +- + mm/filemap.c | 2 +- + mm/madvise.c | 4 +- + mm/memory.c | 2 +- + mm/mmap.c | 17 +- + mm/msync.c | 4 +- + mm/nommu.c | 10 +- + mm/prfile.c | 86 + + 113 files changed, 33091 insertions(+), 34 deletions(-) + +diff --git a/Documentation/ABI/testing/debugfs-aufs b/Documentation/ABI/testing/debugfs-aufs +new file mode 100644 +index 0000000..99642d1 +--- /dev/null ++++ b/Documentation/ABI/testing/debugfs-aufs +@@ -0,0 +1,50 @@ ++What: /debug/aufs/si_/ ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ Under /debug/aufs, a directory named si_ is created ++ per aufs mount, where is a unique id generated ++ internally. ++ ++What: /debug/aufs/si_/plink ++Date: Apr 2013 ++Contact: J. R. Okajima ++Description: ++ It has three lines and shows the information about the ++ pseudo-link. The first line is a single number ++ representing a number of buckets. The second line is a ++ number of pseudo-links per buckets (separated by a ++ blank). The last line is a single number representing a ++ total number of psedo-links. ++ When the aufs mount option 'noplink' is specified, it ++ will show "1\n0\n0\n". ++ ++What: /debug/aufs/si_/xib ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xib (External Inode Number ++ Bitmap), its block size and file size. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. ++ ++What: /debug/aufs/si_/xino0, xino1 ... xinoN ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xino (External Inode Number ++ Translation Table), its link count, block size and file ++ size. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. ++ ++What: /debug/aufs/si_/xigen ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xigen (External Inode ++ Generation Table), its block size and file size. ++ If CONFIG_AUFS_EXPORT is disabled, this entry will not ++ be created. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. +diff --git a/Documentation/ABI/testing/sysfs-aufs b/Documentation/ABI/testing/sysfs-aufs +new file mode 100644 +index 0000000..82f9518 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-aufs +@@ -0,0 +1,31 @@ ++What: /sys/fs/aufs/si_/ ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ Under /sys/fs/aufs, a directory named si_ is created ++ per aufs mount, where is a unique id generated ++ internally. ++ ++What: /sys/fs/aufs/si_/br0, br1 ... brN ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the abolute path of a member directory (which ++ is called branch) in aufs, and its permission. ++ ++What: /sys/fs/aufs/si_/brid0, brid1 ... bridN ++Date: July 2013 ++Contact: J. R. Okajima ++Description: ++ It shows the id of a member directory (which is called ++ branch) in aufs. ++ ++What: /sys/fs/aufs/si_/xi_path ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the abolute path of XINO (External Inode Number ++ Bitmap, Translation Table and Generation Table) file ++ even if it is the default path. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. +diff --git a/Documentation/filesystems/aufs/README b/Documentation/filesystems/aufs/README +new file mode 100644 +index 0000000..27faa06f +--- /dev/null ++++ b/Documentation/filesystems/aufs/README +@@ -0,0 +1,383 @@ ++ ++Aufs4 -- advanced multi layered unification filesystem version 4.x ++http://aufs.sf.net ++Junjiro R. Okajima ++ ++ ++0. Introduction ++---------------------------------------- ++In the early days, aufs was entirely re-designed and re-implemented ++Unionfs Version 1.x series. Adding many original ideas, approaches, ++improvements and implementations, it becomes totally different from ++Unionfs while keeping the basic features. ++Recently, Unionfs Version 2.x series begin taking some of the same ++approaches to aufs1's. ++Unionfs is being developed by Professor Erez Zadok at Stony Brook ++University and his team. ++ ++Aufs4 supports linux-4.0 and later, and for linux-3.x series try aufs3. ++If you want older kernel version support, try aufs2-2.6.git or ++aufs2-standalone.git repository, aufs1 from CVS on SourceForge. ++ ++Note: it becomes clear that "Aufs was rejected. Let's give it up." ++ According to Christoph Hellwig, linux rejects all union-type ++ filesystems but UnionMount. ++ ++ ++PS. Al Viro seems have a plan to merge aufs as well as overlayfs and ++ UnionMount, and he pointed out an issue around a directory mutex ++ lock and aufs addressed it. But it is still unsure whether aufs will ++ be merged (or any other union solution). ++ ++ ++ ++1. Features ++---------------------------------------- ++- unite several directories into a single virtual filesystem. The member ++ directory is called as a branch. ++- you can specify the permission flags to the branch, which are 'readonly', ++ 'readwrite' and 'whiteout-able.' ++- by upper writable branch, internal copyup and whiteout, files/dirs on ++ readonly branch are modifiable logically. ++- dynamic branch manipulation, add, del. ++- etc... ++ ++Also there are many enhancements in aufs, such as: ++- test only the highest one for the directory permission (dirperm1) ++- copyup on open (coo=) ++- 'move' policy for copy-up between two writable branches, after ++ checking free space. ++- xattr, acl ++- readdir(3) in userspace. ++- keep inode number by external inode number table ++- keep the timestamps of file/dir in internal copyup operation ++- seekable directory, supporting NFS readdir. ++- whiteout is hardlinked in order to reduce the consumption of inodes ++ on branch ++- do not copyup, nor create a whiteout when it is unnecessary ++- revert a single systemcall when an error occurs in aufs ++- remount interface instead of ioctl ++- maintain /etc/mtab by an external command, /sbin/mount.aufs. ++- loopback mounted filesystem as a branch ++- kernel thread for removing the dir who has a plenty of whiteouts ++- support copyup sparse file (a file which has a 'hole' in it) ++- default permission flags for branches ++- selectable permission flags for ro branch, whether whiteout can ++ exist or not ++- export via NFS. ++- support /fs/aufs and /aufs. ++- support multiple writable branches, some policies to select one ++ among multiple writable branches. ++- a new semantics for link(2) and rename(2) to support multiple ++ writable branches. ++- no glibc changes are required. ++- pseudo hardlink (hardlink over branches) ++- allow a direct access manually to a file on branch, e.g. bypassing aufs. ++ including NFS or remote filesystem branch. ++- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX. ++- and more... ++ ++Currently these features are dropped temporary from aufs4. ++See design/08plan.txt in detail. ++- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs ++ (robr) ++- statistics of aufs thread (/sys/fs/aufs/stat) ++ ++Features or just an idea in the future (see also design/*.txt), ++- reorder the branch index without del/re-add. ++- permanent xino files for NFSD ++- an option for refreshing the opened files after add/del branches ++- light version, without branch manipulation. (unnecessary?) ++- copyup in userspace ++- inotify in userspace ++- readv/writev ++ ++ ++2. Download ++---------------------------------------- ++There are three GIT trees for aufs4, aufs4-linux.git, ++aufs4-standalone.git, and aufs-util.git. Note that there is no "4" in ++"aufs-util.git." ++While the aufs-util is always necessary, you need either of aufs4-linux ++or aufs4-standalone. ++ ++The aufs4-linux tree includes the whole linux mainline GIT tree, ++git://git.kernel.org/.../torvalds/linux.git. ++And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot ++build aufs4 as an external kernel module. ++Several extra patches are not included in this tree. Only ++aufs4-standalone tree contains them. They are describe in the later ++section "Configuration and Compilation." ++ ++On the other hand, the aufs4-standalone tree has only aufs source files ++and necessary patches, and you can select CONFIG_AUFS_FS=m. ++But you need to apply all aufs patches manually. ++ ++You will find GIT branches whose name is in form of "aufs4.x" where "x" ++represents the linux kernel version, "linux-4.x". For instance, ++"aufs4.0" is for linux-4.0. For latest "linux-4.x-rcN", use ++"aufs4.x-rcN" branch. ++ ++o aufs4-linux tree ++$ git clone --reference /your/linux/git/tree \ ++ git://github.com/sfjro/aufs4-linux.git aufs4-linux.git ++- if you don't have linux GIT tree, then remove "--reference ..." ++$ cd aufs4-linux.git ++$ git checkout origin/aufs4.0 ++ ++Or You may want to directly git-pull aufs into your linux GIT tree, and ++leave the patch-work to GIT. ++$ cd /your/linux/git/tree ++$ git remote add aufs4 git://github.com/sfjro/aufs4-linux.git ++$ git fetch aufs4 ++$ git checkout -b my4.0 v4.0 ++$ (add your local change...) ++$ git pull aufs4 aufs4.0 ++- now you have v4.0 + your_changes + aufs4.0 in you my4.0 branch. ++- you may need to solve some conflicts between your_changes and ++ aufs4.0. in this case, git-rerere is recommended so that you can ++ solve the similar conflicts automatically when you upgrade to 4.1 or ++ later in the future. ++ ++o aufs4-standalone tree ++$ git clone git://github.com/sfjro/aufs4-standalone.git aufs4-standalone.git ++$ cd aufs4-standalone.git ++$ git checkout origin/aufs4.0 ++ ++o aufs-util tree ++$ git clone git://git.code.sf.net/p/aufs/aufs-util aufs-util.git ++- note that the public aufs-util.git is on SourceForge instead of ++ GitHUB. ++$ cd aufs-util.git ++$ git checkout origin/aufs4.0 ++ ++Note: The 4.x-rcN branch is to be used with `rc' kernel versions ONLY. ++The minor version number, 'x' in '4.x', of aufs may not always ++follow the minor version number of the kernel. ++Because changes in the kernel that cause the use of a new ++minor version number do not always require changes to aufs-util. ++ ++Since aufs-util has its own minor version number, you may not be ++able to find a GIT branch in aufs-util for your kernel's ++exact minor version number. ++In this case, you should git-checkout the branch for the ++nearest lower number. ++ ++For (an unreleased) example: ++If you are using "linux-4.10" and the "aufs4.10" branch ++does not exist in aufs-util repository, then "aufs4.9", "aufs4.8" ++or something numerically smaller is the branch for your kernel. ++ ++Also you can view all branches by ++ $ git branch -a ++ ++ ++3. Configuration and Compilation ++---------------------------------------- ++Make sure you have git-checkout'ed the correct branch. ++ ++For aufs4-linux tree, ++- enable CONFIG_AUFS_FS. ++- set other aufs configurations if necessary. ++ ++For aufs4-standalone tree, ++There are several ways to build. ++ ++1. ++- apply ./aufs4-kbuild.patch to your kernel source files. ++- apply ./aufs4-base.patch too. ++- apply ./aufs4-mmap.patch too. ++- apply ./aufs4-standalone.patch too, if you have a plan to set ++ CONFIG_AUFS_FS=m. otherwise you don't need ./aufs4-standalone.patch. ++- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your ++ kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild. ++- enable CONFIG_AUFS_FS, you can select either ++ =m or =y. ++- and build your kernel as usual. ++- install the built kernel. ++ Note: Since linux-3.9, every filesystem module requires an alias ++ "fs-". You should make sure that "fs-aufs" is listed in your ++ modules.aliases file if you set CONFIG_AUFS_FS=m. ++- install the header files too by "make headers_install" to the ++ directory where you specify. By default, it is $PWD/usr. ++ "make help" shows a brief note for headers_install. ++- and reboot your system. ++ ++2. ++- module only (CONFIG_AUFS_FS=m). ++- apply ./aufs4-base.patch to your kernel source files. ++- apply ./aufs4-mmap.patch too. ++- apply ./aufs4-standalone.patch too. ++- build your kernel, don't forget "make headers_install", and reboot. ++- edit ./config.mk and set other aufs configurations if necessary. ++ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes ++ every aufs configurations. ++- build the module by simple "make". ++ Note: Since linux-3.9, every filesystem module requires an alias ++ "fs-". You should make sure that "fs-aufs" is listed in your ++ modules.aliases file. ++- you can specify ${KDIR} make variable which points to your kernel ++ source tree. ++- install the files ++ + run "make install" to install the aufs module, or copy the built ++ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply). ++ + run "make install_headers" (instead of headers_install) to install ++ the modified aufs header file (you can specify DESTDIR which is ++ available in aufs standalone version's Makefile only), or copy ++ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever ++ you like manually. By default, the target directory is $PWD/usr. ++- no need to apply aufs4-kbuild.patch, nor copying source files to your ++ kernel source tree. ++ ++Note: The header file aufs_type.h is necessary to build aufs-util ++ as well as "make headers_install" in the kernel source tree. ++ headers_install is subject to be forgotten, but it is essentially ++ necessary, not only for building aufs-util. ++ You may not meet problems without headers_install in some older ++ version though. ++ ++And then, ++- read README in aufs-util, build and install it ++- note that your distribution may contain an obsoleted version of ++ aufs_type.h in /usr/include/linux or something. When you build aufs ++ utilities, make sure that your compiler refers the correct aufs header ++ file which is built by "make headers_install." ++- if you want to use readdir(3) in userspace or pathconf(3) wrapper, ++ then run "make install_ulib" too. And refer to the aufs manual in ++ detail. ++ ++There several other patches in aufs4-standalone.git. They are all ++optional. When you meet some problems, they will help you. ++- aufs4-loopback.patch ++ Supports a nested loopback mount in a branch-fs. This patch is ++ unnecessary until aufs produces a message like "you may want to try ++ another patch for loopback file". ++- vfs-ino.patch ++ Modifies a system global kernel internal function get_next_ino() in ++ order to stop assigning 0 for an inode-number. Not directly related to ++ aufs, but recommended generally. ++- tmpfs-idr.patch ++ Keeps the tmpfs inode number as the lowest value. Effective to reduce ++ the size of aufs XINO files for tmpfs branch. Also it prevents the ++ duplication of inode number, which is important for backup tools and ++ other utilities. When you find aufs XINO files for tmpfs branch ++ growing too much, try this patch. ++ ++ ++4. Usage ++---------------------------------------- ++At first, make sure aufs-util are installed, and please read the aufs ++manual, aufs.5 in aufs-util.git tree. ++$ man -l aufs.5 ++ ++And then, ++$ mkdir /tmp/rw /tmp/aufs ++# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs ++ ++Here is another example. The result is equivalent. ++# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs ++ Or ++# mount -t aufs -o br:/tmp/rw none /tmp/aufs ++# mount -o remount,append:${HOME} /tmp/aufs ++ ++Then, you can see whole tree of your home dir through /tmp/aufs. If ++you modify a file under /tmp/aufs, the one on your home directory is ++not affected, instead the same named file will be newly created under ++/tmp/rw. And all of your modification to a file will be applied to ++the one under /tmp/rw. This is called the file based Copy on Write ++(COW) method. ++Aufs mount options are described in aufs.5. ++If you run chroot or something and make your aufs as a root directory, ++then you need to customize the shutdown script. See the aufs manual in ++detail. ++ ++Additionally, there are some sample usages of aufs which are a ++diskless system with network booting, and LiveCD over NFS. ++See sample dir in CVS tree on SourceForge. ++ ++ ++5. Contact ++---------------------------------------- ++When you have any problems or strange behaviour in aufs, please let me ++know with: ++- /proc/mounts (instead of the output of mount(8)) ++- /sys/module/aufs/* ++- /sys/fs/aufs/* (if you have them) ++- /debug/aufs/* (if you have them) ++- linux kernel version ++ if your kernel is not plain, for example modified by distributor, ++ the url where i can download its source is necessary too. ++- aufs version which was printed at loading the module or booting the ++ system, instead of the date you downloaded. ++- configuration (define/undefine CONFIG_AUFS_xxx) ++- kernel configuration or /proc/config.gz (if you have it) ++- behaviour which you think to be incorrect ++- actual operation, reproducible one is better ++- mailto: aufs-users at lists.sourceforge.net ++ ++Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches, ++and Feature Requests) on SourceForge. Please join and write to ++aufs-users ML. ++ ++ ++6. Acknowledgements ++---------------------------------------- ++Thanks to everyone who have tried and are using aufs, whoever ++have reported a bug or any feedback. ++ ++Especially donators: ++Tomas Matejicek(slax.org) made a donation (much more than once). ++ Since Apr 2010, Tomas M (the author of Slax and Linux Live ++ scripts) is making "doubling" donations. ++ Unfortunately I cannot list all of the donators, but I really ++ appreciate. ++ It ends Aug 2010, but the ordinary donation URL is still available. ++ ++Dai Itasaka made a donation (2007/8). ++Chuck Smith made a donation (2008/4, 10 and 12). ++Henk Schoneveld made a donation (2008/9). ++Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10). ++Francois Dupoux made a donation (2008/11). ++Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public ++ aufs2 GIT tree (2009/2). ++William Grant made a donation (2009/3). ++Patrick Lane made a donation (2009/4). ++The Mail Archive (mail-archive.com) made donations (2009/5). ++Nippy Networks (Ed Wildgoose) made a donation (2009/7). ++New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11). ++Pavel Pronskiy made a donation (2011/2). ++Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy ++ Networks (Ed Wildgoose) made a donation for hardware (2011/3). ++Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and ++11). ++Sam Liddicott made a donation (2011/9). ++Era Scarecrow made a donation (2013/4). ++Bor Ratajc made a donation (2013/4). ++Alessandro Gorreta made a donation (2013/4). ++POIRETTE Marc made a donation (2013/4). ++Alessandro Gorreta made a donation (2013/4). ++lauri kasvandik made a donation (2013/5). ++"pemasu from Finland" made a donation (2013/7). ++The Parted Magic Project made a donation (2013/9 and 11). ++Pavel Barta made a donation (2013/10). ++Nikolay Pertsev made a donation (2014/5). ++James B made a donation (2014/7). ++Stefano Di Biase made a donation (2014/8). ++Daniel Epellei made a donation (2015/1). ++ ++Thank you very much. ++Donations are always, including future donations, very important and ++helpful for me to keep on developing aufs. ++ ++ ++7. ++---------------------------------------- ++If you are an experienced user, no explanation is needed. Aufs is ++just a linux filesystem. ++ ++ ++Enjoy! ++ ++# Local variables: ; ++# mode: text; ++# End: ; +diff --git a/Documentation/filesystems/aufs/design/01intro.txt b/Documentation/filesystems/aufs/design/01intro.txt +new file mode 100644 +index 0000000..02a8c7b +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/01intro.txt +@@ -0,0 +1,157 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Introduction ++---------------------------------------- ++ ++aufs [ei ju: ef es] | [a u f s] ++1. abbrev. for "advanced multi-layered unification filesystem". ++2. abbrev. for "another unionfs". ++3. abbrev. for "auf das" in German which means "on the" in English. ++ Ex. "Butter aufs Brot"(G) means "butter onto bread"(E). ++ But "Filesystem aufs Filesystem" is hard to understand. ++ ++AUFS is a filesystem with features: ++- multi layered stackable unification filesystem, the member directory ++ is called as a branch. ++- branch permission and attribute, 'readonly', 'real-readonly', ++ 'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their ++ combination. ++- internal "file copy-on-write". ++- logical deletion, whiteout. ++- dynamic branch manipulation, adding, deleting and changing permission. ++- allow bypassing aufs, user's direct branch access. ++- external inode number translation table and bitmap which maintains the ++ persistent aufs inode number. ++- seekable directory, including NFS readdir. ++- file mapping, mmap and sharing pages. ++- pseudo-link, hardlink over branches. ++- loopback mounted filesystem as a branch. ++- several policies to select one among multiple writable branches. ++- revert a single systemcall when an error occurs in aufs. ++- and more... ++ ++ ++Multi Layered Stackable Unification Filesystem ++---------------------------------------------------------------------- ++Most people already knows what it is. ++It is a filesystem which unifies several directories and provides a ++merged single directory. When users access a file, the access will be ++passed/re-directed/converted (sorry, I am not sure which English word is ++correct) to the real file on the member filesystem. The member ++filesystem is called 'lower filesystem' or 'branch' and has a mode ++'readonly' and 'readwrite.' And the deletion for a file on the lower ++readonly branch is handled by creating 'whiteout' on the upper writable ++branch. ++ ++On LKML, there have been discussions about UnionMount (Jan Blunck, ++Bharata B Rao and Valerie Aurora) and Unionfs (Erez Zadok). They took ++different approaches to implement the merged-view. ++The former tries putting it into VFS, and the latter implements as a ++separate filesystem. ++(If I misunderstand about these implementations, please let me know and ++I shall correct it. Because it is a long time ago when I read their ++source files last time). ++ ++UnionMount's approach will be able to small, but may be hard to share ++branches between several UnionMount since the whiteout in it is ++implemented in the inode on branch filesystem and always ++shared. According to Bharata's post, readdir does not seems to be ++finished yet. ++There are several missing features known in this implementations such as ++- for users, the inode number may change silently. eg. copy-up. ++- link(2) may break by copy-up. ++- read(2) may get an obsoleted filedata (fstat(2) too). ++- fcntl(F_SETLK) may be broken by copy-up. ++- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after ++ open(O_RDWR). ++ ++In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was ++merged into mainline. This is another implementation of UnionMount as a ++separated filesystem. All the limitations and known problems which ++UnionMount are equally inherited to "overlay" filesystem. ++ ++Unionfs has a longer history. When I started implementing a stackable ++filesystem (Aug 2005), it already existed. It has virtual super_block, ++inode, dentry and file objects and they have an array pointing lower ++same kind objects. After contributing many patches for Unionfs, I ++re-started my project AUFS (Jun 2006). ++ ++In AUFS, the structure of filesystem resembles to Unionfs, but I ++implemented my own ideas, approaches and enhancements and it became ++totally different one. ++ ++Comparing DM snapshot and fs based implementation ++- the number of bytes to be copied between devices is much smaller. ++- the type of filesystem must be one and only. ++- the fs must be writable, no readonly fs, even for the lower original ++ device. so the compression fs will not be usable. but if we use ++ loopback mount, we may address this issue. ++ for instance, ++ mount /cdrom/squashfs.img /sq ++ losetup /sq/ext2.img ++ losetup /somewhere/cow ++ dmsetup "snapshot /dev/loop0 /dev/loop1 ..." ++- it will be difficult (or needs more operations) to extract the ++ difference between the original device and COW. ++- DM snapshot-merge may help a lot when users try merging. in the ++ fs-layer union, users will use rsync(1). ++ ++You may want to read my old paper "Filesystems in LiveCD" ++(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf). ++ ++ ++Several characters/aspects/persona of aufs ++---------------------------------------------------------------------- ++ ++Aufs has several characters, aspects or persona. ++1. a filesystem, callee of VFS helper ++2. sub-VFS, caller of VFS helper for branches ++3. a virtual filesystem which maintains persistent inode number ++4. reader/writer of files on branches such like an application ++ ++1. Callee of VFS Helper ++As an ordinary linux filesystem, aufs is a callee of VFS. For instance, ++unlink(2) from an application reaches sys_unlink() kernel function and ++then vfs_unlink() is called. vfs_unlink() is one of VFS helper and it ++calls filesystem specific unlink operation. Actually aufs implements the ++unlink operation but it behaves like a redirector. ++ ++2. Caller of VFS Helper for Branches ++aufs_unlink() passes the unlink request to the branch filesystem as if ++it were called from VFS. So the called unlink operation of the branch ++filesystem acts as usual. As a caller of VFS helper, aufs should handle ++every necessary pre/post operation for the branch filesystem. ++- acquire the lock for the parent dir on a branch ++- lookup in a branch ++- revalidate dentry on a branch ++- mnt_want_write() for a branch ++- vfs_unlink() for a branch ++- mnt_drop_write() for a branch ++- release the lock on a branch ++ ++3. Persistent Inode Number ++One of the most important issue for a filesystem is to maintain inode ++numbers. This is particularly important to support exporting a ++filesystem via NFS. Aufs is a virtual filesystem which doesn't have a ++backend block device for its own. But some storage is necessary to ++keep and maintain the inode numbers. It may be a large space and may not ++suit to keep in memory. Aufs rents some space from its first writable ++branch filesystem (by default) and creates file(s) on it. These files ++are created by aufs internally and removed soon (currently) keeping ++opened. ++Note: Because these files are removed, they are totally gone after ++ unmounting aufs. It means the inode numbers are not persistent ++ across unmount or reboot. I have a plan to make them really ++ persistent which will be important for aufs on NFS server. ++ ++4. Read/Write Files Internally (copy-on-write) ++Because a branch can be readonly, when you write a file on it, aufs will ++"copy-up" it to the upper writable branch internally. And then write the ++originally requested thing to the file. Generally kernel doesn't ++open/read/write file actively. In aufs, even a single write may cause a ++internal "file copy". This behaviour is very similar to cp(1) command. ++ ++Some people may think it is better to pass such work to user space ++helper, instead of doing in kernel space. Actually I am still thinking ++about it. But currently I have implemented it in kernel space. +diff --git a/Documentation/filesystems/aufs/design/02struct.txt b/Documentation/filesystems/aufs/design/02struct.txt +new file mode 100644 +index 0000000..1a5e5d0 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/02struct.txt +@@ -0,0 +1,245 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Basic Aufs Internal Structure ++ ++Superblock/Inode/Dentry/File Objects ++---------------------------------------------------------------------- ++As like an ordinary filesystem, aufs has its own ++superblock/inode/dentry/file objects. All these objects have a ++dynamically allocated array and store the same kind of pointers to the ++lower filesystem, branch. ++For example, when you build a union with one readwrite branch and one ++readonly, mounted /au, /rw and /ro respectively. ++- /au = /rw + /ro ++- /ro/fileA exists but /rw/fileA ++ ++Aufs lookup operation finds /ro/fileA and gets dentry for that. These ++pointers are stored in a aufs dentry. The array in aufs dentry will be, ++- [0] = NULL (because /rw/fileA doesn't exist) ++- [1] = /ro/fileA ++ ++This style of an array is essentially same to the aufs ++superblock/inode/dentry/file objects. ++ ++Because aufs supports manipulating branches, ie. add/delete/change ++branches dynamically, these objects has its own generation. When ++branches are changed, the generation in aufs superblock is ++incremented. And a generation in other object are compared when it is ++accessed. When a generation in other objects are obsoleted, aufs ++refreshes the internal array. ++ ++ ++Superblock ++---------------------------------------------------------------------- ++Additionally aufs superblock has some data for policies to select one ++among multiple writable branches, XIB files, pseudo-links and kobject. ++See below in detail. ++About the policies which supports copy-down a directory, see ++wbr_policy.txt too. ++ ++ ++Branch and XINO(External Inode Number Translation Table) ++---------------------------------------------------------------------- ++Every branch has its own xino (external inode number translation table) ++file. The xino file is created and unlinked by aufs internally. When two ++members of a union exist on the same filesystem, they share the single ++xino file. ++The struct of a xino file is simple, just a sequence of aufs inode ++numbers which is indexed by the lower inode number. ++In the above sample, assume the inode number of /ro/fileA is i111 and ++aufs assigns the inode number i999 for fileA. Then aufs writes 999 as ++4(8) bytes at 111 * 4(8) bytes offset in the xino file. ++ ++When the inode numbers are not contiguous, the xino file will be sparse ++which has a hole in it and doesn't consume as much disk space as it ++might appear. If your branch filesystem consumes disk space for such ++holes, then you should specify 'xino=' option at mounting aufs. ++ ++Aufs has a mount option to free the disk blocks for such holes in XINO ++files on tmpfs or ramdisk. But it is not so effective actually. If you ++meet a problem of disk shortage due to XINO files, then you should try ++"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git. ++The patch localizes the assignment inumbers per tmpfs-mount and avoid ++the holes in XINO files. ++ ++Also a writable branch has three kinds of "whiteout bases". All these ++are existed when the branch is joined to aufs, and their names are ++whiteout-ed doubly, so that users will never see their names in aufs ++hierarchy. ++1. a regular file which will be hardlinked to all whiteouts. ++2. a directory to store a pseudo-link. ++3. a directory to store an "orphan"-ed file temporary. ++ ++1. Whiteout Base ++ When you remove a file on a readonly branch, aufs handles it as a ++ logical deletion and creates a whiteout on the upper writable branch ++ as a hardlink of this file in order not to consume inode on the ++ writable branch. ++2. Pseudo-link Dir ++ See below, Pseudo-link. ++3. Step-Parent Dir ++ When "fileC" exists on the lower readonly branch only and it is ++ opened and removed with its parent dir, and then user writes ++ something into it, then aufs copies-up fileC to this ++ directory. Because there is no other dir to store fileC. After ++ creating a file under this dir, the file is unlinked. ++ ++Because aufs supports manipulating branches, ie. add/delete/change ++dynamically, a branch has its own id. When the branch order changes, ++aufs finds the new index by searching the branch id. ++ ++ ++Pseudo-link ++---------------------------------------------------------------------- ++Assume "fileA" exists on the lower readonly branch only and it is ++hardlinked to "fileB" on the branch. When you write something to fileA, ++aufs copies-up it to the upper writable branch. Additionally aufs ++creates a hardlink under the Pseudo-link Directory of the writable ++branch. The inode of a pseudo-link is kept in aufs super_block as a ++simple list. If fileB is read after unlinking fileA, aufs returns ++filedata from the pseudo-link instead of the lower readonly ++branch. Because the pseudo-link is based upon the inode, to keep the ++inode number by xino (see above) is essentially necessary. ++ ++All the hardlinks under the Pseudo-link Directory of the writable branch ++should be restored in a proper location later. Aufs provides a utility ++to do this. The userspace helpers executed at remounting and unmounting ++aufs by default. ++During this utility is running, it puts aufs into the pseudo-link ++maintenance mode. In this mode, only the process which began the ++maintenance mode (and its child processes) is allowed to operate in ++aufs. Some other processes which are not related to the pseudo-link will ++be allowed to run too, but the rest have to return an error or wait ++until the maintenance mode ends. If a process already acquires an inode ++mutex (in VFS), it has to return an error. ++ ++ ++XIB(external inode number bitmap) ++---------------------------------------------------------------------- ++Addition to the xino file per a branch, aufs has an external inode number ++bitmap in a superblock object. It is also an internal file such like a ++xino file. ++It is a simple bitmap to mark whether the aufs inode number is in-use or ++not. ++To reduce the file I/O, aufs prepares a single memory page to cache xib. ++ ++As well as XINO files, aufs has a feature to truncate/refresh XIB to ++reduce the number of consumed disk blocks for these files. ++ ++ ++Virtual or Vertical Dir, and Readdir in Userspace ++---------------------------------------------------------------------- ++In order to support multiple layers (branches), aufs readdir operation ++constructs a virtual dir block on memory. For readdir, aufs calls ++vfs_readdir() internally for each dir on branches, merges their entries ++with eliminating the whiteout-ed ones, and sets it to file (dir) ++object. So the file object has its entry list until it is closed. The ++entry list will be updated when the file position is zero and becomes ++obsoleted. This decision is made in aufs automatically. ++ ++The dynamically allocated memory block for the name of entries has a ++unit of 512 bytes (by default) and stores the names contiguously (no ++padding). Another block for each entry is handled by kmem_cache too. ++During building dir blocks, aufs creates hash list and judging whether ++the entry is whiteouted by its upper branch or already listed. ++The merged result is cached in the corresponding inode object and ++maintained by a customizable life-time option. ++ ++Some people may call it can be a security hole or invite DoS attack ++since the opened and once readdir-ed dir (file object) holds its entry ++list and becomes a pressure for system memory. But I'd say it is similar ++to files under /proc or /sys. The virtual files in them also holds a ++memory page (generally) while they are opened. When an idea to reduce ++memory for them is introduced, it will be applied to aufs too. ++For those who really hate this situation, I've developed readdir(3) ++library which operates this merging in userspace. You just need to set ++LD_PRELOAD environment variable, and aufs will not consume no memory in ++kernel space for readdir(3). ++ ++ ++Workqueue ++---------------------------------------------------------------------- ++Aufs sometimes requires privilege access to a branch. For instance, ++in copy-up/down operation. When a user process is going to make changes ++to a file which exists in the lower readonly branch only, and the mode ++of one of ancestor directories may not be writable by a user ++process. Here aufs copy-up the file with its ancestors and they may ++require privilege to set its owner/group/mode/etc. ++This is a typical case of a application character of aufs (see ++Introduction). ++ ++Aufs uses workqueue synchronously for this case. It creates its own ++workqueue. The workqueue is a kernel thread and has privilege. Aufs ++passes the request to call mkdir or write (for example), and wait for ++its completion. This approach solves a problem of a signal handler ++simply. ++If aufs didn't adopt the workqueue and changed the privilege of the ++process, then the process may receive the unexpected SIGXFSZ or other ++signals. ++ ++Also aufs uses the system global workqueue ("events" kernel thread) too ++for asynchronous tasks, such like handling inotify/fsnotify, re-creating a ++whiteout base and etc. This is unrelated to a privilege. ++Most of aufs operation tries acquiring a rw_semaphore for aufs ++superblock at the beginning, at the same time waits for the completion ++of all queued asynchronous tasks. ++ ++ ++Whiteout ++---------------------------------------------------------------------- ++The whiteout in aufs is very similar to Unionfs's. That is represented ++by its filename. UnionMount takes an approach of a file mode, but I am ++afraid several utilities (find(1) or something) will have to support it. ++ ++Basically the whiteout represents "logical deletion" which stops aufs to ++lookup further, but also it represents "dir is opaque" which also stop ++further lookup. ++ ++In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively. ++In order to make several functions in a single systemcall to be ++revertible, aufs adopts an approach to rename a directory to a temporary ++unique whiteouted name. ++For example, in rename(2) dir where the target dir already existed, aufs ++renames the target dir to a temporary unique whiteouted name before the ++actual rename on a branch, and then handles other actions (make it opaque, ++update the attributes, etc). If an error happens in these actions, aufs ++simply renames the whiteouted name back and returns an error. If all are ++succeeded, aufs registers a function to remove the whiteouted unique ++temporary name completely and asynchronously to the system global ++workqueue. ++ ++ ++Copy-up ++---------------------------------------------------------------------- ++It is a well-known feature or concept. ++When user modifies a file on a readonly branch, aufs operate "copy-up" ++internally and makes change to the new file on the upper writable branch. ++When the trigger systemcall does not update the timestamps of the parent ++dir, aufs reverts it after copy-up. ++ ++ ++Move-down (aufs3.9 and later) ++---------------------------------------------------------------------- ++"Copy-up" is one of the essential feature in aufs. It copies a file from ++the lower readonly branch to the upper writable branch when a user ++changes something about the file. ++"Move-down" is an opposite action of copy-up. Basically this action is ++ran manually instead of automatically and internally. ++For desgin and implementation, aufs has to consider these issues. ++- whiteout for the file may exist on the lower branch. ++- ancestor directories may not exist on the lower branch. ++- diropq for the ancestor directories may exist on the upper branch. ++- free space on the lower branch will reduce. ++- another access to the file may happen during moving-down, including ++ UDBA (see "Revalidate Dentry and UDBA"). ++- the file should not be hard-linked nor pseudo-linked. they should be ++ handled by auplink utility later. ++ ++Sometimes users want to move-down a file from the upper writable branch ++to the lower readonly or writable branch. For instance, ++- the free space of the upper writable branch is going to run out. ++- create a new intermediate branch between the upper and lower branch. ++- etc. ++ ++For this purpose, use "aumvdown" command in aufs-util.git. +diff --git a/Documentation/filesystems/aufs/design/03atomic_open.txt b/Documentation/filesystems/aufs/design/03atomic_open.txt +new file mode 100644 +index 0000000..d2c9834 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/03atomic_open.txt +@@ -0,0 +1,72 @@ ++ ++# Copyright (C) 2015 Junjiro R. Okajima ++ ++Support for a branch who has its ->atomic_open() ++---------------------------------------------------------------------- ++The filesystems who implement its ->atomic_open() are not majority. For ++example NFSv4 does, and aufs should call NFSv4 ->atomic_open, ++particularly for open(O_CREAT|O_EXCL, 0400) case. Other than ++->atomic_open(), NFSv4 returns an error for this open(2). While I am not ++sure whether all filesystems who have ->atomic_open() behave like this, ++but NFSv4 surely returns the error. ++ ++In order to support ->atomic_open() for aufs, there are a few ++approaches. ++ ++A. Introduce aufs_atomic_open() ++ - calls one of VFS:do_last(), lookup_open() or atomic_open() for ++ branch fs. ++B. Introduce aufs_atomic_open() calling create, open and chmod. this is ++ an aufs user Pip Cet's approach ++ - calls aufs_create(), VFS finish_open() and notify_change(). ++ - pass fake-mode to finish_open(), and then correct the mode by ++ notify_change(). ++C. Extend aufs_open() to call branch fs's ->atomic_open() ++ - no aufs_atomic_open(). ++ - aufs_lookup() registers the TID to an aufs internal object. ++ - aufs_create() does nothing when the matching TID is registered, but ++ registers the mode. ++ - aufs_open() calls branch fs's ->atomic_open() when the matching ++ TID is registered. ++D. Extend aufs_open() to re-try branch fs's ->open() with superuser's ++ credential ++ - no aufs_atomic_open(). ++ - aufs_create() registers the TID to an internal object. this info ++ represents "this process created this file just now." ++ - when aufs gets EACCES from branch fs's ->open(), then confirm the ++ registered TID and re-try open() with superuser's credential. ++ ++Pros and cons for each approach. ++ ++A. ++ - straightforward but highly depends upon VFS internal. ++ - the atomic behavaiour is kept. ++ - some of parameters such as nameidata are hard to reproduce for ++ branch fs. ++ - large overhead. ++B. ++ - easy to implement. ++ - the atomic behavaiour is lost. ++C. ++ - the atomic behavaiour is kept. ++ - dirty and tricky. ++ - VFS checks whether the file is created correctly after calling ++ ->create(), which means this approach doesn't work. ++D. ++ - easy to implement. ++ - the atomic behavaiour is lost. ++ - to open a file with superuser's credential and give it to a user ++ process is a bad idea, since the file object keeps the credential ++ in it. It may affect LSM or something. This approach doesn't work ++ either. ++ ++The approach A is ideal, but it hard to implement. So here is a ++variation of A, which is to be implemented. ++ ++A-1. Introduce aufs_atomic_open() ++ - calls branch fs ->atomic_open() if exists. otherwise calls ++ vfs_create() and finish_open(). ++ - the demerit is that the several checks after branch fs ++ ->atomic_open() are lost. in the ordinary case, the checks are ++ done by VFS:do_last(), lookup_open() and atomic_open(). some can ++ be implemented in aufs, but not all I am afraid. +diff --git a/Documentation/filesystems/aufs/design/03lookup.txt b/Documentation/filesystems/aufs/design/03lookup.txt +new file mode 100644 +index 0000000..7adf4cc +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/03lookup.txt +@@ -0,0 +1,100 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Lookup in a Branch ++---------------------------------------------------------------------- ++Since aufs has a character of sub-VFS (see Introduction), it operates ++lookup for branches as VFS does. It may be a heavy work. But almost all ++lookup operation in aufs is the simplest case, ie. lookup only an entry ++directly connected to its parent. Digging down the directory hierarchy ++is unnecessary. VFS has a function lookup_one_len() for that use, and ++aufs calls it. ++ ++When a branch is a remote filesystem, aufs basically relies upon its ++->d_revalidate(), also aufs forces the hardest revalidate tests for ++them. ++For d_revalidate, aufs implements three levels of revalidate tests. See ++"Revalidate Dentry and UDBA" in detail. ++ ++ ++Test Only the Highest One for the Directory Permission (dirperm1 option) ++---------------------------------------------------------------------- ++Let's try case study. ++- aufs has two branches, upper readwrite and lower readonly. ++ /au = /rw + /ro ++- "dirA" exists under /ro, but /rw. and its mode is 0700. ++- user invoked "chmod a+rx /au/dirA" ++- the internal copy-up is activated and "/rw/dirA" is created and its ++ permission bits are set to world readable. ++- then "/au/dirA" becomes world readable? ++ ++In this case, /ro/dirA is still 0700 since it exists in readonly branch, ++or it may be a natively readonly filesystem. If aufs respects the lower ++branch, it should not respond readdir request from other users. But user ++allowed it by chmod. Should really aufs rejects showing the entries ++under /ro/dirA? ++ ++To be honest, I don't have a good solution for this case. So aufs ++implements 'dirperm1' and 'nodirperm1' mount options, and leave it to ++users. ++When dirperm1 is specified, aufs checks only the highest one for the ++directory permission, and shows the entries. Otherwise, as usual, checks ++every dir existing on all branches and rejects the request. ++ ++As a side effect, dirperm1 option improves the performance of aufs ++because the number of permission check is reduced when the number of ++branch is many. ++ ++ ++Revalidate Dentry and UDBA (User's Direct Branch Access) ++---------------------------------------------------------------------- ++Generally VFS helpers re-validate a dentry as a part of lookup. ++0. digging down the directory hierarchy. ++1. lock the parent dir by its i_mutex. ++2. lookup the final (child) entry. ++3. revalidate it. ++4. call the actual operation (create, unlink, etc.) ++5. unlock the parent dir ++ ++If the filesystem implements its ->d_revalidate() (step 3), then it is ++called. Actually aufs implements it and checks the dentry on a branch is ++still valid. ++But it is not enough. Because aufs has to release the lock for the ++parent dir on a branch at the end of ->lookup() (step 2) and ++->d_revalidate() (step 3) while the i_mutex of the aufs dir is still ++held by VFS. ++If the file on a branch is changed directly, eg. bypassing aufs, after ++aufs released the lock, then the subsequent operation may cause ++something unpleasant result. ++ ++This situation is a result of VFS architecture, ->lookup() and ++->d_revalidate() is separated. But I never say it is wrong. It is a good ++design from VFS's point of view. It is just not suitable for sub-VFS ++character in aufs. ++ ++Aufs supports such case by three level of revalidation which is ++selectable by user. ++1. Simple Revalidate ++ Addition to the native flow in VFS's, confirm the child-parent ++ relationship on the branch just after locking the parent dir on the ++ branch in the "actual operation" (step 4). When this validation ++ fails, aufs returns EBUSY. ->d_revalidate() (step 3) in aufs still ++ checks the validation of the dentry on branches. ++2. Monitor Changes Internally by Inotify/Fsnotify ++ Addition to above, in the "actual operation" (step 4) aufs re-lookup ++ the dentry on the branch, and returns EBUSY if it finds different ++ dentry. ++ Additionally, aufs sets the inotify/fsnotify watch for every dir on branches ++ during it is in cache. When the event is notified, aufs registers a ++ function to kernel 'events' thread by schedule_work(). And the ++ function sets some special status to the cached aufs dentry and inode ++ private data. If they are not cached, then aufs has nothing to ++ do. When the same file is accessed through aufs (step 0-3) later, ++ aufs will detect the status and refresh all necessary data. ++ In this mode, aufs has to ignore the event which is fired by aufs ++ itself. ++3. No Extra Validation ++ This is the simplest test and doesn't add any additional revalidation ++ test, and skip the revalidation in step 4. It is useful and improves ++ aufs performance when system surely hide the aufs branches from user, ++ by over-mounting something (or another method). +diff --git a/Documentation/filesystems/aufs/design/04branch.txt b/Documentation/filesystems/aufs/design/04branch.txt +new file mode 100644 +index 0000000..1d11dbe +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/04branch.txt +@@ -0,0 +1,61 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Branch Manipulation ++ ++Since aufs supports dynamic branch manipulation, ie. add/remove a branch ++and changing its permission/attribute, there are a lot of works to do. ++ ++ ++Add a Branch ++---------------------------------------------------------------------- ++o Confirm the adding dir exists outside of aufs, including loopback ++ mount, and its various attributes. ++o Initialize the xino file and whiteout bases if necessary. ++ See struct.txt. ++ ++o Check the owner/group/mode of the directory ++ When the owner/group/mode of the adding directory differs from the ++ existing branch, aufs issues a warning because it may impose a ++ security risk. ++ For example, when a upper writable branch has a world writable empty ++ top directory, a malicious user can create any files on the writable ++ branch directly, like copy-up and modify manually. If something like ++ /etc/{passwd,shadow} exists on the lower readonly branch but the upper ++ writable branch, and the writable branch is world-writable, then a ++ malicious guy may create /etc/passwd on the writable branch directly ++ and the infected file will be valid in aufs. ++ I am afraid it can be a security issue, but aufs can do nothing except ++ producing a warning. ++ ++ ++Delete a Branch ++---------------------------------------------------------------------- ++o Confirm the deleting branch is not busy ++ To be general, there is one merit to adopt "remount" interface to ++ manipulate branches. It is to discard caches. At deleting a branch, ++ aufs checks the still cached (and connected) dentries and inodes. If ++ there are any, then they are all in-use. An inode without its ++ corresponding dentry can be alive alone (for example, inotify/fsnotify case). ++ ++ For the cached one, aufs checks whether the same named entry exists on ++ other branches. ++ If the cached one is a directory, because aufs provides a merged view ++ to users, as long as one dir is left on any branch aufs can show the ++ dir to users. In this case, the branch can be removed from aufs. ++ Otherwise aufs rejects deleting the branch. ++ ++ If any file on the deleting branch is opened by aufs, then aufs ++ rejects deleting. ++ ++ ++Modify the Permission of a Branch ++---------------------------------------------------------------------- ++o Re-initialize or remove the xino file and whiteout bases if necessary. ++ See struct.txt. ++ ++o rw --> ro: Confirm the modifying branch is not busy ++ Aufs rejects the request if any of these conditions are true. ++ - a file on the branch is mmap-ed. ++ - a regular file on the branch is opened for write and there is no ++ same named entry on the upper branch. +diff --git a/Documentation/filesystems/aufs/design/05wbr_policy.txt b/Documentation/filesystems/aufs/design/05wbr_policy.txt +new file mode 100644 +index 0000000..0419a90 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/05wbr_policy.txt +@@ -0,0 +1,51 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Policies to Select One among Multiple Writable Branches ++---------------------------------------------------------------------- ++When the number of writable branch is more than one, aufs has to decide ++the target branch for file creation or copy-up. By default, the highest ++writable branch which has the parent (or ancestor) dir of the target ++file is chosen (top-down-parent policy). ++By user's request, aufs implements some other policies to select the ++writable branch, for file creation several policies, round-robin, ++most-free-space, and other policies. For copy-up, top-down-parent, ++bottom-up-parent, bottom-up and others. ++ ++As expected, the round-robin policy selects the branch in circular. When ++you have two writable branches and creates 10 new files, 5 files will be ++created for each branch. mkdir(2) systemcall is an exception. When you ++create 10 new directories, all will be created on the same branch. ++And the most-free-space policy selects the one which has most free ++space among the writable branches. The amount of free space will be ++checked by aufs internally, and users can specify its time interval. ++ ++The policies for copy-up is more simple, ++top-down-parent is equivalent to the same named on in create policy, ++bottom-up-parent selects the writable branch where the parent dir ++exists and the nearest upper one from the copyup-source, ++bottom-up selects the nearest upper writable branch from the ++copyup-source, regardless the existence of the parent dir. ++ ++There are some rules or exceptions to apply these policies. ++- If there is a readonly branch above the policy-selected branch and ++ the parent dir is marked as opaque (a variation of whiteout), or the ++ target (creating) file is whiteout-ed on the upper readonly branch, ++ then the result of the policy is ignored and the target file will be ++ created on the nearest upper writable branch than the readonly branch. ++- If there is a writable branch above the policy-selected branch and ++ the parent dir is marked as opaque or the target file is whiteouted ++ on the branch, then the result of the policy is ignored and the target ++ file will be created on the highest one among the upper writable ++ branches who has diropq or whiteout. In case of whiteout, aufs removes ++ it as usual. ++- link(2) and rename(2) systemcalls are exceptions in every policy. ++ They try selecting the branch where the source exists as possible ++ since copyup a large file will take long time. If it can't be, ++ ie. the branch where the source exists is readonly, then they will ++ follow the copyup policy. ++- There is an exception for rename(2) when the target exists. ++ If the rename target exists, aufs compares the index of the branches ++ where the source and the target exists and selects the higher ++ one. If the selected branch is readonly, then aufs follows the ++ copyup policy. +diff --git a/Documentation/filesystems/aufs/design/06fhsm.txt b/Documentation/filesystems/aufs/design/06fhsm.txt +new file mode 100644 +index 0000000..0eb7a82 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/06fhsm.txt +@@ -0,0 +1,105 @@ ++ ++# Copyright (C) 2011-2015 Junjiro R. Okajima ++ ++File-based Hierarchical Storage Management (FHSM) ++---------------------------------------------------------------------- ++Hierarchical Storage Management (or HSM) is a well-known feature in the ++storage world. Aufs provides this feature as file-based with multiple ++writable branches, based upon the principle of "Colder, the Lower". ++Here the word "colder" means that the less used files, and "lower" means ++that the position in the order of the stacked branches vertically. ++These multiple writable branches are prioritized, ie. the topmost one ++should be the fastest drive and be used heavily. ++ ++o Characters in aufs FHSM story ++- aufs itself and a new branch attribute. ++- a new ioctl interface to move-down and to establish a connection with ++ the daemon ("move-down" is a converse of "copy-up"). ++- userspace tool and daemon. ++ ++The userspace daemon establishes a connection with aufs and waits for ++the notification. The notified information is very similar to struct ++statfs containing the number of consumed blocks and inodes. ++When the consumed blocks/inodes of a branch exceeds the user-specified ++upper watermark, the daemon activates its move-down process until the ++consumed blocks/inodes reaches the user-specified lower watermark. ++ ++The actual move-down is done by aufs based upon the request from ++user-space since we need to maintain the inode number and the internal ++pointer arrays in aufs. ++ ++Currently aufs FHSM handles the regular files only. Additionally they ++must not be hard-linked nor pseudo-linked. ++ ++ ++o Cowork of aufs and the user-space daemon ++ During the userspace daemon established the connection, aufs sends a ++ small notification to it whenever aufs writes something into the ++ writable branch. But it may cost high since aufs issues statfs(2) ++ internally. So user can specify a new option to cache the ++ info. Actually the notification is controlled by these factors. ++ + the specified cache time. ++ + classified as "force" by aufs internally. ++ Until the specified time expires, aufs doesn't send the info ++ except the forced cases. When aufs decide forcing, the info is always ++ notified to userspace. ++ For example, the number of free inodes is generally large enough and ++ the shortage of it happens rarely. So aufs doesn't force the ++ notification when creating a new file, directory and others. This is ++ the typical case which aufs doesn't force. ++ When aufs writes the actual filedata and the files consumes any of new ++ blocks, the aufs forces notifying. ++ ++ ++o Interfaces in aufs ++- New branch attribute. ++ + fhsm ++ Specifies that the branch is managed by FHSM feature. In other word, ++ participant in the FHSM. ++ When nofhsm is set to the branch, it will not be the source/target ++ branch of the move-down operation. This attribute is set ++ independently from coo and moo attributes, and if you want full ++ FHSM, you should specify them as well. ++- New mount option. ++ + fhsm_sec ++ Specifies a second to suppress many less important info to be ++ notified. ++- New ioctl. ++ + AUFS_CTL_FHSM_FD ++ create a new file descriptor which userspace can read the notification ++ (a subset of struct statfs) from aufs. ++- Module parameter 'brs' ++ It has to be set to 1. Otherwise the new mount option 'fhsm' will not ++ be set. ++- mount helpers /sbin/mount.aufs and /sbin/umount.aufs ++ When there are two or more branches with fhsm attributes, ++ /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs ++ terminates it. As a result of remounting and branch-manipulation, the ++ number of branches with fhsm attribute can be one. In this case, ++ /sbin/mount.aufs will terminate the user-space daemon. ++ ++ ++Finally the operation is done as these steps in kernel-space. ++- make sure that, ++ + no one else is using the file. ++ + the file is not hard-linked. ++ + the file is not pseudo-linked. ++ + the file is a regular file. ++ + the parent dir is not opaqued. ++- find the target writable branch. ++- make sure the file is not whiteout-ed by the upper (than the target) ++ branch. ++- make the parent dir on the target branch. ++- mutex lock the inode on the branch. ++- unlink the whiteout on the target branch (if exists). ++- lookup and create the whiteout-ed temporary name on the target branch. ++- copy the file as the whiteout-ed temporary name on the target branch. ++- rename the whiteout-ed temporary name to the original name. ++- unlink the file on the source branch. ++- maintain the internal pointer array and the external inode number ++ table (XINO). ++- maintain the timestamps and other attributes of the parent dir and the ++ file. ++ ++And of course, in every step, an error may happen. So the operation ++should restore the original file state after an error happens. +diff --git a/Documentation/filesystems/aufs/design/06mmap.txt b/Documentation/filesystems/aufs/design/06mmap.txt +new file mode 100644 +index 0000000..f22d2a1 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/06mmap.txt +@@ -0,0 +1,59 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++mmap(2) -- File Memory Mapping ++---------------------------------------------------------------------- ++In aufs, the file-mapped pages are handled by a branch fs directly, no ++interaction with aufs. It means aufs_mmap() calls the branch fs's ++->mmap(). ++This approach is simple and good, but there is one problem. ++Under /proc, several entries show the mmapped files by its path (with ++device and inode number), and the printed path will be the path on the ++branch fs's instead of virtual aufs's. ++This is not a problem in most cases, but some utilities lsof(1) (and its ++user) may expect the path on aufs. ++ ++To address this issue, aufs adds a new member called vm_prfile in struct ++vm_area_struct (and struct vm_region). The original vm_file points to ++the file on the branch fs in order to handle everything correctly as ++usual. The new vm_prfile points to a virtual file in aufs, and the ++show-functions in procfs refers to vm_prfile if it is set. ++Also we need to maintain several other places where touching vm_file ++such like ++- fork()/clone() copies vma and the reference count of vm_file is ++ incremented. ++- merging vma maintains the ref count too. ++ ++This is not a good approach. It just fakes the printed path. But it ++leaves all behaviour around f_mapping unchanged. This is surely an ++advantage. ++Actually aufs had adopted another complicated approach which calls ++generic_file_mmap() and handles struct vm_operations_struct. In this ++approach, aufs met a hard problem and I could not solve it without ++switching the approach. ++ ++There may be one more another approach which is ++- bind-mount the branch-root onto the aufs-root internally ++- grab the new vfsmount (ie. struct mount) ++- lazy-umount the branch-root internally ++- in open(2) the aufs-file, open the branch-file with the hidden ++ vfsmount (instead of the original branch's vfsmount) ++- ideally this "bind-mount and lazy-umount" should be done atomically, ++ but it may be possible from userspace by the mount helper. ++ ++Adding the internal hidden vfsmount and using it in opening a file, the ++file path under /proc will be printed correctly. This approach looks ++smarter, but is not possible I am afraid. ++- aufs-root may be bind-mount later. when it happens, another hidden ++ vfsmount will be required. ++- it is hard to get the chance to bind-mount and lazy-umount ++ + in kernel-space, FS can have vfsmount in open(2) via ++ file->f_path, and aufs can know its vfsmount. But several locks are ++ already acquired, and if aufs tries to bind-mount and lazy-umount ++ here, then it may cause a deadlock. ++ + in user-space, bind-mount doesn't invoke the mount helper. ++- since /proc shows dev and ino, aufs has to give vma these info. it ++ means a new member vm_prinode will be necessary. this is essentially ++ equivalent to vm_prfile described above. ++ ++I have to give up this "looks-smater" approach. +diff --git a/Documentation/filesystems/aufs/design/06xattr.txt b/Documentation/filesystems/aufs/design/06xattr.txt +new file mode 100644 +index 0000000..ec1e68c +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/06xattr.txt +@@ -0,0 +1,81 @@ ++ ++# Copyright (C) 2014-2015 Junjiro R. Okajima ++ ++Listing XATTR/EA and getting the value ++---------------------------------------------------------------------- ++For the inode standard attributes (owner, group, timestamps, etc.), aufs ++shows the values from the topmost existing file. This behaviour is good ++for the non-dir entries since the bahaviour exactly matches the shown ++information. But for the directories, aufs considers all the same named ++entries on the lower branches. Which means, if one of the lower entry ++rejects readdir call, then aufs returns an error even if the topmost ++entry allows it. This behaviour is necessary to respect the branch fs's ++security, but can make users confused since the user-visible standard ++attributes don't match the behaviour. ++To address this issue, aufs has a mount option called dirperm1 which ++checks the permission for the topmost entry only, and ignores the lower ++entry's permission. ++ ++A similar issue can happen around XATTR. ++getxattr(2) and listxattr(2) families behave as if dirperm1 option is ++always set. Otherwise these very unpleasant situation would happen. ++- listxattr(2) may return the duplicated entries. ++- users may not be able to remove or reset the XATTR forever, ++ ++ ++XATTR/EA support in the internal (copy,move)-(up,down) ++---------------------------------------------------------------------- ++Generally the extended attributes of inode are categorized as these. ++- "security" for LSM and capability. ++- "system" for posix ACL, 'acl' mount option is required for the branch ++ fs generally. ++- "trusted" for userspace, CAP_SYS_ADMIN is required. ++- "user" for userspace, 'user_xattr' mount option is required for the ++ branch fs generally. ++ ++Moreover there are some other categories. Aufs handles these rather ++unpopular categories as the ordinary ones, ie. there is no special ++condition nor exception. ++ ++In copy-up, the support for XATTR on the dst branch may differ from the ++src branch. In this case, the copy-up operation will get an error and ++the original user operation which triggered the copy-up will fail. It ++can happen that even all copy-up will fail. ++When both of src and dst branches support XATTR and if an error occurs ++during copying XATTR, then the copy-up should fail obviously. That is a ++good reason and aufs should return an error to userspace. But when only ++the src branch support that XATTR, aufs should not return an error. ++For example, the src branch supports ACL but the dst branch doesn't ++because the dst branch may natively un-support it or temporary ++un-support it due to "noacl" mount option. Of course, the dst branch fs ++may NOT return an error even if the XATTR is not supported. It is ++totally up to the branch fs. ++ ++Anyway when the aufs internal copy-up gets an error from the dst branch ++fs, then aufs tries removing the just copied entry and returns the error ++to the userspace. The worst case of this situation will be all copy-up ++will fail. ++ ++For the copy-up operation, there two basic approaches. ++- copy the specified XATTR only (by category above), and return the ++ error unconditionally if it happens. ++- copy all XATTR, and ignore the error on the specified category only. ++ ++In order to support XATTR and to implement the correct behaviour, aufs ++chooses the latter approach and introduces some new branch attributes, ++"icexsec", "icexsys", "icextr", "icexusr", and "icexoth". ++They correspond to the XATTR namespaces (see above). Additionally, to be ++convenient, "icex" is also provided which means all "icex*" attributes ++are set (here the word "icex" stands for "ignore copy-error on XATTR"). ++ ++The meaning of these attributes is to ignore the error from setting ++XATTR on that branch. ++Note that aufs tries copying all XATTR unconditionally, and ignores the ++error from the dst branch according to the specified attributes. ++ ++Some XATTR may have its default value. The default value may come from ++the parent dir or the environment. If the default value is set at the ++file creating-time, it will be overwritten by copy-up. ++Some contradiction may happen I am afraid. ++Do we need another attribute to stop copying XATTR? I am unsure. For ++now, aufs implements the branch attributes to ignore the error. +diff --git a/Documentation/filesystems/aufs/design/07export.txt b/Documentation/filesystems/aufs/design/07export.txt +new file mode 100644 +index 0000000..221d70c +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/07export.txt +@@ -0,0 +1,45 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Export Aufs via NFS ++---------------------------------------------------------------------- ++Here is an approach. ++- like xino/xib, add a new file 'xigen' which stores aufs inode ++ generation. ++- iget_locked(): initialize aufs inode generation for a new inode, and ++ store it in xigen file. ++- destroy_inode(): increment aufs inode generation and store it in xigen ++ file. it is necessary even if it is not unlinked, because any data of ++ inode may be changed by UDBA. ++- encode_fh(): for a root dir, simply return FILEID_ROOT. otherwise ++ build file handle by ++ + branch id (4 bytes) ++ + superblock generation (4 bytes) ++ + inode number (4 or 8 bytes) ++ + parent dir inode number (4 or 8 bytes) ++ + inode generation (4 bytes)) ++ + return value of exportfs_encode_fh() for the parent on a branch (4 ++ bytes) ++ + file handle for a branch (by exportfs_encode_fh()) ++- fh_to_dentry(): ++ + find the index of a branch from its id in handle, and check it is ++ still exist in aufs. ++ + 1st level: get the inode number from handle and search it in cache. ++ + 2nd level: if not found in cache, get the parent inode number from ++ the handle and search it in cache. and then open the found parent ++ dir, find the matching inode number by vfs_readdir() and get its ++ name, and call lookup_one_len() for the target dentry. ++ + 3rd level: if the parent dir is not cached, call ++ exportfs_decode_fh() for a branch and get the parent on a branch, ++ build a pathname of it, convert it a pathname in aufs, call ++ path_lookup(). now aufs gets a parent dir dentry, then handle it as ++ the 2nd level. ++ + to open the dir, aufs needs struct vfsmount. aufs keeps vfsmount ++ for every branch, but not itself. to get this, (currently) aufs ++ searches in current->nsproxy->mnt_ns list. it may not be a good ++ idea, but I didn't get other approach. ++ + test the generation of the gotten inode. ++- every inode operation: they may get EBUSY due to UDBA. in this case, ++ convert it into ESTALE for NFSD. ++- readdir(): call lockdep_on/off() because filldir in NFSD calls ++ lookup_one_len(), vfs_getattr(), encode_fh() and others. +diff --git a/Documentation/filesystems/aufs/design/08shwh.txt b/Documentation/filesystems/aufs/design/08shwh.txt +new file mode 100644 +index 0000000..181dc02 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/08shwh.txt +@@ -0,0 +1,39 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Show Whiteout Mode (shwh) ++---------------------------------------------------------------------- ++Generally aufs hides the name of whiteouts. But in some cases, to show ++them is very useful for users. For instance, creating a new middle layer ++(branch) by merging existing layers. ++ ++(borrowing aufs1 HOW-TO from a user, Michael Towers) ++When you have three branches, ++- Bottom: 'system', squashfs (underlying base system), read-only ++- Middle: 'mods', squashfs, read-only ++- Top: 'overlay', ram (tmpfs), read-write ++ ++The top layer is loaded at boot time and saved at shutdown, to preserve ++the changes made to the system during the session. ++When larger changes have been made, or smaller changes have accumulated, ++the size of the saved top layer data grows. At this point, it would be ++nice to be able to merge the two overlay branches ('mods' and 'overlay') ++and rewrite the 'mods' squashfs, clearing the top layer and thus ++restoring save and load speed. ++ ++This merging is simplified by the use of another aufs mount, of just the ++two overlay branches using the 'shwh' option. ++# mount -t aufs -o ro,shwh,br:/livesys/overlay=ro+wh:/livesys/mods=rr+wh \ ++ aufs /livesys/merge_union ++ ++A merged view of these two branches is then available at ++/livesys/merge_union, and the new feature is that the whiteouts are ++visible! ++Note that in 'shwh' mode the aufs mount must be 'ro', which will disable ++writing to all branches. Also the default mode for all branches is 'ro'. ++It is now possible to save the combined contents of the two overlay ++branches to a new squashfs, e.g.: ++# mksquashfs /livesys/merge_union /path/to/newmods.squash ++ ++This new squashfs archive can be stored on the boot device and the ++initramfs will use it to replace the old one at the next boot. +diff --git a/Documentation/filesystems/aufs/design/10dynop.txt b/Documentation/filesystems/aufs/design/10dynop.txt +new file mode 100644 +index 0000000..9d502b5 +--- /dev/null ++++ b/Documentation/filesystems/aufs/design/10dynop.txt +@@ -0,0 +1,34 @@ ++ ++# Copyright (C) 2010-2015 Junjiro R. Okajima ++ ++Dynamically customizable FS operations ++---------------------------------------------------------------------- ++Generally FS operations (struct inode_operations, struct ++address_space_operations, struct file_operations, etc.) are defined as ++"static const", but it never means that FS have only one set of ++operation. Some FS have multiple sets of them. For instance, ext2 has ++three sets, one for XIP, for NOBH, and for normal. ++Since aufs overrides and redirects these operations, sometimes aufs has ++to change its behaviour according to the branch FS type. More importantly ++VFS acts differently if a function (member in the struct) is set or ++not. It means aufs should have several sets of operations and select one ++among them according to the branch FS definition. ++ ++In order to solve this problem and not to affect the behaviour of VFS, ++aufs defines these operations dynamically. For instance, aufs defines ++dummy direct_IO function for struct address_space_operations, but it may ++not be set to the address_space_operations actually. When the branch FS ++doesn't have it, aufs doesn't set it to its address_space_operations ++while the function definition itself is still alive. So the behaviour ++itself will not change, and it will return an error when direct_IO is ++not set. ++ ++The lifetime of these dynamically generated operation object is ++maintained by aufs branch object. When the branch is removed from aufs, ++the reference counter of the object is decremented. When it reaches ++zero, the dynamically generated operation object will be freed. ++ ++This approach is designed to support AIO (io_submit), Direct I/O and ++XIP (DAX) mainly. ++Currently this approach is applied to address_space_operations for ++regular files only. +diff --git a/MAINTAINERS b/MAINTAINERS +index d8afd29..feac5ea 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1880,6 +1880,19 @@ F: include/linux/audit.h + F: include/uapi/linux/audit.h + F: kernel/audit* + ++AUFS (advanced multi layered unification filesystem) FILESYSTEM ++M: "J. R. Okajima" ++L: linux-unionfs@vger.kernel.org ++L: aufs-users@lists.sourceforge.net (members only) ++W: http://aufs.sourceforge.net ++T: git://github.com/sfjro/aufs4-linux.git ++S: Supported ++F: Documentation/filesystems/aufs/ ++F: Documentation/ABI/testing/debugfs-aufs ++F: Documentation/ABI/testing/sysfs-aufs ++F: fs/aufs/ ++F: include/uapi/linux/aufs_type.h ++ + AUXILIARY DISPLAY DRIVERS + M: Miguel Ojeda Sandonis + W: http://miguelojeda.es/auxdisplay.htm +diff --git a/drivers/block/loop.c b/drivers/block/loop.c +index d7173cb..0160952 100644 +--- a/drivers/block/loop.c ++++ b/drivers/block/loop.c +@@ -540,6 +540,24 @@ static inline int is_loop_device(struct file *file) + return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; + } + ++/* ++ * for AUFS ++ * no get/put for file. ++ */ ++struct file *loop_backing_file(struct super_block *sb) ++{ ++ struct file *ret; ++ struct loop_device *l; ++ ++ ret = NULL; ++ if (MAJOR(sb->s_dev) == LOOP_MAJOR) { ++ l = sb->s_bdev->bd_disk->private_data; ++ ret = l->lo_backing_file; ++ } ++ return ret; ++} ++EXPORT_SYMBOL(loop_backing_file); ++ + /* loop sysfs attributes */ + + static ssize_t loop_attr_show(struct device *dev, char *page, +diff --git a/fs/Kconfig b/fs/Kconfig +index 011f433..b1083f6 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -218,6 +218,7 @@ source "fs/pstore/Kconfig" + source "fs/sysv/Kconfig" + source "fs/ufs/Kconfig" + source "fs/exofs/Kconfig" ++source "fs/aufs/Kconfig" + + endif # MISC_FILESYSTEMS + +diff --git a/fs/Makefile b/fs/Makefile +index cb92fd4..8c2df12 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -127,3 +127,4 @@ obj-y += exofs/ # Multiple modules + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_AUFS_FS) += aufs/ +diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig +new file mode 100644 +index 0000000..a4efb8b +--- /dev/null ++++ b/fs/aufs/Kconfig +@@ -0,0 +1,185 @@ ++config AUFS_FS ++ bool "Aufs (Advanced multi layered unification filesystem) support" ++ help ++ Aufs is a stackable unification filesystem such as Unionfs, ++ which unifies several directories and provides a merged single ++ directory. ++ In the early days, aufs was entirely re-designed and ++ re-implemented Unionfs Version 1.x series. Introducing many ++ original ideas, approaches and improvements, it becomes totally ++ different from Unionfs while keeping the basic features. ++ ++if AUFS_FS ++choice ++ prompt "Maximum number of branches" ++ default AUFS_BRANCH_MAX_127 ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_127 ++ bool "127" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_511 ++ bool "511" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_1023 ++ bool "1023" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_32767 ++ bool "32767" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++endchoice ++ ++config AUFS_SBILIST ++ bool ++ depends on AUFS_MAGIC_SYSRQ || PROC_FS ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq or /proc, enabled automatically. ++ ++config AUFS_HNOTIFY ++ bool "Detect direct branch access (bypassing aufs)" ++ help ++ If you want to modify files on branches directly, eg. bypassing aufs, ++ and want aufs to detect the changes of them fully, then enable this ++ option and use 'udba=notify' mount option. ++ Currently there is only one available configuration, "fsnotify". ++ It will have a negative impact to the performance. ++ See detail in aufs.5. ++ ++choice ++ prompt "method" if AUFS_HNOTIFY ++ default AUFS_HFSNOTIFY ++config AUFS_HFSNOTIFY ++ bool "fsnotify" ++ select FSNOTIFY ++endchoice ++ ++config AUFS_EXPORT ++ bool "NFS-exportable aufs" ++ depends on EXPORTFS = y ++ help ++ If you want to export your mounted aufs via NFS, then enable this ++ option. There are several requirements for this configuration. ++ See detail in aufs.5. ++ ++config AUFS_INO_T_64 ++ bool ++ depends on AUFS_EXPORT ++ depends on 64BIT && !(ALPHA || S390) ++ default y ++ help ++ Automatic configuration for internal use. ++ /* typedef unsigned long/int __kernel_ino_t */ ++ /* alpha and s390x are int */ ++ ++config AUFS_XATTR ++ bool "support for XATTR/EA (including Security Labels)" ++ help ++ If your branch fs supports XATTR/EA and you want to make them ++ available in aufs too, then enable this opsion and specify the ++ branch attributes for EA. ++ See detail in aufs.5. ++ ++config AUFS_FHSM ++ bool "File-based Hierarchical Storage Management" ++ help ++ Hierarchical Storage Management (or HSM) is a well-known feature ++ in the storage world. Aufs provides this feature as file-based. ++ with multiple branches. ++ These multiple branches are prioritized, ie. the topmost one ++ should be the fastest drive and be used heavily. ++ ++config AUFS_RDU ++ bool "Readdir in userspace" ++ help ++ Aufs has two methods to provide a merged view for a directory, ++ by a user-space library and by kernel-space natively. The latter ++ is always enabled but sometimes large and slow. ++ If you enable this option, install the library in aufs2-util ++ package, and set some environment variables for your readdir(3), ++ then the work will be handled in user-space which generally ++ shows better performance in most cases. ++ See detail in aufs.5. ++ ++config AUFS_SHWH ++ bool "Show whiteouts" ++ help ++ If you want to make the whiteouts in aufs visible, then enable ++ this option and specify 'shwh' mount option. Although it may ++ sounds like philosophy or something, but in technically it ++ simply shows the name of whiteout with keeping its behaviour. ++ ++config AUFS_BR_RAMFS ++ bool "Ramfs (initramfs/rootfs) as an aufs branch" ++ help ++ If you want to use ramfs as an aufs branch fs, then enable this ++ option. Generally tmpfs is recommended. ++ Aufs prohibited them to be a branch fs by default, because ++ initramfs becomes unusable after switch_root or something ++ generally. If you sets initramfs as an aufs branch and boot your ++ system by switch_root, you will meet a problem easily since the ++ files in initramfs may be inaccessible. ++ Unless you are going to use ramfs as an aufs branch fs without ++ switch_root or something, leave it N. ++ ++config AUFS_BR_FUSE ++ bool "Fuse fs as an aufs branch" ++ depends on FUSE_FS ++ select AUFS_POLL ++ help ++ If you want to use fuse-based userspace filesystem as an aufs ++ branch fs, then enable this option. ++ It implements the internal poll(2) operation which is ++ implemented by fuse only (curretnly). ++ ++config AUFS_POLL ++ bool ++ help ++ Automatic configuration for internal use. ++ ++config AUFS_BR_HFSPLUS ++ bool "Hfsplus as an aufs branch" ++ depends on HFSPLUS_FS ++ default y ++ help ++ If you want to use hfsplus fs as an aufs branch fs, then enable ++ this option. This option introduces a small overhead at ++ copying-up a file on hfsplus. ++ ++config AUFS_BDEV_LOOP ++ bool ++ depends on BLK_DEV_LOOP ++ default y ++ help ++ Automatic configuration for internal use. ++ Convert =[ym] into =y. ++ ++config AUFS_DEBUG ++ bool "Debug aufs" ++ help ++ Enable this to compile aufs internal debug code. ++ It will have a negative impact to the performance. ++ ++config AUFS_MAGIC_SYSRQ ++ bool ++ depends on AUFS_DEBUG && MAGIC_SYSRQ ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq, enabled automatically. ++endif +diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile +new file mode 100644 +index 0000000..c7efb62 +--- /dev/null ++++ b/fs/aufs/Makefile +@@ -0,0 +1,36 @@ ++ ++include ${srctree}/${src}/magic.mk ++ ++# cf. include/linux/kernel.h ++# enable pr_debug ++ccflags-y += -DDEBUG ++# sparse requires the full pathname ++ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h ++ ++obj-$(CONFIG_AUFS_FS) += aufs.o ++aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ++ wkq.o vfsub.o dcsub.o \ ++ cpup.o whout.o wbr_policy.o \ ++ dinfo.o dentry.o \ ++ dynop.o \ ++ finfo.o file.o f_op.o \ ++ dir.o vdir.o \ ++ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ ++ mvdown.o ioctl.o ++ ++# all are boolean ++aufs-$(CONFIG_PROC_FS) += procfs.o plink.o ++aufs-$(CONFIG_SYSFS) += sysfs.o ++aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o ++aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o ++aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o ++aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o ++aufs-$(CONFIG_AUFS_EXPORT) += export.o ++aufs-$(CONFIG_AUFS_XATTR) += xattr.o ++aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o ++aufs-$(CONFIG_AUFS_FHSM) += fhsm.o ++aufs-$(CONFIG_AUFS_POLL) += poll.o ++aufs-$(CONFIG_AUFS_RDU) += rdu.o ++aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o ++aufs-$(CONFIG_AUFS_DEBUG) += debug.o ++aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o +diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h +new file mode 100644 +index 0000000..75290bd +--- /dev/null ++++ b/fs/aufs/aufs.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * all header files ++ */ ++ ++#ifndef __AUFS_H__ ++#define __AUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#define AuStub(type, name, body, ...) \ ++ static inline type name(__VA_ARGS__) { body; } ++ ++#define AuStubVoid(name, ...) \ ++ AuStub(void, name, , __VA_ARGS__) ++#define AuStubInt0(name, ...) \ ++ AuStub(int, name, return 0, __VA_ARGS__) ++ ++#include "debug.h" ++ ++#include "branch.h" ++#include "cpup.h" ++#include "dcsub.h" ++#include "dbgaufs.h" ++#include "dentry.h" ++#include "dir.h" ++#include "dynop.h" ++#include "file.h" ++#include "fstype.h" ++#include "inode.h" ++#include "loop.h" ++#include "module.h" ++#include "opts.h" ++#include "rwsem.h" ++#include "spl.h" ++#include "super.h" ++#include "sysaufs.h" ++#include "vfsub.h" ++#include "whout.h" ++#include "wkq.h" ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_H__ */ +diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c +new file mode 100644 +index 0000000..d34e789 +--- /dev/null ++++ b/fs/aufs/branch.c +@@ -0,0 +1,1401 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * branch management ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * free a single branch ++ */ ++static void au_br_do_free(struct au_branch *br) ++{ ++ int i; ++ struct au_wbr *wbr; ++ struct au_dykey **key; ++ ++ au_hnotify_fin_br(br); ++ ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ mutex_destroy(&br->br_xino.xi_nondir_mtx); ++ ++ AuDebugOn(atomic_read(&br->br_count)); ++ ++ wbr = br->br_wbr; ++ if (wbr) { ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(wbr->wbr_wh[i]); ++ AuDebugOn(atomic_read(&wbr->wbr_wh_running)); ++ AuRwDestroy(&wbr->wbr_wh_rwsem); ++ } ++ ++ if (br->br_fhsm) { ++ au_br_fhsm_fin(br->br_fhsm); ++ kfree(br->br_fhsm); ++ } ++ ++ key = br->br_dykey; ++ for (i = 0; i < AuBrDynOp; i++, key++) ++ if (*key) ++ au_dy_put(*key); ++ else ++ break; ++ ++ /* recursive lock, s_umount of branch's */ ++ lockdep_off(); ++ path_put(&br->br_path); ++ lockdep_on(); ++ kfree(wbr); ++ kfree(br); ++} ++ ++/* ++ * frees all branches ++ */ ++void au_br_free(struct au_sbinfo *sbinfo) ++{ ++ aufs_bindex_t bmax; ++ struct au_branch **br; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ bmax = sbinfo->si_bend + 1; ++ br = sbinfo->si_branch; ++ while (bmax--) ++ au_br_do_free(*br++); ++} ++ ++/* ++ * find the index of a branch which is specified by @br_id. ++ */ ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (au_sbr_id(sb, bindex) == br_id) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * add a branch ++ */ ++ ++static int test_overlap(struct super_block *sb, struct dentry *h_adding, ++ struct dentry *h_root) ++{ ++ if (unlikely(h_adding == h_root ++ || au_test_loopback_overlap(sb, h_adding))) ++ return 1; ++ if (h_adding->d_sb != h_root->d_sb) ++ return 0; ++ return au_test_subdir(h_adding, h_root) ++ || au_test_subdir(h_root, h_adding); ++} ++ ++/* ++ * returns a newly allocated branch. @new_nbranch is a number of branches ++ * after adding a branch. ++ */ ++static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, ++ int perm) ++{ ++ struct au_branch *add_branch; ++ struct dentry *root; ++ struct inode *inode; ++ int err; ++ ++ err = -ENOMEM; ++ root = sb->s_root; ++ add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS); ++ if (unlikely(!add_branch)) ++ goto out; ++ ++ err = au_hnotify_init_br(add_branch, perm); ++ if (unlikely(err)) ++ goto out_br; ++ ++ add_branch->br_wbr = NULL; ++ if (au_br_writable(perm)) { ++ /* may be freed separately at changing the branch permission */ ++ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), ++ GFP_NOFS); ++ if (unlikely(!add_branch->br_wbr)) ++ goto out_hnotify; ++ } ++ ++ add_branch->br_fhsm = NULL; ++ if (au_br_fhsm(perm)) { ++ err = au_fhsm_br_alloc(add_branch); ++ if (unlikely(err)) ++ goto out_wbr; ++ } ++ ++ err = au_sbr_realloc(au_sbi(sb), new_nbranch); ++ if (!err) ++ err = au_di_realloc(au_di(root), new_nbranch); ++ if (!err) { ++ inode = d_inode(root); ++ err = au_ii_realloc(au_ii(inode), new_nbranch); ++ } ++ if (!err) ++ return add_branch; /* success */ ++ ++out_wbr: ++ kfree(add_branch->br_wbr); ++out_hnotify: ++ au_hnotify_fin_br(add_branch); ++out_br: ++ kfree(add_branch); ++out: ++ return ERR_PTR(err); ++} ++ ++/* ++ * test if the branch permission is legal or not. ++ */ ++static int test_br(struct inode *inode, int brperm, char *path) ++{ ++ int err; ++ ++ err = (au_br_writable(brperm) && IS_RDONLY(inode)); ++ if (!err) ++ goto out; ++ ++ err = -EINVAL; ++ pr_err("write permission for readonly mount or inode, %s\n", path); ++ ++out: ++ return err; ++} ++ ++/* ++ * returns: ++ * 0: success, the caller will add it ++ * plus: success, it is already unified, the caller should ignore it ++ * minus: error ++ */ ++static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root, *h_dentry; ++ struct inode *inode, *h_inode; ++ ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ if (unlikely(bend >= 0 ++ && au_find_dbindex(root, add->path.dentry) >= 0)) { ++ err = 1; ++ if (!remount) { ++ err = -EINVAL; ++ pr_err("%s duplicated\n", add->pathname); ++ } ++ goto out; ++ } ++ ++ err = -ENOSPC; /* -E2BIG; */ ++ if (unlikely(AUFS_BRANCH_MAX <= add->bindex ++ || AUFS_BRANCH_MAX - 1 <= bend)) { ++ pr_err("number of branches exceeded %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EDOM; ++ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { ++ pr_err("bad index %d\n", add->bindex); ++ goto out; ++ } ++ ++ inode = d_inode(add->path.dentry); ++ err = -ENOENT; ++ if (unlikely(!inode->i_nlink)) { ++ pr_err("no existence %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ if (unlikely(inode->i_sb == sb)) { ++ pr_err("%s must be outside\n", add->pathname); ++ goto out; ++ } ++ ++ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { ++ pr_err("unsupported filesystem, %s (%s)\n", ++ add->pathname, au_sbtype(inode->i_sb)); ++ goto out; ++ } ++ ++ if (unlikely(inode->i_sb->s_stack_depth)) { ++ pr_err("already stacked, %s (%s)\n", ++ add->pathname, au_sbtype(inode->i_sb)); ++ goto out; ++ } ++ ++ err = test_br(d_inode(add->path.dentry), add->perm, add->pathname); ++ if (unlikely(err)) ++ goto out; ++ ++ if (bend < 0) ++ return 0; /* success */ ++ ++ err = -EINVAL; ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (unlikely(test_overlap(sb, add->path.dentry, ++ au_h_dptr(root, bindex)))) { ++ pr_err("%s is overlapped\n", add->pathname); ++ goto out; ++ } ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), WARN_PERM)) { ++ h_dentry = au_h_dptr(root, 0); ++ h_inode = d_inode(h_dentry); ++ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) ++ || !uid_eq(h_inode->i_uid, inode->i_uid) ++ || !gid_eq(h_inode->i_gid, inode->i_gid)) ++ pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", ++ add->pathname, ++ i_uid_read(inode), i_gid_read(inode), ++ (inode->i_mode & S_IALLUGO), ++ i_uid_read(h_inode), i_gid_read(h_inode), ++ (h_inode->i_mode & S_IALLUGO)); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * initialize or clean the whiteouts for an adding branch ++ */ ++static int au_br_init_wh(struct super_block *sb, struct au_branch *br, ++ int new_perm) ++{ ++ int err, old_perm; ++ aufs_bindex_t bindex; ++ struct mutex *h_mtx; ++ struct au_wbr *wbr; ++ struct au_hinode *hdir; ++ struct dentry *h_dentry; ++ ++ err = vfsub_mnt_want_write(au_br_mnt(br)); ++ if (unlikely(err)) ++ goto out; ++ ++ wbr = br->br_wbr; ++ old_perm = br->br_perm; ++ br->br_perm = new_perm; ++ hdir = NULL; ++ h_mtx = NULL; ++ bindex = au_br_index(sb, br->br_id); ++ if (0 <= bindex) { ++ hdir = au_hi(d_inode(sb->s_root), bindex); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ } else { ++ h_dentry = au_br_dentry(br); ++ h_mtx = &d_inode(h_dentry)->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); ++ } ++ if (!wbr) ++ err = au_wh_init(br, sb); ++ else { ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(br, sb); ++ wbr_wh_write_unlock(wbr); ++ } ++ if (hdir) ++ au_hn_imtx_unlock(hdir); ++ else ++ mutex_unlock(h_mtx); ++ vfsub_mnt_drop_write(au_br_mnt(br)); ++ br->br_perm = old_perm; ++ ++ if (!err && wbr && !au_br_writable(new_perm)) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ ++out: ++ return err; ++} ++ ++static int au_wbr_init(struct au_branch *br, struct super_block *sb, ++ int perm) ++{ ++ int err; ++ struct kstatfs kst; ++ struct au_wbr *wbr; ++ ++ wbr = br->br_wbr; ++ au_rw_init(&wbr->wbr_wh_rwsem); ++ memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); ++ atomic_set(&wbr->wbr_wh_running, 0); ++ wbr->wbr_bytes = 0; ++ ++ /* ++ * a limit for rmdir/rename a dir ++ * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h ++ */ ++ err = vfs_statfs(&br->br_path, &kst); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ if (kst.f_namelen >= NAME_MAX) ++ err = au_br_init_wh(sb, br, perm); ++ else ++ pr_err("%pd(%s), unsupported namelen %ld\n", ++ au_br_dentry(br), ++ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); ++ ++out: ++ return err; ++} ++ ++/* initialize a new branch */ ++static int au_br_init(struct au_branch *br, struct super_block *sb, ++ struct au_opt_add *add) ++{ ++ int err; ++ struct inode *h_inode; ++ ++ err = 0; ++ memset(&br->br_xino, 0, sizeof(br->br_xino)); ++ mutex_init(&br->br_xino.xi_nondir_mtx); ++ br->br_perm = add->perm; ++ br->br_path = add->path; /* set first, path_get() later */ ++ spin_lock_init(&br->br_dykey_lock); ++ memset(br->br_dykey, 0, sizeof(br->br_dykey)); ++ atomic_set(&br->br_count, 0); ++ atomic_set(&br->br_xino_running, 0); ++ br->br_id = au_new_br_id(sb); ++ AuDebugOn(br->br_id < 0); ++ ++ if (au_br_writable(add->perm)) { ++ err = au_wbr_init(br, sb, add->perm); ++ if (unlikely(err)) ++ goto out_err; ++ } ++ ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ h_inode = d_inode(add->path.dentry); ++ err = au_xino_br(sb, br, h_inode->i_ino, ++ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); ++ if (unlikely(err)) { ++ AuDebugOn(br->br_xino.xi_file); ++ goto out_err; ++ } ++ } ++ ++ sysaufs_br_init(br); ++ path_get(&br->br_path); ++ goto out; /* success */ ++ ++out_err: ++ memset(&br->br_path, 0, sizeof(br->br_path)); ++out: ++ return err; ++} ++ ++static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, ++ struct au_branch *br, aufs_bindex_t bend, ++ aufs_bindex_t amount) ++{ ++ struct au_branch **brp; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ brp = sbinfo->si_branch + bindex; ++ memmove(brp + 1, brp, sizeof(*brp) * amount); ++ *brp = br; ++ sbinfo->si_bend++; ++ if (unlikely(bend < 0)) ++ sbinfo->si_bend = 0; ++} ++ ++static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hdentry *hdp; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ hdp = dinfo->di_hdentry + bindex; ++ memmove(hdp + 1, hdp, sizeof(*hdp) * amount); ++ au_h_dentry_init(hdp); ++ dinfo->di_bend++; ++ if (unlikely(bend < 0)) ++ dinfo->di_bstart = 0; ++} ++ ++static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hinode *hip; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ hip = iinfo->ii_hinode + bindex; ++ memmove(hip + 1, hip, sizeof(*hip) * amount); ++ hip->hi_inode = NULL; ++ au_hn_init(hip); ++ iinfo->ii_bend++; ++ if (unlikely(bend < 0)) ++ iinfo->ii_bstart = 0; ++} ++ ++static void au_br_do_add(struct super_block *sb, struct au_branch *br, ++ aufs_bindex_t bindex) ++{ ++ struct dentry *root, *h_dentry; ++ struct inode *root_inode, *h_inode; ++ aufs_bindex_t bend, amount; ++ ++ root = sb->s_root; ++ root_inode = d_inode(root); ++ bend = au_sbend(sb); ++ amount = bend + 1 - bindex; ++ h_dentry = au_br_dentry(br); ++ au_sbilist_lock(); ++ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); ++ au_br_do_add_hdp(au_di(root), bindex, bend, amount); ++ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); ++ au_set_h_dptr(root, bindex, dget(h_dentry)); ++ h_inode = d_inode(h_dentry); ++ au_set_h_iptr(root_inode, bindex, au_igrab(h_inode), /*flags*/0); ++ au_sbilist_unlock(); ++} ++ ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ aufs_bindex_t bend, add_bindex; ++ struct dentry *root, *h_dentry; ++ struct inode *root_inode; ++ struct au_branch *add_branch; ++ ++ root = sb->s_root; ++ root_inode = d_inode(root); ++ IMustLock(root_inode); ++ err = test_add(sb, add, remount); ++ if (unlikely(err < 0)) ++ goto out; ++ if (err) { ++ err = 0; ++ goto out; /* success */ ++ } ++ ++ bend = au_sbend(sb); ++ add_branch = au_br_alloc(sb, bend + 2, add->perm); ++ err = PTR_ERR(add_branch); ++ if (IS_ERR(add_branch)) ++ goto out; ++ ++ err = au_br_init(add_branch, sb, add); ++ if (unlikely(err)) { ++ au_br_do_free(add_branch); ++ goto out; ++ } ++ ++ add_bindex = add->bindex; ++ if (!remount) ++ au_br_do_add(sb, add_branch, add_bindex); ++ else { ++ sysaufs_brs_del(sb, add_bindex); ++ au_br_do_add(sb, add_branch, add_bindex); ++ sysaufs_brs_add(sb, add_bindex); ++ } ++ ++ h_dentry = add->path.dentry; ++ if (!add_bindex) { ++ au_cpup_attr_all(root_inode, /*force*/1); ++ sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; ++ } else ++ au_add_nlink(root_inode, d_inode(h_dentry)); ++ ++ /* ++ * this test/set prevents aufs from handling unnecesary notify events ++ * of xino files, in case of re-adding a writable branch which was ++ * once detached from aufs. ++ */ ++ if (au_xino_brid(sb) < 0 ++ && au_br_writable(add_branch->br_perm) ++ && !au_test_fs_bad_xino(h_dentry->d_sb) ++ && add_branch->br_xino.xi_file ++ && add_branch->br_xino.xi_file->f_path.dentry->d_parent == h_dentry) ++ au_xino_brid_set(sb, add_branch->br_id); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static unsigned long long au_farray_cb(void *a, ++ unsigned long long max __maybe_unused, ++ void *arg) ++{ ++ unsigned long long n; ++ struct file **p, *f; ++ struct au_sphlhead *files; ++ struct au_finfo *finfo; ++ struct super_block *sb = arg; ++ ++ n = 0; ++ p = a; ++ files = &au_sbi(sb)->si_files; ++ spin_lock(&files->spin); ++ hlist_for_each_entry(finfo, &files->head, fi_hlist) { ++ f = finfo->fi_file; ++ if (file_count(f) ++ && !special_file(file_inode(f)->i_mode)) { ++ get_file(f); ++ *p++ = f; ++ n++; ++ AuDebugOn(n > max); ++ } ++ } ++ spin_unlock(&files->spin); ++ ++ return n; ++} ++ ++static struct file **au_farray_alloc(struct super_block *sb, ++ unsigned long long *max) ++{ ++ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); ++ return au_array_alloc(max, au_farray_cb, sb); ++} ++ ++static void au_farray_free(struct file **a, unsigned long long max) ++{ ++ unsigned long long ull; ++ ++ for (ull = 0; ull < max; ull++) ++ if (a[ull]) ++ fput(a[ull]); ++ au_array_free(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * delete a branch ++ */ ++ ++/* to show the line number, do not make it inlined function */ ++#define AuVerbose(do_info, fmt, ...) do { \ ++ if (do_info) \ ++ pr_info(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, ++ aufs_bindex_t bend) ++{ ++ return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; ++} ++ ++static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, ++ aufs_bindex_t bend) ++{ ++ return au_test_ibusy(d_inode(dentry), bstart, bend); ++} ++ ++/* ++ * test if the branch is deletable or not. ++ */ ++static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, ++ unsigned int sigen, const unsigned int verbose) ++{ ++ int err, i, j, ndentry; ++ aufs_bindex_t bstart, bend; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry *d; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, root, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ for (i = 0; !err && i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = 0; !err && j < ndentry; j++) { ++ d = dpage->dentries[j]; ++ AuDebugOn(au_dcount(d) <= 0); ++ if (!au_digen_test(d, sigen)) { ++ di_read_lock_child(d, AuLock_IR); ++ if (unlikely(au_dbrange_test(d))) { ++ di_read_unlock(d, AuLock_IR); ++ continue; ++ } ++ } else { ++ di_write_lock_child(d); ++ if (unlikely(au_dbrange_test(d))) { ++ di_write_unlock(d); ++ continue; ++ } ++ err = au_reval_dpath(d, sigen); ++ if (!err) ++ di_downgrade_lock(d, AuLock_IR); ++ else { ++ di_write_unlock(d); ++ break; ++ } ++ } ++ ++ /* AuDbgDentry(d); */ ++ bstart = au_dbstart(d); ++ bend = au_dbend(d); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_dptr(d, bindex) ++ && au_test_dbusy(d, bstart, bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy %pd\n", d); ++ AuDbgDentry(d); ++ } ++ di_read_unlock(d, AuLock_IR); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, ++ unsigned int sigen, const unsigned int verbose) ++{ ++ int err; ++ unsigned long long max, ull; ++ struct inode *i, **array; ++ aufs_bindex_t bstart, bend; ++ ++ array = au_iarray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ AuDbg("b%d\n", bindex); ++ for (ull = 0; !err && ull < max; ull++) { ++ i = array[ull]; ++ if (unlikely(!i)) ++ break; ++ if (i->i_ino == AUFS_ROOT_INO) ++ continue; ++ ++ /* AuDbgInode(i); */ ++ if (au_iigen(i, NULL) == sigen) ++ ii_read_lock_child(i); ++ else { ++ ii_write_lock_child(i); ++ err = au_refresh_hinode_self(i); ++ au_iigen_dec(i); ++ if (!err) ++ ii_downgrade_lock(i); ++ else { ++ ii_write_unlock(i); ++ break; ++ } ++ } ++ ++ bstart = au_ibstart(i); ++ bend = au_ibend(i); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_iptr(i, bindex) ++ && au_test_ibusy(i, bstart, bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy i%lu\n", i->i_ino); ++ AuDbgInode(i); ++ } ++ ii_read_unlock(i); ++ } ++ au_iarray_free(array, max); ++ ++out: ++ return err; ++} ++ ++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, ++ const unsigned int verbose) ++{ ++ int err; ++ unsigned int sigen; ++ ++ sigen = au_sigen(root->d_sb); ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(d_inode(root)); ++ di_write_unlock(root); ++ err = test_dentry_busy(root, bindex, sigen, verbose); ++ if (!err) ++ err = test_inode_busy(root->d_sb, bindex, sigen, verbose); ++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ ++ ++ return err; ++} ++ ++static int test_dir_busy(struct file *file, aufs_bindex_t br_id, ++ struct file **to_free, int *idx) ++{ ++ int err; ++ unsigned char matched, root; ++ aufs_bindex_t bindex, bend; ++ struct au_fidir *fidir; ++ struct au_hfile *hfile; ++ ++ err = 0; ++ root = IS_ROOT(file->f_path.dentry); ++ if (root) { ++ get_file(file); ++ to_free[*idx] = file; ++ (*idx)++; ++ goto out; ++ } ++ ++ matched = 0; ++ fidir = au_fi(file)->fi_hdir; ++ AuDebugOn(!fidir); ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); bindex <= bend; bindex++) { ++ hfile = fidir->fd_hfile + bindex; ++ if (!hfile->hf_file) ++ continue; ++ ++ if (hfile->hf_br->br_id == br_id) { ++ matched = 1; ++ break; ++ } ++ } ++ if (matched) ++ err = -EBUSY; ++ ++out: ++ return err; ++} ++ ++static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, ++ struct file **to_free, int opened) ++{ ++ int err, idx; ++ unsigned long long ull, max; ++ aufs_bindex_t bstart; ++ struct file *file, **array; ++ struct dentry *root; ++ struct au_hfile *hfile; ++ ++ array = au_farray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ idx = 0; ++ root = sb->s_root; ++ di_write_unlock(root); ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ if (unlikely(!file)) ++ break; ++ ++ /* AuDbg("%pD\n", file); */ ++ fi_read_lock(file); ++ bstart = au_fbstart(file); ++ if (!d_is_dir(file->f_path.dentry)) { ++ hfile = &au_fi(file)->fi_htop; ++ if (hfile->hf_br->br_id == br_id) ++ err = -EBUSY; ++ } else ++ err = test_dir_busy(file, br_id, to_free, &idx); ++ fi_read_unlock(file); ++ if (unlikely(err)) ++ break; ++ } ++ di_write_lock_child(root); ++ au_farray_free(array, max); ++ AuDebugOn(idx > opened); ++ ++out: ++ return err; ++} ++ ++static void br_del_file(struct file **to_free, unsigned long long opened, ++ aufs_bindex_t br_id) ++{ ++ unsigned long long ull; ++ aufs_bindex_t bindex, bstart, bend, bfound; ++ struct file *file; ++ struct au_fidir *fidir; ++ struct au_hfile *hfile; ++ ++ for (ull = 0; ull < opened; ull++) { ++ file = to_free[ull]; ++ if (unlikely(!file)) ++ break; ++ ++ /* AuDbg("%pD\n", file); */ ++ AuDebugOn(!d_is_dir(file->f_path.dentry)); ++ bfound = -1; ++ fidir = au_fi(file)->fi_hdir; ++ AuDebugOn(!fidir); ++ fi_write_lock(file); ++ bstart = au_fbstart(file); ++ bend = au_fbend_dir(file); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ hfile = fidir->fd_hfile + bindex; ++ if (!hfile->hf_file) ++ continue; ++ ++ if (hfile->hf_br->br_id == br_id) { ++ bfound = bindex; ++ break; ++ } ++ } ++ AuDebugOn(bfound < 0); ++ au_set_h_fptr(file, bfound, NULL); ++ if (bfound == bstart) { ++ for (bstart++; bstart <= bend; bstart++) ++ if (au_hf_dir(file, bstart)) { ++ au_set_fbstart(file, bstart); ++ break; ++ } ++ } ++ fi_write_unlock(file); ++ } ++} ++ ++static void au_br_do_del_brp(struct au_sbinfo *sbinfo, ++ const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_branch **brp, **p; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ brp = sbinfo->si_branch + bindex; ++ if (bindex < bend) ++ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); ++ sbinfo->si_branch[0 + bend] = NULL; ++ sbinfo->si_bend--; ++ ++ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); ++ if (p) ++ sbinfo->si_branch = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hdentry *hdp, *p; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ hdp = dinfo->di_hdentry; ++ if (bindex < bend) ++ memmove(hdp + bindex, hdp + bindex + 1, ++ sizeof(*hdp) * (bend - bindex)); ++ hdp[0 + bend].hd_dentry = NULL; ++ dinfo->di_bend--; ++ ++ p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); ++ if (p) ++ dinfo->di_hdentry = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hinode *hip, *p; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ hip = iinfo->ii_hinode + bindex; ++ if (bindex < bend) ++ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); ++ iinfo->ii_hinode[0 + bend].hi_inode = NULL; ++ au_hn_init(iinfo->ii_hinode + bend); ++ iinfo->ii_bend--; ++ ++ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); ++ if (p) ++ iinfo->ii_hinode = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_branch *br) ++{ ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root, *h_root; ++ struct inode *inode, *h_inode; ++ struct au_hinode *hinode; ++ ++ SiMustWriteLock(sb); ++ ++ root = sb->s_root; ++ inode = d_inode(root); ++ sbinfo = au_sbi(sb); ++ bend = sbinfo->si_bend; ++ ++ h_root = au_h_dptr(root, bindex); ++ hinode = au_hi(inode, bindex); ++ h_inode = au_igrab(hinode->hi_inode); ++ au_hiput(hinode); ++ ++ au_sbilist_lock(); ++ au_br_do_del_brp(sbinfo, bindex, bend); ++ au_br_do_del_hdp(au_di(root), bindex, bend); ++ au_br_do_del_hip(au_ii(inode), bindex, bend); ++ au_sbilist_unlock(); ++ ++ dput(h_root); ++ iput(h_inode); ++ au_br_do_free(br); ++} ++ ++static unsigned long long empty_cb(void *array, unsigned long long max, ++ void *arg) ++{ ++ return max; ++} ++ ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) ++{ ++ int err, rerr, i; ++ unsigned long long opened; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bend, br_id; ++ unsigned char do_wh, verbose; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *root; ++ struct file **to_free; ++ ++ err = 0; ++ opened = 0; ++ to_free = NULL; ++ root = sb->s_root; ++ bindex = au_find_dbindex(root, del->h_path.dentry); ++ if (bindex < 0) { ++ if (remount) ++ goto out; /* success */ ++ err = -ENOENT; ++ pr_err("%s no such branch\n", del->pathname); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = -EBUSY; ++ mnt_flags = au_mntflags(sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ bend = au_sbend(sb); ++ if (unlikely(!bend)) { ++ AuVerbose(verbose, "no more branches left\n"); ++ goto out; ++ } ++ br = au_sbr(sb, bindex); ++ AuDebugOn(!path_equal(&br->br_path, &del->h_path)); ++ ++ br_id = br->br_id; ++ opened = atomic_read(&br->br_count); ++ if (unlikely(opened)) { ++ to_free = au_array_alloc(&opened, empty_cb, NULL); ++ err = PTR_ERR(to_free); ++ if (IS_ERR(to_free)) ++ goto out; ++ ++ err = test_file_busy(sb, br_id, to_free, opened); ++ if (unlikely(err)) { ++ AuVerbose(verbose, "%llu file(s) opened\n", opened); ++ goto out; ++ } ++ } ++ ++ wbr = br->br_wbr; ++ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); ++ if (do_wh) { ++ /* instead of WbrWhMustWriteLock(wbr) */ ++ SiMustWriteLock(sb); ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ } ++ ++ err = test_children_busy(root, bindex, verbose); ++ if (unlikely(err)) { ++ if (do_wh) ++ goto out_wh; ++ goto out; ++ } ++ ++ err = 0; ++ if (to_free) { ++ /* ++ * now we confirmed the branch is deletable. ++ * let's free the remaining opened dirs on the branch. ++ */ ++ di_write_unlock(root); ++ br_del_file(to_free, opened, br_id); ++ di_write_lock_child(root); ++ } ++ ++ if (!remount) ++ au_br_do_del(sb, bindex, br); ++ else { ++ sysaufs_brs_del(sb, bindex); ++ au_br_do_del(sb, bindex, br); ++ sysaufs_brs_add(sb, bindex); ++ } ++ ++ if (!bindex) { ++ au_cpup_attr_all(d_inode(root), /*force*/1); ++ sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; ++ } else ++ au_sub_nlink(d_inode(root), d_inode(del->h_path.dentry)); ++ if (au_opt_test(mnt_flags, PLINK)) ++ au_plink_half_refresh(sb, br_id); ++ ++ if (au_xino_brid(sb) == br_id) ++ au_xino_brid_set(sb, -1); ++ goto out; /* success */ ++ ++out_wh: ++ /* revert */ ++ rerr = au_br_init_wh(sb, br, br->br_perm); ++ if (rerr) ++ pr_warn("failed re-creating base whiteout, %s. (%d)\n", ++ del->pathname, rerr); ++out: ++ if (to_free) ++ au_farray_free(to_free, opened); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) ++{ ++ int err; ++ aufs_bindex_t bstart, bend; ++ struct aufs_ibusy ibusy; ++ struct inode *inode, *h_inode; ++ ++ err = -EPERM; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = copy_from_user(&ibusy, arg, sizeof(ibusy)); ++ if (!err) ++ err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ si_read_lock(sb, AuLock_FLUSH); ++ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) ++ goto out_unlock; ++ ++ err = 0; ++ ibusy.h_ino = 0; /* invalid */ ++ inode = ilookup(sb, ibusy.ino); ++ if (!inode ++ || inode->i_ino == AUFS_ROOT_INO ++ || is_bad_inode(inode)) ++ goto out_unlock; ++ ++ ii_read_lock_child(inode); ++ bstart = au_ibstart(inode); ++ bend = au_ibend(inode); ++ if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { ++ h_inode = au_h_iptr(inode, ibusy.bindex); ++ if (h_inode && au_test_ibusy(inode, bstart, bend)) ++ ibusy.h_ino = h_inode->i_ino; ++ } ++ ii_read_unlock(inode); ++ iput(inode); ++ ++out_unlock: ++ si_read_unlock(sb); ++ if (!err) { ++ err = __put_user(ibusy.h_ino, &arg->h_ino); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ } ++out: ++ return err; ++} ++ ++long au_ibusy_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_ibusy(file->f_path.dentry->d_sb, (void __user *)arg); ++} ++ ++#ifdef CONFIG_COMPAT ++long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_ibusy(file->f_path.dentry->d_sb, compat_ptr(arg)); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * change a branch permission ++ */ ++ ++static void au_warn_ima(void) ++{ ++#ifdef CONFIG_IMA ++ /* since it doesn't support mark_files_ro() */ ++ AuWarn1("RW -> RO makes IMA to produce wrong message\n"); ++#endif ++} ++ ++static int do_need_sigen_inc(int a, int b) ++{ ++ return au_br_whable(a) && !au_br_whable(b); ++} ++ ++static int need_sigen_inc(int old, int new) ++{ ++ return do_need_sigen_inc(old, new) ++ || do_need_sigen_inc(new, old); ++} ++ ++static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err, do_warn; ++ unsigned int mnt_flags; ++ unsigned long long ull, max; ++ aufs_bindex_t br_id; ++ unsigned char verbose, writer; ++ struct file *file, *hf, **array; ++ struct au_hfile *hfile; ++ ++ mnt_flags = au_mntflags(sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ ++ array = au_farray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ do_warn = 0; ++ br_id = au_sbr_id(sb, bindex); ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ if (unlikely(!file)) ++ break; ++ ++ /* AuDbg("%pD\n", file); */ ++ fi_read_lock(file); ++ if (unlikely(au_test_mmapped(file))) { ++ err = -EBUSY; ++ AuVerbose(verbose, "mmapped %pD\n", file); ++ AuDbgFile(file); ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ goto out_array; ++ } ++ ++ hfile = &au_fi(file)->fi_htop; ++ hf = hfile->hf_file; ++ if (!d_is_reg(file->f_path.dentry) ++ || !(file->f_mode & FMODE_WRITE) ++ || hfile->hf_br->br_id != br_id ++ || !(hf->f_mode & FMODE_WRITE)) ++ array[ull] = NULL; ++ else { ++ do_warn = 1; ++ get_file(file); ++ } ++ ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ fput(file); ++ } ++ ++ err = 0; ++ if (do_warn) ++ au_warn_ima(); ++ ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ if (!file) ++ continue; ++ ++ /* todo: already flushed? */ ++ /* ++ * fs/super.c:mark_files_ro() is gone, but aufs keeps its ++ * approach which resets f_mode and calls mnt_drop_write() and ++ * file_release_write() for each file, because the branch ++ * attribute in aufs world is totally different from the native ++ * fs rw/ro mode. ++ */ ++ /* fi_read_lock(file); */ ++ hfile = &au_fi(file)->fi_htop; ++ hf = hfile->hf_file; ++ /* fi_read_unlock(file); */ ++ spin_lock(&hf->f_lock); ++ writer = !!(hf->f_mode & FMODE_WRITER); ++ hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); ++ spin_unlock(&hf->f_lock); ++ if (writer) { ++ put_write_access(file_inode(hf)); ++ __mnt_drop_write(hf->f_path.mnt); ++ } ++ } ++ ++out_array: ++ au_farray_free(array, max); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_refresh) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ struct dentry *root; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ root = sb->s_root; ++ bindex = au_find_dbindex(root, mod->h_root); ++ if (bindex < 0) { ++ if (remount) ++ return 0; /* success */ ++ err = -ENOENT; ++ pr_err("%s no such branch\n", mod->path); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = test_br(d_inode(mod->h_root), mod->perm, mod->path); ++ if (unlikely(err)) ++ goto out; ++ ++ br = au_sbr(sb, bindex); ++ AuDebugOn(mod->h_root != au_br_dentry(br)); ++ if (br->br_perm == mod->perm) ++ return 0; /* success */ ++ ++ /* pre-allocate for non-fhsm --> fhsm */ ++ bf = NULL; ++ if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { ++ err = au_fhsm_br_alloc(br); ++ if (unlikely(err)) ++ goto out; ++ bf = br->br_fhsm; ++ br->br_fhsm = NULL; ++ } ++ ++ if (au_br_writable(br->br_perm)) { ++ /* remove whiteout base */ ++ err = au_br_init_wh(sb, br, mod->perm); ++ if (unlikely(err)) ++ goto out_bf; ++ ++ if (!au_br_writable(mod->perm)) { ++ /* rw --> ro, file might be mmapped */ ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(d_inode(root)); ++ di_write_unlock(root); ++ err = au_br_mod_files_ro(sb, bindex); ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ ++ if (unlikely(err)) { ++ rerr = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), ++ GFP_NOFS); ++ if (br->br_wbr) ++ rerr = au_wbr_init(br, sb, br->br_perm); ++ if (unlikely(rerr)) { ++ AuIOErr("nested error %d (%d)\n", ++ rerr, err); ++ br->br_perm = mod->perm; ++ } ++ } ++ } ++ } else if (au_br_writable(mod->perm)) { ++ /* ro --> rw */ ++ err = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); ++ if (br->br_wbr) { ++ err = au_wbr_init(br, sb, mod->perm); ++ if (unlikely(err)) { ++ kfree(br->br_wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ } ++ if (unlikely(err)) ++ goto out_bf; ++ ++ if (au_br_fhsm(br->br_perm)) { ++ if (!au_br_fhsm(mod->perm)) { ++ /* fhsm --> non-fhsm */ ++ au_br_fhsm_fin(br->br_fhsm); ++ kfree(br->br_fhsm); ++ br->br_fhsm = NULL; ++ } ++ } else if (au_br_fhsm(mod->perm)) ++ /* non-fhsm --> fhsm */ ++ br->br_fhsm = bf; ++ ++ *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); ++ br->br_perm = mod->perm; ++ goto out; /* success */ ++ ++out_bf: ++ kfree(bf); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) ++{ ++ int err; ++ struct kstatfs kstfs; ++ ++ err = vfs_statfs(&br->br_path, &kstfs); ++ if (!err) { ++ stfs->f_blocks = kstfs.f_blocks; ++ stfs->f_bavail = kstfs.f_bavail; ++ stfs->f_files = kstfs.f_files; ++ stfs->f_ffree = kstfs.f_ffree; ++ } ++ ++ return err; ++} +diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h +new file mode 100644 +index 0000000..7b9e7c9 +--- /dev/null ++++ b/fs/aufs/branch.h +@@ -0,0 +1,266 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * branch filesystems and xino for them ++ */ ++ ++#ifndef __AUFS_BRANCH_H__ ++#define __AUFS_BRANCH_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "dynop.h" ++#include "rwsem.h" ++#include "super.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* a xino file */ ++struct au_xino_file { ++ struct file *xi_file; ++ struct mutex xi_nondir_mtx; ++ ++ /* todo: make xino files an array to support huge inode number */ ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *xi_dbgaufs; ++#endif ++}; ++ ++/* File-based Hierarchical Storage Management */ ++struct au_br_fhsm { ++#ifdef CONFIG_AUFS_FHSM ++ struct mutex bf_lock; ++ unsigned long bf_jiffy; ++ struct aufs_stfs bf_stfs; ++ int bf_readable; ++#endif ++}; ++ ++/* members for writable branch only */ ++enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; ++struct au_wbr { ++ struct au_rwsem wbr_wh_rwsem; ++ struct dentry *wbr_wh[AuBrWh_Last]; ++ atomic_t wbr_wh_running; ++#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ ++#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ ++#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ ++ ++ /* mfs mode */ ++ unsigned long long wbr_bytes; ++}; ++ ++/* ext2 has 3 types of operations at least, ext3 has 4 */ ++#define AuBrDynOp (AuDyLast * 4) ++ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++/* support for asynchronous destruction */ ++struct au_br_hfsnotify { ++ struct fsnotify_group *hfsn_group; ++}; ++#endif ++ ++/* sysfs entries */ ++struct au_brsysfs { ++ char name[16]; ++ struct attribute attr; ++}; ++ ++enum { ++ AuBrSysfs_BR, ++ AuBrSysfs_BRID, ++ AuBrSysfs_Last ++}; ++ ++/* protected by superblock rwsem */ ++struct au_branch { ++ struct au_xino_file br_xino; ++ ++ aufs_bindex_t br_id; ++ ++ int br_perm; ++ struct path br_path; ++ spinlock_t br_dykey_lock; ++ struct au_dykey *br_dykey[AuBrDynOp]; ++ atomic_t br_count; ++ ++ struct au_wbr *br_wbr; ++ struct au_br_fhsm *br_fhsm; ++ ++ /* xino truncation */ ++ atomic_t br_xino_running; ++ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ struct au_br_hfsnotify *br_hfsn; ++#endif ++ ++#ifdef CONFIG_SYSFS ++ /* entries under sysfs per mount-point */ ++ struct au_brsysfs br_sysfs[AuBrSysfs_Last]; ++#endif ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct vfsmount *au_br_mnt(struct au_branch *br) ++{ ++ return br->br_path.mnt; ++} ++ ++static inline struct dentry *au_br_dentry(struct au_branch *br) ++{ ++ return br->br_path.dentry; ++} ++ ++static inline struct super_block *au_br_sb(struct au_branch *br) ++{ ++ return au_br_mnt(br)->mnt_sb; ++} ++ ++static inline int au_br_rdonly(struct au_branch *br) ++{ ++ return ((au_br_sb(br)->s_flags & MS_RDONLY) ++ || !au_br_writable(br->br_perm)) ++ ? -EROFS : 0; ++} ++ ++static inline int au_br_hnotifyable(int brperm __maybe_unused) ++{ ++#ifdef CONFIG_AUFS_HNOTIFY ++ return !(brperm & AuBrPerm_RR); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_br_test_oflag(int oflag, struct au_branch *br) ++{ ++ int err, exec_flag; ++ ++ err = 0; ++ exec_flag = oflag & __FMODE_EXEC; ++ if (unlikely(exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC))) ++ err = -EACCES; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* branch.c */ ++struct au_sbinfo; ++void au_br_free(struct au_sbinfo *sinfo); ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id); ++struct au_opt_add; ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); ++struct au_opt_del; ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); ++long au_ibusy_ioctl(struct file *file, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); ++#endif ++struct au_opt_mod; ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_refresh); ++struct aufs_stfs; ++int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); ++ ++/* xino.c */ ++static const loff_t au_loff_max = LLONG_MAX; ++ ++int au_xib_trunc(struct super_block *sb); ++ssize_t xino_fread(vfs_readf_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos); ++ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos); ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src); ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent); ++ino_t au_xino_new_ino(struct super_block *sb); ++void au_xino_delete_inode(struct inode *inode, const int unlinked); ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino); ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino); ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, ++ struct file *base_file, int do_test); ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); ++ ++struct au_opt_xino; ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); ++void au_xino_clr(struct super_block *sb); ++struct file *au_xino_def(struct super_block *sb); ++int au_xino_path(struct seq_file *seq, struct file *file); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Superblock to branch */ ++static inline ++aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_id; ++} ++ ++static inline ++struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_mnt(au_sbr(sb, bindex)); ++} ++ ++static inline ++struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_sb(au_sbr(sb, bindex)); ++} ++ ++static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ atomic_dec(&au_sbr(sb, bindex)->br_count); ++} ++ ++static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_perm; ++} ++ ++static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_whable(au_sbr_perm(sb, bindex)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * wbr_wh_read_lock, wbr_wh_write_lock ++ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); ++ ++#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) ++#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) ++#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_FHSM ++static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) ++{ ++ mutex_init(&brfhsm->bf_lock); ++ brfhsm->bf_jiffy = 0; ++ brfhsm->bf_readable = 0; ++} ++ ++static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) ++{ ++ mutex_destroy(&brfhsm->bf_lock); ++} ++#else ++AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) ++AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_BRANCH_H__ */ +diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c +new file mode 100644 +index 0000000..f6f6818 +--- /dev/null ++++ b/fs/aufs/cpup.c +@@ -0,0 +1,1306 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * copy-up functions, see wbr_policy.c for copy-down ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) ++{ ++ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE ++ | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; ++ ++ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); ++ ++ dst->i_flags |= iflags & ~mask; ++ if (au_test_fs_notime(dst->i_sb)) ++ dst->i_flags |= S_NOATIME | S_NOCMTIME; ++} ++ ++void au_cpup_attr_timesizes(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ fsstack_copy_attr_times(inode, h_inode); ++ fsstack_copy_inode_size(inode, h_inode); ++} ++ ++void au_cpup_attr_nlink(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ ++ sb = inode->i_sb; ++ bindex = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (!force ++ && !S_ISDIR(h_inode->i_mode) ++ && au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode)) ++ return; ++ ++ /* ++ * 0 can happen in revalidating. ++ * h_inode->i_mutex may not be held here, but it is harmless since once ++ * i_nlink reaches 0, it will never become positive except O_TMPFILE ++ * case. ++ * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause ++ * the incorrect link count. ++ */ ++ set_nlink(inode, h_inode->i_nlink); ++ ++ /* ++ * fewer nlink makes find(1) noisy, but larger nlink doesn't. ++ * it may includes whplink directory. ++ */ ++ if (S_ISDIR(h_inode->i_mode)) { ++ bend = au_ibend(inode); ++ for (bindex++; bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) ++ au_add_nlink(inode, h_inode); ++ } ++ } ++} ++ ++void au_cpup_attr_changeable(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ inode->i_mode = h_inode->i_mode; ++ inode->i_uid = h_inode->i_uid; ++ inode->i_gid = h_inode->i_gid; ++ au_cpup_attr_timesizes(inode); ++ au_cpup_attr_flags(inode, h_inode->i_flags); ++} ++ ++void au_cpup_igen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ IiMustWriteLock(inode); ++ ++ iinfo->ii_higen = h_inode->i_generation; ++ iinfo->ii_hsb1 = h_inode->i_sb; ++} ++ ++void au_cpup_attr_all(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ au_cpup_attr_changeable(inode); ++ if (inode->i_nlink > 0) ++ au_cpup_attr_nlink(inode, force); ++ inode->i_rdev = h_inode->i_rdev; ++ inode->i_blkbits = h_inode->i_blkbits; ++ au_cpup_igen(inode, h_inode); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ ++ ++/* keep the timestamps of the parent dir when cpup */ ++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, ++ struct path *h_path) ++{ ++ struct inode *h_inode; ++ ++ dt->dt_dentry = dentry; ++ dt->dt_h_path = *h_path; ++ h_inode = d_inode(h_path->dentry); ++ dt->dt_atime = h_inode->i_atime; ++ dt->dt_mtime = h_inode->i_mtime; ++ /* smp_mb(); */ ++} ++ ++void au_dtime_revert(struct au_dtime *dt) ++{ ++ struct iattr attr; ++ int err; ++ ++ attr.ia_atime = dt->dt_atime; ++ attr.ia_mtime = dt->dt_mtime; ++ attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET ++ | ATTR_ATIME | ATTR_ATIME_SET; ++ ++ /* no delegation since this is a directory */ ++ err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); ++ if (unlikely(err)) ++ pr_warn("restoring timestamps failed(%d). ignored\n", err); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* internal use only */ ++struct au_cpup_reg_attr { ++ int valid; ++ struct kstat st; ++ unsigned int iflags; /* inode->i_flags */ ++}; ++ ++static noinline_for_stack ++int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, ++ struct au_cpup_reg_attr *h_src_attr) ++{ ++ int err, sbits, icex; ++ unsigned int mnt_flags; ++ unsigned char verbose; ++ struct iattr ia; ++ struct path h_path; ++ struct inode *h_isrc, *h_idst; ++ struct kstat *h_st; ++ struct au_branch *br; ++ ++ h_path.dentry = au_h_dptr(dst, bindex); ++ h_idst = d_inode(h_path.dentry); ++ br = au_sbr(dst->d_sb, bindex); ++ h_path.mnt = au_br_mnt(br); ++ h_isrc = d_inode(h_src); ++ ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID ++ | ATTR_ATIME | ATTR_MTIME ++ | ATTR_ATIME_SET | ATTR_MTIME_SET; ++ if (h_src_attr && h_src_attr->valid) { ++ h_st = &h_src_attr->st; ++ ia.ia_uid = h_st->uid; ++ ia.ia_gid = h_st->gid; ++ ia.ia_atime = h_st->atime; ++ ia.ia_mtime = h_st->mtime; ++ if (h_idst->i_mode != h_st->mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_st->mode; ++ } ++ sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_src_attr->iflags); ++ } else { ++ ia.ia_uid = h_isrc->i_uid; ++ ia.ia_gid = h_isrc->i_gid; ++ ia.ia_atime = h_isrc->i_atime; ++ ia.ia_mtime = h_isrc->i_mtime; ++ if (h_idst->i_mode != h_isrc->i_mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ } ++ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_isrc->i_flags); ++ } ++ /* no delegation since it is just created */ ++ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); ++ } ++ ++ icex = br->br_perm & AuBrAttr_ICEX; ++ if (!err) { ++ mnt_flags = au_mntflags(dst->d_sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, ++ char *buf, unsigned long blksize) ++{ ++ int err; ++ size_t sz, rbytes, wbytes; ++ unsigned char all_zero; ++ char *p, *zp; ++ struct mutex *h_mtx; ++ /* reduce stack usage */ ++ struct iattr *ia; ++ ++ zp = page_address(ZERO_PAGE(0)); ++ if (unlikely(!zp)) ++ return -ENOMEM; /* possible? */ ++ ++ err = 0; ++ all_zero = 0; ++ while (len) { ++ AuDbg("len %lld\n", len); ++ sz = blksize; ++ if (len < blksize) ++ sz = len; ++ ++ rbytes = 0; ++ /* todo: signal_pending? */ ++ while (!rbytes || err == -EAGAIN || err == -EINTR) { ++ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); ++ err = rbytes; ++ } ++ if (unlikely(err < 0)) ++ break; ++ ++ all_zero = 0; ++ if (len >= rbytes && rbytes == blksize) ++ all_zero = !memcmp(buf, zp, rbytes); ++ if (!all_zero) { ++ wbytes = rbytes; ++ p = buf; ++ while (wbytes) { ++ size_t b; ++ ++ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); ++ err = b; ++ /* todo: signal_pending? */ ++ if (unlikely(err == -EAGAIN || err == -EINTR)) ++ continue; ++ if (unlikely(err < 0)) ++ break; ++ wbytes -= b; ++ p += b; ++ } ++ if (unlikely(err < 0)) ++ break; ++ } else { ++ loff_t res; ++ ++ AuLabel(hole); ++ res = vfsub_llseek(dst, rbytes, SEEK_CUR); ++ err = res; ++ if (unlikely(res < 0)) ++ break; ++ } ++ len -= rbytes; ++ err = 0; ++ } ++ ++ /* the last block may be a hole */ ++ if (!err && all_zero) { ++ AuLabel(last hole); ++ ++ err = 1; ++ if (au_test_nfs(dst->f_path.dentry->d_sb)) { ++ /* nfs requires this step to make last hole */ ++ /* is this only nfs? */ ++ do { ++ /* todo: signal_pending? */ ++ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ if (err == 1) ++ dst->f_pos--; ++ } ++ ++ if (err == 1) { ++ ia = (void *)buf; ++ ia->ia_size = dst->f_pos; ++ ia->ia_valid = ATTR_SIZE | ATTR_FILE; ++ ia->ia_file = dst; ++ h_mtx = &file_inode(dst)->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ /* no delegation since it is just created */ ++ err = vfsub_notify_change(&dst->f_path, ia, ++ /*delegated*/NULL); ++ mutex_unlock(h_mtx); ++ } ++ } ++ ++ return err; ++} ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len) ++{ ++ int err; ++ unsigned long blksize; ++ unsigned char do_kfree; ++ char *buf; ++ ++ err = -ENOMEM; ++ blksize = dst->f_path.dentry->d_sb->s_blocksize; ++ if (!blksize || PAGE_SIZE < blksize) ++ blksize = PAGE_SIZE; ++ AuDbg("blksize %lu\n", blksize); ++ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); ++ if (do_kfree) ++ buf = kmalloc(blksize, GFP_NOFS); ++ else ++ buf = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf)) ++ goto out; ++ ++ if (len > (1 << 22)) ++ AuDbg("copying a large file %lld\n", (long long)len); ++ ++ src->f_pos = 0; ++ dst->f_pos = 0; ++ err = au_do_copy_file(dst, src, len, buf, blksize); ++ if (do_kfree) ++ kfree(buf); ++ else ++ free_page((unsigned long)buf); ++ ++out: ++ return err; ++} ++ ++/* ++ * to support a sparse file which is opened with O_APPEND, ++ * we need to close the file. ++ */ ++static int au_cp_regular(struct au_cp_generic *cpg) ++{ ++ int err, i; ++ enum { SRC, DST }; ++ struct { ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ struct dentry *dentry; ++ int force_wr; ++ struct file *file; ++ void *label; ++ } *f, file[] = { ++ { ++ .bindex = cpg->bsrc, ++ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, ++ .label = &&out ++ }, ++ { ++ .bindex = cpg->bdst, ++ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, ++ .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), ++ .label = &&out_src ++ } ++ }; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ sb = cpg->dentry->d_sb; ++ f = file; ++ for (i = 0; i < 2; i++, f++) { ++ f->dentry = au_h_dptr(cpg->dentry, f->bindex); ++ f->file = au_h_open(cpg->dentry, f->bindex, f->flags, ++ /*file*/NULL, f->force_wr); ++ err = PTR_ERR(f->file); ++ if (IS_ERR(f->file)) ++ goto *f->label; ++ } ++ ++ /* try stopping to update while we copyup */ ++ IMustLock(d_inode(file[SRC].dentry)); ++ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); ++ ++ fput(file[DST].file); ++ au_sbr_put(sb, file[DST].bindex); ++ ++out_src: ++ fput(file[SRC].file); ++ au_sbr_put(sb, file[SRC].bindex); ++out: ++ return err; ++} ++ ++static int au_do_cpup_regular(struct au_cp_generic *cpg, ++ struct au_cpup_reg_attr *h_src_attr) ++{ ++ int err, rerr; ++ loff_t l; ++ struct path h_path; ++ struct inode *h_src_inode, *h_dst_inode; ++ ++ err = 0; ++ h_src_inode = au_h_iptr(d_inode(cpg->dentry), cpg->bsrc); ++ l = i_size_read(h_src_inode); ++ if (cpg->len == -1 || l < cpg->len) ++ cpg->len = l; ++ if (cpg->len) { ++ /* try stopping to update while we are referencing */ ++ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ au_pin_hdir_unlock(cpg->pin); ++ ++ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); ++ h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); ++ h_src_attr->iflags = h_src_inode->i_flags; ++ if (!au_test_nfs(h_src_inode->i_sb)) ++ err = vfs_getattr(&h_path, &h_src_attr->st); ++ else { ++ mutex_unlock(&h_src_inode->i_mutex); ++ err = vfs_getattr(&h_path, &h_src_attr->st); ++ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ } ++ if (unlikely(err)) { ++ mutex_unlock(&h_src_inode->i_mutex); ++ goto out; ++ } ++ h_src_attr->valid = 1; ++ err = au_cp_regular(cpg); ++ mutex_unlock(&h_src_inode->i_mutex); ++ rerr = au_pin_hdir_relock(cpg->pin); ++ if (!err && rerr) ++ err = rerr; ++ } ++ if (!err && (h_src_inode->i_state & I_LINKABLE)) { ++ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); ++ h_dst_inode = d_inode(h_path.dentry); ++ spin_lock(&h_dst_inode->i_lock); ++ h_dst_inode->i_state |= I_LINKABLE; ++ spin_unlock(&h_dst_inode->i_lock); ++ } ++ ++out: ++ return err; ++} ++ ++static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, ++ struct inode *h_dir) ++{ ++ int err, symlen; ++ mm_segment_t old_fs; ++ union { ++ char *k; ++ char __user *u; ++ } sym; ++ struct inode *h_inode = d_inode(h_src); ++ const struct inode_operations *h_iop = h_inode->i_op; ++ ++ err = -ENOSYS; ++ if (unlikely(!h_iop->readlink)) ++ goto out; ++ ++ err = -ENOMEM; ++ sym.k = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!sym.k)) ++ goto out; ++ ++ /* unnecessary to support mmap_sem since symlink is not mmap-able */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ symlen = h_iop->readlink(h_src, sym.u, PATH_MAX); ++ err = symlen; ++ set_fs(old_fs); ++ ++ if (symlen > 0) { ++ sym.k[symlen] = 0; ++ err = vfsub_symlink(h_dir, h_path, sym.k); ++ } ++ free_page((unsigned long)sym.k); ++ ++out: ++ return err; ++} ++ ++static noinline_for_stack ++int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, ++ struct au_cpup_reg_attr *h_src_attr) ++{ ++ int err; ++ umode_t mode; ++ unsigned int mnt_flags; ++ unsigned char isdir, isreg, force; ++ const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); ++ struct au_dtime dt; ++ struct path h_path; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *h_inode, *h_dir, *dir, *inode; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); ++ h_inode = d_inode(h_src); ++ AuDebugOn(h_inode != au_h_iptr(d_inode(cpg->dentry), cpg->bsrc)); ++ ++ /* try stopping to be referenced while we are creating */ ++ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); ++ if (au_ftest_cpup(cpg->flags, RENAME)) ++ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, ++ AUFS_WH_PFX_LEN)); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = d_inode(h_parent); ++ IMustLock(h_dir); ++ AuDebugOn(h_parent != h_dst->d_parent); ++ ++ sb = cpg->dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, cpg->bdst); ++ if (do_dt) { ++ h_path.dentry = h_parent; ++ au_dtime_store(&dt, dst_parent, &h_path); ++ } ++ h_path.dentry = h_dst; ++ ++ isreg = 0; ++ isdir = 0; ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ isreg = 1; ++ err = vfsub_create(h_dir, &h_path, mode | S_IWUSR, ++ /*want_excl*/true); ++ if (!err) ++ err = au_do_cpup_regular(cpg, h_src_attr); ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ err = vfsub_mkdir(h_dir, &h_path, mode); ++ if (!err) { ++ /* ++ * strange behaviour from the users view, ++ * particularry setattr case ++ */ ++ dir = d_inode(dst_parent); ++ if (au_ibstart(dir) == cpg->bdst) ++ au_cpup_attr_nlink(dir, /*force*/1); ++ inode = d_inode(cpg->dentry); ++ au_cpup_attr_nlink(inode, /*force*/1); ++ } ++ break; ++ case S_IFLNK: ++ err = au_do_cpup_symlink(&h_path, h_src, h_dir); ++ break; ++ case S_IFCHR: ++ case S_IFBLK: ++ AuDebugOn(!capable(CAP_MKNOD)); ++ /*FALLTHROUGH*/ ++ case S_IFIFO: ++ case S_IFSOCK: ++ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown inode type 0%o\n", mode); ++ err = -EIO; ++ } ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, UDBA_NONE) ++ && !isdir ++ && au_opt_test(mnt_flags, XINO) ++ && (h_inode->i_nlink == 1 ++ || (h_inode->i_state & I_LINKABLE)) ++ /* todo: unnecessary? */ ++ /* && d_inode(cpg->dentry)->i_nlink == 1 */ ++ && cpg->bdst < cpg->bsrc ++ && !au_ftest_cpup(cpg->flags, KEEPLINO)) ++ au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); ++ /* ignore this error */ ++ ++ if (!err) { ++ force = 0; ++ if (isreg) { ++ force = !!cpg->len; ++ if (cpg->len == -1) ++ force = !!i_size_read(h_inode); ++ } ++ au_fhsm_wrote(sb, cpg->bdst, force); ++ } ++ ++ if (do_dt) ++ au_dtime_revert(&dt); ++ return err; ++} ++ ++static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) ++{ ++ int err; ++ struct dentry *dentry, *h_dentry, *h_parent, *parent; ++ struct inode *h_dir; ++ aufs_bindex_t bdst; ++ ++ dentry = cpg->dentry; ++ bdst = cpg->bdst; ++ h_dentry = au_h_dptr(dentry, bdst); ++ if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { ++ dget(h_dentry); ++ au_set_h_dptr(dentry, bdst, NULL); ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); ++ if (!err) ++ h_path->dentry = dget(au_h_dptr(dentry, bdst)); ++ au_set_h_dptr(dentry, bdst, h_dentry); ++ } else { ++ err = 0; ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ dput(parent); ++ h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent); ++ if (IS_ERR(h_path->dentry)) ++ err = PTR_ERR(h_path->dentry); ++ } ++ if (unlikely(err)) ++ goto out; ++ ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = d_inode(h_parent); ++ IMustLock(h_dir); ++ AuDbg("%pd %pd\n", h_dentry, h_path->dentry); ++ /* no delegation since it is just created */ ++ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL); ++ dput(h_path->dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * copyup the @dentry from @bsrc to @bdst. ++ * the caller must set the both of lower dentries. ++ * @len is for truncating when it is -1 copyup the entire file. ++ * in link/rename cases, @dst_parent may be different from the real one. ++ * basic->bsrc can be larger than basic->bdst. ++ */ ++static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) ++{ ++ int err, rerr; ++ aufs_bindex_t old_ibstart; ++ unsigned char isdir, plink; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *dst_inode, *h_dir, *inode, *delegated, *src_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct path h_path; ++ struct au_cpup_reg_attr h_src_attr; ++ } *a; ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ a->h_src_attr.valid = 0; ++ ++ sb = cpg->dentry->d_sb; ++ br = au_sbr(sb, cpg->bdst); ++ a->h_path.mnt = au_br_mnt(br); ++ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = d_inode(h_parent); ++ IMustLock(h_dir); ++ ++ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); ++ inode = d_inode(cpg->dentry); ++ ++ if (!dst_parent) ++ dst_parent = dget_parent(cpg->dentry); ++ else ++ dget(dst_parent); ++ ++ plink = !!au_opt_test(au_mntflags(sb), PLINK); ++ dst_inode = au_h_iptr(inode, cpg->bdst); ++ if (dst_inode) { ++ if (unlikely(!plink)) { ++ err = -EIO; ++ AuIOErr("hi%lu(i%lu) exists on b%d " ++ "but plink is disabled\n", ++ dst_inode->i_ino, inode->i_ino, cpg->bdst); ++ goto out_parent; ++ } ++ ++ if (dst_inode->i_nlink) { ++ const int do_dt = au_ftest_cpup(cpg->flags, DTIME); ++ ++ h_src = au_plink_lkup(inode, cpg->bdst); ++ err = PTR_ERR(h_src); ++ if (IS_ERR(h_src)) ++ goto out_parent; ++ if (unlikely(d_is_negative(h_src))) { ++ err = -EIO; ++ AuIOErr("i%lu exists on a upper branch " ++ "but not pseudo-linked\n", ++ inode->i_ino); ++ dput(h_src); ++ goto out_parent; ++ } ++ ++ if (do_dt) { ++ a->h_path.dentry = h_parent; ++ au_dtime_store(&a->dt, dst_parent, &a->h_path); ++ } ++ ++ a->h_path.dentry = h_dst; ++ delegated = NULL; ++ err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); ++ if (!err && au_ftest_cpup(cpg->flags, RENAME)) ++ err = au_do_ren_after_cpup(cpg, &a->h_path); ++ if (do_dt) ++ au_dtime_revert(&a->dt); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ dput(h_src); ++ goto out_parent; ++ } else ++ /* todo: cpup_wh_file? */ ++ /* udba work */ ++ au_update_ibrange(inode, /*do_put_zero*/1); ++ } ++ ++ isdir = S_ISDIR(inode->i_mode); ++ old_ibstart = au_ibstart(inode); ++ err = cpup_entry(cpg, dst_parent, &a->h_src_attr); ++ if (unlikely(err)) ++ goto out_rev; ++ dst_inode = d_inode(h_dst); ++ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ /* todo: necessary? */ ++ /* au_pin_hdir_unlock(cpg->pin); */ ++ ++ err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); ++ if (unlikely(err)) { ++ /* todo: necessary? */ ++ /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ ++ mutex_unlock(&dst_inode->i_mutex); ++ goto out_rev; ++ } ++ ++ if (cpg->bdst < old_ibstart) { ++ if (S_ISREG(inode->i_mode)) { ++ err = au_dy_iaop(inode, cpg->bdst, dst_inode); ++ if (unlikely(err)) { ++ /* ignore an error */ ++ /* au_pin_hdir_relock(cpg->pin); */ ++ mutex_unlock(&dst_inode->i_mutex); ++ goto out_rev; ++ } ++ } ++ au_set_ibstart(inode, cpg->bdst); ++ } else ++ au_set_ibend(inode, cpg->bdst); ++ au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), ++ au_hi_flags(inode, isdir)); ++ ++ /* todo: necessary? */ ++ /* err = au_pin_hdir_relock(cpg->pin); */ ++ mutex_unlock(&dst_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_rev; ++ ++ src_inode = d_inode(h_src); ++ if (!isdir ++ && (src_inode->i_nlink > 1 ++ || src_inode->i_state & I_LINKABLE) ++ && plink) ++ au_plink_append(inode, cpg->bdst, h_dst); ++ ++ if (au_ftest_cpup(cpg->flags, RENAME)) { ++ a->h_path.dentry = h_dst; ++ err = au_do_ren_after_cpup(cpg, &a->h_path); ++ } ++ if (!err) ++ goto out_parent; /* success */ ++ ++ /* revert */ ++out_rev: ++ a->h_path.dentry = h_parent; ++ au_dtime_store(&a->dt, dst_parent, &a->h_path); ++ a->h_path.dentry = h_dst; ++ rerr = 0; ++ if (d_is_positive(h_dst)) { ++ if (!isdir) { ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(h_dir, &a->h_path, ++ /*delegated*/NULL, /*force*/0); ++ } else ++ rerr = vfsub_rmdir(h_dir, &a->h_path); ++ } ++ au_dtime_revert(&a->dt); ++ if (rerr) { ++ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); ++ err = -EIO; ++ } ++out_parent: ++ dput(dst_parent); ++ kfree(a); ++out: ++ return err; ++} ++ ++#if 0 /* reserved */ ++struct au_cpup_single_args { ++ int *errp; ++ struct au_cp_generic *cpg; ++ struct dentry *dst_parent; ++}; ++ ++static void au_call_cpup_single(void *args) ++{ ++ struct au_cpup_single_args *a = args; ++ ++ au_pin_hdir_acquire_nest(a->cpg->pin); ++ *a->errp = au_cpup_single(a->cpg, a->dst_parent); ++ au_pin_hdir_release(a->cpg->pin); ++} ++#endif ++ ++/* ++ * prevent SIGXFSZ in copy-up. ++ * testing CAP_MKNOD is for generic fs, ++ * but CAP_FSETID is for xfs only, currently. ++ */ ++static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) ++{ ++ int do_sio; ++ struct super_block *sb; ++ struct inode *h_dir; ++ ++ do_sio = 0; ++ sb = au_pinned_parent(pin)->d_sb; ++ if (!au_wkq_test() ++ && (!au_sbi(sb)->si_plink_maint_pid ++ || au_plink_maint(sb, AuLock_NOPLM))) { ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ /* no condition about RLIMIT_FSIZE and the file size */ ++ do_sio = 1; ++ break; ++ case S_IFCHR: ++ case S_IFBLK: ++ do_sio = !capable(CAP_MKNOD); ++ break; ++ } ++ if (!do_sio) ++ do_sio = ((mode & (S_ISUID | S_ISGID)) ++ && !capable(CAP_FSETID)); ++ /* this workaround may be removed in the future */ ++ if (!do_sio) { ++ h_dir = au_pinned_h_dir(pin); ++ do_sio = h_dir->i_mode & S_ISVTX; ++ } ++ } ++ ++ return do_sio; ++} ++ ++#if 0 /* reserved */ ++int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) ++{ ++ int err, wkq_err; ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); ++ if (!au_cpup_sio_test(pin, d_inode(h_dentry)->i_mode)) ++ err = au_cpup_single(cpg, dst_parent); ++ else { ++ struct au_cpup_single_args args = { ++ .errp = &err, ++ .cpg = cpg, ++ .dst_parent = dst_parent ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_single, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++#endif ++ ++/* ++ * copyup the @dentry from the first active lower branch to @bdst, ++ * using au_cpup_single(). ++ */ ++static int au_cpup_simple(struct au_cp_generic *cpg) ++{ ++ int err; ++ unsigned int flags_orig; ++ struct dentry *dentry; ++ ++ AuDebugOn(cpg->bsrc < 0); ++ ++ dentry = cpg->dentry; ++ DiMustWriteLock(dentry); ++ ++ err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); ++ if (!err) { ++ flags_orig = cpg->flags; ++ au_fset_cpup(cpg->flags, RENAME); ++ err = au_cpup_single(cpg, NULL); ++ cpg->flags = flags_orig; ++ if (!err) ++ return 0; /* success */ ++ ++ /* revert */ ++ au_set_h_dptr(dentry, cpg->bdst, NULL); ++ au_set_dbstart(dentry, cpg->bsrc); ++ } ++ ++ return err; ++} ++ ++struct au_cpup_simple_args { ++ int *errp; ++ struct au_cp_generic *cpg; ++}; ++ ++static void au_call_cpup_simple(void *args) ++{ ++ struct au_cpup_simple_args *a = args; ++ ++ au_pin_hdir_acquire_nest(a->cpg->pin); ++ *a->errp = au_cpup_simple(a->cpg); ++ au_pin_hdir_release(a->cpg->pin); ++} ++ ++static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) ++{ ++ int err, wkq_err; ++ struct dentry *dentry, *parent; ++ struct file *h_file; ++ struct inode *h_dir; ++ ++ dentry = cpg->dentry; ++ h_file = NULL; ++ if (au_ftest_cpup(cpg->flags, HOPEN)) { ++ AuDebugOn(cpg->bsrc < 0); ++ h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ } ++ ++ parent = dget_parent(dentry); ++ h_dir = au_h_iptr(d_inode(parent), cpg->bdst); ++ if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) ++ && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) ++ err = au_cpup_simple(cpg); ++ else { ++ struct au_cpup_simple_args args = { ++ .errp = &err, ++ .cpg = cpg ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_simple, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ dput(parent); ++ if (h_file) ++ au_h_open_post(dentry, cpg->bsrc, h_file); ++ ++out: ++ return err; ++} ++ ++int au_sio_cpup_simple(struct au_cp_generic *cpg) ++{ ++ aufs_bindex_t bsrc, bend; ++ struct dentry *dentry, *h_dentry; ++ ++ if (cpg->bsrc < 0) { ++ dentry = cpg->dentry; ++ bend = au_dbend(dentry); ++ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { ++ h_dentry = au_h_dptr(dentry, bsrc); ++ if (h_dentry) { ++ AuDebugOn(d_is_negative(h_dentry)); ++ break; ++ } ++ } ++ AuDebugOn(bsrc > bend); ++ cpg->bsrc = bsrc; ++ } ++ AuDebugOn(cpg->bsrc <= cpg->bdst); ++ return au_do_sio_cpup_simple(cpg); ++} ++ ++int au_sio_cpdown_simple(struct au_cp_generic *cpg) ++{ ++ AuDebugOn(cpg->bdst <= cpg->bsrc); ++ return au_do_sio_cpup_simple(cpg); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * copyup the deleted file for writing. ++ */ ++static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, ++ struct file *file) ++{ ++ int err; ++ unsigned int flags_orig; ++ aufs_bindex_t bsrc_orig; ++ struct dentry *h_d_dst, *h_d_start; ++ struct au_dinfo *dinfo; ++ struct au_hdentry *hdp; ++ ++ dinfo = au_di(cpg->dentry); ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ bsrc_orig = cpg->bsrc; ++ cpg->bsrc = dinfo->di_bstart; ++ hdp = dinfo->di_hdentry; ++ h_d_dst = hdp[0 + cpg->bdst].hd_dentry; ++ dinfo->di_bstart = cpg->bdst; ++ hdp[0 + cpg->bdst].hd_dentry = wh_dentry; ++ h_d_start = NULL; ++ if (file) { ++ h_d_start = hdp[0 + cpg->bsrc].hd_dentry; ++ hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_path.dentry; ++ } ++ flags_orig = cpg->flags; ++ cpg->flags = !AuCpup_DTIME; ++ err = au_cpup_single(cpg, /*h_parent*/NULL); ++ cpg->flags = flags_orig; ++ if (file) { ++ if (!err) ++ err = au_reopen_nondir(file); ++ hdp[0 + cpg->bsrc].hd_dentry = h_d_start; ++ } ++ hdp[0 + cpg->bdst].hd_dentry = h_d_dst; ++ dinfo->di_bstart = cpg->bsrc; ++ cpg->bsrc = bsrc_orig; ++ ++ return err; ++} ++ ++static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) ++{ ++ int err; ++ aufs_bindex_t bdst; ++ struct au_dtime dt; ++ struct dentry *dentry, *parent, *h_parent, *wh_dentry; ++ struct au_branch *br; ++ struct path h_path; ++ ++ dentry = cpg->dentry; ++ bdst = cpg->bdst; ++ br = au_sbr(dentry->d_sb, bdst); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ h_path.dentry = h_parent; ++ h_path.mnt = au_br_mnt(br); ++ au_dtime_store(&dt, parent, &h_path); ++ err = au_do_cpup_wh(cpg, wh_dentry, file); ++ if (unlikely(err)) ++ goto out_wh; ++ ++ dget(wh_dentry); ++ h_path.dentry = wh_dentry; ++ if (!d_is_dir(wh_dentry)) { ++ /* no delegation since it is just created */ ++ err = vfsub_unlink(d_inode(h_parent), &h_path, ++ /*delegated*/NULL, /*force*/0); ++ } else ++ err = vfsub_rmdir(d_inode(h_parent), &h_path); ++ if (unlikely(err)) { ++ AuIOErr("failed remove copied-up tmp file %pd(%d)\n", ++ wh_dentry, err); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++ au_set_hi_wh(d_inode(dentry), bdst, wh_dentry); ++ ++out_wh: ++ dput(wh_dentry); ++out: ++ dput(parent); ++ return err; ++} ++ ++struct au_cpup_wh_args { ++ int *errp; ++ struct au_cp_generic *cpg; ++ struct file *file; ++}; ++ ++static void au_call_cpup_wh(void *args) ++{ ++ struct au_cpup_wh_args *a = args; ++ ++ au_pin_hdir_acquire_nest(a->cpg->pin); ++ *a->errp = au_cpup_wh(a->cpg, a->file); ++ au_pin_hdir_release(a->cpg->pin); ++} ++ ++int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) ++{ ++ int err, wkq_err; ++ aufs_bindex_t bdst; ++ struct dentry *dentry, *parent, *h_orph, *h_parent; ++ struct inode *dir, *h_dir, *h_tmpdir; ++ struct au_wbr *wbr; ++ struct au_pin wh_pin, *pin_orig; ++ ++ dentry = cpg->dentry; ++ bdst = cpg->bdst; ++ parent = dget_parent(dentry); ++ dir = d_inode(parent); ++ h_orph = NULL; ++ h_parent = NULL; ++ h_dir = au_igrab(au_h_iptr(dir, bdst)); ++ h_tmpdir = h_dir; ++ pin_orig = NULL; ++ if (!h_dir->i_nlink) { ++ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; ++ h_orph = wbr->wbr_orph; ++ ++ h_parent = dget(au_h_dptr(parent, bdst)); ++ au_set_h_dptr(parent, bdst, dget(h_orph)); ++ h_tmpdir = d_inode(h_orph); ++ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); ++ ++ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); ++ /* todo: au_h_open_pre()? */ ++ ++ pin_orig = cpg->pin; ++ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, ++ AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); ++ cpg->pin = &wh_pin; ++ } ++ ++ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) ++ && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) ++ err = au_cpup_wh(cpg, file); ++ else { ++ struct au_cpup_wh_args args = { ++ .errp = &err, ++ .cpg = cpg, ++ .file = file ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_wh, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ if (h_orph) { ++ mutex_unlock(&h_tmpdir->i_mutex); ++ /* todo: au_h_open_post()? */ ++ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); ++ au_set_h_dptr(parent, bdst, h_parent); ++ AuDebugOn(!pin_orig); ++ cpg->pin = pin_orig; ++ } ++ iput(h_dir); ++ dput(parent); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generic routine for both of copy-up and copy-down. ++ */ ++/* cf. revalidate function in file.c */ ++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, ++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent, void *arg), ++ void *arg) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *d, *parent, *h_parent, *real_parent, *h_dentry; ++ ++ err = 0; ++ parent = dget_parent(dentry); ++ if (IS_ROOT(parent)) ++ goto out; ++ ++ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, ++ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); ++ ++ /* do not use au_dpage */ ++ real_parent = parent; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ if (h_parent) ++ goto out; /* success */ ++ ++ /* find top dir which is necessary to cpup */ ++ do { ++ d = parent; ++ dput(parent); ++ parent = dget_parent(d); ++ di_read_lock_parent3(parent, !AuLock_IR); ++ h_parent = au_h_dptr(parent, bdst); ++ di_read_unlock(parent, !AuLock_IR); ++ } while (!h_parent); ++ ++ if (d != real_parent) ++ di_write_lock_child3(d); ++ ++ /* somebody else might create while we were sleeping */ ++ h_dentry = au_h_dptr(d, bdst); ++ if (!h_dentry || d_is_negative(h_dentry)) { ++ if (h_dentry) ++ au_update_dbstart(d); ++ ++ au_pin_set_dentry(&pin, d); ++ err = au_do_pin(&pin); ++ if (!err) { ++ err = cp(d, bdst, &pin, h_parent, arg); ++ au_unpin(&pin); ++ } ++ } ++ ++ if (d != real_parent) ++ di_write_unlock(d); ++ if (unlikely(err)) ++ break; ++ } ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent __maybe_unused, ++ void *arg __maybe_unused) ++{ ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = -1, ++ .len = 0, ++ .pin = pin, ++ .flags = AuCpup_DTIME ++ }; ++ return au_sio_cpup_simple(&cpg); ++} ++ ++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); ++} ++ ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ struct dentry *parent; ++ struct inode *dir; ++ ++ parent = dget_parent(dentry); ++ dir = d_inode(parent); ++ err = 0; ++ if (au_h_iptr(dir, bdst)) ++ goto out; ++ ++ di_read_unlock(parent, AuLock_IR); ++ di_write_lock_parent(parent); ++ /* someone else might change our inode while we were sleeping */ ++ if (!au_h_iptr(dir, bdst)) ++ err = au_cpup_dirs(dentry, bdst); ++ di_downgrade_lock(parent, AuLock_IR); ++ ++out: ++ dput(parent); ++ return err; ++} +diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h +new file mode 100644 +index 0000000..3355f08 +--- /dev/null ++++ b/fs/aufs/cpup.h +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * copy-up/down functions ++ */ ++ ++#ifndef __AUFS_CPUP_H__ ++#define __AUFS_CPUP_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct inode; ++struct file; ++struct au_pin; ++ ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); ++void au_cpup_attr_timesizes(struct inode *inode); ++void au_cpup_attr_nlink(struct inode *inode, int force); ++void au_cpup_attr_changeable(struct inode *inode); ++void au_cpup_igen(struct inode *inode, struct inode *h_inode); ++void au_cpup_attr_all(struct inode *inode, int force); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_cp_generic { ++ struct dentry *dentry; ++ aufs_bindex_t bdst, bsrc; ++ loff_t len; ++ struct au_pin *pin; ++ unsigned int flags; ++}; ++ ++/* cpup flags */ ++#define AuCpup_DTIME 1 /* do dtime_store/revert */ ++#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, ++ for link(2) */ ++#define AuCpup_RENAME (1 << 2) /* rename after cpup */ ++#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in ++ cpup */ ++#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the ++ existing entry */ ++#define AuCpup_RWDST (1 << 5) /* force write target even if ++ the branch is marked as RO */ ++ ++#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) ++#define au_fset_cpup(flags, name) \ ++ do { (flags) |= AuCpup_##name; } while (0) ++#define au_fclr_cpup(flags, name) \ ++ do { (flags) &= ~AuCpup_##name; } while (0) ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len); ++int au_sio_cpup_simple(struct au_cp_generic *cpg); ++int au_sio_cpdown_simple(struct au_cp_generic *cpg); ++int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); ++ ++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, ++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent, void *arg), ++ void *arg); ++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* keep timestamps when copyup */ ++struct au_dtime { ++ struct dentry *dt_dentry; ++ struct path dt_h_path; ++ struct timespec dt_atime, dt_mtime; ++}; ++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, ++ struct path *h_path); ++void au_dtime_revert(struct au_dtime *dt); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_CPUP_H__ */ +diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c +new file mode 100644 +index 0000000..c7c612c +--- /dev/null ++++ b/fs/aufs/dbgaufs.c +@@ -0,0 +1,419 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#include ++#include "aufs.h" ++ ++#ifndef CONFIG_SYSFS ++#error DEBUG_FS depends upon SYSFS ++#endif ++ ++static struct dentry *dbgaufs; ++static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; ++ ++/* 20 is max digits length of ulong 64 */ ++struct dbgaufs_arg { ++ int n; ++ char a[20 * 4]; ++}; ++ ++/* ++ * common function for all XINO files ++ */ ++static int dbgaufs_xi_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ kfree(file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) ++{ ++ int err; ++ struct kstat st; ++ struct dbgaufs_arg *p; ++ ++ err = -ENOMEM; ++ p = kmalloc(sizeof(*p), GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ p->n = 0; ++ file->private_data = p; ++ if (!xf) ++ goto out; ++ ++ err = vfs_getattr(&xf->f_path, &st); ++ if (!err) { ++ if (do_fcnt) ++ p->n = snprintf ++ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", ++ (long)file_count(xf), st.blocks, st.blksize, ++ (long long)st.size); ++ else ++ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", ++ st.blocks, st.blksize, ++ (long long)st.size); ++ AuDebugOn(p->n >= sizeof(p->a)); ++ } else { ++ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); ++ err = 0; ++ } ++ ++out: ++ return err; ++ ++} ++ ++static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dbgaufs_plink_arg { ++ int n; ++ char a[]; ++}; ++ ++static int dbgaufs_plink_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ free_page((unsigned long)file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_plink_open(struct inode *inode, struct file *file) ++{ ++ int err, i, limit; ++ unsigned long n, sum; ++ struct dbgaufs_plink_arg *p; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct au_sphlhead *sphl; ++ ++ err = -ENOMEM; ++ p = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = -EFBIG; ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ limit = PAGE_SIZE - sizeof(p->n); ++ ++ /* the number of buckets */ ++ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); ++ p->n += n; ++ limit -= n; ++ ++ sum = 0; ++ for (i = 0, sphl = sbinfo->si_plink; ++ i < AuPlink_NHASH; ++ i++, sphl++) { ++ n = au_sphl_count(sphl); ++ sum += n; ++ ++ n = snprintf(p->a + p->n, limit, "%lu ", n); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } ++ p->a[p->n - 1] = '\n'; ++ ++ /* the sum of plinks */ ++ n = snprintf(p->a + p->n, limit, "%lu\n", sum); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } else { ++#define str "1\n0\n0\n" ++ p->n = sizeof(str) - 1; ++ strcpy(p->a, str); ++#undef str ++ } ++ si_read_unlock(sb); ++ ++ err = 0; ++ file->private_data = p; ++ goto out; /* success */ ++ ++out_free: ++ free_page((unsigned long)p); ++out: ++ return err; ++} ++ ++static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_plink_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++static const struct file_operations dbgaufs_plink_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_plink_open, ++ .release = dbgaufs_plink_release, ++ .read = dbgaufs_plink_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int dbgaufs_xib_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xib_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xib_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DbgaufsXi_PREFIX "xi" ++ ++static int dbgaufs_xino_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ long l; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct file *xf; ++ struct qstr *name; ++ ++ err = -ENOENT; ++ xf = NULL; ++ name = &file->f_path.dentry->d_name; ++ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) ++ || memcmp(name->name, DbgaufsXi_PREFIX, ++ sizeof(DbgaufsXi_PREFIX) - 1))) ++ goto out; ++ err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); ++ if (unlikely(err)) ++ goto out; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (l <= au_sbend(sb)) { ++ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; ++ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); ++ } else ++ err = -ENOENT; ++ si_read_unlock(sb); ++ ++out: ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xino_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xino_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ aufs_bindex_t bend; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ ++ if (!au_sbi(sb)->si_dbgaufs) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ debugfs_remove(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = NULL; ++ } ++} ++ ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_sbinfo *sbinfo; ++ struct dentry *parent; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ aufs_bindex_t bend; ++ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ ++ ++ sbinfo = au_sbi(sb); ++ parent = sbinfo->si_dbgaufs; ++ if (!parent) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ AuDebugOn(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, ++ sbinfo, &dbgaufs_xino_fop); ++ /* ignore an error */ ++ if (unlikely(!xi->xi_dbgaufs)) ++ AuWarn1("failed %s under debugfs\n", name); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++static int dbgaufs_xigen_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xigen_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xigen_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ /* ++ * This function is a dynamic '__init' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ err = -EIO; ++ sbinfo->si_dbgaufs_xigen = debugfs_create_file ++ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xigen_fop); ++ if (sbinfo->si_dbgaufs_xigen) ++ err = 0; ++ ++ return err; ++} ++#else ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ return 0; ++} ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo) ++{ ++ /* ++ * This function is a dynamic '__fin' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ debugfs_remove_recursive(sbinfo->si_dbgaufs); ++ sbinfo->si_dbgaufs = NULL; ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++int dbgaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ char name[SysaufsSiNameLen]; ++ ++ /* ++ * This function is a dynamic '__init' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ err = -ENOENT; ++ if (!dbgaufs) { ++ AuErr1("/debug/aufs is uninitialized\n"); ++ goto out; ++ } ++ ++ err = -EIO; ++ sysaufs_name(sbinfo, name); ++ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); ++ if (unlikely(!sbinfo->si_dbgaufs)) ++ goto out; ++ kobject_get(&sbinfo->si_kobj); ++ ++ sbinfo->si_dbgaufs_xib = debugfs_create_file ++ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xib_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_xib)) ++ goto out_dir; ++ ++ sbinfo->si_dbgaufs_plink = debugfs_create_file ++ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_plink_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_plink)) ++ goto out_dir; ++ ++ err = dbgaufs_xigen_init(sbinfo); ++ if (!err) ++ goto out; /* success */ ++ ++out_dir: ++ dbgaufs_si_fin(sbinfo); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_fin(void) ++{ ++ debugfs_remove(dbgaufs); ++} ++ ++int __init dbgaufs_init(void) ++{ ++ int err; ++ ++ err = -EIO; ++ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); ++ if (dbgaufs) ++ err = 0; ++ return err; ++} +diff --git a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h +new file mode 100644 +index 0000000..efa9a4d +--- /dev/null ++++ b/fs/aufs/dbgaufs.h +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#ifndef __DBGAUFS_H__ ++#define __DBGAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++struct super_block; ++struct au_sbinfo; ++ ++#ifdef CONFIG_DEBUG_FS ++/* dbgaufs.c */ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo); ++int dbgaufs_si_init(struct au_sbinfo *sbinfo); ++void dbgaufs_fin(void); ++int __init dbgaufs_init(void); ++#else ++AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) ++AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) ++AuStubVoid(dbgaufs_fin, void) ++AuStubInt0(__init dbgaufs_init, void) ++#endif /* CONFIG_DEBUG_FS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __DBGAUFS_H__ */ +diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c +new file mode 100644 +index 0000000..f9d9d52 +--- /dev/null ++++ b/fs/aufs/dcsub.c +@@ -0,0 +1,211 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#include "aufs.h" ++ ++static void au_dpage_free(struct au_dpage *dpage) ++{ ++ int i; ++ struct dentry **p; ++ ++ p = dpage->dentries; ++ for (i = 0; i < dpage->ndentry; i++) ++ dput(*p++); ++ free_page((unsigned long)dpage->dentries); ++} ++ ++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) ++{ ++ int err; ++ void *p; ++ ++ err = -ENOMEM; ++ dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); ++ if (unlikely(!dpages->dpages)) ++ goto out; ++ ++ p = (void *)__get_free_page(gfp); ++ if (unlikely(!p)) ++ goto out_dpages; ++ ++ dpages->dpages[0].ndentry = 0; ++ dpages->dpages[0].dentries = p; ++ dpages->ndpage = 1; ++ return 0; /* success */ ++ ++out_dpages: ++ kfree(dpages->dpages); ++out: ++ return err; ++} ++ ++void au_dpages_free(struct au_dcsub_pages *dpages) ++{ ++ int i; ++ struct au_dpage *p; ++ ++ p = dpages->dpages; ++ for (i = 0; i < dpages->ndpage; i++) ++ au_dpage_free(p++); ++ kfree(dpages->dpages); ++} ++ ++static int au_dpages_append(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, gfp_t gfp) ++{ ++ int err, sz; ++ struct au_dpage *dpage; ++ void *p; ++ ++ dpage = dpages->dpages + dpages->ndpage - 1; ++ sz = PAGE_SIZE / sizeof(dentry); ++ if (unlikely(dpage->ndentry >= sz)) { ++ AuLabel(new dpage); ++ err = -ENOMEM; ++ sz = dpages->ndpage * sizeof(*dpages->dpages); ++ p = au_kzrealloc(dpages->dpages, sz, ++ sz + sizeof(*dpages->dpages), gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpages->dpages = p; ++ dpage = dpages->dpages + dpages->ndpage; ++ p = (void *)__get_free_page(gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpage->ndentry = 0; ++ dpage->dentries = p; ++ dpages->ndpage++; ++ } ++ ++ AuDebugOn(au_dcount(dentry) <= 0); ++ dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); ++ return 0; /* success */ ++ ++out: ++ return err; ++} ++ ++/* todo: BAD approach */ ++/* copied from linux/fs/dcache.c */ ++enum d_walk_ret { ++ D_WALK_CONTINUE, ++ D_WALK_QUIT, ++ D_WALK_NORETRY, ++ D_WALK_SKIP, ++}; ++ ++extern void d_walk(struct dentry *parent, void *data, ++ enum d_walk_ret (*enter)(void *, struct dentry *), ++ void (*finish)(void *)); ++ ++struct ac_dpages_arg { ++ int err; ++ struct au_dcsub_pages *dpages; ++ struct super_block *sb; ++ au_dpages_test test; ++ void *arg; ++}; ++ ++static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) ++{ ++ enum d_walk_ret ret; ++ struct ac_dpages_arg *arg = _arg; ++ ++ ret = D_WALK_CONTINUE; ++ if (dentry->d_sb == arg->sb ++ && !IS_ROOT(dentry) ++ && au_dcount(dentry) > 0 ++ && au_di(dentry) ++ && (!arg->test || arg->test(dentry, arg->arg))) { ++ arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); ++ if (unlikely(arg->err)) ++ ret = D_WALK_QUIT; ++ } ++ ++ return ret; ++} ++ ++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, ++ au_dpages_test test, void *arg) ++{ ++ struct ac_dpages_arg args = { ++ .err = 0, ++ .dpages = dpages, ++ .sb = root->d_sb, ++ .test = test, ++ .arg = arg ++ }; ++ ++ d_walk(root, &args, au_call_dpages_append, NULL); ++ ++ return args.err; ++} ++ ++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, ++ int do_include, au_dpages_test test, void *arg) ++{ ++ int err; ++ ++ err = 0; ++ write_seqlock(&rename_lock); ++ spin_lock(&dentry->d_lock); ++ if (do_include ++ && au_dcount(dentry) > 0 ++ && (!test || test(dentry, arg))) ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ spin_unlock(&dentry->d_lock); ++ if (unlikely(err)) ++ goto out; ++ ++ /* ++ * RCU for vfsmount is unnecessary since this is a traverse in a single ++ * mount ++ */ ++ while (!IS_ROOT(dentry)) { ++ dentry = dentry->d_parent; /* rename_lock is locked */ ++ spin_lock(&dentry->d_lock); ++ if (au_dcount(dentry) > 0 ++ && (!test || test(dentry, arg))) ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ spin_unlock(&dentry->d_lock); ++ if (unlikely(err)) ++ break; ++ } ++ ++out: ++ write_sequnlock(&rename_lock); ++ return err; ++} ++ ++static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) ++{ ++ return au_di(dentry) && dentry->d_sb == arg; ++} ++ ++int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, int do_include) ++{ ++ return au_dcsub_pages_rev(dpages, dentry, do_include, ++ au_dcsub_dpages_aufs, dentry->d_sb); ++} ++ ++int au_test_subdir(struct dentry *d1, struct dentry *d2) ++{ ++ struct path path[2] = { ++ { ++ .dentry = d1 ++ }, ++ { ++ .dentry = d2 ++ } ++ }; ++ ++ return path_is_under(path + 0, path + 1); ++} +diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h +new file mode 100644 +index 0000000..2aa87ac +--- /dev/null ++++ b/fs/aufs/dcsub.h +@@ -0,0 +1,123 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#ifndef __AUFS_DCSUB_H__ ++#define __AUFS_DCSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++struct au_dpage { ++ int ndentry; ++ struct dentry **dentries; ++}; ++ ++struct au_dcsub_pages { ++ int ndpage; ++ struct au_dpage *dpages; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dcsub.c */ ++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); ++void au_dpages_free(struct au_dcsub_pages *dpages); ++typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); ++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, ++ au_dpages_test test, void *arg); ++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, ++ int do_include, au_dpages_test test, void *arg); ++int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, int do_include); ++int au_test_subdir(struct dentry *d1, struct dentry *d2); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * todo: in linux-3.13, several similar (but faster) helpers are added to ++ * include/linux/dcache.h. Try them (in the future). ++ */ ++ ++static inline int au_d_hashed_positive(struct dentry *d) ++{ ++ int err; ++ struct inode *inode = d_inode(d); ++ ++ err = 0; ++ if (unlikely(d_unhashed(d) ++ || d_is_negative(d) ++ || !inode->i_nlink)) ++ err = -ENOENT; ++ return err; ++} ++ ++static inline int au_d_linkable(struct dentry *d) ++{ ++ int err; ++ struct inode *inode = d_inode(d); ++ ++ err = au_d_hashed_positive(d); ++ if (err ++ && d_is_positive(d) ++ && (inode->i_state & I_LINKABLE)) ++ err = 0; ++ return err; ++} ++ ++static inline int au_d_alive(struct dentry *d) ++{ ++ int err; ++ struct inode *inode; ++ ++ err = 0; ++ if (!IS_ROOT(d)) ++ err = au_d_hashed_positive(d); ++ else { ++ inode = d_inode(d); ++ if (unlikely(d_unlinked(d) ++ || d_is_negative(d) ++ || !inode->i_nlink)) ++ err = -ENOENT; ++ } ++ return err; ++} ++ ++static inline int au_alive_dir(struct dentry *d) ++{ ++ int err; ++ ++ err = au_d_alive(d); ++ if (unlikely(err || IS_DEADDIR(d_inode(d)))) ++ err = -ENOENT; ++ return err; ++} ++ ++static inline int au_qstreq(struct qstr *a, struct qstr *b) ++{ ++ return a->len == b->len ++ && !memcmp(a->name, b->name, a->len); ++} ++ ++/* ++ * by the commit ++ * 360f547 2015-01-25 dcache: let the dentry count go down to zero without ++ * taking d_lock ++ * the type of d_lockref.count became int, but the inlined function d_count() ++ * still returns unsigned int. ++ * I don't know why. Maybe it is for every d_count() users? ++ * Anyway au_dcount() lives on. ++ */ ++static inline int au_dcount(struct dentry *d) ++{ ++ return (int)d_count(d); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DCSUB_H__ */ +diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c +new file mode 100644 +index 0000000..e553cef +--- /dev/null ++++ b/fs/aufs/debug.c +@@ -0,0 +1,427 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#include "aufs.h" ++ ++/* Returns 0, or -errno. arg is in kp->arg. */ ++static int param_atomic_t_set(const char *val, const struct kernel_param *kp) ++{ ++ int err, n; ++ ++ err = kstrtoint(val, 0, &n); ++ if (!err) { ++ if (n > 0) ++ au_debug_on(); ++ else ++ au_debug_off(); ++ } ++ return err; ++} ++ ++/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ ++static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) ++{ ++ atomic_t *a; ++ ++ a = kp->arg; ++ return sprintf(buffer, "%d", atomic_read(a)); ++} ++ ++static struct kernel_param_ops param_ops_atomic_t = { ++ .set = param_atomic_t_set, ++ .get = param_atomic_t_get ++ /* void (*free)(void *arg) */ ++}; ++ ++atomic_t aufs_debug = ATOMIC_INIT(0); ++MODULE_PARM_DESC(debug, "debug print"); ++module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); ++ ++DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ ++char *au_plevel = KERN_DEBUG; ++#define dpri(fmt, ...) do { \ ++ if ((au_plevel \ ++ && strcmp(au_plevel, KERN_DEBUG)) \ ++ || au_debug_test()) \ ++ printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ ++} while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_dpri_whlist(struct au_nhash *whlist) ++{ ++ unsigned long ul, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; ul < n; ul++) { ++ hlist_for_each_entry(pos, head, wh_hash) ++ dpri("b%d, %.*s, %d\n", ++ pos->wh_bindex, ++ pos->wh_str.len, pos->wh_str.name, ++ pos->wh_str.len); ++ head++; ++ } ++} ++ ++void au_dpri_vdir(struct au_vdir *vdir) ++{ ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ unsigned char *o; ++ ++ if (!vdir || IS_ERR(vdir)) { ++ dpri("err %ld\n", PTR_ERR(vdir)); ++ return; ++ } ++ ++ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", ++ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, ++ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); ++ for (ul = 0; ul < vdir->vd_nblk; ul++) { ++ p.deblk = vdir->vd_deblk[ul]; ++ o = p.deblk; ++ dpri("[%lu]: %p\n", ul, o); ++ } ++} ++ ++static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, ++ struct dentry *wh) ++{ ++ char *n = NULL; ++ int l = 0; ++ ++ if (!inode || IS_ERR(inode)) { ++ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); ++ return -1; ++ } ++ ++ /* the type of i_blocks depends upon CONFIG_LBDAF */ ++ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) ++ && sizeof(inode->i_blocks) != sizeof(u64)); ++ if (wh) { ++ n = (void *)wh->d_name.name; ++ l = wh->d_name.len; ++ } ++ ++ dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," ++ " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", ++ bindex, inode, ++ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", ++ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, ++ i_size_read(inode), (unsigned long long)inode->i_blocks, ++ hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, ++ inode->i_mapping ? inode->i_mapping->nrpages : 0, ++ inode->i_state, inode->i_flags, inode->i_version, ++ inode->i_generation, ++ l ? ", wh " : "", l, n); ++ return 0; ++} ++ ++void au_dpri_inode(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ aufs_bindex_t bindex; ++ int err, hn; ++ ++ err = do_pri_inode(-1, inode, -1, NULL); ++ if (err || !au_test_aufs(inode->i_sb)) ++ return; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ dpri("i-1: bstart %d, bend %d, gen %d\n", ++ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); ++ if (iinfo->ii_bstart < 0) ++ return; ++ hn = 0; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { ++ hn = !!au_hn(iinfo->ii_hinode + bindex); ++ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, ++ iinfo->ii_hinode[0 + bindex].hi_whdentry); ++ } ++} ++ ++void au_dpri_dalias(struct inode *inode) ++{ ++ struct dentry *d; ++ ++ spin_lock(&inode->i_lock); ++ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) ++ au_dpri_dentry(d); ++ spin_unlock(&inode->i_lock); ++} ++ ++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) ++{ ++ struct dentry *wh = NULL; ++ int hn; ++ struct au_iinfo *iinfo; ++ ++ if (!dentry || IS_ERR(dentry)) { ++ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); ++ return -1; ++ } ++ /* do not call dget_parent() here */ ++ /* note: access d_xxx without d_lock */ ++ dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", ++ bindex, dentry, dentry, ++ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", ++ au_dcount(dentry), dentry->d_flags, ++ d_unhashed(dentry) ? "un" : ""); ++ hn = -1; ++ if (bindex >= 0 ++ && d_is_positive(dentry) ++ && au_test_aufs(dentry->d_sb)) { ++ iinfo = au_ii(d_inode(dentry)); ++ if (iinfo) { ++ hn = !!au_hn(iinfo->ii_hinode + bindex); ++ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; ++ } ++ } ++ do_pri_inode(bindex, d_inode(dentry), hn, wh); ++ return 0; ++} ++ ++void au_dpri_dentry(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ aufs_bindex_t bindex; ++ int err; ++ struct au_hdentry *hdp; ++ ++ err = do_pri_dentry(-1, dentry); ++ if (err || !au_test_aufs(dentry->d_sb)) ++ return; ++ ++ dinfo = au_di(dentry); ++ if (!dinfo) ++ return; ++ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", ++ dinfo->di_bstart, dinfo->di_bend, ++ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), ++ dinfo->di_tmpfile); ++ if (dinfo->di_bstart < 0) ++ return; ++ hdp = dinfo->di_hdentry; ++ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) ++ do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); ++} ++ ++static int do_pri_file(aufs_bindex_t bindex, struct file *file) ++{ ++ char a[32]; ++ ++ if (!file || IS_ERR(file)) { ++ dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); ++ return -1; ++ } ++ a[0] = 0; ++ if (bindex < 0 ++ && !IS_ERR_OR_NULL(file->f_path.dentry) ++ && au_test_aufs(file->f_path.dentry->d_sb) ++ && au_fi(file)) ++ snprintf(a, sizeof(a), ", gen %d, mmapped %d", ++ au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); ++ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", ++ bindex, file->f_mode, file->f_flags, (long)file_count(file), ++ file->f_version, file->f_pos, a); ++ if (!IS_ERR_OR_NULL(file->f_path.dentry)) ++ do_pri_dentry(bindex, file->f_path.dentry); ++ return 0; ++} ++ ++void au_dpri_file(struct file *file) ++{ ++ struct au_finfo *finfo; ++ struct au_fidir *fidir; ++ struct au_hfile *hfile; ++ aufs_bindex_t bindex; ++ int err; ++ ++ err = do_pri_file(-1, file); ++ if (err ++ || IS_ERR_OR_NULL(file->f_path.dentry) ++ || !au_test_aufs(file->f_path.dentry->d_sb)) ++ return; ++ ++ finfo = au_fi(file); ++ if (!finfo) ++ return; ++ if (finfo->fi_btop < 0) ++ return; ++ fidir = finfo->fi_hdir; ++ if (!fidir) ++ do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); ++ else ++ for (bindex = finfo->fi_btop; ++ bindex >= 0 && bindex <= fidir->fd_bbot; ++ bindex++) { ++ hfile = fidir->fd_hfile + bindex; ++ do_pri_file(bindex, hfile ? hfile->hf_file : NULL); ++ } ++} ++ ++static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) ++{ ++ struct vfsmount *mnt; ++ struct super_block *sb; ++ ++ if (!br || IS_ERR(br)) ++ goto out; ++ mnt = au_br_mnt(br); ++ if (!mnt || IS_ERR(mnt)) ++ goto out; ++ sb = mnt->mnt_sb; ++ if (!sb || IS_ERR(sb)) ++ goto out; ++ ++ dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " ++ "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " ++ "xino %d\n", ++ bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), ++ br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ sb->s_flags, sb->s_count, ++ atomic_read(&sb->s_active), !!br->br_xino.xi_file); ++ return 0; ++ ++out: ++ dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); ++ return -1; ++} ++ ++void au_dpri_sb(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ aufs_bindex_t bindex; ++ int err; ++ /* to reuduce stack size */ ++ struct { ++ struct vfsmount mnt; ++ struct au_branch fake; ++ } *a; ++ ++ /* this function can be called from magic sysrq */ ++ a = kzalloc(sizeof(*a), GFP_ATOMIC); ++ if (unlikely(!a)) { ++ dpri("no memory\n"); ++ return; ++ } ++ ++ a->mnt.mnt_sb = sb; ++ a->fake.br_perm = 0; ++ a->fake.br_path.mnt = &a->mnt; ++ a->fake.br_xino.xi_file = NULL; ++ atomic_set(&a->fake.br_count, 0); ++ smp_mb(); /* atomic_set */ ++ err = do_pri_br(-1, &a->fake); ++ kfree(a); ++ dpri("dev 0x%x\n", sb->s_dev); ++ if (err || !au_test_aufs(sb)) ++ return; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ dpri("nw %d, gen %u, kobj %d\n", ++ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, ++ atomic_read(&sbinfo->si_kobj.kref.refcount)); ++ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) ++ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) ++{ ++ struct inode *h_inode, *inode = d_inode(dentry); ++ struct dentry *h_dentry; ++ aufs_bindex_t bindex, bend, bi; ++ ++ if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) ++ return; ++ ++ bend = au_dbend(dentry); ++ bi = au_ibend(inode); ++ if (bi < bend) ++ bend = bi; ++ bindex = au_dbstart(dentry); ++ bi = au_ibstart(inode); ++ if (bi > bindex) ++ bindex = bi; ++ ++ for (; bindex <= bend; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ h_inode = au_h_iptr(inode, bindex); ++ if (unlikely(h_inode != d_inode(h_dentry))) { ++ au_debug_on(); ++ AuDbg("b%d, %s:%d\n", bindex, func, line); ++ AuDbgDentry(dentry); ++ AuDbgInode(inode); ++ au_debug_off(); ++ BUG(); ++ } ++ } ++} ++ ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) ++{ ++ int err, i, j; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ AuDebugOn(err); ++ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); ++ AuDebugOn(err); ++ for (i = dpages.ndpage - 1; !err && i >= 0; i--) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ for (j = dpage->ndentry - 1; !err && j >= 0; j--) ++ AuDebugOn(au_digen_test(dentries[j], sigen)); ++ } ++ au_dpages_free(&dpages); ++} ++ ++void au_dbg_verify_kthread(void) ++{ ++ if (au_wkq_test()) { ++ au_dbg_blocked(); ++ /* ++ * It may be recursive, but udba=notify between two aufs mounts, ++ * where a single ro branch is shared, is not a problem. ++ */ ++ /* WARN_ON(1); */ ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int __init au_debug_init(void) ++{ ++ aufs_bindex_t bindex; ++ struct au_vdir_destr destr; ++ ++ bindex = -1; ++ AuDebugOn(bindex >= 0); ++ ++ destr.len = -1; ++ AuDebugOn(destr.len < NAME_MAX); ++ ++#ifdef CONFIG_4KSTACKS ++ pr_warn("CONFIG_4KSTACKS is defined.\n"); ++#endif ++ ++ return 0; ++} +diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h +new file mode 100644 +index 0000000..0a2e7e7 +--- /dev/null ++++ b/fs/aufs/debug.h +@@ -0,0 +1,212 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#ifndef __AUFS_DEBUG_H__ ++#define __AUFS_DEBUG_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuDebugOn(a) BUG_ON(a) ++ ++/* module parameter */ ++extern atomic_t aufs_debug; ++static inline void au_debug_on(void) ++{ ++ atomic_inc(&aufs_debug); ++} ++static inline void au_debug_off(void) ++{ ++ atomic_dec_if_positive(&aufs_debug); ++} ++ ++static inline int au_debug_test(void) ++{ ++ return atomic_read(&aufs_debug) > 0; ++} ++#else ++#define AuDebugOn(a) do {} while (0) ++AuStubVoid(au_debug_on, void) ++AuStubVoid(au_debug_off, void) ++AuStubInt0(au_debug_test, void) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* debug print */ ++ ++#define AuDbg(fmt, ...) do { \ ++ if (au_debug_test()) \ ++ pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ ++} while (0) ++#define AuLabel(l) AuDbg(#l "\n") ++#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) ++#define AuWarn1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ pr_warn(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuErr1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ pr_err(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuIOErr1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ AuIOErr(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuUnsupportMsg "This operation is not supported." \ ++ " Please report this application to aufs-users ML." ++#define AuUnsupport(fmt, ...) do { \ ++ pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ ++ dump_stack(); \ ++} while (0) ++ ++#define AuTraceErr(e) do { \ ++ if (unlikely((e) < 0)) \ ++ AuDbg("err %d\n", (int)(e)); \ ++} while (0) ++ ++#define AuTraceErrPtr(p) do { \ ++ if (IS_ERR(p)) \ ++ AuDbg("err %ld\n", PTR_ERR(p)); \ ++} while (0) ++ ++/* dirty macros for debug print, use with "%.*s" and caution */ ++#define AuLNPair(qstr) (qstr)->len, (qstr)->name ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry; ++#ifdef CONFIG_AUFS_DEBUG ++extern struct mutex au_dbg_mtx; ++extern char *au_plevel; ++struct au_nhash; ++void au_dpri_whlist(struct au_nhash *whlist); ++struct au_vdir; ++void au_dpri_vdir(struct au_vdir *vdir); ++struct inode; ++void au_dpri_inode(struct inode *inode); ++void au_dpri_dalias(struct inode *inode); ++void au_dpri_dentry(struct dentry *dentry); ++struct file; ++void au_dpri_file(struct file *filp); ++struct super_block; ++void au_dpri_sb(struct super_block *sb); ++ ++#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) ++void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); ++void au_dbg_verify_kthread(void); ++ ++int __init au_debug_init(void); ++ ++#define AuDbgWhlist(w) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#w "\n"); \ ++ au_dpri_whlist(w); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgVdir(v) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#v "\n"); \ ++ au_dpri_vdir(v); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgInode(i) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#i "\n"); \ ++ au_dpri_inode(i); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgDAlias(i) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#i "\n"); \ ++ au_dpri_dalias(i); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgDentry(d) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#d "\n"); \ ++ au_dpri_dentry(d); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgFile(f) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#f "\n"); \ ++ au_dpri_file(f); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgSb(sb) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#sb "\n"); \ ++ au_dpri_sb(sb); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgSym(addr) do { \ ++ char sym[KSYM_SYMBOL_LEN]; \ ++ sprint_symbol(sym, (unsigned long)addr); \ ++ AuDbg("%s\n", sym); \ ++} while (0) ++#else ++AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) ++AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) ++AuStubVoid(au_dbg_verify_kthread, void) ++AuStubInt0(__init au_debug_init, void) ++ ++#define AuDbgWhlist(w) do {} while (0) ++#define AuDbgVdir(v) do {} while (0) ++#define AuDbgInode(i) do {} while (0) ++#define AuDbgDAlias(i) do {} while (0) ++#define AuDbgDentry(d) do {} while (0) ++#define AuDbgFile(f) do {} while (0) ++#define AuDbgSb(sb) do {} while (0) ++#define AuDbgSym(addr) do {} while (0) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_MAGIC_SYSRQ ++int __init au_sysrq_init(void); ++void au_sysrq_fin(void); ++ ++#ifdef CONFIG_HW_CONSOLE ++#define au_dbg_blocked() do { \ ++ WARN_ON(1); \ ++ handle_sysrq('w'); \ ++} while (0) ++#else ++AuStubVoid(au_dbg_blocked, void) ++#endif ++ ++#else ++AuStubInt0(__init au_sysrq_init, void) ++AuStubVoid(au_sysrq_fin, void) ++AuStubVoid(au_dbg_blocked, void) ++#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DEBUG_H__ */ +diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c +new file mode 100644 +index 0000000..993c95e +--- /dev/null ++++ b/fs/aufs/dentry.c +@@ -0,0 +1,1092 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#include ++#include "aufs.h" ++ ++#define AuLkup_ALLOW_NEG 1 ++#define AuLkup_IGNORE_PERM (1 << 1) ++#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) ++#define au_fset_lkup(flags, name) \ ++ do { (flags) |= AuLkup_##name; } while (0) ++#define au_fclr_lkup(flags, name) \ ++ do { (flags) &= ~AuLkup_##name; } while (0) ++ ++struct au_do_lookup_args { ++ unsigned int flags; ++ mode_t type; ++}; ++ ++/* ++ * returns positive/negative dentry, NULL or an error. ++ * NULL means whiteout-ed or not-found. ++ */ ++static struct dentry* ++au_do_lookup(struct dentry *h_parent, struct dentry *dentry, ++ aufs_bindex_t bindex, struct qstr *wh_name, ++ struct au_do_lookup_args *args) ++{ ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct au_branch *br; ++ int wh_found, opq; ++ unsigned char wh_able; ++ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); ++ const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, ++ IGNORE_PERM); ++ ++ wh_found = 0; ++ br = au_sbr(dentry->d_sb, bindex); ++ wh_able = !!au_br_whable(br->br_perm); ++ if (wh_able) ++ wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); ++ h_dentry = ERR_PTR(wh_found); ++ if (!wh_found) ++ goto real_lookup; ++ if (unlikely(wh_found < 0)) ++ goto out; ++ ++ /* We found a whiteout */ ++ /* au_set_dbend(dentry, bindex); */ ++ au_set_dbwh(dentry, bindex); ++ if (!allow_neg) ++ return NULL; /* success */ ++ ++real_lookup: ++ if (!ignore_perm) ++ h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); ++ else ++ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); ++ if (IS_ERR(h_dentry)) { ++ if (PTR_ERR(h_dentry) == -ENAMETOOLONG ++ && !allow_neg) ++ h_dentry = NULL; ++ goto out; ++ } ++ ++ h_inode = d_inode(h_dentry); ++ if (d_is_negative(h_dentry)) { ++ if (!allow_neg) ++ goto out_neg; ++ } else if (wh_found ++ || (args->type && args->type != (h_inode->i_mode & S_IFMT))) ++ goto out_neg; ++ ++ if (au_dbend(dentry) <= bindex) ++ au_set_dbend(dentry, bindex); ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ ++ if (!d_is_dir(h_dentry) ++ || !wh_able ++ || (d_really_is_positive(dentry) && !d_is_dir(dentry))) ++ goto out; /* success */ ++ ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ opq = au_diropq_test(h_dentry); ++ mutex_unlock(&h_inode->i_mutex); ++ if (opq > 0) ++ au_set_dbdiropq(dentry, bindex); ++ else if (unlikely(opq < 0)) { ++ au_set_h_dptr(dentry, bindex, NULL); ++ h_dentry = ERR_PTR(opq); ++ } ++ goto out; ++ ++out_neg: ++ dput(h_dentry); ++ h_dentry = NULL; ++out: ++ return h_dentry; ++} ++ ++static int au_test_shwh(struct super_block *sb, const struct qstr *name) ++{ ++ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) ++ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) ++ return -EPERM; ++ return 0; ++} ++ ++/* ++ * returns the number of lower positive dentries, ++ * otherwise an error. ++ * can be called at unlinking with @type is zero. ++ */ ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) ++{ ++ int npositive, err; ++ aufs_bindex_t bindex, btail, bdiropq; ++ unsigned char isdir, dirperm1; ++ struct qstr whname; ++ struct au_do_lookup_args args = { ++ .flags = 0, ++ .type = type ++ }; ++ const struct qstr *name = &dentry->d_name; ++ struct dentry *parent; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ err = au_test_shwh(sb, name); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_wh_name_alloc(&whname, name); ++ if (unlikely(err)) ++ goto out; ++ ++ isdir = !!d_is_dir(dentry); ++ if (!type) ++ au_fset_lkup(args.flags, ALLOW_NEG); ++ dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); ++ ++ npositive = 0; ++ parent = dget_parent(dentry); ++ btail = au_dbtaildir(parent); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ struct dentry *h_parent, *h_dentry; ++ struct inode *h_inode, *h_dir; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) { ++ if (d_is_positive(h_dentry)) ++ npositive++; ++ if (type != S_IFDIR) ++ break; ++ continue; ++ } ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !d_is_dir(h_parent)) ++ continue; ++ ++ h_dir = d_inode(h_parent); ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, ++ &args); ++ mutex_unlock(&h_dir->i_mutex); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out_parent; ++ if (h_dentry) ++ au_fclr_lkup(args.flags, ALLOW_NEG); ++ if (dirperm1) ++ au_fset_lkup(args.flags, IGNORE_PERM); ++ ++ if (au_dbwh(dentry) >= 0) ++ break; ++ if (!h_dentry) ++ continue; ++ if (d_is_negative(h_dentry)) ++ continue; ++ h_inode = d_inode(h_dentry); ++ npositive++; ++ if (!args.type) ++ args.type = h_inode->i_mode & S_IFMT; ++ if (args.type != S_IFDIR) ++ break; ++ else if (isdir) { ++ /* the type of lower may be different */ ++ bdiropq = au_dbdiropq(dentry); ++ if (bdiropq >= 0 && bdiropq <= bindex) ++ break; ++ } ++ } ++ ++ if (npositive) { ++ AuLabel(positive); ++ au_update_dbstart(dentry); ++ } ++ err = npositive; ++ if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) ++ && au_dbstart(dentry) < 0)) { ++ err = -EIO; ++ AuIOErr("both of real entry and whiteout found, %pd, err %d\n", ++ dentry, err); ++ } ++ ++out_parent: ++ dput(parent); ++ kfree(whname.name); ++out: ++ return err; ++} ++ ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) ++{ ++ struct dentry *dentry; ++ int wkq_err; ++ ++ if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC)) ++ dentry = vfsub_lkup_one(name, parent); ++ else { ++ struct vfsub_lkup_one_args args = { ++ .errp = &dentry, ++ .name = name, ++ .parent = parent ++ }; ++ ++ wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); ++ if (unlikely(wkq_err)) ++ dentry = ERR_PTR(wkq_err); ++ } ++ ++ return dentry; ++} ++ ++/* ++ * lookup @dentry on @bindex which should be negative. ++ */ ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) ++{ ++ int err; ++ struct dentry *parent, *h_parent, *h_dentry; ++ struct au_branch *br; ++ ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bindex); ++ br = au_sbr(dentry->d_sb, bindex); ++ if (wh) ++ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ else ++ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out; ++ if (unlikely(d_is_positive(h_dentry))) { ++ err = -EIO; ++ AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); ++ dput(h_dentry); ++ goto out; ++ } ++ ++ err = 0; ++ if (bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ if (au_dbend(dentry) < bindex) ++ au_set_dbend(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* subset of struct inode */ ++struct au_iattr { ++ unsigned long i_ino; ++ /* unsigned int i_nlink; */ ++ kuid_t i_uid; ++ kgid_t i_gid; ++ u64 i_version; ++/* ++ loff_t i_size; ++ blkcnt_t i_blocks; ++*/ ++ umode_t i_mode; ++}; ++ ++static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) ++{ ++ ia->i_ino = h_inode->i_ino; ++ /* ia->i_nlink = h_inode->i_nlink; */ ++ ia->i_uid = h_inode->i_uid; ++ ia->i_gid = h_inode->i_gid; ++ ia->i_version = h_inode->i_version; ++/* ++ ia->i_size = h_inode->i_size; ++ ia->i_blocks = h_inode->i_blocks; ++*/ ++ ia->i_mode = (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) ++{ ++ return ia->i_ino != h_inode->i_ino ++ /* || ia->i_nlink != h_inode->i_nlink */ ++ || !uid_eq(ia->i_uid, h_inode->i_uid) ++ || !gid_eq(ia->i_gid, h_inode->i_gid) ++ || ia->i_version != h_inode->i_version ++/* ++ || ia->i_size != h_inode->i_size ++ || ia->i_blocks != h_inode->i_blocks ++*/ ++ || ia->i_mode != (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, ++ struct au_branch *br) ++{ ++ int err; ++ struct au_iattr ia; ++ struct inode *h_inode; ++ struct dentry *h_d; ++ struct super_block *h_sb; ++ ++ err = 0; ++ memset(&ia, -1, sizeof(ia)); ++ h_sb = h_dentry->d_sb; ++ h_inode = NULL; ++ if (d_is_positive(h_dentry)) { ++ h_inode = d_inode(h_dentry); ++ au_iattr_save(&ia, h_inode); ++ } else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) ++ /* nfs d_revalidate may return 0 for negative dentry */ ++ /* fuse d_revalidate always return 0 for negative dentry */ ++ goto out; ++ ++ /* main purpose is namei.c:cached_lookup() and d_revalidate */ ++ h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out; ++ ++ err = 0; ++ if (unlikely(h_d != h_dentry ++ || d_inode(h_d) != h_inode ++ || (h_inode && au_iattr_test(&ia, h_inode)))) ++ err = au_busy_or_stale(); ++ dput(h_d); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br) ++{ ++ int err; ++ ++ err = 0; ++ if (udba == AuOpt_UDBA_REVAL ++ && !au_test_fs_remote(h_dentry->d_sb)) { ++ IMustLock(h_dir); ++ err = (d_inode(h_dentry->d_parent) != h_dir); ++ } else if (udba != AuOpt_UDBA_NONE) ++ err = au_h_verify_dentry(h_dentry, h_parent, br); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) ++{ ++ int err; ++ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; ++ struct au_hdentry tmp, *p, *q; ++ struct au_dinfo *dinfo; ++ struct super_block *sb; ++ ++ DiMustWriteLock(dentry); ++ ++ sb = dentry->d_sb; ++ dinfo = au_di(dentry); ++ bend = dinfo->di_bend; ++ bwh = dinfo->di_bwh; ++ bdiropq = dinfo->di_bdiropq; ++ p = dinfo->di_hdentry + dinfo->di_bstart; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { ++ if (!p->hd_dentry) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hd_id); ++ if (new_bindex == bindex) ++ continue; ++ ++ if (dinfo->di_bwh == bindex) ++ bwh = new_bindex; ++ if (dinfo->di_bdiropq == bindex) ++ bdiropq = new_bindex; ++ if (new_bindex < 0) { ++ au_hdput(p); ++ p->hd_dentry = NULL; ++ continue; ++ } ++ ++ /* swap two lower dentries, and loop again */ ++ q = dinfo->di_hdentry + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hd_dentry) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ dinfo->di_bwh = -1; ++ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) ++ dinfo->di_bwh = bwh; ++ ++ dinfo->di_bdiropq = -1; ++ if (bdiropq >= 0 ++ && bdiropq <= au_sbend(sb) ++ && au_sbr_whable(sb, bdiropq)) ++ dinfo->di_bdiropq = bdiropq; ++ ++ err = -EIO; ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ bend = au_dbend(parent); ++ p = dinfo->di_hdentry; ++ for (bindex = 0; bindex <= bend; bindex++, p++) ++ if (p->hd_dentry) { ++ dinfo->di_bstart = bindex; ++ break; ++ } ++ ++ if (dinfo->di_bstart >= 0) { ++ p = dinfo->di_hdentry + bend; ++ for (bindex = bend; bindex >= 0; bindex--, p--) ++ if (p->hd_dentry) { ++ dinfo->di_bend = bindex; ++ err = 0; ++ break; ++ } ++ } ++ ++ return err; ++} ++ ++static void au_do_hide(struct dentry *dentry) ++{ ++ struct inode *inode; ++ ++ if (d_really_is_positive(dentry)) { ++ inode = d_inode(dentry); ++ if (!d_is_dir(dentry)) { ++ if (inode->i_nlink && !d_unhashed(dentry)) ++ drop_nlink(inode); ++ } else { ++ clear_nlink(inode); ++ /* stop next lookup */ ++ inode->i_flags |= S_DEAD; ++ } ++ smp_mb(); /* necessary? */ ++ } ++ d_drop(dentry); ++} ++ ++static int au_hide_children(struct dentry *parent) ++{ ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry *dentry; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, parent, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ /* in reverse order */ ++ for (i = dpages.ndpage - 1; i >= 0; i--) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = ndentry - 1; j >= 0; j--) { ++ dentry = dpage->dentries[j]; ++ if (dentry != parent) ++ au_do_hide(dentry); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static void au_hide(struct dentry *dentry) ++{ ++ int err; ++ ++ AuDbgDentry(dentry); ++ if (d_is_dir(dentry)) { ++ /* shrink_dcache_parent(dentry); */ ++ err = au_hide_children(dentry); ++ if (unlikely(err)) ++ AuIOErr("%pd, failed hiding children, ignored %d\n", ++ dentry, err); ++ } ++ au_do_hide(dentry); ++} ++ ++/* ++ * By adding a dirty branch, a cached dentry may be affected in various ways. ++ * ++ * a dirty branch is added ++ * - on the top of layers ++ * - in the middle of layers ++ * - to the bottom of layers ++ * ++ * on the added branch there exists ++ * - a whiteout ++ * - a diropq ++ * - a same named entry ++ * + exist ++ * * negative --> positive ++ * * positive --> positive ++ * - type is unchanged ++ * - type is changed ++ * + doesn't exist ++ * * negative --> negative ++ * * positive --> negative (rejected by au_br_del() for non-dir case) ++ * - none ++ */ ++static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, ++ struct au_dinfo *tmp) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct { ++ struct dentry *dentry; ++ struct inode *inode; ++ mode_t mode; ++ } orig_h, tmp_h; ++ struct au_hdentry *hd; ++ struct inode *inode, *h_inode; ++ struct dentry *h_dentry; ++ ++ err = 0; ++ AuDebugOn(dinfo->di_bstart < 0); ++ orig_h.mode = 0; ++ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; ++ orig_h.inode = NULL; ++ if (d_is_positive(orig_h.dentry)) { ++ orig_h.inode = d_inode(orig_h.dentry); ++ orig_h.mode = orig_h.inode->i_mode & S_IFMT; ++ } ++ memset(&tmp_h, 0, sizeof(tmp_h)); ++ if (tmp->di_bstart >= 0) { ++ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; ++ tmp_h.inode = NULL; ++ if (d_is_positive(tmp_h.dentry)) { ++ tmp_h.inode = d_inode(tmp_h.dentry); ++ tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; ++ } ++ } ++ ++ inode = NULL; ++ if (d_really_is_positive(dentry)) ++ inode = d_inode(dentry); ++ if (!orig_h.inode) { ++ AuDbg("nagative originally\n"); ++ if (inode) { ++ au_hide(dentry); ++ goto out; ++ } ++ AuDebugOn(inode); ++ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ AuDebugOn(dinfo->di_bdiropq != -1); ++ ++ if (!tmp_h.inode) { ++ AuDbg("negative --> negative\n"); ++ /* should have only one negative lower */ ++ if (tmp->di_bstart >= 0 ++ && tmp->di_bstart < dinfo->di_bstart) { ++ AuDebugOn(tmp->di_bstart != tmp->di_bend); ++ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); ++ au_di_cp(dinfo, tmp); ++ hd = tmp->di_hdentry + tmp->di_bstart; ++ au_set_h_dptr(dentry, tmp->di_bstart, ++ dget(hd->hd_dentry)); ++ } ++ au_dbg_verify_dinode(dentry); ++ } else { ++ AuDbg("negative --> positive\n"); ++ /* ++ * similar to the behaviour of creating with bypassing ++ * aufs. ++ * unhash it in order to force an error in the ++ * succeeding create operation. ++ * we should not set S_DEAD here. ++ */ ++ d_drop(dentry); ++ /* au_di_swap(tmp, dinfo); */ ++ au_dbg_verify_dinode(dentry); ++ } ++ } else { ++ AuDbg("positive originally\n"); ++ /* inode may be NULL */ ++ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); ++ if (!tmp_h.inode) { ++ AuDbg("positive --> negative\n"); ++ /* or bypassing aufs */ ++ au_hide(dentry); ++ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) ++ dinfo->di_bwh = tmp->di_bwh; ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } else if (orig_h.mode == tmp_h.mode) { ++ AuDbg("positive --> positive, same type\n"); ++ if (!S_ISDIR(orig_h.mode) ++ && dinfo->di_bstart > tmp->di_bstart) { ++ /* ++ * similar to the behaviour of removing and ++ * creating. ++ */ ++ au_hide(dentry); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } else { ++ /* fill empty slots */ ++ if (dinfo->di_bstart > tmp->di_bstart) ++ dinfo->di_bstart = tmp->di_bstart; ++ if (dinfo->di_bend < tmp->di_bend) ++ dinfo->di_bend = tmp->di_bend; ++ dinfo->di_bwh = tmp->di_bwh; ++ dinfo->di_bdiropq = tmp->di_bdiropq; ++ hd = tmp->di_hdentry; ++ bend = dinfo->di_bend; ++ for (bindex = tmp->di_bstart; bindex <= bend; ++ bindex++) { ++ if (au_h_dptr(dentry, bindex)) ++ continue; ++ h_dentry = hd[bindex].hd_dentry; ++ if (!h_dentry) ++ continue; ++ AuDebugOn(d_is_negative(h_dentry)); ++ h_inode = d_inode(h_dentry); ++ AuDebugOn(orig_h.mode ++ != (h_inode->i_mode ++ & S_IFMT)); ++ au_set_h_dptr(dentry, bindex, ++ dget(h_dentry)); ++ } ++ err = au_refresh_hinode(inode, dentry); ++ au_dbg_verify_dinode(dentry); ++ } ++ } else { ++ AuDbg("positive --> positive, different type\n"); ++ /* similar to the behaviour of removing and creating */ ++ au_hide(dentry); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } ++ } ++ ++out: ++ return err; ++} ++ ++int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) ++{ ++ int err, ebrange; ++ unsigned int sigen; ++ struct au_dinfo *dinfo, *tmp; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ DiMustWriteLock(dentry); ++ AuDebugOn(IS_ROOT(dentry)); ++ AuDebugOn(d_really_is_negative(parent)); ++ ++ sb = dentry->d_sb; ++ sigen = au_sigen(sb); ++ err = au_digen_test(parent, sigen); ++ if (unlikely(err)) ++ goto out; ++ ++ dinfo = au_di(dentry); ++ err = au_di_realloc(dinfo, au_sbend(sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ ebrange = au_dbrange_test(dentry); ++ if (!ebrange) ++ ebrange = au_do_refresh_hdentry(dentry, parent); ++ ++ if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { ++ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); ++ if (d_really_is_positive(dentry)) { ++ inode = d_inode(dentry); ++ err = au_refresh_hinode_self(inode); ++ } ++ au_dbg_verify_dinode(dentry); ++ if (!err) ++ goto out_dgen; /* success */ ++ goto out; ++ } ++ ++ /* temporary dinfo */ ++ AuDbgDentry(dentry); ++ err = -ENOMEM; ++ tmp = au_di_alloc(sb, AuLsc_DI_TMP); ++ if (unlikely(!tmp)) ++ goto out; ++ au_di_swap(tmp, dinfo); ++ /* returns the number of positive dentries */ ++ /* ++ * if current working dir is removed, it returns an error. ++ * but the dentry is legal. ++ */ ++ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); ++ AuDbgDentry(dentry); ++ au_di_swap(tmp, dinfo); ++ if (err == -ENOENT) ++ err = 0; ++ if (err >= 0) { ++ /* compare/refresh by dinfo */ ++ AuDbgDentry(dentry); ++ err = au_refresh_by_dinfo(dentry, dinfo, tmp); ++ au_dbg_verify_dinode(dentry); ++ AuTraceErr(err); ++ } ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ if (unlikely(err)) ++ goto out; ++ ++out_dgen: ++ au_update_digen(dentry); ++out: ++ if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { ++ AuIOErr("failed refreshing %pd, %d\n", dentry, err); ++ AuDbgDentry(dentry); ++ } ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, ++ struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err, valid; ++ ++ err = 0; ++ if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) ++ goto out; ++ ++ AuDbg("b%d\n", bindex); ++ /* ++ * gave up supporting LOOKUP_CREATE/OPEN for lower fs, ++ * due to whiteout and branch permission. ++ */ ++ flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE ++ | LOOKUP_FOLLOW | LOOKUP_EXCL); ++ /* it may return tri-state */ ++ valid = h_dentry->d_op->d_revalidate(h_dentry, flags); ++ ++ if (unlikely(valid < 0)) ++ err = valid; ++ else if (!valid) ++ err = -EINVAL; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* todo: remove this */ ++static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ++ unsigned int flags, int do_udba) ++{ ++ int err; ++ umode_t mode, h_mode; ++ aufs_bindex_t bindex, btail, bstart, ibs, ibe; ++ unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; ++ struct inode *h_inode, *h_cached_inode; ++ struct dentry *h_dentry; ++ struct qstr *name, *h_name; ++ ++ err = 0; ++ plus = 0; ++ mode = 0; ++ ibs = -1; ++ ibe = -1; ++ unhashed = !!d_unhashed(dentry); ++ is_root = !!IS_ROOT(dentry); ++ name = &dentry->d_name; ++ tmpfile = au_di(dentry)->di_tmpfile; ++ ++ /* ++ * Theoretically, REVAL test should be unnecessary in case of ++ * {FS,I}NOTIFY. ++ * But {fs,i}notify doesn't fire some necessary events, ++ * IN_ATTRIB for atime/nlink/pageio ++ * Let's do REVAL test too. ++ */ ++ if (do_udba && inode) { ++ mode = (inode->i_mode & S_IFMT); ++ plus = (inode->i_nlink > 0); ++ ibs = au_ibstart(inode); ++ ibe = au_ibend(inode); ++ } ++ ++ bstart = au_dbstart(dentry); ++ btail = bstart; ++ if (inode && S_ISDIR(inode->i_mode)) ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ AuDbg("b%d, %pd\n", bindex, h_dentry); ++ h_nfs = !!au_test_nfs(h_dentry->d_sb); ++ spin_lock(&h_dentry->d_lock); ++ h_name = &h_dentry->d_name; ++ if (unlikely(do_udba ++ && !is_root ++ && ((!h_nfs ++ && (unhashed != !!d_unhashed(h_dentry) ++ || (!tmpfile ++ && !au_qstreq(name, h_name)) ++ )) ++ || (h_nfs ++ && !(flags & LOOKUP_OPEN) ++ && (h_dentry->d_flags ++ & DCACHE_NFSFS_RENAMED))) ++ )) { ++ int h_unhashed; ++ ++ h_unhashed = d_unhashed(h_dentry); ++ spin_unlock(&h_dentry->d_lock); ++ AuDbg("unhash 0x%x 0x%x, %pd %pd\n", ++ unhashed, h_unhashed, dentry, h_dentry); ++ goto err; ++ } ++ spin_unlock(&h_dentry->d_lock); ++ ++ err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); ++ if (unlikely(err)) ++ /* do not goto err, to keep the errno */ ++ break; ++ ++ /* todo: plink too? */ ++ if (!do_udba) ++ continue; ++ ++ /* UDBA tests */ ++ if (unlikely(!!inode != d_is_positive(h_dentry))) ++ goto err; ++ ++ h_inode = NULL; ++ if (d_is_positive(h_dentry)) ++ h_inode = d_inode(h_dentry); ++ h_plus = plus; ++ h_mode = mode; ++ h_cached_inode = h_inode; ++ if (h_inode) { ++ h_mode = (h_inode->i_mode & S_IFMT); ++ h_plus = (h_inode->i_nlink > 0); ++ } ++ if (inode && ibs <= bindex && bindex <= ibe) ++ h_cached_inode = au_h_iptr(inode, bindex); ++ ++ if (!h_nfs) { ++ if (unlikely(plus != h_plus && !tmpfile)) ++ goto err; ++ } else { ++ if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) ++ && !is_root ++ && !IS_ROOT(h_dentry) ++ && unhashed != d_unhashed(h_dentry))) ++ goto err; ++ } ++ if (unlikely(mode != h_mode ++ || h_cached_inode != h_inode)) ++ goto err; ++ continue; ++ ++err: ++ err = -EINVAL; ++ break; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* todo: consolidate with do_refresh() and au_reval_for_attr() */ ++static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *parent; ++ ++ if (!au_digen_test(dentry, sigen)) ++ return 0; ++ ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ AuDebugOn(au_digen_test(parent, sigen)); ++ au_dbg_verify_gen(parent, sigen); ++ err = au_refresh_dentry(dentry, parent); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *d, *parent; ++ ++ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) ++ return simple_reval_dpath(dentry, sigen); ++ ++ /* slow loop, keep it simple and stupid */ ++ /* cf: au_cpup_dirs() */ ++ err = 0; ++ parent = NULL; ++ while (au_digen_test(dentry, sigen)) { ++ d = dentry; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(d); ++ if (!au_digen_test(parent, sigen)) ++ break; ++ d = parent; ++ } ++ ++ if (d != dentry) ++ di_write_lock_child2(d); ++ ++ /* someone might update our dentry while we were sleeping */ ++ if (au_digen_test(d, sigen)) { ++ /* ++ * todo: consolidate with simple_reval_dpath(), ++ * do_refresh() and au_reval_for_attr(). ++ */ ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(d, parent); ++ di_read_unlock(parent, AuLock_IR); ++ } ++ ++ if (d != dentry) ++ di_write_unlock(d); ++ dput(parent); ++ if (unlikely(err)) ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * if valid returns 1, otherwise 0. ++ */ ++static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) ++{ ++ int valid, err; ++ unsigned int sigen; ++ unsigned char do_udba; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ /* todo: support rcu-walk? */ ++ if (flags & LOOKUP_RCU) ++ return -ECHILD; ++ ++ valid = 0; ++ if (unlikely(!au_di(dentry))) ++ goto out; ++ ++ valid = 1; ++ sb = dentry->d_sb; ++ /* ++ * todo: very ugly ++ * i_mutex of parent dir may be held, ++ * but we should not return 'invalid' due to busy. ++ */ ++ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); ++ if (unlikely(err)) { ++ valid = err; ++ AuTraceErr(err); ++ goto out; ++ } ++ inode = NULL; ++ if (d_really_is_positive(dentry)) ++ inode = d_inode(dentry); ++ if (unlikely(inode && is_bad_inode(inode))) { ++ err = -EINVAL; ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ if (unlikely(au_dbrange_test(dentry))) { ++ err = -EINVAL; ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ ++ sigen = au_sigen(sb); ++ if (au_digen_test(dentry, sigen)) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_dpath(dentry, sigen); ++ if (unlikely(err)) { ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ } ++ di_downgrade_lock(dentry, AuLock_IR); ++ ++ err = -EINVAL; ++ if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) ++ && inode ++ && !(inode->i_state && I_LINKABLE) ++ && (IS_DEADDIR(inode) || !inode->i_nlink)) ++ goto out_inval; ++ ++ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); ++ if (do_udba && inode) { ++ aufs_bindex_t bstart = au_ibstart(inode); ++ struct inode *h_inode; ++ ++ if (bstart >= 0) { ++ h_inode = au_h_iptr(inode, bstart); ++ if (h_inode && au_test_higen(inode, h_inode)) ++ goto out_inval; ++ } ++ } ++ ++ err = h_d_revalidate(dentry, inode, flags, do_udba); ++ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { ++ err = -EIO; ++ AuDbg("both of real entry and whiteout found, %p, err %d\n", ++ dentry, err); ++ } ++ goto out_inval; ++ ++out_dgrade: ++ di_downgrade_lock(dentry, AuLock_IR); ++out_inval: ++ aufs_read_unlock(dentry, AuLock_IR); ++ AuTraceErr(err); ++ valid = !err; ++out: ++ if (!valid) { ++ AuDbg("%pd invalid, %d\n", dentry, valid); ++ d_drop(dentry); ++ } ++ return valid; ++} ++ ++static void aufs_d_release(struct dentry *dentry) ++{ ++ if (au_di(dentry)) { ++ au_di_fin(dentry); ++ au_hn_di_reinit(dentry); ++ } ++} ++ ++const struct dentry_operations aufs_dop = { ++ .d_revalidate = aufs_d_revalidate, ++ .d_weak_revalidate = aufs_d_revalidate, ++ .d_release = aufs_d_release ++}; +diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h +new file mode 100644 +index 0000000..6516146 +--- /dev/null ++++ b/fs/aufs/dentry.h +@@ -0,0 +1,220 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#ifndef __AUFS_DENTRY_H__ ++#define __AUFS_DENTRY_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "rwsem.h" ++ ++struct au_hdentry { ++ struct dentry *hd_dentry; ++ aufs_bindex_t hd_id; ++}; ++ ++struct au_dinfo { ++ atomic_t di_generation; ++ ++ struct au_rwsem di_rwsem; ++ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; ++ unsigned char di_tmpfile; /* to allow the different name */ ++ struct au_hdentry *di_hdentry; ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dentry.c */ ++extern const struct dentry_operations aufs_dop; ++struct au_branch; ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent); ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br); ++ ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); ++int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen); ++ ++/* dinfo.c */ ++void au_di_init_once(void *_di); ++struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); ++void au_di_free(struct au_dinfo *dinfo); ++void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); ++void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); ++int au_di_init(struct dentry *dentry); ++void au_di_fin(struct dentry *dentry); ++int au_di_realloc(struct au_dinfo *dinfo, int nbr); ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc); ++void di_read_unlock(struct dentry *d, int flags); ++void di_downgrade_lock(struct dentry *d, int flags); ++void di_write_lock(struct dentry *d, unsigned int lsc); ++void di_write_unlock(struct dentry *d); ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); ++struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); ++aufs_bindex_t au_dbtail(struct dentry *dentry); ++aufs_bindex_t au_dbtaildir(struct dentry *dentry); ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++int au_digen_test(struct dentry *dentry, unsigned int sigen); ++int au_dbrange_test(struct dentry *dentry); ++void au_update_digen(struct dentry *dentry); ++void au_update_dbrange(struct dentry *dentry, int do_put_zero); ++void au_update_dbstart(struct dentry *dentry); ++void au_update_dbend(struct dentry *dentry); ++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_dinfo *au_di(struct dentry *dentry) ++{ ++ return dentry->d_fsdata; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for dinfo */ ++enum { ++ AuLsc_DI_CHILD, /* child first */ ++ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ ++ AuLsc_DI_CHILD3, /* copyup dirs */ ++ AuLsc_DI_PARENT, ++ AuLsc_DI_PARENT2, ++ AuLsc_DI_PARENT3, ++ AuLsc_DI_TMP /* temp for replacing dinfo */ ++}; ++ ++/* ++ * di_read_lock_child, di_write_lock_child, ++ * di_read_lock_child2, di_write_lock_child2, ++ * di_read_lock_child3, di_write_lock_child3, ++ * di_read_lock_parent, di_write_lock_parent, ++ * di_read_lock_parent2, di_write_lock_parent2, ++ * di_read_lock_parent3, di_write_lock_parent3, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void di_read_lock_##name(struct dentry *d, int flags) \ ++{ di_read_lock(d, flags, AuLsc_DI_##lsc); } ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void di_write_lock_##name(struct dentry *d) \ ++{ di_write_lock(d, AuLsc_DI_##lsc); } ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) ++#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) ++#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_digen(struct dentry *d) ++{ ++ return atomic_read(&au_di(d)->di_generation); ++} ++ ++static inline void au_h_dentry_init(struct au_hdentry *hdentry) ++{ ++ hdentry->hd_dentry = NULL; ++} ++ ++static inline void au_hdput(struct au_hdentry *hd) ++{ ++ if (hd) ++ dput(hd->hd_dentry); ++} ++ ++static inline aufs_bindex_t au_dbstart(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bstart; ++} ++ ++static inline aufs_bindex_t au_dbend(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bend; ++} ++ ++static inline aufs_bindex_t au_dbwh(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bwh; ++} ++ ++static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bdiropq; ++} ++ ++/* todo: hard/soft set? */ ++static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bstart = bindex; ++} ++ ++static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bend = bindex; ++} ++ ++static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ /* dbwh can be outside of bstart - bend range */ ++ au_di(dentry)->di_bwh = bindex; ++} ++ ++static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bdiropq = bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_HNOTIFY ++static inline void au_digen_dec(struct dentry *d) ++{ ++ atomic_dec(&au_di(d)->di_generation); ++} ++ ++static inline void au_hn_di_reinit(struct dentry *dentry) ++{ ++ dentry->d_fsdata = NULL; ++} ++#else ++AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) ++#endif /* CONFIG_AUFS_HNOTIFY */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DENTRY_H__ */ +diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c +new file mode 100644 +index 0000000..25b971b +--- /dev/null ++++ b/fs/aufs/dinfo.c +@@ -0,0 +1,537 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * dentry private data ++ */ ++ ++#include "aufs.h" ++ ++void au_di_init_once(void *_dinfo) ++{ ++ struct au_dinfo *dinfo = _dinfo; ++ static struct lock_class_key aufs_di; ++ ++ au_rw_init(&dinfo->di_rwsem); ++ au_rw_class(&dinfo->di_rwsem, &aufs_di); ++} ++ ++struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) ++{ ++ struct au_dinfo *dinfo; ++ int nbr, i; ++ ++ dinfo = au_cache_alloc_dinfo(); ++ if (unlikely(!dinfo)) ++ goto out; ++ ++ nbr = au_sbend(sb) + 1; ++ if (nbr <= 0) ++ nbr = 1; ++ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); ++ if (dinfo->di_hdentry) { ++ au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ dinfo->di_bwh = -1; ++ dinfo->di_bdiropq = -1; ++ dinfo->di_tmpfile = 0; ++ for (i = 0; i < nbr; i++) ++ dinfo->di_hdentry[i].hd_id = -1; ++ goto out; ++ } ++ ++ au_cache_free_dinfo(dinfo); ++ dinfo = NULL; ++ ++out: ++ return dinfo; ++} ++ ++void au_di_free(struct au_dinfo *dinfo) ++{ ++ struct au_hdentry *p; ++ aufs_bindex_t bend, bindex; ++ ++ /* dentry may not be revalidated */ ++ bindex = dinfo->di_bstart; ++ if (bindex >= 0) { ++ bend = dinfo->di_bend; ++ p = dinfo->di_hdentry + bindex; ++ while (bindex++ <= bend) ++ au_hdput(p++); ++ } ++ kfree(dinfo->di_hdentry); ++ au_cache_free_dinfo(dinfo); ++} ++ ++void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) ++{ ++ struct au_hdentry *p; ++ aufs_bindex_t bi; ++ ++ AuRwMustWriteLock(&a->di_rwsem); ++ AuRwMustWriteLock(&b->di_rwsem); ++ ++#define DiSwap(v, name) \ ++ do { \ ++ v = a->di_##name; \ ++ a->di_##name = b->di_##name; \ ++ b->di_##name = v; \ ++ } while (0) ++ ++ DiSwap(p, hdentry); ++ DiSwap(bi, bstart); ++ DiSwap(bi, bend); ++ DiSwap(bi, bwh); ++ DiSwap(bi, bdiropq); ++ /* smp_mb(); */ ++ ++#undef DiSwap ++} ++ ++void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) ++{ ++ AuRwMustWriteLock(&dst->di_rwsem); ++ AuRwMustWriteLock(&src->di_rwsem); ++ ++ dst->di_bstart = src->di_bstart; ++ dst->di_bend = src->di_bend; ++ dst->di_bwh = src->di_bwh; ++ dst->di_bdiropq = src->di_bdiropq; ++ /* smp_mb(); */ ++} ++ ++int au_di_init(struct dentry *dentry) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_dinfo *dinfo; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); ++ if (dinfo) { ++ atomic_set(&dinfo->di_generation, au_sigen(sb)); ++ /* smp_mb(); */ /* atomic_set */ ++ dentry->d_fsdata = dinfo; ++ } else ++ err = -ENOMEM; ++ ++ return err; ++} ++ ++void au_di_fin(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ ++ dinfo = au_di(dentry); ++ AuRwDestroy(&dinfo->di_rwsem); ++ au_di_free(dinfo); ++} ++ ++int au_di_realloc(struct au_dinfo *dinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hdentry *hdp; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*hdp) * (dinfo->di_bend + 1); ++ if (!sz) ++ sz = sizeof(*hdp); ++ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); ++ if (hdp) { ++ dinfo->di_hdentry = hdp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void do_ii_write_lock(struct inode *inode, unsigned int lsc) ++{ ++ switch (lsc) { ++ case AuLsc_DI_CHILD: ++ ii_write_lock_child(inode); ++ break; ++ case AuLsc_DI_CHILD2: ++ ii_write_lock_child2(inode); ++ break; ++ case AuLsc_DI_CHILD3: ++ ii_write_lock_child3(inode); ++ break; ++ case AuLsc_DI_PARENT: ++ ii_write_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_write_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ ii_write_lock_parent3(inode); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void do_ii_read_lock(struct inode *inode, unsigned int lsc) ++{ ++ switch (lsc) { ++ case AuLsc_DI_CHILD: ++ ii_read_lock_child(inode); ++ break; ++ case AuLsc_DI_CHILD2: ++ ii_read_lock_child2(inode); ++ break; ++ case AuLsc_DI_CHILD3: ++ ii_read_lock_child3(inode); ++ break; ++ case AuLsc_DI_PARENT: ++ ii_read_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_read_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ ii_read_lock_parent3(inode); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc) ++{ ++ struct inode *inode; ++ ++ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); ++ if (d_really_is_positive(d)) { ++ inode = d_inode(d); ++ if (au_ftest_lock(flags, IW)) ++ do_ii_write_lock(inode, lsc); ++ else if (au_ftest_lock(flags, IR)) ++ do_ii_read_lock(inode, lsc); ++ } ++} ++ ++void di_read_unlock(struct dentry *d, int flags) ++{ ++ struct inode *inode; ++ ++ if (d_really_is_positive(d)) { ++ inode = d_inode(d); ++ if (au_ftest_lock(flags, IW)) { ++ au_dbg_verify_dinode(d); ++ ii_write_unlock(inode); ++ } else if (au_ftest_lock(flags, IR)) { ++ au_dbg_verify_dinode(d); ++ ii_read_unlock(inode); ++ } ++ } ++ au_rw_read_unlock(&au_di(d)->di_rwsem); ++} ++ ++void di_downgrade_lock(struct dentry *d, int flags) ++{ ++ if (d_really_is_positive(d) && au_ftest_lock(flags, IR)) ++ ii_downgrade_lock(d_inode(d)); ++ au_rw_dgrade_lock(&au_di(d)->di_rwsem); ++} ++ ++void di_write_lock(struct dentry *d, unsigned int lsc) ++{ ++ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); ++ if (d_really_is_positive(d)) ++ do_ii_write_lock(d_inode(d), lsc); ++} ++ ++void di_write_unlock(struct dentry *d) ++{ ++ au_dbg_verify_dinode(d); ++ if (d_really_is_positive(d)) ++ ii_write_unlock(d_inode(d)); ++ au_rw_write_unlock(&au_di(d)->di_rwsem); ++} ++ ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) ++{ ++ AuDebugOn(d1 == d2 ++ || d_inode(d1) == d_inode(d2) ++ || d1->d_sb != d2->d_sb); ++ ++ if (isdir && au_test_subdir(d1, d2)) { ++ di_write_lock_child(d1); ++ di_write_lock_child2(d2); ++ } else { ++ /* there should be no races */ ++ di_write_lock_child(d2); ++ di_write_lock_child2(d1); ++ } ++} ++ ++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) ++{ ++ AuDebugOn(d1 == d2 ++ || d_inode(d1) == d_inode(d2) ++ || d1->d_sb != d2->d_sb); ++ ++ if (isdir && au_test_subdir(d1, d2)) { ++ di_write_lock_parent(d1); ++ di_write_lock_parent2(d2); ++ } else { ++ /* there should be no races */ ++ di_write_lock_parent(d2); ++ di_write_lock_parent2(d1); ++ } ++} ++ ++void di_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock(d1); ++ if (d_inode(d1) == d_inode(d2)) ++ au_rw_write_unlock(&au_di(d2)->di_rwsem); ++ else ++ di_write_unlock(d2); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ struct dentry *d; ++ ++ DiMustAnyLock(dentry); ++ ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ return NULL; ++ AuDebugOn(bindex < 0); ++ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; ++ AuDebugOn(d && au_dcount(d) <= 0); ++ return d; ++} ++ ++/* ++ * extended version of au_h_dptr(). ++ * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or ++ * error. ++ */ ++struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ struct dentry *h_dentry; ++ struct inode *inode, *h_inode; ++ ++ AuDebugOn(d_really_is_negative(dentry)); ++ ++ h_dentry = NULL; ++ if (au_dbstart(dentry) <= bindex ++ && bindex <= au_dbend(dentry)) ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && !au_d_linkable(h_dentry)) { ++ dget(h_dentry); ++ goto out; /* success */ ++ } ++ ++ inode = d_inode(dentry); ++ AuDebugOn(bindex < au_ibstart(inode)); ++ AuDebugOn(au_ibend(inode) < bindex); ++ h_inode = au_h_iptr(inode, bindex); ++ h_dentry = d_find_alias(h_inode); ++ if (h_dentry) { ++ if (!IS_ERR(h_dentry)) { ++ if (!au_d_linkable(h_dentry)) ++ goto out; /* success */ ++ dput(h_dentry); ++ } else ++ goto out; ++ } ++ ++ if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { ++ h_dentry = au_plink_lkup(inode, bindex); ++ AuDebugOn(!h_dentry); ++ if (!IS_ERR(h_dentry)) { ++ if (!au_d_hashed_positive(h_dentry)) ++ goto out; /* success */ ++ dput(h_dentry); ++ h_dentry = NULL; ++ } ++ } ++ ++out: ++ AuDbgDentry(h_dentry); ++ return h_dentry; ++} ++ ++aufs_bindex_t au_dbtail(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bwh; ++ ++ bend = au_dbend(dentry); ++ if (0 <= bend) { ++ bwh = au_dbwh(dentry); ++ if (!bwh) ++ return bwh; ++ if (0 < bwh && bwh < bend) ++ return bwh - 1; ++ } ++ return bend; ++} ++ ++aufs_bindex_t au_dbtaildir(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bopq; ++ ++ bend = au_dbtail(dentry); ++ if (0 <= bend) { ++ bopq = au_dbdiropq(dentry); ++ if (0 <= bopq && bopq < bend) ++ bend = bopq; ++ } ++ return bend; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; ++ struct au_branch *br; ++ ++ DiMustWriteLock(dentry); ++ ++ au_hdput(hd); ++ hd->hd_dentry = h_dentry; ++ if (h_dentry) { ++ br = au_sbr(dentry->d_sb, bindex); ++ hd->hd_id = br->br_id; ++ } ++} ++ ++int au_dbrange_test(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bstart, bend; ++ ++ err = 0; ++ bstart = au_dbstart(dentry); ++ bend = au_dbend(dentry); ++ if (bstart >= 0) ++ AuDebugOn(bend < 0 && bstart > bend); ++ else { ++ err = -EIO; ++ AuDebugOn(bend >= 0); ++ } ++ ++ return err; ++} ++ ++int au_digen_test(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(au_digen(dentry) != sigen ++ || au_iigen_test(d_inode(dentry), sigen))) ++ err = -EIO; ++ ++ return err; ++} ++ ++void au_update_digen(struct dentry *dentry) ++{ ++ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++void au_update_dbrange(struct dentry *dentry, int do_put_zero) ++{ ++ struct au_dinfo *dinfo; ++ struct dentry *h_d; ++ struct au_hdentry *hdp; ++ ++ DiMustWriteLock(dentry); ++ ++ dinfo = au_di(dentry); ++ if (!dinfo || dinfo->di_bstart < 0) ++ return; ++ ++ hdp = dinfo->di_hdentry; ++ if (do_put_zero) { ++ aufs_bindex_t bindex, bend; ++ ++ bend = dinfo->di_bend; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { ++ h_d = hdp[0 + bindex].hd_dentry; ++ if (h_d && d_is_negative(h_d)) ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++ } ++ ++ dinfo->di_bstart = -1; ++ while (++dinfo->di_bstart <= dinfo->di_bend) ++ if (hdp[0 + dinfo->di_bstart].hd_dentry) ++ break; ++ if (dinfo->di_bstart > dinfo->di_bend) { ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ return; ++ } ++ ++ dinfo->di_bend++; ++ while (0 <= --dinfo->di_bend) ++ if (hdp[0 + dinfo->di_bend].hd_dentry) ++ break; ++ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); ++} ++ ++void au_update_dbstart(struct dentry *dentry) ++{ ++ aufs_bindex_t bindex, bend; ++ struct dentry *h_dentry; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (d_is_positive(h_dentry)) { ++ au_set_dbstart(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++void au_update_dbend(struct dentry *dentry) ++{ ++ aufs_bindex_t bindex, bstart; ++ struct dentry *h_dentry; ++ ++ bstart = au_dbstart(dentry); ++ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (d_is_positive(h_dentry)) { ++ au_set_dbend(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) ++ if (au_h_dptr(dentry, bindex) == h_dentry) ++ return bindex; ++ return -1; ++} +diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c +new file mode 100644 +index 0000000..6308190 +--- /dev/null ++++ b/fs/aufs/dir.c +@@ -0,0 +1,740 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#include ++#include "aufs.h" ++ ++void au_add_nlink(struct inode *dir, struct inode *h_dir) ++{ ++ unsigned int nlink; ++ ++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); ++ ++ nlink = dir->i_nlink; ++ nlink += h_dir->i_nlink - 2; ++ if (h_dir->i_nlink < 2) ++ nlink += 2; ++ smp_mb(); /* for i_nlink */ ++ /* 0 can happen in revaliding */ ++ set_nlink(dir, nlink); ++} ++ ++void au_sub_nlink(struct inode *dir, struct inode *h_dir) ++{ ++ unsigned int nlink; ++ ++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); ++ ++ nlink = dir->i_nlink; ++ nlink -= h_dir->i_nlink - 2; ++ if (h_dir->i_nlink < 2) ++ nlink -= 2; ++ smp_mb(); /* for i_nlink */ ++ /* nlink == 0 means the branch-fs is broken */ ++ set_nlink(dir, nlink); ++} ++ ++loff_t au_dir_size(struct file *file, struct dentry *dentry) ++{ ++ loff_t sz; ++ aufs_bindex_t bindex, bend; ++ struct file *h_file; ++ struct dentry *h_dentry; ++ ++ sz = 0; ++ if (file) { ++ AuDebugOn(!d_is_dir(file->f_path.dentry)); ++ ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); ++ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (h_file && file_inode(h_file)) ++ sz += vfsub_f_size_read(h_file); ++ } ++ } else { ++ AuDebugOn(!dentry); ++ AuDebugOn(!d_is_dir(dentry)); ++ ++ bend = au_dbtaildir(dentry); ++ for (bindex = au_dbstart(dentry); ++ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && d_is_positive(h_dentry)) ++ sz += i_size_read(d_inode(h_dentry)); ++ } ++ } ++ if (sz < KMALLOC_MAX_SIZE) ++ sz = roundup_pow_of_two(sz); ++ if (sz > KMALLOC_MAX_SIZE) ++ sz = KMALLOC_MAX_SIZE; ++ else if (sz < NAME_MAX) { ++ BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); ++ sz = AUFS_RDBLK_DEF; ++ } ++ return sz; ++} ++ ++struct au_dir_ts_arg { ++ struct dentry *dentry; ++ aufs_bindex_t brid; ++}; ++ ++static void au_do_dir_ts(void *arg) ++{ ++ struct au_dir_ts_arg *a = arg; ++ struct au_dtime dt; ++ struct path h_path; ++ struct inode *dir, *h_dir; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_hinode *hdir; ++ int err; ++ aufs_bindex_t bstart, bindex; ++ ++ sb = a->dentry->d_sb; ++ if (d_really_is_negative(a->dentry)) ++ goto out; ++ aufs_read_lock(a->dentry, AuLock_DW | AuLock_DIR); /* noflush */ ++ ++ /* no dir->i_mutex lock */ ++ dir = d_inode(a->dentry); ++ bstart = au_ibstart(dir); ++ bindex = au_br_index(sb, a->brid); ++ if (bindex < bstart) ++ goto out_unlock; ++ ++ br = au_sbr(sb, bindex); ++ h_path.dentry = au_h_dptr(a->dentry, bindex); ++ if (!h_path.dentry) ++ goto out_unlock; ++ h_path.mnt = au_br_mnt(br); ++ au_dtime_store(&dt, a->dentry, &h_path); ++ ++ br = au_sbr(sb, bstart); ++ if (!au_br_writable(br->br_perm)) ++ goto out_unlock; ++ h_path.dentry = au_h_dptr(a->dentry, bstart); ++ h_path.mnt = au_br_mnt(br); ++ err = vfsub_mnt_want_write(h_path.mnt); ++ if (err) ++ goto out_unlock; ++ hdir = au_hi(dir, bstart); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ h_dir = au_h_iptr(dir, bstart); ++ if (h_dir->i_nlink ++ && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { ++ dt.dt_h_path = h_path; ++ au_dtime_revert(&dt); ++ } ++ au_hn_imtx_unlock(hdir); ++ vfsub_mnt_drop_write(h_path.mnt); ++ au_cpup_attr_timesizes(dir); ++ ++out_unlock: ++ aufs_read_unlock(a->dentry, AuLock_DW); ++out: ++ dput(a->dentry); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ kfree(arg); ++} ++ ++void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) ++{ ++ int perm, wkq_err; ++ aufs_bindex_t bstart; ++ struct au_dir_ts_arg *arg; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ IMustLock(dir); ++ ++ dentry = d_find_any_alias(dir); ++ AuDebugOn(!dentry); ++ sb = dentry->d_sb; ++ bstart = au_ibstart(dir); ++ if (bstart == bindex) { ++ au_cpup_attr_timesizes(dir); ++ goto out; ++ } ++ ++ perm = au_sbr_perm(sb, bstart); ++ if (!au_br_writable(perm)) ++ goto out; ++ ++ arg = kmalloc(sizeof(*arg), GFP_NOFS); ++ if (!arg) ++ goto out; ++ ++ arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ ++ arg->brid = au_sbr_id(sb, bindex); ++ wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); ++ if (unlikely(wkq_err)) { ++ pr_err("wkq %d\n", wkq_err); ++ dput(dentry); ++ kfree(arg); ++ } ++ ++out: ++ dput(dentry); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int reopen_dir(struct file *file) ++{ ++ int err; ++ unsigned int flags; ++ aufs_bindex_t bindex, btail, bstart; ++ struct dentry *dentry, *h_dentry; ++ struct file *h_file; ++ ++ /* open all lower dirs */ ++ dentry = file->f_path.dentry; ++ bstart = au_dbstart(dentry); ++ for (bindex = au_fbstart(file); bindex < bstart; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, bstart); ++ ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbend_dir(file, btail); ++ ++ flags = vfsub_file_flags(file); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ h_file = au_hf_dir(file, bindex); ++ if (h_file) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; /* close all? */ ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ err = 0; ++ ++out: ++ return err; ++} ++ ++static int do_open_dir(struct file *file, int flags, struct file *h_file) ++{ ++ int err; ++ aufs_bindex_t bindex, btail; ++ struct dentry *dentry, *h_dentry; ++ ++ FiMustWriteLock(file); ++ AuDebugOn(h_file); ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ file->f_version = d_inode(dentry)->i_version; ++ bindex = au_dbstart(dentry); ++ au_set_fbstart(file, bindex); ++ btail = au_dbtaildir(dentry); ++ au_set_fbend_dir(file, btail); ++ for (; !err && bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ break; ++ } ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ if (!err) ++ return 0; /* success */ ++ ++ /* close all */ ++ for (bindex = au_fbstart(file); bindex <= btail; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, -1); ++ au_set_fbend_dir(file, -1); ++ ++ return err; ++} ++ ++static int aufs_open_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_fidir *fidir; ++ ++ err = -ENOMEM; ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ fidir = au_fidir_alloc(sb); ++ if (fidir) { ++ struct au_do_open_args args = { ++ .open = do_open_dir, ++ .fidir = fidir ++ }; ++ err = au_do_open(file, &args); ++ if (unlikely(err)) ++ kfree(fidir); ++ } ++ si_read_unlock(sb); ++ return err; ++} ++ ++static int aufs_release_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ struct au_vdir *vdir_cache; ++ struct au_finfo *finfo; ++ struct au_fidir *fidir; ++ aufs_bindex_t bindex, bend; ++ ++ finfo = au_fi(file); ++ fidir = finfo->fi_hdir; ++ if (fidir) { ++ au_sphl_del(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ vdir_cache = fidir->fd_vdir_cache; /* lock-free */ ++ if (vdir_cache) ++ au_vdir_free(vdir_cache); ++ ++ bindex = finfo->fi_btop; ++ if (bindex >= 0) { ++ /* ++ * calls fput() instead of filp_close(), ++ * since no dnotify or lock for the lower file. ++ */ ++ bend = fidir->fd_bbot; ++ for (; bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ } ++ kfree(fidir); ++ finfo->fi_hdir = NULL; ++ } ++ au_finfo_fin(file); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_flush_dir(struct file *file, fl_owner_t id) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct file *h_file; ++ ++ err = 0; ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (h_file) ++ err = vfsub_flush(h_file, id); ++ } ++ return err; ++} ++ ++static int aufs_flush_dir(struct file *file, fl_owner_t id) ++{ ++ return au_do_flush(file, id, au_do_flush_dir); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { ++ struct path h_path; ++ ++ if (au_test_ro(sb, bindex, inode)) ++ continue; ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ if (!h_path.dentry) ++ continue; ++ ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_fsync(NULL, &h_path, datasync); ++ } ++ ++ return err; ++} ++ ++static int au_do_fsync_dir(struct file *file, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct file *h_file; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (!h_file || au_test_ro(sb, bindex, inode)) ++ continue; ++ ++ err = vfsub_fsync(h_file, &h_file->f_path, datasync); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * @file may be NULL ++ */ ++static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, ++ int datasync) ++{ ++ int err; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ struct mutex *mtx; ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ inode = d_inode(dentry); ++ mtx = &inode->i_mutex; ++ mutex_lock(mtx); ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (file) ++ err = au_do_fsync_dir(file, datasync); ++ else { ++ di_write_lock_child(dentry); ++ err = au_do_fsync_dir_no_file(dentry, datasync); ++ } ++ au_cpup_attr_timesizes(inode); ++ di_write_unlock(dentry); ++ if (file) ++ fi_write_unlock(file); ++ ++ si_read_unlock(sb); ++ mutex_unlock(mtx); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_iterate(struct file *file, struct dir_context *ctx) ++{ ++ int err; ++ struct dentry *dentry; ++ struct inode *inode, *h_inode; ++ struct super_block *sb; ++ ++ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); ++ ++ dentry = file->f_path.dentry; ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ err = au_alive_dir(dentry); ++ if (!err) ++ err = au_vdir_init(file); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ if (!au_test_nfsd()) { ++ err = au_vdir_fill_de(file, ctx); ++ fsstack_copy_attr_atime(inode, h_inode); ++ } else { ++ /* ++ * nfsd filldir may call lookup_one_len(), vfs_getattr(), ++ * encode_fh() and others. ++ */ ++ atomic_inc(&h_inode->i_count); ++ di_read_unlock(dentry, AuLock_IR); ++ si_read_unlock(sb); ++ err = au_vdir_fill_de(file, ctx); ++ fsstack_copy_attr_atime(inode, h_inode); ++ fi_write_unlock(file); ++ iput(h_inode); ++ ++ AuTraceErr(err); ++ return err; ++ } ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuTestEmpty_WHONLY 1 ++#define AuTestEmpty_CALLED (1 << 1) ++#define AuTestEmpty_SHWH (1 << 2) ++#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) ++#define au_fset_testempty(flags, name) \ ++ do { (flags) |= AuTestEmpty_##name; } while (0) ++#define au_fclr_testempty(flags, name) \ ++ do { (flags) &= ~AuTestEmpty_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuTestEmpty_SHWH ++#define AuTestEmpty_SHWH 0 ++#endif ++ ++struct test_empty_arg { ++ struct dir_context ctx; ++ struct au_nhash *whlist; ++ unsigned int flags; ++ int err; ++ aufs_bindex_t bindex; ++}; ++ ++static int test_empty_cb(struct dir_context *ctx, const char *__name, ++ int namelen, loff_t offset __maybe_unused, u64 ino, ++ unsigned int d_type) ++{ ++ struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, ++ ctx); ++ char *name = (void *)__name; ++ ++ arg->err = 0; ++ au_fset_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (name[0] == '.' ++ && (namelen == 1 || (name[1] == '.' && namelen == 2))) ++ goto out; /* success */ ++ ++ if (namelen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (au_ftest_testempty(arg->flags, WHONLY) ++ && !au_nhash_test_known_wh(arg->whlist, name, namelen)) ++ arg->err = -ENOTEMPTY; ++ goto out; ++ } ++ ++ name += AUFS_WH_PFX_LEN; ++ namelen -= AUFS_WH_PFX_LEN; ++ if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) ++ arg->err = au_nhash_append_wh ++ (arg->whlist, name, namelen, ino, d_type, arg->bindex, ++ au_ftest_testempty(arg->flags, SHWH)); ++ ++out: ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err; ++ struct file *h_file; ++ ++ h_file = au_h_open(dentry, arg->bindex, ++ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, ++ /*file*/NULL, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = 0; ++ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) ++ && !file_inode(h_file)->i_nlink) ++ goto out_put; ++ ++ do { ++ arg->err = 0; ++ au_fclr_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(h_file, &arg->ctx); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_testempty(arg->flags, CALLED)); ++ ++out_put: ++ fput(h_file); ++ au_sbr_put(dentry->d_sb, arg->bindex); ++out: ++ return err; ++} ++ ++struct do_test_empty_args { ++ int *errp; ++ struct dentry *dentry; ++ struct test_empty_arg *arg; ++}; ++ ++static void call_do_test_empty(void *args) ++{ ++ struct do_test_empty_args *a = args; ++ *a->errp = do_test_empty(a->dentry, a->arg); ++} ++ ++static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err, wkq_err; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, arg->bindex); ++ h_inode = d_inode(h_dentry); ++ /* todo: i_mode changes anytime? */ ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); ++ mutex_unlock(&h_inode->i_mutex); ++ if (!err) ++ err = do_test_empty(dentry, arg); ++ else { ++ struct do_test_empty_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .arg = arg ++ }; ++ unsigned int flags = arg->flags; ++ ++ wkq_err = au_wkq_wait(call_do_test_empty, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ arg->flags = flags; ++ } ++ ++ return err; ++} ++ ++int au_test_empty_lower(struct dentry *dentry) ++{ ++ int err; ++ unsigned int rdhash; ++ aufs_bindex_t bindex, bstart, btail; ++ struct au_nhash whlist; ++ struct test_empty_arg arg = { ++ .ctx = { ++ .actor = test_empty_cb ++ } ++ }; ++ int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); ++ ++ SiMustAnyLock(dentry->d_sb); ++ ++ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); ++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ arg.flags = 0; ++ arg.whlist = &whlist; ++ bstart = au_dbstart(dentry); ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ test_empty = do_test_empty; ++ if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) ++ test_empty = sio_test_empty; ++ arg.bindex = bstart; ++ err = test_empty(dentry, &arg); ++ if (unlikely(err)) ++ goto out_whlist; ++ ++ au_fset_testempty(arg.flags, WHONLY); ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && d_is_positive(h_dentry)) { ++ arg.bindex = bindex; ++ err = test_empty(dentry, &arg); ++ } ++ } ++ ++out_whlist: ++ au_nhash_wh_free(&whlist); ++out: ++ return err; ++} ++ ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ int err; ++ struct test_empty_arg arg = { ++ .ctx = { ++ .actor = test_empty_cb ++ } ++ }; ++ aufs_bindex_t bindex, btail; ++ ++ err = 0; ++ arg.whlist = whlist; ++ arg.flags = AuTestEmpty_WHONLY; ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && d_is_positive(h_dentry)) { ++ arg.bindex = bindex; ++ err = sio_test_empty(dentry, &arg); ++ } ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_dir_fop = { ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++ .read = generic_read_dir, ++ .iterate = aufs_iterate, ++ .unlocked_ioctl = aufs_ioctl_dir, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = aufs_compat_ioctl_dir, ++#endif ++ .open = aufs_open_dir, ++ .release = aufs_release_dir, ++ .flush = aufs_flush_dir, ++ .fsync = aufs_fsync_dir ++}; +diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h +new file mode 100644 +index 0000000..803a0f1 +--- /dev/null ++++ b/fs/aufs/dir.h +@@ -0,0 +1,118 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#ifndef __AUFS_DIR_H__ ++#define __AUFS_DIR_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* need to be faster and smaller */ ++ ++struct au_nhash { ++ unsigned int nh_num; ++ struct hlist_head *nh_head; ++}; ++ ++struct au_vdir_destr { ++ unsigned char len; ++ unsigned char name[0]; ++} __packed; ++ ++struct au_vdir_dehstr { ++ struct hlist_node hash; ++ struct au_vdir_destr *str; ++} ____cacheline_aligned_in_smp; ++ ++struct au_vdir_de { ++ ino_t de_ino; ++ unsigned char de_type; ++ /* caution: packed */ ++ struct au_vdir_destr de_str; ++} __packed; ++ ++struct au_vdir_wh { ++ struct hlist_node wh_hash; ++#ifdef CONFIG_AUFS_SHWH ++ ino_t wh_ino; ++ aufs_bindex_t wh_bindex; ++ unsigned char wh_type; ++#else ++ aufs_bindex_t wh_bindex; ++#endif ++ /* caution: packed */ ++ struct au_vdir_destr wh_str; ++} __packed; ++ ++union au_vdir_deblk_p { ++ unsigned char *deblk; ++ struct au_vdir_de *de; ++}; ++ ++struct au_vdir { ++ unsigned char **vd_deblk; ++ unsigned long vd_nblk; ++ struct { ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ } vd_last; ++ ++ unsigned long vd_version; ++ unsigned int vd_deblk_sz; ++ unsigned long vd_jiffy; ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dir.c */ ++extern const struct file_operations aufs_dir_fop; ++void au_add_nlink(struct inode *dir, struct inode *h_dir); ++void au_sub_nlink(struct inode *dir, struct inode *h_dir); ++loff_t au_dir_size(struct file *file, struct dentry *dentry); ++void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); ++int au_test_empty_lower(struct dentry *dentry); ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); ++ ++/* vdir.c */ ++unsigned int au_rdhash_est(loff_t sz); ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); ++void au_nhash_wh_free(struct au_nhash *whlist); ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit); ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh); ++void au_vdir_free(struct au_vdir *vdir); ++int au_vdir_init(struct file *file); ++int au_vdir_fill_de(struct file *file, struct dir_context *ctx); ++ ++/* ioctl.c */ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); ++ ++#ifdef CONFIG_AUFS_RDU ++/* rdu.c */ ++long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#endif ++#else ++AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, ++ unsigned int cmd, unsigned long arg) ++#ifdef CONFIG_COMPAT ++AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, ++ unsigned int cmd, unsigned long arg) ++#endif ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DIR_H__ */ +diff --git a/fs/aufs/dynop.c b/fs/aufs/dynop.c +new file mode 100644 +index 0000000..1f12838 +--- /dev/null ++++ b/fs/aufs/dynop.c +@@ -0,0 +1,356 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * dynamically customizable operations for regular files ++ */ ++ ++#include "aufs.h" ++ ++#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) ++ ++/* ++ * How large will these lists be? ++ * Usually just a few elements, 20-30 at most for each, I guess. ++ */ ++static struct au_splhead dynop[AuDyLast]; ++ ++static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) ++{ ++ struct au_dykey *key, *tmp; ++ struct list_head *head; ++ ++ key = NULL; ++ head = &spl->head; ++ rcu_read_lock(); ++ list_for_each_entry_rcu(tmp, head, dk_list) ++ if (tmp->dk_op.dy_hop == h_op) { ++ key = tmp; ++ kref_get(&key->dk_kref); ++ break; ++ } ++ rcu_read_unlock(); ++ ++ return key; ++} ++ ++static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) ++{ ++ struct au_dykey **k, *found; ++ const void *h_op = key->dk_op.dy_hop; ++ int i; ++ ++ found = NULL; ++ k = br->br_dykey; ++ for (i = 0; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else ++ break; ++ if (!found) { ++ spin_lock(&br->br_dykey_lock); ++ for (; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else { ++ k[i] = key; ++ break; ++ } ++ spin_unlock(&br->br_dykey_lock); ++ BUG_ON(i == AuBrDynOp); /* expand the array */ ++ } ++ ++ return found; ++} ++ ++/* kref_get() if @key is already added */ ++static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) ++{ ++ struct au_dykey *tmp, *found; ++ struct list_head *head; ++ const void *h_op = key->dk_op.dy_hop; ++ ++ found = NULL; ++ head = &spl->head; ++ spin_lock(&spl->spin); ++ list_for_each_entry(tmp, head, dk_list) ++ if (tmp->dk_op.dy_hop == h_op) { ++ kref_get(&tmp->dk_kref); ++ found = tmp; ++ break; ++ } ++ if (!found) ++ list_add_rcu(&key->dk_list, head); ++ spin_unlock(&spl->spin); ++ ++ if (!found) ++ DyPrSym(key); ++ return found; ++} ++ ++static void dy_free_rcu(struct rcu_head *rcu) ++{ ++ struct au_dykey *key; ++ ++ key = container_of(rcu, struct au_dykey, dk_rcu); ++ DyPrSym(key); ++ kfree(key); ++} ++ ++static void dy_free(struct kref *kref) ++{ ++ struct au_dykey *key; ++ struct au_splhead *spl; ++ ++ key = container_of(kref, struct au_dykey, dk_kref); ++ spl = dynop + key->dk_op.dy_type; ++ au_spl_del_rcu(&key->dk_list, spl); ++ call_rcu(&key->dk_rcu, dy_free_rcu); ++} ++ ++void au_dy_put(struct au_dykey *key) ++{ ++ kref_put(&key->dk_kref, dy_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define DyDbgDeclare(cnt) unsigned int cnt = 0 ++#define DyDbgInc(cnt) do { cnt++; } while (0) ++#else ++#define DyDbgDeclare(cnt) do {} while (0) ++#define DyDbgInc(cnt) do {} while (0) ++#endif ++ ++#define DySet(func, dst, src, h_op, h_sb) do { \ ++ DyDbgInc(cnt); \ ++ if (h_op->func) { \ ++ if (src.func) \ ++ dst.func = src.func; \ ++ else \ ++ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ ++ } \ ++} while (0) ++ ++#define DySetForce(func, dst, src) do { \ ++ AuDebugOn(!src.func); \ ++ DyDbgInc(cnt); \ ++ dst.func = src.func; \ ++} while (0) ++ ++#define DySetAop(func) \ ++ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) ++#define DySetAopForce(func) \ ++ DySetForce(func, dyaop->da_op, aufs_aop) ++ ++static void dy_aop(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused) ++{ ++ struct au_dyaop *dyaop = (void *)key; ++ const struct address_space_operations *h_aop = h_op; ++ DyDbgDeclare(cnt); ++ ++ AuDbg("%s\n", au_sbtype(h_sb)); ++ ++ DySetAop(writepage); ++ DySetAopForce(readpage); /* force */ ++ DySetAop(writepages); ++ DySetAop(set_page_dirty); ++ DySetAop(readpages); ++ DySetAop(write_begin); ++ DySetAop(write_end); ++ DySetAop(bmap); ++ DySetAop(invalidatepage); ++ DySetAop(releasepage); ++ DySetAop(freepage); ++ /* this one will be changed according to an aufs mount option */ ++ DySetAop(direct_IO); ++ DySetAop(migratepage); ++ DySetAop(launder_page); ++ DySetAop(is_partially_uptodate); ++ DySetAop(is_dirty_writeback); ++ DySetAop(error_remove_page); ++ DySetAop(swap_activate); ++ DySetAop(swap_deactivate); ++ ++ DyDbgSize(cnt, *h_aop); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void dy_bug(struct kref *kref) ++{ ++ BUG(); ++} ++ ++static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) ++{ ++ struct au_dykey *key, *old; ++ struct au_splhead *spl; ++ struct op { ++ unsigned int sz; ++ void (*set)(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused); ++ }; ++ static const struct op a[] = { ++ [AuDy_AOP] = { ++ .sz = sizeof(struct au_dyaop), ++ .set = dy_aop ++ } ++ }; ++ const struct op *p; ++ ++ spl = dynop + op->dy_type; ++ key = dy_gfind_get(spl, op->dy_hop); ++ if (key) ++ goto out_add; /* success */ ++ ++ p = a + op->dy_type; ++ key = kzalloc(p->sz, GFP_NOFS); ++ if (unlikely(!key)) { ++ key = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ key->dk_op.dy_hop = op->dy_hop; ++ kref_init(&key->dk_kref); ++ p->set(key, op->dy_hop, au_br_sb(br)); ++ old = dy_gadd(spl, key); ++ if (old) { ++ kfree(key); ++ key = old; ++ } ++ ++out_add: ++ old = dy_bradd(br, key); ++ if (old) ++ /* its ref-count should never be zero here */ ++ kref_put(&key->dk_kref, dy_bug); ++out: ++ return key; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Aufs prohibits O_DIRECT by defaut even if the branch supports it. ++ * This behaviour is necessary to return an error from open(O_DIRECT) instead ++ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes ++ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. ++ * See the aufs manual in detail. ++ */ ++static void dy_adx(struct au_dyaop *dyaop, int do_dx) ++{ ++ if (!do_dx) ++ dyaop->da_op.direct_IO = NULL; ++ else ++ dyaop->da_op.direct_IO = aufs_aop.direct_IO; ++} ++ ++static struct au_dyaop *dy_aget(struct au_branch *br, ++ const struct address_space_operations *h_aop, ++ int do_dx) ++{ ++ struct au_dyaop *dyaop; ++ struct au_dynop op; ++ ++ op.dy_type = AuDy_AOP; ++ op.dy_haop = h_aop; ++ dyaop = (void *)dy_get(&op, br); ++ if (IS_ERR(dyaop)) ++ goto out; ++ dy_adx(dyaop, do_dx); ++ ++out: ++ return dyaop; ++} ++ ++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode) ++{ ++ int err, do_dx; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_dyaop *dyaop; ++ ++ AuDebugOn(!S_ISREG(h_inode->i_mode)); ++ IiMustWriteLock(inode); ++ ++ sb = inode->i_sb; ++ br = au_sbr(sb, bindex); ++ do_dx = !!au_opt_test(au_mntflags(sb), DIO); ++ dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); ++ err = PTR_ERR(dyaop); ++ if (IS_ERR(dyaop)) ++ /* unnecessary to call dy_fput() */ ++ goto out; ++ ++ err = 0; ++ inode->i_mapping->a_ops = &dyaop->da_op; ++ ++out: ++ return err; ++} ++ ++/* ++ * Is it safe to replace a_ops during the inode/file is in operation? ++ * Yes, I hope so. ++ */ ++int au_dy_irefresh(struct inode *inode) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct inode *h_inode; ++ ++ err = 0; ++ if (S_ISREG(inode->i_mode)) { ++ bstart = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bstart); ++ err = au_dy_iaop(inode, bstart, h_inode); ++ } ++ return err; ++} ++ ++void au_dy_arefresh(int do_dx) ++{ ++ struct au_splhead *spl; ++ struct list_head *head; ++ struct au_dykey *key; ++ ++ spl = dynop + AuDy_AOP; ++ head = &spl->head; ++ spin_lock(&spl->spin); ++ list_for_each_entry(key, head, dk_list) ++ dy_adx((void *)key, do_dx); ++ spin_unlock(&spl->spin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void __init au_dy_init(void) ++{ ++ int i; ++ ++ /* make sure that 'struct au_dykey *' can be any type */ ++ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); ++ ++ for (i = 0; i < AuDyLast; i++) ++ au_spl_init(dynop + i); ++} ++ ++void au_dy_fin(void) ++{ ++ int i; ++ ++ for (i = 0; i < AuDyLast; i++) ++ WARN_ON(!list_empty(&dynop[i].head)); ++} +diff --git a/fs/aufs/dynop.h b/fs/aufs/dynop.h +new file mode 100644 +index 0000000..6d739c1 +--- /dev/null ++++ b/fs/aufs/dynop.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * dynamically customizable operations (for regular files only) ++ */ ++ ++#ifndef __AUFS_DYNOP_H__ ++#define __AUFS_DYNOP_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++enum {AuDy_AOP, AuDyLast}; ++ ++struct au_dynop { ++ int dy_type; ++ union { ++ const void *dy_hop; ++ const struct address_space_operations *dy_haop; ++ }; ++}; ++ ++struct au_dykey { ++ union { ++ struct list_head dk_list; ++ struct rcu_head dk_rcu; ++ }; ++ struct au_dynop dk_op; ++ ++ /* ++ * during I am in the branch local array, kref is gotten. when the ++ * branch is removed, kref is put. ++ */ ++ struct kref dk_kref; ++}; ++ ++/* stop unioning since their sizes are very different from each other */ ++struct au_dyaop { ++ struct au_dykey da_key; ++ struct address_space_operations da_op; /* not const */ ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dynop.c */ ++struct au_branch; ++void au_dy_put(struct au_dykey *key); ++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode); ++int au_dy_irefresh(struct inode *inode); ++void au_dy_arefresh(int do_dio); ++ ++void __init au_dy_init(void); ++void au_dy_fin(void); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DYNOP_H__ */ +diff --git a/fs/aufs/export.c b/fs/aufs/export.c +new file mode 100644 +index 0000000..3026f39 +--- /dev/null ++++ b/fs/aufs/export.c +@@ -0,0 +1,819 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * export via nfs ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../fs/mount.h" ++#include "aufs.h" ++ ++union conv { ++#ifdef CONFIG_AUFS_INO_T_64 ++ __u32 a[2]; ++#else ++ __u32 a[1]; ++#endif ++ ino_t ino; ++}; ++ ++static ino_t decode_ino(__u32 *a) ++{ ++ union conv u; ++ ++ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); ++ u.a[0] = a[0]; ++#ifdef CONFIG_AUFS_INO_T_64 ++ u.a[1] = a[1]; ++#endif ++ return u.ino; ++} ++ ++static void encode_ino(__u32 *a, ino_t ino) ++{ ++ union conv u; ++ ++ u.ino = ino; ++ a[0] = u.a[0]; ++#ifdef CONFIG_AUFS_INO_T_64 ++ a[1] = u.a[1]; ++#endif ++} ++ ++/* NFS file handle */ ++enum { ++ Fh_br_id, ++ Fh_sigen, ++#ifdef CONFIG_AUFS_INO_T_64 ++ /* support 64bit inode number */ ++ Fh_ino1, ++ Fh_ino2, ++ Fh_dir_ino1, ++ Fh_dir_ino2, ++#else ++ Fh_ino1, ++ Fh_dir_ino1, ++#endif ++ Fh_igen, ++ Fh_h_type, ++ Fh_tail, ++ ++ Fh_ino = Fh_ino1, ++ Fh_dir_ino = Fh_dir_ino1 ++}; ++ ++static int au_test_anon(struct dentry *dentry) ++{ ++ /* note: read d_flags without d_lock */ ++ return !!(dentry->d_flags & DCACHE_DISCONNECTED); ++} ++ ++int au_test_nfsd(void) ++{ ++ int ret; ++ struct task_struct *tsk = current; ++ char comm[sizeof(tsk->comm)]; ++ ++ ret = 0; ++ if (tsk->flags & PF_KTHREAD) { ++ get_task_comm(comm, tsk); ++ ret = !strcmp(comm, "nfsd"); ++ } ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* inode generation external table */ ++ ++void au_xigen_inc(struct inode *inode) ++{ ++ loff_t pos; ++ ssize_t sz; ++ __u32 igen; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ sb = inode->i_sb; ++ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); ++ ++ sbinfo = au_sbi(sb); ++ pos = inode->i_ino; ++ pos *= sizeof(igen); ++ igen = inode->i_generation + 1; ++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, ++ sizeof(igen), &pos); ++ if (sz == sizeof(igen)) ++ return; /* success */ ++ ++ if (unlikely(sz >= 0)) ++ AuIOErr("xigen error (%zd)\n", sz); ++} ++ ++int au_xigen_new(struct inode *inode) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ err = 0; ++ /* todo: dirty, at mount time */ ++ if (inode->i_ino == AUFS_ROOT_INO) ++ goto out; ++ sb = inode->i_sb; ++ SiMustAnyLock(sb); ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ goto out; ++ ++ err = -EFBIG; ++ pos = inode->i_ino; ++ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { ++ AuIOErr1("too large i%lld\n", pos); ++ goto out; ++ } ++ pos *= sizeof(inode->i_generation); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ file = sbinfo->si_xigen; ++ BUG_ON(!file); ++ ++ if (vfsub_f_size_read(file) ++ < pos + sizeof(inode->i_generation)) { ++ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); ++ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ } else ++ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ if (sz == sizeof(inode->i_generation)) ++ goto out; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xigen error (%zd)\n", sz); ++ } ++ ++out: ++ return err; ++} ++ ++int au_xigen_set(struct super_block *sb, struct file *base) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xigen); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ err = 0; ++ if (sbinfo->si_xigen) ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = file; ++ ++out: ++ return err; ++} ++ ++void au_xigen_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_xigen) { ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino) ++{ ++ struct dentry *dentry, *d; ++ struct inode *inode; ++ unsigned int sigen; ++ ++ dentry = NULL; ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ dentry = ERR_PTR(-ESTALE); ++ sigen = au_sigen(sb); ++ if (unlikely(is_bad_inode(inode) ++ || IS_DEADDIR(inode) ++ || sigen != au_iigen(inode, NULL))) ++ goto out_iput; ++ ++ dentry = NULL; ++ if (!dir_ino || S_ISDIR(inode->i_mode)) ++ dentry = d_find_alias(inode); ++ else { ++ spin_lock(&inode->i_lock); ++ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { ++ spin_lock(&d->d_lock); ++ if (!au_test_anon(d) ++ && d_inode(d->d_parent)->i_ino == dir_ino) { ++ dentry = dget_dlock(d); ++ spin_unlock(&d->d_lock); ++ break; ++ } ++ spin_unlock(&d->d_lock); ++ } ++ spin_unlock(&inode->i_lock); ++ } ++ if (unlikely(dentry && au_digen_test(dentry, sigen))) { ++ /* need to refresh */ ++ dput(dentry); ++ dentry = NULL; ++ } ++ ++out_iput: ++ iput(inode); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: dirty? */ ++/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ ++ ++struct au_compare_mnt_args { ++ /* input */ ++ struct super_block *sb; ++ ++ /* output */ ++ struct vfsmount *mnt; ++}; ++ ++static int au_compare_mnt(struct vfsmount *mnt, void *arg) ++{ ++ struct au_compare_mnt_args *a = arg; ++ ++ if (mnt->mnt_sb != a->sb) ++ return 0; ++ a->mnt = mntget(mnt); ++ return 1; ++} ++ ++static struct vfsmount *au_mnt_get(struct super_block *sb) ++{ ++ int err; ++ struct path root; ++ struct au_compare_mnt_args args = { ++ .sb = sb ++ }; ++ ++ get_fs_root(current->fs, &root); ++ rcu_read_lock(); ++ err = iterate_mounts(au_compare_mnt, &args, root.mnt); ++ rcu_read_unlock(); ++ path_put(&root); ++ AuDebugOn(!err); ++ AuDebugOn(!args.mnt); ++ return args.mnt; ++} ++ ++struct au_nfsd_si_lock { ++ unsigned int sigen; ++ aufs_bindex_t bindex, br_id; ++ unsigned char force_lock; ++}; ++ ++static int si_nfsd_read_lock(struct super_block *sb, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ /* branch id may be wrapped around */ ++ err = 0; ++ bindex = au_br_index(sb, nsi_lock->br_id); ++ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) ++ goto out; /* success */ ++ ++ err = -ESTALE; ++ bindex = -1; ++ if (!nsi_lock->force_lock) ++ si_read_unlock(sb); ++ ++out: ++ nsi_lock->bindex = bindex; ++ return err; ++} ++ ++struct find_name_by_ino { ++ struct dir_context ctx; ++ int called, found; ++ ino_t ino; ++ char *name; ++ int namelen; ++}; ++ ++static int ++find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, ++ ctx); ++ ++ a->called++; ++ if (a->ino != ino) ++ return 0; ++ ++ memcpy(a->name, name, namelen); ++ a->namelen = namelen; ++ a->found = 1; ++ return 1; ++} ++ ++static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *parent; ++ struct file *file; ++ struct inode *dir; ++ struct find_name_by_ino arg = { ++ .ctx = { ++ .actor = find_name_by_ino ++ } ++ }; ++ int err; ++ ++ parent = path->dentry; ++ if (nsi_lock) ++ si_read_unlock(parent->d_sb); ++ file = vfsub_dentry_open(path, au_dir_roflags); ++ dentry = (void *)file; ++ if (IS_ERR(file)) ++ goto out; ++ ++ dentry = ERR_PTR(-ENOMEM); ++ arg.name = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!arg.name)) ++ goto out_file; ++ arg.ino = ino; ++ arg.found = 0; ++ do { ++ arg.called = 0; ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(file, &arg.ctx); ++ } while (!err && !arg.found && arg.called); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_name; ++ /* instead of ENOENT */ ++ dentry = ERR_PTR(-ESTALE); ++ if (!arg.found) ++ goto out_name; ++ ++ /* do not call vfsub_lkup_one() */ ++ dir = d_inode(parent); ++ mutex_lock(&dir->i_mutex); ++ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); ++ mutex_unlock(&dir->i_mutex); ++ AuTraceErrPtr(dentry); ++ if (IS_ERR(dentry)) ++ goto out_name; ++ AuDebugOn(au_test_anon(dentry)); ++ if (unlikely(d_really_is_negative(dentry))) { ++ dput(dentry); ++ dentry = ERR_PTR(-ENOENT); ++ } ++ ++out_name: ++ free_page((unsigned long)arg.name); ++out_file: ++ fput(file); ++out: ++ if (unlikely(nsi_lock ++ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry; ++ struct path path; ++ ++ if (dir_ino != AUFS_ROOT_INO) { ++ path.dentry = decode_by_ino(sb, dir_ino, 0); ++ dentry = path.dentry; ++ if (!path.dentry || IS_ERR(path.dentry)) ++ goto out; ++ AuDebugOn(au_test_anon(path.dentry)); ++ } else ++ path.dentry = dget(sb->s_root); ++ ++ path.mnt = au_mnt_get(sb); ++ dentry = au_lkup_by_ino(&path, ino, nsi_lock); ++ path_put(&path); ++ ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_acceptable(void *expv, struct dentry *dentry) ++{ ++ return 1; ++} ++ ++static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, ++ char *buf, int len, struct super_block *sb) ++{ ++ char *p; ++ int n; ++ struct path path; ++ ++ p = d_path(h_rootpath, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ n = strlen(p); ++ ++ path.mnt = h_rootpath->mnt; ++ path.dentry = h_parent; ++ p = d_path(&path, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p += n; ++ ++ path.mnt = au_mnt_get(sb); ++ path.dentry = sb->s_root; ++ p = d_path(&path, buf, len - strlen(p)); ++ mntput(path.mnt); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p[strlen(p)] = '/'; ++ ++out: ++ AuTraceErrPtr(p); ++ return p; ++} ++ ++static ++struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, ++ int fh_len, struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *h_parent, *root; ++ struct super_block *h_sb; ++ char *pathname, *p; ++ struct vfsmount *h_mnt; ++ struct au_branch *br; ++ int err; ++ struct path path; ++ ++ br = au_sbr(sb, nsi_lock->bindex); ++ h_mnt = au_br_mnt(br); ++ h_sb = h_mnt->mnt_sb; ++ /* todo: call lower fh_to_dentry()? fh_to_parent()? */ ++ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), ++ fh_len - Fh_tail, fh[Fh_h_type], ++ h_acceptable, /*context*/NULL); ++ dentry = h_parent; ++ if (unlikely(!h_parent || IS_ERR(h_parent))) { ++ AuWarn1("%s decode_fh failed, %ld\n", ++ au_sbtype(h_sb), PTR_ERR(h_parent)); ++ goto out; ++ } ++ dentry = NULL; ++ if (unlikely(au_test_anon(h_parent))) { ++ AuWarn1("%s decode_fh returned a disconnected dentry\n", ++ au_sbtype(h_sb)); ++ goto out_h_parent; ++ } ++ ++ dentry = ERR_PTR(-ENOMEM); ++ pathname = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!pathname)) ++ goto out_h_parent; ++ ++ root = sb->s_root; ++ path.mnt = h_mnt; ++ di_read_lock_parent(root, !AuLock_IR); ++ path.dentry = au_h_dptr(root, nsi_lock->bindex); ++ di_read_unlock(root, !AuLock_IR); ++ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); ++ dentry = (void *)p; ++ if (IS_ERR(p)) ++ goto out_pathname; ++ ++ si_read_unlock(sb); ++ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_relock; ++ ++ dentry = ERR_PTR(-ENOENT); ++ AuDebugOn(au_test_anon(path.dentry)); ++ if (unlikely(d_really_is_negative(path.dentry))) ++ goto out_path; ++ ++ if (ino != d_inode(path.dentry)->i_ino) ++ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); ++ else ++ dentry = dget(path.dentry); ++ ++out_path: ++ path_put(&path); ++out_relock: ++ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++out_pathname: ++ free_page((unsigned long)pathname); ++out_h_parent: ++ dput(h_parent); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry * ++aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ struct dentry *dentry; ++ __u32 *fh = fid->raw; ++ struct au_branch *br; ++ ino_t ino, dir_ino; ++ struct au_nfsd_si_lock nsi_lock = { ++ .force_lock = 0 ++ }; ++ ++ dentry = ERR_PTR(-ESTALE); ++ /* it should never happen, but the file handle is unreliable */ ++ if (unlikely(fh_len < Fh_tail)) ++ goto out; ++ nsi_lock.sigen = fh[Fh_sigen]; ++ nsi_lock.br_id = fh[Fh_br_id]; ++ ++ /* branch id may be wrapped around */ ++ br = NULL; ++ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) ++ goto out; ++ nsi_lock.force_lock = 1; ++ ++ /* is this inode still cached? */ ++ ino = decode_ino(fh + Fh_ino); ++ /* it should never happen */ ++ if (unlikely(ino == AUFS_ROOT_INO)) ++ goto out; ++ ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ dentry = decode_by_ino(sb, ino, dir_ino); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* is the parent dir cached? */ ++ br = au_sbr(sb, nsi_lock.bindex); ++ atomic_inc(&br->br_count); ++ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* lookup path */ ++ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (unlikely(!dentry)) ++ /* todo?: make it ESTALE */ ++ goto out_unlock; ++ ++accept: ++ if (!au_digen_test(dentry, au_sigen(sb)) ++ && d_inode(dentry)->i_generation == fh[Fh_igen]) ++ goto out_unlock; /* success */ ++ ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++out_unlock: ++ if (br) ++ atomic_dec(&br->br_count); ++ si_read_unlock(sb); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++#if 0 /* reserved for future use */ ++/* support subtreecheck option */ ++static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, ++ int fh_len, int fh_type) ++{ ++ struct dentry *parent; ++ __u32 *fh = fid->raw; ++ ino_t dir_ino; ++ ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ parent = decode_by_ino(sb, dir_ino, 0); ++ if (IS_ERR(parent)) ++ goto out; ++ if (!parent) ++ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), ++ dir_ino, fh, fh_len); ++ ++out: ++ AuTraceErrPtr(parent); ++ return parent; ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, ++ struct inode *dir) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb, *h_sb; ++ struct dentry *dentry, *parent, *h_parent; ++ struct inode *h_dir; ++ struct au_branch *br; ++ ++ err = -ENOSPC; ++ if (unlikely(*max_len <= Fh_tail)) { ++ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); ++ goto out; ++ } ++ ++ err = FILEID_ROOT; ++ if (inode->i_ino == AUFS_ROOT_INO) { ++ AuDebugOn(inode->i_ino != AUFS_ROOT_INO); ++ goto out; ++ } ++ ++ h_parent = NULL; ++ sb = inode->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH); ++ if (unlikely(err)) ++ goto out; ++ ++#ifdef CONFIG_AUFS_DEBUG ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ AuWarn1("NFS-exporting requires xino\n"); ++#endif ++ err = -EIO; ++ parent = NULL; ++ ii_read_lock_child(inode); ++ bindex = au_ibstart(inode); ++ if (!dir) { ++ dentry = d_find_any_alias(inode); ++ if (unlikely(!dentry)) ++ goto out_unlock; ++ AuDebugOn(au_test_anon(dentry)); ++ parent = dget_parent(dentry); ++ dput(dentry); ++ if (unlikely(!parent)) ++ goto out_unlock; ++ if (d_really_is_positive(parent)) ++ dir = d_inode(parent); ++ } ++ ++ ii_read_lock_parent(dir); ++ h_dir = au_h_iptr(dir, bindex); ++ ii_read_unlock(dir); ++ if (unlikely(!h_dir)) ++ goto out_parent; ++ h_parent = d_find_any_alias(h_dir); ++ if (unlikely(!h_parent)) ++ goto out_hparent; ++ ++ err = -EPERM; ++ br = au_sbr(sb, bindex); ++ h_sb = au_br_sb(br); ++ if (unlikely(!h_sb->s_export_op)) { ++ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); ++ goto out_hparent; ++ } ++ ++ fh[Fh_br_id] = br->br_id; ++ fh[Fh_sigen] = au_sigen(sb); ++ encode_ino(fh + Fh_ino, inode->i_ino); ++ encode_ino(fh + Fh_dir_ino, dir->i_ino); ++ fh[Fh_igen] = inode->i_generation; ++ ++ *max_len -= Fh_tail; ++ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), ++ max_len, ++ /*connectable or subtreecheck*/0); ++ err = fh[Fh_h_type]; ++ *max_len += Fh_tail; ++ /* todo: macros? */ ++ if (err != FILEID_INVALID) ++ err = 99; ++ else ++ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); ++ ++out_hparent: ++ dput(h_parent); ++out_parent: ++ dput(parent); ++out_unlock: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++out: ++ if (unlikely(err < 0)) ++ err = FILEID_INVALID; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_commit_metadata(struct inode *inode) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb; ++ struct inode *h_inode; ++ int (*f)(struct inode *inode); ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ii_write_lock_child(inode); ++ bindex = au_ibstart(inode); ++ AuDebugOn(bindex < 0); ++ h_inode = au_h_iptr(inode, bindex); ++ ++ f = h_inode->i_sb->s_export_op->commit_metadata; ++ if (f) ++ err = f(h_inode); ++ else { ++ struct writeback_control wbc = { ++ .sync_mode = WB_SYNC_ALL, ++ .nr_to_write = 0 /* metadata only */ ++ }; ++ ++ err = sync_inode(h_inode, &wbc); ++ } ++ ++ au_cpup_attr_timesizes(inode); ++ ii_write_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct export_operations aufs_export_op = { ++ .fh_to_dentry = aufs_fh_to_dentry, ++ /* .fh_to_parent = aufs_fh_to_parent, */ ++ .encode_fh = aufs_encode_fh, ++ .commit_metadata = aufs_commit_metadata ++}; ++ ++void au_export_init(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ __u32 u; ++ ++ sb->s_export_op = &aufs_export_op; ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xigen = NULL; ++ get_random_bytes(&u, sizeof(u)); ++ BUILD_BUG_ON(sizeof(u) != sizeof(int)); ++ atomic_set(&sbinfo->si_xigen_next, u); ++} +diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c +new file mode 100644 +index 0000000..91c2ce7 +--- /dev/null ++++ b/fs/aufs/f_op.c +@@ -0,0 +1,725 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * file and vm operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++int au_do_open_nondir(struct file *file, int flags, struct file *h_file) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ struct inode *h_inode; ++ ++ FiMustWriteLock(file); ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ AuDebugOn(IS_ERR_OR_NULL(dentry)); ++ finfo = au_fi(file); ++ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); ++ atomic_set(&finfo->fi_mmapped, 0); ++ bindex = au_dbstart(dentry); ++ if (!h_file) ++ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); ++ else ++ get_file(h_file); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ if ((flags & __O_TMPFILE) ++ && !(flags & O_EXCL)) { ++ h_inode = file_inode(h_file); ++ spin_lock(&h_inode->i_lock); ++ h_inode->i_state |= I_LINKABLE; ++ spin_unlock(&h_inode->i_lock); ++ } ++ au_set_fbstart(file, bindex); ++ au_set_h_fptr(file, bindex, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ } ++ ++ return err; ++} ++ ++static int aufs_open_nondir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_do_open_args args = { ++ .open = au_do_open_nondir ++ }; ++ ++ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", ++ file, vfsub_file_flags(file), file->f_mode); ++ ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_do_open(file, &args); ++ si_read_unlock(sb); ++ return err; ++} ++ ++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) ++{ ++ struct au_finfo *finfo; ++ aufs_bindex_t bindex; ++ ++ finfo = au_fi(file); ++ au_sphl_del(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ bindex = finfo->fi_btop; ++ if (bindex >= 0) ++ au_set_h_fptr(file, bindex, NULL); ++ ++ au_finfo_fin(file); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ int err; ++ struct file *h_file; ++ ++ err = 0; ++ h_file = au_hf_top(file); ++ if (h_file) ++ err = vfsub_flush(h_file, id); ++ return err; ++} ++ ++static int aufs_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ return au_do_flush(file, id, au_do_flush_nondir); ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * read and write functions acquire [fdi]_rwsem once, but release before ++ * mmap_sem. This is because to stop a race condition between mmap(2). ++ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping ++ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in ++ * read functions after [fdi]_rwsem are released, but it should be harmless. ++ */ ++ ++/* Callers should call au_read_post() or fput() in the end */ ++struct file *au_read_pre(struct file *file, int keep_fi) ++{ ++ struct file *h_file; ++ int err; ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (!err) { ++ di_read_unlock(file->f_path.dentry, AuLock_IR); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ if (!keep_fi) ++ fi_read_unlock(file); ++ } else ++ h_file = ERR_PTR(err); ++ ++ return h_file; ++} ++ ++static void au_read_post(struct inode *inode, struct file *h_file) ++{ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(inode, file_inode(h_file)); ++ fput(h_file); ++} ++ ++struct au_write_pre { ++ blkcnt_t blks; ++ aufs_bindex_t bstart; ++}; ++ ++/* ++ * return with iinfo is write-locked ++ * callers should call au_write_post() or iinfo_write_unlock() + fput() in the ++ * end ++ */ ++static struct file *au_write_pre(struct file *file, int do_ready, ++ struct au_write_pre *wpre) ++{ ++ struct file *h_file; ++ struct dentry *dentry; ++ int err; ++ struct au_pin pin; ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ h_file = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_path.dentry; ++ if (do_ready) { ++ err = au_ready_to_write(file, -1, &pin); ++ if (unlikely(err)) { ++ h_file = ERR_PTR(err); ++ di_write_unlock(dentry); ++ goto out_fi; ++ } ++ } ++ ++ di_downgrade_lock(dentry, /*flags*/0); ++ if (wpre) ++ wpre->bstart = au_fbstart(file); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ if (wpre) ++ wpre->blks = file_inode(h_file)->i_blocks; ++ if (do_ready) ++ au_unpin(&pin); ++ di_read_unlock(dentry, /*flags*/0); ++ ++out_fi: ++ fi_write_unlock(file); ++out: ++ return h_file; ++} ++ ++static void au_write_post(struct inode *inode, struct file *h_file, ++ struct au_write_pre *wpre, ssize_t written) ++{ ++ struct inode *h_inode; ++ ++ au_cpup_attr_timesizes(inode); ++ AuDebugOn(au_ibstart(inode) != wpre->bstart); ++ h_inode = file_inode(h_file); ++ inode->i_mode = h_inode->i_mode; ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++ /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */ ++ if (written > 0) ++ au_fhsm_wrote(inode->i_sb, wpre->bstart, ++ /*force*/h_inode->i_blocks > wpre->blks); ++} ++ ++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ struct inode *inode; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ /* filedata may be obsoleted by concurrent copyup, but no problem */ ++ err = vfsub_read_u(h_file, buf, count, ppos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_read_post(inode, h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ++ * todo: very ugly ++ * it locks both of i_mutex and si_rwsem for read in safe. ++ * if the plink maintenance mode continues forever (that is the problem), ++ * may loop forever. ++ */ ++static void au_mtx_and_read_lock(struct inode *inode) ++{ ++ int err; ++ struct super_block *sb = inode->i_sb; ++ ++ while (1) { ++ mutex_lock(&inode->i_mutex); ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (!err) ++ break; ++ mutex_unlock(&inode->i_mutex); ++ si_read_lock(sb, AuLock_NOPLMW); ++ si_read_unlock(sb); ++ } ++} ++ ++static ssize_t aufs_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ char __user *buf = (char __user *)ubuf; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = vfsub_write_u(h_file, buf, count, ppos); ++ au_write_post(inode, h_file, &wpre, err); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, ++ struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct file *file; ++ ssize_t (*iter)(struct kiocb *, struct iov_iter *); ++ ++ err = security_file_permission(h_file, rw); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -ENOSYS; ++ iter = NULL; ++ if (rw == MAY_READ) ++ iter = h_file->f_op->read_iter; ++ else if (rw == MAY_WRITE) ++ iter = h_file->f_op->write_iter; ++ ++ file = kio->ki_filp; ++ kio->ki_filp = h_file; ++ if (iter) { ++ lockdep_off(); ++ err = iter(kio, iov_iter); ++ lockdep_on(); ++ } else ++ /* currently there is no such fs */ ++ WARN_ON_ONCE(1); ++ kio->ki_filp = file; ++ ++out: ++ return err; ++} ++ ++static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct file *file, *h_file; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ file = kio->ki_filp; ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = au_do_iter(h_file, MAY_READ, kio, iov_iter); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_read_post(inode, h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *file, *h_file; ++ ++ file = kio->ki_filp; ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); ++ au_write_post(inode, h_file, &wpre, err); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ ssize_t err; ++ struct file *h_file; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/1); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ if (au_test_loopback_kthread()) { ++ au_warn_loopback(h_file->f_path.dentry->d_sb); ++ if (file->f_mapping != h_file->f_mapping) { ++ file->f_mapping = h_file->f_mapping; ++ smp_mb(); /* unnecessary? */ ++ } ++ } ++ fi_read_unlock(file); ++ ++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); ++ /* todo: necessasry? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_read_post(inode, h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t ++aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); ++ au_write_post(inode, h_file, &wpre, err); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static long aufs_fallocate(struct file *file, int mode, loff_t offset, ++ loff_t len) ++{ ++ long err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_fallocate(h_file, mode, offset, len); ++ lockdep_on(); ++ au_write_post(inode, h_file, &wpre, /*written*/1); ++ ++out: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * The locking order around current->mmap_sem. ++ * - in most and regular cases ++ * file I/O syscall -- aufs_read() or something ++ * -- si_rwsem for read -- mmap_sem ++ * (Note that [fdi]i_rwsem are released before mmap_sem). ++ * - in mmap case ++ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem ++ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for ++ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in ++ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. ++ * It means that when aufs acquires si_rwsem for write, the process should never ++ * acquire mmap_sem. ++ * ++ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a ++ * problem either since any directory is not able to be mmap-ed. ++ * The similar scenario is applied to aufs_readlink() too. ++ */ ++ ++#if 0 /* stop calling security_file_mmap() */ ++/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ ++#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) ++ ++static unsigned long au_arch_prot_conv(unsigned long flags) ++{ ++ /* currently ppc64 only */ ++#ifdef CONFIG_PPC64 ++ /* cf. linux/arch/powerpc/include/asm/mman.h */ ++ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); ++ return AuConv_VM_PROT(flags, SAO); ++#else ++ AuDebugOn(arch_calc_vm_prot_bits(-1)); ++ return 0; ++#endif ++} ++ ++static unsigned long au_prot_conv(unsigned long flags) ++{ ++ return AuConv_VM_PROT(flags, READ) ++ | AuConv_VM_PROT(flags, WRITE) ++ | AuConv_VM_PROT(flags, EXEC) ++ | au_arch_prot_conv(flags); ++} ++ ++/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ ++#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) ++ ++static unsigned long au_flag_conv(unsigned long flags) ++{ ++ return AuConv_VM_MAP(flags, GROWSDOWN) ++ | AuConv_VM_MAP(flags, DENYWRITE) ++ | AuConv_VM_MAP(flags, LOCKED); ++} ++#endif ++ ++static int aufs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int err; ++ const unsigned char wlock ++ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); ++ struct super_block *sb; ++ struct file *h_file; ++ struct inode *inode; ++ ++ AuDbgVmRegion(file, vma); ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ lockdep_off(); ++ si_read_lock(sb, AuLock_NOPLMW); ++ ++ h_file = au_write_pre(file, wlock, /*wpre*/NULL); ++ lockdep_on(); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = 0; ++ au_set_mmapped(file); ++ au_vm_file_reset(vma, h_file); ++ /* ++ * we cannot call security_mmap_file() here since it may acquire ++ * mmap_sem or i_mutex. ++ * ++ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), ++ * au_flag_conv(vma->vm_flags)); ++ */ ++ if (!err) ++ err = h_file->f_op->mmap(h_file, vma); ++ if (!err) { ++ au_vm_prfile_set(vma, file); ++ fsstack_copy_attr_atime(inode, file_inode(h_file)); ++ goto out_fput; /* success */ ++ } ++ au_unset_mmapped(file); ++ au_vm_file_reset(vma, file); ++ ++out_fput: ++ lockdep_off(); ++ ii_write_unlock(inode); ++ lockdep_on(); ++ fput(h_file); ++out: ++ lockdep_off(); ++ si_read_unlock(sb); ++ lockdep_on(); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, ++ int datasync) ++{ ++ int err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *h_file; ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out; ++ ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out_unlock; ++ ++ err = vfsub_fsync(h_file, &h_file->f_path, datasync); ++ au_write_post(inode, h_file, &wpre, /*written*/0); ++ ++out_unlock: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++out: ++ return err; ++} ++ ++/* no one supports this operation, currently */ ++#if 0 ++static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) ++{ ++ int err; ++ struct au_write_pre wpre; ++ struct inode *inode; ++ struct file *file, *h_file; ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out; ++ ++ file = kio->ki_filp; ++ inode = file_inode(file); ++ au_mtx_and_read_lock(inode); ++ ++ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out_unlock; ++ ++ err = -ENOSYS; ++ h_file = au_hf_top(file); ++ if (h_file->f_op->aio_fsync) { ++ struct mutex *h_mtx; ++ ++ h_mtx = &file_inode(h_file)->i_mutex; ++ if (!is_sync_kiocb(kio)) { ++ get_file(h_file); ++ fput(file); ++ } ++ kio->ki_filp = h_file; ++ err = h_file->f_op->aio_fsync(kio, datasync); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ if (!err) ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ mutex_unlock(h_mtx); ++ } ++ au_write_post(inode, h_file, &wpre, /*written*/0); ++ ++out_unlock: ++ si_read_unlock(inode->sb); ++ mutex_unlock(&inode->i_mutex); ++out: ++ return err; ++} ++#endif ++ ++static int aufs_fasync(int fd, struct file *file, int flag) ++{ ++ int err; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ if (h_file->f_op->fasync) ++ err = h_file->f_op->fasync(fd, h_file, flag); ++ fput(h_file); /* instead of au_read_post() */ ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* no one supports this operation, currently */ ++#if 0 ++static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, ++ size_t len, loff_t *pos, int more) ++{ ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_file_fop = { ++ .owner = THIS_MODULE, ++ ++ .llseek = default_llseek, ++ ++ .read = aufs_read, ++ .write = aufs_write, ++ .read_iter = aufs_read_iter, ++ .write_iter = aufs_write_iter, ++ ++#ifdef CONFIG_AUFS_POLL ++ .poll = aufs_poll, ++#endif ++ .unlocked_ioctl = aufs_ioctl_nondir, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = aufs_compat_ioctl_nondir, ++#endif ++ .mmap = aufs_mmap, ++ .open = aufs_open_nondir, ++ .flush = aufs_flush_nondir, ++ .release = aufs_release_nondir, ++ .fsync = aufs_fsync_nondir, ++ /* .aio_fsync = aufs_aio_fsync_nondir, */ ++ .fasync = aufs_fasync, ++ /* .sendpage = aufs_sendpage, */ ++ .splice_write = aufs_splice_write, ++ .splice_read = aufs_splice_read, ++#if 0 ++ .aio_splice_write = aufs_aio_splice_write, ++ .aio_splice_read = aufs_aio_splice_read, ++#endif ++ .fallocate = aufs_fallocate ++}; +diff --git a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c +new file mode 100644 +index 0000000..47106cb +--- /dev/null ++++ b/fs/aufs/fhsm.c +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (C) 2011-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * File-based Hierarchy Storage Management ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ AuDebugOn(!fhsm); ++ return fhsm->fhsm_bottom; ++} ++ ++void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ AuDebugOn(!fhsm); ++ fhsm->fhsm_bottom = bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) ++{ ++ struct au_br_fhsm *bf; ++ ++ bf = br->br_fhsm; ++ MtxMustLock(&bf->bf_lock); ++ ++ return !bf->bf_readable ++ || time_after(jiffies, ++ bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_fhsm_notify(struct super_block *sb, int val) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ if (au_fhsm_pid(fhsm) ++ && atomic_read(&fhsm->fhsm_readable) != -1) { ++ atomic_set(&fhsm->fhsm_readable, val); ++ if (val) ++ wake_up(&fhsm->fhsm_wqh); ++ } ++} ++ ++static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, ++ struct aufs_stfs *rstfs, int do_lock, int do_notify) ++{ ++ int err; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ br = au_sbr(sb, bindex); ++ AuDebugOn(au_br_rdonly(br)); ++ bf = br->br_fhsm; ++ AuDebugOn(!bf); ++ ++ if (do_lock) ++ mutex_lock(&bf->bf_lock); ++ else ++ MtxMustLock(&bf->bf_lock); ++ ++ /* sb->s_root for NFS is unreliable */ ++ err = au_br_stfs(br, &bf->bf_stfs); ++ if (unlikely(err)) { ++ AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); ++ goto out; ++ } ++ ++ bf->bf_jiffy = jiffies; ++ bf->bf_readable = 1; ++ if (do_notify) ++ au_fhsm_notify(sb, /*val*/1); ++ if (rstfs) ++ *rstfs = bf->bf_stfs; ++ ++out: ++ if (do_lock) ++ mutex_unlock(&bf->bf_lock); ++ au_fhsm_notify(sb, /*val*/1); ++ ++ return err; ++} ++ ++void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ AuDbg("b%d, force %d\n", bindex, force); ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ if (!au_ftest_si(sbinfo, FHSM) ++ || fhsm->fhsm_bottom == bindex) ++ return; ++ ++ br = au_sbr(sb, bindex); ++ bf = br->br_fhsm; ++ AuDebugOn(!bf); ++ mutex_lock(&bf->bf_lock); ++ if (force ++ || au_fhsm_pid(fhsm) ++ || au_fhsm_test_jiffy(sbinfo, br)) ++ err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, ++ /*do_notify*/1); ++ mutex_unlock(&bf->bf_lock); ++} ++ ++void au_fhsm_wrote_all(struct super_block *sb, int force) ++{ ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ /* exclude the bottom */ ++ bend = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm)) ++ au_fhsm_wrote(sb, bindex, force); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static unsigned int au_fhsm_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ unsigned int mask; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ mask = 0; ++ sbinfo = file->private_data; ++ fhsm = &sbinfo->si_fhsm; ++ poll_wait(file, &fhsm->fhsm_wqh, wait); ++ if (atomic_read(&fhsm->fhsm_readable)) ++ mask = POLLIN /* | POLLRDNORM */; ++ ++ AuTraceErr((int)mask); ++ return mask; ++} ++ ++static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, ++ struct aufs_stfs *stfs, __s16 brid) ++{ ++ int err; ++ ++ err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); ++ if (!err) ++ err = __put_user(brid, &stbr->brid); ++ if (unlikely(err)) ++ err = -EFAULT; ++ ++ return err; ++} ++ ++static ssize_t au_fhsm_do_read(struct super_block *sb, ++ struct aufs_stbr __user *stbr, size_t count) ++{ ++ ssize_t err; ++ int nstbr; ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ /* except the bottom branch */ ++ err = 0; ++ nstbr = 0; ++ bend = au_fhsm_bottom(sb); ++ for (bindex = 0; !err && bindex < bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!au_br_fhsm(br->br_perm)) ++ continue; ++ ++ bf = br->br_fhsm; ++ mutex_lock(&bf->bf_lock); ++ if (bf->bf_readable) { ++ err = -EFAULT; ++ if (count >= sizeof(*stbr)) ++ err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, ++ br->br_id); ++ if (!err) { ++ bf->bf_readable = 0; ++ count -= sizeof(*stbr); ++ nstbr++; ++ } ++ } ++ mutex_unlock(&bf->bf_lock); ++ } ++ if (!err) ++ err = sizeof(*stbr) * nstbr; ++ ++ return err; ++} ++ ++static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, ++ loff_t *pos) ++{ ++ ssize_t err; ++ int readable; ++ aufs_bindex_t nfhsm, bindex, bend; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ struct au_branch *br; ++ struct super_block *sb; ++ ++ err = 0; ++ sbinfo = file->private_data; ++ fhsm = &sbinfo->si_fhsm; ++need_data: ++ spin_lock_irq(&fhsm->fhsm_wqh.lock); ++ if (!atomic_read(&fhsm->fhsm_readable)) { ++ if (vfsub_file_flags(file) & O_NONBLOCK) ++ err = -EAGAIN; ++ else ++ err = wait_event_interruptible_locked_irq ++ (fhsm->fhsm_wqh, ++ atomic_read(&fhsm->fhsm_readable)); ++ } ++ spin_unlock_irq(&fhsm->fhsm_wqh.lock); ++ if (unlikely(err)) ++ goto out; ++ ++ /* sb may already be dead */ ++ au_rw_read_lock(&sbinfo->si_rwsem); ++ readable = atomic_read(&fhsm->fhsm_readable); ++ if (readable > 0) { ++ sb = sbinfo->si_sb; ++ AuDebugOn(!sb); ++ /* exclude the bottom branch */ ++ nfhsm = 0; ++ bend = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm)) ++ nfhsm++; ++ } ++ err = -EMSGSIZE; ++ if (nfhsm * sizeof(struct aufs_stbr) <= count) { ++ atomic_set(&fhsm->fhsm_readable, 0); ++ err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, ++ count); ++ } ++ } ++ au_rw_read_unlock(&sbinfo->si_rwsem); ++ if (!readable) ++ goto need_data; ++ ++out: ++ return err; ++} ++ ++static int au_fhsm_release(struct inode *inode, struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ /* sb may already be dead */ ++ sbinfo = file->private_data; ++ fhsm = &sbinfo->si_fhsm; ++ spin_lock(&fhsm->fhsm_spin); ++ fhsm->fhsm_pid = 0; ++ spin_unlock(&fhsm->fhsm_spin); ++ kobject_put(&sbinfo->si_kobj); ++ ++ return 0; ++} ++ ++static const struct file_operations au_fhsm_fops = { ++ .owner = THIS_MODULE, ++ .llseek = noop_llseek, ++ .read = au_fhsm_read, ++ .poll = au_fhsm_poll, ++ .release = au_fhsm_release ++}; ++ ++int au_fhsm_fd(struct super_block *sb, int oflags) ++{ ++ int err, fd; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ err = -EPERM; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) ++ goto out; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ spin_lock(&fhsm->fhsm_spin); ++ if (!fhsm->fhsm_pid) ++ fhsm->fhsm_pid = current->pid; ++ else ++ err = -EBUSY; ++ spin_unlock(&fhsm->fhsm_spin); ++ if (unlikely(err)) ++ goto out; ++ ++ oflags |= O_RDONLY; ++ /* oflags |= FMODE_NONOTIFY; */ ++ fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); ++ err = fd; ++ if (unlikely(fd < 0)) ++ goto out_pid; ++ ++ /* succeed reglardless 'fhsm' status */ ++ kobject_get(&sbinfo->si_kobj); ++ si_noflush_read_lock(sb); ++ if (au_ftest_si(sbinfo, FHSM)) ++ au_fhsm_wrote_all(sb, /*force*/0); ++ si_read_unlock(sb); ++ goto out; /* success */ ++ ++out_pid: ++ spin_lock(&fhsm->fhsm_spin); ++ fhsm->fhsm_pid = 0; ++ spin_unlock(&fhsm->fhsm_spin); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_fhsm_br_alloc(struct au_branch *br) ++{ ++ int err; ++ ++ err = 0; ++ br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); ++ if (br->br_fhsm) ++ au_br_fhsm_init(br->br_fhsm); ++ else ++ err = -ENOMEM; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_fhsm_fin(struct super_block *sb) ++{ ++ au_fhsm_notify(sb, /*val*/-1); ++} ++ ++void au_fhsm_init(struct au_sbinfo *sbinfo) ++{ ++ struct au_fhsm *fhsm; ++ ++ fhsm = &sbinfo->si_fhsm; ++ spin_lock_init(&fhsm->fhsm_spin); ++ init_waitqueue_head(&fhsm->fhsm_wqh); ++ atomic_set(&fhsm->fhsm_readable, 0); ++ fhsm->fhsm_expire ++ = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); ++ fhsm->fhsm_bottom = -1; ++} ++ ++void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) ++{ ++ sbinfo->si_fhsm.fhsm_expire ++ = msecs_to_jiffies(sec * MSEC_PER_SEC); ++} ++ ++void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) ++{ ++ unsigned int u; ++ ++ if (!au_ftest_si(sbinfo, FHSM)) ++ return; ++ ++ u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; ++ if (u != AUFS_FHSM_CACHE_DEF_SEC) ++ seq_printf(seq, ",fhsm_sec=%u", u); ++} +diff --git a/fs/aufs/file.c b/fs/aufs/file.c +new file mode 100644 +index 0000000..8b17fba +--- /dev/null ++++ b/fs/aufs/file.c +@@ -0,0 +1,828 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * handling file/dir, and address_space operation ++ */ ++ ++#ifdef CONFIG_AUFS_DEBUG ++#include ++#endif ++#include ++#include "aufs.h" ++ ++/* drop flags for writing */ ++unsigned int au_file_roflags(unsigned int flags) ++{ ++ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); ++ flags |= O_RDONLY | O_NOATIME; ++ return flags; ++} ++ ++/* common functions to regular file and dir */ ++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, ++ struct file *file, int force_wr) ++{ ++ struct file *h_file; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct path h_path; ++ int err; ++ ++ /* a race condition can happen between open and unlink/rmdir */ ++ h_file = ERR_PTR(-ENOENT); ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (au_test_nfsd() && (!h_dentry || d_is_negative(h_dentry))) ++ goto out; ++ h_inode = d_inode(h_dentry); ++ spin_lock(&h_dentry->d_lock); ++ err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) ++ /* || !d_inode(dentry)->i_nlink */ ++ ; ++ spin_unlock(&h_dentry->d_lock); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ err = au_br_test_oflag(flags, br); ++ h_file = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ /* drop flags for writing */ ++ if (au_test_ro(sb, bindex, d_inode(dentry))) { ++ if (force_wr && !(flags & O_WRONLY)) ++ force_wr = 0; ++ flags = au_file_roflags(flags); ++ if (force_wr) { ++ h_file = ERR_PTR(-EROFS); ++ flags = au_file_roflags(flags); ++ if (unlikely(vfsub_native_ro(h_inode) ++ || IS_APPEND(h_inode))) ++ goto out; ++ flags &= ~O_ACCMODE; ++ flags |= O_WRONLY; ++ } ++ } ++ flags &= ~O_CREAT; ++ atomic_inc(&br->br_count); ++ h_path.dentry = h_dentry; ++ h_path.mnt = au_br_mnt(br); ++ h_file = vfsub_dentry_open(&h_path, flags); ++ if (IS_ERR(h_file)) ++ goto out_br; ++ ++ if (flags & __FMODE_EXEC) { ++ err = deny_write_access(h_file); ++ if (unlikely(err)) { ++ fput(h_file); ++ h_file = ERR_PTR(err); ++ goto out_br; ++ } ++ } ++ fsnotify_open(h_file); ++ goto out; /* success */ ++ ++out_br: ++ atomic_dec(&br->br_count); ++out: ++ return h_file; ++} ++ ++static int au_cmoo(struct dentry *dentry) ++{ ++ int err, cmoo; ++ unsigned int udba; ++ struct path h_path; ++ struct au_pin pin; ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = -1, ++ .bsrc = -1, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ struct inode *delegated; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ pid_t pid; ++ struct au_branch *br; ++ struct dentry *parent; ++ struct au_hinode *hdir; ++ ++ DiMustWriteLock(dentry); ++ IiMustWriteLock(d_inode(dentry)); ++ ++ err = 0; ++ if (IS_ROOT(dentry)) ++ goto out; ++ cpg.bsrc = au_dbstart(dentry); ++ if (!cpg.bsrc) ++ goto out; ++ ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ pid = au_fhsm_pid(fhsm); ++ if (pid ++ && (current->pid == pid ++ || current->real_parent->pid == pid)) ++ goto out; ++ ++ br = au_sbr(sb, cpg.bsrc); ++ cmoo = au_br_cmoo(br->br_perm); ++ if (!cmoo) ++ goto out; ++ if (!d_is_reg(dentry)) ++ cmoo &= AuBrAttr_COO_ALL; ++ if (!cmoo) ++ goto out; ++ ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); ++ cpg.bdst = err; ++ if (unlikely(err < 0)) { ++ err = 0; /* there is no upper writable branch */ ++ goto out_dgrade; ++ } ++ AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); ++ ++ /* do not respect the coo attrib for the target branch */ ++ err = au_cpup_dirs(dentry, cpg.bdst); ++ if (unlikely(err)) ++ goto out_dgrade; ++ ++ di_downgrade_lock(parent, AuLock_IR); ++ udba = au_opt_udba(sb); ++ err = au_pin(&pin, dentry, cpg.bdst, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ if (unlikely(err)) ++ goto out_parent; ++ if (!(cmoo & AuBrWAttr_MOO)) ++ goto out_parent; /* success */ ++ ++ err = au_pin(&pin, dentry, cpg.bsrc, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ h_path.mnt = au_br_mnt(br); ++ h_path.dentry = au_h_dptr(dentry, cpg.bsrc); ++ hdir = au_hi(d_inode(parent), cpg.bsrc); ++ delegated = NULL; ++ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); ++ au_unpin(&pin); ++ /* todo: keep h_dentry or not? */ ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) { ++ pr_err("unlink %pd after coo failed (%d), ignored\n", ++ dentry, err); ++ err = 0; ++ } ++ goto out_parent; /* success */ ++ ++out_dgrade: ++ di_downgrade_lock(parent, AuLock_IR); ++out_parent: ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_do_open(struct file *file, struct au_do_open_args *args) ++{ ++ int err, no_lock = args->no_lock; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ ++ if (!no_lock) ++ err = au_finfo_init(file, args->fidir); ++ else { ++ lockdep_off(); ++ err = au_finfo_init(file, args->fidir); ++ lockdep_on(); ++ } ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_path.dentry; ++ AuDebugOn(IS_ERR_OR_NULL(dentry)); ++ if (!no_lock) { ++ di_write_lock_child(dentry); ++ err = au_cmoo(dentry); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (!err) ++ err = args->open(file, vfsub_file_flags(file), NULL); ++ di_read_unlock(dentry, AuLock_IR); ++ } else { ++ err = au_cmoo(dentry); ++ if (!err) ++ err = args->open(file, vfsub_file_flags(file), ++ args->h_file); ++ if (!err && au_fbstart(file) != au_dbstart(dentry)) ++ /* ++ * cmoo happens after h_file was opened. ++ * need to refresh file later. ++ */ ++ atomic_dec(&au_fi(file)->fi_generation); ++ } ++ ++ finfo = au_fi(file); ++ if (!err) { ++ finfo->fi_file = file; ++ au_sphl_add(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ } ++ if (!no_lock) ++ fi_write_unlock(file); ++ else { ++ lockdep_off(); ++ fi_write_unlock(file); ++ lockdep_on(); ++ } ++ if (unlikely(err)) { ++ finfo->fi_hdir = NULL; ++ au_finfo_fin(file); ++ } ++ ++out: ++ return err; ++} ++ ++int au_reopen_nondir(struct file *file) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct dentry *dentry; ++ struct file *h_file, *h_file_tmp; ++ ++ dentry = file->f_path.dentry; ++ bstart = au_dbstart(dentry); ++ h_file_tmp = NULL; ++ if (au_fbstart(file) == bstart) { ++ h_file = au_hf_top(file); ++ if (file->f_mode == h_file->f_mode) ++ return 0; /* success */ ++ h_file_tmp = h_file; ++ get_file(h_file_tmp); ++ au_set_h_fptr(file, bstart, NULL); ++ } ++ AuDebugOn(au_fi(file)->fi_hdir); ++ /* ++ * it can happen ++ * file exists on both of rw and ro ++ * open --> dbstart and fbstart are both 0 ++ * prepend a branch as rw, "rw" become ro ++ * remove rw/file ++ * delete the top branch, "rw" becomes rw again ++ * --> dbstart is 1, fbstart is still 0 ++ * write --> fbstart is 0 but dbstart is 1 ++ */ ++ /* AuDebugOn(au_fbstart(file) < bstart); */ ++ ++ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, ++ file, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) { ++ if (h_file_tmp) { ++ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); ++ au_set_h_fptr(file, bstart, h_file_tmp); ++ h_file_tmp = NULL; ++ } ++ goto out; /* todo: close all? */ ++ } ++ ++ err = 0; ++ au_set_fbstart(file, bstart); ++ au_set_h_fptr(file, bstart, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ ++out: ++ if (h_file_tmp) ++ fput(h_file_tmp); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, ++ struct dentry *hi_wh) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_dinfo *dinfo; ++ struct dentry *h_dentry; ++ struct au_hdentry *hdp; ++ ++ dinfo = au_di(file->f_path.dentry); ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ bstart = dinfo->di_bstart; ++ dinfo->di_bstart = btgt; ++ hdp = dinfo->di_hdentry; ++ h_dentry = hdp[0 + btgt].hd_dentry; ++ hdp[0 + btgt].hd_dentry = hi_wh; ++ err = au_reopen_nondir(file); ++ hdp[0 + btgt].hd_dentry = h_dentry; ++ dinfo->di_bstart = bstart; ++ ++ return err; ++} ++ ++static int au_ready_to_write_wh(struct file *file, loff_t len, ++ aufs_bindex_t bcpup, struct au_pin *pin) ++{ ++ int err; ++ struct inode *inode, *h_inode; ++ struct dentry *h_dentry, *hi_wh; ++ struct au_cp_generic cpg = { ++ .dentry = file->f_path.dentry, ++ .bdst = bcpup, ++ .bsrc = -1, ++ .len = len, ++ .pin = pin ++ }; ++ ++ au_update_dbstart(cpg.dentry); ++ inode = d_inode(cpg.dentry); ++ h_inode = NULL; ++ if (au_dbstart(cpg.dentry) <= bcpup ++ && au_dbend(cpg.dentry) >= bcpup) { ++ h_dentry = au_h_dptr(cpg.dentry, bcpup); ++ if (h_dentry && d_is_positive(h_dentry)) ++ h_inode = d_inode(h_dentry); ++ } ++ hi_wh = au_hi_wh(inode, bcpup); ++ if (!hi_wh && !h_inode) ++ err = au_sio_cpup_wh(&cpg, file); ++ else ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, bcpup, hi_wh); ++ ++ if (!err ++ && (inode->i_nlink > 1 ++ || (inode->i_state & I_LINKABLE)) ++ && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) ++ au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); ++ ++ return err; ++} ++ ++/* ++ * prepare the @file for writing. ++ */ ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) ++{ ++ int err; ++ aufs_bindex_t dbstart; ++ struct dentry *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ struct file *h_file; ++ struct au_cp_generic cpg = { ++ .dentry = file->f_path.dentry, ++ .bdst = -1, ++ .bsrc = -1, ++ .len = len, ++ .pin = pin, ++ .flags = AuCpup_DTIME ++ }; ++ ++ sb = cpg.dentry->d_sb; ++ inode = d_inode(cpg.dentry); ++ cpg.bsrc = au_fbstart(file); ++ err = au_test_ro(sb, cpg.bsrc, inode); ++ if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { ++ err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, ++ /*flags*/0); ++ goto out; ++ } ++ ++ /* need to cpup or reopen */ ++ parent = dget_parent(cpg.dentry); ++ di_write_lock_parent(parent); ++ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); ++ cpg.bdst = err; ++ if (unlikely(err < 0)) ++ goto out_dgrade; ++ err = 0; ++ ++ if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { ++ err = au_cpup_dirs(cpg.dentry, cpg.bdst); ++ if (unlikely(err)) ++ goto out_dgrade; ++ } ++ ++ err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_dgrade; ++ ++ dbstart = au_dbstart(cpg.dentry); ++ if (dbstart <= cpg.bdst) ++ cpg.bsrc = cpg.bdst; ++ ++ if (dbstart <= cpg.bdst /* just reopen */ ++ || !d_unhashed(cpg.dentry) /* copyup and reopen */ ++ ) { ++ h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ di_downgrade_lock(parent, AuLock_IR); ++ if (dbstart > cpg.bdst) ++ err = au_sio_cpup_simple(&cpg); ++ if (!err) ++ err = au_reopen_nondir(file); ++ au_h_open_post(cpg.dentry, cpg.bsrc, h_file); ++ } ++ } else { /* copyup as wh and reopen */ ++ /* ++ * since writable hfsplus branch is not supported, ++ * h_open_pre/post() are unnecessary. ++ */ ++ err = au_ready_to_write_wh(file, len, cpg.bdst, pin); ++ di_downgrade_lock(parent, AuLock_IR); ++ } ++ ++ if (!err) { ++ au_pin_set_parent_lflag(pin, /*lflag*/0); ++ goto out_dput; /* success */ ++ } ++ au_unpin(pin); ++ goto out_unlock; ++ ++out_dgrade: ++ di_downgrade_lock(parent, AuLock_IR); ++out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++out_dput: ++ dput(parent); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_do_flush(struct file *file, fl_owner_t id, ++ int (*flush)(struct file *file, fl_owner_t id)) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_noflush_read_lock(sb); ++ fi_read_lock(file); ++ ii_read_lock_child(inode); ++ ++ err = flush(file, id); ++ au_cpup_attr_timesizes(inode); ++ ++ ii_read_unlock(inode); ++ fi_read_unlock(file); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_file_refresh_by_inode(struct file *file, int *need_reopen) ++{ ++ int err; ++ struct au_pin pin; ++ struct au_finfo *finfo; ++ struct dentry *parent, *hi_wh; ++ struct inode *inode; ++ struct super_block *sb; ++ struct au_cp_generic cpg = { ++ .dentry = file->f_path.dentry, ++ .bdst = -1, ++ .bsrc = -1, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME ++ }; ++ ++ FiMustWriteLock(file); ++ ++ err = 0; ++ finfo = au_fi(file); ++ sb = cpg.dentry->d_sb; ++ inode = d_inode(cpg.dentry); ++ cpg.bdst = au_ibstart(inode); ++ if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) ++ goto out; ++ ++ parent = dget_parent(cpg.dentry); ++ if (au_test_ro(sb, cpg.bdst, inode)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); ++ cpg.bdst = err; ++ di_read_unlock(parent, !AuLock_IR); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ err = 0; ++ } ++ ++ di_read_lock_parent(parent, AuLock_IR); ++ hi_wh = au_hi_wh(inode, cpg.bdst); ++ if (!S_ISDIR(inode->i_mode) ++ && au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode) ++ && !d_unhashed(cpg.dentry) ++ && cpg.bdst < au_dbstart(cpg.dentry)) { ++ err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ /* always superio. */ ++ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) { ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ } ++ } else if (hi_wh) { ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, cpg.bdst, hi_wh); ++ *need_reopen = 0; ++ } ++ ++out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++out_parent: ++ dput(parent); ++out: ++ return err; ++} ++ ++static void au_do_refresh_dir(struct file *file) ++{ ++ aufs_bindex_t bindex, bend, new_bindex, brid; ++ struct au_hfile *p, tmp, *q; ++ struct au_finfo *finfo; ++ struct super_block *sb; ++ struct au_fidir *fidir; ++ ++ FiMustWriteLock(file); ++ ++ sb = file->f_path.dentry->d_sb; ++ finfo = au_fi(file); ++ fidir = finfo->fi_hdir; ++ AuDebugOn(!fidir); ++ p = fidir->fd_hfile + finfo->fi_btop; ++ brid = p->hf_br->br_id; ++ bend = fidir->fd_bbot; ++ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { ++ if (!p->hf_file) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hf_br->br_id); ++ if (new_bindex == bindex) ++ continue; ++ if (new_bindex < 0) { ++ au_set_h_fptr(file, bindex, NULL); ++ continue; ++ } ++ ++ /* swap two lower inode, and loop again */ ++ q = fidir->fd_hfile + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hf_file) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ p = fidir->fd_hfile; ++ if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { ++ bend = au_sbend(sb); ++ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; ++ finfo->fi_btop++, p++) ++ if (p->hf_file) { ++ if (file_inode(p->hf_file)) ++ break; ++ au_hfput(p, file); ++ } ++ } else { ++ bend = au_br_index(sb, brid); ++ for (finfo->fi_btop = 0; finfo->fi_btop < bend; ++ finfo->fi_btop++, p++) ++ if (p->hf_file) ++ au_hfput(p, file); ++ bend = au_sbend(sb); ++ } ++ ++ p = fidir->fd_hfile + bend; ++ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; ++ fidir->fd_bbot--, p--) ++ if (p->hf_file) { ++ if (file_inode(p->hf_file)) ++ break; ++ au_hfput(p, file); ++ } ++ AuDebugOn(fidir->fd_bbot < finfo->fi_btop); ++} ++ ++/* ++ * after branch manipulating, refresh the file. ++ */ ++static int refresh_file(struct file *file, int (*reopen)(struct file *file)) ++{ ++ int err, need_reopen; ++ aufs_bindex_t bend, bindex; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ struct au_hfile *hfile; ++ ++ dentry = file->f_path.dentry; ++ finfo = au_fi(file); ++ if (!finfo->fi_hdir) { ++ hfile = &finfo->fi_htop; ++ AuDebugOn(!hfile->hf_file); ++ bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); ++ AuDebugOn(bindex < 0); ++ if (bindex != finfo->fi_btop) ++ au_set_fbstart(file, bindex); ++ } else { ++ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ au_do_refresh_dir(file); ++ } ++ ++ err = 0; ++ need_reopen = 1; ++ if (!au_test_mmapped(file)) ++ err = au_file_refresh_by_inode(file, &need_reopen); ++ if (!err && need_reopen && !d_unlinked(dentry)) ++ err = reopen(file); ++ if (!err) { ++ au_update_figen(file); ++ goto out; /* success */ ++ } ++ ++ /* error, close all lower files */ ++ if (finfo->fi_hdir) { ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ } ++ ++out: ++ return err; ++} ++ ++/* common function to regular file and dir */ ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock) ++{ ++ int err; ++ unsigned int sigen, figen; ++ aufs_bindex_t bstart; ++ unsigned char pseudo_link; ++ struct dentry *dentry; ++ struct inode *inode; ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ inode = d_inode(dentry); ++ sigen = au_sigen(dentry->d_sb); ++ fi_write_lock(file); ++ figen = au_figen(file); ++ di_write_lock_child(dentry); ++ bstart = au_dbstart(dentry); ++ pseudo_link = (bstart != au_ibstart(inode)); ++ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ goto out; /* success */ ++ } ++ ++ AuDbg("sigen %d, figen %d\n", sigen, figen); ++ if (au_digen_test(dentry, sigen)) { ++ err = au_reval_dpath(dentry, sigen); ++ AuDebugOn(!err && au_digen_test(dentry, sigen)); ++ } ++ ++ if (!err) ++ err = refresh_file(file, reopen); ++ if (!err) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ } else { ++ di_write_unlock(dentry); ++ fi_write_unlock(file); ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* cf. aufs_nopage() */ ++/* for madvise(2) */ ++static int aufs_readpage(struct file *file __maybe_unused, struct page *page) ++{ ++ unlock_page(page); ++ return 0; ++} ++ ++/* it will never be called, but necessary to support O_DIRECT */ ++static ssize_t aufs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, ++ loff_t offset) ++{ BUG(); return 0; } ++ ++/* they will never be called. */ ++#ifdef CONFIG_AUFS_DEBUG ++static int aufs_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *page, void *fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_writepage(struct page *page, struct writeback_control *wbc) ++{ AuUnsupport(); return 0; } ++ ++static int aufs_set_page_dirty(struct page *page) ++{ AuUnsupport(); return 0; } ++static void aufs_invalidatepage(struct page *page, unsigned int offset, ++ unsigned int length) ++{ AuUnsupport(); } ++static int aufs_releasepage(struct page *page, gfp_t gfp) ++{ AuUnsupport(); return 0; } ++static int aufs_migratepage(struct address_space *mapping, struct page *newpage, ++ struct page *page, enum migrate_mode mode) ++{ AuUnsupport(); return 0; } ++static int aufs_launder_page(struct page *page) ++{ AuUnsupport(); return 0; } ++static int aufs_is_partially_uptodate(struct page *page, ++ unsigned long from, ++ unsigned long count) ++{ AuUnsupport(); return 0; } ++static void aufs_is_dirty_writeback(struct page *page, bool *dirty, ++ bool *writeback) ++{ AuUnsupport(); } ++static int aufs_error_remove_page(struct address_space *mapping, ++ struct page *page) ++{ AuUnsupport(); return 0; } ++static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, ++ sector_t *span) ++{ AuUnsupport(); return 0; } ++static void aufs_swap_deactivate(struct file *file) ++{ AuUnsupport(); } ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++const struct address_space_operations aufs_aop = { ++ .readpage = aufs_readpage, ++ .direct_IO = aufs_direct_IO, ++#ifdef CONFIG_AUFS_DEBUG ++ .writepage = aufs_writepage, ++ /* no writepages, because of writepage */ ++ .set_page_dirty = aufs_set_page_dirty, ++ /* no readpages, because of readpage */ ++ .write_begin = aufs_write_begin, ++ .write_end = aufs_write_end, ++ /* no bmap, no block device */ ++ .invalidatepage = aufs_invalidatepage, ++ .releasepage = aufs_releasepage, ++ .migratepage = aufs_migratepage, ++ .launder_page = aufs_launder_page, ++ .is_partially_uptodate = aufs_is_partially_uptodate, ++ .is_dirty_writeback = aufs_is_dirty_writeback, ++ .error_remove_page = aufs_error_remove_page, ++ .swap_activate = aufs_swap_activate, ++ .swap_deactivate = aufs_swap_deactivate ++#endif /* CONFIG_AUFS_DEBUG */ ++}; +diff --git a/fs/aufs/file.h b/fs/aufs/file.h +new file mode 100644 +index 0000000..488473e +--- /dev/null ++++ b/fs/aufs/file.h +@@ -0,0 +1,278 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * file operations ++ */ ++ ++#ifndef __AUFS_FILE_H__ ++#define __AUFS_FILE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "rwsem.h" ++ ++struct au_branch; ++struct au_hfile { ++ struct file *hf_file; ++ struct au_branch *hf_br; ++}; ++ ++struct au_vdir; ++struct au_fidir { ++ aufs_bindex_t fd_bbot; ++ aufs_bindex_t fd_nent; ++ struct au_vdir *fd_vdir_cache; ++ struct au_hfile fd_hfile[]; ++}; ++ ++static inline int au_fidir_sz(int nent) ++{ ++ AuDebugOn(nent < 0); ++ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; ++} ++ ++struct au_finfo { ++ atomic_t fi_generation; ++ ++ struct au_rwsem fi_rwsem; ++ aufs_bindex_t fi_btop; ++ ++ /* do not union them */ ++ struct { /* for non-dir */ ++ struct au_hfile fi_htop; ++ atomic_t fi_mmapped; ++ }; ++ struct au_fidir *fi_hdir; /* for dir only */ ++ ++ struct hlist_node fi_hlist; ++ struct file *fi_file; /* very ugly */ ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* file.c */ ++extern const struct address_space_operations aufs_aop; ++unsigned int au_file_roflags(unsigned int flags); ++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, ++ struct file *file, int force_wr); ++struct au_do_open_args { ++ int no_lock; ++ int (*open)(struct file *file, int flags, ++ struct file *h_file); ++ struct au_fidir *fidir; ++ struct file *h_file; ++}; ++int au_do_open(struct file *file, struct au_do_open_args *args); ++int au_reopen_nondir(struct file *file); ++struct au_pin; ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock); ++int au_do_flush(struct file *file, fl_owner_t id, ++ int (*flush)(struct file *file, fl_owner_t id)); ++ ++/* poll.c */ ++#ifdef CONFIG_AUFS_POLL ++unsigned int aufs_poll(struct file *file, poll_table *wait); ++#endif ++ ++#ifdef CONFIG_AUFS_BR_HFSPLUS ++/* hfsplus.c */ ++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, ++ int force_wr); ++void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file); ++#else ++AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, ++ aufs_bindex_t bindex, int force_wr) ++AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file); ++#endif ++ ++/* f_op.c */ ++extern const struct file_operations aufs_file_fop; ++int au_do_open_nondir(struct file *file, int flags, struct file *h_file); ++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); ++struct file *au_read_pre(struct file *file, int keep_fi); ++ ++/* finfo.c */ ++void au_hfput(struct au_hfile *hf, struct file *file); ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, ++ struct file *h_file); ++ ++void au_update_figen(struct file *file); ++struct au_fidir *au_fidir_alloc(struct super_block *sb); ++int au_fidir_realloc(struct au_finfo *finfo, int nbr); ++ ++void au_fi_init_once(void *_fi); ++void au_finfo_fin(struct file *file); ++int au_finfo_init(struct file *file, struct au_fidir *fidir); ++ ++/* ioctl.c */ ++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, ++ unsigned long arg); ++long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_finfo *au_fi(struct file *file) ++{ ++ return file->private_data; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * fi_read_lock, fi_write_lock, ++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); ++ ++#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) ++#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) ++#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: hard/soft set? */ ++static inline aufs_bindex_t au_fbstart(struct file *file) ++{ ++ FiMustAnyLock(file); ++ return au_fi(file)->fi_btop; ++} ++ ++static inline aufs_bindex_t au_fbend_dir(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_bbot; ++} ++ ++static inline struct au_vdir *au_fvdir_cache(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_vdir_cache; ++} ++ ++static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustWriteLock(file); ++ au_fi(file)->fi_btop = bindex; ++} ++ ++static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustWriteLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ au_fi(file)->fi_hdir->fd_bbot = bindex; ++} ++ ++static inline void au_set_fvdir_cache(struct file *file, ++ struct au_vdir *vdir_cache) ++{ ++ FiMustWriteLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; ++} ++ ++static inline struct file *au_hf_top(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_htop.hf_file; ++} ++ ++static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; ++} ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_figen(struct file *f) ++{ ++ return atomic_read(&au_fi(f)->fi_generation); ++} ++ ++static inline void au_set_mmapped(struct file *f) ++{ ++ if (atomic_inc_return(&au_fi(f)->fi_mmapped)) ++ return; ++ pr_warn("fi_mmapped wrapped around\n"); ++ while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) ++ ; ++} ++ ++static inline void au_unset_mmapped(struct file *f) ++{ ++ atomic_dec(&au_fi(f)->fi_mmapped); ++} ++ ++static inline int au_test_mmapped(struct file *f) ++{ ++ return atomic_read(&au_fi(f)->fi_mmapped); ++} ++ ++/* customize vma->vm_file */ ++ ++static inline void au_do_vm_file_reset(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ struct file *f; ++ ++ f = vma->vm_file; ++ get_file(file); ++ vma->vm_file = file; ++ fput(f); ++} ++ ++#ifdef CONFIG_MMU ++#define AuDbgVmRegion(file, vma) do {} while (0) ++ ++static inline void au_vm_file_reset(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ au_do_vm_file_reset(vma, file); ++} ++#else ++#define AuDbgVmRegion(file, vma) \ ++ AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) ++ ++static inline void au_vm_file_reset(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ struct file *f; ++ ++ au_do_vm_file_reset(vma, file); ++ f = vma->vm_region->vm_file; ++ get_file(file); ++ vma->vm_region->vm_file = file; ++ fput(f); ++} ++#endif /* CONFIG_MMU */ ++ ++/* handle vma->vm_prfile */ ++static inline void au_vm_prfile_set(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ get_file(file); ++ vma->vm_prfile = file; ++#ifndef CONFIG_MMU ++ get_file(file); ++ vma->vm_region->vm_prfile = file; ++#endif ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FILE_H__ */ +diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c +new file mode 100644 +index 0000000..05732c9 +--- /dev/null ++++ b/fs/aufs/finfo.c +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * file private data ++ */ ++ ++#include "aufs.h" ++ ++void au_hfput(struct au_hfile *hf, struct file *file) ++{ ++ /* todo: direct access f_flags */ ++ if (vfsub_file_flags(file) & __FMODE_EXEC) ++ allow_write_access(hf->hf_file); ++ fput(hf->hf_file); ++ hf->hf_file = NULL; ++ atomic_dec(&hf->hf_br->br_count); ++ hf->hf_br = NULL; ++} ++ ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) ++{ ++ struct au_finfo *finfo = au_fi(file); ++ struct au_hfile *hf; ++ struct au_fidir *fidir; ++ ++ fidir = finfo->fi_hdir; ++ if (!fidir) { ++ AuDebugOn(finfo->fi_btop != bindex); ++ hf = &finfo->fi_htop; ++ } else ++ hf = fidir->fd_hfile + bindex; ++ ++ if (hf && hf->hf_file) ++ au_hfput(hf, file); ++ if (val) { ++ FiMustWriteLock(file); ++ AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry)); ++ hf->hf_file = val; ++ hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex); ++ } ++} ++ ++void au_update_figen(struct file *file) ++{ ++ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_fidir *au_fidir_alloc(struct super_block *sb) ++{ ++ struct au_fidir *fidir; ++ int nbr; ++ ++ nbr = au_sbend(sb) + 1; ++ if (nbr < 2) ++ nbr = 2; /* initial allocate for 2 branches */ ++ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); ++ if (fidir) { ++ fidir->fd_bbot = -1; ++ fidir->fd_nent = nbr; ++ fidir->fd_vdir_cache = NULL; ++ } ++ ++ return fidir; ++} ++ ++int au_fidir_realloc(struct au_finfo *finfo, int nbr) ++{ ++ int err; ++ struct au_fidir *fidir, *p; ++ ++ AuRwMustWriteLock(&finfo->fi_rwsem); ++ fidir = finfo->fi_hdir; ++ AuDebugOn(!fidir); ++ ++ err = -ENOMEM; ++ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), ++ GFP_NOFS); ++ if (p) { ++ p->fd_nent = nbr; ++ finfo->fi_hdir = p; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_finfo_fin(struct file *file) ++{ ++ struct au_finfo *finfo; ++ ++ au_nfiles_dec(file->f_path.dentry->d_sb); ++ ++ finfo = au_fi(file); ++ AuDebugOn(finfo->fi_hdir); ++ AuRwDestroy(&finfo->fi_rwsem); ++ au_cache_free_finfo(finfo); ++} ++ ++void au_fi_init_once(void *_finfo) ++{ ++ struct au_finfo *finfo = _finfo; ++ static struct lock_class_key aufs_fi; ++ ++ au_rw_init(&finfo->fi_rwsem); ++ au_rw_class(&finfo->fi_rwsem, &aufs_fi); ++} ++ ++int au_finfo_init(struct file *file, struct au_fidir *fidir) ++{ ++ int err; ++ struct au_finfo *finfo; ++ struct dentry *dentry; ++ ++ err = -ENOMEM; ++ dentry = file->f_path.dentry; ++ finfo = au_cache_alloc_finfo(); ++ if (unlikely(!finfo)) ++ goto out; ++ ++ err = 0; ++ au_nfiles_inc(dentry->d_sb); ++ /* verbose coding for lock class name */ ++ if (!fidir) ++ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); ++ else ++ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); ++ au_rw_write_lock(&finfo->fi_rwsem); ++ finfo->fi_btop = -1; ++ finfo->fi_hdir = fidir; ++ atomic_set(&finfo->fi_generation, au_digen(dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++ ++ file->private_data = finfo; ++ ++out: ++ return err; ++} +diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h +new file mode 100644 +index 0000000..6c3e1de +--- /dev/null ++++ b/fs/aufs/fstype.h +@@ -0,0 +1,387 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * judging filesystem type ++ */ ++ ++#ifndef __AUFS_FSTYPE_H__ ++#define __AUFS_FSTYPE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++ ++static inline int au_test_aufs(struct super_block *sb) ++{ ++ return sb->s_magic == AUFS_SUPER_MAGIC; ++} ++ ++static inline const char *au_sbtype(struct super_block *sb) ++{ ++ return sb->s_type->name; ++} ++ ++static inline int au_test_iso9660(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) ++ return sb->s_magic == ISOFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_romfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) ++ return sb->s_magic == ROMFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_cramfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) ++ return sb->s_magic == CRAMFS_MAGIC; ++#endif ++ return 0; ++} ++ ++static inline int au_test_nfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) ++ return sb->s_magic == NFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_fuse(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) ++ return sb->s_magic == FUSE_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) ++ return sb->s_magic == XFS_SB_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_TMPFS ++ return sb->s_magic == TMPFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) ++ return !strcmp(au_sbtype(sb), "ecryptfs"); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ramfs(struct super_block *sb) ++{ ++ return sb->s_magic == RAMFS_MAGIC; ++} ++ ++static inline int au_test_ubifs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) ++ return sb->s_magic == UBIFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_procfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_PROC_FS ++ return sb->s_magic == PROC_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_sysfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SYSFS ++ return sb->s_magic == SYSFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_configfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) ++ return sb->s_magic == CONFIGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_minix(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) ++ return sb->s_magic == MINIX3_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC2 ++ || sb->s_magic == MINIX_SUPER_MAGIC ++ || sb->s_magic == MINIX_SUPER_MAGIC2; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_fat(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) ++ return sb->s_magic == MSDOS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_msdos(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_vfat(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_securityfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SECURITYFS ++ return sb->s_magic == SECURITYFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_squashfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) ++ return sb->s_magic == SQUASHFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_btrfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) ++ return sb->s_magic == BTRFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xenfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) ++ return sb->s_magic == XENFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_debugfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_DEBUG_FS ++ return sb->s_magic == DEBUGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_nilfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) ++ return sb->s_magic == NILFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) ++ return sb->s_magic == HFSPLUS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * they can't be an aufs branch. ++ */ ++static inline int au_test_fs_unsuppoted(struct super_block *sb) ++{ ++ return ++#ifndef CONFIG_AUFS_BR_RAMFS ++ au_test_ramfs(sb) || ++#endif ++ au_test_procfs(sb) ++ || au_test_sysfs(sb) ++ || au_test_configfs(sb) ++ || au_test_debugfs(sb) ++ || au_test_securityfs(sb) ++ || au_test_xenfs(sb) ++ || au_test_ecryptfs(sb) ++ /* || !strcmp(au_sbtype(sb), "unionfs") */ ++ || au_test_aufs(sb); /* will be supported in next version */ ++} ++ ++static inline int au_test_fs_remote(struct super_block *sb) ++{ ++ return !au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ && !au_test_ramfs(sb) ++#endif ++ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * Note: these functions (below) are created after reading ->getattr() in all ++ * filesystems under linux/fs. it means we have to do so in every update... ++ */ ++ ++/* ++ * some filesystems require getattr to refresh the inode attributes before ++ * referencing. ++ * in most cases, we can rely on the inode attribute in NFS (or every remote fs) ++ * and leave the work for d_revalidate() ++ */ ++static inline int au_test_fs_refresh_iattr(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ /* || au_test_btrfs(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't maintain i_size or i_blocks. ++ */ ++static inline int au_test_fs_bad_iattr_size(struct super_block *sb) ++{ ++ return au_test_xfs(sb) ++ || au_test_btrfs(sb) ++ || au_test_ubifs(sb) ++ || au_test_hfsplus(sb) /* maintained, but incorrect */ ++ /* || au_test_minix(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't store the correct value in some of their inode ++ * attributes. ++ */ ++static inline int au_test_fs_bad_iattr(struct super_block *sb) ++{ ++ return au_test_fs_bad_iattr_size(sb) ++ || au_test_fat(sb) ++ || au_test_msdos(sb) ++ || au_test_vfat(sb); ++} ++ ++/* they don't check i_nlink in link(2) */ ++static inline int au_test_fs_no_limit_nlink(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ || au_test_ramfs(sb) ++#endif ++ || au_test_ubifs(sb) ++ || au_test_hfsplus(sb); ++} ++ ++/* ++ * filesystems which sets S_NOATIME and S_NOCMTIME. ++ */ ++static inline int au_test_fs_notime(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ || au_test_ubifs(sb) ++ ; ++} ++ ++/* temporary support for i#1 in cramfs */ ++static inline int au_test_fs_unique_ino(struct inode *inode) ++{ ++ if (au_test_cramfs(inode->i_sb)) ++ return inode->i_ino != 1; ++ return 1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * the filesystem where the xino files placed must support i/o after unlink and ++ * maintain i_size and i_blocks. ++ */ ++static inline int au_test_fs_bad_xino(struct super_block *sb) ++{ ++ return au_test_fs_remote(sb) ++ || au_test_fs_bad_iattr_size(sb) ++ /* don't want unnecessary work for xino */ ++ || au_test_aufs(sb) ++ || au_test_ecryptfs(sb) ++ || au_test_nilfs(sb); ++} ++ ++static inline int au_test_fs_trunc_xino(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++ || au_test_ramfs(sb); ++} ++ ++/* ++ * test if the @sb is real-readonly. ++ */ ++static inline int au_test_fs_rr(struct super_block *sb) ++{ ++ return au_test_squashfs(sb) ++ || au_test_iso9660(sb) ++ || au_test_cramfs(sb) ++ || au_test_romfs(sb); ++} ++ ++/* ++ * test if the @inode is nfs with 'noacl' option ++ * NFS always sets MS_POSIXACL regardless its mount option 'noacl.' ++ */ ++static inline int au_test_nfs_noacl(struct inode *inode) ++{ ++ return au_test_nfs(inode->i_sb) ++ /* && IS_POSIXACL(inode) */ ++ && !nfs_server_capable(inode, NFS_CAP_ACLS); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FSTYPE_H__ */ +diff --git a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c +new file mode 100644 +index 0000000..5c370196 +--- /dev/null ++++ b/fs/aufs/hfsnotify.c +@@ -0,0 +1,275 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * fsnotify for the lower directories ++ */ ++ ++#include "aufs.h" ++ ++/* FS_IN_IGNORED is unnecessary */ ++static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE ++ | FS_CREATE | FS_EVENT_ON_CHILD); ++static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); ++static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); ++ ++static void au_hfsn_free_mark(struct fsnotify_mark *mark) ++{ ++ struct au_hnotify *hn = container_of(mark, struct au_hnotify, ++ hn_mark); ++ AuDbg("here\n"); ++ au_cache_free_hnotify(hn); ++ smp_mb__before_atomic(); ++ if (atomic64_dec_and_test(&au_hfsn_ifree)) ++ wake_up(&au_hfsn_wq); ++} ++ ++static int au_hfsn_alloc(struct au_hinode *hinode) ++{ ++ int err; ++ struct au_hnotify *hn; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct fsnotify_mark *mark; ++ aufs_bindex_t bindex; ++ ++ hn = hinode->hi_notify; ++ sb = hn->hn_aufs_inode->i_sb; ++ bindex = au_br_index(sb, hinode->hi_id); ++ br = au_sbr(sb, bindex); ++ AuDebugOn(!br->br_hfsn); ++ ++ mark = &hn->hn_mark; ++ fsnotify_init_mark(mark, au_hfsn_free_mark); ++ mark->mask = AuHfsnMask; ++ /* ++ * by udba rename or rmdir, aufs assign a new inode to the known ++ * h_inode, so specify 1 to allow dups. ++ */ ++ lockdep_off(); ++ err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, ++ /*mnt*/NULL, /*allow_dups*/1); ++ /* even if err */ ++ fsnotify_put_mark(mark); ++ lockdep_on(); ++ ++ return err; ++} ++ ++static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) ++{ ++ struct fsnotify_mark *mark; ++ unsigned long long ull; ++ struct fsnotify_group *group; ++ ++ ull = atomic64_inc_return(&au_hfsn_ifree); ++ BUG_ON(!ull); ++ ++ mark = &hn->hn_mark; ++ spin_lock(&mark->lock); ++ group = mark->group; ++ fsnotify_get_group(group); ++ spin_unlock(&mark->lock); ++ lockdep_off(); ++ fsnotify_destroy_mark(mark, group); ++ fsnotify_put_group(group); ++ lockdep_on(); ++ ++ /* free hn by myself */ ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) ++{ ++ struct fsnotify_mark *mark; ++ ++ mark = &hinode->hi_notify->hn_mark; ++ spin_lock(&mark->lock); ++ if (do_set) { ++ AuDebugOn(mark->mask & AuHfsnMask); ++ mark->mask |= AuHfsnMask; ++ } else { ++ AuDebugOn(!(mark->mask & AuHfsnMask)); ++ mark->mask &= ~AuHfsnMask; ++ } ++ spin_unlock(&mark->lock); ++ /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* #define AuDbgHnotify */ ++#ifdef AuDbgHnotify ++static char *au_hfsn_name(u32 mask) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++#define test_ret(flag) \ ++ do { \ ++ if (mask & flag) \ ++ return #flag; \ ++ } while (0) ++ test_ret(FS_ACCESS); ++ test_ret(FS_MODIFY); ++ test_ret(FS_ATTRIB); ++ test_ret(FS_CLOSE_WRITE); ++ test_ret(FS_CLOSE_NOWRITE); ++ test_ret(FS_OPEN); ++ test_ret(FS_MOVED_FROM); ++ test_ret(FS_MOVED_TO); ++ test_ret(FS_CREATE); ++ test_ret(FS_DELETE); ++ test_ret(FS_DELETE_SELF); ++ test_ret(FS_MOVE_SELF); ++ test_ret(FS_UNMOUNT); ++ test_ret(FS_Q_OVERFLOW); ++ test_ret(FS_IN_IGNORED); ++ test_ret(FS_ISDIR); ++ test_ret(FS_IN_ONESHOT); ++ test_ret(FS_EVENT_ON_CHILD); ++ return ""; ++#undef test_ret ++#else ++ return "??"; ++#endif ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_free_group(struct fsnotify_group *group) ++{ ++ struct au_br_hfsnotify *hfsn = group->private; ++ ++ AuDbg("here\n"); ++ kfree(hfsn); ++} ++ ++static int au_hfsn_handle_event(struct fsnotify_group *group, ++ struct inode *inode, ++ struct fsnotify_mark *inode_mark, ++ struct fsnotify_mark *vfsmount_mark, ++ u32 mask, void *data, int data_type, ++ const unsigned char *file_name, u32 cookie) ++{ ++ int err; ++ struct au_hnotify *hnotify; ++ struct inode *h_dir, *h_inode; ++ struct qstr h_child_qstr = QSTR_INIT(file_name, strlen(file_name)); ++ ++ AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); ++ ++ err = 0; ++ /* if FS_UNMOUNT happens, there must be another bug */ ++ AuDebugOn(mask & FS_UNMOUNT); ++ if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) ++ goto out; ++ ++ h_dir = inode; ++ h_inode = NULL; ++#ifdef AuDbgHnotify ++ au_debug_on(); ++ if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 ++ || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { ++ AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", ++ h_dir->i_ino, mask, au_hfsn_name(mask), ++ AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); ++ /* WARN_ON(1); */ ++ } ++ au_debug_off(); ++#endif ++ ++ AuDebugOn(!inode_mark); ++ hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); ++ err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); ++ ++out: ++ return err; ++} ++ ++static struct fsnotify_ops au_hfsn_ops = { ++ .handle_event = au_hfsn_handle_event, ++ .free_group_priv = au_hfsn_free_group ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_fin_br(struct au_branch *br) ++{ ++ struct au_br_hfsnotify *hfsn; ++ ++ hfsn = br->br_hfsn; ++ if (hfsn) { ++ lockdep_off(); ++ fsnotify_put_group(hfsn->hfsn_group); ++ lockdep_on(); ++ } ++} ++ ++static int au_hfsn_init_br(struct au_branch *br, int perm) ++{ ++ int err; ++ struct fsnotify_group *group; ++ struct au_br_hfsnotify *hfsn; ++ ++ err = 0; ++ br->br_hfsn = NULL; ++ if (!au_br_hnotifyable(perm)) ++ goto out; ++ ++ err = -ENOMEM; ++ hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); ++ if (unlikely(!hfsn)) ++ goto out; ++ ++ err = 0; ++ group = fsnotify_alloc_group(&au_hfsn_ops); ++ if (IS_ERR(group)) { ++ err = PTR_ERR(group); ++ pr_err("fsnotify_alloc_group() failed, %d\n", err); ++ goto out_hfsn; ++ } ++ ++ group->private = hfsn; ++ hfsn->hfsn_group = group; ++ br->br_hfsn = hfsn; ++ goto out; /* success */ ++ ++out_hfsn: ++ kfree(hfsn); ++out: ++ return err; ++} ++ ++static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) ++{ ++ int err; ++ ++ err = 0; ++ if (!br->br_hfsn) ++ err = au_hfsn_init_br(br, perm); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_fin(void) ++{ ++ AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); ++ wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); ++} ++ ++const struct au_hnotify_op au_hnotify_op = { ++ .ctl = au_hfsn_ctl, ++ .alloc = au_hfsn_alloc, ++ .free = au_hfsn_free, ++ ++ .fin = au_hfsn_fin, ++ ++ .reset_br = au_hfsn_reset_br, ++ .fin_br = au_hfsn_fin_br, ++ .init_br = au_hfsn_init_br ++}; +diff --git a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c +new file mode 100644 +index 0000000..34c1f1a +--- /dev/null ++++ b/fs/aufs/hfsplus.c +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * special support for filesystems which aqucires an inode mutex ++ * at final closing a file, eg, hfsplus. ++ * ++ * This trick is very simple and stupid, just to open the file before really ++ * neceeary open to tell hfsplus that this is not the final closing. ++ * The caller should call au_h_open_pre() after acquiring the inode mutex, ++ * and au_h_open_post() after releasing it. ++ */ ++ ++#include "aufs.h" ++ ++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, ++ int force_wr) ++{ ++ struct file *h_file; ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ AuDebugOn(!h_dentry); ++ AuDebugOn(d_is_negative(h_dentry)); ++ ++ h_file = NULL; ++ if (au_test_hfsplus(h_dentry->d_sb) ++ && d_is_reg(h_dentry)) ++ h_file = au_h_open(dentry, bindex, ++ O_RDONLY | O_NOATIME | O_LARGEFILE, ++ /*file*/NULL, force_wr); ++ return h_file; ++} ++ ++void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file) ++{ ++ if (h_file) { ++ fput(h_file); ++ au_sbr_put(dentry->d_sb, bindex); ++ } ++} +diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c +new file mode 100644 +index 0000000..3943dc4 +--- /dev/null ++++ b/fs/aufs/hnotify.c +@@ -0,0 +1,697 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * abstraction to notify the direct changes on lower directories ++ */ ++ ++#include "aufs.h" ++ ++int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) ++{ ++ int err; ++ struct au_hnotify *hn; ++ ++ err = -ENOMEM; ++ hn = au_cache_alloc_hnotify(); ++ if (hn) { ++ hn->hn_aufs_inode = inode; ++ hinode->hi_notify = hn; ++ err = au_hnotify_op.alloc(hinode); ++ AuTraceErr(err); ++ if (unlikely(err)) { ++ hinode->hi_notify = NULL; ++ au_cache_free_hnotify(hn); ++ /* ++ * The upper dir was removed by udba, but the same named ++ * dir left. In this case, aufs assignes a new inode ++ * number and set the monitor again. ++ * For the lower dir, the old monitnor is still left. ++ */ ++ if (err == -EEXIST) ++ err = 0; ++ } ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_hn_free(struct au_hinode *hinode) ++{ ++ struct au_hnotify *hn; ++ ++ hn = hinode->hi_notify; ++ if (hn) { ++ hinode->hi_notify = NULL; ++ if (au_hnotify_op.free(hinode, hn)) ++ au_cache_free_hnotify(hn); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_hn_ctl(struct au_hinode *hinode, int do_set) ++{ ++ if (hinode->hi_notify) ++ au_hnotify_op.ctl(hinode, do_set); ++} ++ ++void au_hn_reset(struct inode *inode, unsigned int flags) ++{ ++ aufs_bindex_t bindex, bend; ++ struct inode *hi; ++ struct dentry *iwhdentry; ++ ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ hi = au_h_iptr(inode, bindex); ++ if (!hi) ++ continue; ++ ++ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ ++ iwhdentry = au_hi_wh(inode, bindex); ++ if (iwhdentry) ++ dget(iwhdentry); ++ au_igrab(hi); ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ au_set_h_iptr(inode, bindex, au_igrab(hi), ++ flags & ~AuHi_XINO); ++ iput(hi); ++ dput(iwhdentry); ++ /* mutex_unlock(&hi->i_mutex); */ ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int hn_xino(struct inode *inode, struct inode *h_inode) ++{ ++ int err; ++ aufs_bindex_t bindex, bend, bfound, bstart; ++ struct inode *h_i; ++ ++ err = 0; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warn("branch root dir was changed\n"); ++ goto out; ++ } ++ ++ bfound = -1; ++ bend = au_ibend(inode); ++ bstart = au_ibstart(inode); ++#if 0 /* reserved for future use */ ++ if (bindex == bend) { ++ /* keep this ino in rename case */ ++ goto out; ++ } ++#endif ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex) == h_inode) { ++ bfound = bindex; ++ break; ++ } ++ if (bfound < 0) ++ goto out; ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(inode, bindex); ++ if (!h_i) ++ continue; ++ ++ err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); ++ /* ignore this error */ ++ /* bad action? */ ++ } ++ ++ /* children inode number will be broken */ ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hn_gen_tree(struct dentry *dentry) ++{ ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, dentry, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ struct dentry *d; ++ ++ d = dentries[j]; ++ if (IS_ROOT(d)) ++ continue; ++ ++ au_digen_dec(d); ++ if (d_really_is_positive(d)) ++ /* todo: reset children xino? ++ cached children only? */ ++ au_iigen_dec(d_inode(d)); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++ ++#if 0 ++ /* discard children */ ++ dentry_unhash(dentry); ++ dput(dentry); ++#endif ++out: ++ return err; ++} ++ ++/* ++ * return 0 if processed. ++ */ ++static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, ++ const unsigned int isdir) ++{ ++ int err; ++ struct dentry *d; ++ struct qstr *dname; ++ ++ err = 1; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warn("branch root dir was changed\n"); ++ err = 0; ++ goto out; ++ } ++ ++ if (!isdir) { ++ AuDebugOn(!name); ++ au_iigen_dec(inode); ++ spin_lock(&inode->i_lock); ++ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { ++ spin_lock(&d->d_lock); ++ dname = &d->d_name; ++ if (dname->len != nlen ++ && memcmp(dname->name, name, nlen)) { ++ spin_unlock(&d->d_lock); ++ continue; ++ } ++ err = 0; ++ au_digen_dec(d); ++ spin_unlock(&d->d_lock); ++ break; ++ } ++ spin_unlock(&inode->i_lock); ++ } else { ++ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); ++ d = d_find_any_alias(inode); ++ if (!d) { ++ au_iigen_dec(inode); ++ goto out; ++ } ++ ++ spin_lock(&d->d_lock); ++ dname = &d->d_name; ++ if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { ++ spin_unlock(&d->d_lock); ++ err = hn_gen_tree(d); ++ spin_lock(&d->d_lock); ++ } ++ spin_unlock(&d->d_lock); ++ dput(d); ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) ++{ ++ int err; ++ ++ if (IS_ROOT(dentry)) { ++ pr_warn("branch root dir was changed\n"); ++ return 0; ++ } ++ ++ err = 0; ++ if (!isdir) { ++ au_digen_dec(dentry); ++ if (d_really_is_positive(dentry)) ++ au_iigen_dec(d_inode(dentry)); ++ } else { ++ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); ++ if (d_really_is_positive(dentry)) ++ err = hn_gen_tree(dentry); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* hnotify job flags */ ++#define AuHnJob_XINO0 1 ++#define AuHnJob_GEN (1 << 1) ++#define AuHnJob_DIRENT (1 << 2) ++#define AuHnJob_ISDIR (1 << 3) ++#define AuHnJob_TRYXINO0 (1 << 4) ++#define AuHnJob_MNTPNT (1 << 5) ++#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) ++#define au_fset_hnjob(flags, name) \ ++ do { (flags) |= AuHnJob_##name; } while (0) ++#define au_fclr_hnjob(flags, name) \ ++ do { (flags) &= ~AuHnJob_##name; } while (0) ++ ++enum { ++ AuHn_CHILD, ++ AuHn_PARENT, ++ AuHnLast ++}; ++ ++struct au_hnotify_args { ++ struct inode *h_dir, *dir, *h_child_inode; ++ u32 mask; ++ unsigned int flags[AuHnLast]; ++ unsigned int h_child_nlen; ++ char h_child_name[]; ++}; ++ ++struct hn_job_args { ++ unsigned int flags; ++ struct inode *inode, *h_inode, *dir, *h_dir; ++ struct dentry *dentry; ++ char *h_name; ++ int h_nlen; ++}; ++ ++static int hn_job(struct hn_job_args *a) ++{ ++ const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); ++ int e; ++ ++ /* reset xino */ ++ if (au_ftest_hnjob(a->flags, XINO0) && a->inode) ++ hn_xino(a->inode, a->h_inode); /* ignore this error */ ++ ++ if (au_ftest_hnjob(a->flags, TRYXINO0) ++ && a->inode ++ && a->h_inode) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ if (!a->h_inode->i_nlink ++ && !(a->h_inode->i_state & I_LINKABLE)) ++ hn_xino(a->inode, a->h_inode); /* ignore this error */ ++ mutex_unlock(&a->h_inode->i_mutex); ++ } ++ ++ /* make the generation obsolete */ ++ if (au_ftest_hnjob(a->flags, GEN)) { ++ e = -1; ++ if (a->inode) ++ e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, ++ isdir); ++ if (e && a->dentry) ++ hn_gen_by_name(a->dentry, isdir); ++ /* ignore this error */ ++ } ++ ++ /* make dir entries obsolete */ ++ if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { ++ struct au_vdir *vdir; ++ ++ vdir = au_ivdir(a->inode); ++ if (vdir) ++ vdir->vd_jiffy = 0; ++ /* IMustLock(a->inode); */ ++ /* a->inode->i_version++; */ ++ } ++ ++ /* can do nothing but warn */ ++ if (au_ftest_hnjob(a->flags, MNTPNT) ++ && a->dentry ++ && d_mountpoint(a->dentry)) ++ pr_warn("mount-point %pd is removed or renamed\n", a->dentry); ++ ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, ++ struct inode *dir) ++{ ++ struct dentry *dentry, *d, *parent; ++ struct qstr *dname; ++ ++ parent = d_find_any_alias(dir); ++ if (!parent) ++ return NULL; ++ ++ dentry = NULL; ++ spin_lock(&parent->d_lock); ++ list_for_each_entry(d, &parent->d_subdirs, d_child) { ++ /* AuDbg("%pd\n", d); */ ++ spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); ++ dname = &d->d_name; ++ if (dname->len != nlen || memcmp(dname->name, name, nlen)) ++ goto cont_unlock; ++ if (au_di(d)) ++ au_digen_dec(d); ++ else ++ goto cont_unlock; ++ if (au_dcount(d) > 0) { ++ dentry = dget_dlock(d); ++ spin_unlock(&d->d_lock); ++ break; ++ } ++ ++cont_unlock: ++ spin_unlock(&d->d_lock); ++ } ++ spin_unlock(&parent->d_lock); ++ dput(parent); ++ ++ if (dentry) ++ di_write_lock_child(dentry); ++ ++ return dentry; ++} ++ ++static struct inode *lookup_wlock_by_ino(struct super_block *sb, ++ aufs_bindex_t bindex, ino_t h_ino) ++{ ++ struct inode *inode; ++ ino_t ino; ++ int err; ++ ++ inode = NULL; ++ err = au_xino_read(sb, bindex, h_ino, &ino); ++ if (!err && ino) ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warn("wrong root branch\n"); ++ iput(inode); ++ inode = NULL; ++ goto out; ++ } ++ ++ ii_write_lock_child(inode); ++ ++out: ++ return inode; ++} ++ ++static void au_hn_bh(void *_args) ++{ ++ struct au_hnotify_args *a = _args; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend, bfound; ++ unsigned char xino, try_iput; ++ int err; ++ struct inode *inode; ++ ino_t h_ino; ++ struct hn_job_args args; ++ struct dentry *dentry; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(!_args); ++ AuDebugOn(!a->h_dir); ++ AuDebugOn(!a->dir); ++ AuDebugOn(!a->mask); ++ AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", ++ a->mask, a->dir->i_ino, a->h_dir->i_ino, ++ a->h_child_inode ? a->h_child_inode->i_ino : 0); ++ ++ inode = NULL; ++ dentry = NULL; ++ /* ++ * do not lock a->dir->i_mutex here ++ * because of d_revalidate() may cause a deadlock. ++ */ ++ sb = a->dir->i_sb; ++ AuDebugOn(!sb); ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!sbinfo); ++ si_write_lock(sb, AuLock_NOPLMW); ++ ++ ii_read_lock_parent(a->dir); ++ bfound = -1; ++ bend = au_ibend(a->dir); ++ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) ++ if (au_h_iptr(a->dir, bindex) == a->h_dir) { ++ bfound = bindex; ++ break; ++ } ++ ii_read_unlock(a->dir); ++ if (unlikely(bfound < 0)) ++ goto out; ++ ++ xino = !!au_opt_test(au_mntflags(sb), XINO); ++ h_ino = 0; ++ if (a->h_child_inode) ++ h_ino = a->h_child_inode->i_ino; ++ ++ if (a->h_child_nlen ++ && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) ++ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, ++ a->dir); ++ try_iput = 0; ++ if (dentry && d_really_is_positive(dentry)) ++ inode = d_inode(dentry); ++ if (xino && !inode && h_ino ++ && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { ++ inode = lookup_wlock_by_ino(sb, bfound, h_ino); ++ try_iput = 1; ++ } ++ ++ args.flags = a->flags[AuHn_CHILD]; ++ args.dentry = dentry; ++ args.inode = inode; ++ args.h_inode = a->h_child_inode; ++ args.dir = a->dir; ++ args.h_dir = a->h_dir; ++ args.h_name = a->h_child_name; ++ args.h_nlen = a->h_child_nlen; ++ err = hn_job(&args); ++ if (dentry) { ++ if (au_di(dentry)) ++ di_write_unlock(dentry); ++ dput(dentry); ++ } ++ if (inode && try_iput) { ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++ ++ ii_write_lock_parent(a->dir); ++ args.flags = a->flags[AuHn_PARENT]; ++ args.dentry = NULL; ++ args.inode = a->dir; ++ args.h_inode = a->h_dir; ++ args.dir = NULL; ++ args.h_dir = NULL; ++ args.h_name = NULL; ++ args.h_nlen = 0; ++ err = hn_job(&args); ++ ii_write_unlock(a->dir); ++ ++out: ++ iput(a->h_child_inode); ++ iput(a->h_dir); ++ iput(a->dir); ++ si_write_unlock(sb); ++ au_nwt_done(&sbinfo->si_nowait); ++ kfree(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, ++ struct qstr *h_child_qstr, struct inode *h_child_inode) ++{ ++ int err, len; ++ unsigned int flags[AuHnLast], f; ++ unsigned char isdir, isroot, wh; ++ struct inode *dir; ++ struct au_hnotify_args *args; ++ char *p, *h_child_name; ++ ++ err = 0; ++ AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); ++ dir = igrab(hnotify->hn_aufs_inode); ++ if (!dir) ++ goto out; ++ ++ isroot = (dir->i_ino == AUFS_ROOT_INO); ++ wh = 0; ++ h_child_name = (void *)h_child_qstr->name; ++ len = h_child_qstr->len; ++ if (h_child_name) { ++ if (len > AUFS_WH_PFX_LEN ++ && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ h_child_name += AUFS_WH_PFX_LEN; ++ len -= AUFS_WH_PFX_LEN; ++ wh = 1; ++ } ++ } ++ ++ isdir = 0; ++ if (h_child_inode) ++ isdir = !!S_ISDIR(h_child_inode->i_mode); ++ flags[AuHn_PARENT] = AuHnJob_ISDIR; ++ flags[AuHn_CHILD] = 0; ++ if (isdir) ++ flags[AuHn_CHILD] = AuHnJob_ISDIR; ++ au_fset_hnjob(flags[AuHn_PARENT], DIRENT); ++ au_fset_hnjob(flags[AuHn_CHILD], GEN); ++ switch (mask & FS_EVENTS_POSS_ON_CHILD) { ++ case FS_MOVED_FROM: ++ case FS_MOVED_TO: ++ au_fset_hnjob(flags[AuHn_CHILD], XINO0); ++ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); ++ /*FALLTHROUGH*/ ++ case FS_CREATE: ++ AuDebugOn(!h_child_name); ++ break; ++ ++ case FS_DELETE: ++ /* ++ * aufs never be able to get this child inode. ++ * revalidation should be in d_revalidate() ++ * by checking i_nlink, i_generation or d_unhashed(). ++ */ ++ AuDebugOn(!h_child_name); ++ au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); ++ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); ++ break; ++ ++ default: ++ AuDebugOn(1); ++ } ++ ++ if (wh) ++ h_child_inode = NULL; ++ ++ err = -ENOMEM; ++ /* iput() and kfree() will be called in au_hnotify() */ ++ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ iput(dir); ++ goto out; ++ } ++ args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; ++ args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; ++ args->mask = mask; ++ args->dir = dir; ++ args->h_dir = igrab(h_dir); ++ if (h_child_inode) ++ h_child_inode = igrab(h_child_inode); /* can be NULL */ ++ args->h_child_inode = h_child_inode; ++ args->h_child_nlen = len; ++ if (len) { ++ p = (void *)args; ++ p += sizeof(*args); ++ memcpy(p, h_child_name, len); ++ p[len] = 0; ++ } ++ ++ /* NFS fires the event for silly-renamed one from kworker */ ++ f = 0; ++ if (!dir->i_nlink ++ || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) ++ f = AuWkq_NEST; ++ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); ++ if (unlikely(err)) { ++ pr_err("wkq %d\n", err); ++ iput(args->h_child_inode); ++ iput(args->h_dir); ++ iput(args->dir); ++ kfree(args); ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) ++{ ++ int err; ++ ++ AuDebugOn(!(udba & AuOptMask_UDBA)); ++ ++ err = 0; ++ if (au_hnotify_op.reset_br) ++ err = au_hnotify_op.reset_br(udba, br, perm); ++ ++ return err; ++} ++ ++int au_hnotify_init_br(struct au_branch *br, int perm) ++{ ++ int err; ++ ++ err = 0; ++ if (au_hnotify_op.init_br) ++ err = au_hnotify_op.init_br(br, perm); ++ ++ return err; ++} ++ ++void au_hnotify_fin_br(struct au_branch *br) ++{ ++ if (au_hnotify_op.fin_br) ++ au_hnotify_op.fin_br(br); ++} ++ ++static void au_hn_destroy_cache(void) ++{ ++ kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); ++ au_cachep[AuCache_HNOTIFY] = NULL; ++} ++ ++int __init au_hnotify_init(void) ++{ ++ int err; ++ ++ err = -ENOMEM; ++ au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); ++ if (au_cachep[AuCache_HNOTIFY]) { ++ err = 0; ++ if (au_hnotify_op.init) ++ err = au_hnotify_op.init(); ++ if (unlikely(err)) ++ au_hn_destroy_cache(); ++ } ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_hnotify_fin(void) ++{ ++ if (au_hnotify_op.fin) ++ au_hnotify_op.fin(); ++ /* cf. au_cache_fin() */ ++ if (au_cachep[AuCache_HNOTIFY]) ++ au_hn_destroy_cache(); ++} +diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c +new file mode 100644 +index 0000000..20f26b2 +--- /dev/null ++++ b/fs/aufs/i_op.c +@@ -0,0 +1,1434 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (except add/del/rename) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static int h_permission(struct inode *h_inode, int mask, ++ struct vfsmount *h_mnt, int brperm) ++{ ++ int err; ++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ ++ err = -EACCES; ++ if ((write_mask && IS_IMMUTABLE(h_inode)) ++ || ((mask & MAY_EXEC) ++ && S_ISREG(h_inode->i_mode) ++ && ((h_mnt->mnt_flags & MNT_NOEXEC) ++ || !(h_inode->i_mode & S_IXUGO)))) ++ goto out; ++ ++ /* ++ * - skip the lower fs test in the case of write to ro branch. ++ * - nfs dir permission write check is optimized, but a policy for ++ * link/rename requires a real check. ++ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' ++ * in this case, generic_permission() returns -EOPNOTSUPP. ++ */ ++ if ((write_mask && !au_br_writable(brperm)) ++ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) ++ && write_mask && !(mask & MAY_READ)) ++ || !h_inode->i_op->permission) { ++ /* AuLabel(generic_permission); */ ++ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ ++ err = generic_permission(h_inode, mask); ++ if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } else { ++ /* AuLabel(h_inode->permission); */ ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } ++ ++ if (!err) ++ err = devcgroup_inode_permission(h_inode, mask); ++ if (!err) ++ err = security_inode_permission(h_inode, mask); ++ ++#if 0 ++ if (!err) { ++ /* todo: do we need to call ima_path_check()? */ ++ struct path h_path = { ++ .dentry = ++ .mnt = h_mnt ++ }; ++ err = ima_path_check(&h_path, ++ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), ++ IMA_COUNT_LEAVE); ++ } ++#endif ++ ++out: ++ return err; ++} ++ ++static int aufs_permission(struct inode *inode, int mask) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ const unsigned char isdir = !!S_ISDIR(inode->i_mode), ++ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ ++ /* todo: support rcu-walk? */ ++ if (mask & MAY_NOT_BLOCK) ++ return -ECHILD; ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_read_lock_child(inode); ++#if 0 ++ err = au_iigen_test(inode, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out; ++#endif ++ ++ if (!isdir ++ || write_mask ++ || au_opt_test(au_mntflags(sb), DIRPERM1)) { ++ err = au_busy_or_stale(); ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ if (unlikely(!h_inode ++ || (h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT))) ++ goto out; ++ ++ err = 0; ++ bindex = au_ibstart(inode); ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); ++ if (write_mask ++ && !err ++ && !special_file(h_inode->i_mode)) { ++ /* test whether the upper writable branch exists */ ++ err = -EROFS; ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = 0; ++ break; ++ } ++ } ++ goto out; ++ } ++ ++ /* non-write to dir */ ++ err = 0; ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) { ++ err = au_busy_or_stale(); ++ if (unlikely(!S_ISDIR(h_inode->i_mode))) ++ break; ++ ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, au_br_mnt(br), ++ br->br_perm); ++ } ++ } ++ ++out: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int flags) ++{ ++ struct dentry *ret, *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ int err, npositive; ++ ++ IMustLock(dir); ++ ++ /* todo: support rcu-walk? */ ++ ret = ERR_PTR(-ECHILD); ++ if (flags & LOOKUP_RCU) ++ goto out; ++ ++ ret = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_di_init(dentry); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_si; ++ ++ inode = NULL; ++ npositive = 0; /* suppress a warning */ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_alive_dir(parent); ++ if (!err) ++ err = au_digen_test(parent, au_sigen(sb)); ++ if (!err) { ++ npositive = au_lkup_dentry(dentry, au_dbstart(parent), ++ /*type*/0); ++ err = npositive; ++ } ++ di_read_unlock(parent, AuLock_IR); ++ ret = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ ++ if (npositive) { ++ inode = au_new_inode(dentry, /*must_new*/0); ++ if (IS_ERR(inode)) { ++ ret = (void *)inode; ++ inode = NULL; ++ goto out_unlock; ++ } ++ } ++ ++ if (inode) ++ atomic_inc(&inode->i_count); ++ ret = d_splice_alias(inode, dentry); ++#if 0 ++ if (unlikely(d_need_lookup(dentry))) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; ++ spin_unlock(&dentry->d_lock); ++ } else ++#endif ++ if (inode) { ++ if (!IS_ERR(ret)) { ++ iput(inode); ++ if (ret && ret != dentry) ++ ii_write_unlock(inode); ++ } else { ++ ii_write_unlock(inode); ++ iput(inode); ++ inode = NULL; ++ } ++ } ++ ++out_unlock: ++ di_write_unlock(dentry); ++ if (inode) { ++ /* verbose coding for lock class name */ ++ if (unlikely(S_ISLNK(inode->i_mode))) ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcSymlink_DIINFO); ++ else if (unlikely(S_ISDIR(inode->i_mode))) ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcDir_DIINFO); ++ else /* likely */ ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcNonDir_DIINFO); ++ } ++out_si: ++ si_read_unlock(sb); ++out: ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct aopen_node { ++ struct hlist_node hlist; ++ struct file *file, *h_file; ++}; ++ ++static int au_do_aopen(struct inode *inode, struct file *file) ++{ ++ struct au_sphlhead *aopen; ++ struct aopen_node *node; ++ struct au_do_open_args args = { ++ .no_lock = 1, ++ .open = au_do_open_nondir ++ }; ++ ++ aopen = &au_sbi(inode->i_sb)->si_aopen; ++ spin_lock(&aopen->spin); ++ hlist_for_each_entry(node, &aopen->head, hlist) ++ if (node->file == file) { ++ args.h_file = node->h_file; ++ break; ++ } ++ spin_unlock(&aopen->spin); ++ /* AuDebugOn(!args.h_file); */ ++ ++ return au_do_open(file, &args); ++} ++ ++static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, ++ struct file *file, unsigned int open_flag, ++ umode_t create_mode, int *opened) ++{ ++ int err, h_opened = *opened; ++ struct dentry *parent; ++ struct dentry *d; ++ struct au_sphlhead *aopen; ++ struct vfsub_aopen_args args = { ++ .open_flag = open_flag, ++ .create_mode = create_mode, ++ .opened = &h_opened ++ }; ++ struct aopen_node aopen_node = { ++ .file = file ++ }; ++ ++ IMustLock(dir); ++ AuDbg("open_flag 0x%x\n", open_flag); ++ AuDbgDentry(dentry); ++ ++ err = 0; ++ if (!au_di(dentry)) { ++ d = aufs_lookup(dir, dentry, /*flags*/0); ++ if (IS_ERR(d)) { ++ err = PTR_ERR(d); ++ goto out; ++ } else if (d) { ++ /* ++ * obsoleted dentry found. ++ * another error will be returned later. ++ */ ++ d_drop(d); ++ dput(d); ++ AuDbgDentry(d); ++ } ++ AuDbgDentry(dentry); ++ } ++ ++ if (d_is_positive(dentry) ++ || d_unhashed(dentry) ++ || d_unlinked(dentry) ++ || !(open_flag & O_CREAT)) ++ goto out_no_open; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ ++ parent = dentry->d_parent; /* dir is locked */ ++ di_write_lock_parent(parent); ++ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ AuDbgDentry(dentry); ++ if (d_is_positive(dentry)) ++ goto out_unlock; ++ ++ args.file = get_empty_filp(); ++ err = PTR_ERR(args.file); ++ if (IS_ERR(args.file)) ++ goto out_unlock; ++ ++ args.file->f_flags = file->f_flags; ++ err = au_aopen_or_create(dir, dentry, &args); ++ AuTraceErr(err); ++ AuDbgFile(args.file); ++ if (unlikely(err < 0)) { ++ if (h_opened & FILE_OPENED) ++ fput(args.file); ++ else ++ put_filp(args.file); ++ goto out_unlock; ++ } ++ ++ /* some filesystems don't set FILE_CREATED while succeeded? */ ++ *opened |= FILE_CREATED; ++ if (h_opened & FILE_OPENED) ++ aopen_node.h_file = args.file; ++ else { ++ put_filp(args.file); ++ args.file = NULL; ++ } ++ aopen = &au_sbi(dir->i_sb)->si_aopen; ++ au_sphl_add(&aopen_node.hlist, aopen); ++ err = finish_open(file, dentry, au_do_aopen, opened); ++ au_sphl_del(&aopen_node.hlist, aopen); ++ AuTraceErr(err); ++ AuDbgFile(file); ++ if (aopen_node.h_file) ++ fput(aopen_node.h_file); ++ ++out_unlock: ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ AuDbgDentry(dentry); ++ if (unlikely(err)) ++ goto out; ++out_no_open: ++ if (!err && !(*opened & FILE_CREATED)) { ++ AuLabel(out_no_open); ++ dget(dentry); ++ err = finish_no_open(file, dentry); ++ } ++out: ++ AuDbg("%pd%s%s\n", dentry, ++ (*opened & FILE_CREATED) ? " created" : "", ++ (*opened & FILE_OPENED) ? " opened" : ""); ++ AuTraceErr(err); ++ return err; ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, ++ const unsigned char add_entry, aufs_bindex_t bcpup, ++ aufs_bindex_t bstart) ++{ ++ int err; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ ++ if (add_entry) ++ IMustLock(d_inode(parent)); ++ else ++ di_write_lock_parent(parent); ++ ++ err = 0; ++ if (!au_h_dptr(parent, bcpup)) { ++ if (bstart > bcpup) ++ err = au_cpup_dirs(dentry, bcpup); ++ else if (bstart < bcpup) ++ err = au_cpdown_dirs(dentry, bcpup); ++ else ++ BUG(); ++ } ++ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { ++ h_parent = au_h_dptr(parent, bcpup); ++ h_dir = d_inode(h_parent); ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ err = au_lkup_neg(dentry, bcpup, /*wh*/0); ++ /* todo: no unlock here */ ++ mutex_unlock(&h_dir->i_mutex); ++ ++ AuDbg("bcpup %d\n", bcpup); ++ if (!err) { ++ if (d_really_is_negative(dentry)) ++ au_set_h_dptr(dentry, bstart, NULL); ++ au_update_dbrange(dentry, /*do_put_zero*/0); ++ } ++ } ++ ++ if (!add_entry) ++ di_write_unlock(parent); ++ if (!err) ++ err = bcpup; /* success */ ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * decide the branch and the parent dir where we will create a new entry. ++ * returns new bindex or an error. ++ * copyup the parent dir if needed. ++ */ ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args) ++{ ++ int err; ++ unsigned int flags; ++ aufs_bindex_t bcpup, bstart, src_bstart; ++ const unsigned char add_entry ++ = au_ftest_wrdir(args->flags, ADD_ENTRY) ++ | au_ftest_wrdir(args->flags, TMPFILE); ++ struct super_block *sb; ++ struct dentry *parent; ++ struct au_sbinfo *sbinfo; ++ ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(dentry); ++ bcpup = bstart; ++ if (args->force_btgt < 0) { ++ if (src_dentry) { ++ src_bstart = au_dbstart(src_dentry); ++ if (src_bstart < bstart) ++ bcpup = src_bstart; ++ } else if (add_entry) { ++ flags = 0; ++ if (au_ftest_wrdir(args->flags, ISDIR)) ++ au_fset_wbr(flags, DIR); ++ err = AuWbrCreate(sbinfo, dentry, flags); ++ bcpup = err; ++ } ++ ++ if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) { ++ if (add_entry) ++ err = AuWbrCopyup(sbinfo, dentry); ++ else { ++ if (!IS_ROOT(dentry)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(sbinfo, dentry); ++ di_read_unlock(parent, !AuLock_IR); ++ } else ++ err = AuWbrCopyup(sbinfo, dentry); ++ } ++ bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else { ++ bcpup = args->force_btgt; ++ AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry))); ++ } ++ ++ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); ++ err = bcpup; ++ if (bcpup == bstart) ++ goto out; /* success */ ++ ++ /* copyup the new parent into the branch we process */ ++ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); ++ if (err >= 0) { ++ if (d_really_is_negative(dentry)) { ++ au_set_h_dptr(dentry, bstart, NULL); ++ au_set_dbstart(dentry, bcpup); ++ au_set_dbend(dentry, bcpup); ++ } ++ AuDebugOn(add_entry ++ && !au_ftest_wrdir(args->flags, TMPFILE) ++ && !au_h_dptr(dentry, bcpup)); ++ } ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_pin_hdir_unlock(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_hn_imtx_unlock(p->hdir); ++} ++ ++int au_pin_hdir_lock(struct au_pin *p) ++{ ++ int err; ++ ++ err = 0; ++ if (!p->hdir) ++ goto out; ++ ++ /* even if an error happens later, keep this lock */ ++ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); ++ ++ err = -EBUSY; ++ if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent))) ++ goto out; ++ ++ err = 0; ++ if (p->h_dentry) ++ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, ++ p->h_parent, p->br); ++ ++out: ++ return err; ++} ++ ++int au_pin_hdir_relock(struct au_pin *p) ++{ ++ int err, i; ++ struct inode *h_i; ++ struct dentry *h_d[] = { ++ p->h_dentry, ++ p->h_parent ++ }; ++ ++ err = au_pin_hdir_lock(p); ++ if (unlikely(err)) ++ goto out; ++ ++ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { ++ if (!h_d[i]) ++ continue; ++ if (d_is_positive(h_d[i])) { ++ h_i = d_inode(h_d[i]); ++ err = !h_i->i_nlink; ++ } ++ } ++ ++out: ++ return err; ++} ++ ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) ++{ ++#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) ++ p->hdir->hi_inode->i_mutex.owner = task; ++#endif ++} ++ ++void au_pin_hdir_acquire_nest(struct au_pin *p) ++{ ++ if (p->hdir) { ++ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, ++ p->lsc_hi, 0, NULL, _RET_IP_); ++ au_pin_hdir_set_owner(p, current); ++ } ++} ++ ++void au_pin_hdir_release(struct au_pin *p) ++{ ++ if (p->hdir) { ++ au_pin_hdir_set_owner(p, p->task); ++ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); ++ } ++} ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin) ++{ ++ if (pin && pin->parent) ++ return au_h_dptr(pin->parent, pin->bindex); ++ return NULL; ++} ++ ++void au_unpin(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_pin_hdir_unlock(p); ++ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(p->h_mnt); ++ if (!p->hdir) ++ return; ++ ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ iput(p->hdir->hi_inode); ++ dput(p->parent); ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++ /* do not clear p->task */ ++} ++ ++int au_do_pin(struct au_pin *p) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_dir; ++ ++ err = 0; ++ sb = p->dentry->d_sb; ++ p->br = au_sbr(sb, p->bindex); ++ if (IS_ROOT(p->dentry)) { ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_err; ++ } ++ } ++ goto out; ++ } ++ ++ p->h_dentry = NULL; ++ if (p->bindex <= au_dbend(p->dentry)) ++ p->h_dentry = au_h_dptr(p->dentry, p->bindex); ++ ++ p->parent = dget_parent(p->dentry); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_lock(p->parent, AuLock_IR, p->lsc_di); ++ ++ h_dir = NULL; ++ p->h_parent = au_h_dptr(p->parent, p->bindex); ++ p->hdir = au_hi(d_inode(p->parent), p->bindex); ++ if (p->hdir) ++ h_dir = p->hdir->hi_inode; ++ ++ /* ++ * udba case, or ++ * if DI_LOCKED is not set, then p->parent may be different ++ * and h_parent can be NULL. ++ */ ++ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { ++ err = -EBUSY; ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ } ++ ++ au_igrab(h_dir); ++ err = au_pin_hdir_lock(p); ++ if (!err) ++ goto out; /* success */ ++ ++ au_unpin(p); ++ ++out_err: ++ pr_err("err %d\n", err); ++ err = au_busy_or_stale(); ++out: ++ return err; ++} ++ ++void au_pin_init(struct au_pin *p, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags) ++{ ++ p->dentry = dentry; ++ p->udba = udba; ++ p->lsc_di = lsc_di; ++ p->lsc_hi = lsc_hi; ++ p->flags = flags; ++ p->bindex = bindex; ++ ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++ ++ p->h_dentry = NULL; ++ p->h_parent = NULL; ++ p->br = NULL; ++ p->task = current; ++} ++ ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) ++{ ++ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, ++ udba, flags); ++ return au_do_pin(pin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * ->setattr() and ->getattr() are called in various cases. ++ * chmod, stat: dentry is revalidated. ++ * fchmod, fstat: file and dentry are not revalidated, additionally they may be ++ * unhashed. ++ * for ->setattr(), ia->ia_file is passed from ftruncate only. ++ */ ++/* todo: consolidate with do_refresh() and simple_reval_dpath() */ ++int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *parent; ++ ++ err = 0; ++ if (au_digen_test(dentry, sigen)) { ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(dentry, parent); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, ++ struct au_icpup_args *a) ++{ ++ int err; ++ loff_t sz; ++ aufs_bindex_t bstart, ibstart; ++ struct dentry *hi_wh, *parent; ++ struct inode *inode; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = 0 ++ }; ++ ++ if (d_is_dir(dentry)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ /* plink or hi_wh() case */ ++ bstart = au_dbstart(dentry); ++ inode = d_inode(dentry); ++ ibstart = au_ibstart(inode); ++ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) ++ wr_dir_args.force_btgt = ibstart; ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ if (unlikely(err < 0)) ++ goto out; ++ a->btgt = err; ++ if (err != bstart) ++ au_fset_icpup(a->flags, DID_CPUP); ++ ++ err = 0; ++ a->pin_flags = AuPin_MNT_WRITE; ++ parent = NULL; ++ if (!IS_ROOT(dentry)) { ++ au_fset_pin(a->pin_flags, DI_LOCKED); ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ } ++ ++ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ sz = -1; ++ a->h_inode = d_inode(a->h_path.dentry); ++ if (ia && (ia->ia_valid & ATTR_SIZE)) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ if (ia->ia_size < i_size_read(a->h_inode)) ++ sz = ia->ia_size; ++ mutex_unlock(&a->h_inode->i_mutex); ++ } ++ ++ hi_wh = NULL; ++ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { ++ hi_wh = au_hi_wh(inode, a->btgt); ++ if (!hi_wh) { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->btgt, ++ .bsrc = -1, ++ .len = sz, ++ .pin = &a->pin ++ }; ++ err = au_sio_cpup_wh(&cpg, /*file*/NULL); ++ if (unlikely(err)) ++ goto out_unlock; ++ hi_wh = au_hi_wh(inode, a->btgt); ++ /* todo: revalidate hi_wh? */ ++ } ++ } ++ ++ if (parent) { ++ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); ++ di_downgrade_lock(parent, AuLock_IR); ++ dput(parent); ++ parent = NULL; ++ } ++ if (!au_ftest_icpup(a->flags, DID_CPUP)) ++ goto out; /* success */ ++ ++ if (!d_unhashed(dentry)) { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->btgt, ++ .bsrc = bstart, ++ .len = sz, ++ .pin = &a->pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ err = au_sio_cpup_simple(&cpg); ++ if (!err) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ } else if (!hi_wh) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ else ++ a->h_path.dentry = hi_wh; /* do not dget here */ ++ ++out_unlock: ++ a->h_inode = d_inode(a->h_path.dentry); ++ if (!err) ++ goto out; /* success */ ++ au_unpin(&a->pin); ++out_parent: ++ if (parent) { ++ di_write_unlock(parent); ++ dput(parent); ++ } ++out: ++ if (!err) ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ return err; ++} ++ ++static int aufs_setattr(struct dentry *dentry, struct iattr *ia) ++{ ++ int err; ++ struct inode *inode, *delegated; ++ struct super_block *sb; ++ struct file *file; ++ struct au_icpup_args *a; ++ ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ++ ia->ia_valid &= ~ATTR_MODE; ++ ++ file = NULL; ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_kfree; ++ ++ if (ia->ia_valid & ATTR_FILE) { ++ /* currently ftruncate(2) only */ ++ AuDebugOn(!d_is_reg(dentry)); ++ file = ia->ia_file; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out_si; ++ ia->ia_file = au_hf_top(file); ++ a->udba = AuOpt_UDBA_NONE; ++ } else { ++ /* fchmod() doesn't pass ia_file */ ++ a->udba = au_opt_udba(sb); ++ di_write_lock_child(dentry); ++ /* no d_unlinked(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ if (a->udba != AuOpt_UDBA_NONE) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_for_attr(dentry, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out_dentry; ++ } ++ } ++ ++ err = au_pin_and_icpup(dentry, ia, a); ++ if (unlikely(err < 0)) ++ goto out_dentry; ++ if (au_ftest_icpup(a->flags, DID_CPUP)) { ++ ia->ia_file = NULL; ++ ia->ia_valid &= ~ATTR_FILE; ++ } ++ ++ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); ++ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) ++ == (ATTR_MODE | ATTR_CTIME)) { ++ err = security_path_chmod(&a->h_path, ia->ia_mode); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) ++ && (ia->ia_valid & ATTR_CTIME)) { ++ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ ++ if (ia->ia_valid & ATTR_SIZE) { ++ struct file *f; ++ ++ if (ia->ia_size < i_size_read(inode)) ++ /* unmap only */ ++ truncate_setsize(inode, ia->ia_size); ++ ++ f = NULL; ++ if (ia->ia_valid & ATTR_FILE) ++ f = ia->ia_file; ++ mutex_unlock(&a->h_inode->i_mutex); ++ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ } else { ++ delegated = NULL; ++ while (1) { ++ err = vfsub_notify_change(&a->h_path, ia, &delegated); ++ if (delegated) { ++ err = break_deleg_wait(&delegated); ++ if (!err) ++ continue; ++ } ++ break; ++ } ++ } ++ if (!err) ++ au_cpup_attr_changeable(inode); ++ ++out_unlock: ++ mutex_unlock(&a->h_inode->i_mutex); ++ au_unpin(&a->pin); ++ if (unlikely(err)) ++ au_update_dbstart(dentry); ++out_dentry: ++ di_write_unlock(dentry); ++ if (file) { ++ fi_write_unlock(file); ++ ia->ia_file = file; ++ ia->ia_valid |= ATTR_FILE; ++ } ++out_si: ++ si_read_unlock(sb); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) ++static int au_h_path_to_set_attr(struct dentry *dentry, ++ struct au_icpup_args *a, struct path *h_path) ++{ ++ int err; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ a->udba = au_opt_udba(sb); ++ /* no d_unlinked(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ if (a->udba != AuOpt_UDBA_NONE) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_for_attr(dentry, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out; ++ } ++ err = au_pin_and_icpup(dentry, /*ia*/NULL, a); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ h_path->dentry = a->h_path.dentry; ++ h_path->mnt = au_sbr_mnt(sb, a->btgt); ++ ++out: ++ return err; ++} ++ ++ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) ++{ ++ int err; ++ struct path h_path; ++ struct super_block *sb; ++ struct au_icpup_args *a; ++ struct inode *inode, *h_inode; ++ ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_kfree; ++ ++ h_path.dentry = NULL; /* silence gcc */ ++ di_write_lock_child(dentry); ++ err = au_h_path_to_set_attr(dentry, a, &h_path); ++ if (unlikely(err)) ++ goto out_di; ++ ++ mutex_unlock(&a->h_inode->i_mutex); ++ switch (arg->type) { ++ case AU_XATTR_SET: ++ err = vfsub_setxattr(h_path.dentry, ++ arg->u.set.name, arg->u.set.value, ++ arg->u.set.size, arg->u.set.flags); ++ break; ++ case AU_XATTR_REMOVE: ++ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); ++ break; ++ case AU_ACL_SET: ++ err = -EOPNOTSUPP; ++ h_inode = d_inode(h_path.dentry); ++ if (h_inode->i_op->set_acl) ++ err = h_inode->i_op->set_acl(h_inode, ++ arg->u.acl_set.acl, ++ arg->u.acl_set.type); ++ break; ++ } ++ if (!err) ++ au_cpup_attr_timesizes(inode); ++ ++ au_unpin(&a->pin); ++ if (unlikely(err)) ++ au_update_dbstart(dentry); ++ ++out_di: ++ di_write_unlock(dentry); ++ si_read_unlock(sb); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++#endif ++ ++static void au_refresh_iattr(struct inode *inode, struct kstat *st, ++ unsigned int nlink) ++{ ++ unsigned int n; ++ ++ inode->i_mode = st->mode; ++ /* don't i_[ug]id_write() here */ ++ inode->i_uid = st->uid; ++ inode->i_gid = st->gid; ++ inode->i_atime = st->atime; ++ inode->i_mtime = st->mtime; ++ inode->i_ctime = st->ctime; ++ ++ au_cpup_attr_nlink(inode, /*force*/0); ++ if (S_ISDIR(inode->i_mode)) { ++ n = inode->i_nlink; ++ n -= nlink; ++ n += st->nlink; ++ smp_mb(); /* for i_nlink */ ++ /* 0 can happen */ ++ set_nlink(inode, n); ++ } ++ ++ spin_lock(&inode->i_lock); ++ inode->i_blocks = st->blocks; ++ i_size_write(inode, st->size); ++ spin_unlock(&inode->i_lock); ++} ++ ++/* ++ * common routine for aufs_getattr() and aufs_getxattr(). ++ * returns zero or negative (an error). ++ * @dentry will be read-locked in success. ++ */ ++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) ++{ ++ int err; ++ unsigned int mnt_flags, sigen; ++ unsigned char udba_none; ++ aufs_bindex_t bindex; ++ struct super_block *sb, *h_sb; ++ struct inode *inode; ++ ++ h_path->mnt = NULL; ++ h_path->dentry = NULL; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ mnt_flags = au_mntflags(sb); ++ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); ++ ++ /* support fstat(2) */ ++ if (!d_unlinked(dentry) && !udba_none) { ++ sigen = au_sigen(sb); ++ err = au_digen_test(dentry, sigen); ++ if (!err) { ++ di_read_lock_child(dentry, AuLock_IR); ++ err = au_dbrange_test(dentry); ++ if (unlikely(err)) { ++ di_read_unlock(dentry, AuLock_IR); ++ goto out; ++ } ++ } else { ++ AuDebugOn(IS_ROOT(dentry)); ++ di_write_lock_child(dentry); ++ err = au_dbrange_test(dentry); ++ if (!err) ++ err = au_reval_for_attr(dentry, sigen); ++ if (!err) ++ di_downgrade_lock(dentry, AuLock_IR); ++ else { ++ di_write_unlock(dentry); ++ goto out; ++ } ++ } ++ } else ++ di_read_lock_child(dentry, AuLock_IR); ++ ++ inode = d_inode(dentry); ++ bindex = au_ibstart(inode); ++ h_path->mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_path->mnt->mnt_sb; ++ if (!force ++ && !au_test_fs_bad_iattr(h_sb) ++ && udba_none) ++ goto out; /* success */ ++ ++ if (au_dbstart(dentry) == bindex) ++ h_path->dentry = au_h_dptr(dentry, bindex); ++ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { ++ h_path->dentry = au_plink_lkup(inode, bindex); ++ if (IS_ERR(h_path->dentry)) ++ /* pretending success */ ++ h_path->dentry = NULL; ++ else ++ dput(h_path->dentry); ++ } ++ ++out: ++ return err; ++} ++ ++static int aufs_getattr(struct vfsmount *mnt __maybe_unused, ++ struct dentry *dentry, struct kstat *st) ++{ ++ int err; ++ unsigned char positive; ++ struct path h_path; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ inode = d_inode(dentry); ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ err = au_h_path_getattr(dentry, /*force*/0, &h_path); ++ if (unlikely(err)) ++ goto out_si; ++ if (unlikely(!h_path.dentry)) ++ /* illegally overlapped or something */ ++ goto out_fill; /* pretending success */ ++ ++ positive = d_is_positive(h_path.dentry); ++ if (positive) ++ err = vfs_getattr(&h_path, st); ++ if (!err) { ++ if (positive) ++ au_refresh_iattr(inode, st, ++ d_inode(h_path.dentry)->i_nlink); ++ goto out_fill; /* success */ ++ } ++ AuTraceErr(err); ++ goto out_di; ++ ++out_fill: ++ generic_fillattr(inode, st); ++out_di: ++ di_read_unlock(dentry, AuLock_IR); ++out_si: ++ si_read_unlock(sb); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, ++ int bufsiz) ++{ ++ int err; ++ struct super_block *sb; ++ struct dentry *h_dentry; ++ struct inode *inode, *h_inode; ++ ++ err = -EINVAL; ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = d_inode(h_dentry); ++ if (unlikely(!h_inode->i_op->readlink)) ++ goto out; ++ ++ err = security_inode_readlink(h_dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ inode = d_inode(dentry); ++ if (!au_test_ro(sb, bindex, inode)) { ++ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); ++ fsstack_copy_attr_atime(inode, h_inode); ++ } ++ err = h_inode->i_op->readlink(h_dentry, buf, bufsiz); ++ ++out: ++ return err; ++} ++ ++static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ++{ ++ int err; ++ ++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ err = au_d_hashed_positive(dentry); ++ if (!err) ++ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++out: ++ return err; ++} ++ ++static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int err; ++ mm_segment_t old_fs; ++ union { ++ char *k; ++ char __user *u; ++ } buf; ++ ++ err = -ENOMEM; ++ buf.k = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf.k)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_name; ++ ++ err = au_d_hashed_positive(dentry); ++ if (!err) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); ++ set_fs(old_fs); ++ } ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++ if (err >= 0) { ++ buf.k[err] = 0; ++ /* will be freed by put_link */ ++ nd_set_link(nd, buf.k); ++ return NULL; /* success */ ++ } ++ ++out_name: ++ free_page((unsigned long)buf.k); ++out: ++ AuTraceErr(err); ++ return ERR_PTR(err); ++} ++ ++static void aufs_put_link(struct dentry *dentry __maybe_unused, ++ struct nameidata *nd, void *cookie __maybe_unused) ++{ ++ char *p; ++ ++ p = nd_get_link(nd); ++ if (!IS_ERR_OR_NULL(p)) ++ free_page((unsigned long)p); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_inode; ++ ++ sb = inode->i_sb; ++ /* mmap_sem might be acquired already, cf. aufs_mmap() */ ++ lockdep_off(); ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_write_lock_child(inode); ++ lockdep_on(); ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ err = vfsub_update_time(h_inode, ts, flags); ++ lockdep_off(); ++ if (!err) ++ au_cpup_attr_timesizes(inode); ++ ii_write_unlock(inode); ++ si_read_unlock(sb); ++ lockdep_on(); ++ ++ if (!err && (flags & S_VERSION)) ++ inode_inc_iversion(inode); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct inode_operations aufs_symlink_iop = { ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, /* unsupport for symlink? */ ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .readlink = aufs_readlink, ++ .follow_link = aufs_follow_link, ++ .put_link = aufs_put_link, ++ ++ /* .update_time = aufs_update_time */ ++}; ++ ++struct inode_operations aufs_dir_iop = { ++ .create = aufs_create, ++ .lookup = aufs_lookup, ++ .link = aufs_link, ++ .unlink = aufs_unlink, ++ .symlink = aufs_symlink, ++ .mkdir = aufs_mkdir, ++ .rmdir = aufs_rmdir, ++ .mknod = aufs_mknod, ++ .rename = aufs_rename, ++ ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .update_time = aufs_update_time, ++ .atomic_open = aufs_atomic_open, ++ .tmpfile = aufs_tmpfile ++}; ++ ++struct inode_operations aufs_iop = { ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .update_time = aufs_update_time ++}; +diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c +new file mode 100644 +index 0000000..0c3530e +--- /dev/null ++++ b/fs/aufs/i_op_add.c +@@ -0,0 +1,919 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (add entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * final procedure of adding a new entry, except link(2). ++ * remove whiteout, instantiate, copyup the parent dir's times and size ++ * and update version. ++ * if it failed, re-create the removed whiteout. ++ */ ++static int epilog(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct dentry *dentry) ++{ ++ int err, rerr; ++ aufs_bindex_t bwh; ++ struct path h_path; ++ struct super_block *sb; ++ struct inode *inode, *h_dir; ++ struct dentry *wh; ++ ++ bwh = -1; ++ sb = dir->i_sb; ++ if (wh_dentry) { ++ h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ ++ IMustLock(h_dir); ++ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); ++ bwh = au_dbwh(dentry); ++ h_path.dentry = wh_dentry; ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (!IS_ERR(inode)) { ++ d_instantiate(dentry, inode); ++ dir = d_inode(dentry->d_parent); /* dir inode is locked */ ++ IMustLock(dir); ++ au_dir_ts(dir, bindex); ++ dir->i_version++; ++ au_fhsm_wrote(sb, bindex, /*force*/0); ++ return 0; /* success */ ++ } ++ ++ err = PTR_ERR(inode); ++ if (!wh_dentry) ++ goto out; ++ ++ /* revert */ ++ /* dir inode is locked */ ++ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); ++ rerr = PTR_ERR(wh); ++ if (IS_ERR(wh)) { ++ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } else ++ dput(wh); ++ ++out: ++ return err; ++} ++ ++static int au_d_may_add(struct dentry *dentry) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(d_unhashed(dentry))) ++ err = -ENOENT; ++ if (unlikely(d_really_is_positive(dentry))) ++ err = -EEXIST; ++ return err; ++} ++ ++/* ++ * simple tests for the adding inode operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ err = -ENAMETOOLONG; ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (d_really_is_negative(dentry)) { ++ err = -EEXIST; ++ if (unlikely(d_is_positive(h_dentry))) ++ goto out; ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(d_is_negative(h_dentry))) ++ goto out; ++ h_inode = d_inode(h_dentry); ++ if (unlikely(!h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } ++ ++ err = 0; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ err = -EIO; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * initial procedure of adding a new entry. ++ * prepare writable branch and the parent dir, lock it, ++ * and lookup whiteout for the new entry. ++ */ ++static struct dentry* ++lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, ++ struct dentry *src_dentry, struct au_pin *pin, ++ struct au_wr_dir_args *wr_dir_args) ++{ ++ struct dentry *wh_dentry, *h_parent; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ AuDbg("%pd\n", dentry); ++ ++ err = au_wr_dir(dentry, src_dentry, wr_dir_args); ++ bcpup = err; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_parent = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) ++ err = au_may_add(dentry, bcpup, h_parent, ++ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); ++ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ br = au_sbr(sb, bcpup); ++ if (dt) { ++ struct path tmp = { ++ .dentry = h_parent, ++ .mnt = au_br_mnt(br) ++ }; ++ au_dtime_store(dt, au_pinned_parent(pin), &tmp); ++ } ++ ++ wh_dentry = NULL; ++ if (bcpup != au_dbwh(dentry)) ++ goto out; /* success */ ++ ++ /* ++ * ENAMETOOLONG here means that if we allowed create such name, then it ++ * would not be able to removed in the future. So we don't allow such ++ * name here and we don't handle ENAMETOOLONG differently here. ++ */ ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ ++out_unpin: ++ if (IS_ERR(wh_dentry)) ++ au_unpin(pin); ++out: ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { Mknod, Symlink, Creat }; ++struct simple_arg { ++ int type; ++ union { ++ struct { ++ umode_t mode; ++ bool want_excl; ++ bool try_aopen; ++ struct vfsub_aopen_args *aopen; ++ } c; ++ struct { ++ const char *symname; ++ } s; ++ struct { ++ umode_t mode; ++ dev_t dev; ++ } m; ++ } u; ++}; ++ ++static int add_simple(struct inode *dir, struct dentry *dentry, ++ struct simple_arg *arg) ++{ ++ int err, rerr; ++ aufs_bindex_t bstart; ++ unsigned char created; ++ const unsigned char try_aopen ++ = (arg->type == Creat && arg->u.c.try_aopen); ++ struct dentry *wh_dentry, *parent; ++ struct inode *h_dir; ++ struct super_block *sb; ++ struct au_branch *br; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ struct au_wr_dir_args wr_dir_args; ++ } *a; ++ ++ AuDbg("%pd\n", dentry); ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ a->wr_dir_args.force_btgt = -1; ++ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ if (!try_aopen) { ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ } ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ if (!try_aopen) ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, ++ &a->pin, &a->wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ bstart = au_dbstart(dentry); ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bstart); ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ a->h_path.mnt = au_br_mnt(br); ++ h_dir = au_pinned_h_dir(&a->pin); ++ switch (arg->type) { ++ case Creat: ++ err = 0; ++ if (!try_aopen || !h_dir->i_op->atomic_open) ++ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, ++ arg->u.c.want_excl); ++ else ++ err = vfsub_atomic_open(h_dir, a->h_path.dentry, ++ arg->u.c.aopen, br); ++ break; ++ case Symlink: ++ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); ++ break; ++ case Mknod: ++ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, ++ arg->u.m.dev); ++ break; ++ default: ++ BUG(); ++ } ++ created = !err; ++ if (!err) ++ err = epilog(dir, bstart, wh_dentry, dentry); ++ ++ /* revert */ ++ if (unlikely(created && err && d_is_positive(a->h_path.dentry))) { ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, ++ /*force*/0); ++ if (rerr) { ++ AuIOErr("%pd revert failure(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&a->dt); ++ } ++ ++ if (!err && try_aopen && !h_dir->i_op->atomic_open) ++ *arg->u.c.aopen->opened |= FILE_CREATED; ++ ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ ++out_parent: ++ if (!try_aopen) ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ if (!try_aopen) ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} ++ ++int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t dev) ++{ ++ struct simple_arg arg = { ++ .type = Mknod, ++ .u.m = { ++ .mode = mode, ++ .dev = dev ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ++{ ++ struct simple_arg arg = { ++ .type = Symlink, ++ .u.s.symname = symname ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool want_excl) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = mode, ++ .want_excl = want_excl ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int au_aopen_or_create(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *aopen_args) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = aopen_args->create_mode, ++ .want_excl = aopen_args->open_flag & O_EXCL, ++ .try_aopen = true, ++ .aopen = aopen_args ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent, *h_dentry; ++ struct inode *h_dir, *inode; ++ struct vfsmount *h_mnt; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_TMPFILE ++ }; ++ ++ /* copy-up may happen */ ++ mutex_lock(&dir->i_mutex); ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_di_init(dentry); ++ if (unlikely(err)) ++ goto out_si; ++ ++ err = -EBUSY; ++ parent = d_find_any_alias(dir); ++ AuDebugOn(!parent); ++ di_write_lock_parent(parent); ++ if (unlikely(d_inode(parent) != dir)) ++ goto out_parent; ++ ++ err = au_digen_test(parent, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ bindex = au_dbstart(parent); ++ au_set_dbstart(dentry, bindex); ++ au_set_dbend(dentry, bindex); ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ bindex = err; ++ if (unlikely(err < 0)) ++ goto out_parent; ++ ++ err = -EOPNOTSUPP; ++ h_dir = au_h_iptr(dir, bindex); ++ if (unlikely(!h_dir->i_op->tmpfile)) ++ goto out_parent; ++ ++ h_mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mnt_want_write(h_mnt); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ h_parent = au_h_dptr(parent, bindex); ++ err = inode_permission(d_inode(h_parent), MAY_WRITE | MAY_EXEC); ++ if (unlikely(err)) ++ goto out_mnt; ++ ++ err = -ENOMEM; ++ h_dentry = d_alloc(h_parent, &dentry->d_name); ++ if (unlikely(!h_dentry)) ++ goto out_mnt; ++ ++ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); ++ if (unlikely(err)) ++ goto out_dentry; ++ ++ au_set_dbstart(dentry, bindex); ++ au_set_dbend(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, dget(h_dentry)); ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ au_set_h_dptr(dentry, bindex, NULL); ++ au_set_dbstart(dentry, -1); ++ au_set_dbend(dentry, -1); ++ } else { ++ if (!inode->i_nlink) ++ set_nlink(inode, 1); ++ d_tmpfile(dentry, inode); ++ au_di(dentry)->di_tmpfile = 1; ++ ++ /* update without i_mutex */ ++ if (au_ibstart(dir) == au_dbstart(dentry)) ++ au_cpup_attr_timesizes(dir); ++ } ++ ++out_dentry: ++ dput(h_dentry); ++out_mnt: ++ vfsub_mnt_drop_write(h_mnt); ++out_parent: ++ di_write_unlock(parent); ++ dput(parent); ++ di_write_unlock(dentry); ++ if (!err) ++#if 0 ++ /* verbose coding for lock class name */ ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcNonDir_DIINFO); ++#else ++ ; ++#endif ++ else { ++ au_di_fin(dentry); ++ dentry->d_fsdata = NULL; ++ } ++out_si: ++ si_read_unlock(sb); ++out: ++ mutex_unlock(&dir->i_mutex); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_link_args { ++ aufs_bindex_t bdst, bsrc; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *src_parent, *parent; ++}; ++ ++static int au_cpup_before_link(struct dentry *src_dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ struct dentry *h_src_dentry; ++ struct au_cp_generic cpg = { ++ .dentry = src_dentry, ++ .bdst = a->bdst, ++ .bsrc = a->bsrc, ++ .len = -1, ++ .pin = &a->pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ ++ }; ++ ++ di_read_lock_parent(a->src_parent, AuLock_IR); ++ err = au_test_and_cpup_dirs(src_dentry, a->bdst); ++ if (unlikely(err)) ++ goto out; ++ ++ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); ++ err = au_pin(&a->pin, src_dentry, a->bdst, ++ au_opt_udba(src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&a->pin); ++ ++out: ++ di_read_unlock(a->src_parent, AuLock_IR); ++ return err; ++} ++ ++static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ unsigned char plink; ++ aufs_bindex_t bend; ++ struct dentry *h_src_dentry; ++ struct inode *h_inode, *inode, *delegated; ++ struct super_block *sb; ++ struct file *h_file; ++ ++ plink = 0; ++ h_inode = NULL; ++ sb = src_dentry->d_sb; ++ inode = d_inode(src_dentry); ++ if (au_ibstart(inode) <= a->bdst) ++ h_inode = au_h_iptr(inode, a->bdst); ++ if (!h_inode || !h_inode->i_nlink) { ++ /* copyup src_dentry as the name of dentry. */ ++ bend = au_dbend(dentry); ++ if (bend < a->bsrc) ++ au_set_dbend(dentry, a->bsrc); ++ au_set_h_dptr(dentry, a->bsrc, ++ dget(au_h_dptr(src_dentry, a->bsrc))); ++ dget(a->h_path.dentry); ++ au_set_h_dptr(dentry, a->bdst, NULL); ++ AuDbg("temporary d_inode...\n"); ++ spin_lock(&dentry->d_lock); ++ dentry->d_inode = d_inode(src_dentry); /* tmp */ ++ spin_unlock(&dentry->d_lock); ++ h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->bdst, ++ .bsrc = -1, ++ .len = -1, ++ .pin = &a->pin, ++ .flags = AuCpup_KEEPLINO ++ }; ++ err = au_sio_cpup_simple(&cpg); ++ au_h_open_post(dentry, a->bsrc, h_file); ++ if (!err) { ++ dput(a->h_path.dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ } else ++ au_set_h_dptr(dentry, a->bdst, ++ a->h_path.dentry); ++ } ++ spin_lock(&dentry->d_lock); ++ dentry->d_inode = NULL; /* restore */ ++ spin_unlock(&dentry->d_lock); ++ AuDbg("temporary d_inode...done\n"); ++ au_set_h_dptr(dentry, a->bsrc, NULL); ++ au_set_dbend(dentry, bend); ++ } else { ++ /* the inode of src_dentry already exists on a.bdst branch */ ++ h_src_dentry = d_find_alias(h_inode); ++ if (!h_src_dentry && au_plink_test(inode)) { ++ plink = 1; ++ h_src_dentry = au_plink_lkup(inode, a->bdst); ++ err = PTR_ERR(h_src_dentry); ++ if (IS_ERR(h_src_dentry)) ++ goto out; ++ ++ if (unlikely(d_is_negative(h_src_dentry))) { ++ dput(h_src_dentry); ++ h_src_dentry = NULL; ++ } ++ ++ } ++ if (h_src_dentry) { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ dput(h_src_dentry); ++ } else { ++ AuIOErr("no dentry found for hi%lu on b%d\n", ++ h_inode->i_ino, a->bdst); ++ err = -EIO; ++ } ++ } ++ ++ if (!err && !plink) ++ au_plink_append(inode, a->bdst, a->h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ int err, rerr; ++ struct au_dtime dt; ++ struct au_link_args *a; ++ struct dentry *wh_dentry, *h_src_dentry; ++ struct inode *inode, *delegated; ++ struct super_block *sb; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ IMustLock(dir); ++ inode = d_inode(src_dentry); ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->parent = dentry->d_parent; /* dir inode is locked */ ++ err = aufs_read_and_write_lock2(dentry, src_dentry, ++ AuLock_NOPLM | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_kfree; ++ err = au_d_linkable(src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ a->src_parent = dget_parent(src_dentry); ++ wr_dir_args.force_btgt = au_ibstart(inode); ++ ++ di_write_lock_parent(a->parent); ++ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ a->bdst = au_dbstart(dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); ++ a->bsrc = au_ibstart(inode); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) ++ h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); ++ if (!h_src_dentry) { ++ a->bsrc = au_dbstart(src_dentry); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ AuDebugOn(!h_src_dentry); ++ } else if (IS_ERR(h_src_dentry)) { ++ err = PTR_ERR(h_src_dentry); ++ goto out_parent; ++ } ++ ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) ++ err = au_cpup_or_link(src_dentry, dentry, a); ++ else { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ dput(h_src_dentry); ++ } else { ++ /* ++ * copyup src_dentry to the branch we process, ++ * and then link(2) to it. ++ */ ++ dput(h_src_dentry); ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { ++ au_unpin(&a->pin); ++ di_write_unlock(a->parent); ++ err = au_cpup_before_link(src_dentry, a); ++ di_write_lock_parent(a->parent); ++ if (!err) ++ err = au_pin(&a->pin, dentry, a->bdst, ++ au_opt_udba(sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_wh; ++ } ++ if (!err) { ++ h_src_dentry = au_h_dptr(src_dentry, a->bdst); ++ err = -ENOENT; ++ if (h_src_dentry && d_is_positive(h_src_dentry)) { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, ++ au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry" ++ " for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ } ++ } ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ if (wh_dentry) { ++ a->h_path.dentry = wh_dentry; ++ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out_revert; ++ } ++ ++ au_dir_ts(dir, a->bdst); ++ dir->i_version++; ++ inc_nlink(inode); ++ inode->i_ctime = dir->i_ctime; ++ d_instantiate(dentry, au_igrab(inode)); ++ if (d_unhashed(a->h_path.dentry)) ++ /* some filesystem calls d_drop() */ ++ d_drop(dentry); ++ /* some filesystems consume an inode even hardlink */ ++ au_fhsm_wrote(sb, a->bdst, /*force*/0); ++ goto out_unpin; /* success */ ++ ++out_revert: ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, ++ /*delegated*/NULL, /*force*/0); ++ if (unlikely(rerr)) { ++ AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++out_unpin: ++ au_unpin(&a->pin); ++out_wh: ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(a->parent); ++ dput(a->src_parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_and_write_unlock2(dentry, src_dentry); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ unsigned char diropq; ++ struct path h_path; ++ struct dentry *wh_dentry, *parent, *opq_dentry; ++ struct mutex *h_mtx; ++ struct super_block *sb; ++ struct { ++ struct au_pin pin; ++ struct au_dtime dt; ++ } *a; /* reduce the stack usage */ ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR ++ }; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, ++ &a->pin, &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ sb = dentry->d_sb; ++ bindex = au_dbstart(dentry); ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ /* make the dir opaque */ ++ diropq = 0; ++ h_mtx = &d_inode(h_path.dentry)->i_mutex; ++ if (wh_dentry ++ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ opq_dentry = au_diropq_create(dentry, bindex); ++ mutex_unlock(h_mtx); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out_dir; ++ dput(opq_dentry); ++ diropq = 1; ++ } ++ ++ err = epilog(dir, bindex, wh_dentry, dentry); ++ if (!err) { ++ inc_nlink(dir); ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (diropq) { ++ AuLabel(revert opq); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bindex); ++ mutex_unlock(h_mtx); ++ if (rerr) { ++ AuIOErr("%pd reverting diropq failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ } ++ ++out_dir: ++ AuLabel(revert dir); ++ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); ++ if (rerr) { ++ AuIOErr("%pd reverting dir failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&a->dt); ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} +diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c +new file mode 100644 +index 0000000..3cd4c7b +--- /dev/null ++++ b/fs/aufs/i_op_del.c +@@ -0,0 +1,497 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (del entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * decide if a new whiteout for @dentry is necessary or not. ++ * when it is necessary, prepare the parent dir for the upper branch whose ++ * branch index is @bcpup for creation. the actual creation of the whiteout will ++ * be done by caller. ++ * return value: ++ * 0: wh is unnecessary ++ * plus: wh is necessary ++ * minus: error ++ */ ++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) ++{ ++ int need_wh, err; ++ aufs_bindex_t bstart; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ if (*bcpup < 0) { ++ *bcpup = bstart; ++ if (au_test_ro(sb, bstart, d_inode(dentry))) { ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ *bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else ++ AuDebugOn(bstart < *bcpup ++ || au_test_ro(sb, *bcpup, d_inode(dentry))); ++ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); ++ ++ if (*bcpup != bstart) { ++ err = au_cpup_dirs(dentry, *bcpup); ++ if (unlikely(err)) ++ goto out; ++ need_wh = 1; ++ } else { ++ struct au_dinfo *dinfo, *tmp; ++ ++ need_wh = -ENOMEM; ++ dinfo = au_di(dentry); ++ tmp = au_di_alloc(sb, AuLsc_DI_TMP); ++ if (tmp) { ++ au_di_cp(tmp, dinfo); ++ au_di_swap(tmp, dinfo); ++ /* returns the number of positive dentries */ ++ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); ++ au_di_swap(tmp, dinfo); ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ } ++ } ++ AuDbg("need_wh %d\n", need_wh); ++ err = need_wh; ++ ++out: ++ return err; ++} ++ ++/* ++ * simple tests for the del-entry operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry, *h_latest; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (d_really_is_positive(dentry)) { ++ err = -ENOENT; ++ if (unlikely(d_is_negative(h_dentry))) ++ goto out; ++ h_inode = d_inode(h_dentry); ++ if (unlikely(!h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(d_is_positive(h_dentry))) ++ goto out; ++ } ++ ++ err = -ENOENT; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ goto out; ++ err = 0; ++ ++ /* ++ * rmdir a dir may break the consistency on some filesystem. ++ * let's try heavy test. ++ */ ++ err = -EACCES; ++ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) ++ && au_test_h_perm(d_inode(h_parent), ++ MAY_EXEC | MAY_WRITE))) ++ goto out; ++ ++ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); ++ err = -EIO; ++ if (IS_ERR(h_latest)) ++ goto out; ++ if (h_latest == h_dentry) ++ err = 0; ++ dput(h_latest); ++ ++out: ++ return err; ++} ++ ++/* ++ * decide the branch where we operate for @dentry. the branch index will be set ++ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent ++ * dir for reverting. ++ * when a new whiteout is necessary, create it. ++ */ ++static struct dentry* ++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, ++ struct au_dtime *dt, struct au_pin *pin) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ struct path h_path; ++ int err, need_wh; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); ++ wh_dentry = ERR_PTR(need_wh); ++ if (unlikely(need_wh < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ bcpup = *rbcpup; ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path.dentry = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) { ++ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ } ++ ++ h_path.mnt = au_sbr_mnt(sb, bcpup); ++ au_dtime_store(dt, au_pinned_parent(pin), &h_path); ++ wh_dentry = NULL; ++ if (!need_wh) ++ goto out; /* success, no need to create whiteout */ ++ ++ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_unpin; ++ ++ /* returns with the parent is locked and wh_dentry is dget-ed */ ++ goto out; /* success */ ++ ++out_unpin: ++ au_unpin(pin); ++out: ++ return wh_dentry; ++} ++ ++/* ++ * when removing a dir, rename it to a unique temporary whiteout-ed name first ++ * in order to be revertible and save time for removing many child whiteouts ++ * under the dir. ++ * returns 1 when there are too many child whiteout and caller should remove ++ * them asynchronously. returns 0 when the number of children is enough small to ++ * remove now or the branch fs is a remote fs. ++ * otherwise return an error. ++ */ ++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, ++ struct au_nhash *whlist, struct inode *dir) ++{ ++ int rmdir_later, err, dirwh; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ sb = dentry->d_sb; ++ SiMustAnyLock(sb); ++ h_dentry = au_h_dptr(dentry, bindex); ++ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); ++ if (unlikely(err)) ++ goto out; ++ ++ /* stop monitoring */ ++ inode = d_inode(dentry); ++ au_hn_free(au_hi(inode, bindex)); ++ ++ if (!au_test_fs_remote(h_dentry->d_sb)) { ++ dirwh = au_sbi(sb)->si_dirwh; ++ rmdir_later = (dirwh <= 1); ++ if (!rmdir_later) ++ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, ++ dirwh); ++ if (rmdir_later) ++ return rmdir_later; ++ } ++ ++ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); ++ if (unlikely(err)) { ++ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", ++ h_dentry, bindex, err); ++ err = 0; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * final procedure for deleting a entry. ++ * maintain dentry and iattr. ++ */ ++static void epilog(struct inode *dir, struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ struct inode *inode; ++ ++ inode = d_inode(dentry); ++ d_drop(dentry); ++ inode->i_ctime = dir->i_ctime; ++ ++ au_dir_ts(dir, bindex); ++ dir->i_version++; ++} ++ ++/* ++ * when an error happened, remove the created whiteout and revert everything. ++ */ ++static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, ++ aufs_bindex_t bwh, struct dentry *wh_dentry, ++ struct dentry *dentry, struct au_dtime *dt) ++{ ++ int rerr; ++ struct path h_path = { ++ .dentry = wh_dentry, ++ .mnt = au_sbr_mnt(dir->i_sb, bindex) ++ }; ++ ++ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); ++ if (!rerr) { ++ au_set_dbwh(dentry, bwh); ++ au_dtime_revert(dt); ++ return 0; ++ } ++ ++ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct inode *inode, *h_dir, *delegated; ++ struct dentry *parent, *wh_dentry; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ } *a; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_d_hashed_positive(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ err = -EISDIR; ++ if (unlikely(d_is_dir(dentry))) ++ goto out_unlock; /* possible? */ ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, ++ &a->pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ dget(a->h_path.dentry); ++ if (bindex == bstart) { ++ h_dir = au_pinned_h_dir(&a->pin); ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ } else { ++ /* dir inode is locked */ ++ h_dir = d_inode(wh_dentry->d_parent); ++ IMustLock(h_dir); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_drop_nlink(inode); ++ epilog(dir, dentry, bindex); ++ ++ /* update target timestamps */ ++ if (bindex == bstart) { ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); ++ /*ignore*/ ++ inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; ++ } else ++ /* todo: this timestamp may be reverted later */ ++ inode->i_ctime = h_dir->i_ctime; ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, ++ &a->dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ dput(a->h_path.dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} ++ ++int aufs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err, rmdir_later; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct inode *inode; ++ struct dentry *parent, *wh_dentry, *h_dentry; ++ struct au_whtmp_rmdir *args; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ } *a; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = d_inode(dentry); ++ IMustLock(inode); ++ err = -ENOTDIR; ++ if (unlikely(!d_is_dir(dentry))) ++ goto out_unlock; /* possible? */ ++ ++ err = -ENOMEM; ++ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); ++ if (unlikely(!args)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ err = au_test_empty(dentry, &args->whlist); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, ++ &a->pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ h_dentry = au_h_dptr(dentry, bstart); ++ dget(h_dentry); ++ rmdir_later = 0; ++ if (bindex == bstart) { ++ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); ++ if (err > 0) { ++ rmdir_later = err; ++ err = 0; ++ } ++ } else { ++ /* stop monitoring */ ++ au_hn_free(au_hi(inode, bstart)); ++ ++ /* dir inode is locked */ ++ IMustLock(d_inode(wh_dentry->d_parent)); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_dead_dir(inode); ++ au_set_dbdiropq(dentry, -1); ++ epilog(dir, dentry, bindex); ++ ++ if (rmdir_later) { ++ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); ++ args = NULL; ++ } ++ ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ AuLabel(revert); ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, ++ &a->dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ dput(h_dentry); ++out_parent: ++ di_write_unlock(parent); ++ if (args) ++ au_whtmp_rmdir_free(args); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c +new file mode 100644 +index 0000000..1323012 +--- /dev/null ++++ b/fs/aufs/i_op_ren.c +@@ -0,0 +1,1004 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operation (rename entry) ++ * todo: this is crazy monster ++ */ ++ ++#include "aufs.h" ++ ++enum { AuSRC, AuDST, AuSrcDst }; ++enum { AuPARENT, AuCHILD, AuParentChild }; ++ ++#define AuRen_ISDIR 1 ++#define AuRen_ISSAMEDIR (1 << 1) ++#define AuRen_WHSRC (1 << 2) ++#define AuRen_WHDST (1 << 3) ++#define AuRen_MNT_WRITE (1 << 4) ++#define AuRen_DT_DSTDIR (1 << 5) ++#define AuRen_DIROPQ (1 << 6) ++#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) ++#define au_fset_ren(flags, name) \ ++ do { (flags) |= AuRen_##name; } while (0) ++#define au_fclr_ren(flags, name) \ ++ do { (flags) &= ~AuRen_##name; } while (0) ++ ++struct au_ren_args { ++ struct { ++ struct dentry *dentry, *h_dentry, *parent, *h_parent, ++ *wh_dentry; ++ struct inode *dir, *inode; ++ struct au_hinode *hdir; ++ struct au_dtime dt[AuParentChild]; ++ aufs_bindex_t bstart; ++ } sd[AuSrcDst]; ++ ++#define src_dentry sd[AuSRC].dentry ++#define src_dir sd[AuSRC].dir ++#define src_inode sd[AuSRC].inode ++#define src_h_dentry sd[AuSRC].h_dentry ++#define src_parent sd[AuSRC].parent ++#define src_h_parent sd[AuSRC].h_parent ++#define src_wh_dentry sd[AuSRC].wh_dentry ++#define src_hdir sd[AuSRC].hdir ++#define src_h_dir sd[AuSRC].hdir->hi_inode ++#define src_dt sd[AuSRC].dt ++#define src_bstart sd[AuSRC].bstart ++ ++#define dst_dentry sd[AuDST].dentry ++#define dst_dir sd[AuDST].dir ++#define dst_inode sd[AuDST].inode ++#define dst_h_dentry sd[AuDST].h_dentry ++#define dst_parent sd[AuDST].parent ++#define dst_h_parent sd[AuDST].h_parent ++#define dst_wh_dentry sd[AuDST].wh_dentry ++#define dst_hdir sd[AuDST].hdir ++#define dst_h_dir sd[AuDST].hdir->hi_inode ++#define dst_dt sd[AuDST].dt ++#define dst_bstart sd[AuDST].bstart ++ ++ struct dentry *h_trap; ++ struct au_branch *br; ++ struct au_hinode *src_hinode; ++ struct path h_path; ++ struct au_nhash whlist; ++ aufs_bindex_t btgt, src_bwh, src_bdiropq; ++ ++ unsigned int flags; ++ ++ struct au_whtmp_rmdir *thargs; ++ struct dentry *h_dst; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * functions for reverting. ++ * when an error happened in a single rename systemcall, we should revert ++ * everything as if nothing happend. ++ * we don't need to revert the copied-up/down the parent dir since they are ++ * harmless. ++ */ ++ ++#define RevertFailure(fmt, ...) do { \ ++ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ ++ ##__VA_ARGS__, err, rerr); \ ++ err = -EIO; \ ++} while (0) ++ ++static void au_ren_rev_diropq(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); ++ if (rerr) ++ RevertFailure("remove diropq %pd", a->src_dentry); ++} ++ ++static void au_ren_rev_rename(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ struct inode *delegated; ++ ++ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, ++ a->src_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lkup one %pd", a->src_dentry); ++ return; ++ } ++ ++ delegated = NULL; ++ rerr = vfsub_rename(a->dst_h_dir, ++ au_h_dptr(a->src_dentry, a->btgt), ++ a->src_h_dir, &a->h_path, &delegated); ++ if (unlikely(rerr == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ ++ if (rerr) ++ RevertFailure("rename %pd", a->src_dentry); ++} ++ ++static void au_ren_rev_whtmp(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ struct inode *delegated; ++ ++ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, ++ a->dst_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lkup one %pd", a->dst_dentry); ++ return; ++ } ++ if (d_is_positive(a->h_path.dentry)) { ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ return; ++ } ++ ++ delegated = NULL; ++ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, ++ &delegated); ++ if (unlikely(rerr == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ if (!rerr) ++ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); ++ else ++ RevertFailure("rename %pd", a->h_dst); ++} ++ ++static void au_ren_rev_whsrc(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = a->src_wh_dentry; ++ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); ++ au_set_dbwh(a->src_dentry, a->src_bwh); ++ if (rerr) ++ RevertFailure("unlink %pd", a->src_wh_dentry); ++} ++#undef RevertFailure ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * when we have to copyup the renaming entry, do it with the rename-target name ++ * in order to minimize the cost (the later actual rename is unnecessary). ++ * otherwise rename it on the target branch. ++ */ ++static int au_ren_or_cpup(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d; ++ struct inode *delegated; ++ ++ d = a->src_dentry; ++ if (au_dbstart(d) == a->btgt) { ++ a->h_path.dentry = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, DIROPQ) ++ && au_dbdiropq(d) == a->btgt) ++ au_fclr_ren(a->flags, DIROPQ); ++ AuDebugOn(au_dbstart(d) != a->btgt); ++ delegated = NULL; ++ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), ++ a->dst_h_dir, &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ } else ++ BUG(); ++ ++ if (!err && a->h_dst) ++ /* it will be set to dinfo later */ ++ dget(a->h_dst); ++ ++ return err; ++} ++ ++/* cf. aufs_rmdir() */ ++static int au_ren_del_whtmp(struct au_ren_args *a) ++{ ++ int err; ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ SiMustAnyLock(dir->i_sb); ++ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, ++ au_sbi(dir->i_sb)->si_dirwh) ++ || au_test_fs_remote(a->h_dst->d_sb)) { ++ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); ++ if (unlikely(err)) ++ pr_warn("failed removing whtmp dir %pd (%d), " ++ "ignored.\n", a->h_dst, err); ++ } else { ++ au_nhash_wh_free(&a->thargs->whlist); ++ a->thargs->whlist = a->whlist; ++ a->whlist.nh_num = 0; ++ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); ++ dput(a->h_dst); ++ a->thargs = NULL; ++ } ++ ++ return 0; ++} ++ ++/* make it 'opaque' dir. */ ++static int au_ren_diropq(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *diropq; ++ ++ err = 0; ++ a->src_bdiropq = au_dbdiropq(a->src_dentry); ++ a->src_hinode = au_hi(a->src_inode, a->btgt); ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ diropq = au_diropq_create(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ if (IS_ERR(diropq)) ++ err = PTR_ERR(diropq); ++ else ++ dput(diropq); ++ ++ return err; ++} ++ ++static int do_rename(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d, *h_d; ++ ++ /* prepare workqueue args for asynchronous rmdir */ ++ h_d = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) && d_is_positive(h_d)) { ++ err = -ENOMEM; ++ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); ++ if (unlikely(!a->thargs)) ++ goto out; ++ a->h_dst = dget(h_d); ++ } ++ ++ /* create whiteout for src_dentry */ ++ if (au_ftest_ren(a->flags, WHSRC)) { ++ a->src_bwh = au_dbwh(a->src_dentry); ++ AuDebugOn(a->src_bwh >= 0); ++ a->src_wh_dentry ++ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); ++ err = PTR_ERR(a->src_wh_dentry); ++ if (IS_ERR(a->src_wh_dentry)) ++ goto out_thargs; ++ } ++ ++ /* lookup whiteout for dentry */ ++ if (au_ftest_ren(a->flags, WHDST)) { ++ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, ++ a->br); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out_whsrc; ++ if (d_is_negative(h_d)) ++ dput(h_d); ++ else ++ a->dst_wh_dentry = h_d; ++ } ++ ++ /* rename dentry to tmpwh */ ++ if (a->thargs) { ++ err = au_whtmp_ren(a->dst_h_dentry, a->br); ++ if (unlikely(err)) ++ goto out_whdst; ++ ++ d = a->dst_dentry; ++ au_set_h_dptr(d, a->btgt, NULL); ++ err = au_lkup_neg(d, a->btgt, /*wh*/0); ++ if (unlikely(err)) ++ goto out_whtmp; ++ a->dst_h_dentry = au_h_dptr(d, a->btgt); ++ } ++ ++ BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_bstart != a->btgt); ++ ++ /* rename by vfs_rename or cpup */ ++ d = a->dst_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) ++ && (a->dst_wh_dentry ++ || au_dbdiropq(d) == a->btgt ++ /* hide the lower to keep xino */ ++ || a->btgt < au_dbend(d) ++ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) ++ au_fset_ren(a->flags, DIROPQ); ++ err = au_ren_or_cpup(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_whtmp; ++ ++ /* make dir opaque */ ++ if (au_ftest_ren(a->flags, DIROPQ)) { ++ err = au_ren_diropq(a); ++ if (unlikely(err)) ++ goto out_rename; ++ } ++ ++ /* update target timestamps */ ++ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); ++ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ ++ a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; ++ ++ /* remove whiteout for dentry */ ++ if (a->dst_wh_dentry) { ++ a->h_path.dentry = a->dst_wh_dentry; ++ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, ++ a->dst_dentry); ++ if (unlikely(err)) ++ goto out_diropq; ++ } ++ ++ /* remove whtmp */ ++ if (a->thargs) ++ au_ren_del_whtmp(a); /* ignore this error */ ++ ++ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); ++ err = 0; ++ goto out_success; ++ ++out_diropq: ++ if (au_ftest_ren(a->flags, DIROPQ)) ++ au_ren_rev_diropq(err, a); ++out_rename: ++ au_ren_rev_rename(err, a); ++ dput(a->h_dst); ++out_whtmp: ++ if (a->thargs) ++ au_ren_rev_whtmp(err, a); ++out_whdst: ++ dput(a->dst_wh_dentry); ++ a->dst_wh_dentry = NULL; ++out_whsrc: ++ if (a->src_wh_dentry) ++ au_ren_rev_whsrc(err, a); ++out_success: ++ dput(a->src_wh_dentry); ++ dput(a->dst_wh_dentry); ++out_thargs: ++ if (a->thargs) { ++ dput(a->h_dst); ++ au_whtmp_rmdir_free(a->thargs); ++ a->thargs = NULL; ++ } ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if @dentry dir can be rename destination or not. ++ * success means, it is a logically empty dir. ++ */ ++static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ return au_test_empty(dentry, whlist); ++} ++ ++/* ++ * test if @dentry dir can be rename source or not. ++ * if it can, return 0 and @children is filled. ++ * success means, ++ * - it is a logically empty dir. ++ * - or, it exists on writable branch and has no children including whiteouts ++ * on the lower branch. ++ */ ++static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ int err; ++ unsigned int rdhash; ++ aufs_bindex_t bstart; ++ ++ bstart = au_dbstart(dentry); ++ if (bstart != btgt) { ++ struct au_nhash whlist; ++ ++ SiMustAnyLock(dentry->d_sb); ++ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, ++ dentry)); ++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_test_empty(dentry, &whlist); ++ au_nhash_wh_free(&whlist); ++ goto out; ++ } ++ ++ if (bstart == au_dbtaildir(dentry)) ++ return 0; /* success */ ++ ++ err = au_test_empty_lower(dentry); ++ ++out: ++ if (err == -ENOTEMPTY) { ++ AuWarn1("renaming dir who has child(ren) on multiple branches," ++ " is not supported\n"); ++ err = -EXDEV; ++ } ++ return err; ++} ++ ++/* side effect: sets whlist and h_dentry */ ++static int au_ren_may_dir(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int rdhash; ++ struct dentry *d; ++ ++ d = a->dst_dentry; ++ SiMustAnyLock(d->d_sb); ++ ++ err = 0; ++ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { ++ rdhash = au_sbi(d->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); ++ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ au_set_dbstart(d, a->dst_bstart); ++ err = may_rename_dstdir(d, &a->whlist); ++ au_set_dbstart(d, a->btgt); ++ } ++ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (unlikely(err)) ++ goto out; ++ ++ d = a->src_dentry; ++ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ err = may_rename_srcdir(d, a->btgt); ++ if (unlikely(err)) { ++ au_nhash_wh_free(&a->whlist); ++ a->whlist.nh_num = 0; ++ } ++ } ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * simple tests for rename. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++static int au_may_ren(struct au_ren_args *a) ++{ ++ int err, isdir; ++ struct inode *h_inode; ++ ++ if (a->src_bstart == a->btgt) { ++ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, ++ au_ftest_ren(a->flags, ISDIR)); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ if (unlikely(a->src_h_dentry == a->h_trap)) ++ goto out; ++ } ++ ++ err = 0; ++ if (a->dst_bstart != a->btgt) ++ goto out; ++ ++ err = -ENOTEMPTY; ++ if (unlikely(a->dst_h_dentry == a->h_trap)) ++ goto out; ++ ++ err = -EIO; ++ isdir = !!au_ftest_ren(a->flags, ISDIR); ++ if (d_really_is_negative(a->dst_dentry)) { ++ if (d_is_negative(a->dst_h_dentry)) ++ err = au_may_add(a->dst_dentry, a->btgt, ++ a->dst_h_parent, isdir); ++ } else { ++ if (unlikely(d_is_negative(a->dst_h_dentry))) ++ goto out; ++ h_inode = d_inode(a->dst_h_dentry); ++ if (h_inode->i_nlink) ++ err = au_may_del(a->dst_dentry, a->btgt, ++ a->dst_h_parent, isdir); ++ } ++ ++out: ++ if (unlikely(err == -ENOENT || err == -EEXIST)) ++ err = -EIO; ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * locking order ++ * (VFS) ++ * - src_dir and dir by lock_rename() ++ * - inode if exitsts ++ * (aufs) ++ * - lock all ++ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, ++ * + si_read_lock ++ * + di_write_lock2_child() ++ * + di_write_lock_child() ++ * + ii_write_lock_child() ++ * + di_write_lock_child2() ++ * + ii_write_lock_child2() ++ * + src_parent and parent ++ * + di_write_lock_parent() ++ * + ii_write_lock_parent() ++ * + di_write_lock_parent2() ++ * + ii_write_lock_parent2() ++ * + lower src_dir and dir by vfsub_lock_rename() ++ * + verify the every relationships between child and parent. if any ++ * of them failed, unlock all and return -EBUSY. ++ */ ++static void au_ren_unlock(struct au_ren_args *a) ++{ ++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++} ++ ++static int au_ren_lock(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int udba; ++ ++ err = 0; ++ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); ++ a->src_hdir = au_hi(a->src_dir, a->btgt); ++ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); ++ a->dst_hdir = au_hi(a->dst_dir, a->btgt); ++ ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out; ++ au_fset_ren(a->flags, MNT_WRITE); ++ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ udba = au_opt_udba(a->src_dentry->d_sb); ++ if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) ++ || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) ++ err = au_busy_or_stale(); ++ if (!err && au_dbstart(a->src_dentry) == a->btgt) ++ err = au_h_verify(a->src_h_dentry, udba, ++ d_inode(a->src_h_parent), a->src_h_parent, ++ a->br); ++ if (!err && au_dbstart(a->dst_dentry) == a->btgt) ++ err = au_h_verify(a->dst_h_dentry, udba, ++ d_inode(a->dst_h_parent), a->dst_h_parent, ++ a->br); ++ if (!err) ++ goto out; /* success */ ++ ++ err = au_busy_or_stale(); ++ au_ren_unlock(a); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_ren_refresh_dir(struct au_ren_args *a) ++{ ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ /* is this updating defined in POSIX? */ ++ au_cpup_attr_timesizes(a->src_inode); ++ au_cpup_attr_nlink(dir, /*force*/1); ++ } ++ ++ au_dir_ts(dir, a->btgt); ++ ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ return; ++ ++ dir = a->src_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_cpup_attr_nlink(dir, /*force*/1); ++ au_dir_ts(dir, a->btgt); ++} ++ ++static void au_ren_refresh(struct au_ren_args *a) ++{ ++ aufs_bindex_t bend, bindex; ++ struct dentry *d, *h_d; ++ struct inode *i, *h_i; ++ struct super_block *sb; ++ ++ d = a->dst_dentry; ++ d_drop(d); ++ if (a->h_dst) ++ /* already dget-ed by au_ren_or_cpup() */ ++ au_set_h_dptr(d, a->btgt, a->h_dst); ++ ++ i = a->dst_inode; ++ if (i) { ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ vfsub_drop_nlink(i); ++ else { ++ vfsub_dead_dir(i); ++ au_cpup_attr_timesizes(i); ++ } ++ au_update_dbrange(d, /*do_put_zero*/1); ++ } else { ++ bend = a->btgt; ++ for (bindex = au_dbstart(d); bindex < bend; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ bend = au_dbend(d); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ au_update_dbrange(d, /*do_put_zero*/0); ++ } ++ ++ d = a->src_dentry; ++ au_set_dbwh(d, -1); ++ bend = au_dbend(d); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_d = au_h_dptr(d, bindex); ++ if (h_d) ++ au_set_h_dptr(d, bindex, NULL); ++ } ++ au_set_dbend(d, a->btgt); ++ ++ sb = d->d_sb; ++ i = a->src_inode; ++ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) ++ return; /* success */ ++ ++ bend = au_ibend(i); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(i, bindex); ++ if (h_i) { ++ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); ++ /* ignore this error */ ++ au_set_h_iptr(i, bindex, NULL, 0); ++ } ++ } ++ au_set_ibend(i, a->btgt); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mainly for link(2) and rename(2) */ ++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ aufs_bindex_t bdiropq, bwh; ++ struct dentry *parent; ++ struct au_branch *br; ++ ++ parent = dentry->d_parent; ++ IMustLock(d_inode(parent)); /* dir is locked */ ++ ++ bdiropq = au_dbdiropq(parent); ++ bwh = au_dbwh(dentry); ++ br = au_sbr(dentry->d_sb, btgt); ++ if (au_br_rdonly(br) ++ || (0 <= bdiropq && bdiropq < btgt) ++ || (0 <= bwh && bwh < btgt)) ++ btgt = -1; ++ ++ AuDbg("btgt %d\n", btgt); ++ return btgt; ++} ++ ++/* sets src_bstart, dst_bstart and btgt */ ++static int au_ren_wbr(struct au_ren_args *a) ++{ ++ int err; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ a->src_bstart = au_dbstart(a->src_dentry); ++ a->dst_bstart = au_dbstart(a->dst_dentry); ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ wr_dir_args.force_btgt = a->src_bstart; ++ if (a->dst_inode && a->dst_bstart < a->src_bstart) ++ wr_dir_args.force_btgt = a->dst_bstart; ++ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); ++ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); ++ a->btgt = err; ++ ++ return err; ++} ++ ++static void au_ren_dt(struct au_ren_args *a) ++{ ++ a->h_path.dentry = a->src_h_parent; ++ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { ++ a->h_path.dentry = a->dst_h_parent; ++ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); ++ } ++ ++ au_fclr_ren(a->flags, DT_DSTDIR); ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ return; ++ ++ a->h_path.dentry = a->src_h_dentry; ++ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); ++ if (d_is_positive(a->dst_h_dentry)) { ++ au_fset_ren(a->flags, DT_DSTDIR); ++ a->h_path.dentry = a->dst_h_dentry; ++ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); ++ } ++} ++ ++static void au_ren_rev_dt(int err, struct au_ren_args *a) ++{ ++ struct dentry *h_d; ++ struct mutex *h_mtx; ++ ++ au_dtime_revert(a->src_dt + AuPARENT); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) ++ au_dtime_revert(a->dst_dt + AuPARENT); ++ ++ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { ++ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &d_inode(h_d)->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->src_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ ++ if (au_ftest_ren(a->flags, DT_DSTDIR)) { ++ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &d_inode(h_d)->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->dst_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ } ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, ++ struct inode *_dst_dir, struct dentry *_dst_dentry) ++{ ++ int err, flags; ++ /* reduce stack space */ ++ struct au_ren_args *a; ++ ++ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); ++ IMustLock(_src_dir); ++ IMustLock(_dst_dir); ++ ++ err = -ENOMEM; ++ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->src_dir = _src_dir; ++ a->src_dentry = _src_dentry; ++ a->src_inode = NULL; ++ if (d_really_is_positive(a->src_dentry)) ++ a->src_inode = d_inode(a->src_dentry); ++ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ ++ a->dst_dir = _dst_dir; ++ a->dst_dentry = _dst_dentry; ++ a->dst_inode = NULL; ++ if (d_really_is_positive(a->dst_dentry)) ++ a->dst_inode = d_inode(a->dst_dentry); ++ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ ++ if (a->dst_inode) { ++ IMustLock(a->dst_inode); ++ au_igrab(a->dst_inode); ++ } ++ ++ err = -ENOTDIR; ++ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; ++ if (d_is_dir(a->src_dentry)) { ++ au_fset_ren(a->flags, ISDIR); ++ if (unlikely(d_really_is_positive(a->dst_dentry) ++ && !d_is_dir(a->dst_dentry))) ++ goto out_free; ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ AuLock_DIR | flags); ++ } else ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ flags); ++ if (unlikely(err)) ++ goto out_free; ++ ++ err = au_d_hashed_positive(a->src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = -ENOENT; ++ if (a->dst_inode) { ++ /* ++ * If it is a dir, VFS unhash dst_dentry before this ++ * function. It means we cannot rely upon d_unhashed(). ++ */ ++ if (unlikely(!a->dst_inode->i_nlink)) ++ goto out_unlock; ++ if (!S_ISDIR(a->dst_inode->i_mode)) { ++ err = au_d_hashed_positive(a->dst_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if (unlikely(IS_DEADDIR(a->dst_inode))) ++ goto out_unlock; ++ } else if (unlikely(d_unhashed(a->dst_dentry))) ++ goto out_unlock; ++ ++ /* ++ * is it possible? ++ * yes, it happend (in linux-3.3-rcN) but I don't know why. ++ * there may exist a problem somewhere else. ++ */ ++ err = -EINVAL; ++ if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) ++ goto out_unlock; ++ ++ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ ++ di_write_lock_parent(a->dst_parent); ++ ++ /* which branch we process */ ++ err = au_ren_wbr(a); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); ++ a->h_path.mnt = au_br_mnt(a->br); ++ ++ /* are they available to be renamed */ ++ err = au_ren_may_dir(a); ++ if (unlikely(err)) ++ goto out_children; ++ ++ /* prepare the writable parent dir on the same branch */ ++ if (a->dst_bstart == a->btgt) { ++ au_fset_ren(a->flags, WHDST); ++ } else { ++ err = au_cpup_dirs(a->dst_dentry, a->btgt); ++ if (unlikely(err)) ++ goto out_children; ++ } ++ ++ if (a->src_dir != a->dst_dir) { ++ /* ++ * this temporary unlock is safe, ++ * because both dir->i_mutex are locked. ++ */ ++ di_write_unlock(a->dst_parent); ++ di_write_lock_parent(a->src_parent); ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ di_write_unlock(a->src_parent); ++ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); ++ au_fclr_ren(a->flags, ISSAMEDIR); ++ } else ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ if (unlikely(err < 0)) ++ goto out_children; ++ if (err) ++ au_fset_ren(a->flags, WHSRC); ++ ++ /* cpup src */ ++ if (a->src_bstart != a->btgt) { ++ struct au_pin pin; ++ ++ err = au_pin(&pin, a->src_dentry, a->btgt, ++ au_opt_udba(a->src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) { ++ struct au_cp_generic cpg = { ++ .dentry = a->src_dentry, ++ .bdst = a->btgt, ++ .bsrc = a->src_bstart, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ } ++ if (unlikely(err)) ++ goto out_children; ++ a->src_bstart = a->btgt; ++ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); ++ au_fset_ren(a->flags, WHSRC); ++ } ++ ++ /* lock them all */ ++ err = au_ren_lock(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_children; ++ ++ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) ++ err = au_may_ren(a); ++ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ if (unlikely(err)) ++ goto out_hdir; ++ ++ /* store timestamps to be revertible */ ++ au_ren_dt(a); ++ ++ /* here we go */ ++ err = do_rename(a); ++ if (unlikely(err)) ++ goto out_dt; ++ ++ /* update dir attributes */ ++ au_ren_refresh_dir(a); ++ ++ /* dput/iput all lower dentries */ ++ au_ren_refresh(a); ++ ++ goto out_hdir; /* success */ ++ ++out_dt: ++ au_ren_rev_dt(err, a); ++out_hdir: ++ au_ren_unlock(a); ++out_children: ++ au_nhash_wh_free(&a->whlist); ++ if (err && a->dst_inode && a->dst_bstart != a->btgt) { ++ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); ++ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); ++ au_set_dbstart(a->dst_dentry, a->dst_bstart); ++ } ++out_parent: ++ if (!err) ++ d_move(a->src_dentry, a->dst_dentry); ++ else { ++ au_update_dbstart(a->dst_dentry); ++ if (!a->dst_inode) ++ d_drop(a->dst_dentry); ++ } ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ di_write_unlock(a->dst_parent); ++ else ++ di_write_unlock2(a->src_parent, a->dst_parent); ++out_unlock: ++ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); ++out_free: ++ iput(a->dst_inode); ++ if (a->thargs) ++ au_whtmp_rmdir_free(a->thargs); ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c +new file mode 100644 +index 0000000..b8efd45 +--- /dev/null ++++ b/fs/aufs/iinfo.c +@@ -0,0 +1,264 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode private data ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct inode *h_inode; ++ ++ IiMustAnyLock(inode); ++ ++ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ return h_inode; ++} ++ ++/* todo: hard/soft set? */ ++void au_hiput(struct au_hinode *hinode) ++{ ++ au_hn_free(hinode); ++ dput(hinode->hi_whdentry); ++ iput(hinode->hi_inode); ++} ++ ++unsigned int au_hi_flags(struct inode *inode, int isdir) ++{ ++ unsigned int flags; ++ const unsigned int mnt_flags = au_mntflags(inode->i_sb); ++ ++ flags = 0; ++ if (au_opt_test(mnt_flags, XINO)) ++ au_fset_hi(flags, XINO); ++ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) ++ au_fset_hi(flags, HNOTIFY); ++ return flags; ++} ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags) ++{ ++ struct au_hinode *hinode; ++ struct inode *hi; ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ IiMustWriteLock(inode); ++ ++ hinode = iinfo->ii_hinode + bindex; ++ hi = hinode->hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ ++ if (hi) ++ au_hiput(hinode); ++ hinode->hi_inode = h_inode; ++ if (h_inode) { ++ int err; ++ struct super_block *sb = inode->i_sb; ++ struct au_branch *br; ++ ++ AuDebugOn(inode->i_mode ++ && (h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT)); ++ if (bindex == iinfo->ii_bstart) ++ au_cpup_igen(inode, h_inode); ++ br = au_sbr(sb, bindex); ++ hinode->hi_id = br->br_id; ++ if (au_ftest_hi(flags, XINO)) { ++ err = au_xino_write(sb, bindex, h_inode->i_ino, ++ inode->i_ino); ++ if (unlikely(err)) ++ AuIOErr1("failed au_xino_write() %d\n", err); ++ } ++ ++ if (au_ftest_hi(flags, HNOTIFY) ++ && au_br_hnotifyable(br->br_perm)) { ++ err = au_hn_alloc(hinode, inode); ++ if (unlikely(err)) ++ AuIOErr1("au_hn_alloc() %d\n", err); ++ } ++ } ++} ++ ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh) ++{ ++ struct au_hinode *hinode; ++ ++ IiMustWriteLock(inode); ++ ++ hinode = au_ii(inode)->ii_hinode + bindex; ++ AuDebugOn(hinode->hi_whdentry); ++ hinode->hi_whdentry = h_wh; ++} ++ ++void au_update_iigen(struct inode *inode, int half) ++{ ++ struct au_iinfo *iinfo; ++ struct au_iigen *iigen; ++ unsigned int sigen; ++ ++ sigen = au_sigen(inode->i_sb); ++ iinfo = au_ii(inode); ++ iigen = &iinfo->ii_generation; ++ spin_lock(&iinfo->ii_genspin); ++ iigen->ig_generation = sigen; ++ if (half) ++ au_ig_fset(iigen->ig_flags, HALF_REFRESHED); ++ else ++ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); ++ spin_unlock(&iinfo->ii_genspin); ++} ++ ++/* it may be called at remount time, too */ ++void au_update_ibrange(struct inode *inode, int do_put_zero) ++{ ++ struct au_iinfo *iinfo; ++ aufs_bindex_t bindex, bend; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ ++ IiMustWriteLock(inode); ++ ++ if (do_put_zero && iinfo->ii_bstart >= 0) { ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++) { ++ struct inode *h_i; ++ ++ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; ++ if (h_i ++ && !h_i->i_nlink ++ && !(h_i->i_state & I_LINKABLE)) ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ } ++ } ++ ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ bend = au_sbend(inode->i_sb); ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (iinfo->ii_hinode[0 + bindex].hi_inode) { ++ iinfo->ii_bstart = bindex; ++ break; ++ } ++ if (iinfo->ii_bstart >= 0) ++ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) ++ if (iinfo->ii_hinode[0 + bindex].hi_inode) { ++ iinfo->ii_bend = bindex; ++ break; ++ } ++ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_icntnr_init_once(void *_c) ++{ ++ struct au_icntnr *c = _c; ++ struct au_iinfo *iinfo = &c->iinfo; ++ static struct lock_class_key aufs_ii; ++ ++ spin_lock_init(&iinfo->ii_genspin); ++ au_rw_init(&iinfo->ii_rwsem); ++ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); ++ inode_init_once(&c->vfs_inode); ++} ++ ++int au_iinfo_init(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ int nbr, i; ++ ++ sb = inode->i_sb; ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ nbr = au_sbend(sb) + 1; ++ if (unlikely(nbr <= 0)) ++ nbr = 1; ++ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); ++ if (iinfo->ii_hinode) { ++ au_ninodes_inc(sb); ++ for (i = 0; i < nbr; i++) ++ iinfo->ii_hinode[i].hi_id = -1; ++ ++ iinfo->ii_generation.ig_generation = au_sigen(sb); ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ iinfo->ii_vdir = NULL; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hinode *hip; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*hip) * (iinfo->ii_bend + 1); ++ if (!sz) ++ sz = sizeof(*hip); ++ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); ++ if (hip) { ++ iinfo->ii_hinode = hip; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++void au_iinfo_fin(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ struct au_hinode *hi; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ const unsigned char unlinked = !inode->i_nlink; ++ ++ iinfo = au_ii(inode); ++ /* bad_inode case */ ++ if (!iinfo) ++ return; ++ ++ sb = inode->i_sb; ++ au_ninodes_dec(sb); ++ if (si_pid_test(sb)) ++ au_xino_delete_inode(inode, unlinked); ++ else { ++ /* ++ * it is safe to hide the dependency between sbinfo and ++ * sb->s_umount. ++ */ ++ lockdep_off(); ++ si_noflush_read_lock(sb); ++ au_xino_delete_inode(inode, unlinked); ++ si_read_unlock(sb); ++ lockdep_on(); ++ } ++ ++ if (iinfo->ii_vdir) ++ au_vdir_free(iinfo->ii_vdir); ++ ++ bindex = iinfo->ii_bstart; ++ if (bindex >= 0) { ++ hi = iinfo->ii_hinode + bindex; ++ bend = iinfo->ii_bend; ++ while (bindex++ <= bend) { ++ if (hi->hi_inode) ++ au_hiput(hi); ++ hi++; ++ } ++ } ++ kfree(iinfo->ii_hinode); ++ iinfo->ii_hinode = NULL; ++ AuRwDestroy(&iinfo->ii_rwsem); ++} +diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c +new file mode 100644 +index 0000000..559da33 +--- /dev/null ++++ b/fs/aufs/inode.c +@@ -0,0 +1,487 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode functions ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_igrab(struct inode *inode) ++{ ++ if (inode) { ++ AuDebugOn(!atomic_read(&inode->i_count)); ++ ihold(inode); ++ } ++ return inode; ++} ++ ++static void au_refresh_hinode_attr(struct inode *inode, int do_version) ++{ ++ au_cpup_attr_all(inode, /*force*/0); ++ au_update_iigen(inode, /*half*/1); ++ if (do_version) ++ inode->i_version++; ++} ++ ++static int au_ii_refresh(struct inode *inode, int *update) ++{ ++ int err, e; ++ umode_t type; ++ aufs_bindex_t bindex, new_bindex; ++ struct super_block *sb; ++ struct au_iinfo *iinfo; ++ struct au_hinode *p, *q, tmp; ++ ++ IiMustWriteLock(inode); ++ ++ *update = 0; ++ sb = inode->i_sb; ++ type = inode->i_mode & S_IFMT; ++ iinfo = au_ii(inode); ++ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ ++ AuDebugOn(iinfo->ii_bstart < 0); ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++, p++) { ++ if (!p->hi_inode) ++ continue; ++ ++ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); ++ new_bindex = au_br_index(sb, p->hi_id); ++ if (new_bindex == bindex) ++ continue; ++ ++ if (new_bindex < 0) { ++ *update = 1; ++ au_hiput(p); ++ p->hi_inode = NULL; ++ continue; ++ } ++ ++ if (new_bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = new_bindex; ++ if (iinfo->ii_bend < new_bindex) ++ iinfo->ii_bend = new_bindex; ++ /* swap two lower inode, and loop again */ ++ q = iinfo->ii_hinode + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hi_inode) { ++ bindex--; ++ p--; ++ } ++ } ++ au_update_ibrange(inode, /*do_put_zero*/0); ++ e = au_dy_irefresh(inode); ++ if (unlikely(e && !err)) ++ err = e; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_refresh_hinode_self(struct inode *inode) ++{ ++ int err, update; ++ ++ err = au_ii_refresh(inode, &update); ++ if (!err) ++ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry) ++{ ++ int err, e, update; ++ unsigned int flags; ++ umode_t mode; ++ aufs_bindex_t bindex, bend; ++ unsigned char isdir; ++ struct au_hinode *p; ++ struct au_iinfo *iinfo; ++ ++ err = au_ii_refresh(inode, &update); ++ if (unlikely(err)) ++ goto out; ++ ++ update = 0; ++ iinfo = au_ii(inode); ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ mode = (inode->i_mode & S_IFMT); ++ isdir = S_ISDIR(mode); ++ flags = au_hi_flags(inode, isdir); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ struct inode *h_i, *h_inode; ++ struct dentry *h_d; ++ ++ h_d = au_h_dptr(dentry, bindex); ++ if (!h_d || d_is_negative(h_d)) ++ continue; ++ ++ h_inode = d_inode(h_d); ++ AuDebugOn(mode != (h_inode->i_mode & S_IFMT)); ++ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { ++ h_i = au_h_iptr(inode, bindex); ++ if (h_i) { ++ if (h_i == h_inode) ++ continue; ++ err = -EIO; ++ break; ++ } ++ } ++ if (bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = bindex; ++ if (iinfo->ii_bend < bindex) ++ iinfo->ii_bend = bindex; ++ au_set_h_iptr(inode, bindex, au_igrab(h_inode), flags); ++ update = 1; ++ } ++ au_update_ibrange(inode, /*do_put_zero*/0); ++ e = au_dy_irefresh(inode); ++ if (unlikely(e && !err)) ++ err = e; ++ if (!err) ++ au_refresh_hinode_attr(inode, update && isdir); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int set_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ unsigned int flags; ++ umode_t mode; ++ aufs_bindex_t bindex, bstart, btail; ++ unsigned char isdir; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct au_iinfo *iinfo; ++ ++ IiMustWriteLock(inode); ++ ++ err = 0; ++ isdir = 0; ++ bstart = au_dbstart(dentry); ++ h_dentry = au_h_dptr(dentry, bstart); ++ h_inode = d_inode(h_dentry); ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ inode->i_fop = &aufs_file_fop; ++ err = au_dy_iaop(inode, bstart, h_inode); ++ if (unlikely(err)) ++ goto out; ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ btail = au_dbtaildir(dentry); ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ break; ++ case S_IFLNK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_symlink_iop; ++ break; ++ case S_IFBLK: ++ case S_IFCHR: ++ case S_IFIFO: ++ case S_IFSOCK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ init_special_inode(inode, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown file type 0%o\n", mode); ++ err = -EIO; ++ goto out; ++ } ++ ++ /* do not set hnotify for whiteouted dirs (SHWH mode) */ ++ flags = au_hi_flags(inode, isdir); ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) ++ && au_ftest_hi(flags, HNOTIFY) ++ && dentry->d_name.len > AUFS_WH_PFX_LEN ++ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) ++ au_fclr_hi(flags, HNOTIFY); ++ iinfo = au_ii(inode); ++ iinfo->ii_bstart = bstart; ++ iinfo->ii_bend = btail; ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) ++ au_set_h_iptr(inode, bindex, ++ au_igrab(d_inode(h_dentry)), flags); ++ } ++ au_cpup_attr_all(inode, /*force*/1); ++ /* ++ * to force calling aufs_get_acl() every time, ++ * do not call cache_no_acl() for aufs inode. ++ */ ++ ++out: ++ return err; ++} ++ ++/* ++ * successful returns with iinfo write_locked ++ * minus: errno ++ * zero: success, matched ++ * plus: no error, but unmatched ++ */ ++static int reval_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ unsigned int gen; ++ struct au_iigen iigen; ++ aufs_bindex_t bindex, bend; ++ struct inode *h_inode, *h_dinode; ++ struct dentry *h_dentry; ++ ++ /* ++ * before this function, if aufs got any iinfo lock, it must be only ++ * one, the parent dir. ++ * it can happen by UDBA and the obsoleted inode number. ++ */ ++ err = -EIO; ++ if (unlikely(inode->i_ino == parent_ino(dentry))) ++ goto out; ++ ++ err = 1; ++ ii_write_lock_new_child(inode); ++ h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); ++ h_dinode = d_inode(h_dentry); ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (!h_inode || h_inode != h_dinode) ++ continue; ++ ++ err = 0; ++ gen = au_iigen(inode, &iigen); ++ if (gen == au_digen(dentry) ++ && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) ++ break; ++ ++ /* fully refresh inode using dentry */ ++ err = au_refresh_hinode(inode, dentry); ++ if (!err) ++ au_update_iigen(inode, /*half*/0); ++ break; ++ } ++ ++ if (unlikely(err)) ++ ii_write_unlock(inode); ++out: ++ return err; ++} ++ ++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino) ++{ ++ int err; ++ struct mutex *mtx; ++ ++ /* prevent hardlinked inode number from race condition */ ++ mtx = NULL; ++ if (d_type != DT_DIR) { ++ mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; ++ mutex_lock(mtx); ++ } ++ err = au_xino_read(sb, bindex, h_ino, ino); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!*ino) { ++ err = -EIO; ++ *ino = au_xino_new_ino(sb); ++ if (unlikely(!*ino)) ++ goto out; ++ err = au_xino_write(sb, bindex, h_ino, *ino); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++out: ++ if (mtx) ++ mutex_unlock(mtx); ++ return err; ++} ++ ++/* successful returns with iinfo write_locked */ ++/* todo: return with unlocked? */ ++struct inode *au_new_inode(struct dentry *dentry, int must_new) ++{ ++ struct inode *inode, *h_inode; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ struct mutex *mtx; ++ ino_t h_ino, ino; ++ int err; ++ aufs_bindex_t bstart; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ h_dentry = au_h_dptr(dentry, bstart); ++ h_inode = d_inode(h_dentry); ++ h_ino = h_inode->i_ino; ++ ++ /* ++ * stop 'race'-ing between hardlinks under different ++ * parents. ++ */ ++ mtx = NULL; ++ if (!d_is_dir(h_dentry)) ++ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; ++ ++new_ino: ++ if (mtx) ++ mutex_lock(mtx); ++ err = au_xino_read(sb, bstart, h_ino, &ino); ++ inode = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!ino) { ++ ino = au_xino_new_ino(sb); ++ if (unlikely(!ino)) { ++ inode = ERR_PTR(-EIO); ++ goto out; ++ } ++ } ++ ++ AuDbg("i%lu\n", (unsigned long)ino); ++ inode = au_iget_locked(sb, ino); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); ++ if (inode->i_state & I_NEW) { ++ /* verbose coding for lock class name */ ++ if (unlikely(d_is_symlink(h_dentry))) ++ au_rw_class(&au_ii(inode)->ii_rwsem, ++ au_lc_key + AuLcSymlink_IIINFO); ++ else if (unlikely(d_is_dir(h_dentry))) ++ au_rw_class(&au_ii(inode)->ii_rwsem, ++ au_lc_key + AuLcDir_IIINFO); ++ else /* likely */ ++ au_rw_class(&au_ii(inode)->ii_rwsem, ++ au_lc_key + AuLcNonDir_IIINFO); ++ ++ ii_write_lock_new_child(inode); ++ err = set_inode(inode, dentry); ++ if (!err) { ++ unlock_new_inode(inode); ++ goto out; /* success */ ++ } ++ ++ /* ++ * iget_failed() calls iput(), but we need to call ++ * ii_write_unlock() after iget_failed(). so dirty hack for ++ * i_count. ++ */ ++ atomic_inc(&inode->i_count); ++ iget_failed(inode); ++ ii_write_unlock(inode); ++ au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ /* ignore this error */ ++ goto out_iput; ++ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { ++ /* ++ * horrible race condition between lookup, readdir and copyup ++ * (or something). ++ */ ++ if (mtx) ++ mutex_unlock(mtx); ++ err = reval_inode(inode, dentry); ++ if (unlikely(err < 0)) { ++ mtx = NULL; ++ goto out_iput; ++ } ++ ++ if (!err) { ++ mtx = NULL; ++ goto out; /* success */ ++ } else if (mtx) ++ mutex_lock(mtx); ++ } ++ ++ if (unlikely(au_test_fs_unique_ino(h_inode))) ++ AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," ++ " b%d, %s, %pd, hi%lu, i%lu.\n", ++ bstart, au_sbtype(h_dentry->d_sb), dentry, ++ (unsigned long)h_ino, (unsigned long)ino); ++ ino = 0; ++ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ if (!err) { ++ iput(inode); ++ if (mtx) ++ mutex_unlock(mtx); ++ goto new_ino; ++ } ++ ++out_iput: ++ iput(inode); ++ inode = ERR_PTR(err); ++out: ++ if (mtx) ++ mutex_unlock(mtx); ++ return inode; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode) ++{ ++ int err; ++ struct inode *hi; ++ ++ err = au_br_rdonly(au_sbr(sb, bindex)); ++ ++ /* pseudo-link after flushed may happen out of bounds */ ++ if (!err ++ && inode ++ && au_ibstart(inode) <= bindex ++ && bindex <= au_ibend(inode)) { ++ /* ++ * permission check is unnecessary since vfsub routine ++ * will be called later ++ */ ++ hi = au_h_iptr(inode, bindex); ++ if (hi) ++ err = IS_IMMUTABLE(hi) ? -EROFS : 0; ++ } ++ ++ return err; ++} ++ ++int au_test_h_perm(struct inode *h_inode, int mask) ++{ ++ if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) ++ return 0; ++ return inode_permission(h_inode, mask); ++} ++ ++int au_test_h_perm_sio(struct inode *h_inode, int mask) ++{ ++ if (au_test_nfs(h_inode->i_sb) ++ && (mask & MAY_WRITE) ++ && S_ISDIR(h_inode->i_mode)) ++ mask |= MAY_READ; /* force permission check */ ++ return au_test_h_perm(h_inode, mask); ++} +diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h +new file mode 100644 +index 0000000..003534a +--- /dev/null ++++ b/fs/aufs/inode.h +@@ -0,0 +1,660 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations ++ */ ++ ++#ifndef __AUFS_INODE_H__ ++#define __AUFS_INODE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "rwsem.h" ++ ++struct vfsmount; ++ ++struct au_hnotify { ++#ifdef CONFIG_AUFS_HNOTIFY ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ /* never use fsnotify_add_vfsmount_mark() */ ++ struct fsnotify_mark hn_mark; ++#endif ++ struct inode *hn_aufs_inode; /* no get/put */ ++#endif ++} ____cacheline_aligned_in_smp; ++ ++struct au_hinode { ++ struct inode *hi_inode; ++ aufs_bindex_t hi_id; ++#ifdef CONFIG_AUFS_HNOTIFY ++ struct au_hnotify *hi_notify; ++#endif ++ ++ /* reference to the copied-up whiteout with get/put */ ++ struct dentry *hi_whdentry; ++}; ++ ++/* ig_flags */ ++#define AuIG_HALF_REFRESHED 1 ++#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) ++#define au_ig_fset(flags, name) \ ++ do { (flags) |= AuIG_##name; } while (0) ++#define au_ig_fclr(flags, name) \ ++ do { (flags) &= ~AuIG_##name; } while (0) ++ ++struct au_iigen { ++ __u32 ig_generation, ig_flags; ++}; ++ ++struct au_vdir; ++struct au_iinfo { ++ spinlock_t ii_genspin; ++ struct au_iigen ii_generation; ++ struct super_block *ii_hsb1; /* no get/put */ ++ ++ struct au_rwsem ii_rwsem; ++ aufs_bindex_t ii_bstart, ii_bend; ++ __u32 ii_higen; ++ struct au_hinode *ii_hinode; ++ struct au_vdir *ii_vdir; ++}; ++ ++struct au_icntnr { ++ struct au_iinfo iinfo; ++ struct inode vfs_inode; ++} ____cacheline_aligned_in_smp; ++ ++/* au_pin flags */ ++#define AuPin_DI_LOCKED 1 ++#define AuPin_MNT_WRITE (1 << 1) ++#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) ++#define au_fset_pin(flags, name) \ ++ do { (flags) |= AuPin_##name; } while (0) ++#define au_fclr_pin(flags, name) \ ++ do { (flags) &= ~AuPin_##name; } while (0) ++ ++struct au_pin { ++ /* input */ ++ struct dentry *dentry; ++ unsigned int udba; ++ unsigned char lsc_di, lsc_hi, flags; ++ aufs_bindex_t bindex; ++ ++ /* output */ ++ struct dentry *parent; ++ struct au_hinode *hdir; ++ struct vfsmount *h_mnt; ++ ++ /* temporary unlock/relock for copyup */ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct task_struct *task; ++}; ++ ++void au_pin_hdir_unlock(struct au_pin *p); ++int au_pin_hdir_lock(struct au_pin *p); ++int au_pin_hdir_relock(struct au_pin *p); ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); ++void au_pin_hdir_acquire_nest(struct au_pin *p); ++void au_pin_hdir_release(struct au_pin *p); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_iinfo *au_ii(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ if (iinfo->ii_hinode) ++ return iinfo; ++ return NULL; /* debugging bad_inode case */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* inode.c */ ++struct inode *au_igrab(struct inode *inode); ++int au_refresh_hinode_self(struct inode *inode); ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry); ++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino); ++struct inode *au_new_inode(struct dentry *dentry, int must_new); ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode); ++int au_test_h_perm(struct inode *h_inode, int mask); ++int au_test_h_perm_sio(struct inode *h_inode, int mask); ++ ++static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, ++ ino_t h_ino, unsigned int d_type, ino_t *ino) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ return au_ino(sb, bindex, h_ino, d_type, ino); ++#else ++ return 0; ++#endif ++} ++ ++/* i_op.c */ ++extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; ++ ++/* au_wr_dir flags */ ++#define AuWrDir_ADD_ENTRY 1 ++#define AuWrDir_ISDIR (1 << 1) ++#define AuWrDir_TMPFILE (1 << 2) ++#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) ++#define au_fset_wrdir(flags, name) \ ++ do { (flags) |= AuWrDir_##name; } while (0) ++#define au_fclr_wrdir(flags, name) \ ++ do { (flags) &= ~AuWrDir_##name; } while (0) ++ ++struct au_wr_dir_args { ++ aufs_bindex_t force_btgt; ++ unsigned char flags; ++}; ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args); ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin); ++void au_pin_init(struct au_pin *pin, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags); ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) __must_check; ++int au_do_pin(struct au_pin *pin) __must_check; ++void au_unpin(struct au_pin *pin); ++int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); ++ ++#define AuIcpup_DID_CPUP 1 ++#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) ++#define au_fset_icpup(flags, name) \ ++ do { (flags) |= AuIcpup_##name; } while (0) ++#define au_fclr_icpup(flags, name) \ ++ do { (flags) &= ~AuIcpup_##name; } while (0) ++ ++struct au_icpup_args { ++ unsigned char flags; ++ unsigned char pin_flags; ++ aufs_bindex_t btgt; ++ unsigned int udba; ++ struct au_pin pin; ++ struct path h_path; ++ struct inode *h_inode; ++}; ++ ++int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, ++ struct au_icpup_args *a); ++ ++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path); ++ ++/* i_op_add.c */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t dev); ++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); ++int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool want_excl); ++struct vfsub_aopen_args; ++int au_aopen_or_create(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *args); ++int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry); ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); ++ ++/* i_op_del.c */ ++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++int aufs_unlink(struct inode *dir, struct dentry *dentry); ++int aufs_rmdir(struct inode *dir, struct dentry *dentry); ++ ++/* i_op_ren.c */ ++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); ++int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, ++ struct inode *dir, struct dentry *dentry); ++ ++/* iinfo.c */ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); ++void au_hiput(struct au_hinode *hinode); ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh); ++unsigned int au_hi_flags(struct inode *inode, int isdir); ++ ++/* hinode flags */ ++#define AuHi_XINO 1 ++#define AuHi_HNOTIFY (1 << 1) ++#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) ++#define au_fset_hi(flags, name) \ ++ do { (flags) |= AuHi_##name; } while (0) ++#define au_fclr_hi(flags, name) \ ++ do { (flags) &= ~AuHi_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuHi_HNOTIFY ++#define AuHi_HNOTIFY 0 ++#endif ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags); ++ ++void au_update_iigen(struct inode *inode, int half); ++void au_update_ibrange(struct inode *inode, int do_put_zero); ++ ++void au_icntnr_init_once(void *_c); ++int au_iinfo_init(struct inode *inode); ++void au_iinfo_fin(struct inode *inode); ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr); ++ ++#ifdef CONFIG_PROC_FS ++/* plink.c */ ++int au_plink_maint(struct super_block *sb, int flags); ++struct au_sbinfo; ++void au_plink_maint_leave(struct au_sbinfo *sbinfo); ++int au_plink_maint_enter(struct super_block *sb); ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb); ++#else ++AuStubVoid(au_plink_list, struct super_block *sb) ++#endif ++int au_plink_test(struct inode *inode); ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++void au_plink_put(struct super_block *sb, int verbose); ++void au_plink_clean(struct super_block *sb, int verbose); ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); ++#else ++AuStubInt0(au_plink_maint, struct super_block *sb, int flags); ++AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); ++AuStubInt0(au_plink_maint_enter, struct super_block *sb); ++AuStubVoid(au_plink_list, struct super_block *sb); ++AuStubInt0(au_plink_test, struct inode *inode); ++AuStub(struct dentry *, au_plink_lkup, return NULL, ++ struct inode *inode, aufs_bindex_t bindex); ++AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++AuStubVoid(au_plink_put, struct super_block *sb, int verbose); ++AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); ++AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); ++#endif /* CONFIG_PROC_FS */ ++ ++#ifdef CONFIG_AUFS_XATTR ++/* xattr.c */ ++int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, ++ unsigned int verbose); ++ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); ++ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, ++ size_t size); ++int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, ++ size_t size, int flags); ++int aufs_removexattr(struct dentry *dentry, const char *name); ++ ++/* void au_xattr_init(struct super_block *sb); */ ++#else ++AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, ++ int ignore_flags, unsigned int verbose); ++/* AuStubVoid(au_xattr_init, struct super_block *sb); */ ++#endif ++ ++#ifdef CONFIG_FS_POSIX_ACL ++struct posix_acl *aufs_get_acl(struct inode *inode, int type); ++int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); ++#endif ++ ++#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) ++enum { ++ AU_XATTR_SET, ++ AU_XATTR_REMOVE, ++ AU_ACL_SET ++}; ++ ++struct au_srxattr { ++ int type; ++ union { ++ struct { ++ const char *name; ++ const void *value; ++ size_t size; ++ int flags; ++ } set; ++ struct { ++ const char *name; ++ } remove; ++ struct { ++ struct posix_acl *acl; ++ int type; ++ } acl_set; ++ } u; ++}; ++ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for iinfo */ ++enum { ++ AuLsc_II_CHILD, /* child first */ ++ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ ++ AuLsc_II_CHILD3, /* copyup dirs */ ++ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ ++ AuLsc_II_PARENT2, ++ AuLsc_II_PARENT3, /* copyup dirs */ ++ AuLsc_II_NEW_CHILD ++}; ++ ++/* ++ * ii_read_lock_child, ii_write_lock_child, ++ * ii_read_lock_child2, ii_write_lock_child2, ++ * ii_read_lock_child3, ii_write_lock_child3, ++ * ii_read_lock_parent, ii_write_lock_parent, ++ * ii_read_lock_parent2, ii_write_lock_parent2, ++ * ii_read_lock_parent3, ii_write_lock_parent3, ++ * ii_read_lock_new_child, ii_write_lock_new_child, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void ii_read_lock_##name(struct inode *i) \ ++{ \ ++ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void ii_write_lock_##name(struct inode *i) \ ++{ \ ++ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++AuRWLockFuncs(new_child, NEW_CHILD); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++/* ++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock ++ */ ++AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); ++ ++#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) ++#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) ++#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void au_icntnr_init(struct au_icntnr *c) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++ c->vfs_inode.i_mode = 0; ++#endif ++} ++ ++static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen) ++{ ++ unsigned int gen; ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ spin_lock(&iinfo->ii_genspin); ++ if (iigen) ++ *iigen = iinfo->ii_generation; ++ gen = iinfo->ii_generation.ig_generation; ++ spin_unlock(&iinfo->ii_genspin); ++ ++ return gen; ++} ++ ++/* tiny test for inode number */ ++/* tmpfs generation is too rough */ ++static inline int au_test_higen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ AuRwMustAnyLock(&iinfo->ii_rwsem); ++ return !(iinfo->ii_hsb1 == h_inode->i_sb ++ && iinfo->ii_higen == h_inode->i_generation); ++} ++ ++static inline void au_iigen_dec(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ spin_lock(&iinfo->ii_genspin); ++ iinfo->ii_generation.ig_generation--; ++ spin_unlock(&iinfo->ii_genspin); ++} ++ ++static inline int au_iigen_test(struct inode *inode, unsigned int sigen) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(inode && au_iigen(inode, NULL) != sigen)) ++ err = -EIO; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_ii_br_id(struct inode *inode, ++ aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; ++} ++ ++static inline aufs_bindex_t au_ibstart(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_bstart; ++} ++ ++static inline aufs_bindex_t au_ibend(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_bend; ++} ++ ++static inline struct au_vdir *au_ivdir(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_vdir; ++} ++ ++static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; ++} ++ ++static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_bstart = bindex; ++} ++ ++static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_bend = bindex; ++} ++ ++static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_vdir = vdir; ++} ++ ++static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode + bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_pinned_parent(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->parent; ++ return NULL; ++} ++ ++static inline struct inode *au_pinned_h_dir(struct au_pin *pin) ++{ ++ if (pin && pin->hdir) ++ return pin->hdir->hi_inode; ++ return NULL; ++} ++ ++static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->hdir; ++ return NULL; ++} ++ ++static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) ++{ ++ if (pin) ++ pin->dentry = dentry; ++} ++ ++static inline void au_pin_set_parent_lflag(struct au_pin *pin, ++ unsigned char lflag) ++{ ++ if (pin) { ++ if (lflag) ++ au_fset_pin(pin->flags, DI_LOCKED); ++ else ++ au_fclr_pin(pin->flags, DI_LOCKED); ++ } ++} ++ ++#if 0 /* reserved */ ++static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) ++{ ++ if (pin) { ++ dput(pin->parent); ++ pin->parent = dget(parent); ++ } ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_branch; ++#ifdef CONFIG_AUFS_HNOTIFY ++struct au_hnotify_op { ++ void (*ctl)(struct au_hinode *hinode, int do_set); ++ int (*alloc)(struct au_hinode *hinode); ++ ++ /* ++ * if it returns true, the the caller should free hinode->hi_notify, ++ * otherwise ->free() frees it. ++ */ ++ int (*free)(struct au_hinode *hinode, ++ struct au_hnotify *hn) __must_check; ++ ++ void (*fin)(void); ++ int (*init)(void); ++ ++ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); ++ void (*fin_br)(struct au_branch *br); ++ int (*init_br)(struct au_branch *br, int perm); ++}; ++ ++/* hnotify.c */ ++int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); ++void au_hn_free(struct au_hinode *hinode); ++void au_hn_ctl(struct au_hinode *hinode, int do_set); ++void au_hn_reset(struct inode *inode, unsigned int flags); ++int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, ++ struct qstr *h_child_qstr, struct inode *h_child_inode); ++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); ++int au_hnotify_init_br(struct au_branch *br, int perm); ++void au_hnotify_fin_br(struct au_branch *br); ++int __init au_hnotify_init(void); ++void au_hnotify_fin(void); ++ ++/* hfsnotify.c */ ++extern const struct au_hnotify_op au_hnotify_op; ++ ++static inline ++void au_hn_init(struct au_hinode *hinode) ++{ ++ hinode->hi_notify = NULL; ++} ++ ++static inline struct au_hnotify *au_hn(struct au_hinode *hinode) ++{ ++ return hinode->hi_notify; ++} ++ ++#else ++AuStub(int, au_hn_alloc, return -EOPNOTSUPP, ++ struct au_hinode *hinode __maybe_unused, ++ struct inode *inode __maybe_unused) ++AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) ++AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) ++AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, ++ int do_set __maybe_unused) ++AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, ++ unsigned int flags __maybe_unused) ++AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, ++ struct au_branch *br __maybe_unused, ++ int perm __maybe_unused) ++AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, ++ int perm __maybe_unused) ++AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) ++AuStubInt0(__init au_hnotify_init, void) ++AuStubVoid(au_hnotify_fin, void) ++AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) ++#endif /* CONFIG_AUFS_HNOTIFY */ ++ ++static inline void au_hn_suspend(struct au_hinode *hdir) ++{ ++ au_hn_ctl(hdir, /*do_set*/0); ++} ++ ++static inline void au_hn_resume(struct au_hinode *hdir) ++{ ++ au_hn_ctl(hdir, /*do_set*/1); ++} ++ ++static inline void au_hn_imtx_lock(struct au_hinode *hdir) ++{ ++ mutex_lock(&hdir->hi_inode->i_mutex); ++ au_hn_suspend(hdir); ++} ++ ++static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, ++ unsigned int sc __maybe_unused) ++{ ++ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); ++ au_hn_suspend(hdir); ++} ++ ++static inline void au_hn_imtx_unlock(struct au_hinode *hdir) ++{ ++ au_hn_resume(hdir); ++ mutex_unlock(&hdir->hi_inode->i_mutex); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_INODE_H__ */ +diff --git a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c +new file mode 100644 +index 0000000..87e3ddc +--- /dev/null ++++ b/fs/aufs/ioctl.c +@@ -0,0 +1,206 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * ioctl ++ * plink-management and readdir in userspace. ++ * assist the pathconf(3) wrapper library. ++ * move-down ++ * File-based Hierarchical Storage Management. ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) ++{ ++ int err, fd; ++ aufs_bindex_t wbi, bindex, bend; ++ struct file *h_file; ++ struct super_block *sb; ++ struct dentry *root; ++ struct au_branch *br; ++ struct aufs_wbr_fd wbrfd = { ++ .oflags = au_dir_roflags, ++ .brid = -1 ++ }; ++ const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY ++ | O_NOATIME | O_CLOEXEC; ++ ++ AuDebugOn(wbrfd.oflags & ~valid); ++ ++ if (arg) { ++ err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ goto out; ++ } ++ ++ err = -EINVAL; ++ AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); ++ wbrfd.oflags |= au_dir_roflags; ++ AuDbg("0%o\n", wbrfd.oflags); ++ if (unlikely(wbrfd.oflags & ~valid)) ++ goto out; ++ } ++ ++ fd = get_unused_fd_flags(0); ++ err = fd; ++ if (unlikely(fd < 0)) ++ goto out; ++ ++ h_file = ERR_PTR(-EINVAL); ++ wbi = 0; ++ br = NULL; ++ sb = path->dentry->d_sb; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_IR); ++ bend = au_sbend(sb); ++ if (wbrfd.brid >= 0) { ++ wbi = au_br_index(sb, wbrfd.brid); ++ if (unlikely(wbi < 0 || wbi > bend)) ++ goto out_unlock; ++ } ++ ++ h_file = ERR_PTR(-ENOENT); ++ br = au_sbr(sb, wbi); ++ if (!au_br_writable(br->br_perm)) { ++ if (arg) ++ goto out_unlock; ++ ++ bindex = wbi + 1; ++ wbi = -1; ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_writable(br->br_perm)) { ++ wbi = bindex; ++ br = au_sbr(sb, wbi); ++ break; ++ } ++ } ++ } ++ AuDbg("wbi %d\n", wbi); ++ if (wbi >= 0) ++ h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, ++ /*force_wr*/0); ++ ++out_unlock: ++ aufs_read_unlock(root, AuLock_IR); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out_fd; ++ ++ atomic_dec(&br->br_count); /* cf. au_h_open() */ ++ fd_install(fd, h_file); ++ err = fd; ++ goto out; /* success */ ++ ++out_fd: ++ put_unused_fd(fd); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err; ++ struct dentry *dentry; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ioctl(file, cmd, arg); ++ break; ++ ++ case AUFS_CTL_WBR_FD: ++ err = au_wbr_fd(&file->f_path, (void __user *)arg); ++ break; ++ ++ case AUFS_CTL_IBUSY: ++ err = au_ibusy_ioctl(file, arg); ++ break; ++ ++ case AUFS_CTL_BRINFO: ++ err = au_brinfo_ioctl(file, arg); ++ break; ++ ++ case AUFS_CTL_FHSM_FD: ++ dentry = file->f_path.dentry; ++ if (IS_ROOT(dentry)) ++ err = au_fhsm_fd(dentry->d_sb, arg); ++ else ++ err = -ENOTTY; ++ break; ++ ++ default: ++ /* do not call the lower */ ++ AuDbg("0x%x\n", cmd); ++ err = -ENOTTY; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err; ++ ++ switch (cmd) { ++ case AUFS_CTL_MVDOWN: ++ err = au_mvdown(file->f_path.dentry, (void __user *)arg); ++ break; ++ ++ case AUFS_CTL_WBR_FD: ++ err = au_wbr_fd(&file->f_path, (void __user *)arg); ++ break; ++ ++ default: ++ /* do not call the lower */ ++ AuDbg("0x%x\n", cmd); ++ err = -ENOTTY; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++#ifdef CONFIG_COMPAT ++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ long err; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_compat_ioctl(file, cmd, arg); ++ break; ++ ++ case AUFS_CTL_IBUSY: ++ err = au_ibusy_compat_ioctl(file, arg); ++ break; ++ ++ case AUFS_CTL_BRINFO: ++ err = au_brinfo_compat_ioctl(file, arg); ++ break; ++ ++ default: ++ err = aufs_ioctl_dir(file, cmd, arg); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); ++} ++#endif +diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c +new file mode 100644 +index 0000000..69f7e96 +--- /dev/null ++++ b/fs/aufs/loop.c +@@ -0,0 +1,132 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * support for loopback block device as a branch ++ */ ++ ++#include "aufs.h" ++ ++/* added into drivers/block/loop.c */ ++static struct file *(*backing_file_func)(struct super_block *sb); ++ ++/* ++ * test if two lower dentries have overlapping branches. ++ */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) ++{ ++ struct super_block *h_sb; ++ struct file *backing_file; ++ ++ if (unlikely(!backing_file_func)) { ++ /* don't load "loop" module here */ ++ backing_file_func = symbol_get(loop_backing_file); ++ if (unlikely(!backing_file_func)) ++ /* "loop" module is not loaded */ ++ return 0; ++ } ++ ++ h_sb = h_adding->d_sb; ++ backing_file = backing_file_func(h_sb); ++ if (!backing_file) ++ return 0; ++ ++ h_adding = backing_file->f_path.dentry; ++ /* ++ * h_adding can be local NFS. ++ * in this case aufs cannot detect the loop. ++ */ ++ if (unlikely(h_adding->d_sb == sb)) ++ return 1; ++ return !!au_test_subdir(h_adding, sb->s_root); ++} ++ ++/* true if a kernel thread named 'loop[0-9].*' accesses a file */ ++int au_test_loopback_kthread(void) ++{ ++ int ret; ++ struct task_struct *tsk = current; ++ char c, comm[sizeof(tsk->comm)]; ++ ++ ret = 0; ++ if (tsk->flags & PF_KTHREAD) { ++ get_task_comm(comm, tsk); ++ c = comm[4]; ++ ret = ('0' <= c && c <= '9' ++ && !strncmp(comm, "loop", 4)); ++ } ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define au_warn_loopback_step 16 ++static int au_warn_loopback_nelem = au_warn_loopback_step; ++static unsigned long *au_warn_loopback_array; ++ ++void au_warn_loopback(struct super_block *h_sb) ++{ ++ int i, new_nelem; ++ unsigned long *a, magic; ++ static DEFINE_SPINLOCK(spin); ++ ++ magic = h_sb->s_magic; ++ spin_lock(&spin); ++ a = au_warn_loopback_array; ++ for (i = 0; i < au_warn_loopback_nelem && *a; i++) ++ if (a[i] == magic) { ++ spin_unlock(&spin); ++ return; ++ } ++ ++ /* h_sb is new to us, print it */ ++ if (i < au_warn_loopback_nelem) { ++ a[i] = magic; ++ goto pr; ++ } ++ ++ /* expand the array */ ++ new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; ++ a = au_kzrealloc(au_warn_loopback_array, ++ au_warn_loopback_nelem * sizeof(unsigned long), ++ new_nelem * sizeof(unsigned long), GFP_ATOMIC); ++ if (a) { ++ au_warn_loopback_nelem = new_nelem; ++ au_warn_loopback_array = a; ++ a[i] = magic; ++ goto pr; ++ } ++ ++ spin_unlock(&spin); ++ AuWarn1("realloc failed, ignored\n"); ++ return; ++ ++pr: ++ spin_unlock(&spin); ++ pr_warn("you may want to try another patch for loopback file " ++ "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); ++} ++ ++int au_loopback_init(void) ++{ ++ int err; ++ struct super_block *sb __maybe_unused; ++ ++ AuDebugOn(sizeof(sb->s_magic) != sizeof(unsigned long)); ++ ++ err = 0; ++ au_warn_loopback_array = kcalloc(au_warn_loopback_step, ++ sizeof(unsigned long), GFP_NOFS); ++ if (unlikely(!au_warn_loopback_array)) ++ err = -ENOMEM; ++ ++ return err; ++} ++ ++void au_loopback_fin(void) ++{ ++ symbol_put(loop_backing_file); ++ kfree(au_warn_loopback_array); ++} +diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h +new file mode 100644 +index 0000000..6d9864d +--- /dev/null ++++ b/fs/aufs/loop.h +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * support for loopback mount as a branch ++ */ ++ ++#ifndef __AUFS_LOOP_H__ ++#define __AUFS_LOOP_H__ ++ ++#ifdef __KERNEL__ ++ ++struct dentry; ++struct super_block; ++ ++#ifdef CONFIG_AUFS_BDEV_LOOP ++/* drivers/block/loop.c */ ++struct file *loop_backing_file(struct super_block *sb); ++ ++/* loop.c */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); ++int au_test_loopback_kthread(void); ++void au_warn_loopback(struct super_block *h_sb); ++ ++int au_loopback_init(void); ++void au_loopback_fin(void); ++#else ++AuStubInt0(au_test_loopback_overlap, struct super_block *sb, ++ struct dentry *h_adding) ++AuStubInt0(au_test_loopback_kthread, void) ++AuStubVoid(au_warn_loopback, struct super_block *h_sb) ++ ++AuStubInt0(au_loopback_init, void) ++AuStubVoid(au_loopback_fin, void) ++#endif /* BLK_DEV_LOOP */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_LOOP_H__ */ +diff --git a/fs/aufs/magic.mk b/fs/aufs/magic.mk +new file mode 100644 +index 0000000..4f83bdf +--- /dev/null ++++ b/fs/aufs/magic.mk +@@ -0,0 +1,30 @@ ++ ++# defined in ${srctree}/fs/fuse/inode.c ++# tristate ++ifdef CONFIG_FUSE_FS ++ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 ++endif ++ ++# defined in ${srctree}/fs/xfs/xfs_sb.h ++# tristate ++ifdef CONFIG_XFS_FS ++ccflags-y += -DXFS_SB_MAGIC=0x58465342 ++endif ++ ++# defined in ${srctree}/fs/configfs/mount.c ++# tristate ++ifdef CONFIG_CONFIGFS_FS ++ccflags-y += -DCONFIGFS_MAGIC=0x62656570 ++endif ++ ++# defined in ${srctree}/fs/ubifs/ubifs.h ++# tristate ++ifdef CONFIG_UBIFS_FS ++ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 ++endif ++ ++# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h ++# tristate ++ifdef CONFIG_HFSPLUS_FS ++ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b ++endif +diff --git a/fs/aufs/module.c b/fs/aufs/module.c +new file mode 100644 +index 0000000..64b6fdc +--- /dev/null ++++ b/fs/aufs/module.c +@@ -0,0 +1,196 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * module global variables and operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) ++{ ++ if (new_sz <= nused) ++ return p; ++ ++ p = krealloc(p, new_sz, gfp); ++ if (p) ++ memset(p + nused, 0, new_sz - nused); ++ return p; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * aufs caches ++ */ ++struct kmem_cache *au_cachep[AuCache_Last]; ++static int __init au_cache_init(void) ++{ ++ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); ++ if (au_cachep[AuCache_DINFO]) ++ /* SLAB_DESTROY_BY_RCU */ ++ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, ++ au_icntnr_init_once); ++ if (au_cachep[AuCache_ICNTNR]) ++ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, ++ au_fi_init_once); ++ if (au_cachep[AuCache_FINFO]) ++ au_cachep[AuCache_VDIR] = AuCache(au_vdir); ++ if (au_cachep[AuCache_VDIR]) ++ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); ++ if (au_cachep[AuCache_DEHSTR]) ++ return 0; ++ ++ return -ENOMEM; ++} ++ ++static void au_cache_fin(void) ++{ ++ int i; ++ ++ /* ++ * Make sure all delayed rcu free inodes are flushed before we ++ * destroy cache. ++ */ ++ rcu_barrier(); ++ ++ /* excluding AuCache_HNOTIFY */ ++ BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); ++ for (i = 0; i < AuCache_HNOTIFY; i++) ++ if (au_cachep[i]) { ++ kmem_cache_destroy(au_cachep[i]); ++ au_cachep[i] = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_dir_roflags; ++ ++#ifdef CONFIG_AUFS_SBILIST ++/* ++ * iterate_supers_type() doesn't protect us from ++ * remounting (branch management) ++ */ ++struct au_splhead au_sbilist; ++#endif ++ ++struct lock_class_key au_lc_key[AuLcKey_Last]; ++ ++/* ++ * functions for module interface. ++ */ ++MODULE_LICENSE("GPL"); ++/* MODULE_LICENSE("GPL v2"); */ ++MODULE_AUTHOR("Junjiro R. Okajima "); ++MODULE_DESCRIPTION(AUFS_NAME ++ " -- Advanced multi layered unification filesystem"); ++MODULE_VERSION(AUFS_VERSION); ++ ++/* this module parameter has no meaning when SYSFS is disabled */ ++int sysaufs_brs = 1; ++MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); ++module_param_named(brs, sysaufs_brs, int, S_IRUGO); ++ ++/* this module parameter has no meaning when USER_NS is disabled */ ++static bool au_userns; ++MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); ++module_param_named(allow_userns, au_userns, bool, S_IRUGO); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ ++ ++int au_seq_path(struct seq_file *seq, struct path *path) ++{ ++ return seq_path(seq, path, au_esc_chars); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int __init aufs_init(void) ++{ ++ int err, i; ++ char *p; ++ ++ p = au_esc_chars; ++ for (i = 1; i <= ' '; i++) ++ *p++ = i; ++ *p++ = '\\'; ++ *p++ = '\x7f'; ++ *p = 0; ++ ++ au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); ++ ++ au_sbilist_init(); ++ sysaufs_brs_init(); ++ au_debug_init(); ++ au_dy_init(); ++ err = sysaufs_init(); ++ if (unlikely(err)) ++ goto out; ++ err = au_procfs_init(); ++ if (unlikely(err)) ++ goto out_sysaufs; ++ err = au_wkq_init(); ++ if (unlikely(err)) ++ goto out_procfs; ++ err = au_loopback_init(); ++ if (unlikely(err)) ++ goto out_wkq; ++ err = au_hnotify_init(); ++ if (unlikely(err)) ++ goto out_loopback; ++ err = au_sysrq_init(); ++ if (unlikely(err)) ++ goto out_hin; ++ err = au_cache_init(); ++ if (unlikely(err)) ++ goto out_sysrq; ++ ++ aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; ++ err = register_filesystem(&aufs_fs_type); ++ if (unlikely(err)) ++ goto out_cache; ++ ++ /* since we define pr_fmt, call printk directly */ ++ printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); ++ goto out; /* success */ ++ ++out_cache: ++ au_cache_fin(); ++out_sysrq: ++ au_sysrq_fin(); ++out_hin: ++ au_hnotify_fin(); ++out_loopback: ++ au_loopback_fin(); ++out_wkq: ++ au_wkq_fin(); ++out_procfs: ++ au_procfs_fin(); ++out_sysaufs: ++ sysaufs_fin(); ++ au_dy_fin(); ++out: ++ return err; ++} ++ ++static void __exit aufs_exit(void) ++{ ++ unregister_filesystem(&aufs_fs_type); ++ au_cache_fin(); ++ au_sysrq_fin(); ++ au_hnotify_fin(); ++ au_loopback_fin(); ++ au_wkq_fin(); ++ au_procfs_fin(); ++ sysaufs_fin(); ++ au_dy_fin(); ++} ++ ++module_init(aufs_init); ++module_exit(aufs_exit); +diff --git a/fs/aufs/module.h b/fs/aufs/module.h +new file mode 100644 +index 0000000..adee827 +--- /dev/null ++++ b/fs/aufs/module.h +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * module initialization and module-global ++ */ ++ ++#ifndef __AUFS_MODULE_H__ ++#define __AUFS_MODULE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct path; ++struct seq_file; ++ ++/* module parameters */ ++extern int sysaufs_brs; ++ ++/* ---------------------------------------------------------------------- */ ++ ++extern int au_dir_roflags; ++ ++enum { ++ AuLcNonDir_FIINFO, ++ AuLcNonDir_DIINFO, ++ AuLcNonDir_IIINFO, ++ ++ AuLcDir_FIINFO, ++ AuLcDir_DIINFO, ++ AuLcDir_IIINFO, ++ ++ AuLcSymlink_DIINFO, ++ AuLcSymlink_IIINFO, ++ ++ AuLcKey_Last ++}; ++extern struct lock_class_key au_lc_key[AuLcKey_Last]; ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); ++int au_seq_path(struct seq_file *seq, struct path *path); ++ ++#ifdef CONFIG_PROC_FS ++/* procfs.c */ ++int __init au_procfs_init(void); ++void au_procfs_fin(void); ++#else ++AuStubInt0(au_procfs_init, void); ++AuStubVoid(au_procfs_fin, void); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* kmem cache */ ++enum { ++ AuCache_DINFO, ++ AuCache_ICNTNR, ++ AuCache_FINFO, ++ AuCache_VDIR, ++ AuCache_DEHSTR, ++ AuCache_HNOTIFY, /* must be last */ ++ AuCache_Last ++}; ++ ++#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) ++#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) ++#define AuCacheCtor(type, ctor) \ ++ kmem_cache_create(#type, sizeof(struct type), \ ++ __alignof__(struct type), AuCacheFlags, ctor) ++ ++extern struct kmem_cache *au_cachep[]; ++ ++#define AuCacheFuncs(name, index) \ ++static inline struct au_##name *au_cache_alloc_##name(void) \ ++{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ ++static inline void au_cache_free_##name(struct au_##name *p) \ ++{ kmem_cache_free(au_cachep[AuCache_##index], p); } ++ ++AuCacheFuncs(dinfo, DINFO); ++AuCacheFuncs(icntnr, ICNTNR); ++AuCacheFuncs(finfo, FINFO); ++AuCacheFuncs(vdir, VDIR); ++AuCacheFuncs(vdir_dehstr, DEHSTR); ++#ifdef CONFIG_AUFS_HNOTIFY ++AuCacheFuncs(hnotify, HNOTIFY); ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_MODULE_H__ */ +diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c +new file mode 100644 +index 0000000..bec20d3 +--- /dev/null ++++ b/fs/aufs/mvdown.c +@@ -0,0 +1,681 @@ ++/* ++ * Copyright (C) 2011-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * move-down, opposite of copy-up ++ */ ++ ++#include "aufs.h" ++ ++struct au_mvd_args { ++ struct { ++ struct super_block *h_sb; ++ struct dentry *h_parent; ++ struct au_hinode *hdir; ++ struct inode *h_dir, *h_inode; ++ struct au_pin pin; ++ } info[AUFS_MVDOWN_NARRAY]; ++ ++ struct aufs_mvdown mvdown; ++ struct dentry *dentry, *parent; ++ struct inode *inode, *dir; ++ struct super_block *sb; ++ aufs_bindex_t bopq, bwh, bfound; ++ unsigned char rename_lock; ++}; ++ ++#define mvd_errno mvdown.au_errno ++#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex ++#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid ++#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex ++#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid ++ ++#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb ++#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent ++#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir ++#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir ++#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode ++#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin ++ ++#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb ++#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent ++#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir ++#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir ++#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode ++#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin ++ ++#define AU_MVD_PR(flag, ...) do { \ ++ if (flag) \ ++ pr_err(__VA_ARGS__); \ ++ } while (0) ++ ++static int find_lower_writable(struct au_mvd_args *a) ++{ ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ sb = a->sb; ++ bindex = a->mvd_bsrc; ++ bend = au_sbend(sb); ++ if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) ++ for (bindex++; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm) ++ && (!(au_br_sb(br)->s_flags & MS_RDONLY))) ++ return bindex; ++ } ++ else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) ++ for (bindex++; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!au_br_rdonly(br)) ++ return bindex; ++ } ++ else ++ for (bindex++; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { ++ if (au_br_rdonly(br)) ++ a->mvdown.flags ++ |= AUFS_MVDOWN_ROLOWER_R; ++ return bindex; ++ } ++ } ++ ++ return -1; ++} ++ ++/* make the parent dir on bdst */ ++static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ ++ err = 0; ++ a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); ++ a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); ++ a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); ++ a->mvd_h_dst_parent = NULL; ++ if (au_dbend(a->parent) >= a->mvd_bdst) ++ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); ++ if (!a->mvd_h_dst_parent) { ++ err = au_cpdown_dirs(a->dentry, a->mvd_bdst); ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); ++ goto out; ++ } ++ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* lock them all */ ++static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct dentry *h_trap; ++ ++ a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); ++ a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); ++ err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, ++ au_opt_udba(a->sb), ++ AuPin_MNT_WRITE | AuPin_DI_LOCKED); ++ AuTraceErr(err); ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "pin_dst failed\n"); ++ goto out; ++ } ++ ++ if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { ++ a->rename_lock = 0; ++ au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, ++ AuLsc_DI_PARENT, AuLsc_I_PARENT3, ++ au_opt_udba(a->sb), ++ AuPin_MNT_WRITE | AuPin_DI_LOCKED); ++ err = au_do_pin(&a->mvd_pin_src); ++ AuTraceErr(err); ++ a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "pin_src failed\n"); ++ goto out_dst; ++ } ++ goto out; /* success */ ++ } ++ ++ a->rename_lock = 1; ++ au_pin_hdir_unlock(&a->mvd_pin_dst); ++ err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, ++ au_opt_udba(a->sb), ++ AuPin_MNT_WRITE | AuPin_DI_LOCKED); ++ AuTraceErr(err); ++ a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "pin_src failed\n"); ++ au_pin_hdir_lock(&a->mvd_pin_dst); ++ goto out_dst; ++ } ++ au_pin_hdir_unlock(&a->mvd_pin_src); ++ h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, ++ a->mvd_h_dst_parent, a->mvd_hdir_dst); ++ if (h_trap) { ++ err = (h_trap != a->mvd_h_src_parent); ++ if (err) ++ err = (h_trap != a->mvd_h_dst_parent); ++ } ++ BUG_ON(err); /* it should never happen */ ++ if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { ++ err = -EBUSY; ++ AuTraceErr(err); ++ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, ++ a->mvd_h_dst_parent, a->mvd_hdir_dst); ++ au_pin_hdir_lock(&a->mvd_pin_src); ++ au_unpin(&a->mvd_pin_src); ++ au_pin_hdir_lock(&a->mvd_pin_dst); ++ goto out_dst; ++ } ++ goto out; /* success */ ++ ++out_dst: ++ au_unpin(&a->mvd_pin_dst); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ if (!a->rename_lock) ++ au_unpin(&a->mvd_pin_src); ++ else { ++ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, ++ a->mvd_h_dst_parent, a->mvd_hdir_dst); ++ au_pin_hdir_lock(&a->mvd_pin_src); ++ au_unpin(&a->mvd_pin_src); ++ au_pin_hdir_lock(&a->mvd_pin_dst); ++ } ++ au_unpin(&a->mvd_pin_dst); ++} ++ ++/* copy-down the file */ ++static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct au_cp_generic cpg = { ++ .dentry = a->dentry, ++ .bdst = a->mvd_bdst, ++ .bsrc = a->mvd_bsrc, ++ .len = -1, ++ .pin = &a->mvd_pin_dst, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ ++ AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); ++ if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) ++ au_fset_cpup(cpg.flags, OVERWRITE); ++ if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) ++ au_fset_cpup(cpg.flags, RWDST); ++ err = au_sio_cpdown_simple(&cpg); ++ if (unlikely(err)) ++ AU_MVD_PR(dmsg, "cpdown failed\n"); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * unlink the whiteout on bdst if exist which may be created by UDBA while we ++ * were sleeping ++ */ ++static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct path h_path; ++ struct au_branch *br; ++ struct inode *delegated; ++ ++ br = au_sbr(a->sb, a->mvd_bdst); ++ h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) { ++ AU_MVD_PR(dmsg, "wh_lkup failed\n"); ++ goto out; ++ } ++ ++ err = 0; ++ if (d_is_positive(h_path.dentry)) { ++ h_path.mnt = au_br_mnt(br); ++ delegated = NULL; ++ err = vfsub_unlink(d_inode(a->mvd_h_dst_parent), &h_path, ++ &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) ++ AU_MVD_PR(dmsg, "wh_unlink failed\n"); ++ } ++ dput(h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * unlink the topmost h_dentry ++ */ ++static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct path h_path; ++ struct inode *delegated; ++ ++ h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); ++ h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); ++ delegated = NULL; ++ err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) ++ AU_MVD_PR(dmsg, "unlink failed\n"); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* Since mvdown succeeded, we ignore an error of this function */ ++static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct au_branch *br; ++ ++ a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; ++ br = au_sbr(a->sb, a->mvd_bsrc); ++ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); ++ if (!err) { ++ br = au_sbr(a->sb, a->mvd_bdst); ++ a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; ++ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); ++ } ++ if (!err) ++ a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; ++ else ++ AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); ++} ++ ++/* ++ * copy-down the file and unlink the bsrc file. ++ * - unlink the bdst whout if exist ++ * - copy-down the file (with whtmp name and rename) ++ * - unlink the bsrc file ++ */ ++static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ ++ err = au_do_mkdir(dmsg, a); ++ if (!err) ++ err = au_do_lock(dmsg, a); ++ if (unlikely(err)) ++ goto out; ++ ++ /* ++ * do not revert the activities we made on bdst since they should be ++ * harmless in aufs. ++ */ ++ ++ err = au_do_cpdown(dmsg, a); ++ if (!err) ++ err = au_do_unlink_wh(dmsg, a); ++ if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) ++ err = au_do_unlink(dmsg, a); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ AuDbg("%pd2, 0x%x, %d --> %d\n", ++ a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); ++ if (find_lower_writable(a) < 0) ++ a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; ++ ++ if (a->mvdown.flags & AUFS_MVDOWN_STFS) ++ au_do_stfs(dmsg, a); ++ ++ /* maintain internal array */ ++ if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { ++ au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); ++ au_set_dbstart(a->dentry, a->mvd_bdst); ++ au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); ++ au_set_ibstart(a->inode, a->mvd_bdst); ++ } ++ if (au_dbend(a->dentry) < a->mvd_bdst) ++ au_set_dbend(a->dentry, a->mvd_bdst); ++ if (au_ibend(a->inode) < a->mvd_bdst) ++ au_set_ibend(a->inode, a->mvd_bdst); ++ ++out_unlock: ++ au_do_unlock(dmsg, a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* make sure the file is idle */ ++static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err, plinked; ++ ++ err = 0; ++ plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); ++ if (au_dbstart(a->dentry) == a->mvd_bsrc ++ && au_dcount(a->dentry) == 1 ++ && atomic_read(&a->inode->i_count) == 1 ++ /* && a->mvd_h_src_inode->i_nlink == 1 */ ++ && (!plinked || !au_plink_test(a->inode)) ++ && a->inode->i_nlink == 1) ++ goto out; ++ ++ err = -EBUSY; ++ AU_MVD_PR(dmsg, ++ "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", ++ a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), ++ atomic_read(&a->inode->i_count), a->inode->i_nlink, ++ a->mvd_h_src_inode->i_nlink, ++ plinked, plinked ? au_plink_test(a->inode) : 0); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* make sure the parent dir is fine */ ++static int au_mvd_args_parent(const unsigned char dmsg, ++ struct au_mvd_args *a) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ ++ err = 0; ++ if (unlikely(au_alive_dir(a->parent))) { ++ err = -ENOENT; ++ AU_MVD_PR(dmsg, "parent dir is dead\n"); ++ goto out; ++ } ++ ++ a->bopq = au_dbdiropq(a->parent); ++ bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); ++ AuDbg("b%d\n", bindex); ++ if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) ++ || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { ++ err = -EINVAL; ++ a->mvd_errno = EAU_MVDOWN_OPAQUE; ++ AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", ++ a->bopq, a->mvd_bdst); ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_mvd_args_intermediate(const unsigned char dmsg, ++ struct au_mvd_args *a) ++{ ++ int err; ++ struct au_dinfo *dinfo, *tmp; ++ ++ /* lookup the next lower positive entry */ ++ err = -ENOMEM; ++ tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); ++ if (unlikely(!tmp)) ++ goto out; ++ ++ a->bfound = -1; ++ a->bwh = -1; ++ dinfo = au_di(a->dentry); ++ au_di_cp(tmp, dinfo); ++ au_di_swap(tmp, dinfo); ++ ++ /* returns the number of positive dentries */ ++ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); ++ if (!err) ++ a->bwh = au_dbwh(a->dentry); ++ else if (err > 0) ++ a->bfound = au_dbstart(a->dentry); ++ ++ au_di_swap(tmp, dinfo); ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ if (unlikely(err < 0)) ++ AU_MVD_PR(dmsg, "failed look-up lower\n"); ++ ++ /* ++ * here, we have these cases. ++ * bfound == -1 ++ * no positive dentry under bsrc. there are more sub-cases. ++ * bwh < 0 ++ * there no whiteout, we can safely move-down. ++ * bwh <= bsrc ++ * impossible ++ * bsrc < bwh && bwh < bdst ++ * there is a whiteout on RO branch. cannot proceed. ++ * bwh == bdst ++ * there is a whiteout on the RW target branch. it should ++ * be removed. ++ * bdst < bwh ++ * there is a whiteout somewhere unrelated branch. ++ * -1 < bfound && bfound <= bsrc ++ * impossible. ++ * bfound < bdst ++ * found, but it is on RO branch between bsrc and bdst. cannot ++ * proceed. ++ * bfound == bdst ++ * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return ++ * error. ++ * bdst < bfound ++ * found, after we create the file on bdst, it will be hidden. ++ */ ++ ++ AuDebugOn(a->bfound == -1 ++ && a->bwh != -1 ++ && a->bwh <= a->mvd_bsrc); ++ AuDebugOn(-1 < a->bfound ++ && a->bfound <= a->mvd_bsrc); ++ ++ err = -EINVAL; ++ if (a->bfound == -1 ++ && a->mvd_bsrc < a->bwh ++ && a->bwh != -1 ++ && a->bwh < a->mvd_bdst) { ++ a->mvd_errno = EAU_MVDOWN_WHITEOUT; ++ AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", ++ a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); ++ goto out; ++ } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { ++ a->mvd_errno = EAU_MVDOWN_UPPER; ++ AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", ++ a->mvd_bdst, a->bfound); ++ goto out; ++ } ++ ++ err = 0; /* success */ ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ ++ err = 0; ++ if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) ++ && a->bfound == a->mvd_bdst) ++ err = -EEXIST; ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct au_branch *br; ++ ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(a->inode->i_mode))) ++ goto out; ++ ++ err = -EINVAL; ++ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) ++ a->mvd_bsrc = au_ibstart(a->inode); ++ else { ++ a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); ++ if (unlikely(a->mvd_bsrc < 0 ++ || (a->mvd_bsrc < au_dbstart(a->dentry) ++ || au_dbend(a->dentry) < a->mvd_bsrc ++ || !au_h_dptr(a->dentry, a->mvd_bsrc)) ++ || (a->mvd_bsrc < au_ibstart(a->inode) ++ || au_ibend(a->inode) < a->mvd_bsrc ++ || !au_h_iptr(a->inode, a->mvd_bsrc)))) { ++ a->mvd_errno = EAU_MVDOWN_NOUPPER; ++ AU_MVD_PR(dmsg, "no upper\n"); ++ goto out; ++ } ++ } ++ if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { ++ a->mvd_errno = EAU_MVDOWN_BOTTOM; ++ AU_MVD_PR(dmsg, "on the bottom\n"); ++ goto out; ++ } ++ a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); ++ br = au_sbr(a->sb, a->mvd_bsrc); ++ err = au_br_rdonly(br); ++ if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { ++ if (unlikely(err)) ++ goto out; ++ } else if (!(vfsub_native_ro(a->mvd_h_src_inode) ++ || IS_APPEND(a->mvd_h_src_inode))) { ++ if (err) ++ a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; ++ /* go on */ ++ } else ++ goto out; ++ ++ err = -EINVAL; ++ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { ++ a->mvd_bdst = find_lower_writable(a); ++ if (unlikely(a->mvd_bdst < 0)) { ++ a->mvd_errno = EAU_MVDOWN_BOTTOM; ++ AU_MVD_PR(dmsg, "no writable lower branch\n"); ++ goto out; ++ } ++ } else { ++ a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); ++ if (unlikely(a->mvd_bdst < 0 ++ || au_sbend(a->sb) < a->mvd_bdst)) { ++ a->mvd_errno = EAU_MVDOWN_NOLOWERBR; ++ AU_MVD_PR(dmsg, "no lower brid\n"); ++ goto out; ++ } ++ } ++ ++ err = au_mvd_args_busy(dmsg, a); ++ if (!err) ++ err = au_mvd_args_parent(dmsg, a); ++ if (!err) ++ err = au_mvd_args_intermediate(dmsg, a); ++ if (!err) ++ err = au_mvd_args_exist(dmsg, a); ++ if (!err) ++ AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) ++{ ++ int err, e; ++ unsigned char dmsg; ++ struct au_mvd_args *args; ++ ++ err = -EPERM; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = -ENOMEM; ++ args = kmalloc(sizeof(*args), GFP_NOFS); ++ if (unlikely(!args)) ++ goto out; ++ ++ err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); ++ if (!err) ++ err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out_free; ++ } ++ AuDbg("flags 0x%x\n", args->mvdown.flags); ++ args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); ++ args->mvdown.au_errno = 0; ++ args->dentry = dentry; ++ args->inode = d_inode(dentry); ++ args->sb = dentry->d_sb; ++ ++ err = -ENOENT; ++ dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); ++ args->parent = dget_parent(dentry); ++ args->dir = d_inode(args->parent); ++ mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); ++ dput(args->parent); ++ if (unlikely(args->parent != dentry->d_parent)) { ++ AU_MVD_PR(dmsg, "parent dir is moved\n"); ++ goto out_dir; ++ } ++ ++ mutex_lock_nested(&args->inode->i_mutex, I_MUTEX_CHILD); ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH); ++ if (unlikely(err)) ++ goto out_inode; ++ ++ di_write_lock_parent(args->parent); ++ err = au_mvd_args(dmsg, args); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ err = au_do_mvdown(dmsg, args); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ au_cpup_attr_timesizes(args->dir); ++ au_cpup_attr_timesizes(args->inode); ++ au_cpup_igen(args->inode, au_h_iptr(args->inode, args->mvd_bdst)); ++ /* au_digen_dec(dentry); */ ++ ++out_parent: ++ di_write_unlock(args->parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++out_inode: ++ mutex_unlock(&args->inode->i_mutex); ++out_dir: ++ mutex_unlock(&args->dir->i_mutex); ++out_free: ++ e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); ++ if (unlikely(e)) ++ err = -EFAULT; ++ kfree(args); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c +new file mode 100644 +index 0000000..90098fe +--- /dev/null ++++ b/fs/aufs/opts.c +@@ -0,0 +1,1822 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#include ++#include /* a distribution requires */ ++#include ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { ++ Opt_br, ++ Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, ++ Opt_idel, Opt_imod, ++ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, ++ Opt_rdblk_def, Opt_rdhash_def, ++ Opt_xino, Opt_noxino, ++ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, ++ Opt_trunc_xino_path, Opt_itrunc_xino, ++ Opt_trunc_xib, Opt_notrunc_xib, ++ Opt_shwh, Opt_noshwh, ++ Opt_plink, Opt_noplink, Opt_list_plink, ++ Opt_udba, ++ Opt_dio, Opt_nodio, ++ Opt_diropq_a, Opt_diropq_w, ++ Opt_warn_perm, Opt_nowarn_perm, ++ Opt_wbr_copyup, Opt_wbr_create, ++ Opt_fhsm_sec, ++ Opt_verbose, Opt_noverbose, ++ Opt_sum, Opt_nosum, Opt_wsum, ++ Opt_dirperm1, Opt_nodirperm1, ++ Opt_acl, Opt_noacl, ++ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err ++}; ++ ++static match_table_t options = { ++ {Opt_br, "br=%s"}, ++ {Opt_br, "br:%s"}, ++ ++ {Opt_add, "add=%d:%s"}, ++ {Opt_add, "add:%d:%s"}, ++ {Opt_add, "ins=%d:%s"}, ++ {Opt_add, "ins:%d:%s"}, ++ {Opt_append, "append=%s"}, ++ {Opt_append, "append:%s"}, ++ {Opt_prepend, "prepend=%s"}, ++ {Opt_prepend, "prepend:%s"}, ++ ++ {Opt_del, "del=%s"}, ++ {Opt_del, "del:%s"}, ++ /* {Opt_idel, "idel:%d"}, */ ++ {Opt_mod, "mod=%s"}, ++ {Opt_mod, "mod:%s"}, ++ /* {Opt_imod, "imod:%d:%s"}, */ ++ ++ {Opt_dirwh, "dirwh=%d"}, ++ ++ {Opt_xino, "xino=%s"}, ++ {Opt_noxino, "noxino"}, ++ {Opt_trunc_xino, "trunc_xino"}, ++ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, ++ {Opt_notrunc_xino, "notrunc_xino"}, ++ {Opt_trunc_xino_path, "trunc_xino=%s"}, ++ {Opt_itrunc_xino, "itrunc_xino=%d"}, ++ /* {Opt_zxino, "zxino=%s"}, */ ++ {Opt_trunc_xib, "trunc_xib"}, ++ {Opt_notrunc_xib, "notrunc_xib"}, ++ ++#ifdef CONFIG_PROC_FS ++ {Opt_plink, "plink"}, ++#else ++ {Opt_ignore_silent, "plink"}, ++#endif ++ ++ {Opt_noplink, "noplink"}, ++ ++#ifdef CONFIG_AUFS_DEBUG ++ {Opt_list_plink, "list_plink"}, ++#endif ++ ++ {Opt_udba, "udba=%s"}, ++ ++ {Opt_dio, "dio"}, ++ {Opt_nodio, "nodio"}, ++ ++#ifdef CONFIG_AUFS_FHSM ++ {Opt_fhsm_sec, "fhsm_sec=%d"}, ++#else ++ {Opt_ignore_silent, "fhsm_sec=%d"}, ++#endif ++ ++ {Opt_diropq_a, "diropq=always"}, ++ {Opt_diropq_a, "diropq=a"}, ++ {Opt_diropq_w, "diropq=whiteouted"}, ++ {Opt_diropq_w, "diropq=w"}, ++ ++ {Opt_warn_perm, "warn_perm"}, ++ {Opt_nowarn_perm, "nowarn_perm"}, ++ ++ /* keep them temporary */ ++ {Opt_ignore_silent, "nodlgt"}, ++ {Opt_ignore_silent, "clean_plink"}, ++ ++#ifdef CONFIG_AUFS_SHWH ++ {Opt_shwh, "shwh"}, ++#endif ++ {Opt_noshwh, "noshwh"}, ++ ++ {Opt_dirperm1, "dirperm1"}, ++ {Opt_nodirperm1, "nodirperm1"}, ++ ++ {Opt_verbose, "verbose"}, ++ {Opt_verbose, "v"}, ++ {Opt_noverbose, "noverbose"}, ++ {Opt_noverbose, "quiet"}, ++ {Opt_noverbose, "q"}, ++ {Opt_noverbose, "silent"}, ++ ++ {Opt_sum, "sum"}, ++ {Opt_nosum, "nosum"}, ++ {Opt_wsum, "wsum"}, ++ ++ {Opt_rdcache, "rdcache=%d"}, ++ {Opt_rdblk, "rdblk=%d"}, ++ {Opt_rdblk_def, "rdblk=def"}, ++ {Opt_rdhash, "rdhash=%d"}, ++ {Opt_rdhash_def, "rdhash=def"}, ++ ++ {Opt_wbr_create, "create=%s"}, ++ {Opt_wbr_create, "create_policy=%s"}, ++ {Opt_wbr_copyup, "cpup=%s"}, ++ {Opt_wbr_copyup, "copyup=%s"}, ++ {Opt_wbr_copyup, "copyup_policy=%s"}, ++ ++ /* generic VFS flag */ ++#ifdef CONFIG_FS_POSIX_ACL ++ {Opt_acl, "acl"}, ++ {Opt_noacl, "noacl"}, ++#else ++ {Opt_ignore_silent, "acl"}, ++ {Opt_ignore_silent, "noacl"}, ++#endif ++ ++ /* internal use for the scripts */ ++ {Opt_ignore_silent, "si=%s"}, ++ ++ {Opt_br, "dirs=%s"}, ++ {Opt_ignore, "debug=%d"}, ++ {Opt_ignore, "delete=whiteout"}, ++ {Opt_ignore, "delete=all"}, ++ {Opt_ignore, "imap=%s"}, ++ ++ /* temporary workaround, due to old mount(8)? */ ++ {Opt_ignore_silent, "relatime"}, ++ ++ {Opt_err, NULL} ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const char *au_parser_pattern(int val, match_table_t tbl) ++{ ++ struct match_token *p; ++ ++ p = tbl; ++ while (p->pattern) { ++ if (p->token == val) ++ return p->pattern; ++ p++; ++ } ++ BUG(); ++ return "??"; ++} ++ ++static const char *au_optstr(int *val, match_table_t tbl) ++{ ++ struct match_token *p; ++ int v; ++ ++ v = *val; ++ if (!v) ++ goto out; ++ p = tbl; ++ while (p->pattern) { ++ if (p->token ++ && (v & p->token) == p->token) { ++ *val &= ~p->token; ++ return p->pattern; ++ } ++ p++; ++ } ++ ++out: ++ return NULL; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t brperm = { ++ {AuBrPerm_RO, AUFS_BRPERM_RO}, ++ {AuBrPerm_RR, AUFS_BRPERM_RR}, ++ {AuBrPerm_RW, AUFS_BRPERM_RW}, ++ {0, NULL} ++}; ++ ++static match_table_t brattr = { ++ /* general */ ++ {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, ++ {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, ++ /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ ++ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, ++#ifdef CONFIG_AUFS_FHSM ++ {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, ++#endif ++#ifdef CONFIG_AUFS_XATTR ++ {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, ++ {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, ++ {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, ++ {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, ++ {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, ++ {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, ++#endif ++ ++ /* ro/rr branch */ ++ {AuBrRAttr_WH, AUFS_BRRATTR_WH}, ++ ++ /* rw branch */ ++ {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, ++ {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, ++ ++ {0, NULL} ++}; ++ ++static int br_attr_val(char *str, match_table_t table, substring_t args[]) ++{ ++ int attr, v; ++ char *p; ++ ++ attr = 0; ++ do { ++ p = strchr(str, '+'); ++ if (p) ++ *p = 0; ++ v = match_token(str, table, args); ++ if (v) { ++ if (v & AuBrAttr_CMOO_Mask) ++ attr &= ~AuBrAttr_CMOO_Mask; ++ attr |= v; ++ } else { ++ if (p) ++ *p = '+'; ++ pr_warn("ignored branch attribute %s\n", str); ++ break; ++ } ++ if (p) ++ str = p + 1; ++ } while (p); ++ ++ return attr; ++} ++ ++static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) ++{ ++ int sz; ++ const char *p; ++ char *q; ++ ++ q = str->a; ++ *q = 0; ++ p = au_optstr(&perm, brattr); ++ if (p) { ++ sz = strlen(p); ++ memcpy(q, p, sz + 1); ++ q += sz; ++ } else ++ goto out; ++ ++ do { ++ p = au_optstr(&perm, brattr); ++ if (p) { ++ *q++ = '+'; ++ sz = strlen(p); ++ memcpy(q, p, sz + 1); ++ q += sz; ++ } ++ } while (p); ++ ++out: ++ return q - str->a; ++} ++ ++static int noinline_for_stack br_perm_val(char *perm) ++{ ++ int val, bad, sz; ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ au_br_perm_str_t attr; ++ ++ p = strchr(perm, '+'); ++ if (p) ++ *p = 0; ++ val = match_token(perm, brperm, args); ++ if (!val) { ++ if (p) ++ *p = '+'; ++ pr_warn("ignored branch permission %s\n", perm); ++ val = AuBrPerm_RO; ++ goto out; ++ } ++ if (!p) ++ goto out; ++ ++ val |= br_attr_val(p + 1, brattr, args); ++ ++ bad = 0; ++ switch (val & AuBrPerm_Mask) { ++ case AuBrPerm_RO: ++ case AuBrPerm_RR: ++ bad = val & AuBrWAttr_Mask; ++ val &= ~AuBrWAttr_Mask; ++ break; ++ case AuBrPerm_RW: ++ bad = val & AuBrRAttr_Mask; ++ val &= ~AuBrRAttr_Mask; ++ break; ++ } ++ ++ /* ++ * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs ++ * does not treat it as an error, just warning. ++ * this is a tiny guard for the user operation. ++ */ ++ if (val & AuBrAttr_UNPIN) { ++ bad |= AuBrAttr_UNPIN; ++ val &= ~AuBrAttr_UNPIN; ++ } ++ ++ if (unlikely(bad)) { ++ sz = au_do_optstr_br_attr(&attr, bad); ++ AuDebugOn(!sz); ++ pr_warn("ignored branch attribute %s\n", attr.a); ++ } ++ ++out: ++ return val; ++} ++ ++void au_optstr_br_perm(au_br_perm_str_t *str, int perm) ++{ ++ au_br_perm_str_t attr; ++ const char *p; ++ char *q; ++ int sz; ++ ++ q = str->a; ++ p = au_optstr(&perm, brperm); ++ AuDebugOn(!p || !*p); ++ sz = strlen(p); ++ memcpy(q, p, sz + 1); ++ q += sz; ++ ++ sz = au_do_optstr_br_attr(&attr, perm); ++ if (sz) { ++ *q++ = '+'; ++ memcpy(q, attr.a, sz + 1); ++ } ++ ++ AuDebugOn(strlen(str->a) >= sizeof(str->a)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t udbalevel = { ++ {AuOpt_UDBA_REVAL, "reval"}, ++ {AuOpt_UDBA_NONE, "none"}, ++#ifdef CONFIG_AUFS_HNOTIFY ++ {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ {AuOpt_UDBA_HNOTIFY, "fsnotify"}, ++#endif ++#endif ++ {-1, NULL} ++}; ++ ++static int noinline_for_stack udba_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, udbalevel, args); ++} ++ ++const char *au_optstr_udba(int udba) ++{ ++ return au_parser_pattern(udba, udbalevel); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t au_wbr_create_policy = { ++ {AuWbrCreate_TDP, "tdp"}, ++ {AuWbrCreate_TDP, "top-down-parent"}, ++ {AuWbrCreate_RR, "rr"}, ++ {AuWbrCreate_RR, "round-robin"}, ++ {AuWbrCreate_MFS, "mfs"}, ++ {AuWbrCreate_MFS, "most-free-space"}, ++ {AuWbrCreate_MFSV, "mfs:%d"}, ++ {AuWbrCreate_MFSV, "most-free-space:%d"}, ++ ++ {AuWbrCreate_MFSRR, "mfsrr:%d"}, ++ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, ++ {AuWbrCreate_PMFS, "pmfs"}, ++ {AuWbrCreate_PMFSV, "pmfs:%d"}, ++ {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, ++ {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, ++ ++ {-1, NULL} ++}; ++ ++/* ++ * cf. linux/lib/parser.c and cmdline.c ++ * gave up calling memparse() since it uses simple_strtoull() instead of ++ * kstrto...(). ++ */ ++static int noinline_for_stack ++au_match_ull(substring_t *s, unsigned long long *result) ++{ ++ int err; ++ unsigned int len; ++ char a[32]; ++ ++ err = -ERANGE; ++ len = s->to - s->from; ++ if (len + 1 <= sizeof(a)) { ++ memcpy(a, s->from, len); ++ a[len] = '\0'; ++ err = kstrtoull(a, 0, result); ++ } ++ return err; ++} ++ ++static int au_wbr_mfs_wmark(substring_t *arg, char *str, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ unsigned long long ull; ++ ++ err = 0; ++ if (!au_match_ull(arg, &ull)) ++ create->mfsrr_watermark = ull; ++ else { ++ pr_err("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int au_wbr_mfs_sec(substring_t *arg, char *str, ++ struct au_opt_wbr_create *create) ++{ ++ int n, err; ++ ++ err = 0; ++ if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) ++ create->mfs_second = n; ++ else { ++ pr_err("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int noinline_for_stack ++au_wbr_create_val(char *str, struct au_opt_wbr_create *create) ++{ ++ int err, e; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ err = match_token(str, au_wbr_create_policy, args); ++ create->wbr_create = err; ++ switch (err) { ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_PMFSRRV: ++ e = au_wbr_mfs_wmark(&args[0], str, create); ++ if (!e) ++ e = au_wbr_mfs_sec(&args[1], str, create); ++ if (unlikely(e)) ++ err = e; ++ break; ++ case AuWbrCreate_MFSRR: ++ case AuWbrCreate_PMFSRR: ++ e = au_wbr_mfs_wmark(&args[0], str, create); ++ if (unlikely(e)) { ++ err = e; ++ break; ++ } ++ /*FALLTHROUGH*/ ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_PMFS: ++ create->mfs_second = AUFS_MFS_DEF_SEC; ++ break; ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFSV: ++ e = au_wbr_mfs_sec(&args[0], str, create); ++ if (unlikely(e)) ++ err = e; ++ break; ++ } ++ ++ return err; ++} ++ ++const char *au_optstr_wbr_create(int wbr_create) ++{ ++ return au_parser_pattern(wbr_create, au_wbr_create_policy); ++} ++ ++static match_table_t au_wbr_copyup_policy = { ++ {AuWbrCopyup_TDP, "tdp"}, ++ {AuWbrCopyup_TDP, "top-down-parent"}, ++ {AuWbrCopyup_BUP, "bup"}, ++ {AuWbrCopyup_BUP, "bottom-up-parent"}, ++ {AuWbrCopyup_BU, "bu"}, ++ {AuWbrCopyup_BU, "bottom-up"}, ++ {-1, NULL} ++}; ++ ++static int noinline_for_stack au_wbr_copyup_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, au_wbr_copyup_policy, args); ++} ++ ++const char *au_optstr_wbr_copyup(int wbr_copyup) ++{ ++ return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; ++ ++static void dump_opts(struct au_opts *opts) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++ /* reduce stack space */ ++ union { ++ struct au_opt_add *add; ++ struct au_opt_del *del; ++ struct au_opt_mod *mod; ++ struct au_opt_xino *xino; ++ struct au_opt_xino_itrunc *xino_itrunc; ++ struct au_opt_wbr_create *create; ++ } u; ++ struct au_opt *opt; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ u.add = &opt->add; ++ AuDbg("add {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ u.del = &opt->del; ++ AuDbg("del {%s, %p}\n", ++ u.del->pathname, u.del->h_path.dentry); ++ break; ++ case Opt_mod: ++ case Opt_imod: ++ u.mod = &opt->mod; ++ AuDbg("mod {%s, 0x%x, %p}\n", ++ u.mod->path, u.mod->perm, u.mod->h_root); ++ break; ++ case Opt_append: ++ u.add = &opt->add; ++ AuDbg("append {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_prepend: ++ u.add = &opt->add; ++ AuDbg("prepend {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_dirwh: ++ AuDbg("dirwh %d\n", opt->dirwh); ++ break; ++ case Opt_rdcache: ++ AuDbg("rdcache %d\n", opt->rdcache); ++ break; ++ case Opt_rdblk: ++ AuDbg("rdblk %u\n", opt->rdblk); ++ break; ++ case Opt_rdblk_def: ++ AuDbg("rdblk_def\n"); ++ break; ++ case Opt_rdhash: ++ AuDbg("rdhash %u\n", opt->rdhash); ++ break; ++ case Opt_rdhash_def: ++ AuDbg("rdhash_def\n"); ++ break; ++ case Opt_xino: ++ u.xino = &opt->xino; ++ AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); ++ break; ++ case Opt_trunc_xino: ++ AuLabel(trunc_xino); ++ break; ++ case Opt_notrunc_xino: ++ AuLabel(notrunc_xino); ++ break; ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); ++ break; ++ case Opt_noxino: ++ AuLabel(noxino); ++ break; ++ case Opt_trunc_xib: ++ AuLabel(trunc_xib); ++ break; ++ case Opt_notrunc_xib: ++ AuLabel(notrunc_xib); ++ break; ++ case Opt_shwh: ++ AuLabel(shwh); ++ break; ++ case Opt_noshwh: ++ AuLabel(noshwh); ++ break; ++ case Opt_dirperm1: ++ AuLabel(dirperm1); ++ break; ++ case Opt_nodirperm1: ++ AuLabel(nodirperm1); ++ break; ++ case Opt_plink: ++ AuLabel(plink); ++ break; ++ case Opt_noplink: ++ AuLabel(noplink); ++ break; ++ case Opt_list_plink: ++ AuLabel(list_plink); ++ break; ++ case Opt_udba: ++ AuDbg("udba %d, %s\n", ++ opt->udba, au_optstr_udba(opt->udba)); ++ break; ++ case Opt_dio: ++ AuLabel(dio); ++ break; ++ case Opt_nodio: ++ AuLabel(nodio); ++ break; ++ case Opt_diropq_a: ++ AuLabel(diropq_a); ++ break; ++ case Opt_diropq_w: ++ AuLabel(diropq_w); ++ break; ++ case Opt_warn_perm: ++ AuLabel(warn_perm); ++ break; ++ case Opt_nowarn_perm: ++ AuLabel(nowarn_perm); ++ break; ++ case Opt_verbose: ++ AuLabel(verbose); ++ break; ++ case Opt_noverbose: ++ AuLabel(noverbose); ++ break; ++ case Opt_sum: ++ AuLabel(sum); ++ break; ++ case Opt_nosum: ++ AuLabel(nosum); ++ break; ++ case Opt_wsum: ++ AuLabel(wsum); ++ break; ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ AuDbg("create %d, %s\n", u.create->wbr_create, ++ au_optstr_wbr_create(u.create->wbr_create)); ++ switch (u.create->wbr_create) { ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFSV: ++ AuDbg("%d sec\n", u.create->mfs_second); ++ break; ++ case AuWbrCreate_MFSRR: ++ AuDbg("%llu watermark\n", ++ u.create->mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_PMFSRRV: ++ AuDbg("%llu watermark, %d sec\n", ++ u.create->mfsrr_watermark, ++ u.create->mfs_second); ++ break; ++ } ++ break; ++ case Opt_wbr_copyup: ++ AuDbg("copyup %d, %s\n", opt->wbr_copyup, ++ au_optstr_wbr_copyup(opt->wbr_copyup)); ++ break; ++ case Opt_fhsm_sec: ++ AuDbg("fhsm_sec %u\n", opt->fhsm_second); ++ break; ++ case Opt_acl: ++ AuLabel(acl); ++ break; ++ case Opt_noacl: ++ AuLabel(noacl); ++ break; ++ default: ++ BUG(); ++ } ++ opt++; ++ } ++#endif ++} ++ ++void au_opts_free(struct au_opts *opts) ++{ ++ struct au_opt *opt; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ case Opt_append: ++ case Opt_prepend: ++ path_put(&opt->add.path); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ path_put(&opt->del.h_path); ++ break; ++ case Opt_mod: ++ case Opt_imod: ++ dput(opt->mod.h_root); ++ break; ++ case Opt_xino: ++ fput(opt->xino.file); ++ break; ++ } ++ opt++; ++ } ++} ++ ++static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, ++ aufs_bindex_t bindex) ++{ ++ int err; ++ struct au_opt_add *add = &opt->add; ++ char *p; ++ ++ add->bindex = bindex; ++ add->perm = AuBrPerm_RO; ++ add->pathname = opt_str; ++ p = strchr(opt_str, '='); ++ if (p) { ++ *p++ = 0; ++ if (*p) ++ add->perm = br_perm_val(p); ++ } ++ ++ err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); ++ if (!err) { ++ if (!p) { ++ add->perm = AuBrPerm_RO; ++ if (au_test_fs_rr(add->path.dentry->d_sb)) ++ add->perm = AuBrPerm_RR; ++ else if (!bindex && !(sb_flags & MS_RDONLY)) ++ add->perm = AuBrPerm_RW; ++ } ++ opt->type = Opt_add; ++ goto out; ++ } ++ pr_err("lookup failed %s (%d)\n", add->pathname, err); ++ err = -EINVAL; ++ ++out: ++ return err; ++} ++ ++static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ ++ del->pathname = args[0].from; ++ AuDbg("del path %s\n", del->pathname); ++ ++ err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); ++ if (unlikely(err)) ++ pr_err("lookup failed %s (%d)\n", del->pathname, err); ++ ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ pr_err("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ del->h_path.dentry = dget(au_h_dptr(root, bindex)); ++ del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); ++ ++out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int noinline_for_stack ++au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct path path; ++ char *p; ++ ++ err = -EINVAL; ++ mod->path = args[0].from; ++ p = strchr(mod->path, '='); ++ if (unlikely(!p)) { ++ pr_err("no permssion %s\n", args[0].from); ++ goto out; ++ } ++ ++ *p++ = 0; ++ err = vfsub_kern_path(mod->path, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ pr_err("lookup failed %s (%d)\n", mod->path, err); ++ goto out; ++ } ++ ++ mod->perm = br_perm_val(p); ++ AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); ++ mod->h_root = dget(path.dentry); ++ path_put(&path); ++ ++out: ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ pr_err("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ mod->perm = br_perm_val(args[1].from); ++ AuDbg("mod path %s, perm 0x%x, %s\n", ++ mod->path, mod->perm, args[1].from); ++ mod->h_root = dget(au_h_dptr(root, bindex)); ++ ++out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, ++ substring_t args[]) ++{ ++ int err; ++ struct file *file; ++ ++ file = au_xino_create(sb, args[0].from, /*silent*/0); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(file->f_path.dentry->d_sb == sb)) { ++ fput(file); ++ pr_err("%s must be outside\n", args[0].from); ++ goto out; ++ } ++ ++ err = 0; ++ xino->file = file; ++ xino->path = args[0].from; ++ ++out: ++ return err; ++} ++ ++static int noinline_for_stack ++au_opts_parse_xino_itrunc_path(struct super_block *sb, ++ struct au_opt_xino_itrunc *xino_itrunc, ++ substring_t args[]) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct path path; ++ struct dentry *root; ++ ++ err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ pr_err("lookup failed %s (%d)\n", args[0].from, err); ++ goto out; ++ } ++ ++ xino_itrunc->bindex = -1; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (au_h_dptr(root, bindex) == path.dentry) { ++ xino_itrunc->bindex = bindex; ++ break; ++ } ++ } ++ aufs_read_unlock(root, !AuLock_IR); ++ path_put(&path); ++ ++ if (unlikely(xino_itrunc->bindex < 0)) { ++ pr_err("no such branch %s\n", args[0].from); ++ err = -EINVAL; ++ } ++ ++out: ++ return err; ++} ++ ++/* called without aufs lock */ ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) ++{ ++ int err, n, token; ++ aufs_bindex_t bindex; ++ unsigned char skipped; ++ struct dentry *root; ++ struct au_opt *opt, *opt_tail; ++ char *opt_str; ++ /* reduce the stack space */ ++ union { ++ struct au_opt_xino_itrunc *xino_itrunc; ++ struct au_opt_wbr_create *create; ++ } u; ++ struct { ++ substring_t args[MAX_OPT_ARGS]; ++ } *a; ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ root = sb->s_root; ++ err = 0; ++ bindex = 0; ++ opt = opts->opt; ++ opt_tail = opt + opts->max_opt - 1; ++ opt->type = Opt_tail; ++ while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { ++ err = -EINVAL; ++ skipped = 0; ++ token = match_token(opt_str, options, a->args); ++ switch (token) { ++ case Opt_br: ++ err = 0; ++ while (!err && (opt_str = strsep(&a->args[0].from, ":")) ++ && *opt_str) { ++ err = opt_add(opt, opt_str, opts->sb_flags, ++ bindex++); ++ if (unlikely(!err && ++opt > opt_tail)) { ++ err = -E2BIG; ++ break; ++ } ++ opt->type = Opt_tail; ++ skipped = 1; ++ } ++ break; ++ case Opt_add: ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ bindex = n; ++ err = opt_add(opt, a->args[1].from, opts->sb_flags, ++ bindex); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_append: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*dummy bindex*/1); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_prepend: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*bindex*/0); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_del: ++ err = au_opts_parse_del(&opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#if 0 /* reserved for future use */ ++ case Opt_idel: ++ del->pathname = "(indexed)"; ++ if (unlikely(match_int(&args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_idel(sb, n, &opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_mod: ++ err = au_opts_parse_mod(&opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#ifdef IMOD /* reserved for future use */ ++ case Opt_imod: ++ u.mod->path = "(indexed)"; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_imod(sb, n, &opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_xino: ++ err = au_opts_parse_xino(sb, &opt->xino, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_trunc_xino_path: ++ err = au_opts_parse_xino_itrunc_path ++ (sb, &opt->xino_itrunc, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ u.xino_itrunc->bindex = n; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (n < 0 || au_sbend(sb) < n) { ++ pr_err("out of bounds, %d\n", n); ++ aufs_read_unlock(root, !AuLock_IR); ++ break; ++ } ++ aufs_read_unlock(root, !AuLock_IR); ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_dirwh: ++ if (unlikely(match_int(&a->args[0], &opt->dirwh))) ++ break; ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_rdcache: ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (unlikely(n > AUFS_RDCACHE_MAX)) { ++ pr_err("rdcache must be smaller than %d\n", ++ AUFS_RDCACHE_MAX); ++ break; ++ } ++ opt->rdcache = n; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdblk: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0 ++ || n > KMALLOC_MAX_SIZE)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (unlikely(n && n < NAME_MAX)) { ++ pr_err("rdblk must be larger than %d\n", ++ NAME_MAX); ++ break; ++ } ++ opt->rdblk = n; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdhash: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0 ++ || n * sizeof(struct hlist_head) ++ > KMALLOC_MAX_SIZE)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ opt->rdhash = n; ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_trunc_xino: ++ case Opt_notrunc_xino: ++ case Opt_noxino: ++ case Opt_trunc_xib: ++ case Opt_notrunc_xib: ++ case Opt_shwh: ++ case Opt_noshwh: ++ case Opt_dirperm1: ++ case Opt_nodirperm1: ++ case Opt_plink: ++ case Opt_noplink: ++ case Opt_list_plink: ++ case Opt_dio: ++ case Opt_nodio: ++ case Opt_diropq_a: ++ case Opt_diropq_w: ++ case Opt_warn_perm: ++ case Opt_nowarn_perm: ++ case Opt_verbose: ++ case Opt_noverbose: ++ case Opt_sum: ++ case Opt_nosum: ++ case Opt_wsum: ++ case Opt_rdblk_def: ++ case Opt_rdhash_def: ++ case Opt_acl: ++ case Opt_noacl: ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_udba: ++ opt->udba = udba_val(a->args[0].from); ++ if (opt->udba >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ u.create->wbr_create ++ = au_wbr_create_val(a->args[0].from, u.create); ++ if (u.create->wbr_create >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ case Opt_wbr_copyup: ++ opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); ++ if (opt->wbr_copyup >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_fhsm_sec: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (sysaufs_brs) { ++ opt->fhsm_second = n; ++ opt->type = token; ++ } else ++ pr_warn("ignored %s\n", opt_str); ++ err = 0; ++ break; ++ ++ case Opt_ignore: ++ pr_warn("ignored %s\n", opt_str); ++ /*FALLTHROUGH*/ ++ case Opt_ignore_silent: ++ skipped = 1; ++ err = 0; ++ break; ++ case Opt_err: ++ pr_err("unknown option %s\n", opt_str); ++ break; ++ } ++ ++ if (!err && !skipped) { ++ if (unlikely(++opt > opt_tail)) { ++ err = -E2BIG; ++ opt--; ++ opt->type = Opt_tail; ++ break; ++ } ++ opt->type = Opt_tail; ++ } ++ } ++ ++ kfree(a); ++ dump_opts(opts); ++ if (unlikely(err)) ++ au_opts_free(opts); ++ ++out: ++ return err; ++} ++ ++static int au_opt_wbr_create(struct super_block *sb, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_wbr_create_ops->fin) { ++ err = sbinfo->si_wbr_create_ops->fin(sb); ++ if (!err) ++ err = 1; ++ } ++ ++ sbinfo->si_wbr_create = create->wbr_create; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; ++ switch (create->wbr_create) { ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_MFSRR: ++ case AuWbrCreate_PMFSRR: ++ case AuWbrCreate_PMFSRRV: ++ sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; ++ /*FALLTHROUGH*/ ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFS: ++ case AuWbrCreate_PMFSV: ++ sbinfo->si_wbr_mfs.mfs_expire ++ = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); ++ break; ++ } ++ ++ if (sbinfo->si_wbr_create_ops->init) ++ sbinfo->si_wbr_create_ops->init(sb); /* ignore */ ++ ++ return err; ++} ++ ++/* ++ * returns, ++ * plus: processed without an error ++ * zero: unprocessed ++ */ ++static int au_opt_simple(struct super_block *sb, struct au_opt *opt, ++ struct au_opts *opts) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ switch (opt->type) { ++ case Opt_udba: ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= opt->udba; ++ opts->given_udba |= opt->udba; ++ break; ++ ++ case Opt_plink: ++ au_opt_set(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_noplink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_put(sb, /*verbose*/1); ++ au_opt_clr(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_list_plink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_list(sb); ++ break; ++ ++ case Opt_dio: ++ au_opt_set(sbinfo->si_mntflags, DIO); ++ au_fset_opts(opts->flags, REFRESH_DYAOP); ++ break; ++ case Opt_nodio: ++ au_opt_clr(sbinfo->si_mntflags, DIO); ++ au_fset_opts(opts->flags, REFRESH_DYAOP); ++ break; ++ ++ case Opt_fhsm_sec: ++ au_fhsm_set(sbinfo, opt->fhsm_second); ++ break; ++ ++ case Opt_diropq_a: ++ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ case Opt_diropq_w: ++ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ ++ case Opt_warn_perm: ++ au_opt_set(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ case Opt_nowarn_perm: ++ au_opt_clr(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ ++ case Opt_verbose: ++ au_opt_set(sbinfo->si_mntflags, VERBOSE); ++ break; ++ case Opt_noverbose: ++ au_opt_clr(sbinfo->si_mntflags, VERBOSE); ++ break; ++ ++ case Opt_sum: ++ au_opt_set(sbinfo->si_mntflags, SUM); ++ break; ++ case Opt_wsum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_set(sbinfo->si_mntflags, SUM_W); ++ case Opt_nosum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_clr(sbinfo->si_mntflags, SUM_W); ++ break; ++ ++ case Opt_wbr_create: ++ err = au_opt_wbr_create(sb, &opt->wbr_create); ++ break; ++ case Opt_wbr_copyup: ++ sbinfo->si_wbr_copyup = opt->wbr_copyup; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; ++ break; ++ ++ case Opt_dirwh: ++ sbinfo->si_dirwh = opt->dirwh; ++ break; ++ ++ case Opt_rdcache: ++ sbinfo->si_rdcache ++ = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); ++ break; ++ case Opt_rdblk: ++ sbinfo->si_rdblk = opt->rdblk; ++ break; ++ case Opt_rdblk_def: ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ break; ++ case Opt_rdhash: ++ sbinfo->si_rdhash = opt->rdhash; ++ break; ++ case Opt_rdhash_def: ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ break; ++ ++ case Opt_shwh: ++ au_opt_set(sbinfo->si_mntflags, SHWH); ++ break; ++ case Opt_noshwh: ++ au_opt_clr(sbinfo->si_mntflags, SHWH); ++ break; ++ ++ case Opt_dirperm1: ++ au_opt_set(sbinfo->si_mntflags, DIRPERM1); ++ break; ++ case Opt_nodirperm1: ++ au_opt_clr(sbinfo->si_mntflags, DIRPERM1); ++ break; ++ ++ case Opt_trunc_xino: ++ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ case Opt_notrunc_xino: ++ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); ++ if (!err) ++ err = 1; ++ break; ++ ++ case Opt_trunc_xib: ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ break; ++ case Opt_notrunc_xib: ++ au_fclr_opts(opts->flags, TRUNC_XIB); ++ break; ++ ++ case Opt_acl: ++ sb->s_flags |= MS_POSIXACL; ++ break; ++ case Opt_noacl: ++ sb->s_flags &= ~MS_POSIXACL; ++ break; ++ ++ default: ++ err = 0; ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * returns tri-state. ++ * plus: processed without an error ++ * zero: unprocessed ++ * minus: error ++ */ ++static int au_opt_br(struct super_block *sb, struct au_opt *opt, ++ struct au_opts *opts) ++{ ++ int err, do_refresh; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_append: ++ opt->add.bindex = au_sbend(sb) + 1; ++ if (opt->add.bindex < 0) ++ opt->add.bindex = 0; ++ goto add; ++ case Opt_prepend: ++ opt->add.bindex = 0; ++ add: /* indented label */ ++ case Opt_add: ++ err = au_br_add(sb, &opt->add, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ ++ case Opt_del: ++ case Opt_idel: ++ err = au_br_del(sb, &opt->del, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ ++ case Opt_mod: ++ case Opt_imod: ++ err = au_br_mod(sb, &opt->mod, ++ au_ftest_opts(opts->flags, REMOUNT), ++ &do_refresh); ++ if (!err) { ++ err = 1; ++ if (do_refresh) ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ } ++ ++ return err; ++} ++ ++static int au_opt_xino(struct super_block *sb, struct au_opt *opt, ++ struct au_opt_xino **opt_xino, ++ struct au_opts *opts) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root, *parent, *h_root; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_xino: ++ err = au_xino_set(sb, &opt->xino, ++ !!au_ftest_opts(opts->flags, REMOUNT)); ++ if (unlikely(err)) ++ break; ++ ++ *opt_xino = &opt->xino; ++ au_xino_brid_set(sb, -1); ++ ++ /* safe d_parent access */ ++ parent = opt->xino.file->f_path.dentry->d_parent; ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ h_root = au_h_dptr(root, bindex); ++ if (h_root == parent) { ++ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); ++ break; ++ } ++ } ++ break; ++ ++ case Opt_noxino: ++ au_xino_clr(sb); ++ au_xino_brid_set(sb, -1); ++ *opt_xino = (void *)-1; ++ break; ++ } ++ ++ return err; ++} ++ ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending) ++{ ++ int err, fhsm; ++ aufs_bindex_t bindex, bend; ++ unsigned char do_plink, skip, do_free; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *root; ++ struct inode *dir, *h_dir; ++ struct au_sbinfo *sbinfo; ++ struct au_hinode *hdir; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); ++ ++ if (!(sb_flags & MS_RDONLY)) { ++ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) ++ pr_warn("first branch should be rw\n"); ++ if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) ++ pr_warn("shwh should be used with ro\n"); ++ } ++ ++ if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) ++ && !au_opt_test(sbinfo->si_mntflags, XINO)) ++ pr_warn("udba=*notify requires xino\n"); ++ ++ if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) ++ pr_warn("dirperm1 breaks the protection" ++ " by the permission bits on the lower branch\n"); ++ ++ err = 0; ++ fhsm = 0; ++ root = sb->s_root; ++ dir = d_inode(root); ++ do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ skip = 0; ++ h_dir = au_h_iptr(dir, bindex); ++ br = au_sbr(sb, bindex); ++ ++ if ((br->br_perm & AuBrAttr_ICEX) ++ && !h_dir->i_op->listxattr) ++ br->br_perm &= ~AuBrAttr_ICEX; ++#if 0 ++ if ((br->br_perm & AuBrAttr_ICEX_SEC) ++ && (au_br_sb(br)->s_flags & MS_NOSEC)) ++ br->br_perm &= ~AuBrAttr_ICEX_SEC; ++#endif ++ ++ do_free = 0; ++ wbr = br->br_wbr; ++ if (wbr) ++ wbr_wh_read_lock(wbr); ++ ++ if (!au_br_writable(br->br_perm)) { ++ do_free = !!wbr; ++ skip = (!wbr ++ || (!wbr->wbr_whbase ++ && !wbr->wbr_plink ++ && !wbr->wbr_orph)); ++ } else if (!au_br_wh_linkable(br->br_perm)) { ++ /* skip = (!br->br_whbase && !br->br_orph); */ ++ skip = (!wbr || !wbr->wbr_whbase); ++ if (skip && wbr) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ } else { ++ /* skip = (br->br_whbase && br->br_ohph); */ ++ skip = (wbr && wbr->wbr_whbase); ++ if (skip) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ } ++ if (wbr) ++ wbr_wh_read_unlock(wbr); ++ ++ if (au_br_fhsm(br->br_perm)) { ++ fhsm++; ++ AuDebugOn(!br->br_fhsm); ++ } ++ ++ if (skip) ++ continue; ++ ++ hdir = au_hi(dir, bindex); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ if (wbr) ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(br, sb); ++ if (wbr) ++ wbr_wh_write_unlock(wbr); ++ au_hn_imtx_unlock(hdir); ++ ++ if (!err && do_free) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ ++ if (fhsm >= 2) { ++ au_fset_si(sbinfo, FHSM); ++ for (bindex = bend; bindex >= 0; bindex--) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm)) { ++ au_fhsm_set_bottom(sb, bindex); ++ break; ++ } ++ } ++ } else { ++ au_fclr_si(sbinfo, FHSM); ++ au_fhsm_set_bottom(sb, -1); ++ } ++ ++ return err; ++} ++ ++int au_opts_mount(struct super_block *sb, struct au_opts *opts) ++{ ++ int err; ++ unsigned int tmp; ++ aufs_bindex_t bindex, bend; ++ struct au_opt *opt; ++ struct au_opt_xino *opt_xino, xino; ++ struct au_sbinfo *sbinfo; ++ struct au_branch *br; ++ struct inode *dir; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ opt_xino = NULL; ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) ++ err = au_opt_simple(sb, opt++, opts); ++ if (err > 0) ++ err = 0; ++ else if (unlikely(err < 0)) ++ goto out; ++ ++ /* disable xino and udba temporary */ ++ sbinfo = au_sbi(sb); ++ tmp = sbinfo->si_mntflags; ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); ++ ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) ++ err = au_opt_br(sb, opt++, opts); ++ if (err > 0) ++ err = 0; ++ else if (unlikely(err < 0)) ++ goto out; ++ ++ bend = au_sbend(sb); ++ if (unlikely(bend < 0)) { ++ err = -EINVAL; ++ pr_err("no branches\n"); ++ goto out; ++ } ++ ++ if (au_opt_test(tmp, XINO)) ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ opt = opts->opt; ++ while (!err && opt->type != Opt_tail) ++ err = au_opt_xino(sb, opt++, &opt_xino, opts); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_opts_verify(sb, sb->s_flags, tmp); ++ if (unlikely(err)) ++ goto out; ++ ++ /* restore xino */ ++ if (au_opt_test(tmp, XINO) && !opt_xino) { ++ xino.file = au_xino_def(sb); ++ err = PTR_ERR(xino.file); ++ if (IS_ERR(xino.file)) ++ goto out; ++ ++ err = au_xino_set(sb, &xino, /*remount*/0); ++ fput(xino.file); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ /* restore udba */ ++ tmp &= AuOptMask_UDBA; ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= tmp; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_hnotify_reset_br(tmp, br, br->br_perm); ++ if (unlikely(err)) ++ AuIOErr("hnotify failed on br %d, %d, ignored\n", ++ bindex, err); ++ /* go on even if err */ ++ } ++ if (au_opt_test(tmp, UDBA_HNOTIFY)) { ++ dir = d_inode(sb->s_root); ++ au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); ++ } ++ ++out: ++ return err; ++} ++ ++int au_opts_remount(struct super_block *sb, struct au_opts *opts) ++{ ++ int err, rerr; ++ struct inode *dir; ++ struct au_opt_xino *opt_xino; ++ struct au_opt *opt; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ dir = d_inode(sb->s_root); ++ sbinfo = au_sbi(sb); ++ err = 0; ++ opt_xino = NULL; ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) { ++ err = au_opt_simple(sb, opt, opts); ++ if (!err) ++ err = au_opt_br(sb, opt, opts); ++ if (!err) ++ err = au_opt_xino(sb, opt, &opt_xino, opts); ++ opt++; ++ } ++ if (err > 0) ++ err = 0; ++ AuTraceErr(err); ++ /* go on even err */ ++ ++ rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ ++ if (au_ftest_opts(opts->flags, TRUNC_XIB)) { ++ rerr = au_xib_trunc(sb); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ } ++ ++ /* will be handled by the caller */ ++ if (!au_ftest_opts(opts->flags, REFRESH) ++ && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) ++ au_fset_opts(opts->flags, REFRESH); ++ ++ AuDbg("status 0x%x\n", opts->flags); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_opt_udba(struct super_block *sb) ++{ ++ return au_mntflags(sb) & AuOptMask_UDBA; ++} +diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h +new file mode 100644 +index 0000000..da552ff +--- /dev/null ++++ b/fs/aufs/opts.h +@@ -0,0 +1,197 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#ifndef __AUFS_OPTS_H__ ++#define __AUFS_OPTS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct file; ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mount flags */ ++#define AuOpt_XINO 1 /* external inode number bitmap ++ and translation table */ ++#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ ++#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ ++#define AuOpt_UDBA_REVAL (1 << 3) ++#define AuOpt_UDBA_HNOTIFY (1 << 4) ++#define AuOpt_SHWH (1 << 5) /* show whiteout */ ++#define AuOpt_PLINK (1 << 6) /* pseudo-link */ ++#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm ++ bits */ ++#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ ++#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ ++#define AuOpt_SUM_W (1 << 11) /* unimplemented */ ++#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ ++#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ ++#define AuOpt_DIO (1 << 14) /* direct io */ ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuOpt_UDBA_HNOTIFY ++#define AuOpt_UDBA_HNOTIFY 0 ++#endif ++#ifndef CONFIG_AUFS_SHWH ++#undef AuOpt_SHWH ++#define AuOpt_SHWH 0 ++#endif ++ ++#define AuOpt_Def (AuOpt_XINO \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_PLINK \ ++ /* | AuOpt_DIRPERM1 */ \ ++ | AuOpt_WARN_PERM) ++#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_UDBA_HNOTIFY) ++ ++#define au_opt_test(flags, name) (flags & AuOpt_##name) ++#define au_opt_set(flags, name) do { \ ++ BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_set_udba(flags, name) do { \ ++ (flags) &= ~AuOptMask_UDBA; \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_clr(flags, name) do { \ ++ ((flags) &= ~AuOpt_##name); \ ++} while (0) ++ ++static inline unsigned int au_opts_plink(unsigned int mntflags) ++{ ++#ifdef CONFIG_PROC_FS ++ return mntflags; ++#else ++ return mntflags & ~AuOpt_PLINK; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies to select one among multiple writable branches */ ++enum { ++ AuWbrCreate_TDP, /* top down parent */ ++ AuWbrCreate_RR, /* round robin */ ++ AuWbrCreate_MFS, /* most free space */ ++ AuWbrCreate_MFSV, /* mfs with seconds */ ++ AuWbrCreate_MFSRR, /* mfs then rr */ ++ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ ++ AuWbrCreate_PMFS, /* parent and mfs */ ++ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ ++ AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ ++ AuWbrCreate_PMFSRRV, /* plus seconds */ ++ ++ AuWbrCreate_Def = AuWbrCreate_TDP ++}; ++ ++enum { ++ AuWbrCopyup_TDP, /* top down parent */ ++ AuWbrCopyup_BUP, /* bottom up parent */ ++ AuWbrCopyup_BU, /* bottom up */ ++ ++ AuWbrCopyup_Def = AuWbrCopyup_TDP ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_opt_add { ++ aufs_bindex_t bindex; ++ char *pathname; ++ int perm; ++ struct path path; ++}; ++ ++struct au_opt_del { ++ char *pathname; ++ struct path h_path; ++}; ++ ++struct au_opt_mod { ++ char *path; ++ int perm; ++ struct dentry *h_root; ++}; ++ ++struct au_opt_xino { ++ char *path; ++ struct file *file; ++}; ++ ++struct au_opt_xino_itrunc { ++ aufs_bindex_t bindex; ++}; ++ ++struct au_opt_wbr_create { ++ int wbr_create; ++ int mfs_second; ++ unsigned long long mfsrr_watermark; ++}; ++ ++struct au_opt { ++ int type; ++ union { ++ struct au_opt_xino xino; ++ struct au_opt_xino_itrunc xino_itrunc; ++ struct au_opt_add add; ++ struct au_opt_del del; ++ struct au_opt_mod mod; ++ int dirwh; ++ int rdcache; ++ unsigned int rdblk; ++ unsigned int rdhash; ++ int udba; ++ struct au_opt_wbr_create wbr_create; ++ int wbr_copyup; ++ unsigned int fhsm_second; ++ }; ++}; ++ ++/* opts flags */ ++#define AuOpts_REMOUNT 1 ++#define AuOpts_REFRESH (1 << 1) ++#define AuOpts_TRUNC_XIB (1 << 2) ++#define AuOpts_REFRESH_DYAOP (1 << 3) ++#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) ++#define au_fset_opts(flags, name) \ ++ do { (flags) |= AuOpts_##name; } while (0) ++#define au_fclr_opts(flags, name) \ ++ do { (flags) &= ~AuOpts_##name; } while (0) ++ ++struct au_opts { ++ struct au_opt *opt; ++ int max_opt; ++ ++ unsigned int given_udba; ++ unsigned int flags; ++ unsigned long sb_flags; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* opts.c */ ++void au_optstr_br_perm(au_br_perm_str_t *str, int perm); ++const char *au_optstr_udba(int udba); ++const char *au_optstr_wbr_copyup(int wbr_copyup); ++const char *au_optstr_wbr_create(int wbr_create); ++ ++void au_opts_free(struct au_opts *opts); ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending); ++int au_opts_mount(struct super_block *sb, struct au_opts *opts); ++int au_opts_remount(struct super_block *sb, struct au_opts *opts); ++ ++unsigned int au_opt_udba(struct super_block *sb); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_OPTS_H__ */ +diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c +new file mode 100644 +index 0000000..af2246c +--- /dev/null ++++ b/fs/aufs/plink.c +@@ -0,0 +1,515 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * pseudo-link ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * the pseudo-link maintenance mode. ++ * during a user process maintains the pseudo-links, ++ * prohibit adding a new plink and branch manipulation. ++ * ++ * Flags ++ * NOPLM: ++ * For entry functions which will handle plink, and i_mutex is already held ++ * in VFS. ++ * They cannot wait and should return an error at once. ++ * Callers has to check the error. ++ * NOPLMW: ++ * For entry functions which will handle plink, but i_mutex is not held ++ * in VFS. ++ * They can wait the plink maintenance mode to finish. ++ * ++ * They behave like F_SETLK and F_SETLKW. ++ * If the caller never handle plink, then both flags are unnecessary. ++ */ ++ ++int au_plink_maint(struct super_block *sb, int flags) ++{ ++ int err; ++ pid_t pid, ppid; ++ struct au_sbinfo *sbi; ++ ++ SiMustAnyLock(sb); ++ ++ err = 0; ++ if (!au_opt_test(au_mntflags(sb), PLINK)) ++ goto out; ++ ++ sbi = au_sbi(sb); ++ pid = sbi->si_plink_maint_pid; ++ if (!pid || pid == current->pid) ++ goto out; ++ ++ /* todo: it highly depends upon /sbin/mount.aufs */ ++ rcu_read_lock(); ++ ppid = task_pid_vnr(rcu_dereference(current->real_parent)); ++ rcu_read_unlock(); ++ if (pid == ppid) ++ goto out; ++ ++ if (au_ftest_lock(flags, NOPLMW)) { ++ /* if there is no i_mutex lock in VFS, we don't need to wait */ ++ /* AuDebugOn(!lockdep_depth(current)); */ ++ while (sbi->si_plink_maint_pid) { ++ si_read_unlock(sb); ++ /* gave up wake_up_bit() */ ++ wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); ++ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&sbi->si_nowait); ++ si_noflush_read_lock(sb); ++ } ++ } else if (au_ftest_lock(flags, NOPLM)) { ++ AuDbg("ppid %d, pid %d\n", ppid, pid); ++ err = -EAGAIN; ++ } ++ ++out: ++ return err; ++} ++ ++void au_plink_maint_leave(struct au_sbinfo *sbinfo) ++{ ++ spin_lock(&sbinfo->si_plink_maint_lock); ++ sbinfo->si_plink_maint_pid = 0; ++ spin_unlock(&sbinfo->si_plink_maint_lock); ++ wake_up_all(&sbinfo->si_plink_wq); ++} ++ ++int au_plink_maint_enter(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ /* make sure i am the only one in this fs */ ++ si_write_lock(sb, AuLock_FLUSH); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ spin_lock(&sbinfo->si_plink_maint_lock); ++ if (!sbinfo->si_plink_maint_pid) ++ sbinfo->si_plink_maint_pid = current->pid; ++ else ++ err = -EBUSY; ++ spin_unlock(&sbinfo->si_plink_maint_lock); ++ } ++ si_write_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb) ++{ ++ int i; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct pseudo_link *plink; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) ++ AuDbg("%lu\n", plink->inode->i_ino); ++ rcu_read_unlock(); ++ } ++} ++#endif ++ ++/* is the inode pseudo-linked? */ ++int au_plink_test(struct inode *inode) ++{ ++ int found, i; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct pseudo_link *plink; ++ ++ sbinfo = au_sbi(inode->i_sb); ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); ++ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); ++ ++ found = 0; ++ i = au_plink_hash(inode->i_ino); ++ plink_hlist = &sbinfo->si_plink[i].head; ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ rcu_read_unlock(); ++ return found; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generate a name for plink. ++ * the file will be stored under AUFS_WH_PLINKDIR. ++ */ ++/* 20 is max digits length of ulong 64 */ ++#define PLINK_NAME_LEN ((20 + 1) * 2) ++ ++static int plink_name(char *name, int len, struct inode *inode, ++ aufs_bindex_t bindex) ++{ ++ int rlen; ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, bindex); ++ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); ++ return rlen; ++} ++ ++struct au_do_plink_lkup_args { ++ struct dentry **errp; ++ struct qstr *tgtname; ++ struct dentry *h_parent; ++ struct au_branch *br; ++}; ++ ++static struct dentry *au_do_plink_lkup(struct qstr *tgtname, ++ struct dentry *h_parent, ++ struct au_branch *br) ++{ ++ struct dentry *h_dentry; ++ struct mutex *h_mtx; ++ ++ h_mtx = &d_inode(h_parent)->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ h_dentry = vfsub_lkup_one(tgtname, h_parent); ++ mutex_unlock(h_mtx); ++ return h_dentry; ++} ++ ++static void au_call_do_plink_lkup(void *args) ++{ ++ struct au_do_plink_lkup_args *a = args; ++ *a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br); ++} ++ ++/* lookup the plink-ed @inode under the branch at @bindex */ ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ int wkq_err; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = QSTR_INIT(a, 0); ++ ++ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); ++ ++ br = au_sbr(inode->i_sb, bindex); ++ h_parent = br->br_wbr->wbr_plink; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { ++ struct au_do_plink_lkup_args args = { ++ .errp = &h_dentry, ++ .tgtname = &tgtname, ++ .h_parent = h_parent, ++ .br = br ++ }; ++ ++ wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); ++ if (unlikely(wkq_err)) ++ h_dentry = ERR_PTR(wkq_err); ++ } else ++ h_dentry = au_do_plink_lkup(&tgtname, h_parent, br); ++ ++ return h_dentry; ++} ++ ++/* create a pseudo-link */ ++static int do_whplink(struct qstr *tgt, struct dentry *h_parent, ++ struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = au_br_mnt(br) ++ }; ++ struct inode *h_dir, *delegated; ++ ++ h_dir = d_inode(h_parent); ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); ++again: ++ h_path.dentry = vfsub_lkup_one(tgt, h_parent); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ /* wh.plink dir is not monitored */ ++ /* todo: is it really safe? */ ++ if (d_is_positive(h_path.dentry) ++ && d_inode(h_path.dentry) != d_inode(h_dentry)) { ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ dput(h_path.dentry); ++ h_path.dentry = NULL; ++ if (!err) ++ goto again; ++ } ++ if (!err && d_is_negative(h_path.dentry)) { ++ delegated = NULL; ++ err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ dput(h_path.dentry); ++ ++out: ++ mutex_unlock(&h_dir->i_mutex); ++ return err; ++} ++ ++struct do_whplink_args { ++ int *errp; ++ struct qstr *tgt; ++ struct dentry *h_parent; ++ struct dentry *h_dentry; ++ struct au_branch *br; ++}; ++ ++static void call_do_whplink(void *args) ++{ ++ struct do_whplink_args *a = args; ++ *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); ++} ++ ++static int whplink(struct dentry *h_dentry, struct inode *inode, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err, wkq_err; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = QSTR_INIT(a, 0); ++ ++ wbr = au_sbr(inode->i_sb, bindex)->br_wbr; ++ h_parent = wbr->wbr_plink; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ /* always superio. */ ++ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { ++ struct do_whplink_args args = { ++ .errp = &err, ++ .tgt = &tgtname, ++ .h_parent = h_parent, ++ .h_dentry = h_dentry, ++ .br = br ++ }; ++ wkq_err = au_wkq_wait(call_do_whplink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } else ++ err = do_whplink(&tgtname, h_parent, h_dentry, br); ++ ++ return err; ++} ++ ++/* free a single plink */ ++static void do_put_plink(struct pseudo_link *plink, int do_del) ++{ ++ if (do_del) ++ hlist_del(&plink->hlist); ++ iput(plink->inode); ++ kfree(plink); ++} ++ ++static void do_put_plink_rcu(struct rcu_head *rcu) ++{ ++ struct pseudo_link *plink; ++ ++ plink = container_of(rcu, struct pseudo_link, rcu); ++ iput(plink->inode); ++ kfree(plink); ++} ++ ++/* ++ * create a new pseudo-link for @h_dentry on @bindex. ++ * the linked inode is held in aufs @inode. ++ */ ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct pseudo_link *plink, *tmp; ++ struct au_sphlhead *sphl; ++ int found, err, cnt, i; ++ ++ sb = inode->i_sb; ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ found = au_plink_test(inode); ++ if (found) ++ return; ++ ++ i = au_plink_hash(inode->i_ino); ++ sphl = sbinfo->si_plink + i; ++ plink_hlist = &sphl->head; ++ tmp = kmalloc(sizeof(*plink), GFP_NOFS); ++ if (tmp) ++ tmp->inode = au_igrab(inode); ++ else { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ spin_lock(&sphl->spin); ++ hlist_for_each_entry(plink, plink_hlist, hlist) { ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ hlist_add_head_rcu(&tmp->hlist, plink_hlist); ++ spin_unlock(&sphl->spin); ++ if (!found) { ++ cnt = au_sphl_count(sphl); ++#define msg "unexpectedly unblanced or too many pseudo-links" ++ if (cnt > AUFS_PLINK_WARN) ++ AuWarn1(msg ", %d\n", cnt); ++#undef msg ++ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); ++ } else { ++ do_put_plink(tmp, 0); ++ return; ++ } ++ ++out: ++ if (unlikely(err)) { ++ pr_warn("err %d, damaged pseudo link.\n", err); ++ if (tmp) { ++ au_sphl_del_rcu(&tmp->hlist, sphl); ++ call_rcu(&tmp->rcu, do_put_plink_rcu); ++ } ++ } ++} ++ ++/* free all plinks */ ++void au_plink_put(struct super_block *sb, int verbose) ++{ ++ int i, warned; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *tmp; ++ struct pseudo_link *plink; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ /* no spin_lock since sbinfo is write-locked */ ++ warned = 0; ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ if (!warned && verbose && !hlist_empty(plink_hlist)) { ++ pr_warn("pseudo-link is not flushed"); ++ warned = 1; ++ } ++ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) ++ do_put_plink(plink, 0); ++ INIT_HLIST_HEAD(plink_hlist); ++ } ++} ++ ++void au_plink_clean(struct super_block *sb, int verbose) ++{ ++ struct dentry *root; ++ ++ root = sb->s_root; ++ aufs_write_lock(root); ++ if (au_opt_test(au_mntflags(sb), PLINK)) ++ au_plink_put(sb, verbose); ++ aufs_write_unlock(root); ++} ++ ++static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) ++{ ++ int do_put; ++ aufs_bindex_t bstart, bend, bindex; ++ ++ do_put = 0; ++ bstart = au_ibstart(inode); ++ bend = au_ibend(inode); ++ if (bstart >= 0) { ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ if (!au_h_iptr(inode, bindex) ++ || au_ii_br_id(inode, bindex) != br_id) ++ continue; ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ do_put = 1; ++ break; ++ } ++ if (do_put) ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex)) { ++ do_put = 0; ++ break; ++ } ++ } else ++ do_put = 1; ++ ++ return do_put; ++} ++ ++/* free the plinks on a branch specified by @br_id */ ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *tmp; ++ struct pseudo_link *plink; ++ struct inode *inode; ++ int i, do_put; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ /* no spin_lock since sbinfo is write-locked */ ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) { ++ inode = au_igrab(plink->inode); ++ ii_write_lock_child(inode); ++ do_put = au_plink_do_half_refresh(inode, br_id); ++ if (do_put) ++ do_put_plink(plink, 1); ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++ } ++} +diff --git a/fs/aufs/poll.c b/fs/aufs/poll.c +new file mode 100644 +index 0000000..c324fc7 +--- /dev/null ++++ b/fs/aufs/poll.c +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * poll operation ++ * There is only one filesystem which implements ->poll operation, currently. ++ */ ++ ++#include "aufs.h" ++ ++unsigned int aufs_poll(struct file *file, poll_table *wait) ++{ ++ unsigned int mask; ++ int err; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ /* We should pretend an error happened. */ ++ mask = POLLERR /* | POLLIN | POLLOUT */; ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ++ h_file = au_read_pre(file, /*keep_fi*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ /* it is not an error if h_file has no operation */ ++ mask = DEFAULT_POLLMASK; ++ if (h_file->f_op->poll) ++ mask = h_file->f_op->poll(h_file, wait); ++ fput(h_file); /* instead of au_read_post() */ ++ ++out: ++ si_read_unlock(sb); ++ AuTraceErr((int)mask); ++ return mask; ++} +diff --git a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c +new file mode 100644 +index 0000000..d9ce957 +--- /dev/null ++++ b/fs/aufs/posix_acl.c +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (C) 2014-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * posix acl operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++struct posix_acl *aufs_get_acl(struct inode *inode, int type) ++{ ++ struct posix_acl *acl; ++ int err; ++ aufs_bindex_t bindex; ++ struct inode *h_inode; ++ struct super_block *sb; ++ ++ acl = NULL; ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_read_lock_child(inode); ++ if (!(sb->s_flags & MS_POSIXACL)) ++ goto out; ++ ++ bindex = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (unlikely(!h_inode ++ || ((h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT)))) { ++ err = au_busy_or_stale(); ++ acl = ERR_PTR(err); ++ goto out; ++ } ++ ++ /* always topmost only */ ++ acl = get_acl(h_inode, type); ++ ++out: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++ ++ AuTraceErrPtr(acl); ++ return acl; ++} ++ ++int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) ++{ ++ int err; ++ ssize_t ssz; ++ struct dentry *dentry; ++ struct au_srxattr arg = { ++ .type = AU_ACL_SET, ++ .u.acl_set = { ++ .acl = acl, ++ .type = type ++ }, ++ }; ++ ++ mutex_lock(&inode->i_mutex); ++ if (inode->i_ino == AUFS_ROOT_INO) ++ dentry = dget(inode->i_sb->s_root); ++ else { ++ dentry = d_find_alias(inode); ++ if (!dentry) ++ dentry = d_find_any_alias(inode); ++ if (!dentry) { ++ pr_warn("cannot handle this inode, " ++ "please report to aufs-users ML\n"); ++ err = -ENOENT; ++ goto out; ++ } ++ } ++ ++ ssz = au_srxattr(dentry, &arg); ++ dput(dentry); ++ err = ssz; ++ if (ssz >= 0) ++ err = 0; ++ ++out: ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} +diff --git a/fs/aufs/procfs.c b/fs/aufs/procfs.c +new file mode 100644 +index 0000000..ead2a00 +--- /dev/null ++++ b/fs/aufs/procfs.c +@@ -0,0 +1,156 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * procfs interfaces ++ */ ++ ++#include ++#include "aufs.h" ++ ++static int au_procfs_plm_release(struct inode *inode, struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = file->private_data; ++ if (sbinfo) { ++ au_plink_maint_leave(sbinfo); ++ kobject_put(&sbinfo->si_kobj); ++ } ++ ++ return 0; ++} ++ ++static void au_procfs_plm_write_clean(struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = file->private_data; ++ if (sbinfo) ++ au_plink_clean(sbinfo->si_sb, /*verbose*/0); ++} ++ ++static int au_procfs_plm_write_si(struct file *file, unsigned long id) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ err = -EBUSY; ++ if (unlikely(file->private_data)) ++ goto out; ++ ++ sb = NULL; ++ /* don't use au_sbilist_lock() here */ ++ spin_lock(&au_sbilist.spin); ++ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ if (id == sysaufs_si_id(sbinfo)) { ++ kobject_get(&sbinfo->si_kobj); ++ sb = sbinfo->si_sb; ++ break; ++ } ++ spin_unlock(&au_sbilist.spin); ++ ++ err = -EINVAL; ++ if (unlikely(!sb)) ++ goto out; ++ ++ err = au_plink_maint_enter(sb); ++ if (!err) ++ /* keep kobject_get() */ ++ file->private_data = sbinfo; ++ else ++ kobject_put(&sbinfo->si_kobj); ++out: ++ return err; ++} ++ ++/* ++ * Accept a valid "si=xxxx" only. ++ * Once it is accepted successfully, accept "clean" too. ++ */ ++static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ unsigned long id; ++ /* last newline is allowed */ ++ char buf[3 + sizeof(unsigned long) * 2 + 1]; ++ ++ err = -EACCES; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(count > sizeof(buf))) ++ goto out; ++ ++ err = copy_from_user(buf, ubuf, count); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ goto out; ++ } ++ buf[count] = 0; ++ ++ err = -EINVAL; ++ if (!strcmp("clean", buf)) { ++ au_procfs_plm_write_clean(file); ++ goto out_success; ++ } else if (unlikely(strncmp("si=", buf, 3))) ++ goto out; ++ ++ err = kstrtoul(buf + 3, 16, &id); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_procfs_plm_write_si(file, id); ++ if (unlikely(err)) ++ goto out; ++ ++out_success: ++ err = count; /* success */ ++out: ++ return err; ++} ++ ++static const struct file_operations au_procfs_plm_fop = { ++ .write = au_procfs_plm_write, ++ .release = au_procfs_plm_release, ++ .owner = THIS_MODULE ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct proc_dir_entry *au_procfs_dir; ++ ++void au_procfs_fin(void) ++{ ++ remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); ++ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); ++} ++ ++int __init au_procfs_init(void) ++{ ++ int err; ++ struct proc_dir_entry *entry; ++ ++ err = -ENOMEM; ++ au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); ++ if (unlikely(!au_procfs_dir)) ++ goto out; ++ ++ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, ++ au_procfs_dir, &au_procfs_plm_fop); ++ if (unlikely(!entry)) ++ goto out_dir; ++ ++ err = 0; ++ goto out; /* success */ ++ ++ ++out_dir: ++ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); ++out: ++ return err; ++} +diff --git a/fs/aufs/rdu.c b/fs/aufs/rdu.c +new file mode 100644 +index 0000000..f11d9bb +--- /dev/null ++++ b/fs/aufs/rdu.c +@@ -0,0 +1,375 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * readdir in userspace. ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* bits for struct aufs_rdu.flags */ ++#define AuRdu_CALLED 1 ++#define AuRdu_CONT (1 << 1) ++#define AuRdu_FULL (1 << 2) ++#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) ++#define au_fset_rdu(flags, name) \ ++ do { (flags) |= AuRdu_##name; } while (0) ++#define au_fclr_rdu(flags, name) \ ++ do { (flags) &= ~AuRdu_##name; } while (0) ++ ++struct au_rdu_arg { ++ struct dir_context ctx; ++ struct aufs_rdu *rdu; ++ union au_rdu_ent_ul ent; ++ unsigned long end; ++ ++ struct super_block *sb; ++ int err; ++}; ++ ++static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, ++ loff_t offset, u64 h_ino, unsigned int d_type) ++{ ++ int err, len; ++ struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); ++ struct aufs_rdu *rdu = arg->rdu; ++ struct au_rdu_ent ent; ++ ++ err = 0; ++ arg->err = 0; ++ au_fset_rdu(rdu->cookie.flags, CALLED); ++ len = au_rdu_len(nlen); ++ if (arg->ent.ul + len < arg->end) { ++ ent.ino = h_ino; ++ ent.bindex = rdu->cookie.bindex; ++ ent.type = d_type; ++ ent.nlen = nlen; ++ if (unlikely(nlen > AUFS_MAX_NAMELEN)) ++ ent.type = DT_UNKNOWN; ++ ++ /* unnecessary to support mmap_sem since this is a dir */ ++ err = -EFAULT; ++ if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) ++ goto out; ++ if (copy_to_user(arg->ent.e->name, name, nlen)) ++ goto out; ++ /* the terminating NULL */ ++ if (__put_user(0, arg->ent.e->name + nlen)) ++ goto out; ++ err = 0; ++ /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ ++ arg->ent.ul += len; ++ rdu->rent++; ++ } else { ++ err = -EFAULT; ++ au_fset_rdu(rdu->cookie.flags, FULL); ++ rdu->full = 1; ++ rdu->tail = arg->ent; ++ } ++ ++out: ++ /* AuTraceErr(err); */ ++ return err; ++} ++ ++static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) ++{ ++ int err; ++ loff_t offset; ++ struct au_rdu_cookie *cookie = &arg->rdu->cookie; ++ ++ /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ ++ offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); ++ err = offset; ++ if (unlikely(offset != cookie->h_pos)) ++ goto out; ++ ++ err = 0; ++ do { ++ arg->err = 0; ++ au_fclr_rdu(cookie->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(h_file, &arg->ctx); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err ++ && au_ftest_rdu(cookie->flags, CALLED) ++ && !au_ftest_rdu(cookie->flags, FULL)); ++ cookie->h_pos = h_file->f_pos; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_rdu(struct file *file, struct aufs_rdu *rdu) ++{ ++ int err; ++ aufs_bindex_t bend; ++ struct au_rdu_arg arg = { ++ .ctx = { ++ .actor = au_rdu_fill ++ } ++ }; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *h_file; ++ struct au_rdu_cookie *cookie = &rdu->cookie; ++ ++ err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ rdu->rent = 0; ++ rdu->tail = rdu->ent; ++ rdu->full = 0; ++ arg.rdu = rdu; ++ arg.ent = rdu->ent; ++ arg.end = arg.ent.ul; ++ arg.end += rdu->sz; ++ ++ err = -ENOTDIR; ++ if (unlikely(!file->f_op->iterate)) ++ goto out; ++ ++ err = security_file_permission(file, MAY_READ); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_path.dentry; ++ inode = d_inode(dentry); ++#if 1 ++ mutex_lock(&inode->i_mutex); ++#else ++ err = mutex_lock_killable(&inode->i_mutex); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++#endif ++ ++ arg.sb = inode->i_sb; ++ err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_mtx; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out_si; ++ /* todo: reval? */ ++ fi_read_lock(file); ++ ++ err = -EAGAIN; ++ if (unlikely(au_ftest_rdu(cookie->flags, CONT) ++ && cookie->generation != au_figen(file))) ++ goto out_unlock; ++ ++ err = 0; ++ if (!rdu->blk) { ++ rdu->blk = au_sbi(arg.sb)->si_rdblk; ++ if (!rdu->blk) ++ rdu->blk = au_dir_size(file, /*dentry*/NULL); ++ } ++ bend = au_fbstart(file); ++ if (cookie->bindex < bend) ++ cookie->bindex = bend; ++ bend = au_fbend_dir(file); ++ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ ++ for (; !err && cookie->bindex <= bend; ++ cookie->bindex++, cookie->h_pos = 0) { ++ h_file = au_hf_dir(file, cookie->bindex); ++ if (!h_file) ++ continue; ++ ++ au_fclr_rdu(cookie->flags, FULL); ++ err = au_rdu_do(h_file, &arg); ++ AuTraceErr(err); ++ if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) ++ break; ++ } ++ AuDbg("rent %llu\n", rdu->rent); ++ ++ if (!err && !au_ftest_rdu(cookie->flags, CONT)) { ++ rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); ++ au_fset_rdu(cookie->flags, CONT); ++ cookie->generation = au_figen(file); ++ } ++ ++ ii_read_lock_child(inode); ++ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); ++ ii_read_unlock(inode); ++ ++out_unlock: ++ fi_read_unlock(file); ++out_si: ++ si_read_unlock(arg.sb); ++out_mtx: ++ mutex_unlock(&inode->i_mutex); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) ++{ ++ int err; ++ ino_t ino; ++ unsigned long long nent; ++ union au_rdu_ent_ul *u; ++ struct au_rdu_ent ent; ++ struct super_block *sb; ++ ++ err = 0; ++ nent = rdu->nent; ++ u = &rdu->ent; ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ while (nent-- > 0) { ++ /* unnecessary to support mmap_sem since this is a dir */ ++ err = copy_from_user(&ent, u->e, sizeof(ent)); ++ if (!err) ++ err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ break; ++ } ++ ++ /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ ++ if (!ent.wh) ++ err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); ++ else ++ err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, ++ &ino); ++ if (unlikely(err)) { ++ AuTraceErr(err); ++ break; ++ } ++ ++ err = __put_user(ino, &u->e->ino); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ break; ++ } ++ u->ul += au_rdu_len(ent.nlen); ++ } ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_rdu_verify(struct aufs_rdu *rdu) ++{ ++ AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " ++ "%llu, b%d, 0x%x, g%u}\n", ++ rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], ++ rdu->blk, ++ rdu->rent, rdu->shwh, rdu->full, ++ rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, ++ rdu->cookie.generation); ++ ++ if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) ++ return 0; ++ ++ AuDbg("%u:%u\n", ++ rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); ++ return -EINVAL; ++} ++ ++long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err, e; ++ struct aufs_rdu rdu; ++ void __user *p = (void __user *)arg; ++ ++ err = copy_from_user(&rdu, p, sizeof(rdu)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ err = au_rdu_verify(&rdu); ++ if (unlikely(err)) ++ goto out; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ err = au_rdu(file, &rdu); ++ if (unlikely(err)) ++ break; ++ ++ e = copy_to_user(p, &rdu, sizeof(rdu)); ++ if (unlikely(e)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ break; ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ino(file, &rdu); ++ break; ++ ++ default: ++ /* err = -ENOTTY; */ ++ err = -EINVAL; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++#ifdef CONFIG_COMPAT ++long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err, e; ++ struct aufs_rdu rdu; ++ void __user *p = compat_ptr(arg); ++ ++ /* todo: get_user()? */ ++ err = copy_from_user(&rdu, p, sizeof(rdu)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ rdu.ent.e = compat_ptr(rdu.ent.ul); ++ err = au_rdu_verify(&rdu); ++ if (unlikely(err)) ++ goto out; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ err = au_rdu(file, &rdu); ++ if (unlikely(err)) ++ break; ++ ++ rdu.ent.ul = ptr_to_compat(rdu.ent.e); ++ rdu.tail.ul = ptr_to_compat(rdu.tail.e); ++ e = copy_to_user(p, &rdu, sizeof(rdu)); ++ if (unlikely(e)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ break; ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ino(file, &rdu); ++ break; ++ ++ default: ++ /* err = -ENOTTY; */ ++ err = -EINVAL; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++#endif +diff --git a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h +new file mode 100644 +index 0000000..171ee0c +--- /dev/null ++++ b/fs/aufs/rwsem.h +@@ -0,0 +1,178 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * simple read-write semaphore wrappers ++ */ ++ ++#ifndef __AUFS_RWSEM_H__ ++#define __AUFS_RWSEM_H__ ++ ++#ifdef __KERNEL__ ++ ++#include "debug.h" ++ ++struct au_rwsem { ++ struct rw_semaphore rwsem; ++#ifdef CONFIG_AUFS_DEBUG ++ /* just for debugging, not almighty counter */ ++ atomic_t rcnt, wcnt; ++#endif ++}; ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuDbgCntInit(rw) do { \ ++ atomic_set(&(rw)->rcnt, 0); \ ++ atomic_set(&(rw)->wcnt, 0); \ ++ smp_mb(); /* atomic set */ \ ++} while (0) ++ ++#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) ++#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) ++#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) ++#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) ++#else ++#define AuDbgCntInit(rw) do {} while (0) ++#define AuDbgRcntInc(rw) do {} while (0) ++#define AuDbgRcntDec(rw) do {} while (0) ++#define AuDbgWcntInc(rw) do {} while (0) ++#define AuDbgWcntDec(rw) do {} while (0) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* to debug easier, do not make them inlined functions */ ++#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) ++/* rwsem_is_locked() is unusable */ ++#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) ++#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) ++#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ ++ && atomic_read(&(rw)->wcnt) <= 0) ++#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ ++ || atomic_read(&(rw)->wcnt)) ++ ++#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) ++ ++static inline void au_rw_init(struct au_rwsem *rw) ++{ ++ AuDbgCntInit(rw); ++ init_rwsem(&rw->rwsem); ++} ++ ++static inline void au_rw_init_wlock(struct au_rwsem *rw) ++{ ++ au_rw_init(rw); ++ down_write(&rw->rwsem); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, ++ unsigned int lsc) ++{ ++ au_rw_init(rw); ++ down_write_nested(&rw->rwsem, lsc); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_read_lock(struct au_rwsem *rw) ++{ ++ down_read(&rw->rwsem); ++ AuDbgRcntInc(rw); ++} ++ ++static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) ++{ ++ down_read_nested(&rw->rwsem, lsc); ++ AuDbgRcntInc(rw); ++} ++ ++static inline void au_rw_read_unlock(struct au_rwsem *rw) ++{ ++ AuRwMustReadLock(rw); ++ AuDbgRcntDec(rw); ++ up_read(&rw->rwsem); ++} ++ ++static inline void au_rw_dgrade_lock(struct au_rwsem *rw) ++{ ++ AuRwMustWriteLock(rw); ++ AuDbgRcntInc(rw); ++ AuDbgWcntDec(rw); ++ downgrade_write(&rw->rwsem); ++} ++ ++static inline void au_rw_write_lock(struct au_rwsem *rw) ++{ ++ down_write(&rw->rwsem); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_write_lock_nested(struct au_rwsem *rw, ++ unsigned int lsc) ++{ ++ down_write_nested(&rw->rwsem, lsc); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_write_unlock(struct au_rwsem *rw) ++{ ++ AuRwMustWriteLock(rw); ++ AuDbgWcntDec(rw); ++ up_write(&rw->rwsem); ++} ++ ++/* why is not _nested version defined */ ++static inline int au_rw_read_trylock(struct au_rwsem *rw) ++{ ++ int ret; ++ ++ ret = down_read_trylock(&rw->rwsem); ++ if (ret) ++ AuDbgRcntInc(rw); ++ return ret; ++} ++ ++static inline int au_rw_write_trylock(struct au_rwsem *rw) ++{ ++ int ret; ++ ++ ret = down_write_trylock(&rw->rwsem); ++ if (ret) ++ AuDbgWcntInc(rw); ++ return ret; ++} ++ ++#undef AuDbgCntInit ++#undef AuDbgRcntInc ++#undef AuDbgRcntDec ++#undef AuDbgWcntInc ++#undef AuDbgWcntDec ++ ++#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_lock(param) \ ++{ au_rw_read_lock(rwsem); } \ ++static inline void prefix##_write_lock(param) \ ++{ au_rw_write_lock(rwsem); } \ ++static inline int prefix##_read_trylock(param) \ ++{ return au_rw_read_trylock(rwsem); } \ ++static inline int prefix##_write_trylock(param) \ ++{ return au_rw_write_trylock(rwsem); } ++/* why is not _nested version defined */ ++/* static inline void prefix##_read_trylock_nested(param, lsc) ++{ au_rw_read_trylock_nested(rwsem, lsc)); } ++static inline void prefix##_write_trylock_nestd(param, lsc) ++{ au_rw_write_trylock_nested(rwsem, lsc); } */ ++ ++#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_unlock(param) \ ++{ au_rw_read_unlock(rwsem); } \ ++static inline void prefix##_write_unlock(param) \ ++{ au_rw_write_unlock(rwsem); } \ ++static inline void prefix##_downgrade_lock(param) \ ++{ au_rw_dgrade_lock(rwsem); } ++ ++#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_RWSEM_H__ */ +diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c +new file mode 100644 +index 0000000..5793673 +--- /dev/null ++++ b/fs/aufs/sbinfo.c +@@ -0,0 +1,343 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * superblock private data ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * they are necessary regardless sysfs is disabled. ++ */ ++void au_si_free(struct kobject *kobj) ++{ ++ int i; ++ struct au_sbinfo *sbinfo; ++ char *locked __maybe_unused; /* debug only */ ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ for (i = 0; i < AuPlink_NHASH; i++) ++ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); ++ AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); ++ ++ au_rw_write_lock(&sbinfo->si_rwsem); ++ au_br_free(sbinfo); ++ au_rw_write_unlock(&sbinfo->si_rwsem); ++ ++ AuDebugOn(radix_tree_gang_lookup ++ (&sbinfo->au_si_pid.tree, (void **)&locked, ++ /*first_index*/PID_MAX_DEFAULT - 1, ++ /*max_items*/sizeof(locked)/sizeof(*locked))); ++ ++ kfree(sbinfo->si_branch); ++ kfree(sbinfo->au_si_pid.bitmap); ++ mutex_destroy(&sbinfo->si_xib_mtx); ++ AuRwDestroy(&sbinfo->si_rwsem); ++ ++ kfree(sbinfo); ++} ++ ++int au_si_alloc(struct super_block *sb) ++{ ++ int err, i; ++ struct au_sbinfo *sbinfo; ++ static struct lock_class_key aufs_si; ++ ++ err = -ENOMEM; ++ sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); ++ if (unlikely(!sbinfo)) ++ goto out; ++ ++ BUILD_BUG_ON(sizeof(unsigned long) != ++ sizeof(*sbinfo->au_si_pid.bitmap)); ++ sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT), ++ sizeof(*sbinfo->au_si_pid.bitmap), ++ GFP_NOFS); ++ if (unlikely(!sbinfo->au_si_pid.bitmap)) ++ goto out_sbinfo; ++ ++ /* will be reallocated separately */ ++ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); ++ if (unlikely(!sbinfo->si_branch)) ++ goto out_pidmap; ++ ++ err = sysaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ goto out_br; ++ ++ au_nwt_init(&sbinfo->si_nowait); ++ au_rw_init_wlock(&sbinfo->si_rwsem); ++ au_rw_class(&sbinfo->si_rwsem, &aufs_si); ++ spin_lock_init(&sbinfo->au_si_pid.tree_lock); ++ INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); ++ ++ atomic_long_set(&sbinfo->si_ninodes, 0); ++ atomic_long_set(&sbinfo->si_nfiles, 0); ++ ++ sbinfo->si_bend = -1; ++ sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; ++ ++ sbinfo->si_wbr_copyup = AuWbrCopyup_Def; ++ sbinfo->si_wbr_create = AuWbrCreate_Def; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; ++ ++ au_fhsm_init(sbinfo); ++ ++ sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); ++ ++ sbinfo->si_xino_jiffy = jiffies; ++ sbinfo->si_xino_expire ++ = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); ++ mutex_init(&sbinfo->si_xib_mtx); ++ sbinfo->si_xino_brid = -1; ++ /* leave si_xib_last_pindex and si_xib_next_bit */ ++ ++ au_sphl_init(&sbinfo->si_aopen); ++ ++ sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ sbinfo->si_dirwh = AUFS_DIRWH_DEF; ++ ++ for (i = 0; i < AuPlink_NHASH; i++) ++ au_sphl_init(sbinfo->si_plink + i); ++ init_waitqueue_head(&sbinfo->si_plink_wq); ++ spin_lock_init(&sbinfo->si_plink_maint_lock); ++ ++ au_sphl_init(&sbinfo->si_files); ++ ++ /* leave other members for sysaufs and si_mnt. */ ++ sbinfo->si_sb = sb; ++ sb->s_fs_info = sbinfo; ++ si_pid_set(sb); ++ return 0; /* success */ ++ ++out_br: ++ kfree(sbinfo->si_branch); ++out_pidmap: ++ kfree(sbinfo->au_si_pid.bitmap); ++out_sbinfo: ++ kfree(sbinfo); ++out: ++ return err; ++} ++ ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) ++{ ++ int err, sz; ++ struct au_branch **brp; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*brp) * (sbinfo->si_bend + 1); ++ if (unlikely(!sz)) ++ sz = sizeof(*brp); ++ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); ++ if (brp) { ++ sbinfo->si_branch = brp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_sigen_inc(struct super_block *sb) ++{ ++ unsigned int gen; ++ struct inode *inode; ++ ++ SiMustWriteLock(sb); ++ ++ gen = ++au_sbi(sb)->si_generation; ++ au_update_digen(sb->s_root); ++ inode = d_inode(sb->s_root); ++ au_update_iigen(inode, /*half*/0); ++ inode->i_version++; ++ return gen; ++} ++ ++aufs_bindex_t au_new_br_id(struct super_block *sb) ++{ ++ aufs_bindex_t br_id; ++ int i; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ for (i = 0; i <= AUFS_BRANCH_MAX; i++) { ++ br_id = ++sbinfo->si_last_br_id; ++ AuDebugOn(br_id < 0); ++ if (br_id && au_br_index(sb, br_id) < 0) ++ return br_id; ++ } ++ ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* it is ok that new 'nwt' tasks are appended while we are sleeping */ ++int si_read_lock(struct super_block *sb, int flags) ++{ ++ int err; ++ ++ err = 0; ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ ++ si_noflush_read_lock(sb); ++ err = au_plink_maint(sb, flags); ++ if (unlikely(err)) ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++int si_write_lock(struct super_block *sb, int flags) ++{ ++ int err; ++ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ ++ si_noflush_write_lock(sb); ++ err = au_plink_maint(sb, flags); ++ if (unlikely(err)) ++ si_write_unlock(sb); ++ ++ return err; ++} ++ ++/* dentry and super_block lock. call at entry point */ ++int aufs_read_lock(struct dentry *dentry, int flags) ++{ ++ int err; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, flags); ++ if (unlikely(err)) ++ goto out; ++ ++ if (au_ftest_lock(flags, DW)) ++ di_write_lock_child(dentry); ++ else ++ di_read_lock_child(dentry, flags); ++ ++ if (au_ftest_lock(flags, GEN)) { ++ err = au_digen_test(dentry, au_sigen(sb)); ++ AuDebugOn(!err && au_dbrange_test(dentry)); ++ if (unlikely(err)) ++ aufs_read_unlock(dentry, flags); ++ } ++ ++out: ++ return err; ++} ++ ++void aufs_read_unlock(struct dentry *dentry, int flags) ++{ ++ if (au_ftest_lock(flags, DW)) ++ di_write_unlock(dentry); ++ else ++ di_read_unlock(dentry, flags); ++ si_read_unlock(dentry->d_sb); ++} ++ ++void aufs_write_lock(struct dentry *dentry) ++{ ++ si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); ++ di_write_lock_child(dentry); ++} ++ ++void aufs_write_unlock(struct dentry *dentry) ++{ ++ di_write_unlock(dentry); ++ si_write_unlock(dentry->d_sb); ++} ++ ++int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) ++{ ++ int err; ++ unsigned int sigen; ++ struct super_block *sb; ++ ++ sb = d1->d_sb; ++ err = si_read_lock(sb, flags); ++ if (unlikely(err)) ++ goto out; ++ ++ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); ++ ++ if (au_ftest_lock(flags, GEN)) { ++ sigen = au_sigen(sb); ++ err = au_digen_test(d1, sigen); ++ AuDebugOn(!err && au_dbrange_test(d1)); ++ if (!err) { ++ err = au_digen_test(d2, sigen); ++ AuDebugOn(!err && au_dbrange_test(d2)); ++ } ++ if (unlikely(err)) ++ aufs_read_and_write_unlock2(d1, d2); ++ } ++ ++out: ++ return err; ++} ++ ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock2(d1, d2); ++ si_read_unlock(d1->d_sb); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int si_pid_test_slow(struct super_block *sb) ++{ ++ void *p; ++ ++ rcu_read_lock(); ++ p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid); ++ rcu_read_unlock(); ++ ++ return (long)!!p; ++} ++ ++void si_pid_set_slow(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(si_pid_test_slow(sb)); ++ ++ sbinfo = au_sbi(sb); ++ err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); ++ AuDebugOn(err); ++ spin_lock(&sbinfo->au_si_pid.tree_lock); ++ err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid, ++ /*any valid ptr*/sb); ++ spin_unlock(&sbinfo->au_si_pid.tree_lock); ++ AuDebugOn(err); ++ radix_tree_preload_end(); ++} ++ ++void si_pid_clr_slow(struct super_block *sb) ++{ ++ void *p; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(!si_pid_test_slow(sb)); ++ ++ sbinfo = au_sbi(sb); ++ spin_lock(&sbinfo->au_si_pid.tree_lock); ++ p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid); ++ spin_unlock(&sbinfo->au_si_pid.tree_lock); ++} +diff --git a/fs/aufs/spl.h b/fs/aufs/spl.h +new file mode 100644 +index 0000000..a66d39e +--- /dev/null ++++ b/fs/aufs/spl.h +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * simple list protected by a spinlock ++ */ ++ ++#ifndef __AUFS_SPL_H__ ++#define __AUFS_SPL_H__ ++ ++#ifdef __KERNEL__ ++ ++struct au_splhead { ++ spinlock_t spin; ++ struct list_head head; ++}; ++ ++static inline void au_spl_init(struct au_splhead *spl) ++{ ++ spin_lock_init(&spl->spin); ++ INIT_LIST_HEAD(&spl->head); ++} ++ ++static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_add(list, &spl->head); ++ spin_unlock(&spl->spin); ++} ++ ++static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_del(list); ++ spin_unlock(&spl->spin); ++} ++ ++static inline void au_spl_del_rcu(struct list_head *list, ++ struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_del_rcu(list); ++ spin_unlock(&spl->spin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_sphlhead { ++ spinlock_t spin; ++ struct hlist_head head; ++}; ++ ++static inline void au_sphl_init(struct au_sphlhead *sphl) ++{ ++ spin_lock_init(&sphl->spin); ++ INIT_HLIST_HEAD(&sphl->head); ++} ++ ++static inline void au_sphl_add(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_add_head(hlist, &sphl->head); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del_rcu(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del_rcu(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) ++{ ++ unsigned long cnt; ++ struct hlist_node *pos; ++ ++ cnt = 0; ++ spin_lock(&sphl->spin); ++ hlist_for_each(pos, &sphl->head) ++ cnt++; ++ spin_unlock(&sphl->spin); ++ return cnt; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SPL_H__ */ +diff --git a/fs/aufs/super.c b/fs/aufs/super.c +new file mode 100644 +index 0000000..ee5780d +--- /dev/null ++++ b/fs/aufs/super.c +@@ -0,0 +1,991 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * mount and super_block operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * super_operations ++ */ ++static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) ++{ ++ struct au_icntnr *c; ++ ++ c = au_cache_alloc_icntnr(); ++ if (c) { ++ au_icntnr_init(c); ++ c->vfs_inode.i_version = 1; /* sigen(sb); */ ++ c->iinfo.ii_hinode = NULL; ++ return &c->vfs_inode; ++ } ++ return NULL; ++} ++ ++static void aufs_destroy_inode_cb(struct rcu_head *head) ++{ ++ struct inode *inode = container_of(head, struct inode, i_rcu); ++ ++ INIT_HLIST_HEAD(&inode->i_dentry); ++ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); ++} ++ ++static void aufs_destroy_inode(struct inode *inode) ++{ ++ au_iinfo_fin(inode); ++ call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); ++} ++ ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino) ++{ ++ struct inode *inode; ++ int err; ++ ++ inode = iget_locked(sb, ino); ++ if (unlikely(!inode)) { ++ inode = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ if (!(inode->i_state & I_NEW)) ++ goto out; ++ ++ err = au_xigen_new(inode); ++ if (!err) ++ err = au_iinfo_init(inode); ++ if (!err) ++ inode->i_version++; ++ else { ++ iget_failed(inode); ++ inode = ERR_PTR(err); ++ } ++ ++out: ++ /* never return NULL */ ++ AuDebugOn(!inode); ++ AuTraceErrPtr(inode); ++ return inode; ++} ++ ++/* lock free root dinfo */ ++static int au_show_brs(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct path path; ++ struct au_hdentry *hdp; ++ struct au_branch *br; ++ au_br_perm_str_t perm; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ hdp = au_di(sb->s_root)->di_hdentry; ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ path.mnt = au_br_mnt(br); ++ path.dentry = hdp[bindex].hd_dentry; ++ err = au_seq_path(seq, &path); ++ if (err > 0) { ++ au_optstr_br_perm(&perm, br->br_perm); ++ err = seq_printf(seq, "=%s", perm.a); ++ if (err == -1) ++ err = -E2BIG; ++ } ++ if (!err && bindex != bend) ++ err = seq_putc(seq, ':'); ++ } ++ ++ return err; ++} ++ ++static void au_show_wbr_create(struct seq_file *m, int v, ++ struct au_sbinfo *sbinfo) ++{ ++ const char *pat; ++ ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ ++ seq_puts(m, ",create="); ++ pat = au_optstr_wbr_create(v); ++ switch (v) { ++ case AuWbrCreate_TDP: ++ case AuWbrCreate_RR: ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_PMFS: ++ seq_puts(m, pat); ++ break; ++ case AuWbrCreate_MFSV: ++ seq_printf(m, /*pat*/"mfs:%lu", ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_PMFSV: ++ seq_printf(m, /*pat*/"pmfs:%lu", ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_MFSRR: ++ seq_printf(m, /*pat*/"mfsrr:%llu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ seq_printf(m, /*pat*/"mfsrr:%llu:%lu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark, ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_PMFSRR: ++ seq_printf(m, /*pat*/"pmfsrr:%llu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark); ++ break; ++ case AuWbrCreate_PMFSRRV: ++ seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark, ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ } ++} ++ ++static int au_show_xino(struct seq_file *seq, struct super_block *sb) ++{ ++#ifdef CONFIG_SYSFS ++ return 0; ++#else ++ int err; ++ const int len = sizeof(AUFS_XINO_FNAME) - 1; ++ aufs_bindex_t bindex, brid; ++ struct qstr *name; ++ struct file *f; ++ struct dentry *d, *h_root; ++ struct au_hdentry *hdp; ++ ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ ++ err = 0; ++ f = au_sbi(sb)->si_xib; ++ if (!f) ++ goto out; ++ ++ /* stop printing the default xino path on the first writable branch */ ++ h_root = NULL; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) { ++ bindex = au_br_index(sb, brid); ++ hdp = au_di(sb->s_root)->di_hdentry; ++ h_root = hdp[0 + bindex].hd_dentry; ++ } ++ d = f->f_path.dentry; ++ name = &d->d_name; ++ /* safe ->d_parent because the file is unlinked */ ++ if (d->d_parent == h_root ++ && name->len == len ++ && !memcmp(name->name, AUFS_XINO_FNAME, len)) ++ goto out; ++ ++ seq_puts(seq, ",xino="); ++ err = au_xino_path(seq, f); ++ ++out: ++ return err; ++#endif ++} ++ ++/* seq_file will re-call me in case of too long string */ ++static int aufs_show_options(struct seq_file *m, struct dentry *dentry) ++{ ++ int err; ++ unsigned int mnt_flags, v; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++#define AuBool(name, str) do { \ ++ v = au_opt_test(mnt_flags, name); \ ++ if (v != au_opt_test(AuOpt_Def, name)) \ ++ seq_printf(m, ",%s" #str, v ? "" : "no"); \ ++} while (0) ++ ++#define AuStr(name, str) do { \ ++ v = mnt_flags & AuOptMask_##name; \ ++ if (v != (AuOpt_Def & AuOptMask_##name)) \ ++ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ ++} while (0) ++ ++#define AuUInt(name, str, val) do { \ ++ if (val != AUFS_##name##_DEF) \ ++ seq_printf(m, "," #str "=%u", val); \ ++} while (0) ++ ++ sb = dentry->d_sb; ++ if (sb->s_flags & MS_POSIXACL) ++ seq_puts(m, ",acl"); ++ ++ /* lock free root dinfo */ ++ si_noflush_read_lock(sb); ++ sbinfo = au_sbi(sb); ++ seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); ++ ++ mnt_flags = au_mntflags(sb); ++ if (au_opt_test(mnt_flags, XINO)) { ++ err = au_show_xino(m, sb); ++ if (unlikely(err)) ++ goto out; ++ } else ++ seq_puts(m, ",noxino"); ++ ++ AuBool(TRUNC_XINO, trunc_xino); ++ AuStr(UDBA, udba); ++ AuBool(SHWH, shwh); ++ AuBool(PLINK, plink); ++ AuBool(DIO, dio); ++ AuBool(DIRPERM1, dirperm1); ++ ++ v = sbinfo->si_wbr_create; ++ if (v != AuWbrCreate_Def) ++ au_show_wbr_create(m, v, sbinfo); ++ ++ v = sbinfo->si_wbr_copyup; ++ if (v != AuWbrCopyup_Def) ++ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); ++ ++ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); ++ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) ++ seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); ++ ++ AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); ++ ++ v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; ++ AuUInt(RDCACHE, rdcache, v); ++ ++ AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); ++ AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); ++ ++ au_fhsm_show(m, sbinfo); ++ ++ AuBool(SUM, sum); ++ /* AuBool(SUM_W, wsum); */ ++ AuBool(WARN_PERM, warn_perm); ++ AuBool(VERBOSE, verbose); ++ ++out: ++ /* be sure to print "br:" last */ ++ if (!sysaufs_brs) { ++ seq_puts(m, ",br:"); ++ au_show_brs(m, sb); ++ } ++ si_read_unlock(sb); ++ return 0; ++ ++#undef AuBool ++#undef AuStr ++#undef AuUInt ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sum mode which returns the summation for statfs(2) */ ++ ++static u64 au_add_till_max(u64 a, u64 b) ++{ ++ u64 old; ++ ++ old = a; ++ a += b; ++ if (old <= a) ++ return a; ++ return ULLONG_MAX; ++} ++ ++static u64 au_mul_till_max(u64 a, long mul) ++{ ++ u64 old; ++ ++ old = a; ++ a *= mul; ++ if (old <= a) ++ return a; ++ return ULLONG_MAX; ++} ++ ++static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) ++{ ++ int err; ++ long bsize, factor; ++ u64 blocks, bfree, bavail, files, ffree; ++ aufs_bindex_t bend, bindex, i; ++ unsigned char shared; ++ struct path h_path; ++ struct super_block *h_sb; ++ ++ err = 0; ++ bsize = LONG_MAX; ++ files = 0; ++ ffree = 0; ++ blocks = 0; ++ bfree = 0; ++ bavail = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_path.mnt->mnt_sb; ++ shared = 0; ++ for (i = 0; !shared && i < bindex; i++) ++ shared = (au_sbr_sb(sb, i) == h_sb); ++ if (shared) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, buf); ++ if (unlikely(err)) ++ goto out; ++ ++ if (bsize > buf->f_bsize) { ++ /* ++ * we will reduce bsize, so we have to expand blocks ++ * etc. to match them again ++ */ ++ factor = (bsize / buf->f_bsize); ++ blocks = au_mul_till_max(blocks, factor); ++ bfree = au_mul_till_max(bfree, factor); ++ bavail = au_mul_till_max(bavail, factor); ++ bsize = buf->f_bsize; ++ } ++ ++ factor = (buf->f_bsize / bsize); ++ blocks = au_add_till_max(blocks, ++ au_mul_till_max(buf->f_blocks, factor)); ++ bfree = au_add_till_max(bfree, ++ au_mul_till_max(buf->f_bfree, factor)); ++ bavail = au_add_till_max(bavail, ++ au_mul_till_max(buf->f_bavail, factor)); ++ files = au_add_till_max(files, buf->f_files); ++ ffree = au_add_till_max(ffree, buf->f_ffree); ++ } ++ ++ buf->f_bsize = bsize; ++ buf->f_blocks = blocks; ++ buf->f_bfree = bfree; ++ buf->f_bavail = bavail; ++ buf->f_files = files; ++ buf->f_ffree = ffree; ++ buf->f_frsize = 0; ++ ++out: ++ return err; ++} ++ ++static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ int err; ++ struct path h_path; ++ struct super_block *sb; ++ ++ /* lock free root dinfo */ ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (!au_opt_test(au_mntflags(sb), SUM)) { ++ /* sb->s_root for NFS is unreliable */ ++ h_path.mnt = au_sbr_mnt(sb, 0); ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, buf); ++ } else ++ err = au_statfs_sum(sb, buf); ++ si_read_unlock(sb); ++ ++ if (!err) { ++ buf->f_type = AUFS_SUPER_MAGIC; ++ buf->f_namelen = AUFS_MAX_NAMELEN; ++ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); ++ } ++ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_sync_fs(struct super_block *sb, int wait) ++{ ++ int err, e; ++ aufs_bindex_t bend, bindex; ++ struct au_branch *br; ++ struct super_block *h_sb; ++ ++ err = 0; ++ si_noflush_read_lock(sb); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!au_br_writable(br->br_perm)) ++ continue; ++ ++ h_sb = au_sbr_sb(sb, bindex); ++ if (h_sb->s_op->sync_fs) { ++ e = h_sb->s_op->sync_fs(h_sb, wait); ++ if (unlikely(e && !err)) ++ err = e; ++ /* go on even if an error happens */ ++ } ++ } ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* final actions when unmounting a file system */ ++static void aufs_put_super(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ ++ dbgaufs_si_fin(sbinfo); ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_array_free(void *array) ++{ ++ if (array) { ++ if (!is_vmalloc_addr(array)) ++ kfree(array); ++ else ++ vfree(array); ++ } ++} ++ ++void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg) ++{ ++ void *array; ++ unsigned long long n, sz; ++ ++ array = NULL; ++ n = 0; ++ if (!*hint) ++ goto out; ++ ++ if (*hint > ULLONG_MAX / sizeof(array)) { ++ array = ERR_PTR(-EMFILE); ++ pr_err("hint %llu\n", *hint); ++ goto out; ++ } ++ ++ sz = sizeof(array) * *hint; ++ array = kzalloc(sz, GFP_NOFS); ++ if (unlikely(!array)) ++ array = vzalloc(sz); ++ if (unlikely(!array)) { ++ array = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ n = cb(array, *hint, arg); ++ AuDebugOn(n > *hint); ++ ++out: ++ *hint = n; ++ return array; ++} ++ ++static unsigned long long au_iarray_cb(void *a, ++ unsigned long long max __maybe_unused, ++ void *arg) ++{ ++ unsigned long long n; ++ struct inode **p, *inode; ++ struct list_head *head; ++ ++ n = 0; ++ p = a; ++ head = arg; ++ spin_lock(&inode_sb_list_lock); ++ list_for_each_entry(inode, head, i_sb_list) { ++ if (!is_bad_inode(inode) ++ && au_ii(inode)->ii_bstart >= 0) { ++ spin_lock(&inode->i_lock); ++ if (atomic_read(&inode->i_count)) { ++ au_igrab(inode); ++ *p++ = inode; ++ n++; ++ AuDebugOn(n > max); ++ } ++ spin_unlock(&inode->i_lock); ++ } ++ } ++ spin_unlock(&inode_sb_list_lock); ++ ++ return n; ++} ++ ++struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) ++{ ++ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); ++ return au_array_alloc(max, au_iarray_cb, &sb->s_inodes); ++} ++ ++void au_iarray_free(struct inode **a, unsigned long long max) ++{ ++ unsigned long long ull; ++ ++ for (ull = 0; ull < max; ull++) ++ iput(a[ull]); ++ au_array_free(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * refresh dentry and inode at remount time. ++ */ ++/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ ++static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, ++ struct dentry *parent) ++{ ++ int err; ++ ++ di_write_lock_child(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(dentry, parent); ++ if (!err && dir_flags) ++ au_hn_reset(d_inode(dentry), dir_flags); ++ di_read_unlock(parent, AuLock_IR); ++ di_write_unlock(dentry); ++ ++ return err; ++} ++ ++static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, ++ struct au_sbinfo *sbinfo, ++ const unsigned int dir_flags) ++{ ++ int err; ++ struct dentry *parent; ++ ++ err = 0; ++ parent = dget_parent(dentry); ++ if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { ++ if (d_really_is_positive(dentry)) { ++ if (!d_is_dir(dentry)) ++ err = au_do_refresh(dentry, /*dir_flags*/0, ++ parent); ++ else { ++ err = au_do_refresh(dentry, dir_flags, parent); ++ if (unlikely(err)) ++ au_fset_si(sbinfo, FAILED_REFRESH_DIR); ++ } ++ } else ++ err = au_do_refresh(dentry, /*dir_flags*/0, parent); ++ AuDbgDentry(dentry); ++ } ++ dput(parent); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_refresh_d(struct super_block *sb) ++{ ++ int err, i, j, ndentry, e; ++ unsigned int sigen; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries, *d; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root = sb->s_root; ++ const unsigned int dir_flags = au_hi_flags(d_inode(root), /*isdir*/1); ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, root, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ sigen = au_sigen(sb); ++ sbinfo = au_sbi(sb); ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ d = dentries[j]; ++ e = au_do_refresh_d(d, sigen, sbinfo, dir_flags); ++ if (unlikely(e && !err)) ++ err = e; ++ /* go on even err */ ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int au_refresh_i(struct super_block *sb) ++{ ++ int err, e; ++ unsigned int sigen; ++ unsigned long long max, ull; ++ struct inode *inode, **array; ++ ++ array = au_iarray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ sigen = au_sigen(sb); ++ for (ull = 0; ull < max; ull++) { ++ inode = array[ull]; ++ if (unlikely(!inode)) ++ break; ++ if (au_iigen(inode, NULL) != sigen) { ++ ii_write_lock_child(inode); ++ e = au_refresh_hinode_self(inode); ++ ii_write_unlock(inode); ++ if (unlikely(e)) { ++ pr_err("error %d, i%lu\n", e, inode->i_ino); ++ if (!err) ++ err = e; ++ /* go on even if err */ ++ } ++ } ++ } ++ ++ au_iarray_free(array, max); ++ ++out: ++ return err; ++} ++ ++static void au_remount_refresh(struct super_block *sb) ++{ ++ int err, e; ++ unsigned int udba; ++ aufs_bindex_t bindex, bend; ++ struct dentry *root; ++ struct inode *inode; ++ struct au_branch *br; ++ ++ au_sigen_inc(sb); ++ au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR); ++ ++ root = sb->s_root; ++ DiMustNoWaiters(root); ++ inode = d_inode(root); ++ IiMustNoWaiters(inode); ++ ++ udba = au_opt_udba(sb); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_hnotify_reset_br(udba, br, br->br_perm); ++ if (unlikely(err)) ++ AuIOErr("hnotify failed on br %d, %d, ignored\n", ++ bindex, err); ++ /* go on even if err */ ++ } ++ au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); ++ ++ di_write_unlock(root); ++ err = au_refresh_d(sb); ++ e = au_refresh_i(sb); ++ if (unlikely(e && !err)) ++ err = e; ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ ++ au_cpup_attr_all(inode, /*force*/1); ++ ++ if (unlikely(err)) ++ AuIOErr("refresh failed, ignored, %d\n", err); ++} ++ ++/* stop extra interpretation of errno in mount(8), and strange error messages */ ++static int cvt_err(int err) ++{ ++ AuTraceErr(err); ++ ++ switch (err) { ++ case -ENOENT: ++ case -ENOTDIR: ++ case -EEXIST: ++ case -EIO: ++ err = -EINVAL; ++ } ++ return err; ++} ++ ++static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int err, do_dx; ++ unsigned int mntflags; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ root = sb->s_root; ++ if (!data || !*data) { ++ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (!err) { ++ di_write_lock_child(root); ++ err = au_opts_verify(sb, *flags, /*pending*/0); ++ aufs_write_unlock(root); ++ } ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.flags = AuOpts_REMOUNT; ++ opts.sb_flags = *flags; ++ ++ /* parse it before aufs lock */ ++ err = au_opts_parse(sb, data, &opts); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ sbinfo = au_sbi(sb); ++ inode = d_inode(root); ++ mutex_lock(&inode->i_mutex); ++ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_mtx; ++ di_write_lock_child(root); ++ ++ /* au_opts_remount() may return an error */ ++ err = au_opts_remount(sb, &opts); ++ au_opts_free(&opts); ++ ++ if (au_ftest_opts(opts.flags, REFRESH)) ++ au_remount_refresh(sb); ++ ++ if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { ++ mntflags = au_mntflags(sb); ++ do_dx = !!au_opt_test(mntflags, DIO); ++ au_dy_arefresh(do_dx); ++ } ++ ++ au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ ++ aufs_write_unlock(root); ++ ++out_mtx: ++ mutex_unlock(&inode->i_mutex); ++out_opts: ++ free_page((unsigned long)opts.opt); ++out: ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++static const struct super_operations aufs_sop = { ++ .alloc_inode = aufs_alloc_inode, ++ .destroy_inode = aufs_destroy_inode, ++ /* always deleting, no clearing */ ++ .drop_inode = generic_delete_inode, ++ .show_options = aufs_show_options, ++ .statfs = aufs_statfs, ++ .put_super = aufs_put_super, ++ .sync_fs = aufs_sync_fs, ++ .remount_fs = aufs_remount_fs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int alloc_root(struct super_block *sb) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *root; ++ ++ err = -ENOMEM; ++ inode = au_iget_locked(sb, AUFS_ROOT_INO); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ inode->i_mode = S_IFDIR; ++ set_nlink(inode, 2); ++ unlock_new_inode(inode); ++ ++ root = d_make_root(inode); ++ if (unlikely(!root)) ++ goto out; ++ err = PTR_ERR(root); ++ if (IS_ERR(root)) ++ goto out; ++ ++ err = au_di_init(root); ++ if (!err) { ++ sb->s_root = root; ++ return 0; /* success */ ++ } ++ dput(root); ++ ++out: ++ return err; ++} ++ ++static int aufs_fill_super(struct super_block *sb, void *raw_data, ++ int silent __maybe_unused) ++{ ++ int err; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ char *arg = raw_data; ++ ++ if (unlikely(!arg || !*arg)) { ++ err = -EINVAL; ++ pr_err("no arg\n"); ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.sb_flags = sb->s_flags; ++ ++ err = au_si_alloc(sb); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ /* all timestamps always follow the ones on the branch */ ++ sb->s_flags |= MS_NOATIME | MS_NODIRATIME; ++ sb->s_op = &aufs_sop; ++ sb->s_d_op = &aufs_dop; ++ sb->s_magic = AUFS_SUPER_MAGIC; ++ sb->s_maxbytes = 0; ++ sb->s_stack_depth = 1; ++ au_export_init(sb); ++ /* au_xattr_init(sb); */ ++ ++ err = alloc_root(sb); ++ if (unlikely(err)) { ++ si_write_unlock(sb); ++ goto out_info; ++ } ++ root = sb->s_root; ++ inode = d_inode(root); ++ ++ /* ++ * actually we can parse options regardless aufs lock here. ++ * but at remount time, parsing must be done before aufs lock. ++ * so we follow the same rule. ++ */ ++ ii_write_lock_parent(inode); ++ aufs_write_unlock(root); ++ err = au_opts_parse(sb, arg, &opts); ++ if (unlikely(err)) ++ goto out_root; ++ ++ /* lock vfs_inode first, then aufs. */ ++ mutex_lock(&inode->i_mutex); ++ aufs_write_lock(root); ++ err = au_opts_mount(sb, &opts); ++ au_opts_free(&opts); ++ aufs_write_unlock(root); ++ mutex_unlock(&inode->i_mutex); ++ if (!err) ++ goto out_opts; /* success */ ++ ++out_root: ++ dput(root); ++ sb->s_root = NULL; ++out_info: ++ dbgaufs_si_fin(au_sbi(sb)); ++ kobject_put(&au_sbi(sb)->si_kobj); ++ sb->s_fs_info = NULL; ++out_opts: ++ free_page((unsigned long)opts.opt); ++out: ++ AuTraceErr(err); ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name __maybe_unused, ++ void *raw_data) ++{ ++ struct dentry *root; ++ struct super_block *sb; ++ ++ /* all timestamps always follow the ones on the branch */ ++ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ ++ root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super); ++ if (IS_ERR(root)) ++ goto out; ++ ++ sb = root->d_sb; ++ si_write_lock(sb, !AuLock_FLUSH); ++ sysaufs_brs_add(sb, 0); ++ si_write_unlock(sb); ++ au_sbilist_add(sb); ++ ++out: ++ return root; ++} ++ ++static void aufs_kill_sb(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (sbinfo) { ++ au_sbilist_del(sb); ++ aufs_write_lock(sb->s_root); ++ au_fhsm_fin(sb); ++ if (sbinfo->si_wbr_create_ops->fin) ++ sbinfo->si_wbr_create_ops->fin(sb); ++ if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); ++ au_remount_refresh(sb); ++ } ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_put(sb, /*verbose*/1); ++ au_xino_clr(sb); ++ sbinfo->si_sb = NULL; ++ aufs_write_unlock(sb->s_root); ++ au_nwt_flush(&sbinfo->si_nowait); ++ } ++ kill_anon_super(sb); ++} ++ ++struct file_system_type aufs_fs_type = { ++ .name = AUFS_FSTYPE, ++ /* a race between rename and others */ ++ .fs_flags = FS_RENAME_DOES_D_MOVE, ++ .mount = aufs_mount, ++ .kill_sb = aufs_kill_sb, ++ /* no need to __module_get() and module_put(). */ ++ .owner = THIS_MODULE, ++}; +diff --git a/fs/aufs/super.h b/fs/aufs/super.h +new file mode 100644 +index 0000000..f37d89d +--- /dev/null ++++ b/fs/aufs/super.h +@@ -0,0 +1,622 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * super_block operations ++ */ ++ ++#ifndef __AUFS_SUPER_H__ ++#define __AUFS_SUPER_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "rwsem.h" ++#include "spl.h" ++#include "wkq.h" ++ ++/* policies to select one among multiple writable branches */ ++struct au_wbr_copyup_operations { ++ int (*copyup)(struct dentry *dentry); ++}; ++ ++#define AuWbr_DIR 1 /* target is a dir */ ++#define AuWbr_PARENT (1 << 1) /* always require a parent */ ++ ++#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) ++#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } ++#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } ++ ++struct au_wbr_create_operations { ++ int (*create)(struct dentry *dentry, unsigned int flags); ++ int (*init)(struct super_block *sb); ++ int (*fin)(struct super_block *sb); ++}; ++ ++struct au_wbr_mfs { ++ struct mutex mfs_lock; /* protect this structure */ ++ unsigned long mfs_jiffy; ++ unsigned long mfs_expire; ++ aufs_bindex_t mfs_bindex; ++ ++ unsigned long long mfsrr_bytes; ++ unsigned long long mfsrr_watermark; ++}; ++ ++struct pseudo_link { ++ union { ++ struct hlist_node hlist; ++ struct rcu_head rcu; ++ }; ++ struct inode *inode; ++}; ++ ++#define AuPlink_NHASH 100 ++static inline int au_plink_hash(ino_t ino) ++{ ++ return ino % AuPlink_NHASH; ++} ++ ++/* File-based Hierarchical Storage Management */ ++struct au_fhsm { ++#ifdef CONFIG_AUFS_FHSM ++ /* allow only one process who can receive the notification */ ++ spinlock_t fhsm_spin; ++ pid_t fhsm_pid; ++ wait_queue_head_t fhsm_wqh; ++ atomic_t fhsm_readable; ++ ++ /* these are protected by si_rwsem */ ++ unsigned long fhsm_expire; ++ aufs_bindex_t fhsm_bottom; ++#endif ++}; ++ ++struct au_branch; ++struct au_sbinfo { ++ /* nowait tasks in the system-wide workqueue */ ++ struct au_nowait_tasks si_nowait; ++ ++ /* ++ * tried sb->s_umount, but failed due to the dependecy between i_mutex. ++ * rwsem for au_sbinfo is necessary. ++ */ ++ struct au_rwsem si_rwsem; ++ ++ /* prevent recursive locking in deleting inode */ ++ struct { ++ unsigned long *bitmap; ++ spinlock_t tree_lock; ++ struct radix_tree_root tree; ++ } au_si_pid; ++ ++ /* ++ * dirty approach to protect sb->sb_inodes and ->s_files (gone) from ++ * remount. ++ */ ++ atomic_long_t si_ninodes, si_nfiles; ++ ++ /* branch management */ ++ unsigned int si_generation; ++ ++ /* see AuSi_ flags */ ++ unsigned char au_si_status; ++ ++ aufs_bindex_t si_bend; ++ ++ /* dirty trick to keep br_id plus */ ++ unsigned int si_last_br_id : ++ sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; ++ struct au_branch **si_branch; ++ ++ /* policy to select a writable branch */ ++ unsigned char si_wbr_copyup; ++ unsigned char si_wbr_create; ++ struct au_wbr_copyup_operations *si_wbr_copyup_ops; ++ struct au_wbr_create_operations *si_wbr_create_ops; ++ ++ /* round robin */ ++ atomic_t si_wbr_rr_next; ++ ++ /* most free space */ ++ struct au_wbr_mfs si_wbr_mfs; ++ ++ /* File-based Hierarchical Storage Management */ ++ struct au_fhsm si_fhsm; ++ ++ /* mount flags */ ++ /* include/asm-ia64/siginfo.h defines a macro named si_flags */ ++ unsigned int si_mntflags; ++ ++ /* external inode number (bitmap and translation table) */ ++ vfs_readf_t si_xread; ++ vfs_writef_t si_xwrite; ++ struct file *si_xib; ++ struct mutex si_xib_mtx; /* protect xib members */ ++ unsigned long *si_xib_buf; ++ unsigned long si_xib_last_pindex; ++ int si_xib_next_bit; ++ aufs_bindex_t si_xino_brid; ++ unsigned long si_xino_jiffy; ++ unsigned long si_xino_expire; ++ /* reserved for future use */ ++ /* unsigned long long si_xib_limit; */ /* Max xib file size */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++ /* i_generation */ ++ struct file *si_xigen; ++ atomic_t si_xigen_next; ++#endif ++ ++ /* dirty trick to suppoer atomic_open */ ++ struct au_sphlhead si_aopen; ++ ++ /* vdir parameters */ ++ unsigned long si_rdcache; /* max cache time in jiffies */ ++ unsigned int si_rdblk; /* deblk size */ ++ unsigned int si_rdhash; /* hash size */ ++ ++ /* ++ * If the number of whiteouts are larger than si_dirwh, leave all of ++ * them after au_whtmp_ren to reduce the cost of rmdir(2). ++ * future fsck.aufs or kernel thread will remove them later. ++ * Otherwise, remove all whiteouts and the dir in rmdir(2). ++ */ ++ unsigned int si_dirwh; ++ ++ /* pseudo_link list */ ++ struct au_sphlhead si_plink[AuPlink_NHASH]; ++ wait_queue_head_t si_plink_wq; ++ spinlock_t si_plink_maint_lock; ++ pid_t si_plink_maint_pid; ++ ++ /* file list */ ++ struct au_sphlhead si_files; ++ ++ /* ++ * sysfs and lifetime management. ++ * this is not a small structure and it may be a waste of memory in case ++ * of sysfs is disabled, particulary when many aufs-es are mounted. ++ * but using sysfs is majority. ++ */ ++ struct kobject si_kobj; ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *si_dbgaufs; ++ struct dentry *si_dbgaufs_plink; ++ struct dentry *si_dbgaufs_xib; ++#ifdef CONFIG_AUFS_EXPORT ++ struct dentry *si_dbgaufs_xigen; ++#endif ++#endif ++ ++#ifdef CONFIG_AUFS_SBILIST ++ struct list_head si_list; ++#endif ++ ++ /* dirty, necessary for unmounting, sysfs and sysrq */ ++ struct super_block *si_sb; ++}; ++ ++/* sbinfo status flags */ ++/* ++ * set true when refresh_dirs() failed at remount time. ++ * then try refreshing dirs at access time again. ++ * if it is false, refreshing dirs at access time is unnecesary ++ */ ++#define AuSi_FAILED_REFRESH_DIR 1 ++ ++#define AuSi_FHSM (1 << 1) /* fhsm is active now */ ++ ++#ifndef CONFIG_AUFS_FHSM ++#undef AuSi_FHSM ++#define AuSi_FHSM 0 ++#endif ++ ++static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, ++ unsigned int flag) ++{ ++ AuRwMustAnyLock(&sbi->si_rwsem); ++ return sbi->au_si_status & flag; ++} ++#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) ++#define au_fset_si(sbinfo, name) do { \ ++ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ ++ (sbinfo)->au_si_status |= AuSi_##name; \ ++} while (0) ++#define au_fclr_si(sbinfo, name) do { \ ++ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ ++ (sbinfo)->au_si_status &= ~AuSi_##name; \ ++} while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policy to select one among writable branches */ ++#define AuWbrCopyup(sbinfo, ...) \ ++ ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) ++#define AuWbrCreate(sbinfo, ...) \ ++ ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) ++ ++/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ ++#define AuLock_DW 1 /* write-lock dentry */ ++#define AuLock_IR (1 << 1) /* read-lock inode */ ++#define AuLock_IW (1 << 2) /* write-lock inode */ ++#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ ++#define AuLock_DIR (1 << 4) /* target is a dir */ ++#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ ++#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ ++#define AuLock_GEN (1 << 7) /* test digen/iigen */ ++#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) ++#define au_fset_lock(flags, name) \ ++ do { (flags) |= AuLock_##name; } while (0) ++#define au_fclr_lock(flags, name) \ ++ do { (flags) &= ~AuLock_##name; } while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* super.c */ ++extern struct file_system_type aufs_fs_type; ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino); ++typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max, ++ void *arg); ++void au_array_free(void *array); ++void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg); ++struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); ++void au_iarray_free(struct inode **a, unsigned long long max); ++ ++/* sbinfo.c */ ++void au_si_free(struct kobject *kobj); ++int au_si_alloc(struct super_block *sb); ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); ++ ++unsigned int au_sigen_inc(struct super_block *sb); ++aufs_bindex_t au_new_br_id(struct super_block *sb); ++ ++int si_read_lock(struct super_block *sb, int flags); ++int si_write_lock(struct super_block *sb, int flags); ++int aufs_read_lock(struct dentry *dentry, int flags); ++void aufs_read_unlock(struct dentry *dentry, int flags); ++void aufs_write_lock(struct dentry *dentry); ++void aufs_write_unlock(struct dentry *dentry); ++int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++int si_pid_test_slow(struct super_block *sb); ++void si_pid_set_slow(struct super_block *sb); ++void si_pid_clr_slow(struct super_block *sb); ++ ++/* wbr_policy.c */ ++extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; ++extern struct au_wbr_create_operations au_wbr_create_ops[]; ++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); ++ ++/* mvdown.c */ ++int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); ++ ++#ifdef CONFIG_AUFS_FHSM ++/* fhsm.c */ ++ ++static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) ++{ ++ pid_t pid; ++ ++ spin_lock(&fhsm->fhsm_spin); ++ pid = fhsm->fhsm_pid; ++ spin_unlock(&fhsm->fhsm_spin); ++ ++ return pid; ++} ++ ++void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); ++void au_fhsm_wrote_all(struct super_block *sb, int force); ++int au_fhsm_fd(struct super_block *sb, int oflags); ++int au_fhsm_br_alloc(struct au_branch *br); ++void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); ++void au_fhsm_fin(struct super_block *sb); ++void au_fhsm_init(struct au_sbinfo *sbinfo); ++void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); ++void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); ++#else ++AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, ++ int force) ++AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) ++AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) ++AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) ++AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) ++AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(au_fhsm_fin, struct super_block *sb) ++AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) ++AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) ++AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_sbinfo *au_sbi(struct super_block *sb) ++{ ++ return sb->s_fs_info; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++int au_test_nfsd(void); ++void au_export_init(struct super_block *sb); ++void au_xigen_inc(struct inode *inode); ++int au_xigen_new(struct inode *inode); ++int au_xigen_set(struct super_block *sb, struct file *base); ++void au_xigen_clr(struct super_block *sb); ++ ++static inline int au_busy_or_stale(void) ++{ ++ if (!au_test_nfsd()) ++ return -EBUSY; ++ return -ESTALE; ++} ++#else ++AuStubInt0(au_test_nfsd, void) ++AuStubVoid(au_export_init, struct super_block *sb) ++AuStubVoid(au_xigen_inc, struct inode *inode) ++AuStubInt0(au_xigen_new, struct inode *inode) ++AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) ++AuStubVoid(au_xigen_clr, struct super_block *sb) ++AuStub(int, au_busy_or_stale, return -EBUSY, void) ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_SBILIST ++/* module.c */ ++extern struct au_splhead au_sbilist; ++ ++static inline void au_sbilist_init(void) ++{ ++ au_spl_init(&au_sbilist); ++} ++ ++static inline void au_sbilist_add(struct super_block *sb) ++{ ++ au_spl_add(&au_sbi(sb)->si_list, &au_sbilist); ++} ++ ++static inline void au_sbilist_del(struct super_block *sb) ++{ ++ au_spl_del(&au_sbi(sb)->si_list, &au_sbilist); ++} ++ ++#ifdef CONFIG_AUFS_MAGIC_SYSRQ ++static inline void au_sbilist_lock(void) ++{ ++ spin_lock(&au_sbilist.spin); ++} ++ ++static inline void au_sbilist_unlock(void) ++{ ++ spin_unlock(&au_sbilist.spin); ++} ++#define AuGFP_SBILIST GFP_ATOMIC ++#else ++AuStubVoid(au_sbilist_lock, void) ++AuStubVoid(au_sbilist_unlock, void) ++#define AuGFP_SBILIST GFP_NOFS ++#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ ++#else ++AuStubVoid(au_sbilist_init, void) ++AuStubVoid(au_sbilist_add, struct super_block *sb) ++AuStubVoid(au_sbilist_del, struct super_block *sb) ++AuStubVoid(au_sbilist_lock, void) ++AuStubVoid(au_sbilist_unlock, void) ++#define AuGFP_SBILIST GFP_NOFS ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) ++{ ++ /* ++ * This function is a dynamic '__init' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++#ifdef CONFIG_DEBUG_FS ++ sbinfo->si_dbgaufs = NULL; ++ sbinfo->si_dbgaufs_plink = NULL; ++ sbinfo->si_dbgaufs_xib = NULL; ++#ifdef CONFIG_AUFS_EXPORT ++ sbinfo->si_dbgaufs_xigen = NULL; ++#endif ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline pid_t si_pid_bit(void) ++{ ++ /* the origin of pid is 1, but the bitmap's is 0 */ ++ return current->pid - 1; ++} ++ ++static inline int si_pid_test(struct super_block *sb) ++{ ++ pid_t bit; ++ ++ bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) ++ return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ return si_pid_test_slow(sb); ++} ++ ++static inline void si_pid_set(struct super_block *sb) ++{ ++ pid_t bit; ++ ++ bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) { ++ AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); ++ set_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ /* smp_mb(); */ ++ } else ++ si_pid_set_slow(sb); ++} ++ ++static inline void si_pid_clr(struct super_block *sb) ++{ ++ pid_t bit; ++ ++ bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) { ++ AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); ++ clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ /* smp_mb(); */ ++ } else ++ si_pid_clr_slow(sb); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock superblock. mainly for entry point functions */ ++/* ++ * __si_read_lock, __si_write_lock, ++ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); ++ ++#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) ++#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) ++#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) ++ ++static inline void si_noflush_read_lock(struct super_block *sb) ++{ ++ __si_read_lock(sb); ++ si_pid_set(sb); ++} ++ ++static inline int si_noflush_read_trylock(struct super_block *sb) ++{ ++ int locked; ++ ++ locked = __si_read_trylock(sb); ++ if (locked) ++ si_pid_set(sb); ++ return locked; ++} ++ ++static inline void si_noflush_write_lock(struct super_block *sb) ++{ ++ __si_write_lock(sb); ++ si_pid_set(sb); ++} ++ ++static inline int si_noflush_write_trylock(struct super_block *sb) ++{ ++ int locked; ++ ++ locked = __si_write_trylock(sb); ++ if (locked) ++ si_pid_set(sb); ++ return locked; ++} ++ ++#if 0 /* reserved */ ++static inline int si_read_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_read_trylock(sb); ++} ++#endif ++ ++static inline void si_read_unlock(struct super_block *sb) ++{ ++ si_pid_clr(sb); ++ __si_read_unlock(sb); ++} ++ ++#if 0 /* reserved */ ++static inline int si_write_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_write_trylock(sb); ++} ++#endif ++ ++static inline void si_write_unlock(struct super_block *sb) ++{ ++ si_pid_clr(sb); ++ __si_write_unlock(sb); ++} ++ ++#if 0 /* reserved */ ++static inline void si_downgrade_lock(struct super_block *sb) ++{ ++ __si_downgrade_lock(sb); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_sbend(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_bend; ++} ++ ++static inline unsigned int au_mntflags(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_mntflags; ++} ++ ++static inline unsigned int au_sigen(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_generation; ++} ++ ++static inline void au_ninodes_inc(struct super_block *sb) ++{ ++ atomic_long_inc(&au_sbi(sb)->si_ninodes); ++} ++ ++static inline void au_ninodes_dec(struct super_block *sb) ++{ ++ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); ++ atomic_long_dec(&au_sbi(sb)->si_ninodes); ++} ++ ++static inline void au_nfiles_inc(struct super_block *sb) ++{ ++ atomic_long_inc(&au_sbi(sb)->si_nfiles); ++} ++ ++static inline void au_nfiles_dec(struct super_block *sb) ++{ ++ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); ++ atomic_long_dec(&au_sbi(sb)->si_nfiles); ++} ++ ++static inline struct au_branch *au_sbr(struct super_block *sb, ++ aufs_bindex_t bindex) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_branch[0 + bindex]; ++} ++ ++static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) ++{ ++ SiMustWriteLock(sb); ++ au_sbi(sb)->si_xino_brid = brid; ++} ++ ++static inline aufs_bindex_t au_xino_brid(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_xino_brid; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SUPER_H__ */ +diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c +new file mode 100644 +index 0000000..29ac6fa +--- /dev/null ++++ b/fs/aufs/sysaufs.c +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sysfs interface and lifetime management ++ * they are necessary regardless sysfs is disabled. ++ */ ++ ++#include ++#include "aufs.h" ++ ++unsigned long sysaufs_si_mask; ++struct kset *sysaufs_kset; ++ ++#define AuSiAttr(_name) { \ ++ .attr = { .name = __stringify(_name), .mode = 0444 }, \ ++ .show = sysaufs_si_##_name, \ ++} ++ ++static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); ++struct attribute *sysaufs_si_attrs[] = { ++ &sysaufs_si_attr_xi_path.attr, ++ NULL, ++}; ++ ++static const struct sysfs_ops au_sbi_ops = { ++ .show = sysaufs_si_show ++}; ++ ++static struct kobj_type au_sbi_ktype = { ++ .release = au_si_free, ++ .sysfs_ops = &au_sbi_ops, ++ .default_attrs = sysaufs_si_attrs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ sbinfo->si_kobj.kset = sysaufs_kset; ++ /* cf. sysaufs_name() */ ++ err = kobject_init_and_add ++ (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, ++ SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); ++ ++ dbgaufs_si_null(sbinfo); ++ if (!err) { ++ err = dbgaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ kobject_put(&sbinfo->si_kobj); ++ } ++ return err; ++} ++ ++void sysaufs_fin(void) ++{ ++ dbgaufs_fin(); ++ sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); ++ kset_unregister(sysaufs_kset); ++} ++ ++int __init sysaufs_init(void) ++{ ++ int err; ++ ++ do { ++ get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); ++ } while (!sysaufs_si_mask); ++ ++ err = -EINVAL; ++ sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); ++ if (unlikely(!sysaufs_kset)) ++ goto out; ++ err = PTR_ERR(sysaufs_kset); ++ if (IS_ERR(sysaufs_kset)) ++ goto out; ++ err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); ++ if (unlikely(err)) { ++ kset_unregister(sysaufs_kset); ++ goto out; ++ } ++ ++ err = dbgaufs_init(); ++ if (unlikely(err)) ++ sysaufs_fin(); ++out: ++ return err; ++} +diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h +new file mode 100644 +index 0000000..feb78fd +--- /dev/null ++++ b/fs/aufs/sysaufs.h +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sysfs interface and mount lifetime management ++ */ ++ ++#ifndef __SYSAUFS_H__ ++#define __SYSAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "module.h" ++ ++struct super_block; ++struct au_sbinfo; ++ ++struct sysaufs_si_attr { ++ struct attribute attr; ++ int (*show)(struct seq_file *seq, struct super_block *sb); ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sysaufs.c */ ++extern unsigned long sysaufs_si_mask; ++extern struct kset *sysaufs_kset; ++extern struct attribute *sysaufs_si_attrs[]; ++int sysaufs_si_init(struct au_sbinfo *sbinfo); ++int __init sysaufs_init(void); ++void sysaufs_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* some people doesn't like to show a pointer in kernel */ ++static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) ++{ ++ return sysaufs_si_mask ^ (unsigned long)sbinfo; ++} ++ ++#define SysaufsSiNamePrefix "si_" ++#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) ++static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) ++{ ++ snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", ++ sysaufs_si_id(sbinfo)); ++} ++ ++struct au_branch; ++#ifdef CONFIG_SYSFS ++/* sysfs.c */ ++extern struct attribute_group *sysaufs_attr_group; ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf); ++long au_brinfo_ioctl(struct file *file, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); ++#endif ++ ++void sysaufs_br_init(struct au_branch *br); ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++ ++#define sysaufs_brs_init() do {} while (0) ++ ++#else ++#define sysaufs_attr_group NULL ++ ++AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) ++AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, ++ struct attribute *attr, char *buf) ++AuStubVoid(sysaufs_br_init, struct au_branch *br) ++AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) ++ ++static inline void sysaufs_brs_init(void) ++{ ++ sysaufs_brs = 0; ++} ++ ++#endif /* CONFIG_SYSFS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __SYSAUFS_H__ */ +diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c +new file mode 100644 +index 0000000..75cc82a +--- /dev/null ++++ b/fs/aufs/sysfs.c +@@ -0,0 +1,336 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sysfs interface ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++static struct attribute *au_attr[] = { ++ NULL, /* need to NULL terminate the list of attributes */ ++}; ++ ++static struct attribute_group sysaufs_attr_group_body = { ++ .attrs = au_attr ++}; ++ ++struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ ++ SiMustAnyLock(sb); ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ err = au_xino_path(seq, au_sbi(sb)->si_xib); ++ seq_putc(seq, '\n'); ++ } ++ return err; ++} ++ ++/* ++ * the lifetime of branch is independent from the entry under sysfs. ++ * sysfs handles the lifetime of the entry, and never call ->show() after it is ++ * unlinked. ++ */ ++static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, ++ aufs_bindex_t bindex, int idx) ++{ ++ int err; ++ struct path path; ++ struct dentry *root; ++ struct au_branch *br; ++ au_br_perm_str_t perm; ++ ++ AuDbg("b%d\n", bindex); ++ ++ err = 0; ++ root = sb->s_root; ++ di_read_lock_parent(root, !AuLock_IR); ++ br = au_sbr(sb, bindex); ++ ++ switch (idx) { ++ case AuBrSysfs_BR: ++ path.mnt = au_br_mnt(br); ++ path.dentry = au_h_dptr(root, bindex); ++ au_seq_path(seq, &path); ++ au_optstr_br_perm(&perm, br->br_perm); ++ err = seq_printf(seq, "=%s\n", perm.a); ++ break; ++ case AuBrSysfs_BRID: ++ err = seq_printf(seq, "%d\n", br->br_id); ++ break; ++ } ++ di_read_unlock(root, !AuLock_IR); ++ if (err == -1) ++ err = -E2BIG; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct seq_file *au_seq(char *p, ssize_t len) ++{ ++ struct seq_file *seq; ++ ++ seq = kzalloc(sizeof(*seq), GFP_NOFS); ++ if (seq) { ++ /* mutex_init(&seq.lock); */ ++ seq->buf = p; ++ seq->size = len; ++ return seq; /* success */ ++ } ++ ++ seq = ERR_PTR(-ENOMEM); ++ return seq; ++} ++ ++#define SysaufsBr_PREFIX "br" ++#define SysaufsBrid_PREFIX "brid" ++ ++/* todo: file size may exceed PAGE_SIZE */ ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ ssize_t err; ++ int idx; ++ long l; ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct seq_file *seq; ++ char *name; ++ struct attribute **cattr; ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ sb = sbinfo->si_sb; ++ ++ /* ++ * prevent a race condition between sysfs and aufs. ++ * for instance, sysfs_file_read() calls sysfs_get_active_two() which ++ * prohibits maintaining the sysfs entries. ++ * hew we acquire read lock after sysfs_get_active_two(). ++ * on the other hand, the remount process may maintain the sysfs/aufs ++ * entries after acquiring write lock. ++ * it can cause a deadlock. ++ * simply we gave up processing read here. ++ */ ++ err = -EBUSY; ++ if (unlikely(!si_noflush_read_trylock(sb))) ++ goto out; ++ ++ seq = au_seq(buf, PAGE_SIZE); ++ err = PTR_ERR(seq); ++ if (IS_ERR(seq)) ++ goto out_unlock; ++ ++ name = (void *)attr->name; ++ cattr = sysaufs_si_attrs; ++ while (*cattr) { ++ if (!strcmp(name, (*cattr)->name)) { ++ err = container_of(*cattr, struct sysaufs_si_attr, attr) ++ ->show(seq, sb); ++ goto out_seq; ++ } ++ cattr++; ++ } ++ ++ if (!strncmp(name, SysaufsBrid_PREFIX, ++ sizeof(SysaufsBrid_PREFIX) - 1)) { ++ idx = AuBrSysfs_BRID; ++ name += sizeof(SysaufsBrid_PREFIX) - 1; ++ } else if (!strncmp(name, SysaufsBr_PREFIX, ++ sizeof(SysaufsBr_PREFIX) - 1)) { ++ idx = AuBrSysfs_BR; ++ name += sizeof(SysaufsBr_PREFIX) - 1; ++ } else ++ BUG(); ++ ++ err = kstrtol(name, 10, &l); ++ if (!err) { ++ bend = au_sbend(sb); ++ if (l <= bend) ++ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); ++ else ++ err = -ENOENT; ++ } ++ ++out_seq: ++ if (!err) { ++ err = seq->count; ++ /* sysfs limit */ ++ if (unlikely(err == PAGE_SIZE)) ++ err = -EFBIG; ++ } ++ kfree(seq); ++out_unlock: ++ si_read_unlock(sb); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) ++{ ++ int err; ++ int16_t brid; ++ aufs_bindex_t bindex, bend; ++ size_t sz; ++ char *buf; ++ struct seq_file *seq; ++ struct au_branch *br; ++ ++ si_read_lock(sb, AuLock_FLUSH); ++ bend = au_sbend(sb); ++ err = bend + 1; ++ if (!arg) ++ goto out; ++ ++ err = -ENOMEM; ++ buf = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf)) ++ goto out; ++ ++ seq = au_seq(buf, PAGE_SIZE); ++ err = PTR_ERR(seq); ++ if (IS_ERR(seq)) ++ goto out_buf; ++ ++ sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); ++ for (bindex = 0; bindex <= bend; bindex++, arg++) { ++ err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); ++ if (unlikely(err)) ++ break; ++ ++ br = au_sbr(sb, bindex); ++ brid = br->br_id; ++ BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); ++ err = __put_user(brid, &arg->id); ++ if (unlikely(err)) ++ break; ++ ++ BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); ++ err = __put_user(br->br_perm, &arg->perm); ++ if (unlikely(err)) ++ break; ++ ++ au_seq_path(seq, &br->br_path); ++ err = seq_putc(seq, '\0'); ++ if (!err && seq->count <= sz) { ++ err = copy_to_user(arg->path, seq->buf, seq->count); ++ seq->count = 0; ++ if (unlikely(err)) ++ break; ++ } else { ++ err = -E2BIG; ++ goto out_seq; ++ } ++ } ++ if (unlikely(err)) ++ err = -EFAULT; ++ ++out_seq: ++ kfree(seq); ++out_buf: ++ free_page((unsigned long)buf); ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++long au_brinfo_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_brinfo(file->f_path.dentry->d_sb, (void __user *)arg); ++} ++ ++#ifdef CONFIG_COMPAT ++long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_brinfo(file->f_path.dentry->d_sb, compat_ptr(arg)); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++void sysaufs_br_init(struct au_branch *br) ++{ ++ int i; ++ struct au_brsysfs *br_sysfs; ++ struct attribute *attr; ++ ++ br_sysfs = br->br_sysfs; ++ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { ++ attr = &br_sysfs->attr; ++ sysfs_attr_init(attr); ++ attr->name = br_sysfs->name; ++ attr->mode = S_IRUGO; ++ br_sysfs++; ++ } ++} ++ ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_branch *br; ++ struct kobject *kobj; ++ struct au_brsysfs *br_sysfs; ++ int i; ++ aufs_bindex_t bend; ++ ++ dbgaufs_brs_del(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ br_sysfs = br->br_sysfs; ++ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { ++ sysfs_remove_file(kobj, &br_sysfs->attr); ++ br_sysfs++; ++ } ++ } ++} ++ ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err, i; ++ aufs_bindex_t bend; ++ struct kobject *kobj; ++ struct au_branch *br; ++ struct au_brsysfs *br_sysfs; ++ ++ dbgaufs_brs_add(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ br_sysfs = br->br_sysfs; ++ snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), ++ SysaufsBr_PREFIX "%d", bindex); ++ snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), ++ SysaufsBrid_PREFIX "%d", bindex); ++ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { ++ err = sysfs_create_file(kobj, &br_sysfs->attr); ++ if (unlikely(err)) ++ pr_warn("failed %s under sysfs(%d)\n", ++ br_sysfs->name, err); ++ br_sysfs++; ++ } ++ } ++} +diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c +new file mode 100644 +index 0000000..dcfa824 +--- /dev/null ++++ b/fs/aufs/sysrq.c +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * magic sysrq hanlder ++ */ ++ ++/* #include */ ++#include ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void sysrq_sb(struct super_block *sb) ++{ ++ char *plevel; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ struct au_sphlhead *files; ++ struct au_finfo *finfo; ++ ++ plevel = au_plevel; ++ au_plevel = KERN_WARNING; ++ ++ /* since we define pr_fmt, call printk directly */ ++#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) ++ ++ sbinfo = au_sbi(sb); ++ printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); ++ pr("superblock\n"); ++ au_dpri_sb(sb); ++ ++#if 0 ++ pr("root dentry\n"); ++ au_dpri_dentry(sb->s_root); ++ pr("root inode\n"); ++ au_dpri_inode(d_inode(sb->s_root)); ++#endif ++ ++#if 0 ++ do { ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ ++ err = au_dpages_init(&dpages, GFP_ATOMIC); ++ if (unlikely(err)) ++ break; ++ err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); ++ if (!err) ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) ++ au_dpri_dentry(dpage->dentries[j]); ++ } ++ au_dpages_free(&dpages); ++ } while (0); ++#endif ++ ++#if 1 ++ { ++ struct inode *i; ++ ++ pr("isolated inode\n"); ++ spin_lock(&inode_sb_list_lock); ++ list_for_each_entry(i, &sb->s_inodes, i_sb_list) { ++ spin_lock(&i->i_lock); ++ if (1 || hlist_empty(&i->i_dentry)) ++ au_dpri_inode(i); ++ spin_unlock(&i->i_lock); ++ } ++ spin_unlock(&inode_sb_list_lock); ++ } ++#endif ++ pr("files\n"); ++ files = &au_sbi(sb)->si_files; ++ spin_lock(&files->spin); ++ hlist_for_each_entry(finfo, &files->head, fi_hlist) { ++ umode_t mode; ++ ++ file = finfo->fi_file; ++ mode = file_inode(file)->i_mode; ++ if (!special_file(mode)) ++ au_dpri_file(file); ++ } ++ spin_unlock(&files->spin); ++ pr("done\n"); ++ ++#undef pr ++ au_plevel = plevel; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* module parameter */ ++static char *aufs_sysrq_key = "a"; ++module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); ++MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); ++ ++static void au_sysrq(int key __maybe_unused) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ lockdep_off(); ++ au_sbilist_lock(); ++ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ sysrq_sb(sbinfo->si_sb); ++ au_sbilist_unlock(); ++ lockdep_on(); ++} ++ ++static struct sysrq_key_op au_sysrq_op = { ++ .handler = au_sysrq, ++ .help_msg = "Aufs", ++ .action_msg = "Aufs", ++ .enable_mask = SYSRQ_ENABLE_DUMP ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int __init au_sysrq_init(void) ++{ ++ int err; ++ char key; ++ ++ err = -1; ++ key = *aufs_sysrq_key; ++ if ('a' <= key && key <= 'z') ++ err = register_sysrq_key(key, &au_sysrq_op); ++ if (unlikely(err)) ++ pr_err("err %d, sysrq=%c\n", err, key); ++ return err; ++} ++ ++void au_sysrq_fin(void) ++{ ++ int err; ++ ++ err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); ++ if (unlikely(err)) ++ pr_err("err %d (ignored)\n", err); ++} +diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c +new file mode 100644 +index 0000000..c3f6a74 +--- /dev/null ++++ b/fs/aufs/vdir.c +@@ -0,0 +1,875 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * virtual or vertical directory ++ */ ++ ++#include "aufs.h" ++ ++static unsigned int calc_size(int nlen) ++{ ++ return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); ++} ++ ++static int set_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) { ++ p->de->de_str.len = 0; ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -1; /* error */ ++} ++ ++/* returns true or false */ ++static int is_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) ++ return !p->de->de_str.len; ++ return 1; ++} ++ ++static unsigned char *last_deblk(struct au_vdir *vdir) ++{ ++ return vdir->vd_deblk[vdir->vd_nblk - 1]; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* estimate the apropriate size for name hash table */ ++unsigned int au_rdhash_est(loff_t sz) ++{ ++ unsigned int n; ++ ++ n = UINT_MAX; ++ sz >>= 10; ++ if (sz < n) ++ n = sz; ++ if (sz < AUFS_RDHASH_DEF) ++ n = AUFS_RDHASH_DEF; ++ /* pr_info("n %u\n", n); */ ++ return n; ++} ++ ++/* ++ * the allocated memory has to be freed by ++ * au_nhash_wh_free() or au_nhash_de_free(). ++ */ ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) ++{ ++ struct hlist_head *head; ++ unsigned int u; ++ size_t sz; ++ ++ sz = sizeof(*nhash->nh_head) * num_hash; ++ head = kmalloc(sz, gfp); ++ if (head) { ++ nhash->nh_num = num_hash; ++ nhash->nh_head = head; ++ for (u = 0; u < num_hash; u++) ++ INIT_HLIST_HEAD(head++); ++ return 0; /* success */ ++ } ++ ++ return -ENOMEM; ++} ++ ++static void nhash_count(struct hlist_head *head) ++{ ++#if 0 ++ unsigned long n; ++ struct hlist_node *pos; ++ ++ n = 0; ++ hlist_for_each(pos, head) ++ n++; ++ pr_info("%lu\n", n); ++#endif ++} ++ ++static void au_nhash_wh_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_wh *pos; ++ struct hlist_node *node; ++ ++ hlist_for_each_entry_safe(pos, node, head, wh_hash) ++ kfree(pos); ++} ++ ++static void au_nhash_de_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_dehstr *pos; ++ struct hlist_node *node; ++ ++ hlist_for_each_entry_safe(pos, node, head, hash) ++ au_cache_free_vdir_dehstr(pos); ++} ++ ++static void au_nhash_do_free(struct au_nhash *nhash, ++ void (*free)(struct hlist_head *head)) ++{ ++ unsigned int n; ++ struct hlist_head *head; ++ ++ n = nhash->nh_num; ++ if (!n) ++ return; ++ ++ head = nhash->nh_head; ++ while (n-- > 0) { ++ nhash_count(head); ++ free(head++); ++ } ++ kfree(nhash->nh_head); ++} ++ ++void au_nhash_wh_free(struct au_nhash *whlist) ++{ ++ au_nhash_do_free(whlist, au_nhash_wh_do_free); ++} ++ ++static void au_nhash_de_free(struct au_nhash *delist) ++{ ++ au_nhash_do_free(delist, au_nhash_de_do_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit) ++{ ++ int num; ++ unsigned int u, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ ++ num = 0; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (u = 0; u < n; u++, head++) ++ hlist_for_each_entry(pos, head, wh_hash) ++ if (pos->wh_bindex == btgt && ++num > limit) ++ return 1; ++ return 0; ++} ++ ++static struct hlist_head *au_name_hash(struct au_nhash *nhash, ++ unsigned char *name, ++ unsigned int len) ++{ ++ unsigned int v; ++ /* const unsigned int magic_bit = 12; */ ++ ++ AuDebugOn(!nhash->nh_num || !nhash->nh_head); ++ ++ v = 0; ++ while (len--) ++ v += *name++; ++ /* v = hash_long(v, magic_bit); */ ++ v %= nhash->nh_num; ++ return nhash->nh_head + v; ++} ++ ++static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, ++ int nlen) ++{ ++ return str->len == nlen && !memcmp(str->name, name, nlen); ++} ++ ++/* returns found or not */ ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(whlist, name, nlen); ++ hlist_for_each_entry(pos, head, wh_hash) { ++ str = &pos->wh_str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* returns found(true) or not */ ++static int test_known(struct au_nhash *delist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_dehstr *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(delist, name, nlen); ++ hlist_for_each_entry(pos, head, hash) { ++ str = pos->str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, ++ unsigned char d_type) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ wh->wh_ino = ino; ++ wh->wh_type = d_type; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh) ++{ ++ int err; ++ struct au_vdir_destr *str; ++ struct au_vdir_wh *wh; ++ ++ AuDbg("%.*s\n", nlen, name); ++ AuDebugOn(!whlist->nh_num || !whlist->nh_head); ++ ++ err = -ENOMEM; ++ wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); ++ if (unlikely(!wh)) ++ goto out; ++ ++ err = 0; ++ wh->wh_bindex = bindex; ++ if (shwh) ++ au_shwh_init_wh(wh, ino, d_type); ++ str = &wh->wh_str; ++ str->len = nlen; ++ memcpy(str->name, name, nlen); ++ hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); ++ /* smp_mb(); */ ++ ++out: ++ return err; ++} ++ ++static int append_deblk(struct au_vdir *vdir) ++{ ++ int err; ++ unsigned long ul; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, deblk_end; ++ unsigned char **o; ++ ++ err = -ENOMEM; ++ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), ++ GFP_NOFS); ++ if (unlikely(!o)) ++ goto out; ++ ++ vdir->vd_deblk = o; ++ p.deblk = kmalloc(deblk_sz, GFP_NOFS); ++ if (p.deblk) { ++ ul = vdir->vd_nblk++; ++ vdir->vd_deblk[ul] = p.deblk; ++ vdir->vd_last.ul = ul; ++ vdir->vd_last.p.deblk = p.deblk; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ } ++ ++out: ++ return err; ++} ++ ++static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, ++ unsigned int d_type, struct au_nhash *delist) ++{ ++ int err; ++ unsigned int sz; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, *room, deblk_end; ++ struct au_vdir_dehstr *dehstr; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ room = &vdir->vd_last.p; ++ AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk ++ || !is_deblk_end(room, &deblk_end)); ++ ++ sz = calc_size(nlen); ++ if (unlikely(sz > deblk_end.deblk - room->deblk)) { ++ err = append_deblk(vdir); ++ if (unlikely(err)) ++ goto out; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ /* smp_mb(); */ ++ AuDebugOn(room->deblk != p.deblk); ++ } ++ ++ err = -ENOMEM; ++ dehstr = au_cache_alloc_vdir_dehstr(); ++ if (unlikely(!dehstr)) ++ goto out; ++ ++ dehstr->str = &room->de->de_str; ++ hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); ++ room->de->de_ino = ino; ++ room->de->de_type = d_type; ++ room->de->de_str.len = nlen; ++ memcpy(room->de->de_str.name, name, nlen); ++ ++ err = 0; ++ room->deblk += sz; ++ if (unlikely(set_deblk_end(room, &deblk_end))) ++ err = append_deblk(vdir); ++ /* smp_mb(); */ ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_vdir_free(struct au_vdir *vdir) ++{ ++ unsigned char **deblk; ++ ++ deblk = vdir->vd_deblk; ++ while (vdir->vd_nblk--) ++ kfree(*deblk++); ++ kfree(vdir->vd_deblk); ++ au_cache_free_vdir(vdir); ++} ++ ++static struct au_vdir *alloc_vdir(struct file *file) ++{ ++ struct au_vdir *vdir; ++ struct super_block *sb; ++ int err; ++ ++ sb = file->f_path.dentry->d_sb; ++ SiMustAnyLock(sb); ++ ++ err = -ENOMEM; ++ vdir = au_cache_alloc_vdir(); ++ if (unlikely(!vdir)) ++ goto out; ++ ++ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); ++ if (unlikely(!vdir->vd_deblk)) ++ goto out_free; ++ ++ vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; ++ if (!vdir->vd_deblk_sz) { ++ /* estimate the apropriate size for deblk */ ++ vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); ++ /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ ++ } ++ vdir->vd_nblk = 0; ++ vdir->vd_version = 0; ++ vdir->vd_jiffy = 0; ++ err = append_deblk(vdir); ++ if (!err) ++ return vdir; /* success */ ++ ++ kfree(vdir->vd_deblk); ++ ++out_free: ++ au_cache_free_vdir(vdir); ++out: ++ vdir = ERR_PTR(err); ++ return vdir; ++} ++ ++static int reinit_vdir(struct au_vdir *vdir) ++{ ++ int err; ++ union au_vdir_deblk_p p, deblk_end; ++ ++ while (vdir->vd_nblk > 1) { ++ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); ++ /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ ++ vdir->vd_nblk--; ++ } ++ p.deblk = vdir->vd_deblk[0]; ++ deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ /* keep vd_dblk_sz */ ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ vdir->vd_version = 0; ++ vdir->vd_jiffy = 0; ++ /* smp_mb(); */ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuFillVdir_CALLED 1 ++#define AuFillVdir_WHABLE (1 << 1) ++#define AuFillVdir_SHWH (1 << 2) ++#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) ++#define au_fset_fillvdir(flags, name) \ ++ do { (flags) |= AuFillVdir_##name; } while (0) ++#define au_fclr_fillvdir(flags, name) \ ++ do { (flags) &= ~AuFillVdir_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuFillVdir_SHWH ++#define AuFillVdir_SHWH 0 ++#endif ++ ++struct fillvdir_arg { ++ struct dir_context ctx; ++ struct file *file; ++ struct au_vdir *vdir; ++ struct au_nhash delist; ++ struct au_nhash whlist; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ int err; ++}; ++ ++static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, ++ loff_t offset __maybe_unused, u64 h_ino, ++ unsigned int d_type) ++{ ++ struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); ++ char *name = (void *)__name; ++ struct super_block *sb; ++ ino_t ino; ++ const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); ++ ++ arg->err = 0; ++ sb = arg->file->f_path.dentry->d_sb; ++ au_fset_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (nlen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (test_known(&arg->delist, name, nlen) ++ || au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already exists or whiteouted */ ++ ++ arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); ++ if (!arg->err) { ++ if (unlikely(nlen > AUFS_MAX_NAMELEN)) ++ d_type = DT_UNKNOWN; ++ arg->err = append_de(arg->vdir, name, nlen, ino, ++ d_type, &arg->delist); ++ } ++ } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { ++ name += AUFS_WH_PFX_LEN; ++ nlen -= AUFS_WH_PFX_LEN; ++ if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already whiteouted */ ++ ++ if (shwh) ++ arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, ++ &ino); ++ if (!arg->err) { ++ if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) ++ d_type = DT_UNKNOWN; ++ arg->err = au_nhash_append_wh ++ (&arg->whlist, name, nlen, ino, d_type, ++ arg->bindex, shwh); ++ } ++ } ++ ++out: ++ if (!arg->err) ++ arg->vdir->vd_jiffy = jiffies; ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, ++ struct au_nhash *whlist, struct au_nhash *delist) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ int err; ++ unsigned int nh, u; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ struct hlist_node *n; ++ char *p, *o; ++ struct au_vdir_destr *destr; ++ ++ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); ++ ++ err = -ENOMEM; ++ o = p = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ nh = whlist->nh_num; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ for (u = 0; u < nh; u++) { ++ head = whlist->nh_head + u; ++ hlist_for_each_entry_safe(pos, n, head, wh_hash) { ++ destr = &pos->wh_str; ++ memcpy(p, destr->name, destr->len); ++ err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, ++ pos->wh_ino, pos->wh_type, delist); ++ if (unlikely(err)) ++ break; ++ } ++ } ++ ++ free_page((unsigned long)o); ++ ++out: ++ AuTraceErr(err); ++ return err; ++#else ++ return 0; ++#endif ++} ++ ++static int au_do_read_vdir(struct fillvdir_arg *arg) ++{ ++ int err; ++ unsigned int rdhash; ++ loff_t offset; ++ aufs_bindex_t bend, bindex, bstart; ++ unsigned char shwh; ++ struct file *hf, *file; ++ struct super_block *sb; ++ ++ file = arg->file; ++ sb = file->f_path.dentry->d_sb; ++ SiMustAnyLock(sb); ++ ++ rdhash = au_sbi(sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); ++ err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out_delist; ++ ++ err = 0; ++ arg->flags = 0; ++ shwh = 0; ++ if (au_opt_test(au_mntflags(sb), SHWH)) { ++ shwh = 1; ++ au_fset_fillvdir(arg->flags, SHWH); ++ } ++ bstart = au_fbstart(file); ++ bend = au_fbend_dir(file); ++ for (bindex = bstart; !err && bindex <= bend; bindex++) { ++ hf = au_hf_dir(file, bindex); ++ if (!hf) ++ continue; ++ ++ offset = vfsub_llseek(hf, 0, SEEK_SET); ++ err = offset; ++ if (unlikely(offset)) ++ break; ++ ++ arg->bindex = bindex; ++ au_fclr_fillvdir(arg->flags, WHABLE); ++ if (shwh ++ || (bindex != bend ++ && au_br_whable(au_sbr_perm(sb, bindex)))) ++ au_fset_fillvdir(arg->flags, WHABLE); ++ do { ++ arg->err = 0; ++ au_fclr_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(hf, &arg->ctx); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); ++ ++ /* ++ * dir_relax() may be good for concurrency, but aufs should not ++ * use it since it will cause a lockdep problem. ++ */ ++ } ++ ++ if (!err && shwh) ++ err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); ++ ++ au_nhash_wh_free(&arg->whlist); ++ ++out_delist: ++ au_nhash_de_free(&arg->delist); ++out: ++ return err; ++} ++ ++static int read_vdir(struct file *file, int may_read) ++{ ++ int err; ++ unsigned long expire; ++ unsigned char do_read; ++ struct fillvdir_arg arg = { ++ .ctx = { ++ .actor = fillvdir ++ } ++ }; ++ struct inode *inode; ++ struct au_vdir *vdir, *allocated; ++ ++ err = 0; ++ inode = file_inode(file); ++ IMustLock(inode); ++ SiMustAnyLock(inode->i_sb); ++ ++ allocated = NULL; ++ do_read = 0; ++ expire = au_sbi(inode->i_sb)->si_rdcache; ++ vdir = au_ivdir(inode); ++ if (!vdir) { ++ do_read = 1; ++ vdir = alloc_vdir(file); ++ err = PTR_ERR(vdir); ++ if (IS_ERR(vdir)) ++ goto out; ++ err = 0; ++ allocated = vdir; ++ } else if (may_read ++ && (inode->i_version != vdir->vd_version ++ || time_after(jiffies, vdir->vd_jiffy + expire))) { ++ do_read = 1; ++ err = reinit_vdir(vdir); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ if (!do_read) ++ return 0; /* success */ ++ ++ arg.file = file; ++ arg.vdir = vdir; ++ err = au_do_read_vdir(&arg); ++ if (!err) { ++ /* file->f_pos = 0; */ /* todo: ctx->pos? */ ++ vdir->vd_version = inode->i_version; ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ if (allocated) ++ au_set_ivdir(inode, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++out: ++ return err; ++} ++ ++static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) ++{ ++ int err, rerr; ++ unsigned long ul, n; ++ const unsigned int deblk_sz = src->vd_deblk_sz; ++ ++ AuDebugOn(tgt->vd_nblk != 1); ++ ++ err = -ENOMEM; ++ if (tgt->vd_nblk < src->vd_nblk) { ++ unsigned char **p; ++ ++ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, ++ GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ tgt->vd_deblk = p; ++ } ++ ++ if (tgt->vd_deblk_sz != deblk_sz) { ++ unsigned char *p; ++ ++ tgt->vd_deblk_sz = deblk_sz; ++ p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ tgt->vd_deblk[0] = p; ++ } ++ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); ++ tgt->vd_version = src->vd_version; ++ tgt->vd_jiffy = src->vd_jiffy; ++ ++ n = src->vd_nblk; ++ for (ul = 1; ul < n; ul++) { ++ tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, ++ GFP_NOFS); ++ if (unlikely(!tgt->vd_deblk[ul])) ++ goto out; ++ tgt->vd_nblk++; ++ } ++ tgt->vd_nblk = n; ++ tgt->vd_last.ul = tgt->vd_last.ul; ++ tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; ++ tgt->vd_last.p.deblk += src->vd_last.p.deblk ++ - src->vd_deblk[src->vd_last.ul]; ++ /* smp_mb(); */ ++ return 0; /* success */ ++ ++out: ++ rerr = reinit_vdir(tgt); ++ BUG_ON(rerr); ++ return err; ++} ++ ++int au_vdir_init(struct file *file) ++{ ++ int err; ++ struct inode *inode; ++ struct au_vdir *vdir_cache, *allocated; ++ ++ /* test file->f_pos here instead of ctx->pos */ ++ err = read_vdir(file, !file->f_pos); ++ if (unlikely(err)) ++ goto out; ++ ++ allocated = NULL; ++ vdir_cache = au_fvdir_cache(file); ++ if (!vdir_cache) { ++ vdir_cache = alloc_vdir(file); ++ err = PTR_ERR(vdir_cache); ++ if (IS_ERR(vdir_cache)) ++ goto out; ++ allocated = vdir_cache; ++ } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { ++ /* test file->f_pos here instead of ctx->pos */ ++ err = reinit_vdir(vdir_cache); ++ if (unlikely(err)) ++ goto out; ++ } else ++ return 0; /* success */ ++ ++ inode = file_inode(file); ++ err = copy_vdir(vdir_cache, au_ivdir(inode)); ++ if (!err) { ++ file->f_version = inode->i_version; ++ if (allocated) ++ au_set_fvdir_cache(file, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++out: ++ return err; ++} ++ ++static loff_t calc_offset(struct au_vdir *vdir) ++{ ++ loff_t offset; ++ union au_vdir_deblk_p p; ++ ++ p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; ++ offset = vdir->vd_last.p.deblk - p.deblk; ++ offset += vdir->vd_deblk_sz * vdir->vd_last.ul; ++ return offset; ++} ++ ++/* returns true or false */ ++static int seek_vdir(struct file *file, struct dir_context *ctx) ++{ ++ int valid; ++ unsigned int deblk_sz; ++ unsigned long ul, n; ++ loff_t offset; ++ union au_vdir_deblk_p p, deblk_end; ++ struct au_vdir *vdir_cache; ++ ++ valid = 1; ++ vdir_cache = au_fvdir_cache(file); ++ offset = calc_offset(vdir_cache); ++ AuDbg("offset %lld\n", offset); ++ if (ctx->pos == offset) ++ goto out; ++ ++ vdir_cache->vd_last.ul = 0; ++ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; ++ if (!ctx->pos) ++ goto out; ++ ++ valid = 0; ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ ul = div64_u64(ctx->pos, deblk_sz); ++ AuDbg("ul %lu\n", ul); ++ if (ul >= vdir_cache->vd_nblk) ++ goto out; ++ ++ n = vdir_cache->vd_nblk; ++ for (; ul < n; ul++) { ++ p.deblk = vdir_cache->vd_deblk[ul]; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ offset = ul; ++ offset *= deblk_sz; ++ while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { ++ unsigned int l; ++ ++ l = calc_size(p.de->de_str.len); ++ offset += l; ++ p.deblk += l; ++ } ++ if (!is_deblk_end(&p, &deblk_end)) { ++ valid = 1; ++ vdir_cache->vd_last.ul = ul; ++ vdir_cache->vd_last.p = p; ++ break; ++ } ++ } ++ ++out: ++ /* smp_mb(); */ ++ AuTraceErr(!valid); ++ return valid; ++} ++ ++int au_vdir_fill_de(struct file *file, struct dir_context *ctx) ++{ ++ unsigned int l, deblk_sz; ++ union au_vdir_deblk_p deblk_end; ++ struct au_vdir *vdir_cache; ++ struct au_vdir_de *de; ++ ++ vdir_cache = au_fvdir_cache(file); ++ if (!seek_vdir(file, ctx)) ++ return 0; ++ ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ while (1) { ++ deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ deblk_end.deblk += deblk_sz; ++ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { ++ de = vdir_cache->vd_last.p.de; ++ AuDbg("%.*s, off%lld, i%lu, dt%d\n", ++ de->de_str.len, de->de_str.name, ctx->pos, ++ (unsigned long)de->de_ino, de->de_type); ++ if (unlikely(!dir_emit(ctx, de->de_str.name, ++ de->de_str.len, de->de_ino, ++ de->de_type))) { ++ /* todo: ignore the error caused by udba? */ ++ /* return err; */ ++ return 0; ++ } ++ ++ l = calc_size(de->de_str.len); ++ vdir_cache->vd_last.p.deblk += l; ++ ctx->pos += l; ++ } ++ if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { ++ vdir_cache->vd_last.ul++; ++ vdir_cache->vd_last.p.deblk ++ = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ ctx->pos = deblk_sz * vdir_cache->vd_last.ul; ++ continue; ++ } ++ break; ++ } ++ ++ /* smp_mb(); */ ++ return 0; ++} +diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c +new file mode 100644 +index 0000000..da0df5a +--- /dev/null ++++ b/fs/aufs/vfsub.c +@@ -0,0 +1,835 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did) ++{ ++ int err; ++ struct kstat st; ++ struct super_block *h_sb; ++ ++ /* for remote fs, leave work for its getattr or d_revalidate */ ++ /* for bad i_attr fs, handle them in aufs_getattr() */ ++ /* still some fs may acquire i_mutex. we need to skip them */ ++ err = 0; ++ if (!did) ++ did = &err; ++ h_sb = h_path->dentry->d_sb; ++ *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); ++ if (*did) ++ err = vfs_getattr(h_path, &st); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct file *vfsub_dentry_open(struct path *path, int flags) ++{ ++ struct file *file; ++ ++ file = dentry_open(path, flags /* | __FMODE_NONOTIFY */, ++ current_cred()); ++ if (!IS_ERR_OR_NULL(file) ++ && (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) ++ i_readcount_inc(d_inode(path->dentry)); ++ ++ return file; ++} ++ ++struct file *vfsub_filp_open(const char *path, int oflags, int mode) ++{ ++ struct file *file; ++ ++ lockdep_off(); ++ file = filp_open(path, ++ oflags /* | __FMODE_NONOTIFY */, ++ mode); ++ lockdep_on(); ++ if (IS_ERR(file)) ++ goto out; ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ ++out: ++ return file; ++} ++ ++/* ++ * Ideally this function should call VFS:do_last() in order to keep all its ++ * checkings. But it is very hard for aufs to regenerate several VFS internal ++ * structure such as nameidata. This is a second (or third) best approach. ++ * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). ++ */ ++int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *args, struct au_branch *br) ++{ ++ int err; ++ struct file *file = args->file; ++ /* copied from linux/fs/namei.c:atomic_open() */ ++ struct dentry *const DENTRY_NOT_SET = (void *)-1UL; ++ ++ IMustLock(dir); ++ AuDebugOn(!dir->i_op->atomic_open); ++ ++ err = au_br_test_oflag(args->open_flag, br); ++ if (unlikely(err)) ++ goto out; ++ ++ args->file->f_path.dentry = DENTRY_NOT_SET; ++ args->file->f_path.mnt = au_br_mnt(br); ++ err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, ++ args->create_mode, args->opened); ++ if (err >= 0) { ++ /* some filesystems don't set FILE_CREATED while succeeded? */ ++ if (*args->opened & FILE_CREATED) ++ fsnotify_create(dir, dentry); ++ } else ++ goto out; ++ ++ ++ if (!err) { ++ /* todo: call VFS:may_open() here */ ++ err = open_check_o_direct(file); ++ /* todo: ima_file_check() too? */ ++ if (!err && (args->open_flag & __FMODE_EXEC)) ++ err = deny_write_access(file); ++ if (unlikely(err)) ++ /* note that the file is created and still opened */ ++ goto out; ++ } ++ ++ atomic_inc(&br->br_count); ++ fsnotify_open(file); ++ ++out: ++ return err; ++} ++ ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) ++{ ++ int err; ++ ++ err = kern_path(name, flags, path); ++ if (!err && d_is_positive(path->dentry)) ++ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len) ++{ ++ struct path path = { ++ .mnt = NULL ++ }; ++ ++ /* VFS checks it too, but by WARN_ON_ONCE() */ ++ IMustLock(d_inode(parent)); ++ ++ path.dentry = lookup_one_len(name, parent, len); ++ if (IS_ERR(path.dentry)) ++ goto out; ++ if (d_is_positive(path.dentry)) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++out: ++ AuTraceErrPtr(path.dentry); ++ return path.dentry; ++} ++ ++void vfsub_call_lkup_one(void *args) ++{ ++ struct vfsub_lkup_one_args *a = args; ++ *a->errp = vfsub_lkup_one(a->name, a->parent); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ struct dentry *d; ++ ++ lockdep_off(); ++ d = lock_rename(d1, d2); ++ lockdep_on(); ++ au_hn_suspend(hdir1); ++ if (hdir1 != hdir2) ++ au_hn_suspend(hdir2); ++ ++ return d; ++} ++ ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ au_hn_resume(hdir1); ++ if (hdir1 != hdir2) ++ au_hn_resume(hdir2); ++ lockdep_off(); ++ unlock_rename(d1, d2); ++ lockdep_on(); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, d, mode, 0); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_create(dir, path->dentry, mode, want_excl); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_symlink(path, d, symname); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_symlink(dir, path->dentry, symname); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, d, mode, new_encode_dev(dev)); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_mknod(dir, path->dentry, mode, dev); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++static int au_test_nlink(struct inode *inode) ++{ ++ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ ++ ++ if (!au_test_fs_no_limit_nlink(inode->i_sb) ++ || inode->i_nlink < link_max) ++ return 0; ++ return -EMLINK; ++} ++ ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, ++ struct inode **delegated_inode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ err = au_test_nlink(d_inode(src_dentry)); ++ if (unlikely(err)) ++ return err; ++ ++ /* we don't call may_linkat() */ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_link(src_dentry, path, d); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ /* fuse has different memory inode for the same inumber */ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, ++ struct inode *dir, struct path *path, ++ struct inode **delegated_inode) ++{ ++ int err; ++ struct path tmp = { ++ .mnt = path->mnt ++ }; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ IMustLock(src_dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ tmp.dentry = src_dentry->d_parent; ++ err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_rename(src_dir, src_dentry, dir, path->dentry, ++ delegated_inode, /*flags*/0); ++ lockdep_on(); ++ if (!err) { ++ int did; ++ ++ tmp.dentry = d->d_parent; ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mkdir(path, d, mode); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_mkdir(dir, path->dentry, mode); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_rmdir(struct inode *dir, struct path *path) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_rmdir(path, d); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_rmdir(dir, path->dentry); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = { ++ .dentry = path->dentry->d_parent, ++ .mnt = path->mnt ++ }; ++ ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: support mmap_sem? */ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ lockdep_off(); ++ err = vfs_read(file, ubuf, count, ppos); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++/* todo: kernel_read()? */ ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_read_u(file, buf.u, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ lockdep_off(); ++ err = vfs_write(file, ubuf, count, ppos); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ const char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_write_u(file, buf.u, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++int vfsub_flush(struct file *file, fl_owner_t id) ++{ ++ int err; ++ ++ err = 0; ++ if (file->f_op->flush) { ++ if (!au_test_nfs(file->f_path.dentry->d_sb)) ++ err = file->f_op->flush(file, id); ++ else { ++ lockdep_off(); ++ err = file->f_op->flush(file, id); ++ lockdep_on(); ++ } ++ if (!err) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); ++ /*ignore*/ ++ } ++ return err; ++} ++ ++int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) ++{ ++ int err; ++ ++ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); ++ ++ lockdep_off(); ++ err = iterate_dir(file, ctx); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = do_splice_to(in, ppos, pipe, len, flags); ++ lockdep_on(); ++ file_accessed(in); ++ if (err >= 0) ++ vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = do_splice_from(pipe, out, ppos, len, flags); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++int vfsub_fsync(struct file *file, struct path *path, int datasync) ++{ ++ int err; ++ ++ /* file can be NULL */ ++ lockdep_off(); ++ err = vfs_fsync(file, datasync); ++ lockdep_on(); ++ if (!err) { ++ if (!path) { ++ AuDebugOn(!file); ++ path = &file->f_path; ++ } ++ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ ++ } ++ return err; ++} ++ ++/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file) ++{ ++ int err; ++ struct inode *h_inode; ++ struct super_block *h_sb; ++ ++ if (!h_file) { ++ err = vfsub_truncate(h_path, length); ++ goto out; ++ } ++ ++ h_inode = d_inode(h_path->dentry); ++ h_sb = h_inode->i_sb; ++ lockdep_off(); ++ sb_start_write(h_sb); ++ lockdep_on(); ++ err = locks_verify_truncate(h_inode, h_file, length); ++ if (!err) ++ err = security_path_truncate(h_path); ++ if (!err) { ++ lockdep_off(); ++ err = do_truncate(h_path->dentry, length, attr, h_file); ++ lockdep_on(); ++ } ++ lockdep_off(); ++ sb_end_write(h_sb); ++ lockdep_on(); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_vfsub_mkdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++ int mode; ++}; ++ ++static void au_call_vfsub_mkdir(void *args) ++{ ++ struct au_vfsub_mkdir_args *a = args; ++ *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); ++} ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) { ++ lockdep_off(); ++ err = vfsub_mkdir(dir, path, mode); ++ lockdep_on(); ++ } else { ++ struct au_vfsub_mkdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path, ++ .mode = mode ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++struct au_vfsub_rmdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++}; ++ ++static void au_call_vfsub_rmdir(void *args) ++{ ++ struct au_vfsub_rmdir_args *a = args; ++ *a->errp = vfsub_rmdir(a->dir, a->path); ++} ++ ++int vfsub_sio_rmdir(struct inode *dir, struct path *path) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) { ++ lockdep_off(); ++ err = vfsub_rmdir(dir, path); ++ lockdep_on(); ++ } else { ++ struct au_vfsub_rmdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct notify_change_args { ++ int *errp; ++ struct path *path; ++ struct iattr *ia; ++ struct inode **delegated_inode; ++}; ++ ++static void call_notify_change(void *args) ++{ ++ struct notify_change_args *a = args; ++ struct inode *h_inode; ++ ++ h_inode = d_inode(a->path->dentry); ++ IMustLock(h_inode); ++ ++ *a->errp = -EPERM; ++ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { ++ lockdep_off(); ++ *a->errp = notify_change(a->path->dentry, a->ia, ++ a->delegated_inode); ++ lockdep_on(); ++ if (!*a->errp) ++ vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ ++ } ++ AuTraceErr(*a->errp); ++} ++ ++int vfsub_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode) ++{ ++ int err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia, ++ .delegated_inode = delegated_inode ++ }; ++ ++ call_notify_change(&args); ++ ++ return err; ++} ++ ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode) ++{ ++ int err, wkq_err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia, ++ .delegated_inode = delegated_inode ++ }; ++ ++ wkq_err = au_wkq_wait(call_notify_change, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct unlink_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++ struct inode **delegated_inode; ++}; ++ ++static void call_unlink(void *args) ++{ ++ struct unlink_args *a = args; ++ struct dentry *d = a->path->dentry; ++ struct inode *h_inode; ++ const int stop_sillyrename = (au_test_nfs(d->d_sb) ++ && au_dcount(d) == 1); ++ ++ IMustLock(a->dir); ++ ++ a->path->dentry = d->d_parent; ++ *a->errp = security_path_unlink(a->path, d); ++ a->path->dentry = d; ++ if (unlikely(*a->errp)) ++ return; ++ ++ if (!stop_sillyrename) ++ dget(d); ++ h_inode = NULL; ++ if (d_is_positive(d)) { ++ h_inode = d_inode(d); ++ ihold(h_inode); ++ } ++ ++ lockdep_off(); ++ *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); ++ lockdep_on(); ++ if (!*a->errp) { ++ struct path tmp = { ++ .dentry = d->d_parent, ++ .mnt = a->path->mnt ++ }; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++ if (!stop_sillyrename) ++ dput(d); ++ if (h_inode) ++ iput(h_inode); ++ ++ AuTraceErr(*a->errp); ++} ++ ++/* ++ * @dir: must be locked. ++ * @dentry: target dentry. ++ */ ++int vfsub_unlink(struct inode *dir, struct path *path, ++ struct inode **delegated_inode, int force) ++{ ++ int err; ++ struct unlink_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path, ++ .delegated_inode = delegated_inode ++ }; ++ ++ if (!force) ++ call_unlink(&args); ++ else { ++ int wkq_err; ++ ++ wkq_err = au_wkq_wait(call_unlink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} +diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h +new file mode 100644 +index 0000000..778ea20 +--- /dev/null ++++ b/fs/aufs/vfsub.h +@@ -0,0 +1,273 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#ifndef __AUFS_VFSUB_H__ ++#define __AUFS_VFSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "debug.h" ++ ++/* copied from linux/fs/internal.h */ ++/* todo: BAD approach!! */ ++extern void __mnt_drop_write(struct vfsmount *); ++extern spinlock_t inode_sb_list_lock; ++extern int open_check_o_direct(struct file *f); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for lower inode */ ++/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ ++/* reduce? gave up. */ ++enum { ++ AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ ++ AuLsc_I_PARENT, /* lower inode, parent first */ ++ AuLsc_I_PARENT2, /* copyup dirs */ ++ AuLsc_I_PARENT3, /* copyup wh */ ++ AuLsc_I_CHILD, ++ AuLsc_I_CHILD2, ++ AuLsc_I_End ++}; ++ ++/* to debug easier, do not make them inlined functions */ ++#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) ++#define IMustLock(i) MtxMustLock(&(i)->i_mutex) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void vfsub_drop_nlink(struct inode *inode) ++{ ++ AuDebugOn(!inode->i_nlink); ++ drop_nlink(inode); ++} ++ ++static inline void vfsub_dead_dir(struct inode *inode) ++{ ++ AuDebugOn(!S_ISDIR(inode->i_mode)); ++ inode->i_flags |= S_DEAD; ++ clear_nlink(inode); ++} ++ ++static inline int vfsub_native_ro(struct inode *inode) ++{ ++ return (inode->i_sb->s_flags & MS_RDONLY) ++ || IS_RDONLY(inode) ++ /* || IS_APPEND(inode) */ ++ || IS_IMMUTABLE(inode); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did); ++struct file *vfsub_dentry_open(struct path *path, int flags); ++struct file *vfsub_filp_open(const char *path, int oflags, int mode); ++struct vfsub_aopen_args { ++ struct file *file; ++ unsigned int open_flag; ++ umode_t create_mode; ++ int *opened; ++}; ++struct au_branch; ++int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *args, struct au_branch *br); ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); ++ ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len); ++ ++struct vfsub_lkup_one_args { ++ struct dentry **errp; ++ struct qstr *name; ++ struct dentry *parent; ++}; ++ ++static inline struct dentry *vfsub_lkup_one(struct qstr *name, ++ struct dentry *parent) ++{ ++ return vfsub_lookup_one_len(name->name, parent, name->len); ++} ++ ++void vfsub_call_lkup_one(void *args); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int vfsub_mnt_want_write(struct vfsmount *mnt) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = mnt_want_write(mnt); ++ lockdep_on(); ++ return err; ++} ++ ++static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) ++{ ++ lockdep_off(); ++ mnt_drop_write(mnt); ++ lockdep_on(); ++} ++ ++#if 0 /* reserved */ ++static inline void vfsub_mnt_drop_write_file(struct file *file) ++{ ++ lockdep_off(); ++ mnt_drop_write_file(file); ++ lockdep_on(); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_hinode; ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode, ++ bool want_excl); ++int vfsub_symlink(struct inode *dir, struct path *path, ++ const char *symname); ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, ++ struct path *path, struct inode **delegated_inode); ++int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, ++ struct inode *hdir, struct path *path, ++ struct inode **delegated_inode); ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_rmdir(struct inode *dir, struct path *path); ++ ++/* ---------------------------------------------------------------------- */ ++ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++int vfsub_flush(struct file *file, fl_owner_t id); ++int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); ++ ++static inline loff_t vfsub_f_size_read(struct file *file) ++{ ++ return i_size_read(file_inode(file)); ++} ++ ++static inline unsigned int vfsub_file_flags(struct file *file) ++{ ++ unsigned int flags; ++ ++ spin_lock(&file->f_lock); ++ flags = file->f_flags; ++ spin_unlock(&file->f_lock); ++ ++ return flags; ++} ++ ++#if 0 /* reserved */ ++static inline void vfsub_file_accessed(struct file *h_file) ++{ ++ file_accessed(h_file); ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ ++} ++#endif ++ ++static inline void vfsub_touch_atime(struct vfsmount *h_mnt, ++ struct dentry *h_dentry) ++{ ++ struct path h_path = { ++ .dentry = h_dentry, ++ .mnt = h_mnt ++ }; ++ touch_atime(&h_path); ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++} ++ ++static inline int vfsub_update_time(struct inode *h_inode, struct timespec *ts, ++ int flags) ++{ ++ return generic_update_time(h_inode, ts, flags); ++ /* no vfsub_update_h_iattr() since we don't have struct path */ ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++ ++static inline long vfsub_truncate(struct path *path, loff_t length) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = vfs_truncate(path, length); ++ lockdep_on(); ++ return err; ++} ++ ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file); ++int vfsub_fsync(struct file *file, struct path *path, int datasync); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) ++{ ++ loff_t err; ++ ++ lockdep_off(); ++ err = vfs_llseek(file, offset, origin); ++ lockdep_on(); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_sio_rmdir(struct inode *dir, struct path *path); ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode); ++int vfsub_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode); ++int vfsub_unlink(struct inode *dir, struct path *path, ++ struct inode **delegated_inode, int force); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int vfsub_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = vfs_setxattr(dentry, name, value, size, flags); ++ lockdep_on(); ++ ++ return err; ++} ++ ++static inline int vfsub_removexattr(struct dentry *dentry, const char *name) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = vfs_removexattr(dentry, name); ++ lockdep_on(); ++ ++ return err; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_VFSUB_H__ */ +diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c +new file mode 100644 +index 0000000..53a01fb +--- /dev/null ++++ b/fs/aufs/wbr_policy.c +@@ -0,0 +1,752 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * policies for selecting one among multiple writable branches ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* subset of cpup_attr() */ ++static noinline_for_stack ++int au_cpdown_attr(struct path *h_path, struct dentry *h_src) ++{ ++ int err, sbits; ++ struct iattr ia; ++ struct inode *h_isrc; ++ ++ h_isrc = d_inode(h_src); ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; ++ ia.ia_mode = h_isrc->i_mode; ++ ia.ia_uid = h_isrc->i_uid; ++ ia.ia_gid = h_isrc->i_gid; ++ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(d_inode(h_path->dentry), h_isrc->i_flags); ++ /* no delegation since it is just created */ ++ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); ++ } ++ ++ return err; ++} ++ ++#define AuCpdown_PARENT_OPQ 1 ++#define AuCpdown_WHED (1 << 1) ++#define AuCpdown_MADE_DIR (1 << 2) ++#define AuCpdown_DIROPQ (1 << 3) ++#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) ++#define au_fset_cpdown(flags, name) \ ++ do { (flags) |= AuCpdown_##name; } while (0) ++#define au_fclr_cpdown(flags, name) \ ++ do { (flags) &= ~AuCpdown_##name; } while (0) ++ ++static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, ++ unsigned int *flags) ++{ ++ int err; ++ struct dentry *opq_dentry; ++ ++ opq_dentry = au_diropq_create(dentry, bdst); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ dput(opq_dentry); ++ au_fset_cpdown(*flags, DIROPQ); ++ ++out: ++ return err; ++} ++ ++static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, ++ struct inode *dir, aufs_bindex_t bdst) ++{ ++ int err; ++ struct path h_path; ++ struct au_branch *br; ++ ++ br = au_sbr(dentry->d_sb, bdst); ++ h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ if (d_is_positive(h_path.dentry)) { ++ h_path.mnt = au_br_mnt(br); ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, ++ dentry); ++ } ++ dput(h_path.dentry); ++ ++out: ++ return err; ++} ++ ++static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent, void *arg) ++{ ++ int err, rerr; ++ aufs_bindex_t bopq, bstart; ++ struct path h_path; ++ struct dentry *parent; ++ struct inode *h_dir, *h_inode, *inode, *dir; ++ unsigned int *flags = arg; ++ ++ bstart = au_dbstart(dentry); ++ /* dentry is di-locked */ ++ parent = dget_parent(dentry); ++ dir = d_inode(parent); ++ h_dir = d_inode(h_parent); ++ AuDebugOn(h_dir != au_h_iptr(dir, bdst)); ++ IMustLock(h_dir); ++ ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); ++ if (unlikely(err < 0)) ++ goto out; ++ h_path.dentry = au_h_dptr(dentry, bdst); ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); ++ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, ++ S_IRWXU | S_IRUGO | S_IXUGO); ++ if (unlikely(err)) ++ goto out_put; ++ au_fset_cpdown(*flags, MADE_DIR); ++ ++ bopq = au_dbdiropq(dentry); ++ au_fclr_cpdown(*flags, WHED); ++ au_fclr_cpdown(*flags, DIROPQ); ++ if (au_dbwh(dentry) == bdst) ++ au_fset_cpdown(*flags, WHED); ++ if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) ++ au_fset_cpdown(*flags, PARENT_OPQ); ++ h_inode = d_inode(h_path.dentry); ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ if (au_ftest_cpdown(*flags, WHED)) { ++ err = au_cpdown_dir_opq(dentry, bdst, flags); ++ if (unlikely(err)) { ++ mutex_unlock(&h_inode->i_mutex); ++ goto out_dir; ++ } ++ } ++ ++ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_opq; ++ ++ if (au_ftest_cpdown(*flags, WHED)) { ++ err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); ++ if (unlikely(err)) ++ goto out_opq; ++ } ++ ++ inode = d_inode(dentry); ++ if (au_ibend(inode) < bdst) ++ au_set_ibend(inode, bdst); ++ au_set_h_iptr(inode, bdst, au_igrab(h_inode), ++ au_hi_flags(inode, /*isdir*/1)); ++ au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); ++ goto out; /* success */ ++ ++ /* revert */ ++out_opq: ++ if (au_ftest_cpdown(*flags, DIROPQ)) { ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bdst); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing diropq for %pd b%d (%d)\n", ++ dentry, bdst, rerr); ++ err = -EIO; ++ goto out; ++ } ++ } ++out_dir: ++ if (au_ftest_cpdown(*flags, MADE_DIR)) { ++ rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing %pd b%d (%d)\n", ++ dentry, bdst, rerr); ++ err = -EIO; ++ } ++ } ++out_put: ++ au_set_h_dptr(dentry, bdst, NULL); ++ if (au_dbend(dentry) == bdst) ++ au_update_dbend(dentry); ++out: ++ dput(parent); ++ return err; ++} ++ ++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ unsigned int flags; ++ ++ flags = 0; ++ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies for create */ ++ ++int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err, i, j, ndentry; ++ aufs_bindex_t bopq; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries, *parent, *d; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ parent = dget_parent(dentry); ++ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); ++ if (unlikely(err)) ++ goto out_free; ++ ++ err = bindex; ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ d = dentries[j]; ++ di_read_lock_parent2(d, !AuLock_IR); ++ bopq = au_dbdiropq(d); ++ di_read_unlock(d, !AuLock_IR); ++ if (bopq >= 0 && bopq < err) ++ err = bopq; ++ } ++ } ++ ++out_free: ++ dput(parent); ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) ++ return bindex; ++ return -EROFS; ++} ++ ++/* top down parent */ ++static int au_wbr_create_tdp(struct dentry *dentry, ++ unsigned int flags __maybe_unused) ++{ ++ int err; ++ aufs_bindex_t bstart, bindex; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ err = bstart; ++ if (!au_br_rdonly(au_sbr(sb, bstart))) ++ goto out; ++ ++ err = -EROFS; ++ parent = dget_parent(dentry); ++ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || d_is_negative(h_parent)) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) { ++ err = au_wbr_bu(sb, bstart - 1); ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ } ++ ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* an exception for the policy other than tdp */ ++static int au_wbr_create_exp(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bdiropq; ++ struct dentry *parent; ++ ++ err = -1; ++ bwh = au_dbwh(dentry); ++ parent = dget_parent(dentry); ++ bdiropq = au_dbdiropq(parent); ++ if (bwh >= 0) { ++ if (bdiropq >= 0) ++ err = min(bdiropq, bwh); ++ else ++ err = bwh; ++ AuDbg("%d\n", err); ++ } else if (bdiropq >= 0) { ++ err = bdiropq; ++ AuDbg("%d\n", err); ++ } ++ dput(parent); ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) ++ err = -1; ++ ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* round robin */ ++static int au_wbr_create_init_rr(struct super_block *sb) ++{ ++ int err; ++ ++ err = au_wbr_bu(sb, au_sbend(sb)); ++ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ ++ /* smp_mb(); */ ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) ++{ ++ int err, nbr; ++ unsigned int u; ++ aufs_bindex_t bindex, bend; ++ struct super_block *sb; ++ atomic_t *next; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ next = &au_sbi(sb)->si_wbr_rr_next; ++ bend = au_sbend(sb); ++ nbr = bend + 1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (!au_ftest_wbr(flags, DIR)) { ++ err = atomic_dec_return(next) + 1; ++ /* modulo for 0 is meaningless */ ++ if (unlikely(!err)) ++ err = atomic_dec_return(next) + 1; ++ } else ++ err = atomic_read(next); ++ AuDbg("%d\n", err); ++ u = err; ++ err = u % nbr; ++ AuDbg("%d\n", err); ++ if (!au_br_rdonly(au_sbr(sb, err))) ++ break; ++ err = -EROFS; ++ } ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out: ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space */ ++static void au_mfs(struct dentry *dentry, struct dentry *parent) ++{ ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_wbr_mfs *mfs; ++ struct dentry *h_parent; ++ aufs_bindex_t bindex, bend; ++ int err; ++ unsigned long long b, bavail; ++ struct path h_path; ++ /* reduce the stack usage */ ++ struct kstatfs *st; ++ ++ st = kmalloc(sizeof(*st), GFP_NOFS); ++ if (unlikely(!st)) { ++ AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); ++ return; ++ } ++ ++ bavail = 0; ++ sb = dentry->d_sb; ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ MtxMustLock(&mfs->mfs_lock); ++ mfs->mfs_bindex = -EROFS; ++ mfs->mfsrr_bytes = 0; ++ if (!parent) { ++ bindex = 0; ++ bend = au_sbend(sb); ++ } else { ++ bindex = au_dbstart(parent); ++ bend = au_dbtaildir(parent); ++ } ++ ++ for (; bindex <= bend; bindex++) { ++ if (parent) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || d_is_negative(h_parent)) ++ continue; ++ } ++ br = au_sbr(sb, bindex); ++ if (au_br_rdonly(br)) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ h_path.mnt = au_br_mnt(br); ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, st); ++ if (unlikely(err)) { ++ AuWarn1("failed statfs, b%d, %d\n", bindex, err); ++ continue; ++ } ++ ++ /* when the available size is equal, select the lower one */ ++ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) ++ || sizeof(b) < sizeof(st->f_bsize)); ++ b = st->f_bavail * st->f_bsize; ++ br->br_wbr->wbr_bytes = b; ++ if (b >= bavail) { ++ bavail = b; ++ mfs->mfs_bindex = bindex; ++ mfs->mfs_jiffy = jiffies; ++ } ++ } ++ ++ mfs->mfsrr_bytes = bavail; ++ AuDbg("b%d\n", mfs->mfs_bindex); ++ kfree(st); ++} ++ ++static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) ++{ ++ int err; ++ struct dentry *parent; ++ struct super_block *sb; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ parent = NULL; ++ if (au_ftest_wbr(flags, PARENT)) ++ parent = dget_parent(dentry); ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) ++ || mfs->mfs_bindex < 0 ++ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) ++ au_mfs(dentry, parent); ++ mutex_unlock(&mfs->mfs_lock); ++ err = mfs->mfs_bindex; ++ dput(parent); ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfs(struct super_block *sb) ++{ ++ struct au_wbr_mfs *mfs; ++ ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_init(&mfs->mfs_lock); ++ mfs->mfs_jiffy = 0; ++ mfs->mfs_bindex = -EROFS; ++ ++ return 0; ++} ++ ++static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) ++{ ++ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space and then round robin */ ++static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) ++{ ++ int err; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_mfs(dentry, flags); ++ if (err >= 0) { ++ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) ++ err = au_wbr_create_rr(dentry, flags); ++ mutex_unlock(&mfs->mfs_lock); ++ } ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfsrr(struct super_block *sb) ++{ ++ int err; ++ ++ au_wbr_create_init_mfs(sb); /* ignore */ ++ err = au_wbr_create_init_rr(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* top down parent and most free space */ ++static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) ++{ ++ int err, e2; ++ unsigned long long b; ++ aufs_bindex_t bindex, bstart, bend; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ struct au_branch *br; ++ ++ err = au_wbr_create_tdp(dentry, flags); ++ if (unlikely(err < 0)) ++ goto out; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ bend = au_dbtaildir(parent); ++ if (bstart == bend) ++ goto out_parent; /* success */ ++ ++ e2 = au_wbr_create_mfs(dentry, flags); ++ if (e2 < 0) ++ goto out_parent; /* success */ ++ ++ /* when the available size is equal, select upper one */ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, err); ++ b = br->br_wbr->wbr_bytes; ++ AuDbg("b%d, %llu\n", err, b); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || d_is_negative(h_parent)) ++ continue; ++ ++ br = au_sbr(sb, bindex); ++ if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { ++ b = br->br_wbr->wbr_bytes; ++ err = bindex; ++ AuDbg("b%d, %llu\n", err, b); ++ } ++ } ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out_parent: ++ dput(parent); ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * - top down parent ++ * - most free space with parent ++ * - most free space round-robin regardless parent ++ */ ++static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) ++{ ++ int err; ++ unsigned long long watermark; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, err); ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ watermark = mfs->mfsrr_watermark; ++ mutex_unlock(&mfs->mfs_lock); ++ if (br->br_wbr->wbr_bytes < watermark) ++ /* regardless the parent dir */ ++ err = au_wbr_create_mfsrr(dentry, flags); ++ ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies for copyup */ ++ ++/* top down parent */ ++static int au_wbr_copyup_tdp(struct dentry *dentry) ++{ ++ return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); ++} ++ ++/* bottom up parent */ ++static int au_wbr_copyup_bup(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bindex, bstart; ++ struct dentry *parent, *h_parent; ++ struct super_block *sb; ++ ++ err = -EROFS; ++ sb = dentry->d_sb; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || d_is_negative(h_parent)) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) ++ err = au_wbr_bu(sb, bstart - 1); ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* bottom up */ ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) ++{ ++ int err; ++ ++ err = au_wbr_bu(dentry->d_sb, bstart); ++ AuDbg("b%d\n", err); ++ if (err > bstart) ++ err = au_wbr_nonopq(dentry, err); ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_copyup_bu(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ ++ bstart = au_dbstart(dentry); ++ err = au_wbr_do_copyup_bu(dentry, bstart); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { ++ [AuWbrCopyup_TDP] = { ++ .copyup = au_wbr_copyup_tdp ++ }, ++ [AuWbrCopyup_BUP] = { ++ .copyup = au_wbr_copyup_bup ++ }, ++ [AuWbrCopyup_BU] = { ++ .copyup = au_wbr_copyup_bu ++ } ++}; ++ ++struct au_wbr_create_operations au_wbr_create_ops[] = { ++ [AuWbrCreate_TDP] = { ++ .create = au_wbr_create_tdp ++ }, ++ [AuWbrCreate_RR] = { ++ .create = au_wbr_create_rr, ++ .init = au_wbr_create_init_rr ++ }, ++ [AuWbrCreate_MFS] = { ++ .create = au_wbr_create_mfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSV] = { ++ .create = au_wbr_create_mfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSRR] = { ++ .create = au_wbr_create_mfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSRRV] = { ++ .create = au_wbr_create_mfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFS] = { ++ .create = au_wbr_create_pmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFSV] = { ++ .create = au_wbr_create_pmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFSRR] = { ++ .create = au_wbr_create_pmfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFSRRV] = { ++ .create = au_wbr_create_pmfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ } ++}; +diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c +new file mode 100644 +index 0000000..a7f160e +--- /dev/null ++++ b/fs/aufs/whout.c +@@ -0,0 +1,1050 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#include "aufs.h" ++ ++#define WH_MASK S_IRUGO ++ ++/* ++ * If a directory contains this file, then it is opaque. We start with the ++ * .wh. flag so that it is blocked by lookup. ++ */ ++static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, ++ sizeof(AUFS_WH_DIROPQ) - 1); ++ ++/* ++ * generate whiteout name, which is NOT terminated by NULL. ++ * @name: original d_name.name ++ * @len: original d_name.len ++ * @wh: whiteout qstr ++ * returns zero when succeeds, otherwise error. ++ * succeeded value as wh->name should be freed by kfree(). ++ */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) ++{ ++ char *p; ++ ++ if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) ++ return -ENAMETOOLONG; ++ ++ wh->len = name->len + AUFS_WH_PFX_LEN; ++ p = kmalloc(wh->len, GFP_NOFS); ++ wh->name = p; ++ if (p) { ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if the @wh_name exists under @h_parent. ++ * @try_sio specifies the necessary of super-io. ++ */ ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio) ++{ ++ int err; ++ struct dentry *wh_dentry; ++ ++ if (!try_sio) ++ wh_dentry = vfsub_lkup_one(wh_name, h_parent); ++ else ++ wh_dentry = au_sio_lkup_one(wh_name, h_parent); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) { ++ if (err == -ENAMETOOLONG) ++ err = 0; ++ goto out; ++ } ++ ++ err = 0; ++ if (d_is_negative(wh_dentry)) ++ goto out_wh; /* success */ ++ ++ err = 1; ++ if (d_is_reg(wh_dentry)) ++ goto out_wh; /* success */ ++ ++ err = -EIO; ++ AuIOErr("%pd Invalid whiteout entry type 0%o.\n", ++ wh_dentry, d_inode(wh_dentry)->i_mode); ++ ++out_wh: ++ dput(wh_dentry); ++out: ++ return err; ++} ++ ++/* ++ * test if the @h_dentry sets opaque or not. ++ */ ++int au_diropq_test(struct dentry *h_dentry) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = d_inode(h_dentry); ++ err = au_wh_test(h_dentry, &diropq_name, ++ au_test_h_perm_sio(h_dir, MAY_EXEC)); ++ return err; ++} ++ ++/* ++ * returns a negative dentry whose name is unique and temporary. ++ */ ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix) ++{ ++ struct dentry *dentry; ++ int i; ++ char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], ++ *name, *p; ++ /* strict atomic_t is unnecessary here */ ++ static unsigned short cnt; ++ struct qstr qs; ++ ++ BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); ++ ++ name = defname; ++ qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; ++ if (unlikely(prefix->len > DNAME_INLINE_LEN)) { ++ dentry = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(qs.len > NAME_MAX)) ++ goto out; ++ dentry = ERR_PTR(-ENOMEM); ++ name = kmalloc(qs.len + 1, GFP_NOFS); ++ if (unlikely(!name)) ++ goto out; ++ } ++ ++ /* doubly whiteout-ed */ ++ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); ++ p = name + AUFS_WH_PFX_LEN * 2; ++ memcpy(p, prefix->name, prefix->len); ++ p += prefix->len; ++ *p++ = '.'; ++ AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); ++ ++ qs.name = name; ++ for (i = 0; i < 3; i++) { ++ sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); ++ dentry = au_sio_lkup_one(&qs, h_parent); ++ if (IS_ERR(dentry) || d_is_negative(dentry)) ++ goto out_name; ++ dput(dentry); ++ } ++ /* pr_warn("could not get random name\n"); */ ++ dentry = ERR_PTR(-EEXIST); ++ AuDbg("%.*s\n", AuLNPair(&qs)); ++ BUG(); ++ ++out_name: ++ if (name != defname) ++ kfree(name); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ++ * rename the @h_dentry on @br to the whiteouted temporary name. ++ */ ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = au_br_mnt(br) ++ }; ++ struct inode *h_dir, *delegated; ++ struct dentry *h_parent; ++ ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = d_inode(h_parent); ++ IMustLock(h_dir); ++ ++ h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ /* under the same dir, no need to lock_rename() */ ++ delegated = NULL; ++ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated); ++ AuTraceErr(err); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ dput(h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * functions for removing a whiteout ++ */ ++ ++static int do_unlink_wh(struct inode *h_dir, struct path *h_path) ++{ ++ int err, force; ++ struct inode *delegated; ++ ++ /* ++ * forces superio when the dir has a sticky bit. ++ * this may be a violation of unix fs semantics. ++ */ ++ force = (h_dir->i_mode & S_ISVTX) ++ && !uid_eq(current_fsuid(), d_inode(h_path->dentry)->i_uid); ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, h_path, &delegated, force); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ return err; ++} ++ ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry) ++{ ++ int err; ++ ++ err = do_unlink_wh(h_dir, h_path); ++ if (!err && dentry) ++ au_set_dbwh(dentry, -1); ++ ++ return err; ++} ++ ++static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, ++ struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = au_br_mnt(br) ++ }; ++ ++ err = 0; ++ h_path.dentry = vfsub_lkup_one(wh, h_parent); ++ if (IS_ERR(h_path.dentry)) ++ err = PTR_ERR(h_path.dentry); ++ else { ++ if (d_is_reg(h_path.dentry)) ++ err = do_unlink_wh(d_inode(h_parent), &h_path); ++ dput(h_path.dentry); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * initialize/clean whiteout for a branch ++ */ ++ ++static void au_wh_clean(struct inode *h_dir, struct path *whpath, ++ const int isdir) ++{ ++ int err; ++ struct inode *delegated; ++ ++ if (d_is_negative(whpath->dentry)) ++ return; ++ ++ if (isdir) ++ err = vfsub_rmdir(h_dir, whpath); ++ else { ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ } ++ if (unlikely(err)) ++ pr_warn("failed removing %pd (%d), ignored.\n", ++ whpath->dentry, err); ++} ++ ++static int test_linkable(struct dentry *h_root) ++{ ++ struct inode *h_dir = d_inode(h_root); ++ ++ if (h_dir->i_op->link) ++ return 0; ++ ++ pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", ++ h_root, au_sbtype(h_root->d_sb)); ++ return -ENOSYS; ++} ++ ++/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ ++static int au_whdir(struct inode *h_dir, struct path *path) ++{ ++ int err; ++ ++ err = -EEXIST; ++ if (d_is_negative(path->dentry)) { ++ int mode = S_IRWXU; ++ ++ if (au_test_nfs(path->dentry->d_sb)) ++ mode |= S_IXUGO; ++ err = vfsub_mkdir(h_dir, path, mode); ++ } else if (d_is_dir(path->dentry)) ++ err = 0; ++ else ++ pr_err("unknown %pd exists\n", path->dentry); ++ ++ return err; ++} ++ ++struct au_wh_base { ++ const struct qstr *name; ++ struct dentry *dentry; ++}; ++ ++static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++} ++ ++/* ++ * returns tri-state, ++ * minus: error, caller should print the message ++ * zero: succuess ++ * plus: error, caller should NOT print the message ++ */ ++static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = d_inode(h_root); ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * for the moment, aufs supports the branch filesystem which does not support ++ * link(2). testing on FAT which does not support i_op->setattr() fully either, ++ * copyup failed. finally, such filesystem will not be used as the writable ++ * branch. ++ * ++ * returns tri-state, see above. ++ */ ++static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ WbrWhMustWriteLock(wbr); ++ ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ /* ++ * todo: should this create be done in /sbin/mount.aufs helper? ++ */ ++ err = -EEXIST; ++ h_dir = d_inode(h_root); ++ if (d_is_negative(base[AuBrWh_BASE].dentry)) { ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); ++ } else if (d_is_reg(base[AuBrWh_BASE].dentry)) ++ err = 0; ++ else ++ pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); ++ ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * initialize the whiteout base file/dir for @br. ++ */ ++int au_wh_init(struct au_branch *br, struct super_block *sb) ++{ ++ int err, i; ++ const unsigned char do_plink ++ = !!au_opt_test(au_mntflags(sb), PLINK); ++ struct inode *h_dir; ++ struct path path = br->br_path; ++ struct dentry *h_root = path.dentry; ++ struct au_wbr *wbr = br->br_wbr; ++ static const struct qstr base_name[] = { ++ [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, ++ sizeof(AUFS_BASE_NAME) - 1), ++ [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, ++ sizeof(AUFS_PLINKDIR_NAME) - 1), ++ [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, ++ sizeof(AUFS_ORPHDIR_NAME) - 1) ++ }; ++ struct au_wh_base base[] = { ++ [AuBrWh_BASE] = { ++ .name = base_name + AuBrWh_BASE, ++ .dentry = NULL ++ }, ++ [AuBrWh_PLINK] = { ++ .name = base_name + AuBrWh_PLINK, ++ .dentry = NULL ++ }, ++ [AuBrWh_ORPH] = { ++ .name = base_name + AuBrWh_ORPH, ++ .dentry = NULL ++ } ++ }; ++ ++ if (wbr) ++ WbrWhMustWriteLock(wbr); ++ ++ for (i = 0; i < AuBrWh_Last; i++) { ++ /* doubly whiteouted */ ++ struct dentry *d; ++ ++ d = au_wh_lkup(h_root, (void *)base[i].name, br); ++ err = PTR_ERR(d); ++ if (IS_ERR(d)) ++ goto out; ++ ++ base[i].dentry = d; ++ AuDebugOn(wbr ++ && wbr->wbr_wh[i] ++ && wbr->wbr_wh[i] != base[i].dentry); ++ } ++ ++ if (wbr) ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ ++ err = 0; ++ if (!au_br_writable(br->br_perm)) { ++ h_dir = d_inode(h_root); ++ au_wh_init_ro(h_dir, base, &path); ++ } else if (!au_br_wh_linkable(br->br_perm)) { ++ err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ } else { ++ err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ } ++ goto out; /* success */ ++ ++out_err: ++ pr_err("an error(%d) on the writable branch %pd(%s)\n", ++ err, h_root, au_sbtype(h_root->d_sb)); ++out: ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(base[i].dentry); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * whiteouts are all hard-linked usually. ++ * when its link count reaches a ceiling, we create a new whiteout base ++ * asynchronously. ++ */ ++ ++struct reinit_br_wh { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void reinit_br_wh(void *arg) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct path h_path; ++ struct reinit_br_wh *a = arg; ++ struct au_wbr *wbr; ++ struct inode *dir, *delegated; ++ struct dentry *h_root; ++ struct au_hinode *hdir; ++ ++ err = 0; ++ wbr = a->br->br_wbr; ++ /* big aufs lock */ ++ si_noflush_write_lock(a->sb); ++ if (!au_br_writable(a->br->br_perm)) ++ goto out; ++ bindex = au_br_index(a->sb, a->br->br_id); ++ if (unlikely(bindex < 0)) ++ goto out; ++ ++ di_read_lock_parent(a->sb->s_root, AuLock_IR); ++ dir = d_inode(a->sb->s_root); ++ hdir = au_hi(dir, bindex); ++ h_root = au_h_dptr(a->sb->s_root, bindex); ++ AuDebugOn(h_root != au_br_dentry(a->br)); ++ ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ wbr_wh_write_lock(wbr); ++ err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, ++ h_root, a->br); ++ if (!err) { ++ h_path.dentry = wbr->wbr_whbase; ++ h_path.mnt = au_br_mnt(a->br); ++ delegated = NULL; ++ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, ++ /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ } else { ++ pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); ++ err = 0; ++ } ++ dput(wbr->wbr_whbase); ++ wbr->wbr_whbase = NULL; ++ if (!err) ++ err = au_wh_init(a->br, a->sb); ++ wbr_wh_write_unlock(wbr); ++ au_hn_imtx_unlock(hdir); ++ di_read_unlock(a->sb->s_root, AuLock_IR); ++ if (!err) ++ au_fhsm_wrote(a->sb, bindex, /*force*/0); ++ ++out: ++ if (wbr) ++ atomic_dec(&wbr->wbr_wh_running); ++ atomic_dec(&a->br->br_count); ++ si_write_unlock(a->sb); ++ au_nwt_done(&au_sbi(a->sb)->si_nowait); ++ kfree(arg); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) ++{ ++ int do_dec, wkq_err; ++ struct reinit_br_wh *arg; ++ ++ do_dec = 1; ++ if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) ++ goto out; ++ ++ /* ignore ENOMEM */ ++ arg = kmalloc(sizeof(*arg), GFP_NOFS); ++ if (arg) { ++ /* ++ * dec(wh_running), kfree(arg) and dec(br_count) ++ * in reinit function ++ */ ++ arg->sb = sb; ++ arg->br = br; ++ atomic_inc(&br->br_count); ++ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); ++ if (unlikely(wkq_err)) { ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++ atomic_dec(&br->br_count); ++ kfree(arg); ++ } ++ do_dec = 0; ++ } ++ ++out: ++ if (do_dec) ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create the whiteout @wh. ++ */ ++static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, ++ struct dentry *wh) ++{ ++ int err; ++ struct path h_path = { ++ .dentry = wh ++ }; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ struct inode *h_dir, *delegated; ++ ++ h_parent = wh->d_parent; /* dir inode is locked */ ++ h_dir = d_inode(h_parent); ++ IMustLock(h_dir); ++ ++ br = au_sbr(sb, bindex); ++ h_path.mnt = au_br_mnt(br); ++ wbr = br->br_wbr; ++ wbr_wh_read_lock(wbr); ++ if (wbr->wbr_whbase) { ++ delegated = NULL; ++ err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ if (!err || err != -EMLINK) ++ goto out; ++ ++ /* link count full. re-initialize br_whbase. */ ++ kick_reinit_br_wh(sb, br); ++ } ++ ++ /* return this error in this context */ ++ err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); ++ if (!err) ++ au_fhsm_wrote(sb, bindex, /*force*/0); ++ ++out: ++ wbr_wh_read_unlock(wbr); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create or remove the diropq. ++ */ ++static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *opq_dentry, *h_dentry; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ h_dentry = au_h_dptr(dentry, bindex); ++ opq_dentry = vfsub_lkup_one(&diropq_name, h_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ ++ if (au_ftest_diropq(flags, CREATE)) { ++ err = link_or_create_wh(sb, bindex, opq_dentry); ++ if (!err) { ++ au_set_dbdiropq(dentry, bindex); ++ goto out; /* success */ ++ } ++ } else { ++ struct path tmp = { ++ .dentry = opq_dentry, ++ .mnt = au_br_mnt(br) ++ }; ++ err = do_unlink_wh(au_h_iptr(d_inode(dentry), bindex), &tmp); ++ if (!err) ++ au_set_dbdiropq(dentry, -1); ++ } ++ dput(opq_dentry); ++ opq_dentry = ERR_PTR(err); ++ ++out: ++ return opq_dentry; ++} ++ ++struct do_diropq_args { ++ struct dentry **errp; ++ struct dentry *dentry; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++}; ++ ++static void call_do_diropq(void *args) ++{ ++ struct do_diropq_args *a = args; ++ *a->errp = do_diropq(a->dentry, a->bindex, a->flags); ++} ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *diropq, *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!au_test_h_perm_sio(d_inode(h_dentry), MAY_EXEC | MAY_WRITE)) ++ diropq = do_diropq(dentry, bindex, flags); ++ else { ++ int wkq_err; ++ struct do_diropq_args args = { ++ .errp = &diropq, ++ .dentry = dentry, ++ .bindex = bindex, ++ .flags = flags ++ }; ++ ++ wkq_err = au_wkq_wait(call_do_diropq, &args); ++ if (unlikely(wkq_err)) ++ diropq = ERR_PTR(wkq_err); ++ } ++ ++ return diropq; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * lookup whiteout dentry. ++ * @h_parent: lower parent dentry which must exist and be locked ++ * @base_name: name of dentry which will be whiteouted ++ * returns dentry for whiteout. ++ */ ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br) ++{ ++ int err; ++ struct qstr wh_name; ++ struct dentry *wh_dentry; ++ ++ err = au_wh_name_alloc(&wh_name, base_name); ++ wh_dentry = ERR_PTR(err); ++ if (!err) { ++ wh_dentry = vfsub_lkup_one(&wh_name, h_parent); ++ kfree(wh_name.name); ++ } ++ return wh_dentry; ++} ++ ++/* ++ * link/create a whiteout for @dentry on @bindex. ++ */ ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ int err; ++ ++ sb = dentry->d_sb; ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); ++ if (!IS_ERR(wh_dentry) && d_is_negative(wh_dentry)) { ++ err = link_or_create_wh(sb, bindex, wh_dentry); ++ if (!err) { ++ au_set_dbwh(dentry, bindex); ++ au_fhsm_wrote(sb, bindex, /*force*/0); ++ } else { ++ dput(wh_dentry); ++ wh_dentry = ERR_PTR(err); ++ } ++ } ++ ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Delete all whiteouts in this directory on branch bindex. */ ++static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err; ++ unsigned long ul, n; ++ struct qstr wh_name; ++ char *p; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ struct au_vdir_destr *str; ++ ++ err = -ENOMEM; ++ p = (void *)__get_free_page(GFP_NOFS); ++ wh_name.name = p; ++ if (unlikely(!wh_name.name)) ++ goto out; ++ ++ err = 0; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; !err && ul < n; ul++, head++) { ++ hlist_for_each_entry(pos, head, wh_hash) { ++ if (pos->wh_bindex != bindex) ++ continue; ++ ++ str = &pos->wh_str; ++ if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { ++ memcpy(p, str->name, str->len); ++ wh_name.len = AUFS_WH_PFX_LEN + str->len; ++ err = unlink_wh_name(h_dentry, &wh_name, br); ++ if (!err) ++ continue; ++ break; ++ } ++ AuIOErr("whiteout name too long %.*s\n", ++ str->len, str->name); ++ err = -EIO; ++ break; ++ } ++ } ++ free_page((unsigned long)wh_name.name); ++ ++out: ++ return err; ++} ++ ++struct del_wh_children_args { ++ int *errp; ++ struct dentry *h_dentry; ++ struct au_nhash *whlist; ++ aufs_bindex_t bindex; ++ struct au_branch *br; ++}; ++ ++static void call_del_wh_children(void *args) ++{ ++ struct del_wh_children_args *a = args; ++ *a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) ++{ ++ struct au_whtmp_rmdir *whtmp; ++ int err; ++ unsigned int rdhash; ++ ++ SiMustAnyLock(sb); ++ ++ whtmp = kmalloc(sizeof(*whtmp), gfp); ++ if (unlikely(!whtmp)) { ++ whtmp = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ whtmp->dir = NULL; ++ whtmp->br = NULL; ++ whtmp->wh_dentry = NULL; ++ /* no estimation for dir size */ ++ rdhash = au_sbi(sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = AUFS_RDHASH_DEF; ++ err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); ++ if (unlikely(err)) { ++ kfree(whtmp); ++ whtmp = ERR_PTR(err); ++ } ++ ++out: ++ return whtmp; ++} ++ ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) ++{ ++ if (whtmp->br) ++ atomic_dec(&whtmp->br->br_count); ++ dput(whtmp->wh_dentry); ++ iput(whtmp->dir); ++ au_nhash_wh_free(&whtmp->whlist); ++ kfree(whtmp); ++} ++ ++/* ++ * rmdir the whiteouted temporary named dir @h_dentry. ++ * @whlist: whiteouted children. ++ */ ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist) ++{ ++ int err; ++ unsigned int h_nlink; ++ struct path h_tmp; ++ struct inode *wh_inode, *h_dir; ++ struct au_branch *br; ++ ++ h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ ++ IMustLock(h_dir); ++ ++ br = au_sbr(dir->i_sb, bindex); ++ wh_inode = d_inode(wh_dentry); ++ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); ++ ++ /* ++ * someone else might change some whiteouts while we were sleeping. ++ * it means this whlist may have an obsoleted entry. ++ */ ++ if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) ++ err = del_wh_children(wh_dentry, whlist, bindex, br); ++ else { ++ int wkq_err; ++ struct del_wh_children_args args = { ++ .errp = &err, ++ .h_dentry = wh_dentry, ++ .whlist = whlist, ++ .bindex = bindex, ++ .br = br ++ }; ++ ++ wkq_err = au_wkq_wait(call_del_wh_children, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ mutex_unlock(&wh_inode->i_mutex); ++ ++ if (!err) { ++ h_tmp.dentry = wh_dentry; ++ h_tmp.mnt = au_br_mnt(br); ++ h_nlink = h_dir->i_nlink; ++ err = vfsub_rmdir(h_dir, &h_tmp); ++ /* some fs doesn't change the parent nlink in some cases */ ++ h_nlink -= h_dir->i_nlink; ++ } ++ ++ if (!err) { ++ if (au_ibstart(dir) == bindex) { ++ /* todo: dir->i_mutex is necessary */ ++ au_cpup_attr_timesizes(dir); ++ if (h_nlink) ++ vfsub_drop_nlink(dir); ++ } ++ return 0; /* success */ ++ } ++ ++ pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); ++ return err; ++} ++ ++static void call_rmdir_whtmp(void *args) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct au_whtmp_rmdir *a = args; ++ struct super_block *sb; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ struct au_hinode *hdir; ++ ++ /* rmdir by nfsd may cause deadlock with this i_mutex */ ++ /* mutex_lock(&a->dir->i_mutex); */ ++ err = -EROFS; ++ sb = a->dir->i_sb; ++ si_read_lock(sb, !AuLock_FLUSH); ++ if (!au_br_writable(a->br->br_perm)) ++ goto out; ++ bindex = au_br_index(sb, a->br->br_id); ++ if (unlikely(bindex < 0)) ++ goto out; ++ ++ err = -EIO; ++ ii_write_lock_parent(a->dir); ++ h_parent = dget_parent(a->wh_dentry); ++ h_dir = d_inode(h_parent); ++ hdir = au_hi(a->dir, bindex); ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out_mnt; ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, ++ a->br); ++ if (!err) ++ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); ++ au_hn_imtx_unlock(hdir); ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++ ++out_mnt: ++ dput(h_parent); ++ ii_write_unlock(a->dir); ++out: ++ /* mutex_unlock(&a->dir->i_mutex); */ ++ au_whtmp_rmdir_free(a); ++ si_read_unlock(sb); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args) ++{ ++ int wkq_err; ++ struct super_block *sb; ++ ++ IMustLock(dir); ++ ++ /* all post-process will be done in do_rmdir_whtmp(). */ ++ sb = dir->i_sb; ++ args->dir = au_igrab(dir); ++ args->br = au_sbr(sb, bindex); ++ atomic_inc(&args->br->br_count); ++ args->wh_dentry = dget(wh_dentry); ++ wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); ++ if (unlikely(wkq_err)) { ++ pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); ++ au_whtmp_rmdir_free(args); ++ } ++} +diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h +new file mode 100644 +index 0000000..b39acea +--- /dev/null ++++ b/fs/aufs/whout.h +@@ -0,0 +1,72 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#ifndef __AUFS_WHOUT_H__ ++#define __AUFS_WHOUT_H__ ++ ++#ifdef __KERNEL__ ++ ++#include "dir.h" ++ ++/* whout.c */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio); ++int au_diropq_test(struct dentry *h_dentry); ++struct au_branch; ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix); ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry); ++int au_wh_init(struct au_branch *br, struct super_block *sb); ++ ++/* diropq flags */ ++#define AuDiropq_CREATE 1 ++#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) ++#define au_fset_diropq(flags, name) \ ++ do { (flags) |= AuDiropq_##name; } while (0) ++#define au_fclr_diropq(flags, name) \ ++ do { (flags) &= ~AuDiropq_##name; } while (0) ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags); ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br); ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent); ++ ++/* real rmdir for the whiteout-ed dir */ ++struct au_whtmp_rmdir { ++ struct inode *dir; ++ struct au_branch *br; ++ struct dentry *wh_dentry; ++ struct au_nhash whlist; ++}; ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist); ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_diropq_create(struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); ++} ++ ++static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WHOUT_H__ */ +diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c +new file mode 100644 +index 0000000..08870ec +--- /dev/null ++++ b/fs/aufs/wkq.c +@@ -0,0 +1,200 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new dredential scheme ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* internal workqueue named AUFS_WKQ_NAME */ ++ ++static struct workqueue_struct *au_wkq; ++ ++struct au_wkinfo { ++ struct work_struct wk; ++ struct kobject *kobj; ++ ++ unsigned int flags; /* see wkq.h */ ++ ++ au_wkq_func_t func; ++ void *args; ++ ++ struct completion *comp; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void wkq_func(struct work_struct *wk) ++{ ++ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); ++ ++ AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); ++ AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); ++ ++ wkinfo->func(wkinfo->args); ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) ++ complete(wkinfo->comp); ++ else { ++ kobject_put(wkinfo->kobj); ++ module_put(THIS_MODULE); /* todo: ?? */ ++ kfree(wkinfo); ++ } ++} ++ ++/* ++ * Since struct completion is large, try allocating it dynamically. ++ */ ++#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */ ++#define AuWkqCompDeclare(name) struct completion *comp = NULL ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ *comp = kmalloc(sizeof(**comp), GFP_NOFS); ++ if (*comp) { ++ init_completion(*comp); ++ wkinfo->comp = *comp; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++static void au_wkq_comp_free(struct completion *comp) ++{ ++ kfree(comp); ++} ++ ++#else ++ ++/* no braces */ ++#define AuWkqCompDeclare(name) \ ++ DECLARE_COMPLETION_ONSTACK(_ ## name); \ ++ struct completion *comp = &_ ## name ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ wkinfo->comp = *comp; ++ return 0; ++} ++ ++static void au_wkq_comp_free(struct completion *comp __maybe_unused) ++{ ++ /* empty */ ++} ++#endif /* 4KSTACKS */ ++ ++static void au_wkq_run(struct au_wkinfo *wkinfo) ++{ ++ if (au_ftest_wkq(wkinfo->flags, NEST)) { ++ if (au_wkq_test()) { ++ AuWarn1("wkq from wkq, unless silly-rename on NFS," ++ " due to a dead dir by UDBA?\n"); ++ AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); ++ } ++ } else ++ au_dbg_verify_kthread(); ++ ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) { ++ INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); ++ queue_work(au_wkq, &wkinfo->wk); ++ } else { ++ INIT_WORK(&wkinfo->wk, wkq_func); ++ schedule_work(&wkinfo->wk); ++ } ++} ++ ++/* ++ * Be careful. It is easy to make deadlock happen. ++ * processA: lock, wkq and wait ++ * processB: wkq and wait, lock in wkq ++ * --> deadlock ++ */ ++int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) ++{ ++ int err; ++ AuWkqCompDeclare(comp); ++ struct au_wkinfo wkinfo = { ++ .flags = flags, ++ .func = func, ++ .args = args ++ }; ++ ++ err = au_wkq_comp_alloc(&wkinfo, &comp); ++ if (!err) { ++ au_wkq_run(&wkinfo); ++ /* no timeout, no interrupt */ ++ wait_for_completion(wkinfo.comp); ++ au_wkq_comp_free(comp); ++ destroy_work_on_stack(&wkinfo.wk); ++ } ++ ++ return err; ++ ++} ++ ++/* ++ * Note: dget/dput() in func for aufs dentries are not supported. It will be a ++ * problem in a concurrent umounting. ++ */ ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, ++ unsigned int flags) ++{ ++ int err; ++ struct au_wkinfo *wkinfo; ++ ++ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); ++ ++ /* ++ * wkq_func() must free this wkinfo. ++ * it highly depends upon the implementation of workqueue. ++ */ ++ err = 0; ++ wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); ++ if (wkinfo) { ++ wkinfo->kobj = &au_sbi(sb)->si_kobj; ++ wkinfo->flags = flags & ~AuWkq_WAIT; ++ wkinfo->func = func; ++ wkinfo->args = args; ++ wkinfo->comp = NULL; ++ kobject_get(wkinfo->kobj); ++ __module_get(THIS_MODULE); /* todo: ?? */ ++ ++ au_wkq_run(wkinfo); ++ } else { ++ err = -ENOMEM; ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_nwt_init(struct au_nowait_tasks *nwt) ++{ ++ atomic_set(&nwt->nw_len, 0); ++ /* smp_mb(); */ /* atomic_set */ ++ init_waitqueue_head(&nwt->nw_wq); ++} ++ ++void au_wkq_fin(void) ++{ ++ destroy_workqueue(au_wkq); ++} ++ ++int __init au_wkq_init(void) ++{ ++ int err; ++ ++ err = 0; ++ au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); ++ if (IS_ERR(au_wkq)) ++ err = PTR_ERR(au_wkq); ++ else if (!au_wkq) ++ err = -ENOMEM; ++ ++ return err; ++} +diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h +new file mode 100644 +index 0000000..8f01343 +--- /dev/null ++++ b/fs/aufs/wkq.h +@@ -0,0 +1,78 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new credentials management scheme ++ */ ++ ++#ifndef __AUFS_WKQ_H__ ++#define __AUFS_WKQ_H__ ++ ++#ifdef __KERNEL__ ++ ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue ++ */ ++struct au_nowait_tasks { ++ atomic_t nw_len; ++ wait_queue_head_t nw_wq; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++typedef void (*au_wkq_func_t)(void *args); ++ ++/* wkq flags */ ++#define AuWkq_WAIT 1 ++#define AuWkq_NEST (1 << 1) ++#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) ++#define au_fset_wkq(flags, name) \ ++ do { (flags) |= AuWkq_##name; } while (0) ++#define au_fclr_wkq(flags, name) \ ++ do { (flags) &= ~AuWkq_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuWkq_NEST ++#define AuWkq_NEST 0 ++#endif ++ ++/* wkq.c */ ++int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, ++ unsigned int flags); ++void au_nwt_init(struct au_nowait_tasks *nwt); ++int __init au_wkq_init(void); ++void au_wkq_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int au_wkq_test(void) ++{ ++ return current->flags & PF_WQ_WORKER; ++} ++ ++static inline int au_wkq_wait(au_wkq_func_t func, void *args) ++{ ++ return au_wkq_do_wait(AuWkq_WAIT, func, args); ++} ++ ++static inline void au_nwt_done(struct au_nowait_tasks *nwt) ++{ ++ if (atomic_dec_and_test(&nwt->nw_len)) ++ wake_up_all(&nwt->nw_wq); ++} ++ ++static inline int au_nwt_flush(struct au_nowait_tasks *nwt) ++{ ++ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); ++ return 0; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WKQ_H__ */ +diff --git a/fs/aufs/xattr.c b/fs/aufs/xattr.c +new file mode 100644 +index 0000000..c1695b3 +--- /dev/null ++++ b/fs/aufs/xattr.c +@@ -0,0 +1,331 @@ ++/* ++ * Copyright (C) 2014-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * handling xattr functions ++ */ ++ ++#include ++#include "aufs.h" ++ ++static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) ++{ ++ if (!ignore_flags) ++ goto out; ++ switch (err) { ++ case -ENOMEM: ++ case -EDQUOT: ++ goto out; ++ } ++ ++ if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { ++ err = 0; ++ goto out; ++ } ++ ++#define cmp(brattr, prefix) do { \ ++ if (!strncmp(name, XATTR_##prefix##_PREFIX, \ ++ XATTR_##prefix##_PREFIX_LEN)) { \ ++ if (ignore_flags & AuBrAttr_ICEX_##brattr) \ ++ err = 0; \ ++ goto out; \ ++ } \ ++ } while (0) ++ ++ cmp(SEC, SECURITY); ++ cmp(SYS, SYSTEM); ++ cmp(TR, TRUSTED); ++ cmp(USR, USER); ++#undef cmp ++ ++ if (ignore_flags & AuBrAttr_ICEX_OTH) ++ err = 0; ++ ++out: ++ return err; ++} ++ ++static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; ++ ++static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, ++ char *name, char **buf, unsigned int ignore_flags, ++ unsigned int verbose) ++{ ++ int err; ++ ssize_t ssz; ++ struct inode *h_idst; ++ ++ ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); ++ err = ssz; ++ if (unlikely(err <= 0)) { ++ if (err == -ENODATA ++ || (err == -EOPNOTSUPP ++ && ((ignore_flags & au_xattr_out_of_list) ++ || (au_test_nfs_noacl(d_inode(h_src)) ++ && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) ++ || !strcmp(name, ++ XATTR_NAME_POSIX_ACL_DEFAULT)))) ++ )) ++ err = 0; ++ if (err && (verbose || au_debug_test())) ++ pr_err("%s, err %d\n", name, err); ++ goto out; ++ } ++ ++ /* unlock it temporary */ ++ h_idst = d_inode(h_dst); ++ mutex_unlock(&h_idst->i_mutex); ++ err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); ++ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ if (unlikely(err)) { ++ if (verbose || au_debug_test()) ++ pr_err("%s, err %d\n", name, err); ++ err = au_xattr_ignore(err, name, ignore_flags); ++ } ++ ++out: ++ return err; ++} ++ ++int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, ++ unsigned int verbose) ++{ ++ int err, unlocked, acl_access, acl_default; ++ ssize_t ssz; ++ struct inode *h_isrc, *h_idst; ++ char *value, *p, *o, *e; ++ ++ /* try stopping to update the source inode while we are referencing */ ++ /* there should not be the parent-child relationship between them */ ++ h_isrc = d_inode(h_src); ++ h_idst = d_inode(h_dst); ++ mutex_unlock(&h_idst->i_mutex); ++ mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); ++ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ unlocked = 0; ++ ++ /* some filesystems don't list POSIX ACL, for example tmpfs */ ++ ssz = vfs_listxattr(h_src, NULL, 0); ++ err = ssz; ++ if (unlikely(err < 0)) { ++ AuTraceErr(err); ++ if (err == -ENODATA ++ || err == -EOPNOTSUPP) ++ err = 0; /* ignore */ ++ goto out; ++ } ++ ++ err = 0; ++ p = NULL; ++ o = NULL; ++ if (ssz) { ++ err = -ENOMEM; ++ p = kmalloc(ssz, GFP_NOFS); ++ o = p; ++ if (unlikely(!p)) ++ goto out; ++ err = vfs_listxattr(h_src, p, ssz); ++ } ++ mutex_unlock(&h_isrc->i_mutex); ++ unlocked = 1; ++ AuDbg("err %d, ssz %zd\n", err, ssz); ++ if (unlikely(err < 0)) ++ goto out_free; ++ ++ err = 0; ++ e = p + ssz; ++ value = NULL; ++ acl_access = 0; ++ acl_default = 0; ++ while (!err && p < e) { ++ acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, ++ sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); ++ acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, ++ sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) ++ - 1); ++ err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, ++ verbose); ++ p += strlen(p) + 1; ++ } ++ AuTraceErr(err); ++ ignore_flags |= au_xattr_out_of_list; ++ if (!err && !acl_access) { ++ err = au_do_cpup_xattr(h_dst, h_src, ++ XATTR_NAME_POSIX_ACL_ACCESS, &value, ++ ignore_flags, verbose); ++ AuTraceErr(err); ++ } ++ if (!err && !acl_default) { ++ err = au_do_cpup_xattr(h_dst, h_src, ++ XATTR_NAME_POSIX_ACL_DEFAULT, &value, ++ ignore_flags, verbose); ++ AuTraceErr(err); ++ } ++ ++ kfree(value); ++ ++out_free: ++ kfree(o); ++out: ++ if (!unlocked) ++ mutex_unlock(&h_isrc->i_mutex); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { ++ AU_XATTR_LIST, ++ AU_XATTR_GET ++}; ++ ++struct au_lgxattr { ++ int type; ++ union { ++ struct { ++ char *list; ++ size_t size; ++ } list; ++ struct { ++ const char *name; ++ void *value; ++ size_t size; ++ } get; ++ } u; ++}; ++ ++static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg) ++{ ++ ssize_t err; ++ struct path h_path; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ err = au_h_path_getattr(dentry, /*force*/1, &h_path); ++ if (unlikely(err)) ++ goto out_si; ++ if (unlikely(!h_path.dentry)) ++ /* illegally overlapped or something */ ++ goto out_di; /* pretending success */ ++ ++ /* always topmost entry only */ ++ switch (arg->type) { ++ case AU_XATTR_LIST: ++ err = vfs_listxattr(h_path.dentry, ++ arg->u.list.list, arg->u.list.size); ++ break; ++ case AU_XATTR_GET: ++ err = vfs_getxattr(h_path.dentry, ++ arg->u.get.name, arg->u.get.value, ++ arg->u.get.size); ++ break; ++ } ++ ++out_di: ++ di_read_unlock(dentry, AuLock_IR); ++out_si: ++ si_read_unlock(sb); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) ++{ ++ struct au_lgxattr arg = { ++ .type = AU_XATTR_LIST, ++ .u.list = { ++ .list = list, ++ .size = size ++ }, ++ }; ++ ++ return au_lgxattr(dentry, &arg); ++} ++ ++ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, ++ size_t size) ++{ ++ struct au_lgxattr arg = { ++ .type = AU_XATTR_GET, ++ .u.get = { ++ .name = name, ++ .value = value, ++ .size = size ++ }, ++ }; ++ ++ return au_lgxattr(dentry, &arg); ++} ++ ++int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, ++ size_t size, int flags) ++{ ++ struct au_srxattr arg = { ++ .type = AU_XATTR_SET, ++ .u.set = { ++ .name = name, ++ .value = value, ++ .size = size, ++ .flags = flags ++ }, ++ }; ++ ++ return au_srxattr(dentry, &arg); ++} ++ ++int aufs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct au_srxattr arg = { ++ .type = AU_XATTR_REMOVE, ++ .u.remove = { ++ .name = name ++ }, ++ }; ++ ++ return au_srxattr(dentry, &arg); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#if 0 ++static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size, ++ const char *name, size_t name_len, int type) ++{ ++ return aufs_listxattr(dentry, list, list_size); ++} ++ ++static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer, ++ size_t size, int type) ++{ ++ return aufs_getxattr(dentry, name, buffer, size); ++} ++ ++static int au_xattr_set(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags, int type) ++{ ++ return aufs_setxattr(dentry, name, value, size, flags); ++} ++ ++static const struct xattr_handler au_xattr_handler = { ++ /* no prefix, no flags */ ++ .list = au_xattr_list, ++ .get = au_xattr_get, ++ .set = au_xattr_set ++ /* why no remove? */ ++}; ++ ++static const struct xattr_handler *au_xattr_handlers[] = { ++ &au_xattr_handler ++}; ++ ++void au_xattr_init(struct super_block *sb) ++{ ++ /* sb->s_xattr = au_xattr_handlers; */ ++} ++#endif +diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c +new file mode 100644 +index 0000000..8a2ddac +--- /dev/null ++++ b/fs/aufs/xino.c +@@ -0,0 +1,1284 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * external inode number translation table and bitmap ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++/* todo: unnecessary to support mmap_sem since kernel-space? */ ++ssize_t xino_fread(vfs_readf_t func, struct file *file, void *kbuf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, buf.u, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_access(file->f_path.dentry); ++#endif ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static ssize_t do_xino_fwrite(vfs_writef_t func, struct file *file, void *kbuf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ const char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, buf.u, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_modify(file->f_path.dentry); ++#endif ++ ++ return err; ++} ++ ++struct do_xino_fwrite_args { ++ ssize_t *errp; ++ vfs_writef_t func; ++ struct file *file; ++ void *buf; ++ size_t size; ++ loff_t *pos; ++}; ++ ++static void call_do_xino_fwrite(void *args) ++{ ++ struct do_xino_fwrite_args *a = args; ++ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); ++} ++ ++ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ ++ /* todo: signal block and no wkq? */ ++ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { ++ lockdep_off(); ++ err = do_xino_fwrite(func, file, buf, size, pos); ++ lockdep_on(); ++ } else { ++ /* ++ * it breaks RLIMIT_FSIZE and normal user's limit, ++ * users should care about quota and real 'filesystem full.' ++ */ ++ int wkq_err; ++ struct do_xino_fwrite_args args = { ++ .errp = &err, ++ .func = func, ++ .file = file, ++ .buf = buf, ++ .size = size, ++ .pos = pos ++ }; ++ ++ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a new xinofile at the same place/path as @base_file. ++ */ ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src) ++{ ++ struct file *file; ++ struct dentry *base, *parent; ++ struct inode *dir, *delegated; ++ struct qstr *name; ++ struct path path; ++ int err; ++ ++ base = base_file->f_path.dentry; ++ parent = base->d_parent; /* dir inode is locked */ ++ dir = d_inode(parent); ++ IMustLock(dir); ++ ++ file = ERR_PTR(-EINVAL); ++ name = &base->d_name; ++ path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); ++ if (IS_ERR(path.dentry)) { ++ file = (void *)path.dentry; ++ pr_err("%pd lookup err %ld\n", ++ base, PTR_ERR(path.dentry)); ++ goto out; ++ } ++ ++ /* no need to mnt_want_write() since we call dentry_open() later */ ++ err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); ++ if (unlikely(err)) { ++ file = ERR_PTR(err); ++ pr_err("%pd create err %d\n", base, err); ++ goto out_dput; ++ } ++ ++ path.mnt = base_file->f_path.mnt; ++ file = vfsub_dentry_open(&path, ++ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE ++ /* | __FMODE_NONOTIFY */); ++ if (IS_ERR(file)) { ++ pr_err("%pd open err %ld\n", base, PTR_ERR(file)); ++ goto out_dput; ++ } ++ ++ delegated = NULL; ++ err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) { ++ pr_err("%pd unlink err %d\n", base, err); ++ goto out_fput; ++ } ++ ++ if (copy_src) { ++ /* no one can touch copy_src xino */ ++ err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); ++ if (unlikely(err)) { ++ pr_err("%pd copy err %d\n", base, err); ++ goto out_fput; ++ } ++ } ++ goto out_dput; /* success */ ++ ++out_fput: ++ fput(file); ++ file = ERR_PTR(err); ++out_dput: ++ dput(path.dentry); ++out: ++ return file; ++} ++ ++struct au_xino_lock_dir { ++ struct au_hinode *hdir; ++ struct dentry *parent; ++ struct mutex *mtx; ++}; ++ ++static void au_xino_lock_dir(struct super_block *sb, struct file *xino, ++ struct au_xino_lock_dir *ldir) ++{ ++ aufs_bindex_t brid, bindex; ++ ++ ldir->hdir = NULL; ++ bindex = -1; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) ++ bindex = au_br_index(sb, brid); ++ if (bindex >= 0) { ++ ldir->hdir = au_hi(d_inode(sb->s_root), bindex); ++ au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); ++ } else { ++ ldir->parent = dget_parent(xino->f_path.dentry); ++ ldir->mtx = &d_inode(ldir->parent)->i_mutex; ++ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); ++ } ++} ++ ++static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) ++{ ++ if (ldir->hdir) ++ au_hn_imtx_unlock(ldir->hdir); ++ else { ++ mutex_unlock(ldir->mtx); ++ dput(ldir->parent); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate xino files asynchronously */ ++ ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err; ++ unsigned long jiffy; ++ blkcnt_t blocks; ++ aufs_bindex_t bi, bend; ++ struct kstatfs *st; ++ struct au_branch *br; ++ struct file *new_xino, *file; ++ struct super_block *h_sb; ++ struct au_xino_lock_dir ldir; ++ ++ err = -ENOMEM; ++ st = kzalloc(sizeof(*st), GFP_NOFS); ++ if (unlikely(!st)) ++ goto out; ++ ++ err = -EINVAL; ++ bend = au_sbend(sb); ++ if (unlikely(bindex < 0 || bend < bindex)) ++ goto out_st; ++ br = au_sbr(sb, bindex); ++ file = br->br_xino.xi_file; ++ if (!file) ++ goto out_st; ++ ++ err = vfs_statfs(&file->f_path, st); ++ if (unlikely(err)) ++ AuErr1("statfs err %d, ignored\n", err); ++ jiffy = jiffies; ++ blocks = file_inode(file)->i_blocks; ++ pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", ++ bindex, (u64)blocks, st->f_bfree, st->f_blocks); ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ new_xino = au_xino_create2(file, file); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(new_xino); ++ if (IS_ERR(new_xino)) { ++ pr_err("err %d, ignored\n", err); ++ goto out_st; ++ } ++ err = 0; ++ fput(file); ++ br->br_xino.xi_file = new_xino; ++ ++ h_sb = au_br_sb(br); ++ for (bi = 0; bi <= bend; bi++) { ++ if (unlikely(bi == bindex)) ++ continue; ++ br = au_sbr(sb, bi); ++ if (au_br_sb(br) != h_sb) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = new_xino; ++ get_file(new_xino); ++ } ++ ++ err = vfs_statfs(&new_xino->f_path, st); ++ if (!err) { ++ pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", ++ bindex, (u64)file_inode(new_xino)->i_blocks, ++ st->f_bfree, st->f_blocks); ++ if (file_inode(new_xino)->i_blocks < blocks) ++ au_sbi(sb)->si_xino_jiffy = jiffy; ++ } else ++ AuErr1("statfs err %d, ignored\n", err); ++ ++out_st: ++ kfree(st); ++out: ++ return err; ++} ++ ++struct xino_do_trunc_args { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void xino_do_trunc(void *_args) ++{ ++ struct xino_do_trunc_args *args = _args; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct inode *dir; ++ int err; ++ aufs_bindex_t bindex; ++ ++ err = 0; ++ sb = args->sb; ++ dir = d_inode(sb->s_root); ++ br = args->br; ++ ++ si_noflush_write_lock(sb); ++ ii_read_lock_parent(dir); ++ bindex = au_br_index(sb, br->br_id); ++ err = au_xino_trunc(sb, bindex); ++ ii_read_unlock(dir); ++ if (unlikely(err)) ++ pr_warn("err b%d, (%d)\n", bindex, err); ++ atomic_dec(&br->br_xino_running); ++ atomic_dec(&br->br_count); ++ si_write_unlock(sb); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ kfree(args); ++} ++ ++static int xino_trunc_test(struct super_block *sb, struct au_branch *br) ++{ ++ int err; ++ struct kstatfs st; ++ struct au_sbinfo *sbinfo; ++ ++ /* todo: si_xino_expire and the ratio should be customizable */ ++ sbinfo = au_sbi(sb); ++ if (time_before(jiffies, ++ sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) ++ return 0; ++ ++ /* truncation border */ ++ err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); ++ if (unlikely(err)) { ++ AuErr1("statfs err %d, ignored\n", err); ++ return 0; ++ } ++ if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) ++ return 0; ++ ++ return 1; ++} ++ ++static void xino_try_trunc(struct super_block *sb, struct au_branch *br) ++{ ++ struct xino_do_trunc_args *args; ++ int wkq_err; ++ ++ if (!xino_trunc_test(sb, br)) ++ return; ++ ++ if (atomic_inc_return(&br->br_xino_running) > 1) ++ goto out; ++ ++ /* lock and kfree() will be called in trunc_xino() */ ++ args = kmalloc(sizeof(*args), GFP_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ goto out_args; ++ } ++ ++ atomic_inc(&br->br_count); ++ args->sb = sb; ++ args->br = br; ++ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); ++ if (!wkq_err) ++ return; /* success */ ++ ++ pr_err("wkq %d\n", wkq_err); ++ atomic_dec(&br->br_count); ++ ++out_args: ++ kfree(args); ++out: ++ atomic_dec(&br->br_xino_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_xino_do_write(vfs_writef_t write, struct file *file, ++ ino_t h_ino, ino_t ino) ++{ ++ loff_t pos; ++ ssize_t sz; ++ ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(ino); ++ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); ++ if (sz == sizeof(ino)) ++ return 0; /* success */ ++ ++ AuIOErr("write failed (%zd)\n", sz); ++ return -EIO; ++} ++ ++/* ++ * write @ino to the xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * even if @ino is zero, it is written to the xinofile and means no entry. ++ * if the size of the xino file on a specific filesystem exceeds the watermark, ++ * try truncating it. ++ */ ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino) ++{ ++ int err; ++ unsigned int mnt_flags; ++ struct au_branch *br; ++ ++ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) ++ || ((loff_t)-1) > 0); ++ SiMustAnyLock(sb); ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO)) ++ return 0; ++ ++ br = au_sbr(sb, bindex); ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (!err) { ++ if (au_opt_test(mnt_flags, TRUNC_XINO) ++ && au_test_fs_trunc_xino(au_br_sb(br))) ++ xino_try_trunc(sb, br); ++ return 0; /* success */ ++ } ++ ++ AuIOErr("write failed (%d)\n", err); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* aufs inode number bitmap */ ++ ++static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; ++static ino_t xib_calc_ino(unsigned long pindex, int bit) ++{ ++ ino_t ino; ++ ++ AuDebugOn(bit < 0 || page_bits <= bit); ++ ino = AUFS_FIRST_INO + pindex * page_bits + bit; ++ return ino; ++} ++ ++static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) ++{ ++ AuDebugOn(ino < AUFS_FIRST_INO); ++ ino -= AUFS_FIRST_INO; ++ *pindex = ino / page_bits; ++ *bit = ino % page_bits; ++} ++ ++static int xib_pindex(struct super_block *sb, unsigned long pindex) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ struct au_sbinfo *sbinfo; ++ struct file *xib; ++ unsigned long *p; ++ ++ sbinfo = au_sbi(sb); ++ MtxMustLock(&sbinfo->si_xib_mtx); ++ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE ++ || !au_opt_test(sbinfo->si_mntflags, XINO)); ++ ++ if (pindex == sbinfo->si_xib_last_pindex) ++ return 0; ++ ++ xib = sbinfo->si_xib; ++ p = sbinfo->si_xib_buf; ++ pos = sbinfo->si_xib_last_pindex; ++ pos *= PAGE_SIZE; ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ if (unlikely(sz != PAGE_SIZE)) ++ goto out; ++ ++ pos = pindex; ++ pos *= PAGE_SIZE; ++ if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) ++ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); ++ else { ++ memset(p, 0, PAGE_SIZE); ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ } ++ if (sz == PAGE_SIZE) { ++ sbinfo->si_xib_last_pindex = pindex; ++ return 0; /* success */ ++ } ++ ++out: ++ AuIOErr1("write failed (%zd)\n", sz); ++ err = sz; ++ if (sz >= 0) ++ err = -EIO; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_xib_clear_bit(struct inode *inode) ++{ ++ int err, bit; ++ unsigned long pindex; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(inode->i_nlink); ++ ++ sb = inode->i_sb; ++ xib_calc_bit(inode->i_ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ sbinfo = au_sbi(sb); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ err = xib_pindex(sb, pindex); ++ if (!err) { ++ clear_bit(bit, sbinfo->si_xib_buf); ++ sbinfo->si_xib_next_bit = bit; ++ } ++ mutex_unlock(&sbinfo->si_xib_mtx); ++} ++ ++/* for s_op->delete_inode() */ ++void au_xino_delete_inode(struct inode *inode, const int unlinked) ++{ ++ int err; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bend, bi; ++ unsigned char try_trunc; ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ struct au_hinode *hi; ++ struct inode *h_inode; ++ struct au_branch *br; ++ vfs_writef_t xwrite; ++ ++ sb = inode->i_sb; ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO) ++ || inode->i_ino == AUFS_ROOT_INO) ++ return; ++ ++ if (unlinked) { ++ au_xigen_inc(inode); ++ au_xib_clear_bit(inode); ++ } ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ ++ bindex = iinfo->ii_bstart; ++ if (bindex < 0) ++ return; ++ ++ xwrite = au_sbi(sb)->si_xwrite; ++ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); ++ hi = iinfo->ii_hinode + bindex; ++ bend = iinfo->ii_bend; ++ for (; bindex <= bend; bindex++, hi++) { ++ h_inode = hi->hi_inode; ++ if (!h_inode ++ || (!unlinked && h_inode->i_nlink)) ++ continue; ++ ++ /* inode may not be revalidated */ ++ bi = au_br_index(sb, hi->hi_id); ++ if (bi < 0) ++ continue; ++ ++ br = au_sbr(sb, bi); ++ err = au_xino_do_write(xwrite, br->br_xino.xi_file, ++ h_inode->i_ino, /*ino*/0); ++ if (!err && try_trunc ++ && au_test_fs_trunc_xino(au_br_sb(br))) ++ xino_try_trunc(sb, br); ++ } ++} ++ ++/* get an unused inode number from bitmap */ ++ino_t au_xino_new_ino(struct super_block *sb) ++{ ++ ino_t ino; ++ unsigned long *p, pindex, ul, pend; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ int free_bit, err; ++ ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return iunique(sb, AUFS_FIRST_INO); ++ ++ sbinfo = au_sbi(sb); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ free_bit = sbinfo->si_xib_next_bit; ++ if (free_bit < page_bits && !test_bit(free_bit, p)) ++ goto out; /* success */ ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ ++ pindex = sbinfo->si_xib_last_pindex; ++ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ ++ file = sbinfo->si_xib; ++ pend = vfsub_f_size_read(file) / PAGE_SIZE; ++ for (ul = pindex + 1; ul <= pend; ul++) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ BUG(); ++ ++out: ++ set_bit(free_bit, p); ++ sbinfo->si_xib_next_bit = free_bit + 1; ++ pindex = sbinfo->si_xib_last_pindex; ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ino = xib_calc_ino(pindex, free_bit); ++ AuDbg("i%lu\n", (unsigned long)ino); ++ return ino; ++out_err: ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ AuDbg("i0\n"); ++ return 0; ++} ++ ++/* ++ * read @ino from xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * if @ino does not exist and @do_new is true, get new one. ++ */ ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct file *file; ++ struct au_sbinfo *sbinfo; ++ ++ *ino = 0; ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return 0; /* no xino */ ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(*ino); ++ ++ file = au_sbr(sb, bindex)->br_xino.xi_file; ++ if (vfsub_f_size_read(file) < pos + sizeof(*ino)) ++ return 0; /* no ino */ ++ ++ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); ++ if (sz == sizeof(*ino)) ++ return 0; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xino read error (%zd)\n", sz); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* create and set a new xino file */ ++ ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent) ++{ ++ struct file *file; ++ struct dentry *h_parent, *d; ++ struct inode *h_dir, *inode; ++ int err; ++ ++ /* ++ * at mount-time, and the xino file is the default path, ++ * hnotify is disabled so we have no notify events to ignore. ++ * when a user specified the xino, we cannot get au_hdir to be ignored. ++ */ ++ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE ++ /* | __FMODE_NONOTIFY */, ++ S_IRUGO | S_IWUGO); ++ if (IS_ERR(file)) { ++ if (!silent) ++ pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); ++ return file; ++ } ++ ++ /* keep file count */ ++ err = 0; ++ inode = file_inode(file); ++ h_parent = dget_parent(file->f_path.dentry); ++ h_dir = d_inode(h_parent); ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ /* no delegation since it is just created */ ++ if (inode->i_nlink) ++ err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, ++ /*force*/0); ++ mutex_unlock(&h_dir->i_mutex); ++ dput(h_parent); ++ if (unlikely(err)) { ++ if (!silent) ++ pr_err("unlink %s(%d)\n", fname, err); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ d = file->f_path.dentry; ++ if (unlikely(sb == d->d_sb)) { ++ if (!silent) ++ pr_err("%s must be outside\n", fname); ++ goto out; ++ } ++ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { ++ if (!silent) ++ pr_err("xino doesn't support %s(%s)\n", ++ fname, au_sbtype(d->d_sb)); ++ goto out; ++ } ++ return file; /* success */ ++ ++out: ++ fput(file); ++ file = ERR_PTR(err); ++ return file; ++} ++ ++/* ++ * find another branch who is on the same filesystem of the specified ++ * branch{@btgt}. search until @bend. ++ */ ++static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, ++ aufs_bindex_t bend) ++{ ++ aufs_bindex_t bindex; ++ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); ++ ++ for (bindex = 0; bindex < btgt; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ for (bindex++; bindex <= bend; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * initialize the xinofile for the specified branch @br ++ * at the place/path where @base_file indicates. ++ * test whether another branch is on the same filesystem or not, ++ * if @do_test is true. ++ */ ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, ++ struct file *base_file, int do_test) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bend, bindex; ++ struct au_branch *shared_br, *b; ++ struct file *file; ++ struct super_block *tgt_sb; ++ ++ shared_br = NULL; ++ bend = au_sbend(sb); ++ if (do_test) { ++ tgt_sb = au_br_sb(br); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ b = au_sbr(sb, bindex); ++ if (tgt_sb == au_br_sb(b)) { ++ shared_br = b; ++ break; ++ } ++ } ++ } ++ ++ if (!shared_br || !shared_br->br_xino.xi_file) { ++ struct au_xino_lock_dir ldir; ++ ++ au_xino_lock_dir(sb, base_file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(base_file, NULL); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ br->br_xino.xi_file = file; ++ } else { ++ br->br_xino.xi_file = shared_br->br_xino.xi_file; ++ get_file(br->br_xino.xi_file); ++ } ++ ++ ino = AUFS_ROOT_INO; ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (unlikely(err)) { ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = NULL; ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate a xino bitmap file */ ++ ++/* todo: slow */ ++static int do_xib_restore(struct super_block *sb, struct file *file, void *page) ++{ ++ int err, bit; ++ ssize_t sz; ++ unsigned long pindex; ++ loff_t pos, pend; ++ struct au_sbinfo *sbinfo; ++ vfs_readf_t func; ++ ino_t *ino; ++ unsigned long *p; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ MtxMustLock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ func = sbinfo->si_xread; ++ pend = vfsub_f_size_read(file); ++ pos = 0; ++ while (pos < pend) { ++ sz = xino_fread(func, file, page, PAGE_SIZE, &pos); ++ err = sz; ++ if (unlikely(sz <= 0)) ++ goto out; ++ ++ err = 0; ++ for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { ++ if (unlikely(*ino < AUFS_FIRST_INO)) ++ continue; ++ ++ xib_calc_bit(*ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ err = xib_pindex(sb, pindex); ++ if (!err) ++ set_bit(bit, p); ++ else ++ goto out; ++ } ++ } ++ ++out: ++ return err; ++} ++ ++static int xib_restore(struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ void *page; ++ ++ err = -ENOMEM; ++ page = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!page)) ++ goto out; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) ++ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) ++ err = do_xib_restore ++ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); ++ else ++ AuDbg("b%d\n", bindex); ++ free_page((unsigned long)page); ++ ++out: ++ return err; ++} ++ ++int au_xib_trunc(struct super_block *sb) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct au_xino_lock_dir ldir; ++ struct au_sbinfo *sbinfo; ++ unsigned long *p; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ if (!au_opt_test(sbinfo->si_mntflags, XINO)) ++ goto out; ++ ++ file = sbinfo->si_xib; ++ if (vfsub_f_size_read(file) <= PAGE_SIZE) ++ goto out; ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(sbinfo->si_xib, NULL); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = file; ++ ++ p = sbinfo->si_xib_buf; ++ memset(p, 0, PAGE_SIZE); ++ pos = 0; ++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); ++ if (unlikely(sz != PAGE_SIZE)) { ++ err = sz; ++ AuIOErr("err %d\n", err); ++ if (sz >= 0) ++ err = -EIO; ++ goto out; ++ } ++ ++ mutex_lock(&sbinfo->si_xib_mtx); ++ /* mnt_want_write() is unnecessary here */ ++ err = xib_restore(sb); ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * xino mount option handlers ++ */ ++ ++/* xino bitmap */ ++static void xino_clear_xib(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = NULL; ++ free_page((unsigned long)sbinfo->si_xib_buf); ++ sbinfo->si_xib_buf = NULL; ++} ++ ++static int au_xino_set_xib(struct super_block *sb, struct file *base) ++{ ++ int err; ++ loff_t pos; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xib); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = file; ++ sbinfo->si_xread = vfs_readf(file); ++ sbinfo->si_xwrite = vfs_writef(file); ++ ++ err = -ENOMEM; ++ if (!sbinfo->si_xib_buf) ++ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!sbinfo->si_xib_buf)) ++ goto out_unset; ++ ++ sbinfo->si_xib_last_pindex = 0; ++ sbinfo->si_xib_next_bit = 0; ++ if (vfsub_f_size_read(file) < PAGE_SIZE) { ++ pos = 0; ++ err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, ++ PAGE_SIZE, &pos); ++ if (unlikely(err != PAGE_SIZE)) ++ goto out_free; ++ } ++ err = 0; ++ goto out; /* success */ ++ ++out_free: ++ free_page((unsigned long)sbinfo->si_xib_buf); ++ sbinfo->si_xib_buf = NULL; ++ if (err >= 0) ++ err = -EIO; ++out_unset: ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = NULL; ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++out: ++ return err; ++} ++ ++/* xino for each branch */ ++static void xino_clear_br(struct super_block *sb) ++{ ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!br || !br->br_xino.xi_file) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = NULL; ++ } ++} ++ ++static int au_xino_set_br(struct super_block *sb, struct file *base) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bindex, bend, bshared; ++ struct { ++ struct file *old, *new; ++ } *fpair, *p; ++ struct au_branch *br; ++ struct inode *inode; ++ vfs_writef_t writef; ++ ++ SiMustWriteLock(sb); ++ ++ err = -ENOMEM; ++ bend = au_sbend(sb); ++ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); ++ if (unlikely(!fpair)) ++ goto out; ++ ++ inode = d_inode(sb->s_root); ++ ino = AUFS_ROOT_INO; ++ writef = au_sbi(sb)->si_xwrite; ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ bshared = is_sb_shared(sb, bindex, bindex - 1); ++ if (bshared >= 0) { ++ /* shared xino */ ++ *p = fpair[bshared]; ++ get_file(p->new); ++ } ++ ++ if (!p->new) { ++ /* new xino */ ++ p->old = br->br_xino.xi_file; ++ p->new = au_xino_create2(base, br->br_xino.xi_file); ++ err = PTR_ERR(p->new); ++ if (IS_ERR(p->new)) { ++ p->new = NULL; ++ goto out_pair; ++ } ++ } ++ ++ err = au_xino_do_write(writef, p->new, ++ au_h_iptr(inode, bindex)->i_ino, ino); ++ if (unlikely(err)) ++ goto out_pair; ++ } ++ ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ get_file(p->new); ++ br->br_xino.xi_file = p->new; ++ } ++ ++out_pair: ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) ++ if (p->new) ++ fput(p->new); ++ else ++ break; ++ kfree(fpair); ++out: ++ return err; ++} ++ ++void au_xino_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ au_xigen_clr(sb); ++ xino_clear_xib(sb); ++ xino_clear_br(sb); ++ sbinfo = au_sbi(sb); ++ /* lvalue, do not call au_mntflags() */ ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++} ++ ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) ++{ ++ int err, skip; ++ struct dentry *parent, *cur_parent; ++ struct qstr *dname, *cur_name; ++ struct file *cur_xino; ++ struct inode *dir; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(xino->file->f_path.dentry); ++ if (remount) { ++ skip = 0; ++ dname = &xino->file->f_path.dentry->d_name; ++ cur_xino = sbinfo->si_xib; ++ if (cur_xino) { ++ cur_parent = dget_parent(cur_xino->f_path.dentry); ++ cur_name = &cur_xino->f_path.dentry->d_name; ++ skip = (cur_parent == parent ++ && au_qstreq(dname, cur_name)); ++ dput(cur_parent); ++ } ++ if (skip) ++ goto out; ++ } ++ ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ dir = d_inode(parent); ++ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ err = au_xino_set_xib(sb, xino->file); ++ if (!err) ++ err = au_xigen_set(sb, xino->file); ++ if (!err) ++ err = au_xino_set_br(sb, xino->file); ++ mutex_unlock(&dir->i_mutex); ++ if (!err) ++ goto out; /* success */ ++ ++ /* reset all */ ++ AuIOErr("failed creating xino(%d).\n", err); ++ au_xigen_clr(sb); ++ xino_clear_xib(sb); ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a xinofile at the default place/path. ++ */ ++struct file *au_xino_def(struct super_block *sb) ++{ ++ struct file *file; ++ char *page, *p; ++ struct au_branch *br; ++ struct super_block *h_sb; ++ struct path path; ++ aufs_bindex_t bend, bindex, bwr; ++ ++ br = NULL; ++ bend = au_sbend(sb); ++ bwr = -1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_writable(br->br_perm) ++ && !au_test_fs_bad_xino(au_br_sb(br))) { ++ bwr = bindex; ++ break; ++ } ++ } ++ ++ if (bwr >= 0) { ++ file = ERR_PTR(-ENOMEM); ++ page = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!page)) ++ goto out; ++ path.mnt = au_br_mnt(br); ++ path.dentry = au_h_dptr(sb->s_root, bwr); ++ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); ++ file = (void *)p; ++ if (!IS_ERR(p)) { ++ strcat(p, "/" AUFS_XINO_FNAME); ++ AuDbg("%s\n", p); ++ file = au_xino_create(sb, p, /*silent*/0); ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, br->br_id); ++ } ++ free_page((unsigned long)page); ++ } else { ++ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); ++ if (IS_ERR(file)) ++ goto out; ++ h_sb = file->f_path.dentry->d_sb; ++ if (unlikely(au_test_fs_bad_xino(h_sb))) { ++ pr_err("xino doesn't support %s(%s)\n", ++ AUFS_XINO_DEFPATH, au_sbtype(h_sb)); ++ fput(file); ++ file = ERR_PTR(-EINVAL); ++ } ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, -1); ++ } ++ ++out: ++ return file; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_xino_path(struct seq_file *seq, struct file *file) ++{ ++ int err; ++ ++ err = au_seq_path(seq, &file->f_path); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ err = 0; ++#define Deleted "\\040(deleted)" ++ seq->count -= sizeof(Deleted) - 1; ++ AuDebugOn(memcmp(seq->buf + seq->count, Deleted, ++ sizeof(Deleted) - 1)); ++#undef Deleted ++ ++out: ++ return err; ++} +diff --git a/fs/buffer.c b/fs/buffer.c +index c7a5602..8c50a22 100644 +--- a/fs/buffer.c ++++ b/fs/buffer.c +@@ -2450,7 +2450,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, + * Update file times before taking page lock. We may end up failing the + * fault so this update may be superfluous but who really cares... + */ +- file_update_time(vma->vm_file); ++ vma_file_update_time(vma); + + ret = __block_page_mkwrite(vma, vmf, get_block); + sb_end_pagefault(sb); +diff --git a/fs/dcache.c b/fs/dcache.c +index 37b5afd..bc261e2 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1164,7 +1164,7 @@ enum d_walk_ret { + * + * The @enter() and @finish() callbacks are called with d_lock held. + */ +-static void d_walk(struct dentry *parent, void *data, ++void d_walk(struct dentry *parent, void *data, + enum d_walk_ret (*enter)(void *, struct dentry *), + void (*finish)(void *)) + { +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 093ca14..fc1ac03 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -1744,7 +1744,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path) + down_read(&mm->mmap_sem); + vma = find_exact_vma(mm, vm_start, vm_end); + if (vma && vma->vm_file) { +- *path = vma->vm_file->f_path; ++ *path = vma_pr_or_file(vma)->f_path; + path_get(path); + rc = 0; + } +diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c +index d4a3574..1397181 100644 +--- a/fs/proc/nommu.c ++++ b/fs/proc/nommu.c +@@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) + file = region->vm_file; + + if (file) { +- struct inode *inode = file_inode(region->vm_file); ++ struct inode *inode; ++ ++ file = vmr_pr_or_file(region); ++ inode = file_inode(file); + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + } +diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c +index 6dee68d..9afa35d 100644 +--- a/fs/proc/task_mmu.c ++++ b/fs/proc/task_mmu.c +@@ -279,7 +279,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) + const char *name = NULL; + + if (file) { +- struct inode *inode = file_inode(vma->vm_file); ++ struct inode *inode; ++ ++ file = vma_pr_or_file(vma); ++ inode = file_inode(file); + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; +@@ -1479,7 +1482,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) + struct proc_maps_private *proc_priv = &numa_priv->proc_maps; + struct vm_area_struct *vma = v; + struct numa_maps *md = &numa_priv->md; +- struct file *file = vma->vm_file; ++ struct file *file = vma_pr_or_file(vma); + struct mm_struct *mm = vma->vm_mm; + struct mm_walk walk = { + .hugetlb_entry = gather_hugetlb_stats, +diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c +index 599ec2e..1740207 100644 +--- a/fs/proc/task_nommu.c ++++ b/fs/proc/task_nommu.c +@@ -160,7 +160,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, + file = vma->vm_file; + + if (file) { +- struct inode *inode = file_inode(vma->vm_file); ++ struct inode *inode; ++ ++ file = vma_pr_or_file(vma); ++ inode = file_inode(file); + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; +diff --git a/fs/read_write.c b/fs/read_write.c +index 819ef3f..fd0414e 100644 +--- a/fs/read_write.c ++++ b/fs/read_write.c +@@ -494,6 +494,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, + } + EXPORT_SYMBOL(__vfs_write); + ++vfs_readf_t vfs_readf(struct file *file) ++{ ++ const struct file_operations *fop = file->f_op; ++ ++ if (fop->read) ++ return fop->read; ++ if (fop->read_iter) ++ return new_sync_read; ++ return ERR_PTR(-ENOSYS); ++} ++ ++vfs_writef_t vfs_writef(struct file *file) ++{ ++ const struct file_operations *fop = file->f_op; ++ ++ if (fop->write) ++ return fop->write; ++ if (fop->write_iter) ++ return new_sync_write; ++ return ERR_PTR(-ENOSYS); ++} ++ + ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) + { + mm_segment_t old_fs; +diff --git a/fs/splice.c b/fs/splice.c +index bfe62ae..fa5eee5 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1101,8 +1101,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +- loff_t *ppos, size_t len, unsigned int flags) ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, + loff_t *, size_t, unsigned int); +@@ -1118,9 +1118,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, +- struct pipe_inode_info *pipe, size_t len, +- unsigned int flags) ++long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) + { + ssize_t (*splice_read)(struct file *, loff_t *, + struct pipe_inode_info *, size_t, unsigned int); +diff --git a/include/linux/file.h b/include/linux/file.h +index f87d308..9a290b3 100644 +--- a/include/linux/file.h ++++ b/include/linux/file.h +@@ -19,6 +19,7 @@ struct dentry; + struct path; + extern struct file *alloc_file(struct path *, fmode_t mode, + const struct file_operations *fop); ++extern struct file *get_empty_filp(void); + + static inline void fput_light(struct file *file, int fput_needed) + { +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 35ec87e..3229f97 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1649,6 +1649,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, + struct iovec *fast_pointer, + struct iovec **ret_pointer); + ++typedef ssize_t (*vfs_readf_t)(struct file *, char __user *, size_t, loff_t *); ++typedef ssize_t (*vfs_writef_t)(struct file *, const char __user *, size_t, ++ loff_t *); ++vfs_readf_t vfs_readf(struct file *file); ++vfs_writef_t vfs_writef(struct file *file); ++ + extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *); + extern ssize_t __vfs_write(struct file *, const char __user *, size_t, loff_t *); + extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *); +diff --git a/include/linux/mm.h b/include/linux/mm.h +index 0755b9f..2ee5500 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1172,6 +1172,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, + } + #endif + ++extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); ++extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], ++ int); ++extern void vma_do_get_file(struct vm_area_struct *, const char[], int); ++extern void vma_do_fput(struct vm_area_struct *, const char[], int); ++ ++#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \ ++ __LINE__) ++#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \ ++ __LINE__) ++#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__) ++#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__) ++ ++#ifndef CONFIG_MMU ++extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int); ++extern void vmr_do_fput(struct vm_region *, const char[], int); ++ ++#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \ ++ __LINE__) ++#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) ++#endif /* !CONFIG_MMU */ ++ + extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); + extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, + void *buf, int len, int write); +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 8d37e26..ce89d4c 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -241,6 +241,7 @@ struct vm_region { + unsigned long vm_top; /* region allocated to here */ + unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ + struct file *vm_file; /* the backing file or NULL */ ++ struct file *vm_prfile; /* the virtual backing file or NULL */ + + int vm_usage; /* region usage count (access under nommu_region_sem) */ + bool vm_icache_flushed : 1; /* true if the icache has been flushed for +@@ -305,6 +306,7 @@ struct vm_area_struct { + unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE + units, *not* PAGE_CACHE_SIZE */ + struct file * vm_file; /* File we map to (can be NULL). */ ++ struct file *vm_prfile; /* shadow of vm_file */ + void * vm_private_data; /* was vm_pte (shared mem) */ + + #ifndef CONFIG_MMU +diff --git a/include/linux/splice.h b/include/linux/splice.h +index da2751d..2e0fca6 100644 +--- a/include/linux/splice.h ++++ b/include/linux/splice.h +@@ -83,4 +83,10 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); + extern void spd_release_page(struct splice_pipe_desc *, unsigned int); + + extern const struct pipe_buf_operations page_cache_pipe_buf_ops; ++ ++extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++extern long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); + #endif +diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild +index 1a0006a..ddad01a 100644 +--- a/include/uapi/linux/Kbuild ++++ b/include/uapi/linux/Kbuild +@@ -59,6 +59,7 @@ header-y += atmsvc.h + header-y += atm_tcp.h + header-y += atm_zatm.h + header-y += audit.h ++header-y += aufs_type.h + header-y += auto_fs4.h + header-y += auto_fs.h + header-y += auxvec.h +diff --git a/include/uapi/linux/aufs_type.h b/include/uapi/linux/aufs_type.h +new file mode 100644 +index 0000000..a06c985 +--- /dev/null ++++ b/include/uapi/linux/aufs_type.h +@@ -0,0 +1,406 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++#ifndef __AUFS_TYPE_H__ ++#define __AUFS_TYPE_H__ ++ ++#define AUFS_NAME "aufs" ++ ++#ifdef __KERNEL__ ++/* ++ * define it before including all other headers. ++ * sched.h may use pr_* macros before defining "current", so define the ++ * no-current version first, and re-define later. ++ */ ++#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ ++#include ++#undef pr_fmt ++#define pr_fmt(fmt) \ ++ AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ ++ (int)sizeof(current->comm), current->comm, current->pid ++#else ++#include ++#include ++#endif /* __KERNEL__ */ ++ ++#include ++ ++#define AUFS_VERSION "4.x-rcN" ++ ++/* todo? move this to linux-2.6.19/include/magic.h */ ++#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_BRANCH_MAX_127 ++typedef int8_t aufs_bindex_t; ++#define AUFS_BRANCH_MAX 127 ++#else ++typedef int16_t aufs_bindex_t; ++#ifdef CONFIG_AUFS_BRANCH_MAX_511 ++#define AUFS_BRANCH_MAX 511 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) ++#define AUFS_BRANCH_MAX 1023 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) ++#define AUFS_BRANCH_MAX 32767 ++#endif ++#endif ++ ++#ifdef __KERNEL__ ++#ifndef AUFS_BRANCH_MAX ++#error unknown CONFIG_AUFS_BRANCH_MAX value ++#endif ++#endif /* __KERNEL__ */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AUFS_FSTYPE AUFS_NAME ++ ++#define AUFS_ROOT_INO 2 ++#define AUFS_FIRST_INO 11 ++ ++#define AUFS_WH_PFX ".wh." ++#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) ++#define AUFS_WH_TMP_LEN 4 ++/* a limit for rmdir/rename a dir and copyup */ ++#define AUFS_MAX_NAMELEN (NAME_MAX \ ++ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ ++ - 1 /* dot */\ ++ - AUFS_WH_TMP_LEN) /* hex */ ++#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" ++#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME ++#define AUFS_XINO_DEF_SEC 30 /* seconds */ ++#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ ++#define AUFS_DIRWH_DEF 3 ++#define AUFS_RDCACHE_DEF 10 /* seconds */ ++#define AUFS_RDCACHE_MAX 3600 /* seconds */ ++#define AUFS_RDBLK_DEF 512 /* bytes */ ++#define AUFS_RDHASH_DEF 32 ++#define AUFS_WKQ_NAME AUFS_NAME "d" ++#define AUFS_MFS_DEF_SEC 30 /* seconds */ ++#define AUFS_MFS_MAX_SEC 3600 /* seconds */ ++#define AUFS_FHSM_CACHE_DEF_SEC 30 /* seconds */ ++#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ ++ ++/* pseudo-link maintenace under /proc */ ++#define AUFS_PLINK_MAINT_NAME "plink_maint" ++#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME ++#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME ++ ++#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ ++#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME ++ ++#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME ++#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" ++#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" ++ ++/* doubly whiteouted */ ++#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME ++#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME ++#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME ++ ++/* branch permissions and attributes */ ++#define AUFS_BRPERM_RW "rw" ++#define AUFS_BRPERM_RO "ro" ++#define AUFS_BRPERM_RR "rr" ++#define AUFS_BRATTR_COO_REG "coo_reg" ++#define AUFS_BRATTR_COO_ALL "coo_all" ++#define AUFS_BRATTR_FHSM "fhsm" ++#define AUFS_BRATTR_UNPIN "unpin" ++#define AUFS_BRATTR_ICEX "icex" ++#define AUFS_BRATTR_ICEX_SEC "icexsec" ++#define AUFS_BRATTR_ICEX_SYS "icexsys" ++#define AUFS_BRATTR_ICEX_TR "icextr" ++#define AUFS_BRATTR_ICEX_USR "icexusr" ++#define AUFS_BRATTR_ICEX_OTH "icexoth" ++#define AUFS_BRRATTR_WH "wh" ++#define AUFS_BRWATTR_NLWH "nolwh" ++#define AUFS_BRWATTR_MOO "moo" ++ ++#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ ++#define AuBrPerm_RO (1 << 1) /* readonly */ ++#define AuBrPerm_RR (1 << 2) /* natively readonly */ ++#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR) ++ ++#define AuBrAttr_COO_REG (1 << 3) /* copy-up on open */ ++#define AuBrAttr_COO_ALL (1 << 4) ++#define AuBrAttr_COO_Mask (AuBrAttr_COO_REG | AuBrAttr_COO_ALL) ++ ++#define AuBrAttr_FHSM (1 << 5) /* file-based hsm */ ++#define AuBrAttr_UNPIN (1 << 6) /* rename-able top dir of ++ branch. meaningless since ++ linux-3.18-rc1 */ ++ ++/* ignore error in copying XATTR */ ++#define AuBrAttr_ICEX_SEC (1 << 7) ++#define AuBrAttr_ICEX_SYS (1 << 8) ++#define AuBrAttr_ICEX_TR (1 << 9) ++#define AuBrAttr_ICEX_USR (1 << 10) ++#define AuBrAttr_ICEX_OTH (1 << 11) ++#define AuBrAttr_ICEX (AuBrAttr_ICEX_SEC \ ++ | AuBrAttr_ICEX_SYS \ ++ | AuBrAttr_ICEX_TR \ ++ | AuBrAttr_ICEX_USR \ ++ | AuBrAttr_ICEX_OTH) ++ ++#define AuBrRAttr_WH (1 << 12) /* whiteout-able */ ++#define AuBrRAttr_Mask AuBrRAttr_WH ++ ++#define AuBrWAttr_NoLinkWH (1 << 13) /* un-hardlinkable whiteouts */ ++#define AuBrWAttr_MOO (1 << 14) /* move-up on open */ ++#define AuBrWAttr_Mask (AuBrWAttr_NoLinkWH | AuBrWAttr_MOO) ++ ++#define AuBrAttr_CMOO_Mask (AuBrAttr_COO_Mask | AuBrWAttr_MOO) ++ ++/* #warning test userspace */ ++#ifdef __KERNEL__ ++#ifndef CONFIG_AUFS_FHSM ++#undef AuBrAttr_FHSM ++#define AuBrAttr_FHSM 0 ++#endif ++#ifndef CONFIG_AUFS_XATTR ++#undef AuBrAttr_ICEX ++#define AuBrAttr_ICEX 0 ++#undef AuBrAttr_ICEX_SEC ++#define AuBrAttr_ICEX_SEC 0 ++#undef AuBrAttr_ICEX_SYS ++#define AuBrAttr_ICEX_SYS 0 ++#undef AuBrAttr_ICEX_TR ++#define AuBrAttr_ICEX_TR 0 ++#undef AuBrAttr_ICEX_USR ++#define AuBrAttr_ICEX_USR 0 ++#undef AuBrAttr_ICEX_OTH ++#define AuBrAttr_ICEX_OTH 0 ++#endif ++#endif ++ ++/* the longest combination */ ++/* AUFS_BRATTR_ICEX and AUFS_BRATTR_ICEX_TR don't affect here */ ++#define AuBrPermStrSz sizeof(AUFS_BRPERM_RW \ ++ "+" AUFS_BRATTR_COO_REG \ ++ "+" AUFS_BRATTR_FHSM \ ++ "+" AUFS_BRATTR_UNPIN \ ++ "+" AUFS_BRATTR_ICEX_SEC \ ++ "+" AUFS_BRATTR_ICEX_SYS \ ++ "+" AUFS_BRATTR_ICEX_USR \ ++ "+" AUFS_BRATTR_ICEX_OTH \ ++ "+" AUFS_BRWATTR_NLWH) ++ ++typedef struct { ++ char a[AuBrPermStrSz]; ++} au_br_perm_str_t; ++ ++static inline int au_br_writable(int brperm) ++{ ++ return brperm & AuBrPerm_RW; ++} ++ ++static inline int au_br_whable(int brperm) ++{ ++ return brperm & (AuBrPerm_RW | AuBrRAttr_WH); ++} ++ ++static inline int au_br_wh_linkable(int brperm) ++{ ++ return !(brperm & AuBrWAttr_NoLinkWH); ++} ++ ++static inline int au_br_cmoo(int brperm) ++{ ++ return brperm & AuBrAttr_CMOO_Mask; ++} ++ ++static inline int au_br_fhsm(int brperm) ++{ ++ return brperm & AuBrAttr_FHSM; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ioctl */ ++enum { ++ /* readdir in userspace */ ++ AuCtl_RDU, ++ AuCtl_RDU_INO, ++ ++ AuCtl_WBR_FD, /* pathconf wrapper */ ++ AuCtl_IBUSY, /* busy inode */ ++ AuCtl_MVDOWN, /* move-down */ ++ AuCtl_BR, /* info about branches */ ++ AuCtl_FHSM_FD /* connection for fhsm */ ++}; ++ ++/* borrowed from linux/include/linux/kernel.h */ ++#ifndef ALIGN ++#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) ++#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) ++#endif ++ ++/* borrowed from linux/include/linux/compiler-gcc3.h */ ++#ifndef __aligned ++#define __aligned(x) __attribute__((aligned(x))) ++#endif ++ ++#ifdef __KERNEL__ ++#ifndef __packed ++#define __packed __attribute__((packed)) ++#endif ++#endif ++ ++struct au_rdu_cookie { ++ uint64_t h_pos; ++ int16_t bindex; ++ uint8_t flags; ++ uint8_t pad; ++ uint32_t generation; ++} __aligned(8); ++ ++struct au_rdu_ent { ++ uint64_t ino; ++ int16_t bindex; ++ uint8_t type; ++ uint8_t nlen; ++ uint8_t wh; ++ char name[0]; ++} __aligned(8); ++ ++static inline int au_rdu_len(int nlen) ++{ ++ /* include the terminating NULL */ ++ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, ++ sizeof(uint64_t)); ++} ++ ++union au_rdu_ent_ul { ++ struct au_rdu_ent __user *e; ++ uint64_t ul; ++}; ++ ++enum { ++ AufsCtlRduV_SZ, ++ AufsCtlRduV_End ++}; ++ ++struct aufs_rdu { ++ /* input */ ++ union { ++ uint64_t sz; /* AuCtl_RDU */ ++ uint64_t nent; /* AuCtl_RDU_INO */ ++ }; ++ union au_rdu_ent_ul ent; ++ uint16_t verify[AufsCtlRduV_End]; ++ ++ /* input/output */ ++ uint32_t blk; ++ ++ /* output */ ++ union au_rdu_ent_ul tail; ++ /* number of entries which were added in a single call */ ++ uint64_t rent; ++ uint8_t full; ++ uint8_t shwh; ++ ++ struct au_rdu_cookie cookie; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct aufs_wbr_fd { ++ uint32_t oflags; ++ int16_t brid; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct aufs_ibusy { ++ uint64_t ino, h_ino; ++ int16_t bindex; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* error code for move-down */ ++/* the actual message strings are implemented in aufs-util.git */ ++enum { ++ EAU_MVDOWN_OPAQUE = 1, ++ EAU_MVDOWN_WHITEOUT, ++ EAU_MVDOWN_UPPER, ++ EAU_MVDOWN_BOTTOM, ++ EAU_MVDOWN_NOUPPER, ++ EAU_MVDOWN_NOLOWERBR, ++ EAU_Last ++}; ++ ++/* flags for move-down */ ++#define AUFS_MVDOWN_DMSG 1 ++#define AUFS_MVDOWN_OWLOWER (1 << 1) /* overwrite lower */ ++#define AUFS_MVDOWN_KUPPER (1 << 2) /* keep upper */ ++#define AUFS_MVDOWN_ROLOWER (1 << 3) /* do even if lower is RO */ ++#define AUFS_MVDOWN_ROLOWER_R (1 << 4) /* did on lower RO */ ++#define AUFS_MVDOWN_ROUPPER (1 << 5) /* do even if upper is RO */ ++#define AUFS_MVDOWN_ROUPPER_R (1 << 6) /* did on upper RO */ ++#define AUFS_MVDOWN_BRID_UPPER (1 << 7) /* upper brid */ ++#define AUFS_MVDOWN_BRID_LOWER (1 << 8) /* lower brid */ ++#define AUFS_MVDOWN_FHSM_LOWER (1 << 9) /* find fhsm attr for lower */ ++#define AUFS_MVDOWN_STFS (1 << 10) /* req. stfs */ ++#define AUFS_MVDOWN_STFS_FAILED (1 << 11) /* output: stfs is unusable */ ++#define AUFS_MVDOWN_BOTTOM (1 << 12) /* output: no more lowers */ ++ ++/* index for move-down */ ++enum { ++ AUFS_MVDOWN_UPPER, ++ AUFS_MVDOWN_LOWER, ++ AUFS_MVDOWN_NARRAY ++}; ++ ++/* ++ * additional info of move-down ++ * number of free blocks and inodes. ++ * subset of struct kstatfs, but smaller and always 64bit. ++ */ ++struct aufs_stfs { ++ uint64_t f_blocks; ++ uint64_t f_bavail; ++ uint64_t f_files; ++ uint64_t f_ffree; ++}; ++ ++struct aufs_stbr { ++ int16_t brid; /* optional input */ ++ int16_t bindex; /* output */ ++ struct aufs_stfs stfs; /* output when AUFS_MVDOWN_STFS set */ ++} __aligned(8); ++ ++struct aufs_mvdown { ++ uint32_t flags; /* input/output */ ++ struct aufs_stbr stbr[AUFS_MVDOWN_NARRAY]; /* input/output */ ++ int8_t au_errno; /* output */ ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++union aufs_brinfo { ++ /* PATH_MAX may differ between kernel-space and user-space */ ++ char _spacer[4096]; ++ struct { ++ int16_t id; ++ int perm; ++ char path[0]; ++ }; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuCtlType 'A' ++#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) ++#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) ++#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ ++ struct aufs_wbr_fd) ++#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) ++#define AUFS_CTL_MVDOWN _IOWR(AuCtlType, AuCtl_MVDOWN, \ ++ struct aufs_mvdown) ++#define AUFS_CTL_BRINFO _IOW(AuCtlType, AuCtl_BR, union aufs_brinfo) ++#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) ++ ++#endif /* __AUFS_TYPE_H__ */ +diff --git a/kernel/fork.c b/kernel/fork.c +index 03c1eaa..7e215ba 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -456,7 +456,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) + struct inode *inode = file_inode(file); + struct address_space *mapping = file->f_mapping; + +- get_file(file); ++ vma_get_file(tmp); + if (tmp->vm_flags & VM_DENYWRITE) + atomic_dec(&inode->i_writecount); + i_mmap_lock_write(mapping); +diff --git a/mm/Makefile b/mm/Makefile +index 98c4eae..3f0c9b9 100644 +--- a/mm/Makefile ++++ b/mm/Makefile +@@ -21,7 +21,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ + mm_init.o mmu_context.o percpu.o slab_common.o \ + compaction.o vmacache.o \ + interval_tree.o list_lru.o workingset.o \ +- debug.o $(mmu-y) ++ prfile.o debug.o $(mmu-y) + + obj-y += init-mm.o + +diff --git a/mm/filemap.c b/mm/filemap.c +index 6bf5e42..a863d0f 100644 +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -2062,7 +2062,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) + int ret = VM_FAULT_LOCKED; + + sb_start_pagefault(inode->i_sb); +- file_update_time(vma->vm_file); ++ vma_file_update_time(vma); + lock_page(page); + if (page->mapping != inode->i_mapping) { + unlock_page(page); +diff --git a/mm/madvise.c b/mm/madvise.c +index d551475..1ebf71b 100644 +--- a/mm/madvise.c ++++ b/mm/madvise.c +@@ -320,12 +320,12 @@ static long madvise_remove(struct vm_area_struct *vma, + * vma's reference to the file) can go away as soon as we drop + * mmap_sem. + */ +- get_file(f); ++ vma_get_file(vma); + up_read(¤t->mm->mmap_sem); + error = vfs_fallocate(f, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + offset, end - start); +- fput(f); ++ vma_fput(vma); + down_read(¤t->mm->mmap_sem); + return error; + } +diff --git a/mm/memory.c b/mm/memory.c +index 22e037e..62096a2 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -2034,7 +2034,7 @@ static inline int wp_page_reuse(struct mm_struct *mm, + } + + if (!page_mkwrite) +- file_update_time(vma->vm_file); ++ vma_file_update_time(vma); + } + + return VM_FAULT_WRITE; +diff --git a/mm/mmap.c b/mm/mmap.c +index bb50cac..1ab5e596 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -274,7 +274,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) + if (vma->vm_ops && vma->vm_ops->close) + vma->vm_ops->close(vma); + if (vma->vm_file) +- fput(vma->vm_file); ++ vma_fput(vma); + mpol_put(vma_policy(vma)); + kmem_cache_free(vm_area_cachep, vma); + return next; +@@ -886,7 +886,7 @@ again: remove_next = 1 + (end > next->vm_end); + if (remove_next) { + if (file) { + uprobe_munmap(next, next->vm_start, next->vm_end); +- fput(file); ++ vma_fput(vma); + } + if (next->anon_vma) + anon_vma_merge(vma, next); +@@ -1671,8 +1671,8 @@ out: + return addr; + + unmap_and_free_vma: ++ vma_fput(vma); + vma->vm_file = NULL; +- fput(file); + + /* Undo any partial mapping done by a device driver. */ + unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); +@@ -2473,7 +2473,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, + goto out_free_mpol; + + if (new->vm_file) +- get_file(new->vm_file); ++ vma_get_file(new); + + if (new->vm_ops && new->vm_ops->open) + new->vm_ops->open(new); +@@ -2492,7 +2492,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, + if (new->vm_ops && new->vm_ops->close) + new->vm_ops->close(new); + if (new->vm_file) +- fput(new->vm_file); ++ vma_fput(new); + unlink_anon_vmas(new); + out_free_mpol: + mpol_put(vma_policy(new)); +@@ -2635,7 +2635,6 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, + struct vm_area_struct *vma; + unsigned long populate = 0; + unsigned long ret = -EINVAL; +- struct file *file; + + pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. " + "See Documentation/vm/remap_file_pages.txt.\n", +@@ -2679,10 +2678,10 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, + munlock_vma_pages_range(vma, start, start + size); + } + +- file = get_file(vma->vm_file); ++ vma_get_file(vma); + ret = do_mmap_pgoff(vma->vm_file, start, size, + prot, flags, pgoff, &populate); +- fput(file); ++ vma_fput(vma); + out: + up_write(&mm->mmap_sem); + if (populate) +@@ -2949,7 +2948,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, + if (anon_vma_clone(new_vma, vma)) + goto out_free_mempol; + if (new_vma->vm_file) +- get_file(new_vma->vm_file); ++ vma_get_file(new_vma); + if (new_vma->vm_ops && new_vma->vm_ops->open) + new_vma->vm_ops->open(new_vma); + vma_link(mm, new_vma, prev, rb_link, rb_parent); +diff --git a/mm/msync.c b/mm/msync.c +index bb04d53..5c24c54 100644 +--- a/mm/msync.c ++++ b/mm/msync.c +@@ -84,10 +84,10 @@ SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags) + start = vma->vm_end; + if ((flags & MS_SYNC) && file && + (vma->vm_flags & VM_SHARED)) { +- get_file(file); ++ vma_get_file(vma); + up_read(&mm->mmap_sem); + error = vfs_fsync_range(file, fstart, fend, 1); +- fput(file); ++ vma_fput(vma); + if (error || start >= end) + goto out; + down_read(&mm->mmap_sem); +diff --git a/mm/nommu.c b/mm/nommu.c +index e544508..dd6f74a 100644 +--- a/mm/nommu.c ++++ b/mm/nommu.c +@@ -693,7 +693,7 @@ static void __put_nommu_region(struct vm_region *region) + up_write(&nommu_region_sem); + + if (region->vm_file) +- fput(region->vm_file); ++ vmr_fput(region); + + /* IO memory and memory shared directly out of the pagecache + * from ramfs/tmpfs mustn't be released here */ +@@ -858,7 +858,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) + if (vma->vm_ops && vma->vm_ops->close) + vma->vm_ops->close(vma); + if (vma->vm_file) +- fput(vma->vm_file); ++ vma_fput(vma); + put_nommu_region(vma->vm_region); + kmem_cache_free(vm_area_cachep, vma); + } +@@ -1398,7 +1398,7 @@ unsigned long do_mmap_pgoff(struct file *file, + goto error_just_free; + } + } +- fput(region->vm_file); ++ vmr_fput(region); + kmem_cache_free(vm_region_jar, region); + region = pregion; + result = start; +@@ -1474,10 +1474,10 @@ error_just_free: + up_write(&nommu_region_sem); + error: + if (region->vm_file) +- fput(region->vm_file); ++ vmr_fput(region); + kmem_cache_free(vm_region_jar, region); + if (vma->vm_file) +- fput(vma->vm_file); ++ vma_fput(vma); + kmem_cache_free(vm_area_cachep, vma); + kleave(" = %d", ret); + return ret; +diff --git a/mm/prfile.c b/mm/prfile.c +new file mode 100644 +index 0000000..6aa5ab5 +--- /dev/null ++++ b/mm/prfile.c +@@ -0,0 +1,86 @@ ++/* ++ * Mainly for aufs which mmap(2) diffrent file and wants to print different path ++ * in /proc/PID/maps. ++ * Call these functions via macros defined in linux/mm.h. ++ * ++ * See Documentation/filesystems/aufs/design/06mmap.txt ++ * ++ * Copyright (c) 2014 Junjro R. Okajima ++ * Copyright (c) 2014 Ian Campbell ++ */ ++ ++#include ++#include ++#include ++ ++/* #define PRFILE_TRACE */ ++static inline void prfile_trace(struct file *f, struct file *pr, ++ const char func[], int line, const char func2[]) ++{ ++#ifdef PRFILE_TRACE ++ if (pr) ++ pr_info("%s:%d: %s, %p\n", func, line, func2, ++ f ? (char *)f->f_path.dentry->d_name.name : "(null)"); ++#endif ++} ++ ++void vma_do_file_update_time(struct vm_area_struct *vma, const char func[], ++ int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ file_update_time(f); ++ if (f && pr) ++ file_update_time(pr); ++} ++ ++struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[], ++ int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ return (f && pr) ? pr : f; ++} ++ ++void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ get_file(f); ++ if (f && pr) ++ get_file(pr); ++} ++ ++void vma_do_fput(struct vm_area_struct *vma, const char func[], int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ fput(f); ++ if (f && pr) ++ fput(pr); ++} ++ ++#ifndef CONFIG_MMU ++struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[], ++ int line) ++{ ++ struct file *f = region->vm_file, *pr = region->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ return (f && pr) ? pr : f; ++} ++ ++void vmr_do_fput(struct vm_region *region, const char func[], int line) ++{ ++ struct file *f = region->vm_file, *pr = region->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ fput(f); ++ if (f && pr) ++ fput(pr); ++} ++#endif /* !CONFIG_MMU */ diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.31.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.31.patch new file mode 100644 index 00000000..ac0254e5 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.31.patch @@ -0,0 +1,100 @@ +Adapt mach64 to build with 2.6.31 series kernels + +Signed-off-by: Thomas Backlund + +diff -urp linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64/mach64_dma.c linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64.fixed/mach64_dma.c +--- linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64/mach64_dma.c 2009-07-23 00:24:12.000000000 +0300 ++++ linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64.fixed/mach64_dma.c 2009-07-23 02:28:21.000000000 +0300 +@@ -1006,7 +1006,7 @@ static int mach64_do_dma_init(struct drm + + DRM_DEBUG("\n"); + +- dev_priv = drm_alloc(sizeof(drm_mach64_private_t), DRM_MEM_DRIVER); ++ dev_priv = kmalloc(sizeof(drm_mach64_private_t), GFP_KERNEL); + if (dev_priv == NULL) + return -ENOMEM; + +@@ -1386,8 +1386,7 @@ int mach64_do_cleanup_dma(struct drm_dev + + mach64_destroy_freelist(dev); + +- drm_free(dev_priv, sizeof(drm_mach64_private_t), +- DRM_MEM_DRIVER); ++ kfree(dev_priv); + dev->dev_private = NULL; + } + +@@ -1476,8 +1475,8 @@ int mach64_init_freelist(struct drm_devi + for (i = 0; i < dma->buf_count; i++) { + if ((entry = + (drm_mach64_freelist_t *) +- drm_alloc(sizeof(drm_mach64_freelist_t), +- DRM_MEM_BUFLISTS)) == NULL) ++ kmalloc(sizeof(drm_mach64_freelist_t), ++ GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(entry, 0, sizeof(drm_mach64_freelist_t)); + entry->buf = dma->buflist[i]; +@@ -1500,18 +1499,18 @@ void mach64_destroy_freelist(struct drm_ + list_for_each_safe(ptr, tmp, &dev_priv->pending) { + list_del(ptr); + entry = list_entry(ptr, drm_mach64_freelist_t, list); +- drm_free(entry, sizeof(*entry), DRM_MEM_BUFLISTS); ++ kfree(entry); + } + list_for_each_safe(ptr, tmp, &dev_priv->placeholders) { + list_del(ptr); + entry = list_entry(ptr, drm_mach64_freelist_t, list); +- drm_free(entry, sizeof(*entry), DRM_MEM_BUFLISTS); ++ kfree(entry); + } + + list_for_each_safe(ptr, tmp, &dev_priv->free_list) { + list_del(ptr); + entry = list_entry(ptr, drm_mach64_freelist_t, list); +- drm_free(entry, sizeof(*entry), DRM_MEM_BUFLISTS); ++ kfree(entry); + } + } + +diff -urp linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64/mach64_state.c linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64.fixed/mach64_state.c +--- linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64/mach64_state.c 2009-07-23 00:24:12.000000000 +0300 ++++ linux-2.6.31-rc3-git5-mnb1/drivers/gpu/drm/mach64.fixed/mach64_state.c 2009-07-23 02:29:24.000000000 +0300 +@@ -490,12 +490,12 @@ static __inline__ int copy_from_user_ver + unsigned long n = bytes; /* dwords remaining in buffer */ + u32 *from, *orig_from; + +- from = drm_alloc(bytes, DRM_MEM_DRIVER); ++ from = kmalloc(bytes, GFP_KERNEL); + if (from == NULL) + return -ENOMEM; + + if (DRM_COPY_FROM_USER(from, ufrom, bytes)) { +- drm_free(from, bytes, DRM_MEM_DRIVER); ++ kfree(from); + return -EFAULT; + } + orig_from = from; /* we'll be modifying the "from" ptr, so save it */ +@@ -526,19 +526,19 @@ static __inline__ int copy_from_user_ver + to += count; + } else { + DRM_ERROR("Got bad command: 0x%04x\n", reg); +- drm_free(orig_from, bytes, DRM_MEM_DRIVER); ++ kfree(orig_from); + return -EACCES; + } + } else { + DRM_ERROR + ("Got bad command count(=%u) dwords remaining=%lu\n", + count, n); +- drm_free(orig_from, bytes, DRM_MEM_DRIVER); ++ kfree(orig_from); + return -EINVAL; + } + } + +- drm_free(orig_from, bytes, DRM_MEM_DRIVER); ++ kfree(orig_from); + if (n == 0) + return 0; + else { diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.36-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.36-buildfix.patch new file mode 100644 index 00000000..ef428983 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.36-buildfix.patch @@ -0,0 +1,27 @@ +--- linux-2.6.35/drivers/gpu/drm/mach64/mach64_state.c.orig 2010-10-03 13:29:10.000000000 +0300 ++++ linux-2.6.35/drivers/gpu/drm/mach64/mach64_state.c 2010-10-03 14:04:45.991164007 +0300 +@@ -41,15 +41,15 @@ + * + */ + struct drm_ioctl_desc mach64_ioctls[] = { +- DRM_IOCTL_DEF(DRM_MACH64_INIT, mach64_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), +- DRM_IOCTL_DEF(DRM_MACH64_CLEAR, mach64_dma_clear, DRM_AUTH), +- DRM_IOCTL_DEF(DRM_MACH64_SWAP, mach64_dma_swap, DRM_AUTH), +- DRM_IOCTL_DEF(DRM_MACH64_IDLE, mach64_dma_idle, DRM_AUTH), +- DRM_IOCTL_DEF(DRM_MACH64_RESET, mach64_engine_reset, DRM_AUTH), +- DRM_IOCTL_DEF(DRM_MACH64_VERTEX, mach64_dma_vertex, DRM_AUTH), +- DRM_IOCTL_DEF(DRM_MACH64_BLIT, mach64_dma_blit, DRM_AUTH), +- DRM_IOCTL_DEF(DRM_MACH64_FLUSH, mach64_dma_flush, DRM_AUTH), +- DRM_IOCTL_DEF(DRM_MACH64_GETPARAM, mach64_get_param, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_INIT, mach64_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), ++ DRM_IOCTL_DEF_DRV(MACH64_CLEAR, mach64_dma_clear, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_SWAP, mach64_dma_swap, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_IDLE, mach64_dma_idle, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_RESET, mach64_engine_reset, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_VERTEX, mach64_dma_vertex, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_BLIT, mach64_dma_blit, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_FLUSH, mach64_dma_flush, DRM_AUTH), ++ DRM_IOCTL_DEF_DRV(MACH64_GETPARAM, mach64_get_param, DRM_AUTH), + }; + + int mach64_max_ioctl = DRM_ARRAY_SIZE(mach64_ioctls); diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.37-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.37-buildfix.patch new file mode 100644 index 00000000..43dc70ec --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.37-buildfix.patch @@ -0,0 +1,24 @@ + +Fix build for 2.6.37 + +Signed-off-by: Thomas Backlund + +--- a/drivers/gpu/drm/mach64/mach64_drv.c.orig 2011-01-02 16:55:48.000000000 +0200 ++++ a/drivers/gpu/drm/mach64/mach64_drv.c 2011-01-02 17:53:54.808775632 +0200 +@@ -51,8 +51,6 @@ static struct drm_driver driver = { + .irq_uninstall = mach64_driver_irq_uninstall, + .irq_handler = mach64_driver_irq_handler, + .reclaim_buffers = drm_core_reclaim_buffers, +- .get_map_ofs = drm_core_get_map_ofs, +- .get_reg_ofs = drm_core_get_reg_ofs, + .ioctls = mach64_ioctls, + .dma_ioctl = mach64_dma_buffers, + .fops = { +@@ -63,6 +61,7 @@ static struct drm_driver driver = { + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, ++ .llseek = noop_llseek, + }, + .pci_driver = { + .name = DRIVER_NAME, diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.0-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.0-buildfix.patch new file mode 100644 index 00000000..9d7538a9 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.0-buildfix.patch @@ -0,0 +1,44 @@ + +fix build with linux-3.0 + +Signed-off-by: Thomas Backlund + + drivers/gpu/drm/mach64/mach64_drv.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/mach64/mach64_drv.c.orig 2011-07-15 02:48:40.000000000 +0300 ++++ a/drivers/gpu/drm/mach64/mach64_drv.c 2011-07-15 13:24:41.454753025 +0300 +@@ -63,10 +63,6 @@ static struct drm_driver driver = { + .fasync = drm_fasync, + .llseek = noop_llseek, + }, +- .pci_driver = { +- .name = DRIVER_NAME, +- .id_table = pciidlist, +- }, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, +@@ -76,15 +72,20 @@ static struct drm_driver driver = { + .patchlevel = DRIVER_PATCHLEVEL, + }; + ++static struct pci_driver mach64_pci_driver = { ++ .name = DRIVER_NAME, ++ .id_table = pciidlist, ++}; ++ + static int __init mach64_init(void) + { + driver.num_ioctls = mach64_max_ioctl; +- return drm_init(&driver); ++ return drm_pci_init(&driver, &mach64_pci_driver); + } + + static void __exit mach64_exit(void) + { +- drm_exit(&driver); ++ drm_pci_exit(&driver, &mach64_pci_driver); + } + + module_init(mach64_init); diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.12-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.12-buildfix.patch new file mode 100644 index 00000000..5ec06698 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.12-buildfix.patch @@ -0,0 +1,17 @@ +--- linux/drivers/gpu/drm/mach64/mach64_drv.c.orig 2013-10-13 12:19:41.000000000 +0300 ++++ linux/drivers/gpu/drm/mach64/mach64_drv.c 2013-10-13 14:27:25.684247911 +0300 +@@ -46,13 +46,12 @@ static const struct file_operations mach + .unlocked_ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, +- .fasync = drm_fasync, + .llseek = noop_llseek, + }; + + static struct drm_driver driver = { + .driver_features = +- DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_HAVE_DMA ++ DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_HAVE_DMA + | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, + .lastclose = mach64_driver_lastclose, + .get_vblank_counter = mach64_get_vblank_counter, diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.17-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.17-buildfix.patch new file mode 100644 index 00000000..6b7b3780 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.17-buildfix.patch @@ -0,0 +1,31 @@ + +Adapt for kernel-3.17 + +DRM_ARRAY_SIZE is a duplicate of ARRAY_SIZE and have been dropped. + +drm_dev_to_irq has been dropped, so call dev->pdev->irq directly +for code simplicity. + +Signed-off-by: Thomas Backlund + +diff -urp linux/drivers/gpu/drm/mach64/mach64_state.c.orig linux/drivers/gpu/drm/mach64/mach64_state.c +--- linux/drivers/gpu/drm/mach64/mach64_state.c.orig ++++ linux/drivers/gpu/drm/mach64/mach64_state.c +@@ -52,7 +52,7 @@ struct drm_ioctl_desc mach64_ioctls[] = + DRM_IOCTL_DEF_DRV(MACH64_GETPARAM, mach64_get_param, DRM_AUTH), + }; + +-int mach64_max_ioctl = DRM_ARRAY_SIZE(mach64_ioctls); ++int mach64_max_ioctl = ARRAY_SIZE(mach64_ioctls); + + /* ================================================================ + * DMA hardware state programming functions +@@ -895,7 +895,7 @@ int mach64_get_param(struct drm_device * + value = mach64_do_get_frames_queued(dev_priv); + break; + case MACH64_PARAM_IRQ_NR: +- value = drm_dev_to_irq(dev); ++ value = dev->pdev->irq; + break; + default: + return -EINVAL; diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.18-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.18-buildfix.patch new file mode 100644 index 00000000..bc0d4606 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.18-buildfix.patch @@ -0,0 +1,142 @@ + +Fix build with kernel 3.18 + +Signed-off-by: Thomas Backlund + +diff -urp linux/drivers/gpu/drm/mach64.orig/mach64_dma.c linux/drivers/gpu/drm/mach64/mach64_dma.c +--- linux/drivers/gpu/drm/mach64.orig/mach64_dma.c 2014-11-29 22:01:54.000000000 +0200 ++++ linux/drivers/gpu/drm/mach64/mach64_dma.c 2014-11-30 00:02:06.906821818 +0200 +@@ -1038,21 +1038,21 @@ static int mach64_do_dma_init(struct drm + INIT_LIST_HEAD(&dev_priv->placeholders); + INIT_LIST_HEAD(&dev_priv->pending); + +- dev_priv->sarea = drm_getsarea(dev); ++ dev_priv->sarea = drm_legacy_getsarea(dev); + if (!dev_priv->sarea) { + DRM_ERROR("can not find sarea!\n"); + dev->dev_private = (void *)dev_priv; + mach64_do_cleanup_dma(dev); + return -EINVAL; + } +- dev_priv->fb = drm_core_findmap(dev, init->fb_offset); ++ dev_priv->fb = drm_legacy_findmap(dev, init->fb_offset); + if (!dev_priv->fb) { + DRM_ERROR("can not find frame buffer map!\n"); + dev->dev_private = (void *)dev_priv; + mach64_do_cleanup_dma(dev); + return -EINVAL; + } +- dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); ++ dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset); + if (!dev_priv->mmio) { + DRM_ERROR("can not find mmio map!\n"); + dev->dev_private = (void *)dev_priv; +@@ -1060,7 +1060,7 @@ static int mach64_do_dma_init(struct drm + return -EINVAL; + } + +- dev_priv->ring_map = drm_core_findmap(dev, init->ring_offset); ++ dev_priv->ring_map = drm_legacy_findmap(dev, init->ring_offset); + if (!dev_priv->ring_map) { + DRM_ERROR("can not find ring map!\n"); + dev->dev_private = (void *)dev_priv; +@@ -1072,7 +1072,7 @@ static int mach64_do_dma_init(struct drm + ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset); + + if (!dev_priv->is_pci) { +- drm_core_ioremap(dev_priv->ring_map, dev); ++ drm_legacy_ioremap(dev_priv->ring_map, dev); + if (!dev_priv->ring_map->handle) { + DRM_ERROR("can not ioremap virtual address for" + " descriptor ring\n"); +@@ -1082,7 +1082,7 @@ static int mach64_do_dma_init(struct drm + } + dev->agp_buffer_token = init->buffers_offset; + dev->agp_buffer_map = +- drm_core_findmap(dev, init->buffers_offset); ++ drm_legacy_findmap(dev, init->buffers_offset); + if (!dev->agp_buffer_map) { + DRM_ERROR("can not find dma buffer map!\n"); + dev->dev_private = (void *)dev_priv; +@@ -1093,7 +1093,7 @@ static int mach64_do_dma_init(struct drm + dev isn't passed all the way though the mach64 - DA */ + dev_priv->dev_buffers = dev->agp_buffer_map; + +- drm_core_ioremap(dev->agp_buffer_map, dev); ++ drm_legacy_ioremap(dev->agp_buffer_map, dev); + if (!dev->agp_buffer_map->handle) { + DRM_ERROR("can not ioremap virtual address for" + " dma buffer\n"); +@@ -1102,7 +1102,7 @@ static int mach64_do_dma_init(struct drm + return -ENOMEM; + } + dev_priv->agp_textures = +- drm_core_findmap(dev, init->agp_textures_offset); ++ drm_legacy_findmap(dev, init->agp_textures_offset); + if (!dev_priv->agp_textures) { + DRM_ERROR("can not find agp texture region!\n"); + dev->dev_private = (void *)dev_priv; +@@ -1376,10 +1376,10 @@ int mach64_do_cleanup_dma(struct drm_dev + + if (!dev_priv->is_pci) { + if (dev_priv->ring_map) +- drm_core_ioremapfree(dev_priv->ring_map, dev); ++ drm_legacy_ioremapfree(dev_priv->ring_map, dev); + + if (dev->agp_buffer_map) { +- drm_core_ioremapfree(dev->agp_buffer_map, dev); ++ drm_legacy_ioremapfree(dev->agp_buffer_map, dev); + dev->agp_buffer_map = NULL; + } + } +diff -urp linux/drivers/gpu/drm/mach64.orig/mach64_drv.c linux/drivers/gpu/drm/mach64/mach64_drv.c +--- linux/drivers/gpu/drm/mach64.orig/mach64_drv.c 2014-11-29 22:01:54.000000000 +0200 ++++ linux/drivers/gpu/drm/mach64/mach64_drv.c 2014-11-29 23:14:21.918009653 +0200 +@@ -34,6 +34,7 @@ + #include "mach64_drv.h" + + #include ++#include + + static struct pci_device_id pciidlist[] = { + mach64_PCI_IDS +@@ -44,7 +45,7 @@ static const struct file_operations mach + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +- .mmap = drm_mmap, ++ .mmap = drm_legacy_mmap, + .poll = drm_poll, + .llseek = noop_llseek, + }; +diff -urp linux/drivers/gpu/drm/mach64.orig/mach64_drv.h linux/drivers/gpu/drm/mach64/mach64_drv.h +--- linux/drivers/gpu/drm/mach64.orig/mach64_drv.h 2014-11-29 22:01:54.000000000 +0200 ++++ linux/drivers/gpu/drm/mach64/mach64_drv.h 2014-11-29 23:58:20.111491646 +0200 +@@ -35,6 +35,8 @@ + #ifndef __MACH64_DRV_H__ + #define __MACH64_DRV_H__ + ++#include ++ + /* General customization: + */ + +@@ -102,12 +104,12 @@ typedef struct drm_mach64_private { + u32 back_offset_pitch; + u32 depth_offset_pitch; + +- drm_local_map_t *sarea; +- drm_local_map_t *fb; +- drm_local_map_t *mmio; +- drm_local_map_t *ring_map; +- drm_local_map_t *dev_buffers; /* this is a pointer to a structure in dev */ +- drm_local_map_t *agp_textures; ++ struct drm_local_map *sarea; ++ struct drm_local_map *fb; ++ struct drm_local_map *mmio; ++ struct drm_local_map *ring_map; ++ struct drm_local_map *dev_buffers; /* this is a pointer to a structure in dev */ ++ struct drm_local_map *agp_textures; + } drm_mach64_private_t; + + extern struct drm_ioctl_desc mach64_ioctls[]; diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.3-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.3-buildfix.patch new file mode 100644 index 00000000..824b9552 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.3-buildfix.patch @@ -0,0 +1,47 @@ + +This moves the .fops to a separate struct as required by kernel-3.3 + +Signed-off-by: Thomas Backlund + + b/drivers/gpu/drm/mach64/mach64_drv.c | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +--- a/drivers/gpu/drm/mach64/mach64_drv.c ++++ b/drivers/gpu/drm/mach64/mach64_drv.c +@@ -39,6 +39,17 @@ static struct pci_device_id pciidlist[] + mach64_PCI_IDS + }; + ++static const struct file_operations mach64_driver_fops = { ++ .owner = THIS_MODULE, ++ .open = drm_open, ++ .release = drm_release, ++ .unlocked_ioctl = drm_ioctl, ++ .mmap = drm_mmap, ++ .poll = drm_poll, ++ .fasync = drm_fasync, ++ .llseek = noop_llseek, ++}; ++ + static struct drm_driver driver = { + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_HAVE_DMA +@@ -54,17 +65,7 @@ static struct drm_driver driver = { + .reclaim_buffers = drm_core_reclaim_buffers, + .ioctls = mach64_ioctls, + .dma_ioctl = mach64_dma_buffers, +- .fops = { +- .owner = THIS_MODULE, +- .open = drm_open, +- .release = drm_release, +- .unlocked_ioctl = drm_ioctl, +- .mmap = drm_mmap, +- .poll = drm_poll, +- .fasync = drm_fasync, +- .llseek = noop_llseek, +- }, +- ++ .fops = &mach64_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.6-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.6-buildfix.patch new file mode 100644 index 00000000..c7df6444 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.6-buildfix.patch @@ -0,0 +1,15 @@ + +reclaim_buffers is gone in 3.6 + +Signed-off-by: Thomas Backlund + +--- linux/drivers/gpu/drm/mach64/mach64_drv.c.orig 2012-10-17 22:26:58.000000000 +0300 ++++ linux/drivers/gpu/drm/mach64/mach64_drv.c 2012-10-17 23:05:10.212566630 +0300 +@@ -62,7 +62,6 @@ static struct drm_driver driver = { + .irq_postinstall = mach64_driver_irq_postinstall, + .irq_uninstall = mach64_driver_irq_uninstall, + .irq_handler = mach64_driver_irq_handler, +- .reclaim_buffers = drm_core_reclaim_buffers, + .ioctls = mach64_ioctls, + .dma_ioctl = mach64_dma_buffers, + .fops = &mach64_driver_fops, diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.7-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.7-buildfix.patch new file mode 100644 index 00000000..cc1c5b27 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.7-buildfix.patch @@ -0,0 +1,61 @@ +diff -Nurp linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_dma.c linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_dma.c +--- linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_dma.c 2012-12-06 19:50:10.000000000 +0200 ++++ linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_dma.c 2012-12-06 20:19:10.590681644 +0200 +@@ -34,8 +34,8 @@ + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +-#include "drmP.h" +-#include "drm.h" ++#include ++#include + #include "mach64_drm.h" + #include "mach64_drv.h" + +diff -Nurp linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_drv.c linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_drv.c +--- linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_drv.c 2012-12-06 19:50:10.000000000 +0200 ++++ linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_drv.c 2012-12-06 20:20:13.062073838 +0200 +@@ -28,12 +28,12 @@ + */ + + #include +-#include "drmP.h" +-#include "drm.h" ++#include ++#include + #include "mach64_drm.h" + #include "mach64_drv.h" + +-#include "drm_pciids.h" ++#include + + static struct pci_device_id pciidlist[] = { + mach64_PCI_IDS +diff -Nurp linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_irq.c linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_irq.c +--- linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_irq.c 2012-12-06 19:50:10.000000000 +0200 ++++ linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_irq.c 2012-12-06 20:20:40.192678476 +0200 +@@ -35,8 +35,8 @@ + * Leif Delgass + */ + +-#include "drmP.h" +-#include "drm.h" ++#include ++#include + #include "mach64_drm.h" + #include "mach64_drv.h" + +diff -Nurp linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_state.c linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_state.c +--- linux-3.7-rc8/drivers/gpu/drm/mach64.orig/mach64_state.c 2012-12-06 19:50:10.000000000 +0200 ++++ linux-3.7-rc8/drivers/gpu/drm/mach64/mach64_state.c 2012-12-06 20:21:00.783137370 +0200 +@@ -30,8 +30,8 @@ + * José Fonseca + */ + +-#include "drmP.h" +-#include "drm.h" ++#include ++#include + #include "mach64_drm.h" + #include "mach64_drv.h" + diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_ioctl.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_ioctl.patch new file mode 100644 index 00000000..951eabb9 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_ioctl.patch @@ -0,0 +1,20 @@ +Update mach64 for changes in drm_ioctl done in 2.6.33: +"drm: convert drm_ioctl to unlocked_ioctl" (commit ed8b670) + +Signed-off-by: Herton Ronaldo Krzesinski +--- + drivers/gpu/drm/mach64/mach64_drv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff -p -up linux-2.6.33/drivers/gpu/drm/mach64/mach64_drv.c.orig linux-2.6.33/drivers/gpu/drm/mach64/mach64_drv.c +--- linux-2.6.33/drivers/gpu/drm/mach64/mach64_drv.c.orig 2010-03-16 19:27:22.517068880 -0300 ++++ linux-2.6.33/drivers/gpu/drm/mach64/mach64_drv.c 2010-03-16 19:27:54.890318416 -0300 +@@ -59,7 +59,7 @@ static struct drm_driver driver = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, +- .ioctl = drm_ioctl, ++ .unlocked_ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_pci_alloc.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_pci_alloc.patch new file mode 100644 index 00000000..b67cecab --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_pci_alloc.patch @@ -0,0 +1,12 @@ +diff -p -up linux-2.6.32/drivers/gpu/drm/mach64/mach64_dma.c.orig linux-2.6.32/drivers/gpu/drm/mach64/mach64_dma.c +--- linux-2.6.32/drivers/gpu/drm/mach64/mach64_dma.c.orig 2010-01-06 11:35:13.784843304 -0200 ++++ linux-2.6.32/drivers/gpu/drm/mach64/mach64_dma.c 2010-01-06 11:37:50.793843502 -0200 +@@ -835,7 +835,7 @@ static int mach64_bm_dma_test(struct drm + /* FIXME: get a dma buffer from the freelist here */ + DRM_DEBUG("Allocating data memory ...\n"); + cpu_addr_dmah = +- drm_pci_alloc(dev, 0x1000, 0x1000, 0xfffffffful); ++ drm_pci_alloc(dev, 0x1000, 0x1000); + if (!cpu_addr_dmah) { + DRM_INFO("data-memory allocation failed!\n"); + return -ENOMEM; diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fixes.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fixes.patch new file mode 100644 index 00000000..0ea07cd7 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fixes.patch @@ -0,0 +1,97 @@ +Fix/cleanup mach64 drm module for building with current kernel. + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + drivers/gpu/drm/mach64/mach64_dma.c | 9 +-------- + drivers/gpu/drm/mach64/mach64_drv.c | 11 +---------- + drivers/gpu/drm/mach64/mach64_state.c | 4 ++-- + 3 files changed, 4 insertions(+), 20 deletions(-) + +diff -p -up linux-2.6.28/drivers/gpu/drm/mach64/mach64_dma.c.orig linux-2.6.28/drivers/gpu/drm/mach64/mach64_dma.c +--- linux-2.6.28/drivers/gpu/drm/mach64/mach64_dma.c.orig 2008-12-10 12:19:14.000000000 -0500 ++++ linux-2.6.28/drivers/gpu/drm/mach64/mach64_dma.c 2008-12-10 13:34:19.000000000 -0500 +@@ -834,14 +834,8 @@ static int mach64_bm_dma_test(struct drm + + /* FIXME: get a dma buffer from the freelist here */ + DRM_DEBUG("Allocating data memory ...\n"); +-#ifdef __FreeBSD__ +- DRM_UNLOCK(); +-#endif + cpu_addr_dmah = + drm_pci_alloc(dev, 0x1000, 0x1000, 0xfffffffful); +-#ifdef __FreeBSD__ +- DRM_LOCK(); +-#endif + if (!cpu_addr_dmah) { + DRM_INFO("data-memory allocation failed!\n"); + return -ENOMEM; +@@ -1375,8 +1369,7 @@ int mach64_do_cleanup_dma(struct drm_dev + * may not have been called from userspace and after dev_private + * is freed, it's too late. + */ +- if (dev->irq) +- drm_irq_uninstall(dev); ++ drm_irq_uninstall(dev); + + if (dev->dev_private) { + drm_mach64_private_t *dev_priv = dev->dev_private; +diff -p -up linux-2.6.28/drivers/gpu/drm/mach64/mach64_drv.c.orig linux-2.6.28/drivers/gpu/drm/mach64/mach64_drv.c +--- linux-2.6.28/drivers/gpu/drm/mach64/mach64_drv.c.orig 2008-12-10 12:13:13.000000000 -0500 ++++ linux-2.6.28/drivers/gpu/drm/mach64/mach64_drv.c 2008-12-10 12:16:54.000000000 -0500 +@@ -38,7 +38,6 @@ static struct pci_device_id pciidlist[] + mach64_PCI_IDS + }; + +-static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); + static struct drm_driver driver = { + .driver_features = + DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_HAVE_DMA +@@ -68,8 +67,6 @@ static struct drm_driver driver = { + .pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +- .probe = probe, +- .remove = __devexit_p(drm_cleanup_pci), + }, + + .name = DRIVER_NAME, +@@ -80,16 +77,10 @@ static struct drm_driver driver = { + .patchlevel = DRIVER_PATCHLEVEL, + }; + +-static int probe(struct pci_dev *pdev, const struct pci_device_id *ent) +-{ +- return drm_get_dev(pdev, ent, &driver); +-} +- +- + static int __init mach64_init(void) + { + driver.num_ioctls = mach64_max_ioctl; +- return drm_init(&driver, pciidlist); ++ return drm_init(&driver); + } + + static void __exit mach64_exit(void) +diff -p -up linux-2.6.28/drivers/gpu/drm/mach64/mach64_state.c.orig linux-2.6.28/drivers/gpu/drm/mach64/mach64_state.c +--- linux-2.6.28/drivers/gpu/drm/mach64/mach64_state.c.orig 2008-12-10 12:13:13.000000000 -0500 ++++ linux-2.6.28/drivers/gpu/drm/mach64/mach64_state.c 2008-12-10 13:38:54.000000000 -0500 +@@ -455,7 +455,7 @@ static int mach64_do_get_frames_queued(d + head = ring->head; + + start = (MACH64_MAX_QUEUED_FRAMES - +- DRM_MIN(MACH64_MAX_QUEUED_FRAMES, sarea_priv->frames_queued)); ++ min(MACH64_MAX_QUEUED_FRAMES, sarea_priv->frames_queued)); + + if (head == tail) { + sarea_priv->frames_queued = 0; +@@ -895,7 +895,7 @@ int mach64_get_param(struct drm_device * + value = mach64_do_get_frames_queued(dev_priv); + break; + case MACH64_PARAM_IRQ_NR: +- value = dev->irq; ++ value = drm_dev_to_irq(dev); + break; + default: + return -EINVAL; diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-include-module.h.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-include-module.h.patch new file mode 100644 index 00000000..9c1163d5 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-include-module.h.patch @@ -0,0 +1,10 @@ +--- linux/drivers/gpu/drm/mach64/mach64_drv.c.orig 2011-12-25 00:48:42.814932355 +0200 ++++ linux/drivers/gpu/drm/mach64/mach64_drv.c 2011-12-25 00:47:43.393625260 +0200 +@@ -27,6 +27,7 @@ + * Leif Delgass + */ + ++#include + #include "drmP.h" + #include "drm.h" + #include "mach64_drm.h" diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-linux-3.14-buildfix.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-linux-3.14-buildfix.patch new file mode 100644 index 00000000..80010d3a --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-linux-3.14-buildfix.patch @@ -0,0 +1,102 @@ + +Fix build with kernel-3.14 + +Signed-off-by: Thomas Backlund + + drivers/gpu/drm/mach64/mach64_dma.c | 10 +++++----- + drivers/gpu/drm/mach64/mach64_drv.h | 2 +- + drivers/gpu/drm/mach64/mach64_irq.c | 2 +- + drivers/gpu/drm/mach64/mach64_state.c | 6 +++--- + 4 files changed, 10 insertions(+), 10 deletions(-) + +diff -urp linux/drivers/gpu/drm/mach64.orig/mach64_dma.c linux/drivers/gpu/drm/mach64/mach64_dma.c +--- linux/drivers/gpu/drm/mach64.orig/mach64_dma.c 2014-04-26 19:10:55.000000000 +0300 ++++ linux/drivers/gpu/drm/mach64/mach64_dma.c 2014-04-26 20:26:05.263501325 +0300 +@@ -696,9 +696,9 @@ do { \ + DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ + _ring_write, _ring_tail ); \ + } \ +- DRM_MEMORYBARRIER(); \ ++ mb(); \ + mach64_clear_dma_eol( &_ring[(_ring_tail - 2) & _ring_mask] ); \ +- DRM_MEMORYBARRIER(); \ ++ mb(); \ + dev_priv->ring.tail = _ring_write; \ + mach64_ring_tick( dev_priv, &(dev_priv)->ring ); \ + } while (0) +@@ -912,7 +912,7 @@ static int mach64_bm_dma_test(struct drm + DRM_DEBUG(" data[%d] = 0x%08x\n", i, data[i]); + } + +- DRM_MEMORYBARRIER(); ++ mb(); + + DRM_DEBUG("waiting for idle...\n"); + if ((i = mach64_do_wait_for_idle(dev_priv))) { +@@ -1716,10 +1716,10 @@ static int mach64_dma_get_buffers(struct + + buf->file_priv = file_priv; + +- if (DRM_COPY_TO_USER(&d->request_indices[i], &buf->idx, ++ if (copy_to_user(&d->request_indices[i], &buf->idx, + sizeof(buf->idx))) + return -EFAULT; +- if (DRM_COPY_TO_USER(&d->request_sizes[i], &buf->total, ++ if (copy_to_user(&d->request_sizes[i], &buf->total, + sizeof(buf->total))) + return -EFAULT; + +diff -urp linux/drivers/gpu/drm/mach64.orig/mach64_drv.h linux/drivers/gpu/drm/mach64/mach64_drv.h +--- linux/drivers/gpu/drm/mach64.orig/mach64_drv.h 2014-04-26 19:10:55.000000000 +0300 ++++ linux/drivers/gpu/drm/mach64/mach64_drv.h 2014-04-26 20:28:43.693457652 +0300 +@@ -166,7 +166,7 @@ extern int mach64_get_param(struct drm_d + extern u32 mach64_get_vblank_counter(struct drm_device *dev, int crtc); + extern int mach64_enable_vblank(struct drm_device *dev, int crtc); + extern void mach64_disable_vblank(struct drm_device *dev, int crtc); +-extern irqreturn_t mach64_driver_irq_handler(DRM_IRQ_ARGS); ++extern irqreturn_t mach64_driver_irq_handler(int irq, void *arg); + extern void mach64_driver_irq_preinstall(struct drm_device *dev); + extern int mach64_driver_irq_postinstall(struct drm_device *dev); + extern void mach64_driver_irq_uninstall(struct drm_device *dev); +diff -urp linux/drivers/gpu/drm/mach64.orig/mach64_irq.c linux/drivers/gpu/drm/mach64/mach64_irq.c +--- linux/drivers/gpu/drm/mach64.orig/mach64_irq.c 2014-04-26 19:10:55.000000000 +0300 ++++ linux/drivers/gpu/drm/mach64/mach64_irq.c 2014-04-26 20:32:01.464898140 +0300 +@@ -40,7 +40,7 @@ + #include "mach64_drm.h" + #include "mach64_drv.h" + +-irqreturn_t mach64_driver_irq_handler(DRM_IRQ_ARGS) ++irqreturn_t mach64_driver_irq_handler(int irq, void *arg) + { + struct drm_device *dev = arg; + drm_mach64_private_t *dev_priv = dev->dev_private; +diff -urp linux/drivers/gpu/drm/mach64.orig/mach64_state.c linux/drivers/gpu/drm/mach64/mach64_state.c +--- linux/drivers/gpu/drm/mach64.orig/mach64_state.c 2014-04-26 19:10:55.000000000 +0300 ++++ linux/drivers/gpu/drm/mach64/mach64_state.c 2014-04-26 20:07:16.701527488 +0300 +@@ -494,7 +494,7 @@ static __inline__ int copy_from_user_ver + if (from == NULL) + return -ENOMEM; + +- if (DRM_COPY_FROM_USER(from, ufrom, bytes)) { ++ if (copy_from_user(from, ufrom, bytes)) { + kfree(from); + return -EFAULT; + } +@@ -640,7 +640,7 @@ static __inline__ int copy_from_user_bli + { + to = (u32 *)((char *)to + MACH64_HOSTDATA_BLIT_OFFSET); + +- if (DRM_COPY_FROM_USER(to, ufrom, bytes)) { ++ if (copy_from_user(to, ufrom, bytes)) { + return -EFAULT; + } + +@@ -901,7 +901,7 @@ int mach64_get_param(struct drm_device * + return -EINVAL; + } + +- if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) { ++ if (copy_to_user(param->value, &value, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return -EFAULT; + } diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-restore-mach64_PCI_IDS.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-restore-mach64_PCI_IDS.patch new file mode 100644 index 00000000..651d7631 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-restore-mach64_PCI_IDS.patch @@ -0,0 +1,38 @@ +Theese got dropped in 3.12-rc + +Restore for now to get the mach64 driver to build + +TODO: Need to check mesa & drm if it's dropped there too... + +--- linux/include/drm/drm_pciids.h.orig 2013-10-13 11:05:33.000000000 +0300 ++++ linux/include/drm/drm_pciids.h 2013-10-13 14:30:43.625995248 +0300 +@@ -711,6 +711,29 @@ + {0x102b, 0x2527, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MGA_CARD_TYPE_G550}, \ + {0, 0, 0} + ++#define mach64_PCI_IDS \ ++ {0x1002, 0x4749, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x474c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x474f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4752, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4753, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x474d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x474e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0x1002, 0x4c4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ ++ {0, 0, 0} ++ + #define sisdrv_PCI_IDS \ + {0x1039, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x1039, 0x5300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ diff --git a/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64.patch b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64.patch new file mode 100644 index 00000000..be3e1631 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/gpu-drm-mach64.patch @@ -0,0 +1,4144 @@ +mach64 driver from git://anongit.freedesktop.org/git/mesa/drm +head: c99566fb810c9d8cae5e9cd39d1772b55e2f514c + +--- + drivers/gpu/drm/Kconfig | 11 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/mach64/Makefile | 8 + drivers/gpu/drm/Kconfig | 11 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/mach64/Makefile | 8 + drivers/gpu/drm/mach64/mach64_dma.c | 1778 ++++++++++++++++++++++++++++++++++ + drivers/gpu/drm/mach64/mach64_drm.h | 256 ++++ + drivers/gpu/drm/mach64/mach64_drv.c | 105 ++ + drivers/gpu/drm/mach64/mach64_drv.h | 859 ++++++++++++++++ + drivers/gpu/drm/mach64/mach64_irq.c | 159 +++ + drivers/gpu/drm/mach64/mach64_state.c | 910 +++++++++++++++++ + 9 files changed, 4087 insertions(+) + +--- /dev/null ++++ b/drivers/gpu/drm/mach64/Makefile +@@ -0,0 +1,8 @@ ++# ++# Makefile for the drm device driver. This driver provides support for the ++# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. ++ ++ccflags-y = -Iinclude/drm ++mach64-y := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o ++ ++obj-$(CONFIG_DRM_MACH64) += mach64.o +--- /dev/null ++++ b/drivers/gpu/drm/mach64/mach64_dma.c +@@ -0,0 +1,1778 @@ ++/* mach64_dma.c -- DMA support for mach64 (Rage Pro) driver -*- linux-c -*- */ ++/** ++ * \file mach64_dma.c ++ * DMA support for mach64 (Rage Pro) driver ++ * ++ * \author Gareth Hughes ++ * \author Frank C. Earl ++ * \author Leif Delgass ++ * \author José Fonseca ++ */ ++ ++/* ++ * Copyright 2000 Gareth Hughes ++ * Copyright 2002 Frank C. Earl ++ * Copyright 2002-2003 Leif Delgass ++ * All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++#include "drmP.h" ++#include "drm.h" ++#include "mach64_drm.h" ++#include "mach64_drv.h" ++ ++/*******************************************************************/ ++/** \name Engine, FIFO control */ ++/*@{*/ ++ ++/** ++ * Waits for free entries in the FIFO. ++ * ++ * \note Most writes to Mach64 registers are automatically routed through ++ * command FIFO which is 16 entry deep. Prior to writing to any draw engine ++ * register one has to ensure that enough FIFO entries are available by calling ++ * this function. Failure to do so may cause the engine to lock. ++ * ++ * \param dev_priv pointer to device private data structure. ++ * \param entries number of free entries in the FIFO to wait for. ++ * ++ * \returns zero on success, or -EBUSY if the timeout (specificed by ++ * drm_mach64_private::usec_timeout) occurs. ++ */ ++int mach64_do_wait_for_fifo(drm_mach64_private_t *dev_priv, int entries) ++{ ++ int slots = 0, i; ++ ++ for (i = 0; i < dev_priv->usec_timeout; i++) { ++ slots = (MACH64_READ(MACH64_FIFO_STAT) & MACH64_FIFO_SLOT_MASK); ++ if (slots <= (0x8000 >> entries)) ++ return 0; ++ DRM_UDELAY(1); ++ } ++ ++ DRM_INFO("failed! slots=%d entries=%d\n", slots, entries); ++ return -EBUSY; ++} ++ ++/** ++ * Wait for the draw engine to be idle. ++ */ ++int mach64_do_wait_for_idle(drm_mach64_private_t *dev_priv) ++{ ++ int i, ret; ++ ++ ret = mach64_do_wait_for_fifo(dev_priv, 16); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < dev_priv->usec_timeout; i++) { ++ if (!(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE)) ++ return 0; ++ DRM_UDELAY(1); ++ } ++ ++ DRM_INFO("failed! GUI_STAT=0x%08x\n", MACH64_READ(MACH64_GUI_STAT)); ++ mach64_dump_ring_info(dev_priv); ++ return -EBUSY; ++} ++ ++/** ++ * Wait for free entries in the ring buffer. ++ * ++ * The Mach64 bus master can be configured to act as a virtual FIFO, using a ++ * circular buffer (commonly referred as "ring buffer" in other drivers) with ++ * pointers to engine commands. This allows the CPU to do other things while ++ * the graphics engine is busy, i.e., DMA mode. ++ * ++ * This function should be called before writing new entries to the ring ++ * buffer. ++ * ++ * \param dev_priv pointer to device private data structure. ++ * \param n number of free entries in the ring buffer to wait for. ++ * ++ * \returns zero on success, or -EBUSY if the timeout (specificed by ++ * drm_mach64_private_t::usec_timeout) occurs. ++ * ++ * \sa mach64_dump_ring_info() ++ */ ++int mach64_wait_ring(drm_mach64_private_t *dev_priv, int n) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ int i; ++ ++ for (i = 0; i < dev_priv->usec_timeout; i++) { ++ mach64_update_ring_snapshot(dev_priv); ++ if (ring->space >= n) { ++ if (i > 0) ++ DRM_DEBUG("%d usecs\n", i); ++ return 0; ++ } ++ DRM_UDELAY(1); ++ } ++ ++ /* FIXME: This is being ignored... */ ++ DRM_ERROR("failed!\n"); ++ mach64_dump_ring_info(dev_priv); ++ return -EBUSY; ++} ++ ++/** ++ * Wait until all DMA requests have been processed... ++ * ++ * \sa mach64_wait_ring() ++ */ ++static int mach64_ring_idle(drm_mach64_private_t *dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ u32 head; ++ int i; ++ ++ head = ring->head; ++ i = 0; ++ while (i < dev_priv->usec_timeout) { ++ mach64_update_ring_snapshot(dev_priv); ++ if (ring->head == ring->tail && ++ !(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE)) { ++ if (i > 0) ++ DRM_DEBUG("%d usecs\n", i); ++ return 0; ++ } ++ if (ring->head == head) { ++ ++i; ++ } else { ++ head = ring->head; ++ i = 0; ++ } ++ DRM_UDELAY(1); ++ } ++ ++ DRM_INFO("failed! GUI_STAT=0x%08x\n", MACH64_READ(MACH64_GUI_STAT)); ++ mach64_dump_ring_info(dev_priv); ++ return -EBUSY; ++} ++ ++/** ++ * Reset the the ring buffer descriptors. ++ * ++ * \sa mach64_do_engine_reset() ++ */ ++static void mach64_ring_reset(drm_mach64_private_t *dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ ++ mach64_do_release_used_buffers(dev_priv); ++ ring->head_addr = ring->start_addr; ++ ring->head = ring->tail = 0; ++ ring->space = ring->size; ++ ++ MACH64_WRITE(MACH64_BM_GUI_TABLE_CMD, ++ ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB); ++ ++ dev_priv->ring_running = 0; ++} ++ ++/** ++ * Ensure the all the queued commands will be processed. ++ */ ++int mach64_do_dma_flush(drm_mach64_private_t *dev_priv) ++{ ++ /* FIXME: It's not necessary to wait for idle when flushing ++ * we just need to ensure the ring will be completely processed ++ * in finite time without another ioctl ++ */ ++ return mach64_ring_idle(dev_priv); ++} ++ ++/** ++ * Stop all DMA activity. ++ */ ++int mach64_do_dma_idle(drm_mach64_private_t *dev_priv) ++{ ++ int ret; ++ ++ /* wait for completion */ ++ if ((ret = mach64_ring_idle(dev_priv)) < 0) { ++ DRM_ERROR("failed BM_GUI_TABLE=0x%08x tail: %u\n", ++ MACH64_READ(MACH64_BM_GUI_TABLE), ++ dev_priv->ring.tail); ++ return ret; ++ } ++ ++ mach64_ring_stop(dev_priv); ++ ++ /* clean up after pass */ ++ mach64_do_release_used_buffers(dev_priv); ++ return 0; ++} ++ ++/** ++ * Reset the engine. This will stop the DMA if it is running. ++ */ ++int mach64_do_engine_reset(drm_mach64_private_t *dev_priv) ++{ ++ u32 tmp; ++ ++ DRM_DEBUG("\n"); ++ ++ /* Kill off any outstanding DMA transfers. ++ */ ++ tmp = MACH64_READ(MACH64_BUS_CNTL); ++ MACH64_WRITE(MACH64_BUS_CNTL, tmp | MACH64_BUS_MASTER_DIS); ++ ++ /* Reset the GUI engine (high to low transition). ++ */ ++ tmp = MACH64_READ(MACH64_GEN_TEST_CNTL); ++ MACH64_WRITE(MACH64_GEN_TEST_CNTL, tmp & ~MACH64_GUI_ENGINE_ENABLE); ++ /* Enable the GUI engine ++ */ ++ tmp = MACH64_READ(MACH64_GEN_TEST_CNTL); ++ MACH64_WRITE(MACH64_GEN_TEST_CNTL, tmp | MACH64_GUI_ENGINE_ENABLE); ++ ++ /* ensure engine is not locked up by clearing any FIFO or HOST errors ++ */ ++ tmp = MACH64_READ(MACH64_BUS_CNTL); ++ MACH64_WRITE(MACH64_BUS_CNTL, tmp | 0x00a00000); ++ ++ /* Once GUI engine is restored, disable bus mastering */ ++ MACH64_WRITE(MACH64_SRC_CNTL, 0); ++ ++ /* Reset descriptor ring */ ++ mach64_ring_reset(dev_priv); ++ ++ return 0; ++} ++ ++/*@}*/ ++ ++ ++/*******************************************************************/ ++/** \name Debugging output */ ++/*@{*/ ++ ++/** ++ * Dump engine registers values. ++ */ ++void mach64_dump_engine_info(drm_mach64_private_t *dev_priv) ++{ ++ DRM_INFO("\n"); ++ if (!dev_priv->is_pci) { ++ DRM_INFO(" AGP_BASE = 0x%08x\n", ++ MACH64_READ(MACH64_AGP_BASE)); ++ DRM_INFO(" AGP_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_AGP_CNTL)); ++ } ++ DRM_INFO(" ALPHA_TST_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_ALPHA_TST_CNTL)); ++ DRM_INFO("\n"); ++ DRM_INFO(" BM_COMMAND = 0x%08x\n", ++ MACH64_READ(MACH64_BM_COMMAND)); ++ DRM_INFO("BM_FRAME_BUF_OFFSET = 0x%08x\n", ++ MACH64_READ(MACH64_BM_FRAME_BUF_OFFSET)); ++ DRM_INFO(" BM_GUI_TABLE = 0x%08x\n", ++ MACH64_READ(MACH64_BM_GUI_TABLE)); ++ DRM_INFO(" BM_STATUS = 0x%08x\n", ++ MACH64_READ(MACH64_BM_STATUS)); ++ DRM_INFO(" BM_SYSTEM_MEM_ADDR = 0x%08x\n", ++ MACH64_READ(MACH64_BM_SYSTEM_MEM_ADDR)); ++ DRM_INFO(" BM_SYSTEM_TABLE = 0x%08x\n", ++ MACH64_READ(MACH64_BM_SYSTEM_TABLE)); ++ DRM_INFO(" BUS_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_BUS_CNTL)); ++ DRM_INFO("\n"); ++ /* DRM_INFO( " CLOCK_CNTL = 0x%08x\n", MACH64_READ( MACH64_CLOCK_CNTL ) ); */ ++ DRM_INFO(" CLR_CMP_CLR = 0x%08x\n", ++ MACH64_READ(MACH64_CLR_CMP_CLR)); ++ DRM_INFO(" CLR_CMP_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_CLR_CMP_CNTL)); ++ /* DRM_INFO( " CLR_CMP_MSK = 0x%08x\n", MACH64_READ( MACH64_CLR_CMP_MSK ) ); */ ++ DRM_INFO(" CONFIG_CHIP_ID = 0x%08x\n", ++ MACH64_READ(MACH64_CONFIG_CHIP_ID)); ++ DRM_INFO(" CONFIG_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_CONFIG_CNTL)); ++ DRM_INFO(" CONFIG_STAT0 = 0x%08x\n", ++ MACH64_READ(MACH64_CONFIG_STAT0)); ++ DRM_INFO(" CONFIG_STAT1 = 0x%08x\n", ++ MACH64_READ(MACH64_CONFIG_STAT1)); ++ DRM_INFO(" CONFIG_STAT2 = 0x%08x\n", ++ MACH64_READ(MACH64_CONFIG_STAT2)); ++ DRM_INFO(" CRC_SIG = 0x%08x\n", MACH64_READ(MACH64_CRC_SIG)); ++ DRM_INFO(" CUSTOM_MACRO_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_CUSTOM_MACRO_CNTL)); ++ DRM_INFO("\n"); ++ /* DRM_INFO( " DAC_CNTL = 0x%08x\n", MACH64_READ( MACH64_DAC_CNTL ) ); */ ++ /* DRM_INFO( " DAC_REGS = 0x%08x\n", MACH64_READ( MACH64_DAC_REGS ) ); */ ++ DRM_INFO(" DP_BKGD_CLR = 0x%08x\n", ++ MACH64_READ(MACH64_DP_BKGD_CLR)); ++ DRM_INFO(" DP_FRGD_CLR = 0x%08x\n", ++ MACH64_READ(MACH64_DP_FRGD_CLR)); ++ DRM_INFO(" DP_MIX = 0x%08x\n", MACH64_READ(MACH64_DP_MIX)); ++ DRM_INFO(" DP_PIX_WIDTH = 0x%08x\n", ++ MACH64_READ(MACH64_DP_PIX_WIDTH)); ++ DRM_INFO(" DP_SRC = 0x%08x\n", MACH64_READ(MACH64_DP_SRC)); ++ DRM_INFO(" DP_WRITE_MASK = 0x%08x\n", ++ MACH64_READ(MACH64_DP_WRITE_MASK)); ++ DRM_INFO(" DSP_CONFIG = 0x%08x\n", ++ MACH64_READ(MACH64_DSP_CONFIG)); ++ DRM_INFO(" DSP_ON_OFF = 0x%08x\n", ++ MACH64_READ(MACH64_DSP_ON_OFF)); ++ DRM_INFO(" DST_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_DST_CNTL)); ++ DRM_INFO(" DST_OFF_PITCH = 0x%08x\n", ++ MACH64_READ(MACH64_DST_OFF_PITCH)); ++ DRM_INFO("\n"); ++ /* DRM_INFO( " EXT_DAC_REGS = 0x%08x\n", MACH64_READ( MACH64_EXT_DAC_REGS ) ); */ ++ DRM_INFO(" EXT_MEM_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_EXT_MEM_CNTL)); ++ DRM_INFO("\n"); ++ DRM_INFO(" FIFO_STAT = 0x%08x\n", ++ MACH64_READ(MACH64_FIFO_STAT)); ++ DRM_INFO("\n"); ++ DRM_INFO(" GEN_TEST_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_GEN_TEST_CNTL)); ++ /* DRM_INFO( " GP_IO = 0x%08x\n", MACH64_READ( MACH64_GP_IO ) ); */ ++ DRM_INFO(" GUI_CMDFIFO_DATA = 0x%08x\n", ++ MACH64_READ(MACH64_GUI_CMDFIFO_DATA)); ++ DRM_INFO(" GUI_CMDFIFO_DEBUG = 0x%08x\n", ++ MACH64_READ(MACH64_GUI_CMDFIFO_DEBUG)); ++ DRM_INFO(" GUI_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_GUI_CNTL)); ++ DRM_INFO(" GUI_STAT = 0x%08x\n", ++ MACH64_READ(MACH64_GUI_STAT)); ++ DRM_INFO(" GUI_TRAJ_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_GUI_TRAJ_CNTL)); ++ DRM_INFO("\n"); ++ DRM_INFO(" HOST_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_HOST_CNTL)); ++ DRM_INFO(" HW_DEBUG = 0x%08x\n", ++ MACH64_READ(MACH64_HW_DEBUG)); ++ DRM_INFO("\n"); ++ DRM_INFO(" MEM_ADDR_CONFIG = 0x%08x\n", ++ MACH64_READ(MACH64_MEM_ADDR_CONFIG)); ++ DRM_INFO(" MEM_BUF_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_MEM_BUF_CNTL)); ++ DRM_INFO("\n"); ++ DRM_INFO(" PAT_REG0 = 0x%08x\n", ++ MACH64_READ(MACH64_PAT_REG0)); ++ DRM_INFO(" PAT_REG1 = 0x%08x\n", ++ MACH64_READ(MACH64_PAT_REG1)); ++ DRM_INFO("\n"); ++ DRM_INFO(" SC_LEFT = 0x%08x\n", MACH64_READ(MACH64_SC_LEFT)); ++ DRM_INFO(" SC_RIGHT = 0x%08x\n", ++ MACH64_READ(MACH64_SC_RIGHT)); ++ DRM_INFO(" SC_TOP = 0x%08x\n", MACH64_READ(MACH64_SC_TOP)); ++ DRM_INFO(" SC_BOTTOM = 0x%08x\n", ++ MACH64_READ(MACH64_SC_BOTTOM)); ++ DRM_INFO("\n"); ++ DRM_INFO(" SCALE_3D_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_SCALE_3D_CNTL)); ++ DRM_INFO(" SCRATCH_REG0 = 0x%08x\n", ++ MACH64_READ(MACH64_SCRATCH_REG0)); ++ DRM_INFO(" SCRATCH_REG1 = 0x%08x\n", ++ MACH64_READ(MACH64_SCRATCH_REG1)); ++ DRM_INFO(" SETUP_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_SETUP_CNTL)); ++ DRM_INFO(" SRC_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_SRC_CNTL)); ++ DRM_INFO("\n"); ++ DRM_INFO(" TEX_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_TEX_CNTL)); ++ DRM_INFO(" TEX_SIZE_PITCH = 0x%08x\n", ++ MACH64_READ(MACH64_TEX_SIZE_PITCH)); ++ DRM_INFO(" TIMER_CONFIG = 0x%08x\n", ++ MACH64_READ(MACH64_TIMER_CONFIG)); ++ DRM_INFO("\n"); ++ DRM_INFO(" Z_CNTL = 0x%08x\n", MACH64_READ(MACH64_Z_CNTL)); ++ DRM_INFO(" Z_OFF_PITCH = 0x%08x\n", ++ MACH64_READ(MACH64_Z_OFF_PITCH)); ++ DRM_INFO("\n"); ++} ++ ++#define MACH64_DUMP_CONTEXT 3 ++ ++/** ++ * Used by mach64_dump_ring_info() to dump the contents of the current buffer ++ * pointed by the ring head. ++ */ ++static void mach64_dump_buf_info(drm_mach64_private_t *dev_priv, ++ struct drm_buf *buf) ++{ ++ u32 addr = GETBUFADDR(buf); ++ u32 used = buf->used >> 2; ++ u32 sys_addr = MACH64_READ(MACH64_BM_SYSTEM_MEM_ADDR); ++ u32 *p = GETBUFPTR(buf); ++ int skipped = 0; ++ ++ DRM_INFO("buffer contents:\n"); ++ ++ while (used) { ++ u32 reg, count; ++ ++ reg = le32_to_cpu(*p++); ++ if (addr <= GETBUFADDR(buf) + MACH64_DUMP_CONTEXT * 4 || ++ (addr >= sys_addr - MACH64_DUMP_CONTEXT * 4 && ++ addr <= sys_addr + MACH64_DUMP_CONTEXT * 4) || ++ addr >= ++ GETBUFADDR(buf) + buf->used - MACH64_DUMP_CONTEXT * 4) { ++ DRM_INFO("%08x: 0x%08x\n", addr, reg); ++ } ++ addr += 4; ++ used--; ++ ++ count = (reg >> 16) + 1; ++ reg = reg & 0xffff; ++ reg = MMSELECT(reg); ++ while (count && used) { ++ if (addr <= GETBUFADDR(buf) + MACH64_DUMP_CONTEXT * 4 || ++ (addr >= sys_addr - MACH64_DUMP_CONTEXT * 4 && ++ addr <= sys_addr + MACH64_DUMP_CONTEXT * 4) || ++ addr >= ++ GETBUFADDR(buf) + buf->used - ++ MACH64_DUMP_CONTEXT * 4) { ++ DRM_INFO("%08x: 0x%04x = 0x%08x\n", addr, ++ reg, le32_to_cpu(*p)); ++ skipped = 0; ++ } else { ++ if (!skipped) { ++ DRM_INFO(" ...\n"); ++ skipped = 1; ++ } ++ } ++ p++; ++ addr += 4; ++ used--; ++ ++ reg += 4; ++ count--; ++ } ++ } ++ ++ DRM_INFO("\n"); ++} ++ ++/** ++ * Dump the ring state and contents, including the contents of the buffer being ++ * processed by the graphics engine. ++ */ ++void mach64_dump_ring_info(drm_mach64_private_t *dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ int i, skipped; ++ ++ DRM_INFO("\n"); ++ ++ DRM_INFO("ring contents:\n"); ++ DRM_INFO(" head_addr: 0x%08x head: %u tail: %u\n\n", ++ ring->head_addr, ring->head, ring->tail); ++ ++ skipped = 0; ++ for (i = 0; i < ring->size / sizeof(u32); i += 4) { ++ if (i <= MACH64_DUMP_CONTEXT * 4 || ++ i >= ring->size / sizeof(u32) - MACH64_DUMP_CONTEXT * 4 || ++ (i >= ring->tail - MACH64_DUMP_CONTEXT * 4 && ++ i <= ring->tail + MACH64_DUMP_CONTEXT * 4) || ++ (i >= ring->head - MACH64_DUMP_CONTEXT * 4 && ++ i <= ring->head + MACH64_DUMP_CONTEXT * 4)) { ++ DRM_INFO(" 0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x%s%s\n", ++ (u32)(ring->start_addr + i * sizeof(u32)), ++ le32_to_cpu(((u32 *) ring->start)[i + 0]), ++ le32_to_cpu(((u32 *) ring->start)[i + 1]), ++ le32_to_cpu(((u32 *) ring->start)[i + 2]), ++ le32_to_cpu(((u32 *) ring->start)[i + 3]), ++ i == ring->head ? " (head)" : "", ++ i == ring->tail ? " (tail)" : ""); ++ skipped = 0; ++ } else { ++ if (!skipped) { ++ DRM_INFO(" ...\n"); ++ skipped = 1; ++ } ++ } ++ } ++ ++ DRM_INFO("\n"); ++ ++ if (ring->head >= 0 && ring->head < ring->size / sizeof(u32)) { ++ struct list_head *ptr; ++ u32 addr = le32_to_cpu(((u32 *) ring->start)[ring->head + 1]); ++ ++ list_for_each(ptr, &dev_priv->pending) { ++ drm_mach64_freelist_t *entry = ++ list_entry(ptr, drm_mach64_freelist_t, list); ++ struct drm_buf *buf = entry->buf; ++ ++ u32 buf_addr = GETBUFADDR(buf); ++ ++ if (buf_addr <= addr && addr < buf_addr + buf->used) ++ mach64_dump_buf_info(dev_priv, buf); ++ } ++ } ++ ++ DRM_INFO("\n"); ++ DRM_INFO(" BM_GUI_TABLE = 0x%08x\n", ++ MACH64_READ(MACH64_BM_GUI_TABLE)); ++ DRM_INFO("\n"); ++ DRM_INFO("BM_FRAME_BUF_OFFSET = 0x%08x\n", ++ MACH64_READ(MACH64_BM_FRAME_BUF_OFFSET)); ++ DRM_INFO(" BM_SYSTEM_MEM_ADDR = 0x%08x\n", ++ MACH64_READ(MACH64_BM_SYSTEM_MEM_ADDR)); ++ DRM_INFO(" BM_COMMAND = 0x%08x\n", ++ MACH64_READ(MACH64_BM_COMMAND)); ++ DRM_INFO("\n"); ++ DRM_INFO(" BM_STATUS = 0x%08x\n", ++ MACH64_READ(MACH64_BM_STATUS)); ++ DRM_INFO(" BUS_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_BUS_CNTL)); ++ DRM_INFO(" FIFO_STAT = 0x%08x\n", ++ MACH64_READ(MACH64_FIFO_STAT)); ++ DRM_INFO(" GUI_STAT = 0x%08x\n", ++ MACH64_READ(MACH64_GUI_STAT)); ++ DRM_INFO(" SRC_CNTL = 0x%08x\n", ++ MACH64_READ(MACH64_SRC_CNTL)); ++} ++ ++/*@}*/ ++ ++ ++/*******************************************************************/ ++/** \name DMA descriptor ring macros */ ++/*@{*/ ++ ++/** ++ * Add the end mark to the ring's new tail position. ++ * ++ * The bus master engine will keep processing the DMA buffers listed in the ring ++ * until it finds this mark, making it stop. ++ * ++ * \sa mach64_clear_dma_eol ++ */ ++static __inline__ void mach64_set_dma_eol(volatile u32 *addr) ++{ ++#if defined(__i386__) ++ int nr = 31; ++ ++ /* Taken from include/asm-i386/bitops.h linux header */ ++ __asm__ __volatile__("lock;" "btsl %1,%0":"=m"(*addr) ++ :"Ir"(nr)); ++#elif defined(__powerpc__) ++ u32 old; ++ u32 mask = cpu_to_le32(MACH64_DMA_EOL); ++ ++ /* Taken from the include/asm-ppc/bitops.h linux header */ ++ __asm__ __volatile__("\n\ ++1: lwarx %0,0,%3 \n\ ++ or %0,%0,%2 \n\ ++ stwcx. %0,0,%3 \n\ ++ bne- 1b":"=&r"(old), "=m"(*addr) ++ :"r"(mask), "r"(addr), "m"(*addr) ++ :"cc"); ++#elif defined(__alpha__) ++ u32 temp; ++ u32 mask = MACH64_DMA_EOL; ++ ++ /* Taken from the include/asm-alpha/bitops.h linux header */ ++ __asm__ __volatile__("1: ldl_l %0,%3\n" ++ " bis %0,%2,%0\n" ++ " stl_c %0,%1\n" ++ " beq %0,2f\n" ++ ".subsection 2\n" ++ "2: br 1b\n" ++ ".previous":"=&r"(temp), "=m"(*addr) ++ :"Ir"(mask), "m"(*addr)); ++#else ++ u32 mask = cpu_to_le32(MACH64_DMA_EOL); ++ ++ *addr |= mask; ++#endif ++} ++ ++/** ++ * Remove the end mark from the ring's old tail position. ++ * ++ * It should be called after calling mach64_set_dma_eol to mark the ring's new ++ * tail position. ++ * ++ * We update the end marks while the bus master engine is in operation. Since ++ * the bus master engine may potentially be reading from the same position ++ * that we write, we must change atomically to avoid having intermediary bad ++ * data. ++ */ ++static __inline__ void mach64_clear_dma_eol(volatile u32 *addr) ++{ ++#if defined(__i386__) ++ int nr = 31; ++ ++ /* Taken from include/asm-i386/bitops.h linux header */ ++ __asm__ __volatile__("lock;" "btrl %1,%0":"=m"(*addr) ++ :"Ir"(nr)); ++#elif defined(__powerpc__) ++ u32 old; ++ u32 mask = cpu_to_le32(MACH64_DMA_EOL); ++ ++ /* Taken from the include/asm-ppc/bitops.h linux header */ ++ __asm__ __volatile__("\n\ ++1: lwarx %0,0,%3 \n\ ++ andc %0,%0,%2 \n\ ++ stwcx. %0,0,%3 \n\ ++ bne- 1b":"=&r"(old), "=m"(*addr) ++ :"r"(mask), "r"(addr), "m"(*addr) ++ :"cc"); ++#elif defined(__alpha__) ++ u32 temp; ++ u32 mask = ~MACH64_DMA_EOL; ++ ++ /* Taken from the include/asm-alpha/bitops.h linux header */ ++ __asm__ __volatile__("1: ldl_l %0,%3\n" ++ " and %0,%2,%0\n" ++ " stl_c %0,%1\n" ++ " beq %0,2f\n" ++ ".subsection 2\n" ++ "2: br 1b\n" ++ ".previous":"=&r"(temp), "=m"(*addr) ++ :"Ir"(mask), "m"(*addr)); ++#else ++ u32 mask = cpu_to_le32(~MACH64_DMA_EOL); ++ ++ *addr &= mask; ++#endif ++} ++ ++#define RING_LOCALS \ ++ int _ring_tail, _ring_write; unsigned int _ring_mask; volatile u32 *_ring ++ ++#define RING_WRITE_OFS _ring_write ++ ++#define BEGIN_RING(n) \ ++ do { \ ++ if (MACH64_VERBOSE) { \ ++ DRM_INFO( "BEGIN_RING( %d ) \n", \ ++ (n) ); \ ++ } \ ++ if (dev_priv->ring.space <= (n) * sizeof(u32)) { \ ++ int ret; \ ++ if ((ret = mach64_wait_ring( dev_priv, (n) * sizeof(u32))) < 0 ) { \ ++ DRM_ERROR( "wait_ring failed, resetting engine\n"); \ ++ mach64_dump_engine_info( dev_priv ); \ ++ mach64_do_engine_reset( dev_priv ); \ ++ return ret; \ ++ } \ ++ } \ ++ dev_priv->ring.space -= (n) * sizeof(u32); \ ++ _ring = (u32 *) dev_priv->ring.start; \ ++ _ring_tail = _ring_write = dev_priv->ring.tail; \ ++ _ring_mask = dev_priv->ring.tail_mask; \ ++ } while (0) ++ ++#define OUT_RING( x ) \ ++do { \ ++ if (MACH64_VERBOSE) { \ ++ DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \ ++ (unsigned int)(x), _ring_write ); \ ++ } \ ++ _ring[_ring_write++] = cpu_to_le32( x ); \ ++ _ring_write &= _ring_mask; \ ++} while (0) ++ ++#define ADVANCE_RING() \ ++do { \ ++ if (MACH64_VERBOSE) { \ ++ DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ ++ _ring_write, _ring_tail ); \ ++ } \ ++ DRM_MEMORYBARRIER(); \ ++ mach64_clear_dma_eol( &_ring[(_ring_tail - 2) & _ring_mask] ); \ ++ DRM_MEMORYBARRIER(); \ ++ dev_priv->ring.tail = _ring_write; \ ++ mach64_ring_tick( dev_priv, &(dev_priv)->ring ); \ ++} while (0) ++ ++/** ++ * Queue a DMA buffer of registers writes into the ring buffer. ++ */ ++int mach64_add_buf_to_ring(drm_mach64_private_t *dev_priv, ++ drm_mach64_freelist_t *entry) ++{ ++ int bytes, pages, remainder; ++ u32 address, page; ++ int i; ++ struct drm_buf *buf = entry->buf; ++ RING_LOCALS; ++ ++ bytes = buf->used; ++ address = GETBUFADDR( buf ); ++ pages = (bytes + MACH64_DMA_CHUNKSIZE - 1) / MACH64_DMA_CHUNKSIZE; ++ ++ BEGIN_RING( pages * 4 ); ++ ++ for ( i = 0 ; i < pages-1 ; i++ ) { ++ page = address + i * MACH64_DMA_CHUNKSIZE; ++ OUT_RING( MACH64_APERTURE_OFFSET + MACH64_BM_ADDR ); ++ OUT_RING( page ); ++ OUT_RING( MACH64_DMA_CHUNKSIZE | MACH64_DMA_HOLD_OFFSET ); ++ OUT_RING( 0 ); ++ } ++ ++ /* generate the final descriptor for any remaining commands in this buffer */ ++ page = address + i * MACH64_DMA_CHUNKSIZE; ++ remainder = bytes - i * MACH64_DMA_CHUNKSIZE; ++ ++ /* Save dword offset of last descriptor for this buffer. ++ * This is needed to check for completion of the buffer in freelist_get ++ */ ++ entry->ring_ofs = RING_WRITE_OFS; ++ ++ OUT_RING( MACH64_APERTURE_OFFSET + MACH64_BM_ADDR ); ++ OUT_RING( page ); ++ OUT_RING( remainder | MACH64_DMA_HOLD_OFFSET | MACH64_DMA_EOL ); ++ OUT_RING( 0 ); ++ ++ ADVANCE_RING(); ++ ++ return 0; ++} ++ ++/** ++ * Queue DMA buffer controlling host data tranfers (e.g., blit). ++ * ++ * Almost identical to mach64_add_buf_to_ring. ++ */ ++int mach64_add_hostdata_buf_to_ring(drm_mach64_private_t *dev_priv, ++ drm_mach64_freelist_t *entry) ++{ ++ int bytes, pages, remainder; ++ u32 address, page; ++ int i; ++ struct drm_buf *buf = entry->buf; ++ RING_LOCALS; ++ ++ bytes = buf->used - MACH64_HOSTDATA_BLIT_OFFSET; ++ pages = (bytes + MACH64_DMA_CHUNKSIZE - 1) / MACH64_DMA_CHUNKSIZE; ++ address = GETBUFADDR( buf ); ++ ++ BEGIN_RING( 4 + pages * 4 ); ++ ++ OUT_RING( MACH64_APERTURE_OFFSET + MACH64_BM_ADDR ); ++ OUT_RING( address ); ++ OUT_RING( MACH64_HOSTDATA_BLIT_OFFSET | MACH64_DMA_HOLD_OFFSET ); ++ OUT_RING( 0 ); ++ address += MACH64_HOSTDATA_BLIT_OFFSET; ++ ++ for ( i = 0 ; i < pages-1 ; i++ ) { ++ page = address + i * MACH64_DMA_CHUNKSIZE; ++ OUT_RING( MACH64_APERTURE_OFFSET + MACH64_BM_HOSTDATA ); ++ OUT_RING( page ); ++ OUT_RING( MACH64_DMA_CHUNKSIZE | MACH64_DMA_HOLD_OFFSET ); ++ OUT_RING( 0 ); ++ } ++ ++ /* generate the final descriptor for any remaining commands in this buffer */ ++ page = address + i * MACH64_DMA_CHUNKSIZE; ++ remainder = bytes - i * MACH64_DMA_CHUNKSIZE; ++ ++ /* Save dword offset of last descriptor for this buffer. ++ * This is needed to check for completion of the buffer in freelist_get ++ */ ++ entry->ring_ofs = RING_WRITE_OFS; ++ ++ OUT_RING( MACH64_APERTURE_OFFSET + MACH64_BM_HOSTDATA ); ++ OUT_RING( page ); ++ OUT_RING( remainder | MACH64_DMA_HOLD_OFFSET | MACH64_DMA_EOL ); ++ OUT_RING( 0 ); ++ ++ ADVANCE_RING(); ++ ++ return 0; ++} ++ ++/*@}*/ ++ ++ ++/*******************************************************************/ ++/** \name DMA test and initialization */ ++/*@{*/ ++ ++/** ++ * Perform a simple DMA operation using the pattern registers to test whether ++ * DMA works. ++ * ++ * \return zero if successful. ++ * ++ * \note This function was the testbed for many experiences regarding Mach64 ++ * DMA operation. It is left here since it so tricky to get DMA operating ++ * properly in some architectures and hardware. ++ */ ++static int mach64_bm_dma_test(struct drm_device * dev) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_dma_handle_t *cpu_addr_dmah; ++ u32 data_addr; ++ u32 *table, *data; ++ u32 expected[2]; ++ u32 src_cntl, pat_reg0, pat_reg1; ++ int i, count, failed; ++ ++ DRM_DEBUG("\n"); ++ ++ table = (u32 *) dev_priv->ring.start; ++ ++ /* FIXME: get a dma buffer from the freelist here */ ++ DRM_DEBUG("Allocating data memory ...\n"); ++#ifdef __FreeBSD__ ++ DRM_UNLOCK(); ++#endif ++ cpu_addr_dmah = ++ drm_pci_alloc(dev, 0x1000, 0x1000, 0xfffffffful); ++#ifdef __FreeBSD__ ++ DRM_LOCK(); ++#endif ++ if (!cpu_addr_dmah) { ++ DRM_INFO("data-memory allocation failed!\n"); ++ return -ENOMEM; ++ } else { ++ data = (u32 *) cpu_addr_dmah->vaddr; ++ data_addr = (u32) cpu_addr_dmah->busaddr; ++ } ++ ++ /* Save the X server's value for SRC_CNTL and restore it ++ * in case our test fails. This prevents the X server ++ * from disabling it's cache for this register ++ */ ++ src_cntl = MACH64_READ(MACH64_SRC_CNTL); ++ pat_reg0 = MACH64_READ(MACH64_PAT_REG0); ++ pat_reg1 = MACH64_READ(MACH64_PAT_REG1); ++ ++ mach64_do_wait_for_fifo(dev_priv, 3); ++ ++ MACH64_WRITE(MACH64_SRC_CNTL, 0); ++ MACH64_WRITE(MACH64_PAT_REG0, 0x11111111); ++ MACH64_WRITE(MACH64_PAT_REG1, 0x11111111); ++ ++ mach64_do_wait_for_idle(dev_priv); ++ ++ for (i = 0; i < 2; i++) { ++ u32 reg; ++ reg = MACH64_READ((MACH64_PAT_REG0 + i * 4)); ++ DRM_DEBUG("(Before DMA Transfer) reg %d = 0x%08x\n", i, reg); ++ if (reg != 0x11111111) { ++ DRM_INFO("Error initializing test registers\n"); ++ DRM_INFO("resetting engine ...\n"); ++ mach64_do_engine_reset(dev_priv); ++ DRM_INFO("freeing data buffer memory.\n"); ++ drm_pci_free(dev, cpu_addr_dmah); ++ return -EIO; ++ } ++ } ++ ++ /* fill up a buffer with sets of 2 consecutive writes starting with PAT_REG0 */ ++ count = 0; ++ ++ data[count++] = cpu_to_le32(DMAREG(MACH64_PAT_REG0) | (1 << 16)); ++ data[count++] = expected[0] = 0x22222222; ++ data[count++] = expected[1] = 0xaaaaaaaa; ++ ++ while (count < 1020) { ++ data[count++] = ++ cpu_to_le32(DMAREG(MACH64_PAT_REG0) | (1 << 16)); ++ data[count++] = 0x22222222; ++ data[count++] = 0xaaaaaaaa; ++ } ++ data[count++] = cpu_to_le32(DMAREG(MACH64_SRC_CNTL) | (0 << 16)); ++ data[count++] = 0; ++ ++ DRM_DEBUG("Preparing table ...\n"); ++ table[MACH64_DMA_FRAME_BUF_OFFSET] = cpu_to_le32(MACH64_BM_ADDR + ++ MACH64_APERTURE_OFFSET); ++ table[MACH64_DMA_SYS_MEM_ADDR] = cpu_to_le32(data_addr); ++ table[MACH64_DMA_COMMAND] = cpu_to_le32(count * sizeof(u32) ++ | MACH64_DMA_HOLD_OFFSET ++ | MACH64_DMA_EOL); ++ table[MACH64_DMA_RESERVED] = 0; ++ ++ DRM_DEBUG("table[0] = 0x%08x\n", table[0]); ++ DRM_DEBUG("table[1] = 0x%08x\n", table[1]); ++ DRM_DEBUG("table[2] = 0x%08x\n", table[2]); ++ DRM_DEBUG("table[3] = 0x%08x\n", table[3]); ++ ++ for (i = 0; i < 6; i++) { ++ DRM_DEBUG(" data[%d] = 0x%08x\n", i, data[i]); ++ } ++ DRM_DEBUG(" ...\n"); ++ for (i = count - 5; i < count; i++) { ++ DRM_DEBUG(" data[%d] = 0x%08x\n", i, data[i]); ++ } ++ ++ DRM_MEMORYBARRIER(); ++ ++ DRM_DEBUG("waiting for idle...\n"); ++ if ((i = mach64_do_wait_for_idle(dev_priv))) { ++ DRM_INFO("mach64_do_wait_for_idle failed (result=%d)\n", i); ++ DRM_INFO("resetting engine ...\n"); ++ mach64_do_engine_reset(dev_priv); ++ mach64_do_wait_for_fifo(dev_priv, 3); ++ MACH64_WRITE(MACH64_SRC_CNTL, src_cntl); ++ MACH64_WRITE(MACH64_PAT_REG0, pat_reg0); ++ MACH64_WRITE(MACH64_PAT_REG1, pat_reg1); ++ DRM_INFO("freeing data buffer memory.\n"); ++ drm_pci_free(dev, cpu_addr_dmah); ++ return i; ++ } ++ DRM_DEBUG("waiting for idle...done\n"); ++ ++ DRM_DEBUG("BUS_CNTL = 0x%08x\n", MACH64_READ(MACH64_BUS_CNTL)); ++ DRM_DEBUG("SRC_CNTL = 0x%08x\n", MACH64_READ(MACH64_SRC_CNTL)); ++ DRM_DEBUG("\n"); ++ DRM_DEBUG("data bus addr = 0x%08x\n", data_addr); ++ DRM_DEBUG("table bus addr = 0x%08x\n", dev_priv->ring.start_addr); ++ ++ DRM_DEBUG("starting DMA transfer...\n"); ++ MACH64_WRITE(MACH64_BM_GUI_TABLE_CMD, ++ dev_priv->ring.start_addr | MACH64_CIRCULAR_BUF_SIZE_16KB); ++ ++ MACH64_WRITE(MACH64_SRC_CNTL, ++ MACH64_SRC_BM_ENABLE | MACH64_SRC_BM_SYNC | ++ MACH64_SRC_BM_OP_SYSTEM_TO_REG); ++ ++ /* Kick off the transfer */ ++ DRM_DEBUG("starting DMA transfer... done.\n"); ++ MACH64_WRITE(MACH64_DST_HEIGHT_WIDTH, 0); ++ ++ DRM_DEBUG("waiting for idle...\n"); ++ ++ if ((i = mach64_do_wait_for_idle(dev_priv))) { ++ /* engine locked up, dump register state and reset */ ++ DRM_INFO("mach64_do_wait_for_idle failed (result=%d)\n", i); ++ mach64_dump_engine_info(dev_priv); ++ DRM_INFO("resetting engine ...\n"); ++ mach64_do_engine_reset(dev_priv); ++ mach64_do_wait_for_fifo(dev_priv, 3); ++ MACH64_WRITE(MACH64_SRC_CNTL, src_cntl); ++ MACH64_WRITE(MACH64_PAT_REG0, pat_reg0); ++ MACH64_WRITE(MACH64_PAT_REG1, pat_reg1); ++ DRM_INFO("freeing data buffer memory.\n"); ++ drm_pci_free(dev, cpu_addr_dmah); ++ return i; ++ } ++ ++ DRM_DEBUG("waiting for idle...done\n"); ++ ++ /* restore SRC_CNTL */ ++ mach64_do_wait_for_fifo(dev_priv, 1); ++ MACH64_WRITE(MACH64_SRC_CNTL, src_cntl); ++ ++ failed = 0; ++ ++ /* Check register values to see if the GUI master operation succeeded */ ++ for (i = 0; i < 2; i++) { ++ u32 reg; ++ reg = MACH64_READ((MACH64_PAT_REG0 + i * 4)); ++ DRM_DEBUG("(After DMA Transfer) reg %d = 0x%08x\n", i, reg); ++ if (reg != expected[i]) { ++ failed = -1; ++ } ++ } ++ ++ /* restore pattern registers */ ++ mach64_do_wait_for_fifo(dev_priv, 2); ++ MACH64_WRITE(MACH64_PAT_REG0, pat_reg0); ++ MACH64_WRITE(MACH64_PAT_REG1, pat_reg1); ++ ++ DRM_DEBUG("freeing data buffer memory.\n"); ++ drm_pci_free(dev, cpu_addr_dmah); ++ DRM_DEBUG("returning ...\n"); ++ ++ return failed; ++} ++ ++/** ++ * Called during the DMA initialization ioctl to initialize all the necessary ++ * software and hardware state for DMA operation. ++ */ ++static int mach64_do_dma_init(struct drm_device * dev, drm_mach64_init_t * init) ++{ ++ drm_mach64_private_t *dev_priv; ++ u32 tmp; ++ int i, ret; ++ ++ DRM_DEBUG("\n"); ++ ++ dev_priv = drm_alloc(sizeof(drm_mach64_private_t), DRM_MEM_DRIVER); ++ if (dev_priv == NULL) ++ return -ENOMEM; ++ ++ memset(dev_priv, 0, sizeof(drm_mach64_private_t)); ++ ++ dev_priv->is_pci = init->is_pci; ++ ++ dev_priv->fb_bpp = init->fb_bpp; ++ dev_priv->front_offset = init->front_offset; ++ dev_priv->front_pitch = init->front_pitch; ++ dev_priv->back_offset = init->back_offset; ++ dev_priv->back_pitch = init->back_pitch; ++ ++ dev_priv->depth_bpp = init->depth_bpp; ++ dev_priv->depth_offset = init->depth_offset; ++ dev_priv->depth_pitch = init->depth_pitch; ++ ++ dev_priv->front_offset_pitch = (((dev_priv->front_pitch / 8) << 22) | ++ (dev_priv->front_offset >> 3)); ++ dev_priv->back_offset_pitch = (((dev_priv->back_pitch / 8) << 22) | ++ (dev_priv->back_offset >> 3)); ++ dev_priv->depth_offset_pitch = (((dev_priv->depth_pitch / 8) << 22) | ++ (dev_priv->depth_offset >> 3)); ++ ++ dev_priv->usec_timeout = 1000000; ++ ++ /* Set up the freelist, placeholder list and pending list */ ++ INIT_LIST_HEAD(&dev_priv->free_list); ++ INIT_LIST_HEAD(&dev_priv->placeholders); ++ INIT_LIST_HEAD(&dev_priv->pending); ++ ++ dev_priv->sarea = drm_getsarea(dev); ++ if (!dev_priv->sarea) { ++ DRM_ERROR("can not find sarea!\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -EINVAL; ++ } ++ dev_priv->fb = drm_core_findmap(dev, init->fb_offset); ++ if (!dev_priv->fb) { ++ DRM_ERROR("can not find frame buffer map!\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -EINVAL; ++ } ++ dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); ++ if (!dev_priv->mmio) { ++ DRM_ERROR("can not find mmio map!\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -EINVAL; ++ } ++ ++ dev_priv->ring_map = drm_core_findmap(dev, init->ring_offset); ++ if (!dev_priv->ring_map) { ++ DRM_ERROR("can not find ring map!\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -EINVAL; ++ } ++ ++ dev_priv->sarea_priv = (drm_mach64_sarea_t *) ++ ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset); ++ ++ if (!dev_priv->is_pci) { ++ drm_core_ioremap(dev_priv->ring_map, dev); ++ if (!dev_priv->ring_map->handle) { ++ DRM_ERROR("can not ioremap virtual address for" ++ " descriptor ring\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -ENOMEM; ++ } ++ dev->agp_buffer_token = init->buffers_offset; ++ dev->agp_buffer_map = ++ drm_core_findmap(dev, init->buffers_offset); ++ if (!dev->agp_buffer_map) { ++ DRM_ERROR("can not find dma buffer map!\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -EINVAL; ++ } ++ /* there might be a nicer way to do this - ++ dev isn't passed all the way though the mach64 - DA */ ++ dev_priv->dev_buffers = dev->agp_buffer_map; ++ ++ drm_core_ioremap(dev->agp_buffer_map, dev); ++ if (!dev->agp_buffer_map->handle) { ++ DRM_ERROR("can not ioremap virtual address for" ++ " dma buffer\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -ENOMEM; ++ } ++ dev_priv->agp_textures = ++ drm_core_findmap(dev, init->agp_textures_offset); ++ if (!dev_priv->agp_textures) { ++ DRM_ERROR("can not find agp texture region!\n"); ++ dev->dev_private = (void *)dev_priv; ++ mach64_do_cleanup_dma(dev); ++ return -EINVAL; ++ } ++ } ++ ++ dev->dev_private = (void *)dev_priv; ++ ++ dev_priv->driver_mode = init->dma_mode; ++ ++ /* changing the FIFO size from the default causes problems with DMA */ ++ tmp = MACH64_READ(MACH64_GUI_CNTL); ++ if ((tmp & MACH64_CMDFIFO_SIZE_MASK) != MACH64_CMDFIFO_SIZE_128) { ++ DRM_INFO("Setting FIFO size to 128 entries\n"); ++ /* FIFO must be empty to change the FIFO depth */ ++ if ((ret = mach64_do_wait_for_idle(dev_priv))) { ++ DRM_ERROR ++ ("wait for idle failed before changing FIFO depth!\n"); ++ mach64_do_cleanup_dma(dev); ++ return ret; ++ } ++ MACH64_WRITE(MACH64_GUI_CNTL, ((tmp & ~MACH64_CMDFIFO_SIZE_MASK) ++ | MACH64_CMDFIFO_SIZE_128)); ++ /* need to read GUI_STAT for proper sync according to docs */ ++ if ((ret = mach64_do_wait_for_idle(dev_priv))) { ++ DRM_ERROR ++ ("wait for idle failed when changing FIFO depth!\n"); ++ mach64_do_cleanup_dma(dev); ++ return ret; ++ } ++ } ++ ++ dev_priv->ring.size = 0x4000; /* 16KB */ ++ dev_priv->ring.start = dev_priv->ring_map->handle; ++ dev_priv->ring.start_addr = (u32) dev_priv->ring_map->offset; ++ ++ memset(dev_priv->ring.start, 0, dev_priv->ring.size); ++ DRM_INFO("descriptor ring: cpu addr %p, bus addr: 0x%08x\n", ++ dev_priv->ring.start, dev_priv->ring.start_addr); ++ ++ ret = 0; ++ if (dev_priv->driver_mode != MACH64_MODE_MMIO) { ++ ++ /* enable block 1 registers and bus mastering */ ++ MACH64_WRITE(MACH64_BUS_CNTL, ((MACH64_READ(MACH64_BUS_CNTL) ++ | MACH64_BUS_EXT_REG_EN) ++ & ~MACH64_BUS_MASTER_DIS)); ++ ++ /* try a DMA GUI-mastering pass and fall back to MMIO if it fails */ ++ DRM_DEBUG("Starting DMA test...\n"); ++ if ((ret = mach64_bm_dma_test(dev))) { ++ dev_priv->driver_mode = MACH64_MODE_MMIO; ++ } ++ } ++ ++ switch (dev_priv->driver_mode) { ++ case MACH64_MODE_MMIO: ++ MACH64_WRITE(MACH64_BUS_CNTL, (MACH64_READ(MACH64_BUS_CNTL) ++ | MACH64_BUS_EXT_REG_EN ++ | MACH64_BUS_MASTER_DIS)); ++ if (init->dma_mode == MACH64_MODE_MMIO) ++ DRM_INFO("Forcing pseudo-DMA mode\n"); ++ else ++ DRM_INFO ++ ("DMA test failed (ret=%d), using pseudo-DMA mode\n", ++ ret); ++ break; ++ case MACH64_MODE_DMA_SYNC: ++ DRM_INFO("DMA test succeeded, using synchronous DMA mode\n"); ++ break; ++ case MACH64_MODE_DMA_ASYNC: ++ default: ++ DRM_INFO("DMA test succeeded, using asynchronous DMA mode\n"); ++ } ++ ++ dev_priv->ring_running = 0; ++ ++ /* setup offsets for physical address of table start and end */ ++ dev_priv->ring.head_addr = dev_priv->ring.start_addr; ++ dev_priv->ring.head = dev_priv->ring.tail = 0; ++ dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1; ++ dev_priv->ring.space = dev_priv->ring.size; ++ ++ /* setup physical address and size of descriptor table */ ++ mach64_do_wait_for_fifo(dev_priv, 1); ++ MACH64_WRITE(MACH64_BM_GUI_TABLE_CMD, ++ (dev_priv->ring. ++ head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB)); ++ ++ /* init frame counter */ ++ dev_priv->sarea_priv->frames_queued = 0; ++ for (i = 0; i < MACH64_MAX_QUEUED_FRAMES; i++) { ++ dev_priv->frame_ofs[i] = ~0; /* All ones indicates placeholder */ ++ } ++ ++ /* Allocate the DMA buffer freelist */ ++ if ((ret = mach64_init_freelist(dev))) { ++ DRM_ERROR("Freelist allocation failed\n"); ++ mach64_do_cleanup_dma(dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/*******************************************************************/ ++/** MMIO Pseudo-DMA (intended primarily for debugging, not performance) ++ */ ++ ++int mach64_do_dispatch_pseudo_dma(drm_mach64_private_t *dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ volatile u32 *ring_read; ++ struct list_head *ptr; ++ drm_mach64_freelist_t *entry; ++ struct drm_buf *buf = NULL; ++ u32 *buf_ptr; ++ u32 used, reg, target; ++ int fifo, count, found, ret, no_idle_wait; ++ ++ fifo = count = reg = no_idle_wait = 0; ++ target = MACH64_BM_ADDR; ++ ++ if ((ret = mach64_do_wait_for_idle(dev_priv)) < 0) { ++ DRM_INFO("idle failed before pseudo-dma dispatch, resetting engine\n"); ++ mach64_dump_engine_info(dev_priv); ++ mach64_do_engine_reset(dev_priv); ++ return ret; ++ } ++ ++ ring_read = (u32 *) ring->start; ++ ++ while (ring->tail != ring->head) { ++ u32 buf_addr, new_target, offset; ++ u32 bytes, remaining, head, eol; ++ ++ head = ring->head; ++ ++ new_target = ++ le32_to_cpu(ring_read[head++]) - MACH64_APERTURE_OFFSET; ++ buf_addr = le32_to_cpu(ring_read[head++]); ++ eol = le32_to_cpu(ring_read[head]) & MACH64_DMA_EOL; ++ bytes = le32_to_cpu(ring_read[head++]) ++ & ~(MACH64_DMA_HOLD_OFFSET | MACH64_DMA_EOL); ++ head++; ++ head &= ring->tail_mask; ++ ++ /* can't wait for idle between a blit setup descriptor ++ * and a HOSTDATA descriptor or the engine will lock ++ */ ++ if (new_target == MACH64_BM_HOSTDATA ++ && target == MACH64_BM_ADDR) ++ no_idle_wait = 1; ++ ++ target = new_target; ++ ++ found = 0; ++ offset = 0; ++ list_for_each(ptr, &dev_priv->pending) { ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ buf = entry->buf; ++ offset = buf_addr - GETBUFADDR(buf); ++ if (offset >= 0 && offset < MACH64_BUFFER_SIZE) { ++ found = 1; ++ break; ++ } ++ } ++ ++ if (!found || buf == NULL) { ++ DRM_ERROR ++ ("Couldn't find pending buffer: head: %u tail: %u buf_addr: 0x%08x %s\n", ++ head, ring->tail, buf_addr, (eol ? "eol" : "")); ++ mach64_dump_ring_info(dev_priv); ++ mach64_do_engine_reset(dev_priv); ++ return -EINVAL; ++ } ++ ++ /* Hand feed the buffer to the card via MMIO, waiting for the fifo ++ * every 16 writes ++ */ ++ DRM_DEBUG("target: (0x%08x) %s\n", target, ++ (target == ++ MACH64_BM_HOSTDATA ? "BM_HOSTDATA" : "BM_ADDR")); ++ DRM_DEBUG("offset: %u bytes: %u used: %u\n", offset, bytes, ++ buf->used); ++ ++ remaining = (buf->used - offset) >> 2; /* dwords remaining in buffer */ ++ used = bytes >> 2; /* dwords in buffer for this descriptor */ ++ buf_ptr = (u32 *) ((char *)GETBUFPTR(buf) + offset); ++ ++ while (used) { ++ ++ if (count == 0) { ++ if (target == MACH64_BM_HOSTDATA) { ++ reg = DMAREG(MACH64_HOST_DATA0); ++ count = ++ (remaining > 16) ? 16 : remaining; ++ fifo = 0; ++ } else { ++ reg = le32_to_cpu(*buf_ptr++); ++ used--; ++ count = (reg >> 16) + 1; ++ } ++ ++ reg = reg & 0xffff; ++ reg = MMSELECT(reg); ++ } ++ while (count && used) { ++ if (!fifo) { ++ if (no_idle_wait) { ++ if ((ret = ++ mach64_do_wait_for_fifo ++ (dev_priv, 16)) < 0) { ++ no_idle_wait = 0; ++ return ret; ++ } ++ } else { ++ if ((ret = ++ mach64_do_wait_for_idle ++ (dev_priv)) < 0) { ++ return ret; ++ } ++ } ++ fifo = 16; ++ } ++ --fifo; ++ MACH64_WRITE(reg, le32_to_cpu(*buf_ptr++)); ++ used--; ++ remaining--; ++ ++ reg += 4; ++ count--; ++ } ++ } ++ ring->head = head; ++ ring->head_addr = ring->start_addr + (ring->head * sizeof(u32)); ++ ring->space += (4 * sizeof(u32)); ++ } ++ ++ if ((ret = mach64_do_wait_for_idle(dev_priv)) < 0) { ++ return ret; ++ } ++ MACH64_WRITE(MACH64_BM_GUI_TABLE_CMD, ++ ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB); ++ ++ DRM_DEBUG("completed\n"); ++ return 0; ++} ++ ++/*@}*/ ++ ++ ++/*******************************************************************/ ++/** \name DMA cleanup */ ++/*@{*/ ++ ++int mach64_do_cleanup_dma(struct drm_device * dev) ++{ ++ DRM_DEBUG("\n"); ++ ++ /* Make sure interrupts are disabled here because the uninstall ioctl ++ * may not have been called from userspace and after dev_private ++ * is freed, it's too late. ++ */ ++ if (dev->irq) ++ drm_irq_uninstall(dev); ++ ++ if (dev->dev_private) { ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ ++ if (!dev_priv->is_pci) { ++ if (dev_priv->ring_map) ++ drm_core_ioremapfree(dev_priv->ring_map, dev); ++ ++ if (dev->agp_buffer_map) { ++ drm_core_ioremapfree(dev->agp_buffer_map, dev); ++ dev->agp_buffer_map = NULL; ++ } ++ } ++ ++ mach64_destroy_freelist(dev); ++ ++ drm_free(dev_priv, sizeof(drm_mach64_private_t), ++ DRM_MEM_DRIVER); ++ dev->dev_private = NULL; ++ } ++ ++ return 0; ++} ++ ++/*@}*/ ++ ++ ++/*******************************************************************/ ++/** \name IOCTL handlers */ ++/*@{*/ ++ ++int mach64_dma_init(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_init_t *init = data; ++ ++ DRM_DEBUG("\n"); ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ switch (init->func) { ++ case DRM_MACH64_INIT_DMA: ++ return mach64_do_dma_init(dev, init); ++ case DRM_MACH64_CLEANUP_DMA: ++ return mach64_do_cleanup_dma(dev); ++ } ++ ++ return -EINVAL; ++} ++ ++int mach64_dma_idle(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ ++ DRM_DEBUG("\n"); ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ return mach64_do_dma_idle(dev_priv); ++} ++ ++int mach64_dma_flush(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ ++ DRM_DEBUG("\n"); ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ return mach64_do_dma_flush(dev_priv); ++} ++ ++int mach64_engine_reset(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ ++ DRM_DEBUG("\n"); ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ return mach64_do_engine_reset(dev_priv); ++} ++ ++/*@}*/ ++ ++ ++/*******************************************************************/ ++/** \name Freelist management */ ++/*@{*/ ++ ++int mach64_init_freelist(struct drm_device * dev) ++{ ++ struct drm_device_dma *dma = dev->dma; ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_freelist_t *entry; ++ struct list_head *ptr; ++ int i; ++ ++ DRM_DEBUG("adding %d buffers to freelist\n", dma->buf_count); ++ ++ for (i = 0; i < dma->buf_count; i++) { ++ if ((entry = ++ (drm_mach64_freelist_t *) ++ drm_alloc(sizeof(drm_mach64_freelist_t), ++ DRM_MEM_BUFLISTS)) == NULL) ++ return -ENOMEM; ++ memset(entry, 0, sizeof(drm_mach64_freelist_t)); ++ entry->buf = dma->buflist[i]; ++ ptr = &entry->list; ++ list_add_tail(ptr, &dev_priv->free_list); ++ } ++ ++ return 0; ++} ++ ++void mach64_destroy_freelist(struct drm_device * dev) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_freelist_t *entry; ++ struct list_head *ptr; ++ struct list_head *tmp; ++ ++ DRM_DEBUG("\n"); ++ ++ list_for_each_safe(ptr, tmp, &dev_priv->pending) { ++ list_del(ptr); ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ drm_free(entry, sizeof(*entry), DRM_MEM_BUFLISTS); ++ } ++ list_for_each_safe(ptr, tmp, &dev_priv->placeholders) { ++ list_del(ptr); ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ drm_free(entry, sizeof(*entry), DRM_MEM_BUFLISTS); ++ } ++ ++ list_for_each_safe(ptr, tmp, &dev_priv->free_list) { ++ list_del(ptr); ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ drm_free(entry, sizeof(*entry), DRM_MEM_BUFLISTS); ++ } ++} ++ ++/* IMPORTANT: This function should only be called when the engine is idle or locked up, ++ * as it assumes all buffers in the pending list have been completed by the hardware. ++ */ ++int mach64_do_release_used_buffers(drm_mach64_private_t *dev_priv) ++{ ++ struct list_head *ptr; ++ struct list_head *tmp; ++ drm_mach64_freelist_t *entry; ++ int i; ++ ++ if (list_empty(&dev_priv->pending)) ++ return 0; ++ ++ /* Iterate the pending list and move all buffers into the freelist... */ ++ i = 0; ++ list_for_each_safe(ptr, tmp, &dev_priv->pending) { ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ if (entry->discard) { ++ entry->buf->pending = 0; ++ list_del(ptr); ++ list_add_tail(ptr, &dev_priv->free_list); ++ i++; ++ } ++ } ++ ++ DRM_DEBUG("released %d buffers from pending list\n", i); ++ ++ return 0; ++} ++ ++static int mach64_do_reclaim_completed(drm_mach64_private_t *dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ struct list_head *ptr; ++ struct list_head *tmp; ++ drm_mach64_freelist_t *entry; ++ u32 head, tail, ofs; ++ ++ mach64_ring_tick(dev_priv, ring); ++ head = ring->head; ++ tail = ring->tail; ++ ++ if (head == tail) { ++#if MACH64_EXTRA_CHECKING ++ if (MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE) { ++ DRM_ERROR("Empty ring with non-idle engine!\n"); ++ mach64_dump_ring_info(dev_priv); ++ return -1; ++ } ++#endif ++ /* last pass is complete, so release everything */ ++ mach64_do_release_used_buffers(dev_priv); ++ DRM_DEBUG("idle engine, freed all buffers.\n"); ++ if (list_empty(&dev_priv->free_list)) { ++ DRM_ERROR("Freelist empty with idle engine\n"); ++ return -1; ++ } ++ return 0; ++ } ++ /* Look for a completed buffer and bail out of the loop ++ * as soon as we find one -- don't waste time trying ++ * to free extra bufs here, leave that to do_release_used_buffers ++ */ ++ list_for_each_safe(ptr, tmp, &dev_priv->pending) { ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ ofs = entry->ring_ofs; ++ if (entry->discard && ++ ((head < tail && (ofs < head || ofs >= tail)) || ++ (head > tail && (ofs < head && ofs >= tail)))) { ++#if MACH64_EXTRA_CHECKING ++ int i; ++ ++ for (i = head; i != tail; i = (i + 4) & ring->tail_mask) ++ { ++ u32 o1 = le32_to_cpu(((u32 *) ring-> ++ start)[i + 1]); ++ u32 o2 = GETBUFADDR(entry->buf); ++ ++ if (o1 == o2) { ++ DRM_ERROR ++ ("Attempting to free used buffer: " ++ "i=%d buf=0x%08x\n", ++ i, o1); ++ mach64_dump_ring_info(dev_priv); ++ return -1; ++ } ++ } ++#endif ++ /* found a processed buffer */ ++ entry->buf->pending = 0; ++ list_del(ptr); ++ list_add_tail(ptr, &dev_priv->free_list); ++ DRM_DEBUG ++ ("freed processed buffer (head=%d tail=%d " ++ "buf ring ofs=%d).\n", ++ head, tail, ofs); ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++struct drm_buf *mach64_freelist_get(drm_mach64_private_t *dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ drm_mach64_freelist_t *entry; ++ struct list_head *ptr; ++ int t; ++ ++ if (list_empty(&dev_priv->free_list)) { ++ if (list_empty(&dev_priv->pending)) { ++ DRM_ERROR ++ ("Couldn't get buffer - pending and free lists empty\n"); ++ t = 0; ++ list_for_each(ptr, &dev_priv->placeholders) { ++ t++; ++ } ++ DRM_INFO("Placeholders: %d\n", t); ++ return NULL; ++ } ++ ++ for (t = 0; t < dev_priv->usec_timeout; t++) { ++ int ret; ++ ++ ret = mach64_do_reclaim_completed(dev_priv); ++ if (ret == 0) ++ goto _freelist_entry_found; ++ if (ret < 0) ++ return NULL; ++ ++ DRM_UDELAY(1); ++ } ++ mach64_dump_ring_info(dev_priv); ++ DRM_ERROR ++ ("timeout waiting for buffers: ring head_addr: 0x%08x head: %d tail: %d\n", ++ ring->head_addr, ring->head, ring->tail); ++ return NULL; ++ } ++ ++ _freelist_entry_found: ++ ptr = dev_priv->free_list.next; ++ list_del(ptr); ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ entry->buf->used = 0; ++ list_add_tail(ptr, &dev_priv->placeholders); ++ return entry->buf; ++} ++ ++int mach64_freelist_put(drm_mach64_private_t *dev_priv, struct drm_buf *copy_buf) ++{ ++ struct list_head *ptr; ++ drm_mach64_freelist_t *entry; ++ ++#if MACH64_EXTRA_CHECKING ++ list_for_each(ptr, &dev_priv->pending) { ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ if (copy_buf == entry->buf) { ++ DRM_ERROR("Trying to release a pending buf\n"); ++ return -EFAULT; ++ } ++ } ++#endif ++ ptr = dev_priv->placeholders.next; ++ entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ copy_buf->pending = 0; ++ copy_buf->used = 0; ++ entry->buf = copy_buf; ++ entry->discard = 1; ++ list_del(ptr); ++ list_add_tail(ptr, &dev_priv->free_list); ++ ++ return 0; ++} ++ ++/*@}*/ ++ ++ ++/*******************************************************************/ ++/** \name DMA buffer request and submission IOCTL handler */ ++/*@{*/ ++ ++static int mach64_dma_get_buffers(struct drm_device *dev, ++ struct drm_file *file_priv, ++ struct drm_dma * d) ++{ ++ int i; ++ struct drm_buf *buf; ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ ++ for (i = d->granted_count; i < d->request_count; i++) { ++ buf = mach64_freelist_get(dev_priv); ++#if MACH64_EXTRA_CHECKING ++ if (!buf) ++ return -EFAULT; ++#else ++ if (!buf) ++ return -EAGAIN; ++#endif ++ ++ buf->file_priv = file_priv; ++ ++ if (DRM_COPY_TO_USER(&d->request_indices[i], &buf->idx, ++ sizeof(buf->idx))) ++ return -EFAULT; ++ if (DRM_COPY_TO_USER(&d->request_sizes[i], &buf->total, ++ sizeof(buf->total))) ++ return -EFAULT; ++ ++ d->granted_count++; ++ } ++ return 0; ++} ++ ++int mach64_dma_buffers(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_device_dma *dma = dev->dma; ++ struct drm_dma *d = data; ++ int ret = 0; ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ /* Please don't send us buffers. ++ */ ++ if (d->send_count != 0) { ++ DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n", ++ DRM_CURRENTPID, d->send_count); ++ return -EINVAL; ++ } ++ ++ /* We'll send you buffers. ++ */ ++ if (d->request_count < 0 || d->request_count > dma->buf_count) { ++ DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", ++ DRM_CURRENTPID, d->request_count, dma->buf_count); ++ ret = -EINVAL; ++ } ++ ++ d->granted_count = 0; ++ ++ if (d->request_count) { ++ ret = mach64_dma_get_buffers(dev, file_priv, d); ++ } ++ ++ return ret; ++} ++ ++void mach64_driver_lastclose(struct drm_device * dev) ++{ ++ mach64_do_cleanup_dma(dev); ++} ++ ++/*@}*/ +--- /dev/null ++++ b/drivers/gpu/drm/mach64/mach64_drm.h +@@ -0,0 +1,256 @@ ++/* mach64_drm.h -- Public header for the mach64 driver -*- linux-c -*- ++ * Created: Thu Nov 30 20:04:32 2000 by gareth@valinux.com ++ */ ++/* ++ * Copyright 2000 Gareth Hughes ++ * Copyright 2002 Frank C. Earl ++ * Copyright 2002-2003 Leif Delgass ++ * All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: ++ * Gareth Hughes ++ * Frank C. Earl ++ * Leif Delgass ++ */ ++ ++#ifndef __MACH64_DRM_H__ ++#define __MACH64_DRM_H__ ++ ++/* WARNING: If you change any of these defines, make sure to change the ++ * defines in the Xserver file (mach64_sarea.h) ++ */ ++#ifndef __MACH64_SAREA_DEFINES__ ++#define __MACH64_SAREA_DEFINES__ ++ ++/* What needs to be changed for the current vertex buffer? ++ * GH: We're going to be pedantic about this. We want the card to do as ++ * little as possible, so let's avoid having it fetch a whole bunch of ++ * register values that don't change all that often, if at all. ++ */ ++#define MACH64_UPLOAD_DST_OFF_PITCH 0x0001 ++#define MACH64_UPLOAD_Z_OFF_PITCH 0x0002 ++#define MACH64_UPLOAD_Z_ALPHA_CNTL 0x0004 ++#define MACH64_UPLOAD_SCALE_3D_CNTL 0x0008 ++#define MACH64_UPLOAD_DP_FOG_CLR 0x0010 ++#define MACH64_UPLOAD_DP_WRITE_MASK 0x0020 ++#define MACH64_UPLOAD_DP_PIX_WIDTH 0x0040 ++#define MACH64_UPLOAD_SETUP_CNTL 0x0080 ++#define MACH64_UPLOAD_MISC 0x0100 ++#define MACH64_UPLOAD_TEXTURE 0x0200 ++#define MACH64_UPLOAD_TEX0IMAGE 0x0400 ++#define MACH64_UPLOAD_TEX1IMAGE 0x0800 ++#define MACH64_UPLOAD_CLIPRECTS 0x1000 /* handled client-side */ ++#define MACH64_UPLOAD_CONTEXT 0x00ff ++#define MACH64_UPLOAD_ALL 0x1fff ++ ++/* DMA buffer size ++ */ ++#define MACH64_BUFFER_SIZE 16384 ++ ++/* Max number of swaps allowed on the ring ++ * before the client must wait ++ */ ++#define MACH64_MAX_QUEUED_FRAMES 3U ++ ++/* Byte offsets for host blit buffer data ++ */ ++#define MACH64_HOSTDATA_BLIT_OFFSET 104 ++ ++/* Keep these small for testing. ++ */ ++#define MACH64_NR_SAREA_CLIPRECTS 8 ++ ++#define MACH64_CARD_HEAP 0 ++#define MACH64_AGP_HEAP 1 ++#define MACH64_NR_TEX_HEAPS 2 ++#define MACH64_NR_TEX_REGIONS 64 ++#define MACH64_LOG_TEX_GRANULARITY 16 ++ ++#define MACH64_TEX_MAXLEVELS 1 ++ ++#define MACH64_NR_CONTEXT_REGS 15 ++#define MACH64_NR_TEXTURE_REGS 4 ++ ++#endif /* __MACH64_SAREA_DEFINES__ */ ++ ++typedef struct { ++ unsigned int dst_off_pitch; ++ ++ unsigned int z_off_pitch; ++ unsigned int z_cntl; ++ unsigned int alpha_tst_cntl; ++ ++ unsigned int scale_3d_cntl; ++ ++ unsigned int sc_left_right; ++ unsigned int sc_top_bottom; ++ ++ unsigned int dp_fog_clr; ++ unsigned int dp_write_mask; ++ unsigned int dp_pix_width; ++ unsigned int dp_mix; ++ unsigned int dp_src; ++ ++ unsigned int clr_cmp_cntl; ++ unsigned int gui_traj_cntl; ++ ++ unsigned int setup_cntl; ++ ++ unsigned int tex_size_pitch; ++ unsigned int tex_cntl; ++ unsigned int secondary_tex_off; ++ unsigned int tex_offset; ++} drm_mach64_context_regs_t; ++ ++typedef struct drm_mach64_sarea { ++ /* The channel for communication of state information to the kernel ++ * on firing a vertex dma buffer. ++ */ ++ drm_mach64_context_regs_t context_state; ++ unsigned int dirty; ++ unsigned int vertsize; ++ ++ /* The current cliprects, or a subset thereof. ++ */ ++ struct drm_clip_rect boxes[MACH64_NR_SAREA_CLIPRECTS]; ++ unsigned int nbox; ++ ++ /* Counters for client-side throttling of rendering clients. ++ */ ++ unsigned int frames_queued; ++ ++ /* Texture memory LRU. ++ */ ++ struct drm_tex_region tex_list[MACH64_NR_TEX_HEAPS][MACH64_NR_TEX_REGIONS + ++ 1]; ++ unsigned int tex_age[MACH64_NR_TEX_HEAPS]; ++ int ctx_owner; ++} drm_mach64_sarea_t; ++ ++/* WARNING: If you change any of these defines, make sure to change the ++ * defines in the Xserver file (mach64_common.h) ++ */ ++ ++/* Mach64 specific ioctls ++ * The device specific ioctl range is 0x40 to 0x79. ++ */ ++ ++#define DRM_MACH64_INIT 0x00 ++#define DRM_MACH64_IDLE 0x01 ++#define DRM_MACH64_RESET 0x02 ++#define DRM_MACH64_SWAP 0x03 ++#define DRM_MACH64_CLEAR 0x04 ++#define DRM_MACH64_VERTEX 0x05 ++#define DRM_MACH64_BLIT 0x06 ++#define DRM_MACH64_FLUSH 0x07 ++#define DRM_MACH64_GETPARAM 0x08 ++ ++#define DRM_IOCTL_MACH64_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_INIT, drm_mach64_init_t) ++#define DRM_IOCTL_MACH64_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_IDLE ) ++#define DRM_IOCTL_MACH64_RESET DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_RESET ) ++#define DRM_IOCTL_MACH64_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_SWAP ) ++#define DRM_IOCTL_MACH64_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_CLEAR, drm_mach64_clear_t) ++#define DRM_IOCTL_MACH64_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_VERTEX, drm_mach64_vertex_t) ++#define DRM_IOCTL_MACH64_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_BLIT, drm_mach64_blit_t) ++#define DRM_IOCTL_MACH64_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_FLUSH ) ++#define DRM_IOCTL_MACH64_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_MACH64_GETPARAM, drm_mach64_getparam_t) ++ ++/* Buffer flags for clears ++ */ ++#define MACH64_FRONT 0x1 ++#define MACH64_BACK 0x2 ++#define MACH64_DEPTH 0x4 ++ ++/* Primitive types for vertex buffers ++ */ ++#define MACH64_PRIM_POINTS 0x00000000 ++#define MACH64_PRIM_LINES 0x00000001 ++#define MACH64_PRIM_LINE_LOOP 0x00000002 ++#define MACH64_PRIM_LINE_STRIP 0x00000003 ++#define MACH64_PRIM_TRIANGLES 0x00000004 ++#define MACH64_PRIM_TRIANGLE_STRIP 0x00000005 ++#define MACH64_PRIM_TRIANGLE_FAN 0x00000006 ++#define MACH64_PRIM_QUADS 0x00000007 ++#define MACH64_PRIM_QUAD_STRIP 0x00000008 ++#define MACH64_PRIM_POLYGON 0x00000009 ++ ++typedef enum _drm_mach64_dma_mode_t { ++ MACH64_MODE_DMA_ASYNC, ++ MACH64_MODE_DMA_SYNC, ++ MACH64_MODE_MMIO ++} drm_mach64_dma_mode_t; ++ ++typedef struct drm_mach64_init { ++ enum { ++ DRM_MACH64_INIT_DMA = 0x01, ++ DRM_MACH64_CLEANUP_DMA = 0x02 ++ } func; ++ ++ unsigned long sarea_priv_offset; ++ int is_pci; ++ drm_mach64_dma_mode_t dma_mode; ++ ++ unsigned int fb_bpp; ++ unsigned int front_offset, front_pitch; ++ unsigned int back_offset, back_pitch; ++ ++ unsigned int depth_bpp; ++ unsigned int depth_offset, depth_pitch; ++ ++ unsigned long fb_offset; ++ unsigned long mmio_offset; ++ unsigned long ring_offset; ++ unsigned long buffers_offset; ++ unsigned long agp_textures_offset; ++} drm_mach64_init_t; ++ ++typedef struct drm_mach64_clear { ++ unsigned int flags; ++ int x, y, w, h; ++ unsigned int clear_color; ++ unsigned int clear_depth; ++} drm_mach64_clear_t; ++ ++typedef struct drm_mach64_vertex { ++ int prim; ++ void *buf; /* Address of vertex buffer */ ++ unsigned long used; /* Number of bytes in buffer */ ++ int discard; /* Client finished with buffer? */ ++} drm_mach64_vertex_t; ++ ++typedef struct drm_mach64_blit { ++ void *buf; ++ int pitch; ++ int offset; ++ int format; ++ unsigned short x, y; ++ unsigned short width, height; ++} drm_mach64_blit_t; ++ ++typedef struct drm_mach64_getparam { ++ enum { ++ MACH64_PARAM_FRAMES_QUEUED = 0x01, ++ MACH64_PARAM_IRQ_NR = 0x02 ++ } param; ++ void *value; ++} drm_mach64_getparam_t; ++ ++#endif +--- /dev/null ++++ b/drivers/gpu/drm/mach64/mach64_drv.c +@@ -0,0 +1,105 @@ ++/* mach64_drv.c -- mach64 (Rage Pro) driver -*- linux-c -*- ++ * Created: Fri Nov 24 18:34:32 2000 by gareth@valinux.com ++ * ++ * Copyright 2000 Gareth Hughes ++ * All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * GARETH HUGHES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: ++ * Gareth Hughes ++ * Leif Delgass ++ */ ++ ++#include "drmP.h" ++#include "drm.h" ++#include "mach64_drm.h" ++#include "mach64_drv.h" ++ ++#include "drm_pciids.h" ++ ++static struct pci_device_id pciidlist[] = { ++ mach64_PCI_IDS ++}; ++ ++static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); ++static struct drm_driver driver = { ++ .driver_features = ++ DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_HAVE_DMA ++ | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, ++ .lastclose = mach64_driver_lastclose, ++ .get_vblank_counter = mach64_get_vblank_counter, ++ .enable_vblank = mach64_enable_vblank, ++ .disable_vblank = mach64_disable_vblank, ++ .irq_preinstall = mach64_driver_irq_preinstall, ++ .irq_postinstall = mach64_driver_irq_postinstall, ++ .irq_uninstall = mach64_driver_irq_uninstall, ++ .irq_handler = mach64_driver_irq_handler, ++ .reclaim_buffers = drm_core_reclaim_buffers, ++ .get_map_ofs = drm_core_get_map_ofs, ++ .get_reg_ofs = drm_core_get_reg_ofs, ++ .ioctls = mach64_ioctls, ++ .dma_ioctl = mach64_dma_buffers, ++ .fops = { ++ .owner = THIS_MODULE, ++ .open = drm_open, ++ .release = drm_release, ++ .ioctl = drm_ioctl, ++ .mmap = drm_mmap, ++ .poll = drm_poll, ++ .fasync = drm_fasync, ++ }, ++ .pci_driver = { ++ .name = DRIVER_NAME, ++ .id_table = pciidlist, ++ .probe = probe, ++ .remove = __devexit_p(drm_cleanup_pci), ++ }, ++ ++ .name = DRIVER_NAME, ++ .desc = DRIVER_DESC, ++ .date = DRIVER_DATE, ++ .major = DRIVER_MAJOR, ++ .minor = DRIVER_MINOR, ++ .patchlevel = DRIVER_PATCHLEVEL, ++}; ++ ++static int probe(struct pci_dev *pdev, const struct pci_device_id *ent) ++{ ++ return drm_get_dev(pdev, ent, &driver); ++} ++ ++ ++static int __init mach64_init(void) ++{ ++ driver.num_ioctls = mach64_max_ioctl; ++ return drm_init(&driver, pciidlist); ++} ++ ++static void __exit mach64_exit(void) ++{ ++ drm_exit(&driver); ++} ++ ++module_init(mach64_init); ++module_exit(mach64_exit); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL and additional rights"); +--- /dev/null ++++ b/drivers/gpu/drm/mach64/mach64_drv.h +@@ -0,0 +1,859 @@ ++/* mach64_drv.h -- Private header for mach64 driver -*- linux-c -*- ++ * Created: Fri Nov 24 22:07:58 2000 by gareth@valinux.com ++ */ ++/* ++ * Copyright 2000 Gareth Hughes ++ * Copyright 2002 Frank C. Earl ++ * Copyright 2002-2003 Leif Delgass ++ * All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: ++ * Gareth Hughes ++ * Frank C. Earl ++ * Leif Delgass ++ * José Fonseca ++ */ ++ ++#ifndef __MACH64_DRV_H__ ++#define __MACH64_DRV_H__ ++ ++/* General customization: ++ */ ++ ++#define DRIVER_AUTHOR "Gareth Hughes, Leif Delgass, José Fonseca" ++ ++#define DRIVER_NAME "mach64" ++#define DRIVER_DESC "DRM module for the ATI Rage Pro" ++#define DRIVER_DATE "20060718" ++ ++#define DRIVER_MAJOR 2 ++#define DRIVER_MINOR 0 ++#define DRIVER_PATCHLEVEL 0 ++ ++/* FIXME: remove these when not needed */ ++/* Development driver options */ ++#define MACH64_EXTRA_CHECKING 0 /* Extra sanity checks for DMA/freelist management */ ++#define MACH64_VERBOSE 0 /* Verbose debugging output */ ++ ++typedef struct drm_mach64_freelist { ++ struct list_head list; /* List pointers for free_list, placeholders, or pending list */ ++ struct drm_buf *buf; /* Pointer to the buffer */ ++ int discard; /* This flag is set when we're done (re)using a buffer */ ++ u32 ring_ofs; /* dword offset in ring of last descriptor for this buffer */ ++} drm_mach64_freelist_t; ++ ++typedef struct drm_mach64_descriptor_ring { ++ void *start; /* write pointer (cpu address) to start of descriptor ring */ ++ u32 start_addr; /* bus address of beginning of descriptor ring */ ++ int size; /* size of ring in bytes */ ++ ++ u32 head_addr; /* bus address of descriptor ring head */ ++ u32 head; /* dword offset of descriptor ring head */ ++ u32 tail; /* dword offset of descriptor ring tail */ ++ u32 tail_mask; /* mask used to wrap ring */ ++ int space; /* number of free bytes in ring */ ++} drm_mach64_descriptor_ring_t; ++ ++typedef struct drm_mach64_private { ++ drm_mach64_sarea_t *sarea_priv; ++ ++ int is_pci; ++ drm_mach64_dma_mode_t driver_mode; /* Async DMA, sync DMA, or MMIO */ ++ ++ int usec_timeout; /* Timeout for the wait functions */ ++ ++ drm_mach64_descriptor_ring_t ring; /* DMA descriptor table (ring buffer) */ ++ int ring_running; /* Is bus mastering is enabled */ ++ ++ struct list_head free_list; /* Free-list head */ ++ struct list_head placeholders; /* Placeholder list for buffers held by clients */ ++ struct list_head pending; /* Buffers pending completion */ ++ ++ u32 frame_ofs[MACH64_MAX_QUEUED_FRAMES]; /* dword ring offsets of most recent frame swaps */ ++ ++ unsigned int fb_bpp; ++ unsigned int front_offset, front_pitch; ++ unsigned int back_offset, back_pitch; ++ ++ unsigned int depth_bpp; ++ unsigned int depth_offset, depth_pitch; ++ ++ atomic_t vbl_received; /**< Number of vblanks received. */ ++ ++ u32 front_offset_pitch; ++ u32 back_offset_pitch; ++ u32 depth_offset_pitch; ++ ++ drm_local_map_t *sarea; ++ drm_local_map_t *fb; ++ drm_local_map_t *mmio; ++ drm_local_map_t *ring_map; ++ drm_local_map_t *dev_buffers; /* this is a pointer to a structure in dev */ ++ drm_local_map_t *agp_textures; ++} drm_mach64_private_t; ++ ++extern struct drm_ioctl_desc mach64_ioctls[]; ++extern int mach64_max_ioctl; ++ ++ /* mach64_dma.c */ ++extern int mach64_dma_init(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_dma_idle(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_dma_flush(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_engine_reset(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_dma_buffers(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern void mach64_driver_lastclose(struct drm_device * dev); ++ ++extern int mach64_init_freelist(struct drm_device * dev); ++extern void mach64_destroy_freelist(struct drm_device * dev); ++extern struct drm_buf *mach64_freelist_get(drm_mach64_private_t * dev_priv); ++extern int mach64_freelist_put(drm_mach64_private_t * dev_priv, ++ struct drm_buf * copy_buf); ++ ++extern int mach64_do_wait_for_fifo(drm_mach64_private_t * dev_priv, ++ int entries); ++extern int mach64_do_wait_for_idle(drm_mach64_private_t * dev_priv); ++extern int mach64_wait_ring(drm_mach64_private_t * dev_priv, int n); ++extern int mach64_do_dispatch_pseudo_dma(drm_mach64_private_t * dev_priv); ++extern int mach64_do_release_used_buffers(drm_mach64_private_t * dev_priv); ++extern void mach64_dump_engine_info(drm_mach64_private_t * dev_priv); ++extern void mach64_dump_ring_info(drm_mach64_private_t * dev_priv); ++extern int mach64_do_engine_reset(drm_mach64_private_t * dev_priv); ++ ++extern int mach64_add_buf_to_ring(drm_mach64_private_t *dev_priv, ++ drm_mach64_freelist_t *_entry); ++extern int mach64_add_hostdata_buf_to_ring(drm_mach64_private_t *dev_priv, ++ drm_mach64_freelist_t *_entry); ++ ++extern int mach64_do_dma_idle(drm_mach64_private_t * dev_priv); ++extern int mach64_do_dma_flush(drm_mach64_private_t * dev_priv); ++extern int mach64_do_cleanup_dma(struct drm_device * dev); ++ ++ /* mach64_state.c */ ++extern int mach64_dma_clear(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_dma_swap(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_dma_vertex(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_dma_blit(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++extern int mach64_get_param(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++ ++extern u32 mach64_get_vblank_counter(struct drm_device *dev, int crtc); ++extern int mach64_enable_vblank(struct drm_device *dev, int crtc); ++extern void mach64_disable_vblank(struct drm_device *dev, int crtc); ++extern irqreturn_t mach64_driver_irq_handler(DRM_IRQ_ARGS); ++extern void mach64_driver_irq_preinstall(struct drm_device *dev); ++extern int mach64_driver_irq_postinstall(struct drm_device *dev); ++extern void mach64_driver_irq_uninstall(struct drm_device *dev); ++ ++/* ================================================================ ++ * Registers ++ */ ++ ++#define MACH64_AGP_BASE 0x0148 ++#define MACH64_AGP_CNTL 0x014c ++#define MACH64_ALPHA_TST_CNTL 0x0550 ++ ++#define MACH64_DSP_CONFIG 0x0420 ++#define MACH64_DSP_ON_OFF 0x0424 ++#define MACH64_EXT_MEM_CNTL 0x04ac ++#define MACH64_GEN_TEST_CNTL 0x04d0 ++#define MACH64_HW_DEBUG 0x047c ++#define MACH64_MEM_ADDR_CONFIG 0x0434 ++#define MACH64_MEM_BUF_CNTL 0x042c ++#define MACH64_MEM_CNTL 0x04b0 ++ ++#define MACH64_BM_ADDR 0x0648 ++#define MACH64_BM_COMMAND 0x0188 ++#define MACH64_BM_DATA 0x0648 ++#define MACH64_BM_FRAME_BUF_OFFSET 0x0180 ++#define MACH64_BM_GUI_TABLE 0x01b8 ++#define MACH64_BM_GUI_TABLE_CMD 0x064c ++# define MACH64_CIRCULAR_BUF_SIZE_16KB (0 << 0) ++# define MACH64_CIRCULAR_BUF_SIZE_32KB (1 << 0) ++# define MACH64_CIRCULAR_BUF_SIZE_64KB (2 << 0) ++# define MACH64_CIRCULAR_BUF_SIZE_128KB (3 << 0) ++# define MACH64_LAST_DESCRIPTOR (1 << 31) ++#define MACH64_BM_HOSTDATA 0x0644 ++#define MACH64_BM_STATUS 0x018c ++#define MACH64_BM_SYSTEM_MEM_ADDR 0x0184 ++#define MACH64_BM_SYSTEM_TABLE 0x01bc ++#define MACH64_BUS_CNTL 0x04a0 ++# define MACH64_BUS_MSTR_RESET (1 << 1) ++# define MACH64_BUS_APER_REG_DIS (1 << 4) ++# define MACH64_BUS_FLUSH_BUF (1 << 2) ++# define MACH64_BUS_MASTER_DIS (1 << 6) ++# define MACH64_BUS_EXT_REG_EN (1 << 27) ++ ++#define MACH64_CLR_CMP_CLR 0x0700 ++#define MACH64_CLR_CMP_CNTL 0x0708 ++#define MACH64_CLR_CMP_MASK 0x0704 ++#define MACH64_CONFIG_CHIP_ID 0x04e0 ++#define MACH64_CONFIG_CNTL 0x04dc ++#define MACH64_CONFIG_STAT0 0x04e4 ++#define MACH64_CONFIG_STAT1 0x0494 ++#define MACH64_CONFIG_STAT2 0x0498 ++#define MACH64_CONTEXT_LOAD_CNTL 0x072c ++#define MACH64_CONTEXT_MASK 0x0720 ++#define MACH64_COMPOSITE_SHADOW_ID 0x0798 ++#define MACH64_CRC_SIG 0x04e8 ++#define MACH64_CUSTOM_MACRO_CNTL 0x04d4 ++ ++#define MACH64_DP_BKGD_CLR 0x06c0 ++#define MACH64_DP_FOG_CLR 0x06c4 ++#define MACH64_DP_FGRD_BKGD_CLR 0x06e0 ++#define MACH64_DP_FRGD_CLR 0x06c4 ++#define MACH64_DP_FGRD_CLR_MIX 0x06dc ++ ++#define MACH64_DP_MIX 0x06d4 ++# define BKGD_MIX_NOT_D (0 << 0) ++# define BKGD_MIX_ZERO (1 << 0) ++# define BKGD_MIX_ONE (2 << 0) ++# define MACH64_BKGD_MIX_D (3 << 0) ++# define BKGD_MIX_NOT_S (4 << 0) ++# define BKGD_MIX_D_XOR_S (5 << 0) ++# define BKGD_MIX_NOT_D_XOR_S (6 << 0) ++# define MACH64_BKGD_MIX_S (7 << 0) ++# define BKGD_MIX_NOT_D_OR_NOT_S (8 << 0) ++# define BKGD_MIX_D_OR_NOT_S (9 << 0) ++# define BKGD_MIX_NOT_D_OR_S (10 << 0) ++# define BKGD_MIX_D_OR_S (11 << 0) ++# define BKGD_MIX_D_AND_S (12 << 0) ++# define BKGD_MIX_NOT_D_AND_S (13 << 0) ++# define BKGD_MIX_D_AND_NOT_S (14 << 0) ++# define BKGD_MIX_NOT_D_AND_NOT_S (15 << 0) ++# define BKGD_MIX_D_PLUS_S_DIV2 (23 << 0) ++# define FRGD_MIX_NOT_D (0 << 16) ++# define FRGD_MIX_ZERO (1 << 16) ++# define FRGD_MIX_ONE (2 << 16) ++# define FRGD_MIX_D (3 << 16) ++# define FRGD_MIX_NOT_S (4 << 16) ++# define FRGD_MIX_D_XOR_S (5 << 16) ++# define FRGD_MIX_NOT_D_XOR_S (6 << 16) ++# define MACH64_FRGD_MIX_S (7 << 16) ++# define FRGD_MIX_NOT_D_OR_NOT_S (8 << 16) ++# define FRGD_MIX_D_OR_NOT_S (9 << 16) ++# define FRGD_MIX_NOT_D_OR_S (10 << 16) ++# define FRGD_MIX_D_OR_S (11 << 16) ++# define FRGD_MIX_D_AND_S (12 << 16) ++# define FRGD_MIX_NOT_D_AND_S (13 << 16) ++# define FRGD_MIX_D_AND_NOT_S (14 << 16) ++# define FRGD_MIX_NOT_D_AND_NOT_S (15 << 16) ++# define FRGD_MIX_D_PLUS_S_DIV2 (23 << 16) ++ ++#define MACH64_DP_PIX_WIDTH 0x06d0 ++# define MACH64_HOST_TRIPLE_ENABLE (1 << 13) ++# define MACH64_BYTE_ORDER_MSB_TO_LSB (0 << 24) ++# define MACH64_BYTE_ORDER_LSB_TO_MSB (1 << 24) ++ ++#define MACH64_DP_SRC 0x06d8 ++# define MACH64_BKGD_SRC_BKGD_CLR (0 << 0) ++# define MACH64_BKGD_SRC_FRGD_CLR (1 << 0) ++# define MACH64_BKGD_SRC_HOST (2 << 0) ++# define MACH64_BKGD_SRC_BLIT (3 << 0) ++# define MACH64_BKGD_SRC_PATTERN (4 << 0) ++# define MACH64_BKGD_SRC_3D (5 << 0) ++# define MACH64_FRGD_SRC_BKGD_CLR (0 << 8) ++# define MACH64_FRGD_SRC_FRGD_CLR (1 << 8) ++# define MACH64_FRGD_SRC_HOST (2 << 8) ++# define MACH64_FRGD_SRC_BLIT (3 << 8) ++# define MACH64_FRGD_SRC_PATTERN (4 << 8) ++# define MACH64_FRGD_SRC_3D (5 << 8) ++# define MACH64_MONO_SRC_ONE (0 << 16) ++# define MACH64_MONO_SRC_PATTERN (1 << 16) ++# define MACH64_MONO_SRC_HOST (2 << 16) ++# define MACH64_MONO_SRC_BLIT (3 << 16) ++ ++#define MACH64_DP_WRITE_MASK 0x06c8 ++ ++#define MACH64_DST_CNTL 0x0530 ++# define MACH64_DST_X_RIGHT_TO_LEFT (0 << 0) ++# define MACH64_DST_X_LEFT_TO_RIGHT (1 << 0) ++# define MACH64_DST_Y_BOTTOM_TO_TOP (0 << 1) ++# define MACH64_DST_Y_TOP_TO_BOTTOM (1 << 1) ++# define MACH64_DST_X_MAJOR (0 << 2) ++# define MACH64_DST_Y_MAJOR (1 << 2) ++# define MACH64_DST_X_TILE (1 << 3) ++# define MACH64_DST_Y_TILE (1 << 4) ++# define MACH64_DST_LAST_PEL (1 << 5) ++# define MACH64_DST_POLYGON_ENABLE (1 << 6) ++# define MACH64_DST_24_ROTATION_ENABLE (1 << 7) ++ ++#define MACH64_DST_HEIGHT_WIDTH 0x0518 ++#define MACH64_DST_OFF_PITCH 0x0500 ++#define MACH64_DST_WIDTH_HEIGHT 0x06ec ++#define MACH64_DST_X_Y 0x06e8 ++#define MACH64_DST_Y_X 0x050c ++ ++#define MACH64_FIFO_STAT 0x0710 ++# define MACH64_FIFO_SLOT_MASK 0x0000ffff ++# define MACH64_FIFO_ERR (1 << 31) ++ ++#define MACH64_GEN_TEST_CNTL 0x04d0 ++# define MACH64_GUI_ENGINE_ENABLE (1 << 8) ++#define MACH64_GUI_CMDFIFO_DEBUG 0x0170 ++#define MACH64_GUI_CMDFIFO_DATA 0x0174 ++#define MACH64_GUI_CNTL 0x0178 ++# define MACH64_CMDFIFO_SIZE_MASK 0x00000003ul ++# define MACH64_CMDFIFO_SIZE_192 0x00000000ul ++# define MACH64_CMDFIFO_SIZE_128 0x00000001ul ++# define MACH64_CMDFIFO_SIZE_64 0x00000002ul ++#define MACH64_GUI_STAT 0x0738 ++# define MACH64_GUI_ACTIVE (1 << 0) ++#define MACH64_GUI_TRAJ_CNTL 0x0730 ++ ++#define MACH64_HOST_CNTL 0x0640 ++#define MACH64_HOST_DATA0 0x0600 ++ ++#define MACH64_ONE_OVER_AREA 0x029c ++#define MACH64_ONE_OVER_AREA_UC 0x0300 ++ ++#define MACH64_PAT_REG0 0x0680 ++#define MACH64_PAT_REG1 0x0684 ++ ++#define MACH64_SC_LEFT 0x06a0 ++#define MACH64_SC_RIGHT 0x06a4 ++#define MACH64_SC_LEFT_RIGHT 0x06a8 ++#define MACH64_SC_TOP 0x06ac ++#define MACH64_SC_BOTTOM 0x06b0 ++#define MACH64_SC_TOP_BOTTOM 0x06b4 ++ ++#define MACH64_SCALE_3D_CNTL 0x05fc ++#define MACH64_SCRATCH_REG0 0x0480 ++#define MACH64_SCRATCH_REG1 0x0484 ++#define MACH64_SECONDARY_TEX_OFF 0x0778 ++#define MACH64_SETUP_CNTL 0x0304 ++#define MACH64_SRC_CNTL 0x05b4 ++# define MACH64_SRC_BM_ENABLE (1 << 8) ++# define MACH64_SRC_BM_SYNC (1 << 9) ++# define MACH64_SRC_BM_OP_FRAME_TO_SYSTEM (0 << 10) ++# define MACH64_SRC_BM_OP_SYSTEM_TO_FRAME (1 << 10) ++# define MACH64_SRC_BM_OP_REG_TO_SYSTEM (2 << 10) ++# define MACH64_SRC_BM_OP_SYSTEM_TO_REG (3 << 10) ++#define MACH64_SRC_HEIGHT1 0x0594 ++#define MACH64_SRC_HEIGHT2 0x05ac ++#define MACH64_SRC_HEIGHT1_WIDTH1 0x0598 ++#define MACH64_SRC_HEIGHT2_WIDTH2 0x05b0 ++#define MACH64_SRC_OFF_PITCH 0x0580 ++#define MACH64_SRC_WIDTH1 0x0590 ++#define MACH64_SRC_Y_X 0x058c ++ ++#define MACH64_TEX_0_OFF 0x05c0 ++#define MACH64_TEX_CNTL 0x0774 ++#define MACH64_TEX_SIZE_PITCH 0x0770 ++#define MACH64_TIMER_CONFIG 0x0428 ++ ++#define MACH64_VERTEX_1_ARGB 0x0254 ++#define MACH64_VERTEX_1_S 0x0240 ++#define MACH64_VERTEX_1_SECONDARY_S 0x0328 ++#define MACH64_VERTEX_1_SECONDARY_T 0x032c ++#define MACH64_VERTEX_1_SECONDARY_W 0x0330 ++#define MACH64_VERTEX_1_SPEC_ARGB 0x024c ++#define MACH64_VERTEX_1_T 0x0244 ++#define MACH64_VERTEX_1_W 0x0248 ++#define MACH64_VERTEX_1_X_Y 0x0258 ++#define MACH64_VERTEX_1_Z 0x0250 ++#define MACH64_VERTEX_2_ARGB 0x0274 ++#define MACH64_VERTEX_2_S 0x0260 ++#define MACH64_VERTEX_2_SECONDARY_S 0x0334 ++#define MACH64_VERTEX_2_SECONDARY_T 0x0338 ++#define MACH64_VERTEX_2_SECONDARY_W 0x033c ++#define MACH64_VERTEX_2_SPEC_ARGB 0x026c ++#define MACH64_VERTEX_2_T 0x0264 ++#define MACH64_VERTEX_2_W 0x0268 ++#define MACH64_VERTEX_2_X_Y 0x0278 ++#define MACH64_VERTEX_2_Z 0x0270 ++#define MACH64_VERTEX_3_ARGB 0x0294 ++#define MACH64_VERTEX_3_S 0x0280 ++#define MACH64_VERTEX_3_SECONDARY_S 0x02a0 ++#define MACH64_VERTEX_3_SECONDARY_T 0x02a4 ++#define MACH64_VERTEX_3_SECONDARY_W 0x02a8 ++#define MACH64_VERTEX_3_SPEC_ARGB 0x028c ++#define MACH64_VERTEX_3_T 0x0284 ++#define MACH64_VERTEX_3_W 0x0288 ++#define MACH64_VERTEX_3_X_Y 0x0298 ++#define MACH64_VERTEX_3_Z 0x0290 ++ ++#define MACH64_Z_CNTL 0x054c ++#define MACH64_Z_OFF_PITCH 0x0548 ++ ++#define MACH64_CRTC_VLINE_CRNT_VLINE 0x0410 ++# define MACH64_CRTC_VLINE_MASK 0x000007ff ++# define MACH64_CRTC_CRNT_VLINE_MASK 0x07ff0000 ++#define MACH64_CRTC_OFF_PITCH 0x0414 ++#define MACH64_CRTC_INT_CNTL 0x0418 ++# define MACH64_CRTC_VBLANK (1 << 0) ++# define MACH64_CRTC_VBLANK_INT_EN (1 << 1) ++# define MACH64_CRTC_VBLANK_INT (1 << 2) ++# define MACH64_CRTC_VLINE_INT_EN (1 << 3) ++# define MACH64_CRTC_VLINE_INT (1 << 4) ++# define MACH64_CRTC_VLINE_SYNC (1 << 5) /* 0=even, 1=odd */ ++# define MACH64_CRTC_FRAME (1 << 6) /* 0=even, 1=odd */ ++# define MACH64_CRTC_SNAPSHOT_INT_EN (1 << 7) ++# define MACH64_CRTC_SNAPSHOT_INT (1 << 8) ++# define MACH64_CRTC_I2C_INT_EN (1 << 9) ++# define MACH64_CRTC_I2C_INT (1 << 10) ++# define MACH64_CRTC2_VBLANK (1 << 11) /* LT Pro */ ++# define MACH64_CRTC2_VBLANK_INT_EN (1 << 12) /* LT Pro */ ++# define MACH64_CRTC2_VBLANK_INT (1 << 13) /* LT Pro */ ++# define MACH64_CRTC2_VLINE_INT_EN (1 << 14) /* LT Pro */ ++# define MACH64_CRTC2_VLINE_INT (1 << 15) /* LT Pro */ ++# define MACH64_CRTC_CAPBUF0_INT_EN (1 << 16) ++# define MACH64_CRTC_CAPBUF0_INT (1 << 17) ++# define MACH64_CRTC_CAPBUF1_INT_EN (1 << 18) ++# define MACH64_CRTC_CAPBUF1_INT (1 << 19) ++# define MACH64_CRTC_OVERLAY_EOF_INT_EN (1 << 20) ++# define MACH64_CRTC_OVERLAY_EOF_INT (1 << 21) ++# define MACH64_CRTC_ONESHOT_CAP_INT_EN (1 << 22) ++# define MACH64_CRTC_ONESHOT_CAP_INT (1 << 23) ++# define MACH64_CRTC_BUSMASTER_EOL_INT_EN (1 << 24) ++# define MACH64_CRTC_BUSMASTER_EOL_INT (1 << 25) ++# define MACH64_CRTC_GP_INT_EN (1 << 26) ++# define MACH64_CRTC_GP_INT (1 << 27) ++# define MACH64_CRTC2_VLINE_SYNC (1 << 28) /* LT Pro */ /* 0=even, 1=odd */ ++# define MACH64_CRTC_SNAPSHOT2_INT_EN (1 << 29) /* LT Pro */ ++# define MACH64_CRTC_SNAPSHOT2_INT (1 << 30) /* LT Pro */ ++# define MACH64_CRTC_VBLANK2_INT (1 << 31) ++# define MACH64_CRTC_INT_ENS \ ++ ( \ ++ MACH64_CRTC_VBLANK_INT_EN | \ ++ MACH64_CRTC_VLINE_INT_EN | \ ++ MACH64_CRTC_SNAPSHOT_INT_EN | \ ++ MACH64_CRTC_I2C_INT_EN | \ ++ MACH64_CRTC2_VBLANK_INT_EN | \ ++ MACH64_CRTC2_VLINE_INT_EN | \ ++ MACH64_CRTC_CAPBUF0_INT_EN | \ ++ MACH64_CRTC_CAPBUF1_INT_EN | \ ++ MACH64_CRTC_OVERLAY_EOF_INT_EN | \ ++ MACH64_CRTC_ONESHOT_CAP_INT_EN | \ ++ MACH64_CRTC_BUSMASTER_EOL_INT_EN | \ ++ MACH64_CRTC_GP_INT_EN | \ ++ MACH64_CRTC_SNAPSHOT2_INT_EN | \ ++ 0 \ ++ ) ++# define MACH64_CRTC_INT_ACKS \ ++ ( \ ++ MACH64_CRTC_VBLANK_INT | \ ++ MACH64_CRTC_VLINE_INT | \ ++ MACH64_CRTC_SNAPSHOT_INT | \ ++ MACH64_CRTC_I2C_INT | \ ++ MACH64_CRTC2_VBLANK_INT | \ ++ MACH64_CRTC2_VLINE_INT | \ ++ MACH64_CRTC_CAPBUF0_INT | \ ++ MACH64_CRTC_CAPBUF1_INT | \ ++ MACH64_CRTC_OVERLAY_EOF_INT | \ ++ MACH64_CRTC_ONESHOT_CAP_INT | \ ++ MACH64_CRTC_BUSMASTER_EOL_INT | \ ++ MACH64_CRTC_GP_INT | \ ++ MACH64_CRTC_SNAPSHOT2_INT | \ ++ MACH64_CRTC_VBLANK2_INT | \ ++ 0 \ ++ ) ++ ++#define MACH64_DATATYPE_CI8 2 ++#define MACH64_DATATYPE_ARGB1555 3 ++#define MACH64_DATATYPE_RGB565 4 ++#define MACH64_DATATYPE_ARGB8888 6 ++#define MACH64_DATATYPE_RGB332 7 ++#define MACH64_DATATYPE_Y8 8 ++#define MACH64_DATATYPE_RGB8 9 ++#define MACH64_DATATYPE_VYUY422 11 ++#define MACH64_DATATYPE_YVYU422 12 ++#define MACH64_DATATYPE_AYUV444 14 ++#define MACH64_DATATYPE_ARGB4444 15 ++ ++#define MACH64_READ(reg) DRM_READ32(dev_priv->mmio, (reg) ) ++#define MACH64_WRITE(reg,val) DRM_WRITE32(dev_priv->mmio, (reg), (val) ) ++ ++#define DWMREG0 0x0400 ++#define DWMREG0_END 0x07ff ++#define DWMREG1 0x0000 ++#define DWMREG1_END 0x03ff ++ ++#define ISREG0(r) (((r) >= DWMREG0) && ((r) <= DWMREG0_END)) ++#define DMAREG0(r) (((r) - DWMREG0) >> 2) ++#define DMAREG1(r) ((((r) - DWMREG1) >> 2 ) | 0x0100) ++#define DMAREG(r) (ISREG0(r) ? DMAREG0(r) : DMAREG1(r)) ++ ++#define MMREG0 0x0000 ++#define MMREG0_END 0x00ff ++ ++#define ISMMREG0(r) (((r) >= MMREG0) && ((r) <= MMREG0_END)) ++#define MMSELECT0(r) (((r) << 2) + DWMREG0) ++#define MMSELECT1(r) (((((r) & 0xff) << 2) + DWMREG1)) ++#define MMSELECT(r) (ISMMREG0(r) ? MMSELECT0(r) : MMSELECT1(r)) ++ ++/* ================================================================ ++ * DMA constants ++ */ ++ ++/* DMA descriptor field indices: ++ * The descriptor fields are loaded into the read-only ++ * BM_* system bus master registers during a bus-master operation ++ */ ++#define MACH64_DMA_FRAME_BUF_OFFSET 0 /* BM_FRAME_BUF_OFFSET */ ++#define MACH64_DMA_SYS_MEM_ADDR 1 /* BM_SYSTEM_MEM_ADDR */ ++#define MACH64_DMA_COMMAND 2 /* BM_COMMAND */ ++#define MACH64_DMA_RESERVED 3 /* BM_STATUS */ ++ ++/* BM_COMMAND descriptor field flags */ ++#define MACH64_DMA_HOLD_OFFSET (1<<30) /* Don't increment DMA_FRAME_BUF_OFFSET */ ++#define MACH64_DMA_EOL (1<<31) /* End of descriptor list flag */ ++ ++#define MACH64_DMA_CHUNKSIZE 0x1000 /* 4kB per DMA descriptor */ ++#define MACH64_APERTURE_OFFSET 0x7ff800 /* frame-buffer offset for gui-masters */ ++ ++/* ================================================================ ++ * Ring operations ++ * ++ * Since the Mach64 bus master engine requires polling, these functions end ++ * up being called frequently, hence being inline. ++ */ ++ ++static __inline__ void mach64_ring_start(drm_mach64_private_t * dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ ++ DRM_DEBUG("head_addr: 0x%08x head: %d tail: %d space: %d\n", ++ ring->head_addr, ring->head, ring->tail, ring->space); ++ ++ if (mach64_do_wait_for_idle(dev_priv) < 0) { ++ mach64_do_engine_reset(dev_priv); ++ } ++ ++ if (dev_priv->driver_mode != MACH64_MODE_MMIO) { ++ /* enable bus mastering and block 1 registers */ ++ MACH64_WRITE(MACH64_BUS_CNTL, ++ (MACH64_READ(MACH64_BUS_CNTL) & ++ ~MACH64_BUS_MASTER_DIS) ++ | MACH64_BUS_EXT_REG_EN); ++ mach64_do_wait_for_idle(dev_priv); ++ } ++ ++ /* reset descriptor table ring head */ ++ MACH64_WRITE(MACH64_BM_GUI_TABLE_CMD, ++ ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB); ++ ++ dev_priv->ring_running = 1; ++} ++ ++static __inline__ void mach64_ring_resume(drm_mach64_private_t * dev_priv, ++ drm_mach64_descriptor_ring_t * ring) ++{ ++ DRM_DEBUG("head_addr: 0x%08x head: %d tail: %d space: %d\n", ++ ring->head_addr, ring->head, ring->tail, ring->space); ++ ++ /* reset descriptor table ring head */ ++ MACH64_WRITE(MACH64_BM_GUI_TABLE_CMD, ++ ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB); ++ ++ if (dev_priv->driver_mode == MACH64_MODE_MMIO) { ++ mach64_do_dispatch_pseudo_dma(dev_priv); ++ } else { ++ /* enable GUI bus mastering, and sync the bus master to the GUI */ ++ MACH64_WRITE(MACH64_SRC_CNTL, ++ MACH64_SRC_BM_ENABLE | MACH64_SRC_BM_SYNC | ++ MACH64_SRC_BM_OP_SYSTEM_TO_REG); ++ ++ /* kick off the transfer */ ++ MACH64_WRITE(MACH64_DST_HEIGHT_WIDTH, 0); ++ if (dev_priv->driver_mode == MACH64_MODE_DMA_SYNC) { ++ if ((mach64_do_wait_for_idle(dev_priv)) < 0) { ++ DRM_ERROR("idle failed, resetting engine\n"); ++ mach64_dump_engine_info(dev_priv); ++ mach64_do_engine_reset(dev_priv); ++ return; ++ } ++ mach64_do_release_used_buffers(dev_priv); ++ } ++ } ++} ++ ++/** ++ * Poll the ring head and make sure the bus master is alive. ++ * ++ * Mach64's bus master engine will stop if there are no more entries to process. ++ * This function polls the engine for the last processed entry and calls ++ * mach64_ring_resume if there is an unprocessed entry. ++ * ++ * Note also that, since we update the ring tail while the bus master engine is ++ * in operation, it is possible that the last tail update was too late to be ++ * processed, and the bus master engine stops at the previous tail position. ++ * Therefore it is important to call this function frequently. ++ */ ++static __inline__ void mach64_ring_tick(drm_mach64_private_t * dev_priv, ++ drm_mach64_descriptor_ring_t * ring) ++{ ++ DRM_DEBUG("head_addr: 0x%08x head: %d tail: %d space: %d\n", ++ ring->head_addr, ring->head, ring->tail, ring->space); ++ ++ if (!dev_priv->ring_running) { ++ mach64_ring_start(dev_priv); ++ ++ if (ring->head != ring->tail) { ++ mach64_ring_resume(dev_priv, ring); ++ } ++ } else { ++ /* GUI_ACTIVE must be read before BM_GUI_TABLE to ++ * correctly determine the ring head ++ */ ++ int gui_active = ++ MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE; ++ ++ ring->head_addr = MACH64_READ(MACH64_BM_GUI_TABLE) & 0xfffffff0; ++ ++ if (gui_active) { ++ /* If not idle, BM_GUI_TABLE points one descriptor ++ * past the current head ++ */ ++ if (ring->head_addr == ring->start_addr) { ++ ring->head_addr += ring->size; ++ } ++ ring->head_addr -= 4 * sizeof(u32); ++ } ++ ++ if (ring->head_addr < ring->start_addr || ++ ring->head_addr >= ring->start_addr + ring->size) { ++ DRM_ERROR("bad ring head address: 0x%08x\n", ++ ring->head_addr); ++ mach64_dump_ring_info(dev_priv); ++ mach64_do_engine_reset(dev_priv); ++ return; ++ } ++ ++ ring->head = (ring->head_addr - ring->start_addr) / sizeof(u32); ++ ++ if (!gui_active && ring->head != ring->tail) { ++ mach64_ring_resume(dev_priv, ring); ++ } ++ } ++} ++ ++static __inline__ void mach64_ring_stop(drm_mach64_private_t * dev_priv) ++{ ++ DRM_DEBUG("head_addr: 0x%08x head: %d tail: %d space: %d\n", ++ dev_priv->ring.head_addr, dev_priv->ring.head, ++ dev_priv->ring.tail, dev_priv->ring.space); ++ ++ /* restore previous SRC_CNTL to disable busmastering */ ++ mach64_do_wait_for_fifo(dev_priv, 1); ++ MACH64_WRITE(MACH64_SRC_CNTL, 0); ++ ++ /* disable busmastering but keep the block 1 registers enabled */ ++ mach64_do_wait_for_idle(dev_priv); ++ MACH64_WRITE(MACH64_BUS_CNTL, MACH64_READ(MACH64_BUS_CNTL) ++ | MACH64_BUS_MASTER_DIS | MACH64_BUS_EXT_REG_EN); ++ ++ dev_priv->ring_running = 0; ++} ++ ++static __inline__ void ++mach64_update_ring_snapshot(drm_mach64_private_t * dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ ++ DRM_DEBUG("\n"); ++ ++ mach64_ring_tick(dev_priv, ring); ++ ++ ring->space = (ring->head - ring->tail) * sizeof(u32); ++ if (ring->space <= 0) { ++ ring->space += ring->size; ++ } ++} ++ ++/* ================================================================ ++ * DMA macros ++ * ++ * Mach64's ring buffer doesn't take register writes directly. These ++ * have to be written indirectly in DMA buffers. These macros simplify ++ * the task of setting up a buffer, writing commands to it, and ++ * queuing the buffer in the ring. ++ */ ++ ++#define DMALOCALS \ ++ drm_mach64_freelist_t *_entry = NULL; \ ++ struct drm_buf *_buf = NULL; \ ++ u32 *_buf_wptr; int _outcount ++ ++#define GETBUFPTR( __buf ) \ ++((dev_priv->is_pci) ? \ ++ ((u32 *)(__buf)->address) : \ ++ ((u32 *)((char *)dev_priv->dev_buffers->handle + (__buf)->offset))) ++ ++#define GETBUFADDR( __buf ) ((u32)(__buf)->bus_address) ++ ++#define GETRINGOFFSET() (_entry->ring_ofs) ++ ++static __inline__ int mach64_find_pending_buf_entry(drm_mach64_private_t * ++ dev_priv, ++ drm_mach64_freelist_t ** ++ entry, struct drm_buf * buf) ++{ ++ struct list_head *ptr; ++#if MACH64_EXTRA_CHECKING ++ if (list_empty(&dev_priv->pending)) { ++ DRM_ERROR("Empty pending list in \n"); ++ return -EINVAL; ++ } ++#endif ++ ptr = dev_priv->pending.prev; ++ *entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ while ((*entry)->buf != buf) { ++ if (ptr == &dev_priv->pending) { ++ return -EFAULT; ++ } ++ ptr = ptr->prev; ++ *entry = list_entry(ptr, drm_mach64_freelist_t, list); ++ } ++ return 0; ++} ++ ++#define DMASETPTR( _p ) \ ++do { \ ++ _buf = (_p); \ ++ _outcount = 0; \ ++ _buf_wptr = GETBUFPTR( _buf ); \ ++} while(0) ++ ++/* FIXME: use a private set of smaller buffers for state emits, clears, and swaps? */ ++#define DMAGETPTR( file_priv, dev_priv, n ) \ ++do { \ ++ if ( MACH64_VERBOSE ) { \ ++ DRM_INFO( "DMAGETPTR( %d )\n", (n) ); \ ++ } \ ++ _buf = mach64_freelist_get( dev_priv ); \ ++ if (_buf == NULL) { \ ++ DRM_ERROR("couldn't get buffer in DMAGETPTR\n"); \ ++ return -EAGAIN; \ ++ } \ ++ if (_buf->pending) { \ ++ DRM_ERROR("pending buf in DMAGETPTR\n"); \ ++ return -EFAULT; \ ++ } \ ++ _buf->file_priv = file_priv; \ ++ _outcount = 0; \ ++ \ ++ _buf_wptr = GETBUFPTR( _buf ); \ ++} while (0) ++ ++#define DMAOUTREG( reg, val ) \ ++do { \ ++ if ( MACH64_VERBOSE ) { \ ++ DRM_INFO( " DMAOUTREG( 0x%x = 0x%08x )\n", \ ++ reg, val ); \ ++ } \ ++ _buf_wptr[_outcount++] = cpu_to_le32(DMAREG(reg)); \ ++ _buf_wptr[_outcount++] = cpu_to_le32((val)); \ ++ _buf->used += 8; \ ++} while (0) ++ ++#define DMAADVANCE( dev_priv, _discard ) \ ++ do { \ ++ struct list_head *ptr; \ ++ int ret; \ ++ \ ++ if ( MACH64_VERBOSE ) { \ ++ DRM_INFO( "DMAADVANCE() in \n" ); \ ++ } \ ++ \ ++ if (_buf->used <= 0) { \ ++ DRM_ERROR( "DMAADVANCE(): sending empty buf %d\n", \ ++ _buf->idx ); \ ++ return -EFAULT; \ ++ } \ ++ if (_buf->pending) { \ ++ /* This is a resued buffer, so we need to find it in the pending list */ \ ++ if ((ret = mach64_find_pending_buf_entry(dev_priv, &_entry, _buf))) { \ ++ DRM_ERROR( "DMAADVANCE(): couldn't find pending buf %d\n", _buf->idx ); \ ++ return ret; \ ++ } \ ++ if (_entry->discard) { \ ++ DRM_ERROR( "DMAADVANCE(): sending discarded pending buf %d\n", _buf->idx ); \ ++ return -EFAULT; \ ++ } \ ++ } else { \ ++ if (list_empty(&dev_priv->placeholders)) { \ ++ DRM_ERROR( "DMAADVANCE(): empty placeholder list\n"); \ ++ return -EFAULT; \ ++ } \ ++ ptr = dev_priv->placeholders.next; \ ++ list_del(ptr); \ ++ _entry = list_entry(ptr, drm_mach64_freelist_t, list); \ ++ _buf->pending = 1; \ ++ _entry->buf = _buf; \ ++ list_add_tail(ptr, &dev_priv->pending); \ ++ } \ ++ _entry->discard = (_discard); \ ++ if ((ret = mach64_add_buf_to_ring( dev_priv, _entry ))) \ ++ return ret; \ ++ } while (0) ++ ++#define DMADISCARDBUF() \ ++ do { \ ++ if (_entry == NULL) { \ ++ int ret; \ ++ if ((ret = mach64_find_pending_buf_entry(dev_priv, &_entry, _buf))) { \ ++ DRM_ERROR( "couldn't find pending buf %d\n", \ ++ _buf->idx ); \ ++ return ret; \ ++ } \ ++ } \ ++ _entry->discard = 1; \ ++ } while(0) ++ ++#define DMAADVANCEHOSTDATA( dev_priv ) \ ++ do { \ ++ struct list_head *ptr; \ ++ int ret; \ ++ \ ++ if ( MACH64_VERBOSE ) { \ ++ DRM_INFO( "DMAADVANCEHOSTDATA() in \n" ); \ ++ } \ ++ \ ++ if (_buf->used <= 0) { \ ++ DRM_ERROR( "DMAADVANCEHOSTDATA(): sending empty buf %d\n", _buf->idx ); \ ++ return -EFAULT; \ ++ } \ ++ if (list_empty(&dev_priv->placeholders)) { \ ++ DRM_ERROR( "empty placeholder list in DMAADVANCEHOSTDATA()\n" ); \ ++ return -EFAULT; \ ++ } \ ++ \ ++ ptr = dev_priv->placeholders.next; \ ++ list_del(ptr); \ ++ _entry = list_entry(ptr, drm_mach64_freelist_t, list); \ ++ _entry->buf = _buf; \ ++ _entry->buf->pending = 1; \ ++ list_add_tail(ptr, &dev_priv->pending); \ ++ _entry->discard = 1; \ ++ if ((ret = mach64_add_hostdata_buf_to_ring( dev_priv, _entry ))) \ ++ return ret; \ ++ } while (0) ++ ++#endif /* __MACH64_DRV_H__ */ +--- /dev/null ++++ b/drivers/gpu/drm/mach64/mach64_irq.c +@@ -0,0 +1,159 @@ ++/* mach64_irq.c -- IRQ handling for ATI Mach64 -*- linux-c -*- ++ * Created: Tue Feb 25, 2003 by Leif Delgass, based on radeon_irq.c/r128_irq.c ++ */ ++/*- ++ * Copyright (C) The Weather Channel, Inc. 2002. ++ * Copyright 2003 Leif Delgass ++ * All Rights Reserved. ++ * ++ * The Weather Channel (TM) funded Tungsten Graphics to develop the ++ * initial release of the Radeon 8500 driver under the XFree86 license. ++ * This notice must be preserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR ++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: ++ * Keith Whitwell ++ * Eric Anholt ++ * Leif Delgass ++ */ ++ ++#include "drmP.h" ++#include "drm.h" ++#include "mach64_drm.h" ++#include "mach64_drv.h" ++ ++irqreturn_t mach64_driver_irq_handler(DRM_IRQ_ARGS) ++{ ++ struct drm_device *dev = arg; ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ int status; ++ ++ status = MACH64_READ(MACH64_CRTC_INT_CNTL); ++ ++ /* VBLANK interrupt */ ++ if (status & MACH64_CRTC_VBLANK_INT) { ++ /* Mask off all interrupt ack bits before setting the ack bit, since ++ * there may be other handlers outside the DRM. ++ * ++ * NOTE: On mach64, you need to keep the enable bits set when doing ++ * the ack, despite what the docs say about not acking and enabling ++ * in a single write. ++ */ ++ MACH64_WRITE(MACH64_CRTC_INT_CNTL, ++ (status & ~MACH64_CRTC_INT_ACKS) ++ | MACH64_CRTC_VBLANK_INT); ++ ++ atomic_inc(&dev_priv->vbl_received); ++ drm_handle_vblank(dev, 0); ++ return IRQ_HANDLED; ++ } ++ return IRQ_NONE; ++} ++ ++u32 mach64_get_vblank_counter(struct drm_device * dev, int crtc) ++{ ++ const drm_mach64_private_t *const dev_priv = dev->dev_private; ++ ++ if (crtc != 0) ++ return 0; ++ ++ return atomic_read(&dev_priv->vbl_received); ++} ++ ++int mach64_enable_vblank(struct drm_device * dev, int crtc) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ u32 status = MACH64_READ(MACH64_CRTC_INT_CNTL); ++ ++ if (crtc != 0) { ++ DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", ++ crtc); ++ return -EINVAL; ++ } ++ ++ DRM_DEBUG("before enable vblank CRTC_INT_CTNL: 0x%08x\n", status); ++ ++ /* Turn on VBLANK interrupt */ ++ MACH64_WRITE(MACH64_CRTC_INT_CNTL, MACH64_READ(MACH64_CRTC_INT_CNTL) ++ | MACH64_CRTC_VBLANK_INT_EN); ++ ++ return 0; ++} ++ ++void mach64_disable_vblank(struct drm_device * dev, int crtc) ++{ ++ if (crtc != 0) { ++ DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", ++ crtc); ++ return; ++ } ++ ++ /* ++ * FIXME: implement proper interrupt disable by using the vblank ++ * counter register (if available). ++ */ ++} ++ ++static void mach64_disable_vblank_local(struct drm_device * dev, int crtc) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ u32 status = MACH64_READ(MACH64_CRTC_INT_CNTL); ++ ++ if (crtc != 0) { ++ DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", ++ crtc); ++ return; ++ } ++ ++ DRM_DEBUG("before disable vblank CRTC_INT_CTNL: 0x%08x\n", status); ++ ++ /* Disable and clear VBLANK interrupt */ ++ MACH64_WRITE(MACH64_CRTC_INT_CNTL, (status & ~MACH64_CRTC_VBLANK_INT_EN) ++ | MACH64_CRTC_VBLANK_INT); ++} ++ ++void mach64_driver_irq_preinstall(struct drm_device * dev) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ ++ u32 status = MACH64_READ(MACH64_CRTC_INT_CNTL); ++ ++ DRM_DEBUG("before install CRTC_INT_CTNL: 0x%08x\n", status); ++ ++ mach64_disable_vblank_local(dev, 0); ++} ++ ++int mach64_driver_irq_postinstall(struct drm_device * dev) ++{ ++ return drm_vblank_init(dev, 1); ++} ++ ++void mach64_driver_irq_uninstall(struct drm_device * dev) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ if (!dev_priv) ++ return; ++ ++ mach64_disable_vblank_local(dev, 0); ++ ++ DRM_DEBUG("after uninstall CRTC_INT_CTNL: 0x%08x\n", ++ MACH64_READ(MACH64_CRTC_INT_CNTL)); ++} +--- /dev/null ++++ b/drivers/gpu/drm/mach64/mach64_state.c +@@ -0,0 +1,910 @@ ++/* mach64_state.c -- State support for mach64 (Rage Pro) driver -*- linux-c -*- ++ * Created: Sun Dec 03 19:20:26 2000 by gareth@valinux.com ++ */ ++/* ++ * Copyright 2000 Gareth Hughes ++ * Copyright 2002-2003 Leif Delgass ++ * All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Authors: ++ * Gareth Hughes ++ * Leif Delgass ++ * José Fonseca ++ */ ++ ++#include "drmP.h" ++#include "drm.h" ++#include "mach64_drm.h" ++#include "mach64_drv.h" ++ ++/* Interface history: ++ * ++ * 1.0 - Initial mach64 DRM ++ * ++ */ ++struct drm_ioctl_desc mach64_ioctls[] = { ++ DRM_IOCTL_DEF(DRM_MACH64_INIT, mach64_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), ++ DRM_IOCTL_DEF(DRM_MACH64_CLEAR, mach64_dma_clear, DRM_AUTH), ++ DRM_IOCTL_DEF(DRM_MACH64_SWAP, mach64_dma_swap, DRM_AUTH), ++ DRM_IOCTL_DEF(DRM_MACH64_IDLE, mach64_dma_idle, DRM_AUTH), ++ DRM_IOCTL_DEF(DRM_MACH64_RESET, mach64_engine_reset, DRM_AUTH), ++ DRM_IOCTL_DEF(DRM_MACH64_VERTEX, mach64_dma_vertex, DRM_AUTH), ++ DRM_IOCTL_DEF(DRM_MACH64_BLIT, mach64_dma_blit, DRM_AUTH), ++ DRM_IOCTL_DEF(DRM_MACH64_FLUSH, mach64_dma_flush, DRM_AUTH), ++ DRM_IOCTL_DEF(DRM_MACH64_GETPARAM, mach64_get_param, DRM_AUTH), ++}; ++ ++int mach64_max_ioctl = DRM_ARRAY_SIZE(mach64_ioctls); ++ ++/* ================================================================ ++ * DMA hardware state programming functions ++ */ ++ ++static void mach64_print_dirty(const char *msg, unsigned int flags) ++{ ++ DRM_DEBUG("%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s\n", ++ msg, ++ flags, ++ (flags & MACH64_UPLOAD_DST_OFF_PITCH) ? "dst_off_pitch, " : ++ "", ++ (flags & MACH64_UPLOAD_Z_ALPHA_CNTL) ? "z_alpha_cntl, " : "", ++ (flags & MACH64_UPLOAD_SCALE_3D_CNTL) ? "scale_3d_cntl, " : ++ "", (flags & MACH64_UPLOAD_DP_FOG_CLR) ? "dp_fog_clr, " : "", ++ (flags & MACH64_UPLOAD_DP_WRITE_MASK) ? "dp_write_mask, " : ++ "", ++ (flags & MACH64_UPLOAD_DP_PIX_WIDTH) ? "dp_pix_width, " : "", ++ (flags & MACH64_UPLOAD_SETUP_CNTL) ? "setup_cntl, " : "", ++ (flags & MACH64_UPLOAD_MISC) ? "misc, " : "", ++ (flags & MACH64_UPLOAD_TEXTURE) ? "texture, " : "", ++ (flags & MACH64_UPLOAD_TEX0IMAGE) ? "tex0 image, " : "", ++ (flags & MACH64_UPLOAD_TEX1IMAGE) ? "tex1 image, " : "", ++ (flags & MACH64_UPLOAD_CLIPRECTS) ? "cliprects, " : ""); ++} ++ ++/* Mach64 doesn't have hardware cliprects, just one hardware scissor, ++ * so the GL scissor is intersected with each cliprect here ++ */ ++/* This function returns 0 on success, 1 for no intersection, and ++ * negative for an error ++ */ ++static int mach64_emit_cliprect(struct drm_file *file_priv, ++ drm_mach64_private_t * dev_priv, ++ struct drm_clip_rect * box) ++{ ++ u32 sc_left_right, sc_top_bottom; ++ struct drm_clip_rect scissor; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ drm_mach64_context_regs_t *regs = &sarea_priv->context_state; ++ DMALOCALS; ++ ++ DRM_DEBUG("box=%p\n", box); ++ ++ /* Get GL scissor */ ++ /* FIXME: store scissor in SAREA as a cliprect instead of in ++ * hardware format, or do intersection client-side ++ */ ++ scissor.x1 = regs->sc_left_right & 0xffff; ++ scissor.x2 = (regs->sc_left_right & 0xffff0000) >> 16; ++ scissor.y1 = regs->sc_top_bottom & 0xffff; ++ scissor.y2 = (regs->sc_top_bottom & 0xffff0000) >> 16; ++ ++ /* Intersect GL scissor with cliprect */ ++ if (box->x1 > scissor.x1) ++ scissor.x1 = box->x1; ++ if (box->y1 > scissor.y1) ++ scissor.y1 = box->y1; ++ if (box->x2 < scissor.x2) ++ scissor.x2 = box->x2; ++ if (box->y2 < scissor.y2) ++ scissor.y2 = box->y2; ++ /* positive return means skip */ ++ if (scissor.x1 >= scissor.x2) ++ return 1; ++ if (scissor.y1 >= scissor.y2) ++ return 1; ++ ++ DMAGETPTR(file_priv, dev_priv, 2); /* returns on failure to get buffer */ ++ ++ sc_left_right = ((scissor.x1 << 0) | (scissor.x2 << 16)); ++ sc_top_bottom = ((scissor.y1 << 0) | (scissor.y2 << 16)); ++ ++ DMAOUTREG(MACH64_SC_LEFT_RIGHT, sc_left_right); ++ DMAOUTREG(MACH64_SC_TOP_BOTTOM, sc_top_bottom); ++ ++ DMAADVANCE(dev_priv, 1); ++ ++ return 0; ++} ++ ++static __inline__ int mach64_emit_state(struct drm_file *file_priv, ++ drm_mach64_private_t * dev_priv) ++{ ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ drm_mach64_context_regs_t *regs = &sarea_priv->context_state; ++ unsigned int dirty = sarea_priv->dirty; ++ u32 offset = ((regs->tex_size_pitch & 0xf0) >> 2); ++ DMALOCALS; ++ ++ if (MACH64_VERBOSE) { ++ mach64_print_dirty(__FUNCTION__, dirty); ++ } else { ++ DRM_DEBUG("dirty=0x%08x\n", dirty); ++ } ++ ++ DMAGETPTR(file_priv, dev_priv, 17); /* returns on failure to get buffer */ ++ ++ if (dirty & MACH64_UPLOAD_MISC) { ++ DMAOUTREG(MACH64_DP_MIX, regs->dp_mix); ++ DMAOUTREG(MACH64_DP_SRC, regs->dp_src); ++ DMAOUTREG(MACH64_CLR_CMP_CNTL, regs->clr_cmp_cntl); ++ DMAOUTREG(MACH64_GUI_TRAJ_CNTL, regs->gui_traj_cntl); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_MISC; ++ } ++ ++ if (dirty & MACH64_UPLOAD_DST_OFF_PITCH) { ++ DMAOUTREG(MACH64_DST_OFF_PITCH, regs->dst_off_pitch); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_DST_OFF_PITCH; ++ } ++ if (dirty & MACH64_UPLOAD_Z_OFF_PITCH) { ++ DMAOUTREG(MACH64_Z_OFF_PITCH, regs->z_off_pitch); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_Z_OFF_PITCH; ++ } ++ if (dirty & MACH64_UPLOAD_Z_ALPHA_CNTL) { ++ DMAOUTREG(MACH64_Z_CNTL, regs->z_cntl); ++ DMAOUTREG(MACH64_ALPHA_TST_CNTL, regs->alpha_tst_cntl); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_Z_ALPHA_CNTL; ++ } ++ if (dirty & MACH64_UPLOAD_SCALE_3D_CNTL) { ++ DMAOUTREG(MACH64_SCALE_3D_CNTL, regs->scale_3d_cntl); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_SCALE_3D_CNTL; ++ } ++ if (dirty & MACH64_UPLOAD_DP_FOG_CLR) { ++ DMAOUTREG(MACH64_DP_FOG_CLR, regs->dp_fog_clr); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_DP_FOG_CLR; ++ } ++ if (dirty & MACH64_UPLOAD_DP_WRITE_MASK) { ++ DMAOUTREG(MACH64_DP_WRITE_MASK, regs->dp_write_mask); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_DP_WRITE_MASK; ++ } ++ if (dirty & MACH64_UPLOAD_DP_PIX_WIDTH) { ++ DMAOUTREG(MACH64_DP_PIX_WIDTH, regs->dp_pix_width); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_DP_PIX_WIDTH; ++ } ++ if (dirty & MACH64_UPLOAD_SETUP_CNTL) { ++ DMAOUTREG(MACH64_SETUP_CNTL, regs->setup_cntl); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_SETUP_CNTL; ++ } ++ ++ if (dirty & MACH64_UPLOAD_TEXTURE) { ++ DMAOUTREG(MACH64_TEX_SIZE_PITCH, regs->tex_size_pitch); ++ DMAOUTREG(MACH64_TEX_CNTL, regs->tex_cntl); ++ DMAOUTREG(MACH64_SECONDARY_TEX_OFF, regs->secondary_tex_off); ++ DMAOUTREG(MACH64_TEX_0_OFF + offset, regs->tex_offset); ++ sarea_priv->dirty &= ~MACH64_UPLOAD_TEXTURE; ++ } ++ ++ DMAADVANCE(dev_priv, 1); ++ ++ sarea_priv->dirty &= MACH64_UPLOAD_CLIPRECTS; ++ ++ return 0; ++ ++} ++ ++/* ================================================================ ++ * DMA command dispatch functions ++ */ ++ ++static int mach64_dma_dispatch_clear(struct drm_device * dev, ++ struct drm_file *file_priv, ++ unsigned int flags, ++ int cx, int cy, int cw, int ch, ++ unsigned int clear_color, ++ unsigned int clear_depth) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ drm_mach64_context_regs_t *ctx = &sarea_priv->context_state; ++ int nbox = sarea_priv->nbox; ++ struct drm_clip_rect *pbox = sarea_priv->boxes; ++ u32 fb_bpp, depth_bpp; ++ int i; ++ DMALOCALS; ++ ++ DRM_DEBUG("\n"); ++ ++ switch (dev_priv->fb_bpp) { ++ case 16: ++ fb_bpp = MACH64_DATATYPE_RGB565; ++ break; ++ case 32: ++ fb_bpp = MACH64_DATATYPE_ARGB8888; ++ break; ++ default: ++ return -EINVAL; ++ } ++ switch (dev_priv->depth_bpp) { ++ case 16: ++ depth_bpp = MACH64_DATATYPE_RGB565; ++ break; ++ case 24: ++ case 32: ++ depth_bpp = MACH64_DATATYPE_ARGB8888; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (!nbox) ++ return 0; ++ ++ DMAGETPTR(file_priv, dev_priv, nbox * 31); /* returns on failure to get buffer */ ++ ++ for (i = 0; i < nbox; i++) { ++ int x = pbox[i].x1; ++ int y = pbox[i].y1; ++ int w = pbox[i].x2 - x; ++ int h = pbox[i].y2 - y; ++ ++ DRM_DEBUG("dispatch clear %d,%d-%d,%d flags 0x%x\n", ++ pbox[i].x1, pbox[i].y1, ++ pbox[i].x2, pbox[i].y2, flags); ++ ++ if (flags & (MACH64_FRONT | MACH64_BACK)) { ++ /* Setup for color buffer clears ++ */ ++ ++ DMAOUTREG(MACH64_Z_CNTL, 0); ++ DMAOUTREG(MACH64_SCALE_3D_CNTL, 0); ++ ++ DMAOUTREG(MACH64_SC_LEFT_RIGHT, ctx->sc_left_right); ++ DMAOUTREG(MACH64_SC_TOP_BOTTOM, ctx->sc_top_bottom); ++ ++ DMAOUTREG(MACH64_CLR_CMP_CNTL, 0); ++ DMAOUTREG(MACH64_GUI_TRAJ_CNTL, ++ (MACH64_DST_X_LEFT_TO_RIGHT | ++ MACH64_DST_Y_TOP_TO_BOTTOM)); ++ ++ DMAOUTREG(MACH64_DP_PIX_WIDTH, ((fb_bpp << 0) | ++ (fb_bpp << 4) | ++ (fb_bpp << 8) | ++ (fb_bpp << 16) | ++ (fb_bpp << 28))); ++ ++ DMAOUTREG(MACH64_DP_FRGD_CLR, clear_color); ++ DMAOUTREG(MACH64_DP_WRITE_MASK, ctx->dp_write_mask); ++ DMAOUTREG(MACH64_DP_MIX, (MACH64_BKGD_MIX_D | ++ MACH64_FRGD_MIX_S)); ++ DMAOUTREG(MACH64_DP_SRC, (MACH64_BKGD_SRC_FRGD_CLR | ++ MACH64_FRGD_SRC_FRGD_CLR | ++ MACH64_MONO_SRC_ONE)); ++ ++ } ++ ++ if (flags & MACH64_FRONT) { ++ ++ DMAOUTREG(MACH64_DST_OFF_PITCH, ++ dev_priv->front_offset_pitch); ++ DMAOUTREG(MACH64_DST_X_Y, (y << 16) | x); ++ DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w); ++ ++ } ++ ++ if (flags & MACH64_BACK) { ++ ++ DMAOUTREG(MACH64_DST_OFF_PITCH, ++ dev_priv->back_offset_pitch); ++ DMAOUTREG(MACH64_DST_X_Y, (y << 16) | x); ++ DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w); ++ ++ } ++ ++ if (flags & MACH64_DEPTH) { ++ /* Setup for depth buffer clear ++ */ ++ DMAOUTREG(MACH64_Z_CNTL, 0); ++ DMAOUTREG(MACH64_SCALE_3D_CNTL, 0); ++ ++ DMAOUTREG(MACH64_SC_LEFT_RIGHT, ctx->sc_left_right); ++ DMAOUTREG(MACH64_SC_TOP_BOTTOM, ctx->sc_top_bottom); ++ ++ DMAOUTREG(MACH64_CLR_CMP_CNTL, 0); ++ DMAOUTREG(MACH64_GUI_TRAJ_CNTL, ++ (MACH64_DST_X_LEFT_TO_RIGHT | ++ MACH64_DST_Y_TOP_TO_BOTTOM)); ++ ++ DMAOUTREG(MACH64_DP_PIX_WIDTH, ((depth_bpp << 0) | ++ (depth_bpp << 4) | ++ (depth_bpp << 8) | ++ (depth_bpp << 16) | ++ (depth_bpp << 28))); ++ ++ DMAOUTREG(MACH64_DP_FRGD_CLR, clear_depth); ++ DMAOUTREG(MACH64_DP_WRITE_MASK, 0xffffffff); ++ DMAOUTREG(MACH64_DP_MIX, (MACH64_BKGD_MIX_D | ++ MACH64_FRGD_MIX_S)); ++ DMAOUTREG(MACH64_DP_SRC, (MACH64_BKGD_SRC_FRGD_CLR | ++ MACH64_FRGD_SRC_FRGD_CLR | ++ MACH64_MONO_SRC_ONE)); ++ ++ DMAOUTREG(MACH64_DST_OFF_PITCH, ++ dev_priv->depth_offset_pitch); ++ DMAOUTREG(MACH64_DST_X_Y, (y << 16) | x); ++ DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w); ++ } ++ } ++ ++ DMAADVANCE(dev_priv, 1); ++ ++ return 0; ++} ++ ++static int mach64_dma_dispatch_swap(struct drm_device * dev, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ int nbox = sarea_priv->nbox; ++ struct drm_clip_rect *pbox = sarea_priv->boxes; ++ u32 fb_bpp; ++ int i; ++ DMALOCALS; ++ ++ DRM_DEBUG("\n"); ++ ++ switch (dev_priv->fb_bpp) { ++ case 16: ++ fb_bpp = MACH64_DATATYPE_RGB565; ++ break; ++ case 32: ++ default: ++ fb_bpp = MACH64_DATATYPE_ARGB8888; ++ break; ++ } ++ ++ if (!nbox) ++ return 0; ++ ++ DMAGETPTR(file_priv, dev_priv, 13 + nbox * 4); /* returns on failure to get buffer */ ++ ++ DMAOUTREG(MACH64_Z_CNTL, 0); ++ DMAOUTREG(MACH64_SCALE_3D_CNTL, 0); ++ ++ DMAOUTREG(MACH64_SC_LEFT_RIGHT, 0 | (8191 << 16)); /* no scissor */ ++ DMAOUTREG(MACH64_SC_TOP_BOTTOM, 0 | (16383 << 16)); ++ ++ DMAOUTREG(MACH64_CLR_CMP_CNTL, 0); ++ DMAOUTREG(MACH64_GUI_TRAJ_CNTL, (MACH64_DST_X_LEFT_TO_RIGHT | ++ MACH64_DST_Y_TOP_TO_BOTTOM)); ++ ++ DMAOUTREG(MACH64_DP_PIX_WIDTH, ((fb_bpp << 0) | ++ (fb_bpp << 4) | ++ (fb_bpp << 8) | ++ (fb_bpp << 16) | (fb_bpp << 28))); ++ ++ DMAOUTREG(MACH64_DP_WRITE_MASK, 0xffffffff); ++ DMAOUTREG(MACH64_DP_MIX, (MACH64_BKGD_MIX_D | MACH64_FRGD_MIX_S)); ++ DMAOUTREG(MACH64_DP_SRC, (MACH64_BKGD_SRC_BKGD_CLR | ++ MACH64_FRGD_SRC_BLIT | MACH64_MONO_SRC_ONE)); ++ ++ DMAOUTREG(MACH64_SRC_OFF_PITCH, dev_priv->back_offset_pitch); ++ DMAOUTREG(MACH64_DST_OFF_PITCH, dev_priv->front_offset_pitch); ++ ++ for (i = 0; i < nbox; i++) { ++ int x = pbox[i].x1; ++ int y = pbox[i].y1; ++ int w = pbox[i].x2 - x; ++ int h = pbox[i].y2 - y; ++ ++ DRM_DEBUG("dispatch swap %d,%d-%d,%d\n", ++ pbox[i].x1, pbox[i].y1, pbox[i].x2, pbox[i].y2); ++ ++ DMAOUTREG(MACH64_SRC_WIDTH1, w); ++ DMAOUTREG(MACH64_SRC_Y_X, (x << 16) | y); ++ DMAOUTREG(MACH64_DST_Y_X, (x << 16) | y); ++ DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (h << 16) | w); ++ ++ } ++ ++ DMAADVANCE(dev_priv, 1); ++ ++ if (dev_priv->driver_mode == MACH64_MODE_DMA_ASYNC) { ++ for (i = 0; i < MACH64_MAX_QUEUED_FRAMES - 1; i++) { ++ dev_priv->frame_ofs[i] = dev_priv->frame_ofs[i + 1]; ++ } ++ dev_priv->frame_ofs[i] = GETRINGOFFSET(); ++ ++ dev_priv->sarea_priv->frames_queued++; ++ } ++ ++ return 0; ++} ++ ++static int mach64_do_get_frames_queued(drm_mach64_private_t * dev_priv) ++{ ++ drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ int i, start; ++ u32 head, tail, ofs; ++ ++ DRM_DEBUG("\n"); ++ ++ if (sarea_priv->frames_queued == 0) ++ return 0; ++ ++ tail = ring->tail; ++ mach64_ring_tick(dev_priv, ring); ++ head = ring->head; ++ ++ start = (MACH64_MAX_QUEUED_FRAMES - ++ DRM_MIN(MACH64_MAX_QUEUED_FRAMES, sarea_priv->frames_queued)); ++ ++ if (head == tail) { ++ sarea_priv->frames_queued = 0; ++ for (i = start; i < MACH64_MAX_QUEUED_FRAMES; i++) { ++ dev_priv->frame_ofs[i] = ~0; ++ } ++ return 0; ++ } ++ ++ for (i = start; i < MACH64_MAX_QUEUED_FRAMES; i++) { ++ ofs = dev_priv->frame_ofs[i]; ++ DRM_DEBUG("frame_ofs[%d] ofs: %d\n", i, ofs); ++ if (ofs == ~0 || ++ (head < tail && (ofs < head || ofs >= tail)) || ++ (head > tail && (ofs < head && ofs >= tail))) { ++ sarea_priv->frames_queued = ++ (MACH64_MAX_QUEUED_FRAMES - 1) - i; ++ dev_priv->frame_ofs[i] = ~0; ++ } ++ } ++ ++ return sarea_priv->frames_queued; ++} ++ ++/* Copy and verify a client submited buffer. ++ * FIXME: Make an assembly optimized version ++ */ ++static __inline__ int copy_from_user_vertex(u32 *to, ++ const u32 __user *ufrom, ++ unsigned long bytes) ++{ ++ unsigned long n = bytes; /* dwords remaining in buffer */ ++ u32 *from, *orig_from; ++ ++ from = drm_alloc(bytes, DRM_MEM_DRIVER); ++ if (from == NULL) ++ return -ENOMEM; ++ ++ if (DRM_COPY_FROM_USER(from, ufrom, bytes)) { ++ drm_free(from, bytes, DRM_MEM_DRIVER); ++ return -EFAULT; ++ } ++ orig_from = from; /* we'll be modifying the "from" ptr, so save it */ ++ ++ n >>= 2; ++ ++ while (n > 1) { ++ u32 data, reg, count; ++ ++ data = *from++; ++ ++ n--; ++ ++ reg = le32_to_cpu(data); ++ count = (reg >> 16) + 1; ++ if (count <= n) { ++ n -= count; ++ reg &= 0xffff; ++ ++ /* This is an exact match of Mach64's Setup Engine registers, ++ * excluding SETUP_CNTL (1_C1). ++ */ ++ if ((reg >= 0x0190 && reg < 0x01c1) || ++ (reg >= 0x01ca && reg <= 0x01cf)) { ++ *to++ = data; ++ memcpy(to, from, count << 2); ++ from += count; ++ to += count; ++ } else { ++ DRM_ERROR("Got bad command: 0x%04x\n", reg); ++ drm_free(orig_from, bytes, DRM_MEM_DRIVER); ++ return -EACCES; ++ } ++ } else { ++ DRM_ERROR ++ ("Got bad command count(=%u) dwords remaining=%lu\n", ++ count, n); ++ drm_free(orig_from, bytes, DRM_MEM_DRIVER); ++ return -EINVAL; ++ } ++ } ++ ++ drm_free(orig_from, bytes, DRM_MEM_DRIVER); ++ if (n == 0) ++ return 0; ++ else { ++ DRM_ERROR("Bad buf->used(=%lu)\n", bytes); ++ return -EINVAL; ++ } ++} ++ ++static int mach64_dma_dispatch_vertex(struct drm_device * dev, ++ struct drm_file *file_priv, ++ drm_mach64_vertex_t * vertex) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ struct drm_buf *copy_buf; ++ void *buf = vertex->buf; ++ unsigned long used = vertex->used; ++ int ret = 0; ++ int i = 0; ++ int done = 0; ++ int verify_ret = 0; ++ DMALOCALS; ++ ++ DRM_DEBUG("buf=%p used=%lu nbox=%d\n", ++ buf, used, sarea_priv->nbox); ++ ++ if (!used) ++ goto _vertex_done; ++ ++ copy_buf = mach64_freelist_get(dev_priv); ++ if (copy_buf == NULL) { ++ DRM_ERROR("couldn't get buffer\n"); ++ return -EAGAIN; ++ } ++ ++ /* Mach64's vertex data is actually register writes. To avoid security ++ * compromises these register writes have to be verified and copied from ++ * user space into a private DMA buffer. ++ */ ++ verify_ret = copy_from_user_vertex(GETBUFPTR(copy_buf), buf, used); ++ ++ if (verify_ret != 0) { ++ mach64_freelist_put(dev_priv, copy_buf); ++ goto _vertex_done; ++ } ++ ++ copy_buf->used = used; ++ ++ DMASETPTR(copy_buf); ++ ++ if (sarea_priv->dirty & ~MACH64_UPLOAD_CLIPRECTS) { ++ ret = mach64_emit_state(file_priv, dev_priv); ++ if (ret < 0) ++ return ret; ++ } ++ ++ do { ++ /* Emit the next cliprect */ ++ if (i < sarea_priv->nbox) { ++ ret = mach64_emit_cliprect(file_priv, dev_priv, ++ &sarea_priv->boxes[i]); ++ if (ret < 0) { ++ /* failed to get buffer */ ++ return ret; ++ } else if (ret != 0) { ++ /* null intersection with scissor */ ++ continue; ++ } ++ } ++ if ((i >= sarea_priv->nbox - 1)) ++ done = 1; ++ ++ /* Add the buffer to the DMA queue */ ++ DMAADVANCE(dev_priv, done); ++ ++ } while (++i < sarea_priv->nbox); ++ ++ if (!done) { ++ if (copy_buf->pending) { ++ DMADISCARDBUF(); ++ } else { ++ /* This buffer wasn't used (no cliprects), so place it ++ * back on the free list ++ */ ++ mach64_freelist_put(dev_priv, copy_buf); ++ } ++ } ++ ++_vertex_done: ++ sarea_priv->dirty &= ~MACH64_UPLOAD_CLIPRECTS; ++ sarea_priv->nbox = 0; ++ ++ return verify_ret; ++} ++ ++static __inline__ int copy_from_user_blit(u32 *to, ++ const u32 __user *ufrom, ++ unsigned long bytes) ++{ ++ to = (u32 *)((char *)to + MACH64_HOSTDATA_BLIT_OFFSET); ++ ++ if (DRM_COPY_FROM_USER(to, ufrom, bytes)) { ++ return -EFAULT; ++ } ++ ++ return 0; ++} ++ ++static int mach64_dma_dispatch_blit(struct drm_device * dev, ++ struct drm_file *file_priv, ++ drm_mach64_blit_t * blit) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ int dword_shift, dwords; ++ unsigned long used; ++ struct drm_buf *copy_buf; ++ int verify_ret = 0; ++ DMALOCALS; ++ ++ /* The compiler won't optimize away a division by a variable, ++ * even if the only legal values are powers of two. Thus, we'll ++ * use a shift instead. ++ */ ++ switch (blit->format) { ++ case MACH64_DATATYPE_ARGB8888: ++ dword_shift = 0; ++ break; ++ case MACH64_DATATYPE_ARGB1555: ++ case MACH64_DATATYPE_RGB565: ++ case MACH64_DATATYPE_VYUY422: ++ case MACH64_DATATYPE_YVYU422: ++ case MACH64_DATATYPE_ARGB4444: ++ dword_shift = 1; ++ break; ++ case MACH64_DATATYPE_CI8: ++ case MACH64_DATATYPE_RGB8: ++ dword_shift = 2; ++ break; ++ default: ++ DRM_ERROR("invalid blit format %d\n", blit->format); ++ return -EINVAL; ++ } ++ ++ /* Set buf->used to the bytes of blit data based on the blit dimensions ++ * and verify the size. When the setup is emitted to the buffer with ++ * the DMA* macros below, buf->used is incremented to include the bytes ++ * used for setup as well as the blit data. ++ */ ++ dwords = (blit->width * blit->height) >> dword_shift; ++ used = dwords << 2; ++ if (used <= 0 || ++ used > MACH64_BUFFER_SIZE - MACH64_HOSTDATA_BLIT_OFFSET) { ++ DRM_ERROR("Invalid blit size: %lu bytes\n", used); ++ return -EINVAL; ++ } ++ ++ copy_buf = mach64_freelist_get(dev_priv); ++ if (copy_buf == NULL) { ++ DRM_ERROR("couldn't get buffer\n"); ++ return -EAGAIN; ++ } ++ ++ /* Copy the blit data from userspace. ++ * ++ * XXX: This is overkill. The most efficient solution would be having ++ * two sets of buffers (one set private for vertex data, the other set ++ * client-writable for blits). However that would bring more complexity ++ * and would break backward compatability. The solution currently ++ * implemented is keeping all buffers private, allowing to secure the ++ * driver, without increasing complexity at the expense of some speed ++ * transfering data. ++ */ ++ verify_ret = copy_from_user_blit(GETBUFPTR(copy_buf), blit->buf, used); ++ ++ if (verify_ret != 0) { ++ mach64_freelist_put(dev_priv, copy_buf); ++ goto _blit_done; ++ } ++ ++ copy_buf->used = used; ++ ++ /* FIXME: Use a last buffer flag and reduce the state emitted for subsequent, ++ * continuation buffers? ++ */ ++ ++ /* Blit via BM_HOSTDATA (gui-master) - like HOST_DATA[0-15], but doesn't require ++ * a register command every 16 dwords. State setup is added at the start of the ++ * buffer -- the client leaves space for this based on MACH64_HOSTDATA_BLIT_OFFSET ++ */ ++ DMASETPTR(copy_buf); ++ ++ DMAOUTREG(MACH64_Z_CNTL, 0); ++ DMAOUTREG(MACH64_SCALE_3D_CNTL, 0); ++ ++ DMAOUTREG(MACH64_SC_LEFT_RIGHT, 0 | (8191 << 16)); /* no scissor */ ++ DMAOUTREG(MACH64_SC_TOP_BOTTOM, 0 | (16383 << 16)); ++ ++ DMAOUTREG(MACH64_CLR_CMP_CNTL, 0); /* disable */ ++ DMAOUTREG(MACH64_GUI_TRAJ_CNTL, ++ MACH64_DST_X_LEFT_TO_RIGHT | MACH64_DST_Y_TOP_TO_BOTTOM); ++ ++ DMAOUTREG(MACH64_DP_PIX_WIDTH, (blit->format << 0) /* dst pix width */ ++ |(blit->format << 4) /* composite pix width */ ++ |(blit->format << 8) /* src pix width */ ++ |(blit->format << 16) /* host data pix width */ ++ |(blit->format << 28) /* scaler/3D pix width */ ++ ); ++ ++ DMAOUTREG(MACH64_DP_WRITE_MASK, 0xffffffff); /* enable all planes */ ++ DMAOUTREG(MACH64_DP_MIX, MACH64_BKGD_MIX_D | MACH64_FRGD_MIX_S); ++ DMAOUTREG(MACH64_DP_SRC, ++ MACH64_BKGD_SRC_BKGD_CLR ++ | MACH64_FRGD_SRC_HOST | MACH64_MONO_SRC_ONE); ++ ++ DMAOUTREG(MACH64_DST_OFF_PITCH, ++ (blit->pitch << 22) | (blit->offset >> 3)); ++ DMAOUTREG(MACH64_DST_X_Y, (blit->y << 16) | blit->x); ++ DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (blit->height << 16) | blit->width); ++ ++ DRM_DEBUG("%lu bytes\n", used); ++ ++ /* Add the buffer to the queue */ ++ DMAADVANCEHOSTDATA(dev_priv); ++ ++_blit_done: ++ return verify_ret; ++} ++ ++/* ================================================================ ++ * IOCTL functions ++ */ ++ ++int mach64_dma_clear(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ drm_mach64_clear_t *clear = data; ++ int ret; ++ ++ DRM_DEBUG("pid=%d\n", DRM_CURRENTPID); ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ if (sarea_priv->nbox > MACH64_NR_SAREA_CLIPRECTS) ++ sarea_priv->nbox = MACH64_NR_SAREA_CLIPRECTS; ++ ++ ret = mach64_dma_dispatch_clear(dev, file_priv, clear->flags, ++ clear->x, clear->y, clear->w, clear->h, ++ clear->clear_color, ++ clear->clear_depth); ++ ++ /* Make sure we restore the 3D state next time. ++ */ ++ sarea_priv->dirty |= (MACH64_UPLOAD_CONTEXT | MACH64_UPLOAD_MISC); ++ return ret; ++} ++ ++int mach64_dma_swap(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ int ret; ++ ++ DRM_DEBUG("pid=%d\n", DRM_CURRENTPID); ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ if (sarea_priv->nbox > MACH64_NR_SAREA_CLIPRECTS) ++ sarea_priv->nbox = MACH64_NR_SAREA_CLIPRECTS; ++ ++ ret = mach64_dma_dispatch_swap(dev, file_priv); ++ ++ /* Make sure we restore the 3D state next time. ++ */ ++ sarea_priv->dirty |= (MACH64_UPLOAD_CONTEXT | MACH64_UPLOAD_MISC); ++ return ret; ++} ++ ++int mach64_dma_vertex(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ drm_mach64_vertex_t *vertex = data; ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ if (!dev_priv) { ++ DRM_ERROR("called with no initialization\n"); ++ return -EINVAL; ++ } ++ ++ DRM_DEBUG("pid=%d buf=%p used=%lu discard=%d\n", ++ DRM_CURRENTPID, ++ vertex->buf, vertex->used, vertex->discard); ++ ++ if (vertex->prim < 0 || vertex->prim > MACH64_PRIM_POLYGON) { ++ DRM_ERROR("buffer prim %d\n", vertex->prim); ++ return -EINVAL; ++ } ++ ++ if (vertex->used > MACH64_BUFFER_SIZE || (vertex->used & 3) != 0) { ++ DRM_ERROR("Invalid vertex buffer size: %lu bytes\n", ++ vertex->used); ++ return -EINVAL; ++ } ++ ++ if (sarea_priv->nbox > MACH64_NR_SAREA_CLIPRECTS) ++ sarea_priv->nbox = MACH64_NR_SAREA_CLIPRECTS; ++ ++ return mach64_dma_dispatch_vertex(dev, file_priv, vertex); ++} ++ ++int mach64_dma_blit(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; ++ drm_mach64_blit_t *blit = data; ++ int ret; ++ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ ++ ret = mach64_dma_dispatch_blit(dev, file_priv, blit); ++ ++ /* Make sure we restore the 3D state next time. ++ */ ++ sarea_priv->dirty |= (MACH64_UPLOAD_CONTEXT | ++ MACH64_UPLOAD_MISC | MACH64_UPLOAD_CLIPRECTS); ++ ++ return ret; ++} ++ ++int mach64_get_param(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ drm_mach64_private_t *dev_priv = dev->dev_private; ++ drm_mach64_getparam_t *param = data; ++ int value; ++ ++ DRM_DEBUG("\n"); ++ ++ if (!dev_priv) { ++ DRM_ERROR("called with no initialization\n"); ++ return -EINVAL; ++ } ++ ++ switch (param->param) { ++ case MACH64_PARAM_FRAMES_QUEUED: ++ /* Needs lock since it calls mach64_ring_tick() */ ++ LOCK_TEST_WITH_RETURN(dev, file_priv); ++ value = mach64_do_get_frames_queued(dev_priv); ++ break; ++ case MACH64_PARAM_IRQ_NR: ++ value = dev->irq; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) { ++ DRM_ERROR("copy_to_user\n"); ++ return -EFAULT; ++ } ++ ++ return 0; ++} +--- linux/drivers/gpu/drm/Kconfig.gpu-drm-mach64.orig 2014-03-08 18:52:34.979768492 +0200 ++++ linux/drivers/gpu/drm/Kconfig 2014-03-08 18:55:18.769681473 +0200 +@@ -130,6 +130,17 @@ config DRM_I810 + + source "drivers/gpu/drm/i915/Kconfig" + ++config DRM_MACH64 ++ tristate "ATI Rage Pro (Mach64)" ++ depends on DRM && PCI ++ help ++ Choose this option if you have an ATI Rage Pro (mach64 chipset) ++ graphics card. Example cards include: 3D Rage Pro, Xpert 98, ++ 3D Rage LT Pro, 3D Rage XL/XC, and 3D Rage Mobility (P/M, M1). ++ Cards earlier than ATI Rage Pro (e.g. Rage II) are not supported. ++ If M is selected, the module will be called mach64. AGP support for ++ this card is strongly suggested (unless you have a PCI version). ++ + config DRM_MGA + tristate "Matrox g200/g400" + depends on DRM && PCI +--- linux/drivers/gpu/drm/Makefile.gpu-drm-mach64.orig ++++ linux/drivers/gpu/drm/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_DRM_TDFX) += tdfx/ + obj-$(CONFIG_DRM_R128) += r128/ + obj-$(CONFIG_HSA_AMD) += amd/amdkfd/ + obj-$(CONFIG_DRM_RADEON)+= radeon/ ++obj-$(CONFIG_DRM_MACH64)+= mach64/ + obj-$(CONFIG_DRM_MGA) += mga/ + obj-$(CONFIG_DRM_I810) += i810/ + obj-$(CONFIG_DRM_I915) += i915/ diff --git a/kernel/kernel/files/patches/mageia.old/hid-usbhid-IBM-BladeCenterHS20-quirk.patch b/kernel/kernel/files/patches/mageia.old/hid-usbhid-IBM-BladeCenterHS20-quirk.patch new file mode 100644 index 00000000..d8e7fa4d --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/hid-usbhid-IBM-BladeCenterHS20-quirk.patch @@ -0,0 +1,37 @@ +Subject: Add blacklist of usb hid modules + +IBM Blade Center HS20 can connect PS2 kerboard and mouse, It is USB emulation. +This devices need HID_QUIRK_IGNORE option. + +This patch is workaround. + +[ rediffed for 2.6.28 -- herton ] +Signed-off-by: Go Taniguchi + +--- + drivers/hid/hid-core.c | 2 ++ + drivers/hid/hid-ids.h | 2 ++ + 2 files changed, 4 insertions(+) + +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -2062,6 +2062,8 @@ static const struct hid_device_id hid_ig + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_IBM, 0x4001) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_IBM, 0x4002) }, + { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -46,6 +46,8 @@ + #define USB_VENDOR_ID_AFATECH 0x15a4 + #define USB_DEVICE_ID_AFATECH_AF9016 0x9016 + ++#define USB_VENDOR_ID_IBM 0x04b3 ++ + #define USB_VENDOR_ID_AIPTEK 0x08ca + #define USB_DEVICE_ID_AIPTEK_01 0x0001 + #define USB_DEVICE_ID_AIPTEK_10 0x0010 diff --git a/kernel/kernel/files/patches/mageia.old/ide-pci-sis5513-965.patch b/kernel/kernel/files/patches/mageia.old/ide-pci-sis5513-965.patch new file mode 100644 index 00000000..9c54ab70 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/ide-pci-sis5513-965.patch @@ -0,0 +1,23 @@ +--- + drivers/ide/sis5513.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff -p -up linux-2.6.28/drivers/ide/sis5513.c.orig linux-2.6.28/drivers/ide/sis5513.c +--- linux-2.6.28/drivers/ide/sis5513.c.orig 2008-12-08 12:41:13.000000000 -0500 ++++ linux-2.6.28/drivers/ide/sis5513.c 2008-12-08 12:51:32.000000000 -0500 +@@ -412,6 +412,15 @@ static int __devinit sis_find_family(str + pci_name(dev)); + } + } ++ else if (trueid == 0x180) { /* sis965L */ ++ u16 pci_command; ++ pci_read_config_word(dev, PCI_COMMAND, &pci_command); ++ pci_command &= ~PCI_COMMAND_INTX_DISABLE; ++ pci_write_config_word(dev, PCI_COMMAND, pci_command); ++ chipset_family = ATA_133; ++ printk(KERN_INFO DRV_NAME " %s: SiS 965 IDE UDMA133 controller\n", ++ pci_name(dev)); ++ } + } + + if (!chipset_family) { /* Belongs to pci-quirks */ diff --git a/kernel/kernel/files/patches/mageia.old/include-kbuild-export-pci_ids.patch b/kernel/kernel/files/patches/mageia.old/include-kbuild-export-pci_ids.patch new file mode 100644 index 00000000..ebcf9832 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/include-kbuild-export-pci_ids.patch @@ -0,0 +1,19 @@ +From Thierry Vignaud (Mandriva) + +We now lacks /usr/include/linux/pci_ids.h which break ldetect build... +Can you readd it please? +Thanks +--- + include/linux/Kbuild | 1 + + 1 file changed, 1 insertion(+) + +--- linux/include/uapi/linux/Kbuild.include-kbuild-export-pci_ids.orig ++++ linux/include/uapi/linux/Kbuild +@@ -277,6 +277,7 @@ header-y += param.h + header-y += parport.h + header-y += patchkey.h + header-y += pci.h ++header-y += pci_ids.h + header-y += pci_regs.h + header-y += perf_event.h + header-y += personality.h diff --git a/kernel/kernel/files/patches/mageia.old/input-i8042-quirks-for-Fujitsu-Lifebook-A544-and-Lif.patch b/kernel/kernel/files/patches/mageia.old/input-i8042-quirks-for-Fujitsu-Lifebook-A544-and-Lif.patch new file mode 100644 index 00000000..2ed76671 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/input-i8042-quirks-for-Fujitsu-Lifebook-A544-and-Lif.patch @@ -0,0 +1,47 @@ +From 993b3a3f80a7842a48cd46c2b41e1b3ef6302468 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 24 Oct 2014 14:55:24 -0700 +Subject: [PATCH] Input: i8042 - quirks for Fujitsu Lifebook A544 and Lifebook + AH544 + +These models need i8042.notimeout, otherwise the touchpad will not work. + +BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=69731 +BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1111138 +Cc: stable@vger.kernel.org +Signed-off-by: Hans de Goede +Signed-off-by: Dmitry Torokhov +--- + drivers/input/serio/i8042-x86ia64io.h | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h +index a0bcbb6..aa9b299 100644 +--- a/drivers/input/serio/i8042-x86ia64io.h ++++ b/drivers/input/serio/i8042-x86ia64io.h +@@ -364,6 +364,22 @@ static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { + }, + }, + { ++ /* Fujitsu A544 laptop */ ++ /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), ++ }, ++ }, ++ { ++ /* Fujitsu AH544 laptop */ ++ /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), ++ }, ++ }, ++ { + /* Fujitsu U574 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + .matches = { +-- +2.1.2 + diff --git a/kernel/kernel/files/patches/mageia.old/media-usb-pwc-lie-in-proc-usb-devices.patch b/kernel/kernel/files/patches/mageia.old/media-usb-pwc-lie-in-proc-usb-devices.patch new file mode 100644 index 00000000..32d496e3 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/media-usb-pwc-lie-in-proc-usb-devices.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c +index f976df4..32c883b 100644 +--- a/drivers/media/usb/pwc/pwc-if.c ++++ b/drivers/media/usb/pwc/pwc-if.c +@@ -119,7 +119,7 @@ static void usb_pwc_disconnect(struct us + static void pwc_isoc_cleanup(struct pwc_device *pdev); + + static struct usb_driver pwc_driver = { +- .name = "Philips webcam", /* name */ ++ .name = "pwc", /* name */ + .id_table = pwc_device_table, + .probe = usb_pwc_probe, /* probe() */ + .disconnect = usb_pwc_disconnect, /* disconnect() */ diff --git a/kernel/kernel/files/patches/mageia.old/mpt-vmware-fix.patch b/kernel/kernel/files/patches/mageia.old/mpt-vmware-fix.patch new file mode 100644 index 00000000..0b90277c --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/mpt-vmware-fix.patch @@ -0,0 +1,49 @@ +Subject: mpt scsi modules for VMWare's emulated + +The attached patch is a workaround for a bug in VMWare's emulated LSI +Fusion SCSI HBA. The emulated firmware returns zero for the maximum +number of attached devices; the real firmware returns a positive +number. Therefore, the kernel that boots and works fine on bare metal +will fail on VMWare because this firmware value is handed to the SCSI +midlayer, which then skips the entire bus scan. + +F7 bz 241935 + +The patch below was submitted by Eric Moore of LSI to the linux-scsi +mailing list: + +http://marc.info/?l=linux-scsi&m=117432237404247 + +then immediately rejected by Christoph Hellwig, who prefers that +VMWare fix their emulation instead. + +This patch is workaround. + +--- + drivers/message/fusion/mptbase.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +--- a/drivers/message/fusion/mptbase.c ++++ b/drivers/message/fusion/mptbase.c +@@ -3051,8 +3051,19 @@ GetPortFacts(MPT_ADAPTER *ioc, int portn + pfacts->MaxPersistentIDs = le16_to_cpu(pfacts->MaxPersistentIDs); + pfacts->MaxLanBuckets = le16_to_cpu(pfacts->MaxLanBuckets); + +- max_id = (ioc->bus_type == SAS) ? pfacts->PortSCSIID : +- pfacts->MaxDevices; ++ switch (ioc->bus_type) { ++ case SAS: ++ max_id = pfacts->PortSCSIID; ++ break; ++ case FC: ++ max_id = pfacts->MaxDevices; ++ break; ++ case SPI: ++ default: ++ max_id = MPT_MAX_SCSI_DEVICES; ++ break; ++ } ++ + ioc->devices_per_bus = (max_id > 255) ? 256 : max_id; + ioc->number_of_buses = (ioc->devices_per_bus < 256) ? 1 : max_id/256; + diff --git a/kernel/kernel/files/patches/mageia.old/net-ipv4-netfilter-ipt_IFWLOG-3.6-buildfix.patch b/kernel/kernel/files/patches/mageia.old/net-ipv4-netfilter-ipt_IFWLOG-3.6-buildfix.patch new file mode 100644 index 00000000..51041b6f --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-ipv4-netfilter-ipt_IFWLOG-3.6-buildfix.patch @@ -0,0 +1,33 @@ + +Adapt to netlink changes in 3.6 + +Signed-off-by: Thomas Backlund + +--- linux/net/ipv4/netfilter/ipt_IFWLOG.c.orig 2012-10-17 22:44:57.000000000 +0300 ++++ linux/net/ipv4/netfilter/ipt_IFWLOG.c 2012-10-17 23:17:10.012725918 +0300 +@@ -56,9 +56,9 @@ static void send_packet(const struct nl_ + return; + } + +- nlh = NLMSG_PUT(skb, 0, 0, 0, size - sizeof(*nlh)); ++ nlh = nlmsg_put(skb, 0, 0, 0, size - sizeof(*nlh), 0); + +- memcpy(NLMSG_DATA(nlh), (const void *) msg, sizeof(*msg)); ++ memcpy(nlmsg_data(nlh), (const void *) msg, sizeof(*msg)); + + NETLINK_CB(skb).pid = 0; /* from kernel */ + NETLINK_CB(skb).dst_group = IFWLOGNLGRP_DEF; +@@ -169,9 +169,11 @@ static struct xt_target ipt_IFWLOG = { + static int __init ipt_ifwlog_init(void) + { + int err; ++ struct netlink_kernel_cfg cfg = { ++ .groups = IFWLOGNLGRP_MAX, ++ }; + +- nl = netlink_kernel_create(&init_net, NETLINK_IFWLOG, IFWLOGNLGRP_MAX, +- NULL, NULL, THIS_MODULE); ++ nl = netlink_kernel_create(&init_net, NETLINK_IFWLOG, THIS_MODULE, &cfg); + if (!nl) { + PRINTR(KERN_WARNING "IFWLOG: cannot create netlink socket\n"); + return -ENOMEM; diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.35-buildfix.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.35-buildfix.patch new file mode 100644 index 00000000..99d4d063 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.35-buildfix.patch @@ -0,0 +1,32 @@ +--- linux-2.6.35-rc6-git-mnb0.1/net/ipv4/netfilter/ipt_IFWLOG.c.orig 2010-07-30 21:17:30.000000000 +0300 ++++ linux-2.6.35-rc6-git-mnb0.1/net/ipv4/netfilter/ipt_IFWLOG.c 2010-07-31 13:46:33.834611944 +0300 +@@ -135,7 +135,7 @@ static void ipt_IFWLOG_packet(const stru + } + + static unsigned int ipt_IFWLOG_target(struct sk_buff *skb, +- const struct xt_target_param *target_param) ++ const struct xt_action_param *target_param) + { + const struct ipt_IFWLOG_info *info = target_param->targinfo; + +@@ -144,17 +144,17 @@ static unsigned int ipt_IFWLOG_target(st + return IPT_CONTINUE; + } + +-static bool ipt_IFWLOG_checkentry(const struct xt_tgchk_param *tgchk_param) ++static int ipt_IFWLOG_checkentry(const struct xt_tgchk_param *tgchk_param) + { + const struct ipt_IFWLOG_info *info = tgchk_param->targinfo; + + if (info->prefix[sizeof(info->prefix)-1] != '\0') { + DEBUGP("IFWLOG: prefix term %i\n", + info->prefix[sizeof(info->prefix)-1]); +- return false; ++ return -EINVAL; + } + +- return true; ++ return 0; + } + + static struct xt_target ipt_IFWLOG = { diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.37-buildfix.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.37-buildfix.patch new file mode 100644 index 00000000..0ae95aa3 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.37-buildfix.patch @@ -0,0 +1,15 @@ + + net/ipv4/netfilter/ipt_IFWLOG.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- linux-2.6.37-rc3-git1-tmb0.3/net/ipv4/netfilter/ipt_IFWLOG.c.orig 2010-11-24 21:58:36.000000000 +0200 ++++ linux-2.6.37-rc3-git1-tmb0.3/net/ipv4/netfilter/ipt_IFWLOG.c 2010-11-25 13:08:55.719379646 +0200 +@@ -141,7 +141,7 @@ static unsigned int ipt_IFWLOG_target(st + + ipt_IFWLOG_packet(skb, target_param->in, target_param->out, info); + +- return IPT_CONTINUE; ++ return XT_CONTINUE; + } + + static int ipt_IFWLOG_checkentry(const struct xt_tgchk_param *tgchk_param) diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-3.7-buildfix.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-3.7-buildfix.patch new file mode 100644 index 00000000..744e840f --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-3.7-buildfix.patch @@ -0,0 +1,20 @@ +--- linux-3.7-rc8/net/ipv4/netfilter/ipt_IFWLOG.c.orig 2012-12-06 19:50:10.000000000 +0200 ++++ linux-3.7-rc8/net/ipv4/netfilter/ipt_IFWLOG.c 2012-12-06 20:55:23.669152916 +0200 +@@ -60,7 +60,7 @@ static void send_packet(const struct nl_ + + memcpy(nlmsg_data(nlh), (const void *) msg, sizeof(*msg)); + +- NETLINK_CB(skb).pid = 0; /* from kernel */ ++ NETLINK_CB(skb).portid = 0; /* from kernel */ + NETLINK_CB(skb).dst_group = IFWLOGNLGRP_DEF; + + if (nl) { +@@ -173,7 +173,7 @@ static int __init ipt_ifwlog_init(void) + .groups = IFWLOGNLGRP_MAX, + }; + +- nl = netlink_kernel_create(&init_net, NETLINK_IFWLOG, THIS_MODULE, &cfg); ++ nl = netlink_kernel_create(&init_net, NETLINK_IFWLOG, &cfg); + if (!nl) { + PRINTR(KERN_WARNING "IFWLOG: cannot create netlink socket\n"); + return -ENOMEM; diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-mdv.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-mdv.patch new file mode 100644 index 00000000..5ecc0151 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-mdv.patch @@ -0,0 +1,264 @@ +ipt_IFWLOG: Mandriva changes + +This patch holds all the Mandriva changes done in ipt_IFWLOG +netfilter module. + +This work is mostly done by Thomas Backlund, Herton R. Krzesinski +and Luiz Fernando N. Capitulino. + +Signed-off-by: Luiz Fernando N. Capitulino +Signed-off-by: Herton Ronaldo Krzesinski + +--- + include/linux/netfilter_ipv4/Kbuild | 1 + include/linux/netfilter_ipv4/ipt_IFWLOG.h | 23 +++++- + net/ipv4/netfilter/ipt_IFWLOG.c | 108 +++++++++++++++--------------- + 3 files changed, 77 insertions(+), 55 deletions(-) + +diff -p -up linux-2.6.28/include/linux/netfilter_ipv4/ipt_IFWLOG.h.orig linux-2.6.28/include/linux/netfilter_ipv4/ipt_IFWLOG.h +--- linux-2.6.28/include/linux/netfilter_ipv4/ipt_IFWLOG.h.orig 2008-12-12 10:55:07.000000000 -0500 ++++ linux-2.6.28/include/linux/netfilter_ipv4/ipt_IFWLOG.h 2008-12-12 10:56:30.000000000 -0500 +@@ -1,10 +1,25 @@ +-#ifndef _IPT_IFWLOG_H +-#define _IPT_IFWLOG_H ++#ifndef _LINUX_IPT_IFWLOG_H ++#define _LINUX_IPT_IFWLOG_H + + #ifndef NETLINK_IFWLOG +-#define NETLINK_IFWLOG 19 ++#define NETLINK_IFWLOG 20 + #endif + ++#ifndef __KERNEL__ ++/* Multicast groups - backwards compatiblility for userspace */ ++#define IFWLOG_NLGRP_NONE 0x00000000 ++#define IFWLOG_NLGRP_DEF 0x00000001 /* default message group */ ++#endif ++ ++enum { ++ IFWLOGNLGRP_NONE, ++#define IFWLOGNLGRP_NONE IFWLOGNLGRP_NONE ++ IFWLOGNLGRP_DEF, ++#define IFWLOGNLGRP_DEF IFWLOGNLGRP_DEF ++ __IFWLOGNLGRP_MAX ++}; ++#define IFWLOGNLGRP_MAX (__IFWLOGNLGRP_MAX - 1) ++ + #define PREFSIZ 32 + + struct nl_msg { /* Netlink message */ +@@ -23,4 +38,4 @@ struct ipt_IFWLOG_info { + char prefix[PREFSIZ]; + }; + +-#endif /* _IPT_IFWLOG_H */ ++#endif /* _LINUX_IPT_IFWLOG_H */ +diff -p -up linux-2.6.28/net/ipv4/netfilter/ipt_IFWLOG.c.orig linux-2.6.28/net/ipv4/netfilter/ipt_IFWLOG.c +--- linux-2.6.28/net/ipv4/netfilter/ipt_IFWLOG.c.orig 2008-12-12 10:55:07.000000000 -0500 ++++ linux-2.6.28/net/ipv4/netfilter/ipt_IFWLOG.c 2008-12-12 10:57:16.000000000 -0500 +@@ -4,6 +4,14 @@ + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. ++ * ++ * 2007-10-10 Thomas Backlund : build fixes for 2.6.22.9 ++ * 2007-11-11 Herton Krzesinski : build fixes for 2.6.24-rc ++ * 2007-12-03 Luiz Capitulino : v1.1 ++ * - Better multicast group usage ++ * - Coding style fixes ++ * - Do not return -EINVAL by default in ipt_ifwlog_init() ++ * - Minor refinements + */ + + #include +@@ -19,12 +27,10 @@ + #include + + #include ++#include + #include + #include + +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Samir Bellabes "); +-MODULE_DESCRIPTION("Interactive firewall logging and module"); + + #if 0 + #define DEBUGP PRINTR +@@ -36,44 +42,41 @@ MODULE_DESCRIPTION("Interactive firewall + + static struct sock *nl; + +-#define GROUP 10 +- + /* send struct to userspace */ +-static void send_packet(struct nl_msg msg) ++static void send_packet(const struct nl_msg *msg) + { + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; ++ unsigned int size; + +- skb = alloc_skb(NLMSG_SPACE(sizeof(struct nl_msg)), GFP_ATOMIC); ++ size = NLMSG_SPACE(sizeof(*msg)); ++ skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) { + PRINTR(KERN_WARNING "IFWLOG: OOM can't allocate skb\n"); +- return ; ++ return; + } + +- nlh = NLMSG_PUT(skb, 0, 0, 0, sizeof(struct nl_msg) - sizeof(*nlh)); ++ nlh = NLMSG_PUT(skb, 0, 0, 0, size - sizeof(*nlh)); + +- memcpy(NLMSG_DATA(nlh), (const void*)&msg, sizeof(struct nl_msg)); ++ memcpy(NLMSG_DATA(nlh), (const void *) msg, sizeof(*msg)); + + NETLINK_CB(skb).pid = 0; /* from kernel */ +- NETLINK_CB(skb).dst_pid = 0; /* multicast */ +- NETLINK_CB(skb).dst_group = 10; ++ NETLINK_CB(skb).dst_group = IFWLOGNLGRP_DEF; + + if (nl) { + DEBUGP(KERN_WARNING + "IFWLOG: nlmsg_len=%ld\nnlmsg_type=%d nlmsg_flags=%d\nnlmsg_seq=%ld nlmsg_pid = %ld\n", + (long)nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags, + (long)nlh->nlmsg_seq, (long)nlh->nlmsg_pid); +- DEBUGP(KERN_WARNING "prefix : %s\n", msg.prefix); ++ DEBUGP(KERN_WARNING "prefix : %s\n", msg->prefix); + +- netlink_broadcast(nl, skb, 0, 10, GFP_ATOMIC); +- return ; ++ netlink_broadcast(nl, skb, 0, IFWLOGNLGRP_DEF, GFP_ATOMIC); ++ return; + } + +- nlmsg_failure: +- if (skb) +- kfree_skb(skb); +- PRINTR(KERN_WARNING "IFWLOG: Error sending netlink packet\n"); +- return ; ++nlmsg_failure: ++ kfree_skb(skb); ++ PRINTR(KERN_WARNING "IFWLOG: Error sending netlink packet\n"); + } + + /* fill struct for userspace */ +@@ -128,73 +131,76 @@ static void ipt_IFWLOG_packet(const stru + do_gettimeofday((struct timeval *)&tv); + msg.timestamp_sec = tv.tv_sec; + +- send_packet(msg); ++ send_packet(&msg); + } + +-static unsigned int ipt_IFWLOG_target(struct sk_buff **pskb, +- const struct net_device *in, +- const struct net_device *out, +- unsigned int hooknum, +- const void *targinfo, +- void *userinfo) ++static unsigned int ipt_IFWLOG_target(struct sk_buff *skb, ++ const struct xt_target_param *target_param) + { +- const struct ipt_IFWLOG_info *info = targinfo; ++ const struct ipt_IFWLOG_info *info = target_param->targinfo; + +- ipt_IFWLOG_packet(*pskb, in, out, info); ++ ipt_IFWLOG_packet(skb, target_param->in, target_param->out, info); + + return IPT_CONTINUE; + } + +-static int ipt_IFWLOG_checkentry(const char *tablename, +- const struct ipt_entry *e, +- void *targinfo, +- unsigned int targinfosize, +- unsigned int hook_mask) ++static bool ipt_IFWLOG_checkentry(const struct xt_tgchk_param *tgchk_param) + { +- const struct ipt_IFWLOG_info *info = targinfo; ++ const struct ipt_IFWLOG_info *info = tgchk_param->targinfo; + + if (info->prefix[sizeof(info->prefix)-1] != '\0') { + DEBUGP("IFWLOG: prefix term %i\n", + info->prefix[sizeof(info->prefix)-1]); +- return 0; ++ return false; + } + +- return 1; ++ return true; + } + +-static struct ipt_target ipt_IFWLOG = { ++static struct xt_target ipt_IFWLOG = { + .name = "IFWLOG", ++ .family = AF_INET, + .target = ipt_IFWLOG_target, + .targetsize = sizeof(struct ipt_IFWLOG_info), + .checkentry = ipt_IFWLOG_checkentry, + .me = THIS_MODULE, + }; + +-static int __init init(void) ++static int __init ipt_ifwlog_init(void) + { +- nl = (struct sock*) netlink_kernel_create(NETLINK_IFWLOG, GROUP, NULL, THIS_MODULE); +- if (!nl) { +- PRINTR(KERN_WARNING "IFWLOG: cannot create netlink socket\n"); +- return -EINVAL; +- } ++ int err; + +- if (ipt_register_target(&ipt_IFWLOG)) { ++ nl = netlink_kernel_create(&init_net, NETLINK_IFWLOG, IFWLOGNLGRP_MAX, ++ NULL, NULL, THIS_MODULE); ++ if (!nl) { ++ PRINTR(KERN_WARNING "IFWLOG: cannot create netlink socket\n"); ++ return -ENOMEM; ++ } ++ ++ err = xt_register_target(&ipt_IFWLOG); ++ if (err) { + if (nl && nl->sk_socket) + sock_release(nl->sk_socket); +- return -EINVAL; ++ return err; + } + + PRINTR(KERN_INFO "IFWLOG: register target\n"); + return 0; + } + +-static void __exit fini(void) ++static void __exit ipt_ifwlog_fini(void) + { + if (nl && nl->sk_socket) +- sock_release(nl->sk_socket); ++ sock_release(nl->sk_socket); + PRINTR(KERN_INFO "IFWLOG: unregister target\n"); +- ipt_unregister_target(&ipt_IFWLOG); ++ xt_unregister_target(&ipt_IFWLOG); + } + +-module_init(init); +-module_exit(fini); ++module_init(ipt_ifwlog_init); ++module_exit(ipt_ifwlog_fini); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Samir Bellabes "); ++MODULE_AUTHOR("Luiz Capitulino "); ++MODULE_DESCRIPTION("Interactive firewall logging and module"); ++MODULE_VERSION("v1.1"); +--- linux/include/uapi/linux/netfilter_ipv4/Kbuild.net-netfilter-IFWLOG-mdv.orig ++++ linux/include/uapi/linux/netfilter_ipv4/Kbuild +@@ -2,6 +2,7 @@ header-y += ip_queue.h + header-y += ip_tables.h + header-y += ipt_CLUSTERIP.h + header-y += ipt_ECN.h ++header-y += ipt_IFWLOG.h + header-y += ipt_LOG.h + header-y += ipt_REJECT.h + header-y += ipt_TTL.h diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG.patch new file mode 100644 index 00000000..c8654422 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG.patch @@ -0,0 +1,269 @@ +--- + include/linux/netfilter_ipv4/ipt_IFWLOG.h | 26 +++ + net/ipv4/netfilter/Kconfig | 11 + + net/ipv4/netfilter/Makefile | 1 + net/ipv4/netfilter/ipt_IFWLOG.c | 200 ++++++++++++++++++++++++++++++ + 4 files changed, 238 insertions(+) + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_IFWLOG.c +@@ -0,0 +1,200 @@ ++/* Interactive Firewall for Mandriva ++ * Samir Bellabes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Samir Bellabes "); ++MODULE_DESCRIPTION("Interactive firewall logging and module"); ++ ++#if 0 ++#define DEBUGP PRINTR ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++#define PRINTR(format, args...) do { if(net_ratelimit()) printk(format, ##args); } while(0) ++ ++static struct sock *nl; ++ ++#define GROUP 10 ++ ++/* send struct to userspace */ ++static void send_packet(struct nl_msg msg) ++{ ++ struct sk_buff *skb = NULL; ++ struct nlmsghdr *nlh; ++ ++ skb = alloc_skb(NLMSG_SPACE(sizeof(struct nl_msg)), GFP_ATOMIC); ++ if (!skb) { ++ PRINTR(KERN_WARNING "IFWLOG: OOM can't allocate skb\n"); ++ return ; ++ } ++ ++ nlh = NLMSG_PUT(skb, 0, 0, 0, sizeof(struct nl_msg) - sizeof(*nlh)); ++ ++ memcpy(NLMSG_DATA(nlh), (const void*)&msg, sizeof(struct nl_msg)); ++ ++ NETLINK_CB(skb).pid = 0; /* from kernel */ ++ NETLINK_CB(skb).dst_pid = 0; /* multicast */ ++ NETLINK_CB(skb).dst_group = 10; ++ ++ if (nl) { ++ DEBUGP(KERN_WARNING ++ "IFWLOG: nlmsg_len=%ld\nnlmsg_type=%d nlmsg_flags=%d\nnlmsg_seq=%ld nlmsg_pid = %ld\n", ++ (long)nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags, ++ (long)nlh->nlmsg_seq, (long)nlh->nlmsg_pid); ++ DEBUGP(KERN_WARNING "prefix : %s\n", msg.prefix); ++ ++ netlink_broadcast(nl, skb, 0, 10, GFP_ATOMIC); ++ return ; ++ } ++ ++ nlmsg_failure: ++ if (skb) ++ kfree_skb(skb); ++ PRINTR(KERN_WARNING "IFWLOG: Error sending netlink packet\n"); ++ return ; ++} ++ ++/* fill struct for userspace */ ++static void ipt_IFWLOG_packet(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct ipt_IFWLOG_info *info) ++{ ++ struct iphdr iph; ++ struct tcphdr tcph; ++ struct udphdr udph; ++ struct nl_msg msg; ++ struct iphdr _iph, *ih; ++ struct timeval tv; ++ ++ memset(&msg, 0, sizeof(struct nl_msg)); ++ ++ ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); ++ if (ih == NULL) { ++ PRINTR(KERN_WARNING "IFWLOG: skb truncated"); ++ return; ++ } ++ ++ /* save interface name */ ++ if (in) ++ strcpy(msg.indev_name, in->name); ++ if (out) ++ strcpy(msg.outdev_name, out->name); ++ ++ /* save log-prefix */ ++ strcpy(msg.prefix, info->prefix); ++ ++ /* save ip header */ ++ skb_copy_bits(skb, 0, &iph, sizeof(iph)); ++ memcpy(&msg.ip, &iph, sizeof(struct iphdr)); ++ ++ /* save transport header */ ++ switch (iph.protocol){ ++ case IPPROTO_TCP: ++ skb_copy_bits(skb, iph.ihl*4 , &tcph, sizeof(tcph)); ++ memcpy(&msg.h.th, &tcph, sizeof(struct tcphdr)); ++ break; ++ case IPPROTO_UDP: ++ skb_copy_bits(skb, iph.ihl*4 , &udph, sizeof(udph)); ++ memcpy(&msg.h.uh, &udph, sizeof(struct udphdr)); ++ break; ++ default: ++ break; ++ } ++ ++ /* save timetamp */ ++ do_gettimeofday((struct timeval *)&tv); ++ msg.timestamp_sec = tv.tv_sec; ++ ++ send_packet(msg); ++} ++ ++static unsigned int ipt_IFWLOG_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const void *targinfo, ++ void *userinfo) ++{ ++ const struct ipt_IFWLOG_info *info = targinfo; ++ ++ ipt_IFWLOG_packet(*pskb, in, out, info); ++ ++ return IPT_CONTINUE; ++} ++ ++static int ipt_IFWLOG_checkentry(const char *tablename, ++ const struct ipt_entry *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++{ ++ const struct ipt_IFWLOG_info *info = targinfo; ++ ++ if (info->prefix[sizeof(info->prefix)-1] != '\0') { ++ DEBUGP("IFWLOG: prefix term %i\n", ++ info->prefix[sizeof(info->prefix)-1]); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ipt_target ipt_IFWLOG = { ++ .name = "IFWLOG", ++ .target = ipt_IFWLOG_target, ++ .targetsize = sizeof(struct ipt_IFWLOG_info), ++ .checkentry = ipt_IFWLOG_checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ nl = (struct sock*) netlink_kernel_create(NETLINK_IFWLOG, GROUP, NULL, THIS_MODULE); ++ if (!nl) { ++ PRINTR(KERN_WARNING "IFWLOG: cannot create netlink socket\n"); ++ return -EINVAL; ++ } ++ ++ if (ipt_register_target(&ipt_IFWLOG)) { ++ if (nl && nl->sk_socket) ++ sock_release(nl->sk_socket); ++ return -EINVAL; ++ } ++ ++ PRINTR(KERN_INFO "IFWLOG: register target\n"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ if (nl && nl->sk_socket) ++ sock_release(nl->sk_socket); ++ PRINTR(KERN_INFO "IFWLOG: unregister target\n"); ++ ipt_unregister_target(&ipt_IFWLOG); ++} ++ ++module_init(init); ++module_exit(fini); +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -331,6 +331,17 @@ config IP_NF_TARGET_TTL + (e.g. when running oldconfig). It selects + CONFIG_NETFILTER_XT_TARGET_HL. + ++config IP_NF_TARGET_IFWLOG ++ tristate 'IFWLOG target support' ++ depends on IP_NF_IPTABLES ++ help ++ This option adds a `IFWLOG' target, which is used by ++ Interactive Firewall for sending informations to a userspace ++ daemon ++ ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. If unsure, say `N'. ++ + # raw + specific targets + config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_IFWLOG.h +@@ -0,0 +1,26 @@ ++#ifndef _IPT_IFWLOG_H ++#define _IPT_IFWLOG_H ++ ++#ifndef NETLINK_IFWLOG ++#define NETLINK_IFWLOG 19 ++#endif ++ ++#define PREFSIZ 32 ++ ++struct nl_msg { /* Netlink message */ ++ long timestamp_sec; /* time packet */ ++ char indev_name[IFNAMSIZ]; /* name of the ingoing interface */ ++ char outdev_name[IFNAMSIZ]; /* name of the outgoing interface */ ++ unsigned char prefix[PREFSIZ]; /* informations on the logging reason */ ++ struct iphdr ip; ++ union { ++ struct tcphdr th; ++ struct udphdr uh; ++ } h; ++}; ++ ++struct ipt_IFWLOG_info { ++ char prefix[PREFSIZ]; ++}; ++ ++#endif /* _IPT_IFWLOG_H */ +--- linux/net/ipv4/netfilter/Makefile.net-netfilter-IFWLOG.orig 2013-10-13 11:29:08.799136037 +0300 ++++ linux/net/ipv4/netfilter/Makefile 2013-10-13 11:30:28.085842333 +0300 +@@ -44,6 +44,7 @@ obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ip + # targets + obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o + obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o ++obj-$(CONFIG_IP_NF_TARGET_IFWLOG) += ipt_IFWLOG.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o + obj-$(CONFIG_IP_NF_TARGET_SYNPROXY) += ipt_SYNPROXY.o diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-psd-2.6.35-buildfix.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-psd-2.6.35-buildfix.patch new file mode 100644 index 00000000..218031c4 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-psd-2.6.35-buildfix.patch @@ -0,0 +1,11 @@ +--- linux-2.6.35-rc6-git-mnb0.1/net/ipv4/netfilter/ipt_psd.c.orig 2010-07-30 21:17:30.000000000 +0300 ++++ linux-2.6.35-rc6-git-mnb0.1/net/ipv4/netfilter/ipt_psd.c 2010-07-31 13:29:00.623601957 +0300 +@@ -98,7 +98,7 @@ static inline int hashfunc(struct in_add + + static bool + ipt_psd_match(const struct sk_buff *pskb, +- const struct xt_match_param *match_param) ++ struct xt_action_param *match_param) + { + struct iphdr *ip_hdr; + struct tcphdr *tcp_hdr; diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-psd-mdv.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-psd-mdv.patch new file mode 100644 index 00000000..3b5c94d5 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-psd-mdv.patch @@ -0,0 +1,233 @@ +ipt_psd: Mandriva changes + +This patch holds all the Mandriva changes done in ipt_psd +netfilter module. + +Most of the time they're just upgrades to match with new +API in the kernel. + +This work is mostly done by Thomas Backlund, Herton R. +Krzesinski and Luiz Fernando N. Capitulino. + +Signed-off-by: Luiz Fernando N. Capitulino +Signed-off-by: Herton Ronaldo Krzesinski + +--- + include/linux/netfilter_ipv4/Kbuild | 1 + net/ipv4/netfilter/Kconfig | 8 ++ + net/ipv4/netfilter/ipt_psd.c | 113 ++++++++++++++---------------------- + 3 files changed, 55 insertions(+), 67 deletions(-) + +diff -p -up linux-2.6.28/net/ipv4/netfilter/ipt_psd.c.orig linux-2.6.28/net/ipv4/netfilter/ipt_psd.c +--- linux-2.6.28/net/ipv4/netfilter/ipt_psd.c.orig 2008-12-12 11:03:05.000000000 -0500 ++++ linux-2.6.28/net/ipv4/netfilter/ipt_psd.c 2008-12-12 11:04:03.000000000 -0500 +@@ -1,21 +1,24 @@ + /* +- This is a module which is used for PSD (portscan detection) +- Derived from scanlogd v2.1 written by Solar Designer +- and LOG target module. +- +- Copyright (C) 2000,2001 astaro AG +- +- This file is distributed under the terms of the GNU General Public +- License (GPL). Copies of the GPL can be obtained from: +- ftp://prep.ai.mit.edu/pub/gnu/GPL +- +- 2000-05-04 Markus Hennig : initial +- 2000-08-18 Dennis Koslowski : first release +- 2000-12-01 Dennis Koslowski : UDP scans detection added +- 2001-01-02 Dennis Koslowski : output modified +- 2001-02-04 Jan Rekorajski : converted from target to match +- 2004-05-05 Martijn Lievaart : ported to 2.6 +-*/ ++ * This is a module which is used for PSD (portscan detection) ++ * Derived from scanlogd v2.1 written by Solar Designer ++ * and LOG target module. ++ * ++ * Copyright (C) 2000,2001 astaro AG ++ * ++ * This file is distributed under the terms of the GNU General Public ++ * License (GPL). Copies of the GPL can be obtained from: ++ * ftp://prep.ai.mit.edu/pub/gnu/GPL ++ * ++ * 2000-05-04 Markus Hennig : initial ++ * 2000-08-18 Dennis Koslowski : first release ++ * 2000-12-01 Dennis Koslowski : UDP scans detection added ++ * 2001-01-02 Dennis Koslowski : output modified ++ * 2001-02-04 Jan Rekorajski : converted from target to match ++ * 2004-05-05 Martijn Lievaart : ported to 2.6 ++ * 2007-10-10 Thomas Backlund : 2.6.22 update ++ * 2007-11-14 Luiz Capitulino : 2.6.22 API usage fixes ++ * 2007-11-26 Herton Ronaldo Krzesinski : switch xt_match->match to bool ++ */ + + #include + #include +@@ -54,7 +57,7 @@ struct port { + */ + struct host { + struct host *next; /* Next entry with the same hash */ +- clock_t timestamp; /* Last update time */ ++ unsigned long timestamp; /* Last update time */ + struct in_addr src_addr; /* Source address */ + struct in_addr dest_addr; /* Destination address */ + unsigned short src_port; /* Source port */ +@@ -93,33 +96,29 @@ static inline int hashfunc(struct in_add + return hash & (HASH_SIZE - 1); + } + +-static int ++static bool + ipt_psd_match(const struct sk_buff *pskb, +- const struct net_device *in, +- const struct net_device *out, +- const void *matchinfo, +- int offset, +- int *hotdrop) ++ const struct xt_match_param *match_param) + { + struct iphdr *ip_hdr; + struct tcphdr *tcp_hdr; + struct in_addr addr; + u_int16_t src_port,dest_port; + u_int8_t tcp_flags, proto; +- clock_t now; ++ unsigned long now; + struct host *curr, *last, **head; + int hash, index, count; + + /* Parameters from userspace */ +- const struct ipt_psd_info *psdinfo = matchinfo; ++ const struct ipt_psd_info *psdinfo = match_param->matchinfo; + + /* IP header */ +- ip_hdr = pskb->nh.iph; ++ ip_hdr = ipip_hdr(pskb); + + /* Sanity check */ + if (ntohs(ip_hdr->frag_off) & IP_OFFSET) { + DEBUGP("PSD: sanity check failed\n"); +- return 0; ++ return false; + } + + /* TCP or UDP ? */ +@@ -127,7 +126,7 @@ ipt_psd_match(const struct sk_buff *pskb + + if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) { + DEBUGP("PSD: protocol not supported\n"); +- return 0; ++ return false; + } + + /* Get the source address, source & destination ports, and TCP flags */ +@@ -151,7 +150,7 @@ ipt_psd_match(const struct sk_buff *pskb + * them spoof us. [DHCP needs this feature - HW] */ + if (!addr.s_addr) { + DEBUGP("PSD: spoofed source address (0.0.0.0)\n"); +- return 0; ++ return false; + } + + /* Use jiffies here not to depend on someone setting the time while we're +@@ -298,46 +297,26 @@ ipt_psd_match(const struct sk_buff *pskb + + out_no_match: + spin_unlock(&state.lock); +- return 0; ++ return false; + + out_match: + spin_unlock(&state.lock); +- return 1; ++ DEBUGP("PSD: Dropping packets from "NIPQUAD_FMT" \n", ++ NIPQUAD(curr->src_addr.s_addr)); ++ return true; + } + +-static int ipt_psd_checkentry(const char *tablename, +- const struct ipt_ip *e, +- void *matchinfo, +- unsigned int matchsize, +- unsigned int hook_mask) +-{ +-/* const struct ipt_psd_info *psdinfo = targinfo;*/ +- +- /* we accept TCP only */ +-/* if (e->ip.proto != IPPROTO_TCP) { */ +-/* DEBUGP("PSD: specified protocol may be TCP only\n"); */ +-/* return 0; */ +-/* } */ +- +- if (matchsize != IPT_ALIGN(sizeof(struct ipt_psd_info))) { +- DEBUGP("PSD: matchsize %u != %u\n", +- matchsize, +- IPT_ALIGN(sizeof(struct ipt_psd_info))); +- return 0; +- } +- +- return 1; +-} +- +-static struct ipt_match ipt_psd_reg = { +- .name = "psd", +- .match = ipt_psd_match, +- .checkentry = ipt_psd_checkentry, +- .me = THIS_MODULE }; ++static struct xt_match ipt_psd_reg = { ++ .name = "psd", ++ .family = AF_INET, ++ .match = ipt_psd_match, ++ .matchsize = sizeof(struct ipt_psd_info), ++ .me = THIS_MODULE ++}; + +-static int __init init(void) ++static int __init ipt_psd_init(void) + { +- if (ipt_register_match(&ipt_psd_reg)) ++ if (xt_register_match(&ipt_psd_reg)) + return -EINVAL; + + memset(&state, 0, sizeof(state)); +@@ -348,11 +327,11 @@ static int __init init(void) + return 0; + } + +-static void __exit fini(void) ++static void __exit ipt_psd_fini(void) + { +- ipt_unregister_match(&ipt_psd_reg); ++ xt_unregister_match(&ipt_psd_reg); + printk("netfilter PSD unloaded - (c) astaro AG\n"); + } + +-module_init(init); +-module_exit(fini); ++module_init(ipt_psd_init); ++module_exit(ipt_psd_fini); +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -69,6 +69,14 @@ config IP_NF_MATCH_ECN + (e.g. when running oldconfig). It selects + CONFIG_NETFILTER_XT_MATCH_ECN. + ++config IP_NF_MATCH_PSD ++ tristate 'Port scanner detection support' ++ depends on NETFILTER_ADVANCED ++ help ++ Module used for PSD (portscan detection). ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP_NF_MATCH_RPFILTER + tristate '"rpfilter" reverse path filter match support' + depends on NETFILTER_ADVANCED && (IP_NF_MANGLE || IP_NF_RAW) +--- linux/include/uapi/linux/netfilter_ipv4/Kbuild.orig ++++ linux/include/uapi/linux/netfilter_ipv4/Kbuild +@@ -8,4 +8,5 @@ header-y += ipt_REJECT.h + header-y += ipt_TTL.h + header-y += ipt_ah.h + header-y += ipt_ecn.h ++header-y += ipt_psd.h + header-y += ipt_ttl.h diff --git a/kernel/kernel/files/patches/mageia.old/net-netfilter-psd.patch b/kernel/kernel/files/patches/mageia.old/net-netfilter-psd.patch new file mode 100644 index 00000000..8ec326ff --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-netfilter-psd.patch @@ -0,0 +1,420 @@ +--- + include/linux/netfilter_ipv4/ipt_psd.h | 40 +++ + net/ipv4/netfilter/Makefile | 1 + net/ipv4/netfilter/ipt_psd.c | 358 +++++++++++++++++++++++++++++++++ + 3 files changed, 399 insertions(+) + +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_psd.c +@@ -0,0 +1,358 @@ ++/* ++ This is a module which is used for PSD (portscan detection) ++ Derived from scanlogd v2.1 written by Solar Designer ++ and LOG target module. ++ ++ Copyright (C) 2000,2001 astaro AG ++ ++ This file is distributed under the terms of the GNU General Public ++ License (GPL). Copies of the GPL can be obtained from: ++ ftp://prep.ai.mit.edu/pub/gnu/GPL ++ ++ 2000-05-04 Markus Hennig : initial ++ 2000-08-18 Dennis Koslowski : first release ++ 2000-12-01 Dennis Koslowski : UDP scans detection added ++ 2001-01-02 Dennis Koslowski : output modified ++ 2001-02-04 Jan Rekorajski : converted from target to match ++ 2004-05-05 Martijn Lievaart : ported to 2.6 ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dennis Koslowski "); ++ ++#define HF_DADDR_CHANGING 0x01 ++#define HF_SPORT_CHANGING 0x02 ++#define HF_TOS_CHANGING 0x04 ++#define HF_TTL_CHANGING 0x08 ++ ++/* ++ * Information we keep per each target port ++ */ ++struct port { ++ u_int16_t number; /* port number */ ++ u_int8_t proto; /* protocol number */ ++ u_int8_t and_flags; /* tcp ANDed flags */ ++ u_int8_t or_flags; /* tcp ORed flags */ ++}; ++ ++/* ++ * Information we keep per each source address. ++ */ ++struct host { ++ struct host *next; /* Next entry with the same hash */ ++ clock_t timestamp; /* Last update time */ ++ struct in_addr src_addr; /* Source address */ ++ struct in_addr dest_addr; /* Destination address */ ++ unsigned short src_port; /* Source port */ ++ int count; /* Number of ports in the list */ ++ int weight; /* Total weight of ports in the list */ ++ struct port ports[SCAN_MAX_COUNT - 1]; /* List of ports */ ++ unsigned char tos; /* TOS */ ++ unsigned char ttl; /* TTL */ ++ unsigned char flags; /* HF_ flags bitmask */ ++}; ++ ++/* ++ * State information. ++ */ ++static struct { ++ spinlock_t lock; ++ struct host list[LIST_SIZE]; /* List of source addresses */ ++ struct host *hash[HASH_SIZE]; /* Hash: pointers into the list */ ++ int index; /* Oldest entry to be replaced */ ++} state; ++ ++/* ++ * Convert an IP address into a hash table index. ++ */ ++static inline int hashfunc(struct in_addr addr) ++{ ++ unsigned int value; ++ int hash; ++ ++ value = addr.s_addr; ++ hash = 0; ++ do { ++ hash ^= value; ++ } while ((value >>= HASH_LOG)); ++ ++ return hash & (HASH_SIZE - 1); ++} ++ ++static int ++ipt_psd_match(const struct sk_buff *pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ int *hotdrop) ++{ ++ struct iphdr *ip_hdr; ++ struct tcphdr *tcp_hdr; ++ struct in_addr addr; ++ u_int16_t src_port,dest_port; ++ u_int8_t tcp_flags, proto; ++ clock_t now; ++ struct host *curr, *last, **head; ++ int hash, index, count; ++ ++ /* Parameters from userspace */ ++ const struct ipt_psd_info *psdinfo = matchinfo; ++ ++ /* IP header */ ++ ip_hdr = pskb->nh.iph; ++ ++ /* Sanity check */ ++ if (ntohs(ip_hdr->frag_off) & IP_OFFSET) { ++ DEBUGP("PSD: sanity check failed\n"); ++ return 0; ++ } ++ ++ /* TCP or UDP ? */ ++ proto = ip_hdr->protocol; ++ ++ if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) { ++ DEBUGP("PSD: protocol not supported\n"); ++ return 0; ++ } ++ ++ /* Get the source address, source & destination ports, and TCP flags */ ++ ++ addr.s_addr = ip_hdr->saddr; ++ ++ tcp_hdr = (struct tcphdr*)((u_int32_t *)ip_hdr + ip_hdr->ihl); ++ ++ /* Yep, it´s dirty */ ++ src_port = tcp_hdr->source; ++ dest_port = tcp_hdr->dest; ++ ++ if (proto == IPPROTO_TCP) { ++ tcp_flags = *((u_int8_t*)tcp_hdr + 13); ++ } ++ else { ++ tcp_flags = 0x00; ++ } ++ ++ /* We're using IP address 0.0.0.0 for a special purpose here, so don't let ++ * them spoof us. [DHCP needs this feature - HW] */ ++ if (!addr.s_addr) { ++ DEBUGP("PSD: spoofed source address (0.0.0.0)\n"); ++ return 0; ++ } ++ ++ /* Use jiffies here not to depend on someone setting the time while we're ++ * running; we need to be careful with possible return value overflows. */ ++ now = jiffies; ++ ++ spin_lock(&state.lock); ++ ++ /* Do we know this source address already? */ ++ count = 0; ++ last = NULL; ++ if ((curr = *(head = &state.hash[hash = hashfunc(addr)]))) ++ do { ++ if (curr->src_addr.s_addr == addr.s_addr) break; ++ count++; ++ if (curr->next) last = curr; ++ } while ((curr = curr->next)); ++ ++ if (curr) { ++ ++ /* We know this address, and the entry isn't too old. Update it. */ ++ if (now - curr->timestamp <= (psdinfo->delay_threshold*HZ)/100 && ++ time_after_eq(now, curr->timestamp)) { ++ ++ /* Just update the appropriate list entry if we've seen this port already */ ++ for (index = 0; index < curr->count; index++) { ++ if (curr->ports[index].number == dest_port) { ++ curr->ports[index].proto = proto; ++ curr->ports[index].and_flags &= tcp_flags; ++ curr->ports[index].or_flags |= tcp_flags; ++ goto out_no_match; ++ } ++ } ++ ++ /* TCP/ACK and/or TCP/RST to a new port? This could be an outgoing connection. */ ++ if (proto == IPPROTO_TCP && (tcp_hdr->ack || tcp_hdr->rst)) ++ goto out_no_match; ++ ++ /* Packet to a new port, and not TCP/ACK: update the timestamp */ ++ curr->timestamp = now; ++ ++ /* Logged this scan already? Then drop the packet. */ ++ if (curr->weight >= psdinfo->weight_threshold) ++ goto out_match; ++ ++ /* Specify if destination address, source port, TOS or TTL are not fixed */ ++ if (curr->dest_addr.s_addr != ip_hdr->daddr) ++ curr->flags |= HF_DADDR_CHANGING; ++ if (curr->src_port != src_port) ++ curr->flags |= HF_SPORT_CHANGING; ++ if (curr->tos != ip_hdr->tos) ++ curr->flags |= HF_TOS_CHANGING; ++ if (curr->ttl != ip_hdr->ttl) ++ curr->flags |= HF_TTL_CHANGING; ++ ++ /* Update the total weight */ ++ curr->weight += (ntohs(dest_port) < 1024) ? ++ psdinfo->lo_ports_weight : psdinfo->hi_ports_weight; ++ ++ /* Got enough destination ports to decide that this is a scan? */ ++ /* Then log it and drop the packet. */ ++ if (curr->weight >= psdinfo->weight_threshold) ++ goto out_match; ++ ++ /* Remember the new port */ ++ if (curr->count < SCAN_MAX_COUNT) { ++ curr->ports[curr->count].number = dest_port; ++ curr->ports[curr->count].proto = proto; ++ curr->ports[curr->count].and_flags = tcp_flags; ++ curr->ports[curr->count].or_flags = tcp_flags; ++ curr->count++; ++ } ++ ++ goto out_no_match; ++ } ++ ++ /* We know this address, but the entry is outdated. Mark it unused, and ++ * remove from the hash table. We'll allocate a new entry instead since ++ * this one might get re-used too soon. */ ++ curr->src_addr.s_addr = 0; ++ if (last) ++ last->next = last->next->next; ++ else if (*head) ++ *head = (*head)->next; ++ last = NULL; ++ } ++ ++ /* We don't need an ACK from a new source address */ ++ if (proto == IPPROTO_TCP && tcp_hdr->ack) ++ goto out_no_match; ++ ++ /* Got too many source addresses with the same hash value? Then remove the ++ * oldest one from the hash table, so that they can't take too much of our ++ * CPU time even with carefully chosen spoofed IP addresses. */ ++ if (count >= HASH_MAX && last) last->next = NULL; ++ ++ /* We're going to re-use the oldest list entry, so remove it from the hash ++ * table first (if it is really already in use, and isn't removed from the ++ * hash table already because of the HASH_MAX check above). */ ++ ++ /* First, find it */ ++ if (state.list[state.index].src_addr.s_addr) ++ head = &state.hash[hashfunc(state.list[state.index].src_addr)]; ++ else ++ head = &last; ++ last = NULL; ++ if ((curr = *head)) ++ do { ++ if (curr == &state.list[state.index]) break; ++ last = curr; ++ } while ((curr = curr->next)); ++ ++ /* Then, remove it */ ++ if (curr) { ++ if (last) ++ last->next = last->next->next; ++ else if (*head) ++ *head = (*head)->next; ++ } ++ ++ /* Get our list entry */ ++ curr = &state.list[state.index++]; ++ if (state.index >= LIST_SIZE) state.index = 0; ++ ++ /* Link it into the hash table */ ++ head = &state.hash[hash]; ++ curr->next = *head; ++ *head = curr; ++ ++ /* And fill in the fields */ ++ curr->timestamp = now; ++ curr->src_addr = addr; ++ curr->dest_addr.s_addr = ip_hdr->daddr; ++ curr->src_port = src_port; ++ curr->count = 1; ++ curr->weight = (ntohs(dest_port) < 1024) ? ++ psdinfo->lo_ports_weight : psdinfo->hi_ports_weight; ++ curr->ports[0].number = dest_port; ++ curr->ports[0].proto = proto; ++ curr->ports[0].and_flags = tcp_flags; ++ curr->ports[0].or_flags = tcp_flags; ++ curr->tos = ip_hdr->tos; ++ curr->ttl = ip_hdr->ttl; ++ ++out_no_match: ++ spin_unlock(&state.lock); ++ return 0; ++ ++out_match: ++ spin_unlock(&state.lock); ++ return 1; ++} ++ ++static int ipt_psd_checkentry(const char *tablename, ++ const struct ipt_ip *e, ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++{ ++/* const struct ipt_psd_info *psdinfo = targinfo;*/ ++ ++ /* we accept TCP only */ ++/* if (e->ip.proto != IPPROTO_TCP) { */ ++/* DEBUGP("PSD: specified protocol may be TCP only\n"); */ ++/* return 0; */ ++/* } */ ++ ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_psd_info))) { ++ DEBUGP("PSD: matchsize %u != %u\n", ++ matchsize, ++ IPT_ALIGN(sizeof(struct ipt_psd_info))); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ipt_match ipt_psd_reg = { ++ .name = "psd", ++ .match = ipt_psd_match, ++ .checkentry = ipt_psd_checkentry, ++ .me = THIS_MODULE }; ++ ++static int __init init(void) ++{ ++ if (ipt_register_match(&ipt_psd_reg)) ++ return -EINVAL; ++ ++ memset(&state, 0, sizeof(state)); ++ ++ spin_lock_init(&(state.lock)); ++ ++ printk("netfilter PSD loaded - (c) astaro AG\n"); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_match(&ipt_psd_reg); ++ printk("netfilter PSD unloaded - (c) astaro AG\n"); ++} ++ ++module_init(init); ++module_exit(fini); +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_psd.h +@@ -0,0 +1,40 @@ ++#ifndef _IPT_PSD_H ++#define _IPT_PSD_H ++ ++#include ++#include ++ ++/* ++ * High port numbers have a lower weight to reduce the frequency of false ++ * positives, such as from passive mode FTP transfers. ++ */ ++#define PORT_WEIGHT_PRIV 3 ++#define PORT_WEIGHT_HIGH 1 ++ ++/* ++ * Port scan detection thresholds: at least COUNT ports need to be scanned ++ * from the same source, with no longer than DELAY ticks between ports. ++ */ ++#define SCAN_MIN_COUNT 7 ++#define SCAN_MAX_COUNT (SCAN_MIN_COUNT * PORT_WEIGHT_PRIV) ++#define SCAN_WEIGHT_THRESHOLD SCAN_MAX_COUNT ++#define SCAN_DELAY_THRESHOLD (300) /* old usage of HZ here was erroneously and broke under uml */ ++ ++/* ++ * Keep track of up to LIST_SIZE source addresses, using a hash table of ++ * HASH_SIZE entries for faster lookups, but limiting hash collisions to ++ * HASH_MAX source addresses per the same hash value. ++ */ ++#define LIST_SIZE 0x100 ++#define HASH_LOG 9 ++#define HASH_SIZE (1 << HASH_LOG) ++#define HASH_MAX 0x10 ++ ++struct ipt_psd_info { ++ unsigned int weight_threshold; ++ unsigned int delay_threshold; ++ unsigned short lo_ports_weight; ++ unsigned short hi_ports_weight; ++}; ++ ++#endif /*_IPT_PSD_H*/ +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -49,6 +49,7 @@ + + # matches + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o ++obj-$(CONFIG_IP_NF_MATCH_PSD) += ipt_psd.o + obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o + + # targets diff --git a/kernel/kernel/files/patches/mageia.old/net-sis190-fix-list-usage.patch b/kernel/kernel/files/patches/mageia.old/net-sis190-fix-list-usage.patch new file mode 100644 index 00000000..eda13d1d --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/net-sis190-fix-list-usage.patch @@ -0,0 +1,30 @@ +--- + drivers/net/ethernet/sis/sis190.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- linux/drivers/net/ethernet/sis/sis190.c.net-sis190-fix-list-usage.orig ++++ linux/drivers/net/ethernet/sis/sis190.c +@@ -1252,6 +1252,7 @@ static u16 sis190_default_phy(struct net + struct sis190_private *tp = netdev_priv(dev); + struct mii_if_info *mii_if = &tp->mii_if; + void __iomem *ioaddr = tp->mmio_addr; ++ struct list_head *l; + u16 status; + + phy_home = phy_default = phy_lan = NULL; +@@ -1280,9 +1281,12 @@ static u16 sis190_default_phy(struct net + phy_default = phy_home; + else if (phy_lan) + phy_default = phy_lan; +- else +- phy_default = list_first_entry(&tp->first_phy, +- struct sis190_phy, list); ++ else { ++ l = &tp->first_phy; ++ l = l->next; ++ phy_default = list_first_entry(l, struct sis190_phy, list); ++ ++ } + } + + if (mii_if->phy_id != phy_default->phy_id) { diff --git a/kernel/kernel/files/patches/mageia.old/pci-acs-quirks-for-Intel-9-series-PCH-root-ports.patch b/kernel/kernel/files/patches/mageia.old/pci-acs-quirks-for-Intel-9-series-PCH-root-ports.patch new file mode 100644 index 00000000..937447d3 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/pci-acs-quirks-for-Intel-9-series-PCH-root-ports.patch @@ -0,0 +1,28 @@ +From: Alex Williamson +Subject: [PATCH] PCI: ACS quirks for Intel 9-series PCH root ports +Date: Fri, 01 May 2015 13:20:13 -0600 + +Intel confirms that 9-series chipset root ports provide ACS-equivalent +isolation when configured via the existing Intel PCH ACS quirk setup. + +Signed-off-by: Alex Williamson +Acked-by: Don Dugger + +--- + drivers/pci/quirks.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index c6dc1df..f126bd6 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -3740,6 +3740,8 @@ static const u16 pci_quirk_intel_pch_acs_ids[] = { + /* Wellsburg (X99) PCH */ + 0x8d10, 0x8d11, 0x8d12, 0x8d13, 0x8d14, 0x8d15, 0x8d16, 0x8d17, + 0x8d18, 0x8d19, 0x8d1a, 0x8d1b, 0x8d1c, 0x8d1d, 0x8d1e, ++ /* Lynx Point (9 series) PCH */ ++ 0x8c90, 0x8c92, 0x8c94, 0x8c96, 0x8c98, 0x8c9a, 0x8c9c, 0x8c9e, + }; + + static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev) + diff --git a/kernel/kernel/files/patches/mageia.old/pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch b/kernel/kernel/files/patches/mageia.old/pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch new file mode 100644 index 00000000..47e1a2f5 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch @@ -0,0 +1,60 @@ +PCI: Add ALI M5229 comaptibility mode quirk + +The libata driver will not work with an M5229 set into native IDE +mode (the old drivers/pci one does) if the M5229 does not provide +it's own IRQ. + +The M5229 implementation embedded into the ALI M1543 uses the +M1543's ISA PIC to provide the interrupts and thus does not have +an valid PCI IRQ set. This quirk detects the abscence of IRQ and +sets the M5229 back into compatibility mode to use IRQs 14 and 15 +so that libata works correctly. + +Note, I belive that the check for an valid interrupt line is +correct, I only have an M5229 in an ALI M1543 to check this +on. It would be useful to confirm that a M5229 with an valid IRQ +does not trigger this quirk. + +Signed-off-by: Ben Dooks + +--- + drivers/pci/quirks.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -964,6 +964,34 @@ static void __devinit quirk_svwks_csb5id + } + DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, quirk_svwks_csb5ide); + ++/* Some systems set the ALI M5229 in the ALI M1543 bridge to native mode, ++ * which cannot be supported by the pata_ali.c driver (the old drivers/ide ++ * makes a compatibility effort to change the IDE interrupts). ++ */ ++static void __devinit quirk_ali_ide_compatibility(struct pci_dev *pdev) ++{ ++ u8 tmp; ++ ++ /* pdev->irq and pdev->pin have yet to be initialised, so check ++ * by reading from the configuration header to see if we've got ++ * a valid interrupt line. */ ++ ++ pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp); ++ if (tmp != 0xff) ++ return; ++ ++ pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp); ++ if (tmp & 0x5) { ++ dev_info(&pdev->dev, "quirk: changing to IDE compatibility mode\n"); ++ ++ tmp &= ~0x05; ++ pdev->class &= ~0x05; ++ pci_write_config_byte(pdev, PCI_CLASS_PROG, tmp); ++ } ++} ++ ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, quirk_ali_ide_compatibility); ++ + /* + * Intel 82801CAM ICH3-M datasheet says IDE modes must be the same + */ diff --git a/kernel/kernel/files/patches/mageia.old/pci-quirks-drop-devinit-exit.patch b/kernel/kernel/files/patches/mageia.old/pci-quirks-drop-devinit-exit.patch new file mode 100644 index 00000000..c8eeeda0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/pci-quirks-drop-devinit-exit.patch @@ -0,0 +1,11 @@ +--- ./drivers/pci/quirks.c.orig 2013-01-16 18:13:57.000000000 +0200 ++++ ./drivers/pci/quirks.c 2013-01-16 18:42:24.331008402 +0200 +@@ -1083,7 +1083,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SE + * which cannot be supported by the pata_ali.c driver (the old drivers/ide + * makes a compatibility effort to change the IDE interrupts). + */ +-static void __devinit quirk_ali_ide_compatibility(struct pci_dev *pdev) ++static void quirk_ali_ide_compatibility(struct pci_dev *pdev) + { + u8 tmp; + diff --git a/kernel/kernel/files/patches/mageia.old/platform-x86-add-shuttle-wmi-driver.patch b/kernel/kernel/files/patches/mageia.old/platform-x86-add-shuttle-wmi-driver.patch new file mode 100644 index 00000000..2361b737 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/platform-x86-add-shuttle-wmi-driver.patch @@ -0,0 +1,1437 @@ + + Documentation/ABI/testing/sysfs-platform-shuttle-wmi | 72 + + MAINTAINERS | 6 + drivers/platform/x86/Kconfig | 15 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/shuttle-wmi.c | 1297 +++++++++++++++++++ + 5 files changed, 1391 insertions(+) + +diff -Nurp linux-3.14.2/Documentation/ABI/testing/sysfs-platform-shuttle-wmi linux-3.14.2-shuttle/Documentation/ABI/testing/sysfs-platform-shuttle-wmi +--- linux-3.14.2/Documentation/ABI/testing/sysfs-platform-shuttle-wmi 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.14.2-shuttle/Documentation/ABI/testing/sysfs-platform-shuttle-wmi 2014-04-26 18:33:26.867761088 +0300 +@@ -0,0 +1,72 @@ ++What: /sys/devices/platform/shuttle_wmi/lcd_auto_adjust ++Date: December 2010 ++KernelVersion: 2.6.36 ++Contact: "Herton Ronaldo Krzesinski" ++Description: ++ This is a write only option (accepts any single value, eg. ++ "echo 1 > lcd_auto_adjust") that starts LCD auto-adjust ++ function, if the machine has this function enabled. Some ++ shuttle machines have LCD attached to analog VGA connector, ++ so uses/needs auto-adjust. ++ ++What: /sys/devices/platform/shuttle_wmi/model_name ++Date: December 2010 ++KernelVersion: 2.6.36 ++Contact: "Herton Ronaldo Krzesinski" ++Description: ++ This is a read only attribute which outputs a string with model ++ name of the machine. When shuttle-wmi can't determine which ++ model it is, "Unknown" is returned. Otherwise, the possible ++ models are "Shuttle MA", "Shuttle DA18IE", "Shuttle DA18IM", ++ "Shuttle X50 V2", "Positivo A14IE01", "Positivo P13", ++ "Positivo P14". ++ ++What: /sys/devices/platform/shuttle_wmi/panel_set_default ++Date: December 2010 ++KernelVersion: 2.6.36 ++Contact: "Herton Ronaldo Krzesinski" ++Description: ++ This is a write only option (accepts any single value, eg. ++ "echo 1 > panel_set_default"). Probably resets panel/lcd to ++ default configuration, function not explained in shuttle wmi ++ documentation. It also starts an auto adjust and color adjust ++ cycle. The function should only work in shuttle machines with ++ LCD attached to an analog VGA connector. ++ ++What: /sys/devices/platform/shuttle_wmi/powersave ++Date: December 2010 ++KernelVersion: 2.6.36 ++Contact: "Herton Ronaldo Krzesinski" ++Description: ++ Control powersave state. 1 means on, 0 means off. ++ When enabled, it basically forces the cpu to stay on powersave ++ state (only works if cpu has P-states support, it is similar to ++ powersave governor in cpufreq) when machine is only running on ++ battery. If not running on battery, this function isn't expected ++ to work, any attempt to enable this returns -EIO. ++ ++What: /sys/devices/platform/shuttle_wmi/touchpad_off ++Date: December 2010 ++KernelVersion: 2.6.36 ++Contact: "Herton Ronaldo Krzesinski" ++Description: ++ Control touchpad state. 1 means off, 0 means on. ++ ++What: /sys/devices/platform/shuttle_wmi/webcam ++Date: December 2010 ++KernelVersion: 2.6.36 ++Contact: "Herton Ronaldo Krzesinski" ++Description: ++ Control webcam state. 1 means on, 0 means off. ++ ++What: /sys/devices/platform/shuttle_wmi/white_balance ++Date: December 2010 ++KernelVersion: 2.6.36 ++Contact: "Herton Ronaldo Krzesinski" ++Description: ++ This is a write only option (accepts any single value, eg. ++ "echo 1 > white_balance"). Probably triggers an automatic ++ white balance adjustment for lcd, function not explained in ++ shuttle wmi documentation. It also starts an auto adjust and ++ color adjust cycle. The function should only work in shuttle ++ machines with LCD attached to an analog VGA connector. +diff -Nurp linux-3.14.2/drivers/platform/x86/Kconfig linux-3.14.2-shuttle/drivers/platform/x86/Kconfig +--- linux-3.14.2/drivers/platform/x86/Kconfig 2014-03-31 06:40:15.000000000 +0300 ++++ linux-3.14.2-shuttle/drivers/platform/x86/Kconfig 2014-04-26 18:33:26.867761088 +0300 +@@ -811,6 +811,21 @@ config INTEL_SMARTCONNECT + This driver checks to determine whether the device has Intel Smart + Connect enabled, and if so disables it. + ++config SHUTTLE_WMI ++ tristate "Shuttle WMI Extras Driver" ++ depends on ACPI_WMI ++ depends on BACKLIGHT_CLASS_DEVICE ++ depends on RFKILL ++ depends on INPUT ++ select INPUT_SPARSEKMAP ++ ---help--- ++ This is a driver for the WMI interface present on some Shuttle ++ machines. It adds controls for wireless, bluetooth and 3g radios, ++ webcam switch, backlight controls, among others. ++ ++ If you have a Shuttle machine with ACPI-WMI interface say Y or M ++ here. ++ + config PVPANIC + tristate "pvpanic device support" + depends on ACPI +diff -Nurp linux-3.14.2/drivers/platform/x86/Makefile linux-3.14.2-shuttle/drivers/platform/x86/Makefile +--- linux-3.15/drivers/platform/x86/Makefile ++++ linux-3.15-shuttle/drivers/platform/x86/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10 + obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o + obj-$(CONFIG_INTEL_RST) += intel-rst.o + obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o ++obj-$(CONFIG_SHUTTLE_WMI) += shuttle-wmi.o + + obj-$(CONFIG_PVPANIC) += pvpanic.o + obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o +diff -Nurp linux-3.14.2/drivers/platform/x86/shuttle-wmi.c linux-3.14.2-shuttle/drivers/platform/x86/shuttle-wmi.c +--- linux-3.14.2/drivers/platform/x86/shuttle-wmi.c 1970-01-01 02:00:00.000000000 +0200 ++++ linux-3.14.2-shuttle/drivers/platform/x86/shuttle-wmi.c 2014-04-26 18:33:26.868761096 +0300 +@@ -0,0 +1,1297 @@ ++/* ++ * ACPI-WMI driver for Shuttle WMI interface ++ * ++ * Copyright (c) 2010 Herton Ronaldo Krzesinski ++ * ++ * Development of this driver was funded by Positivo Informatica S.A. ++ * Parts of the driver were based on some WMI documentation provided by Shuttle ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Herton Ronaldo Krzesinski"); ++MODULE_DESCRIPTION("Shuttle WMI Extras Driver"); ++MODULE_LICENSE("GPL"); ++ ++#define SHUTTLE_WMI_SETGET_GUID "abbc0f6f-8ea1-11d1-00a0-c90629100000" ++#define SHUTTLE_WMI_EVENT_GUID "abbc0f72-8ea1-11d1-00a0-c90629100000" ++MODULE_ALIAS("wmi:"SHUTTLE_WMI_SETGET_GUID); ++MODULE_ALIAS("wmi:"SHUTTLE_WMI_EVENT_GUID); ++ ++#define CMD_WRITEEC 0x00 ++#define CMD_READEC 0x01 ++#define CMD_SCMD 0x02 ++#define CMD_INT15 0x03 ++#define CMD_HWSW 0x07 ++#define CMD_LCTRL 0x09 ++#define CMD_CUTLVDS 0x11 ++#define CMD_MA 0x18 ++#define CMD_DA18IE 0x19 ++#define CMD_DA18IM 0x20 ++ ++#define ECRAM_ER0 0x443 ++#define ECRAM_ER1 0x45a ++#define ECRAM_ER2 0x47b ++#define ECRAM_ER3 0x758 ++#define ECRAM_ER4 0x759 ++ ++struct shuttle_ecram { ++ unsigned short addr; ++ u32 mask; ++}; ++ ++struct shuttle_state { ++ struct shuttle_ecram ecram; ++ struct device_attribute *dev_attr; ++}; ++ ++static struct shuttle_state state_powersave = { ++ .ecram = { ++ .addr = ECRAM_ER3, ++ .mask = 0x10, ++ }, ++}; ++ ++static struct shuttle_state state_touchpad_off = { ++ .ecram = { ++ .addr = ECRAM_ER2, ++ .mask = 0x02, ++ }, ++}; ++ ++static struct shuttle_state state_webcam = { ++ .ecram = { ++ .addr = ECRAM_ER2, ++ .mask = 0x10, ++ }, ++}; ++ ++struct shuttle_rfkill { ++ struct rfkill *rfk; ++ enum rfkill_type type; ++ struct shuttle_ecram ecram_state; ++ struct shuttle_ecram ecram_present; ++ /* lists of rf state switch notification codes */ ++ u32 rf_on[3]; ++ u32 rf_off[3]; ++}; ++ ++static struct shuttle_rfkill srfk_3g = { ++ .type = RFKILL_TYPE_WWAN, ++ .ecram_state = { ++ .addr = ECRAM_ER2, ++ .mask = 0x40, ++ }, ++ .rf_on = { 0x10, 0x29 }, ++ .rf_off = { 0x11, 0x2a }, ++}; ++ ++static struct shuttle_rfkill srfk_bluetooth = { ++ .type = RFKILL_TYPE_BLUETOOTH, ++ .ecram_state = { ++ .addr = ECRAM_ER2, ++ .mask = 0x20, ++ }, ++ .rf_on = { 0x0c, 0x29 }, ++ .rf_off = { 0x0d, 0x2a }, ++}; ++ ++static struct shuttle_rfkill srfk_wlan = { ++ .type = RFKILL_TYPE_WLAN, ++ .ecram_state = { ++ .addr = ECRAM_ER2, ++ .mask = 0x80, ++ }, ++ .ecram_present = { ++ .addr = ECRAM_ER1, ++ .mask = 0x80, ++ }, ++ .rf_on = { 0x08 }, ++ .rf_off = { 0x09 }, ++}; ++ ++enum fn_type { ++ FN_CMD, ++ FN_CMD_DEBUG, ++ FN_RFKILL, ++ FN_STATE ++}; ++ ++struct shuttle_fn_map { ++ char *name; ++ enum fn_type type; ++ unsigned short cmd; ++ unsigned short arg; ++ unsigned short fn; ++ void *data; ++}; ++ ++static struct shuttle_fn_map unknown_fn_map[] = { ++ { "fn_f1", FN_CMD_DEBUG, CMD_SCMD, 0, 0x01, NULL }, ++ { "fn_f2", FN_CMD_DEBUG, CMD_SCMD, 0, 0x02, NULL }, ++ { "fn_f3", FN_CMD_DEBUG, CMD_SCMD, 0, 0x03, NULL }, ++ { "fn_f4", FN_CMD_DEBUG, CMD_SCMD, 0, 0x04, NULL }, ++ { "fn_f5", FN_CMD_DEBUG, CMD_SCMD, 0, 0x05, NULL }, ++ { "fn_f6", FN_CMD_DEBUG, CMD_SCMD, 0, 0x06, NULL }, ++ { "fn_f7", FN_CMD_DEBUG, CMD_SCMD, 0, 0x07, NULL }, ++ { "fn_f8", FN_CMD_DEBUG, CMD_SCMD, 0, 0x08, NULL }, ++ { "fn_f9", FN_CMD_DEBUG, CMD_SCMD, 0, 0x09, NULL }, ++ { "fn_f10", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0a, NULL }, ++ { "fn_f11", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0b, NULL }, ++ { "fn_f12", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0c, NULL }, ++ { "fn_f13", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0d, NULL }, ++ { "fn_f14", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0e, NULL }, ++ { "fn_f15", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0f, NULL }, ++ { "lcd_auto_adjust", FN_CMD, CMD_SCMD, 0, 0x81, NULL }, ++ { "lightbar_brightness_down", FN_CMD_DEBUG, CMD_LCTRL, 1, 0x00, NULL }, ++ { "lightbar_brightness_up", FN_CMD_DEBUG, CMD_LCTRL, 1, 0x01, NULL }, ++ { "panel_set_default", FN_CMD, CMD_SCMD, 0, 0x83, NULL }, ++ { "video_output_on", FN_CMD_DEBUG, CMD_CUTLVDS, 0, 0x01, NULL}, ++ { "video_output_off", FN_CMD_DEBUG, CMD_CUTLVDS, 0, 0x00, NULL}, ++ { "white_balance", FN_CMD, CMD_SCMD, 0, 0x82, NULL }, ++ { } ++}; ++ ++static struct shuttle_fn_map fn_map_1[] = { ++ { "brightness_down", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0b, NULL }, ++ { "brightness_up", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0c, NULL }, ++ { "lcd_auto_adjust", FN_CMD, CMD_SCMD, 0, 0x81, NULL }, ++ { "lightbar_brightness_down", FN_CMD_DEBUG, CMD_LCTRL, 1, 0x00, NULL }, ++ { "lightbar_brightness_up", FN_CMD_DEBUG, CMD_LCTRL, 1, 0x01, NULL }, ++ { "panel_set_default", FN_CMD, CMD_SCMD, 0, 0x83, NULL }, ++ { "powersave", FN_STATE, CMD_SCMD, 0, 0x02, &state_powersave }, ++ { "shuttle_3g", FN_RFKILL, CMD_SCMD, 0, 0x05, &srfk_3g }, ++ { "shuttle_bluetooth", FN_RFKILL, CMD_SCMD, 0, 0x0d, &srfk_bluetooth }, ++ { "shuttle_wlan", FN_RFKILL, CMD_SCMD, 0, 0x04, &srfk_wlan }, ++ { "sleep", FN_CMD_DEBUG, CMD_SCMD, 0, 0x01, NULL }, ++ { "sound_mute", FN_CMD_DEBUG, CMD_SCMD, 0, 0x08, NULL }, ++ { "switch_video", FN_CMD_DEBUG, CMD_SCMD, 0, 0x03, NULL }, ++ { "touchpad_off", FN_STATE, CMD_SCMD, 0, 0x06, &state_touchpad_off }, ++ { "volume_down", FN_CMD_DEBUG, CMD_SCMD, 0, 0x09, NULL }, ++ { "volume_up", FN_CMD_DEBUG, CMD_SCMD, 0, 0x0a, NULL }, ++ { "video_output_on", FN_CMD_DEBUG, CMD_CUTLVDS, 0, 0x01, NULL}, ++ { "video_output_off", FN_CMD_DEBUG, CMD_CUTLVDS, 0, 0x00, NULL}, ++ { "webcam", FN_STATE, CMD_SCMD, 0, 0x07, &state_webcam }, ++ { "white_balance", FN_CMD, CMD_SCMD, 0, 0x82, NULL }, ++ { } ++}; ++ ++static struct shuttle_fn_map fn_map_2[] = { ++ { "brightness_down", FN_CMD_DEBUG, CMD_SCMD, 0, 0x08, NULL }, ++ { "brightness_up", FN_CMD_DEBUG, CMD_SCMD, 0, 0x09, NULL }, ++ { "video_output", FN_CMD_DEBUG, CMD_SCMD, 0, 0x02, NULL }, ++ { "video_output_on", FN_CMD_DEBUG, CMD_CUTLVDS, 0, 0x01, NULL}, ++ { "video_output_off", FN_CMD_DEBUG, CMD_CUTLVDS, 0, 0x00, NULL}, ++ { "shuttle_wlan", FN_RFKILL, CMD_SCMD, 0, 0x0b, &srfk_wlan }, ++ { "sleep", FN_CMD_DEBUG, CMD_SCMD, 0, 0x03, NULL }, ++ { "sound_mute", FN_CMD_DEBUG, CMD_SCMD, 0, 0x04, NULL }, ++ { "switch_video", FN_CMD_DEBUG, CMD_SCMD, 0, 0x07, NULL }, ++ { "touchpad_off", FN_STATE, CMD_SCMD, 0, 0x01, &state_touchpad_off }, ++ { "volume_down", FN_CMD_DEBUG, CMD_SCMD, 0, 0x05, NULL }, ++ { "volume_up", FN_CMD_DEBUG, CMD_SCMD, 0, 0x06, NULL }, ++ { "webcam", FN_STATE, CMD_SCMD, 0, 0x0a, &state_webcam }, ++ { } ++}; ++ ++struct shuttle_backlight { ++ u8 ec_addr; ++ struct shuttle_fn_map *fn_bl_down; ++ struct shuttle_fn_map *fn_bl_up; ++}; ++ ++static struct shuttle_backlight common_bl_desc = { ++ .ec_addr = 0x79, ++}; ++ ++static struct shuttle_backlight quirk_bl_desc = { ++ .ec_addr = 0x79, ++ .fn_bl_down = &fn_map_1[0], ++ .fn_bl_up = &fn_map_1[1], ++}; ++ ++struct shuttle_id { ++ unsigned char cmd_id; ++ const char *model_name; ++ struct shuttle_backlight *bl_desc; ++ struct shuttle_fn_map *fn_map; ++}; ++ ++static struct shuttle_id shuttle_ids[] = { ++ { CMD_MA, "Shuttle MA", &common_bl_desc, fn_map_1 }, ++ { CMD_DA18IE, "Shuttle DA18IE", &quirk_bl_desc, fn_map_1 }, ++ { CMD_DA18IM, "Shuttle DA18IM", &common_bl_desc, fn_map_1 } ++}; ++ ++static struct shuttle_id id_unknown = { ++ .model_name = "Unknown", ++ .fn_map = unknown_fn_map, ++}; ++ ++static struct shuttle_id shuttle_dmi_id; ++ ++static int shuttle_dmi_matched(const struct dmi_system_id *dmi) ++{ ++ shuttle_dmi_id.model_name = dmi->ident; ++ shuttle_dmi_id.bl_desc = &common_bl_desc; ++ shuttle_dmi_id.fn_map = dmi->driver_data; ++ return 1; ++} ++ ++static struct dmi_system_id shuttle_dmi_ids[] = { ++ { ++ .callback = shuttle_dmi_matched, ++ .ident = "Shuttle X50 V2", ++ .matches = { ++ DMI_MATCH(DMI_PRODUCT_NAME, "X50-V2"), ++ }, ++ .driver_data = fn_map_1, ++ }, ++ { ++ .callback = shuttle_dmi_matched, ++ .ident = "Positivo A14IE01", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "POSITIVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "A14IE01"), ++ }, ++ .driver_data = fn_map_2, ++ }, ++ { ++ .callback = shuttle_dmi_matched, ++ .ident = "Positivo P13", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "POSITIVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "P13"), ++ }, ++ .driver_data = fn_map_2, ++ }, ++ { ++ .callback = shuttle_dmi_matched, ++ .ident = "Positivo P14", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "POSITIVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "P14"), ++ }, ++ .driver_data = fn_map_2, ++ }, ++ {} ++}; ++ ++static struct dmi_system_id __devinitdata shuttle_quirk_bl_dmi_ids[] = { ++ { ++ .ident = "Positivo M13", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "POSITIVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "M13"), ++ }, ++ }, ++ { ++ .ident = "Positivo M14", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "POSITIVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "M14"), ++ }, ++ }, ++ { ++ .ident = "Positivo A14IM01", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "POSITIVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "A14IM01"), ++ }, ++ }, ++ { ++ .ident = "Positivo J14IM21", ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "POSITIVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "J14IM21"), ++ }, ++ }, ++ {} ++}; ++ ++struct shuttle_wmi { ++ struct platform_device *pdev; ++ struct shuttle_id *id; ++ struct dentry *dbg_root; ++ struct attribute_group *attr_group; ++ struct input_dev *inputdev; ++ struct backlight_device *bd; ++}; ++ ++struct shuttle_cmd { ++ u16 param2; ++ u16 param1; ++ u8 arg; ++ u8 cmd; ++ u16 hdr; ++}; ++ ++static int wmi_setget_mtd(struct shuttle_cmd *scmd, u32 *res) ++{ ++ acpi_status status; ++ union acpi_object *obj; ++ struct acpi_buffer input; ++ static DEFINE_MUTEX(mtd_lock); ++ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; ++ ++ input.length = sizeof(struct shuttle_cmd); ++ scmd->hdr = 0xec00; ++ input.pointer = (u8 *) scmd; ++ ++ /* We must serialize access to wmi_evaluate_method: the wmi interface ++ * functions in the bios save its parameters on a common shared buffer, ++ * which gets overwritten on parallel calls with unpredicted results; ++ * AML code doesn't have any locking, so we must do this here */ ++ mutex_lock(&mtd_lock); ++ status = wmi_evaluate_method(SHUTTLE_WMI_SETGET_GUID, 0, 2, ++ &input, &output); ++ mutex_unlock(&mtd_lock); ++ if (ACPI_FAILURE(status)) ++ return -1; ++ ++ obj = output.pointer; ++ if (obj) { ++ if (obj->type == ACPI_TYPE_INTEGER) { ++ if (res) ++ *res = obj->integer.value; ++ } else { ++ pr_err("Unsupported object returned (%s)", __func__); ++ res = NULL; ++ } ++ kfree(obj); ++ } else { ++ if (res) { ++ pr_warning("No result from WMI method (%s)", __func__); ++ res = NULL; ++ } ++ } ++ ++ return (res) ? 0 : 1; ++} ++ ++static int wmi_ec_cmd(unsigned char cmd, unsigned char arg, ++ unsigned short param1, unsigned short param2, ++ u32 *res) ++{ ++ struct shuttle_cmd scmd = { ++ .cmd = cmd, ++ .arg = arg, ++ .param1 = param1, ++ .param2 = param2 ++ }; ++ ++ return wmi_setget_mtd(&scmd, res); ++} ++ ++static int wmi_ec_state(struct shuttle_ecram *ecram) ++{ ++ u32 val; ++ ++ if (wmi_ec_cmd(CMD_READEC, 0, 0, ecram->addr, &val)) ++ return -EIO; ++ return (val & ecram->mask) ? 1 : 0; ++} ++ ++static int rfkill_common_set_block(void *data, bool blocked) ++{ ++ int sw; ++ struct shuttle_fn_map *fn_map = data; ++ struct shuttle_rfkill *srfk = fn_map->data; ++ ++ sw = wmi_ec_state(&srfk->ecram_state); ++ if (sw < 0) ++ return sw; ++ ++ if (blocked == sw) ++ wmi_ec_cmd(fn_map->cmd, fn_map->arg, 0, fn_map->fn, NULL); ++ else ++ return 0; ++ ++ sw = wmi_ec_state(&srfk->ecram_state); ++ if (sw < 0) ++ return sw; ++ ++ return (sw != blocked) ? 0 : -EIO; ++} ++ ++static const struct rfkill_ops rfkill_common_ops = { ++ .set_block = rfkill_common_set_block, ++}; ++ ++static void pr_possible_dev_state(void) ++{ ++ static bool pr; ++ u32 val; ++ ++ if (!pr) { ++ pr = true; ++ pr_info("need to unblock some rfkills to check device" ++ " presence\n"); ++ if (!wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER1, &val)) ++ pr_info("possible device present state at address" ++ " 0x%04x is 0x%08x\n", ECRAM_ER1, val); ++ } ++} ++ ++static int shuttle_rfkill_init(struct shuttle_fn_map *fn_map, ++ struct device *dev) ++{ ++ int rc; ++ struct shuttle_rfkill *srfk = fn_map->data; ++ ++ /* Try to detect if device controlled by this rfkill is present, to ++ * avoid having an rfkill switch when not needed */ ++ if (srfk->ecram_present.mask && srfk->ecram_present.addr) { ++ /* we have an address to read to check if device is present */ ++ rc = wmi_ec_state(&srfk->ecram_present); ++ if (rc <= 0) ++ return rc; ++ ++ rc = wmi_ec_state(&srfk->ecram_state); ++ if (rc < 0) ++ return rc; ++ } else { ++ /* print only once the possible value of devices presence, for ++ * extra information (useful to check if really device presence ++ * is or isn't available at usual ECRAM_ER1 address) */ ++ pr_possible_dev_state(); ++ ++ /* no address/mask to check, detect if device is available by ++ * trying to enable it, in case it's disabled */ ++ rc = wmi_ec_state(&srfk->ecram_state); ++ if (rc < 0) ++ return rc; ++ if (!rc) { ++ if (rfkill_common_set_block(fn_map, false)) ++ return 0; ++ ++ /* after check, reset to initial setting; should be ++ * unlikely this returns with error, but really check if ++ * we could reset to initial blocked setting, otherwise ++ * don't make it a fatal error and assume rfkill not ++ * blocked */ ++ if (rfkill_common_set_block(fn_map, true)) ++ rc = 1; ++ } ++ } ++ ++ srfk->rfk = rfkill_alloc(fn_map->name, dev, srfk->type, ++ &rfkill_common_ops, fn_map); ++ if (!srfk->rfk) ++ return -ENOMEM; ++ ++ rfkill_init_sw_state(srfk->rfk, !rc); ++ ++ rc = rfkill_register(srfk->rfk); ++ if (rc) { ++ rfkill_destroy(srfk->rfk); ++ srfk->rfk = NULL; ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static void shuttle_rfkill_remove(struct shuttle_fn_map *fn_map) ++{ ++ struct shuttle_rfkill *srfk = fn_map->data; ++ ++ if (srfk->rfk) { ++ rfkill_unregister(srfk->rfk); ++ rfkill_destroy(srfk->rfk); ++ srfk->rfk = NULL; ++ } ++} ++ ++static int shuttle_rfkill_resume(struct device *dev) ++{ ++ struct shuttle_fn_map *fn_map; ++ struct shuttle_rfkill *srfk; ++ int rc; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shuttle_wmi *priv = platform_get_drvdata(pdev); ++ ++ for (fn_map = priv->id->fn_map; fn_map->name; fn_map++) { ++ if (fn_map->type != FN_RFKILL) ++ continue; ++ ++ srfk = fn_map->data; ++ if (srfk->rfk) { ++ rc = wmi_ec_state(&srfk->ecram_state); ++ if (rc < 0) ++ return rc; ++ rfkill_set_sw_state(srfk->rfk, rc); ++ } ++ } ++ return 0; ++} ++ ++static bool set_rfkill_sw(u32 *list, u32 code, struct rfkill *rfk, bool blocked) ++{ ++ while (*list) { ++ if (*list == code) { ++ rfkill_set_sw_state(rfk, blocked); ++ return true; ++ } ++ list++; ++ } ++ return false; ++} ++ ++static bool notify_switch_rfkill(struct shuttle_wmi *priv, u32 code) ++{ ++ struct shuttle_fn_map *fn_map; ++ struct shuttle_rfkill *srfk; ++ struct rfkill *rfk; ++ bool res = false; ++ ++ for (fn_map = priv->id->fn_map; fn_map->name; fn_map++) { ++ if (fn_map->type != FN_RFKILL) ++ continue; ++ ++ srfk = fn_map->data; ++ rfk = srfk->rfk; ++ if (!rfk) ++ continue; ++ ++ /* check if notification code means radio turned on, looking ++ * at list_on array of "on" notification codes for this rfkill; ++ * if code is in this list, we notify rfkill core (set_rfkill_sw ++ * does the check, notification and returns true) and we can ++ * skip to next rfkill on the list (some notification codes are ++ * shared, firmware may want to turn on two radios at same time, ++ * so we must check all rfkills with this code) */ ++ if (set_rfkill_sw(srfk->rf_on, code, rfk, false)) { ++ res = true; ++ continue; ++ } ++ ++ /* same as above, but we check the list_off to see if ++ * notification code means radio turned off */ ++ if (set_rfkill_sw(srfk->rf_off, code, rfk, true)) ++ res = true; ++ } ++ /* if we found that notification code was indeed a radio on/off event, ++ * return true here */ ++ return res; ++} ++ ++static bool notify_switch_attr(struct platform_device *pdev, u32 code) ++{ ++ int i; ++ struct shuttle_switch { ++ u32 switch_on; ++ u32 switch_off; ++ char *sys_attr; ++ }; ++ static const struct shuttle_switch codes[] = { ++ { 0x04, 0x05, "touchpad_off" }, ++ { 0x12, 0x13, "webcam" }, ++ { 0x31, 0x32, "powersave" } ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(codes); i++) { ++ if (codes[i].switch_on == code || codes[i].switch_off == code) { ++ sysfs_notify(&pdev->dev.kobj, NULL, codes[i].sys_attr); ++ return true; ++ } ++ } ++ return false; ++} ++ ++static void shuttle_wmi_notify(u32 value, void *data) ++{ ++ acpi_status status; ++ union acpi_object *obj; ++ u8 type; ++ u32 code; ++ struct acpi_buffer res = { ACPI_ALLOCATE_BUFFER, NULL }; ++ struct shuttle_wmi *priv = data; ++ ++ status = wmi_get_event_data(value, &res); ++ if (status != AE_OK) { ++ pr_warning("unable to retrieve wmi event status" ++ " (error=0x%x)\n", status); ++ return; ++ } ++ ++ obj = (union acpi_object *) res.pointer; ++ if (!obj) ++ return; ++ if (obj->type != ACPI_TYPE_INTEGER) { ++ pr_info("unknown object returned in wmi event\n"); ++ goto notify_exit; ++ } ++ ++ type = (obj->integer.value >> 24) & 0xFF; ++ switch (type) { ++ case 0: /* OSD/Scancode event */ ++ code = obj->integer.value & 0xFFFFFF; ++ ++ /* update rfkill switches */ ++ if (notify_switch_rfkill(priv, code)) ++ break; ++ ++ /* send notification on state switch attributes */ ++ if (notify_switch_attr(priv->pdev, code)) ++ break; ++ ++ if (priv->bd && (code == 0x14 || code == 0x15)) { ++ backlight_force_update(priv->bd, ++ BACKLIGHT_UPDATE_HOTKEY); ++ break; ++ } ++ ++ if (!sparse_keymap_report_event(priv->inputdev, code, 1, true)) ++ pr_info("unhandled scancode (0x%06x)\n", code); ++ break; ++ case 1: /* Power management event */ ++ /* Events not used. ++ * Possible values for obj->integer.value: ++ * 0x01000000 - silent mode ++ * 0x01010000 - brightness sync */ ++ case 2: /* i-PowerXross event */ ++ /* i-PowerXross is a overclocking feature, not ++ * implemented, there are no further details, possible ++ * values for obj->integer.value in documentation: ++ * 0x02000000 - idle mode ++ * 0x02010000 - action mode ++ * 0x02020000 - entry s3 */ ++ break; ++ case 0xec: /* Lost event */ ++ if (printk_ratelimit()) ++ pr_warning("lost event because of buggy BIOS"); ++ break; ++ default: ++ pr_info("unknown wmi notification type (0x%02x)\n", type); ++ } ++ ++notify_exit: ++ kfree(obj); ++} ++ ++static const struct key_entry shuttle_wmi_keymap[] = { ++ { KE_IGNORE, 0x14, { KEY_BRIGHTNESSUP } }, ++ { KE_IGNORE, 0x15, { KEY_BRIGHTNESSDOWN } }, ++ { KE_KEY, 0x16, { KEY_FASTFORWARD } }, ++ { KE_KEY, 0x17, { KEY_REWIND } }, ++ { KE_KEY, 0x18, { KEY_F13 } }, /* OSD Beep */ ++ { KE_KEY, 0x2b, { KEY_F14 } }, /* OSD menu 1 */ ++ { KE_KEY, 0x2c, { KEY_F15 } }, /* OSD menu 2 */ ++ { KE_KEY, 0x2d, { KEY_F16 } }, /* OSD menu 3 */ ++ { KE_KEY, 0x2e, { KEY_F17 } }, /* OSD menu 4 */ ++ { KE_KEY, 0x33, { KEY_F18 } }, /* Update OSD bar status */ ++ { KE_KEY, 0x90, { KEY_WWW } }, ++ { KE_KEY, 0x95, { KEY_PREVIOUSSONG } }, ++ { KE_KEY, 0xa0, { KEY_PROG1 } }, /* Call OSD software */ ++ { KE_KEY, 0xa1, { KEY_VOLUMEDOWN } }, ++ { KE_KEY, 0xa3, { KEY_MUTE } }, ++ { KE_KEY, 0xb2, { KEY_VOLUMEUP } }, ++ { KE_KEY, 0xb4, { KEY_PLAYPAUSE } }, ++ { KE_KEY, 0xbb, { KEY_STOPCD } }, ++ { KE_KEY, 0xc8, { KEY_MAIL } }, ++ { KE_KEY, 0xcd, { KEY_NEXTSONG } }, ++ { KE_KEY, 0xd0, { KEY_MEDIA } }, ++ ++ /* Known non hotkey events don't handled, that we don't care or ++ * which we must ignore */ ++ { KE_IGNORE, 0x01, }, /* Caps Lock toggled */ ++ { KE_IGNORE, 0x02, }, /* Num Lock toggled */ ++ { KE_IGNORE, 0x03, }, /* Scroll Lock toggled */ ++ { KE_IGNORE, 0x06, }, /* Downclock/Silent on */ ++ { KE_IGNORE, 0x07, }, /* Downclock/Silent off */ ++ { KE_IGNORE, 0x0a, }, /* WiMax on */ ++ { KE_IGNORE, 0x0b, }, /* WiMax off */ ++ { KE_IGNORE, 0x0e, }, /* RF on */ ++ { KE_IGNORE, 0x0f, }, /* RF off */ ++ { KE_IGNORE, 0x1a, }, /* Auto Brightness on */ ++ { KE_IGNORE, 0x1b, }, /* Auto Brightness off */ ++ { KE_IGNORE, 0x1c, }, /* Auto-KB Brightness on */ ++ { KE_IGNORE, 0x1d, }, /* Auto-KB Brightness off */ ++ { KE_IGNORE, 0x1e, }, /* Light Bar Brightness up */ ++ { KE_IGNORE, 0x1f, }, /* Light Bar Brightness down */ ++ { KE_IGNORE, 0x20, }, /* China Telecom AP enable */ ++ { KE_IGNORE, 0x21, }, /* China Mobile AP enable */ ++ { KE_IGNORE, 0x22, }, /* Huawei AP enable */ ++ { KE_IGNORE, 0x23, }, /* Docking in */ ++ { KE_IGNORE, 0x24, }, /* Docking out */ ++ { KE_IGNORE, 0x25, }, /* Device no function */ ++ { KE_IGNORE, 0x26, }, /* i-PowerXross OverClocking */ ++ { KE_IGNORE, 0x27, }, /* i-PowerXross PowerSaving */ ++ { KE_IGNORE, 0x28, }, /* i-PowerXross off */ ++ { KE_IGNORE, 0x2f, }, /* Optimus on */ ++ { KE_IGNORE, 0x30, }, /* Optimus off */ ++ { KE_IGNORE, 0x91, }, /* ICO 2 on */ ++ { KE_IGNORE, 0x92, }, /* ICO 2 off */ ++ ++ { KE_END, 0 } ++}; ++ ++static int shuttle_wmi_input_init(struct shuttle_wmi *priv) ++{ ++ struct input_dev *input; ++ int rc; ++ ++ input = input_allocate_device(); ++ if (!input) ++ return -ENOMEM; ++ ++ input->name = "Shuttle WMI hotkeys"; ++ input->phys = KBUILD_MODNAME "/input0"; ++ input->id.bustype = BUS_HOST; ++ ++ rc = sparse_keymap_setup(input, shuttle_wmi_keymap, NULL); ++ if (rc) ++ goto err_free_dev; ++ ++ rc = input_register_device(input); ++ if (rc) ++ goto err_free_keymap; ++ ++ priv->inputdev = input; ++ return 0; ++ ++err_free_keymap: ++ sparse_keymap_free(input); ++err_free_dev: ++ input_free_device(input); ++ return rc; ++} ++ ++static void shuttle_wmi_input_remove(struct shuttle_wmi *priv) ++{ ++ struct input_dev *input = priv->inputdev; ++ ++ sparse_keymap_free(input); ++ input_unregister_device(input); ++} ++ ++static int shuttle_wmi_get_bl(struct backlight_device *bd) ++{ ++ u8 val; ++ int rc; ++ struct shuttle_wmi *priv = bl_get_data(bd); ++ struct shuttle_backlight *sbl = priv->id->bl_desc; ++ ++ rc = ec_read(sbl->ec_addr, &val); ++ if (rc) ++ return rc; ++ return val & 7; ++} ++ ++static int shuttle_wmi_update_bl(struct backlight_device *bd) ++{ ++ int rc, steps; ++ u8 val; ++ struct shuttle_fn_map *fn_down, *fn_up; ++ struct shuttle_wmi *priv = bl_get_data(bd); ++ struct shuttle_backlight *sbl = priv->id->bl_desc; ++ ++ fn_down = sbl->fn_bl_down; ++ fn_up = sbl->fn_bl_up; ++ if (!fn_down || !fn_up) { ++ rc = ec_write(sbl->ec_addr, bd->props.brightness); ++ if (rc) ++ return rc; ++ } else { ++ /* change brightness by steps, this is a quirk for shuttle ++ * machines which don't accept direct write to ec for this */ ++ rc = ec_read(sbl->ec_addr, &val); ++ if (rc) ++ return rc; ++ steps = bd->props.brightness - (val & 7); ++ while (steps > 0) { ++ wmi_ec_cmd(fn_up->cmd, fn_up->arg, 0, fn_up->fn, NULL); ++ steps--; ++ } ++ while (steps < 0) { ++ wmi_ec_cmd(fn_down->cmd, fn_down->arg, 0, fn_down->fn, ++ NULL); ++ steps++; ++ } ++ } ++ ++ wmi_ec_cmd(CMD_CUTLVDS, 0, 0, ++ (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0, ++ NULL); ++ ++ return 0; ++} ++ ++static const struct backlight_ops shuttle_wmi_bl_ops = { ++ .get_brightness = shuttle_wmi_get_bl, ++ .update_status = shuttle_wmi_update_bl, ++}; ++ ++static int shuttle_wmi_backlight_init(struct shuttle_wmi *priv) ++{ ++ int rc; ++ u8 val; ++ struct backlight_properties props; ++ struct backlight_device *bd; ++ struct shuttle_backlight *sbl = priv->id->bl_desc; ++ ++ rc = ec_read(sbl->ec_addr, &val); ++ if (rc) ++ return rc; ++ memset(&props, 0, sizeof(struct backlight_properties)); ++ props.max_brightness = 7; ++ props.brightness = val & 7; ++ props.power = FB_BLANK_UNBLANK; ++ ++ bd = backlight_device_register(KBUILD_MODNAME, &priv->pdev->dev, priv, ++ &shuttle_wmi_bl_ops, &props); ++ if (IS_ERR(bd)) ++ return PTR_ERR(bd); ++ priv->bd = bd; ++ return 0; ++} ++ ++static void shuttle_wmi_backlight_exit(struct shuttle_wmi *priv) ++{ ++ if (priv->bd) ++ backlight_device_unregister(priv->bd); ++} ++ ++static ssize_t store_fn_cmd(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shuttle_wmi *priv = platform_get_drvdata(pdev); ++ struct shuttle_fn_map *fn_map; ++ ++ for (fn_map = priv->id->fn_map; fn_map->name; fn_map++) { ++ if (fn_map->name == attr->attr.name) { ++ wmi_ec_cmd(fn_map->cmd, fn_map->arg, 0, fn_map->fn, ++ NULL); ++ return count; ++ } ++ } ++ return -EIO; ++} ++ ++static int set_fn_cmd_debug(void *data, u64 val) ++{ ++ struct shuttle_fn_map *fn_map = data; ++ ++ wmi_ec_cmd(fn_map->cmd, fn_map->arg, 0, fn_map->fn, NULL); ++ /* we don't know yet how many brightness values or maximum brightness ++ * values for lightbar, for now print possible brightness value change ++ * to aid in discovering these */ ++ if (fn_map->cmd == CMD_LCTRL) { ++ u32 val; ++ if (!wmi_ec_cmd(CMD_READEC, 0, 0, ECRAM_ER4, &val)) ++ pr_info("possible lightbar brightness change to value" ++ " 0x%08x\n", val); ++ } ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(fops_fn_cmd_debug, NULL, set_fn_cmd_debug, "%llu"); ++ ++static ssize_t show_fn_state(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct shuttle_fn_map *fn_map; ++ struct shuttle_state *state; ++ int sw; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shuttle_wmi *priv = platform_get_drvdata(pdev); ++ ++ for (fn_map = priv->id->fn_map; fn_map->name; fn_map++) { ++ if (fn_map->name != attr->attr.name) ++ continue; ++ ++ state = fn_map->data; ++ sw = wmi_ec_state(&state->ecram); ++ if (sw < 0) ++ return sw; ++ return sprintf(buf, "%d\n", sw); ++ } ++ return -EIO; ++} ++ ++static ssize_t store_fn_state(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int enable, sw; ++ struct shuttle_fn_map *fn_map; ++ struct shuttle_state *state; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shuttle_wmi *priv = platform_get_drvdata(pdev); ++ ++ if (sscanf(buf, "%i", &enable) != 1) ++ return -EINVAL; ++ ++ for (fn_map = priv->id->fn_map; fn_map->name; fn_map++) { ++ if (fn_map->name != attr->attr.name) ++ continue; ++ ++ state = fn_map->data; ++ sw = wmi_ec_state(&state->ecram); ++ if (sw < 0) ++ return sw; ++ enable = enable ? 1 : 0; ++ if (enable != sw) { ++ wmi_ec_cmd(fn_map->cmd, fn_map->arg, 0, fn_map->fn, ++ NULL); ++ sw = wmi_ec_state(&state->ecram); ++ if (sw < 0) ++ return sw; ++ if (enable != sw) ++ return -EIO; ++ } ++ return count; ++ } ++ return -EIO; ++} ++ ++static void shuttle_fn_exit(struct shuttle_wmi *priv) ++{ ++ struct shuttle_fn_map *fn_map; ++ struct dentry *dbg_entry; ++ struct shuttle_state *state; ++ ++ if (priv->attr_group) { ++ sysfs_remove_group(&priv->pdev->dev.kobj, priv->attr_group); ++ kfree(priv->attr_group->attrs); ++ kfree(priv->attr_group); ++ priv->attr_group = NULL; ++ } ++ ++ fn_map = priv->id->fn_map; ++ while (fn_map->name) { ++ switch (fn_map->type) { ++ case FN_CMD: ++ kfree(fn_map->data); ++ fn_map->data = NULL; ++ break; ++ case FN_CMD_DEBUG: ++ dbg_entry = fn_map->data; ++ if (dbg_entry) { ++ debugfs_remove(dbg_entry); ++ fn_map->data = NULL; ++ } ++ break; ++ case FN_RFKILL: ++ shuttle_rfkill_remove(fn_map); ++ break; ++ case FN_STATE: ++ state = fn_map->data; ++ kfree(state->dev_attr); ++ state->dev_attr = NULL; ++ break; ++ } ++ fn_map++; ++ } ++} ++ ++static struct device_attribute *new_dev_attr(struct shuttle_fn_map *fn_map) ++{ ++ struct device_attribute *dev_attr; ++ ++ dev_attr = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); ++ if (!dev_attr) ++ return NULL; ++ dev_attr->attr.name = fn_map->name; ++ return dev_attr; ++} ++ ++static int shuttle_fn_init(struct shuttle_wmi *priv) ++{ ++ struct shuttle_fn_map *fn_map; ++ struct device_attribute *dev_attr; ++ struct dentry *dbg_entry; ++ struct shuttle_state *state; ++ struct attribute **attr; ++ int nattr = 0; ++ int rc = -ENOMEM; ++ ++ fn_map = priv->id->fn_map; ++ while (fn_map->name) { ++ switch (fn_map->type) { ++ case FN_CMD: ++ dev_attr = new_dev_attr(fn_map); ++ if (!dev_attr) ++ goto fn_init_err; ++ dev_attr->attr.mode = 0200; ++ dev_attr->store = store_fn_cmd; ++ fn_map->data = dev_attr; ++ nattr++; ++ break; ++ case FN_CMD_DEBUG: ++ dbg_entry = debugfs_create_file(fn_map->name, 0200, ++ priv->dbg_root, fn_map, ++ &fops_fn_cmd_debug); ++ if (!dbg_entry) ++ goto fn_init_err; ++ fn_map->data = dbg_entry; ++ break; ++ case FN_RFKILL: ++ rc = shuttle_rfkill_init(fn_map, &priv->pdev->dev); ++ if (rc) ++ goto fn_init_err; ++ break; ++ case FN_STATE: ++ dev_attr = new_dev_attr(fn_map); ++ if (!dev_attr) ++ goto fn_init_err; ++ dev_attr->attr.mode = 0644; ++ dev_attr->show = show_fn_state; ++ dev_attr->store = store_fn_state; ++ state = fn_map->data; ++ state->dev_attr = dev_attr; ++ nattr++; ++ break; ++ } ++ fn_map++; ++ } ++ ++ /* create array of sysfs attributes (FN_CMD and FN_STATE types) */ ++ if (nattr > 0) { ++ priv->attr_group = kzalloc(sizeof(struct attribute_group), ++ GFP_KERNEL); ++ if (!priv->attr_group) ++ goto fn_init_err; ++ priv->attr_group->attrs = kzalloc(sizeof(struct attribute *) * ++ (nattr + 1), GFP_KERNEL); ++ if (!priv->attr_group->attrs) ++ goto fn_attrs_err; ++ attr = priv->attr_group->attrs; ++ fn_map = priv->id->fn_map; ++ while (fn_map->name) { ++ if (fn_map->type == FN_CMD) { ++ dev_attr = fn_map->data; ++ *attr = &dev_attr->attr; ++ attr++; ++ } else if (fn_map->type == FN_STATE) { ++ state = fn_map->data; ++ *attr = &state->dev_attr->attr; ++ attr++; ++ } ++ fn_map++; ++ } ++ rc = sysfs_create_group(&priv->pdev->dev.kobj, ++ priv->attr_group); ++ if (rc) ++ goto fn_grp_err; ++ } ++ ++ return 0; ++ ++fn_grp_err: ++ kfree(priv->attr_group->attrs); ++fn_attrs_err: ++ kfree(priv->attr_group); ++ priv->attr_group = NULL; ++fn_init_err: ++ shuttle_fn_exit(priv); ++ return rc; ++} ++ ++static int __devinit shuttle_wmi_probe(struct platform_device *pdev) ++{ ++ struct shuttle_wmi *priv; ++ int rc, i; ++ acpi_status status; ++ u32 val; ++ ++ priv = kzalloc(sizeof(struct shuttle_wmi), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ priv->pdev = pdev; ++ platform_set_drvdata(pdev, priv); ++ ++ for (i = 0; i < ARRAY_SIZE(shuttle_ids); i++) { ++ rc = wmi_ec_cmd(shuttle_ids[i].cmd_id, 0, 0, 0, &val); ++ if (!rc && val == 1) { ++ priv->id = &shuttle_ids[i]; ++ break; ++ } ++ } ++ /* If we can't identify the system using a WMI command, try using a DMI ++ * match, otherwise set id to unknown model */ ++ if (i == ARRAY_SIZE(shuttle_ids)) { ++ if (dmi_check_system(shuttle_dmi_ids)) ++ priv->id = &shuttle_dmi_id; ++ else ++ priv->id = &id_unknown; ++ } ++ ++ /* Process backlight quirks for some models based on DA18IM */ ++ if (priv->id->cmd_id == CMD_DA18IM) { ++ if (dmi_check_system(shuttle_quirk_bl_dmi_ids)) ++ priv->id->bl_desc = &quirk_bl_desc; ++ } ++ ++ priv->dbg_root = debugfs_create_dir(KBUILD_MODNAME, NULL); ++ if (!priv->dbg_root) { ++ rc = -ENOMEM; ++ goto err_debugfs; ++ } ++ ++ rc = shuttle_fn_init(priv); ++ if (rc) ++ goto err_fn; ++ ++ rc = shuttle_wmi_input_init(priv); ++ if (rc) ++ goto err_input; ++ ++ status = wmi_install_notify_handler(SHUTTLE_WMI_EVENT_GUID, ++ shuttle_wmi_notify, priv); ++ if (ACPI_FAILURE(status)) { ++ rc = -EIO; ++ goto err_notify; ++ } ++ ++ if (!acpi_video_backlight_support()) { ++ rc = shuttle_wmi_backlight_init(priv); ++ if (rc) ++ goto err_backlight; ++ } ++ return 0; ++ ++err_backlight: ++ wmi_remove_notify_handler(SHUTTLE_WMI_EVENT_GUID); ++err_notify: ++ shuttle_wmi_input_remove(priv); ++err_input: ++ shuttle_fn_exit(priv); ++err_fn: ++ debugfs_remove(priv->dbg_root); ++err_debugfs: ++ kfree(priv); ++ return rc; ++} ++ ++static int __devexit shuttle_wmi_remove(struct platform_device *pdev) ++{ ++ struct shuttle_wmi *priv = platform_get_drvdata(pdev); ++ ++ shuttle_wmi_backlight_exit(priv); ++ wmi_remove_notify_handler(SHUTTLE_WMI_EVENT_GUID); ++ shuttle_wmi_input_remove(priv); ++ shuttle_fn_exit(priv); ++ debugfs_remove(priv->dbg_root); ++ kfree(priv); ++ return 0; ++} ++ ++static int shuttle_wmi_resume(struct device *dev) ++{ ++ return shuttle_rfkill_resume(dev); ++} ++ ++static const struct dev_pm_ops shuttle_wmi_pm_ops = { ++ .restore = shuttle_wmi_resume, ++ .resume = shuttle_wmi_resume, ++}; ++ ++static struct platform_driver shuttle_wmi_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .pm = &shuttle_wmi_pm_ops, ++ }, ++ .probe = shuttle_wmi_probe, ++ .remove = __devexit_p(shuttle_wmi_remove), ++}; ++ ++static struct platform_device *shuttle_wmi_device; ++ ++static ssize_t show_model_name(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct shuttle_wmi *priv = platform_get_drvdata(pdev); ++ ++ return sprintf(buf, "%s\n", priv->id->model_name); ++} ++ ++static DEVICE_ATTR(model_name, 0444, show_model_name, NULL); ++ ++static struct attribute *shuttle_platform_attributes[] = { ++ &dev_attr_model_name.attr, ++ NULL ++}; ++ ++static struct attribute_group shuttle_attribute_group = { ++ .attrs = shuttle_platform_attributes ++}; ++ ++static int __init shuttle_wmi_init(void) ++{ ++ int rc; ++ u32 val; ++ ++ if (!wmi_has_guid(SHUTTLE_WMI_SETGET_GUID) || ++ !wmi_has_guid(SHUTTLE_WMI_EVENT_GUID)) { ++ pr_err("Required WMI GUID not available\n"); ++ return -ENODEV; ++ } ++ ++ /* Check that we are really on a shuttle BIOS */ ++ rc = wmi_ec_cmd(CMD_INT15, 0, 0, 0, &val); ++ if (rc || val != 0x534c) { ++ pr_err("Shuttle WMI device not found or unsupported" ++ " (val=0x%08x)\n", val); ++ return -ENODEV; ++ } ++ ++ rc = platform_driver_register(&shuttle_wmi_driver); ++ if (rc) ++ goto err_driver_register; ++ shuttle_wmi_device = platform_device_alloc(KBUILD_MODNAME, -1); ++ if (!shuttle_wmi_device) { ++ rc = -ENOMEM; ++ goto err_device_alloc; ++ } ++ rc = platform_device_add(shuttle_wmi_device); ++ if (rc) ++ goto err_device_add; ++ ++ rc = sysfs_create_group(&shuttle_wmi_device->dev.kobj, ++ &shuttle_attribute_group); ++ if (rc) ++ goto err_sysfs; ++ ++ return 0; ++ ++err_sysfs: ++ platform_device_del(shuttle_wmi_device); ++err_device_add: ++ platform_device_put(shuttle_wmi_device); ++err_device_alloc: ++ platform_driver_unregister(&shuttle_wmi_driver); ++err_driver_register: ++ return rc; ++} ++ ++static void __exit shuttle_wmi_exit(void) ++{ ++ sysfs_remove_group(&shuttle_wmi_device->dev.kobj, ++ &shuttle_attribute_group); ++ platform_device_unregister(shuttle_wmi_device); ++ platform_driver_unregister(&shuttle_wmi_driver); ++} ++ ++module_init(shuttle_wmi_init); ++module_exit(shuttle_wmi_exit); +diff -Nurp linux-3.14.2/MAINTAINERS linux-3.14.2-shuttle/MAINTAINERS +--- linux-3.14.2/MAINTAINERS 2014-03-31 06:40:15.000000000 +0300 ++++ linux-3.14.2-shuttle/MAINTAINERS 2014-04-26 18:33:26.869761100 +0300 +@@ -7880,6 +7880,12 @@ S: Odd Fixes + F: drivers/media/platform/sh_vou.c + F: include/media/sh_vou.h + ++SHUTTLE WMI EXTRAS DRIVER ++M: Herton Ronaldo Krzesinski ++L: platform-driver-x86@vger.kernel.org ++S: Maintained ++F: drivers/platform/x86/shuttle-wmi.c ++ + SIMPLE FIRMWARE INTERFACE (SFI) + M: Len Brown + L: sfi-devel@simplefirmware.org diff --git a/kernel/kernel/files/patches/mageia.old/platform-x86-shuttle-wmi-drop-devinit-exit.patch b/kernel/kernel/files/patches/mageia.old/platform-x86-shuttle-wmi-drop-devinit-exit.patch new file mode 100644 index 00000000..17128641 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/platform-x86-shuttle-wmi-drop-devinit-exit.patch @@ -0,0 +1,38 @@ +--- ./drivers/platform/x86/shuttle-wmi.c.orig 2013-01-16 18:13:57.000000000 +0200 ++++ ./drivers/platform/x86/shuttle-wmi.c 2013-01-16 18:41:13.676433242 +0200 +@@ -294,7 +294,7 @@ static struct dmi_system_id shuttle_dmi_ + {} + }; + +-static struct dmi_system_id __devinitdata shuttle_quirk_bl_dmi_ids[] = { ++static struct dmi_system_id shuttle_quirk_bl_dmi_ids[] = { + { + .ident = "Positivo M13", + .matches = { +@@ -1105,7 +1105,7 @@ fn_init_err: + return rc; + } + +-static int __devinit shuttle_wmi_probe(struct platform_device *pdev) ++static int shuttle_wmi_probe(struct platform_device *pdev) + { + struct shuttle_wmi *priv; + int rc, i; +@@ -1181,7 +1181,7 @@ err_debugfs: + return rc; + } + +-static int __devexit shuttle_wmi_remove(struct platform_device *pdev) ++static int shuttle_wmi_remove(struct platform_device *pdev) + { + struct shuttle_wmi *priv = platform_get_drvdata(pdev); + +@@ -1211,7 +1211,7 @@ static struct platform_driver shuttle_wm + .pm = &shuttle_wmi_pm_ops, + }, + .probe = shuttle_wmi_probe, +- .remove = __devexit_p(shuttle_wmi_remove), ++ .remove = shuttle_wmi_remove, + }; + + static struct platform_device *shuttle_wmi_device; diff --git a/kernel/kernel/files/patches/mageia.old/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch b/kernel/kernel/files/patches/mageia.old/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch new file mode 100644 index 00000000..49928d17 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch @@ -0,0 +1,74 @@ +SCSI: Fix NULL pointer dereference in RTPM of block layer + +The routines in scsi_pm.c assume that if a runtime-PM callback is +invoked for a SCSI device, it can only mean that the device's driver +has asked the block layer to handle the runtime power management (by +calling blk_pm_runtime_init(), which among other things sets q->dev). + +However, this assumption turns out to be wrong for things like the ses +driver. Normally ses devices are not allowed to do runtime PM, but +userspace can override this setting. If this happens, the kernel gets +a NULL pointer dereference when blk_post_runtime_resume() tries to use +the uninitialized q->dev pointer. + +This patch fixes the problem by checking q->dev in block layer before +handle runtime PM. Since ses doesn't define any PM callbacks and call +blk_pm_runtime_init(), the crash won't occur. + +This fixes Bugzilla #101371. +https://bugzilla.kernel.org/show_bug.cgi?id=101371 + +Signed-off-by: Ken Xue +Acked-by: Alan Stern +Cc: stable@vger.kernel.org + +--- + block/blk-core.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/block/blk-core.c b/block/blk-core.c +index 60912e9..a07ab18 100644 +--- a/block/blk-core.c ++++ b/block/blk-core.c +@@ -3280,6 +3280,9 @@ int blk_pre_runtime_suspend(struct request_queue *q) + { + int ret = 0; + ++ if (!q->dev) ++ return ret; ++ + spin_lock_irq(q->queue_lock); + if (q->nr_pending) { + ret = -EBUSY; +@@ -3307,6 +3310,9 @@ EXPORT_SYMBOL(blk_pre_runtime_suspend); + */ + void blk_post_runtime_suspend(struct request_queue *q, int err) + { ++ if (!q->dev) ++ return; ++ + spin_lock_irq(q->queue_lock); + if (!err) { + q->rpm_status = RPM_SUSPENDED; +@@ -3331,6 +3337,9 @@ EXPORT_SYMBOL(blk_post_runtime_suspend); + */ + void blk_pre_runtime_resume(struct request_queue *q) + { ++ if (!q->dev) ++ return; ++ + spin_lock_irq(q->queue_lock); + q->rpm_status = RPM_RESUMING; + spin_unlock_irq(q->queue_lock); +@@ -3353,6 +3362,9 @@ EXPORT_SYMBOL(blk_pre_runtime_resume); + */ + void blk_post_runtime_resume(struct request_queue *q, int err) + { ++ if (!q->dev) ++ return; ++ + spin_lock_irq(q->queue_lock); + if (!err) { + q->rpm_status = RPM_ACTIVE; +-- +1.9.1 diff --git a/kernel/kernel/files/patches/mageia.old/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch b/kernel/kernel/files/patches/mageia.old/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch new file mode 100644 index 00000000..dc20be03 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch @@ -0,0 +1,64 @@ +Revert "SCSI: Fix NULL pointer dereference in runtime PM" + +This reverts commit 49718f0fb8c9 ("SCSI: Fix NULL pointer dereference in +runtime PM") + +The old commit may lead to a issue that blk_{pre|post}_runtime_suspend and +blk_{pre|post}_runtime_resume can not be called in pairs. + +Take sr device as example, when sr device goes to runtime suspend, +blk_{pre|post}_runtime_suspend will be called since sr device defined +pm->runtime_suspend. But blk_{pre|post}_runtime_resume will not be called +since sr device doesn't have pm->runtime_resume. Then sr device can not +resume correctly anymore. + +Signed-off-by: Ken Xue +Acked-by: Alan Stern +Cc: stable@vger.kernel.org + +--- + drivers/scsi/scsi_pm.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c +index e4b7998..459abe1 100644 +--- a/drivers/scsi/scsi_pm.c ++++ b/drivers/scsi/scsi_pm.c +@@ -219,13 +219,13 @@ static int sdev_runtime_suspend(struct device *dev) + struct scsi_device *sdev = to_scsi_device(dev); + int err = 0; + +- if (pm && pm->runtime_suspend) { +- err = blk_pre_runtime_suspend(sdev->request_queue); +- if (err) +- return err; ++ err = blk_pre_runtime_suspend(sdev->request_queue); ++ if (err) ++ return err; ++ if (pm && pm->runtime_suspend) + err = pm->runtime_suspend(dev); +- blk_post_runtime_suspend(sdev->request_queue, err); +- } ++ blk_post_runtime_suspend(sdev->request_queue, err); ++ + return err; + } + +@@ -248,11 +248,11 @@ static int sdev_runtime_resume(struct device *dev) + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int err = 0; + +- if (pm && pm->runtime_resume) { +- blk_pre_runtime_resume(sdev->request_queue); ++ blk_pre_runtime_resume(sdev->request_queue); ++ if (pm && pm->runtime_resume) + err = pm->runtime_resume(dev); +- blk_post_runtime_resume(sdev->request_queue, err); +- } ++ blk_post_runtime_resume(sdev->request_queue, err); ++ + return err; + } + +-- +1.9.1 diff --git a/kernel/kernel/files/patches/mageia.old/scsi-megaraid-new-sysfs-name.patch b/kernel/kernel/files/patches/mageia.old/scsi-megaraid-new-sysfs-name.patch new file mode 100644 index 00000000..c750150f --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-megaraid-new-sysfs-name.patch @@ -0,0 +1,15 @@ +--- + drivers/scsi/megaraid/megaraid_mbox.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- linux-3.8-rc3-mga0.1/drivers/scsi/megaraid/megaraid_mbox.c.scsi-megaraid-new-sysfs-name.orig 2013-01-10 08:17:11.000000000 +0200 ++++ linux-3.8-rc3-mga0.1/drivers/scsi/megaraid/megaraid_mbox.c 2013-01-10 08:30:19.908301909 +0200 +@@ -302,7 +302,7 @@ MODULE_DEVICE_TABLE(pci, pci_id_table_g) + + + static struct pci_driver megaraid_pci_driver = { +- .name = "megaraid", ++ .name = "megaraid_mbox", + .id_table = pci_id_table_g, + .probe = megaraid_probe_one, + .remove = megaraid_detach_one, diff --git a/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-2.6.2.patch b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-2.6.2.patch new file mode 100644 index 00000000..bf7be4ba --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-2.6.2.patch @@ -0,0 +1,5083 @@ +--- + drivers/scsi/Kconfig | 36 + + drivers/scsi/Makefile | 10 + drivers/scsi/epsa2.c | 507 +++++++++++++++++++ + drivers/scsi/epst.c | 478 ++++++++++++++++++ + drivers/scsi/onscsi.c | 544 ++++++++++++++++++++ + drivers/scsi/ppscsi.c | 1294 +++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/scsi/ppscsi.h | 356 +++++++++++++ + drivers/scsi/sparcsi.c | 389 ++++++++++++++ + drivers/scsi/t348.c | 318 ++++++++++++ + drivers/scsi/t358.c | 394 ++++++++++++++ + drivers/scsi/vpi0.c | 276 ++++++++++ + drivers/scsi/vpi2.c | 418 +++++++++++++++ + 12 files changed, 5020 insertions(+) + +--- /dev/null ++++ b/drivers/scsi/vpi2.c +@@ -0,0 +1,418 @@ ++/* ++ vpi2.c (c) 1995-1999 Grant Guenther ++ (c) 1997-1999 David Campbell ++ (c) 2000 Tim Waugh ++ ++ This is the ppSCSI protocol module for the Iomega VPI2 adapter ++ found in the newer ZIP-100 drives. ++ ++*/ ++ ++#error "This doesn't work yet." ++ ++#define VPI2_VERSION "0.91" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++static char vpi2_map[256]; /* status bits permutation */ ++ ++static void vpi2_init (PHA *pha) ++{ ++ ++/* *** No MSG line on the VPI2 ! *** */ /* tmw: is this true for VPI2? */ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x80,0x40,0x00,0x20,0x10}; ++ ++ ppsc_make_map(vpi2_map,key,0); ++ sprintf(pha->ident,"vpi2 %s (%s) ",VPI2_VERSION,PPSC_H_VERSION); ++} ++ ++#define j44(a,b) ((a&0xf0)|((b>>4)&0x0f)) ++ ++#define CST(v) w2(0xc);w0(v);w2(4);w2(6);w2(4);w2(0xc); ++#define DST(v) w2(0xc);w0(v);w2(0xc);w2(0xe);w2(0xc);w2(4);w2(0xc); ++ ++static inline int imm_cpp (PHA *pha, unsigned char b) ++{ ++ unsigned char s1, s2, s3; ++ ++ w2(0xc); ++ udelay(2); /* 1 usec - infinite */ ++ w0(0xaa); ++ udelay(10); /* 7 usec - infinite */ ++ w0(0x55); ++ udelay(10); /* 7 usec - infinite */ ++ w0(0x00); ++ udelay(10); /* 7 usec - infinite */ ++ w0(0xff); ++ udelay(10); /* 7 usec - infinite */ ++ s1 = r1() & 0xb8; ++ w0(0x87); ++ udelay(10); /* 7 usec - infinite */ ++ s2 = r1() & 0xb8; ++ w0(0x78); ++ udelay(10); ++ s3 = r1() & 0x38; ++ /* ++ * Values for b are: ++ * 0000 00aa Assign address aa to current device ++ * 0010 00aa Select device aa in EPP Winbond mode ++ * 0010 10aa Select device aa in EPP mode ++ * 0011 xxxx Deselect all devices ++ * 0110 00aa Test device aa ++ * 1101 00aa Select device aa in ECP mode ++ * 1110 00aa Select device aa in Compatible mode ++ */ ++ w0(b); ++ udelay(2); /* 1 usec - infinite */ ++ w2(0x0c); ++ udelay(10); /* 7 usec - infinite */ ++ w2(0x0d); ++ udelay(2); /* 1 usec - infinite */ ++ w2(0x0c); ++ udelay(10); /* 7 usec - infinite */ ++ w0(0xff); ++ udelay(10); /* 7 usec - infinite */ ++ ++ /* ++ * The following table is electrical pin values. ++ * (BSY is inverted at the CTR register) ++ * ++ * BSY ACK POut SEL Fault ++ * S1 0 X 1 1 1 ++ * S2 1 X 0 1 1 ++ * S3 L X 1 1 S ++ * ++ * L => Last device in chain ++ * S => Selected ++ * ++ * Observered values for S1,S2,S3 are: ++ * Disconnect => f8/58/78 ++ * Connect => f8/58/70 ++ */ ++ if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x30)) ++ return 1; /* Connected */ ++ if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x38)) ++ return 0; /* Disconnected */ ++ ++ return -1; /* No device present */ ++} ++ ++static inline int do_vpi2_connect (PHA *pha) ++{ ++ pha->saved_r0 = r0(); ++ pha->saved_r2 = r2(); ++ ++ imm_cpp(pha, 0xe0); /* Select device 0 in compatible mode */ ++ imm_cpp(pha, 0x30); /* Disconnect all devices */ ++ ++ if (pha->mode >= 2) ++ /* Select device 0 in EPP mode */ ++ return imm_cpp (pha, 0x28); ++ ++ /* Select device 0 in compatible mode */ ++ return imm_cpp (pha, 0xe0); ++} ++ ++static void vpi2_connect (PHA *pha) ++{ ++ printk ("--> vpi2_connect\n"); ++ do_vpi2_connect (pha); ++ printk ("<--\n"); ++} ++ ++static void vpi2_disconnect (PHA *pha) ++{ ++ printk ("--> vpi2_disconnect\n"); ++ imm_cpp (pha, 0x30); /* Disconnect all devices */ ++ ++ w2(pha->saved_r2); ++ w0(pha->saved_r0); ++ printk ("<--\n"); ++} ++ ++ ++/* There are no data-transfer tests available, just this simple ++ check that we are talking to a VPI2. */ ++static int vpi2_test_proto (PHA *pha) ++{ ++ int e = 1; ++ ++ printk ("--> vpi2_test_proto\n"); ++ if (do_vpi2_connect(pha) == 1) ++ e--; ++ ++ vpi2_disconnect (pha); ++ printk ("<-- %d\n", e); ++ return e; ++} ++ ++static int vpi2_select (PHA *pha, int initiator, int target) ++{ ++ printk ("--> vpi2_select\n"); ++ w2(0xc); ++ if (r1() & 0x08) { ++ printk ("<-- -1 (busy)\n"); ++ return -1; /* bus busy */ ++ } ++ ++ /* ++ * Now assert the SCSI ID (HOST and TARGET) on the data bus ++ */ ++ w2(0x4); ++ w0(0x80 | (1 << target)); ++ udelay (1); ++ ++ /* ++ * Deassert SELIN first followed by STROBE ++ */ ++ w2(0xc); ++ w2(0xd); ++ ++ printk ("<-- 0\n"); ++ return 0; ++ ++} ++ ++static int vpi2_test_select (PHA *pha) ++{ ++ int val = r1() & 0x08; ++ printk ("--> vpi2_test_select\n<-- %d\n", val); ++ return val; /* BSY asserted ? */ ++} ++ ++static void vpi2_select_finish (PHA *pha) ++{ ++ printk ("--> vpi2_select_finish\n<--\n"); ++ w2(0xc); ++} ++ ++static void vpi2_deselect (PHA *pha) ++{ ++ printk ("--> vpi2_deselect\n<--\n"); ++ w2(0xc); ++} ++ ++static int vpi2_get_bus_status (PHA *pha) ++{ ++ int val; ++ printk ("--> vpi2_get_bus_status\n"); ++ w2(0xc); ++ val = vpi2_map[r1()]; ++ printk ("<-- %d\n", val); ++ return val; ++} ++ ++/* These functions are inlined so the C optimiser can move the switches ++ outside of loops where possible, am I dreaming ? */ ++ ++static inline int vpi2_read (PHA *pha, int first) ++{ ++ int l, h; ++ ++ printk ("--> vpi2_read\n<--\n"); ++ ++ switch (pha->mode) { ++ ++ case 0: if (first) w2(4); ++ w2(0x6); h = r1(); ++ w2(0x5); l = r1(); w2(4); ++ return j44(h,l); ++ ++ case 1: if (first) w2(0x25); ++ w2(0x26); ++ l = r0(); ++ w2(0x25); ++ return l; ++ ++ case 2: if (first) w2(0x24); ++ return r4(); ++ ++ default: return -1; ++ ++ } ++} ++ ++static inline void vpi2_write (PHA *pha, int v, int first ) ++{ ++ static int alternate; ++ ++ printk ("--> vpi2_write\n"); ++ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: ++ if (first) { ++ w2(0xc); ++ alternate = 0; ++ } ++ w0(v); ++ if (alternate) ++ w2(0x0); ++ else ++ w2(0x5); ++ alternate = 1 - alternate; ++ break; ++ ++ case 2: if (first) w2(0x4); ++ w4(v); ++ break; ++ ++ } ++ printk ("<--\n"); ++} ++ ++static void vpi2_slow_start (PHA *pha, char *val) ++{ ++ int r; ++ ++ printk ("--> vpi2_slow_start\n"); ++ ++ w2(0xc); ++ ++ r = (r1() & 0x10); ++ ++ if (r) *val = vpi2_read(pha,1); ++ else vpi2_write(pha,*val,1); ++ ++ printk ("<--\n"); ++} ++ ++static int vpi2_slow_done (PHA *pha) ++{ ++ printk ("--> vpi2_slow_done\n<--\n"); ++ return 1; /* vpi2 does its own REQ/ACK handshaking */ ++} ++ ++static void vpi2_slow_end (PHA *pha) ++{ ++ printk ("--> vpi2_slow_end\n<--\n"); ++ w2(0xc); ++} ++ ++static void vpi2_start_block (PHA *pha, int rd) ++{ ++ printk ("--> vpi2_start_block\n<--\n"); ++ pha->priv_flag = rd; ++} ++ ++static int vpi2_transfer_ready (PHA *pha) ++{ ++ int b; ++ ++ printk ("--> vpi2_transfer_ready\n<--\n"); ++ b = vpi2_get_bus_status(pha); ++ if ((b & PPSC_PH_STAT) == PPSC_PH_STAT) return -1; ++ if (b == (PPSC_REQ|PPSC_BSY| pha->priv_flag)) return 128; ++ return 0; ++} ++ ++static int vpi2_transfer_block (PHA *pha, char * buf, int buflen, int rd) ++{ ++ int k, n, i; ++ ++ printk ("--> vpi2_transfer_block\n"); ++ k = 0; ++ while (k < buflen) { ++ n = vpi2_transfer_ready(pha); ++ if (n <= 0 ) break; ++ if (n > (buflen-k)) n = buflen-k; ++ for (i=0;i vpi2_transfer_done\n<-- 1\n"); ++ return 1; ++} ++ ++static void vpi2_end_block (PHA *pha, int rd) ++{ ++ printk ("--> vpi2_end_block\n<--\n"); ++ w2(0xc); ++} ++ ++static void vpi2_reset_bus (PHA *pha) ++{ ++ printk ("--> vpi2_reset_bus\n<--\n"); ++ w2(0xc); ++ w0(0x40); w2(8); udelay(60); ++ w2(0xc); ++} ++ ++/* Make these correspond to the actual modes supported by the adapter */ ++ ++static char *(mode_strings[3]) = {"Nybble","PS/2","EPP"}; ++ ++static struct ppsc_protocol vpi2_psp = { ++ ++ {&host0,&host1,&host2,&host3}, /* params */ ++ &host_structs, /* hosts */ ++ 3, /* num_modes */ ++ 2, /* epp_first */ ++ 1, /* default_delay */ ++ 0, /* can_message */ ++ 16, /* sg_tablesize */ ++ mode_strings, ++ vpi2_init, ++ NULL, ++ vpi2_connect, ++ vpi2_disconnect, ++ vpi2_test_proto, ++ vpi2_select, ++ vpi2_test_select, ++ vpi2_select_finish, ++ vpi2_deselect, ++ vpi2_get_bus_status, ++ vpi2_slow_start, ++ vpi2_slow_done, ++ vpi2_slow_end, ++ vpi2_start_block, ++ vpi2_transfer_block, ++ vpi2_transfer_ready, ++ vpi2_transfer_done, ++ vpi2_end_block, ++ vpi2_reset_bus ++}; ++ ++int vpi2_detect (struct scsi_host_template *tpnt) ++{ ++ int val; ++ printk ("--> vpi2_detect\n"); ++ val = ppsc_detect( &vpi2_psp, tpnt, verbose); ++ printk ("<-- %d\n", val); ++ return val; ++} ++ ++#ifdef MODULE ++ ++struct scsi_host_template driver_template = PPSC_TEMPLATE(vpi2); ++ ++#include "scsi_module.c" ++ ++MODULE_LICENSE("GPL"); ++ ++#else ++ ++void vpi2_setup (char *str, int *ints) ++{ ++ printk ("--> vpi2_setup\n"); ++ ppsc_gen_setup(stt,4,str); ++ printk ("<--\n"); ++} ++ ++#endif ++ ++/* end of vpi2.c */ +--- /dev/null ++++ b/drivers/scsi/vpi0.c +@@ -0,0 +1,276 @@ ++/* ++ vpi0.c (c) 1995-1999 Grant Guenther ++ (c) 1997-1999 David Campbell ++ ++ This is the ppSCSI protocol module for the Iomega VPI0 adapter ++ found in the original ZIP-100 drives and the Jaz Traveller. ++ ++*/ ++ ++#define VPI0_VERSION "0.91" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++static char vpi0_map[256]; /* status bits permutation */ ++ ++static void vpi0_init (PHA *pha) ++{ ++ ++/* *** No MSG line on the VPI0 ! *** */ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x80,0x40,0x00,0x20,0x10}; ++ ++ ppsc_make_map(vpi0_map,key,0); ++ sprintf(pha->ident,"vpi0 %s (%s) ",VPI0_VERSION,PPSC_H_VERSION); ++} ++ ++#define j44(a,b) ((a&0xf0)|((b>>4)&0x0f)) ++ ++#define CST(v) w2(0xc);w0(v);w2(4);w2(6);w2(4);w2(0xc); ++#define DST(v) w2(0xc);w0(v);w2(0xc);w2(0xe);w2(0xc);w2(4);w2(0xc); ++ ++static void vpi0_connect (PHA *pha) ++{ ++ pha->saved_r0 = r0(); ++ pha->saved_r2 = r2(); ++ ++ CST(0); CST(0x3c); CST(0x20); ++ if (pha->mode >= 2) { CST(0xcf) } else { CST(0x8f) } ++} ++ ++static void vpi0_disconnect (PHA *pha) ++{ ++ DST(0); DST(0x3c); DST(0x20); DST(0xf); ++ ++ w2(pha->saved_r2); ++ w0(pha->saved_r0); ++} ++ ++ ++/* There are no data-transfer tests available, just this simple ++ check that we are talking to a VPI0. */ ++static int vpi0_test_proto (PHA *pha) ++{ ++ int e = 2; ++ ++ vpi0_connect(pha); ++ w2(0xe); ++ if ((r1() & 8) == 8) e--; ++ w2(0xc); ++ if ((r1() & 8) == 0) e--; ++ vpi0_disconnect(pha); ++ return e; ++} ++ ++static int vpi0_select (PHA *pha, int initiator, int target) ++{ ++ w2(0xc); ++ if (r1() & 0x40) return -1; /* bus busy */ ++ ++ w0(1<mode) { ++ ++ case 0: if (first) w2(4); ++ h = r1(); w2(6); ++ l = r1(); w2(4); ++ return j44(h,l); ++ ++ case 1: if (first) w2(0x25); ++ l = r0(); ++ w2(0x27); w2(0x25); ++ return l; ++ ++ case 2: if (first) w2(0x24); ++ return r4(); ++ ++ default: return -1; ++ ++ } ++} ++ ++static inline void vpi0_write (PHA *pha, int v, int first ) ++{ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: if (first) w2(0xc); ++ w0(v); w2(0xe); w2(0xc); ++ break; ++ ++ case 2: if (first) w2(0x4); ++ w4(v); ++ break; ++ ++ } ++} ++ ++static void vpi0_slow_start (PHA *pha, char *val) ++{ ++ int r; ++ ++ w2(0xc); ++ ++ r = (r1() & 0x10); ++ ++ if (r) *val = vpi0_read(pha,1); ++ else vpi0_write(pha,*val,1); ++ ++} ++ ++static int vpi0_slow_done (PHA *pha) ++{ ++ return 1; /* vpi0 does its own REQ/ACK handshaking */ ++} ++ ++static void vpi0_slow_end (PHA *pha) ++{ ++ w2(0xc); ++} ++ ++static void vpi0_start_block (PHA *pha, int rd) ++{ ++ pha->priv_flag = rd; ++} ++ ++static int vpi0_transfer_ready (PHA *pha) ++{ ++ int b; ++ ++ b = vpi0_get_bus_status(pha); ++ if ((b & PPSC_PH_STAT) == PPSC_PH_STAT) return -1; ++ if (b == (PPSC_REQ|PPSC_BSY| pha->priv_flag)) return 128; ++ return 0; ++} ++ ++static int vpi0_transfer_block (PHA *pha, char * buf, int buflen, int rd) ++{ ++ int k, n, i; ++ ++ k = 0; ++ while (k < buflen) { ++ n = vpi0_transfer_ready(pha); ++ if (n <= 0 ) break; ++ if (n > (buflen-k)) n = buflen-k; ++ for (i=0;i ++ ++ This is the low-level protocol module for the Adaptec APA-348 ++ (aka Trantor T348) parallel port SCSI adapter. It forms part ++ of the 'ppSCSI' suite of drivers. ++ ++*/ ++ ++#define T348_VERSION "0.92" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++#define j44(a,b) (((a<<1)&0xf0)+((b>>3)&0x0f)) ++ ++static char t348_map[256]; /* status bits permutation */ ++ ++static void t348_init (PHA *pha) ++{ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x20,0x40,0x10,0x08,0x04}; ++ ++ ppsc_make_map(t348_map,key,0); ++ sprintf(pha->ident,"t348 %s (%s), Adaptec APA-348", ++ T348_VERSION,PPSC_H_VERSION); ++} ++ ++static void t348_write_regr (PHA *pha, int regr, int value) ++{ ++ w0(0x40+regr); w2(1); w2(0); w0(value); w2(8); w2(0); ++} ++ ++static int t348_read_regr (PHA *pha, int regr) ++{ ++ int s,a,b; ++ ++ w0(0x10+regr); s = r2(); w2(s|1); w2(s); w2(8); ++ w0(0x80); a = r1(); w0(0); b = r1(); w2(0); ++ return j44(a,b); ++} ++ ++static void t348_connect (PHA *pha) ++{ ++ int t; ++ ++ pha->saved_r0 = r0(); ++ w0(0); ++ t = r2(); ++ w2(t%16); w0(0xfe); w2(t%4); w2((t%4)+8); w2(0); ++ pha->saved_r2 = t; ++} ++ ++static void t348_disconnect (PHA *pha) ++{ ++ w0(0x71); w2(1); w2(0); ++ w0(pha->saved_r0); ++ w2(pha->saved_r2); ++} ++ ++static int t348_test_proto (PHA *pha) ++{ ++ int k, e, a, b; ++ int wnt[3] = {0x6c, 0x55, 0xaa}; ++ ++ e = 0; ++ ++ t348_connect(pha); ++ ++ switch (pha->mode) { ++ ++ case 0: w0(0x70); w2(1); w2(0); w0(0); ++ for (k=0;k<3;k++) { ++ w2(8); a = r1(); w2(0); ++ w2(8); w2(8); w2(8); w2(8); w2(8); ++ b = r1(); w2(0); ++ if (j44(b,a) != wnt[k]) e++; ++ } ++ break; ++ ++ case 1: w0(0x50); w2(1); w2(0); ++ for (k=0;k<3;k++) { ++ w2(0xe0); w2(0xe8); ++ if (r0() != wnt[k]) e++; ++ w2(0xe0); w2(0xe8); ++ } ++ ++ } ++ ++ t348_disconnect(pha); ++ ++ return e; ++} ++ ++/* The T348 appears to contain a NCR 5380 core. The following ++ functions use the 5380 registers. See NCR5380.h for clues. ++*/ ++ ++#define WR(r,v) t348_write_regr(pha,r,v) ++#define RR(r) (t348_read_regr(pha,r)) ++ ++static int t348_select (PHA *pha, int initiator, int target) ++{ ++ WR(3,0); WR(1,1); ++ WR(0,(1 << initiator)); WR(2,1); udelay(100); ++ if (RR(1) != 0x41) { ++ WR(1,0); ++ return -1; ++ } ++ ++ WR(1,5); WR(0,(1 << initiator)|(1 << target)); ++ WR(2,0); WR(2,0); WR(2,0); ++ return 0; ++} ++ ++static int t348_test_select (PHA *pha) ++{ ++ return ((RR(4) & 0x42) == 0x42); ++} ++ ++static void t348_select_finish (PHA *pha) ++{ ++ WR(3,2); WR(1,5); WR(1,1); ++} ++ ++static void t348_deselect (PHA *pha) ++{ ++ WR(1,0); ++} ++ ++static int t348_get_bus_status (PHA *pha) ++{ ++ int s; ++ ++ s = RR(4); ++ return t348_map[s]; ++} ++ ++static void t348_slow_start (PHA *pha, char *val) ++{ ++ int ph, io; ++ ++ ph = ((RR(4)>>2)&7); ++ io = (ph & 1); ++ ++ WR(3,ph); ++ WR(1,1-io); ++ if (io) *val = RR(0); else WR(0,*val); ++ WR(1,0x10+(1-io)); ++} ++ ++static int t348_slow_done (PHA *pha) ++{ ++ return ((RR(4) & 0x20) == 0); ++} ++ ++static void t348_slow_end (PHA *pha) ++{ ++ int io; ++ ++ io = ((RR(4)>>2)&1); ++ ++ WR(1,1-io); ++} ++ ++static void t348_start_block (PHA *pha, int rd) ++{ ++ if (rd) { ++ ++ WR(3,1); WR(1,0); ++ WR(2,2); WR(7,3); ++ WR(3,1); WR(1,0); ++ ++ switch (pha->mode) { ++ ++ case 0: w0(0x31); w2(1); w2(0); w0(0x80); w2(8); ++ break; ++ ++ case 1: w0(0x21); w2(1); w2(0); w2(0xe8); ++ break; ++ } ++ ++ } else { ++ ++ WR(3,0); WR(1,1); ++ WR(2,2); WR(5,0); ++ WR(3,0); WR(1,1); ++ ++ w0(0x61); w2(1); w2(0); ++ } ++} ++ ++static int t348_transfer_ready (PHA *pha) ++{ ++ if (r1() & 0x80) return 1; ++ ++ if (pha->data_dir == 0) return 0; ++ return -1; ++} ++ ++static int t348_transfer_block (PHA *pha, char * buf, int buflen, int rd) ++{ ++ int k, a, b; ++ ++ k = 0; ++ while (k < buflen) { ++ ++ if (t348_transfer_ready(pha) <= 0) break; ++ ++ if (rd) { ++ switch(pha->mode) { ++ ++ case 0: a = r1(); w0(0); b = r1(); w0(0xc0); ++ buf[k++] = j44(a,b); ++ a = r1(); w0(0x40); b = r1(); w0(0x80); ++ buf[k++] = j44(a,b); ++ break; ++ ++ case 1: buf[k++] = r0(); w2(0xea); ++ buf[k++] = r0(); w2(0xe8); ++ break; ++ } ++ ++ } else { ++ ++ w0(buf[k++]); w2(2); ++ w0(buf[k++]); w2(0); ++ } ++ ++ } ++ ++ return k; ++} ++ ++static int t348_transfer_done (PHA *pha) ++{ ++ return 1; ++} ++ ++static void t348_end_block (PHA *pha, int rd) ++{ ++ w2(0); ++ WR(2,0); ++} ++ ++ ++static void t348_reset_bus (PHA *pha) ++{ ++ WR(1,1); WR(3,0); ++ WR(2,0); ++ WR(1,0x80); udelay(60); ++ WR(1,0); ++ WR(2,0); ++ WR(1,1); WR(3,0); ++ WR(2,0); ++} ++ ++static char *(mode_strings[2]) = {"Nybble","PS/2"}; ++ ++static struct ppsc_protocol t348_psp = { ++ ++ {&host0,&host1,&host2,&host3}, /* params */ ++ &host_structs, /* hosts */ ++ 2, /* num_modes */ ++ 2, /* epp_first */ ++ 1, /* default_delay */ ++ 1, /* can_message */ ++ 0, /* sg_tablesize */ ++ mode_strings, ++ t348_init, ++ NULL, ++ t348_connect, ++ t348_disconnect, ++ t348_test_proto, ++ t348_select, ++ t348_test_select, ++ t348_select_finish, ++ t348_deselect, ++ t348_get_bus_status, ++ t348_slow_start, ++ t348_slow_done, ++ t348_slow_end, ++ t348_start_block, ++ t348_transfer_block, ++ t348_transfer_ready, ++ t348_transfer_done, ++ t348_end_block, ++ t348_reset_bus ++}; ++ ++int t348_detect (struct scsi_host_template *tpnt) ++{ ++ return ppsc_detect( &t348_psp, tpnt, verbose); ++} ++ ++#ifdef MODULE ++ ++struct scsi_host_template driver_template = PPSC_TEMPLATE(t348); ++ ++#include "scsi_module.c" ++ ++MODULE_LICENSE("GPL"); ++ ++#else ++ ++void t348_setup (char *str, int *ints) ++{ ++ ppsc_gen_setup(stt,4,str); ++} ++ ++#endif ++ ++/* end of t348.c */ ++ +--- /dev/null ++++ b/drivers/scsi/t358.c +@@ -0,0 +1,394 @@ ++/* ++ t358.c (c) 1997-1999 Grant Guenther ++ ++ This is the low-level protocol module for the Adaptec APA-358 ++ (aka Trantor T358) parallel port SCSI adapter. It forms part ++ of the 'ppSCSI' suite of drivers. ++ ++*/ ++ ++#define T358_VERSION "0.91" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++#define j44(a,b) (((a<<1)&0xf0)+((b>>3)&0x0f)) ++ ++static char t358_map[256]; /* status bits permutation */ ++ ++static void t358_init (PHA *pha) ++{ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x20,0x40,0x10,0x08,0x04}; ++ ++ ppsc_make_map(t358_map,key,0); ++ sprintf(pha->ident,"t358 %s (%s), Adaptec APA-358", ++ T358_VERSION,PPSC_H_VERSION); ++} ++ ++static void t358_write_regr (PHA *pha, int regr, int value) ++{ ++ int x; ++ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: w0(regr); x = r2(); w2(1); w2(9); w2(0); w2(0); ++ w0(value); w2(1); w2(3); w2(0); w2(x); ++ break; ++ ++ case 2: w2(0xc0); w3(regr); w4(value); ++ break; ++ ++ } ++} ++ ++static int t358_read_regr (PHA *pha, int regr) ++{ ++ int h, l; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(regr); w2(1); w2(9); w2(0); w2(0); ++ w0(0x80); w2(2); h = r1(); ++ w0(0); l = r1(); w2(0); ++ return j44(h,l); ++ ++ case 1: w0(regr); h = r2(); w2(1); w2(9); w2(0); w2(0); ++ w2(0xe2); l = r0(); w2(h); ++ return l; ++ ++ case 2: h = r2(); w2(0xe0); w3(regr); w2(0xe0); ++ l = r4(); w2(h); ++ return l; ++ } ++ ++ return 0; ++} ++ ++static void t358_read_block (PHA *pha, char *buf, int len) ++{ ++ int k, h, l; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(0x10); w2(1); w2(9); w2(0); w2(0); ++ for (k=0;kmode) { ++ ++ case 0: ++ case 1: w0(0x10); x = r2(); ++ w2(1); w2(9); w2(0); w2(0); ++ for (k=0;ksaved_r0 = r0(); ++ w0(0); ++ pha->saved_r2 = r2(); ++ b = pha->saved_r2 % 4; ++ w0(0xf7); w2(b+4); w2(b); w2(b+8); w2(b); w2(0); ++ ++ if (pha->mode) { w0(0x80); w2(1); w2(9); w2(1); w2(0); } ++ else { w0(0xa0); w2(1); w2(9); w2(0); } ++} ++ ++static void t358_disconnect (PHA *pha) ++{ ++ w0(pha->saved_r0); ++ w2(pha->saved_r2); ++} ++ ++static int t358_test_proto (PHA *pha) ++{ ++ int h, l, a, b; ++ int j = 0, k = 0, e = 0; ++ ++ t358_connect(pha); ++ ++ switch (pha->mode) { ++ ++ case 0: w0(0x80); w2(8); h = r1(); w0(0); l = r1(); ++ w2(0); w2(8); a = r1(); w0(0); b = r1(); w2(0); ++ k = j44(h,l); j = j44(a,b); ++ break; ++ ++ case 1: w2(0xe0); w0(0); w2(0xe8); k = r0(); ++ w2(0xe0); w2(0xe8); j = r0(); w2(0xe0); ++ break; ++ ++ case 2: w0(0xa0); w2(1); w2(9); w2(0); ++ w0(0x80); w2(8); h = r1(); w0(0); l = r1(); ++ w2(0); w2(8); a = r1(); w0(0); b = r1(); w2(0); ++ k = j44(h,l); j = j44(a,b); ++ w0(0x80); w2(1); w2(9); w2(1); w2(0); ++ ++ } ++ ++ if (V_PROBE) printk("%s: Signature: %x %x\n",pha->device,k,j); ++ ++ if ((k != 0xe8) || (j != 0xff)) e++; ++ ++ t358_disconnect(pha); ++ ++ if (!e) { ++ ++ t358_connect(pha); ++ ++ for (j=0;j<256;j++) { ++ t358_write_regr(pha,0,j); ++ k = t358_read_regr(pha,0); ++ if (k != j) e++; ++ } ++ ++ t358_disconnect(pha); ++ ++ } ++ ++ return e; ++} ++ ++/* The T358 appears to contain a NCR 53c400 core. Check NCR5380.h ++ for hints about the regrs ... */ ++ ++#define WR(r,v) t358_write_regr(pha,r+8,v) ++#define RR(r) (t358_read_regr(pha,r+8)) ++ ++static int t358_select (PHA *pha, int initiator, int target) ++{ ++ WR(3,0); WR(1,1); ++ WR(0,(1 << initiator)); WR(2,1); udelay(100); ++ if (RR(1) != 0x41) { ++ WR(1,0); ++ return -1; ++ } ++ ++ WR(1,5); WR(0,(1 << initiator)|(1 << target)); ++ WR(2,0); WR(2,0); WR(2,0); ++ return 0; ++} ++ ++static int t358_test_select (PHA *pha) ++{ ++ return ((RR(4) & 0x42) == 0x42); ++} ++ ++static void t358_select_finish (PHA *pha) ++{ ++ WR(3,2); WR(1,5); WR(1,1); ++} ++ ++static void t358_deselect (PHA *pha) ++{ ++ WR(1,0); ++} ++ ++static int t358_get_bus_status (PHA *pha) ++{ ++ int s; ++ ++ s = RR(4); ++ return t358_map[s]; ++} ++ ++static void t358_slow_start (PHA *pha, char *val) ++{ ++ int ph, io; ++ ++ ph = ((RR(4)>>2)&7); ++ io = (ph & 1); ++ ++ WR(3,ph); ++ WR(1,1-io); ++ if (io) *val = RR(0); else WR(0,*val); ++ WR(1,0x10+(1-io)); ++} ++ ++static int t358_slow_done (PHA *pha) ++{ ++ return ((RR(4) & 0x20) == 0); ++} ++ ++static void t358_slow_end (PHA *pha) ++{ ++ int io; ++ ++ io = ((RR(4)>>2)&1); ++ ++ WR(1,1-io); ++} ++ ++static void t358_start_block (PHA *pha, int rd) ++{ ++ if (rd) { ++ WR(3,1); WR(1,0); ++ WR(2,2); ++ WR(0x10,0x40); WR(2,0); WR(2,0xa); ++ WR(3,1); WR(1,0); WR(7,3); ++ } else { ++ WR(3,0); WR(1,1); ++ WR(2,2); ++ WR(0x10,0); WR(2,0); WR(2,0xa); ++ WR(3,0); WR(1,1); WR(5,0); ++ } ++ WR(0x11,pha->tlen/128); ++} ++ ++static int t358_transfer_ready (PHA *pha) ++{ ++ int r; ++ ++ r = RR(0x10); ++ ++ if (!(r & 4)) return 128; /* 4 is host buffer not ready */ ++ ++ if (r & 1) return -1; /* last block transferred */ ++ ++ return 0; ++} ++ ++static int t358_transfer_block (PHA *pha, char * buf, int buflen, int rd) ++{ ++ int k, n; ++ ++ k = 0; ++ while (k < buflen) { ++ ++ n = t358_transfer_ready(pha); ++ ++ if (n <= 0) break; ++ ++ if (n > (buflen - k)) n = buflen - k; ++ ++ if (rd) t358_read_block(pha,buf,n); ++ else t358_write_block(pha,buf,n); ++ ++ k += n; buf += n; ++ ++ } ++ ++ return k; ++} ++ ++static int t358_transfer_done (PHA *pha) ++{ ++ if (RR(0x10) & 1) return 1; /* last block transferred */ ++ return 0; ++} ++ ++static void t358_end_block (PHA *pha, int rd) ++{ ++ WR(2,0); ++} ++ ++ ++static void t358_reset_bus (PHA *pha) ++{ ++ WR(1,1); WR(3,0); ++ WR(2,0); ++ WR(1,0x80); udelay(60); ++ WR(1,0); ++ WR(2,0); ++ WR(1,1); WR(3,0); ++ WR(2,0); ++} ++ ++static char *(mode_strings[3]) = {"Nybble","PS/2","EPP"}; ++ ++static struct ppsc_protocol t358_psp = { ++ ++ {&host0,&host1,&host2,&host3}, /* params */ ++ &host_structs, /* hosts */ ++ 3, /* num_modes */ ++ 2, /* epp_first */ ++ 1, /* default_delay */ ++ 1, /* can_message */ ++ 16, /* sg_tablesize */ ++ mode_strings, ++ t358_init, ++ NULL, ++ t358_connect, ++ t358_disconnect, ++ t358_test_proto, ++ t358_select, ++ t358_test_select, ++ t358_select_finish, ++ t358_deselect, ++ t358_get_bus_status, ++ t358_slow_start, ++ t358_slow_done, ++ t358_slow_end, ++ t358_start_block, ++ t358_transfer_block, ++ t358_transfer_ready, ++ t358_transfer_done, ++ t358_end_block, ++ t358_reset_bus ++}; ++ ++int t358_detect (struct scsi_host_template *tpnt ) ++{ ++ return ppsc_detect( &t358_psp, tpnt, verbose); ++} ++ ++#ifdef MODULE ++ ++struct scsi_host_template driver_template = PPSC_TEMPLATE(t358); ++ ++#include "scsi_module.c" ++ ++MODULE_LICENSE("GPL"); ++ ++#else ++ ++void t358_setup (char *str, int *ints) ++{ ++ ppsc_gen_setup(stt,4,str); ++} ++ ++#endif ++ ++/* end of t358.c */ ++ +--- a/drivers/scsi/Makefile ++++ b/drivers/scsi/Makefile +@@ -129,6 +129,16 @@ obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libisc + + obj-$(CONFIG_ARM) += arm/ + ++obj-$(CONFIG_PPSCSI) += ppscsi.o ++obj-$(CONFIG_PPSCSI_T348) += t348.o ++obj-$(CONFIG_PPSCSI_T358) += t358.o ++obj-$(CONFIG_PPSCSI_ONSCSI) += onscsi.o ++obj-$(CONFIG_PPSCSI_EPSA2) += epsa2.o ++obj-$(CONFIG_PPSCSI_EPST) += epst.o ++obj-$(CONFIG_PPSCSI_VPI0) += vpi0.o ++obj-$(CONFIG_PPSCSI_VPI2) += vpi2.o ++obj-$(CONFIG_PPSCSI_SPARCSI) += sparcsi.o ++ + obj-$(CONFIG_CHR_DEV_ST) += st.o + obj-$(CONFIG_CHR_DEV_OSST) += osst.o + obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o +--- /dev/null ++++ b/drivers/scsi/epst.c +@@ -0,0 +1,478 @@ ++/* ++ epst.c (c) 1996-1999 Grant Guenther ++ ++ This is the ppSCSI protocol module for the Shuttle ++ Technologies EPST parallel port SCSI adapter. ++ ++*/ ++ ++#define EPST_VERSION "0.92" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++#define EPST_VER_CODE 0xb2 ++ ++#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) ++#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) ++ ++static char epst_map[256]; /* status bits permutation */ ++ ++static void epst_init (PHA *pha) ++{ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x20,0x40,0x04,0x02,0x01}; ++ ++ ppsc_make_map(epst_map,key,0); ++ sprintf(pha->ident,"epst %s (%s), Shuttle EPST", ++ EPST_VERSION,PPSC_H_VERSION); ++} ++ ++static void epst_write_regr (PHA *pha, int regr, int value) ++{ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: ++ case 2: w0(0x60+regr); w2(1); w0(value); w2(4); ++ break; ++ ++ case 3: ++ case 4: ++ case 5: w3(0x40+regr); w4(value); ++ break; ++ ++ } ++} ++ ++static int epst_read_regr (PHA *pha, int regr) ++{ ++ int a, b; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(regr); w2(1); w2(3); ++ a = r1(); w2(4); b = r1(); ++ return j44(a,b); ++ ++ case 1: w0(0x40+regr); w2(1); w2(4); ++ a = r1(); b = r2(); w0(0xff); ++ return j53(a,b); ++ ++ case 2: w0(0x20+regr); w2(1); w2(0x25); ++ a = r0(); w2(4); ++ return a; ++ ++ case 3: ++ case 4: ++ case 5: w3(regr); w2(0x24); a = r4(); ++ return a; ++ ++ } ++ ++ return -1; ++} ++ ++/* for performance reasons, these block transfer functions make ++ some assumptions about the behaviour of the SCSI devices. In ++ particular, DMA transfers are assumed not to stall within the ++ last few bytes of a block ... ++*/ ++ ++static int epst_read_block (PHA *pha, char *buf, int len) ++{ ++ int t, k, p, a, b; ++ ++ k = 0; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(7); w2(1); w2(3); w0(0xff); ++ p = 1; ++ while (k < len) { ++ w2(6+p); a = r1(); ++ if (a & 8) b = a; else { w2(4+p); b = r1(); } ++ buf[k++] = j44(a,b); ++ p = 1 - p; ++ if (!(k % 16)) { ++ w0(0xfe); t = r1(); w0(0xff); ++ if (t & 8) break; ++ } ++ } ++ w0(0); w2(4); ++ break; ++ ++ case 1: w0(0x47); w2(1); w2(5); w0(0xff); ++ p = 0; ++ while (k < len) { ++ a = r1(); b = r2(); ++ buf[k++] = j53(a,b); ++ w2(4+p); ++ p = 1 - p; ++ if (!(k % 16)) { ++ w0(0xfe); t = r1(); w0(0xff); ++ if (t & 8) break; ++ } ++ } ++ w0(0); w2(4); ++ break; ++ ++ case 2: w0(0x27); w2(1); ++ p = 1; ++ while (k < len) { ++ w2(0x24+p); ++ buf[k++] = r0(); ++ p = 1 - p; ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(6); w2(4); ++ break; ++ ++ case 3: w3(0x80); w2(0x24); ++ while (k < len) { ++ buf[k++] = r4(); ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 4: w3(0x80); w2(0x24); ++ while (k < len) { ++ if ((len - k) > 1) { ++ *((u16 *)(&buf[k])) = r4w(); ++ k += 2; ++ } else { ++ buf[k++] = r4(); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 5: w3(0x80); w2(0x24); ++ while (k < len) { ++ if ((len - k) > 3) { ++ *((u32 *)(&buf[k])) = r4l(); ++ k += 4; ++ } else { ++ buf[k++] = r4(); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ } ++ ++ return k; ++} ++ ++static int epst_write_block (PHA *pha, char *buf, int len) ++{ ++ int p, k; ++ ++ k = 0; ++ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: ++ case 2: w0(0x67); w2(1); ++ p = 1; ++ while (k < len) { ++ w2(4+p); ++ w0(buf[k++]); ++ p = 1 - p; ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(5); w2(7); w2(4); ++ break; ++ ++ case 3: w3(0xc0); ++ while (k < len) { ++ w4(buf[k++]); ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 4: w3(0xc0); ++ while (k < len) { ++ if ((len - k) > 1) { ++ w4w(*((u16 *)(&buf[k]))); ++ k += 2; ++ } else { ++ w4(buf[k++]); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 5: w3(0xc0); ++ while (k < len) { ++ if ((len - k) > 3) { ++ w4l(*((u32 *)(&buf[k]))); ++ k += 4; ++ } else { ++ w4(buf[k++]); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ } ++ ++ return k; ++} ++ ++#define WR(r,v) epst_write_regr(pha,r,v) ++#define RR(r) (epst_read_regr(pha,r)) ++ ++#define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ ++ w0(0x87);w0(0x78);w0(x);w2(5);w2(4);w0(0xff); ++ ++static void epst_connect (PHA *pha) ++{ ++ w2(4); ++ CPP(0x40); CPP(0xe0); ++ w0(0); w2(1); w2(3); w2(4); ++ ++ if (pha->mode >= 3) { ++ w0(0); w2(1); w2(3); w2(4); w2(0xc); ++ w0(0x40); w2(6); w2(7); w2(4); ++ } ++ ++ WR(0x1d,0x20); WR(0x1d,0); /* clear the ring buffer */ ++ WR(0xa,0x1e); /* set up PDMA */ ++ WR(0xc,4); /* enable status bits */ ++ WR(8,2); /* deglitch timing */ ++} ++ ++static void epst_disconnect (PHA *pha) ++{ ++ CPP(0x30); w2(4); ++ CPP(0x40); w2(4); ++} ++ ++#define Wsr(r,v) WR(0x18+r,v) ++#define Rsr(r) (RR(0x18+r)) ++ ++static int epst_test_proto (PHA *pha) ++{ ++ int i, j, e; ++ char wb[16], rb[16]; ++ ++ e = 0; ++ ++ epst_connect(pha); ++ i = RR(0xb); ++ if (V_PROBE) printk("%s: version code reads: 0x%x\n",pha->device,i); ++ epst_disconnect(pha); ++ ++ if (i != EPST_VER_CODE) return 1; ++ ++ epst_connect(pha); ++ ++ for (j=0;j<200;j++) { ++ for (i=0;i<16;i++) { wb[i] = i+j; rb[i] = i+j+6; } ++ Wsr(5,1); ++ epst_write_block(pha,wb,16); ++ Wsr(5,0x11); ++ epst_read_block(pha,rb,16); ++ for (i=0;i<16;i++) if (wb[i] != rb[i]) e++; ++ } ++ ++ epst_disconnect(pha); ++ ++ if (V_FULL) ++ printk("%s: test port 0x%x mode %d errors %d\n", ++ pha->device,pha->port,pha->mode,e); ++ ++ return e; ++} ++ ++/* The EPST contains a core SCSI controller that is very ++ similar to the NCR 5380. Some bits have been shuffled ++ around, but the basic structure is the same. ++*/ ++ ++static int epst_select (PHA *pha, int initiator, int target) ++{ ++ Wsr(4,(1< ++ ++ This is the low-level protocol module for the WBS-11A parallel ++ port SCSI adapter. This adapter has been marketed by LinkSys ++ as the "ParaSCSI+" and by Shining Technologies as the "SparCSI". ++ The device is constructed from the KBIC-951A ISA replicator ++ chip from KingByte and the NCR 5380. ++ ++*/ ++ ++#define SPARCSI_VERSION "0.91" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++#define r12w() (delay_p,inw(pha->port+1)&0xffff) ++ ++#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) ++#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0)) ++ ++static char sparcsi_map[256]; /* status bits permutation */ ++ ++static void sparcsi_init (PHA *pha) ++{ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x20,0x40,0x10,0x08,0x04}; ++ ++ ppsc_make_map(sparcsi_map,key,0); ++ sprintf(pha->ident,"sparcsi %s (%s), WBS-11A", ++ SPARCSI_VERSION,PPSC_H_VERSION); ++} ++ ++static void sparcsi_write_regr (PHA *pha, int regr, int value) ++{ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: ++ case 2: w0(regr|0x10); w2(4); w2(6); w2(4); ++ w0(value); w2(5); w2(4); ++ break; ++ ++ case 3: w0(0x20); w2(4); w2(6); w2(4); w3(regr); ++ w4(value); w2(4); w2(0); w2(4); ++ break; ++ ++ } ++} ++ ++static int sparcsi_read_regr (PHA *pha, int regr) ++{ ++ int a, b; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(regr|0x18); w2(4); w2(6); w2(4); w2(5); ++ a = r1(); w0(0x58); b = r1(); w2(4); ++ return j44(a,b); ++ ++ case 1: w0(regr|0x58); w2(4); w2(6); w2(4); w2(5); ++ a = r12w(); w2(4); ++ return j53(a); ++ ++ case 2: w0(regr|0x98); w2(4); w2(6); w2(4); w2(0xa5); ++ a = r0(); w2(4); ++ return a; ++ ++ case 3: w0(0x20); w2(4); w2(6); w2(4); w3(regr); ++ w2(0xe4); a = r4(); w2(4); w2(0); w2(4); ++ return a; ++ ++ } ++ return -1; ++} ++ ++static void sparcsi_read_block (PHA *pha, char *buf, int len) ++{ ++ int k, a, b; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(8); w2(4); w2(6); w2(4); ++ for (k=0;kmode) { ++ ++ case 0: ++ case 1: ++ case 2: w0(0); w2(4); w2(6); w2(4); ++ for(k=0;ksaved_r0 = r0(); ++ pha->saved_r2 = r2(); ++ w2(4); ++} ++ ++static void sparcsi_disconnect (PHA *pha) ++{ ++ w0(pha->saved_r0); ++ w2(pha->saved_r2); ++} ++ ++#define WR(r,v) sparcsi_write_regr(pha,r,v) ++#define RR(r) (sparcsi_read_regr(pha,r)) ++ ++static int sparcsi_test_proto (PHA *pha) ++{ ++ int k, e; ++ ++ e = 0; ++ ++ sparcsi_connect(pha); ++ ++ if (!pha->private[0]) { /* reset the SCSI bus on first sight */ ++ ++ if (V_FULL) printk("%s: SCSI reset ...\n",pha->device); ++ ++ WR(1,0x80); udelay(60); ++ WR(1,0); ++ scsi_sleep(5*HZ); ++ pha->private[0] = 1; ++ } ++ ++ WR(1,0); ++ WR(1,1); ++ ++ if (V_PROBE) ++ printk("%s: 5380 regrs [4]=%x [5]=%x\n",pha->device,RR(4),RR(5)); ++ ++ for (k=0;k<256;k++) { ++ WR(0,k); ++ if (RR(0) != k) e++; ++ WR(0,255-k); ++ if (RR(0) != (255-k)) e++; ++ } ++ ++ WR(1,0); ++ ++ sparcsi_disconnect(pha); ++ ++ return e; ++} ++ ++static int sparcsi_select (PHA *pha, int initiator, int target) ++{ ++ WR(3,0); WR(1,1); ++ WR(0,(1 << initiator)); WR(2,1); udelay(100); ++ if (RR(1) != 0x41) { ++ WR(1,0); ++ return -1; ++ } ++ ++ WR(1,5); WR(0,(1 << initiator)|(1 << target)); ++ WR(2,0); WR(2,0); WR(2,0); ++ return 0; ++} ++ ++static int sparcsi_test_select (PHA *pha) ++{ ++ return ((RR(4) & 0x42) == 0x42); ++} ++ ++static void sparcsi_select_finish (PHA *pha) ++{ ++ WR(3,2); WR(1,5); WR(1,1); ++} ++ ++static void sparcsi_deselect (PHA *pha) ++{ ++ WR(1,0); ++} ++ ++static int sparcsi_get_bus_status (PHA *pha) ++{ ++ int s; ++ ++ s = RR(4); ++ return sparcsi_map[s]; ++} ++ ++static void sparcsi_slow_start (PHA *pha, char *val) ++{ ++ int ph, io; ++ ++ ph = ((RR(4)>>2)&7); ++ io = (ph & 1); ++ ++ WR(3,ph); ++ WR(1,1-io); ++ if (io) *val = RR(0); else WR(0,*val); ++ WR(1,0x10+(1-io)); ++} ++ ++static int sparcsi_slow_done (PHA *pha) ++{ ++ return ((RR(4) & 0x20) == 0); ++} ++ ++static void sparcsi_slow_end (PHA *pha) ++{ ++ int io; ++ ++ io = ((RR(4)>>2)&1); ++ ++ WR(1,1-io); ++} ++ ++static void sparcsi_start_block (PHA *pha, int rd) ++{ ++ if (rd) { ++ ++ WR(3,1); WR(1,0); ++ WR(2,2); WR(7,3); ++ WR(3,1); WR(1,0); ++ ++ } else { ++ ++ WR(3,0); WR(1,1); ++ WR(2,2); WR(5,0); ++ WR(3,0); WR(1,1); ++ ++ } ++ pha->priv_flag = rd; ++} ++ ++static int sparcsi_transfer_ready (PHA *pha) ++{ ++ int chunk; ++ ++ chunk = 512; ++ if ((pha->data_count == 0) && (!pha->priv_flag)) chunk++; ++ ++ if (r1() & 0x40) return chunk; ++ if (!(RR(5) & 8)) return -1; ++ return 0; ++} ++ ++static int sparcsi_transfer_block (PHA *pha, char * buf, int buflen, int rd) ++{ ++ int k, n; ++ ++ k = 0; ++ while (k < buflen) { ++ ++ n = sparcsi_transfer_ready(pha); ++ ++ if (n <= 0) break; ++ ++ if (n > (buflen - k)) n = buflen - k; ++ ++ if (rd) sparcsi_read_block(pha,buf,n); ++ else sparcsi_write_block(pha,buf,n); ++ ++ k += n; buf += n; ++ } ++ ++ return k; ++} ++ ++static int sparcsi_transfer_done (PHA *pha) ++{ ++ return 1; ++} ++ ++static void sparcsi_end_block (PHA *pha, int rd) ++{ ++ char buf[2] = {0,0}; ++ ++ if (!rd) sparcsi_write_block(pha,buf,1); ++ ++ WR(2,0); ++} ++ ++static void sparcsi_reset_bus (PHA *pha) ++{ ++ WR(1,1); WR(3,0); ++ WR(2,0); ++ WR(1,0x80); udelay(60); ++ WR(1,0); ++ WR(2,0); ++ WR(1,1); WR(3,0); ++ WR(2,0); ++} ++ ++static char *(mode_strings[4]) = {"Nybble","KBIC 5/3","PS/2","EPP"}; ++ ++static struct ppsc_protocol sparcsi_psp = { ++ ++ {&host0,&host1,&host2,&host3}, /* params */ ++ &host_structs, /* hosts */ ++ 4, /* num_modes */ ++ 3, /* epp_first */ ++ 1, /* default_delay */ ++ 1, /* can_message */ ++ 16, /* sg_tablesize */ ++ mode_strings, ++ sparcsi_init, ++ NULL, ++ sparcsi_connect, ++ sparcsi_disconnect, ++ sparcsi_test_proto, ++ sparcsi_select, ++ sparcsi_test_select, ++ sparcsi_select_finish, ++ sparcsi_deselect, ++ sparcsi_get_bus_status, ++ sparcsi_slow_start, ++ sparcsi_slow_done, ++ sparcsi_slow_end, ++ sparcsi_start_block, ++ sparcsi_transfer_block, ++ sparcsi_transfer_ready, ++ sparcsi_transfer_done, ++ sparcsi_end_block, ++ sparcsi_reset_bus ++}; ++ ++int sparcsi_detect (Scsi_Host_Template *tpnt) ++{ ++ return ppsc_detect( &sparcsi_psp, tpnt, verbose); ++} ++ ++#ifdef MODULE ++ ++Scsi_Host_Template driver_template = PPSC_TEMPLATE(sparcsi); ++ ++#include "scsi_module.c" ++ ++MODULE_LICENSE("GPL"); ++ ++#else ++ ++void sparcsi_setup (char *str, int *ints) ++{ ++ ppsc_gen_setup(stt,4,str); ++} ++ ++#endif ++ ++/* end of sparcsi.c */ ++ +--- /dev/null ++++ b/drivers/scsi/epsa2.c +@@ -0,0 +1,507 @@ ++/* ++ epsa2.c (c) 1996-1999 Grant Guenther ++ ++ This is the ppSCSI protocol module for the Shuttle ++ Technologies EPSA2 parallel port SCSI adapter. EPSA2 is ++ a predecessor to the EPST. It uses slightly different ++ command encoding and has a less elaborate internal register ++ model. ++ ++*/ ++ ++#define EPSA2_VERSION "0.91" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++#define EPSA2_VER_CODE 0xb1 ++ ++#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) ++#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) ++ ++static char epsa2_map[256]; /* status bits permutation */ ++ ++static void epsa2_init( PHA *pha) ++{ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x20,0x40,0x04,0x02,0x01}; ++ ++ ppsc_make_map(epsa2_map,key,0); ++ sprintf(pha->ident,"epsa2 %s (%s), Shuttle EPSA2", ++ EPSA2_VERSION,PPSC_H_VERSION); ++} ++ ++static void epsa2_write_regr (PHA *pha, int regr, int value) ++{ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: ++ case 2: w0(0x70+regr); w2(1); w0(value); w2(4); ++ break; ++ ++ case 3: ++ case 4: ++ case 5: w3(0x40+regr); w4(value); w2(4); ++ break; ++ ++ } ++} ++ ++static int epsa2_read_regr (PHA *pha, int regr) ++{ ++ int a, b; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(0x40+regr); w2(1); w2(3); ++ a = r1(); w2(4); b = r1(); ++ return j44(a,b); ++ ++ case 1: w0(0x60+regr); w2(1); w2(4); ++ a = r1(); b = r2(); w0(0xff); ++ return j53(a,b); ++ ++ case 2: w0(0x50+regr); w2(1); w2(0x25); ++ a = r0(); w2(4); ++ return a; ++ ++ case 3: ++ case 4: ++ case 5: w3(regr); w2(0x24); a = r4(); w2(4); ++ return a; ++ ++ } ++ ++ return -1; ++} ++ ++/* for performance reasons, these block transfer functions make ++ some assumptions about the behaviour of the SCSI devices. In ++ particular, DMA transfers are assumed not to stall within the ++ last few bytes of a block ... ++*/ ++ ++static int epsa2_read_block (PHA *pha, char *buf, int len) ++{ ++ int t, k, p, a, b; ++ ++ k = 0; ++ ++ switch (pha->mode) { ++ ++ case 0: w0(7); w2(1); w2(3); w0(0xff); ++ p = 1; ++ while (k < len) { ++ w2(6+p); a = r1(); ++ if (a & 8) b = a; else { w2(4+p); b = r1(); } ++ buf[k++] = j44(a,b); ++ p = 1 - p; ++ if (!(k % 16)) { ++ w0(0xfe); t = r1(); w0(0xff); ++ if (t & 8) break; ++ } ++ } ++ w0(0); w2(4); ++ break; ++ ++ case 1: w0(0x27); w2(1); w2(5); w0(0xff); ++ p = 0; ++ while (k < len) { ++ a = r1(); b = r2(); ++ buf[k++] = j53(a,b); ++ w2(4+p); ++ p = 1 - p; ++ if (!(k % 16)) { ++ w0(0xfe); t = r1(); w0(0xff); ++ if (t & 8) break; ++ } ++ } ++ w0(0); w2(4); ++ break; ++ ++ case 2: w0(0x17); w2(1); ++ p = 1; ++ while (k < len) { ++ w2(0x24+p); ++ buf[k++] = r0(); ++ p = 1 - p; ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(6); w2(4); ++ break; ++ ++ case 3: w3(6); w2(0x24); ++ while (k < len) { ++ buf[k++] = r4(); ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 4: w3(6); w2(0x24); ++ while (k < len) { ++ if ((len - k) > 1) { ++ *((u16 *)(&buf[k])) = r4w(); ++ k += 2; ++ } else { ++ buf[k++] = r4(); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 5: w3(6); w2(0x24); ++ while (k < len) { ++ if ((len - k) > 3) { ++ *((u32 *)(&buf[k])) = r4l(); ++ k += 4; ++ } else { ++ buf[k++] = r4(); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ } ++ ++ return k; ++} ++ ++static int epsa2_write_block (PHA *pha, char *buf, int len) ++{ ++ int p, k; ++ ++ k = 0; ++ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: ++ case 2: w0(0x37); w2(1); ++ p = 1; ++ while (k < len) { ++ w2(4+p); ++ w0(buf[k++]); ++ p = 1 - p; ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(5); w2(7); w2(4); ++ break; ++ ++ case 3: w3(0x46); ++ while (k < len) { ++ w4(buf[k++]); ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 4: w3(0x46); ++ while (k < len) { ++ if ((len - k) > 1) { ++ w4w(*((u16 *)(&buf[k]))); ++ k += 2; ++ } else { ++ w4(buf[k++]); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ ++ case 5: w3(0x46); ++ while (k < len) { ++ if ((len - k) > 3) { ++ w4l(*((u32 *)(&buf[k]))); ++ k += 4; ++ } else { ++ w4(buf[k++]); ++ } ++ if ((!(k % 16)) && (r1() & 8)) break; ++ } ++ w2(4); ++ break; ++ } ++ ++ return k; ++} ++ ++#define WR(r,v) epsa2_write_regr(pha,r,v) ++#define RR(r) (epsa2_read_regr(pha,r)) ++ ++#define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ ++ w0(0x87);w0(0x78);w0(x);w2(5);w2(4);w0(0xff); ++ ++static void epsa2_connect (PHA *pha) ++{ ++ CPP(0x40); CPP(0xe0); ++ ++ w0(0x73); w2(1); w0(0); w2(4); ++ w0(0x72); w2(1); w0(0x40); w2(4); ++ ++ w0(0); w2(1); w2(4); ++ ++ CPP(0x50); CPP(0x48); ++ ++ switch (pha->mode) { ++ ++ case 0: WR(7,0x82); ++ break; ++ ++ case 1: ++ case 2: WR(7,0xa2); ++ break; ++ ++ case 3: ++ case 4: ++ case 5: CPP(0x30); CPP(0x20); ++ WR(7,0xa3); ++ break; ++ } ++ ++ w2(4); ++} ++ ++static void epsa2_disconnect (PHA *pha) ++{ ++ switch (pha->mode) { ++ ++ case 0: WR(7,2); WR(2,0); ++ break; ++ ++ case 1: ++ case 2: WR(7,0x22); WR(2,0); ++ break; ++ ++ case 3: ++ case 4: ++ case 5: WR(7,0x23); w2(4); ++ w0(0x72); w2(1); w0(0); w2(4); ++ break; ++ } ++ ++ CPP(0x30); CPP(0x40); ++} ++ ++static int epsa2_test_proto (PHA *pha) ++{ ++ int i, j, e; ++ char wb[16], rb[16]; ++ ++ e = 0; ++ ++ epsa2_connect(pha); ++ i = RR(7); ++ if (V_PROBE) printk("%s: version code reads: 0x%x\n",pha->device,i); ++ epsa2_disconnect(pha); ++ ++ if (i != EPSA2_VER_CODE) return 1; ++ ++ epsa2_connect(pha); ++ ++ for (j=0;j<200;j++) { ++ for (i=0;i<16;i++) { wb[i] = i+j; rb[i] = i+j+6; } ++ WR(5,1); ++ epsa2_write_block(pha,wb,16); ++ udelay(100); ++ WR(5,0x11); ++ epsa2_read_block(pha,rb,16); ++ for (i=0;i<16;i++) if (wb[i] != rb[i]) e++; ++ } ++ ++ epsa2_disconnect(pha); ++ ++ if (V_FULL) ++ printk("%s: test port 0x%x mode %d errors %d\n", ++ pha->device,pha->port,pha->mode,e); ++ ++ return e; ++} ++ ++/* The EPSA2 contains a core SCSI controller that is very ++ similar to the NCR 5380. Some bits have been shuffled ++ around, but the basic structure is the same. ++*/ ++ ++static int epsa2_select (PHA *pha, int initiator, int target) ++{ ++ WR(4,(1< ++ (C) 2000 Tim Waugh ++ Under the terms of the GNU general public license. ++ ++ This is the common code shared by the PPSCSI family of ++ low-level drivers for parallel port SCSI host adapters. ++ ++ ++ To use one of the ppSCSI drivers, you must first have this module ++ built-in to your kernel, or loaded. Then, you can load the ++ appropriate protocol module. All protocol modules accept the ++ same parameters: ++ ++ verbose=N determines the logging level where N= ++ 0 only serious errors are logged ++ 1 report progress messages while probing adapters ++ 2 log the scsi commands sent to adapters ++ 3 basic debugging information ++ 4 full debugging (generates lots of output) ++ ++ hostN=,,,,, ++ ++ sets per-host-adapter parameters where ++ ++ N is between 0 and 3, each protocol can ++ support up to four separate adapters. ++ ++ The parport for this adapter, eg: ++ 0 for parport0. ++ ++ Protocol dependent mode number. Usually ++ probed to determine the fastest available ++ mode. ++ ++ microseconds of delay per port access. ++ Default is protocol dependent. ++ ++ Determines this host's ability to load ++ the system. Default 0. Set to 1 or 2 ++ to reduce load at the expense of device ++ performance. ++ ++ scatter-gather table size. ++ ++ bit mask of targets on which to force ++ all commands to use explicit REQ/ACK ++ handshaking, rather than adapter buffers. ++ ++*/ ++ ++#define PPSC_VERSION "0.92" ++ ++#define PPSC_BASE ++#include "ppscsi.h" ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define PPSC_GEN_TMO 40*HZ ++#define PPSC_SELECT_TMO HZ/10 ++#define PPSC_PROBE_TMO HZ/2 ++#define PPSC_RESET_TMO 4*HZ ++#define PPSC_SLOW_LOOPS 30 ++#define PPSC_BUSY_SNOOZE HZ; ++ ++#define PPSC_DEF_NICE 0 ++#define PPSC_INITIATOR 7 ++ ++spinlock_t ppsc_spinlock = SPIN_LOCK_UNLOCKED; ++ ++static char ppsc_bulk_map[256]; ++ ++struct ppsc_port_list_struct { ++ struct parport *port; ++ struct ppsc_port_list_struct *next; ++}; ++static struct ppsc_port_list_struct *ppsc_port_list; ++ ++/* ppsc_attach and ppsc_detach are for keeping a list of currently ++ * available ports, held under a mutex. We do this rather than ++ * using parport_enumerate because it stops a load of races. ++ */ ++ ++static void ppsc_attach (struct parport *port) ++{ ++ struct ppsc_port_list_struct *add; ++ ++ add = kmalloc (sizeof (struct ppsc_port_list_struct), GFP_KERNEL); ++ if (!add) { ++ printk (KERN_WARNING "ppscsi: memory squeeze\n"); ++ return; ++ } ++ ++ add->port = parport_get_port (port); ++ add->next = ppsc_port_list; ++ wmb (); ++ ppsc_port_list = add; ++} ++ ++static void ppsc_detach (struct parport *port) ++{ ++ /* Do nothing. We have a reference to the port already, so ++ * it won't go away. We'll clean up the port list when we ++ * unload. */ ++} ++ ++static struct parport_driver ppsc_driver = { ++ name: "ppscsi", ++ attach: ppsc_attach, ++ detach: ppsc_detach ++}; ++ ++void ppsc_make_map (char map[256], char key[5], int inv) ++{ ++ int i, j; ++ ++ for (i=0;i<256;i++) { ++ map[i] = 0; ++ for (j=0;j<5;j++) ++ map[i] = (map[i] << 1) | ((i & key[j]) != inv*key[j]); ++ } ++} ++ ++void ppsc_gen_setup (STT t[], int n, char *ss) ++{ ++ int j, k, sgn; ++ ++ k = 0; ++ for (j=0;jcontinuation = continuation; ++ pha->ready = ready; ++ if (timeout) ++ pha->timeout = jiffies + timeout; ++ else pha->timeout = pha->then + pha->tmo; ++ ++ if (!pha->nice && !pha->wq_active) { ++#ifdef HAVE_DISABLE_HLT ++ disable_hlt(); ++#endif ++ pha->wq_active = 1; ++ schedule_work (&pha->wq); ++ } ++ ++ if (!pha->timer_active) { ++ pha->timer_active = 1; ++ pha->timer.expires = jiffies + ((pha->nice>0)?(pha->nice-1):0); ++ add_timer(&pha->timer); ++ } ++ ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++} ++ ++static void ppsc_tq_int (void *data) ++{ ++ void (*con)(PHA *); ++ unsigned long flags; ++ PHA *pha = (PHA *)data; ++ ++ spin_lock_irqsave(&ppsc_spinlock,flags); ++ ++ con = pha->continuation; ++ ++#ifdef HAVE_DISABLE_HLT ++ enable_hlt(); ++#endif ++ ++ pha->wq_active = 0; ++ ++ if (!con) { ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++ return; ++ } ++ pha->timedout = time_after_eq (jiffies, pha->timeout); ++ if (!pha->ready || pha->ready(pha) || pha->timedout) { ++ pha->continuation = NULL; ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++ con(pha); ++ return; ++ } ++ ++#ifdef HAVE_DISABLE_HLT ++ disable_hlt(); ++#endif ++ ++ pha->wq_active = 1; ++ schedule_work (&pha->wq); ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++} ++ ++static void ppsc_timer_int (unsigned long data) ++{ ++ void (*con)(PHA *); ++ unsigned long flags; ++ PHA *pha = (PHA *)data; ++ ++ spin_lock_irqsave(&ppsc_spinlock,flags); ++ ++ con = pha->continuation; ++ pha->timer_active = 0; ++ if (!con) { ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++ return; ++ } ++ pha->timedout = time_after_eq (jiffies, pha->timeout); ++ if (!pha->ready || pha->ready(pha) || pha->timedout) { ++ pha->continuation = NULL; ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++ con(pha); ++ return; ++ } ++ pha->timer_active = 1; ++ pha->timer.expires = jiffies + ((pha->nice>0)?(pha->nice-1):0); ++ add_timer(&pha->timer); ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++} ++ ++static void ppsc_wake_up( void *p) ++{ ++ PHA *pha = (PHA *) p; ++ unsigned long flags; ++ void (*cont)(PHA *) = NULL; ++ ++ spin_lock_irqsave(&ppsc_spinlock,flags); ++ ++ if (pha->claim_cont && ++ !parport_claim(pha->pardev)) { ++ cont = pha->claim_cont; ++ pha->claim_cont = NULL; ++ pha->claimed = 1; ++ } ++ ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++ ++ wake_up(&(pha->parq)); ++ ++ if (cont) cont(pha); ++} ++ ++void ppsc_do_claimed (PHA *pha, void(*cont)(PHA *)) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ppsc_spinlock,flags); ++ ++ if (!parport_claim(pha->pardev)) { ++ pha->claimed = 1; ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++ cont(pha); ++ } else { ++ pha->claim_cont = cont; ++ spin_unlock_irqrestore(&ppsc_spinlock,flags); ++ } ++} ++ ++static void ppsc_claim (PHA *pha) ++{ ++ if (pha->claimed) return; ++ pha->claimed = 1; ++ ++ wait_event (pha->parq, !parport_claim (pha->pardev)); ++} ++ ++static void ppsc_unclaim (PHA *pha) ++{ ++ pha->claimed = 0; ++ parport_release(pha->pardev); ++} ++ ++static void ppsc_unregister_parport (PHA *pha) ++{ ++ parport_unregister_device(pha->pardev); ++ pha->pardev = NULL; ++} ++ ++static int ppsc_register_parport (PHA *pha, int verbose) ++{ ++ struct ppsc_port_list_struct *ports; ++ struct parport *port = NULL; ++ ++ ports = ppsc_port_list; ++ while((ports)&&(ports->port->number != pha->port)) ++ ports = ports->next; ++ if (ports) { ++ port = ports->port; ++ pha->pardev = parport_register_device(port, pha->device, ++ NULL, ppsc_wake_up, NULL, ++ 0, (void *)pha); ++ } else { ++ printk (KERN_DEBUG "%s: no such device: parport%d\n", ++ pha->device, pha->port); ++ return 1; ++ } ++ ++ if (!pha->pardev) { ++ printk (KERN_DEBUG "%s: couldn't register device\n", ++ pha->device); ++ return 1; ++ } ++ ++ init_waitqueue_head (&pha->parq); ++ ++ /* For now, cache the port base address. Won't need this ++ after transition to parport_xxx_yyy. */ ++ pha->port = port->base; ++ ++ if (verbose) ++ printk("%s: 0x%x is %s\n",pha->device,pha->port, ++ port->name); ++ pha->parname = port->name; ++ return 0; ++} ++ ++/* Here's the actual core SCSI stuff ... */ ++ ++#define PPSC_FAIL(err,msg) { ppsc_fail_command(pha,err,msg); return; } ++ ++static void ppsc_start (PHA *pha); ++static void ppsc_select_intr (PHA *pha); ++static void ppsc_engine (PHA *pha); ++static void ppsc_transfer (PHA *pha); ++static void ppsc_transfer_done (PHA *pha); ++static int ppsc_slow (PHA *pha, char *val); ++static void ppsc_slow_done (PHA *pha); ++static void ppsc_cleanup (PHA *pha); ++static void ppsc_fail_command (PHA *pha, int err_code, char *msg); ++static int ppsc_ready (PHA *pha); ++ ++/* synchronous interface is deprecated, but we maintain it for ++ internal use. It just starts an asynchronous command and waits ++ for it to complete. ++*/ ++ ++int ppsc_command (struct scsi_cmnd *cmd) ++{ ++ PHA *pha = (PHA *) cmd->device->host->hostdata[0]; ++ ++ pha->cur_cmd = cmd; ++ pha->done = NULL; ++ pha->then = jiffies; ++ ++ ppsc_do_claimed(pha,ppsc_start); ++ ++ while (pha->cur_cmd) scsi_sleep(1); ++ ++ return cmd->result; ++} ++ ++int ppsc_queuecommand (struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) ++{ ++ PHA *pha = (PHA *) cmd->device->host->hostdata[0]; ++ ++ if (pha->cur_cmd) { ++ printk("%s: Driver is busy\n",pha->device); ++ return 0; ++ } ++ ++ pha->cur_cmd = cmd; ++ pha->done = done; ++ pha->then = jiffies; ++ ++ ppsc_do_claimed(pha,ppsc_start); ++ ++ return 0; ++} ++ ++static void ppsc_arb_fail (PHA *pha) ++{ ++ PPSC_FAIL(DID_BUS_BUSY,"Arbitration failure"); ++} ++ ++static void ppsc_start (PHA *pha) ++{ ++ int k, r, b, bf; ++ struct scatterlist *p; ++ ++ pha->last_phase = PPSC_PH_NONE; ++ pha->return_code = (DID_OK << 16); ++ pha->overflow = 0; ++ pha->protocol_error = 0; ++ pha->cmd_count = 0; ++ ++ k = pha->cur_cmd->cmnd[0]; ++ bf = ppsc_bulk_map[k]; ++ ++ bf &= (!((1<cur_cmd->device->id) & pha->slow_targets)); ++ ++ r = pha->cur_cmd->use_sg; ++ if (r) { ++ b = 0; ++ p = (struct scatterlist *)pha->cur_cmd->request_buffer; ++ for (k=0;klength; ++ p++; ++ } ++ } else { ++ b = pha->cur_cmd->request_bufflen; ++ } ++ ++ bf &= (b > 127); ++ ++ if (V_DEBUG) ++ printk("%s: Target %d, bl=%d us=%d bf=%d cm=%x\n", ++ pha->device,pha->cur_cmd->device->id,b,r,bf,k); ++ ++ pha->bulk = bf; ++ pha->tlen = b; ++ ++ pha->proto->connect(pha); ++ ++ r = 0; ++ while (r++ < 5) { ++ k = pha->proto->select(pha,PPSC_INITIATOR,pha->cur_cmd->device->id); ++ if (k != -1) break; ++ udelay(200); ++ } ++ ++ if (k == -1) { ++ ppsc_set_intr(pha,ppsc_arb_fail,NULL,1); ++ return; ++ } ++ ++ ppsc_set_intr(pha,ppsc_select_intr,pha->proto->test_select, ++ PPSC_SELECT_TMO); ++} ++ ++static void ppsc_select_intr (PHA *pha) ++{ ++ if (!pha->proto->test_select(pha)) { ++ pha->return_code = DID_NO_CONNECT << 16; ++ ppsc_cleanup(pha); ++ return; ++ } ++ if (pha->proto->select_finish) ++ pha->proto->select_finish(pha); ++ ++ if (V_FULL) ++ printk("%s: selected target\n",pha->device); ++ ++ pha->timedout = 0; ++ ppsc_engine(pha); ++} ++ ++static void ppsc_update_sg (PHA *pha) ++{ ++ if ((!pha->cur_len) && pha->sg_count) { ++ pha->sg_count--; ++ pha->sg_list++; ++ pha->cur_buf = page_address(pha->sg_list->page) + pha->sg_list->offset; ++ pha->cur_len = pha->sg_list->length; ++ } ++} ++ ++static void ppsc_engine (PHA *pha) ++{ ++ int phase, i; ++ char *sb; ++ ++ while (1) { ++ if ((pha->last_phase == PPSC_PH_MSGIN) || ++ ((pha->last_phase == PPSC_PH_STAT) ++ && (!pha->proto->can_message))) { ++ pha->return_code |= (pha->status_byte & STATUS_MASK) ++ | (pha->message_byte << 8); ++ ppsc_cleanup(pha); ++ return; ++ } ++ ++ phase = pha->proto->get_bus_status(pha); ++ ++ if (pha->abort_flag) ++ PPSC_FAIL(DID_ABORT,"Command aborted"); ++ ++ if (pha->protocol_error) ++ PPSC_FAIL(DID_ERROR,"Adapter protocol failure"); ++ ++ if (!(phase & PPSC_BSY)) { ++ if (pha->last_phase == PPSC_PH_STAT) { ++ if (V_DEBUG) printk("%s: No msg phase ?\n", pha->device); ++ pha->return_code |= (pha->status_byte & STATUS_MASK); ++ ppsc_cleanup(pha); ++ return; ++ } ++ PPSC_FAIL(DID_ERROR,"Unexpected bus free"); ++ } ++ ++ if (!(phase & PPSC_REQ)) { ++ if (pha->timedout) ++ PPSC_FAIL(DID_TIME_OUT,"Pseudo-interrupt timeout"); ++ ppsc_set_intr(pha,ppsc_engine,ppsc_ready,0); ++ return; ++ } ++ ++ switch (phase) { ++ ++ case PPSC_PH_CMD: ++ ++ if (phase != pha->last_phase) { ++ if (pha->last_phase != PPSC_PH_NONE) ++ PPSC_FAIL(DID_ERROR,"Phase sequence error 1"); ++ pha->cmd_count = 0; ++ if (V_TRACE) { ++ printk("%s: Command to %d (%d): ", ++ pha->device, pha->cur_cmd->device->id, ++ pha->cur_cmd->cmd_len); ++ for (i=0;icur_cmd->cmd_len;i++) ++ printk("%2x ",pha->cur_cmd->cmnd[i]); ++ printk("\n"); ++ } ++ } ++ ++ pha->last_phase = phase; ++ ++ if (pha->cmd_count >= pha->cur_cmd->cmd_len) ++ PPSC_FAIL(DID_ERROR,"Command buffer overrun"); ++ ++ if (!ppsc_slow(pha,&(pha->cur_cmd->cmnd[pha->cmd_count++]))) ++ return; ++ ++ break; ++ ++ case PPSC_PH_READ: ++ case PPSC_PH_WRITE: ++ ++ if (phase != pha->last_phase) { ++ if (pha->last_phase != PPSC_PH_CMD) ++ PPSC_FAIL(DID_ERROR,"Phase sequence error 2"); ++ pha->data_dir = phase & PPSC_IO; ++ pha->data_count = 0; ++ ++ pha->sg_count = pha->cur_cmd->use_sg; ++ if (pha->sg_count) { ++ pha->sg_count--; ++ pha->sg_list = ++ (struct scatterlist *)pha->cur_cmd->request_buffer; ++ pha->cur_buf = page_address(pha->sg_list->page) + pha->sg_list->offset; ++ pha->cur_len = pha->sg_list->length; ++ } else { ++ pha->cur_buf = pha->cur_cmd->request_buffer; ++ pha->cur_len = pha->cur_cmd->request_bufflen; ++ } ++ ++ pha->last_phase = phase; ++ ++ } ++ ++ if ((pha->bulk) && (pha->cur_len > 0 )) { ++ pha->proto->start_block(pha,pha->data_dir); ++ ppsc_transfer(pha); ++ return; ++ } ++ ++ ppsc_update_sg(pha); ++ ++ if (!pha->cur_len) { ++ pha->cur_len = 1; ++ pha->cur_buf = (char *)&i; ++ i = 0x5a; ++ pha->overflow++; ++ } ++ ++ pha->cur_len--; ++ pha->data_count++; ++ ++ if (!ppsc_slow(pha,pha->cur_buf++)) return; ++ ++ break; ++ ++ case PPSC_PH_STAT: ++ ++ if ((pha->last_phase != PPSC_PH_CMD) && ++ (pha->last_phase != PPSC_PH_READ) && ++ (pha->last_phase != PPSC_PH_WRITE)) ++ PPSC_FAIL(DID_ERROR,"Phase sequence error 3"); ++ ++ if ((pha->last_phase != PPSC_PH_CMD) && ++ (V_DEBUG)) { ++ printk("%s: %s%s %d bytes\n", ++ pha->device, ++ pha->bulk?"":"slow ", ++ pha->data_dir?"read":"write", ++ pha->data_count); ++ ++ if (pha->cur_cmd->cmnd[0] == REQUEST_SENSE) { ++ ++ sb = (char *)pha->cur_cmd->request_buffer; ++ printk("%s: Sense key: %x ASC: %x ASCQ: %x\n", ++ pha->device, sb[2] & 0xff, ++ sb[12] & 0xff, sb[13] & 0xff); ++ } ++ } ++ ++ if (pha->overflow) ++ printk("%s: WARNING: data %s overran by %d/%d bytes\n", ++ pha->device,pha->data_dir?"read":"write", ++ pha->overflow,pha->data_count); ++ ++ pha->last_phase = phase; ++ ++ if (!ppsc_slow(pha,&pha->status_byte)) return; ++ ++ break; ++ ++ case PPSC_PH_MSGIN: ++ ++ if (pha->last_phase != PPSC_PH_STAT) ++ PPSC_FAIL(DID_ERROR,"Phase sequence error 4"); ++ ++ pha->last_phase = phase; ++ ++ if (V_FULL) ++ printk("%s: status = %x\n",pha->device,pha->status_byte); ++ ++ if (!ppsc_slow(pha,&pha->message_byte)) return; ++ ++ break; ++ ++ default: ++ ++ PPSC_FAIL(DID_ERROR,"Unexpected bus phase"); ++ ++ } ++ } ++} ++ ++static void ppsc_transfer (PHA *pha) ++{ ++ int i, j; ++ ++ if (pha->timedout) ++ PPSC_FAIL(DID_TIME_OUT,"PDMA timeout"); ++ ++ while(1) { ++ ++ if (!(j=pha->proto->transfer_ready(pha))) { ++ ppsc_set_intr(pha,ppsc_transfer, ++ pha->proto->transfer_ready,0); ++ return; ++ } ++ ++ if (j < 0) { ++ if (V_DEBUG) ++ printk("%s: short transfer\n",pha->device); ++ ppsc_set_intr(pha,ppsc_transfer_done, ++ pha->proto->transfer_done,0); ++ return; ++ } ++ ++ i = pha->proto->transfer_block(pha,pha->cur_buf, ++ pha->cur_len,pha->data_dir); ++ ++ if (V_FULL) ++ printk("%s: Fragment %d\n",pha->device,i); ++ ++ if ((i < 0) || (i > pha->cur_len)) ++ PPSC_FAIL(DID_ERROR,"Block transfer error"); ++ ++ pha->cur_len -= i; ++ pha->cur_buf += i; ++ pha->data_count += i; ++ ++ ppsc_update_sg(pha); ++ ++ if (pha->cur_len == 0 ) { ++ ppsc_set_intr(pha,ppsc_transfer_done, ++ pha->proto->transfer_done,0); ++ return; ++ } ++ } ++} ++ ++static void ppsc_transfer_done (PHA *pha) ++{ ++ if (pha->timedout) PPSC_FAIL(DID_TIME_OUT,"PDMA done timeout"); ++ ++ pha->proto->end_block(pha,pha->data_dir); ++ ppsc_engine(pha); ++} ++ ++static int ppsc_slow (PHA *pha, char *val) ++{ ++ int k; ++ ++ pha->proto->slow_start(pha,val); ++ ++ k = 0; ++ while (k++ < PPSC_SLOW_LOOPS) ++ if (pha->proto->slow_done(pha)) { ++ pha->proto->slow_end(pha); ++ return 1; ++ } ++ ++ ppsc_set_intr(pha,ppsc_slow_done,pha->proto->slow_done,0); ++ return 0; ++} ++ ++static void ppsc_slow_done (PHA *pha) ++{ ++ int k; ++ ++ if (pha->timedout) PPSC_FAIL(DID_TIME_OUT,"PIO timeout"); ++ ++ pha->proto->slow_end(pha); ++ ++ k = 0; ++ while (k++ < PPSC_SLOW_LOOPS) ++ if (ppsc_ready(pha)) break; ++ ++ ppsc_engine(pha); ++} ++ ++static void ppsc_try_again (unsigned long data ) ++{ ++ PHA *pha = (PHA *)data; ++ ++ ppsc_do_claimed(pha,ppsc_start); ++} ++ ++static void ppsc_cleanup (PHA *pha) ++{ ++ struct scsi_cmnd *cmd; ++ void (*done)(struct scsi_cmnd *); ++ unsigned long saved_flags; ++ ++ pha->tot_bytes += pha->data_count; ++ ++ cmd = pha->cur_cmd; ++ done = pha->done; ++ cmd->result = pha->return_code; ++ pha->cur_cmd = 0; ++ ++ pha->proto->deselect(pha); ++ pha->proto->disconnect(pha); ++ ++ if (V_FULL) printk("%s: releasing parport\n",pha->device); ++ ++ ppsc_unclaim(pha); ++ ++ if (pha->abort_flag) { ++ ++ if (V_DEBUG) printk("%s: command aborted !\n",pha->device); ++ ++ return; /* kill the thread */ ++ } ++ ++ if (V_DEBUG) ++ printk("%s: Command status %08x last phase %o\n", ++ pha->device,cmd->result,pha->last_phase); ++ ++ if (status_byte(pha->return_code) == BUSY) { ++ ++ pha->cur_cmd = cmd; ++ ++ if (V_FULL) ++ printk("%s: BUSY, sleeping before retry ...\n", ++ pha->device); ++ ++ init_timer (&pha->sleeper); ++ pha->sleeper.data = (unsigned long) pha; ++ pha->sleeper.function = ppsc_try_again; ++ pha->sleeper.expires = jiffies + PPSC_BUSY_SNOOZE; ++ add_timer(&pha->sleeper); ++ ++ return; ++ ++ } ++ ++ pha->tot_cmds++; ++ ++ if ((cmd->cmnd[0] != REQUEST_SENSE) && ++ (status_byte(pha->return_code) == CHECK_CONDITION)) { ++ ++ if (V_FULL) ++ printk("%s: Requesting sense data\n",pha->device); ++ ++ cmd->cmnd[0] = REQUEST_SENSE; ++ cmd->cmnd[1] &= 0xe0; ++ cmd->cmnd[2] = 0; ++ cmd->cmnd[3] = 0; ++ cmd->cmnd[4] = sizeof(cmd->sense_buffer); ++ cmd->cmnd[5] = 0; ++ cmd->cmd_len = 6; ++ cmd->use_sg = 0; ++ cmd->request_buffer = (char *) cmd->sense_buffer; ++ cmd->request_bufflen = sizeof(cmd->sense_buffer); ++ ++ pha->cur_cmd = cmd; ++ ppsc_do_claimed(pha,ppsc_start); ++ ++ return; ++ } ++ ++ if (done) { ++ ++ spin_lock_irqsave(pha->host_ptr->host_lock,saved_flags); ++ done(cmd); ++ spin_unlock_irqrestore(pha->host_ptr->host_lock,saved_flags); ++ ++ } ++ ++} ++ ++static void ppsc_fail_command (PHA *pha, int err_code, char *msg) ++{ ++ int bs; ++ ++ pha->tot_errs++; ++ ++ bs = pha->proto->get_bus_status(pha); ++ ++ pha->return_code = err_code << 16; ++ if (!pha->quiet) ++ printk("%s: %s, bs=%o cb=%d db=%d bu=%d sg=%d " ++ "rd=%d lp=%o pe=%d cc=%d\n", ++ pha->device, msg, bs, ++ pha->cmd_count, pha->data_count, ++ pha->bulk, pha->sg_count, pha->data_dir, ++ pha->last_phase, pha->protocol_error, pha->tot_cmds); ++ ++ ppsc_cleanup(pha); ++} ++ ++static int ppsc_ready (PHA *pha) ++{ ++ int bs; ++ ++ if (pha->abort_flag || pha->protocol_error) return 1; ++ bs = pha->proto->get_bus_status(pha); ++ ++ if ( (bs & (PPSC_REQ|PPSC_BSY)) != PPSC_BSY) return 1; ++ ++ return 0; ++} ++ ++int ppsc_abort (struct scsi_cmnd * cmd) ++{ ++ PHA *pha = (PHA *)cmd->device->host->hostdata[0]; ++ ++ printk("%s: Command abort not supported\n",pha->device); ++ return FAILED; ++} ++ ++static void ppsc_reset_pha (PHA *pha) ++{ ++ if (!pha->proto->reset_bus) { ++ printk("%s: No reset method available\n",pha->device); ++ return; ++ } ++ ++ ppsc_claim(pha); ++ pha->proto->connect(pha); ++ pha->proto->reset_bus(pha); ++ scsi_sleep(4*HZ); ++ pha->proto->disconnect(pha); ++ ppsc_unclaim(pha); ++ ++ if (!pha->quiet) printk("%s: Bus reset\n",pha->device); ++} ++ ++int ppsc_reset (struct scsi_cmnd * cmd) ++{ ++ PHA *pha = (PHA *)cmd->device->host->hostdata[0]; ++ int k = 0; ++ ++ if (!pha->proto->reset_bus) ++ return FAILED; ++ ++ if (pha->cur_cmd) ++ pha->abort_flag = PPSC_DO_RESET; ++ ++ while (pha->cur_cmd && (k < PPSC_RESET_TMO)) { ++ scsi_sleep(HZ/10); ++ k += HZ/10; ++ } ++ ++ if (pha->cur_cmd) { ++ printk("%s: Driver won't give up for reset\n",pha->device); ++ return FAILED; ++ } ++ ++ ppsc_reset_pha(pha); ++ ++ return SUCCESS; ++} ++ ++#define PROCIN(n,var) \ ++ if ((length>n+1)&&(strncmp(buffer,#var"=",n+1)==0)) { \ ++ pha->var = simple_strtoul(buffer+n+1,NULL,0); \ ++ return length; \ ++ } ++ ++#define PROCOUT(fmt,val) len+=sprintf(buffer+len,fmt"\n",val); ++ ++int ppsc_proc_info(struct Scsi_Host *p, char *buffer, char **start, off_t offset, ++ int length, int inout) ++{ ++ int len = 0; ++ PHA *pha; ++ ++ if (!p) return 0; /* should never happen */ ++ pha = (PHA *)p->hostdata[0]; ++ ++ if (inout) { ++ ++ PROCIN(4,mode); ++ PROCIN(5,delay); ++ PROCIN(7,verbose); ++ PROCIN(10,abort_flag); ++ PROCIN(4,nice); ++ ++ return (-EINVAL); ++ } ++ ++ PROCOUT("ident: %s",pha->ident); ++ PROCOUT("base port: 0x%03x",pha->port); ++ PROCOUT("mode: %d",pha->mode); ++ if (pha->proto->mode_names) ++ PROCOUT("mode name: %s",pha->proto->mode_names[pha->mode]); ++ PROCOUT("delay: %d",pha->delay); ++ PROCOUT("nice: %d",pha->nice); ++ PROCOUT("verbose: %d",pha->verbose); ++ PROCOUT("quiet: %d",pha->quiet); ++ PROCOUT("tot_cmds: %d",pha->tot_cmds); ++ PROCOUT("tot_bytes: %ld",pha->tot_bytes); ++ PROCOUT("tot_errs: %d",pha->tot_errs); ++ ++ if (pha->pardev) { ++ PROCOUT("parport device: %s",pha->parname); ++ PROCOUT("claimed: %d",pha->claimed); ++ } ++ if (V_DEBUG) { ++ PROCOUT("then: %ld",pha->then); ++ PROCOUT("timeout: %ld",pha->timeout); ++ PROCOUT("now: %ld",jiffies); ++ PROCOUT("timer active: %d",pha->timer_active); ++ PROCOUT("wq_active: %d",pha->wq_active); ++ PROCOUT("abort_flag: %d",pha->abort_flag); ++ PROCOUT("return_code: %08x",pha->return_code); ++ PROCOUT("last_phase: %o",pha->last_phase); ++ PROCOUT("cmd_count: %d",pha->cmd_count); ++ PROCOUT("data_count: %d",pha->data_count); ++ PROCOUT("data_dir: %d",pha->data_dir); ++ PROCOUT("bulk: %d",pha->bulk); ++ PROCOUT("tlen: %d",pha->tlen); ++ PROCOUT("overflow: %d",pha->overflow); ++ } ++ ++ if (offset > len) return 0; ++ ++ *start = buffer+offset; len -= offset; ++ if (len > length) len = length; ++ return len; ++} ++ ++int ppsc_biosparam (struct scsi_device * sdev, struct block_device *bdev, sector_t capacity, int ip[]) ++{ ++ ip[0] = 0x40; ++ ip[1] = 0x20; ++ ip[2] = (capacity +1) / (ip[0] * ip[1]); ++ if (ip[2] > 1024) { ++ ip[0] = 0xff; ++ ip[1] = 0x3f; ++ ip[2] = (capacity +1) / (ip[0] * ip[1]); ++ if (ip[2] > 1023) ++ ip[2] = 1023; ++ } ++ return 0; ++} ++ ++static int ppsc_inquire (PHA *pha, int target, char *buf) ++{ ++ char inq[6] = {0x12,0,0,0,36,0}; ++ int i; ++ struct scsi_cmnd cmd; ++ struct scsi_device dev; ++ ++ dev.host = pha->host_ptr; ++ dev.id = target; ++ cmd.device = &dev; ++ cmd.cmd_len = 6; ++ for (i=0;i<6;i++) cmd.cmnd[i] = inq[i]; ++ cmd.use_sg = 0; ++ cmd.request_buffer = buf; ++ cmd.request_bufflen = 36; ++ ++ return ppsc_command(&cmd); ++} ++ ++static void ppsc_test_mode (PHA *pha, int mode) ++{ ++ int i, t, s, e, f, g, ok, old_mode; ++ char ibuf[38]; ++ ++ if ((mode >= pha->proto->epp_first) && ++ !(pha->pardev->port->modes & PARPORT_MODE_EPP)) ++ return; ++ ++ old_mode = pha->mode; ++ pha->mode = mode; ++ ++ e = -1; f = -1; g = 0; ++ ++ if (pha->proto->test_proto) { ++ ppsc_claim(pha); ++ e = pha->proto->test_proto(pha); ++ ppsc_unclaim(pha); ++ } ++ ++ if (e <= 0) { ++ f = 0; ++ for (t=0;t<8;t++) { ++ s = ppsc_inquire(pha,t,ibuf); ++ if (s == DID_NO_CONNECT << 16) continue; ++ if (s) { ++ f++; ++ break; ++ } ++ if (V_FULL) { ++ for (i=0;i<36;i++) ++ if ((ibuf[i] < ' ') || (ibuf[i] >= '~')) ibuf[i] = '.'; ++ ibuf[36] = 0; ++ printk("%s: port 0x%x mode %d targ %d: %s\n", ++ pha->device,pha->port,mode,t,ibuf); ++ } ++ g++; ++ } ++ if (f) ppsc_reset_pha(pha); ++ } ++ ++ ok = (e<=0) && (f == 0); ++ ++ if (!ok) pha->mode = old_mode; ++ ++ if (V_PROBE) printk("%s: port 0x%3x mode %d test %s (%d,%d,%d)\n", ++ pha->device,pha->port,mode,ok?"passed":"failed",e,f,g); ++} ++ ++ ++int ppsc_release_pha (PHA *pha) ++{ ++ if (pha->proto->release) pha->proto->release(pha); ++ ++ ppsc_unregister_parport(pha); ++ ++ /* MOD_DEC_USE_COUNT; */ ++ ++ return 0; ++} ++ ++ ++int ppsc_detect (PSP *proto, Scsi_Host_Template *tpnt, int verbose) ++{ ++ int i, m, p, d, n, s, z; ++ struct ppsc_port_list_struct *next_port = NULL; /* shut gcc up */ ++ int user_specified = 1; ++ PHA *pha; ++ int host_count = 0; ++ struct Scsi_Host *hreg; ++ ++ m = 0; ++ for (i=0;i<4;i++) if ((*proto->params[i])[PPSC_PARM_PORT] != -1) m++; ++ ++ if (!m) { ++ /* Just take parports from the list as they come. */ ++ next_port = ppsc_port_list; ++ user_specified = 0; ++ } ++ ++ tpnt->this_id = PPSC_INITIATOR; ++ ++ for (i=0;i<4;i++) { ++ if (!user_specified) { ++ if (!next_port) ++ break; ++ ++ p = next_port->port->number; ++ next_port = next_port->next; ++ } ++ else { ++ p = (*proto->params[i])[PPSC_PARM_PORT]; ++ if (p < 0) ++ continue; ++ } ++ ++ m = (*proto->params[i])[PPSC_PARM_MODE]; ++ n = (*proto->params[i])[PPSC_PARM_NICE]; ++ if (n == -1) n = PPSC_DEF_NICE; ++ d = (*proto->params[i])[PPSC_PARM_DLY]; ++ if (d == -1) d = proto->default_delay; ++ s = (*proto->params[i])[PPSC_PARM_SGTS]; ++ if (s == -1) s = proto->default_sg_tablesize; ++ z = (*proto->params[i])[PPSC_PARM_SLOW]; ++ if (z == -1) z = 0; ++ ++ /* MOD_INC_USE_COUNT; */ ++ ++ pha = &(((*proto->hosts)[i])); ++ ++ pha->proto = proto; ++ ++ pha->port = p; ++ pha->delay = d; ++ pha->nice = n; ++ ++ d = sizeof(pha->device)-3; ++ p = strlen(tpnt->name); ++ if (p > d) p = d; ++ for (n=0;ndevice[n] = tpnt->name[n]; ++ pha->device[p] = '.'; ++ pha->device[p+1] = '0' + i; ++ pha->device[p+2] = 0; ++ ++ INIT_WORK(&pha->wq, ppsc_tq_int, pha); ++ ++ init_timer (&pha->timer); ++ pha->timer.data = (unsigned long) pha; ++ pha->timer.function = ppsc_timer_int; ++ ++ init_waitqueue_head (&pha->parq); ++ pha->pardev = NULL; ++ pha->claimed = 0; ++ pha->claim_cont = NULL; ++ pha->timer_active = 0; ++ pha->wq_active = 0; ++ pha->timedout = 0; ++ ++ pha->cur_cmd = NULL; ++ pha->done = NULL; ++ pha->abort_flag = 0; ++ pha->protocol_error = 0; ++ pha->tot_errs = 0; ++ pha->tot_cmds = 0; ++ pha->tot_bytes = 0; ++ ++ for (n=0;n<8;n++) pha->private[n] = 0; ++ ++ pha->slow_targets = z; ++ ++ if (ppsc_register_parport(pha,verbose)) { ++ /* MOD_DEC_USE_COUNT; */ ++ continue; ++ } ++ ++ pha->proto->init(pha); ++ ++ pha->verbose = verbose; ++ pha->quiet = 1; /* no errors until probe over */ ++ if (V_FULL) pha->quiet = 0; /* unless we want them ... */ ++ ++ pha->tmo = PPSC_PROBE_TMO; ++ ++ hreg = scsi_register(tpnt,sizeof(PHA*)); ++ hreg->dma_channel = -1; ++ hreg->n_io_port = 0; ++ hreg->unique_id = (int) pha; /* What should we put in here??? */ ++ hreg->sg_tablesize = s; ++ hreg->hostdata[0]=(unsigned long)pha; /* Will be our pointer */ ++ ++ pha->host_ptr = hreg; ++ ++ pha->mode = -1; ++ ++ if (m == -1) for (m=0;mnum_modes;m++) ++ ppsc_test_mode(pha,m); ++ else ppsc_test_mode(pha,m); ++ ++ if (pha->mode != -1) { ++ ++ pha->quiet = 0; /* enable PPSC_FAIL msgs */ ++ pha->tmo = PPSC_GEN_TMO; ++ host_count++; ++ ++ printk("%s: %s at 0x%3x mode %d (%s) dly %d nice %d sg %d\n", ++ pha->device, ++ pha->ident, ++ pha->port, ++ pha->mode, ++ (pha->proto->mode_names)? ++ pha->proto->mode_names[pha->mode]:"", ++ pha->delay, ++ pha->nice, ++ hreg->sg_tablesize); ++ ++ } else { ++ ++ scsi_unregister(hreg); ++ ppsc_release_pha(pha); ++ ++ } ++ } ++ return host_count; ++} ++ ++int ppsc_release (struct Scsi_Host *host) ++{ ++ PHA *pha = (PHA *) host->hostdata[0]; ++ ++ return ppsc_release_pha(pha); ++} ++ ++int ppsc_initialise (void) ++{ ++ int i; ++ ++ for (i=0;i<256;i++) ppsc_bulk_map[i] = 0; ++ ++/* commands marked in this map will use pseudo-DMA transfers, while ++ the rest will use the slow handshaking. ++*/ ++ ++ ppsc_bulk_map[READ_6] = 1; ++ ppsc_bulk_map[READ_10] = 1; ++ ppsc_bulk_map[READ_BUFFER] = 1; ++ ppsc_bulk_map[WRITE_6] = 1; ++ ppsc_bulk_map[WRITE_10] = 1; ++ ppsc_bulk_map[WRITE_BUFFER] = 1; ++ ++ if (parport_register_driver (&ppsc_driver)) { ++ printk (KERN_WARNING "ppscsi: couldn't register driver\n"); ++ return -EIO; ++ } ++ ++ printk("ppSCSI %s (%s) installed\n",PPSC_VERSION,PPSC_H_VERSION); ++ return 0; ++} ++ ++#ifdef MODULE ++ ++int init_module (void) ++{ ++ return ppsc_initialise(); ++} ++ ++void cleanup_module (void) ++{ ++ struct ppsc_port_list_struct *ports, *next; ++ parport_unregister_driver (&ppsc_driver); ++ for (ports = ppsc_port_list; ports; ports = next) { ++ next = ports->next; ++ parport_put_port (ports->port); ++ kfree (ports); ++ } ++} ++ ++MODULE_LICENSE("GPL"); ++ ++#endif ++ ++EXPORT_SYMBOL(ppsc_make_map); ++EXPORT_SYMBOL(ppsc_queuecommand); ++EXPORT_SYMBOL(ppsc_abort); ++EXPORT_SYMBOL(ppsc_reset); ++EXPORT_SYMBOL(ppsc_proc_info); ++EXPORT_SYMBOL(ppsc_biosparam); ++EXPORT_SYMBOL(ppsc_detect); ++EXPORT_SYMBOL(ppsc_release); ++ ++/* end of ppscsi.c */ +--- /dev/null ++++ b/drivers/scsi/ppscsi.h +@@ -0,0 +1,356 @@ ++#ifndef _PPSC_H ++#define _PPSC_H ++ ++/* ++ ppscsi.h (c) 1999 Grant Guenther ++ Under the terms of the GNU public license. ++ ++ This header file defines a common interface for constructing ++ low-level SCSI drivers for parallel port SCSI adapters. ++ ++*/ ++ ++#define PPSC_H_VERSION "0.92" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "scsi.h" ++#include "hosts.h" ++ ++ ++/* ppscsi global functions */ ++ ++extern void ppsc_make_map( char map[256], char key[5], int inv); ++ ++extern int ppsc_proc_info(struct Scsi_Host *, char *,char **,off_t,int,int); ++extern int ppsc_command(struct scsi_cmnd *); ++extern int ppsc_queuecommand(struct scsi_cmnd *, void (* done)(struct scsi_cmnd *)); ++extern int ppsc_abort(struct scsi_cmnd *); ++extern int ppsc_reset(struct scsi_cmnd *); ++extern int ppsc_biosparam(struct scsi_device *, struct block_device *, sector_t capacity, int[]); ++extern int ppsc_release(struct Scsi_Host *); ++ ++#ifndef PPSC_BASE ++ ++/* imports for hosts.c */ ++ ++#ifdef CONFIG_PPSCSI_T348 ++extern int t348_detect( Scsi_Host_Template *); ++#endif ++ ++#ifdef CONFIG_PPSCSI_T358 ++extern int t358_detect( Scsi_Host_Template *); ++#endif ++ ++#ifdef CONFIG_PPSCSI_ONSCSI ++extern int onscsi_detect( Scsi_Host_Template *); ++#endif ++ ++#ifdef CONFIG_PPSCSI_EPST ++extern int epst_detect( Scsi_Host_Template *); ++#endif ++ ++#ifdef CONFIG_PPSCSI_EPSA2 ++extern int epsa2_detect( Scsi_Host_Template *); ++#endif ++ ++#ifdef CONFIG_PPSCSI_VPI0 ++extern int vpi0_detect( Scsi_Host_Template *); ++#endif ++ ++#ifdef CONFIG_PPSCSI_SPARCSI ++extern int sparcsi_detect( Scsi_Host_Template *); ++#endif ++ ++#endif ++ ++#define PPSC_TEMPLATE(proto){ \ ++ .name = #proto, \ ++ .detect = proto##_detect, \ ++ .release = ppsc_release, \ ++ .proc_name = #proto, \ ++ .proc_info = ppsc_proc_info, \ ++ .queuecommand = ppsc_queuecommand, \ ++ .eh_abort_handler = ppsc_abort, \ ++ .eh_bus_reset_handler = ppsc_reset, \ ++ .eh_host_reset_handler = ppsc_reset, \ ++ .bios_param = ppsc_biosparam, \ ++ .can_queue = 1, \ ++ .sg_tablesize = SG_NONE, \ ++ .cmd_per_lun = 1, \ ++ .use_clustering = DISABLE_CLUSTERING \ ++} ++ ++/* types used by the actual driver modules */ ++ ++#ifdef PPSC_BASE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++struct setup_tab_t { ++ ++ char *tag; /* variable name */ ++ int size; /* number of elements in array */ ++ int *iv; /* pointer to variable */ ++}; ++ ++typedef struct setup_tab_t STT; ++ ++extern void ppsc_gen_setup( STT t[], int n, char *ss ); ++ ++typedef struct ppsc_host_adapter PHA; ++ ++struct ppsc_host_adapter { ++ ++ char ident[80]; /* Adapter name and version info */ ++ ++ char device[12]; /* device name for messages */ ++ ++ struct Scsi_Host *host_ptr; /* SCSI host structure */ ++ struct ppsc_protocol *proto; /* adapter protocol */ ++ ++ int port; /* parallel port base address */ ++ int mode; /* transfer mode in use */ ++ int delay; /* parallel port settling delay */ ++ int saved_r0; /* saved port state */ ++ int saved_r2; /* saved port state */ ++ ++ int reserved; /* number of ports reserved */ ++ int tmo; /* default command timeout */ ++ int verbose; /* logging level */ ++ int quiet; /* do not log PPSC_FAIL msgs */ ++ ++ int slow_targets; /* bit mask for disabling block mode */ ++ ++ wait_queue_head_t parq; /* semaphore for parport sharing */ ++ struct pardevice *pardev; /* pointer to pardevice */ ++ const char *parname; /* parport name */ ++ int claimed; /* parport has been claimed */ ++ void (*claim_cont)(PHA *); /* continuation for parport wait */ ++ ++ void (*continuation)(PHA *); /* next "interrupt" handler */ ++ int (*ready)(PHA *); /* current ready test */ ++ unsigned long then; /* jiffies at start of last wait */ ++ unsigned long timeout; /* when to timeout this wait */ ++ int timedout; /* timeout was seen */ ++ int timer_active; /* we're using a timer */ ++ int wq_active; /* we have a task queued */ ++ int nice; /* tune the CPU load */ ++ struct timer_list timer; /* timer queue element */ ++ struct work_struct wq; /* task queue element */ ++ ++ int private[8]; /* for the protocol layer, if needed */ ++ char *priv_ptr; ++ int priv_flag; ++ ++ struct scsi_cmnd *cur_cmd; /* current command on this host */ ++ void (*done)(struct scsi_cmnd *); /* current "done" function */ ++ ++ int overflow; /* excess bytes transferred */ ++ int bulk; /* should we use block mode ? */ ++ int tlen; /* total transfer length */ ++ int abort_flag; /* abort=1 reset=2 requested */ ++ int return_code; /* build return value here */ ++ ++ struct scatterlist *sg_list; /* current fragment, if any */ ++ int sg_count; /* remaining fragments */ ++ char *cur_buf; /* current buffer pointer */ ++ int cur_len; /* remaining bytes in buffer */ ++ ++ struct timer_list sleeper; /* for BUSY handling */ ++ ++ int last_phase; /* to detect phase changes */ ++ char message_byte; ++ char status_byte; ++ ++ int cmd_count; /* bytes of command transfered */ ++ int data_count; /* bytes of data transferred */ ++ int data_dir; /* direction of transfer */ ++ ++ int tot_cmds; /* number of commands processed */ ++ long tot_bytes; /* total bytes transferred */ ++ int tot_errs; /* number of failed commands */ ++ ++ int protocol_error; /* Some protocols can set this ++ != zero to signal a fatal error ++ we report it and expect to die ++ */ ++}; ++ ++/* constants for 'verbose' */ ++ ++#define PPSC_VERB_NORMAL 0 ++#define PPSC_VERB_PROBE 1 ++#define PPSC_VERB_TRACE 2 ++#define PPSC_VERB_DEBUG 3 ++#define PPSC_VERB_FULL 4 ++ ++#define V_PROBE (pha->verbose >= PPSC_VERB_PROBE) ++#define V_TRACE (pha->verbose >= PPSC_VERB_TRACE) ++#define V_DEBUG (pha->verbose >= PPSC_VERB_DEBUG) ++#define V_FULL (pha->verbose >= PPSC_VERB_FULL) ++ ++/* constants for abort_flag */ ++ ++#define PPSC_DO_ABORT 1 ++#define PPSC_DO_RESET 2 ++ ++ ++struct ppsc_protocol { ++ ++ int (*params[4])[8]; /* hostN tuning parameters */ ++ ++ PHA (*hosts)[4]; /* actual PHA structs */ ++ ++ int num_modes; /* number of modes*/ ++ int epp_first; /* modes >= this use 8 ports */ ++ int default_delay; /* delay parameter if not specified */ ++ ++ int can_message; /* adapter can send/rcv SCSI msgs */ ++ int default_sg_tablesize; /* sg_tablesize if not specified */ ++ ++ char **mode_names; /* printable names of comm. modes */ ++ ++/* first two functions are NOT called with the port claimed. */ ++ ++ void (*init)(PHA *); /* (pha) ++ protocol initialisation ++ should fill in pha->ident */ ++ void (*release)(PHA *); /* (pha) optional ++ protocol no longer in use */ ++ void (*connect)(PHA *); /* (pha) ++ connect to adapter */ ++ void (*disconnect)(PHA *); /* (pha) ++ release adapter */ ++ int (*test_proto)(PHA *); /* (pha) optional ++ test protocol in current settings, ++ returns error count */ ++ int (*select)(PHA *,int,int); /* (pha,initiator,target) ++ start artibration and selection ++ 0 = OK, -1 = arb. failed */ ++ int (*test_select)(PHA *); /* (pha) ++ test for selection to complete ++ 1 = OK, 0 try again */ ++ void (*select_finish)(PHA *); /* (pha) optional ++ called after successful select */ ++ void (*deselect)(PHA *); /* (pha) ++ release SCSI bus */ ++ int (*get_bus_status)(PHA *); /* (pha) ++ return (REQ,BSY,MSG,C/D,I/O) */ ++ void (*slow_start)(PHA *,char *); /* (pha,byte) ++ start transfer of one byte using ++ explicit handshaking */ ++ int (*slow_done)(PHA *); /* (pha) ++ has the device acked the byte ? */ ++ void (*slow_end)(PHA *); /* (pha) ++ shut down the slow transfer */ ++ void (*start_block)(PHA *,int); /* (pha,read) ++ start data transfer */ ++ int (*transfer_block)(PHA *,char *,int,int); ++ /* (pha,buf,len,read) ++ transfer as much as possible and ++ return count of bytes ++ can return -1 if error detected */ ++ int (*transfer_ready)(PHA *pha);/* (pha) ++ can we go again yet ? ++ >0 = yes, 0 = try again, -1 = done */ ++ int (*transfer_done)(PHA *pha); /* (pha) ++ has all data been flushed ? ++ 1 = yes, 0 = try again */ ++ void (*end_block)(PHA *,int); /* (pha,read) ++ shut down block transfer */ ++ void (*reset_bus)(PHA *); /* (pha) optional ++ reset SCSI bus if possible */ ++ ++}; ++ ++/* constants for the params array */ ++ ++#define PPSC_PARM_PORT 0 ++#define PPSC_PARM_MODE 1 ++#define PPSC_PARM_DLY 2 ++#define PPSC_PARM_NICE 3 ++#define PPSC_PARM_SGTS 4 ++#define PPSC_PARM_SLOW 5 ++ ++/* constants for get_bus_status */ ++ ++#define PPSC_REQ 16 ++#define PPSC_BSY 8 ++#define PPSC_MSG 4 ++#define PPSC_CD 2 ++#define PPSC_IO 1 ++ ++/* phases */ ++ ++#define PPSC_PH_NONE 0 ++#define PPSC_PH_WRITE (PPSC_REQ|PPSC_BSY) ++#define PPSC_PH_READ (PPSC_PH_WRITE|PPSC_IO) ++#define PPSC_PH_CMD (PPSC_PH_WRITE|PPSC_CD) ++#define PPSC_PH_STAT (PPSC_PH_READ|PPSC_CD) ++#define PPSC_PH_MSGIN (PPSC_PH_STAT|PPSC_MSG) ++ ++typedef struct ppsc_protocol PSP; ++ ++extern int ppsc_detect( PSP *, Scsi_Host_Template *, int); ++ ++#ifdef PPSC_HA_MODULE ++ ++static int verbose = PPSC_VERB_NORMAL; ++ ++static int host0[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; ++static int host1[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; ++static int host2[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; ++static int host3[8] = {-1,-1,-1,-1,-1,-1,-1,-1}; ++ ++#ifndef MODULE ++ ++static STT stt[4] = { {"host0",8,host0}, ++ {"host1",8,host1}, ++ {"host2",8,host2}, ++ {"host3",8,host3} }; ++#endif ++ ++MODULE_PARM(host0,"1-8i"); ++MODULE_PARM(host1,"1-8i"); ++MODULE_PARM(host2,"1-8i"); ++MODULE_PARM(host3,"1-8i"); ++MODULE_PARM(verbose,"i"); ++ ++static struct ppsc_host_adapter host_structs[4]; ++ ++#define delay_p (pha->delay?udelay(pha->delay):0) ++#define out_p(offs,byte) outb(byte,pha->port+offs); delay_p; ++#define in_p(offs) (delay_p,inb(pha->port+offs)) ++ ++#define w0(byte) do {out_p(0,byte);} while (0) ++#define r0() (in_p(0) & 0xff) ++#define w1(byte) do {out_p(1,byte);} while (0) ++#define r1() (in_p(1) & 0xff) ++#define w2(byte) do {out_p(2,byte);} while (0) ++#define r2() (in_p(2) & 0xff) ++#define w3(byte) do {out_p(3,byte);} while (0) ++#define w4(byte) do {out_p(4,byte);} while (0) ++#define r4() (in_p(4) & 0xff) ++#define w4w(data) do {outw(data,pha->port+4); delay_p;} while (0) ++#define w4l(data) do {outl(data,pha->port+4); delay_p;} while (0) ++#define r4w() (delay_p,inw(pha->port+4)&0xffff) ++#define r4l() (delay_p,inl(pha->port+4)&0xffffffff) ++ ++#endif /* PPSC_HA_MODULE */ ++#endif /* PPSC_BASE */ ++#endif /* _PPSC_H */ ++ ++/* end of ppscsi.h */ ++ +--- /dev/null ++++ b/drivers/scsi/onscsi.c +@@ -0,0 +1,544 @@ ++/* ++ onscsi.c (c) 1999 Grant Guenther ++ ++ This is the ppSCSI protocol module for the OnSpec 90c26 ++ in its SCSI adapter mode. ++*/ ++ ++#define ONSCSI_VERSION "0.91" ++ ++#define PPSC_BASE ++#define PPSC_HA_MODULE ++ ++#include "ppscsi.h" ++ ++#define ONSCSI_REP_COUNT 256 ++ ++#define TOGL pha->private[0] ++ ++static char onscsi_map[256]; /* status bits permutation */ ++ ++static void onscsi_init (PHA *pha) ++{ ++ ++/* { REQ, BSY, MSG, CD, IO} */ ++ ++ char key[5] = {0x10,0x01,0x20,0x40,0x80}; ++ ++ ppsc_make_map(onscsi_map,key,0); ++ sprintf(pha->ident,"onscsi %s (%s), OnSpec 90c26", ++ ONSCSI_VERSION,PPSC_H_VERSION); ++} ++ ++#define j44(a,b) ((b&0xf0)|((a>>4)&0x0f)) ++ ++#define CMD(x) w0(x);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); ++#define VAL(v) w0(v);w2(5);w2(7);w2(5);w2(4); ++ ++static inline void onscsi_opcode (PHA *pha, int x ) ++{ ++ if (pha->mode < 2) { ++ CMD(x); ++ } else { ++ w3(x); ++ } ++} ++ ++#define OP(x) onscsi_opcode(pha,x) ++#define FULLBYTE (pha->mode > 0) ++ ++static void onscsi_write_regr (PHA *pha, int r, int v) ++{ ++ onscsi_opcode(pha,r); ++ ++ if (pha->mode < 2) { ++ VAL(v); ++ } else { ++ w2(5); w4(v); w2(4); ++ } ++} ++ ++static inline int onscsi_read_nybble (PHA *pha) ++{ ++ int a, b; ++ ++ w2(6); a = r1(); w2(4); ++ w2(6); b = r1(); w2(4); ++ ++ return j44(a,b); ++} ++ ++static int onscsi_read_regr (PHA *pha, int r) ++{ ++ int v = -1; ++ ++ onscsi_opcode(pha,r); ++ ++ switch (pha->mode) { ++ ++ case 0: v = onscsi_read_nybble(pha); ++ break; ++ ++ case 1: w2(0x26); v = r0(); w2(4); ++ break; ++ ++ case 2: ++ case 3: ++ case 4: w2(0x24); v = r4(); w2(4); ++ break; ++ ++ } ++ ++ return v; ++} ++ ++#define RR(r) onscsi_read_regr(pha,r) ++#define WR(r,v) onscsi_write_regr(pha,r,v) ++ ++static void onscsi_write_block (PHA *pha, char *buf, int n) ++{ ++ int i; ++ ++ w2(5+TOGL); ++ ++ switch (pha->mode) { ++ ++ case 0: ++ case 1: for (i=0;imode) { ++ ++ case 0: w2(4); ++ for (i=0;isaved_r0 = r0(); ++ pha->saved_r2 = r2(); ++ ++ CPP(0x20,4); ++ ++ CMD(2); VAL(0); ++ CMD(2); VAL(FULLBYTE); ++ ++ WR(2,FULLBYTE); ++} ++ ++static void onscsi_disconnect (PHA *pha) ++{ ++ WR(3,0); WR(7,0x48); ++ OP(4); ++ CPP(0x30,pha->saved_r2); ++ ++ w0(pha->saved_r0); ++ w2(pha->saved_r2); ++} ++ ++static int onscsi_test_proto (PHA *pha) ++{ ++ int i, k, j; ++ char wbuf[16], rbuf[16]; ++ int e = 0; ++ ++ pha->saved_r0 = r0(); ++ pha->saved_r2 = r2(); ++ ++ CPP(0x30,pha->saved_r2); ++ CPP(0x0,pha->saved_r2); ++ ++ w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff); ++ i = ((r1() & 0xf0) << 4); w0(0x87); ++ i |= (r1() & 0xf0); w0(0x78); ++ w0(0x20);w2(5); ++ i |= ((r1() & 0xf0) >> 4); ++ w2(4);w0(0xff); ++ ++ if (V_PROBE) printk("%s: signature 0x%x\n",pha->device,i); ++ ++ if (i == 0xb5f) { ++ ++ CMD(2); VAL(FULLBYTE); ++ ++ w2(4); w2(0xc); udelay(100); w2(4); udelay(100); ++ ++ CMD(2); VAL(0); ++ CMD(2); VAL(FULLBYTE); ++ ++ WR(2,FULLBYTE); ++ ++ k = RR(4); ++ ++ if (V_PROBE) ++ printk("%s: OnSpec 90c26 version %x\n",pha->device,k); ++ } ++ ++ CPP(0x30,pha->saved_r2); ++ ++ w0(pha->saved_r0); ++ w2(pha->saved_r2); ++ ++ if (i != 0xb5f) return 1; ++ ++ onscsi_connect(pha); ++ ++ for (k=0;kmode == 0) WR(2,0x30); ++ if (pha->mode == 1) WR(2,0x10); ++ ++ WR(3,0); WR(7,0x48); ++ ++ WR(3,1); WR(7,0x48); OP(5); ++ TOGL = 0; ++ onscsi_write_block(pha,wbuf,16); ++ w2(4); ++ ++ if (pha->mode == 0) WR(2,0); ++ if (pha->mode == 1) WR(2,0x11); ++ ++ WR(3,5); WR(7,0x48); OP(5); ++ TOGL = 0; ++ onscsi_read_block(pha,rbuf,16); ++ w2(4); ++ ++ for (j=0;j<16;j++) ++ if (rbuf[j] != wbuf[j]) e++; ++ ++ } ++ ++ onscsi_disconnect(pha); ++ ++#ifdef EXPERIMENT ++ ++ /* enable this to see how the buffer status bits work */ ++ ++ if (pha->mode == 2) { ++ ++ onscsi_connect(pha); ++ ++ WR(3,0); WR(7,0x48); ++ WR(3,1); WR(7,0x48); OP(5); ++ w2(5); ++ ++ for (k=0;k<16;k++) { ++ j = r1(); w4(k); ++ printk("%2x:%d ",j,k); ++ } ++ printk("\n"); ++ ++ w2(4); WR(3,5); WR(7,0x48); OP(5); ++ w2(0x24); ++ ++ for (k=0;k<16;k++) { ++ j = r1(); ++ printk("%2x:%d ",j,r4()); ++ } ++ printk("\n"); ++ ++ w2(4); ++ ++ onscsi_disconnect(pha); ++ } ++ ++ if (pha->mode == 1) { ++ ++ onscsi_connect(pha); ++ ++ WR(2,0x11); ++ WR(3,0); WR(7,0x48); ++ WR(3,1); WR(7,0x48); OP(5); ++ w2(5); i = 0; ++ ++ for (k=0;k<16;k++) { ++ j = r1(); w0(k); i = 2 - i; w2(5+i); ++ printk("%2x:%d ",j,k); ++ } ++ printk("%2x.\n",r1()); ++ ++ w2(4); ++ ++ WR(2,0x11); ++ WR(3,0); WR(7,0x48); ++ WR(3,5); WR(7,0x48); OP(5); ++ w2(0x24); i = 0; ++ ++ printk("%2x ",r1()); ++ for (k=0;k<16;k++) { ++ i = 2 - i; w2(0x24+i); j = r1(); ++ printk("%2x:%d ",j,r0()); ++ } ++ printk("\n"); ++ ++ w2(4); ++ ++ onscsi_disconnect(pha); ++ } ++ ++#endif ++ ++ if (V_FULL) ++ printk("%s: test port 0x%x mode %d errors %d\n", ++ pha->device,pha->port,pha->mode,e); ++ ++ return e; ++} ++ ++static int onscsi_select (PHA *pha, int initiator, int target) ++{ ++ WR(1,0); ++ WR(2,0x80+FULLBYTE); ++ if (RR(1) != 0) return -1; ++ WR(0,((1 << initiator) | (1 << target))); ++ WR(1,2); ++ return 0; ++} ++ ++static int onscsi_test_select (PHA *pha) ++{ ++ return ((RR(1) & 3) == 3); ++} ++ ++static void onscsi_select_finish (PHA *pha) ++{ ++ WR(1,0); ++} ++ ++static void onscsi_deselect (PHA *pha) ++{ ++ WR(1,0); ++ /* WR(2,0x20+FULLBYTE); */ ++ WR(2,FULLBYTE); ++ WR(3,0); WR(7,0x48); ++} ++ ++static int onscsi_get_bus_status (PHA *pha) ++{ ++ WR(2,0x20+FULLBYTE); ++ return onscsi_map[RR(1)]; ++} ++ ++static void onscsi_slow_start (PHA *pha, char *val) ++{ ++ pha->priv_flag = (RR(1) & 0x80); ++ pha->priv_ptr = val; ++ ++ if (pha->priv_flag) WR(2,0x20); else WR(2,0x21); ++ ++ OP(0); ++ ++ if (pha->priv_flag) { ++ w2(6); ++ } else { ++ w0(*val); w2(5); w2(7); ++ } ++} ++ ++static int onscsi_slow_done (PHA *pha) ++{ ++ return (!(r1() & 8)); ++} ++ ++static void onscsi_slow_end (PHA *pha) ++{ ++ if (pha->priv_flag) { ++ *pha->priv_ptr = onscsi_read_nybble(pha); ++ } else { ++ w2(5); w2(4); ++ } ++} ++ ++static void onscsi_start_block (PHA *pha, int rd) ++{ ++ pha->priv_flag = rd; ++ ++ if (rd) { ++ WR(3,5); WR(7,0x48); ++ if (pha->mode == 1) WR(2,0x31); ++ OP(5); ++ w2(5); w0(0xff); w2(4); ++ } else { ++ WR(3,1); WR(7,0x48); ++ if (pha->mode == 1) WR(2,0x31); ++ OP(5); ++ } ++ TOGL = 0; ++} ++ ++static int onscsi_transfer_done (PHA *pha) ++{ ++ int x; ++ ++ if (pha->priv_flag) return 1; ++ ++ if (pha->mode == 0) { WR(2,0x20); OP(5); } ++ x = r1(); x = r1(); ++ if (pha->mode == 0) { WR(2,0x30); OP(5); } ++ ++ if ((x & 0xf0) == 0x80) return 16; ++ return 0; ++} ++ ++static int onscsi_transfer_ready (PHA *pha) ++{ ++ int x; ++ ++ if (pha->priv_flag) { ++ x = r1(); x = r1(); ++ if ((x & 0xf0) == 0xf0) return 16; ++ if ((x & 0xf0) == 0xb0) return 8; ++ if ((x & 0xf0) == 0x90) return 1; ++ if ((x & 0xf8) == 0x88) return -1; ++ if ((x & 0xf8) == 0x08) return -1; ++ if ((x & 0xf8) == 0x0) return 1; ++ ++ if ((x & 0xf8) != 0x80) printk("DEBUG: %x\n",x); ++ ++ return 0; ++ } ++ ++ return onscsi_transfer_done(pha); ++} ++ ++ ++static int onscsi_transfer_block (PHA *pha, char * buf, int buflen, int rd) ++{ ++ int k, b; ++ ++ k = 0; ++ while ( k < buflen) { ++ ++ if ((b=onscsi_transfer_ready(pha)) <= 0) break; ++ if (b > (buflen-k)) b = buflen-k; ++ ++ if (rd) onscsi_read_block(pha,buf,b); ++ else onscsi_write_block(pha,buf,b); ++ ++ k += b; buf += b; ++ } ++ ++ return k; ++} ++ ++static void onscsi_end_block (PHA *pha, int rd) ++{ ++ w2(4); WR(3,0); WR(7,0x48); ++} ++ ++static void onscsi_reset_bus (PHA *pha) ++{ ++ WR(2,2); ++ udelay(500); ++ WR(2,0); ++ WR(2,FULLBYTE); ++} ++ ++static char *(mode_strings[5]) = {"Nybble","PS/2","EPP","EPP-16","EPP-32"}; ++ ++static struct ppsc_protocol onscsi_psp = { ++ ++ {&host0,&host1,&host2,&host3}, /* params */ ++ &host_structs, /* hosts */ ++ 5, /* num_modes */ ++ 2, /* epp_first */ ++ 1, /* default_delay */ ++ 1, /* can_message */ ++ 16, /* sg_tablesize */ ++ mode_strings, ++ onscsi_init, ++ NULL, /* release */ ++ onscsi_connect, ++ onscsi_disconnect, ++ onscsi_test_proto, ++ onscsi_select, ++ onscsi_test_select, ++ onscsi_select_finish, ++ onscsi_deselect, ++ onscsi_get_bus_status, ++ onscsi_slow_start, ++ onscsi_slow_done, ++ onscsi_slow_end, ++ onscsi_start_block, ++ onscsi_transfer_block, ++ onscsi_transfer_ready, ++ onscsi_transfer_done, ++ onscsi_end_block, ++ onscsi_reset_bus ++}; ++ ++int onscsi_detect (struct scsi_host_template *tpnt ) ++{ ++ return ppsc_detect( &onscsi_psp, tpnt, verbose); ++} ++ ++#ifdef MODULE ++ ++struct scsi_host_template driver_template = PPSC_TEMPLATE(onscsi); ++ ++#include "scsi_module.c" ++ ++MODULE_LICENSE("GPL"); ++ ++#else ++ ++void onscsi_setup (char *str, int *ints) ++{ ++ ppsc_gen_setup(stt,4,str); ++} ++ ++#endif ++ ++/* end of onscsi.c */ +--- linux-2.6.31-rc3-git5.mnb1/drivers/scsi/Kconfig.scsi-ppscsi-2.6.2.orig 2009-07-22 01:11:12.000000000 +0300 ++++ linux-2.6.31-rc3-git5.mnb1/drivers/scsi/Kconfig 2009-07-22 21:54:04.000000000 +0300 +@@ -1062,6 +1062,42 @@ config SCSI_IZIP_SLOW_CTR + + Generally, saying N is fine. + ++config PPSCSI ++ tristate "Parallel Port SCSI adapters" ++ depends on SCSI && PARPORT ++ ++config PPSCSI_T348 ++ tristate "Adaptec APA-348 adapter" ++ depends on PPSCSI ++ ++config PPSCSI_T358 ++ tristate "Adaptec APA-358 adapter" ++ depends on PPSCSI ++ ++config PPSCSI_VPI0 ++ tristate "Iomega VPI0 adapter" ++ depends on PPSCSI ++ ++config PPSCSI_VPI2 ++ tristate "Iomega VPI2 adapter (EXPERIMENTAL)" ++ depends on PPSCSI && EXPERIMENTAL ++ ++config PPSCSI_ONSCSI ++ tristate "OnSpec 90c26 adapter" ++ depends on PPSCSI ++ ++config PPSCSI_SPARCSI ++ tristate "Shining SparSCI adapter" ++ depends on PPSCSI ++ ++config PPSCSI_EPSA2 ++ tristate "Shuttle EPSA-2 adapter" ++ depends on PPSCSI ++ ++config PPSCSI_EPST ++ tristate "Shuttle EPST adapter" ++ depends on PPSCSI ++ + config SCSI_NCR53C406A + tristate "NCR53c406a SCSI support" + depends on ISA && SCSI diff --git a/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-3.0-buildfix.patch b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-3.0-buildfix.patch new file mode 100644 index 00000000..c0be53e2 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-3.0-buildfix.patch @@ -0,0 +1,19 @@ + +fix build with 3.0 + +Signed-off-by: Thomas Backlund + + drivers/scsi/ppscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/scsi/ppscsi.c.orig 2011-07-15 02:37:27.000000000 +0300 ++++ a/drivers/scsi/ppscsi.c 2011-07-15 13:44:29.514928985 +0300 +@@ -72,7 +72,7 @@ + #define PPSC_DEF_NICE 0 + #define PPSC_INITIATOR 7 + +-spinlock_t ppsc_spinlock = SPIN_LOCK_UNLOCKED; ++DEFINE_SPINLOCK(ppsc_spinlock); + + static char ppsc_bulk_map[256]; + diff --git a/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-mdvbz45393.patch b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-mdvbz45393.patch new file mode 100644 index 00000000..2e919ea6 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-mdvbz45393.patch @@ -0,0 +1,31 @@ +Fix for https://qa.mandriva.com/show_bug.cgi?id=45393 + +cmnd field of struct scsi_cmnd changed after commit "[SCSI] Let +scsi_cmnd->cmnd use request->cmd buffer" +(64a87b244b9297667ca80264aab849a36f494884) + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + drivers/scsi/ppscsi.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/scsi/ppscsi.c ++++ b/drivers/scsi/ppscsi.c +@@ -1009,7 +1009,6 @@ int ppsc_biosparam (struct scsi_device * + static int ppsc_inquire (PHA *pha, int target, char *buf) + { + char inq[6] = {0x12,0,0,0,36,0}; +- int i; + struct scsi_cmnd cmd; + struct scsi_device dev; + +@@ -1017,7 +1016,7 @@ static int ppsc_inquire (PHA *pha, int t + dev.id = target; + cmd.device = &dev; + cmd.cmd_len = 6; +- for (i=0;i<6;i++) cmd.cmnd[i] = inq[i]; ++ cmd.cmnd = inq; + cmd.sdb.table.nents = 0; + cmd.sdb.table.sgl = (void *) buf; + cmd.sdb.length = 36; diff --git a/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-sg-helper-update.patch b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-sg-helper-update.patch new file mode 100644 index 00000000..defdcefe --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-sg-helper-update.patch @@ -0,0 +1,36 @@ +Update ppscsi for 2.6.24 changes. + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + drivers/scsi/ppscsi.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/scsi/ppscsi.c ++++ b/drivers/scsi/ppscsi.c +@@ -58,6 +58,7 @@ + #include + #include + #include ++#include + + #include + +@@ -484,7 +485,7 @@ static void ppsc_update_sg (PHA *pha) + if ((!pha->cur_len) && pha->sg_count) { + pha->sg_count--; + pha->sg_list++; +- pha->cur_buf = page_address(pha->sg_list->page) + pha->sg_list->offset; ++ pha->cur_buf = sg_virt(pha->sg_list); + pha->cur_len = pha->sg_list->length; + } + } +@@ -571,7 +572,7 @@ static void ppsc_engine (PHA *pha) + pha->sg_count--; + pha->sg_list = + (struct scatterlist *)pha->cur_cmd->request_buffer; +- pha->cur_buf = page_address(pha->sg_list->page) + pha->sg_list->offset; ++ pha->cur_buf = sg_virt(pha->sg_list); + pha->cur_len = pha->sg_list->length; + } else { + pha->cur_buf = pha->cur_cmd->request_buffer; diff --git a/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-update-for-scsi_data_buffer.patch b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-update-for-scsi_data_buffer.patch new file mode 100644 index 00000000..496326e3 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi-update-for-scsi_data_buffer.patch @@ -0,0 +1,88 @@ +Update ppscsi for "[SCSI] implement scsi_data_buffer" change on Linux 2.6.25 +(commit 30b0c37b27485a9cb897bfe3824f6f517b8c80d6) + +Signed-off-by: Herton Ronaldo Krzesinski + +--- + drivers/scsi/ppscsi.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +--- a/drivers/scsi/ppscsi.c ++++ b/drivers/scsi/ppscsi.c +@@ -424,16 +424,16 @@ static void ppsc_start (PHA *pha) + + bf &= (!((1<cur_cmd->device->id) & pha->slow_targets)); + +- r = pha->cur_cmd->use_sg; ++ r = scsi_sg_count(pha->cur_cmd); + if (r) { + b = 0; +- p = (struct scatterlist *)pha->cur_cmd->request_buffer; ++ p = scsi_sglist(pha->cur_cmd); + for (k=0;klength; + p++; + } + } else { +- b = pha->cur_cmd->request_bufflen; ++ b = scsi_bufflen(pha->cur_cmd); + } + + bf &= (b > 127); +@@ -567,16 +567,16 @@ static void ppsc_engine (PHA *pha) + pha->data_dir = phase & PPSC_IO; + pha->data_count = 0; + +- pha->sg_count = pha->cur_cmd->use_sg; ++ pha->sg_count = scsi_sg_count(pha->cur_cmd); + if (pha->sg_count) { + pha->sg_count--; +- pha->sg_list = +- (struct scatterlist *)pha->cur_cmd->request_buffer; ++ pha->sg_list = scsi_sglist(pha->cur_cmd); + pha->cur_buf = sg_virt(pha->sg_list); + pha->cur_len = pha->sg_list->length; + } else { +- pha->cur_buf = pha->cur_cmd->request_buffer; +- pha->cur_len = pha->cur_cmd->request_bufflen; ++ pha->cur_buf = ++ (char *)pha->cur_cmd->sdb.table.sgl; ++ pha->cur_len = scsi_bufflen(pha->cur_cmd); + } + + pha->last_phase = phase; +@@ -622,7 +622,7 @@ static void ppsc_engine (PHA *pha) + + if (pha->cur_cmd->cmnd[0] == REQUEST_SENSE) { + +- sb = (char *)pha->cur_cmd->request_buffer; ++ sb = (char *)pha->cur_cmd->sdb.table.sgl; + printk("%s: Sense key: %x ASC: %x ASCQ: %x\n", + pha->device, sb[2] & 0xff, + sb[12] & 0xff, sb[13] & 0xff); +@@ -819,9 +819,9 @@ static void ppsc_cleanup (PHA *pha) + cmd->cmnd[4] = sizeof(cmd->sense_buffer); + cmd->cmnd[5] = 0; + cmd->cmd_len = 6; +- cmd->use_sg = 0; +- cmd->request_buffer = (char *) cmd->sense_buffer; +- cmd->request_bufflen = sizeof(cmd->sense_buffer); ++ cmd->sdb.table.nents = 0; ++ cmd->sdb.table.sgl = (void *) cmd->sense_buffer; ++ cmd->sdb.length = sizeof(cmd->sense_buffer); + + pha->cur_cmd = cmd; + ppsc_do_claimed(pha,ppsc_start); +@@ -1018,9 +1018,9 @@ static int ppsc_inquire (PHA *pha, int t + cmd.device = &dev; + cmd.cmd_len = 6; + for (i=0;i<6;i++) cmd.cmnd[i] = inq[i]; +- cmd.use_sg = 0; +- cmd.request_buffer = buf; +- cmd.request_bufflen = 36; ++ cmd.sdb.table.nents = 0; ++ cmd.sdb.table.sgl = (void *) buf; ++ cmd.sdb.length = 36; + + return ppsc_command(&cmd); + } diff --git a/kernel/kernel/files/patches/mageia.old/scsi-ppscsi_fixes.patch b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi_fixes.patch new file mode 100644 index 00000000..acc011e9 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/scsi-ppscsi_fixes.patch @@ -0,0 +1,226 @@ +--- + drivers/scsi/Makefile | 2 +- + drivers/scsi/ppscsi.c | 22 ++++++++++++---------- + drivers/scsi/ppscsi.h | 34 ++++++++++++++++++---------------- + drivers/scsi/sparcsi.c | 6 +++--- + 4 files changed, 34 insertions(+), 30 deletions(-) + +--- a/drivers/scsi/ppscsi.c ++++ b/drivers/scsi/ppscsi.c +@@ -57,6 +57,7 @@ + #include + #include + #include ++#include + + #include + +@@ -95,7 +96,8 @@ static void ppsc_attach (struct parport + return; + } + +- add->port = parport_get_port (port); ++ atomic_inc(&port->ref_count); ++ add->port = port; + add->next = ppsc_port_list; + wmb (); + ppsc_port_list = add; +@@ -187,11 +189,11 @@ static void ppsc_set_intr (PHA *pha, voi + spin_unlock_irqrestore(&ppsc_spinlock,flags); + } + +-static void ppsc_tq_int (void *data) ++static void ppsc_tq_int (struct work_struct *work) + { + void (*con)(PHA *); + unsigned long flags; +- PHA *pha = (PHA *)data; ++ PHA *pha = container_of(work, PHA, wq); + + spin_lock_irqsave(&ppsc_spinlock,flags); + +@@ -377,7 +379,7 @@ int ppsc_command (struct scsi_cmnd *cmd) + + ppsc_do_claimed(pha,ppsc_start); + +- while (pha->cur_cmd) scsi_sleep(1); ++ while (pha->cur_cmd) ssleep(1); + + return cmd->result; + } +@@ -886,7 +888,7 @@ static void ppsc_reset_pha (PHA *pha) + ppsc_claim(pha); + pha->proto->connect(pha); + pha->proto->reset_bus(pha); +- scsi_sleep(4*HZ); ++ ssleep(4*HZ); + pha->proto->disconnect(pha); + ppsc_unclaim(pha); + +@@ -905,7 +907,7 @@ int ppsc_reset (struct scsi_cmnd * cmd) + pha->abort_flag = PPSC_DO_RESET; + + while (pha->cur_cmd && (k < PPSC_RESET_TMO)) { +- scsi_sleep(HZ/10); ++ ssleep(HZ/10); + k += HZ/10; + } + +@@ -992,11 +994,11 @@ int ppsc_biosparam (struct scsi_device * + { + ip[0] = 0x40; + ip[1] = 0x20; +- ip[2] = (capacity +1) / (ip[0] * ip[1]); ++ ip[2] = (unsigned int)(capacity +1) >> 11; + if (ip[2] > 1024) { + ip[0] = 0xff; + ip[1] = 0x3f; +- ip[2] = (capacity +1) / (ip[0] * ip[1]); ++ ip[2] = (unsigned int)(capacity +1) / (0xff * 0x3f); + if (ip[2] > 1023) + ip[2] = 1023; + } +@@ -1084,7 +1086,7 @@ int ppsc_release_pha (PHA *pha) + } + + +-int ppsc_detect (PSP *proto, Scsi_Host_Template *tpnt, int verbose) ++int ppsc_detect (PSP *proto, struct scsi_host_template *tpnt, int verbose) + { + int i, m, p, d, n, s, z; + struct ppsc_port_list_struct *next_port = NULL; /* shut gcc up */ +@@ -1146,7 +1148,7 @@ int ppsc_detect (PSP *proto, Scsi_Host_T + pha->device[p+1] = '0' + i; + pha->device[p+2] = 0; + +- INIT_WORK(&pha->wq, ppsc_tq_int, pha); ++ INIT_WORK(&pha->wq, ppsc_tq_int); + + init_timer (&pha->timer); + pha->timer.data = (unsigned long) pha; +--- a/drivers/scsi/sparcsi.c ++++ b/drivers/scsi/sparcsi.c +@@ -165,7 +165,7 @@ static int sparcsi_test_proto (PHA *pha) + + WR(1,0x80); udelay(60); + WR(1,0); +- scsi_sleep(5*HZ); ++ ssleep(5*HZ); + pha->private[0] = 1; + } + +@@ -363,14 +363,14 @@ static struct ppsc_protocol sparcsi_psp + sparcsi_reset_bus + }; + +-int sparcsi_detect (Scsi_Host_Template *tpnt) ++int sparcsi_detect (struct scsi_host_template *tpnt) + { + return ppsc_detect( &sparcsi_psp, tpnt, verbose); + } + + #ifdef MODULE + +-Scsi_Host_Template driver_template = PPSC_TEMPLATE(sparcsi); ++struct scsi_host_template driver_template = PPSC_TEMPLATE(sparcsi); + + #include "scsi_module.c" + +--- a/drivers/scsi/ppscsi.h ++++ b/drivers/scsi/ppscsi.h +@@ -13,14 +13,16 @@ + #define PPSC_H_VERSION "0.92" + + #include +-#include ++#include + #include + #include + #include + #include + +-#include "scsi.h" +-#include "hosts.h" ++#include ++#include ++#include ++#include + + + /* ppscsi global functions */ +@@ -40,31 +42,31 @@ extern int ppsc_release(struct Scsi_Host + /* imports for hosts.c */ + + #ifdef CONFIG_PPSCSI_T348 +-extern int t348_detect( Scsi_Host_Template *); ++extern int t348_detect( struct scsi_host_template *); + #endif + + #ifdef CONFIG_PPSCSI_T358 +-extern int t358_detect( Scsi_Host_Template *); ++extern int t358_detect( struct scsi_host_template *); + #endif + + #ifdef CONFIG_PPSCSI_ONSCSI +-extern int onscsi_detect( Scsi_Host_Template *); ++extern int onscsi_detect( struct scsi_host_template *); + #endif + + #ifdef CONFIG_PPSCSI_EPST +-extern int epst_detect( Scsi_Host_Template *); ++extern int epst_detect( struct scsi_host_template *); + #endif + + #ifdef CONFIG_PPSCSI_EPSA2 +-extern int epsa2_detect( Scsi_Host_Template *); ++extern int epsa2_detect( struct scsi_host_template *); + #endif + + #ifdef CONFIG_PPSCSI_VPI0 +-extern int vpi0_detect( Scsi_Host_Template *); ++extern int vpi0_detect( struct scsi_host_template *); + #endif + + #ifdef CONFIG_PPSCSI_SPARCSI +-extern int sparcsi_detect( Scsi_Host_Template *); ++extern int sparcsi_detect( struct scsi_host_template *); + #endif + + #endif +@@ -303,7 +305,7 @@ struct ppsc_protocol { + + typedef struct ppsc_protocol PSP; + +-extern int ppsc_detect( PSP *, Scsi_Host_Template *, int); ++extern int ppsc_detect( PSP *, struct scsi_host_template *, int); + + #ifdef PPSC_HA_MODULE + +@@ -322,11 +324,11 @@ static STT stt[4] = { {"host0",8,host0}, + {"host3",8,host3} }; + #endif + +-MODULE_PARM(host0,"1-8i"); +-MODULE_PARM(host1,"1-8i"); +-MODULE_PARM(host2,"1-8i"); +-MODULE_PARM(host3,"1-8i"); +-MODULE_PARM(verbose,"i"); ++module_param_array(host0, int, NULL, 0); ++module_param_array(host1, int, NULL, 0); ++module_param_array(host2, int, NULL, 0); ++module_param_array(host3, int, NULL, 0); ++module_param(verbose, int, 0); + + static struct ppsc_host_adapter host_structs[4]; + +--- a/drivers/scsi/Makefile ++++ b/drivers/scsi/Makefile +@@ -136,7 +136,7 @@ obj-$(CONFIG_PPSCSI_ONSCSI) += onscsi.o + obj-$(CONFIG_PPSCSI_EPSA2) += epsa2.o + obj-$(CONFIG_PPSCSI_EPST) += epst.o + obj-$(CONFIG_PPSCSI_VPI0) += vpi0.o +-obj-$(CONFIG_PPSCSI_VPI2) += vpi2.o ++obj-n += vpi2.o + obj-$(CONFIG_PPSCSI_SPARCSI) += sparcsi.o + + obj-$(CONFIG_CHR_DEV_ST) += st.o diff --git a/kernel/kernel/files/patches/mageia.old/series b/kernel/kernel/files/patches/mageia.old/series new file mode 100644 index 00000000..b96a67db --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/series @@ -0,0 +1,411 @@ +# +# Mageia kernel series file +# +# This file describes the order in which patches should be applied +# in the kernel and why the patch is needed. +# +# Luiz Fernando N. Capitulino +# + +### +### Upstream git +### + +### +### Stable Queue +### +stable-x86-ldt-make-modify_ldt-synchronous.patch +stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch +stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch +stable-x86-ldt-further-fix-fpu-emulation.patch +stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch +stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch +stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch +stable-drm-radeon-atom-send-out-the-full-aux-address.patch +stable-drm-radeon-native-send-out-the-full-aux-address.patch +stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch +stable-drm-i915-preserve-ssc-earlier.patch +stable-drm-qxl-validate-monitors-config-modes.patch +stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch +stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch +stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch +stable-s390-sclp-fix-compile-error.patch +stable-s390-setup-fix-novx-parameter.patch +stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch +stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch +stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch +stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch +stable-iio-add-inverse-unit-conversion-macros.patch +stable-iio-adis16480-fix-scale-factors.patch +stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch +stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch +stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch +stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch +stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch +stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch +stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch +stable-clk-s5pv210-add-missing-call-to.patch +stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch +stable-clk-pistachio-correct-critical-clock-list.patch +stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch +stable-clk-pxa-fix-core-frequency-reporting-unit.patch +stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch +stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch +stable-pci-fix-ti816x-class-code-quirk.patch +stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch +stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch +stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch +stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch +stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch +stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch +stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch +stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch +stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch +stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch +stable-usb-symbolserial-use-usb_get_serial_port_data.patch +stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch +stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch +stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch +stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch +stable-xfs-fix-xfs_attr_leafblock-definition.patch +stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch +stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch +stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch +stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch +stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch +stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch +stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch +stable-asoc-rt5640-fix-line-out-no-sound-issue.patch +stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch +stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch +stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch +stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch + +### +### Arch x86 +### + +# laptop needing pci=assign-busses (#18989, needs to be submitted upstream) +x86-pci-toshiba-equium-a60-assign-busses.patch + +# If users choose a bad video mode, allow to jump to +# a working one (TTL: forever) +x86-boot-video-80x25-if-break.patch + +# Allow poweroff on UP machines running SMP kernels +x86-default_poweroff_up_machines.patch + +# Fix #38760, need to be revised and submitted upstream +# BROKEN: x86-cpufreq-speedstep-dothan-3.patch + +# raise vmalloc to fix https://bugs.mageia.org/show_bug.cgi?id=904 +x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch + +# slows down boot +Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch + +### +### Core +### + +### +### i2c +### + +### +### PCI core +### + +# BROKEN: pci-pciprobe-CardBusNo.patch + +# http://lkml.org/lkml/2008/9/12/52 +pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch +pci-quirks-drop-devinit-exit.patch + +# Intel ACS quirks +pci-acs-quirks-for-Intel-9-series-PCH-root-ports.patch + +### +### PNP core +### + +### +### ACPI +### + +# CLEVO M360S acpi irq workaround +acpi-CLEVO-M360S-disable_acpi_irq.patch + +# Clevo M720SR freezes with C3 +acpi-processor-M720SR-limit-to-C2.patch + +### +### Block +### + +# epsa2 SCSI driver, don't know from where it came +scsi-ppscsi-2.6.2.patch + +# epsa2 is far behind +scsi-ppscsi_fixes.patch + +# Fix build of ppscsi on 2.6.24 +scsi-ppscsi-sg-helper-update.patch + +# Update/fix for ppscsi on 2.6.25 +scsi-ppscsi-update-for-scsi_data_buffer.patch + +# https://qa.mandriva.com/show_bug.cgi?id=45393 +scsi-ppscsi-mdvbz45393.patch + +# epsa2 3.0 buildfix +scsi-ppscsi-3.0-buildfix.patch + +# Don't know know why this is needed +scsi-megaraid-new-sysfs-name.patch + +# Looks like fixes from Arnaud, not sure why they're needed +ide-pci-sis5513-965.patch + +mpt-vmware-fix.patch + +# adds aliases to support upgrade from old dm-raid45 patch +dm-raid-aliases.patch + +# disable floppy autoloading (mga #4696) +block-floppy-disable-pnp-modalias.patch + +# prefer ata over ide drivers +ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch + +# Nice SSD speedup +block-Make-CFQ-default-to-IOPS-mode-on-SSDs.patch +block-cfq-iosched-fix-the-setting-of-IOPS-mode-on-SSDs.patch + +# properly fix null pointer deref introduced in 4.1.7 +scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch +scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch + +### +### File-system +### + +# aufs from: http://aufs.sourceforge.net/ (mga#8314) +fs-aufs4.patch +fs-aufs4-modular.patch + +# ext4 +fs-Revert-ext4-remove-block_device_ejected.patch + +### +### FireWire +### + +# adding module aliases to ease upgrade from ieee1394 +firewire-ieee1394-module-aliases.patch + +### +### GPU/DRM +### + +# new Q57 Host Bridge id +char-agp-intel-new-Q57-id.patch + +# External mach64 drm support from git://anongit.freedesktop.org/git/mesa/drm +gpu-drm-mach64.patch +gpu-drm-mach64-fixes.patch +gpu-drm-mach64-2.6.31.patch +gpu-drm-mach64-fix-for-changed-drm_pci_alloc.patch +gpu-drm-mach64-fix-for-changed-drm_ioctl.patch +gpu-drm-mach64-2.6.36-buildfix.patch +gpu-drm-mach64-2.6.37-buildfix.patch +gpu-drm-mach64-3.0-buildfix.patch +gpu-drm-mach64-include-module.h.patch +gpu-drm-mach64-3.3-buildfix.patch +gpu-drm-mach64-3.6-buildfix.patch +gpu-drm-mach64-3.7-buildfix.patch +gpu-drm-mach64-3.12-buildfix.patch +gpu-drm-mach64-restore-mach64_PCI_IDS.patch +gpu-drm-mach64-linux-3.14-buildfix.patch +gpu-drm-mach64-3.17-buildfix.patch +gpu-drm-mach64-3.18-buildfix.patch + +### +### Hardware Monitoring +### + +### +### Input +### + +input-i8042-quirks-for-Fujitsu-Lifebook-A544-and-Lif.patch + +# fixes systemd-logind killing itself +Input-evdev-do-not-report-errors-form-flush.patch + +### +### idle +### + +### +### Kbuild +### + +### +### Media +### + +### +### Misc +### + +### +### MM +### + +### +### Network +### + +# SiS 190 fixes +net-sis190-fix-list-usage.patch + +# netfilter IFWLOG support +net-netfilter-IFWLOG.patch +net-netfilter-IFWLOG-mdv.patch +net-netfilter-IFWLOG-2.6.35-buildfix.patch +net-netfilter-IFWLOG-2.6.37-buildfix.patch +net-ipv4-netfilter-ipt_IFWLOG-3.6-buildfix.patch +net-netfilter-IFWLOG-3.7-buildfix.patch + +# netfilter psd support +net-netfilter-psd.patch +net-netfilter-psd-mdv.patch +net-netfilter-psd-2.6.35-buildfix.patch + +### +### Platform drivers +### + +# Allow access to Shuttle WMI interface controls +# (Mainly allow turning on/off webcam and wireless on Shuttle DA18IE and DA18IM) +platform-x86-add-shuttle-wmi-driver.patch +platform-x86-shuttle-wmi-drop-devinit-exit.patch + +### +### RTC +### + +### +### Serial +### + +# Export pci_ids.h to user space, needed by ldetect +include-kbuild-export-pci_ids.patch + +### +### Sound +### + +### +### Staging +### + + +### +### TTY +### + +### +### USB +### + +hid-usbhid-IBM-BladeCenterHS20-quirk.patch + +usb-storage-unusual_devs-add-id.patch +usb-storage-unusual_devs-add-id-2.6.37-buildfix.patch + +### +### V4L +### + +# pwc driver name in /proc/bus/devices, /sys fix and "advertisement" removal +media-usb-pwc-lie-in-proc-usb-devices.patch + +### +### Video +### + +# Mageia framebuffer boot logo +video-mageia-logo.patch + +### +### 3rdparty +### + +3rd-3rdparty-1.0-tree.patch + +# TODO: fix up patch below to include all archs? +3rd-3rdparty-merge.patch + +# acerhk +3rd-acerhk-0.5.35.patch +3rd-acerhk-kbuild.patch +3rd-acerhk-extra-cflags.patch +3rd-acerhk-proc_dir_entry-owner.patch +3rd-acerhk-fix-build-with-function-tracer.patch +3rd-acerhk-2.6.36-buildfix.patch +3rd-acerhk-fix-include.patch + +# aes2501 +3rd-aes2501-r19.patch +3rd-aes2501-kbuild.patch +3rd-aes2501-rmmod-oops-fix.patch + +# ndiswrapper +3rd-ndiswrapper-1.59.patch +3rd-ndiswrapper-Kconfig.patch +3rd-ndiswrapper-Makefile-build-fix.patch +3rd-ndiswrapper-linux-3.14-buildfix.patch +3rd-ndiswrapper-4.0-buildfix.patch + +# rfswitch +3rd-rfswitch-1.3.patch +3rd-rfswitch-build-fix.patch +3rd-rfswitch-3.0-buildfix.patch + +# viahss +3rd-viahss-0.92.patch +3rd-viahss-config.patch +3rd-viahss-module-license.patch +3rd-viahss-2.6.35-buildfix.patch +3rd-viahss-3.0-buildfix.patch + +# add rtl8723bs support (mga#15874) +3rd-rtl8723bs.patch +3rd-rtl8723bs-buildfix.patch +3rd-rtl8723bs-updates.patch + +### +### Security +### + +### +### Smack fixes +### + +### +### XEN +### + +### +### ARM +### + +### +### IA64 +### + +### +### Tools +### diff --git a/kernel/kernel/files/patches/mageia/stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch b/kernel/kernel/files/patches/mageia.old/stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch rename to kernel/kernel/files/patches/mageia.old/stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch diff --git a/kernel/kernel/files/patches/mageia/stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch b/kernel/kernel/files/patches/mageia.old/stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch rename to kernel/kernel/files/patches/mageia.old/stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch diff --git a/kernel/kernel/files/patches/mageia/stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch b/kernel/kernel/files/patches/mageia.old/stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch rename to kernel/kernel/files/patches/mageia.old/stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch diff --git a/kernel/kernel/files/patches/mageia/stable-asoc-rt5640-fix-line-out-no-sound-issue.patch b/kernel/kernel/files/patches/mageia.old/stable-asoc-rt5640-fix-line-out-no-sound-issue.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-asoc-rt5640-fix-line-out-no-sound-issue.patch rename to kernel/kernel/files/patches/mageia.old/stable-asoc-rt5640-fix-line-out-no-sound-issue.patch diff --git a/kernel/kernel/files/patches/mageia/stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch b/kernel/kernel/files/patches/mageia.old/stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch rename to kernel/kernel/files/patches/mageia.old/stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-pistachio-correct-critical-clock-list.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-pistachio-correct-critical-clock-list.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-pistachio-correct-critical-clock-list.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-pistachio-correct-critical-clock-list.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-pxa-fix-core-frequency-reporting-unit.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-pxa-fix-core-frequency-reporting-unit.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-pxa-fix-core-frequency-reporting-unit.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-pxa-fix-core-frequency-reporting-unit.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-s5pv210-add-missing-call-to.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-s5pv210-add-missing-call-to.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-s5pv210-add-missing-call-to.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-s5pv210-add-missing-call-to.patch diff --git a/kernel/kernel/files/patches/mageia/stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch b/kernel/kernel/files/patches/mageia.old/stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch rename to kernel/kernel/files/patches/mageia.old/stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch diff --git a/kernel/kernel/files/patches/mageia/stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch b/kernel/kernel/files/patches/mageia.old/stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch rename to kernel/kernel/files/patches/mageia.old/stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-i915-preserve-ssc-earlier.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-i915-preserve-ssc-earlier.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-i915-preserve-ssc-earlier.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-i915-preserve-ssc-earlier.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-qxl-validate-monitors-config-modes.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-qxl-validate-monitors-config-modes.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-qxl-validate-monitors-config-modes.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-qxl-validate-monitors-config-modes.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-radeon-atom-send-out-the-full-aux-address.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-radeon-atom-send-out-the-full-aux-address.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-radeon-atom-send-out-the-full-aux-address.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-radeon-atom-send-out-the-full-aux-address.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch diff --git a/kernel/kernel/files/patches/mageia/stable-drm-radeon-native-send-out-the-full-aux-address.patch b/kernel/kernel/files/patches/mageia.old/stable-drm-radeon-native-send-out-the-full-aux-address.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-drm-radeon-native-send-out-the-full-aux-address.patch rename to kernel/kernel/files/patches/mageia.old/stable-drm-radeon-native-send-out-the-full-aux-address.patch diff --git a/kernel/kernel/files/patches/mageia/stable-iio-add-inverse-unit-conversion-macros.patch b/kernel/kernel/files/patches/mageia.old/stable-iio-add-inverse-unit-conversion-macros.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-iio-add-inverse-unit-conversion-macros.patch rename to kernel/kernel/files/patches/mageia.old/stable-iio-add-inverse-unit-conversion-macros.patch diff --git a/kernel/kernel/files/patches/mageia/stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch b/kernel/kernel/files/patches/mageia.old/stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch rename to kernel/kernel/files/patches/mageia.old/stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch diff --git a/kernel/kernel/files/patches/mageia/stable-iio-adis16480-fix-scale-factors.patch b/kernel/kernel/files/patches/mageia.old/stable-iio-adis16480-fix-scale-factors.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-iio-adis16480-fix-scale-factors.patch rename to kernel/kernel/files/patches/mageia.old/stable-iio-adis16480-fix-scale-factors.patch diff --git a/kernel/kernel/files/patches/mageia/stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch b/kernel/kernel/files/patches/mageia.old/stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch rename to kernel/kernel/files/patches/mageia.old/stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch diff --git a/kernel/kernel/files/patches/mageia/stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch b/kernel/kernel/files/patches/mageia.old/stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch rename to kernel/kernel/files/patches/mageia.old/stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch diff --git a/kernel/kernel/files/patches/mageia/stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch b/kernel/kernel/files/patches/mageia.old/stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch rename to kernel/kernel/files/patches/mageia.old/stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch diff --git a/kernel/kernel/files/patches/mageia/stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch b/kernel/kernel/files/patches/mageia.old/stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch rename to kernel/kernel/files/patches/mageia.old/stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch diff --git a/kernel/kernel/files/patches/mageia/stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch b/kernel/kernel/files/patches/mageia.old/stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch rename to kernel/kernel/files/patches/mageia.old/stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch diff --git a/kernel/kernel/files/patches/mageia/stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch b/kernel/kernel/files/patches/mageia.old/stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch rename to kernel/kernel/files/patches/mageia.old/stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch diff --git a/kernel/kernel/files/patches/mageia/stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch b/kernel/kernel/files/patches/mageia.old/stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch rename to kernel/kernel/files/patches/mageia.old/stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch diff --git a/kernel/kernel/files/patches/mageia/stable-pci-fix-ti816x-class-code-quirk.patch b/kernel/kernel/files/patches/mageia.old/stable-pci-fix-ti816x-class-code-quirk.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-pci-fix-ti816x-class-code-quirk.patch rename to kernel/kernel/files/patches/mageia.old/stable-pci-fix-ti816x-class-code-quirk.patch diff --git a/kernel/kernel/files/patches/mageia/stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch b/kernel/kernel/files/patches/mageia.old/stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch rename to kernel/kernel/files/patches/mageia.old/stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch diff --git a/kernel/kernel/files/patches/mageia/stable-s390-sclp-fix-compile-error.patch b/kernel/kernel/files/patches/mageia.old/stable-s390-sclp-fix-compile-error.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-s390-sclp-fix-compile-error.patch rename to kernel/kernel/files/patches/mageia.old/stable-s390-sclp-fix-compile-error.patch diff --git a/kernel/kernel/files/patches/mageia/stable-s390-setup-fix-novx-parameter.patch b/kernel/kernel/files/patches/mageia.old/stable-s390-setup-fix-novx-parameter.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-s390-setup-fix-novx-parameter.patch rename to kernel/kernel/files/patches/mageia.old/stable-s390-setup-fix-novx-parameter.patch diff --git a/kernel/kernel/files/patches/mageia/stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch b/kernel/kernel/files/patches/mageia.old/stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch rename to kernel/kernel/files/patches/mageia.old/stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch diff --git a/kernel/kernel/files/patches/mageia/stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch b/kernel/kernel/files/patches/mageia.old/stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch rename to kernel/kernel/files/patches/mageia.old/stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch diff --git a/kernel/kernel/files/patches/mageia/stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch b/kernel/kernel/files/patches/mageia.old/stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch rename to kernel/kernel/files/patches/mageia.old/stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch diff --git a/kernel/kernel/files/patches/mageia/stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch b/kernel/kernel/files/patches/mageia.old/stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch rename to kernel/kernel/files/patches/mageia.old/stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch diff --git a/kernel/kernel/files/patches/mageia/stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch b/kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch rename to kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch diff --git a/kernel/kernel/files/patches/mageia/stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch b/kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch rename to kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch diff --git a/kernel/kernel/files/patches/mageia/stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch b/kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch rename to kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch diff --git a/kernel/kernel/files/patches/mageia/stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch b/kernel/kernel/files/patches/mageia.old/stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch rename to kernel/kernel/files/patches/mageia.old/stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch diff --git a/kernel/kernel/files/patches/mageia/stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch b/kernel/kernel/files/patches/mageia.old/stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch rename to kernel/kernel/files/patches/mageia.old/stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch diff --git a/kernel/kernel/files/patches/mageia/stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch b/kernel/kernel/files/patches/mageia.old/stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch rename to kernel/kernel/files/patches/mageia.old/stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch diff --git a/kernel/kernel/files/patches/mageia/stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch b/kernel/kernel/files/patches/mageia.old/stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch rename to kernel/kernel/files/patches/mageia.old/stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch diff --git a/kernel/kernel/files/patches/mageia/stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch b/kernel/kernel/files/patches/mageia.old/stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch rename to kernel/kernel/files/patches/mageia.old/stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch diff --git a/kernel/kernel/files/patches/mageia/stable-usb-symbolserial-use-usb_get_serial_port_data.patch b/kernel/kernel/files/patches/mageia.old/stable-usb-symbolserial-use-usb_get_serial_port_data.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-usb-symbolserial-use-usb_get_serial_port_data.patch rename to kernel/kernel/files/patches/mageia.old/stable-usb-symbolserial-use-usb_get_serial_port_data.patch diff --git a/kernel/kernel/files/patches/mageia/stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch b/kernel/kernel/files/patches/mageia.old/stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch rename to kernel/kernel/files/patches/mageia.old/stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch diff --git a/kernel/kernel/files/patches/mageia/stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch b/kernel/kernel/files/patches/mageia.old/stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch rename to kernel/kernel/files/patches/mageia.old/stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch diff --git a/kernel/kernel/files/patches/mageia/stable-x86-ldt-further-fix-fpu-emulation.patch b/kernel/kernel/files/patches/mageia.old/stable-x86-ldt-further-fix-fpu-emulation.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-x86-ldt-further-fix-fpu-emulation.patch rename to kernel/kernel/files/patches/mageia.old/stable-x86-ldt-further-fix-fpu-emulation.patch diff --git a/kernel/kernel/files/patches/mageia/stable-x86-ldt-make-modify_ldt-synchronous.patch b/kernel/kernel/files/patches/mageia.old/stable-x86-ldt-make-modify_ldt-synchronous.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-x86-ldt-make-modify_ldt-synchronous.patch rename to kernel/kernel/files/patches/mageia.old/stable-x86-ldt-make-modify_ldt-synchronous.patch diff --git a/kernel/kernel/files/patches/mageia/stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch b/kernel/kernel/files/patches/mageia.old/stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch rename to kernel/kernel/files/patches/mageia.old/stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch diff --git a/kernel/kernel/files/patches/mageia/stable-xfs-fix-xfs_attr_leafblock-definition.patch b/kernel/kernel/files/patches/mageia.old/stable-xfs-fix-xfs_attr_leafblock-definition.patch similarity index 100% rename from kernel/kernel/files/patches/mageia/stable-xfs-fix-xfs_attr_leafblock-definition.patch rename to kernel/kernel/files/patches/mageia.old/stable-xfs-fix-xfs_attr_leafblock-definition.patch diff --git a/kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id-2.6.37-buildfix.patch b/kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id-2.6.37-buildfix.patch new file mode 100644 index 00000000..fa6655de --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id-2.6.37-buildfix.patch @@ -0,0 +1,46 @@ +--- linux/drivers/usb/storage/unusual_devs.h.orig 2012-12-06 18:26:48.639813048 +0200 ++++ linux/drivers/usb/storage/unusual_devs.h 2012-12-06 18:27:16.570440504 +0200 +@@ -2046,16 +2046,16 @@ UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0 + UNUSUAL_DEV( 0x0584, 0x0001, 0x0100, 0x0102, + "RATOCSystems", + "Compact Flash Adapter", +- US_SC_SCSI, US_PR_BULK, NULL, ++ USB_SC_SCSI, USB_PR_BULK, NULL, + US_FL_SINGLE_LUN | US_FL_MAX_SECTORS_MIN), + + /* + * Panasonic/OEMs compact USB CDROMs status + * KXL-840(CD-ROM11): usb_stor_Bulk_max_lun() is danger, need US_FL_SINGLE_LUN + * KXL-RW11(CDRRW02): usb_stor_Bulk_max_lun() is danger, need US_FL_SINGLE_LUN +- * KXL-RW20(CDRRW03): original IClass is 0xFF, use US_PR_CB and need init reset ++ * KXL-RW20(CDRRW03): original IClass is 0xFF, use USB_PR_CB and need init reset + * KXL-RW31(CDRRW05): work fine with current code +- * KXL-RW21(CDRRW06): original IClass is 0xFF, use US_PR_CB and need init reset ++ * KXL-RW21(CDRRW06): original IClass is 0xFF, use USB_PR_CB and need init reset + * KXL-RW32(CDRRW09): work fine with current code + * KXL-RW40(CDRRW10): work fine with current code + * Checked: Go Taniguchi +@@ -2063,19 +2063,19 @@ UNUSUAL_DEV( 0x0584, 0x0001, 0x0100, 0x0 + UNUSUAL_DEV( 0x04da, 0x0d01, 0x0000, 0xffff, + "Panasonic", + "CD-ROM11", +- US_SC_8020, US_PR_BULK, NULL, US_FL_SINGLE_LUN), ++ USB_SC_8020, USB_PR_BULK, NULL, US_FL_SINGLE_LUN), + UNUSUAL_DEV( 0x04da, 0x0d02, 0x0000, 0xffff, + "Panasonic", + "CDRRW02", +- US_SC_8020, US_PR_BULK, NULL, US_FL_SINGLE_LUN), ++ USB_SC_8020, USB_PR_BULK, NULL, US_FL_SINGLE_LUN), + UNUSUAL_DEV( 0x04da, 0x0d03, 0x0000, 0xffff, + "Panasonic", + "CDRRW03", +- US_SC_8020, US_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), ++ USB_SC_8020, USB_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), + UNUSUAL_DEV( 0x04da, 0x0d06, 0x0000, 0xffff, + "Panasonic", + "CDRRW06", +- US_SC_8020, US_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), ++ USB_SC_8020, USB_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), + + /* Control/Bulk transport for all SubClass values */ + USUAL_DEV(USB_SC_RBC, USB_PR_CB), diff --git a/kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id.patch b/kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id.patch new file mode 100644 index 00000000..b9fc0200 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id.patch @@ -0,0 +1,55 @@ +Subject: Change to usb storage of unusual_dev. + +This patch is japanease unique USB devices quirks. +There are many unique USB CD, which probides by Panasonic. +I try to use all models. all devices worked fine. + +Signed-off-by: Go Taniguchi + +--- + drivers/usb/storage/unusual_devs.h | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +--- linux/drivers/usb/storage/unusual_devs.h.orig 2014-06-03 21:48:03.211099341 +0300 ++++ linux/drivers/usb/storage/unusual_devs.h 2014-06-04 21:57:21.546299167 +0300 +@@ -2105,6 +2105,40 @@ UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0 + #include "unusual_uas.h" + #endif + ++UNUSUAL_DEV( 0x0584, 0x0001, 0x0100, 0x0102, ++ "RATOCSystems", ++ "Compact Flash Adapter", ++ US_SC_SCSI, US_PR_BULK, NULL, ++ US_FL_SINGLE_LUN | US_FL_MAX_SECTORS_MIN), ++ ++/* ++ * Panasonic/OEMs compact USB CDROMs status ++ * KXL-840(CD-ROM11): usb_stor_Bulk_max_lun() is danger, need US_FL_SINGLE_LUN ++ * KXL-RW11(CDRRW02): usb_stor_Bulk_max_lun() is danger, need US_FL_SINGLE_LUN ++ * KXL-RW20(CDRRW03): original IClass is 0xFF, use US_PR_CB and need init reset ++ * KXL-RW31(CDRRW05): work fine with current code ++ * KXL-RW21(CDRRW06): original IClass is 0xFF, use US_PR_CB and need init reset ++ * KXL-RW32(CDRRW09): work fine with current code ++ * KXL-RW40(CDRRW10): work fine with current code ++ * Checked: Go Taniguchi ++ */ ++UNUSUAL_DEV( 0x04da, 0x0d01, 0x0000, 0xffff, ++ "Panasonic", ++ "CD-ROM11", ++ US_SC_8020, US_PR_BULK, NULL, US_FL_SINGLE_LUN), ++UNUSUAL_DEV( 0x04da, 0x0d02, 0x0000, 0xffff, ++ "Panasonic", ++ "CDRRW02", ++ US_SC_8020, US_PR_BULK, NULL, US_FL_SINGLE_LUN), ++UNUSUAL_DEV( 0x04da, 0x0d03, 0x0000, 0xffff, ++ "Panasonic", ++ "CDRRW03", ++ US_SC_8020, US_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), ++UNUSUAL_DEV( 0x04da, 0x0d06, 0x0000, 0xffff, ++ "Panasonic", ++ "CDRRW06", ++ US_SC_8020, US_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), ++ + /* Control/Bulk transport for all SubClass values */ + USUAL_DEV(USB_SC_RBC, USB_PR_CB), + USUAL_DEV(USB_SC_8020, USB_PR_CB), diff --git a/kernel/kernel/files/patches/mageia.old/video-mageia-logo.patch b/kernel/kernel/files/patches/mageia.old/video-mageia-logo.patch new file mode 100644 index 00000000..efc5307e --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/video-mageia-logo.patch @@ -0,0 +1,949 @@ + +Mageia framebuffer boot logo + +Signed-off-by: Thomas Backlund + + drivers/video/logo/Kconfig | 5 + drivers/video/logo/Makefile | 1 + drivers/video/logo/logo.c | 5 + drivers/video/logo/logo_mageia_clut224.ppm | 883 +++++++++++++++++++++++++++++ + include/linux/linux_logo.h | 1 + 5 files changed, 895 insertions(+) + +diff -Nurp linux-2.6.37.3/drivers/video/logo/Kconfig linux-2.6.37.3.mga/drivers/video/logo/Kconfig +--- linux-2.6.37.3/drivers/video/logo/Kconfig 2011-01-05 02:50:19.000000000 +0200 ++++ linux-2.6.37.3.mga/drivers/video/logo/Kconfig 2011-03-08 21:50:08.077611366 +0200 +@@ -82,4 +82,9 @@ config LOGO_M32R_CLUT224 + depends on M32R + default y + ++config LOGO_MAGEIA_CLUT224 ++ bool "224-color Mageia logo" ++ depends on LOGO ++ default y ++ + endif # LOGO +diff -Nurp linux-2.6.37.3/drivers/video/logo/logo.c linux-2.6.37.3.mga/drivers/video/logo/logo.c +--- linux-2.6.37.3/drivers/video/logo/logo.c 2011-01-05 02:50:19.000000000 +0200 ++++ linux-2.6.37.3.mga/drivers/video/logo/logo.c 2011-03-08 21:51:27.042063947 +0200 +@@ -63,6 +63,11 @@ const struct linux_logo * __init_refok f + } + + if (depth >= 8) { ++#ifdef CONFIG_LOGO_MAGEIA_CLUT224 ++ /* Mageia logo */ ++ logo = &logo_mageia_clut224; ++ return logo; ++#endif + #ifdef CONFIG_LOGO_LINUX_CLUT224 + /* Generic Linux logo */ + logo = &logo_linux_clut224; +diff -Nurp linux-2.6.37.3/drivers/video/logo/logo_mageia_clut224.ppm linux-2.6.37.3.mga/drivers/video/logo/logo_mageia_clut224.ppm +--- linux-2.6.37.3/drivers/video/logo/logo_mageia_clut224.ppm 1970-01-01 02:00:00.000000000 +0200 ++++ linux-2.6.37.3.mga/drivers/video/logo/logo_mageia_clut224.ppm 2011-03-08 23:47:47.000000000 +0200 +@@ -0,0 +1,883 @@ ++P3 ++80 80 ++255 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 11 16 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 9 14 29 ++9 14 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ ++0 0 0 0 0 0 0 0 0 0 0 0 8 23 42 32 55 114 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 37 56 117 ++37 56 117 37 56 117 18 45 91 9 14 29 0 0 0 0 0 0 0 0 0 0 0 0 ++ ++0 0 0 0 0 0 0 0 0 18 45 91 37 56 117 37 56 117 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 36 57 118 ++36 57 118 36 57 118 37 56 117 36 57 118 17 36 71 0 0 0 0 0 0 0 0 0 ++ ++0 0 0 3 11 16 18 45 91 35 58 120 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 35 58 119 ++35 58 119 35 58 119 35 58 119 35 58 119 35 58 120 17 36 71 0 0 0 0 0 0 ++ ++0 0 0 11 38 58 37 56 117 34 59 122 35 58 119 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 ++35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 35 58 120 9 14 29 0 0 0 ++ ++0 0 0 32 55 114 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 35 58 119 63 96 153 63 96 153 63 96 153 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 ++34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 34 59 122 18 45 91 0 0 0 ++ ++8 23 42 34 59 122 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 125 157 204 191 220 251 194 222 252 191 218 250 108 152 201 ++32 55 114 32 62 125 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 ++33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 33 60 123 34 59 122 0 0 0 ++ ++11 38 58 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 62 125 63 96 153 170 209 245 168 209 245 168 209 245 170 209 245 164 205 243 ++63 96 153 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 ++32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 61 124 32 62 125 8 16 31 ++ ++17 36 71 33 60 123 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 86 129 181 148 198 240 145 197 239 145 197 239 145 197 239 145 197 239 ++63 96 153 32 61 124 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 ++32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 32 62 125 8 16 31 ++ ++11 38 58 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 ++31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 ++31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 ++31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 ++31 63 126 30 65 129 63 96 153 123 185 232 123 185 232 123 185 232 123 185 232 122 182 230 ++41 91 154 31 63 126 31 63 126 31 63 126 31 63 126 63 96 153 40 74 135 31 63 126 ++31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 ++31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 ++31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 ++31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 31 63 126 8 16 31 ++ ++17 36 71 31 63 126 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 ++30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 ++30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 ++30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 ++30 64 128 30 64 128 30 65 129 85 139 195 97 172 225 100 173 226 100 173 226 85 139 195 ++32 61 124 30 64 128 30 64 128 86 129 181 184 213 246 201 225 254 201 225 254 142 183 225 ++40 74 135 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 ++30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 ++30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 ++30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 30 64 128 8 16 31 ++ ++17 36 71 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 ++30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 ++30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 ++30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 ++30 65 129 30 65 129 29 66 130 31 63 126 58 114 175 58 114 175 41 91 154 29 66 130 ++30 65 129 28 68 132 40 74 135 178 210 243 176 212 247 173 211 246 176 212 247 176 212 247 ++125 157 204 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 ++30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 ++30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 ++30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 30 65 129 8 16 31 ++ ++17 36 71 30 64 128 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 ++29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 ++29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 ++29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 ++29 66 130 29 66 130 29 66 130 29 66 130 30 64 128 28 67 131 29 66 130 29 66 130 ++29 66 130 29 66 130 85 139 195 158 203 242 154 201 241 155 202 241 155 202 241 155 202 241 ++154 201 241 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 ++29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 ++29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 ++29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 29 66 130 6 18 34 ++ ++17 36 71 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 86 129 181 134 191 236 134 191 236 134 191 236 134 191 236 134 191 236 ++134 191 236 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 ++28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 28 67 131 6 18 34 ++ ++17 36 71 28 67 131 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 40 74 135 117 182 230 115 181 230 115 181 230 115 181 230 115 181 230 ++85 139 195 27 69 133 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 ++28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 28 68 132 6 18 34 ++ ++17 36 71 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 ++27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 ++27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 ++27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 ++27 69 133 27 69 133 27 69 133 27 69 133 26 71 136 63 96 153 108 152 201 142 183 225 ++86 129 181 27 69 133 27 69 133 58 114 175 95 170 224 95 170 224 97 172 225 77 152 208 ++41 91 154 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 ++27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 ++27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 ++27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 27 69 133 6 18 34 ++ ++11 38 71 27 69 133 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 ++26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 ++26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 ++26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 ++26 70 135 26 70 135 26 70 135 26 70 135 86 129 181 197 223 253 188 217 249 188 217 249 ++191 220 251 152 193 232 40 74 135 26 70 135 23 75 141 41 91 154 41 91 154 26 70 135 ++27 69 133 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 ++26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 ++26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 ++26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 26 70 135 6 18 34 ++ ++11 38 71 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 ++26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 ++26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 ++26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 ++26 71 136 26 71 136 26 70 135 41 91 154 172 210 246 170 209 245 170 209 245 170 209 245 ++168 209 245 178 210 243 85 139 195 26 71 136 26 71 136 25 72 137 26 71 136 26 71 136 ++26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 ++26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 ++26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 ++26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 26 71 136 6 18 34 ++ ++11 38 71 26 71 136 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 86 129 181 151 200 240 152 200 240 152 200 240 152 200 240 ++152 200 240 151 200 240 122 172 221 27 69 133 24 73 138 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 ++25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 25 72 137 6 18 34 ++ ++11 38 71 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 25 72 137 58 114 175 134 191 236 134 191 236 134 191 236 134 191 236 ++134 191 236 134 191 236 108 152 201 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 ++24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 24 73 138 6 18 34 ++ ++11 38 71 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 41 91 154 115 181 230 115 181 230 115 181 230 115 181 230 ++115 181 230 115 181 230 60 128 188 24 73 138 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 ++23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 23 74 139 5 19 36 ++ ++11 38 71 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 ++23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 ++23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 ++23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 ++23 75 141 23 75 141 23 75 141 23 74 139 58 114 175 97 172 225 97 172 225 97 172 225 ++97 172 225 77 152 208 24 73 138 23 75 141 41 91 154 125 157 204 152 193 232 142 183 225 ++86 129 181 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 ++23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 ++23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 ++23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 23 75 141 5 19 36 ++ ++11 38 71 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 ++22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 ++22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 ++22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 ++22 76 142 22 76 142 22 76 142 22 76 142 21 76 143 30 98 155 58 114 175 60 128 188 ++42 114 176 23 74 139 21 76 143 63 96 153 176 212 247 188 218 250 188 218 250 188 217 249 ++191 218 250 125 157 204 21 77 144 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 ++22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 ++22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 ++22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 22 76 142 5 19 36 ++ ++11 38 71 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 ++21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 ++21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 ++21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 23 75 141 ++58 114 175 125 157 204 152 193 232 108 152 201 41 91 154 21 77 144 21 77 144 21 76 143 ++21 76 143 21 76 143 21 76 143 131 179 222 172 210 246 172 210 246 172 210 246 172 210 246 ++172 210 246 170 209 245 86 129 181 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 ++21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 ++21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 ++21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 21 76 143 5 19 36 ++ ++11 38 71 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 ++21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 ++21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 ++21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 125 157 204 ++194 222 252 191 220 251 191 220 251 191 220 251 184 213 246 86 129 181 21 77 144 21 77 144 ++21 77 144 21 77 144 22 89 156 155 202 241 155 202 242 155 202 242 155 202 242 155 202 242 ++155 202 242 155 202 242 122 172 221 21 76 143 21 77 144 21 77 144 21 77 144 21 77 144 ++21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 ++21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 ++21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 21 77 144 5 19 36 ++ ++11 38 71 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 ++20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 ++20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 ++20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 21 77 144 85 139 195 176 212 247 ++176 212 247 176 212 247 176 212 247 176 212 247 176 212 247 164 205 243 41 91 154 20 78 145 ++20 78 145 20 78 145 41 91 154 140 193 237 140 193 237 140 193 237 140 193 237 140 193 237 ++137 192 236 143 192 235 100 169 221 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 ++20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 ++20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 ++20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 20 78 145 5 19 36 ++ ++11 38 71 19 80 148 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 ++19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 ++19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 ++19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 131 179 222 161 204 243 ++160 204 243 160 204 243 160 204 243 160 204 243 160 204 243 160 204 243 85 139 195 19 79 147 ++19 79 147 19 79 147 19 79 147 111 178 227 123 185 232 121 184 232 122 185 232 122 185 232 ++122 185 232 122 185 232 85 139 195 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 ++19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 ++19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 ++19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 19 79 147 5 19 36 ++ ++11 38 71 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 ++19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 ++19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 ++19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 145 196 239 145 196 239 ++145 196 239 145 196 239 145 196 239 145 196 239 145 196 239 145 196 239 85 139 195 19 80 148 ++19 80 148 19 80 148 19 79 147 60 128 188 106 176 228 106 176 228 106 176 228 106 176 228 ++106 176 228 100 172 225 22 89 156 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 ++19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 ++19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 ++19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 19 80 148 5 19 36 ++ ++11 38 71 17 82 150 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 ++18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 ++18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 ++18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 111 178 227 132 189 234 ++130 189 235 130 189 235 130 189 235 130 189 235 130 189 235 130 189 235 85 139 195 17 82 150 ++18 81 149 18 81 149 18 81 149 18 81 149 42 114 176 88 166 221 87 167 223 89 168 223 ++77 152 208 30 98 155 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 ++18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 ++18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 ++18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 18 81 149 5 19 36 ++ ++11 38 71 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 ++17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 ++17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 ++17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 16 83 151 85 139 195 115 181 230 ++115 181 230 115 181 230 115 181 230 115 181 230 117 182 230 111 178 227 42 114 176 17 82 150 ++17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 18 81 149 23 96 163 22 89 156 ++18 81 149 16 83 151 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 ++17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 ++17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 ++17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 17 82 150 4 21 38 ++ ++9 48 78 17 82 150 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 22 89 156 83 162 217 ++100 172 225 100 173 226 100 173 226 100 173 226 100 173 226 62 142 198 19 79 147 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 ++16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 16 83 151 4 21 38 ++ ++9 48 78 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 14 87 156 ++60 128 188 77 152 208 84 165 222 65 151 210 42 114 176 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 ++16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 16 84 153 4 21 38 ++ ++9 48 78 16 84 153 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 58 114 175 42 114 176 16 84 153 15 85 154 15 85 154 ++15 85 154 14 86 155 15 85 154 16 84 153 15 85 154 15 85 154 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 58 114 175 42 114 176 16 83 151 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 ++15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 15 85 154 4 21 38 ++ ++9 48 78 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 ++14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 ++14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 ++15 85 154 42 114 176 178 210 243 204 226 255 204 226 255 178 210 243 42 114 176 14 86 155 ++14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 ++14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 ++14 86 155 85 139 195 191 218 250 204 226 255 201 225 254 142 183 225 23 96 163 14 86 155 ++14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 ++14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 ++14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 14 86 155 4 21 38 ++ ++9 48 78 14 86 155 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 ++14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 ++14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 ++42 114 176 191 218 250 201 225 254 201 225 254 201 225 254 201 225 254 201 225 254 108 152 201 ++13 88 157 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 ++14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 23 96 163 ++142 183 225 201 225 254 201 225 254 201 225 254 201 225 254 201 225 254 163 199 237 23 96 163 ++14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 ++14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 ++14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 14 87 156 4 21 38 ++ ++9 48 78 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 ++13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 ++13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 14 87 156 42 114 176 ++184 213 246 197 223 253 197 223 253 197 223 253 197 223 253 197 223 253 197 223 253 196 222 253 ++178 210 243 60 128 188 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 ++13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 23 96 163 91 153 206 184 213 246 ++197 223 253 197 223 253 197 223 253 197 223 253 197 223 253 197 223 253 197 223 253 163 199 237 ++23 96 163 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 ++13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 ++13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 13 88 157 4 21 38 ++ ++9 48 78 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 ++12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 ++12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 28 113 174 184 213 246 ++194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 ++194 222 252 196 222 253 172 207 243 125 157 204 60 128 188 13 88 157 12 89 158 12 89 158 ++12 89 158 12 89 158 23 96 163 60 128 188 122 172 221 184 213 246 194 222 252 194 222 252 ++194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 194 222 252 ++163 199 237 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 ++12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 ++12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 12 89 158 4 21 38 ++ ++9 48 78 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 ++12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 ++12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 13 88 157 163 199 237 191 220 251 ++191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 ++191 220 251 191 220 251 191 220 251 188 218 250 191 218 250 191 220 251 178 210 243 152 193 232 ++142 183 225 191 220 251 191 220 251 194 222 252 191 220 251 191 220 251 191 220 251 191 220 251 ++191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 191 220 251 ++185 217 250 125 157 204 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 ++12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 ++12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 12 90 160 1 24 42 ++ ++9 48 78 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 ++11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 ++11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 91 153 206 188 218 250 188 218 250 ++188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 ++188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 ++188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 ++188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 188 218 250 ++188 218 250 188 218 250 60 128 188 12 89 158 11 91 161 11 91 161 11 91 161 11 91 161 ++11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 ++11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 11 91 161 1 24 42 ++ ++9 48 78 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 ++10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 ++10 92 162 10 92 162 10 92 162 11 91 161 28 113 174 188 218 250 185 217 250 185 217 250 ++185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 ++185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 ++185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 ++185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 185 217 250 ++185 217 250 185 217 250 163 199 237 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 ++10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 ++10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 10 92 162 1 24 42 ++ ++2 50 86 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 ++9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 ++9 92 163 9 92 163 9 92 163 9 92 163 122 172 221 179 214 248 182 215 249 182 215 249 ++182 215 249 182 215 249 182 215 249 182 215 249 152 193 232 182 215 249 182 215 249 182 215 249 ++182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 ++182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 182 215 249 ++182 215 249 182 215 249 172 207 243 163 199 237 182 215 249 182 215 249 182 215 249 182 215 249 ++182 215 249 182 215 249 182 215 249 85 139 195 8 94 166 9 92 163 9 92 163 9 92 163 ++9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 ++9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 9 92 163 1 24 42 ++ ++9 48 78 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 ++9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 ++9 93 164 9 93 164 9 92 163 28 113 174 179 214 248 179 214 248 179 214 248 179 214 248 ++179 214 248 179 214 248 179 214 248 122 172 221 11 91 161 40 129 186 142 183 225 179 214 248 ++179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 ++179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 179 214 248 ++176 212 247 122 172 221 28 113 174 18 102 171 163 199 237 179 214 248 179 214 248 179 214 248 ++179 214 248 179 214 248 179 214 248 154 201 241 9 93 164 9 93 164 9 93 164 9 93 164 ++9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 ++9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 9 93 164 1 24 42 ++ ++2 50 86 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 ++8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 ++8 94 166 8 94 166 8 94 166 107 172 224 178 210 243 176 212 247 176 212 247 176 212 247 ++176 212 247 176 212 247 164 205 243 18 102 171 8 94 166 8 94 166 8 94 166 28 113 174 ++92 162 218 163 199 237 173 211 246 176 212 247 176 212 247 176 212 247 176 212 247 176 212 247 ++176 212 247 176 212 247 176 212 247 176 212 247 176 212 247 176 212 247 143 192 235 85 139 195 ++18 102 171 8 94 166 8 94 166 8 94 166 40 129 186 176 212 247 176 212 247 176 212 247 ++176 212 247 176 212 247 176 212 247 176 212 247 62 142 198 9 93 164 8 94 166 8 94 166 ++8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 ++8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 8 94 166 1 24 42 ++ ++2 50 86 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 ++7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 ++7 95 167 7 95 167 8 94 166 161 204 243 173 211 246 173 211 246 173 211 246 173 211 246 ++172 210 246 178 210 243 62 142 198 7 95 167 7 95 167 7 95 167 7 95 167 8 94 166 ++8 94 166 8 94 166 28 119 185 85 139 195 92 162 218 131 179 222 131 179 222 131 179 222 ++131 179 222 131 179 222 131 179 222 91 153 206 62 142 198 28 113 174 7 95 167 7 96 168 ++7 95 167 7 95 167 7 95 167 7 96 168 9 93 164 107 172 224 173 211 246 173 211 246 ++173 211 246 173 211 246 173 211 246 173 211 246 122 172 221 7 95 167 7 95 167 7 95 167 ++7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 ++7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 7 95 167 1 24 42 ++ ++2 50 86 7 95 167 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 ++7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 ++7 96 168 7 96 168 60 128 188 170 209 245 170 209 245 170 209 245 170 209 245 170 209 245 ++170 209 245 145 196 239 8 94 166 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 ++7 96 168 7 96 168 7 96 168 6 97 169 7 96 168 7 96 168 6 97 169 7 96 168 ++6 97 169 7 96 168 6 97 169 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 ++7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 28 113 174 170 209 245 170 209 245 ++170 209 245 170 209 245 170 209 245 170 209 245 170 209 245 18 102 171 7 96 168 7 96 168 ++7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 ++7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 7 96 168 1 24 42 ++ ++2 50 86 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 ++6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 ++6 97 169 7 96 168 92 162 218 167 207 244 167 207 244 167 207 244 167 207 244 167 207 244 ++167 207 244 91 153 206 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 ++6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 ++6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 ++6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 5 99 172 131 179 222 167 207 244 ++167 207 244 167 207 244 167 207 244 167 207 244 168 209 245 60 128 188 6 97 169 6 97 169 ++6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 ++6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 6 97 169 1 24 42 ++ ++2 50 86 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 ++5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 ++5 98 170 5 98 170 132 189 234 164 205 243 164 205 243 164 205 243 164 205 243 164 205 243 ++164 205 243 28 113 174 5 99 172 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 ++5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 ++5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 ++5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 62 142 198 164 205 243 ++164 205 243 164 205 243 164 205 243 164 205 243 164 205 243 92 162 218 5 98 170 5 98 170 ++5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 ++5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 5 98 170 1 24 42 ++ ++2 50 86 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 ++5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 ++5 99 172 5 99 172 161 204 243 161 204 243 161 204 243 161 204 243 161 204 243 161 204 243 ++139 191 235 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 ++5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 ++5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 ++5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 4 100 173 28 113 174 161 204 243 ++161 204 243 161 204 243 161 204 243 161 204 243 161 204 243 122 172 221 5 99 172 5 99 172 ++5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 ++5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 5 99 172 1 24 42 ++ ++2 50 86 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 ++4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 ++4 100 173 28 119 185 160 204 243 158 203 242 158 203 242 158 203 242 158 203 242 158 203 242 ++100 169 221 5 98 170 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 ++4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 ++4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 ++4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 3 101 174 139 191 235 ++158 203 242 158 203 242 158 203 242 158 203 242 158 203 242 148 198 240 4 100 173 4 100 173 ++4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 ++4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 4 100 173 1 24 42 ++ ++2 50 86 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 ++3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 ++3 101 174 40 129 186 154 201 241 154 201 241 154 201 241 154 201 241 154 201 241 154 201 241 ++77 152 208 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 ++3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 ++3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 ++3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 5 99 172 111 178 227 ++154 201 241 154 201 241 154 201 241 154 201 241 154 201 241 154 201 241 3 101 174 3 101 174 ++3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 ++3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 3 101 174 0 26 44 ++ ++2 50 86 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 ++3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 ++3 102 175 40 129 186 151 200 240 151 200 240 151 200 240 151 200 240 151 200 240 151 200 240 ++62 142 198 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 ++3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 ++3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 ++3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 107 172 224 ++151 200 240 151 200 240 151 200 240 151 200 240 151 200 240 151 200 240 3 102 175 3 102 175 ++3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 ++3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 3 102 175 0 26 44 ++ ++2 50 86 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 ++2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 ++1 104 177 40 129 186 148 198 240 148 198 240 148 198 240 148 198 240 148 198 240 148 198 240 ++40 129 186 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 ++2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 ++2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 ++2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 77 152 208 ++148 198 240 148 198 240 148 198 240 148 198 240 148 198 240 148 198 240 2 103 176 2 103 176 ++2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 ++2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 2 103 176 0 26 44 ++ ++2 50 86 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 ++1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 ++1 104 177 40 129 186 145 196 239 145 197 239 145 197 239 145 197 239 145 197 239 145 197 239 ++51 141 199 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 ++1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 ++1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 ++1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 100 169 221 ++145 196 239 145 197 239 145 197 239 145 197 239 145 197 239 145 197 239 1 104 177 1 104 177 ++1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 ++1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 1 104 177 0 26 44 ++ ++2 50 86 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 ++0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 ++1 106 180 27 129 189 145 196 239 140 193 237 140 193 237 140 193 237 140 193 237 140 193 237 ++77 152 208 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 ++0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 ++0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 ++0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 107 172 224 ++145 196 239 140 193 237 140 193 237 140 193 237 140 193 237 140 193 237 0 105 179 0 105 179 ++0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 ++0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 105 179 0 26 44 ++ ++4 57 93 0 105 179 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 ++1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 ++1 106 180 28 119 185 140 193 237 140 193 237 140 193 237 140 193 237 140 193 237 140 193 237 ++83 162 217 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 ++1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 ++1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 ++1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 122 182 230 ++140 193 237 140 193 237 140 193 237 140 193 237 140 193 237 132 189 234 1 106 180 1 106 180 ++1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 ++1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 1 106 180 0 26 44 ++ ++4 57 93 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 ++2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 ++2 108 181 2 108 181 137 192 236 137 192 236 137 192 236 137 192 236 137 192 236 137 192 236 ++111 178 227 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 ++2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 ++2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 ++2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 8 115 186 137 192 236 ++137 192 236 137 192 236 137 192 236 137 192 236 137 192 236 100 169 221 2 108 181 2 108 181 ++2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 ++2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 2 108 181 0 26 44 ++ ++4 57 93 2 108 181 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 ++4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 ++4 110 182 4 110 182 107 172 224 134 191 236 134 191 236 134 191 236 134 191 236 134 191 236 ++132 189 234 17 119 189 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 ++4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 ++4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 ++4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 51 141 199 134 191 236 ++134 191 236 134 191 236 134 191 236 134 191 236 134 191 236 77 152 208 4 110 182 4 110 182 ++4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 ++4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 4 110 182 0 26 44 ++ ++4 57 93 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 ++5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 ++5 112 184 5 112 184 83 162 217 130 189 234 130 189 234 130 189 234 130 189 234 130 189 234 ++127 187 234 65 151 210 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 ++5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 ++5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 ++5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 92 162 218 130 189 234 ++130 189 234 130 189 234 130 189 234 130 189 234 130 189 235 51 141 199 5 112 184 5 112 184 ++5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 ++5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 5 112 184 6 113 185 0 26 44 ++ ++4 57 93 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 ++6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 ++6 113 185 6 113 185 42 139 203 127 187 234 127 187 234 127 187 234 127 187 234 127 187 234 ++127 187 234 111 178 227 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 ++6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 ++6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 ++6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 19 124 193 127 187 234 127 187 234 ++127 187 234 127 187 234 127 187 234 127 187 234 127 187 234 17 119 189 6 113 185 6 113 185 ++6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 ++6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 6 113 185 3 30 48 ++ ++4 57 93 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 ++8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 ++8 115 186 8 115 186 17 119 189 117 182 230 124 186 233 124 186 233 124 186 233 124 186 233 ++124 186 233 124 186 233 51 141 199 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 ++8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 ++8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 ++8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 79 161 217 124 186 233 124 186 233 ++124 186 233 124 186 233 124 186 233 124 186 233 95 168 221 8 115 186 8 115 186 8 115 186 ++8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 ++8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 8 115 186 3 30 48 ++ ++4 57 93 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 ++9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 ++9 117 188 9 117 188 9 117 188 79 161 217 121 184 232 121 184 232 121 184 232 121 184 232 ++121 184 232 121 184 232 106 176 228 17 119 189 9 117 188 9 117 188 9 117 188 9 117 188 ++9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 ++9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 ++9 117 188 9 117 188 9 117 188 9 117 188 27 133 194 122 182 230 121 184 232 121 184 232 ++121 184 232 121 184 232 121 184 232 121 184 232 51 141 199 9 117 188 9 117 188 9 117 188 ++9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 ++9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 9 117 188 3 30 48 ++ ++4 57 93 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 ++11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 ++11 119 189 11 119 189 11 119 189 27 133 194 117 182 230 117 182 230 117 182 230 117 182 230 ++117 182 230 117 182 230 117 182 230 79 161 217 11 119 189 11 119 189 11 119 189 11 119 189 ++11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 ++11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 ++11 119 189 11 119 189 11 119 189 17 119 189 100 172 225 117 182 230 117 182 230 117 182 230 ++117 182 230 117 182 230 117 182 230 111 178 227 11 119 189 11 119 189 11 119 189 11 119 189 ++11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 ++11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 11 119 189 3 30 48 ++ ++4 57 93 13 122 191 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 ++12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 ++12 121 190 12 121 190 12 121 190 13 122 191 88 166 221 115 181 230 115 181 230 115 181 230 ++115 181 230 115 181 230 115 181 230 115 181 230 51 141 199 13 122 191 12 121 190 12 121 190 ++12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 ++12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 ++12 121 190 12 121 190 12 121 190 79 161 217 115 181 230 115 181 230 115 181 230 115 181 230 ++115 181 230 115 181 230 115 181 230 65 151 210 12 121 190 12 121 190 12 121 190 12 121 190 ++12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 ++12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 12 121 190 3 30 48 ++ ++4 57 93 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 ++13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 ++13 122 191 13 122 191 13 122 191 13 122 191 27 133 194 112 180 229 112 180 229 112 180 229 ++112 180 229 112 180 229 112 180 229 112 180 229 112 180 229 51 141 199 13 122 191 13 122 191 ++13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 ++13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 ++13 122 191 12 121 190 79 161 217 112 180 229 112 180 229 112 180 229 112 180 229 112 180 229 ++112 180 229 112 180 229 100 172 225 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 ++13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 ++13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 13 122 191 3 30 48 ++ ++12 68 101 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 ++15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 ++15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 65 151 210 109 178 229 109 178 229 ++109 178 229 109 178 229 109 178 229 109 178 229 109 178 229 109 178 229 65 151 210 15 124 193 ++15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 ++15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 ++17 128 195 79 161 217 109 178 229 109 178 229 109 178 229 109 178 229 109 178 229 109 178 229 ++109 178 229 109 178 229 42 139 203 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 ++15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 ++15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 15 124 193 3 30 48 ++ ++4 57 93 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 ++16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 ++16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 89 168 223 106 176 228 ++106 176 228 106 176 228 106 176 228 106 176 228 106 176 228 106 176 228 106 176 228 81 164 221 ++27 133 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 ++16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 42 139 203 ++94 170 224 106 176 228 106 176 228 106 176 228 106 176 228 106 176 228 106 176 228 106 176 228 ++106 176 228 65 151 210 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 ++16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 ++16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 16 126 194 3 30 48 ++ ++12 68 101 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 ++17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 ++17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 28 137 198 97 172 225 ++103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 ++103 175 227 65 151 210 28 137 198 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 ++17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 42 139 203 79 161 217 103 175 227 ++103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 103 175 227 ++88 166 221 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 ++17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 ++17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 17 128 195 3 30 48 ++ ++12 68 101 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 ++19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 ++19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 31 139 202 ++95 170 224 100 173 226 100 173 226 100 173 226 100 173 226 100 173 226 100 173 226 100 173 226 ++100 173 226 100 173 226 100 173 226 81 164 221 65 151 210 42 139 203 42 139 203 19 130 197 ++22 132 197 42 139 203 42 139 203 65 151 210 89 168 223 100 173 226 100 173 226 100 173 226 ++100 173 226 100 173 226 100 173 226 100 173 226 100 173 226 100 173 226 100 173 226 84 165 222 ++22 132 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 ++19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 ++19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 19 130 197 20 131 198 3 30 48 ++ ++12 68 101 19 130 197 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 ++20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 ++20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 ++31 139 202 93 169 223 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 ++97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 ++97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 ++97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 97 172 225 81 164 221 23 135 201 ++20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 ++20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 ++20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 20 131 198 3 30 48 ++ ++12 68 101 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 ++21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 ++21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 ++23 135 201 31 139 202 89 168 223 94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 ++94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 ++94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 ++94 170 224 94 170 224 94 170 224 94 170 224 94 170 224 81 164 221 24 137 202 21 133 199 ++21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 ++21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 ++21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 21 133 199 23 135 201 3 30 48 ++ ++12 68 101 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 ++23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 ++23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 ++22 130 192 21 125 186 28 122 176 77 152 208 89 168 223 90 169 224 90 169 224 90 169 224 ++90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 ++90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 90 169 224 ++90 169 224 90 169 224 90 169 224 90 169 224 62 142 198 17 118 175 21 125 186 22 132 197 ++23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 ++23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 ++23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 23 135 201 3 30 48 ++ ++12 68 101 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 ++24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 ++24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 22 130 192 17 118 175 21 111 162 ++21 111 162 21 111 162 21 111 162 21 111 162 40 129 186 83 162 217 87 167 223 87 167 223 ++87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 ++87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 87 167 223 ++87 167 223 87 167 223 77 152 208 28 122 176 21 111 162 21 111 162 21 111 162 21 111 162 ++22 114 165 17 118 175 22 132 197 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 ++24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 ++24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 24 137 202 26 139 203 3 30 48 ++ ++12 68 101 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 ++26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 ++26 139 203 26 139 203 26 139 203 26 139 203 28 122 176 21 111 162 21 111 162 21 111 162 ++21 111 162 21 111 162 21 111 162 21 111 162 21 111 162 24 115 166 51 141 199 79 161 217 ++84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 ++84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 84 166 222 ++79 161 217 40 129 186 22 112 163 21 111 162 21 111 162 21 111 162 21 111 162 21 111 162 ++21 111 162 22 112 163 21 111 162 22 130 192 26 139 203 26 139 203 26 139 203 26 139 203 ++26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 ++26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 26 139 203 3 30 48 ++ ++9 48 78 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 ++27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 ++27 140 204 27 140 204 27 140 204 27 140 204 17 118 175 22 112 163 22 112 163 22 112 163 ++22 112 163 22 112 163 22 112 163 22 112 163 22 112 163 22 112 163 22 112 163 21 111 162 ++40 129 186 62 142 198 79 161 217 81 164 221 81 164 221 81 164 221 81 164 221 81 164 221 ++81 164 221 81 164 221 81 164 221 81 164 221 81 164 221 79 161 217 62 142 198 28 122 176 ++22 112 163 22 112 163 22 112 163 22 112 163 22 112 163 22 112 163 22 112 163 22 112 163 ++22 112 163 22 112 163 22 112 163 21 125 186 27 140 204 27 140 204 27 140 204 27 140 204 ++27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 ++27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 27 140 204 4 18 26 ++ ++4 18 26 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 ++28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 ++28 142 206 28 142 206 28 142 206 28 142 206 28 137 198 28 122 176 22 114 165 22 114 165 ++22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 ++22 114 165 22 114 165 22 114 165 28 122 176 40 129 186 51 141 199 51 141 199 51 141 199 ++51 141 199 51 141 199 51 141 199 28 122 176 28 122 176 22 112 163 22 114 165 22 114 165 ++22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 22 114 165 ++22 114 165 22 114 165 28 122 176 27 140 204 28 142 206 28 142 206 28 142 206 28 142 206 ++28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 ++28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 142 206 28 122 176 0 0 0 ++ ++0 0 0 19 91 130 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 ++30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 ++30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 28 140 203 27 129 189 ++28 122 176 25 117 166 24 115 166 24 115 166 24 115 166 24 115 166 24 115 166 24 115 166 ++24 115 166 24 115 166 24 115 166 24 115 166 24 115 166 22 112 163 24 115 166 22 112 163 ++24 115 166 22 112 163 24 115 166 25 117 166 24 115 166 24 115 166 24 115 166 24 115 166 ++24 115 166 24 115 166 24 115 166 24 115 166 24 115 166 22 114 165 28 122 176 28 122 176 ++27 133 194 29 142 204 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 ++30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 ++30 144 207 30 144 207 30 144 207 30 144 207 30 144 207 31 146 208 9 48 78 0 0 0 ++ ++0 0 0 4 18 26 28 137 198 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 ++31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 ++31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 ++31 146 208 32 148 210 30 144 206 28 137 198 27 133 194 27 129 189 27 129 189 28 122 176 ++28 122 176 28 122 176 25 117 166 25 117 166 25 117 166 25 117 166 25 117 166 25 117 166 ++25 117 166 25 117 166 25 117 166 25 117 166 25 117 166 25 117 166 28 122 176 28 122 176 ++28 122 176 27 129 189 27 129 189 28 137 198 28 137 198 31 146 208 31 146 208 31 146 208 ++31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 ++31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 ++31 146 208 31 146 208 31 146 208 31 146 208 31 146 208 25 117 166 0 0 0 0 0 0 ++ ++0 0 0 0 0 0 8 23 42 28 137 198 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 32 148 210 ++32 148 210 32 148 210 32 148 210 32 148 210 25 117 166 3 11 16 0 0 0 0 0 0 ++ ++0 0 0 0 0 0 0 0 0 4 18 26 19 91 130 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 34 149 211 ++34 149 211 34 149 211 31 139 202 18 76 106 3 11 16 0 0 0 0 0 0 0 0 0 ++ ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 18 26 9 48 78 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 18 76 106 ++18 76 106 11 38 58 3 11 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++ +diff -Nurp linux-2.6.37.3/drivers/video/logo/Makefile linux-2.6.37.3.mga/drivers/video/logo/Makefile +--- linux-2.6.37.3/drivers/video/logo/Makefile 2011-01-05 02:50:19.000000000 +0200 ++++ linux-2.6.37.3.mga/drivers/video/logo/Makefile 2011-03-08 21:51:35.728113700 +0200 +@@ -15,6 +15,7 @@ obj-$(CONFIG_LOGO_SUPERH_MONO) += logo_ + obj-$(CONFIG_LOGO_SUPERH_VGA16) += logo_superh_vga16.o + obj-$(CONFIG_LOGO_SUPERH_CLUT224) += logo_superh_clut224.o + obj-$(CONFIG_LOGO_M32R_CLUT224) += logo_m32r_clut224.o ++obj-$(CONFIG_LOGO_MAGEIA_CLUT224) += logo_mageia_clut224.o + + obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o + +diff -Nurp linux-2.6.37.3/include/linux/linux_logo.h linux-2.6.37.3.mga/include/linux/linux_logo.h +--- linux-2.6.37.3/include/linux/linux_logo.h 2011-01-05 02:50:19.000000000 +0200 ++++ linux-2.6.37.3.mga/include/linux/linux_logo.h 2011-03-08 21:51:20.263024073 +0200 +@@ -47,6 +47,7 @@ extern const struct linux_logo logo_supe + extern const struct linux_logo logo_superh_clut224; + extern const struct linux_logo logo_m32r_clut224; + extern const struct linux_logo logo_spe_clut224; ++extern const struct linux_logo logo_mageia_clut224; + + extern const struct linux_logo *fb_find_logo(int depth); + #ifdef CONFIG_FB_LOGO_EXTRA diff --git a/kernel/kernel/files/patches/mageia.old/x86-boot-video-80x25-if-break.patch b/kernel/kernel/files/patches/mageia.old/x86-boot-video-80x25-if-break.patch new file mode 100644 index 00000000..e7e92f47 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/x86-boot-video-80x25-if-break.patch @@ -0,0 +1,24 @@ +Automatically switches textmode if the user selects a wrong one. + +Original author of the idea/first patch is unknown. + +Signed-off-by: Herton Ronaldo Krzesinski +Signed-off-by: Luiz Fernando N. Capitulino + +-- + arch/x86/boot/video.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/x86/boot/video.c ++++ b/arch/x86/boot/video.c +@@ -325,8 +325,8 @@ void set_video(void) + if (!set_mode(mode)) + break; + +- printf("Undefined video mode number: %x\n", mode); +- mode = ASK_VGA; ++ printf("Undefined video mode number: %x, jump to 80x25\n", mode); ++ mode = VIDEO_80x25; + } + boot_params.hdr.vid_mode = mode; + vesa_store_edid(); diff --git a/kernel/kernel/files/patches/mageia.old/x86-default_poweroff_up_machines.patch b/kernel/kernel/files/patches/mageia.old/x86-default_poweroff_up_machines.patch new file mode 100644 index 00000000..28ce1a0d --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/x86-default_poweroff_up_machines.patch @@ -0,0 +1,58 @@ +From: Olaf Dabrunz +Subject: [apm] default to "power_off" when SMP kernel is used on single processo +r machines +Reference: SUSE221667 + +This patch turns on support for the APM power_off function by default when the +SMP kernel is used on single processor machines. + +It is a bit ugly to use a separate variable to make sure the default value is +only used when needed and the power_off variable is not initialized twice. But +I did not find a better way to do this with the way the current initialization +system works. + +Signed-off-by: Olaf Dabrunz + + + arch/x86/kernel/apm_32.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +--- a/arch/x86/kernel/apm_32.c ++++ b/arch/x86/kernel/apm_32.c +@@ -388,6 +388,7 @@ static bool smp __read_mostly; + static int apm_disabled = -1; + #ifdef CONFIG_SMP + static bool power_off; ++static int power_off_set; + #else + static bool power_off = 1; + #endif +@@ -1843,6 +1844,14 @@ static int apm(void *unused) + } + } + ++#ifdef CONFIG_SMP ++ if (!power_off_set) { ++ power_off = (num_online_cpus() == 1); ++ /* remember not to initialize (with default value) again */ ++ power_off_set = 1; ++ } ++#endif ++ + /* Install our power off handler.. */ + if (power_off) + pm_power_off = apm_power_off; +@@ -1886,8 +1895,12 @@ static int __init apm_setup(char *str) + if (strncmp(str, "debug", 5) == 0) + debug = !invert; + if ((strncmp(str, "power-off", 9) == 0) || +- (strncmp(str, "power_off", 9) == 0)) ++ (strncmp(str, "power_off", 9) == 0)) { + power_off = !invert; ++#ifdef CONFIG_SMP ++ power_off_set = 1; ++#endif ++ } + if (strncmp(str, "smp", 3) == 0) { + smp = !invert; + idle_threshold = 100; diff --git a/kernel/kernel/files/patches/mageia.old/x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch b/kernel/kernel/files/patches/mageia.old/x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch new file mode 100644 index 00000000..05d57ee2 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch @@ -0,0 +1,28 @@ +From 2db91ae7c0862bc83c1fe8b8bd14b8de3e2c02b8 Mon Sep 17 00:00:00 2001 +From: Anssi Hannula +Date: Sat, 30 Apr 2011 17:09:04 +0300 +Subject: [PATCH] x86: increase default minimum vmalloc area by 64MB to 192MB + +This fixes issues like https://bugs.mageia.org/show_bug.cgi?id=904. + +Signed-off-by: Anssi Hannula +--- + arch/x86/mm/pgtable_32.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/arch/x86/mm/pgtable_32.c b/arch/x86/mm/pgtable_32.c +index cac7184..5801fb7 100644 +--- a/arch/x86/mm/pgtable_32.c ++++ b/arch/x86/mm/pgtable_32.c +@@ -19,7 +19,7 @@ + #include + #include + +-unsigned int __VMALLOC_RESERVE = 128 << 20; ++unsigned int __VMALLOC_RESERVE = 192 << 20; + + /* + * Associate a virtual page frame with a given physical page frame +-- +1.7.3 + diff --git a/kernel/kernel/files/patches/mageia.old/x86-pci-toshiba-equium-a60-assign-busses.patch b/kernel/kernel/files/patches/mageia.old/x86-pci-toshiba-equium-a60-assign-busses.patch new file mode 100644 index 00000000..f9b4b1c7 --- /dev/null +++ b/kernel/kernel/files/patches/mageia.old/x86-pci-toshiba-equium-a60-assign-busses.patch @@ -0,0 +1,29 @@ + +The Toshiba Equium A6 Laptop needs pci-assign-busses in order to detect +the Wireless card Netgear MA521 + +More info here: http://qa.mandriva.com/show_bug.cgi?id=18989 + +Signed-off-by: Thomas Backlund + +--- + arch/x86/pci/common.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/x86/pci/common.c ++++ b/arch/x86/pci/common.c +@@ -245,6 +245,14 @@ static const struct dmi_system_id __devi + DMI_MATCH(DMI_PRODUCT_NAME, "SX20S"), + }, + }, ++ { ++ .callback = assign_all_busses, ++ .ident = "Toshiba Equium A6 Laptop", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Equium A60"), ++ }, ++ }, + #endif /* __i386__ */ + { + .callback = set_bf_sort, diff --git a/kernel/kernel/files/patches/mageia/arm-rockchip-fix-broken-build.patch b/kernel/kernel/files/patches/mageia/arm-rockchip-fix-broken-build.patch new file mode 100644 index 00000000..940399c8 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-rockchip-fix-broken-build.patch @@ -0,0 +1,42 @@ +From cb8cc37f4d38d96552f2c52deb15e511cdacf906 Mon Sep 17 00:00:00 2001 +From: Caesar Wang +Date: Mon, 6 Jul 2015 11:37:23 +0800 +Subject: [PATCH] ARM: rockchip: fix broken build + +The following was seen in branch[0] build. + +arch/arm/mach-rockchip/platsmp.c:154:23: error: + 'rockchip_secondary_startup' undeclared (first use in this function) + +branch[0]: +git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git +v4.3-armsoc/soc + +The broken build is caused by the commit fe4407c0dc58 +("ARM: rockchip: fix the CPU soft reset"). + +Signed-off-by: Caesar Wang + +The breakage was a result of it being wrongly merged in my branch with +the cache invalidation rework from Russell 02b4e2756e01c +("ARM: v7 setup function should invalidate L1 cache"). + +Signed-off-by: Heiko Stuebner +--- + arch/arm/mach-rockchip/platsmp.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c +index 7ebd1c1..3e7a4b7 100644 +--- a/arch/arm/mach-rockchip/platsmp.c ++++ b/arch/arm/mach-rockchip/platsmp.c +@@ -152,8 +152,7 @@ static int __cpuinit rockchip_boot_secon + * */ + mdelay(1); /* ensure the cpus other than cpu0 to startup */ + +- writel(virt_to_phys(rockchip_secondary_startup), +- sram_base_addr + 8); ++ writel(virt_to_phys(secondary_startup), sram_base_addr + 8); + writel(0xDEADBEAF, sram_base_addr + 4); + dsb_sev(); + } diff --git a/kernel/kernel/files/patches/mageia/cpu-cacheinfo-Fix-teardown-path.patch b/kernel/kernel/files/patches/mageia/cpu-cacheinfo-Fix-teardown-path.patch new file mode 100644 index 00000000..750fba8d --- /dev/null +++ b/kernel/kernel/files/patches/mageia/cpu-cacheinfo-Fix-teardown-path.patch @@ -0,0 +1,81 @@ +From 2110d70c5e58326a10e93cfefdc0b3686e2ada12 Mon Sep 17 00:00:00 2001 +From: Borislav Petkov +Date: Sat, 8 Aug 2015 10:46:02 +0200 +Subject: cpu/cacheinfo: Fix teardown path +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Philip Müller reported a hang when booting 32-bit 4.1 kernel on an AMD +box. A fragment of the splat was enough to pinpoint the issue: + + task: f58e0000 ti: f58e8000 task.ti: f58e800 + EIP: 0060:[] EFLAGS: 00010206 CPU: 0 + EIP is at free_cache_attributes+0x83/0xd0 + EAX: 00000001 EBX: f589d46c ECX: 00000090 EDX: 360c2000 + ESI: 00000000 EDI: c1724a80 EBP: f58e9ec0 ESP: f58e9ea0 + DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 + CR0: 8005003b CR2: 000000ac CR3: 01731000 CR4: 000006d0 + +cache_shared_cpu_map_setup() did check sibling CPUs cacheinfo descriptor +while the respective teardown path cache_shared_cpu_map_remove() didn't. +Fix that. + +>From tglx's version: to be on the safe side, move the cacheinfo +descriptor check to free_cache_attributes(), thus cleaning up the +hotplug path a little and making this even more robust. + +Reported-and-tested-by: Philip Müller +Reviewed-by: Thomas Gleixner +Acked-by: Sudeep Holla +Cc: # 4.1 +Cc: Andre Przywara +Cc: Guenter Roeck +Cc: "H. Peter Anvin" +Cc: Ingo Molnar +Cc: linux-kernel@vger.kernel.org +Cc: manjaro-dev@manjaro.org +Cc: Philip Müller +Link: https://lkml.kernel.org/r/55B47BB8.6080202@manjaro.org +Signed-off-by: Borislav Petkov +Signed-off-by: Greg Kroah-Hartman + +diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c +index 764280a..e9fd32e 100644 +--- a/drivers/base/cacheinfo.c ++++ b/drivers/base/cacheinfo.c +@@ -148,7 +148,11 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) + + if (sibling == cpu) /* skip itself */ + continue; ++ + sib_cpu_ci = get_cpu_cacheinfo(sibling); ++ if (!sib_cpu_ci->info_list) ++ continue; ++ + sib_leaf = sib_cpu_ci->info_list + index; + cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); + cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); +@@ -159,6 +163,9 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) + + static void free_cache_attributes(unsigned int cpu) + { ++ if (!per_cpu_cacheinfo(cpu)) ++ return; ++ + cache_shared_cpu_map_remove(cpu); + + kfree(per_cpu_cacheinfo(cpu)); +@@ -514,8 +521,7 @@ static int cacheinfo_cpu_callback(struct notifier_block *nfb, + break; + case CPU_DEAD: + cache_remove_dev(cpu); +- if (per_cpu_cacheinfo(cpu)) +- free_cache_attributes(cpu); ++ free_cache_attributes(cpu); + break; + } + return notifier_from_errno(rc); +-- +cgit v0.10.2 + diff --git a/kernel/kernel/files/patches/mageia/fs-aufs-CVE-pending.patch b/kernel/kernel/files/patches/mageia/fs-aufs-CVE-pending.patch new file mode 100644 index 00000000..cec11c0f --- /dev/null +++ b/kernel/kernel/files/patches/mageia/fs-aufs-CVE-pending.patch @@ -0,0 +1,115 @@ +From bbb2dddd30f6b2079dca3e805bceb55328d7cb53 Mon Sep 17 00:00:00 2001 +From: Ben Hutchings +Date: Thu, 10 Sep 2015 20:13:47 +0100 +Subject: [PATCH] aufs mmap: bugfix, concurrent msync triggers NULL pointer + dereference + +On Thu, 2015-09-03 at 01:21 +0900, sfjro@users.sourceforge.net wrote: +> sfjro@users.sourceforge.net: +> > +> > Xavier Chantry: +> > > I can reproduce the bug on all my machines, so it shouldn't be too +> > > difficult to reproduce. Applying the following hunk is enough to trigger +> > > the bug. +> > +> > Unfortunately still I don't have enough time. +> > I understand this bug has a big impact for you and any other user who +> > have applied aufs3-mmap.patch. As soon as I get the time, I'm sure I +> > will dive into this problem. +> +> I've thought about the problem a little (still I'm busy). +> It looks like a race condition around mm->mmap_sem. +> +> - get_file(file); +> + vma_get_file(vma); +> up_read(&mm->mmap_sem); +> error = vfs_fsync(file, 0); +> - fput(file); +> + vma_fput(vma); +> if (error || start >= end) +> goto out; +> down_read(&mm->mmap_sem); +> +> Since this fput/vma_fput is between up(mmap_sem) and down(mmap_sem), +> vma_fput() is not protected, and got vm_file NULL unexpectedly. It +> means vma->vm_file is changed (or being changed) by someone else (other +> thread in your case) after up_read(&mm->mmap_sem). This is surely a bug +> in aufs[34]-mmap.patch. It shoule be done such like this essentially. +> get_file(file); +> pr = vma->vm_prfile; +> ;;; +> fput(file); +> if (pr) +> fput(pr); +> +> The fix will look a little smarter than above. +> I hope I can post the fix in a few weeks. + +I don't see any need for holding a reference to vma->vm_prfile here. + +There is also a similar bug in madvise_remove() which I can trigger by +calling madvise(..., MADV_REMOVE) in parallel with another thread that +does mmap() and munmap() of the same address range. + +This patch against 3.16 fixes both for me: + +From: Ben Hutchings +Date: Thu, 10 Sep 2015 02:19:59 +0100 +Subject: aufs3: mmap: Fix races in madvise_remove() and sys_msync() +Bug-Debian: https://bugs.debian.org/796036 + +In madvise_remove() and sys_msync() we drop the mmap_sem before +dropping references to the mapped file(s). As soon as we drop the +mmap_sem, the vma we got them from might be destroyed by another +thread, so calling vma_do_fput() is a possible use-after-free. + +In these cases we don't actually need a reference to the aufs file, so +revert to using get_file() and fput() directly. + +Signed-off-by: Ben Hutchings +Signed-off-by: J. R. Okajima +(cherry picked from commit 9a6882cd8c6ec75a6393f80e743bf6b2bb91420e) + +Conflicts: + mm/msync.c +--- + mm/madvise.c | 4 ++-- + mm/msync.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/mm/madvise.c b/mm/madvise.c +index 1ebf71b..d551475 100644 +--- a/mm/madvise.c ++++ b/mm/madvise.c +@@ -320,12 +320,12 @@ static long madvise_remove(struct vm_area_struct *vma, + * vma's reference to the file) can go away as soon as we drop + * mmap_sem. + */ +- vma_get_file(vma); ++ get_file(f); + up_read(¤t->mm->mmap_sem); + error = vfs_fallocate(f, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + offset, end - start); +- vma_fput(vma); ++ fput(f); + down_read(¤t->mm->mmap_sem); + return error; + } +diff --git a/mm/msync.c b/mm/msync.c +index 5c24c54..bb04d53 100644 +--- a/mm/msync.c ++++ b/mm/msync.c +@@ -84,10 +84,10 @@ SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags) + start = vma->vm_end; + if ((flags & MS_SYNC) && file && + (vma->vm_flags & VM_SHARED)) { +- vma_get_file(vma); ++ get_file(file); + up_read(&mm->mmap_sem); + error = vfs_fsync_range(file, fstart, fend, 1); +- vma_fput(vma); ++ fput(file); + if (error || start >= end) + goto out; + down_read(&mm->mmap_sem); diff --git a/kernel/kernel/files/patches/mageia/series b/kernel/kernel/files/patches/mageia/series index b96a67db..b7860fef 100644 --- a/kernel/kernel/files/patches/mageia/series +++ b/kernel/kernel/files/patches/mageia/series @@ -14,72 +14,6 @@ ### ### Stable Queue ### -stable-x86-ldt-make-modify_ldt-synchronous.patch -stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch -stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch -stable-x86-ldt-further-fix-fpu-emulation.patch -stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch -stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch -stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch -stable-drm-radeon-atom-send-out-the-full-aux-address.patch -stable-drm-radeon-native-send-out-the-full-aux-address.patch -stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch -stable-drm-i915-preserve-ssc-earlier.patch -stable-drm-qxl-validate-monitors-config-modes.patch -stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch -stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch -stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch -stable-s390-sclp-fix-compile-error.patch -stable-s390-setup-fix-novx-parameter.patch -stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch -stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch -stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch -stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch -stable-iio-add-inverse-unit-conversion-macros.patch -stable-iio-adis16480-fix-scale-factors.patch -stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch -stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch -stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch -stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch -stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch -stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch -stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch -stable-clk-s5pv210-add-missing-call-to.patch -stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch -stable-clk-pistachio-correct-critical-clock-list.patch -stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch -stable-clk-pxa-fix-core-frequency-reporting-unit.patch -stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch -stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch -stable-pci-fix-ti816x-class-code-quirk.patch -stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch -stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch -stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch -stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch -stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch -stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch -stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch -stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch -stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch -stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch -stable-usb-symbolserial-use-usb_get_serial_port_data.patch -stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch -stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch -stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch -stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch -stable-xfs-fix-xfs_attr_leafblock-definition.patch -stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch -stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch -stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch -stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch -stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch -stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch -stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch -stable-asoc-rt5640-fix-line-out-no-sound-issue.patch -stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch -stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch -stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch -stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch ### ### Arch x86 @@ -108,6 +42,9 @@ Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch ### Core ### +# amd cpu boot crash (mga#16527) +cpu-cacheinfo-Fix-teardown-path.patch + ### ### i2c ### @@ -193,6 +130,8 @@ scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch # aufs from: http://aufs.sourceforge.net/ (mga#8314) fs-aufs4.patch fs-aufs4-modular.patch +# CVE pending +fs-aufs-CVE-pending.patch # ext4 fs-Revert-ext4-remove-block_device_ejected.patch @@ -401,6 +340,7 @@ video-mageia-logo.patch ### ### ARM ### +arm-rockchip-fix-broken-build.patch ### ### IA64 diff --git a/kernel/kernel/pspec.xml b/kernel/kernel/pspec.xml index 485dd269..99ce43a4 100644 --- a/kernel/kernel/pspec.xml +++ b/kernel/kernel/pspec.xml @@ -29,13 +29,14 @@ - patches/linux/patch-4.1.7.xz - + patches/linux/patch-4.1.8.xz + patches/mageia/x86-pci-toshiba-equium-a60-assign-busses.patch patches/mageia/x86-boot-video-80x25-if-break.patch patches/mageia/x86-default_poweroff_up_machines.patch patches/mageia/x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch patches/mageia/Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch + patches/mageia/cpu-cacheinfo-Fix-teardown-path.patch patches/mageia/pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch patches/mageia/pci-quirks-drop-devinit-exit.patch patches/mageia/pci-acs-quirks-for-Intel-9-series-PCH-root-ports.patch @@ -57,6 +58,7 @@ patches/mageia/block-cfq-iosched-fix-the-setting-of-IOPS-mode-on-SSDs.patch patches/mageia/fs-aufs4.patch patches/mageia/fs-aufs4-modular.patch + patches/mageia/fs-aufs-CVE-pending.patch patches/mageia/firewire-ieee1394-module-aliases.patch patches/mageia/char-agp-intel-new-Q57-id.patch patches/mageia/gpu-drm-mach64.patch @@ -123,6 +125,7 @@ patches/mageia/3rd-rtl8723bs-buildfix.patch patches/mageia/3rd-rtl8723bs-updates.patch patches/mageia/fs-Revert-ext4-remove-block_device_ejected.patch + patches/mageia/Input-evdev-do-not-report-errors-form-flush.patch patches/mageia/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch patches/mageia/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch @@ -180,6 +183,14 @@ + + 2015-09-23 + 4.1.8 + Version bump to 4.1.8 https://www.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.1.8 + security + Ertuğrul Erata + ertugrulerata@gmail.com + 2015-09-18 4.1.7 From 02d9e68d77de4c242849a52e19e71227e36f5683 Mon Sep 17 00:00:00 2001 From: ertugerata Date: Wed, 23 Sep 2015 01:07:31 +0300 Subject: [PATCH 2/3] sil --- .../mageia.old/3rd-3rdparty-1.0-tree.patch | 181 - .../mageia.old/3rd-3rdparty-merge.patch | 181 - .../mageia.old/3rd-acerhk-0.5.35.patch | 4212 - .../3rd-acerhk-2.6.36-buildfix.patch | 20 - .../mageia.old/3rd-acerhk-extra-cflags.patch | 33 - ...cerhk-fix-build-with-function-tracer.patch | 22 - .../mageia.old/3rd-acerhk-fix-include.patch | 23 - .../mageia.old/3rd-acerhk-kbuild.patch | 37 - .../3rd-acerhk-proc_dir_entry-owner.patch | 67 - .../mageia.old/3rd-aes2501-kbuild.patch | 47 - .../patches/mageia.old/3rd-aes2501-r19.patch | 2356 - .../3rd-aes2501-rmmod-oops-fix.patch | 21 - .../mageia.old/3rd-ndiswrapper-1.59.patch | 26399 --- .../3rd-ndiswrapper-4.0-buildfix.patch | 32 - .../mageia.old/3rd-ndiswrapper-Kconfig.patch | 7 - .../3rd-ndiswrapper-Makefile-build-fix.patch | 45 - .../3rd-ndiswrapper-linux-3.14-buildfix.patch | 33 - .../patches/mageia.old/3rd-rfswitch-1.3.patch | 978 - .../3rd-rfswitch-3.0-buildfix.patch | 19 - .../mageia.old/3rd-rfswitch-build-fix.patch | 51 - .../mageia.old/3rd-rtl8723bs-buildfix.patch | 23 - .../mageia.old/3rd-rtl8723bs-updates.patch | 28991 --- .../patches/mageia.old/3rd-rtl8723bs.patch | 140470 --------------- .../patches/mageia.old/3rd-viahss-0.92.patch | 189 - .../3rd-viahss-2.6.35-buildfix.patch | 20 - .../mageia.old/3rd-viahss-3.0-buildfix.patch | 19 - .../mageia.old/3rd-viahss-config.patch | 18 - .../3rd-viahss-module-license.patch | 7 - .../files/patches/mageia.old/3rdparty.series | 3 - ...vdev-do-not-report-errors-form-flush.patch | 64 - ...cc-Enable-autoload-of-pcc-cpufreq-fo.patch | 32 - .../acpi-CLEVO-M360S-disable_acpi_irq.patch | 28 - .../acpi-processor-M720SR-limit-to-C2.patch | 21 - ...over-ide-drivers-when-both-are-built.patch | 33 - ...ake-CFQ-default-to-IOPS-mode-on-SSDs.patch | 58 - ...fix-the-setting-of-IOPS-mode-on-SSDs.patch | 95 - .../block-floppy-disable-pnp-modalias.patch | 21 - .../char-agp-intel-new-Q57-id.patch | 35 - .../patches/mageia.old/dm-raid-aliases.patch | 20 - .../firewire-ieee1394-module-aliases.patch | 29 - ...ert-ext4-remove-block_device_ejected.patch | 53 - .../patches/mageia.old/fs-aufs4-modular.patch | 338 - .../files/patches/mageia.old/fs-aufs4.patch | 34146 ---- .../mageia.old/gpu-drm-mach64-2.6.31.patch | 100 - .../gpu-drm-mach64-2.6.36-buildfix.patch | 27 - .../gpu-drm-mach64-2.6.37-buildfix.patch | 24 - .../gpu-drm-mach64-3.0-buildfix.patch | 44 - .../gpu-drm-mach64-3.12-buildfix.patch | 17 - .../gpu-drm-mach64-3.17-buildfix.patch | 31 - .../gpu-drm-mach64-3.18-buildfix.patch | 142 - .../gpu-drm-mach64-3.3-buildfix.patch | 47 - .../gpu-drm-mach64-3.6-buildfix.patch | 15 - .../gpu-drm-mach64-3.7-buildfix.patch | 61 - ...drm-mach64-fix-for-changed-drm_ioctl.patch | 20 - ...mach64-fix-for-changed-drm_pci_alloc.patch | 12 - .../mageia.old/gpu-drm-mach64-fixes.patch | 97 - .../gpu-drm-mach64-include-module.h.patch | 10 - .../gpu-drm-mach64-linux-3.14-buildfix.patch | 102 - ...pu-drm-mach64-restore-mach64_PCI_IDS.patch | 38 - .../patches/mageia.old/gpu-drm-mach64.patch | 4144 - ...hid-usbhid-IBM-BladeCenterHS20-quirk.patch | 37 - .../mageia.old/ide-pci-sis5513-965.patch | 23 - .../include-kbuild-export-pci_ids.patch | 19 - ...ks-for-Fujitsu-Lifebook-A544-and-Lif.patch | 47 - ...edia-usb-pwc-lie-in-proc-usb-devices.patch | 13 - .../patches/mageia.old/mpt-vmware-fix.patch | 49 - ...v4-netfilter-ipt_IFWLOG-3.6-buildfix.patch | 33 - ...net-netfilter-IFWLOG-2.6.35-buildfix.patch | 32 - ...net-netfilter-IFWLOG-2.6.37-buildfix.patch | 15 - .../net-netfilter-IFWLOG-3.7-buildfix.patch | 20 - .../mageia.old/net-netfilter-IFWLOG-mdv.patch | 264 - .../mageia.old/net-netfilter-IFWLOG.patch | 269 - .../net-netfilter-psd-2.6.35-buildfix.patch | 11 - .../mageia.old/net-netfilter-psd-mdv.patch | 233 - .../mageia.old/net-netfilter-psd.patch | 420 - .../net-sis190-fix-list-usage.patch | 30 - ...ks-for-Intel-9-series-PCH-root-ports.patch | 28 - ...I-M5229-ide-compatibility-mode-quirk.patch | 60 - .../pci-quirks-drop-devinit-exit.patch | 11 - .../platform-x86-add-shuttle-wmi-driver.patch | 1437 - ...rm-x86-shuttle-wmi-drop-devinit-exit.patch | 38 - ...r-dereference-in-RTPM-of-block-layer.patch | 74 - ...LL-pointer-dereference-in-runtime-PM.patch | 64 - .../scsi-megaraid-new-sysfs-name.patch | 15 - .../mageia.old/scsi-ppscsi-2.6.2.patch | 5083 - .../mageia.old/scsi-ppscsi-3.0-buildfix.patch | 19 - .../mageia.old/scsi-ppscsi-mdvbz45393.patch | 31 - .../scsi-ppscsi-sg-helper-update.patch | 36 - ...i-ppscsi-update-for-scsi_data_buffer.patch | 88 - .../mageia.old/scsi-ppscsi_fixes.patch | 226 - kernel/kernel/files/patches/mageia.old/series | 411 - ...k-setting-from-adav80x_regmap_config.patch | 31 - ...ain-settings-of-fll-in-free-run-mode.patch | 33 - ...-clock-ok-rather-than-use-interrupts.patch | 141 - ...c-rt5640-fix-line-out-no-sound-issue.patch | 89 - ...emove-redundant-arndale_audio_remove.patch | 48 - ...4-fix-wrong-clock-for-exynos4x12-adc.patch | 55 - ...istachio-correct-critical-clock-list.patch | 81 - ...of-clk-pll-settings-from-boot-loader.patch | 54 - ...xa-fix-core-frequency-reporting-unit.patch | 61 - ...om-fix-msm8916-prng-clock-enable-bit.patch | 32 - ...et-clk_set_rate_parent-on-ce1-clocks.patch | 45 - ...-add-clk_set_rate_parent-to-sclk_mac.patch | 39 - ...able-clk-s5pv210-add-missing-call-to.patch | 37 - ...y-one-in-clk_sp810_timerclken_of_get.patch | 34 - ...-testing-using-the-updated-testusb.c.patch | 44 - ...al-link-to-be-configured-on-any-pipe.patch | 58 - ...object-as-dirty-when-used-by-the-gpu.patch | 58 - ...on-workaround-everywhere-on-pre-gen6.patch | 79 - ...check-dp-link-status-on-long-hpd-too.patch | 75 - ...s-for-reading-a-split-64bit-register.patch | 57 - ...stable-drm-i915-preserve-ssc-earlier.patch | 117 - ...m-qxl-validate-monitors-config-modes.patch | 147 - ...n-atom-send-out-the-full-aux-address.patch | 39 - ...layport-on-hpd-until-we-get-the-dpcd.patch | 75 - ...uantization_range-for-pre-dce5-asics.patch | 47 - ...native-send-out-the-full-aux-address.patch | 40 - ...o-add-inverse-unit-conversion-macros.patch | 70 - ...s16400-fix-adis16448-gyroscope-scale.patch | 30 - ...able-iio-adis16480-fix-scale-factors.patch | 101 - ...nd-iio_triggered_buffer-are-required.patch | 33 - ...ative-error-code-from-iio_event_poll.patch | 33 - ...fer-fix-iio_buffer_poll-return-value.patch | 34 - ...-blocks-should-use-the-read-verifier.patch | 37 - ...bit-to-access-vpd-through-function-0.patch | 135 - ...n-0-quirk-for-intel-ethernet-devices.patch | 41 - ...for-jmicron-multi-function-sata-ahci.patch | 120 - ...able-pci-fix-ti816x-class-code-quirk.patch | 45 - ...-don-t-return-int-on-__pm_clk_enable.patch | 48 - .../stable-s390-sclp-fix-compile-error.patch | 35 - ...stable-s390-setup-fix-novx-parameter.patch | 74 - ...cpu_active_mask-cpu_online_mask-race.patch | 146 - ...up-spi-mode-before-asserting-cs-gpio.patch | 88 - ...vers-to-limit-data-i-o-to-word-sizes.patch | 129 - ...fix-regression-in-spi-bitbang-txrx.h.patch | 43 - ...-for-timeout-error-before-proceeding.patch | 48 - ...g-spfi-fix-kbuild-test-robot-warning.patch | 33 - ...i-fix-multiple-calls-to-request-gpio.patch | 97 - ...x-fifo-size-to-64-word-from-256-word.patch | 66 - ...i7x3x-fix-digital-output-on-pci-7230.patch | 56 - ...n-t-clobber-ai_timer-in-command-test.patch | 87 - ...n-t-clobber-ao_timer-in-command-test.patch | 90 - ...fix-race-between-irq-and-set_termios.patch | 75 - ...out-transfers-of-more-than-512-bytes.patch | 58 - ...d-custom-pid-for-customware-products.patch | 51 - ...maxpacketsize-according-to-bandwidth.patch | 87 - ...6592-udc-forever-loop-in-set_feature.patch | 32 - ...delete-useless-bus_to_hcd-conversion.patch | 58 - ...3-fix-baud-rate-divisor-calculations.patch | 97 - ...lt4111-lte-ev-do-hspa-gobi-4g-module.patch | 30 - ...lserial-use-usb_get_serial_port_data.patch | 52 - ...-correct-fpu-emulation-access-to-ldt.patch | 98 - ...-ldt-access-in-single-stepping-logic.patch | 45 - ...le-x86-ldt-further-fix-fpu-emulation.patch | 35 - ...-x86-ldt-make-modify_ldt-synchronous.patch | 628 - ...ory-corruption-for-btree-directories.patch | 65 - ...fs-fix-xfs_attr_leafblock-definition.patch | 47 - ...-unusual_devs-add-id-2.6.37-buildfix.patch | 46 - .../usb-storage-unusual_devs-add-id.patch | 55 - .../mageia.old/video-mageia-logo.patch | 949 - .../x86-boot-video-80x25-if-break.patch | 24 - .../x86-default_poweroff_up_machines.patch | 58 - ...inimum-vmalloc-area-by-64MB-to-192MB.patch | 28 - ...pci-toshiba-equium-a60-assign-busses.patch | 29 - 164 files changed, 259526 deletions(-) delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-0.92.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-2.6.35-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-3.0-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-config.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rd-viahss-module-license.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/3rdparty.series delete mode 100644 kernel/kernel/files/patches/mageia.old/Input-evdev-do-not-report-errors-form-flush.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/acpi-CLEVO-M360S-disable_acpi_irq.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/acpi-processor-M720SR-limit-to-C2.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/block-Make-CFQ-default-to-IOPS-mode-on-SSDs.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/block-cfq-iosched-fix-the-setting-of-IOPS-mode-on-SSDs.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/block-floppy-disable-pnp-modalias.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/char-agp-intel-new-Q57-id.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/dm-raid-aliases.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/firewire-ieee1394-module-aliases.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/fs-Revert-ext4-remove-block_device_ejected.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/fs-aufs4-modular.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/fs-aufs4.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.31.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.36-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-2.6.37-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.0-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.12-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.17-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.18-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.3-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.6-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-3.7-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_ioctl.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fix-for-changed-drm_pci_alloc.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-fixes.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-include-module.h.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-linux-3.14-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64-restore-mach64_PCI_IDS.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/gpu-drm-mach64.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/hid-usbhid-IBM-BladeCenterHS20-quirk.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/ide-pci-sis5513-965.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/include-kbuild-export-pci_ids.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/input-i8042-quirks-for-Fujitsu-Lifebook-A544-and-Lif.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/media-usb-pwc-lie-in-proc-usb-devices.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/mpt-vmware-fix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-ipv4-netfilter-ipt_IFWLOG-3.6-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.35-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-2.6.37-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-3.7-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG-mdv.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-IFWLOG.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-psd-2.6.35-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-psd-mdv.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-netfilter-psd.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/net-sis190-fix-list-usage.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/pci-acs-quirks-for-Intel-9-series-PCH-root-ports.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/pci-quirks-drop-devinit-exit.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/platform-x86-add-shuttle-wmi-driver.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/platform-x86-shuttle-wmi-drop-devinit-exit.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-megaraid-new-sysfs-name.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-2.6.2.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-3.0-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-mdvbz45393.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-sg-helper-update.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi-update-for-scsi_data_buffer.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/scsi-ppscsi_fixes.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/series delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-asoc-adav80x-remove-.read_flag_mask-setting-from-adav80x_regmap_config.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-asoc-arizona-fix-gain-settings-of-fll-in-free-run-mode.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-asoc-arizona-poll-for-fll-clock-ok-rather-than-use-interrupts.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-asoc-rt5640-fix-line-out-no-sound-issue.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-asoc-samsung-remove-redundant-arndale_audio_remove.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-exynos4-fix-wrong-clock-for-exynos4x12-adc.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-pistachio-correct-critical-clock-list.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-pistachio-fix-override-of-clk-pll-settings-from-boot-loader.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-pxa-fix-core-frequency-reporting-unit.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-qcom-fix-msm8916-prng-clock-enable-bit.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-qcom-set-clk_set_rate_parent-on-ce1-clocks.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-rockchip-rk3288-add-clk_set_rate_parent-to-sclk_mac.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-s5pv210-add-missing-call-to.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-clk-versatile-off-by-one-in-clk_sp810_timerclken_of_get.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-doc-usb-gadget-testing-using-the-updated-testusb.c.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-i915-allow-dsi-dual-link-to-be-configured-on-any-pipe.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-i915-always-mark-the-object-as-dirty-when-used-by-the-gpu.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-i915-apply-the-pci_d0-d3-hibernation-workaround-everywhere-on-pre-gen6.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-i915-check-dp-link-status-on-long-hpd-too.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-i915-limit-the-number-of-loops-for-reading-a-split-64bit-register.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-i915-preserve-ssc-earlier.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-qxl-validate-monitors-config-modes.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-radeon-atom-send-out-the-full-aux-address.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-radeon-don-t-link-train-displayport-on-hpd-until-we-get-the-dpcd.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-radeon-fix-hdmi-quantization_range-for-pre-dce5-asics.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-drm-radeon-native-send-out-the-full-aux-address.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-iio-add-inverse-unit-conversion-macros.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-iio-adis16400-fix-adis16448-gyroscope-scale.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-iio-adis16480-fix-scale-factors.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-iio-bmg160-iio_buffer-and-iio_triggered_buffer-are-required.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-iio-event-remove-negative-error-code-from-iio_event_poll.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-iio-industrialio-buffer-fix-iio_buffer_poll-return-value.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-libxfs-readahead-of-dir3-data-blocks-should-use-the-read-verifier.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-pci-add-dev_flags-bit-to-access-vpd-through-function-0.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-pci-add-vpd-function-0-quirk-for-intel-ethernet-devices.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-pci-disable-async-suspend-resume-for-jmicron-multi-function-sata-ahci.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-pci-fix-ti816x-class-code-quirk.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-pm-clk-don-t-return-int-on-__pm_clk_enable.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-s390-sclp-fix-compile-error.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-s390-setup-fix-novx-parameter.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-sched-fix-cpu_active_mask-cpu_online_mask-race.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-spi-bcm2835-set-up-spi-mode-before-asserting-cs-gpio.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-spi-dw-allow-interface-drivers-to-limit-data-i-o-to-word-sizes.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-spi-fix-regression-in-spi-bitbang-txrx.h.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-check-for-timeout-error-before-proceeding.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-fix-kbuild-test-robot-warning.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-spi-img-spfi-fix-multiple-calls-to-request-gpio.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-spi-sh-msiof-fix-fifo-size-to-64-word-from-256-word.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-staging-comedi-adl_pci7x3x-fix-digital-output-on-pci-7230.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-staging-comedi-usbduxsigma-don-t-clobber-ai_timer-in-command-test.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-staging-comedi-usbduxsigma-don-t-clobber-ao_timer-in-command-test.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-tty-serial-men_z135_uart.c-fix-race-between-irq-and-set_termios.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-dwc3-ep0-fix-mem-corruption-on-out-transfers-of-more-than-512-bytes.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-ftdi_sio-added-custom-pid-for-customware-products.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-gadget-f_uac2-finalize-wmaxpacketsize-according-to-bandwidth.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-gadget-m66592-udc-forever-loop-in-set_feature.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-host-ehci-sys-delete-useless-bus_to_hcd-conversion.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-pl2303-fix-baud-rate-divisor-calculations.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-qcserial-add-hp-lt4111-lte-ev-do-hspa-gobi-4g-module.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-usb-symbolserial-use-usb_get_serial_port_data.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-x86-ldt-correct-fpu-emulation-access-to-ldt.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-x86-ldt-correct-ldt-access-in-single-stepping-logic.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-x86-ldt-further-fix-fpu-emulation.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-x86-ldt-make-modify_ldt-synchronous.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-xfs-fix-file-type-directory-corruption-for-btree-directories.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/stable-xfs-fix-xfs_attr_leafblock-definition.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id-2.6.37-buildfix.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/usb-storage-unusual_devs-add-id.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/video-mageia-logo.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/x86-boot-video-80x25-if-break.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/x86-default_poweroff_up_machines.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch delete mode 100644 kernel/kernel/files/patches/mageia.old/x86-pci-toshiba-equium-a60-assign-busses.patch diff --git a/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch b/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch deleted file mode 100644 index 233389b4..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-1.0-tree.patch +++ /dev/null @@ -1,181 +0,0 @@ - - 3rdparty/mkbuild.pl | 92 +++++++++++++++++++++++++++++++++++++++++++++ - Documentation/3rdparty.txt | 76 +++++++++++++++++++++++++++++++++++++ - 2 files changed, 168 insertions(+) - -diff -Nurp linux-2.6.37/3rdparty/mkbuild.pl linux-2.6.37.3rdparty/3rdparty/mkbuild.pl ---- linux-2.6.37/3rdparty/mkbuild.pl 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/mkbuild.pl 2004-04-23 14:59:03.000000000 +0300 -@@ -0,0 +1,92 @@ -+#!/usr/bin/perl -w -+# -+# Version 1.0 -+# -+# Copyright 2001 Jeff Garzik -+# Copyright 2002 Juan Quintela -+# Copyright 2003 Nicolas Planel -+# -+# This software may be used and distributed according to the terms -+# of the GNU General Public License, incorporated herein by reference. -+# -+# -+# Run "mkbuild.pl" -+# -+# This program generates the following files -+# Makefile -+# Makefile.drivers -+# Config.in -+# using the information in the subdirs of this directory. -+# -+# subdirs need to have: -+# a Config.in file -+# a Makefile with a O_TARGET/L_TARGET targets -+# The config.in should set a CONFIG_ to m/y. -+ -+use strict; -+ -+opendir(THISDIR, "."); -+# get dirs without . and .. garbage -+my (@modules) = grep(!/\.\.?$/,grep(-d, readdir(THISDIR))); -+closedir(THISDIR); -+ -+generate_kconfig(@modules); -+generate_makefile(@modules); -+exit(0); -+ -+########################################################################## -+ -+sub generate_makefile { -+ my (@modules) = @_; -+ -+ local *F; -+ open F, "> Makefile" or die "Cannot create new Makefile: $!\n"; -+ print F <<'EOM'; -+# -+# THIS IS AN AUTOMATICALLY GENERATED FILE. DO NOT EDIT. -+# -+ -+EOM -+ printf F "obj- := 3rdparty.o # Dummy rule to force built-in.o to be made\n"; -+ printf F "obj-\$(%s) += %s\n", to_CONFIG($_), $_ . '/' foreach @modules; -+} -+ -+sub generate_kconfig { -+ my (@modules) = @_; -+ -+ local *F; -+ open F, "> Kconfig" or die "Cannot create Kconfig: $!\n"; -+ print F <<"EOM"; -+# -+# THIS IS AN AUTOMATICALLY GENERATED FILE. DO NOT EDIT. -+# -+ -+menu "Unofficial 3rd party kernel additions" -+ -+EOM -+ -+ foreach (@modules) { -+ die "No Kconfig in $_.\n" if ! -r "$_/Kconfig"; -+ print F "source 3rdparty/$_/Kconfig\n"; -+ } -+ print F "\n\nendmenu\n"; -+} -+ -+sub to_CONFIG { -+ local $_ = $_[0]; -+ tr/a-z/A-Z/; -+ s/[\-\. ]/_/g; -+ "CONFIG_$_"; -+} -+ -+sub find_target { -+ my ($module_dir) = @_; -+ -+ local *F; -+ open(F, "$module_dir/Makefile") or die "$module_dir/Makefile: $!\n"; -+ while () { -+ chomp; -+ return $1 if (/[LO]_TARGET.*:=\s+(\S+)/); -+ } -+} -+ -diff -Nurp linux-2.6.37/Documentation/3rdparty.txt linux-2.6.37.3rdparty/Documentation/3rdparty.txt ---- linux-2.6.37/Documentation/3rdparty.txt 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/Documentation/3rdparty.txt 2003-11-22 01:07:26.000000000 +0200 -@@ -0,0 +1,76 @@ -+ -+Third-Party Kernel Source Module Support, or -+an easy way to add modules to your kernel build. -+ -+ -+ -+Vendors quite often add additional drivers and features to the kernel -+which require nothing more than modifying Kconfig, Makefile, and -+adding one or more files to a sub-directory. As a single discrete task, -+this is not a problem. However, using patches to add modules to the -+kernel very often results in patch conflicts, resulting in needless time -+wastage as developers regenerate an otherwise working kernel patch. -+ -+This is designed as a solution to these problems. It is NOT designed as -+a replacement for the kernel build system, but merely as a tool for -+vendors and system administrators to ease the pain of patch management. -+ -+The key feature of this system is the distinct lack of patches. Drivers -+are installed via unpacking a tarball. -+ -+ -+ -+Adding a directory to the build (usually from a tarball) -+-------------------------------------------------------- -+If a directory exists inside the 3rdparty sub-directory that contains a -+proper Makefile, it can be added to the build. It also needs a -+Kconfig file. -+ -+ cd /usr/src/linux-2.4.3/3rdparty -+ bzcat /tmp/my-driver2.tar.bz2 | tar xf - # creates "my2" dir -+ -+ -+Limitations -+----------- -+There are some limitations to this system. This system is only -+designed to support a very common case. If you find yourself running -+into limitations (kernel build experts can spot them right off), -+then you should probably be patching the kernel instead of using -+mkbuild.pl for that particular module. -+ -+FIXME: actually list the limitations -+ -+ -+ -+Other notes -+----------- -+Link order is controlled by the order of mkbuild.pl executions. -+ -+"make mrproper" will erase Makefile.meta, and empty Kconfig, Makefile, -+and Makefile.drivers. -+ -+IMPORTANT NOTE: Because this feature modifies the kernel's makefiles and -+configuration system, you MUST complete all mkbuild.pl runs before -+running any "make" command. -+ -+Building in the 3rdparty dir -+---------------------------- -+ -+If you use modules that: -+ - are contained in one subdir with the name of the module -+ - has a Makefile -+ - has a Kconfig file -+ -+The system calls the ./mkbuild.pl script. It will search for -+subdirectories, and will try to build each of them as a module. -+Things to note: -+ -+ The dependencies will be done in a module called: -+ -+ 3rdparty// -+ -+depending of CONFIG_. -+ -+ is the value of O_TARGET/L_TARGET. -+ -+ diff --git a/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch b/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch deleted file mode 100644 index 8c1f8421..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-3rdparty-merge.patch +++ /dev/null @@ -1,181 +0,0 @@ - -Include 3rdparty directory into the main build-system. - -Original author is unknown. -(Was either Juan Quintela or Jeff Garzik) - -Signed-off-by: Luiz Fernando N. Capitulino -Signed-off-by: Herton Ronaldo Krzesinski -Signed-off-by: Thomas Backlund - - Makefile | 2 +- - arch/alpha/Kconfig | 1 + - arch/ia64/Kconfig | 2 ++ - arch/mips/Kconfig | 2 ++ - arch/powerpc/Kconfig | 2 ++ - arch/sparc/Kconfig | 2 ++ - arch/x86/Kconfig | 2 ++ - scripts/kconfig/Makefile | 31 +++++++++++++++++-------------- - 8 files changed, 29 insertions(+), 15 deletions(-) - -diff -Nurp linux-4.1/arch/alpha/Kconfig linux-4.1-3rd/arch/alpha/Kconfig ---- linux-4.1/arch/alpha/Kconfig 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/arch/alpha/Kconfig 2015-06-25 20:44:03.221647317 +0200 -@@ -750,3 +750,4 @@ source "crypto/Kconfig" - - source "lib/Kconfig" - -+source "3rdparty/Kconfig" -diff -Nurp linux-4.1/arch/ia64/Kconfig linux-4.1-3rd/arch/ia64/Kconfig ---- linux-4.1/arch/ia64/Kconfig 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/arch/ia64/Kconfig 2015-06-25 20:44:03.231647227 +0200 -@@ -636,3 +636,5 @@ source "lib/Kconfig" - - config IOMMU_HELPER - def_bool (IA64_HP_ZX1 || IA64_HP_ZX1_SWIOTLB || IA64_GENERIC || SWIOTLB) -+ -+source "3rdparty/Kconfig" -diff -Nurp linux-4.1/arch/mips/Kconfig linux-4.1-3rd/arch/mips/Kconfig ---- linux-4.1/arch/mips/Kconfig 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/arch/mips/Kconfig 2015-06-25 20:44:03.231647227 +0200 -@@ -2893,3 +2893,5 @@ source "crypto/Kconfig" - source "lib/Kconfig" - - source "arch/mips/kvm/Kconfig" -+ -+source "3rdparty/Kconfig" -diff -Nurp linux-4.1/arch/powerpc/Kconfig linux-4.1-3rd/arch/powerpc/Kconfig ---- linux-4.1/arch/powerpc/Kconfig 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/arch/powerpc/Kconfig 2015-06-25 20:44:03.231647227 +0200 -@@ -1090,3 +1090,5 @@ config PPC_LIB_RHEAP - bool - - source "arch/powerpc/kvm/Kconfig" -+ -+source "3rdparty/Kconfig" -diff -Nurp linux-4.1/arch/sparc/Kconfig linux-4.1-3rd/arch/sparc/Kconfig ---- linux-4.1/arch/sparc/Kconfig 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/arch/sparc/Kconfig 2015-06-25 20:44:03.231647227 +0200 -@@ -569,3 +569,5 @@ source "security/Kconfig" - source "crypto/Kconfig" - - source "lib/Kconfig" -+ -+source "3rdparty/Kconfig" -diff -Nurp linux-4.1/arch/x86/Kconfig linux-4.1-3rd/arch/x86/Kconfig ---- linux-4.1/arch/x86/Kconfig 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/arch/x86/Kconfig 2015-06-25 20:44:03.231647227 +0200 -@@ -2594,3 +2594,5 @@ source "crypto/Kconfig" - source "arch/x86/kvm/Kconfig" - - source "lib/Kconfig" -+ -+source "3rdparty/Kconfig" -diff -Nurp linux-4.1/Makefile linux-4.1-3rd/Makefile ---- linux-4.1/Makefile 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/Makefile 2015-06-25 20:44:03.231647227 +0200 -@@ -556,7 +556,7 @@ scripts: scripts_basic include/config/au - - # Objects we will link into vmlinux / subdirs we need to visit - init-y := init/ --drivers-y := drivers/ sound/ firmware/ -+drivers-y := drivers/ sound/ firmware/ 3rdparty/ - net-y := net/ - libs-y := lib/ - core-y := usr/ -diff -Nurp linux-4.1/scripts/kconfig/Makefile linux-4.1-3rd/scripts/kconfig/Makefile ---- linux-4.1/scripts/kconfig/Makefile 2015-06-22 07:05:43.000000000 +0200 -+++ linux-4.1-3rd/scripts/kconfig/Makefile 2015-06-25 20:47:50.109596717 +0200 -@@ -18,26 +18,26 @@ endif - # We need this, in case the user has it in its environment - unexport CONFIG_ - --xconfig: $(obj)/qconf -+xconfig: $(obj)/qconf 3rdparty/Makefile - $< $(silent) $(Kconfig) - --gconfig: $(obj)/gconf -+gconfig: $(obj)/gconf 3rdparty/Makefile - $< $(silent) $(Kconfig) - --menuconfig: $(obj)/mconf -+menuconfig: $(obj)/mconf 3rdparty/Makefile - $< $(silent) $(Kconfig) - --config: $(obj)/conf -+config: $(obj)/conf 3rdparty/Makefile - $< $(silent) --oldaskconfig $(Kconfig) - --nconfig: $(obj)/nconf -+nconfig: $(obj)/nconf 3rdparty/Makefile - $< $(silent) $(Kconfig) - --silentoldconfig: $(obj)/conf -+silentoldconfig: $(obj)/conf 3rdparty/Makefile - $(Q)mkdir -p include/config include/generated - $< $(silent) --$@ $(Kconfig) - --localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf -+localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf 3rdparty/Makefile - $(Q)mkdir -p include/config include/generated - $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config - $(Q)if [ -f .config ]; then \ -@@ -80,7 +80,7 @@ simple-targets := oldconfig allnoconfig - alldefconfig randconfig listnewconfig olddefconfig - PHONY += $(simple-targets) - --$(simple-targets): $(obj)/conf -+$(simple-targets): $(obj)/conf 3rdparty/Makefile - $< $(silent) --$@ $(Kconfig) - - PHONY += oldnoconfig savedefconfig defconfig -@@ -88,12 +88,12 @@ PHONY += oldnoconfig savedefconfig defco - # oldnoconfig is an alias of olddefconfig, because people already are dependent - # on its behavior(sets new symbols to their default value but not 'n') with the - # counter-intuitive name. --oldnoconfig: olddefconfig -+oldnoconfig: olddefconfig 3rdparty/Makefile - --savedefconfig: $(obj)/conf -+savedefconfig: $(obj)/conf 3rdparty/Makefile - $< $(silent) --$@=defconfig $(Kconfig) - --defconfig: $(obj)/conf -+defconfig: $(obj)/conf 3rdparty/Makefile - ifeq ($(KBUILD_DEFCONFIG),) - $< $(silent) --defconfig $(Kconfig) - else -@@ -101,7 +101,7 @@ else - $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) - endif - --%_defconfig: $(obj)/conf -+%_defconfig: $(obj)/conf 3rdparty/Makefile - $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) - - configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) -@@ -112,12 +112,12 @@ configfiles=$(wildcard $(srctree)/kernel - +$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig - - PHONY += kvmconfig --kvmconfig: kvm_guest.config -+kvmconfig: kvm_guest.config 3rdparty/Makefile - @: - - PHONY += tinyconfig - tinyconfig: -- $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config -+ $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config 3rdparty/Makefile - - # Help text used by make help - help: -@@ -176,6 +176,9 @@ gconf-objs := gconf.o zconf.tab.o - - hostprogs-y := conf nconf mconf kxgettext qconf gconf - -+3rdparty/Makefile: -+ pushd $(srctree)/3rdparty ; $(PERL) ./mkbuild.pl ; popd -+ - clean-files := qconf.moc .tmp_qtcheck .tmp_gtkcheck - clean-files += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h - clean-files += config.pot linux.pot diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch deleted file mode 100644 index 5d61570f..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-0.5.35.patch +++ /dev/null @@ -1,4212 +0,0 @@ - 3rdparty/acerhk/AUTHORS | 1 - 3rdparty/acerhk/COPYING | 340 ++++ - 3rdparty/acerhk/INSTALL | 109 + - 3rdparty/acerhk/Makefile | 65 - 3rdparty/acerhk/NEWS | 16 - 3rdparty/acerhk/README | 215 ++ - 3rdparty/acerhk/acerhk.c | 2999 ++++++++++++++++++++++++++++++++++++++++ - 3rdparty/acerhk/acerhk.h | 91 + - 3rdparty/acerhk/doc/FAQ | 150 ++ - 3rdparty/acerhk/doc/IOCTL | 61 - 3rdparty/acerhk/doc/acertm.def | 56 - 3rdparty/acerhk/doc/keycodes | 26 - 3rdparty/acerhk/doc/md95400.def | 14 - 13 files changed, 4143 insertions(+) -diff -Nurp linux-2.6.37/3rdparty/acerhk/acerhk.c linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.c ---- linux-2.6.37/3rdparty/acerhk/acerhk.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.c 2007-02-10 17:46:23.000000000 +0200 -@@ -0,0 +1,2999 @@ -+/********************************************************************* -+ * Filename: acerhk.c -+ * Version: 0.5 -+ * -+ * Copyright (C) 2002-2006, Olaf Tauber (olaf-tauber@versanet.de) -+ * -+ * Description: kernel driver for Acer Travelmate and similar -+ * laptops special keys -+ * Author: Olaf Tauber -+ * Created at: Mon Apr 29 22:16:42 2002 -+ * Modified at: Mon Oct 16 21:36:44 2006 -+ * Modified by: Olaf Tauber -+ * Modified at: Thu Nov 24 13:03:01 2005 -+ * Modified by: Antonio Cuni -+ * Modified at: Wed Oct 27 19:47:11 CEST 2004 -+ * Modified by: Joachim Fenkes -+ * -+ * This program is free software; you can redistribute -+ * it and/or modify it under the terms of the GNU General -+ * Public License as published by the Free Software -+ * Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * 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., 59 Temple Place, -+ * Suite 330, Boston, MA 02111-1307 USA -+ * -+ * -+ */ -+ -+#ifndef AUTOCONF_INCLUDED -+#include -+#endif -+ -+/* This driver is heavily dependent on the architecture, don't let -+ * anyone without an X86 machine use it. On laptops with AMD64 -+ * architecture this driver is only useable in 32 bit mode. -+ */ -+#ifdef CONFIG_X86 -+ -+#include -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#define KERNEL26 -+#include -+#else -+#include -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) -+#define STATIC_INPUT_DEV -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "acerhk.h" -+ -+/* #define ACERDEBUG */ -+/* #define DUMMYHW */ -+ -+#define ACERHK_VERSION "0.5.35" -+#define MODULE_NAME "acerhk" -+ -+/* maximum number of polling loops, adjust it if needed to values between -+ * 1 and 32 -+ */ -+#define MAX_POLLING_LOOPS 16U -+ -+/* maximum length for model string */ -+#define ACERHK_MODEL_STRLEN 16 -+/* size of mapped areas */ -+#define AREA_SIZE 0xffff -+/* needed for colussi algorithm */ -+#define XSIZE 20 -+ -+/* Module parameters */ -+static int poll=1; -+static int autowlan; -+static int usedritek=1; -+static int wlan_state=-1; -+static int bluetooth_state=-1; -+static int verbose; -+static unsigned int force_series; -+#ifdef KERNEL26 -+module_param(poll, int, 0444); -+module_param(autowlan, int, 0444); -+module_param(usedritek, int, 0444); -+module_param(verbose, int, 0444); -+module_param(wlan_state, int, 0444); -+module_param(bluetooth_state, int, 0444); -+module_param(force_series, uint, 0444); -+#else -+MODULE_PARM(poll, "i"); -+MODULE_PARM(autowlan, "i"); -+MODULE_PARM(wlan_state, "i"); -+MODULE_PARM(bluetooth_state, "i"); -+MODULE_PARM(usedritek, "i"); -+MODULE_PARM(verbose, "i"); -+MODULE_PARM(force_series, "i"); -+#endif -+MODULE_PARM_DESC(poll, "start polling timer"); -+MODULE_PARM_DESC(autowlan, "automatic switching of wlan hardware"); -+MODULE_PARM_DESC(wlan_state, "(assumed) initial state of WLAN LED/hardware"); -+MODULE_PARM_DESC(bluetooth_state, "(assumed) initial state of Bluetooth LED/hardware"); -+MODULE_PARM_DESC(usedritek, "enable dritek keyboard extension"); -+MODULE_PARM_DESC(verbose, "output additional information"); -+MODULE_PARM_DESC(force_series, "force laptop series, skip autodetection"); -+ -+/* input device */ -+static struct input_dev *acerhk_input_dev_ptr; -+#ifdef STATIC_INPUT_DEV -+static struct input_dev acerhk_input_dev; -+#endif -+ -+/* mapped IO area from 0xf0000 */ -+static void *reg1; -+/* mapped IO area from 0xe0000 */ -+static void *reg2; -+/* Pointer to mapped area at 0x400 on 520 series */ -+static void *preg400; -+/* location of IO routine in mapped area */ -+static unsigned int bios_routine; -+/* index of CMOS port to get key event */ -+static unsigned int cmos_index; -+/* function for bios call */ -+static bios_call call_bios; -+/* address of model string */ -+static char *acerhk_model_addr; -+/* copied string, maximum length 16 ('TravelMate xxx') */ -+static char acerhk_model_string[ACERHK_MODEL_STRLEN]; -+/* type of hardware access */ -+static t_acer_type acerhk_type; -+/* travelmate series */ -+static unsigned int acerhk_series; -+/* supported features for this model */ -+static unsigned int acerhk_model_features; -+/* map of acer key codes to acer key names */ -+static unsigned char acerhk_key2name[0xff]; -+/* map of acer key names to key events */ -+static t_map_name2event acerhk_name2event; -+/* timer for polling key presses */ -+static struct timer_list acerhk_timer_poll; -+/* polling active */ -+static int acerhk_polling_state; -+/* polling delay */ -+static unsigned acerhk_polling_delay = HZ/5; -+/* wlan hardware toggle */ -+static int acerhk_wlan_state; -+/* bluetooth hardware toggle */ -+static int acerhk_bluetooth_state; -+ -+/* bluetooth blinking state; added by Antonio Cuni -+ possible values: -+ -1: blinking disabled (default) -+ 0: blinking enabled, led currently off -+ 1: blinking enabled, led currently on -+*/ -+static int acerhk_blueled_blinking = -1; -+/* delay between two changes of state, in jiffies */ -+static unsigned acerhk_blueled_blinking_delay; -+/* timer for blinking */ -+static struct timer_list acerhk_timer_blinking; -+ -+/* function prototypes */ -+static void start_polling(void); -+static void stop_polling(void); -+ -+/* Added by Antonio Cuni */ -+static void start_blinking(void); -+static void stop_blinking(void); -+ -+/* {{{ Experimental use of dritek keyboard extension */ -+ -+#define EC_STATUS_REG 0x66 /* Status register of EC (R) */ -+#define EC_CNTL_REG 0x66 /* Controller command register of EC (W) */ -+#define EC_DATA_REG 0x62 /* EC data register (R/W) */ -+ -+#ifdef KERNEL26 -+ -+#include -+ -+#define KBD_STATUS_REG 0x64 /* Status register (R) */ -+#define KBD_CNTL_REG 0x64 /* Controller command register (W) */ -+#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */ -+ -+#else -+ -+#ifndef KEY_MEDIA -+#define KEY_MEDIA 226 -+#endif -+ -+#define preempt_disable() do { } while (0) -+#define preempt_enable_no_resched() do { } while (0) -+#define preempt_enable() do { } while (0) -+#define preempt_check_resched() do { } while (0) -+#include -+ -+#endif -+ -+static inline int my_i8042_read_status(void) -+{ -+ return inb(KBD_STATUS_REG); -+} -+static int my_i8042_wait_write(void) -+{ -+ int i = 0; -+ while ((my_i8042_read_status() & 0x02) && (i < 10000)) { -+ udelay(50); -+ i++; -+ } -+ return -(i == 10000); -+} -+static void send_kbd_cmd(unsigned char cmd, unsigned char val) -+{ -+ if (usedritek) { -+ preempt_disable(); -+ if (!my_i8042_wait_write()) -+ outb(cmd, KBD_CNTL_REG); -+ if (!my_i8042_wait_write()) -+ outb(val, KBD_DATA_REG); -+ preempt_enable_no_resched(); -+ } else { -+ printk(KERN_INFO"acerhk: request for accessing EC ignored\n" -+ KERN_INFO"acerhk: Use of dritek keyboard extension not enabled, use module\n" -+ KERN_INFO"acerhk: parameter usedritek=1 to do that (possibly dangerous)\n"); -+ } -+} -+#ifdef ACERDEBUG -+static inline int my_i8042_read_ecstatus(void) -+{ -+ return inb(EC_STATUS_REG); -+} -+static int my_i8042_wait_ecwrite(void) -+{ -+ int i = 0; -+ while ((my_i8042_read_ecstatus() & 0x02) && (i < 10000)) { -+ udelay(50); -+ i++; -+ } -+ return -(i == 10000); -+} -+static void send_ec_cmd(unsigned char cmd, unsigned char val) -+{ -+ if (usedritek) { -+ preempt_disable(); -+ if (!my_i8042_wait_ecwrite()) -+ outb(cmd, EC_CNTL_REG); -+ if (!my_i8042_wait_ecwrite()) -+ outb(val, EC_DATA_REG); -+ preempt_enable_no_resched(); -+ } else { -+ printk(KERN_INFO"acerhk: request for accessing EC ignored\n" -+ KERN_INFO"acerhk: Use of dritek keyboard extension not enabled, use module\n" -+ KERN_INFO"acerhk: parameter usedritek=1 to do that (possibly dangerous)\n"); -+ } -+} -+#endif -+#ifdef ACERDEBUG -+static void enable_mute_led_ec(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling mute led via EC\n"); -+ send_kbd_cmd(0x59, 0x94); -+} -+static void disable_mute_led_ec(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling mute led via EC\n"); -+ send_kbd_cmd(0x59, 0x95); -+} -+static void enable_dmm_function(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 2\n"); -+ send_kbd_cmd(0x45, 0xd3); -+} -+#endif -+static void enable_wlan_ec_1(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 1\n"); -+ send_kbd_cmd(0xe7, 0x01); -+ acerhk_wlan_state = 1; -+} -+static void disable_wlan_ec_1(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling WLAN via EC variant 1\n"); -+ send_kbd_cmd(0xe7, 0x00); -+ acerhk_wlan_state = 0; -+} -+static void enable_bluetooth_ec_1(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling Bluetooth via EC variant 1\n"); -+ send_kbd_cmd(0xe7, 0x03); -+ acerhk_bluetooth_state = 1; -+} -+static void disable_bluetooth_ec_1(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling Bluetooth via EC variant 1\n"); -+ send_kbd_cmd(0xe7, 0x02); -+ acerhk_bluetooth_state = 0; -+} -+static void enable_wlan_ec_2(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 2\n"); -+ send_kbd_cmd(0x45, acerhk_bluetooth_state ? 0xa2 : 0xa0); -+ acerhk_wlan_state = 1; -+} -+static void disable_wlan_ec_2(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling WLAN via EC variant 2\n"); -+ send_kbd_cmd(0x45, acerhk_bluetooth_state ? 0xa1 : 0xa3); -+ acerhk_wlan_state = 0; -+} -+static void enable_bluetooth_ec_2(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling Bluetooth via EC variant 2\n"); -+ send_kbd_cmd(0x45, acerhk_wlan_state ? 0xa2 : 0xa1); -+ acerhk_bluetooth_state = 1; -+} -+static void disable_bluetooth_ec_2(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling Bluetooth via EC variant 2\n"); -+ send_kbd_cmd(0x45, acerhk_wlan_state ? 0xa0 : 0xa3); -+ acerhk_bluetooth_state = 0; -+} -+#ifdef ACERDEBUG -+static void enable_wireless_ec(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling wireless hardware\n"); -+ if (usedritek) { -+ preempt_disable(); -+ if (!my_i8042_wait_ecwrite()) -+ outb(0x4d, EC_CNTL_REG); -+ if (!my_i8042_wait_ecwrite()) -+ outb(0xd2, EC_DATA_REG); -+ if (!my_i8042_wait_ecwrite()) -+ outb(0x01, EC_DATA_REG); -+ preempt_enable_no_resched(); -+ } -+ acerhk_wlan_state = 1; -+} -+static void disable_wireless_ec(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling wireless hardware\n"); -+ if (usedritek) { -+ preempt_disable(); -+ if (!my_i8042_wait_ecwrite()) -+ outb(0x4d, EC_CNTL_REG); -+ if (!my_i8042_wait_ecwrite()) -+ outb(0xd2, EC_DATA_REG); -+ if (!my_i8042_wait_ecwrite()) -+ outb(0x00, EC_DATA_REG); -+ preempt_enable_no_resched(); -+ } -+ acerhk_wlan_state = 0; -+} -+#endif -+static void enable_dritek_keyboard(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling dritek keyboard extension\n"); -+ send_kbd_cmd(0x59, 0x90); -+} -+static void disable_dritek_keyboard(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling dritek keyboard extension\n"); -+ send_kbd_cmd(0x59, 0x91); -+} -+static void enable_mail_led_ec_1(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling mail led via EC variant 1\n"); -+ send_kbd_cmd(0xe8, 0x01); -+} -+static void disable_mail_led_ec_1(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling mail led via EC variant 1\n"); -+ send_kbd_cmd(0xe8, 0x00); -+} -+ -+static void enable_mail_led_ec_2(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling mail led via EC variant 2\n"); -+ send_kbd_cmd(0x59, 0x92); -+} -+static void disable_mail_led_ec_2(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling mail led via EC variant 2\n"); -+ send_kbd_cmd(0x59, 0x93); -+} -+static void enable_mail_led_ec_3(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: enabling mail led via EC variant 3\n"); -+ if (usedritek) { -+ preempt_disable(); -+ if (!my_i8042_wait_write()) -+ outl(0x80008894, 0xCF8); -+ if (!my_i8042_wait_write()) -+ outw(0xC061, 0xCFC); -+ preempt_enable_no_resched(); -+ } -+} -+static void disable_mail_led_ec_3(void) -+{ -+ if (verbose) -+ printk(KERN_INFO "acerhk: disabling mail led via EC variant 3\n"); -+ if (usedritek) { -+ preempt_disable(); -+ if (!my_i8042_wait_write()) -+ outl(0x80008894, 0xCF8); -+ if (!my_i8042_wait_write()) -+ outw(0xC060, 0xCFC); -+ preempt_enable_no_resched(); -+ } -+} -+ -+/* }}} */ -+ -+/* {{{ string search functions */ -+ -+/* This is the Colussi algorithm, the code is taken from -+ http://www-igm.univ-mlv.fr/~lecroq/string -+*/ -+int preColussi(char *x, int m, int *h, int *next, int *shift) -+{ -+ int i, k, nd, q, r, s; -+ int hmax[XSIZE], kmin[XSIZE], nhd0[XSIZE], rmin[XSIZE]; -+ /* Computation of hmax */ -+ i = k = 1; -+ do { -+ while (x[i] == x[i - k]) -+ i++; -+ hmax[k] = i; -+ q = k + 1; -+ while (hmax[q - k] + k < i) { -+ hmax[q] = hmax[q - k] + k; -+ q++; -+ } -+ k = q; -+ if (k == i + 1) -+ i = k; -+ } while (k <= m); /* Computation of kmin */ -+ memset(kmin, 0, m*sizeof(int)); -+ r = 0; -+ for (i = m; i >= 1; --i) -+ if (hmax[i] < m) -+ kmin[hmax[i]] = i; /* Computation of rmin */ -+ for (i = m - 1; i >= 0; --i) { -+ if (hmax[i + 1] == m) -+ r = i + 1; -+ if (kmin[i] == 0) -+ rmin[i] = r; -+ else -+ rmin[i] = 0; -+ } /* Computation of h */ -+ s = -1; -+ r = m; -+ for (i = 0; i < m; ++i) -+ if (kmin[i] == 0) -+ h[--r] = i; -+ else -+ h[++s] = i; -+ nd = s; /* Computation of shift */ -+ for (i = 0; i <= nd; ++i) -+ shift[i] = kmin[h[i]]; -+ for (i = nd + 1; i < m; ++i) -+ shift[i] = rmin[h[i]]; -+ shift[m] = rmin[0]; /* Computation of nhd0 */ -+ s = 0; -+ for (i = 0; i < m; ++i) { -+ nhd0[i] = s; -+ if (kmin[i] > 0) -+ ++s; -+ } /* Computation of next */ -+ for (i = 0; i <= nd; ++i) -+ next[i] = nhd0[h[i] - kmin[h[i]]]; -+ for (i = nd + 1; i < m; ++i) -+ next[i] = nhd0[m - rmin[h[i]]]; -+ next[m] = nhd0[m - rmin[h[m - 1]]]; return(nd); -+} -+ -+int COLUSSI(char *x, int m, char *y, int n) { -+ int i, j, last, nd, -+ h[XSIZE], next[XSIZE], shift[XSIZE]; /* Processing */ -+ int match_pos; /* position of first match */ -+ nd = preColussi(x, m, h, next, shift); /* Searching */ -+ i = j = 0; -+ last = -1; -+ match_pos = -1; -+ while ( (match_pos == -1) -+ && (j <= n - m) ) { -+ while (i < m && last < j + h[i] && -+ x[h[i]] == y[j + h[i]]) -+ i++; -+ if (i >= m || last >= j + h[i]) { -+ /* Match found, bail out */ -+ match_pos = j; -+ i = m; -+ } -+ if (i > nd) -+ last = j + m - 1; -+ j += shift[i]; -+ i = next[i]; -+ } -+ return match_pos; -+} -+ -+/* }}} */ -+ -+/* {{{ hardware access functions */ -+ -+/* call_bios_ -+ * -+ * call request handler in mapped system rom -+ * -+ * the request is handed over via all 6 general purpose registers, results are -+ * taken from them and copied back to buf -+ */ -+static asmlinkage void call_bios_6xx(struct register_buffer *buf) -+{ -+ if (bios_routine) { -+ local_irq_disable(); -+ __asm__ __volatile__( -+ "movl %1,%%edx\n\t" -+ "pusha\n\t" -+ "movl %%edx,%%ebp\n\t" -+ "movl (%%ebp),%%eax\n\t" -+ "movl 4(%%ebp),%%ebx\n\t" -+ "movl 8(%%ebp),%%ecx\n\t" -+ "movl 12(%%ebp),%%edx\n\t" -+ "movl 16(%%ebp),%%edi\n\t" -+ "movl 20(%%ebp),%%esi\n\t" -+ "pushl %%ebp\n\t" -+ "call *%0\n\t" -+ "popl %%ebp\n\t" -+ "movl %%eax, (%%ebp)\n\t" -+ "movl %%ebx, 4(%%ebp)\n\t" -+ "movl %%ecx, 8(%%ebp)\n\t" -+ "movl %%edx, 12(%%ebp)\n\t" -+ "movl %%edi, 16(%%ebp)\n\t" -+ "movl %%esi, 20(%%ebp)\n\t" -+ "popa\n\t" -+ : -+ :"m" (bios_routine), "m" (buf) -+ :"%eax", "%ebx", "%ecx", "%edx", "%edi", "%esi", "%ebp" -+ ); -+ local_irq_enable(); -+ } -+} -+ -+static asmlinkage void call_bios_52x(struct register_buffer *buf) -+{ -+ if (bios_routine) { -+ local_irq_disable(); -+ __asm__ __volatile__( -+ "movl %2,%%edx\n\t" -+ "pusha\n\t" -+ "movl %%edx,%%ebp\n\t" -+ "movl (%%ebp),%%eax\n\t" -+ "movl 4(%%ebp),%%ebx\n\t" -+ "movl 8(%%ebp),%%ecx\n\t" -+ "movl 12(%%ebp),%%edx\n\t" -+ "movl 16(%%ebp),%%edi\n\t" -+ "movl 20(%%ebp),%%esi\n\t" -+ "pushl %%ebp\n\t" -+ "movl %1, %%ebp\n\t" -+ "call *%0\n\t" -+ "popl %%ebp\n\t" -+ "movl %%eax, (%%ebp)\n\t" -+ "movl %%ebx, 4(%%ebp)\n\t" -+ "movl %%ecx, 8(%%ebp)\n\t" -+ "movl %%edx, 12(%%ebp)\n\t" -+ "movl %%edi, 16(%%ebp)\n\t" -+ "movl %%esi, 20(%%ebp)\n\t" -+ "popa\n\t" -+ : -+ :"m" (bios_routine), "m" (preg400), "m" (buf) -+ :"%eax", "%ebx", "%ecx", "%edx", "%edi", "%esi", "%ebp" -+ ); -+ local_irq_enable(); -+ } -+} -+ -+#define PRINT_BUFFER(x) \ -+ printk(KERN_INFO"acerhk: eax=0x%x ebx=0x%x ecx=0x%x edx=0x%x\n" \ -+ "acerhk: edi=0x%x esi=0x%x ebp=0x%x\n", \ -+ x.eax, x.ebx, x.ecx, x.edx, x.edi, x.esi, x.ebp); -+ -+/* get_fnkey_event -+ * -+ * gets the first (oldest) key id from the queue of events -+ * -+ * return value: id of key -+ */ -+static int get_fnkey_event(void) -+{ -+ struct register_buffer regs; -+ regs.eax = 0x9610; -+ regs.ebx = 0x61C; -+ /* clear other registers, some models need this */ -+ regs.ecx = 0; -+ regs.edx = 0; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ return regs.eax & 0xffff; -+} -+ -+/* get_thermal_event -+ * -+ * does what? -+ * -+ * return value: event ? -+ */ -+static int get_thermal_event(void) -+{ -+ struct register_buffer regs; -+ if (acerhk_model_features & TM_F_THERMAL) { -+ regs.eax = 0x9612; -+ regs.ebx = 0x12e; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: thermal event = 0x%x\n", regs.eax); -+ } else { -+ regs.eax = 0x00; -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: thermal event not supported\n"); -+ } -+ return regs.eax & 0xffff; -+} -+ -+#ifdef ACERDEBUG -+/* pbutton_fct -+ * -+ * does what? -+ * -+ * return value: ? -+ */ -+static int pbutton_fct(void) -+{ -+ struct register_buffer regs; -+ if (acerhk_model_features & TM_F_PBUTTON) { -+ regs.eax = 0x9612; -+ regs.ebx = 0x10b; -+ regs.ecx = 0x2; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: pbutton = 0x%x\n", regs.eax); -+ } else { -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: pbutton function not supported\n"); -+ regs.eax = 0x00; -+ } -+ return regs.eax & 0xffff; -+} -+#endif -+ -+/* wbutton_fct_1 -+ * -+ * turn on installed Bluetooth hardware together with the corresponding LED -+ * -+ * val: 0 turns off the LED -+ * 1 turns the LED to green/blue -+ * -+ * return value: ? -+ */ -+static int wbutton_fct_1(int val) -+{ -+ struct register_buffer regs; -+ if (acerhk_model_features & TM_F_WBUTTON) { -+ acerhk_bluetooth_state = val; -+ regs.eax = 0x9610; -+ regs.ebx = ((val & 0xff) << 8) | 0x34; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: wbutton1 = 0x%x\n", regs.eax); -+ } else { -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: wbutton function 1 not supported\n"); -+ regs.eax = 0x00; -+ } -+ return regs.eax & 0xffff; -+} -+ -+/* wbutton_fct_2 -+ * -+ * turn on installed WLAN hardware together with the corresponding LED -+ * -+ * val: 0 turns off the LED -+ * 1 turns the LED to orange -+ * -+ * return value: ? -+ */ -+static int wbutton_fct_2(int val) -+{ -+ struct register_buffer regs; -+ if (acerhk_model_features & TM_F_WBUTTON) { -+ acerhk_wlan_state = val; -+ regs.eax = 0x9610; -+ regs.ebx = ((val & 0xff) << 8) | 0x35; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: wbutton2 = 0x%x\n", regs.eax); -+ } else { -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: wbutton function 2 not supported\n"); -+ regs.eax = 0x00; -+ } -+ return regs.eax & 0xffff; -+} -+ -+/* get_cmos_index -+ * -+ * gets index of CMOS port from ROM. The number of events is monitored -+ * in this port. -+ * -+ * return value: index of CMOS port -+ */ -+static int get_cmos_index(void) -+{ -+ struct register_buffer regs; -+ regs.eax = 0x9610; -+ regs.ebx = 0x51C; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ cmos_index = regs.ecx & 0xff; -+ if (verbose) -+ printk(KERN_INFO"acerhk: cmos index set to 0x%x\n", cmos_index); -+ return cmos_index; -+} -+ -+/* get_nr_events -+ * -+ * gets the number of cached events (keys pressed) in queue. Up to 31 events -+ * are cached. -+ * -+ * return value: number of events in queue -+ */ -+static int get_nr_events(void) -+{ -+ unsigned long flags; -+ unsigned char c = 0; -+ -+ spin_lock_irqsave (&rtc_lock, flags); -+#ifndef DUMMYHW -+ if (cmos_index) -+ c = CMOS_READ(cmos_index); -+ else if (verbose > 3) -+ printk(KERN_INFO"acerhk: get_nr_events - no valid cmos index set\n"); -+#endif -+ spin_unlock_irqrestore (&rtc_lock, flags); -+ return c; -+} -+ -+/* set_mail_led -+ * -+ * change state of mail led -+ * -+ * val: 0 - switch led off -+ * 1 - switch led on (blinking) -+ * -+ * return value: 1 - action succesfull (val valid) -+ * 0 - no action taken (val invalid) -+ */ -+static int set_mail_led(int val) -+{ -+ struct register_buffer regs; -+ if (acerhk_model_features & TM_F_MAIL_LED) { -+ regs.eax = 0x9610; -+ regs.ebx = ((val & 0xff) << 8) | 0x31; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: mail led set to = 0x%x\n", val); -+ } else if (acerhk_model_features & TM_F_MAIL_LED_EC) { -+ if (val == 1) -+ enable_mail_led_ec_1(); -+ else if (val == 0) -+ disable_mail_led_ec_1(); -+ } else if (acerhk_model_features & TM_F_MAIL_LED_EC2) { -+ if (val == 1) -+ enable_mail_led_ec_2(); -+ else if (val == 0) -+ disable_mail_led_ec_2(); -+ } else if (acerhk_model_features & TM_F_MAIL_LED_EC3) { -+ if (val == 1) -+ enable_mail_led_ec_3(); -+ else if (val == 0) -+ disable_mail_led_ec_3(); -+ } else { -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: mail led not supported\n"); -+ regs.eax = 0x00; -+ } -+ return regs.eax & 0xffff; -+} -+ -+/* launch_connect -+ * -+ * does what? -+ * val: 1 - only known value from windows driver -+ */ -+static int launch_connect(int val) -+{ -+ struct register_buffer regs; -+ if (acerhk_model_features & TM_F_CONNECT) { -+ regs.eax = 0x9610; -+ regs.ebx = ((val & 0xff) << 8) | 0x2e; -+ preempt_disable(); -+ call_bios(®s); -+ preempt_enable_no_resched(); -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: connect(%d) = 0x%x\n", val, regs.eax); -+ } else { -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: connect not supported\n"); -+ regs.eax = 0x00; -+ } -+ return regs.eax & 0xffff; -+} -+ -+/* }}} */ -+ -+/* {{{ hardware probing */ -+ -+static struct proc_dir_entry *proc_acer_dir; -+ -+static unsigned int __init find_hk_area(void) -+{ -+ int offset, sig; -+ unsigned int fkt; -+ fkt = 0; -+ sig = -1; /* offset to signature in io area */ -+ /* Look for signature, start at 0xf0000, search until 0xffff0 */ -+ for (offset = 0;offset < 0xfffd; offset += 16) { -+ if (readl(reg1 + offset) == 0x30552142) { -+ sig = offset; -+ offset = 0xffff; -+ } -+ } -+ if (sig < 0) -+ printk(KERN_WARNING"acerhk: could not find request handler, possibly not all functions available\n"); -+ else { -+ /* compute location of bios routine */ -+ fkt = readl(reg1 + sig + 5); -+ /* adjust fkt to address of mapped IO area */ -+ if (fkt >= 0xf0000) -+ fkt = (unsigned int)reg1 + fkt - 0xf0000; -+ else if (fkt >= 0xe0000) -+ fkt = (unsigned int)reg1 + fkt - 0xe0000; -+ else -+ fkt = 0; -+ } -+ return fkt; -+} -+ -+static void print_features(void) -+{ -+ int i; -+ printk(KERN_INFO"acerhk: supported keys:"); -+ for (i = 0; i < 255; i++) { -+ switch (acerhk_key2name[i]) { -+ case k_help: printk(" help"); break; -+ case k_setup: printk(" setup"); break; -+ case k_p1: printk(" p1"); break; -+ case k_p2: printk(" p2"); break; -+ case k_p3: printk(" p3"); break; -+ case k_www: printk(" www"); break; -+ case k_mail: printk(" mail"); break; -+ case k_wireless: printk(" wireless"); break; -+ case k_power: printk(" power"); break; -+ case k_mute: printk(" mute"); break; -+ case k_volup: printk(" volup"); break; -+ case k_voldn: printk(" voldn"); break; -+ case k_res: printk(" res"); break; -+ case k_close: printk(" close"); break; -+ case k_open: printk(" open"); break; -+ case k_wireless2: printk(" wireless2"); break; -+ case k_play: printk(" play"); break; -+ case k_stop: printk(" stop"); break; -+ case k_prev: printk(" prev"); break; -+ case k_next: printk(" next"); break; -+ case k_display: printk(" display"); break; -+ default: break; -+ } -+ } -+ printk("\n"); -+ if (acerhk_model_features & TM_F_MUTE_LED_EC) -+ printk(KERN_INFO"acerhk: mute led is supported\n"); -+ if (acerhk_model_features & TM_F_MAIL_LED) -+ printk(KERN_INFO"acerhk: mail led is supported\n"); -+ else if (acerhk_model_features & TM_F_MAIL_LED_EC) -+ printk(KERN_INFO"acerhk: mail led (EC) is supported\n"); -+ else if (acerhk_model_features & TM_F_MAIL_LED_EC2) -+ printk(KERN_INFO"acerhk: mail led (EC2) is supported\n"); -+ else if (acerhk_model_features & TM_F_MAIL_LED_EC3) -+ printk(KERN_INFO"acerhk: mail led (EC3) is supported\n"); -+ if (acerhk_model_features & TM_F_WLAN_EC1) -+ printk(KERN_INFO"acerhk: wlan control (EC1) is supported\n"); -+ else if (acerhk_model_features & TM_F_WLAN_EC2) -+ printk(KERN_INFO"acerhk: wlan control (EC2) is supported\n"); -+ if (acerhk_model_features & TM_F_BLUE_EC1) -+ printk(KERN_INFO"acerhk: bluetooth control (EC1) is supported\n"); -+ else if (acerhk_model_features & TM_F_BLUE_EC2) -+ printk(KERN_INFO"acerhk: bluetooth control (EC2) is supported\n"); -+ printk(KERN_INFO"acerhk: supported functions:"); -+ if (acerhk_model_features & TM_F_CONNECT) -+ printk(" connect"); -+ if (acerhk_model_features & TM_F_THERMAL) -+ printk(" thermal"); -+ if (acerhk_model_features & TM_F_PBUTTON) -+ printk(" pbutton"); -+ if (acerhk_model_features & TM_F_WBUTTON) -+ printk(" wbutton"); -+ printk("\n"); -+} -+ -+static void __init setup_keymap_model(unsigned int series) -+{ -+ /* clear mapping keycode -> keyname, */ -+ memset(&acerhk_key2name[0], k_none, sizeof(acerhk_key2name)); -+ /* first set the common keys, namely FnF1 and FnF2, */ -+ acerhk_key2name[1] = k_help; -+ acerhk_key2name[2] = k_setup; -+ /* then set known keycodes according to model */ -+ switch (series) { -+ case 110: -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[3] = k_power; -+ acerhk_key2name[8] = k_mute; -+ acerhk_key2name[32] = k_volup; -+ acerhk_key2name[33] = k_voldn; -+ /* C110 generates 2 extra codes when opening/closing the lid */ -+ acerhk_key2name[74] = k_close; -+ acerhk_key2name[75] = k_open; -+ break; -+ case 300: /* treat C300 like C100 with Bluetooth button */ -+ acerhk_key2name[68] = k_wireless2; -+ case 100: -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[49] = k_www; -+ acerhk_key2name[54] = k_mail; -+ acerhk_key2name[3] = k_power; -+ acerhk_key2name[8] = k_mute; -+ acerhk_key2name[32] = k_volup; -+ acerhk_key2name[33] = k_voldn; -+ break; -+ default: -+ /* only the two common keys are supported */ -+ break; -+ case 210: -+ acerhk_key2name[19] = k_p1; -+ acerhk_key2name[20] = k_p2; -+ acerhk_key2name[17] = k_www; -+ acerhk_key2name[18] = k_mail; -+ break; -+ case 220: -+ case 260: /* 260 with same keys? */ -+ acerhk_key2name[49] = k_p1; -+ acerhk_key2name[19] = k_p2; -+ acerhk_key2name[18] = k_www; -+ acerhk_key2name[17] = k_mail; -+ break; -+ case 230: -+ case 280: /* 280 with same keys? */ -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[54] = k_www; -+ break; -+ case 1500: -+ acerhk_key2name[0x49] = k_setup; -+ acerhk_key2name[0x36] = k_www; -+ acerhk_key2name[0x31] = k_mail; -+ acerhk_key2name[0x11] = k_p1; -+ acerhk_key2name[0x12] = k_p2; -+ acerhk_key2name[0x30] = k_wireless; -+ acerhk_key2name[0x44] = k_wireless2; -+ acerhk_key2name[0x03] = k_power; -+ break; -+ case 240: -+ acerhk_key2name[0x31] = k_www; -+ acerhk_key2name[0x36] = k_mail; -+ acerhk_key2name[0x11] = k_p1; -+ acerhk_key2name[0x12] = k_p2; -+ acerhk_key2name[0x44] = k_wireless; -+ acerhk_key2name[0x30] = k_wireless2; -+ acerhk_key2name[0x03] = k_power; -+ acerhk_key2name[0x08] = k_mute; -+ // acerhk_key2name[] = k_volup; -+ // acerhk_key2name[] = k_voldn; -+ break; -+ case 2900: -+ acerhk_key2name[0x31] = k_mail; /* with led */ -+ acerhk_key2name[0x36] = k_www; -+ acerhk_key2name[0x11] = k_p1; -+ acerhk_key2name[0x12] = k_p2; -+ acerhk_key2name[0x30] = k_wireless; /* wireless, with led, related with autowlan=1 */ -+ break; -+ case 250: /* enriqueg@altern.org */ -+ /* TravelMate 254LMi_DT manual common for 240/250 series, but key order -+ differ from 240 already present on acerhk driver */ -+ /* TravelMate 254LMi_DT: 6 buttons: left to right: mail, www, p1, p2, bluetooth, wireless */ -+ acerhk_key2name[0x31] = k_mail; /* with led */ -+ acerhk_key2name[0x36] = k_www; -+ acerhk_key2name[0x11] = k_p1; -+ acerhk_key2name[0x12] = k_p2; -+ acerhk_key2name[0x44] = k_wireless2; /* bluetooth, hw optional */ -+ acerhk_key2name[0x30] = k_wireless; /* wireless, with led, related with autowlan=1 */ -+ acerhk_key2name[0x03] = k_power; /* Fn+F3 */ -+ acerhk_key2name[0x08] = k_mute; /* Fn+F8 */ -+ break; -+ case 380: -+ /* TM 380 has same codes as TM 370, with an additional one */ -+ acerhk_key2name[0x03] = k_power; -+ case 370: -+ acerhk_key2name[0x30] = k_wireless; -+ acerhk_key2name[0x11] = k_p1; -+ acerhk_key2name[0x12] = k_p2; -+ acerhk_key2name[0x13] = k_p3; -+ acerhk_key2name[0x36] = k_www; -+ acerhk_key2name[0x31] = k_mail; -+ break; -+ case 360: -+ /* 360 series has the same layout as 350, with an -+ additional wireless key */ -+ acerhk_key2name[64] = k_wireless; -+ case 350: -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[20] = k_p3; -+ acerhk_key2name[21] = k_www; -+ acerhk_key2name[19] = k_mail; -+ break; -+ case 520: -+ acerhk_key2name[19] = k_p1; -+ acerhk_key2name[20] = k_p2; -+ acerhk_key2name[17] = k_www; -+ acerhk_key2name[18] = k_mail; -+ break; -+ case 610: -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[19] = k_p3; -+ acerhk_key2name[21] = k_www; -+ acerhk_key2name[20] = k_mail; -+ acerhk_key2name[64] = k_wireless; -+ break; -+ case 630: -+ /* 630 has all keys of 620 plus one */ -+ acerhk_key2name[8] = k_mute; -+ case 620: -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[19] = k_p3; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[3] = k_power; -+ acerhk_key2name[32] = k_volup; -+ acerhk_key2name[33] = k_voldn; -+ break; -+ case 290: -+ case 420: -+ case 430: -+ case 530: -+ case 540: -+ case 650: -+ case 660: -+ case 800: -+ case 1450: -+ case 2300: -+ case 2350: -+ case 4000: -+ case 4050: -+ case 6000: -+ case 8000: -+ case 4100: -+ case 4150: -+ case 4500: -+ case 4600: -+ case 4650: -+ case 1680: -+ case 1690: -+ /* keys are handled by dritek EC */ -+ acerhk_key2name[1] = k_none; -+ acerhk_key2name[2] = k_none; -+ break; -+ case 1300: -+ case 1310: -+ case 1350: -+ case 1360: -+ case 1400: -+ case 1700: -+ case 1800: -+ case 2000: -+ case 2010: -+ case 2020: -+ /* Aspire 13xx series laptops use dritek hardware, no -+ acerhk-mapping needed -+ VolUp and VolDown are managed as normal keys -+ 1300/1310 series should have P1, P2, Mail, WWW, Mute buttons -+ 1353 has bluetooth, wifi, p1, p2, www, mail, help, setup, power -+ and mute -+ Aspire 1400/1450/Ferrari use dritek EC, too -+ 1450 should have bluetooth, wifi, p1, p2, www, mail, help, -+ setup, power and mute -+ Aspire 1700 uses dritek EC, too -+ 1700 should have bluetooth, wifi, p1, p2, www, mail, help, -+ setup, power and mute -+ need the MM-buttons Activation? (forward, shuffle, ...) -+ 2000 hast lots of MM buttons -+ 2010 should have bluetooth, wifi, p1, p2, www, mail, help, -+ setup, power and mute -+ */ -+ acerhk_key2name[1] = k_none; -+ acerhk_key2name[2] = k_none; -+ break; -+ case 1600: -+ /* Aspire 1600 has acer keycode 0x49 for FnF2 */ -+ acerhk_key2name[73] = k_setup; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[19] = k_p3; -+ acerhk_key2name[3] = k_power; -+ acerhk_key2name[8] = k_mute; -+ /* VolUp and VolDown keys doesn't seem to be managed as special keys -+ but as normal keys ! */ -+ break; -+ case 5020: /* Aspire 5020 has 0x6a for Fn+F2 */ -+ acerhk_key2name[2] = k_none; -+ acerhk_key2name[106] = k_setup; -+ acerhk_key2name[3] = k_power; -+ acerhk_key2name[5] = k_display; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[68] = k_wireless2; -+ break; -+ case 2410: /* TM 2410 is very similar to Aspire 5020, but has 0x6s for Fn-F3 */ -+ acerhk_key2name[2] = k_none; -+ acerhk_key2name[106] = k_setup; -+ acerhk_key2name[109] = k_power; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[68] = k_wireless2; -+ break; -+ case 40100: -+ /* Medion MD40100, 4 keys */ -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[55] = k_res; -+ break; -+ case 96500: -+ case 95400: -+ /* Medion MD95400, many keys */ -+ acerhk_key2name[49] = k_mail; /* 1 */ -+ acerhk_key2name[54] = k_www; /* 2 */ -+ acerhk_key2name[48] = k_wireless; /* 3 */ -+ acerhk_key2name[68] = k_wireless2; /* 4 (Bluetooth) */ -+ -+ acerhk_key2name[17] = k_p1; /* 5 */ -+ acerhk_key2name[18] = k_p2; /* 6 */ -+ acerhk_key2name[36] = k_play; /* 7 */ -+ acerhk_key2name[37] = k_stop; /* 8 */ -+ acerhk_key2name[34] = k_prev; /* 9 */ -+ acerhk_key2name[35] = k_next; /* 10 */ -+ acerhk_key2name[33] = k_voldn; /* 11 */ -+ acerhk_key2name[32] = k_volup; /* 12 */ -+ acerhk_key2name[38] = k_p3; /* 13 */ -+ acerhk_key2name[8] = k_mute; /* 14 */ -+ -+ acerhk_key2name[1] = k_help; /* FN+F1 (Help) */ -+ acerhk_key2name[5] = k_display; /* FN+F3 (Display switch) */ -+ acerhk_key2name[6] = k_res; /* FN+F4 (Display ein/ausschalten) */ -+ break; -+ case 42200: -+ /* Medion MD42200, 7 keys, no setup */ -+ acerhk_key2name[2] = k_none; -+ acerhk_key2name[5] = k_display; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ break; -+ case 9783: -+ /* Medion MD9783, 6 keys + info, no setup */ -+ acerhk_key2name[2] = k_none; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[19] = k_p3; -+ acerhk_key2name[8] = k_mute; -+ break; -+ case 7400: -+ /* Amilo Pro V2000 does not have Help and Setup key (?) -+ Amilo M 7400 has Help key, disabling only setup -+ */ -+ acerhk_key2name[2] = k_none; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ break; -+ case 1559: -+ acerhk_key2name[6] = k_display; /* FN+F4 (Display ein/ausschalten) */ -+ case 1555: -+ /* AOpen (Ahtec Signal 1555M) is similar to FS Amilo M */ -+ acerhk_key2name[2] = k_none; -+ acerhk_key2name[48] = k_wireless; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[34] = k_prev; -+ acerhk_key2name[35] = k_next; -+ acerhk_key2name[36] = k_play; -+ acerhk_key2name[37] = k_stop; -+ break; -+ case 6800: -+ case 7820: -+ /* Amilo D does not have Setup key */ -+ acerhk_key2name[2] = k_none; -+ acerhk_key2name[49] = k_mail; -+ acerhk_key2name[54] = k_www; -+ acerhk_key2name[17] = k_p1; -+ acerhk_key2name[18] = k_p2; -+ acerhk_key2name[19] = k_p3; -+ acerhk_key2name[8] = k_mute; -+ break; -+ } -+} -+ -+static void __init setup_model_features(unsigned int series) -+{ -+ switch (series) { -+ case 200: -+ case 210: -+ case 520: -+ /* nothing special */ -+ acerhk_model_features = 0; -+ acerhk_type = TM_old; -+ break; -+ case 220: -+ case 230: -+ case 260: -+ case 280: -+ case 360: -+ case 40100: /* Medion MD40100 */ -+ case 95400: /* Medion MD95400 */ -+ case 96500: /* Medion MD96500 */ -+ /* all special functions, no mail led */ -+ acerhk_model_features = 0x00f00000; -+ acerhk_type = TM_new; -+ break; -+ case 42200: /* Medion MD42200 */ -+ /* has WLAN button, should call connect() */ -+ acerhk_model_features = TM_F_WBUTTON | TM_F_CONNECT; -+ acerhk_type = TM_old; -+ break; -+ case 9783: /* Medion MD9783 */ -+ /* only email led */ -+ acerhk_model_features = TM_F_MAIL_LED; -+ acerhk_type = TM_new; -+ break; -+ case 1600: -+ acerhk_type = TM_new; -+ /* Do Aspire 1600 series have special functions or not ? I enable -+ them, perhaps it helps with problems Francois Valenduc has */ -+ acerhk_model_features = 0x00f00000; -+ break; -+ case 300: -+ case 100: -+ case 110: -+ case 240: -+ case 350: -+ case 610: -+ case 620: -+ case 630: -+ /* all special functions, mail led */ -+ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000; -+ acerhk_type = TM_new; -+ break; -+ case 370: -+ case 380: -+ case 2410: -+ case 2900: /* Medion MD2900 */ -+ case 2100: /* TM 2100 uses same driver as 5020 */ -+ case 5020: /* Aspire 5020 is still old hardware */ -+ acerhk_model_features = TM_F_MAIL_LED | TM_F_CONNECT| TM_F_WBUTTON; -+ acerhk_type = TM_new; -+ break; -+ case 7400: -+ case 1555: -+ case 1559: -+ /* all special functions for Fujitsu-Siemens Amilo M7400, Pro V2000; AOpen */ -+ acerhk_model_features = 0x00f00000; -+ acerhk_type = TM_new; -+ break; -+ case 6800: -+ case 7820: -+ /* mail led and all special functions for FS Amilo D */ -+ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000; -+ acerhk_type = TM_new; -+ break; -+ case 2350: -+ case 4050: -+ acerhk_wlan_state = 1; // Default state is on -+ case 290: -+ /* no special functions, wireless hardware controlled by EC */ -+ acerhk_model_features = TM_F_WLAN_EC2 | TM_F_BLUE_EC2; -+ acerhk_type = TM_dritek; -+ break; -+ case 650: -+ case 1300: -+ case 1310: -+ case 1400: -+ case 1700: -+ /* all special functions, wireless hardware can be controlled */ -+ acerhk_model_features = 0x00f00000; -+ acerhk_type = TM_dritek; -+ break; -+ case 4100: -+ case 4600: -+ case 1680: -+ case 1690: /* Aspire 1680/1690 should be similar to TM 4100/4600 */ -+ /* mail led, wireless and bluetooth controlled the old way, but keys are -+ controlled by normal keyboard controller, so mark as dritek and -+ deactivate dritek use */ -+ acerhk_model_features = TM_F_MAIL_LED | TM_F_WBUTTON; -+ acerhk_type = TM_dritek; -+ usedritek=0; -+ break; -+ case 660: -+ case 800: -+ /* all special functions, mail led */ -+ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000; -+ acerhk_type = TM_dritek; -+ break; -+ case 1350: -+ case 1360: -+ /* mail led, handled by EC, wireless HW is not (yet) controllable ? */ -+ acerhk_model_features = TM_F_MAIL_LED_EC|TM_F_WLAN_EC1; -+ acerhk_type = TM_dritek; -+ break; -+ case 1450: -+ /* Bluetooth/Wlan led, Mail led handled by EC (variant 3) */ -+ acerhk_model_features = TM_F_MAIL_LED_EC3|TM_F_WBUTTON; -+ acerhk_type = TM_dritek; -+ break; -+ case 1500: -+ /* Bluetooth/Wlan led */ -+ acerhk_model_features = TM_F_WBUTTON; -+ acerhk_type = TM_new; -+ break; -+ case 420: -+ case 430: -+ /* all functions and dritek EC, mail LED is handled by EC, second -+ variant. An additional led is available, mute. (really?) -+ */ -+ acerhk_type = TM_dritek; -+ acerhk_model_features = TM_F_MUTE_LED_EC|TM_F_MAIL_LED_EC2; -+ break; -+ case 2300: -+ case 4000: -+ case 4500: -+ /* wireless hardware, hopefully under control of my driver */ -+ acerhk_type = TM_dritek; -+ acerhk_model_features = TM_F_BLUE_EC1|TM_F_WLAN_EC1; -+ break; -+ case 3200: -+ /* test, if this model uses old style wlan control */ -+ acerhk_model_features = TM_F_WBUTTON; -+ acerhk_type = TM_dritek; -+ break; -+ case 6000: -+ case 8000: -+ /* 6000 and 8000 have wireless hardware, but I don't know how to handle, -+ so I choose no features */ -+ acerhk_type = TM_dritek; -+ break; -+ case 530: -+ case 540: -+ case 2000: -+ /* No features (?) dritek EC, mail LED is handled by EC but -+ different from other Aspire series */ -+ acerhk_type = TM_dritek; -+ acerhk_model_features = TM_F_MAIL_LED_EC2; -+ break; -+ case 4150: -+ case 4650: -+ /* Dritek EC, bluetooth, wifi, mail */ -+ /* According to Andreas Stumpfl his TM 4652LMi does also work as series -+ 3200, which might mean that the BIOS function accesses the EC */ -+ acerhk_type = TM_dritek; -+ acerhk_model_features = TM_F_MAIL_LED_EC2 | TM_F_WLAN_EC2 | TM_F_BLUE_EC2; -+ break; -+ case 1800: -+ case 2010: -+ case 2020: -+ /* Dritek EC, bluetooth, wifi, mail */ -+ acerhk_type = TM_dritek; -+ acerhk_model_features = TM_F_MAIL_LED_EC2 | TM_F_WLAN_EC2 | TM_F_BLUE_EC2; -+ acerhk_wlan_state = 1; // Default state is on -+ break; -+ case 250: /* enriqueg@altern.org */ -+ /* TravelMate254LMi_DT : mail led, bluetooth (button present, hw optional), wifi (with led) */ -+ acerhk_model_features = TM_F_MAIL_LED| -+ TM_F_WBUTTON ; -+ acerhk_type = TM_new; -+ acerhk_wlan_state = 0; //Initial state is off on 254LMi_DT -+ break; -+ default: -+ /* nothing special */ -+ acerhk_model_features = 0; -+ acerhk_type = TM_unknown; -+ break; -+ } -+ /* set the correct bios call function according to type */ -+ if ((acerhk_type == TM_new) || (acerhk_type == TM_dritek)) { -+ call_bios = call_bios_6xx; -+ if (verbose > 2) -+ printk(KERN_INFO"acerhk: using call_bios_6xx mode\n"); -+ } else { -+ call_bios = call_bios_52x; -+ if (verbose > 2) -+ printk(KERN_INFO"acerhk: using call_bios_52x mode\n"); -+ } -+ /* remove key file on dritek hardware */ -+ if (acerhk_type == TM_dritek) { -+ remove_proc_entry("key", proc_acer_dir); -+ } -+ /* setup available keys */ -+ setup_keymap_model(acerhk_series); -+ if (verbose > 1) -+ print_features(); -+} -+ -+static unsigned int __init determine_laptop_series(char * str) -+{ -+ /* 0 means unknown series, handled like TM 200 */ -+ unsigned int series = 0; -+ if (strncmp(str, "TravelMate ", 11) == 0) { -+ switch (str[11]) { -+ case 'C': -+ if (str[12] == '1') { -+ if (str[13] == '0') { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates TM C100 series\n"); -+ series = 100; -+ } else if (str[13] == '1') { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates TM C110 series\n"); -+ series = 110; -+ } -+ } else if (str[12] == '3') { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates TM C300 series\n"); -+ series = 300; -+ } -+ break; -+ case 'F': -+ if (str[12] == '4') { -+ series = 230; -+ } -+ break; -+ case '2': -+ if (str[14] == '0') { -+ /* newer Travelmate 2xxx series */ -+ switch (str[12]) { -+ case '0': -+ case '5': -+ series = 2000; // 2000 and 2500 are the same -+ break; -+ case '1': -+ if (str[13] == '0') -+ series = 2100; -+ break; -+ case '2': -+ case '7': -+ series = 2200; // 2200 and 2700 are the same -+ break; -+ case '3': -+ if (str[13] == '0') -+ series = 4000; // 2300 is the same as 4000 -+ else if (str[13] == '5') -+ series = 4050; // 2350 is the same as 4050 -+ break; -+ case '4': -+ if (str[13] == '1') -+ series = 2410; -+ break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM 2xxx series\n"); -+ break; -+ } -+ } else { -+ /* older Travelmate 2xx series */ -+ switch (str[12]) { -+ case '0': series = 200; break; -+ case '1': series = 210; break; -+ case '2': series = 220; break; -+ case '4': series = 240; break; -+ case '5': series = 250; break; /* enriqueg@altern.org */ -+ case '6': series = 260; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM 2xx series\n"); -+ break; -+ } -+ } -+ break; -+ case '3': -+ switch (str[12]) { -+ case '0': series = 3200; break; /* TM 3000 works like TM 3200 */ -+ /* Travelmate 3xx series */ -+ case '5': series = 350; break; -+ case '6': series = 360; break; -+ case '7': series = 370; break; -+ case '8': series = 380; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM 3xx series\n"); -+ break; -+ } -+ break; -+ case '4': -+ if ( (strnlen(str, ACERHK_MODEL_STRLEN-1) == 15) && -+ (str[14] == '0') ) { /* Travelmate 4xxx series */ -+ switch (str[12]) { -+ case '0': /* 4000 and 4500 are the same */ -+ case '5': -+ series = 4000; -+ break; -+ case '1': -+ case '6': /* 4100 and 4600 are the same */ -+ series = 4100; -+ break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM 4xxx series\n"); -+ break; -+ } -+ } else { /* Travelmate 4xx series */ -+ switch (str[12]) { -+ case '2': series = 420; break; -+ case '3': series = 430; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM 4xx series\n"); -+ break; -+ } -+ } -+ break; -+ case '5': /* Travelmate 5xx series */ -+ if (str[12] == '2') -+ series = 520; -+ else if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM 5xx series\n"); -+ break; -+ case '6': /* older Travelmate 6xx series */ -+ switch (str[12]) { -+ case '1': series = 610; break; -+ case '2': series = 620; break; -+ case '3': series = 630; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM 6xx series\n"); -+ break; -+ } -+ break; -+ default: -+ printk(KERN_INFO"acerhk: model string indicates unknown TM xxx series\n"); -+ break; -+ } -+ if (series && verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates TM %d series\n", series); -+ } -+ /* newer Travelmate series do not have a space after 'TravelMate' */ -+ else if (strncmp(str, "TravelMate", 10) == 0) { -+ switch (str[10]) { -+ case '2': -+ if (str[11] == '9') { -+ series = 290; -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM2xx series\n"); -+ } -+ break; -+ case '3': -+ if (str[11] == '2' && str[14] == '3') { -+ // TM 3200 uses "TravelMate32003" -+ series = 3200; -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM3xxx series\n"); -+ } -+ break; -+ case '4': -+ switch (str[11]) { -+ case '3': series = 430; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM4xx series\n"); -+ break; -+ } -+ break; -+ case '5': -+ switch (str[11]) { -+ case '3': series = 530; break; -+ case '4': series = 540; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM5xx series\n"); -+ break; -+ } -+ break; -+ case '6': -+ switch (str[11]) { -+ case '5': series = 650; break; -+ case '6': series = 660; break; -+ case '0': -+ if (strncmp(str, "TravelMate60003", 15) == 0) { -+ series = 6000; break; -+ } -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM6xx series\n"); -+ break; -+ } -+ break; -+ case '8': -+ if (strncmp(str, "TravelMate80003", 15) == 0) { -+ series = 8000; -+ } else if (str[11] == '0') { -+ series = 800; -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown TM8xx series\n"); -+ } -+ break; -+ default: -+ printk(KERN_INFO"acerhk: model string indicates unknown TMxxx series\n"); -+ break; -+ } -+ if (series && verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates TM%d series\n", series); -+ } -+ else if (strncmp(str, "Aspire ", 7) == 0) { -+ switch(str[7]) { -+ case '1': /* Aspire 1xxx series */ -+ switch(str[8]) { -+ case '3': /* Aspire 13xx series */ -+ switch (str[9]) { -+ case '0': series = 1300; break; -+ case '1': series = 1310; break; -+ case '5': series = 1350; break; -+ case '6': series = 1360; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 13xx series\n"); -+ break; -+ } -+ break; -+ case '4': /* Aspire 14xx series */ -+ switch (str[9]) { -+ case '0': series = 1400; break; -+ case '5': series = 1450; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 14xx series\n"); -+ break; -+ } -+ break; -+ case '5': series = 1500; break; -+ case '6': /* Aspire 14xx series */ -+ switch (str[9]) { -+ case '0': series = 1600; break; -+ case '8': -+ case '9': series = 1680; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 16xx series\n"); -+ break; -+ } -+ break; -+ case '7': series = 1700; break; -+ case '8': series = 1800; break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 1xxx series\n"); -+ break; -+ } -+ break; -+ case '2': /* Aspire 2xxx series */ -+ if (str[8] == '0') { -+ switch (str[9]) { -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 20xx series\n"); -+ break; -+ case '0': series = 2000; break; -+ case '1': series = 2010; break; -+ case '2': series = 2020; break; -+ } -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 2xxx series\n"); -+ } -+ break; -+ case '3': /* Aspire 3xxx series */ -+ if (str[8] == '0') { -+ switch (str[9]) { -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 30xx series\n"); -+ break; -+ case '2': series = 5020; break; /* Aspire 3020/5020 are identical */ -+ } -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 3xxx series\n"); -+ } -+ break; -+ case '5': /* Aspire 5xxx series */ -+ if (str[8] == '0') { -+ switch (str[9]) { -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 50xx series\n"); -+ break; -+ case '2': series = 5020; break; -+ } -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 5xxx series\n"); -+ } -+ break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire series\n"); -+ break; -+ } -+ if (series && verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates Aspire %d series\n", series); -+ } -+ else if (strncmp(str, "Extensa ", 8) == 0) { -+ /* Extensa series */ -+ switch (str[8]) { -+ case '3': -+ switch (str[9]) { -+ case '0': -+ series = 3000; break; -+ default: break; -+ } -+ break; -+ default: break; -+ } -+ if (series && verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates Extensa %d series\n", series); -+ else if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown Extensa series\n"); -+ } -+ else if (strncmp(str, "Amilo ", 6) == 0) { -+ switch (str[6]) { -+ case 'D': /* complete string is "Amilo D-Series", there seems to be no model number */ -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates FS Amilo D series\n"); -+ /* this is the model number of my Amilo */ -+ series = 7820; -+ break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown FS Amilo XX series\n"); -+ series = 7820; -+ } -+ } -+ else if (strncmp(str, "AMILO ", 6) == 0) { -+ switch (str[6]) { -+ case 'D': /* AMILO D 6800 P4-2000 */ -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates FS AMILO D series\n"); -+ series = 6800; -+ break; -+ case 'M': -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates FS AMILO M(7400) series\n"); -+ series = 7400; -+ break; -+ case 'P': -+ /* it is assumed, that 'AMILO P' appears only on Amilo Pro Series */ -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates FS AMILO Pro (V2000) series\n"); -+ series = 7400; -+ break; -+ default: -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates unknown FS AMILO XX series\n"); -+ series = 6800; -+ } -+ } -+ else if (strncmp(str, "MEDIONPC", 8) == 0) { -+ uint medionmodel; -+ if ((medionmodel = COLUSSI("WIM 2040", 4, reg1, AREA_SIZE)) >= 0) { -+ printk(KERN_INFO"acerhk: found Medion model string:'%s'\n", (char*)reg1+medionmodel); -+ series = 96500; -+ } else { -+ if ((medionmodel = COLUSSI("MD 9", 4, reg1, AREA_SIZE)) >= 0) { -+ printk(KERN_INFO"acerhk: found Medion model string:'%s'\n", (char*)reg1+medionmodel); -+ } -+ series = 95400; -+ } -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates a medion MD %d\n", series); -+ } -+ else if (strncmp(str, "MEDIONNB", 8) == 0) { -+ /* Search for the Product string of the MD9783. */ -+ if (COLUSSI("MD 42200", 8, reg1, AREA_SIZE) >= 0) { -+ if (verbose>1) -+ printk(KERN_INFO"acerhk: model string indicates a Medion MD 42200\n"); -+ series = 42200; -+ } else if (COLUSSI("MD 9783", 7, reg1, AREA_SIZE) >= 0){ -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates a medion MD 9783\n"); -+ series = 9783; -+ } else if (COLUSSI("WIM 2000", 7, reg1, AREA_SIZE) >= 0){ -+ if (verbose>1) -+ printk(KERN_INFO"acerhk: model string indicates a Medion MD 2900\n"); -+ series = 2900; -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates a medion MD40100\n"); -+ series = 40100; -+ } -+ } else if (strncmp(str, "AOpen", 5) == 0) { -+ if (strncmp(str, "AOpen*EzRestore", 15) == 0) { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates a AOpen 1559\n"); -+ series = 1559; -+ } else { -+ /* Unless I know of other models no further differentiation, -+ although there is a second part of the model string */ -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates a AOpen\n"); -+ series = 1555; -+ } -+ } else if (strncmp(str, "CL56", 4) == 0) { -+ /* Unless I know of other models no further differentiation, -+ although there are strings with more numbers ("CL561" on a Compal -+ CL56/Zepto 4200, reported by Stian B. Barmen) -+ It has the same functions as Acer Aspire 2010 -+ */ -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates a Compal CL56 (or similar)\n"); -+ series = 2010; -+ } else if (strncmp(str, "Geneva2", 7) == 0) { -+ /* This might be an Aspire 9110 which is very similar to 4650 */ -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates an Aspire 9110\n"); -+ series = 4650; -+ } else { -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: model string indicates no supported hardware\n"); -+ } -+ return (series); -+} -+ -+static void __init probe_model(void) { -+ int offset; /* offset from beginning of reg1 to Model string */ -+ if (verbose) -+ printk(KERN_INFO"acerhk: start search for model string at %p\n", reg1); -+ /* first we look for Travelmate, if it isn't one we try to identify other -+ laptops, such as Medion or Aspire */ -+ offset = COLUSSI("Travel", 6, reg1, AREA_SIZE); -+ /* Try to detect Aspire laptops */ -+ if (offset < 0) -+ offset = COLUSSI("Aspire", 6, reg1, AREA_SIZE); -+ /* Try to detect Extensa laptops */ -+ if (offset < 0) -+ offset = COLUSSI("Extensa", 7, reg1, AREA_SIZE); -+ /* Try to detect Medion laptops */ -+ if (offset < 0) -+ offset = COLUSSI("MEDION", 6, reg1, AREA_SIZE); -+ /* Try to detect AOpen laptops */ -+ if (offset < 0) -+ offset = COLUSSI("AOpen", 5, reg1, AREA_SIZE); -+ /* Try to detect Fujitsu Siemens Amilo laptops */ -+ if (offset < 0) -+ offset = COLUSSI("Amilo", 5, reg1, AREA_SIZE); -+ if (offset < 0) -+ offset = COLUSSI("AMILO", 5, reg1, AREA_SIZE); -+ /* Try to detect Compal */ -+ if (offset < 0) -+ offset = COLUSSI("CL56", 4, reg1, AREA_SIZE); -+ /* That might be an Aspire 9110 */ -+ if (offset < 0) -+ offset = COLUSSI("Geneva2", 7, reg1, AREA_SIZE); -+ if (offset >= 0) { -+ acerhk_model_addr = reg1 + offset; -+ /* copy the string, but not more than 15 characters */ -+ strncpy(acerhk_model_string, acerhk_model_addr, ACERHK_MODEL_STRLEN-1); -+ if (verbose) -+ printk(KERN_INFO"acerhk: found model string '%s' at %p\n", -+ acerhk_model_string, acerhk_model_addr); -+ if (bios_routine && verbose > 2) -+ printk(KERN_INFO"acerhk: offset from model string to function address: 0x%lx\n", -+ bios_routine - (unsigned long)acerhk_model_addr); -+ acerhk_series = determine_laptop_series(acerhk_model_string); -+ } else { -+ printk(KERN_WARNING"acerhk: Could not find model string, will assume type 200 series\n"); -+ acerhk_series = 200; -+ } -+} -+ -+/* }}} */ -+ -+/* {{{ key polling and translation */ -+ -+static void print_mapping(void) -+{ -+ printk(KERN_INFO"acerhk: key mapping:\n"); -+ printk("acerhk: help 0x%x\n", acerhk_name2event[k_help]); -+ printk("acerhk: setup 0x%x\n", acerhk_name2event[k_setup]); -+ printk("acerhk: p1 0x%x\n", acerhk_name2event[k_p1]); -+ printk("acerhk: p2 0x%x\n", acerhk_name2event[k_p2]); -+ printk("acerhk: p3 0x%x\n", acerhk_name2event[k_p3]); -+ printk("acerhk: www 0x%x\n", acerhk_name2event[k_www]); -+ printk("acerhk: mail 0x%x\n", acerhk_name2event[k_mail]); -+ printk("acerhk: wireless 0x%x\n", acerhk_name2event[k_wireless]); -+ printk("acerhk: power 0x%x\n", acerhk_name2event[k_power]); -+ printk("acerhk: mute 0x%x\n", acerhk_name2event[k_mute]); -+ printk("acerhk: volup 0x%x\n", acerhk_name2event[k_volup]); -+ printk("acerhk: voldn 0x%x\n", acerhk_name2event[k_voldn]); -+ printk("acerhk: res 0x%x\n", acerhk_name2event[k_res]); -+ printk("acerhk: close 0x%x\n", acerhk_name2event[k_close]); -+ printk("acerhk: open 0x%x\n", acerhk_name2event[k_open]); -+ printk("acerhk: wireless2 0x%x\n", acerhk_name2event[k_wireless2]); -+ printk("acerhk: play 0x%x\n", acerhk_name2event[k_play]); -+ printk("acerhk: stop 0x%x\n", acerhk_name2event[k_stop]); -+ printk("acerhk: prev 0x%x\n", acerhk_name2event[k_prev]); -+ printk("acerhk: next 0x%x\n", acerhk_name2event[k_next]); -+ printk("acerhk: display 0x%x\n", acerhk_name2event[k_display]); -+} -+ -+static void set_keymap_name(t_key_names name, unsigned int key) -+{ -+ acerhk_name2event[name] = key; -+} -+ -+static void init_keymap_input(void) -+{ -+ /* these values for input keys are chosen to match the key names on the -+ actual Acer laptop */ -+ set_keymap_name(k_none, KEY_RESERVED); -+ set_keymap_name(k_help, KEY_HELP); -+ set_keymap_name(k_setup, KEY_CONFIG); -+ set_keymap_name(k_p1, KEY_PROG1); -+ set_keymap_name(k_p2, KEY_PROG2); -+ set_keymap_name(k_p3, KEY_PROG3); -+ set_keymap_name(k_www, KEY_WWW); -+ set_keymap_name(k_mail, KEY_MAIL); -+ set_keymap_name(k_wireless, KEY_XFER); -+ set_keymap_name(k_power, KEY_POWER); -+ set_keymap_name(k_mute, KEY_MUTE); -+ set_keymap_name(k_volup, KEY_VOLUMEUP); -+ set_keymap_name(k_voldn, KEY_VOLUMEDOWN); -+ set_keymap_name(k_res, KEY_CONFIG); -+ set_keymap_name(k_close, KEY_CLOSE); -+ set_keymap_name(k_open, KEY_OPEN); -+ /* I am not really happy with the selections for wireless and wireless2, -+ but coffee looks good. Michal Veselenyi proposed this value */ -+ set_keymap_name(k_wireless2, KEY_COFFEE); -+ set_keymap_name(k_play, KEY_PLAYPAUSE); -+ set_keymap_name(k_stop, KEY_STOPCD); -+ set_keymap_name(k_prev, KEY_PREVIOUSSONG); -+ set_keymap_name(k_next, KEY_NEXTSONG); -+ set_keymap_name(k_display, KEY_MEDIA); /* also not happy with this */ -+ if (verbose > 1) -+ print_mapping(); -+} -+ -+static int filter_idle_value(int keycode) -+{ -+ int validkey = 0; -+ if (keycode != 0x0 && -+ keycode != 0x9610 && -+ keycode != 0xc100 && /* Francois Valenduc, Aspire 1601 LC */ -+ keycode != 0x8610 && -+ keycode != 0x861 && -+ keycode != 0x8650 && -+ keycode != 0x865) -+ validkey = keycode; -+ if (verbose > 4 && !validkey) -+ printk(KERN_INFO"acerhk: throw away idle value 0x%x\n", keycode); -+ return validkey; -+} -+ -+static void send_key_event(t_key_names key) -+{ -+ unsigned int input_key; -+ if (key != k_none) { -+ /* convert key name to kernel keycode */ -+ input_key = acerhk_name2event[key]; -+ if (verbose > 2) -+ printk(KERN_INFO"acerhk: translated acer key name 0x%x to input key 0x%x\n", -+ key, input_key); -+ /* send press and release together, as there is no such event from acer as 'release' */ -+ input_report_key(acerhk_input_dev_ptr, input_key, 1); -+ input_report_key(acerhk_input_dev_ptr, input_key, 0); -+ } -+} -+ -+static t_key_names transl8_key_code(int keycode) -+{ -+ t_key_names keyname = k_none; -+ /* first filter out idle values */ -+ if ( (keycode = filter_idle_value(keycode)) ) { -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: received key code 0x%x\n", keycode); -+ /* translate keycode to key name */ -+ if (keycode >= 0 && keycode <= 255) -+ keyname = acerhk_key2name[keycode]; -+ else { -+ if (verbose > 3) -+ printk(KERN_INFO"acerhk: keycode 0x%x too big, will use only 8 bits\n", keycode); -+ /* use only lower 8 bits of value to distinguish keys */ -+ keyname = acerhk_key2name[keycode&0xff]; -+ } -+ /* produce some log information for higher verbosity levels */ -+ if (keyname != k_none && verbose > 2) -+ printk(KERN_INFO"acerhk: translated acer key code 0x%x to key name 0x%x\n", -+ keycode, keyname); -+ else if (keyname == k_none && verbose > 3) -+ printk(KERN_INFO"acerhk: translated acer key code 0x%x to no key\n", -+ keycode); -+ if (autowlan) { -+ /* if automatic switching of wlan hardware is enabled, do it here -+ on wireless key press */ -+ if (keyname == k_wireless2) { -+ if (acerhk_bluetooth_state) -+ wbutton_fct_1(0); -+ else -+ wbutton_fct_1(1); -+ } -+ if (keyname == k_wireless) { -+ if (acerhk_wlan_state) -+ wbutton_fct_2(0); -+ else -+ wbutton_fct_2(1); -+ } -+ } -+ } -+ return keyname; -+} -+ -+/* polling timer handler */ -+static void acerhk_poll_event(unsigned long save_size) -+{ -+#ifndef DUMMYHW -+ unsigned int max = MAX_POLLING_LOOPS; -+ /* make sure not to loop more then 32 times */ -+ if (!max || max > 32) -+ max = 32; -+ if (acerhk_type != TM_dritek) { -+ while (get_nr_events() && max--) { -+ send_key_event(transl8_key_code(get_fnkey_event())); -+ } -+ } else { -+ send_key_event(transl8_key_code(get_fnkey_event())); -+ } -+#endif -+ acerhk_timer_poll.expires = jiffies + acerhk_polling_delay; -+ add_timer(&acerhk_timer_poll); -+} -+ -+/* blinking timer handler; added by Antonio Cuni */ -+static void acerhk_blink_event(unsigned long not_used) -+{ -+ if (acerhk_blueled_blinking != -1) { -+ acerhk_blueled_blinking = !acerhk_blueled_blinking; -+#ifndef DUMMYHW -+ wbutton_fct_1(acerhk_blueled_blinking); -+#endif -+ acerhk_timer_blinking.expires = jiffies + acerhk_blueled_blinking_delay; -+ add_timer(&acerhk_timer_blinking); -+ } -+ else -+ printk(KERN_WARNING "acerhk: blinking event called, but blinking not active\n"); -+} -+ -+static void init_input(void) -+{ -+ int i; -+ -+#ifndef KERNEL26 -+ /* request keyboard input module */ -+ request_module("keybdev"); -+ if (verbose > 3) -+ printk(KERN_INFO"requested keyboard input driver\n"); -+#endif -+ -+#ifndef STATIC_INPUT_DEV -+ /* allocate acerhk input device */ -+ acerhk_input_dev_ptr=input_allocate_device(); -+ /* enter some name */ -+ acerhk_input_dev_ptr->name = "Acer hotkey driver"; -+#else -+ acerhk_input_dev_ptr=&acerhk_input_dev; -+#endif -+ -+ /* some laptops have a mail led, should I announce it here? */ -+ acerhk_input_dev_ptr->evbit[0] = BIT(EV_KEY); -+ /* announce keys to input system -+ * the generated keys can be changed on runtime, -+ * but to publish those changes the device needs to -+ * get reconnected (I dont't know any other way) -+ * Therefore I enable all possible keys */ -+ for (i = KEY_RESERVED; i < BTN_MISC; i++) -+ set_bit(i, acerhk_input_dev_ptr->keybit); -+ /* set mapping keyname -> input event */ -+ init_keymap_input(); -+ if (verbose) -+ printk(KERN_INFO"acerhk: registered input device\n"); -+ input_register_device(acerhk_input_dev_ptr); -+ init_timer(&acerhk_timer_poll); -+ acerhk_polling_state = 0; -+} -+ -+static void stop_polling(void) -+{ -+ if (acerhk_polling_state == 1) { -+ del_timer(&acerhk_timer_poll); -+ if (verbose) -+ printk(KERN_INFO"acerhk: key polling stopped\n"); -+ acerhk_polling_state = 0; -+ } else -+ if (verbose) -+ printk(KERN_INFO"acerhk: key polling not active\n"); -+} -+ -+static void start_polling(void) -+{ -+ if (acerhk_polling_state != 1) { -+ acerhk_timer_poll.function = acerhk_poll_event; -+ acerhk_timer_poll.expires = jiffies + acerhk_polling_delay; -+ acerhk_timer_poll.data = get_nr_events(); -+ add_timer(&acerhk_timer_poll); -+ acerhk_polling_state = 1; -+ if (acerhk_type == TM_dritek) { -+ printk(KERN_INFO"acerhk: Your hardware does not need polling enabled for hotkeys to work, " -+ "you can safely disable polling by using the module parameter poll=0 (unless you " -+ "want to play around with the driver and see if there are buttons which need polling).\n"); -+ } -+ if (verbose) -+ printk(KERN_INFO"acerhk: starting key polling, every %d ms\n", acerhk_polling_delay); -+ } else -+ if (verbose) -+ printk(KERN_INFO"acerhk: key polling already active\n"); -+} -+ -+/* addedd by Antonio Cuni */ -+static void start_blinking(void) -+{ -+ if (acerhk_blueled_blinking == -1) { -+ // blinking was disabled... enable it! -+ acerhk_timer_blinking.function = acerhk_blink_event; -+ acerhk_timer_blinking.expires = jiffies + acerhk_blueled_blinking_delay; -+ acerhk_timer_blinking.data = 0; // not used -+ add_timer(&acerhk_timer_blinking); -+ acerhk_blueled_blinking = 0; -+ if (verbose) -+ printk(KERN_INFO "acerhk: starting blueled blinking\n"); -+ } else -+ if (verbose) -+ printk(KERN_INFO "acerhk: blueled already blinking\n"); -+} -+ -+/* Added by Antonio Cuni */ -+static void stop_blinking(void) -+{ -+ if (acerhk_blueled_blinking != -1) { -+ del_timer(&acerhk_timer_blinking); -+ if (verbose) -+ printk(KERN_INFO "acerhk: blueled blinking stopped\n"); -+ acerhk_blueled_blinking = -1; -+ } -+} -+ -+static void release_input(void) -+{ -+ stop_polling(); -+ input_unregister_device(acerhk_input_dev_ptr); -+} -+ -+/* }}} */ -+ -+/* {{{ procfs functions */ -+ -+#ifndef CONFIG_PROC_FS -+ -+static int acerhk_proc_init(void) -+{ -+ return 1; -+} -+#else -+ -+/* This macro frees the machine specific function from bounds checking and -+ * things like that... */ -+#define PRINT_PROC(fmt,args...) \ -+ do { \ -+ *len += sprintf( buffer+*len, fmt, ##args ); \ -+ if (*begin + *len > offset + size) \ -+ return( 0 ); \ -+ if (*begin + *len < offset) { \ -+ *begin += *len; \ -+ *len = 0; \ -+ } \ -+ } while(0) -+ -+static int pc_proc_infos( char *buffer, int *len, -+ off_t *begin, off_t offset, int size ) -+{ -+ PRINT_PROC( "Acer hotkeys version %s\n", ACERHK_VERSION); -+ PRINT_PROC( "Model(Type)\t: %s(", acerhk_model_string); -+ switch(acerhk_type) { -+ default: -+ PRINT_PROC( "unknown)\n"); -+ break; -+ case TM_old: -+ PRINT_PROC( "old)\n"); -+ break; -+ case TM_new: -+ PRINT_PROC( "new)\n"); -+ break; -+ case TM_dritek: -+ PRINT_PROC( "Dritek)\n"); -+ break; -+ } -+ if (bios_routine != 0) { -+ PRINT_PROC( "request handler\t: 0x%x\n", bios_routine); -+ if (cmos_index) { -+ PRINT_PROC( "CMOS index\t: 0x%x\n", cmos_index); -+ PRINT_PROC( "events pending\t: %u\n", get_nr_events()); -+ } else { -+ PRINT_PROC( "CMOS index\t: not available\n"); -+ } -+ if (acerhk_polling_state == 1) -+ PRINT_PROC( "kernel polling\t: active\n"); -+ else -+ PRINT_PROC( "kernel polling\t: inactive\n"); -+ PRINT_PROC( "autoswitch wlan\t: "); -+ if (autowlan == 1) -+ PRINT_PROC( "enabled\n"); -+ else -+ PRINT_PROC( "disabled\n"); -+ } else { -+ PRINT_PROC( "request handler\t: not found\n"); -+ PRINT_PROC( "kernel polling\t: not possible\n"); -+ } -+ /* model specific infos */ -+ if (acerhk_type == TM_dritek) { -+ PRINT_PROC( "use of Dritek EC: "); -+ if (usedritek) -+ PRINT_PROC( "enabled\n"); -+ else -+ PRINT_PROC( "disabled\n"); -+ } -+ if (acerhk_type == TM_old) -+ PRINT_PROC( "preg400\t\t: 0x%p\n", preg400); -+ return (1); -+} -+ -+static int acerhk_proc_info( char *buffer, char **start, off_t offset, -+ int size, int *eof, void *data ) -+{ -+ int len = 0; -+ off_t begin = 0; -+ -+ *eof = pc_proc_infos( buffer, &len, &begin, offset, size ); -+ -+ if (offset >= begin + len) -+ return( 0 ); -+ *start = buffer + (offset - begin); -+ return( size < begin + len - offset ? size : begin + len - offset ); -+ -+} -+ -+static int acerhk_proc_key( char *buffer, char **start, off_t offset, -+ int size, int *eof, void *data ) -+{ -+ if (size >= 5 && offset == 0) { -+ if (acerhk_type == TM_dritek || acerhk_polling_state == 1) { -+ snprintf(buffer+offset, size, "n/a\n"); -+ } else { -+ snprintf(buffer+offset, size, "0x%02x\n", filter_idle_value(get_fnkey_event())); -+ } -+ *eof = 1; -+ return 5; -+ } -+ *eof = 1; -+ return 0; -+} -+ -+static int acerhk_proc_led(struct file* file, const char* buffer, -+ unsigned long count, void* data) -+{ -+ char str[4]; -+ int len; -+ if (count > 4) -+ len = 4; -+ else -+ len = count; -+ if (copy_from_user(str, buffer, len)) -+ return -EFAULT; -+ str[3] = '\0'; -+ if ( ( (len >= 2) && (!strncmp(str, "on", 2) || !strncmp(str, "an", 2)) ) -+ || str[0] == '1') -+ set_mail_led(1); -+ else -+ set_mail_led(0); -+ return len; -+} -+ -+static int acerhk_proc_wirelessled(struct file* file, const char* buffer, -+ unsigned long count, void* data) -+{ -+ char str[4]; -+ int len; -+ if (count > 4) -+ len = 4; -+ else -+ len = count; -+ if (copy_from_user(str, buffer, len)) -+ return -EFAULT; -+ str[3] = '\0'; -+ if ( ( (len >= 2) && (!strncmp(str, "on", 2) || !strncmp(str, "an", 2)) ) -+ || str[0] == '1') { -+ if (acerhk_model_features & TM_F_WLAN_EC1) -+ enable_wlan_ec_1(); -+ else if (acerhk_model_features & TM_F_WLAN_EC2) -+ enable_wlan_ec_2(); -+ else -+ wbutton_fct_2(1); -+ } -+ else { -+ if (acerhk_model_features & TM_F_WLAN_EC1) -+ disable_wlan_ec_1(); -+ else if (acerhk_model_features & TM_F_WLAN_EC2) -+ disable_wlan_ec_2(); -+ else -+ wbutton_fct_2(0); -+ } -+ return len; -+} -+ -+ -+/* Modified by Antonio Cuni: added support for blinking -+ possible values: -+ - off, 0: led always off -+ - on, an, 1: led alway on -+ - n (a number): led blinking; n is the delay between -+ two changes of state, in jiffies; n must -+ be > 50, to prevent the user from overloading -+ the kernel. -+ -+ */ -+static int acerhk_proc_blueled(struct file* file, const char* buffer, -+ unsigned long count, void* data) -+{ -+ const int MAXLEN=11; -+ char str[MAXLEN]; -+ int len; -+ int isNumber; -+ -+ if (count > MAXLEN) -+ len = MAXLEN; -+ else -+ len = count; -+ if (copy_from_user(str, buffer, len)) -+ return -EFAULT; -+ str[MAXLEN - 1] = '\0'; -+ -+ /* try to parse a number */ -+ isNumber = sscanf(str, "%u", &acerhk_blueled_blinking_delay); -+ /* if the delay is 0, turn off the led */ -+ if (isNumber && acerhk_blueled_blinking_delay != 0 && acerhk_blueled_blinking_delay != 1) { -+ if (acerhk_blueled_blinking_delay < 50) -+ printk(KERN_INFO"acerhk: blinking request rejected. The delay must be > 50.\n"); -+ else { -+ if (verbose) -+ printk(KERN_INFO"acerhk: blinking delay set to %u.\n", acerhk_blueled_blinking_delay); -+ start_blinking(); -+ } -+ } else if (acerhk_blueled_blinking_delay == 1 || !strncmp(str, "on", 2) || !strncmp(str, "an", 2)) { -+ stop_blinking(); -+ if (acerhk_model_features & TM_F_BLUE_EC1) -+ enable_bluetooth_ec_1(); -+ else if (acerhk_model_features & TM_F_BLUE_EC2) -+ enable_bluetooth_ec_2(); -+ else -+ wbutton_fct_1(1); -+ } else { -+ /* it's 0 or everything else */ -+ stop_blinking(); -+ if (acerhk_model_features & TM_F_BLUE_EC1) -+ disable_bluetooth_ec_1(); -+ else if (acerhk_model_features & TM_F_BLUE_EC2) -+ disable_bluetooth_ec_2(); -+ else -+ wbutton_fct_1(0); -+ } -+ return len; -+} -+ -+#ifdef ACERDEBUG -+static void do_debug(const char* buffer, unsigned long len) -+{ -+ unsigned int h, i; -+ switch (buffer[0]) { -+ case 'b': -+ /* test WLAN on TM 4001 */ -+ switch (buffer[1]) { -+ case '0': -+ disable_wlan_ec_1(); -+ break; -+ case '1': -+ default: -+ enable_wlan_ec_1(); -+ } -+ break; -+ case 'B': -+ /* test BLUETOOTH on TM 4001 */ -+ switch (buffer[1]) { -+ case '0': -+ disable_bluetooth_ec_1(); -+ break; -+ case '1': -+ default: -+ enable_bluetooth_ec_1(); -+ } -+ break; -+ case 'D': -+ /* test "DMM Function Enabled" entry of TM 4150/4650 */ -+ enable_dmm_function(); -+ break; -+ case 'i': -+ case '1': -+#ifndef KERNEL26 -+ MOD_INC_USE_COUNT; -+#endif -+ break; -+ case 'e': -+ switch (buffer[1]) { -+ case '1': -+ start_polling(); -+ break; -+ default: -+ stop_polling(); -+ } -+ break; -+ case 'k': -+ for (i = 0; i <= 255;i++) { -+ input_report_key(acerhk_input_dev_ptr, i, 1); -+ input_report_key(acerhk_input_dev_ptr, i, 0); -+ } -+ break; -+ case 'm': -+ /* set mapping key names -> input events */ -+ sscanf(&buffer[2],"%x", &i); -+ h = buffer[1] - '0' + 1; -+ printk("acerhk: key name %x maps to %x\n", h, i); -+ acerhk_name2event[h] = i; -+ break; -+ case 'M': -+ /* test mute LED on dritek hardware */ -+ switch (buffer[1]) { -+ case '0': -+ disable_mute_led_ec(); -+ break; -+ case '1': -+ default: -+ enable_mute_led_ec(); -+ } -+ break; -+ case 'p': -+ printk("acerhk: pbutton = 0x%x\n", pbutton_fct()); -+ break; -+ case 's': -+ /* send key event to test the key translation in input system */ -+ sscanf(&buffer[1],"%x", &h); -+ printk("acerhk: sending key event 0x%x\n", h); -+ input_report_key(acerhk_input_dev_ptr, h, 1); -+ input_report_key(acerhk_input_dev_ptr, h, 0); -+ break; -+ case 'S': -+ /* simulate key codes to test the key translation in acerhk */ -+ sscanf(&buffer[1],"%x", &h); -+ send_key_event(transl8_key_code(h)); -+ break; -+ case 't': -+ printk("acerhk: thermal event = 0x%x\n", get_thermal_event()); -+ break; -+ case 'w': -+ /* test the wbutton functions, someone really needs to have another look -+ at the windows driver */ -+ switch (buffer[1]) { -+ case '2': -+ printk("acerhk: wbutton_2(%d) = 0x%x\n", buffer[2]-'0', wbutton_fct_2(buffer[2]-'0')); -+ break; -+ case '1': -+ default: -+ printk("acerhk: wbutton_1(%d) = 0x%x\n", buffer[2]-'0', wbutton_fct_1(buffer[2]-'0')); -+ } -+ break; -+ case 'W': -+ /* test wireless HW/LED on some models using dritek hardware */ -+ switch (buffer[1]) { -+ case '0': -+ disable_wireless_ec(); -+ break; -+ case '1': -+ default: -+ enable_wireless_ec(); -+ } -+ break; -+ case 'v': -+ verbose = buffer[1]-'0'; -+ printk("acerhk: verbosity level changed to %d\n", verbose); -+ break; -+ case 'd': -+ case '0': -+ default: -+#ifndef KERNEL26 -+ MOD_DEC_USE_COUNT; -+#endif -+ break; -+ } -+} -+ -+static int acerhk_proc_debug(struct file* file, const char* buffer, -+ unsigned long count, void* data) -+{ -+ char str[5]; -+ int len; -+ if (count > 5) -+ len = 5; -+ else -+ len = count; -+ if (copy_from_user(str, buffer, len)) -+ return -EFAULT; -+ str[4] = '\0'; -+ do_debug(str, len); -+ return len; -+} -+#endif -+ -+static int acerhk_proc_init(void) -+{ -+ int retval; -+ struct proc_dir_entry *entry; -+ /* create own directory */ -+ proc_acer_dir = proc_mkdir("driver/acerhk", NULL); -+ if (proc_acer_dir == NULL) { -+ retval = 0; -+ printk(KERN_INFO"acerhk: could not create /proc/driver/acerhk\n"); -+ } -+ else { -+ proc_acer_dir->owner = THIS_MODULE; -+ /* now create several files, first general info ... */ -+ entry = create_proc_read_entry("info", -+ 0444, proc_acer_dir, acerhk_proc_info, NULL); -+ if (entry == NULL) { -+ printk(KERN_INFO"acerhk: cannot create info file\n"); -+ remove_proc_entry("driver/acerhk", NULL); -+ retval = 0; -+ } else { -+ entry->owner = THIS_MODULE; -+ /* ... last pressed key ... */ -+ entry = create_proc_read_entry("key", -+ 0444, proc_acer_dir, acerhk_proc_key, NULL); -+ if (entry == NULL) { -+ printk(KERN_INFO"acerhk: cannot create key file\n"); -+ remove_proc_entry("info", proc_acer_dir); -+ remove_proc_entry("driver/acerhk", NULL); -+ retval = 0; -+ } else { -+ entry->owner = THIS_MODULE; -+ /* ... and led control file */ -+ entry = create_proc_entry("led", 0222, proc_acer_dir); -+ if (entry == NULL) { -+ printk(KERN_INFO"acerhk: cannot create LED file\n"); -+ remove_proc_entry("info", proc_acer_dir); -+ remove_proc_entry("key", proc_acer_dir); -+ remove_proc_entry("driver/acerhk", NULL); -+ retval = 0; -+ } -+ else { -+ entry->write_proc = acerhk_proc_led; -+ entry->owner = THIS_MODULE; -+ /* ... and wireless led controll file */ -+ entry = create_proc_entry("wirelessled", 0222, proc_acer_dir); -+ if (entry == NULL) { -+ printk(KERN_INFO"acerhk: cannot create wirelessled file\n"); -+ remove_proc_entry("info", proc_acer_dir); -+ remove_proc_entry("key", proc_acer_dir); -+ remove_proc_entry("led", proc_acer_dir); -+ remove_proc_entry("driver/acerhk", NULL); -+ retval = 0; -+ } -+ else { -+ entry->write_proc = acerhk_proc_wirelessled; -+ entry->owner = THIS_MODULE; -+ /* ... and bluetooth led controll file */ -+ entry = create_proc_entry("blueled", 0222, proc_acer_dir); -+ if (entry == NULL) { -+ printk(KERN_INFO"acerhk: cannot create blueled file\n"); -+ remove_proc_entry("info", proc_acer_dir); -+ remove_proc_entry("key", proc_acer_dir); -+ remove_proc_entry("led", proc_acer_dir); -+ remove_proc_entry("wirelessled", proc_acer_dir); -+ remove_proc_entry("driver/acerhk", NULL); -+ retval = 0; -+ } else { -+ entry->write_proc = acerhk_proc_blueled; -+ entry->owner = THIS_MODULE; -+ retval = 1; -+#ifdef ACERDEBUG -+ /* add extra file for debugging purposes */ -+ entry = create_proc_entry("debug", 0222, proc_acer_dir); -+ if (entry == NULL) { -+ printk(KERN_INFO"acerhk: cannot create debug file\n"); -+ remove_proc_entry("info", proc_acer_dir); -+ remove_proc_entry("key", proc_acer_dir); -+ remove_proc_entry("led", proc_acer_dir); -+ remove_proc_entry("wirelessled", proc_acer_dir); -+ remove_proc_entry("blueled", proc_acer_dir); -+ remove_proc_entry("driver/acerhk", NULL); -+ retval = 0; -+ } -+ else { -+ entry->write_proc = acerhk_proc_debug; -+ entry->owner = THIS_MODULE; -+ retval = 1; -+ } -+#endif -+ } -+ } -+ } -+ } -+ } -+ } -+ return retval; -+} -+ -+static void acerhk_proc_cleanup(void) -+{ -+ if (proc_acer_dir) { -+ remove_proc_entry("info", proc_acer_dir); -+ /* On dritek type hardware key file is already removed */ -+ if (acerhk_type != TM_dritek) -+ remove_proc_entry("key", proc_acer_dir); -+ remove_proc_entry("led", proc_acer_dir); -+ remove_proc_entry("wirelessled", proc_acer_dir); -+ remove_proc_entry("blueled", proc_acer_dir); -+#ifdef ACERDEBUG -+ remove_proc_entry("debug", proc_acer_dir); -+#endif -+ remove_proc_entry("driver/acerhk", NULL); -+ proc_acer_dir = NULL; -+ } -+} -+ -+#endif /* CONFIG_PROC_FS */ -+ -+/* }}} */ -+ -+/* {{{ file operations */ -+ -+static int acerhk_ioctl( struct inode *inode, struct file *file, -+ unsigned int cmd, unsigned long arg ) -+{ -+ int retval; -+ switch( cmd ) { -+ case ACERHK_GET_KEYCOUNT: -+ { -+ char nr; -+ nr = get_nr_events(); -+ put_user(nr, (char*)arg); -+ retval = 0; -+ break; -+ } -+ case ACERHK_GET_KEYID: -+ { -+ char id; -+ id = get_fnkey_event(); -+ put_user(id, (char*)arg); -+ retval = 0; -+ break; -+ } -+ case ACERHK_CONNECT: -+ launch_connect(1); -+ retval = 0; -+ break; -+ case ACERHK_START_POLLING: -+ start_polling(); -+ retval = 0; -+ break; -+ case ACERHK_STOP_POLLING: -+ stop_polling(); -+ retval = 0; -+ break; -+ case ACERHK_DISCONNECT: -+ launch_connect(0); -+ retval = 0; -+ break; -+ case ACERHK_GET_THERMAL_EVENT: -+ { -+ short event; -+ event = get_thermal_event(); -+ put_user(event, (short*)arg); -+ retval = 0; -+ break; -+ } -+ case ACERHK_MAIL_LED_OFF: -+ set_mail_led(0); -+ retval = 0; -+ break; -+ case ACERHK_MAIL_LED_ON: -+ set_mail_led(1); -+ retval = 0; -+ break; -+ case ACERHK_GET_KEY_MAP: -+ if (copy_to_user((t_map_name2event*)arg, &acerhk_name2event, sizeof(acerhk_name2event))) -+ retval = -EFAULT; -+ else -+ retval = 0; -+ break; -+ case ACERHK_SET_KEY_MAP: -+ if (copy_from_user(&acerhk_name2event, (t_map_name2event*)arg, sizeof(acerhk_name2event))) -+ retval = -EFAULT; -+ else { -+ if (verbose) { -+ printk(KERN_INFO"acerhk: changed key mapping\n"); -+ print_mapping(); -+ } -+ retval = 0; -+ } -+ break; -+ default: -+ retval = -EINVAL; -+ } -+ return retval; -+} -+ -+#ifdef ACERDEBUG -+static ssize_t acerhk_write (struct file* file, const char* buffer, size_t length, loff_t* offset) -+{ -+ if (length) -+ do_debug(buffer, length); -+ return length; -+} -+#endif -+ -+static int acerhk_open( struct inode *inode, struct file *file ) -+{ -+ return 0; -+} -+ -+static int acerhk_release( struct inode *inode, struct file *file ) -+{ -+ return 0; -+} -+ -+static struct file_operations acerhk_fops = { -+ owner: THIS_MODULE, -+ ioctl: acerhk_ioctl, -+ open: acerhk_open, -+#ifdef ACERDEBUG -+ write: acerhk_write, -+#endif -+ release: acerhk_release, -+}; -+ -+static struct miscdevice acerhk_dev = { -+ MISC_DYNAMIC_MINOR, -+ "acerhk", -+ &acerhk_fops -+}; -+ -+/* }}} */ -+ -+static void __init model_init(void) -+{ -+ /* set callroutine, features and keymap for model */ -+ setup_model_features(acerhk_series); -+ /* override initial state of wireless hardware if specified by module options */ -+ if (wlan_state >= 0) acerhk_wlan_state = wlan_state; -+ if (bluetooth_state >= 0) acerhk_bluetooth_state = bluetooth_state; -+ /* Launch connect only if available */ -+ if (acerhk_model_features & TM_F_CONNECT) { -+ if (verbose) -+ printk(KERN_INFO"acerhk: Model type %d, calling launch_connect(1)\n", -+ acerhk_type); -+ launch_connect(1); -+ } -+ if ( acerhk_type != TM_dritek ) { -+ get_cmos_index(); -+ } -+ if ( acerhk_type == TM_dritek ) { -+ enable_dritek_keyboard(); -+ } -+ /* added by Antonio Cuni */ -+ init_timer(&acerhk_timer_blinking); -+} -+ -+ -+static void __exit acerhk_cleanup_module (void); -+static int __init acerhk_init(void) -+{ -+ int ret; -+ -+ ret = misc_register( &acerhk_dev ); -+ if (ret) { -+ printk(KERN_ERR "acerhk: can't misc_register on minor=%d\n", ACERHK_MINOR); -+ ret = -EAGAIN; -+ } -+ else if (!acerhk_proc_init()) { -+ printk(KERN_ERR "acerhk: can't create procfs entries\n"); -+ ret = -ENOMEM; -+ misc_deregister( &acerhk_dev ); -+ } -+ else { -+ reg1 = ioremap(0xf0000, 0xffff); -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: area from 0xf000 to 0xffff mapped to %p\n", reg1); -+ reg2 = ioremap(0xe0000, 0xffff); -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: area from 0xe000 to 0xffff mapped to %p\n", reg2); -+ /* the area 0x400 is used as data area by earlier (520) series */ -+ preg400 = ioremap(0x400, 0xfff); -+ if (verbose > 1) -+ printk(KERN_INFO"acerhk: area from 0x400 to 0x13ff mapped to %p\n", preg400); -+ /* attach to input system */ -+ init_input(); -+ memset(acerhk_model_string, 0x00, ACERHK_MODEL_STRLEN); -+#ifdef DUMMYHW -+ acerhk_model_addr = (void*)0x12345678; -+ /* copy the string, but not more than 15 characters */ -+ strncpy(acerhk_model_string, "TravelmateDummy", ACERHK_MODEL_STRLEN-1); -+ /* set callroutine for model */ -+ if (force_series) -+ acerhk_series = force_series; -+ else -+ acerhk_series = 2000; -+ setup_model_features(acerhk_series); -+ printk(KERN_INFO "Acer Travelmate hotkey driver v" ACERHK_VERSION " dummy\n"); -+ if ( acerhk_type == TM_dritek ) -+ enable_dritek_keyboard(); -+ if (poll) -+ start_polling(); -+ init_timer(&acerhk_timer_blinking); -+#else -+ bios_routine = find_hk_area(); -+ if (!force_series) -+ probe_model(); -+ else { -+ if (verbose) -+ printk(KERN_INFO"acerhk: forced laptop series to %d\n", force_series); -+ acerhk_series = force_series; -+ } -+ /* do model specific initialization */ -+ model_init(); -+ /* Without a bios routine we cannot do anything except on dritek -+ type HW, unload on other types */ -+ if (bios_routine || (acerhk_type == TM_dritek)) { -+ ret = 0; -+ if (verbose && bios_routine) -+ printk(KERN_INFO"acerhk: bios routine found at 0x%x\n", bios_routine); -+ printk(KERN_INFO "Acer Travelmate hotkey driver v" ACERHK_VERSION "\n"); -+ /* If automatic switching of wlan is wanted but polling is disabled, -+ automatically enable it */ -+ if (!poll && autowlan) { -+ printk(KERN_INFO "Automatic switching of wireless hardware needs polling, enabling it\n"); -+ poll = 1; -+ } -+ /* start automatic polling of key presses if wanted and bios routine found */ -+ if (poll && bios_routine) -+ start_polling(); -+ } else { -+ printk(KERN_ERR "acerhk: can't find bios routine, cannot do anything for you, sorry!\n"); -+ ret = -ENOMEM; -+ acerhk_cleanup_module(); -+ } -+#endif -+ } -+ return ret; -+} -+ -+static void __exit acerhk_cleanup_module (void) -+{ -+ acerhk_proc_cleanup(); -+ stop_blinking(); -+ if (reg1) -+ iounmap(reg1); -+ if (reg2) -+ iounmap(reg2); -+ if (preg400) -+ iounmap(preg400); -+ release_input(); -+ misc_deregister( &acerhk_dev ); -+ if ( acerhk_type == TM_dritek ) { -+ disable_dritek_keyboard(); -+ } -+ if (verbose > 2) -+ printk(KERN_INFO "acerhk: unloaded\n"); -+} -+ -+module_init(acerhk_init); -+module_exit(acerhk_cleanup_module); -+ -+MODULE_AUTHOR("Olaf Tauber"); -+MODULE_DESCRIPTION("AcerHotkeys extra buttons keyboard driver"); -+MODULE_LICENSE("GPL"); -+ -+#ifndef KERNEL26 -+EXPORT_NO_SYMBOLS; -+#endif -+ -+#else -+#error This driver is only available for X86 architecture -+#endif -+/* -+ * Local variables: -+ * c-indent-level: 4 -+ * tab-width: 4 -+ * End: -+ */ -+ -diff -Nurp linux-2.6.37/3rdparty/acerhk/acerhk.h linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.h ---- linux-2.6.37/3rdparty/acerhk/acerhk.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/acerhk.h 2005-06-17 00:10:12.000000000 +0300 -@@ -0,0 +1,91 @@ -+#ifndef __ACERHK_H__ -+#define __ACERHK_H__ -+ -+#include -+ -+#define ACERHK_MINOR MISC_DYNAMIC_MINOR -+ -+#define ACERHK_GET_KEYCOUNT _IOR('p', 0x01, char) /* Get number of cached key presses */ -+#define ACERHK_GET_KEYID _IOR('p', 0x02, char) /* Get first key in queue */ -+#define ACERHK_CONNECT _IO('p', 0x03) /* ? */ -+#define ACERHK_DISCONNECT _IO('p', 0x04) /* ? */ -+#define ACERHK_GET_THERMAL_EVENT _IOR('p', 0x05, short) /* ? */ -+#define ACERHK_MAIL_LED_OFF _IO('p', 0x10) /* switch mail LED off */ -+#define ACERHK_MAIL_LED_ON _IO('p', 0x11) /* switch mail LED on (blinking) */ -+#define ACERHK_START_POLLING _IO('p', 0x12) /* poll keys in kernel, send real key events */ -+#define ACERHK_STOP_POLLING _IO('p', 0x13) /* stop key polling in kernel */ -+#define ACERHK_GET_KEY_MAP _IOR('p', 0x20, int) /* Get mapping of key names to key events, */ -+#define ACERHK_SET_KEY_MAP _IOW('p', 0x21, int) /* Set mapping of key names to key events */ -+ -+/* all possible keys (known to me) */ -+typedef enum e_key_names { -+ k_none = 0, -+ k_help = 1, /* Fn+F1 */ -+ k_setup = 2, /* Fn+F2 */ -+ k_p1 = 3, -+ k_p2 = 4, -+ k_p3 = 5, -+ k_www = 6, -+ k_mail = 7, -+ k_wireless = 8, -+ k_power = 9, /* Fn+F3 */ -+ k_mute = 10, /* Fn+F8 */ -+ k_volup = 11, /* Fn+Up */ -+ k_voldn = 12, /* Fn+Down */ -+ k_res = 13, /* resolution change on Medion MD 40100 */ -+ k_close = 14, /* if lid is closed in tablet mode */ -+ k_open = 15, /* if lid is opend in tablet mode */ -+ k_wireless2 = 16, /* second wireless button on TM 243LC */ -+ k_play = 17, /* Play/Pause found on AOpen */ -+ k_stop = 18, /* Stop/Eject found on AOpen */ -+ k_prev = 19, /* Prev found on AOpen */ -+ k_next = 20, /* Next found on AOpen */ -+ k_display = 21 /* Change internal/external display on MD 42200 */ -+} t_key_names; -+#define NR_KEY_NAMES 22 -+typedef unsigned int t_map_name2event[NR_KEY_NAMES]; -+ -+#ifdef __KERNEL__ -+ -+/* available features */ -+#define TM_F_WLAN_EC1 0x00000010 -+#define TM_F_BLUE_EC1 0x00000020 -+#define TM_F_WLAN_EC2 0x00000040 -+#define TM_F_BLUE_EC2 0x00000080 -+#define TM_F_MUTE_LED_EC 0x00001000 -+#define TM_F_MAIL_LED 0x00010000 -+#define TM_F_MAIL_LED_EC 0x00020000 -+#define TM_F_MAIL_LED_EC2 0x00040000 -+#define TM_F_MAIL_LED_EC3 0x00080000 -+ -+#define TM_F_CONNECT 0x00100000 -+#define TM_F_THERMAL 0x00200000 -+#define TM_F_PBUTTON 0x00400000 -+#define TM_F_WBUTTON 0x00800000 -+ -+typedef enum acer_type { -+ TM_unknown, -+ /* 200, 210, 520, 600 and 730 series, Medion MD42200 */ -+ TM_old, -+ /* C100, C110, 220, 230, 240, 260, 350, 360, 610, 620, 630, 740 series -+ Medion MD40100, Aspire 1600, FS Amilo */ -+ TM_new, -+ /* Aspire 13xx, 14xx, 1700, TM 290, 650, 660, 800 */ -+ TM_dritek -+} t_acer_type; -+ -+struct register_buffer { -+ unsigned int eax; -+ unsigned int ebx; -+ unsigned int ecx; -+ unsigned int edx; -+ unsigned int edi; -+ unsigned int esi; -+ unsigned int ebp; -+}; -+ -+typedef asmlinkage void (*bios_call) (struct register_buffer *); -+ -+#endif -+ -+#endif -diff -Nurp linux-2.6.37/3rdparty/acerhk/AUTHORS linux-2.6.37.3rdparty/3rdparty/acerhk/AUTHORS ---- linux-2.6.37/3rdparty/acerhk/AUTHORS 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/AUTHORS 2006-11-10 00:02:55.000000000 +0200 -@@ -0,0 +1 @@ -+Olaf Tauber -\ Ingen nyrad vid filslut -diff -Nurp linux-2.6.37/3rdparty/acerhk/COPYING linux-2.6.37.3rdparty/3rdparty/acerhk/COPYING ---- linux-2.6.37/3rdparty/acerhk/COPYING 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/COPYING 2002-05-08 22:01:52.000000000 +0300 -@@ -0,0 +1,340 @@ -+ GNU GENERAL PUBLIC LICENSE -+ Version 2, June 1991 -+ -+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. -+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ -+ Preamble -+ -+ The licenses for most software are designed to take away your -+freedom to share and change it. By contrast, the GNU General Public -+License is intended to guarantee your freedom to share and change free -+software--to make sure the software is free for all its users. This -+General Public License applies to most of the Free Software -+Foundation's software and to any other program whose authors commit to -+using it. (Some other Free Software Foundation software is covered by -+the GNU Library General Public License instead.) You can apply it to -+your programs, too. -+ -+ When we speak of free software, we are referring to freedom, not -+price. Our General Public Licenses are designed to make sure that you -+have the freedom to distribute copies of free software (and charge for -+this service if you wish), that you receive source code or can get it -+if you want it, that you can change the software or use pieces of it -+in new free programs; and that you know you can do these things. -+ -+ To protect your rights, we need to make restrictions that forbid -+anyone to deny you these rights or to ask you to surrender the rights. -+These restrictions translate to certain responsibilities for you if you -+distribute copies of the software, or if you modify it. -+ -+ For example, if you distribute copies of such a program, whether -+gratis or for a fee, you must give the recipients all the rights that -+you have. You must make sure that they, too, receive or can get the -+source code. And you must show them these terms so they know their -+rights. -+ -+ We protect your rights with two steps: (1) copyright the software, and -+(2) offer you this license which gives you legal permission to copy, -+distribute and/or modify the software. -+ -+ Also, for each author's protection and ours, we want to make certain -+that everyone understands that there is no warranty for this free -+software. If the software is modified by someone else and passed on, we -+want its recipients to know that what they have is not the original, so -+that any problems introduced by others will not reflect on the original -+authors' reputations. -+ -+ Finally, any free program is threatened constantly by software -+patents. We wish to avoid the danger that redistributors of a free -+program will individually obtain patent licenses, in effect making the -+program proprietary. To prevent this, we have made it clear that any -+patent must be licensed for everyone's free use or not licensed at all. -+ -+ The precise terms and conditions for copying, distribution and -+modification follow. -+ -+ GNU GENERAL PUBLIC LICENSE -+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -+ -+ 0. This License applies to any program or other work which contains -+a notice placed by the copyright holder saying it may be distributed -+under the terms of this General Public License. The "Program", below, -+refers to any such program or work, and a "work based on the Program" -+means either the Program or any derivative work under copyright law: -+that is to say, a work containing the Program or a portion of it, -+either verbatim or with modifications and/or translated into another -+language. (Hereinafter, translation is included without limitation in -+the term "modification".) Each licensee is addressed as "you". -+ -+Activities other than copying, distribution and modification are not -+covered by this License; they are outside its scope. The act of -+running the Program is not restricted, and the output from the Program -+is covered only if its contents constitute a work based on the -+Program (independent of having been made by running the Program). -+Whether that is true depends on what the Program does. -+ -+ 1. You may copy and distribute verbatim copies of the Program's -+source code as you receive it, in any medium, provided that you -+conspicuously and appropriately publish on each copy an appropriate -+copyright notice and disclaimer of warranty; keep intact all the -+notices that refer to this License and to the absence of any warranty; -+and give any other recipients of the Program a copy of this License -+along with the Program. -+ -+You may charge a fee for the physical act of transferring a copy, and -+you may at your option offer warranty protection in exchange for a fee. -+ -+ 2. You may modify your copy or copies of the Program or any portion -+of it, thus forming a work based on the Program, and copy and -+distribute such modifications or work under the terms of Section 1 -+above, provided that you also meet all of these conditions: -+ -+ a) You must cause the modified files to carry prominent notices -+ stating that you changed the files and the date of any change. -+ -+ b) You must cause any work that you distribute or publish, that in -+ whole or in part contains or is derived from the Program or any -+ part thereof, to be licensed as a whole at no charge to all third -+ parties under the terms of this License. -+ -+ c) If the modified program normally reads commands interactively -+ when run, you must cause it, when started running for such -+ interactive use in the most ordinary way, to print or display an -+ announcement including an appropriate copyright notice and a -+ notice that there is no warranty (or else, saying that you provide -+ a warranty) and that users may redistribute the program under -+ these conditions, and telling the user how to view a copy of this -+ License. (Exception: if the Program itself is interactive but -+ does not normally print such an announcement, your work based on -+ the Program is not required to print an announcement.) -+ -+These requirements apply to the modified work as a whole. If -+identifiable sections of that work are not derived from the Program, -+and can be reasonably considered independent and separate works in -+themselves, then this License, and its terms, do not apply to those -+sections when you distribute them as separate works. But when you -+distribute the same sections as part of a whole which is a work based -+on the Program, the distribution of the whole must be on the terms of -+this License, whose permissions for other licensees extend to the -+entire whole, and thus to each and every part regardless of who wrote it. -+ -+Thus, it is not the intent of this section to claim rights or contest -+your rights to work written entirely by you; rather, the intent is to -+exercise the right to control the distribution of derivative or -+collective works based on the Program. -+ -+In addition, mere aggregation of another work not based on the Program -+with the Program (or with a work based on the Program) on a volume of -+a storage or distribution medium does not bring the other work under -+the scope of this License. -+ -+ 3. You may copy and distribute the Program (or a work based on it, -+under Section 2) in object code or executable form under the terms of -+Sections 1 and 2 above provided that you also do one of the following: -+ -+ a) Accompany it with the complete corresponding machine-readable -+ source code, which must be distributed under the terms of Sections -+ 1 and 2 above on a medium customarily used for software interchange; or, -+ -+ b) Accompany it with a written offer, valid for at least three -+ years, to give any third party, for a charge no more than your -+ cost of physically performing source distribution, a complete -+ machine-readable copy of the corresponding source code, to be -+ distributed under the terms of Sections 1 and 2 above on a medium -+ customarily used for software interchange; or, -+ -+ c) Accompany it with the information you received as to the offer -+ to distribute corresponding source code. (This alternative is -+ allowed only for noncommercial distribution and only if you -+ received the program in object code or executable form with such -+ an offer, in accord with Subsection b above.) -+ -+The source code for a work means the preferred form of the work for -+making modifications to it. For an executable work, complete source -+code means all the source code for all modules it contains, plus any -+associated interface definition files, plus the scripts used to -+control compilation and installation of the executable. However, as a -+special exception, the source code distributed need not include -+anything that is normally distributed (in either source or binary -+form) with the major components (compiler, kernel, and so on) of the -+operating system on which the executable runs, unless that component -+itself accompanies the executable. -+ -+If distribution of executable or object code is made by offering -+access to copy from a designated place, then offering equivalent -+access to copy the source code from the same place counts as -+distribution of the source code, even though third parties are not -+compelled to copy the source along with the object code. -+ -+ 4. You may not copy, modify, sublicense, or distribute the Program -+except as expressly provided under this License. Any attempt -+otherwise to copy, modify, sublicense or distribute the Program is -+void, and will automatically terminate your rights under this License. -+However, parties who have received copies, or rights, from you under -+this License will not have their licenses terminated so long as such -+parties remain in full compliance. -+ -+ 5. You are not required to accept this License, since you have not -+signed it. However, nothing else grants you permission to modify or -+distribute the Program or its derivative works. These actions are -+prohibited by law if you do not accept this License. Therefore, by -+modifying or distributing the Program (or any work based on the -+Program), you indicate your acceptance of this License to do so, and -+all its terms and conditions for copying, distributing or modifying -+the Program or works based on it. -+ -+ 6. Each time you redistribute the Program (or any work based on the -+Program), the recipient automatically receives a license from the -+original licensor to copy, distribute or modify the Program subject to -+these terms and conditions. You may not impose any further -+restrictions on the recipients' exercise of the rights granted herein. -+You are not responsible for enforcing compliance by third parties to -+this License. -+ -+ 7. If, as a consequence of a court judgment or allegation of patent -+infringement or for any other reason (not limited to patent issues), -+conditions are imposed on you (whether by court order, agreement or -+otherwise) that contradict the conditions of this License, they do not -+excuse you from the conditions of this License. If you cannot -+distribute so as to satisfy simultaneously your obligations under this -+License and any other pertinent obligations, then as a consequence you -+may not distribute the Program at all. For example, if a patent -+license would not permit royalty-free redistribution of the Program by -+all those who receive copies directly or indirectly through you, then -+the only way you could satisfy both it and this License would be to -+refrain entirely from distribution of the Program. -+ -+If any portion of this section is held invalid or unenforceable under -+any particular circumstance, the balance of the section is intended to -+apply and the section as a whole is intended to apply in other -+circumstances. -+ -+It is not the purpose of this section to induce you to infringe any -+patents or other property right claims or to contest validity of any -+such claims; this section has the sole purpose of protecting the -+integrity of the free software distribution system, which is -+implemented by public license practices. Many people have made -+generous contributions to the wide range of software distributed -+through that system in reliance on consistent application of that -+system; it is up to the author/donor to decide if he or she is willing -+to distribute software through any other system and a licensee cannot -+impose that choice. -+ -+This section is intended to make thoroughly clear what is believed to -+be a consequence of the rest of this License. -+ -+ 8. If the distribution and/or use of the Program is restricted in -+certain countries either by patents or by copyrighted interfaces, the -+original copyright holder who places the Program under this License -+may add an explicit geographical distribution limitation excluding -+those countries, so that distribution is permitted only in or among -+countries not thus excluded. In such case, this License incorporates -+the limitation as if written in the body of this License. -+ -+ 9. The Free Software Foundation may publish revised and/or new versions -+of the General Public License from time to time. Such new versions will -+be similar in spirit to the present version, but may differ in detail to -+address new problems or concerns. -+ -+Each version is given a distinguishing version number. If the Program -+specifies a version number of this License which applies to it and "any -+later version", you have the option of following the terms and conditions -+either of that version or of any later version published by the Free -+Software Foundation. If the Program does not specify a version number of -+this License, you may choose any version ever published by the Free Software -+Foundation. -+ -+ 10. If you wish to incorporate parts of the Program into other free -+programs whose distribution conditions are different, write to the author -+to ask for permission. For software which is copyrighted by the Free -+Software Foundation, write to the Free Software Foundation; we sometimes -+make exceptions for this. Our decision will be guided by the two goals -+of preserving the free status of all derivatives of our free software and -+of promoting the sharing and reuse of software generally. -+ -+ NO WARRANTY -+ -+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -+REPAIR OR CORRECTION. -+ -+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -+POSSIBILITY OF SUCH DAMAGES. -+ -+ END OF TERMS AND CONDITIONS -+ -+ How to Apply These Terms to Your New Programs -+ -+ If you develop a new program, and you want it to be of the greatest -+possible use to the public, the best way to achieve this is to make it -+free software which everyone can redistribute and change under these terms. -+ -+ To do so, attach the following notices to the program. It is safest -+to attach them to the start of each source file to most effectively -+convey the exclusion of warranty; and each file should have at least -+the "copyright" line and a pointer to where the full notice is found. -+ -+ -+ Copyright (C) 19yy -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ -+ -+Also add information on how to contact you by electronic and paper mail. -+ -+If the program is interactive, make it output a short notice like this -+when it starts in an interactive mode: -+ -+ Gnomovision version 69, Copyright (C) 19yy name of author -+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. -+ This is free software, and you are welcome to redistribute it -+ under certain conditions; type `show c' for details. -+ -+The hypothetical commands `show w' and `show c' should show the appropriate -+parts of the General Public License. Of course, the commands you use may -+be called something other than `show w' and `show c'; they could even be -+mouse-clicks or menu items--whatever suits your program. -+ -+You should also get your employer (if you work as a programmer) or your -+school, if any, to sign a "copyright disclaimer" for the program, if -+necessary. Here is a sample; alter the names: -+ -+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program -+ `Gnomovision' (which makes passes at compilers) written by James Hacker. -+ -+ , 1 April 1989 -+ Ty Coon, President of Vice -+ -+This General Public License does not permit incorporating your program into -+proprietary programs. If your program is a subroutine library, you may -+consider it more useful to permit linking proprietary applications with the -+library. If this is what you want to do, use the GNU Library General -+Public License instead of this License. -diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/acertm.def linux-2.6.37.3rdparty/3rdparty/acerhk/doc/acertm.def ---- linux-2.6.37/3rdparty/acerhk/doc/acertm.def 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/acertm.def 2003-08-24 19:46:37.000000000 +0300 -@@ -0,0 +1,56 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ Konfiguration -+ -+ Mute/Unmute -+ Lauter -+ Leiser -+ -+ Terminal -+ XEmacs -+ Play/Pause -+ Play/Pause -+ -+ -+ -+ -diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/FAQ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/FAQ ---- linux-2.6.37/3rdparty/acerhk/doc/FAQ 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/FAQ 2005-11-10 19:45:33.000000000 +0200 -@@ -0,0 +1,150 @@ -+****************************************************************************** -+ -+Q: I have a (non Acer) notebook which is not recognized by your driver but I -+think it should. What information do you need? -+ -+A: If it is a non-Acer laptop I would like to know, what makes you think that -+your laptop would work with my driver. A windows driver/utility package of the -+name "LaunchManager" or "EasyButton" is a good hint. If it is clear that your -+hardware is okay, I need the string with your model name from BIOS. Please -+don't ask me how to do it. To find out which functions of your laptop the -+driver will support, it is best, if you can tell me where to get this -+"LaunchManager" or "EasyButton" package. -+If it is a model from 2003 or newer then chances are good that it's a dritek -+type one. In this case try the option "force_series=6000" to at least enable -+the keys. -+ -+****************************************************************************** -+ -+Q: Why can't I activate my wireless hardware on Acer TM 420/430/6000/8000 ...? -+ -+A: On many newer laptops (Dritek type) I do not now how to do this, -+sorry. Someone would need to find out how windows does it. Volunteers? -+ -+****************************************************************************** -+ -+Q: I know that wireless hardware is supported on my Aspire 1690/TravelMate4600, -+but I cannot get it to work, why? -+ -+A: On these model (and similar) the wireless hardware is controlled on two -+different levels. One is controlled by acerhk's xxxled file, the other level -+is controlled by the key itself. To actually activate the hardware you need to -+write '1' to the xxled file and you need to press the corresponding button. -+Example: -+echo 1 > /proc/driver/acerhk/wirelessled -+ -+ -+****************************************************************************** -+ -+Q: Why do I always get 0x00 from /proc/driver/acerhk/key? -+ -+A1: Check the type of your laptop if /proc/driver/acerhk/info. If it is -+"Dritek", you cannot read the keys from the driver. Starting from version -+0.5.17 you get "n/a" out of the key file in this case. -+A2: If you have polling enabled (default) you will almost always read 0x00 -+from this file. This is because in every polling cycle it is checked for key -+events and if there is one, it is instantly translated and sent to the kernel -+input system. Starting with version 0.5.20 you get "n/a" out of the key file -+in this case. -+ -+****************************************************************************** -+ -+Q: When I press a key, nothing happens. Why are they not working? -+ -+A: Most keys won't do anything by themself. If nothing happens, that is -+because no programm knows what to do with the new keys. You need to assign -+actions to them, use the hotkey manager of your desktop to do that. If -+using Gnome, you find it under desktop settings - keyboard shortcuts. -+ -+****************************************************************************** -+ -+Q: The driver works for my laptop, but not all of the keys are working. What -+can I do? -+ -+A: There are some keys/key combinations which generate an ACPI event, -+e.g. Fn+F4 on some models or the lid button. -+If you have a different laptop than the one which got detected (or you used -+"force_series=xxx" anyway), then it is possible that the mapping acerhk uses -+to translate the codes from the buttons to key events is wrong. -+In this case, load the driver with "verbose=4" and press the buttons which do -+not work. Then look for messages from acerhk of the form "translated acer key -+code xxx to no key". Note these codes together with the button they belong to -+and send me this list along with the model name of your laptop. -+If you do not see a usable name in /proc/driver/acerhk/info please try the -+tools dmidecode/biosdecode/vpddecode to find this name. -+ -+****************************************************************************** -+Q: I press the wireless key but the hardware doesn't get activated, what's wrong? -+ -+A: The driver is only on older models (non-Dritek type) able to read -+keypresses by itself and toggle the hardware/LED automatically (with option -+autowlan=1). On Dritek type models this must be done by writing the desired -+value to one of the wireless files in the proc filesystem. In most -+cases this would be /proc/drivers/acerhk/wirelessled to control wlan -+hardware (blueled for Bluetooth hardware): -+ -+echo 1 > /proc/driver/acerhk/wirelessled -+ -+But you could use a hotkey manager to do that automatically when you press the -+button. In this case be aware that on some models the button generates -+different key events according to the actual state of the wireless hardware. -+ -+****************************************************************************** -+ -+Q: My keys do not work, I only get kernel messages of the form: -+atkbd.c: Unknown key pressed (translated set 2, code 0xf4 on isa0060/serio0). -+atkbd.c: Use 'setkeycodes e074 ' to make it known. -+ -+A: Press each of the buttons and note the mentioned -+code for it (e074 in this example). You should get a list like this: -+P1 e074 -+P2 e075 -+... -+If you are finished with it, look into /usr/include/linux/input.h, using -+your favourite text viewer/editor. Search for "KEY_STOP". You should see -+the following line in the file: -+ -+#define KEY_STOP 128 -+ -+After it many more lines with equal #defines should be visible. Look for -+key names which best match the names of your buttons, e.g. KEY_PROG1, -+KEY_WWW and so on. Note the numbers assigned to the names, for KEY_STOP -+this would be 128, for KEY_PROG1 148. -+Now you have a list with three items per entry, your button, a code -+from the kernel messages and a corresponding number from the file -+linux.h: -+P1 e074 148 -+P2 e075 149 -+... -+For each line in this list, issue the setkeycodes command as mentioned -+in the kernel message: -+setkeycodes e074 148 -+setkeycodes e075 149 -+... -+After doing that, the keys should be available for your hotkey manager. -+ -+****************************************************************************** -+ -+Q: My WLAN hardware gets activated through /proc/driver/acerhk/wirelessled, -+but the LED is not working. Why not? -+ -+A: Try if adding the option "led=1" to your wireless module helps. For the -+ipw2200 driver it works, as Didier CLERC found out: -+ -+ I have to load the module "ipw2200" with the option "led=1", then -+ the wifi button blinks until a network is detected. -+ -+****************************************************************************** -+ -+Q: I have an unsupported laptop, but the driver works when I force the series -+to a type like 610 or 2100. Only my buttons doesn't get recognized, why? -+ -+A: On models of the type TM_new (like TravelMate 600, 2100 and many other, see -+acerhk.c, function setup_model_features for details) the buttons use different -+codes on different models. Therefore the driver needs to know these codes. You -+can get them if you load the driver with verbose=4, press the buttons and key -+combinations (Fn+xx) you are interested in and look for kernel messages of the -+form "acerhk: translated acer key code 0xnn to ...". Write down the key code -+for each button/key combination and send them to me so I can patch the -+driver. I also need your model string to make autodetection work, so include -+/proc/driver/acerk/info. -\ Ingen nyrad vid filslut -diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/IOCTL linux-2.6.37.3rdparty/3rdparty/acerhk/doc/IOCTL ---- linux-2.6.37/3rdparty/acerhk/doc/IOCTL 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/IOCTL 2004-01-31 22:38:17.000000000 +0200 -@@ -0,0 +1,61 @@ -+Documentation of possible IOCTLs used by acerhk -++++++++++++++++++++++++++++++++++++++++++++++++ -+ -+ACERHK_GET_KEYCOUNT -+ Read the number of unread key presses in queue -+ Parameter: pointer to char -+ -+ACERHK_GET_KEYID -+ Read the code of the first key press (oldest) in queue -+ Parameter: pointer to char -+ -+ACERHK_CONNECT -+ Don't know what it does, used in windows driver -+ -+ACERHK_DISCONNECT -+ Don't know what it does, used in windows driver -+ -+ACERHK_GET_THERMAL_EVENT -+ Don't know what it does, used in windows driver -+ Parameter: pointer to short -+ -+ACERHK_MAIL_LED_OFF -+ Switch off the LED of the mail button(if available) -+ -+ACERHK_MAIL_LED_ON -+ Switch on the LED of the mail button (if available) -+ -+ACERHK_START_POLLING -+ Start polling (and translation to key events) in kernel -+ -+ACERHK_STOP_POLLING -+ Stop polling in kernel -+ -+ACERHK_GET_KEY_MAP -+ Get mapping of key names to key events -+ Parameter: pointer to t_map_name2event -+ -+ACERHK_SET_KEY_MAP -+ Set mapping of key names to key events -+ Parameter: pointer to t_map_name2event -+ -+IOCTLs used by windows driver -++++++++++++++++++++++++++++++ -+device name: -+\DosDevices\HOTKEY -+\Device\HOTKEY -+ -+ -+630 series: -+0x222404 -+ Get CMOS index -+0x222408 -+ ACERHK_GET_KEYCOUNT -+0x22240C -+ ACERHK_GET_KEYID -+0x222410 -+ ACERHK_MAIL_LED_OFF/ACERHK_MAIL_LED_ON -+0x222414 -+ ACERHK_CONNECT/ACERHK_DISCONNECT -+0x222418 -+ ACERHK_GET_THERMAL_EVENT -diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/keycodes linux-2.6.37.3rdparty/3rdparty/acerhk/doc/keycodes ---- linux-2.6.37/3rdparty/acerhk/doc/keycodes 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/keycodes 2004-04-18 20:42:59.000000000 +0300 -@@ -0,0 +1,26 @@ -+standard X keycodes with older (acerhk controlled) hardware from Acer -+********************************************************************* -+help (Fn+F1) 226 -+setup (Fn+F2) 129 -+p1 153 -+p2 144 -+p3 171 -+www 178 -+mail 236 -+wireless 147 -+power (Fn+F3) 222 -+mute (Fn+F8) 166 -+volup (Fn+Up) 158 -+voldn (Fn+Down) 165 -+ -+standard X keycodes with newer (acerhk activated) hardware from Dritek -+********************************************************************** -+help (Fn+F1) 226 -+setup (Fn+F2) 129 -+p1 153 -+p2 144 -+www 178 -+mail 236 -+volup (Fn+Up) 176 -+voldn (Fn+Down) 174 -+mute (Fn+F8) 160 -diff -Nurp linux-2.6.37/3rdparty/acerhk/doc/md95400.def linux-2.6.37.3rdparty/3rdparty/acerhk/doc/md95400.def ---- linux-2.6.37/3rdparty/acerhk/doc/md95400.def 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/doc/md95400.def 2005-03-01 22:46:53.000000000 +0200 -@@ -0,0 +1,14 @@ -+ -+ -+ -+Mail -+Web -+Play/Pause -+Stop -+Previous -+Next -+Leiser -+Lauter -+ -+ -+ -diff -Nurp linux-2.6.37/3rdparty/acerhk/INSTALL linux-2.6.37.3rdparty/3rdparty/acerhk/INSTALL ---- linux-2.6.37/3rdparty/acerhk/INSTALL 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/INSTALL 2005-12-07 14:27:16.000000000 +0200 -@@ -0,0 +1,109 @@ -+Installation -+************ -+ -+1. You need the kernel sources (or kernel headers for your kernel) -+installed to compile the driver. -+ -+2. Your kernel needs loadable module support with version information for -+modules enabled. Usage of procfs is highly recommended. -+If you want the driver to generate regular keyboard events using -+kernel version 2.4 you need the input system of the kernel enabled -+(Input core support AND keyboard support). In kernel version 2.6 all -+needed functionality should be available by default. -+ -+In most cases you can skip the next step, the Makefile tries do determine -+the correct directory on its own. Change KERNELSRC only if the autodetection -+does not work for you. Otherwise proceed directly with step 4. -+ -+3. Before you compile the driver, change KERNELSRC in the makefile to your -+path to the kernel build environment. If you are using a self compiled kernel, -+point it to the root of your sources. If you are using a packaged kernel of -+your distribution, install the package with kernel headers -+(Debian:kernel-headers) and point KERNELSRC to where the headers and config -+files are located. If you are using Debian, this -+would be "/lib/modules//build". -+ -+4. Do: -+ make -+to compile the driver. If you run into problems because of the makefile not -+recognizing your kernel version correctly, try this: -+ make acerhk.o - kernel version 2.4 -+ make acerhk.ko - kernel version 2.6 -+ -+5. Do: -+ make install -+to automatically copy the driver into the kernel module library. If you've -+done so, proceed directly with step 8. If you want to install the module -+binary yourself (because you want a different location), use steps 6 and 7 -+instead. -+ -+6. Copy the created file "acerhk.o" ("acerhk.ko" with version 2.6) to your -+kernel modules path. In Debian this could be -+"/lib/modules//kernel/drivers/extra/". -+ -+7. Update module dependencies: depmod -a -+ -+8. Try loading the module with: -+ insmod/modprobe acerhk -+If it succeeds - congratulations! If you have procfs enabled, you can try the -+following to test the driver: -+ -+Non-Dritek models: -+Press one of the special keys and after that: -+ cat /proc/driver/acerhk/key -+to read the (hexadezimal) code of the key pressed. It should -+be different from 0x00. -+(Note: You mustn't have the polling feature enabled for this to work, so load -+ the module with poll=0) -+ -+Dritek-models: -+Press one of the special keys and look for the generated key with "xev". If -+there is none, then you should see at least kernel messages about using -+setkeycodes. -+ -+If your notebook has a mail led you can try this: -+ echo on > /proc/driver/acerhk/led -+This should sete the mail led to blinking mode. -+ echo off > /proc/driver/acerhk/led -+turns it off again. -+See README for further usage information. -+ -+If the module didn't load look into your kernel messages what went wrong. If -+you see something like the following lines: -+ acerhk: could not find request handler -+ acerhk: can't find ROM area -+ acerhk: unloaded -+then your hardware is not recognized. See README for supported models. If it -+won't work on your notebook, please contact me and I will see if I can fix -+that. -+ -+Integrating the driver into kernel tree version 2.6 -+*************************************************** -+ -+If you want the driver to fully integrate into the kernel tree of version 2.6 -+proceed as follows: -+1. Copy the acerhk directory into the source tree, for instance -+ /usr/src/linux/drivers/misc/acerhk -+2. Include the driver directory in the config files. Add to the Kconfig -+file of the parent directory(/usr/src/linux/drivers/misc/Kconfig): -+ -+config ACERHK -+ tristate "Acerhk driver" -+ depends on EXPERIMENTAL -+ ---help--- -+ This is an experimental acer keyboard driver for -+ acer laptops -+ -+3. Include the acer directory in it's parents -+Makefile(/usr/src/linux/drivers/misc/Makefile): -+ -+obj-$(CONFIG_ACERHK) += acerhk/ -+ -+4. In this case you also need to activate the misc drivers first -+(/usr/src/linux/drivers/Kconfig): -+ -+source "drivers/misc/Kconfig" -+ -+If that's done, you should be able to select the driver from the configuration -+programm and build the module. -+ -diff -Nurp linux-2.6.37/3rdparty/acerhk/Makefile linux-2.6.37.3rdparty/3rdparty/acerhk/Makefile ---- linux-2.6.37/3rdparty/acerhk/Makefile 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/Makefile 2006-08-16 19:11:08.000000000 +0300 -@@ -0,0 +1,65 @@ -+# change KERNELSRC to the location of your kernel build tree only if -+# autodetection does not work -+#KERNELSRC=/usr/src/linux -+KERNELSRC?=/lib/modules/`uname -r`/build -+# Starting with 2.6.18, the kernel version is in utsrelease.h instead of version.h, accomodate both cases -+KERNELVERSION=$(shell awk -F\" '/REL/ {print $$2}' $(shell grep -s -l REL $(KERNELSRC)/include/linux/version.h $(KERNELSRC)/include/linux/utsrelease.h)) -+KERNELMAJOR=$(shell echo $(KERNELVERSION)|head -c3) -+ -+# next line is for kernel 2.6, if you integrate the driver in the kernel tree -+# /usr/src/linux/drivers/acerhk - or something similar -+# don't forget to add the following line to the parent dir's Makefile: -+# (/usr/src/linux/drivers/Makefile) -+# obj-m += acerhk/ -+CONFIG_ACERHK?=m -+obj-$(CONFIG_ACERHK) += acerhk.o -+ -+CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe -+INCLUDE=-I$(KERNELSRC)/include -+ -+ifeq ($(KERNELMAJOR), 2.6) -+TARGET := acerhk.ko -+else -+TARGET := acerhk.o -+endif -+ -+SOURCE := acerhk.c -+ -+all: $(TARGET) -+ -+help: -+ @echo Possible targets: -+ @echo -e all\\t- default target, builds kernel module -+ @echo -e install\\t- copies module binary to /lib/modules/$(KERNELVERSION)/extra/ -+ @echo -e clean\\t- removes all binaries and temporary files -+ -+# this target is only for me, don't use it yourself (Olaf) -+export: -+ sh export.sh -+ -+acerhk.ko: $(SOURCE) acerhk.h -+ $(MAKE) -C $(KERNELSRC) SUBDIRS=$(PWD) modules -+ -+acerhk.o: $(SOURCE) acerhk.h -+ $(CC) $(INCLUDE) $(CFLAGS) -DMODVERSIONS -DMODULE -D__KERNEL__ -o $(TARGET) $(SOURCE) -+ -+asm: $(SOURCE) -+ifeq ($(KERNELMAJOR), 2.6) -+ $(CC) $(INCLUDE) $(INCLUDE)/asm-i386/mach-default $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) -+else -+ $(CC) $(INCLUDE) $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) -+endif -+ -+clean: -+ rm -f *~ *.o *.s *.ko .acerhk* *.mod.c -+ -+load: $(TARGET) -+ insmod $(TARGET) -+ -+unload: -+ rmmod acerhk -+ -+install: $(TARGET) -+ mkdir -p /lib/modules/$(KERNELVERSION)/extra -+ cp -v $(TARGET) /lib/modules/$(KERNELVERSION)/extra/ -+ depmod -a -diff -Nurp linux-2.6.37/3rdparty/acerhk/NEWS linux-2.6.37.3rdparty/3rdparty/acerhk/NEWS ---- linux-2.6.37/3rdparty/acerhk/NEWS 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/NEWS 2003-02-28 02:47:18.000000000 +0200 -@@ -0,0 +1,16 @@ -+2003-02-28 Version 0.5.0 -+ completely changed key polling/translation -+ support for more series -+2002-07-06 Version 0.4.2 -+ added debug functionality -+ bugfix with 520/210 -+2002-06-24 Version 0.4.1 -+ added support for TM 210 series -+ removed nvram dependency -+2002-06-15 Version 0.4 -+ added model recognition -+ added kernel polling an key event generation -+ added support for 520 series -+ -+2002-04-29 Version 0.3 -+ Initial release -\ Ingen nyrad vid filslut -diff -Nurp linux-2.6.37/3rdparty/acerhk/README linux-2.6.37.3rdparty/3rdparty/acerhk/README ---- linux-2.6.37/3rdparty/acerhk/README 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/acerhk/README 2006-11-10 00:01:46.000000000 +0200 -@@ -0,0 +1,215 @@ -+ -+What is this driver good for? -+***************************** -+ -+This driver will give access to the special keys on notebooks of the -+Acer Travelmate series, which are not handled by the keyboard -+driver. -+It also works on notebooks from other manufacturers (some Medion, -+Fujitsu-Siemens, ...). -+ -+It also has some other related functionality (depending on the model): -+- controlling LEDs (Mail, Wireless) -+- enable/disable wireless hardware -+ -+Usage -+***** -+ -+The driver provides a device /dev/misc/acerhk where you can access all -+functions through IOCTL's, look into acerhk.h for their definitions. -+I use devfs and don't care about the minor number. You may want to change -+ACERHK_MINOR in acerhk.h. -+If you don't use devfs you need to create the device node. The device uses -+major 10 (misc character devices), the minor is chosen by the kernel (if -+ACERHK_MINOR equals MISC_DYNAMIC_MINOR) or by yourself (any other value). -+Use something like -+ mknod /dev/acerhk c 10 123 -+to create the device node. -+You can also use most functions through procfs. You will find some files in -+/proc/driver/acerhk: -+ led - write "on" or "off" to set the mail led state -+ key - read it to get a key press, only useful on non-dritek models -+ and only if polling is disabled -+ This file exists only on non-dritek models -+ info - some information about the driver, including the number of -+ pending keys -+ wirelessled - write "on" or "off" to set the led state and enable -+ wlan hardware -+ On some laptops you need to load the ipw2200 (others -+ too?) with option "led=1" to make the led work. -+ blueled - write "on" or "off" to set the led state and enable -+ bluetooth hardware -+ write a value n >= 50 to let the led blink every n jiffies -+ Do not use blinking mode where not only the led but also the -+ bluetooth hardware is controlled! -+ -+Probably the best way to use the driver is to let it poll for the keys itself -+and generate real key events. this is the normal case when loading the module -+without an additional parameter : -+ -+ modprobe acerhk -+ -+or you can use the ioctl as specified in acerhk.h to control the polling. If -+your kernel has keyboard input support you will get real key events when -+pressing the special keys. -+You can then use whatever software you like to make use of those keys, -+i.e. 'hotkeys' or Linux Easy Access Keyboard 'lineakd'. I prefer hotkeys, just -+in case anyone is interested, you can find a sample configuration for this -+programm in doc/acertm.def. -+Gnome >= 2.4 has built in support for multimedia keys (acme), that's -+what I use now. -+ -+Have a look at -+ http://mmkc.sourceforge.net/ -+ -+Note: If you have one of the newer models using dritek hardware you don't need -+polling (nor the file /proc/driver/acerhk/key) to use your buttons. On this -+hardware the driver only needs to send a special command to the keyboard -+controller to make them work. The actual key presses are then handled by the -+normal keyboard driver of the kernel. You can switch of polling by -+loading the module with poll=0. It saves you precious cpu time. -+ -+ -+Module parameters -+***************** -+name values meaning -+autowlan 0,1 disable(default)/enable automatic switching of wlan hardware -+ on wireless keypress, only useful for older (no -+ dritek-hardware) models, works only will poll=1 -+poll 0,1 disable/enable(default) kernel polling of key events -+verbose 0-4 verbosity level, see below -+usedritek 0,1 disable/enable(default) use of dritek hardware on newer -+ series, needed to activate the keys on such models -+force_series set this to the laptop series number you want the driver to -+ assume, skip autodetection (look at function -+ setup_model_features() for possible values) -+ -+Debugging -+********* -+ -+You can make the the driver be more verbose. To do this, add the following -+parameter when loading the module: -+ -+ verbose= -+ -+n is an integer, 0 (default) means no additional information while increasing -+values provide an increasing amount of information. Currently 3 is maximum, -+bigger values will have the same effect as 3. The existing verbosity levels -+will generate information about: -+ Level Information -+ 1 state changes and variable initialization -+ 2 model probing -+ 3 key translation, only known keys -+ 4 key translation, also unknown keys -+ 5 misc. information (idle values) -+ -+If you have rather serious problems you can activate debugging functionality -+in the driver. To do this, uncomment the '#define ACERDEBUG' in acerhk.c. You -+will find a new file in /proc/driver/acerhk, named 'debug'. You cannot read -+from this file, but you can write commands consisting of up to 4 digits. The -+first one specifies the action, while the latter can give additional -+parameters. Implemented are: -+ 'd' decrement module usage counter -+ 'i' increment module usage counter -+ -+ 'p' call function pbutton_fct() -+ 't' call function get_thermal_event() -+ 'w1x' call function wbutton_fct_1() with parameter x (0-9) -+ 'w2x' call function wbutton_fct_2() with parameter x (0-9) -+ -+ 'vx' change verbosity level to x (0-9) -+ -+ 'mxyy' set mapping of key name x to key event yy (hex) -+ 'sxx' send key event xx (hex) to input system -+ 'Sxx' simulate acer key press with code xx (hex) -+ -+ 'e0' stop kernel polling -+ 'e1' start kernel polling -+ -+Example: -+ -+1) echo d > /proc/driver/acerhk/debug -+ -+will decrement the usage counter, very useful if a program using the driver -+segfaulted. This way you can still unload the driver. -+ -+2) echo v4 > /proc/driver/acerhk/debug -+ -+will set the verbosity level to maximum. -+ -+Keycodes -+******** -+ -+see doc/keycodes -+Also see http://bernd-wurst.de/linux/tm800.php#mmkeys (only in german) -+ -+If you have one of the newer models with the dritek hardware, use kernel 2.6 -+and get (after enabling it) kernel messages of the form: -+ -+ atkbd.c: Unknown key pressed (translated set 2, code 0xf4 on -+ isa0060/serio0). -+ atkbd.c: Use 'setkeycodes e074 ' to make it known. -+ -+then you should do exactly what your told. In this case you could do -+ -+ setkeycodes e074 158 -+ -+to map the button with scancode e074 (hex) to keycode 158 (decimal). To find -+out the scancodes of the buttons either look into the kernel log or into the -+file MMKEYBD.CFG of the windows launch manager package. There you should find -+lines like this: -+ -+Key 1 = 1,E0,74,E0,F4,F500,P1 -+ ** ** ** -+ -+The important information is marked in the example above. The numbers give the -+scancode produced by the button which's name is given last. -+The keycode you give as parameter to setkeycodes is one out of the header file -+linux/input.h, in the example above the one for KEY_BACK. -+Important note: I received mails from a number of people where setkeycodes -+rejected keycodes above a certain value. This is caused by an outdated version -+of setkeycodes, use a more recent one. -+To ease the setup of keymappings for the newer series I will try to include -+setup scripts for the different notebook series. If I have enough time to -+spare I will perhaps expand the driver itself do to that. -+ -+How does it work? -+***************** -+ -+The driver is based on the windows Me for series 610 driver and -+resembles its structure and functionality. -+Key presses are events, which are stored in a FIFO queue with 31 -+entries. You can access the event count via CMOS nvram, but the access to -+the actual queue (and other functionality like switching the mail led) is -+done through calling a system ROM function. -+Upon loading the driver looks for this function, if it cannot find it, -+loading aborts. -+Newer Dritek Hardware: -+Button handling is done entirely by the EC (embbedded/environment -+controller) which behaves like an extended keyboard controller. My -+driver only enables/disables this extension. -+ -+ -+Credits -+******* -+ -+Leif Jensen, whose driver inspired me to do the probing stuff -+http://www.math.columbia.edu/~jensen/linux/acertm/ -+ -+Thanks to all who contributed patches to this driver or who just tried it out -+on their laptops, without them it wouldn't support anything else but my -+TM 613. -+ -+Contact -+******* -+ -+If you have problems with the driver, please include the following information: -+1. name of your laptop (e.g. Acer TM 4001) -+2. content of /proc/driver/acerhk/info, if available -+3. kernel output after loading the module with verbose=2 -+ -+Email: Olaf Tauber -+ -+The latest version can be found here: -+ -+http://www.informatik.hu-berlin.de/~tauber/acerhk diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch deleted file mode 100644 index 47a71053..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-2.6.36-buildfix.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- linux-2.6.35/3rdparty/acerhk/acerhk.c.orig 2010-10-03 12:50:33.000000000 +0000 -+++ linux-2.6.35/3rdparty/acerhk/acerhk.c 2010-10-03 13:13:22.093868410 +0000 -@@ -2730,7 +2730,7 @@ static void acerhk_proc_cleanup(void) - - /* {{{ file operations */ - --static int acerhk_ioctl( struct inode *inode, struct file *file, -+static long acerhk_ioctl( struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg ) - { - int retval; -@@ -2827,7 +2827,7 @@ static int acerhk_release( struct inode - - static struct file_operations acerhk_fops = { - owner: THIS_MODULE, -- ioctl: acerhk_ioctl, -+ unlocked_ioctl: acerhk_ioctl, - open: acerhk_open, - #ifdef ACERDEBUG - write: acerhk_write, diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch deleted file mode 100644 index 7971c699..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-extra-cflags.patch +++ /dev/null @@ -1,33 +0,0 @@ -CFLAGS use isn't allowed anymore with 2.6.24, we must use EXTRA_CFLAGS -in its place - -Signed-off-by: Herton Ronaldo Krzesinski - ---- linux-2.6.23/3rdparty/acerhk/Makefile.orig 2007-11-08 21:32:43.000000000 -0200 -+++ linux-2.6.23/3rdparty/acerhk/Makefile 2007-11-08 21:33:28.000000000 -0200 -@@ -14,7 +14,7 @@ - CONFIG_ACERHK?=m - obj-$(CONFIG_ACERHK) += acerhk.o - --CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe -+EXTRA_CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe - INCLUDE=-I$(KERNELSRC)/include - - ifeq ($(KERNELMAJOR), 2.6) -@@ -41,13 +41,13 @@ acerhk.ko: $(SOURCE) acerhk.h - $(MAKE) -C $(KERNELSRC) SUBDIRS=$(PWD) modules - - acerhk.o: $(SOURCE) acerhk.h -- $(CC) $(INCLUDE) $(CFLAGS) -DMODVERSIONS -DMODULE -D__KERNEL__ -o $(TARGET) $(SOURCE) -+ $(CC) $(INCLUDE) $(EXTRA_CFLAGS) -DMODVERSIONS -DMODULE -D__KERNEL__ -o $(TARGET) $(SOURCE) - - asm: $(SOURCE) - ifeq ($(KERNELMAJOR), 2.6) -- $(CC) $(INCLUDE) $(INCLUDE)/asm-i386/mach-default $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) -+ $(CC) $(INCLUDE) $(INCLUDE)/asm-i386/mach-default $(EXTRA_CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) - else -- $(CC) $(INCLUDE) $(CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) -+ $(CC) $(INCLUDE) $(EXTRA_CFLAGS) -fverbose-asm -S -DMODVERSIONS -DMODULE -D__KERNEL__ $(SOURCE) - endif - - clean: diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch deleted file mode 100644 index c8d000b5..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-build-with-function-tracer.patch +++ /dev/null @@ -1,22 +0,0 @@ -Fix acerhk build with FUNCTION_TRACER enabled. - -Signed-off-by: Herton Ronaldo Krzesinski ---- - 3rdparty/acerhk/Makefile | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff -p -up linux-2.6.31/3rdparty/acerhk/Makefile.orig linux-2.6.31/3rdparty/acerhk/Makefile ---- linux-2.6.31/3rdparty/acerhk/Makefile.orig 2009-10-02 16:39:06.000000000 -0300 -+++ linux-2.6.31/3rdparty/acerhk/Makefile 2009-10-02 16:45:13.000000000 -0300 -@@ -14,7 +14,10 @@ - CONFIG_ACERHK?=m - obj-$(CONFIG_ACERHK) += acerhk.o - --EXTRA_CFLAGS+=-c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe -+CFLAGS_acerhk.o += -c -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common -pipe -+ifdef CONFIG_FUNCTION_TRACER -+CFLAGS_REMOVE_acerhk.o = -pg -+endif - INCLUDE=-I$(KERNELSRC)/include - - ifeq ($(KERNELMAJOR), 2.6) diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch deleted file mode 100644 index cba26984..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-fix-include.patch +++ /dev/null @@ -1,23 +0,0 @@ - -AUTOCONF_INCLUDED is not defined anymore in 2.6.38, so old include got exposed. - -Fix by removing the offending bits. - -Signed-off-by: Thomas Backlund - - 3rdparty/acerhk/acerhk.c | 4 ---- - 1 file changed, 4 deletions(-) - ---- linux-2.6.38/3rdparty/acerhk/acerhk.c.orig 2011-03-20 13:57:32.000000000 +0200 -+++ linux-2.6.38/3rdparty/acerhk/acerhk.c 2011-03-20 15:00:45.768659568 +0200 -@@ -35,10 +35,6 @@ - * - */ - --#ifndef AUTOCONF_INCLUDED --#include --#endif -- - /* This driver is heavily dependent on the architecture, don't let - * anyone without an X86 machine use it. On laptops with AMD64 - * architecture this driver is only useable in 32 bit mode. diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch deleted file mode 100644 index f0c419ad..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-kbuild.patch +++ /dev/null @@ -1,37 +0,0 @@ -Fix Makefile and add Kconfig for the acerhk driver ---- - 3rdparty/acerhk/Kconfig | 8 8 + 0 - 0 ! - 3rdparty/acerhk/Makefile | 6 3 + 3 - 0 ! - 2 files changed, 11 insertions(+), 3 deletions(-) - -Index: linux-2.6.21/3rdparty/acerhk/Makefile -=================================================================== ---- linux-2.6.21.orig/3rdparty/acerhk/Makefile 2007-05-15 15:34:48.000000000 +0200 -+++ linux-2.6.21/3rdparty/acerhk/Makefile 2007-05-15 15:37:17.000000000 +0200 -@@ -1,10 +1,10 @@ - # change KERNELSRC to the location of your kernel build tree only if - # autodetection does not work - #KERNELSRC=/usr/src/linux --KERNELSRC?=/lib/modules/`uname -r`/build -+#KERNELSRC?=/lib/modules/`uname -r`/build - # Starting with 2.6.18, the kernel version is in utsrelease.h instead of version.h, accomodate both cases --KERNELVERSION=$(shell awk -F\" '/REL/ {print $$2}' $(shell grep -s -l REL $(KERNELSRC)/include/linux/version.h $(KERNELSRC)/include/linux/utsrelease.h)) --KERNELMAJOR=$(shell echo $(KERNELVERSION)|head -c3) -+#KERNELVERSION=$(shell awk -F\" '/REL/ {print $$2}' $(shell grep -s -l REL $(KERNELSRC)/include/linux/version.h $(KERNELSRC)/include/linux/utsrelease.h)) -+#KERNELMAJOR=$(shell echo $(KERNELVERSION)|head -c3) - - # next line is for kernel 2.6, if you integrate the driver in the kernel tree - # /usr/src/linux/drivers/acerhk - or something similar -Index: linux-2.6.21/3rdparty/acerhk/Kconfig -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.21/3rdparty/acerhk/Kconfig 2007-05-15 15:39:30.000000000 +0200 -@@ -0,0 +1,8 @@ -+config ACERHK -+ tristate "Acerhk driver" -+ depends on EXPERIMENTAL && X86 -+ ---help--- -+ This is an experimental acer keyboard driver for -+ acer laptops. If you have a notebook with a ipw2X00 -+ wireless card, it allows you to turn off the rf_kill -+ diff --git a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch b/kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch deleted file mode 100644 index e5821d46..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-acerhk-proc_dir_entry-owner.patch +++ /dev/null @@ -1,67 +0,0 @@ -Updated for owner removal from struct proc_dir_entry (2.6.30) - -Signed-off-by: Herton Ronaldo Krzesinski - ---- - 3rdparty/acerhk/acerhk.c | 7 ------- - 1 file changed, 7 deletions(-) - -diff -p -up linux-2.6.29/3rdparty/acerhk/acerhk.c.orig linux-2.6.29/3rdparty/acerhk/acerhk.c ---- linux-2.6.29/3rdparty/acerhk/acerhk.c.orig 2009-05-22 02:42:44.000000000 -0300 -+++ linux-2.6.29/3rdparty/acerhk/acerhk.c 2009-05-22 02:43:45.000000000 -0300 -@@ -2626,7 +2626,6 @@ static int acerhk_proc_init(void) - printk(KERN_INFO"acerhk: could not create /proc/driver/acerhk\n"); - } - else { -- proc_acer_dir->owner = THIS_MODULE; - /* now create several files, first general info ... */ - entry = create_proc_read_entry("info", - 0444, proc_acer_dir, acerhk_proc_info, NULL); -@@ -2635,7 +2634,6 @@ static int acerhk_proc_init(void) - remove_proc_entry("driver/acerhk", NULL); - retval = 0; - } else { -- entry->owner = THIS_MODULE; - /* ... last pressed key ... */ - entry = create_proc_read_entry("key", - 0444, proc_acer_dir, acerhk_proc_key, NULL); -@@ -2645,7 +2643,6 @@ static int acerhk_proc_init(void) - remove_proc_entry("driver/acerhk", NULL); - retval = 0; - } else { -- entry->owner = THIS_MODULE; - /* ... and led control file */ - entry = create_proc_entry("led", 0222, proc_acer_dir); - if (entry == NULL) { -@@ -2657,7 +2654,6 @@ static int acerhk_proc_init(void) - } - else { - entry->write_proc = acerhk_proc_led; -- entry->owner = THIS_MODULE; - /* ... and wireless led controll file */ - entry = create_proc_entry("wirelessled", 0222, proc_acer_dir); - if (entry == NULL) { -@@ -2670,7 +2666,6 @@ static int acerhk_proc_init(void) - } - else { - entry->write_proc = acerhk_proc_wirelessled; -- entry->owner = THIS_MODULE; - /* ... and bluetooth led controll file */ - entry = create_proc_entry("blueled", 0222, proc_acer_dir); - if (entry == NULL) { -@@ -2683,7 +2678,6 @@ static int acerhk_proc_init(void) - retval = 0; - } else { - entry->write_proc = acerhk_proc_blueled; -- entry->owner = THIS_MODULE; - retval = 1; - #ifdef ACERDEBUG - /* add extra file for debugging purposes */ -@@ -2700,7 +2694,6 @@ static int acerhk_proc_init(void) - } - else { - entry->write_proc = acerhk_proc_debug; -- entry->owner = THIS_MODULE; - retval = 1; - } - #endif diff --git a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch deleted file mode 100644 index 1dcf8fa0..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-kbuild.patch +++ /dev/null @@ -1,47 +0,0 @@ -Fix Makefile and add Kconfig for the aes2501 driver. - -Signed-off-by: Herton Ronaldo Krzesinski - ---- - linux-2.6.23/3rdparty/aes2501/Kconfig | 10 ++++++++++ - linux-2.6.23/3rdparty/aes2501/Makefile | 19 +------------------ - 2 files changed, 11 insertions(+), 18 deletions(-) - -diff -p -up linux-2.6.23/3rdparty/aes2501/Kconfig.orig linux-2.6.23/3rdparty/aes2501/Kconfig ---- linux-2.6.23/3rdparty/aes2501/Kconfig.orig 2008-01-13 23:14:47.000000000 -0200 -+++ linux-2.6.23/3rdparty/aes2501/Kconfig 2008-01-13 23:35:21.000000000 -0200 -@@ -0,0 +1,10 @@ -+config AES2501 -+ tristate "AuthenTec AES2501 Fingerprint Sensor Driver" -+ depends on USB -+ default m -+ ---help--- -+ Say Y here if you have a AuthenTec AES2501 Fingerprint -+ Sensor device. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called aes2501. -diff -p -up linux-2.6.23/3rdparty/aes2501/Makefile.orig linux-2.6.23/3rdparty/aes2501/Makefile ---- linux-2.6.23/3rdparty/aes2501/Makefile.orig 2008-01-13 23:14:36.000000000 -0200 -+++ linux-2.6.23/3rdparty/aes2501/Makefile 2008-01-13 23:21:33.000000000 -0200 -@@ -1,19 +1,2 @@ -- --obj-m := aes2501.o -- --KERNELDIR ?= /lib/modules/$(shell uname -r)/build -- --PWD := $(shell pwd) -- --all: usertest -- $(MAKE) -C $(KERNELDIR) M=$(PWD) -- --usertest: -- gcc -I. -o usertest usertest.c -- --clean: -- rm -f *.o *~ aes2501.ko aes2501.mod.c Module.symvers usertest -- rm -rf .tmp_versions -- rm -f .*.cmd -- -+obj-$(CONFIG_AES2501) := aes2501.o - diff --git a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch deleted file mode 100644 index a5f95e79..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-r19.patch +++ /dev/null @@ -1,2356 +0,0 @@ - 3rdparty/aes2501/ChangeLog | 95 ++ - 3rdparty/aes2501/Makefile | 19 - 3rdparty/aes2501/aes2501.c | 1629 ++++++++++++++++++++++++++++++++++++++++ - 3rdparty/aes2501/aes2501.h | 40 - 3rdparty/aes2501/aes2501_regs.h | 464 +++++++++++ - 3rdparty/aes2501/usertest.c | 78 + - 6 files changed, 2325 insertions(+) -diff -Nurp linux-2.6.37/3rdparty/aes2501/aes2501.c linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.c ---- linux-2.6.37/3rdparty/aes2501/aes2501.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.c 2008-01-14 03:07:30.000000000 +0200 -@@ -0,0 +1,1629 @@ -+ -+/* -+ * aes2501.c -- AuthenTec AES2501 Fingerprint Sensor Driver for Linux -+ * -+ * Maintainer: Cyrille Bagard -+ * -+ * Copyright (C) 2007 Cyrille Bagard -+ * -+ * This file is part of the AES2501 driver. -+ * -+ * the AES2501 driver is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * the AES2501 driver 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 the AES2501 driver; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ * -+ */ -+ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+#include "aes2501.h" -+#include "aes2501_regs.h" -+ -+ -+static int major = 0; -+ -+module_param(major, int, 0); -+MODULE_PARM_DESC(major, "major number"); -+ -+ -+//#define DEBUG -+ -+ -+#define PRINTK_INFO(fmt, arg...) printk(KERN_INFO "aes2501: " fmt, ## arg) -+ -+#define PRINTK_CRIT(fmt, arg...) printk(KERN_CRIT "aes2501: " fmt, ## arg) -+ -+#define PRINTK_DBG(fmt, arg...) printk(KERN_DEBUG "aes2501: " fmt, ## arg) -+ -+ -+static struct workqueue_struct *comm_queue; -+ -+ -+static char *finger_print = NULL; -+static size_t finger_print_len; -+ -+ -+ -+static DECLARE_WAIT_QUEUE_HEAD(wq); -+static int flag = 0; -+ -+ -+ -+struct usb_aes2501 *_dev; -+ -+ -+ -+ -+ -+ -+struct aes2501 { -+ -+ struct usb_device *udev; -+ -+ int temp; -+ -+ unsigned char *int_in_buffer; -+ struct urb *int_in_urb; -+ -+}; -+ -+ -+/* -+#define VENDOR_ID 0x08f7 -+#define PRODUCT_ID 0x0002 -+*/ -+ -+#define VENDOR_ID 0x08ff -+#define PRODUCT_ID 0x2580 -+ -+ -+/* Table of devices that work with this driver */ -+ -+static struct usb_device_id id_table [] = { -+ { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(usb, id_table); -+ -+ -+/* Get a minor range for your devices from the usb maintainer */ -+#define USB_AES2501_MINOR_BASE 192 -+ -+ -+ -+#define WRITES_IN_FLIGHT 8 /* ??? */ -+ -+ -+ -+enum AES2501_Status { -+ -+ AESS_INIT_DONE, -+ AESS_IS_SCANNING, -+ -+ AESS_COUNT -+ -+}; -+ -+ -+#define BULK_OUT_BUFFER_SIZE 32 -+ -+ -+/* Read a register value from a dump sent by the device. */ -+static int read_register_value(const uint8_t *, Aes2501Registers); -+ -+/* Tells if a finger motion is still detected. */ -+static int sum_histogram_values(const uint8_t *, uint8_t); -+ -+ -+/* Structure to hold all device specific stuff */ -+struct usb_aes2501 { -+ -+ struct usb_device *udev; /* the usb device for this device */ -+ struct usb_interface *interface; /* the interface for this device */ -+ struct semaphore limit_sem; /* limiting the number of writes in progress */ -+ unsigned char *bulk_in_buffer; /* the buffer to receive data */ -+ size_t bulk_in_size; /* the size of the receive buffer */ -+ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ -+ uint8_t bulk_out_buffer[BULK_OUT_BUFFER_SIZE]; /* the buffer to send data (32 / 8 = 4) */ -+ size_t bulk_out_buffer_used; /* the quantity of buffered data to send */ -+ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ -+ struct kref kref; -+ struct mutex io_mutex; /* synchronize I/O with disconnect */ -+ -+ DECLARE_BITMAP(status, AESS_COUNT); /* Information about what has been set / is running */ -+ -+ struct work_struct scan_work; -+ uint8_t stop_scan; /* Flag to stop finger detecting during module removal */ -+}; -+ -+ -+#define to_aes2501_dev(r) container_of(r, struct usb_aes2501, kref) -+ -+static struct usb_driver aes2501_driver; -+ -+static void aes2501_delete(struct kref *kref) -+{ -+ struct usb_aes2501 *dev; -+ -+ dev = to_aes2501_dev(kref); -+ -+ usb_put_dev(dev->udev); -+ kfree(dev->bulk_in_buffer); -+ kfree(dev); -+ -+} -+ -+ -+static int aes2501_open(struct inode *inode, struct file *file); -+/* -+struct usb_aes2501 *_dev; -+static int aes2501_open(struct inode *inode, struct file *file) -+{ -+ printk(KERN_INFO "Plop !\n"); -+ -+ -+ -+ return 0; -+} -+*/ -+ -+static ssize_t aes2501_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -+{ -+ int read_len; -+ -+ //printk(KERN_INFO "aes2501_read ! got %d -> need (%d ; %d)\n", finger_print_len, *ppos, count); -+ -+ if (flag == 0) -+ printk(KERN_INFO "Process %i (%s) is going to sleep\n", current->pid, current->comm); -+ -+ wait_event_interruptible(wq, flag == 1); -+ -+ if (signal_pending(current)) -+ return -ERESTARTSYS; -+ -+ if (finger_print_len == 0) -+ return -ENODATA; -+ -+ if (unlikely((*ppos < 0) || (loff_t)(*ppos + count) < 0)) -+ return -EINVAL; -+ -+ if (*ppos + count > finger_print_len) -+ read_len = finger_print_len - *ppos; -+ else read_len = count; -+ -+ if (read_len > 0) -+ { -+ if (!access_ok(VERIFY_WRITE, buffer, read_len)) -+ return -EFAULT; -+ -+ read_len -= copy_to_user(buffer, finger_print + *ppos, read_len); -+ *ppos += read_len; -+ -+ } -+ -+ /* !!!! */ -+ else flag = 0; -+ -+ return read_len; -+ -+} -+ -+ -+ -+static int detect_finger_on_aes2501(struct usb_aes2501 *dev); -+ -+static void do_scanning(struct work_struct *work); -+ -+ -+// ioctl - I/O control -+static int aes2501_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ struct usb_aes2501 *dev; -+ int retval; -+ -+ dev = _dev; -+ -+ -+ printk(KERN_INFO "Get IOCtl :: cmd = %d vs %d\n", cmd, (AES2501_IOC_TEST)); -+ -+ switch (cmd) { -+ -+ case AES2501_IOC_TEST: -+ -+#if defined(_arch_um__) || !(LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) -+ INIT_WORK(&dev->scan_work, do_scanning); -+#else -+ INIT_WORK(&dev->scan_work, do_scanning, NULL); -+#endif -+ -+ retval = queue_work(comm_queue, &dev->scan_work); -+ -+ -+ } -+ -+ -+ -+ return 0; -+ -+ -+#if 0 -+ int retval = 0; -+ switch ( cmd ) { -+ case CASE1:/* for writing data to arg */ -+ if (copy_from_user(&data, (int *)arg, sizeof(int))) -+ return -EFAULT; -+ break; -+ case CASE2:/* for reading data from arg */ -+ if (copy_to_user((int *)arg, &data, sizeof(int))) -+ return -EFAULT; -+ break; -+ default: -+ retval = -EINVAL; -+ } -+ return retval; -+#endif -+} -+ -+ -+ -+static const struct file_operations aes2501_fops = { -+ .owner = THIS_MODULE, -+ .read = aes2501_read,/* -+ .write = aes2501_write,*/ -+ .open = aes2501_open,/* -+ .release = skel_release,*/ -+ .ioctl = aes2501_ioctl -+}; -+ -+ -+ -+/* -+static int send_data_to_aes2501(struct usb_aes2501 *dev, unsigned char *buffer, int len) -+{ -+#ifdef DEBUG -+ int i; -+#endif -+ int bytes_written; -+ int ret; -+ -+#ifdef DEBUG -+ printk(KERN_INFO "\n:: OUT :: len=%d\n", len); -+ -+ for (i = 0; i < len; i++) -+ { -+ if (i > 0 && i % 16 == 0) -+ printk("\n"); -+ if (i % 16 == 0) -+ printk(KERN_INFO " %05x: ", i); -+ -+ if (i % 8 == 0) -+ printk(" "); -+ -+ printk("%02x ", buffer[i]); -+ -+ } -+ -+ printk("\n"); -+#endif -+ -+ ret = usb_bulk_msg(dev->udev, -+ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), -+ buffer, -+ len, -+ &bytes_written, 4000); -+ -+ if (ret < 0) -+ PRINTK_CRIT("error while sending data: %d\n", ret); -+ -+ if (bytes_written != len) -+ printk(KERN_INFO "did not write all ! %d vs %d\n", bytes_written, len); -+ -+ return ret; -+ -+} -+*/ -+ -+static int flush_aes2501_bulk_out(struct usb_aes2501 *dev) -+{ -+ int ret; -+ int bytes_written; -+ -+ ret = 0; -+ -+ if (dev->bulk_out_buffer_used > 0) { -+ -+ ret = usb_bulk_msg(dev->udev, -+ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), -+ dev->bulk_out_buffer, -+ dev->bulk_out_buffer_used, -+ &bytes_written, 4000); -+ -+ if (ret < 0) -+ PRINTK_CRIT("error while sending data: %d\n", ret); -+ -+ else if (bytes_written != dev->bulk_out_buffer_used) -+ { -+ printk(KERN_INFO "did not write all ! %d vs %d\n", bytes_written, dev->bulk_out_buffer_used); -+ /*ret = ... */ -+ } -+ -+ dev->bulk_out_buffer_used = 0; -+ -+ } -+ -+ return ret; -+ -+} -+ -+ -+static int write_aes2501_register(struct usb_aes2501 *dev, uint8_t reg, uint8_t data) -+{ -+ int ret; -+ -+ if (dev->bulk_out_buffer_used == BULK_OUT_BUFFER_SIZE) -+ ret = flush_aes2501_bulk_out(dev); -+ else -+ ret = 0; -+ -+ dev->bulk_out_buffer[dev->bulk_out_buffer_used++] = reg; -+ dev->bulk_out_buffer[dev->bulk_out_buffer_used++] = data; -+ -+ return ret; -+ -+} -+ -+ -+static int recv_data_to_aes2501(struct usb_aes2501 *dev, unsigned char *buffer, int len) -+{ -+ int bytes_read; -+ int ret; -+#ifdef DEBUG -+ int i; -+#endif -+ -+ ret = usb_bulk_msg(dev->udev, -+ usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), -+ buffer, -+ len, -+ &bytes_read, 4000); -+ -+ if (ret < 0) -+ PRINTK_CRIT("error while sending data: %d\n", ret); -+ -+ if (bytes_read != len) -+ printk(KERN_INFO "did not read all ! %d vs %d\n", bytes_read, len); -+ -+#ifdef DEBUG -+ if (ret == 0) -+ { -+ printk(KERN_INFO "\n:: IN :: len=%d\n", len); -+ -+ for (i = 0; i < len; i++) -+ { -+ if (i > 0 && i % 16 == 0) -+ printk("\n"); -+ if (i % 16 == 0) -+ printk(KERN_INFO " %05x: ", i); -+ -+ if (i % 8 == 0) -+ printk(" "); -+ -+ printk("%02x ", buffer[i]); -+ -+ } -+ -+ printk("\n"); -+ -+ } -+#endif -+ -+ return ret; -+ -+} -+ -+ -+ -+static int standby_aes2501(struct usb_aes2501 *dev) -+{ -+ -+ //unsigned char cmd[] = "\xac\x01\xad\x1a\x81\x02"; -+ -+ -+ unsigned char buffer[4]; -+ -+ -+ -+ -+ printk(KERN_INFO ">> Standby aes2501 !\n"); -+ -+ -+ //msleep(800); -+ -+ write_aes2501_register(dev, AES2501_REG_TREGC, AES2501_TREGC_ENABLE); -+ write_aes2501_register(dev, AES2501_REG_TREGD, 0x1a); -+ -+ flush_aes2501_bulk_out(dev); -+ -+ /** -+ * TODO: -+ * - lire ici les deux octets. -+ * - voir pour inverser les commandes. -+ */ -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL2, 0x02/* AES2501_CTRL2_READ_REGS ??? */); -+ flush_aes2501_bulk_out(dev); -+ -+ //send_data_to_aes2501(dev, cmd, 6); -+ recv_data_to_aes2501(dev, buffer, 2); -+ -+ -+ -+ printk(KERN_INFO " %02x %02x\n", buffer[0], buffer[1]); -+ -+ -+ if ((buffer[1] & AES2501_STAT_SCAN) == buffer[1]) -+ { -+ PRINTK_DBG("scan state: "); -+ -+ switch (buffer[1]) -+ { -+ case STATE_WAITING_FOR_FINGER: -+ printk("'Waiting for finger'\n"); -+ break; -+ case STATE_FINGER_SETTLING_DELAY: -+ printk("'In Finger settling delay'\n"); -+ break; -+ case STATE_POWER_UP_DELAY: -+ printk("'In power up delay'\n"); -+ break; -+ case STATE_WAITING_TO_START_SCAN: -+ printk("'Waiting to start image scan'\n"); -+ break; -+ case STATE_PRELOADING_SUBARRAY_0: -+ printk("'Pre-loading subarray 0'\n"); -+ break; -+ case STATE_SETUP_FOR_ROW_ADVANCE: -+ printk("'Setup for row advance'\n"); -+ break; -+ case STATE_WAITING_FOR_ROW_ADVANCE: -+ printk("'Waiting for row advance'\n"); -+ break; -+ case STATE_PRELOADING_COL_0: -+ printk("'Pre-loading column 0'\n"); -+ break; -+ case STATE_SETUP_FOR_COL_ADVANCE: -+ printk("'Setup for column advance'\n"); -+ break; -+ case STATE_WAITING_FOR_COL_ADVANCE: -+ printk("'Waiting for column advance'\n"); -+ break; -+ case STATE_WAITING_FOR_SCAN_START: -+ printk("'Waiting for scan start'\n"); -+ break; -+ case STATE_WAITING_FOR_SCAN_END: -+ printk("'Waiting for scan end'\n"); -+ break; -+ case STATE_WAITING_FOR_ROW_SETUP: -+ printk("'Waiting for row setup'\n"); -+ break; -+ case STATE_WAITING_FOR_COL_TIME: -+ printk("'Waiting for one column time (depends on scan rate)'\n"); -+ break; -+ case STATE_WAITING_FOR_QUEUED_DATA: -+ printk("'Waiting for queued data transmission to be completed'\n"); -+ break; -+ case STATE_WAIT_FOR_128_US: -+ printk("'Wait for 128 us'\n"); -+ break; -+ default: -+ printk("none (?!)\n"); -+ break; -+ -+ } -+ -+ } -+ -+ if (buffer[1] & AES2501_STAT_ERROR) -+ PRINTK_DBG("got serial interface framing error\n"); -+ -+ if (buffer[1] & AES2501_STAT_PAUSED) -+ PRINTK_DBG("scan was paused due to input buffer full\n"); -+ -+ if (buffer[1] & AES2501_STAT_RESET) -+ PRINTK_DBG("master reset input has been asserted\n"); -+ -+ -+ -+ -+ return 0; -+ -+ -+} -+ -+ -+ -+ -+static int setup_aes2501(struct usb_aes2501 *dev) -+{ -+ //unsigned char patch_msg[] = "\x80\x01"; -+ -+ -+ //unsigned char cmd[] = "\x80\x01\x81\x02"; -+ -+ //unsigned char cmd_00[] = "\xb0\x27"; -+ //unsigned char cmd_01[] = "\x80\x01\x82\x40"; -+ //unsigned char cmd_02[] = "\xff\x00"; -+ //unsigned char cmd_03[] = "\x80\x01\x82\x40\x83\x00\x88\x02\x89\x10\x8a\x05\x8c\x00\x8e\x13\x91\x44\x92\x34\x95\x16\x96\x16\x97\x18\xa1\x70\xa2\x02\xa7\x00\xac\x01\xad\x1a\x80\x04\x81\x04\xb4\x00";/*42*/ -+ //unsigned char cmd_04[] = "\x80\x01\x82\x40"; -+ -+ //unsigned char cmd2[] = "\x80\x01\xa8\x41\x82\x42\x83\x53\x80\x04\x81\x02";/*12*/ -+ -+ //unsigned char cmd3_00[] = "\xff\x00"; -+ //unsigned char cmd3_01[] = "\x80\x01\xa8\x41\x82\x42\x83\x53\x80\x04\x81\x02";/*12*/ -+ -+ //unsigned char cmd4[] = "\x80\x01\x82\x40\xb0\x27\x94\x0a\x80\x04\x83\x45\xa8\x41";/*14*/ -+ -+ //unsigned char cmd5_00[] = "\xb0\x27"; -+ //unsigned char cmd5_01[] = "\x80\x01\x82\x40"; -+ //unsigned char cmd5_02[] = "\xff\x00"; -+ //unsigned char cmd5_03[] = "\x80\x02"; -+ //unsigned char cmd5_04[] = "\x81\x02"; -+ -+ -+ int i; -+ -+ unsigned char buffer[128]; -+ -+ -+ /* To be sure the device will respond */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ //send_data_to_aes2501(dev, patch_msg, 2); -+ flush_aes2501_bulk_out(dev);/* ... */ -+ -+ printk(KERN_INFO ">> Setting up aes2501 !\n"); -+ -+ /* Part 1 */ -+ -+ printk(KERN_INFO " -- part 1 --\n"); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); -+ flush_aes2501_bulk_out(dev); -+ -+ //send_data_to_aes2501(dev, cmd, 4); -+ recv_data_to_aes2501(dev, buffer, 126); -+ -+ -+ -+ //send_data_to_aes2501(dev, cmd_00, 2); -+ -+ write_aes2501_register(dev, 0xb0, 0x27);/* Reserved */ -+ -+ -+ -+ //send_data_to_aes2501(dev, cmd_01, 4); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ -+ for (i = 0; i <= 10; i++) -+ { -+ //msleep(200); -+ //send_data_to_aes2501(dev, cmd_02, 2); -+ write_aes2501_register(dev, 0xff, 0x00);/* Reserved */ -+ } -+ -+ //msleep(100); -+ -+ -+ //send_data_to_aes2501(dev, cmd_03, 42); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_DETCTRL, AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS); -+ write_aes2501_register(dev, AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US); -+ write_aes2501_register(dev, AES2501_REG_MEASDRV, AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE); -+ write_aes2501_register(dev, AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M); -+ write_aes2501_register(dev, AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE);/* Default */ -+ write_aes2501_register(dev, AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE); -+ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X); -+ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x44); -+ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x34); -+ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x16); -+ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x16); -+ write_aes2501_register(dev, AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_TREG1, 0x70);/* Reserved + 0xXX */ -+ write_aes2501_register(dev, 0xa2, 0x02);/* Reserved */ -+ write_aes2501_register(dev, 0xa7, 0x00);/* Reserved */ -+ write_aes2501_register(dev, AES2501_REG_TREGC, AES2501_TREGC_ENABLE); -+ write_aes2501_register(dev, AES2501_REG_TREGD, 0x1a); -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); -+ write_aes2501_register(dev, AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE); -+ -+ -+ flush_aes2501_bulk_out(dev); -+ -+ recv_data_to_aes2501(dev, buffer, 20); -+ -+ -+ -+ //send_data_to_aes2501(dev, cmd_04, 4); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ -+ /* Part 2 */ -+ -+ printk(KERN_INFO " -- part 2 --\n"); -+ -+ //msleep(100); -+ -+ //send_data_to_aes2501(dev, cmd2, 12); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_AUTOCALOFFSET, 0x41); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x42);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_DETCTRL, 0x53);/* Cumul ??? */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); -+ -+ -+ -+ -+ flush_aes2501_bulk_out(dev); -+ -+ -+ recv_data_to_aes2501(dev, buffer, 126); -+ -+ /* Part 3 */ -+ -+ printk(KERN_INFO " -- part 3 --\n"); -+ -+ i = 0; -+ -+ printk(KERN_INFO " reg 0xaf = 0x%x\n", buffer[0x5f]); -+ while (buffer[0x5f] == 0x6b) -+ { -+ //msleep(200); -+ -+ -+ //send_data_to_aes2501(dev, cmd3_00, 2); -+ write_aes2501_register(dev, 0xff, 0x00);/* Reserved */ -+ -+ //msleep(80); -+ -+ //send_data_to_aes2501(dev, cmd3_01, 12); -+ -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_AUTOCALOFFSET, 0x41); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x42);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_DETCTRL, 0x53);/* Cumul ??? */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); -+ -+ flush_aes2501_bulk_out(dev); -+ -+ -+ -+ -+ recv_data_to_aes2501(dev, buffer, 126); -+ -+ printk(KERN_INFO " +reg 0xaf = 0x%x\n", buffer[0x5f]); -+ -+ if (++i == 13) break; -+ -+ } -+ -+ -+ -+ -+ -+ /* Part 4 */ -+ -+ printk(KERN_INFO " -- part 4 --\n"); -+ -+ //send_data_to_aes2501(dev, cmd4, 14); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ write_aes2501_register(dev, 0xb0, 0x27);/* Reserved */ -+ write_aes2501_register(dev, AES2501_REG_ENDROW, 0x0a); -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); -+ write_aes2501_register(dev, AES2501_REG_DETCTRL, 0x45);/* ???? */ -+ write_aes2501_register(dev, AES2501_REG_AUTOCALOFFSET, 0x41); -+ -+ -+ -+ -+ -+ -+ /* ... */ -+ -+ -+/* -+ for (i = 0; i < 126; i += 10) -+ { -+ printk(KERN_INFO " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", -+ buffer[i + 0], buffer[i + 1], buffer[i + 2], buffer[i + 3], buffer[i + 4], -+ buffer[i + 5], buffer[i + 6], buffer[i + 7], buffer[i + 8], buffer[i + 9]); -+ -+ if (i % 20 == 0) -+ printk(KERN_INFO "\n "); -+ -+ } -+ -+ printk(KERN_INFO "\n"); -+*/ -+ -+ -+ -+ /* Part 5 */ -+ -+ printk(KERN_INFO " -- part 5 --\n"); -+ -+ //send_data_to_aes2501(dev, cmd5_00, 2); -+ -+ write_aes2501_register(dev, 0xb0, 0x27);/* Reserved */ -+ -+ //send_data_to_aes2501(dev, cmd5_01, 4); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ -+ //msleep(200); -+ -+ -+ //send_data_to_aes2501(dev, cmd5_02, 2); -+ //send_data_to_aes2501(dev, cmd5_01, 4); -+ //send_data_to_aes2501(dev, cmd5_01, 4); -+ -+ write_aes2501_register(dev, 0xff, 0x00);/* Reserved */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ -+ -+ -+ //msleep(50); -+ -+ //send_data_to_aes2501(dev, cmd5_01, 4); -+ //send_data_to_aes2501(dev, cmd5_01, 4); -+ //send_data_to_aes2501(dev, cmd5_01, 4); -+ //send_data_to_aes2501(dev, cmd5_03, 2); -+ //send_data_to_aes2501(dev, cmd5_03, 2); -+ //send_data_to_aes2501(dev, cmd5_04, 2); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* ??? */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET); -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); -+ flush_aes2501_bulk_out(dev); -+ -+ -+ recv_data_to_aes2501(dev, buffer, 126); -+ -+ -+ -+ -+ -+ return 0; -+ -+} -+ -+static int detect_finger_on_aes2501(struct usb_aes2501 *dev) -+{ -+ //uint8_t cmd1[] = "\x80\x01\x82\x40"; -+ //uint8_t cmd2[] = "\x80\x01\x82\x40\x83\x00\x88\x02\x89\x10\x8a\x05\x8c\x00\x8e\x13\x91\x44\x92\x34\x95\x16\x96\x16\x97\x18\xa1\x70\xa2\x02\xa7\x00\xac\x01\xad\x1a\x80\x04\x81\x04\xb4\x00";/*42*/ -+ -+ -+ unsigned char buffer[22]; -+ unsigned i, sum; -+ -+ //send_data_to_aes2501(dev, cmd1, 4); -+ -+ //msleep(30); -+ -+ -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* Reserved */ -+ write_aes2501_register(dev, AES2501_REG_DETCTRL, AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS); -+ write_aes2501_register(dev, AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US);/* not the faster ??? */ -+ write_aes2501_register(dev, AES2501_REG_MEASDRV, AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE); -+ write_aes2501_register(dev, AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M); -+ write_aes2501_register(dev, AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE);/* Default */ -+ write_aes2501_register(dev, AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE); -+ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X); -+ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x44); -+ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x34); -+ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x16); -+ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x16); -+ write_aes2501_register(dev, AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08); -+ write_aes2501_register(dev, AES2501_REG_TREG1, 0x70);/* Reserved + 0xXX */ -+ write_aes2501_register(dev, 0xa2, 0x02);/* Reserved */ -+ write_aes2501_register(dev, 0xa7, 0x00);/* Reserved */ -+ write_aes2501_register(dev, AES2501_REG_TREGC, AES2501_TREGC_ENABLE); -+ write_aes2501_register(dev, AES2501_REG_TREGD, 0x1a); -+ -+ flush_aes2501_bulk_out(dev); -+ -+ /** -+ * TODO: -+ * - lire ici les deux octets. -+ * - voir pour inverser les commandes. -+ */ -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); -+ write_aes2501_register(dev, AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE); -+ -+ flush_aes2501_bulk_out(dev); -+ -+ -+ //send_data_to_aes2501(dev, cmd2, 42); -+ -+ recv_data_to_aes2501(dev, buffer, 20); -+ -+ -+ /* One column is returned here but I don't know which one. -+ Maybe an average over the whole sensor area. */ -+ sum = 0; -+ for (i = 1; i != 9; i++) { -+ sum += (buffer[i] & 0xf) + (buffer[i] >> 4); -+ } -+ -+ -+ //printk(KERN_INFO "===finger=== sum = %d vs %d\n", sum, 20); -+ -+ -+ /* -+ return (sum > aes->fingerTh1); -+ */ -+ -+ return sum > 20; -+ -+} -+ -+ -+static unsigned read_fingerprint_on_aes2501(struct usb_aes2501 *dev, void *raw_, unsigned maxstrip) -+{ -+ //unsigned char patch_msg[] = "\x80\x01"; -+ -+ /* 8e xx = set gain */ -+ //uint8_t cmd1_00[] = "0x80\x01\x82\x40"; -+ //uint8_t cmd1_01[] = "0x80\x01\x82\x40\x83\x00\x88\x02\x8c\x7c\x89\x10\x8d\x24\x9b\x00\x9c\x6c\x9d\x09\x9e\x54\x9f\x78\xa2\x02\xa7\x00\xb6\x26\xb7\x1a\x80\x04\x98\x23\x95\x10\x96\x1f\x8e\x00\x91\x70\x92\x20\x81\x04\xb4\x00";/*50*/ -+ -+ //uint8_t cmd2[] = "\x98\x23\x95\x10\x96\x1f\x8e\x03\x91\x70\x92\x20\x81\x04";/*14*/ -+ -+ //uint8_t cmd3[] = "\x98\x22\x95\x00\x96\x2f\x8e\x03\x91\x5b\x92\x20\x81\x04";/*14*/ -+ -+ uint8_t buf[1705]; -+ uint8_t *raw = (uint8_t *) raw_; -+ unsigned nstrips; -+ -+ int threshold; -+ int sum; -+ -+ -+ //send_data_to_aes2501(dev, patch_msg, 2); -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ flush_aes2501_bulk_out(dev); -+ -+ -+ -+ //send_data_to_aes2501(dev, cmd1_00, 4); -+ -+ -+ //send_data_to_aes2501(dev, cmd1_01, 50); -+ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET); -+ write_aes2501_register(dev, AES2501_REG_EXCITCTRL, 0x40);/* Reserved */ -+ write_aes2501_register(dev, AES2501_REG_DETCTRL, AES2501_DETCTRL_SDELAY_31_MS | AES2501_DETCTRL_DRATE_CONTINUOUS); -+ write_aes2501_register(dev, AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US); -+ write_aes2501_register(dev, AES2501_REG_DEMODPHASE2, 0x7c);/* ... */ -+ write_aes2501_register(dev, AES2501_REG_MEASDRV, AES2501_MEASDRV_MEASURE_SQUARE | AES2501_MEASDRV_MDRIVE_0_325); -+ write_aes2501_register(dev, AES2501_REG_DEMODPHASE1, 0x24);/* ... */ -+ write_aes2501_register(dev, AES2501_REG_CHWORD1, 0x00);/* Challenge Word */ -+ write_aes2501_register(dev, AES2501_REG_CHWORD2, 0x6c);/* Challenge Word */ -+ write_aes2501_register(dev, AES2501_REG_CHWORD3, 0x09);/* Challenge Word */ -+ write_aes2501_register(dev, AES2501_REG_CHWORD4, 0x54);/* Challenge Word */ -+ write_aes2501_register(dev, AES2501_REG_CHWORD5, 0x78);/* Challenge Word */ -+ write_aes2501_register(dev, 0xa2, 0x02);/* Reserved */ -+ write_aes2501_register(dev, 0xa7, 0x00);/* Reserved */ -+ write_aes2501_register(dev, 0xb6, 0x26);/* Reserved */ -+ write_aes2501_register(dev, 0xb7, 0x1a);/* Reserved */ -+ write_aes2501_register(dev, AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE); -+ write_aes2501_register(dev, AES2501_REG_IMAGCTRL, AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE | AES2501_IMAGCTRL_IMG_DATA_DISABLE); -+ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x10); -+ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x1f); -+ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_2X | AES2501_CHANGAIN_STAGE2_2X); -+ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x70); -+ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x20); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); -+ write_aes2501_register(dev, AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE); -+ -+ ///////recv_data_to_aes2501(dev, buf, 159); -+ /* TODO: calibration e.g setting gain (0x8e xx) */ -+ -+ -+ -+ -+ //send_data_to_aes2501(dev, cmd2, 14); -+ -+ -+ -+ write_aes2501_register(dev, AES2501_REG_IMAGCTRL, AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE | AES2501_IMAGCTRL_IMG_DATA_DISABLE); -+ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x10); -+ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x1f); -+ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X); -+ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x70); -+ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x20); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); -+ -+ flush_aes2501_bulk_out(dev); -+ -+ -+ recv_data_to_aes2501(dev, buf, 159); -+ -+ nstrips = 0; -+ do { -+ printk(KERN_INFO "-start-\n"); -+ /* Timing in this loop is critical. It decides how fast you can move your finger. -+ If one loop takes tl second, the maximum speed is: -+ (16/500 * 25.4) / tl [mm per sec] -+ */ -+ //send_data_to_aes2501(dev, cmd3, 14); -+ -+ write_aes2501_register(dev, AES2501_REG_IMAGCTRL, AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE); -+ write_aes2501_register(dev, AES2501_REG_STRTCOL, 0x00); -+ write_aes2501_register(dev, AES2501_REG_ENDCOL, 0x2f); -+ write_aes2501_register(dev, AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X); -+ write_aes2501_register(dev, AES2501_REG_ADREFHI, 0x5b); -+ write_aes2501_register(dev, AES2501_REG_ADREFLO, 0x20); -+ write_aes2501_register(dev, AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT); -+ -+ flush_aes2501_bulk_out(dev); -+ -+ -+ -+ recv_data_to_aes2501(dev, buf, 1705); -+ memcpy(raw, buf+1, 192*8); -+ raw += 192*8; -+ -+ printk(KERN_INFO "-end of copy-\n"); -+ -+ -+ threshold = read_register_value((buf + 1 + 192*8 + 1 + 16*2 + 1 + 8), _AES2501_REG_DATFMT); -+ /*if (threshold < 0) -+ return threshold;*/ -+ -+ threshold &= 0x0f; -+ -+ sum = sum_histogram_values((buf + 1 + 192*8), threshold); -+ /*if (threshold < 0) -+ return threshold;*/ -+ -+ nstrips++; -+ printk(KERN_INFO "-end- %d vs %d\n", nstrips, maxstrip); -+ } while (sum > 0 && nstrips < maxstrip); -+ printk(KERN_INFO "nstrips = %u\n", nstrips); -+ if (nstrips == maxstrip) -+ printk(KERN_INFO "nstrips == %u, swiping the finger too slowly?\n", maxstrip); -+ return nstrips; -+} -+ -+ -+ -+/** -+ * Read a register value from a dump sent by the device. -+ * @data: list of couples . -+ * @target: index of the register to process. -+ * @return: -EILSEQ,-EINVAL if error, value > 0 if a finger is here. -+ */ -+int read_register_value(const uint8_t *data, Aes2501Registers target) -+{ -+ int result; -+ uint8_t offset; -+ -+ if (*data == FIRST_AES2501_REG) -+ { -+ offset = target; -+ -+ if (!(FIRST_AES2501_REG <= offset && offset <= LAST_AES2501_REG)) -+ result = -EINVAL; -+ -+ else -+ { -+ offset -= FIRST_AES2501_REG; -+ offset *= 2; -+ -+ result = data[++offset]; -+ -+ } -+ -+ } -+ else result = -EILSEQ; -+ -+ return result; -+ -+} -+ -+ -+/** -+ * Tells if a finger motion is still detected. -+ * @data: start point of data to handle, preceded by 0xde. -+ * @threshold: index of values set to 1 by the device. -+ * @return: -EILSEQ,-EINVAL if error, value > 0 if a finger is here. -+ * -+ * Histograms always are a 16-uint16_t long message, where -+ * histogram[i] = number of pixels of value i. -+ */ -+int sum_histogram_values(const uint8_t *data, uint8_t threshold) -+{ -+ int result; -+ uint16_t *histogram; -+ uint8_t i; -+ -+ if (*data == 0xde) -+ { -+ if (threshold > 0x0f) -+ result = -EINVAL; -+ -+ else -+ { -+ result = 0; -+ histogram = (uint16_t *)(data + 1); -+ -+ for (i = threshold; i < 16; i++) -+ result += histogram[i]; -+ -+ } -+ -+ } -+ else result = -EILSEQ; -+ -+ return result; -+ -+} -+ -+ -+ -+/********************************************************** -+ ** Image processing -+ **********************************************************/ -+ -+/* This function finds overlapping parts of two frames -+ * Based on calculating normalized hamming distance -+ * between two frames -+ */ -+ -+static unsigned find_overlap(const uint8_t *first_frame, -+ const uint8_t *second_frame, -+ uint32_t frame_height, -+ uint32_t frame_width, -+ uint32_t *min_error) -+{ -+ uint32_t dy, i; -+ uint32_t error, not_overlapped_height = 0; -+ *min_error = 255 * frame_height * frame_width; -+ for (dy = 0; dy < frame_height; dy++) { -+ /* Calculating difference (error) between parts of frames */ -+ error = 0; -+ for (i = 0; i < frame_width * (frame_height - dy); i++) { -+ /* Using ? operator to avoid abs function */ -+ error += first_frame[i] > second_frame[i] ? -+ (first_frame[i] - second_frame[i]) : -+ (second_frame[i] - first_frame[i]); -+ } -+ -+ /* Normalizing error */ -+ error *= 15; -+ error /= i; -+ if (error < *min_error) { -+ *min_error = error; -+ not_overlapped_height = dy; -+ } -+ first_frame += frame_width; -+ } -+ -+ return not_overlapped_height; -+} -+ -+ -+/* This function assembles frames to single image, returns image height -+ * TODO: add Doxygen comments -+ */ -+static unsigned assemble(const uint8_t *input, /* Raw data received from device */ -+ uint8_t *output, /* Output buffer */ -+ uint32_t frame_height, /* Height of each frame */ -+ uint32_t frame_width, /* Width of each frame */ -+ uint32_t frames_count, /* Frames count */ -+ /*int overlap, */ -+ uint8_t reversed, -+ uint32_t *errors_sum -+ ) -+{ -+ uint8_t *assembled = output; -+ uint32_t frame, column, row, image_height = frame_height; -+ uint32_t error, not_overlapped; -+ -+ *errors_sum = 0; -+ -+ if (frames_count < 1) return 0; -+ -+ /* Rotating given data by 90 degrees -+ * Taken from document describing aes2501 image format -+ * TODO: move reversing detection here */ -+ if (reversed) { -+ output += (frames_count - 1) * frame_width * frame_height; -+ } -+ -+ for (frame = 0; frame < frames_count; frame++) { -+ for (column = 0; column < frame_width; column++) { -+ for (row = 0; row < (frame_height / 2); row++) { -+ output[frame_width * ( 2 * row) + column] = *input & 0x0F; -+ output[frame_width * ( 2 * row + 1) + column] = *input >> 4; -+ input++; -+ } -+ } -+ -+ if (reversed) { -+ output -= frame_width * frame_height; -+ } -+ else { -+ output += frame_width * frame_height; -+ } -+ } -+ -+ /* Detecting where frames overlaped */ -+ output = assembled; -+ for (frame = 1; frame < frames_count; frame++) -+ { -+ output += frame_width * frame_height; -+ not_overlapped = find_overlap(assembled, output, frame_height, frame_width, &error); -+ *errors_sum += error; -+ image_height += not_overlapped; -+ assembled += frame_width * not_overlapped; -+ memcpy(assembled, output, frame_width * frame_height); -+ } -+ -+ return image_height; -+} -+ -+ -+ -+static void store_new_aes2501_pnm(const uint8_t *data, unsigned int width, unsigned int height) -+{ -+ char header[20]; -+ __kernel_size_t header_len; -+ int i; -+ char *iter; -+ -+ sprintf(header, "P5\n%u %u\n255\n", width, height); -+ header_len = strlen(header); -+ -+ finger_print_len = header_len + width * height; -+ finger_print = kzalloc(finger_print_len, GFP_KERNEL); -+ -+ memcpy(finger_print, header, header_len); -+ -+ for (i = 0, iter = &finger_print[header_len]; i < width * height; i++, iter++) -+ *iter = (data[i] << 4) + 0x0f; -+ -+} -+ -+ -+ -+static void do_scanning(struct work_struct *work) -+{ -+ struct usb_aes2501 *dev; -+ -+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ -+ -+ uint8_t *raw, *cooked; -+ unsigned swidth = 192; -+ unsigned sheight = 16; -+ unsigned maxstrip = 150; -+ unsigned nstrips, height, mindiff_sum, mindiff_sum_r; -+ -+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ -+ -+ dev = container_of(work, struct usb_aes2501, scan_work); -+ -+ -+ -+ -+ -+ -+ setup_aes2501(dev); -+ -+ while (!detect_finger_on_aes2501(dev)) -+ { -+ if (dev->stop_scan) -+ { -+ PRINTK_INFO("aborting scan due to module removal\n"); -+ return; -+ } -+ }; -+ -+ /* We don't need physical continuity of allocated memory */ -+ raw = (uint8_t *)vmalloc((3 * maxstrip * sheight * swidth) / 2); -+ if (raw == NULL) -+ { -+ PRINTK_CRIT("failed to allocate memory\n"); -+ return; -+ } -+ -+ cooked = raw + (maxstrip * sheight * swidth)/2; -+ -+ -+ -+ printk(KERN_INFO "Raw is 0x%p\n", raw); -+ -+ -+ printk(KERN_INFO "Scanning...\n"); -+ nstrips = read_fingerprint_on_aes2501(dev, raw, maxstrip); -+ -+ printk(KERN_INFO "Assembling...\n"); -+ -+ -+ height = assemble(raw, cooked, 16, 192, nstrips, 0, &mindiff_sum); -+ height = assemble(raw, cooked, 16, 192, nstrips, 1, &mindiff_sum_r); -+ -+ -+ printk(KERN_INFO "Mindiff normal: %d, mindiff reversed: %d\n", mindiff_sum, mindiff_sum_r); -+ -+ if (mindiff_sum_r < mindiff_sum) -+ { -+ PRINTK_INFO("reversed image detected\n"); -+ } -+ else -+ { -+ height = assemble(raw, cooked, 16, 192, nstrips, 0, &mindiff_sum); -+ } -+ -+ printk(KERN_INFO "First height :: %d\n", height); -+ -+#if 0 -+ if (height < 100) { -+ /* It was a "touch", not a finger scan. */ -+ printk(KERN_INFO "Was only a 'touch' ;( [%d]\n", height); -+ /*break;*/ -+ } -+ else -+ -+ { -+ //store_new_aes2501_pnm(cooked, swidth, height); -+ height = assemble(raw, cooked, nstrips, 1, sensor_reversed); -+ printk(KERN_INFO "Second height :: %d\n", height); -+ store_new_aes2501_pnm(cooked, swidth, height); -+ } -+#endif -+ store_new_aes2501_pnm(cooked, swidth, height); -+ standby_aes2501(dev); -+ -+ vfree(raw); -+ -+ flag = 1; -+ wake_up_interruptible(&wq); -+ -+ -+} -+ -+ -+static int aes2501_open(struct inode *inode, struct file *file) -+{ -+ struct usb_aes2501 *dev; -+ -+ //return 0; -+ -+ -+ dev = _dev; -+ -+ printk(KERN_INFO "Plop !\n"); -+ -+ -+ return 0; -+} -+ -+ -+ -+ -+ -+ -+/* -+ * usb class driver info in order to get a minor number from the usb core, -+ * and to have the device registered with the driver core -+ */ -+static struct usb_class_driver aes2501_class = { -+ .name = "aes2501%d", -+ .fops = &aes2501_fops, -+ .minor_base = USB_AES2501_MINOR_BASE, -+}; -+ -+static int aes2501_probe(struct usb_interface *interface, const struct usb_device_id *id) -+{ -+ int retval; -+ struct usb_aes2501 *dev; -+ struct usb_host_interface *iface_desc; -+ __u8 i; -+ struct usb_endpoint_descriptor *endpoint; -+ __u16 buffer_size; -+ -+ -+ -+#if 0 -+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ -+ -+ uint8_t *raw, *cooked; -+ unsigned swidth = 192; -+ unsigned sheight = 16; -+ unsigned maxstrip = 150; -+ unsigned nstrips, height; -+ -+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ -+ -+ -+ raw = (uint8_t *)__get_free_page(GFP_KERNEL);//kmalloc((3 * maxstrip * sheight * swidth) / 2, GFP_KERNEL); -+ cooked = raw + (maxstrip * sheight * swidth)/2; -+#endif -+ -+ printk(KERN_INFO "aes2501 probe !\n"); -+ -+ -+ retval = -ENOMEM; -+ -+ /* Allocate memory for our device state and initialize it */ -+ -+ dev = kzalloc(sizeof(*dev), GFP_KERNEL); -+ if (dev == NULL) -+ { -+ PRINTK_CRIT("out of memory\n"); -+ goto error; -+ } -+ -+ _dev = dev; -+ -+ dev->stop_scan = 0; -+ -+ kref_init(&dev->kref); -+ sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); -+ mutex_init(&dev->io_mutex); -+ -+ dev->udev = usb_get_dev(interface_to_usbdev(interface)); -+ dev->interface = interface; -+ -+ printk(KERN_INFO "num_altsetting == %d\n", interface->num_altsetting); -+ -+ /** -+ * Set up the endpoint information. -+ * Use only the first bulk-in and bulk-out endpoints. -+ */ -+ -+ iface_desc = interface->cur_altsetting; -+ -+ printk(KERN_INFO "BULK COUNT %d\n", iface_desc->desc.bNumEndpoints); -+ -+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) -+ { -+ endpoint = &iface_desc->endpoint[i].desc; -+ -+ if (!dev->bulk_in_endpointAddr && /*usb_endpoint_is_bulk_in(endpoint))*/ -+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) -+ == USB_DIR_IN) && -+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) -+ == USB_ENDPOINT_XFER_BULK)) -+ { -+ /* We found a bulk in endpoint */ -+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); -+ dev->bulk_in_size = buffer_size; -+ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; -+ dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); -+ -+ printk(KERN_INFO "BULK IN %d ; size = %d\n", i, buffer_size); -+ -+ if (dev->bulk_in_buffer == NULL) -+ { -+ PRINTK_CRIT("could not allocate bulk_in_buffer\n"); -+ goto error; -+ } -+ -+ } -+ -+ if (!dev->bulk_out_endpointAddr && /*usb_endpoint_is_bulk_out(endpoint))*/ -+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) -+ == USB_DIR_OUT) && -+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) -+ == USB_ENDPOINT_XFER_BULK)) -+ { -+ printk(KERN_INFO "BULK OUT %d\n", i); -+ -+ /* We found a bulk out endpoint */ -+ dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; -+ } -+ -+ } -+ -+ if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) -+ { -+ PRINTK_CRIT("could not find both bulk-in and bulk-out endpoints\n"); -+ goto error; -+ } -+ -+/* -+ dev->once_tasklet = kzalloc(sizeof(*dev->once_tasklet), GFP_KERNEL); -+ if (dev->once_tasklet == NULL) -+ { -+ PRINTK_CRIT("out of memory\n"); -+ goto error; -+ } -+*/ -+ /* Save our data pointer in this interface device */ -+ usb_set_intfdata(interface, dev); -+ -+ /* We can register the device now, as it is ready */ -+ retval = usb_register_dev(interface, &aes2501_class); -+ if (retval) { -+ /* something prevented us from registering this driver */ -+ PRINTK_CRIT("not able to get a minor for this device\n"); -+ usb_set_intfdata(interface, NULL); -+ goto error; -+ } -+ -+ /* Let the user know what node this device is now attached to */ -+ PRINTK_INFO("device now attached to aes2501-%d\n", interface->minor); -+ -+#if 0 -+ setup_aes2501(dev); -+ -+ while (!detect_finger_on_aes2501(dev)); -+ -+ -+ printk(KERN_INFO "Raw is 0x%p\n", raw); -+ -+ -+ printk(KERN_INFO "Scanning...\n"); -+ nstrips = read_fingerprint_on_aes2501(dev, raw, maxstrip); -+ -+ printk(KERN_INFO "Assembling...\n"); -+ -+ height = assemble(raw, cooked, nstrips, 1); -+ if (height < 100) { -+ /* It was a "touch", not a finger scan. */ -+ printk(KERN_INFO "Was only a 'touch' ;(\n"); -+ /*break;*/ -+ } -+ else -+ { -+ store_new_aes2501_pnm(cooked, swidth, height); -+ height = assemble(raw, cooked, nstrips, 0); -+ store_new_aes2501_pnm(cooked, swidth, height); -+ } -+ -+ standby_aes2501(dev); -+#endif -+ -+ return 0; -+ -+error: -+ if (dev != NULL) -+ kref_put(&dev->kref, aes2501_delete); -+ -+ return retval; -+ -+} -+ -+ -+static void aes2501_disconnect(struct usb_interface *interface) -+{ -+ int minor; -+ struct usb_aes2501 *dev; -+ -+ minor = interface->minor; -+ -+ /* Prevent aes2501_open() from racing aes2501_disconnect() */ -+ lock_kernel(); -+ -+ dev = usb_get_intfdata(interface); -+ usb_set_intfdata(interface, NULL); -+ -+ /* Give back our minor */ -+ usb_deregister_dev(interface, &aes2501_class); -+ -+ /* Prevent more I/O from starting */ -+ mutex_lock(&dev->io_mutex); -+ dev->interface = NULL; -+ mutex_unlock(&dev->io_mutex); -+ -+ unlock_kernel(); -+ -+ /* Decrement our usage count */ -+ kref_put(&dev->kref, aes2501_delete); -+ -+ PRINTK_INFO("aes2501 device #%d now disconnected\n", minor); -+ -+ -+ -+ -+ printk(KERN_INFO "aes2501 disconnect !\n"); -+ -+ -+} -+ -+ -+static struct usb_driver aes2501_driver = { -+ .name = "aes2501", -+ .probe = aes2501_probe, -+ .disconnect = aes2501_disconnect, -+ .id_table = id_table, -+}; -+ -+ -+static int __init aes2501_init(void) -+{ -+ int retval; -+ -+ comm_queue = create_singlethread_workqueue("aes2501"); -+ if (comm_queue == NULL) { -+ PRINTK_CRIT("could not create work queue\n"); -+ return -ENOMEM; -+ } -+ -+ retval = usb_register(&aes2501_driver); -+ if (retval == -EINVAL) -+ { -+ printk(KERN_ERR "usb_register failed. Error number %d\n", retval); -+ destroy_workqueue(comm_queue); -+ return retval; -+ } -+ -+ return 0; -+ -+} -+ -+static void __exit aes2501_exit(void) -+{ -+ _dev->stop_scan = 1; -+ destroy_workqueue(comm_queue); -+ usb_deregister(&aes2501_driver); -+} -+ -+module_init(aes2501_init); -+module_exit(aes2501_exit); -+ -+MODULE_AUTHOR("Cyrille Bagard"); -+MODULE_DESCRIPTION("AES 2501 fingerprint scanner driver"); -+MODULE_LICENSE("GPL"); -diff -Nurp linux-2.6.37/3rdparty/aes2501/aes2501.h linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.h ---- linux-2.6.37/3rdparty/aes2501/aes2501.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501.h 2008-01-14 03:07:30.000000000 +0200 -@@ -0,0 +1,40 @@ -+ -+/* -+ * aes2501.h -- AuthenTec AES2501 Fingerprint Sensor Driver for Linux -+ * -+ * Maintainer: Cyrille Bagard -+ * -+ * Copyright (C) 2007 Cyrille Bagard -+ * -+ * This file is part of the AES2501 driver. -+ * -+ * the AES2501 driver is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * the AES2501 driver 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 the AES2501 driver; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ * -+ */ -+ -+ -+#ifndef __AES2501_H -+#define __AES2501_H -+ -+ -+#define AES2501_IOC_MAGIC 'N' -+ -+ -+#define AES2501_IOC_TEST _IOW(AES2501_IOC_MAGIC, 1, int) -+ -+ -+ -+ -+#endif /* __AES2501_H */ -diff -Nurp linux-2.6.37/3rdparty/aes2501/aes2501_regs.h linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501_regs.h ---- linux-2.6.37/3rdparty/aes2501/aes2501_regs.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/aes2501/aes2501_regs.h 2008-01-14 03:07:30.000000000 +0200 -@@ -0,0 +1,464 @@ -+ -+/* -+ * aes2501_regs.h -- AuthenTec AES2501 Fingerprint Sensor Driver for Linux -+ * -+ * Maintainer: Cyrille Bagard -+ * -+ * Copyright (C) 2007 Cyrille Bagard -+ * -+ * This file is part of the AES2501 driver. -+ * -+ * the AES2501 driver is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * the AES2501 driver 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 the AES2501 driver; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ * -+ */ -+ -+ -+#ifndef __AES2501_REGS_H -+#define __AES2501_REGS_H -+ -+ -+ -+/* -+ * AES2501 Control Register 1 (CTRL1) -+ */ -+ -+#define AES2501_REG_CTRL1 0x80 -+ -+#define AES2501_CTRL1_MASTER_RESET 0x01 /* Master reset, same as Power On Reset */ -+#define AES2501_CTRL1_SCAN_RESET 0x02 /* Scan reset: stop and restart the scan sequencer */ -+#define AES2501_CTRL1_REG_UPDATE 0x04 /* 1 = continuously updated, 0 = updated prior to starting a scan */ -+ -+ -+/* -+ * AES2501 Control Register 2 (CTRL2) -+ */ -+ -+#define AES2501_REG_CTRL2 0x81 -+ -+#define AES2501_CTRL2_CONTINUOUS 0x01 /* 1 = continuous scans, 0 = single scans */ -+#define AES2501_CTRL2_READ_REGS 0x02 /* Read the current state of the local registers in the Sensor IC */ -+#define AES2501_CTRL2_SET_ONE_SHOT 0x04 /* Set the one-shot flip-flop */ -+#define AES2501_CTRL2_CLR_ONE_SHOT 0x08 /* Clear the one-shot flip-flop */ -+#define AES2501_CTRL2_READ_ID 0x10 /* Read the ID register of the chip */ -+ -+ -+/* -+ * AES2501 Excitation Common Control Register (EXCITCTRL) -+ */ -+ -+#define AES2501_REG_EXCITCTRL 0x82 -+ -+#define AES2501_EXCITCTRL_LO_PWR 0x01 /* If set, enable detection in sleep/suspend mode */ -+#define AES2501_EXCITCTRL_AUTO_CAL 0x02 /* If set, perform finger detection calibration */ -+#define AES2501_EXCITCTRL_SGC_ENABLE 0x04 /* ??? */ -+#define AES2501_EXCITCTRL_SGC_RESTART 0x08 /* ??? */ -+ -+#define AES2501_EXCITCTRL_EXCIT_SQR 0x20 /* Select the (1=square | 0=sine) wave finger drive signal */ -+#define AES2501_EXCITCTRL_EXCIT_BOOST 0x10 /* TODO: understand this part */ -+ -+ -+/* -+ * AES2501 Detect Control Register (DETCTRL) -+ */ -+ -+#define AES2501_REG_DETCTRL 0x83 -+ -+enum aes2501_detection_rate { -+ -+ AES2501_DETCTRL_DRATE_CONTINUOUS = 0x00, /* Detection cycles occur continuously */ -+ AES2501_DETCTRL_DRATE_16_MS = 0x01, /* Detection cycles occur every 16.62 ms */ -+ AES2501_DETCTRL_DRATE_31_MS = 0x02, /* Detection cycles occur every 31.24 ms */ -+ AES2501_DETCTRL_DRATE_62_MS = 0x03, /* Detection cycles occur every 62.50 ms */ -+ AES2501_DETCTRL_DRATE_125_MS = 0x04, /* Detection cycles occur every 125.0 ms */ -+ AES2501_DETCTRL_DRATE_250_MS = 0x05, /* Detection cycles occur every 250.0 ms */ -+ AES2501_DETCTRL_DRATE_500_MS = 0x06, /* Detection cycles occur every 500.0 ms */ -+ AES2501_DETCTRL_DRATE_1_S = 0x07 /* Detection cycles occur every 1 s */ -+ -+}; -+ -+enum aes2501_settling_delay { -+ -+ AES2501_DETCTRL_SDELAY_31_MS = 0x00, /* 31.25 ms */ -+ AES2501_DETCTRL_SSDELAY_62_MS = 0x10, /* 62.5 ms */ -+ AES2501_DETCTRL_SSDELAY_125_MS = 0x20, /* 125 ms */ -+ AES2501_DETCTRL_SSDELAY_250_MS = 0x30 /* 250 ms */ -+ -+}; -+ -+ -+ -+ -+ -+/* -+ * AES2501 Column Scan Rate Register (COLSCAN) -+ */ -+ -+#define AES2501_REG_COLSCAN 0x88 -+ -+enum aes2501_col_scan_rate { -+ -+ AES2501_COLSCAN_SRATE_32_US = 0x00, /* 32 us */ -+ AES2501_COLSCAN_SRATE_64_US = 0x01, /* 64 us */ -+ AES2501_COLSCAN_SRATE_128_US = 0x02, /* 128 us */ -+ AES2501_COLSCAN_SRATE_256_US = 0x03, /* 256 us */ -+ AES2501_COLSCAN_SRATE_512_US = 0x04, /* 512 us */ -+ AES2501_COLSCAN_SRATE_1024_US = 0x05, /* 1024 us */ -+ AES2501_COLSCAN_SRATE_2048_US = 0x06, /* 2048 us */ -+ -+}; -+ -+ -+/* -+ * AES2501 Measure Drive Register (MEASDRV) -+ */ -+ -+#define AES2501_REG_MEASDRV 0x89 -+ -+enum aes2501_mesure_drive { -+ -+ AES2501_MEASDRV_MDRIVE_0_325 = 0x00, /* 0.325 Vpp */ -+ AES2501_MEASDRV_MDRIVE_0_65 = 0x01, /* 0.65 Vpp */ -+ AES2501_MEASDRV_MDRIVE_1_3 = 0x02, /* 1.3 Vpp */ -+ AES2501_MEASDRV_MDRIVE_2_6 = 0x03 /* 2.6 Vpp */ -+ -+}; -+ -+#define AES2501_MEASDRV_SQUARE 0x20 /* Select (1=square | 0=sine) wave drive during measure */ -+#define AES2501_MEASDRV_MEASURE_SQUARE 0x10 /* 0 = use mesure drive setting, 1 = when sine wave is selected */ -+ -+ -+/* -+ * AES2501 Measure Frequency Register (MEASFREQ) -+ */ -+ -+#define AES2501_REG_MEASFREQ 0x8a -+ -+enum aes2501_measure_freq { -+ -+ AES2501_MEASFREQ_125K = 0x01, /* 125 kHz */ -+ AES2501_MEASFREQ_250K = 0x02, /* 250 kHz */ -+ AES2501_MEASFREQ_500K = 0x03, /* 500 kHz */ -+ AES2501_MEASFREQ_1M = 0x04, /* 1 MHz */ -+ AES2501_MEASFREQ_2M = 0x05 /* 2 MHz */ -+ -+}; -+ -+ -+ -+ -+/* -+ * AES2501 Demod Phase 2 Register (DEMODPHASE2) -+ */ -+ -+#define AES2501_REG_DEMODPHASE2 0x8c -+ -+#define DEMODPHASE_NONE 0x00 -+ -+#define DEMODPHASE_180_00 0x40 /* 180 degrees */ -+#define DEMODPHASE_2_81 0x01 /* 2.8125 degrees */ -+ -+ -+/* -+ * AES2501 Demod Phase 1 Register (DEMODPHASE1) -+ */ -+ -+#define AES2501_REG_DEMODPHASE1 0x8d -+ -+#define DEMODPHASE_1_40 0x40 /* 1.40625 degrees */ -+#define DEMODPHASE_0_02 0x01 /* 0.02197256 degrees */ -+ -+ -+/* -+ * AES2501 Channel Gain Register (CHANGAIN) -+ */ -+ -+#define AES2501_REG_CHANGAIN 0x8e -+ -+enum aes2501_sensor_gain1 { -+ -+ AES2501_CHANGAIN_STAGE1_2X = 0x00, /* 2x */ -+ AES2501_CHANGAIN_STAGE1_4X = 0x01, /* 4x */ -+ AES2501_CHANGAIN_STAGE1_8X = 0x02, /* 8x */ -+ AES2501_CHANGAIN_STAGE1_16X = 0x03 /* 16x */ -+ -+}; -+ -+enum aes2501_sensor_gain2 { -+ -+ AES2501_CHANGAIN_STAGE2_2X = 0x00, /* 2x */ -+ AES2501_CHANGAIN_STAGE2_4X = 0x10, /* 4x */ -+ AES2501_CHANGAIN_STAGE2_8X = 0x20, /* 8x */ -+ AES2501_CHANGAIN_STAGE2_16X = 0x30 /* 16x */ -+ -+}; -+ -+ -+ -+/* -+ * AES2501 A/D Reference High Register (ADREFHI) -+ */ -+ -+#define AES2501_REG_ADREFHI 0x91 -+ -+ -+/* -+ * AES2501 A/D Reference Low Register (ADREFLO) -+ */ -+ -+#define AES2501_REG_ADREFLO 0x92 -+ -+ -+/* -+ * AES2501 Start Row Register (STRTROW) -+ */ -+ -+#define AES2501_REG_STRTROW 0x93 -+ -+ -+/* -+ * AES2501 End Row Register (ENDROW) -+ */ -+ -+#define AES2501_REG_ENDROW 0x94 -+ -+ -+/* -+ * AES2501 Start Column Register (STRTCOL) -+ */ -+ -+#define AES2501_REG_STRTCOL 0x95 -+ -+ -+/* -+ * AES2501 End Column Register (ENDCOL) -+ */ -+ -+#define AES2501_REG_ENDCOL 0x96 -+ -+ -+/* -+ * AES2501 Data Format Register (DATFMT) -+ */ -+ -+#define AES2501_REG_DATFMT 0x97 -+ -+#define AES2501_DATFMT_EIGHT 0x40 /* 1 = 8-bit data, 0 = 4-bit data */ -+#define AES2501_DATFMT_LOW_RES 0x20 /* TODO: understand this part */ -+#define AES2501_DATFMT_BIN_IMG 0x10 /* TODO: understand this part */ -+ -+/* TODO: Threshold */ -+ -+ -+/* -+ * AES2501 Image Data Control Register (IMAGCTRL) -+ */ -+ -+#define AES2501_REG_IMAGCTRL 0x98 -+ -+#define AES2501_IMAGCTRL_IMG_DATA_DISABLE 0x01 /* If set, image data message and authentication message are not returned when imaging */ -+#define AES2501_IMAGCTRL_HISTO_DATA_ENABLE 0x02 /* if set, send histogram message when imaging */ -+#define AES2501_IMAGCTRL_HISTO_EACH_ROW 0x04 /* A histo message is sent at the end of (1=each row | 0 = scanning) */ -+#define AES2501_IMAGCTRL_HISTO_FULL_ARRAY 0x08 /* 1 = full image array, 0 = 64x64 center */ -+#define AES2501_IMAGCTRL_REG_FIRST 0x10 /* Registers are returned (1=before | 0=after) image data */ -+#define AES2501_IMAGCTRL_TST_REG_ENABLE 0x20 /* If set, Test Registers are returned with register messages */ -+ -+ -+ -+ -+ -+ -+ -+ -+/* -+ * AES2501 Status Register (STAT) -+ */ -+ -+#define AES2501_REG_STAT 0x9a -+ -+#define AES2501_STAT_SCAN 0x0f /* Scan state */ -+#define AES2501_STAT_ERROR 0x10 /* Framing error */ -+#define AES2501_STAT_PAUSED 0x20 /* Scan paused */ -+#define AES2501_STAT_RESET 0x40 /* Reset occurred */ -+ -+enum aes2501_scan_state { -+ -+ STATE_WAITING_FOR_FINGER = 0x00, /* Waiting for finger */ -+ STATE_FINGER_SETTLING_DELAY = 0x01, /* In Finger settling delay */ -+ STATE_POWER_UP_DELAY = 0x02, /* In power up delay */ -+ STATE_WAITING_TO_START_SCAN = 0x03, /* Waiting to start image scan */ -+ STATE_PRELOADING_SUBARRAY_0 = 0x04, /* Pre-loading subarray 0 */ -+ STATE_SETUP_FOR_ROW_ADVANCE = 0x05, /* Setup for row advance */ -+ STATE_WAITING_FOR_ROW_ADVANCE = 0x06, /* Waiting for row advance */ -+ STATE_PRELOADING_COL_0 = 0x07, /* Pre-loading column 0 */ -+ STATE_SETUP_FOR_COL_ADVANCE = 0x08, /* Setup for column advance */ -+ STATE_WAITING_FOR_COL_ADVANCE = 0x09, /* Waiting for column advance */ -+ STATE_WAITING_FOR_SCAN_START = 0x0a, /* Waiting for scan start */ -+ STATE_WAITING_FOR_SCAN_END = 0x0b, /* Waiting for scan end */ -+ STATE_WAITING_FOR_ROW_SETUP = 0x0c, /* Waiting for row setup */ -+ STATE_WAITING_FOR_COL_TIME = 0x0d, /* Waiting for one column time (depends on scan rate) */ -+ STATE_WAITING_FOR_QUEUED_DATA = 0x0e, /* Waiting for queued data transmission to be completed */ -+ STATE_WAIT_FOR_128_US = 0x0f /* Wait for 128 us */ -+ -+}; -+ -+ -+/* -+ * AES2501 Challenge Word 1 Register (CHWORD1) -+ */ -+ -+#define AES2501_REG_CHWORD1 0x9b -+ -+#define AES2501_CHWORD1_IS_FINGER 0x01 /* If set, finger is present */ -+ -+ -+/* -+ * AES2501 Challenge Word 2 Register (CHWORD2) -+ */ -+ -+#define AES2501_REG_CHWORD2 0x9c -+ -+ -+/* -+ * AES2501 Challenge Word 3 Register (CHWORD3) -+ */ -+ -+#define AES2501_REG_CHWORD3 0x9d -+ -+ -+/* -+ * AES2501 Challenge Word 4 Register (CHWORD4) -+ */ -+ -+#define AES2501_REG_CHWORD4 0x9e -+ -+ -+/* -+ * AES2501 Challenge Word 5 Register (CHWORD5) -+ */ -+ -+#define AES2501_REG_CHWORD5 0x9f -+ -+ -+ -+ -+ -+/* -+ * AES2501 Test Register 1 (TREG1) -+ */ -+ -+#define AES2501_REG_TREG1 0xa1 -+ -+#define AES2501_TREG1_SBIAS_UNLCK 0x10 /* 1 = unlock the controlling of sense amp bias, 0 = sense amp bias changes */ -+ -+enum aes2501_sense_amp_bias { -+ -+ AES2501_TREG1_SAMP_BIAS_2_UA = 0x00, /* 2.5 uA */ -+ AES2501_TREG1_SAMP_BIAS_5_UA = 0x01, /* 5 uA */ -+ AES2501_TREG1_SAMP_BIAS_8_UA = 0x02, /* 8 uA */ -+ AES2501_TREG1_SAMP_BIAS_10_UA = 0x03 /* 10 uA */ -+ -+}; -+ -+ -+ -+ -+/* -+ * AES2501 Auto-Calibration Offset Register (AUTOCALOFFSET) -+ */ -+ -+#define AES2501_REG_AUTOCALOFFSET 0xa8 -+ -+ -+ -+ -+ -+/* -+ * AES2501 Test Register C (TREGC) -+ */ -+ -+#define AES2501_REG_TREGC 0xac -+ -+#define AES2501_TREGC_ENABLE 0x01 /* Enable the reading of the register in TREGD */ -+ -+ -+/* -+ * AES2501 Test Register D (TREGD) -+ */ -+ -+#define AES2501_REG_TREGD 0xad -+ -+ -+ -+ -+ -+/* -+ * AES2501 Low Power Oscillator On Time Register (LPONT) -+ */ -+ -+#define AES2501_REG_LPONT 0xb4 -+ -+/* -+ * This register sets the low power oscillator on time. -+ * Units are roughly equivalent to milliseconds. -+ */ -+ -+#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */ -+#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */ -+ -+ -+ -+#define ENUM_REG(reg) _ ## reg = reg -+ -+typedef enum _Aes2501Registers -+{ -+ ENUM_REG(AES2501_REG_CTRL1), -+ ENUM_REG(AES2501_REG_CTRL2), -+ ENUM_REG(AES2501_REG_EXCITCTRL), -+ ENUM_REG(AES2501_REG_DETCTRL), -+ ENUM_REG(AES2501_REG_COLSCAN), -+ ENUM_REG(AES2501_REG_MEASDRV), -+ ENUM_REG(AES2501_REG_MEASFREQ), -+ ENUM_REG(AES2501_REG_DEMODPHASE2), -+ ENUM_REG(AES2501_REG_DEMODPHASE1), -+ ENUM_REG(AES2501_REG_CHANGAIN), -+ ENUM_REG(AES2501_REG_ADREFHI), -+ ENUM_REG(AES2501_REG_ADREFLO), -+ ENUM_REG(AES2501_REG_STRTROW), -+ ENUM_REG(AES2501_REG_ENDROW), -+ ENUM_REG(AES2501_REG_STRTCOL), -+ ENUM_REG(AES2501_REG_ENDCOL), -+ ENUM_REG(AES2501_REG_DATFMT), -+ ENUM_REG(AES2501_REG_IMAGCTRL), -+ ENUM_REG(AES2501_REG_STAT), -+ ENUM_REG(AES2501_REG_CHWORD1), -+ ENUM_REG(AES2501_REG_CHWORD2), -+ ENUM_REG(AES2501_REG_CHWORD3), -+ ENUM_REG(AES2501_REG_CHWORD4), -+ ENUM_REG(AES2501_REG_CHWORD5), -+ ENUM_REG(AES2501_REG_TREG1), -+ ENUM_REG(AES2501_REG_AUTOCALOFFSET), -+ ENUM_REG(AES2501_REG_TREGC), -+ ENUM_REG(AES2501_REG_TREGD), -+ ENUM_REG(AES2501_REG_LPONT) -+ -+} Aes2501Registers; -+ -+ -+#define FIRST_AES2501_REG 0x80 -+#define LAST_AES2501_REG 0x9f -+ -+ -+ -+#endif /* __AES2501_REGS_H */ -diff -Nurp linux-2.6.37/3rdparty/aes2501/ChangeLog linux-2.6.37.3rdparty/3rdparty/aes2501/ChangeLog ---- linux-2.6.37/3rdparty/aes2501/ChangeLog 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/aes2501/ChangeLog 2008-01-14 03:07:30.000000000 +0200 -@@ -0,0 +1,95 @@ -+2007-11-05 Cyrille Bagard -+ -+ * aes2501.c: -+ Apply Vasily Khoruzhick's new patch: rewrite image processing functions. -+ Thus, there should be no more significant common points with aes2501-wy. -+ -+2007-11-05 Cyrille Bagard -+ -+ * aes2501.c: -+ In order to respect the license of aes2501-wy, on which this driver is -+ based, begin to rewrite some functions. Thus, better handle histograms. -+ One more strip is often processed, but that is more logical like this. -+ -+ * aes2501_regs.h: -+ Enumerate all used registers for convenience. -+ -+2007-11-03 Cyrille Bagard -+ -+ * aes2501.c: -+ Clean the code. Take care of signals when processes are awaking. -+ Apply Vasily Khoruzhick's new patch: autodetect picture reversing needs -+ and fix kernel oops in case of module removal. -+ -+ * Makefile: -+ Remove hidden files and clean the rules. KERNELDIR has to be overrided -+ from the sheel if needed now. -+ -+2007-10-29 Cyrille Bagard -+ -+ * aes2501.c: -+ Apply Vasily Khoruzhick's patch and thus increase the resulting pictures -+ quality. Many thanks to him ! -+ -+ * Makefile: -+ Add two new rules: usertest and clean. -+ -+ * usertest.c: -+ Initial commit. -+ -+2007-08-27 Cyrille Bagard -+ -+ * aes2501.c: -+ Make INIT_WORK macro working with 2.6.20+ kernels. Many thanks to -+ Miguel Gea Milvaques for the patch ! -+ -+2007-06-13 Cyrille Bagard -+ -+ * aes2501.c: -+ * aes2501_regs.h: -+ Complete translation of all binary communications using well documented -+ registers and values (as often as possible). -+ -+2007-06-13 Cyrille Bagard -+ -+ * aes2501.c: -+ Increase communication speed by buffering bulk out. Output data -+ need to be flushed at the end now. -+ -+2007-06-13 Cyrille Bagard -+ -+ * aes2501.c: -+ * aes2501_regs.h: -+ Translate binary data used in finger detection. Some register values -+ still need to be understood. Note: some hex values are reserved bits -+ in registers. -+ -+2007-06-12 Cyrille Bagard -+ -+ * aes2501.c: -+ Begin the binary translation: the "standby" function now uses understood -+ instructions and prints debug status information if needed. CTRL2 register -+ value needs to be checked. -+ -+ * aes2501_regs.h: -+ Define the interesting hex values for the following registers: -+ CTRL2, STAT, TREGC and TREGD. -+ -+2007-06-12 Cyrille Bagard -+ -+ * aes2501.c: -+ Clean the driver a little bit by removing old piece of code to handle -+ a character device in the old way. -+ -+2007-06-12 Cyrille Bagard -+ -+ * aes2501.c: -+ Use an own work queue. Scanning now starts with the AES2501_IOC_TEST -+ ioctl command. Reading the /dev entry blocks until data is available. -+ -+2007-06-12 Cyrille Bagard -+ -+ * aes2501.c: -+ * aes2501.h: -+ * Makefile: -+ Initial [crappy] commit. -diff -Nurp linux-2.6.37/3rdparty/aes2501/Makefile linux-2.6.37.3rdparty/3rdparty/aes2501/Makefile ---- linux-2.6.37/3rdparty/aes2501/Makefile 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/aes2501/Makefile 2008-01-14 03:07:30.000000000 +0200 -@@ -0,0 +1,19 @@ -+ -+obj-m := aes2501.o -+ -+KERNELDIR ?= /lib/modules/$(shell uname -r)/build -+ -+PWD := $(shell pwd) -+ -+all: usertest -+ $(MAKE) -C $(KERNELDIR) M=$(PWD) -+ -+usertest: -+ gcc -I. -o usertest usertest.c -+ -+clean: -+ rm -f *.o *~ aes2501.ko aes2501.mod.c Module.symvers usertest -+ rm -rf .tmp_versions -+ rm -f .*.cmd -+ -+ -diff -Nurp linux-2.6.37/3rdparty/aes2501/usertest.c linux-2.6.37.3rdparty/3rdparty/aes2501/usertest.c ---- linux-2.6.37/3rdparty/aes2501/usertest.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/aes2501/usertest.c 2008-01-14 03:07:30.000000000 +0200 -@@ -0,0 +1,78 @@ -+/** -+ * AES2501 Device Driver -+ * Userspace test program -+ * -+ * Compile with: -+ * gcc -I. -o usertest usertest.c -+ */ -+ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+#define BUFFER_SIZE 256 -+ -+ -+int main(int argc, char *argv[]) -+{ -+ int fd_in; -+ int fd_out; -+ char buffer[BUFFER_SIZE]; -+ size_t len; -+ -+ fd_in = -1; -+ fd_out = -1; -+ -+ -+ int data, rdata; -+ -+ -+ -+ fd_in = open("/dev/aes2501", O_RDONLY); -+ if (fd_in == -1) { -+ perror("open() -- "); -+ goto err; -+ } -+ -+ fd_out = open("/root/pic.pnm", O_CREAT | O_WRONLY); -+ if (fd_out == -1) { -+ perror("open() -- "); -+ goto err; -+ } -+ -+ -+ -+ data = 0x55555555; -+ ioctl(fd_in, AES2501_IOC_TEST, data); -+ //ioctl(fd_in, CASE2, &rdata); -+ -+ printf("IOCTL test: written: '%x' - received: '%x'\n", data, rdata); -+ -+ -+ -+ -+ /* Write the fingerprint */ -+ while ((len = read(fd_in, buffer, BUFFER_SIZE)) > 0) -+ write(fd_out, buffer, len); -+ -+ close(fd_in); -+ close(fd_out); -+ -+ return EXIT_SUCCESS; -+ -+ err: -+ -+ if (fd_in != -1) -+ close(fd_in); -+ -+ if (fd_out != -1) -+ close(fd_out); -+ -+ return EXIT_FAILURE; -+ -+} diff --git a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch b/kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch deleted file mode 100644 index f11cc2e5..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-aes2501-rmmod-oops-fix.patch +++ /dev/null @@ -1,21 +0,0 @@ -Prevent oops on module removal when we don't have the device available. - -Signed-off-by: Herton Ronaldo Krzesinski - ---- - linux-2.6.23/3rdparty/aes2501/aes2501.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff -p -up linux-2.6.23/3rdparty/aes2501/aes2501.c.orig linux-2.6.23/3rdparty/aes2501/aes2501.c ---- linux-2.6.23/3rdparty/aes2501/aes2501.c.orig 2008-01-13 23:37:38.000000000 -0200 -+++ linux-2.6.23/3rdparty/aes2501/aes2501.c 2008-01-13 23:38:26.000000000 -0200 -@@ -1616,7 +1616,8 @@ static int __init aes2501_init(void) - - static void __exit aes2501_exit(void) - { -- _dev->stop_scan = 1; -+ if (_dev) -+ _dev->stop_scan = 1; - destroy_workqueue(comm_queue); - usb_deregister(&aes2501_driver); - } diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch deleted file mode 100644 index 63ccc093..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-1.59.patch +++ /dev/null @@ -1,26399 +0,0 @@ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/crt.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/crt.c ---- linux-3.12.2/3rdparty/ndiswrapper/crt.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/crt.c 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,589 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ntoskernel.h" -+#include "crt_exports.h" -+ -+#ifdef CONFIG_X86_64 -+/* Windows long is 32-bit, so strip single 'l' in integer formats */ -+static void strip_l_modifier(char *str) -+{ -+ char *ptr = str; -+ int in_format = 0; -+ char *lptr = NULL; -+ char last = 0; -+ char *end_ptr; -+ char *wptr; -+ -+ /* Replace single 'l' inside integer formats with '\0' */ -+ for (ptr = str; *ptr; ptr++) { -+ if (!in_format) { -+ if (*ptr == '%') -+ in_format = 1; -+ last = *ptr; -+ continue; -+ } -+ switch (*ptr) { -+ case 'd': -+ case 'i': -+ case 'o': -+ case 'u': -+ case 'x': -+ case 'X': -+ case 'p': -+ case 'n': -+ case 'm': -+ if (lptr) { -+ *lptr = '\0'; -+ lptr = NULL; -+ } -+ in_format = 0; -+ break; -+ case 'c': -+ case 'C': -+ case 's': -+ case 'S': -+ case 'f': -+ case 'e': -+ case 'E': -+ case 'g': -+ case 'G': -+ case 'a': -+ case 'A': -+ lptr = NULL; -+ in_format = 0; -+ break; -+ case '%': -+ lptr = NULL; -+ if (last == '%') -+ in_format = 0; -+ else -+ in_format = 1; /* ignore previous junk */ -+ break; -+ case 'l': -+ if (last == 'l') -+ lptr = NULL; -+ else -+ lptr = ptr; -+ break; -+ default: -+ break; -+ } -+ last = *ptr; -+ } -+ -+ /* Purge zeroes from the resulting string */ -+ end_ptr = ptr; -+ wptr = str; -+ for (ptr = str; ptr < end_ptr; ptr++) -+ if (*ptr != 0) -+ *(wptr++) = *ptr; -+ *wptr = 0; -+} -+ -+/* -+ * va_list on x86_64 Linux is designed to allow passing arguments in registers -+ * even to variadic functions. va_list is a structure holding pointers to the -+ * register save area, which holds the arguments passed in registers, and to -+ * the stack, which may have the arguments that did not fit the registers. -+ * va_list also holds offsets in the register save area for the next general -+ * purpose and floating point registers that the next va_arg() would fetch. -+ * -+ * Unlike Linux, the Windows va_list is just a pointer to the stack. No -+ * arguments are passed in the registers. That's why we construct the Linux -+ * va_list so that the register save area is never used. For that goal, we set -+ * the offsets to the maximal allowed values, meaning that the arguments passed -+ * in the registers have been exhausted. The values are 48 for general purpose -+ * registers (6 registers, 8 bytes each) and 304 for floating point registers -+ * (16 registers, 16 bytes each, on top of general purpose register). -+ */ -+ -+struct x86_64_va_list { -+ int gp_offset; -+ int fp_offset; -+ void *overflow_arg_area; -+ void *reg_save_area; -+}; -+ -+#define VA_LIST_DECL(_args) \ -+ va_list _args##new; \ -+ struct x86_64_va_list *_args##x; -+#define VA_LIST_PREP(_args) \ -+do { \ -+ _args##x = (struct x86_64_va_list *)&_args##new; \ -+ _args##x->gp_offset = 6 * 8; /* GP registers exhausted */ \ -+ _args##x->fp_offset = 6 * 8 + 16 * 16; /* FP registers exhausted */ \ -+ _args##x->overflow_arg_area = (void *)_args; \ -+ _args##x->reg_save_area = NULL; \ -+} while (0) -+#define VA_LIST_CONV(_args) (_args##new) -+#define VA_LIST_FREE(_args) -+#define FMT_DECL(_fmt) \ -+ char *_fmt##copy; \ -+ int _fmt##len; -+#define FMT_PREP(_fmt) \ -+do { \ -+ _fmt##len = strlen(format) + 1; \ -+ _fmt##copy = kmalloc(_fmt##len, irql_gfp()); \ -+ if (_fmt##copy) { \ -+ memcpy(_fmt##copy, format, _fmt##len); \ -+ strip_l_modifier(_fmt##copy); \ -+ } \ -+} while (0) -+#define FMT_CONV(_fmt) (_fmt##copy ? _fmt##copy : format) -+#define FMT_FREE(_fmt) kfree(_fmt##copy) -+ -+#else /* !CONFIG_X86_64 */ -+ -+#define VA_LIST_DECL(_args) -+#define VA_LIST_PREP(_args) -+#define VA_LIST_CONV(_args) (_args) -+#define VA_LIST_FREE(_args) -+#define FMT_DECL(_fmt) -+#define FMT_PREP(_fmt) -+#define FMT_CONV(_fmt) (format) -+#define FMT_FREE(_fmt) -+ -+#endif /* !CONFIG_X86_64 */ -+ -+__attribute__((format(printf, 2, 3))) -+noregparm INT WIN_FUNC(_win_sprintf,12) -+ (char *buf, const char *format, ...) -+{ -+ va_list args; -+ int res; -+ FMT_DECL(format) -+ -+ FMT_PREP(format); -+ va_start(args, format); -+ res = vsprintf(buf, FMT_CONV(format), args); -+ va_end(args); -+ FMT_FREE(format); -+ -+ TRACE2("buf: %p: %s", buf, buf); -+ return res; -+} -+ -+noregparm INT WIN_FUNC(swprintf,12) -+ (wchar_t *buf, const wchar_t *format, ...) -+{ -+ TODO(); -+ EXIT2(return 0); -+} -+ -+noregparm INT WIN_FUNC(_win_vsprintf,3) -+ (char *str, const char *format, va_list ap) -+{ -+ INT i; -+ VA_LIST_DECL(ap) -+ FMT_DECL(format) -+ -+ VA_LIST_PREP(ap); -+ FMT_PREP(format); -+ -+ i = vsprintf(str, FMT_CONV(format), VA_LIST_CONV(ap)); -+ TRACE2("str: %p: %s", str, str); -+ -+ FMT_FREE(format); -+ VA_LIST_FREE(ap); -+ EXIT2(return i); -+} -+ -+__attribute__((format(printf, 3, 4))) -+noregparm INT WIN_FUNC(_win_snprintf,12) -+ (char *buf, SIZE_T count, const char *format, ...) -+{ -+ va_list args; -+ int res; -+ FMT_DECL(format) -+ -+ FMT_PREP(format); -+ va_start(args, format); -+ res = vsnprintf(buf, count, FMT_CONV(format), args); -+ va_end(args); -+ TRACE2("buf: %p: %s", buf, buf); -+ -+ FMT_FREE(format); -+ return res; -+} -+ -+__attribute__((format(printf, 3, 4))) -+noregparm INT WIN_FUNC(_win__snprintf,12) -+ (char *buf, SIZE_T count, const char *format, ...) -+{ -+ va_list args; -+ int res; -+ FMT_DECL(format) -+ -+ FMT_PREP(format); -+ va_start(args, format); -+ res = vsnprintf(buf, count, FMT_CONV(format), args); -+ va_end(args); -+ TRACE2("buf: %p: %s", buf, buf); -+ -+ FMT_FREE(format); -+ return res; -+} -+ -+noregparm INT WIN_FUNC(_win_vsnprintf,4) -+ (char *str, SIZE_T size, const char *format, va_list ap) -+{ -+ INT i; -+ VA_LIST_DECL(ap) -+ FMT_DECL(format) -+ -+ VA_LIST_PREP(ap); -+ FMT_PREP(format); -+ -+ i = vsnprintf(str, size, FMT_CONV(format), VA_LIST_CONV(ap)); -+ TRACE2("str: %p: %s", str, str); -+ -+ FMT_FREE(format); -+ VA_LIST_FREE(ap); -+ EXIT2(return i); -+} -+ -+noregparm INT WIN_FUNC(_win__vsnprintf,4) -+ (char *str, SIZE_T size, const char *format, va_list ap) -+{ -+ INT i; -+ VA_LIST_DECL(ap) -+ FMT_DECL(format) -+ -+ VA_LIST_PREP(ap); -+ FMT_PREP(format); -+ -+ i = vsnprintf(str, size, FMT_CONV(format), VA_LIST_CONV(ap)); -+ TRACE2("str: %p: %s", str, str); -+ -+ FMT_FREE(format); -+ VA_LIST_FREE(ap); -+ EXIT2(return i); -+} -+ -+noregparm INT WIN_FUNC(_win__vsnwprintf,4) -+ (wchar_t *str, SIZE_T size, const wchar_t *format, va_list ap) -+{ -+ int ret; -+ -+ TODO(); /* format expansion not implemented */ -+ _win_wcsncpy(str, format, size); -+ ret = _win_wcslen(format); -+ if (ret >= size) -+ ret = -1; -+ return ret; -+} -+ -+noregparm char *WIN_FUNC(_win_strncpy,3) -+ (char *dst, char *src, SIZE_T n) -+{ -+ return strncpy(dst, src, n); -+} -+ -+noregparm SIZE_T WIN_FUNC(_win_strlen,1) -+ (const char *s) -+{ -+ return strlen(s); -+} -+ -+noregparm INT WIN_FUNC(_win_strncmp,3) -+ (const char *s1, const char *s2, SIZE_T n) -+{ -+ return strncmp(s1, s2, n); -+} -+ -+noregparm INT WIN_FUNC(_win_strcmp,2) -+ (const char *s1, const char *s2) -+{ -+ return strcmp(s1, s2); -+} -+ -+noregparm INT WIN_FUNC(_win_stricmp,2) -+ (const char *s1, const char *s2) -+{ -+ return stricmp(s1, s2); -+} -+ -+noregparm char *WIN_FUNC(_win_strncat,3) -+ (char *dest, const char *src, SIZE_T n) -+{ -+ return strncat(dest, src, n); -+} -+ -+noregparm INT WIN_FUNC(_win_wcscmp,2) -+ (const wchar_t *s1, const wchar_t *s2) -+{ -+ while (*s1 && *s1 == *s2) { -+ s1++; -+ s2++; -+ } -+ return *s1 - *s2; -+} -+ -+noregparm INT WIN_FUNC(_win_wcsicmp,2) -+ (const wchar_t *s1, const wchar_t *s2) -+{ -+ while (*s1 && tolower((char)*s1) == tolower((char)*s2)) { -+ s1++; -+ s2++; -+ } -+ return tolower((char)*s1) - tolower((char)*s2); -+} -+ -+noregparm SIZE_T WIN_FUNC(_win_wcslen,1) -+ (const wchar_t *s) -+{ -+ const wchar_t *t = s; -+ while (*t) -+ t++; -+ return t - s; -+} -+ -+noregparm wchar_t *WIN_FUNC(_win_wcsncpy,3) -+ (wchar_t *dest, const wchar_t *src, SIZE_T n) -+{ -+ const wchar_t *s; -+ wchar_t *d; -+ s = src + n; -+ d = dest; -+ while (src < s && (*d++ = *src++)) -+ ; -+ if (s > src) -+ memset(d, 0, (s - src) * sizeof(wchar_t)); -+ return dest; -+} -+ -+noregparm wchar_t *WIN_FUNC(_win_wcscpy,2) -+ (wchar_t *dest, const wchar_t *src) -+{ -+ wchar_t *d = dest; -+ while ((*d++ = *src++)) -+ ; -+ return dest; -+} -+ -+noregparm wchar_t *WIN_FUNC(_win_wcscat,2) -+ (wchar_t *dest, const wchar_t *src) -+{ -+ wchar_t *d; -+ d = dest; -+ while (*d) -+ d++; -+ while ((*d++ = *src++)) -+ ; -+ return dest; -+} -+ -+noregparm INT WIN_FUNC(_win_towupper,1) -+ (wchar_t c) -+{ -+ return toupper(c); -+} -+ -+noregparm INT WIN_FUNC(_win_towlower,1) -+ (wchar_t c) -+{ -+ return tolower(c); -+} -+ -+noregparm INT WIN_FUNC(_win_tolower,1) -+ (INT c) -+{ -+ return tolower(c); -+} -+ -+noregparm INT WIN_FUNC(_win_toupper,1) -+ (INT c) -+{ -+ return toupper(c); -+} -+ -+noregparm void *WIN_FUNC(_win_strcpy,2) -+ (void *to, const void *from) -+{ -+ return strcpy(to, from); -+} -+ -+noregparm char *WIN_FUNC(_win_strstr,2) -+ (const char *s1, const char *s2) -+{ -+ return strstr(s1, s2); -+} -+ -+noregparm char *WIN_FUNC(_win_strchr,2) -+ (const char *s, int c) -+{ -+ return strchr(s, c); -+} -+ -+noregparm char *WIN_FUNC(_win_strrchr,2) -+ (const char *s, int c) -+{ -+ return strrchr(s, c); -+} -+ -+noregparm void *WIN_FUNC(_win_memmove,3) -+ (void *to, void *from, SIZE_T count) -+{ -+ return memmove(to, from, count); -+} -+ -+noregparm void *WIN_FUNC(_win_memchr,3) -+ (const void *s, INT c, SIZE_T n) -+{ -+ return memchr(s, c, n); -+} -+ -+noregparm void *WIN_FUNC(_win_memcpy,3) -+ (void *to, const void *from, SIZE_T n) -+{ -+ return memcpy(to, from, n); -+} -+ -+noregparm void *WIN_FUNC(_win_memset,3) -+ (void *s, char c, SIZE_T count) -+{ -+ return memset(s, c, count); -+} -+ -+noregparm int WIN_FUNC(_win_memcmp,3) -+ (void *s1, void *s2, SIZE_T n) -+{ -+ return memcmp(s1, s2, n); -+} -+ -+noregparm void WIN_FUNC(_win_srand,1) -+ (UINT seed) -+{ -+ net_srandom(seed); -+} -+ -+noregparm int WIN_FUNC(rand,0) -+ (void) -+{ -+ char buf[6]; -+ int i, n; -+ -+ get_random_bytes(buf, sizeof(buf)); -+ for (n = i = 0; i < sizeof(buf); i++) -+ n += buf[i]; -+ return n; -+} -+ -+noregparm int WIN_FUNC(_win_atoi,1) -+ (const char *ptr) -+{ -+ int i = simple_strtol(ptr, NULL, 10); -+ return i; -+} -+ -+noregparm int WIN_FUNC(_win_isdigit,1) -+ (int c) -+{ -+ return isdigit(c); -+} -+ -+noregparm int WIN_FUNC(_win_isprint,1) -+ (int c) -+{ -+ return isprint(c); -+} -+ -+wstdcall s64 WIN_FUNC(_alldiv,2) -+ (s64 a, s64 b) -+{ -+ return a / b; -+} -+ -+wstdcall u64 WIN_FUNC(_aulldiv,2) -+ (u64 a, u64 b) -+{ -+ return a / b; -+} -+ -+wstdcall s64 WIN_FUNC(_allmul,2) -+ (s64 a, s64 b) -+{ -+ return a * b; -+} -+ -+wstdcall u64 WIN_FUNC(_aullmul,2) -+ (u64 a, u64 b) -+{ -+ return a * b; -+} -+ -+wstdcall s64 WIN_FUNC(_allrem,2) -+ (s64 a, s64 b) -+{ -+ return a % b; -+} -+ -+wstdcall u64 WIN_FUNC(_aullrem,2) -+ (u64 a, u64 b) -+{ -+ return a % b; -+} -+ -+regparm3 s64 WIN_FUNC(_allshl,2) -+ (s64 a, u8 b) -+{ -+ return a << b; -+} -+ -+regparm3 u64 WIN_FUNC(_aullshl,2) -+ (u64 a, u8 b) -+{ -+ return a << b; -+} -+ -+regparm3 s64 WIN_FUNC(_allshr,2) -+ (s64 a, u8 b) -+{ -+ return a >> b; -+} -+ -+regparm3 u64 WIN_FUNC(_aullshr,2) -+ (u64 a, u8 b) -+{ -+ return a >> b; -+} -+ -+int stricmp(const char *s1, const char *s2) -+{ -+ while (*s1 && tolower(*s1) == tolower(*s2)) { -+ s1++; -+ s2++; -+ } -+ return *s1 - *s2; -+} -+ -+void dump_bytes(const char *ctx, const u8 *from, int len) -+{ -+ int i, j; -+ u8 *buf; -+ -+ buf = kmalloc(len * 3 + 1, irql_gfp()); -+ if (!buf) { -+ ERROR("couldn't allocate memory"); -+ return; -+ } -+ for (i = j = 0; i < len; i++, j += 3) { -+ sprintf(&buf[j], "%02x ", from[i]); -+ } -+ buf[j] = 0; -+ printk(KERN_DEBUG "%s: %p: %s\n", ctx, from, buf); -+ kfree(buf); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/divdi3.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/divdi3.c ---- linux-3.12.2/3rdparty/ndiswrapper/divdi3.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/divdi3.c 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,329 @@ -+/* 64-bit multiplication and division -+ Copyright (C) 1989, 1992-1999, 2000, 2001, 2002, 2003 -+ Free Software Foundation, Inc. -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library 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 -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, write to the Free -+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ 02111-1307 USA. */ -+ -+#include -+#include -+ -+#if BITS_PER_LONG != 32 -+#error This is for 32-bit targets only -+#endif -+ -+typedef unsigned int UQItype __attribute__ ((mode (QI))); -+typedef int SItype __attribute__ ((mode (SI))); -+typedef unsigned int USItype __attribute__ ((mode (SI))); -+typedef int DItype __attribute__ ((mode (DI))); -+typedef unsigned int UDItype __attribute__ ((mode (DI))); -+#define Wtype SItype -+#define HWtype SItype -+#define DWtype DItype -+#define UWtype USItype -+#define UHWtype USItype -+#define UDWtype UDItype -+#define W_TYPE_SIZE 32 -+ -+#include "longlong.h" -+ -+#if defined(__BIG_ENDIAN) -+struct DWstruct { Wtype high, low;}; -+#elif defined(__LITTLE_ENDIAN) -+struct DWstruct { Wtype low, high;}; -+#else -+#error Unhandled endianity -+#endif -+typedef union { struct DWstruct s; DWtype ll; } DWunion; -+ -+/* Prototypes of exported functions. */ -+extern DWtype __divdi3 (DWtype u, DWtype v); -+extern DWtype __moddi3 (DWtype u, DWtype v); -+extern UDWtype __udivdi3 (UDWtype u, UDWtype v); -+extern UDWtype __umoddi3 (UDWtype u, UDWtype v); -+ -+static UDWtype -+__udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) -+{ -+ DWunion ww; -+ DWunion nn, dd; -+ DWunion rr; -+ UWtype d0, d1, n0, n1, n2; -+ UWtype q0, q1; -+ UWtype b, bm; -+ -+ nn.ll = n; -+ dd.ll = d; -+ -+ d0 = dd.s.low; -+ d1 = dd.s.high; -+ n0 = nn.s.low; -+ n1 = nn.s.high; -+ -+#if !UDIV_NEEDS_NORMALIZATION -+ if (d1 == 0) -+ { -+ if (d0 > n1) -+ { -+ /* 0q = nn / 0D */ -+ -+ udiv_qrnnd (q0, n0, n1, n0, d0); -+ q1 = 0; -+ -+ /* Remainder in n0. */ -+ } -+ else -+ { -+ /* qq = NN / 0d */ -+ -+ if (d0 == 0) -+ d0 = 1 / d0; /* Divide intentionally by zero. */ -+ -+ udiv_qrnnd (q1, n1, 0, n1, d0); -+ udiv_qrnnd (q0, n0, n1, n0, d0); -+ -+ /* Remainder in n0. */ -+ } -+ -+ if (rp != NULL) -+ { -+ rr.s.low = n0; -+ rr.s.high = 0; -+ *rp = rr.ll; -+ } -+ } -+ -+#else /* UDIV_NEEDS_NORMALIZATION */ -+ -+ if (d1 == 0) -+ { -+ if (d0 > n1) -+ { -+ /* 0q = nn / 0D */ -+ -+ count_leading_zeros (bm, d0); -+ -+ if (bm != 0) -+ { -+ /* Normalize, i.e. make the most significant bit of the -+ denominator set. */ -+ -+ d0 = d0 << bm; -+ n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); -+ n0 = n0 << bm; -+ } -+ -+ udiv_qrnnd (q0, n0, n1, n0, d0); -+ q1 = 0; -+ -+ /* Remainder in n0 >> bm. */ -+ } -+ else -+ { -+ /* qq = NN / 0d */ -+ -+ if (d0 == 0) -+ d0 = 1 / d0; /* Divide intentionally by zero. */ -+ -+ count_leading_zeros (bm, d0); -+ -+ if (bm == 0) -+ { -+ /* From (n1 >= d0) /\ (the most significant bit of d0 is set), -+ conclude (the most significant bit of n1 is set) /\ (the -+ leading quotient digit q1 = 1). -+ -+ This special case is necessary, not an optimization. -+ (Shifts counts of W_TYPE_SIZE are undefined.) */ -+ -+ n1 -= d0; -+ q1 = 1; -+ } -+ else -+ { -+ /* Normalize. */ -+ -+ b = W_TYPE_SIZE - bm; -+ -+ d0 = d0 << bm; -+ n2 = n1 >> b; -+ n1 = (n1 << bm) | (n0 >> b); -+ n0 = n0 << bm; -+ -+ udiv_qrnnd (q1, n1, n2, n1, d0); -+ } -+ -+ /* n1 != d0... */ -+ -+ udiv_qrnnd (q0, n0, n1, n0, d0); -+ -+ /* Remainder in n0 >> bm. */ -+ } -+ -+ if (rp != NULL) -+ { -+ rr.s.low = n0 >> bm; -+ rr.s.high = 0; -+ *rp = rr.ll; -+ } -+ } -+#endif /* UDIV_NEEDS_NORMALIZATION */ -+ -+ else -+ { -+ if (d1 > n1) -+ { -+ /* 00 = nn / DD */ -+ -+ q0 = 0; -+ q1 = 0; -+ -+ /* Remainder in n1n0. */ -+ if (rp != NULL) -+ { -+ rr.s.low = n0; -+ rr.s.high = n1; -+ *rp = rr.ll; -+ } -+ } -+ else -+ { -+ /* 0q = NN / dd */ -+ -+ count_leading_zeros (bm, d1); -+ if (bm == 0) -+ { -+ /* From (n1 >= d1) /\ (the most significant bit of d1 is set), -+ conclude (the most significant bit of n1 is set) /\ (the -+ quotient digit q0 = 0 or 1). -+ -+ This special case is necessary, not an optimization. */ -+ -+ /* The condition on the next line takes advantage of that -+ n1 >= d1 (true due to program flow). */ -+ if (n1 > d1 || n0 >= d0) -+ { -+ q0 = 1; -+ sub_ddmmss (n1, n0, n1, n0, d1, d0); -+ } -+ else -+ q0 = 0; -+ -+ q1 = 0; -+ -+ if (rp != NULL) -+ { -+ rr.s.low = n0; -+ rr.s.high = n1; -+ *rp = rr.ll; -+ } -+ } -+ else -+ { -+ UWtype m1, m0; -+ /* Normalize. */ -+ -+ b = W_TYPE_SIZE - bm; -+ -+ d1 = (d1 << bm) | (d0 >> b); -+ d0 = d0 << bm; -+ n2 = n1 >> b; -+ n1 = (n1 << bm) | (n0 >> b); -+ n0 = n0 << bm; -+ -+ udiv_qrnnd (q0, n1, n2, n1, d1); -+ umul_ppmm (m1, m0, q0, d0); -+ -+ if (m1 > n1 || (m1 == n1 && m0 > n0)) -+ { -+ q0--; -+ sub_ddmmss (m1, m0, m1, m0, d1, d0); -+ } -+ -+ q1 = 0; -+ -+ /* Remainder in (n1n0 - m1m0) >> bm. */ -+ if (rp != NULL) -+ { -+ sub_ddmmss (n1, n0, n1, n0, m1, m0); -+ rr.s.low = (n1 << b) | (n0 >> bm); -+ rr.s.high = n1 >> bm; -+ *rp = rr.ll; -+ } -+ } -+ } -+ } -+ -+ ww.s.low = q0; -+ ww.s.high = q1; -+ return ww.ll; -+} -+ -+DWtype -+__divdi3 (DWtype u, DWtype v) -+{ -+ Wtype c = 0; -+ DWtype w; -+ -+ if (u < 0) -+ { -+ c = ~c; -+ u = -u; -+ } -+ if (v < 0) -+ { -+ c = ~c; -+ v = -v; -+ } -+ w = __udivmoddi4 (u, v, NULL); -+ if (c) -+ w = -w; -+ return w; -+} -+ -+DWtype -+__moddi3 (DWtype u, DWtype v) -+{ -+ Wtype c = 0; -+ DWtype w; -+ -+ if (u < 0) -+ { -+ c = ~c; -+ u = -u; -+ } -+ if (v < 0) -+ v = -v; -+ __udivmoddi4 (u, v, &w); -+ if (c) -+ w = -w; -+ return w; -+} -+ -+UDWtype -+__udivdi3 (UDWtype u, UDWtype v) -+{ -+ return __udivmoddi4 (u, v, NULL); -+} -+ -+UDWtype -+__umoddi3 (UDWtype u, UDWtype v) -+{ -+ UDWtype w; -+ -+ __udivmoddi4 (u, v, &w); -+ return w; -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/hal.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/hal.c ---- linux-3.12.2/3rdparty/ndiswrapper/hal.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/hal.c 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,157 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ntoskernel.h" -+#include "hal_exports.h" -+ -+wstdcall void WIN_FUNC(WRITE_PORT_ULONG,2) -+ (ULONG_PTR port, ULONG value) -+{ -+ outl(value, port); -+} -+ -+wstdcall ULONG WIN_FUNC(READ_PORT_ULONG,1) -+ (ULONG_PTR port) -+{ -+ return inl(port); -+} -+ -+wstdcall void WIN_FUNC(WRITE_PORT_USHORT,2) -+ (ULONG_PTR port, USHORT value) -+{ -+ outw(value, port); -+} -+ -+wstdcall USHORT WIN_FUNC(READ_PORT_USHORT,1) -+ (ULONG_PTR port) -+{ -+ return inw(port); -+} -+ -+wstdcall void WIN_FUNC(WRITE_PORT_UCHAR,2) -+ (ULONG_PTR port, UCHAR value) -+{ -+ outb(value, port); -+} -+ -+wstdcall UCHAR WIN_FUNC(READ_PORT_UCHAR,1) -+ (ULONG_PTR port) -+{ -+ return inb(port); -+} -+ -+wstdcall void WIN_FUNC(WRITE_PORT_BUFFER_USHORT,3) -+ (ULONG_PTR port, USHORT *buf, ULONG count) -+{ -+ outsw(port, buf, count); -+} -+ -+wstdcall void WIN_FUNC(READ_PORT_BUFFER_USHORT,3) -+ (ULONG_PTR port, USHORT *buf, ULONG count) -+{ -+ insw(port, buf, count); -+} -+ -+wstdcall void WIN_FUNC(WRITE_PORT_BUFFER_ULONG,3) -+ (ULONG_PTR port, ULONG *buf, ULONG count) -+{ -+ outsl(port, buf, count); -+} -+ -+wstdcall void WIN_FUNC(READ_PORT_BUFFER_ULONG,3) -+ (ULONG_PTR port, ULONG *buf, ULONG count) -+{ -+ insl(port, buf, count); -+} -+ -+wstdcall USHORT WIN_FUNC(READ_REGISTER_USHORT,1) -+ (void __iomem *reg) -+{ -+ return readw(reg); -+} -+ -+wstdcall void WIN_FUNC(WRITE_REGISTER_ULONG,2) -+ (void __iomem *reg, UINT val) -+{ -+ writel(val, reg); -+} -+ -+wstdcall void WIN_FUNC(WRITE_REGISTER_USHORT,2) -+ (void __iomem *reg, USHORT val) -+{ -+ writew(val, reg); -+} -+ -+wstdcall void WIN_FUNC(WRITE_REGISTER_UCHAR,2) -+ (void __iomem *reg, UCHAR val) -+{ -+ writeb(val, reg); -+} -+ -+wstdcall void WIN_FUNC(KeStallExecutionProcessor,1) -+ (ULONG usecs) -+{ -+ udelay(usecs); -+} -+ -+wstdcall KIRQL WIN_FUNC(KeGetCurrentIrql,0) -+ (void) -+{ -+ return current_irql(); -+} -+ -+wfastcall KIRQL WIN_FUNC(KfRaiseIrql,1) -+ (KIRQL newirql) -+{ -+ return raise_irql(newirql); -+} -+ -+wfastcall void WIN_FUNC(KfLowerIrql,1) -+ (KIRQL oldirql) -+{ -+ lower_irql(oldirql); -+} -+ -+wfastcall KIRQL WIN_FUNC(KfAcquireSpinLock,1) -+ (NT_SPIN_LOCK *lock) -+{ -+ return nt_spin_lock_irql(lock, DISPATCH_LEVEL); -+} -+ -+wfastcall void WIN_FUNC(KfReleaseSpinLock,2) -+ (NT_SPIN_LOCK *lock, KIRQL oldirql) -+{ -+ nt_spin_unlock_irql(lock, oldirql); -+} -+ -+wfastcall void WIN_FUNC(KefAcquireSpinLockAtDpcLevel,1) -+ (NT_SPIN_LOCK *lock) -+{ -+#ifdef DEBUG_IRQL -+ if (current_irql() != DISPATCH_LEVEL) -+ ERROR("irql != DISPATCH_LEVEL"); -+#endif -+ nt_spin_lock(lock); -+} -+ -+wfastcall void WIN_FUNC(KefReleaseSpinLockFromDpcLevel,1) -+ (NT_SPIN_LOCK *lock) -+{ -+#ifdef DEBUG_IRQL -+ if (current_irql() != DISPATCH_LEVEL) -+ ERROR("irql != DISPATCH_LEVEL"); -+#endif -+ nt_spin_unlock(lock); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.c ---- linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.c 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,2001 @@ -+ /* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "iw_ndis.h" -+#include "wrapndis.h" -+ -+#ifdef CONFIG_WIRELESS_EXT -+ -+static int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, -+ 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; -+ -+static const char *network_names[] = {"IEEE 802.11FH", "IEEE 802.11b", -+ "IEEE 802.11a", "IEEE 802.11g", "Auto"}; -+ -+static int set_essid(struct ndis_device *wnd, const char *ssid, int ssid_len) -+{ -+ NDIS_STATUS res; -+ struct ndis_essid req; -+ -+ if (ssid_len > NDIS_ESSID_MAX_SIZE) -+ return -EINVAL; -+ -+ memset(&req, 0, sizeof(req)); -+ req.length = ssid_len; -+ if (ssid_len) -+ memcpy(&req.essid, ssid, ssid_len); -+ -+ res = mp_set(wnd, OID_802_11_SSID, &req, sizeof(req)); -+ if (res) { -+ WARNING("setting essid failed (%08X)", res); -+ EXIT2(return -EINVAL); -+ } -+ memcpy(&wnd->essid, &req, sizeof(req)); -+ EXIT2(return 0); -+} -+ -+static int set_iw_auth_mode(struct ndis_device *wnd, int wpa_version, -+ int auth_80211_alg) -+{ -+ NDIS_STATUS res; -+ ULONG auth_mode; -+ -+ ENTER2("%d, %d", wpa_version, auth_80211_alg); -+ if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) { -+ if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X) -+ auth_mode = Ndis802_11AuthModeWPA2; -+ else -+ auth_mode = Ndis802_11AuthModeWPA2PSK; -+ } else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) { -+ if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X) -+ auth_mode = Ndis802_11AuthModeWPA; -+ else if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_PSK) -+ auth_mode = Ndis802_11AuthModeWPAPSK; -+ else -+ auth_mode = Ndis802_11AuthModeWPANone; -+ } else if (auth_80211_alg & IW_AUTH_ALG_SHARED_KEY) { -+ if (auth_80211_alg & IW_AUTH_ALG_OPEN_SYSTEM) -+ auth_mode = Ndis802_11AuthModeAutoSwitch; -+ else -+ auth_mode = Ndis802_11AuthModeShared; -+ } else -+ auth_mode = Ndis802_11AuthModeOpen; -+ -+ res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode); -+ if (res) { -+ WARNING("setting auth mode to %u failed (%08X)", -+ auth_mode, res); -+ if (res == NDIS_STATUS_INVALID_DATA) -+ EXIT2(return -EINVAL); -+ return -EOPNOTSUPP; -+ } -+ wnd->iw_auth_wpa_version = wpa_version; -+ wnd->iw_auth_80211_alg = auth_80211_alg; -+ EXIT2(return 0); -+} -+ -+static int set_auth_mode(struct ndis_device *wnd) -+{ -+ return set_iw_auth_mode(wnd, wnd->iw_auth_wpa_version, -+ wnd->iw_auth_80211_alg); -+} -+ -+static enum ndis_priv_filter ndis_priv_mode(struct ndis_device *wnd) -+{ -+ if (wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2 || -+ wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA) -+ return Ndis802_11PrivFilter8021xWEP; -+ else -+ return Ndis802_11PrivFilterAcceptAll; -+} -+ -+static int set_priv_filter(struct ndis_device *wnd) -+{ -+ NDIS_STATUS res; -+ ULONG flags; -+ -+ flags = ndis_priv_mode(wnd); -+ ENTER2("filter: %d", flags); -+ res = mp_set_int(wnd, OID_802_11_PRIVACY_FILTER, flags); -+ if (res) -+ TRACE2("setting privacy filter to %d failed (%08X)", -+ flags, res); -+ EXIT2(return 0); -+} -+ -+static int set_encr_mode(struct ndis_device *wnd) -+{ -+ return set_iw_encr_mode(wnd, wnd->iw_auth_cipher_pairwise, -+ wnd->iw_auth_cipher_group); -+} -+ -+static int set_assoc_params(struct ndis_device *wnd) -+{ -+ TRACE2("wpa_version=0x%x auth_alg=0x%x key_mgmt=0x%x " -+ "cipher_pairwise=0x%x cipher_group=0x%x", -+ wnd->iw_auth_wpa_version, wnd->iw_auth_80211_alg, -+ wnd->iw_auth_key_mgmt, wnd->iw_auth_cipher_pairwise, -+ wnd->iw_auth_cipher_group); -+ set_auth_mode(wnd); -+ set_priv_filter(wnd); -+ set_encr_mode(wnd); -+ return 0; -+} -+ -+static int iw_set_essid(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ int length; -+ -+ ENTER2(""); -+ /* there is no way to turn off essid other than to set to -+ * random bytes; instead, we use off to mean any */ -+ if (wrqu->essid.flags) { -+ length = wrqu->essid.length; -+ /* Strip '\0' appended by wireless extensions 19 and older */ -+ if (length > 0 && extra[length - 1] == '\0') -+ length--; -+ TRACE2("%d", length); -+ if (length <= 0 || length > NDIS_ESSID_MAX_SIZE) -+ EXIT2(return -EINVAL); -+ } else -+ length = 0; -+ -+ set_assoc_params(wnd); -+ -+ if (set_essid(wnd, extra, length)) -+ EXIT2(return -EINVAL); -+ -+ EXIT2(return 0); -+} -+ -+static int iw_get_essid(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ struct ndis_essid req; -+ -+ ENTER2(""); -+ memset(&req, 0, sizeof(req)); -+ res = mp_query(wnd, OID_802_11_SSID, &req, sizeof(req)); -+ if (res) { -+ WARNING("getting essid failed (%08X)", res); -+ EXIT2(return -EOPNOTSUPP); -+ } -+ memcpy(extra, req.essid, req.length); -+ if (req.length > 0) -+ wrqu->essid.flags = 1; -+ else -+ wrqu->essid.flags = 0; -+ wrqu->essid.length = req.length; -+ EXIT2(return 0); -+} -+ -+/* index must be 0 - N, as per NDIS */ -+static int add_wep_key(struct ndis_device *wnd, char *key, int key_len, -+ int index) -+{ -+ struct ndis_encr_key ndis_key; -+ NDIS_STATUS res; -+ -+ ENTER2("key index: %d, length: %d", index, key_len); -+ if (key_len <= 0 || key_len > NDIS_ENCODING_TOKEN_MAX) { -+ WARNING("invalid key length (%d)", key_len); -+ EXIT2(return -EINVAL); -+ } -+ if (index < 0 || index >= MAX_ENCR_KEYS) { -+ WARNING("invalid key index (%d)", index); -+ EXIT2(return -EINVAL); -+ } -+ ndis_key.struct_size = sizeof(ndis_key); -+ ndis_key.length = key_len; -+ memcpy(&ndis_key.key, key, key_len); -+ ndis_key.index = index; -+ -+ if (index == wnd->encr_info.tx_key_index) { -+ ndis_key.index |= (1 << 31); -+ res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104, -+ IW_AUTH_CIPHER_NONE); -+ if (res) -+ WARNING("encryption couldn't be enabled (%08X)", res); -+ } -+ TRACE2("key %d: " MACSTRSEP, index, MAC2STR(key)); -+ res = mp_set(wnd, OID_802_11_ADD_WEP, &ndis_key, sizeof(ndis_key)); -+ if (res) { -+ WARNING("adding encryption key %d failed (%08X)", -+ index+1, res); -+ EXIT2(return -EINVAL); -+ } -+ -+ /* Atheros driver messes up ndis_key during ADD_WEP, so -+ * don't rely on that; instead use info in key and key_len */ -+ wnd->encr_info.keys[index].length = key_len; -+ memcpy(&wnd->encr_info.keys[index].key, key, key_len); -+ -+ EXIT2(return 0); -+} -+ -+static int set_infra_mode(struct ndis_device *wnd, -+ enum ndis_infrastructure_mode mode) -+{ -+ NDIS_STATUS res; -+ unsigned int i; -+ -+ ENTER2("%d", mode); -+ res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, -+ &wnd->infrastructure_mode); -+ if (res != NDIS_STATUS_SUCCESS) { -+ WARNING("getting operating mode failed (%08X)", res); -+ EXIT2(return -EINVAL); -+ } -+ if (wnd->infrastructure_mode == mode) -+ EXIT2(return 0); -+ res = mp_set_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, mode); -+ if (res) { -+ WARNING("setting operating mode to %d failed (%08X)", -+ mode, res); -+ EXIT2(return -EINVAL); -+ } -+ /* NDIS drivers clear keys when infrastructure mode is -+ * changed. But Linux tools assume otherwise. So set the -+ * keys */ -+ if (wnd->iw_auth_key_mgmt == 0 || -+ wnd->iw_auth_key_mgmt == IW_AUTH_KEY_MGMT_802_1X) { -+ for (i = 0; i < MAX_ENCR_KEYS; i++) { -+ if (wnd->encr_info.keys[i].length > 0) -+ add_wep_key(wnd, wnd->encr_info.keys[i].key, -+ wnd->encr_info.keys[i].length, i); -+ } -+ } -+ wnd->infrastructure_mode = mode; -+ EXIT2(return 0); -+} -+ -+static int iw_set_infra_mode(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ enum ndis_infrastructure_mode ndis_mode; -+ -+ ENTER2("%d", wrqu->mode); -+ switch (wrqu->mode) { -+ case IW_MODE_ADHOC: -+ ndis_mode = Ndis802_11IBSS; -+ break; -+ case IW_MODE_INFRA: -+ ndis_mode = Ndis802_11Infrastructure; -+ break; -+ case IW_MODE_AUTO: -+ ndis_mode = Ndis802_11AutoUnknown; -+ break; -+ default: -+ EXIT2(return -EINVAL); -+ } -+ -+ if (set_infra_mode(wnd, ndis_mode)) -+ EXIT2(return -EINVAL); -+ -+ EXIT2(return 0); -+} -+ -+static int iw_get_infra_mode(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ int ndis_mode, iw_mode; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &ndis_mode); -+ if (res) { -+ WARNING("getting operating mode failed (%08X)", res); -+ EXIT2(return -EOPNOTSUPP); -+ } -+ -+ switch (ndis_mode) { -+ case Ndis802_11IBSS: -+ iw_mode = IW_MODE_ADHOC; -+ break; -+ case Ndis802_11Infrastructure: -+ iw_mode = IW_MODE_INFRA; -+ break; -+ case Ndis802_11AutoUnknown: -+ iw_mode = IW_MODE_AUTO; -+ break; -+ default: -+ ERROR("invalid operating mode (%u)", ndis_mode); -+ EXIT2(return -EINVAL); -+ } -+ wrqu->mode = iw_mode; -+ EXIT2(return 0); -+} -+ -+static const char *network_type_to_name(int net_type) -+{ -+ if (net_type >= 0 && net_type < ARRAY_SIZE(network_names)) -+ return network_names[net_type]; -+ else -+ return network_names[ARRAY_SIZE(network_names) - 1]; -+} -+ -+static int iw_get_network_type(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ unsigned int network_type; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ res = mp_query_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, -+ &network_type); -+ if (res) { -+ WARNING("getting network type failed: %08X", res); -+ network_type = -1; -+ } -+ strncpy(wrqu->name, network_type_to_name(network_type), -+ sizeof(wrqu->name) - 1); -+ wrqu->name[sizeof(wrqu->name)-1] = 0; -+ return 0; -+} -+ -+static int iw_get_freq(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ struct ndis_configuration req; -+ -+ ENTER2(""); -+ memset(&req, 0, sizeof(req)); -+ res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); -+ if (res) { -+ WARNING("getting configuration failed (%08X)", res); -+ EXIT2(return -EOPNOTSUPP); -+ } -+ -+ memset(&(wrqu->freq), 0, sizeof(struct iw_freq)); -+ -+ /* see comment in wireless.h above the "struct iw_freq" -+ definition for an explanation of this if -+ NOTE: 1000000 is due to the kHz -+ */ -+ if (req.ds_config > 1000000) { -+ wrqu->freq.m = req.ds_config / 10; -+ wrqu->freq.e = 1; -+ } -+ else -+ wrqu->freq.m = req.ds_config; -+ -+ /* convert from kHz to Hz */ -+ wrqu->freq.e += 3; -+ -+ return 0; -+} -+ -+static int iw_set_freq(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ struct ndis_configuration req; -+ -+ ENTER2(""); -+ /* this OID is valid only when not associated */ -+ if (netif_carrier_ok(wnd->net_dev)) -+ EXIT2(return 0); -+ memset(&req, 0, sizeof(req)); -+ res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); -+ if (res) { -+ WARNING("getting configuration failed (%08X)", res); -+ EXIT2(return 0); -+ } -+ -+ if (wrqu->freq.m < 1000 && wrqu->freq.e == 0) { -+ if (wrqu->freq.m >= 1 && wrqu->freq.m <= ARRAY_SIZE(freq_chan)) -+ req.ds_config = freq_chan[wrqu->freq.m - 1] * 1000; -+ else -+ return -EINVAL; -+ } else { -+ int i; -+ req.ds_config = wrqu->freq.m; -+ for (i = wrqu->freq.e; i > 0; i--) -+ req.ds_config *= 10; -+ req.ds_config /= 1000; -+ } -+ res = mp_set(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); -+ if (res) -+ WARNING("setting configuration failed (%08X)", res); -+ return 0; -+} -+ -+static int iw_get_tx_power(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ ndis_tx_power_level ndis_power; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, -+ &ndis_power, sizeof(ndis_power)); -+ if (res) -+ return -EOPNOTSUPP; -+ wrqu->txpower.flags = IW_TXPOW_MWATT; -+ wrqu->txpower.disabled = 0; -+ wrqu->txpower.fixed = 0; -+ wrqu->txpower.value = ndis_power; -+ return 0; -+} -+ -+static int iw_set_tx_power(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ ndis_tx_power_level ndis_power; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ if (wrqu->txpower.disabled) -+ ndis_power = 0; -+ else { -+ if (wrqu->txpower.flags == IW_TXPOW_MWATT) -+ ndis_power = wrqu->txpower.value; -+ else { // wrqu->txpower.flags == IW_TXPOW_DBM -+ if (wrqu->txpower.value > 20) -+ ndis_power = 128; -+ else if (wrqu->txpower.value < -43) -+ ndis_power = 127; -+ else { -+ signed char tmp; -+ tmp = wrqu->txpower.value; -+ tmp = -12 - tmp; -+ tmp <<= 2; -+ ndis_power = (unsigned char)tmp; -+ } -+ } -+ } -+ TRACE2("%d", ndis_power); -+ res = mp_set(wnd, OID_802_11_TX_POWER_LEVEL, -+ &ndis_power, sizeof(ndis_power)); -+ if (res) -+ EXIT2(return -EOPNOTSUPP); -+ if (ndis_power == 0) -+ res = disassociate(wnd, 0); -+ EXIT2(return 0); -+} -+ -+static int iw_get_bitrate(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ ULONG ndis_rate; -+ int res; -+ -+ ENTER2(""); -+ res = mp_query(wnd, OID_GEN_LINK_SPEED, &ndis_rate, sizeof(ndis_rate)); -+ if (res) { -+ WARNING("getting bitrate failed (%08X)", res); -+ ndis_rate = 0; -+ } -+ -+ wrqu->bitrate.value = ndis_rate * 100; -+ return 0; -+} -+ -+static int iw_set_bitrate(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ int i, n; -+ NDIS_STATUS res; -+ UCHAR rates[NDIS_MAX_RATES_EX]; -+ -+ ENTER2(""); -+ if (wrqu->bitrate.fixed == 0) -+ EXIT2(return 0); -+ -+ res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES, &rates, -+ sizeof(rates), &n, NULL); -+ if (res) { -+ WARNING("getting bit rate failed (%08X)", res); -+ EXIT2(return 0); -+ } -+ for (i = 0; i < n; i++) { -+ if (rates[i] & 0x80) -+ continue; -+ if ((rates[i] & 0x7f) * 500000 > wrqu->bitrate.value) { -+ TRACE2("setting rate %d to 0", -+ (rates[i] & 0x7f) * 500000); -+ rates[i] = 0; -+ } -+ } -+ -+ res = mp_set(wnd, OID_802_11_DESIRED_RATES, &rates, n); -+ if (res) { -+ WARNING("setting bit rate failed (%08X)", res); -+ EXIT2(return 0); -+ } -+ -+ return 0; -+} -+ -+static int iw_set_dummy(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ /* Do nothing. Used for ioctls that are not implemented. */ -+ return 0; -+} -+ -+static int iw_get_rts_threshold(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ ndis_rts_threshold threshold; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ res = mp_query(wnd, OID_802_11_RTS_THRESHOLD, -+ &threshold, sizeof(threshold)); -+ if (res) -+ return -EOPNOTSUPP; -+ -+ wrqu->rts.value = threshold; -+ return 0; -+} -+ -+static int iw_set_rts_threshold(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ ndis_rts_threshold threshold; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ threshold = wrqu->rts.value; -+ res = mp_set(wnd, OID_802_11_RTS_THRESHOLD, -+ &threshold, sizeof(threshold)); -+ if (res == NDIS_STATUS_INVALID_DATA) -+ return -EINVAL; -+ if (res) -+ return -EOPNOTSUPP; -+ -+ return 0; -+} -+ -+static int iw_get_frag_threshold(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ ndis_fragmentation_threshold frag_threshold; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, -+ &frag_threshold, sizeof(frag_threshold)); -+ if (res) -+ return -ENOTSUPP; -+ -+ wrqu->frag.value = frag_threshold; -+ return 0; -+} -+ -+static int iw_set_frag_threshold(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ ndis_rts_threshold threshold; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ threshold = wrqu->frag.value; -+ res = mp_set(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, -+ &threshold, sizeof(threshold)); -+ if (res == NDIS_STATUS_INVALID_DATA) -+ return -EINVAL; -+ if (res) -+ return -EOPNOTSUPP; -+ return 0; -+} -+ -+int get_ap_address(struct ndis_device *wnd, mac_address ap_addr) -+{ -+ NDIS_STATUS res; -+ -+ res = mp_query(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN); -+ TRACE2(MACSTRSEP, MAC2STR(ap_addr)); -+ if (res) { -+ TRACE2("res: %08X", res); -+ memset(ap_addr, 0x0, ETH_ALEN); -+ EXIT2(return -EOPNOTSUPP); -+ } -+ EXIT2(return 0); -+} -+ -+static int iw_get_ap_address(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ mac_address ap_addr; -+ -+ ENTER2(""); -+ get_ap_address(wnd, ap_addr); -+ memcpy(wrqu->ap_addr.sa_data, ap_addr, ETH_ALEN); -+ wrqu->ap_addr.sa_family = ARPHRD_ETHER; -+ EXIT2(return 0); -+} -+ -+static int iw_set_ap_address(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ mac_address ap_addr; -+ -+ ENTER2(""); -+ memcpy(ap_addr, wrqu->ap_addr.sa_data, ETH_ALEN); -+ TRACE2(MACSTRSEP, MAC2STR(ap_addr)); -+ res = mp_set(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN); -+ /* user apps may set ap's mac address, which is not required; -+ * they may fail to work if this function fails, so return -+ * success */ -+ if (res) -+ WARNING("setting AP mac address failed (%08X)", res); -+ -+ EXIT2(return 0); -+} -+ -+int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode) -+{ -+ NDIS_STATUS res; -+ -+ ENTER2("%d", auth_mode); -+ res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode); -+ if (res) { -+ WARNING("setting auth mode to %u failed (%08X)", -+ auth_mode, res); -+ if (res == NDIS_STATUS_INVALID_DATA) -+ EXIT2(return -EINVAL); -+ return -EOPNOTSUPP; -+ } -+ switch (auth_mode) { -+ case Ndis802_11AuthModeWPA: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA; -+ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X; -+ break; -+ case Ndis802_11AuthModeWPAPSK: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA; -+ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; -+ case Ndis802_11AuthModeWPANone: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; -+ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; -+ break; -+ case Ndis802_11AuthModeWPA2: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2; -+ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X; -+ break; -+ case Ndis802_11AuthModeWPA2PSK: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2; -+ wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; -+ break; -+ case Ndis802_11AuthModeOpen: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; -+ wnd->iw_auth_80211_alg = IW_AUTH_ALG_OPEN_SYSTEM; -+ break; -+ case Ndis802_11AuthModeShared: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; -+ wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY; -+ break; -+ case Ndis802_11AuthModeAutoSwitch: -+ wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; -+ wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY; -+ wnd->iw_auth_80211_alg |= IW_AUTH_ALG_OPEN_SYSTEM; -+ break; -+ default: -+ WARNING("invalid authentication algorithm: %d", auth_mode); -+ break; -+ } -+ EXIT2(return 0); -+} -+ -+int get_ndis_auth_mode(struct ndis_device *wnd) -+{ -+ ULONG mode; -+ NDIS_STATUS res; -+ -+ res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &mode); -+ if (res) { -+ WARNING("getting authentication mode failed (%08X)", res); -+ EXIT2(return -EOPNOTSUPP); -+ } -+ TRACE2("%d", mode); -+ return mode; -+} -+ -+int set_iw_encr_mode(struct ndis_device *wnd, int cipher_pairwise, -+ int cipher_groupwise) -+{ -+ NDIS_STATUS res; -+ ULONG ndis_mode; -+ -+ ENTER2("%d, %d", cipher_pairwise, cipher_groupwise); -+ if (cipher_pairwise & IW_AUTH_CIPHER_CCMP) -+ ndis_mode = Ndis802_11Encryption3Enabled; -+ else if (cipher_pairwise & IW_AUTH_CIPHER_TKIP) -+ ndis_mode = Ndis802_11Encryption2Enabled; -+ else if (cipher_pairwise & -+ (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) -+ ndis_mode = Ndis802_11Encryption1Enabled; -+ else if (cipher_groupwise & IW_AUTH_CIPHER_CCMP) -+ ndis_mode = Ndis802_11Encryption3Enabled; -+ else if (cipher_groupwise & IW_AUTH_CIPHER_TKIP) -+ ndis_mode = Ndis802_11Encryption2Enabled; -+ else -+ ndis_mode = Ndis802_11EncryptionDisabled; -+ -+ res = mp_set_int(wnd, OID_802_11_ENCRYPTION_STATUS, ndis_mode); -+ if (res) { -+ WARNING("setting encryption mode to %u failed (%08X)", -+ ndis_mode, res); -+ if (res == NDIS_STATUS_INVALID_DATA) -+ EXIT2(return -EINVAL); -+ return -EOPNOTSUPP; -+ } -+ wnd->iw_auth_cipher_pairwise = cipher_pairwise; -+ wnd->iw_auth_cipher_group = cipher_groupwise; -+ EXIT2(return 0); -+} -+ -+int get_ndis_encr_mode(struct ndis_device *wnd) -+{ -+ ULONG mode; -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &mode); -+ if (res) { -+ WARNING("getting encryption status failed (%08X)", res); -+ EXIT2(return -EOPNOTSUPP); -+ } else -+ EXIT2(return mode); -+} -+ -+static int iw_get_encr(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ int index, mode; -+ struct encr_info *encr_info = &wnd->encr_info; -+ -+ ENTER2("wnd = %p", wnd); -+ wrqu->data.length = 0; -+ extra[0] = 0; -+ -+ index = (wrqu->encoding.flags & IW_ENCODE_INDEX); -+ TRACE2("index = %u", index); -+ if (index > 0) -+ index--; -+ else -+ index = encr_info->tx_key_index; -+ -+ if (index < 0 || index >= MAX_ENCR_KEYS) { -+ WARNING("encryption index out of range (%u)", index); -+ EXIT2(return -EINVAL); -+ } -+ -+ if (index != encr_info->tx_key_index) { -+ if (encr_info->keys[index].length > 0) { -+ wrqu->data.flags |= IW_ENCODE_ENABLED; -+ wrqu->data.length = encr_info->keys[index].length; -+ memcpy(extra, encr_info->keys[index].key, -+ encr_info->keys[index].length); -+ } -+ else -+ wrqu->data.flags |= IW_ENCODE_DISABLED; -+ -+ EXIT2(return 0); -+ } -+ -+ /* transmit key */ -+ mode = get_ndis_encr_mode(wnd); -+ if (mode < 0) -+ EXIT2(return -EOPNOTSUPP); -+ -+ if (mode == Ndis802_11EncryptionDisabled || -+ mode == Ndis802_11EncryptionNotSupported) -+ wrqu->data.flags |= IW_ENCODE_DISABLED; -+ else { -+ if (mode == Ndis802_11Encryption1KeyAbsent || -+ mode == Ndis802_11Encryption2KeyAbsent || -+ mode == Ndis802_11Encryption3KeyAbsent) -+ wrqu->data.flags |= IW_ENCODE_NOKEY; -+ else { -+ wrqu->data.flags |= IW_ENCODE_ENABLED; -+ wrqu->encoding.flags |= index+1; -+ wrqu->data.length = encr_info->keys[index].length; -+ memcpy(extra, encr_info->keys[index].key, -+ encr_info->keys[index].length); -+ } -+ } -+ mode = get_ndis_auth_mode(wnd); -+ if (mode < 0) -+ EXIT2(return -EOPNOTSUPP); -+ -+ if (mode == Ndis802_11AuthModeOpen) -+ wrqu->data.flags |= IW_ENCODE_OPEN; -+ else if (mode == Ndis802_11AuthModeAutoSwitch) -+ wrqu->data.flags |= IW_ENCODE_RESTRICTED; -+ else // Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA etc. -+ wrqu->data.flags |= IW_ENCODE_RESTRICTED; -+ -+ EXIT2(return 0); -+} -+ -+/* remove_key is for both wep and wpa */ -+static int remove_key(struct ndis_device *wnd, int index, -+ mac_address bssid) -+{ -+ NDIS_STATUS res; -+ if (wnd->encr_info.keys[index].length == 0) -+ EXIT2(return 0); -+ wnd->encr_info.keys[index].length = 0; -+ memset(&wnd->encr_info.keys[index].key, 0, -+ sizeof(wnd->encr_info.keys[index].length)); -+ if (wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_TKIP || -+ wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_CCMP || -+ wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_TKIP || -+ wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_CCMP) { -+ struct ndis_remove_key rmkey; -+ rmkey.struct_size = sizeof(rmkey); -+ rmkey.index = index; -+ if (bssid) { -+ /* pairwise key */ -+ if (memcmp(bssid, "\xff\xff\xff\xff\xff\xff", -+ ETH_ALEN) != 0) -+ rmkey.index |= (1 << 30); -+ memcpy(rmkey.bssid, bssid, sizeof(rmkey.bssid)); -+ } else -+ memset(rmkey.bssid, 0xff, sizeof(rmkey.bssid)); -+ if (mp_set(wnd, OID_802_11_REMOVE_KEY, &rmkey, sizeof(rmkey))) -+ EXIT2(return -EINVAL); -+ } else { -+ ndis_key_index keyindex = index; -+ res = mp_set_int(wnd, OID_802_11_REMOVE_WEP, keyindex); -+ if (res) { -+ WARNING("removing encryption key %d failed (%08X)", -+ keyindex, res); -+ EXIT2(return -EINVAL); -+ } -+ } -+ /* if it is transmit key, disable encryption */ -+ if (index == wnd->encr_info.tx_key_index) { -+ res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE, -+ IW_AUTH_CIPHER_NONE); -+ if (res) -+ WARNING("changing encr status failed (%08X)", res); -+ } -+ TRACE2("key %d removed", index); -+ EXIT2(return 0); -+} -+ -+static int iw_set_wep(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ unsigned int index, key_len; -+ struct encr_info *encr_info = &wnd->encr_info; -+ unsigned char *key; -+ -+ ENTER2(""); -+ index = (wrqu->encoding.flags & IW_ENCODE_INDEX); -+ TRACE2("index = %u", index); -+ -+ /* iwconfig gives index as 1 - N */ -+ if (index > 0) -+ index--; -+ else -+ index = encr_info->tx_key_index; -+ -+ if (index >= MAX_ENCR_KEYS) { -+ WARNING("encryption index out of range (%u)", index); -+ EXIT2(return -EINVAL); -+ } -+ -+ /* remove key if disabled */ -+ if (wrqu->data.flags & IW_ENCODE_DISABLED) { -+ if (remove_key(wnd, index, NULL)) -+ EXIT2(return -EINVAL); -+ else -+ EXIT2(return 0); -+ } -+ -+ /* global encryption state (for all keys) */ -+ if (wrqu->data.flags & IW_ENCODE_OPEN) -+ res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen); -+ else // if (wrqu->data.flags & IW_ENCODE_RESTRICTED) -+ res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeShared); -+ if (res) { -+ WARNING("setting authentication mode failed (%08X)", res); -+ EXIT2(return -EINVAL); -+ } -+ -+ TRACE2("key length: %d", wrqu->data.length); -+ -+ if (wrqu->data.length > 0) { -+ key_len = wrqu->data.length; -+ key = extra; -+ } else { // must be set as tx key -+ if (encr_info->keys[index].length == 0) { -+ WARNING("key %d is not set", index+1); -+ EXIT2(return -EINVAL); -+ } -+ key_len = encr_info->keys[index].length; -+ key = encr_info->keys[index].key; -+ encr_info->tx_key_index = index; -+ } -+ -+ if (add_wep_key(wnd, key, key_len, index)) -+ EXIT2(return -EINVAL); -+ -+ if (index == encr_info->tx_key_index) { -+ /* if transmit key is at index other than 0, some -+ * drivers, at least Atheros and TI, want another -+ * (global) non-transmit key to be set; don't know why */ -+ if (index != 0) { -+ int i; -+ for (i = 0; i < MAX_ENCR_KEYS; i++) -+ if (i != index && -+ encr_info->keys[i].length != 0) -+ break; -+ if (i == MAX_ENCR_KEYS) { -+ if (index == 0) -+ i = index + 1; -+ else -+ i = index - 1; -+ if (add_wep_key(wnd, key, key_len, i)) -+ WARNING("couldn't add broadcast key" -+ " at %d", i); -+ } -+ } -+ /* ndis drivers want essid to be set after setting encr */ -+ set_essid(wnd, wnd->essid.essid, wnd->essid.length); -+ } -+ EXIT2(return 0); -+} -+ -+static int iw_set_nick(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ if (wrqu->data.length >= sizeof(wnd->nick)) -+ return -EINVAL; -+ memcpy(wnd->nick, extra, wrqu->data.length); -+ wnd->nick[wrqu->data.length] = 0; -+ return 0; -+} -+ -+static int iw_get_nick(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ wrqu->data.length = strlen(wnd->nick); -+ memcpy(extra, wnd->nick, wrqu->data.length); -+ return 0; -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) && !defined(IW_REQUEST_FLAG_COMPAT) -+#define iwe_stream_add_event(a, b, c, d, e) iwe_stream_add_event(b, c, d, e) -+#define iwe_stream_add_point(a, b, c, d, e) iwe_stream_add_point(b, c, d, e) -+#define iwe_stream_add_value(a, b, c, d, e, f) \ -+ iwe_stream_add_value(b, c, d, e, f) -+#define iwe_stream_lcp_len(a) IW_EV_LCP_LEN -+#endif -+ -+static char *ndis_translate_scan(struct net_device *dev, -+ struct iw_request_info *info, char *event, -+ char *end_buf, void *item) -+{ -+ struct iw_event iwe; -+ char *current_val; -+ char *ret; -+ int i, nrates; -+ unsigned char custom_str[64]; -+ struct ndis_wlan_bssid *bssid; -+ struct ndis_wlan_bssid_ex *bssid_ex; -+ int extended; -+ -+ ENTER2("%p, %p", event, item); -+ bssid = item; -+ bssid_ex = item; -+ extended = (bssid->length > offsetof(struct ndis_wlan_bssid_ex, var)); -+ -+ /* add mac address */ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = SIOCGIWAP; -+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; -+ iwe.len = IW_EV_ADDR_LEN; -+ memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN); -+ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_ADDR_LEN); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ /* add essid */ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = SIOCGIWESSID; -+ iwe.u.data.length = bssid->ssid.length; -+ if (iwe.u.data.length > IW_ESSID_MAX_SIZE) -+ iwe.u.data.length = IW_ESSID_MAX_SIZE; -+ iwe.u.data.flags = 1; -+ iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; -+ ret = iwe_stream_add_point(info, event, end_buf, &iwe, -+ bssid->ssid.essid); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ /* add protocol name */ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = SIOCGIWNAME; -+ strncpy(iwe.u.name, network_type_to_name(bssid->net_type), IFNAMSIZ); -+ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_CHAR_LEN); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ /* add mode */ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = SIOCGIWMODE; -+ if (bssid->mode == Ndis802_11IBSS) -+ iwe.u.mode = IW_MODE_ADHOC; -+ else if (bssid->mode == Ndis802_11Infrastructure) -+ iwe.u.mode = IW_MODE_MASTER; -+ else // if (bssid->mode == Ndis802_11AutoUnknown) -+ iwe.u.mode = IW_MODE_AUTO; -+ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_UINT_LEN); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ /* add freq */ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = SIOCGIWFREQ; -+ iwe.u.freq.m = bssid->config.ds_config; -+ if (bssid->config.ds_config > 1000000) { -+ iwe.u.freq.m = bssid->config.ds_config / 10; -+ iwe.u.freq.e = 1; -+ } -+ else -+ iwe.u.freq.m = bssid->config.ds_config; -+ /* convert from kHz to Hz */ -+ iwe.u.freq.e += 3; -+ iwe.len = IW_EV_FREQ_LEN; -+ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_FREQ_LEN); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ /* add qual */ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = IWEVQUAL; -+ i = 100 * (bssid->rssi - WL_NOISE) / (WL_SIGMAX - WL_NOISE); -+ if (i < 0) -+ i = 0; -+ else if (i > 100) -+ i = 100; -+ iwe.u.qual.level = bssid->rssi; -+ iwe.u.qual.noise = WL_NOISE; -+ iwe.u.qual.qual = i; -+ iwe.len = IW_EV_QUAL_LEN; -+ ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_QUAL_LEN); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ /* add key info */ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = SIOCGIWENCODE; -+ if (bssid->privacy == Ndis802_11PrivFilterAcceptAll) -+ iwe.u.data.flags = IW_ENCODE_DISABLED; -+ else -+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; -+ iwe.u.data.length = 0; -+ iwe.len = IW_EV_POINT_LEN; -+ ret = iwe_stream_add_point(info, event, end_buf, &iwe, -+ bssid->ssid.essid); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ /* add rate */ -+ memset(&iwe, 0, sizeof(iwe)); -+ current_val = event + iwe_stream_lcp_len(info); -+ iwe.cmd = SIOCGIWRATE; -+ if (extended) -+ nrates = ARRAY_SIZE(bssid->rates); -+ else -+ nrates = ARRAY_SIZE(bssid_ex->rates_ex); -+ for (i = 0; i < nrates; i++) { -+ if (bssid_ex->rates_ex[i] & 0x7f) { -+ iwe.u.bitrate.value = ((bssid->rates[i] & 0x7f) * -+ 500000); -+ ret = iwe_stream_add_value(info, event, current_val, -+ end_buf, &iwe, -+ IW_EV_PARAM_LEN); -+ if (ret == current_val) -+ return NULL; -+ current_val = ret; -+ } -+ } -+ -+ if ((current_val - event) > iwe_stream_lcp_len(info)) -+ event = current_val; -+ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = IWEVCUSTOM; -+ sprintf(custom_str, "bcn_int=%d", bssid->config.beacon_period); -+ iwe.u.data.length = strlen(custom_str); -+ ret = iwe_stream_add_point(info, event, end_buf, &iwe, custom_str); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = IWEVCUSTOM; -+ sprintf(custom_str, "atim=%u", bssid->config.atim_window); -+ iwe.u.data.length = strlen(custom_str); -+ ret = iwe_stream_add_point(info, event, end_buf, &iwe, custom_str); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ -+ TRACE2("%d, %zu", bssid->length, sizeof(*bssid)); -+ if (extended) { -+ struct ndis_variable_ies *iep = bssid_ex->var; -+ unsigned char *end = (unsigned char *)&bssid_ex->fixed + -+ bssid_ex->ie_length; -+ -+ while (&iep->length < end && &iep->data[iep->length] <= end) { -+ unsigned char ielen = iep->length + 2; -+ -+ memset(&iwe, 0, sizeof(iwe)); -+ iwe.cmd = IWEVGENIE; -+ iwe.u.data.length = ielen; -+ ret = iwe_stream_add_point(info, event, end_buf, &iwe, -+ (char *)iep); -+ if (ret == event) -+ return NULL; -+ event = ret; -+ iep = (typeof(iep))&iep->data[iep->length]; -+ } -+ } -+ TRACE2("event = %p, current_val = %p", event, current_val); -+ EXIT2(return event); -+} -+ -+static int set_scan(struct ndis_device *wnd) -+{ -+ NDIS_STATUS res; -+ -+ ENTER2(""); -+ res = mp_set(wnd, OID_802_11_BSSID_LIST_SCAN, NULL, 0); -+ if (res) { -+ WARNING("scanning failed (%08X)", res); -+ EXIT2(return -EOPNOTSUPP); -+ } -+ wnd->scan_timestamp = jiffies; -+ EXIT2(return 0); -+} -+ -+static int iw_set_scan(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ return set_scan(wnd); -+} -+ -+static int iw_get_scan(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ unsigned int i, buf_len, needed, data_len; -+ NDIS_STATUS res; -+ struct ndis_bssid_list *bssid_list = NULL; -+ char *event = extra; -+ struct ndis_wlan_bssid *cur_item; -+ -+ ENTER2(""); -+ if (time_before(jiffies, wnd->scan_timestamp + 3 * HZ)) -+ return -EAGAIN; -+ /* try with space for a few scan items */ -+ buf_len = sizeof(ULONG) + offsetof(struct ndis_wlan_bssid_ex, var) * 8; -+ -+ /* Try many times, as the needed space may grow between queries */ -+ for (i = 0; i < 10; i++) { -+ bssid_list = kzalloc(buf_len, GFP_KERNEL); -+ if (!bssid_list) { -+ ERROR("couldn't allocate %u bytes for scan results", -+ buf_len); -+ return -ENOMEM; -+ } -+ -+ needed = 0; -+ data_len = 0; -+ res = mp_query_info(wnd, OID_802_11_BSSID_LIST, bssid_list, -+ buf_len, &data_len, &needed); -+ TRACE2("try %d: given %d bytes, needed %d, written %d", -+ i, buf_len, needed, data_len); -+ if (needed <= buf_len) -+ break; -+ kfree(bssid_list); -+ buf_len = needed; -+ } -+ if (res) { -+ WARNING("getting BSSID list failed (%08X)", res); -+ kfree(bssid_list); -+ EXIT2(return -EOPNOTSUPP); -+ } -+ -+ /* some drivers don't set bssid_list->num_items to 0 if -+ OID_802_11_BSSID_LIST returns no items (prism54 driver, e.g.,) */ -+ TRACE2("items: %d", bssid_list->num_items); -+ cur_item = &bssid_list->bssid[0]; -+ for (i = 0; i < bssid_list->num_items; i++) { -+ TRACE2("item %d: len %d, remaining data %d", -+ i, cur_item->length, data_len); -+ /* drop truncated items */ -+ if (cur_item->length > data_len) -+ break; -+ event = ndis_translate_scan(dev, info, event, -+ extra + wrqu->data.length, -+ cur_item); -+ if (!event) { -+ kfree(bssid_list); -+ return -E2BIG; -+ } -+ data_len -= cur_item->length; -+ cur_item = (struct ndis_wlan_bssid *)((char *)cur_item + -+ cur_item->length); -+ } -+ wrqu->data.length = event - extra; -+ wrqu->data.flags = 0; -+ kfree(bssid_list); -+ EXIT2(return 0); -+} -+ -+static int iw_set_power_mode(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ enum ndis_power power_mode; -+ -+ if (wrqu->power.disabled == 1) -+ power_mode = NDIS_POWER_OFF; -+ else if (wrqu->power.flags & IW_POWER_MIN) -+ power_mode = NDIS_POWER_MIN; -+ else // if (wrqu->power.flags & IW_POWER_MAX) -+ power_mode = NDIS_POWER_MAX; -+ -+ TRACE2("%d", power_mode); -+ res = mp_set(wnd, OID_802_11_POWER_MODE, -+ &power_mode, sizeof(power_mode)); -+ if (res) -+ WARNING("setting power mode failed (%08X)", res); -+ return 0; -+} -+ -+static int iw_get_power_mode(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ enum ndis_power power_mode; -+ -+ ENTER2(""); -+ res = mp_query(wnd, OID_802_11_POWER_MODE, -+ &power_mode, sizeof(power_mode)); -+ if (res) -+ return -ENOTSUPP; -+ -+ if (power_mode == NDIS_POWER_OFF) -+ wrqu->power.disabled = 1; -+ else { -+ if (wrqu->power.flags != 0) -+ return 0; -+ wrqu->power.flags |= IW_POWER_ALL_R; -+ wrqu->power.flags |= IW_POWER_TIMEOUT; -+ wrqu->power.value = 0; -+ wrqu->power.disabled = 0; -+ -+ if (power_mode == NDIS_POWER_MIN) -+ wrqu->power.flags |= IW_POWER_MIN; -+ else // if (power_mode == NDIS_POWER_MAX) -+ wrqu->power.flags |= IW_POWER_MAX; -+ } -+ return 0; -+} -+ -+static int iw_get_sensitivity(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ ndis_rssi rssi_trigger; -+ -+ ENTER2(""); -+ res = mp_query(wnd, OID_802_11_RSSI_TRIGGER, -+ &rssi_trigger, sizeof(rssi_trigger)); -+ if (res) -+ return -EOPNOTSUPP; -+ wrqu->param.value = rssi_trigger; -+ wrqu->param.disabled = (rssi_trigger == 0); -+ wrqu->param.fixed = 1; -+ return 0; -+} -+ -+static int iw_set_sensitivity(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ ndis_rssi rssi_trigger; -+ -+ ENTER2(""); -+ if (wrqu->param.disabled) -+ rssi_trigger = 0; -+ else -+ rssi_trigger = wrqu->param.value; -+ res = mp_set(wnd, OID_802_11_RSSI_TRIGGER, -+ &rssi_trigger, sizeof(rssi_trigger)); -+ if (res == NDIS_STATUS_INVALID_DATA) -+ return -EINVAL; -+ if (res) -+ return -EOPNOTSUPP; -+ return 0; -+} -+ -+static int iw_get_ndis_stats(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ struct iw_statistics *stats = &wnd->iw_stats; -+ memcpy(&wrqu->qual, &stats->qual, sizeof(stats->qual)); -+ return 0; -+} -+ -+static int iw_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 iw_point *data = &wrqu->data; -+ struct ndis_device *wnd = netdev_priv(dev); -+ unsigned int i, n; -+ NDIS_STATUS res; -+ UCHAR rates[NDIS_MAX_RATES_EX]; -+ ndis_tx_power_level tx_power; -+ -+ ENTER2(""); -+ data->length = sizeof(struct iw_range); -+ memset(range, 0, sizeof(struct iw_range)); -+ -+ range->txpower_capa = IW_TXPOW_MWATT; -+ range->num_txpower = 0; -+ -+ res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, -+ &tx_power, sizeof(tx_power)); -+ if (!res) { -+ range->num_txpower = 1; -+ range->txpower[0] = tx_power; -+ } -+ -+ range->we_version_compiled = WIRELESS_EXT; -+ range->we_version_source = 19; -+ -+ range->retry_capa = IW_RETRY_LIMIT; -+ range->retry_flags = IW_RETRY_LIMIT; -+ range->min_retry = 0; -+ range->max_retry = 255; -+ -+ range->num_channels = 1; -+ -+ range->max_qual.qual = 100; -+ range->max_qual.level = 154; -+ range->max_qual.noise = 154; -+ range->sensitivity = 3; -+ -+ range->max_encoding_tokens = 4; -+ range->num_encoding_sizes = 2; -+ range->encoding_size[0] = 5; -+ range->encoding_size[1] = 13; -+ -+ range->num_bitrates = 0; -+ memset(&rates, 0, sizeof(rates)); -+ res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES, -+ &rates, sizeof(rates), &n, NULL); -+ if (res) -+ WARNING("getting bit rates failed: %08X", res); -+ else { -+ for (i = 0; i < n && range->num_bitrates < IW_MAX_BITRATES; i++) -+ if (rates[i] & 0x80) -+ continue; -+ else if (rates[i] & 0x7f) { -+ range->bitrate[range->num_bitrates] = -+ (rates[i] & 0x7f) * 500000; -+ range->num_bitrates++; -+ } -+ } -+ -+ range->num_channels = ARRAY_SIZE(freq_chan); -+ -+ for (i = 0; i < ARRAY_SIZE(freq_chan) && i < IW_MAX_FREQUENCIES; i++) { -+ range->freq[i].i = i + 1; -+ range->freq[i].m = freq_chan[i] * 100000; -+ range->freq[i].e = 1; -+ } -+ range->num_frequency = i; -+ -+ range->min_rts = 0; -+ range->max_rts = 2347; -+ range->min_frag = 256; -+ range->max_frag = 2346; -+ -+ /* Event capability (kernel + driver) */ -+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 | -+ IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | -+ IW_EVENT_CAPA_MASK(SIOCGIWAP) | -+ IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); -+ range->event_capa[1] = IW_EVENT_CAPA_K_1; -+ range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | -+ IW_EVENT_CAPA_MASK(IWEVCUSTOM) | -+ IW_EVENT_CAPA_MASK(IWEVREGISTERED) | -+ IW_EVENT_CAPA_MASK(IWEVEXPIRED)); -+ -+ range->enc_capa = 0; -+ -+ if (test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr)) -+ range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; -+ if (test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr)) -+ range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; -+ -+ if (test_bit(Ndis802_11AuthModeWPA, &wnd->capa.auth) || -+ test_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.auth)) -+ range->enc_capa |= IW_ENC_CAPA_WPA; -+ if (test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) || -+ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth)) -+ range->enc_capa |= IW_ENC_CAPA_WPA2; -+ -+ return 0; -+} -+ -+void set_default_iw_params(struct ndis_device *wnd) -+{ -+ wnd->iw_auth_key_mgmt = 0; -+ wnd->iw_auth_wpa_version = 0; -+ set_infra_mode(wnd, Ndis802_11Infrastructure); -+ set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen); -+ set_priv_filter(wnd); -+ set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE); -+} -+ -+static int deauthenticate(struct ndis_device *wnd) -+{ -+ int ret; -+ -+ ENTER2(""); -+ ret = disassociate(wnd, 1); -+ set_default_iw_params(wnd); -+ EXIT2(return ret); -+} -+ -+NDIS_STATUS disassociate(struct ndis_device *wnd, int reset_ssid) -+{ -+ NDIS_STATUS res; -+ u8 buf[NDIS_ESSID_MAX_SIZE]; -+ int i; -+ -+ TRACE2(""); -+ res = mp_set(wnd, OID_802_11_DISASSOCIATE, NULL, 0); -+ /* disassociate causes radio to be turned off; if reset_ssid -+ * is given, set ssid to random to enable radio */ -+ if (reset_ssid) { -+ get_random_bytes(buf, sizeof(buf)); -+ for (i = 0; i < sizeof(buf); i++) -+ buf[i] = 'a' + (buf[i] % 26); -+ set_essid(wnd, buf, sizeof(buf)); -+ } -+ return res; -+} -+ -+static int iw_set_mlme(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ struct iw_mlme *mlme = (struct iw_mlme *)extra; -+ -+ ENTER2(""); -+ switch (mlme->cmd) { -+ case IW_MLME_DEAUTH: -+ return deauthenticate(wnd); -+ case IW_MLME_DISASSOC: -+ TRACE2("cmd=%d reason_code=%d", mlme->cmd, mlme->reason_code); -+ return disassociate(wnd, 1); -+ default: -+ return -EOPNOTSUPP; -+ } -+ -+ return 0; -+} -+ -+static int iw_set_genie(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ /* -+ * NDIS drivers do not allow IEs to be configured; this is -+ * done by the driver based on other configuration. Return 0 -+ * to avoid causing issues with user space programs that -+ * expect this function to succeed. -+ */ -+ return 0; -+} -+ -+static int iw_set_auth(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ TRACE2("index=%d value=%d", wrqu->param.flags & IW_AUTH_INDEX, -+ wrqu->param.value); -+ switch (wrqu->param.flags & IW_AUTH_INDEX) { -+ case IW_AUTH_WPA_VERSION: -+ wnd->iw_auth_wpa_version = wrqu->param.value; -+ break; -+ case IW_AUTH_CIPHER_PAIRWISE: -+ wnd->iw_auth_cipher_pairwise = wrqu->param.value; -+ break; -+ case IW_AUTH_CIPHER_GROUP: -+ wnd->iw_auth_cipher_group = wrqu->param.value; -+ break; -+ case IW_AUTH_KEY_MGMT: -+ wnd->iw_auth_key_mgmt = wrqu->param.value; -+ break; -+ case IW_AUTH_80211_AUTH_ALG: -+ wnd->iw_auth_80211_alg = wrqu->param.value; -+ break; -+ case IW_AUTH_WPA_ENABLED: -+ if (wrqu->param.value) -+ deauthenticate(wnd); -+ break; -+#ifdef IW_AUTH_MFP -+ case IW_AUTH_MFP: -+ if (wrqu->param.value == IW_AUTH_MFP_DISABLED || -+ wrqu->param.value == IW_AUTH_MFP_OPTIONAL) -+ break; -+ WARNING("MFP not implemented"); -+ return -EOPNOTSUPP; -+#endif -+ case IW_AUTH_TKIP_COUNTERMEASURES: -+ case IW_AUTH_DROP_UNENCRYPTED: -+ case IW_AUTH_RX_UNENCRYPTED_EAPOL: -+ case IW_AUTH_PRIVACY_INVOKED: -+ TRACE2("%d not implemented: %d", -+ wrqu->param.flags & IW_AUTH_INDEX, wrqu->param.value); -+ break; -+ default: -+ WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX); -+ return -EOPNOTSUPP; -+ } -+ return 0; -+} -+ -+static int iw_get_auth(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ ENTER2("index=%d", wrqu->param.flags & IW_AUTH_INDEX); -+ switch (wrqu->param.flags & IW_AUTH_INDEX) { -+ case IW_AUTH_WPA_VERSION: -+ wrqu->param.value = wnd->iw_auth_wpa_version; -+ break; -+ case IW_AUTH_CIPHER_PAIRWISE: -+ wrqu->param.value = wnd->iw_auth_cipher_pairwise; -+ break; -+ case IW_AUTH_CIPHER_GROUP: -+ wrqu->param.value = wnd->iw_auth_cipher_group; -+ break; -+ case IW_AUTH_KEY_MGMT: -+ wrqu->param.value = wnd->iw_auth_key_mgmt; -+ break; -+ case IW_AUTH_80211_AUTH_ALG: -+ wrqu->param.value = wnd->iw_auth_80211_alg; -+ break; -+ default: -+ WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX); -+ return -EOPNOTSUPP; -+ } -+ return 0; -+} -+ -+static int iw_set_encodeext(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; -+ struct ndis_device *wnd = netdev_priv(dev); -+ struct ndis_add_key ndis_key; -+ int i, keyidx; -+ NDIS_STATUS res; -+ u8 *addr; -+ -+ keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX; -+ ENTER2("%d", keyidx); -+ if (keyidx) -+ keyidx--; -+ else -+ keyidx = wnd->encr_info.tx_key_index; -+ -+ if (keyidx < 0 || keyidx >= MAX_ENCR_KEYS) -+ return -EINVAL; -+ -+ if (ext->alg == WPA_ALG_WEP) { -+ if (!test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr)) -+ EXIT2(return -1); -+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) -+ wnd->encr_info.tx_key_index = keyidx; -+ if (add_wep_key(wnd, ext->key, ext->key_len, keyidx)) -+ EXIT2(return -1); -+ else -+ EXIT2(return 0); -+ } -+ if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) || -+ ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0) -+ EXIT2(return remove_key(wnd, keyidx, ndis_key.bssid)); -+ -+ if (ext->key_len > sizeof(ndis_key.key)) { -+ TRACE2("incorrect key length (%u)", ext->key_len); -+ EXIT2(return -1); -+ } -+ -+ memset(&ndis_key, 0, sizeof(ndis_key)); -+ -+ ndis_key.struct_size = -+ sizeof(ndis_key) - sizeof(ndis_key.key) + ext->key_len; -+ ndis_key.length = ext->key_len; -+ ndis_key.index = keyidx; -+ -+ if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { -+ for (i = 0; i < 6; i++) -+ ndis_key.rsc |= (((u64)ext->rx_seq[i]) << (i * 8)); -+ TRACE2("0x%llx", ndis_key.rsc); -+ ndis_key.index |= 1 << 29; -+ } -+ -+ addr = ext->addr.sa_data; -+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { -+ /* group key */ -+ if (wnd->infrastructure_mode == Ndis802_11IBSS) -+ memset(ndis_key.bssid, 0xff, ETH_ALEN); -+ else -+ get_ap_address(wnd, ndis_key.bssid); -+ } else { -+ /* pairwise key */ -+ ndis_key.index |= (1 << 30); -+ memcpy(ndis_key.bssid, addr, ETH_ALEN); -+ } -+ TRACE2(MACSTRSEP, MAC2STR(ndis_key.bssid)); -+ -+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) -+ ndis_key.index |= (1 << 31); -+ -+ if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) { -+ /* wpa_supplicant gives us the Michael MIC RX/TX keys in -+ * different order than NDIS spec, so swap the order here. */ -+ memcpy(ndis_key.key, ext->key, 16); -+ memcpy(ndis_key.key + 16, ext->key + 24, 8); -+ memcpy(ndis_key.key + 24, ext->key + 16, 8); -+ } else -+ memcpy(ndis_key.key, ext->key, ext->key_len); -+ -+ res = mp_set(wnd, OID_802_11_ADD_KEY, &ndis_key, ndis_key.struct_size); -+ if (res) { -+ TRACE2("adding key failed (%08X), %u", -+ res, ndis_key.struct_size); -+ EXIT2(return -1); -+ } -+ wnd->encr_info.keys[keyidx].length = ext->key_len; -+ memcpy(&wnd->encr_info.keys[keyidx].key, ndis_key.key, ext->key_len); -+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) -+ wnd->encr_info.tx_key_index = keyidx; -+ TRACE2("key %d added", keyidx); -+ -+ EXIT2(return 0); -+} -+ -+static int iw_get_encodeext(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ /* struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; */ -+ /* TODO */ -+ ENTER2(""); -+ return 0; -+} -+ -+static int iw_set_pmksa(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; -+ struct ndis_pmkid pmkid; -+ NDIS_STATUS res; -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ /* TODO: must keep local list of PMKIDs since NDIS drivers -+ * expect that all PMKID entries are included whenever a new -+ * one is added. */ -+ -+ ENTER2("%d", pmksa->cmd); -+ if ((pmksa->cmd == IW_PMKSA_ADD || pmksa->cmd == IW_PMKSA_REMOVE) && -+ (!(wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2))) -+ EXIT2(return -EOPNOTSUPP); -+ -+ memset(&pmkid, 0, sizeof(pmkid)); -+ if (pmksa->cmd == IW_PMKSA_ADD) { -+ pmkid.bssid_info_count = 1; -+ memcpy(pmkid.bssid_info[0].bssid, pmksa->bssid.sa_data, -+ ETH_ALEN); -+ memcpy(pmkid.bssid_info[0].pmkid, pmksa->pmkid, IW_PMKID_LEN); -+ } -+ pmkid.length = sizeof(pmkid); -+ -+ res = mp_set(wnd, OID_802_11_PMKID, &pmkid, pmkid.length); -+ if (res == NDIS_STATUS_FAILURE) -+ EXIT2(return -EOPNOTSUPP); -+ TRACE2("OID_802_11_PMKID -> %d", res); -+ if (res) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+#define WEXT(id) [id - SIOCIWFIRST] -+ -+static const iw_handler ndis_handler[] = { -+ WEXT(SIOCGIWNAME) = iw_get_network_type, -+ WEXT(SIOCSIWESSID) = iw_set_essid, -+ WEXT(SIOCGIWESSID) = iw_get_essid, -+ WEXT(SIOCSIWMODE) = iw_set_infra_mode, -+ WEXT(SIOCGIWMODE) = iw_get_infra_mode, -+ WEXT(SIOCGIWFREQ) = iw_get_freq, -+ WEXT(SIOCSIWFREQ) = iw_set_freq, -+ WEXT(SIOCGIWTXPOW) = iw_get_tx_power, -+ WEXT(SIOCSIWTXPOW) = iw_set_tx_power, -+ WEXT(SIOCGIWRATE) = iw_get_bitrate, -+ WEXT(SIOCSIWRATE) = iw_set_bitrate, -+ WEXT(SIOCGIWRTS) = iw_get_rts_threshold, -+ WEXT(SIOCSIWRTS) = iw_set_rts_threshold, -+ WEXT(SIOCGIWFRAG) = iw_get_frag_threshold, -+ WEXT(SIOCSIWFRAG) = iw_set_frag_threshold, -+ WEXT(SIOCGIWAP) = iw_get_ap_address, -+ WEXT(SIOCSIWAP) = iw_set_ap_address, -+ WEXT(SIOCSIWENCODE) = iw_set_wep, -+ WEXT(SIOCGIWENCODE) = iw_get_encr, -+ WEXT(SIOCSIWSCAN) = iw_set_scan, -+ WEXT(SIOCGIWSCAN) = iw_get_scan, -+ WEXT(SIOCGIWPOWER) = iw_get_power_mode, -+ WEXT(SIOCSIWPOWER) = iw_set_power_mode, -+ WEXT(SIOCGIWRANGE) = iw_get_range, -+ WEXT(SIOCGIWSTATS) = iw_get_ndis_stats, -+ WEXT(SIOCGIWSENS) = iw_get_sensitivity, -+ WEXT(SIOCSIWSENS) = iw_set_sensitivity, -+ WEXT(SIOCGIWNICKN) = iw_get_nick, -+ WEXT(SIOCSIWNICKN) = iw_set_nick, -+ WEXT(SIOCSIWCOMMIT) = iw_set_dummy, -+ WEXT(SIOCSIWMLME) = iw_set_mlme, -+ WEXT(SIOCSIWGENIE) = iw_set_genie, -+ WEXT(SIOCSIWAUTH) = iw_set_auth, -+ WEXT(SIOCGIWAUTH) = iw_get_auth, -+ WEXT(SIOCSIWENCODEEXT) = iw_set_encodeext, -+ WEXT(SIOCGIWENCODEEXT) = iw_get_encodeext, -+ WEXT(SIOCSIWPMKSA) = iw_set_pmksa, -+}; -+ -+/* private ioctl's */ -+ -+static int priv_reset(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ int res; -+ ENTER2(""); -+ res = mp_reset(netdev_priv(dev)); -+ if (res) { -+ WARNING("reset failed: %08X", res); -+ return -EOPNOTSUPP; -+ } -+ return 0; -+} -+ -+static int priv_deauthenticate(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ int res; -+ ENTER2(""); -+ res = deauthenticate(netdev_priv(dev)); -+ return res; -+} -+ -+static int priv_power_profile(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ struct miniport *mp; -+ ULONG profile_inf; -+ -+ ENTER2(""); -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ if (!mp->pnp_event_notify) -+ EXIT2(return -EOPNOTSUPP); -+ -+ /* 1 for AC and 0 for Battery */ -+ if (wrqu->param.value) -+ profile_inf = NdisPowerProfileAcOnLine; -+ else -+ profile_inf = NdisPowerProfileBattery; -+ -+ LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx, -+ NdisDevicePnPEventPowerProfileChanged, -+ &profile_inf, sizeof(profile_inf)); -+ EXIT2(return 0); -+} -+ -+static int priv_network_type(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ enum network_type network_type; -+ NDIS_STATUS res; -+ char type; -+ -+ ENTER2(""); -+ type = wrqu->param.value; -+ if (type == 'f') -+ network_type = Ndis802_11FH; -+ else if (type == 'b') -+ network_type = Ndis802_11DS; -+ else if (type == 'a') -+ network_type = Ndis802_11OFDM5; -+ else if (type == 'g' || type == 'n') -+ network_type = Ndis802_11OFDM24; -+ else -+ network_type = Ndis802_11Automode; -+ -+ res = mp_set_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, network_type); -+ if (res) { -+ WARNING("setting network type to %d failed (%08X)", -+ network_type, res); -+ EXIT2(return -EINVAL); -+ } -+ -+ EXIT2(return 0); -+} -+ -+static int priv_media_stream_mode(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ NDIS_STATUS res; -+ int mode; -+ -+ ENTER2(""); -+ if (wrqu->param.value > 0) -+ mode = Ndis802_11MediaStreamOn; -+ else -+ mode = Ndis802_11MediaStreamOff; -+ res = mp_set_int(wnd, OID_802_11_MEDIA_STREAM_MODE, mode); -+ if (res) { -+ WARNING("oid failed (%08X)", res); -+ EXIT2(return -EINVAL); -+ } -+ EXIT2(return 0); -+} -+ -+static int priv_reload_defaults(struct net_device *dev, -+ struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ int res; -+ ENTER2(""); -+ res = mp_set_int(wnd, OID_802_11_RELOAD_DEFAULTS, -+ Ndis802_11ReloadWEPKeys); -+ if (res) { -+ WARNING("reloading defaults failed: %08X", res); -+ return -EOPNOTSUPP; -+ } -+ return 0; -+} -+ -+static const struct iw_priv_args priv_args[] = { -+ {PRIV_RESET, 0, 0, "ndis_reset"}, -+ {PRIV_POWER_PROFILE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, -+ "power_profile"}, -+ {PRIV_DEAUTHENTICATE, 0, 0, "deauthenticate"}, -+ {PRIV_NETWORK_TYPE, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, -+ "network_type"}, -+ {PRIV_MEDIA_STREAM_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, -+ "media_stream"}, -+ -+ {PRIV_RELOAD_DEFAULTS, 0, 0, "reload_defaults"}, -+}; -+ -+#define WEPRIV(id) [id - SIOCIWFIRSTPRIV] -+ -+static const iw_handler priv_handler[] = { -+ WEPRIV(PRIV_RESET) = priv_reset, -+ WEPRIV(PRIV_POWER_PROFILE) = priv_power_profile, -+ WEPRIV(PRIV_DEAUTHENTICATE) = priv_deauthenticate, -+ WEPRIV(PRIV_NETWORK_TYPE) = priv_network_type, -+ WEPRIV(PRIV_MEDIA_STREAM_MODE) = priv_media_stream_mode, -+ WEPRIV(PRIV_RELOAD_DEFAULTS) = priv_reload_defaults, -+}; -+ -+const struct iw_handler_def ndis_handler_def = { -+ .num_standard = ARRAY_SIZE(ndis_handler), -+ .num_private = ARRAY_SIZE(priv_handler), -+ .num_private_args = ARRAY_SIZE(priv_args), -+ -+ .standard = (iw_handler *)ndis_handler, -+ .private = (iw_handler *)priv_handler, -+ .private_args = (struct iw_priv_args *)priv_args, -+ .get_wireless_stats = get_iw_stats, -+}; -+ -+#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.h ---- linux-3.12.2/3rdparty/ndiswrapper/iw_ndis.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/iw_ndis.h 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,194 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _IW_NDIS_H_ -+#define _IW_NDIS_H_ -+ -+#include "ndis.h" -+ -+#define WL_NOISE -96 /* typical noise level in dBm */ -+#define WL_SIGMAX -32 /* typical maximum signal level in dBm */ -+ -+struct ndis_encr_key { -+ ULONG struct_size; -+ ULONG index; -+ ULONG length; -+ UCHAR key[NDIS_ENCODING_TOKEN_MAX]; -+}; -+ -+struct ndis_add_key { -+ ULONG struct_size; -+ ndis_key_index index; -+ ULONG length; -+ mac_address bssid; -+ UCHAR pad[6]; -+ ndis_key_rsc rsc; -+ UCHAR key[NDIS_ENCODING_TOKEN_MAX]; -+}; -+ -+struct ndis_remove_key { -+ ULONG struct_size; -+ ndis_key_index index; -+ mac_address bssid; -+}; -+ -+struct ndis_fixed_ies { -+ UCHAR time_stamp[8]; -+ USHORT beacon_interval; -+ USHORT capa; -+}; -+ -+struct ndis_variable_ies { -+ UCHAR elem_id; -+ UCHAR length; -+ UCHAR data[]; -+}; -+ -+enum ndis_reload_defaults { Ndis802_11ReloadWEPKeys }; -+ -+struct ndis_assoc_info { -+ ULONG length; -+ USHORT req_ies; -+ struct req_ie { -+ USHORT capa; -+ USHORT listen_interval; -+ mac_address cur_ap_address; -+ } req_ie; -+ ULONG req_ie_length; -+ ULONG offset_req_ies; -+ USHORT resp_ies; -+ struct resp_ie { -+ USHORT capa; -+ USHORT status_code; -+ USHORT assoc_id; -+ } resp_ie; -+ ULONG resp_ie_length; -+ ULONG offset_resp_ies; -+}; -+ -+struct ndis_configuration_fh { -+ ULONG length; -+ ULONG hop_pattern; -+ ULONG hop_set; -+ ULONG dwell_time; -+}; -+ -+struct ndis_configuration { -+ ULONG length; -+ ULONG beacon_period; -+ ULONG atim_window; -+ ULONG ds_config; -+ struct ndis_configuration_fh fh_config; -+}; -+ -+struct ndis_wlan_bssid { -+ ULONG length; -+ mac_address mac; -+ UCHAR reserved[2]; -+ struct ndis_essid ssid; -+ ULONG privacy; -+ ndis_rssi rssi; -+ UINT net_type; -+ struct ndis_configuration config; -+ UINT mode; -+ UCHAR rates[NDIS_MAX_RATES]; -+}; -+ -+struct ndis_wlan_bssid_ex { -+ ULONG length; -+ mac_address mac; -+ UCHAR reserved[2]; -+ struct ndis_essid ssid; -+ ULONG privacy; -+ ndis_rssi rssi; -+ UINT net_type; -+ struct ndis_configuration config; -+ UINT mode; -+ UCHAR rates_ex[NDIS_MAX_RATES_EX]; -+ ULONG ie_length; -+ struct ndis_fixed_ies fixed; -+ struct ndis_variable_ies var[]; -+}; -+ -+/* we use bssid_list as bssid_list_ex also */ -+struct ndis_bssid_list { -+ ULONG num_items; -+ struct ndis_wlan_bssid bssid[1]; -+}; -+ -+enum ndis_priv_filter { -+ Ndis802_11PrivFilterAcceptAll, Ndis802_11PrivFilter8021xWEP -+}; -+ -+enum network_type { -+ Ndis802_11FH, Ndis802_11DS, Ndis802_11OFDM5, Ndis802_11OFDM24, -+ /* MSDN site uses Ndis802_11Automode, which is not mentioned -+ * in DDK, so add one and assign it to -+ * Ndis802_11NetworkTypeMax */ -+ Ndis802_11Automode, Ndis802_11NetworkTypeMax = Ndis802_11Automode -+}; -+ -+struct network_type_list { -+ ULONG num; -+ enum network_type types[1]; -+}; -+ -+enum ndis_power { -+ NDIS_POWER_OFF = 0, NDIS_POWER_MAX, NDIS_POWER_MIN, -+}; -+ -+struct ndis_auth_req { -+ ULONG length; -+ mac_address bssid; -+ ULONG flags; -+}; -+ -+struct ndis_bssid_info { -+ mac_address bssid; -+ UCHAR pmkid[IW_PMKID_LEN]; -+}; -+ -+struct ndis_pmkid { -+ ULONG length; -+ ULONG bssid_info_count; -+ struct ndis_bssid_info bssid_info[1]; -+}; -+ -+int get_ap_address(struct ndis_device *wnd, mac_address mac); -+int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode); -+int get_ndis_encr_mode(struct ndis_device *wnd); -+int set_iw_encr_mode(struct ndis_device *wnd, int cipher_pairwise, -+ int cipher_groupwise); -+int get_ndis_auth_mode(struct ndis_device *wnd); -+NDIS_STATUS disassociate(struct ndis_device *wnd, int reset_ssid); -+void set_default_iw_params(struct ndis_device *wnd); -+extern const struct iw_handler_def ndis_handler_def; -+ -+#define PRIV_RESET SIOCIWFIRSTPRIV+16 -+#define PRIV_POWER_PROFILE SIOCIWFIRSTPRIV+17 -+#define PRIV_NETWORK_TYPE SIOCIWFIRSTPRIV+18 -+#define PRIV_DEAUTHENTICATE SIOCIWFIRSTPRIV+19 -+#define PRIV_MEDIA_STREAM_MODE SIOCIWFIRSTPRIV+20 -+#define PRIV_RELOAD_DEFAULTS SIOCIWFIRSTPRIV+23 -+ -+/* these have to match what is in wpa_supplicant */ -+ -+enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP }; -+enum wpa_cipher { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, -+ CIPHER_WEP104 }; -+enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, -+ KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE }; -+ -+#endif // IW_NDIS_H -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.h ---- linux-3.12.2/3rdparty/ndiswrapper/lin2win.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.h 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,111 @@ -+/* -+ * Copyright (C) 2006 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifdef CONFIG_X86_64 -+ -+u64 lin2win0(void *func); -+u64 lin2win1(void *func, u64 arg1); -+u64 lin2win2(void *func, u64 arg1, u64 arg2); -+u64 lin2win3(void *func, u64 arg1, u64 arg2, u64 arg3); -+u64 lin2win4(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4); -+u64 lin2win5(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5); -+u64 lin2win6(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, -+ u64 arg6); -+ -+#define LIN2WIN0(func) \ -+({ \ -+ if (0) \ -+ func(); \ -+ lin2win0(func); \ -+}) -+ -+#define LIN2WIN1(func, arg1) \ -+({ \ -+ if (0) \ -+ func(arg1); \ -+ lin2win1(func, (u64)arg1); \ -+}) -+ -+#define LIN2WIN2(func, arg1, arg2) \ -+({ \ -+ if (0) \ -+ func(arg1, arg2); \ -+ lin2win2(func, (u64)arg1, (u64)arg2); \ -+}) -+ -+#define LIN2WIN3(func, arg1, arg2, arg3) \ -+({ \ -+ if (0) \ -+ func(arg1, arg2, arg3); \ -+ lin2win3(func, (u64)arg1, (u64)arg2, (u64)arg3); \ -+}) -+ -+#define LIN2WIN4(func, arg1, arg2, arg3, arg4) \ -+({ \ -+ if (0) \ -+ func(arg1, arg2, arg3, arg4); \ -+ lin2win4(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4); \ -+}) -+ -+#define LIN2WIN5(func, arg1, arg2, arg3, arg4, arg5) \ -+({ \ -+ if (0) \ -+ func(arg1, arg2, arg3, arg4, arg5); \ -+ lin2win5(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4, \ -+ (u64)arg5); \ -+}) -+ -+#define LIN2WIN6(func, arg1, arg2, arg3, arg4, arg5, arg6) \ -+({ \ -+ if (0) \ -+ func(arg1, arg2, arg3, arg4, arg5, arg6); \ -+ lin2win6(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4, \ -+ (u64)arg5, (u64)arg6); \ -+}) -+ -+#else // CONFIG_X86_64 -+ -+#define LIN2WIN1(func, arg1) \ -+({ \ -+ TRACE6("calling %p", func); \ -+ func(arg1); \ -+}) -+#define LIN2WIN2(func, arg1, arg2) \ -+({ \ -+ TRACE6("calling %p", func); \ -+ func(arg1, arg2); \ -+}) -+#define LIN2WIN3(func, arg1, arg2, arg3) \ -+({ \ -+ TRACE6("calling %p", func); \ -+ func(arg1, arg2, arg3); \ -+}) -+#define LIN2WIN4(func, arg1, arg2, arg3, arg4) \ -+({ \ -+ TRACE6("calling %p", func); \ -+ func(arg1, arg2, arg3, arg4); \ -+}) -+#define LIN2WIN5(func, arg1, arg2, arg3, arg4, arg5) \ -+({ \ -+ TRACE6("calling %p", func); \ -+ func(arg1, arg2, arg3, arg4, arg5); \ -+}) -+#define LIN2WIN6(func, arg1, arg2, arg3, arg4, arg5, arg6) \ -+({ \ -+ TRACE6("calling %p", func); \ -+ func(arg1, arg2, arg3, arg4, arg5, arg6); \ -+}) -+ -+#endif // CONFIG_X86_64 -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/lin2win.S linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.S ---- linux-3.12.2/3rdparty/ndiswrapper/lin2win.S 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/lin2win.S 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,138 @@ -+/* -+ * Copyright (C) 2011 Pavel Roskin -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include -+ -+ .text -+ -+#define WORD_BYTES 8 -+#define LINUX_REG_ARGS 6 -+#define WINDOWS_REG_ARGS 4 -+ -+/* %rbp is saved to create a stack frame, which can help with debugging */ -+#define SAVED_REGS 1 -+ -+/* -+ * When calling a Windows function, stack space is allocated for at least 4 -+ * arguments even if the number of arguments is less than 4. The value of -+ * true is -1 in assembler, so we multiply it by another true value. -+ */ -+#define stack_args(argc) \ -+ (WINDOWS_REG_ARGS + \ -+ (0 < 1) * (argc > WINDOWS_REG_ARGS) * (argc - WINDOWS_REG_ARGS)) -+ -+/* Full required change of stack pointer, in words */ -+#define stack_words_raw(argc) (stack_args(argc) + SAVED_REGS + 1) -+ -+/* Full actual change of stack pointer, in words (must be even) */ -+#define stack_words_aligned(argc) ((stack_words_raw(argc) + 1) & ~1) -+ -+/* Space allocated for Linux arguments on stack */ -+#define stack_space(argc) \ -+ ((stack_words_aligned(argc) - SAVED_REGS - 1) * WORD_BYTES) -+ -+/* -+ * lin2win_win_arg(N) gives the address of the Nth Windows argument on our -+ * stack frame. %rsp points to the first argument. The Nth argument is -+ * therefore at ((N - 1) * 8)(%rsp). -+ * -+ * Don't call with N less than 5! -+ */ -+#define lin2win_win_arg(n) ((n - 1) * WORD_BYTES)(%rsp) -+ -+/* -+ * lin2win_lin_arg(N, ARGC) gives the address of the Nth Linux argument after -+ * the stack has been prepared for a Windows function call with ARGC arguments. -+ * -+ * When called from Linux, the Nth argument is at ((N - 6) * 8)(%rsp). We add -+ * the allocated stack space and saved registers to compensate for %rsp change. -+ * -+ * Don't call with N less than 7! -+ */ -+#define lin2win_lin_arg(n, argc) \ -+ (stack_space(argc) + \ -+ (SAVED_REGS + n - LINUX_REG_ARGS) * WORD_BYTES)(%rsp) -+ -+/* -+ * lin2win(func, winarg1, winarg2, ...) -+ * Call Windows FUNC function with ARGC arguments WINARG1, WINARG2, ... -+ * We get (ARGC + 1) arguments. -+ */ -+.macro lin2win name, argc -+ .type \name, @function -+ ENTRY(\name) -+ -+ /* Create a call frame - it's optional, but good for debugging */ -+ .cfi_startproc -+ push %rbp -+ .cfi_def_cfa %rsp, 2 * WORD_BYTES -+ .cfi_offset %rbp, -2 * WORD_BYTES -+ mov %rsp, %rbp -+ .cfi_def_cfa %rbp, 2 * WORD_BYTES -+ -+ /* Allocate space for Windows arguments */ -+ sub $stack_space(\argc), %rsp -+ -+ /* arg7 to winarg6 */ -+ .if (\argc >= 6) -+ mov lin2win_lin_arg(7, \argc), %r11 -+ mov %r11, lin2win_win_arg(6) -+ .endif -+ -+ /* arg6 to winarg5 */ -+ .if (\argc >= 5) -+ mov %r9, lin2win_win_arg(5) -+ .endif -+ -+ /* arg5 to winarg4 */ -+ .if (\argc >= 4) -+ mov %r8, %r9 -+ .endif -+ -+ /* arg4 to winarg3 */ -+ .if (\argc >= 3) -+ mov %rcx, %r8 -+ .endif -+ -+ /* arg3 to winarg2 - nothing needed, both are in %rdx */ -+ -+ /* arg2 to winarg1 */ -+ .if (\argc >= 1) -+ mov %rsi, %rcx -+ .endif -+ -+ /* Call function (arg1) */ -+ call *%rdi -+ -+ /* Reclaim space for Windows arguments */ -+ add $stack_space(\argc), %rsp -+ -+ /* Return to the caller */ -+ leave -+ .cfi_def_cfa %rsp, WORD_BYTES -+ .cfi_restore %rbp -+ ret -+ .cfi_endproc -+ .size \name, (. - \name) -+.endm -+ -+/* Define lin2winN functions */ -+lin2win lin2win0, 0 -+lin2win lin2win1, 1 -+lin2win lin2win2, 2 -+lin2win lin2win3, 3 -+lin2win lin2win4, 4 -+lin2win lin2win5, 5 -+lin2win lin2win6, 6 -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.c ---- linux-3.12.2/3rdparty/ndiswrapper/loader.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.c 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,967 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ndis.h" -+#include "loader.h" -+#include "wrapndis.h" -+#include "pnp.h" -+ -+#include -+#include -+#include -+#include -+ -+/* -+ Network adapter: ClassGuid = {4d36e972-e325-11ce-bfc1-08002be10318} -+ Network client: ClassGuid = {4d36e973-e325-11ce-bfc1-08002be10318} -+ PCMCIA adapter: ClassGuid = {4d36e977-e325-11ce-bfc1-08002be10318} -+ USB: ClassGuid = {36fc9e60-c465-11cf-8056-444553540000} -+*/ -+ -+/* the indices used here must match macros WRAP_NDIS_DEVICE etc. */ -+static struct guid class_guids[] = { -+ /* Network */ -+ { .data1 = 0x4d36e972, .data2 = 0xe325, .data3 = 0x11ce }, -+ /* USB WDM */ -+ { .data1 = 0x36fc9e60, .data2 = 0xc465, .data3 = 0x11cf }, -+ /* Bluetooth */ -+ { .data1 = 0xe0cbf06c, .data2 = 0xcd8b, .data3 = 0x4647 }, -+ /* ivtcorporation.com's bluetooth device claims this is -+ * bluetooth guid */ -+ { .data1 = 0xf12d3cf8, .data2 = 0xb11d, .data3 = 0x457e}, -+}; -+ -+struct mutex loader_mutex; -+static struct completion loader_complete; -+ -+static struct nt_list wrap_devices; -+static struct nt_list wrap_drivers; -+ -+static int wrap_device_type(int data1) -+{ -+ int i; -+ for (i = 0; i < ARRAY_SIZE(class_guids); i++) -+ if (data1 == class_guids[i].data1) -+ return i; -+ ERROR("unknown device: 0x%x\n", data1); -+ return -1; -+} -+ -+/* load driver for given device, if not already loaded */ -+struct wrap_driver *load_wrap_driver(struct wrap_device *wd) -+{ -+ int ret; -+ struct nt_list *cur; -+ struct wrap_driver *wrap_driver; -+ -+ ENTER1("device: %04X:%04X:%04X:%04X", wd->vendor, wd->device, -+ wd->subvendor, wd->subdevice); -+ mutex_lock(&loader_mutex); -+ wrap_driver = NULL; -+ nt_list_for_each(cur, &wrap_drivers) { -+ wrap_driver = container_of(cur, struct wrap_driver, list); -+ if (!stricmp(wrap_driver->name, wd->driver_name)) { -+ TRACE1("driver %s already loaded", wrap_driver->name); -+ break; -+ } else -+ wrap_driver = NULL; -+ } -+ mutex_unlock(&loader_mutex); -+ -+ if (!wrap_driver) { -+ char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DRIVER, -+#if DEBUG >= 1 -+ "1", -+#else -+ "0", -+#endif -+ UTILS_VERSION, wd->driver_name, -+ wd->conf_file_name, NULL}; -+ char *env[] = {NULL}; -+ -+ TRACE1("loading driver %s", wd->driver_name); -+ mutex_lock(&loader_mutex); -+ reinit_completion(&loader_complete); -+ ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, -+ UMH_WAIT_PROC); -+ if (ret) { -+ mutex_unlock(&loader_mutex); -+ ERROR("couldn't load driver %s; check system log " -+ "for messages from 'loadndisdriver'", -+ wd->driver_name); -+ EXIT1(return NULL); -+ } -+ wait_for_completion(&loader_complete); -+ TRACE1("%s", wd->driver_name); -+ wrap_driver = NULL; -+ nt_list_for_each(cur, &wrap_drivers) { -+ wrap_driver = container_of(cur, struct wrap_driver, -+ list); -+ if (!stricmp(wrap_driver->name, wd->driver_name)) { -+ wd->driver = wrap_driver; -+ break; -+ } else -+ wrap_driver = NULL; -+ } -+ mutex_unlock(&loader_mutex); -+ if (wrap_driver) -+ TRACE1("driver %s is loaded", wrap_driver->name); -+ else -+ ERROR("couldn't load driver '%s'", wd->driver_name); -+ } -+ EXIT1(return wrap_driver); -+} -+ -+/* load the driver files from userspace. */ -+static int load_sys_files(struct wrap_driver *driver, -+ struct load_driver *load_driver) -+{ -+ int i, err; -+ -+ TRACE1("num_pe_images = %d", load_driver->num_sys_files); -+ TRACE1("loading driver: %s", load_driver->name); -+ strncpy(driver->name, load_driver->name, sizeof(driver->name)); -+ driver->name[sizeof(driver->name)-1] = 0; -+ TRACE1("driver: %s", driver->name); -+ err = 0; -+ driver->num_pe_images = 0; -+ for (i = 0; i < load_driver->num_sys_files; i++) { -+ struct pe_image *pe_image; -+ pe_image = &driver->pe_images[driver->num_pe_images]; -+ -+ strncpy(pe_image->name, load_driver->sys_files[i].name, -+ sizeof(pe_image->name)); -+ pe_image->name[sizeof(pe_image->name)-1] = 0; -+ TRACE1("image size: %zu bytes", load_driver->sys_files[i].size); -+ -+#ifdef CONFIG_X86_64 -+#ifdef PAGE_KERNEL_EXECUTABLE -+ pe_image->image = -+ __vmalloc(load_driver->sys_files[i].size, -+ GFP_KERNEL | __GFP_HIGHMEM, -+ PAGE_KERNEL_EXECUTABLE); -+#elif defined PAGE_KERNEL_EXEC -+ pe_image->image = -+ __vmalloc(load_driver->sys_files[i].size, -+ GFP_KERNEL | __GFP_HIGHMEM, -+ PAGE_KERNEL_EXEC); -+#else -+#error x86_64 should have either PAGE_KERNEL_EXECUTABLE or PAGE_KERNEL_EXEC -+#endif -+#else -+ /* hate to play with kernel macros, but PAGE_KERNEL_EXEC is -+ * not available to modules! */ -+#ifdef cpu_has_nx -+ if (cpu_has_nx) -+ pe_image->image = -+ __vmalloc(load_driver->sys_files[i].size, -+ GFP_KERNEL | __GFP_HIGHMEM, -+ __pgprot(__PAGE_KERNEL & ~_PAGE_NX)); -+ else -+ pe_image->image = -+ vmalloc(load_driver->sys_files[i].size); -+#else -+ pe_image->image = -+ vmalloc(load_driver->sys_files[i].size); -+#endif -+#endif -+ if (!pe_image->image) { -+ ERROR("couldn't allocate memory"); -+ err = -ENOMEM; -+ break; -+ } -+ TRACE1("image is at %p", pe_image->image); -+ -+ if (copy_from_user(pe_image->image, -+ load_driver->sys_files[i].data, -+ load_driver->sys_files[i].size)) { -+ ERROR("couldn't load file %s", -+ load_driver->sys_files[i].name); -+ err = -EFAULT; -+ break; -+ } -+ pe_image->size = load_driver->sys_files[i].size; -+ driver->num_pe_images++; -+ } -+ -+ if (!err && link_pe_images(driver->pe_images, driver->num_pe_images)) { -+ ERROR("couldn't prepare driver '%s'", load_driver->name); -+ err = -EINVAL; -+ } -+ -+ if (driver->num_pe_images < load_driver->num_sys_files || err) { -+ for (i = 0; i < driver->num_pe_images; i++) -+ if (driver->pe_images[i].image) -+ vfree(driver->pe_images[i].image); -+ driver->num_pe_images = 0; -+ EXIT1(return err); -+ } else -+ EXIT1(return 0); -+} -+ -+struct wrap_bin_file *get_bin_file(char *bin_file_name) -+{ -+ int i = 0; -+ struct wrap_driver *driver, *cur; -+ -+ ENTER1("%s", bin_file_name); -+ mutex_lock(&loader_mutex); -+ driver = NULL; -+ nt_list_for_each_entry(cur, &wrap_drivers, list) { -+ for (i = 0; i < cur->num_bin_files; i++) -+ if (!stricmp(cur->bin_files[i].name, bin_file_name)) { -+ driver = cur; -+ break; -+ } -+ if (driver) -+ break; -+ } -+ mutex_unlock(&loader_mutex); -+ if (!driver) { -+ TRACE1("couldn't find bin file '%s'", bin_file_name); -+ return NULL; -+ } -+ -+ if (!driver->bin_files[i].data) { -+ char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_BIN_FILE, -+#if DEBUG >= 1 -+ "1", -+#else -+ "0", -+#endif -+ UTILS_VERSION, driver->name, -+ driver->bin_files[i].name, NULL}; -+ char *env[] = {NULL}; -+ int ret; -+ -+ TRACE1("loading bin file %s/%s", driver->name, -+ driver->bin_files[i].name); -+ mutex_lock(&loader_mutex); -+ reinit_completion(&loader_complete); -+ ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, -+ UMH_WAIT_PROC); -+ if (ret) { -+ mutex_unlock(&loader_mutex); -+ ERROR("couldn't load file %s/%s; check system log " -+ "for messages from 'loadndisdriver' (%d)", -+ driver->name, driver->bin_files[i].name, ret); -+ EXIT1(return NULL); -+ } -+ wait_for_completion(&loader_complete); -+ mutex_unlock(&loader_mutex); -+ if (!driver->bin_files[i].data) { -+ WARNING("couldn't load binary file %s", -+ driver->bin_files[i].name); -+ EXIT1(return NULL); -+ } -+ } -+ EXIT2(return &(driver->bin_files[i])); -+} -+ -+/* called with loader_mutex down */ -+static int add_bin_file(struct load_driver_file *driver_file) -+{ -+ struct wrap_driver *driver, *cur; -+ struct wrap_bin_file *bin_file; -+ int i = 0; -+ -+ driver = NULL; -+ nt_list_for_each_entry(cur, &wrap_drivers, list) { -+ for (i = 0; i < cur->num_bin_files; i++) -+ if (!stricmp(cur->bin_files[i].name, -+ driver_file->name)) { -+ driver = cur; -+ break; -+ } -+ if (driver) -+ break; -+ } -+ if (!driver) { -+ ERROR("couldn't find %s", driver_file->name); -+ return -EINVAL; -+ } -+ bin_file = &driver->bin_files[i]; -+ strncpy(bin_file->name, driver_file->name, sizeof(bin_file->name)); -+ bin_file->name[sizeof(bin_file->name)-1] = 0; -+ bin_file->data = vmalloc(driver_file->size); -+ if (!bin_file->data) { -+ ERROR("couldn't allocate memory"); -+ return -ENOMEM; -+ } -+ bin_file->size = driver_file->size; -+ if (copy_from_user(bin_file->data, driver_file->data, bin_file->size)) { -+ ERROR("couldn't copy data"); -+ free_bin_file(bin_file); -+ return -EFAULT; -+ } -+ return 0; -+} -+ -+void free_bin_file(struct wrap_bin_file *bin_file) -+{ -+ TRACE2("unloading %s", bin_file->name); -+ if (bin_file->data) -+ vfree(bin_file->data); -+ bin_file->data = NULL; -+ bin_file->size = 0; -+ EXIT2(return); -+} -+ -+/* load firmware files from userspace */ -+static int load_bin_files_info(struct wrap_driver *driver, -+ struct load_driver *load_driver) -+{ -+ struct wrap_bin_file *bin_files; -+ int i; -+ -+ ENTER1("%s, %d", load_driver->name, load_driver->num_bin_files); -+ driver->num_bin_files = 0; -+ driver->bin_files = NULL; -+ if (load_driver->num_bin_files == 0) -+ EXIT1(return 0); -+ bin_files = kzalloc(load_driver->num_bin_files * sizeof(*bin_files), -+ GFP_KERNEL); -+ if (!bin_files) { -+ ERROR("couldn't allocate memory"); -+ EXIT1(return -ENOMEM); -+ } -+ -+ for (i = 0; i < load_driver->num_bin_files; i++) { -+ strncpy(bin_files[i].name, load_driver->bin_files[i].name, -+ sizeof(bin_files[i].name)); -+ bin_files[i].name[sizeof(bin_files[i].name)-1] = 0; -+ TRACE2("loaded bin file %s", bin_files[i].name); -+ } -+ driver->num_bin_files = load_driver->num_bin_files; -+ driver->bin_files = bin_files; -+ EXIT1(return 0); -+} -+ -+/* load settings for a device. called with loader_mutex down */ -+static int load_settings(struct wrap_driver *wrap_driver, -+ struct load_driver *load_driver) -+{ -+ int i, num_settings; -+ -+ ENTER1("%p, %p", wrap_driver, load_driver); -+ -+ num_settings = 0; -+ for (i = 0; i < load_driver->num_settings; i++) { -+ struct load_device_setting *load_setting = -+ &load_driver->settings[i]; -+ struct wrap_device_setting *setting; -+ ULONG data1; -+ -+ setting = kzalloc(sizeof(*setting), GFP_KERNEL); -+ if (!setting) { -+ ERROR("couldn't allocate memory"); -+ break; -+ } -+ strncpy(setting->name, load_setting->name, -+ sizeof(setting->name)); -+ setting->name[sizeof(setting->name)-1] = 0; -+ strncpy(setting->value, load_setting->value, -+ sizeof(setting->value)); -+ setting->value[sizeof(setting->value)-1] = 0; -+ TRACE2("%p: %s=%s", setting, setting->name, setting->value); -+ -+ if (strcmp(setting->name, "driver_version") == 0) { -+ strncpy(wrap_driver->version, setting->value, -+ sizeof(wrap_driver->version)); -+ wrap_driver->version[sizeof(wrap_driver->version)-1] = 0; -+ } else if (strcmp(setting->name, "class_guid") == 0 && -+ sscanf(setting->value, "%x", &data1) == 1) { -+ wrap_driver->dev_type = wrap_device_type(data1); -+ if (wrap_driver->dev_type < 0) { -+ WARNING("unknown guid: %x", data1); -+ wrap_driver->dev_type = 0; -+ } -+ } -+ InsertTailList(&wrap_driver->settings, &setting->list); -+ num_settings++; -+ } -+ /* it is not a fatal error if some settings couldn't be loaded */ -+ if (num_settings > 0) -+ EXIT1(return 0); -+ else -+ EXIT1(return -EINVAL); -+} -+ -+void unload_wrap_device(struct wrap_device *wd) -+{ -+ struct nt_list *cur; -+ ENTER1("unloading device %p (%04X:%04X:%04X:%04X), driver %s", wd, -+ wd->vendor, wd->device, wd->subvendor, wd->subdevice, -+ wd->driver_name); -+ mutex_lock(&loader_mutex); -+ while ((cur = RemoveHeadList(&wd->settings))) { -+ struct wrap_device_setting *setting; -+ setting = container_of(cur, struct wrap_device_setting, list); -+ kfree(setting); -+ } -+ RemoveEntryList(&wd->list); -+ mutex_unlock(&loader_mutex); -+ kfree(wd); -+ EXIT1(return); -+} -+ -+/* should be called with loader_mutex down */ -+void unload_wrap_driver(struct wrap_driver *driver) -+{ -+ int i; -+ struct driver_object *drv_obj; -+ struct nt_list *cur, *next; -+ -+ ENTER1("unloading driver: %s (%p)", driver->name, driver); -+ TRACE1("freeing %d images", driver->num_pe_images); -+ drv_obj = driver->drv_obj; -+ for (i = 0; i < driver->num_pe_images; i++) -+ if (driver->pe_images[i].image) { -+ TRACE1("freeing image at %p", -+ driver->pe_images[i].image); -+ vfree(driver->pe_images[i].image); -+ } -+ -+ TRACE1("freeing %d bin files", driver->num_bin_files); -+ for (i = 0; i < driver->num_bin_files; i++) { -+ TRACE1("freeing image at %p", driver->bin_files[i].data); -+ if (driver->bin_files[i].data) -+ vfree(driver->bin_files[i].data); -+ } -+ kfree(driver->bin_files); -+ RtlFreeUnicodeString(&drv_obj->name); -+ RemoveEntryList(&driver->list); -+ nt_list_for_each_safe(cur, next, &driver->settings) { -+ struct wrap_device_setting *setting; -+ struct ndis_configuration_parameter *param; -+ -+ setting = container_of(cur, struct wrap_device_setting, list); -+ TRACE2("%p", setting); -+ param = setting->encoded; -+ if (param) { -+ TRACE2("%p", param); -+ if (param->type == NdisParameterString) -+ RtlFreeUnicodeString(¶m->data.string); -+ ExFreePool(param); -+ } -+ kfree(setting); -+ } -+ /* this frees driver */ -+ free_custom_extensions(drv_obj->drv_ext); -+ kfree(drv_obj->drv_ext); -+ TRACE1("drv_obj: %p", drv_obj); -+ -+ EXIT1(return); -+} -+ -+/* call the entry point of the driver */ -+static int start_wrap_driver(struct wrap_driver *driver) -+{ -+ int i; -+ NTSTATUS ret, res; -+ struct driver_object *drv_obj; -+ typeof(driver->pe_images[0].entry) entry; -+ -+ ENTER1("%s", driver->name); -+ drv_obj = driver->drv_obj; -+ for (ret = res = 0, i = 0; i < driver->num_pe_images; i++) -+ /* dlls are already started by loader */ -+ if (driver->pe_images[i].type == IMAGE_FILE_EXECUTABLE_IMAGE) { -+ entry = driver->pe_images[i].entry; -+ drv_obj->start = driver->pe_images[i].entry; -+ drv_obj->driver_size = driver->pe_images[i].size; -+ TRACE1("entry: %p, %p, drv_obj: %p", -+ entry, *entry, drv_obj); -+ res = LIN2WIN2(entry, drv_obj, &drv_obj->name); -+ ret |= res; -+ TRACE1("entry returns %08X", res); -+ break; -+ } -+ if (ret) { -+ ERROR("driver initialization failed: %08X", ret); -+ RtlFreeUnicodeString(&drv_obj->name); -+ /* this frees ndis_driver */ -+ free_custom_extensions(drv_obj->drv_ext); -+ kfree(drv_obj->drv_ext); -+ TRACE1("drv_obj: %p", drv_obj); -+ ObDereferenceObject(drv_obj); -+ EXIT1(return -EINVAL); -+ } -+ EXIT1(return 0); -+} -+ -+/* -+ * add driver to list of loaded driver but make sure this driver is -+ * not loaded before. called with loader_mutex down -+ */ -+static int add_wrap_driver(struct wrap_driver *driver) -+{ -+ struct wrap_driver *tmp; -+ -+ ENTER1("name: %s", driver->name); -+ nt_list_for_each_entry(tmp, &wrap_drivers, list) { -+ if (stricmp(tmp->name, driver->name) == 0) { -+ ERROR("cannot add duplicate driver"); -+ EXIT1(return -EBUSY); -+ } -+ } -+ InsertHeadList(&wrap_drivers, &driver->list); -+ EXIT1(return 0); -+} -+ -+/* load a driver from userspace and initialize it. called with -+ * loader_mutex down */ -+static int load_user_space_driver(struct load_driver *load_driver) -+{ -+ struct driver_object *drv_obj; -+ struct ansi_string ansi_reg; -+ struct wrap_driver *wrap_driver = NULL; -+ -+ ENTER1("%p", load_driver); -+ drv_obj = allocate_object(sizeof(*drv_obj), OBJECT_TYPE_DRIVER, NULL); -+ if (!drv_obj) { -+ ERROR("couldn't allocate memory"); -+ EXIT1(return -ENOMEM); -+ } -+ TRACE1("drv_obj: %p", drv_obj); -+ drv_obj->drv_ext = kzalloc(sizeof(*(drv_obj->drv_ext)), GFP_KERNEL); -+ if (!drv_obj->drv_ext) { -+ ERROR("couldn't allocate memory"); -+ ObDereferenceObject(drv_obj); -+ EXIT1(return -ENOMEM); -+ } -+ InitializeListHead(&drv_obj->drv_ext->custom_ext); -+ if (IoAllocateDriverObjectExtension(drv_obj, -+ (void *)WRAP_DRIVER_CLIENT_ID, -+ sizeof(*wrap_driver), -+ (void **)&wrap_driver) != -+ STATUS_SUCCESS) -+ EXIT1(return -ENOMEM); -+ TRACE1("driver: %p", wrap_driver); -+ memset(wrap_driver, 0, sizeof(*wrap_driver)); -+ InitializeListHead(&wrap_driver->list); -+ InitializeListHead(&wrap_driver->settings); -+ wrap_driver->drv_obj = drv_obj; -+ RtlInitAnsiString(&ansi_reg, "/tmp"); -+ if (RtlAnsiStringToUnicodeString(&drv_obj->name, &ansi_reg, TRUE) != -+ STATUS_SUCCESS) { -+ ERROR("couldn't initialize registry path"); -+ free_custom_extensions(drv_obj->drv_ext); -+ kfree(drv_obj->drv_ext); -+ TRACE1("drv_obj: %p", drv_obj); -+ ObDereferenceObject(drv_obj); -+ EXIT1(return -EINVAL); -+ } -+ strncpy(wrap_driver->name, load_driver->name, sizeof(wrap_driver->name)); -+ wrap_driver->name[sizeof(wrap_driver->name)-1] = 0; -+ if (load_sys_files(wrap_driver, load_driver) || -+ load_bin_files_info(wrap_driver, load_driver) || -+ load_settings(wrap_driver, load_driver) || -+ start_wrap_driver(wrap_driver) || -+ add_wrap_driver(wrap_driver)) { -+ unload_wrap_driver(wrap_driver); -+ ObDereferenceObject(drv_obj); -+ EXIT1(return -EINVAL); -+ } else { -+ printk(KERN_INFO "%s: driver %s (%s) loaded\n", -+ DRIVER_NAME, wrap_driver->name, wrap_driver->version); -+ add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); -+ EXIT1(return 0); -+ } -+} -+ -+static struct pci_device_id wrap_pci_id_table[] = { -+ { -+ .vendor = PCI_ANY_ID, -+ .device = PCI_ANY_ID, -+ .subvendor = PCI_ANY_ID, -+ .subdevice = PCI_ANY_ID, -+ .class = 0, -+ .class_mask = 0, -+ .driver_data = 0 -+ } -+}; -+ -+static struct pci_driver wrap_pci_driver = { -+ .name = DRIVER_NAME, -+ .id_table = wrap_pci_id_table, -+ .probe = wrap_pnp_start_pci_device, -+ .remove = wrap_pnp_remove_pci_device, -+ .suspend = wrap_pnp_suspend_pci_device, -+ .resume = wrap_pnp_resume_pci_device, -+}; -+ -+#ifdef ENABLE_USB -+static struct usb_device_id wrap_usb_id_table[] = { -+ { -+ .driver_info = 1 -+ }, -+}; -+ -+static struct usb_driver wrap_usb_driver = { -+ .name = DRIVER_NAME, -+ .id_table = wrap_usb_id_table, -+ .probe = wrap_pnp_start_usb_device, -+ .disconnect = wrap_pnp_remove_usb_device, -+ .suspend = wrap_pnp_suspend_usb_device, -+ .resume = wrap_pnp_resume_usb_device, -+}; -+#endif -+ -+/* register drivers for pci and usb */ -+static void register_devices(void) -+{ -+ int res; -+ -+ res = pci_register_driver(&wrap_pci_driver); -+ if (res < 0) { -+ ERROR("couldn't register pci driver: %d", res); -+ wrap_pci_driver.name = NULL; -+ } -+ -+#ifdef ENABLE_USB -+ res = usb_register(&wrap_usb_driver); -+ if (res < 0) { -+ ERROR("couldn't register usb driver: %d", res); -+ wrap_usb_driver.name = NULL; -+ } -+#endif -+ EXIT1(return); -+} -+ -+static void unregister_devices(void) -+{ -+ struct nt_list *cur, *next; -+ -+ mutex_lock(&loader_mutex); -+ nt_list_for_each_safe(cur, next, &wrap_devices) { -+ struct wrap_device *wd; -+ wd = container_of(cur, struct wrap_device, list); -+ set_bit(HW_DISABLED, &wd->hw_status); -+ } -+ mutex_unlock(&loader_mutex); -+ -+ if (wrap_pci_driver.name) -+ pci_unregister_driver(&wrap_pci_driver); -+#ifdef ENABLE_USB -+ if (wrap_usb_driver.name) -+ usb_deregister(&wrap_usb_driver); -+#endif -+} -+ -+struct wrap_device *load_wrap_device(struct load_device *load_device) -+{ -+ int ret; -+ struct nt_list *cur; -+ struct wrap_device *wd = NULL; -+ char vendor[5], device[5], subvendor[5], subdevice[5], bus[5]; -+ -+ ENTER1("%04x, %04x, %04x, %04x", load_device->vendor, -+ load_device->device, load_device->subvendor, -+ load_device->subdevice); -+ if (sprintf(vendor, "%04x", load_device->vendor) == 4 && -+ sprintf(device, "%04x", load_device->device) == 4 && -+ sprintf(subvendor, "%04x", load_device->subvendor) == 4 && -+ sprintf(subdevice, "%04x", load_device->subdevice) == 4 && -+ sprintf(bus, "%04x", load_device->bus) == 4) { -+ char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DEVICE, -+#if DEBUG >= 1 -+ "1", -+#else -+ "0", -+#endif -+ UTILS_VERSION, vendor, device, -+ subvendor, subdevice, bus, NULL}; -+ char *env[] = {NULL}; -+ TRACE2("%s, %s, %s, %s, %s", vendor, device, -+ subvendor, subdevice, bus); -+ mutex_lock(&loader_mutex); -+ reinit_completion(&loader_complete); -+ ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, -+ UMH_WAIT_PROC); -+ if (ret) { -+ mutex_unlock(&loader_mutex); -+ TRACE1("couldn't load device %04x:%04x; check system " -+ "log for messages from 'loadndisdriver'", -+ load_device->vendor, load_device->device); -+ EXIT1(return NULL); -+ } -+ wait_for_completion(&loader_complete); -+ wd = NULL; -+ nt_list_for_each(cur, &wrap_devices) { -+ wd = container_of(cur, struct wrap_device, list); -+ TRACE2("%p, %04x, %04x, %04x, %04x", wd, wd->vendor, -+ wd->device, wd->subvendor, wd->subdevice); -+ if (wd->vendor == load_device->vendor && -+ wd->device == load_device->device) -+ break; -+ else -+ wd = NULL; -+ } -+ mutex_unlock(&loader_mutex); -+ } else -+ wd = NULL; -+ EXIT1(return wd); -+} -+ -+struct wrap_device *get_wrap_device(void *dev, int bus) -+{ -+ struct nt_list *cur; -+ struct wrap_device *wd; -+ -+ mutex_lock(&loader_mutex); -+ wd = NULL; -+ nt_list_for_each(cur, &wrap_devices) { -+ wd = container_of(cur, struct wrap_device, list); -+ if (bus == WRAP_PCI_BUS && -+ wrap_is_pci_bus(wd->dev_bus) && wd->pci.pdev == dev) -+ break; -+ else if (bus == WRAP_USB_BUS && -+ wrap_is_usb_bus(wd->dev_bus) && wd->usb.udev == dev) -+ break; -+ else -+ wd = NULL; -+ } -+ mutex_unlock(&loader_mutex); -+ return wd; -+} -+ -+/* called with loader_mutex is down */ -+static long wrapper_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ struct load_driver *load_driver; -+ struct load_device load_device; -+ struct load_driver_file load_bin_file; -+ int ret; -+ void __user *addr = (void __user *)arg; -+ -+ ENTER1("cmd: 0x%x", cmd); -+ -+ ret = 0; -+ switch (cmd) { -+ case WRAP_IOCTL_LOAD_DEVICE: -+ if (copy_from_user(&load_device, addr, sizeof(load_device))) { -+ ret = -EFAULT; -+ break; -+ } -+ TRACE2("%04x, %04x, %04x, %04x", load_device.vendor, -+ load_device.device, load_device.subvendor, -+ load_device.subdevice); -+ if (load_device.vendor) { -+ struct wrap_device *wd; -+ wd = kzalloc(sizeof(*wd), GFP_KERNEL); -+ if (!wd) { -+ ret = -ENOMEM; -+ break; -+ } -+ InitializeListHead(&wd->settings); -+ wd->dev_bus = WRAP_BUS(load_device.bus); -+ wd->vendor = load_device.vendor; -+ wd->device = load_device.device; -+ wd->subvendor = load_device.subvendor; -+ wd->subdevice = load_device.subdevice; -+ strncpy(wd->conf_file_name, load_device.conf_file_name, -+ sizeof(wd->conf_file_name)); -+ wd->conf_file_name[sizeof(wd->conf_file_name)-1] = 0; -+ strncpy(wd->driver_name, load_device.driver_name, -+ sizeof(wd->driver_name)); -+ wd->driver_name[sizeof(wd->driver_name)-1] = 0; -+ InsertHeadList(&wrap_devices, &wd->list); -+ ret = 0; -+ } else -+ ret = -EINVAL; -+ break; -+ case WRAP_IOCTL_LOAD_DRIVER: -+ TRACE1("loading driver at %p", addr); -+ load_driver = vmalloc(sizeof(*load_driver)); -+ if (!load_driver) { -+ ret = -ENOMEM; -+ break; -+ } -+ if (copy_from_user(load_driver, addr, sizeof(*load_driver))) -+ ret = -EFAULT; -+ else -+ ret = load_user_space_driver(load_driver); -+ vfree(load_driver); -+ break; -+ case WRAP_IOCTL_LOAD_BIN_FILE: -+ if (copy_from_user(&load_bin_file, addr, sizeof(load_bin_file))) -+ ret = -EFAULT; -+ else -+ ret = add_bin_file(&load_bin_file); -+ break; -+ default: -+ ERROR("unknown ioctl 0x%x", cmd); -+ ret = -EINVAL; -+ break; -+ } -+ complete(&loader_complete); -+ EXIT1(return ret); -+} -+ -+#ifdef CONFIG_COMPAT -+static int copy_load_driver_file32(struct load_driver_file *k, -+ struct load_driver_file32 __user *u) -+{ -+ u32 data; -+ -+ if (copy_from_user(&k->driver_name, &u->driver_name, -+ sizeof(u->driver_name) + sizeof(u->name))) -+ return -EFAULT; -+ -+ if (get_user(k->size, &u->size)) -+ return -EFAULT; -+ if (get_user(data, &u->data)) -+ return -EFAULT; -+ -+ k->data = (void __user *)(unsigned long)data; -+ return 0; -+} -+ -+static int copy_load_driver32(struct load_driver *k, -+ struct load_driver32 __user *u) -+{ -+ int i; -+ -+ if (copy_from_user(&k->name, &u->name, -+ sizeof(u->name) + sizeof(u->conf_file_name))) -+ return -EFAULT; -+ -+ if (get_user(k->num_sys_files, &u->num_sys_files)) -+ return -EFAULT; -+ -+ for (i = 0; i < k->num_sys_files; i++) -+ if (copy_load_driver_file32(&k->sys_files[i], &u->sys_files[i])) -+ return -EFAULT; -+ -+ if (get_user(k->num_settings, &u->num_settings)) -+ return -EFAULT; -+ -+ if (copy_from_user(&k->settings, &u->settings, -+ sizeof(u->settings[0]) * k->num_settings)) -+ return -EFAULT; -+ -+ if (get_user(k->num_bin_files, &u->num_bin_files)) -+ return -EFAULT; -+ -+ for (i = 0; i < k->num_bin_files; i++) -+ if (copy_load_driver_file32(&k->bin_files[i], &u->bin_files[i])) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static long wrapper_ioctl_compat(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ int ret = 0; -+ void __user *addr = (void __user *)arg; -+ struct load_driver *kdriver; -+ struct load_driver32 __user *udriver = addr; -+ struct load_driver_file kfile; -+ struct load_driver_file32 __user *ufile = addr; -+ -+ ENTER1("cmd: 0x%x", cmd); -+ -+ switch (cmd) { -+ case WRAP_IOCTL_LOAD_DEVICE32: -+ return wrapper_ioctl(file, WRAP_IOCTL_LOAD_DEVICE, arg); -+ case WRAP_IOCTL_LOAD_DRIVER32: -+ TRACE1("loading driver at %p", addr); -+ kdriver = vmalloc(sizeof(*kdriver)); -+ if (!kdriver) { -+ ret = -ENOMEM; -+ break; -+ } -+ -+ ret = copy_load_driver32(kdriver, udriver); -+ if (!ret) -+ ret = load_user_space_driver(kdriver); -+ -+ vfree(kdriver); -+ break; -+ case WRAP_IOCTL_LOAD_BIN_FILE32: -+ ret = copy_load_driver_file32(&kfile, ufile); -+ if (ret) -+ break; -+ -+ ret = add_bin_file(&kfile); -+ break; -+ default: -+ ERROR("unknown ioctl 0x%x", cmd); -+ ret = -EINVAL; -+ break; -+ } -+ complete(&loader_complete); -+ EXIT1(return ret); -+} -+#endif -+ -+static int wrapper_ioctl_release(struct inode *inode, struct file *file) -+{ -+ ENTER1(""); -+ complete(&loader_complete); -+ return 0; -+} -+ -+static struct file_operations wrapper_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = wrapper_ioctl, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = wrapper_ioctl_compat, -+#endif -+ .release = wrapper_ioctl_release, -+}; -+ -+static struct miscdevice wrapper_misc = { -+ .name = DRIVER_NAME, -+ .minor = MISC_DYNAMIC_MINOR, -+ .fops = &wrapper_fops -+}; -+ -+int loader_init(void) -+{ -+ int err; -+ -+ InitializeListHead(&wrap_drivers); -+ InitializeListHead(&wrap_devices); -+ mutex_init(&loader_mutex); -+ init_completion(&loader_complete); -+ if ((err = misc_register(&wrapper_misc)) < 0) { -+ ERROR("couldn't register module (%d)", err); -+ unregister_devices(); -+ EXIT1(return err); -+ } -+ register_devices(); -+ EXIT1(return 0); -+} -+ -+void loader_exit(void) -+{ -+ struct nt_list *cur, *next; -+ -+ ENTER1(""); -+ misc_deregister(&wrapper_misc); -+ unregister_devices(); -+ mutex_lock(&loader_mutex); -+ nt_list_for_each_safe(cur, next, &wrap_drivers) { -+ struct wrap_driver *driver; -+ driver = container_of(cur, struct wrap_driver, list); -+ unload_wrap_driver(driver); -+ } -+ mutex_unlock(&loader_mutex); -+ EXIT1(return); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/loader.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.h ---- linux-3.12.2/3rdparty/ndiswrapper/loader.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/loader.h 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,108 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _LOADER_H_ -+#define _LOADER_H_ -+ -+#include "ndiswrapper.h" -+ -+#ifndef __KERNEL__ -+#define __user -+#endif -+ -+struct load_driver_file { -+ char driver_name[MAX_DRIVER_NAME_LEN]; -+ char name[MAX_DRIVER_NAME_LEN]; -+ size_t size; -+ void __user *data; -+}; -+ -+struct load_device_setting { -+ char name[MAX_SETTING_NAME_LEN]; -+ char value[MAX_SETTING_VALUE_LEN]; -+}; -+ -+struct load_device { -+ int bus; -+ int vendor; -+ int device; -+ int subvendor; -+ int subdevice; -+ char conf_file_name[MAX_DRIVER_NAME_LEN]; -+ char driver_name[MAX_DRIVER_NAME_LEN]; -+}; -+ -+struct load_driver { -+ char name[MAX_DRIVER_NAME_LEN]; -+ char conf_file_name[MAX_DRIVER_NAME_LEN]; -+ unsigned int num_sys_files; -+ struct load_driver_file sys_files[MAX_DRIVER_PE_IMAGES]; -+ unsigned int num_settings; -+ struct load_device_setting settings[MAX_DEVICE_SETTINGS]; -+ unsigned int num_bin_files; -+ struct load_driver_file bin_files[MAX_DRIVER_BIN_FILES]; -+}; -+ -+#define WRAP_IOCTL_LOAD_DEVICE _IOW(('N' + 'd' + 'i' + 'S'), 0, \ -+ struct load_device *) -+#define WRAP_IOCTL_LOAD_DRIVER _IOW(('N' + 'd' + 'i' + 'S'), 1, \ -+ struct load_driver *) -+#define WRAP_IOCTL_LOAD_BIN_FILE _IOW(('N' + 'd' + 'i' + 'S'), 2, \ -+ struct load_driver_file *) -+ -+#ifdef CONFIG_COMPAT -+struct load_driver_file32 { -+ char driver_name[MAX_DRIVER_NAME_LEN]; -+ char name[MAX_DRIVER_NAME_LEN]; -+ u32 size; -+ u32 data; -+}; -+ -+struct load_driver32 { -+ char name[MAX_DRIVER_NAME_LEN]; -+ char conf_file_name[MAX_DRIVER_NAME_LEN]; -+ u32 num_sys_files; -+ struct load_driver_file32 sys_files[MAX_DRIVER_PE_IMAGES]; -+ u32 num_settings; -+ struct load_device_setting settings[MAX_DEVICE_SETTINGS]; -+ u32 num_bin_files; -+ struct load_driver_file32 bin_files[MAX_DRIVER_BIN_FILES]; -+} __packed; -+ -+#define WRAP_IOCTL_LOAD_DEVICE32 _IOW(('N' + 'd' + 'i' + 'S'), 0, u32) -+#define WRAP_IOCTL_LOAD_DRIVER32 _IOW(('N' + 'd' + 'i' + 'S'), 1, u32) -+#define WRAP_IOCTL_LOAD_BIN_FILE32 _IOW(('N' + 'd' + 'i' + 'S'), 2, u32) -+#endif -+ -+#define WRAP_CMD_LOAD_DEVICE "load_device" -+#define WRAP_CMD_LOAD_DRIVER "load_driver" -+#define WRAP_CMD_LOAD_BIN_FILE "load_bin_file" -+ -+int loader_init(void); -+void loader_exit(void); -+ -+#ifdef __KERNEL__ -+struct wrap_device *load_wrap_device(struct load_device *load_device); -+struct wrap_driver *load_wrap_driver(struct wrap_device *device); -+struct wrap_bin_file *get_bin_file(char *bin_file_name); -+void free_bin_file(struct wrap_bin_file *bin_file); -+void unload_wrap_driver(struct wrap_driver *driver); -+void unload_wrap_device(struct wrap_device *wd); -+struct wrap_device *get_wrap_device(void *dev, int bus_type); -+ -+extern struct mutex loader_mutex; -+#endif -+ -+#endif /* LOADER_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/longlong.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/longlong.h ---- linux-3.12.2/3rdparty/ndiswrapper/longlong.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/longlong.h 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,1333 @@ -+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic. -+ Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000 -+ Free Software Foundation, Inc. -+ -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library 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 -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, write to the Free -+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ 02111-1307 USA. */ -+ -+/* You have to define the following before including this file: -+ -+ UWtype -- An unsigned type, default type for operations (typically a "word") -+ UHWtype -- An unsigned type, at least half the size of UWtype. -+ UDWtype -- An unsigned type, at least twice as large a UWtype -+ W_TYPE_SIZE -- size in bits of UWtype -+ -+ UQItype -- Unsigned 8 bit type. -+ SItype, USItype -- Signed and unsigned 32 bit types. -+ DItype, UDItype -- Signed and unsigned 64 bit types. -+ -+ On a 32 bit machine UWtype should typically be USItype; -+ on a 64 bit machine, UWtype should typically be UDItype. -+*/ -+ -+#define __BITS4 (W_TYPE_SIZE / 4) -+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) -+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1)) -+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2)) -+ -+#ifndef W_TYPE_SIZE -+#define W_TYPE_SIZE 32 -+#define UWtype USItype -+#define UHWtype USItype -+#define UDWtype UDItype -+#endif -+ -+/* Define auxiliary asm macros. -+ -+ 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two -+ UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype -+ word product in HIGH_PROD and LOW_PROD. -+ -+ 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a -+ UDWtype product. This is just a variant of umul_ppmm. -+ -+ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, -+ denominator) divides a UDWtype, composed by the UWtype integers -+ HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient -+ in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less -+ than DENOMINATOR for correct operation. If, in addition, the most -+ significant bit of DENOMINATOR must be 1, then the pre-processor symbol -+ UDIV_NEEDS_NORMALIZATION is defined to 1. -+ -+ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, -+ denominator). Like udiv_qrnnd but the numbers are signed. The quotient -+ is rounded towards 0. -+ -+ 5) count_leading_zeros(count, x) counts the number of zero-bits from the -+ msb to the first nonzero bit in the UWtype X. This is the number of -+ steps X needs to be shifted left to set the msb. Undefined for X == 0, -+ unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value. -+ -+ 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts -+ from the least significant end. -+ -+ 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, -+ high_addend_2, low_addend_2) adds two UWtype integers, composed by -+ HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 -+ respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow -+ (i.e. carry out) is not stored anywhere, and is lost. -+ -+ 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, -+ high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, -+ composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and -+ LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE -+ and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, -+ and is lost. -+ -+ If any of these macros are left undefined for a particular CPU, -+ C macros are used. */ -+ -+/* The CPUs come in alphabetical order below. -+ -+ Please add support for more CPUs here, or improve the current support -+ for the CPUs below! -+ (E.g. WE32100, IBM360.) */ -+ -+#if defined (__GNUC__) && !defined (NO_ASM) -+ -+/* We sometimes need to clobber "cc" with gcc2, but that would not be -+ understood by gcc1. Use cpp to avoid major code duplication. */ -+#if __GNUC__ < 2 -+#define __CLOBBER_CC -+#define __AND_CLOBBER_CC -+#else /* __GNUC__ >= 2 */ -+#define __CLOBBER_CC : "cc" -+#define __AND_CLOBBER_CC , "cc" -+#endif /* __GNUC__ < 2 */ -+ -+#if defined (__alpha) && W_TYPE_SIZE == 64 -+#define umul_ppmm(ph, pl, m0, m1) \ -+ do { \ -+ UDItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("umulh %r1,%2,%0" \ -+ : "=r" ((UDItype) ph) \ -+ : "%rJ" (__m0), \ -+ "rI" (__m1)); \ -+ (pl) = __m0 * __m1; \ -+ } while (0) -+#define UMUL_TIME 46 -+#ifndef LONGLONG_STANDALONE -+#define udiv_qrnnd(q, r, n1, n0, d) \ -+ do { UDItype __r; \ -+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \ -+ (r) = __r; \ -+ } while (0) -+extern UDItype __udiv_qrnnd (UDItype *, UDItype, UDItype, UDItype); -+#define UDIV_TIME 220 -+#endif /* LONGLONG_STANDALONE */ -+#ifdef __alpha_cix__ -+#define count_leading_zeros(COUNT,X) \ -+ __asm__("ctlz %1,%0" : "=r"(COUNT) : "r"(X)) -+#define count_trailing_zeros(COUNT,X) \ -+ __asm__("cttz %1,%0" : "=r"(COUNT) : "r"(X)) -+#define COUNT_LEADING_ZEROS_0 64 -+#else -+extern const UQItype __clz_tab[]; -+#define count_leading_zeros(COUNT,X) \ -+ do { \ -+ UDItype __xr = (X), __t, __a; \ -+ __asm__("cmpbge $31,%1,%0" : "=r"(__t) : "r"(__xr)); \ -+ __a = __clz_tab[__t ^ 0xff] - 1; \ -+ __asm__("extbl %1,%2,%0" : "=r"(__t) : "r"(__xr), "r"(__a)); \ -+ (COUNT) = 64 - (__clz_tab[__t] + __a*8); \ -+ } while (0) -+#define count_trailing_zeros(COUNT,X) \ -+ do { \ -+ UDItype __xr = (X), __t, __a; \ -+ __asm__("cmpbge $31,%1,%0" : "=r"(__t) : "r"(__xr)); \ -+ __t = ~__t & -~__t; \ -+ __a = ((__t & 0xCC) != 0) * 2; \ -+ __a += ((__t & 0xF0) != 0) * 4; \ -+ __a += ((__t & 0xAA) != 0); \ -+ __asm__("extbl %1,%2,%0" : "=r"(__t) : "r"(__xr), "r"(__a)); \ -+ __a <<= 3; \ -+ __t &= -__t; \ -+ __a += ((__t & 0xCC) != 0) * 2; \ -+ __a += ((__t & 0xF0) != 0) * 4; \ -+ __a += ((__t & 0xAA) != 0); \ -+ (COUNT) = __a; \ -+ } while (0) -+#endif /* __alpha_cix__ */ -+#endif /* __alpha */ -+ -+#if defined (__arc__) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("add.f %1, %4, %5\n\tadc %0, %2, %3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "%r" ((USItype) (ah)), \ -+ "rIJ" ((USItype) (bh)), \ -+ "%r" ((USItype) (al)), \ -+ "rIJ" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("sub.f %1, %4, %5\n\tsbc %0, %2, %3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "r" ((USItype) (ah)), \ -+ "rIJ" ((USItype) (bh)), \ -+ "r" ((USItype) (al)), \ -+ "rIJ" ((USItype) (bl))) -+/* Call libgcc routine. */ -+#define umul_ppmm(w1, w0, u, v) \ -+do { \ -+ DWunion __w; \ -+ __w.ll = __umulsidi3 (u, v); \ -+ w1 = __w.s.high; \ -+ w0 = __w.s.low; \ -+} while (0) -+#define __umulsidi3 __umulsidi3 -+UDItype __umulsidi3 (USItype, USItype); -+#endif -+ -+#if defined (__arm__) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("adds %1, %4, %5\n\tadc %0, %2, %3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "%r" ((USItype) (ah)), \ -+ "rI" ((USItype) (bh)), \ -+ "%r" ((USItype) (al)), \ -+ "rI" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("subs %1, %4, %5\n\tsbc %0, %2, %3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "r" ((USItype) (ah)), \ -+ "rI" ((USItype) (bh)), \ -+ "r" ((USItype) (al)), \ -+ "rI" ((USItype) (bl))) -+#define umul_ppmm(xh, xl, a, b) \ -+{register USItype __t0, __t1, __t2; \ -+ __asm__ ("%@ Inlined umul_ppmm\n" \ -+ " mov %2, %5, lsr #16\n" \ -+ " mov %0, %6, lsr #16\n" \ -+ " bic %3, %5, %2, lsl #16\n" \ -+ " bic %4, %6, %0, lsl #16\n" \ -+ " mul %1, %3, %4\n" \ -+ " mul %4, %2, %4\n" \ -+ " mul %3, %0, %3\n" \ -+ " mul %0, %2, %0\n" \ -+ " adds %3, %4, %3\n" \ -+ " addcs %0, %0, #65536\n" \ -+ " adds %1, %1, %3, lsl #16\n" \ -+ " adc %0, %0, %3, lsr #16" \ -+ : "=&r" ((USItype) (xh)), \ -+ "=r" ((USItype) (xl)), \ -+ "=&r" (__t0), "=&r" (__t1), "=r" (__t2) \ -+ : "r" ((USItype) (a)), \ -+ "r" ((USItype) (b)));} -+#define UMUL_TIME 20 -+#define UDIV_TIME 100 -+#endif /* __arm__ */ -+ -+#if defined (__hppa) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("add %4,%5,%1\n\taddc %2,%3,%0" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "%rM" ((USItype) (ah)), \ -+ "rM" ((USItype) (bh)), \ -+ "%rM" ((USItype) (al)), \ -+ "rM" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("sub %4,%5,%1\n\tsubb %2,%3,%0" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "rM" ((USItype) (ah)), \ -+ "rM" ((USItype) (bh)), \ -+ "rM" ((USItype) (al)), \ -+ "rM" ((USItype) (bl))) -+#if defined (_PA_RISC1_1) -+#define umul_ppmm(w1, w0, u, v) \ -+ do { \ -+ union \ -+ { \ -+ UDItype __f; \ -+ struct {USItype __w1, __w0;} __w1w0; \ -+ } __t; \ -+ __asm__ ("xmpyu %1,%2,%0" \ -+ : "=x" (__t.__f) \ -+ : "x" ((USItype) (u)), \ -+ "x" ((USItype) (v))); \ -+ (w1) = __t.__w1w0.__w1; \ -+ (w0) = __t.__w1w0.__w0; \ -+ } while (0) -+#define UMUL_TIME 8 -+#else -+#define UMUL_TIME 30 -+#endif -+#define UDIV_TIME 40 -+#define count_leading_zeros(count, x) \ -+ do { \ -+ USItype __tmp; \ -+ __asm__ ( \ -+ "ldi 1,%0\n" \ -+" extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \ -+" extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n"\ -+" ldo 16(%0),%0 ; Yes. Perform add.\n" \ -+" extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \ -+" extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n"\ -+" ldo 8(%0),%0 ; Yes. Perform add.\n" \ -+" extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \ -+" extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n"\ -+" ldo 4(%0),%0 ; Yes. Perform add.\n" \ -+" extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \ -+" extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n"\ -+" ldo 2(%0),%0 ; Yes. Perform add.\n" \ -+" extru %1,30,1,%1 ; Extract bit 1.\n" \ -+" sub %0,%1,%0 ; Subtract it.\n" \ -+ : "=r" (count), "=r" (__tmp) : "1" (x)); \ -+ } while (0) -+#endif -+ -+#if (defined (__i370__) || defined (__mvs__)) && W_TYPE_SIZE == 32 -+#define umul_ppmm(xh, xl, m0, m1) \ -+ do { \ -+ union {UDItype __ll; \ -+ struct {USItype __h, __l;} __i; \ -+ } __xx; \ -+ USItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("mr %0,%3" \ -+ : "=r" (__xx.__i.__h), \ -+ "=r" (__xx.__i.__l) \ -+ : "%1" (__m0), \ -+ "r" (__m1)); \ -+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \ -+ (xh) += ((((SItype) __m0 >> 31) & __m1) \ -+ + (((SItype) __m1 >> 31) & __m0)); \ -+ } while (0) -+#define smul_ppmm(xh, xl, m0, m1) \ -+ do { \ -+ union {DItype __ll; \ -+ struct {USItype __h, __l;} __i; \ -+ } __xx; \ -+ __asm__ ("mr %0,%3" \ -+ : "=r" (__xx.__i.__h), \ -+ "=r" (__xx.__i.__l) \ -+ : "%1" (m0), \ -+ "r" (m1)); \ -+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \ -+ } while (0) -+#define sdiv_qrnnd(q, r, n1, n0, d) \ -+ do { \ -+ union {DItype __ll; \ -+ struct {USItype __h, __l;} __i; \ -+ } __xx; \ -+ __xx.__i.__h = n1; __xx.__i.__l = n0; \ -+ __asm__ ("dr %0,%2" \ -+ : "=r" (__xx.__ll) \ -+ : "0" (__xx.__ll), "r" (d)); \ -+ (q) = __xx.__i.__l; (r) = __xx.__i.__h; \ -+ } while (0) -+#endif -+ -+#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("addl %5,%1\n\tadcl %3,%0" \ -+ : "=r" (sh), \ -+ "=&r" (sl) \ -+ : "%0" ((USItype) (ah)), \ -+ "g" ((USItype) (bh)), \ -+ "%1" ((USItype) (al)), \ -+ "g" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("subl %5,%1\n\tsbbl %3,%0" \ -+ : "=r" (sh), \ -+ "=&r" (sl) \ -+ : "0" ((USItype) (ah)), \ -+ "g" ((USItype) (bh)), \ -+ "1" ((USItype) (al)), \ -+ "g" ((USItype) (bl))) -+#define umul_ppmm(w1, w0, u, v) \ -+ __asm__ ("mull %3" \ -+ : "=a" (w0), \ -+ "=d" (w1) \ -+ : "%0" ((USItype) (u)), \ -+ "rm" ((USItype) (v))) -+#define udiv_qrnnd(q, r, n1, n0, dv) \ -+ __asm__ ("divl %4" \ -+ : "=a" (q), \ -+ "=d" (r) \ -+ : "0" ((USItype) (n0)), \ -+ "1" ((USItype) (n1)), \ -+ "rm" ((USItype) (dv))) -+#define count_leading_zeros(count, x) \ -+ do { \ -+ USItype __cbtmp; \ -+ __asm__ ("bsrl %1,%0" \ -+ : "=r" (__cbtmp) : "rm" ((USItype) (x))); \ -+ (count) = __cbtmp ^ 31; \ -+ } while (0) -+#define count_trailing_zeros(count, x) \ -+ __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x))) -+#define UMUL_TIME 40 -+#define UDIV_TIME 40 -+#endif /* 80x86 */ -+ -+#if defined (__i960__) && W_TYPE_SIZE == 32 -+#define umul_ppmm(w1, w0, u, v) \ -+ ({union {UDItype __ll; \ -+ struct {USItype __l, __h;} __i; \ -+ } __xx; \ -+ __asm__ ("emul %2,%1,%0" \ -+ : "=d" (__xx.__ll) \ -+ : "%dI" ((USItype) (u)), \ -+ "dI" ((USItype) (v))); \ -+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;}) -+#define __umulsidi3(u, v) \ -+ ({UDItype __w; \ -+ __asm__ ("emul %2,%1,%0" \ -+ : "=d" (__w) \ -+ : "%dI" ((USItype) (u)), \ -+ "dI" ((USItype) (v))); \ -+ __w; }) -+#endif /* __i960__ */ -+ -+#if defined (__M32R__) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ /* The cmp clears the condition bit. */ \ -+ __asm__ ("cmp %0,%0\n\taddx %%5,%1\n\taddx %%3,%0" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "%0" ((USItype) (ah)), \ -+ "r" ((USItype) (bh)), \ -+ "%1" ((USItype) (al)), \ -+ "r" ((USItype) (bl)) \ -+ : "cbit") -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ /* The cmp clears the condition bit. */ \ -+ __asm__ ("cmp %0,%0\n\tsubx %5,%1\n\tsubx %3,%0" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "0" ((USItype) (ah)), \ -+ "r" ((USItype) (bh)), \ -+ "1" ((USItype) (al)), \ -+ "r" ((USItype) (bl)) \ -+ : "cbit") -+#endif /* __M32R__ */ -+ -+#if defined (__mc68000__) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \ -+ : "=d" ((USItype) (sh)), \ -+ "=&d" ((USItype) (sl)) \ -+ : "%0" ((USItype) (ah)), \ -+ "d" ((USItype) (bh)), \ -+ "%1" ((USItype) (al)), \ -+ "g" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0" \ -+ : "=d" ((USItype) (sh)), \ -+ "=&d" ((USItype) (sl)) \ -+ : "0" ((USItype) (ah)), \ -+ "d" ((USItype) (bh)), \ -+ "1" ((USItype) (al)), \ -+ "g" ((USItype) (bl))) -+ -+/* The '020, '030, '040 and CPU32 have 32x32->64 and 64/32->32q-32r. */ -+#if defined (__mc68020__) || defined(mc68020) \ -+ || defined(__mc68030__) || defined(mc68030) \ -+ || defined(__mc68040__) || defined(mc68040) \ -+ || defined(__mcpu32__) || defined(mcpu32) -+#define umul_ppmm(w1, w0, u, v) \ -+ __asm__ ("mulu%.l %3,%1:%0" \ -+ : "=d" ((USItype) (w0)), \ -+ "=d" ((USItype) (w1)) \ -+ : "%0" ((USItype) (u)), \ -+ "dmi" ((USItype) (v))) -+#define UMUL_TIME 45 -+#define udiv_qrnnd(q, r, n1, n0, d) \ -+ __asm__ ("divu%.l %4,%1:%0" \ -+ : "=d" ((USItype) (q)), \ -+ "=d" ((USItype) (r)) \ -+ : "0" ((USItype) (n0)), \ -+ "1" ((USItype) (n1)), \ -+ "dmi" ((USItype) (d))) -+#define UDIV_TIME 90 -+#define sdiv_qrnnd(q, r, n1, n0, d) \ -+ __asm__ ("divs%.l %4,%1:%0" \ -+ : "=d" ((USItype) (q)), \ -+ "=d" ((USItype) (r)) \ -+ : "0" ((USItype) (n0)), \ -+ "1" ((USItype) (n1)), \ -+ "dmi" ((USItype) (d))) -+ -+#else /* not mc68020 */ -+#if !defined(__mcf5200__) -+/* %/ inserts REGISTER_PREFIX, %# inserts IMMEDIATE_PREFIX. */ -+#define umul_ppmm(xh, xl, a, b) \ -+ __asm__ ("| Inlined umul_ppmm\n" \ -+ " move%.l %2,%/d0\n" \ -+ " move%.l %3,%/d1\n" \ -+ " move%.l %/d0,%/d2\n" \ -+ " swap %/d0\n" \ -+ " move%.l %/d1,%/d3\n" \ -+ " swap %/d1\n" \ -+ " move%.w %/d2,%/d4\n" \ -+ " mulu %/d3,%/d4\n" \ -+ " mulu %/d1,%/d2\n" \ -+ " mulu %/d0,%/d3\n" \ -+ " mulu %/d0,%/d1\n" \ -+ " move%.l %/d4,%/d0\n" \ -+ " eor%.w %/d0,%/d0\n" \ -+ " swap %/d0\n" \ -+ " add%.l %/d0,%/d2\n" \ -+ " add%.l %/d3,%/d2\n" \ -+ " jcc 1f\n" \ -+ " add%.l %#65536,%/d1\n" \ -+ "1: swap %/d2\n" \ -+ " moveq %#0,%/d0\n" \ -+ " move%.w %/d2,%/d0\n" \ -+ " move%.w %/d4,%/d2\n" \ -+ " move%.l %/d2,%1\n" \ -+ " add%.l %/d1,%/d0\n" \ -+ " move%.l %/d0,%0" \ -+ : "=g" ((USItype) (xh)), \ -+ "=g" ((USItype) (xl)) \ -+ : "g" ((USItype) (a)), \ -+ "g" ((USItype) (b)) \ -+ : "d0", "d1", "d2", "d3", "d4") -+#define UMUL_TIME 100 -+#define UDIV_TIME 400 -+#endif /* not mcf5200 */ -+#endif /* not mc68020 */ -+ -+/* The '020, '030, '040 and '060 have bitfield insns. */ -+#if defined (__mc68020__) || defined(mc68020) \ -+ || defined(__mc68030__) || defined(mc68030) \ -+ || defined(__mc68040__) || defined(mc68040) \ -+ || defined(__mc68060__) || defined(mc68060) -+#define count_leading_zeros(count, x) \ -+ __asm__ ("bfffo %1{%b2:%b2},%0" \ -+ : "=d" ((USItype) (count)) \ -+ : "od" ((USItype) (x)), "n" (0)) -+#endif -+#endif /* mc68000 */ -+ -+#if defined (__m88000__) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "%rJ" ((USItype) (ah)), \ -+ "rJ" ((USItype) (bh)), \ -+ "%rJ" ((USItype) (al)), \ -+ "rJ" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "rJ" ((USItype) (ah)), \ -+ "rJ" ((USItype) (bh)), \ -+ "rJ" ((USItype) (al)), \ -+ "rJ" ((USItype) (bl))) -+#define count_leading_zeros(count, x) \ -+ do { \ -+ USItype __cbtmp; \ -+ __asm__ ("ff1 %0,%1" \ -+ : "=r" (__cbtmp) \ -+ : "r" ((USItype) (x))); \ -+ (count) = __cbtmp ^ 31; \ -+ } while (0) -+#define COUNT_LEADING_ZEROS_0 63 /* sic */ -+#if defined (__mc88110__) -+#define umul_ppmm(wh, wl, u, v) \ -+ do { \ -+ union {UDItype __ll; \ -+ struct {USItype __h, __l;} __i; \ -+ } __xx; \ -+ __asm__ ("mulu.d %0,%1,%2" \ -+ : "=r" (__xx.__ll) \ -+ : "r" ((USItype) (u)), \ -+ "r" ((USItype) (v))); \ -+ (wh) = __xx.__i.__h; \ -+ (wl) = __xx.__i.__l; \ -+ } while (0) -+#define udiv_qrnnd(q, r, n1, n0, d) \ -+ ({union {UDItype __ll; \ -+ struct {USItype __h, __l;} __i; \ -+ } __xx; \ -+ USItype __q; \ -+ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \ -+ __asm__ ("divu.d %0,%1,%2" \ -+ : "=r" (__q) \ -+ : "r" (__xx.__ll), \ -+ "r" ((USItype) (d))); \ -+ (r) = (n0) - __q * (d); (q) = __q; }) -+#define UMUL_TIME 5 -+#define UDIV_TIME 25 -+#else -+#define UMUL_TIME 17 -+#define UDIV_TIME 150 -+#endif /* __mc88110__ */ -+#endif /* __m88000__ */ -+ -+#if defined (__mips__) && W_TYPE_SIZE == 32 -+#define umul_ppmm(w1, w0, u, v) \ -+ __asm__ ("multu %2,%3" \ -+ : "=l" ((USItype) (w0)), \ -+ "=h" ((USItype) (w1)) \ -+ : "d" ((USItype) (u)), \ -+ "d" ((USItype) (v))) -+#define UMUL_TIME 10 -+#define UDIV_TIME 100 -+#endif /* __mips__ */ -+ -+#if defined (__ns32000__) && W_TYPE_SIZE == 32 -+#define umul_ppmm(w1, w0, u, v) \ -+ ({union {UDItype __ll; \ -+ struct {USItype __l, __h;} __i; \ -+ } __xx; \ -+ __asm__ ("meid %2,%0" \ -+ : "=g" (__xx.__ll) \ -+ : "%0" ((USItype) (u)), \ -+ "g" ((USItype) (v))); \ -+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;}) -+#define __umulsidi3(u, v) \ -+ ({UDItype __w; \ -+ __asm__ ("meid %2,%0" \ -+ : "=g" (__w) \ -+ : "%0" ((USItype) (u)), \ -+ "g" ((USItype) (v))); \ -+ __w; }) -+#define udiv_qrnnd(q, r, n1, n0, d) \ -+ ({union {UDItype __ll; \ -+ struct {USItype __l, __h;} __i; \ -+ } __xx; \ -+ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \ -+ __asm__ ("deid %2,%0" \ -+ : "=g" (__xx.__ll) \ -+ : "0" (__xx.__ll), \ -+ "g" ((USItype) (d))); \ -+ (r) = __xx.__i.__l; (q) = __xx.__i.__h; }) -+#define count_trailing_zeros(count,x) \ -+ do { \ -+ __asm__ ("ffsd %2,%0" \ -+ : "=r" ((USItype) (count)) \ -+ : "0" ((USItype) 0), \ -+ "r" ((USItype) (x))); \ -+ } while (0) -+#endif /* __ns32000__ */ -+ -+/* FIXME: We should test _IBMR2 here when we add assembly support for the -+ system vendor compilers. -+ FIXME: What's needed for gcc PowerPC VxWorks? __vxworks__ is not good -+ enough, since that hits ARM and m68k too. */ -+#if (defined (_ARCH_PPC) /* AIX */ \ -+ || defined (_ARCH_PWR) /* AIX */ \ -+ || defined (_ARCH_COM) /* AIX */ \ -+ || defined (__powerpc__) /* gcc */ \ -+ || defined (__POWERPC__) /* BEOS */ \ -+ || defined (__ppc__) /* Darwin */ \ -+ || defined (PPC) /* GNU/Linux, SysV */ \ -+ ) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ do { \ -+ if (__builtin_constant_p (bh) && (bh) == 0) \ -+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ -+ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ -+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ -+ else \ -+ __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \ -+ : "=r" (sh), "=&r" (sl) \ -+ : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ -+ } while (0) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ do { \ -+ if (__builtin_constant_p (ah) && (ah) == 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ -+ else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ -+ else if (__builtin_constant_p (bh) && (bh) == 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ -+ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ -+ else \ -+ __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \ -+ : "=r" (sh), "=&r" (sl) \ -+ : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ -+ } while (0) -+#define count_leading_zeros(count, x) \ -+ __asm__ ("{cntlz|cntlzw} %0,%1" : "=r" (count) : "r" (x)) -+#define COUNT_LEADING_ZEROS_0 32 -+#if defined (_ARCH_PPC) || defined (__powerpc__) || defined (__POWERPC__) \ -+ || defined (__ppc__) || defined (PPC) || defined (__vxworks__) -+#define umul_ppmm(ph, pl, m0, m1) \ -+ do { \ -+ USItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -+ (pl) = __m0 * __m1; \ -+ } while (0) -+#define UMUL_TIME 15 -+#define smul_ppmm(ph, pl, m0, m1) \ -+ do { \ -+ SItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -+ (pl) = __m0 * __m1; \ -+ } while (0) -+#define SMUL_TIME 14 -+#define UDIV_TIME 120 -+#elif defined (_ARCH_PWR) -+#define UMUL_TIME 8 -+#define smul_ppmm(xh, xl, m0, m1) \ -+ __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1)) -+#define SMUL_TIME 4 -+#define sdiv_qrnnd(q, r, nh, nl, d) \ -+ __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d)) -+#define UDIV_TIME 100 -+#endif -+#endif /* 32-bit POWER architecture variants. */ -+ -+/* We should test _IBMR2 here when we add assembly support for the system -+ vendor compilers. */ -+#if (defined (_ARCH_PPC64) || defined (__powerpc64__)) && W_TYPE_SIZE == 64 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ do { \ -+ if (__builtin_constant_p (bh) && (bh) == 0) \ -+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ -+ else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\ -+ else \ -+ __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \ -+ : "=r" (sh), "=&r" (sl) \ -+ : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ -+ } while (0) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ do { \ -+ if (__builtin_constant_p (ah) && (ah) == 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ -+ else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\ -+ else if (__builtin_constant_p (bh) && (bh) == 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ -+ else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \ -+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\ -+ else \ -+ __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \ -+ : "=r" (sh), "=&r" (sl) \ -+ : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ -+ } while (0) -+#define count_leading_zeros(count, x) \ -+ __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x)) -+#define COUNT_LEADING_ZEROS_0 64 -+#define umul_ppmm(ph, pl, m0, m1) \ -+ do { \ -+ UDItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -+ (pl) = __m0 * __m1; \ -+ } while (0) -+#define UMUL_TIME 15 -+#define smul_ppmm(ph, pl, m0, m1) \ -+ do { \ -+ DItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -+ (pl) = __m0 * __m1; \ -+ } while (0) -+#define SMUL_TIME 14 /* ??? */ -+#define UDIV_TIME 120 /* ??? */ -+#endif /* 64-bit PowerPC. */ -+ -+#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("a %1,%5\n\tae %0,%3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "%0" ((USItype) (ah)), \ -+ "r" ((USItype) (bh)), \ -+ "%1" ((USItype) (al)), \ -+ "r" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("s %1,%5\n\tse %0,%3" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "0" ((USItype) (ah)), \ -+ "r" ((USItype) (bh)), \ -+ "1" ((USItype) (al)), \ -+ "r" ((USItype) (bl))) -+#define umul_ppmm(ph, pl, m0, m1) \ -+ do { \ -+ USItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ( \ -+ "s r2,r2\n" \ -+" mts r10,%2\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" m r2,%3\n" \ -+" cas %0,r2,r0\n" \ -+" mfs r10,%1" \ -+ : "=r" ((USItype) (ph)), \ -+ "=r" ((USItype) (pl)) \ -+ : "%r" (__m0), \ -+ "r" (__m1) \ -+ : "r2"); \ -+ (ph) += ((((SItype) __m0 >> 31) & __m1) \ -+ + (((SItype) __m1 >> 31) & __m0)); \ -+ } while (0) -+#define UMUL_TIME 20 -+#define UDIV_TIME 200 -+#define count_leading_zeros(count, x) \ -+ do { \ -+ if ((x) >= 0x10000) \ -+ __asm__ ("clz %0,%1" \ -+ : "=r" ((USItype) (count)) \ -+ : "r" ((USItype) (x) >> 16)); \ -+ else \ -+ { \ -+ __asm__ ("clz %0,%1" \ -+ : "=r" ((USItype) (count)) \ -+ : "r" ((USItype) (x))); \ -+ (count) += 16; \ -+ } \ -+ } while (0) -+#endif -+ -+#if defined (__sh2__) && W_TYPE_SIZE == 32 -+#define umul_ppmm(w1, w0, u, v) \ -+ __asm__ ( \ -+ "dmulu.l %2,%3\n\tsts macl,%1\n\tsts mach,%0" \ -+ : "=r" ((USItype)(w1)), \ -+ "=r" ((USItype)(w0)) \ -+ : "r" ((USItype)(u)), \ -+ "r" ((USItype)(v)) \ -+ : "macl", "mach") -+#define UMUL_TIME 5 -+#endif -+ -+#if defined (__SH5__) && __SHMEDIA__ && W_TYPE_SIZE == 32 -+#define __umulsidi3(u,v) ((UDItype)(USItype)u*(USItype)v) -+#define count_leading_zeros(count, x) \ -+ do \ -+ { \ -+ UDItype x_ = (USItype)(x); \ -+ SItype c_; \ -+ \ -+ __asm__ ("nsb %1, %0" : "=r" (c_) : "r" (x_)); \ -+ (count) = c_ - 31; \ -+ } \ -+ while (0) -+#define COUNT_LEADING_ZEROS_0 32 -+#endif -+ -+#if defined (__sparc__) && !defined (__arch64__) && !defined (__sparcv9) \ -+ && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "%rJ" ((USItype) (ah)), \ -+ "rI" ((USItype) (bh)), \ -+ "%rJ" ((USItype) (al)), \ -+ "rI" ((USItype) (bl)) \ -+ __CLOBBER_CC) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0" \ -+ : "=r" ((USItype) (sh)), \ -+ "=&r" ((USItype) (sl)) \ -+ : "rJ" ((USItype) (ah)), \ -+ "rI" ((USItype) (bh)), \ -+ "rJ" ((USItype) (al)), \ -+ "rI" ((USItype) (bl)) \ -+ __CLOBBER_CC) -+#if defined (__sparc_v8__) -+#define umul_ppmm(w1, w0, u, v) \ -+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \ -+ : "=r" ((USItype) (w1)), \ -+ "=r" ((USItype) (w0)) \ -+ : "r" ((USItype) (u)), \ -+ "r" ((USItype) (v))) -+#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \ -+ __asm__ ("mov %2,%%y;nop;nop;nop;udiv %3,%4,%0;umul %0,%4,%1;sub %3,%1,%1"\ -+ : "=&r" ((USItype) (__q)), \ -+ "=&r" ((USItype) (__r)) \ -+ : "r" ((USItype) (__n1)), \ -+ "r" ((USItype) (__n0)), \ -+ "r" ((USItype) (__d))) -+#else -+#if defined (__sparclite__) -+/* This has hardware multiply but not divide. It also has two additional -+ instructions scan (ffs from high bit) and divscc. */ -+#define umul_ppmm(w1, w0, u, v) \ -+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \ -+ : "=r" ((USItype) (w1)), \ -+ "=r" ((USItype) (w0)) \ -+ : "r" ((USItype) (u)), \ -+ "r" ((USItype) (v))) -+#define udiv_qrnnd(q, r, n1, n0, d) \ -+ __asm__ ("! Inlined udiv_qrnnd\n" \ -+" wr %%g0,%2,%%y ! Not a delayed write for sparclite\n" \ -+" tst %%g0\n" \ -+" divscc %3,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%%g1\n" \ -+" divscc %%g1,%4,%0\n" \ -+" rd %%y,%1\n" \ -+" bl,a 1f\n" \ -+" add %1,%4,%1\n" \ -+"1: ! End of inline udiv_qrnnd" \ -+ : "=r" ((USItype) (q)), \ -+ "=r" ((USItype) (r)) \ -+ : "r" ((USItype) (n1)), \ -+ "r" ((USItype) (n0)), \ -+ "rI" ((USItype) (d)) \ -+ : "g1" __AND_CLOBBER_CC) -+#define UDIV_TIME 37 -+#define count_leading_zeros(count, x) \ -+ do { \ -+ __asm__ ("scan %1,1,%0" \ -+ : "=r" ((USItype) (count)) \ -+ : "r" ((USItype) (x))); \ -+ } while (0) -+/* Early sparclites return 63 for an argument of 0, but they warn that future -+ implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0 -+ undefined. */ -+#else -+/* SPARC without integer multiplication and divide instructions. -+ (i.e. at least Sun4/20,40,60,65,75,110,260,280,330,360,380,470,490) */ -+#define umul_ppmm(w1, w0, u, v) \ -+ __asm__ ("! Inlined umul_ppmm\n" \ -+" wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr\n"\ -+" sra %3,31,%%o5 ! Don't move this insn\n" \ -+" and %2,%%o5,%%o5 ! Don't move this insn\n" \ -+" andcc %%g0,0,%%g1 ! Don't move this insn\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,%3,%%g1\n" \ -+" mulscc %%g1,0,%%g1\n" \ -+" add %%g1,%%o5,%0\n" \ -+" rd %%y,%1" \ -+ : "=r" ((USItype) (w1)), \ -+ "=r" ((USItype) (w0)) \ -+ : "%rI" ((USItype) (u)), \ -+ "r" ((USItype) (v)) \ -+ : "g1", "o5" __AND_CLOBBER_CC) -+#define UMUL_TIME 39 /* 39 instructions */ -+/* It's quite necessary to add this much assembler for the sparc. -+ The default udiv_qrnnd (in C) is more than 10 times slower! */ -+#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \ -+ __asm__ ("! Inlined udiv_qrnnd\n" \ -+" mov 32,%%g1\n" \ -+" subcc %1,%2,%%g0\n" \ -+"1: bcs 5f\n" \ -+" addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n" \ -+" sub %1,%2,%1 ! this kills msb of n\n" \ -+" addx %1,%1,%1 ! so this can't give carry\n" \ -+" subcc %%g1,1,%%g1\n" \ -+"2: bne 1b\n" \ -+" subcc %1,%2,%%g0\n" \ -+" bcs 3f\n" \ -+" addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb\n" \ -+" b 3f\n" \ -+" sub %1,%2,%1 ! this kills msb of n\n" \ -+"4: sub %1,%2,%1\n" \ -+"5: addxcc %1,%1,%1\n" \ -+" bcc 2b\n" \ -+" subcc %%g1,1,%%g1\n" \ -+"! Got carry from n. Subtract next step to cancel this carry.\n" \ -+" bne 4b\n" \ -+" addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb\n" \ -+" sub %1,%2,%1\n" \ -+"3: xnor %0,0,%0\n" \ -+" ! End of inline udiv_qrnnd" \ -+ : "=&r" ((USItype) (__q)), \ -+ "=&r" ((USItype) (__r)) \ -+ : "r" ((USItype) (__d)), \ -+ "1" ((USItype) (__n1)), \ -+ "0" ((USItype) (__n0)) : "g1" __AND_CLOBBER_CC) -+#define UDIV_TIME (3+7*32) /* 7 instructions/iteration. 32 iterations. */ -+#endif /* __sparclite__ */ -+#endif /* __sparc_v8__ */ -+#endif /* sparc32 */ -+ -+#if ((defined (__sparc__) && defined (__arch64__)) || defined (__sparcv9)) \ -+ && W_TYPE_SIZE == 64 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("addcc %r4,%5,%1\n\t" \ -+ "add %r2,%3,%0\n\t" \ -+ "bcs,a,pn %%xcc, 1f\n\t" \ -+ "add %0, 1, %0\n" \ -+ "1:" \ -+ : "=r" ((UDItype)(sh)), \ -+ "=&r" ((UDItype)(sl)) \ -+ : "%rJ" ((UDItype)(ah)), \ -+ "rI" ((UDItype)(bh)), \ -+ "%rJ" ((UDItype)(al)), \ -+ "rI" ((UDItype)(bl)) \ -+ __CLOBBER_CC) -+ -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("subcc %r4,%5,%1\n\t" \ -+ "sub %r2,%3,%0\n\t" \ -+ "bcs,a,pn %%xcc, 1f\n\t" \ -+ "sub %0, 1, %0\n\t" \ -+ "1:" \ -+ : "=r" ((UDItype)(sh)), \ -+ "=&r" ((UDItype)(sl)) \ -+ : "rJ" ((UDItype)(ah)), \ -+ "rI" ((UDItype)(bh)), \ -+ "rJ" ((UDItype)(al)), \ -+ "rI" ((UDItype)(bl)) \ -+ __CLOBBER_CC) -+ -+#define umul_ppmm(wh, wl, u, v) \ -+ do { \ -+ UDItype tmp1, tmp2, tmp3, tmp4; \ -+ __asm__ __volatile__ ( \ -+ "srl %7,0,%3\n\t" \ -+ "mulx %3,%6,%1\n\t" \ -+ "srlx %6,32,%2\n\t" \ -+ "mulx %2,%3,%4\n\t" \ -+ "sllx %4,32,%5\n\t" \ -+ "srl %6,0,%3\n\t" \ -+ "sub %1,%5,%5\n\t" \ -+ "srlx %5,32,%5\n\t" \ -+ "addcc %4,%5,%4\n\t" \ -+ "srlx %7,32,%5\n\t" \ -+ "mulx %3,%5,%3\n\t" \ -+ "mulx %2,%5,%5\n\t" \ -+ "sethi %%hi(0x80000000),%2\n\t" \ -+ "addcc %4,%3,%4\n\t" \ -+ "srlx %4,32,%4\n\t" \ -+ "add %2,%2,%2\n\t" \ -+ "movcc %%xcc,%%g0,%2\n\t" \ -+ "addcc %5,%4,%5\n\t" \ -+ "sllx %3,32,%3\n\t" \ -+ "add %1,%3,%1\n\t" \ -+ "add %5,%2,%0" \ -+ : "=r" ((UDItype)(wh)), \ -+ "=&r" ((UDItype)(wl)), \ -+ "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3), "=&r" (tmp4) \ -+ : "r" ((UDItype)(u)), \ -+ "r" ((UDItype)(v)) \ -+ __CLOBBER_CC); \ -+ } while (0) -+#define UMUL_TIME 96 -+#define UDIV_TIME 230 -+#endif /* sparc64 */ -+ -+#if defined (__vax__) && W_TYPE_SIZE == 32 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("addl2 %5,%1\n\tadwc %3,%0" \ -+ : "=g" ((USItype) (sh)), \ -+ "=&g" ((USItype) (sl)) \ -+ : "%0" ((USItype) (ah)), \ -+ "g" ((USItype) (bh)), \ -+ "%1" ((USItype) (al)), \ -+ "g" ((USItype) (bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("subl2 %5,%1\n\tsbwc %3,%0" \ -+ : "=g" ((USItype) (sh)), \ -+ "=&g" ((USItype) (sl)) \ -+ : "0" ((USItype) (ah)), \ -+ "g" ((USItype) (bh)), \ -+ "1" ((USItype) (al)), \ -+ "g" ((USItype) (bl))) -+#define umul_ppmm(xh, xl, m0, m1) \ -+ do { \ -+ union { \ -+ UDItype __ll; \ -+ struct {USItype __l, __h;} __i; \ -+ } __xx; \ -+ USItype __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("emul %1,%2,$0,%0" \ -+ : "=r" (__xx.__ll) \ -+ : "g" (__m0), \ -+ "g" (__m1)); \ -+ (xh) = __xx.__i.__h; \ -+ (xl) = __xx.__i.__l; \ -+ (xh) += ((((SItype) __m0 >> 31) & __m1) \ -+ + (((SItype) __m1 >> 31) & __m0)); \ -+ } while (0) -+#define sdiv_qrnnd(q, r, n1, n0, d) \ -+ do { \ -+ union {DItype __ll; \ -+ struct {SItype __l, __h;} __i; \ -+ } __xx; \ -+ __xx.__i.__h = n1; __xx.__i.__l = n0; \ -+ __asm__ ("ediv %3,%2,%0,%1" \ -+ : "=g" (q), "=g" (r) \ -+ : "g" (__xx.__ll), "g" (d)); \ -+ } while (0) -+#endif /* __vax__ */ -+ -+#if defined (__z8000__) && W_TYPE_SIZE == 16 -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \ -+ : "=r" ((unsigned int)(sh)), \ -+ "=&r" ((unsigned int)(sl)) \ -+ : "%0" ((unsigned int)(ah)), \ -+ "r" ((unsigned int)(bh)), \ -+ "%1" ((unsigned int)(al)), \ -+ "rQR" ((unsigned int)(bl))) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \ -+ : "=r" ((unsigned int)(sh)), \ -+ "=&r" ((unsigned int)(sl)) \ -+ : "0" ((unsigned int)(ah)), \ -+ "r" ((unsigned int)(bh)), \ -+ "1" ((unsigned int)(al)), \ -+ "rQR" ((unsigned int)(bl))) -+#define umul_ppmm(xh, xl, m0, m1) \ -+ do { \ -+ union {long int __ll; \ -+ struct {unsigned int __h, __l;} __i; \ -+ } __xx; \ -+ unsigned int __m0 = (m0), __m1 = (m1); \ -+ __asm__ ("mult %S0,%H3" \ -+ : "=r" (__xx.__i.__h), \ -+ "=r" (__xx.__i.__l) \ -+ : "%1" (__m0), \ -+ "rQR" (__m1)); \ -+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \ -+ (xh) += ((((signed int) __m0 >> 15) & __m1) \ -+ + (((signed int) __m1 >> 15) & __m0)); \ -+ } while (0) -+#endif /* __z8000__ */ -+ -+#endif /* __GNUC__ */ -+ -+/* If this machine has no inline assembler, use C macros. */ -+ -+#if !defined (add_ssaaaa) -+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+ do { \ -+ UWtype __x; \ -+ __x = (al) + (bl); \ -+ (sh) = (ah) + (bh) + (__x < (al)); \ -+ (sl) = __x; \ -+ } while (0) -+#endif -+ -+#if !defined (sub_ddmmss) -+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+ do { \ -+ UWtype __x; \ -+ __x = (al) - (bl); \ -+ (sh) = (ah) - (bh) - (__x > (al)); \ -+ (sl) = __x; \ -+ } while (0) -+#endif -+ -+#if !defined (umul_ppmm) -+#define umul_ppmm(w1, w0, u, v) \ -+ do { \ -+ UWtype __x0, __x1, __x2, __x3; \ -+ UHWtype __ul, __vl, __uh, __vh; \ -+ \ -+ __ul = __ll_lowpart (u); \ -+ __uh = __ll_highpart (u); \ -+ __vl = __ll_lowpart (v); \ -+ __vh = __ll_highpart (v); \ -+ \ -+ __x0 = (UWtype) __ul * __vl; \ -+ __x1 = (UWtype) __ul * __vh; \ -+ __x2 = (UWtype) __uh * __vl; \ -+ __x3 = (UWtype) __uh * __vh; \ -+ \ -+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \ -+ __x1 += __x2; /* but this indeed can */ \ -+ if (__x1 < __x2) /* did we get it? */ \ -+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \ -+ \ -+ (w1) = __x3 + __ll_highpart (__x1); \ -+ (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \ -+ } while (0) -+#endif -+ -+#if !defined (__umulsidi3) -+#define __umulsidi3(u, v) \ -+ ({DWunion __w; \ -+ umul_ppmm (__w.s.high, __w.s.low, u, v); \ -+ __w.ll; }) -+#endif -+ -+/* Define this unconditionally, so it can be used for debugging. */ -+#define __udiv_qrnnd_c(q, r, n1, n0, d) \ -+ do { \ -+ UWtype __d1, __d0, __q1, __q0; \ -+ UWtype __r1, __r0, __m; \ -+ __d1 = __ll_highpart (d); \ -+ __d0 = __ll_lowpart (d); \ -+ \ -+ __r1 = (n1) % __d1; \ -+ __q1 = (n1) / __d1; \ -+ __m = (UWtype) __q1 * __d0; \ -+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \ -+ if (__r1 < __m) \ -+ { \ -+ __q1--, __r1 += (d); \ -+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\ -+ if (__r1 < __m) \ -+ __q1--, __r1 += (d); \ -+ } \ -+ __r1 -= __m; \ -+ \ -+ __r0 = __r1 % __d1; \ -+ __q0 = __r1 / __d1; \ -+ __m = (UWtype) __q0 * __d0; \ -+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ -+ if (__r0 < __m) \ -+ { \ -+ __q0--, __r0 += (d); \ -+ if (__r0 >= (d)) \ -+ if (__r0 < __m) \ -+ __q0--, __r0 += (d); \ -+ } \ -+ __r0 -= __m; \ -+ \ -+ (q) = (UWtype) __q1 * __ll_B | __q0; \ -+ (r) = __r0; \ -+ } while (0) -+ -+/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through -+ __udiv_w_sdiv (defined in libgcc or elsewhere). */ -+#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd) -+#define udiv_qrnnd(q, r, nh, nl, d) \ -+ do { \ -+ USItype __r; \ -+ (q) = __udiv_w_sdiv (&__r, nh, nl, d); \ -+ (r) = __r; \ -+ } while (0) -+#endif -+ -+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */ -+#if !defined (udiv_qrnnd) -+#define UDIV_NEEDS_NORMALIZATION 1 -+#define udiv_qrnnd __udiv_qrnnd_c -+#endif -+ -+#if !defined (count_leading_zeros) -+extern const UQItype __clz_tab[]; -+#define count_leading_zeros(count, x) \ -+ do { \ -+ UWtype __xr = (x); \ -+ UWtype __a; \ -+ \ -+ if (W_TYPE_SIZE <= 32) \ -+ { \ -+ __a = __xr < ((UWtype)1<<2*__BITS4) \ -+ ? (__xr < ((UWtype)1<<__BITS4) ? 0 : __BITS4) \ -+ : (__xr < ((UWtype)1<<3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \ -+ } \ -+ else \ -+ { \ -+ for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \ -+ if (((__xr >> __a) & 0xff) != 0) \ -+ break; \ -+ } \ -+ \ -+ (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \ -+ } while (0) -+#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE -+#endif -+ -+#if !defined (count_trailing_zeros) -+/* Define count_trailing_zeros using count_leading_zeros. The latter might be -+ defined in asm, but if it is not, the C version above is good enough. */ -+#define count_trailing_zeros(count, x) \ -+ do { \ -+ UWtype __ctz_x = (x); \ -+ UWtype __ctz_c; \ -+ count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \ -+ (count) = W_TYPE_SIZE - 1 - __ctz_c; \ -+ } while (0) -+#endif -+ -+#ifndef UDIV_NEEDS_NORMALIZATION -+#define UDIV_NEEDS_NORMALIZATION 0 -+#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/Makefile linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile ---- linux-3.12.2/3rdparty/ndiswrapper/Makefile 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile 2013-11-28 21:42:10.000000000 +0200 -@@ -0,0 +1,213 @@ -+# Name of the module -+MODNAME = ndiswrapper -+ -+DISTFILES = \ -+ Makefile crt.c divdi3.c hal.c iw_ndis.c iw_ndis.h lin2win.S lin2win.h \ -+ loader.c loader.h longlong.h mkexport.sh mkstubs.sh ndis.c ndis.h \ -+ ndiswrapper.h ntoskernel.c ntoskernel.h ntoskernel_io.c pe_linker.c \ -+ pe_linker.h pnp.c pnp.h proc.c rtl.c usb.c usb.h win2lin_stubs.S \ -+ winnt_types.h workqueue.c wrapmem.c wrapmem.h wrapndis.c wrapndis.h \ -+ wrapper.c wrapper.h -+ -+# By default, we try to compile the modules for the currently running -+# kernel. But it's the first approximation, as we will re-read the -+# version from the kernel sources. -+KVERS_UNAME ?= $(shell uname -r) -+ -+# KBUILD is the path to the Linux kernel build tree. It is usually the -+# same as the kernel source tree, except when the kernel was compiled in -+# a separate directory. -+KBUILD ?= $(shell readlink -f /lib/modules/$(KVERS_UNAME)/build) -+ -+ifeq (,$(KBUILD)) -+$(error Kernel build tree not found - please set KBUILD to configured kernel) -+endif -+ -+KCONFIG := $(KBUILD)/.config -+ifeq (,$(wildcard $(KCONFIG))) -+$(error No .config found in $(KBUILD), please set KBUILD to configured kernel) -+endif -+ -+ifneq (,$(wildcard $(KBUILD)/include/linux/version.h)) -+ifneq (,$(wildcard $(KBUILD)/include/generated/uapi/linux/version.h)) -+$(error Multiple copies of version.h found, please clean your build tree) -+endif -+endif -+ -+# Kernel Makefile doesn't always know the exact kernel version, so we -+# get it from the kernel headers instead and pass it to make. -+VERSION_H := $(KBUILD)/include/generated/utsrelease.h -+ifeq (,$(wildcard $(VERSION_H))) -+VERSION_H := $(KBUILD)/include/linux/utsrelease.h -+endif -+ifeq (,$(wildcard $(VERSION_H))) -+VERSION_H := $(KBUILD)/include/linux/version.h -+endif -+ifeq (,$(wildcard $(VERSION_H))) -+$(error Please run 'make modules_prepare' in $(KBUILD)) -+endif -+ -+KVERS := $(shell sed -ne 's/"//g;s/^\#define UTS_RELEASE //p' $(VERSION_H)) -+ -+ifeq (,$(KVERS)) -+$(error Cannot find UTS_RELEASE in $(VERSION_H), please report) -+endif -+ -+INST_DIR = /lib/modules/$(KVERS)/misc -+ -+SRC_DIR=$(shell pwd) -+ -+include $(KCONFIG) -+ -+# returns of structs and unions in registers when possible, like Windows -+EXTRA_CFLAGS += -freg-struct-return -+ -+# to produce debug trace, add option "DEBUG=" where is 1 to 6 -+ifdef DEBUG -+EXTRA_CFLAGS += -DDEBUG=$(DEBUG) -g -+endif -+ -+# to debug timers, add option "TIMER_DEBUG=1" -+ifdef TIMER_DEBUG -+EXTRA_CFLAGS += -DTIMER_DEBUG -+endif -+ -+# to debug event layer, add option "EVENT_DEBUG=1" -+ifdef EVENT_DEBUG -+EXTRA_CFLAGS += -DEVENT_DEBUG -+endif -+ -+# to debug USB layer, add option "USB_DEBUG=1" -+ifdef USB_DEBUG -+EXTRA_CFLAGS += -DUSB_DEBUG -+endif -+ -+# to debug I/O layer, add option "IO_DEBUG=1" -+ifdef IO_DEBUG -+EXTRA_CFLAGS += -DIO_DEBUG -+endif -+ -+# to debug worker threads, add option "WORK_DEBUG=1" -+ifdef WORK_DEBUG -+EXTRA_CFLAGS += -DWORK_DEBUG -+endif -+ -+# to debug memory allocation, add option "ALLOC_DEBUG=" where is 1 or 2 -+ifdef ALLOC_DEBUG -+EXTRA_CFLAGS += -DALLOC_DEBUG=$(ALLOC_DEBUG) -+endif -+ -+OBJS = crt.o hal.o iw_ndis.o loader.o ndis.o ntoskernel.o ntoskernel_io.o \ -+ pe_linker.o pnp.o proc.o rtl.o wrapmem.o wrapndis.o wrapper.o -+ -+EXPORT_SRCS = crt.c hal.c ndis.c ntoskernel.c ntoskernel_io.c rtl.c -+ -+STUB_SRCS = crt.c hal.c ndis.c ntoskernel.c ntoskernel_io.c \ -+ pnp.c rtl.c wrapndis.c -+ -+ -+# By default, USB layer is compiled in if USB support is in kernel; -+# to disable USB support in ndiswrapper even if USB support is in kernel, -+# add option "DISABLE_USB=1" -+ifndef DISABLE_USB -+ifeq ($(CONFIG_USB),y) -+ENABLE_USB = 1 -+endif -+ifeq ($(CONFIG_USB),m) -+ENABLE_USB = 1 -+endif -+endif -+ -+ifdef ENABLE_USB -+EXPORT_SRCS += usb.c -+STUB_SRCS += usb.c -+OBJS += usb.o -+EXTRA_CFLAGS += -DENABLE_USB -+endif -+ -+ifdef WRAP_WQ -+EXTRA_CFLAGS += -DWRAP_WQ -+OBJS += workqueue.o -+endif -+ -+ -+all: config_check modules -+ -+# generate exports symbol table from C files -+quiet_cmd_mkexport = MKEXPORT $@ -+cmd_mkexport = $(SHELL) $(obj)/mkexport.sh $< $@ -+ -+extra-y += $(EXPORT_SRCS:.c=_exports.h) -+%_exports.h: %.c $(obj)/mkexport.sh FORCE -+ $(call if_changed,mkexport) -+ -+$(addprefix $(obj)/,$(EXPORT_SRCS:.c=.o)): %.o: %_exports.h -+ -+ifeq ($(CONFIG_X86_64),y) -+quiet_cmd_mkstubs = MKSTUBS $@ -+cmd_mkstubs = $(SHELL) $(obj)/mkstubs.sh $(addprefix $(src)/,$(STUB_SRCS)) >$@ -+ -+extra-y += win2lin_stubs.h -+$(obj)/win2lin_stubs.h: $(addprefix $(src)/,$(STUB_SRCS)) FORCE -+ $(call if_changed,mkstubs) -+ -+$(obj)/win2lin_stubs.o: $(obj)/win2lin_stubs.h -+OBJS += win2lin_stubs.o lin2win.o -+else -+OBJS += divdi3.o -+endif -+ -+MODULE := $(MODNAME).ko -+obj-m := $(MODNAME).o -+ -+$(MODNAME)-objs := $(OBJS) -+ -+ -+config_check: -+ @if [ -z "$(CONFIG_WIRELESS_EXT)$(CONFIG_NET_RADIO)" ]; then \ -+ echo; echo; \ -+ echo "*** WARNING: This kernel lacks wireless extensions."; \ -+ echo "Wireless drivers will not work properly."; \ -+ echo; echo; \ -+ fi -+ @if [ -z "$(CONFIG_X86_64)" ] && [ -n "$(CONFIG_4KSTACKS)" ]; then \ -+ echo; echo; \ -+ echo "*** WARNING: This kernel uses 4K stack size option"; \ -+ echo "(CONFIG_4KSTACKS); many Windows drivers will not work"; \ -+ echo "with this option enabled. Disable CONFIG_4KSTACKS"; \ -+ echo "in kernel's .config file, recompile and install kernel"; \ -+ echo; echo; \ -+ fi -+ -+modules: -+ $(MAKE) -C $(KBUILD) M=$(SRC_DIR) -+ -+$(MODULE): -+ $(MAKE) modules -+ -+clean: -+ rm -f *.o *.ko .*.cmd *.mod.c *.symvers modules.order *~ .\#* -+ rm -f *_exports.h win2lin_stubs.h -+ rm -rf .tmp_versions -+ -+install: config_check $(MODULE) -+ @/sbin/modinfo $(MODULE) | grep -q "^vermagic: *$(KVERS) " || \ -+ { echo "$(MODULE)" is not for Linux $(KVERS); exit 1; } -+ mkdir -p -m 755 $(DESTDIR)$(INST_DIR) -+ install -m 0644 $(MODULE) $(DESTDIR)$(INST_DIR) -+ifndef DESTDIR -+ -/sbin/depmod -a $(KVERS) -+endif -+ -+uninstall: -+ rm -f $(DESTDIR)$(INST_DIR)/$(MODULE) -+ifndef DESTDIR -+ -/sbin/depmod -a $(KVERS) -+endif -+ -+dist: -+ @for file in $(DISTFILES); do \ -+ cp $$file $(distdir)/$$file || exit 1; \ -+ done -+ -+.PHONY: all modules clean install config_check dist -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkexport.sh linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkexport.sh ---- linux-3.12.2/3rdparty/ndiswrapper/mkexport.sh 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkexport.sh 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,42 @@ -+#! /bin/sh -+ -+# Generate exports symbol table from C files -+ -+input="$1" -+output="$2" -+exports=$(basename "$output" .h) -+exec >"$output" -+ -+echo "/* automatically generated from src */"; -+ -+sed -n -e '/^\(wstdcall\|wfastcall\|noregparm\|regparm3\|__attribute__\)/{ -+:more -+N -+s/\([^{]\)$/\1/ -+t more -+s/\n{$/;/ -+p -+}' $input -+ -+echo "#ifdef CONFIG_X86_64"; -+ -+sed -n \ -+ -e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ -+'WIN_FUNC_DECL(\1, \2)/p' \ -+ -e 's/.*WIN_FUNC_PTR(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ -+'WIN_FUNC_DECL(\1, \2)/p' $input | sort -u -+ -+echo "#endif" -+echo "extern struct wrap_export $exports[];" -+echo "struct wrap_export $exports[] = {" -+ -+sed -n \ -+ -e 's/.*WIN_FUNC(_win_\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ -+' WIN_WIN_SYMBOL(\1, \2),/p' \ -+ -e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\ -+' WIN_SYMBOL(\1, \2),/p' \ -+ -e 's/.*WIN_SYMBOL_MAP(\("[^"]\+"\)[ ,\n]\+\([^)]\+\)).*/'\ -+' {\1, (generic_func)\2},/p' $input | sort -u -+ -+echo " {NULL, NULL}" -+echo "};" -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/mkstubs.sh linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkstubs.sh ---- linux-3.12.2/3rdparty/ndiswrapper/mkstubs.sh 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/mkstubs.sh 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,12 @@ -+#! /bin/sh -+ -+for file in "$@"; do -+ echo -+ echo "# automatically generated from $file" -+ sed -n \ -+ -e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/\ -+ win2lin(\1, \2)/p' \ -+ -e 's/.*WIN_FUNC_PTR(\([^\,]\+\) *\, *\([0-9]\+\)).*/\ -+ win2lin(\1, \2)/p' \ -+ $file | sed -e 's/[ \t ]\+//' | sort -u; \ -+done -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.c ---- linux-3.12.2/3rdparty/ndiswrapper/ndis.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,3025 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ndis.h" -+#include "iw_ndis.h" -+#include "wrapndis.h" -+#include "pnp.h" -+#include "loader.h" -+#include -+#include -+#include "ndis_exports.h" -+ -+#define MAX_ALLOCATED_NDIS_PACKETS TX_RING_SIZE -+#define MAX_ALLOCATED_NDIS_BUFFERS TX_RING_SIZE -+ -+static struct work_struct ndis_work; -+static struct nt_list ndis_work_list; -+static spinlock_t ndis_work_list_lock; -+ -+struct workqueue_struct *ndis_wq; -+ -+static void *ndis_get_routine_address(char *name); -+ -+wstdcall void WIN_FUNC(NdisInitializeWrapper,4) -+ (void **driver_handle, struct driver_object *driver, -+ struct unicode_string *reg_path, void *unused) -+{ -+ ENTER1("handle: %p, driver: %p", driver_handle, driver); -+ *driver_handle = driver; -+ EXIT1(return); -+} -+ -+wstdcall void WIN_FUNC(NdisTerminateWrapper,2) -+ (struct device_object *dev_obj, void *system_specific) -+{ -+ EXIT1(return); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterMiniport,3) -+ (struct driver_object *drv_obj, struct miniport *mp, UINT length) -+{ -+ int min_length; -+ struct wrap_driver *wrap_driver; -+ struct ndis_driver *ndis_driver; -+ -+ min_length = ((char *)&mp->co_create_vc) - ((char *)mp); -+ -+ ENTER1("%p %p %d", drv_obj, mp, length); -+ -+ if (mp->major_version < 4) { -+ ERROR("Driver is using ndis version %d which is too old.", -+ mp->major_version); -+ EXIT1(return NDIS_STATUS_BAD_VERSION); -+ } -+ -+ if (length < min_length) { -+ ERROR("Characteristics length %d is too small", length); -+ EXIT1(return NDIS_STATUS_BAD_CHARACTERISTICS); -+ } -+ -+ TRACE1("%d.%d, %d, %u", mp->major_version, mp->minor_version, length, -+ (u32)sizeof(struct miniport)); -+ wrap_driver = IoGetDriverObjectExtension(drv_obj, -+ (void *)WRAP_DRIVER_CLIENT_ID); -+ if (!wrap_driver) { -+ ERROR("couldn't get wrap_driver"); -+ EXIT1(return NDIS_STATUS_RESOURCES); -+ } -+ if (IoAllocateDriverObjectExtension( -+ drv_obj, (void *)NDIS_DRIVER_CLIENT_ID, -+ sizeof(*ndis_driver), (void **)&ndis_driver) != -+ STATUS_SUCCESS) -+ EXIT1(return NDIS_STATUS_RESOURCES); -+ wrap_driver->ndis_driver = ndis_driver; -+ TRACE1("driver: %p", ndis_driver); -+ memcpy(&ndis_driver->mp, mp, min_t(int, sizeof(*mp), length)); -+ -+ DBG_BLOCK(2) { -+ int i; -+ void **func; -+ char *mp_funcs[] = { -+ "queryinfo", "reconfig", "reset", "send", "setinfo", -+ "tx_data", "return_packet", "send_packets", -+ "alloc_complete", "co_create_vc", "co_delete_vc", -+ "co_activate_vc", "co_deactivate_vc", -+ "co_send_packets", "co_request", "cancel_send_packets", -+ "pnp_event_notify", "shutdown", -+ }; -+ func = (void **)&ndis_driver->mp.queryinfo; -+ for (i = 0; i < ARRAY_SIZE(mp_funcs); i++) -+ TRACE0("function '%s' is at %p", mp_funcs[i], func[i]); -+ } -+ EXIT1(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterDevice,6) -+ (struct driver_object *drv_obj, struct unicode_string *dev_name, -+ struct unicode_string *link, void **funcs, -+ struct device_object **dev_obj, void **dev_obj_handle) -+{ -+ NTSTATUS status; -+ struct device_object *tmp; -+ int i; -+ -+ ENTER1("%p, %p, %p", drv_obj, dev_name, link); -+ status = IoCreateDevice(drv_obj, 0, dev_name, FILE_DEVICE_NETWORK, 0, -+ FALSE, &tmp); -+ -+ if (status != STATUS_SUCCESS) -+ EXIT1(return NDIS_STATUS_RESOURCES); -+ if (link) -+ status = IoCreateSymbolicLink(link, dev_name); -+ if (status != STATUS_SUCCESS) { -+ IoDeleteDevice(tmp); -+ EXIT1(return NDIS_STATUS_RESOURCES); -+ } -+ -+ *dev_obj = tmp; -+ *dev_obj_handle = *dev_obj; -+ for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) -+ if (funcs[i] && i != IRP_MJ_PNP && i != IRP_MJ_POWER) { -+ drv_obj->major_func[i] = funcs[i]; -+ TRACE1("mj_fn for 0x%x is at %p", i, funcs[i]); -+ } -+ EXIT1(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMDeregisterDevice,1) -+ (struct device_object *dev_obj) -+{ -+ ENTER2("%p", dev_obj); -+ IoDeleteDevice(dev_obj); -+ return NDIS_STATUS_SUCCESS; -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisAllocateMemoryWithTag,3) -+ (void **dest, UINT length, ULONG tag) -+{ -+ void *addr; -+ -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ addr = ExAllocatePoolWithTag(NonPagedPool, length, tag); -+ TRACE4("%p", addr); -+ if (addr) { -+ *dest = addr; -+ EXIT4(return NDIS_STATUS_SUCCESS); -+ } else -+ EXIT4(return NDIS_STATUS_FAILURE); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisAllocateMemory,4) -+ (void **dest, UINT length, UINT flags, NDIS_PHY_ADDRESS highest_address) -+{ -+ return NdisAllocateMemoryWithTag(dest, length, 0); -+} -+ -+/* length_tag is either length or tag, depending on if -+ * NdisAllocateMemory or NdisAllocateMemoryTag is used to allocate -+ * memory */ -+wstdcall void WIN_FUNC(NdisFreeMemory,3) -+ (void *addr, UINT length_tag, UINT flags) -+{ -+ TRACE4("%p", addr); -+ ExFreePool(addr); -+} -+ -+noregparm void WIN_FUNC(NdisWriteErrorLogEntry,12) -+ (struct driver_object *drv_obj, ULONG error, ULONG count, ...) -+{ -+ va_list args; -+ int i; -+ ULONG code; -+ -+ va_start(args, count); -+ ERROR("log: %08X, count: %d, return_address: %p", -+ error, count, __builtin_return_address(0)); -+ for (i = 0; i < count; i++) { -+ code = va_arg(args, ULONG); -+ ERROR("code: 0x%x", code); -+ } -+ va_end(args); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisOpenConfiguration,3) -+ (NDIS_STATUS *status, struct ndis_mp_block **conf_handle, -+ struct ndis_mp_block *handle) -+{ -+ ENTER2("%p", conf_handle); -+ *conf_handle = handle; -+ *status = NDIS_STATUS_SUCCESS; -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisOpenProtocolConfiguration,3) -+ (NDIS_STATUS *status, void **confhandle, -+ struct unicode_string *section) -+{ -+ ENTER2("%p", confhandle); -+ *status = NDIS_STATUS_SUCCESS; -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisOpenConfigurationKeyByName,4) -+ (NDIS_STATUS *status, void *handle, -+ struct unicode_string *key, void **subkeyhandle) -+{ -+ struct ansi_string ansi; -+ ENTER2(""); -+ if (RtlUnicodeStringToAnsiString(&ansi, key, TRUE) == STATUS_SUCCESS) { -+ TRACE2("%s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ *subkeyhandle = handle; -+ *status = NDIS_STATUS_SUCCESS; -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisOpenConfigurationKeyByIndex,5) -+ (NDIS_STATUS *status, void *handle, ULONG index, -+ struct unicode_string *key, void **subkeyhandle) -+{ -+ ENTER2("%u", index); -+// *subkeyhandle = handle; -+ *status = NDIS_STATUS_FAILURE; -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisCloseConfiguration,1) -+ (void *handle) -+{ -+ /* instead of freeing all configuration parameters as we are -+ * supposed to do here, we free them when the device is -+ * removed */ -+ ENTER2("%p", handle); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisOpenFile,5) -+ (NDIS_STATUS *status, struct wrap_bin_file **file, -+ UINT *filelength, struct unicode_string *filename, -+ NDIS_PHY_ADDRESS highest_address) -+{ -+ struct ansi_string ansi; -+ struct wrap_bin_file *bin_file; -+ -+ ENTER2("%p, %d, %llx, %p", status, *filelength, highest_address, *file); -+ if (RtlUnicodeStringToAnsiString(&ansi, filename, TRUE) != -+ STATUS_SUCCESS) { -+ *status = NDIS_STATUS_RESOURCES; -+ EXIT2(return); -+ } -+ TRACE2("%s", ansi.buf); -+ bin_file = get_bin_file(ansi.buf); -+ if (bin_file) { -+ *file = bin_file; -+ *filelength = bin_file->size; -+ *status = NDIS_STATUS_SUCCESS; -+ } else -+ *status = NDIS_STATUS_FILE_NOT_FOUND; -+ -+ RtlFreeAnsiString(&ansi); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMapFile,3) -+ (NDIS_STATUS *status, void **mappedbuffer, struct wrap_bin_file *file) -+{ -+ ENTER2("%p", file); -+ -+ if (!file) { -+ *status = NDIS_STATUS_ALREADY_MAPPED; -+ EXIT2(return); -+ } -+ -+ *status = NDIS_STATUS_SUCCESS; -+ *mappedbuffer = file->data; -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisUnmapFile,1) -+ (struct wrap_bin_file *file) -+{ -+ ENTER2("%p", file); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisCloseFile,1) -+ (struct wrap_bin_file *file) -+{ -+ ENTER2("%p", file); -+ free_bin_file(file); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisGetSystemUpTime,1) -+ (ULONG *ms) -+{ -+ *ms = 1000 * jiffies / HZ; -+ EXIT5(return); -+} -+ -+wstdcall ULONG WIN_FUNC(NDIS_BUFFER_TO_SPAN_PAGES,1) -+ (ndis_buffer *buffer) -+{ -+ ULONG n, length; -+ -+ if (buffer == NULL) -+ EXIT2(return 0); -+ if (MmGetMdlByteCount(buffer) == 0) -+ EXIT2(return 1); -+ -+ length = MmGetMdlByteCount(buffer); -+ n = SPAN_PAGES(MmGetMdlVirtualAddress(buffer), length); -+ TRACE4("%p, %p, %d, %d", buffer->startva, buffer->mappedsystemva, -+ length, n); -+ EXIT3(return n); -+} -+ -+wstdcall void WIN_FUNC(NdisGetBufferPhysicalArraySize,2) -+ (ndis_buffer *buffer, UINT *arraysize) -+{ -+ ENTER3("%p", buffer); -+ *arraysize = NDIS_BUFFER_TO_SPAN_PAGES(buffer); -+ EXIT3(return); -+} -+ -+static struct ndis_configuration_parameter * -+ndis_encode_setting(struct wrap_device_setting *setting, -+ enum ndis_parameter_type type) -+{ -+ struct ansi_string ansi; -+ struct ndis_configuration_parameter *param; -+ -+ param = setting->encoded; -+ if (param) { -+ if (param->type == type) -+ EXIT2(return param); -+ if (param->type == NdisParameterString) -+ RtlFreeUnicodeString(¶m->data.string); -+ setting->encoded = NULL; -+ } else -+ param = ExAllocatePoolWithTag(NonPagedPool, sizeof(*param), 0); -+ if (!param) { -+ ERROR("couldn't allocate memory"); -+ return NULL; -+ } -+ switch (type) { -+ case NdisParameterInteger: -+ param->data.integer = simple_strtol(setting->value, NULL, 0); -+ TRACE2("0x%x", param->data.integer); -+ break; -+ case NdisParameterHexInteger: -+ param->data.integer = simple_strtol(setting->value, NULL, 16); -+ TRACE2("0x%x", param->data.integer); -+ break; -+ case NdisParameterString: -+ RtlInitAnsiString(&ansi, setting->value); -+ TRACE2("'%s'", ansi.buf); -+ if (RtlAnsiStringToUnicodeString(¶m->data.string, -+ &ansi, TRUE)) { -+ ExFreePool(param); -+ EXIT2(return NULL); -+ } -+ break; -+ case NdisParameterBinary: -+ param->data.integer = simple_strtol(setting->value, NULL, 2); -+ TRACE2("0x%x", param->data.integer); -+ break; -+ default: -+ ERROR("unknown type: %d", type); -+ ExFreePool(param); -+ return NULL; -+ } -+ param->type = type; -+ setting->encoded = param; -+ EXIT2(return param); -+} -+ -+static int ndis_decode_setting(struct wrap_device_setting *setting, -+ struct ndis_configuration_parameter *param) -+{ -+ struct ansi_string ansi; -+ struct ndis_configuration_parameter *prev; -+ -+ ENTER2("%p, %p", setting, param); -+ prev = setting->encoded; -+ if (prev && prev->type == NdisParameterString) { -+ RtlFreeUnicodeString(&prev->data.string); -+ setting->encoded = NULL; -+ } -+ switch (param->type) { -+ case NdisParameterInteger: -+ snprintf(setting->value, MAX_SETTING_VALUE_LEN, "%u", -+ param->data.integer); -+ break; -+ case NdisParameterHexInteger: -+ snprintf(setting->value, MAX_SETTING_VALUE_LEN, "%x", -+ param->data.integer); -+ break; -+ case NdisParameterString: -+ ansi.buf = setting->value; -+ ansi.max_length = MAX_SETTING_VALUE_LEN; -+ if ((RtlUnicodeStringToAnsiString(&ansi, ¶m->data.string, -+ FALSE) != STATUS_SUCCESS) -+ || ansi.length >= MAX_SETTING_VALUE_LEN) { -+ EXIT1(return -1); -+ } -+ if (ansi.length == ansi.max_length) -+ ansi.length--; -+ setting->value[ansi.length] = 0; -+ break; -+ default: -+ TRACE2("unknown setting type: %d", param->type); -+ return -1; -+ } -+ TRACE2("setting changed %s='%s', %d", setting->name, setting->value, -+ ansi.length); -+ return 0; -+} -+ -+static int read_setting(struct nt_list *setting_list, char *keyname, int length, -+ struct ndis_configuration_parameter **param, -+ enum ndis_parameter_type type) -+{ -+ struct wrap_device_setting *setting; -+ mutex_lock(&loader_mutex); -+ nt_list_for_each_entry(setting, setting_list, list) { -+ if (strnicmp(keyname, setting->name, length) == 0) { -+ TRACE2("setting %s='%s'", keyname, setting->value); -+ mutex_unlock(&loader_mutex); -+ *param = ndis_encode_setting(setting, type); -+ if (*param) -+ EXIT2(return 0); -+ else -+ EXIT2(return -1); -+ } -+ } -+ mutex_unlock(&loader_mutex); -+ EXIT2(return -1); -+} -+ -+wstdcall void WIN_FUNC(NdisReadConfiguration,5) -+ (NDIS_STATUS *status, struct ndis_configuration_parameter **param, -+ struct ndis_mp_block *nmb, struct unicode_string *key, -+ enum ndis_parameter_type type) -+{ -+ struct ansi_string ansi; -+ int ret; -+ -+ ENTER2("nmb: %p", nmb); -+ ret = RtlUnicodeStringToAnsiString(&ansi, key, TRUE); -+ if (ret != STATUS_SUCCESS || ansi.buf == NULL) { -+ *param = NULL; -+ *status = NDIS_STATUS_FAILURE; -+ RtlFreeAnsiString(&ansi); -+ EXIT2(return); -+ } -+ TRACE2("%d, %s", type, ansi.buf); -+ -+ if (read_setting(&nmb->wnd->wd->settings, ansi.buf, -+ ansi.length, param, type) == 0 || -+ read_setting(&nmb->wnd->wd->driver->settings, ansi.buf, -+ ansi.length, param, type) == 0) -+ *status = NDIS_STATUS_SUCCESS; -+ else { -+ TRACE2("setting %s not found (type:%d)", ansi.buf, type); -+ *status = NDIS_STATUS_FAILURE; -+ } -+ RtlFreeAnsiString(&ansi); -+ EXIT2(return); -+ -+} -+ -+wstdcall void WIN_FUNC(NdisWriteConfiguration,4) -+ (NDIS_STATUS *status, struct ndis_mp_block *nmb, -+ struct unicode_string *key, struct ndis_configuration_parameter *param) -+{ -+ struct ansi_string ansi; -+ char *keyname; -+ struct wrap_device_setting *setting; -+ -+ ENTER2("nmb: %p", nmb); -+ if (RtlUnicodeStringToAnsiString(&ansi, key, TRUE)) { -+ *status = NDIS_STATUS_FAILURE; -+ EXIT2(return); -+ } -+ keyname = ansi.buf; -+ TRACE2("%s", keyname); -+ -+ mutex_lock(&loader_mutex); -+ nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) { -+ if (strnicmp(keyname, setting->name, ansi.length) == 0) { -+ mutex_unlock(&loader_mutex); -+ if (ndis_decode_setting(setting, param)) -+ *status = NDIS_STATUS_FAILURE; -+ else -+ *status = NDIS_STATUS_SUCCESS; -+ RtlFreeAnsiString(&ansi); -+ EXIT2(return); -+ } -+ } -+ mutex_unlock(&loader_mutex); -+ setting = kzalloc(sizeof(*setting), GFP_KERNEL); -+ if (setting) { -+ if (ansi.length == ansi.max_length) -+ ansi.length--; -+ memcpy(setting->name, keyname, ansi.length); -+ setting->name[ansi.length] = 0; -+ if (ndis_decode_setting(setting, param)) -+ *status = NDIS_STATUS_FAILURE; -+ else { -+ *status = NDIS_STATUS_SUCCESS; -+ mutex_lock(&loader_mutex); -+ InsertTailList(&nmb->wnd->wd->settings, &setting->list); -+ mutex_unlock(&loader_mutex); -+ } -+ } else -+ *status = NDIS_STATUS_RESOURCES; -+ -+ RtlFreeAnsiString(&ansi); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisReadNetworkAddress,4) -+ (NDIS_STATUS *status, void **addr, UINT *len, -+ struct ndis_mp_block *nmb) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ struct ndis_configuration_parameter *param; -+ struct unicode_string key; -+ struct ansi_string ansi; -+ typeof(wnd->mac) mac; -+ int i, ret; -+ -+ ENTER2("%p", nmb); -+ RtlInitAnsiString(&ansi, "NetworkAddress"); -+ *status = NDIS_STATUS_FAILURE; -+ if (RtlAnsiStringToUnicodeString(&key, &ansi, TRUE) != STATUS_SUCCESS) -+ EXIT1(return); -+ -+ NdisReadConfiguration(&ret, ¶m, nmb, &key, NdisParameterString); -+ RtlFreeUnicodeString(&key); -+ if (ret != NDIS_STATUS_SUCCESS) -+ EXIT1(return); -+ ret = RtlUnicodeStringToAnsiString(&ansi, ¶m->data.string, TRUE); -+ if (ret != STATUS_SUCCESS) -+ EXIT1(return); -+ -+ i = 0; -+ if (ansi.length >= 2 * sizeof(mac)) { -+ for (i = 0; i < sizeof(mac); i++) { -+ char c[3]; -+ int x; -+ c[0] = ansi.buf[i*2]; -+ c[1] = ansi.buf[i*2+1]; -+ c[2] = 0; -+ ret = sscanf(c, "%x", &x); -+ if (ret != 1) -+ break; -+ mac[i] = x; -+ } -+ } -+ TRACE2("%s, %d, " MACSTR, ansi.buf, i, MAC2STR(mac)); -+ RtlFreeAnsiString(&ansi); -+ if (i == sizeof(mac)) { -+ memcpy(wnd->mac, mac, sizeof(wnd->mac)); -+ *len = sizeof(mac); -+ *addr = wnd->mac; -+ *status = NDIS_STATUS_SUCCESS; -+ } -+ EXIT1(return); -+} -+ -+wstdcall void WIN_FUNC(NdisInitializeString,2) -+ (struct unicode_string *dest, UCHAR *src) -+{ -+ struct ansi_string ansi; -+ -+ ENTER2(""); -+ if (src == NULL) { -+ dest->length = dest->max_length = 0; -+ dest->buf = NULL; -+ } else { -+ RtlInitAnsiString(&ansi, src); -+ /* the string is freed with NdisFreeMemory */ -+ RtlAnsiStringToUnicodeString(dest, &ansi, TRUE); -+ } -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisInitAnsiString,2) -+ (struct ansi_string *dst, CHAR *src) -+{ -+ RtlInitAnsiString(dst, src); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisInitUnicodeString,2) -+ (struct unicode_string *dest, const wchar_t *src) -+{ -+ RtlInitUnicodeString(dest, src); -+ return; -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisAnsiStringToUnicodeString,2) -+ (struct unicode_string *dst, struct ansi_string *src) -+{ -+ ENTER2(""); -+ if (dst == NULL || src == NULL) -+ EXIT2(return NDIS_STATUS_FAILURE); -+ if (RtlAnsiStringToUnicodeString(dst, src, FALSE) == STATUS_SUCCESS) -+ return NDIS_STATUS_SUCCESS; -+ else -+ return NDIS_STATUS_FAILURE; -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisUnicodeStringToAnsiString,2) -+ (struct ansi_string *dst, struct unicode_string *src) -+{ -+ ENTER2(""); -+ if (dst == NULL || src == NULL) -+ EXIT2(return NDIS_STATUS_FAILURE); -+ if (RtlUnicodeStringToAnsiString(dst, src, FALSE) == STATUS_SUCCESS) -+ return NDIS_STATUS_SUCCESS; -+ else -+ return NDIS_STATUS_FAILURE; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(NdisUpcaseUnicodeString,2) -+ (struct unicode_string *dst, struct unicode_string *src) -+{ -+ EXIT2(return RtlUpcaseUnicodeString(dst, src, FALSE)); -+} -+ -+wstdcall void WIN_FUNC(NdisMSetAttributesEx,5) -+ (struct ndis_mp_block *nmb, void *mp_ctx, -+ UINT hangcheck_interval, UINT attributes, ULONG adaptertype) -+{ -+ struct ndis_device *wnd; -+ -+ ENTER1("%p, %p, %d, %08x, %d", nmb, mp_ctx, hangcheck_interval, -+ attributes, adaptertype); -+ wnd = nmb->wnd; -+ nmb->mp_ctx = mp_ctx; -+ wnd->attributes = attributes; -+ -+ if ((attributes & NDIS_ATTRIBUTE_BUS_MASTER) && -+ wrap_is_pci_bus(wnd->wd->dev_bus)) -+ pci_set_master(wnd->wd->pci.pdev); -+ -+ if (hangcheck_interval > 0) -+ wnd->hangcheck_interval = 2 * hangcheck_interval * HZ; -+ else -+ wnd->hangcheck_interval = 2 * HZ; -+ -+ EXIT1(return); -+} -+ -+wstdcall ULONG WIN_FUNC(NdisReadPciSlotInformation,5) -+ (struct ndis_mp_block *nmb, ULONG slot, -+ ULONG offset, char *buf, ULONG len) -+{ -+ struct wrap_device *wd = nmb->wnd->wd; -+ ULONG i; -+ if (!wrap_is_pci_bus(wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return 0; -+ } -+ for (i = 0; i < len; i++) -+ if (pci_read_config_byte(wd->pci.pdev, offset + i, &buf[i]) != -+ PCIBIOS_SUCCESSFUL) -+ break; -+ DBG_BLOCK(2) { -+ if (i != len) -+ WARNING("%u, %u", i, len); -+ } -+ return i; -+} -+ -+wstdcall ULONG WIN_FUNC(NdisImmediateReadPciSlotInformation,5) -+ (struct ndis_mp_block *nmb, ULONG slot, -+ ULONG offset, char *buf, ULONG len) -+{ -+ return NdisReadPciSlotInformation(nmb, slot, offset, buf, len); -+} -+ -+wstdcall ULONG WIN_FUNC(NdisWritePciSlotInformation,5) -+ (struct ndis_mp_block *nmb, ULONG slot, -+ ULONG offset, char *buf, ULONG len) -+{ -+ struct wrap_device *wd = nmb->wnd->wd; -+ ULONG i; -+ if (!wrap_is_pci_bus(wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return 0; -+ } -+ for (i = 0; i < len; i++) -+ if (pci_write_config_byte(wd->pci.pdev, offset + i, buf[i]) != -+ PCIBIOS_SUCCESSFUL) -+ break; -+ DBG_BLOCK(2) { -+ if (i != len) -+ WARNING("%u, %u", i, len); -+ } -+ return i; -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterIoPortRange,4) -+ (void **virt, struct ndis_mp_block *nmb, UINT start, UINT len) -+{ -+ ENTER3("%08x %08x", start, len); -+ *virt = (void *)(ULONG_PTR)start; -+ return NDIS_STATUS_SUCCESS; -+} -+ -+wstdcall void WIN_FUNC(NdisMDeregisterIoPortRange,4) -+ (struct ndis_mp_block *nmb, UINT start, UINT len, void* virt) -+{ -+ ENTER1("%08x %08x", start, len); -+} -+ -+wstdcall void WIN_FUNC(NdisReadPortUchar,3) -+ (struct ndis_mp_block *nmb, ULONG port, char *data) -+{ -+ *data = inb(port); -+} -+ -+wstdcall void WIN_FUNC(NdisImmediateReadPortUchar,3) -+ (struct ndis_mp_block *nmb, ULONG port, char *data) -+{ -+ *data = inb(port); -+} -+ -+wstdcall void WIN_FUNC(NdisWritePortUchar,3) -+ (struct ndis_mp_block *nmb, ULONG port, char data) -+{ -+ outb(data, port); -+} -+ -+wstdcall void WIN_FUNC(NdisImmediateWritePortUchar,3) -+ (struct ndis_mp_block *nmb, ULONG port, char data) -+{ -+ outb(data, port); -+} -+ -+wstdcall void WIN_FUNC(NdisMQueryAdapterResources,4) -+ (NDIS_STATUS *status, struct ndis_mp_block *nmb, -+ NDIS_RESOURCE_LIST *resource_list, UINT *size) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ NDIS_RESOURCE_LIST *list; -+ UINT resource_length; -+ -+ list = &wnd->wd->resource_list->list->partial_resource_list; -+ resource_length = sizeof(struct cm_partial_resource_list) + -+ sizeof(struct cm_partial_resource_descriptor) * -+ (list->count - 1); -+ TRACE2("%p, %p,%d (%d), %p %d %d", wnd, resource_list, *size, -+ resource_length, &list->partial_descriptors[list->count-1], -+ list->partial_descriptors[list->count-1].u.interrupt.level, -+ list->partial_descriptors[list->count-1].u.interrupt.vector); -+ if (*size < sizeof(*list)) { -+ *size = resource_length; -+ *status = NDIS_STATUS_BUFFER_TOO_SHORT; -+ } else { -+ ULONG count; -+ if (*size >= resource_length) { -+ *size = resource_length; -+ count = list->count; -+ } else { -+ UINT n = sizeof(*list); -+ count = 1; -+ while (count++ < list->count && n < *size) -+ n += sizeof(list->partial_descriptors); -+ *size = n; -+ } -+ memcpy(resource_list, list, *size); -+ resource_list->count = count; -+ *status = NDIS_STATUS_SUCCESS; -+ } -+ EXIT2(return); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMPciAssignResources,3) -+ (struct ndis_mp_block *nmb, ULONG slot_number, -+ NDIS_RESOURCE_LIST **resources) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ -+ ENTER2("%p, %p", wnd, wnd->wd->resource_list); -+ *resources = &wnd->wd->resource_list->list->partial_resource_list; -+ EXIT2(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMMapIoSpace,4) -+ (void __iomem **virt, struct ndis_mp_block *nmb, -+ NDIS_PHY_ADDRESS phy_addr, UINT len) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ -+ ENTER2("%llx, %d", phy_addr, len); -+ *virt = MmMapIoSpace(phy_addr, len, MmCached); -+ if (*virt == NULL) { -+ ERROR("ioremap failed"); -+ EXIT2(return NDIS_STATUS_FAILURE); -+ } -+ wnd->mem_start = phy_addr; -+ wnd->mem_end = phy_addr + len; -+ TRACE2("%p", *virt); -+ EXIT2(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(NdisMUnmapIoSpace,3) -+ (struct ndis_mp_block *nmb, void __iomem *virt, UINT len) -+{ -+ ENTER2("%p, %d", virt, len); -+ MmUnmapIoSpace(virt, len); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisAllocateSpinLock,1) -+ (struct ndis_spinlock *lock) -+{ -+ TRACE4("lock %p, %p", lock, &lock->klock); -+ KeInitializeSpinLock(&lock->klock); -+ lock->irql = PASSIVE_LEVEL; -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisFreeSpinLock,1) -+ (struct ndis_spinlock *lock) -+{ -+ TRACE4("lock %p, %p", lock, &lock->klock); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisAcquireSpinLock,1) -+ (struct ndis_spinlock *lock) -+{ -+ ENTER6("lock %p, %p", lock, &lock->klock); -+// assert_irql(_irql_ <= DISPATCH_LEVEL); -+ lock->irql = nt_spin_lock_irql(&lock->klock, DISPATCH_LEVEL); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisReleaseSpinLock,1) -+ (struct ndis_spinlock *lock) -+{ -+ ENTER6("lock %p, %p", lock, &lock->klock); -+// assert_irql(_irql_ == DISPATCH_LEVEL); -+ nt_spin_unlock_irql(&lock->klock, lock->irql); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisDprAcquireSpinLock,1) -+ (struct ndis_spinlock *lock) -+{ -+ ENTER6("lock %p", &lock->klock); -+// assert_irql(_irql_ == DISPATCH_LEVEL); -+ nt_spin_lock(&lock->klock); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisDprReleaseSpinLock,1) -+ (struct ndis_spinlock *lock) -+{ -+ ENTER6("lock %p", &lock->klock); -+// assert_irql(_irql_ == DISPATCH_LEVEL); -+ nt_spin_unlock(&lock->klock); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisInitializeReadWriteLock,1) -+ (struct ndis_rw_lock *rw_lock) -+{ -+ ENTER3("%p", rw_lock); -+ memset(rw_lock, 0, sizeof(*rw_lock)); -+ KeInitializeSpinLock(&rw_lock->klock); -+ return; -+} -+ -+/* read/write locks are implemented in a rather simplistic way - we -+ * should probably use Linux's rw_lock implementation */ -+ -+wstdcall void WIN_FUNC(NdisAcquireReadWriteLock,3) -+ (struct ndis_rw_lock *rw_lock, BOOLEAN write, -+ struct lock_state *lock_state) -+{ -+ if (write) { -+ while (1) { -+ if (cmpxchg(&rw_lock->count, 0, -1) == 0) -+ return; -+ while (rw_lock->count) -+ cpu_relax(); -+ } -+ return; -+ } -+ while (1) { -+ typeof(rw_lock->count) count; -+ while ((count = rw_lock->count) < 0) -+ cpu_relax(); -+ if (cmpxchg(&rw_lock->count, count, count + 1) == count) -+ return; -+ } -+} -+ -+wstdcall void WIN_FUNC(NdisReleaseReadWriteLock,2) -+ (struct ndis_rw_lock *rw_lock, struct lock_state *lock_state) -+{ -+ if (rw_lock->count > 0) -+ pre_atomic_add(rw_lock->count, -1); -+ else if (rw_lock->count == -1) -+ rw_lock->count = 0; -+ else -+ WARNING("invalid state: %d", rw_lock->count); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMAllocateMapRegisters,5) -+ (struct ndis_mp_block *nmb, UINT dmachan, -+ NDIS_DMA_SIZE dmasize, ULONG basemap, ULONG max_buf_size) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ -+ ENTER2("%p, %d %d %d %d", wnd, dmachan, dmasize, basemap, max_buf_size); -+ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return NDIS_STATUS_NOT_SUPPORTED; -+ } -+ if (wnd->dma_map_count > 0) { -+ WARNING("%s: map registers already allocated: %u", -+ wnd->net_dev->name, wnd->dma_map_count); -+ EXIT2(return NDIS_STATUS_RESOURCES); -+ } -+ if (dmasize == NDIS_DMA_24BITS) { -+ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(24)) || -+ pci_set_consistent_dma_mask(wnd->wd->pci.pdev, -+ DMA_BIT_MASK(24))) -+ WARNING("setting dma mask failed"); -+ } else if (dmasize == NDIS_DMA_32BITS) { -+ /* consistent dma is in low 32-bits by default */ -+ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(32))) -+ WARNING("setting dma mask failed"); -+#ifdef CONFIG_X86_64 -+ } else if (dmasize == NDIS_DMA_64BITS) { -+ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(64)) || -+ pci_set_consistent_dma_mask(wnd->wd->pci.pdev, -+ DMA_BIT_MASK(64))) -+ WARNING("setting dma mask failed"); -+ else -+ wnd->net_dev->features |= NETIF_F_HIGHDMA; -+#endif -+ } else { -+ ERROR("dmasize %d not supported", dmasize); -+ EXIT2(return NDIS_STATUS_NOT_SUPPORTED); -+ } -+ /* since memory for buffer is allocated with kmalloc, buffer -+ * is physically contiguous, so entire map will fit in one -+ * register */ -+ if (basemap > 64) { -+ WARNING("Windows driver %s requesting too many (%u) " -+ "map registers", wnd->wd->driver->name, basemap); -+ /* As per NDIS, NDIS_STATUS_RESOURCES should be -+ * returned, but with that Atheros PCI driver fails - -+ * for now tolerate it */ -+// EXIT2(return NDIS_STATUS_RESOURCES); -+ } -+ -+ wnd->dma_map_addr = kzalloc(basemap * sizeof(*(wnd->dma_map_addr)), -+ GFP_KERNEL); -+ if (!wnd->dma_map_addr) -+ EXIT2(return NDIS_STATUS_RESOURCES); -+ wnd->dma_map_count = basemap; -+ TRACE2("%u", wnd->dma_map_count); -+ EXIT2(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(NdisMFreeMapRegisters,1) -+ (struct ndis_mp_block *nmb) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ int i; -+ -+ ENTER2("wnd: %p", wnd); -+ if (wnd->dma_map_addr) { -+ for (i = 0; i < wnd->dma_map_count; i++) { -+ if (wnd->dma_map_addr[i]) -+ WARNING("%s: dma addr 0x%llx not freed by " -+ "Windows driver", wnd->net_dev->name, -+ (unsigned long long)wnd->dma_map_addr[i]); -+ } -+ kfree(wnd->dma_map_addr); -+ wnd->dma_map_addr = NULL; -+ } else -+ WARNING("map registers already freed?"); -+ wnd->dma_map_count = 0; -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMStartBufferPhysicalMapping,6) -+ (struct ndis_mp_block *nmb, ndis_buffer *buf, -+ ULONG index, BOOLEAN write_to_dev, -+ struct ndis_phy_addr_unit *phy_addr_array, UINT *array_size) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ -+ ENTER3("%p, %p, %u, %u", wnd, buf, index, wnd->dma_map_count); -+ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return; -+ } -+ if (unlikely(wnd->sg_dma_size || !write_to_dev || -+ index >= wnd->dma_map_count)) { -+ WARNING("invalid request: %d, %d, %d, %d", wnd->sg_dma_size, -+ write_to_dev, index, wnd->dma_map_count); -+ phy_addr_array[0].phy_addr = 0; -+ phy_addr_array[0].length = 0; -+ *array_size = 0; -+ return; -+ } -+ if (wnd->dma_map_addr[index]) { -+ TRACE2("buffer %p at %d is already mapped: %llx", buf, index, -+ (unsigned long long)wnd->dma_map_addr[index]); -+// *array_size = 1; -+ return; -+ } -+ TRACE3("%p, %p, %u", buf, MmGetSystemAddressForMdl(buf), -+ MmGetMdlByteCount(buf)); -+ DBG_BLOCK(4) { -+ dump_bytes(__func__, MmGetSystemAddressForMdl(buf), -+ MmGetMdlByteCount(buf)); -+ } -+ wnd->dma_map_addr[index] = -+ PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, -+ MmGetSystemAddressForMdl(buf), -+ MmGetMdlByteCount(buf), PCI_DMA_TODEVICE); -+ phy_addr_array[0].phy_addr = wnd->dma_map_addr[index]; -+ phy_addr_array[0].length = MmGetMdlByteCount(buf); -+ TRACE4("%llx, %d, %d", phy_addr_array[0].phy_addr, -+ phy_addr_array[0].length, index); -+ *array_size = 1; -+} -+ -+wstdcall void WIN_FUNC(NdisMCompleteBufferPhysicalMapping,3) -+ (struct ndis_mp_block *nmb, ndis_buffer *buf, ULONG index) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ -+ ENTER3("%p, %p %u (%u)", wnd, buf, index, wnd->dma_map_count); -+ -+ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return; -+ } -+ if (unlikely(wnd->sg_dma_size)) -+ WARNING("buffer %p may have been unmapped already", buf); -+ if (index >= wnd->dma_map_count) { -+ ERROR("invalid map register (%u >= %u)", -+ index, wnd->dma_map_count); -+ return; -+ } -+ TRACE4("%llx", (unsigned long long)wnd->dma_map_addr[index]); -+ if (wnd->dma_map_addr[index]) { -+ PCI_DMA_UNMAP_SINGLE(wnd->wd->pci.pdev, wnd->dma_map_addr[index], -+ MmGetMdlByteCount(buf), PCI_DMA_TODEVICE); -+ wnd->dma_map_addr[index] = 0; -+ } else -+ WARNING("map registers at %u not used", index); -+} -+ -+wstdcall void WIN_FUNC(NdisMAllocateSharedMemory,5) -+ (struct ndis_mp_block *nmb, ULONG size, -+ BOOLEAN cached, void **virt, NDIS_PHY_ADDRESS *phys) -+{ -+ dma_addr_t dma_addr; -+ struct wrap_device *wd = nmb->wnd->wd; -+ -+ ENTER3("size: %u, cached: %d", size, cached); -+ if (!wrap_is_pci_bus(wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return; -+ } -+ *virt = PCI_DMA_ALLOC_COHERENT(wd->pci.pdev, size, &dma_addr); -+ if (*virt) -+ *phys = dma_addr; -+ else -+ WARNING("couldn't allocate %d bytes of %scached DMA memory", -+ size, cached ? "" : "un-"); -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMFreeSharedMemory,5) -+ (struct ndis_mp_block *nmb, ULONG size, BOOLEAN cached, -+ void *virt, NDIS_PHY_ADDRESS addr) -+{ -+ struct wrap_device *wd = nmb->wnd->wd; -+ ENTER3("%p, %llx, %u", virt, addr, size); -+ if (!wrap_is_pci_bus(wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return; -+ } -+ PCI_DMA_FREE_COHERENT(wd->pci.pdev, size, virt, addr); -+ EXIT3(return); -+} -+ -+wstdcall void alloc_shared_memory_async(void *arg1, void *arg2) -+{ -+ struct ndis_device *wnd; -+ struct alloc_shared_mem *alloc_shared_mem; -+ struct miniport *mp; -+ void *virt; -+ NDIS_PHY_ADDRESS phys; -+ KIRQL irql; -+ -+ wnd = arg1; -+ alloc_shared_mem = arg2; -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ NdisMAllocateSharedMemory(wnd->nmb, alloc_shared_mem->size, -+ alloc_shared_mem->cached, &virt, &phys); -+ irql = serialize_lock_irql(wnd); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ LIN2WIN5(mp->alloc_complete, wnd->nmb, virt, -+ &phys, alloc_shared_mem->size, alloc_shared_mem->ctx); -+ serialize_unlock_irql(wnd, irql); -+ kfree(alloc_shared_mem); -+} -+WIN_FUNC_DECL(alloc_shared_memory_async,2) -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMAllocateSharedMemoryAsync,4) -+ (struct ndis_mp_block *nmb, ULONG size, BOOLEAN cached, void *ctx) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ struct alloc_shared_mem *alloc_shared_mem; -+ -+ ENTER3("wnd: %p", wnd); -+ alloc_shared_mem = kmalloc(sizeof(*alloc_shared_mem), irql_gfp()); -+ if (!alloc_shared_mem) { -+ WARNING("couldn't allocate memory"); -+ return NDIS_STATUS_FAILURE; -+ } -+ -+ alloc_shared_mem->size = size; -+ alloc_shared_mem->cached = cached; -+ alloc_shared_mem->ctx = ctx; -+ if (schedule_ntos_work_item(WIN_FUNC_PTR(alloc_shared_memory_async,2), -+ wnd, alloc_shared_mem)) -+ EXIT3(return NDIS_STATUS_FAILURE); -+ EXIT3(return NDIS_STATUS_PENDING); -+} -+ -+/* Some drivers allocate NDIS_BUFFER (aka MDL) very often; instead of -+ * allocating and freeing with kernel functions, we chain them into -+ * ndis_buffer_pool. When an MDL is freed, it is added to the list of -+ * free MDLs. When allocated, we first check if there is one in free -+ * list and if so just return it; otherwise, we allocate a new one and -+ * return that. This reduces memory fragmentation. Windows DDK says -+ * that the driver itself shouldn't check what is returned in -+ * pool_handle, presumably because buffer pools are not used in -+ * XP. However, as long as driver follows rest of the semantics - that -+ * it should indicate maximum number of MDLs used with num_descr and -+ * pass the same pool_handle in other buffer functions, this should -+ * work. Sadly, though, NdisFreeBuffer doesn't pass the pool_handle, -+ * so we use 'process' field of MDL to store pool_handle. */ -+ -+wstdcall void WIN_FUNC(NdisAllocateBufferPool,3) -+ (NDIS_STATUS *status, struct ndis_buffer_pool **pool_handle, -+ UINT num_descr) -+{ -+ struct ndis_buffer_pool *pool; -+ -+ ENTER1("buffers: %d", num_descr); -+ pool = kmalloc(sizeof(*pool), irql_gfp()); -+ if (!pool) { -+ *status = NDIS_STATUS_RESOURCES; -+ EXIT3(return); -+ } -+ spin_lock_init(&pool->lock); -+ pool->max_descr = num_descr; -+ pool->num_allocated_descr = 0; -+ pool->free_descr = NULL; -+ *pool_handle = pool; -+ *status = NDIS_STATUS_SUCCESS; -+ TRACE1("pool: %p, num_descr: %d", pool, num_descr); -+ EXIT1(return); -+} -+ -+wstdcall void WIN_FUNC(NdisAllocateBuffer,5) -+ (NDIS_STATUS *status, ndis_buffer **buffer, -+ struct ndis_buffer_pool *pool, void *virt, UINT length) -+{ -+ ndis_buffer *descr; -+ -+ ENTER4("pool: %p (%d)", pool, pool->num_allocated_descr); -+ /* NDIS drivers should call this at DISPATCH_LEVEL, but -+ * alloc_tx_packet calls at SOFT_IRQL */ -+ assert_irql(_irql_ <= SOFT_LEVEL); -+ if (!pool) { -+ *status = NDIS_STATUS_FAILURE; -+ *buffer = NULL; -+ EXIT4(return); -+ } -+ spin_lock_bh(&pool->lock); -+ if ((descr = pool->free_descr)) -+ pool->free_descr = descr->next; -+ spin_unlock_bh(&pool->lock); -+ if (descr) { -+ typeof(descr->flags) flags; -+ flags = descr->flags; -+ memset(descr, 0, sizeof(*descr)); -+ MmInitializeMdl(descr, virt, length); -+ if (flags & MDL_CACHE_ALLOCATED) -+ descr->flags |= MDL_CACHE_ALLOCATED; -+ } else { -+ if (pool->num_allocated_descr > pool->max_descr) { -+ TRACE2("pool %p is full: %d(%d)", pool, -+ pool->num_allocated_descr, pool->max_descr); -+#ifndef ALLOW_POOL_OVERFLOW -+ *status = NDIS_STATUS_FAILURE; -+ *buffer = NULL; -+ return; -+#endif -+ } -+ descr = allocate_init_mdl(virt, length); -+ if (!descr) { -+ WARNING("couldn't allocate buffer"); -+ *status = NDIS_STATUS_FAILURE; -+ *buffer = NULL; -+ EXIT4(return); -+ } -+ TRACE4("buffer %p for %p, %d", descr, virt, length); -+ atomic_inc_var(pool->num_allocated_descr); -+ } -+ /* TODO: make sure this mdl can map given buffer */ -+ MmBuildMdlForNonPagedPool(descr); -+// descr->flags |= MDL_ALLOCATED_FIXED_SIZE | -+// MDL_MAPPED_TO_SYSTEM_VA | MDL_PAGES_LOCKED; -+ descr->pool = pool; -+ *buffer = descr; -+ *status = NDIS_STATUS_SUCCESS; -+ TRACE4("buffer: %p", descr); -+ EXIT4(return); -+} -+ -+wstdcall void WIN_FUNC(NdisFreeBuffer,1) -+ (ndis_buffer *buffer) -+{ -+ struct ndis_buffer_pool *pool; -+ -+ ENTER4("%p", buffer); -+ if (!buffer || !buffer->pool) { -+ ERROR("invalid buffer"); -+ EXIT4(return); -+ } -+ pool = buffer->pool; -+ if (pool->num_allocated_descr > MAX_ALLOCATED_NDIS_BUFFERS) { -+ /* NB NB NB: set mdl's 'pool' field to NULL before -+ * calling free_mdl; otherwise free_mdl calls -+ * NdisFreeBuffer back */ -+ atomic_dec_var(pool->num_allocated_descr); -+ buffer->pool = NULL; -+ free_mdl(buffer); -+ } else { -+ spin_lock_bh(&pool->lock); -+ buffer->next = pool->free_descr; -+ pool->free_descr = buffer; -+ spin_unlock_bh(&pool->lock); -+ } -+ EXIT4(return); -+} -+ -+wstdcall void WIN_FUNC(NdisFreeBufferPool,1) -+ (struct ndis_buffer_pool *pool) -+{ -+ ndis_buffer *cur, *next; -+ -+ TRACE3("pool: %p", pool); -+ if (!pool) { -+ WARNING("invalid pool"); -+ EXIT3(return); -+ } -+ spin_lock_bh(&pool->lock); -+ cur = pool->free_descr; -+ while (cur) { -+ next = cur->next; -+ cur->pool = NULL; -+ free_mdl(cur); -+ cur = next; -+ } -+ spin_unlock_bh(&pool->lock); -+ kfree(pool); -+ pool = NULL; -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisAdjustBufferLength,2) -+ (ndis_buffer *buffer, UINT length) -+{ -+ ENTER4("%p, %d", buffer, length); -+ buffer->bytecount = length; -+} -+ -+wstdcall void WIN_FUNC(NdisQueryBuffer,3) -+ (ndis_buffer *buffer, void **virt, UINT *length) -+{ -+ ENTER4("buffer: %p", buffer); -+ if (virt) -+ *virt = MmGetSystemAddressForMdl(buffer); -+ *length = MmGetMdlByteCount(buffer); -+ TRACE4("%p, %u", virt ? *virt : NULL, *length); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisQueryBufferSafe,4) -+ (ndis_buffer *buffer, void **virt, UINT *length, -+ enum mm_page_priority priority) -+{ -+ ENTER4("%p, %p, %p, %d", buffer, virt, length, priority); -+ if (virt) -+ *virt = MmGetSystemAddressForMdlSafe(buffer, priority); -+ *length = MmGetMdlByteCount(buffer); -+ TRACE4("%p, %u", virt ? *virt : NULL, *length); -+} -+ -+wstdcall void *WIN_FUNC(NdisBufferVirtualAddress,1) -+ (ndis_buffer *buffer) -+{ -+ ENTER3("%p", buffer); -+ return MmGetSystemAddressForMdl(buffer); -+} -+ -+wstdcall ULONG WIN_FUNC(NdisBufferLength,1) -+ (ndis_buffer *buffer) -+{ -+ ENTER3("%p", buffer); -+ return MmGetMdlByteCount(buffer); -+} -+ -+wstdcall void WIN_FUNC(NdisQueryBufferOffset,3) -+ (ndis_buffer *buffer, UINT *offset, UINT *length) -+{ -+ ENTER3("%p", buffer); -+ *offset = MmGetMdlByteOffset(buffer); -+ *length = MmGetMdlByteCount(buffer); -+ TRACE3("%d, %d", *offset, *length); -+} -+ -+wstdcall void WIN_FUNC(NdisUnchainBufferAtBack,2) -+ (struct ndis_packet *packet, ndis_buffer **buffer) -+{ -+ ndis_buffer *b, *btail; -+ -+ ENTER3("%p", packet); -+ b = packet->private.buffer_head; -+ if (!b) { -+ /* no buffer in packet */ -+ *buffer = NULL; -+ EXIT3(return); -+ } -+ btail = packet->private.buffer_tail; -+ *buffer = btail; -+ if (b == btail) { -+ /* one buffer in packet */ -+ packet->private.buffer_head = NULL; -+ packet->private.buffer_tail = NULL; -+ } else { -+ while (b->next != btail) -+ b = b->next; -+ packet->private.buffer_tail = b; -+ b->next = NULL; -+ } -+ packet->private.valid_counts = FALSE; -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisUnchainBufferAtFront,2) -+ (struct ndis_packet *packet, ndis_buffer **buffer) -+{ -+ ENTER3("%p", packet); -+ if (packet->private.buffer_head == NULL) { -+ /* no buffer in packet */ -+ *buffer = NULL; -+ EXIT3(return); -+ } -+ -+ *buffer = packet->private.buffer_head; -+ if (packet->private.buffer_head == packet->private.buffer_tail) { -+ /* one buffer in packet */ -+ packet->private.buffer_head = NULL; -+ packet->private.buffer_tail = NULL; -+ } else -+ packet->private.buffer_head = (*buffer)->next; -+ -+ packet->private.valid_counts = FALSE; -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisGetFirstBufferFromPacketSafe,6) -+ (struct ndis_packet *packet, ndis_buffer **first_buffer, -+ void **first_buffer_va, UINT *first_buffer_length, -+ UINT *total_buffer_length, enum mm_page_priority priority) -+{ -+ ndis_buffer *b = packet->private.buffer_head; -+ -+ ENTER3("%p(%p)", packet, b); -+ *first_buffer = b; -+ if (b) { -+ *first_buffer_va = MmGetSystemAddressForMdlSafe(b, priority); -+ *first_buffer_length = *total_buffer_length = -+ MmGetMdlByteCount(b); -+ for (b = b->next; b; b = b->next) -+ *total_buffer_length += MmGetMdlByteCount(b); -+ } else { -+ *first_buffer_va = NULL; -+ *first_buffer_length = 0; -+ *total_buffer_length = 0; -+ } -+ TRACE3("%p, %d, %d", *first_buffer_va, *first_buffer_length, -+ *total_buffer_length); -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisGetFirstBufferFromPacket,6) -+ (struct ndis_packet *packet, ndis_buffer **first_buffer, -+ void **first_buffer_va, UINT *first_buffer_length, -+ UINT *total_buffer_length, enum mm_page_priority priority) -+{ -+ NdisGetFirstBufferFromPacketSafe(packet, first_buffer, -+ first_buffer_va, first_buffer_length, -+ total_buffer_length, -+ NormalPagePriority); -+} -+ -+wstdcall void WIN_FUNC(NdisAllocatePacketPoolEx,5) -+ (NDIS_STATUS *status, struct ndis_packet_pool **pool_handle, -+ UINT num_descr, UINT overflowsize, UINT proto_rsvd_length) -+{ -+ struct ndis_packet_pool *pool; -+ -+ ENTER3("buffers: %d, length: %d", num_descr, proto_rsvd_length); -+ pool = kzalloc(sizeof(*pool), irql_gfp()); -+ if (!pool) { -+ *status = NDIS_STATUS_RESOURCES; -+ EXIT3(return); -+ } -+ spin_lock_init(&pool->lock); -+ pool->max_descr = num_descr; -+ pool->num_allocated_descr = 0; -+ pool->num_used_descr = 0; -+ pool->free_descr = NULL; -+ pool->proto_rsvd_length = proto_rsvd_length; -+ *pool_handle = pool; -+ *status = NDIS_STATUS_SUCCESS; -+ TRACE3("pool: %p", pool); -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisAllocatePacketPool,4) -+ (NDIS_STATUS *status, struct ndis_packet_pool **pool_handle, -+ UINT num_descr, UINT proto_rsvd_length) -+{ -+ NdisAllocatePacketPoolEx(status, pool_handle, num_descr, 0, -+ proto_rsvd_length); -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisFreePacketPool,1) -+ (struct ndis_packet_pool *pool) -+{ -+ struct ndis_packet *packet, *next; -+ -+ ENTER3("pool: %p", pool); -+ if (!pool) { -+ WARNING("invalid pool"); -+ EXIT3(return); -+ } -+ spin_lock_bh(&pool->lock); -+ packet = pool->free_descr; -+ while (packet) { -+ next = (struct ndis_packet *)packet->reserved[0]; -+ kfree(packet); -+ packet = next; -+ } -+ pool->num_allocated_descr = 0; -+ pool->num_used_descr = 0; -+ pool->free_descr = NULL; -+ spin_unlock_bh(&pool->lock); -+ kfree(pool); -+ EXIT3(return); -+} -+ -+wstdcall UINT WIN_FUNC(NdisPacketPoolUsage,1) -+ (struct ndis_packet_pool *pool) -+{ -+ EXIT4(return pool->num_used_descr); -+} -+ -+wstdcall void WIN_FUNC(NdisAllocatePacket,3) -+ (NDIS_STATUS *status, struct ndis_packet **ndis_packet, -+ struct ndis_packet_pool *pool) -+{ -+ struct ndis_packet *packet; -+ int packet_length; -+ -+ ENTER4("pool: %p", pool); -+ if (!pool) { -+ *status = NDIS_STATUS_RESOURCES; -+ *ndis_packet = NULL; -+ EXIT4(return); -+ } -+ assert_irql(_irql_ <= SOFT_LEVEL); -+ if (pool->num_used_descr > pool->max_descr) { -+ TRACE3("pool %p is full: %d(%d)", pool, -+ pool->num_used_descr, pool->max_descr); -+#ifndef ALLOW_POOL_OVERFLOW -+ *status = NDIS_STATUS_RESOURCES; -+ *ndis_packet = NULL; -+ return; -+#endif -+ } -+ /* packet has space for 1 byte in protocol_reserved field */ -+ packet_length = sizeof(*packet) - 1 + pool->proto_rsvd_length + -+ sizeof(struct ndis_packet_oob_data); -+ spin_lock_bh(&pool->lock); -+ if ((packet = pool->free_descr)) -+ pool->free_descr = (void *)packet->reserved[0]; -+ spin_unlock_bh(&pool->lock); -+ if (!packet) { -+ packet = kmalloc(packet_length, irql_gfp()); -+ if (!packet) { -+ WARNING("couldn't allocate packet"); -+ *status = NDIS_STATUS_RESOURCES; -+ *ndis_packet = NULL; -+ return; -+ } -+ atomic_inc_var(pool->num_allocated_descr); -+ } -+ TRACE4("%p, %p", pool, packet); -+ atomic_inc_var(pool->num_used_descr); -+ memset(packet, 0, packet_length); -+ packet->private.oob_offset = -+ packet_length - sizeof(struct ndis_packet_oob_data); -+ packet->private.packet_flags = fPACKET_ALLOCATED_BY_NDIS; -+ packet->private.pool = pool; -+ *ndis_packet = packet; -+ *status = NDIS_STATUS_SUCCESS; -+ EXIT4(return); -+} -+ -+wstdcall void WIN_FUNC(NdisDprAllocatePacket,3) -+ (NDIS_STATUS *status, struct ndis_packet **packet, -+ struct ndis_packet_pool *pool) -+{ -+ NdisAllocatePacket(status, packet, pool); -+} -+ -+wstdcall void WIN_FUNC(NdisFreePacket,1) -+ (struct ndis_packet *packet) -+{ -+ struct ndis_packet_pool *pool; -+ -+ ENTER4("%p, %p", packet, packet->private.pool); -+ pool = packet->private.pool; -+ if (!pool) { -+ ERROR("invalid pool %p", packet); -+ EXIT4(return); -+ } -+ assert((int)pool->num_used_descr > 0); -+ atomic_dec_var(pool->num_used_descr); -+ if (packet->reserved[1]) { -+ TRACE3("%p, %p", packet, (void *)packet->reserved[1]); -+ kfree((void *)packet->reserved[1]); -+ packet->reserved[1] = 0; -+ } -+ if (pool->num_allocated_descr > MAX_ALLOCATED_NDIS_PACKETS) { -+ TRACE3("%p", pool); -+ atomic_dec_var(pool->num_allocated_descr); -+ kfree(packet); -+ } else { -+ TRACE4("%p, %p, %p", pool, packet, pool->free_descr); -+ spin_lock_bh(&pool->lock); -+ packet->reserved[0] = -+ (typeof(packet->reserved[0]))pool->free_descr; -+ pool->free_descr = packet; -+ spin_unlock_bh(&pool->lock); -+ } -+ EXIT4(return); -+} -+ -+wstdcall struct ndis_packet_stack *WIN_FUNC(NdisIMGetCurrentPacketStack,2) -+ (struct ndis_packet *packet, BOOLEAN *stacks_remain) -+{ -+ struct ndis_packet_stack *stack; -+ -+ if (!packet->reserved[1]) { -+ stack = kzalloc(2 * sizeof(*stack), irql_gfp()); -+ TRACE3("%p, %p", packet, stack); -+ packet->reserved[1] = (typeof(packet->reserved[1]))stack; -+ } else { -+ stack = (void *)packet->reserved[1];; -+ if (xchg(&stack->ndis_reserved[0], 1)) { -+ stack++; -+ if (xchg(&stack->ndis_reserved[0], 1)) -+ stack = NULL; -+ } -+ TRACE3("%p", stack); -+ } -+ if (stack) -+ *stacks_remain = TRUE; -+ else -+ *stacks_remain = FALSE; -+ -+ EXIT3(return stack); -+} -+ -+wstdcall void WIN_FUNC(NdisCopyFromPacketToPacketSafe,7) -+ (struct ndis_packet *dst, UINT dst_offset, UINT num_to_copy, -+ struct ndis_packet *src, UINT src_offset, UINT *num_copied, -+ enum mm_page_priority priority) -+{ -+ UINT dst_n, src_n, n, left; -+ ndis_buffer *dst_buf; -+ ndis_buffer *src_buf; -+ -+ ENTER4(""); -+ if (!dst || !src) { -+ *num_copied = 0; -+ EXIT4(return); -+ } -+ -+ dst_buf = dst->private.buffer_head; -+ src_buf = src->private.buffer_head; -+ -+ if (!dst_buf || !src_buf) { -+ *num_copied = 0; -+ EXIT4(return); -+ } -+ dst_n = MmGetMdlByteCount(dst_buf) - dst_offset; -+ src_n = MmGetMdlByteCount(src_buf) - src_offset; -+ -+ n = min(src_n, dst_n); -+ n = min(n, num_to_copy); -+ memcpy(MmGetSystemAddressForMdl(dst_buf) + dst_offset, -+ MmGetSystemAddressForMdl(src_buf) + src_offset, n); -+ -+ left = num_to_copy - n; -+ while (left > 0) { -+ src_offset += n; -+ dst_offset += n; -+ dst_n -= n; -+ src_n -= n; -+ if (dst_n == 0) { -+ dst_buf = dst_buf->next; -+ if (!dst_buf) -+ break; -+ dst_n = MmGetMdlByteCount(dst_buf); -+ dst_offset = 0; -+ } -+ if (src_n == 0) { -+ src_buf = src_buf->next; -+ if (!src_buf) -+ break; -+ src_n = MmGetMdlByteCount(src_buf); -+ src_offset = 0; -+ } -+ -+ n = min(src_n, dst_n); -+ n = min(n, left); -+ memcpy(MmGetSystemAddressForMdl(dst_buf) + dst_offset, -+ MmGetSystemAddressForMdl(src_buf) + src_offset, n); -+ left -= n; -+ } -+ *num_copied = num_to_copy - left; -+ EXIT4(return); -+} -+ -+wstdcall void WIN_FUNC(NdisCopyFromPacketToPacket,6) -+ (struct ndis_packet *dst, UINT dst_offset, UINT num_to_copy, -+ struct ndis_packet *src, UINT src_offset, UINT *num_copied) -+{ -+ NdisCopyFromPacketToPacketSafe(dst, dst_offset, num_to_copy, -+ src, src_offset, num_copied, -+ NormalPagePriority); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisIMCopySendPerPacketInfo,2) -+ (struct ndis_packet *dst, struct ndis_packet *src) -+{ -+ struct ndis_packet_oob_data *dst_oob, *src_oob; -+ dst_oob = NDIS_PACKET_OOB_DATA(dst); -+ src_oob = NDIS_PACKET_OOB_DATA(src); -+ memcpy(&dst_oob->ext, &src_oob->ext, sizeof(dst_oob->ext)); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisSend,3) -+ (NDIS_STATUS *status, struct ndis_mp_block *nmb, -+ struct ndis_packet *packet) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ struct miniport *mp; -+ KIRQL irql; -+ -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ if (mp->send_packets) { -+ irql = serialize_lock_irql(wnd); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx, &packet, 1); -+ serialize_unlock_irql(wnd, irql); -+ if (deserialized_driver(wnd)) -+ *status = NDIS_STATUS_PENDING; -+ else { -+ struct ndis_packet_oob_data *oob_data; -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ *status = oob_data->status; -+ switch (*status) { -+ case NDIS_STATUS_SUCCESS: -+ free_tx_packet(wnd, packet, *status); -+ break; -+ case NDIS_STATUS_PENDING: -+ break; -+ case NDIS_STATUS_RESOURCES: -+ wnd->tx_ok = 0; -+ break; -+ case NDIS_STATUS_FAILURE: -+ default: -+ free_tx_packet(wnd, packet, *status); -+ break; -+ } -+ } -+ } else { -+ irql = serialize_lock_irql(wnd); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ *status = LIN2WIN3(mp->send, wnd->nmb->mp_ctx, packet, 0); -+ serialize_unlock_irql(wnd, irql); -+ switch (*status) { -+ case NDIS_STATUS_SUCCESS: -+ free_tx_packet(wnd, packet, *status); -+ break; -+ case NDIS_STATUS_PENDING: -+ break; -+ case NDIS_STATUS_RESOURCES: -+ wnd->tx_ok = 0; -+ break; -+ case NDIS_STATUS_FAILURE: -+ default: -+ free_tx_packet(wnd, packet, *status); -+ break; -+ } -+ } -+ EXIT3(return); -+} -+ -+/* called for serialized drivers only */ -+wstdcall void mp_timer_dpc(struct kdpc *kdpc, void *ctx, void *arg1, void *arg2) -+{ -+ struct ndis_mp_timer *timer; -+ struct ndis_mp_block *nmb; -+ -+ timer = ctx; -+ TIMERENTER("%p, %p, %p, %p", timer, timer->func, timer->ctx, timer->nmb); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ nmb = timer->nmb; -+ serialize_lock(nmb->wnd); -+ LIN2WIN4(timer->func, NULL, timer->ctx, NULL, NULL); -+ serialize_unlock(nmb->wnd); -+ TIMEREXIT(return); -+} -+WIN_FUNC_DECL(mp_timer_dpc,4) -+ -+wstdcall void WIN_FUNC(NdisMInitializeTimer,4) -+ (struct ndis_mp_timer *timer, struct ndis_mp_block *nmb, -+ DPC func, void *ctx) -+{ -+ TIMERENTER("%p, %p, %p, %p", timer, func, ctx, nmb); -+ assert_irql(_irql_ == PASSIVE_LEVEL); -+ timer->func = func; -+ timer->ctx = ctx; -+ timer->nmb = nmb; -+ if (deserialized_driver(nmb->wnd)) -+ KeInitializeDpc(&timer->kdpc, func, ctx); -+ else -+ KeInitializeDpc(&timer->kdpc, WIN_FUNC_PTR(mp_timer_dpc,4), -+ timer); -+ wrap_init_timer(&timer->nt_timer, NotificationTimer, nmb); -+ TIMEREXIT(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMSetPeriodicTimer,2) -+ (struct ndis_mp_timer *timer, UINT period_ms) -+{ -+ unsigned long expires = MSEC_TO_HZ(period_ms); -+ -+ TIMERENTER("%p, %u, %ld", timer, period_ms, expires); -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ wrap_set_timer(&timer->nt_timer, expires, expires, &timer->kdpc); -+ TIMEREXIT(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMCancelTimer,2) -+ (struct ndis_mp_timer *timer, BOOLEAN *canceled) -+{ -+ TIMERENTER("%p", timer); -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ *canceled = KeCancelTimer(&timer->nt_timer); -+ TIMERTRACE("%d", *canceled); -+ return; -+} -+ -+wstdcall void WIN_FUNC(NdisInitializeTimer,3) -+ (struct ndis_timer *timer, void *func, void *ctx) -+{ -+ TIMERENTER("%p, %p, %p", timer, func, ctx); -+ assert_irql(_irql_ == PASSIVE_LEVEL); -+ KeInitializeDpc(&timer->kdpc, func, ctx); -+ wrap_init_timer(&timer->nt_timer, NotificationTimer, NULL); -+ TIMEREXIT(return); -+} -+ -+/* NdisMSetTimer is a macro that calls NdisSetTimer with -+ * ndis_mp_timer typecast to ndis_timer */ -+ -+wstdcall void WIN_FUNC(NdisSetTimer,2) -+ (struct ndis_timer *timer, UINT duetime_ms) -+{ -+ unsigned long expires = MSEC_TO_HZ(duetime_ms); -+ -+ TIMERENTER("%p, %p, %u, %ld", timer, timer->nt_timer.wrap_timer, -+ duetime_ms, expires); -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ wrap_set_timer(&timer->nt_timer, expires, 0, &timer->kdpc); -+ TIMEREXIT(return); -+} -+ -+wstdcall void WIN_FUNC(NdisCancelTimer,2) -+ (struct ndis_timer *timer, BOOLEAN *canceled) -+{ -+ TIMERENTER("%p", timer); -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ *canceled = KeCancelTimer(&timer->nt_timer); -+ TIMEREXIT(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMRegisterAdapterShutdownHandler,3) -+ (struct ndis_mp_block *nmb, void *ctx, void *func) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ ENTER1("%p", func); -+ wnd->wd->driver->ndis_driver->mp.shutdown = func; -+ wnd->shutdown_ctx = ctx; -+} -+ -+wstdcall void WIN_FUNC(NdisMDeregisterAdapterShutdownHandler,1) -+ (struct ndis_mp_block *nmb) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ wnd->wd->driver->ndis_driver->mp.shutdown = NULL; -+ wnd->shutdown_ctx = NULL; -+} -+ -+/* TODO: rt61 (serialized) driver doesn't want MiniportEnableInterrupt -+ * to be called in irq handler, but mrv800c (deserialized) driver -+ * wants. NDIS is confusing about when to call MiniportEnableInterrupt -+ * For now, handle these cases with two separate irq handlers based on -+ * observation of these two drivers. However, it is likely not -+ * correct. */ -+wstdcall void deserialized_irq_handler(struct kdpc *kdpc, void *ctx, -+ void *arg1, void *arg2) -+{ -+ struct ndis_device *wnd = ctx; -+ ndis_interrupt_handler irq_handler = arg1; -+ struct miniport *mp = arg2; -+ -+ TRACE6("%p", irq_handler); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ LIN2WIN1(irq_handler, wnd->nmb->mp_ctx); -+ if (mp->enable_interrupt) -+ LIN2WIN1(mp->enable_interrupt, wnd->nmb->mp_ctx); -+ EXIT6(return); -+} -+WIN_FUNC_DECL(deserialized_irq_handler,4) -+ -+wstdcall void serialized_irq_handler(struct kdpc *kdpc, void *ctx, -+ void *arg1, void *arg2) -+{ -+ struct ndis_device *wnd = ctx; -+ ndis_interrupt_handler irq_handler = arg1; -+ -+ TRACE6("%p, %p, %p", wnd, irq_handler, arg2); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ serialize_lock(wnd); -+ LIN2WIN1(irq_handler, arg2); -+ serialize_unlock(wnd); -+ EXIT6(return); -+} -+WIN_FUNC_DECL(serialized_irq_handler,4) -+ -+wstdcall BOOLEAN ndis_isr(struct kinterrupt *kinterrupt, void *ctx) -+{ -+ struct ndis_mp_interrupt *mp_interrupt = ctx; -+ struct ndis_device *wnd = mp_interrupt->nmb->wnd; -+ BOOLEAN recognized = TRUE, queue_handler = TRUE; -+ -+ TRACE6("%p", wnd); -+ /* kernel may call ISR when registering interrupt, in -+ * the same context if DEBUG_SHIRQ is enabled */ -+ assert_irql(_irql_ == DIRQL || _irql_ == PASSIVE_LEVEL); -+ if (mp_interrupt->shared) -+ LIN2WIN3(mp_interrupt->isr, &recognized, &queue_handler, -+ wnd->nmb->mp_ctx); -+ else { -+ struct miniport *mp; -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ LIN2WIN1(mp->disable_interrupt, wnd->nmb->mp_ctx); -+ /* it is not shared interrupt, so handler must be called */ -+ recognized = queue_handler = TRUE; -+ } -+ if (recognized) { -+ if (queue_handler) { -+ TRACE5("%p", &wnd->irq_kdpc); -+ queue_kdpc(&wnd->irq_kdpc); -+ } -+ EXIT6(return TRUE); -+ } -+ EXIT6(return FALSE); -+} -+WIN_FUNC_DECL(ndis_isr,2) -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterInterrupt,7) -+ (struct ndis_mp_interrupt *mp_interrupt, -+ struct ndis_mp_block *nmb, UINT vector, UINT level, -+ BOOLEAN req_isr, BOOLEAN shared, enum kinterrupt_mode mode) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ struct miniport *mp; -+ -+ ENTER1("%p, vector:%d, level:%d, req_isr:%d, shared:%d, mode:%d", -+ mp_interrupt, vector, level, req_isr, shared, mode); -+ -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ nt_spin_lock_init(&mp_interrupt->lock); -+ mp_interrupt->irq = vector; -+ mp_interrupt->isr = mp->isr; -+ mp_interrupt->mp_dpc = mp->handle_interrupt; -+ mp_interrupt->nmb = nmb; -+ mp_interrupt->req_isr = req_isr; -+ if (shared && !req_isr) -+ WARNING("shared but dynamic interrupt!"); -+ mp_interrupt->shared = shared; -+ wnd->mp_interrupt = mp_interrupt; -+ if (mp->enable_interrupt) -+ mp_interrupt->enable = TRUE; -+ else -+ mp_interrupt->enable = FALSE; -+ -+ if (deserialized_driver(wnd)) { -+ KeInitializeDpc(&wnd->irq_kdpc, -+ WIN_FUNC_PTR(deserialized_irq_handler,4), -+ nmb->wnd); -+ wnd->irq_kdpc.arg1 = mp->handle_interrupt; -+ wnd->irq_kdpc.arg2 = mp; -+ TRACE2("%p, %p, %p, %p", wnd->irq_kdpc.arg1, wnd->irq_kdpc.arg2, -+ nmb->wnd, nmb->mp_ctx); -+ } else { -+ KeInitializeDpc(&wnd->irq_kdpc, -+ WIN_FUNC_PTR(serialized_irq_handler,4), -+ nmb->wnd); -+ wnd->irq_kdpc.arg1 = mp->handle_interrupt; -+ wnd->irq_kdpc.arg2 = nmb->mp_ctx; -+ TRACE2("%p, %p, %p", wnd->irq_kdpc.arg1, wnd->irq_kdpc.arg2, -+ nmb->wnd); -+ } -+ -+ if (IoConnectInterrupt(&mp_interrupt->kinterrupt, -+ WIN_FUNC_PTR(ndis_isr,2), mp_interrupt, NULL, -+ vector, DIRQL, DIRQL, mode, shared, 0, FALSE) != -+ STATUS_SUCCESS) { -+ printk(KERN_WARNING "%s: request for IRQ %d failed\n", -+ DRIVER_NAME, vector); -+ return NDIS_STATUS_RESOURCES; -+ } -+ printk(KERN_INFO "%s: using IRQ %d\n", DRIVER_NAME, vector); -+ EXIT1(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(NdisMDeregisterInterrupt,1) -+ (struct ndis_mp_interrupt *mp_interrupt) -+{ -+ struct ndis_mp_block *nmb; -+ -+ ENTER1("%p", mp_interrupt); -+ nmb = xchg(&mp_interrupt->nmb, NULL); -+ TRACE1("%p", nmb); -+ if (!nmb) { -+ WARNING("interrupt already freed?"); -+ return; -+ } -+ nmb->wnd->mp_interrupt = NULL; -+ if (dequeue_kdpc(&nmb->wnd->irq_kdpc)) -+ TRACE2("interrupt kdpc was pending"); -+ flush_workqueue(wrapndis_wq); -+ IoDisconnectInterrupt(mp_interrupt->kinterrupt); -+ EXIT1(return); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(NdisMSynchronizeWithInterrupt,3) -+ (struct ndis_mp_interrupt *mp_interrupt, -+ PKSYNCHRONIZE_ROUTINE sync_func, void *ctx) -+{ -+ return KeSynchronizeExecution(mp_interrupt->kinterrupt, sync_func, ctx); -+} -+ -+/* called via function pointer; but 64-bit RNDIS driver calls directly */ -+wstdcall void WIN_FUNC(NdisMIndicateStatus,4) -+ (struct ndis_mp_block *nmb, NDIS_STATUS status, void *buf, UINT len) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ struct ndis_status_indication *si; -+ -+ ENTER2("status=0x%x len=%d", status, len); -+ switch (status) { -+ case NDIS_STATUS_MEDIA_CONNECT: -+ set_media_state(wnd, NdisMediaStateConnected); -+ break; -+ case NDIS_STATUS_MEDIA_DISCONNECT: -+ set_media_state(wnd, NdisMediaStateDisconnected); -+ break; -+ case NDIS_STATUS_MEDIA_SPECIFIC_INDICATION: -+ if (!buf) -+ break; -+ si = buf; -+ TRACE2("status_type=%d", si->status_type); -+ switch (si->status_type) { -+ case Ndis802_11StatusType_MediaStreamMode: -+ break; -+#ifdef CONFIG_WIRELESS_EXT -+ case Ndis802_11StatusType_Authentication: -+ buf = (char *)buf + sizeof(*si); -+ len -= sizeof(*si); -+ while (len > 0) { -+ int pairwise_error = 0, group_error = 0; -+ struct ndis_auth_req *auth_req = -+ (struct ndis_auth_req *)buf; -+ TRACE1(MACSTRSEP, MAC2STR(auth_req->bssid)); -+ if (auth_req->flags & 0x01) -+ TRACE2("reauth request"); -+ if (auth_req->flags & 0x02) -+ TRACE2("key update request"); -+ if (auth_req->flags & 0x06) { -+ pairwise_error = 1; -+ TRACE2("pairwise_error"); -+ } -+ if (auth_req->flags & 0x0E) { -+ group_error = 1; -+ TRACE2("group_error"); -+ } -+ if (pairwise_error || group_error) { -+ union iwreq_data wrqu; -+ struct iw_michaelmicfailure micfailure; -+ -+ memset(&micfailure, 0, sizeof(micfailure)); -+ if (pairwise_error) -+ micfailure.flags |= -+ IW_MICFAILURE_PAIRWISE; -+ if (group_error) -+ micfailure.flags |= -+ IW_MICFAILURE_GROUP; -+ memcpy(micfailure.src_addr.sa_data, -+ auth_req->bssid, ETH_ALEN); -+ memset(&wrqu, 0, sizeof(wrqu)); -+ wrqu.data.length = sizeof(micfailure); -+ wireless_send_event(wnd->net_dev, -+ IWEVMICHAELMICFAILURE, -+ &wrqu, (u8 *)&micfailure); -+ } -+ len -= auth_req->length; -+ buf = (char *)buf + auth_req->length; -+ } -+ break; -+ case Ndis802_11StatusType_PMKID_CandidateList: -+ { -+ u8 *end; -+ unsigned long i; -+ struct ndis_pmkid_candidate_list *cand; -+ -+ cand = buf + sizeof(struct ndis_status_indication); -+ if (len < sizeof(struct ndis_status_indication) + -+ sizeof(struct ndis_pmkid_candidate_list) || -+ cand->version != 1) { -+ WARNING("unrecognized PMKID ignored"); -+ EXIT1(return); -+ } -+ -+ end = (u8 *)buf + len; -+ TRACE2("PMKID ver %d num_cand %d", -+ cand->version, cand->num_candidates); -+ for (i = 0; i < cand->num_candidates; i++) { -+ struct iw_pmkid_cand pcand; -+ union iwreq_data wrqu; -+ struct ndis_pmkid_candidate *c = -+ &cand->candidates[i]; -+ if ((u8 *)(c + 1) > end) { -+ TRACE2("truncated PMKID"); -+ break; -+ } -+ TRACE2("%ld: " MACSTRSEP " 0x%x", -+ i, MAC2STR(c->bssid), c->flags); -+ memset(&pcand, 0, sizeof(pcand)); -+ if (c->flags & 0x01) -+ pcand.flags |= IW_PMKID_CAND_PREAUTH; -+ pcand.index = i; -+ memcpy(pcand.bssid.sa_data, c->bssid, ETH_ALEN); -+ -+ memset(&wrqu, 0, sizeof(wrqu)); -+ wrqu.data.length = sizeof(pcand); -+ wireless_send_event(wnd->net_dev, IWEVPMKIDCAND, -+ &wrqu, (u8 *)&pcand); -+ } -+ break; -+ } -+ case Ndis802_11StatusType_RadioState: -+ { -+ struct ndis_radio_status_indication *radio_status = buf; -+ if (radio_status->radio_state == -+ Ndis802_11RadioStatusOn) -+ INFO("radio is turned on"); -+ else if (radio_status->radio_state == -+ Ndis802_11RadioStatusHardwareOff) -+ INFO("radio is turned off by hardware"); -+ else if (radio_status->radio_state == -+ Ndis802_11RadioStatusSoftwareOff) -+ INFO("radio is turned off by software"); -+ break; -+ } -+#endif -+ default: -+ /* is this RSSI indication? */ -+ TRACE2("unknown indication: %x", si->status_type); -+ break; -+ } -+ break; -+ default: -+ TRACE2("unknown status: %08X", status); -+ break; -+ } -+ -+ EXIT2(return); -+} -+ -+/* called via function pointer; but 64-bit RNDIS driver calls directly */ -+wstdcall void WIN_FUNC(NdisMIndicateStatusComplete,1) -+ (struct ndis_mp_block *nmb) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ ENTER2("%p", wnd); -+ if (wnd->tx_ok) -+ queue_work(wrapndis_wq, &wnd->tx_work); -+} -+ -+/* called via function pointer */ -+wstdcall void NdisMSendComplete(struct ndis_mp_block *nmb, -+ struct ndis_packet *packet, NDIS_STATUS status) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ ENTER4("%p, %08X", packet, status); -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ if (deserialized_driver(wnd)) -+ free_tx_packet(wnd, packet, status); -+ else { -+ struct ndis_packet_oob_data *oob_data; -+ NDIS_STATUS pkt_status; -+ TRACE3("%p, %08x", packet, status); -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ switch ((pkt_status = xchg(&oob_data->status, status))) { -+ case NDIS_STATUS_NOT_RECOGNIZED: -+ free_tx_packet(wnd, packet, status); -+ break; -+ case NDIS_STATUS_PENDING: -+ case 0: -+ break; -+ default: -+ WARNING("%p: invalid status: %08X", packet, pkt_status); -+ break; -+ } -+ /* In case a serialized driver has earlier requested a -+ * pause by returning NDIS_STATUS_RESOURCES during -+ * MiniportSend(Packets), wakeup tx worker now. -+ */ -+ if (xchg(&wnd->tx_ok, 1) == 0) { -+ TRACE3("%d, %d", wnd->tx_ring_start, wnd->tx_ring_end); -+ queue_work(wrapndis_wq, &wnd->tx_work); -+ } -+ } -+ EXIT3(return); -+} -+ -+/* called via function pointer */ -+wstdcall void NdisMSendResourcesAvailable(struct ndis_mp_block *nmb) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ ENTER3("%d, %d", wnd->tx_ring_start, wnd->tx_ring_end); -+ wnd->tx_ok = 1; -+ queue_work(wrapndis_wq, &wnd->tx_work); -+ EXIT3(return); -+} -+ -+wstdcall void return_packet(void *arg1, void *arg2) -+{ -+ struct ndis_device *wnd; -+ struct ndis_packet *packet; -+ struct miniport *mp; -+ KIRQL irql; -+ -+ wnd = arg1; -+ packet = arg2; -+ ENTER4("%p, %p", wnd, packet); -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ irql = serialize_lock_irql(wnd); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ LIN2WIN2(mp->return_packet, wnd->nmb->mp_ctx, packet); -+ serialize_unlock_irql(wnd, irql); -+ EXIT4(return); -+} -+WIN_FUNC_DECL(return_packet,2) -+ -+/* called via function pointer */ -+wstdcall void NdisMIndicateReceivePacket(struct ndis_mp_block *nmb, -+ struct ndis_packet **packets, -+ UINT nr_packets) -+{ -+ struct ndis_device *wnd; -+ ndis_buffer *buffer; -+ struct ndis_packet *packet; -+ struct sk_buff *skb; -+ ULONG i, length, total_length; -+ struct ndis_packet_oob_data *oob_data; -+ void *virt; -+ struct ndis_tcp_ip_checksum_packet_info csum; -+ -+ ENTER3("%p, %d", nmb, nr_packets); -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ wnd = nmb->wnd; -+ for (i = 0; i < nr_packets; i++) { -+ packet = packets[i]; -+ if (!packet) { -+ WARNING("empty packet ignored"); -+ continue; -+ } -+ wnd->net_dev->last_rx = jiffies; -+ /* get total number of bytes in packet */ -+ NdisGetFirstBufferFromPacketSafe(packet, &buffer, &virt, -+ &length, &total_length, -+ NormalPagePriority); -+ TRACE3("%d, %d", length, total_length); -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ TRACE3("0x%x, 0x%x, %llu", packet->private.flags, -+ packet->private.packet_flags, oob_data->time_rxed); -+ skb = dev_alloc_skb(total_length); -+ if (skb) { -+ while (buffer) { -+ memcpy_skb(skb, MmGetSystemAddressForMdl(buffer), -+ MmGetMdlByteCount(buffer)); -+ buffer = buffer->next; -+ } -+ skb->dev = wnd->net_dev; -+ skb->protocol = eth_type_trans(skb, wnd->net_dev); -+ pre_atomic_add(wnd->net_stats.rx_bytes, total_length); -+ atomic_inc_var(wnd->net_stats.rx_packets); -+ csum.value = (typeof(csum.value))(ULONG_PTR) -+ oob_data->ext.info[TcpIpChecksumPacketInfo]; -+ TRACE3("0x%05x", csum.value); -+ if (wnd->rx_csum.value && -+ (csum.rx.tcp_succeeded || csum.rx.udp_succeeded || -+ csum.rx.ip_succeeded)) -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ else -+ skb->ip_summed = CHECKSUM_NONE; -+ -+ if (in_interrupt()) -+ netif_rx(skb); -+ else -+ netif_rx_ni(skb); -+ } else { -+ WARNING("couldn't allocate skb; packet dropped"); -+ atomic_inc_var(wnd->net_stats.rx_dropped); -+ } -+ -+ /* serialized drivers check the status upon return -+ * from this function */ -+ if (!deserialized_driver(wnd)) { -+ oob_data->status = NDIS_STATUS_SUCCESS; -+ continue; -+ } -+ -+ /* if a deserialized driver sets -+ * NDIS_STATUS_RESOURCES, then it reclaims the packet -+ * upon return from this function */ -+ if (oob_data->status == NDIS_STATUS_RESOURCES) -+ continue; -+ -+ assert(oob_data->status == NDIS_STATUS_SUCCESS); -+ /* deserialized driver doesn't check the status upon -+ * return from this function; we need to call -+ * MiniportReturnPacket later for this packet. Calling -+ * MiniportReturnPacket from here is not correct - the -+ * driver doesn't expect it (at least Centrino driver -+ * crashes) */ -+ schedule_ntos_work_item(WIN_FUNC_PTR(return_packet,2), -+ wnd, packet); -+ } -+ EXIT3(return); -+} -+ -+/* called via function pointer (by NdisMEthIndicateReceive macro); the -+ * first argument is nmb->eth_db */ -+wstdcall void EthRxIndicateHandler(struct ndis_mp_block *nmb, void *rx_ctx, -+ char *header1, char *header, UINT header_size, -+ void *look_ahead, UINT look_ahead_size, -+ UINT packet_size) -+{ -+ struct sk_buff *skb = NULL; -+ struct ndis_device *wnd; -+ unsigned int skb_size = 0; -+ KIRQL irql; -+ struct ndis_packet_oob_data *oob_data; -+ -+ ENTER3("nmb = %p, rx_ctx = %p, buf = %p, size = %d, buf = %p, " -+ "size = %d, packet = %d", nmb, rx_ctx, header, header_size, -+ look_ahead, look_ahead_size, packet_size); -+ -+ wnd = nmb->wnd; -+ TRACE3("wnd = %p", wnd); -+ if (!wnd) { -+ ERROR("nmb is NULL"); -+ EXIT3(return); -+ } -+ wnd->net_dev->last_rx = jiffies; -+ -+ if (look_ahead_size < packet_size) { -+ struct ndis_packet *packet; -+ struct miniport *mp; -+ unsigned int bytes_txed; -+ NDIS_STATUS res; -+ -+ NdisAllocatePacket(&res, &packet, wnd->tx_packet_pool); -+ if (res != NDIS_STATUS_SUCCESS) { -+ atomic_inc_var(wnd->net_stats.rx_dropped); -+ EXIT3(return); -+ } -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ irql = serialize_lock_irql(wnd); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ res = LIN2WIN6(mp->tx_data, packet, &bytes_txed, nmb, -+ rx_ctx, look_ahead_size, packet_size); -+ serialize_unlock_irql(wnd, irql); -+ TRACE3("%d, %d, %d", header_size, look_ahead_size, bytes_txed); -+ if (res == NDIS_STATUS_SUCCESS) { -+ ndis_buffer *buffer; -+ struct ndis_tcp_ip_checksum_packet_info csum; -+ skb = dev_alloc_skb(header_size + look_ahead_size + -+ bytes_txed); -+ if (!skb) { -+ ERROR("couldn't allocate skb; packet dropped"); -+ atomic_inc_var(wnd->net_stats.rx_dropped); -+ NdisFreePacket(packet); -+ return; -+ } -+ memcpy_skb(skb, header, header_size); -+ memcpy_skb(skb, look_ahead, look_ahead_size); -+ buffer = packet->private.buffer_head; -+ while (buffer) { -+ memcpy_skb(skb, -+ MmGetSystemAddressForMdl(buffer), -+ MmGetMdlByteCount(buffer)); -+ buffer = buffer->next; -+ } -+ skb_size = header_size + look_ahead_size + bytes_txed; -+ csum.value = (typeof(csum.value))(ULONG_PTR) -+ oob_data->ext.info[TcpIpChecksumPacketInfo]; -+ TRACE3("0x%05x", csum.value); -+ if (wnd->rx_csum.value && -+ (csum.rx.tcp_succeeded || csum.rx.udp_succeeded)) -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ else -+ skb->ip_summed = CHECKSUM_NONE; -+ NdisFreePacket(packet); -+ } else if (res == NDIS_STATUS_PENDING) { -+ /* driver will call td_complete */ -+ oob_data->look_ahead = kmalloc(look_ahead_size, -+ GFP_ATOMIC); -+ if (!oob_data->look_ahead) { -+ NdisFreePacket(packet); -+ ERROR("packet dropped"); -+ atomic_inc_var(wnd->net_stats.rx_dropped); -+ EXIT3(return); -+ } -+ assert(sizeof(oob_data->header) == header_size); -+ memcpy(oob_data->header, header, -+ sizeof(oob_data->header)); -+ memcpy(oob_data->look_ahead, look_ahead, -+ look_ahead_size); -+ oob_data->look_ahead_size = look_ahead_size; -+ EXIT3(return); -+ } else { -+ WARNING("packet dropped: %08X", res); -+ atomic_inc_var(wnd->net_stats.rx_dropped); -+ NdisFreePacket(packet); -+ EXIT3(return); -+ } -+ } else { -+ skb_size = header_size + packet_size; -+ skb = dev_alloc_skb(skb_size); -+ if (skb) { -+ memcpy_skb(skb, header, header_size); -+ memcpy_skb(skb, look_ahead, packet_size); -+ } -+ } -+ -+ if (skb) { -+ skb->dev = wnd->net_dev; -+ skb->protocol = eth_type_trans(skb, wnd->net_dev); -+ pre_atomic_add(wnd->net_stats.rx_bytes, skb_size); -+ atomic_inc_var(wnd->net_stats.rx_packets); -+ if (in_interrupt()) -+ netif_rx(skb); -+ else -+ netif_rx_ni(skb); -+ } -+ -+ EXIT3(return); -+} -+ -+/* called via function pointer */ -+wstdcall void NdisMTransferDataComplete(struct ndis_mp_block *nmb, -+ struct ndis_packet *packet, -+ NDIS_STATUS status, UINT bytes_txed) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ struct sk_buff *skb; -+ unsigned int skb_size; -+ struct ndis_packet_oob_data *oob_data; -+ ndis_buffer *buffer; -+ struct ndis_tcp_ip_checksum_packet_info csum; -+ -+ ENTER3("wnd = %p, packet = %p, bytes_txed = %d", -+ wnd, packet, bytes_txed); -+ if (!packet) { -+ WARNING("illegal packet"); -+ EXIT3(return); -+ } -+ wnd->net_dev->last_rx = jiffies; -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ skb_size = sizeof(oob_data->header) + oob_data->look_ahead_size + -+ bytes_txed; -+ skb = dev_alloc_skb(skb_size); -+ if (!skb) { -+ kfree(oob_data->look_ahead); -+ NdisFreePacket(packet); -+ ERROR("couldn't allocate skb; packet dropped"); -+ atomic_inc_var(wnd->net_stats.rx_dropped); -+ EXIT3(return); -+ } -+ memcpy_skb(skb, oob_data->header, sizeof(oob_data->header)); -+ memcpy_skb(skb, oob_data->look_ahead, oob_data->look_ahead_size); -+ buffer = packet->private.buffer_head; -+ while (buffer) { -+ memcpy_skb(skb, MmGetSystemAddressForMdl(buffer), -+ MmGetMdlByteCount(buffer)); -+ buffer = buffer->next; -+ } -+ kfree(oob_data->look_ahead); -+ NdisFreePacket(packet); -+ skb->dev = wnd->net_dev; -+ skb->protocol = eth_type_trans(skb, wnd->net_dev); -+ pre_atomic_add(wnd->net_stats.rx_bytes, skb_size); -+ atomic_inc_var(wnd->net_stats.rx_packets); -+ -+ csum.value = (typeof(csum.value))(ULONG_PTR) -+ oob_data->ext.info[TcpIpChecksumPacketInfo]; -+ TRACE3("0x%05x", csum.value); -+ if (wnd->rx_csum.value && -+ (csum.rx.tcp_succeeded || csum.rx.udp_succeeded)) -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ else -+ skb->ip_summed = CHECKSUM_NONE; -+ -+ if (in_interrupt()) -+ netif_rx(skb); -+ else -+ netif_rx_ni(skb); -+} -+ -+/* called via function pointer */ -+wstdcall void EthRxComplete(struct ndis_mp_block *nmb) -+{ -+ TRACE3(""); -+} -+ -+/* called via function pointer */ -+wstdcall void NdisMQueryInformationComplete(struct ndis_mp_block *nmb, -+ NDIS_STATUS status) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ typeof(wnd->ndis_req_task) task; -+ -+ ENTER2("nmb: %p, wnd: %p, %08X", nmb, wnd, status); -+ wnd->ndis_req_status = status; -+ wnd->ndis_req_done = 1; -+ if ((task = xchg(&wnd->ndis_req_task, NULL))) -+ wake_up_process(task); -+ else -+ WARNING("invalid task"); -+ EXIT2(return); -+} -+ -+/* called via function pointer */ -+wstdcall void NdisMSetInformationComplete(struct ndis_mp_block *nmb, -+ NDIS_STATUS status) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ typeof(wnd->ndis_req_task) task; -+ -+ ENTER2("status = %08X", status); -+ wnd->ndis_req_status = status; -+ wnd->ndis_req_done = 1; -+ if ((task = xchg(&wnd->ndis_req_task, NULL))) -+ wake_up_process(task); -+ else -+ WARNING("invalid task"); -+ EXIT2(return); -+} -+ -+/* called via function pointer */ -+wstdcall void NdisMResetComplete(struct ndis_mp_block *nmb, -+ NDIS_STATUS status, BOOLEAN address_reset) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ typeof(wnd->ndis_req_task) task; -+ -+ ENTER2("status: %08X, %u", status, address_reset); -+ wnd->ndis_req_status = status; -+ wnd->ndis_req_done = address_reset + 1; -+ if ((task = xchg(&wnd->ndis_req_task, NULL))) -+ wake_up_process(task); -+ else -+ WARNING("invalid task"); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMSleep,1) -+ (ULONG us) -+{ -+ unsigned long delay; -+ -+ ENTER4("%p: us: %u", current, us); -+ delay = USEC_TO_HZ(us); -+ sleep_hz(delay); -+ TRACE4("%p: done", current); -+} -+ -+wstdcall void WIN_FUNC(NdisGetCurrentSystemTime,1) -+ (LARGE_INTEGER *time) -+{ -+ *time = ticks_1601(); -+ TRACE5("%llu, %lu", *time, jiffies); -+} -+ -+wstdcall LONG WIN_FUNC(NdisInterlockedDecrement,1) -+ (LONG *val) -+{ -+ return InterlockedDecrement(val); -+} -+ -+wstdcall LONG WIN_FUNC(NdisInterlockedIncrement,1) -+ (LONG *val) -+{ -+ return InterlockedIncrement(val); -+} -+ -+wstdcall struct nt_list *WIN_FUNC(NdisInterlockedInsertHeadList,3) -+ (struct nt_list *head, struct nt_list *entry, -+ struct ndis_spinlock *lock) -+{ -+ return ExInterlockedInsertHeadList(head, entry, &lock->klock); -+} -+ -+wstdcall struct nt_list *WIN_FUNC(NdisInterlockedInsertTailList,3) -+ (struct nt_list *head, struct nt_list *entry, -+ struct ndis_spinlock *lock) -+{ -+ return ExInterlockedInsertTailList(head, entry, &lock->klock); -+} -+ -+wstdcall struct nt_list *WIN_FUNC(NdisInterlockedRemoveHeadList,2) -+ (struct nt_list *head, struct ndis_spinlock *lock) -+{ -+ return ExInterlockedRemoveHeadList(head, &lock->klock); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMInitializeScatterGatherDma,3) -+ (struct ndis_mp_block *nmb, BOOLEAN dma64_supported, ULONG max_phy_map) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ ENTER2("dma64_supported=%d, maxtransfer=%u", dma64_supported, -+ max_phy_map); -+ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ ERROR("used on a non-PCI device"); -+ return NDIS_STATUS_NOT_SUPPORTED; -+ } -+#ifdef CONFIG_X86_64 -+ if (!dma64_supported) { -+ TRACE1("64-bit DMA size is not supported"); -+ if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(32)) || -+ pci_set_consistent_dma_mask(wnd->wd->pci.pdev, -+ DMA_BIT_MASK(32))) -+ WARNING("setting dma mask failed"); -+ } -+#endif -+ if ((wnd->attributes & NDIS_ATTRIBUTE_BUS_MASTER) && -+ wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ wnd->sg_dma_size = max_phy_map; -+ return NDIS_STATUS_SUCCESS; -+ } else -+ EXIT1(return NDIS_STATUS_NOT_SUPPORTED); -+} -+ -+wstdcall ULONG WIN_FUNC(NdisMGetDmaAlignment,1) -+ (struct ndis_mp_block *nmb) -+{ -+ ENTER3(""); -+ return dma_get_cache_alignment(); -+} -+ -+wstdcall CHAR WIN_FUNC(NdisSystemProcessorCount,0) -+ (void) -+{ -+ return num_online_cpus(); -+} -+ -+wstdcall void WIN_FUNC(NdisGetCurrentProcessorCounts,3) -+ (ULONG *idle, ULONG *kernel_user, ULONG *index) -+{ -+ int cpu = smp_processor_id(); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) -+ *idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; -+ *kernel_user = kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM] + -+ kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; -+#else -+ *idle = kstat_cpu(cpu).cpustat.idle; -+ *kernel_user = kstat_cpu(cpu).cpustat.system + -+ kstat_cpu(cpu).cpustat.user; -+#endif -+ *index = cpu; -+} -+ -+wstdcall void WIN_FUNC(NdisInitializeEvent,1) -+ (struct ndis_event *ndis_event) -+{ -+ EVENTENTER("%p", ndis_event); -+ KeInitializeEvent(&ndis_event->nt_event, NotificationEvent, 0); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(NdisWaitEvent,2) -+ (struct ndis_event *ndis_event, UINT ms) -+{ -+ LARGE_INTEGER ticks; -+ NTSTATUS res; -+ -+ EVENTENTER("%p %u", ndis_event, ms); -+ ticks = -((LARGE_INTEGER)ms * TICKSPERMSEC); -+ res = KeWaitForSingleObject(&ndis_event->nt_event, 0, 0, TRUE, -+ ms == 0 ? NULL : &ticks); -+ if (res == STATUS_SUCCESS) -+ EXIT3(return TRUE); -+ else -+ EXIT3(return FALSE); -+} -+ -+wstdcall void WIN_FUNC(NdisSetEvent,1) -+ (struct ndis_event *ndis_event) -+{ -+ EVENTENTER("%p", ndis_event); -+ KeSetEvent(&ndis_event->nt_event, 0, 0); -+} -+ -+wstdcall void WIN_FUNC(NdisResetEvent,1) -+ (struct ndis_event *ndis_event) -+{ -+ EVENTENTER("%p", ndis_event); -+ KeResetEvent(&ndis_event->nt_event); -+} -+ -+static void ndis_worker(struct work_struct *dummy) -+{ -+ struct nt_list *ent; -+ struct ndis_work_item *ndis_work_item; -+ -+ WORKENTER(""); -+ while (1) { -+ spin_lock_bh(&ndis_work_list_lock); -+ ent = RemoveHeadList(&ndis_work_list); -+ spin_unlock_bh(&ndis_work_list_lock); -+ if (!ent) -+ break; -+ ndis_work_item = container_of(ent, struct ndis_work_item, list); -+ WORKTRACE("%p: %p, %p", ndis_work_item, -+ ndis_work_item->func, ndis_work_item->ctx); -+ LIN2WIN2(ndis_work_item->func, ndis_work_item, -+ ndis_work_item->ctx); -+ WORKTRACE("%p done", ndis_work_item); -+ } -+ WORKEXIT(return); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisScheduleWorkItem,1) -+ (struct ndis_work_item *ndis_work_item) -+{ -+ ENTER3("%p", ndis_work_item); -+ spin_lock_bh(&ndis_work_list_lock); -+ InsertTailList(&ndis_work_list, &ndis_work_item->list); -+ spin_unlock_bh(&ndis_work_list_lock); -+ WORKTRACE("scheduling %p", ndis_work_item); -+ queue_work(ndis_wq, &ndis_work); -+ EXIT3(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(NdisMGetDeviceProperty,6) -+ (struct ndis_mp_block *nmb, void **phy_dev, void **func_dev, -+ void **next_dev, void **alloc_res, void**trans_res) -+{ -+ ENTER2("nmb: %p, phy_dev = %p, func_dev = %p, next_dev = %p, " -+ "alloc_res = %p, trans_res = %p", nmb, phy_dev, func_dev, -+ next_dev, alloc_res, trans_res); -+ if (phy_dev) -+ *phy_dev = nmb->pdo; -+ if (func_dev) -+ *func_dev = nmb->fdo; -+ if (next_dev) -+ *next_dev = nmb->next_device; -+} -+ -+wstdcall void WIN_FUNC(NdisMRegisterUnloadHandler,2) -+ (struct driver_object *drv_obj, void *unload) -+{ -+ if (drv_obj) -+ drv_obj->unload = unload; -+ return; -+} -+ -+wstdcall UINT WIN_FUNC(NdisGetVersion,0) -+ (void) -+{ -+ return 0x00050001; -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMQueryAdapterInstanceName,2) -+ (struct unicode_string *name, struct ndis_mp_block *nmb) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ struct ansi_string ansi; -+ -+ if (wrap_is_pci_bus(wnd->wd->dev_bus)) -+ RtlInitAnsiString(&ansi, "PCI Ethernet Adapter"); -+ else -+ RtlInitAnsiString(&ansi, "USB Ethernet Adapter"); -+ -+ if (RtlAnsiStringToUnicodeString(name, &ansi, TRUE)) -+ EXIT2(return NDIS_STATUS_RESOURCES); -+ else -+ EXIT2(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisWriteEventLogEntry,7) -+ (void *handle, NDIS_STATUS code, ULONG value, USHORT n, -+ void *strings, ULONG datasize, void *data) -+{ -+ TRACE1("0x%x, 0x%x, %u, %u", code, value, n, datasize); -+ return NDIS_STATUS_SUCCESS; -+} -+ -+wstdcall void *WIN_FUNC(NdisGetRoutineAddress,1) -+ (struct unicode_string *unicode_string) -+{ -+ struct ansi_string ansi_string; -+ void *address; -+ -+ if (RtlUnicodeStringToAnsiString(&ansi_string, unicode_string, TRUE) != -+ STATUS_SUCCESS) -+ EXIT1(return NULL); -+ INFO("%s", ansi_string.buf); -+ address = ndis_get_routine_address(ansi_string.buf); -+ RtlFreeAnsiString(&ansi_string); -+ return address; -+} -+ -+wstdcall ULONG WIN_FUNC(NdisReadPcmciaAttributeMemory,4) -+ (struct ndis_mp_block *nmb, ULONG offset, void *buffer, -+ ULONG length) -+{ -+ TODO(); -+ return 0; -+} -+ -+wstdcall ULONG WIN_FUNC(NdisWritePcmciaAttributeMemory,4) -+ (struct ndis_mp_block *nmb, ULONG offset, void *buffer, -+ ULONG length) -+{ -+ TODO(); -+ return 0; -+} -+ -+wstdcall void WIN_FUNC(NdisMCoIndicateReceivePacket,3) -+ (struct ndis_mp_block *nmb, struct ndis_packet **packets, -+ UINT nr_packets) -+{ -+ ENTER3("nmb = %p", nmb); -+ NdisMIndicateReceivePacket(nmb, packets, nr_packets); -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMCoSendComplete,3) -+ (NDIS_STATUS status, struct ndis_mp_block *nmb, -+ struct ndis_packet *packet) -+{ -+ ENTER3("%08x", status); -+ NdisMSendComplete(nmb, packet, status); -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(NdisMCoRequestComplete,3) -+ (NDIS_STATUS status, struct ndis_mp_block *nmb, -+ struct ndis_request *ndis_request) -+{ -+ struct ndis_device *wnd = nmb->wnd; -+ typeof(wnd->ndis_req_task) task; -+ -+ ENTER3("%08X", status); -+ wnd->ndis_req_status = status; -+ wnd->ndis_req_done = 1; -+ if ((task = xchg(&wnd->ndis_req_task, NULL))) -+ wake_up_process(task); -+ else -+ WARNING("invalid task"); -+ EXIT3(return); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisIMNotifiyPnPEvent,2) -+ (struct ndis_mp_block *nmb, struct net_pnp_event *event) -+{ -+ ENTER2("%p, %d", nmb, event->code); -+ /* NdisWrapper never calls protocol's pnp event notifier, so -+ * nothing to do here */ -+ EXIT2(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(NdisCompletePnPEvent,3) -+ (NDIS_STATUS status, void *handle, struct net_pnp_event *event) -+{ -+ ENTER2("%d, %p, %d", status, handle, event->code); -+ /* NdisWrapper never calls protocol's pnp event notifier, so -+ * nothing to do here */ -+ EXIT2(return); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMSetMiniportSecondary,2) -+ (struct ndis_mp_block *nmb2, struct ndis_mp_block *nmb1) -+{ -+ ENTER3("%p, %p", nmb1, nmb2); -+ TODO(); -+ EXIT3(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMPromoteMiniport,1) -+ (struct ndis_mp_block *nmb) -+{ -+ ENTER3("%p", nmb); -+ TODO(); -+ EXIT3(return NDIS_STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(NdisMCoActivateVcComplete,3) -+ (NDIS_STATUS status, void *handle, void *params) -+{ -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(NdisMCoDeactivateVcComplete,2) -+ (NDIS_STATUS status, void *handle) -+{ -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(NdisMRemoveMiniport,1) -+ (void *handle) -+{ -+ TODO(); -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMOpenLog,3) -+ (struct ndis_device *wnd, UINT size, void *handle) -+{ -+ if (size >= sizeof(int)) -+ *((int *)handle) = 42; -+ return NDIS_STATUS_SUCCESS; -+} -+ -+wstdcall NDIS_STATUS WIN_FUNC(NdisMWriteLogData,3) -+ (void *handle, char *buffer, UINT buffer_size) -+{ -+ TODO(); -+ return NDIS_STATUS_SUCCESS; -+} -+ -+wstdcall void WIN_FUNC(NdisMFlushLog,1) -+ (void *handle) -+{ -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(NdisMCloseLog,1) -+ (void *handle) -+{ -+ TODO(); -+} -+ -+static void *ndis_get_routine_address(char *name) -+{ -+ int i; -+ ENTER2("%p", name); -+ for (i = 0; i < ARRAY_SIZE(ndis_exports); i++) { -+ if (strcmp(name, ndis_exports[i].name) == 0) { -+ TRACE2("%p", ndis_exports[i].func); -+ return ndis_exports[i].func; -+ } -+ } -+ EXIT2(return NULL); -+} -+ -+/* ndis_init_device is called for each device */ -+int ndis_init_device(struct ndis_device *wnd) -+{ -+ struct ndis_mp_block *nmb = wnd->nmb; -+ -+ KeInitializeSpinLock(&nmb->lock); -+ wnd->mp_interrupt = NULL; -+ wnd->wrap_timer_slist.next = NULL; -+ if (wnd->wd->driver->ndis_driver) -+ wnd->wd->driver->ndis_driver->mp.shutdown = NULL; -+ -+ nmb->filterdbs.eth_db = nmb; -+ nmb->filterdbs.tr_db = nmb; -+ nmb->filterdbs.fddi_db = nmb; -+ nmb->filterdbs.arc_db = nmb; -+ -+ nmb->rx_packet = WIN_FUNC_PTR(NdisMIndicateReceivePacket,3); -+ nmb->send_complete = WIN_FUNC_PTR(NdisMSendComplete,3); -+ nmb->send_resource_avail = WIN_FUNC_PTR(NdisMSendResourcesAvailable,1); -+ nmb->status = WIN_FUNC_PTR(NdisMIndicateStatus,4); -+ nmb->status_complete = WIN_FUNC_PTR(NdisMIndicateStatusComplete,1); -+ nmb->queryinfo_complete = WIN_FUNC_PTR(NdisMQueryInformationComplete,2); -+ nmb->setinfo_complete = WIN_FUNC_PTR(NdisMSetInformationComplete,2); -+ nmb->reset_complete = WIN_FUNC_PTR(NdisMResetComplete,3); -+ nmb->eth_rx_indicate = WIN_FUNC_PTR(EthRxIndicateHandler,8); -+ nmb->eth_rx_complete = WIN_FUNC_PTR(EthRxComplete,1); -+ nmb->td_complete = WIN_FUNC_PTR(NdisMTransferDataComplete,4); -+ return 0; -+} -+ -+/* ndis_exit_device is called for each device */ -+void ndis_exit_device(struct ndis_device *wnd) -+{ -+ struct wrap_device_setting *setting; -+ ENTER2("%p", wnd); -+ mutex_lock(&loader_mutex); -+ nt_list_for_each_entry(setting, &wnd->wd->settings, list) { -+ struct ndis_configuration_parameter *param; -+ param = setting->encoded; -+ if (param) { -+ if (param->type == NdisParameterString) -+ RtlFreeUnicodeString(¶m->data.string); -+ ExFreePool(param); -+ setting->encoded = NULL; -+ } -+ } -+ mutex_unlock(&loader_mutex); -+} -+ -+/* ndis_init is called once when module is loaded */ -+int ndis_init(void) -+{ -+ InitializeListHead(&ndis_work_list); -+ spin_lock_init(&ndis_work_list_lock); -+ INIT_WORK(&ndis_work, ndis_worker); -+ -+ ndis_wq = create_singlethread_workqueue("ndis_wq"); -+ if (!ndis_wq) { -+ WARNING("couldn't create worker thread"); -+ EXIT1(return -ENOMEM); -+ } -+ -+ TRACE1("ndis_wq: %p", ndis_wq); -+ return 0; -+} -+ -+/* ndis_exit is called once when module is removed */ -+void ndis_exit(void) -+{ -+ ENTER1(""); -+ if (ndis_wq) -+ destroy_workqueue(ndis_wq); -+ EXIT1(return); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.h ---- linux-3.12.2/3rdparty/ndiswrapper/ndis.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndis.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,1309 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _NDIS_H_ -+#define _NDIS_H_ -+ -+#include "ntoskernel.h" -+ -+//#define ALLOW_POOL_OVERFLOW 1 -+ -+#define NDIS_DMA_24BITS 0 -+#define NDIS_DMA_32BITS 1 -+#define NDIS_DMA_64BITS 2 -+ -+#ifdef CONFIG_X86_64 -+#define MAXIMUM_PROCESSORS 64 -+#else -+#define MAXIMUM_PROCESSORS 32 -+#endif -+ -+typedef UINT NDIS_STATUS; -+typedef UCHAR NDIS_DMA_SIZE; -+typedef LONG ndis_rssi; -+typedef ULONG ndis_key_index; -+typedef ULONG ndis_tx_power_level; -+typedef ULONGULONG ndis_key_rsc; -+typedef UCHAR mac_address[ETH_ALEN]; -+typedef ULONG ndis_fragmentation_threshold; -+typedef ULONG ndis_rts_threshold; -+typedef ULONG ndis_antenna; -+typedef ULONG ndis_oid; -+ -+typedef uint64_t NDIS_PHY_ADDRESS; -+ -+struct ndis_sg_element { -+ PHYSICAL_ADDRESS address; -+ ULONG length; -+ ULONG_PTR reserved; -+}; -+ -+struct ndis_sg_list { -+ ULONG nent; -+ ULONG_PTR reserved; -+ struct ndis_sg_element elements[]; -+}; -+ -+/* when sending packets, ndiswrapper associates exactly one sg element -+ * in sg list */ -+struct wrap_tx_sg_list { -+ ULONG nent; -+ ULONG_PTR reserved; -+ struct ndis_sg_element elements[1]; -+}; -+ -+struct ndis_phy_addr_unit { -+ NDIS_PHY_ADDRESS phy_addr; -+ UINT length; -+}; -+ -+typedef struct mdl ndis_buffer; -+ -+struct ndis_buffer_pool { -+ ndis_buffer *free_descr; -+// NT_SPIN_LOCK lock; -+ spinlock_t lock; -+ UINT max_descr; -+ UINT num_allocated_descr; -+}; -+ -+#define NDIS_PROTOCOL_ID_DEFAULT 0x00 -+#define NDIS_PROTOCOL_ID_TCP_IP 0x02 -+#define NDIS_PROTOCOL_ID_IPX 0x06 -+#define NDIS_PROTOCOL_ID_NBF 0x07 -+#define NDIS_PROTOCOL_ID_MAX 0x0F -+#define NDIS_PROTOCOL_ID_MASK 0x0F -+ -+#define fPACKET_WRAPPER_RESERVED 0x3F -+#define fPACKET_CONTAINS_MEDIA_SPECIFIC_INFO 0x40 -+#define fPACKET_ALLOCATED_BY_NDIS 0x80 -+ -+#define PROTOCOL_RESERVED_SIZE_IN_PACKET (4 * sizeof(void *)) -+ -+struct transport_header_offset { -+ USHORT protocol_type; -+ USHORT header_offset; -+}; -+ -+struct ndis_network_address { -+ USHORT length; -+ USHORT type; -+ UCHAR address[1]; -+}; -+ -+struct ndis_network_address_list { -+ LONG count; -+ USHORT type; -+ struct ndis_network_address address[1]; -+}; -+ -+struct ndis_tcp_ip_checksum_packet_info { -+ union { -+ struct { -+ ULONG v4:1; -+ ULONG v6:1; -+ ULONG tcp:1; -+ ULONG udp:1; -+ ULONG ip:1; -+ } tx; -+ struct { -+ ULONG tcp_failed:1; -+ ULONG udp_failed:1; -+ ULONG ip_failed:1; -+ ULONG tcp_succeeded:1; -+ ULONG udp_succeeded:1; -+ ULONG ip_succeeded:1; -+ ULONG loopback:1; -+ } rx; -+ ULONG value; -+ }; -+}; -+ -+enum ndis_task { -+ TcpIpChecksumNdisTask, IpSecNdisTask, TcpLargeSendNdisTask, MaxNdisTask -+}; -+ -+enum ndis_encapsulation { -+ UNSPECIFIED_Encapsulation, NULL_Encapsulation, -+ IEEE_802_3_Encapsulation, IEEE_802_5_Encapsulation, -+ LLC_SNAP_ROUTED_Encapsulation, LLC_SNAP_BRIDGED_Encapsulation -+}; -+ -+#define NDIS_TASK_OFFLOAD_VERSION 1 -+ -+struct ndis_encapsulation_format { -+ enum ndis_encapsulation encap; -+ struct { -+ ULONG fixed_header_size:1; -+ ULONG reserved:31; -+ } flags; -+ ULONG header_size; -+}; -+ -+struct ndis_task_offload_header { -+ ULONG version; -+ ULONG size; -+ ULONG reserved; -+ ULONG offset_first_task; -+ struct ndis_encapsulation_format encap_format; -+}; -+ -+struct ndis_task_offload { -+ ULONG version; -+ ULONG size; -+ enum ndis_task task; -+ ULONG offset_next_task; -+ ULONG task_buf_length; -+ UCHAR task_buf[1]; -+}; -+ -+struct v4_checksum { -+ union { -+ struct { -+ ULONG ip_opts:1; -+ ULONG tcp_opts:1; -+ ULONG tcp_csum:1; -+ ULONG udp_csum:1; -+ ULONG ip_csum:1; -+ }; -+ ULONG value; -+ }; -+}; -+ -+struct v6_checksum { -+ ULONG ip_supported:1; -+ ULONG tcp_supported:1; -+ ULONG tcp_csum:1; -+ ULONG udp_csum:1; -+}; -+ -+struct ndis_task_tcp_ip_checksum { -+ struct v4_checksum v4_tx; -+ struct v4_checksum v4_rx; -+ struct v6_checksum v6_tx; -+ struct v6_checksum v6_rx; -+}; -+ -+struct ndis_task_tcp_large_send { -+ ULONG version; -+ ULONG max_size; -+ ULONG min_seg_count; -+ BOOLEAN tcp_opts; -+ BOOLEAN ip_opts; -+}; -+ -+struct ndis_packet; -+ -+struct ndis_packet_pool { -+ struct ndis_packet *free_descr; -+// NT_SPIN_LOCK lock; -+ spinlock_t lock; -+ UINT max_descr; -+ UINT num_allocated_descr; -+ UINT num_used_descr; -+ UINT proto_rsvd_length; -+}; -+ -+struct ndis_packet_stack { -+ ULONG_PTR IM_reserved[2]; -+ ULONG_PTR ndis_reserved[4]; -+}; -+ -+enum ndis_per_packet_info { -+ TcpIpChecksumPacketInfo, IpSecPacketInfo, TcpLargeSendPacketInfo, -+ ClassificationHandlePacketInfo, NdisReserved, -+ ScatterGatherListPacketInfo, Ieee8021QInfo, OriginalPacketInfo, -+ PacketCancelId, MaxPerPacketInfo -+}; -+ -+struct ndis_packet_extension { -+ void *info[MaxPerPacketInfo]; -+}; -+ -+struct ndis_packet_private { -+ UINT nr_pages; -+ UINT len; -+ ndis_buffer *buffer_head; -+ ndis_buffer *buffer_tail; -+ struct ndis_packet_pool *pool; -+ UINT count; -+ ULONG flags; -+ BOOLEAN valid_counts; -+ UCHAR packet_flags; -+ USHORT oob_offset; -+}; -+ -+struct ndis_packet { -+ struct ndis_packet_private private; -+ /* for use by miniport */ -+ union { -+ /* for connectionless mininports */ -+ struct { -+ UCHAR miniport_reserved[2 * sizeof(void *)]; -+ UCHAR wrapper_reserved[2 * sizeof(void *)]; -+ } cl_reserved; -+ /* for deserialized miniports */ -+ struct { -+ UCHAR miniport_reserved_ex[3 * sizeof(void *)]; -+ UCHAR wrapper_reserved_ex[sizeof(void *)]; -+ } deserialized_reserved; -+ struct { -+ UCHAR mac_reserved[4 * sizeof(void *)]; -+ } mac_reserved; -+ }; -+ ULONG_PTR reserved[2]; -+ UCHAR protocol_reserved[1]; -+}; -+ -+/* OOB data */ -+struct ndis_packet_oob_data { -+ union { -+ ULONGLONG time_to_tx; -+ ULONGLONG time_txed; -+ }; -+ ULONGLONG time_rxed; -+ UINT header_size; -+ UINT media_size; -+ void *media; -+ NDIS_STATUS status; -+ -+ /* ndiswrapper specific info; extension should be right after -+ * ndis's oob_data */ -+ struct ndis_packet_extension ext; -+ union { -+ /* used for tx only */ -+ struct { -+ struct sk_buff *tx_skb; -+ union { -+ struct wrap_tx_sg_list wrap_tx_sg_list; -+ struct ndis_sg_list *tx_sg_list; -+ }; -+ }; -+ /* used for rx only */ -+ struct { -+ unsigned char header[ETH_HLEN]; -+ unsigned char *look_ahead; -+ UINT look_ahead_size; -+ }; -+ }; -+}; -+ -+#define NDIS_PACKET_OOB_DATA(packet) \ -+ (struct ndis_packet_oob_data *)(((void *)(packet)) + \ -+ (packet)->private.oob_offset) -+ -+enum ndis_device_pnp_event { -+ NdisDevicePnPEventQueryRemoved, NdisDevicePnPEventRemoved, -+ NdisDevicePnPEventSurpriseRemoved, NdisDevicePnPEventQueryStopped, -+ NdisDevicePnPEventStopped, NdisDevicePnPEventPowerProfileChanged, -+ NdisDevicePnPEventMaximum -+}; -+ -+enum ndis_request_type { -+ NdisRequestQueryInformation, NdisRequestSetInformation, -+ NdisRequestQueryStatistics, NdisRequestOpen, NdisRequestClose, -+ NdisRequestSend, NdisRequestTransferData, NdisRequestReset, -+ NdisRequestGeneric1, NdisRequestGeneric2, NdisRequestGeneric3, -+ NdisRequestGeneric4 -+}; -+ -+struct ndis_request { -+ mac_address mac; -+ enum ndis_request_type request_type; -+ union data { -+ struct query_info { -+ UINT oid; -+ void *buf; -+ UINT buf_len; -+ UINT written; -+ UINT needed; -+ } query_info; -+ struct set_info { -+ UINT oid; -+ void *buf; -+ UINT buf_len; -+ UINT written; -+ UINT needed; -+ } set_info; -+ } data; -+}; -+ -+enum ndis_medium { -+ NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumWan, -+ NdisMediumLocalTalk, NdisMediumDix, NdisMediumArcnetRaw, -+ NdisMediumArcnet878_2, NdisMediumAtm, NdisMediumWirelessWan, -+ NdisMediumIrda, NdisMediumBpc, NdisMediumCoWan, -+ NdisMedium1394, NdisMediumMax -+}; -+ -+enum ndis_physical_medium { -+ NdisPhysicalMediumUnspecified, NdisPhysicalMediumWirelessLan, -+ NdisPhysicalMediumCableModem, NdisPhysicalMediumPhoneLine, -+ NdisPhysicalMediumPowerLine, NdisPhysicalMediumDSL, -+ NdisPhysicalMediumFibreChannel, NdisPhysicalMedium1394, -+ NdisPhysicalMediumWirelessWan, NdisPhysicalMediumMax -+}; -+ -+enum ndis_power_state { -+ NdisDeviceStateUnspecified = 0, -+ NdisDeviceStateD0, NdisDeviceStateD1, NdisDeviceStateD2, -+ NdisDeviceStateD3, NdisDeviceStateMaximum -+}; -+ -+enum ndis_power_profile { -+ NdisPowerProfileBattery, NdisPowerProfileAcOnLine -+}; -+ -+struct ndis_pm_wakeup_capabilities { -+ enum ndis_power_state min_magic_packet_wakeup; -+ enum ndis_power_state min_pattern_wakeup; -+ enum ndis_power_state min_link_change_wakeup; -+}; -+ -+#define NDIS_PNP_WAKE_UP_MAGIC_PACKET 0x00000001 -+#define NDIS_PNP_WAKE_UP_PATTERN_MATCH 0x00000002 -+#define NDIS_PNP_WAKE_UP_LINK_CHANGE 0x00000004 -+ -+enum net_pnp_event_code { -+ NetEventSetPower, NetEventQueryPower, NetEventQueryRemoveDevice, -+ NetEventCancelRemoveDevice, NetEventReconfigure, NetEventBindList, -+ NetEventBindsComplete, NetEventPnPCapabilities, NetEventMaximum -+}; -+ -+struct net_pnp_event { -+ enum net_pnp_event_code code; -+ void *buf; -+ ULONG buf_length; -+ ULONG_PTR ndis_reserved[4]; -+ ULONG_PTR transport_reserved[4]; -+ ULONG_PTR tdi_reserved[4]; -+ ULONG_PTR tdi_client_reserved[4]; -+}; -+ -+struct ndis_pnp_capabilities { -+ ULONG flags; -+ struct ndis_pm_wakeup_capabilities wakeup; -+}; -+ -+typedef void (*ndis_isr_handler)(BOOLEAN *recognized, BOOLEAN *queue_handler, -+ void *handle) wstdcall; -+typedef void (*ndis_interrupt_handler)(void *ctx) wstdcall; -+ -+struct miniport { -+ /* NDIS 3.0 */ -+ UCHAR major_version; -+ UCHAR minor_version; -+ USHORT filler; -+ UINT reserved; -+ BOOLEAN (*hangcheck)(void *ctx) wstdcall; -+ void (*disable_interrupt)(void *ctx) wstdcall; -+ void (*enable_interrupt)(void *ctx) wstdcall; -+ void (*mp_halt)(void *ctx) wstdcall; -+ ndis_interrupt_handler handle_interrupt; -+ NDIS_STATUS (*init)(NDIS_STATUS *error_status, UINT *medium_index, -+ enum ndis_medium medium[], UINT medium_array_size, -+ void *handle, void *conf_handle) wstdcall; -+ ndis_isr_handler isr; -+ NDIS_STATUS (*queryinfo)(void *ctx, ndis_oid oid, void *buffer, -+ ULONG buflen, ULONG *written, -+ ULONG *needed) wstdcall; -+ void *reconfig; -+ NDIS_STATUS (*reset)(BOOLEAN *reset_address, void *ctx) wstdcall; -+ NDIS_STATUS (*send)(void *ctx, struct ndis_packet *packet, -+ UINT flags) wstdcall; -+ NDIS_STATUS (*setinfo)(void *ctx, ndis_oid oid, void *buffer, -+ ULONG buflen, ULONG *written, -+ ULONG *needed) wstdcall; -+ NDIS_STATUS (*tx_data)(struct ndis_packet *ndis_packet, -+ UINT *bytes_txed, void *mp_ctx, void *rx_ctx, -+ UINT offset, UINT bytes_to_tx) wstdcall; -+ /* NDIS 4.0 extensions */ -+ void (*return_packet)(void *ctx, void *packet) wstdcall; -+ void (*send_packets)(void *ctx, struct ndis_packet **packets, -+ INT nr_of_packets) wstdcall; -+ void (*alloc_complete)(void *handle, void *virt, -+ NDIS_PHY_ADDRESS *phys, -+ ULONG size, void *ctx) wstdcall; -+ /* NDIS 5.0 extensions */ -+ NDIS_STATUS (*co_create_vc)(void *ctx, void *vc_handle, -+ void *vc_ctx) wstdcall; -+ NDIS_STATUS (*co_delete_vc)(void *vc_ctx) wstdcall; -+ NDIS_STATUS (*co_activate_vc)(void *vc_ctx, void *call_params) wstdcall; -+ NDIS_STATUS (*co_deactivate_vc)(void *vc_ctx) wstdcall; -+ NDIS_STATUS (*co_send_packets)(void *vc_ctx, void **packets, -+ UINT nr_of_packets) wstdcall; -+ NDIS_STATUS (*co_request)(void *ctx, void *vc_ctx, UINT *req) wstdcall; -+ /* NDIS 5.1 extensions */ -+ void (*cancel_send_packets)(void *ctx, void *id) wstdcall; -+ void (*pnp_event_notify)(void *ctx, enum ndis_device_pnp_event event, -+ void *inf_buf, ULONG inf_buf_len) wstdcall; -+ void (*shutdown)(void *ctx) wstdcall; -+ void *reserved1; -+ void *reserved2; -+ void *reserved3; -+ void *reserved4; -+}; -+ -+struct ndis_spinlock { -+ NT_SPIN_LOCK klock; -+ KIRQL irql; -+}; -+ -+union ndis_rw_lock_refcount { -+ UCHAR cache_line[16]; -+}; -+ -+struct ndis_rw_lock { -+ union { -+ struct { -+ NT_SPIN_LOCK klock; -+ void *context; -+ }; -+ UCHAR reserved[16]; -+ }; -+ union { -+ union ndis_rw_lock_refcount ref_count[MAXIMUM_PROCESSORS]; -+ /* ndiswrapper specific */ -+ volatile int count; -+ }; -+}; -+ -+struct lock_state { -+ USHORT state; -+ KIRQL irql; -+}; -+ -+struct ndis_work_item; -+typedef void (*NDIS_PROC)(struct ndis_work_item *, void *) wstdcall; -+ -+struct ndis_work_item { -+ void *ctx; -+ NDIS_PROC func; -+ union { -+ UCHAR reserved[8 * sizeof(void *)]; -+ /* ndiswrapper specific */ -+ struct nt_list list; -+ }; -+}; -+ -+struct alloc_shared_mem { -+ void *ctx; -+ ULONG size; -+ BOOLEAN cached; -+}; -+ -+struct ndis_mp_block; -+ -+/* this is opaque to drivers, so we can use it as we please */ -+struct ndis_mp_interrupt { -+ struct kinterrupt *kinterrupt; -+ NT_SPIN_LOCK lock; -+ union { -+ void *reserved; -+ unsigned int irq; -+ }; -+ ndis_isr_handler isr; -+ ndis_interrupt_handler mp_dpc; -+ struct kdpc intr_dpc; -+ struct ndis_mp_block *nmb; -+ UCHAR dpc_count; -+ BOOLEAN enable; -+ struct nt_event dpc_completed_event; -+ BOOLEAN shared; -+ BOOLEAN req_isr; -+}; -+ -+struct ndis_binary_data { -+ USHORT len; -+ void *buf; -+}; -+ -+enum ndis_parameter_type { -+ NdisParameterInteger, NdisParameterHexInteger, -+ NdisParameterString, NdisParameterMultiString, NdisParameterBinary, -+}; -+ -+typedef struct unicode_string NDIS_STRING; -+ -+struct ndis_configuration_parameter { -+ enum ndis_parameter_type type; -+ union { -+ ULONG integer; -+ NDIS_STRING string; -+ } data; -+}; -+ -+struct ndis_driver { -+ struct miniport mp; -+}; -+ -+/* IDs used to store extensions in driver_object's custom extension */ -+#define NDIS_DRIVER_CLIENT_ID 10 -+ -+struct ndis_wireless_stats { -+ ULONG length; -+ LARGE_INTEGER tx_frag; -+ LARGE_INTEGER tx_multi_frag; -+ LARGE_INTEGER failed; -+ LARGE_INTEGER retry; -+ LARGE_INTEGER multi_retry; -+ LARGE_INTEGER rtss_succ; -+ LARGE_INTEGER rtss_fail; -+ LARGE_INTEGER ack_fail; -+ LARGE_INTEGER frame_dup; -+ LARGE_INTEGER rx_frag; -+ LARGE_INTEGER rx_multi_frag; -+ LARGE_INTEGER fcs_err; -+ LARGE_INTEGER tkip_local_mic_failures; -+ LARGE_INTEGER tkip_icv_errors; -+ LARGE_INTEGER tkip_counter_measures_invoked; -+ LARGE_INTEGER tkip_replays; -+ LARGE_INTEGER ccmp_format_errors; -+ LARGE_INTEGER ccmp_replays; -+ LARGE_INTEGER ccmp_decrypt_errors; -+ LARGE_INTEGER fourway_handshake_failures; -+ LARGE_INTEGER wep_undecryptable_count; -+ LARGE_INTEGER wep_icv_errorcount; -+ LARGE_INTEGER decrypt_success_count; -+ LARGE_INTEGER decrypt_failure_count; -+}; -+ -+enum ndis_status_type { -+ Ndis802_11StatusType_Authentication, -+ Ndis802_11StatusType_MediaStreamMode, -+ Ndis802_11StatusType_PMKID_CandidateList, -+ Ndis802_11StatusType_RadioState, -+}; -+ -+struct ndis_status_indication { -+ enum ndis_status_type status_type; -+}; -+ -+enum ndis_radio_status { -+ Ndis802_11RadioStatusOn, Ndis802_11RadioStatusHardwareOff, -+ Ndis802_11RadioStatusSoftwareOff, -+}; -+ -+struct ndis_radio_status_indication -+{ -+ enum ndis_status_type status_type; -+ enum ndis_radio_status radio_state; -+}; -+ -+enum ndis_media_state { -+ NdisMediaStateConnected, -+ NdisMediaStateDisconnected, -+}; -+ -+enum ndis_media_stream_mode { -+ Ndis802_11MediaStreamOff, Ndis802_11MediaStreamOn -+}; -+ -+enum wrapper_work { -+ LINK_STATUS_OFF, LINK_STATUS_ON, SET_MULTICAST_LIST, COLLECT_IW_STATS, -+ HANGCHECK, NETIF_WAKEQ, -+}; -+ -+struct encr_info { -+ struct encr_key { -+ ULONG length; -+ UCHAR key[NDIS_ENCODING_TOKEN_MAX]; -+ } keys[MAX_ENCR_KEYS]; -+ unsigned short tx_key_index; -+}; -+ -+struct ndis_essid { -+ ULONG length; -+ UCHAR essid[NDIS_ESSID_MAX_SIZE]; -+}; -+ -+enum ndis_infrastructure_mode { -+ Ndis802_11IBSS, Ndis802_11Infrastructure, Ndis802_11AutoUnknown, -+ Ndis802_11InfrastructureMax -+}; -+ -+enum authentication_mode { -+ Ndis802_11AuthModeOpen, Ndis802_11AuthModeShared, -+ Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA, -+ Ndis802_11AuthModeWPAPSK, Ndis802_11AuthModeWPANone, -+ Ndis802_11AuthModeWPA2, Ndis802_11AuthModeWPA2PSK, -+ Ndis802_11AuthModeMax -+}; -+ -+enum encryption_status { -+ Ndis802_11WEPEnabled, -+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, -+ Ndis802_11WEPDisabled, -+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, -+ Ndis802_11WEPKeyAbsent, -+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, -+ Ndis802_11WEPNotSupported, -+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, -+ Ndis802_11Encryption2Enabled, Ndis802_11Encryption2KeyAbsent, -+ Ndis802_11Encryption3Enabled, Ndis802_11Encryption3KeyAbsent -+}; -+ -+struct ndis_auth_encr_pair { -+ enum authentication_mode auth_mode; -+ enum encryption_status encr_mode; -+}; -+ -+struct ndis_capability { -+ ULONG length; -+ ULONG version; -+ ULONG num_PMKIDs; -+ ULONG num_auth_encr_pair; -+ struct ndis_auth_encr_pair auth_encr_pair[1]; -+}; -+ -+struct ndis_guid { -+ struct guid guid; -+ union { -+ ndis_oid oid; -+ NDIS_STATUS status; -+ }; -+ ULONG size; -+ ULONG flags; -+}; -+ -+struct ndis_timer { -+ struct nt_timer nt_timer; -+ struct kdpc kdpc; -+}; -+ -+struct ndis_mp_timer { -+ struct nt_timer nt_timer; -+ struct kdpc kdpc; -+ DPC func; -+ void *ctx; -+ struct ndis_mp_block *nmb; -+ struct ndis_mp_timer *next; -+}; -+ -+typedef struct cm_partial_resource_list NDIS_RESOURCE_LIST; -+ -+struct ndis_event { -+ struct nt_event nt_event; -+}; -+ -+struct ndis_bind_paths { -+ UINT number; -+ struct unicode_string paths[1]; -+}; -+ -+struct ndis_reference { -+ NT_SPIN_LOCK lock; -+ USHORT ref_count; -+ BOOLEAN closing; -+}; -+ -+struct ndis_filterdbs { -+ union { -+ void *eth_db; -+ void *null_db; -+ }; -+ void *tr_db; -+ void *fddi_db; -+ void *arc_db; -+}; -+ -+enum ndis_interface_type { -+ NdisInterfaceInternal, NdisInterfaceIsa, NdisInterfaceEisa, -+ NdisInterfaceMca, NdisInterfaceTurboChannel, NdisInterfacePci, -+ NdisInterfacePcMcia, -+}; -+ -+struct auth_encr_capa { -+ unsigned long auth; -+ unsigned long encr; -+}; -+ -+struct ndis_pmkid_candidate { -+ mac_address bssid; -+ DWORD flags; -+}; -+ -+struct ndis_pmkid_candidate_list { -+ ULONG version; -+ ULONG num_candidates; -+ struct ndis_pmkid_candidate candidates[1]; -+}; -+ -+/* -+ * This struct contains function pointers that the drivers references -+ * directly via macros, so it's important that they are at the correct -+ * position. -+ */ -+struct ndis_mp_block { -+ void *signature; -+ struct ndis_mp_block *next; -+ struct driver_object *drv_obj; -+ void *mp_ctx; -+ struct unicode_string name; -+ struct ndis_bind_paths *bindpaths; -+ void *openqueue; -+ struct ndis_reference reference; -+ void *device_ctx; -+ UCHAR padding; -+ UCHAR lock_acquired; -+ UCHAR pmode_opens; -+ UCHAR assigned_cpu; -+ NT_SPIN_LOCK lock; -+ enum ndis_request_type *mediarequest; -+ struct ndis_mp_interrupt *interrupt; -+ ULONG flags; -+ ULONG pnp_flags; -+ struct nt_list packet_list; -+ struct ndis_packet *first_pending_tx_packet; -+ struct ndis_packet *return_packet_queue; -+ ULONG request_buffer; -+ void *set_mcast_buffer; -+ struct ndis_mp_block *primary_mp; -+ void *wrapper_ctx; -+ void *bus_data_ctx; -+ ULONG pnp_capa; -+ void *resources; -+ struct ndis_timer wakeup_dpc_timer; -+ struct unicode_string basename; -+ struct unicode_string symlink_name; -+ ULONG ndis_hangcheck_interval; -+ USHORT hangcheck_ticks; -+ USHORT hangcheck_tick; -+ NDIS_STATUS ndis_reset_status; -+ void *resetopen; -+ struct ndis_filterdbs filterdbs; -+ void *rx_packet; -+ void *send_complete; -+ void *send_resource_avail; -+ void *reset_complete; -+ -+ enum ndis_medium media_type; -+ ULONG bus_number; -+ enum ndis_interface_type bus_type; -+ enum ndis_interface_type adapter_type; -+ struct device_object *fdo; -+ struct device_object *pdo; -+ struct device_object *next_device; -+ void *mapreg; -+ void *call_mgraflist; -+ void *mp_thread; -+ void *setinfobuf; -+ USHORT setinfo_buf_len; -+ USHORT max_send_pkts; -+ NDIS_STATUS fake_status; -+ void *lock_handler; -+ struct unicode_string *adapter_instance_name; -+ void *timer_queue; -+ UINT mac_options; -+ void *pending_req; -+ UINT max_long_addrs; -+ UINT max_short_addrs; -+ UINT cur_lookahead; -+ UINT max_lookahead; -+ -+ ndis_interrupt_handler irq_bh; -+ void *disable_intr; -+ void *enable_intr; -+ void *send_pkts; -+ void *deferred_send; -+ void *eth_rx_indicate; -+ void *tr_rx_indicate; -+ void *fddi_rx_indicate; -+ void *eth_rx_complete; -+ void *tr_rx_complete; -+ void *fddi_rx_complete; -+ -+ void *status; -+ void *status_complete; -+ void *td_complete; -+ -+ void *queryinfo_complete; -+ void *setinfo_complete; -+ void *wan_tx_complete; -+ void *wan_rx; -+ void *wan_rx_complete; -+ /* ndiswrapper specific */ -+ struct ndis_device *wnd; -+}; -+ -+struct ndis_device { -+ struct ndis_mp_block *nmb; -+ struct wrap_device *wd; -+ struct net_device *net_dev; -+ void *shutdown_ctx; -+ struct ndis_mp_interrupt *mp_interrupt; -+ struct kdpc irq_kdpc; -+ unsigned long mem_start; -+ unsigned long mem_end; -+ -+ struct net_device_stats net_stats; -+ struct iw_statistics iw_stats; -+ BOOLEAN iw_stats_enabled; -+ struct ndis_wireless_stats ndis_stats; -+ -+ struct work_struct tx_work; -+ struct ndis_packet *tx_ring[TX_RING_SIZE]; -+ u8 tx_ring_start; -+ u8 tx_ring_end; -+ u8 is_tx_ring_full; -+ u8 tx_ok; -+ spinlock_t tx_ring_lock; -+ struct mutex tx_ring_mutex; -+ unsigned int max_tx_packets; -+ struct mutex ndis_req_mutex; -+ struct task_struct *ndis_req_task; -+ int ndis_req_done; -+ NDIS_STATUS ndis_req_status; -+ ULONG packet_filter; -+ -+ ULONG sg_dma_size; -+ ULONG dma_map_count; -+ dma_addr_t *dma_map_addr; -+ -+ int hangcheck_interval; -+ struct timer_list hangcheck_timer; -+ int iw_stats_interval; -+ struct timer_list iw_stats_timer; -+ unsigned long scan_timestamp; -+ struct encr_info encr_info; -+ char nick[IW_ESSID_MAX_SIZE + 1]; -+ struct ndis_essid essid; -+ struct auth_encr_capa capa; -+ enum ndis_infrastructure_mode infrastructure_mode; -+ int max_pmkids; -+ int num_pmkids; -+ struct ndis_pmkid *pmkids; -+ mac_address mac; -+ struct proc_dir_entry *procfs_iface; -+ -+ struct work_struct ndis_work; -+ unsigned long ndis_pending_work; -+ UINT attributes; -+ int iw_auth_wpa_version; -+ int iw_auth_cipher_pairwise; -+ int iw_auth_cipher_group; -+ int iw_auth_key_mgmt; -+ int iw_auth_80211_alg; -+ struct ndis_packet_pool *tx_packet_pool; -+ struct ndis_buffer_pool *tx_buffer_pool; -+ int multicast_size; -+ struct v4_checksum rx_csum; -+ struct v4_checksum tx_csum; -+ enum ndis_physical_medium physical_medium; -+ ULONG ndis_wolopts; -+ struct nt_slist wrap_timer_slist; -+ int drv_ndis_version; -+ struct ndis_pnp_capabilities pnp_capa; -+}; -+ -+BOOLEAN ndis_isr(struct kinterrupt *kinterrupt, void *ctx) wstdcall; -+ -+int ndis_init(void); -+void ndis_exit(void); -+int ndis_init_device(struct ndis_device *wnd); -+void ndis_exit_device(struct ndis_device *wnd); -+ -+int wrap_procfs_add_ndis_device(struct ndis_device *wnd); -+void wrap_procfs_remove_ndis_device(struct ndis_device *wnd); -+ -+void NdisAllocatePacketPoolEx(NDIS_STATUS *status, -+ struct ndis_packet_pool **pool_handle, -+ UINT num_descr, UINT overflowsize, -+ UINT proto_rsvd_length) wstdcall; -+void NdisFreePacketPool(struct ndis_packet_pool *pool) wstdcall; -+void NdisAllocatePacket(NDIS_STATUS *status, struct ndis_packet **packet, -+ struct ndis_packet_pool *pool) wstdcall; -+void NdisFreePacket(struct ndis_packet *descr) wstdcall; -+void NdisAllocateBufferPool(NDIS_STATUS *status, -+ struct ndis_buffer_pool **pool_handle, -+ UINT num_descr) wstdcall; -+void NdisFreeBufferPool(struct ndis_buffer_pool *pool) wstdcall; -+void NdisAllocateBuffer(NDIS_STATUS *status, ndis_buffer **buffer, -+ struct ndis_buffer_pool *pool, void *virt, -+ UINT length) wstdcall; -+void NdisFreeBuffer(ndis_buffer *descr) wstdcall; -+void NdisMIndicateReceivePacket(struct ndis_mp_block *nmb, -+ struct ndis_packet **packets, -+ UINT nr_packets) wstdcall; -+void NdisMSendComplete(struct ndis_mp_block *nmb, struct ndis_packet *packet, -+ NDIS_STATUS status) wstdcall; -+void NdisMSendResourcesAvailable(struct ndis_mp_block *nmb) wstdcall; -+void NdisMIndicateStatus(struct ndis_mp_block *nmb, -+ NDIS_STATUS status, void *buf, UINT len) wstdcall; -+void NdisMIndicateStatusComplete(struct ndis_mp_block *nmb) wstdcall; -+void NdisMQueryInformationComplete(struct ndis_mp_block *nmb, -+ NDIS_STATUS status) wstdcall; -+void NdisMSetInformationComplete(struct ndis_mp_block *nmb, -+ NDIS_STATUS status) wstdcall; -+void NdisMResetComplete(struct ndis_mp_block *nmb, NDIS_STATUS status, -+ BOOLEAN address_reset) wstdcall; -+ULONG NDIS_BUFFER_TO_SPAN_PAGES(ndis_buffer *buffer) wstdcall; -+BOOLEAN NdisWaitEvent(struct ndis_event *event, UINT timeout) wstdcall; -+void NdisSetEvent(struct ndis_event *event) wstdcall; -+void NdisMDeregisterInterrupt(struct ndis_mp_interrupt *mp_interrupt) wstdcall; -+void EthRxIndicateHandler(struct ndis_mp_block *nmb, void *rx_ctx, -+ char *header1, char *header, UINT header_size, -+ void *look_ahead, UINT look_ahead_size, -+ UINT packet_size) wstdcall; -+void EthRxComplete(struct ndis_mp_block *nmb) wstdcall; -+void NdisMTransferDataComplete(struct ndis_mp_block *nmb, -+ struct ndis_packet *packet, NDIS_STATUS status, -+ UINT bytes_txed) wstdcall; -+void NdisWriteConfiguration(NDIS_STATUS *status, struct ndis_mp_block *nmb, -+ struct unicode_string *key, -+ struct ndis_configuration_parameter *param) wstdcall; -+void NdisReadConfiguration(NDIS_STATUS *status, -+ struct ndis_configuration_parameter **param, -+ struct ndis_mp_block *nmb, -+ struct unicode_string *key, -+ enum ndis_parameter_type type) wstdcall; -+ -+/* Required OIDs */ -+#define OID_GEN_SUPPORTED_LIST 0x00010101 -+#define OID_GEN_HARDWARE_STATUS 0x00010102 -+#define OID_GEN_MEDIA_SUPPORTED 0x00010103 -+#define OID_GEN_MEDIA_IN_USE 0x00010104 -+#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 -+#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 -+#define OID_GEN_LINK_SPEED 0x00010107 -+#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 -+#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 -+#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A -+#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B -+#define OID_GEN_VENDOR_ID 0x0001010C -+#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D -+#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E -+#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F -+#define OID_GEN_DRIVER_VERSION 0x00010110 -+#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 -+#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 -+#define OID_GEN_MAC_OPTIONS 0x00010113 -+#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 -+#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 -+#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 -+#define OID_GEN_SUPPORTED_GUIDS 0x00010117 -+#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 /* Set only */ -+#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 /* Set only */ -+#define OID_GEN_MACHINE_NAME 0x0001021A -+#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B /* Set only */ -+#define OID_GEN_VLAN_ID 0x0001021C -+ -+/* Optional OIDs. */ -+#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 -+#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 -+ -+/* Required statistics OIDs. */ -+#define OID_GEN_XMIT_OK 0x00020101 -+#define OID_GEN_RCV_OK 0x00020102 -+#define OID_GEN_XMIT_ERROR 0x00020103 -+#define OID_GEN_RCV_ERROR 0x00020104 -+#define OID_GEN_RCV_NO_BUFFER 0x00020105 -+ -+/* Optional OID statistics */ -+#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 -+#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 -+#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 -+#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 -+#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 -+#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 -+#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 -+#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 -+#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 -+#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A -+#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B -+#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C -+#define OID_GEN_RCV_CRC_ERROR 0x0002020D -+#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E -+#define OID_GEN_GET_TIME_CAPS 0x0002020F -+#define OID_GEN_GET_NETCARD_TIME 0x00020210 -+#define OID_GEN_NETCARD_LOAD 0x00020211 -+#define OID_GEN_DEVICE_PROFILE 0x00020212 -+ -+/* 802.3 (ethernet) OIDs */ -+#define OID_802_3_PERMANENT_ADDRESS 0x01010101 -+#define OID_802_3_CURRENT_ADDRESS 0x01010102 -+#define OID_802_3_MULTICAST_LIST 0x01010103 -+#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 -+#define OID_802_3_MAC_OPTIONS 0x01010105 -+#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 -+#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 -+#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 -+#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 -+#define OID_802_3_XMIT_DEFERRED 0x01020201 -+#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 -+#define OID_802_3_RCV_OVERRUN 0x01020203 -+#define OID_802_3_XMIT_UNDERRUN 0x01020204 -+#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 -+#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 -+#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 -+ -+/* PnP and power management OIDs */ -+#define OID_PNP_CAPABILITIES 0xFD010100 -+#define OID_PNP_SET_POWER 0xFD010101 -+#define OID_PNP_QUERY_POWER 0xFD010102 -+#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 -+#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 -+#define OID_PNP_WAKE_UP_PATTERN_LIST 0xFD010105 -+#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 -+ -+/* PnP/PM Statistics (Optional). */ -+#define OID_PNP_WAKE_UP_OK 0xFD020200 -+#define OID_PNP_WAKE_UP_ERROR 0xFD020201 -+ -+/* The following bits are defined for OID_PNP_ENABLE_WAKE_UP */ -+#define NDIS_PNP_WAKE_UP_MAGIC_PACKET 0x00000001 -+#define NDIS_PNP_WAKE_UP_PATTERN_MATCH 0x00000002 -+#define NDIS_PNP_WAKE_UP_LINK_CHANGE 0x00000004 -+ -+/* 802.11 OIDs */ -+#define OID_802_11_BSSID 0x0D010101 -+#define OID_802_11_SSID 0x0D010102 -+#define OID_802_11_NETWORK_TYPES_SUPPORTED 0x0D010203 -+#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 -+#define OID_802_11_TX_POWER_LEVEL 0x0D010205 -+#define OID_802_11_RSSI 0x0D010206 -+#define OID_802_11_RSSI_TRIGGER 0x0D010207 -+#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 -+#define OID_802_11_FRAGMENTATION_THRESHOLD 0x0D010209 -+#define OID_802_11_RTS_THRESHOLD 0x0D01020A -+#define OID_802_11_NUMBER_OF_ANTENNAS 0x0D01020B -+#define OID_802_11_RX_ANTENNA_SELECTED 0x0D01020C -+#define OID_802_11_TX_ANTENNA_SELECTED 0x0D01020D -+#define OID_802_11_SUPPORTED_RATES 0x0D01020E -+#define OID_802_11_DESIRED_RATES 0x0D010210 -+#define OID_802_11_CONFIGURATION 0x0D010211 -+#define OID_802_11_STATISTICS 0x0D020212 -+#define OID_802_11_ADD_WEP 0x0D010113 -+#define OID_802_11_REMOVE_WEP 0x0D010114 -+#define OID_802_11_DISASSOCIATE 0x0D010115 -+#define OID_802_11_POWER_MODE 0x0D010216 -+#define OID_802_11_BSSID_LIST 0x0D010217 -+#define OID_802_11_AUTHENTICATION_MODE 0x0D010118 -+#define OID_802_11_PRIVACY_FILTER 0x0D010119 -+#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A -+#define OID_802_11_WEP_STATUS 0x0D01011B -+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS -+#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C -+#define OID_802_11_ADD_KEY 0x0D01011D -+#define OID_802_11_REMOVE_KEY 0x0D01011E -+#define OID_802_11_ASSOCIATION_INFORMATION 0x0D01011F -+#define OID_802_11_TEST 0x0D010120 -+#define OID_802_11_MEDIA_STREAM_MODE 0x0D010121 -+#define OID_802_11_CAPABILITY 0x0D010122 -+#define OID_802_11_PMKID 0x0D010123 -+ -+#define NDIS_STATUS_SUCCESS 0 -+#define NDIS_STATUS_PENDING 0x00000103 -+#define NDIS_STATUS_NOT_RECOGNIZED 0x00010001 -+#define NDIS_STATUS_NOT_COPIED 0x00010002 -+#define NDIS_STATUS_NOT_ACCEPTED 0x00010003 -+#define NDIS_STATUS_CALL_ACTIVE 0x00010007 -+#define NDIS_STATUS_ONLINE 0x40010003 -+#define NDIS_STATUS_RESET_START 0x40010004 -+#define NDIS_STATUS_RESET_END 0x40010005 -+#define NDIS_STATUS_RING_STATUS 0x40010006 -+#define NDIS_STATUS_CLOSED 0x40010007 -+#define NDIS_STATUS_WAN_LINE_UP 0x40010008 -+#define NDIS_STATUS_WAN_LINE_DOWN 0x40010009 -+#define NDIS_STATUS_WAN_FRAGMENT 0x4001000A -+#define NDIS_STATUS_MEDIA_CONNECT 0x4001000B -+#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C -+#define NDIS_STATUS_HARDWARE_LINE_UP 0x4001000D -+#define NDIS_STATUS_HARDWARE_LINE_DOWN 0x4001000E -+#define NDIS_STATUS_INTERFACE_UP 0x4001000F -+#define NDIS_STATUS_INTERFACE_DOWN 0x40010010 -+#define NDIS_STATUS_MEDIA_BUSY 0x40010011 -+#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 -+#define NDIS_STATUS_WW_INDICATION NDIS_STATUS_MEDIA_SPECIFIC_INDICATION -+#define NDIS_STATUS_LINK_SPEED_CHANGE 0x40010013 -+#define NDIS_STATUS_WAN_GET_STATS 0x40010014 -+#define NDIS_STATUS_WAN_CO_FRAGMENT 0x40010015 -+#define NDIS_STATUS_WAN_CO_LINKPARAMS 0x40010016 -+#define NDIS_STATUS_NOT_RESETTABLE 0x80010001 -+#define NDIS_STATUS_SOFT_ERRORS 0x80010003 -+#define NDIS_STATUS_HARD_ERRORS 0x80010004 -+#define NDIS_STATUS_BUFFER_OVERFLOW 0x80000005 -+#define NDIS_STATUS_FAILURE 0xC0000001 -+#define NDIS_STATUS_INVALID_PARAMETER 0xC000000D -+#define NDIS_STATUS_RESOURCES 0xC000009A -+#define NDIS_STATUS_CLOSING 0xC0010002 -+#define NDIS_STATUS_BAD_VERSION 0xC0010004 -+#define NDIS_STATUS_BAD_CHARACTERISTICS 0xC0010005 -+#define NDIS_STATUS_ADAPTER_NOT_FOUND 0xC0010006 -+#define NDIS_STATUS_OPEN_FAILED 0xC0010007 -+#define NDIS_STATUS_DEVICE_FAILED 0xC0010008 -+#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 -+#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A -+#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B -+#define NDIS_STATUS_REQUEST_ABORTED 0xC001000C -+#define NDIS_STATUS_RESET_IN_PROGRESS 0xC001000D -+#define NDIS_STATUS_CLOSING_INDICATING 0xC001000E -+#define NDIS_STATUS_BAD_VERSION 0xC0010004 -+#define NDIS_STATUS_NOT_SUPPORTED 0xC00000BB -+#define NDIS_STATUS_INVALID_PACKET 0xC001000F -+#define NDIS_STATUS_OPEN_LIST_FULL 0xC0010010 -+#define NDIS_STATUS_ADAPTER_NOT_READY 0xC0010011 -+#define NDIS_STATUS_ADAPTER_NOT_OPEN 0xC0010012 -+#define NDIS_STATUS_NOT_INDICATING 0xC0010013 -+#define NDIS_STATUS_INVALID_LENGTH 0xC0010014 -+#define NDIS_STATUS_INVALID_DATA 0xC0010015 -+#define NDIS_STATUS_BUFFER_TOO_SHORT 0xC0010016 -+#define NDIS_STATUS_INVALID_OID 0xC0010017 -+#define NDIS_STATUS_ADAPTER_REMOVED 0xC0010018 -+#define NDIS_STATUS_UNSUPPORTED_MEDIA 0xC0010019 -+#define NDIS_STATUS_GROUP_ADDRESS_IN_USE 0xC001001A -+#define NDIS_STATUS_FILE_NOT_FOUND 0xC001001B -+#define NDIS_STATUS_ERROR_READING_FILE 0xC001001C -+#define NDIS_STATUS_ALREADY_MAPPED 0xC001001D -+#define NDIS_STATUS_RESOURCE_CONFLICT 0xC001001E -+#define NDIS_STATUS_NO_CABLE 0xC001001F -+#define NDIS_STATUS_INVALID_SAP 0xC0010020 -+#define NDIS_STATUS_SAP_IN_USE 0xC0010021 -+#define NDIS_STATUS_INVALID_ADDRESS 0xC0010022 -+#define NDIS_STATUS_VC_NOT_ACTIVATED 0xC0010023 -+#define NDIS_STATUS_DEST_OUT_OF_ORDER 0xC0010024 -+#define NDIS_STATUS_VC_NOT_AVAILABLE 0xC0010025 -+#define NDIS_STATUS_CELLRATE_NOT_AVAILABLE 0xC0010026 -+#define NDIS_STATUS_INCOMPATABLE_QOS 0xC0010027 -+#define NDIS_STATUS_AAL_PARAMS_UNSUPPORTED 0xC0010028 -+#define NDIS_STATUS_NO_ROUTE_TO_DESTINATION 0xC0010029 -+#define NDIS_STATUS_TOKEN_RING_OPEN_ERROR 0xC0011000 -+#define NDIS_STATUS_INVALID_DEVICE_REQUEST 0xC0000010 -+#define NDIS_STATUS_NETWORK_UNREACHABLE 0xC000023C -+ -+/* Event codes */ -+ -+#define EVENT_NDIS_RESOURCE_CONFLICT 0xC0001388 -+#define EVENT_NDIS_OUT_OF_RESOURCE 0xC0001389 -+#define EVENT_NDIS_HARDWARE_FAILURE 0xC000138A -+#define EVENT_NDIS_ADAPTER_NOT_FOUND 0xC000138B -+#define EVENT_NDIS_INTERRUPT_CONNECT 0xC000138C -+#define EVENT_NDIS_DRIVER_FAILURE 0xC000138D -+#define EVENT_NDIS_BAD_VERSION 0xC000138E -+#define EVENT_NDIS_TIMEOUT 0x8000138F -+#define EVENT_NDIS_NETWORK_ADDRESS 0xC0001390 -+#define EVENT_NDIS_UNSUPPORTED_CONFIGURATION 0xC0001391 -+#define EVENT_NDIS_INVALID_VALUE_FROM_ADAPTER 0xC0001392 -+#define EVENT_NDIS_MISSING_CONFIGURATION_PARAMETER 0xC0001393 -+#define EVENT_NDIS_BAD_IO_BASE_ADDRESS 0xC0001394 -+#define EVENT_NDIS_RECEIVE_SPACE_SMALL 0x40001395 -+#define EVENT_NDIS_ADAPTER_DISABLED 0x80001396 -+#define EVENT_NDIS_IO_PORT_CONFLICT 0x80001397 -+#define EVENT_NDIS_PORT_OR_DMA_CONFLICT 0x80001398 -+#define EVENT_NDIS_MEMORY_CONFLICT 0x80001399 -+#define EVENT_NDIS_INTERRUPT_CONFLICT 0x8000139A -+#define EVENT_NDIS_DMA_CONFLICT 0x8000139B -+#define EVENT_NDIS_INVALID_DOWNLOAD_FILE_ERROR 0xC000139C -+#define EVENT_NDIS_MAXRECEIVES_ERROR 0x8000139D -+#define EVENT_NDIS_MAXTRANSMITS_ERROR 0x8000139E -+#define EVENT_NDIS_MAXFRAMESIZE_ERROR 0x8000139F -+#define EVENT_NDIS_MAXINTERNALBUFS_ERROR 0x800013A0 -+#define EVENT_NDIS_MAXMULTICAST_ERROR 0x800013A1 -+#define EVENT_NDIS_PRODUCTID_ERROR 0x800013A2 -+#define EVENT_NDIS_LOBE_FAILUE_ERROR 0x800013A3 -+#define EVENT_NDIS_SIGNAL_LOSS_ERROR 0x800013A4 -+#define EVENT_NDIS_REMOVE_RECEIVED_ERROR 0x800013A5 -+#define EVENT_NDIS_TOKEN_RING_CORRECTION 0x400013A6 -+#define EVENT_NDIS_ADAPTER_CHECK_ERROR 0xC00013A7 -+#define EVENT_NDIS_RESET_FAILURE_ERROR 0x800013A8 -+#define EVENT_NDIS_CABLE_DISCONNECTED_ERROR 0x800013A9 -+#define EVENT_NDIS_RESET_FAILURE_CORRECTION 0x800013AA -+ -+/* packet filter bits used by NDIS_OID_PACKET_FILTER */ -+#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 -+#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 -+#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 -+#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 -+#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 -+#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 -+#define NDIS_PACKET_TYPE_SMT 0x00000040 -+#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 -+#define NDIS_PACKET_TYPE_GROUP 0x00001000 -+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00002000 -+#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00004000 -+#define NDIS_PACKET_TYPE_MAC_FRAME 0x00008000 -+ -+/* memory allocation flags */ -+#define NDIS_MEMORY_CONTIGUOUS 0x00000001 -+#define NDIS_MEMORY_NONCACHED 0x00000002 -+ -+/* Attribute flags to NdisMSetAtrributesEx */ -+#define NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT 0x00000001 -+#define NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT 0x00000002 -+#define NDIS_ATTRIBUTE_IGNORE_TOKEN_RING_ERRORS 0x00000004 -+#define NDIS_ATTRIBUTE_BUS_MASTER 0x00000008 -+#define NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER 0x00000010 -+#define NDIS_ATTRIBUTE_DESERIALIZE 0x00000020 -+#define NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND 0x00000040 -+#define NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK 0x00000080 -+#define NDIS_ATTRIBUTE_NOT_CO_NDIS 0x00000100 -+#define NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS 0x00000200 -+ -+#define OID_TCP_TASK_OFFLOAD 0xFC010201 -+ -+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 -+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 -+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 -+#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 -+#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 -+#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 -+#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 -+#define NDIS_MAC_OPTION_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00000080 -+#define NDIS_MAC_OPTION_RECEIVE_AT_DPC 0x00000100 -+#define NDIS_MAC_OPTION_8021Q_VLAN 0x00000200 -+#define NDIS_MAC_OPTION_RESERVED 0x80000000 -+ -+#define deserialized_driver(wnd) (wnd->attributes & NDIS_ATTRIBUTE_DESERIALIZE) -+ -+static inline void serialize_lock(struct ndis_device *wnd) -+{ -+ nt_spin_lock(&wnd->nmb->lock); -+} -+ -+static inline void serialize_unlock(struct ndis_device *wnd) -+{ -+ nt_spin_unlock(&wnd->nmb->lock); -+} -+ -+static inline KIRQL serialize_lock_irql(struct ndis_device *wnd) -+{ -+ if (deserialized_driver(wnd)) -+ return raise_irql(DISPATCH_LEVEL); -+ else -+ return nt_spin_lock_irql(&wnd->nmb->lock, DISPATCH_LEVEL); -+} -+ -+static inline void serialize_unlock_irql(struct ndis_device *wnd, -+ KIRQL irql) -+{ -+ if (deserialized_driver(wnd)) -+ lower_irql(irql); -+ else -+ nt_spin_unlock_irql(&wnd->nmb->lock, irql); -+} -+ -+static inline void if_serialize_lock(struct ndis_device *wnd) -+{ -+ if (!deserialized_driver(wnd)) -+ nt_spin_lock(&wnd->nmb->lock); -+} -+ -+static inline void if_serialize_unlock(struct ndis_device *wnd) -+{ -+ if (!deserialized_driver(wnd)) -+ nt_spin_unlock(&wnd->nmb->lock); -+} -+ -+#endif /* NDIS_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndiswrapper.h ---- linux-3.12.2/3rdparty/ndiswrapper/ndiswrapper.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ndiswrapper.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,219 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _NDISWRAPPER_H_ -+#define _NDISWRAPPER_H_ -+ -+#define DRIVER_VERSION "1.59" -+#define UTILS_VERSION "1.9" -+ -+#define DRIVER_NAME "ndiswrapper" -+#define DRIVER_CONFIG_DIR "/etc/ndiswrapper" -+ -+#define NDIS_ESSID_MAX_SIZE 32 -+#define NDIS_ENCODING_TOKEN_MAX 32 -+#define MAX_ENCR_KEYS 4 -+#define TX_RING_SIZE 16 -+#define NDIS_MAX_RATES 8 -+#define NDIS_MAX_RATES_EX 16 -+ -+#define WRAP_PCI_BUS 5 -+#define WRAP_PCMCIA_BUS 8 -+/* some USB devices, e.g., DWL-G120 have BusType as 0 */ -+#define WRAP_INTERNAL_BUS 0 -+/* documentation at msdn says 15 is PNP bus, but inf files from all -+ * vendors say 15 is USB; which is correct? */ -+#define WRAP_USB_BUS 15 -+ -+/* NDIS device must be 0, for compatibility with old versions of -+ * ndiswrapper where device type for NDIS drivers is 0 */ -+#define WRAP_NDIS_DEVICE 0 -+#define WRAP_USB_DEVICE 1 -+#define WRAP_BLUETOOTH_DEVICE1 2 -+#define WRAP_BLUETOOTH_DEVICE2 3 -+ -+#define WRAP_DEVICE_BUS(dev, bus) ((dev) << 8 | (bus)) -+#define WRAP_BUS(dev_bus) ((dev_bus) & 0x000FF) -+#define WRAP_DEVICE(dev_bus) ((dev_bus) >> 8) -+ -+#define MAX_DRIVER_NAME_LEN 32 -+#define MAX_VERSION_STRING_LEN 64 -+#define MAX_SETTING_NAME_LEN 128 -+#define MAX_SETTING_VALUE_LEN 256 -+ -+#define MAX_DRIVER_PE_IMAGES 4 -+#define MAX_DRIVER_BIN_FILES 5 -+#define MAX_DEVICE_SETTINGS 512 -+ -+#define MAX_ALLOCATED_URBS 15 -+ -+#define DEV_ANY_ID -1 -+ -+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -+#define MACSTRSEP "%02x:%02x:%02x:%02x:%02x:%02x" -+#define MACSTR "%02x%02x%02x%02x%02x%02x" -+#define MACINTADR(a) (int*)&((a)[0]), (int*)&((a)[1]), (int*)&((a)[2]), \ -+ (int*)&((a)[3]), (int*)&((a)[4]), (int*)&((a)[5]) -+ -+#ifdef __KERNEL__ -+/* DEBUG macros */ -+ -+#define MSG(level, fmt, ...) \ -+ printk(level DRIVER_NAME " (%s:%d): " fmt "\n", \ -+ __func__, __LINE__ , ## __VA_ARGS__) -+ -+#define WARNING(fmt, ...) MSG(KERN_WARNING, fmt, ## __VA_ARGS__) -+#define ERROR(fmt, ...) MSG(KERN_ERR, fmt , ## __VA_ARGS__) -+#define INFO(fmt, ...) MSG(KERN_INFO, fmt , ## __VA_ARGS__) -+#define TODO() WARNING("not fully implemented (yet)") -+ -+#define TRACE(level, fmt, ...) \ -+do { \ -+ if (debug >= level) \ -+ printk(KERN_INFO "%s (%s:%d): " fmt "\n", DRIVER_NAME, \ -+ __func__, __LINE__ , ## __VA_ARGS__); \ -+} while (0) -+#define TRACE0(fmt, ...) TRACE(0, fmt , ## __VA_ARGS__) -+ -+extern int debug; -+ -+#ifndef DEBUG -+#define DEBUG 0 -+#endif -+ -+/* for a block of code */ -+#if DEBUG >= 1 -+#define DBG_BLOCK(level) if (debug >= level) -+#else -+#define DBG_BLOCK(level) while (0) -+#endif -+ -+#if DEBUG >= 1 -+#define TRACE1(fmt, ...) TRACE(1, fmt , ## __VA_ARGS__) -+#else -+#define TRACE1(fmt, ...) do { } while (0) -+#endif -+ -+#if DEBUG >= 2 -+#define TRACE2(fmt, ...) TRACE(2, fmt , ## __VA_ARGS__) -+#else -+#define TRACE2(fmt, ...) do { } while (0) -+#endif -+ -+#if DEBUG >= 3 -+#define TRACE3(fmt, ...) TRACE(3, fmt , ## __VA_ARGS__) -+#else -+#define TRACE3(fmt, ...) do { } while (0) -+#endif -+ -+#if DEBUG >= 4 -+#define TRACE4(fmt, ...) TRACE(4, fmt , ## __VA_ARGS__) -+#else -+#define TRACE4(fmt, ...) do { } while (0) -+#endif -+ -+#if DEBUG >= 5 -+#define TRACE5(fmt, ...) TRACE(5, fmt , ## __VA_ARGS__) -+#else -+#define TRACE5(fmt, ...) do { } while (0) -+#endif -+ -+#if DEBUG >= 6 -+#define TRACE6(fmt, ...) TRACE(6, fmt , ## __VA_ARGS__) -+#else -+#define TRACE6(fmt, ...) do { } while (0) -+#endif -+ -+#define ENTER0(fmt, ...) TRACE0("Enter " fmt , ## __VA_ARGS__) -+#define ENTER1(fmt, ...) TRACE1("Enter " fmt , ## __VA_ARGS__) -+#define ENTER2(fmt, ...) TRACE2("Enter " fmt , ## __VA_ARGS__) -+#define ENTER3(fmt, ...) TRACE3("Enter " fmt , ## __VA_ARGS__) -+#define ENTER4(fmt, ...) TRACE4("Enter " fmt , ## __VA_ARGS__) -+#define ENTER5(fmt, ...) TRACE5("Enter " fmt , ## __VA_ARGS__) -+#define ENTER6(fmt, ...) TRACE6("Enter " fmt , ## __VA_ARGS__) -+ -+#define EXIT0(stmt) do { TRACE0("Exit"); stmt; } while (0) -+#define EXIT1(stmt) do { TRACE1("Exit"); stmt; } while (0) -+#define EXIT2(stmt) do { TRACE2("Exit"); stmt; } while (0) -+#define EXIT3(stmt) do { TRACE3("Exit"); stmt; } while (0) -+#define EXIT4(stmt) do { TRACE4("Exit"); stmt; } while (0) -+#define EXIT5(stmt) do { TRACE5("Exit"); stmt; } while (0) -+#define EXIT6(stmt) do { TRACE6("Exit"); stmt; } while (0) -+ -+#if defined(USB_DEBUG) -+#define USBTRACE TRACE0 -+#define USBENTER ENTER0 -+#define USBEXIT EXIT0 -+#else -+#define USBTRACE(fmt, ...) do { } while (0) -+#define USBENTER(fmt, ...) -+#define USBEXIT(stmt) stmt -+#endif -+ -+#if defined(EVENT_DEBUG) -+#define EVENTTRACE TRACE0 -+#define EVENTENTER ENTER0 -+#define EVENTEXIT EXIT0 -+#else -+#define EVENTTRACE(fmt, ...) do { } while (0) -+#define EVENTENTER(fmt, ...) -+#define EVENTEXIT(stmt) stmt -+#endif -+ -+#if defined(TIMER_DEBUG) -+#define TIMERTRACE TRACE0 -+#define TIMERENTER ENTER0 -+#define TIMEREXIT EXIT0 -+#else -+#define TIMERTRACE(fmt, ...) do { } while (0) -+#define TIMERENTER(fmt, ...) -+#define TIMEREXIT(stmt) stmt -+#endif -+ -+#if defined(IO_DEBUG) -+#define IOTRACE TRACE0 -+#define IOENTER ENTER0 -+#define IOEXIT EXIT0 -+#else -+#define IOTRACE(fmt, ...) do { } while (0) -+#define IOENTER(fmt, ...) -+#define IOEXIT(stmt) stmt -+#endif -+ -+#if defined(WORK_DEBUG) -+#define WORKTRACE TRACE0 -+#define WORKENTER ENTER0 -+#define WORKEXIT EXIT0 -+#else -+#define WORKTRACE(fmt, ...) do { } while (0) -+#define WORKENTER(fmt, ...) -+#define WORKEXIT(stmt) stmt -+#endif -+ -+#if DEBUG >= 1 -+#define assert(expr) \ -+do { \ -+ if (!(expr)) { \ -+ ERROR("assertion '%s' failed", #expr); \ -+ dump_stack(); \ -+ } \ -+} while (0) -+#else -+#define assert(expr) do { } while (0) -+#endif -+ -+#endif // __KERNEL__ -+ -+#endif // NDISWRAPPER_H -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.c ---- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,2677 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ntoskernel.h" -+#include "ndis.h" -+#include "usb.h" -+#include "pnp.h" -+#include "loader.h" -+#include "ntoskernel_exports.h" -+ -+/* MDLs describe a range of virtual address with an array of physical -+ * pages right after the header. For different ranges of virtual -+ * addresses, the number of entries of physical pages may be different -+ * (depending on number of entries required). If we want to allocate -+ * MDLs from a pool, the size has to be constant. So we assume that -+ * maximum range used by a driver is MDL_CACHE_PAGES; if a driver -+ * requests an MDL for a bigger region, we allocate it with kmalloc; -+ * otherwise, we allocate from the pool */ -+ -+#define MDL_CACHE_PAGES 3 -+#define MDL_CACHE_SIZE (sizeof(struct mdl) + \ -+ (sizeof(PFN_NUMBER) * MDL_CACHE_PAGES)) -+struct wrap_mdl { -+ struct nt_list list; -+ struct mdl mdl[0]; -+}; -+ -+/* everything here is for all drivers/devices - not per driver/device */ -+static spinlock_t dispatcher_lock; -+spinlock_t ntoskernel_lock; -+static void *mdl_cache; -+static struct nt_list wrap_mdl_list; -+ -+static struct work_struct kdpc_work; -+static void kdpc_worker(struct work_struct *dummy); -+ -+static struct nt_list kdpc_list; -+static spinlock_t kdpc_list_lock; -+ -+static struct nt_list callback_objects; -+ -+struct nt_list object_list; -+ -+struct bus_driver { -+ struct nt_list list; -+ char name[MAX_DRIVER_NAME_LEN]; -+ struct driver_object drv_obj; -+}; -+ -+static struct nt_list bus_driver_list; -+ -+static struct work_struct ntos_work; -+static struct nt_list ntos_work_list; -+static spinlock_t ntos_work_lock; -+static void ntos_work_worker(struct work_struct *dummy); -+spinlock_t irp_cancel_lock; -+static NT_SPIN_LOCK nt_list_lock; -+static struct nt_slist wrap_timer_slist; -+CCHAR cpu_count; -+ -+/* compute ticks (100ns) since 1601 until when system booted into -+ * wrap_ticks_to_boot */ -+u64 wrap_ticks_to_boot; -+ -+#if defined(CONFIG_X86_64) -+static struct timer_list shared_data_timer; -+struct kuser_shared_data kuser_shared_data; -+static void update_user_shared_data_proc(unsigned long data); -+#endif -+ -+WIN_SYMBOL_MAP("KeTickCount", &jiffies) -+WIN_SYMBOL_MAP("KeNumberProcessors", &cpu_count) -+WIN_SYMBOL_MAP("NlsMbCodePageTag", FALSE) -+ -+struct workqueue_struct *ntos_wq; -+ -+#ifdef WRAP_PREEMPT -+DEFINE_PER_CPU(struct irql_info, irql_info); -+#endif -+ -+#if defined(CONFIG_X86_64) -+static void update_user_shared_data_proc(unsigned long data) -+{ -+ /* timer is supposed to be scheduled every 10ms, but bigger -+ * intervals seem to work (tried up to 50ms) */ -+ *((ULONG64 *)&kuser_shared_data.system_time) = ticks_1601(); -+ *((ULONG64 *)&kuser_shared_data.interrupt_time) = -+ jiffies * TICKSPERSEC / HZ; -+ *((ULONG64 *)&kuser_shared_data.tick) = jiffies; -+ -+ mod_timer(&shared_data_timer, jiffies + MSEC_TO_HZ(30)); -+} -+#endif -+ -+void *allocate_object(ULONG size, enum common_object_type type, -+ struct unicode_string *name) -+{ -+ struct common_object_header *hdr; -+ void *body; -+ -+ /* we pad header as prefix to body */ -+ hdr = ExAllocatePoolWithTag(NonPagedPool, OBJECT_SIZE(size), 0); -+ if (!hdr) { -+ WARNING("couldn't allocate memory"); -+ return NULL; -+ } -+ memset(hdr, 0, OBJECT_SIZE(size)); -+ if (name) { -+ hdr->name.buf = ExAllocatePoolWithTag(NonPagedPool, -+ name->max_length, 0); -+ if (!hdr->name.buf) { -+ ExFreePool(hdr); -+ return NULL; -+ } -+ memcpy(hdr->name.buf, name->buf, name->max_length); -+ hdr->name.length = name->length; -+ hdr->name.max_length = name->max_length; -+ } -+ hdr->type = type; -+ hdr->ref_count = 1; -+ spin_lock_bh(&ntoskernel_lock); -+ /* threads are looked up often (in KeWaitForXXX), so optimize -+ * for fast lookups of threads */ -+ if (type == OBJECT_TYPE_NT_THREAD) -+ InsertHeadList(&object_list, &hdr->list); -+ else -+ InsertTailList(&object_list, &hdr->list); -+ spin_unlock_bh(&ntoskernel_lock); -+ body = HEADER_TO_OBJECT(hdr); -+ TRACE3("allocated hdr: %p, body: %p", hdr, body); -+ return body; -+} -+ -+static void free_object(void *object) -+{ -+ struct common_object_header *hdr; -+ -+ hdr = OBJECT_TO_HEADER(object); -+ spin_lock_bh(&ntoskernel_lock); -+ RemoveEntryList(&hdr->list); -+ spin_unlock_bh(&ntoskernel_lock); -+ TRACE3("freed hdr: %p, body: %p", hdr, object); -+ if (hdr->name.buf) -+ ExFreePool(hdr->name.buf); -+ ExFreePool(hdr); -+} -+ -+static int add_bus_driver(const char *name) -+{ -+ struct bus_driver *bus_driver; -+ -+ bus_driver = kzalloc(sizeof(*bus_driver), GFP_KERNEL); -+ if (!bus_driver) { -+ ERROR("couldn't allocate memory"); -+ return -ENOMEM; -+ } -+ strncpy(bus_driver->name, name, sizeof(bus_driver->name)); -+ bus_driver->name[sizeof(bus_driver->name)-1] = 0; -+ spin_lock_bh(&ntoskernel_lock); -+ InsertTailList(&bus_driver_list, &bus_driver->list); -+ spin_unlock_bh(&ntoskernel_lock); -+ TRACE1("bus driver %s is at %p", name, &bus_driver->drv_obj); -+ return STATUS_SUCCESS; -+} -+ -+struct driver_object *find_bus_driver(const char *name) -+{ -+ struct bus_driver *bus_driver; -+ struct driver_object *drv_obj; -+ -+ spin_lock_bh(&ntoskernel_lock); -+ drv_obj = NULL; -+ nt_list_for_each_entry(bus_driver, &bus_driver_list, list) { -+ if (strcmp(bus_driver->name, name) == 0) { -+ drv_obj = &bus_driver->drv_obj; -+ break; -+ } -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ return drv_obj; -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedInsertHeadList,3) -+ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) -+{ -+ struct nt_list *first; -+ unsigned long flags; -+ -+ ENTER5("head = %p, entry = %p", head, entry); -+ nt_spin_lock_irqsave(lock, flags); -+ first = InsertHeadList(head, entry); -+ nt_spin_unlock_irqrestore(lock, flags); -+ TRACE5("head = %p, old = %p", head, first); -+ return first; -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExInterlockedInsertHeadList,3) -+ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) -+{ -+ ENTER5("%p", head); -+ return ExfInterlockedInsertHeadList(head, entry, lock); -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedInsertTailList,3) -+ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) -+{ -+ struct nt_list *last; -+ unsigned long flags; -+ -+ ENTER5("head = %p, entry = %p", head, entry); -+ nt_spin_lock_irqsave(lock, flags); -+ last = InsertTailList(head, entry); -+ nt_spin_unlock_irqrestore(lock, flags); -+ TRACE5("head = %p, old = %p", head, last); -+ return last; -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExInterlockedInsertTailList,3) -+ (struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock) -+{ -+ ENTER5("%p", head); -+ return ExfInterlockedInsertTailList(head, entry, lock); -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedRemoveHeadList,2) -+ (struct nt_list *head, NT_SPIN_LOCK *lock) -+{ -+ struct nt_list *ret; -+ unsigned long flags; -+ -+ ENTER5("head = %p", head); -+ nt_spin_lock_irqsave(lock, flags); -+ ret = RemoveHeadList(head); -+ nt_spin_unlock_irqrestore(lock, flags); -+ TRACE5("head = %p, ret = %p", head, ret); -+ return ret; -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExInterlockedRemoveHeadList,2) -+ (struct nt_list *head, NT_SPIN_LOCK *lock) -+{ -+ ENTER5("%p", head); -+ return ExfInterlockedRemoveHeadList(head, lock); -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedRemoveTailList,2) -+ (struct nt_list *head, NT_SPIN_LOCK *lock) -+{ -+ struct nt_list *ret; -+ unsigned long flags; -+ -+ ENTER5("head = %p", head); -+ nt_spin_lock_irqsave(lock, flags); -+ ret = RemoveTailList(head); -+ nt_spin_unlock_irqrestore(lock, flags); -+ TRACE5("head = %p, ret = %p", head, ret); -+ return ret; -+} -+ -+wfastcall struct nt_list *WIN_FUNC(ExInterlockedRemoveTailList,2) -+ (struct nt_list *head, NT_SPIN_LOCK *lock) -+{ -+ ENTER5("%p", head); -+ return ExfInterlockedRemoveTailList(head, lock); -+} -+ -+wfastcall void WIN_FUNC(InitializeSListHead,1) -+ (nt_slist_header *head) -+{ -+ memset(head, 0, sizeof(*head)); -+} -+ -+wfastcall struct nt_slist *WIN_FUNC(ExInterlockedPushEntrySList,3) -+ (nt_slist_header *head, struct nt_slist *entry, NT_SPIN_LOCK *lock) -+{ -+ struct nt_slist *ret; -+ -+ ret = PushEntrySList(head, entry, lock); -+ return ret; -+} -+ -+wstdcall struct nt_slist *WIN_FUNC(ExpInterlockedPushEntrySList,2) -+ (nt_slist_header *head, struct nt_slist *entry) -+{ -+ struct nt_slist *ret; -+ -+ ret = PushEntrySList(head, entry, &nt_list_lock); -+ return ret; -+} -+ -+wfastcall struct nt_slist *WIN_FUNC(InterlockedPushEntrySList,2) -+ (nt_slist_header *head, struct nt_slist *entry) -+{ -+ struct nt_slist *ret; -+ -+ ret = PushEntrySList(head, entry, &nt_list_lock); -+ return ret; -+} -+ -+wfastcall struct nt_slist *WIN_FUNC(ExInterlockedPopEntrySList,2) -+ (nt_slist_header *head, NT_SPIN_LOCK *lock) -+{ -+ struct nt_slist *ret; -+ -+ ret = PopEntrySList(head, lock); -+ return ret; -+} -+ -+wstdcall struct nt_slist *WIN_FUNC(ExpInterlockedPopEntrySList,1) -+ (nt_slist_header *head) -+{ -+ struct nt_slist *ret; -+ -+ ret = PopEntrySList(head, &nt_list_lock); -+ return ret; -+} -+ -+wfastcall struct nt_slist *WIN_FUNC(InterlockedPopEntrySList,1) -+ (nt_slist_header *head) -+{ -+ struct nt_slist *ret; -+ -+ ret = PopEntrySList(head, &nt_list_lock); -+ return ret; -+} -+ -+wstdcall USHORT WIN_FUNC(ExQueryDepthSList,1) -+ (nt_slist_header *head) -+{ -+ USHORT depth; -+ ENTER5("%p", head); -+ depth = head->depth; -+ TRACE5("%d, %p", depth, head->next); -+ return depth; -+} -+ -+wfastcall LONG WIN_FUNC(InterlockedIncrement,1) -+ (LONG volatile *val) -+{ -+ return post_atomic_add(*val, 1); -+} -+ -+wfastcall LONG WIN_FUNC(InterlockedDecrement,1) -+ (LONG volatile *val) -+{ -+ return post_atomic_add(*val, -1); -+} -+ -+wfastcall LONG WIN_FUNC(InterlockedExchange,2) -+ (LONG volatile *target, LONG val) -+{ -+ return xchg(target, val); -+} -+ -+wfastcall LONG WIN_FUNC(InterlockedCompareExchange,3) -+ (LONG volatile *dest, LONG new, LONG old) -+{ -+ return cmpxchg(dest, old, new); -+} -+ -+wfastcall void WIN_FUNC(ExInterlockedAddLargeStatistic,2) -+ (LARGE_INTEGER volatile *plint, ULONG n) -+{ -+ unsigned long flags; -+ -+ local_irq_save(flags); -+#ifdef CONFIG_X86_64 -+ __asm__ __volatile__( -+ "\n" -+ LOCK_PREFIX "add %1, %0\n\t" -+ : "+m" (*plint) -+ : "r" (n)); -+#else -+ __asm__ __volatile__( -+ "1:\t" -+ " movl %1, %%ebx\n\t" -+ " movl %%edx, %%ecx\n\t" -+ " addl %%eax, %%ebx\n\t" -+ " adcl $0, %%ecx\n\t" -+ LOCK_PREFIX "cmpxchg8b %0\n\t" -+ " jnz 1b\n\t" -+ : "+m" (*plint) -+ : "m" (n), "A" (*plint) -+ : "ebx", "ecx"); -+#endif -+ local_irq_restore(flags); -+} -+ -+static void initialize_object(struct dispatcher_header *dh, enum dh_type type, -+ int state) -+{ -+ memset(dh, 0, sizeof(*dh)); -+ set_object_type(dh, type); -+ dh->signal_state = state; -+ InitializeListHead(&dh->wait_blocks); -+} -+ -+static void timer_proc(unsigned long data) -+{ -+ struct wrap_timer *wrap_timer = (struct wrap_timer *)data; -+ struct nt_timer *nt_timer; -+ struct kdpc *kdpc; -+ -+ nt_timer = wrap_timer->nt_timer; -+ TIMERENTER("%p(%p), %lu", wrap_timer, nt_timer, jiffies); -+#ifdef TIMER_DEBUG -+ BUG_ON(wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC); -+ BUG_ON(nt_timer->wrap_timer_magic != WRAP_TIMER_MAGIC); -+#endif -+ KeSetEvent((struct nt_event *)nt_timer, 0, FALSE); -+ if (wrap_timer->repeat) -+ mod_timer(&wrap_timer->timer, jiffies + wrap_timer->repeat); -+ kdpc = nt_timer->kdpc; -+ if (kdpc) -+ queue_kdpc(kdpc); -+ TIMEREXIT(return); -+} -+ -+void wrap_init_timer(struct nt_timer *nt_timer, enum timer_type type, -+ struct ndis_mp_block *nmb) -+{ -+ struct wrap_timer *wrap_timer; -+ -+ /* TODO: if a timer is initialized more than once, we allocate -+ * memory for wrap_timer more than once for the same nt_timer, -+ * wasting memory. We can check if nt_timer->wrap_timer_magic is -+ * set and not allocate, but it is not guaranteed always to be -+ * safe */ -+ TIMERENTER("%p", nt_timer); -+ /* we allocate memory for wrap_timer behind driver's back and -+ * there is no NDIS/DDK function where this memory can be -+ * freed, so we use slack_kmalloc so it gets freed when driver -+ * is unloaded */ -+ if (nmb) -+ wrap_timer = kzalloc(sizeof(*wrap_timer), irql_gfp()); -+ else -+ wrap_timer = slack_kzalloc(sizeof(*wrap_timer)); -+ if (!wrap_timer) { -+ ERROR("couldn't allocate memory for timer"); -+ return; -+ } -+ -+ init_timer(&wrap_timer->timer); -+ wrap_timer->timer.data = (unsigned long)wrap_timer; -+ wrap_timer->timer.function = timer_proc; -+ wrap_timer->nt_timer = nt_timer; -+#ifdef TIMER_DEBUG -+ wrap_timer->wrap_timer_magic = WRAP_TIMER_MAGIC; -+#endif -+ nt_timer->wrap_timer = wrap_timer; -+ nt_timer->kdpc = NULL; -+ initialize_object(&nt_timer->dh, (enum dh_type)type, 0); -+ nt_timer->wrap_timer_magic = WRAP_TIMER_MAGIC; -+ TIMERTRACE("timer %p (%p)", wrap_timer, nt_timer); -+ spin_lock_bh(&ntoskernel_lock); -+ if (nmb) { -+ wrap_timer->slist.next = nmb->wnd->wrap_timer_slist.next; -+ nmb->wnd->wrap_timer_slist.next = &wrap_timer->slist; -+ } else { -+ wrap_timer->slist.next = wrap_timer_slist.next; -+ wrap_timer_slist.next = &wrap_timer->slist; -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ TIMEREXIT(return); -+} -+ -+wstdcall void WIN_FUNC(KeInitializeTimerEx,2) -+ (struct nt_timer *nt_timer, enum timer_type type) -+{ -+ TIMERENTER("%p", nt_timer); -+ wrap_init_timer(nt_timer, type, NULL); -+} -+ -+wstdcall void WIN_FUNC(KeInitializeTimer,1) -+ (struct nt_timer *nt_timer) -+{ -+ TIMERENTER("%p", nt_timer); -+ wrap_init_timer(nt_timer, NotificationTimer, NULL); -+} -+ -+/* expires and repeat are in HZ */ -+BOOLEAN wrap_set_timer(struct nt_timer *nt_timer, unsigned long expires_hz, -+ unsigned long repeat_hz, struct kdpc *kdpc) -+{ -+ struct wrap_timer *wrap_timer; -+ -+ TIMERENTER("%p, %lu, %lu, %p, %lu", -+ nt_timer, expires_hz, repeat_hz, kdpc, jiffies); -+ -+ wrap_timer = nt_timer->wrap_timer; -+ TIMERTRACE("%p", wrap_timer); -+#ifdef TIMER_DEBUG -+ if (wrap_timer->nt_timer != nt_timer) -+ WARNING("bad timers: %p, %p, %p", wrap_timer, nt_timer, -+ wrap_timer->nt_timer); -+ if (nt_timer->wrap_timer_magic != WRAP_TIMER_MAGIC) { -+ WARNING("buggy Windows timer didn't initialize timer %p", -+ nt_timer); -+ return FALSE; -+ } -+ if (wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC) { -+ WARNING("timer %p is not initialized (%lx)?", -+ wrap_timer, wrap_timer->wrap_timer_magic); -+ wrap_timer->wrap_timer_magic = WRAP_TIMER_MAGIC; -+ } -+#endif -+ KeClearEvent((struct nt_event *)nt_timer); -+ nt_timer->kdpc = kdpc; -+ wrap_timer->repeat = repeat_hz; -+ if (mod_timer(&wrap_timer->timer, jiffies + expires_hz)) -+ TIMEREXIT(return TRUE); -+ else -+ TIMEREXIT(return FALSE); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeSetTimerEx,4) -+ (struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks, -+ LONG period_ms, struct kdpc *kdpc) -+{ -+ unsigned long expires_hz, repeat_hz; -+ -+ TIMERENTER("%p, %lld, %d", nt_timer, duetime_ticks, period_ms); -+ expires_hz = SYSTEM_TIME_TO_HZ(duetime_ticks); -+ repeat_hz = MSEC_TO_HZ(period_ms); -+ return wrap_set_timer(nt_timer, expires_hz, repeat_hz, kdpc); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeSetTimer,3) -+ (struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks, -+ struct kdpc *kdpc) -+{ -+ TIMERENTER("%p, %lld, %p", nt_timer, duetime_ticks, kdpc); -+ return KeSetTimerEx(nt_timer, duetime_ticks, 0, kdpc); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeCancelTimer,1) -+ (struct nt_timer *nt_timer) -+{ -+ struct wrap_timer *wrap_timer; -+ int ret; -+ -+ TIMERENTER("%p", nt_timer); -+ wrap_timer = nt_timer->wrap_timer; -+ if (!wrap_timer) { -+ ERROR("invalid wrap_timer"); -+ return TRUE; -+ } -+#ifdef TIMER_DEBUG -+ BUG_ON(wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC); -+#endif -+ /* disable timer before deleting so if it is periodic timer, it -+ * won't be re-armed after deleting */ -+ wrap_timer->repeat = 0; -+ ret = del_timer_sync(&wrap_timer->timer); -+ /* the documentation for KeCancelTimer suggests the DPC is -+ * deqeued, but actually DPC is left to run */ -+ if (ret) -+ TIMEREXIT(return TRUE); -+ else -+ TIMEREXIT(return FALSE); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeReadStateTimer,1) -+ (struct nt_timer *nt_timer) -+{ -+ if (nt_timer->dh.signal_state) -+ return TRUE; -+ else -+ return FALSE; -+} -+ -+wstdcall void WIN_FUNC(KeInitializeDpc,3) -+ (struct kdpc *kdpc, void *func, void *ctx) -+{ -+ ENTER3("%p, %p, %p", kdpc, func, ctx); -+ memset(kdpc, 0, sizeof(*kdpc)); -+ kdpc->func = func; -+ kdpc->ctx = ctx; -+ InitializeListHead(&kdpc->list); -+} -+ -+static void kdpc_worker(struct work_struct *dummy) -+{ -+ struct nt_list *entry; -+ struct kdpc *kdpc; -+ unsigned long flags; -+ KIRQL irql; -+ -+ WORKENTER(""); -+ irql = raise_irql(DISPATCH_LEVEL); -+ while (1) { -+ spin_lock_irqsave(&kdpc_list_lock, flags); -+ entry = RemoveHeadList(&kdpc_list); -+ if (entry) { -+ kdpc = container_of(entry, struct kdpc, list); -+ assert(kdpc->queued); -+ kdpc->queued = 0; -+ } else -+ kdpc = NULL; -+ spin_unlock_irqrestore(&kdpc_list_lock, flags); -+ if (!kdpc) -+ break; -+ WORKTRACE("%p, %p, %p, %p, %p", kdpc, kdpc->func, kdpc->ctx, -+ kdpc->arg1, kdpc->arg2); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ LIN2WIN4(kdpc->func, kdpc, kdpc->ctx, kdpc->arg1, kdpc->arg2); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ } -+ lower_irql(irql); -+ WORKEXIT(return); -+} -+ -+wstdcall void WIN_FUNC(KeFlushQueuedDpcs,0) -+ (void) -+{ -+ kdpc_worker(NULL); -+} -+ -+BOOLEAN queue_kdpc(struct kdpc *kdpc) -+{ -+ BOOLEAN ret; -+ unsigned long flags; -+ -+ WORKENTER("%p", kdpc); -+ spin_lock_irqsave(&kdpc_list_lock, flags); -+ if (kdpc->queued) -+ ret = FALSE; -+ else { -+ if (unlikely(kdpc->importance == HighImportance)) -+ InsertHeadList(&kdpc_list, &kdpc->list); -+ else -+ InsertTailList(&kdpc_list, &kdpc->list); -+ kdpc->queued = 1; -+ ret = TRUE; -+ } -+ spin_unlock_irqrestore(&kdpc_list_lock, flags); -+ if (ret == TRUE) -+ queue_work(ntos_wq, &kdpc_work); -+ WORKTRACE("%d", ret); -+ return ret; -+} -+ -+BOOLEAN dequeue_kdpc(struct kdpc *kdpc) -+{ -+ BOOLEAN ret; -+ unsigned long flags; -+ -+ WORKENTER("%p", kdpc); -+ spin_lock_irqsave(&kdpc_list_lock, flags); -+ if (kdpc->queued) { -+ RemoveEntryList(&kdpc->list); -+ kdpc->queued = 0; -+ ret = TRUE; -+ } else -+ ret = FALSE; -+ spin_unlock_irqrestore(&kdpc_list_lock, flags); -+ WORKTRACE("%d", ret); -+ return ret; -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeInsertQueueDpc,3) -+ (struct kdpc *kdpc, void *arg1, void *arg2) -+{ -+ WORKENTER("%p, %p, %p", kdpc, arg1, arg2); -+ kdpc->arg1 = arg1; -+ kdpc->arg2 = arg2; -+ return queue_kdpc(kdpc); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeRemoveQueueDpc,1) -+ (struct kdpc *kdpc) -+{ -+ return dequeue_kdpc(kdpc); -+} -+ -+wstdcall void WIN_FUNC(KeSetImportanceDpc,2) -+ (struct kdpc *kdpc, enum kdpc_importance importance) -+{ -+ kdpc->importance = importance; -+} -+ -+static void ntos_work_worker(struct work_struct *dummy) -+{ -+ struct ntos_work_item *ntos_work_item; -+ struct nt_list *cur; -+ -+ while (1) { -+ spin_lock_bh(&ntos_work_lock); -+ cur = RemoveHeadList(&ntos_work_list); -+ spin_unlock_bh(&ntos_work_lock); -+ if (!cur) -+ break; -+ ntos_work_item = container_of(cur, struct ntos_work_item, list); -+ WORKTRACE("%p: executing %p, %p, %p", current, -+ ntos_work_item->func, ntos_work_item->arg1, -+ ntos_work_item->arg2); -+ LIN2WIN2(ntos_work_item->func, ntos_work_item->arg1, -+ ntos_work_item->arg2); -+ kfree(ntos_work_item); -+ } -+ WORKEXIT(return); -+} -+ -+int schedule_ntos_work_item(NTOS_WORK_FUNC func, void *arg1, void *arg2) -+{ -+ struct ntos_work_item *ntos_work_item; -+ -+ WORKENTER("adding work: %p, %p, %p", func, arg1, arg2); -+ ntos_work_item = kmalloc(sizeof(*ntos_work_item), irql_gfp()); -+ if (!ntos_work_item) { -+ ERROR("couldn't allocate memory"); -+ return -ENOMEM; -+ } -+ ntos_work_item->func = func; -+ ntos_work_item->arg1 = arg1; -+ ntos_work_item->arg2 = arg2; -+ spin_lock_bh(&ntos_work_lock); -+ InsertTailList(&ntos_work_list, &ntos_work_item->list); -+ spin_unlock_bh(&ntos_work_lock); -+ queue_work(ntos_wq, &ntos_work); -+ WORKEXIT(return 0); -+} -+ -+wstdcall void WIN_FUNC(KeInitializeSpinLock,1) -+ (NT_SPIN_LOCK *lock) -+{ -+ ENTER6("%p", lock); -+ nt_spin_lock_init(lock); -+} -+ -+wstdcall void WIN_FUNC(KeAcquireSpinLock,2) -+ (NT_SPIN_LOCK *lock, KIRQL *irql) -+{ -+ ENTER6("%p", lock); -+ *irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL); -+} -+ -+wstdcall void WIN_FUNC(KeReleaseSpinLock,2) -+ (NT_SPIN_LOCK *lock, KIRQL oldirql) -+{ -+ ENTER6("%p", lock); -+ nt_spin_unlock_irql(lock, oldirql); -+} -+ -+wstdcall void WIN_FUNC(KeAcquireSpinLockAtDpcLevel,1) -+ (NT_SPIN_LOCK *lock) -+{ -+ ENTER6("%p", lock); -+ nt_spin_lock(lock); -+} -+ -+wstdcall void WIN_FUNC(KeReleaseSpinLockFromDpcLevel,1) -+ (NT_SPIN_LOCK *lock) -+{ -+ ENTER6("%p", lock); -+ nt_spin_unlock(lock); -+} -+ -+wstdcall void WIN_FUNC(KeRaiseIrql,2) -+ (KIRQL newirql, KIRQL *oldirql) -+{ -+ ENTER6("%d", newirql); -+ *oldirql = raise_irql(newirql); -+} -+ -+wstdcall KIRQL WIN_FUNC(KeRaiseIrqlToDpcLevel,0) -+ (void) -+{ -+ return raise_irql(DISPATCH_LEVEL); -+} -+ -+wstdcall void WIN_FUNC(KeLowerIrql,1) -+ (KIRQL irql) -+{ -+ ENTER6("%d", irql); -+ lower_irql(irql); -+} -+ -+wstdcall KIRQL WIN_FUNC(KeAcquireSpinLockRaiseToDpc,1) -+ (NT_SPIN_LOCK *lock) -+{ -+ ENTER6("%p", lock); -+ return nt_spin_lock_irql(lock, DISPATCH_LEVEL); -+} -+ -+wstdcall void *WIN_FUNC(ExAllocatePoolWithTag,3) -+ (enum pool_type pool_type, SIZE_T size, ULONG tag) -+{ -+ void *addr; -+ -+ ENTER4("pool_type: %d, size: %zu, tag: 0x%x", pool_type, size, tag); -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ if (size < PAGE_SIZE) -+ addr = kmalloc(size, irql_gfp()); -+ else { -+ if (irql_gfp() & GFP_ATOMIC) { -+ addr = __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM, -+ PAGE_KERNEL); -+ TRACE1("%p, %zu", addr, size); -+ } else { -+ addr = vmalloc(size); -+ TRACE1("%p, %zu", addr, size); -+ } -+ } -+ DBG_BLOCK(1) { -+ if (addr) -+ TRACE4("addr: %p, %zu", addr, size); -+ else -+ TRACE1("failed: %zu", size); -+ } -+ return addr; -+} -+WIN_FUNC_DECL(ExAllocatePoolWithTag,3) -+ -+wstdcall void WIN_FUNC(ExFreePoolWithTag,2) -+ (void *addr, ULONG tag) -+{ -+ TRACE4("%p", addr); -+ if ((unsigned long)addr < VMALLOC_START || -+ (unsigned long)addr >= VMALLOC_END) -+ kfree(addr); -+ else -+ vfree(addr); -+ -+ EXIT4(return); -+} -+ -+wstdcall void WIN_FUNC(ExFreePool,1) -+ (void *addr) -+{ -+ ExFreePoolWithTag(addr, 0); -+} -+WIN_FUNC_DECL(ExFreePool,1) -+ -+wstdcall void WIN_FUNC(ExInitializeNPagedLookasideList,7) -+ (struct npaged_lookaside_list *lookaside, -+ LOOKASIDE_ALLOC_FUNC *alloc_func, LOOKASIDE_FREE_FUNC *free_func, -+ ULONG flags, SIZE_T size, ULONG tag, USHORT depth) -+{ -+ ENTER3("lookaside: %p, size: %zu, flags: %u, head: %p, " -+ "alloc: %p, free: %p", lookaside, size, flags, -+ lookaside, alloc_func, free_func); -+ -+ memset(lookaside, 0, sizeof(*lookaside)); -+ -+ lookaside->size = size; -+ lookaside->tag = tag; -+ lookaside->depth = 4; -+ lookaside->maxdepth = 256; -+ lookaside->pool_type = NonPagedPool; -+ -+ if (alloc_func) -+ lookaside->alloc_func = alloc_func; -+ else -+ lookaside->alloc_func = WIN_FUNC_PTR(ExAllocatePoolWithTag,3); -+ if (free_func) -+ lookaside->free_func = free_func; -+ else -+ lookaside->free_func = WIN_FUNC_PTR(ExFreePool,1); -+ -+#ifndef CONFIG_X86_64 -+ nt_spin_lock_init(&lookaside->obsolete); -+#endif -+ EXIT3(return); -+} -+ -+wstdcall void WIN_FUNC(ExDeleteNPagedLookasideList,1) -+ (struct npaged_lookaside_list *lookaside) -+{ -+ struct nt_slist *entry; -+ -+ ENTER3("lookaside = %p", lookaside); -+ while ((entry = ExpInterlockedPopEntrySList(&lookaside->head))) -+ LIN2WIN1(lookaside->free_func, entry); -+ EXIT3(return); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ExCreateCallback,4) -+ (struct callback_object **object, struct object_attributes *attributes, -+ BOOLEAN create, BOOLEAN allow_multiple_callbacks) -+{ -+ struct callback_object *obj; -+ -+ ENTER2(""); -+ spin_lock_bh(&ntoskernel_lock); -+ nt_list_for_each_entry(obj, &callback_objects, callback_funcs) { -+ if (obj->attributes == attributes) { -+ spin_unlock_bh(&ntoskernel_lock); -+ *object = obj; -+ return STATUS_SUCCESS; -+ } -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ obj = allocate_object(sizeof(struct callback_object), -+ OBJECT_TYPE_CALLBACK, NULL); -+ if (!obj) -+ EXIT2(return STATUS_INSUFFICIENT_RESOURCES); -+ InitializeListHead(&obj->callback_funcs); -+ nt_spin_lock_init(&obj->lock); -+ obj->allow_multiple_callbacks = allow_multiple_callbacks; -+ obj->attributes = attributes; -+ *object = obj; -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall void *WIN_FUNC(ExRegisterCallback,3) -+ (struct callback_object *object, PCALLBACK_FUNCTION func, void *context) -+{ -+ struct callback_func *callback; -+ KIRQL irql; -+ -+ ENTER2(""); -+ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); -+ if (object->allow_multiple_callbacks == FALSE && -+ !IsListEmpty(&object->callback_funcs)) { -+ nt_spin_unlock_irql(&object->lock, irql); -+ EXIT2(return NULL); -+ } -+ nt_spin_unlock_irql(&object->lock, irql); -+ callback = kmalloc(sizeof(*callback), GFP_KERNEL); -+ if (!callback) { -+ ERROR("couldn't allocate memory"); -+ return NULL; -+ } -+ callback->func = func; -+ callback->context = context; -+ callback->object = object; -+ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); -+ InsertTailList(&object->callback_funcs, &callback->list); -+ nt_spin_unlock_irql(&object->lock, irql); -+ EXIT2(return callback); -+} -+ -+wstdcall void WIN_FUNC(ExUnregisterCallback,1) -+ (struct callback_func *callback) -+{ -+ struct callback_object *object; -+ KIRQL irql; -+ -+ ENTER3("%p", callback); -+ if (!callback) -+ return; -+ object = callback->object; -+ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); -+ RemoveEntryList(&callback->list); -+ nt_spin_unlock_irql(&object->lock, irql); -+ kfree(callback); -+ return; -+} -+ -+wstdcall void WIN_FUNC(ExNotifyCallback,3) -+ (struct callback_object *object, void *arg1, void *arg2) -+{ -+ struct callback_func *callback; -+ KIRQL irql; -+ -+ ENTER3("%p", object); -+ irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL); -+ nt_list_for_each_entry(callback, &object->callback_funcs, list) { -+ LIN2WIN3(callback->func, callback->context, arg1, arg2); -+ } -+ nt_spin_unlock_irql(&object->lock, irql); -+ return; -+} -+ -+/* check and set signaled state; should be called with dispatcher_lock held */ -+/* @grab indicates if the event should be grabbed or checked -+ * - note that a semaphore may stay in signaled state for multiple -+ * 'grabs' if the count is > 1 */ -+static int grab_object(struct dispatcher_header *dh, -+ struct task_struct *thread, int grab) -+{ -+ EVENTTRACE("%p, %p, %d, %d", dh, thread, grab, dh->signal_state); -+ if (unlikely(is_mutex_object(dh))) { -+ struct nt_mutex *nt_mutex; -+ nt_mutex = container_of(dh, struct nt_mutex, dh); -+ EVENTTRACE("%p, %p, %d, %p, %d", nt_mutex, -+ nt_mutex->owner_thread, dh->signal_state, -+ thread, grab); -+ /* either no thread owns the mutex or this thread owns -+ * it */ -+ assert(dh->signal_state == 1 && nt_mutex->owner_thread == NULL); -+ assert(dh->signal_state < 1 && nt_mutex->owner_thread != NULL); -+ if ((dh->signal_state == 1 && nt_mutex->owner_thread == NULL) || -+ nt_mutex->owner_thread == thread) { -+ if (grab) { -+ dh->signal_state--; -+ nt_mutex->owner_thread = thread; -+ } -+ EVENTEXIT(return 1); -+ } -+ } else if (dh->signal_state > 0) { -+ /* to grab, decrement signal_state for synchronization -+ * or semaphore objects */ -+ if (grab && (is_synch_object(dh) || is_semaphore_object(dh))) -+ dh->signal_state--; -+ EVENTEXIT(return 1); -+ } -+ EVENTEXIT(return 0); -+} -+ -+/* this function should be called holding dispatcher_lock */ -+static void object_signaled(struct dispatcher_header *dh) -+{ -+ struct nt_list *cur, *next; -+ struct wait_block *wb; -+ -+ EVENTENTER("%p", dh); -+ nt_list_for_each_safe(cur, next, &dh->wait_blocks) { -+ wb = container_of(cur, struct wait_block, list); -+ assert(wb->thread != NULL); -+ assert(wb->object == NULL); -+ if (!grab_object(dh, wb->thread, 1)) -+ continue; -+ EVENTTRACE("%p (%p): waking %p", dh, wb, wb->thread); -+ RemoveEntryList(cur); -+ wb->object = dh; -+ *(wb->wait_done) = 1; -+ wake_up_process(wb->thread); -+ } -+ EVENTEXIT(return); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(KeWaitForMultipleObjects,8) -+ (ULONG count, void *object[], enum wait_type wait_type, -+ KWAIT_REASON wait_reason, KPROCESSOR_MODE wait_mode, -+ BOOLEAN alertable, LARGE_INTEGER *timeout, -+ struct wait_block *wait_block_array) -+{ -+ int i, res = 0, wait_count, wait_done; -+ typeof(jiffies) wait_hz = 0; -+ struct wait_block *wb, wb_array[THREAD_WAIT_OBJECTS]; -+ struct dispatcher_header *dh; -+ KIRQL irql = current_irql(); -+ -+ EVENTENTER("%p, %d, %u, %p", current, count, wait_type, timeout); -+ -+ if (count > MAX_WAIT_OBJECTS || -+ (count > THREAD_WAIT_OBJECTS && wait_block_array == NULL)) -+ EVENTEXIT(return STATUS_INVALID_PARAMETER); -+ -+ if (wait_block_array == NULL) -+ wb = wb_array; -+ else -+ wb = wait_block_array; -+ -+ /* If *timeout == 0: In the case of WaitAny, if an object can -+ * be grabbed (object is in signaled state), grab and -+ * return. In the case of WaitAll, we have to first make sure -+ * all objects can be grabbed. If any/some of them can't be -+ * grabbed, either we return STATUS_TIMEOUT or wait for them, -+ * depending on how to satisfy wait. If all of them can be -+ * grabbed, we will grab them in the next loop below */ -+ -+ spin_lock_bh(&dispatcher_lock); -+ for (i = wait_count = 0; i < count; i++) { -+ dh = object[i]; -+ EVENTTRACE("%p: event %p (%d)", current, dh, dh->signal_state); -+ /* wait_type == 1 for WaitAny, 0 for WaitAll */ -+ if (grab_object(dh, current, wait_type)) { -+ if (wait_type == WaitAny) { -+ spin_unlock_bh(&dispatcher_lock); -+ EVENTEXIT(return STATUS_WAIT_0 + i); -+ } -+ } else { -+ EVENTTRACE("%p: wait for %p", current, dh); -+ wait_count++; -+ } -+ } -+ -+ if (timeout && *timeout == 0 && wait_count) { -+ spin_unlock_bh(&dispatcher_lock); -+ EVENTEXIT(return STATUS_TIMEOUT); -+ } -+ -+ /* get the list of objects the thread needs to wait on and add -+ * the thread on the wait list for each such object */ -+ /* if *timeout == 0, this step will grab all the objects */ -+ wait_done = 0; -+ for (i = 0; i < count; i++) { -+ dh = object[i]; -+ EVENTTRACE("%p: event %p (%d)", current, dh, dh->signal_state); -+ wb[i].object = NULL; -+ if (grab_object(dh, current, 1)) { -+ EVENTTRACE("%p: no wait for %p (%d)", -+ current, dh, dh->signal_state); -+ /* mark that we are not waiting on this object */ -+ wb[i].thread = NULL; -+ } else { -+ wb[i].wait_done = &wait_done; -+ wb[i].thread = current; -+ EVENTTRACE("%p: wait for %p", current, dh); -+ InsertTailList(&dh->wait_blocks, &wb[i].list); -+ } -+ } -+ spin_unlock_bh(&dispatcher_lock); -+ if (wait_count == 0) -+ EVENTEXIT(return STATUS_SUCCESS); -+ -+ assert(timeout == NULL || *timeout != 0); -+ if (timeout == NULL) -+ wait_hz = 0; -+ else -+ wait_hz = SYSTEM_TIME_TO_HZ(*timeout); -+ -+ if (irql >= DISPATCH_LEVEL) { -+ WARNING("attempt to wait with irql %d", irql); -+ EVENTEXIT(return STATUS_INVALID_PARAMETER); -+ } -+ EVENTTRACE("%p: sleep for %ld on %p", current, wait_hz, &wait_done); -+ /* we don't honor 'alertable' - according to description for -+ * this, even if waiting in non-alertable state, thread may be -+ * alerted in some circumstances */ -+ while (wait_count) { -+ res = wait_condition(wait_done, wait_hz, TASK_INTERRUPTIBLE); -+ spin_lock_bh(&dispatcher_lock); -+ EVENTTRACE("%p woke up: %d, %d", current, res, wait_done); -+ /* the event may have been set by the time -+ * wrap_wait_event returned and spinlock obtained, so -+ * don't rely on value of 'res' - check event status */ -+ if (!wait_done) { -+ assert(res <= 0); -+ /* timed out or interrupted; remove from wait list */ -+ for (i = 0; i < count; i++) { -+ if (!wb[i].thread) -+ continue; -+ EVENTTRACE("%p: timedout, dequeue %p (%p)", -+ current, object[i], wb[i].object); -+ assert(wb[i].object == NULL); -+ RemoveEntryList(&wb[i].list); -+ } -+ spin_unlock_bh(&dispatcher_lock); -+ if (res < 0) -+ EVENTEXIT(return STATUS_ALERTED); -+ else -+ EVENTEXIT(return STATUS_TIMEOUT); -+ } -+ assert(res > 0); -+ /* woken because object(s) signaled */ -+ for (i = 0; wait_count && i < count; i++) { -+ if (!wb[i].thread || !wb[i].object) -+ continue; -+ DBG_BLOCK(1) { -+ if (wb[i].object != object[i]) { -+ EVENTTRACE("oops %p != %p", -+ wb[i].object, object[i]); -+ continue; -+ } -+ } -+ wait_count--; -+ if (wait_type == WaitAny) { -+ int j; -+ /* done; remove from rest of wait list */ -+ for (j = i + 1; j < count; j++) { -+ if (wb[j].thread && !wb[j].object) -+ RemoveEntryList(&wb[j].list); -+ } -+ spin_unlock_bh(&dispatcher_lock); -+ EVENTEXIT(return STATUS_WAIT_0 + i); -+ } -+ } -+ wait_done = 0; -+ spin_unlock_bh(&dispatcher_lock); -+ if (wait_count == 0) -+ EVENTEXIT(return STATUS_SUCCESS); -+ -+ /* this thread is still waiting for more objects, so -+ * let it wait for remaining time and those objects */ -+ if (timeout) -+ wait_hz = res; -+ else -+ wait_hz = 0; -+ } -+ /* should never reach here, but compiler wants return value */ -+ ERROR("%p: wait_hz: %ld", current, wait_hz); -+ EVENTEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(KeWaitForSingleObject,5) -+ (void *object, KWAIT_REASON wait_reason, KPROCESSOR_MODE wait_mode, -+ BOOLEAN alertable, LARGE_INTEGER *timeout) -+{ -+ return KeWaitForMultipleObjects(1, &object, WaitAny, wait_reason, -+ wait_mode, alertable, timeout, NULL); -+} -+ -+wstdcall void WIN_FUNC(KeInitializeEvent,3) -+ (struct nt_event *nt_event, enum event_type type, BOOLEAN state) -+{ -+ EVENTENTER("event = %p, type = %d, state = %d", nt_event, type, state); -+ initialize_object(&nt_event->dh, (enum dh_type)type, state); -+ EVENTEXIT(return); -+} -+ -+wstdcall LONG WIN_FUNC(KeSetEvent,3) -+ (struct nt_event *nt_event, KPRIORITY incr, BOOLEAN wait) -+{ -+ LONG old_state; -+ -+ EVENTENTER("%p, %d", nt_event, nt_event->dh.type); -+ if (wait == TRUE) -+ WARNING("wait = %d, not yet implemented", wait); -+ spin_lock_bh(&dispatcher_lock); -+ old_state = nt_event->dh.signal_state; -+ nt_event->dh.signal_state = 1; -+ if (old_state == 0) -+ object_signaled(&nt_event->dh); -+ spin_unlock_bh(&dispatcher_lock); -+ EVENTEXIT(return old_state); -+} -+ -+wstdcall void WIN_FUNC(KeClearEvent,1) -+ (struct nt_event *nt_event) -+{ -+ EVENTENTER("%p", nt_event); -+ nt_event->dh.signal_state = 0; -+ EVENTEXIT(return); -+} -+ -+wstdcall LONG WIN_FUNC(KeResetEvent,1) -+ (struct nt_event *nt_event) -+{ -+ LONG old_state; -+ -+ EVENTENTER("%p", nt_event); -+ old_state = xchg(&nt_event->dh.signal_state, 0); -+ EVENTEXIT(return old_state); -+} -+ -+wstdcall LONG WIN_FUNC(KeReadStateEvent,1) -+ (struct nt_event *nt_event) -+{ -+ LONG state; -+ -+ state = nt_event->dh.signal_state; -+ EVENTTRACE("%d", state); -+ return state; -+} -+ -+wstdcall void WIN_FUNC(KeInitializeMutex,2) -+ (struct nt_mutex *mutex, ULONG level) -+{ -+ EVENTENTER("%p", mutex); -+ initialize_object(&mutex->dh, MutexObject, 1); -+ mutex->dh.size = sizeof(*mutex); -+ InitializeListHead(&mutex->list); -+ mutex->abandoned = FALSE; -+ mutex->apc_disable = 1; -+ mutex->owner_thread = NULL; -+ EVENTEXIT(return); -+} -+ -+wstdcall LONG WIN_FUNC(KeReleaseMutex,2) -+ (struct nt_mutex *mutex, BOOLEAN wait) -+{ -+ LONG ret; -+ struct task_struct *thread; -+ -+ EVENTENTER("%p, %d, %p", mutex, wait, current); -+ if (wait == TRUE) -+ WARNING("wait: %d", wait); -+ thread = current; -+ spin_lock_bh(&dispatcher_lock); -+ EVENTTRACE("%p, %p, %p, %d", mutex, thread, mutex->owner_thread, -+ mutex->dh.signal_state); -+ if ((mutex->owner_thread == thread) && (mutex->dh.signal_state <= 0)) { -+ ret = mutex->dh.signal_state++; -+ if (ret == 0) { -+ mutex->owner_thread = NULL; -+ object_signaled(&mutex->dh); -+ } -+ } else { -+ ret = STATUS_MUTANT_NOT_OWNED; -+ WARNING("invalid mutex: %p, %p, %p", mutex, mutex->owner_thread, -+ thread); -+ } -+ EVENTTRACE("%p, %p, %p, %d", mutex, thread, mutex->owner_thread, -+ mutex->dh.signal_state); -+ spin_unlock_bh(&dispatcher_lock); -+ EVENTEXIT(return ret); -+} -+ -+wstdcall void WIN_FUNC(KeInitializeSemaphore,3) -+ (struct nt_semaphore *semaphore, LONG count, LONG limit) -+{ -+ EVENTENTER("%p: %d", semaphore, count); -+ /* if limit > 1, we need to satisfy as many waits (until count -+ * becomes 0); so we keep decrementing count every time a wait -+ * is satisfied */ -+ initialize_object(&semaphore->dh, SemaphoreObject, count); -+ semaphore->dh.size = sizeof(*semaphore); -+ semaphore->limit = limit; -+ EVENTEXIT(return); -+} -+ -+wstdcall LONG WIN_FUNC(KeReleaseSemaphore,4) -+ (struct nt_semaphore *semaphore, KPRIORITY incr, LONG adjustment, -+ BOOLEAN wait) -+{ -+ LONG ret; -+ -+ EVENTENTER("%p", semaphore); -+ spin_lock_bh(&dispatcher_lock); -+ ret = semaphore->dh.signal_state; -+ assert(ret >= 0); -+ if (semaphore->dh.signal_state + adjustment <= semaphore->limit) -+ semaphore->dh.signal_state += adjustment; -+ else { -+ WARNING("releasing %d over limit %d", adjustment, -+ semaphore->limit); -+ semaphore->dh.signal_state = semaphore->limit; -+ } -+ if (semaphore->dh.signal_state > 0) -+ object_signaled(&semaphore->dh); -+ spin_unlock_bh(&dispatcher_lock); -+ EVENTEXIT(return ret); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(KeDelayExecutionThread,3) -+ (KPROCESSOR_MODE wait_mode, BOOLEAN alertable, LARGE_INTEGER *interval) -+{ -+ int res; -+ long timeout; -+ -+ if (wait_mode != 0) -+ ERROR("invalid wait_mode %d", wait_mode); -+ -+ timeout = SYSTEM_TIME_TO_HZ(*interval); -+ EVENTTRACE("%p, %lld, %ld", current, *interval, timeout); -+ if (timeout <= 0) -+ EVENTEXIT(return STATUS_SUCCESS); -+ -+ if (alertable) -+ set_current_state(TASK_INTERRUPTIBLE); -+ else -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ -+ res = schedule_timeout(timeout); -+ EVENTTRACE("%p, %d", current, res); -+ if (res == 0) -+ EVENTEXIT(return STATUS_SUCCESS); -+ else -+ EVENTEXIT(return STATUS_ALERTED); -+} -+ -+wstdcall ULONGLONG WIN_FUNC(KeQueryInterruptTime,0) -+ (void) -+{ -+ EXIT5(return jiffies * TICKSPERJIFFY); -+} -+ -+wstdcall ULONG WIN_FUNC(KeQueryTimeIncrement,0) -+ (void) -+{ -+ EXIT5(return TICKSPERSEC / HZ); -+} -+ -+wstdcall void WIN_FUNC(KeQuerySystemTime,1) -+ (LARGE_INTEGER *time) -+{ -+ *time = ticks_1601(); -+ TRACE5("%llu, %lu", *time, jiffies); -+} -+ -+wstdcall void WIN_FUNC(KeQueryTickCount,1) -+ (LARGE_INTEGER *count) -+{ -+ *count = jiffies; -+} -+ -+wstdcall LARGE_INTEGER WIN_FUNC(KeQueryPerformanceCounter,1) -+ (LARGE_INTEGER *counter) -+{ -+ if (counter) -+ *counter = HZ; -+ return jiffies; -+} -+ -+wstdcall KAFFINITY WIN_FUNC(KeQueryActiveProcessors,0) -+ (void) -+{ -+ int i, n; -+ KAFFINITY bits = 0; -+ n = num_online_cpus(); -+ for (i = 0; i < n; i++) -+ bits = (bits << 1) | 1; -+ return bits; -+} -+ -+struct nt_thread *get_current_nt_thread(void) -+{ -+ struct task_struct *task = current; -+ struct nt_thread *thread; -+ struct common_object_header *header; -+ -+ TRACE6("task: %p", task); -+ thread = NULL; -+ spin_lock_bh(&ntoskernel_lock); -+ nt_list_for_each_entry(header, &object_list, list) { -+ TRACE6("%p, %d", header, header->type); -+ if (header->type != OBJECT_TYPE_NT_THREAD) -+ break; -+ thread = HEADER_TO_OBJECT(header); -+ TRACE6("%p, %p", thread, thread->task); -+ if (thread->task == task) -+ break; -+ else -+ thread = NULL; -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ if (thread == NULL) -+ TRACE4("couldn't find thread for task %p, %d", task, task->pid); -+ TRACE6("%p", thread); -+ return thread; -+} -+ -+static struct task_struct *get_nt_thread_task(struct nt_thread *thread) -+{ -+ struct task_struct *task; -+ struct common_object_header *header; -+ -+ TRACE6("%p", thread); -+ task = NULL; -+ spin_lock_bh(&ntoskernel_lock); -+ nt_list_for_each_entry(header, &object_list, list) { -+ TRACE6("%p, %d", header, header->type); -+ if (header->type != OBJECT_TYPE_NT_THREAD) -+ break; -+ if (thread == HEADER_TO_OBJECT(header)) { -+ task = thread->task; -+ break; -+ } -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ if (task == NULL) -+ TRACE2("%p: couldn't find task for %p", current, thread); -+ return task; -+} -+ -+static struct nt_thread *create_nt_thread(struct task_struct *task) -+{ -+ struct nt_thread *thread; -+ thread = allocate_object(sizeof(*thread), OBJECT_TYPE_NT_THREAD, NULL); -+ if (!thread) { -+ ERROR("couldn't allocate thread object"); -+ EXIT2(return NULL); -+ } -+ thread->task = task; -+ if (task) -+ thread->pid = task->pid; -+ else -+ thread->pid = 0; -+ nt_spin_lock_init(&thread->lock); -+ InitializeListHead(&thread->irps); -+ initialize_object(&thread->dh, ThreadObject, 0); -+ thread->dh.size = sizeof(*thread); -+ thread->prio = LOW_PRIORITY; -+ return thread; -+} -+ -+wstdcall struct nt_thread *WIN_FUNC(KeGetCurrentThread,0) -+ (void) -+{ -+ struct nt_thread *thread = get_current_nt_thread(); -+ TRACE2("%p, %p", thread, current); -+ return thread; -+} -+ -+wstdcall KPRIORITY WIN_FUNC(KeQueryPriorityThread,1) -+ (struct nt_thread *thread) -+{ -+ KPRIORITY prio; -+ struct task_struct *task; -+ -+ TRACE2("%p", thread); -+#ifdef CONFIG_X86_64 -+ /* sis163u driver for amd64 passes 0x1f from thread created by -+ * PsCreateSystemThread - no idea what is 0x1f */ -+ if (thread == (void *)0x1f) -+ thread = get_current_nt_thread(); -+#endif -+ if (!thread) { -+ TRACE2("invalid thread"); -+ EXIT2(return LOW_REALTIME_PRIORITY); -+ } -+ task = get_nt_thread_task(thread); -+ if (!task) { -+ TRACE2("couldn't find task for thread: %p", thread); -+ EXIT2(return LOW_REALTIME_PRIORITY); -+ } -+ -+ prio = thread->prio; -+ -+ TRACE2("%d", prio); -+ return prio; -+} -+ -+wstdcall KPRIORITY WIN_FUNC(KeSetPriorityThread,2) -+ (struct nt_thread *thread, KPRIORITY prio) -+{ -+ KPRIORITY old_prio; -+ struct task_struct *task; -+ -+ TRACE2("thread: %p, priority = %u", thread, prio); -+#ifdef CONFIG_X86_64 -+ if (thread == (void *)0x1f) -+ thread = get_current_nt_thread(); -+#endif -+ if (!thread) { -+ TRACE2("invalid thread"); -+ EXIT2(return LOW_REALTIME_PRIORITY); -+ } -+ task = get_nt_thread_task(thread); -+ if (!task) { -+ TRACE2("couldn't find task for thread: %p", thread); -+ EXIT2(return LOW_REALTIME_PRIORITY); -+ } -+ -+ old_prio = thread->prio; -+ thread->prio = prio; -+ -+ TRACE2("%d, %d", old_prio, thread->prio); -+ return old_prio; -+} -+ -+struct thread_trampoline { -+ void (*func)(void *) wstdcall; -+ void *ctx; -+ struct nt_thread *thread; -+ struct completion started; -+}; -+ -+static int ntdriver_thread(void *data) -+{ -+ struct thread_trampoline *thread_tramp = data; -+ /* yes, a tramp! */ -+ typeof(thread_tramp->func) func = thread_tramp->func; -+ typeof(thread_tramp->ctx) ctx = thread_tramp->ctx; -+ -+ thread_tramp->thread->task = current; -+ thread_tramp->thread->pid = current->pid; -+ TRACE2("thread: %p, task: %p (%d)", thread_tramp->thread, -+ current, current->pid); -+ complete(&thread_tramp->started); -+ -+#ifdef PF_NOFREEZE -+ current->flags |= PF_NOFREEZE; -+#endif -+ strncpy(current->comm, "ntdriver", sizeof(current->comm)); -+ current->comm[sizeof(current->comm)-1] = 0; -+ LIN2WIN1(func, ctx); -+ ERROR("task: %p", current); -+ return 0; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(PsCreateSystemThread,7) -+ (void **handle, ULONG access, void *obj_attr, void *process, -+ void *client_id, void (*func)(void *) wstdcall, void *ctx) -+{ -+ struct thread_trampoline thread_tramp; -+ -+ ENTER2("handle = %p, access = %u, obj_attr = %p, process = %p, " -+ "client_id = %p, func = %p, context = %p", handle, access, -+ obj_attr, process, client_id, func, ctx); -+ -+ thread_tramp.thread = create_nt_thread(NULL); -+ if (!thread_tramp.thread) { -+ ERROR("couldn't allocate thread object"); -+ EXIT2(return STATUS_RESOURCES); -+ } -+ TRACE2("thread: %p", thread_tramp.thread); -+ thread_tramp.func = func; -+ thread_tramp.ctx = ctx; -+ init_completion(&thread_tramp.started); -+ -+ thread_tramp.thread->task = kthread_run(ntdriver_thread, -+ &thread_tramp, "ntdriver"); -+ if (IS_ERR(thread_tramp.thread->task)) { -+ free_object(thread_tramp.thread); -+ EXIT2(return STATUS_FAILURE); -+ } -+ TRACE2("created task: %p", thread_tramp.thread->task); -+ -+ wait_for_completion(&thread_tramp.started); -+ *handle = OBJECT_TO_HEADER(thread_tramp.thread); -+ TRACE2("created thread: %p, %p", thread_tramp.thread, *handle); -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(PsTerminateSystemThread,1) -+ (NTSTATUS status) -+{ -+ struct nt_thread *thread; -+ -+ TRACE2("%p, %08X", current, status); -+ thread = get_current_nt_thread(); -+ TRACE2("%p", thread); -+ if (thread) { -+ KeSetEvent((struct nt_event *)&thread->dh, 0, FALSE); -+ while (1) { -+ struct nt_list *ent; -+ struct irp *irp; -+ KIRQL irql; -+ irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL); -+ ent = RemoveHeadList(&thread->irps); -+ nt_spin_unlock_irql(&thread->lock, irql); -+ if (!ent) -+ break; -+ irp = container_of(ent, struct irp, thread_list); -+ IOTRACE("%p", irp); -+ IoCancelIrp(irp); -+ } -+ /* the driver may later query this status with -+ * ZwQueryInformationThread */ -+ thread->status = status; -+ } else -+ ERROR("couldn't find thread for task: %p", current); -+ -+ complete_and_exit(NULL, status); -+ ERROR("oops: %p, %d", thread->task, thread->pid); -+ return STATUS_FAILURE; -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeRemoveEntryDeviceQueue,2) -+ (struct kdevice_queue *dev_queue, struct kdevice_queue_entry *entry) -+{ -+ struct kdevice_queue_entry *e; -+ KIRQL irql; -+ -+ irql = nt_spin_lock_irql(&dev_queue->lock, DISPATCH_LEVEL); -+ nt_list_for_each_entry(e, &dev_queue->list, list) { -+ if (e == entry) { -+ RemoveEntryList(&e->list); -+ nt_spin_unlock_irql(&dev_queue->lock, irql); -+ return TRUE; -+ } -+ } -+ nt_spin_unlock_irql(&dev_queue->lock, irql); -+ return FALSE; -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeSynchronizeExecution,3) -+ (struct kinterrupt *interrupt, PKSYNCHRONIZE_ROUTINE synch_routine, -+ void *ctx) -+{ -+ BOOLEAN ret; -+ unsigned long flags; -+ -+ nt_spin_lock_irqsave(interrupt->actual_lock, flags); -+ ret = LIN2WIN1(synch_routine, ctx); -+ nt_spin_unlock_irqrestore(interrupt->actual_lock, flags); -+ TRACE6("%d", ret); -+ return ret; -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeRegisterBugCheckReasonCallback,4) -+ (void *callback_record, void *callback_routine, UINT reason, -+ char *component) -+{ -+ TRACE1("callback_record: %p, callback_routine: %p, reason: %d, " -+ "component: %s", callback_record, callback_routine, reason, -+ component); -+ TODO(); -+ return FALSE; -+} -+ -+wstdcall BOOLEAN WIN_FUNC(KeDeregisterBugCheckReasonCallback,1) -+ (void *callback_record) -+{ -+ TRACE1("callback_record: %p", callback_record); -+ TODO(); -+ return TRUE; -+} -+ -+wstdcall void *WIN_FUNC(MmAllocateContiguousMemorySpecifyCache,5) -+ (SIZE_T size, PHYSICAL_ADDRESS lowest, PHYSICAL_ADDRESS highest, -+ PHYSICAL_ADDRESS boundary, enum memory_caching_type cache_type) -+{ -+ void *addr; -+ gfp_t flags; -+ -+ ENTER2("%zu, 0x%llx, 0x%llx, 0x%llx, %d", size, lowest, -+ highest, boundary, cache_type); -+ flags = irql_gfp(); -+ addr = wrap_get_free_pages(flags, size); -+ TRACE2("%p, %zu, 0x%x", addr, size, flags); -+ if (addr && ((virt_to_phys(addr) + size) <= highest)) -+ EXIT2(return addr); -+#ifdef CONFIG_X86_64 -+ /* GFP_DMA is really only 16MB even on x86-64, but there is no -+ * other zone available */ -+ if (highest <= DMA_BIT_MASK(31)) -+ flags |= __GFP_DMA; -+ else if (highest <= DMA_BIT_MASK(32)) -+ flags |= __GFP_DMA32; -+#else -+ if (highest <= DMA_BIT_MASK(24)) -+ flags |= __GFP_DMA; -+ else if (highest > DMA_BIT_MASK(30)) -+ flags |= __GFP_HIGHMEM; -+#endif -+ if (addr) -+ free_pages((unsigned long)addr, get_order(size)); -+ addr = wrap_get_free_pages(flags, size); -+ TRACE2("%p, %zu, 0x%x", addr, size, flags); -+ return addr; -+} -+ -+wstdcall void WIN_FUNC(MmFreeContiguousMemorySpecifyCache,3) -+ (void *base, SIZE_T size, enum memory_caching_type cache_type) -+{ -+ TRACE2("%p, %zu", base, size); -+ free_pages((unsigned long)base, get_order(size)); -+} -+ -+wstdcall PHYSICAL_ADDRESS WIN_FUNC(MmGetPhysicalAddress,1) -+ (void *base) -+{ -+ unsigned long phy = virt_to_phys(base); -+ TRACE2("%p, %p", base, (void *)phy); -+ return phy; -+} -+ -+/* Atheros card with pciid 168C:0014 calls this function with 0xf0000 -+ * and 0xf6ef0 address, and then check for things that seem to be -+ * related to ACPI: "_SM_" and "_DMI_". This may be the hack they do -+ * to check if this card is installed in IBM thinkpads; we can -+ * probably get this device to work if we create a buffer with the -+ * strings as required by the driver and return virtual address for -+ * that address instead */ -+wstdcall void __iomem *WIN_FUNC(MmMapIoSpace,3) -+ (PHYSICAL_ADDRESS phys_addr, SIZE_T size, -+ enum memory_caching_type cache) -+{ -+ void __iomem *virt; -+ ENTER1("cache type: %d", cache); -+ if (cache == MmCached) -+ virt = ioremap(phys_addr, size); -+ else -+ virt = ioremap_nocache(phys_addr, size); -+ TRACE1("%llx, %zu, %p", phys_addr, size, virt); -+ return virt; -+} -+ -+wstdcall void WIN_FUNC(MmUnmapIoSpace,2) -+ (void __iomem *addr, SIZE_T size) -+{ -+ ENTER1("%p, %zu", addr, size); -+ iounmap(addr); -+ return; -+} -+ -+wstdcall ULONG WIN_FUNC(MmSizeOfMdl,2) -+ (void *base, ULONG length) -+{ -+ return sizeof(struct mdl) + -+ (sizeof(PFN_NUMBER) * SPAN_PAGES(base, length)); -+} -+ -+struct mdl *allocate_init_mdl(void *virt, ULONG length) -+{ -+ struct wrap_mdl *wrap_mdl; -+ struct mdl *mdl; -+ int mdl_size = MmSizeOfMdl(virt, length); -+ -+ if (mdl_size <= MDL_CACHE_SIZE) { -+ wrap_mdl = kmem_cache_alloc(mdl_cache, irql_gfp()); -+ if (!wrap_mdl) -+ return NULL; -+ spin_lock_bh(&dispatcher_lock); -+ InsertHeadList(&wrap_mdl_list, &wrap_mdl->list); -+ spin_unlock_bh(&dispatcher_lock); -+ mdl = wrap_mdl->mdl; -+ TRACE3("allocated mdl from cache: %p(%p), %p(%d)", -+ wrap_mdl, mdl, virt, length); -+ memset(mdl, 0, MDL_CACHE_SIZE); -+ MmInitializeMdl(mdl, virt, length); -+ /* mark the MDL as allocated from cache pool so when -+ * it is freed, we free it back to the pool */ -+ mdl->flags = MDL_ALLOCATED_FIXED_SIZE | MDL_CACHE_ALLOCATED; -+ } else { -+ wrap_mdl = -+ kmalloc(sizeof(*wrap_mdl) + mdl_size, irql_gfp()); -+ if (!wrap_mdl) -+ return NULL; -+ mdl = wrap_mdl->mdl; -+ TRACE3("allocated mdl from memory: %p(%p), %p(%d)", -+ wrap_mdl, mdl, virt, length); -+ spin_lock_bh(&dispatcher_lock); -+ InsertHeadList(&wrap_mdl_list, &wrap_mdl->list); -+ spin_unlock_bh(&dispatcher_lock); -+ memset(mdl, 0, mdl_size); -+ MmInitializeMdl(mdl, virt, length); -+ mdl->flags = MDL_ALLOCATED_FIXED_SIZE; -+ } -+ return mdl; -+} -+ -+void free_mdl(struct mdl *mdl) -+{ -+ /* A driver may allocate Mdl with NdisAllocateBuffer and free -+ * with IoFreeMdl (e.g., 64-bit Broadcom). Since we need to -+ * treat buffers allocated with Ndis calls differently, we -+ * must call NdisFreeBuffer if it is allocated with Ndis -+ * function. We set 'pool' field in Ndis functions. */ -+ if (!mdl) -+ return; -+ if (mdl->pool) -+ NdisFreeBuffer(mdl); -+ else { -+ struct wrap_mdl *wrap_mdl = (struct wrap_mdl *) -+ ((char *)mdl - offsetof(struct wrap_mdl, mdl)); -+ spin_lock_bh(&dispatcher_lock); -+ RemoveEntryList(&wrap_mdl->list); -+ spin_unlock_bh(&dispatcher_lock); -+ -+ if (mdl->flags & MDL_CACHE_ALLOCATED) { -+ TRACE3("freeing mdl cache: %p, %p, %p", -+ wrap_mdl, mdl, mdl->mappedsystemva); -+ kmem_cache_free(mdl_cache, wrap_mdl); -+ } else { -+ TRACE3("freeing mdl: %p, %p, %p", -+ wrap_mdl, mdl, mdl->mappedsystemva); -+ kfree(wrap_mdl); -+ } -+ } -+ return; -+} -+ -+wstdcall void WIN_FUNC(IoBuildPartialMdl,4) -+ (struct mdl *source, struct mdl *target, void *virt, ULONG length) -+{ -+ MmInitializeMdl(target, virt, length); -+ target->flags |= MDL_PARTIAL; -+} -+ -+wstdcall void WIN_FUNC(MmBuildMdlForNonPagedPool,1) -+ (struct mdl *mdl) -+{ -+ PFN_NUMBER *mdl_pages; -+ int i, n; -+ -+ ENTER4("%p", mdl); -+ /* already mapped */ -+// mdl->mappedsystemva = MmGetMdlVirtualAddress(mdl); -+ mdl->flags |= MDL_SOURCE_IS_NONPAGED_POOL; -+ TRACE4("%p, %p, %p, %d, %d", mdl, mdl->mappedsystemva, mdl->startva, -+ mdl->byteoffset, mdl->bytecount); -+ n = SPAN_PAGES(MmGetSystemAddressForMdl(mdl), MmGetMdlByteCount(mdl)); -+ if (n > MDL_CACHE_PAGES) -+ WARNING("%p, %d, %d", MmGetSystemAddressForMdl(mdl), -+ MmGetMdlByteCount(mdl), n); -+ mdl_pages = MmGetMdlPfnArray(mdl); -+ for (i = 0; i < n; i++) -+ mdl_pages[i] = (ULONG_PTR)mdl->startva + (i * PAGE_SIZE); -+ EXIT4(return); -+} -+ -+wstdcall void *WIN_FUNC(MmMapLockedPages,2) -+ (struct mdl *mdl, KPROCESSOR_MODE access_mode) -+{ -+ /* already mapped */ -+// mdl->mappedsystemva = MmGetMdlVirtualAddress(mdl); -+ mdl->flags |= MDL_MAPPED_TO_SYSTEM_VA; -+ /* what is the need for MDL_PARTIAL_HAS_BEEN_MAPPED? */ -+ if (mdl->flags & MDL_PARTIAL) -+ mdl->flags |= MDL_PARTIAL_HAS_BEEN_MAPPED; -+ return mdl->mappedsystemva; -+} -+ -+wstdcall void *WIN_FUNC(MmMapLockedPagesSpecifyCache,6) -+ (struct mdl *mdl, KPROCESSOR_MODE access_mode, -+ enum memory_caching_type cache_type, void *base_address, -+ ULONG bug_check, enum mm_page_priority priority) -+{ -+ return MmMapLockedPages(mdl, access_mode); -+} -+ -+wstdcall void WIN_FUNC(MmUnmapLockedPages,2) -+ (void *base, struct mdl *mdl) -+{ -+ mdl->flags &= ~MDL_MAPPED_TO_SYSTEM_VA; -+ return; -+} -+ -+wstdcall void WIN_FUNC(MmProbeAndLockPages,3) -+ (struct mdl *mdl, KPROCESSOR_MODE access_mode, -+ enum lock_operation operation) -+{ -+ /* already locked */ -+ mdl->flags |= MDL_PAGES_LOCKED; -+ return; -+} -+ -+wstdcall void WIN_FUNC(MmUnlockPages,1) -+ (struct mdl *mdl) -+{ -+ mdl->flags &= ~MDL_PAGES_LOCKED; -+ return; -+} -+ -+wstdcall BOOLEAN WIN_FUNC(MmIsAddressValid,1) -+ (void *virt_addr) -+{ -+ if (virt_addr_valid(virt_addr)) -+ return TRUE; -+ else -+ return FALSE; -+} -+ -+wstdcall void *WIN_FUNC(MmLockPagableDataSection,1) -+ (void *address) -+{ -+ return address; -+} -+ -+wstdcall void WIN_FUNC(MmUnlockPagableImageSection,1) -+ (void *handle) -+{ -+ return; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ObReferenceObjectByHandle,6) -+ (void *handle, ACCESS_MASK desired_access, void *obj_type, -+ KPROCESSOR_MODE access_mode, void **object, void *handle_info) -+{ -+ struct common_object_header *hdr; -+ -+ TRACE2("%p", handle); -+ hdr = HANDLE_TO_HEADER(handle); -+ atomic_inc_var(hdr->ref_count); -+ *object = HEADER_TO_OBJECT(hdr); -+ TRACE2("%p, %p, %d, %p", hdr, object, hdr->ref_count, *object); -+ return STATUS_SUCCESS; -+} -+ -+/* DDK doesn't say if return value should be before incrementing or -+ * after incrementing reference count, but according to #reactos -+ * developers, it should be return value after incrementing */ -+wfastcall LONG WIN_FUNC(ObfReferenceObject,1) -+ (void *object) -+{ -+ struct common_object_header *hdr; -+ LONG ret; -+ -+ hdr = OBJECT_TO_HEADER(object); -+ ret = post_atomic_add(hdr->ref_count, 1); -+ TRACE2("%p, %d, %p", hdr, hdr->ref_count, object); -+ return ret; -+} -+ -+static int dereference_object(void *object) -+{ -+ struct common_object_header *hdr; -+ int ref_count; -+ -+ ENTER2("object: %p", object); -+ hdr = OBJECT_TO_HEADER(object); -+ TRACE2("hdr: %p", hdr); -+ ref_count = post_atomic_add(hdr->ref_count, -1); -+ TRACE2("object: %p, %d", object, ref_count); -+ if (ref_count < 0) -+ ERROR("invalid object: %p (%d)", object, ref_count); -+ if (ref_count <= 0) { -+ free_object(object); -+ return 1; -+ } else -+ return 0; -+} -+ -+wfastcall void WIN_FUNC(ObfDereferenceObject,1) -+ (void *object) -+{ -+ TRACE2("%p", object); -+ dereference_object(object); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwCreateFile,11) -+ (void **handle, ACCESS_MASK access_mask, -+ struct object_attributes *obj_attr, struct io_status_block *iosb, -+ LARGE_INTEGER *size, ULONG file_attr, ULONG share_access, -+ ULONG create_disposition, ULONG create_options, void *ea_buffer, -+ ULONG ea_length) -+{ -+ struct common_object_header *coh; -+ struct file_object *fo; -+ struct ansi_string ansi; -+ struct wrap_bin_file *bin_file; -+ char *file_basename; -+ NTSTATUS status; -+ -+ spin_lock_bh(&ntoskernel_lock); -+ nt_list_for_each_entry(coh, &object_list, list) { -+ if (coh->type != OBJECT_TYPE_FILE) -+ continue; -+ /* TODO: check if file is opened in shared mode */ -+ if (!RtlCompareUnicodeString(&coh->name, obj_attr->name, TRUE)) { -+ fo = HEADER_TO_OBJECT(coh); -+ bin_file = fo->wrap_bin_file; -+ *handle = coh; -+ spin_unlock_bh(&ntoskernel_lock); -+ ObReferenceObject(fo); -+ iosb->status = FILE_OPENED; -+ iosb->info = bin_file->size; -+ EXIT2(return STATUS_SUCCESS); -+ } -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ -+ if (RtlUnicodeStringToAnsiString(&ansi, obj_attr->name, TRUE) != -+ STATUS_SUCCESS) -+ EXIT2(return STATUS_INSUFFICIENT_RESOURCES); -+ -+ file_basename = strrchr(ansi.buf, '\\'); -+ if (file_basename) -+ file_basename++; -+ else -+ file_basename = ansi.buf; -+ TRACE2("file: '%s', '%s'", ansi.buf, file_basename); -+ -+ fo = allocate_object(sizeof(struct file_object), OBJECT_TYPE_FILE, -+ obj_attr->name); -+ if (!fo) { -+ RtlFreeAnsiString(&ansi); -+ iosb->status = STATUS_INSUFFICIENT_RESOURCES; -+ iosb->info = 0; -+ EXIT2(return STATUS_FAILURE); -+ } -+ coh = OBJECT_TO_HEADER(fo); -+ bin_file = get_bin_file(file_basename); -+ if (bin_file) { -+ TRACE2("%s, %s", bin_file->name, file_basename); -+ fo->flags = FILE_OPENED; -+ } else if (access_mask & FILE_WRITE_DATA) { -+ bin_file = kzalloc(sizeof(*bin_file), GFP_KERNEL); -+ if (bin_file) { -+ strncpy(bin_file->name, file_basename, -+ sizeof(bin_file->name)); -+ bin_file->name[sizeof(bin_file->name)-1] = 0; -+ bin_file->data = vmalloc(*size); -+ if (bin_file->data) { -+ memset(bin_file->data, 0, *size); -+ bin_file->size = *size; -+ fo->flags = FILE_CREATED; -+ } else { -+ kfree(bin_file); -+ bin_file = NULL; -+ } -+ } -+ } else -+ bin_file = NULL; -+ -+ RtlFreeAnsiString(&ansi); -+ if (!bin_file) { -+ iosb->status = FILE_DOES_NOT_EXIST; -+ iosb->info = 0; -+ free_object(fo); -+ EXIT2(return STATUS_FAILURE); -+ } -+ -+ fo->wrap_bin_file = bin_file; -+ fo->current_byte_offset = 0; -+ if (access_mask & FILE_READ_DATA) -+ fo->read_access = TRUE; -+ if (access_mask & FILE_WRITE_DATA) -+ fo->write_access = TRUE; -+ iosb->status = FILE_OPENED; -+ iosb->info = bin_file->size; -+ *handle = coh; -+ TRACE2("handle: %p", *handle); -+ status = STATUS_SUCCESS; -+ EXIT2(return status); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwOpenFile,6) -+ (void **handle, ACCESS_MASK access_mask, -+ struct object_attributes *obj_attr, struct io_status_block *iosb, -+ ULONG share_access, ULONG open_options) -+{ -+ LARGE_INTEGER size; -+ return ZwCreateFile(handle, access_mask, obj_attr, iosb, &size, 0, -+ share_access, 0, open_options, NULL, 0); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwReadFile,9) -+ (void *handle, struct nt_event *event, void *apc_routine, -+ void *apc_context, struct io_status_block *iosb, void *buffer, -+ ULONG length, LARGE_INTEGER *byte_offset, ULONG *key) -+{ -+ struct file_object *fo; -+ struct common_object_header *coh; -+ ULONG count; -+ size_t offset; -+ struct wrap_bin_file *file; -+ -+ TRACE2("%p", handle); -+ coh = handle; -+ if (coh->type != OBJECT_TYPE_FILE) { -+ ERROR("handle %p is invalid: %d", handle, coh->type); -+ EXIT2(return STATUS_FAILURE); -+ } -+ fo = HANDLE_TO_OBJECT(coh); -+ file = fo->wrap_bin_file; -+ TRACE2("file: %s (%zu)", file->name, file->size); -+ spin_lock_bh(&ntoskernel_lock); -+ if (byte_offset) -+ offset = *byte_offset; -+ else -+ offset = fo->current_byte_offset; -+ count = min((size_t)length, file->size - offset); -+ TRACE2("count: %u, offset: %zu, length: %u", count, offset, length); -+ memcpy(buffer, ((void *)file->data) + offset, count); -+ fo->current_byte_offset = offset + count; -+ spin_unlock_bh(&ntoskernel_lock); -+ iosb->status = STATUS_SUCCESS; -+ iosb->info = count; -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwWriteFile,9) -+ (void *handle, struct nt_event *event, void *apc_routine, -+ void *apc_context, struct io_status_block *iosb, void *buffer, -+ ULONG length, LARGE_INTEGER *byte_offset, ULONG *key) -+{ -+ struct file_object *fo; -+ struct common_object_header *coh; -+ struct wrap_bin_file *file; -+ unsigned long offset; -+ -+ TRACE2("%p", handle); -+ coh = handle; -+ if (coh->type != OBJECT_TYPE_FILE) { -+ ERROR("handle %p is invalid: %d", handle, coh->type); -+ EXIT2(return STATUS_FAILURE); -+ } -+ fo = HANDLE_TO_OBJECT(coh); -+ file = fo->wrap_bin_file; -+ TRACE2("file: %zu, %u", file->size, length); -+ spin_lock_bh(&ntoskernel_lock); -+ if (byte_offset) -+ offset = *byte_offset; -+ else -+ offset = fo->current_byte_offset; -+ if (length + offset > file->size) { -+ WARNING("%lu, %zu", length + offset, file->size); -+ /* TODO: implement writing past end of current size */ -+ iosb->status = STATUS_FAILURE; -+ iosb->info = 0; -+ } else { -+ memcpy(file->data + offset, buffer, length); -+ iosb->status = STATUS_SUCCESS; -+ iosb->info = length; -+ fo->current_byte_offset = offset + length; -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ EXIT2(return iosb->status); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwClose,1) -+ (void *handle) -+{ -+ struct common_object_header *coh; -+ -+ TRACE2("%p", handle); -+ if (handle == NULL) { -+ TRACE1(""); -+ EXIT2(return STATUS_SUCCESS); -+ } -+ coh = handle; -+ if (coh->type == OBJECT_TYPE_FILE) { -+ struct file_object *fo; -+ struct wrap_bin_file *bin_file; -+ typeof(fo->flags) flags; -+ -+ fo = HANDLE_TO_OBJECT(handle); -+ flags = fo->flags; -+ bin_file = fo->wrap_bin_file; -+ if (dereference_object(fo)) { -+ if (flags == FILE_CREATED) { -+ vfree(bin_file->data); -+ kfree(bin_file); -+ } else -+ free_bin_file(bin_file); -+ } -+ } else if (coh->type == OBJECT_TYPE_NT_THREAD) { -+ struct nt_thread *thread = HANDLE_TO_OBJECT(handle); -+ TRACE2("thread: %p (%p)", thread, handle); -+ ObDereferenceObject(thread); -+ } else { -+ /* TODO: can we just dereference object here? */ -+ WARNING("closing handle 0x%x not implemented", coh->type); -+ } -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwQueryInformationFile,5) -+ (void *handle, struct io_status_block *iosb, void *info, -+ ULONG length, enum file_info_class class) -+{ -+ struct file_object *fo; -+ struct file_name_info *fni; -+ struct file_std_info *fsi; -+ struct wrap_bin_file *file; -+ struct common_object_header *coh; -+ -+ ENTER2("%p", handle); -+ coh = handle; -+ if (coh->type != OBJECT_TYPE_FILE) { -+ ERROR("handle %p is invalid: %d", coh, coh->type); -+ EXIT2(return STATUS_FAILURE); -+ } -+ fo = HANDLE_TO_OBJECT(handle); -+ TRACE2("fo: %p, %d", fo, class); -+ switch (class) { -+ case FileNameInformation: -+ fni = info; -+ fni->length = min(length, (typeof(length))coh->name.length); -+ memcpy(fni->name, coh->name.buf, fni->length); -+ iosb->status = STATUS_SUCCESS; -+ iosb->info = fni->length; -+ break; -+ case FileStandardInformation: -+ fsi = info; -+ file = fo->wrap_bin_file; -+ fsi->alloc_size = file->size; -+ fsi->eof = file->size; -+ fsi->num_links = 1; -+ fsi->delete_pending = FALSE; -+ fsi->dir = FALSE; -+ iosb->status = STATUS_SUCCESS; -+ iosb->info = 0; -+ break; -+ default: -+ WARNING("type %d not implemented yet", class); -+ iosb->status = STATUS_FAILURE; -+ iosb->info = 0; -+ break; -+ } -+ EXIT2(return iosb->status); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwOpenSection,3) -+ (void **handle, ACCESS_MASK access, struct object_attributes *obj_attrs) -+{ -+ INFO("%p, 0x%x, %d", obj_attrs, obj_attrs->attributes, access); -+ TODO(); -+ *handle = obj_attrs; -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwMapViewOfSection,10) -+ (void *secn_handle, void *process_handle, void **base_address, -+ ULONG zero_bits, SIZE_T commit_size, LARGE_INTEGER *secn_offset, -+ SIZE_T *view_size, enum section_inherit inherit, ULONG alloc_type, -+ ULONG protect) -+{ -+ INFO("%p, %p, %p", secn_handle, process_handle, base_address); -+ TODO(); -+ *base_address = (void *)0xdeadbeef; -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwUnmapViewOfSection,2) -+ (void *process_handle, void *base_address) -+{ -+ INFO("%p, %p", process_handle, base_address); -+ TODO(); -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwCreateKey,7) -+ (void **handle, ACCESS_MASK desired_access, -+ struct object_attributes *attr, ULONG title_index, -+ struct unicode_string *class, ULONG create_options, -+ ULONG *disposition) -+{ -+ struct ansi_string ansi; -+ if (RtlUnicodeStringToAnsiString(&ansi, attr->name, TRUE) == -+ STATUS_SUCCESS) { -+ TRACE1("key: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ *handle = NULL; -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwOpenKey,3) -+ (void **handle, ACCESS_MASK desired_access, -+ struct object_attributes *attr) -+{ -+ struct ansi_string ansi; -+ if (RtlUnicodeStringToAnsiString(&ansi, attr->name, TRUE) == -+ STATUS_SUCCESS) { -+ TRACE1("key: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ *handle = NULL; -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwSetValueKey,6) -+ (void *handle, struct unicode_string *name, ULONG title_index, -+ ULONG type, void *data, ULONG data_size) -+{ -+ struct ansi_string ansi; -+ if (RtlUnicodeStringToAnsiString(&ansi, name, TRUE) == -+ STATUS_SUCCESS) { -+ TRACE1("key: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwQueryValueKey,6) -+ (void *handle, struct unicode_string *name, -+ enum key_value_information_class class, void *info, -+ ULONG length, ULONG *res_length) -+{ -+ struct ansi_string ansi; -+ if (RtlUnicodeStringToAnsiString(&ansi, name, TRUE) == STATUS_SUCCESS) { -+ TRACE1("key: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ TODO(); -+ return STATUS_INVALID_PARAMETER; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwDeleteKey,1) -+ (void *handle) -+{ -+ ENTER2("%p", handle); -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(ZwPowerInformation,5) -+ (INT info_level, void *in_buf, ULONG in_buf_len, void *out_buf, -+ ULONG out_buf_len) -+{ -+ INFO("%d, %u, %u", info_level, in_buf_len, out_buf_len); -+ TODO(); -+ return STATUS_ACCESS_DENIED; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(WmiSystemControl,4) -+ (struct wmilib_context *info, struct device_object *dev_obj, -+ struct irp *irp, void *irp_disposition) -+{ -+ TODO(); -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(WmiCompleteRequest,5) -+ (struct device_object *dev_obj, struct irp *irp, NTSTATUS status, -+ ULONG buffer_used, CCHAR priority_boost) -+{ -+ TODO(); -+ return STATUS_SUCCESS; -+} -+ -+noregparm NTSTATUS WIN_FUNC(WmiTraceMessage,12) -+ (void *tracehandle, ULONG message_flags, -+ void *message_guid, USHORT message_no, ...) -+{ -+ TODO(); -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(WmiQueryTraceInformation,4) -+ (enum trace_information_class trace_info_class, void *trace_info, -+ ULONG *req_length, void *buf) -+{ -+ TODO(); -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+/* this function can't be wstdcall as it takes variable number of args */ -+__attribute__((format(printf, 1, 2))) -+noregparm ULONG WIN_FUNC(DbgPrint,12) -+ (char *format, ...) -+{ -+#if DEBUG >= 1 -+ va_list args; -+ static char buf[100]; -+ -+ va_start(args, format); -+ vsnprintf(buf, sizeof(buf), format, args); -+ printk(KERN_DEBUG "%s (%s): %s", DRIVER_NAME, __func__, buf); -+ va_end(args); -+#endif -+ return STATUS_SUCCESS; -+} -+ -+__attribute__((format(printf, 3, 4))) -+noregparm ULONG WIN_FUNC(DbgPrintEx,12) -+ (ULONG component_id, ULONG severity, char *format, ...) -+{ -+#if DEBUG >= 1 -+ va_list args; -+ static char buf[100]; -+ -+ va_start(args, format); -+ vsnprintf(buf, sizeof(buf), format, args); -+ TRACE1("component_id: %d, severity: %d\n", component_id, severity); -+ printk(KERN_DEBUG "%s (%s): %s", DRIVER_NAME, __func__, buf); -+ va_end(args); -+#endif -+ return STATUS_SUCCESS; -+} -+ -+wstdcall void WIN_FUNC(KeBugCheck,1) -+ (ULONG code) -+{ -+ ERROR("Unrecoverable error reported by the driver"); -+ ERROR("code: 0x%x\n", code); -+ dump_stack(); -+ return; -+} -+ -+wstdcall void WIN_FUNC(KeBugCheckEx,5) -+ (ULONG code, ULONG_PTR param1, ULONG_PTR param2, -+ ULONG_PTR param3, ULONG_PTR param4) -+{ -+ ERROR("Unrecoverable error reported by the driver"); -+ ERROR("code: 0x%x, params: 0x%lx 0x%lx 0x%lx 0x%lx\n", code, param1, -+ param2, param3, param4); -+ dump_stack(); -+ return; -+} -+ -+wstdcall void WIN_FUNC(ExSystemTimeToLocalTime,2) -+ (LARGE_INTEGER *system_time, LARGE_INTEGER *local_time) -+{ -+ *local_time = *system_time; -+} -+ -+wstdcall ULONG WIN_FUNC(ExSetTimerResolution,2) -+ (ULONG time, BOOLEAN set) -+{ -+ /* why a driver should change system wide timer resolution is -+ * beyond me */ -+ return time; -+} -+ -+wstdcall void WIN_FUNC(DbgBreakPoint,0) -+ (void) -+{ -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(_except_handler3,0) -+ (void) -+{ -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(__C_specific_handler,0) -+ (void) -+{ -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(_purecall,0) -+ (void) -+{ -+ TODO(); -+} -+ -+struct worker_init_struct { -+ struct work_struct work; -+ struct completion completion; -+ struct nt_thread *nt_thread; -+}; -+ -+int ntoskernel_init(void) -+{ -+ struct timeval now; -+ -+ spin_lock_init(&dispatcher_lock); -+ spin_lock_init(&ntoskernel_lock); -+ spin_lock_init(&ntos_work_lock); -+ spin_lock_init(&kdpc_list_lock); -+ spin_lock_init(&irp_cancel_lock); -+ InitializeListHead(&wrap_mdl_list); -+ InitializeListHead(&kdpc_list); -+ InitializeListHead(&callback_objects); -+ InitializeListHead(&bus_driver_list); -+ InitializeListHead(&object_list); -+ InitializeListHead(&ntos_work_list); -+ -+ nt_spin_lock_init(&nt_list_lock); -+ -+ INIT_WORK(&kdpc_work, kdpc_worker); -+ INIT_WORK(&ntos_work, ntos_work_worker); -+ wrap_timer_slist.next = NULL; -+ -+ do_gettimeofday(&now); -+ wrap_ticks_to_boot = TICKS_1601_TO_1970; -+ wrap_ticks_to_boot += (u64)now.tv_sec * TICKSPERSEC; -+ wrap_ticks_to_boot += now.tv_usec * 10; -+ wrap_ticks_to_boot -= jiffies * TICKSPERJIFFY; -+ TRACE2("%llu", wrap_ticks_to_boot); -+ -+ cpu_count = num_online_cpus(); -+ -+#ifdef WRAP_PREEMPT -+ do { -+ int cpu; -+ for_each_possible_cpu(cpu) { -+ struct irql_info *info; -+ info = &per_cpu(irql_info, cpu); -+ mutex_init(&(info->lock)); -+ info->task = NULL; -+ info->count = 0; -+#ifdef CONFIG_SMP -+ cpumask_setall(&info->cpus_allowed); -+#endif -+ } -+ } while (0); -+#endif -+ -+ ntos_wq = create_singlethread_workqueue("ntos_wq"); -+ if (!ntos_wq) { -+ WARNING("couldn't create ntos_wq thread"); -+ return -ENOMEM; -+ } -+ TRACE1("ntos_wq: %p", ntos_wq); -+ -+ if (add_bus_driver("PCI") -+#ifdef ENABLE_USB -+ || add_bus_driver("USB") -+#endif -+ ) { -+ ntoskernel_exit(); -+ return -ENOMEM; -+ } -+ mdl_cache = -+ wrap_kmem_cache_create(DRIVER_NAME "_mdl", -+ sizeof(struct wrap_mdl) + MDL_CACHE_SIZE, -+ 0, 0); -+ TRACE2("%p", mdl_cache); -+ if (!mdl_cache) { -+ ERROR("couldn't allocate MDL cache"); -+ ntoskernel_exit(); -+ return -ENOMEM; -+ } -+ -+#if defined(CONFIG_X86_64) -+ memset(&kuser_shared_data, 0, sizeof(kuser_shared_data)); -+ *((ULONG64 *)&kuser_shared_data.system_time) = ticks_1601(); -+ init_timer(&shared_data_timer); -+ shared_data_timer.function = update_user_shared_data_proc; -+ shared_data_timer.data = 0; -+#endif -+ return 0; -+} -+ -+int ntoskernel_init_device(struct wrap_device *wd) -+{ -+#if defined(CONFIG_X86_64) -+ if (kuser_shared_data.reserved1) -+ mod_timer(&shared_data_timer, jiffies + MSEC_TO_HZ(30)); -+#endif -+ return 0; -+} -+ -+void ntoskernel_exit_device(struct wrap_device *wd) -+{ -+ ENTER2(""); -+ -+ KeFlushQueuedDpcs(); -+ EXIT2(return); -+} -+ -+void ntoskernel_exit(void) -+{ -+ struct nt_list *cur; -+ -+ ENTER2(""); -+ -+ /* free kernel (Ke) timers */ -+ TRACE2("freeing timers"); -+ while (1) { -+ struct wrap_timer *wrap_timer; -+ struct nt_slist *slist; -+ -+ spin_lock_bh(&ntoskernel_lock); -+ if ((slist = wrap_timer_slist.next)) -+ wrap_timer_slist.next = slist->next; -+ spin_unlock_bh(&ntoskernel_lock); -+ TIMERTRACE("%p", slist); -+ if (!slist) -+ break; -+ wrap_timer = container_of(slist, struct wrap_timer, slist); -+ if (del_timer_sync(&wrap_timer->timer)) -+ WARNING("Buggy Windows driver left timer %p running", -+ wrap_timer->nt_timer); -+ memset(wrap_timer, 0, sizeof(*wrap_timer)); -+ slack_kfree(wrap_timer); -+ } -+ -+ TRACE2("freeing MDLs"); -+ if (mdl_cache) { -+ spin_lock_bh(&ntoskernel_lock); -+ if (!IsListEmpty(&wrap_mdl_list)) -+ ERROR("Windows driver didn't free all MDLs; " -+ "freeing them now"); -+ while ((cur = RemoveHeadList(&wrap_mdl_list))) { -+ struct wrap_mdl *wrap_mdl; -+ wrap_mdl = container_of(cur, struct wrap_mdl, list); -+ if (wrap_mdl->mdl->flags & MDL_CACHE_ALLOCATED) -+ kmem_cache_free(mdl_cache, wrap_mdl); -+ else -+ kfree(wrap_mdl); -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ kmem_cache_destroy(mdl_cache); -+ mdl_cache = NULL; -+ } -+ -+ TRACE2("freeing callbacks"); -+ spin_lock_bh(&ntoskernel_lock); -+ while ((cur = RemoveHeadList(&callback_objects))) { -+ struct callback_object *object; -+ struct nt_list *ent; -+ object = container_of(cur, struct callback_object, list); -+ while ((ent = RemoveHeadList(&object->callback_funcs))) { -+ struct callback_func *f; -+ f = container_of(ent, struct callback_func, list); -+ kfree(f); -+ } -+ kfree(object); -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ -+ spin_lock_bh(&ntoskernel_lock); -+ while ((cur = RemoveHeadList(&bus_driver_list))) { -+ struct bus_driver *bus_driver; -+ bus_driver = container_of(cur, struct bus_driver, list); -+ /* TODO: make sure all all drivers are shutdown/removed */ -+ kfree(bus_driver); -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ -+#if defined(CONFIG_X86_64) -+ del_timer_sync(&shared_data_timer); -+#endif -+ if (ntos_wq) -+ destroy_workqueue(ntos_wq); -+ ENTER2("freeing objects"); -+ spin_lock_bh(&ntoskernel_lock); -+ while ((cur = RemoveHeadList(&object_list))) { -+ struct common_object_header *hdr; -+ hdr = container_of(cur, struct common_object_header, list); -+ if (hdr->type == OBJECT_TYPE_NT_THREAD) -+ TRACE1("object %p(%d) was not freed, freeing it now", -+ HEADER_TO_OBJECT(hdr), hdr->type); -+ else -+ WARNING("object %p(%d) was not freed, freeing it now", -+ HEADER_TO_OBJECT(hdr), hdr->type); -+ ExFreePool(hdr); -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ -+ EXIT2(return); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.h ---- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,1083 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _NTOSKERNEL_H_ -+#define _NTOSKERNEL_H_ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if !defined(CONFIG_X86) && !defined(CONFIG_X86_64) -+#error "this module is for x86 or x86_64 architectures only" -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) -+#define gfp_t unsigned int __nocast -+ -+static inline void *_kzalloc(size_t size, gfp_t flags) -+{ -+ void *p = kmalloc(size, flags); -+ if (likely(p != NULL)) -+ memset(p, 0, size); -+ return p; -+} -+ -+#define kzalloc(size, flags) _kzalloc(size, flags) -+#endif -+ -+/* Interrupt backwards compatibility stuff */ -+#include -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) -+#ifndef IRQ_HANDLED -+#define IRQ_HANDLED -+#define IRQ_NONE -+#define irqreturn_t void -+#endif -+#endif /* Linux < 2.6.29 */ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) -+#ifndef mutex_init -+#define mutex semaphore -+#define mutex_init(m) sema_init(m, 1) -+#define mutex_lock(m) down(m) -+#define mutex_trylock(m) (!down_trylock(m)) -+#define mutex_unlock(m) up(m) -+#define mutex_is_locked(m) (atomic_read(m.count) == 0) -+#endif -+#endif /* Linux < 2.6.16 */ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) -+#define set_cpus_allowed_ptr(task, mask) set_cpus_allowed(task, *mask) -+#endif /* Linux < 2.6.26 */ -+ -+#ifdef CONFIG_SMP -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -+#define cpumask_copy(dst, src) do { *dst = *src; } while (0) -+#define cpumask_equal(mask1, mask2) cpus_equal(*mask1, *mask2) -+#define cpumask_setall(mask) cpus_setall(*mask) -+static cpumask_t cpumasks[NR_CPUS]; -+#define cpumask_of(cpu) \ -+({ \ -+ cpumasks[cpu] = cpumask_of_cpu(cpu); \ -+ &cpumasks[cpu]; \ -+}) -+#endif /* Linux < 2.6.28 */ -+#endif /* CONFIG_SMP */ -+ -+#ifndef tsk_cpus_allowed -+#define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed) -+#endif -+ -+#ifndef __packed -+#define __packed __attribute__((packed)) -+#endif -+ -+/* pci functions in 2.6 kernels have problems allocating dma buffers, -+ * but seem to work fine with dma functions -+ */ -+#include -+ -+#define PCI_DMA_ALLOC_COHERENT(pci_dev,size,dma_handle) \ -+ dma_alloc_coherent(&pci_dev->dev,size,dma_handle, \ -+ GFP_KERNEL | __GFP_REPEAT) -+#define PCI_DMA_FREE_COHERENT(pci_dev,size,cpu_addr,dma_handle) \ -+ dma_free_coherent(&pci_dev->dev,size,cpu_addr,dma_handle) -+#define PCI_DMA_MAP_SINGLE(pci_dev,addr,size,direction) \ -+ dma_map_single(&pci_dev->dev,addr,size,direction) -+#define PCI_DMA_UNMAP_SINGLE(pci_dev,dma_handle,size,direction) \ -+ dma_unmap_single(&pci_dev->dev,dma_handle,size,direction) -+#define MAP_SG(pci_dev, sglist, nents, direction) \ -+ dma_map_sg(&pci_dev->dev, sglist, nents, direction) -+#define UNMAP_SG(pci_dev, sglist, nents, direction) \ -+ dma_unmap_sg(&pci_dev->dev, sglist, nents, direction) -+#define PCI_DMA_MAP_ERROR(dma_addr) dma_mapping_error(dma_addr) -+ -+ -+#if defined(CONFIG_NET_RADIO) && !defined(CONFIG_WIRELESS_EXT) -+#define CONFIG_WIRELESS_EXT -+#endif -+ -+#define prepare_wait_condition(task, var, value) \ -+do { \ -+ var = value; \ -+ task = current; \ -+ barrier(); \ -+} while (0) -+ -+/* Wait in wait_state (e.g., TASK_INTERRUPTIBLE) for condition to -+ * become true; timeout is either jiffies (> 0) to wait or 0 to wait -+ * forever. -+ * When timeout == 0, return value is -+ * > 0 if condition becomes true, or -+ * < 0 if signal is pending on the thread. -+ * When timeout > 0, return value is -+ * > 0 if condition becomes true before timeout, -+ * < 0 if signal is pending on the thread before timeout, or -+ * 0 if timedout (condition may have become true at the same time) -+ */ -+ -+#define wait_condition(condition, timeout, wait_state) \ -+({ \ -+ long ret = timeout ? timeout : 1; \ -+ while (1) { \ -+ if (signal_pending(current)) { \ -+ ret = -ERESTARTSYS; \ -+ break; \ -+ } \ -+ set_current_state(wait_state); \ -+ if (condition) { \ -+ __set_current_state(TASK_RUNNING); \ -+ break; \ -+ } \ -+ if (timeout) { \ -+ ret = schedule_timeout(ret); \ -+ if (!ret) \ -+ break; \ -+ } else \ -+ schedule(); \ -+ } \ -+ ret; \ -+}) -+ -+#ifdef WRAP_WQ -+ -+struct wrap_workqueue_struct; -+ -+struct wrap_work_struct { -+ struct list_head list; -+ void (*func)(struct wrap_work_struct *data); -+ void *data; -+ /* whether/on which thread scheduled */ -+ struct workqueue_thread *thread; -+}; -+ -+#define work_struct wrap_work_struct -+#define workqueue_struct wrap_workqueue_struct -+ -+#undef INIT_WORK -+#define INIT_WORK(work, pfunc) \ -+ do { \ -+ (work)->func = (pfunc); \ -+ (work)->data = (work); \ -+ (work)->thread = NULL; \ -+ } while (0) -+ -+#undef create_singlethread_workqueue -+#define create_singlethread_workqueue(wq) wrap_create_wq(wq, 1, 0) -+#undef create_workqueue -+#define create_workqueue(wq) wrap_create_wq(wq, 0, 0) -+#undef destroy_workqueue -+#define destroy_workqueue(wq) wrap_destroy_wq(wq) -+#undef queue_work -+#define queue_work(wq, work) wrap_queue_work(wq, work) -+#undef flush_workqueue -+#define flush_workqueue(wq) wrap_flush_wq(wq) -+ -+struct workqueue_struct *wrap_create_wq(const char *name, u8 singlethread, -+ u8 freeze); -+void wrap_destroy_wq(struct workqueue_struct *workq); -+int wrap_queue_work(struct workqueue_struct *workq, struct work_struct *work); -+void wrap_cancel_work(struct work_struct *work); -+void wrap_flush_wq(struct workqueue_struct *workq); -+ -+#else // WRAP_WQ -+ -+/* Compatibility for Linux before 2.6.20 where INIT_WORK takes 3 arguments */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && \ -+ !defined(INIT_WORK_NAR) && \ -+ !defined(INIT_DELAYED_WORK_DEFERRABLE) -+typedef void (*compat_work_func_t)(void *work); -+typedef void (*work_func_t)(struct work_struct *work); -+static inline void (INIT_WORK)(struct work_struct *work, work_func_t func) -+{ -+ INIT_WORK(work, (compat_work_func_t)func, work); -+} -+#undef INIT_WORK -+#endif -+ -+#endif // WRAP_WQ -+ -+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18) -+#define ISR_PT_REGS_PARAM_DECL -+#else -+#define ISR_PT_REGS_PARAM_DECL , struct pt_regs *regs -+#endif -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) -+#define for_each_possible_cpu(_cpu) for_each_cpu(_cpu) -+#endif -+ -+#ifndef CHECKSUM_PARTIAL -+#define CHECKSUM_PARTIAL CHECKSUM_HW -+#endif -+ -+#ifndef IRQF_SHARED -+#define IRQF_SHARED SA_SHIRQ -+#endif -+ -+#ifndef UMH_WAIT_PROC -+#define UMH_WAIT_PROC 1 -+#endif -+ -+#define memcpy_skb(skb, from, length) \ -+ memcpy(skb_put(skb, length), from, length) -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) -+#ifndef DMA_BIT_MASK -+#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1)) -+#endif -+#endif -+ -+#ifndef __GFP_DMA32 -+#define __GFP_DMA32 GFP_DMA -+#endif -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) -+#define wrap_kmem_cache_create(name, size, align, flags) \ -+ kmem_cache_create(name, size, align, flags, NULL, NULL) -+#else -+#define wrap_kmem_cache_create(name, size, align, flags) \ -+ kmem_cache_create(name, size, align, flags, NULL) -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) -+#define netdev_mc_count(dev) ((dev)->mc_count) -+#define usb_alloc_coherent(dev, size, mem_flags, dma) (usb_buffer_alloc((dev), (size), (mem_flags), (dma))) -+#define usb_free_coherent(dev, size, addr, dma) (usb_buffer_free((dev), (size), (addr), (dma))) -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) -+#define daemonize(name, ...) do {} while (0) -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) -+#define add_taint(flag, lockdep_ok) add_taint(flag) -+#endif -+ -+#include "winnt_types.h" -+#include "ndiswrapper.h" -+#include "pe_linker.h" -+#include "wrapmem.h" -+#include "lin2win.h" -+#include "loader.h" -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) -+static inline void netif_tx_lock(struct net_device *dev) -+{ -+ spin_lock(&dev->xmit_lock); -+} -+static inline void netif_tx_unlock(struct net_device *dev) -+{ -+ spin_unlock(&dev->xmit_lock); -+} -+static inline void netif_tx_lock_bh(struct net_device *dev) -+{ -+ spin_lock_bh(&dev->xmit_lock); -+} -+static inline void netif_tx_unlock_bh(struct net_device *dev) -+{ -+ spin_unlock_bh(&dev->xmit_lock); -+} -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) -+static inline void netif_poll_enable(struct net_device *dev) -+{ -+} -+static inline void netif_poll_disable(struct net_device *dev) -+{ -+} -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) -+#define proc_net_root init_net.proc_net -+#else -+#define proc_net_root proc_net -+#endif -+ -+#if ((LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) && \ -+ (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0))) || \ -+ (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,42)) -+#ifndef skb_frag_page -+#define skb_frag_page(frag) ((frag)->page) -+#endif -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) -+#define netdev_notifier_info_to_dev(x) ((struct net_device *)(x)) -+#endif -+ -+#ifdef INIT_COMPLETION -+static inline void reinit_completion(struct completion *x) -+{ -+ INIT_COMPLETION(*x); -+} -+#endif -+ -+/* TICK is 100ns */ -+#define TICKSPERSEC 10000000 -+#define TICKSPERMSEC 10000 -+#define SECSPERDAY 86400 -+#define TICKSPERJIFFY ((TICKSPERSEC + HZ - 1) / HZ) -+ -+#define int_div_round(x, y) (((x) + (y - 1)) / (y)) -+ -+/* 1601 to 1970 is 369 years plus 89 leap days */ -+#define SECS_1601_TO_1970 ((369 * 365 + 89) * (u64)SECSPERDAY) -+#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC) -+ -+/* 100ns units to HZ; if sys_time is negative, relative to current -+ * clock, otherwise from year 1601 */ -+#define SYSTEM_TIME_TO_HZ(sys_time) \ -+ (((sys_time) <= 0) ? \ -+ int_div_round(((u64)HZ * (-(sys_time))), TICKSPERSEC) : \ -+ int_div_round(((s64)HZ * ((sys_time) - ticks_1601())), TICKSPERSEC)) -+ -+#define MSEC_TO_HZ(ms) int_div_round((ms * HZ), 1000) -+#define USEC_TO_HZ(us) int_div_round((us * HZ), 1000000) -+ -+extern u64 wrap_ticks_to_boot; -+ -+static inline u64 ticks_1601(void) -+{ -+ return wrap_ticks_to_boot + (u64)jiffies * TICKSPERJIFFY; -+} -+ -+typedef void (*generic_func)(void); -+ -+struct wrap_export { -+ const char *name; -+ generic_func func; -+}; -+ -+#ifdef CONFIG_X86_64 -+ -+#define WIN_SYMBOL(name, argc) \ -+ {#name, (generic_func) win2lin_ ## name ## _ ## argc} -+#define WIN_WIN_SYMBOL(name, argc) \ -+ {#name, (generic_func) win2lin__win_ ## name ## _ ## argc} -+#define WIN_FUNC_DECL(name, argc) \ -+ extern typeof(name) win2lin_ ## name ## _ ## argc; -+#define WIN_FUNC_PTR(name, argc) win2lin_ ## name ## _ ## argc -+ -+#else -+ -+#define WIN_SYMBOL(name, argc) {#name, (generic_func)name} -+#define WIN_WIN_SYMBOL(name, argc) {#name, (generic_func)_win_ ## name} -+#define WIN_FUNC_DECL(name, argc) -+#define WIN_FUNC_PTR(name, argc) name -+ -+#endif -+ -+#define WIN_FUNC(name, argc) (name) -+/* map name s to f - if f is different from s */ -+#define WIN_SYMBOL_MAP(s, f) -+ -+#define POOL_TAG(A, B, C, D) \ -+ ((ULONG)((A) + ((B) << 8) + ((C) << 16) + ((D) << 24))) -+ -+struct pe_image { -+ char name[MAX_DRIVER_NAME_LEN]; -+ UINT (*entry)(struct driver_object *, struct unicode_string *) wstdcall; -+ void *image; -+ int size; -+ int type; -+ -+ IMAGE_NT_HEADERS *nt_hdr; -+ IMAGE_OPTIONAL_HEADER *opt_hdr; -+}; -+ -+struct ndis_mp_block; -+ -+struct wrap_timer { -+ struct nt_slist slist; -+ struct timer_list timer; -+ struct nt_timer *nt_timer; -+ long repeat; -+#ifdef TIMER_DEBUG -+ unsigned long wrap_timer_magic; -+#endif -+}; -+ -+struct ntos_work_item { -+ struct nt_list list; -+ void *arg1; -+ void *arg2; -+ NTOS_WORK_FUNC func; -+}; -+ -+struct wrap_device_setting { -+ struct nt_list list; -+ char name[MAX_SETTING_NAME_LEN]; -+ char value[MAX_SETTING_VALUE_LEN]; -+ void *encoded; -+}; -+ -+struct wrap_bin_file { -+ char name[MAX_DRIVER_NAME_LEN]; -+ size_t size; -+ void *data; -+}; -+ -+#define WRAP_DRIVER_CLIENT_ID 1 -+ -+struct wrap_driver { -+ struct nt_list list; -+ struct driver_object *drv_obj; -+ char name[MAX_DRIVER_NAME_LEN]; -+ char version[MAX_SETTING_VALUE_LEN]; -+ unsigned short num_pe_images; -+ struct pe_image pe_images[MAX_DRIVER_PE_IMAGES]; -+ unsigned short num_bin_files; -+ struct wrap_bin_file *bin_files; -+ struct nt_list settings; -+ int dev_type; -+ struct ndis_driver *ndis_driver; -+}; -+ -+enum hw_status { -+ HW_INITIALIZED = 1, HW_SUSPENDED, HW_HALTED, HW_DISABLED, -+}; -+ -+struct wrap_device { -+ /* first part is (de)initialized once by loader */ -+ struct nt_list list; -+ int dev_bus; -+ int vendor; -+ int device; -+ int subvendor; -+ int subdevice; -+ char conf_file_name[MAX_DRIVER_NAME_LEN]; -+ char driver_name[MAX_DRIVER_NAME_LEN]; -+ struct wrap_driver *driver; -+ struct nt_list settings; -+ -+ /* rest should be (de)initialized when a device is -+ * (un)plugged */ -+ struct cm_resource_list *resource_list; -+ unsigned long hw_status; -+ struct device_object *pdo; -+ union { -+ struct { -+ struct pci_dev *pdev; -+ enum device_power_state wake_state; -+ } pci; -+ struct { -+ struct usb_device *udev; -+ struct usb_interface *intf; -+ int num_alloc_urbs; -+ struct nt_list wrap_urb_list; -+ } usb; -+ }; -+}; -+ -+#define wrap_is_pci_bus(dev_bus) \ -+ (WRAP_BUS(dev_bus) == WRAP_PCI_BUS || \ -+ WRAP_BUS(dev_bus) == WRAP_PCMCIA_BUS) -+#ifdef ENABLE_USB -+/* earlier versions of ndiswrapper used 0 as USB_BUS */ -+#define wrap_is_usb_bus(dev_bus) \ -+ (WRAP_BUS(dev_bus) == WRAP_USB_BUS || \ -+ WRAP_BUS(dev_bus) == WRAP_INTERNAL_BUS) -+#else -+#define wrap_is_usb_bus(dev_bus) 0 -+#endif -+#define wrap_is_bluetooth_device(dev_bus) \ -+ (WRAP_DEVICE(dev_bus) == WRAP_BLUETOOTH_DEVICE1 || \ -+ WRAP_DEVICE(dev_bus) == WRAP_BLUETOOTH_DEVICE2) -+ -+extern struct workqueue_struct *ntos_wq; -+extern struct workqueue_struct *ndis_wq; -+extern struct workqueue_struct *wrapndis_wq; -+ -+#define atomic_unary_op(var, size, oper) \ -+do { \ -+ if (size == 1) \ -+ __asm__ __volatile__( \ -+ LOCK_PREFIX oper "b %b0\n\t" : "+m" (var)); \ -+ else if (size == 2) \ -+ __asm__ __volatile__( \ -+ LOCK_PREFIX oper "w %w0\n\t" : "+m" (var)); \ -+ else if (size == 4) \ -+ __asm__ __volatile__( \ -+ LOCK_PREFIX oper "l %0\n\t" : "+m" (var)); \ -+ else if (size == 8) \ -+ __asm__ __volatile__( \ -+ LOCK_PREFIX oper "q %q0\n\t" : "+m" (var)); \ -+ else { \ -+ extern void _invalid_op_size_(void); \ -+ _invalid_op_size_(); \ -+ } \ -+} while (0) -+ -+#define atomic_inc_var_size(var, size) atomic_unary_op(var, size, "inc") -+ -+#define atomic_inc_var(var) atomic_inc_var_size(var, sizeof(var)) -+ -+#define atomic_dec_var_size(var, size) atomic_unary_op(var, size, "dec") -+ -+#define atomic_dec_var(var) atomic_dec_var_size(var, sizeof(var)) -+ -+#define pre_atomic_add(var, i) \ -+({ \ -+ typeof(var) pre; \ -+ __asm__ __volatile__( \ -+ LOCK_PREFIX "xadd %0, %1\n\t" \ -+ : "=r"(pre), "+m"(var) \ -+ : "0"(i)); \ -+ pre; \ -+}) -+ -+#define post_atomic_add(var, i) (pre_atomic_add(var, i) + i) -+ -+//#define DEBUG_IRQL 1 -+ -+#ifdef DEBUG_IRQL -+#define assert_irql(cond) \ -+do { \ -+ KIRQL _irql_ = current_irql(); \ -+ if (!(cond)) { \ -+ WARNING("assertion '%s' failed: %d", #cond, _irql_); \ -+ DBG_BLOCK(4) { \ -+ dump_stack(); \ -+ } \ -+ } \ -+} while (0) -+#else -+#define assert_irql(cond) do { } while (0) -+#endif -+ -+/* When preempt is enabled, we should preempt_disable to raise IRQL to -+ * DISPATCH_LEVEL, to be consistent with the semantics. However, using -+ * a mutex instead, so that only ndiswrapper threads run one at a time -+ * on a processor when at DISPATCH_LEVEL seems to be enough. So that -+ * is what we will use until we learn otherwise. If -+ * preempt_(en|dis)able is required for some reason, comment out -+ * following #define. */ -+ -+#define WRAP_PREEMPT 1 -+ -+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PREEMPT_RT) -+#ifndef WRAP_PREEMPT -+#define WRAP_PREEMPT 1 -+#endif -+#endif -+ -+//#undef WRAP_PREEMPT -+ -+#ifdef WRAP_PREEMPT -+ -+struct irql_info { -+ int count; -+ struct mutex lock; -+#ifdef CONFIG_SMP -+ cpumask_t cpus_allowed; -+#endif -+ struct task_struct *task; -+}; -+ -+DECLARE_PER_CPU(struct irql_info, irql_info); -+ -+static inline KIRQL raise_irql(KIRQL newirql) -+{ -+ struct irql_info *info; -+ -+ assert(newirql == DISPATCH_LEVEL); -+ info = &get_cpu_var(irql_info); -+ if (info->task == current) { -+ assert(info->count > 0); -+ assert(mutex_is_locked(&info->lock)); -+#if defined(CONFIG_SMP) && DEBUG >= 1 -+ assert(cpumask_equal(tsk_cpus_allowed(current), -+ cpumask_of(smp_processor_id()))); -+#endif -+ info->count++; -+ put_cpu_var(irql_info); -+ return DISPATCH_LEVEL; -+ } -+ /* TODO: is this enough to pin down to current cpu? */ -+#ifdef CONFIG_SMP -+ assert(task_cpu(current) == smp_processor_id()); -+ cpumask_copy(&info->cpus_allowed, tsk_cpus_allowed(current)); -+ set_cpus_allowed_ptr(current, cpumask_of(smp_processor_id())); -+#endif -+ put_cpu_var(irql_info); -+ mutex_lock(&info->lock); -+ assert(info->count == 0); -+ assert(info->task == NULL); -+ info->count = 1; -+ info->task = current; -+ return PASSIVE_LEVEL; -+} -+ -+static inline void lower_irql(KIRQL oldirql) -+{ -+ struct irql_info *info; -+ -+ assert(oldirql <= DISPATCH_LEVEL); -+ info = &get_cpu_var(irql_info); -+ assert(info->task == current); -+ assert(mutex_is_locked(&info->lock)); -+ assert(info->count > 0); -+ if (--info->count == 0) { -+ info->task = NULL; -+#ifdef CONFIG_SMP -+ set_cpus_allowed_ptr(current, &info->cpus_allowed); -+#endif -+ mutex_unlock(&info->lock); -+ } -+ put_cpu_var(irql_info); -+} -+ -+static inline KIRQL current_irql(void) -+{ -+ int count; -+ if (in_irq() || irqs_disabled()) -+ EXIT4(return DIRQL); -+ if (in_atomic() || in_interrupt()) -+ EXIT4(return SOFT_IRQL); -+ count = get_cpu_var(irql_info).count; -+ put_cpu_var(irql_info); -+ if (count) -+ EXIT6(return DISPATCH_LEVEL); -+ else -+ EXIT6(return PASSIVE_LEVEL); -+} -+ -+#else -+ -+static inline KIRQL current_irql(void) -+{ -+ if (in_irq() || irqs_disabled()) -+ EXIT4(return DIRQL); -+ if (in_interrupt()) -+ EXIT4(return SOFT_IRQL); -+ if (in_atomic()) -+ EXIT6(return DISPATCH_LEVEL); -+ else -+ EXIT6(return PASSIVE_LEVEL); -+} -+ -+static inline KIRQL raise_irql(KIRQL newirql) -+{ -+ KIRQL ret = in_atomic() ? DISPATCH_LEVEL : PASSIVE_LEVEL; -+ assert(newirql == DISPATCH_LEVEL); -+ assert(current_irql() <= DISPATCH_LEVEL); -+ preempt_disable(); -+ return ret; -+} -+ -+static inline void lower_irql(KIRQL oldirql) -+{ -+ assert(current_irql() == DISPATCH_LEVEL); -+ preempt_enable(); -+} -+ -+#endif -+ -+#define irql_gfp() (in_atomic() ? GFP_ATOMIC : GFP_KERNEL) -+ -+/* Windows spinlocks are of type ULONG_PTR which is not big enough to -+ * store Linux spinlocks; so we implement Windows spinlocks using -+ * ULONG_PTR space with our own functions/macros */ -+ -+/* Windows seems to use 0 for unlocked state of spinlock - if Linux -+ * convention of 1 for unlocked state is used, at least prism54 driver -+ * crashes */ -+ -+#define NT_SPIN_LOCK_UNLOCKED 0 -+#define NT_SPIN_LOCK_LOCKED 1 -+ -+static inline void nt_spin_lock_init(NT_SPIN_LOCK *lock) -+{ -+ *lock = NT_SPIN_LOCK_UNLOCKED; -+} -+ -+#ifdef CONFIG_SMP -+ -+static inline void nt_spin_lock(NT_SPIN_LOCK *lock) -+{ -+ while (1) { -+ unsigned long lockval = xchg(lock, NT_SPIN_LOCK_LOCKED); -+ -+ if (likely(lockval == NT_SPIN_LOCK_UNLOCKED)) -+ break; -+ if (unlikely(lockval > NT_SPIN_LOCK_LOCKED)) { -+ ERROR("bad spinlock: 0x%lx at %p", lockval, lock); -+ return; -+ } -+ /* "rep; nop" doesn't change cx register, it's a "pause" */ -+ __asm__ __volatile__("rep; nop"); -+ } -+} -+ -+static inline void nt_spin_unlock(NT_SPIN_LOCK *lock) -+{ -+ unsigned long lockval = xchg(lock, NT_SPIN_LOCK_UNLOCKED); -+ -+ if (likely(lockval == NT_SPIN_LOCK_LOCKED)) -+ return; -+ WARNING("unlocking unlocked spinlock: 0x%lx at %p", lockval, lock); -+} -+ -+#else // CONFIG_SMP -+ -+#define nt_spin_lock(lock) do { } while (0) -+ -+#define nt_spin_unlock(lock) do { } while (0) -+ -+#endif // CONFIG_SMP -+ -+/* When kernel would've disabled preempt (e.g., in interrupt -+ * handlers), we need to fake preempt so driver thinks it is running -+ * at right IRQL */ -+ -+/* raise IRQL to given (higher) IRQL if necessary before locking */ -+static inline KIRQL nt_spin_lock_irql(NT_SPIN_LOCK *lock, KIRQL newirql) -+{ -+ KIRQL oldirql = raise_irql(newirql); -+ nt_spin_lock(lock); -+ return oldirql; -+} -+ -+/* lower IRQL to given (lower) IRQL if necessary after unlocking */ -+static inline void nt_spin_unlock_irql(NT_SPIN_LOCK *lock, KIRQL oldirql) -+{ -+ nt_spin_unlock(lock); -+ lower_irql(oldirql); -+} -+ -+#define nt_spin_lock_irqsave(lock, flags) \ -+do { \ -+ local_irq_save(flags); \ -+ preempt_disable(); \ -+ nt_spin_lock(lock); \ -+} while (0) -+ -+#define nt_spin_unlock_irqrestore(lock, flags) \ -+do { \ -+ nt_spin_unlock(lock); \ -+ preempt_enable_no_resched(); \ -+ local_irq_restore(flags); \ -+ preempt_check_resched(); \ -+} while (0) -+ -+static inline ULONG SPAN_PAGES(void *ptr, SIZE_T length) -+{ -+ return PAGE_ALIGN(((unsigned long)ptr & (PAGE_SIZE - 1)) + length) -+ >> PAGE_SHIFT; -+} -+ -+#ifdef CONFIG_X86_64 -+ -+/* TODO: can these be implemented without using spinlock? */ -+ -+static inline struct nt_slist *PushEntrySList(nt_slist_header *head, -+ struct nt_slist *entry, -+ NT_SPIN_LOCK *lock) -+{ -+ KIRQL irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL); -+ entry->next = head->next; -+ head->next = entry; -+ head->depth++; -+ nt_spin_unlock_irql(lock, irql); -+ TRACE4("%p, %p, %p", head, entry, entry->next); -+ return entry->next; -+} -+ -+static inline struct nt_slist *PopEntrySList(nt_slist_header *head, -+ NT_SPIN_LOCK *lock) -+{ -+ struct nt_slist *entry; -+ KIRQL irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL); -+ entry = head->next; -+ if (entry) { -+ head->next = entry->next; -+ head->depth--; -+ } -+ nt_spin_unlock_irql(lock, irql); -+ TRACE4("%p, %p", head, entry); -+ return entry; -+} -+ -+#else -+ -+#define u64_low_32(x) ((u32)x) -+#define u64_high_32(x) ((u32)(x >> 32)) -+ -+static inline u64 nt_cmpxchg8b(volatile u64 *ptr, u64 old, u64 new) -+{ -+ u64 prev; -+ -+ __asm__ __volatile__( -+ "\n" -+ LOCK_PREFIX "cmpxchg8b %0\n" -+ : "+m" (*ptr), "=A" (prev) -+ : "A" (old), "b" (u64_low_32(new)), "c" (u64_high_32(new))); -+ return prev; -+} -+ -+/* slist routines below update slist atomically - no need for -+ * spinlocks */ -+ -+static inline struct nt_slist *PushEntrySList(nt_slist_header *head, -+ struct nt_slist *entry, -+ NT_SPIN_LOCK *lock) -+{ -+ nt_slist_header old, new; -+ do { -+ old.align = head->align; -+ entry->next = old.next; -+ new.next = entry; -+ new.depth = old.depth + 1; -+ } while (nt_cmpxchg8b(&head->align, old.align, new.align) != old.align); -+ TRACE4("%p, %p, %p", head, entry, old.next); -+ return old.next; -+} -+ -+static inline struct nt_slist *PopEntrySList(nt_slist_header *head, -+ NT_SPIN_LOCK *lock) -+{ -+ struct nt_slist *entry; -+ nt_slist_header old, new; -+ do { -+ old.align = head->align; -+ entry = old.next; -+ if (!entry) -+ break; -+ new.next = entry->next; -+ new.depth = old.depth - 1; -+ } while (nt_cmpxchg8b(&head->align, old.align, new.align) != old.align); -+ TRACE4("%p, %p", head, entry); -+ return entry; -+} -+ -+#endif -+ -+#define sleep_hz(n) \ -+do { \ -+ set_current_state(TASK_INTERRUPTIBLE); \ -+ schedule_timeout(n); \ -+} while (0) -+ -+int ntoskernel_init(void); -+void ntoskernel_exit(void); -+int ntoskernel_init_device(struct wrap_device *wd); -+void ntoskernel_exit_device(struct wrap_device *wd); -+void *allocate_object(ULONG size, enum common_object_type type, -+ struct unicode_string *name); -+ -+#ifdef ENABLE_USB -+int usb_init(void); -+void usb_exit(void); -+#else -+static inline int usb_init(void) { return 0; } -+static inline void usb_exit(void) {} -+#endif -+int usb_init_device(struct wrap_device *wd); -+void usb_exit_device(struct wrap_device *wd); -+ -+int wrap_procfs_init(void); -+void wrap_procfs_remove(void); -+ -+int link_pe_images(struct pe_image *pe_image, unsigned short n); -+ -+int stricmp(const char *s1, const char *s2); -+void dump_bytes(const char *name, const u8 *from, int len); -+struct mdl *allocate_init_mdl(void *virt, ULONG length); -+void free_mdl(struct mdl *mdl); -+struct driver_object *find_bus_driver(const char *name); -+void free_custom_extensions(struct driver_extension *drv_obj_ext); -+struct nt_thread *get_current_nt_thread(void); -+u64 ticks_1601(void); -+int schedule_ntos_work_item(NTOS_WORK_FUNC func, void *arg1, void *arg2); -+void wrap_init_timer(struct nt_timer *nt_timer, enum timer_type type, -+ struct ndis_mp_block *nmb); -+BOOLEAN wrap_set_timer(struct nt_timer *nt_timer, unsigned long expires_hz, -+ unsigned long repeat_hz, struct kdpc *kdpc); -+ -+LONG InterlockedDecrement(LONG volatile *val) wfastcall; -+LONG InterlockedIncrement(LONG volatile *val) wfastcall; -+struct nt_list *ExInterlockedInsertHeadList -+ (struct nt_list *head, struct nt_list *entry, -+ NT_SPIN_LOCK *lock) wfastcall; -+struct nt_list *ExInterlockedInsertTailList -+ (struct nt_list *head, struct nt_list *entry, -+ NT_SPIN_LOCK *lock) wfastcall; -+struct nt_list *ExInterlockedRemoveHeadList -+ (struct nt_list *head, NT_SPIN_LOCK *lock) wfastcall; -+NTSTATUS IofCallDriver(struct device_object *dev_obj, struct irp *irp) wfastcall; -+KIRQL KfRaiseIrql(KIRQL newirql) wfastcall; -+void KfLowerIrql(KIRQL oldirql) wfastcall; -+KIRQL KfAcquireSpinLock(NT_SPIN_LOCK *lock) wfastcall; -+void KfReleaseSpinLock(NT_SPIN_LOCK *lock, KIRQL oldirql) wfastcall; -+void IofCompleteRequest(struct irp *irp, CHAR prio_boost) wfastcall; -+void KefReleaseSpinLockFromDpcLevel(NT_SPIN_LOCK *lock) wfastcall; -+ -+LONG ObfReferenceObject(void *object) wfastcall; -+void ObfDereferenceObject(void *object) wfastcall; -+ -+#define ObReferenceObject(object) ObfReferenceObject(object) -+#define ObDereferenceObject(object) ObfDereferenceObject(object) -+ -+/* prevent expansion of ExAllocatePoolWithTag macro */ -+void *(ExAllocatePoolWithTag)(enum pool_type pool_type, SIZE_T size, -+ ULONG tag) wstdcall; -+ -+void ExFreePool(void *p) wstdcall; -+ULONG MmSizeOfMdl(void *base, ULONG length) wstdcall; -+void __iomem *MmMapIoSpace(PHYSICAL_ADDRESS phys_addr, SIZE_T size, -+ enum memory_caching_type cache) wstdcall; -+void MmUnmapIoSpace(void __iomem *addr, SIZE_T size) wstdcall; -+void MmProbeAndLockPages(struct mdl *mdl, KPROCESSOR_MODE access_mode, -+ enum lock_operation operation) wstdcall; -+void MmUnlockPages(struct mdl *mdl) wstdcall; -+void KeInitializeEvent(struct nt_event *nt_event, -+ enum event_type type, BOOLEAN state) wstdcall; -+LONG KeSetEvent(struct nt_event *nt_event, KPRIORITY incr, -+ BOOLEAN wait) wstdcall; -+LONG KeResetEvent(struct nt_event *nt_event) wstdcall; -+BOOLEAN queue_kdpc(struct kdpc *kdpc); -+BOOLEAN dequeue_kdpc(struct kdpc *kdpc); -+ -+NTSTATUS IoConnectInterrupt(struct kinterrupt **kinterrupt, -+ PKSERVICE_ROUTINE service_routine, -+ void *service_context, NT_SPIN_LOCK *lock, -+ ULONG vector, KIRQL irql, KIRQL synch_irql, -+ enum kinterrupt_mode interrupt_mode, -+ BOOLEAN shareable, KAFFINITY processor_enable_mask, -+ BOOLEAN floating_save) wstdcall; -+void IoDisconnectInterrupt(struct kinterrupt *interrupt) wstdcall; -+BOOLEAN KeSynchronizeExecution(struct kinterrupt *interrupt, -+ PKSYNCHRONIZE_ROUTINE synch_routine, -+ void *ctx) wstdcall; -+ -+NTSTATUS KeWaitForSingleObject(void *object, KWAIT_REASON reason, -+ KPROCESSOR_MODE waitmode, BOOLEAN alertable, -+ LARGE_INTEGER *timeout) wstdcall; -+void MmBuildMdlForNonPagedPool(struct mdl *mdl) wstdcall; -+NTSTATUS IoCreateDevice(struct driver_object *driver, ULONG dev_ext_length, -+ struct unicode_string *dev_name, DEVICE_TYPE dev_type, -+ ULONG dev_chars, BOOLEAN exclusive, -+ struct device_object **dev_obj) wstdcall; -+NTSTATUS IoCreateSymbolicLink(struct unicode_string *link, -+ struct unicode_string *dev_name) wstdcall; -+void IoDeleteDevice(struct device_object *dev) wstdcall; -+void IoDetachDevice(struct device_object *topdev) wstdcall; -+struct device_object *IoGetAttachedDevice(struct device_object *dev) wstdcall; -+struct device_object *IoGetAttachedDeviceReference -+ (struct device_object *dev) wstdcall; -+NTSTATUS IoAllocateDriverObjectExtension -+ (struct driver_object *drv_obj, void *client_id, ULONG extlen, -+ void **ext) wstdcall; -+void *IoGetDriverObjectExtension(struct driver_object *drv, -+ void *client_id) wstdcall; -+struct device_object *IoAttachDeviceToDeviceStack -+ (struct device_object *src, struct device_object *dst) wstdcall; -+BOOLEAN IoCancelIrp(struct irp *irp) wstdcall; -+struct irp *IoBuildSynchronousFsdRequest -+ (ULONG major_func, struct device_object *dev_obj, void *buf, -+ ULONG length, LARGE_INTEGER *offset, struct nt_event *event, -+ struct io_status_block *status) wstdcall; -+ -+NTSTATUS IoPassIrpDown(struct device_object *dev_obj, struct irp *irp) wstdcall; -+WIN_FUNC_DECL(IoPassIrpDown,2); -+NTSTATUS IoSyncForwardIrp(struct device_object *dev_obj, -+ struct irp *irp) wstdcall; -+NTSTATUS IoAsyncForwardIrp(struct device_object *dev_obj, -+ struct irp *irp) wstdcall; -+NTSTATUS IoInvalidDeviceRequest(struct device_object *dev_obj, -+ struct irp *irp) wstdcall; -+ -+void KeInitializeSpinLock(NT_SPIN_LOCK *lock) wstdcall; -+void IoAcquireCancelSpinLock(KIRQL *irql) wstdcall; -+void IoReleaseCancelSpinLock(KIRQL irql) wstdcall; -+ -+NTSTATUS RtlUnicodeStringToAnsiString -+ (struct ansi_string *dst, const struct unicode_string *src, -+ BOOLEAN dup) wstdcall; -+NTSTATUS RtlAnsiStringToUnicodeString -+ (struct unicode_string *dst, const struct ansi_string *src, -+ BOOLEAN dup) wstdcall; -+void RtlInitAnsiString(struct ansi_string *dst, const char *src) wstdcall; -+void RtlInitUnicodeString(struct unicode_string *dest, -+ const wchar_t *src) wstdcall; -+void RtlFreeUnicodeString(struct unicode_string *string) wstdcall; -+void RtlFreeAnsiString(struct ansi_string *string) wstdcall; -+LONG RtlCompareUnicodeString(const struct unicode_string *s1, -+ const struct unicode_string *s2, -+ BOOLEAN case_insensitive) wstdcall; -+NTSTATUS RtlUpcaseUnicodeString(struct unicode_string *dst, -+ struct unicode_string *src, -+ BOOLEAN alloc) wstdcall; -+BOOLEAN KeCancelTimer(struct nt_timer *nt_timer) wstdcall; -+void KeInitializeDpc(struct kdpc *kdpc, void *func, void *ctx) wstdcall; -+ -+extern spinlock_t ntoskernel_lock; -+extern spinlock_t irp_cancel_lock; -+extern struct nt_list object_list; -+extern CCHAR cpu_count; -+#ifdef CONFIG_X86_64 -+extern struct kuser_shared_data kuser_shared_data; -+#endif -+ -+#define IoCompleteRequest(irp, prio) IofCompleteRequest(irp, prio) -+#define IoCallDriver(dev, irp) IofCallDriver(dev, irp) -+ -+#if defined(IO_DEBUG) -+#define DUMP_IRP(_irp) \ -+do { \ -+ struct io_stack_location *_irp_sl; \ -+ _irp_sl = IoGetCurrentIrpStackLocation(_irp); \ -+ IOTRACE("irp: %p, stack size: %d, cl: %d, sl: %p, dev_obj: %p, " \ -+ "mj_fn: %d, minor_fn: %d, nt_urb: %p, event: %p", \ -+ _irp, _irp->stack_count, (_irp)->current_location, \ -+ _irp_sl, _irp_sl->dev_obj, _irp_sl->major_fn, \ -+ _irp_sl->minor_fn, IRP_URB(_irp), \ -+ (_irp)->user_event); \ -+} while (0) -+#else -+#define DUMP_IRP(_irp) do { } while (0) -+#endif -+ -+#endif // _NTOSKERNEL_H_ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/ntoskernel_io.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel_io.c ---- linux-3.12.2/3rdparty/ndiswrapper/ntoskernel_io.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/ntoskernel_io.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,1161 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ntoskernel.h" -+#include "ndis.h" -+#include "wrapndis.h" -+#include "usb.h" -+#include "loader.h" -+#include "ntoskernel_io_exports.h" -+ -+wstdcall void WIN_FUNC(IoAcquireCancelSpinLock,1) -+ (KIRQL *irql) __acquires(irql) -+{ -+ spin_lock_bh(&irp_cancel_lock); -+ *irql = 0; -+} -+ -+wstdcall void WIN_FUNC(IoReleaseCancelSpinLock,1) -+ (KIRQL irql) __releases(irql) -+{ -+ spin_unlock_bh(&irp_cancel_lock); -+} -+ -+wstdcall int WIN_FUNC(IoIsWdmVersionAvailable,2) -+ (UCHAR major, UCHAR minor) -+{ -+ IOENTER("%d, %x", major, minor); -+ if (major == 1 && -+ (minor == 0x30 || // Windows 2003 -+ minor == 0x20 || // Windows XP -+ minor == 0x10)) // Windows 2000 -+ IOEXIT(return TRUE); -+ IOEXIT(return FALSE); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(IoIs32bitProcess,1) -+ (struct irp *irp) -+{ -+#ifdef CONFIG_X86_64 -+ return FALSE; -+#else -+ return TRUE; -+#endif -+} -+ -+wstdcall void WIN_FUNC(IoInitializeIrp,3) -+ (struct irp *irp, USHORT size, CCHAR stack_count) -+{ -+ IOENTER("irp: %p, %d, %d", irp, size, stack_count); -+ -+ memset(irp, 0, size); -+ irp->size = size; -+ irp->stack_count = stack_count; -+ irp->current_location = stack_count; -+ IoGetCurrentIrpStackLocation(irp) = IRP_SL(irp, stack_count); -+ IOEXIT(return); -+} -+ -+wstdcall void WIN_FUNC(IoReuseIrp,2) -+ (struct irp *irp, NTSTATUS status) -+{ -+ IOENTER("%p, %d", irp, status); -+ if (irp) { -+ UCHAR alloc_flags; -+ -+ alloc_flags = irp->alloc_flags; -+ IoInitializeIrp(irp, irp->size, irp->stack_count); -+ irp->alloc_flags = alloc_flags; -+ irp->io_status.status = status; -+ } -+ IOEXIT(return); -+} -+ -+wstdcall struct irp *WIN_FUNC(IoAllocateIrp,2) -+ (char stack_count, BOOLEAN charge_quota) -+{ -+ struct irp *irp; -+ int irp_size; -+ -+ IOENTER("count: %d", stack_count); -+ stack_count++; -+ irp_size = IoSizeOfIrp(stack_count); -+ irp = kmalloc(irp_size, irql_gfp()); -+ if (irp) -+ IoInitializeIrp(irp, irp_size, stack_count); -+ IOTRACE("irp %p", irp); -+ IOEXIT(return irp); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(IoCancelIrp,1) -+ (struct irp *irp) -+{ -+ typeof(irp->cancel_routine) cancel_routine; -+ -+ /* NB: this function may be called at DISPATCH_LEVEL */ -+ IOTRACE("irp: %p", irp); -+ if (!irp) -+ return FALSE; -+ DUMP_IRP(irp); -+ IoAcquireCancelSpinLock(&irp->cancel_irql); -+ cancel_routine = xchg(&irp->cancel_routine, NULL); -+ IOTRACE("%p", cancel_routine); -+ irp->cancel = TRUE; -+ if (cancel_routine) { -+ struct device_object *dev_obj; -+ -+ if (irp->current_location >= 0 && -+ irp->current_location < irp->stack_count) -+ dev_obj = IoGetCurrentIrpStackLocation(irp)->dev_obj; -+ else -+ dev_obj = NULL; -+ IOTRACE("current_location: %d, dev_obj: %p", -+ irp->current_location, dev_obj); -+ /* cancel_routine will release the spin lock */ -+ __release(irp->cancel_irql); -+ LIN2WIN2(cancel_routine, dev_obj, irp); -+ /* in usb's cancel, irp->cancel is set to indicate -+ * status of cancel */ -+ IOEXIT(return xchg(&irp->cancel, TRUE)); -+ } else { -+ IOTRACE("irp %p already canceled", irp); -+ IoReleaseCancelSpinLock(irp->cancel_irql); -+ IOEXIT(return FALSE); -+ } -+} -+ -+wstdcall void IoQueueThreadIrp(struct irp *irp) -+{ -+ struct nt_thread *thread; -+ KIRQL irql; -+ -+ thread = get_current_nt_thread(); -+ if (thread) { -+ IOTRACE("thread: %p, task: %p", thread, thread->task); -+ irp->flags |= IRP_SYNCHRONOUS_API; -+ irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL); -+ InsertTailList(&thread->irps, &irp->thread_list); -+ IoIrpThread(irp) = thread; -+ nt_spin_unlock_irql(&thread->lock, irql); -+ } else -+ IoIrpThread(irp) = NULL; -+} -+ -+wstdcall void IoDequeueThreadIrp(struct irp *irp) -+{ -+ struct nt_thread *thread; -+ KIRQL irql; -+ -+ thread = IoIrpThread(irp); -+ if (thread) { -+ irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL); -+ RemoveEntryList(&irp->thread_list); -+ nt_spin_unlock_irql(&thread->lock, irql); -+ } -+} -+ -+wstdcall void WIN_FUNC(IoFreeIrp,1) -+ (struct irp *irp) -+{ -+ IOENTER("irp = %p", irp); -+ if (!irp) { -+ WARNING("irp is NULL"); -+ return; -+ } -+ -+ if (irp->flags & IRP_SYNCHRONOUS_API) -+ IoDequeueThreadIrp(irp); -+ IoCancelIrp(irp); -+ kfree(irp); -+ -+ IOEXIT(return); -+} -+ -+wstdcall struct irp *WIN_FUNC(IoBuildAsynchronousFsdRequest,6) -+ (ULONG major_fn, struct device_object *dev_obj, void *buffer, -+ ULONG length, LARGE_INTEGER *offset, -+ struct io_status_block *user_status) -+{ -+ struct irp *irp; -+ struct io_stack_location *irp_sl; -+ -+ IOENTER("%p", dev_obj); -+ if (!dev_obj) -+ IOEXIT(return NULL); -+ irp = IoAllocateIrp(dev_obj->stack_count, FALSE); -+ if (irp == NULL) { -+ WARNING("couldn't allocate irp"); -+ IOEXIT(return NULL); -+ } -+ -+ irp_sl = IoGetNextIrpStackLocation(irp); -+ irp_sl->major_fn = major_fn; -+ IOTRACE("major_fn: %d", major_fn); -+ irp_sl->minor_fn = 0; -+ irp_sl->flags = 0; -+ irp_sl->control = 0; -+ irp_sl->dev_obj = dev_obj; -+ irp_sl->file_obj = NULL; -+ irp_sl->completion_routine = NULL; -+ -+ if (dev_obj->flags & DO_DIRECT_IO) { -+ irp->mdl = IoAllocateMdl(buffer, length, FALSE, FALSE, irp); -+ if (irp->mdl == NULL) { -+ IoFreeIrp(irp); -+ return NULL; -+ } -+ MmProbeAndLockPages(irp->mdl, KernelMode, -+ major_fn == IRP_MJ_WRITE ? -+ IoReadAccess : IoWriteAccess); -+ IOTRACE("mdl: %p", irp->mdl); -+ } else if (dev_obj->flags & DO_BUFFERED_IO) { -+ irp->associated_irp.system_buffer = buffer; -+ irp->flags = IRP_BUFFERED_IO; -+ irp->mdl = NULL; -+ IOTRACE("buffer: %p", buffer); -+ } -+ if (major_fn == IRP_MJ_READ) { -+ irp_sl->params.read.length = length; -+ irp_sl->params.read.byte_offset = *offset; -+ } else if (major_fn == IRP_MJ_WRITE) { -+ irp_sl->params.write.length = length; -+ irp_sl->params.write.byte_offset = *offset; -+ } -+ irp->user_status = user_status; -+ IOTRACE("irp: %p", irp); -+ return irp; -+} -+ -+wstdcall struct irp *WIN_FUNC(IoBuildSynchronousFsdRequest,7) -+ (ULONG major_fn, struct device_object *dev_obj, void *buf, -+ ULONG length, LARGE_INTEGER *offset, struct nt_event *event, -+ struct io_status_block *user_status) -+{ -+ struct irp *irp; -+ -+ irp = IoBuildAsynchronousFsdRequest(major_fn, dev_obj, buf, length, -+ offset, user_status); -+ if (irp == NULL) -+ return NULL; -+ irp->user_event = event; -+ IoQueueThreadIrp(irp); -+ return irp; -+} -+ -+wstdcall struct irp *WIN_FUNC(IoBuildDeviceIoControlRequest,9) -+ (ULONG ioctl, struct device_object *dev_obj, -+ void *input_buf, ULONG input_buf_len, void *output_buf, -+ ULONG output_buf_len, BOOLEAN internal_ioctl, -+ struct nt_event *event, struct io_status_block *io_status) -+{ -+ struct irp *irp; -+ struct io_stack_location *irp_sl; -+ ULONG buf_len; -+ -+ IOENTER("%p, 0x%08x, %d", dev_obj, ioctl, internal_ioctl); -+ if (!dev_obj) -+ IOEXIT(return NULL); -+ irp = IoAllocateIrp(dev_obj->stack_count, FALSE); -+ if (irp == NULL) { -+ WARNING("couldn't allocate irp"); -+ return NULL; -+ } -+ irp_sl = IoGetNextIrpStackLocation(irp); -+ irp_sl->params.dev_ioctl.code = ioctl; -+ irp_sl->params.dev_ioctl.input_buf_len = input_buf_len; -+ irp_sl->params.dev_ioctl.output_buf_len = output_buf_len; -+ irp_sl->major_fn = (internal_ioctl) ? -+ IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL; -+ IOTRACE("%d", IO_METHOD_FROM_CTL_CODE(ioctl)); -+ -+ switch (IO_METHOD_FROM_CTL_CODE(ioctl)) { -+ case METHOD_BUFFERED: -+ buf_len = max(input_buf_len, output_buf_len); -+ if (buf_len) { -+ irp->associated_irp.system_buffer = -+ ExAllocatePoolWithTag(NonPagedPool, buf_len, 0); -+ if (!irp->associated_irp.system_buffer) { -+ IoFreeIrp(irp); -+ IOEXIT(return NULL); -+ } -+ irp->associated_irp.system_buffer = input_buf; -+ if (input_buf) -+ memcpy(irp->associated_irp.system_buffer, -+ input_buf, input_buf_len); -+ irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; -+ if (output_buf) -+ irp->flags = IRP_INPUT_OPERATION; -+ irp->user_buf = output_buf; -+ } else -+ irp->user_buf = NULL; -+ break; -+ case METHOD_IN_DIRECT: -+ case METHOD_OUT_DIRECT: -+ if (input_buf) { -+ irp->associated_irp.system_buffer = -+ ExAllocatePoolWithTag(NonPagedPool, -+ input_buf_len, 0); -+ if (!irp->associated_irp.system_buffer) { -+ IoFreeIrp(irp); -+ IOEXIT(return NULL); -+ } -+ memcpy(irp->associated_irp.system_buffer, -+ input_buf, input_buf_len); -+ irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; -+ } -+ /* TODO: we are supposed to setup MDL, but USB layer -+ * doesn't use MDLs. Moreover, USB layer mirrors -+ * non-DMAable buffers, so no need to allocate -+ * DMAable buffer here */ -+ if (output_buf) { -+ irp->associated_irp.system_buffer = -+ ExAllocatePoolWithTag(NonPagedPool, -+ output_buf_len, 0); -+ if (!irp->associated_irp.system_buffer) { -+ IoFreeIrp(irp); -+ IOEXIT(return NULL); -+ } -+ irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; -+ } -+ break; -+ case METHOD_NEITHER: -+ irp->user_buf = output_buf; -+ irp_sl->params.dev_ioctl.type3_input_buf = input_buf; -+ break; -+ } -+ -+ irp->user_status = io_status; -+ irp->user_event = event; -+ IoQueueThreadIrp(irp); -+ -+ IOTRACE("irp: %p", irp); -+ IOEXIT(return irp); -+} -+ -+wfastcall NTSTATUS WIN_FUNC(IofCallDriver,2) -+ (struct device_object *dev_obj, struct irp *irp) -+{ -+ struct io_stack_location *irp_sl; -+ NTSTATUS status; -+ driver_dispatch_t *major_func; -+ struct driver_object *drv_obj; -+ -+ if (irp->current_location <= 0) { -+ ERROR("invalid irp: %p, %d", irp, irp->current_location); -+ return STATUS_INVALID_PARAMETER; -+ } -+ IOTRACE("%p, %p, %p, %d, %d, %p", dev_obj, irp, dev_obj->drv_obj, -+ irp->current_location, irp->stack_count, -+ IoGetCurrentIrpStackLocation(irp)); -+ IoSetNextIrpStackLocation(irp); -+ DUMP_IRP(irp); -+ irp_sl = IoGetCurrentIrpStackLocation(irp); -+ drv_obj = dev_obj->drv_obj; -+ irp_sl->dev_obj = dev_obj; -+ major_func = drv_obj->major_func[irp_sl->major_fn]; -+ IOTRACE("major_func: %p, dev_obj: %p", major_func, dev_obj); -+ if (major_func) -+ status = LIN2WIN2(major_func, dev_obj, irp); -+ else { -+ ERROR("major_function %d is not implemented", -+ irp_sl->major_fn); -+ status = STATUS_NOT_SUPPORTED; -+ } -+ IOEXIT(return status); -+} -+ -+wfastcall void WIN_FUNC(IofCompleteRequest,2) -+ (struct irp *irp, CHAR prio_boost) -+{ -+ struct io_stack_location *irp_sl; -+ -+#ifdef IO_DEBUG -+ DUMP_IRP(irp); -+ if (irp->io_status.status == STATUS_PENDING) { -+ ERROR("invalid irp: %p, STATUS_PENDING", irp); -+ return; -+ } -+ if (irp->current_location < 0 || -+ irp->current_location >= irp->stack_count) { -+ ERROR("invalid irp: %p, %d", irp, irp->current_location); -+ return; -+ } -+#endif -+ for (irp_sl = IoGetCurrentIrpStackLocation(irp); -+ irp->current_location < irp->stack_count; irp_sl++) { -+ struct device_object *dev_obj; -+ NTSTATUS status; -+ -+ DUMP_IRP(irp); -+ if (irp_sl->control & SL_PENDING_RETURNED) -+ irp->pending_returned = TRUE; -+ -+ /* current_location and dev_obj must be same as when -+ * driver called IoSetCompletionRoutine, which sets -+ * completion routine at next (lower) location, which -+ * is what we are going to call below; so we set -+ * current_location and dev_obj for the previous -+ * (higher) location */ -+ IoSkipCurrentIrpStackLocation(irp); -+ if (irp->current_location < irp->stack_count) -+ dev_obj = IoGetCurrentIrpStackLocation(irp)->dev_obj; -+ else -+ dev_obj = NULL; -+ -+ IOTRACE("%d, %d, %p", irp->current_location, irp->stack_count, -+ dev_obj); -+ if (irp_sl->completion_routine && -+ ((irp->io_status.status == STATUS_SUCCESS && -+ irp_sl->control & SL_INVOKE_ON_SUCCESS) || -+ (irp->io_status.status != STATUS_SUCCESS && -+ irp_sl->control & SL_INVOKE_ON_ERROR) || -+ (irp->cancel == TRUE && -+ irp_sl->control & SL_INVOKE_ON_CANCEL))) { -+ IOTRACE("calling completion_routine at: %p, %p", -+ irp_sl->completion_routine, irp_sl->context); -+ status = LIN2WIN3(irp_sl->completion_routine, -+ dev_obj, irp, irp_sl->context); -+ IOTRACE("status: %08X", status); -+ if (status == STATUS_MORE_PROCESSING_REQUIRED) -+ IOEXIT(return); -+ } else { -+ /* propagate pending status to next irp_sl */ -+ if (irp->pending_returned && -+ irp->current_location < irp->stack_count) -+ IoMarkIrpPending(irp); -+ } -+ } -+ -+ if (irp->user_status) { -+ irp->user_status->status = irp->io_status.status; -+ irp->user_status->info = irp->io_status.info; -+ } -+ -+ if (irp->user_event) { -+ IOTRACE("setting event %p", irp->user_event); -+ KeSetEvent(irp->user_event, prio_boost, FALSE); -+ } -+ -+ if (irp->associated_irp.system_buffer && -+ (irp->flags & IRP_DEALLOCATE_BUFFER)) -+ ExFreePool(irp->associated_irp.system_buffer); -+ else { -+ struct mdl *mdl; -+ while ((mdl = irp->mdl)) { -+ irp->mdl = mdl->next; -+ MmUnlockPages(mdl); -+ IoFreeMdl(mdl); -+ } -+ } -+ IOTRACE("freeing irp %p", irp); -+ IoFreeIrp(irp); -+ IOEXIT(return); -+} -+ -+wstdcall NTSTATUS IoPassIrpDown(struct device_object *dev_obj, struct irp *irp) -+{ -+ IoSkipCurrentIrpStackLocation(irp); -+ IOEXIT(return IoCallDriver(dev_obj, irp)); -+} -+ -+wstdcall NTSTATUS IoIrpSyncComplete(struct device_object *dev_obj, -+ struct irp *irp, void *context) -+{ -+ if (irp->pending_returned == TRUE) -+ KeSetEvent(context, IO_NO_INCREMENT, FALSE); -+ IOEXIT(return STATUS_MORE_PROCESSING_REQUIRED); -+} -+WIN_FUNC_DECL(IoIrpSyncComplete,3) -+ -+wstdcall NTSTATUS IoSyncForwardIrp(struct device_object *dev_obj, -+ struct irp *irp) -+{ -+ struct nt_event event; -+ NTSTATUS status; -+ -+ IoCopyCurrentIrpStackLocationToNext(irp); -+ KeInitializeEvent(&event, SynchronizationEvent, FALSE); -+ /* completion function is called as Windows function */ -+ IoSetCompletionRoutine(irp, WIN_FUNC_PTR(IoIrpSyncComplete,3), &event, -+ TRUE, TRUE, TRUE); -+ status = IoCallDriver(dev_obj, irp); -+ IOTRACE("%08X", status); -+ if (status == STATUS_PENDING) { -+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, -+ NULL); -+ status = irp->io_status.status; -+ } -+ IOTRACE("%08X", status); -+ IOEXIT(return status); -+} -+WIN_FUNC_DECL(IoSyncForwardIrp,2) -+ -+wstdcall NTSTATUS IoAsyncForwardIrp(struct device_object *dev_obj, -+ struct irp *irp) -+{ -+ NTSTATUS status; -+ -+ IoCopyCurrentIrpStackLocationToNext(irp); -+ status = IoCallDriver(dev_obj, irp); -+ IOEXIT(return status); -+} -+WIN_FUNC_DECL(IoAsyncForwardIrp,2) -+ -+wstdcall NTSTATUS IoInvalidDeviceRequest(struct device_object *dev_obj, -+ struct irp *irp) -+{ -+ struct io_stack_location *irp_sl; -+ NTSTATUS status; -+ -+ irp_sl = IoGetCurrentIrpStackLocation(irp); -+ WARNING("%d:%d not implemented", irp_sl->major_fn, irp_sl->minor_fn); -+ irp->io_status.status = STATUS_SUCCESS; -+ irp->io_status.info = 0; -+ status = irp->io_status.status; -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ IOEXIT(return status); -+} -+WIN_FUNC_DECL(IoInvalidDeviceRequest,2) -+ -+static irqreturn_t io_irq_isr(int irq, void *data ISR_PT_REGS_PARAM_DECL) -+{ -+ struct kinterrupt *interrupt = data; -+ BOOLEAN ret; -+ -+#ifdef CONFIG_DEBUG_SHIRQ -+ if (!interrupt->u.enabled) -+ EXIT1(return IRQ_NONE); -+#endif -+ TRACE6("%p", interrupt); -+ nt_spin_lock(interrupt->actual_lock); -+ ret = LIN2WIN2(interrupt->isr, interrupt, interrupt->isr_ctx); -+ nt_spin_unlock(interrupt->actual_lock); -+ if (ret == TRUE) -+ EXIT6(return IRQ_HANDLED); -+ else -+ EXIT6(return IRQ_NONE); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoConnectInterrupt,11) -+ (struct kinterrupt **kinterrupt, PKSERVICE_ROUTINE isr, void *isr_ctx, -+ NT_SPIN_LOCK *lock, ULONG vector, KIRQL irql, KIRQL synch_irql, -+ enum kinterrupt_mode mode, BOOLEAN shared, KAFFINITY cpu_mask, -+ BOOLEAN save_fp) -+{ -+ struct kinterrupt *interrupt; -+ IOENTER(""); -+ interrupt = kzalloc(sizeof(*interrupt), GFP_KERNEL); -+ if (!interrupt) -+ IOEXIT(return STATUS_INSUFFICIENT_RESOURCES); -+ interrupt->vector = vector; -+ interrupt->cpu_mask = cpu_mask; -+ nt_spin_lock_init(&interrupt->lock); -+ if (lock) -+ interrupt->actual_lock = lock; -+ else -+ interrupt->actual_lock = &interrupt->lock; -+ interrupt->shared = shared; -+ interrupt->save_fp = save_fp; -+ interrupt->isr = isr; -+ interrupt->isr_ctx = isr_ctx; -+ InitializeListHead(&interrupt->list); -+ interrupt->irql = irql; -+ interrupt->synch_irql = synch_irql; -+ interrupt->mode = mode; -+ if (request_irq(vector, io_irq_isr, shared ? IRQF_SHARED : 0, -+ DRIVER_NAME, interrupt)) { -+ WARNING("request for irq %d failed", vector); -+ kfree(interrupt); -+ IOEXIT(return STATUS_INSUFFICIENT_RESOURCES); -+ } -+ *kinterrupt = interrupt; -+#ifdef CONFIG_DEBUG_SHIRQ -+ interrupt->u.enabled = 1; -+#endif -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(IoDisconnectInterrupt,1) -+ (struct kinterrupt *interrupt) -+{ -+#ifdef CONFIG_DEBUG_SHIRQ -+ interrupt->u.enabled = 0; -+#endif -+ free_irq(interrupt->vector, interrupt); -+ kfree(interrupt); -+} -+ -+wstdcall struct mdl *WIN_FUNC(IoAllocateMdl,5) -+ (void *virt, ULONG length, BOOLEAN second_buf, BOOLEAN charge_quota, -+ struct irp *irp) -+{ -+ struct mdl *mdl; -+ mdl = allocate_init_mdl(virt, length); -+ if (!mdl) -+ return NULL; -+ if (irp) { -+ if (second_buf == TRUE) { -+ struct mdl *last; -+ -+ last = irp->mdl; -+ while (last->next) -+ last = last->next; -+ last->next = mdl; -+ } else -+ irp->mdl = mdl; -+ } -+ IOTRACE("%p", mdl); -+ return mdl; -+} -+ -+wstdcall void WIN_FUNC(IoFreeMdl,1) -+ (struct mdl *mdl) -+{ -+ IOTRACE("%p", mdl); -+ free_mdl(mdl); -+} -+ -+wstdcall struct io_workitem *WIN_FUNC(IoAllocateWorkItem,1) -+ (struct device_object *dev_obj) -+{ -+ struct io_workitem *io_workitem; -+ -+ IOENTER("%p", dev_obj); -+ io_workitem = kmalloc(sizeof(*io_workitem), irql_gfp()); -+ if (!io_workitem) -+ IOEXIT(return NULL); -+ io_workitem->dev_obj = dev_obj; -+ IOEXIT(return io_workitem); -+} -+ -+wstdcall void WIN_FUNC(IoFreeWorkItem,1) -+ (struct io_workitem *io_workitem) -+{ -+ kfree(io_workitem); -+ IOEXIT(return); -+} -+ -+wstdcall void WIN_FUNC(IoQueueWorkItem,4) -+ (struct io_workitem *io_workitem, void *func, -+ enum work_queue_type queue_type, void *context) -+{ -+ IOENTER("%p, %p", io_workitem, io_workitem->dev_obj); -+ io_workitem->worker_routine = func; -+ io_workitem->context = context; -+ schedule_ntos_work_item(func, io_workitem->dev_obj, context); -+ IOEXIT(return); -+} -+ -+wstdcall void WIN_FUNC(ExQueueWorkItem,2) -+ (struct io_workitem *io_workitem, enum work_queue_type queue_type) -+{ -+ IOENTER("%p", io_workitem); -+ schedule_ntos_work_item(io_workitem->worker_routine, -+ io_workitem->dev_obj, io_workitem->context); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoAllocateDriverObjectExtension,4) -+ (struct driver_object *drv_obj, void *client_id, ULONG extlen, -+ void **ext) -+{ -+ struct custom_ext *ce; -+ -+ IOENTER("%p, %p", drv_obj, client_id); -+ ce = kmalloc(sizeof(*ce) + extlen, irql_gfp()); -+ if (ce == NULL) -+ return STATUS_INSUFFICIENT_RESOURCES; -+ -+ IOTRACE("custom_ext: %p", ce); -+ ce->client_id = client_id; -+ spin_lock_bh(&ntoskernel_lock); -+ InsertTailList(&drv_obj->drv_ext->custom_ext, &ce->list); -+ spin_unlock_bh(&ntoskernel_lock); -+ -+ *ext = (void *)ce + sizeof(*ce); -+ IOTRACE("ext: %p", *ext); -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall void *WIN_FUNC(IoGetDriverObjectExtension,2) -+ (struct driver_object *drv_obj, void *client_id) -+{ -+ struct custom_ext *ce; -+ void *ret; -+ -+ IOENTER("drv_obj: %p, client_id: %p", drv_obj, client_id); -+ ret = NULL; -+ spin_lock_bh(&ntoskernel_lock); -+ nt_list_for_each_entry(ce, &drv_obj->drv_ext->custom_ext, list) { -+ if (ce->client_id == client_id) { -+ ret = (void *)ce + sizeof(*ce); -+ break; -+ } -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ IOTRACE("ret: %p", ret); -+ return ret; -+} -+ -+void free_custom_extensions(struct driver_extension *drv_ext) -+{ -+ struct nt_list *ent; -+ -+ IOENTER("%p", drv_ext); -+ spin_lock_bh(&ntoskernel_lock); -+ while ((ent = RemoveHeadList(&drv_ext->custom_ext))) -+ kfree(ent); -+ spin_unlock_bh(&ntoskernel_lock); -+ IOEXIT(return); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoCreateDevice,7) -+ (struct driver_object *drv_obj, ULONG dev_ext_length, -+ struct unicode_string *dev_name, DEVICE_TYPE dev_type, -+ ULONG dev_chars, BOOLEAN exclusive, struct device_object **newdev) -+{ -+ struct device_object *dev; -+ struct dev_obj_ext *dev_obj_ext; -+ int size; -+ -+ IOENTER("%p, %u, %p", drv_obj, dev_ext_length, dev_name); -+ -+ size = sizeof(*dev) + dev_ext_length + sizeof(*dev_obj_ext); -+ dev = allocate_object(size, OBJECT_TYPE_DEVICE, dev_name); -+ if (!dev) -+ IOEXIT(return STATUS_INSUFFICIENT_RESOURCES); -+ if (dev_ext_length) -+ dev->dev_ext = dev + 1; -+ else -+ dev->dev_ext = NULL; -+ -+ dev_obj_ext = ((void *)(dev + 1)) + dev_ext_length; -+ dev_obj_ext->dev_obj = dev; -+ dev_obj_ext->size = 0; -+ dev_obj_ext->type = IO_TYPE_DEVICE; -+ dev->dev_obj_ext = dev_obj_ext; -+ -+ dev->type = dev_type; -+ dev->flags = 0; -+ dev->size = sizeof(*dev) + dev_ext_length; -+ dev->ref_count = 1; -+ dev->attached = NULL; -+ dev->stack_count = 1; -+ -+ dev->drv_obj = drv_obj; -+ dev->next = drv_obj->dev_obj; -+ drv_obj->dev_obj = dev; -+ -+ dev->align_req = 1; -+ dev->characteristics = dev_chars; -+ dev->io_timer = NULL; -+ KeInitializeEvent(&dev->lock, SynchronizationEvent, TRUE); -+ dev->vpb = NULL; -+ -+ IOTRACE("dev: %p, ext: %p", dev, dev->dev_ext); -+ *newdev = dev; -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoCreateUnprotectedSymbolicLink,2) -+ (struct unicode_string *link, struct unicode_string *dev_name) -+{ -+ struct ansi_string ansi; -+ -+ IOENTER("%p, %p", dev_name, link); -+ if (dev_name && (RtlUnicodeStringToAnsiString(&ansi, dev_name, TRUE) == -+ STATUS_SUCCESS)) { -+ IOTRACE("dev_name: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ if (link && (RtlUnicodeStringToAnsiString(&ansi, link, TRUE) == -+ STATUS_SUCCESS)) { -+ IOTRACE("link: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+// TODO(); -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoCreateSymbolicLink,2) -+ (struct unicode_string *link, struct unicode_string *dev_name) -+{ -+ IOEXIT(return IoCreateUnprotectedSymbolicLink(link, dev_name)); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoDeleteSymbolicLink,1) -+ (struct unicode_string *link) -+{ -+ struct ansi_string ansi; -+ -+ IOENTER("%p", link); -+ if (link && (RtlUnicodeStringToAnsiString(&ansi, link, TRUE) == -+ STATUS_SUCCESS)) { -+ IOTRACE("dev_name: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(IoDeleteDevice,1) -+ (struct device_object *dev) -+{ -+ IOENTER("%p", dev); -+ if (dev == NULL) -+ IOEXIT(return); -+ IOTRACE("drv_obj: %p", dev->drv_obj); -+ if (dev->drv_obj) { -+ struct device_object *prev; -+ -+ prev = dev->drv_obj->dev_obj; -+ IOTRACE("dev_obj: %p", prev); -+ if (prev == dev) -+ dev->drv_obj->dev_obj = dev->next; -+ else if (prev) { -+ while (prev->next != dev) -+ prev = prev->next; -+ prev->next = dev->next; -+ } -+ } -+ ObDereferenceObject(dev); -+ IOEXIT(return); -+} -+ -+wstdcall void WIN_FUNC(IoDetachDevice,1) -+ (struct device_object *tgt) -+{ -+ struct device_object *tail; -+ -+ IOENTER("%p", tgt); -+ if (!tgt) -+ IOEXIT(return); -+ tail = tgt->attached; -+ if (!tail) -+ IOEXIT(return); -+ IOTRACE("tail: %p", tail); -+ -+ spin_lock_bh(&ntoskernel_lock); -+ tgt->attached = tail->attached; -+ IOTRACE("attached:%p", tgt->attached); -+ for ( ; tail; tail = tail->attached) { -+ IOTRACE("tail:%p", tail); -+ tail->stack_count--; -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ IOEXIT(return); -+} -+ -+wstdcall struct device_object *WIN_FUNC(IoGetAttachedDevice,1) -+ (struct device_object *dev) -+{ -+ IOENTER("%p", dev); -+ if (!dev) -+ IOEXIT(return NULL); -+ spin_lock_bh(&ntoskernel_lock); -+ while (dev->attached) -+ dev = dev->attached; -+ spin_unlock_bh(&ntoskernel_lock); -+ IOEXIT(return dev); -+} -+ -+wstdcall struct device_object *WIN_FUNC(IoGetAttachedDeviceReference,1) -+ (struct device_object *dev) -+{ -+ IOENTER("%p", dev); -+ if (!dev) -+ IOEXIT(return NULL); -+ dev = IoGetAttachedDevice(dev); -+ ObReferenceObject(dev); -+ IOEXIT(return dev); -+} -+ -+wstdcall struct device_object *WIN_FUNC(IoAttachDeviceToDeviceStack,2) -+ (struct device_object *src, struct device_object *tgt) -+{ -+ struct device_object *attached; -+ struct dev_obj_ext *src_dev_ext; -+ -+ IOENTER("%p, %p", src, tgt); -+ attached = IoGetAttachedDevice(tgt); -+ IOTRACE("%p", attached); -+ src_dev_ext = src->dev_obj_ext; -+ spin_lock_bh(&ntoskernel_lock); -+ if (attached) -+ attached->attached = src; -+ src->attached = NULL; -+ src->stack_count = attached->stack_count + 1; -+ src_dev_ext->attached_to = attached; -+ spin_unlock_bh(&ntoskernel_lock); -+ IOTRACE("stack_count: %d -> %d", attached->stack_count, -+ src->stack_count); -+ IOEXIT(return attached); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoGetDeviceProperty,5) -+ (struct device_object *pdo, enum device_registry_property dev_property, -+ ULONG buffer_len, void *buffer, ULONG *result_len) -+{ -+ struct ansi_string ansi; -+ struct unicode_string unicode; -+ struct wrap_device *wd; -+ ULONG need; -+ -+ IOENTER("dev_obj = %p, dev_property = %d, buffer_len = %u, " -+ "buffer = %p, result_len = %p", pdo, dev_property, -+ buffer_len, buffer, result_len); -+ -+ wd = pdo->reserved; -+ switch (dev_property) { -+ case DevicePropertyDeviceDescription: -+ case DevicePropertyFriendlyName: -+ case DevicePropertyDriverKeyName: -+ if (wrap_is_pci_bus(wd->dev_bus)) -+ RtlInitAnsiString(&ansi, "PCI"); -+ else // if (wrap_is_usb_bus(wd->dev_bus)) -+ RtlInitAnsiString(&ansi, "USB"); -+ need = sizeof(wchar_t) * (ansi.max_length + 1); -+ if (buffer_len < need) { -+ *result_len = need; -+ IOEXIT(return STATUS_BUFFER_TOO_SMALL); -+ } -+ unicode.max_length = buffer_len; -+ unicode.buf = buffer; -+ if (RtlAnsiStringToUnicodeString(&unicode, &ansi, -+ FALSE) != STATUS_SUCCESS) { -+ *result_len = unicode.length; -+ IOEXIT(return STATUS_BUFFER_TOO_SMALL); -+ } -+ IOEXIT(return STATUS_SUCCESS); -+ default: -+ WARNING("%d not implemented", dev_property); -+ IOEXIT(return STATUS_INVALID_PARAMETER_2); -+ } -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoGetDeviceObjectPointer,4) -+ (struct unicode_string *name, ACCESS_MASK desired_access, -+ struct file_object *file_obj, struct device_object *dev_obj) -+{ -+ struct common_object_header *coh; -+ -+ dev_obj = NULL; -+ /* TODO: access is not checked and file_obj is filled with zeroes */ -+ spin_lock_bh(&ntoskernel_lock); -+ nt_list_for_each_entry(coh, &object_list, list) { -+ TRACE5("header: %p, type: %d", coh, coh->type); -+ if (coh->type != OBJECT_TYPE_DEVICE) -+ continue; -+ if (!RtlCompareUnicodeString(&coh->name, name, TRUE)) { -+ dev_obj = HEADER_TO_OBJECT(coh); -+ TRACE5("dev_obj: %p", dev_obj); -+ break; -+ } -+ } -+ spin_unlock_bh(&ntoskernel_lock); -+ if (dev_obj) { -+ memset(file_obj, 0, sizeof(*file_obj)); -+ WARNING("file_obj filled with zeroes"); -+ IOEXIT(return STATUS_SUCCESS); -+ } else -+ IOEXIT(return STATUS_OBJECT_NAME_INVALID); -+} -+ -+/* NOTE: Make sure to compile with -freg-struct-return, so gcc will -+ * return union in register, like Windows */ -+wstdcall union power_state WIN_FUNC(PoSetPowerState,3) -+ (struct device_object *dev_obj, enum power_state_type type, -+ union power_state state) -+{ -+ IOEXIT(return state); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(PoCallDriver,2) -+ (struct device_object *dev_obj, struct irp *irp) -+{ -+ return IoCallDriver(dev_obj, irp); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(PoRequestPowerIrp,6) -+ (struct device_object *dev_obj, UCHAR minor_fn, -+ union power_state power_state, void *completion_func, -+ void *context, struct irp **pirp) -+{ -+ struct irp *irp; -+ struct io_stack_location *irp_sl; -+ -+ TRACE1("%p, %d, %p", dev_obj, dev_obj->stack_count, dev_obj->drv_obj); -+ irp = IoAllocateIrp(dev_obj->stack_count, FALSE); -+ if (!irp) -+ return STATUS_INSUFFICIENT_RESOURCES; -+ irp_sl = IoGetNextIrpStackLocation(irp); -+ irp_sl->major_fn = IRP_MJ_POWER; -+ irp_sl->minor_fn = minor_fn; -+ if (minor_fn == IRP_MN_WAIT_WAKE) -+ irp_sl->params.power.type = SystemPowerState; -+ else -+ irp_sl->params.power.type = DevicePowerState; -+ irp_sl->params.power.state = power_state; -+ irp_sl->completion_routine = completion_func; -+ irp->io_status.status = STATUS_NOT_SUPPORTED; -+ *pirp = irp; -+ return PoCallDriver(dev_obj, irp); -+} -+ -+wstdcall void WIN_FUNC(PoStartNextPowerIrp,1) -+ (struct irp *irp) -+{ -+ IOENTER("irp = %p", irp); -+ IOEXIT(return); -+} -+ -+wstdcall void WIN_FUNC(IoInitializeRemoveLockEx,5) -+ (struct io_remove_lock *lock, ULONG alloc_tag, ULONG max_locked_min, -+ ULONG high_mark, ULONG lock_size) -+{ -+ TODO(); -+} -+ -+wstdcall void *WIN_FUNC(IoAllocateErrorLogEntry,2) -+ (void *io_object, UCHAR entry_size) -+{ -+ /* not implemented fully */ -+ void *ret = kmalloc(sizeof(struct io_error_log_packet) + entry_size, -+ irql_gfp()); -+ TRACE2("%p", ret); -+ if (ret) -+ return ret + sizeof(struct io_error_log_packet); -+ else -+ return NULL; -+} -+ -+wstdcall void WIN_FUNC(IoWriteErrorLogEntry,1) -+ (void *entry) -+{ -+ /* TODO: log error with codes and message */ -+ ERROR(""); -+} -+ -+wstdcall void WIN_FUNC(IoFreeErrorLogEntry,1) -+ (void *entry) -+{ -+ TRACE2("%p", entry); -+ kfree(entry - sizeof(struct io_error_log_packet)); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoAcquireRemoveLockEx,5) -+ (struct io_remove_lock *lock, void *tag, char *file, ULONG line, -+ ULONG lock_size) -+{ -+ TODO(); -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoReleaseRemoveLockEx,3) -+ (struct io_remove_lock *lock, void *tag, ULONG lock_size) -+{ -+ TODO(); -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoRegisterDeviceInterface,4) -+ (struct device_object *pdo, struct guid *guid_class, -+ struct unicode_string *reference, struct unicode_string *link) -+{ -+ struct ansi_string ansi; -+ -+ /* TODO: check if pdo is valid */ -+ RtlInitAnsiString(&ansi, "ndis"); -+ ENTER1("pdo: %p, ref: %p, link: %p, %x, %x, %x", pdo, reference, link, -+ guid_class->data1, guid_class->data2, guid_class->data3); -+ return RtlAnsiStringToUnicodeString(link, &ansi, TRUE); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoSetDeviceInterfaceState,2) -+ (struct unicode_string *link, BOOLEAN enable) -+{ -+ ENTER1("link: %p, enable: %d", link, enable); -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoOpenDeviceRegistryKey,4) -+ (struct device_object *dev_obj, ULONG type, ACCESS_MASK mask, -+ void **handle) -+{ -+ ENTER1("dev_obj: %p", dev_obj); -+ *handle = dev_obj; -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoWMIRegistrationControl,2) -+ (struct device_object *dev_obj, ULONG action) -+{ -+ ENTER2("%p, %d", dev_obj, action); -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(IoInvalidateDeviceRelations,2) -+ (struct device_object *dev_obj, enum device_relation_type type) -+{ -+ INFO("%p, %d", dev_obj, type); -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(IoInvalidateDeviceState,1) -+ (struct device_object *pdo) -+{ -+ INFO("%p", pdo); -+ TODO(); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoRegisterPlugPlayNotification,7) -+ (UINT category, ULONG flags, void *data, struct driver_object *object, -+ void *callback, void *context, void **notification_entry) -+{ -+ TRACE2("category: %d, flags 0x%x, data: %p, object: %p, callback: %p, " -+ "context: %p, notification_entry: %p", category, flags, data, -+ object, callback, context, notification_entry); -+ TODO(); -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoUnregisterPlugPlayNotification,1) -+ (void *notification_entry) -+{ -+ TRACE2("%p", notification_entry); -+ TODO(); -+ IOEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoWMIOpenBlock,3) -+ (struct guid *guid, ULONG access, void *object) -+{ -+ TODO(); -+ IOEXIT(return STATUS_NOT_IMPLEMENTED); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(IoWMIQueryAllData,3) -+ (void *data_block_object, UINT *buffer_size, void *buffer) -+{ -+ TODO(); -+ IOEXIT(return STATUS_NOT_IMPLEMENTED); -+} -+ -+wstdcall void WIN_FUNC(IoGetStackLimits,2) -+ (ULONG_PTR *LowLimit, ULONG_PTR *HighLimit) -+{ -+ *LowLimit = (ULONG_PTR)&LowLimit & ~(THREAD_SIZE - 1); -+ *HighLimit = *LowLimit + THREAD_SIZE; -+ IOTRACE("LowLimit: 0x%lx, HighLimit: 0x%lx", *LowLimit, *HighLimit); -+ IOEXIT(return); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.c ---- linux-3.12.2/3rdparty/ndiswrapper/pe_linker.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,600 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifdef TEST_LOADER -+ -+#include "usr_linker.h" -+ -+#else -+ -+#include -+#include -+ -+//#define DEBUGLINKER 2 -+ -+#include "ntoskernel.h" -+ -+#endif -+ -+struct pe_exports { -+ char *dll; -+ char *name; -+ generic_func addr; -+}; -+ -+static struct pe_exports pe_exports[40]; -+static int num_pe_exports; -+ -+#define RVA2VA(image, rva, type) (type)(ULONG_PTR)((void *)image + rva) -+#define CHECK_SZ(a,b) { if (sizeof(a) != b) { \ -+ ERROR("%s is bad, got %zd, expected %d", \ -+ #a , sizeof(a), (b)); return -EINVAL; } } -+ -+#if defined(DEBUGLINKER) && DEBUGLINKER > 0 -+#define DBGLINKER(fmt, ...) printk(KERN_INFO "%s (%s:%d): " fmt "\n", \ -+ DRIVER_NAME, __func__, \ -+ __LINE__ , ## __VA_ARGS__); -+static const char *image_directory_name[] = { -+ "EXPORT", -+ "IMPORT", -+ "RESOURCE", -+ "EXCEPTION", -+ "SECURITY", -+ "BASERELOC", -+ "DEBUG", -+ "COPYRIGHT", -+ "GLOBALPTR", -+ "TLS", -+ "LOAD_CONFIG", -+ "BOUND_IMPORT", -+ "IAT", -+ "DELAY_IMPORT", -+ "COM_DESCRIPTOR" }; -+#else -+#define DBGLINKER(fmt, ...) do { } while (0) -+#endif -+ -+#ifndef TEST_LOADER -+extern struct wrap_export ntoskernel_exports[], ntoskernel_io_exports[], -+ ndis_exports[], crt_exports[], hal_exports[], rtl_exports[]; -+#ifdef ENABLE_USB -+extern struct wrap_export usb_exports[]; -+#endif -+ -+static int get_export(char *name, generic_func *func) -+{ -+ int i, j; -+ -+ struct wrap_export *exports[] = { -+ ntoskernel_exports, -+ ntoskernel_io_exports, -+ ndis_exports, -+ crt_exports, -+ hal_exports, -+ rtl_exports, -+#ifdef ENABLE_USB -+ usb_exports, -+#endif -+ }; -+ -+ for (j = 0; j < ARRAY_SIZE(exports); j++) -+ for (i = 0; exports[j][i].name != NULL; i++) -+ if (strcmp(exports[j][i].name, name) == 0) { -+ *func = exports[j][i].func; -+ return 0; -+ } -+ -+ for (i = 0; i < num_pe_exports; i++) -+ if (strcmp(pe_exports[i].name, name) == 0) { -+ *func = pe_exports[i].addr; -+ return 0; -+ } -+ -+ return -1; -+} -+#endif // TEST_LOADER -+ -+static void *get_dll_init(char *name) -+{ -+ int i; -+ for (i = 0; i < num_pe_exports; i++) -+ if ((strcmp(pe_exports[i].dll, name) == 0) && -+ (strcmp(pe_exports[i].name, "DllInitialize") == 0)) -+ return (void *)pe_exports[i].addr; -+ return NULL; -+} -+ -+/* -+ * Find and validate the coff header -+ * -+ */ -+static int check_nt_hdr(IMAGE_NT_HEADERS *nt_hdr) -+{ -+ int i; -+ WORD attr; -+ PIMAGE_OPTIONAL_HEADER opt_hdr; -+ -+ /* Validate the "PE\0\0" signature */ -+ if (nt_hdr->Signature != IMAGE_NT_SIGNATURE) { -+ ERROR("is this driver file? bad signature %08x", -+ nt_hdr->Signature); -+ return -EINVAL; -+ } -+ -+ opt_hdr = &nt_hdr->OptionalHeader; -+ /* Make sure Image is PE32 or PE32+ */ -+#ifdef CONFIG_X86_64 -+ if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { -+ ERROR("kernel is 64-bit, but Windows driver is not 64-bit;" -+ "bad magic: %04X", opt_hdr->Magic); -+ return -EINVAL; -+ } -+#else -+ if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { -+ ERROR("kernel is 32-bit, but Windows driver is not 32-bit;" -+ "bad magic: %04X", opt_hdr->Magic); -+ return -EINVAL; -+ } -+#endif -+ -+ /* Validate the image for the current architecture. */ -+#ifdef CONFIG_X86_64 -+ if (nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) { -+ ERROR("kernel is 64-bit, but Windows driver is not 64-bit;" -+ " (PE signature is %04X)", nt_hdr->FileHeader.Machine); -+ return -EINVAL; -+ } -+#else -+ if (nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) { -+ ERROR("kernel is 32-bit, but Windows driver is not 32-bit;" -+ " (PE signature is %04X)", nt_hdr->FileHeader.Machine); -+ return -EINVAL; -+ } -+#endif -+ -+ /* Must have attributes */ -+#ifdef CONFIG_X86_64 -+ attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE; -+#else -+ attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE; -+#endif -+ if ((nt_hdr->FileHeader.Characteristics & attr) != attr) -+ return -EINVAL; -+ -+ /* Must be relocatable */ -+ attr = IMAGE_FILE_RELOCS_STRIPPED; -+ if ((nt_hdr->FileHeader.Characteristics & attr)) -+ return -EINVAL; -+ -+ /* Make sure we have at least one section */ -+ if (nt_hdr->FileHeader.NumberOfSections == 0) -+ return -EINVAL; -+ -+ if (opt_hdr->SectionAlignment < opt_hdr->FileAlignment) { -+ ERROR("alignment mismatch: section: 0x%x, file: 0x%x", -+ opt_hdr->SectionAlignment, opt_hdr->FileAlignment); -+ return -EINVAL; -+ } -+ -+ DBGLINKER("number of datadictionary entries %d", -+ opt_hdr->NumberOfRvaAndSizes); -+ for (i = 0; i < opt_hdr->NumberOfRvaAndSizes; i++) { -+ DBGLINKER("datadirectory %s RVA:%X Size:%d", -+ (i <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) ? -+ image_directory_name[i] : "unknown", -+ opt_hdr->DataDirectory[i].VirtualAddress, -+ opt_hdr->DataDirectory[i].Size); -+ } -+ -+ if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) -+ return IMAGE_FILE_EXECUTABLE_IMAGE; -+ if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_DLL)) -+ return IMAGE_FILE_DLL; -+ return -EINVAL; -+} -+ -+static int import(void *image, IMAGE_IMPORT_DESCRIPTOR *dirent, char *dll) -+{ -+ ULONG_PTR *lookup_tbl, *address_tbl; -+ char *symname = NULL; -+ int i; -+ int ret = 0; -+ generic_func adr; -+ -+ lookup_tbl = RVA2VA(image, dirent->u.OriginalFirstThunk, ULONG_PTR *); -+ address_tbl = RVA2VA(image, dirent->FirstThunk, ULONG_PTR *); -+ -+ for (i = 0; lookup_tbl[i]; i++) { -+ if (IMAGE_SNAP_BY_ORDINAL(lookup_tbl[i])) { -+ ERROR("ordinal import not supported: %llu", -+ (uint64_t)lookup_tbl[i]); -+ return -1; -+ } -+ else { -+ symname = RVA2VA(image, -+ ((lookup_tbl[i] & -+ ~IMAGE_ORDINAL_FLAG) + 2), char *); -+ } -+ -+ if (get_export(symname, &adr) < 0) { -+ ERROR("unknown symbol: %s:'%s'", dll, symname); -+ ret = -1; -+ } else { -+ DBGLINKER("found symbol: %s:%s: addr: %p, rva = %llu", -+ dll, symname, adr, (uint64_t)address_tbl[i]); -+ address_tbl[i] = (ULONG_PTR)adr; -+ } -+ } -+ return ret; -+} -+ -+static int read_exports(struct pe_image *pe) -+{ -+ IMAGE_EXPORT_DIRECTORY *export_dir_table; -+ uint32_t *export_addr_table; -+ int i; -+ uint32_t *name_table; -+ PIMAGE_OPTIONAL_HEADER opt_hdr; -+ IMAGE_DATA_DIRECTORY *export_data_dir; -+ -+ opt_hdr = &pe->nt_hdr->OptionalHeader; -+ export_data_dir = -+ &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; -+ -+ if (export_data_dir->Size == 0) { -+ DBGLINKER("no exports"); -+ return 0; -+ } -+ -+ export_dir_table = -+ RVA2VA(pe->image, export_data_dir->VirtualAddress, -+ IMAGE_EXPORT_DIRECTORY *); -+ -+ name_table = (unsigned int *)(pe->image + -+ export_dir_table->AddressOfNames); -+ export_addr_table = (uint32_t *) -+ (pe->image + export_dir_table->AddressOfFunctions); -+ -+ for (i = 0; i < export_dir_table->NumberOfNames; i++) { -+ -+ if (export_data_dir->VirtualAddress <= *export_addr_table || -+ *export_addr_table >= (export_data_dir->VirtualAddress + -+ export_data_dir->Size)) -+ DBGLINKER("forwarder rva"); -+ -+ DBGLINKER("export symbol: %s, at %p", -+ (char *)(pe->image + *name_table), -+ pe->image + *export_addr_table); -+ -+ pe_exports[num_pe_exports].dll = pe->name; -+ pe_exports[num_pe_exports].name = pe->image + *name_table; -+ pe_exports[num_pe_exports].addr = -+ pe->image + *export_addr_table; -+ -+ num_pe_exports++; -+ name_table++; -+ export_addr_table++; -+ } -+ return 0; -+} -+ -+static int fixup_imports(void *image, IMAGE_NT_HEADERS *nt_hdr) -+{ -+ int i; -+ char *name; -+ int ret = 0; -+ IMAGE_IMPORT_DESCRIPTOR *dirent; -+ IMAGE_DATA_DIRECTORY *import_data_dir; -+ PIMAGE_OPTIONAL_HEADER opt_hdr; -+ -+ opt_hdr = &nt_hdr->OptionalHeader; -+ import_data_dir = -+ &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; -+ dirent = RVA2VA(image, import_data_dir->VirtualAddress, -+ IMAGE_IMPORT_DESCRIPTOR *); -+ -+ for (i = 0; dirent[i].Name; i++) { -+ name = RVA2VA(image, dirent[i].Name, char*); -+ -+ DBGLINKER("imports from dll: %s", name); -+ ret += import(image, &dirent[i], name); -+ } -+ return ret; -+} -+ -+static int fixup_reloc(void *image, IMAGE_NT_HEADERS *nt_hdr) -+{ -+ ULONG_PTR base; -+ ULONG_PTR size; -+ IMAGE_BASE_RELOCATION *fixup_block; -+ IMAGE_DATA_DIRECTORY *base_reloc_data_dir; -+ PIMAGE_OPTIONAL_HEADER opt_hdr; -+ -+ opt_hdr = &nt_hdr->OptionalHeader; -+ base = opt_hdr->ImageBase; -+ base_reloc_data_dir = -+ &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; -+ if (base_reloc_data_dir->Size == 0) -+ return 0; -+ -+ fixup_block = RVA2VA(image, base_reloc_data_dir->VirtualAddress, -+ IMAGE_BASE_RELOCATION *); -+ DBGLINKER("fixup_block=%p, image=%p", fixup_block, image); -+ DBGLINKER("fixup_block info: %x %d", -+ fixup_block->VirtualAddress, fixup_block->SizeOfBlock); -+ -+ while (fixup_block->SizeOfBlock) { -+ int i; -+ WORD fixup, offset; -+ -+ size = (fixup_block->SizeOfBlock - -+ sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); -+ DBGLINKER("found %llu relocations in this block", -+ (uint64_t)size); -+ -+ for (i = 0; i < size; i++) { -+ fixup = fixup_block->TypeOffset[i]; -+ offset = fixup & 0xfff; -+ switch ((fixup >> 12) & 0x0f) { -+ case IMAGE_REL_BASED_ABSOLUTE: -+ break; -+ -+ case IMAGE_REL_BASED_HIGHLOW: { -+ uint32_t addr; -+ uint32_t *loc = -+ RVA2VA(image, -+ fixup_block->VirtualAddress + -+ offset, uint32_t *); -+ addr = RVA2VA(image, (*loc - base), uint32_t); -+ DBGLINKER("relocation: *%p (Val:%X)= %X", -+ loc, *loc, addr); -+ *loc = addr; -+ } -+ break; -+ -+ case IMAGE_REL_BASED_DIR64: { -+ uint64_t addr; -+ uint64_t *loc = -+ RVA2VA(image, -+ fixup_block->VirtualAddress + -+ offset, uint64_t *); -+ addr = RVA2VA(image, (*loc - base), uint64_t); -+ DBGLINKER("relocation: *%p (Val:%llX)= %llx", -+ loc, *loc, addr); -+ *loc = addr; -+ } -+ break; -+ -+ default: -+ ERROR("unknown fixup: %08X", -+ (fixup >> 12) & 0x0f); -+ return -EOPNOTSUPP; -+ break; -+ } -+ } -+ DBGLINKER("finished relocating block"); -+ -+ fixup_block = (IMAGE_BASE_RELOCATION *) -+ ((void *)fixup_block + fixup_block->SizeOfBlock); -+ }; -+ DBGLINKER("done relocating all"); -+ -+ return 0; -+} -+ -+/* Expand the image in memory if necessary. The image on disk does not -+ * necessarily maps the image of the driver in memory, so we have to -+ * re-write it in order to fulfill the sections alignments. The -+ * advantage to do that is that rva_to_va becomes a simple -+ * addition. */ -+static int fix_pe_image(struct pe_image *pe) -+{ -+ void *image; -+ IMAGE_SECTION_HEADER *sect_hdr; -+ int i, sections; -+ int image_size; -+ -+ if (pe->size == pe->opt_hdr->SizeOfImage) { -+ /* Nothing to do */ -+ return 0; -+ } -+ -+ image_size = pe->opt_hdr->SizeOfImage; -+#ifdef CONFIG_X86_64 -+#ifdef PAGE_KERNEL_EXECUTABLE -+ image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, -+ PAGE_KERNEL_EXECUTABLE); -+#elif defined PAGE_KERNEL_EXEC -+ image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, -+ PAGE_KERNEL_EXEC); -+#else -+#error x86_64 should have either PAGE_KERNEL_EXECUTABLE or PAGE_KERNEL_EXEC -+#endif -+#else -+#ifdef cpu_has_nx -+ /* hate to play with kernel macros, but PAGE_KERNEL_EXEC is -+ * not available to modules! */ -+ if (cpu_has_nx) -+ image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, -+ __pgprot(__PAGE_KERNEL & ~_PAGE_NX)); -+ else -+ image = vmalloc(image_size); -+#else -+ image = vmalloc(image_size); -+#endif -+#endif -+ if (image == NULL) { -+ ERROR("failed to allocate enough space for new image:" -+ " %d bytes", image_size); -+ return -ENOMEM; -+ } -+ memset(image, 0, image_size); -+ -+ /* Copy all the headers, ie everything before the first section. */ -+ -+ sections = pe->nt_hdr->FileHeader.NumberOfSections; -+ sect_hdr = IMAGE_FIRST_SECTION(pe->nt_hdr); -+ -+ DBGLINKER("copying headers: %u bytes", sect_hdr->PointerToRawData); -+ -+ memcpy(image, pe->image, sect_hdr->PointerToRawData); -+ -+ /* Copy all the sections */ -+ for (i = 0; i < sections; i++) { -+ DBGLINKER("Copy section %s from %x to %x", -+ sect_hdr->Name, sect_hdr->PointerToRawData, -+ sect_hdr->VirtualAddress); -+ if (sect_hdr->VirtualAddress+sect_hdr->SizeOfRawData > -+ image_size) { -+ ERROR("Invalid section %s in driver", sect_hdr->Name); -+ vfree(image); -+ return -EINVAL; -+ } -+ -+ memcpy(image+sect_hdr->VirtualAddress, -+ pe->image + sect_hdr->PointerToRawData, -+ sect_hdr->SizeOfRawData); -+ sect_hdr++; -+ } -+ -+ vfree(pe->image); -+ pe->image = image; -+ pe->size = image_size; -+ -+ /* Update our internal pointers */ -+ pe->nt_hdr = (IMAGE_NT_HEADERS *) -+ (pe->image + ((IMAGE_DOS_HEADER *)pe->image)->e_lfanew); -+ pe->opt_hdr = &pe->nt_hdr->OptionalHeader; -+ -+ DBGLINKER("set nt headers: nt_hdr=%p, opt_hdr=%p, image=%p", -+ pe->nt_hdr, pe->opt_hdr, pe->image); -+ -+ return 0; -+} -+ -+#if defined(CONFIG_X86_64) -+static void fix_user_shared_data_addr(char *driver, unsigned long length) -+{ -+ unsigned long i, n, max_addr, *addr; -+ -+ TRACE1("fixing KI_USER_SHARED_DATA address in the driver"); -+ n = length - sizeof(unsigned long); -+ max_addr = KI_USER_SHARED_DATA + sizeof(kuser_shared_data); -+ for (i = 0; i < n; i++) { -+ addr = (unsigned long *)(driver + i); -+ if (*addr >= KI_USER_SHARED_DATA && *addr < max_addr) { -+ *addr -= KI_USER_SHARED_DATA; -+ *addr += (unsigned long)&kuser_shared_data; -+ kuser_shared_data.reserved1 = 1; -+ } -+ } -+} -+#endif -+ -+int link_pe_images(struct pe_image *pe_image, unsigned short n) -+{ -+ int i; -+ struct pe_image *pe; -+ -+#if DEBUG >= 1 -+ /* Sanity checks */ -+ CHECK_SZ(IMAGE_SECTION_HEADER, IMAGE_SIZEOF_SECTION_HEADER); -+ CHECK_SZ(IMAGE_FILE_HEADER, IMAGE_SIZEOF_FILE_HEADER); -+ CHECK_SZ(IMAGE_OPTIONAL_HEADER, IMAGE_SIZEOF_NT_OPTIONAL_HEADER); -+ CHECK_SZ(IMAGE_NT_HEADERS, 4 + IMAGE_SIZEOF_FILE_HEADER + -+ IMAGE_SIZEOF_NT_OPTIONAL_HEADER); -+ CHECK_SZ(IMAGE_DOS_HEADER, 0x40); -+ CHECK_SZ(IMAGE_EXPORT_DIRECTORY, 40); -+ CHECK_SZ(IMAGE_BASE_RELOCATION, 8); -+ CHECK_SZ(IMAGE_IMPORT_DESCRIPTOR, 20); -+#endif -+ -+ for (i = 0; i < n; i++) { -+ IMAGE_DOS_HEADER *dos_hdr; -+ pe = &pe_image[i]; -+ dos_hdr = pe->image; -+ -+ if (pe->size < sizeof(IMAGE_DOS_HEADER)) { -+ TRACE1("image too small: %d", pe->size); -+ return -EINVAL; -+ } -+ -+ pe->nt_hdr = -+ (IMAGE_NT_HEADERS *)(pe->image + dos_hdr->e_lfanew); -+ pe->opt_hdr = &pe->nt_hdr->OptionalHeader; -+ -+ pe->type = check_nt_hdr(pe->nt_hdr); -+ if (pe->type <= 0) { -+ TRACE1("type <= 0"); -+ return -EINVAL; -+ } -+ -+ if (fix_pe_image(pe)) { -+ TRACE1("bad PE image"); -+ return -EINVAL; -+ } -+ -+ if (read_exports(pe)) { -+ TRACE1("read exports failed"); -+ return -EINVAL; -+ } -+ } -+ -+ for (i = 0; i < n; i++) { -+ pe = &pe_image[i]; -+ -+ if (fixup_reloc(pe->image, pe->nt_hdr)) { -+ TRACE1("fixup reloc failed"); -+ return -EINVAL; -+ } -+ if (fixup_imports(pe->image, pe->nt_hdr)) { -+ TRACE1("fixup imports failed"); -+ return -EINVAL; -+ } -+#if defined(CONFIG_X86_64) -+ fix_user_shared_data_addr(pe_image[i].image, pe_image[i].size); -+#endif -+ flush_icache_range((unsigned long)pe->image, pe->size); -+ -+ pe->entry = -+ RVA2VA(pe->image, -+ pe->opt_hdr->AddressOfEntryPoint, void *); -+ TRACE1("entry is at %p, rva at %08X", pe->entry, -+ pe->opt_hdr->AddressOfEntryPoint); -+ } -+ -+ for (i = 0; i < n; i++) { -+ pe = &pe_image[i]; -+ -+ if (pe->type == IMAGE_FILE_DLL) { -+ struct unicode_string ustring; -+ char *buf = "0/0t0m0p00"; -+ int (*dll_entry)(struct unicode_string *ustring) -+ wstdcall; -+ -+ memset(&ustring, 0, sizeof(ustring)); -+ ustring.buf = (wchar_t *)buf; -+ dll_entry = (void *)get_dll_init(pe->name); -+ -+ TRACE1("calling dll_init at %p", dll_entry); -+ if (!dll_entry || dll_entry(&ustring)) -+ ERROR("DLL initialize failed for %s", -+ pe->name); -+ } -+ else if (pe->type != IMAGE_FILE_EXECUTABLE_IMAGE) -+ ERROR("illegal image type: %d", pe->type); -+ } -+ return 0; -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pe_linker.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.h ---- linux-3.12.2/3rdparty/ndiswrapper/pe_linker.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pe_linker.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,992 @@ -+/* -+ * This file is an excerpt of winnt.h from WINE, which bears the -+ * following copyright: -+ * -+ * Win32 definitions for Windows NT -+ * -+ * Copyright 1996 Alexandre Julliard -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library 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 -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+/* -+ * File formats definitions -+ */ -+typedef struct _IMAGE_DOS_HEADER { -+ WORD e_magic; /* 00: MZ Header signature */ -+ WORD e_cblp; /* 02: Bytes on last page of file */ -+ WORD e_cp; /* 04: Pages in file */ -+ WORD e_crlc; /* 06: Relocations */ -+ WORD e_cparhdr; /* 08: Size of header in paragraphs */ -+ WORD e_minalloc; /* 0a: Minimum extra paragraphs needed */ -+ WORD e_maxalloc; /* 0c: Maximum extra paragraphs needed */ -+ WORD e_ss; /* 0e: Initial (relative) SS value */ -+ WORD e_sp; /* 10: Initial SP value */ -+ WORD e_csum; /* 12: Checksum */ -+ WORD e_ip; /* 14: Initial IP value */ -+ WORD e_cs; /* 16: Initial (relative) CS value */ -+ WORD e_lfarlc; /* 18: File address of relocation table */ -+ WORD e_ovno; /* 1a: Overlay number */ -+ WORD e_res[4]; /* 1c: Reserved words */ -+ WORD e_oemid; /* 24: OEM identifier (for e_oeminfo) */ -+ WORD e_oeminfo; /* 26: OEM information; e_oemid specific */ -+ WORD e_res2[10]; /* 28: Reserved words */ -+ DWORD e_lfanew; /* 3c: Offset to extended header */ -+} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; -+ -+#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ -+#define IMAGE_OS2_SIGNATURE 0x454E /* NE */ -+#define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */ -+#define IMAGE_OS2_SIGNATURE_LX 0x584C /* LX */ -+#define IMAGE_VXD_SIGNATURE 0x454C /* LE */ -+#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ -+ -+/* -+ * This is the Windows executable (NE) header. -+ * the name IMAGE_OS2_HEADER is misleading, but in the SDK this way. -+ */ -+typedef struct -+{ -+ WORD ne_magic; /* 00 NE signature 'NE' */ -+ BYTE ne_ver; /* 02 Linker version number */ -+ BYTE ne_rev; /* 03 Linker revision number */ -+ WORD ne_enttab; /* 04 Offset to entry table relative to NE */ -+ WORD ne_cbenttab; /* 06 Length of entry table in bytes */ -+ LONG ne_crc; /* 08 Checksum */ -+ WORD ne_flags; /* 0c Flags about segments in this file */ -+ WORD ne_autodata; /* 0e Automatic data segment number */ -+ WORD ne_heap; /* 10 Initial size of local heap */ -+ WORD ne_stack; /* 12 Initial size of stack */ -+ DWORD ne_csip; /* 14 Initial CS:IP */ -+ DWORD ne_sssp; /* 18 Initial SS:SP */ -+ WORD ne_cseg; /* 1c # of entries in segment table */ -+ WORD ne_cmod; /* 1e # of entries in module reference tab. */ -+ WORD ne_cbnrestab; /* 20 Length of nonresident-name table */ -+ WORD ne_segtab; /* 22 Offset to segment table */ -+ WORD ne_rsrctab; /* 24 Offset to resource table */ -+ WORD ne_restab; /* 26 Offset to resident-name table */ -+ WORD ne_modtab; /* 28 Offset to module reference table */ -+ WORD ne_imptab; /* 2a Offset to imported name table */ -+ DWORD ne_nrestab; /* 2c Offset to nonresident-name table */ -+ WORD ne_cmovent; /* 30 # of movable entry points */ -+ WORD ne_align; /* 32 Logical sector alignment shift count */ -+ WORD ne_cres; /* 34 # of resource segments */ -+ BYTE ne_exetyp; /* 36 Flags indicating target OS */ -+ BYTE ne_flagsothers; /* 37 Additional information flags */ -+ WORD ne_pretthunks; /* 38 Offset to return thunks */ -+ WORD ne_psegrefbytes; /* 3a Offset to segment ref. bytes */ -+ WORD ne_swaparea; /* 3c Reserved by Microsoft */ -+ WORD ne_expver; /* 3e Expected Windows version number */ -+} IMAGE_OS2_HEADER, *PIMAGE_OS2_HEADER; -+ -+typedef struct _IMAGE_VXD_HEADER { -+ WORD e32_magic; -+ BYTE e32_border; -+ BYTE e32_worder; -+ DWORD e32_level; -+ WORD e32_cpu; -+ WORD e32_os; -+ DWORD e32_ver; -+ DWORD e32_mflags; -+ DWORD e32_mpages; -+ DWORD e32_startobj; -+ DWORD e32_eip; -+ DWORD e32_stackobj; -+ DWORD e32_esp; -+ DWORD e32_pagesize; -+ DWORD e32_lastpagesize; -+ DWORD e32_fixupsize; -+ DWORD e32_fixupsum; -+ DWORD e32_ldrsize; -+ DWORD e32_ldrsum; -+ DWORD e32_objtab; -+ DWORD e32_objcnt; -+ DWORD e32_objmap; -+ DWORD e32_itermap; -+ DWORD e32_rsrctab; -+ DWORD e32_rsrccnt; -+ DWORD e32_restab; -+ DWORD e32_enttab; -+ DWORD e32_dirtab; -+ DWORD e32_dircnt; -+ DWORD e32_fpagetab; -+ DWORD e32_frectab; -+ DWORD e32_impmod; -+ DWORD e32_impmodcnt; -+ DWORD e32_impproc; -+ DWORD e32_pagesum; -+ DWORD e32_datapage; -+ DWORD e32_preload; -+ DWORD e32_nrestab; -+ DWORD e32_cbnrestab; -+ DWORD e32_nressum; -+ DWORD e32_autodata; -+ DWORD e32_debuginfo; -+ DWORD e32_debuglen; -+ DWORD e32_instpreload; -+ DWORD e32_instdemand; -+ DWORD e32_heapsize; -+ BYTE e32_res3[12]; -+ DWORD e32_winresoff; -+ DWORD e32_winreslen; -+ WORD e32_devid; -+ WORD e32_ddkver; -+} IMAGE_VXD_HEADER, *PIMAGE_VXD_HEADER; -+ -+/* These defines describe the meanings of the bits in the -+ Characteristics field */ -+ -+#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 /* No relocation info */ -+#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 -+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 -+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 -+#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 -+#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 -+#define IMAGE_FILE_16BIT_MACHINE 0x0040 -+#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 -+#define IMAGE_FILE_32BIT_MACHINE 0x0100 -+#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 -+#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 -+#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 -+#define IMAGE_FILE_SYSTEM 0x1000 -+#define IMAGE_FILE_DLL 0x2000 -+#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 -+#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 -+ -+/* These are the settings of the Machine field. */ -+#define IMAGE_FILE_MACHINE_UNKNOWN 0 -+#define IMAGE_FILE_MACHINE_I860 0x014d -+#define IMAGE_FILE_MACHINE_I386 0x014c -+#define IMAGE_FILE_MACHINE_R3000 0x0162 -+#define IMAGE_FILE_MACHINE_R4000 0x0166 -+#define IMAGE_FILE_MACHINE_R10000 0x0168 -+#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 -+#define IMAGE_FILE_MACHINE_ALPHA 0x0184 -+#define IMAGE_FILE_MACHINE_SH3 0x01a2 -+#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 -+#define IMAGE_FILE_MACHINE_SH3E 0x01a4 -+#define IMAGE_FILE_MACHINE_SH4 0x01a6 -+#define IMAGE_FILE_MACHINE_SH5 0x01a8 -+#define IMAGE_FILE_MACHINE_ARM 0x01c0 -+#define IMAGE_FILE_MACHINE_THUMB 0x01c2 -+#define IMAGE_FILE_MACHINE_AM33 0x01d3 -+#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 -+#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 -+#define IMAGE_FILE_MACHINE_IA64 0x0200 -+#define IMAGE_FILE_MACHINE_MIPS16 0x0266 -+#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 -+#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 -+#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 -+#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 -+#define IMAGE_FILE_MACHINE_TRICORE 0x0520 -+#define IMAGE_FILE_MACHINE_CEF 0x0cef -+#define IMAGE_FILE_MACHINE_EBC 0x0ebc -+#define IMAGE_FILE_MACHINE_AMD64 0x8664 -+#define IMAGE_FILE_MACHINE_M32R 0x9041 -+#define IMAGE_FILE_MACHINE_CEE 0xc0ee -+ -+#define IMAGE_SIZEOF_FILE_HEADER 20 -+#define IMAGE_SIZEOF_ROM_OPTIONAL_HEADER 56 -+#define IMAGE_SIZEOF_STD_OPTIONAL_HEADER 28 -+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER32 224 -+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER64 240 -+#define IMAGE_SIZEOF_SHORT_NAME 8 -+#define IMAGE_SIZEOF_SECTION_HEADER 40 -+#define IMAGE_SIZEOF_SYMBOL 18 -+#define IMAGE_SIZEOF_AUX_SYMBOL 18 -+#define IMAGE_SIZEOF_RELOCATION 10 -+#define IMAGE_SIZEOF_BASE_RELOCATION 8 -+#define IMAGE_SIZEOF_LINENUMBER 6 -+#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 -+ -+/* Possible Magic values */ -+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x010b -+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x020b -+#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x0107 -+ -+#ifdef CONFIG_X86_64 -+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL_HEADER64 -+#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC -+#else -+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL_HEADER32 -+#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC -+#endif -+ -+/* These are indexes into the DataDirectory array */ -+#define IMAGE_FILE_EXPORT_DIRECTORY 0 -+#define IMAGE_FILE_IMPORT_DIRECTORY 1 -+#define IMAGE_FILE_RESOURCE_DIRECTORY 2 -+#define IMAGE_FILE_EXCEPTION_DIRECTORY 3 -+#define IMAGE_FILE_SECURITY_DIRECTORY 4 -+#define IMAGE_FILE_BASE_RELOCATION_TABLE 5 -+#define IMAGE_FILE_DEBUG_DIRECTORY 6 -+#define IMAGE_FILE_DESCRIPTION_STRING 7 -+#define IMAGE_FILE_MACHINE_VALUE 8 /* Mips */ -+#define IMAGE_FILE_THREAD_LOCAL_STORAGE 9 -+#define IMAGE_FILE_CALLBACK_DIRECTORY 10 -+ -+/* Directory Entries, indices into the DataDirectory array */ -+ -+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 -+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 -+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 -+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 -+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 -+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 -+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 -+#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 -+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* (MIPS GP) */ -+#define IMAGE_DIRECTORY_ENTRY_TLS 9 -+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 -+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 -+#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ -+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 -+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 -+ -+/* Subsystem Values */ -+ -+#define IMAGE_SUBSYSTEM_UNKNOWN 0 -+#define IMAGE_SUBSYSTEM_NATIVE 1 -+#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 /* Windows GUI subsystem */ -+#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 /* Windows character subsystem */ -+#define IMAGE_SUBSYSTEM_OS2_CUI 5 -+#define IMAGE_SUBSYSTEM_POSIX_CUI 7 -+#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 /* native Win9x driver */ -+#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 /* Windows CE subsystem */ -+#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 -+#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 -+#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 -+#define IMAGE_SUBSYSTEM_EFI_ROM 13 -+#define IMAGE_SUBSYSTEM_XBOX 14 -+ -+typedef struct _IMAGE_FILE_HEADER { -+ WORD Machine; -+ WORD NumberOfSections; -+ DWORD TimeDateStamp; -+ DWORD PointerToSymbolTable; -+ DWORD NumberOfSymbols; -+ WORD SizeOfOptionalHeader; -+ WORD Characteristics; -+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; -+ -+typedef struct _IMAGE_DATA_DIRECTORY { -+ DWORD VirtualAddress; -+ DWORD Size; -+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; -+ -+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 -+ -+typedef struct _IMAGE_OPTIONAL_HEADER32 { -+ -+ /* Standard fields */ -+ -+ WORD Magic; -+ BYTE MajorLinkerVersion; -+ BYTE MinorLinkerVersion; -+ DWORD SizeOfCode; -+ DWORD SizeOfInitializedData; -+ DWORD SizeOfUninitializedData; -+ DWORD AddressOfEntryPoint; -+ DWORD BaseOfCode; -+ DWORD BaseOfData; -+ -+ /* NT additional fields */ -+ DWORD ImageBase; -+ DWORD SectionAlignment; -+ DWORD FileAlignment; -+ WORD MajorOperatingSystemVersion; -+ WORD MinorOperatingSystemVersion; -+ WORD MajorImageVersion; -+ WORD MinorImageVersion; -+ WORD MajorSubsystemVersion; -+ WORD MinorSubsystemVersion; -+ DWORD Win32VersionValue; -+ DWORD SizeOfImage; -+ DWORD SizeOfHeaders; -+ DWORD CheckSum; -+ WORD Subsystem; -+ WORD DllCharacteristics; -+ DWORD SizeOfStackReserve; -+ DWORD SizeOfStackCommit; -+ DWORD SizeOfHeapReserve; -+ DWORD SizeOfHeapCommit; -+ DWORD LoaderFlags; -+ DWORD NumberOfRvaAndSizes; -+ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -+} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; -+ -+typedef struct _IMAGE_OPTIONAL_HEADER64 { -+ -+ /* Standard fields */ -+ -+ WORD Magic; -+ BYTE MajorLinkerVersion; -+ BYTE MinorLinkerVersion; -+ DWORD SizeOfCode; -+ DWORD SizeOfInitializedData; -+ DWORD SizeOfUninitializedData; -+ DWORD AddressOfEntryPoint; -+ DWORD BaseOfCode; -+ -+ /* NT additional fields */ -+ ULONGLONG ImageBase; -+ DWORD SectionAlignment; -+ DWORD FileAlignment; -+ WORD MajorOperatingSystemVersion; -+ WORD MinorOperatingSystemVersion; -+ WORD MajorImageVersion; -+ WORD MinorImageVersion; -+ WORD MajorSubsystemVersion; -+ WORD MinorSubsystemVersion; -+ DWORD Win32VersionValue; -+ DWORD SizeOfImage; -+ DWORD SizeOfHeaders; -+ DWORD CheckSum; -+ WORD Subsystem; -+ WORD DllCharacteristics; -+ ULONGLONG SizeOfStackReserve; -+ ULONGLONG SizeOfStackCommit; -+ ULONGLONG SizeOfHeapReserve; -+ ULONGLONG SizeOfHeapCommit; -+ DWORD LoaderFlags; -+ DWORD NumberOfRvaAndSizes; -+ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -+} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; -+ -+#ifdef CONFIG_X86_64 -+typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER; -+typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER; -+#else -+typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER; -+typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER; -+#endif -+ -+typedef struct _IMAGE_NT_HEADERS32 { -+ DWORD Signature; /* "PE"\0\0 */ /* 0x00 */ -+ IMAGE_FILE_HEADER FileHeader; /* 0x04 */ -+ IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */ -+} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; -+ -+typedef struct _IMAGE_NT_HEADERS64 { -+ DWORD Signature; /* "PE"\0\0 */ /* 0x00 */ -+ IMAGE_FILE_HEADER FileHeader; /* 0x04 */ -+ IMAGE_OPTIONAL_HEADER64 OptionalHeader; /* 0x18 */ -+} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; -+ -+#ifdef CONFIG_X86_64 -+typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS; -+typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS; -+#else -+typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS; -+typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS; -+#endif -+ -+#define IMAGE_SIZEOF_SHORT_NAME 8 -+ -+typedef struct _IMAGE_SECTION_HEADER { -+ BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; -+ union { -+ DWORD PhysicalAddress; -+ DWORD VirtualSize; -+ } Misc; -+ DWORD VirtualAddress; -+ DWORD SizeOfRawData; -+ DWORD PointerToRawData; -+ DWORD PointerToRelocations; -+ DWORD PointerToLinenumbers; -+ WORD NumberOfRelocations; -+ WORD NumberOfLinenumbers; -+ DWORD Characteristics; -+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; -+ -+#define IMAGE_SIZEOF_SECTION_HEADER 40 -+ -+#define IMAGE_FIRST_SECTION(ntheader) \ -+((PIMAGE_SECTION_HEADER)((LPBYTE)&((PIMAGE_NT_HEADERS)(ntheader))->OptionalHeader + \ -+((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader)) -+ -+/* These defines are for the Characteristics bitfield. */ -+/* #define IMAGE_SCN_TYPE_REG 0x00000000 - Reserved */ -+/* #define IMAGE_SCN_TYPE_DSECT 0x00000001 - Reserved */ -+/* #define IMAGE_SCN_TYPE_NOLOAD 0x00000002 - Reserved */ -+/* #define IMAGE_SCN_TYPE_GROUP 0x00000004 - Reserved */ -+#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved */ -+/* #define IMAGE_SCN_TYPE_COPY 0x00000010 - Reserved */ -+ -+#define IMAGE_SCN_CNT_CODE 0x00000020 -+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 -+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 -+ -+#define IMAGE_SCN_LNK_OTHER 0x00000100 -+#define IMAGE_SCN_LNK_INFO 0x00000200 -+/* #define IMAGE_SCN_TYPE_OVER 0x00000400 - Reserved */ -+#define IMAGE_SCN_LNK_REMOVE 0x00000800 -+#define IMAGE_SCN_LNK_COMDAT 0x00001000 -+ -+/* 0x00002000 - Reserved */ -+/* #define IMAGE_SCN_MEM_PROTECTED 0x00004000 - Obsolete */ -+#define IMAGE_SCN_MEM_FARDATA 0x00008000 -+ -+/* #define IMAGE_SCN_MEM_SYSHEAP 0x00010000 - Obsolete */ -+#define IMAGE_SCN_MEM_PURGEABLE 0x00020000 -+#define IMAGE_SCN_MEM_16BIT 0x00020000 -+#define IMAGE_SCN_MEM_LOCKED 0x00040000 -+#define IMAGE_SCN_MEM_PRELOAD 0x00080000 -+ -+#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 -+#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 -+#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 -+#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 -+#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default */ -+#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 -+#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 -+#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 -+#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 -+#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 -+#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 -+#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 -+#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 -+#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 -+/* 0x00F00000 - Unused */ -+#define IMAGE_SCN_ALIGN_MASK 0x00F00000 -+ -+#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 -+ -+ -+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 -+#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 -+#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 -+#define IMAGE_SCN_MEM_SHARED 0x10000000 -+#define IMAGE_SCN_MEM_EXECUTE 0x20000000 -+#define IMAGE_SCN_MEM_READ 0x40000000 -+#define IMAGE_SCN_MEM_WRITE 0x80000000 -+ -+typedef struct _IMAGE_SYMBOL { -+ union { -+ BYTE ShortName[8]; -+ struct { -+ DWORD Short; -+ DWORD Long; -+ } Name; -+ DWORD LongName[2]; -+ } N; -+ DWORD Value; -+ SHORT SectionNumber; -+ WORD Type; -+ BYTE StorageClass; -+ BYTE NumberOfAuxSymbols; -+} IMAGE_SYMBOL; -+typedef IMAGE_SYMBOL *PIMAGE_SYMBOL; -+ -+#define IMAGE_SIZEOF_SYMBOL 18 -+ -+typedef struct _IMAGE_LINENUMBER { -+ union { -+ DWORD SymbolTableIndex; -+ DWORD VirtualAddress; -+ } Type; -+ WORD Linenumber; -+} IMAGE_LINENUMBER; -+typedef IMAGE_LINENUMBER *PIMAGE_LINENUMBER; -+ -+#define IMAGE_SIZEOF_LINENUMBER 6 -+ -+typedef union _IMAGE_AUX_SYMBOL { -+ struct { -+ DWORD TagIndex; -+ union { -+ struct { -+ WORD Linenumber; -+ WORD Size; -+ } LnSz; -+ DWORD TotalSize; -+ } Misc; -+ union { -+ struct { -+ DWORD PointerToLinenumber; -+ DWORD PointerToNextFunction; -+ } Function; -+ struct { -+ WORD Dimension[4]; -+ } Array; -+ } FcnAry; -+ WORD TvIndex; -+ } Sym; -+ struct { -+ BYTE Name[IMAGE_SIZEOF_SYMBOL]; -+ } File; -+ struct { -+ DWORD Length; -+ WORD NumberOfRelocations; -+ WORD NumberOfLinenumbers; -+ DWORD CheckSum; -+ SHORT Number; -+ BYTE Selection; -+ } Section; -+} IMAGE_AUX_SYMBOL; -+typedef IMAGE_AUX_SYMBOL *PIMAGE_AUX_SYMBOL; -+ -+#define IMAGE_SIZEOF_AUX_SYMBOL 18 -+ -+#define IMAGE_SYM_UNDEFINED (SHORT)0 -+#define IMAGE_SYM_ABSOLUTE (SHORT)-1 -+#define IMAGE_SYM_DEBUG (SHORT)-2 -+ -+#define IMAGE_SYM_TYPE_NULL 0x0000 -+#define IMAGE_SYM_TYPE_VOID 0x0001 -+#define IMAGE_SYM_TYPE_CHAR 0x0002 -+#define IMAGE_SYM_TYPE_SHORT 0x0003 -+#define IMAGE_SYM_TYPE_INT 0x0004 -+#define IMAGE_SYM_TYPE_LONG 0x0005 -+#define IMAGE_SYM_TYPE_FLOAT 0x0006 -+#define IMAGE_SYM_TYPE_DOUBLE 0x0007 -+#define IMAGE_SYM_TYPE_STRUCT 0x0008 -+#define IMAGE_SYM_TYPE_UNION 0x0009 -+#define IMAGE_SYM_TYPE_ENUM 0x000A -+#define IMAGE_SYM_TYPE_MOE 0x000B -+#define IMAGE_SYM_TYPE_BYTE 0x000C -+#define IMAGE_SYM_TYPE_WORD 0x000D -+#define IMAGE_SYM_TYPE_UINT 0x000E -+#define IMAGE_SYM_TYPE_DWORD 0x000F -+#define IMAGE_SYM_TYPE_PCODE 0x8000 -+ -+#define IMAGE_SYM_DTYPE_NULL 0 -+#define IMAGE_SYM_DTYPE_POINTER 1 -+#define IMAGE_SYM_DTYPE_FUNCTION 2 -+#define IMAGE_SYM_DTYPE_ARRAY 3 -+ -+#define IMAGE_SYM_CLASS_END_OF_FUNCTION (BYTE )-1 -+#define IMAGE_SYM_CLASS_NULL 0x0000 -+#define IMAGE_SYM_CLASS_AUTOMATIC 0x0001 -+#define IMAGE_SYM_CLASS_EXTERNAL 0x0002 -+#define IMAGE_SYM_CLASS_STATIC 0x0003 -+#define IMAGE_SYM_CLASS_REGISTER 0x0004 -+#define IMAGE_SYM_CLASS_EXTERNAL_DEF 0x0005 -+#define IMAGE_SYM_CLASS_LABEL 0x0006 -+#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 0x0007 -+#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 0x0008 -+#define IMAGE_SYM_CLASS_ARGUMENT 0x0009 -+#define IMAGE_SYM_CLASS_STRUCT_TAG 0x000A -+#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 0x000B -+#define IMAGE_SYM_CLASS_UNION_TAG 0x000C -+#define IMAGE_SYM_CLASS_TYPE_DEFINITION 0x000D -+#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 0x000E -+#define IMAGE_SYM_CLASS_ENUM_TAG 0x000F -+#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 0x0010 -+#define IMAGE_SYM_CLASS_REGISTER_PARAM 0x0011 -+#define IMAGE_SYM_CLASS_BIT_FIELD 0x0012 -+ -+#define IMAGE_SYM_CLASS_FAR_EXTERNAL 0x0044 -+#define IMAGE_SYM_CLASS_BLOCK 0x0064 -+#define IMAGE_SYM_CLASS_FUNCTION 0x0065 -+#define IMAGE_SYM_CLASS_END_OF_STRUCT 0x0066 -+#define IMAGE_SYM_CLASS_FILE 0x0067 -+#define IMAGE_SYM_CLASS_SECTION 0x0068 -+#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069 -+ -+#define N_BTMASK 0x000F -+#define N_TMASK 0x0030 -+#define N_TMASK1 0x00C0 -+#define N_TMASK2 0x00F0 -+#define N_BTSHFT 4 -+#define N_TSHIFT 2 -+ -+#define BTYPE(x) ((x) & N_BTMASK) -+ -+#ifndef ISPTR -+#define ISPTR(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_POINTER << N_BTSHFT)) -+#endif -+ -+#ifndef ISFCN -+#define ISFCN(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_FUNCTION << N_BTSHFT)) -+#endif -+ -+#ifndef ISARY -+#define ISARY(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_ARRAY << N_BTSHFT)) -+#endif -+ -+#ifndef ISTAG -+#define ISTAG(x) ((x)==IMAGE_SYM_CLASS_STRUCT_TAG || (x)==IMAGE_SYM_CLASS_UNION_TAG || (x)==IMAGE_SYM_CLASS_ENUM_TAG) -+#endif -+ -+#ifndef INCREF -+#define INCREF(x) ((((x)&~N_BTMASK)<>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK)) -+#endif -+ -+#define IMAGE_COMDAT_SELECT_NODUPLICATES 1 -+#define IMAGE_COMDAT_SELECT_ANY 2 -+#define IMAGE_COMDAT_SELECT_SAME_SIZE 3 -+#define IMAGE_COMDAT_SELECT_EXACT_MATCH 4 -+#define IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 -+#define IMAGE_COMDAT_SELECT_LARGEST 6 -+#define IMAGE_COMDAT_SELECT_NEWEST 7 -+ -+#define IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1 -+#define IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2 -+#define IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3 -+ -+/* Export module directory */ -+ -+typedef struct _IMAGE_EXPORT_DIRECTORY { -+ DWORD Characteristics; -+ DWORD TimeDateStamp; -+ WORD MajorVersion; -+ WORD MinorVersion; -+ DWORD Name; -+ DWORD Base; -+ DWORD NumberOfFunctions; -+ DWORD NumberOfNames; -+ DWORD AddressOfFunctions; -+ DWORD AddressOfNames; -+ DWORD AddressOfNameOrdinals; -+} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY; -+ -+/* Import name entry */ -+typedef struct _IMAGE_IMPORT_BY_NAME { -+ WORD Hint; -+ BYTE Name[1]; -+} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME; -+ -+/* Import thunk */ -+typedef struct _IMAGE_THUNK_DATA32 { -+ union { -+ DWORD ForwarderString; -+ DWORD Function; -+ DWORD Ordinal; -+ DWORD AddressOfData; -+ } u1; -+} IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32; -+ -+typedef struct _IMAGE_THUNK_DATA64 { -+ union { -+ ULONGLONG ForwarderString; -+ ULONGLONG Function; -+ ULONGLONG Ordinal; -+ ULONGLONG AddressOfData; -+ } u1; -+} IMAGE_THUNK_DATA64,*PIMAGE_THUNK_DATA64; -+ -+#ifdef CONFIG_X86_64 -+typedef IMAGE_THUNK_DATA32 IMAGE_THUNK_DATA; -+typedef PIMAGE_THUNK_DATA32 PIMAGE_THUNK_DATA; -+#else -+typedef IMAGE_THUNK_DATA64 IMAGE_THUNK_DATA; -+typedef PIMAGE_THUNK_DATA64 PIMAGE_THUNK_DATA; -+#endif -+ -+/* Import module directory */ -+ -+typedef struct __packed _IMAGE_IMPORT_DESCRIPTOR { -+ union { -+ DWORD Characteristics; /* 0 for terminating null -+ * import descriptor */ -+ DWORD OriginalFirstThunk; /* RVA to original unbound -+ * IAT */ -+ } u; -+ DWORD TimeDateStamp; /* 0 if not bound, -+ * -1 if bound, and real date\time stamp -+ * in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT -+ * (new BIND) -+ * otherwise date/time stamp of DLL bound to -+ * (Old BIND) -+ */ -+ DWORD ForwarderChain; /* -1 if no forwarders */ -+ DWORD Name; -+ /* RVA to IAT (if bound this IAT has actual addresses) */ -+ DWORD FirstThunk; -+} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR; -+ -+#define IMAGE_ORDINAL_FLAG32 0x80000000 -+#define IMAGE_ORDINAL_FLAG64 0x8000000000000000UL -+#define IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0) -+#define IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0) -+#define IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) -+ -+#ifdef CONFIG_X86_64 -+#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG64 -+#define IMAGE_SNAP_BY_ORDINAL IMAGE_SNAP_BY_ORDINAL64 -+#else -+#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG32 -+#define IMAGE_SNAP_BY_ORDINAL IMAGE_SNAP_BY_ORDINAL32 -+#endif -+ -+typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR -+{ -+ DWORD TimeDateStamp; -+ WORD OffsetModuleName; -+ WORD NumberOfModuleForwarderRefs; -+/* Array of zero or more IMAGE_BOUND_FORWARDER_REF follows */ -+} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR; -+ -+typedef struct _IMAGE_BOUND_FORWARDER_REF -+{ -+ DWORD TimeDateStamp; -+ WORD OffsetModuleName; -+ WORD Reserved; -+} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF; -+ -+typedef struct _IMAGE_BASE_RELOCATION -+{ -+ DWORD VirtualAddress; -+ DWORD SizeOfBlock; -+ WORD TypeOffset[0]; -+} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; -+ -+typedef struct _IMAGE_RELOCATION -+{ -+ union { -+ DWORD VirtualAddress; -+ DWORD RelocCount; -+ } DUMMYUNIONNAME; -+ DWORD SymbolTableIndex; -+ WORD Type; -+} IMAGE_RELOCATION, *PIMAGE_RELOCATION; -+ -+#define IMAGE_SIZEOF_RELOCATION 10 -+ -+/* generic relocation types */ -+#define IMAGE_REL_BASED_ABSOLUTE 0 -+#define IMAGE_REL_BASED_HIGH 1 -+#define IMAGE_REL_BASED_LOW 2 -+#define IMAGE_REL_BASED_HIGHLOW 3 -+#define IMAGE_REL_BASED_HIGHADJ 4 -+#define IMAGE_REL_BASED_MIPS_JMPADDR 5 -+#define IMAGE_REL_BASED_SECTION 6 -+#define IMAGE_REL_BASED_REL 7 -+#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 -+#define IMAGE_REL_BASED_IA64_IMM64 9 /* yes, 9 too */ -+#define IMAGE_REL_BASED_DIR64 10 -+#define IMAGE_REL_BASED_HIGH3ADJ 11 -+ -+/* I386 relocation types */ -+#define IMAGE_REL_I386_ABSOLUTE 0 -+#define IMAGE_REL_I386_DIR16 1 -+#define IMAGE_REL_I386_REL16 2 -+#define IMAGE_REL_I386_DIR32 6 -+#define IMAGE_REL_I386_DIR32NB 7 -+#define IMAGE_REL_I386_SEG12 9 -+#define IMAGE_REL_I386_SECTION 10 -+#define IMAGE_REL_I386_SECREL 11 -+#define IMAGE_REL_I386_REL32 20 -+ -+/* MIPS relocation types */ -+#define IMAGE_REL_MIPS_ABSOLUTE 0x0000 -+#define IMAGE_REL_MIPS_REFHALF 0x0001 -+#define IMAGE_REL_MIPS_REFWORD 0x0002 -+#define IMAGE_REL_MIPS_JMPADDR 0x0003 -+#define IMAGE_REL_MIPS_REFHI 0x0004 -+#define IMAGE_REL_MIPS_REFLO 0x0005 -+#define IMAGE_REL_MIPS_GPREL 0x0006 -+#define IMAGE_REL_MIPS_LITERAL 0x0007 -+#define IMAGE_REL_MIPS_SECTION 0x000A -+#define IMAGE_REL_MIPS_SECREL 0x000B -+#define IMAGE_REL_MIPS_SECRELLO 0x000C -+#define IMAGE_REL_MIPS_SECRELHI 0x000D -+#define IMAGE_REL_MIPS_JMPADDR16 0x0010 -+#define IMAGE_REL_MIPS_REFWORDNB 0x0022 -+#define IMAGE_REL_MIPS_PAIR 0x0025 -+ -+/* ALPHA relocation types */ -+#define IMAGE_REL_ALPHA_ABSOLUTE 0x0000 -+#define IMAGE_REL_ALPHA_REFLONG 0x0001 -+#define IMAGE_REL_ALPHA_REFQUAD 0x0002 -+#define IMAGE_REL_ALPHA_GPREL 0x0003 -+#define IMAGE_REL_ALPHA_LITERAL 0x0004 -+#define IMAGE_REL_ALPHA_LITUSE 0x0005 -+#define IMAGE_REL_ALPHA_GPDISP 0x0006 -+#define IMAGE_REL_ALPHA_BRADDR 0x0007 -+#define IMAGE_REL_ALPHA_HINT 0x0008 -+#define IMAGE_REL_ALPHA_INLINE_REFLONG 0x0009 -+#define IMAGE_REL_ALPHA_REFHI 0x000A -+#define IMAGE_REL_ALPHA_REFLO 0x000B -+#define IMAGE_REL_ALPHA_PAIR 0x000C -+#define IMAGE_REL_ALPHA_MATCH 0x000D -+#define IMAGE_REL_ALPHA_SECTION 0x000E -+#define IMAGE_REL_ALPHA_SECREL 0x000F -+#define IMAGE_REL_ALPHA_REFLONGNB 0x0010 -+#define IMAGE_REL_ALPHA_SECRELLO 0x0011 -+#define IMAGE_REL_ALPHA_SECRELHI 0x0012 -+#define IMAGE_REL_ALPHA_REFQ3 0x0013 -+#define IMAGE_REL_ALPHA_REFQ2 0x0014 -+#define IMAGE_REL_ALPHA_REFQ1 0x0015 -+#define IMAGE_REL_ALPHA_GPRELLO 0x0016 -+#define IMAGE_REL_ALPHA_GPRELHI 0x0017 -+ -+/* PowerPC relocation types */ -+#define IMAGE_REL_PPC_ABSOLUTE 0x0000 -+#define IMAGE_REL_PPC_ADDR64 0x0001 -+#define IMAGE_REL_PPC_ADDR 0x0002 -+#define IMAGE_REL_PPC_ADDR24 0x0003 -+#define IMAGE_REL_PPC_ADDR16 0x0004 -+#define IMAGE_REL_PPC_ADDR14 0x0005 -+#define IMAGE_REL_PPC_REL24 0x0006 -+#define IMAGE_REL_PPC_REL14 0x0007 -+#define IMAGE_REL_PPC_TOCREL16 0x0008 -+#define IMAGE_REL_PPC_TOCREL14 0x0009 -+#define IMAGE_REL_PPC_ADDR32NB 0x000A -+#define IMAGE_REL_PPC_SECREL 0x000B -+#define IMAGE_REL_PPC_SECTION 0x000C -+#define IMAGE_REL_PPC_IFGLUE 0x000D -+#define IMAGE_REL_PPC_IMGLUE 0x000E -+#define IMAGE_REL_PPC_SECREL16 0x000F -+#define IMAGE_REL_PPC_REFHI 0x0010 -+#define IMAGE_REL_PPC_REFLO 0x0011 -+#define IMAGE_REL_PPC_PAIR 0x0012 -+#define IMAGE_REL_PPC_SECRELLO 0x0013 -+#define IMAGE_REL_PPC_SECRELHI 0x0014 -+#define IMAGE_REL_PPC_GPREL 0x0015 -+#define IMAGE_REL_PPC_TYPEMASK 0x00FF -+/* modifier bits */ -+#define IMAGE_REL_PPC_NEG 0x0100 -+#define IMAGE_REL_PPC_BRTAKEN 0x0200 -+#define IMAGE_REL_PPC_BRNTAKEN 0x0400 -+#define IMAGE_REL_PPC_TOCDEFN 0x0800 -+ -+/* SH3 ? relocation type */ -+#define IMAGE_REL_SH3_ABSOLUTE 0x0000 -+#define IMAGE_REL_SH3_DIRECT16 0x0001 -+#define IMAGE_REL_SH3_DIRECT 0x0002 -+#define IMAGE_REL_SH3_DIRECT8 0x0003 -+#define IMAGE_REL_SH3_DIRECT8_WORD 0x0004 -+#define IMAGE_REL_SH3_DIRECT8_LONG 0x0005 -+#define IMAGE_REL_SH3_DIRECT4 0x0006 -+#define IMAGE_REL_SH3_DIRECT4_WORD 0x0007 -+#define IMAGE_REL_SH3_DIRECT4_LONG 0x0008 -+#define IMAGE_REL_SH3_PCREL8_WORD 0x0009 -+#define IMAGE_REL_SH3_PCREL8_LONG 0x000A -+#define IMAGE_REL_SH3_PCREL12_WORD 0x000B -+#define IMAGE_REL_SH3_STARTOF_SECTION 0x000C -+#define IMAGE_REL_SH3_SIZEOF_SECTION 0x000D -+#define IMAGE_REL_SH3_SECTION 0x000E -+#define IMAGE_REL_SH3_SECREL 0x000F -+#define IMAGE_REL_SH3_DIRECT32_NB 0x0010 -+ -+/* ARM (Archimedes?) relocation types */ -+#define IMAGE_REL_ARM_ABSOLUTE 0x0000 -+#define IMAGE_REL_ARM_ADDR 0x0001 -+#define IMAGE_REL_ARM_ADDR32NB 0x0002 -+#define IMAGE_REL_ARM_BRANCH24 0x0003 -+#define IMAGE_REL_ARM_BRANCH11 0x0004 -+#define IMAGE_REL_ARM_SECTION 0x000E -+#define IMAGE_REL_ARM_SECREL 0x000F -+ -+/* IA64 relocation types */ -+#define IMAGE_REL_IA64_ABSOLUTE 0x0000 -+#define IMAGE_REL_IA64_IMM14 0x0001 -+#define IMAGE_REL_IA64_IMM22 0x0002 -+#define IMAGE_REL_IA64_IMM64 0x0003 -+#define IMAGE_REL_IA64_DIR 0x0004 -+#define IMAGE_REL_IA64_DIR64 0x0005 -+#define IMAGE_REL_IA64_PCREL21B 0x0006 -+#define IMAGE_REL_IA64_PCREL21M 0x0007 -+#define IMAGE_REL_IA64_PCREL21F 0x0008 -+#define IMAGE_REL_IA64_GPREL22 0x0009 -+#define IMAGE_REL_IA64_LTOFF22 0x000A -+#define IMAGE_REL_IA64_SECTION 0x000B -+#define IMAGE_REL_IA64_SECREL22 0x000C -+#define IMAGE_REL_IA64_SECREL64I 0x000D -+#define IMAGE_REL_IA64_SECREL 0x000E -+#define IMAGE_REL_IA64_LTOFF64 0x000F -+#define IMAGE_REL_IA64_DIR32NB 0x0010 -+#define IMAGE_REL_IA64_RESERVED_11 0x0011 -+#define IMAGE_REL_IA64_RESERVED_12 0x0012 -+#define IMAGE_REL_IA64_RESERVED_13 0x0013 -+#define IMAGE_REL_IA64_RESERVED_14 0x0014 -+#define IMAGE_REL_IA64_RESERVED_15 0x0015 -+#define IMAGE_REL_IA64_RESERVED_16 0x0016 -+#define IMAGE_REL_IA64_ADDEND 0x001F -+ -+/* archive format */ -+ -+#define IMAGE_ARCHIVE_START_SIZE 8 -+#define IMAGE_ARCHIVE_START "!\n" -+#define IMAGE_ARCHIVE_END "`\n" -+#define IMAGE_ARCHIVE_PAD "\n" -+#define IMAGE_ARCHIVE_LINKER_MEMBER "/ " -+#define IMAGE_ARCHIVE_LONGNAMES_MEMBER "// " -+ -+typedef struct _IMAGE_ARCHIVE_MEMBER_HEADER -+{ -+ BYTE Name[16]; -+ BYTE Date[12]; -+ BYTE UserID[6]; -+ BYTE GroupID[6]; -+ BYTE Mode[8]; -+ BYTE Size[10]; -+ BYTE EndHeader[2]; -+} IMAGE_ARCHIVE_MEMBER_HEADER, *PIMAGE_ARCHIVE_MEMBER_HEADER; -+ -+#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 -+ -+/* -+ * Resource directory stuff -+ */ -+typedef struct _IMAGE_RESOURCE_DIRECTORY { -+ DWORD Characteristics; -+ DWORD TimeDateStamp; -+ WORD MajorVersion; -+ WORD MinorVersion; -+ WORD NumberOfNamedEntries; -+ WORD NumberOfIdEntries; -+ /* IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; */ -+} IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY; -+ -+#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000 -+#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000 -+ -+typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { -+ union { -+ struct { -+#ifdef BITFIELDS_BIGENDIAN -+ unsigned NameIsString:1; -+ unsigned NameOffset:31; -+#else -+ unsigned NameOffset:31; -+ unsigned NameIsString:1; -+#endif -+ } DUMMYSTRUCTNAME1; -+ DWORD Name; -+ struct { -+#ifdef WORDS_BIGENDIAN -+ WORD __pad; -+ WORD Id; -+#else -+ WORD Id; -+ WORD __pad; -+#endif -+ } DUMMYSTRUCTNAME2; -+ } DUMMYUNIONNAME1; -+ union { -+ DWORD OffsetToData; -+ struct { -+#ifdef BITFIELDS_BIGENDIAN -+ unsigned DataIsDirectory:1; -+ unsigned OffsetToDirectory:31; -+#else -+ unsigned OffsetToDirectory:31; -+ unsigned DataIsDirectory:1; -+#endif -+ } DUMMYSTRUCTNAME3; -+ } DUMMYUNIONNAME2; -+} IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY; -+ -+ -+typedef struct _IMAGE_RESOURCE_DIRECTORY_STRING { -+ WORD Length; -+ CHAR NameString[ 1 ]; -+} IMAGE_RESOURCE_DIRECTORY_STRING,*PIMAGE_RESOURCE_DIRECTORY_STRING; -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.c ---- linux-3.12.2/3rdparty/ndiswrapper/pnp.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,697 @@ -+/* -+ * Copyright (C) 2005 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "usb.h" -+#include "pnp.h" -+#include "wrapndis.h" -+#include "loader.h" -+ -+/* Functions callable from the NDIS driver */ -+wstdcall NTSTATUS pdoDispatchDeviceControl(struct device_object *pdo, -+ struct irp *irp); -+wstdcall NTSTATUS pdoDispatchPnp(struct device_object *pdo, struct irp *irp); -+wstdcall NTSTATUS pdoDispatchPower(struct device_object *pdo, struct irp *irp); -+ -+static NTSTATUS start_pdo(struct device_object *pdo) -+{ -+ int i, ret, count, resources_size; -+ struct wrap_device *wd; -+ struct pci_dev *pdev; -+ struct cm_partial_resource_descriptor *entry; -+ struct cm_partial_resource_list *partial_resource_list; -+ -+ ENTER1("%p, %p", pdo, pdo->reserved); -+ wd = pdo->reserved; -+ if (ntoskernel_init_device(wd)) -+ EXIT1(return STATUS_FAILURE); -+ if (wrap_is_usb_bus(wd->dev_bus)) { -+ if (usb_init_device(wd)) { -+ ntoskernel_exit_device(wd); -+ EXIT1(return STATUS_FAILURE); -+ } -+ EXIT1(return STATUS_SUCCESS); -+ } -+ if (!wrap_is_pci_bus(wd->dev_bus)) -+ EXIT1(return STATUS_SUCCESS); -+ pdev = wd->pci.pdev; -+ ret = pci_enable_device(pdev); -+ if (ret) { -+ ERROR("couldn't enable PCI device: %x", ret); -+ return STATUS_FAILURE; -+ } -+ ret = pci_request_regions(pdev, DRIVER_NAME); -+ if (ret) { -+ ERROR("couldn't request PCI regions: %x", ret); -+ goto err_enable; -+ } -+ pci_set_power_state(pdev, PCI_D0); -+#ifdef CONFIG_X86_64 -+ /* 64-bit broadcom driver doesn't work if DMA is allocated -+ * from over 1GB */ -+ if (wd->vendor == 0x14e4) { -+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(30)) || -+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(30))) -+ WARNING("couldn't set DMA mask; this driver " -+ "may not work with more than 1GB RAM"); -+ } -+#endif -+ /* IRQ resource entry is filled in from pdev, instead of -+ * pci_resource macros */ -+ for (i = count = 0; pci_resource_start(pdev, i); i++) -+ if ((pci_resource_flags(pdev, i) & IORESOURCE_MEM) || -+ (pci_resource_flags(pdev, i) & IORESOURCE_IO)) -+ count++; -+ /* space for entry for IRQ is already in -+ * cm_partial_resource_list */ -+ resources_size = sizeof(struct cm_resource_list) + -+ sizeof(struct cm_partial_resource_descriptor) * count; -+ TRACE2("resources: %d, %d", count, resources_size); -+ wd->resource_list = kzalloc(resources_size, GFP_KERNEL); -+ if (!wd->resource_list) { -+ WARNING("couldn't allocate memory"); -+ goto err_regions; -+ } -+ wd->resource_list->count = 1; -+ wd->resource_list->list[0].interface_type = PCIBus; -+ /* bus_number is not used by WDM drivers */ -+ wd->resource_list->list[0].bus_number = pdev->bus->number; -+ -+ partial_resource_list = -+ &wd->resource_list->list->partial_resource_list; -+ partial_resource_list->version = 1; -+ partial_resource_list->revision = 1; -+ partial_resource_list->count = count + 1; -+ -+ for (i = count = 0; pci_resource_start(pdev, i); i++) { -+ entry = &partial_resource_list->partial_descriptors[count]; -+ TRACE2("%d", count); -+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { -+ entry->type = CmResourceTypeMemory; -+ entry->flags = CM_RESOURCE_MEMORY_READ_WRITE; -+ entry->share = CmResourceShareDeviceExclusive; -+ } else if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { -+ entry->type = CmResourceTypePort; -+ entry->flags = CM_RESOURCE_PORT_IO; -+ entry->share = CmResourceShareDeviceExclusive; -+#if 0 -+ } else if (pci_resource_flags(pdev, i) & IORESOURCE_DMA) { -+ /* it looks like no driver uses this resource */ -+ typeof(pci_resource_flags(pdev, 0)) flags; -+ entry->type = CmResourceTypeDma; -+ flags = pci_resource_flags(pdev, i); -+ if (flags & IORESOURCE_DMA_TYPEA) -+ entry->flags |= CM_RESOURCE_DMA_TYPE_A; -+ else if (flags & IORESOURCE_DMA_TYPEB) -+ entry->flags |= CM_RESOURCE_DMA_TYPE_B; -+ else if (flags & IORESOURCE_DMA_TYPEF) -+ entry->flags |= CM_RESOURCE_DMA_TYPE_F; -+ if (flags & IORESOURCE_DMA_8BIT) -+ entry->flags |= CM_RESOURCE_DMA_8; -+ else if (flags & IORESOURCE_DMA_16BIT) -+ entry->flags |= CM_RESOURCE_DMA_16; -+ /* what about 32bit DMA? */ -+ else if (flags & IORESOURCE_DMA_8AND16BIT) -+ entry->flags |= CM_RESOURCE_DMA_8_AND_16; -+ if (flags & IORESOURCE_DMA_MASTER) -+ entry->flags |= CM_RESOURCE_DMA_BUS_MASTER; -+ entry->u.dma.channel = pci_resource_start(pdev, i); -+ /* what should this be? */ -+ entry->u.dma.port = 1; -+#endif -+ } else -+ continue; -+ /* TODO: Add other resource types? */ -+ entry->u.generic.start = -+ (ULONG_PTR)pci_resource_start(pdev, i); -+ entry->u.generic.length = pci_resource_len(pdev, i); -+ count++; -+ } -+ -+ /* put IRQ resource at the end */ -+ entry = &partial_resource_list->partial_descriptors[count++]; -+ entry->type = CmResourceTypeInterrupt; -+ entry->flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; -+ /* we assume all devices use shared IRQ */ -+ entry->share = CmResourceShareShared; -+ /* as per documentation, interrupt level should be DIRQL, but -+ * examples from DDK as well some drivers, such as AR5211, -+ * RT8180L use interrupt level as interrupt vector also in -+ * NdisMRegisterInterrupt */ -+ entry->u.interrupt.level = pdev->irq; -+ entry->u.interrupt.vector = pdev->irq; -+ entry->u.interrupt.affinity = -1; -+ -+ TRACE2("resource list count %d, irq: %d", -+ partial_resource_list->count, pdev->irq); -+ pci_set_drvdata(pdev, wd); -+ EXIT1(return STATUS_SUCCESS); -+err_regions: -+ pci_release_regions(pdev); -+err_enable: -+ pci_disable_device(pdev); -+ wd->pci.pdev = NULL; -+ wd->pdo = NULL; -+ EXIT1(return STATUS_FAILURE); -+} -+ -+static void remove_pdo(struct device_object *pdo) -+{ -+ struct wrap_device *wd = pdo->reserved; -+ -+ ntoskernel_exit_device(wd); -+ if (wrap_is_pci_bus(wd->dev_bus)) { -+ struct pci_dev *pdev = wd->pci.pdev; -+ pci_release_regions(pdev); -+ pci_disable_device(pdev); -+ wd->pci.pdev = NULL; -+ pci_set_drvdata(pdev, NULL); -+ } else if (wrap_is_usb_bus(wd->dev_bus)) { -+ usb_exit_device(wd); -+ } -+ kfree(wd->resource_list); -+ wd->resource_list = NULL; -+ return; -+} -+ -+static NTSTATUS IoSendIrpTopDev(struct device_object *dev_obj, ULONG major_fn, -+ ULONG minor_fn, struct io_stack_location *sl) -+{ -+ NTSTATUS status; -+ struct nt_event event; -+ struct irp *irp; -+ struct io_stack_location *irp_sl; -+ struct device_object *top_dev = IoGetAttachedDeviceReference(dev_obj); -+ -+ KeInitializeEvent(&event, NotificationEvent, FALSE); -+ irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, top_dev, NULL, 0, NULL, -+ &event, NULL); -+ irp->io_status.status = STATUS_NOT_IMPLEMENTED; -+ irp->io_status.info = 0; -+ irp_sl = IoGetNextIrpStackLocation(irp); -+ if (sl) -+ memcpy(irp_sl, sl, sizeof(*irp_sl)); -+ irp_sl->major_fn = major_fn; -+ irp_sl->minor_fn = minor_fn; -+ status = IoCallDriver(top_dev, irp); -+ if (status == STATUS_PENDING) { -+ KeWaitForSingleObject(&event, Executive, KernelMode, -+ FALSE, NULL); -+ status = irp->io_status.status; -+ } -+ ObDereferenceObject(top_dev); -+ return status; -+} -+ -+wstdcall NTSTATUS pdoDispatchDeviceControl(struct device_object *pdo, -+ struct irp *irp) -+{ -+ NTSTATUS status; -+ struct wrap_device *wd = pdo->reserved; -+ -+ DUMP_IRP(irp); -+ (void)wd; -+ if (wrap_is_usb_bus(wd->dev_bus)) { -+ status = wrap_submit_irp(pdo, irp); -+ IOTRACE("status: %08X", status); -+ if (status != STATUS_PENDING) -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ } else { -+ status = irp->io_status.status = STATUS_NOT_IMPLEMENTED; -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ } -+ IOEXIT(return status); -+} -+WIN_FUNC_DECL(pdoDispatchDeviceControl,2) -+ -+wstdcall NTSTATUS pdoDispatchPnp(struct device_object *pdo, struct irp *irp) -+{ -+ struct io_stack_location *irp_sl; -+ NTSTATUS status; -+ struct wrap_device *wd = pdo->reserved; -+ -+ irp_sl = IoGetCurrentIrpStackLocation(irp); -+ TRACE2("%p %d:%d", pdo, irp_sl->major_fn, irp_sl->minor_fn); -+ switch (irp_sl->minor_fn) { -+ case IRP_MN_START_DEVICE: -+ status = start_pdo(pdo); -+ break; -+ case IRP_MN_QUERY_STOP_DEVICE: -+ case IRP_MN_STOP_DEVICE: -+ case IRP_MN_QUERY_REMOVE_DEVICE: -+ status = STATUS_SUCCESS; -+ break; -+ case IRP_MN_REMOVE_DEVICE: -+ remove_pdo(pdo); -+ status = STATUS_SUCCESS; -+ break; -+ case IRP_MN_QUERY_INTERFACE: -+ if (wrap_is_usb_bus(wd->dev_bus)) -+ status = usb_query_interface(wd, irp_sl); -+ else -+ status = STATUS_NOT_IMPLEMENTED; -+ break; -+ default: -+ TRACE2("fn %d not implemented", irp_sl->minor_fn); -+ status = STATUS_SUCCESS; -+ break; -+ } -+ irp->io_status.status = status; -+ TRACE2("status: %08X", status); -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ IOEXIT(return status); -+} -+WIN_FUNC_DECL(pdoDispatchPnp,2) -+ -+wstdcall NTSTATUS pdoDispatchPower(struct device_object *pdo, struct irp *irp) -+{ -+ struct io_stack_location *irp_sl; -+ struct wrap_device *wd; -+ union power_state power_state; -+ struct pci_dev *pdev; -+ NTSTATUS status; -+ -+ irp_sl = IoGetCurrentIrpStackLocation(irp); -+ wd = pdo->reserved; -+ TRACE2("pdo: %p, fn: %d:%d, wd: %p", -+ pdo, irp_sl->major_fn, irp_sl->minor_fn, wd); -+ switch (irp_sl->minor_fn) { -+ case IRP_MN_WAIT_WAKE: -+ /* TODO: this is not complete/correct */ -+ TRACE2("state: %d, completion: %p", -+ irp_sl->params.power.state.system_state, -+ irp_sl->completion_routine); -+ IoMarkIrpPending(irp); -+ status = STATUS_PENDING; -+ break; -+ case IRP_MN_SET_POWER: -+ power_state = irp_sl->params.power.state; -+ if (power_state.device_state == PowerDeviceD0) { -+ TRACE2("resuming %p", wd); -+ if (wrap_is_pci_bus(wd->dev_bus)) { -+ pdev = wd->pci.pdev; -+ pci_restore_state(pdev); -+ if (wd->pci.wake_state == PowerDeviceD3) { -+ pci_enable_wake(wd->pci.pdev, -+ PCI_D3hot, 0); -+ pci_enable_wake(wd->pci.pdev, -+ PCI_D3cold, 0); -+ } -+ pci_set_power_state(pdev, PCI_D0); -+ } else if (wrap_is_usb_bus(wd->dev_bus)) { -+ wrap_resume_urbs(wd); -+ } -+ } else { -+ TRACE2("suspending device %p", wd); -+ if (wrap_is_pci_bus(wd->dev_bus)) { -+ pdev = wd->pci.pdev; -+ pci_save_state(pdev); -+ TRACE2("%d", wd->pci.wake_state); -+ if (wd->pci.wake_state == PowerDeviceD3) { -+ pci_enable_wake(wd->pci.pdev, -+ PCI_D3hot, 1); -+ pci_enable_wake(wd->pci.pdev, -+ PCI_D3cold, 1); -+ } -+ pci_set_power_state(pdev, PCI_D3hot); -+ } else if (wrap_is_usb_bus(wd->dev_bus)) { -+ wrap_suspend_urbs(wd); -+ } -+ } -+ status = STATUS_SUCCESS; -+ break; -+ case IRP_MN_QUERY_POWER: -+ status = STATUS_SUCCESS; -+ break; -+ default: -+ TRACE2("fn %d not implemented", irp_sl->minor_fn); -+ status = STATUS_SUCCESS; -+ break; -+ } -+ irp->io_status.status = status; -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ return status; -+} -+WIN_FUNC_DECL(pdoDispatchPower,2) -+ -+static NTSTATUS pnp_set_device_power_state(struct wrap_device *wd, -+ enum device_power_state state) -+{ -+ NTSTATUS status; -+ struct device_object *pdo; -+ struct io_stack_location irp_sl; -+ -+ pdo = wd->pdo; -+ IOTRACE("%p, %p", pdo, IoGetAttachedDevice(pdo)); -+ memset(&irp_sl, 0, sizeof(irp_sl)); -+ irp_sl.params.power.state.device_state = state; -+ irp_sl.params.power.type = DevicePowerState; -+ if (state > PowerDeviceD0) { -+ status = IoSendIrpTopDev(pdo, IRP_MJ_POWER, IRP_MN_QUERY_POWER, -+ &irp_sl); -+ if (status != STATUS_SUCCESS) { -+ TRACE1("query of power to %d returns %08X", -+ state, status); -+ EXIT1(return status); -+ } -+ } -+ status = IoSendIrpTopDev(pdo, IRP_MJ_POWER, IRP_MN_SET_POWER, &irp_sl); -+ if (status != STATUS_SUCCESS) -+ WARNING("setting power to %d failed: %08X", state, status); -+ EXIT1(return status); -+} -+ -+static NTSTATUS pnp_start_device(struct wrap_device *wd) -+{ -+ struct device_object *fdo; -+ struct device_object *pdo; -+ struct io_stack_location irp_sl; -+ NTSTATUS status; -+ -+ pdo = wd->pdo; -+ /* TODO: for now we use same resources for both translated -+ * resources and raw resources */ -+ memset(&irp_sl, 0, sizeof(irp_sl)); -+ irp_sl.params.start_device.allocated_resources = -+ wd->resource_list; -+ irp_sl.params.start_device.allocated_resources_translated = -+ wd->resource_list; -+ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_START_DEVICE, &irp_sl); -+ fdo = IoGetAttachedDevice(pdo); -+ fdo->drv_obj->drv_ext->count++; -+ if (status != STATUS_SUCCESS) -+ WARNING("Windows driver couldn't initialize the device (%08X)", -+ status); -+ EXIT1(return status); -+} -+ -+#if 0 -+static NTSTATUS pnp_stop_device(struct wrap_device *wd) -+{ -+ struct device_object *pdo; -+ NTSTATUS status; -+ -+ pdo = wd->pdo; -+ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_QUERY_STOP_DEVICE, -+ NULL); -+ if (status != STATUS_SUCCESS) -+ WARNING("status: %08X", status); -+ /* for now we ignore query status */ -+ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_STOP_DEVICE, NULL); -+ if (status != STATUS_SUCCESS) -+ WARNING("status: %08X", status); -+ if (status != STATUS_SUCCESS) -+ WARNING("status: %08X", status); -+ EXIT2(return status); -+} -+#endif -+ -+static NTSTATUS pnp_remove_device(struct wrap_device *wd) -+{ -+ struct device_object *pdo, *fdo; -+ struct driver_object *fdo_drv_obj; -+ NTSTATUS status; -+ -+ pdo = wd->pdo; -+ fdo = IoGetAttachedDevice(pdo); -+ fdo_drv_obj = fdo->drv_obj; -+ TRACE2("%p, %p, %p", pdo, fdo, fdo_drv_obj); -+ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_QUERY_REMOVE_DEVICE, -+ NULL); -+ if (status != STATUS_SUCCESS) -+ WARNING("status: %08X", status); -+ -+ status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_REMOVE_DEVICE, NULL); -+ if (status != STATUS_SUCCESS) -+ WARNING("status: %08X", status); -+ /* TODO: should we use count in drv_ext or driver's Object -+ * header reference count to keep count of devices associated -+ * with a driver? */ -+ if (status == STATUS_SUCCESS) -+ fdo_drv_obj->drv_ext->count--; -+ TRACE1("count: %d", fdo_drv_obj->drv_ext->count); -+ if ((LONG)fdo_drv_obj->drv_ext->count < 0) -+ WARNING("wrong count: %d", fdo_drv_obj->drv_ext->count); -+ if (fdo_drv_obj->drv_ext->count == 0) { -+ struct wrap_driver *wrap_driver; -+ TRACE1("unloading driver: %p", fdo_drv_obj); -+ wrap_driver = -+ IoGetDriverObjectExtension(fdo_drv_obj, -+ (void *)WRAP_DRIVER_CLIENT_ID); -+ if (fdo_drv_obj->unload) -+ LIN2WIN1(fdo_drv_obj->unload, fdo_drv_obj); -+ if (wrap_driver) { -+ mutex_lock(&loader_mutex); -+ unload_wrap_driver(wrap_driver); -+ mutex_unlock(&loader_mutex); -+ } else -+ ERROR("couldn't get wrap_driver"); -+ ObDereferenceObject(fdo_drv_obj); -+ } -+ IoDeleteDevice(pdo); -+ unload_wrap_device(wd); -+ EXIT1(return status); -+} -+ -+WIN_FUNC_DECL(IoInvalidDeviceRequest,2) -+ -+static struct device_object *alloc_pdo(struct driver_object *drv_obj) -+{ -+ struct device_object *pdo; -+ NTSTATUS status; -+ int i; -+ struct ansi_string ansi_name; -+ struct unicode_string unicode_name; -+ -+ RtlInitAnsiString(&ansi_name, "NDISpdo"); -+ if (RtlAnsiStringToUnicodeString(&unicode_name, &ansi_name, TRUE) == -+ STATUS_SUCCESS) { -+ status = IoCreateDevice(drv_obj, 0, &unicode_name, -+ FILE_DEVICE_UNKNOWN, -+ FILE_AUTOGENERATED_DEVICE_NAME, -+ FALSE, &pdo); -+ RtlFreeUnicodeString(&unicode_name); -+ } else { -+ status = IoCreateDevice(drv_obj, 0, NULL, -+ FILE_DEVICE_UNKNOWN, -+ FILE_AUTOGENERATED_DEVICE_NAME, -+ FALSE, &pdo); -+ } -+ TRACE1("%p, %d, %p", drv_obj, status, pdo); -+ if (status != STATUS_SUCCESS) -+ return NULL; -+ /* dispatch routines are called as Windows functions */ -+ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) -+ drv_obj->major_func[i] = WIN_FUNC_PTR(IoInvalidDeviceRequest,2); -+ drv_obj->major_func[IRP_MJ_INTERNAL_DEVICE_CONTROL] = -+ WIN_FUNC_PTR(pdoDispatchDeviceControl,2); -+ drv_obj->major_func[IRP_MJ_DEVICE_CONTROL] = -+ WIN_FUNC_PTR(pdoDispatchDeviceControl,2); -+ drv_obj->major_func[IRP_MJ_POWER] = WIN_FUNC_PTR(pdoDispatchPower,2); -+ drv_obj->major_func[IRP_MJ_PNP] = WIN_FUNC_PTR(pdoDispatchPnp,2); -+ return pdo; -+} -+ -+static int wrap_pnp_start_device(struct wrap_device *wd) -+{ -+ struct wrap_driver *driver; -+ struct device_object *pdo; -+ struct driver_object *pdo_drv_obj; -+ -+ ENTER1("wd: %p", wd); -+ -+ if (!((wrap_is_pci_bus(wd->dev_bus)) || -+ (wrap_is_usb_bus(wd->dev_bus)))) { -+ ERROR("bus type %d (%d) not supported", -+ WRAP_BUS(wd->dev_bus), wd->dev_bus); -+ EXIT1(return -EINVAL); -+ } -+ driver = load_wrap_driver(wd); -+ if (!driver) -+ return -ENODEV; -+ -+ wd->driver = driver; -+ wd->dev_bus = WRAP_DEVICE_BUS(driver->dev_type, WRAP_BUS(wd->dev_bus)); -+ TRACE1("dev type: %d, bus type: %d, %d", WRAP_DEVICE(wd->dev_bus), -+ WRAP_BUS(wd->dev_bus), wd->dev_bus); -+ TRACE1("%d, %d", driver->dev_type, wrap_is_usb_bus(wd->dev_bus)); -+ /* first create pdo */ -+ if (wrap_is_pci_bus(wd->dev_bus)) -+ pdo_drv_obj = find_bus_driver("PCI"); -+ else // if (wrap_is_usb_bus(wd->dev_bus)) -+ pdo_drv_obj = find_bus_driver("USB"); -+ if (!pdo_drv_obj) -+ return -EINVAL; -+ pdo = alloc_pdo(pdo_drv_obj); -+ if (!pdo) -+ return -ENOMEM; -+ wd->pdo = pdo; -+ pdo->reserved = wd; -+ if (WRAP_DEVICE(wd->dev_bus) == WRAP_NDIS_DEVICE) { -+ if (init_ndis_driver(driver->drv_obj)) { -+ IoDeleteDevice(pdo); -+ return -EINVAL; -+ } -+ } -+ TRACE1("%p", driver->drv_obj->drv_ext->add_device); -+ if (driver->drv_obj->drv_ext->add_device(driver->drv_obj, pdo) != -+ STATUS_SUCCESS) { -+ IoDeleteDevice(pdo); -+ return -ENOMEM; -+ } -+ if (pnp_start_device(wd) != STATUS_SUCCESS) { -+ /* TODO: we need proper cleanup, to deallocate memory, -+ * for example */ -+ pnp_remove_device(wd); -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+int wrap_pnp_start_pci_device(struct pci_dev *pdev, -+ const struct pci_device_id *ent) -+{ -+ struct load_device load_device; -+ struct wrap_device *wd; -+ -+ ENTER1("called for %04x:%04x:%04x:%04x", pdev->vendor, pdev->device, -+ pdev->subsystem_vendor, pdev->subsystem_device); -+ -+ load_device.bus = WRAP_PCI_BUS; -+ load_device.vendor = pdev->vendor; -+ load_device.device = pdev->device; -+ load_device.subvendor = pdev->subsystem_vendor; -+ load_device.subdevice = pdev->subsystem_device; -+ wd = load_wrap_device(&load_device); -+ if (!wd) -+ EXIT1(return -ENODEV); -+ wd->pci.pdev = pdev; -+ return wrap_pnp_start_device(wd); -+} -+ -+void wrap_pnp_remove_pci_device(struct pci_dev *pdev) -+{ -+ struct wrap_device *wd; -+ -+ wd = (struct wrap_device *)pci_get_drvdata(pdev); -+ ENTER1("%p, %p", pdev, wd); -+ if (!wd) -+ EXIT1(return); -+ pnp_remove_device(wd); -+} -+ -+int wrap_pnp_suspend_pci_device(struct pci_dev *pdev, pm_message_t state) -+{ -+ struct wrap_device *wd; -+ -+ wd = (struct wrap_device *)pci_get_drvdata(pdev); -+ return pnp_set_device_power_state(wd, PowerDeviceD3); -+} -+ -+int wrap_pnp_resume_pci_device(struct pci_dev *pdev) -+{ -+ struct wrap_device *wd; -+ -+ wd = (struct wrap_device *)pci_get_drvdata(pdev); -+ return pnp_set_device_power_state(wd, PowerDeviceD0); -+} -+ -+#ifdef ENABLE_USB -+int wrap_pnp_start_usb_device(struct usb_interface *intf, -+ const struct usb_device_id *usb_id) -+{ -+ struct wrap_device *wd; -+ int ret; -+ struct usb_device *udev = interface_to_usbdev(intf); -+ ENTER1("%04x, %04x, %04x", udev->descriptor.idVendor, -+ udev->descriptor.idProduct, udev->descriptor.bDeviceClass); -+ -+ /* USB device (e.g., RNDIS) may have multiple interfaces; -+ initialize one interface only (is there a way to know which -+ of these interfaces is for network?) */ -+ -+ if ((wd = get_wrap_device(udev, WRAP_USB_BUS))) { -+ TRACE1("device already initialized: %p", wd); -+ usb_set_intfdata(intf, NULL); -+ ret = 0; -+ } else { -+ struct load_device load_device; -+ -+ load_device.bus = WRAP_USB_BUS; -+ load_device.vendor = le16_to_cpu(udev->descriptor.idVendor); -+ load_device.device = le16_to_cpu(udev->descriptor.idProduct); -+ load_device.subvendor = 0; -+ load_device.subdevice = 0; -+ wd = load_wrap_device(&load_device); -+ TRACE2("%p", wd); -+ if (wd) { -+ /* some devices (e.g., TI 4150, RNDIS) need -+ * full reset */ -+ ret = usb_reset_device(udev); -+ if (ret) -+ WARNING("reset failed: %d", ret); -+ usb_set_intfdata(intf, wd); -+ wd->usb.intf = intf; -+ wd->usb.udev = udev; -+ ret = wrap_pnp_start_device(wd); -+ } else -+ ret = -ENODEV; -+ } -+ -+ TRACE2("ret: %d", ret); -+ if (ret) -+ EXIT1(return ret); -+ else -+ return 0; -+} -+ -+void wrap_pnp_remove_usb_device(struct usb_interface *intf) -+{ -+ struct wrap_device *wd; -+ -+ wd = (struct wrap_device *)usb_get_intfdata(intf); -+ TRACE1("%p, %p", intf, wd); -+ if (wd == NULL) -+ EXIT1(return); -+ usb_set_intfdata(intf, NULL); -+ wd->usb.intf = NULL; -+ pnp_remove_device(wd); -+} -+ -+int wrap_pnp_suspend_usb_device(struct usb_interface *intf, pm_message_t state) -+{ -+ struct wrap_device *wd; -+ -+ wd = usb_get_intfdata(intf); -+ ENTER1("%p, %p", intf, wd); -+ if (!wd) -+ EXIT1(return 0); -+ if (pnp_set_device_power_state(wd, PowerDeviceD3)) -+ return -1; -+ return 0; -+} -+ -+int wrap_pnp_resume_usb_device(struct usb_interface *intf) -+{ -+ struct wrap_device *wd; -+ wd = usb_get_intfdata(intf); -+ ENTER1("%p, %p", intf, wd); -+ if (!wd) -+ EXIT1(return 0); -+ if (pnp_set_device_power_state(wd, PowerDeviceD0)) -+ return -1; -+ return 0; -+} -+ -+#endif // USB -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/pnp.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.h ---- linux-3.12.2/3rdparty/ndiswrapper/pnp.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/pnp.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,36 @@ -+/* -+ * Copyright (C) 2005 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _PNP_H_ -+#define _PNP_H_ -+ -+#include "ntoskernel.h" -+#include "ndis.h" -+#include "wrapndis.h" -+ -+int wrap_pnp_start_pci_device(struct pci_dev *pdev, -+ const struct pci_device_id *ent); -+void wrap_pnp_remove_pci_device(struct pci_dev *pdev); -+int wrap_pnp_suspend_pci_device(struct pci_dev *pdev, pm_message_t state); -+int wrap_pnp_resume_pci_device(struct pci_dev *pdev); -+ -+int wrap_pnp_start_usb_device(struct usb_interface *intf, -+ const struct usb_device_id *usb_id); -+void wrap_pnp_remove_usb_device(struct usb_interface *intf); -+int wrap_pnp_suspend_usb_device(struct usb_interface *intf, -+ pm_message_t state); -+int wrap_pnp_resume_usb_device(struct usb_interface *intf); -+ -+#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/proc.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/proc.c ---- linux-3.12.2/3rdparty/ndiswrapper/proc.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/proc.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,588 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+#include -+#include -+#include -+#include -+ -+#include "ndis.h" -+#include "iw_ndis.h" -+#include "wrapndis.h" -+#include "pnp.h" -+#include "wrapper.h" -+ -+#define MAX_PROC_STR_LEN 32 -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) -+static kuid_t proc_kuid; -+static kgid_t proc_kgid; -+#else -+#define proc_kuid proc_uid -+#define proc_kgid proc_gid -+#define kuid_t uid_t -+#define kgid_t gid_t -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -+static inline struct inode *file_inode(struct file *f) -+{ -+ return f->f_dentry->d_inode; -+} -+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) -+static inline struct inode *file_inode(struct file *f) -+{ -+ return f->f_path.dentry->d_inode; -+} -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) -+static inline void proc_set_user(struct proc_dir_entry *de, kuid_t uid, -+ kgid_t gid) -+{ -+ de->uid = uid; -+ de->gid = gid; -+} -+ -+static inline void proc_remove(struct proc_dir_entry *de) -+{ -+ if (de) -+ remove_proc_entry(de->name, de->parent); -+} -+ -+static inline void *PDE_DATA(const struct inode *inode) -+{ -+ return PDE(inode)->data; -+} -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) -+static inline struct proc_dir_entry *proc_create_data(const char *name, -+ umode_t mode, struct proc_dir_entry *parent, -+ struct file_operations *fops, void *data) -+{ -+ struct proc_dir_entry *de; -+ -+ de = create_proc_entry(name, mode, parent); -+ if (de) { -+ de->data = data; -+ de->proc_fops = fops; -+ } -+ -+ return de; -+} -+#endif -+ -+static int do_proc_make_entry(const char *name, umode_t mode, -+ struct proc_dir_entry *parent, -+ struct file_operations *fops, kuid_t uid, -+ kgid_t gid, struct ndis_device *wnd) -+{ -+ struct proc_dir_entry *de; -+ -+ de = proc_create_data(name, mode, parent, fops, wnd); -+ if (de == NULL) { -+ ERROR("couldn't create proc entry for '%s'", name); -+ return -ENOMEM; -+ } -+ proc_set_user(de, uid, gid); -+ return 0; -+} -+ -+#define PROC_DECLARE_RO(name) \ -+ static int proc_##name##_open(struct inode *inode, struct file *file) \ -+ { \ -+ return single_open(file, proc_##name##_read, PDE_DATA(inode)); \ -+ } \ -+ static struct file_operations name##_fops = { \ -+ .owner = THIS_MODULE, \ -+ .open = proc_##name##_open, \ -+ .read = seq_read, \ -+ .llseek = seq_lseek, \ -+ .release = single_release, \ -+ }; -+ -+#define PROC_DECLARE_RW(name) \ -+ static int proc_##name##_open(struct inode *inode, struct file *file) \ -+ { \ -+ return single_open(file, proc_##name##_read, PDE_DATA(inode)); \ -+ } \ -+ static struct file_operations name##_fops = { \ -+ .owner = THIS_MODULE, \ -+ .open = proc_##name##_open, \ -+ .read = seq_read, \ -+ .llseek = seq_lseek, \ -+ .release = single_release, \ -+ .write = proc_##name##_write, \ -+ }; -+ -+#define proc_make_entry_ro(name, parent, wnd) \ -+ do_proc_make_entry(#name, S_IFREG | S_IRUSR | S_IRGRP, parent, \ -+ &name##_fops, proc_kuid, proc_kgid, wnd) -+#define proc_make_entry_rw(name, parent, wnd) \ -+ do_proc_make_entry(#name, \ -+ S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, \ -+ parent, &name##_fops, proc_kuid, proc_kgid, wnd) -+ -+#define add_text(fmt, ...) seq_printf(sf, fmt, ##__VA_ARGS__) -+ -+static struct proc_dir_entry *wrap_procfs_entry; -+ -+static int proc_stats_read(struct seq_file *sf, void *v) -+{ -+ struct ndis_device *wnd = (struct ndis_device *)sf->private; -+ struct ndis_wireless_stats stats; -+ NDIS_STATUS res; -+ ndis_rssi rssi; -+ -+ res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi)); -+ if (!res) -+ add_text("signal_level=%d dBm\n", (s32)rssi); -+ -+ res = mp_query(wnd, OID_802_11_STATISTICS, &stats, sizeof(stats)); -+ if (!res) { -+ add_text("tx_frames=%llu\n", stats.tx_frag); -+ add_text("tx_multicast_frames=%llu\n", stats.tx_multi_frag); -+ add_text("tx_failed=%llu\n", stats.failed); -+ add_text("tx_retry=%llu\n", stats.retry); -+ add_text("tx_multi_retry=%llu\n", stats.multi_retry); -+ add_text("tx_rtss_success=%llu\n", stats.rtss_succ); -+ add_text("tx_rtss_fail=%llu\n", stats.rtss_fail); -+ add_text("ack_fail=%llu\n", stats.ack_fail); -+ add_text("frame_duplicates=%llu\n", stats.frame_dup); -+ add_text("rx_frames=%llu\n", stats.rx_frag); -+ add_text("rx_multicast_frames=%llu\n", stats.rx_multi_frag); -+ add_text("fcs_errors=%llu\n", stats.fcs_err); -+ } -+ -+ return 0; -+} -+ -+PROC_DECLARE_RO(stats) -+ -+static int proc_encr_read(struct seq_file *sf, void *v) -+{ -+ struct ndis_device *wnd = (struct ndis_device *)sf->private; -+ int i, encr_status, auth_mode, infra_mode; -+ NDIS_STATUS res; -+ struct ndis_essid essid; -+ mac_address ap_address; -+ -+ res = mp_query(wnd, OID_802_11_BSSID, -+ &ap_address, sizeof(ap_address)); -+ if (res) -+ memset(ap_address, 0, ETH_ALEN); -+ add_text("ap_address=" MACSTRSEP "\n", MAC2STR(ap_address)); -+ -+ res = mp_query(wnd, OID_802_11_SSID, &essid, sizeof(essid)); -+ if (!res) -+ add_text("essid=%.*s\n", essid.length, essid.essid); -+ -+ res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &encr_status); -+ if (!res) { -+ typeof(&wnd->encr_info.keys[0]) tx_key; -+ add_text("tx_key=%u\n", wnd->encr_info.tx_key_index); -+ add_text("key="); -+ tx_key = &wnd->encr_info.keys[wnd->encr_info.tx_key_index]; -+ if (tx_key->length > 0) -+ for (i = 0; i < tx_key->length; i++) -+ add_text("%2.2X", tx_key->key[i]); -+ else -+ add_text("off"); -+ add_text("\n"); -+ add_text("encr_mode=%d\n", encr_status); -+ } -+ res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &auth_mode); -+ if (!res) -+ add_text("auth_mode=%d\n", auth_mode); -+ res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &infra_mode); -+ add_text("mode=%s\n", (infra_mode == Ndis802_11IBSS) ? "adhoc" : -+ (infra_mode == Ndis802_11Infrastructure) ? "managed" : "auto"); -+ -+ return 0; -+} -+ -+PROC_DECLARE_RO(encr) -+ -+static int proc_hw_read(struct seq_file *sf, void *v) -+{ -+ struct ndis_device *wnd = (struct ndis_device *)sf->private; -+ struct ndis_configuration config; -+ enum ndis_power power_mode; -+ NDIS_STATUS res; -+ ndis_tx_power_level tx_power; -+ ULONG bit_rate; -+ ndis_rts_threshold rts_threshold; -+ ndis_fragmentation_threshold frag_threshold; -+ ndis_antenna antenna; -+ ULONG packet_filter; -+ int n; -+ mac_address mac; -+ char *hw_status[] = {"ready", "initializing", "resetting", "closing", -+ "not ready"}; -+ -+ res = mp_query_int(wnd, OID_GEN_HARDWARE_STATUS, &n); -+ if (res == NDIS_STATUS_SUCCESS && n >= 0 && n < ARRAY_SIZE(hw_status)) -+ add_text("status=%s\n", hw_status[n]); -+ -+ res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac)); -+ if (!res) -+ add_text("mac: " MACSTRSEP "\n", MAC2STR(mac)); -+ res = mp_query(wnd, OID_802_11_CONFIGURATION, &config, sizeof(config)); -+ if (!res) { -+ add_text("beacon_period=%u msec\n", config.beacon_period); -+ add_text("atim_window=%u msec\n", config.atim_window); -+ add_text("frequency=%u kHz\n", config.ds_config); -+ add_text("hop_pattern=%u\n", config.fh_config.hop_pattern); -+ add_text("hop_set=%u\n", config.fh_config.hop_set); -+ add_text("dwell_time=%u msec\n", config.fh_config.dwell_time); -+ } -+ -+ res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, -+ &tx_power, sizeof(tx_power)); -+ if (!res) -+ add_text("tx_power=%u mW\n", tx_power); -+ -+ res = mp_query(wnd, OID_GEN_LINK_SPEED, &bit_rate, sizeof(bit_rate)); -+ if (!res) -+ add_text("bit_rate=%u kBps\n", (u32)bit_rate / 10); -+ -+ res = mp_query(wnd, OID_802_11_RTS_THRESHOLD, -+ &rts_threshold, sizeof(rts_threshold)); -+ if (!res) -+ add_text("rts_threshold=%u bytes\n", rts_threshold); -+ -+ res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, -+ &frag_threshold, sizeof(frag_threshold)); -+ if (!res) -+ add_text("frag_threshold=%u bytes\n", frag_threshold); -+ -+ res = mp_query_int(wnd, OID_802_11_POWER_MODE, &power_mode); -+ if (!res) -+ add_text("power_mode=%s\n", -+ (power_mode == NDIS_POWER_OFF) ? "always_on" : -+ (power_mode == NDIS_POWER_MAX) ? "max_savings" : -+ "min_savings"); -+ -+ res = mp_query(wnd, OID_802_11_NUMBER_OF_ANTENNAS, -+ &antenna, sizeof(antenna)); -+ if (!res) -+ add_text("num_antennas=%u\n", antenna); -+ -+ res = mp_query(wnd, OID_802_11_TX_ANTENNA_SELECTED, -+ &antenna, sizeof(antenna)); -+ if (!res) -+ add_text("tx_antenna=%u\n", antenna); -+ -+ res = mp_query(wnd, OID_802_11_RX_ANTENNA_SELECTED, -+ &antenna, sizeof(antenna)); -+ if (!res) -+ add_text("rx_antenna=%u\n", antenna); -+ -+ add_text("encryption_modes=%s%s%s%s%s%s%s\n", -+ test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr) ? -+ "WEP" : "none", -+ test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr) ? -+ "; TKIP with WPA" : "", -+ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? -+ ", WPA2" : "", -+ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? -+ ", WPA2PSK" : "", -+ test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr) ? -+ "; AES/CCMP with WPA" : "", -+ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? -+ ", WPA2" : "", -+ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? -+ ", WPA2PSK" : ""); -+ -+ res = mp_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter); -+ if (!res) { -+ if (packet_filter != wnd->packet_filter) -+ WARNING("wrong packet_filter? 0x%08x, 0x%08x\n", -+ packet_filter, wnd->packet_filter); -+ add_text("packet_filter: 0x%08x\n", packet_filter); -+ } -+ -+ return 0; -+} -+ -+PROC_DECLARE_RO(hw) -+ -+static int proc_settings_read(struct seq_file *sf, void *v) -+{ -+ struct ndis_device *wnd = (struct ndis_device *)sf->private; -+ struct wrap_device_setting *setting; -+ -+ add_text("hangcheck_interval=%d\n", (hangcheck_interval == 0) ? -+ (wnd->hangcheck_interval / HZ) : -1); -+ -+ list_for_each_entry(setting, &wnd->wd->settings, list) { -+ add_text("%s=%s\n", setting->name, setting->value); -+ } -+ -+ list_for_each_entry(setting, &wnd->wd->driver->settings, list) { -+ add_text("%s=%s\n", setting->name, setting->value); -+ } -+ -+ return 0; -+} -+ -+static ssize_t proc_settings_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct ndis_device *wnd = PDE_DATA(file_inode(file)); -+ char setting[MAX_PROC_STR_LEN], *p; -+ unsigned int i; -+ NDIS_STATUS res; -+ -+ if (count > MAX_PROC_STR_LEN) -+ return -EINVAL; -+ -+ memset(setting, 0, sizeof(setting)); -+ if (copy_from_user(setting, buf, count)) -+ return -EFAULT; -+ -+ if ((p = strchr(setting, '\n'))) -+ *p = 0; -+ -+ if ((p = strchr(setting, '='))) -+ *p = 0; -+ -+ if (!strcmp(setting, "hangcheck_interval")) { -+ if (!p) -+ return -EINVAL; -+ p++; -+ i = simple_strtol(p, NULL, 10); -+ hangcheck_del(wnd); -+ if (i > 0) { -+ wnd->hangcheck_interval = i * HZ; -+ hangcheck_add(wnd); -+ } -+ } else if (!strcmp(setting, "suspend")) { -+ if (!p) -+ return -EINVAL; -+ p++; -+ i = simple_strtol(p, NULL, 10); -+ if (i <= 0 || i > 3) -+ return -EINVAL; -+ i = -1; -+ if (wrap_is_pci_bus(wnd->wd->dev_bus)) -+ i = wrap_pnp_suspend_pci_device(wnd->wd->pci.pdev, -+ PMSG_SUSPEND); -+ else if (wrap_is_usb_bus(wnd->wd->dev_bus)) -+ i = wrap_pnp_suspend_usb_device(wnd->wd->usb.intf, -+ PMSG_SUSPEND); -+ if (i) -+ return -EINVAL; -+ } else if (!strcmp(setting, "resume")) { -+ i = -1; -+ if (wrap_is_pci_bus(wnd->wd->dev_bus)) -+ i = wrap_pnp_resume_pci_device(wnd->wd->pci.pdev); -+ else if (wrap_is_usb_bus(wnd->wd->dev_bus)) -+ i = wrap_pnp_resume_usb_device(wnd->wd->usb.intf); -+ if (i) -+ return -EINVAL; -+ } else if (!strcmp(setting, "stats_enabled")) { -+ if (!p) -+ return -EINVAL; -+ p++; -+ i = simple_strtol(p, NULL, 10); -+ if (i > 0) -+ wnd->iw_stats_enabled = TRUE; -+ else -+ wnd->iw_stats_enabled = FALSE; -+ } else if (!strcmp(setting, "packet_filter")) { -+ if (!p) -+ return -EINVAL; -+ p++; -+ i = simple_strtol(p, NULL, 10); -+ res = mp_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, i); -+ if (res) -+ WARNING("setting packet_filter failed: %08X", res); -+ } else if (!strcmp(setting, "reinit")) { -+ if (ndis_reinit(wnd) != NDIS_STATUS_SUCCESS) -+ return -EFAULT; -+ } else { -+ struct ndis_configuration_parameter param; -+ struct unicode_string key; -+ struct ansi_string ansi; -+ -+ if (!p) -+ return -EINVAL; -+ p++; -+ RtlInitAnsiString(&ansi, p); -+ if (RtlAnsiStringToUnicodeString(¶m.data.string, &ansi, -+ TRUE) != STATUS_SUCCESS) -+ EXIT1(return -EFAULT); -+ param.type = NdisParameterString; -+ RtlInitAnsiString(&ansi, setting); -+ if (RtlAnsiStringToUnicodeString(&key, &ansi, -+ TRUE) != STATUS_SUCCESS) { -+ RtlFreeUnicodeString(¶m.data.string); -+ EXIT1(return -EINVAL); -+ } -+ NdisWriteConfiguration(&res, wnd->nmb, &key, ¶m); -+ RtlFreeUnicodeString(&key); -+ RtlFreeUnicodeString(¶m.data.string); -+ if (res != NDIS_STATUS_SUCCESS) -+ return -EFAULT; -+ } -+ return count; -+} -+ -+PROC_DECLARE_RW(settings) -+ -+int wrap_procfs_add_ndis_device(struct ndis_device *wnd) -+{ -+ int ret; -+ -+ if (wrap_procfs_entry == NULL) -+ return -ENOMEM; -+ -+ if (wnd->procfs_iface) { -+ ERROR("%s already registered?", wnd->net_dev->name); -+ return -EINVAL; -+ } -+ wnd->procfs_iface = proc_mkdir(wnd->net_dev->name, wrap_procfs_entry); -+ if (wnd->procfs_iface == NULL) { -+ ERROR("couldn't create proc directory"); -+ return -ENOMEM; -+ } -+ proc_set_user(wnd->procfs_iface, proc_kuid, proc_kgid); -+ -+ ret = proc_make_entry_ro(hw, wnd->procfs_iface, wnd); -+ if (ret) -+ goto err_hw; -+ -+ ret = proc_make_entry_ro(stats, wnd->procfs_iface, wnd); -+ if (ret) -+ goto err_stats; -+ -+ ret = proc_make_entry_ro(encr, wnd->procfs_iface, wnd); -+ if (ret) -+ goto err_encr; -+ -+ ret = proc_make_entry_rw(settings, wnd->procfs_iface, wnd); -+ if (ret) -+ goto err_settings; -+ -+ return 0; -+ -+err_settings: -+ remove_proc_entry("encr", wnd->procfs_iface); -+err_encr: -+ remove_proc_entry("stats", wnd->procfs_iface); -+err_stats: -+ remove_proc_entry("hw", wnd->procfs_iface); -+err_hw: -+ proc_remove(wnd->procfs_iface); -+ wnd->procfs_iface = NULL; -+ return -ENOMEM; -+} -+ -+void wrap_procfs_remove_ndis_device(struct ndis_device *wnd) -+{ -+ struct proc_dir_entry *procfs_iface = xchg(&wnd->procfs_iface, NULL); -+ -+ if (procfs_iface == NULL) -+ return; -+ remove_proc_entry("hw", procfs_iface); -+ remove_proc_entry("stats", procfs_iface); -+ remove_proc_entry("encr", procfs_iface); -+ remove_proc_entry("settings", procfs_iface); -+ if (wrap_procfs_entry) -+ proc_remove(procfs_iface); -+} -+ -+static int proc_debug_read(struct seq_file *sf, void *v) -+{ -+#if ALLOC_DEBUG -+ enum alloc_type type; -+#endif -+ -+ add_text("%d\n", debug); -+#if ALLOC_DEBUG -+ for (type = 0; type < ALLOC_TYPE_MAX; type++) -+ add_text("total size of allocations in %s: %d\n", -+ alloc_type_name[type], alloc_size(type)); -+#endif -+ return 0; -+} -+ -+static ssize_t proc_debug_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ int i; -+ char setting[MAX_PROC_STR_LEN], *p; -+ -+ if (count > MAX_PROC_STR_LEN) -+ return -EINVAL; -+ -+ memset(setting, 0, sizeof(setting)); -+ if (copy_from_user(setting, buf, count)) -+ return -EFAULT; -+ -+ if ((p = strchr(setting, '\n'))) -+ *p = 0; -+ -+ if ((p = strchr(setting, '='))) -+ *p = 0; -+ -+ i = simple_strtol(setting, NULL, 10); -+ if (i >= 0 && i < 10) -+ debug = i; -+ else -+ return -EINVAL; -+ return count; -+} -+ -+PROC_DECLARE_RW(debug) -+ -+int wrap_procfs_init(void) -+{ -+ int ret; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) -+ struct user_namespace *ns = current_user_ns(); -+ proc_kuid = make_kuid(ns, proc_uid); -+ if (!uid_valid(proc_kuid)) { -+ ERROR("invalid UID\n"); -+ return -EINVAL; -+ } -+ proc_kgid = make_kgid(ns, proc_gid); -+ if (!gid_valid(proc_kgid)) { -+ ERROR("invalid GID\n"); -+ return -EINVAL; -+ } -+#endif -+ -+ wrap_procfs_entry = proc_mkdir(DRIVER_NAME, proc_net_root); -+ if (wrap_procfs_entry == NULL) { -+ ERROR("couldn't create procfs directory"); -+ return -ENOMEM; -+ } -+ proc_set_user(wrap_procfs_entry, proc_kuid, proc_kgid); -+ -+ ret = proc_make_entry_rw(debug, wrap_procfs_entry, NULL); -+ -+ return ret; -+} -+ -+void wrap_procfs_remove(void) -+{ -+ if (wrap_procfs_entry == NULL) -+ return; -+ remove_proc_entry("debug", wrap_procfs_entry); -+ proc_remove(wrap_procfs_entry); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/rtl.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/rtl.c ---- linux-3.12.2/3rdparty/ndiswrapper/rtl.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/rtl.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,715 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * Copyright (C) 2006-2007 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ntoskernel.h" -+#include "rtl_exports.h" -+ -+wstdcall SIZE_T WIN_FUNC(RtlCompareMemory,3) -+ (const void *a, const void *b, SIZE_T len) -+{ -+ size_t i; -+ char *x, *y; -+ -+ ENTER1("%p %p %zd", a, b, len); -+ x = (char *)a; -+ y = (char *)b; -+ /* MSDN says this should return number of bytes that compare as -+ * equal. This can be interpreted as either all bytes that are -+ * equal in 'len' bytes or that only until the bytes compare as -+ * not equal. Initially we had it the former way, but Realtek driver -+ * doesn't like it that way - it takes many attempts to associate -+ * with WPA. ReactOS returns the number of bytes that are equal -+ * before the first differing byte. -+ * According to lords at #reactos, that is the way it should be -+ * and that msdn is wrong about it! -+ */ -+ for (i = 0; i < len && x[i] == y[i]; i++) -+ ; -+ return i; -+} -+ -+wstdcall void WIN_FUNC(RtlCopyMemory,3) -+ (void *dst, const void *src, SIZE_T length) -+{ -+ memcpy(dst, src, length); -+} -+ -+wstdcall void WIN_FUNC(RtlZeroMemory,2) -+ (void *dst, SIZE_T length) -+{ -+ memset(dst, 0, length); -+} -+ -+wstdcall void WIN_FUNC(RtlSecureZeroMemory,2) -+ (void *dst, SIZE_T length) -+{ -+ memset(dst, 0, length); -+} -+ -+wstdcall void WIN_FUNC(RtlFillMemory,3) -+ (void *dest, SIZE_T length, UCHAR fill) -+{ -+ memset(dest, fill, length); -+} -+ -+wstdcall void WIN_FUNC(RtlMoveMemory,3) -+ (void *dest, const void *src, SIZE_T length) -+{ -+ memmove(dest, src, length); -+} -+ -+wstdcall LONG WIN_FUNC(RtlCompareString,3) -+ (const struct ansi_string *s1, const struct ansi_string *s2, -+ BOOLEAN case_insensitive) -+{ -+ unsigned int len; -+ LONG ret = 0; -+ const char *p1, *p2; -+ -+ ENTER2(""); -+ len = min(s1->length, s2->length); -+ p1 = s1->buf; -+ p2 = s2->buf; -+ if (case_insensitive) -+ while (!ret && len--) -+ ret = toupper(*p1++) - toupper(*p2++); -+ else -+ while (!ret && len--) -+ ret = *p1++ - *p2++; -+ if (!ret) -+ ret = s1->length - s2->length; -+ EXIT2(return ret); -+} -+ -+wstdcall LONG WIN_FUNC(RtlCompareUnicodeString,3) -+ (const struct unicode_string *s1, const struct unicode_string *s2, -+ BOOLEAN case_insensitive) -+{ -+ unsigned int len; -+ LONG ret = 0; -+ const wchar_t *p1, *p2; -+ -+ ENTER2(""); -+ -+ len = min(s1->length, s2->length) / sizeof(wchar_t); -+ p1 = s1->buf; -+ p2 = s2->buf; -+ if (case_insensitive) -+ while (!ret && len--) -+ ret = toupper((u8)*p1++) - toupper((u8)*p2++); -+ else -+ while (!ret && len--) -+ ret = (u8)*p1++ - (u8)*p2++; -+ if (!ret) -+ ret = s1->length - s2->length; -+ TRACE2("len: %d, ret: %d", len, ret); -+ EXIT2(return ret); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(RtlEqualString,3) -+ (const struct ansi_string *s1, const struct ansi_string *s2, -+ BOOLEAN case_insensitive) -+{ -+ ENTER1(""); -+ if (s1->length != s2->length) -+ return FALSE; -+ return !RtlCompareString(s1, s2, case_insensitive); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(RtlEqualUnicodeString,3) -+ (const struct unicode_string *s1, const struct unicode_string *s2, -+ BOOLEAN case_insensitive) -+{ -+ if (s1->length != s2->length) -+ return FALSE; -+ return !RtlCompareUnicodeString(s1, s2, case_insensitive); -+} -+ -+wstdcall void WIN_FUNC(RtlCopyUnicodeString,2) -+ (struct unicode_string *dst, struct unicode_string *src) -+{ -+ ENTER1("%p, %p", dst, src); -+ if (src && src->buf && dst->buf) { -+ dst->length = min(src->length, dst->max_length); -+ memcpy(dst->buf, src->buf, dst->length); -+ if (dst->length < dst->max_length) -+ dst->buf[dst->length / sizeof(dst->buf[0])] = 0; -+ } else -+ dst->length = 0; -+ EXIT1(return); -+} -+ -+wstdcall void WIN_FUNC(RtlCopyString,2) -+ (struct ansi_string *dst, struct ansi_string *src) -+{ -+ ENTER1("%p, %p", dst, src); -+ if (src && src->buf && dst->buf) { -+ dst->length = min(src->length, dst->max_length); -+ memcpy(dst->buf, src->buf, dst->length); -+ if (dst->length < dst->max_length) -+ dst->buf[dst->length] = 0; -+ } else -+ dst->length = 0; -+ EXIT1(return); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlAppendUnicodeToString,2) -+ (struct unicode_string *dst, wchar_t *src) -+{ -+ if (src) { -+ int len; -+ for (len = 0; src[len]; len++) -+ ; -+ if (dst->length + (len * sizeof(dst->buf[0])) > dst->max_length) -+ return STATUS_BUFFER_TOO_SMALL; -+ memcpy(&dst->buf[dst->length], src, len * sizeof(dst->buf[0])); -+ dst->length += len * sizeof(dst->buf[0]); -+ if (dst->max_length > dst->length) -+ dst->buf[dst->length / sizeof(dst->buf[0])] = 0; -+ } -+ return STATUS_SUCCESS; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlAppendUnicodeStringToString,2) -+ (struct unicode_string *dst, struct unicode_string *src) -+{ -+ if (dst->max_length < src->length + dst->length) -+ return STATUS_BUFFER_TOO_SMALL; -+ if (src->length) { -+ memcpy(&dst->buf[dst->length], src->buf, src->length); -+ dst->length += src->length; -+ if (dst->max_length > dst->length) -+ dst->buf[dst->length / sizeof(dst->buf[0])] = 0; -+ } -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall ULONG WIN_FUNC(RtlxAnsiStringToUnicodeSize,1) -+ (const struct ansi_string *string) -+{ -+ int i; -+ -+ for (i = 0; i < string->max_length && string->buf[i]; i++) -+ ; -+ return i * sizeof(wchar_t); -+} -+ -+wstdcall ULONG WIN_FUNC(RtlxUnicodeStringToAnsiSize,1) -+ (const struct unicode_string *string) -+{ -+ int i; -+ -+ for (i = 0; i < string->max_length && string->buf[i]; i++) -+ ; -+ return i; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlAnsiStringToUnicodeString,3) -+ (struct unicode_string *dst, const struct ansi_string *src, -+ BOOLEAN alloc) -+{ -+ int i, n; -+ -+ n = RtlxAnsiStringToUnicodeSize(src); -+ TRACE2("%d, %d, %d, %d, %p", n, dst->max_length, src->length, -+ src->max_length, src->buf); -+ if (alloc == TRUE) { -+#if 0 -+ if (n == 0) { -+ dst->length = dst->max_length = 0; -+ dst->buf = NULL; -+ EXIT2(return STATUS_SUCCESS); -+ } -+#endif -+ dst->max_length = n + sizeof(dst->buf[0]); -+ dst->buf = ExAllocatePoolWithTag(NonPagedPool, -+ dst->max_length, 0); -+ if (!dst->buf) { -+ dst->max_length = dst->length = 0; -+ EXIT2(return STATUS_NO_MEMORY); -+ } -+ } else if (dst->max_length < n) -+ EXIT2(return STATUS_BUFFER_TOO_SMALL); -+ -+ dst->length = n; -+ n /= sizeof(dst->buf[0]); -+ for (i = 0; i < n; i++) -+ dst->buf[i] = src->buf[i]; -+ if (i * sizeof(dst->buf[0]) < dst->max_length) -+ dst->buf[i] = 0; -+ TRACE2("dst: length: %d, max_length: %d, string: %p", -+ dst->length, dst->max_length, src->buf); -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlUnicodeStringToAnsiString,3) -+ (struct ansi_string *dst, const struct unicode_string *src, -+ BOOLEAN alloc) -+{ -+ int i, n; -+ -+ n = RtlxUnicodeStringToAnsiSize(src); -+ TRACE2("%d, %d, %d, %d, %p", n, dst->max_length, src->length, -+ src->max_length, src->buf); -+ if (alloc == TRUE) { -+#if 0 -+ if (n == 0) { -+ dst->length = dst->max_length = 0; -+ dst->buf = NULL; -+ EXIT2(return STATUS_SUCCESS); -+ } -+#endif -+ dst->max_length = n + sizeof(dst->buf[0]); -+ dst->buf = ExAllocatePoolWithTag(NonPagedPool, -+ dst->max_length, 0); -+ if (!dst->buf) { -+ dst->max_length = dst->length = 0; -+ EXIT1(return STATUS_NO_MEMORY); -+ } -+ } else if (dst->max_length < n) -+ EXIT2(return STATUS_BUFFER_TOO_SMALL); -+ -+ dst->length = n; -+ for (i = 0; i < n; i++) -+ dst->buf[i] = src->buf[i]; -+ if (i < dst->max_length) -+ dst->buf[i] = 0; -+ TRACE2("string: %p, len: %d(%d)", dst->buf, dst->length, -+ dst->max_length); -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlUnicodeStringToInteger,3) -+ (struct unicode_string *ustring, ULONG base, ULONG *value) -+{ -+ int i, sign = 1; -+ ULONG res; -+ typeof(ustring->buf) string; -+ -+ if (ustring->length == 0) { -+ *value = 0; -+ return STATUS_SUCCESS; -+ } -+ -+ string = ustring->buf; -+ i = 0; -+ while (i < (ustring->length / sizeof(*string)) && string[i] == ' ') -+ i++; -+ if (string[i] == '+') -+ i++; -+ else if (string[i] == '-') { -+ i++; -+ sign = -1; -+ } -+ if (base == 0) { -+ base = 10; -+ if (i <= ((ustring->length / sizeof(*string)) - 2) && -+ string[i] == '0') { -+ i++; -+ if (string[i] == 'b') { -+ base = 2; -+ i++; -+ } else if (string[i] == 'o') { -+ base = 8; -+ i++; -+ } else if (string[i] == 'x') { -+ base = 16; -+ i++; -+ } -+ } -+ } -+ if (!(base == 2 || base == 8 || base == 10 || base == 16)) -+ EXIT2(return STATUS_INVALID_PARAMETER); -+ -+ for (res = 0; i < (ustring->length / sizeof(*string)); i++) { -+ int v; -+ if (isdigit((char)string[i])) -+ v = string[i] - '0'; -+ else if (isxdigit((char)string[i])) -+ v = tolower((char)string[i]) - 'a' + 10; -+ else -+ v = base; -+ if (v >= base) -+ EXIT2(return STATUS_INVALID_PARAMETER); -+ res = res * base + v; -+ } -+ *value = sign * res; -+ EXIT3(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlCharToInteger,3) -+ (const char *string, ULONG base, ULONG *value) -+{ -+ int sign = 1; -+ ULONG res; -+ -+ if (!string || !value) -+ EXIT2(return STATUS_INVALID_PARAMETER); -+ while (*string == ' ') -+ string++; -+ if (*string == '+') -+ string++; -+ else if (*string == '-') { -+ string++; -+ sign = -1; -+ } -+ if (base == 0) { -+ base = 10; -+ if (*string == '0') { -+ string++; -+ if (*string == 'b') { -+ base = 2; -+ string++; -+ } else if (*string == 'o') { -+ base = 8; -+ string++; -+ } else if (*string == 'x') { -+ base = 16; -+ string++; -+ } -+ } -+ } -+ if (!(base == 2 || base == 8 || base == 10 || base == 16)) -+ EXIT2(return STATUS_INVALID_PARAMETER); -+ -+ for (res = 0; *string; string++) { -+ int v; -+ if (isdigit(*string)) -+ v = *string - '0'; -+ else if (isxdigit(*string)) -+ v = tolower(*string) - 'a' + 10; -+ else -+ v = base; -+ if (v >= base) -+ EXIT2(return STATUS_INVALID_PARAMETER); -+ res = res * base + v; -+ } -+ *value = sign * res; -+ EXIT3(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlIntegerToUnicodeString,3) -+ (ULONG value, ULONG base, struct unicode_string *ustring) -+{ -+ typeof(ustring->buf) buf = ustring->buf; -+ int i; -+ -+ if (base == 0) -+ base = 10; -+ if (!(base == 2 || base == 8 || base == 10 || base == 16)) -+ return STATUS_INVALID_PARAMETER; -+ for (i = 0; value && i < ustring->max_length / sizeof(*buf); i++) { -+ int r; -+ r = value % base; -+ value /= base; -+ if (r < 10) -+ buf[i] = r + '0'; -+ else -+ buf[i] = r + 'a' - 10; -+ } -+ if (value) -+ return STATUS_BUFFER_OVERFLOW; -+ ustring->length = i * sizeof(*buf); -+ return STATUS_SUCCESS; -+} -+ -+wstdcall LARGE_INTEGER WIN_FUNC(RtlConvertUlongToLargeInteger,1) -+ (ULONG ul) -+{ -+ LARGE_INTEGER li = ul; -+ return li; -+} -+ -+wfastcall USHORT WIN_FUNC(RtlUshortByteSwap,1) -+ (USHORT src) -+{ -+ return __swab16(src); -+} -+ -+wfastcall ULONG WIN_FUNC(RtlUlongByteSwap,1) -+ (ULONG src) -+{ -+ /* ULONG is 32 bits for both 32-bit and 64-bit architectures */ -+ return __swab32(src); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlUpcaseUnicodeString,3) -+ (struct unicode_string *dst, struct unicode_string *src, BOOLEAN alloc) -+{ -+ USHORT i, n; -+ -+ if (alloc) { -+ dst->buf = ExAllocatePoolWithTag(NonPagedPool, src->length, 0); -+ if (dst->buf) -+ dst->max_length = src->length; -+ else -+ EXIT2(return STATUS_NO_MEMORY); -+ } else { -+ if (dst->max_length < src->length) -+ EXIT2(return STATUS_BUFFER_OVERFLOW); -+ } -+ -+ n = src->length / sizeof(src->buf[0]); -+ for (i = 0; i < n; i++) -+ dst->buf[i] = toupper(src->buf[i]); -+ -+ dst->length = src->length; -+ EXIT3(return STATUS_SUCCESS); -+} -+ -+wstdcall void WIN_FUNC(RtlInitUnicodeString,2) -+ (struct unicode_string *dst, const wchar_t *src) -+{ -+ ENTER2("%p", dst); -+ if (dst == NULL) -+ EXIT1(return); -+ if (src == NULL) { -+ dst->max_length = dst->length = 0; -+ dst->buf = NULL; -+ } else { -+ int i; -+ for (i = 0; (char)src[i]; i++) -+ ; -+ dst->buf = (typeof(dst->buf))src; -+ dst->length = i * sizeof(dst->buf[0]); -+ dst->max_length = (i + 1) * sizeof(dst->buf[0]); -+ } -+ EXIT1(return); -+} -+ -+wstdcall void WIN_FUNC(RtlInitAnsiString,2) -+ (struct ansi_string *dst, const char *src) -+{ -+ ENTER2("%p", dst); -+ if (dst == NULL) -+ EXIT2(return); -+ if (src == NULL) { -+ dst->max_length = dst->length = 0; -+ dst->buf = NULL; -+ } else { -+ int i; -+ for (i = 0; src[i]; i++) -+ ; -+ dst->buf = (typeof(dst->buf))src; -+ dst->length = i; -+ dst->max_length = i + 1; -+ } -+ TRACE2("%p", dst->buf); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(RtlInitString,2) -+ (struct ansi_string *dst, const char *src) -+{ -+ ENTER2("%p", dst); -+ RtlInitAnsiString(dst, src); -+ EXIT2(return); -+} -+ -+wstdcall void WIN_FUNC(RtlFreeUnicodeString,1) -+ (struct unicode_string *string) -+{ -+ ENTER2("%p", string); -+ if (string == NULL) -+ return; -+ if (string->buf) -+ ExFreePool(string->buf); -+ string->length = string->max_length = 0; -+ string->buf = NULL; -+ return; -+} -+ -+wstdcall void WIN_FUNC(RtlFreeAnsiString,1) -+ (struct ansi_string *string) -+{ -+ ENTER2("%p", string); -+ if (string == NULL) -+ return; -+ if (string->buf) -+ ExFreePool(string->buf); -+ string->length = string->max_length = 0; -+ string->buf = NULL; -+ return; -+} -+ -+/* guid string is of the form: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */ -+wstdcall NTSTATUS WIN_FUNC(RtlGUIDFromString,2) -+ (struct unicode_string *guid_string, struct guid *guid) -+{ -+ struct ansi_string ansi; -+ NTSTATUS ret; -+ int i, j, k, l, m; -+ -+ ret = RtlUnicodeStringToAnsiString(&ansi, guid_string, TRUE); -+ if (ret != STATUS_SUCCESS) -+ return ret; -+ if (ansi.length != 37 || ansi.buf[0] != '{' || -+ ansi.buf[36] != '}' || ansi.buf[9] != '-' || -+ ansi.buf[14] != '-' || ansi.buf[19] != '-' || -+ ansi.buf[24] != '-') { -+ RtlFreeAnsiString(&ansi); -+ EXIT2(return STATUS_INVALID_PARAMETER); -+ } -+ memcpy(&guid->data4, &ansi.buf[29], sizeof(guid->data3)); -+ /* set end of data3 for scanf */ -+ ansi.buf[29] = 0; -+ if (sscanf(&ansi.buf[1], "%x", &i) == 1 && -+ sscanf(&ansi.buf[10], "%x", &j) == 1 && -+ sscanf(&ansi.buf[15], "%x", &k) == 1 && -+ sscanf(&ansi.buf[20], "%x", &l) == 1 && -+ sscanf(&ansi.buf[25], "%x", &m) == 1) { -+ guid->data1 = (i << 16) | (j < 8) | k; -+ guid->data2 = l; -+ guid->data3 = m; -+ ret = STATUS_SUCCESS; -+ } else -+ ret = STATUS_INVALID_PARAMETER; -+ RtlFreeAnsiString(&ansi); -+ return ret; -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlQueryRegistryValues,5) -+ (ULONG relative, wchar_t *path, struct rtl_query_registry_table *tbl, -+ void *context, void *env) -+{ -+ struct ansi_string ansi; -+ struct unicode_string unicode; -+ NTSTATUS status, ret; -+ static int i = 0; -+ -+ ENTER3("%x, %p", relative, tbl); -+// TODO(); -+ -+ RtlInitUnicodeString(&unicode, path); -+ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == -+ STATUS_SUCCESS) { -+ TRACE2("%s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ ret = STATUS_SUCCESS; -+ for (; tbl->name; tbl++) { -+ RtlInitUnicodeString(&unicode, tbl->name); -+ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == -+ STATUS_SUCCESS) { -+ TRACE2("name: %s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ TRACE2("flags: %08X", tbl->flags); -+ if (tbl->flags == RTL_QUERY_REGISTRY_DIRECT) { -+ TRACE2("type: %08X", tbl->def_type); -+ if (tbl->def_type == REG_DWORD) { -+ /* Atheros USB driver needs this, but -+ * don't know where and how to get its -+ * value */ -+ if (tbl->def_data) { -+ TRACE2("def_data: %x", -+ *(int *)tbl->def_data); -+ *(DWORD *)tbl->context = 0x5f292a + i++; -+// *(DWORD *)tbl->def_data; -+ } else -+ *(DWORD *)tbl->context = 0x2345dbe; -+ } -+ } else { -+ void *data; -+ ULONG type, length; -+ -+ if (!tbl->query_func) { -+ ERROR("oops: no query_func"); -+ ret = STATUS_INVALID_PARAMETER; -+ break; -+ } -+ if (tbl->flags & RTL_QUERY_REGISTRY_NOVALUE) { -+ data = NULL; -+ type = REG_NONE; -+ length = 0; -+ } else { -+ data = tbl->def_data; -+ type = tbl->def_type; -+ length = tbl->def_length;; -+ } -+ TRACE2("calling query_func: %p", tbl->query_func); -+ status = LIN2WIN6(tbl->query_func, tbl->name, type, -+ data, length, context, env); -+ TRACE2("status: %08X", status); -+ if (status) { -+ if (status == STATUS_BUFFER_TOO_SMALL) -+ ret = STATUS_BUFFER_TOO_SMALL; -+ else -+ EXIT2(return STATUS_INVALID_PARAMETER); -+ } -+ } -+ } -+ EXIT3(return ret); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlWriteRegistryValue,6) -+ (ULONG relative, wchar_t *path, wchar_t *name, ULONG type, -+ void *data, ULONG length) -+{ -+ struct ansi_string ansi; -+ struct unicode_string unicode; -+ -+ ENTER3("%d", relative); -+ TODO(); -+ -+ RtlInitUnicodeString(&unicode, path); -+ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == -+ STATUS_SUCCESS) { -+ TRACE2("%s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ RtlInitUnicodeString(&unicode, name); -+ if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) == -+ STATUS_SUCCESS) { -+ TRACE2("%s", ansi.buf); -+ RtlFreeAnsiString(&ansi); -+ } -+ EXIT5(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS WIN_FUNC(RtlDeleteRegistryValue,3) -+ (ULONG relative, wchar_t *path, wchar_t *name) -+{ -+ return STATUS_SUCCESS; -+} -+ -+wstdcall void WIN_FUNC(RtlAssert,4) -+ (char *failed_assertion, char *file_name, ULONG line_num, char *message) -+{ -+ ERROR("assertion '%s' failed at %s line %d%s", -+ failed_assertion, file_name, line_num, message ? message : ""); -+ return; -+} -+ -+wstdcall void WIN_FUNC(RtlUnwind,0) -+ (void) -+{ -+ TODO(); -+} -+ -+wstdcall void WIN_FUNC(RtlRaiseException,1) -+ (void *exception_record) -+{ -+ TODO(); -+} -+ -+wstdcall BOOLEAN WIN_FUNC(RtlIsServicePackVersionInstalled,1) -+ (ULONG version) -+{ -+ /* Assume we have all service packs */ -+ TRACE1("version: %d", version); -+ return TRUE; -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.c ---- linux-3.12.2/3rdparty/ndiswrapper/usb.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,1496 @@ -+/* -+ * Copyright (C) 2004 Jan Kiszka -+ * Copyright (C) 2005 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ndis.h" -+#include "usb.h" -+#include "usb_exports.h" -+ -+#ifdef USB_DEBUG -+static unsigned int urb_id = 0; -+ -+#define DUMP_WRAP_URB(wrap_urb, dir) \ -+ USBTRACE("urb %p (%d) %s: buf: %p, len: %d, pipe: 0x%x, %d", \ -+ (wrap_urb)->urb, (wrap_urb)->id, \ -+ (dir == USB_DIR_OUT) ? "going down" : "coming back", \ -+ (wrap_urb)->urb->transfer_buffer, \ -+ (wrap_urb)->urb->transfer_buffer_length, \ -+ (wrap_urb)->urb->pipe, (wrap_urb)->urb->status) -+ -+#define DUMP_URB_BUFFER(urb, dir) \ -+ while (debug >= 2) { \ -+ int i; \ -+ char msg[20], *t; \ -+ if (!urb->transfer_buffer) \ -+ break; \ -+ if (!((usb_pipein(urb->pipe) && dir == USB_DIR_IN) || \ -+ (usb_pipeout(urb->pipe) && dir == USB_DIR_OUT))) \ -+ break; \ -+ t = msg; \ -+ t += sprintf(t, "%d: ", (urb)->actual_length); \ -+ for (i = 0; i < urb->actual_length && \ -+ t < &msg[sizeof(msg) - 4]; i++) \ -+ t += sprintf(t, "%02X ", \ -+ ((char *)urb->transfer_buffer)[i]); \ -+ *t = 0; \ -+ USBTRACE("%s", msg); \ -+ break; \ -+ } -+ -+#else -+ -+#define DUMP_WRAP_URB(wrap_urb, dir) (void)0 -+#define DUMP_URB_BUFFER(urb, dir) (void)0 -+ -+#endif -+ -+#define CUR_ALT_SETTING(intf) (intf)->cur_altsetting -+ -+#ifndef USB_CTRL_SET_TIMEOUT -+#define USB_CTRL_SET_TIMEOUT 5000 -+#endif -+ -+#ifndef USB_CTRL_GET_TIMEOUT -+#define USB_CTRL_GET_TIMEOUT 5000 -+#endif -+ -+#ifndef URB_NO_TRANSFER_DMA_MAP -+#define URB_NO_TRANSFER_DMA_MAP 0 -+#endif -+ -+/* wrap_urb->flags */ -+/* transfer_buffer for urb is allocated; free it in wrap_free_urb */ -+#define WRAP_URB_COPY_BUFFER 0x01 -+ -+static inline int wrap_cancel_urb(struct wrap_urb *wrap_urb) -+{ -+ int ret; -+ USBTRACE("%p, %p, %d", wrap_urb, wrap_urb->urb, wrap_urb->state); -+ if (wrap_urb->state != URB_SUBMITTED) -+ USBEXIT(return -1); -+ ret = usb_unlink_urb(wrap_urb->urb); -+ USBTRACE("ret: %d", ret); -+ if (ret == -EINPROGRESS) -+ return 0; -+ else { -+ WARNING("unlink failed: %d", ret); -+ return ret; -+ } -+} -+ -+#define URB_STATUS(wrap_urb) (wrap_urb->urb->status) -+ -+static struct nt_list wrap_urb_complete_list; -+static spinlock_t wrap_urb_complete_list_lock; -+ -+static struct work_struct wrap_urb_complete_work; -+static void wrap_urb_complete_worker(struct work_struct *dummy); -+ -+static void kill_all_urbs(struct wrap_device *wd, int complete) -+{ -+ struct nt_list *ent; -+ struct wrap_urb *wrap_urb; -+ KIRQL irql; -+ -+ USBTRACE("%d", wd->usb.num_alloc_urbs); -+ while (1) { -+ IoAcquireCancelSpinLock(&irql); -+ ent = RemoveHeadList(&wd->usb.wrap_urb_list); -+ IoReleaseCancelSpinLock(irql); -+ if (!ent) -+ break; -+ wrap_urb = container_of(ent, struct wrap_urb, list); -+ if (wrap_urb->state == URB_SUBMITTED) { -+ WARNING("Windows driver %s didn't free urb: %p", -+ wd->driver->name, wrap_urb->urb); -+ if (!complete) -+ wrap_urb->urb->complete = NULL; -+ usb_kill_urb(wrap_urb->urb); -+ } -+ USBTRACE("%p, %p", wrap_urb, wrap_urb->urb); -+ usb_free_urb(wrap_urb->urb); -+ kfree(wrap_urb); -+ } -+ wd->usb.num_alloc_urbs = 0; -+} -+ -+/* for a given Linux urb status code, return corresponding NT urb status */ -+static USBD_STATUS wrap_urb_status(int urb_status) -+{ -+ switch (urb_status) { -+ case 0: -+ return USBD_STATUS_SUCCESS; -+ case -EPROTO: -+ return USBD_STATUS_TIMEOUT; -+ case -EILSEQ: -+ return USBD_STATUS_CRC; -+ case -EPIPE: -+ return USBD_STATUS_INVALID_PIPE_HANDLE; -+ case -ECOMM: -+ return USBD_STATUS_DATA_OVERRUN; -+ case -ENOSR: -+ return USBD_STATUS_DATA_UNDERRUN; -+ case -EOVERFLOW: -+ return USBD_STATUS_BABBLE_DETECTED; -+ case -EREMOTEIO: -+ return USBD_STATUS_ERROR_SHORT_TRANSFER;; -+ case -ENODEV: -+ case -ESHUTDOWN: -+ case -ENOENT: -+ return USBD_STATUS_DEVICE_GONE; -+ case -ENOMEM: -+ return USBD_STATUS_NO_MEMORY; -+ case -EINVAL: -+ return USBD_STATUS_REQUEST_FAILED; -+ default: -+ return USBD_STATUS_NOT_SUPPORTED; -+ } -+} -+ -+/* for a given USBD_STATUS, return its corresponding NTSTATUS (for irp) */ -+static NTSTATUS nt_urb_irp_status(USBD_STATUS nt_urb_status) -+{ -+ switch (nt_urb_status) { -+ case USBD_STATUS_SUCCESS: -+ return STATUS_SUCCESS; -+ case USBD_STATUS_DEVICE_GONE: -+ return STATUS_DEVICE_REMOVED; -+ case USBD_STATUS_PENDING: -+ return STATUS_PENDING; -+ case USBD_STATUS_NOT_SUPPORTED: -+ return STATUS_NOT_IMPLEMENTED; -+ case USBD_STATUS_NO_MEMORY: -+ return STATUS_NO_MEMORY; -+ case USBD_STATUS_REQUEST_FAILED: -+ return STATUS_NOT_SUPPORTED; -+ default: -+ return STATUS_FAILURE; -+ } -+} -+ -+static void wrap_free_urb(struct urb *urb) -+{ -+ struct wrap_urb *wrap_urb = urb->context; -+ struct irp *irp = wrap_urb->irp; -+ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); -+ -+ USBTRACE("freeing urb: %p", urb); -+ irp->cancel_routine = NULL; -+ IRP_WRAP_URB(irp) = NULL; -+ if (wrap_urb->flags & WRAP_URB_COPY_BUFFER) { -+ USBTRACE("freeing DMA buffer for URB: %p %p", -+ urb, urb->transfer_buffer); -+ usb_free_coherent(wd->usb.udev, urb->transfer_buffer_length, -+ urb->transfer_buffer, urb->transfer_dma); -+ } -+ kfree(urb->setup_packet); -+ if (wd->usb.num_alloc_urbs > MAX_ALLOCATED_URBS) { -+ IoAcquireCancelSpinLock(&irp->cancel_irql); -+ RemoveEntryList(&wrap_urb->list); -+ wd->usb.num_alloc_urbs--; -+ IoReleaseCancelSpinLock(irp->cancel_irql); -+ usb_free_urb(urb); -+ kfree(wrap_urb); -+ } else { -+ wrap_urb->state = URB_FREE; -+ wrap_urb->flags = 0; -+ wrap_urb->irp = NULL; -+ } -+ return; -+} -+ -+void wrap_suspend_urbs(struct wrap_device *wd) -+{ -+ /* TODO: do we need to cancel urbs? */ -+ USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs); -+} -+ -+void wrap_resume_urbs(struct wrap_device *wd) -+{ -+ /* TODO: do we need to resubmit urbs? */ -+ USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs); -+} -+ -+wstdcall void wrap_cancel_irp(struct device_object *dev_obj, struct irp *irp) -+{ -+ struct urb *urb; -+ struct wrap_urb *wrap_urb = IRP_WRAP_URB(irp); -+ -+ /* NB: this function is called holding Cancel spinlock */ -+ USBENTER("irp: %p", irp); -+ urb = wrap_urb->urb; -+ USBTRACE("canceling urb %p", urb); -+ if (wrap_cancel_urb(IRP_WRAP_URB(irp))) { -+ irp->cancel = FALSE; -+ ERROR("urb %p can't be canceled: %d", urb, wrap_urb->state); -+ } else -+ USBTRACE("urb %p canceled", urb); -+ IoReleaseCancelSpinLock(irp->cancel_irql); -+ return; -+} -+WIN_FUNC_DECL(wrap_cancel_irp,2) -+ -+static struct urb *wrap_alloc_urb(struct irp *irp, unsigned int pipe, -+ void *buf, unsigned int buf_len) -+{ -+ struct urb *urb; -+ gfp_t alloc_flags; -+ struct wrap_urb *wrap_urb; -+ struct wrap_device *wd; -+ -+ USBENTER("irp: %p", irp); -+ wd = IRP_WRAP_DEVICE(irp); -+ -+ /* Don't interfere with URB cleanup by the kernel */ -+ if (test_bit(HW_DISABLED, &wd->hw_status)) -+ return NULL; -+ -+ alloc_flags = irql_gfp(); -+ IoAcquireCancelSpinLock(&irp->cancel_irql); -+ urb = NULL; -+ nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) { -+ if (cmpxchg(&wrap_urb->state, URB_FREE, -+ URB_ALLOCATED) == URB_FREE) { -+ urb = wrap_urb->urb; -+ /* Clean URB but keep the refcount */ -+ memset((char *)urb + sizeof(urb->kref), 0, -+ sizeof(*urb) - sizeof(urb->kref)); -+ break; -+ } -+ } -+ if (!urb) { -+ IoReleaseCancelSpinLock(irp->cancel_irql); -+ wrap_urb = kzalloc(sizeof(*wrap_urb), alloc_flags); -+ if (!wrap_urb) { -+ WARNING("couldn't allocate memory"); -+ return NULL; -+ } -+ urb = usb_alloc_urb(0, alloc_flags); -+ if (!urb) { -+ WARNING("couldn't allocate urb"); -+ kfree(wrap_urb); -+ return NULL; -+ } -+ IoAcquireCancelSpinLock(&irp->cancel_irql); -+ wrap_urb->urb = urb; -+ wrap_urb->state = URB_ALLOCATED; -+ InsertTailList(&wd->usb.wrap_urb_list, &wrap_urb->list); -+ wd->usb.num_alloc_urbs++; -+ } -+ -+#ifdef URB_ASYNC_UNLINK -+ urb->transfer_flags |= URB_ASYNC_UNLINK; -+#elif defined(USB_ASYNC_UNLINK) -+ urb->transfer_flags |= USB_ASYNC_UNLINK; -+#endif -+ urb->context = wrap_urb; -+ wrap_urb->irp = irp; -+ IRP_WRAP_URB(irp) = wrap_urb; -+ /* called as Windows function */ -+ irp->cancel_routine = WIN_FUNC_PTR(wrap_cancel_irp,2); -+ IoReleaseCancelSpinLock(irp->cancel_irql); -+ USBTRACE("urb: %p", urb); -+ -+ urb->transfer_buffer_length = buf_len; -+ if (buf_len && buf && (!virt_addr_valid(buf) -+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_HIGHMEM4G) -+ || PageHighMem(virt_to_page(buf)) -+#endif -+ )) { -+ urb->transfer_buffer = -+ usb_alloc_coherent(wd->usb.udev, buf_len, alloc_flags, -+ &urb->transfer_dma); -+ if (!urb->transfer_buffer) { -+ WARNING("couldn't allocate dma buf"); -+ IoAcquireCancelSpinLock(&irp->cancel_irql); -+ irp->cancel_routine = NULL; -+ wrap_urb->state = URB_FREE; -+ wrap_urb->irp = NULL; -+ IRP_WRAP_URB(irp) = NULL; -+ IoReleaseCancelSpinLock(irp->cancel_irql); -+ return NULL; -+ } -+ if (urb->transfer_dma) -+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -+ wrap_urb->flags |= WRAP_URB_COPY_BUFFER; -+ if (usb_pipeout(pipe)) -+ memcpy(urb->transfer_buffer, buf, buf_len); -+ USBTRACE("DMA buf for urb %p: %p", urb, urb->transfer_buffer); -+ } else -+ urb->transfer_buffer = buf; -+ return urb; -+} -+ -+static USBD_STATUS wrap_submit_urb(struct irp *irp) -+{ -+ int ret; -+ struct wrap_urb *wrap_urb = IRP_WRAP_URB(irp); -+ struct urb *urb = wrap_urb->urb; -+ union nt_urb *nt_urb = IRP_URB(irp); -+ -+#ifdef USB_DEBUG -+ if (wrap_urb->state != URB_ALLOCATED) { -+ ERROR("urb %p is in wrong state: %d", -+ urb, wrap_urb->state); -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_REQUEST_FAILED; -+ return NT_URB_STATUS(nt_urb); -+ } -+ wrap_urb->id = pre_atomic_add(urb_id, 1); -+#endif -+ DUMP_WRAP_URB(IRP_WRAP_URB(irp), USB_DIR_OUT); -+ irp->io_status.status = STATUS_PENDING; -+ irp->io_status.info = 0; -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_PENDING; -+ IoMarkIrpPending(irp); -+ DUMP_URB_BUFFER(urb, USB_DIR_OUT); -+ USBTRACE("%p", urb); -+ wrap_urb->state = URB_SUBMITTED; -+ ret = usb_submit_urb(urb, irql_gfp()); -+ if (ret) { -+ USBTRACE("ret: %d", ret); -+ wrap_free_urb(urb); -+ /* we assume that IRP was not in pending state before */ -+ IoUnmarkIrpPending(irp); -+ NT_URB_STATUS(nt_urb) = wrap_urb_status(ret); -+ USBEXIT(return NT_URB_STATUS(nt_urb)); -+ } else -+ USBEXIT(return USBD_STATUS_PENDING); -+} -+ -+static void wrap_urb_complete(struct urb *urb ISR_PT_REGS_PARAM_DECL) -+{ -+ struct irp *irp; -+ struct wrap_urb *wrap_urb; -+ -+ wrap_urb = urb->context; -+ USBTRACE("%p (%p) completed", wrap_urb, urb); -+ irp = wrap_urb->irp; -+ DUMP_WRAP_URB(wrap_urb, USB_DIR_IN); -+ irp->cancel_routine = NULL; -+#ifdef USB_DEBUG -+ if (wrap_urb->state != URB_SUBMITTED) { -+ WARNING("urb %p in wrong state: %d (%d)", urb, wrap_urb->state, -+ urb->status); -+ return; -+ } -+#endif -+ wrap_urb->state = URB_COMPLETED; -+ spin_lock(&wrap_urb_complete_list_lock); -+ InsertTailList(&wrap_urb_complete_list, &wrap_urb->complete_list); -+ spin_unlock(&wrap_urb_complete_list_lock); -+ queue_work(ntos_wq, &wrap_urb_complete_work); -+} -+ -+/* one worker for all devices */ -+static void wrap_urb_complete_worker(struct work_struct *dummy) -+{ -+ struct irp *irp; -+ struct urb *urb; -+ struct usbd_bulk_or_intr_transfer *bulk_int_tx; -+ struct usbd_vendor_or_class_request *vc_req; -+ union nt_urb *nt_urb; -+ struct wrap_urb *wrap_urb; -+ struct nt_list *ent; -+ unsigned long flags; -+ -+ USBENTER(""); -+ while (1) { -+ spin_lock_irqsave(&wrap_urb_complete_list_lock, flags); -+ ent = RemoveHeadList(&wrap_urb_complete_list); -+ spin_unlock_irqrestore(&wrap_urb_complete_list_lock, flags); -+ if (!ent) -+ break; -+ wrap_urb = container_of(ent, struct wrap_urb, complete_list); -+ urb = wrap_urb->urb; -+#ifdef USB_DEBUG -+ if (wrap_urb->state != URB_COMPLETED && -+ wrap_urb->state != URB_INT_UNLINKED) -+ WARNING("urb %p in wrong state: %d", -+ urb, wrap_urb->state); -+#endif -+ irp = wrap_urb->irp; -+ DUMP_IRP(irp); -+ nt_urb = IRP_URB(irp); -+ USBTRACE("urb: %p, nt_urb: %p, status: %d", -+ urb, nt_urb, urb->status); -+ switch (urb->status) { -+ case 0: -+ /* successfully transferred */ -+ irp->io_status.info = urb->actual_length; -+ if (nt_urb->header.function == -+ URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) { -+ bulk_int_tx = &nt_urb->bulk_int_transfer; -+ bulk_int_tx->transfer_buffer_length = -+ urb->actual_length; -+ DUMP_URB_BUFFER(urb, USB_DIR_IN); -+ if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) && -+ usb_pipein(urb->pipe)) -+ memcpy(bulk_int_tx->transfer_buffer, -+ urb->transfer_buffer, -+ urb->actual_length); -+ } else { // vendor or class request -+ vc_req = &nt_urb->vendor_class_request; -+ vc_req->transfer_buffer_length = -+ urb->actual_length; -+ DUMP_URB_BUFFER(urb, USB_DIR_IN); -+ if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) && -+ usb_pipein(urb->pipe)) -+ memcpy(vc_req->transfer_buffer, -+ urb->transfer_buffer, -+ urb->actual_length); -+ } -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS; -+ irp->io_status.status = STATUS_SUCCESS; -+ break; -+ case -ENOENT: -+ case -ECONNRESET: -+ /* urb canceled */ -+ irp->io_status.info = 0; -+ TRACE2("urb %p canceled", urb); -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS; -+ irp->io_status.status = STATUS_CANCELLED; -+ break; -+ default: -+ TRACE2("irp: %p, urb: %p, status: %d/%d", -+ irp, urb, urb->status, wrap_urb->state); -+ irp->io_status.info = 0; -+ NT_URB_STATUS(nt_urb) = wrap_urb_status(urb->status); -+ irp->io_status.status = -+ nt_urb_irp_status(NT_URB_STATUS(nt_urb)); -+ break; -+ } -+ wrap_free_urb(urb); -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ } -+ USBEXIT(return); -+} -+ -+static USBD_STATUS wrap_bulk_or_intr_trans(struct irp *irp) -+{ -+ struct usb_endpoint_descriptor *pipe_handle; -+ struct urb *urb; -+ unsigned int pipe; -+ struct usbd_bulk_or_intr_transfer *bulk_int_tx; -+ USBD_STATUS status; -+ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); -+ struct usb_device *udev = wd->usb.udev; -+ union nt_urb *nt_urb = IRP_URB(irp); -+ -+ bulk_int_tx = &nt_urb->bulk_int_transfer; -+ pipe_handle = bulk_int_tx->pipe_handle; -+ USBTRACE("flags: 0x%x, length: %u, buffer: %p, handle: %p", -+ bulk_int_tx->transfer_flags, -+ bulk_int_tx->transfer_buffer_length, -+ bulk_int_tx->transfer_buffer, pipe_handle); -+ -+ if (USBD_IS_BULK_PIPE(pipe_handle)) { -+ if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN) -+ pipe = usb_rcvbulkpipe(udev, -+ pipe_handle->bEndpointAddress); -+ else -+ pipe = usb_sndbulkpipe(udev, -+ pipe_handle->bEndpointAddress); -+ } else { -+ if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN) -+ pipe = usb_rcvintpipe(udev, -+ pipe_handle->bEndpointAddress); -+ else -+ pipe = usb_sndintpipe(udev, -+ pipe_handle->bEndpointAddress); -+ } -+ -+ DUMP_IRP(irp); -+ urb = wrap_alloc_urb(irp, pipe, bulk_int_tx->transfer_buffer, -+ bulk_int_tx->transfer_buffer_length); -+ if (!urb) { -+ ERROR("couldn't allocate urb"); -+ return USBD_STATUS_NO_MEMORY; -+ } -+ if (usb_pipein(pipe) && -+ (!(bulk_int_tx->transfer_flags & USBD_SHORT_TRANSFER_OK))) { -+ USBTRACE("short not ok"); -+ urb->transfer_flags |= URB_SHORT_NOT_OK; -+ } -+ if (usb_pipebulk(pipe)) { -+ usb_fill_bulk_urb(urb, udev, pipe, urb->transfer_buffer, -+ bulk_int_tx->transfer_buffer_length, -+ wrap_urb_complete, urb->context); -+ USBTRACE("submitting bulk urb %p on pipe 0x%x (ep 0x%x)", -+ urb, urb->pipe, pipe_handle->bEndpointAddress); -+ } else { -+ usb_fill_int_urb(urb, udev, pipe, urb->transfer_buffer, -+ bulk_int_tx->transfer_buffer_length, -+ wrap_urb_complete, urb->context, -+ pipe_handle->bInterval); -+ USBTRACE("submitting interrupt urb %p on pipe 0x%x (ep 0x%x), " -+ "intvl: %d", urb, urb->pipe, -+ pipe_handle->bEndpointAddress, pipe_handle->bInterval); -+ } -+ status = wrap_submit_urb(irp); -+ USBTRACE("status: %08X", status); -+ USBEXIT(return status); -+} -+ -+static USBD_STATUS wrap_vendor_or_class_req(struct irp *irp) -+{ -+ u8 req_type; -+ unsigned int pipe; -+ struct usbd_vendor_or_class_request *vc_req; -+ USBD_STATUS status; -+ struct urb *urb; -+ struct usb_ctrlrequest *dr; -+ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); -+ struct usb_device *udev = wd->usb.udev; -+ union nt_urb *nt_urb = IRP_URB(irp); -+ -+ vc_req = &nt_urb->vendor_class_request; -+ USBTRACE("bits: %x, req: %x, val: %08x, index: %08x, flags: %x," -+ "buf: %p, len: %d", vc_req->reserved_bits, vc_req->request, -+ vc_req->value, vc_req->index, vc_req->transfer_flags, -+ vc_req->transfer_buffer, vc_req->transfer_buffer_length); -+ -+ USBTRACE("%x", nt_urb->header.function); -+ switch (nt_urb->header.function) { -+ case URB_FUNCTION_VENDOR_DEVICE: -+ req_type = USB_TYPE_VENDOR | USB_RECIP_DEVICE; -+ break; -+ case URB_FUNCTION_VENDOR_INTERFACE: -+ req_type = USB_TYPE_VENDOR | USB_RECIP_INTERFACE; -+ break; -+ case URB_FUNCTION_VENDOR_ENDPOINT: -+ req_type = USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; -+ break; -+ case URB_FUNCTION_VENDOR_OTHER: -+ req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER; -+ break; -+ case URB_FUNCTION_CLASS_DEVICE: -+ req_type = USB_TYPE_CLASS | USB_RECIP_DEVICE; -+ break; -+ case URB_FUNCTION_CLASS_INTERFACE: -+ req_type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; -+ break; -+ case URB_FUNCTION_CLASS_ENDPOINT: -+ req_type = USB_TYPE_CLASS | USB_RECIP_ENDPOINT; -+ break; -+ case URB_FUNCTION_CLASS_OTHER: -+ req_type = USB_TYPE_CLASS | USB_RECIP_OTHER; -+ break; -+ default: -+ ERROR("unknown request type: %x", nt_urb->header.function); -+ req_type = 0; -+ break; -+ } -+ -+ req_type |= vc_req->reserved_bits; -+ USBTRACE("req type: %08x", req_type); -+ -+ if (vc_req->transfer_flags & USBD_TRANSFER_DIRECTION_IN) { -+ pipe = usb_rcvctrlpipe(udev, 0); -+ req_type |= USB_DIR_IN; -+ USBTRACE("pipe: %x, dir in", pipe); -+ } else { -+ pipe = usb_sndctrlpipe(udev, 0); -+ req_type |= USB_DIR_OUT; -+ USBTRACE("pipe: %x, dir out", pipe); -+ } -+ urb = wrap_alloc_urb(irp, pipe, vc_req->transfer_buffer, -+ vc_req->transfer_buffer_length); -+ if (!urb) { -+ ERROR("couldn't allocate urb"); -+ return USBD_STATUS_NO_MEMORY; -+ } -+ -+ if (usb_pipein(pipe) && -+ (!(vc_req->transfer_flags & USBD_SHORT_TRANSFER_OK))) { -+ USBTRACE("short not ok"); -+ urb->transfer_flags |= URB_SHORT_NOT_OK; -+ } -+ -+ dr = kzalloc(sizeof(*dr), irql_gfp()); -+ if (!dr) { -+ ERROR("couldn't allocate memory"); -+ wrap_free_urb(urb); -+ return USBD_STATUS_NO_MEMORY; -+ } -+ dr->bRequestType = req_type; -+ dr->bRequest = vc_req->request; -+ dr->wValue = cpu_to_le16(vc_req->value); -+ dr->wIndex = cpu_to_le16((u16)vc_req->index); -+ dr->wLength = cpu_to_le16((u16)urb->transfer_buffer_length); -+ -+ usb_fill_control_urb(urb, udev, pipe, (unsigned char *)dr, -+ urb->transfer_buffer, urb->transfer_buffer_length, -+ wrap_urb_complete, urb->context); -+ status = wrap_submit_urb(irp); -+ USBTRACE("status: %08X", status); -+ USBEXIT(return status); -+} -+ -+static USBD_STATUS wrap_reset_pipe(struct usb_device *udev, struct irp *irp) -+{ -+ int ret; -+ union nt_urb *nt_urb; -+ struct usb_endpoint_descriptor *pipe_handle; -+ unsigned int pipe1, pipe2; -+ -+ nt_urb = IRP_URB(irp); -+ pipe_handle = nt_urb->pipe_req.pipe_handle; -+ /* TODO: not clear if both directions should be cleared? */ -+ if (USBD_IS_BULK_PIPE(pipe_handle)) { -+ pipe1 = usb_rcvbulkpipe(udev, pipe_handle->bEndpointAddress); -+ pipe2 = usb_sndbulkpipe(udev, pipe_handle->bEndpointAddress); -+ } else if (USBD_IS_INT_PIPE(pipe_handle)) { -+ pipe1 = usb_rcvintpipe(udev, pipe_handle->bEndpointAddress); -+ pipe2 = pipe1; -+ } else { -+ WARNING("invalid pipe %d", pipe_handle->bEndpointAddress); -+ return USBD_STATUS_INVALID_PIPE_HANDLE; -+ } -+ USBTRACE("ep: %d, pipe: 0x%x", pipe_handle->bEndpointAddress, pipe1); -+ ret = usb_clear_halt(udev, pipe1); -+ if (ret) -+ USBTRACE("resetting pipe %d failed: %d", pipe1, ret); -+ if (pipe2 != pipe1) { -+ ret = usb_clear_halt(udev, pipe2); -+ if (ret) -+ USBTRACE("resetting pipe %d failed: %d", pipe2, ret); -+ } -+// return wrap_urb_status(ret); -+ return USBD_STATUS_SUCCESS; -+} -+ -+static USBD_STATUS wrap_abort_pipe(struct usb_device *udev, struct irp *irp) -+{ -+ union nt_urb *nt_urb; -+ struct usb_endpoint_descriptor *pipe_handle; -+ struct wrap_urb *wrap_urb; -+ struct wrap_device *wd; -+ KIRQL irql; -+ -+ wd = IRP_WRAP_DEVICE(irp); -+ nt_urb = IRP_URB(irp); -+ pipe_handle = nt_urb->pipe_req.pipe_handle; -+ USBENTER("%p, %x", irp, pipe_handle->bEndpointAddress); -+ IoAcquireCancelSpinLock(&irql); -+ nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) { -+ USBTRACE("%p, %p, %d, %x, %x", wrap_urb, wrap_urb->urb, -+ wrap_urb->state, wrap_urb->urb->pipe, -+ usb_pipeendpoint(wrap_urb->urb->pipe)); -+ /* for WG111T driver, urbs for endpoint 0 should also -+ * be canceled */ -+ if ((usb_pipeendpoint(wrap_urb->urb->pipe) == -+ pipe_handle->bEndpointAddress) || -+ (usb_pipeendpoint(wrap_urb->urb->pipe) == 0)) { -+ if (wrap_cancel_urb(wrap_urb) == 0) -+ USBTRACE("canceled wrap_urb: %p", wrap_urb); -+ } -+ } -+ IoReleaseCancelSpinLock(irql); -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_CANCELED; -+ USBEXIT(return USBD_STATUS_SUCCESS); -+} -+ -+static USBD_STATUS wrap_set_clear_feature(struct usb_device *udev, -+ struct irp *irp) -+{ -+ union nt_urb *nt_urb; -+ struct urb_control_feature_request *feat_req; -+ int ret = 0; -+ __u8 request, type; -+ __u16 feature; -+ -+ nt_urb = IRP_URB(irp); -+ feat_req = &nt_urb->feat_req; -+ feature = feat_req->feature_selector; -+ switch (nt_urb->header.function) { -+ case URB_FUNCTION_SET_FEATURE_TO_DEVICE: -+ request = USB_REQ_SET_FEATURE; -+ type = USB_DT_DEVICE; -+ break; -+ case URB_FUNCTION_SET_FEATURE_TO_INTERFACE: -+ request = USB_REQ_SET_FEATURE; -+ type = USB_DT_INTERFACE; -+ break; -+ case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT: -+ request = USB_REQ_SET_FEATURE; -+ type = USB_DT_ENDPOINT; -+ break; -+ case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE: -+ request = USB_REQ_CLEAR_FEATURE; -+ type = USB_DT_DEVICE; -+ break; -+ case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE: -+ request = USB_REQ_CLEAR_FEATURE; -+ type = USB_DT_INTERFACE; -+ break; -+ case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT: -+ request = USB_REQ_CLEAR_FEATURE; -+ type = USB_DT_ENDPOINT; -+ break; -+ default: -+ WARNING("invalid function: %x", nt_urb->header.function); -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED; -+ return NT_URB_STATUS(nt_urb); -+ } -+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), request, type, -+ feature, feat_req->index, NULL, 0, 1000); -+ NT_URB_STATUS(nt_urb) = wrap_urb_status(ret); -+ USBEXIT(return NT_URB_STATUS(nt_urb)); -+} -+ -+static USBD_STATUS wrap_get_status_request(struct usb_device *udev, -+ struct irp *irp) -+{ -+ union nt_urb *nt_urb; -+ struct urb_control_get_status_request *status_req; -+ int ret = 0; -+ __u8 type; -+ -+ nt_urb = IRP_URB(irp); -+ status_req = &nt_urb->status_req; -+ switch (nt_urb->header.function) { -+ case URB_FUNCTION_GET_STATUS_FROM_DEVICE: -+ type = USB_RECIP_DEVICE; -+ break; -+ case URB_FUNCTION_GET_STATUS_FROM_INTERFACE: -+ type = USB_RECIP_INTERFACE; -+ break; -+ case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT: -+ type = USB_RECIP_ENDPOINT; -+ break; -+ default: -+ WARNING("invalid function: %x", nt_urb->header.function); -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED; -+ return NT_URB_STATUS(nt_urb); -+ } -+ assert(status_req->transfer_buffer_length == sizeof(u16)); -+ ret = usb_get_status(udev, type, status_req->index, -+ status_req->transfer_buffer); -+ if (ret >= 0) { -+ assert(ret <= status_req->transfer_buffer_length); -+ status_req->transfer_buffer_length = ret; -+ NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS; -+ } else -+ NT_URB_STATUS(nt_urb) = wrap_urb_status(ret); -+ USBEXIT(return NT_URB_STATUS(nt_urb)); -+} -+ -+static void set_intf_pipe_info(struct wrap_device *wd, -+ struct usb_interface *usb_intf, -+ struct usbd_interface_information *intf) -+{ -+ int i; -+ struct usb_endpoint_descriptor *ep; -+ struct usbd_pipe_information *pipe; -+ -+ for (i = 0; i < CUR_ALT_SETTING(usb_intf)->desc.bNumEndpoints; i++) { -+ ep = &(CUR_ALT_SETTING(usb_intf)->endpoint[i]).desc; -+ if (i >= intf->bNumEndpoints) { -+ ERROR("intf %p has only %d endpoints, " -+ "ignoring endpoints above %d", -+ intf, intf->bNumEndpoints, i); -+ break; -+ } -+ pipe = &intf->pipes[i]; -+ -+ if (pipe->flags & USBD_PF_CHANGE_MAX_PACKET) -+ USBTRACE("pkt_sz: %d: %d", pipe->wMaxPacketSize, -+ pipe->max_tx_size); -+ USBTRACE("driver wants max_tx_size to %d", -+ pipe->max_tx_size); -+ -+ pipe->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); -+ pipe->bEndpointAddress = ep->bEndpointAddress; -+ pipe->type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; -+ if (pipe->type == UsbdPipeTypeInterrupt) { -+ /* Windows and Linux differ in how the -+ * bInterval is interpreted */ -+ /* for low speed: -+ interval (Windows) -> frames per ms (Linux) -+ 0 to 15 -> 8 -+ 16 to 35 -> 16 -+ 36 to 255 -> 32 -+ -+ for full speed: interval -> frames per ms -+ 1 -> 1 -+ 2 to 3 -> 2 -+ 4 to 7 -> 4 -+ 8 to 15 -> 8 -+ 16 to 31 -> 16 -+ 32 to 255 -> 32 -+ -+ for high speed: interval -> microframes -+ 1 -> 1 -+ 2 -> 2 -+ 3 -> 4 -+ 4 -> 8 -+ 5 -> 16 -+ 6 -> 32 -+ 7 to 255 -> 32 -+ */ -+ if (wd->usb.udev->speed == USB_SPEED_LOW) -+ pipe->bInterval = ep->bInterval + 5; -+ else if (wd->usb.udev->speed == USB_SPEED_FULL) -+ pipe->bInterval = ep->bInterval; -+ else { -+ int j, k; -+ for (j = k = 1; j < ep->bInterval; k++) -+ j *= 2; -+ pipe->bInterval = k; -+ } -+ } -+ pipe->handle = ep; -+ USBTRACE("%d: ep 0x%x, type %d, pkt_sz %d, intv %d (%d)," -+ "type: %d, handle %p", i, ep->bEndpointAddress, -+ ep->bmAttributes, pipe->wMaxPacketSize, ep->bInterval, -+ pipe->bInterval, pipe->type, pipe->handle); -+ } -+} -+ -+static USBD_STATUS wrap_select_configuration(struct wrap_device *wd, -+ union nt_urb *nt_urb, -+ struct irp *irp) -+{ -+ int i, ret; -+ struct usbd_select_configuration *sel_conf; -+ struct usb_device *udev; -+ struct usbd_interface_information *intf; -+ struct usb_config_descriptor *config; -+ struct usb_interface *usb_intf; -+ -+ udev = wd->usb.udev; -+ sel_conf = &nt_urb->select_conf; -+ config = sel_conf->config; -+ USBTRACE("%p", config); -+ if (config == NULL) { -+ kill_all_urbs(wd, 1); -+ ret = usb_reset_configuration(udev); -+ return wrap_urb_status(ret); -+ } -+ -+ USBTRACE("conf: %d, type: %d, length: %d, numif: %d, attr: %08x", -+ config->bConfigurationValue, config->bDescriptorType, -+ config->wTotalLength, config->bNumInterfaces, -+ config->bmAttributes); -+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), -+ USB_REQ_SET_CONFIGURATION, 0, -+ config->bConfigurationValue, 0, -+ NULL, 0, USB_CTRL_SET_TIMEOUT); -+ if (ret < 0) { -+ ERROR("ret: %d", ret); -+ return wrap_urb_status(ret); -+ } -+ sel_conf->handle = udev->actconfig; -+ intf = &sel_conf->intf; -+ for (i = 0; i < config->bNumInterfaces && intf->bLength > 0; -+ i++, intf = (((void *)intf) + intf->bLength)) { -+ -+ USBTRACE("intf: %d, alt setting: %d", -+ intf->bInterfaceNumber, intf->bAlternateSetting); -+ ret = usb_set_interface(udev, intf->bInterfaceNumber, -+ intf->bAlternateSetting); -+ if (ret < 0) { -+ ERROR("failed with %d", ret); -+ return wrap_urb_status(ret); -+ } -+ usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber); -+ if (!usb_intf) { -+ ERROR("couldn't obtain ifnum"); -+ return USBD_STATUS_REQUEST_FAILED; -+ } -+ USBTRACE("intf: %p, num ep: %d", intf, intf->bNumEndpoints); -+ set_intf_pipe_info(wd, usb_intf, intf); -+ } -+ return USBD_STATUS_SUCCESS; -+} -+ -+static USBD_STATUS wrap_select_interface(struct wrap_device *wd, -+ union nt_urb *nt_urb, -+ struct irp *irp) -+{ -+ int ret; -+ struct usbd_select_interface *sel_intf; -+ struct usb_device *udev; -+ struct usbd_interface_information *intf; -+ struct usb_interface *usb_intf; -+ -+ udev = wd->usb.udev; -+ sel_intf = &nt_urb->select_intf; -+ intf = &sel_intf->intf; -+ -+ ret = usb_set_interface(udev, intf->bInterfaceNumber, -+ intf->bAlternateSetting); -+ if (ret < 0) { -+ ERROR("failed with %d", ret); -+ return wrap_urb_status(ret); -+ } -+ usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber); -+ if (!usb_intf) { -+ ERROR("couldn't get interface information"); -+ return USBD_STATUS_REQUEST_FAILED; -+ } -+ USBTRACE("intf: %p, num ep: %d", usb_intf, intf->bNumEndpoints); -+ set_intf_pipe_info(wd, usb_intf, intf); -+ return USBD_STATUS_SUCCESS; -+} -+ -+static int wrap_usb_get_string(struct usb_device *udev, unsigned short langid, -+ unsigned char index, void *buf, int size) -+{ -+ int i, ret; -+ /* if langid is 0, return array of languages supported in -+ * buf */ -+ for (i = 0; i < 3; i++) { -+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), -+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, -+ (USB_DT_STRING << 8) + index, langid, -+ buf, size, USB_CTRL_GET_TIMEOUT); -+ if (ret > 0 || ret == -EPIPE) -+ break; -+ } -+ return ret; -+} -+ -+static USBD_STATUS wrap_get_descriptor(struct wrap_device *wd, -+ union nt_urb *nt_urb, struct irp *irp) -+{ -+ struct usbd_control_descriptor_request *control_desc; -+ int ret = 0; -+ struct usb_device *udev; -+ -+ udev = wd->usb.udev; -+ control_desc = &nt_urb->control_desc; -+ USBTRACE("desctype = %d, descindex = %d, transfer_buffer = %p," -+ "transfer_buffer_length = %d", control_desc->desc_type, -+ control_desc->index, control_desc->transfer_buffer, -+ control_desc->transfer_buffer_length); -+ -+ if (control_desc->desc_type == USB_DT_STRING) { -+ USBTRACE("langid: %x", control_desc->language_id); -+ ret = wrap_usb_get_string(udev, control_desc->language_id, -+ control_desc->index, -+ control_desc->transfer_buffer, -+ control_desc->transfer_buffer_length); -+ } else { -+ ret = usb_get_descriptor(udev, control_desc->desc_type, -+ control_desc->index, -+ control_desc->transfer_buffer, -+ control_desc->transfer_buffer_length); -+ } -+ if (ret < 0) { -+ USBTRACE("request %d failed: %d", control_desc->desc_type, ret); -+ control_desc->transfer_buffer_length = 0; -+ return wrap_urb_status(ret); -+ } else { -+ USBTRACE("ret: %08x", ret); -+ control_desc->transfer_buffer_length = ret; -+ irp->io_status.info = ret; -+ return USBD_STATUS_SUCCESS; -+ } -+} -+ -+static USBD_STATUS wrap_process_nt_urb(struct irp *irp) -+{ -+ USBD_STATUS status; -+ struct wrap_device *wd = IRP_WRAP_DEVICE(irp); -+ struct usb_device *udev = wd->usb.udev; -+ union nt_urb *nt_urb = IRP_URB(irp); -+ -+ USBENTER("nt_urb = %p, irp = %p, length = %d, function = %x", -+ nt_urb, irp, nt_urb->header.length, nt_urb->header.function); -+ -+ if (test_bit(HW_DISABLED, &wd->hw_status)) { -+ status = USBD_STATUS_DEVICE_GONE; -+ NT_URB_STATUS(nt_urb) = status; -+ return status; -+ } -+ -+ DUMP_IRP(irp); -+ switch (nt_urb->header.function) { -+ /* bulk/int and vendor/class urbs are submitted to -+ * Linux USB core; if the call is successful, urb's -+ * completion worker will return IRP later */ -+ case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: -+ USBTRACE("submitting bulk/int irp: %p", irp); -+ status = wrap_bulk_or_intr_trans(irp); -+ break; -+ -+ case URB_FUNCTION_VENDOR_DEVICE: -+ case URB_FUNCTION_VENDOR_INTERFACE: -+ case URB_FUNCTION_VENDOR_ENDPOINT: -+ case URB_FUNCTION_VENDOR_OTHER: -+ case URB_FUNCTION_CLASS_DEVICE: -+ case URB_FUNCTION_CLASS_INTERFACE: -+ case URB_FUNCTION_CLASS_ENDPOINT: -+ case URB_FUNCTION_CLASS_OTHER: -+ USBTRACE("submitting vendor/class irp: %p", irp); -+ status = wrap_vendor_or_class_req(irp); -+ break; -+ -+ /* rest are synchronous */ -+ case URB_FUNCTION_SELECT_CONFIGURATION: -+ status = wrap_select_configuration(wd, nt_urb, irp); -+ NT_URB_STATUS(nt_urb) = status; -+ break; -+ -+ case URB_FUNCTION_SELECT_INTERFACE: -+ status = wrap_select_interface(wd, nt_urb, irp); -+ NT_URB_STATUS(nt_urb) = status; -+ break; -+ -+ case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: -+ status = wrap_get_descriptor(wd, nt_urb, irp); -+ NT_URB_STATUS(nt_urb) = status; -+ break; -+ -+ case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: -+ status = wrap_reset_pipe(udev, irp); -+ NT_URB_STATUS(nt_urb) = status; -+ break; -+ -+ case URB_FUNCTION_ABORT_PIPE: -+ status = wrap_abort_pipe(udev, irp); -+ break; -+ -+ case URB_FUNCTION_SET_FEATURE_TO_DEVICE: -+ case URB_FUNCTION_SET_FEATURE_TO_INTERFACE: -+ case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT: -+ case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE: -+ case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE: -+ case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT: -+ status = wrap_set_clear_feature(udev, irp); -+ break; -+ -+ case URB_FUNCTION_GET_STATUS_FROM_DEVICE: -+ case URB_FUNCTION_GET_STATUS_FROM_INTERFACE: -+ case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT: -+ status = wrap_get_status_request(udev, irp); -+ break; -+ -+ default: -+ ERROR("function %x not implemented", nt_urb->header.function); -+ status = NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED; -+ break; -+ } -+ USBTRACE("status: %08X", status); -+ return status; -+} -+ -+static USBD_STATUS wrap_reset_port(struct irp *irp) -+{ -+ int ret, lock = 0; -+ struct wrap_device *wd; -+ -+ wd = IRP_WRAP_DEVICE(irp); -+ USBENTER("%p, %p", wd, wd->usb.udev); -+ lock = usb_lock_device_for_reset(wd->usb.udev, wd->usb.intf); -+ if (lock < 0) { -+ WARNING("locking failed: %d", lock); -+// return wrap_urb_status(lock); -+ return USBD_STATUS_SUCCESS; -+ } -+ ret = usb_reset_device(wd->usb.udev); -+ if (ret < 0) -+ USBTRACE("reset failed: %d", ret); -+ /* TODO: should reconfigure? */ -+ if (lock) -+ usb_unlock_device(wd->usb.udev); -+// return wrap_urb_status(ret); -+ return USBD_STATUS_SUCCESS; -+} -+ -+static USBD_STATUS wrap_get_port_status(struct irp *irp) -+{ -+ struct wrap_device *wd; -+ ULONG *status; -+ enum usb_device_state state; -+ -+ wd = IRP_WRAP_DEVICE(irp); -+ USBENTER("%p, %p", wd, wd->usb.udev); -+ status = IoGetCurrentIrpStackLocation(irp)->params.others.arg1; -+ state = wd->usb.udev->state; -+ if (state != USB_STATE_NOTATTACHED && -+ state != USB_STATE_SUSPENDED) { -+ *status |= USBD_PORT_CONNECTED; -+ if (state == USB_STATE_CONFIGURED) -+ *status |= USBD_PORT_ENABLED; -+ } -+ USBTRACE("state: %d, *status: %08X", state, *status); -+ return USBD_STATUS_SUCCESS; -+} -+ -+NTSTATUS wrap_submit_irp(struct device_object *pdo, struct irp *irp) -+{ -+ struct io_stack_location *irp_sl; -+ struct wrap_device *wd; -+ USBD_STATUS status; -+ struct usbd_idle_callback *idle_callback; -+ -+ USBENTER("%p, %p", pdo, irp); -+ wd = pdo->reserved; -+ if (wd->usb.intf == NULL) { -+ USBTRACE("%p", irp); -+ irp->io_status.status = STATUS_DEVICE_REMOVED; -+ irp->io_status.info = 0; -+ USBEXIT(return STATUS_DEVICE_REMOVED); -+ } -+ IRP_WRAP_DEVICE(irp) = wd; -+ irp_sl = IoGetCurrentIrpStackLocation(irp); -+ switch (irp_sl->params.dev_ioctl.code) { -+ case IOCTL_INTERNAL_USB_SUBMIT_URB: -+ status = wrap_process_nt_urb(irp); -+ break; -+ case IOCTL_INTERNAL_USB_RESET_PORT: -+ status = wrap_reset_port(irp); -+ break; -+ case IOCTL_INTERNAL_USB_GET_PORT_STATUS: -+ status = wrap_get_port_status(irp); -+ break; -+ case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: -+ idle_callback = irp_sl->params.dev_ioctl.type3_input_buf; -+ (void)idle_callback; -+ USBTRACE("suspend function: %p", idle_callback->callback); -+ status = USBD_STATUS_NOT_SUPPORTED; -+ break; -+ default: -+ ERROR("ioctl %08X NOT IMPLEMENTED", -+ irp_sl->params.dev_ioctl.code); -+ status = USBD_STATUS_NOT_SUPPORTED; -+ break; -+ } -+ -+ USBTRACE("status: %08X", status); -+ if (status == USBD_STATUS_PENDING) { -+ /* don't touch this IRP - it may have been already -+ * completed/returned */ -+ return STATUS_PENDING; -+ } else { -+ irp->io_status.status = nt_urb_irp_status(status); -+ if (status != USBD_STATUS_SUCCESS) -+ irp->io_status.info = 0; -+ USBEXIT(return irp->io_status.status); -+ } -+} -+ -+/* TODO: The example on msdn in reference section suggests that second -+ * argument should be an array of usbd_interface_information, but -+ * description and examples elsewhere suggest that it should be -+ * usbd_interface_list_entry structre. Which is correct? */ -+ -+wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequestEx,2) -+ (struct usb_config_descriptor *config, -+ struct usbd_interface_list_entry *intf_list) -+{ -+ int size, i, n; -+ struct usbd_interface_information *intf; -+ struct usbd_pipe_information *pipe; -+ struct usb_interface_descriptor *intf_desc; -+ struct usbd_select_configuration *select_conf; -+ -+ USBENTER("config = %p, intf_list = %p", config, intf_list); -+ -+ /* calculate size required; select_conf already has space for -+ * one intf structure */ -+ size = sizeof(*select_conf) - sizeof(*intf); -+ for (n = 0; n < config->bNumInterfaces; n++) { -+ i = intf_list[n].intf_desc->bNumEndpoints; -+ /* intf already has space for one pipe */ -+ size += sizeof(*intf) + (i - 1) * sizeof(*pipe); -+ } -+ /* don't use kmalloc - driver frees it with ExFreePool */ -+ select_conf = ExAllocatePoolWithTag(NonPagedPool, size, -+ POOL_TAG('L', 'U', 'S', 'B')); -+ if (!select_conf) { -+ WARNING("couldn't allocate memory"); -+ return NULL; -+ } -+ memset(select_conf, 0, size); -+ intf = &select_conf->intf; -+ select_conf->handle = config; -+ for (n = 0; n < config->bNumInterfaces && intf_list[n].intf_desc; n++) { -+ /* initialize 'intf' fields in intf_list so they point -+ * to appropriate entry; these may be read/written by -+ * driver after this function returns */ -+ intf_list[n].intf = intf; -+ intf_desc = intf_list[n].intf_desc; -+ -+ i = intf_desc->bNumEndpoints; -+ intf->bLength = sizeof(*intf) + (i - 1) * sizeof(*pipe); -+ -+ intf->bInterfaceNumber = intf_desc->bInterfaceNumber; -+ intf->bAlternateSetting = intf_desc->bAlternateSetting; -+ intf->bInterfaceClass = intf_desc->bInterfaceClass; -+ intf->bInterfaceSubClass = intf_desc->bInterfaceSubClass; -+ intf->bInterfaceProtocol = intf_desc->bInterfaceProtocol; -+ intf->bNumEndpoints = intf_desc->bNumEndpoints; -+ -+ pipe = &intf->pipes[0]; -+ for (i = 0; i < intf->bNumEndpoints; i++) { -+ memset(&pipe[i], 0, sizeof(*pipe)); -+ pipe[i].max_tx_size = -+ USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; -+ } -+ intf->handle = intf_desc; -+ intf = (((void *)intf) + intf->bLength); -+ } -+ select_conf->header.function = URB_FUNCTION_SELECT_CONFIGURATION; -+ select_conf->header.length = size; -+ select_conf->config = config; -+ USBEXIT(return (union nt_urb *)select_conf); -+} -+ -+WIN_SYMBOL_MAP("_USBD_CreateConfigurationRequestEx@8", USBD_CreateConfigurationRequestEx) -+ -+wstdcall struct usb_interface_descriptor * -+WIN_FUNC(USBD_ParseConfigurationDescriptorEx,7) -+ (struct usb_config_descriptor *config, void *start, -+ LONG bInterfaceNumber, LONG bAlternateSetting, LONG bInterfaceClass, -+ LONG bInterfaceSubClass, LONG bInterfaceProtocol) -+{ -+ void *pos; -+ struct usb_interface_descriptor *intf; -+ -+ USBENTER("config = %p, start = %p, ifnum = %d, alt_setting = %d," -+ " class = %d, subclass = %d, proto = %d", config, start, -+ bInterfaceNumber, bAlternateSetting, bInterfaceClass, -+ bInterfaceSubClass, bInterfaceProtocol); -+ -+ for (pos = start; -+ pos < ((void *)config + le16_to_cpu(config->wTotalLength)); -+ pos += intf->bLength) { -+ -+ intf = pos; -+ -+ if ((intf->bDescriptorType == USB_DT_INTERFACE) && -+ ((bInterfaceNumber == -1) || -+ (intf->bInterfaceNumber == bInterfaceNumber)) && -+ ((bAlternateSetting == -1) || -+ (intf->bAlternateSetting == bAlternateSetting)) && -+ ((bInterfaceClass == -1) || -+ (intf->bInterfaceClass == bInterfaceClass)) && -+ ((bInterfaceSubClass == -1) || -+ (intf->bInterfaceSubClass == bInterfaceSubClass)) && -+ ((bInterfaceProtocol == -1) || -+ (intf->bInterfaceProtocol == bInterfaceProtocol))) { -+ USBTRACE("selected interface = %p", intf); -+ USBEXIT(return intf); -+ } -+ } -+ USBEXIT(return NULL); -+} -+ -+WIN_SYMBOL_MAP("_USBD_ParseConfigurationDescriptorEx@28", USBD_ParseConfigurationDescriptorEx) -+ -+wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequest,2) -+ (struct usb_config_descriptor *config, USHORT *size) -+{ -+ union nt_urb *nt_urb; -+ struct usbd_interface_list_entry intf_list[2]; -+ struct usb_interface_descriptor *intf_desc; -+ -+ USBENTER("config = %p, urb_size = %p", config, size); -+ -+ intf_desc = USBD_ParseConfigurationDescriptorEx(config, config, -1, -1, -+ -1, -1, -1); -+ intf_list[0].intf_desc = intf_desc; -+ intf_list[0].intf = NULL; -+ intf_list[1].intf_desc = NULL; -+ intf_list[1].intf = NULL; -+ nt_urb = USBD_CreateConfigurationRequestEx(config, intf_list); -+ if (!nt_urb) -+ return NULL; -+ -+ *size = nt_urb->select_conf.header.length; -+ USBEXIT(return nt_urb); -+} -+ -+wstdcall struct usb_interface_descriptor * -+WIN_FUNC(USBD_ParseConfigurationDescriptor,3) -+ (struct usb_config_descriptor *config, UCHAR bInterfaceNumber, -+ UCHAR bAlternateSetting) -+{ -+ return USBD_ParseConfigurationDescriptorEx(config, config, -+ bInterfaceNumber, -+ bAlternateSetting, -+ -1, -1, -1); -+} -+ -+wstdcall struct usb_descriptor_header *WIN_FUNC(USBD_ParseDescriptors,4) -+ (void *buf, ULONG length, struct usb_descriptor_header *descr, -+ LONG type) -+{ -+ while ((void *)descr < buf + length) { -+ if (descr->bDescriptorType == type) -+ return descr; -+ if (descr->bLength == 0) -+ break; -+ descr = (void *)descr + descr->bLength; -+ } -+ USBEXIT(return NULL); -+} -+ -+WIN_SYMBOL_MAP("_USBD_ParseDescriptors@16", USBD_ParseDescriptors) -+ -+wstdcall void WIN_FUNC(USBD_GetUSBDIVersion,1) -+ (struct usbd_version_info *version_info) -+{ -+ /* this function is obsolete in Windows XP */ -+ if (version_info) { -+ version_info->usbdi_version = USBDI_VERSION_XP; -+ /* TODO: how do we get this correctly? */ -+ version_info->supported_usb_version = 0x110; -+ } -+ USBEXIT(return); -+} -+ -+wstdcall void -+USBD_InterfaceGetUSBDIVersion(void *context, -+ struct usbd_version_info *version_info, -+ ULONG *hcd_capa) -+{ -+ struct wrap_device *wd = context; -+ -+ if (version_info) { -+ version_info->usbdi_version = USBDI_VERSION_XP; -+ if (wd->usb.udev->speed == USB_SPEED_HIGH) -+ version_info->supported_usb_version = 0x200; -+ else -+ version_info->supported_usb_version = 0x110; -+ } -+ *hcd_capa = USB_HCD_CAPS_SUPPORTS_RT_THREADS; -+ USBEXIT(return); -+} -+ -+wstdcall BOOLEAN USBD_InterfaceIsDeviceHighSpeed(void *context) -+{ -+ struct wrap_device *wd = context; -+ -+ USBTRACE("wd: %p", wd); -+ if (wd->usb.udev->speed == USB_SPEED_HIGH) -+ USBEXIT(return TRUE); -+ else -+ USBEXIT(return FALSE); -+} -+ -+wstdcall void USBD_InterfaceReference(void *context) -+{ -+ USBTRACE("%p", context); -+ TODO(); -+} -+ -+wstdcall void USBD_InterfaceDereference(void *context) -+{ -+ USBTRACE("%p", context); -+ TODO(); -+} -+ -+wstdcall NTSTATUS USBD_InterfaceQueryBusTime(void *context, ULONG *frame) -+{ -+ struct wrap_device *wd = context; -+ -+ *frame = usb_get_current_frame_number(wd->usb.udev); -+ USBEXIT(return STATUS_SUCCESS); -+} -+ -+wstdcall NTSTATUS USBD_InterfaceSubmitIsoOutUrb(void *context, -+ union nt_urb *nt_urb) -+{ -+ /* TODO: implement this */ -+ TODO(); -+ USBEXIT(return STATUS_NOT_IMPLEMENTED); -+} -+ -+wstdcall NTSTATUS -+USBD_InterfaceQueryBusInformation(void *context, ULONG level, void *buf, -+ ULONG *buf_length, ULONG *buf_actual_length) -+{ -+#if 0 -+ struct wrap_device *wd = context; -+ struct usb_bus *bus = wd->usb.udev->bus; -+ struct usb_bus_information_level *bus_info = buf; -+#endif -+ -+ TODO(); -+ USBEXIT(return STATUS_NOT_IMPLEMENTED); -+} -+ -+wstdcall NTSTATUS -+USBD_InterfaceLogEntry(void *context, ULONG driver_tag, ULONG enum_tag, -+ ULONG p1, ULONG p2) -+{ -+ ERROR("%p, %x, %x, %x, %x", context, driver_tag, enum_tag, p1, p2); -+ USBEXIT(return STATUS_SUCCESS); -+} -+ -+NTSTATUS -+usb_query_interface(struct wrap_device *wd, struct io_stack_location *irp_sl) -+{ -+ struct usbd_bus_interface_usbdi *intf = -+ (struct usbd_bus_interface_usbdi *) -+ irp_sl->params.query_intf.intf; -+ -+ TRACE2("type: %x, size: %d, version: %d", -+ irp_sl->params.query_intf.type->data1, -+ irp_sl->params.query_intf.size, -+ irp_sl->params.query_intf.version); -+ intf->Context = wd; -+ intf->InterfaceReference = -+ WIN_FUNC_PTR(USBD_InterfaceReference, 1); -+ intf->InterfaceDereference = -+ WIN_FUNC_PTR(USBD_InterfaceDereference, 1); -+ intf->GetUSBDIVersion = -+ WIN_FUNC_PTR(USBD_InterfaceGetUSBDIVersion, 3); -+ intf->QueryBusTime = -+ WIN_FUNC_PTR(USBD_InterfaceQueryBusTime, 2); -+ intf->SubmitIsoOutUrb = -+ WIN_FUNC_PTR(USBD_InterfaceSubmitIsoOutUrb, 2); -+ intf->QueryBusInformation = -+ WIN_FUNC_PTR(USBD_InterfaceQueryBusInformation, 5); -+ if (irp_sl->params.query_intf.version >= USB_BUSIF_USBDI_VERSION_1) -+ intf->IsDeviceHighSpeed = -+ WIN_FUNC_PTR(USBD_InterfaceIsDeviceHighSpeed, 1); -+ if (irp_sl->params.query_intf.version >= USB_BUSIF_USBDI_VERSION_2) -+ intf->LogEntry = WIN_FUNC_PTR(USBD_InterfaceLogEntry, 5); -+ return STATUS_SUCCESS; -+} -+ -+int usb_init(void) -+{ -+ InitializeListHead(&wrap_urb_complete_list); -+ spin_lock_init(&wrap_urb_complete_list_lock); -+ INIT_WORK(&wrap_urb_complete_work, wrap_urb_complete_worker); -+#ifdef USB_DEBUG -+ urb_id = 0; -+#endif -+ return 0; -+} -+ -+void usb_exit(void) -+{ -+ USBEXIT(return); -+} -+ -+int usb_init_device(struct wrap_device *wd) -+{ -+ InitializeListHead(&wd->usb.wrap_urb_list); -+ wd->usb.num_alloc_urbs = 0; -+ USBEXIT(return 0); -+} -+ -+void usb_exit_device(struct wrap_device *wd) -+{ -+ kill_all_urbs(wd, 0); -+ USBEXIT(return); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/usb.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.h ---- linux-3.12.2/3rdparty/ndiswrapper/usb.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/usb.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,360 @@ -+/* -+ * Copyright (C) 2004 Jan Kiszka -+ * Copyright (C) 2005 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _USB_H_ -+#define _USB_H_ -+ -+#include "ntoskernel.h" -+ -+#define IOCTL_INTERNAL_USB_SUBMIT_URB 0x00220003 -+#define IOCTL_INTERNAL_USB_RESET_PORT 0x00220007 -+#define IOCTL_INTERNAL_USB_GET_PORT_STATUS 0x00220013 -+#define IOCTL_INTERNAL_USB_CYCLE_PORT 0x0022001F -+#define IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION 0x00220027 -+ -+#define URB_FUNCTION_SELECT_CONFIGURATION 0x0000 -+#define URB_FUNCTION_SELECT_INTERFACE 0x0001 -+#define URB_FUNCTION_ABORT_PIPE 0x0002 -+#define URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL 0x0003 -+#define URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL 0x0004 -+#define URB_FUNCTION_GET_FRAME_LENGTH 0x0005 -+#define URB_FUNCTION_SET_FRAME_LENGTH 0x0006 -+#define URB_FUNCTION_GET_CURRENT_FRAME_NUMBER 0x0007 -+#define URB_FUNCTION_CONTROL_TRANSFER 0x0008 -+#define URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 0x0009 -+#define URB_FUNCTION_ISOCH_TRANSFER 0x000A -+#define URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 0x000B -+#define URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE 0x000C -+#define URB_FUNCTION_SET_FEATURE_TO_DEVICE 0x000D -+#define URB_FUNCTION_SET_FEATURE_TO_INTERFACE 0x000E -+#define URB_FUNCTION_SET_FEATURE_TO_ENDPOINT 0x000F -+#define URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE 0x0010 -+#define URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE 0x0011 -+#define URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT 0x0012 -+#define URB_FUNCTION_GET_STATUS_FROM_DEVICE 0x0013 -+#define URB_FUNCTION_GET_STATUS_FROM_INTERFACE 0x0014 -+#define URB_FUNCTION_GET_STATUS_FROM_ENDPOINT 0x0015 -+#define URB_FUNCTION_RESERVED_0X0016 0x0016 -+#define URB_FUNCTION_VENDOR_DEVICE 0x0017 -+#define URB_FUNCTION_VENDOR_INTERFACE 0x0018 -+#define URB_FUNCTION_VENDOR_ENDPOINT 0x0019 -+#define URB_FUNCTION_CLASS_DEVICE 0x001A -+#define URB_FUNCTION_CLASS_INTERFACE 0x001B -+#define URB_FUNCTION_CLASS_ENDPOINT 0x001C -+#define URB_FUNCTION_RESERVE_0X001D 0x001D -+#define URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL 0x001E -+#define URB_FUNCTION_CLASS_OTHER 0x001F -+#define URB_FUNCTION_VENDOR_OTHER 0x0020 -+#define URB_FUNCTION_GET_STATUS_FROM_OTHER 0x0021 -+#define URB_FUNCTION_CLEAR_FEATURE_TO_OTHER 0x0022 -+#define URB_FUNCTION_SET_FEATURE_TO_OTHER 0x0023 -+#define URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT 0x0024 -+#define URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT 0x0025 -+#define URB_FUNCTION_GET_CONFIGURATION 0x0026 -+#define URB_FUNCTION_GET_INTERFACE 0x0027 -+#define URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE 0x0028 -+#define URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE 0x0029 -+#define URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR 0x002A -+#define URB_FUNCTION_RESERVE_0X002B 0x002B -+#define URB_FUNCTION_RESERVE_0X002C 0x002C -+#define URB_FUNCTION_RESERVE_0X002D 0x002D -+#define URB_FUNCTION_RESERVE_0X002E 0x002E -+#define URB_FUNCTION_RESERVE_0X002F 0x002F -+// USB 2.0 calls start at 0x0030 -+#define URB_FUNCTION_SYNC_RESET_PIPE 0x0030 -+#define URB_FUNCTION_SYNC_CLEAR_STALL 0x0031 -+#define URB_FUNCTION_CONTROL_TRANSFER_EX 0x0032 -+ -+#define USBD_PF_CHANGE_MAX_PACKET 0x00000001 -+ -+#define USBD_TRANSFER_DIRECTION_OUT 0 -+#define USBD_TRANSFER_DIRECTION_IN 1 -+ -+#define USBD_SHORT_TRANSFER_OK 0x00000002 -+#define USBD_START_ISO_TRANSFER_ASAP 0x00000004 -+#define USBD_DEFAULT_PIPE_TRANSFER 0x00000008 -+ -+#define USBD_TRANSFER_DIRECTION(flags) \ -+ ((flags) & USBD_TRANSFER_DIRECTION_IN) -+ -+enum pipe_type {UsbdPipeTypeControl = USB_ENDPOINT_XFER_CONTROL, -+ UsbdPipeTypeIsochronous = USB_ENDPOINT_XFER_ISOC, -+ UsbdPipeTypeBulk = USB_ENDPOINT_XFER_BULK, -+ UsbdPipeTypeInterrupt = USB_ENDPOINT_XFER_INT}; -+ -+#define USBD_IS_BULK_PIPE(pipe_handle) \ -+ (((pipe_handle)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) \ -+ == USB_ENDPOINT_XFER_BULK) -+ -+#define USBD_IS_INT_PIPE(pipe_handle) \ -+ (((pipe_handle)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) \ -+ == USB_ENDPOINT_XFER_INT) -+ -+#define USBD_PORT_ENABLED 0x00000001 -+#define USBD_PORT_CONNECTED 0x00000002 -+ -+typedef LONG USBD_STATUS; -+ -+#define USBD_STATUS_SUCCESS 0x0 -+#define USBD_STATUS_PENDING 0x40000000 -+#define USBD_STATUS_CANCELED 0x00010000 -+ -+#define USBD_STATUS_CRC 0xC0000001 -+#define USBD_STATUS_BTSTUFF 0xC0000002 -+#define USBD_STATUS_DATA_TOGGLE_MISMATCH 0xC0000003 -+#define USBD_STATUS_STALL_PID 0xC0000004 -+#define USBD_STATUS_DEV_NOT_RESPONDING 0xC0000005 -+#define USBD_STATUS_PID_CHECK_FAILURE 0xC0000006 -+#define USBD_STATUS_UNEXPECTED_PID 0xC0000007 -+#define USBD_STATUS_DATA_OVERRUN 0xC0000008 -+#define USBD_STATUS_DATA_UNDERRUN 0xC0000009 -+#define USBD_STATUS_RESERVED1 0xC000000A -+#define USBD_STATUS_RESERVED2 0xC000000B -+#define USBD_STATUS_BUFFER_OVERRUN 0xC000000C -+#define USBD_STATUS_BUFFER_UNDERRUN 0xC000000D -+#define USBD_STATUS_NOT_ACCESSED 0xC000000F -+#define USBD_STATUS_FIFO 0xC0000010 -+#define USBD_STATUS_XACT_ERROR 0xC0000011 -+#define USBD_STATUS_BABBLE_DETECTED 0xC0000012 -+#define USBD_STATUS_DATA_BUFFER_ERROR 0xC0000013 -+ -+#define USBD_STATUS_NOT_SUPPORTED 0xC0000E00 -+#define USBD_STATUS_BUFFER_TOO_SMALL 0xC0003000 -+#define USBD_STATUS_TIMEOUT 0xC0006000 -+#define USBD_STATUS_DEVICE_GONE 0xC0007000 -+ -+#define USBD_STATUS_NO_MEMORY 0x80000100 -+#define USBD_STATUS_INVALID_URB_FUNCTION 0x80000200 -+#define USBD_STATUS_INVALID_PARAMETER 0x80000300 -+#define USBD_STATUS_REQUEST_FAILED 0x80000500 -+#define USBD_STATUS_INVALID_PIPE_HANDLE 0x80000600 -+#define USBD_STATUS_ERROR_SHORT_TRANSFER 0x80000900 -+ -+#define USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE PAGE_SIZE -+ -+struct urb_hcd_area { -+ void *reserved8[8]; -+}; -+ -+struct usbd_pipe_information { -+ USHORT wMaxPacketSize; -+ UCHAR bEndpointAddress; -+ UCHAR bInterval; -+ enum pipe_type type; -+ struct usb_endpoint_descriptor *handle; -+ ULONG max_tx_size; -+ ULONG flags; -+}; -+ -+struct usbd_interface_information { -+ USHORT bLength; -+ UCHAR bInterfaceNumber; -+ UCHAR bAlternateSetting; -+ UCHAR bInterfaceClass; -+ UCHAR bInterfaceSubClass; -+ UCHAR bInterfaceProtocol; -+ UCHAR reserved; -+ void *handle; -+ ULONG bNumEndpoints; -+ struct usbd_pipe_information pipes[1]; -+}; -+ -+struct usbd_interface_list_entry { -+ struct usb_interface_descriptor *intf_desc; -+ struct usbd_interface_information *intf; -+}; -+ -+struct nt_urb_header { -+ USHORT length; -+ USHORT function; -+ USBD_STATUS status; -+ void *usbd_dev_handle; -+ ULONG usbd_flags; -+}; -+ -+struct usbd_select_interface { -+ struct nt_urb_header header; -+ void *handle; -+ struct usbd_interface_information intf; -+}; -+ -+struct usbd_select_configuration { -+ struct nt_urb_header header; -+ struct usb_config_descriptor *config; -+ void *handle; -+ struct usbd_interface_information intf; -+}; -+ -+struct usbd_control_descriptor_request { -+ struct nt_urb_header header; -+ void *reserved; -+ ULONG reserved0; -+ ULONG transfer_buffer_length; -+ void *transfer_buffer; -+ struct mdl *mdl; -+ union nt_urb *urb_link; -+ struct urb_hcd_area hca; -+ USHORT reserved1; -+ UCHAR index; -+ UCHAR desc_type; -+ USHORT language_id; -+ USHORT reserved2; -+}; -+ -+struct usbd_bulk_or_intr_transfer { -+ struct nt_urb_header header; -+ struct usb_endpoint_descriptor *pipe_handle; -+ ULONG transfer_flags; -+ ULONG transfer_buffer_length; -+ void *transfer_buffer; -+ struct mdl *mdl; -+ union nt_urb *urb_link; -+ struct urb_hcd_area hca; -+}; -+ -+struct usbd_pipe_request { -+ struct nt_urb_header header; -+ struct usb_endpoint_descriptor *pipe_handle; -+}; -+ -+struct usbd_vendor_or_class_request { -+ struct nt_urb_header header; -+ void *reserved; -+ ULONG transfer_flags; -+ ULONG transfer_buffer_length; -+ void *transfer_buffer; -+ struct mdl *mdl; -+ union nt_urb *link; -+ struct urb_hcd_area hca; -+ UCHAR reserved_bits; -+ UCHAR request; -+ USHORT value; -+ USHORT index; -+ USHORT reserved1; -+}; -+ -+struct urb_control_feature_request { -+ struct nt_urb_header header; -+ void *reserved; -+ ULONG reserved2; -+ ULONG reserved3; -+ void *reserved4; -+ struct mdl *reserved5; -+ union nt_urb *link; -+ struct urb_hcd_area hca; -+ USHORT reserved0; -+ USHORT feature_selector; -+ USHORT index; -+ USHORT reserved1; -+}; -+ -+struct urb_control_get_status_request { -+ struct nt_urb_header header; -+ void *reserved; -+ ULONG reserved0; -+ ULONG transfer_buffer_length; -+ void *transfer_buffer; -+ struct mdl *mdl; -+ union nt_urb *link; -+ struct urb_hcd_area hca; -+ UCHAR reserved1[4]; -+ USHORT index; -+ USHORT reserved2; -+}; -+ -+struct usbd_iso_packet_desc { -+ ULONG offset; -+ ULONG length; -+ USBD_STATUS status; -+}; -+ -+struct usbd_isochronous_transfer { -+ struct nt_urb_header header; -+ struct usb_endpoint_descriptor *pipe_handle; -+ ULONG transfer_flags; -+ ULONG transfer_buffer_length; -+ void *transfer_buffer; -+ struct mdl *mdl; -+ union nt_urb *urb_link; -+ struct urb_hcd_area hca; -+ ULONG start_frame; -+ ULONG number_of_packets; -+ ULONG error_count; -+ struct usbd_iso_packet_desc iso_packet[1]; -+}; -+ -+union nt_urb { -+ struct nt_urb_header header; -+ struct usbd_select_interface select_intf; -+ struct usbd_select_configuration select_conf; -+ struct usbd_bulk_or_intr_transfer bulk_int_transfer; -+ struct usbd_control_descriptor_request control_desc; -+ struct usbd_vendor_or_class_request vendor_class_request; -+ struct usbd_isochronous_transfer isochronous; -+ struct usbd_pipe_request pipe_req; -+ struct urb_control_feature_request feat_req; -+ struct urb_control_get_status_request status_req; -+}; -+ -+struct usbd_bus_interface_usbdi { -+ USHORT Size; -+ USHORT Version; -+ void *Context; -+ void *InterfaceReference; -+ void *InterfaceDereference; -+ void *GetUSBDIVersion; -+ void *QueryBusTime; -+ void *SubmitIsoOutUrb; -+ void *QueryBusInformation; -+ /* version 1 and above have following field */ -+ void *IsDeviceHighSpeed; -+ /* version 2 (and above) have following field */ -+ void *LogEntry; -+}; -+ -+struct usbd_bus_information_level { -+ ULONG TotalBandwidth; -+ ULONG ConsumedBandwidth; -+ /* level 1 and above have following fields */ -+ ULONG ControllerNameLength; -+ wchar_t ControllerName[1]; -+}; -+ -+#define USBDI_VERSION_XP 0x00000500 // Windows XP -+#define USB_HCD_CAPS_SUPPORTS_RT_THREADS 0x00000001 -+#define USB_BUSIF_USBDI_VERSION_0 0x0000 -+#define USB_BUSIF_USBDI_VERSION_1 0x0001 -+#define USB_BUSIF_USBDI_VERSION_2 0x0002 -+ -+struct usbd_version_info { -+ ULONG usbdi_version; -+ ULONG supported_usb_version; -+}; -+ -+struct usbd_idle_callback { -+ void *callback; -+ void *context; -+}; -+ -+#define NT_URB_STATUS(nt_urb) ((nt_urb)->header.status) -+ -+NTSTATUS wrap_submit_irp(struct device_object *pdo, struct irp *irp); -+void wrap_suspend_urbs(struct wrap_device *wd); -+void wrap_resume_urbs(struct wrap_device *wd); -+NTSTATUS usb_query_interface(struct wrap_device *wd, -+ struct io_stack_location *irp_sl); -+ -+#endif /* USB_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/win2lin_stubs.S linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/win2lin_stubs.S ---- linux-3.12.2/3rdparty/ndiswrapper/win2lin_stubs.S 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/win2lin_stubs.S 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,253 @@ -+/* -+ * Copyright (C) 2005 Karl Vogel, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include -+ -+#ifdef CONFIG_X86_64 -+ -+/* -+# Windows <---> Linux register usage conversion when calling functions -+# V = Volatile -+# NV = Non Volatile (needs to be saved) -+# -+# Win Lin -+# --------------------------------------- -+# Rax Return V Return V -+# Rbx NV NV -+# Rcx Arg1 V Arg4 V -+# Rdx Arg2 V Arg3 V -+# Rsi NV Arg2 V -+# Rdi NV Arg1 V -+# Rsp NV NV -+# Rbp NV NV -+# R8 Arg3 V Arg5 V -+# R9 Arg4 V Arg6 V -+# R10 V V -+# R11 V V -+# R12 NV NV -+# R13 NV NV -+# R14 NV NV -+# R15 NV NV -+# -+# In addition, Linux uses %rax to indicate number of SSE registers used -+# when variadic functions are called. Since there is no way to obtain this -+# from Windows, for now, we just assume this is 0 (hence %rax is cleared). -+# -+# Windows pushes arguments 5 and higher onto stack in case of integer -+# variables and 4 and higher in case of floating point variables (passed -+# in SSE registers). -+ -+In a windows function, the stackframe/registers look like this: -+ -+# 0x0048 .... -+# 0x0040 arg8 -+# 0x0038 arg7 -+# 0x0030 arg6 -+# 0x0028 arg5 -+# 0x0020 shadow/spill space for arg4 -+# 0x0018 shadow/spill space for arg3 -+# 0x0010 shadow/spill space for arg2 -+# 0x0008 shadow/spill space for arg1 -+# 0x0000 ret -+ -+# register spill space is same irrespective of number of arguments - even -+# if Windows function takes less than 4 arguments, 32 bytes above return -+# address is reserved for the function -+ -+In Linux it should look like: -+ -+# 0x0018 .... -+# 0x0010 arg8 -+# 0x0008 arg7 -+# 0x0000 ret -+ -+*/ -+ -+ .text -+ -+#define LINUX_REG_ARGS 6 -+#define LOOP_THRESHOLD 9 -+#define WORD_BYTES 8 -+ -+/* -+ * %rsi and %rdi must be saved because they are not saved by Linux calls, but -+ * Windows callers expect them to be saved. %rbp is saved to create a stack -+ * frame, which can help with debugging. We need to reserve space for an odd -+ * number of registers anyway to keep 16-bit alignment of the stack (one more -+ * position is used by the return address). -+ */ -+#define SAVED_REGS 3 -+ -+/* -+ * When calling the Linux function, several registers are saved on the stack. -+ * When passing more than 6 arguments, arguments starting with argument 7 are -+ * pushed to the stack as well. -+ * -+ * We also need to allocate an additional word on the stack to keep it aligned -+ * to the 16-bit boundary if the number of saved arguments plus one (for the -+ * return address) is odd. -+ */ -+ -+/* -+ * Number of arguments we pass on stack to the Linux function. -+ * The value of true is -1 in assembler, so we multiply it by another true -+ * value. -+ */ -+#define stack_args(argc) \ -+ ((0 < 1) * (argc > LINUX_REG_ARGS) * (argc - LINUX_REG_ARGS)) -+ -+/* Full required change of stack pointer, in words */ -+#define stack_words_raw(argc) (stack_args(argc) + SAVED_REGS + 1) -+ -+/* Full actual change of stack pointer, in words (must be even) */ -+#define stack_words_aligned(argc) ((stack_words_raw(argc) + 1) & ~1) -+ -+/* Space allocated for Linux arguments on stack */ -+#define stack_space(argc) \ -+ ((stack_words_aligned(argc) - SAVED_REGS - 1) * WORD_BYTES) -+ -+/* -+ * win2lin_win_arg(N, ARGC) gives the address of the Windows argument N out of -+ * total ARGC after the stack has been prepared for the Linux function call. -+ * -+ * When called from Windows, the Nth argument is at (N * 8)(%rsp). We add the -+ * stack space allocated by the Linux function to compensate for %rsp change. -+ * -+ * Don't call with N less than 5! -+ */ -+#define win2lin_win_arg(n, argc) \ -+ ((n + SAVED_REGS) * WORD_BYTES + stack_space(argc))(%rsp) -+ -+/* -+ * win2lin_lin_arg(N) gives the address of the Nth Linux argument on the extra -+ * Linux stack frame. When more than 6 arguments are used, %rsp points to the -+ * 7th argument. The Nth argument is therefore at ((N - 7) * 8)(%rsp). -+ * -+ * Don't call with N less than 7! -+ */ -+#define win2lin_lin_arg(n) ((n - 1 - LINUX_REG_ARGS) * WORD_BYTES)(%rsp) -+ -+/* Declare function LONGNAME, call function SHORTNAME with ARGC arguments */ -+.macro win2linm longname, shortname, argc -+ .type \longname, @function -+ ENTRY(\longname) -+ -+ /* Create a call frame - it's optional, but good for debugging */ -+ .cfi_startproc -+ push %rbp -+ .cfi_def_cfa %rsp, 2 * WORD_BYTES -+ .cfi_offset %rbp, -2 * WORD_BYTES -+ mov %rsp, %rbp -+ .cfi_def_cfa %rbp, 2 * WORD_BYTES -+ -+ /* -+ * Registers %rdi and %rsi are volatile on Linux, but not on Windows, -+ * so save them on the stack. -+ */ -+ push %rsi -+ push %rdi -+ -+ /* Allocate extra stack space for arguments 7 and up */ -+ sub $stack_space(\argc), %rsp -+ -+ /* -+ * Copy arguments 7 and up. We do it early, before %rdi and %rsi -+ * are used for arguments 1 and 2, so we don't have to save them. -+ * We still need to save %rcx if using a string copy. -+ */ -+ .if (\argc < LOOP_THRESHOLD) -+ /* If a few arguments, copy them individually through %r11 */ -+ .if (\argc >= 7) -+ mov win2lin_win_arg(7, \argc), %r11 -+ mov %r11, win2lin_lin_arg(7) -+ .endif -+ .if (\argc >= 8) -+ mov win2lin_win_arg(8, \argc), %r11 -+ mov %r11, win2lin_lin_arg(8) -+ .endif -+ .else -+ /* If there are many arguments, copy them in a loop */ -+ /* Save arg1 to %r11 */ -+ mov %rcx, %r11 -+ /* Source and destination */ -+ lea win2lin_win_arg(LINUX_REG_ARGS + 1, \argc), %rsi -+ lea win2lin_lin_arg(LINUX_REG_ARGS + 1), %rdi -+ /* Number of arguments to copy (%ecx zero-extends to %rcx) */ -+ mov $(\argc - LINUX_REG_ARGS), %ecx -+ rep movsq -+ /* Restore arg1 directly to %rdi */ -+ mov %r11, %rdi -+ .endif -+ -+ /* -+ * Argument 1 - %rcx on Windows, %rdi on Linux -+ * Micro-optimization - if we used loop, arg1 is already in %rdi -+ */ -+ .if (\argc >= 1) && (\argc < LOOP_THRESHOLD) -+ mov %rcx, %rdi -+ .endif -+ -+ /* Argument 2 - %rdx on Windows, %rsi on Linux */ -+ .if (\argc >= 2) -+ mov %rdx, %rsi -+ .endif -+ -+ /* Argument 3 - %r8 on Windows, %rdx on Linux */ -+ .if (\argc >= 3) -+ mov %r8, %rdx -+ .endif -+ -+ /* Argument 4 - %r9 on Windows, %rcx on Linux */ -+ .if (\argc >= 4) -+ mov %r9, %rcx -+ .endif -+ -+ /* Argument 5 - first argument on stack on Windows, %r8 Linux */ -+ .if (\argc >= 5) -+ mov win2lin_win_arg(5, \argc), %r8 -+ .endif -+ -+ /* Argument 6 - second argument on stack on Windows, %r9 Linux */ -+ .if (\argc >= 6) -+ mov win2lin_win_arg(6, \argc), %r9 -+ .endif -+ -+ /* %rax on Linux is the number of arguments in SSE registers (zero) */ -+ xor %rax, %rax -+ -+ /* Call the function */ -+ call \shortname -+ -+ /* Free stack space for arguments 7 and up */ -+ add $stack_space(\argc), %rsp -+ -+ /* Restore saved registers */ -+ pop %rdi -+ pop %rsi -+ -+ /* Return to Windows code */ -+ leave -+ .cfi_def_cfa %rsp, WORD_BYTES -+ .cfi_restore %rbp -+ ret -+ .cfi_endproc -+ .size \longname, (. - \longname) -+.endm -+ -+#define win2lin(name, argc) win2linm win2lin_ ## name ## _ ## argc, name, argc -+ -+#include "win2lin_stubs.h" -+ -+#endif /* CONFIG_X86_64 */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/winnt_types.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/winnt_types.h ---- linux-3.12.2/3rdparty/ndiswrapper/winnt_types.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/winnt_types.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,1701 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _WINNT_TYPES_H_ -+#define _WINNT_TYPES_H_ -+ -+#define TRUE 1 -+#define FALSE 0 -+ -+#define PASSIVE_LEVEL 0 -+#define APC_LEVEL 1 -+#define DISPATCH_LEVEL 2 -+#define DEVICE_LEVEL_BASE 4 -+ -+/* soft interrupts / bottom-half's are disabled at SOFT_IRQL */ -+#define SOFT_IRQL (DEVICE_LEVEL_BASE + 1) -+#define DIRQL (DEVICE_LEVEL_BASE + 2) -+ -+#define STATUS_WAIT_0 0 -+#define STATUS_SUCCESS 0 -+#define STATUS_ALERTED 0x00000101 -+#define STATUS_TIMEOUT 0x00000102 -+#define STATUS_PENDING 0x00000103 -+#define STATUS_FAILURE 0xC0000001 -+#define STATUS_NOT_IMPLEMENTED 0xC0000002 -+#define STATUS_INVALID_PARAMETER 0xC000000D -+#define STATUS_INVALID_DEVICE_REQUEST 0xC0000010 -+#define STATUS_MORE_PROCESSING_REQUIRED 0xC0000016 -+#define STATUS_ACCESS_DENIED 0xC0000022 -+#define STATUS_BUFFER_TOO_SMALL 0xC0000023 -+#define STATUS_OBJECT_NAME_INVALID 0xC0000023 -+#define STATUS_MUTANT_NOT_OWNED 0xC0000046 -+#define STATUS_RESOURCES 0xC000009A -+#define STATUS_DELETE_PENDING 0xC0000056 -+#define STATUS_INSUFFICIENT_RESOURCES 0xC000009A -+#define STATUS_NOT_SUPPORTED 0xC00000BB -+#define STATUS_INVALID_PARAMETER_2 0xC00000F0 -+#define STATUS_NO_MEMORY 0xC0000017 -+#define STATUS_CANCELLED 0xC0000120 -+#define STATUS_DEVICE_REMOVED 0xC00002B6 -+#define STATUS_DEVICE_NOT_CONNECTED 0xC000009D -+ -+#define STATUS_BUFFER_OVERFLOW 0x80000005 -+ -+#define SL_PENDING_RETURNED 0x01 -+#define SL_INVOKE_ON_CANCEL 0x20 -+#define SL_INVOKE_ON_SUCCESS 0x40 -+#define SL_INVOKE_ON_ERROR 0x80 -+ -+#define IRP_MJ_CREATE 0x00 -+#define IRP_MJ_CREATE_NAMED_PIPE 0x01 -+#define IRP_MJ_CLOSE 0x02 -+#define IRP_MJ_READ 0x03 -+#define IRP_MJ_WRITE 0x04 -+ -+#define IRP_MJ_DEVICE_CONTROL 0x0E -+#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0F -+#define IRP_MJ_POWER 0x16 -+#define IRP_MJ_SYSTEM_CONTROL 0x0E -+#define IRP_MJ_PNP 0x1b -+#define IRP_MJ_MAXIMUM_FUNCTION 0x1b -+ -+#define IRP_MN_WAIT_WAKE 0x00 -+#define IRP_MN_POWER_SEQUENCE 0x01 -+#define IRP_MN_SET_POWER 0x02 -+#define IRP_MN_QUERY_POWER 0x03 -+ -+#define IRP_MN_REGINFO 0x08 -+#define IRP_MN_REGINFO_EX 0x0b -+ -+#define IRP_MN_START_DEVICE 0x00 -+#define IRP_MN_QUERY_REMOVE_DEVICE 0x01 -+#define IRP_MN_REMOVE_DEVICE 0x02 -+#define IRP_MN_CANCEL_REMOVE_DEVICE 0x03 -+#define IRP_MN_STOP_DEVICE 0x04 -+#define IRP_MN_QUERY_STOP_DEVICE 0x05 -+#define IRP_MN_CANCEL_STOP_DEVICE 0x06 -+#define IRP_MN_QUERY_DEVICE_RELATIONS 0x07 -+#define IRP_MN_QUERY_INTERFACE 0x08 -+ -+#define IRP_BUFFERED_IO 0x00000010 -+#define IRP_DEALLOCATE_BUFFER 0x00000020 -+#define IRP_INPUT_OPERATION 0x00000040 -+ -+#define IRP_DEFFER_IO_COMPLETION 0x00000800 -+ -+#define THREAD_WAIT_OBJECTS 3 -+#define MAX_WAIT_OBJECTS 64 -+ -+#define LOW_PRIORITY 0 -+#define LOW_REALTIME_PRIORITY 16 -+#define HIGH_PRIORITY 31 -+#define MAXIMUM_PRIORITY 32 -+ -+#define PROCESSOR_FEATURE_MAX 64 -+ -+#define IO_NO_INCREMENT 0 -+ -+#define WMIREG_ACTION_REGISTER 1 -+#define WMIREG_ACTION_DEREGISTER 2 -+#define WMIREG_ACTION_REREGISTER 3 -+#define WMIREG_ACTION_UPDATE_GUIDS 4 -+ -+#define WMIREGISTER 0 -+#define WMIUPDATE 1 -+ -+#ifdef CONFIG_X86_64 -+#define wstdcall -+#define wfastcall -+#define noregparm -+#define regparm3 -+ -+#define KI_USER_SHARED_DATA 0xfffff78000000000UL -+ -+#else -+ -+#define noregparm __attribute__((regparm(0))) -+#define regparm3 __attribute__((regparm(3))) -+#define wstdcall __attribute__((__stdcall__, regparm(0))) -+#if defined(__GNUC__) && ((__GNUC__ == 3 && __GNUC_MINOR__ > 3) || __GNUC__ > 3) -+#undef fastcall -+#define wfastcall __attribute__((fastcall)) -+#else -+#error "gcc 3.4 or newer should be used for compiling this module" -+#endif -+ -+#define KI_USER_SHARED_DATA 0xffdf0000 -+ -+#endif -+ -+typedef u8 BOOLEAN; -+typedef u8 BYTE; -+typedef u8 *LPBYTE; -+typedef s8 CHAR; -+typedef u8 UCHAR; -+typedef s16 SHORT; -+typedef u16 USHORT; -+typedef u16 WORD; -+typedef s32 INT; -+typedef u32 UINT; -+typedef u32 DWORD; -+typedef s32 LONG; -+typedef u32 ULONG; -+typedef s64 LONGLONG; -+typedef u64 ULONGLONG; -+typedef u64 ULONGULONG; -+typedef u64 ULONG64; -+ -+typedef CHAR CCHAR; -+typedef USHORT wchar_t; -+typedef SHORT CSHORT; -+typedef LONGLONG LARGE_INTEGER; -+ -+typedef LONG NTSTATUS; -+ -+typedef LONG KPRIORITY; -+typedef LARGE_INTEGER PHYSICAL_ADDRESS; -+typedef UCHAR KIRQL; -+typedef CHAR KPROCESSOR_MODE; -+ -+/* ULONG_PTR is 32 bits on 32-bit platforms and 64 bits on 64-bit -+ * platform, which is same as 'unsigned long' in Linux */ -+typedef unsigned long ULONG_PTR; -+ -+typedef size_t SIZE_T; -+typedef ULONG_PTR KAFFINITY; -+typedef ULONG ACCESS_MASK; -+ -+typedef ULONG_PTR PFN_NUMBER; -+typedef ULONG SECURITY_INFORMATION; -+ -+/* non-negative numbers indicate success */ -+#define NT_SUCCESS(status) ((NTSTATUS)(status) >= 0) -+ -+struct ansi_string { -+ USHORT length; -+ USHORT max_length; -+ char *buf; -+}; -+ -+struct unicode_string { -+ USHORT length; -+ USHORT max_length; -+ wchar_t *buf; -+}; -+ -+struct nt_slist { -+ struct nt_slist *next; -+}; -+ -+#ifdef CONFIG_X86_64 -+/* it is not clear how nt_slist_head is used to store pointer to -+ * slists and depth; here we assume 'align' field is used to store -+ * depth and 'region' field is used to store slist pointers */ -+struct nt_slist_head { -+ union { -+ USHORT depth; -+ ULONGLONG align; -+ }; -+ union { -+ ULONGLONG region; -+ struct nt_slist *next; -+ }; -+} __attribute__((aligned(16))); -+typedef struct nt_slist_head nt_slist_header; -+#else -+union nt_slist_head { -+ ULONGLONG align; -+ struct { -+ struct nt_slist *next; -+ USHORT depth; -+ USHORT sequence; -+ }; -+}; -+typedef union nt_slist_head nt_slist_header; -+#endif -+ -+struct nt_list { -+ struct nt_list *next; -+ struct nt_list *prev; -+}; -+ -+typedef ULONG_PTR NT_SPIN_LOCK; -+ -+enum kdpc_importance {LowImportance, MediumImportance, HighImportance}; -+ -+struct kdpc; -+typedef void (*DPC)(struct kdpc *kdpc, void *ctx, void *arg1, -+ void *arg2) wstdcall; -+struct kdpc { -+ SHORT type; -+ UCHAR nr_cpu; -+ UCHAR importance; -+ struct nt_list list; -+ DPC func; -+ void *ctx; -+ void *arg1; -+ void *arg2; -+ union { -+ NT_SPIN_LOCK *lock; -+ /* 'lock' is not used; 'queued' represents whether -+ * kdpc is queued or not */ -+ int queued; -+ }; -+}; -+ -+enum pool_type { -+ NonPagedPool, PagedPool, NonPagedPoolMustSucceed, DontUseThisType, -+ NonPagedPoolCacheAligned, PagedPoolCacheAligned, -+ NonPagedPoolCacheAlignedMustS, MaxPoolType, -+ NonPagedPoolSession = 32, -+ PagedPoolSession = NonPagedPoolSession + 1, -+ NonPagedPoolMustSucceedSession = PagedPoolSession + 1, -+ DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1, -+ NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1, -+ PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1, -+ NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1 -+}; -+ -+enum memory_caching_type_orig { -+ MmFrameBufferCached = 2 -+}; -+ -+enum memory_caching_type { -+ MmNonCached = FALSE, MmCached = TRUE, -+ MmWriteCombined = MmFrameBufferCached, MmHardwareCoherentCached, -+ MmNonCachedUnordered, MmUSWCCached, MmMaximumCacheType -+}; -+ -+enum lock_operation { -+ IoReadAccess, IoWriteAccess, IoModifyAccess -+}; -+ -+enum mode { -+ KernelMode, UserMode, MaximumMode -+}; -+ -+struct mdl { -+ struct mdl *next; -+ CSHORT size; -+ CSHORT flags; -+ /* NdisFreeBuffer doesn't pass pool, so we store pool in -+ * unused field 'process' */ -+ union { -+ void *process; -+ void *pool; -+ }; -+ void *mappedsystemva; -+ void *startva; -+ ULONG bytecount; -+ ULONG byteoffset; -+}; -+ -+#define MDL_MAPPED_TO_SYSTEM_VA 0x0001 -+#define MDL_PAGES_LOCKED 0x0002 -+#define MDL_SOURCE_IS_NONPAGED_POOL 0x0004 -+#define MDL_ALLOCATED_FIXED_SIZE 0x0008 -+#define MDL_PARTIAL 0x0010 -+#define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020 -+#define MDL_IO_PAGE_READ 0x0040 -+#define MDL_WRITE_OPERATION 0x0080 -+#define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100 -+#define MDL_FREE_EXTRA_PTES 0x0200 -+#define MDL_IO_SPACE 0x0800 -+#define MDL_NETWORK_HEADER 0x1000 -+#define MDL_MAPPING_CAN_FAIL 0x2000 -+#define MDL_ALLOCATED_MUST_SUCCEED 0x4000 -+ -+#define MDL_POOL_ALLOCATED 0x0400 -+#define MDL_CACHE_ALLOCATED 0x8000 -+ -+#define PAGE_START(ptr) ((void *)((ULONG_PTR)(ptr) & ~(PAGE_SIZE - 1))) -+#define BYTE_OFFSET(ptr) ((ULONG)((ULONG_PTR)(ptr) & (PAGE_SIZE - 1))) -+ -+#define MmGetMdlByteCount(mdl) ((mdl)->bytecount) -+#define MmGetMdlVirtualAddress(mdl) ((mdl)->startva + (mdl)->byteoffset) -+#define MmGetMdlByteOffset(mdl) ((mdl)->byteoffset) -+#define MmGetSystemAddressForMdl(mdl) ((mdl)->mappedsystemva) -+#define MmGetSystemAddressForMdlSafe(mdl, priority) ((mdl)->mappedsystemva) -+#define MmGetMdlPfnArray(mdl) ((PFN_NUMBER *)(mdl + 1)) -+#define MmInitializeMdl(mdl, baseva, length) \ -+do { \ -+ (mdl)->next = NULL; \ -+ (mdl)->size = MmSizeOfMdl(baseva, length); \ -+ (mdl)->flags = 0; \ -+ (mdl)->startva = PAGE_START(baseva); \ -+ (mdl)->byteoffset = BYTE_OFFSET(baseva); \ -+ (mdl)->bytecount = length; \ -+ (mdl)->mappedsystemva = baseva; \ -+ TRACE4("%p %p %p %d %d", (mdl), baseva, (mdl)->startva, \ -+ (mdl)->byteoffset, length); \ -+} while (0) -+ -+struct kdevice_queue_entry { -+ struct nt_list list; -+ ULONG sort_key; -+ BOOLEAN inserted; -+}; -+ -+struct kdevice_queue { -+ USHORT type; -+ USHORT size; -+ struct nt_list list; -+ NT_SPIN_LOCK lock; -+ BOOLEAN busy; -+}; -+ -+struct wait_context_block { -+ struct kdevice_queue_entry wait_queue_entry; -+ void *device_routine; -+ void *device_context; -+ ULONG num_regs; -+ void *device_object; -+ void *current_irp; -+ void *buffer_chaining_dpc; -+}; -+ -+struct wait_block { -+ struct nt_list list; -+ struct task_struct *thread; -+ void *object; -+ int *wait_done; -+ USHORT wait_key; -+ USHORT wait_type; -+}; -+ -+struct dispatcher_header { -+ UCHAR type; -+ UCHAR absolute; -+ UCHAR size; -+ UCHAR inserted; -+ LONG signal_state; -+ struct nt_list wait_blocks; -+}; -+ -+enum event_type { -+ NotificationEvent, -+ SynchronizationEvent, -+}; -+ -+enum timer_type { -+ NotificationTimer = NotificationEvent, -+ SynchronizationTimer = SynchronizationEvent, -+}; -+ -+enum dh_type { -+ NotificationObject = NotificationEvent, -+ SynchronizationObject = SynchronizationEvent, -+ MutexObject, -+ SemaphoreObject, -+ ThreadObject, -+}; -+ -+enum wait_type { -+ WaitAll, WaitAny -+}; -+ -+/* objects that use dispatcher_header have it as the first field, so -+ * whenever we need to initialize dispatcher_header, we can convert -+ * that object into a nt_event and access dispatcher_header */ -+struct nt_event { -+ struct dispatcher_header dh; -+}; -+ -+struct wrap_timer; -+ -+#define WRAP_TIMER_MAGIC 47697249 -+ -+struct nt_timer { -+ struct dispatcher_header dh; -+ /* We can't fit Linux timer in this structure. Instead of -+ * padding the nt_timer structure, we replace due_time field -+ * with *wrap_timer and allocate memory for it when nt_timer is -+ * initialized */ -+ union { -+ ULONGLONG due_time; -+ struct wrap_timer *wrap_timer; -+ }; -+ struct nt_list nt_timer_list; -+ struct kdpc *kdpc; -+ union { -+ LONG period; -+ LONG wrap_timer_magic; -+ }; -+}; -+ -+struct nt_mutex { -+ struct dispatcher_header dh; -+ struct nt_list list; -+ struct task_struct *owner_thread; -+ BOOLEAN abandoned; -+ BOOLEAN apc_disable; -+}; -+ -+struct nt_semaphore { -+ struct dispatcher_header dh; -+ LONG limit; -+}; -+ -+struct nt_thread { -+ struct dispatcher_header dh; -+ /* the rest in Windows is a long structure; since this -+ * structure is opaque to drivers, we just define what we -+ * need */ -+ int pid; -+ NTSTATUS status; -+ struct task_struct *task; -+ struct nt_list irps; -+ NT_SPIN_LOCK lock; -+ KPRIORITY prio; -+}; -+ -+#define set_object_type(dh, type) ((dh)->type = (type)) -+#define is_notify_object(dh) ((dh)->type == NotificationObject) -+#define is_synch_object(dh) ((dh)->type == SynchronizationObject) -+#define is_mutex_object(dh) ((dh)->type == MutexObject) -+#define is_semaphore_object(dh) ((dh)->type == SemaphoreObject) -+#define is_nt_thread_object(dh) ((dh)->type == ThreadObject) -+ -+#define IO_TYPE_ADAPTER 1 -+#define IO_TYPE_CONTROLLER 2 -+#define IO_TYPE_DEVICE 3 -+#define IO_TYPE_DRIVER 4 -+#define IO_TYPE_FILE 5 -+#define IO_TYPE_IRP 6 -+#define IO_TYPE_DEVICE_OBJECT_EXTENSION 13 -+ -+struct irp; -+struct dev_obj_ext; -+struct driver_object; -+ -+struct device_object { -+ CSHORT type; -+ USHORT size; -+ LONG ref_count; -+ struct driver_object *drv_obj; -+ struct device_object *next; -+ struct device_object *attached; -+ struct irp *current_irp; -+ void *io_timer; -+ ULONG flags; -+ ULONG characteristics; -+ void *vpb; -+ void *dev_ext; -+ CCHAR stack_count; -+ union { -+ struct nt_list queue_list; -+ struct wait_context_block wcb; -+ } queue; -+ ULONG align_req; -+ struct kdevice_queue dev_queue; -+ struct kdpc dpc; -+ ULONG active_threads; -+ void *security_desc; -+ struct nt_event lock; -+ USHORT sector_size; -+ USHORT spare1; -+ struct dev_obj_ext *dev_obj_ext; -+ void *reserved; -+}; -+ -+struct dev_obj_ext { -+ CSHORT type; -+ CSHORT size; -+ struct device_object *dev_obj; -+ struct device_object *attached_to; -+}; -+ -+struct io_status_block { -+ union { -+ NTSTATUS status; -+ void *pointer; -+ }; -+ ULONG_PTR info; -+}; -+ -+#ifdef CONFIG_X86_64 -+struct io_status_block32 { -+ NTSTATUS status; -+ ULONG info; -+}; -+#endif -+ -+#define DEVICE_TYPE ULONG -+ -+struct driver_extension; -+ -+typedef NTSTATUS driver_dispatch_t(struct device_object *dev_obj, -+ struct irp *irp) wstdcall; -+ -+struct driver_object { -+ CSHORT type; -+ CSHORT size; -+ struct device_object *dev_obj; -+ ULONG flags; -+ void *start; -+ ULONG driver_size; -+ void *section; -+ struct driver_extension *drv_ext; -+ struct unicode_string name; -+ struct unicode_string *hardware_database; -+ void *fast_io_dispatch; -+ void *init; -+ void *start_io; -+ void (*unload)(struct driver_object *driver) wstdcall; -+ driver_dispatch_t *major_func[IRP_MJ_MAXIMUM_FUNCTION + 1]; -+}; -+ -+struct driver_extension { -+ struct driver_object *drv_obj; -+ NTSTATUS (*add_device)(struct driver_object *drv_obj, -+ struct device_object *dev_obj); -+ ULONG count; -+ struct unicode_string service_key_name; -+ struct nt_list custom_ext; -+}; -+ -+struct custom_ext { -+ struct nt_list list; -+ void *client_id; -+}; -+ -+struct wrap_bin_file; -+ -+struct file_object { -+ CSHORT type; -+ CSHORT size; -+ struct device_object *dev_obj; -+ void *volume_parameter_block; -+ void *fs_context; -+ void *fs_context2; -+ void *section_object_pointer; -+ void *private_cache_map; -+ NTSTATUS final_status; -+ union { -+ struct file_object *related_file_object; -+ struct wrap_bin_file *wrap_bin_file; -+ }; -+ BOOLEAN lock_operation; -+ BOOLEAN delete_pending; -+ BOOLEAN read_access; -+ BOOLEAN write_access; -+ BOOLEAN delete_access; -+ BOOLEAN shared_read; -+ BOOLEAN shared_write; -+ BOOLEAN shared_delete; -+ ULONG flags; -+ struct unicode_string _name_; -+ LARGE_INTEGER current_byte_offset; -+ ULONG waiters; -+ ULONG busy; -+ void *last_lock; -+ struct nt_event lock; -+ struct nt_event event; -+ void *completion_context; -+}; -+ -+#ifdef CONFIG_X86_64 -+#define POINTER_ALIGN __attribute__((aligned(8))) -+#else -+#define POINTER_ALIGN -+#endif -+ -+#define CACHE_ALIGN __attribute__((aligned(128))) -+ -+enum system_power_state { -+ PowerSystemUnspecified = 0, -+ PowerSystemWorking, PowerSystemSleeping1, PowerSystemSleeping2, -+ PowerSystemSleeping3, PowerSystemHibernate, PowerSystemShutdown, -+ PowerSystemMaximum, -+}; -+ -+enum device_power_state { -+ PowerDeviceUnspecified = 0, -+ PowerDeviceD0, PowerDeviceD1, PowerDeviceD2, PowerDeviceD3, -+ PowerDeviceMaximum, -+}; -+ -+union power_state { -+ enum system_power_state system_state; -+ enum device_power_state device_state; -+}; -+ -+enum power_state_type { -+ SystemPowerState = 0, DevicePowerState, -+}; -+ -+enum power_action { -+ PowerActionNone = 0, -+ PowerActionReserved, PowerActionSleep, PowerActionHibernate, -+ PowerActionShutdown, PowerActionShutdownReset, PowerActionShutdownOff, -+ PowerActionWarmEject, -+}; -+ -+struct guid { -+ ULONG data1; -+ USHORT data2; -+ USHORT data3; -+ UCHAR data4[8]; -+}; -+ -+struct nt_interface { -+ USHORT size; -+ USHORT version; -+ void *context; -+ void (*reference)(void *context) wstdcall; -+ void (*dereference)(void *context) wstdcall; -+}; -+ -+enum interface_type { -+ InterfaceTypeUndefined = -1, Internal, Isa, Eisa, MicroChannel, -+ TurboChannel, PCIBus, VMEBus, NuBus, PCMCIABus, CBus, MPIBus, -+ MPSABus, ProcessorInternal, InternalPowerBus, PNPISABus, -+ PNPBus, MaximumInterfaceType, -+}; -+ -+#define CmResourceTypeNull 0 -+#define CmResourceTypePort 1 -+#define CmResourceTypeInterrupt 2 -+#define CmResourceTypeMemory 3 -+#define CmResourceTypeDma 4 -+#define CmResourceTypeDeviceSpecific 5 -+#define CmResourceTypeBusNumber 6 -+#define CmResourceTypeMaximum 7 -+ -+#define CmResourceTypeNonArbitrated 128 -+#define CmResourceTypeConfigData 128 -+#define CmResourceTypeDevicePrivate 129 -+#define CmResourceTypePcCardConfig 130 -+#define CmResourceTypeMfCardConfig 131 -+ -+enum cm_share_disposition { -+ CmResourceShareUndetermined = 0, CmResourceShareDeviceExclusive, -+ CmResourceShareDriverExclusive, CmResourceShareShared -+}; -+ -+#define CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE 0 -+#define CM_RESOURCE_INTERRUPT_LATCHED 1 -+#define CM_RESOURCE_MEMORY_READ_WRITE 0x0000 -+#define CM_RESOURCE_MEMORY_READ_ONLY 0x0001 -+#define CM_RESOURCE_MEMORY_WRITE_ONLY 0x0002 -+#define CM_RESOURCE_MEMORY_PREFETCHABLE 0x0004 -+ -+#define CM_RESOURCE_MEMORY_COMBINEDWRITE 0x0008 -+#define CM_RESOURCE_MEMORY_24 0x0010 -+#define CM_RESOURCE_MEMORY_CACHEABLE 0x0020 -+ -+#define CM_RESOURCE_PORT_MEMORY 0x0000 -+#define CM_RESOURCE_PORT_IO 0x0001 -+#define CM_RESOURCE_PORT_10_BIT_DECODE 0x0004 -+#define CM_RESOURCE_PORT_12_BIT_DECODE 0x0008 -+#define CM_RESOURCE_PORT_16_BIT_DECODE 0x0010 -+#define CM_RESOURCE_PORT_POSITIVE_DECODE 0x0020 -+#define CM_RESOURCE_PORT_PASSIVE_DECODE 0x0040 -+#define CM_RESOURCE_PORT_WINDOW_DECODE 0x0080 -+ -+#define CM_RESOURCE_DMA_8 0x0000 -+#define CM_RESOURCE_DMA_16 0x0001 -+#define CM_RESOURCE_DMA_32 0x0002 -+#define CM_RESOURCE_DMA_8_AND_16 0x0004 -+#define CM_RESOURCE_DMA_BUS_MASTER 0x0008 -+#define CM_RESOURCE_DMA_TYPE_A 0x0010 -+#define CM_RESOURCE_DMA_TYPE_B 0x0020 -+#define CM_RESOURCE_DMA_TYPE_F 0x0040 -+ -+#define MAX_RESOURCES 20 -+ -+#pragma pack(push,4) -+struct cm_partial_resource_descriptor { -+ UCHAR type; -+ UCHAR share; -+ USHORT flags; -+ union { -+ struct { -+ PHYSICAL_ADDRESS start; -+ ULONG length; -+ } generic; -+ struct { -+ PHYSICAL_ADDRESS start; -+ ULONG length; -+ } port; -+ struct { -+ ULONG level; -+ ULONG vector; -+ KAFFINITY affinity; -+ } interrupt; -+ struct { -+ PHYSICAL_ADDRESS start; -+ ULONG length; -+ } memory; -+ struct { -+ ULONG channel; -+ ULONG port; -+ ULONG reserved1; -+ } dma; -+ struct { -+ ULONG data[3]; -+ } device_private; -+ struct { -+ ULONG start; -+ ULONG length; -+ ULONG reserved; -+ } bus_number; -+ struct { -+ ULONG data_size; -+ ULONG reserved1; -+ ULONG reserved2; -+ } device_specific_data; -+ } u; -+}; -+#pragma pack(pop) -+ -+struct cm_partial_resource_list { -+ USHORT version; -+ USHORT revision; -+ ULONG count; -+ struct cm_partial_resource_descriptor partial_descriptors[1]; -+}; -+ -+struct cm_full_resource_descriptor { -+ enum interface_type interface_type; -+ ULONG bus_number; -+ struct cm_partial_resource_list partial_resource_list; -+}; -+ -+struct cm_resource_list { -+ ULONG count; -+ struct cm_full_resource_descriptor list[1]; -+}; -+ -+enum file_info_class { -+ FileDirectoryInformation = 1, -+ FileBasicInformation = 4, -+ FileStandardInformation = 5, -+ FileNameInformation = 9, -+ FilePositionInformation = 14, -+ FileAlignmentInformation = 17, -+ FileNetworkOpenInformation = 34, -+ FileAttributeTagInformation = 35, -+ FileMaximumInformation = 41, -+}; -+ -+enum fs_info_class { -+ FileFsVolumeInformation = 1, -+ /* ... */ -+ FileFsMaximumInformation = 9, -+}; -+ -+enum device_relation_type { -+ BusRelations, EjectionRelations, PowerRelations, RemovalRelations, -+ TargetDeviceRelation, SingleBusRelations, -+}; -+ -+enum bus_query_id_type { -+ BusQueryDeviceID = 0, BusQueryHardwareIDs = 1, -+ BusQueryCompatibleIDs = 2, BusQueryInstanceID = 3, -+ BusQueryDeviceSerialNumber = 4, -+}; -+ -+enum device_text_type { -+ DeviceTextDescription = 0, DeviceTextLocationInformation = 1, -+}; -+ -+enum device_usage_notification_type { -+ DeviceUsageTypeUndefined, DeviceUsageTypePaging, -+ DeviceUsageTypeHibernation, DevbiceUsageTypeDumpFile, -+}; -+ -+#define METHOD_BUFFERED 0 -+#define METHOD_IN_DIRECT 1 -+#define METHOD_OUT_DIRECT 2 -+#define METHOD_NEITHER 3 -+ -+#define CTL_CODE(dev_type, func, method, access) \ -+ (((dev_type) << 16) | ((access) << 14) | ((func) << 2) | (method)) -+ -+#define IO_METHOD_FROM_CTL_CODE(code) (code & 0x3) -+ -+#ifndef CONFIG_X86_64 -+#pragma pack(push,4) -+#endif -+struct io_stack_location { -+ UCHAR major_fn; -+ UCHAR minor_fn; -+ UCHAR flags; -+ UCHAR control; -+ union { -+ struct { -+ void *security_context; -+ ULONG options; -+ USHORT POINTER_ALIGN file_attributes; -+ USHORT share_access; -+ ULONG POINTER_ALIGN ea_length; -+ } create; -+ struct { -+ ULONG length; -+ ULONG POINTER_ALIGN key; -+ LARGE_INTEGER byte_offset; -+ } read; -+ struct { -+ ULONG length; -+ ULONG POINTER_ALIGN key; -+ LARGE_INTEGER byte_offset; -+ } write; -+ struct { -+ ULONG length; -+ enum file_info_class POINTER_ALIGN file_info_class; -+ } query_file; -+ struct { -+ ULONG length; -+ enum file_info_class POINTER_ALIGN file_info_class; -+ struct file_object *file_object; -+ union { -+ struct { -+ BOOLEAN replace_if_exists; -+ BOOLEAN advance_only; -+ }; -+ ULONG cluster_count; -+ void *delete_handle; -+ }; -+ } set_file; -+ struct { -+ ULONG length; -+ enum fs_info_class POINTER_ALIGN fs_info_class; -+ } query_volume; -+ struct { -+ ULONG output_buf_len; -+ ULONG POINTER_ALIGN input_buf_len; -+ ULONG POINTER_ALIGN code; -+ void *type3_input_buf; -+ } dev_ioctl; -+ struct { -+ SECURITY_INFORMATION security_info; -+ ULONG POINTER_ALIGN length; -+ } query_security; -+ struct { -+ SECURITY_INFORMATION security_info; -+ void *security_descriptor; -+ } set_security; -+ struct { -+ void *vpb; -+ struct device_object *device_object; -+ } mount_volume; -+ struct { -+ void *vpb; -+ struct device_object *device_object; -+ } verify_volume; -+ struct { -+ void *srb; -+ } scsi; -+ struct { -+ enum device_relation_type type; -+ } query_device_relations; -+ struct { -+ const struct guid *type; -+ USHORT size; -+ USHORT version; -+ struct nt_interface *intf; -+ void *intf_data; -+ } query_intf; -+ struct { -+ void *capabilities; -+ } device_capabilities; -+ struct { -+ void *io_resource_requirement_list; -+ } filter_resource_requirements; -+ struct { -+ ULONG which_space; -+ void *buffer; -+ ULONG offset; -+ ULONG POINTER_ALIGN length; -+ } read_write_config; -+ struct { -+ BOOLEAN lock; -+ } set_lock; -+ struct { -+ enum bus_query_id_type id_type; -+ } query_id; -+ struct { -+ enum device_text_type device_text_type; -+ ULONG POINTER_ALIGN locale_id; -+ } query_device_text; -+ struct { -+ BOOLEAN in_path; -+ BOOLEAN reserved[3]; -+ enum device_usage_notification_type POINTER_ALIGN type; -+ } usage_notification; -+ struct { -+ enum system_power_state power_state; -+ } wait_wake; -+ struct { -+ void *power_sequence; -+ } power_sequence; -+ struct { -+ ULONG sys_context; -+ enum power_state_type POINTER_ALIGN type; -+ union power_state POINTER_ALIGN state; -+ enum power_action POINTER_ALIGN shutdown_type; -+ } power; -+ struct { -+ struct cm_resource_list *allocated_resources; -+ struct cm_resource_list *allocated_resources_translated; -+ } start_device; -+ struct { -+ ULONG_PTR provider_id; -+ void *data_path; -+ ULONG buf_len; -+ void *buf; -+ } wmi; -+ struct { -+ void *arg1; -+ void *arg2; -+ void *arg3; -+ void *arg4; -+ } others; -+ } params; -+ struct device_object *dev_obj; -+ struct file_object *file_obj; -+ NTSTATUS (*completion_routine)(struct device_object *, -+ struct irp *, void *) wstdcall; -+ void *context; -+}; -+#ifndef CONFIG_X86_64 -+#pragma pack(pop) -+#endif -+ -+struct kapc { -+ CSHORT type; -+ CSHORT size; -+ ULONG spare0; -+ struct nt_thread *thread; -+ struct nt_list list; -+ void *kernele_routine; -+ void *rundown_routine; -+ void *normal_routine; -+ void *normal_context; -+ void *sys_arg1; -+ void *sys_arg2; -+ CCHAR apc_state_index; -+ KPROCESSOR_MODE apc_mode; -+ BOOLEAN inserted; -+}; -+ -+#define IRP_NOCACHE 0x00000001 -+#define IRP_SYNCHRONOUS_API 0x00000004 -+#define IRP_ASSOCIATED_IRP 0x00000008 -+ -+enum urb_state { -+ URB_INVALID = 1, URB_ALLOCATED, URB_SUBMITTED, -+ URB_COMPLETED, URB_FREE, URB_SUSPEND, URB_INT_UNLINKED }; -+ -+struct wrap_urb { -+ struct nt_list list; -+ enum urb_state state; -+ struct nt_list complete_list; -+ unsigned int flags; -+ struct urb *urb; -+ struct irp *irp; -+#ifdef USB_DEBUG -+ unsigned int id; -+#endif -+}; -+ -+struct irp { -+ SHORT type; -+ USHORT size; -+ struct mdl *mdl; -+ ULONG flags; -+ union { -+ struct irp *master_irp; -+ LONG irp_count; -+ void *system_buffer; -+ } associated_irp; -+ struct nt_list thread_list; -+ struct io_status_block io_status; -+ KPROCESSOR_MODE requestor_mode; -+ BOOLEAN pending_returned; -+ CHAR stack_count; -+ CHAR current_location; -+ BOOLEAN cancel; -+ KIRQL cancel_irql; -+ CCHAR apc_env; -+ UCHAR alloc_flags; -+ struct io_status_block *user_status; -+ struct nt_event *user_event; -+ union { -+ struct { -+ void *user_apc_routine; -+ void *user_apc_context; -+ } async_params; -+ LARGE_INTEGER alloc_size; -+ } overlay; -+ void (*cancel_routine)(struct device_object *, struct irp *) wstdcall; -+ void *user_buf; -+ union { -+ struct { -+ union { -+ struct kdevice_queue_entry dev_q_entry; -+ struct { -+ void *driver_context[4]; -+ }; -+ }; -+ void *thread; -+ char *aux_buf; -+ struct { -+ struct nt_list list; -+ union { -+ struct io_stack_location *csl; -+ ULONG packet_type; -+ }; -+ }; -+ struct file_object *file_object; -+ } overlay; -+ union { -+ struct kapc apc; -+ /* space for apc is used for ndiswrapper -+ * specific fields */ -+ struct { -+ struct wrap_urb *wrap_urb; -+ struct wrap_device *wrap_device; -+ }; -+ }; -+ void *completion_key; -+ } tail; -+}; -+ -+#define IoSizeOfIrp(stack_count) \ -+ ((USHORT)(sizeof(struct irp) + \ -+ ((stack_count) * sizeof(struct io_stack_location)))) -+#define IoGetCurrentIrpStackLocation(irp) \ -+ (irp)->tail.overlay.csl -+#define IoGetNextIrpStackLocation(irp) \ -+ (IoGetCurrentIrpStackLocation(irp) - 1) -+#define IoGetPreviousIrpStackLocation(irp) \ -+ (IoGetCurrentIrpStackLocation(irp) + 1) -+ -+#define IoSetNextIrpStackLocation(irp) \ -+do { \ -+ KIRQL _irql_; \ -+ IoAcquireCancelSpinLock(&_irql_); \ -+ (irp)->current_location--; \ -+ IoGetCurrentIrpStackLocation(irp)--; \ -+ IoReleaseCancelSpinLock(_irql_); \ -+} while (0) -+ -+#define IoSkipCurrentIrpStackLocation(irp) \ -+do { \ -+ KIRQL _irql_; \ -+ IoAcquireCancelSpinLock(&_irql_); \ -+ (irp)->current_location++; \ -+ IoGetCurrentIrpStackLocation(irp)++; \ -+ IoReleaseCancelSpinLock(_irql_); \ -+} while (0) -+ -+static inline void -+IoCopyCurrentIrpStackLocationToNext(struct irp *irp) -+{ -+ struct io_stack_location *next; -+ next = IoGetNextIrpStackLocation(irp); -+ memcpy(next, IoGetCurrentIrpStackLocation(irp), -+ offsetof(struct io_stack_location, completion_routine)); -+ next->control = 0; -+} -+ -+static inline void -+IoSetCompletionRoutine(struct irp *irp, void *routine, void *context, -+ BOOLEAN success, BOOLEAN error, BOOLEAN cancel) -+{ -+ struct io_stack_location *irp_sl = IoGetNextIrpStackLocation(irp); -+ irp_sl->completion_routine = routine; -+ irp_sl->context = context; -+ irp_sl->control = 0; -+ if (success) -+ irp_sl->control |= SL_INVOKE_ON_SUCCESS; -+ if (error) -+ irp_sl->control |= SL_INVOKE_ON_ERROR; -+ if (cancel) -+ irp_sl->control |= SL_INVOKE_ON_CANCEL; -+} -+ -+#define IoMarkIrpPending(irp) \ -+ (IoGetCurrentIrpStackLocation((irp))->control |= SL_PENDING_RETURNED) -+#define IoUnmarkIrpPending(irp) \ -+ (IoGetCurrentIrpStackLocation((irp))->control &= ~SL_PENDING_RETURNED) -+ -+#define IRP_SL(irp, n) (((struct io_stack_location *)((irp) + 1)) + (n)) -+#define IRP_DRIVER_CONTEXT(irp) (irp)->tail.overlay.driver_context -+#define IoIrpThread(irp) ((irp)->tail.overlay.thread) -+ -+#define IRP_URB(irp) \ -+ (union nt_urb *)(IoGetCurrentIrpStackLocation(irp)->params.others.arg1) -+ -+#define IRP_WRAP_DEVICE(irp) (irp)->tail.wrap_device -+#define IRP_WRAP_URB(irp) (irp)->tail.wrap_urb -+ -+struct wmi_guid_reg_info { -+ struct guid *guid; -+ ULONG instance_count; -+ ULONG flags; -+}; -+ -+struct wmilib_context { -+ ULONG guid_count; -+ struct wmi_guid_reg_info *guid_list; -+ void *query_wmi_reg_info; -+ void *query_wmi_data_block; -+ void *set_wmi_data_block; -+ void *set_wmi_data_item; -+ void *execute_wmi_method; -+ void *wmi_function_control; -+}; -+ -+enum key_value_information_class { -+ KeyValueBasicInformation, KeyValueFullInformation, -+ KeyValuePartialInformation, KeyValueFullInformationAlign64, -+ KeyValuePartialInformationAlign64 -+}; -+ -+struct file_name_info { -+ ULONG length; -+ wchar_t *name; -+}; -+ -+struct file_std_info { -+ LARGE_INTEGER alloc_size; -+ LARGE_INTEGER eof; -+ ULONG num_links; -+ BOOLEAN delete_pending; -+ BOOLEAN dir; -+}; -+ -+enum nt_obj_type { -+ NT_OBJ_EVENT = 10, NT_OBJ_MUTEX, NT_OBJ_THREAD, NT_OBJ_TIMER, -+ NT_OBJ_SEMAPHORE, -+}; -+ -+enum common_object_type { -+ OBJECT_TYPE_NONE, OBJECT_TYPE_DEVICE, OBJECT_TYPE_DRIVER, -+ OBJECT_TYPE_NT_THREAD, OBJECT_TYPE_FILE, OBJECT_TYPE_CALLBACK, -+}; -+ -+struct common_object_header { -+ struct nt_list list; -+ enum common_object_type type; -+ UINT size; -+ UINT ref_count; -+ BOOLEAN close_in_process; -+ BOOLEAN permanent; -+ struct unicode_string name; -+}; -+ -+#define OBJECT_TO_HEADER(object) \ -+ (struct common_object_header *)((void *)(object) - \ -+ sizeof(struct common_object_header)) -+#define OBJECT_SIZE(size) \ -+ ((size) + sizeof(struct common_object_header)) -+#define HEADER_TO_OBJECT(hdr) \ -+ ((void *)(hdr) + sizeof(struct common_object_header)) -+#define HANDLE_TO_OBJECT(handle) HEADER_TO_OBJECT(handle) -+#define HANDLE_TO_HEADER(handle) (handle) -+ -+enum work_queue_type { -+ CriticalWorkQueue, DelayedWorkQueue, HyperCriticalWorkQueue, -+ MaximumWorkQueue -+}; -+ -+typedef void (*NTOS_WORK_FUNC)(void *arg1, void *arg2) wstdcall; -+ -+struct io_workitem { -+ enum work_queue_type type; -+ struct device_object *dev_obj; -+ NTOS_WORK_FUNC worker_routine; -+ void *context; -+}; -+ -+struct io_workitem_entry { -+ struct nt_list list; -+ struct io_workitem *io_workitem; -+}; -+ -+enum mm_page_priority { -+ LowPagePriority, NormalPagePriority = 16, HighPagePriority = 32 -+}; -+ -+enum kinterrupt_mode { -+ LevelSensitive, Latched -+}; -+ -+enum ntos_wait_reason { -+ Executive, FreePage, PageIn, PoolAllocation, DelayExecution, -+ Suspended, UserRequest, WrExecutive, WrFreePage, WrPageIn, -+ WrPoolAllocation, WrDelayExecution, WrSuspended, WrUserRequest, -+ WrEventPair, WrQueue, WrLpcReceive, WrLpcReply, WrVirtualMemory, -+ WrPageOut, WrRendezvous, Spare2, Spare3, Spare4, Spare5, Spare6, -+ WrKernel, MaximumWaitReason -+}; -+ -+typedef enum ntos_wait_reason KWAIT_REASON; -+ -+typedef void *LOOKASIDE_ALLOC_FUNC(enum pool_type pool_type, -+ SIZE_T size, ULONG tag) wstdcall; -+typedef void LOOKASIDE_FREE_FUNC(void *) wstdcall; -+ -+struct npaged_lookaside_list { -+ nt_slist_header head; -+ USHORT depth; -+ USHORT maxdepth; -+ ULONG totalallocs; -+ union { -+ ULONG allocmisses; -+ ULONG allochits; -+ } u1; -+ ULONG totalfrees; -+ union { -+ ULONG freemisses; -+ ULONG freehits; -+ } u2; -+ enum pool_type pool_type; -+ ULONG tag; -+ ULONG size; -+ LOOKASIDE_ALLOC_FUNC *alloc_func; -+ LOOKASIDE_FREE_FUNC *free_func; -+ struct nt_list list; -+ ULONG lasttotallocs; -+ union { -+ ULONG lastallocmisses; -+ ULONG lastallochits; -+ } u3; -+ ULONG pad[2]; -+#ifndef CONFIG_X86_64 -+ NT_SPIN_LOCK obsolete; -+#endif -+} -+#ifdef CONFIG_X86_64 -+CACHE_ALIGN -+#endif -+; -+ -+enum device_registry_property { -+ DevicePropertyDeviceDescription, DevicePropertyHardwareID, -+ DevicePropertyCompatibleIDs, DevicePropertyBootConfiguration, -+ DevicePropertyBootConfigurationTranslated, -+ DevicePropertyClassName, DevicePropertyClassGuid, -+ DevicePropertyDriverKeyName, DevicePropertyManufacturer, -+ DevicePropertyFriendlyName, DevicePropertyLocationInformation, -+ DevicePropertyPhysicalDeviceObjectName, DevicePropertyBusTypeGuid, -+ DevicePropertyLegacyBusType, DevicePropertyBusNumber, -+ DevicePropertyEnumeratorName, DevicePropertyAddress, -+ DevicePropertyUINumber, DevicePropertyInstallState, -+ DevicePropertyRemovalPolicy -+}; -+ -+enum trace_information_class { -+ TraceIdClass, TraceHandleClass, TraceEnableFlagsClass, -+ TraceEnableLevelClass, GlobalLoggerHandleClass, EventLoggerHandleClass, -+ AllLoggerHandlesClass, TraceHandleByNameClass -+}; -+ -+struct kinterrupt; -+typedef BOOLEAN (*PKSERVICE_ROUTINE)(struct kinterrupt *interrupt, -+ void *context) wstdcall; -+typedef BOOLEAN (*PKSYNCHRONIZE_ROUTINE)(void *context) wstdcall; -+ -+struct kinterrupt { -+ ULONG vector; -+ KAFFINITY cpu_mask; -+ NT_SPIN_LOCK lock; -+ NT_SPIN_LOCK *actual_lock; -+ BOOLEAN shared; -+ BOOLEAN save_fp; -+ union { -+ CHAR processor_number; -+#ifdef CONFIG_DEBUG_SHIRQ -+ CHAR enabled; -+#endif -+ } u; -+ PKSERVICE_ROUTINE isr; -+ void *isr_ctx; -+ struct nt_list list; -+ KIRQL irql; -+ KIRQL synch_irql; -+ enum kinterrupt_mode mode; -+}; -+ -+struct time_fields { -+ CSHORT year; -+ CSHORT month; -+ CSHORT day; -+ CSHORT hour; -+ CSHORT minute; -+ CSHORT second; -+ CSHORT milliseconds; -+ CSHORT weekday; -+}; -+ -+struct object_attributes { -+ ULONG length; -+ void *root_dir; -+ struct unicode_string *name; -+ ULONG attributes; -+ void *security_descr; -+ void *security_qos; -+}; -+ -+typedef void (*PCALLBACK_FUNCTION)(void *context, void *arg1, -+ void *arg2) wstdcall; -+ -+struct callback_object; -+struct callback_func { -+ PCALLBACK_FUNCTION func; -+ void *context; -+ struct nt_list list; -+ struct callback_object *object; -+}; -+ -+struct callback_object { -+ NT_SPIN_LOCK lock; -+ struct nt_list list; -+ struct nt_list callback_funcs; -+ BOOLEAN allow_multiple_callbacks; -+ struct object_attributes *attributes; -+}; -+ -+enum section_inherit { -+ ViewShare = 1, ViewUnmap = 2 -+}; -+ -+struct ksystem_time { -+ ULONG low_part; -+ LONG high1_time; -+ LONG high2_time; -+}; -+ -+enum nt_product_type { -+ nt_product_win_nt = 1, nt_product_lan_man_nt, nt_product_server -+}; -+ -+enum alt_arch_type { -+ arch_type_standard, arch_type_nex98x86, end_alternatives -+}; -+ -+struct kuser_shared_data { -+ ULONG tick_count; -+ ULONG tick_count_multiplier; -+ volatile struct ksystem_time interrupt_time; -+ volatile struct ksystem_time system_time; -+ volatile struct ksystem_time time_zone_bias; -+ USHORT image_number_low; -+ USHORT image_number_high; -+ wchar_t nt_system_root[260]; -+ ULONG max_stack_trace_depth; -+ ULONG crypto_exponent; -+ ULONG time_zone_id; -+ ULONG large_page_min; -+ ULONG reserved2[7]; -+ enum nt_product_type nt_product_type; -+ BOOLEAN product_type_is_valid; -+ ULONG nt_major_version; -+ ULONG nt_minor_version; -+ BOOLEAN processor_features[PROCESSOR_FEATURE_MAX]; -+ ULONG reserved1; -+ ULONG reserved3; -+ volatile LONG time_slip; -+ enum alt_arch_type alt_arch_type; -+ LARGE_INTEGER system_expiration_date; -+ ULONG suite_mask; -+ BOOLEAN kdbg_enabled; -+ volatile ULONG active_console; -+ volatile ULONG dismount_count; -+ ULONG com_plus_package; -+ ULONG last_system_rite_event_tick_count; -+ ULONG num_phys_pages; -+ BOOLEAN safe_boot_mode; -+ ULONG trace_log; -+ ULONGLONG fill0; -+ ULONGLONG sys_call[4]; -+ union { -+ volatile struct ksystem_time tick_count; -+ volatile ULONG64 tick_count_quad; -+ } tick; -+}; -+ -+#define REG_NONE (0) -+#define REG_SZ (1) -+#define REG_EXPAND_SZ (2) -+#define REG_BINARY (3) -+#define REG_DWORD (4) -+ -+#define RTL_REGISTRY_ABSOLUTE 0 -+#define RTL_REGISTRY_SERVICES 1 -+#define RTL_REGISTRY_CONTROL 2 -+#define RTL_REGISTRY_WINDOWS_NT 3 -+#define RTL_REGISTRY_DEVICEMAP 4 -+#define RTL_REGISTRY_USER 5 -+#define RTL_REGISTRY_MAXIMUM 6 -+#define RTL_REGISTRY_HANDLE 0x40000000 -+#define RTL_REGISTRY_OPTIONAL 0x80000000 -+ -+#define RTL_QUERY_REGISTRY_SUBKEY 0x00000001 -+#define RTL_QUERY_REGISTRY_TOPKEY 0x00000002 -+#define RTL_QUERY_REGISTRY_REQUIRED 0x00000004 -+#define RTL_QUERY_REGISTRY_NOVALUE 0x00000008 -+#define RTL_QUERY_REGISTRY_NOEXPAND 0x00000010 -+#define RTL_QUERY_REGISTRY_DIRECT 0x00000020 -+#define RTL_QUERY_REGISTRY_DELETE 0x00000040 -+ -+typedef NTSTATUS (*PRTL_QUERY_REGISTRY_ROUTINE)(wchar_t *name, ULONG type, -+ void *data, ULONG length, -+ void *context, -+ void *entry) wstdcall; -+ -+struct rtl_query_registry_table { -+ PRTL_QUERY_REGISTRY_ROUTINE query_func; -+ ULONG flags; -+ wchar_t *name; -+ void *context; -+ ULONG def_type; -+ void *def_data; -+ ULONG def_length; -+}; -+ -+struct io_remove_lock { -+ BOOLEAN removed; -+ BOOLEAN reserved[3]; -+ LONG io_count; -+ struct nt_event remove_event; -+}; -+ -+struct io_error_log_packet { -+ UCHAR major_fn_code; -+ UCHAR retry_count; -+ USHORT dump_data_size; -+ USHORT nr_of_strings; -+ USHORT string_offset; -+ USHORT event_category; -+ NTSTATUS error_code; -+ ULONG unique_error_value; -+ NTSTATUS final_status; -+ ULONG sequence_number; -+ ULONG io_control_code; -+ LARGE_INTEGER device_offset; -+ ULONG dump_data[1]; -+}; -+ -+/* some of the functions below are slightly different from DDK's -+ * implementation; e.g., Insert functions return appropriate -+ * pointer */ -+ -+/* instead of using Linux's lists, we implement list manipulation -+ * functions because nt_list is used by drivers and we don't want to -+ * worry about Linux's list being different from nt_list (right now -+ * they are same, but in future they could be different) */ -+ -+static inline void InitializeListHead(struct nt_list *head) -+{ -+ head->next = head->prev = head; -+} -+ -+static inline BOOLEAN IsListEmpty(struct nt_list *head) -+{ -+ if (head == head->next) -+ return TRUE; -+ else -+ return FALSE; -+} -+ -+static inline void RemoveEntryList(struct nt_list *entry) -+{ -+ entry->prev->next = entry->next; -+ entry->next->prev = entry->prev; -+} -+ -+static inline struct nt_list *RemoveHeadList(struct nt_list *head) -+{ -+ struct nt_list *entry; -+ -+ entry = head->next; -+ if (entry == head) -+ return NULL; -+ else { -+ RemoveEntryList(entry); -+ return entry; -+ } -+} -+ -+static inline struct nt_list *RemoveTailList(struct nt_list *head) -+{ -+ struct nt_list *entry; -+ -+ entry = head->prev; -+ if (entry == head) -+ return NULL; -+ else { -+ RemoveEntryList(entry); -+ return entry; -+ } -+} -+ -+static inline void InsertListEntry(struct nt_list *entry, struct nt_list *prev, -+ struct nt_list *next) -+{ -+ next->prev = entry; -+ entry->next = next; -+ entry->prev = prev; -+ prev->next = entry; -+} -+ -+static inline struct nt_list *InsertHeadList(struct nt_list *head, -+ struct nt_list *entry) -+{ -+ struct nt_list *ret; -+ -+ if (IsListEmpty(head)) -+ ret = NULL; -+ else -+ ret = head->next; -+ -+ InsertListEntry(entry, head, head->next); -+ return ret; -+} -+ -+static inline struct nt_list *InsertTailList(struct nt_list *head, -+ struct nt_list *entry) -+{ -+ struct nt_list *ret; -+ -+ if (IsListEmpty(head)) -+ ret = NULL; -+ else -+ ret = head->prev; -+ -+ InsertListEntry(entry, head->prev, head); -+ return ret; -+} -+ -+#define nt_list_for_each(pos, head) \ -+ for (pos = (head)->next; pos != (head); pos = pos->next) -+ -+#define nt_list_for_each_entry(pos, head, member) \ -+ for (pos = container_of((head)->next, typeof(*pos), member); \ -+ &pos->member != (head); \ -+ pos = container_of(pos->member.next, typeof(*pos), member)) -+ -+#define nt_list_for_each_safe(pos, n, head) \ -+ for (pos = (head)->next, n = pos->next; pos != (head); \ -+ pos = n, n = pos->next) -+ -+/* device object flags */ -+#define DO_VERIFY_VOLUME 0x00000002 -+#define DO_BUFFERED_IO 0x00000004 -+#define DO_EXCLUSIVE 0x00000008 -+#define DO_DIRECT_IO 0x00000010 -+#define DO_MAP_IO_BUFFER 0x00000020 -+#define DO_DEVICE_HAS_NAME 0x00000040 -+#define DO_DEVICE_INITIALIZING 0x00000080 -+#define DO_SYSTEM_BOOT_PARTITION 0x00000100 -+#define DO_LONG_TERM_REQUESTS 0x00000200 -+#define DO_NEVER_LAST_DEVICE 0x00000400 -+#define DO_SHUTDOWN_REGISTERED 0x00000800 -+#define DO_BUS_ENUMERATED_DEVICE 0x00001000 -+#define DO_POWER_PAGABLE 0x00002000 -+#define DO_POWER_INRUSH 0x00004000 -+#define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 -+ -+/* Various supported device types (used with IoCreateDevice()) */ -+ -+#define FILE_DEVICE_BEEP 0x00000001 -+#define FILE_DEVICE_CD_ROM 0x00000002 -+#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 -+#define FILE_DEVICE_CONTROLLER 0x00000004 -+#define FILE_DEVICE_DATALINK 0x00000005 -+#define FILE_DEVICE_DFS 0x00000006 -+#define FILE_DEVICE_DISK 0x00000007 -+#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 -+#define FILE_DEVICE_FILE_SYSTEM 0x00000009 -+#define FILE_DEVICE_INPORT_PORT 0x0000000A -+#define FILE_DEVICE_KEYBOARD 0x0000000B -+#define FILE_DEVICE_MAILSLOT 0x0000000C -+#define FILE_DEVICE_MIDI_IN 0x0000000D -+#define FILE_DEVICE_MIDI_OUT 0x0000000E -+#define FILE_DEVICE_MOUSE 0x0000000F -+#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 -+#define FILE_DEVICE_NAMED_PIPE 0x00000011 -+#define FILE_DEVICE_NETWORK 0x00000012 -+#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 -+#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 -+#define FILE_DEVICE_NULL 0x00000015 -+#define FILE_DEVICE_PARALLEL_PORT 0x00000016 -+#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 -+#define FILE_DEVICE_PRINTER 0x00000018 -+#define FILE_DEVICE_SCANNER 0x00000019 -+#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001A -+#define FILE_DEVICE_SERIAL_PORT 0x0000001B -+#define FILE_DEVICE_SCREEN 0x0000001C -+#define FILE_DEVICE_SOUND 0x0000001D -+#define FILE_DEVICE_STREAMS 0x0000001E -+#define FILE_DEVICE_TAPE 0x0000001F -+#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 -+#define FILE_DEVICE_TRANSPORT 0x00000021 -+#define FILE_DEVICE_UNKNOWN 0x00000022 -+#define FILE_DEVICE_VIDEO 0x00000023 -+#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 -+#define FILE_DEVICE_WAVE_IN 0x00000025 -+#define FILE_DEVICE_WAVE_OUT 0x00000026 -+#define FILE_DEVICE_8042_PORT 0x00000027 -+#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 -+#define FILE_DEVICE_BATTERY 0x00000029 -+#define FILE_DEVICE_BUS_EXTENDER 0x0000002A -+#define FILE_DEVICE_MODEM 0x0000002B -+#define FILE_DEVICE_VDM 0x0000002C -+#define FILE_DEVICE_MASS_STORAGE 0x0000002D -+#define FILE_DEVICE_SMB 0x0000002E -+#define FILE_DEVICE_KS 0x0000002F -+#define FILE_DEVICE_CHANGER 0x00000030 -+#define FILE_DEVICE_SMARTCARD 0x00000031 -+#define FILE_DEVICE_ACPI 0x00000032 -+#define FILE_DEVICE_DVD 0x00000033 -+#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 -+#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 -+#define FILE_DEVICE_DFS_VOLUME 0x00000036 -+#define FILE_DEVICE_SERENUM 0x00000037 -+#define FILE_DEVICE_TERMSRV 0x00000038 -+#define FILE_DEVICE_KSEC 0x00000039 -+#define FILE_DEVICE_FIPS 0x0000003A -+ -+/* Device characteristics */ -+ -+#define FILE_REMOVABLE_MEDIA 0x00000001 -+#define FILE_READ_ONLY_DEVICE 0x00000002 -+#define FILE_FLOPPY_DISKETTE 0x00000004 -+#define FILE_WRITE_ONCE_MEDIA 0x00000008 -+#define FILE_REMOTE_DEVICE 0x00000010 -+#define FILE_DEVICE_IS_MOUNTED 0x00000020 -+#define FILE_VIRTUAL_VOLUME 0x00000040 -+#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 -+#define FILE_DEVICE_SECURE_OPEN 0x00000100 -+ -+#define FILE_READ_DATA 0x0001 -+#define FILE_WRITE_DATA 0x0002 -+ -+#define FILE_SUPERSEDED 0x00000000 -+#define FILE_OPENED 0x00000001 -+#define FILE_CREATED 0x00000002 -+#define FILE_OVERWRITTEN 0x00000003 -+#define FILE_EXISTS 0x00000004 -+#define FILE_DOES_NOT_EXIST 0x00000005 -+ -+ -+#endif /* WINNT_TYPES_H */ -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/workqueue.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/workqueue.c ---- linux-3.12.2/3rdparty/ndiswrapper/workqueue.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/workqueue.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,279 @@ -+/* -+ * Copyright (C) 2006 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ntoskernel.h" -+ -+struct workqueue_thread { -+ spinlock_t lock; -+ struct task_struct *task; -+ struct completion *completion; -+ char name[16]; -+ int pid; -+ /* whether any work_structs pending? <0 implies quit */ -+ s8 pending; -+ /* list of work_structs pending */ -+ struct list_head work_list; -+}; -+ -+struct workq_thread_data { -+ struct workqueue_struct *workq; -+ int index; -+}; -+ -+struct wrap_workqueue_struct { -+ u8 singlethread; -+ u8 qon; -+ int num_cpus; -+ struct workqueue_thread threads[0]; -+}; -+ -+static void wrap_destroy_wq_on(struct workqueue_struct *workq, int cpu); -+ -+static int workq_thread(void *data) -+{ -+ struct workq_thread_data *thread_data = data; -+ struct workqueue_thread *thread; -+ struct workqueue_struct *workq; -+ struct work_struct *work; -+ -+ workq = thread_data->workq; -+ thread = &workq->threads[thread_data->index]; -+ WORKTRACE("%p, %d, %p", workq, thread_data->index, thread); -+ strncpy(thread->name, current->comm, sizeof(thread->name)); -+ -+ daemonize(thread->name); -+ set_user_nice(current, -5); -+ -+ if (thread->task != current) { -+ WARNING("invalid task: %p, %p", thread->task, current); -+ thread->task = current; -+ } -+ thread->pid = current->pid; -+ complete(xchg(&thread->completion, NULL)); -+ WORKTRACE("%s (%d) started", thread->name, thread->pid); -+ while (1) { -+ if (wait_condition(thread->pending, 0, TASK_INTERRUPTIBLE) < 0) { -+ /* TODO: deal with signal */ -+ WARNING("signal not blocked?"); -+ flush_signals(current); -+ continue; -+ } -+ while (1) { -+ struct list_head *entry; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&thread->lock, flags); -+ if (list_empty(&thread->work_list)) { -+ struct completion *completion; -+ if (thread->pending < 0) { -+ spin_unlock_irqrestore(&thread->lock, -+ flags); -+ goto out; -+ } -+ thread->pending = 0; -+ completion = thread->completion; -+ thread->completion = NULL; -+ spin_unlock_irqrestore(&thread->lock, flags); -+ if (completion) -+ complete(completion); -+ break; -+ } -+ entry = thread->work_list.next; -+ work = list_entry(entry, struct work_struct, list); -+ if (xchg(&work->thread, NULL)) -+ list_del(entry); -+ else -+ work = NULL; -+ spin_unlock_irqrestore(&thread->lock, flags); -+ DBG_BLOCK(4) { -+ WORKTRACE("%p, %p", work, thread); -+ } -+ if (work) -+ work->func(work->data); -+ } -+ } -+ -+out: -+ WORKTRACE("%s exiting", thread->name); -+ thread->pid = 0; -+ return 0; -+} -+ -+static int wrap_queue_work_on(struct workqueue_struct *workq, -+ struct work_struct *work, int cpu) -+{ -+ struct workqueue_thread *thread = &workq->threads[cpu]; -+ unsigned long flags; -+ int ret; -+ -+ assert(thread->pid > 0); -+ DBG_BLOCK(4) { -+ WORKTRACE("%p, %d", workq, cpu); -+ } -+ spin_lock_irqsave(&thread->lock, flags); -+ if (work->thread) -+ ret = 0; -+ else { -+ work->thread = thread; -+ list_add_tail(&work->list, &thread->work_list); -+ thread->pending = 1; -+ wake_up_process(thread->task); -+ ret = 1; -+ } -+ spin_unlock_irqrestore(&thread->lock, flags); -+ return ret; -+} -+ -+int wrap_queue_work(struct workqueue_struct *workq, struct work_struct *work) -+{ -+ if (num_online_cpus() == 1 || workq->singlethread) -+ return wrap_queue_work_on(workq, work, 0); -+ else { -+ typeof(workq->qon) qon; -+ /* work is queued on threads in a round-robin fashion */ -+ do { -+ qon = workq->qon % workq->num_cpus; -+ atomic_inc_var(workq->qon); -+ } while (!workq->threads[qon].pid); -+ return wrap_queue_work_on(workq, work, qon); -+ } -+} -+ -+void wrap_cancel_work(struct work_struct *work) -+{ -+ struct workqueue_thread *thread; -+ unsigned long flags; -+ -+ WORKTRACE("%p", work); -+ if ((thread = xchg(&work->thread, NULL))) { -+ WORKTRACE("%p", thread); -+ spin_lock_irqsave(&thread->lock, flags); -+ list_del(&work->list); -+ spin_unlock_irqrestore(&thread->lock, flags); -+ } -+} -+ -+struct workqueue_struct *wrap_create_wq(const char *name, u8 singlethread, -+ u8 freeze) -+{ -+ struct completion started; -+ struct workqueue_struct *workq; -+ int i, n; -+ -+ if (singlethread) -+ n = 1; -+ else -+ n = num_online_cpus(); -+ workq = kzalloc(sizeof(*workq) + n * sizeof(workq->threads[0]), -+ GFP_KERNEL); -+ if (!workq) { -+ WARNING("couldn't allocate memory"); -+ return NULL; -+ } -+ WORKTRACE("%p", workq); -+ workq->singlethread = singlethread; -+ init_completion(&started); -+ for_each_online_cpu(i) { -+ struct workq_thread_data thread_data; -+ spin_lock_init(&workq->threads[i].lock); -+ INIT_LIST_HEAD(&workq->threads[i].work_list); -+ reinit_completion(&started); -+ workq->threads[i].completion = &started; -+ thread_data.workq = workq; -+ thread_data.index = i; -+ WORKTRACE("%p, %d, %p", workq, i, &workq->threads[i]); -+ workq->threads[i].task = -+ kthread_create(workq_thread, &thread_data, -+ "%s/%d", name, i); -+ if (IS_ERR(workq->threads[i].task)) { -+ int j; -+ for (j = 0; j < i; j++) -+ wrap_destroy_wq_on(workq, j); -+ kfree(workq); -+ WARNING("couldn't start thread %s", name); -+ return NULL; -+ } -+#ifdef PF_NOFREEZE -+ if (!freeze) -+ workq->threads[i].task->flags |= PF_NOFREEZE; -+#endif -+ kthread_bind(workq->threads[i].task, i); -+ workq->num_cpus = max(workq->num_cpus, i); -+ wake_up_process(workq->threads[i].task); -+ wait_for_completion(&started); -+ WORKTRACE("%s, %d: %p, %d", name, i, -+ workq, workq->threads[i].pid); -+ if (singlethread) -+ break; -+ } -+ workq->num_cpus++; -+ return workq; -+} -+ -+static void wrap_flush_wq_on(struct workqueue_struct *workq, int cpu) -+{ -+ struct workqueue_thread *thread = &workq->threads[cpu]; -+ struct completion done; -+ -+ WORKTRACE("%p: %d, %s", workq, cpu, thread->name); -+ init_completion(&done); -+ thread->completion = &done; -+ thread->pending = 1; -+ wake_up_process(thread->task); -+ wait_for_completion(&done); -+ return; -+} -+ -+void wrap_flush_wq(struct workqueue_struct *workq) -+{ -+ int i, n; -+ -+ WORKTRACE("%p", workq); -+ if (workq->singlethread) -+ n = 1; -+ else -+ n = num_online_cpus(); -+ for (i = 0; i < n; i++) -+ wrap_flush_wq_on(workq, i); -+} -+ -+static void wrap_destroy_wq_on(struct workqueue_struct *workq, int cpu) -+{ -+ struct workqueue_thread *thread = &workq->threads[cpu]; -+ -+ WORKTRACE("%p: %d, %s", workq, cpu, thread->name); -+ if (!thread->pid) -+ return; -+ thread->pending = -1; -+ wake_up_process(thread->task); -+ while (thread->pid) { -+ WORKTRACE("%d", thread->pid); -+ schedule(); -+ } -+} -+ -+void wrap_destroy_wq(struct workqueue_struct *workq) -+{ -+ int i, n; -+ -+ WORKTRACE("%p", workq); -+ if (workq->singlethread) -+ n = 1; -+ else -+ n = num_online_cpus(); -+ for (i = 0; i < n; i++) -+ wrap_destroy_wq_on(workq, i); -+ kfree(workq); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.c ---- linux-3.12.2/3rdparty/ndiswrapper/wrapmem.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,366 @@ -+/* -+ * Copyright (C) 2006 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#define _WRAPMEM_C_ -+ -+#include "ntoskernel.h" -+#include "wrapmem.h" -+ -+struct slack_alloc_info { -+ struct nt_list list; -+ size_t size; -+}; -+ -+#if ALLOC_DEBUG > 1 -+static struct nt_list allocs; -+#endif -+ -+static struct nt_list slack_allocs; -+static spinlock_t alloc_lock; -+ -+#if ALLOC_DEBUG -+const char *alloc_type_name[ALLOC_TYPE_MAX] = { -+ "kmalloc_atomic", -+ "kmalloc_nonatomic", -+ "vmalloc_atomic", -+ "vmalloc_nonatomic", -+ "kmalloc_slack", -+ "pages" -+}; -+ -+struct alloc_info { -+ enum alloc_type type; -+ size_t size; -+#if ALLOC_DEBUG > 1 -+ struct nt_list list; -+ const char *file; -+ int line; -+ ULONG tag; -+#endif -+}; -+ -+static atomic_t alloc_sizes[ALLOC_TYPE_MAX]; -+#endif -+ -+/* allocate memory and add it to list of allocated pointers; if a -+ * driver doesn't free this memory for any reason (buggy driver or we -+ * allocate space behind driver's back since we need more space than -+ * corresponding Windows structure provides etc.), this gets freed -+ * automatically when module is unloaded -+ */ -+void *slack_kmalloc(size_t size) -+{ -+ struct slack_alloc_info *info; -+ -+ ENTER4("size = %zu", size); -+ info = kmalloc(size + sizeof(*info), irql_gfp()); -+ if (!info) -+ return NULL; -+ info->size = size; -+ spin_lock_bh(&alloc_lock); -+ InsertTailList(&slack_allocs, &info->list); -+ spin_unlock_bh(&alloc_lock); -+#if ALLOC_DEBUG -+ atomic_add(size, &alloc_sizes[ALLOC_TYPE_SLACK]); -+#endif -+ TRACE4("%p, %p", info, info + 1); -+ EXIT4(return info + 1); -+} -+ -+/* free pointer and remove from list of allocated pointers */ -+void slack_kfree(void *ptr) -+{ -+ struct slack_alloc_info *info; -+ -+ ENTER4("%p", ptr); -+ info = ptr - sizeof(*info); -+ spin_lock_bh(&alloc_lock); -+ RemoveEntryList(&info->list); -+ spin_unlock_bh(&alloc_lock); -+#if ALLOC_DEBUG -+ atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]); -+#endif -+ kfree(info); -+ EXIT4(return); -+} -+ -+void *slack_kzalloc(size_t size) -+{ -+ void *ptr = slack_kmalloc(size); -+ if (ptr) -+ memset(ptr, 0, size); -+ return ptr; -+} -+ -+#if ALLOC_DEBUG -+void *wrap_kmalloc(size_t size, gfp_t flags, const char *file, int line) -+{ -+ struct alloc_info *info; -+ -+ info = kmalloc(size + sizeof(*info), flags); -+ if (!info) -+ return NULL; -+ if (flags & GFP_ATOMIC) -+ info->type = ALLOC_TYPE_KMALLOC_ATOMIC; -+ else -+ info->type = ALLOC_TYPE_KMALLOC_NON_ATOMIC; -+ info->size = size; -+ atomic_add(size, &alloc_sizes[info->type]); -+#if ALLOC_DEBUG > 1 -+ info->file = file; -+ info->line = line; -+ info->tag = 0; -+ spin_lock_bh(&alloc_lock); -+ InsertTailList(&allocs, &info->list); -+ spin_unlock_bh(&alloc_lock); -+#endif -+ TRACE4("%p", info + 1); -+ return info + 1; -+} -+ -+void *wrap_kzalloc(size_t size, gfp_t flags, const char *file, int line) -+{ -+ void *ptr = wrap_kmalloc(size, flags, file, line); -+ if (ptr) -+ memset(ptr, 0, size); -+ return ptr; -+} -+ -+void wrap_kfree(void *ptr) -+{ -+ struct alloc_info *info; -+ -+ TRACE4("%p", ptr); -+ if (!ptr) -+ return; -+ info = ptr - sizeof(*info); -+ atomic_sub(info->size, &alloc_sizes[info->type]); -+#if ALLOC_DEBUG > 1 -+ spin_lock_bh(&alloc_lock); -+ RemoveEntryList(&info->list); -+ spin_unlock_bh(&alloc_lock); -+ if (!(info->type == ALLOC_TYPE_KMALLOC_ATOMIC || -+ info->type == ALLOC_TYPE_KMALLOC_NON_ATOMIC)) { -+ WARNING("invalid type: %d", info->type); -+ return; -+ } -+#endif -+ kfree(info); -+} -+ -+void *wrap_vmalloc(unsigned long size, const char *file, int line) -+{ -+ struct alloc_info *info; -+ -+ info = vmalloc(size + sizeof(*info)); -+ if (!info) -+ return NULL; -+ info->type = ALLOC_TYPE_VMALLOC_NON_ATOMIC; -+ info->size = size; -+ atomic_add(size, &alloc_sizes[info->type]); -+#if ALLOC_DEBUG > 1 -+ info->file = file; -+ info->line = line; -+ info->tag = 0; -+ spin_lock_bh(&alloc_lock); -+ InsertTailList(&allocs, &info->list); -+ spin_unlock_bh(&alloc_lock); -+#endif -+ return info + 1; -+} -+ -+void *wrap__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot, -+ const char *file, int line) -+{ -+ struct alloc_info *info; -+ -+ info = __vmalloc(size + sizeof(*info), gfp_mask, prot); -+ if (!info) -+ return NULL; -+ if (gfp_mask & GFP_ATOMIC) -+ info->type = ALLOC_TYPE_VMALLOC_ATOMIC; -+ else -+ info->type = ALLOC_TYPE_VMALLOC_NON_ATOMIC; -+ info->size = size; -+ atomic_add(size, &alloc_sizes[info->type]); -+#if ALLOC_DEBUG > 1 -+ info->file = file; -+ info->line = line; -+ info->tag = 0; -+ spin_lock_bh(&alloc_lock); -+ InsertTailList(&allocs, &info->list); -+ spin_unlock_bh(&alloc_lock); -+#endif -+ return info + 1; -+} -+ -+void wrap_vfree(void *ptr) -+{ -+ struct alloc_info *info; -+ -+ info = ptr - sizeof(*info); -+ atomic_sub(info->size, &alloc_sizes[info->type]); -+#if ALLOC_DEBUG > 1 -+ spin_lock_bh(&alloc_lock); -+ RemoveEntryList(&info->list); -+ spin_unlock_bh(&alloc_lock); -+ if (!(info->type == ALLOC_TYPE_VMALLOC_ATOMIC || -+ info->type == ALLOC_TYPE_VMALLOC_NON_ATOMIC)) { -+ WARNING("invalid type: %d", info->type); -+ return; -+ } -+#endif -+ vfree(info); -+} -+ -+void *wrap_alloc_pages(gfp_t flags, unsigned int size, -+ const char *file, int line) -+{ -+ struct alloc_info *info; -+ -+ size += sizeof(*info); -+ info = (struct alloc_info *)__get_free_pages(flags, get_order(size)); -+ if (!info) -+ return NULL; -+ info->type = ALLOC_TYPE_PAGES; -+ info->size = size; -+ atomic_add(size, &alloc_sizes[info->type]); -+#if ALLOC_DEBUG > 1 -+ info->file = file; -+ info->line = line; -+ info->tag = 0; -+ spin_lock_bh(&alloc_lock); -+ InsertTailList(&allocs, &info->list); -+ spin_unlock_bh(&alloc_lock); -+#endif -+ return info + 1; -+} -+ -+void wrap_free_pages(unsigned long ptr, int order) -+{ -+ struct alloc_info *info; -+ -+ info = (void *)ptr - sizeof(*info); -+ atomic_sub(info->size, &alloc_sizes[info->type]); -+#if ALLOC_DEBUG > 1 -+ spin_lock_bh(&alloc_lock); -+ RemoveEntryList(&info->list); -+ spin_unlock_bh(&alloc_lock); -+ if (info->type != ALLOC_TYPE_PAGES) { -+ WARNING("invalid type: %d", info->type); -+ return; -+ } -+#endif -+ free_pages((unsigned long)info, get_order(info->size)); -+} -+ -+#if ALLOC_DEBUG > 1 -+void *wrap_ExAllocatePoolWithTag(enum pool_type pool_type, SIZE_T size, -+ ULONG tag, const char *file, int line) -+{ -+ void *addr; -+ struct alloc_info *info; -+ -+ ENTER4("pool_type: %d, size: %zu, tag: %u", pool_type, size, tag); -+ addr = (ExAllocatePoolWithTag)(pool_type, size, tag); -+ if (!addr) -+ return NULL; -+ info = addr - sizeof(*info); -+ info->file = file; -+ info->line = line; -+ info->tag = tag; -+ EXIT4(return addr); -+} -+#endif -+ -+int alloc_size(enum alloc_type type) -+{ -+ if ((int)type >= 0 && type < ALLOC_TYPE_MAX) -+ return atomic_read(&alloc_sizes[type]); -+ else -+ return -EINVAL; -+} -+ -+#endif // ALLOC_DEBUG -+ -+int wrapmem_init(void) -+{ -+#if ALLOC_DEBUG > 1 -+ InitializeListHead(&allocs); -+#endif -+ InitializeListHead(&slack_allocs); -+ spin_lock_init(&alloc_lock); -+ return 0; -+} -+ -+void wrapmem_exit(void) -+{ -+#if ALLOC_DEBUG -+ enum alloc_type type; -+#endif -+ struct nt_list *ent; -+ -+ /* free all pointers on the slack list */ -+ while (1) { -+ struct slack_alloc_info *info; -+ spin_lock_bh(&alloc_lock); -+ ent = RemoveHeadList(&slack_allocs); -+ spin_unlock_bh(&alloc_lock); -+ if (!ent) -+ break; -+ info = container_of(ent, struct slack_alloc_info, list); -+#if ALLOC_DEBUG -+ atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]); -+#endif -+ kfree(info); -+ } -+#if ALLOC_DEBUG -+ for (type = 0; type < ALLOC_TYPE_MAX; type++) { -+ int n = atomic_read(&alloc_sizes[type]); -+ if (n) -+ WARNING("%d bytes of memory in %s leaking", n, -+ alloc_type_name[type]); -+ } -+ -+#if ALLOC_DEBUG > 1 -+ while (1) { -+ struct alloc_info *info; -+ -+ spin_lock_bh(&alloc_lock); -+ ent = RemoveHeadList(&allocs); -+ spin_unlock_bh(&alloc_lock); -+ if (!ent) -+ break; -+ info = container_of(ent, struct alloc_info, list); -+ atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]); -+ printk(KERN_DEBUG DRIVER_NAME -+ ": %s:%d leaked %zd bytes at %p (%s, tag 0x%08X)\n", -+ info->file, info->line, info->size, info + 1, -+ alloc_type_name[info->type], info->tag); -+ if (info->type == ALLOC_TYPE_KMALLOC_ATOMIC || -+ info->type == ALLOC_TYPE_KMALLOC_NON_ATOMIC) -+ kfree(info); -+ else if (info->type == ALLOC_TYPE_VMALLOC_ATOMIC || -+ info->type == ALLOC_TYPE_VMALLOC_NON_ATOMIC) -+ vfree(info); -+ else if (info->type == ALLOC_TYPE_PAGES) -+ free_pages((unsigned long)info, get_order(info->size)); -+ else -+ WARNING("invalid type: %d; not freed", info->type); -+ } -+#endif -+#endif -+ return; -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapmem.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.h ---- linux-3.12.2/3rdparty/ndiswrapper/wrapmem.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapmem.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,97 @@ -+/* -+ * Copyright (C) 2006 Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _WRAPMEM_H_ -+#define _WRAPMEM_H_ -+ -+/* -+ * Set ALLOC_DEBUG to 1 to show information about memory used by both -+ * ndiswrapper and Windows driver in /proc/net/ndiswrapper/debug -+ * This will also show memory leaks (memory allocated but not freed) when -+ * ndiswrapper module is unloaded. -+ * -+ * Set ALLOC_DEBUG to 2 to see details about every leaking allocation. -+*/ -+ -+#ifndef ALLOC_DEBUG -+#define ALLOC_DEBUG 0 -+#endif -+ -+int wrapmem_init(void); -+void wrapmem_exit(void); -+void *slack_kmalloc(size_t size); -+void *slack_kzalloc(size_t size); -+void slack_kfree(void *ptr); -+ -+#if ALLOC_DEBUG -+enum alloc_type { ALLOC_TYPE_KMALLOC_ATOMIC, -+ ALLOC_TYPE_KMALLOC_NON_ATOMIC, -+ ALLOC_TYPE_VMALLOC_ATOMIC, ALLOC_TYPE_VMALLOC_NON_ATOMIC, -+ ALLOC_TYPE_SLACK, ALLOC_TYPE_PAGES, ALLOC_TYPE_MAX }; -+ -+extern const char *alloc_type_name[ALLOC_TYPE_MAX]; -+ -+void *wrap_kmalloc(size_t size, gfp_t flags, const char *file, int line); -+void *wrap_kzalloc(size_t size, gfp_t flags, const char *file, int line); -+void wrap_kfree(void *ptr); -+void *wrap_vmalloc(unsigned long size, const char *file, int line); -+void *wrap__vmalloc(unsigned long size, gfp_t flags, pgprot_t prot, -+ const char *file, int line); -+void wrap_vfree(void *ptr); -+void *wrap_alloc_pages(gfp_t flags, unsigned int size, -+ const char *file, int line); -+void wrap_free_pages(unsigned long ptr, int order); -+int alloc_size(enum alloc_type type); -+ -+#if ALLOC_DEBUG > 1 -+void *wrap_ExAllocatePoolWithTag(enum pool_type pool_type, SIZE_T size, -+ ULONG tag, const char *file, int line); -+#define ExAllocatePoolWithTag(pool_type, size, tag) \ -+ wrap_ExAllocatePoolWithTag(pool_type, size, tag, __FILE__, __LINE__) -+#endif -+ -+#ifndef _WRAPMEM_C_ -+#undef kmalloc -+#undef kzalloc -+#undef kfree -+#undef vmalloc -+#undef __vmalloc -+#undef vfree -+#define kmalloc(size, flags) \ -+ wrap_kmalloc(size, flags, __FILE__, __LINE__) -+#define kzalloc(size, flags) \ -+ wrap_kzalloc(size, flags, __FILE__, __LINE__) -+#define vmalloc(size) \ -+ wrap_vmalloc(size, __FILE__, __LINE__) -+#define __vmalloc(size, flags, prot) \ -+ wrap__vmalloc(size, flags, prot, __FILE__, __LINE__) -+#define kfree(ptr) wrap_kfree(ptr) -+#define vfree(ptr) wrap_vfree(ptr) -+ -+#define wrap_get_free_pages(flags, size) \ -+ wrap_alloc_pages(flags, size, __FILE__, __LINE__) -+#undef free_pages -+#define free_pages(ptr, order) wrap_free_pages(ptr, order) -+ -+#endif // _WRAPMEM_C_ -+ -+#else -+ -+#define wrap_get_free_pages(flags, size) \ -+ (void *)__get_free_pages(flags, get_order(size)) -+ -+#endif // ALLOC_DEBUG -+ -+#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.c ---- linux-3.12.2/3rdparty/ndiswrapper/wrapndis.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,2186 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include "ndis.h" -+#include "iw_ndis.h" -+#include "pnp.h" -+#include "loader.h" -+#include "wrapndis.h" -+#include "wrapper.h" -+ -+/* Functions callable from the NDIS driver */ -+wstdcall NTSTATUS NdisDispatchDeviceControl(struct device_object *fdo, -+ struct irp *irp); -+wstdcall NTSTATUS NdisDispatchPnp(struct device_object *fdo, struct irp *irp); -+wstdcall NTSTATUS NdisDispatchPower(struct device_object *fdo, struct irp *irp); -+ -+struct workqueue_struct *wrapndis_wq; -+ -+static int set_packet_filter(struct ndis_device *wnd, -+ ULONG packet_filter); -+static void add_iw_stats_timer(struct ndis_device *wnd); -+static void del_iw_stats_timer(struct ndis_device *wnd); -+static NDIS_STATUS ndis_start_device(struct ndis_device *wnd); -+static int ndis_remove_device(struct ndis_device *wnd); -+static void set_multicast_list(struct ndis_device *wnd); -+static int ndis_net_dev_open(struct net_device *net_dev); -+static int ndis_net_dev_close(struct net_device *net_dev); -+ -+/* MiniportReset */ -+NDIS_STATUS mp_reset(struct ndis_device *wnd) -+{ -+ NDIS_STATUS res; -+ struct miniport *mp; -+ BOOLEAN reset_address; -+ KIRQL irql; -+ -+ ENTER2("wnd: %p", wnd); -+ mutex_lock(&wnd->tx_ring_mutex); -+ mutex_lock(&wnd->ndis_req_mutex); -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ prepare_wait_condition(wnd->ndis_req_task, wnd->ndis_req_done, 0); -+ WARNING("%s is being reset", wnd->net_dev->name); -+ irql = serialize_lock_irql(wnd); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ res = LIN2WIN2(mp->reset, &reset_address, wnd->nmb->mp_ctx); -+ serialize_unlock_irql(wnd, irql); -+ -+ TRACE2("%08X, %08X", res, reset_address); -+ if (res == NDIS_STATUS_PENDING) { -+ /* wait for NdisMResetComplete */ -+ if (wait_condition((wnd->ndis_req_done > 0), 0, -+ TASK_INTERRUPTIBLE) < 0) -+ res = NDIS_STATUS_FAILURE; -+ else { -+ res = wnd->ndis_req_status; -+ reset_address = wnd->ndis_req_done - 1; -+ } -+ TRACE2("%08X, %08X", res, reset_address); -+ } -+ mutex_unlock(&wnd->ndis_req_mutex); -+ if (res == NDIS_STATUS_SUCCESS && reset_address) { -+ set_packet_filter(wnd, wnd->packet_filter); -+ set_multicast_list(wnd); -+ } -+ mutex_unlock(&wnd->tx_ring_mutex); -+ EXIT3(return res); -+} -+ -+/* MiniportRequest(Query/Set)Information */ -+NDIS_STATUS mp_request(enum ndis_request_type request, -+ struct ndis_device *wnd, ndis_oid oid, -+ void *buf, ULONG buflen, ULONG *written, ULONG *needed) -+{ -+ NDIS_STATUS res; -+ ULONG w, n; -+ struct miniport *mp; -+ KIRQL irql; -+ -+ mutex_lock(&wnd->ndis_req_mutex); -+ if (!written) -+ written = &w; -+ if (!needed) -+ needed = &n; -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ prepare_wait_condition(wnd->ndis_req_task, wnd->ndis_req_done, 0); -+ irql = serialize_lock_irql(wnd); -+ assert_irql(_irql_ == DISPATCH_LEVEL); -+ switch (request) { -+ case NdisRequestQueryInformation: -+ TRACE2("%p, %08X, %p", mp->queryinfo, oid, wnd->nmb->mp_ctx); -+ res = LIN2WIN6(mp->queryinfo, wnd->nmb->mp_ctx, oid, buf, -+ buflen, written, needed); -+ break; -+ case NdisRequestSetInformation: -+ TRACE2("%p, %08X, %p", mp->setinfo, oid, wnd->nmb->mp_ctx); -+ res = LIN2WIN6(mp->setinfo, wnd->nmb->mp_ctx, oid, buf, -+ buflen, written, needed); -+ break; -+ default: -+ WARNING("invalid request %d, %08X", request, oid); -+ res = NDIS_STATUS_NOT_SUPPORTED; -+ break; -+ } -+ serialize_unlock_irql(wnd, irql); -+ TRACE2("%08X, %08X", res, oid); -+ if (res == NDIS_STATUS_PENDING) { -+ /* wait for NdisMQueryInformationComplete */ -+ if (wait_condition((wnd->ndis_req_done > 0), 0, -+ TASK_INTERRUPTIBLE) < 0) -+ res = NDIS_STATUS_FAILURE; -+ else -+ res = wnd->ndis_req_status; -+ TRACE2("%08X, %08X", res, oid); -+ } -+ mutex_unlock(&wnd->ndis_req_mutex); -+ DBG_BLOCK(2) { -+ if (res || needed) -+ TRACE2("%08X, %d, %d, %d", res, buflen, *written, -+ *needed); -+ } -+ EXIT3(return res); -+} -+ -+/* MiniportPnPEventNotify */ -+static NDIS_STATUS mp_pnp_event(struct ndis_device *wnd, -+ enum ndis_device_pnp_event event, -+ ULONG power_profile) -+{ -+ struct miniport *mp; -+ -+ ENTER1("%p, %d", wnd, event); -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ if (!mp->pnp_event_notify) { -+ TRACE1("Windows driver %s doesn't support " -+ "MiniportPnpEventNotify", wnd->wd->driver->name); -+ return NDIS_STATUS_FAILURE; -+ } -+ /* RNDIS driver doesn't like to be notified if device is -+ * already halted */ -+ if (!test_bit(HW_INITIALIZED, &wnd->wd->hw_status)) -+ EXIT1(return NDIS_STATUS_SUCCESS); -+ switch (event) { -+ case NdisDevicePnPEventSurpriseRemoved: -+ TRACE1("%u, %p", -+ (wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK), -+ mp->pnp_event_notify); -+ if ((wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK) && -+ !test_bit(HW_DISABLED, &wnd->wd->hw_status) && -+ mp->pnp_event_notify) { -+ TRACE1("calling surprise_removed"); -+ LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx, -+ NdisDevicePnPEventSurpriseRemoved, NULL, 0); -+ } else -+ TRACE1("Windows driver %s doesn't support " -+ "MiniportPnpEventNotify for safe unplugging", -+ wnd->wd->driver->name); -+ return NDIS_STATUS_SUCCESS; -+ case NdisDevicePnPEventPowerProfileChanged: -+ if (power_profile) -+ power_profile = NdisPowerProfileAcOnLine; -+ LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx, -+ NdisDevicePnPEventPowerProfileChanged, -+ &power_profile, sizeof(power_profile)); -+ return NDIS_STATUS_SUCCESS; -+ default: -+ WARNING("event %d not yet implemented", event); -+ return NDIS_STATUS_SUCCESS; -+ } -+} -+ -+/* MiniportInitialize */ -+static NDIS_STATUS mp_init(struct ndis_device *wnd) -+{ -+ NDIS_STATUS error_status, status; -+ UINT medium_index; -+ enum ndis_medium medium_array[] = {NdisMedium802_3}; -+ struct miniport *mp; -+ -+ ENTER1("irql: %d", current_irql()); -+ if (test_bit(HW_INITIALIZED, &wnd->wd->hw_status)) { -+ WARNING("device %p already initialized!", wnd); -+ return NDIS_STATUS_FAILURE; -+ } -+ -+ if (!wnd->wd->driver->ndis_driver || -+ !wnd->wd->driver->ndis_driver->mp.init) { -+ WARNING("assuming WDM (non-NDIS) driver"); -+ EXIT1(return NDIS_STATUS_NOT_RECOGNIZED); -+ } -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ status = LIN2WIN6(mp->init, &error_status, &medium_index, medium_array, -+ ARRAY_SIZE(medium_array), wnd->nmb, wnd->nmb); -+ TRACE1("init returns: %08X, irql: %d", status, current_irql()); -+ if (status != NDIS_STATUS_SUCCESS) { -+ WARNING("couldn't initialize device: %08X", status); -+ EXIT1(return NDIS_STATUS_FAILURE); -+ } -+ -+ /* Wait a little to let card power up otherwise ifup might -+ * fail after boot */ -+ sleep_hz(HZ / 5); -+ status = mp_pnp_event(wnd, NdisDevicePnPEventPowerProfileChanged, -+ NdisPowerProfileAcOnLine); -+ if (status != NDIS_STATUS_SUCCESS) -+ TRACE1("setting power failed: %08X", status); -+ set_bit(HW_INITIALIZED, &wnd->wd->hw_status); -+ /* the description about NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND is -+ * misleading/confusing */ -+ status = mp_query(wnd, OID_PNP_CAPABILITIES, -+ &wnd->pnp_capa, sizeof(wnd->pnp_capa)); -+ if (status == NDIS_STATUS_SUCCESS) { -+ TRACE1("%d, %d", wnd->pnp_capa.wakeup.min_magic_packet_wakeup, -+ wnd->pnp_capa.wakeup.min_pattern_wakeup); -+ wnd->attributes |= NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; -+ status = mp_query_int(wnd, OID_PNP_ENABLE_WAKE_UP, -+ &wnd->ndis_wolopts); -+ TRACE1("%08X, %x", status, wnd->ndis_wolopts); -+ } else if (status == NDIS_STATUS_NOT_SUPPORTED) -+ wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; -+ TRACE1("%d", wnd->pnp_capa.wakeup.min_magic_packet_wakeup); -+ /* although some NDIS drivers support suspend, Linux kernel -+ * has issues with suspending USB devices */ -+ if (wrap_is_usb_bus(wnd->wd->dev_bus)) { -+ wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; -+ wnd->ndis_wolopts = 0; -+ } -+ mp_set_int(wnd, OID_802_11_POWER_MODE, NDIS_POWER_OFF); -+ EXIT1(return NDIS_STATUS_SUCCESS); -+} -+ -+/* MiniportHalt */ -+static void mp_halt(struct ndis_device *wnd) -+{ -+ struct miniport *mp; -+ -+ ENTER1("%p", wnd); -+ if (!test_and_clear_bit(HW_INITIALIZED, &wnd->wd->hw_status)) { -+ WARNING("device %p is not initialized - not halting", wnd); -+ return; -+ } -+ hangcheck_del(wnd); -+ del_iw_stats_timer(wnd); -+#ifdef CONFIG_WIRELESS_EXT -+ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan && -+ wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ mutex_unlock(&wnd->ndis_req_mutex); -+ disassociate(wnd, 0); -+ mutex_lock(&wnd->ndis_req_mutex); -+ } -+#endif -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ TRACE1("halt: %p", mp->mp_halt); -+ LIN2WIN1(mp->mp_halt, wnd->nmb->mp_ctx); -+ /* if a driver doesn't call NdisMDeregisterInterrupt during -+ * halt, deregister it now */ -+ if (wnd->mp_interrupt) -+ NdisMDeregisterInterrupt(wnd->mp_interrupt); -+ /* cancel any timers left by buggy windows driver; also free -+ * the memory for timers */ -+ while (1) { -+ struct nt_slist *slist; -+ struct wrap_timer *wrap_timer; -+ -+ spin_lock_bh(&ntoskernel_lock); -+ if ((slist = wnd->wrap_timer_slist.next)) -+ wnd->wrap_timer_slist.next = slist->next; -+ spin_unlock_bh(&ntoskernel_lock); -+ TIMERTRACE("%p", slist); -+ if (!slist) -+ break; -+ wrap_timer = container_of(slist, struct wrap_timer, slist); -+ wrap_timer->repeat = 0; -+ /* ktimer that this wrap_timer is associated to can't -+ * be touched, as it may have been freed by the driver -+ * already */ -+ if (del_timer_sync(&wrap_timer->timer)) -+ WARNING("Buggy Windows driver left timer %p " -+ "running", wrap_timer->nt_timer); -+ memset(wrap_timer, 0, sizeof(*wrap_timer)); -+ kfree(wrap_timer); -+ } -+ EXIT1(return); -+} -+ -+static NDIS_STATUS mp_set_power_state(struct ndis_device *wnd, -+ enum ndis_power_state state) -+{ -+ NDIS_STATUS status; -+ -+ TRACE1("%d", state); -+ if (state == NdisDeviceStateD0) { -+ status = NDIS_STATUS_SUCCESS; -+ mutex_unlock(&wnd->ndis_req_mutex); -+ if (test_and_clear_bit(HW_HALTED, &wnd->wd->hw_status)) { -+ status = mp_init(wnd); -+ if (status == NDIS_STATUS_SUCCESS) { -+ set_packet_filter(wnd, wnd->packet_filter); -+ set_multicast_list(wnd); -+ } -+ } else if (test_and_clear_bit(HW_SUSPENDED, -+ &wnd->wd->hw_status)) { -+ status = mp_set_int(wnd, OID_PNP_SET_POWER, state); -+ if (status != NDIS_STATUS_SUCCESS) -+ WARNING("%s: setting power to state %d failed? " -+ "%08X", wnd->net_dev->name, state, -+ status); -+ } else -+ return NDIS_STATUS_FAILURE; -+ -+ if (wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ pci_enable_wake(wnd->wd->pci.pdev, PCI_D3hot, 0); -+ pci_enable_wake(wnd->wd->pci.pdev, PCI_D3cold, 0); -+ } -+ if (status == NDIS_STATUS_SUCCESS) { -+ mutex_unlock(&wnd->tx_ring_mutex); -+ netif_device_attach(wnd->net_dev); -+ hangcheck_add(wnd); -+ add_iw_stats_timer(wnd); -+ } else -+ WARNING("%s: couldn't set power to state %d; device not" -+ " resumed", wnd->net_dev->name, state); -+ EXIT1(return status); -+ } else { -+ mutex_lock(&wnd->tx_ring_mutex); -+ netif_device_detach(wnd->net_dev); -+ hangcheck_del(wnd); -+ del_iw_stats_timer(wnd); -+ status = NDIS_STATUS_NOT_SUPPORTED; -+ if (wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND) { -+ status = mp_set_int(wnd, OID_PNP_ENABLE_WAKE_UP, -+ wnd->ndis_wolopts); -+ TRACE2("0x%x, 0x%x", status, wnd->ndis_wolopts); -+ if (status == NDIS_STATUS_SUCCESS && -+ wrap_is_pci_bus(wnd->wd->dev_bus)) { -+ if (wnd->ndis_wolopts) -+ wnd->wd->pci.wake_state = -+ PowerDeviceD3; -+ else -+ wnd->wd->pci.wake_state = -+ PowerDeviceUnspecified; -+ } else -+ WARNING("couldn't set wake-on-lan options: " -+ "0x%x, %08X", wnd->ndis_wolopts, status); -+ status = mp_set_int(wnd, OID_PNP_SET_POWER, state); -+ if (status == NDIS_STATUS_SUCCESS) -+ set_bit(HW_SUSPENDED, &wnd->wd->hw_status); -+ else -+ WARNING("suspend failed: %08X", status); -+ } -+ if (status != NDIS_STATUS_SUCCESS) { -+ WARNING("%s does not support power management; " -+ "halting the device", wnd->net_dev->name); -+ mp_halt(wnd); -+ set_bit(HW_HALTED, &wnd->wd->hw_status); -+ status = STATUS_SUCCESS; -+ } -+ mutex_lock(&wnd->ndis_req_mutex); -+ EXIT1(return status); -+ } -+} -+ -+static int ndis_set_mac_address(struct net_device *dev, void *p) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ struct sockaddr *addr = p; -+ struct ndis_configuration_parameter param; -+ struct unicode_string key; -+ struct ansi_string ansi; -+ NDIS_STATUS res; -+ unsigned char mac_string[2 * ETH_ALEN + 1]; -+ mac_address mac; -+ -+ memcpy(mac, addr->sa_data, sizeof(mac)); -+ memset(mac_string, 0, sizeof(mac_string)); -+ res = snprintf(mac_string, sizeof(mac_string), MACSTR, MAC2STR(mac)); -+ if (res != (sizeof(mac_string) - 1)) -+ EXIT1(return -EINVAL); -+ TRACE1("new mac: %s", mac_string); -+ -+ RtlInitAnsiString(&ansi, mac_string); -+ if (RtlAnsiStringToUnicodeString(¶m.data.string, &ansi, -+ TRUE) != STATUS_SUCCESS) -+ EXIT1(return -EINVAL); -+ -+ param.type = NdisParameterString; -+ RtlInitAnsiString(&ansi, "NetworkAddress"); -+ if (RtlAnsiStringToUnicodeString(&key, &ansi, TRUE) != STATUS_SUCCESS) { -+ RtlFreeUnicodeString(¶m.data.string); -+ EXIT1(return -EINVAL); -+ } -+ NdisWriteConfiguration(&res, wnd->nmb, &key, ¶m); -+ RtlFreeUnicodeString(&key); -+ RtlFreeUnicodeString(¶m.data.string); -+ -+ if (res != NDIS_STATUS_SUCCESS) -+ EXIT1(return -EFAULT); -+ if (ndis_reinit(wnd) == NDIS_STATUS_SUCCESS) { -+ res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, -+ mac, sizeof(mac)); -+ if (res == NDIS_STATUS_SUCCESS) { -+ TRACE1("mac:" MACSTRSEP, MAC2STR(mac)); -+ memcpy(dev->dev_addr, mac, sizeof(mac)); -+ } else -+ ERROR("couldn't get mac address: %08X", res); -+ } -+ EXIT1(return 0); -+} -+ -+static int setup_tx_sg_list(struct ndis_device *wnd, struct sk_buff *skb, -+ struct ndis_packet_oob_data *oob_data) -+{ -+ struct ndis_sg_element *sg_element; -+ struct ndis_sg_list *sg_list; -+ int i; -+ -+ ENTER3("%p, %d", skb, skb_shinfo(skb)->nr_frags); -+ if (skb_shinfo(skb)->nr_frags <= 1) { -+ sg_element = &oob_data->wrap_tx_sg_list.elements[0]; -+ sg_element->address = -+ PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, skb->data, -+ skb->len, PCI_DMA_TODEVICE); -+ sg_element->length = skb->len; -+ oob_data->wrap_tx_sg_list.nent = 1; -+ oob_data->ext.info[ScatterGatherListPacketInfo] = -+ &oob_data->wrap_tx_sg_list; -+ TRACE3("%llx, %u", sg_element->address, sg_element->length); -+ return 0; -+ } -+ sg_list = kmalloc(sizeof(*sg_list) + -+ (skb_shinfo(skb)->nr_frags + 1) * sizeof(*sg_element), -+ GFP_ATOMIC); -+ if (!sg_list) -+ return -ENOMEM; -+ sg_list->nent = skb_shinfo(skb)->nr_frags + 1; -+ TRACE3("%p, %d", sg_list, sg_list->nent); -+ sg_element = sg_list->elements; -+ sg_element->length = skb_headlen(skb); -+ sg_element->address = -+ PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, skb->data, -+ skb_headlen(skb), PCI_DMA_TODEVICE); -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ sg_element++; -+ sg_element->length = frag->size; -+ sg_element->address = -+ pci_map_page(wnd->wd->pci.pdev, skb_frag_page(frag), -+ frag->page_offset, frag->size, -+ PCI_DMA_TODEVICE); -+ TRACE3("%llx, %u", sg_element->address, sg_element->length); -+ } -+ oob_data->ext.info[ScatterGatherListPacketInfo] = sg_list; -+ return 0; -+} -+ -+static void free_tx_sg_list(struct ndis_device *wnd, -+ struct ndis_packet_oob_data *oob_data) -+{ -+ int i; -+ struct ndis_sg_element *sg_element; -+ struct ndis_sg_list *sg_list = -+ oob_data->ext.info[ScatterGatherListPacketInfo]; -+ sg_element = sg_list->elements; -+ TRACE3("%p, %d", sg_list, sg_list->nent); -+ PCI_DMA_UNMAP_SINGLE(wnd->wd->pci.pdev, sg_element->address, -+ sg_element->length, PCI_DMA_TODEVICE); -+ if (sg_list->nent == 1) -+ EXIT3(return); -+ for (i = 1; i < sg_list->nent; i++, sg_element++) { -+ TRACE3("%llx, %u", sg_element->address, sg_element->length); -+ pci_unmap_page(wnd->wd->pci.pdev, sg_element->address, -+ sg_element->length, PCI_DMA_TODEVICE); -+ } -+ TRACE3("%p", sg_list); -+ kfree(sg_list); -+} -+ -+static struct ndis_packet *alloc_tx_packet(struct ndis_device *wnd, -+ struct sk_buff *skb) -+{ -+ struct ndis_packet *packet; -+ ndis_buffer *buffer; -+ struct ndis_packet_oob_data *oob_data; -+ NDIS_STATUS status; -+ -+ NdisAllocatePacket(&status, &packet, wnd->tx_packet_pool); -+ if (status != NDIS_STATUS_SUCCESS) -+ return NULL; -+ NdisAllocateBuffer(&status, &buffer, wnd->tx_buffer_pool, -+ skb->data, skb->len); -+ if (status != NDIS_STATUS_SUCCESS) { -+ NdisFreePacket(packet); -+ return NULL; -+ } -+ packet->private.buffer_head = buffer; -+ packet->private.buffer_tail = buffer; -+ -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ oob_data->tx_skb = skb; -+ if (wnd->sg_dma_size) { -+ if (setup_tx_sg_list(wnd, skb, oob_data)) { -+ NdisFreeBuffer(buffer); -+ NdisFreePacket(packet); -+ return NULL; -+ } -+ } -+ if (skb->ip_summed == CHECKSUM_PARTIAL) { -+ struct ndis_tcp_ip_checksum_packet_info csum; -+ int protocol; -+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21) -+ protocol = ntohs(skb->protocol); -+#else -+ protocol = skb->nh.iph->protocol; -+#endif -+ csum.value = 0; -+ csum.tx.v4 = 1; -+ if (protocol == IPPROTO_TCP) -+ csum.tx.tcp = 1; -+ else if (protocol == IPPROTO_UDP) -+ csum.tx.udp = 1; -+// csum->tx.ip = 1; -+ packet->private.flags |= NDIS_PROTOCOL_ID_TCP_IP; -+ oob_data->ext.info[TcpIpChecksumPacketInfo] = -+ (void *)(ULONG_PTR)csum.value; -+ } -+ DBG_BLOCK(4) { -+ dump_bytes(__func__, skb->data, skb->len); -+ } -+ TRACE4("%p, %p, %p", packet, buffer, skb); -+ return packet; -+} -+ -+void free_tx_packet(struct ndis_device *wnd, struct ndis_packet *packet, -+ NDIS_STATUS status) -+{ -+ ndis_buffer *buffer; -+ struct ndis_packet_oob_data *oob_data; -+ struct sk_buff *skb; -+ struct ndis_packet_pool *pool; -+ -+ assert_irql(_irql_ <= DISPATCH_LEVEL); -+ assert(packet->private.packet_flags); -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ skb = oob_data->tx_skb; -+ buffer = packet->private.buffer_head; -+ TRACE4("%p, %p, %p, %08X", packet, buffer, skb, status); -+ if (status == NDIS_STATUS_SUCCESS) { -+ pre_atomic_add(wnd->net_stats.tx_bytes, packet->private.len); -+ atomic_inc_var(wnd->net_stats.tx_packets); -+ } else { -+ TRACE1("packet dropped: %08X", status); -+ atomic_inc_var(wnd->net_stats.tx_dropped); -+ } -+ if (wnd->sg_dma_size) -+ free_tx_sg_list(wnd, oob_data); -+ NdisFreeBuffer(buffer); -+ dev_kfree_skb_any(skb); -+ pool = packet->private.pool; -+ NdisFreePacket(packet); -+ if (netif_queue_stopped(wnd->net_dev) && -+ ((pool->max_descr - pool->num_used_descr) >= -+ (wnd->max_tx_packets / 4))) { -+ set_bit(NETIF_WAKEQ, &wnd->ndis_pending_work); -+ queue_work(wrapndis_wq, &wnd->ndis_work); -+ } -+ EXIT4(return); -+} -+ -+/* MiniportSend and MiniportSendPackets */ -+/* this function is called holding tx_ring_mutex. start and n are such -+ * that start + n < TX_RING_SIZE; i.e., packets don't wrap around -+ * ring */ -+static u8 mp_tx_packets(struct ndis_device *wnd, u8 start, u8 n) -+{ -+ NDIS_STATUS res; -+ struct miniport *mp; -+ struct ndis_packet *packet; -+ u8 sent; -+ KIRQL irql; -+ -+ ENTER3("%d, %d", start, n); -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ if (mp->send_packets) { -+ if (deserialized_driver(wnd)) { -+ LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx, -+ &wnd->tx_ring[start], n); -+ sent = n; -+ } else { -+ irql = serialize_lock_irql(wnd); -+ LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx, -+ &wnd->tx_ring[start], n); -+ serialize_unlock_irql(wnd, irql); -+ for (sent = 0; sent < n && wnd->tx_ok; sent++) { -+ struct ndis_packet_oob_data *oob_data; -+ packet = wnd->tx_ring[start + sent]; -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ switch ((res = -+ xchg(&oob_data->status, -+ NDIS_STATUS_NOT_RECOGNIZED))) { -+ case NDIS_STATUS_SUCCESS: -+ free_tx_packet(wnd, packet, -+ NDIS_STATUS_SUCCESS); -+ break; -+ case NDIS_STATUS_PENDING: -+ break; -+ case NDIS_STATUS_RESOURCES: -+ wnd->tx_ok = 0; -+ /* resubmit this packet and -+ * the rest when resources -+ * become available */ -+ sent--; -+ break; -+ case NDIS_STATUS_FAILURE: -+ free_tx_packet(wnd, packet, -+ NDIS_STATUS_FAILURE); -+ break; -+ default: -+ ERROR("%p: invalid status: %08X", -+ packet, res); -+ free_tx_packet(wnd, packet, -+ oob_data->status); -+ break; -+ } -+ TRACE3("%p, %d", packet, res); -+ } -+ } -+ TRACE3("sent: %d(%d)", sent, n); -+ } else { -+ for (sent = 0; sent < n && wnd->tx_ok; sent++) { -+ struct ndis_packet_oob_data *oob_data; -+ packet = wnd->tx_ring[start + sent]; -+ oob_data = NDIS_PACKET_OOB_DATA(packet); -+ oob_data->status = NDIS_STATUS_NOT_RECOGNIZED; -+ irql = serialize_lock_irql(wnd); -+ res = LIN2WIN3(mp->send, wnd->nmb->mp_ctx, -+ packet, packet->private.flags); -+ serialize_unlock_irql(wnd, irql); -+ switch (res) { -+ case NDIS_STATUS_SUCCESS: -+ free_tx_packet(wnd, packet, res); -+ break; -+ case NDIS_STATUS_PENDING: -+ break; -+ case NDIS_STATUS_RESOURCES: -+ wnd->tx_ok = 0; -+ /* resend this packet when resources -+ * become available */ -+ sent--; -+ break; -+ case NDIS_STATUS_FAILURE: -+ free_tx_packet(wnd, packet, res); -+ break; -+ default: -+ ERROR("packet %p: invalid status: %08X", -+ packet, res); -+ break; -+ } -+ } -+ } -+ EXIT3(return sent); -+} -+ -+static void tx_worker(struct work_struct *work) -+{ -+ struct ndis_device *wnd; -+ s8 n; -+ -+ wnd = container_of(work, struct ndis_device, tx_work); -+ ENTER3("tx_ok %d", wnd->tx_ok); -+ while (wnd->tx_ok) { -+ mutex_lock(&wnd->tx_ring_mutex); -+ spin_lock_bh(&wnd->tx_ring_lock); -+ n = wnd->tx_ring_end - wnd->tx_ring_start; -+ TRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n); -+ /* end == start if either ring is empty or full; in -+ * the latter case is_tx_ring_full is set */ -+ if (n == 0) { -+ if (wnd->is_tx_ring_full) -+ n = TX_RING_SIZE - wnd->tx_ring_start; -+ else { -+ spin_unlock_bh(&wnd->tx_ring_lock); -+ mutex_unlock(&wnd->tx_ring_mutex); -+ break; -+ } -+ } else if (n < 0) -+ n = TX_RING_SIZE - wnd->tx_ring_start; -+ spin_unlock_bh(&wnd->tx_ring_lock); -+ if (unlikely(n > wnd->max_tx_packets)) -+ n = wnd->max_tx_packets; -+ n = mp_tx_packets(wnd, wnd->tx_ring_start, n); -+ if (n) { -+ wnd->net_dev->trans_start = jiffies; -+ wnd->tx_ring_start = -+ (wnd->tx_ring_start + n) % TX_RING_SIZE; -+ wnd->is_tx_ring_full = 0; -+ } -+ mutex_unlock(&wnd->tx_ring_mutex); -+ TRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n); -+ } -+ EXIT3(return); -+} -+ -+static int tx_skbuff(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ struct ndis_packet *packet; -+ -+ packet = alloc_tx_packet(wnd, skb); -+ if (!packet) { -+ TRACE2("couldn't allocate packet"); -+ netif_tx_lock(dev); -+ netif_stop_queue(dev); -+ netif_tx_unlock(dev); -+ return NETDEV_TX_BUSY; -+ } -+ spin_lock(&wnd->tx_ring_lock); -+ wnd->tx_ring[wnd->tx_ring_end++] = packet; -+ if (wnd->tx_ring_end == TX_RING_SIZE) -+ wnd->tx_ring_end = 0; -+ if (wnd->tx_ring_end == wnd->tx_ring_start) { -+ netif_tx_lock(dev); -+ wnd->is_tx_ring_full = 1; -+ netif_stop_queue(dev); -+ netif_tx_unlock(dev); -+ } -+ spin_unlock(&wnd->tx_ring_lock); -+ TRACE4("ring: %d, %d", wnd->tx_ring_start, wnd->tx_ring_end); -+ queue_work(wrapndis_wq, &wnd->tx_work); -+ return NETDEV_TX_OK; -+} -+ -+static int set_packet_filter(struct ndis_device *wnd, ULONG packet_filter) -+{ -+ NDIS_STATUS res; -+ -+ while (1) { -+ res = mp_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, -+ packet_filter); -+ if (res == NDIS_STATUS_SUCCESS) -+ break; -+ TRACE2("couldn't set filter 0x%08x", packet_filter); -+ /* NDIS_PACKET_TYPE_PROMISCUOUS may not work with 802.11 */ -+ if (packet_filter & NDIS_PACKET_TYPE_PROMISCUOUS) { -+ packet_filter &= ~NDIS_PACKET_TYPE_PROMISCUOUS; -+ continue; -+ } -+ if (packet_filter & NDIS_PACKET_TYPE_ALL_LOCAL) { -+ packet_filter &= ~NDIS_PACKET_TYPE_ALL_LOCAL; -+ continue; -+ } -+ if (packet_filter & NDIS_PACKET_TYPE_ALL_FUNCTIONAL) { -+ packet_filter &= ~NDIS_PACKET_TYPE_ALL_FUNCTIONAL; -+ continue; -+ } -+ if (packet_filter & NDIS_PACKET_TYPE_MULTICAST) { -+ packet_filter &= ~NDIS_PACKET_TYPE_MULTICAST; -+ packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; -+ continue; -+ } -+ if (packet_filter & NDIS_PACKET_TYPE_ALL_MULTICAST) { -+ packet_filter &= ~NDIS_PACKET_TYPE_ALL_MULTICAST; -+ continue; -+ } -+ break; -+ } -+ -+ wnd->packet_filter = packet_filter; -+ res = mp_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter); -+ if (packet_filter != wnd->packet_filter) { -+ WARNING("filter not set: 0x%08x, 0x%08x", -+ packet_filter, wnd->packet_filter); -+ wnd->packet_filter = packet_filter; -+ } -+ if (wnd->packet_filter) -+ EXIT3(return 0); -+ else -+ EXIT3(return -1); -+} -+ -+void set_media_state(struct ndis_device *wnd, enum ndis_media_state state) -+{ -+ struct net_device *net_dev = wnd->net_dev; -+ -+ ENTER2("state: 0x%x, carrier %d", state, netif_carrier_ok(net_dev)); -+ switch (state) { -+ case NdisMediaStateConnected: -+ if (netif_carrier_ok(net_dev)) -+ return; -+ netif_carrier_on(net_dev); -+ wnd->tx_ok = 1; -+ if (netif_queue_stopped(net_dev)) -+ netif_wake_queue(net_dev); -+ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { -+ set_bit(LINK_STATUS_ON, &wnd->ndis_pending_work); -+ queue_work(wrapndis_wq, &wnd->ndis_work); -+ } -+ break; -+ case NdisMediaStateDisconnected: -+ if (!netif_carrier_ok(net_dev)) -+ return; -+ netif_carrier_off(net_dev); -+ netif_stop_queue(net_dev); -+ wnd->tx_ok = 0; -+ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { -+ memset(&wnd->essid, 0, sizeof(wnd->essid)); -+ set_bit(LINK_STATUS_OFF, &wnd->ndis_pending_work); -+ queue_work(wrapndis_wq, &wnd->ndis_work); -+ } -+ break; -+ default: -+ WARNING("invalid media state: 0x%x", state); -+ break; -+ } -+} -+ -+static int ndis_net_dev_init(struct net_device *net_dev) -+{ -+ struct ndis_device *wnd = netdev_priv(net_dev); -+ -+ ENTER1("%p", wnd); -+ wrap_procfs_add_ndis_device(wnd); -+ EXIT1(return 0); -+} -+ -+static void ndis_net_dev_uninit(struct net_device *net_dev) -+{ -+ struct ndis_device *wnd = netdev_priv(net_dev); -+ -+ ENTER1("%p", wnd); -+ wrap_procfs_remove_ndis_device(wnd); -+ EXIT1(return); -+} -+ -+static int ndis_net_dev_open(struct net_device *net_dev) -+{ -+ int status, res; -+ struct ndis_device *wnd = netdev_priv(net_dev); -+ -+ ENTER1("%p", wnd); -+ res = mp_query_int(wnd, OID_GEN_MEDIA_CONNECT_STATUS, &status); -+ if (res == NDIS_STATUS_SUCCESS && status >= NdisMediaStateConnected && -+ status <= NdisMediaStateDisconnected) -+ set_media_state(wnd, status); -+ netif_start_queue(net_dev); -+ netif_poll_enable(net_dev); -+ EXIT1(return 0); -+} -+ -+static int ndis_net_dev_close(struct net_device *net_dev) -+{ -+ ENTER1("%p", netdev_priv(net_dev)); -+ netif_poll_disable(net_dev); -+ netif_tx_disable(net_dev); -+ EXIT1(return 0); -+} -+ -+static int ndis_change_mtu(struct net_device *net_dev, int mtu) -+{ -+ struct ndis_device *wnd = netdev_priv(net_dev); -+ int max; -+ -+ if (mtu < ETH_ZLEN) -+ return -EINVAL; -+ if (mp_query_int(wnd, OID_GEN_MAXIMUM_TOTAL_SIZE, &max) != -+ NDIS_STATUS_SUCCESS) -+ return -EOPNOTSUPP; -+ TRACE1("%d", max); -+ max -= ETH_HLEN; -+ if (max <= ETH_ZLEN) -+ return -EINVAL; -+ if (mtu + ETH_HLEN > max) -+ return -EINVAL; -+ net_dev->mtu = mtu; -+ return 0; -+} -+ -+#ifdef CONFIG_NET_POLL_CONTROLLER -+static void ndis_poll_controller(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ disable_irq(dev->irq); -+ ndis_isr(wnd->mp_interrupt->kinterrupt, wnd->mp_interrupt); -+ enable_irq(dev->irq); -+} -+#endif -+ -+/* called from BH context */ -+static struct net_device_stats *ndis_get_stats(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ return &wnd->net_stats; -+} -+ -+/* called from BH context */ -+static void ndis_set_multicast_list(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ set_bit(SET_MULTICAST_LIST, &wnd->ndis_pending_work); -+ queue_work(wrapndis_wq, &wnd->ndis_work); -+} -+ -+/* called from BH context */ -+struct iw_statistics *get_iw_stats(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ return &wnd->iw_stats; -+} -+ -+static void update_iw_stats(struct ndis_device *wnd) -+{ -+ struct iw_statistics *iw_stats = &wnd->iw_stats; -+ struct ndis_wireless_stats ndis_stats; -+ NDIS_STATUS res; -+ ndis_rssi rssi; -+ int qual; -+ -+ ENTER2("%p", wnd); -+ if (wnd->iw_stats_enabled == FALSE || !netif_carrier_ok(wnd->net_dev)) { -+ memset(iw_stats, 0, sizeof(*iw_stats)); -+ EXIT2(return); -+ } -+ res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi)); -+ if (res == NDIS_STATUS_SUCCESS) -+ iw_stats->qual.level = rssi; -+ -+ qual = 100 * (rssi - WL_NOISE) / (WL_SIGMAX - WL_NOISE); -+ if (qual < 0) -+ qual = 0; -+ else if (qual > 100) -+ qual = 100; -+ -+ iw_stats->qual.noise = WL_NOISE; -+ iw_stats->qual.qual = qual; -+ -+ res = mp_query(wnd, OID_802_11_STATISTICS, -+ &ndis_stats, sizeof(ndis_stats)); -+ if (res != NDIS_STATUS_SUCCESS) -+ EXIT2(return); -+ iw_stats->discard.retries = (unsigned long)ndis_stats.retry + -+ (unsigned long)ndis_stats.multi_retry; -+ iw_stats->discard.misc = (unsigned long)ndis_stats.fcs_err + -+ (unsigned long)ndis_stats.rtss_fail + -+ (unsigned long)ndis_stats.ack_fail + -+ (unsigned long)ndis_stats.frame_dup; -+ -+ EXIT2(return); -+} -+ -+static void set_multicast_list(struct ndis_device *wnd) -+{ -+ struct net_device *net_dev; -+ ULONG packet_filter; -+ NDIS_STATUS res; -+ -+ net_dev = wnd->net_dev; -+ packet_filter = wnd->packet_filter; -+ -+ TRACE2("0x%08x", packet_filter); -+ if (net_dev->flags & IFF_PROMISC) { -+ packet_filter |= NDIS_PACKET_TYPE_PROMISCUOUS | -+ NDIS_PACKET_TYPE_ALL_LOCAL; -+ } else if (net_dev->flags & IFF_ALLMULTI || -+ netdev_mc_count(net_dev) > wnd->multicast_size) { -+ packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; -+ TRACE2("0x%08x", packet_filter); -+ } else if (netdev_mc_count(net_dev) > 0) { -+ int i, size; -+ char *buf; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) -+ struct netdev_hw_addr *ha; -+#else -+ struct dev_mc_list *mclist; -+#endif -+ size = min(wnd->multicast_size, netdev_mc_count(net_dev)); -+ TRACE2("%d, %d", wnd->multicast_size, netdev_mc_count(net_dev)); -+ buf = kmalloc(size * ETH_ALEN, GFP_KERNEL); -+ if (!buf) { -+ WARNING("couldn't allocate memory"); -+ EXIT2(return); -+ } -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) -+ i = 0; -+ netdev_for_each_mc_addr(ha, net_dev) { -+ if (i >= size) -+ break; -+ memcpy(buf + i * ETH_ALEN, ha->addr, ETH_ALEN); -+ TRACE2(MACSTRSEP, MAC2STR(ha->addr)); -+ i++; -+ } -+#else -+ mclist = net_dev->mc_list; -+ for (i = 0; i < size && mclist; mclist = mclist->next) { -+ if (mclist->dmi_addrlen != ETH_ALEN) -+ continue; -+ memcpy(buf + i * ETH_ALEN, mclist->dmi_addr, ETH_ALEN); -+ TRACE2(MACSTRSEP, MAC2STR(mclist->dmi_addr)); -+ i++; -+ } -+#endif -+ res = mp_set(wnd, OID_802_3_MULTICAST_LIST, buf, i * ETH_ALEN); -+ if (res == NDIS_STATUS_SUCCESS && i > 0) -+ packet_filter |= NDIS_PACKET_TYPE_MULTICAST; -+ else -+ packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; -+ kfree(buf); -+ } -+ TRACE2("0x%08x", packet_filter); -+ res = set_packet_filter(wnd, packet_filter); -+ if (res) -+ TRACE1("couldn't set packet filter (%08X)", res); -+ EXIT2(return); -+} -+ -+static void link_status_off(struct ndis_device *wnd) -+{ -+#ifdef CONFIG_WIRELESS_EXT -+ union iwreq_data wrqu; -+ -+ memset(&wrqu, 0, sizeof(wrqu)); -+ wrqu.ap_addr.sa_family = ARPHRD_ETHER; -+ wireless_send_event(wnd->net_dev, SIOCGIWAP, &wrqu, NULL); -+#endif -+ EXIT2(return); -+} -+ -+static void link_status_on(struct ndis_device *wnd) -+{ -+#ifdef CONFIG_WIRELESS_EXT -+ struct ndis_assoc_info *ndis_assoc_info; -+ union iwreq_data wrqu; -+ NDIS_STATUS res; -+ const int assoc_size = sizeof(*ndis_assoc_info) + IW_CUSTOM_MAX + 32; -+#endif -+ -+ ENTER2(""); -+#ifdef CONFIG_WIRELESS_EXT -+ memset(&wrqu, 0, sizeof(wrqu)); -+ ndis_assoc_info = kzalloc(assoc_size, GFP_KERNEL); -+ if (!ndis_assoc_info) { -+ ERROR("couldn't allocate memory"); -+ goto send_assoc_event; -+ } -+ res = mp_query(wnd, OID_802_11_ASSOCIATION_INFORMATION, -+ ndis_assoc_info, assoc_size); -+ if (res) { -+ TRACE2("query assoc_info failed (%08X)", res); -+ kfree(ndis_assoc_info); -+ goto send_assoc_event; -+ } -+ TRACE2("%u, 0x%x, %u, 0x%x, %u", ndis_assoc_info->length, -+ ndis_assoc_info->req_ies, ndis_assoc_info->req_ie_length, -+ ndis_assoc_info->resp_ies, ndis_assoc_info->resp_ie_length); -+ if (ndis_assoc_info->req_ie_length > 0) { -+ wrqu.data.length = ndis_assoc_info->req_ie_length; -+ wireless_send_event(wnd->net_dev, IWEVASSOCREQIE, &wrqu, -+ ((char *)ndis_assoc_info) + -+ ndis_assoc_info->offset_req_ies); -+ } -+ if (ndis_assoc_info->resp_ie_length > 0) { -+ wrqu.data.length = ndis_assoc_info->resp_ie_length; -+ wireless_send_event(wnd->net_dev, IWEVASSOCRESPIE, &wrqu, -+ ((char *)ndis_assoc_info) + -+ ndis_assoc_info->offset_resp_ies); -+ } -+ kfree(ndis_assoc_info); -+ -+send_assoc_event: -+ get_ap_address(wnd, wrqu.ap_addr.sa_data); -+ wrqu.ap_addr.sa_family = ARPHRD_ETHER; -+ TRACE2(MACSTRSEP, MAC2STR(wrqu.ap_addr.sa_data)); -+ wireless_send_event(wnd->net_dev, SIOCGIWAP, &wrqu, NULL); -+#endif -+ EXIT2(return); -+} -+ -+static void iw_stats_timer_proc(unsigned long data) -+{ -+ struct ndis_device *wnd = (struct ndis_device *)data; -+ -+ ENTER2("%d", wnd->iw_stats_interval); -+ if (wnd->iw_stats_interval > 0) { -+ set_bit(COLLECT_IW_STATS, &wnd->ndis_pending_work); -+ queue_work(wrapndis_wq, &wnd->ndis_work); -+ } -+ mod_timer(&wnd->iw_stats_timer, jiffies + wnd->iw_stats_interval); -+} -+ -+static void add_iw_stats_timer(struct ndis_device *wnd) -+{ -+ if (wnd->physical_medium != NdisPhysicalMediumWirelessLan) -+ return; -+ if (wnd->iw_stats_interval < 0) -+ wnd->iw_stats_interval *= -1; -+ wnd->iw_stats_timer.data = (unsigned long)wnd; -+ wnd->iw_stats_timer.function = iw_stats_timer_proc; -+ mod_timer(&wnd->iw_stats_timer, jiffies + wnd->iw_stats_interval); -+} -+ -+static void del_iw_stats_timer(struct ndis_device *wnd) -+{ -+ ENTER2("%d", wnd->iw_stats_interval); -+ wnd->iw_stats_interval *= -1; -+ del_timer_sync(&wnd->iw_stats_timer); -+ EXIT2(return); -+} -+ -+static void hangcheck_proc(unsigned long data) -+{ -+ struct ndis_device *wnd = (struct ndis_device *)data; -+ -+ ENTER3("%d", wnd->hangcheck_interval); -+ if (wnd->hangcheck_interval > 0) { -+ set_bit(HANGCHECK, &wnd->ndis_pending_work); -+ queue_work(wrapndis_wq, &wnd->ndis_work); -+ } -+ mod_timer(&wnd->hangcheck_timer, jiffies + wnd->hangcheck_interval); -+ EXIT3(return); -+} -+ -+void hangcheck_add(struct ndis_device *wnd) -+{ -+ if (!wnd->wd->driver->ndis_driver->mp.hangcheck || -+ hangcheck_interval < 0) -+ EXIT2(return); -+ -+ if (hangcheck_interval > 0) -+ wnd->hangcheck_interval = hangcheck_interval * HZ; -+ if (wnd->hangcheck_interval < 0) -+ wnd->hangcheck_interval *= -1; -+ wnd->hangcheck_timer.data = (unsigned long)wnd; -+ wnd->hangcheck_timer.function = hangcheck_proc; -+ mod_timer(&wnd->hangcheck_timer, jiffies + wnd->hangcheck_interval); -+ EXIT2(return); -+} -+ -+void hangcheck_del(struct ndis_device *wnd) -+{ -+ ENTER2("%d", wnd->hangcheck_interval); -+ if (wnd->hangcheck_interval > 0) -+ wnd->hangcheck_interval *= -1; -+ del_timer_sync(&wnd->hangcheck_timer); -+ EXIT2(return); -+} -+ -+/* worker procedure to take care of setting/checking various states */ -+static void wrapndis_worker(struct work_struct *work) -+{ -+ struct ndis_device *wnd; -+ -+ wnd = container_of(work, struct ndis_device, ndis_work); -+ WORKTRACE("0x%lx", wnd->ndis_pending_work); -+ -+ if (test_and_clear_bit(NETIF_WAKEQ, &wnd->ndis_pending_work)) { -+ netif_tx_lock_bh(wnd->net_dev); -+ netif_wake_queue(wnd->net_dev); -+ netif_tx_unlock_bh(wnd->net_dev); -+ } -+ -+ if (test_and_clear_bit(LINK_STATUS_OFF, &wnd->ndis_pending_work)) -+ link_status_off(wnd); -+ -+ if (test_and_clear_bit(LINK_STATUS_ON, &wnd->ndis_pending_work)) -+ link_status_on(wnd); -+ -+ if (test_and_clear_bit(COLLECT_IW_STATS, &wnd->ndis_pending_work)) -+ update_iw_stats(wnd); -+ -+ if (test_and_clear_bit(SET_MULTICAST_LIST, -+ &wnd->ndis_pending_work)) -+ set_multicast_list(wnd); -+ -+ if (test_and_clear_bit(HANGCHECK, &wnd->ndis_pending_work)) { -+ struct miniport *mp; -+ BOOLEAN reset; -+ KIRQL irql; -+ -+ mp = &wnd->wd->driver->ndis_driver->mp; -+ irql = serialize_lock_irql(wnd); -+ reset = LIN2WIN1(mp->hangcheck, wnd->nmb->mp_ctx); -+ serialize_unlock_irql(wnd, irql); -+ if (reset) { -+ TRACE2("%s needs reset", wnd->net_dev->name); -+ mp_reset(wnd); -+ } -+ } -+ WORKEXIT(return); -+} -+ -+NDIS_STATUS ndis_reinit(struct ndis_device *wnd) -+{ -+ NDIS_STATUS status; -+ -+ wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; -+ status = mp_set_power_state(wnd, NdisDeviceStateD3); -+ if (status != NDIS_STATUS_SUCCESS) { -+ ERROR("halting device %s failed: %08X", wnd->net_dev->name, -+ status); -+ return status; -+ } -+ status = mp_set_power_state(wnd, NdisDeviceStateD0); -+ if (status != NDIS_STATUS_SUCCESS) -+ ERROR("starting device %s failed: %08X", wnd->net_dev->name, -+ status); -+ return status; -+} -+ -+#ifdef CONFIG_WIRELESS_EXT -+static void get_encryption_capa(struct ndis_device *wnd, char *buf, -+ const int buf_len) -+{ -+ int i, mode; -+ NDIS_STATUS res; -+ struct ndis_assoc_info ndis_assoc_info; -+ struct ndis_add_key ndis_key; -+ struct ndis_capability *c; -+ -+ ENTER1("%p", wnd); -+ /* set network type to g, b, or a, in that order */ -+ res = mp_query(wnd, OID_802_11_NETWORK_TYPES_SUPPORTED, buf, buf_len); -+ if (res == NDIS_STATUS_SUCCESS) { -+ struct network_type_list *net_types; -+ unsigned long types = 0; -+ net_types = (typeof(net_types))buf; -+ for (i = 0; i < net_types->num; i++) { -+ TRACE2("%d", net_types->types[i]); -+ set_bit(net_types->types[i], &types); -+ } -+ if (test_bit(Ndis802_11OFDM24, &types)) -+ mode = Ndis802_11OFDM24; -+ else if (test_bit(Ndis802_11DS, &types)) -+ mode = Ndis802_11DS; -+ else if (test_bit(Ndis802_11OFDM5, &types)) -+ mode = Ndis802_11OFDM5; -+ else -+ mode = Ndis802_11DS; -+ mp_set_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, mode); -+ } -+ /* check if WEP is supported */ -+ if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104, -+ IW_AUTH_CIPHER_NONE) == 0 && -+ get_ndis_encr_mode(wnd) == Ndis802_11Encryption1KeyAbsent) -+ set_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr); -+ -+ /* check if WPA is supported */ -+ if (set_ndis_auth_mode(wnd, Ndis802_11AuthModeWPA) == 0 && -+ get_ndis_auth_mode(wnd) == Ndis802_11AuthModeWPA) -+ set_bit(Ndis802_11AuthModeWPA, &wnd->capa.encr); -+ else -+ EXIT1(return); -+ -+ if (set_ndis_auth_mode(wnd, Ndis802_11AuthModeWPAPSK) == 0 && -+ get_ndis_auth_mode(wnd) == Ndis802_11AuthModeWPAPSK) -+ set_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.encr); -+ -+ /* check for highest encryption */ -+ mode = 0; -+ if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_CCMP, -+ IW_AUTH_CIPHER_NONE) == 0 && -+ (i = get_ndis_encr_mode(wnd)) > 0 && -+ (i == Ndis802_11Encryption3KeyAbsent || -+ i == Ndis802_11Encryption3Enabled)) -+ mode = Ndis802_11Encryption3Enabled; -+ else if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_TKIP, -+ IW_AUTH_CIPHER_NONE) == 0 && -+ (i = get_ndis_encr_mode(wnd)) > 0 && -+ (i == Ndis802_11Encryption2KeyAbsent || -+ i == Ndis802_11Encryption2Enabled)) -+ mode = Ndis802_11Encryption2Enabled; -+ else if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104, -+ IW_AUTH_CIPHER_NONE) == 0 && -+ (i = get_ndis_encr_mode(wnd)) > 0 && -+ (i == Ndis802_11Encryption1KeyAbsent || -+ i == Ndis802_11Encryption1Enabled)) -+ mode = Ndis802_11Encryption1Enabled; -+ -+ TRACE1("mode: %d", mode); -+ if (mode == 0) -+ EXIT1(return); -+ set_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr); -+ if (mode == Ndis802_11Encryption1Enabled) -+ EXIT1(return); -+ -+ ndis_key.length = 32; -+ ndis_key.index = 0xC0000001; -+ ndis_key.struct_size = sizeof(ndis_key); -+ res = mp_set(wnd, OID_802_11_ADD_KEY, &ndis_key, ndis_key.struct_size); -+ TRACE2("%08X, %zu", res, sizeof(ndis_key)); -+ if (res && res != NDIS_STATUS_INVALID_DATA) -+ EXIT1(return); -+ res = mp_query(wnd, OID_802_11_ASSOCIATION_INFORMATION, -+ &ndis_assoc_info, sizeof(ndis_assoc_info)); -+ TRACE1("%08X", res); -+ if (res == NDIS_STATUS_NOT_SUPPORTED) -+ EXIT1(return); -+ -+ set_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr); -+ if (mode == Ndis802_11Encryption3Enabled) -+ set_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr); -+ /* not all drivers support OID_802_11_CAPABILITY, so we don't -+ * know for sure if driver support WPA or WPAPSK; assume -+ * WPAPSK */ -+ set_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.auth); -+ wnd->max_pmkids = 1; -+ -+ memset(buf, 0, buf_len); -+ c = (struct ndis_capability *)buf; -+ res = mp_query(wnd, OID_802_11_CAPABILITY, buf, buf_len); -+ if (!(res == NDIS_STATUS_SUCCESS && c->version == 2)) -+ EXIT1(return); -+ wnd->max_pmkids = c->num_PMKIDs; -+ -+ for (i = 0; i < c->num_auth_encr_pair; i++) { -+ struct ndis_auth_encr_pair *ae; -+ -+ ae = &c->auth_encr_pair[i]; -+ if ((char *)(ae + 1) > buf + buf_len) -+ break; -+ switch (ae->auth_mode) { -+ case Ndis802_11AuthModeOpen: -+ case Ndis802_11AuthModeShared: -+ case Ndis802_11AuthModeWPA: -+ case Ndis802_11AuthModeWPAPSK: -+ case Ndis802_11AuthModeWPANone: -+ case Ndis802_11AuthModeWPA2: -+ case Ndis802_11AuthModeWPA2PSK: -+ set_bit(ae->auth_mode, &wnd->capa.auth); -+ break; -+ default: -+ WARNING("unknown auth_mode: %d", ae->auth_mode); -+ break; -+ } -+ switch (ae->encr_mode) { -+ case Ndis802_11EncryptionDisabled: -+ case Ndis802_11Encryption1Enabled: -+ case Ndis802_11Encryption2Enabled: -+ case Ndis802_11Encryption3Enabled: -+ set_bit(ae->encr_mode, &wnd->capa.encr); -+ break; -+ default: -+ WARNING("unknown encr_mode: %d", ae->encr_mode); -+ break; -+ } -+ } -+ EXIT1(return); -+} -+#endif -+ -+wstdcall NTSTATUS NdisDispatchDeviceControl(struct device_object *fdo, -+ struct irp *irp) -+{ -+ struct ndis_device *wnd; -+ -+ TRACE3("fdo: %p", fdo); -+ /* for now, we don't have anything interesting here, so pass it -+ * down to bus driver */ -+ wnd = fdo->reserved; -+ return IoPassIrpDown(wnd->nmb->pdo, irp); -+} -+WIN_FUNC_DECL(NdisDispatchDeviceControl,2) -+ -+wstdcall NTSTATUS NdisDispatchPower(struct device_object *fdo, struct irp *irp) -+{ -+ struct io_stack_location *irp_sl; -+ struct ndis_device *wnd; -+ enum ndis_power_state state; -+ NTSTATUS status; -+ NDIS_STATUS ndis_status; -+ -+ irp_sl = IoGetCurrentIrpStackLocation(irp); -+ wnd = fdo->reserved; -+ IOTRACE("fdo: %p, fn: %d:%d, wnd: %p", fdo, irp_sl->major_fn, -+ irp_sl->minor_fn, wnd); -+ if ((irp_sl->params.power.type == SystemPowerState && -+ irp_sl->params.power.state.system_state > PowerSystemWorking) || -+ (irp_sl->params.power.type == DevicePowerState && -+ irp_sl->params.power.state.device_state > PowerDeviceD0)) -+ state = NdisDeviceStateD3; -+ else -+ state = NdisDeviceStateD0; -+ switch (irp_sl->minor_fn) { -+ case IRP_MN_SET_POWER: -+ if (state == NdisDeviceStateD0) { -+ status = IoSyncForwardIrp(wnd->nmb->pdo, irp); -+ if (status != STATUS_SUCCESS) -+ break; -+ ndis_status = mp_set_power_state(wnd, state); -+ if (ndis_status != NDIS_STATUS_SUCCESS) -+ WARNING("couldn't set power to %d: %08X", -+ state, ndis_status); -+ TRACE2("%s: device resumed", wnd->net_dev->name); -+ irp->io_status.status = status = STATUS_SUCCESS; -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ break; -+ } else { -+ ndis_status = mp_set_power_state(wnd, state); -+ /* TODO: handle error case */ -+ if (ndis_status != NDIS_STATUS_SUCCESS) -+ WARNING("setting power to %d failed: %08X", -+ state, ndis_status); -+ status = IoAsyncForwardIrp(wnd->nmb->pdo, irp); -+ } -+ break; -+ case IRP_MN_QUERY_POWER: -+ if (wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND) { -+ ndis_status = mp_query(wnd, OID_PNP_QUERY_POWER, -+ &state, sizeof(state)); -+ TRACE2("%d, %08X", state, ndis_status); -+ /* this OID must always succeed */ -+ if (ndis_status != NDIS_STATUS_SUCCESS) -+ TRACE1("query power returns %08X", ndis_status); -+ irp->io_status.status = STATUS_SUCCESS; -+ } else -+ irp->io_status.status = STATUS_SUCCESS; -+ status = IoPassIrpDown(wnd->nmb->pdo, irp); -+ break; -+ case IRP_MN_WAIT_WAKE: -+ case IRP_MN_POWER_SEQUENCE: -+ /* TODO: implement WAIT_WAKE */ -+ status = IoPassIrpDown(wnd->nmb->pdo, irp); -+ break; -+ default: -+ status = IoPassIrpDown(wnd->nmb->pdo, irp); -+ break; -+ } -+ IOEXIT(return status); -+} -+WIN_FUNC_DECL(NdisDispatchPower,2) -+ -+wstdcall NTSTATUS NdisDispatchPnp(struct device_object *fdo, struct irp *irp) -+{ -+ struct io_stack_location *irp_sl; -+ struct ndis_device *wnd; -+ struct device_object *pdo; -+ NTSTATUS status; -+ -+ IOTRACE("fdo: %p, irp: %p", fdo, irp); -+ irp_sl = IoGetCurrentIrpStackLocation(irp); -+ wnd = fdo->reserved; -+ pdo = wnd->nmb->pdo; -+ switch (irp_sl->minor_fn) { -+ case IRP_MN_START_DEVICE: -+ status = IoSyncForwardIrp(pdo, irp); -+ if (status != STATUS_SUCCESS) -+ break; -+ if (ndis_start_device(wnd) == NDIS_STATUS_SUCCESS) -+ status = STATUS_SUCCESS; -+ else -+ status = STATUS_FAILURE; -+ irp->io_status.status = status; -+ IoCompleteRequest(irp, IO_NO_INCREMENT); -+ break; -+ case IRP_MN_QUERY_STOP_DEVICE: -+ /* TODO: implement in NDIS */ -+ status = IoPassIrpDown(wnd->nmb->pdo, irp); -+ break; -+ case IRP_MN_STOP_DEVICE: -+ mp_halt(wnd); -+ irp->io_status.status = STATUS_SUCCESS; -+ status = IoAsyncForwardIrp(pdo, irp); -+ break; -+ case IRP_MN_REMOVE_DEVICE: -+ TRACE1("%s", wnd->net_dev->name); -+ mp_pnp_event(wnd, NdisDevicePnPEventSurpriseRemoved, 0); -+ if (ndis_remove_device(wnd)) { -+ status = STATUS_FAILURE; -+ break; -+ } -+ /* wnd is already freed */ -+ status = IoAsyncForwardIrp(pdo, irp); -+ IoDetachDevice(fdo); -+ IoDeleteDevice(fdo); -+ break; -+ default: -+ status = IoAsyncForwardIrp(pdo, irp); -+ break; -+ } -+ IOTRACE("status: %08X", status); -+ IOEXIT(return status); -+} -+WIN_FUNC_DECL(NdisDispatchPnp,2) -+ -+static void set_task_offload(struct ndis_device *wnd, void *buf, -+ const int buf_size) -+{ -+ struct ndis_task_offload_header *task_offload_header; -+ struct ndis_task_offload *task_offload; -+ struct ndis_task_tcp_ip_checksum *csum = NULL; -+ struct ndis_task_tcp_large_send *tso = NULL; -+ NDIS_STATUS status; -+ -+ memset(buf, 0, buf_size); -+ task_offload_header = buf; -+ task_offload_header->version = NDIS_TASK_OFFLOAD_VERSION; -+ task_offload_header->size = sizeof(*task_offload_header); -+ task_offload_header->encap_format.flags.fixed_header_size = 1; -+ task_offload_header->encap_format.header_size = sizeof(struct ethhdr); -+ task_offload_header->encap_format.encap = IEEE_802_3_Encapsulation; -+ status = mp_query(wnd, OID_TCP_TASK_OFFLOAD, buf, buf_size); -+ TRACE1("%08X", status); -+ if (status != NDIS_STATUS_SUCCESS) -+ EXIT1(return); -+ if (task_offload_header->offset_first_task == 0) -+ EXIT1(return); -+ task_offload = ((void *)task_offload_header + -+ task_offload_header->offset_first_task); -+ while (1) { -+ TRACE1("%d, %d", task_offload->version, task_offload->task); -+ switch (task_offload->task) { -+ case TcpIpChecksumNdisTask: -+ csum = (void *)task_offload->task_buf; -+ break; -+ case TcpLargeSendNdisTask: -+ tso = (void *)task_offload->task_buf; -+ break; -+ default: -+ TRACE1("%d", task_offload->task); -+ break; -+ } -+ if (task_offload->offset_next_task == 0) -+ break; -+ task_offload = (void *)task_offload + -+ task_offload->offset_next_task; -+ } -+ if (tso) -+ TRACE1("%u, %u, %d, %d", tso->max_size, tso->min_seg_count, -+ tso->tcp_opts, tso->ip_opts); -+ if (!csum) -+ EXIT1(return); -+ TRACE1("%08x, %08x", csum->v4_tx.value, csum->v4_rx.value); -+ task_offload_header->encap_format.flags.fixed_header_size = 1; -+ task_offload_header->encap_format.header_size = sizeof(struct ethhdr); -+ task_offload_header->offset_first_task = sizeof(*task_offload_header); -+ task_offload = ((void *)task_offload_header + -+ task_offload_header->offset_first_task); -+ task_offload->offset_next_task = 0; -+ task_offload->size = sizeof(*task_offload); -+ task_offload->task = TcpIpChecksumNdisTask; -+ memcpy(task_offload->task_buf, csum, sizeof(*csum)); -+ task_offload->task_buf_length = sizeof(*csum); -+ status = mp_set(wnd, OID_TCP_TASK_OFFLOAD, task_offload_header, -+ sizeof(*task_offload_header) + -+ sizeof(*task_offload) + sizeof(*csum)); -+ TRACE1("%08X", status); -+ if (status != NDIS_STATUS_SUCCESS) -+ EXIT2(return); -+ wnd->tx_csum = csum->v4_tx; -+ if (csum->v4_tx.tcp_csum && csum->v4_tx.udp_csum) { -+ if (csum->v4_tx.ip_csum) { -+ wnd->net_dev->features |= NETIF_F_HW_CSUM; -+ TRACE1("hw checksum enabled"); -+ } else { -+ wnd->net_dev->features |= NETIF_F_IP_CSUM; -+ TRACE1("IP checksum enabled"); -+ } -+ if (wnd->sg_dma_size) -+ wnd->net_dev->features |= NETIF_F_SG; -+ } -+ wnd->rx_csum = csum->v4_rx; -+ EXIT1(return); -+} -+ -+static void get_supported_oids(struct ndis_device *wnd) -+{ -+ NDIS_STATUS res; -+ int i, n, needed; -+ ndis_oid *oids; -+ -+ res = mp_query_info(wnd, OID_GEN_SUPPORTED_LIST, NULL, 0, NULL, -+ &needed); -+ if (!(res == NDIS_STATUS_BUFFER_TOO_SHORT || -+ res == NDIS_STATUS_INVALID_LENGTH)) -+ EXIT1(return); -+ oids = kmalloc(needed, GFP_KERNEL); -+ if (!oids) { -+ TRACE1("couldn't allocate memory"); -+ EXIT1(return); -+ } -+ res = mp_query(wnd, OID_GEN_SUPPORTED_LIST, oids, needed); -+ if (res) { -+ TRACE1("failed: %08X", res); -+ kfree(oids); -+ EXIT1(return); -+ } -+ for (i = 0, n = needed / sizeof(*oids); i < n; i++) { -+ TRACE1("oid: %08X", oids[i]); -+ /* if a wireless device didn't say so for -+ * OID_GEN_PHYSICAL_MEDIUM (they should, but in case) */ -+ if (wnd->physical_medium != NdisPhysicalMediumWirelessLan && -+ oids[i] == OID_802_11_SSID) -+ wnd->physical_medium = NdisPhysicalMediumWirelessLan; -+ } -+ kfree(oids); -+ EXIT1(return); -+} -+ -+static void ndis_get_drvinfo(struct net_device *dev, -+ struct ethtool_drvinfo *info) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 2); -+ strcat(info->driver, "+"); -+ strncat(info->driver, wnd->wd->driver->name, -+ sizeof(info->driver) - strlen(DRIVER_NAME) - 1); -+ strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 2); -+ strcat(info->version, "+"); -+ strncat(info->version, wnd->wd->driver->version, -+ sizeof(info->version) - strlen(DRIVER_VERSION) - 1); -+ if (wrap_is_pci_bus(wnd->wd->dev_bus)) -+ strncpy(info->bus_info, pci_name(wnd->wd->pci.pdev), -+ sizeof(info->bus_info) - 1); -+#ifdef ENABLE_USB -+ else -+ usb_make_path(wnd->wd->usb.udev, info->bus_info, -+ sizeof(info->bus_info) - 1); -+#endif -+ return; -+} -+ -+static u32 ndis_get_link(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ return netif_carrier_ok(wnd->net_dev); -+} -+ -+static void ndis_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ wol->supported = 0; -+ wol->wolopts = 0; -+ if (!(wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND)) -+ EXIT2(return); -+ if (!wrap_is_pci_bus(wnd->wd->dev_bus)) -+ EXIT2(return); -+ /* we always suspend to D3 */ -+ if (wnd->pnp_capa.wakeup.min_magic_packet_wakeup < NdisDeviceStateD3) -+ return; -+ wol->supported |= WAKE_MAGIC; -+ if (wnd->ndis_wolopts & NDIS_PNP_WAKE_UP_MAGIC_PACKET) -+ wol->wolopts |= WAKE_MAGIC; -+ return; -+} -+ -+static int ndis_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ if (!(wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND)) -+ return -EOPNOTSUPP; -+ if (wnd->pnp_capa.wakeup.min_magic_packet_wakeup < NdisDeviceStateD3) -+ EXIT2(return -EOPNOTSUPP); -+ TRACE2("0x%x", wol->wolopts); -+ if (wol->wolopts & WAKE_MAGIC) { -+ wnd->ndis_wolopts |= NDIS_PNP_WAKE_UP_MAGIC_PACKET; -+ if (wol->wolopts != WAKE_MAGIC) -+ WARNING("ignored wake-on-lan options: 0x%x", -+ wol->wolopts & ~WAKE_MAGIC); -+ } else if (!wol->wolopts) -+ wnd->ndis_wolopts = 0; -+ else -+ return -EOPNOTSUPP; -+ TRACE2("0x%x", wnd->ndis_wolopts); -+ return 0; -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) -+static u32 ndis_get_tx_csum(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ if (wnd->tx_csum.tcp_csum && wnd->tx_csum.udp_csum) -+ return 1; -+ else -+ return 0; -+} -+ -+static u32 ndis_get_rx_csum(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ if (wnd->rx_csum.value) -+ return 1; -+ else -+ return 0; -+} -+ -+static int ndis_set_tx_csum(struct net_device *dev, u32 data) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ if (data && (wnd->tx_csum.value == 0)) -+ return -EOPNOTSUPP; -+ -+ if (wnd->tx_csum.ip_csum) -+ ethtool_op_set_tx_hw_csum(dev, data); -+ else -+ ethtool_op_set_tx_csum(dev, data); -+ return 0; -+} -+ -+static int ndis_set_rx_csum(struct net_device *dev, u32 data) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ -+ if (data && (wnd->tx_csum.value == 0)) -+ return -EOPNOTSUPP; -+ -+ /* TODO: enable/disable rx csum through NDIS */ -+ return 0; -+} -+ -+static u32 ndis_get_sg(struct net_device *dev) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ if (wnd->sg_dma_size) -+ return ethtool_op_get_sg(dev); -+ else -+ return 0; -+} -+ -+static int ndis_set_sg(struct net_device *dev, u32 data) -+{ -+ struct ndis_device *wnd = netdev_priv(dev); -+ if (wnd->sg_dma_size) -+ return ethtool_op_set_sg(dev, data); -+ else -+ return -EOPNOTSUPP; -+} -+#endif -+ -+static struct ethtool_ops ndis_ethtool_ops = { -+ .get_drvinfo = ndis_get_drvinfo, -+ .get_link = ndis_get_link, -+ .get_wol = ndis_get_wol, -+ .set_wol = ndis_set_wol, -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) -+ .get_tx_csum = ndis_get_tx_csum, -+ .get_rx_csum = ndis_get_rx_csum, -+ .set_tx_csum = ndis_set_tx_csum, -+ .set_rx_csum = ndis_set_rx_csum, -+ .get_sg = ndis_get_sg, -+ .set_sg = ndis_set_sg, -+#endif -+}; -+ -+static int notifier_event(struct notifier_block *notifier, unsigned long event, -+ void *ptr) -+{ -+ struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); -+ -+ ENTER2("0x%lx", event); -+ if (net_dev->ethtool_ops == &ndis_ethtool_ops -+ && event == NETDEV_CHANGENAME) { -+ struct ndis_device *wnd = netdev_priv(net_dev); -+ -+ /* called with rtnl lock held, so no need to lock */ -+ if (likely(wnd->procfs_iface)) { -+ printk(KERN_INFO "%s: interface renamed to '%s'\n", -+ DRIVER_NAME, net_dev->name); -+ wrap_procfs_remove_ndis_device(wnd); -+ wrap_procfs_add_ndis_device(wnd); -+ } -+ } -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block netdev_notifier = { -+ .notifier_call = notifier_event, -+}; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) -+static const struct net_device_ops ndis_netdev_ops = { -+ .ndo_init = ndis_net_dev_init, -+ .ndo_uninit = ndis_net_dev_uninit, -+ .ndo_open = ndis_net_dev_open, -+ .ndo_stop = ndis_net_dev_close, -+ .ndo_start_xmit = tx_skbuff, -+ .ndo_change_mtu = ndis_change_mtu, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) -+ .ndo_set_rx_mode = ndis_set_multicast_list, -+#else -+ .ndo_set_multicast_list = ndis_set_multicast_list, -+#endif -+ .ndo_set_mac_address = ndis_set_mac_address, -+ .ndo_get_stats = ndis_get_stats, -+#ifdef CONFIG_NET_POLL_CONTROLLER -+ .ndo_poll_controller = ndis_poll_controller, -+#endif -+}; -+#endif -+ -+static NDIS_STATUS ndis_start_device(struct ndis_device *wnd) -+{ -+ struct wrap_device *wd; -+ struct net_device *net_dev; -+ NDIS_STATUS status; -+ char *buf; -+ const int buf_len = 256; -+ mac_address mac; -+ struct transport_header_offset *tx_header_offset; -+ int n; -+ -+ ENTER2("%d", in_atomic()); -+ status = mp_init(wnd); -+ if (status == NDIS_STATUS_NOT_RECOGNIZED) -+ EXIT1(return NDIS_STATUS_SUCCESS); -+ if (status != NDIS_STATUS_SUCCESS) -+ EXIT1(return status); -+ wd = wnd->wd; -+ net_dev = wnd->net_dev; -+ -+ get_supported_oids(wnd); -+ memset(mac, 0, sizeof(mac)); -+ status = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac)); -+ if (memcmp(mac, "\x00\x00\x00\x00\x00\x00", sizeof(mac)) == 0) { -+ status = mp_query(wnd, OID_802_3_PERMANENT_ADDRESS, mac, -+ sizeof(mac)); -+ if (status != NDIS_STATUS_SUCCESS) { -+ ERROR("couldn't get mac address: %08X", status); -+ goto err_start; -+ } -+ } -+ TRACE1("mac:" MACSTRSEP, MAC2STR(mac)); -+ memcpy(net_dev->dev_addr, mac, ETH_ALEN); -+ -+ strncpy(net_dev->name, if_name, IFNAMSIZ - 1); -+ net_dev->name[IFNAMSIZ - 1] = 0; -+ -+ wnd->packet_filter = NDIS_PACKET_TYPE_DIRECTED | -+ NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_MULTICAST; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) -+ net_dev->netdev_ops = &ndis_netdev_ops; -+#else -+ net_dev->init = ndis_net_dev_init; -+ net_dev->uninit = ndis_net_dev_uninit; -+ net_dev->open = ndis_net_dev_open; -+ net_dev->hard_start_xmit = tx_skbuff; -+ net_dev->stop = ndis_net_dev_close; -+ net_dev->get_stats = ndis_get_stats; -+ net_dev->change_mtu = ndis_change_mtu; -+ net_dev->set_multicast_list = ndis_set_multicast_list; -+ net_dev->set_mac_address = ndis_set_mac_address; -+#ifdef CONFIG_NET_POLL_CONTROLLER -+ net_dev->poll_controller = ndis_poll_controller; -+#endif -+#endif -+#ifdef CONFIG_WIRELESS_EXT -+ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { -+ net_dev->wireless_handlers = &ndis_handler_def; -+ } -+#endif -+ net_dev->ethtool_ops = &ndis_ethtool_ops; -+ if (wnd->mp_interrupt) -+ net_dev->irq = wnd->mp_interrupt->irq; -+ net_dev->mem_start = wnd->mem_start; -+ net_dev->mem_end = wnd->mem_end; -+ status = mp_query_int(wnd, OID_802_3_MAXIMUM_LIST_SIZE, -+ &wnd->multicast_size); -+ if (status != NDIS_STATUS_SUCCESS || wnd->multicast_size < 0) -+ wnd->multicast_size = 0; -+ if (wnd->multicast_size > 0) -+ net_dev->flags |= IFF_MULTICAST; -+ else -+ net_dev->flags &= ~IFF_MULTICAST; -+ -+ buf = kmalloc(buf_len, GFP_KERNEL); -+ if (!buf) { -+ WARNING("couldn't allocate memory"); -+ goto err_start; -+ } -+ -+ set_task_offload(wnd, buf, buf_len); -+#ifdef NETIF_F_LLTX -+ net_dev->features |= NETIF_F_LLTX; -+#endif -+ -+ if (register_netdev(net_dev)) { -+ ERROR("cannot register net device %s", net_dev->name); -+ goto err_register; -+ } -+ memset(buf, 0, buf_len); -+ status = mp_query(wnd, OID_GEN_VENDOR_DESCRIPTION, buf, buf_len); -+ if (status != NDIS_STATUS_SUCCESS) { -+ WARNING("couldn't get vendor information: 0x%x", status); -+ buf[0] = 0; -+ } -+ wnd->drv_ndis_version = n = 0; -+ mp_query_int(wnd, OID_GEN_DRIVER_VERSION, &wnd->drv_ndis_version); -+ mp_query_int(wnd, OID_GEN_VENDOR_DRIVER_VERSION, &n); -+ -+ printk(KERN_INFO "%s: ethernet device " MACSTRSEP " using %sNDIS " -+ "driver: %s, version: 0x%x, NDIS version: 0x%x, vendor: '%s', " -+ "%s\n", net_dev->name, MAC2STR(net_dev->dev_addr), -+ deserialized_driver(wnd) ? "" : "serialized ", -+ wd->driver->name, n, wnd->drv_ndis_version, buf, -+ wd->conf_file_name); -+ -+ if (deserialized_driver(wnd)) { -+ /* deserialized drivers don't have a limit, but we -+ * keep max at TX_RING_SIZE */ -+ wnd->max_tx_packets = TX_RING_SIZE; -+ } else { -+ status = mp_query_int(wnd, OID_GEN_MAXIMUM_SEND_PACKETS, -+ &wnd->max_tx_packets); -+ if (status != NDIS_STATUS_SUCCESS) -+ wnd->max_tx_packets = 1; -+ if (wnd->max_tx_packets > TX_RING_SIZE) -+ wnd->max_tx_packets = TX_RING_SIZE; -+ } -+ TRACE2("maximum send packets: %d", wnd->max_tx_packets); -+ NdisAllocatePacketPoolEx(&status, &wnd->tx_packet_pool, -+ wnd->max_tx_packets, 0, -+ PROTOCOL_RESERVED_SIZE_IN_PACKET); -+ if (status != NDIS_STATUS_SUCCESS) { -+ ERROR("couldn't allocate packet pool"); -+ goto packet_pool_err; -+ } -+ NdisAllocateBufferPool(&status, &wnd->tx_buffer_pool, -+ wnd->max_tx_packets + 4); -+ if (status != NDIS_STATUS_SUCCESS) { -+ ERROR("couldn't allocate buffer pool"); -+ goto buffer_pool_err; -+ } -+ TRACE1("pool: %p", wnd->tx_buffer_pool); -+ -+ if (mp_query_int(wnd, OID_GEN_MAXIMUM_TOTAL_SIZE, &n) == -+ NDIS_STATUS_SUCCESS && n > ETH_HLEN) -+ ndis_change_mtu(wnd->net_dev, n - ETH_HLEN); -+ -+ if (mp_query_int(wnd, OID_GEN_MAC_OPTIONS, &n) == NDIS_STATUS_SUCCESS) -+ TRACE2("mac options supported: 0x%x", n); -+ -+ tx_header_offset = (typeof(tx_header_offset))buf; -+ tx_header_offset->protocol_type = NDIS_PROTOCOL_ID_TCP_IP; -+ tx_header_offset->header_offset = sizeof(ETH_HLEN); -+ status = mp_set(wnd, OID_GEN_TRANSPORT_HEADER_OFFSET, -+ tx_header_offset, sizeof(*tx_header_offset)); -+ TRACE2("%08X", status); -+ -+ status = mp_query_int(wnd, OID_GEN_PHYSICAL_MEDIUM, -+ &wnd->physical_medium); -+ if (status != NDIS_STATUS_SUCCESS) -+ wnd->physical_medium = NdisPhysicalMediumUnspecified; -+ -+#ifdef CONFIG_WIRELESS_EXT -+ if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) { -+ mp_set_int(wnd, OID_802_11_POWER_MODE, NDIS_POWER_OFF); -+ get_encryption_capa(wnd, buf, buf_len); -+ TRACE1("capabilities = %ld", wnd->capa.encr); -+ printk(KERN_INFO "%s: encryption modes supported: " -+ "%s%s%s%s%s%s%s\n", net_dev->name, -+ test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr) ? -+ "WEP" : "none", -+ -+ test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr) ? -+ "; TKIP with WPA" : "", -+ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? -+ ", WPA2" : "", -+ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? -+ ", WPA2-PSK" : "", -+ -+ test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr) ? -+ "; AES/CCMP with WPA" : "", -+ test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ? -+ ", WPA2" : "", -+ test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ? -+ ", WPA2-PSK" : ""); -+ -+ set_default_iw_params(wnd); -+ } -+#endif -+ kfree(buf); -+ hangcheck_add(wnd); -+ add_iw_stats_timer(wnd); -+ EXIT1(return NDIS_STATUS_SUCCESS); -+ -+buffer_pool_err: -+ wnd->tx_buffer_pool = NULL; -+ if (wnd->tx_packet_pool) { -+ NdisFreePacketPool(wnd->tx_packet_pool); -+ wnd->tx_packet_pool = NULL; -+ } -+packet_pool_err: -+ unregister_netdev(net_dev); -+ wnd->max_tx_packets = 0; -+err_register: -+ kfree(buf); -+err_start: -+ mp_halt(wnd); -+ EXIT1(return NDIS_STATUS_FAILURE); -+} -+ -+static int ndis_remove_device(struct ndis_device *wnd) -+{ -+ s8 tx_pending; -+ int our_mutex; -+ -+ /* prevent setting essid during disassociation */ -+ memset(&wnd->essid, 0, sizeof(wnd->essid)); -+ wnd->tx_ok = 0; -+ netif_carrier_off(wnd->net_dev); -+ if (wnd->max_tx_packets) -+ unregister_netdev(wnd->net_dev); -+ /* if device is suspended, but resume failed, tx_ring_mutex -+ * may already be locked */ -+ our_mutex = mutex_trylock(&wnd->tx_ring_mutex); -+ if (!our_mutex) -+ WARNING("couldn't obtain tx_ring_mutex"); -+ spin_lock_bh(&wnd->tx_ring_lock); -+ tx_pending = wnd->tx_ring_end - wnd->tx_ring_start; -+ if (tx_pending < 0) -+ tx_pending += TX_RING_SIZE; -+ else if (tx_pending == 0 && wnd->is_tx_ring_full) -+ tx_pending = TX_RING_SIZE - 1; -+ wnd->is_tx_ring_full = 0; -+ /* throw away pending packets */ -+ while (tx_pending-- > 0) { -+ struct ndis_packet *packet; -+ -+ packet = wnd->tx_ring[wnd->tx_ring_start]; -+ free_tx_packet(wnd, packet, NDIS_STATUS_CLOSING); -+ wnd->tx_ring_start = (wnd->tx_ring_start + 1) % TX_RING_SIZE; -+ } -+ spin_unlock_bh(&wnd->tx_ring_lock); -+ if (our_mutex) -+ mutex_unlock(&wnd->tx_ring_mutex); -+ mp_halt(wnd); -+ ndis_exit_device(wnd); -+ -+ if (wnd->tx_packet_pool) { -+ NdisFreePacketPool(wnd->tx_packet_pool); -+ wnd->tx_packet_pool = NULL; -+ } -+ if (wnd->tx_buffer_pool) { -+ NdisFreeBufferPool(wnd->tx_buffer_pool); -+ wnd->tx_buffer_pool = NULL; -+ } -+ kfree(wnd->pmkids); -+ printk(KERN_INFO "%s: device %s removed\n", DRIVER_NAME, -+ wnd->net_dev->name); -+ kfree(wnd->nmb); -+ free_netdev(wnd->net_dev); -+ EXIT2(return 0); -+} -+ -+static NTSTATUS ndis_add_device(struct driver_object *drv_obj, -+ struct device_object *pdo) -+{ -+ struct device_object *fdo; -+ struct ndis_mp_block *nmb; -+ NTSTATUS status; -+ struct ndis_device *wnd; -+ struct net_device *net_dev; -+ struct wrap_device *wd; -+ unsigned long i; -+ -+ ENTER2("%p, %p", drv_obj, pdo); -+ if (strlen(if_name) >= IFNAMSIZ) { -+ ERROR("interface name '%s' is too long", if_name); -+ return STATUS_INVALID_PARAMETER; -+ } -+ net_dev = alloc_etherdev(sizeof(*wnd)); -+ if (!net_dev) { -+ ERROR("couldn't allocate device"); -+ return STATUS_RESOURCES; -+ } -+ wd = pdo->reserved; -+ if (wrap_is_pci_bus(wd->dev_bus)) -+ SET_NETDEV_DEV(net_dev, &wd->pci.pdev->dev); -+ if (wrap_is_usb_bus(wd->dev_bus)) -+ SET_NETDEV_DEV(net_dev, &wd->usb.intf->dev); -+ status = IoCreateDevice(drv_obj, 0, NULL, FILE_DEVICE_UNKNOWN, 0, -+ FALSE, &fdo); -+ if (status != STATUS_SUCCESS) { -+ free_netdev(net_dev); -+ EXIT2(return status); -+ } -+ wnd = netdev_priv(net_dev); -+ TRACE1("wnd: %p", wnd); -+ -+ nmb = kmalloc(sizeof(*nmb), GFP_KERNEL); -+ if (!nmb) { -+ WARNING("couldn't allocate memory"); -+ IoDeleteDevice(fdo); -+ free_netdev(net_dev); -+ return STATUS_RESOURCES; -+ } -+#if DEBUG >= 6 -+ /* poison nmb so if a driver accesses uninitialized pointers, we -+ * know what it is */ -+ for (i = 0; i < sizeof(*nmb) / sizeof(unsigned long); i++) -+ ((unsigned long *)nmb)[i] = i + 0x8a3fc1; -+#endif -+ -+ wnd->nmb = nmb; -+ nmb->wnd = wnd; -+ nmb->pdo = pdo; -+ wnd->wd = wd; -+ wnd->net_dev = net_dev; -+ fdo->reserved = wnd; -+ nmb->fdo = fdo; -+ if (ndis_init_device(wnd)) { -+ IoDeleteDevice(fdo); -+ kfree(nmb); -+ free_netdev(net_dev); -+ EXIT1(return STATUS_RESOURCES); -+ } -+ nmb->next_device = IoAttachDeviceToDeviceStack(fdo, pdo); -+ spin_lock_init(&wnd->tx_ring_lock); -+ mutex_init(&wnd->tx_ring_mutex); -+ mutex_init(&wnd->ndis_req_mutex); -+ wnd->ndis_req_done = 0; -+ INIT_WORK(&wnd->tx_work, tx_worker); -+ wnd->tx_ring_start = 0; -+ wnd->tx_ring_end = 0; -+ wnd->is_tx_ring_full = 0; -+ wnd->capa.encr = 0; -+ wnd->capa.auth = 0; -+ wnd->attributes = 0; -+ wnd->dma_map_count = 0; -+ wnd->dma_map_addr = NULL; -+ wnd->nick[0] = 0; -+ init_timer(&wnd->hangcheck_timer); -+ wnd->scan_timestamp = 0; -+ init_timer(&wnd->iw_stats_timer); -+ wnd->iw_stats_interval = 10 * HZ; -+ wnd->ndis_pending_work = 0; -+ memset(&wnd->essid, 0, sizeof(wnd->essid)); -+ memset(&wnd->encr_info, 0, sizeof(wnd->encr_info)); -+ wnd->infrastructure_mode = Ndis802_11Infrastructure; -+ INIT_WORK(&wnd->ndis_work, wrapndis_worker); -+ wnd->iw_stats_enabled = TRUE; -+ -+ TRACE1("nmb: %p, pdo: %p, fdo: %p, attached: %p, next: %p", -+ nmb, pdo, fdo, fdo->attached, nmb->next_device); -+ -+ /* dispatch routines are called as Windows functions */ -+ for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) -+ drv_obj->major_func[i] = WIN_FUNC_PTR(IoPassIrpDown,2); -+ -+ drv_obj->major_func[IRP_MJ_PNP] = WIN_FUNC_PTR(NdisDispatchPnp,2); -+ drv_obj->major_func[IRP_MJ_POWER] = WIN_FUNC_PTR(NdisDispatchPower,2); -+ drv_obj->major_func[IRP_MJ_INTERNAL_DEVICE_CONTROL] = -+ WIN_FUNC_PTR(NdisDispatchDeviceControl,2); -+// drv_obj->major_func[IRP_MJ_DEVICE_CONTROL] = -+// WIN_FUNC_PTR(NdisDispatchDeviceControl,2); -+ EXIT2(return STATUS_SUCCESS); -+} -+ -+int init_ndis_driver(struct driver_object *drv_obj) -+{ -+ ENTER1("%p", drv_obj); -+ drv_obj->drv_ext->add_device = ndis_add_device; -+ return 0; -+} -+ -+int wrapndis_init(void) -+{ -+ wrapndis_wq = create_singlethread_workqueue("wrapndis_wq"); -+ if (!wrapndis_wq) -+ EXIT1(return -ENOMEM); -+ TRACE1("wrapndis_wq: %p", wrapndis_wq); -+ register_netdevice_notifier(&netdev_notifier); -+ return 0; -+} -+ -+void wrapndis_exit(void) -+{ -+ unregister_netdevice_notifier(&netdev_notifier); -+ if (wrapndis_wq) -+ destroy_workqueue(wrapndis_wq); -+} -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapndis.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.h ---- linux-3.12.2/3rdparty/ndiswrapper/wrapndis.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapndis.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,86 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef _WRAPNDIS_H_ -+#define _WRAPNDIS_H_ -+ -+#include "ndis.h" -+#include "pnp.h" -+ -+int wrapndis_init(void); -+void wrapndis_exit(void); -+ -+NDIS_STATUS mp_reset(struct ndis_device *wnd); -+ -+NDIS_STATUS mp_request(enum ndis_request_type request, -+ struct ndis_device *wnd, ndis_oid oid, -+ void *buf, ULONG buflen, ULONG *written, ULONG *needed); -+ -+static inline NDIS_STATUS mp_query_info(struct ndis_device *wnd, -+ ndis_oid oid, void *buf, ULONG buflen, -+ ULONG *written, ULONG *needed) -+{ -+ return mp_request(NdisRequestQueryInformation, wnd, oid, -+ buf, buflen, written, needed); -+} -+ -+static inline NDIS_STATUS mp_set_info(struct ndis_device *wnd, -+ ndis_oid oid, void *buf, ULONG buflen, -+ ULONG *written, ULONG *needed) -+{ -+ return mp_request(NdisRequestSetInformation, wnd, oid, -+ buf, buflen, written, needed); -+} -+ -+static inline NDIS_STATUS mp_query(struct ndis_device *wnd, ndis_oid oid, -+ void *buf, ULONG buflen) -+{ -+ return mp_request(NdisRequestQueryInformation, wnd, oid, -+ buf, buflen, NULL, NULL); -+} -+ -+static inline NDIS_STATUS mp_query_int(struct ndis_device *wnd, -+ ndis_oid oid, ULONG *data) -+{ -+ return mp_request(NdisRequestQueryInformation, wnd, oid, -+ data, sizeof(ULONG), NULL, NULL); -+} -+ -+static inline NDIS_STATUS mp_set(struct ndis_device *wnd, ndis_oid oid, -+ void *buf, ULONG buflen) -+{ -+ return mp_request(NdisRequestSetInformation, wnd, oid, -+ buf, buflen, NULL, NULL); -+} -+ -+static inline NDIS_STATUS mp_set_int(struct ndis_device *wnd, -+ ndis_oid oid, ULONG data) -+{ -+ return mp_request(NdisRequestSetInformation, wnd, oid, -+ &data, sizeof(ULONG), NULL, NULL); -+} -+ -+void free_tx_packet(struct ndis_device *wnd, struct ndis_packet *packet, -+ NDIS_STATUS status); -+int init_ndis_driver(struct driver_object *drv_obj); -+NDIS_STATUS ndis_reinit(struct ndis_device *wnd); -+void set_media_state(struct ndis_device *wnd, enum ndis_media_state state); -+ -+void hangcheck_add(struct ndis_device *wnd); -+void hangcheck_del(struct ndis_device *wnd); -+ -+struct iw_statistics *get_iw_stats(struct net_device *dev); -+ -+#endif -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapper.c linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.c ---- linux-3.12.2/3rdparty/ndiswrapper/wrapper.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.c 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,111 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#include "ndis.h" -+#include "iw_ndis.h" -+#include "loader.h" -+#include "pnp.h" -+#include "wrapper.h" -+ -+char *if_name = "wlan%d"; -+int proc_uid, proc_gid; -+int hangcheck_interval; -+static char *utils_version = UTILS_VERSION; -+int debug = DEBUG; -+ -+module_param(if_name, charp, 0400); -+MODULE_PARM_DESC(if_name, "Network interface name or template " -+ "(default: wlan%d)"); -+module_param(proc_uid, int, 0600); -+MODULE_PARM_DESC(proc_uid, "The uid of the files created in /proc " -+ "(default: 0)."); -+module_param(proc_gid, int, 0600); -+MODULE_PARM_DESC(proc_gid, "The gid of the files created in /proc " -+ "(default: 0)."); -+module_param(debug, int, 0600); -+MODULE_PARM_DESC(debug, "debug level"); -+ -+/* 0 - default value provided by NDIS driver, -+ * positive value - force hangcheck interval to that many seconds -+ * negative value - disable hangcheck -+ */ -+module_param(hangcheck_interval, int, 0600); -+MODULE_PARM_DESC(hangcheck_interval, "The interval, in seconds, for checking" -+ " if driver is hung. (default: 0)"); -+ -+module_param(utils_version, charp, 0400); -+MODULE_PARM_DESC(utils_version, "Compatible version of utils " -+ "(read only: " UTILS_VERSION ")"); -+ -+MODULE_AUTHOR("ndiswrapper team "); -+#ifdef MODULE_DESCRIPTION -+MODULE_DESCRIPTION("NDIS wrapper driver"); -+#endif -+#ifdef MODULE_VERSION -+MODULE_VERSION(DRIVER_VERSION); -+#endif -+MODULE_LICENSE("GPL"); -+ -+static void module_cleanup(void) -+{ -+ loader_exit(); -+ usb_exit(); -+ wrap_procfs_remove(); -+ wrapndis_exit(); -+ ndis_exit(); -+ ntoskernel_exit(); -+ wrapmem_exit(); -+} -+ -+static int __init wrapper_init(void) -+{ -+#ifdef TAINT_OOT_MODULE -+ add_taint(TAINT_OOT_MODULE, LOCKDEP_NOW_UNRELIABLE); -+#endif -+ printk(KERN_INFO "%s version %s loaded (smp=%s, preempt=%s)\n", -+ DRIVER_NAME, DRIVER_VERSION, -+#ifdef CONFIG_SMP -+ "yes" -+#else -+ "no" -+#endif -+ , -+#ifdef CONFIG_PREEMPT_RT -+ "rt" -+#elif defined(CONFIG_PREEMPT) -+ "yes" -+#else -+ "no" -+#endif -+ ); -+ -+ if (wrapmem_init() || ntoskernel_init() || ndis_init() || -+ wrapndis_init() || usb_init() || wrap_procfs_init() || -+ loader_init()) { -+ module_cleanup(); -+ ERROR("%s: initialization failed", DRIVER_NAME); -+ return -EINVAL; -+ } -+ EXIT1(return 0); -+} -+ -+static void __exit wrapper_exit(void) -+{ -+ ENTER1(""); -+ module_cleanup(); -+} -+ -+module_init(wrapper_init); -+module_exit(wrapper_exit); -diff -Nurp linux-3.12.2/3rdparty/ndiswrapper/wrapper.h linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.h ---- linux-3.12.2/3rdparty/ndiswrapper/wrapper.h 1970-01-01 02:00:00.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/wrapper.h 2013-11-28 21:42:11.000000000 +0200 -@@ -0,0 +1,24 @@ -+/* -+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * 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. -+ * -+ */ -+ -+#ifndef WRAPPER_H -+#define WRAPPER_H -+ -+extern char *if_name; -+extern int proc_uid; -+extern int proc_gid; -+extern int hangcheck_interval; -+ -+#endif /* WRAPPER_H */ diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch deleted file mode 100644 index 52f39680..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-4.0-buildfix.patch +++ /dev/null @@ -1,32 +0,0 @@ - -Adapt for strnicmp has been replaced by strncasecmp, -and removed upstream in v4.0-rc1~98: - -From af3cd13501eb04ca61d017ff4406f1cbffafdc04 Mon Sep 17 00:00:00 2001 -From: Rasmus Villemoes -Date: Thu, 12 Feb 2015 15:02:15 -0800 -Subject: lib/string.c: remove strnicmp() - - -Signed-off-by: Thomas Backlund - ---- linux/3rdparty/ndiswrapper/ndis.c.orig -+++ linux/3rdparty/ndiswrapper/ndis.c -@@ -438,7 +438,7 @@ static int read_setting(struct nt_list * - struct wrap_device_setting *setting; - mutex_lock(&loader_mutex); - nt_list_for_each_entry(setting, setting_list, list) { -- if (strnicmp(keyname, setting->name, length) == 0) { -+ if (strncasecmp(keyname, setting->name, length) == 0) { - TRACE2("setting %s='%s'", keyname, setting->value); - mutex_unlock(&loader_mutex); - *param = ndis_encode_setting(setting, type); -@@ -502,7 +502,7 @@ wstdcall void WIN_FUNC(NdisWriteConfigur - - mutex_lock(&loader_mutex); - nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) { -- if (strnicmp(keyname, setting->name, ansi.length) == 0) { -+ if (strncasecmp(keyname, setting->name, ansi.length) == 0) { - mutex_unlock(&loader_mutex); - if (ndis_decode_setting(setting, param)) - *status = NDIS_STATUS_FAILURE; diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch deleted file mode 100644 index f7d26e62..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Kconfig.patch +++ /dev/null @@ -1,7 +0,0 @@ ---- linux-2.6.24.orig/3rdparty/ndiswrapper/Kconfig 1969-12-31 21:00:00.000000000 -0300 -+++ linux-2.6.24/3rdparty/ndiswrapper/Kconfig 2007-11-26 15:11:31.000000000 -0200 -@@ -0,0 +1,4 @@ -+ -+config NDISWRAPPER -+ tristate "NDIS driver wrapper support" -+ depends on PCI && USB && !X86_64_XEN diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch deleted file mode 100644 index b44f4b29..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-Makefile-build-fix.patch +++ /dev/null @@ -1,45 +0,0 @@ ---- linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile.orig 2013-11-28 21:42:10.000000000 +0200 -+++ linux-3.12.2-ndiswrapper-1.59/3rdparty/ndiswrapper/Makefile 2013-11-30 21:41:19.550504380 +0200 -@@ -9,24 +9,10 @@ DISTFILES = \ - winnt_types.h workqueue.c wrapmem.c wrapmem.h wrapndis.c wrapndis.h \ - wrapper.c wrapper.h - --# By default, we try to compile the modules for the currently running --# kernel. But it's the first approximation, as we will re-read the --# version from the kernel sources. --KVERS_UNAME ?= $(shell uname -r) -- - # KBUILD is the path to the Linux kernel build tree. It is usually the - # same as the kernel source tree, except when the kernel was compiled in - # a separate directory. --KBUILD ?= $(shell readlink -f /lib/modules/$(KVERS_UNAME)/build) -- --ifeq (,$(KBUILD)) --$(error Kernel build tree not found - please set KBUILD to configured kernel) --endif -- --KCONFIG := $(KBUILD)/.config --ifeq (,$(wildcard $(KCONFIG))) --$(error No .config found in $(KBUILD), please set KBUILD to configured kernel) --endif -+KBUILD ?= $(srctree) - - ifneq (,$(wildcard $(KBUILD)/include/linux/version.h)) - ifneq (,$(wildcard $(KBUILD)/include/generated/uapi/linux/version.h)) -@@ -43,16 +29,9 @@ endif - ifeq (,$(wildcard $(VERSION_H))) - VERSION_H := $(KBUILD)/include/linux/version.h - endif --ifeq (,$(wildcard $(VERSION_H))) --$(error Please run 'make modules_prepare' in $(KBUILD)) --endif - - KVERS := $(shell sed -ne 's/"//g;s/^\#define UTS_RELEASE //p' $(VERSION_H)) - --ifeq (,$(KVERS)) --$(error Cannot find UTS_RELEASE in $(VERSION_H), please report) --endif -- - INST_DIR = /lib/modules/$(KVERS)/misc - - SRC_DIR=$(shell pwd) diff --git a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch deleted file mode 100644 index 6550b9f2..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-ndiswrapper-linux-3.14-buildfix.patch +++ /dev/null @@ -1,33 +0,0 @@ - -Fix build with kernel-3.14 - -Signed-off-by: Thomas Backlund - - 3rdparty/ndiswrapper/crt.c | 2 +- - 3rdparty/ndiswrapper/ntoskernel.h | 3 +-- - 2 files changed, 2 insertions(+), 3 deletions(-) - ---- linux/3rdparty/ndiswrapper/crt.c.orig 2014-04-26 19:10:55.000000000 +0300 -+++ linux/3rdparty/ndiswrapper/crt.c 2014-04-26 19:25:27.766116082 +0300 -@@ -467,7 +467,7 @@ noregparm int WIN_FUNC(_win_memcmp,3) - noregparm void WIN_FUNC(_win_srand,1) - (UINT seed) - { -- net_srandom(seed); -+ prandom_seed((__force u32) seed); - } - - noregparm int WIN_FUNC(rand,0) ---- linux/3rdparty/ndiswrapper/ntoskernel.h.orig 2014-04-26 19:42:32.000000000 +0300 -+++ linux/3rdparty/ndiswrapper/ntoskernel.h 2014-04-26 19:59:13.662522942 +0300 -@@ -798,9 +798,8 @@ do { \ - #define nt_spin_unlock_irqrestore(lock, flags) \ - do { \ - nt_spin_unlock(lock); \ -- preempt_enable_no_resched(); \ -+ preempt_enable(); \ - local_irq_restore(flags); \ -- preempt_check_resched(); \ - } while (0) - - static inline ULONG SPAN_PAGES(void *ptr, SIZE_T length) diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch deleted file mode 100644 index e87bf7e0..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-1.3.patch +++ /dev/null @@ -1,978 +0,0 @@ - 3rdparty/rfswitch/FILES | 6 - 3rdparty/rfswitch/ISSUES | 28 +++ - 3rdparty/rfswitch/Kconfig | 22 ++ - 3rdparty/rfswitch/LICENSE | 339 +++++++++++++++++++++++++++++++++++++++++++++ - 3rdparty/rfswitch/Makefile | 119 +++++++++++++++ - 3rdparty/rfswitch/README | 44 +++++ - 3rdparty/rfswitch/av5100.c | 174 +++++++++++++++++++++++ - 3rdparty/rfswitch/pbe5.c | 205 +++++++++++++++++++++++++++ - 8 files changed, 937 insertions(+) -diff -Nurp linux-2.6.37/3rdparty/rfswitch/av5100.c linux-2.6.37.3rdparty/3rdparty/rfswitch/av5100.c ---- linux-2.6.37/3rdparty/rfswitch/av5100.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/av5100.c 2008-03-28 05:25:28.000000000 +0200 -@@ -0,0 +1,174 @@ -+/******************************************************************************* -+ -+ Copyright(c) 2003 - 2004 Intel 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., 59 -+ Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+ The full GNU General Public License is included in this distribution in the -+ file called LICENSE. -+ -+ Contact Information: -+ James P. Ketrenos -+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 -+ -+*******************************************************************************/ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+#define DRV_NAME "av5100" -+#define DRV_VERSION "1.3" -+#define DRV_DESCRIPTION "SW RF kill switch for Averatec 5100P" -+#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation" -+ -+static int radio = 1; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ -+MODULE_PARM(radio, "i"); -+ -+#else /* LINUX_VERSION_CODE < 2.6.0 */ -+ -+#include -+module_param(radio, int, 1); -+ -+#endif /* LINUX_VERSION_CODE < 2.6.0 */ -+ -+MODULE_PARM_DESC(radio, "controls state of radio (1=on, 0=off)"); -+ -+MODULE_DESCRIPTION(DRV_DESCRIPTION); -+MODULE_AUTHOR(DRV_COPYRIGHT); -+MODULE_LICENSE("GPL"); -+ -+#define AV5100_RADIO_ON (0xe0) -+#define AV5100_RADIO_OFF (0xe1) -+ -+static int av5100_radio = AV5100_RADIO_OFF; -+ -+static void av5100_set_radio(int state) -+{ -+ printk(KERN_INFO DRV_NAME ": Radio being turned %s\n", -+ (state == AV5100_RADIO_ON) ? "ON" : "OFF"); -+ outl(0x80020800, 0xcf8); -+ outb(0x6f, 0x0072); -+ outl(0x1800ffff, 0x1184); -+ outb(state, 0x00b2); -+ av5100_radio = state; -+} -+ -+ -+/* -+ * proc stuff -+ */ -+static struct proc_dir_entry *dir_base = NULL; -+ -+static int proc_set_radio(struct file *file, const char *buffer, -+ unsigned long count, void *data) -+{ -+ av5100_set_radio(buffer[0] == '0' ? AV5100_RADIO_OFF : AV5100_RADIO_ON); -+ -+ return count; -+} -+ -+static int proc_get_radio(char *page, char **start, off_t offset, -+ int count, int *eof, void *data) -+{ -+ int len = 0; -+ -+ len += snprintf(page, count, DRV_NAME ": %d\n", -+ av5100_radio == AV5100_RADIO_OFF ? 0 : 1); -+ -+ *eof = 1; -+ return len; -+} -+ -+ -+static void av5100_proc_cleanup(void) -+{ -+ if (dir_base) { -+ remove_proc_entry("radio", dir_base); -+ remove_proc_entry(DRV_NAME, &proc_root); -+ dir_base = NULL; -+ } -+} -+ -+ -+static int av5100_proc_init(void) -+{ -+ struct proc_dir_entry *ent; -+ int err = 0; -+ -+ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); -+ if (dir_base == NULL) { -+ printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" -+ DRV_NAME "\n"); -+ err = -ENOMEM; -+ goto fail; -+ } -+ -+ -+ ent = create_proc_entry("radio", S_IFREG | S_IRUGO | S_IWUSR, -+ dir_base); -+ if (ent) { -+ ent->read_proc = proc_get_radio; -+ ent->write_proc = proc_set_radio; -+ } else { -+ printk(KERN_ERR -+ "Unable to initialize /proc/" DRV_NAME "/radio\n"); -+ err = -ENOMEM; -+ goto fail; -+ } -+ -+ return 0; -+ -+ fail: -+ av5100_proc_cleanup(); -+ return err; -+} -+ -+/* -+ * module stuff -+ */ -+static int __init av5100_init(void) -+{ -+ av5100_proc_init(); -+ -+ av5100_set_radio((radio == 1) ? AV5100_RADIO_ON : AV5100_RADIO_OFF); -+ -+ return 0; -+} -+ -+static void __exit av5100_exit(void) -+{ -+ av5100_set_radio(AV5100_RADIO_OFF); -+ -+ av5100_proc_cleanup(); -+} -+ -+module_init(av5100_init); -+module_exit(av5100_exit); -+ -+/* -+ 1 2 3 4 5 6 7 -+12345678901234567890123456789012345678901234567890123456789012345678901234567890 -+*/ -diff -Nurp linux-2.6.37/3rdparty/rfswitch/FILES linux-2.6.37.3rdparty/3rdparty/rfswitch/FILES ---- linux-2.6.37/3rdparty/rfswitch/FILES 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/FILES 2004-07-09 07:44:19.000000000 +0300 -@@ -0,0 +1,6 @@ -+Makefile Used for building on 2.4, 2.6, internally and exteranlly -+ -+FILES This file -+ -+av5100.c Averatec 5100P SW Switch Module -+pbe5.c Packard Bell Easynote SW Switch Module -diff -Nurp linux-2.6.37/3rdparty/rfswitch/ISSUES linux-2.6.37.3rdparty/3rdparty/rfswitch/ISSUES ---- linux-2.6.37/3rdparty/rfswitch/ISSUES 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/ISSUES 2004-07-09 07:43:22.000000000 +0300 -@@ -0,0 +1,28 @@ -+ -+ISSUES -+------------ ----- ----- ---- --- -- - -+ -+No packets! - RF kill switch -+ -+If the module loads, but no packets are transferred you may have a SW based -+radio kill switch. All laptops have some capability to disable the radio -+via a button or switch. On some laptops that switch is physically tied to the -+IPW2100; simply toggling the switch should enable the radio. -+ -+On other laptops, the switch is a button that when pressed requires some -+software driver to send some hardware command to some other piece of hardware -+on the laptop, that then controls the radio. The driver currently has support -+for the Averatec 5100P laptop's SW switch. Support will be added for other -+laptop vendors as we become aware of them and figure out how to enable the -+radio. -+ -+To know if the radio is being disabled via the RF switch, perform the following: -+ -+% cat /proc/net/ipw2100/eth1/state -+Radio is disabled by RF switch -+ -+If it says that, then your RF switch is currently disabling the radio. The -+driver doesn't currently support switching back to the on state if you have a -+physical RF switch (the radio may turn on and packets will work, but the proc -+entry won't be updated) -+ -diff -Nurp linux-2.6.37/3rdparty/rfswitch/Kconfig linux-2.6.37.3rdparty/3rdparty/rfswitch/Kconfig ---- linux-2.6.37/3rdparty/rfswitch/Kconfig 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/Kconfig 2008-05-27 23:39:10.000000000 +0300 -@@ -0,0 +1,22 @@ -+menu "RF Switch" -+ depends on EXPERIMENTAL -+ -+config RFSWITCH -+ tristate "RF Switch" -+ depends on EXPERIMENTAL -+ -+config AVERATEC_5100P -+ tristate "averatec 5100P driver" -+ depends on RFSWITCH -+ ---help--- -+ This is an experimental driver for the wireless switch -+ of some laptops (usefull with some ipw2x00 cards) -+ -+config PACKARDBELL_E5 -+ tristate "pbe5 driver" -+ depends on RFSWITCH -+ ---help--- -+ This is an experimental driver for the wireless switch -+ of some laptops (usefull with some ipw2x00 cards) -+ -+endmenu -diff -Nurp linux-2.6.37/3rdparty/rfswitch/LICENSE linux-2.6.37.3rdparty/3rdparty/rfswitch/LICENSE ---- linux-2.6.37/3rdparty/rfswitch/LICENSE 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/LICENSE 2004-07-09 07:43:22.000000000 +0300 -@@ -0,0 +1,339 @@ -+ -+"This software program is licensed subject to the GNU General Public License -+(GPL). Version 2, June 1991, available at -+" -+ -+GNU General Public License -+ -+Version 2, June 1991 -+ -+Copyright (C) 1989, 1991 Free Software Foundation, Inc. -+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA -+ -+Everyone is permitted to copy and distribute verbatim copies of this license -+document, but changing it is not allowed. -+ -+Preamble -+ -+The licenses for most software are designed to take away your freedom to -+share and change it. By contrast, the GNU General Public License is intended -+to guarantee your freedom to share and change free software--to make sure -+the software is free for all its users. This General Public License applies -+to most of the Free Software Foundation's software and to any other program -+whose authors commit to using it. (Some other Free Software Foundation -+software is covered by the GNU Library General Public License instead.) You -+can apply it to your programs, too. -+ -+When we speak of free software, we are referring to freedom, not price. Our -+General Public Licenses are designed to make sure that you have the freedom -+to distribute copies of free software (and charge for this service if you -+wish), that you receive source code or can get it if you want it, that you -+can change the software or use pieces of it in new free programs; and that -+you know you can do these things. -+ -+To protect your rights, we need to make restrictions that forbid anyone to -+deny you these rights or to ask you to surrender the rights. These -+restrictions translate to certain responsibilities for you if you distribute -+copies of the software, or if you modify it. -+ -+For example, if you distribute copies of such a program, whether gratis or -+for a fee, you must give the recipients all the rights that you have. You -+must make sure that they, too, receive or can get the source code. And you -+must show them these terms so they know their rights. -+ -+We protect your rights with two steps: (1) copyright the software, and (2) -+offer you this license which gives you legal permission to copy, distribute -+and/or modify the software. -+ -+Also, for each author's protection and ours, we want to make certain that -+everyone understands that there is no warranty for this free software. If -+the software is modified by someone else and passed on, we want its -+recipients to know that what they have is not the original, so that any -+problems introduced by others will not reflect on the original authors' -+reputations. -+ -+Finally, any free program is threatened constantly by software patents. We -+wish to avoid the danger that redistributors of a free program will -+individually obtain patent licenses, in effect making the program -+proprietary. To prevent this, we have made it clear that any patent must be -+licensed for everyone's free use or not licensed at all. -+ -+The precise terms and conditions for copying, distribution and modification -+follow. -+ -+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -+ -+0. This License applies to any program or other work which contains a notice -+ placed by the copyright holder saying it may be distributed under the -+ terms of this General Public License. The "Program", below, refers to any -+ such program or work, and a "work based on the Program" means either the -+ Program or any derivative work under copyright law: that is to say, a -+ work containing the Program or a portion of it, either verbatim or with -+ modifications and/or translated into another language. (Hereinafter, -+ translation is included without limitation in the term "modification".) -+ Each licensee is addressed as "you". -+ -+ Activities other than copying, distribution and modification are not -+ covered by this License; they are outside its scope. The act of running -+ the Program is not restricted, and the output from the Program is covered -+ only if its contents constitute a work based on the Program (independent -+ of having been made by running the Program). Whether that is true depends -+ on what the Program does. -+ -+1. You may copy and distribute verbatim copies of the Program's source code -+ as you receive it, in any medium, provided that you conspicuously and -+ appropriately publish on each copy an appropriate copyright notice and -+ disclaimer of warranty; keep intact all the notices that refer to this -+ License and to the absence of any warranty; and give any other recipients -+ of the Program a copy of this License along with the Program. -+ -+ You may charge a fee for the physical act of transferring a copy, and you -+ may at your option offer warranty protection in exchange for a fee. -+ -+2. You may modify your copy or copies of the Program or any portion of it, -+ thus forming a work based on the Program, and copy and distribute such -+ modifications or work under the terms of Section 1 above, provided that -+ you also meet all of these conditions: -+ -+ * a) You must cause the modified files to carry prominent notices stating -+ that you changed the files and the date of any change. -+ -+ * b) You must cause any work that you distribute or publish, that in -+ whole or in part contains or is derived from the Program or any part -+ thereof, to be licensed as a whole at no charge to all third parties -+ under the terms of this License. -+ -+ * c) If the modified program normally reads commands interactively when -+ run, you must cause it, when started running for such interactive -+ use in the most ordinary way, to print or display an announcement -+ including an appropriate copyright notice and a notice that there is -+ no warranty (or else, saying that you provide a warranty) and that -+ users may redistribute the program under these conditions, and -+ telling the user how to view a copy of this License. (Exception: if -+ the Program itself is interactive but does not normally print such -+ an announcement, your work based on the Program is not required to -+ print an announcement.) -+ -+ These requirements apply to the modified work as a whole. If identifiable -+ sections of that work are not derived from the Program, and can be -+ reasonably considered independent and separate works in themselves, then -+ this License, and its terms, do not apply to those sections when you -+ distribute them as separate works. But when you distribute the same -+ sections as part of a whole which is a work based on the Program, the -+ distribution of the whole must be on the terms of this License, whose -+ permissions for other licensees extend to the entire whole, and thus to -+ each and every part regardless of who wrote it. -+ -+ Thus, it is not the intent of this section to claim rights or contest -+ your rights to work written entirely by you; rather, the intent is to -+ exercise the right to control the distribution of derivative or -+ collective works based on the Program. -+ -+ In addition, mere aggregation of another work not based on the Program -+ with the Program (or with a work based on the Program) on a volume of a -+ storage or distribution medium does not bring the other work under the -+ scope of this License. -+ -+3. You may copy and distribute the Program (or a work based on it, under -+ Section 2) in object code or executable form under the terms of Sections -+ 1 and 2 above provided that you also do one of the following: -+ -+ * a) Accompany it with the complete corresponding machine-readable source -+ code, which must be distributed under the terms of Sections 1 and 2 -+ above on a medium customarily used for software interchange; or, -+ -+ * b) Accompany it with a written offer, valid for at least three years, -+ to give any third party, for a charge no more than your cost of -+ physically performing source distribution, a complete machine- -+ readable copy of the corresponding source code, to be distributed -+ under the terms of Sections 1 and 2 above on a medium customarily -+ used for software interchange; or, -+ -+ * c) Accompany it with the information you received as to the offer to -+ distribute corresponding source code. (This alternative is allowed -+ only for noncommercial distribution and only if you received the -+ program in object code or executable form with such an offer, in -+ accord with Subsection b above.) -+ -+ The source code for a work means the preferred form of the work for -+ making modifications to it. For an executable work, complete source code -+ means all the source code for all modules it contains, plus any -+ associated interface definition files, plus the scripts used to control -+ compilation and installation of the executable. However, as a special -+ exception, the source code distributed need not include anything that is -+ normally distributed (in either source or binary form) with the major -+ components (compiler, kernel, and so on) of the operating system on which -+ the executable runs, unless that component itself accompanies the -+ executable. -+ -+ If distribution of executable or object code is made by offering access -+ to copy from a designated place, then offering equivalent access to copy -+ the source code from the same place counts as distribution of the source -+ code, even though third parties are not compelled to copy the source -+ along with the object code. -+ -+4. You may not copy, modify, sublicense, or distribute the Program except as -+ expressly provided under this License. Any attempt otherwise to copy, -+ modify, sublicense or distribute the Program is void, and will -+ automatically terminate your rights under this License. However, parties -+ who have received copies, or rights, from you under this License will not -+ have their licenses terminated so long as such parties remain in full -+ compliance. -+ -+5. You are not required to accept this License, since you have not signed -+ it. However, nothing else grants you permission to modify or distribute -+ the Program or its derivative works. These actions are prohibited by law -+ if you do not accept this License. Therefore, by modifying or -+ distributing the Program (or any work based on the Program), you -+ indicate your acceptance of this License to do so, and all its terms and -+ conditions for copying, distributing or modifying the Program or works -+ based on it. -+ -+6. Each time you redistribute the Program (or any work based on the -+ Program), the recipient automatically receives a license from the -+ original licensor to copy, distribute or modify the Program subject to -+ these terms and conditions. You may not impose any further restrictions -+ on the recipients' exercise of the rights granted herein. You are not -+ responsible for enforcing compliance by third parties to this License. -+ -+7. If, as a consequence of a court judgment or allegation of patent -+ infringement or for any other reason (not limited to patent issues), -+ conditions are imposed on you (whether by court order, agreement or -+ otherwise) that contradict the conditions of this License, they do not -+ excuse you from the conditions of this License. If you cannot distribute -+ so as to satisfy simultaneously your obligations under this License and -+ any other pertinent obligations, then as a consequence you may not -+ distribute the Program at all. For example, if a patent license would -+ not permit royalty-free redistribution of the Program by all those who -+ receive copies directly or indirectly through you, then the only way you -+ could satisfy both it and this License would be to refrain entirely from -+ distribution of the Program. -+ -+ If any portion of this section is held invalid or unenforceable under any -+ particular circumstance, the balance of the section is intended to apply -+ and the section as a whole is intended to apply in other circumstances. -+ -+ It is not the purpose of this section to induce you to infringe any -+ patents or other property right claims or to contest validity of any -+ such claims; this section has the sole purpose of protecting the -+ integrity of the free software distribution system, which is implemented -+ by public license practices. Many people have made generous contributions -+ to the wide range of software distributed through that system in -+ reliance on consistent application of that system; it is up to the -+ author/donor to decide if he or she is willing to distribute software -+ through any other system and a licensee cannot impose that choice. -+ -+ This section is intended to make thoroughly clear what is believed to be -+ a consequence of the rest of this License. -+ -+8. If the distribution and/or use of the Program is restricted in certain -+ countries either by patents or by copyrighted interfaces, the original -+ copyright holder who places the Program under this License may add an -+ explicit geographical distribution limitation excluding those countries, -+ so that distribution is permitted only in or among countries not thus -+ excluded. In such case, this License incorporates the limitation as if -+ written in the body of this License. -+ -+9. The Free Software Foundation may publish revised and/or new versions of -+ the General Public License from time to time. Such new versions will be -+ similar in spirit to the present version, but may differ in detail to -+ address new problems or concerns. -+ -+ Each version is given a distinguishing version number. If the Program -+ specifies a version number of this License which applies to it and "any -+ later version", you have the option of following the terms and -+ conditions either of that version or of any later version published by -+ the Free Software Foundation. If the Program does not specify a version -+ number of this License, you may choose any version ever published by the -+ Free Software Foundation. -+ -+10. If you wish to incorporate parts of the Program into other free programs -+ whose distribution conditions are different, write to the author to ask -+ for permission. For software which is copyrighted by the Free Software -+ Foundation, write to the Free Software Foundation; we sometimes make -+ exceptions for this. Our decision will be guided by the two goals of -+ preserving the free status of all derivatives of our free software and -+ of promoting the sharing and reuse of software generally. -+ -+ NO WARRANTY -+ -+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER -+ EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE -+ ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH -+ YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL -+ NECESSARY SERVICING, REPAIR OR CORRECTION. -+ -+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR -+ DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL -+ DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM -+ (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED -+ INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF -+ THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR -+ OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -+ -+END OF TERMS AND CONDITIONS -+ -+How to Apply These Terms to Your New Programs -+ -+If you develop a new program, and you want it to be of the greatest -+possible use to the public, the best way to achieve this is to make it free -+software which everyone can redistribute and change under these terms. -+ -+To do so, attach the following notices to the program. It is safest to -+attach them to the start of each source file to most effectively convey the -+exclusion of warranty; and each file should have at least the "copyright" -+line and a pointer to where the full notice is found. -+ -+one line to give the program's name and an idea of what it does. -+Copyright (C) yyyy name of author -+ -+This program is free software; you can redistribute it and/or modify it -+under the terms of the GNU General Public License as published by the Free -+Software Foundation; either version 2 of the License, or (at your option) -+any later version. -+ -+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., 59 -+Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+Also add information on how to contact you by electronic and paper mail. -+ -+If the program is interactive, make it output a short notice like this when -+it starts in an interactive mode: -+ -+Gnomovision version 69, Copyright (C) year name of author Gnomovision comes -+with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free -+software, and you are welcome to redistribute it under certain conditions; -+type 'show c' for details. -+ -+The hypothetical commands 'show w' and 'show c' should show the appropriate -+parts of the General Public License. Of course, the commands you use may be -+called something other than 'show w' and 'show c'; they could even be -+mouse-clicks or menu items--whatever suits your program. -+ -+You should also get your employer (if you work as a programmer) or your -+school, if any, to sign a "copyright disclaimer" for the program, if -+necessary. Here is a sample; alter the names: -+ -+Yoyodyne, Inc., hereby disclaims all copyright interest in the program -+'Gnomovision' (which makes passes at compilers) written by James Hacker. -+ -+signature of Ty Coon, 1 April 1989 -+Ty Coon, President of Vice -+ -+This General Public License does not permit incorporating your program into -+proprietary programs. If your program is a subroutine library, you may -+consider it more useful to permit linking proprietary applications with the -+library. If this is what you want to do, use the GNU Library General Public -+License instead of this License. -diff -Nurp linux-2.6.37/3rdparty/rfswitch/Makefile linux-2.6.37.3rdparty/3rdparty/rfswitch/Makefile ---- linux-2.6.37/3rdparty/rfswitch/Makefile 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/Makefile 2008-03-28 05:17:16.000000000 +0200 -@@ -0,0 +1,119 @@ -+# -+# Makefile for the SW RF Switch kernel modules -+# -+# NOTE: This make file can serve as both an external Makefile (launched -+# directly by the user), or as the sub-dir Makefile used by the kernel -+# build system. -+ -+ -+CONFIG_AVERATEC_5100P=m -+CONFIG_PACKARDBELL_E5=m -+ -+ -+ -+list-m := -+list-$(CONFIG_AVERATEC_5100P) += av5100 -+list-$(CONFIG_PACKARDBELL_E5) += pbe5 -+ -+ -+obj-$(CONFIG_AVERATEC_5100P) += av5100.o -+obj-$(CONFIG_PACKARDBELL_E5) += pbe5.o -+ -+# -+# Begin dual Makefile mode here. First we provide support for when we -+# are being invoked by the kernel build system -+# -+ifneq ($(KERNELRELEASE),) -+ -+ifneq ($(PATCHLEVEL),6) # If we are not on a 2.6, then do 2.4 specific things -+ -+O_TARGET := rfswitch.o -+ -+include $(TOPDIR)/Rules.make -+ -+endif # End if 2.4 specific settings -+ -+else -+# Here we begin the portion that is executed if the user invoked this Makefile -+# directly. -+ -+# KSRC should be set to the path to your sources -+# modules are installed into KMISC -+KVER := $(shell uname -r) -+KSRC := /lib/modules/$(KVER)/build -+KMISC := /lib/modules/$(KVER)/kernel/drivers/net/wireless/ -+ -+# KSRC_OUTPUT should be overridden if you are using a 2.6 kernel that -+# has it's output sent elsewhere via KBUILD_OUTPUT= or O= -+KSRC_OUTPUT := $(KSRC) -+ -+# If we find Rules.make, we can assume we're using the old 2.4 style building -+OLDMAKE=$(wildcard $(KSRC)/Rules.make) -+PWD=$(shell pwd) -+ -+VERFILE := $(KSRC_OUTPUT)/include/linux/version.h -+KERNELRELEASE := $(shell \ -+ if [ -r $(VERFILE) ]; then \ -+ (cat $(VERFILE); echo UTS_RELEASE) | \ -+ $(CC) -I$(KSRC_OUTPUT) $(CFLAGS) -E - | \ -+ tail -n 1 | \ -+ xargs echo; \ -+ else \ -+ uname -r; \ -+ fi) -+ -+MODPATH := $(DESTDIR)/lib/modules/$(KERNELRELEASE) -+ -+all: modules -+ -+clean: -+ rm -f *.mod.c *.mod *.o *.ko .*.cmd .*.flags Modules.symvers -+ rm -rf $(PWD)/tmp -+ -+ -+ifeq ($(OLDMAKE),) -+ -+TMP=$(PWD)/tmp -+MODVERDIR=$(TMP)/.tmp_versions -+ -+modules: -+ifdef ($(KSRC_OUTPUT)/.tmp_versions) -+ mkdir -p $(MODVERDIR) -+ -cp $(KSRC_OUTPUT)/.tmp_versions/*.mod $(MODVERDIR) -+endif -+ifeq ($(KSRC),$(KSRC_OUTPUT)) # We're not outputting elsewhere -+ifdef ($(KSRC)/.tmp_versions) -+ -cp $(KSRC)/.tmp_versions/*.mod $(MODVERDIR) -+endif -+ $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) MODVERDIR=$(MODVERDIR) modules -+else # We've got a kernel with seperate output, copy the config, and use O= -+ mkdir -p $(TMP) -+ cp $(KSRC_OUTPUT)/.config $(TMP) -+ $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) MODVERDIR=$(MODVERDIR) O=$(PWD)/tmp modules -+endif -+ -+install: modules -+ install -d $(KMISC) -+ install -m 644 -c $(addsuffix .ko,$(list-m)) $(KMISC) -+ /sbin/depmod -a -+ -+ -+else # We're on 2.4, and things are slightly different -+ -+modules: -+ $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) BUILD_DIR=$(PWD) modules -+ -+install: modules -+ install -d $(KMISC) -+ install -m 644 -c $(addsuffix .o,$(list-m)) $(KMISC) -+ /sbin/depmod -a -+ -+endif -+ -+uninstall: -+ cd $(KMISC) -+ rm -rf $(addsuffix .ko,$(list-m)) -+ cd - -+ /sbin/depmod -a -+ -+endif -diff -Nurp linux-2.6.37/3rdparty/rfswitch/pbe5.c linux-2.6.37.3rdparty/3rdparty/rfswitch/pbe5.c ---- linux-2.6.37/3rdparty/rfswitch/pbe5.c 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/pbe5.c 2008-03-28 05:25:16.000000000 +0200 -@@ -0,0 +1,205 @@ -+/******************************************************************************* -+ -+ 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., 59 -+ Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+ The full GNU General Public License is included in this distribution in the -+ file called LICENSE. -+ -+ Author: -+ Pedro Ramalhais -+ -+ Based on: -+ av5100.c from http://ipw2100.sourceforge.net/ -+ -+*******************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRV_NAME "pbe5" -+#define DRV_VERSION "1.3" -+#define DRV_DESCRIPTION "SW RF kill switch for Packard Bell EasyNote E5" -+#define DRV_AUTHOR "Pedro Ramalhais" -+#define DRV_LICENSE "GPL" -+ -+static int radio = 1; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ -+MODULE_PARM(radio, "i"); -+ -+#else /* LINUX_VERSION_CODE < 2.6.0 */ -+ -+#include -+module_param(radio, int, 1); -+ -+#endif /* LINUX_VERSION_CODE < 2.6.0 */ -+ -+MODULE_PARM_DESC(radio, "controls state of radio (1=on, 0=off)"); -+ -+MODULE_DESCRIPTION(DRV_DESCRIPTION); -+MODULE_AUTHOR(DRV_AUTHOR); -+MODULE_LICENSE(DRV_LICENSE); -+ -+/* -+ * NOTE: These values were obtained from disassembling the Icon.exe program -+ * installed in the Packard Bell EasyNote E5 laptop. The names were guessed, -+ * so don't rely on them. -+ */ -+#define PBE5_PORT_TOGGLE 0x0b3 -+#define PBE5_VALUE_TOGGLE_ON 0x01 -+#define PBE5_VALUE_TOGGLE_OFF 0x00 -+#define PBE5_PORT_APPLY 0x0b2 -+#define PBE5_VALUE_APPLY 0xef -+ -+// Some "booleans" =;-) -+#define PBE5_RADIO_OFF 0 -+#define PBE5_RADIO_ON 1 -+ -+static int pbe5_radio_status = PBE5_RADIO_ON; -+ -+unsigned char pbe5_get_radio(void) -+{ -+ unsigned char val = 0x00; -+ -+ val = inb(PBE5_PORT_TOGGLE); -+ -+ return val; -+} -+ -+static void pbe5_set_radio(int state_set) -+{ -+ pbe5_radio_status = pbe5_get_radio(); -+ -+ if (pbe5_radio_status != state_set) { -+ // Set the radio toggle register -+ outb(PBE5_VALUE_TOGGLE_ON, PBE5_PORT_TOGGLE); -+ // Commit the radio toggle register value -+ outb(PBE5_VALUE_APPLY, PBE5_PORT_APPLY); -+ // Update the radio status -+ pbe5_radio_status = pbe5_get_radio(); -+ -+ printk(KERN_INFO DRV_NAME ": Radio turned %s\n", -+ (state_set == PBE5_RADIO_ON) ? "ON" : "OFF"); -+ } else { -+ printk(KERN_INFO DRV_NAME ": Radio already %s\n", -+ (state_set == PBE5_RADIO_ON) ? "ON" : "OFF"); -+ } -+} -+ -+ -+/* -+ * proc stuff -+ */ -+static struct proc_dir_entry *dir_base = NULL; -+ -+static int proc_set_radio(struct file *file, const char *buffer, -+ unsigned long count, void *data) -+{ -+ pbe5_set_radio(buffer[0] == '0' ? PBE5_RADIO_OFF : PBE5_RADIO_ON); -+ -+ return count; -+} -+ -+static int proc_get_radio(char *page, char **start, off_t offset, -+ int count, int *eof, void *data) -+{ -+ int len = 0; -+ -+ len += snprintf(page, count, DRV_NAME ": %d\n", -+ pbe5_radio_status == PBE5_RADIO_OFF ? 0 : 1); -+ -+ *eof = 1; -+ return len; -+} -+ -+ -+static void pbe5_proc_cleanup(void) -+{ -+ if (dir_base) { -+ remove_proc_entry("radio", dir_base); -+ remove_proc_entry(DRV_NAME, &proc_root); -+ dir_base = NULL; -+ } -+} -+ -+ -+static int pbe5_proc_init(void) -+{ -+ struct proc_dir_entry *ent; -+ int err = 0; -+ -+ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); -+ if (dir_base == NULL) { -+ printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" -+ DRV_NAME "\n"); -+ err = -ENOMEM; -+ goto fail; -+ } -+ -+ -+ ent = create_proc_entry("radio", S_IFREG | S_IRUGO | S_IWUSR, -+ dir_base); -+ if (ent) { -+ ent->read_proc = proc_get_radio; -+ ent->write_proc = proc_set_radio; -+ } else { -+ printk(KERN_ERR -+ "Unable to initialize /proc/" DRV_NAME "/radio\n"); -+ err = -ENOMEM; -+ goto fail; -+ } -+ -+ return 0; -+ -+ fail: -+ pbe5_proc_cleanup(); -+ return err; -+} -+ -+/* -+ * module stuff -+ */ -+static int __init pbe5_init(void) -+{ -+ pbe5_proc_init(); -+ -+ pbe5_set_radio((radio == 1) ? PBE5_RADIO_ON : PBE5_RADIO_OFF); -+ -+ return 0; -+} -+ -+static void __exit pbe5_exit(void) -+{ -+ pbe5_set_radio(PBE5_RADIO_OFF); -+ -+ pbe5_proc_cleanup(); -+} -+ -+module_init(pbe5_init); -+module_exit(pbe5_exit); -+ -+/* -+ 1 2 3 4 5 6 7 -+12345678901234567890123456789012345678901234567890123456789012345678901234567890 -+*/ -diff -Nurp linux-2.6.37/3rdparty/rfswitch/README linux-2.6.37.3rdparty/3rdparty/rfswitch/README ---- linux-2.6.37/3rdparty/rfswitch/README 1970-01-01 02:00:00.000000000 +0200 -+++ linux-2.6.37.3rdparty/3rdparty/rfswitch/README 2004-07-09 17:26:20.000000000 +0300 -@@ -0,0 +1,44 @@ -+Radio Kill Switch -+------------ ----- ----- ---- --- -- - -+Most laptops provide the ability for the user to physically disable the radio. -+Some vendors have implemented this as a physical switch that requires no -+software to turn the radio off and on. On other laptops, however, the switch -+is controlled through a button being pressed and a software driver then making -+calls to turn the radio off and on. This is referred to as a "software based -+RF kill switch" -+ -+Currently this project provides modules for controlling the software RF kill -+switch on the Averatec 5100P and Packard Bell EasyNote E5. The code may work -+on other laptops, but these are the only models on which it has been tested. -+ -+To determine if you have a system that might be compatible with one of the -+provided SW RF Kill switch modules, you can run: -+ -+ -+ To check for the Packard Bell (to use module pbe) -- -+ -+ % dd if=/dev/mem bs=1 skip=983040 count=65535 2>/dev/null | strings | egrep "NEW-PC|Insyde Software MobilePRO BIOS" -+ -+ To check for the Averatec (to use module av5100) -- -+ -+ % dd if=/dev/mem bs=1 skip=983040 count=65535 2>/dev/null | strings | egrep "AVERATEC" -+ -+If you have one of those laptop models you can imply loading the av5100/pbe5 -+module and the radio will be toggled on and off. In addition, you can turn -+the driver on and off by writing either a 1 or 0 to /proc/av5100/radio or -+/proc/pbe5/radio. If you automatically load the av5100/pbe5 module when your -+system boots, you may wish to use the radio module parameter to control the -+state of the radio upon loading: -+ -+ modprobe av5100 radio=0 -+ modprobe pbe5 radio=0 -+ -+results in the module loading with the radio turned off. You can then turn the -+radio on by: -+ -+ echo 1 > /proc/av5100/radio -+ echo 1 > /proc/pbe5/radio -+ -+If you have a SW RF kill switch and can not use one of the above modules, -+please join us on IRC (irc.freenode.net) on channel #ipw2100 and someone may -+be able to help. diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch deleted file mode 100644 index 7bfc2c64..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-3.0-buildfix.patch +++ /dev/null @@ -1,19 +0,0 @@ - -fix build with 3.0 - -Signed-off-by: Thomas Backlund - - 3rdparty/rfswitch/Makefile | 2 -- - 1 file changed, 2 deletions(-) - ---- a/3rdparty/rfswitch/Makefile.orig 2011-07-15 02:37:27.000000000 +0300 -+++ a/3rdparty/rfswitch/Makefile 2011-07-15 13:47:10.463914526 +0300 -@@ -29,8 +29,6 @@ ifneq ($(PATCHLEVEL),6) # If we are not - - O_TARGET := rfswitch.o - --include $(TOPDIR)/Rules.make -- - endif # End if 2.4 specific settings - - else diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch b/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch deleted file mode 100644 index ab1c279c..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-rfswitch-build-fix.patch +++ /dev/null @@ -1,51 +0,0 @@ -Remove 'proc_root' usage - -Commit c74c120a21d87b0b6925ada5830d8cac21e852d9 (Linus's tree) removed -the export of 'proc_root' variable, because you can create on the proc's -root directory by passing NULL where you would pass 'proc_root'. - -Signed-off-by: Luiz Fernando N. Capitulino -Index: linux-2.6.25/3rdparty/rfswitch/av5100.c -=================================================================== ---- linux-2.6.25.orig/3rdparty/rfswitch/av5100.c -+++ linux-2.6.25/3rdparty/rfswitch/av5100.c -@@ -107,7 +107,7 @@ static void av5100_proc_cleanup(void) - { - if (dir_base) { - remove_proc_entry("radio", dir_base); -- remove_proc_entry(DRV_NAME, &proc_root); -+ remove_proc_entry(DRV_NAME, NULL); - dir_base = NULL; - } - } -@@ -118,7 +118,7 @@ static int av5100_proc_init(void) - struct proc_dir_entry *ent; - int err = 0; - -- dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); -+ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, NULL); - if (dir_base == NULL) { - printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" - DRV_NAME "\n"); -Index: linux-2.6.25/3rdparty/rfswitch/pbe5.c -=================================================================== ---- linux-2.6.25.orig/3rdparty/rfswitch/pbe5.c -+++ linux-2.6.25/3rdparty/rfswitch/pbe5.c -@@ -138,7 +138,7 @@ static void pbe5_proc_cleanup(void) - { - if (dir_base) { - remove_proc_entry("radio", dir_base); -- remove_proc_entry(DRV_NAME, &proc_root); -+ remove_proc_entry(DRV_NAME, NULL); - dir_base = NULL; - } - } -@@ -149,7 +149,7 @@ static int pbe5_proc_init(void) - struct proc_dir_entry *ent; - int err = 0; - -- dir_base = create_proc_entry(DRV_NAME, S_IFDIR, &proc_root); -+ dir_base = create_proc_entry(DRV_NAME, S_IFDIR, NULL); - if (dir_base == NULL) { - printk(KERN_ERR DRV_NAME ": Unable to initialise /proc/" - DRV_NAME "\n"); diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch b/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch deleted file mode 100644 index 61c96c46..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-buildfix.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 842e61cb85813b1ed8a1646cbe6739950c0c9822 Mon Sep 17 00:00:00 2001 -From: Larry Finger -Date: Thu, 7 May 2015 19:08:17 +0000 -Subject: [PATCH] rtl8723bs: Fix Makefile to allow warnings - -Signed-off-by: Larry Finger ---- - Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git linux.old/3rdparty/rtl8723bs/Makefile linux/3rdparty/rtl8723bs/Makefile -index a4d9f45..4da73fa 100644 ---- linux.old/3rdparty/rtl8723bs/Makefile -+++ linux/3rdparty/rtl8723bs/Makefile -@@ -3,7 +3,7 @@ EXTRA_CFLAGS += -O1 - #EXTRA_CFLAGS += -O3 - #EXTRA_CFLAGS += -Wall - #EXTRA_CFLAGS += -Wextra --EXTRA_CFLAGS += -Werror -+#EXTRA_CFLAGS += -Werror - #EXTRA_CFLAGS += -pedantic - #EXTRA_CFLAGS += -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes - diff --git a/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch b/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch deleted file mode 100644 index a620e888..00000000 --- a/kernel/kernel/files/patches/mageia.old/3rd-rtl8723bs-updates.patch +++ /dev/null @@ -1,28991 +0,0 @@ - -Updates / cleanups from: - -git://github.com/hadess/rtl8723bs.git - -Topmost commit: - -commit 8d329c00dba5d8805a738ecea2bddde8eb0e4945 -Author: Bastien Nocera -Date: Tue May 5 18:30:38 2015 +0200 - - ioctl_linux: Fix compiling with Wake-On-LAN support - - - -Signed-off-by: Thomas backlund - -diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_ap.c linux/3rdparty/rtl8723bs/core/rtw_ap.c ---- linux/3rdparty/rtl8723bs.old/core/rtw_ap.c 2015-05-10 14:36:05.663667846 +0300 -+++ linux/3rdparty/rtl8723bs/core/rtw_ap.c 2015-05-07 17:46:55.000000000 +0300 -@@ -21,9 +21,6 @@ - - #include - -- --#ifdef CONFIG_AP_MODE -- - extern unsigned char RTW_WPA_OUI[]; - extern unsigned char WMM_OUI[]; - extern unsigned char WPS_OUI[]; -@@ -32,7 +29,6 @@ extern unsigned char WFD_OUI[]; - - void init_mlme_ap_info(_adapter *padapter) - { -- struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - struct sta_priv *pstapriv = &padapter->stapriv; - struct wlan_acl_pool *pacl_list = &pstapriv->acl_list; -@@ -71,9 +67,9 @@ void free_mlme_ap_info(_adapter *padapte - - //free bc/mc sta_info - psta = rtw_get_bcmc_stainfo(padapter); -- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_lock_bh(&(pstapriv->sta_hash_lock)); - rtw_free_stainfo(padapter, psta); -- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_unlock_bh(&(pstapriv->sta_hash_lock)); - } - - static void update_BCNTIM(_adapter *padapter) -@@ -191,7 +187,7 @@ static void update_BCNTIM(_adapter *pada - { - memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen); - -- rtw_mfree(pbackup_remainder_ie, remainder_ielen); -+ kfree(pbackup_remainder_ie); - } - - offset = (uint)(dst_ie - pie); -@@ -200,122 +196,6 @@ static void update_BCNTIM(_adapter *pada - } - } - --void rtw_add_bcn_ie(_adapter *padapter, WLAN_BSSID_EX *pnetwork, u8 index, u8 *data, u8 len) --{ -- PNDIS_802_11_VARIABLE_IEs pIE; -- u8 bmatch = false; -- u8 *pie = pnetwork->IEs; -- u8 *p=NULL, *dst_ie=NULL, *premainder_ie=NULL, *pbackup_remainder_ie=NULL; -- u32 i, offset, ielen, ie_offset, remainder_ielen = 0; -- -- for (i = sizeof(NDIS_802_11_FIXED_IEs); i < pnetwork->IELength;) -- { -- pIE = (PNDIS_802_11_VARIABLE_IEs)(pnetwork->IEs + i); -- -- if (pIE->ElementID > index) -- { -- break; -- } -- else if(pIE->ElementID == index) // already exist the same IE -- { -- p = (u8 *)pIE; -- ielen = pIE->Length; -- bmatch = true; -- break; -- } -- -- p = (u8 *)pIE; -- ielen = pIE->Length; -- i += (pIE->Length + 2); -- } -- -- if (p != NULL && ielen>0) -- { -- ielen += 2; -- -- premainder_ie = p+ielen; -- -- ie_offset = (sint)(p -pie); -- -- remainder_ielen = pnetwork->IELength - ie_offset - ielen; -- -- if(bmatch) -- dst_ie = p; -- else -- dst_ie = (p+ielen); -- } -- -- if(dst_ie == NULL) -- return; -- -- if(remainder_ielen>0) -- { -- pbackup_remainder_ie = rtw_malloc(remainder_ielen); -- if(pbackup_remainder_ie && premainder_ie) -- memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen); -- } -- -- *dst_ie++=index; -- *dst_ie++=len; -- -- memcpy(dst_ie, data, len); -- dst_ie+=len; -- -- //copy remainder IE -- if(pbackup_remainder_ie) -- { -- memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen); -- -- rtw_mfree(pbackup_remainder_ie, remainder_ielen); -- } -- -- offset = (uint)(dst_ie - pie); -- pnetwork->IELength = offset + remainder_ielen; --} -- --void rtw_remove_bcn_ie(_adapter *padapter, WLAN_BSSID_EX *pnetwork, u8 index) --{ -- u8 *p, *dst_ie=NULL, *premainder_ie=NULL, *pbackup_remainder_ie=NULL; -- uint offset, ielen, ie_offset, remainder_ielen = 0; -- u8 *pie = pnetwork->IEs; -- -- p = rtw_get_ie(pie + _FIXED_IE_LENGTH_, index, &ielen, pnetwork->IELength - _FIXED_IE_LENGTH_); -- if (p != NULL && ielen>0) -- { -- ielen += 2; -- -- premainder_ie = p+ielen; -- -- ie_offset = (sint)(p -pie); -- -- remainder_ielen = pnetwork->IELength - ie_offset - ielen; -- -- dst_ie = p; -- } -- else { -- return; -- } -- -- if(remainder_ielen>0) -- { -- pbackup_remainder_ie = rtw_malloc(remainder_ielen); -- if(pbackup_remainder_ie && premainder_ie) -- memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen); -- } -- -- //copy remainder IE -- if(pbackup_remainder_ie) -- { -- memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen); -- -- rtw_mfree(pbackup_remainder_ie, remainder_ielen); -- } -- -- offset = (uint)(dst_ie - pie); -- pnetwork->IELength = offset + remainder_ielen; --} -- -- - u8 chk_sta_is_alive(struct sta_info *psta); - u8 chk_sta_is_alive(struct sta_info *psta) - { -@@ -349,19 +229,19 @@ void expire_timeout_chk(_adapter *padapt - int i; - - -- _enter_critical_bh(&pstapriv->auth_list_lock, &irqL); -+ spin_lock_bh(&pstapriv->auth_list_lock); - - phead = &pstapriv->auth_list; - plist = get_next(phead); - - //check auth_queue - #ifdef DBG_EXPIRATION_CHK -- if (rtw_end_of_queue_search(phead, plist) == false) { -+ if (phead != plist) { - DBG_871X(FUNC_NDEV_FMT" auth_list, cnt:%u\n" - , FUNC_NDEV_ARG(padapter->pnetdev), pstapriv->auth_list_cnt); - } - #endif -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - psta = LIST_CONTAINOR(plist, struct sta_info, auth_list); - -@@ -372,41 +252,41 @@ void expire_timeout_chk(_adapter *padapt - psta->expire_to--; - if (psta->expire_to == 0) - { -- rtw_list_delete(&psta->auth_list); -+ list_del_init(&psta->auth_list); - pstapriv->auth_list_cnt--; - - DBG_871X("auth expire %02X%02X%02X%02X%02X%02X\n", - psta->hwaddr[0],psta->hwaddr[1],psta->hwaddr[2],psta->hwaddr[3],psta->hwaddr[4],psta->hwaddr[5]); - -- _exit_critical_bh(&pstapriv->auth_list_lock, &irqL); -+ spin_unlock_bh(&pstapriv->auth_list_lock); - -- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_lock_bh(&(pstapriv->sta_hash_lock)); - rtw_free_stainfo(padapter, psta); -- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_unlock_bh(&(pstapriv->sta_hash_lock)); - -- _enter_critical_bh(&pstapriv->auth_list_lock, &irqL); -+ spin_lock_bh(&pstapriv->auth_list_lock); - } - } - - } - -- _exit_critical_bh(&pstapriv->auth_list_lock, &irqL); -+ spin_unlock_bh(&pstapriv->auth_list_lock); - psta = NULL; - - -- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_lock_bh(&pstapriv->asoc_list_lock); - - phead = &pstapriv->asoc_list; - plist = get_next(phead); - - //check asoc_queue - #ifdef DBG_EXPIRATION_CHK -- if (rtw_end_of_queue_search(phead, plist) == false) { -+ if (phead != plist) { - DBG_871X(FUNC_NDEV_FMT" asoc_list, cnt:%u\n" - , FUNC_NDEV_ARG(padapter->pnetdev), pstapriv->asoc_list_cnt); - } - #endif -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); - plist = get_next(plist); -@@ -417,14 +297,13 @@ void expire_timeout_chk(_adapter *padapt - if (chk_sta_is_alive(psta) || !psta->expire_to) { - psta->expire_to = pstapriv->expire_to; - psta->keep_alive_trycnt = 0; -- #ifdef CONFIG_TX_MCAST2UNI - psta->under_exist_checking = 0; -- #endif // CONFIG_TX_MCAST2UNI - } else { -- psta->expire_to--; -+ if (psta->expire_to > 0) -+ psta->expire_to--; - } - -- if (psta->expire_to <= 0) -+ if (psta->expire_to == 0) - { - struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; - -@@ -460,7 +339,7 @@ void expire_timeout_chk(_adapter *padapt - - continue; - } -- rtw_list_delete(&psta->asoc_list); -+ list_del_init(&psta->asoc_list); - pstapriv->asoc_list_cnt--; - DBG_871X("asoc expire "MAC_FMT", state=0x%x\n", MAC_ARG(psta->hwaddr), psta->state); - updated = ap_free_sta(padapter, psta, false, WLAN_REASON_DEAUTH_LEAVING); -@@ -479,7 +358,7 @@ void expire_timeout_chk(_adapter *padapt - } - } - -- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_unlock_bh(&pstapriv->asoc_list_lock); - - if (chk_alive_num) { - -@@ -521,13 +400,13 @@ if (chk_alive_num) { - - psta->keep_alive_trycnt = 0; - DBG_871X("asoc expire "MAC_FMT", state=0x%x\n", MAC_ARG(psta->hwaddr), psta->state); -- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); -- if (rtw_is_list_empty(&psta->asoc_list)==false) { -- rtw_list_delete(&psta->asoc_list); -+ spin_lock_bh(&pstapriv->asoc_list_lock); -+ if (list_empty(&psta->asoc_list)==false) { -+ list_del_init(&psta->asoc_list); - pstapriv->asoc_list_cnt--; - updated = ap_free_sta(padapter, psta, false, WLAN_REASON_DEAUTH_LEAVING); - } -- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_unlock_bh(&pstapriv->asoc_list_lock); - - } - -@@ -544,13 +423,10 @@ void add_RATid(_adapter *padapter, struc - u8 rf_type; - unsigned char sta_band = 0, shortGIrate = false; - unsigned int tx_ra_bitmap=0; -- struct ht_priv *psta_ht = NULL; - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - WLAN_BSSID_EX *pcur_network = (WLAN_BSSID_EX *)&pmlmepriv->cur_network.network; - -- if(psta) -- psta_ht = &psta->htpriv; -- else -+ if(!psta) - return; - - if(!(psta->state & _FW_LINKED)) -@@ -672,9 +548,9 @@ void update_bmc_sta(_adapter *padapter) - - rtw_sta_media_status_rpt(padapter, psta, 1); - -- _enter_critical_bh(&psta->lock, &irqL); -+ spin_lock_bh(&psta->lock); - psta->state = _FW_LINKED; -- _exit_critical_bh(&psta->lock, &irqL); -+ spin_unlock_bh(&psta->lock); - - } - else -@@ -815,9 +691,9 @@ void update_sta_info_apmode(_adapter *pa - //add_RATid(padapter, psta);//move to ap_sta_info_defer_update() - - -- _enter_critical_bh(&psta->lock, &irqL); -+ spin_lock_bh(&psta->lock); - psta->state |= _FW_LINKED; -- _exit_critical_bh(&psta->lock, &irqL); -+ spin_unlock_bh(&psta->lock); - - - } -@@ -826,7 +702,6 @@ static void update_ap_info(_adapter *pad - { - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - WLAN_BSSID_EX *pnetwork = (WLAN_BSSID_EX *)&pmlmepriv->cur_network.network; -- struct security_priv *psecuritypriv = &padapter->securitypriv; - struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); - struct ht_priv *phtpriv_ap = &pmlmepriv->htpriv; - -@@ -875,7 +750,6 @@ static void update_hw_ht_param(_adapter - { - unsigned char max_AMPDU_len; - unsigned char min_MPDU_spacing; -- struct registry_priv *pregpriv = &padapter->registrypriv; - struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; - struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); - -@@ -933,8 +807,7 @@ void start_bss_network(_adapter *padapte - WLAN_BSSID_EX *pnetwork_mlmeext = &(pmlmeinfo->network); - struct HT_info_element *pht_info=NULL; - u8 cbw40_enable=0; -- u8 change_band = false; -- -+ - //DBG_871X("%s\n", __FUNCTION__); - - bcn_interval = (u16)pnetwork->Configuration.BeaconPeriod; -@@ -1114,15 +987,12 @@ int rtw_check_beacon_data(_adapter *pada - u8 channel, network_type, supportRate[NDIS_802_11_LENGTH_RATES_EX]; - int supportRateNum = 0; - u8 OUI1[] = {0x00, 0x50, 0xf2,0x01}; -- u8 wps_oui[4]={0x0,0x50,0xf2,0x04}; - u8 WMM_PARA_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01}; - struct registry_priv *pregistrypriv = &padapter->registrypriv; - struct security_priv *psecuritypriv = &padapter->securitypriv; - struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - WLAN_BSSID_EX *pbss_network = (WLAN_BSSID_EX *)&pmlmepriv->cur_network.network; -- struct sta_priv *pstapriv = &padapter->stapriv; - u8 *ie = pbss_network->IEs; -- u8 vht_cap=false; - - /* SSID */ - /* Supported rates */ -@@ -1483,12 +1353,12 @@ int rtw_acl_add_sta(_adapter *padapter, - return (-1); - - -- _enter_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_lock_bh(&(pacl_node_q->lock)); - - phead = get_list_head(pacl_node_q); - plist = get_next(phead); - -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - paclnode = LIST_CONTAINOR(plist, struct rtw_wlan_acl_node, list); - plist = get_next(plist); -@@ -1504,14 +1374,14 @@ int rtw_acl_add_sta(_adapter *padapter, - } - } - -- _exit_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_unlock_bh(&(pacl_node_q->lock)); - - - if(added == true) - return ret; - - -- _enter_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_lock_bh(&(pacl_node_q->lock)); - - for(i=0; i< NUM_ACL; i++) - { -@@ -1519,13 +1389,13 @@ int rtw_acl_add_sta(_adapter *padapter, - - if(paclnode->valid == false) - { -- _rtw_init_listhead(&paclnode->list); -+ INIT_LIST_HEAD(&paclnode->list); - - memcpy(paclnode->addr, addr, ETH_ALEN); - - paclnode->valid = true; - -- rtw_list_insert_tail(&paclnode->list, get_list_head(pacl_node_q)); -+ list_add_tail(&paclnode->list, get_list_head(pacl_node_q)); - - pacl_list->num++; - -@@ -1535,7 +1405,7 @@ int rtw_acl_add_sta(_adapter *padapter, - - DBG_871X("%s, acl_num=%d\n", __func__, pacl_list->num); - -- _exit_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_unlock_bh(&(pacl_node_q->lock)); - - return ret; - } -@@ -1553,12 +1423,12 @@ int rtw_acl_remove_sta(_adapter *padapte - - DBG_871X("%s(acl_num=%d)=" MAC_FMT "\n", __func__, pacl_list->num, MAC_ARG(addr)); - -- _enter_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_lock_bh(&(pacl_node_q->lock)); - - phead = get_list_head(pacl_node_q); - plist = get_next(phead); - -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - paclnode = LIST_CONTAINOR(plist, struct rtw_wlan_acl_node, list); - plist = get_next(plist); -@@ -1569,14 +1439,14 @@ int rtw_acl_remove_sta(_adapter *padapte - { - paclnode->valid = false; - -- rtw_list_delete(&paclnode->list); -+ list_del_init(&paclnode->list); - - pacl_list->num--; - } - } - } - -- _exit_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_unlock_bh(&(pacl_node_q->lock)); - - DBG_871X("%s, acl_num=%d\n", __func__, pacl_list->num); - -@@ -1599,7 +1469,7 @@ u8 rtw_ap_set_pairwise_key(_adapter *pad - - psetstakey_para = (struct set_stakey_parm*)rtw_zmalloc(sizeof(struct set_stakey_parm)); - if(psetstakey_para==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -+ kfree((u8 *) ph2c); - res=_FAIL; - goto exit; - } -@@ -1639,7 +1509,7 @@ static int rtw_ap_set_key(_adapter *pada - } - psetkeyparm=(struct setkey_parm*)rtw_zmalloc(sizeof(struct setkey_parm)); - if(psetkeyparm==NULL){ -- rtw_mfree((unsigned char *)pcmd, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)pcmd); - res= _FAIL; - goto exit; - } -@@ -1678,7 +1548,7 @@ static int rtw_ap_set_key(_adapter *pada - pcmd->rspsz = 0; - - -- _rtw_init_listhead(&pcmd->list); -+ INIT_LIST_HEAD(&pcmd->list); - - res = rtw_enqueue_cmd(pcmdpriv, pcmd); - -@@ -1715,8 +1585,6 @@ int rtw_ap_set_wep_key(_adapter *padapte - return rtw_ap_set_key(padapter, key, alg, keyid, set_tx); - } - --#ifdef CONFIG_NATIVEAP_MLME -- - static void update_bcn_fixed_ie(_adapter *padapter) - { - DBG_871X("%s\n", __FUNCTION__); -@@ -1838,7 +1706,7 @@ static void update_bcn_wps_ie(_adapter * - } - - if(pbackup_remainder_ie) -- rtw_mfree(pbackup_remainder_ie, remainder_ielen); -+ kfree(pbackup_remainder_ie); - - // deal with the case without set_tx_beacon_cmd() in update_beacon() - #if defined( CONFIG_INTERRUPT_BASED_TXBCN ) -@@ -1907,7 +1775,7 @@ void update_beacon(_adapter *padapter, u - if(false == pmlmeext->bstart_bss) - return; - -- _enter_critical_bh(&pmlmepriv->bcn_update_lock, &irqL); -+ spin_lock_bh(&pmlmepriv->bcn_update_lock); - - switch(ie_id) - { -@@ -1959,7 +1827,7 @@ void update_beacon(_adapter *padapter, u - - pmlmepriv->update_bcn = true; - -- _exit_critical_bh(&pmlmepriv->bcn_update_lock, &irqL); -+ spin_unlock_bh(&pmlmepriv->bcn_update_lock); - - #ifndef CONFIG_INTERRUPT_BASED_TXBCN - if(tx) -@@ -2064,13 +1932,13 @@ void associated_clients_update(_adapter - struct sta_info *psta=NULL; - struct sta_priv *pstapriv = &padapter->stapriv; - -- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_lock_bh(&pstapriv->asoc_list_lock); - - phead = &pstapriv->asoc_list; - plist = get_next(phead); - - //check asoc_queue -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); - -@@ -2079,7 +1947,7 @@ void associated_clients_update(_adapter - VCS_update(padapter, psta); - } - -- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_unlock_bh(&pstapriv->asoc_list_lock); - - } - -@@ -2332,8 +2200,6 @@ u8 ap_free_sta(_adapter *padapter, struc - { - _irqL irqL; - u8 beacon_updated = false; -- struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); -- struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); - struct sta_priv *pstapriv = &padapter->stapriv; - - if(!psta) -@@ -2360,9 +2226,9 @@ u8 ap_free_sta(_adapter *padapter, struc - rtw_clearstakey_cmd(padapter, psta, true); - - -- _enter_critical_bh(&psta->lock, &irqL); -+ spin_lock_bh(&psta->lock); - psta->state &= ~_FW_LINKED; -- _exit_critical_bh(&psta->lock, &irqL); -+ spin_unlock_bh(&psta->lock); - - rtw_cfg80211_indicate_sta_disassoc(padapter, psta->hwaddr, reason); - -@@ -2370,52 +2236,15 @@ u8 ap_free_sta(_adapter *padapter, struc - - beacon_updated = bss_cap_update_on_sta_leave(padapter, psta); - -- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_lock_bh(&(pstapriv->sta_hash_lock)); - rtw_free_stainfo(padapter, psta); -- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_unlock_bh(&(pstapriv->sta_hash_lock)); - - - return beacon_updated; - - } - --int rtw_ap_inform_ch_switch(_adapter *padapter, u8 new_ch, u8 ch_offset) --{ -- _irqL irqL; -- _list *phead, *plist; -- int ret=0; -- struct sta_info *psta = NULL; -- struct sta_priv *pstapriv = &padapter->stapriv; -- struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; -- struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); -- u8 bc_addr[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff}; -- -- if((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE) -- return ret; -- -- DBG_871X(FUNC_NDEV_FMT" with ch:%u, offset:%u\n", -- FUNC_NDEV_ARG(padapter->pnetdev), new_ch, ch_offset); -- -- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); -- phead = &pstapriv->asoc_list; -- plist = get_next(phead); -- -- /* for each sta in asoc_queue */ -- while ((rtw_end_of_queue_search(phead, plist)) == false) -- { -- psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); -- plist = get_next(plist); -- -- issue_action_spct_ch_switch(padapter, psta->hwaddr, new_ch, ch_offset); -- psta->expire_to = ((pstapriv->expire_to * 2) > 5) ? 5 : (pstapriv->expire_to * 2); -- } -- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); -- -- issue_action_spct_ch_switch(padapter, bc_addr, new_ch, ch_offset); -- -- return ret; --} -- - int rtw_sta_flush(_adapter *padapter) - { - _irqL irqL; -@@ -2433,25 +2262,25 @@ int rtw_sta_flush(_adapter *padapter) - return ret; - - -- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_lock_bh(&pstapriv->asoc_list_lock); - phead = &pstapriv->asoc_list; - plist = get_next(phead); - - //free sta asoc_queue -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); - - plist = get_next(plist); - -- rtw_list_delete(&psta->asoc_list); -+ list_del_init(&psta->asoc_list); - pstapriv->asoc_list_cnt--; - -- //_exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ //spin_unlock_bh(&pstapriv->asoc_list_lock); - ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING); -- //_enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ //spin_lock_bh(&pstapriv->asoc_list_lock); - } -- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_unlock_bh(&pstapriv->asoc_list_lock); - - - issue_deauth(padapter, bc_addr, WLAN_REASON_DEAUTH_LEAVING); -@@ -2516,7 +2345,6 @@ void rtw_ap_restore_network(_adapter *pa - { - struct mlme_priv *mlmepriv = &padapter->mlmepriv; - struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; -- struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); - struct sta_priv * pstapriv = &padapter->stapriv; - struct sta_info *psta; - struct security_priv* psecuritypriv=&(padapter->securitypriv); -@@ -2539,12 +2367,12 @@ void rtw_ap_restore_network(_adapter *pa - rtw_set_key(padapter, psecuritypriv, psecuritypriv->dot118021XGrpKeyid, 0,false); - } - -- _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_lock_bh(&pstapriv->asoc_list_lock); - - phead = &pstapriv->asoc_list; - plist = get_next(phead); - -- while ((rtw_end_of_queue_search(phead, plist)) == false) { -+ while (phead != plist) { - int stainfo_offset; - - psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list); -@@ -2556,7 +2384,7 @@ void rtw_ap_restore_network(_adapter *pa - } - } - -- _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); -+ spin_unlock_bh(&pstapriv->asoc_list_lock); - - for (i = 0; i < chk_alive_num; i++) { - psta = rtw_get_stainfo_by_offset(pstapriv, chk_alive_list[i]); -@@ -2619,12 +2447,12 @@ void start_ap_mode(_adapter *padapter) - - - //for ACL -- _rtw_init_listhead(&(pacl_list->acl_node_q.queue)); -+ INIT_LIST_HEAD(&(pacl_list->acl_node_q.queue)); - pacl_list->num = 0; - pacl_list->mode = 0; - for(i = 0; i < NUM_ACL; i++) - { -- _rtw_init_listhead(&pacl_list->aclnode[i].list); -+ INIT_LIST_HEAD(&pacl_list->aclnode[i].list); - pacl_list->aclnode[i].valid = false; - } - -@@ -2651,10 +2479,10 @@ void stop_ap_mode(_adapter *padapter) - padapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled; - - //for ACL -- _enter_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_lock_bh(&(pacl_node_q->lock)); - phead = get_list_head(pacl_node_q); - plist = get_next(phead); -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - paclnode = LIST_CONTAINOR(plist, struct rtw_wlan_acl_node, list); - plist = get_next(plist); -@@ -2663,12 +2491,12 @@ void stop_ap_mode(_adapter *padapter) - { - paclnode->valid = false; - -- rtw_list_delete(&paclnode->list); -+ list_del_init(&paclnode->list); - - pacl_list->num--; - } - } -- _exit_critical_bh(&(pacl_node_q->lock), &irqL); -+ spin_unlock_bh(&(pacl_node_q->lock)); - - DBG_871X("%s, free acl_node_queue, num=%d\n", __func__, pacl_list->num); - -@@ -2678,9 +2506,9 @@ void stop_ap_mode(_adapter *padapter) - rtw_free_all_stainfo(padapter); - - psta = rtw_get_bcmc_stainfo(padapter); -- _enter_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_lock_bh(&(pstapriv->sta_hash_lock)); - rtw_free_stainfo(padapter, psta); -- _exit_critical_bh(&(pstapriv->sta_hash_lock), &irqL); -+ spin_unlock_bh(&(pstapriv->sta_hash_lock)); - - rtw_init_bcmc_stainfo(padapter); - -@@ -2688,8 +2516,3 @@ void stop_ap_mode(_adapter *padapter) - - rtw_btcoex_MediaStatusNotify(padapter, 0); //disconnect - } -- --#endif //CONFIG_NATIVEAP_MLME -- --#endif //CONFIG_AP_MODE -- -diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_btcoex.c linux/3rdparty/rtl8723bs/core/rtw_btcoex.c ---- linux/3rdparty/rtl8723bs.old/core/rtw_btcoex.c 2015-05-10 14:36:05.663667846 +0300 -+++ linux/3rdparty/rtl8723bs/core/rtw_btcoex.c 2015-05-07 17:46:55.000000000 +0300 -@@ -108,16 +108,6 @@ void rtw_btcoex_HaltNotify(PADAPTER pada - hal_btcoex_HaltNotify(padapter); - } - --void rtw_btcoex_SwitchBtTRxMask(PADAPTER padapter) --{ -- hal_btcoex_SwitchBtTRxMask(padapter); --} -- --void rtw_btcoex_Switch(PADAPTER padapter, u8 enable) --{ -- hal_btcoex_SetBTCoexist(padapter, enable); --} -- - u8 rtw_btcoex_IsBtDisabled(PADAPTER padapter) - { - return hal_btcoex_IsBtDisabled(padapter); -@@ -137,15 +127,6 @@ s32 rtw_btcoex_IsBTCoexCtrlAMPDUSize(PAD - return coexctrl; - } - --u32 rtw_btcoex_GetAMPDUSize(PADAPTER padapter) --{ -- u32 size; -- -- size = hal_btcoex_GetAMPDUSize(padapter); -- -- return size; --} -- - void rtw_btcoex_SetManualControl(PADAPTER padapter, u8 manual) - { - if (true == manual) -@@ -158,11 +139,6 @@ void rtw_btcoex_SetManualControl(PADAPTE - } - } - --u8 rtw_btcoex_1Ant(PADAPTER padapter) --{ -- return hal_btcoex_1Ant(padapter); --} -- - u8 rtw_btcoex_IsBtControlLps(PADAPTER padapter) - { - return hal_btcoex_IsBtControlLps(padapter); -@@ -198,11 +174,6 @@ void rtw_btcoex_SetPGAntNum(PADAPTER pad - hal_btcoex_SetPgAntNum(padapter, antNum); - } - --u8 rtw_btcoex_GetPGAntNum(PADAPTER padapter) --{ -- return hal_btcoex_GetPgAntNum(padapter); --} -- - void rtw_btcoex_SetSingleAntPath(PADAPTER padapter, u8 singleAntPath) - { - hal_btcoex_SetSingleAntPath(padapter, singleAntPath); -@@ -233,16 +204,6 @@ u32 rtw_btcoex_GetDBG(PADAPTER padapter, - return hal_btcoex_GetDBG(padapter, pStrBuf, bufSize); - } - --u8 rtw_btcoex_IncreaseScanDeviceNum(PADAPTER padapter) --{ -- return hal_btcoex_IncreaseScanDeviceNum(padapter); --} -- --u8 rtw_btcoex_IsBtLinkExist(PADAPTER padapter) --{ -- return hal_btcoex_IsBtLinkExist(padapter); --} -- - // ================================================== - // Below Functions are called by BT-Coex - // ================================================== -diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_cmd.c linux/3rdparty/rtl8723bs/core/rtw_cmd.c ---- linux/3rdparty/rtl8723bs.old/core/rtw_cmd.c 2015-05-10 14:36:05.665667868 +0300 -+++ linux/3rdparty/rtl8723bs/core/rtw_cmd.c 2015-05-07 17:46:55.000000000 +0300 -@@ -255,10 +255,10 @@ _func_enter_; - void *c2h; - if ((c2h = rtw_cbuf_pop(pevtpriv->c2h_queue)) != NULL - && c2h != (void *)pevtpriv) { -- rtw_mfree(c2h, 16); -+ kfree(c2h); - } - } -- rtw_cbuf_free(pevtpriv->c2h_queue); -+ kfree(pevtpriv->c2h_queue); - - RT_TRACE(_module_rtl871x_cmd_c_,_drv_info_,("-_rtw_free_evt_priv \n")); - -@@ -272,10 +272,10 @@ _func_enter_; - - if(pcmdpriv){ - if (pcmdpriv->cmd_allocated_buf) -- rtw_mfree(pcmdpriv->cmd_allocated_buf, MAX_CMDSZ + CMDBUFF_ALIGN_SZ); -+ kfree(pcmdpriv->cmd_allocated_buf); - - if (pcmdpriv->rsp_allocated_buf) -- rtw_mfree(pcmdpriv->rsp_allocated_buf, MAX_RSPSZ + 4); -+ kfree(pcmdpriv->rsp_allocated_buf); - - mutex_destroy(&pcmdpriv->sctx_mutex); - } -@@ -301,13 +301,13 @@ _func_enter_; - if (obj == NULL) - goto exit; - -- //_enter_critical_bh(&queue->lock, &irqL); -- _enter_critical(&queue->lock, &irqL); -+ //spin_lock_bh(&queue->lock); -+ spin_lock_irqsave(&queue->lock, irqL); - -- rtw_list_insert_tail(&obj->list, &queue->queue); -+ list_add_tail(&obj->list, &queue->queue); - -- //_exit_critical_bh(&queue->lock, &irqL); -- _exit_critical(&queue->lock, &irqL); -+ //spin_unlock_bh(&queue->lock); -+ spin_unlock_irqrestore(&queue->lock, irqL); - - exit: - -@@ -323,18 +323,18 @@ struct cmd_obj *_rtw_dequeue_cmd(_queue - - _func_enter_; - -- //_enter_critical_bh(&(queue->lock), &irqL); -- _enter_critical(&queue->lock, &irqL); -- if (rtw_is_list_empty(&(queue->queue))) -+ //spin_lock_bh(&(queue->lock)); -+ spin_lock_irqsave(&queue->lock, irqL); -+ if (list_empty(&(queue->queue))) - obj = NULL; - else - { - obj = LIST_CONTAINOR(get_next(&(queue->queue)), struct cmd_obj, list); -- rtw_list_delete(&obj->list); -+ list_del_init(&obj->list); - } - -- //_exit_critical_bh(&(queue->lock), &irqL); -- _exit_critical(&queue->lock, &irqL); -+ //spin_unlock_bh(&(queue->lock)); -+ spin_unlock_irqrestore(&queue->lock, irqL); - - _func_exit_; - -@@ -442,14 +442,6 @@ _func_exit_; - return cmd_obj; - } - --void rtw_cmd_clr_isr(struct cmd_priv *pcmdpriv) --{ --_func_enter_; -- pcmdpriv->cmd_done_cnt++; -- //up(&(pcmdpriv->cmd_done_sema)); --_func_exit_; --} -- - void rtw_free_cmd_obj(struct cmd_obj *pcmd) - { - _func_enter_; -@@ -457,7 +449,7 @@ _func_enter_; - if((pcmd->cmdcode!=_JoinBss_CMD_) &&(pcmd->cmdcode!= _CreateBss_CMD_)) - { - //free parmbuf in cmd_obj -- rtw_mfree((unsigned char*)pcmd->parmbuf, pcmd->cmdsz); -+ kfree((unsigned char*)pcmd->parmbuf); - } - - if(pcmd->rsp!=NULL) -@@ -465,12 +457,12 @@ _func_enter_; - if(pcmd->rspsz!= 0) - { - //free rsp in cmd_obj -- rtw_mfree((unsigned char*)pcmd->rsp, pcmd->rspsz); -+ kfree((unsigned char*)pcmd->rsp); - } - } - - //free cmd_obj -- rtw_mfree((unsigned char*)pcmd, sizeof(struct cmd_obj)); -+ kfree((unsigned char*)pcmd); - - _func_exit_; - } -@@ -533,7 +525,7 @@ _func_enter_; - break; - } - -- if(rtw_is_list_empty(&(pcmdpriv->cmd_queue.queue))) -+ if(list_empty(&(pcmdpriv->cmd_queue.queue))) - { - //DBG_871X("%s: cmd queue is empty!\n", __func__); - continue; -@@ -573,7 +565,7 @@ _next: - - memcpy(pcmdbuf, pcmd->parmbuf, pcmd->cmdsz); - -- if(pcmd->cmdcode < (sizeof(wlancmds) /sizeof(struct cmd_hdl))) -+ if(pcmd->cmdcode < ARRAY_SIZE(wlancmds)) - { - cmd_hdl = wlancmds[pcmd->cmdcode].h2cfuns; - -@@ -594,18 +586,18 @@ _next: - - post_process: - -- _enter_critical_mutex(&(pcmd->padapter->cmdpriv.sctx_mutex), NULL); -- if (pcmd->sctx) { -- DBG_871X_LEVEL(_drv_always_, FUNC_ADPT_FMT" pcmd->sctx\n", -- FUNC_ADPT_ARG(pcmd->padapter)); -- -- if (pcmd->res == H2C_SUCCESS) -- rtw_sctx_done(&pcmd->sctx); -- else -- rtw_sctx_done_err(&pcmd->sctx, RTW_SCTX_DONE_CMD_ERROR); -+ if (mutex_lock_interruptible(&(pcmd->padapter->cmdpriv.sctx_mutex)) == 0) { -+ if (pcmd->sctx) { -+ DBG_871X_LEVEL(_drv_always_, FUNC_ADPT_FMT" pcmd->sctx\n", -+ FUNC_ADPT_ARG(pcmd->padapter)); -+ -+ if (pcmd->res == H2C_SUCCESS) -+ rtw_sctx_done(&pcmd->sctx); -+ else -+ rtw_sctx_done_err(&pcmd->sctx, RTW_SCTX_DONE_CMD_ERROR); -+ } -+ mutex_unlock(&(pcmd->padapter->cmdpriv.sctx_mutex)); - } -- _exit_critical_mutex(&(pcmd->padapter->cmdpriv.sctx_mutex), NULL); -- - - if((cmd_process_time = jiffies_to_msecs(jiffies - cmd_start_time)) > 1000) - { -@@ -627,7 +619,7 @@ post_process: - } - - //call callback function for post-processed -- if(pcmd->cmdcode < (sizeof(rtw_cmd_callback) /sizeof(struct _cmd_callback))) -+ if(pcmd->cmdcode < ARRAY_SIZE(rtw_cmd_callback)) - { - pcmd_callback = rtw_cmd_callback[pcmd->cmdcode].callback; - if(pcmd_callback == NULL) -@@ -666,7 +658,7 @@ post_process: - if (pcmd->cmdcode == GEN_CMD_CODE(_Set_Drv_Extra)) { - extra_parm = (struct drvextra_cmd_parm *)pcmd->parmbuf; - if(extra_parm->pbuf && extra_parm->size > 0) { -- rtw_mfree(extra_parm->pbuf, extra_parm->size); -+ kfree(extra_parm->pbuf); - } - } - -@@ -682,42 +674,6 @@ _func_exit_; - - } - --u8 rtw_setstandby_cmd(_adapter *padapter, uint action) --{ -- struct cmd_obj* ph2c; -- struct usb_suspend_parm* psetusbsuspend; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- -- u8 ret = _SUCCESS; -- --_func_enter_; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if (ph2c == NULL) { -- ret = _FAIL; -- goto exit; -- } -- -- psetusbsuspend = (struct usb_suspend_parm*)rtw_zmalloc(sizeof(struct usb_suspend_parm)); -- if (psetusbsuspend == NULL) { -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- ret = _FAIL; -- goto exit; -- } -- -- psetusbsuspend->action = action; -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, psetusbsuspend, GEN_CMD_CODE(_SetUsbSuspend)); -- -- ret = rtw_enqueue_cmd(pcmdpriv, ph2c); -- --exit: -- --_func_exit_; -- -- return ret; --} -- - /* - rtw_sitesurvey_cmd(~) - ### NOTE:#### (!!!!) -@@ -744,7 +700,7 @@ _func_enter_; - - psurveyPara = (struct sitesurvey_parm*)rtw_zmalloc(sizeof(struct sitesurvey_parm)); - if (psurveyPara == NULL) { -- rtw_mfree((unsigned char*) ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char*) ph2c); - return _FAIL; - } - -@@ -793,8 +749,6 @@ _func_enter_; - - pmlmepriv->scan_start_time = jiffies; - _set_timer(&pmlmepriv->scan_to_timer, SCANNING_TIMEOUT); -- -- rtw_led_control(padapter, LED_CTL_SITE_SURVEY); - } else { - _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY); - } -@@ -821,7 +775,7 @@ _func_enter_; - - pbsetdataratepara = (struct setdatarate_parm*)rtw_zmalloc(sizeof(struct setdatarate_parm)); - if (pbsetdataratepara == NULL) { -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -+ kfree((u8 *) ph2c); - res = _FAIL; - goto exit; - } -@@ -838,243 +792,17 @@ _func_exit_; - return res; - } - --u8 rtw_setbasicrate_cmd(_adapter *padapter, u8 *rateset) --{ -- struct cmd_obj* ph2c; -- struct setbasicrate_parm* pssetbasicratepara; -- struct cmd_priv* pcmdpriv=&padapter->cmdpriv; -- u8 res = _SUCCESS; -- --_func_enter_; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if (ph2c == NULL) { -- res= _FAIL; -- goto exit; -- } -- pssetbasicratepara = (struct setbasicrate_parm*)rtw_zmalloc(sizeof(struct setbasicrate_parm)); -- -- if (pssetbasicratepara == NULL) { -- rtw_mfree((u8*) ph2c, sizeof(struct cmd_obj)); -- res = _FAIL; -- goto exit; -- } -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, pssetbasicratepara, _SetBasicRate_CMD_); -- -- memcpy(pssetbasicratepara->basicrates, rateset, NumRates); -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: -- --_func_exit_; -- -- return res; --} -- -- --/* --unsigned char rtw_setphy_cmd(unsigned char *adapter) -- --1. be called only after rtw_update_registrypriv_dev_network( ~) or mp testing program --2. for AdHoc/Ap mode or mp mode? -- --*/ --u8 rtw_setphy_cmd(_adapter *padapter, u8 modem, u8 ch) --{ -- struct cmd_obj* ph2c; -- struct setphy_parm* psetphypara; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; --// struct mlme_priv *pmlmepriv = &padapter->mlmepriv; --// struct registry_priv* pregistry_priv = &padapter->registrypriv; -- u8 res=_SUCCESS; -- --_func_enter_; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res= _FAIL; -- goto exit; -- } -- psetphypara = (struct setphy_parm*)rtw_zmalloc(sizeof(struct setphy_parm)); -- -- if(psetphypara==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, psetphypara, _SetPhy_CMD_); -- -- RT_TRACE(_module_rtl871x_cmd_c_,_drv_info_,("CH=%d, modem=%d", ch, modem)); -- -- psetphypara->modem = modem; -- psetphypara->rfchannel = ch; -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: --_func_exit_; -- return res; --} -- --u8 rtw_setbbreg_cmd(_adapter*padapter, u8 offset, u8 val) --{ -- struct cmd_obj* ph2c; -- struct writeBB_parm* pwritebbparm; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- u8 res=_SUCCESS; --_func_enter_; -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res= _FAIL; -- goto exit; -- } -- pwritebbparm = (struct writeBB_parm*)rtw_zmalloc(sizeof(struct writeBB_parm)); -- -- if(pwritebbparm==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, pwritebbparm, GEN_CMD_CODE(_SetBBReg)); -- -- pwritebbparm->offset = offset; -- pwritebbparm->value = val; -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: --_func_exit_; -- return res; --} -- --u8 rtw_getbbreg_cmd(_adapter *padapter, u8 offset, u8 *pval) --{ -- struct cmd_obj* ph2c; -- struct readBB_parm* prdbbparm; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- u8 res=_SUCCESS; -- --_func_enter_; -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res=_FAIL; -- goto exit; -- } -- prdbbparm = (struct readBB_parm*)rtw_zmalloc(sizeof(struct readBB_parm)); -- -- if(prdbbparm ==NULL){ -- rtw_mfree((unsigned char *) ph2c, sizeof(struct cmd_obj)); -- return _FAIL; -- } -- -- _rtw_init_listhead(&ph2c->list); -- ph2c->cmdcode =GEN_CMD_CODE(_GetBBReg); -- ph2c->parmbuf = (unsigned char *)prdbbparm; -- ph2c->cmdsz = sizeof(struct readBB_parm); -- ph2c->rsp = pval; -- ph2c->rspsz = sizeof(struct readBB_rsp); -- -- prdbbparm ->offset = offset; -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: --_func_exit_; -- return res; --} -- --u8 rtw_setrfreg_cmd(_adapter *padapter, u8 offset, u32 val) --{ -- struct cmd_obj* ph2c; -- struct writeRF_parm* pwriterfparm; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- u8 res=_SUCCESS; --_func_enter_; -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res= _FAIL; -- goto exit; -- } -- pwriterfparm = (struct writeRF_parm*)rtw_zmalloc(sizeof(struct writeRF_parm)); -- -- if(pwriterfparm==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, pwriterfparm, GEN_CMD_CODE(_SetRFReg)); -- -- pwriterfparm->offset = offset; -- pwriterfparm->value = val; -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: --_func_exit_; -- return res; --} -- --u8 rtw_getrfreg_cmd(_adapter *padapter, u8 offset, u8 *pval) --{ -- struct cmd_obj* ph2c; -- struct readRF_parm* prdrfparm; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- u8 res=_SUCCESS; -- --_func_enter_; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res= _FAIL; -- goto exit; -- } -- -- prdrfparm = (struct readRF_parm*)rtw_zmalloc(sizeof(struct readRF_parm)); -- if(prdrfparm ==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- _rtw_init_listhead(&ph2c->list); -- ph2c->cmdcode =GEN_CMD_CODE(_GetRFReg); -- ph2c->parmbuf = (unsigned char *)prdrfparm; -- ph2c->cmdsz = sizeof(struct readRF_parm); -- ph2c->rsp = pval; -- ph2c->rspsz = sizeof(struct readRF_rsp); -- -- prdrfparm ->offset = offset; -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); -- --exit: -- --_func_exit_; -- -- return res; --} -- - void rtw_getbbrfreg_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd) - { - _func_enter_; - - //rtw_free_cmd_obj(pcmd); -- rtw_mfree((unsigned char*) pcmd->parmbuf, pcmd->cmdsz); -- rtw_mfree((unsigned char*) pcmd, sizeof(struct cmd_obj)); -+ kfree((unsigned char*) pcmd->parmbuf); -+ kfree((unsigned char*) pcmd); - - _func_exit_; - } - --void rtw_readtssi_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd) --{ -- _func_enter_; -- -- rtw_mfree((unsigned char*) pcmd->parmbuf, pcmd->cmdsz); -- rtw_mfree((unsigned char*) pcmd, sizeof(struct cmd_obj)); -- --_func_exit_; --} -- - u8 rtw_createbss_cmd(_adapter *padapter) - { - struct cmd_obj* pcmd; -@@ -1085,8 +813,6 @@ u8 rtw_createbss_cmd(_adapter *padapter - - _func_enter_; - -- rtw_led_control(padapter, LED_CTL_START_TO_LINK); -- - if (pmlmepriv->assoc_ssid.SsidLength == 0){ - RT_TRACE(_module_rtl871x_cmd_c_,_drv_info_,(" createbss for Any SSid:%s\n",pmlmepriv->assoc_ssid.Ssid)); - } else { -@@ -1099,7 +825,7 @@ _func_enter_; - goto exit; - } - -- _rtw_init_listhead(&pcmd->list); -+ INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = _CreateBss_CMD_; - pcmd->parmbuf = (unsigned char *)pdev_network; - pcmd->cmdsz = get_WLAN_BSSID_EX_sz((WLAN_BSSID_EX*)pdev_network); -@@ -1117,36 +843,6 @@ _func_exit_; - return res; - } - --u8 rtw_createbss_cmd_ex(_adapter *padapter, unsigned char *pbss, unsigned int sz) --{ -- struct cmd_obj* pcmd; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- u8 res=_SUCCESS; -- --_func_enter_; -- -- pcmd = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(pcmd==NULL){ -- res= _FAIL; -- goto exit; -- } -- -- _rtw_init_listhead(&pcmd->list); -- pcmd->cmdcode = GEN_CMD_CODE(_CreateBss); -- pcmd->parmbuf = pbss; -- pcmd->cmdsz = sz; -- pcmd->rsp = NULL; -- pcmd->rspsz = 0; -- -- res = rtw_enqueue_cmd(pcmdpriv, pcmd); -- --exit: -- --_func_exit_; -- -- return res; --} -- - u8 rtw_startbss_cmd(_adapter *padapter, int flags) - { - struct cmd_obj* pcmd; -@@ -1167,7 +863,7 @@ _func_enter_; - goto exit; - } - -- _rtw_init_listhead(&pcmd->list); -+ INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = GEN_CMD_CODE(_CreateBss); - pcmd->parmbuf = NULL; - pcmd->cmdsz = 0; -@@ -1183,10 +879,11 @@ _func_enter_; - - if (res == _SUCCESS && (flags & RTW_CMDF_WAIT_ACK)) { - rtw_sctx_wait(&sctx, __func__); -- _enter_critical_mutex(&pcmdpriv->sctx_mutex, NULL); -- if (sctx.status == RTW_SCTX_SUBMITTED) -- pcmd->sctx = NULL; -- _exit_critical_mutex(&pcmdpriv->sctx_mutex, NULL); -+ if (mutex_lock_interruptible(&pcmdpriv->sctx_mutex) == 0) { -+ if (sctx.status == RTW_SCTX_SUBMITTED) -+ pcmd->sctx = NULL; -+ mutex_unlock(&pcmdpriv->sctx_mutex); -+ } - } - } - -@@ -1216,8 +913,6 @@ u8 rtw_joinbss_cmd(_adapter *padapter, - u8 *ptmp=NULL; - _func_enter_; - -- rtw_led_control(padapter, LED_CTL_START_TO_LINK); -- - if (pmlmepriv->assoc_ssid.SsidLength == 0){ - RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, ("+Join cmd: Any SSid\n")); - } else { -@@ -1267,7 +962,7 @@ _func_enter_; - if(psecnetwork==NULL) - { - if(pcmd !=NULL) -- rtw_mfree((unsigned char *)pcmd, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)pcmd); - - res=_FAIL; - -@@ -1348,7 +1043,7 @@ _func_enter_; - - pcmd->cmdsz = get_WLAN_BSSID_EX_sz(psecnetwork);//get cmdsz before endian conversion - -- _rtw_init_listhead(&pcmd->list); -+ INIT_LIST_HEAD(&pcmd->list); - pcmd->cmdcode = _JoinBss_CMD_;//GEN_CMD_CODE(_JoinBss) - pcmd->parmbuf = (unsigned char *)psecnetwork; - pcmd->rsp = NULL; -@@ -1387,7 +1082,7 @@ _func_enter_; - cmdobj = (struct cmd_obj *)rtw_zmalloc(sizeof(*cmdobj)); - if (cmdobj == NULL) { - res = _FAIL; -- rtw_mfree((u8 *)param, sizeof(*param)); -+ kfree((u8 *)param); - goto exit; - } - init_h2fwcmd_w_parm_no_rsp(cmdobj, param, _DisConnect_CMD_); -@@ -1396,7 +1091,7 @@ _func_enter_; - /* no need to enqueue, do the cmd hdl directly and free cmd parameter */ - if (H2C_SUCCESS != disconnect_hdl(padapter, (u8 *)param)) - res = _FAIL; -- rtw_mfree((u8 *)param, sizeof(*param)); -+ kfree((u8 *)param); - } - - exit: -@@ -1426,7 +1121,7 @@ _func_enter_; - if(enqueue){ - ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); - if(ph2c==NULL){ -- rtw_mfree((u8 *)psetop, sizeof(*psetop)); -+ kfree((u8 *)psetop); - res= _FAIL; - goto exit; - } -@@ -1436,7 +1131,7 @@ _func_enter_; - } - else{ - setopmode_hdl(padapter, (u8 *)psetop); -- rtw_mfree((u8 *)psetop, sizeof(*psetop)); -+ kfree((u8 *)psetop); - } - exit: - -@@ -1486,15 +1181,15 @@ _func_enter_; - { - ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); - if ( ph2c == NULL){ -- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); -+ kfree((u8 *) psetstakey_para); - res= _FAIL; - goto exit; - } - - psetstakey_rsp = (struct set_stakey_rsp*)rtw_zmalloc(sizeof(struct set_stakey_rsp)); - if(psetstakey_rsp == NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); -+ kfree((u8 *) ph2c); -+ kfree((u8 *) psetstakey_para); - res=_FAIL; - goto exit; - } -@@ -1506,7 +1201,7 @@ _func_enter_; - } - else{ - set_stakey_hdl(padapter, (u8 *)psetstakey_para); -- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); -+ kfree((u8 *) psetstakey_para); - } - exit: - -@@ -1546,15 +1241,15 @@ _func_enter_; - - psetstakey_para = (struct set_stakey_parm*)rtw_zmalloc(sizeof(struct set_stakey_parm)); - if(psetstakey_para==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -+ kfree((u8 *) ph2c); - res=_FAIL; - goto exit; - } - - psetstakey_rsp = (struct set_stakey_rsp*)rtw_zmalloc(sizeof(struct set_stakey_rsp)); - if(psetstakey_rsp == NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- rtw_mfree((u8 *) psetstakey_para, sizeof(struct set_stakey_parm)); -+ kfree((u8 *) ph2c); -+ kfree((u8 *) psetstakey_para); - res=_FAIL; - goto exit; - } -@@ -1578,123 +1273,6 @@ _func_exit_; - return res; - } - --u8 rtw_setrttbl_cmd(_adapter *padapter, struct setratable_parm *prate_table) --{ -- struct cmd_obj* ph2c; -- struct setratable_parm * psetrttblparm; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- u8 res=_SUCCESS; --_func_enter_; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res= _FAIL; -- goto exit; -- } -- psetrttblparm = (struct setratable_parm*)rtw_zmalloc(sizeof(struct setratable_parm)); -- -- if(psetrttblparm==NULL){ -- rtw_mfree((unsigned char *) ph2c, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, psetrttblparm, GEN_CMD_CODE(_SetRaTable)); -- -- memcpy(psetrttblparm,prate_table,sizeof(struct setratable_parm)); -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: --_func_exit_; -- return res; -- --} -- --u8 rtw_getrttbl_cmd(_adapter *padapter, struct getratable_rsp *pval) --{ -- struct cmd_obj* ph2c; -- struct getratable_parm * pgetrttblparm; -- struct cmd_priv *pcmdpriv=&padapter->cmdpriv; -- u8 res=_SUCCESS; --_func_enter_; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res= _FAIL; -- goto exit; -- } -- pgetrttblparm = (struct getratable_parm*)rtw_zmalloc(sizeof(struct getratable_parm)); -- -- if(pgetrttblparm==NULL){ -- rtw_mfree((unsigned char *) ph2c, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- --// init_h2fwcmd_w_parm_no_rsp(ph2c, psetrttblparm, GEN_CMD_CODE(_SetRaTable)); -- -- _rtw_init_listhead(&ph2c->list); -- ph2c->cmdcode =GEN_CMD_CODE(_GetRaTable); -- ph2c->parmbuf = (unsigned char *)pgetrttblparm; -- ph2c->cmdsz = sizeof(struct getratable_parm); -- ph2c->rsp = (u8*)pval; -- ph2c->rspsz = sizeof(struct getratable_rsp); -- -- pgetrttblparm ->rsvd = 0x0; -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: --_func_exit_; -- return res; -- --} -- --u8 rtw_setassocsta_cmd(_adapter *padapter, u8 *mac_addr) --{ -- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; -- struct cmd_obj* ph2c; -- struct set_assocsta_parm *psetassocsta_para; -- struct set_stakey_rsp *psetassocsta_rsp = NULL; -- -- u8 res=_SUCCESS; -- --_func_enter_; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(ph2c==NULL){ -- res= _FAIL; -- goto exit; -- } -- -- psetassocsta_para = (struct set_assocsta_parm*)rtw_zmalloc(sizeof(struct set_assocsta_parm)); -- if(psetassocsta_para==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- res=_FAIL; -- goto exit; -- } -- -- psetassocsta_rsp = (struct set_stakey_rsp*)rtw_zmalloc(sizeof(struct set_assocsta_rsp)); -- if(psetassocsta_rsp==NULL){ -- rtw_mfree((u8 *) ph2c, sizeof(struct cmd_obj)); -- rtw_mfree((u8 *) psetassocsta_para, sizeof(struct set_assocsta_parm)); -- return _FAIL; -- } -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, psetassocsta_para, _SetAssocSta_CMD_); -- ph2c->rsp = (u8 *) psetassocsta_rsp; -- ph2c->rspsz = sizeof(struct set_assocsta_rsp); -- -- memcpy(psetassocsta_para->addr, mac_addr,ETH_ALEN); -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); -- --exit: -- --_func_exit_; -- -- return res; -- } -- - u8 rtw_addbareq_cmd(_adapter*padapter, u8 tid, u8 *addr) - { - struct cmd_priv *pcmdpriv = &padapter->cmdpriv; -@@ -1713,7 +1291,7 @@ _func_enter_; - - paddbareq_parm = (struct addBaReq_parm*)rtw_zmalloc(sizeof(struct addBaReq_parm)); - if(paddbareq_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -1752,7 +1330,7 @@ _func_enter_; - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -1793,7 +1371,7 @@ _func_enter_; - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -1835,7 +1413,7 @@ _func_enter_; - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -1858,61 +1436,6 @@ _func_exit_; - - } - --u8 rtw_set_ch_cmd(_adapter*padapter, u8 ch, u8 bw, u8 ch_offset, u8 enqueue) --{ -- struct cmd_obj *pcmdobj; -- struct set_ch_parm *set_ch_parm; -- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; -- -- u8 res=_SUCCESS; -- --_func_enter_; -- -- DBG_871X(FUNC_NDEV_FMT" ch:%u, bw:%u, ch_offset:%u\n", -- FUNC_NDEV_ARG(padapter->pnetdev), ch, bw, ch_offset); -- -- /* check input parameter */ -- -- /* prepare cmd parameter */ -- set_ch_parm = (struct set_ch_parm *)rtw_zmalloc(sizeof(*set_ch_parm)); -- if (set_ch_parm == NULL) { -- res= _FAIL; -- goto exit; -- } -- set_ch_parm->ch = ch; -- set_ch_parm->bw = bw; -- set_ch_parm->ch_offset = ch_offset; -- -- if (enqueue) { -- /* need enqueue, prepare cmd_obj and enqueue */ -- pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(pcmdobj == NULL){ -- rtw_mfree((u8 *)set_ch_parm, sizeof(*set_ch_parm)); -- res=_FAIL; -- goto exit; -- } -- -- init_h2fwcmd_w_parm_no_rsp(pcmdobj, set_ch_parm, GEN_CMD_CODE(_SetChannel)); -- res = rtw_enqueue_cmd(pcmdpriv, pcmdobj); -- } else { -- /* no need to enqueue, do the cmd hdl directly and free cmd parameter */ -- if( H2C_SUCCESS !=set_ch_hdl(padapter, (u8 *)set_ch_parm) ) -- res = _FAIL; -- -- rtw_mfree((u8 *)set_ch_parm, sizeof(*set_ch_parm)); -- } -- -- /* do something based on res... */ -- --exit: -- -- DBG_871X(FUNC_NDEV_FMT" res:%u\n", FUNC_NDEV_ARG(padapter->pnetdev), res); -- --_func_exit_; -- -- return res; --} -- - u8 rtw_set_chplan_cmd(_adapter*padapter, u8 chplan, u8 enqueue, u8 swconfig) - { - struct cmd_obj* pcmdobj; -@@ -1951,7 +1474,7 @@ _func_enter_; - //need enqueue, prepare cmd_obj and enqueue - pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); - if(pcmdobj == NULL){ -- rtw_mfree((u8 *)setChannelPlan_param, sizeof(struct SetChannelPlan_param)); -+ kfree((u8 *)setChannelPlan_param); - res=_FAIL; - goto exit; - } -@@ -1965,7 +1488,7 @@ _func_enter_; - if( H2C_SUCCESS !=set_chplan_hdl(padapter, (unsigned char *)setChannelPlan_param) ) - res = _FAIL; - -- rtw_mfree((u8 *)setChannelPlan_param, sizeof(struct SetChannelPlan_param)); -+ kfree((u8 *)setChannelPlan_param); - } - - //do something based on res... -@@ -1979,92 +1502,6 @@ _func_exit_; - return res; - } - --u8 rtw_led_blink_cmd(_adapter*padapter, void * pLed) --{ -- struct cmd_obj* pcmdobj; -- struct LedBlink_param *ledBlink_param; -- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; -- -- u8 res=_SUCCESS; -- --_func_enter_; -- -- RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+rtw_led_blink_cmd\n")); -- -- pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(pcmdobj == NULL){ -- res=_FAIL; -- goto exit; -- } -- -- ledBlink_param = (struct LedBlink_param *)rtw_zmalloc(sizeof(struct LedBlink_param)); -- if(ledBlink_param == NULL) { -- rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- ledBlink_param->pLed=pLed; -- -- init_h2fwcmd_w_parm_no_rsp(pcmdobj, ledBlink_param, GEN_CMD_CODE(_LedBlink)); -- res = rtw_enqueue_cmd(pcmdpriv, pcmdobj); -- --exit: -- --_func_exit_; -- -- return res; --} -- --u8 rtw_set_csa_cmd(_adapter*padapter, u8 new_ch_no) --{ -- struct cmd_obj* pcmdobj; -- struct SetChannelSwitch_param*setChannelSwitch_param; -- struct mlme_priv *pmlmepriv = &padapter->mlmepriv; -- struct cmd_priv *pcmdpriv = &padapter->cmdpriv; -- -- u8 res=_SUCCESS; -- --_func_enter_; -- -- RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+rtw_set_csa_cmd\n")); -- -- pcmdobj = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if(pcmdobj == NULL){ -- res=_FAIL; -- goto exit; -- } -- -- setChannelSwitch_param = (struct SetChannelSwitch_param *)rtw_zmalloc(sizeof(struct SetChannelSwitch_param)); -- if(setChannelSwitch_param == NULL) { -- rtw_mfree((u8 *)pcmdobj, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- setChannelSwitch_param->new_ch_no=new_ch_no; -- -- init_h2fwcmd_w_parm_no_rsp(pcmdobj, setChannelSwitch_param, GEN_CMD_CODE(_SetChannelSwitch)); -- res = rtw_enqueue_cmd(pcmdpriv, pcmdobj); -- --exit: -- --_func_exit_; -- -- return res; --} -- --u8 rtw_tdls_cmd(_adapter *padapter, u8 *addr, u8 option) --{ -- u8 res=_SUCCESS; -- --_func_enter_; -- --_func_exit_; -- -- return res; --} -- - static void collect_traffic_statistics(_adapter *padapter) - { - struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter); -@@ -2150,8 +1587,6 @@ u8 traffic_status_watchdog(_adapter *pad - TRAFFIC_PROTECT_PERIOD_MS, - link_detect->NumTxOkInPeriod, - link_detect->NumRxUnicastOkInPeriod); -- -- rtw_lock_traffic_suspend_timeout(TRAFFIC_PROTECT_PERIOD_MS); - } - #endif - -@@ -2250,12 +1685,10 @@ static void dynamic_chk_wk_hdl(_adapter - struct mlme_priv *pmlmepriv; - pmlmepriv = &(padapter->mlmepriv); - --#ifdef CONFIG_AP_MODE - if(check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) - { - expire_timeout_chk(padapter); - } --#endif - - //for debug purpose - _linked_info_dump(padapter); -@@ -2370,7 +1803,7 @@ _func_enter_; - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -2418,7 +1851,7 @@ u8 rtw_dm_in_lps_wk_cmd(_adapter*padapte - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -2470,43 +1903,6 @@ static void rtw_lps_change_dtim_hdl(_ada - up(&pwrpriv->lock); - } - --u8 rtw_lps_change_dtim_cmd(_adapter*padapter, u8 dtim) --{ -- struct cmd_obj *ph2c; -- struct drvextra_cmd_parm *pdrvextra_cmd_parm; -- 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; -- } -- -- pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); -- if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -- res= _FAIL; -- goto exit; -- } -- -- pdrvextra_cmd_parm->ec_id = LPS_CHANGE_DTIM_CID; -- pdrvextra_cmd_parm->type = dtim; -- pdrvextra_cmd_parm->size = 0; -- pdrvextra_cmd_parm->pbuf = NULL; -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); -- } -- --exit: -- -- return res; -- --} -- - static void rtw_dm_ra_mask_hdl(_adapter *padapter, struct sta_info *psta) - { - if (psta) { -@@ -2530,7 +1926,7 @@ u8 rtw_dm_ra_mask_wk_cmd(_adapter*padapt - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -2583,7 +1979,7 @@ _func_enter_; - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ppscmd, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ppscmd); - res= _FAIL; - goto exit; - } -@@ -2604,8 +2000,6 @@ _func_exit_; - - } - --#ifdef CONFIG_AP_MODE -- - extern u32 g_wait_hiq_empty; - - static void rtw_chk_hi_queue_hdl(_adapter *padapter) -@@ -2666,7 +2060,7 @@ u8 rtw_chk_hi_queue_cmd(_adapter*padapte - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if(pdrvextra_cmd_parm==NULL){ -- rtw_mfree((unsigned char *)ph2c, sizeof(struct cmd_obj)); -+ kfree((unsigned char *)ph2c); - res= _FAIL; - goto exit; - } -@@ -2685,7 +2079,6 @@ exit: - return res; - - } --#endif - - struct btinfo { - u8 cid; -@@ -2751,50 +2144,6 @@ static void rtw_btinfo_hdl(_adapter *ada - rtw_btcoex_BtInfoNotify(adapter ,len+1, &buf[1]); - } - --u8 rtw_btinfo_cmd(_adapter *adapter, u8 *buf, u16 len) --{ -- struct cmd_obj *ph2c; -- struct drvextra_cmd_parm *pdrvextra_cmd_parm; -- u8 *btinfo; -- struct cmd_priv *pcmdpriv = &adapter->cmdpriv; -- u8 res = _SUCCESS; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if (ph2c == NULL) { -- res = _FAIL; -- goto exit; -- } -- -- pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); -- if (pdrvextra_cmd_parm == NULL) { -- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); -- res = _FAIL; -- goto exit; -- } -- -- btinfo = rtw_zmalloc(len); -- if (btinfo == NULL) { -- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); -- rtw_mfree((u8*)pdrvextra_cmd_parm, sizeof(struct drvextra_cmd_parm)); -- res = _FAIL; -- goto exit; -- } -- -- pdrvextra_cmd_parm->ec_id = BTINFO_WK_CID; -- pdrvextra_cmd_parm->type = 0; -- pdrvextra_cmd_parm->size = len; -- pdrvextra_cmd_parm->pbuf = btinfo; -- -- memcpy(btinfo, buf, len); -- -- init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra)); -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); -- --exit: -- return res; --} -- - u8 rtw_c2h_packet_wk_cmd(PADAPTER padapter, u8 *pbuf, u16 length) - { - struct cmd_obj *ph2c; -@@ -2810,7 +2159,7 @@ u8 rtw_c2h_packet_wk_cmd(PADAPTER padapt - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if (pdrvextra_cmd_parm == NULL) { -- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); -+ kfree((u8*)ph2c); - res = _FAIL; - goto exit; - } -@@ -2845,7 +2194,7 @@ u8 rtw_c2h_wk_cmd(PADAPTER padapter, u8 - - pdrvextra_cmd_parm = (struct drvextra_cmd_parm*)rtw_zmalloc(sizeof(struct drvextra_cmd_parm)); - if (pdrvextra_cmd_parm == NULL) { -- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); -+ kfree((u8*)ph2c); - res = _FAIL; - goto exit; - } -@@ -2864,42 +2213,6 @@ exit: - return res; - } - --u8 rtw_run_in_thread_cmd(PADAPTER padapter, void (*func)(void*), void* context) --{ -- struct cmd_priv *pcmdpriv; -- struct cmd_obj *ph2c; -- struct RunInThread_param *parm; -- s32 res = _SUCCESS; -- --_func_enter_; -- -- pcmdpriv = &padapter->cmdpriv; -- -- ph2c = (struct cmd_obj*)rtw_zmalloc(sizeof(struct cmd_obj)); -- if (NULL == ph2c) { -- res = _FAIL; -- goto exit; -- } -- -- parm = (struct RunInThread_param*)rtw_zmalloc(sizeof(struct RunInThread_param)); -- if (NULL == parm) { -- rtw_mfree((u8*)ph2c, sizeof(struct cmd_obj)); -- res = _FAIL; -- goto exit; -- } -- -- parm->func = func; -- parm->context = context; -- init_h2fwcmd_w_parm_no_rsp(ph2c, parm, GEN_CMD_CODE(_RunInThreadCMD)); -- -- res = rtw_enqueue_cmd(pcmdpriv, ph2c); --exit: -- --_func_exit_; -- -- return res; --} -- - static void c2h_wk_callback(_workitem *work) - { - struct evt_priv *evtpriv = container_of(work, struct evt_priv, c2h_wk); -@@ -2916,7 +2229,7 @@ static void c2h_wk_callback(_workitem *w - } else if ((c2h_evt = (u8 *)rtw_malloc(16)) != NULL) { - /* This C2H event is not read, read & clear now */ - if (rtw_hal_c2h_evt_read(adapter, c2h_evt) != _SUCCESS) { -- rtw_mfree(c2h_evt, 16); -+ kfree(c2h_evt); - continue; - } - } -@@ -2926,14 +2239,14 @@ static void c2h_wk_callback(_workitem *w - continue; - - if (!rtw_hal_c2h_valid(adapter, c2h_evt)) { -- rtw_mfree(c2h_evt, 16); -+ kfree(c2h_evt); - continue; - } - - if (ccx_id_filter(c2h_evt) == true) { - /* Handle CCX report here */ - rtw_hal_c2h_handler(adapter, c2h_evt); -- rtw_mfree(c2h_evt, 16); -+ kfree(c2h_evt); - } else { - /* Enqueue into cmd_thread for others */ - rtw_c2h_wk_cmd(adapter, c2h_evt); -@@ -2969,11 +2282,9 @@ u8 rtw_drvextra_cmd_hdl(_adapter *padapt - case LPS_CHANGE_DTIM_CID: - rtw_lps_change_dtim_hdl(padapter, (u8)pdrvextra_cmd->type); - break; --#ifdef CONFIG_AP_MODE - case CHECK_HIQ_WK_CID: - rtw_chk_hi_queue_hdl(padapter); - break; --#endif //CONFIG_AP_MODE - #ifdef CONFIG_INTEL_WIDI - case INTEl_WIDI_WK_CID: - intel_widi_wk_hdl(padapter, pdrvextra_cmd->type, pdrvextra_cmd->pbuf); -@@ -3001,7 +2312,7 @@ u8 rtw_drvextra_cmd_hdl(_adapter *padapt - - if (pdrvextra_cmd->pbuf && pdrvextra_cmd->size>0) - { -- rtw_mfree(pdrvextra_cmd->pbuf, pdrvextra_cmd->size); -+ kfree(pdrvextra_cmd->pbuf); - } - - return H2C_SUCCESS; -@@ -3038,9 +2349,9 @@ _func_enter_; - - if (pcmd->res != H2C_SUCCESS) - { -- _enter_critical_bh(&pmlmepriv->lock, &irqL); -+ spin_lock_bh(&pmlmepriv->lock); - set_fwstate(pmlmepriv, _FW_LINKED); -- _exit_critical_bh(&pmlmepriv->lock, &irqL); -+ spin_unlock_bh(&pmlmepriv->lock); - - RT_TRACE(_module_rtl871x_cmd_c_,_drv_err_,("\n ***Error: disconnect_cmd_callback Fail ***\n.")); - -@@ -3100,7 +2411,7 @@ _func_enter_; - - _cancel_timer(&pmlmepriv->assoc_timer, &timer_cancelled); - -- _enter_critical_bh(&pmlmepriv->lock, &irqL); -+ spin_lock_bh(&pmlmepriv->lock); - - - if(check_fwstate(pmlmepriv, WIFI_AP_STATE) ) -@@ -3123,28 +2434,28 @@ _func_enter_; - _irqL irqL; - - pwlan = _rtw_alloc_network(pmlmepriv); -- _enter_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); -+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock)); - if ( pwlan == NULL) - { - pwlan = rtw_get_oldest_wlan_network(&pmlmepriv->scanned_queue); - if( pwlan == NULL) - { - RT_TRACE(_module_rtl871x_cmd_c_,_drv_err_,("\n Error: can't get pwlan in rtw_joinbss_event_callback \n")); -- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); -+ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); - goto createbss_cmd_fail; - } - pwlan->last_scanned = jiffies; - } - else - { -- rtw_list_insert_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); -+ list_add_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); - } - - pnetwork->Length = get_WLAN_BSSID_EX_sz(pnetwork); - memcpy(&(pwlan->network), pnetwork, pnetwork->Length); - //pwlan->fixed = true; - -- //rtw_list_insert_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); -+ //list_add_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue); - - // copy pdev_network information to pmlmepriv->cur_network - memcpy(&tgt_network->network, pnetwork, (get_WLAN_BSSID_EX_sz(pnetwork))); -@@ -3154,14 +2465,14 @@ _func_enter_; - - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - -- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); -+ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); - // we will set _FW_LINKED when there is one more sat to join us (rtw_stassoc_event_callback) - - } - - createbss_cmd_fail: - -- _exit_critical_bh(&pmlmepriv->lock, &irqL); -+ spin_unlock_bh(&pmlmepriv->lock); - exit: - rtw_free_cmd_obj(pcmd); - -@@ -3214,28 +2525,16 @@ _func_enter_; - - psta->aid = psta->mac_id = passocsta_rsp->cam_id; - -- _enter_critical_bh(&pmlmepriv->lock, &irqL); -+ spin_lock_bh(&pmlmepriv->lock); - - if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) && (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true)) - _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); - - set_fwstate(pmlmepriv, _FW_LINKED); -- _exit_critical_bh(&pmlmepriv->lock, &irqL); -+ spin_unlock_bh(&pmlmepriv->lock); - - exit: - rtw_free_cmd_obj(pcmd); - - _func_exit_; - } -- --void rtw_getrttbl_cmd_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd); --void rtw_getrttbl_cmd_cmdrsp_callback(_adapter* padapter, struct cmd_obj *pcmd) --{ --_func_enter_; -- -- rtw_free_cmd_obj(pcmd); -- --_func_exit_; -- --} -- -diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_debug.c linux/3rdparty/rtl8723bs/core/rtw_debug.c ---- linux/3rdparty/rtl8723bs.old/core/rtw_debug.c 2015-05-10 14:36:05.666667879 +0300 -+++ linux/3rdparty/rtl8723bs/core/rtw_debug.c 2015-05-07 17:46:55.000000000 +0300 -@@ -62,7 +62,7 @@ u32 GlobalDebugLevel = _drv_err_; - - void dump_drv_version(void *sel) - { -- DBG_871X_SEL_NL(sel, "%s %s\n", DRV_NAME, DRIVERVERSION); -+ DBG_871X_SEL_NL(sel, "%s %s\n", "rtl8723bs", DRIVERVERSION); - } - - void dump_log_level(void *sel) -@@ -309,7 +309,6 @@ int proc_get_mlmext_state(struct seq_fil - return 0; - } - --#ifdef CONFIG_LAYER2_ROAMING - int proc_get_roam_flags(struct seq_file *m, void *v) - { - struct net_device *dev = m->private; -@@ -411,7 +410,6 @@ ssize_t proc_set_roam_tgt_addr(struct fi - - return count; - } --#endif /* CONFIG_LAYER2_ROAMING */ - - int proc_get_qos_option(struct seq_file *m, void *v) - { -@@ -463,18 +461,19 @@ int proc_get_survey_info(struct seq_file - s16 notify_noise = 0; - u16 index = 0; - -- _enter_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); -+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock)); - phead = get_list_head(queue); -+ plist = phead ? get_next(phead) : NULL; - plist = get_next(phead); - if ((!phead) || (!plist)) { -- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); -+ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); - return 0; - } - - DBG_871X_SEL_NL(m, "%5s %-17s %3s %-3s %-4s %-4s %5s %s\n","index", "bssid", "ch", "RSSI", "SdBm", "Noise", "age", "ssid"); - while(1) - { -- if (rtw_end_of_queue_search(phead,plist)== true) -+ if (phead == plist) - break; - - pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list); -@@ -504,7 +503,7 @@ int proc_get_survey_info(struct seq_file - pnetwork->network.Ssid.Ssid); - plist = get_next(plist); - } -- _exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL); -+ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); - - return 0; - } -@@ -1325,9 +1324,6 @@ ssize_t proc_set_rssi_disp(struct file * - - } - -- --#ifdef CONFIG_AP_MODE -- - int proc_get_all_sta_info(struct seq_file *m, void *v) - { - struct net_device *dev = m->private; -@@ -1341,14 +1337,14 @@ int proc_get_all_sta_info(struct seq_fil - - DBG_871X_SEL_NL(m, "sta_dz_bitmap=0x%x, tim_bitmap=0x%x\n", pstapriv->sta_dz_bitmap, pstapriv->tim_bitmap); - -- _enter_critical_bh(&pstapriv->sta_hash_lock, &irqL); -+ spin_lock_bh(&pstapriv->sta_hash_lock); - - for(i=0; i< NUM_STA; i++) - { - phead = &(pstapriv->sta_hash[i]); - plist = get_next(phead); - -- while ((rtw_end_of_queue_search(phead, plist)) == false) -+ while (phead != plist) - { - psta = LIST_CONTAINOR(plist, struct sta_info, hash_list); - -@@ -1394,12 +1390,10 @@ int proc_get_all_sta_info(struct seq_fil - - } - -- _exit_critical_bh(&pstapriv->sta_hash_lock, &irqL); -+ spin_unlock_bh(&pstapriv->sta_hash_lock); - - return 0; - } -- --#endif - - int proc_get_btcoex_dbg(struct seq_file *m, void *v) - { -@@ -1497,7 +1491,7 @@ int proc_get_btcoex_info(struct seq_file - - DBG_871X_SEL(m, "%s\n", pbuf); - -- rtw_mfree(pbuf, bufsize); -+ kfree(pbuf); - - return 0; - } -diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_eeprom.c linux/3rdparty/rtl8723bs/core/rtw_eeprom.c ---- linux/3rdparty/rtl8723bs.old/core/rtw_eeprom.c 2015-05-10 14:36:05.666667879 +0300 -+++ linux/3rdparty/rtl8723bs/core/rtw_eeprom.c 1970-01-01 02:00:00.000000000 +0200 -@@ -1,381 +0,0 @@ --/****************************************************************************** -- * -- * 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 _RTW_EEPROM_C_ -- --#include --#include --#include -- --void up_clk(_adapter* padapter, u16 *x) --{ --_func_enter_; -- *x = *x | _EESK; -- rtw_write8(padapter, EE_9346CR, (u8)*x); -- udelay(CLOCK_RATE); -- --_func_exit_; -- --} -- --void down_clk(_adapter * padapter, u16 *x ) --{ --_func_enter_; -- *x = *x & ~_EESK; -- rtw_write8(padapter, EE_9346CR, (u8)*x); -- udelay(CLOCK_RATE); --_func_exit_; --} -- --void shift_out_bits(_adapter * padapter, u16 data, u16 count) --{ -- u16 x,mask; --_func_enter_; -- -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- mask = 0x01 << (count - 1); -- x = rtw_read8(padapter, EE_9346CR); -- -- x &= ~(_EEDO | _EEDI); -- -- do -- { -- x &= ~_EEDI; -- if(data & mask) -- x |= _EEDI; -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- rtw_write8(padapter, EE_9346CR, (u8)x); -- udelay(CLOCK_RATE); -- up_clk(padapter, &x); -- down_clk(padapter, &x); -- mask = mask >> 1; -- } while(mask); -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- x &= ~_EEDI; -- rtw_write8(padapter, EE_9346CR, (u8)x); --out: --_func_exit_; --} -- --u16 shift_in_bits (_adapter * padapter) --{ -- u16 x,d=0,i; --_func_enter_; -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- x = rtw_read8(padapter, EE_9346CR); -- -- x &= ~( _EEDO | _EEDI); -- d = 0; -- -- for(i=0; i<16; i++) -- { -- d = d << 1; -- up_clk(padapter, &x); -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- x = rtw_read8(padapter, EE_9346CR); -- -- x &= ~(_EEDI); -- if(x & _EEDO) -- d |= 1; -- -- down_clk(padapter, &x); -- } --out: --_func_exit_; -- -- return d; --} -- --void standby(_adapter * padapter ) --{ -- u8 x; --_func_enter_; -- x = rtw_read8(padapter, EE_9346CR); -- -- x &= ~(_EECS | _EESK); -- rtw_write8(padapter, EE_9346CR,x); -- -- udelay(CLOCK_RATE); -- x |= _EECS; -- rtw_write8(padapter, EE_9346CR, x); -- udelay(CLOCK_RATE); --_func_exit_; --} -- --u16 wait_eeprom_cmd_done(_adapter* padapter) --{ -- u8 x; -- u16 i,res=false; --_func_enter_; -- standby(padapter ); -- for (i=0; i<200; i++) -- { -- x = rtw_read8(padapter, EE_9346CR); -- if (x & _EEDO){ -- res=true; -- goto exit; -- } -- udelay(CLOCK_RATE); -- } --exit: --_func_exit_; -- return res; --} -- --void eeprom_clean(_adapter * padapter) --{ -- u16 x; --_func_enter_; -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- x = rtw_read8(padapter, EE_9346CR); -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- x &= ~(_EECS | _EEDI); -- rtw_write8(padapter, EE_9346CR, (u8)x); -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- up_clk(padapter, &x); -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- down_clk(padapter, &x); --out: --_func_exit_; --} -- --void eeprom_write16(_adapter * padapter, u16 reg, u16 data) --{ -- u8 x; -- --_func_enter_; -- -- x = rtw_read8(padapter, EE_9346CR); -- -- x &= ~(_EEDI | _EEDO | _EESK | _EEM0); -- x |= _EEM1 | _EECS; -- rtw_write8(padapter, EE_9346CR, x); -- -- shift_out_bits(padapter, EEPROM_EWEN_OPCODE, 5); -- -- if(padapter->EepromAddressSize==8) //CF+ and SDIO -- shift_out_bits(padapter, 0, 6); -- else //USB -- shift_out_bits(padapter, 0, 4); -- -- standby( padapter); -- --// Commented out by rcnjko, 2004.0 --// // Erase this particular word. Write the erase opcode and register --// // number in that order. The opcode is 3bits in length; reg is 6 bits long. --// shift_out_bits(Adapter, EEPROM_ERASE_OPCODE, 3); --// shift_out_bits(Adapter, reg, Adapter->EepromAddressSize); --// --// if (wait_eeprom_cmd_done(Adapter ) == false) --// { --// return; --// } -- -- -- standby(padapter ); -- -- // write the new word to the EEPROM -- -- // send the write opcode the EEPORM -- shift_out_bits(padapter, EEPROM_WRITE_OPCODE, 3); -- -- // select which word in the EEPROM that we are writing to. -- shift_out_bits(padapter, reg, padapter->EepromAddressSize); -- -- // write the data to the selected EEPROM word. -- shift_out_bits(padapter, data, 16); -- -- if (wait_eeprom_cmd_done(padapter ) == false) -- { -- -- goto exit; -- } -- -- standby(padapter ); -- -- shift_out_bits(padapter, EEPROM_EWDS_OPCODE, 5); -- shift_out_bits(padapter, reg, 4); -- -- eeprom_clean(padapter ); --exit: --_func_exit_; -- return; --} -- --u16 eeprom_read16(_adapter * padapter, u16 reg) //ReadEEprom --{ -- -- u16 x; -- u16 data=0; -- --_func_enter_; -- -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- // select EEPROM, reset bits, set _EECS -- x = rtw_read8(padapter, EE_9346CR); -- -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- -- x &= ~(_EEDI | _EEDO | _EESK | _EEM0); -- x |= _EEM1 | _EECS; -- rtw_write8(padapter, EE_9346CR, (unsigned char)x); -- -- // write the read opcode and register number in that order -- // The opcode is 3bits in length, reg is 6 bits long -- shift_out_bits(padapter, EEPROM_READ_OPCODE, 3); -- shift_out_bits(padapter, reg, padapter->EepromAddressSize); -- -- // Now read the data (16 bits) in from the selected EEPROM word -- data = shift_in_bits(padapter); -- -- eeprom_clean(padapter); --out: --_func_exit_; -- return data; -- -- --} -- -- -- -- --//From even offset --void eeprom_read_sz(_adapter * padapter, u16 reg, u8* data, u32 sz) --{ -- -- u16 x, data16; -- u32 i; --_func_enter_; -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- // select EEPROM, reset bits, set _EECS -- x = rtw_read8(padapter, EE_9346CR); -- -- if(padapter->bSurpriseRemoved==true){ -- RT_TRACE(_module_rtl871x_eeprom_c_,_drv_err_,("padapter->bSurpriseRemoved==true")); -- goto out; -- } -- -- x &= ~(_EEDI | _EEDO | _EESK | _EEM0); -- x |= _EEM1 | _EECS; -- rtw_write8(padapter, EE_9346CR, (unsigned char)x); -- -- // write the read opcode and register number in that order -- // The opcode is 3bits in length, reg is 6 bits long -- shift_out_bits(padapter, EEPROM_READ_OPCODE, 3); -- shift_out_bits(padapter, reg, padapter->EepromAddressSize); -- -- -- for(i=0; i>8; -- } -- -- eeprom_clean(padapter); --out: --_func_exit_; -- -- -- --} -- -- --//addr_off : address offset of the entry in eeprom (not the tuple number of eeprom (reg); that is addr_off !=reg) --u8 eeprom_read(_adapter * padapter, u32 addr_off, u8 sz, u8* rbuf) --{ -- u8 quotient, remainder, addr_2align_odd; -- u16 reg, stmp , i=0, idx = 0; --_func_enter_; -- reg = (u16)(addr_off >> 1); -- addr_2align_odd = (u8)(addr_off & 0x1); -- -- if(addr_2align_odd) //read that start at high part: e.g 1,3,5,7,9,... -- { -- stmp = eeprom_read16(padapter, reg); -- rbuf[idx++] = (u8) ((stmp>>8)&0xff); //return hogh-part of the short -- reg++; sz--; -- } -- -- quotient = sz >> 1; -- remainder = sz & 0x1; -- -- for( i=0 ; i < quotient; i++) -- { -- stmp = eeprom_read16(padapter, reg+i); -- rbuf[idx++] = (u8) (stmp&0xff); -- rbuf[idx++] = (u8) ((stmp>>8)&0xff); -- } -- -- reg = reg+i; -- if(remainder){ //end of read at lower part of short : 0,2,4,6,... -- stmp = eeprom_read16(padapter, reg); -- rbuf[idx] = (u8)(stmp & 0xff); -- } --_func_exit_; -- return true; --} -- -- -- --void read_eeprom_content(_adapter * padapter) --{ -- --_func_enter_; -- -- --_func_exit_; --} -- -diff -Nurp linux/3rdparty/rtl8723bs.old/core/rtw_efuse.c linux/3rdparty/rtl8723bs/core/rtw_efuse.c ---- linux/3rdparty/rtl8723bs.old/core/rtw_efuse.c 2015-05-10 14:36:05.666667879 +0300 -+++ linux/3rdparty/rtl8723bs/core/rtw_efuse.c 2015-05-07 17:46:55.000000000 +0300 -@@ -121,18 +121,8 @@ Efuse_PowerSwitch( - pAdapter->HalFunc.EfusePowerSwitch(pAdapter, bWrite, PwrState); - } - --void --BTEfuse_PowerSwitch( -- IN PADAPTER pAdapter, -- IN u8 bWrite, -- IN u8 PwrState) --{ -- if(pAdapter->HalFunc.BTEfusePowerSwitch) -- pAdapter->HalFunc.BTEfusePowerSwitch(pAdapter, bWrite, PwrState); --} -- - /*----------------------------------------------------------------------------- -- * Function: efuse_GetCurrentSize -+ * Function: Efuse_GetCurrentSize - * - * Overview: Get current efuse size!!! - * -@@ -174,68 +164,6 @@ Efuse_CalculateWordCnts(IN u8 word_en) - - // - // Description: --// Execute E-Fuse read byte operation. --// Refered from SD1 Richard. --// --// Assumption: --// 1. Boot from E-Fuse and successfully auto-load. --// 2. PASSIVE_LEVEL (USB interface) --// --// Created by Roger, 2008.10.21. --// --void --ReadEFuseByte( -- PADAPTER Adapter, -- u16 _offset, -- u8 *pbuf, -- IN bool bPseudoTest) --{ -- u32 value32; -- u8 readbyte; -- u16 retry; -- -- if(bPseudoTest) -- { -- Efuse_Read1ByteFromFakeContent(Adapter, _offset, pbuf); -- return; -- } -- // <20130121, Kordan> For SMIC S55 EFUSE specificatoin. -- //0x34[11]: SW force PGMEN input of efuse to high. (for the bank selected by 0x34[9:8]) -- PHY_SetMacReg(Adapter, EFUSE_TEST, BIT11, 0); -- -- //Write Address -- rtw_write8(Adapter, EFUSE_CTRL+1, (_offset & 0xff)); -- readbyte = rtw_read8(Adapter, EFUSE_CTRL+2); -- rtw_write8(Adapter, EFUSE_CTRL+2, ((_offset >> 8) & 0x03) | (readbyte & 0xfc)); -- -- //Write bit 32 0 -- readbyte = rtw_read8(Adapter, EFUSE_CTRL+3); -- rtw_write8(Adapter, EFUSE_CTRL+3, (readbyte & 0x7f)); -- -- //Check bit 32 read-ready -- retry = 0; -- value32 = rtw_read32(Adapter, EFUSE_CTRL); -- //while(!(((value32 >> 24) & 0xff) & 0x80) && (retry<10)) -- while(!(((value32 >> 24) & 0xff) & 0x80) && (retry<10000)) -- { -- value32 = rtw_read32(Adapter, EFUSE_CTRL); -- retry++; -- } -- -- // 20100205 Joseph: Add delay suggested by SD1 Victor. -- // This fix the problem that Efuse read error in high temperature condition. -- // Designer says that there shall be some delay after ready bit is set, or the -- // result will always stay on last data we read. -- udelay(50); -- value32 = rtw_read32(Adapter, EFUSE_CTRL); -- -- *pbuf = (u8)(value32 & 0xff); -- //DBG_871X("ReadEFuseByte _offset:%08u, in %d ms\n",_offset ,jiffies_to_msecs(jiffies - start)); -- --} -- --// --// Description: - // 1. Execute E-Fuse read byte operation according as map offset and - // save to E-Fuse table. - // 2. Refered from SD1 Richard. -@@ -350,76 +278,6 @@ EFUSE_Read1Byte( - - }/* EFUSE_Read1Byte */ - --/*----------------------------------------------------------------------------- -- * Function: EFUSE_Write1Byte -- * -- * Overview: Copy from WMAC fot EFUSE write 1 byte. -- * -- * Input: NONE -- * -- * Output: NONE -- * -- * Return: NONE -- * -- * Revised History: -- * When Who Remark -- * 09/23/2008 MHC Copy from WMAC. -- * -- *---------------------------------------------------------------------------*/ -- --void --EFUSE_Write1Byte( -- IN PADAPTER Adapter, -- IN u16 Address, -- IN u8 Value); --void --EFUSE_Write1Byte( -- IN PADAPTER Adapter, -- IN u16 Address, -- IN u8 Value) --{ -- u8 Bytetemp = {0x00}; -- u8 temp = {0x00}; -- u32 k=0; -- u16 contentLen=0; -- -- //RT_TRACE(COMP_EFUSE, DBG_LOUD, ("Addr=%x Data =%x\n", Address, Value)); -- EFUSE_GetEfuseDefinition(Adapter, EFUSE_WIFI , TYPE_EFUSE_REAL_CONTENT_LEN, (void *)&contentLen, false); -- -- if( Address < contentLen) //E-fuse 512Byte -- { -- rtw_write8(Adapter, EFUSE_CTRL, Value); -- -- //Write E-fuse Register address bit0~7 -- temp = Address & 0xFF; -- rtw_write8(Adapter, EFUSE_CTRL+1, temp); -- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+2); -- -- //Write E-fuse Register address bit8~9 -- temp = ((Address >> 8) & 0x03) | (Bytetemp & 0xFC); -- rtw_write8(Adapter, EFUSE_CTRL+2, temp); -- -- //Write 0x30[31]=1 -- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+3); -- temp = Bytetemp | 0x80; -- rtw_write8(Adapter, EFUSE_CTRL+3, temp); -- -- //Wait Write-ready (0x30[31]=0) -- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+3); -- while(Bytetemp & 0x80) -- { -- Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+3); -- k++; -- if(k==100) -- { -- k=0; -- break; -- } -- } -- } --}/* EFUSE_Write1Byte */ -- -- - /* 11/16/2008 MH Read one byte from real Efuse. */ - u8 - efuse_OneByteRead( -@@ -642,289 +500,6 @@ Efuse_WordEnableDataWrite( IN PADAPTER p - return ret; - } - --static u8 efuse_read8(PADAPTER padapter, u16 address, u8 *value) --{ -- return efuse_OneByteRead(padapter,address, value, false); --} -- --static u8 efuse_write8(PADAPTER padapter, u16 address, u8 *value) --{ -- return efuse_OneByteWrite(padapter,address, *value, false); --} -- --/* -- * read/wirte raw efuse data -- */ --u8 rtw_efuse_access(PADAPTER padapter, u8 bWrite, u16 start_addr, u16 cnts, u8 *data) --{ -- int i = 0; -- u16 real_content_len = 0, max_available_size = 0; -- u8 res = _FAIL ; -- u8 (*rw8)(PADAPTER, u16, u8*); -- -- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_REAL_CONTENT_LEN, (void *)&real_content_len, false); -- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_available_size, false); -- -- if (start_addr > real_content_len) -- return _FAIL; -- -- if (true == bWrite) { -- if ((start_addr + cnts) > max_available_size) -- return _FAIL; -- rw8 = &efuse_write8; -- } else -- rw8 = &efuse_read8; -- -- Efuse_PowerSwitch(padapter, bWrite, true); -- -- // e-fuse one byte read / write -- for (i = 0; i < cnts; i++) { -- if (start_addr >= real_content_len) { -- res = _FAIL; -- break; -- } -- -- res = rw8(padapter, start_addr++, data++); -- if (_FAIL == res) break; -- } -- -- Efuse_PowerSwitch(padapter, bWrite, false); -- -- return res; --} --//------------------------------------------------------------------------------ --u16 efuse_GetMaxSize(PADAPTER padapter) --{ -- u16 max_size; -- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI , TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, (void *)&max_size, false); -- return max_size; --} --//------------------------------------------------------------------------------ --u8 efuse_GetCurrentSize(PADAPTER padapter, u16 *size) --{ -- Efuse_PowerSwitch(padapter, false, true); -- *size = Efuse_GetCurrentSize(padapter, EFUSE_WIFI, false); -- Efuse_PowerSwitch(padapter, false, false); -- -- return _SUCCESS; --} --//------------------------------------------------------------------------------ --u8 rtw_efuse_map_read(PADAPTER padapter, u16 addr, u16 cnts, u8 *data) --{ -- u16 mapLen=0; -- -- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_MAP_LEN, (void *)&mapLen, false); -- -- if ((addr + cnts) > mapLen) -- return _FAIL; -- -- Efuse_PowerSwitch(padapter, false, true); -- -- efuse_ReadEFuse(padapter, EFUSE_WIFI, addr, cnts, data, false); -- -- Efuse_PowerSwitch(padapter, false, false); -- -- return _SUCCESS; --} -- --u8 rtw_BT_efuse_map_read(PADAPTER padapter, u16 addr, u16 cnts, u8 *data) --{ -- u16 mapLen=0; -- -- EFUSE_GetEfuseDefinition(padapter, EFUSE_BT, TYPE_EFUSE_MAP_LEN, (void *)&mapLen, false); -- -- if ((addr + cnts) > mapLen) -- return _FAIL; -- -- Efuse_PowerSwitch(padapter, false, true); -- -- efuse_ReadEFuse(padapter, EFUSE_BT, addr, cnts, data, false); -- -- Efuse_PowerSwitch(padapter, false, false); -- -- return _SUCCESS; --} --//------------------------------------------------------------------------------ --u8 rtw_efuse_map_write(PADAPTER padapter, u16 addr, u16 cnts, u8 *data) --{ --#define RT_ASSERT_RET(expr) \ -- if(!(expr)) { \ -- printk( "Assertion failed! %s at ......\n", #expr); \ -- printk( " ......%s,%s,line=%d\n",__FILE__,__FUNCTION__,__LINE__); \ -- return _FAIL; \ -- } -- -- u8 offset, word_en; -- u8 *map; -- u8 newdata[PGPKT_DATA_SIZE]; -- s32 i, j, idx; -- u8 ret = _SUCCESS; -- u16 mapLen=0; -- EEPROM_EFUSE_PRIV *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter); -- HAL_DATA_TYPE *pHalData = GET_HAL_DATA(padapter); -- -- EFUSE_GetEfuseDefinition(padapter, EFUSE_WIFI, TYPE_EFUSE_MAP_LEN, (void *)&mapLen, false); -- -- if ((addr + cnts) > mapLen) -- return _FAIL; -- -- RT_ASSERT_RET(PGPKT_DATA_SIZE == 8); // have to be 8 byte alignment -- RT_ASSERT_RET((mapLen & 0x7) == 0); // have to be PGPKT_DATA_SIZE alignment for memcpy -- -- map = rtw_zmalloc(mapLen); -- if(map == NULL){ -- return _FAIL; -- } -- -- memset(map, 0xFF, mapLen); -- -- ret = rtw_efuse_map_read(padapter, 0, mapLen, map); -- if (ret == _FAIL) goto exit; -- -- Efuse_PowerSwitch(padapter, true, true); -- -- idx = 0; -- offset = (addr >> 3); -- while (idx < cnts) -- { -- word_en = 0xF; -- j = (addr + idx) & 0x7; -- memcpy(newdata, &map[offset << 3], PGPKT_DATA_SIZE); -- for (i = j; i> 1); -- newdata[i] = data[idx]; -- -- if( addr + idx == 0x8) -- { -- if (IS_C_CUT(pHalData->VersionID) || IS_B_CUT(pHalData->VersionID)) -- { -- if(pEEPROM->adjuseVoltageVal == 6) -- { -- newdata[i] = map[addr + idx]; -- DBG_8192C(" %s ,\n adjuseVoltageVal = %d ,newdata[%d] = %x \n",__func__,pEEPROM->adjuseVoltageVal,i,newdata[i]); -- } -- } -- } -- } -- } -- -- if (word_en != 0xF) { -- ret = Efuse_PgPacketWrite(padapter, offset, word_en, newdata, false); -- DBG_871X("offset=%x \n",offset); -- DBG_871X("word_en=%x \n",word_en); -- -- for(i=0;i mapLen) -- return _FAIL; -- -- RT_ASSERT_RET(PGPKT_DATA_SIZE == 8); // have to be 8 byte alignment -- RT_ASSERT_RET((mapLen & 0x7) == 0); // have to be PGPKT_DATA_SIZE alignment for memcpy -- -- map = rtw_zmalloc(mapLen); -- if(map == NULL){ -- return _FAIL; -- } -- -- ret = rtw_BT_efuse_map_read(padapter, 0, mapLen, map); -- if (ret == _FAIL) goto exit; -- DBG_871X("OFFSET\tVALUE(hex)\n"); -- for (i=0; i<1024; i+=16) // set 512 because the iwpriv's extra size have limit 0x7FF -- { -- DBG_871X("0x%03x\t", i); -- for (j=0; j<8; j++) { -- DBG_871X("%02X ", map[i+j]); -- } -- DBG_871X("\t"); -- for (; j<16; j++) { -- DBG_871X("%02X ", map[i+j]); -- } -- DBG_871X("\n"); -- } -- DBG_871X("\n"); -- Efuse_PowerSwitch(padapter, true, true); -- -- idx = 0; -- offset = (addr >> 3); -- while (idx < cnts) -- { -- word_en = 0xF; -- j = (addr + idx) & 0x7; -- memcpy(newdata, &map[offset << 3], PGPKT_DATA_SIZE); -- for (i = j; i> 1); -- newdata[i] = data[idx]; -- } -- } -- -- if (word_en != 0xF) { -- DBG_871X("offset=%x \n",offset); -- DBG_871X("word_en=%x \n",word_en); -- DBG_871X("%s: data=", __FUNCTION__); -- for(i=0;i