interface.c 11.7 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3
/*
 * interface.c - contains everything related to the user interface
 *
4
 * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela <perex@perex.cz>
Linus Torvalds's avatar
Linus Torvalds committed
5 6 7 8 9 10 11 12
 * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
 */

#include <linux/pnp.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/types.h>
13
#include <linux/pnp.h>
Linus Torvalds's avatar
Linus Torvalds committed
14 15 16
#include <linux/stat.h>
#include <linux/ctype.h>
#include <linux/slab.h>
17 18
#include <linux/mutex.h>

Linus Torvalds's avatar
Linus Torvalds committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#include <asm/uaccess.h>

#include "base.h"

struct pnp_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 pnp_info_buffer pnp_info_buffer_t;

34
static int pnp_printf(pnp_info_buffer_t * buffer, char *fmt, ...)
Linus Torvalds's avatar
Linus Torvalds committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
{
	va_list args;
	int res;

	if (buffer->stop || buffer->error)
		return 0;
	va_start(args, fmt);
	res = vsnprintf(buffer->curr, buffer->len - buffer->size, fmt, args);
	va_end(args);
	if (buffer->size + res >= buffer->len) {
		buffer->stop = 1;
		return 0;
	}
	buffer->curr += res;
	buffer->size += res;
	return res;
}

53 54
static void pnp_print_port(pnp_info_buffer_t * buffer, char *space,
			   struct pnp_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
55
{
56 57 58 59 60
	pnp_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 & PNP_PORT_FLAG_16BITADDR ? 16 : 10);
Linus Torvalds's avatar
Linus Torvalds committed
61 62
}

63 64
static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space,
			  struct pnp_irq *irq)
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
{
	int first = 1, i;

	pnp_printf(buffer, "%sirq ", space);
	for (i = 0; i < PNP_IRQ_NR; i++)
		if (test_bit(i, irq->map)) {
			if (!first) {
				pnp_printf(buffer, ",");
			} else {
				first = 0;
			}
			if (i == 2 || i == 9)
				pnp_printf(buffer, "2/9");
			else
				pnp_printf(buffer, "%i", i);
		}
	if (bitmap_empty(irq->map, PNP_IRQ_NR))
		pnp_printf(buffer, "<none>");
	if (irq->flags & IORESOURCE_IRQ_HIGHEDGE)
		pnp_printf(buffer, " High-Edge");
	if (irq->flags & IORESOURCE_IRQ_LOWEDGE)
		pnp_printf(buffer, " Low-Edge");
	if (irq->flags & IORESOURCE_IRQ_HIGHLEVEL)
		pnp_printf(buffer, " High-Level");
	if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
		pnp_printf(buffer, " Low-Level");
	pnp_printf(buffer, "\n");
}

94 95
static void pnp_print_dma(pnp_info_buffer_t * buffer, char *space,
			  struct pnp_dma *dma)
Linus Torvalds's avatar
Linus Torvalds committed
96 97 98 99 100 101
{
	int first = 1, i;
	char *s;

	pnp_printf(buffer, "%sdma ", space);
	for (i = 0; i < 8; i++)
102
		if (dma->map & (1 << i)) {
Linus Torvalds's avatar
Linus Torvalds committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
			if (!first) {
				pnp_printf(buffer, ",");
			} else {
				first = 0;
			}
			pnp_printf(buffer, "%i", i);
		}
	if (!dma->map)
		pnp_printf(buffer, "<none>");
	switch (dma->flags & IORESOURCE_DMA_TYPE_MASK) {
	case IORESOURCE_DMA_8BIT:
		s = "8-bit";
		break;
	case IORESOURCE_DMA_8AND16BIT:
		s = "8-bit&16-bit";
		break;
	default:
		s = "16-bit";
	}
	pnp_printf(buffer, " %s", s);
	if (dma->flags & IORESOURCE_DMA_MASTER)
		pnp_printf(buffer, " master");
	if (dma->flags & IORESOURCE_DMA_BYTE)
		pnp_printf(buffer, " byte-count");
	if (dma->flags & IORESOURCE_DMA_WORD)
		pnp_printf(buffer, " word-count");
	switch (dma->flags & IORESOURCE_DMA_SPEED_MASK) {
	case IORESOURCE_DMA_TYPEA:
		s = "type-A";
		break;
	case IORESOURCE_DMA_TYPEB:
		s = "type-B";
		break;
	case IORESOURCE_DMA_TYPEF:
		s = "type-F";
		break;
	default:
		s = "compatible";
		break;
	}
	pnp_printf(buffer, " %s\n", s);
}

