📄 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"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_stats_timer(struct wrap_ndis_device *wnd);static void del_stats_timer(struct wrap_ndis_device *wnd);static NDIS_STATUS ndis_start_device(struct wrap_ndis_device *wnd);static int 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);static inline int ndis_wait_comm_completion(struct wrap_ndis_device *wnd){ if ((wait_event_interruptible(wnd->ndis_comm_wq, (wnd->ndis_comm_done > 0)))) return -1; else return 0;}/* 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; TRACEENTER2("wnd: %p", wnd); if (down_interruptible(&wnd->tx_ring_mutex)) TRACEEXIT3(return NDIS_STATUS_FAILURE); if (down_interruptible(&wnd->ndis_comm_mutex)) { up(&wnd->tx_ring_mutex); TRACEEXIT3(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; 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); DBGTRACE2("%08X, %08X", res, reset_address); if (res == NDIS_STATUS_PENDING) { /* wait for NdisMResetComplete */ if (ndis_wait_comm_completion(wnd)) res = NDIS_STATUS_FAILURE; else { res = wnd->ndis_comm_status; reset_address = wnd->ndis_comm_done - 1; } DBGTRACE2("%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); TRACEEXIT3(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; DBGTRACE2("oid: %08X", oid); if (down_interruptible(&wnd->ndis_comm_mutex)) TRACEEXIT3(return NDIS_STATUS_FAILURE); miniport = &wnd->wd->driver->ndis_driver->miniport; DBGTRACE2("%p, %08X", miniport->query, oid); wnd->ndis_comm_done = 0; irql = serialize_lock_irql(wnd); res = LIN2WIN6(miniport->query, wnd->nmb->adapter_ctx, oid, buf, bufsize, &written, needed); serialize_unlock_irql(wnd, irql); DBGTRACE2("%08X, %08X", res, oid); if (res == NDIS_STATUS_PENDING) { /* wait for NdisMQueryInformationComplete */ if (ndis_wait_comm_completion(wnd)) res = NDIS_STATUS_FAILURE; else res = wnd->ndis_comm_status; DBGTRACE2("%08X, %08X", res, oid); } up(&wnd->ndis_comm_mutex); DBG_BLOCK(2) { if (res || needed) DBGTRACE2("%08X, %d, %d, %d", res, bufsize, written, *needed); } TRACEEXIT3(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; DBGTRACE2("oid: %08X", oid); if (down_interruptible(&wnd->ndis_comm_mutex)) TRACEEXIT3(return NDIS_STATUS_FAILURE); miniport = &wnd->wd->driver->ndis_driver->miniport; DBGTRACE2("%p, %08X", miniport->query, oid); wnd->ndis_comm_done = 0; irql = serialize_lock_irql(wnd); res = LIN2WIN6(miniport->setinfo, wnd->nmb->adapter_ctx, oid, buf, bufsize, &written, &needed); serialize_unlock_irql(wnd, irql); DBGTRACE2("%08X, %08X", res, oid); if (res == NDIS_STATUS_PENDING) { /* wait for NdisMQueryInformationComplete */ if (ndis_wait_comm_completion(wnd)) res = NDIS_STATUS_FAILURE; else res = wnd->ndis_comm_status; DBGTRACE2("%08X, %08X", res, oid); } up(&wnd->ndis_comm_mutex); DBG_BLOCK(2) { if (res && needed) DBGTRACE2("%08X, %d, %d, %d", res, bufsize, written, needed); } TRACEEXIT3(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){ struct miniport_char *miniport; ULONG power_profile; TRACEENTER1("%p, %d", wnd, event); miniport = &wnd->wd->driver->ndis_driver->miniport; if (!miniport->pnp_event_notify) { DBGTRACE1("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->hw_status)) TRACEEXIT1(return NDIS_STATUS_SUCCESS); switch (event) { case NdisDevicePnPEventSurpriseRemoved: DBGTRACE1("%u, %p", (wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK), miniport->pnp_event_notify); if ((wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK) && wnd->wd->surprise_removed == TRUE && miniport->pnp_event_notify) { DBGTRACE1("calling surprise_removed"); LIN2WIN4(miniport->pnp_event_notify, wnd->nmb->adapter_ctx, NdisDevicePnPEventSurpriseRemoved, NULL, 0); } else DBGTRACE1("Windows driver %s doesn't support " "MiniportPnpEventNotify for safe unplugging", wnd->wd->driver->name); return NDIS_STATUS_SUCCESS; case NdisDevicePnPEventPowerProfileChanged: 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; UINT medium_array[] = {NdisMedium802_3}; struct miniport_char *miniport; struct ndis_pnp_capabilities pnp_capa; TRACEENTER1("irql: %d", current_irql()); if (test_bit(HW_INITIALIZED, &wnd->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"); TRACEEXIT1(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); DBGTRACE1("init returns: %08X, irql: %d", status, current_irql()); if (status != NDIS_STATUS_SUCCESS) { WARNING("couldn't initialize device: %08X", status); TRACEEXIT1(return NDIS_STATUS_FAILURE); } /* Wait a little to let card power up otherwise ifup might * fail after boot */ sleep_hz(HZ / 2); set_bit(HW_INITIALIZED, &wnd->hw_status); hangcheck_add(wnd); /* the description about NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND is * misleading/confusing; we just ignore it */ status = miniport_query_info(wnd, OID_PNP_CAPABILITIES, &pnp_capa, sizeof(pnp_capa)); if (status == NDIS_STATUS_SUCCESS) wnd->pm_capa = TRUE; else wnd->pm_capa = FALSE; DBGTRACE1("%d", pnp_capa.wakeup_capa.min_magic_packet_wakeup); TRACEEXIT1(return NDIS_STATUS_SUCCESS);}/* MiniportHalt */static void miniport_halt(struct wrap_ndis_device *wnd){ struct miniport_char *miniport; TRACEENTER1("%p", wnd); if (test_and_clear_bit(HW_INITIALIZED, &wnd->hw_status)) { hangcheck_del(wnd); del_stats_timer(wnd); miniport = &wnd->wd->driver->ndis_driver->miniport; DBGTRACE1("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->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->timer); memset(wrap_timer, 0, sizeof(*wrap_timer)); slack_kfree(wrap_timer); } } else WARNING("device %p is not initialized - not halting", wnd); TRACEEXIT1(return);}static NDIS_STATUS miniport_set_power_state(struct wrap_ndis_device *wnd, enum ndis_power_state state){ NDIS_STATUS status; DBGTRACE1("%d", state); if (state == NdisDeviceStateD0) { status = NDIS_STATUS_SUCCESS; up(&wnd->ndis_comm_mutex); if (test_and_clear_bit(HW_HALTED, &wnd->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->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_stats_timer(wnd); set_scan(wnd); } else { WARNING("%s: couldn't set power to state %d; device not" " resumed", wnd->net_dev->name, state); } TRACEEXIT1(return status); } else { if (down_interruptible(&wnd->tx_ring_mutex)) TRACEEXIT1(return NDIS_STATUS_FAILURE); netif_device_detach(wnd->net_dev); hangcheck_del(wnd); del_stats_timer(wnd); status = NDIS_STATUS_NOT_SUPPORTED; if (wnd->pm_capa == TRUE) { enum ndis_power_state pm_state = state; if (wnd->ndis_wolopts) { status = miniport_set_int(wnd, OID_PNP_ENABLE_WAKE_UP, wnd->ndis_wolopts); if (status == NDIS_STATUS_SUCCESS) { if (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, pm_state); if (status == NDIS_STATUS_SUCCESS) { set_bit(HW_SUSPENDED, &wnd->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->hw_status); status = STATUS_SUCCESS; } if (down_interruptible(&wnd->ndis_comm_mutex)) WARNING("couldn't lock ndis_comm_mutex"); TRACEEXIT1(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), MACSTR, MAC2STR(mac)); DBGTRACE1("%d", res); if (res != (2 * sizeof(mac))) TRACEEXIT1(return -EINVAL); RtlInitAnsiString(&ansi, mac_string); if (RtlAnsiStringToUnicodeString(¶m.data.string, &ansi, TRUE)) { RtlFreeUnicodeString(&key); TRACEEXIT1(return -EINVAL); } param.type = NdisParameterString; RtlInitAnsiString(&ansi, "NetworkAddress"); if (RtlAnsiStringToUnicodeString(&key, &ansi, TRUE)) TRACEEXIT1(return -EINVAL); NdisWriteConfiguration(&res, wnd->nmb, &key, ¶m); RtlFreeUnicodeString(&key); RtlFreeUnicodeString(¶m.data.string); if (res != NDIS_STATUS_SUCCESS) TRACEEXIT1(return -EINVAL); if (ndis_reinit(wnd) == NDIS_STATUS_SUCCESS) { res = miniport_query_info(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac)); if (res == NDIS_STATUS_SUCCESS) { DBGTRACE1("mac:" MACSTRSEP, MAC2STR(mac)); memcpy(dev->dev_addr, mac, sizeof(mac)); } else ERROR("couldn't get mac address: %08X", res); } TRACEEXIT1(return 0);}static struct ndis_packet *alloc_tx_packet(struct wrap_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->skb = skb; if (wnd->use_sg_dma) { oob_data->ndis_sg_element.address = PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, skb->data,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -