patch-2.3.14 linux/drivers/pnp/isapnp_proc.c

Next file: linux/drivers/scsi/53c7xx.c
Previous file: linux/drivers/pnp/isapnp.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.13/linux/drivers/pnp/isapnp_proc.c linux/drivers/pnp/isapnp_proc.c
@@ -0,0 +1,945 @@
+/*
+ *  ISA Plug & Play support
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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.
+ *
+ */
+
+static void *isapnp_alloc(long size);
+struct pci_bus *isapnp_cards;
+struct pci_dev *isapnp_devices;
+
+struct isapnp_info_buffer {
+	char *buffer;		/* pointer to begin of buffer */
+	char *curr;		/* current position in buffer */
+	unsigned long size;	/* current size */
+	unsigned long len;	/* total length of buffer */
+	int stop;		/* stop flag */
+	int error;		/* error code */
+};
+
+typedef struct isapnp_info_buffer isapnp_info_buffer_t;
+
+static struct proc_dir_entry *isapnp_proc_entry = NULL;
+
+static void isapnp_info_read(isapnp_info_buffer_t *buffer);
+static void isapnp_info_write(isapnp_info_buffer_t *buffer);
+
+int isapnp_printf(isapnp_info_buffer_t * buffer, char *fmt,...)
+{
+	va_list args;
+	int res;
+	char sbuffer[512];
+
+	if (buffer->stop || buffer->error)
+		return 0;
+	va_start(args, fmt);
+	res = vsprintf(sbuffer, fmt, args);
+	va_end(args);
+	if (buffer->size + res >= buffer->len) {
+		buffer->stop = 1;
+		return 0;
+	}
+	strcpy(buffer->curr, sbuffer);
+	buffer->curr += res;
+	buffer->size += res;
+	return res;
+}
+
+static loff_t isapnp_info_entry_lseek(struct file *file, loff_t offset, int orig)
+{
+	switch (orig) {
+	case 0:	/* SEEK_SET */
+		file->f_pos = offset;
+		return file->f_pos;
+	case 1:	/* SEEK_CUR */
+		file->f_pos += offset;
+		return file->f_pos;
+	case 2:	/* SEEK_END */
+	default:
+		return -EINVAL;
+	}
+	return -ENXIO;
+}
+
+static ssize_t isapnp_info_entry_read(struct file *file, char *buffer,
+				      size_t count, loff_t * offset)
+{
+	isapnp_info_buffer_t *buf;
+	long size = 0, size1;
+	int mode;
+
+	mode = file->f_flags & O_ACCMODE;
+	if (mode != O_RDONLY)
+		return -EINVAL;
+	buf = (isapnp_info_buffer_t *) file->private_data;
+	if (!buf)
+		return -EIO;
+	if (file->f_pos >= buf->size)
+		return 0;
+	size = buf->size < count ? buf->size : count;
+	size1 = buf->size - file->f_pos;
+	if (size1 < size)
+		size = size1;
+	if (copy_to_user(buffer, buf->buffer + file->f_pos, size))
+		return -EFAULT;
+	file->f_pos += size;
+	return size;
+}
+
+static ssize_t isapnp_info_entry_write(struct file *file, const char *buffer,
+				       size_t count, loff_t * offset)
+{
+	isapnp_info_buffer_t *buf;
+	long size = 0, size1;
+	int mode;
+
+	mode = file->f_flags & O_ACCMODE;
+	if (mode != O_WRONLY)
+		return -EINVAL;
+	buf = (isapnp_info_buffer_t *) file->private_data;
+	if (!buf)
+		return -EIO;
+	if (file->f_pos < 0)
+		return -EINVAL;
+	if (file->f_pos >= buf->len)
+		return -ENOMEM;
+	size = buf->len < count ? buf->len : count;
+	size1 = buf->len - file->f_pos;
+	if (size1 < size)
+		size = size1;
+	if (copy_from_user(buf->buffer + file->f_pos, buffer, size))
+		return -EFAULT;
+	if (buf->size < file->f_pos + size)
+		buf->size = file->f_pos + size;
+	file->f_pos += size;
+	return size;
+}
+
+static int isapnp_info_entry_open(struct inode *inode, struct file *file)
+{
+	isapnp_info_buffer_t *buffer;
+	int mode;
+
+	mode = file->f_flags & O_ACCMODE;
+	if (mode != O_RDONLY && mode != O_WRONLY)
+		return -EINVAL;
+	buffer = (isapnp_info_buffer_t *)
+				isapnp_alloc(sizeof(isapnp_info_buffer_t));
+	if (!buffer)
+		return -ENOMEM;
+	buffer->len = 4 * PAGE_SIZE;
+	buffer->buffer = vmalloc(buffer->len);
+	if (!buffer->buffer) {
+		kfree(buffer);
+		return -ENOMEM;
+	}
+	buffer->curr = buffer->buffer;
+	file->private_data = buffer;
+	MOD_INC_USE_COUNT;
+	if (mode == O_RDONLY)
+		isapnp_info_read(buffer);
+	return 0;
+}
+
+static int isapnp_info_entry_release(struct inode *inode, struct file *file)
+{
+	isapnp_info_buffer_t *buffer;
+	int mode;
+
+	if ((buffer = (isapnp_info_buffer_t *) file->private_data) == NULL)
+		return -EINVAL;
+	mode = file->f_flags & O_ACCMODE;
+	if (mode == O_WRONLY)
+		isapnp_info_write(buffer);
+	vfree(buffer->buffer);
+	kfree(buffer);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static unsigned int isapnp_info_entry_poll(struct file *file, poll_table * wait)
+{
+	if (!file->private_data)
+		return 0;
+	return POLLIN | POLLRDNORM;
+}
+
+static int isapnp_info_entry_ioctl(struct inode *inode, struct file *file,
+				   unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+static int isapnp_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	return -ENXIO;
+}
+
+static struct file_operations isapnp_info_entry_operations =
+{
+	isapnp_info_entry_lseek,	/* lseek */
+	isapnp_info_entry_read,		/* read */
+	isapnp_info_entry_write,	/* write */
+	NULL,				/* readdir */
+	isapnp_info_entry_poll,		/* poll */
+	isapnp_info_entry_ioctl,	/* ioctl - default */
+	isapnp_info_entry_mmap,		/* mmap */
+	isapnp_info_entry_open,		/* open */
+	NULL,				/* flush */
+	isapnp_info_entry_release,	/* release */
+	NULL,				/* can't fsync */
+	NULL,				/* fasync */
+	NULL,				/* check_media_change */
+	NULL,				/* revalidate */
+	NULL,				/* lock */
+};
+
+static struct inode_operations isapnp_info_entry_inode_operations =
+{
+	&isapnp_info_entry_operations,	/* default sound info directory file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+__initfunc(static int isapnp_proc_init(void))
+{
+	struct proc_dir_entry *p;
+
+	isapnp_proc_entry = NULL;
+	p = create_proc_entry("isapnp", S_IFREG | S_IRUGO | S_IWUSR, &proc_root);
+	if (!p)
+		return -ENOMEM;
+	p->ops = &isapnp_info_entry_inode_operations;
+	isapnp_proc_entry = p;
+	return 0;
+}
+
+#ifdef MODULE
+static int isapnp_proc_done(void)
+{
+	if (isapnp_proc_entry)
+		proc_unregister(&proc_root, isapnp_proc_entry->low_ino);
+	return 0;
+}
+#endif /* MODULE */
+
+/*
+ *
+ */
+
+static void isapnp_print_devid(isapnp_info_buffer_t *buffer, unsigned short vendor, unsigned short device)
+{
+	char tmp[8];
+	
+	sprintf(tmp, "%c%c%c%x%x%x%x",
+			'A' + ((vendor >> 2) & 0x3f) - 1,
+			'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1,
+			'A' + ((vendor >> 8) & 0x1f) - 1,
+			(device >> 4) & 0x0f,
+			device & 0x0f,
+			(device >> 12) & 0x0f,
+			(device >> 8) & 0x0f);
+	isapnp_printf(buffer, tmp);
+}
+
+static void isapnp_print_compatible(isapnp_info_buffer_t *buffer, struct pci_dev *dev)
+{
+	int idx;
+
+	for (idx = 0; idx < DEVICE_COUNT_COMPATIBLE; idx++) {
+		if (dev->vendor_compatible[idx] == 0)
+			continue;
+		isapnp_printf(buffer, "    Compatible device ");
+		isapnp_print_devid(buffer,
+				   dev->vendor_compatible[idx],
+				   dev->device_compatible[idx]);
+		isapnp_printf(buffer, "\n");
+	}
+}
+
+static void isapnp_print_port(isapnp_info_buffer_t *buffer, char *space, struct isapnp_port *port)
+{
+	isapnp_printf(buffer, "%sPort 0x%x-0x%x, align 0x%x, size 0x%x, %i-bit address decoding\n",
+			space, port->min, port->max, port->align ? (port->align-1) : 0, port->size,
+			port->flags & ISAPNP_PORT_FLAG_16BITADDR ? 16 : 10);
+}
+
+static void isapnp_print_irq(isapnp_info_buffer_t *buffer, char *space, struct isapnp_irq *irq)
+{
+	int first = 1, i;
+
+	isapnp_printf(buffer, "%sIRQ ", space);
+	for (i = 0; i < 16; i++)
+		if (irq->map & (1<<i)) {
+			if (!first) {
+				isapnp_printf(buffer, ",");
+			} else {
+				first = 0;
+			}
+			if (i == 2 || i == 9)
+				isapnp_printf(buffer, "2/9");
+			else
+				isapnp_printf(buffer, "%i", i);
+		}
+	if (!irq->map)
+		isapnp_printf(buffer, "<none>");
+	if (irq->flags & DEVICE_IRQ_FLAG_HIGHEDGE)
+		isapnp_printf(buffer, " High-Edge");
+	if (irq->flags & DEVICE_IRQ_FLAG_LOWEDGE)
+		isapnp_printf(buffer, " Low-Edge");
+	if (irq->flags & DEVICE_IRQ_FLAG_HIGHLEVEL)
+		isapnp_printf(buffer, " High-Level");
+	if (irq->flags & DEVICE_IRQ_FLAG_LOWLEVEL)
+		isapnp_printf(buffer, " Low-Level");
+	isapnp_printf(buffer, "\n");
+}
+
+static void isapnp_print_dma(isapnp_info_buffer_t *buffer, char *space, struct isapnp_dma *dma)
+{
+	int first = 1, i;
+	char *s;
+
+	isapnp_printf(buffer, "%sDMA ", space);
+	for (i = 0; i < 8; i++)
+		if (dma->map & (1<<i)) {
+			if (!first) {
+				isapnp_printf(buffer, ",");
+			} else {
+				first = 0;
+			}
+			isapnp_printf(buffer, "%i", i);
+		}
+	if (!dma->map)
+		isapnp_printf(buffer, "<none>");
+	switch (dma->type) {
+	case DEVICE_DMA_TYPE_8BIT:
+		s = "8-bit";
+		break;
+	case DEVICE_DMA_TYPE_8AND16BIT:
+		s = "8-bit&16-bit";
+		break;
+	default:
+		s = "16-bit";
+	}
+	isapnp_printf(buffer, " %s", s);
+	if (dma->flags & DEVICE_DMA_FLAG_MASTER)
+		isapnp_printf(buffer, " master");
+	if (dma->flags & DEVICE_DMA_FLAG_BYTE)
+		isapnp_printf(buffer, " byte-count");
+	if (dma->flags & DEVICE_DMA_FLAG_WORD)
+		isapnp_printf(buffer, " word-count");
+	switch (dma->speed) {
+	case DEVICE_DMA_SPEED_TYPEA:
+		s = "type-A";
+		break;
+	case DEVICE_DMA_SPEED_TYPEB:
+		s = "type-B";
+		break;
+	case DEVICE_DMA_SPEED_TYPEF:
+		s = "type-F";
+		break;
+	default:
+		s = "compatible";
+		break;
+	}
+	isapnp_printf(buffer, " %s\n", s);
+}
+
+static void isapnp_print_mem(isapnp_info_buffer_t *buffer, char *space, struct isapnp_mem *mem)
+{
+	char *s;
+
+	isapnp_printf(buffer, "%sMemory 0x%x-0x%x, align 0x%x, size 0x%x",
+			space, mem->min, mem->max, mem->align, mem->size);
+	if (mem->flags & DEVICE_IO_FLAG_WRITEABLE)
+		isapnp_printf(buffer, ", writeable");
+	if (mem->flags & DEVICE_IO_FLAG_CACHEABLE)
+		isapnp_printf(buffer, ", cacheable");
+	if (mem->flags & DEVICE_IO_FLAG_RANGELENGTH)
+		isapnp_printf(buffer, ", range-length");
+	if (mem->flags & DEVICE_IO_FLAG_SHADOWABLE)
+		isapnp_printf(buffer, ", shadowable");
+	if (mem->flags & DEVICE_IO_FLAG_EXPANSIONROM)
+		isapnp_printf(buffer, ", expansion ROM");
+	switch (mem->type) {
+	case DEVICE_IO_TYPE_8BIT:
+		s = "8-bit";
+		break;
+	case DEVICE_IO_TYPE_8AND16BIT:
+		s = "8-bit&16-bit";
+		break;
+	default:
+		s = "16-bit";
+	}
+	isapnp_printf(buffer, ", %s\n", s);
+}
+
+static void isapnp_print_mem32(isapnp_info_buffer_t *buffer, char *space, struct isapnp_mem32 *mem32)
+{
+	int first = 1, i;
+
+	isapnp_printf(buffer, "%s32-bit memory ", space);
+	for (i = 0; i < 17; i++) {
+		if (first) {
+			first = 0;
+		} else {
+			isapnp_printf(buffer, ":");
+		}
+		isapnp_printf(buffer, "%02x", mem32->data[i]);
+	}
+}
+
+static void isapnp_print_resources(isapnp_info_buffer_t *buffer, char *space, struct isapnp_resources *res)
+{
+	char *s;
+	struct isapnp_port *port;
+	struct isapnp_irq *irq;
+	struct isapnp_dma *dma;
+	struct isapnp_mem *mem;
+	struct isapnp_mem32 *mem32;
+
+	switch (res->priority) {
+	case ISAPNP_RES_PRIORITY_PREFERRED:
+		s = "preferred";
+		break;
+	case ISAPNP_RES_PRIORITY_ACCEPTABLE:
+		s = "acceptable";
+		break;
+	case ISAPNP_RES_PRIORITY_FUNCTIONAL:
+		s = "functional";
+		break;
+	default:
+		s = "invalid";
+	}
+	isapnp_printf(buffer, "%sPriority %s\n", space, s);
+	for (port = res->port; port; port = port->next)
+		isapnp_print_port(buffer, space, port);
+	for (irq = res->irq; irq; irq = irq->next)
+		isapnp_print_irq(buffer, space, irq);
+	for (dma = res->dma; dma; dma = dma->next)
+		isapnp_print_dma(buffer, space, dma);
+	for (mem = res->mem; mem; mem = mem->next)
+		isapnp_print_mem(buffer, space, mem);
+	for (mem32 = res->mem32; mem32; mem32 = mem32->next)
+		isapnp_print_mem32(buffer, space, mem32);
+}
+
+static void isapnp_print_configuration(isapnp_info_buffer_t *buffer, struct pci_dev *dev)
+{
+	int i, tmp, next;
+	char *space = "    ";
+
+	isapnp_cfg_begin(dev->bus->number, dev->devfn);
+	isapnp_printf(buffer, "%sDevice is %sactive\n",
+			space, isapnp_read_byte(ISAPNP_CFG_ACTIVATE)?"":"not ");
+	for (i = next = 0; i < 8; i++) {
+		tmp = isapnp_read_word(ISAPNP_CFG_PORT + (i << 1));
+		if (!tmp)
+			continue;
+		if (!next) {
+			isapnp_printf(buffer, "%sActive port ", space);
+			next = 1;
+		}
+		isapnp_printf(buffer, "%s0x%x", i > 0 ? "," : "", tmp);
+	}
+	if (next)
+		isapnp_printf(buffer, "\n");
+	for (i = next = 0; i < 2; i++) {
+		tmp = isapnp_read_word(ISAPNP_CFG_IRQ + (i << 1));
+		if (!(tmp >> 8))
+			continue;
+		if (!next) {
+			isapnp_printf(buffer, "%sActive IRQ ", space);
+			next = 1;
+		}
+		isapnp_printf(buffer, "%s%i", i > 0 ? "," : "", tmp >> 8);
+		if (tmp & 0xff)
+			isapnp_printf(buffer, " [0x%x]", tmp & 0xff);
+	}
+	if (next)
+		isapnp_printf(buffer, "\n");
+	for (i = next = 0; i < 2; i++) {
+		tmp = isapnp_read_byte(ISAPNP_CFG_DMA + i);
+		if (tmp == 4)
+			continue;
+		if (!next) {
+			isapnp_printf(buffer, "%sActive DMA ", space);
+			next = 1;
+		}
+		isapnp_printf(buffer, "%s%i", i > 0 ? "," : "", tmp);
+	}
+	if (next)
+		isapnp_printf(buffer, "\n");
+	for (i = next = 0; i < 4; i++) {
+		tmp = isapnp_read_dword(ISAPNP_CFG_MEM + (i << 3));
+		if (!tmp)
+			continue;
+		if (!next) {
+			isapnp_printf(buffer, "%sActive memory ", space);
+			next = 1;
+		}
+		isapnp_printf(buffer, "%s0x%x", i > 0 ? "," : "", tmp);
+	}
+	if (next)
+		isapnp_printf(buffer, "\n");
+	isapnp_cfg_end();
+}
+
+static void isapnp_print_device(isapnp_info_buffer_t *buffer, struct pci_dev *dev)
+{
+	int block, block1;
+	char *space = "    ";
+	struct isapnp_resources *res, *resa;
+
+	if (!dev)
+		return;
+	isapnp_printf(buffer, "  Logical device %i '", dev->devfn);
+	isapnp_print_devid(buffer, dev->vendor, dev->device);
+	isapnp_printf(buffer, ":%s'", dev->name[0]?dev->name:"Unknown");
+	isapnp_printf(buffer, "\n");
+#if 0
+	isapnp_cfg_begin(dev->bus->number, dev->devfn);
+	for (block = 0; block < 128; block++)
+		if ((block % 16) == 15)
+			isapnp_printf(buffer, "%02x\n", isapnp_read_byte(block));
+		else
+			isapnp_printf(buffer, "%02x:", isapnp_read_byte(block));
+	isapnp_cfg_end();
+#endif
+	if (dev->regs)
+		isapnp_printf(buffer, "%sSupported registers 0x%x\n", space, dev->regs);
+	isapnp_print_compatible(buffer, dev);
+	isapnp_print_configuration(buffer, dev);
+	for (res = (struct isapnp_resources *)dev->sysdata, block = 0; res; res = res->next, block++) {
+		isapnp_printf(buffer, "%sResources %i\n", space, block);
+		isapnp_print_resources(buffer, "      ", res);
+		for (resa = res->alt, block1 = 1; resa; resa = resa->alt, block1++) {
+			isapnp_printf(buffer, "%s  Alternate resources %i:%i\n", space, block, block1);
+			isapnp_print_resources(buffer, "        ", resa);
+		}
+	}
+}
+
+/*
+ *  Main read routine
+ */
+ 
+static void isapnp_info_read(isapnp_info_buffer_t *buffer)
+{
+	struct pci_bus *card;
+	struct pci_dev *dev;
+	
+	for (card = isapnp_cards; card; card = card->next) {
+		isapnp_printf(buffer, "Card %i '", card->number);
+		isapnp_print_devid(buffer, card->vendor, card->device);
+		isapnp_printf(buffer, ":%s'", card->name[0]?card->name:"Unknown");
+		if (card->pnpver)
+			isapnp_printf(buffer, " PnP version %x.%x", card->pnpver >> 4, card->pnpver & 0x0f);
+		if (card->productver)
+			isapnp_printf(buffer, " Product version %x.%x", card->productver >> 4, card->productver & 0x0f);
+		isapnp_printf(buffer,"\n");
+		for (dev = card->devices; dev; dev = dev->sibling)
+			isapnp_print_device(buffer, dev);
+	}
+}
+
+/*
+ *
+ */
+
+static struct pci_bus *isapnp_info_card;
+static struct pci_dev *isapnp_info_device;
+
+static char *isapnp_get_str(char *dest, char *src, int len)
+{
+	int c;
+
+	while (*src == ' ' || *src == '\t')
+		src++;
+	if (*src == '"' || *src == '\'') {
+		c = *src++;
+		while (--len > 0 && *src && *src != c) {
+			*dest++ = *src++;
+		}
+		if (*src == c)
+			src++;
+	} else {
+		while (--len > 0 && *src && *src != ' ' && *src != '\t') {
+			*dest++ = *src++;
+		}
+	}
+	*dest = 0;
+	while (*src == ' ' || *src == '\t')
+		src++;
+	return src;
+}
+
+static unsigned char isapnp_get_hex(unsigned char c)
+{
+	if (c >= '0' || c <= '9')
+		return c - '0';
+	if (c >= 'a' || c <= 'f')
+		return (c - 'a') + 10;
+	if (c >= 'A' || c <= 'F')
+		return (c - 'A') + 10;
+	return 0;
+}
+
+static unsigned int isapnp_parse_id(const char *id)
+{
+	if (strlen(id) != 7) {
+		printk("isapnp: wrong PnP ID\n");
+		return 0;
+	}
+	return (ISAPNP_VENDOR(id[0], id[1], id[2])<<16) |
+			(isapnp_get_hex(id[3])<<4) |
+			(isapnp_get_hex(id[4])<<0) |
+			(isapnp_get_hex(id[5])<<12) |
+			(isapnp_get_hex(id[6])<<8);
+}
+
+static int isapnp_set_card(char *line)
+{
+	int idx, idx1;
+	unsigned int id;
+	char index[16], value[32];
+
+	isapnp_info_card = NULL;
+	line = isapnp_get_str(index, line, sizeof(index));
+	isapnp_get_str(value, line, sizeof(value));
+	idx = idx1 = simple_strtoul(index, NULL, 0);
+	id = isapnp_parse_id(value);
+	isapnp_info_card = isapnp_find_card(id >> 16, id & 0xffff, NULL);
+	while (isapnp_info_card && idx1-- > 0)
+		isapnp_info_card = isapnp_find_card(id >> 16, id & 0xffff, isapnp_info_card);
+	if (isapnp_info_card == NULL) {
+		printk("isapnp: card '%s' order %i not found\n", value, idx);
+		return 1;
+	}
+	if (isapnp_cfg_begin(isapnp_info_card->number, -1)<0) {
+		printk("isapnp: configuration start sequence for device '%s' failed\n", value);
+		isapnp_info_card = NULL;
+		return 1;
+	}
+	return 0;
+}
+
+static int isapnp_select_csn(char *line)
+{
+	int csn;
+	char index[16], value[32];
+
+	isapnp_info_device = NULL;
+	isapnp_get_str(index, line, sizeof(index));
+	csn = simple_strtoul(index, NULL, 0);
+	for (isapnp_info_card = isapnp_cards; isapnp_info_card; isapnp_info_card = isapnp_info_card->next)
+		if (isapnp_info_card->number == csn)
+			break;
+	if (isapnp_info_card == NULL) {
+		printk("isapnp: cannot find CSN %i\n", csn);
+		return 1;
+	}
+	if (isapnp_cfg_begin(isapnp_info_card->number, -1)<0) {
+		printk("isapnp: configuration start sequence for device '%s' failed\n", value);
+		isapnp_info_card = NULL;
+		return 1;
+	}
+	return 0;
+}
+
+static int isapnp_set_device(char *line)
+{
+	int idx, idx1;
+	unsigned int id;
+	char index[16], value[32];
+
+	line = isapnp_get_str(index, line, sizeof(index));
+	isapnp_get_str(value, line, sizeof(value));
+	idx = idx1 = simple_strtoul(index, NULL, 0);
+	id = isapnp_parse_id(value);
+	isapnp_info_device = isapnp_find_dev(isapnp_info_card, id >> 16, id & 0xffff, NULL);
+	while (isapnp_info_device && idx-- > 0)
+		isapnp_info_device = isapnp_find_dev(isapnp_info_card, id >> 16, id & 0xffff, isapnp_info_device);
+	if (isapnp_info_device == NULL) {
+		printk("isapnp: device '%s' order %i not found\n", value, idx);
+		return 1;
+	}
+	isapnp_device(isapnp_info_device->devfn);
+	return 0;
+}
+
+static int isapnp_autoconfigure(void)
+{
+	if (isapnp_info_device == NULL) {
+		printk("isapnp: device is not set\n");
+		return 0;
+	}
+	if (isapnp_info_device->active)
+		isapnp_info_device->deactivate(isapnp_info_device);
+	if (isapnp_info_device->prepare(isapnp_info_device) < 0) {
+		printk("isapnp: cannot prepare device for the activation");
+		return 0;
+	}
+	if (isapnp_info_device->activate(isapnp_info_device) < 0) {
+		printk("isapnp: cannot activate device");
+		return 0;
+	}
+	return 0;
+}
+
+static int isapnp_set_port(char *line)
+{
+	int idx, port;
+	char index[16], value[32];
+
+	line = isapnp_get_str(index, line, sizeof(index));
+	isapnp_get_str(value, line, sizeof(value));
+	idx = simple_strtoul(index, NULL, 0);
+	port = simple_strtoul(value, NULL, 0);
+	if (idx < 0 || idx > 7) {
+		printk("isapnp: wrong port index %i\n", idx);
+		return 1;
+	}
+	if (port < 0 || port > 0xffff) {
+		printk("isapnp: wrong port value 0x%x\n", port);
+		return 1;
+	}
+	isapnp_write_word(ISAPNP_CFG_PORT + (idx << 1), port);
+	if (isapnp_info_device->resource[idx].start == DEVICE_IO_NOTSET)
+		return 0;
+	if (isapnp_info_device->resource[idx].start == DEVICE_IO_AUTO) {
+		isapnp_info_device->resource[idx].start = port;
+		isapnp_info_device->resource[idx].end += port - 1;
+	} else {
+		isapnp_info_device->resource[idx].end -= isapnp_info_device->resource[idx].start;
+		isapnp_info_device->resource[idx].start = port;
+		isapnp_info_device->resource[idx].end += port;
+	}
+	return 0;
+}
+ 
+static int isapnp_set_irq(char *line)
+{
+	int idx, irq;
+	char index[16], value[32];
+
+	line = isapnp_get_str(index, line, sizeof(index));
+	isapnp_get_str(value, line, sizeof(value));
+	idx = simple_strtoul(index, NULL, 0);
+	irq = simple_strtoul(value, NULL, 0);
+	if (idx < 0 || idx > 1) {
+		printk("isapnp: wrong IRQ index %i\n", idx);
+		return 1;
+	}
+	if (irq == 2)
+		irq = 9;
+	if (irq < 0 || irq > 15) {
+		printk("isapnp: wrong IRQ value %i\n", irq);
+		return 1;
+	}
+	isapnp_write_byte(ISAPNP_CFG_IRQ + (idx << 1), irq);
+	if (idx == 0) {
+		 if (isapnp_info_device->irq == DEVICE_IRQ_NOTSET)
+		 	return 0;
+		 isapnp_info_device->irq = irq;
+	} else {
+		 if (isapnp_info_device->irq2 == DEVICE_IRQ_NOTSET)
+		 	return 0;
+		 isapnp_info_device->irq2 = irq;	
+	}
+	return 0;
+}
+ 
+static int isapnp_set_dma(char *line)
+{
+	int idx, dma;
+	char index[16], value[32];
+
+	line = isapnp_get_str(index, line, sizeof(index));
+	isapnp_get_str(value, line, sizeof(value));
+	idx = simple_strtoul(index, NULL, 0);
+	dma = simple_strtoul(value, NULL, 0);
+	if (idx < 0 || idx > 1) {
+		printk("isapnp: wrong DMA index %i\n", idx);
+		return 1;
+	}
+	if (dma < 0 || dma > 7) {
+		printk("isapnp: wrong DMA value %i\n", dma);
+		return 1;
+	}
+	isapnp_write_byte(ISAPNP_CFG_DMA + idx, dma);
+	if (isapnp_info_device->dma[idx] == DEVICE_DMA_NOTSET)
+		return 0;
+	isapnp_info_device->dma[idx] = dma;
+	return 0;
+}
+ 
+static int isapnp_set_mem(char *line)
+{
+	int idx;
+	unsigned int mem;
+	char index[16], value[32];
+
+	line = isapnp_get_str(index, line, sizeof(index));
+	isapnp_get_str(value, line, sizeof(value));
+	idx = simple_strtoul(index, NULL, 0);
+	mem = simple_strtoul(value, NULL, 0);
+	if (idx < 0 || idx > 3) {
+		printk("isapnp: wrong memory index %i\n", idx);
+		return 1;
+	}
+	mem >>= 8;
+	isapnp_write_word(ISAPNP_CFG_MEM + (idx<<2), mem & 0xffff);
+	if (isapnp_info_device->resource[idx + 8].start == DEVICE_IO_NOTSET)
+		return 0;
+	if (isapnp_info_device->resource[idx + 8].start == DEVICE_IO_AUTO) {
+		isapnp_info_device->resource[idx + 8].start = mem & ~0x00ffff00;
+		isapnp_info_device->resource[idx + 8].end += (mem & ~0x00ffff00) - 1;
+	} else {
+		isapnp_info_device->resource[idx + 8].end -= isapnp_info_device->resource[idx + 8].start;
+		isapnp_info_device->resource[idx + 8].start = mem & ~0x00ffff00;
+		isapnp_info_device->resource[idx + 8].end += mem & ~0x00ffff00;
+	}
+	return 0;
+}
+ 
+static int isapnp_poke(char *line, int what)
+{
+	int reg;
+	unsigned int val;
+	char index[16], value[32];
+
+	line = isapnp_get_str(index, line, sizeof(index));
+	isapnp_get_str(value, line, sizeof(value));
+	reg = simple_strtoul(index, NULL, 0);
+	val = simple_strtoul(value, NULL, 0);
+	if (reg < 0 || reg > 127) {
+		printk("isapnp: wrong register %i\n", reg);
+		return 1;
+	}
+	switch (what) {
+	case 1:
+		isapnp_write_word(reg, val);
+		break;
+	case 2:
+		isapnp_write_dword(reg, val);
+		break;
+	default:
+		isapnp_write_byte(reg, val);
+		break;
+	}
+	return 0;
+}
+ 
+static int isapnp_decode_line(char *line)
+{
+	char cmd[32];
+
+	line = isapnp_get_str(cmd, line, sizeof(cmd));
+	if (!strcmp(cmd, "card"))
+		return isapnp_set_card(line);
+	if (!strcmp(cmd, "csn"))
+		return isapnp_select_csn(line);
+	if (!isapnp_info_card) {
+		printk("isapnp: card is not selected\n");
+		return 1;
+	}
+	if (!strncmp(cmd, "dev", 3))
+		return isapnp_set_device(line);
+	if (!isapnp_info_device) {
+		printk("isapnp: device is not selected\n");
+		return 1;
+	}
+	if (!strncmp(cmd, "auto", 4))
+		return isapnp_autoconfigure();
+	if (!strncmp(cmd, "act", 3)) {
+		isapnp_activate(isapnp_info_device->devfn);
+		isapnp_info_device->active = 1;
+		return 0;
+	}
+	if (!strncmp(cmd, "deact", 5)) {
+		isapnp_deactivate(isapnp_info_device->devfn);
+		isapnp_info_device->active = 0;
+		return 0;
+	}
+	if (!strcmp(cmd, "port"))
+		return isapnp_set_port(line);
+	if (!strcmp(cmd, "irq"))
+		return isapnp_set_irq(line);
+	if (!strcmp(cmd, "dma"))
+		return isapnp_set_dma(line);
+	if (!strncmp(cmd, "mem", 3))
+		return isapnp_set_mem(line);
+	if (!strcmp(cmd, "poke"))
+		return isapnp_poke(line, 0);
+	if (!strcmp(cmd, "pokew"))
+		return isapnp_poke(line, 1);
+	if (!strcmp(cmd, "poked"))
+		return isapnp_poke(line, 2);
+	printk("isapnp: wrong command '%s'\n", cmd);
+	return 1;
+}
+
+/*
+ *  Main write routine
+ */
+
+static void isapnp_info_write(isapnp_info_buffer_t *buffer)
+{
+	int c, idx, idx1 = 0;
+	char line[128];
+
+	if (buffer->size <= 0)
+		return;
+	isapnp_info_card = NULL;
+	isapnp_info_device = NULL;
+	for (idx = 0; idx < buffer->size; idx++) {
+		c = buffer->buffer[idx];
+		if (c == '\n') {
+			line[idx1] = '\0';
+			if (line[0] != '#') {
+				if (isapnp_decode_line(line))
+					goto __end;
+			}
+			idx1 = 0;
+			continue;
+		}
+		if (idx1 >= sizeof(line)-1) {
+			printk("isapnp: line too long, aborting\n");
+			return;
+		}
+		line[idx1++] = c;
+	}
+      __end:
+	if (isapnp_info_card)
+		isapnp_cfg_end();
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)