146 147
static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space,
			  struct pnp_mem *mem)
Linus Torvalds's avatar
Linus Torvalds committed
148 149 150 151
{
	char *s;

	pnp_printf(buffer, "%sMemory 0x%x-0x%x, align 0x%x, size 0x%x",
152
		   space, mem->min, mem->max, mem->align, mem->size);
Linus Torvalds's avatar
Linus Torvalds committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	if (mem->flags & IORESOURCE_MEM_WRITEABLE)
		pnp_printf(buffer, ", writeable");
	if (mem->flags & IORESOURCE_MEM_CACHEABLE)
		pnp_printf(buffer, ", cacheable");
	if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
		pnp_printf(buffer, ", range-length");
	if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
		pnp_printf(buffer, ", shadowable");
	if (mem->flags & IORESOURCE_MEM_EXPANSIONROM)
		pnp_printf(buffer, ", expansion ROM");
	switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
	case IORESOURCE_MEM_8BIT:
		s = "8-bit";
		break;
	case IORESOURCE_MEM_8AND16BIT:
		s = "8-bit&16-bit";
		break;
	case IORESOURCE_MEM_32BIT:
		s = "32-bit";
		break;
	default:
		s = "16-bit";
	}
	pnp_printf(buffer, ", %s\n", s);
}

179
static void pnp_print_option(pnp_info_buffer_t * buffer, char *space,
Linus Torvalds's avatar
Linus Torvalds committed
180 181 182 183 184 185 186 187 188 189
			     struct pnp_option *option, int dep)
{
	char *s;
	struct pnp_port *port;
	struct pnp_irq *irq;
	struct pnp_dma *dma;
	struct pnp_mem *mem;

	if (dep) {
		switch (option->priority) {
190
		case PNP_RES_PRIORITY_PREFERRED:
Linus Torvalds's avatar
Linus Torvalds committed
191 192
			s = "preferred";
			break;
193
		case PNP_RES_PRIORITY_ACCEPTABLE:
Linus Torvalds's avatar
Linus Torvalds committed
194 195
			s = "acceptable";
			break;
196
		case PNP_RES_PRIORITY_FUNCTIONAL:
Linus Torvalds's avatar
Linus Torvalds committed
197 198
			s = "functional";
			break;
199
		default:
Linus Torvalds's avatar
Linus Torvalds committed
200 201
			s = "invalid";
		}
202
		pnp_printf(buffer, "Dependent: %02i - Priority %s\n", dep, s);
Linus Torvalds's avatar
Linus Torvalds committed
203 204 205 206 207 208 209 210 211 212 213 214
	}

	for (port = option->port; port; port = port->next)
		pnp_print_port(buffer, space, port);
	for (irq = option->irq; irq; irq = irq->next)
		pnp_print_irq(buffer, space, irq);
	for (dma = option->dma; dma; dma = dma->next)
		pnp_print_dma(buffer, space, dma);
	for (mem = option->mem; mem; mem = mem->next)
		pnp_print_mem(buffer, space, mem);
}

