📄 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 NT_SPIN_LOCK timer_lock;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 miniport_reset(struct wrap_ndis_device *wnd){ NDIS_STATUS res; struct miniport_char *miniport; UINT cur_lookahead, max_lookahead; 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_comm_mutex)) { up(&wnd->tx_ring_mutex); EXIT3(return NDIS_STATUS_FAILURE); } miniport = &wnd->wd->driver->ndis_driver->miniport; cur_lookahead = wnd->nmb->cur_lookahead; max_lookahead = wnd->nmb->max_lookahead; wnd->ndis_comm_done = 0; wnd->ndis_comm_task = current; WARNING("%s is being reset", wnd->net_dev->name); irql = serialize_lock_irql(wnd); res = LIN2WIN2(miniport->reset, &reset_address, wnd->nmb->adapter_ctx); serialize_unlock_irql(wnd, irql); TRACE2("%08X, %08X", res, reset_address); if (res == NDIS_STATUS_PENDING) { /* wait for NdisMResetComplete */ if (wrap_wait_event((wnd->ndis_comm_done > 0), 0, TASK_INTERRUPTIBLE) < 0) res = NDIS_STATUS_FAILURE; else { res = wnd->ndis_comm_status; reset_address = wnd->ndis_comm_done - 1; } TRACE2("%08X, %08X", res, reset_address); } up(&wnd->ndis_comm_mutex); if (res == NDIS_STATUS_SUCCESS && reset_address) { wnd->nmb->cur_lookahead = cur_lookahead; wnd->nmb->max_lookahead = max_lookahead; set_packet_filter(wnd, wnd->packet_filter); set_multicast_list(wnd); } up(&wnd->tx_ring_mutex); EXIT3(return res);}/* MiniportQueryInformation */NDIS_STATUS miniport_query_info_needed(struct wrap_ndis_device *wnd, ndis_oid oid, void *buf, ULONG bufsize, ULONG *needed){ NDIS_STATUS res; ULONG written; struct miniport_char *miniport; KIRQL irql; ENTER2("oid: %08X", oid); if (down_interruptible(&wnd->ndis_comm_mutex)) EXIT3(return NDIS_STATUS_FAILURE); miniport = &wnd->wd->driver->ndis_driver->miniport; TRACE2("%p, %08X", miniport->query, oid); wnd->ndis_comm_done = 0; wnd->ndis_comm_task = current; irql = serialize_lock_irql(wnd); res = LIN2WIN6(miniport->query, wnd->nmb->adapter_ctx, oid, buf, bufsize, &written, needed); serialize_unlock_irql(wnd, irql); TRACE2("%08X, %08X", res, oid); if (res == NDIS_STATUS_PENDING) { /* wait for NdisMQueryInformationComplete */ if (wrap_wait_event((wnd->ndis_comm_done > 0), 0, TASK_INTERRUPTIBLE) < 0) res = NDIS_STATUS_FAILURE; else res = wnd->ndis_comm_status; TRACE2("%08X, %08X", res, oid); } up(&wnd->ndis_comm_mutex); DBG_BLOCK(2) { if (res || needed) TRACE2("%08X, %d, %d, %d", res, bufsize, written, *needed); } EXIT3(return res);}NDIS_STATUS miniport_query_info(struct wrap_ndis_device *wnd, ndis_oid oid, void *buf, ULONG bufsize){ NDIS_STATUS res; ULONG needed; res = miniport_query_info_needed(wnd, oid, buf, bufsize, &needed); return res;}/* MiniportSetInformation */NDIS_STATUS miniport_set_info(struct wrap_ndis_device *wnd, ndis_oid oid, void *buf, ULONG bufsize){ NDIS_STATUS res; ULONG written, needed; struct miniport_char *miniport; KIRQL irql; ENTER2("oid: %08X", oid); if (down_interruptible(&wnd->ndis_comm_mutex)) EXIT3(return NDIS_STATUS_FAILURE); miniport = &wnd->wd->driver->ndis_driver->miniport; TRACE2("%p, %08X", miniport->query, oid); wnd->ndis_comm_done = 0; wnd->ndis_comm_task = current; irql = serialize_lock_irql(wnd); res = LIN2WIN6(miniport->setinfo, wnd->nmb->adapter_ctx, oid, buf, bufsize, &written, &needed); serialize_unlock_irql(wnd, irql); TRACE2("%08X, %08X", res, oid); if (res == NDIS_STATUS_PENDING) { /* wait for NdisMQueryInformationComplete */ if (wrap_wait_event((wnd->ndis_comm_done > 0), 0, TASK_INTERRUPTIBLE) < 0) res = NDIS_STATUS_FAILURE; else res = wnd->ndis_comm_status; TRACE2("%08X, %08X", res, oid); } up(&wnd->ndis_comm_mutex); DBG_BLOCK(2) { if (res && needed) TRACE2("%08X, %d, %d, %d", res, bufsize, written, needed); } EXIT3(return res);}NDIS_STATUS miniport_query_int(struct wrap_ndis_device *wnd, ndis_oid oid, ULONG *data){ return miniport_query_info(wnd, oid, data, sizeof(ULONG));}NDIS_STATUS miniport_set_int(struct wrap_ndis_device *wnd, ndis_oid oid, ULONG data){ return miniport_set_info(wnd, oid, &data, sizeof(data));}/* MiniportPnPEventNotify */static NDIS_STATUS miniport_pnp_event(struct wrap_ndis_device *wnd, enum ndis_device_pnp_event event, ULONG power_profile){ struct miniport_char *miniport; ENTER1("%p, %d", wnd, event); miniport = &wnd->wd->driver->ndis_driver->miniport; if (!miniport->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), miniport->pnp_event_notify); if ((wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK) && !test_bit(HW_PRESENT, &wnd->wd->hw_status) && miniport->pnp_event_notify) { TRACE1("calling surprise_removed"); LIN2WIN4(miniport->pnp_event_notify, wnd->nmb->adapter_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(miniport->pnp_event_notify, wnd->nmb->adapter_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 miniport_init(struct wrap_ndis_device *wnd){ NDIS_STATUS error_status, status; UINT medium_index, medium_array[] = {NdisMedium802_3}; struct miniport_char *miniport; struct ndis_pnp_capabilities pnp_capa; 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->miniport.init) { WARNING("assuming WDM (non-NDIS) driver"); EXIT1(return NDIS_STATUS_NOT_RECOGNIZED); } miniport = &wnd->wd->driver->ndis_driver->miniport; status = LIN2WIN6(miniport->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 = miniport_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 = miniport_query_info(wnd, OID_PNP_CAPABILITIES, &pnp_capa, sizeof(pnp_capa)); if (status == NDIS_STATUS_SUCCESS) wnd->attributes |= NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; else wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND; TRACE1("%d", pnp_capa.wakeup_capa.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; EXIT1(return NDIS_STATUS_SUCCESS);}/* MiniportHalt */static void miniport_halt(struct wrap_ndis_device *wnd){ struct miniport_char *miniport; ENTER1("%p", wnd); if (test_and_clear_bit(HW_INITIALIZED, &wnd->wd->hw_status)) { hangcheck_del(wnd); del_iw_stats_timer(wnd); miniport = &wnd->wd->driver->ndis_driver->miniport; TRACE1("halt: %p", miniport->miniport_halt); LIN2WIN1(miniport->miniport_halt, wnd->nmb->adapter_ctx); /* cancel any timers left by bugyy windows driver; also free * the memory for timers */ while (1) { KIRQL irql; struct nt_list *ent; struct wrap_timer *wrap_timer; irql = nt_spin_lock_irql(&timer_lock, DISPATCH_LEVEL); ent = RemoveHeadList(&wnd->wrap_timer_list); nt_spin_unlock_irql(&timer_lock, irql); if (!ent) break; wrap_timer = container_of(ent, struct wrap_timer, list); 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); } } else WARNING("device %p is not initialized - not halting", wnd); EXIT1(return);}static NDIS_STATUS miniport_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_comm_mutex); if (test_and_clear_bit(HW_HALTED, &wnd->wd->hw_status)) { status = miniport_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 = miniport_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); if (wnd->ndis_wolopts && wrap_is_pci_bus(wnd->wd->dev_bus)) pci_enable_wake(wnd->wd->pci.pdev, PCI_D0, 0); } else return NDIS_STATUS_FAILURE; if (status == NDIS_STATUS_SUCCESS) { up(&wnd->tx_ring_mutex); netif_device_attach(wnd->net_dev); hangcheck_add(wnd); add_iw_stats_timer(wnd); set_scan(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) { if (wnd->ndis_wolopts) { status = miniport_set_int(wnd, OID_PNP_ENABLE_WAKE_UP, wnd->ndis_wolopts); if (status == NDIS_STATUS_SUCCESS && wrap_is_pci_bus(wnd->wd->dev_bus)) pci_enable_wake(wnd->wd->pci.pdev, PCI_D0, 1); else WARNING("%s: couldn't enable WOL: %08x", wnd->net_dev->name, status); } status = miniport_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); miniport_halt(wnd); set_bit(HW_HALTED, &wnd->wd->hw_status); status = STATUS_SUCCESS; } if (down_interruptible(&wnd->ndis_comm_mutex)) WARNING("couldn't lock ndis_comm_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[3 * ETH_ALEN]; mac_address mac; memcpy(mac, addr->sa_data, sizeof(mac)); memset(mac_string, 0, sizeof(mac_string)); res = snprintf(mac_string, sizeof(mac_string), MACSTRSEP, MAC2STR(mac));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -