1 /*
2  *  ISA Plug & Play support
3  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
4  *
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21 
22 #define __NO_VERSION__
23 
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/init.h>
27 #include <linux/proc_fs.h>
28 #include <linux/poll.h>
29 #include <linux/vmalloc.h>
30 #include <asm/uaccess.h>
31 #include <linux/smp_lock.h>
32 #include <linux/isapnp.h>
33 
34 struct isapnp_info_buffer {
35 	char *buffer;		/* pointer to begin of buffer */
36 	char *curr;		/* current position in buffer */
37 	unsigned long size;	/* current size */
38 	unsigned long len;	/* total length of buffer */
39 	int stop;		/* stop flag */
40 	int error;		/* error code */
41 };
42 
43 typedef struct isapnp_info_buffer isapnp_info_buffer_t;
44 
45 static struct proc_dir_entry *isapnp_proc_entry = NULL;
46 static struct proc_dir_entry *isapnp_proc_bus_dir = NULL;
47 static struct proc_dir_entry *isapnp_proc_devices_entry = NULL;
48 
49 static void isapnp_info_read(isapnp_info_buffer_t *buffer);
50 static void isapnp_info_write(isapnp_info_buffer_t *buffer);
51 
isapnp_printf(isapnp_info_buffer_t * buffer,char * fmt,...)52 int isapnp_printf(isapnp_info_buffer_t * buffer, char *fmt,...)
53 {
54 	va_list args;
55 	int res;
56 	char sbuffer[512];
57 
58 	if (buffer->stop || buffer->error)
59 		return 0;
60 	va_start(args, fmt);
61 	res = vsprintf(sbuffer, fmt, args);
62 	va_end(args);
63 	if (buffer->size + res >= buffer->len) {
64 		buffer->stop = 1;
65 		return 0;
66 	}
67 	strcpy(buffer->curr, sbuffer);
68 	buffer->curr += res;
69 	buffer->size += res;
70 	return res;
71 }
72 
isapnp_devid(char * str,unsigned short vendor,unsigned short device)73 static void isapnp_devid(char *str, unsigned short vendor, unsigned short device)
74 {
75 	sprintf(str, "%c%c%c%x%x%x%x",
76 			'A' + ((vendor >> 2) & 0x3f) - 1,
77 			'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1,
78 			'A' + ((vendor >> 8) & 0x1f) - 1,
79 			(device >> 4) & 0x0f,
80 			device & 0x0f,
81 			(device >> 12) & 0x0f,
82 			(device >> 8) & 0x0f);
83 }
84 
isapnp_info_entry_lseek(struct file * file,loff_t offset,int orig)85 static loff_t isapnp_info_entry_lseek(struct file *file, loff_t offset, int orig)
86 {
87 	switch (orig) {
88 	case 0:	/* SEEK_SET */
89 		file->f_pos = offset;
90 		return file->f_pos;
91 	case 1:	/* SEEK_CUR */
92 		file->f_pos += offset;
93 		return file->f_pos;
94 	case 2:	/* SEEK_END */
95 	default:
96 		return -EINVAL;
97 	}
98 	return -ENXIO;
99 }
100 
isapnp_info_entry_read(struct file * file,char * buffer,size_t count,loff_t * offset)101 static ssize_t isapnp_info_entry_read(struct file *file, char *buffer,
102 				      size_t count, loff_t * offset)
103 {
104 	isapnp_info_buffer_t *buf;
105 	loff_t pos = *offset;
106 	long size = 0, size1;
107 	int mode;
108 
109 	mode = file->f_flags & O_ACCMODE;
110 	if (mode != O_RDONLY)
111 		return -EINVAL;
112 	buf = (isapnp_info_buffer_t *) file->private_data;
113 	if (!buf)
114 		return -EIO;
115 	if (pos != (unsigned) pos || pos >= buf->size)
116 		return 0;
117 	size = buf->size < count ? buf->size : count;
118 	size1 = buf->size - pos;
119 	if (size1 < size)
120 		size = size1;
121 	if (copy_to_user(buffer, buf->buffer + pos, size))
122 		return -EFAULT;
123 	*offset = pos + size;
124 	return size;
125 }
126 
isapnp_info_entry_write(struct file * file,const char * buffer,size_t count,loff_t * offset)127 static ssize_t isapnp_info_entry_write(struct file *file, const char *buffer,
128 				       size_t count, loff_t * offset)
129 {
130 	isapnp_info_buffer_t *buf;
131 	long size = 0, size1;
132 	loff_t pos = *offset;
133 	int mode;
134 
135 	mode = file->f_flags & O_ACCMODE;
136 	if (mode != O_WRONLY)
137 		return -EINVAL;
138 	buf = (isapnp_info_buffer_t *) file->private_data;
139 	if (!buf)
140 		return -EIO;
141 	if (pos < 0)
142 		return -EINVAL;
143 	if (pos >= buf->len)
144 		return -ENOMEM;
145 	size = buf->len < count ? buf->len : count;
146 	size1 = buf->len - pos;
147 	if (size1 < size)
148 		size = size1;
149 	if (copy_from_user(buf->buffer + pos, buffer, size))
150 		return -EFAULT;
151 	if (buf->size < pos + size)
152 		buf->size = pos + size;
153 	*offset = pos + size;
154 	return size;
155 }
156 
isapnp_info_entry_open(struct inode * inode,struct file * file)157 static int isapnp_info_entry_open(struct inode *inode, struct file *file)
158 {
159 	isapnp_info_buffer_t *buffer;
160 	int mode;
161 
162 	mode = file->f_flags & O_ACCMODE;
163 	if (mode != O_RDONLY && mode != O_WRONLY)
164 		return -EINVAL;
165 	buffer = (isapnp_info_buffer_t *)
166 				isapnp_alloc(sizeof(isapnp_info_buffer_t));
167 	if (!buffer)
168 		return -ENOMEM;
169 	buffer->len = 4 * PAGE_SIZE;
170 	buffer->buffer = vmalloc(buffer->len);
171 	if (!buffer->buffer) {
172 		kfree(buffer);
173 		return -ENOMEM;
174 	}
175 	lock_kernel();
176 	buffer->curr = buffer->buffer;
177 	file->private_data = buffer;
178 	if (mode == O_RDONLY)
179 		isapnp_info_read(buffer);
180 	unlock_kernel();
181 	return 0;
182 }
183 
isapnp_info_entry_release(struct inode * inode,struct file * file)184 static int isapnp_info_entry_release(struct inode *inode, struct file *file)
185 {
186 	isapnp_info_buffer_t *buffer;
187 	int mode;
188 
189 	if ((buffer = (isapnp_info_buffer_t *) file->private_data) == NULL)
190 		return -EINVAL;
191 	mode = file->f_flags & O_ACCMODE;
192 	lock_kernel();
193 	if (mode == O_WRONLY)
194 		isapnp_info_write(buffer);
195 	vfree(buffer->buffer);
196 	kfree(buffer);
197 	unlock_kernel();
198 	return 0;
199 }
200 
isapnp_info_entry_poll(struct file * file,poll_table * wait)201 static unsigned int isapnp_info_entry_poll(struct file *file, poll_table * wait)
202 {
203 	if (!file->private_data)
204 		return 0;
205 	return POLLIN | POLLRDNORM;
206 }
207 
208 static struct file_operations isapnp_info_entry_operations =
209 {
210 	llseek:		isapnp_info_entry_lseek,
211 	read:		isapnp_info_entry_read,
212 	write:		isapnp_info_entry_write,
213 	poll:		isapnp_info_entry_poll,
214 	open:		isapnp_info_entry_open,
215 	release:	isapnp_info_entry_release,
216 };
217 
isapnp_proc_bus_lseek(struct file * file,loff_t off,int whence)218 static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence)
219 {
220 	loff_t new;
221 
222 	switch (whence) {
223 	case 0:
224 		new = off;
225 		break;
226 	case 1:
227 		new = file->f_pos + off;
228 		break;
229 	case 2:
230 		new = 256 + off;
231 		break;
232 	default:
233 		return -EINVAL;
234 	}
235 	if (new < 0 || new > 256)
236 		return -EINVAL;
237 	return (file->f_pos = new);
238 }
239 
isapnp_proc_bus_read(struct file * file,char * buf,size_t nbytes,loff_t * ppos)240 static ssize_t isapnp_proc_bus_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
241 {
242 	struct inode *ino = file->f_dentry->d_inode;
243 	struct proc_dir_entry *dp = ino->u.generic_ip;
244 	struct pci_dev *dev = dp->data;
245 	loff_t n = *ppos;
246 	unsigned pos = n;
247 	int cnt, size = 256;
248 
249 	if (pos != n || pos >= size)
250 		return 0;
251 	if (nbytes >= size)
252 		nbytes = size;
253 	if (nbytes > size - pos)
254 		nbytes = size - pos;
255 	cnt = nbytes;
256 
257 	if (!access_ok(VERIFY_WRITE, buf, cnt))
258 		return -EINVAL;
259 
260 	isapnp_cfg_begin(dev->bus->number, dev->devfn);
261 	for ( ; pos < 256 && cnt > 0; pos++, buf++, cnt--) {
262 		unsigned char val;
263 		val = isapnp_read_byte(pos);
264 		__put_user(val, buf);
265 	}
266 	isapnp_cfg_end();
267 
268 	*ppos = pos;
269 	return nbytes;
270 }
271 
272 static struct file_operations isapnp_proc_bus_file_operations =
273 {
274 	llseek:		isapnp_proc_bus_lseek,
275 	read:		isapnp_proc_bus_read,
276 };
277 
isapnp_proc_attach_device(struct pci_dev * dev)278 static int isapnp_proc_attach_device(struct pci_dev *dev)
279 {
280 	struct pci_bus *bus = dev->bus;
281 	struct proc_dir_entry *de, *e;
282 	char name[16];
283 
284 	if (!(de = bus->procdir)) {
285 		sprintf(name, "%02x", bus->number);
286 		de = bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir);
287 		if (!de)
288 			return -ENOMEM;
289 	}
290 	sprintf(name, "%02x", dev->devfn);
291 	e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO, de);
292 	if (!e)
293 		return -ENOMEM;
294 	e->proc_fops = &isapnp_proc_bus_file_operations;
295 	e->owner = THIS_MODULE;
296 	e->data = dev;
297 	e->size = 256;
298 	return 0;
299 }
300 
301 #ifdef MODULE
isapnp_proc_detach_device(struct pci_dev * dev)302 static int __exit isapnp_proc_detach_device(struct pci_dev *dev)
303 {
304 	struct pci_bus *bus = dev->bus;
305 	struct proc_dir_entry *de;
306 	char name[16];
307 
308 	if (!(de = bus->procdir))
309 		return -EINVAL;
310 	sprintf(name, "%02x", dev->devfn);
311 	remove_proc_entry(name, de);
312 	return 0;
313 }
314 
isapnp_proc_detach_bus(struct pci_bus * bus)315 static int __exit isapnp_proc_detach_bus(struct pci_bus *bus)
316 {
317 	struct proc_dir_entry *de;
318 	char name[16];
319 
320 	if (!(de = bus->procdir))
321 		return -EINVAL;
322 	sprintf(name, "%02x", bus->number);
323 	remove_proc_entry(name, isapnp_proc_bus_dir);
324 	return 0;
325 }
326 #endif
327 
isapnp_proc_read_devices(char * buf,char ** start,off_t pos,int count)328 static int isapnp_proc_read_devices(char *buf, char **start, off_t pos, int count)
329 {
330 	struct pci_dev *dev;
331 	off_t at = 0;
332 	int len, cnt, i;
333 
334 	cnt = 0;
335 	isapnp_for_each_dev(dev) {
336 		char bus_id[8], device_id[8];
337 
338 		isapnp_devid(bus_id, dev->bus->vendor, dev->bus->device);
339 		isapnp_devid(device_id, dev->vendor, dev->device);
340 		len = sprintf(buf, "%02x%02x\t%s%s\t",
341 			dev->bus->number,
342 			dev->devfn,
343 			bus_id,
344 			device_id);
345 		isapnp_cfg_begin(dev->bus->number, dev->devfn);
346 		len += sprintf(buf+len, "%02x", isapnp_read_byte(ISAPNP_CFG_ACTIVATE));
347 		for (i = 0; i < 8; i++)
348 			len += sprintf(buf+len, "%04x", isapnp_read_word(ISAPNP_CFG_PORT + (i << 1)));
349 		for (i = 0; i < 2; i++)
350 			len += sprintf(buf+len, "%04x", isapnp_read_word(ISAPNP_CFG_IRQ + (i << 1)));
351 		for (i = 0; i < 2; i++)
352 			len += sprintf(buf+len, "%04x", isapnp_read_word(ISAPNP_CFG_DMA + i));
353 		for (i = 0; i < 4; i++)
354 			len += sprintf(buf+len, "%08x", isapnp_read_dword(ISAPNP_CFG_MEM + (i << 3)));
355 		isapnp_cfg_end();
356 		buf[len++] = '\n';
357 		at += len;
358 		if (at >= pos) {
359 			if (!*start) {
360 				*start = buf + (pos - (at - len));
361 				cnt = at - pos;
362 			} else
363 				cnt += len;
364 			buf += len;
365 		}
366 	}
367 	return (count > cnt) ? cnt : count;
368 }
369 
isapnp_proc_init(void)370 int __init isapnp_proc_init(void)
371 {
372 	struct proc_dir_entry *p;
373 	struct pci_dev *dev;
374 
375 	isapnp_proc_entry = NULL;
376 	p = create_proc_entry("isapnp", S_IFREG | S_IRUGO | S_IWUSR, &proc_root);
377 	if (p) {
378 		p->proc_fops = &isapnp_info_entry_operations;
379 		p->owner = THIS_MODULE;
380 	}
381 	isapnp_proc_entry = p;
382 	isapnp_proc_bus_dir = proc_mkdir("isapnp", proc_bus);
383 	isapnp_proc_devices_entry = create_proc_info_entry("devices", 0,
384 							   isapnp_proc_bus_dir,
385 							   isapnp_proc_read_devices);
386 	isapnp_for_each_dev(dev) {
387 		isapnp_proc_attach_device(dev);
388 	}
389 	return 0;
390 }
391 
392 #ifdef MODULE
isapnp_proc_done(void)393 int __exit isapnp_proc_done(void)
394 {
395 	struct pci_dev *dev;
396 	struct pci_bus *card;
397 
398 	isapnp_for_each_dev(dev) {
399 		isapnp_proc_detach_device(dev);
400 	}
401 	isapnp_for_each_card(card) {
402 		isapnp_proc_detach_bus(card);
403 	}
404 	if (isapnp_proc_devices_entry)
405 		remove_proc_entry("devices", isapnp_proc_devices_entry);
406 	if (isapnp_proc_bus_dir)
407 		remove_proc_entry("isapnp", proc_bus);
408 	if (isapnp_proc_entry)
409 		remove_proc_entry("isapnp", &proc_root);
410 	return 0;
411 }
412 #endif /* MODULE */
413 
414 /*
415  *
416  */
417 
isapnp_print_devid(isapnp_info_buffer_t * buffer,unsigned short vendor,unsigned short device)418 static void isapnp_print_devid(isapnp_info_buffer_t *buffer, unsigned short vendor, unsigned short device)
419 {
420 	char tmp[8];
421 
422 	isapnp_devid(tmp, vendor, device);
423 	isapnp_printf(buffer, tmp);
424 }
425 
isapnp_print_compatible(isapnp_info_buffer_t * buffer,struct pci_dev * dev)426 static void isapnp_print_compatible(isapnp_info_buffer_t *buffer, struct pci_dev *dev)
427 {
428 	int idx;
429 
430 	for (idx = 0; idx < DEVICE_COUNT_COMPATIBLE; idx++) {
431 		if (dev->vendor_compatible[idx] == 0)
432 			continue;
433 		isapnp_printf(buffer, "    Compatible device ");
434 		isapnp_print_devid(buffer,
435 				   dev->vendor_compatible[idx],
436 				   dev->device_compatible[idx]);
437 		isapnp_printf(buffer, "\n");
438 	}
439 }
440 
isapnp_print_port(isapnp_info_buffer_t * buffer,char * space,struct isapnp_port * port)441 static void isapnp_print_port(isapnp_info_buffer_t *buffer, char *space, struct isapnp_port *port)
442 {
443 	isapnp_printf(buffer, "%sPort 0x%x-0x%x, align 0x%x, size 0x%x, %i-bit address decoding\n",
444 			space, port->min, port->max, port->align ? (port->align-1) : 0, port->size,
445 			port->flags & ISAPNP_PORT_FLAG_16BITADDR ? 16 : 10);
446 }
447 
isapnp_print_irq(isapnp_info_buffer_t * buffer,char * space,struct isapnp_irq * irq)448 static void isapnp_print_irq(isapnp_info_buffer_t *buffer, char *space, struct isapnp_irq *irq)
449 {
450 	int first = 1, i;
451 
452 	isapnp_printf(buffer, "%sIRQ ", space);
453 	for (i = 0; i < 16; i++)
454 		if (irq->map & (1<<i)) {
455 			if (!first) {
456 				isapnp_printf(buffer, ",");
457 			} else {
458 				first = 0;
459 			}
460 			if (i == 2 || i == 9)
461 				isapnp_printf(buffer, "2/9");
462 			else
463 				isapnp_printf(buffer, "%i", i);
464 		}
465 	if (!irq->map)
466 		isapnp_printf(buffer, "<none>");
467 	if (irq->flags & IORESOURCE_IRQ_HIGHEDGE)
468 		isapnp_printf(buffer, " High-Edge");
469 	if (irq->flags & IORESOURCE_IRQ_LOWEDGE)
470 		isapnp_printf(buffer, " Low-Edge");
471 	if (irq->flags & IORESOURCE_IRQ_HIGHLEVEL)
472 		isapnp_printf(buffer, " High-Level");
473 	if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
474 		isapnp_printf(buffer, " Low-Level");
475 	isapnp_printf(buffer, "\n");
476 }
477 
isapnp_print_dma(isapnp_info_buffer_t * buffer,char * space,struct isapnp_dma * dma)478 static void isapnp_print_dma(isapnp_info_buffer_t *buffer, char *space, struct isapnp_dma *dma)
479 {
480 	int first = 1, i;
481 	char *s;
482 
483 	isapnp_printf(buffer, "%sDMA ", space);
484 	for (i = 0; i < 8; i++)
485 		if (dma->map & (1<<i)) {
486 			if (!first) {
487 				isapnp_printf(buffer, ",");
488 			} else {
489 				first = 0;
490 			}
491 			isapnp_printf(buffer, "%i", i);
492 		}
493 	if (!dma->map)
494 		isapnp_printf(buffer, "<none>");
495 	switch (dma->flags & IORESOURCE_DMA_TYPE_MASK) {
496 	case IORESOURCE_DMA_8BIT:
497 		s = "8-bit";
498 		break;
499 	case IORESOURCE_DMA_8AND16BIT:
500 		s = "8-bit&16-bit";
501 		break;
502 	default:
503 		s = "16-bit";
504 	}
505 	isapnp_printf(buffer, " %s", s);
506 	if (dma->flags & IORESOURCE_DMA_MASTER)
507 		isapnp_printf(buffer, " master");
508 	if (dma->flags & IORESOURCE_DMA_BYTE)
509 		isapnp_printf(buffer, " byte-count");
510 	if (dma->flags & IORESOURCE_DMA_WORD)
511 		isapnp_printf(buffer, " word-count");
512 	switch (dma->flags & IORESOURCE_DMA_SPEED_MASK) {
513 	case IORESOURCE_DMA_TYPEA:
514 		s = "type-A";
515 		break;
516 	case IORESOURCE_DMA_TYPEB:
517 		s = "type-B";
518 		break;
519 	case IORESOURCE_DMA_TYPEF:
520 		s = "type-F";
521 		break;
522 	default:
523 		s = "compatible";
524 		break;
525 	}
526 	isapnp_printf(buffer, " %s\n", s);
527 }
528 
isapnp_print_mem(isapnp_info_buffer_t * buffer,char * space,struct isapnp_mem * mem)529 static void isapnp_print_mem(isapnp_info_buffer_t *buffer, char *space, struct isapnp_mem *mem)
530 {
531 	char *s;
532 
533 	isapnp_printf(buffer, "%sMemory 0x%x-0x%x, align 0x%x, size 0x%x",
534 			space, mem->min, mem->max, mem->align, mem->size);
535 	if (mem->flags & IORESOURCE_MEM_WRITEABLE)
536 		isapnp_printf(buffer, ", writeable");
537 	if (mem->flags & IORESOURCE_MEM_CACHEABLE)
538 		isapnp_printf(buffer, ", cacheable");
539 	if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
540 		isapnp_printf(buffer, ", range-length");
541 	if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
542 		isapnp_printf(buffer, ", shadowable");
543 	if (mem->flags & IORESOURCE_MEM_EXPANSIONROM)
544 		isapnp_printf(buffer, ", expansion ROM");
545 	switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
546 	case IORESOURCE_MEM_8BIT:
547 		s = "8-bit";
548 		break;
549 	case IORESOURCE_MEM_8AND16BIT:
550 		s = "8-bit&16-bit";
551 		break;
552 	default:
553 		s = "16-bit";
554 	}
555 	isapnp_printf(buffer, ", %s\n", s);
556 }
557 
isapnp_print_mem32(isapnp_info_buffer_t * buffer,char * space,struct isapnp_mem32 * mem32)558 static void isapnp_print_mem32(isapnp_info_buffer_t *buffer, char *space, struct isapnp_mem32 *mem32)
559 {
560 	int first = 1, i;
561 
562 	isapnp_printf(buffer, "%s32-bit memory ", space);
563 	for (i = 0; i < 17; i++) {
564 		if (first) {
565 			first = 0;
566 		} else {
567 			isapnp_printf(buffer, ":");
568 		}
569 		isapnp_printf(buffer, "%02x", mem32->data[i]);
570 	}
571 }
572 
isapnp_print_resources(isapnp_info_buffer_t * buffer,char * space,struct isapnp_resources * res)573 static void isapnp_print_resources(isapnp_info_buffer_t *buffer, char *space, struct isapnp_resources *res)
574 {
575 	char *s;
576 	struct isapnp_port *port;
577 	struct isapnp_irq *irq;
578 	struct isapnp_dma *dma;
579 	struct isapnp_mem *mem;
580 	struct isapnp_mem32 *mem32;
581 
582 	switch (res->priority) {
583 	case ISAPNP_RES_PRIORITY_PREFERRED:
584 		s = "preferred";
585 		break;
586 	case ISAPNP_RES_PRIORITY_ACCEPTABLE:
587 		s = "acceptable";
588 		break;
589 	case ISAPNP_RES_PRIORITY_FUNCTIONAL:
590 		s = "functional";
591 		break;
592 	default:
593 		s = "invalid";
594 	}
595 	isapnp_printf(buffer, "%sPriority %s\n", space, s);
596 	for (port = res->port; port; port = port->next)
597 		isapnp_print_port(buffer, space, port);
598 	for (irq = res->irq; irq; irq = irq->next)
599 		isapnp_print_irq(buffer, space, irq);
600 	for (dma = res->dma; dma; dma = dma->next)
601 		isapnp_print_dma(buffer, space, dma);
602 	for (mem = res->mem; mem; mem = mem->next)
603 		isapnp_print_mem(buffer, space, mem);
604 	for (mem32 = res->mem32; mem32; mem32 = mem32->next)
605 		isapnp_print_mem32(buffer, space, mem32);
606 }
607 
isapnp_print_configuration(isapnp_info_buffer_t * buffer,struct pci_dev * dev)608 static void isapnp_print_configuration(isapnp_info_buffer_t *buffer, struct pci_dev *dev)
609 {
610 	int i, tmp, next;
611 	char *space = "    ";
612 
613 	isapnp_cfg_begin(dev->bus->number, dev->devfn);
614 	isapnp_printf(buffer, "%sDevice is %sactive\n",
615 			space, isapnp_read_byte(ISAPNP_CFG_ACTIVATE)?"":"not ");
616 	for (i = next = 0; i < 8; i++) {
617 		tmp = isapnp_read_word(ISAPNP_CFG_PORT + (i << 1));
618 		if (!tmp)
619 			continue;
620 		if (!next) {
621 			isapnp_printf(buffer, "%sActive port ", space);
622 			next = 1;
623 		}
624 		isapnp_printf(buffer, "%s0x%x", i > 0 ? "," : "", tmp);
625 	}
626 	if (next)
627 		isapnp_printf(buffer, "\n");
628 	for (i = next = 0; i < 2; i++) {
629 		tmp = isapnp_read_word(ISAPNP_CFG_IRQ + (i << 1));
630 		if (!(tmp >> 8))
631 			continue;
632 		if (!next) {
633 			isapnp_printf(buffer, "%sActive IRQ ", space);
634 			next = 1;
635 		}
636 		isapnp_printf(buffer, "%s%i", i > 0 ? "," : "", tmp >> 8);
637 		if (tmp & 0xff)
638 			isapnp_printf(buffer, " [0x%x]", tmp & 0xff);
639 	}
640 	if (next)
641 		isapnp_printf(buffer, "\n");
642 	for (i = next = 0; i < 2; i++) {
643 		tmp = isapnp_read_byte(ISAPNP_CFG_DMA + i);
644 		if (tmp == 4)
645 			continue;
646 		if (!next) {
647 			isapnp_printf(buffer, "%sActive DMA ", space);
648 			next = 1;
649 		}
650 		isapnp_printf(buffer, "%s%i", i > 0 ? "," : "", tmp);
651 	}
652 	if (next)
653 		isapnp_printf(buffer, "\n");
654 	for (i = next = 0; i < 4; i++) {
655 		tmp = isapnp_read_dword(ISAPNP_CFG_MEM + (i << 3));
656 		if (!tmp)
657 			continue;
658 		if (!next) {
659 			isapnp_printf(buffer, "%sActive memory ", space);
660 			next = 1;
661 		}
662 		isapnp_printf(buffer, "%s0x%x", i > 0 ? "," : "", tmp);
663 	}
664 	if (next)
665 		isapnp_printf(buffer, "\n");
666 	isapnp_cfg_end();
667 }
668 
isapnp_print_device(isapnp_info_buffer_t * buffer,struct pci_dev * dev)669 static void isapnp_print_device(isapnp_info_buffer_t *buffer, struct pci_dev *dev)
670 {
671 	int block, block1;
672 	char *space = "    ";
673 	struct isapnp_resources *res, *resa;
674 
675 	if (!dev)
676 		return;
677 	isapnp_printf(buffer, "  Logical device %i '", dev->devfn);
678 	isapnp_print_devid(buffer, dev->vendor, dev->device);
679 	isapnp_printf(buffer, ":%s'", dev->name[0]?dev->name:"Unknown");
680 	isapnp_printf(buffer, "\n");
681 #if 0
682 	isapnp_cfg_begin(dev->bus->number, dev->devfn);
683 	for (block = 0; block < 128; block++)
684 		if ((block % 16) == 15)
685 			isapnp_printf(buffer, "%02x\n", isapnp_read_byte(block));
686 		else
687 			isapnp_printf(buffer, "%02x:", isapnp_read_byte(block));
688 	isapnp_cfg_end();
689 #endif
690 	if (dev->regs)
691 		isapnp_printf(buffer, "%sSupported registers 0x%x\n", space, dev->regs);
692 	isapnp_print_compatible(buffer, dev);
693 	isapnp_print_configuration(buffer, dev);
694 	for (res = (struct isapnp_resources *)dev->sysdata, block = 0; res; res = res->next, block++) {
695 		isapnp_printf(buffer, "%sResources %i\n", space, block);
696 		isapnp_print_resources(buffer, "      ", res);
697 		for (resa = res->alt, block1 = 1; resa; resa = resa->alt, block1++) {
698 			isapnp_printf(buffer, "%s  Alternate resources %i:%i\n", space, block, block1);
699 			isapnp_print_resources(buffer, "        ", resa);
700 		}
701 	}
702 }
703 
704 /*
705  *  Main read routine
706  */
707 
isapnp_info_read(isapnp_info_buffer_t * buffer)708 static void isapnp_info_read(isapnp_info_buffer_t *buffer)
709 {
710 	struct pci_bus *card;
711 
712 	isapnp_for_each_card(card) {
713 		struct list_head *dev_list;
714 
715 		isapnp_printf(buffer, "Card %i '", card->number);
716 		isapnp_print_devid(buffer, card->vendor, card->device);
717 		isapnp_printf(buffer, ":%s'", card->name[0]?card->name:"Unknown");
718 		if (card->pnpver)
719 			isapnp_printf(buffer, " PnP version %x.%x", card->pnpver >> 4, card->pnpver & 0x0f);
720 		if (card->productver)
721 			isapnp_printf(buffer, " Product version %x.%x", card->productver >> 4, card->productver & 0x0f);
722 		isapnp_printf(buffer,"\n");
723 		for (dev_list = card->devices.next; dev_list != &card->devices; dev_list = dev_list->next)
724 			isapnp_print_device(buffer, pci_dev_b(dev_list));
725 	}
726 }
727 
728 /*
729  *
730  */
731 
732 static struct pci_bus *isapnp_info_card;
733 static struct pci_dev *isapnp_info_device;
734 
isapnp_get_str(char * dest,char * src,int len)735 static char *isapnp_get_str(char *dest, char *src, int len)
736 {
737 	int c;
738 
739 	while (*src == ' ' || *src == '\t')
740 		src++;
741 	if (*src == '"' || *src == '\'') {
742 		c = *src++;
743 		while (--len > 0 && *src && *src != c) {
744 			*dest++ = *src++;
745 		}
746 		if (*src == c)
747 			src++;
748 	} else {
749 		while (--len > 0 && *src && *src != ' ' && *src != '\t') {
750 			*dest++ = *src++;
751 		}
752 	}
753 	*dest = 0;
754 	while (*src == ' ' || *src == '\t')
755 		src++;
756 	return src;
757 }
758 
isapnp_get_hex(unsigned char c)759 static unsigned char isapnp_get_hex(unsigned char c)
760 {
761 	if (c >= '0' && c <= '9')
762 		return c - '0';
763 	if (c >= 'a' && c <= 'f')
764 		return (c - 'a') + 10;
765 	if (c >= 'A' && c <= 'F')
766 		return (c - 'A') + 10;
767 	return 0;
768 }
769 
isapnp_parse_id(const char * id)770 static unsigned int isapnp_parse_id(const char *id)
771 {
772 	if (strlen(id) != 7) {
773 		printk("isapnp: wrong PnP ID\n");
774 		return 0;
775 	}
776 	return (ISAPNP_VENDOR(id[0], id[1], id[2])<<16) |
777 			(isapnp_get_hex(id[3])<<4) |
778 			(isapnp_get_hex(id[4])<<0) |
779 			(isapnp_get_hex(id[5])<<12) |
780 			(isapnp_get_hex(id[6])<<8);
781 }
782 
isapnp_set_card(char * line)783 static int isapnp_set_card(char *line)
784 {
785 	int idx, idx1;
786 	unsigned int id;
787 	char index[16], value[32];
788 
789 	if (isapnp_info_card) {
790 		isapnp_cfg_end();
791 		isapnp_info_card = NULL;
792 	}
793 	line = isapnp_get_str(index, line, sizeof(index));
794 	isapnp_get_str(value, line, sizeof(value));
795 	idx = idx1 = simple_strtoul(index, NULL, 0);
796 	id = isapnp_parse_id(value);
797 	isapnp_info_card = isapnp_find_card(id >> 16, id & 0xffff, NULL);
798 	while (isapnp_info_card && idx1-- > 0)
799 		isapnp_info_card = isapnp_find_card(id >> 16, id & 0xffff, isapnp_info_card);
800 	if (isapnp_info_card == NULL) {
801 		printk("isapnp: card '%s' order %i not found\n", value, idx);
802 		return 1;
803 	}
804 	if (isapnp_cfg_begin(isapnp_info_card->number, -1)<0) {
805 		printk("isapnp: configuration start sequence for device '%s' failed\n", value);
806 		isapnp_info_card = NULL;
807 		return 1;
808 	}
809 	return 0;
810 }
811 
isapnp_select_csn(char * line)812 static int isapnp_select_csn(char *line)
813 {
814 	int csn;
815 	struct list_head *list;
816 	char index[16], value[32];
817 
818 	isapnp_info_device = NULL;
819 	isapnp_get_str(index, line, sizeof(index));
820 	csn = simple_strtoul(index, NULL, 0);
821 
822 	for (list = isapnp_cards.next; list != &isapnp_cards; list = list->next) {
823 		isapnp_info_card = pci_bus_b(list);
824 		if (isapnp_info_card->number == csn)
825 			break;
826 	}
827 	if (list == &isapnp_cards) {
828 		printk("isapnp: cannot find CSN %i\n", csn);
829 		return 1;
830 	}
831 	if (isapnp_cfg_begin(isapnp_info_card->number, -1)<0) {
832 		printk("isapnp: configuration start sequence for device '%s' failed\n", value);
833 		isapnp_info_card = NULL;
834 		return 1;
835 	}
836 	return 0;
837 }
838 
isapnp_set_device(char * line)839 static int isapnp_set_device(char *line)
840 {
841 	int idx, idx1;
842 	unsigned int id;
843 	char index[16], value[32];
844 
845 	line = isapnp_get_str(index, line, sizeof(index));
846 	isapnp_get_str(value, line, sizeof(value));
847 	idx = idx1 = simple_strtoul(index, NULL, 0);
848 	id = isapnp_parse_id(value);
849 	isapnp_info_device = isapnp_find_dev(isapnp_info_card, id >> 16, id & 0xffff, NULL);
850 	while (isapnp_info_device && idx-- > 0)
851 		isapnp_info_device = isapnp_find_dev(isapnp_info_card, id >> 16, id & 0xffff, isapnp_info_device);
852 	if (isapnp_info_device == NULL) {
853 		printk("isapnp: device '%s' order %i not found\n", value, idx);
854 		return 1;
855 	}
856 	isapnp_device(isapnp_info_device->devfn);
857 	return 0;
858 }
859 
isapnp_autoconfigure(void)860 static int isapnp_autoconfigure(void)
861 {
862 	isapnp_cfg_end();
863 	if (isapnp_info_device->active)
864 		isapnp_info_device->deactivate(isapnp_info_device);
865 	if (isapnp_info_device->prepare(isapnp_info_device) < 0) {
866 		printk("isapnp: cannot prepare device for the activation");
867 		return 0;
868 	}
869 	if (isapnp_info_device->activate(isapnp_info_device) < 0) {
870 		printk("isapnp: cannot activate device");
871 		return 0;
872 	}
873 	if (isapnp_cfg_begin(isapnp_info_card->number, -1)<0) {
874 		printk("isapnp: configuration start sequence for card %d failed\n", isapnp_info_card->number);
875 		isapnp_info_card = NULL;
876 		isapnp_info_device = NULL;
877 		return 1;
878 	}
879 	isapnp_device(isapnp_info_device->devfn);
880 	return 0;
881 }
882 
isapnp_set_port(char * line)883 static int isapnp_set_port(char *line)
884 {
885 	int idx, port;
886 	char index[16], value[32];
887 
888 	line = isapnp_get_str(index, line, sizeof(index));
889 	isapnp_get_str(value, line, sizeof(value));
890 	idx = simple_strtoul(index, NULL, 0);
891 	port = simple_strtoul(value, NULL, 0);
892 	if (idx < 0 || idx > 7) {
893 		printk("isapnp: wrong port index %i\n", idx);
894 		return 1;
895 	}
896 	if (port < 0 || port > 0xffff) {
897 		printk("isapnp: wrong port value 0x%x\n", port);
898 		return 1;
899 	}
900 	isapnp_write_word(ISAPNP_CFG_PORT + (idx << 1), port);
901 	if (!isapnp_info_device->resource[idx].flags)
902 		return 0;
903 	if (isapnp_info_device->resource[idx].flags & IORESOURCE_AUTO) {
904 		isapnp_info_device->resource[idx].start = port;
905 		isapnp_info_device->resource[idx].end += port - 1;
906 		isapnp_info_device->resource[idx].flags &= ~IORESOURCE_AUTO;
907 	} else {
908 		isapnp_info_device->resource[idx].end -= isapnp_info_device->resource[idx].start;
909 		isapnp_info_device->resource[idx].start = port;
910 		isapnp_info_device->resource[idx].end += port;
911 	}
912 	return 0;
913 }
914 
isapnp_set_irqresource(struct resource * res,int irq)915 static void isapnp_set_irqresource(struct resource *res, int irq)
916 {
917 	res->start = res->end = irq;
918 	res->flags = IORESOURCE_IRQ;
919 }
920 
isapnp_set_irq(char * line)921 static int isapnp_set_irq(char *line)
922 {
923 	int idx, irq;
924 	char index[16], value[32];
925 
926 	line = isapnp_get_str(index, line, sizeof(index));
927 	isapnp_get_str(value, line, sizeof(value));
928 	idx = simple_strtoul(index, NULL, 0);
929 	irq = simple_strtoul(value, NULL, 0);
930 	if (idx < 0 || idx > 1) {
931 		printk("isapnp: wrong IRQ index %i\n", idx);
932 		return 1;
933 	}
934 	if (irq == 2)
935 		irq = 9;
936 	if (irq < 0 || irq > 15) {
937 		printk("isapnp: wrong IRQ value %i\n", irq);
938 		return 1;
939 	}
940 	isapnp_write_byte(ISAPNP_CFG_IRQ + (idx << 1), irq);
941 	isapnp_set_irqresource(isapnp_info_device->irq_resource + idx, irq);
942 	return 0;
943 }
944 
isapnp_set_dmaresource(struct resource * res,int dma)945 static void isapnp_set_dmaresource(struct resource *res, int dma)
946 {
947 	res->start = res->end = dma;
948 	res->flags = IORESOURCE_DMA;
949 }
950 
951 extern int isapnp_allow_dma0;
isapnp_set_allow_dma0(char * line)952 static int isapnp_set_allow_dma0(char *line)
953 {
954 	int i;
955 	char value[32];
956 
957 	isapnp_get_str(value, line, sizeof(value));
958 	i = simple_strtoul(value, NULL, 0);
959 	if (i < 0 || i > 1) {
960 		printk("isapnp: wrong value %i for allow_dma0\n", i);
961 		return 1;
962 	}
963 	isapnp_allow_dma0 = i;
964 	return 0;
965 }
966 
isapnp_set_dma(char * line)967 static int isapnp_set_dma(char *line)
968 {
969 	int idx, dma;
970 	char index[16], value[32];
971 
972 	line = isapnp_get_str(index, line, sizeof(index));
973 	isapnp_get_str(value, line, sizeof(value));
974 	idx = simple_strtoul(index, NULL, 0);
975 	dma = simple_strtoul(value, NULL, 0);
976 	if (idx < 0 || idx > 1) {
977 		printk("isapnp: wrong DMA index %i\n", idx);
978 		return 1;
979 	}
980 	if (dma < 0 || dma > 7) {
981 		printk("isapnp: wrong DMA value %i\n", dma);
982 		return 1;
983 	}
984 	isapnp_write_byte(ISAPNP_CFG_DMA + idx, dma);
985 	isapnp_set_dmaresource(isapnp_info_device->dma_resource + idx, dma);
986 	return 0;
987 }
988 
isapnp_set_mem(char * line)989 static int isapnp_set_mem(char *line)
990 {
991 	int idx;
992 	unsigned int mem;
993 	char index[16], value[32];
994 
995 	line = isapnp_get_str(index, line, sizeof(index));
996 	isapnp_get_str(value, line, sizeof(value));
997 	idx = simple_strtoul(index, NULL, 0);
998 	mem = simple_strtoul(value, NULL, 0);
999 	if (idx < 0 || idx > 3) {
1000 		printk("isapnp: wrong memory index %i\n", idx);
1001 		return 1;
1002 	}
1003 	mem >>= 8;
1004 	isapnp_write_word(ISAPNP_CFG_MEM + (idx<<2), mem & 0xffff);
1005 	if (!isapnp_info_device->resource[idx + 8].flags)
1006 		return 0;
1007 	if (isapnp_info_device->resource[idx + 8].flags & IORESOURCE_AUTO) {
1008 		isapnp_info_device->resource[idx + 8].start = mem & ~0x00ffff00;
1009 		isapnp_info_device->resource[idx + 8].end += (mem & ~0x00ffff00) - 1;
1010 		isapnp_info_device->resource[idx + 8].flags &= ~IORESOURCE_AUTO;
1011 	} else {
1012 		isapnp_info_device->resource[idx + 8].end -= isapnp_info_device->resource[idx + 8].start;
1013 		isapnp_info_device->resource[idx + 8].start = mem & ~0x00ffff00;
1014 		isapnp_info_device->resource[idx + 8].end += mem & ~0x00ffff00;
1015 	}
1016 	return 0;
1017 }
1018 
isapnp_poke(char * line,int what)1019 static int isapnp_poke(char *line, int what)
1020 {
1021 	int reg;
1022 	unsigned int val;
1023 	char index[16], value[32];
1024 
1025 	line = isapnp_get_str(index, line, sizeof(index));
1026 	isapnp_get_str(value, line, sizeof(value));
1027 	reg = simple_strtoul(index, NULL, 0);
1028 	val = simple_strtoul(value, NULL, 0);
1029 	if (reg < 0 || reg > 127) {
1030 		printk("isapnp: wrong register %i\n", reg);
1031 		return 1;
1032 	}
1033 	switch (what) {
1034 	case 1:
1035 		isapnp_write_word(reg, val);
1036 		break;
1037 	case 2:
1038 		isapnp_write_dword(reg, val);
1039 		break;
1040 	default:
1041 		isapnp_write_byte(reg, val);
1042 		break;
1043 	}
1044 	return 0;
1045 }
1046 
isapnp_decode_line(char * line)1047 static int isapnp_decode_line(char *line)
1048 {
1049 	char cmd[32];
1050 
1051 	line = isapnp_get_str(cmd, line, sizeof(cmd));
1052 	if (!strcmp(cmd, "allow_dma0"))
1053 		return isapnp_set_allow_dma0(line);
1054 	if (!strcmp(cmd, "card"))
1055 		return isapnp_set_card(line);
1056 	if (!strcmp(cmd, "csn"))
1057 		return isapnp_select_csn(line);
1058 	if (!isapnp_info_card) {
1059 		printk("isapnp: card is not selected\n");
1060 		return 1;
1061 	}
1062 	if (!strncmp(cmd, "dev", 3))
1063 		return isapnp_set_device(line);
1064 	if (!isapnp_info_device) {
1065 		printk("isapnp: device is not selected\n");
1066 		return 1;
1067 	}
1068 	if (!strncmp(cmd, "auto", 4))
1069 		return isapnp_autoconfigure();
1070 	if (!strncmp(cmd, "act", 3)) {
1071 		isapnp_activate(isapnp_info_device->devfn);
1072 		isapnp_info_device->active = 1;
1073 		return 0;
1074 	}
1075 	if (!strncmp(cmd, "deact", 5)) {
1076 		isapnp_deactivate(isapnp_info_device->devfn);
1077 		isapnp_info_device->active = 0;
1078 		return 0;
1079 	}
1080 	if (!strcmp(cmd, "port"))
1081 		return isapnp_set_port(line);
1082 	if (!strcmp(cmd, "irq"))
1083 		return isapnp_set_irq(line);
1084 	if (!strcmp(cmd, "dma"))
1085 		return isapnp_set_dma(line);
1086 	if (!strncmp(cmd, "mem", 3))
1087 		return isapnp_set_mem(line);
1088 	if (!strcmp(cmd, "poke"))
1089 		return isapnp_poke(line, 0);
1090 	if (!strcmp(cmd, "pokew"))
1091 		return isapnp_poke(line, 1);
1092 	if (!strcmp(cmd, "poked"))
1093 		return isapnp_poke(line, 2);
1094 	printk("isapnp: wrong command '%s'\n", cmd);
1095 	return 1;
1096 }
1097 
1098 /*
1099  *  Main write routine
1100  */
1101 
isapnp_info_write(isapnp_info_buffer_t * buffer)1102 static void isapnp_info_write(isapnp_info_buffer_t *buffer)
1103 {
1104 	int c, idx, idx1 = 0;
1105 	char line[128];
1106 
1107 	if (buffer->size <= 0)
1108 		return;
1109 	isapnp_info_card = NULL;
1110 	isapnp_info_device = NULL;
1111 	for (idx = 0; idx < buffer->size; idx++) {
1112 		c = buffer->buffer[idx];
1113 		if (c == '\n') {
1114 			line[idx1] = '\0';
1115 			if (line[0] != '#') {
1116 				if (isapnp_decode_line(line))
1117 					goto __end;
1118 			}
1119 			idx1 = 0;
1120 			continue;
1121 		}
1122 		if (idx1 >= sizeof(line)-1) {
1123 			printk("isapnp: line too long, aborting\n");
1124 			return;
1125 		}
1126 		line[idx1++] = c;
1127 	}
1128       __end:
1129 	if (isapnp_info_card)
1130 		isapnp_cfg_end();
1131 }
1132