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