📄 wrapndis.c
字号:
/* * 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 "pnp.h"#include "loader.h"#include "wrapndis.h"#include <linux/inetdevice.h>#include <linux/ip.h>#include <linux/tcp.h>#include <linux/udp.h>#include <linux/in.h>extern char *if_name;extern int hangcheck_interval;extern struct iw_handler_def ndis_handler_def;extern spinlock_t ntoskernel_lock;workqueue_struct_t *wrapndis_wq;static struct nt_thread *wrapndis_worker_thread;static int set_packet_filter(struct wrap_ndis_device *wnd, ULONG packet_filter);static void add_iw_stats_timer(struct wrap_ndis_device *wnd);static void del_iw_stats_timer(struct wrap_ndis_device *wnd);static NDIS_STATUS wrap_ndis_start_device(struct wrap_ndis_device *wnd);static int wrap_ndis_remove_device(struct wrap_ndis_device *wnd);static void set_multicast_list(struct wrap_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 wrap_ndis_device *wnd){ NDIS_STATUS res; struct miniport *mp; BOOLEAN reset_address; KIRQL irql; ENTER2("wnd: %p", wnd); if (down_interruptible(&wnd->tx_ring_mutex)) EXIT3(return NDIS_STATUS_FAILURE); if (down_interruptible(&wnd->ndis_req_mutex)) { up(&wnd->tx_ring_mutex); EXIT3(return NDIS_STATUS_FAILURE); } 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); } up(&wnd->ndis_req_mutex); if (res == NDIS_STATUS_SUCCESS && reset_address) { set_packet_filter(wnd, wnd->packet_filter); set_multicast_list(wnd); } up(&wnd->tx_ring_mutex); EXIT3(return res);}/* MiniportRequest(Query/Set)Information */NDIS_STATUS mp_request(enum ndis_request_type request, struct wrap_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; if (down_interruptible(&wnd->ndis_req_mutex)) EXIT3(return NDIS_STATUS_FAILURE); 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); } up(&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 wrap_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_PRESENT, &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, (ULONG)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 wrap_ndis_device *wnd){ NDIS_STATUS error_status, status; UINT medium_index, 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, sizeof(medium_array) / sizeof(medium_array[0]), 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 wrap_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); if (wnd->physical_medium == NdisPhysicalMediumWirelessLan && wrap_is_pci_bus(wnd->wd->dev_bus)) { up(&wnd->ndis_req_mutex); disassociate(wnd, 0); down_interruptible(&wnd->ndis_req_mutex); } 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 bugyy 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 wrap_ndis_device *wnd, enum ndis_power_state state){ NDIS_STATUS status; TRACE1("%d", state); if (state == NdisDeviceStateD0) { status = NDIS_STATUS_SUCCESS; up(&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) { up(&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 { if (down_interruptible(&wnd->tx_ring_mutex)) EXIT1(return NDIS_STATUS_FAILURE); 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) { 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; } if (down_interruptible(&wnd->ndis_req_mutex)) WARNING("couldn't lock ndis_req_mutex"); EXIT1(return status); }}static int ndis_set_mac_address(struct net_device *dev, void *p){ struct wrap_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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -