Logo Search packages:      
Sourcecode: ndiswrapper version File versions

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 "ndis.h"
#include "loader.h"
#include "wrapndis.h"
#include "pnp.h"

#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>

/*
  Network adapter: ClassGuid = {4d36e972-e325-11ce-bfc1-08002be10318}
  Network client: ClassGuid = {4d36e973-e325-11ce-bfc1-08002be10318}
  PCMCIA adapter: ClassGuid = {4d36e977-e325-11ce-bfc1-08002be10318}
  USB: ClassGuid = {36fc9e60-c465-11cf-8056-444553540000}
*/

/* the indices used here must match macros WRAP_NDIS_DEVICE etc. */
static struct guid class_guids[] = {
      /* Network */
      {0x4d36e972, 0xe325, 0x11ce, },
      /* USB WDM */
      {0x36fc9e60, 0xc465, 0x11cf, },
      /* Bluetooth */
      {0xe0cbf06c, 0xcd8b, 0x4647, },
      /* ivtcorporatino.com's bluetooth device claims this is
       * bluetooth guid */
      {0xf12d3cf8, 0xb11d, 0x457e, },
};

struct semaphore loader_mutex;
static struct completion loader_complete;

static struct nt_list wrap_devices;
static struct nt_list wrap_drivers;
static struct pci_device_id wrap_pci_device;
static struct pci_driver wrap_pci_driver;
#if defined(CONFIG_USB)
static struct usb_device_id wrap_usb_device;
struct usb_driver wrap_usb_driver;
#endif

int wrap_device_type(int data1)
{
      int i;
      for (i = 0; i < sizeof(class_guids) / sizeof(class_guids[0]); i++)
            if (data1 == class_guids[i].data1)
                  return i;
      ERROR("unknown device: 0x%x\n", data1);
      return -1;
}

/* load driver for given device, if not already loaded */
struct wrap_driver *load_wrap_driver(struct wrap_device *wd)
{
      int ret;
      struct nt_list *cur;
      struct wrap_driver *wrap_driver;

      ENTER1("device: %04X:%04X:%04X:%04X", wd->vendor, wd->device,
             wd->subvendor, wd->subdevice);
      if (down_interruptible(&loader_mutex)) {
            WARNING("couldn't obtain loader_mutex");
            EXIT1(return NULL);
      }
      wrap_driver = NULL;
      nt_list_for_each(cur, &wrap_drivers) {
            wrap_driver = container_of(cur, struct wrap_driver, list);
            if (!stricmp(wrap_driver->name, wd->driver_name)) {
                  TRACE1("driver %s already loaded", wrap_driver->name);
                  break;
            } else
                  wrap_driver = NULL;
      }
      up(&loader_mutex);

      if (!wrap_driver) {
            char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DRIVER,
#if defined(DEBUG) && DEBUG >= 1
                        "1",
#else
                        "0",
#endif
                        UTILS_VERSION, wd->driver_name,
                        wd->conf_file_name, NULL};
            char *env[] = {NULL};

            TRACE1("loading driver %s", wd->driver_name);
            if (down_interruptible(&loader_mutex)) {
                  WARNING("couldn't obtain loader_mutex");
                  EXIT1(return NULL);
            }
            INIT_COMPLETION(loader_complete);
            ret = call_usermodehelper("/sbin/loadndisdriver", argv, env
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
                                , 1
#endif
                  );
            if (ret) {
                  up(&loader_mutex);
                  ERROR("couldn't load driver %s; check system log "
                        "for messages from 'loadndisdriver'",
                        wd->driver_name);
                  EXIT1(return NULL);
            }
            wait_for_completion(&loader_complete);
            TRACE1("%s", wd->driver_name);
            wrap_driver = NULL;
            nt_list_for_each(cur, &wrap_drivers) {
                  wrap_driver = container_of(cur, struct wrap_driver,
                                       list);
                  if (!stricmp(wrap_driver->name, wd->driver_name)) {
                        wd->driver = wrap_driver;
                        break;
                  } else
                        wrap_driver = NULL;
            }
            up(&loader_mutex);
            if (wrap_driver)
                  TRACE1("driver %s is loaded", wrap_driver->name);
            else
                  ERROR("couldn't load driver '%s'", wd->driver_name);
      }
      EXIT1(return wrap_driver);
}

