📄 loader.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/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/vmalloc.h>#include <linux/kmod.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/miscdevice.h>#include <linux/pci.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/if_arp.h>#include <net/iw_handler.h>#include <linux/rtnetlink.h>#include <asm/uaccess.h>#include "ndis.h"#include "loader.h"#include "wrapper.h"static KSPIN_LOCK loader_lock;static struct ndis_device *ndis_devices;static unsigned int num_ndis_devices;struct list_head ndis_drivers;static struct pci_device_id *ndiswrapper_pci_devices;static struct usb_device_id *ndiswrapper_usb_devices;static struct pci_driver ndiswrapper_pci_driver;static struct usb_driver ndiswrapper_usb_driver;/* load driver for given device, if not already loaded */static struct ndis_driver *ndiswrapper_load_driver(struct ndis_device *device){ int err, found; struct ndis_driver *ndis_driver; TRACEENTER1("device: %04X:%04X:%04X:%04X", device->vendor, device->device, device->subvendor, device->subdevice); found = 0; kspin_lock(&loader_lock); list_for_each_entry(ndis_driver, &ndis_drivers, list) { if (strcmp(ndis_driver->name, device->driver_name) == 0) { DBGTRACE1("driver %s already loaded", ndis_driver->name); found = 1; break; } } kspin_unlock(&loader_lock); if (found) TRACEEXIT1(return ndis_driver); else { char *argv[] = {"loadndisdriver", #if defined DEBUG && DEBUG >= 1 "1",#else "0",#endif NDISWRAPPER_VERSION, device->driver_name, device->conf_file_name, NULL}; char *env[] = {NULL}; DBGTRACE1("loading driver %s", device->driver_name); err = call_usermodehelper("/sbin/loadndisdriver", argv, env#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) , 1#endif ); if (err) { ERROR("loadndiswrapper failed (%d); check system log " "for messages from 'loadndisdriver'", err); TRACEEXIT1(return NULL); }#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* wait for the driver to load and initialize */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ);#endif found = 0; kspin_lock(&loader_lock); list_for_each_entry(ndis_driver, &ndis_drivers, list) { if (strcmp(ndis_driver->name, device->driver_name) == 0) { found = 1; break; } } kspin_unlock(&loader_lock); if (!found) { ERROR("couldn't load driver '%s'", device->driver_name); TRACEEXIT1(return NULL); } DBGTRACE1("driver %s is loaded", ndis_driver->name); } TRACEEXIT1(return ndis_driver);}/* * Called by PCI-subsystem for each PCI-card found. * * This function should not be marked __devinit because ndiswrapper * adds PCI_id's dynamically. */static int ndiswrapper_add_one_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent){ int res; struct ndis_device *device; struct ndis_driver *driver; struct ndis_handle *handle; struct net_device *dev; struct miniport_char *miniport; TRACEENTER1("ent: %p", ent); DBGTRACE1("called for %04x:%04x:%04x:%04x", pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device); device = &ndis_devices[ent->driver_data]; driver = ndiswrapper_load_driver(device); if (!driver) { res = -ENODEV; goto out_nodev; } dev = ndis_init_netdev(&handle, device, driver); if (!dev) { ERROR("couldn't initialize network device"); res = -ENOMEM; goto out_nodev; }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) SET_NETDEV_DEV(dev, &pdev->dev);#endif handle->dev.pci = pdev; handle->device = device; pci_set_drvdata(pdev, handle); device->handle = handle; res = pci_enable_device(pdev); if (res) { ERROR("couldn't enable PCI device: %08x", res); goto out_enable; } res = pci_request_regions(pdev, DRIVER_NAME); if (res) { ERROR("couldn't request PCI regions: %08x", res); goto out_regions; } pci_set_power_state(pdev, 0);#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,9) pci_restore_state(pdev, NULL);#endif DBGTRACE1("%s", "calling ndis init routine"); if ((res = miniport_init(handle))) { ERROR("Windows driver couldn't initialize the device (%08X)", res); res = -EINVAL; goto out_start; } handle->hw_status = 0; handle->wrapper_work = 0; /* do we need to power up the card explicitly? */ miniport_set_int(handle, OID_PNP_SET_POWER, NdisDeviceStateD0); miniport = &handle->driver->miniport_char; /* According NDIS, pnp_event_notify should be called whenever power * is set to D0 * Only NDIS 5.1 drivers are required to supply this function; some * drivers don't seem to support it (at least Orinoco) */ /* if (miniport->pnp_event_notify) { DBGTRACE3("%s", "calling pnp_event_notify"); miniport->pnp_event_notify(handle, NDIS_PNP_PROFILE_CHANGED, &profile_inf, sizeof(profile_inf)); } */ /* IPW2200 devices turn off radio if reset is called */ if (pdev->vendor != 0x8086) miniport_reset(handle); /* Wait a little to let card power up otherwise ifup might fail after boot */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/2); if (setup_dev(handle->net_dev)) { ERROR("couldn't setup network device"); res = -EINVAL; goto out_setup; } atomic_inc(&driver->users); TRACEEXIT1(return 0);out_setup: miniport_halt(handle);out_start: pci_release_regions(pdev);out_regions: pci_disable_device(pdev);out_enable: free_netdev(dev);out_nodev: TRACEEXIT1(return res);}/* * Remove one PCI-card. */static void __devexit ndiswrapper_remove_one_pci_dev(struct pci_dev *pdev){ struct ndis_handle *handle; TRACEENTER1("%p", pdev); handle = (struct ndis_handle *)pci_get_drvdata(pdev); TRACEENTER1("%p", handle); if (!handle) TRACEEXIT1(return); atomic_dec(&handle->driver->users); ndiswrapper_remove_one_dev(handle); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL);}#ifdef CONFIG_USB#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)static int ndiswrapper_add_one_usb_dev(struct usb_interface *intf, const struct usb_device_id *usb_id)#elsestatic void *ndiswrapper_add_one_usb_dev(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *usb_id)#endif{ int res; struct ndis_device *device; struct ndis_driver *driver; struct ndis_handle *handle; struct net_device *dev; struct miniport_char *miniport;// unsigned long profile_inf = NDIS_POWER_PROFILE_AC; TRACEENTER1("vendor: %04x, product: %04x", usb_id->idVendor, usb_id->idProduct); device = &ndis_devices[usb_id->driver_info]; driver = ndiswrapper_load_driver(device); if (!driver) { res = -ENODEV; goto out_nodev; } dev = ndis_init_netdev(&handle, device, driver); if (!dev) { ERROR("couldn't initialize network device"); res = -ENOMEM; goto out_nodev; }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) SET_NETDEV_DEV(dev, &intf->dev); handle->dev.usb = interface_to_usbdev(intf); handle->intf = intf; usb_set_intfdata(intf, handle);#else handle->dev.usb = udev;#endif TRACEENTER1("calling ndis init routine"); if ((res = miniport_init(handle))) { ERROR("Windows driver couldn't initialize the device (%08X)", res); res = -EINVAL; goto out_start; } handle->hw_status = 0; handle->wrapper_work = 0; /* do we need to power up the card explicitly? */ miniport_set_int(handle, OID_PNP_SET_POWER, NdisDeviceStateD0); miniport = &handle->driver->miniport_char; /* if (miniport->pnp_event_notify) { DBGTRACE3("%s", "calling pnp_event_notify"); miniport->pnp_event_notify(handle->adapter_ctx, NDIS_PNP_PROFILE_CHANGED, &profile_inf, sizeof(profile_inf)); DBGTRACE3("%s", "done"); } */ miniport_reset(handle); /* wait here seems crucial; without this delay, at least * prism54 driver crashes (why?) */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(3*HZ); if (setup_dev(handle->net_dev)) { ERROR("couldn't setup network device"); res = -EINVAL; goto out_setup; } atomic_inc(&driver->users);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) TRACEEXIT1(return 0);#else TRACEEXIT1(return handle);#endifout_setup: miniport_halt(handle);out_start: free_netdev(dev);out_nodev:#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) TRACEEXIT1(return res);#else TRACEEXIT1(return NULL);#endif}#endif // CONFIG_USB#ifdef CONFIG_USB#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)static voidndiswrapper_remove_one_usb_dev(struct usb_interface *intf){ struct ndis_handle *handle; TRACEENTER1(""); handle = (struct ndis_handle *)usb_get_intfdata(intf); if (!handle) TRACEEXIT1(return); usb_set_intfdata(intf, NULL); atomic_dec(&handle->driver->users); ndiswrapper_remove_one_dev(handle);}#elsestatic voidndiswrapper_remove_one_usb_dev(struct usb_device *udev, void *ptr){ struct ndis_handle *handle = (struct ndis_handle *)ptr; TRACEENTER1(""); if (!handle || !handle->dev.usb) TRACEEXIT1(return); handle->dev.usb = NULL; atomic_dec(&handle->driver->users); ndiswrapper_remove_one_dev(handle);}#endif#endif /* CONFIG_USB *//* load the driver files from userspace. */static int load_sys_files(struct ndis_driver *driver, struct load_driver *load_driver){ int i, err; TRACEENTER1(""); DBGTRACE1("num_pe_images = %d", load_driver->nr_sys_files); DBGTRACE1("loading driver: %s", load_driver->name); memcpy(driver->name, load_driver->name, MAX_DRIVER_NAME_LEN); DBGTRACE1("driver: %s", driver->name); err = 0; driver->num_pe_images = 0; for (i = 0; i < load_driver->nr_sys_files; i++) { struct pe_image *pe_image; pe_image = &driver->pe_images[driver->num_pe_images]; pe_image->name[MAX_DRIVER_NAME_LEN-1] = 0; memcpy(pe_image->name, load_driver->sys_files[i].name, MAX_DRIVER_NAME_LEN); DBGTRACE1("image size: %lu bytes", (unsigned long)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 pe_image->image = vmalloc(load_driver->sys_files[i].size);#endif if (!pe_image->image) { ERROR("couldn't allocate memory"); break; } DBGTRACE1("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); break; } pe_image->size = load_driver->sys_files[i].size; driver->num_pe_images++; } if (load_pe_images(driver->pe_images, driver->num_pe_images)) { ERROR("unable to prepare driver '%s'", load_driver->name); err = -EINVAL; } if (driver->num_pe_images < load_driver->nr_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; TRACEEXIT1(return -EINVAL); } else { TRACEEXIT1(return 0); }}/* load firmware files from userspace */static int load_bin_files(struct ndis_driver *driver, struct load_driver *load_driver){ struct ndis_bin_file *bin_files; int i; TRACEENTER1("loading bin files for driver %s", load_driver->name); bin_files = kmalloc(load_driver->nr_bin_files * sizeof(*bin_files), GFP_KERNEL); if (!bin_files) { ERROR("couldn't allocate memory"); TRACEEXIT1(return -ENOMEM); } memset(bin_files, 0, load_driver->nr_bin_files * sizeof(*bin_files)); driver->num_bin_files = 0; for (i = 0; i < load_driver->nr_bin_files; i++) { struct ndis_bin_file *bin_file = &bin_files[i]; struct load_driver_file *load_bin_file = &load_driver->bin_files[i]; memcpy(bin_file->name, load_bin_file->name, MAX_DRIVER_NAME_LEN); bin_file->size = load_bin_file->size; bin_file->data = vmalloc(load_bin_file->size); if (!bin_file->data) { ERROR("cound't allocate memory"); break; } if (copy_from_user(bin_file->data, load_bin_file->data, load_bin_file->size)) { ERROR("couldn't load file %s", load_bin_file->name); break; } DBGTRACE2("loaded bin file %s", bin_file->name); driver->num_bin_files++; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -