/*
module/drivers.c
functions for manipulating drivers
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define _GNU_SOURCE
#define __NO_VERSION__
#include "comedi_fops.h"
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/usb.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/highmem.h> /* for SuSE brokenness */
#include <linux/vmalloc.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <asm/system.h>
#include "comedidev.h"
#include "internal.h"
static int postconfig(struct comedi_device *dev);
static int insn_rw_emulate_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
static void *comedi_recognize(struct comedi_driver *driv, const char *name);
static void comedi_report_boards(struct comedi_driver *driv);
static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s);
struct comedi_driver *comedi_drivers;
static void cleanup_device(struct comedi_device *dev)
{
int i;
struct comedi_subdevice *s;
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
s = dev->subdevices + i;
comedi_free_subdevice_minor(s);
if (s->async) {
comedi_buf_alloc(dev, s, 0);
kfree(s->async);
}
}
kfree(dev->subdevices);
dev->subdevices = NULL;
dev->n_subdevices = 0;
}
kfree(dev->private);
dev->private = NULL;
dev->driver = NULL;
dev->board_name = NULL;
dev->board_ptr = NULL;
dev->iobase = 0;
dev->irq = 0;
dev->read_subdev = NULL;
dev->write_subdev = NULL;
dev->open = NULL;
dev->close = NULL;
comedi_set_hw_dev(dev, NULL);
}
static void __comedi_device_detach(struct comedi_device *dev)
{
dev->attached = 0;
if (dev->driver)
dev->driver->detach(dev);
else
printk(KERN_WARNING
"BUG: dev->driver=NULL in comedi_device_detach()\n");
cleanup_device(dev);
}
void comedi_device_detach(struct comedi_device *dev)
{
if (!dev->attached)
return;
__comedi_device_detach(dev);
}
int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
struct comedi_driver *driv;
int ret;
if (dev->attached)
return -EBUSY;
for (driv = comedi_drivers; driv; driv = driv->next) {
if (!try_module_get(driv->module)) {
printk(KERN_INFO "comedi: failed to increment module count, skipping\n");
continue;
}
if (driv->num_names) {
dev->board_ptr = comedi_recognize(driv, it->board_name);
if (dev->board_ptr == NULL) {
module_put(driv->module);
continue;
}
} else {
if (strcmp(driv->driver_name, it->board_name)) {
module_put(driv->module);
continue;
}
}
/* initialize dev->driver here so
* comedi_error() can be called from attach */
dev->driver = driv;
ret = driv->attach(dev, it);
if (ret < 0) {
module_put(dev->driver->module);
__comedi_device_detach(dev);
return ret;
}
goto attached;
}
/* recognize has failed if we get here */
/* report valid board names before returning error */
for (driv = comedi_drivers; driv; driv = driv->next) {
if (!try_module_get(driv->module)) {
printk(KERN_INFO
"comedi: failed to increment module count\n");
continue;
}
comedi_report_boards(driv);
module_put(driv->module);
}
return -EIO;
attached:
/* do a little post-config cleanup */
ret = postconfig(dev);
module_put(dev->driver->module);
if (ret < 0) {
__comedi_device_detach(dev);
return ret;
}
if (!dev->board_name) {
printk(KERN_WARNING "BUG: dev->board_name=<%p>\n",
dev->board_name);
dev->board_n