/* load the driver files from userspace. */
static int load_sys_files(struct wrap_driver *driver,
                    struct load_driver *load_driver)
{
      int i, err;

      TRACE1("num_pe_images = %d", load_driver->nr_sys_files);
      TRACE1("loading driver: %s", load_driver->name);
      strncpy(driver->name, load_driver->name, sizeof(driver->name));
      driver->name[sizeof(driver->name)-1] = 0;
      TRACE1("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];

            strncpy(pe_image->name, load_driver->sys_files[i].name,
                  sizeof(pe_image->name));
            pe_image->name[sizeof(pe_image->name)-1] = 0;
            TRACE1("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
            /* hate to play with kernel macros, but PAGE_KERNEL_EXEC is
             * not available to modules! */
#ifdef cpu_has_nx
            if (cpu_has_nx)
                  pe_image->image =
                        __vmalloc(load_driver->sys_files[i].size,
                                GFP_KERNEL | __GFP_HIGHMEM,
                                __pgprot(__PAGE_KERNEL & ~_PAGE_NX));
            else
                  pe_image->image =
                        vmalloc(load_driver->sys_files[i].size);
#else
                  pe_image->image =
                        vmalloc(load_driver->sys_files[i].size);
#endif
#endif
            if (!pe_image->image) {
                  ERROR("couldn't allocate memory");
                  err = -ENOMEM;
                  break;
            }
            TRACE1("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);
                  err = -EFAULT;
                  break;
            }
            pe_image->size = load_driver->sys_files[i].size;
            driver->num_pe_images++;
      }

      if (!err && link_pe_images(driver->pe_images, driver->num_pe_images)) {
            ERROR("couldn't 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;
            EXIT1(return err);
      } else
            EXIT1(return 0);
}

struct wrap_bin_file *get_bin_file(char *bin_file_name)
{
      int i = 0;
      struct wrap_driver *driver, *cur;

      ENTER1("%s", bin_file_name);
      if (down_interruptible(&loader_mutex)) {
            WARNING("couldn't obtain loader_mutex");
            EXIT1(return NULL);
      }
      driver = NULL;
      nt_list_for_each_entry(cur, &wrap_drivers, list) {
            for (i = 0; i < cur->num_bin_files; i++)
                  if (!stricmp(cur->bin_files[i].name, bin_file_name)) {
                        driver = cur;
                        break;
                  }
            if (driver)
                  break;
      }
      up(&loader_mutex);
      if (!driver) {
            TRACE1("coudln't find bin file '%s'", bin_file_name);
            return NULL;
      }

      if (!driver->bin_files[i].data) {
            char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_BIN_FILE,
#if defined(DEBUG) && DEBUG >= 1
                        "1",
#else
                        "0",
#endif
                        UTILS_VERSION, driver->name,
                        driver->bin_files[i].name, NULL};
            char *env[] = {NULL};
            int ret;

            TRACE1("loading bin file %s/%s", driver->name,
                   driver->bin_files[i].name);
            if (down_interruptible(&loader_mutex)) {
                  WARNING("couldn't obtain loader_mutex");
                  EXIT1(return NULL);
            }
            INIT_COMPLETION(loader_complete);
            ret = call_usermodehelper("/sbin/loadndisdriver", argv, env
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
                                , 1
#endif
                  );
            if (ret) {
                  up(&loader_mutex);
                  ERROR("couldn't load file %s/%s; check system log "
                        "for messages from 'loadndisdriver'",
                        driver->name, driver->bin_files[i].name);
                  EXIT1(return NULL);
            }
            wait_for_completion(&loader_complete);
            up(&loader_mutex);
            if (!driver->bin_files[i].data) {
                  WARNING("couldn't load binary file %s",
                        driver->bin_files[i].name);
                  EXIT1(return NULL);
            }
      }
      EXIT2(return &(driver->bin_files[i]));
}

/* called with loader_mutex down */
static int add_bin_file(struct load_driver_file *driver_file)
{
      struct wrap_driver *driver, *cur;
      struct wrap_bin_file *bin_file;
      int i = 0;

      driver = NULL;
      nt_list_for_each_entry(cur, &wrap_drivers, list) {
            for (i = 0; i < cur->num_bin_files; i++)
                  if (!stricmp(cur->bin_files[i].name,
                             driver_file->name)) {
                        driver = cur;
                        break;
                  }
            if (driver)
                  break;
      }
      if (!driver) {
            ERROR("couldn't find %s", driver_file->name);
            return -EINVAL;
      }
      bin_file = &driver->bin_files[i];
      strncpy(bin_file->name, driver_file->name, sizeof(bin_file->name));
      bin_file->name[sizeof(bin_file->name)-1] = 0;
      bin_file->data = vmalloc(driver_file->size);
      if (!bin_file->data) {
            ERROR("couldn't allocate memory");
            return -ENOMEM;
      }
      bin_file->size = driver_file->size;
      if (copy_from_user(bin_file->data, driver_file->data, bin_file->size)) {
            ERROR("couldn't copy data");
            free_bin_file(bin_file);
            return -EFAULT;
      }
      return 0;
}

void free_bin_file(struct wrap_bin_file *bin_file)
{
      TRACE2("unloading %s", bin_file->name);
      if (bin_file->data)
            vfree(bin_file->data);
      bin_file->data = NULL;
      bin_file->size = 0;
      EXIT2(return);
}

/* load firmware files from userspace */
static int load_bin_files_info(struct wrap_driver *driver,
                         struct load_driver *load_driver)
{
      struct wrap_bin_file *bin_files;
      int i;

