📄 ndis.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 <linux/module.h>#include <linux/pci.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/types.h>#include <linux/string.h>#include <linux/ctype.h>#include "ndis.h"#include "iw_ndis.h"#include "wrapper.h"extern struct list_head ndis_drivers;extern KSPIN_LOCK ntoskernel_lock;static struct list_head handle_ctx_list;static struct work_struct ndis_work;static struct list_head ndis_work_list;static KSPIN_LOCK ndis_work_list_lock;static void ndis_worker(void *data);static void free_handle_ctx(struct ndis_handle *handle);/* Some drivers allocate all NDIS_PACKETs they need at the beginning * and others allocate them quite often - every time a packet is * sent/received. We use cache pool for this to avoid memory * fragmentation, just like MDLs */static kmem_cache_t *packet_cache;/* ndis_init is called once when module is loaded */int ndis_init(void){ /* only one worker is used for all drivers */ INIT_WORK(&ndis_work, &ndis_worker, NULL); INIT_LIST_HEAD(&ndis_work_list); INIT_LIST_HEAD(&handle_ctx_list); kspin_lock_init(&ndis_work_list_lock); packet_cache = kmem_cache_create("ndis_packet", sizeof(struct ndis_packet), 0, 0, NULL, NULL); if (!packet_cache) { ERROR("couldn't allocate packet cache"); return -ENOMEM; } return 0;}/* ndis_exit is called once when module is removed */void ndis_exit(void){ if (packet_cache && kmem_cache_destroy(packet_cache)) ERROR("A Windows driver didn't free all packet(s);" "memory is leaking"); return;}/* ndis_exit_handle is called for each handle */void ndis_exit_handle(struct ndis_handle *handle){ /* TI driver doesn't call NdisMDeregisterInterrupt during halt! */ if (handle->ndis_irq) NdisMDeregisterInterrupt(handle->ndis_irq); free_handle_ctx(handle); if (handle->pci_resources) vfree(handle->pci_resources);}/* remove all 'handle X ctx' pairs for the given handle */static void free_handle_ctx(struct ndis_handle *handle){ struct list_head *cur, *tmp; kspin_lock(&ntoskernel_lock); list_for_each_safe(cur, tmp, &handle_ctx_list) { struct handle_ctx_entry *handle_ctx = list_entry(cur, struct handle_ctx_entry, list); if (handle_ctx->handle == handle) { list_del(&handle_ctx->list); kfree(handle_ctx); } } kspin_unlock(&ntoskernel_lock); return;}/* Called from the driver entry. */STDCALL void WRAP_EXPORT(NdisInitializeWrapper) (struct ndis_handle **ndis_handle, void *SystemSpecific1, void *SystemSpecific2, void *SystemSpecific3){ TRACEENTER1("handle=%p, SS1=%p, SS2=%p", ndis_handle, SystemSpecific1, SystemSpecific2); *ndis_handle = (struct ndis_handle *)SystemSpecific1; TRACEEXIT1(return);}STDCALL void WRAP_EXPORT(NdisTerminateWrapper) (struct ndis_handle *handle, void *SystemSpecific1){ TRACEEXIT1(return);}/* Register a miniport with NDIS. Called from driver entry */STDCALL NDIS_STATUS WRAP_EXPORT(NdisMRegisterMiniport) (struct ndis_driver *ndis_driver, struct miniport_char *miniport_char, UINT char_len){ int i, min_length; int *func; char *miniport_funcs[] = { "query", "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", "adapter_shutdown", }; min_length = ((char *) &miniport_char->co_create_vc) - ((char *) miniport_char); TRACEENTER1("driver: %p %p %d", ndis_driver, miniport_char, char_len); if (miniport_char->majorVersion < 4) { ERROR("Driver %s using ndis version %d which is too old.", ndis_driver->name, miniport_char->majorVersion); TRACEEXIT1(return NDIS_STATUS_BAD_VERSION); } if (char_len < min_length) { ERROR("Characteristics length %d is too small for driver %s", char_len, ndis_driver->name); TRACEEXIT1(return NDIS_STATUS_BAD_CHARACTERISTICS); } DBGTRACE1("Version %d.%d", miniport_char->majorVersion, miniport_char->minorVersion); DBGTRACE1("Len: %08x:%u", char_len, (u32)sizeof(struct miniport_char)); memcpy(&ndis_driver->miniport_char, miniport_char, sizeof(struct miniport_char)); i = 0; func = (int *)&ndis_driver->miniport_char.query; while (i < sizeof(miniport_funcs) / sizeof(miniport_funcs[0])) { DBGTRACE2("miniport function '%s' is at %lx", miniport_funcs[i], (unsigned long)func[i]); i++; } TRACEEXIT1(return NDIS_STATUS_SUCCESS);}STDCALL NDIS_STATUS WRAP_EXPORT(NdisAllocateMemory) (void **dest, UINT length, UINT flags, NDIS_PHY_ADDRESS highest_address){ TRACEENTER3("length = %u, flags = %08X", length, flags); if (length <= KMALLOC_THRESHOLD) { if (current_irql() < DISPATCH_LEVEL) *dest = kmalloc(length, GFP_KERNEL); else *dest = kmalloc(length, GFP_ATOMIC); } else if (flags & NDIS_MEMORY_CONTIGUOUS) { WARNING("Allocating %u bytes of physically " "contiguous memory may fail", length); *dest = kmalloc(length, GFP_KERNEL); } else { if (current_irql() == DISPATCH_LEVEL) ERROR("Windows driver allocating too big a block" " at DISPATCH_LEVEL: %d", length); *dest = vmalloc(length); } if (*dest) TRACEEXIT3(return NDIS_STATUS_SUCCESS); DBGTRACE3("Allocatemem failed size=%d", length); TRACEEXIT3(return NDIS_STATUS_FAILURE);}STDCALL NDIS_STATUS WRAP_EXPORT(NdisAllocateMemoryWithTag) (void **dest, UINT length, ULONG tag){ TRACEEXIT3(return NdisAllocateMemory(dest, length, 0, 0));}STDCALL void WRAP_EXPORT(NdisFreeMemory) (void *addr, UINT length, UINT flags){ struct ndis_work_entry *ndis_work_entry; struct ndis_free_mem_work_item *free_mem; KIRQL irql; TRACEENTER3("addr = %p, flags = %08X", addr, flags); if (!addr) TRACEEXIT3(return); if (length <= KMALLOC_THRESHOLD) kfree(addr); else if (flags & NDIS_MEMORY_CONTIGUOUS) kfree(addr); else { if (!in_interrupt()) { vfree(addr); TRACEEXIT3(return); } /* Centrino 2200 driver calls this function when in * ad-hoc mode in interrupt context when length > * KMALLOC_THRESHOLD, which implies that vfree is * called in interrupt context, which is not * correct. So we use worker for it */ ndis_work_entry = kmalloc(sizeof(*ndis_work_entry), GFP_ATOMIC); BUG_ON(!ndis_work_entry); ndis_work_entry->type = NDIS_FREE_MEM_WORK_ITEM; free_mem = &ndis_work_entry->entry.free_mem_work_item; free_mem->addr = addr; free_mem->length = length; free_mem->flags = flags; irql = kspin_lock_irql(&ndis_work_list_lock, DISPATCH_LEVEL); list_add_tail(&ndis_work_entry->list, &ndis_work_list); kspin_unlock_irql(&ndis_work_list_lock, irql); schedule_work(&ndis_work); } TRACEEXIT3(return);}/* * This function should not be STDCALL because it's a variable args function. */NOREGPARM void WRAP_EXPORT(NdisWriteErrorLogEntry) (struct ndis_handle *handle, unsigned int error, ULONG count, unsigned int p1){ ERROR("log: %08X, count: %d (%08x), return address: %p, entry: %p" " offset: %lu", error, count, p1, __builtin_return_address(0), handle->driver->entry, (unsigned long)addr_offset(handle->driver)); return;}STDCALL void WRAP_EXPORT(NdisOpenConfiguration) (NDIS_STATUS *status, struct ndis_handle **confhandle, struct ndis_handle *handle){ TRACEENTER2("confHandle: %p, handle->dev_name: %s", confhandle, handle->net_dev->name); *confhandle = handle; *status = NDIS_STATUS_SUCCESS; TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisOpenProtocolConfiguration) (NDIS_STATUS *status, struct ndis_handle **confhandle, struct unicode_string *section){ TRACEENTER2("confHandle: %p", confhandle); *confhandle = (struct ndis_handle *)section; *status = NDIS_STATUS_SUCCESS; TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisOpenConfigurationKeyByName) (NDIS_STATUS *status, struct ndis_handle *handle, struct unicode_string *key, struct ndis_handle **subkeyhandle){ TRACEENTER2("%s", ""); *subkeyhandle = handle; *status = NDIS_STATUS_SUCCESS; TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisOpenConfigurationKeyByIndex) (NDIS_STATUS *status, struct ndis_handle *handle, ULONG index, struct unicode_string *key, struct ndis_handle **subkeyhandle){ TRACEENTER2("%s", ""); *subkeyhandle = handle; *status = NDIS_STATUS_SUCCESS; TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisCloseConfiguration) (struct ndis_handle *handle){ TRACEENTER2("handle: %p", handle); return;}STDCALL void WRAP_EXPORT(NdisOpenFile) (NDIS_STATUS *status, struct ndis_bin_file **filehandle, UINT *filelength, struct unicode_string *filename, NDIS_PHY_ADDRESS highest_address){ struct ansi_string ansi; struct list_head *cur, *tmp; struct ndis_bin_file *file; TRACEENTER2("status = %p, filelength = %p, *filelength = %d, " "high = %llx, filehandle = %p, *filehandle = %p", status, filelength, *filelength, highest_address, filehandle, *filehandle); ansi.buf = kmalloc(MAX_STR_LEN, GFP_KERNEL); if (!ansi.buf) { *status = NDIS_STATUS_RESOURCES; return; } ansi.buf[MAX_STR_LEN-1] = 0; ansi.buflen = MAX_STR_LEN; if (RtlUnicodeStringToAnsiString(&ansi, filename, 0)) { *status = NDIS_STATUS_RESOURCES; RtlFreeAnsiString(&ansi); TRACEEXIT2(return); } DBGTRACE2("Filename: %s", ansi.buf); /* Loop through all drivers and all files to find the requested file */ list_for_each_safe(cur, tmp, &ndis_drivers) { struct ndis_driver *driver; int i; driver = list_entry(cur, struct ndis_driver, list); for (i = 0; i < driver->num_bin_files; i++) { int n; file = &driver->bin_files[i]; DBGTRACE2("considering %s", file->name); n = min(strlen(file->name), strlen(ansi.buf)); if (strnicmp(file->name, ansi.buf, n) == 0) { *filehandle = file; *filelength = file->size; *status = NDIS_STATUS_SUCCESS; RtlFreeAnsiString(&ansi); TRACEEXIT2(return); } } } *status = NDIS_STATUS_FILE_NOT_FOUND; RtlFreeAnsiString(&ansi); TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisMapFile) (NDIS_STATUS *status, void **mappedbuffer, struct ndis_bin_file *filehandle){ TRACEENTER2("handle: %p", filehandle); if (!filehandle) { *status = NDIS_STATUS_ALREADY_MAPPED; TRACEEXIT2(return); } *status = NDIS_STATUS_SUCCESS; *mappedbuffer = filehandle->data; TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisUnmapFile) (struct ndis_bin_file *filehandle){ TRACEENTER2("handle: %p", filehandle); TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisCloseFile) (struct ndis_bin_file *filehandle){ TRACEENTER2("handle: %p", filehandle); TRACEEXIT2(return);}STDCALL void WRAP_EXPORT(NdisGetSystemUpTime) (ULONG *systemuptime){ TRACEENTER4("%s", ""); *systemuptime = 10 * jiffies / HZ; TRACEEXIT4(return);}/* called as macro */STDCALL ULONG WRAP_EXPORT(NDIS_BUFFER_TO_SPAN_PAGES) (ndis_buffer *buffer){ ULONG_PTR start; ULONG n; TRACEENTER4("%s", ""); if (buffer == NULL) return 0; if (MmGetMdlByteCount(buffer) == 0) return 1; start = (ULONG_PTR)(MmGetMdlVirtualAddress(buffer)); n = SPAN_PAGES(start, MmGetMdlByteCount(buffer)); DBGTRACE4("pages = %u", n); TRACEEXIT3(return n);}STDCALL void WRAP_EXPORT(NdisGetBufferPhysicalArraySize) (ndis_buffer *buffer, UINT *arraysize){ TRACEENTER3("Buffer: %p", buffer); *arraysize = NDIS_BUFFER_TO_SPAN_PAGES(buffer); TRACEEXIT3(return);}static int ndis_encode_setting(struct device_setting *setting, int device_setting_type){ struct ansi_string ansi; struct ndis_config_param *param; TRACEENTER2("type = %d", device_setting_type); if (setting->config_param.type == device_setting_type) return NDIS_STATUS_SUCCESS; switch(device_setting_type) { case NDIS_CONFIG_PARAM_INT: setting->config_param.data.intval = simple_strtol(setting->value, NULL, 0); DBGTRACE1("value = %u", (ULONG)setting->config_param.data.intval); break; case NDIS_CONFIG_PARAM_HEXINT: setting->config_param.data.intval = simple_strtol(setting->value, NULL, 16); DBGTRACE2("value = %u", (ULONG)setting->config_param.data.intval); break; case NDIS_CONFIG_PARAM_STRING: ansi.buflen = ansi.len = strlen(setting->value); ansi.buf = setting->value; DBGTRACE2("setting value = %s", ansi.buf); param = &setting->config_param; if (param->data.ustring.buf) RtlFreeUnicodeString(¶m->data.ustring); if (RtlAnsiStringToUnicodeString(¶m->data.ustring, &ansi, 1)) TRACEEXIT1(return NDIS_STATUS_FAILURE); break; default: return NDIS_STATUS_FAILURE; } setting->config_param.type = device_setting_type; return NDIS_STATUS_SUCCESS;}static int ndis_decode_setting(struct device_setting *setting, struct ndis_config_param *val){ struct ansi_string ansi; if (setting->config_param.type == NDIS_CONFIG_PARAM_STRING &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -