interface.c revision d5ebde6ef5c2d51828f975a81d7d0e58bccfd833
1/*
2 * interface.c - contains everything related to the user interface
3 *
4 * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela <perex@perex.cz>
5 * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
6 */
7
8#include <linux/pnp.h>
9#include <linux/string.h>
10#include <linux/errno.h>
11#include <linux/list.h>
12#include <linux/types.h>
13#include <linux/pnp.h>
14#include <linux/stat.h>
15#include <linux/ctype.h>
16#include <linux/slab.h>
17#include <linux/mutex.h>
18
19#include <asm/uaccess.h>
20
21#include "base.h"
22
23struct pnp_info_buffer {
24	char *buffer;		/* pointer to begin of buffer */
25	char *curr;		/* current position in buffer */
26	unsigned long size;	/* current size */
27	unsigned long len;	/* total length of buffer */
28	int stop;		/* stop flag */
29	int error;		/* error code */
30};
31
32typedef struct pnp_info_buffer pnp_info_buffer_t;
33
34static int pnp_printf(pnp_info_buffer_t * buffer, char *fmt, ...)
35{
36	va_list args;
37	int res;
38
39	if (buffer->stop || buffer->error)
40		return 0;
41	va_start(args, fmt);
42	res = vsnprintf(buffer->curr, buffer->len - buffer->size, fmt, args);
43	va_end(args);
44	if (buffer->size + res >= buffer->len) {
45		buffer->stop = 1;
46		return 0;
47	}
48	buffer->curr += res;
49	buffer->size += res;
50	return res;
51}
52
53static void pnp_print_port(pnp_info_buffer_t * buffer, char *space,
54			   struct pnp_port *port)
55{
56	pnp_printf(buffer, "%sport %#llx-%#llx, align %#llx, size %#llx, "
57		   "%i-bit address decoding\n", space,
58		   (unsigned long long) port->min,
59		   (unsigned long long) port->max,
60		   port->align ? ((unsigned long long) port->align - 1) : 0,
61		   (unsigned long long) port->size,
62		   port->flags & IORESOURCE_IO_16BIT_ADDR ? 16 : 10);
63}
64
65static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space,
66			  struct pnp_irq *irq)
67{
68	int first = 1, i;
69
70	pnp_printf(buffer, "%sirq ", space);
71	for (i = 0; i < PNP_IRQ_NR; i++)
72		if (test_bit(i, irq->map.bits)) {
73			if (!first) {
74				pnp_printf(buffer, ",");
75			} else {
76				first = 0;
77			}
78			if (i == 2 || i == 9)
79				pnp_printf(buffer, "2/9");
80			else
81				pnp_printf(buffer, "%i", i);
82		}
83	if (bitmap_empty(irq->map.bits, PNP_IRQ_NR))
84		pnp_printf(buffer, "<none>");
85	if (irq->flags & IORESOURCE_IRQ_HIGHEDGE)
86		pnp_printf(buffer, " High-Edge");
87	if (irq->flags & IORESOURCE_IRQ_LOWEDGE)
88		pnp_printf(buffer, " Low-Edge");
89	if (irq->flags & IORESOURCE_IRQ_HIGHLEVEL)
90		pnp_printf(buffer, " High-Level");
91	if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
92		pnp_printf(buffer, " Low-Level");
93	if (irq->flags & IORESOURCE_IRQ_OPTIONAL)
94		pnp_printf(buffer, " (optional)");
95	pnp_printf(buffer, "\n");
96}
97
98static void pnp_print_dma(pnp_info_buffer_t * buffer, char *space,
99			  struct pnp_dma *dma)
100{
101	int first = 1, i;
102	char *s;
103
104	pnp_printf(buffer, "%sdma ", space);
105	for (i = 0; i < 8; i++)
106		if (dma->map & (1 << i)) {
107			if (!first) {
108				pnp_printf(buffer, ",");
109			} else {
110				first = 0;
111			}
112			pnp_printf(buffer, "%i", i);
113		}
114	if (!dma->map)
115		pnp_printf(buffer, "<none>");
116	switch (dma->flags & IORESOURCE_DMA_TYPE_MASK) {
117	case IORESOURCE_DMA_8BIT:
118		s = "8-bit";
119		break;
120	case IORESOURCE_DMA_8AND16BIT:
121		s = "8-bit&16-bit";
122		break;
123	default:
124		s = "16-bit";
125	}
126	pnp_printf(buffer, " %s", s);
127	if (dma->flags & IORESOURCE_DMA_MASTER)
128		pnp_printf(buffer, " master");
129	if (dma->flags & IORESOURCE_DMA_BYTE)
130		pnp_printf(buffer, " byte-count");
131	if (dma->flags & IORESOURCE_DMA_WORD)
132		pnp_printf(buffer, " word-count");
133	switch (dma->flags & IORESOURCE_DMA_SPEED_MASK) {
134	case IORESOURCE_DMA_TYPEA:
135		s = "type-A";
136		break;
137	case IORESOURCE_DMA_TYPEB:
138		s = "type-B";
139		break;
140	case IORESOURCE_DMA_TYPEF:
141		s = "type-F";
142		break;
143	default:
144		s = "compatible";
145		break;
146	}
147	pnp_printf(buffer, " %s\n", s);
148}
149
150static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space,
151			  struct pnp_mem *mem)
152{
153	char *s;
154
155	pnp_printf(buffer, "%sMemory %#llx-%#llx, align %#llx, size %#llx",
156		   space, (unsigned long long) mem->min,
157		   (unsigned long long) mem->max,
158		   (unsigned long long) mem->align,
159		   (unsigned long long) mem->size);
160	if (mem->flags & IORESOURCE_MEM_WRITEABLE)
161		pnp_printf(buffer, ", writeable");
162	if (mem->flags & IORESOURCE_MEM_CACHEABLE)
163		pnp_printf(buffer, ", cacheable");
164	if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
165		pnp_printf(buffer, ", range-length");
166	if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
167		pnp_printf(buffer, ", shadowable");
168	if (mem->flags & IORESOURCE_MEM_EXPANSIONROM)
169		pnp_printf(buffer, ", expansion ROM");
170	switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
171	case IORESOURCE_MEM_8BIT:
172		s = "8-bit";
173		break;
174	case IORESOURCE_MEM_8AND16BIT:
175		s = "8-bit&16-bit";
176		break;
177	case IORESOURCE_MEM_32BIT:
178		s = "32-bit";
179		break;
180	default:
181		s = "16-bit";
182	}
183	pnp_printf(buffer, ", %s\n", s);
184}
185
186static void pnp_print_option(pnp_info_buffer_t * buffer, char *space,
187			     struct pnp_option *option, int dep)
188{
189	char *s;
190	struct pnp_port *port;
191	struct pnp_irq *irq;
192	struct pnp_dma *dma;
193	struct pnp_mem *mem;
194
195	if (dep) {
196		switch (option->priority) {
197		case PNP_RES_PRIORITY_PREFERRED:
198			s = "preferred";
199			break;
200		case PNP_RES_PRIORITY_ACCEPTABLE:
201			s = "acceptable";
202			break;
203		case PNP_RES_PRIORITY_FUNCTIONAL:
204			s = "functional";
205			break;
206		default:
207			s = "invalid";
208		}
209		pnp_printf(buffer, "Dependent: %02i - Priority %s\n", dep, s);
210	}
211
212	for (port = option->port; port; port = port->next)
213		pnp_print_port(buffer, space, port);
214	for (irq = option->irq; irq; irq = irq->next)
215		pnp_print_irq(buffer, space, irq);
216	for (dma = option->dma; dma; dma = dma->next)
217		pnp_print_dma(buffer, space, dma);
218	for (mem = option->mem; mem; mem = mem->next)
219		pnp_print_mem(buffer, space, mem);
220}
221
222static ssize_t pnp_show_options(struct device *dmdev,
223				struct device_attribute *attr, char *buf)
224{
225	struct pnp_dev *dev = to_pnp_dev(dmdev);
226	pnp_info_buffer_t *buffer;
227	struct pnp_option *independent = dev->independent;
228	struct pnp_option *dependent = dev->dependent;
229	int ret, dep = 1;
230
231	buffer = pnp_alloc(sizeof(pnp_info_buffer_t));
232	if (!buffer)
233		return -ENOMEM;
234
235	buffer->len = PAGE_SIZE;
236	buffer->buffer = buf;
237	buffer->curr = buffer->buffer;
238	if (independent)
239		pnp_print_option(buffer, "", independent, 0);
240
241	while (dependent) {
242		pnp_print_option(buffer, "   ", dependent, dep);
243		dependent = dependent->next;
244		dep++;
245	}
246	ret = (buffer->curr - buf);
247	kfree(buffer);
248	return ret;
249}
250
251static DEVICE_ATTR(options, S_IRUGO, pnp_show_options, NULL);
252
253static ssize_t pnp_show_current_resources(struct device *dmdev,
254					  struct device_attribute *attr,
255					  char *buf)
256{
257	struct pnp_dev *dev = to_pnp_dev(dmdev);
258	pnp_info_buffer_t *buffer;
259	struct pnp_resource *pnp_res;
260	struct resource *res;
261	int ret;
262
263	if (!dev)
264		return -EINVAL;
265
266	buffer = pnp_alloc(sizeof(pnp_info_buffer_t));
267	if (!buffer)
268		return -ENOMEM;
269
270	buffer->len = PAGE_SIZE;
271	buffer->buffer = buf;
272	buffer->curr = buffer->buffer;
273
274	pnp_printf(buffer, "state = %s\n", dev->active ? "active" : "disabled");
275
276	list_for_each_entry(pnp_res, &dev->resources, list) {
277		res = &pnp_res->res;
278
279		pnp_printf(buffer, pnp_resource_type_name(res));
280
281		if (res->flags & IORESOURCE_DISABLED) {
282			pnp_printf(buffer, " disabled\n");
283			continue;
284		}
285
286		switch (pnp_resource_type(res)) {
287		case IORESOURCE_IO:
288		case IORESOURCE_MEM:
289			pnp_printf(buffer, " %#llx-%#llx\n",
290				   (unsigned long long) res->start,
291				   (unsigned long long) res->end);
292			break;
293		case IORESOURCE_IRQ:
294		case IORESOURCE_DMA:
295			pnp_printf(buffer, " %lld\n",
296				   (unsigned long long) res->start);
297			break;
298		}
299	}
300
301	ret = (buffer->curr - buf);
302	kfree(buffer);
303	return ret;
304}
305
306static ssize_t pnp_set_current_resources(struct device *dmdev,
307					 struct device_attribute *attr,
308					 const char *ubuf, size_t count)
309{
310	struct pnp_dev *dev = to_pnp_dev(dmdev);
311	char *buf = (void *)ubuf;
312	int retval = 0;
313	resource_size_t start, end;
314
315	if (dev->status & PNP_ATTACHED) {
316		retval = -EBUSY;
317		dev_info(&dev->dev, "in use; can't configure\n");
318		goto done;
319	}
320
321	while (isspace(*buf))
322		++buf;
323	if (!strnicmp(buf, "disable", 7)) {
324		retval = pnp_disable_dev(dev);
325		goto done;
326	}
327	if (!strnicmp(buf, "activate", 8)) {
328		retval = pnp_activate_dev(dev);
329		goto done;
330	}
331	if (!strnicmp(buf, "fill", 4)) {
332		if (dev->active)
333			goto done;
334		retval = pnp_auto_config_dev(dev);
335		goto done;
336	}
337	if (!strnicmp(buf, "auto", 4)) {
338		if (dev->active)
339			goto done;
340		pnp_init_resources(dev);
341		retval = pnp_auto_config_dev(dev);
342		goto done;
343	}
344	if (!strnicmp(buf, "clear", 5)) {
345		if (dev->active)
346			goto done;
347		pnp_init_resources(dev);
348		goto done;
349	}
350	if (!strnicmp(buf, "get", 3)) {
351		mutex_lock(&pnp_res_mutex);
352		if (pnp_can_read(dev))
353			dev->protocol->get(dev);
354		mutex_unlock(&pnp_res_mutex);
355		goto done;
356	}
357	if (!strnicmp(buf, "set", 3)) {
358		if (dev->active)
359			goto done;
360		buf += 3;
361		pnp_init_resources(dev);
362		mutex_lock(&pnp_res_mutex);
363		while (1) {
364			while (isspace(*buf))
365				++buf;
366			if (!strnicmp(buf, "io", 2)) {
367				buf += 2;
368				while (isspace(*buf))
369					++buf;
370				start = simple_strtoul(buf, &buf, 0);
371				while (isspace(*buf))
372					++buf;
373				if (*buf == '-') {
374					buf += 1;
375					while (isspace(*buf))
376						++buf;
377					end = simple_strtoul(buf, &buf, 0);
378				} else
379					end = start;
380				pnp_add_io_resource(dev, start, end, 0);
381				continue;
382			}
383			if (!strnicmp(buf, "mem", 3)) {
384				buf += 3;
385				while (isspace(*buf))
386					++buf;
387				start = simple_strtoul(buf, &buf, 0);
388				while (isspace(*buf))
389					++buf;
390				if (*buf == '-') {
391					buf += 1;
392					while (isspace(*buf))
393						++buf;
394					end = simple_strtoul(buf, &buf, 0);
395				} else
396					end = start;
397				pnp_add_mem_resource(dev, start, end, 0);
398				continue;
399			}
400			if (!strnicmp(buf, "irq", 3)) {
401				buf += 3;
402				while (isspace(*buf))
403					++buf;
404				start = simple_strtoul(buf, &buf, 0);
405				pnp_add_irq_resource(dev, start, 0);
406				continue;
407			}
408			if (!strnicmp(buf, "dma", 3)) {
409				buf += 3;
410				while (isspace(*buf))
411					++buf;
412				start = simple_strtoul(buf, &buf, 0);
413				pnp_add_dma_resource(dev, start, 0);
414				continue;
415			}
416			break;
417		}
418		mutex_unlock(&pnp_res_mutex);
419		goto done;
420	}
421
422done:
423	if (retval < 0)
424		return retval;
425	return count;
426}
427
428static DEVICE_ATTR(resources, S_IRUGO | S_IWUSR,
429		   pnp_show_current_resources, pnp_set_current_resources);
430
431static ssize_t pnp_show_current_ids(struct device *dmdev,
432				    struct device_attribute *attr, char *buf)
433{
434	char *str = buf;
435	struct pnp_dev *dev = to_pnp_dev(dmdev);
436	struct pnp_id *pos = dev->id;
437
438	while (pos) {
439		str += sprintf(str, "%s\n", pos->id);
440		pos = pos->next;
441	}
442	return (str - buf);
443}
444
445static DEVICE_ATTR(id, S_IRUGO, pnp_show_current_ids, NULL);
446
447int pnp_interface_attach_device(struct pnp_dev *dev)
448{
449	int rc = device_create_file(&dev->dev, &dev_attr_options);
450
451	if (rc)
452		goto err;
453	rc = device_create_file(&dev->dev, &dev_attr_resources);
454	if (rc)
455		goto err_opt;
456	rc = device_create_file(&dev->dev, &dev_attr_id);
457	if (rc)
458		goto err_res;
459
460	return 0;
461
462err_res:
463	device_remove_file(&dev->dev, &dev_attr_resources);
464err_opt:
465	device_remove_file(&dev->dev, &dev_attr_options);
466err:
467	return rc;
468}
469