      ENTER1("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");
            EXIT1(return -ENOMEM);
      }
      memset(bin_files, 0, load_driver->nr_bin_files * sizeof(*bin_files));

      for (i = 0; i < load_driver->nr_bin_files; i++) {
            strncpy(bin_files[i].name, load_driver->bin_files[i].name,
                  sizeof(bin_files[i].name));
            bin_files[i].name[sizeof(bin_files[i].name)-1] = 0;
            TRACE2("loaded bin file %s", bin_files[i].name);
      }
      driver->num_bin_files = load_driver->nr_bin_files;
      driver->bin_files = bin_files;
      EXIT1(return 0);
}

/* load settnigs for a device. called with loader_mutex down */
static int load_settings(struct wrap_driver *wrap_driver,
                   struct load_driver *load_driver)
{
      int i, nr_settings;

      ENTER1("%p, %p", wrap_driver, load_driver);

      nr_settings = 0;
      for (i = 0; i < load_driver->nr_settings; i++) {
            struct load_device_setting *load_setting =
                  &load_driver->settings[i];
            struct wrap_device_setting *setting;
            ULONG data1;

            setting = kmalloc(sizeof(*setting), GFP_KERNEL);
            if (!setting) {
                  ERROR("couldn't allocate memory");
                  break;
            }
            memset(setting, 0, sizeof(*setting));
            strncpy(setting->name, load_setting->name,
                  sizeof(setting->name));
            setting->name[sizeof(setting->name)-1] = 0;
            strncpy(setting->value, load_setting->value,
                   sizeof(setting->value));
            setting->value[sizeof(setting->value)-1] = 0;
            TRACE2("setting %s=%s", setting->name, setting->value);

            if (strcmp(setting->name, "driver_version") == 0) {
                  strncpy(wrap_driver->version, setting->value,
                        sizeof(wrap_driver->version));
                  wrap_driver->version[sizeof(wrap_driver->version)-1] = 0;
            } else if (strcmp(setting->name, "class_guid") == 0 &&
                     sscanf(setting->value, "%x", &data1) == 1) {
                  wrap_driver->dev_type = wrap_device_type(data1);
                  if (wrap_driver->dev_type < 0) {
                        WARNING("unknown guid: %x", data1);
                        wrap_driver->dev_type = 0;
                  }
            }
            InsertTailList(&wrap_driver->settings, &setting->list);
            nr_settings++;
      }
      /* it is not a fatal error if some settings couldn't be loaded */
      if (nr_settings > 0)
            EXIT1(return 0);
      else
            EXIT1(return -EINVAL);
}

void unload_wrap_device(struct wrap_device *wd)
{
      struct nt_list *cur;
      ENTER1("unloading device %p (%04X:%04X:%04X:%04X), driver %s", wd,
             wd->vendor, wd->device, wd->subvendor, wd->subdevice,
             wd->driver_name);
      if (down_interruptible(&loader_mutex))
            WARNING("couldn't obtain loader_mutex");
      while ((cur = RemoveHeadList(&wd->settings))) {
            struct wrap_device_setting *setting;
            setting = container_of(cur, struct wrap_device_setting, list);
            kfree(setting);
      }
      RemoveEntryList(&wd->list);
      up(&loader_mutex);
      kfree(wd);
      EXIT1(return);
}

