pax_global_header00006660000000000000000000000064122573155050014517gustar00rootroot0000000000000052 comment=7ae2074f1d7e5f4163efcae1bda58dcbc623f9d7 ndiswrapper-1.59/000077500000000000000000000000001225731550500140135ustar00rootroot00000000000000ndiswrapper-1.59/AUTHORS000066400000000000000000000001641225731550500150640ustar00rootroot00000000000000Pontus Fuchs - Main developer. Giridhar Pemmasani - Main developer. Various - Misc minor patches. ndiswrapper-1.59/ChangeLog000066400000000000000000000701711225731550500155730ustar00rootroot00000000000000Version 1.59 2013-11-28 ======================= * Support for Linux kernels from 2.6.13 to 3.12 * 32-bit userspace on 64-bit kernel is supported now * Fixed kernel hang if loadndisdriver exits with an error Version 1.58 2013-02-19 ======================= * Added support for Linux kernels up to 3.8 Version 1.57 2011-12-31 ======================= * Added support for Linux 2.6.35 - Linux 3.1 * Kernels as old as 2.6.14 are now supported * IW_AUTH_MFP implemented * Reserve stack before copying arguments when calling from Windows to Linux on x86_64 * Fixed memory corruption on module unload seen with Atheros and Marvell USB devices * Fixed a kernel panic if there are many devices for one driver and one fails to initialize * Fixed support for AVM Fritz USB * Loading drivers with unknown symbols is disallowed, it's a certain crash * Fixed some memory leaks * Memory usage information has been improved * Fixed procfs registration if the network device is renamed * Added more function stubs * Build system improved * Code cleanup, fixed many warnings, style issues and typos Version 1.56 2010-02-10 ======================= * Fixed compilation with 2.6.31 and newer on i386 * Use /etc/modprobe.d/ndiswrapper.conf, not /etc/modprobe.d/ndiswrapper. Version 1.55 2009-06-28 ======================= * Fixed compilation issues with recent kernels Version 1.54 2009-01-21 ======================= * Fixed buffer overflows with nickname * Compilation issues with 2.6.27 and 2.6.28 fixed Version 1.53 2008-05-17 ======================= * Implemented va_list conversion for x86_64, which fixes oops in vsprintf() and vsnprintf(). * Fixed oops on unload if using our workqueue implementation with SMP enabled. * Don't change the actual thread priority, just pretend it was changed. * Implemented format string conversion for x86_64, so that Windows long is mapped to Linux int. * Fixed most sparse warnings. * Simplified code and build system to remove already broken support for Linux versions prior to 2.6.16. * Added .size and .type for all functions in win2lin_stubs.S to improve backtrace on x86_64. * Fixed rx key authentication sequence number conversion from Windows to Linux so WPA authentication doesn't sometimes go into re-key auth loop. Version 1.52 2008-02-02 ======================= * Fixed kernel crash when CONFIG_DEBUG_SHIRQ is enabled * Compilation issues with current 2.6.25 kernel tree fixed Version 1.51 2007-12-17 ======================= * Fixed an SMP issue that may cause ndiswrapper to stop transmitting packets after a while (noticed with Marvell Pre-N USB driver) Version 1.50 2007-11-27 ======================= * Added support for 2.6.24-rcX kernels * Fixed issue with changing mac address (with 'ifconfig hw ether ') - its broken since 1.45-rc2. Now one can also edit appropriate .conf file to set the NetworkAddress setting to whatever mac address should be used by the driver (e.g., NetworkAddress|0123456789ab) * Fixed kernel crash observed with mrv8335 in ad-hoc mode Version 1.49 2007-10-27 ======================= * If a driver returns invalid MAC address (00:00:00:00:00) when queried with OID_802_3_CURRENT_ADDRESS (probably because NdisReadNetworkAddress returns NDIS_STATUS_FAILURE?), use OID_802_3_PERMANENT_ADDRESS to get the correct address. This is required for some Atheros devices (e.g., AR5007EG) * Fixed a kernel crash with non-preempt kernels (observed with acx111 driver) * Preemption, as required by Windows, has been reimplemented. With this, ndiswrapper works with RT-preempt and non-preempt kernels * Dropped support for 2.4 kernels (and very old 2.6 kernels) Version 1.48 2007-09-19 ======================= * Support for 2.6.23 kernel * Disassociation with wpa_supplicant is fixed (earlier, after disassociation, the driver and ndiswrapper would be configured differently, requiring unloading and reloading of ndiswrapper module) * (Re)Association / (Re)Authentication issues with wpa_supplicant with 2.6.23 kernels fixed * Fixed an issue with 64-bit Broadcom drivers that cause ntos_wq worker thread to take up all the CPU in some cases Version 1.47 2007-06-12 ======================= * Fixed random (occasional) crash issues with 64-bit drivers (observed with Broadcom driver) * Fixed compilation issues with version 1.46 Version 1.46 2007-06-03 ======================= * Fixed crash with large transfers (bug in version 1.45) Version 1.45 2007-05-28 ======================= * Fixes 'NOHZ: local_softirq_pending: 08' warnings from tickless kernels * Fixed issue(s) with RT-preempt enabled kernels * Fixed ndiswrapper script to handle installation of Intel 4965AGN driver (which apparently works with ndiswrapper) * Fixed a warning when SLUB allocator is used * Fixed kernel crash with PsCreateSystemThread that manifested with sis163u when preempt is enabled * Changing MAC address with 'ifconfig hw ether' should work with all drivers now. If MAC address should be changed every time ndiswrapper is loaded, change 'NetworkAddress' setting, as explained in wiki entry 'Tips' (earlier 'mac_address' setting was used, but that no longer applies) * Fixed an issue with some drivers (e.g., sis163u) that would stall after working for a while * Fixed crashes when removing module with USB devices Version 1.44 2007-05-17 ======================= * Fixed issues with Ubuntu kernels (where PREEMPT is not enabled) * Issues with combination of DEBUG_PREEMPT and RT-preempt fixed * Fixed issues with newer 64-bit Broadcom driver when more than 1GB RAM is present * Added support for upcoming 2.6.22 kernels Version 1.43 2007-04-30 ======================= * Fixed long standing memory allocation issues with some drivers, Atheros especially. With this fix, Atheros cards in MacBook with Core 2 Duo are known to work * The above fix changes core architecture of ndiswrapper and improves memory allocation issues with all drivers, as well as executes mostly with bottom-half's enabled for better system response and performance. Many drivers have been tested to work with these changes; however, some drivers may have been broken * Added support for Marvell Gigabit ethernet drivers (apparently in-kernel sky2 driver has some issues) Version 1.42 2007-04-16 ======================= * Yet another SMP fix * Bug fix related to events (no driver seems to be affected by this) Version 1.41 2007-03-31 ======================= * Fixed soft-lockup issue when multiple network devices are used * Fixed a bug when unloading driver * Fixed issues with recent RT-preempt patch Version 1.40 2007-03-29 ======================= * The workaround for large memory allocations (needed by Atheros drivers) with 2.6.19+ kernels, introduced in version 1.33, causes soft-lockup with SMP. This is now fixed. Version 1.39 2007-03-23 ======================= * An SMP issue is fixed * When initializing a card that supports 802.11a and either 802.11g or b, set the mode to 802.11g or b, as 802.11a is far less common * Added support for driver for Atheros device with PCI ID 168C:1014 * Fixed issue with initializing athfmwdl driver (for Atheros USB cards) Version 1.38 2007-02-28 ======================= * Fixed ndiswrapper script so it installs drivers for certain PCMCIA cards (which have bus type 8, instead of 5) * Fixed compilation issues with 2.4 kernels Version 1.37 2007-02-02 ======================= * This time, really fixed the ndiswrapper script for sis163u driver (wrong version was packaged with 1.36, although issue was fixed in svn a few days ago) Version 1.36 2007-02-01 ======================= * Fixed ndiswrapper script (broken since 1.23 for sis163u driver) * Implemented full scatter/gather lists (not tested) * Implemented support for hardware TCP/IP checksumming (through NDIS) Version 1.35 2007-01-28 ======================= * Fixed a bug with scatter/gather. With this rt61 from RaLink and VT6655 from VIA drivers transmit data. Other drivers that associated but didn't transmit data (e.g., RT8855, Airgo's Gen3 PCI with ID 17CB:0002) may also work. Version 1.34 2007-01-08 ======================= * Provide workaround for large memory allocations in interrupt context with 2.6.19+ kernels. With this, Atheros (and other?) drivers don't need kernel to be patched. Version 1.33 2006-12-28 ======================= * Allow large memory allocations in older kernels; with 2.6.19 and newer kernels, vmalloc in atomic allocations is not allowed (see 'patches' section on project page for workaround on these newer kernels). Version 1.32 2006-12-23 ======================= * Added compatibility with 2.6.20-rc1 kernel * Fixed crash when changing MAC address (with 'ifconfig hw ether') * Fixed crash when names of other network device are changed (e.g., by udev) Version 1.31 2006-12-05 ======================= * Fixed crashes with shutting down computer without unloading ndiswrapper module. * Added support for Davicom 9601 USB ethernet device. * Fixed ndiswrapper script to show correct device IDs. Version 1.30 2006-11-28 ======================= * Yet another fix to ndiswrapper script (to handle file names with upper case extensions). Version 1.29 2006-11-23 ======================= * Fixed ndiswrapper script to handle INF files properly. * Added support for Atheros 5416 (802.11n) chipset. Version 1.28 2006-10-29 ======================= * Windows drivers may request firmware to be loaded in either upper or lower case (either full name or partially). Instead of using given name, use installed name. This fixes firmware loading issue with Airgo networks driver. * When compiling with 2.6.19-rc2 and possibly next rc(s), compile with 'make USE_OWN_WQ=1'. Final 2.6.19 may not need this (hopefully). Version 1.27 2006-10-21 ======================= * Bug fix to drivers that load binary firmware files (broken in 1.26). This fixes issues with Atheros and TI drivers. * Atheros PCI driver requests more map registers for DMA than limit specified in NDIS. Instead of failing such invalid requests, continue. Version 1.26 2006-10-14 ======================= * Removed debug flag in ndiswrapper script * When HIGHMEM is enabled, allocate and copy URB buffers only if necessary. This prevents memory allocation failures. Version 1.25 2006-10-04 ======================= * Support for WE-21 * Bug fixes Version 1.24 2006-10-01 ======================= * Compilation issues with 2.4 fixed (yet again!) * Devices are recognized when they are inserted, instead of registering when module is loaded. With this, new Windows drivers can be installed / updated without unloading ndiswrapper module, and any number of Windows drivers can be installed without overhead. * Minor tweaks and optimizations * Fixed issues with RT-Preempt patch * 64-bit RT2500 USB driver works Version 1.23 2006-08-10 ======================= * Bug fixes to recent changes in 64-bit driver support. * ZyDas ZD1211 driver uses interrupt-out URBs, so set them up properly. * Bug fixes to Atheros USB driver support. Version 1.22 2006-08-05 ======================= * Added support for Broadcom 802.11n (draft) driver * Added support for 64-bit Marvell driver * Optimizations for 64-bit drivers * If network interface name changes (through udev, ifrename etc), ndiswrapper notices it and changes entry in procfs * Compilation issue with 2.4 issue fixed Version 1.21 2006-07-15 ======================= * Fix to a bug (in 1.20) that locked up when used with RT2500 with SMP. * RT2500 is supported with 64-bit. Version 1.20 2006-07-15 ======================= * Fixes to 64-bit drivers; TI 1450 (used in AVM Fritz) is supported with 64-bit. * Fix to SMP kernel crash when USB device is unplugged. Version 1.19 2006-07-02 ======================= * Calls to Miniport functions with serialized drivers (such as RT2500) are serialized, so they should work with SMP. * Enable interrupts in IRQ handler; otherwise, some drivers (e.g., Marvell 8335) don't work. * Kernel crash with changing mac address (with 'ifconfig hw ether ...') fixed. Version 1.18 2006-06-22 ======================= * Kernel crash with RT2500 (and possibly other serialized drivers) under heavy traffic load fixed. * Support for RNDIS driver with Vista drivers for 64-bit added. Version 1.17 2006-06-04 ======================= * netpoll support added; with this netconsole can be used with ndiswrapper. * Suspend/resume improved: Until now devices are halted (which is similar to removing the device) during suspend and initialized during resume. Now, if a driver supports suspend and resume, that is used instead. With this, any wireless settings, such as encryption, may be preserved by the driver. * Support for RNDIS was broken in 1.16; they work now. Version 1.16 2006-05-06 ======================= * Compilation issues 2.4 kernels fixed. * Support for lck patchset (Con Kolivas's preempt patch) for 2.4 kernels added. * Added workqueue functions for 2.4 kernels so drivers that need more than one workqueue work. * Added support for more USB devices with 2.4 kernels (so far, devices which use interrupt URBs, such as ZyDas ZD1211, didn't work; now some of them do). Version 1.15 2006-04-26 ======================= * Memory allocation tracking support added; this should help find any memory leaks (with ALLOC_INFO and ALLOC_DEBUG options to 'make'). * Proper fix for drivers (Atheros PCI and USB drivers) allocating large chunk of memory in atomic context. * Fix for crash when module is removed with new Atheros USB driver, which supports WPA2 and has no firmware driver (athfmwdl). * Memory leaks plugged. * Wireless statistics are enabled (inadvertently disabled in version 1.14). * Suspend/resume issues fixed - all devices, including USB devices, should work with suspend/resume. * Plugging/unplugging/rmmod issues fixed; Marvell devices don't need to be unplugged before removing module. Version 1.14 2006-04-17 ======================= * Added support for Xpeed X400 ADSL adapter. * Only wireless devices will be registered as wireless devices to kernel; others (e.g., X400 ADSL) will be registered as network devices, by default, with wlan%d. * Issues with multicast fixed. * Issues with ZyDas ZD1211 fixed (broken since 1.9 or so). * Fixed memory corruption issues, discovered with CONFIG_DEBUG_SLAB. Version 1.13 2006-04-08 ======================= * Compilation issues with older kernels fixed. * SMP issues fixed; Netgear WG111 USB (Prism54 chipset) works with SMP. * Association to open networks (without encryption) is supported with 'wext' driver interface with wpa_supplicant. * Workaround is provided for Atheros driver so large block of memory allocated by this driver in atomic context succeed. Version 1.12 2006-04-04 ====================== * Sis163 USB driver with version 1.11 failed to associate reliably. This is fixed. * TI 1450 USB driver failed to initialize card if module is reloaded. This is fixed. * WPA with TKIP with TI ACX100 driver now works with 'ap_scan=2' in wpa_supplicant's configuration file. Driver for D-Link DWL-650+ has support for WPA with TKIP for this chipset. * Added support for non-encrypted networks with wpa_supplicant. * Am1772 with WPA and TKIP authenticates reliably now. * Compilation issue with 2.4 kernels fixed. * Added support for WEP with wpa_supplicant through 'wext' driver interface. There is no need for 'ndiswrapper' interface with wpa_supplicant anymore. * Fixed an issue with 64-bit USB drivers. No 64-bit USB driver is known to work, though. Version 1.11 2006-03-25 ====================== * Added support for newer RNDIS (USB) cards (e.g., USR5421, F5D7051uk, WUSB54GSv2, all based on Broadcom chip). These support WPA2, whereas older RNDIS cards support only WPA. * Kernel crash when removing USB cards fixed. * Fixed performance issues with SMP. * Added support for AMD Am1772. Version 1.10 2006-02-11 ====================== * Added support for 64-bit USB drivers. Not tested with any driver, though. * Bug fixes. Version 1.9 2006-02-03 ====================== * Added support for real-time preempt (RT) patch. * Added support for TNETW1450 (TI's USB chipset). * Added support for latest Windows Broadcom driver. * Added support for Airgo Networks MIMO Pre-N driver. * Added support for Intel PRO/Wireless 3945ABG driver; this driver needs 16KB stacks in kernel. * Bug fixes. Version 1.8 2006-01-16 ====================== * Issue with WEP with wpa_supplicant fixed (keys must be saved and restored). * Support for newer kernels added. * If USB support is not required, passing 'DISABLE_USB=1' option to 'make' will not include USB layer (introduced in version 1.5, but broken in at least 1.7). * Crash when removing ndiswrapper module with certain Windows drivers fixed. Version 1.7 2005-12-07 ====================== * Support for Atheros USB drivers natively (without the need for load_fw_ar5523 user space firmware layer introduced in 1.6 release) added. * Drivers and driver files are dynamically loaded and unloaded as and when necessary; this should save memory. * Bug fixes / improvements. Version 1.6 2005-11-29 ====================== * Support for Highmem with USB devices added. * Bulk and interrupt URBs were being mixed up in earlier releases - this issue is fixed. * Crashes due to 'divide by zero' error in update_wireless_stats fixed. * Added support for Atheros USB drivers including RangeMAX MIMO. This requires loading ar5523.bin firmware file with user-space tool load_fw_ar5523 in utils. * Suspend and resume support improved; USB devices should now be able to suspend and resume. * Bug fixes. Version 1.5 2005-10-30 ====================== * SMP issues fixed. * Zero-copy for sending packets implemented. * It is possible to compile ndiswrapper even if USB is not enabled in kernel, without support for USB devices. * Even if USB is enabled in kernel, ndiswrapper can be compiled with 'make DISABLE_USB=1' to disable support for USB devices. * If a device doesn't resume properly (from suspend), de-initialize device so it can be initialized later. This fixes issue with suspend/resume with Marvell driver. * Issues with WUSB11v4 (Ali chipset) fixed. * Rtl string functions have been reimplemented. * Resume from suspend works with Marvell chipset 8335. Version 1.4 2005-10-06 ====================== * WPA2 support added. * Implemented most of IO manager, PnP manager and Obj manager. * No need to update utils every time ndiswrapper is updated but only when utils interface changes. * USB layer has been rewritten with many fixes; e.g., USB devices no longer disconnect unexpectedly. * New devices supported: sis162, sis163, ZyDas 1211, Linksys WUSB11v4 (with Ali chipset). These are USB devices. * Unplugging USB devices without removing ndiswrapper module supported. * USB devices work with PREEMPT enabled. * Separate workqueue is used in 2.6 kernels; this should prevent locking up keyboard etc. in case of kernel crash. * Support for new kernels added. * Many bug fixes. * Added support for Inprocomm IPN2220 with 64-bit. * Added support for RNDIS USB devices (tested with U.S. Robotics USR5420). * Issue with unloading module with some drivers (e.g., sis and Inprocomm ipn2220) fixed. * Issue with WPA with sis162u, sis163u and ZyDas zd1211 fixed. * Initialization failure with WUSB54v1 fixed. Version 1.2 2005-06-10 ====================== * Support for NX (no-execute) bit * 64-bit Broadcom driver works with HIGHMEM. * Implemented I/O manager and Object layers. * Many bug fixes Version 1.1 2005-03-04 ====================== * Free kmem_cache of MDLs; this fixes crashes when removing module. * Yet another spinlock (re)implementation. We don't use Linux spinlocks to implement Windows spinlocks anymore - ndiswrapper implements new spinlock functions that use Windows spinlock variables directly. * Use kmem_cache pools for MDL (aka NDIS_BUFFER) and NDIS_PACKET structures which are allocated often. This should reduce memory fragmentation. * Use MDL functions when initializing ndis_packet while sending packets. This fixes crashes with Fedora kernels (and amd64 driver at least). * Memory leak with 64-bit Broadcom driver fixed * Crash when module is removed with Marvell driver fixed * In some cases, a Windows driver loads successfully, but ndiswrapper concludes otherwise. This is fixed. * Don't use spinlocks unless CONFIG_SMP or CONFIG_DEBUG_SPINLOCK is enabled. * Don't reset Centrino cards during initialization as this seems to turns off radio. * 2.4 kernels don't have hashing, so provide wrapper macros for hashing. * Set the active/transmit key index as given, instead of assuming 1 always. This fixes authentication with Cisco routers. This should also fix WEP issues when active key used on the router is at index > 1. * NdisAcquire(Dpr)SpinLock obtains the lock at DISPATCH_LEVEL instead of PASSIVE_LEVEL. This fixes PREEMPT and SMP issues with some drivers. * Issue with Prism54 driver sometimes crashing kernel when scanning fixed. * Added private ioctl to set the network type to a specific type. Use "iwpriv wlan0 network_type " to set the network type to what represents, where is one of a, b, g or any other character. For example, to set to 802.11b, use "iwpriv wlan0 network_type b", and to set to auto, "iwpriv wlan0 network_type x". Version 1.0 2005-01-29 ====================== * Yet another spinlock reimplementation. Since I don't have SMP, it is taking lot of time (and effort) to get it working. * With new spinlock implementation, CONFIG_DEBUG_SPINLOCK option can be enabled in the kernel. * WavePlus 1200 chipset supported. * Works with some vendor kernels that use different semantics (from vanilla kernels) for PCI/USB register functions. * Don't free memory allocated for 'devices' twice (which was causing kernel crash) when device registration failed. * Don't set infrastructure mode in wpa_associate, which clears the keys. With this, WEP should work again with wpa_supplicant. * Fix (small) memory leak - free xmit_array when device is removed. * Use spinlock when calling any miniport function. This may fix some SMP issues. * Preliminary support for AMD64 (tested with Broadcom chipset) * Dynamic loader support: A driver is loaded only when necessary, so even if many drivers are installed or if a driver installs many .conf files (e.g., Centrino), only drivers and device configurations necessary are loaded. * Set RadioState used by Broadcom drivers to 0, instead of 1. This turns on radio (although name suggests the other way round). * Bug fixes * Workaround for CONFIG_DEBUG_SPINLOCK, but this option is discouraged * task_nice is not used anymore, so should compile with kernels where this is not exported * More spinlock correction. Some drivers are reported to work with SMP. Not all drivers may work (at least Prism54 USB driver seems to be buggy) * Spinlock rewrite: This time SMP should work. Really. * Sends multiple packets (if driver supports it) in each send operation for better efficiency * Most of the functionality of loader is moved to userspace tool loadndisdriver so it is more robust * Encryption info is restored during disassociation; this is useful if ad-hoc mode is used as pseudo master mode * MAC address can be set in .conf files (in /etc/ndiswrapper/ directory) or changed at any time (with 'ifconfig hw class' command) * Association and disassociation should happen quickly especially with wpa_supplicant * Compilation issues with old gcc and sed are fixed * Kernel crash when ejecting cards with cardctl in 2.4 kernels is fixed Version 0.12 2004-11-25 ======================= * Works with Preempt and SMP kernels. * WPA-RADIUS is supported. * Compiles with kernels > 2.6.9 and swsuspend2 * USB cards are reset during initialization; they should work without having to reset explicitly * Some crashes in USB, especially during initialization, are fixed. * WEP key setting problems fixed. * Check for 4K stack size during compilation and issue warning if necessary. * Bug fixes Version 0.11 2004-10-08 ======================= * Compilation issues with USB are fixed. * WPA issues fixed. * Older Prism USB devices (e.g., D-Link DWL-122, Netgear MA111) supported. * Suspend/resume of devices with no power management (e.g., TI) fixed. * ndiswrapper script has an option "-d" to install drivers for a different device (if the chipset is same); the script also installs drivers properly when the CopyFiles section has commented out file names. * ndiswrapper script lists USB devices and reports status of drivers with more understandable messages. * Many bug fixes and improvements Version 0.10 2004-08-20 ======================= * Centrino 2200: works in ad-hoc mode, keyboard doesn't lock even when there is no power to the card, suspend/resume works, restricted mode encryption works * SMP should work again * USB cards are supported (only Linksys WUSB54G tested with 2.6.7 kernel) * From now on, utils should be installed along with the module - newer version of module doesn't work with older version of utils * hangcheck_interval option is no longer required; this option may be dropped in future versions * suspend/resume work with procfs interface: e.g., to suspend echo suspend=3 > /proc/net/ndiswrapper/wlan0/settings and to resume echo resume > /proc/net/ndiswrapper/wlan0/settings Version 0.9 2004-08-05 ====================== * Wi-Fi Protected Access (WPA) is supported with both AES/CCMP and TKIP encryption * Debian packages can be built with 'make deb'; RPMs can be built with 'make rpm' * Support for RT2400/RT2500 chipset * ndiswrapper script can be used to install INF files in Unicode * Private ioctl to change power profile to AC/Battery * Many bug fixes Version 0.8 2004-06-10 ====================== * Support for 802.1x (multiple WEP keys). * Support for more chipsets/cards (DWL-G630, Marvell W8100, Atheros 5211, Atheros 5212). * Many bug fixes (scanning, DMA aka shared memory allocation, timers, IRQs, spinlocks, memory allocation, serialized drivers). * Added hangcheck_interval module option to either disable it (some Realtek drivers seem to have problems with it) or force it to any given value. Version 0.7 2004-04-23 ====================== * Power down while unloading the module, so it saves trees! * Fixed a bug with scanning; earlier it would fail sometimes. * procfs exposes multiple cards, if present. * Fixed problems with unloading of module in some cases. * Realtek 8180L is supported. * Support for more cards/chips (see "Supported Chipsets" page for details). * Many bug fixes! Version 0.6 2004-03-17 ====================== * Support for kernels with CONFIG_REGPARM. * SMP problem with Centrino driver fixed. * essid problems with WEP enabled on Centrino fixed. * Compiles on 2.4 kernels with old ethtool. * Support for SMC2802 V2 card. * Support for 3Com 3CRSHPW796 card. * Support for TI AC111 card. Version 0.5 2004-03-08 ====================== * Integrated RPM packaging. * Better makefiles. * New simpler installation. * Better support for multiple driver and cards. * Compiles on some more kernels. * Suspend/resume follow new power management driver model and work with S3/S4. * Power up the card during initialization so ndiswrapper works if the card is in powered down state. * Background AP scanning is removed, so it doesn't affect IP traffic anymore. * WEP key and essid can be set in any order, not necessarily key first and essid later. * Essid is set twice with a delay in between so that it gets set properly the first time after the machine is booted. * A new util for controlling the radio for Averatec 5110HX laptop added. Version 0.4 2004-01-05 ====================== * Admtek chipset works, including WEP. * Performance issues on Centrino have been fixed. * Removing the module doesn't give oops anymore. * Power management (suspend/resume) works reliably. * procfs interface is added. * Removed unnecessary 64 bit and floating point operations. Version 0.3 2003-12-16 ====================== * Centrino now works with some drivers. * Unified makefile in driver directory for easier compiling. * SMP fixes (not 100% complete yet). * Support for Atheros AR5004 drivers. * Taint kernel when loading windows driver. Version 0.2 2003-12-04 ====================== * New inf file parser. * Access point scanning. * WEP support. * Byte statistics for ifconfig. * Kernel 2.4 support. * Support for more Broadcom cards (No Centrino yet). * Tons of bugfixes should improve stability a lot. Version 0.1 2003-11-17 ====================== * Initial release. ndiswrapper-1.59/INSTALL000066400000000000000000000052751225731550500150550ustar00rootroot00000000000000The instructions below explain how to install ndiswrapper. This is rather short version; more details about installation, troubleshooting, FAQ etc. can be found in the Wiki at http://ndiswrapper.sourceforge.net/wiki Prerequisites ============= You need a recent kernel, at least 2.6.13, with header files for the kernel. Make sure there is a link to the kernel source from the modules directory. The command ls /lib/modules/`uname -r`/build should have at least 'include' directory and '.config' file. Downloading =========== Download the latest version of the ndiswrapper sources from here and extract it with the command tar zxvf ndiswrapper-version.tar.gz This will create ndiswrapper-version directory. Change to that directory and run make uninstall make Login as root and run make install Install Windows driver ====================== If this is the first time you install ndiswrapper, you need to install Windows driver for Windows XP (in some cases Windows NT or Windows 2000 may also work). First, get a Windows driver that is known to work. See http://ndiswrapper.sourceforge.net/wiki/List for status about the device for which you are installing Windows driver. For this, you need to identify device ID with lspci -nn if it is PCI device or lsusb if it is USB device. Then lookup in that List for the device ID and if a driver is known to work, get that driver. Occasionally, Windows driver on the CD or your Windows partition may work, but if it doesn't, don't complain - get a known-to-work driver. Many Windows drivers are distributed either as zipped files or cab files. Zipped files, even if they are .exe files, can be extracted with 'unzip' in Linux; cab files can be extracted with combination of 'cabextract' and 'unshield' programs. Once the driver has been unpacked, locate .inf and .sys files. If necessary, move these files so both .inf and .sys are in the same directory. Some drivers also come with firmware files, such as fwrad16.bin etc. These files also should be in the same directory. Then install the Windows driver with ndiswrapper -i driver.inf This installs .inf file and required .sys and .bin files. Now, see if installation of Windows driver is "valid" with ndiswrapper -l This should report "driver present, hardware present" for the driver installed and if that driver is for the device that is already in the system. If device is not present, it should report "driver present" If not, the Windows driver has not been installed properly. Now load ndiswrapper module with modprobe ndiswrapper If everything worked properly, this should initialize 'wlan0' wireless device, which can be configured with wireless tools, such as 'iwconfig', 'wpa_supplicant' etc. ndiswrapper-1.59/Makefile000066400000000000000000000037111225731550500154550ustar00rootroot00000000000000DRIVER_VERSION = $(shell sed -n 's/^\#define[ \t]\+DRIVER_VERSION[ \t]\+"\([^"]\+\)"/\1/p' driver/ndiswrapper.h) UTILS_VERSION = $(shell sed -n 's/^\#define[ \t]\+UTILS_VERSION[ \t]\+"\([^"]\+\)"/\1/p' driver/ndiswrapper.h) distdir=ndiswrapper-${DRIVER_VERSION} distarchive=${distdir}.tar.gz DISTFILES=AUTHORS ChangeLog INSTALL Makefile README ndiswrapper.spec \ ndiswrapper.8 loadndisdriver.8 SUBDIRS = utils driver ifeq ($(wildcard /usr/share/man)$(wildcard /usr/man),/usr/man) mandir = /usr/man else mandir = /usr/share/man endif .PHONY: all all: $(SUBDIRS) .PHONY: $(SUBDIRS) $(SUBDIRS): $(MAKE) -C $@ .PHONY: install install: $(MAKE) -C driver install $(MAKE) -C utils install mkdir -p -m 755 $(DESTDIR)$(mandir)/man8 install -m 644 ndiswrapper.8 $(DESTDIR)$(mandir)/man8 install -m 644 loadndisdriver.8 $(DESTDIR)$(mandir)/man8 .PHONY: clean distclean clean: $(MAKE) -C driver clean $(MAKE) -C utils clean rm -f *~ rm -fr ${distdir} ${distdir}.tar.gz patch-stamp distclean: clean $(MAKE) -C driver distclean $(MAKE) -C utils distclean rm -f .\#* uninstall: rm -f $(DESTDIR)$(mandir)/man8/ndiswrapper.8 rm -f $(DESTDIR)$(mandir)/man8/loadndisdriver.8 $(MAKE) -C driver uninstall $(MAKE) -C utils uninstall dist: @rm -rf ${distdir} mkdir -p ${distdir} @for file in $(DISTFILES); do \ cp $$file $(distdir)/$$file || exit 1; \ done for subdir in $(SUBDIRS); do \ if test "$$subdir" = .; then :; else \ test -d $(distdir)/$$subdir \ || mkdir $(distdir)/$$subdir \ || exit 1; \ fi; \ done $(MAKE) -C driver distdir=../${distdir}/driver dist $(MAKE) -C utils distdir=../${distdir}/utils dist # Update version in dist rpm spec file sed "s/\%define\s\+ndiswrapper_version\s\+[^\}]\+\}/%define ndiswrapper_version $(DRIVER_VERSION)\}/" \ ndiswrapper.spec >$(distdir)/ndiswrapper.spec tar cfz ${distarchive} ${distdir} rpm: dist ndiswrapper.spec rpmbuild -ta $(distarchive) --define="ndiswrapper_version $(DRIVER_VERSION)" ndiswrapper-1.59/README000066400000000000000000000026011225731550500146720ustar00rootroot00000000000000What is it? =========== Some vendors do not release specifications of the hardware or provide a Linux driver for their wireless network cards. This project provides a Linux kernel module that loads and runs NDIS (Windows network driver API) drivers supplied by the vendors Status ====== Many cards work; see http://ndiswrapper.sourceforge.net/wiki/index.php/List for details. Installation ============ See INSTALL for a short version of installation instructions. More up-to-date instructions, as well as FAQ etc., can be found at http://ndiswrapper.sourceforge.net/wiki/index.php?Installation. Problems ======== Read documentation at http://ndiswrapper.sourceforge.net/wiki. Before sending bug reports, please check archives of mailing list and public forum available at project home page: http://ndiswrapper.sourceforge.net. Both these have search facility. Follow the instructions below when sending a bug report: * Recompile with 'make DEBUG=3' and reload ndiswrapper module. Report dmesg output. Without this step, proper feedback may not be possible. Include details about your card, chipset (reported by lspci), distribution, kernel version in the post. * Check if latest SVN works. * Use an alternate windows driver; see the link in Status section above for a driver that is known to work for your chipset. You may also find more information in the Wiki. ndiswrapper-1.59/driver/000077500000000000000000000000001225731550500153065ustar00rootroot00000000000000ndiswrapper-1.59/driver/Makefile000066400000000000000000000132311225731550500167460ustar00rootroot00000000000000# 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 ndiswrapper-1.59/driver/crt.c000066400000000000000000000256721225731550500162560ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/divdi3.c000066400000000000000000000142211225731550500166340ustar00rootroot00000000000000/* 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; } ndiswrapper-1.59/driver/hal.c000066400000000000000000000060201225731550500162140ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/iw_ndis.c000066400000000000000000001527201225731550500171150ustar00rootroot00000000000000 /* * 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 ndiswrapper-1.59/driver/iw_ndis.h000066400000000000000000000107331225731550500171170ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/lin2win.S000066400000000000000000000071431225731550500170210ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/lin2win.h000066400000000000000000000057151225731550500170510ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/loader.c000066400000000000000000000634441225731550500167330ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/loader.h000066400000000000000000000061321225731550500167270ustar00rootroot00000000000000/* * 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 */ ndiswrapper-1.59/driver/longlong.h000066400000000000000000001316231225731550500173040ustar00rootroot00000000000000/* 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 ndiswrapper-1.59/driver/mkexport.sh000077500000000000000000000016631225731550500175240ustar00rootroot00000000000000#! /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 "};" ndiswrapper-1.59/driver/mkstubs.sh000077500000000000000000000004701225731550500173360ustar00rootroot00000000000000#! /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 ndiswrapper-1.59/driver/ndis.c000066400000000000000000002411411225731550500164120ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/ndis.h000066400000000000000000001130321225731550500164140ustar00rootroot00000000000000/* * 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 */ ndiswrapper-1.59/driver/ndiswrapper.h000066400000000000000000000135321225731550500200210ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/ntoskernel.c000066400000000000000000002120761225731550500176460ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/ntoskernel.h000066400000000000000000000746371225731550500176640ustar00rootroot00000000000000/* * 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_ ndiswrapper-1.59/driver/ntoskernel_io.c000066400000000000000000000744741225731550500203450ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/pe_linker.c000066400000000000000000000364201225731550500174270ustar00rootroot00000000000000/* * 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; } ndiswrapper-1.59/driver/pe_linker.h000066400000000000000000001016161225731550500174340ustar00rootroot00000000000000/* * 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; ndiswrapper-1.59/driver/pnp.c000066400000000000000000000502001225731550500162440ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/pnp.h000066400000000000000000000023541225731550500162600ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/proc.c000066400000000000000000000371701225731550500164250ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/rtl.c000066400000000000000000000415561225731550500162660ustar00rootroot00000000000000/* * 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; } ndiswrapper-1.59/driver/usb.c000066400000000000000000001260371225731550500162540ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/usb.h000066400000000000000000000250621225731550500162550ustar00rootroot00000000000000/* * 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 */ ndiswrapper-1.59/driver/win2lin_stubs.S000066400000000000000000000170701225731550500202410ustar00rootroot00000000000000/* * 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 */ ndiswrapper-1.59/driver/winnt_types.h000066400000000000000000001221231225731550500200430ustar00rootroot00000000000000/* * 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 */ ndiswrapper-1.59/driver/workqueue.c000066400000000000000000000153601225731550500175060ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/wrapmem.c000066400000000000000000000205461225731550500171310ustar00rootroot00000000000000/* * 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; } ndiswrapper-1.59/driver/wrapmem.h000066400000000000000000000057651225731550500171440ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/wrapndis.c000066400000000000000000001775301225731550500173160ustar00rootroot00000000000000/* * 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); } ndiswrapper-1.59/driver/wrapndis.h000066400000000000000000000051451225731550500173130ustar00rootroot00000000000000/* * 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 ndiswrapper-1.59/driver/wrapper.c000066400000000000000000000055151225731550500171400ustar00rootroot00000000000000/* * 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); ndiswrapper-1.59/driver/wrapper.h000066400000000000000000000013431225731550500171400ustar00rootroot00000000000000/* * 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 */ ndiswrapper-1.59/loadndisdriver.8000066400000000000000000000015611225731550500171200ustar00rootroot00000000000000.TH LOADNDISDRIVER 8 "September 17, 2006" .SH NAME loadndisdriver \- Userspace ndis driver loader for the ndiswrapper Linux kernel module .SH SYNOPSIS .B loadndisdriver .SH DESCRIPTION \fBloadndisdriver\fP is a support program for the ndiswrapper Linux kernel module. The ndiswrapper kernel module uses it to load ndis drivers that have been installed via the \fBndiswrapper\fP(8) tool. .PP \fBloadndisdriver\fP should not be used directly, only the kernel module should use it. Therefore, it's options are volatile, and subject to change during the development of the ndiswrapper kernel module. .PP The \fI/etc/ndiswrapper\fP directory is expected to contain the ndis files that \fBloadndisdriver\fP requires. .PP .SH "SEE ALSO" \fBndiswrapper\fP(8) .SH AUTHOR This manual page was written by Kel Modderman , for the Debian project (but may be used by others). ndiswrapper-1.59/ndiswrapper.8000066400000000000000000000066031225731550500164470ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH NDISWRAPPER 8 "June 25, 2004" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME ndiswrapper \- Linux kernel module and user space tool to load and run Windows XP drivers for wireless cards .SH SYNOPSIS .B ndiswrapper .br .SH DESCRIPTION \fBndiswrapper\fP is two parts: user space tool that is used to install Windows XP drivers and kernel module to load the Windows XP drivers. Both are called .B ndiswrapper. .br .br .SH ndiswrapper - tool .br .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invoke bold face and italics, .\" respectively. The user space tool (/usr/sbin/ndiswrapper) is used whenever a new Windows XP driver is to be installed. This program takes the following options: .br .PP .B OPTIONS .TP .B \-i installs new Windows XP driver, where is full path to INF file for that driver. .TP .B \-l lists the currently installed drivers. .TP .B \-e removes an installed Windows XP driver named . .TP .B \-m writes an alias for wlan0 (default wireless device) into module configuration file so that ndiswrapper kernel module is loaded automatically when this interface is used. .PP .SH ndiswrapper - kernel module .br The kernel module loads the installed Windows drivers and executes them so that the wireless cards can be used in Linux. The module takes the following options: .PP .B OPTIONS .TP .B if_name= The default basename for the interface is wlan%d, so that the wireless cards will be configured as wlan0, wlan1, ... (%d is replaced with lowest available number). You may use any other basename or fixed name, such as eth%d, ndis2 etc. .TP .B proc_uid= The module creates files in /proc/net/ndiswrapper that provide some useful information. These files are created so that they can be read only by root by default. If some other user needs to access these files without having to login as root, then replace with the user ID of that user. .TP .B proc_gid= The module creates files in /proc/net/ndiswrapper that provide some useful information. These files are created so that they can be read only by people in root group by default. If users from other groups need to access these files, then replace with the group ID of those users. .br ndiswrapper kernel module uses loadndisdriver user space tool to load all the Windows drivers. This tool is not to be used under normal circumstances. Only when ndiswrapper module is already loaded and a new Windows XP driver is installed (by ndiswrapper user space tool) and you want the kernel module to load that driver without having to unload the module that you need to use it. .SH AUTHOR This manual page was written by Giridhar Pemmasani , for the Debian project (but may be used by others). ndiswrapper-1.59/ndiswrapper.spec000066400000000000000000000051001225731550500172210ustar00rootroot00000000000000# Define ndiswrapper_version only if it is not already defined. %{!?ndiswrapper_version: %define ndiswrapper_version 1.59} %{!?ndiswrapper_release: %define ndiswrapper_release 1} # Define kernel version if not already defined %{!?kernel: %define kernel %(uname -r)} %{!?ksrc: %define ksrc /lib/modules/%{kernel}/source} %{!?inst_dir: %define inst_dir /lib/modules/%{kernel}/misc} Summary: ndiswrapper allows you to use windows XP drivers for that WLAN card without proper Linux drivers. Name: ndiswrapper Version: %{ndiswrapper_version} Release: %{ndiswrapper_release} License: GPL Group: System Environment/Base URL: http://ndiswrapper.sourceforge.net Source: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: %{ksrc}/Makefile %description Some wireless LAN vendors refuse to release hardware specifications or drivers for their products for operating systems other than Microsoft Windows. The ndiswrapper project makes it possible to use such hardware with Linux by means of a loadable kernel module that "wraps around" NDIS (Windows network driver API) drivers. This rpm contains just the userspace tools. You will also need the kernel module rpm. %package -n kernel-module-%{name}-%{kernel} Summary: Ndiswrapper kernel module Group: System Environment/Base Requires: /boot/vmlinuz-%{kernel}, modutils Requires: %{name} = %{version}-%{release} BuildRequires: %{ksrc}/Makefile %description -n kernel-module-%{name}-%{kernel} Kernel module for ndiswrapper. %prep %setup -q %build make all KVERS=%{kernel} KSRC=%{ksrc} %install rm -rf $RPM_BUILD_ROOT make all install DESTDIR=$RPM_BUILD_ROOT INST_DIR=%{inst_dir} \ KVERS=%{kernel} KSRC=%{ksrc} sbindir=/sbin \ usrsbindir=%{_sbindir} mandir=%{_mandir} %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) /sbin/* %{_sbindir}/* %{_mandir}/man8/* %doc README AUTHORS ChangeLog INSTALL %files -n kernel-module-%{name}-%{kernel} %{inst_dir}/* %post -n kernel-module-%{name}-%{kernel} if [ "`uname -r`" = "%{kernel}" ] ; then depmod -a >/dev/null 2>&1 || : fi %postun -n kernel-module-%{name}-%{kernel} if [ "`uname -r`" = "%{kernel}" ] ; then depmod -a >/dev/null 2>&1 || : fi %changelog * Mon Jan 10 2005 David Kaplan - - Got rid of makeinstall macro as it asks for problems and use naming convention of make files. * Tue Jan 4 2005 David Kaplan - - Updated spec file so that it is closer to kernel module standard spec - Made ndiswrapper_version a configurable macro * Tue Feb 3 2004 - - Initial build. ndiswrapper-1.59/utils/000077500000000000000000000000001225731550500151535ustar00rootroot00000000000000ndiswrapper-1.59/utils/Makefile000066400000000000000000000016331225731550500166160ustar00rootroot00000000000000sbindir = /sbin usrsbindir = /usr/sbin DRIVER_DIR ?= ../driver HEADERS = $(DRIVER_DIR)/loader.h $(DRIVER_DIR)/ndiswrapper.h CC = gcc HOSTCC = $(CC) CFLAGS = -g -Wall -I$(DRIVER_DIR) DISTFILES=Makefile ndiswrapper loadndisdriver.c ndiswrapper-buginfo all: loadndisdriver loadndisdriver: loadndisdriver.c $(HEADERS) $(HOSTCC) $(CFLAGS) $(LDFLAGS) -o $@ $< clean: rm -f *~ *.o loadndisdriver distclean: clean rm -f .\#* install: all mkdir -p -m 755 $(DESTDIR)$(sbindir) mkdir -p -m 755 $(DESTDIR)$(usrsbindir) install -m 755 loadndisdriver $(DESTDIR)$(sbindir) install -m 755 ndiswrapper $(DESTDIR)$(usrsbindir) install -m 755 ndiswrapper-buginfo $(DESTDIR)$(usrsbindir) uninstall: rm -f $(DESTDIR)$(sbindir)/loadndisdriver rm -f $(DESTDIR)$(usrsbindir)/ndiswrapper rm -f $(DESTDIR)$(usrsbindir)/ndiswrapper-buginfo dist: @for file in $(DISTFILES); do \ cp $$file $(distdir)/$$file || exit 1; \ done ndiswrapper-1.59/utils/loadndisdriver.c000066400000000000000000000365231225731550500203410ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include "loader.h" #define PROG_NAME "loadndisdriver" #define SETTING_LEN (MAX_SETTING_NAME_LEN + MAX_SETTING_VALUE_LEN + 2) static const char confdir[] = DRIVER_CONFIG_DIR; static const char ioctl_file[] = "/dev/" DRIVER_NAME; static int debug; #ifndef UTILS_VERSION #error "compile this file with 'make' in the 'utils' directory only" #endif #define LOG_MSG(where, fmt, ...) \ syslog(LOG_KERN | where, "%s: %s(%d): " fmt "\n", \ PROG_NAME, __func__, __LINE__ , ## __VA_ARGS__) #define ERROR(fmt, ...) LOG_MSG(LOG_INFO, fmt, ## __VA_ARGS__) #define INFO(fmt, ...) LOG_MSG(LOG_INFO, fmt, ## __VA_ARGS__) #define DBG(fmt, ...) do { \ if (debug > 0) \ LOG_MSG(LOG_INFO, fmt, ## __VA_ARGS__); \ } while (0) #define WARN(fmt, ...) LOG_MSG(LOG_INFO, fmt, ## __VA_ARGS__) /* load .sys or .bin file */ static int load_file(char *filename, struct load_driver_file *driver_file) { int fd; size_t size; void *image = NULL; struct stat statbuf; char *file_basename = basename(filename); fd = open(filename, O_RDONLY); if (fd == -1) { ERROR("unable to open file: %s", strerror(errno)); return -EINVAL; } if (fstat(fd, &statbuf)) { ERROR("incorrect driver file '%s'", filename); close(fd); return -EINVAL; } size = statbuf.st_size; image = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (image == MAP_FAILED) { ERROR("unable to mmap driver: %s", strerror(errno)); close(fd); return -EINVAL; } strncpy(driver_file->name, file_basename, sizeof(driver_file->name)); driver_file->name[sizeof(driver_file->name)-1] = 0; driver_file->size = size; driver_file->data = image; return 0; } /* split setting into name and value pair */ static int parse_setting_line(const char *setting_line, char *setting_name, char *setting_val) { const char *s; char *val, *end; int i; /* We try to be really paranoid parsing settings */ for (s = setting_line; isspace(*s); s++) ; /* ignore comments and blank lines */ if (*s == '#' || *s == ';' || *s == '\0') return 0; val = strchr(s, '|'); end = strchr(s, '\n'); if (val == NULL || end == NULL) { ERROR("invalid setting: %s", setting_line); return -EINVAL; } for (i = 0; s != val && i < MAX_SETTING_NAME_LEN; s++, i++) setting_name[i] = *s; setting_name[i] = 0; if (*s != '|') { ERROR("invalid setting: %s", setting_line); return -EINVAL; } for (i = 0, s++; s != end && i < MAX_SETTING_VALUE_LEN ; s++, i++) setting_val[i] = *s; setting_val[i] = 0; if (*s != '\n') { ERROR("invalid setting: %s", setting_line); return -EINVAL; } DBG("Found setting: name=%s, val=\"%s\"", setting_name, setting_val); /* setting_val can be empty, but not name */ if (strlen(setting_name) == 0) { ERROR("invalid setting: \"%s\"", setting_line); return -EINVAL; } return 1; } /* read .conf file and store info in driver */ static int read_conf_file(char *conf_file_name, struct load_driver *driver) { char setting_line[SETTING_LEN]; struct stat statbuf; FILE *config; char setting_name[MAX_SETTING_NAME_LEN]; char setting_value[MAX_SETTING_VALUE_LEN]; int ret, num_settings; int vendor, device, subvendor, subdevice, bus; if (lstat(conf_file_name, &statbuf)) { ERROR("unable to open config file %s: %s", conf_file_name, strerror(errno)); return -EINVAL; } if (sscanf(conf_file_name, "%04X:%04X.%X.conf", &vendor, &device, &bus) == 3) { DBG("bus: %X", bus); } else if (sscanf(conf_file_name, "%04X:%04X:%04X:%04X.%X.conf", &vendor, &device, &subvendor, &subdevice, &bus) == 5) { DBG("bus: %X", bus); } else { ERROR("unable to parse conf file name %s", conf_file_name); return -EINVAL; } num_settings = 0; driver->num_settings = 0; config = fopen(conf_file_name, "r"); if (config == NULL) { ERROR("unable to open config file: %s", strerror(errno)); return -EINVAL; } while (fgets(setting_line, SETTING_LEN-1, config)) { struct load_device_setting *setting; setting_line[SETTING_LEN-1] = 0; ret = parse_setting_line(setting_line, setting_name, setting_value); if (ret == 0) continue; if (ret < 0) return -EINVAL; setting = &driver->settings[num_settings]; strncpy(setting->name, setting_name, MAX_SETTING_NAME_LEN); strncpy(setting->value, setting_value, MAX_SETTING_VALUE_LEN); num_settings++; if (num_settings >= MAX_DEVICE_SETTINGS) { ERROR("too many settings"); return -EINVAL; } } fclose(config); driver->num_settings = num_settings; return 0; } static int load_bin_file(int ioctl_device, char *driver_name, char *file_name) { struct load_driver_file driver_file; char lc_file_name[MAX_DRIVER_NAME_LEN]; int i; DBG("loading driver %s", driver_name); for (i = 0; file_name[i] && i < sizeof(lc_file_name); i++) lc_file_name[i] = tolower(file_name[i]); lc_file_name[i] = 0; if (chdir(confdir) || chdir(driver_name)) { ERROR("couldn't change to directory %s: %s", driver_name, strerror(errno)); return -EINVAL; } if (load_file(lc_file_name, &driver_file)) { ERROR("couldn't open file %s", file_name); return -EINVAL; } strncpy(driver_file.driver_name, driver_name, sizeof(driver_file.driver_name)); if (ioctl(ioctl_device, WRAP_IOCTL_LOAD_BIN_FILE, &driver_file)) { ERROR("couldn't upload bin file: %s", file_name); return -EINVAL; } return 0; } /* * open a windows driver and pass it to the kernel module. * returns 0: on success, -1 on error */ static int load_driver(int ioctl_device, char *driver_name, char *conf_file_name) { int i; struct dirent *dirent; struct load_driver *driver; int num_sys_files, num_bin_files; DIR *driver_dir; driver_dir = NULL; num_sys_files = 0; num_bin_files = 0; DBG("loading driver %s", driver_name); if (chdir(confdir) || chdir(driver_name)) { ERROR("couldn't change to directory %s: %s", driver_name, strerror(errno)); return -EINVAL; } driver_dir = opendir("."); if (driver_dir == NULL) { ERROR("couldn't open driver directory %s: %s", driver_name, strerror(errno)); return -EINVAL; } driver = malloc(sizeof(*driver)); if (driver == NULL) { ERROR("couldn't allocate memory for driver %s", driver_name); goto err; } memset(driver, 0, sizeof(*driver)); strncpy(driver->name, driver_name, MAX_DRIVER_NAME_LEN); if (read_conf_file(conf_file_name, driver) || driver->num_settings == 0) { ERROR("couldn't read conf file %s for driver %s", conf_file_name, driver_name); goto err; } while ((dirent = readdir(driver_dir))) { int len; struct stat statbuf; if (dirent->d_name[0] == '.') continue; if (stat(dirent->d_name, &statbuf) || !S_ISREG(statbuf.st_mode)) { ERROR("%s in %s is not a valid file: %s", dirent->d_name, driver_name, strerror(errno)); continue; } len = strlen(dirent->d_name); if (len > 4 && strcasecmp(&dirent->d_name[len-4], ".inf") == 0) continue; if (len > 5 && strcasecmp(&dirent->d_name[len-5], ".conf") == 0) continue; if (len > 4 && strcasecmp(&dirent->d_name[len-4], ".sys") == 0) { if (load_file(dirent->d_name, &driver->sys_files[num_sys_files])) { ERROR("couldn't load .sys file %s", dirent->d_name); goto err; } else num_sys_files++; } else if (len > 4 && ((strcasecmp(&dirent->d_name[len-4], ".bin") == 0) || (strcasecmp(&dirent->d_name[len-4], ".out") == 0))) { strcpy(driver->bin_files[num_bin_files].name, dirent->d_name); strcpy(driver->bin_files[num_bin_files].driver_name, driver_name); driver->bin_files[num_bin_files].size = 0; driver->bin_files[num_bin_files].data = NULL; num_bin_files++; } else ERROR("file %s is ignored", dirent->d_name); if (num_sys_files == MAX_DRIVER_PE_IMAGES) { ERROR("too many .sys files for driver %s", driver_name); goto err; } if (num_bin_files == MAX_DRIVER_BIN_FILES) { ERROR("too many .bin files for driver %s", driver_name); goto err; } } if (num_sys_files == 0) { ERROR("couldn't find valid drivers files for driver %s", driver_name); goto err; } driver->num_sys_files = num_sys_files; driver->num_bin_files = num_bin_files; strncpy(driver->conf_file_name, conf_file_name, sizeof(driver->conf_file_name)); if (ioctl(ioctl_device, WRAP_IOCTL_LOAD_DRIVER, driver)) goto err; closedir(driver_dir); DBG("driver %s loaded", driver_name); free(driver); return 0; err: if (driver_dir) closedir(driver_dir); for (i = 0; i < num_sys_files; i++) munmap(driver->sys_files[i].data, driver->sys_files[i].size); for (i = 0; i < num_bin_files; i++) munmap(driver->bin_files[i].data, driver->bin_files[i].size); ERROR("couldn't load driver %s", driver_name); free(driver); return -1; } static int get_device(char *driver_name, int vendor, int device, int subvendor, int subdevice, int bus, struct load_device *ld) { int ret; struct stat statbuf; char file[32]; DBG("%s", driver_name); ret = -1; if (chdir(driver_name)) { DBG("couldn't chdir to %s: %s", driver_name, strerror(errno)); return -EINVAL; } if ((snprintf(file, sizeof(file), "%04X:%04X:%04X:%04X.%X.conf", vendor, device, subvendor, subdevice, bus) && stat(file, &statbuf) == 0) || (bus == WRAP_USB_BUS && snprintf(file, sizeof(file), "%04X:%04X:%04X:%04X.%X.conf", vendor, device, subvendor, subdevice, WRAP_INTERNAL_BUS) && stat(file, &statbuf) == 0)) { DBG("found %s", file); ld->subvendor = subvendor; ld->subdevice = subdevice; ret = 0; } else if ((snprintf(file, sizeof(file), "%04X:%04X.%X.conf", vendor, device, bus) && stat(file, &statbuf) == 0) || (bus == WRAP_USB_BUS && snprintf(file, sizeof(file), "%04X:%04X.%X.conf", vendor, device, WRAP_INTERNAL_BUS) && stat(file, &statbuf) == 0)) { DBG("found %s", file); ld->subvendor = 0; ld->subdevice = 0; ret = 0; } if (chdir(confdir)) { ERROR("couldn't chdir to %s: %s", confdir, strerror(errno)); return -EINVAL; } if (ret) ld->vendor = 0; else { DBG("found file: %s/%s", driver_name, file); ld->vendor = vendor; ld->device = device; ld->bus = bus; strncpy(ld->driver_name, driver_name, sizeof(ld->driver_name)); strncpy(ld->conf_file_name, file, sizeof(ld->conf_file_name)); } DBG("%04x, %04x, %04x, %04x", ld->vendor, ld->device, ld->subvendor, ld->subdevice); return ret; } static int load_device(int ioctl_device, int vendor, int device, int subvendor, int subdevice, int bus) { struct dirent *dirent; DIR *dir; int res; struct load_device load_device; DBG("%04x, %04x, %04x, %04x", vendor, device, subvendor, subdevice); memset(&load_device, 0, sizeof(load_device)); if (chdir(confdir)) { ERROR("couldn't chdir to %s: %s", confdir, strerror(errno)); return -EINVAL; } dir = opendir("."); if (dir == NULL) { ERROR("directory %s is not valid: %s", confdir, strerror(errno)); return -EINVAL; } while ((dirent = readdir(dir))) { DBG("%s", dirent->d_name); if (dirent->d_name[0] == '.') continue; if (!get_device(dirent->d_name, vendor, device, subvendor, subdevice, bus, &load_device)) break; } closedir(dir); DBG("%04x, %04x, %04x, %04x", load_device.vendor, load_device.device, load_device.subvendor, load_device.subdevice); res = ioctl(ioctl_device, WRAP_IOCTL_LOAD_DEVICE, &load_device); DBG("res: %d", res); if (res) return -1; return 0; } /* * we need a device to use ioctl to communicate with wrapper module * we create a device in /dev instead of /tmp as some distributions don't * allow creation of devices in /tmp */ static int get_ioctl_device(void) { int fd, minor_dev; char line[64]; FILE *proc_misc; /* get minor device number used by wrapper driver */ proc_misc = fopen("/proc/misc", "r"); if (!proc_misc) return -1; minor_dev = -1; while (fgets(line, sizeof(line), proc_misc)) { if (strstr(line, DRIVER_NAME)) { long i = strtol(line, NULL, 10); if (i != LONG_MAX && i != LONG_MIN) { minor_dev = i; break; } } } fclose(proc_misc); if (minor_dev == -1) { ERROR("couldn't find wrapper in /proc/misc; " "is module loaded?"); return -1; } unlink(ioctl_file); if (mknod(ioctl_file, S_IFCHR | 0600, MISC_MAJOR << 8 | minor_dev)) { ERROR("couldn't create file %s: %s", ioctl_file, strerror(errno)); return -1; } fd = open(ioctl_file, O_RDONLY); unlink(ioctl_file); if (fd == -1) { ERROR("couldn't open file %s: %s", ioctl_file, strerror(errno)); return -1; } return fd; } int main(int argc, char *argv[0]) { int i, res; int ioctl_device = -1; char *cmd; openlog(PROG_NAME, LOG_PERROR | LOG_CONS, LOG_KERN | LOG_DEBUG); DBG("argc: %d", argc); if (argc == 2 && (strncmp(argv[1], "-v", 2) == 0 || strncmp(argv[1], "--v", 3) == 0)) { printf("version: %s\n", UTILS_VERSION); return 0; } if (argc < 4) { res = 1; goto out; } cmd = argv[1]; i = -1; i = atoi(argv[2]); if (i < 0) { ERROR("invalid debug value %d", i); res = 2; goto out; } else debug = i; ioctl_device = get_ioctl_device(); if (ioctl_device == -1) { ERROR("unable to open ioctl device %s", ioctl_file); res = 5; goto out; } if (atof(argv[3]) != atof(UTILS_VERSION)) { ERROR("version %s doesn't match driver version %s", UTILS_VERSION, argv[3]); res = 6; goto out; } if (strcmp(cmd, WRAP_CMD_LOAD_DEVICE) == 0) { int vendor, device, subvendor, subdevice, bus; if (argc != 9) { ERROR("incorrect usage of %s (%d)", argv[0], argc); res = 7; goto out; } if (sscanf(argv[4], "%04x", &vendor) != 1 || sscanf(argv[5], "%04x", &device) != 1 || sscanf(argv[6], "%04x", &subvendor) != 1 || sscanf(argv[7], "%04x", &subdevice) != 1 || sscanf(argv[8], "%04x", &bus) != 1) { ERROR("couldn't get device info"); res = 8; goto out; } if (load_device(ioctl_device, vendor, device, subvendor, subdevice, bus)) res = 9; else res = 0; } else if (strcmp(cmd, WRAP_CMD_LOAD_DRIVER) == 0) { /* load specific driver and conf file */ if (argc != 6) { ERROR("incorrect usage of %s (%d)", argv[0], argc); res = 11; goto out; } res = load_driver(ioctl_device, argv[4], argv[5]); } else if (strcmp(cmd, WRAP_CMD_LOAD_BIN_FILE) == 0) { /* load specific driver and conf file */ if (argc != 6) { ERROR("incorrect usage of %s (%d)", argv[0], argc); res = 12; goto out; } res = load_bin_file(ioctl_device, argv[4], argv[5]); } else { ERROR("incorrect usage of %s (%d)", argv[0], argc); res = 13; goto out; } out: if (ioctl_device != -1) close(ioctl_device); closelog(); return res; } ndiswrapper-1.59/utils/ndiswrapper000077500000000000000000000652631225731550500174530ustar00rootroot00000000000000#!/usr/bin/perl #/* #* Copyright (C) 2005-2006 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. #* #*/ use strict; use Fcntl ':mode'; use File::Basename; use File::Copy; use File::Path; use Cwd; $ENV{PATH} = "/sbin:/usr/sbin:$ENV{PATH}"; my $WRAP_PCI_BUS = 5; my $WRAP_PCMCIA_BUS = 8; my $WRAP_USB_BUS = 15; my %sections; my %parsed_sections; my $confdir = "/etc/ndiswrapper"; my $src_dir; my $driver_name; my @source_disks_files; my $modconf; my $re_dev_id = "([[:xdigit:]]{4})"; my $re_sub_dev_conf = "$re_dev_id:$re_dev_id:$re_dev_id:$re_dev_id" . "\.([[:xdigit:]]+)\.conf"; my $re_dev_conf = "$re_dev_id:$re_dev_id\.([[:xdigit:]]+)\.conf"; # fixup list for parameters. my %param_fixlist = ("EnableRadio|0" => "1", "IBSSGMode|0" => "2", "PrivacyMode|0" => "2", "MapRegisters|256" => "64", "AdhocGMode|1" => "0"); sub usage() { print "install/manage Windows drivers for ndiswrapper\n\n" . "usage: ndiswrapper OPTION\n" . "-i inffile install driver described by 'inffile'\n" . "-a devid driver use installed 'driver' for 'devid' (dangerous)\n" . "-r driver remove 'driver'\n" . "-l list installed drivers\n" . "-m write configuration for modprobe\n" . "-ma write module alias configuration for all devices\n" . "-mi write module install configuration for all devices\n" . "-v report version information\n\n" . "where 'devid' is either PCIID or USBID of the form XXXX:XXXX,\n" . "as reported by 'lspci -n' or 'lsusb' for the card\n"; } sub remove_driver { my $driver = shift; if (!rmtree("$confdir/$driver", 0, 1)) { warn "couldn't delete $confdir/$driver: $!\n"; } return 0; } sub abort { remove_driver($driver_name); exit 1; } sub check_version { my ($utils_version, $module_utils_version, $res); $res = 0; $utils_version = `loadndisdriver -v`; chomp($utils_version); $utils_version =~ s/^version: //; if (length($utils_version) eq 0) { printf "utils version is too old!\n"; $res = -1; } $module_utils_version = 0; open(MODINFO, "modinfo ndiswrapper |"); while (my $line = ) { if ($line =~ /utils_version:.*read only:\s([0-9\.]+)/) { $module_utils_version = $1; last; } } if ($module_utils_version eq 0) { printf "module version is too old!\n"; $res = -1; } elsif ($utils_version ne $module_utils_version) { printf "utils version '%s' is incompatible with utils version needed" . " by driver ('%s')!\n", $utils_version, $module_utils_version; $res = -1; } printf "utils version: '%s', utils version needed by module: '%s'\n", $utils_version, $module_utils_version; printf "module details:\n"; system("modinfo ndiswrapper | grep -E '^(version|vermagic|filename)'"); if ($res) { printf "\nYou may need to upgrade driver and/or utils to latest " . "versions available at\n" . "http://ndiswrapper.sourceforge.net\n"; } return $res; } sub install { my $inf = shift; chomp($inf); $src_dir = dirname($inf); $driver_name = lc(basename($inf)); unless ($driver_name =~ s/\.inf$//) { die "install argument must be .inf file\n"; } if (! -d $confdir) { mkdir($confdir) or die "couldn't create $confdir: $!"; } (-d "$confdir/$driver_name") and die "driver $driver_name is already installed\n"; read_sections($inf); parse_section("Strings"); parse_section("Version"); parse_source_disks_files(); mkdir("$confdir/$driver_name") or die "couldn't create $confdir/$driver_name: $!"; print "installing $driver_name ...\n"; parse_mfr(); copy_file(basename($inf), basename($inf)); create_fuzzy_conf($driver_name); return 0; } # return lines in section sub get_section { my $name = shift; foreach my $key (keys %sections) { if (lc($key) eq lc($name)) { printf DBG "section: $key\n"; return \@{$sections{$key}}; } } printf DBG "couldn't find section \"$name\"\n"; return 0; } # load inf and split into different sections. sub read_sections { my $filename = shift; open(INF, $filename) or die "couldn't open $filename: $!"; my $name = "none"; @{$sections{$name}} = (); while (my $line = ) { # convert from unicode $line =~ s/\xff\xfe//; $line =~ s/\0//g; chomp($line); $line = trim($line); next if ($line =~ /^$/); if ($line =~ /^\[(.+)\]/) { $name = $1; @{$sections{$name}} = (); } else { push(@{$sections{$name}}, $line); } } close(INF); foreach $name (keys %sections) { printf DBG "section: %s\n", $name; foreach my $line (@{$sections{$name}}) { printf DBG "%s: %s\n", $name, $line; } } } sub parse_section { my $name = shift; my $lines = get_section($name); if (!$lines) { return; } $parsed_sections{$name} = (); foreach my $line (@{$lines}) { (my $key, my $val) = parse_key_value($line); if ($key) { $val = strip_quotes($val); $parsed_sections{$name}->{$key} = $val; printf DBG "$name: %s = %s\n", $key, $val; } } } sub parse_mfr() { my $lines = get_section("Manufacturer"); $lines or die "couldn't get manufacturer section - " . "installation may be incomplete\n"; foreach my $line (@{$lines}) { my ($strkey, $val) = parse_key_value($line); if ($strkey) { my ($models, @targets) = split(",", $val); if ($models) { printf DBG "mfr: %s, %s\n", $line, $models; my $target = choose_target_os(@targets); printf DBG "target: '%s'\n", $target; parse_models($models, $target); } } } } sub parse_models { my ($models, $target) = @_; printf DBG "models: target: '%s'.'%s'\n", $models, $target; my $lines = get_target_os_section($models, $target); if (!$lines) { warn "couldn't find models section \"$models\" -\n" . "installation may be incomplete\n"; return -1; } foreach my $line (@{$lines}) { $line = del_comment($line); next if (length($line) eq 0); (my $dev_desc, my $val) = parse_key_value($line); my @fields = split(",", $val); if (@fields le 1) { printf "couldn't find install directive: %s\n", $line; next; } my $section = trim($fields[0]); my $hwid = trim($fields[1]); if ($hwid =~ /^%.+%$/) { $hwid = get_string_value($hwid); } # TODO: deal with compatible IDs as hwid? my ($bus_type, $vendor, $device, $subvendor, $subdevice) = parse_hwid($hwid); next if (!$vendor); printf DBG "models: %s, %s, %s\n", $section, $hwid, $vendor; parse_install($section, $target, $bus_type, $vendor, $device, $subvendor, $subdevice); } } sub parse_install { my ($section, $target, $bus_type, $vendor, $device, $subvendor, $subdevice) = @_; my $lines = get_target_os_section($section, $target); if (!$lines) { warn "couldn't find install section \"$section\" -\n" . "installation may be incomplete\n"; return -1; } my $filename = "$vendor:$device"; if ($subvendor) { $filename .= ":$subvendor:$subdevice" } $filename .= sprintf(".%X.conf", $bus_type); my (@addregs, @copyfiles); foreach my $line (@{$lines}) { $line =~ s/^;\s*//; $line = trim(del_comment($line)); my ($key, $val) = parse_key_value($line); my @array; if ($key) { if (lc($key) eq "addreg") { @array = split(",", $val); foreach my $reg (@array) { push @addregs, trim($reg); } } elsif (lc($key) eq "copyfiles") { printf DBG "copyfiles: %s\n", $val; @array = split(",", $val); foreach my $copy_file_dirs (@array) { my @copy_sec = split(",", $copy_file_dirs); foreach my $file (@copy_sec) { push @copyfiles, trim($file); } } } elsif (lc($key) eq "bustype") { printf DBG "bustype: %s\n", $val; $bus_type = $val; } } } open(CONF, ">$confdir/$driver_name/$filename") or die "couldn't create file $confdir/$driver_name/$filename: $!"; printf CONF "sys_files|"; foreach my $file (@copyfiles) { parse_copy_file($file); } printf CONF "\n"; my $version = get_section_value("Version", "DriverVer"); my $provider = get_section_value("Version", "Provider"); my $classguid = get_section_value("Version", "ClassGUID"); my $providerstring = trim(strip_quotes(get_string_value(trim($provider)))); $classguid =~ s/^\s*{//; $classguid =~ s/}\s*$//; printf CONF "NdisVersion|0x50001\n"; printf CONF "Environment|1\n"; printf CONF "class_guid|%s\n", $classguid; printf CONF "driver_version|%s,%s\n", $providerstring, $version; printf CONF "BusType|%s\n", $bus_type; printf CONF "SlotNumber|01\n"; printf CONF "NetCfgInstanceId|{28022A01-1234-5678-ABCDE-123813291A00}\n"; printf CONF "\n"; close(CONF); open(CONF, "|sort|uniq >>$confdir/$driver_name/$filename") or die "couldn't create file $confdir/$driver_name/$filename: $!"; foreach my $reg (@addregs) { parse_registry($reg); } close(CONF); } sub parse_registry { my ($reg, $conf) = @_; my $lines = get_section($reg); if (!$lines) { warn "couldn't find section \"$reg\" -\n" . "installation may be incomplete\n"; return -1; } my $driver_desc = 0; foreach my $line (@{$lines}) { $line = del_comment($line); my @fields = split(",", $line); next if (@fields lt 4); my $value; my $param = trim($fields[1]); if ($param =~ /^ndi\\/i) { if ($param =~ /^ndi\\params\\(.+)/i) { $param = strip_quotes(trim($1)); $param =~ s/\\.*$//; next if (lc(trim($fields[2])) ne "default"); $value = strip_quotes(trim($fields[4])); } else { printf DBG "ignoring parameter $line\n"; next; } } else { $param = strip_quotes(trim($fields[2])); next if (length($param) eq 0); $value = strip_quotes(trim($fields[4])); } $value = get_string_value($value); if (length($param) gt 0) { if ($param_fixlist{"$param|$value"}) { my $orig_value = $value; $value = $param_fixlist{"$param|$value"}; printf "forcing parameter $param from $orig_value to $value\n"; } printf CONF "%s|%s\n", $param, $value; if ($param =~ /^DriverDesc$/) { $driver_desc = 1; } } } if ($driver_desc == 0) { printf CONF "DriverDesc|NDIS Network Adapter\n"; } } sub parse_copy_file { my $copy_name = shift; if ($copy_name =~ /^\@/) { $copy_name =~ s/^\@//; $copy_name = trim($copy_name); if (valid_copy_file_name($copy_name)) { return copy_file($copy_name, $copy_name); } } my $lines = get_section($copy_name); if (!$lines) { warn "couldn't find section \"$copy_name\" -\n" . "installation may be incomplete\n"; return -1; } foreach my $line (@{$lines}) { $line = trim($line); # some inf files have file names commented out; get file names from them $line =~ s/^\s*;//; my @files = split(",", $line); if (@files == 0) { printf DBG "copyfiles section $copy_name has no files\n"; return -1; } my $src, my $dst; if (@files > 1 and length(trim($files[1])) > 0) { $src = $files[1]; if (length(trim($files[0])) > 0) { $dst = $files[0]; } else { $dst = $src; } } else { $src = $files[0]; $dst = $src; } $src =~ s/^.*\\//; $dst =~ s/^.*\\//; printf DBG "src: '%s', dst: '%s'\n", $src, $dst; $src = trim(del_comment($src)); next if (length($src) eq 0); if (valid_copy_file_name($src)) { $dst = trim(del_comment($dst)); printf DBG "src: '%s', dst: '%s'\n", $src, $dst; copy_file($src, $dst); } else { printf DBG "invalid file '%s' ignored\n", $src; } } return 0; } sub parse_hwid { my $hwid = uc(shift); if ($hwid =~ /(PCI\\)?VEN_(\w+)&DEV_(\w+)&SUBSYS_(\w{4})(\S{4})/) { return ($WRAP_PCI_BUS, $2, $3, $4, $5); } elsif ($hwid =~ /(PCI\\)?VEN_(\w+)&DEV_(\w+)/) { return ($WRAP_PCI_BUS, $2, $3, 0, 0); } elsif ($hwid =~ /(USB\\)?VID_(\w+)&PID_(\w+)/) { return ($WRAP_USB_BUS, $2, $3, 0, 0); } else { return 0; } } sub parse_key_value { my $line = shift; $line = del_comment($line); if ($line =~ /([^=]+)=(.+)/) { return (trim($1), trim($2)); } else { return 0; } } sub choose_target_os { my @targets = @_; my $arch = `uname -m`; chomp($arch); printf DBG "arch: %s\n", $arch; if ($arch =~ /64$/) { $arch = "amd64"; } else { $arch = "x86"; } printf DBG "arch: %s\n", $arch; my @prefs = ("NT($arch)\.5\.1", "NT($arch)\.5", "NT($arch)", "NT\.5\.1", "NT\.5", "NT"); foreach my $pref (@prefs) { foreach my $target (@targets) { $target = trim($target); printf DBG "target: '%s', pref: '%s'\n", $target, $pref; if ($target =~ /NT((amd64)|(x86))/i) { printf DBG "target arch: '%s'\n", $1; next if ($1 !~ /$arch/i); } if ($target =~ /$pref/i) { return $target; } } } return ""; } sub get_target_os_section { my ($section, $target) = @_; my $lines; chomp($section); $section =~ s/^\s*"\s*//; $section =~ s/\s*"\s*$//; printf DBG "section: \"%s\", target: \"%s\"\n", $section, $target; if (length($target) gt 0) { $lines = get_section($section . "." . $target); return $lines if ($lines); } my $arch = `uname -m`; chomp($arch); printf DBG "arch: %s\n", $arch; if ($arch =~ /64$/) { $arch = "AMD64"; } else { $arch = "X86"; } printf DBG "arch: %s\n", $arch; my @prefs = ("NT$arch.5.1", "NT$arch.5", "NT$arch", "NT.5.1", "NT.5", "NT"); foreach my $pref (@prefs) { $lines = get_section($section . "." . $pref); return $lines if ($lines); } $lines = get_section($section); return $lines if ($lines); printf DBG "couldn't find section \"$section\" for \"$arch\"\n"; return 0; } sub get_section_value { (my $section, my $name) = @_; return $parsed_sections{$section}->{$name}; } sub get_string_value { my $key = shift; if ($key =~ /%(.+)%/) { $key = $1; return get_section_value("Strings", $key); } else { return $key; } } sub copy_file { my ($src, $dst) = @_; # ignore files not needed return 0 if (lc($src) =~ /\.((exe)|(dll)|(cpl)|(hlp))$/); my $real_file = get_file($src); if (length($real_file) gt 0) { $dst = lc($dst); printf DBG "copying \"$src_dir/$real_file\" to " . "\"$confdir/$driver_name/$dst\"\n"; copy("$src_dir/$real_file", "$confdir/$driver_name/$dst") or warn "couldn't copy \"$src_dir/$real_file\" to " . "\"$confdir/$driver_name\": $! -\n" . "installation may be incomplete\n"; printf DBG "chmod: $confdir/$driver_name/$dst\n"; chmod(0644, "$confdir/$driver_name/$dst"); if ($dst =~ /\.sys$/) { printf CONF "%s ", $dst; } } else { warn "couldn't find \"$src\" in \"$src_dir\"; make sure " . "all driver files, including .inf, .sys (and any firmware files) " . "are in \"$src_dir\" -\n" . "installation may be incomplete\n"; } } # for conf files with subvendor and subdevice, create conf files with just # vendor and device sub create_fuzzy_conf { my $driver = shift; my $cwd = cwd(); chdir("$confdir/$driver") or die "couldn't chdir to $confdir/$driver: $!"; open(LS, "ls -1 . |") or die "couldn't open $confdir/$driver: $!"; while (my $file = ) { chomp($file); if ($file =~ /$re_sub_dev_conf/) { my $fuzzy_file = "$1:$2.$5.conf"; printf DBG "file: $file, fuzzy file: $fuzzy_file\n"; if (! -e "$confdir/$driver/$fuzzy_file") { symlink("$file", "$fuzzy_file") or warn "couldn't link $confdir/$driver/$file " . "to $confdir/$driver/$fuzzy_file: $!\n"; } } } close(LS); chdir($cwd) or warn "couldn't chdir to $cwd: $!"; return 0; } # find a file in a case-insensitive way. sub get_file { my $file = lc(shift); if (opendir(DIR, "$src_dir")) { my @allfiles = readdir(DIR); foreach my $real_file (@allfiles) { if (lc($real_file) eq $file) { closedir(DIR); return $real_file; } } closedir(DIR); } else { warn "couldn't open \"$src_dir\": $! -\n" . "installation may be incomplete\n"; } return ""; } sub strip_quotes { my $s = shift; $s =~ s/"(.*)"/$1/; return $s; } sub del_comment { my $s = shift; $s =~ s/;.*//; return $s; } # remove whitespace at front and end. sub trim { my $s = shift; $s =~ s/^\s*//; $s =~ s/\s*$//; return $s; } sub valid_copy_file_name { my $file = shift; $file = lc($file); printf DBG "file name: %s\n", $file; foreach my $disk_file (@source_disks_files) { return 1 if ($file eq $disk_file); } # some inf files may not have SourceDisksFiles section, so use # known file names return 1 if ($file =~ /\.((sys)|(bin)|(out))$/); return 0; } sub parse_source_disks_files { my $lines = get_source_disks_files(); if ($lines) { foreach my $line (@{$lines}) { $line = del_comment($line); next if (length($line) eq 0); my @file = split("=", $line); next if (@file eq 0 or length($file[0] eq 0)); printf DBG "source disk file: \"%s\"\n", trim($file[0]); push (@source_disks_files, lc(trim($file[0]))); } } else { warn "couldn't find SourceDisksFiles section - " . "continuing anyway...\n"; } } sub get_source_disks_files { my $arch = `uname -m`; chomp($arch); if ($arch =~ /64$/) { $arch = "AMD64"; } else { $arch = "X86"; } my $lines = get_section("SourceDisksFiles." . $arch); return $lines if ($lines); $lines = get_section("SourceDisksFiles"); return $lines if ($lines); return 0; } sub device_driver_alias { my ($devid, $driver) = @_; my $done = 0; $devid = uc($devid); if (!($devid =~ /^$re_dev_id:$re_dev_id$/)) { printf "'$devid' is not a valid device ID\n"; return 1; } open(LS, "ls -1 $confdir/$driver/ |") or die "couldn't open $confdir/$driver: $!"; while (my $f = ) { chomp($f); if ($f =~ /\.([[:xdigit:]]+)\.conf$/) { if (stat("$confdir/$driver/$devid.$1.conf")) { printf "Driver '$driver' is already used for '$devid'\n"; $done = 1; last; } if (symlink("$f", "$confdir/$driver/$devid.$1.conf")) { printf "WARNING: Driver '$driver' will be used for '$devid'\n" . "This is safe _only_ if driver $driver is meant for " . "chip in device $devid\n"; $done = 1; last; } else { warn "couldn't create symlink for \"$f\": $! -\n" . "installation may be incomplete\n"; } } } close(LS); if ($done == 0) { printf "driver '$driver' is not installed (properly)!\n"; return 1; } return 0; } sub generate_module_device_map { my $mode = shift; my ($vendor, $device, $subvendor, $subdevice, $bustype, $busid); my $device_map; if (-d "/etc/modprobe.d") { $device_map = $modconf; } elsif (-d "/etc/modules.d") { $device_map = "/etc/modules.d/ndiswrapper"; } else { $device_map = "/etc/ndiswrapper/ndiswrapper"; } open(DEVMAP, "| sort >$device_map") or die "couldn't create modules alias file $device_map: $!"; open(LS, "ls -1 $confdir|") or die "couldn't open $confdir: $!"; while (my $driver = ) { chomp($driver); my $stat = (stat("$confdir/$driver"))[2]; if (S_ISDIR($stat)) { open(LS2, "ls -1 $confdir/$driver/ |") or die "couldn't open $confdir/$driver: $!"; while (my $file = ) { chomp ($file); if ($file =~ /\.conf$/) { if ($file =~ /^$re_sub_dev_conf$/) { ($vendor, $device, $subvendor, $subdevice, $busid) = (uc($1), uc($2), "0000$3", "0000$4", hex($5)); } elsif ($file =~ /^$re_dev_conf$/) { ($vendor, $device, $subvendor, $subdevice, $busid) = (uc($1), uc($2), "*", "*", hex($3)); } my $devstring; if ($busid eq $WRAP_USB_BUS or $busid eq 0) { $devstring = sprintf("usb:v%sp%sd*dc*dsc*dp*ic*isc*ip*", $vendor, $device); } elsif ($busid eq $WRAP_PCI_BUS) { $devstring = sprintf("pci:v0000%sd0000%ssv%ssd%sbc*sc*i*", $vendor, $device, $subvendor, $subdevice); } else { warn "wrong bustype ($busid) for " . "configuration file $file - ignoring it\n"; next; } if ($mode == 0) { printf DEVMAP "alias %s ndiswrapper\n", $devstring; } else { printf DEVMAP "install %s /sbin/modprobe ndiswrapper\n", $devstring; } } } close(LS2); } } close(LS); close(DEVMAP); printf "module configuration information is stored in $device_map\n"; return 0; } sub list_drivers { my $cards = get_cards(); open(LS, "ls -1 $confdir|") or die "couldn't open $confdir: $!"; while (my $driver = ) { chomp($driver); if (-e "$confdir/$driver") { printf "%s : %s\n", $driver, install_status($cards, $driver); } } close(LS); return 0; } sub add_module_alias { my $alias = 0; open(MODPROBE, "modprobe -c|") or die "couldn't run modprobe: $!"; while (my $line = ) { if ($line =~ /^alias\s.+\sndiswrapper/) { printf "module configuration already contains alias directive\n\n"; $alias = 1; } elsif ($line =~ /^install\s.*ndiswrapper/) { warn "module configuration contains directive $line;" . "you should delete that"; } elsif ($line =~ /^post-install\s+ndiswrapper/) { warn "module configuration contains directive $line;" . "you should delete that"; } } close(MODPROBE); if ($alias) { return 0; } printf "adding \"alias wlan0 ndiswrapper\" to $modconf ...\n"; system("echo \"alias wlan0 ndiswrapper\" >>$modconf") == 0 or die "couldn't add module alias: $!"; if (-x "/sbin/update-modules") { system("/sbin/update-modules"); } return 0; } sub get_cards { #01:00.0 Class 0300: 1002:4c66 (rev 01) # Subsystem: 1043:1732 my @cards = (); if (open(LSPCI, "lspci -vn|")) { my $card; while (my $line = ) { if ($line =~ /^[0-9a-f]+.+\s$re_dev_id:$re_dev_id/) { $card = {vendor => uc($1), device => uc($2)}; printf DBG "card: %s, %s\n", $1, $2; } elsif ($line =~ /.+Subsystem:\s$re_dev_id:$re_dev_id/) { $card->{subvendor} = uc($1); $card->{subdevice} = uc($2); printf DBG "sub: %s, %s\n", $1, $2; push(@cards, $card); } } close(LSPCI); } if (open(LSUSB, "lsusb |")) { my $card; while (my $line = ) { if ($line =~ /.+: ID\s$re_dev_id:$re_dev_id/) { $card = {vendor => uc($1), device => uc($2)}; push(@cards, $card); } } close(LSUSB); } return \@cards; } sub install_status { my ($cards, $driver) = @_; if (!$cards or !$driver) { return; } my ($sys, $conf, $inf); my ($vendor, $device, $subvendor, $subdevice, $busid, $ret); $sys = $conf = $inf = 0; open(LS2, "ls -1 $confdir/$driver|") or die "couldn't open $confdir/$driver: $!"; while (my $file = ) { chomp($file); if ($file =~ /\.sys$/) { $sys = 1; } elsif ($file =~ /\.inf$/) { $inf = 1; } elsif ($file =~ /^$re_sub_dev_conf$/) { $busid = hex($5); $conf = 1 if ($busid eq $WRAP_PCI_BUS); } elsif ($file =~ /^$re_dev_conf$/) { $busid = hex($3); $conf = 1 if ($busid eq $WRAP_USB_BUS or $busid eq 0 or $busid eq $WRAP_PCI_BUS); } } close(LS2); printf DBG "status: $sys, $inf, $conf\n"; if ($sys eq 0 or $inf eq 0 or $conf eq 0) { $ret = "invalid driver!"; return $ret; } $ret = "driver installed"; open(LS2, "ls -1 $confdir/$driver|") or die "couldn't open $confdir/$driver: $!"; while (my $file = ) { chomp($file); next if ($file !~ /\.conf$/); $conf = 0; if ($file =~ /^$re_sub_dev_conf$/) { ($vendor, $device, $subvendor, $subdevice, $busid) = (uc($1), uc($2), uc($3), uc($4), hex($5)); $conf = 1; foreach my $card (@{$cards}) { if ($card->{vendor} eq $vendor and $card->{device} eq $device and $card->{subvendor} eq $subvendor and $card->{subdevice} eq $subdevice and $busid eq $WRAP_PCI_BUS) { $ret .= "\n\tdevice ($vendor:$device" . ":$subvendor:$subdevice) present"; $conf = 2; last; } } } elsif ($file =~ /^$re_dev_conf/) { ($vendor, $device, $subvendor, $subdevice, $busid) = (uc($1), uc($2), "\\*", "\\*", hex($3)); $conf = 1; foreach my $card (@{$cards}) { if ($card->{vendor} eq $vendor and $card->{device} eq $device and ($busid eq $WRAP_USB_BUS or $busid eq 0 or $busid eq $WRAP_PCI_BUS)) { $ret .= "\n\tdevice ($vendor:$device) present"; $conf = 2; last; } } } next if ($conf le 1); # find if kernel knows of an alternate driver for this device my $devstring; if ($busid eq $WRAP_USB_BUS or $busid eq 0) { $devstring = sprintf("usb:v%sp%sd", $vendor, $device); } elsif ($busid eq $WRAP_PCI_BUS) { $devstring = sprintf("pci:v0000%sd0000%ssv", $vendor, $device); } else { next; } open(MODPROBE, "modprobe -c|") or next; while (my $line = ) { my $alt; chomp($line); next if $line !~ /$devstring/; $alt = (split(' ', $line))[-1]; chomp($alt); if (length($alt) gt 0 and $alt ne "ndiswrapper") { $ret .= " (alternate driver: $alt)"; last; } } close(MODPROBE); } close(LS2); printf DBG "driver: $driver, $ret\n"; return $ret; } if (@ARGV < 1) { usage(); exit(1); } if (`uname -r` =~ /(\d+)\.(\d+)\.(\d+)/) { if ($2 > 4) { if (-d "/etc/modprobe.d") { $modconf = "/etc/modprobe.d/ndiswrapper.conf"; } else { $modconf = "/etc/modprobe.conf"; } } else { if (-d "/etc/modutils") { $modconf = "/etc/modutils/ndiswrapper"; } else { $modconf = "/etc/modules.conf"; } } } my $res; my $dbg_file; $dbg_file = "/dev/null"; # "-D" is for development/debugging only if ($ARGV[0] eq "-D") { $dbg_file = "/tmp/ndiswrapper.dbg"; $confdir = "/tmp/ndiswrapper"; shift; } open(DBG, "> $dbg_file") or die "couldn't open $dbg_file: $!"; if ($ARGV[0] eq "-i" and @ARGV == 2) { $res = install($ARGV[1]); } elsif (($ARGV[0] eq "-a" or $ARGV[0] eq "-d") and @ARGV == 3) { $res = device_driver_alias($ARGV[1], $ARGV[2]); } elsif (($ARGV[0] eq "-e" or $ARGV[0] eq "-r") and @ARGV == 2) { $res = remove_driver($ARGV[1]); } elsif ($ARGV[0] eq "-l" and @ARGV == 1) { $res = list_drivers(); } elsif ($ARGV[0] eq "-m" and @ARGV == 1) { $res = add_module_alias(); } elsif ($ARGV[0] eq "-v" and @ARGV == 1) { $res = check_version(); } elsif ($ARGV[0] eq "-ma" and @ARGV == 1) { $res = generate_module_device_map(0); } elsif ($ARGV[0] eq "-mi" and @ARGV == 1) { $res = generate_module_device_map(1); } else { usage(); } close(DBG); exit($res); ## Local Variables: ## ## cperl-indent-level: 4 ## ## End: ## ndiswrapper-1.59/utils/ndiswrapper-buginfo000077500000000000000000000065051225731550500210740ustar00rootroot00000000000000#!/bin/sh #set -ex # Copyright (c) 2004 Torbjörn Svensson . # # 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 LOGFILE=`mktemp /tmp/ndiswrapper.XXXXXX` KVERS=`uname -r` log() { echo -e "$*" 2>&1 >> $LOGFILE } log_cmd() { /bin/sh -c "$*" 2>&1 >> $LOGFILE } newblock() { log "------------------------------------\n" } if [ $# -eq 1 -a "$1" = "-full" ]; then FULL=1 else FULL=0 fi # utils log "utils:" log_cmd 'ls -l /sbin/loadndisdriver' log_cmd 'ls -l /usr/sbin/ndiswrapper' # the kernel newblock log "kernel:" log_cmd 'cat /proc/version' if [ -d /lib/modules/$KVERS/build/include ]; then log "kernel sources are in /lib/modules/$KVERS/build" else log "kernel sources missing" fi newblock log "module information:" log_cmd 'modprobe -c | grep ndis' log "module(s):" log_cmd "find /lib/modules/$KVERS/ -name ndiswrapper\* | xargs ls -l" log "" # the version of gcc newblock log "gcc --version:" log_cmd 'gcc --version' newblock log "ndiswrapper -v:" log_cmd 'ndiswrapper -v' # installed drivers newblock log "installed drivers:" log_cmd 'ls -lR /etc/ndiswrapper' while :; do echo "Is it okay to shutdown 'wlan0' interface and reload the module?" echo "If loading the module crashes kernel, then say N here" echo -n "Reload ndiswrapper module? [N/y]:" read res # default to N, (nothing entered) if [ "$res" == "" ]; then res="N" fi case $res in y|Y) log "Reloading ndiswrapper..." if [ $FULL -eq 1 ]; then log_cmd 'ifconfig wlan0 down; rmmod ndiswrapper; modprobe ndiswrapper; dmesg | grep ndiswrapper | tail -n 500' else log_cmd 'ifconfig wlan0 down; rmmod ndiswrapper; modprobe ndiswrapper; dmesg | grep ndiswrapper | tail -n 50' fi break ;; n|N) log "Not reloading ndiswrapper..." log "" if [ $FULL -eq 1 ]; then log_cmd 'dmesg | grep ndiswrapper | tail -n 100' else log_cmd 'dmesg | grep ndiswrapper | tail -n 20' fi break ;; *) echo "No input.. retry." ;; esac done if [ $FULL -eq 1 ]; then # module information newblock # pci devices newblock log "PCI devices:" log_cmd "lspci -n" log "" log "USB devices:" log_cmd "lsusb" log "" # try to get kernelconfig newblock log "kernel configuration:" if [ -r /proc/config.gz ]; then log_cmd 'gzip -d < /proc/config.gz' else if [ -r /lib/modules/$KVERS/build/.config ]; then log_cmd "cat /lib/modules/$KVERS/build/.config" else log "kernel config missing" fi fi fi gzip -c $LOGFILE > /tmp/ndiswrapper-buginfo.gz echo "please attach /tmp/ndiswrapper-buginfo.gz to your bugreport!" \rm -f $LOGFILE