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