/* should be called with loader_mutex down */
void unload_wrap_driver(struct wrap_driver *driver)
{
      int i;
      struct driver_object *drv_obj;
      struct wrap_device_setting *setting;

      ENTER1("unloading driver: %s (%p)", driver->name, driver);
      TRACE1("freeing %d images", driver->num_pe_images);
      drv_obj = driver->drv_obj;
      for (i = 0; i < driver->num_pe_images; i++)
            if (driver->pe_images[i].image) {
                  TRACE1("freeing image at %p",
                         driver->pe_images[i].image);
                  vfree(driver->pe_images[i].image);
            }

      TRACE1("freeing %d bin files", driver->num_bin_files);
      for (i = 0; i < driver->num_bin_files; i++) {
            TRACE1("freeing image at %p", driver->bin_files[i].data);
            if (driver->bin_files[i].data)
                  vfree(driver->bin_files[i].data);
      }
      if (driver->bin_files)
            kfree(driver->bin_files);
      RtlFreeUnicodeString(&drv_obj->name);
      RemoveEntryList(&driver->list);
      nt_list_for_each_entry(setting, &driver->settings, list) {
            struct ndis_configuration_parameter *param;
            param = setting->encoded;
            if (param) {
                  if (param->type == NdisParameterString)
                        RtlFreeUnicodeString(&param->data.string);
                  ExFreePool(param);
                  setting->encoded = NULL;
            }
      }
      /* this frees driver */
      free_custom_extensions(drv_obj->drv_ext);
      kfree(drv_obj->drv_ext);
      TRACE1("drv_obj: %p", drv_obj);

      EXIT1(return);
}

/* call the entry point of the driver */
static int start_wrap_driver(struct wrap_driver *driver)
{
      int i;
      NTSTATUS ret, res;
      struct driver_object *drv_obj;
      typeof(driver->pe_images[0].entry) entry;

      ENTER1("%s", driver->name);
      drv_obj = driver->drv_obj;
      for (ret = res = 0, i = 0; i < driver->num_pe_images; i++)
            /* dlls are already started by loader */
            if (driver->pe_images[i].type == IMAGE_FILE_EXECUTABLE_IMAGE) {
                  entry = driver->pe_images[i].entry;
                  drv_obj->start = driver->pe_images[i].entry;
                  drv_obj->driver_size = driver->pe_images[i].size;
                  TRACE1("entry: %p, %p, drv_obj: %p",
                         entry, *entry, drv_obj);
                  res = LIN2WIN2(entry, drv_obj, &drv_obj->name);
                  ret |= res;
                  TRACE1("entry returns %08X", res);
                  break;
            }
      if (ret) {
            ERROR("driver initialization failed: %08X", ret);
            RtlFreeUnicodeString(&drv_obj->name);
            /* this frees ndis_driver */
            free_custom_extensions(drv_obj->drv_ext);
            kfree(drv_obj->drv_ext);
            TRACE1("drv_obj: %p", drv_obj);
            ObDereferenceObject(drv_obj);
            EXIT1(return -EINVAL);
      }
      EXIT1(return 0);
}

/*
 * add driver to list of loaded driver but make sure this driver is
 * not loaded before. called with loader_mutex down
 */
static int add_wrap_driver(struct wrap_driver *driver)
{
      struct wrap_driver *tmp;

      ENTER1("name: %s", driver->name);
      nt_list_for_each_entry(tmp, &wrap_drivers, list) {
            if (stricmp(tmp->name, driver->name) == 0) {
                  ERROR("cannot add duplicate driver");
                  EXIT1(return -EBUSY);
            }
      }
      InsertHeadList(&wrap_drivers, &driver->list);
      EXIT1(return 0);
}

/* load a driver from userspace and initialize it. called with
 * loader_mutex down */