215 216
static ssize_t pnp_show_options(struct device *dmdev,
				struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
217 218
{
	struct pnp_dev *dev = to_pnp_dev(dmdev);
219 220
	struct pnp_option *independent = dev->independent;
	struct pnp_option *dependent = dev->dependent;
Linus Torvalds's avatar
Linus Torvalds committed
221 222 223
	int ret, dep = 1;

	pnp_info_buffer_t *buffer = (pnp_info_buffer_t *)
224
	    pnp_alloc(sizeof(pnp_info_buffer_t));
Linus Torvalds's avatar
Linus Torvalds committed
225 226 227 228 229 230 231 232 233
	if (!buffer)
		return -ENOMEM;

	buffer->len = PAGE_SIZE;
	buffer->buffer = buf;
	buffer->curr = buffer->buffer;
	if (independent)
		pnp_print_option(buffer, "", independent, 0);

234
	while (dependent) {
Linus Torvalds's avatar
Linus Torvalds committed
235 236 237 238 239 240 241 242 243
		pnp_print_option(buffer, "   ", dependent, dep);
		dependent = dependent->next;
		dep++;
	}
	ret = (buffer->curr - buf);
	kfree(buffer);
	return ret;
}

244
static DEVICE_ATTR(options, S_IRUGO, pnp_show_options, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
245

246 247 248
static ssize_t pnp_show_current_resources(struct device *dmdev,
					  struct device_attribute *attr,
					  char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
249 250
{
	struct pnp_dev *dev = to_pnp_dev(dmdev);
Bjorn Helgaas's avatar
Bjorn Helgaas committed
251
	struct resource *res;
Linus Torvalds's avatar
Linus Torvalds committed
252 253 254 255 256 257 258 259 260 261 262 263 264
	int i, ret;
	pnp_info_buffer_t *buffer;

	if (!dev)
		return -EINVAL;

	buffer = (pnp_info_buffer_t *) pnp_alloc(sizeof(pnp_info_buffer_t));
	if (!buffer)
		return -ENOMEM;
	buffer->len = PAGE_SIZE;
	buffer->buffer = buf;
	buffer->curr = buffer->buffer;

265
	pnp_printf(buffer, "state = ");
Linus Torvalds's avatar
Linus Torvalds committed
266
	if (dev->active)
267
		pnp_printf(buffer, "active\n");
Linus Torvalds's avatar
Linus Torvalds committed
268
	else
269
		pnp_printf(buffer, "disabled\n");
Linus Torvalds's avatar
Linus Torvalds committed
270

Bjorn Helgaas's avatar
Bjorn Helgaas committed
271 272
	for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) {
		if (pnp_resource_valid(res)) {
273
			pnp_printf(buffer, "io");
Bjorn Helgaas's avatar
Bjorn Helgaas committed
274
			if (res->flags & IORESOURCE_DISABLED)
275
				pnp_printf(buffer, " disabled\n");
Linus Torvalds's avatar
Linus Torvalds committed
276
			else
277
				pnp_printf(buffer, " 0x%llx-0x%llx\n",
Bjorn Helgaas's avatar
Bjorn Helgaas committed
278 279
					   (unsigned long long) res->start,
					   (unsigned long long) res->end);
Linus Torvalds's avatar
Linus Torvalds committed
280 281
		}
	}
Bjorn Helgaas's avatar
Bjorn Helgaas committed
282 283
	for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) {
		if (pnp_resource_valid(res)) {
284
			pnp_printf(buffer, "mem");
Bjorn Helgaas's avatar
Bjorn Helgaas committed
285
			if (res->flags & IORESOURCE_DISABLED)
286
				pnp_printf(buffer, " disabled\n");
Linus Torvalds's avatar
Linus Torvalds committed
287
			else
288
				pnp_printf(buffer, " 0x%llx-0x%llx\n",
Bjorn Helgaas's avatar
Bjorn Helgaas committed
289 290
					   (unsigned long long) res->start,
					   (unsigned long long) res->end);
Linus Torvalds's avatar
Linus Torvalds committed
291 292
		}
	}
Bjorn Helgaas's avatar
Bjorn Helgaas committed
293 294
	for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IRQ, i)); i++) {
		if (pnp_resource_valid(res)) {
295
			pnp_printf(buffer, "irq");
Bjorn Helgaas's avatar
Bjorn Helgaas committed
296
			if (res->flags & IORESOURCE_DISABLED)
297
				pnp_printf(buffer, " disabled\n");
Linus Torvalds's avatar
Linus Torvalds committed
298
			else
299
				pnp_printf(buffer, " %lld\n",
Bjorn Helgaas's avatar
Bjorn Helgaas committed
300
					   (unsigned long long) res->start);
Linus Torvalds's avatar
Linus Torvalds committed
301 302
		}
	}
Bjorn Helgaas's avatar
Bjorn Helgaas committed
303 304
	for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_DMA, i)); i++) {
		if (pnp_resource_valid(res)) {
305
			pnp_printf(buffer, "dma");
Bjorn Helgaas's avatar
Bjorn Helgaas committed
306
			if (res->flags & IORESOURCE_DISABLED)
307
				pnp_printf(buffer, " disabled\n");
Linus Torvalds's avatar
Linus Torvalds committed
308
			else
309
				pnp_printf(buffer, " %lld\n",
Bjorn Helgaas's avatar
Bjorn Helgaas committed
310
					   (unsigned long long) res->start);
Linus Torvalds's avatar
Linus Torvalds committed
311 312 313 314 315 316 317 318
		}
	}
	ret = (buffer->curr - buf);
	kfree(buffer);
	return ret;
}