static int load_user_space_driver(struct load_driver *load_driver)
{
      struct driver_object *drv_obj;
      struct ansi_string ansi_reg;
      struct wrap_driver *wrap_driver = NULL;

      ENTER1("%p", load_driver);
      drv_obj = allocate_object(sizeof(*drv_obj), OBJECT_TYPE_DRIVER, NULL);
      if (!drv_obj) {
            ERROR("couldn't allocate memory");
            EXIT1(return -ENOMEM);
      }
      TRACE1("drv_obj: %p", drv_obj);
      drv_obj->drv_ext = kmalloc(sizeof(*(drv_obj->drv_ext)), GFP_KERNEL);
      if (!drv_obj->drv_ext) {
            ERROR("couldn't allocate memory");
            ObDereferenceObject(drv_obj);
            EXIT1(return -ENOMEM);
      }
      memset(drv_obj->drv_ext, 0, sizeof(*(drv_obj->drv_ext)));
      InitializeListHead(&drv_obj->drv_ext->custom_ext);
      if (IoAllocateDriverObjectExtension(drv_obj,
                                  (void *)WRAP_DRIVER_CLIENT_ID,
                                  sizeof(*wrap_driver),
                                  (void **)&wrap_driver) !=
          STATUS_SUCCESS)
            EXIT1(return -ENOMEM);
      TRACE1("driver: %p", wrap_driver);
      memset(wrap_driver, 0, sizeof(*wrap_driver));
      InitializeListHead(&wrap_driver->list);
      InitializeListHead(&wrap_driver->settings);
      InitializeListHead(&wrap_driver->wrap_devices);
      wrap_driver->drv_obj = drv_obj;
      RtlInitAnsiString(&ansi_reg, "/tmp");
      if (RtlAnsiStringToUnicodeString(&drv_obj->name, &ansi_reg, TRUE) !=
          STATUS_SUCCESS) {
            ERROR("couldn't initialize registry path");
            free_custom_extensions(drv_obj->drv_ext);
            kfree(drv_obj->drv_ext);
            TRACE1("drv_obj: %p", drv_obj);
            ObDereferenceObject(drv_obj);
            EXIT1(return -EINVAL);
      }
      strncpy(wrap_driver->name, load_driver->name, sizeof(wrap_driver->name));
      wrap_driver->name[sizeof(wrap_driver->name)-1] = 0;
      if (load_sys_files(wrap_driver, load_driver) ||
          load_bin_files_info(wrap_driver, load_driver) ||
          load_settings(wrap_driver, load_driver) ||
          start_wrap_driver(wrap_driver) ||
          add_wrap_driver(wrap_driver)) {
            unload_wrap_driver(wrap_driver);
            EXIT1(return -EINVAL);
      } else {
            printk(KERN_INFO "%s: driver %s (%s) loaded\n",
                   DRIVER_NAME, wrap_driver->name, wrap_driver->version);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
            add_taint(TAINT_PROPRIETARY_MODULE);
            /* older kernels don't seem to have a way to set
             * tainted information */
#endif
            EXIT1(return 0);
      }
}

/* register drivers for pci and usb */
static void register_devices(void)
{
      int res;

      memset(&wrap_pci_device, 0, sizeof(wrap_pci_device));
      wrap_pci_device.vendor = PCI_ANY_ID;
      wrap_pci_device.device = PCI_ANY_ID;
      wrap_pci_device.subvendor = PCI_ANY_ID;
      wrap_pci_device.subdevice = PCI_ANY_ID;

      memset(&wrap_pci_driver, 0, sizeof(wrap_pci_driver));
      wrap_pci_driver.name = DRIVER_NAME;
      wrap_pci_driver.id_table = &wrap_pci_device;
      wrap_pci_driver.probe = wrap_pnp_start_pci_device;
      wrap_pci_driver.remove = __devexit_p(wrap_pnp_remove_pci_device);
      wrap_pci_driver.suspend = wrap_pnp_suspend_pci_device;
      wrap_pci_driver.resume = wrap_pnp_resume_pci_device;
      res = pci_register_driver(&wrap_pci_driver);
      if (res < 0) {
            ERROR("couldn't register pci driver: %d", res);
            wrap_pci_driver.name = NULL;
      }

#ifdef CONFIG_USB
      memset(&wrap_usb_device, 0, sizeof(wrap_usb_device));
      wrap_usb_device.driver_info = 1;

      memset(&wrap_usb_driver, 0, sizeof(wrap_usb_driver));
      wrap_usb_driver.name = DRIVER_NAME;
      wrap_usb_driver.id_table = &wrap_usb_device;
      wrap_usb_driver.probe = wrap_pnp_start_usb_device;
      wrap_usb_driver.disconnect = __devexit_p(wrap_pnp_remove_usb_device);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
      wrap_usb_driver.suspend = wrap_pnp_suspend_usb_device;
      wrap_usb_driver.resume = wrap_pnp_resume_usb_device;
#endif
      res = usb_register(&wrap_usb_driver);
      if (res < 0) {
            ERROR("couldn't register usb driver: %d", res);
            wrap_usb_driver.name = NULL;
      }
#endif
      EXIT1(return);
}

static void unregister_devices(void)
{
      struct nt_list *cur, *next;

      if (down_interruptible(&loader_mutex))
            WARNING("couldn't obtain loader_mutex");
      nt_list_for_each_safe(cur, next, &wrap_devices) {
            struct wrap_device *wd;
            wd = container_of(cur, struct wrap_device, list);
            wd->surprise_removed = FALSE;
      }
      up(&loader_mutex);

      if (wrap_pci_driver.name)
            pci_unregister_driver(&wrap_pci_driver);
#ifdef CONFIG_USB
      if (wrap_usb_driver.name)
            usb_deregister(&wrap_usb_driver);
#endif
}

struct wrap_device *load_wrap_device(struct load_device *load_device)
{
      int ret;
      struct nt_list *cur;
      struct wrap_device *wd = NULL;
      char vendor[5], device[5], subvendor[5], subdevice[5], bus[5];

      ENTER1("%04x, %04x, %04x, %04x", load_device->vendor,
             load_device->device, load_device->subvendor,
             load_device->subdevice);
      if (sprintf(vendor, "%04x", load_device->vendor) == 4 &&
          sprintf(device, "%04x", load_device->device) == 4 &&
          sprintf(subvendor, "%04x", load_device->subvendor) == 4 &&
          sprintf(subdevice, "%04x", load_device->subdevice) == 4 &&
          sprintf(bus, "%04x", load_device->bus) == 4) {
            char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DEVICE,
#if defined(DEBUG) && DEBUG >= 1
                        "1",
#else
                        "0",
#endif
                        UTILS_VERSION, vendor, device,
                        subvendor, subdevice, bus, NULL};
            char *env[] = {NULL};
            TRACE2("%s, %s, %s, %s, %s", vendor, device,
                   subvendor, subdevice, bus);
            if (down_interruptible(&loader_mutex)) {
                  WARNING("couldn't obtain loader_mutex");
                  EXIT1(return NULL);
            }
            INIT_COMPLETION(loader_complete);
            ret = call_usermodehelper("/sbin/loadndisdriver", argv, env
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
                                , 1
#endif
                  );
            if (ret) {
                  up(&loader_mutex);
                  TRACE1("couldn't load device %04x:%04x; check system "
                         "log for messages from 'loadndisdriver'",
                         load_device->vendor, load_device->device);
                  EXIT1(return NULL);
            }
            wait_for_completion(&loader_complete);
            wd = NULL;
            nt_list_for_each(cur, &wrap_devices) {
                  wd = container_of(cur, struct wrap_device, list);
                  TRACE2("%p, %04x, %04x, %04x, %04x", wd, wd->vendor,
                         wd->device, wd->subvendor, wd->subdevice);
                  if (wd->vendor == load_device->vendor &&
                      wd->device == load_device->device)
                        break;
                  else
                        wd = NULL;
            }
            up(&loader_mutex);
      } else
            wd = NULL;
      EXIT1(return wd);
}

struct wrap_device *get_wrap_device(void *dev, int bus)
{
      struct nt_list *cur;
      struct wrap_device *wd;

      if (down_interruptible(&loader_mutex)) {
            WARNING("couldn't obtain loader_mutex");
            return NULL;
      }
      wd = NULL;
      nt_list_for_each(cur, &wrap_devices) {
            wd = container_of(cur, struct wrap_device, list);
            if (bus == WRAP_PCI_BUS &&
                wrap_is_pci_bus(wd->dev_bus) && wd->pci.pdev == dev)
                  break;
            else if (bus == WRAP_USB_BUS &&
                   wrap_is_usb_bus(wd->dev_bus) && wd->usb.udev == dev)
                  break;
            else
                  wd = NULL;
      }
      up(&loader_mutex);
      return wd;
}