static ssize_t
319 320
pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr,
			  const char *ubuf, size_t count)
Linus Torvalds's avatar
Linus Torvalds committed
321 322
{
	struct pnp_dev *dev = to_pnp_dev(dmdev);
323
	struct pnp_resource *pnp_res;
324
	struct resource *res;
325 326
	char *buf = (void *)ubuf;
	int retval = 0;
327
	resource_size_t start, end;
Linus Torvalds's avatar
Linus Torvalds committed
328 329 330

	if (dev->status & PNP_ATTACHED) {
		retval = -EBUSY;
331
		dev_info(&dev->dev, "in use; can't configure\n");
Linus Torvalds's avatar
Linus Torvalds committed
332 333 334 335 336
		goto done;
	}

	while (isspace(*buf))
		++buf;
337
	if (!strnicmp(buf, "disable", 7)) {
Linus Torvalds's avatar
Linus Torvalds committed
338 339 340
		retval = pnp_disable_dev(dev);
		goto done;
	}
341
	if (!strnicmp(buf, "activate", 8)) {
Linus Torvalds's avatar
Linus Torvalds committed
342 343 344
		retval = pnp_activate_dev(dev);
		goto done;
	}
345
	if (!strnicmp(buf, "fill", 4)) {
Linus Torvalds's avatar
Linus Torvalds committed
346 347 348 349 350
		if (dev->active)
			goto done;
		retval = pnp_auto_config_dev(dev);
		goto done;
	}
351
	if (!strnicmp(buf, "auto", 4)) {
Linus Torvalds's avatar
Linus Torvalds committed
352 353
		if (dev->active)
			goto done;
354
		pnp_init_resources(dev);
Linus Torvalds's avatar
Linus Torvalds committed
355 356 357
		retval = pnp_auto_config_dev(dev);
		goto done;
	}
358
	if (!strnicmp(buf, "clear", 5)) {
Linus Torvalds's avatar
Linus Torvalds committed
359 360
		if (dev->active)
			goto done;
361
		pnp_init_resources(dev);
Linus Torvalds's avatar
Linus Torvalds committed
362 363
		goto done;
	}
364
	if (!strnicmp(buf, "get", 3)) {
365
		mutex_lock(&pnp_res_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
366
		if (pnp_can_read(dev))
367
			dev->protocol->get(dev);
368
		mutex_unlock(&pnp_res_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
369 370
		goto done;
	}
371
	if (!strnicmp(buf, "set", 3)) {
Linus Torvalds's avatar
Linus Torvalds committed
372 373 374 375
		int nport = 0, nmem = 0, nirq = 0, ndma = 0;
		if (dev->active)
			goto done;
		buf += 3;
376
		pnp_init_resources(dev);
377
		mutex_lock(&pnp_res_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
378 379 380
		while (1) {
			while (isspace(*buf))
				++buf;
381
			if (!strnicmp(buf, "io", 2)) {
Linus Torvalds's avatar
Linus Torvalds committed
382 383 384
				buf += 2;
				while (isspace(*buf))
					++buf;
385
				start = simple_strtoul(buf, &buf, 0);
Linus Torvalds's avatar
Linus Torvalds committed
386 387
				while (isspace(*buf))
					++buf;
388
				if (*buf == '-') {
Linus Torvalds's avatar
Linus Torvalds committed
389 390 391
					buf += 1;
					while (isspace(*buf))
						++buf;
392
					end = simple_strtoul(buf, &buf, 0);
Linus Torvalds's avatar
Linus Torvalds committed
393
				} else
394 395 396 397 398
					end = start;
				pnp_res = pnp_add_io_resource(dev, start, end,
							      0);
				if (pnp_res)
					pnp_res->index = nport++;
Linus Torvalds's avatar
Linus Torvalds committed
399 400
				continue;
			}
401
			if (!strnicmp(buf, "mem", 3)) {
Linus Torvalds's avatar
Linus Torvalds committed
402 403 404
				buf += 3;
				while (isspace(*buf))
					++buf;
405 406 407
				pnp_res = pnp_get_pnp_resource(dev,
						IORESOURCE_MEM, nmem);
				if (!pnp_res)
408
					break;
409 410
				pnp_res->index = nmem;
				res = &pnp_res->res;
411
				res->start = simple_strtoul(buf, &buf, 0);
Linus Torvalds's avatar
Linus Torvalds committed
412 413
				while (isspace(*buf))
					++buf;
414
				if (*buf == '-') {
Linus Torvalds's avatar
Linus Torvalds committed
415 416 417
					buf += 1;
					while (isspace(*buf))
						++buf;
418
					res->end = simple_strtoul(buf, &buf, 0);
Linus Torvalds's avatar
Linus Torvalds committed
419
				} else
420 421
					res->end = res->start;
				res->flags = IORESOURCE_MEM;
Linus Torvalds's avatar
Linus Torvalds committed
422 423 424
				nmem++;
				continue;
			}
425
			if (!strnicmp(buf, "irq", 3)) {
Linus Torvalds's avatar
Linus Torvalds committed
426 427 428
				buf += 3;
				while (isspace(*buf))
					++buf;
429 430 431 432
				start = simple_strtoul(buf, &buf, 0);
				pnp_res = pnp_add_irq_resource(dev, start, 0);
				if (pnp_res)
					nirq++;
Linus Torvalds's avatar
Linus Torvalds committed
433 434
				continue;
			}
435
			if (!strnicmp(buf, "dma", 3)) {
Linus Torvalds's avatar
Linus Torvalds committed
436 437 438
				buf += 3;
				while (isspace(*buf))
					++buf;
439 440 441 442
				start = simple_strtoul(buf, &buf, 0);
				pnp_res = pnp_add_dma_resource(dev, start, 0);
				if (pnp_res)
					pnp_res->index = ndma++;
Linus Torvalds's avatar
Linus Torvalds committed
443 444 445 446
				continue;
			}
			break;
		}
447
		mutex_unlock(&pnp_res_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
448 449
		goto done;
	}
Bjorn Helgaas's avatar
Bjorn Helgaas committed
450 451

done:
Linus Torvalds's avatar
Linus Torvalds committed
452 453 454 455 456
	if (retval < 0)
		return retval;
	return count;
}

457 458
static DEVICE_ATTR(resources, S_IRUGO | S_IWUSR,
		   pnp_show_current_resources, pnp_set_current_resources);
Linus Torvalds's avatar
Linus Torvalds committed
459

460 461
static ssize_t pnp_show_current_ids(struct device *dmdev,
				    struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
462 463 464
{
	char *str = buf;
	struct pnp_dev *dev = to_pnp_dev(dmdev);
465
	struct pnp_id *pos = dev->id;
Linus Torvalds's avatar
Linus Torvalds committed
466 467

	while (pos) {
468
		str += sprintf(str, "%s\n", pos->id);
Linus Torvalds's avatar
Linus Torvalds committed
469 470 471 472 473
		pos = pos->next;
	}
	return (str - buf);
}

474
static DEVICE_ATTR(id, S_IRUGO, pnp_show_current_ids, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
475 476 477

int pnp_interface_attach_device(struct pnp_dev *dev)
{
478
	int rc = device_create_file(&dev->dev, &dev_attr_options);
Bjorn Helgaas's avatar
Bjorn Helgaas committed
479

480 481 482 483 484 485 486 487
	if (rc)
		goto err;
	rc = device_create_file(&dev->dev, &dev_attr_resources);
	if (rc)
		goto err_opt;
	rc = device_create_file(&dev->dev, &dev_attr_id);
	if (rc)
		goto err_res;
488

Linus Torvalds's avatar
Linus Torvalds committed
489
	return 0;
490

Bjorn Helgaas's avatar
Bjorn Helgaas committed
491
err_res:
492
	device_remove_file(&dev->dev, &dev_attr_resources);
Bjorn Helgaas's avatar
Bjorn Helgaas committed
493
err_opt:
494
	device_remove_file(&dev->dev, &dev_attr_options);
Bjorn Helgaas's avatar
Bjorn Helgaas committed
495
err:
496
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
497
}