/* called with loader_mutex is down */
static int wrapper_ioctl(struct inode *inode, struct file *file,
                   unsigned int cmd, unsigned long arg)
{
      struct load_driver *load_driver;
      struct load_device load_device;
      struct load_driver_file load_bin_file;
      int ret;

      ENTER1("cmd: %u", cmd);

      ret = 0;
      switch (cmd) {
      case WRAP_IOCTL_LOAD_DEVICE:
            if (copy_from_user(&load_device, (void *)arg,
                           sizeof(load_device))) {
                  ret = -EFAULT;
                  break;
            }
            TRACE2("%04x, %04x, %04x, %04x", load_device.vendor,
                   load_device.device, load_device.subvendor,
                   load_device.subdevice);
            if (load_device.vendor) {
                  struct wrap_device *wd;
                  wd = kmalloc(sizeof(*wd), GFP_KERNEL);
                  if (!wd) {
                        ret = -ENOMEM;
                        break;
                  }
                  memset(wd, 0, sizeof(*wd));
                  InitializeListHead(&wd->settings);
                  wd->dev_bus = WRAP_BUS(load_device.bus);
                  wd->vendor = load_device.vendor;
                  wd->device = load_device.device;
                  wd->subvendor = load_device.subvendor;
                  wd->subdevice = load_device.subdevice;
                  strncpy(wd->conf_file_name, load_device.conf_file_name,
                        sizeof(wd->conf_file_name));
                  wd->conf_file_name[sizeof(wd->conf_file_name)-1] = 0;
                  strncpy(wd->driver_name, load_device.driver_name,
                         sizeof(wd->driver_name));
                  wd->driver_name[sizeof(wd->driver_name)-1] = 0;
                  InsertHeadList(&wrap_devices, &wd->list);
                  ret = 0;
            } else
                  ret = -EINVAL;
            break;
      case WRAP_IOCTL_LOAD_DRIVER:
            TRACE1("loading driver at %p", (void *)arg);
            load_driver = vmalloc(sizeof(*load_driver));
            if (!load_driver) {
                  ret = -ENOMEM;
                  break;
            }
            if (copy_from_user(load_driver, (void *)arg,
                           sizeof(*load_driver)))
                  ret = -EFAULT;
            else
                  ret = load_user_space_driver(load_driver);
            vfree(load_driver);
            break;
      case WRAP_IOCTL_LOAD_BIN_FILE:
            if (copy_from_user(&load_bin_file, (void *)arg,
                           sizeof(load_bin_file)))
                  ret = -EFAULT;
            else
                  ret = add_bin_file(&load_bin_file);
            break;
      default:
            ERROR("unknown ioctl %u", cmd);
            ret = -EINVAL;
            break;
      }
      complete(&loader_complete);
      EXIT1(return ret);
}

static int wrapper_ioctl_release(struct inode *inode, struct file *file)
{
      ENTER1("");
      return 0;
}

static struct file_operations wrapper_fops = {
      .owner          = THIS_MODULE,
      .ioctl            = wrapper_ioctl,
      .release    = wrapper_ioctl_release,
};

static struct miscdevice wrapper_misc = {
      .name   = DRIVER_NAME,
      .minor      = MISC_DYNAMIC_MINOR,
      .fops   = &wrapper_fops
};

int loader_init(void)
{
      int err;

      InitializeListHead(&wrap_drivers);
      InitializeListHead(&wrap_devices);
      init_MUTEX(&loader_mutex);
      init_completion(&loader_complete);
      if ((err = misc_register(&wrapper_misc)) < 0 ) {
            ERROR("couldn't register module (%d)", err);
            unregister_devices();
            EXIT1(return err);
      }
      register_devices();
      EXIT1(return 0);
}

void loader_exit(void)
{
      struct nt_list *cur, *next;

      ENTER1("");
      misc_deregister(&wrapper_misc);
      unregister_devices();
      if (down_interruptible(&loader_mutex))
            WARNING("couldn't obtain loader_mutex");
      nt_list_for_each_safe(cur, next, &wrap_drivers) {
            struct wrap_driver *driver;
            driver = container_of(cur, struct wrap_driver, list);
            unload_wrap_driver(driver);
      }
      up(&loader_mutex);
      EXIT1(return);
}

Generated by  Doxygen 1.6.0   Back to index