diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pnp/isapnp |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/pnp/isapnp')
-rw-r--r-- | drivers/pnp/isapnp/Kconfig | 11 | ||||
-rw-r--r-- | drivers/pnp/isapnp/Makefile | 7 | ||||
-rw-r--r-- | drivers/pnp/isapnp/compat.c | 91 | ||||
-rw-r--r-- | drivers/pnp/isapnp/core.c | 1156 | ||||
-rw-r--r-- | drivers/pnp/isapnp/proc.c | 171 |
5 files changed, 1436 insertions, 0 deletions
diff --git a/drivers/pnp/isapnp/Kconfig b/drivers/pnp/isapnp/Kconfig new file mode 100644 index 000000000000..578651eeb4b0 --- /dev/null +++ b/drivers/pnp/isapnp/Kconfig | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # ISA Plug and Play configuration | ||
3 | # | ||
4 | config ISAPNP | ||
5 | bool "ISA Plug and Play support" | ||
6 | depends on PNP && ISA | ||
7 | help | ||
8 | Say Y here if you would like support for ISA Plug and Play devices. | ||
9 | Some information is in <file:Documentation/isapnp.txt>. | ||
10 | |||
11 | If unsure, say Y. | ||
diff --git a/drivers/pnp/isapnp/Makefile b/drivers/pnp/isapnp/Makefile new file mode 100644 index 000000000000..cac18bbfb817 --- /dev/null +++ b/drivers/pnp/isapnp/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for the kernel ISAPNP driver. | ||
3 | # | ||
4 | |||
5 | isapnp-proc-$(CONFIG_PROC_FS) = proc.o | ||
6 | |||
7 | obj-y := core.o compat.o $(isapnp-proc-y) | ||
diff --git a/drivers/pnp/isapnp/compat.c b/drivers/pnp/isapnp/compat.c new file mode 100644 index 000000000000..3ff7e76b33bd --- /dev/null +++ b/drivers/pnp/isapnp/compat.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * compat.c - A series of functions to make it easier to convert drivers that use | ||
3 | * the old isapnp APIs. If possible use the new APIs instead. | ||
4 | * | ||
5 | * Copyright 2002 Adam Belay <ambx1@neo.rr.com> | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | /* TODO: see if more isapnp functions are needed here */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/isapnp.h> | ||
14 | #include <linux/string.h> | ||
15 | |||
16 | static void pnp_convert_id(char *buf, unsigned short vendor, unsigned short device) | ||
17 | { | ||
18 | sprintf(buf, "%c%c%c%x%x%x%x", | ||
19 | 'A' + ((vendor >> 2) & 0x3f) - 1, | ||
20 | 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, | ||
21 | 'A' + ((vendor >> 8) & 0x1f) - 1, | ||
22 | (device >> 4) & 0x0f, | ||
23 | device & 0x0f, | ||
24 | (device >> 12) & 0x0f, | ||
25 | (device >> 8) & 0x0f); | ||
26 | } | ||
27 | |||
28 | struct pnp_card *pnp_find_card(unsigned short vendor, | ||
29 | unsigned short device, | ||
30 | struct pnp_card *from) | ||
31 | { | ||
32 | char id[8]; | ||
33 | char any[8]; | ||
34 | struct list_head *list; | ||
35 | pnp_convert_id(id, vendor, device); | ||
36 | pnp_convert_id(any, ISAPNP_ANY_ID, ISAPNP_ANY_ID); | ||
37 | |||
38 | list = from ? from->global_list.next : pnp_cards.next; | ||
39 | |||
40 | while (list != &pnp_cards) { | ||
41 | struct pnp_card *card = global_to_pnp_card(list); | ||
42 | if (compare_pnp_id(card->id,id) || (memcmp(id,any,7)==0)) | ||
43 | return card; | ||
44 | list = list->next; | ||
45 | } | ||
46 | return NULL; | ||
47 | } | ||
48 | |||
49 | struct pnp_dev *pnp_find_dev(struct pnp_card *card, | ||
50 | unsigned short vendor, | ||
51 | unsigned short function, | ||
52 | struct pnp_dev *from) | ||
53 | { | ||
54 | char id[8]; | ||
55 | char any[8]; | ||
56 | pnp_convert_id(id, vendor, function); | ||
57 | pnp_convert_id(any, ISAPNP_ANY_ID, ISAPNP_ANY_ID); | ||
58 | if (card == NULL) { /* look for a logical device from all cards */ | ||
59 | struct list_head *list; | ||
60 | |||
61 | list = pnp_global.next; | ||
62 | if (from) | ||
63 | list = from->global_list.next; | ||
64 | |||
65 | while (list != &pnp_global) { | ||
66 | struct pnp_dev *dev = global_to_pnp_dev(list); | ||
67 | if (compare_pnp_id(dev->id,id) || (memcmp(id,any,7)==0)) | ||
68 | return dev; | ||
69 | list = list->next; | ||
70 | } | ||
71 | } else { | ||
72 | struct list_head *list; | ||
73 | |||
74 | list = card->devices.next; | ||
75 | if (from) { | ||
76 | list = from->card_list.next; | ||
77 | if (from->card != card) /* something is wrong */ | ||
78 | return NULL; | ||
79 | } | ||
80 | while (list != &card->devices) { | ||
81 | struct pnp_dev *dev = card_to_pnp_dev(list); | ||
82 | if (compare_pnp_id(dev->id,id)) | ||
83 | return dev; | ||
84 | list = list->next; | ||
85 | } | ||
86 | } | ||
87 | return NULL; | ||
88 | } | ||
89 | |||
90 | EXPORT_SYMBOL(pnp_find_card); | ||
91 | EXPORT_SYMBOL(pnp_find_dev); | ||
diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c new file mode 100644 index 000000000000..82c5edd5b9ee --- /dev/null +++ b/drivers/pnp/isapnp/core.c | |||
@@ -0,0 +1,1156 @@ | |||
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 | * Changelog: | ||
21 | * 2000-01-01 Added quirks handling for buggy hardware | ||
22 | * Peter Denison <peterd@pnd-pc.demon.co.uk> | ||
23 | * 2000-06-14 Added isapnp_probe_devs() and isapnp_activate_dev() | ||
24 | * Christoph Hellwig <hch@infradead.org> | ||
25 | * 2001-06-03 Added release_region calls to correspond with | ||
26 | * request_region calls when a failure occurs. Also | ||
27 | * added KERN_* constants to printk() calls. | ||
28 | * 2001-11-07 Added isapnp_{,un}register_driver calls along the lines | ||
29 | * of the pci driver interface | ||
30 | * Kai Germaschewski <kai.germaschewski@gmx.de> | ||
31 | * 2002-06-06 Made the use of dma channel 0 configurable | ||
32 | * Gerald Teschl <gerald.teschl@univie.ac.at> | ||
33 | * 2002-10-06 Ported to PnP Layer - Adam Belay <ambx1@neo.rr.com> | ||
34 | * 2003-08-11 Resource Management Updates - Adam Belay <ambx1@neo.rr.com> | ||
35 | */ | ||
36 | |||
37 | #include <linux/config.h> | ||
38 | #include <linux/module.h> | ||
39 | #include <linux/kernel.h> | ||
40 | #include <linux/errno.h> | ||
41 | #include <linux/slab.h> | ||
42 | #include <linux/delay.h> | ||
43 | #include <linux/init.h> | ||
44 | #include <linux/isapnp.h> | ||
45 | #include <asm/io.h> | ||
46 | |||
47 | #if 0 | ||
48 | #define ISAPNP_REGION_OK | ||
49 | #endif | ||
50 | #if 0 | ||
51 | #define ISAPNP_DEBUG | ||
52 | #endif | ||
53 | |||
54 | int isapnp_disable; /* Disable ISA PnP */ | ||
55 | static int isapnp_rdp; /* Read Data Port */ | ||
56 | static int isapnp_reset = 1; /* reset all PnP cards (deactivate) */ | ||
57 | static int isapnp_verbose = 1; /* verbose mode */ | ||
58 | |||
59 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
60 | MODULE_DESCRIPTION("Generic ISA Plug & Play support"); | ||
61 | module_param(isapnp_disable, int, 0); | ||
62 | MODULE_PARM_DESC(isapnp_disable, "ISA Plug & Play disable"); | ||
63 | module_param(isapnp_rdp, int, 0); | ||
64 | MODULE_PARM_DESC(isapnp_rdp, "ISA Plug & Play read data port"); | ||
65 | module_param(isapnp_reset, int, 0); | ||
66 | MODULE_PARM_DESC(isapnp_reset, "ISA Plug & Play reset all cards"); | ||
67 | module_param(isapnp_verbose, int, 0); | ||
68 | MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode"); | ||
69 | MODULE_LICENSE("GPL"); | ||
70 | |||
71 | #define _PIDXR 0x279 | ||
72 | #define _PNPWRP 0xa79 | ||
73 | |||
74 | /* short tags */ | ||
75 | #define _STAG_PNPVERNO 0x01 | ||
76 | #define _STAG_LOGDEVID 0x02 | ||
77 | #define _STAG_COMPATDEVID 0x03 | ||
78 | #define _STAG_IRQ 0x04 | ||
79 | #define _STAG_DMA 0x05 | ||
80 | #define _STAG_STARTDEP 0x06 | ||
81 | #define _STAG_ENDDEP 0x07 | ||
82 | #define _STAG_IOPORT 0x08 | ||
83 | #define _STAG_FIXEDIO 0x09 | ||
84 | #define _STAG_VENDOR 0x0e | ||
85 | #define _STAG_END 0x0f | ||
86 | /* long tags */ | ||
87 | #define _LTAG_MEMRANGE 0x81 | ||
88 | #define _LTAG_ANSISTR 0x82 | ||
89 | #define _LTAG_UNICODESTR 0x83 | ||
90 | #define _LTAG_VENDOR 0x84 | ||
91 | #define _LTAG_MEM32RANGE 0x85 | ||
92 | #define _LTAG_FIXEDMEM32RANGE 0x86 | ||
93 | |||
94 | static unsigned char isapnp_checksum_value; | ||
95 | static DECLARE_MUTEX(isapnp_cfg_mutex); | ||
96 | static int isapnp_detected; | ||
97 | static int isapnp_csn_count; | ||
98 | |||
99 | /* some prototypes */ | ||
100 | |||
101 | static inline void write_data(unsigned char x) | ||
102 | { | ||
103 | outb(x, _PNPWRP); | ||
104 | } | ||
105 | |||
106 | static inline void write_address(unsigned char x) | ||
107 | { | ||
108 | outb(x, _PIDXR); | ||
109 | udelay(20); | ||
110 | } | ||
111 | |||
112 | static inline unsigned char read_data(void) | ||
113 | { | ||
114 | unsigned char val = inb(isapnp_rdp); | ||
115 | return val; | ||
116 | } | ||
117 | |||
118 | unsigned char isapnp_read_byte(unsigned char idx) | ||
119 | { | ||
120 | write_address(idx); | ||
121 | return read_data(); | ||
122 | } | ||
123 | |||
124 | static unsigned short isapnp_read_word(unsigned char idx) | ||
125 | { | ||
126 | unsigned short val; | ||
127 | |||
128 | val = isapnp_read_byte(idx); | ||
129 | val = (val << 8) + isapnp_read_byte(idx+1); | ||
130 | return val; | ||
131 | } | ||
132 | |||
133 | void isapnp_write_byte(unsigned char idx, unsigned char val) | ||
134 | { | ||
135 | write_address(idx); | ||
136 | write_data(val); | ||
137 | } | ||
138 | |||
139 | static void isapnp_write_word(unsigned char idx, unsigned short val) | ||
140 | { | ||
141 | isapnp_write_byte(idx, val >> 8); | ||
142 | isapnp_write_byte(idx+1, val); | ||
143 | } | ||
144 | |||
145 | static void *isapnp_alloc(long size) | ||
146 | { | ||
147 | void *result; | ||
148 | |||
149 | result = kmalloc(size, GFP_KERNEL); | ||
150 | if (!result) | ||
151 | return NULL; | ||
152 | memset(result, 0, size); | ||
153 | return result; | ||
154 | } | ||
155 | |||
156 | static void isapnp_key(void) | ||
157 | { | ||
158 | unsigned char code = 0x6a, msb; | ||
159 | int i; | ||
160 | |||
161 | mdelay(1); | ||
162 | write_address(0x00); | ||
163 | write_address(0x00); | ||
164 | |||
165 | write_address(code); | ||
166 | |||
167 | for (i = 1; i < 32; i++) { | ||
168 | msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7; | ||
169 | code = (code >> 1) | msb; | ||
170 | write_address(code); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* place all pnp cards in wait-for-key state */ | ||
175 | static void isapnp_wait(void) | ||
176 | { | ||
177 | isapnp_write_byte(0x02, 0x02); | ||
178 | } | ||
179 | |||
180 | static void isapnp_wake(unsigned char csn) | ||
181 | { | ||
182 | isapnp_write_byte(0x03, csn); | ||
183 | } | ||
184 | |||
185 | static void isapnp_device(unsigned char logdev) | ||
186 | { | ||
187 | isapnp_write_byte(0x07, logdev); | ||
188 | } | ||
189 | |||
190 | static void isapnp_activate(unsigned char logdev) | ||
191 | { | ||
192 | isapnp_device(logdev); | ||
193 | isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 1); | ||
194 | udelay(250); | ||
195 | } | ||
196 | |||
197 | static void isapnp_deactivate(unsigned char logdev) | ||
198 | { | ||
199 | isapnp_device(logdev); | ||
200 | isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 0); | ||
201 | udelay(500); | ||
202 | } | ||
203 | |||
204 | static void __init isapnp_peek(unsigned char *data, int bytes) | ||
205 | { | ||
206 | int i, j; | ||
207 | unsigned char d=0; | ||
208 | |||
209 | for (i = 1; i <= bytes; i++) { | ||
210 | for (j = 0; j < 20; j++) { | ||
211 | d = isapnp_read_byte(0x05); | ||
212 | if (d & 1) | ||
213 | break; | ||
214 | udelay(100); | ||
215 | } | ||
216 | if (!(d & 1)) { | ||
217 | if (data != NULL) | ||
218 | *data++ = 0xff; | ||
219 | continue; | ||
220 | } | ||
221 | d = isapnp_read_byte(0x04); /* PRESDI */ | ||
222 | isapnp_checksum_value += d; | ||
223 | if (data != NULL) | ||
224 | *data++ = d; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | #define RDP_STEP 32 /* minimum is 4 */ | ||
229 | |||
230 | static int isapnp_next_rdp(void) | ||
231 | { | ||
232 | int rdp = isapnp_rdp; | ||
233 | static int old_rdp = 0; | ||
234 | |||
235 | if(old_rdp) | ||
236 | { | ||
237 | release_region(old_rdp, 1); | ||
238 | old_rdp = 0; | ||
239 | } | ||
240 | while (rdp <= 0x3ff) { | ||
241 | /* | ||
242 | * We cannot use NE2000 probe spaces for ISAPnP or we | ||
243 | * will lock up machines. | ||
244 | */ | ||
245 | if ((rdp < 0x280 || rdp > 0x380) && request_region(rdp, 1, "ISAPnP")) | ||
246 | { | ||
247 | isapnp_rdp = rdp; | ||
248 | old_rdp = rdp; | ||
249 | return 0; | ||
250 | } | ||
251 | rdp += RDP_STEP; | ||
252 | } | ||
253 | return -1; | ||
254 | } | ||
255 | |||
256 | /* Set read port address */ | ||
257 | static inline void isapnp_set_rdp(void) | ||
258 | { | ||
259 | isapnp_write_byte(0x00, isapnp_rdp >> 2); | ||
260 | udelay(100); | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Perform an isolation. The port selection code now tries to avoid | ||
265 | * "dangerous to read" ports. | ||
266 | */ | ||
267 | |||
268 | static int __init isapnp_isolate_rdp_select(void) | ||
269 | { | ||
270 | isapnp_wait(); | ||
271 | isapnp_key(); | ||
272 | |||
273 | /* Control: reset CSN and conditionally everything else too */ | ||
274 | isapnp_write_byte(0x02, isapnp_reset ? 0x05 : 0x04); | ||
275 | mdelay(2); | ||
276 | |||
277 | isapnp_wait(); | ||
278 | isapnp_key(); | ||
279 | isapnp_wake(0x00); | ||
280 | |||
281 | if (isapnp_next_rdp() < 0) { | ||
282 | isapnp_wait(); | ||
283 | return -1; | ||
284 | } | ||
285 | |||
286 | isapnp_set_rdp(); | ||
287 | udelay(1000); | ||
288 | write_address(0x01); | ||
289 | udelay(1000); | ||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Isolate (assign uniqued CSN) to all ISA PnP devices. | ||
295 | */ | ||
296 | |||
297 | static int __init isapnp_isolate(void) | ||
298 | { | ||
299 | unsigned char checksum = 0x6a; | ||
300 | unsigned char chksum = 0x00; | ||
301 | unsigned char bit = 0x00; | ||
302 | int data; | ||
303 | int csn = 0; | ||
304 | int i; | ||
305 | int iteration = 1; | ||
306 | |||
307 | isapnp_rdp = 0x213; | ||
308 | if (isapnp_isolate_rdp_select() < 0) | ||
309 | return -1; | ||
310 | |||
311 | while (1) { | ||
312 | for (i = 1; i <= 64; i++) { | ||
313 | data = read_data() << 8; | ||
314 | udelay(250); | ||
315 | data = data | read_data(); | ||
316 | udelay(250); | ||
317 | if (data == 0x55aa) | ||
318 | bit = 0x01; | ||
319 | checksum = ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) | (checksum >> 1); | ||
320 | bit = 0x00; | ||
321 | } | ||
322 | for (i = 65; i <= 72; i++) { | ||
323 | data = read_data() << 8; | ||
324 | udelay(250); | ||
325 | data = data | read_data(); | ||
326 | udelay(250); | ||
327 | if (data == 0x55aa) | ||
328 | chksum |= (1 << (i - 65)); | ||
329 | } | ||
330 | if (checksum != 0x00 && checksum == chksum) { | ||
331 | csn++; | ||
332 | |||
333 | isapnp_write_byte(0x06, csn); | ||
334 | udelay(250); | ||
335 | iteration++; | ||
336 | isapnp_wake(0x00); | ||
337 | isapnp_set_rdp(); | ||
338 | udelay(1000); | ||
339 | write_address(0x01); | ||
340 | udelay(1000); | ||
341 | goto __next; | ||
342 | } | ||
343 | if (iteration == 1) { | ||
344 | isapnp_rdp += RDP_STEP; | ||
345 | if (isapnp_isolate_rdp_select() < 0) | ||
346 | return -1; | ||
347 | } else if (iteration > 1) { | ||
348 | break; | ||
349 | } | ||
350 | __next: | ||
351 | if (csn == 255) | ||
352 | break; | ||
353 | checksum = 0x6a; | ||
354 | chksum = 0x00; | ||
355 | bit = 0x00; | ||
356 | } | ||
357 | isapnp_wait(); | ||
358 | isapnp_csn_count = csn; | ||
359 | return csn; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * Read one tag from stream. | ||
364 | */ | ||
365 | |||
366 | static int __init isapnp_read_tag(unsigned char *type, unsigned short *size) | ||
367 | { | ||
368 | unsigned char tag, tmp[2]; | ||
369 | |||
370 | isapnp_peek(&tag, 1); | ||
371 | if (tag == 0) /* invalid tag */ | ||
372 | return -1; | ||
373 | if (tag & 0x80) { /* large item */ | ||
374 | *type = tag; | ||
375 | isapnp_peek(tmp, 2); | ||
376 | *size = (tmp[1] << 8) | tmp[0]; | ||
377 | } else { | ||
378 | *type = (tag >> 3) & 0x0f; | ||
379 | *size = tag & 0x07; | ||
380 | } | ||
381 | #if 0 | ||
382 | printk(KERN_DEBUG "tag = 0x%x, type = 0x%x, size = %i\n", tag, *type, *size); | ||
383 | #endif | ||
384 | if (type == 0) /* wrong type */ | ||
385 | return -1; | ||
386 | if (*type == 0xff && *size == 0xffff) /* probably invalid data */ | ||
387 | return -1; | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | * Skip specified number of bytes from stream. | ||
393 | */ | ||
394 | |||
395 | static void __init isapnp_skip_bytes(int count) | ||
396 | { | ||
397 | isapnp_peek(NULL, count); | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * Parse EISA id. | ||
402 | */ | ||
403 | |||
404 | static void isapnp_parse_id(struct pnp_dev * dev, unsigned short vendor, unsigned short device) | ||
405 | { | ||
406 | struct pnp_id * id; | ||
407 | if (!dev) | ||
408 | return; | ||
409 | id = isapnp_alloc(sizeof(struct pnp_id)); | ||
410 | if (!id) | ||
411 | return; | ||
412 | sprintf(id->id, "%c%c%c%x%x%x%x", | ||
413 | 'A' + ((vendor >> 2) & 0x3f) - 1, | ||
414 | 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, | ||
415 | 'A' + ((vendor >> 8) & 0x1f) - 1, | ||
416 | (device >> 4) & 0x0f, | ||
417 | device & 0x0f, | ||
418 | (device >> 12) & 0x0f, | ||
419 | (device >> 8) & 0x0f); | ||
420 | pnp_add_id(id, dev); | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * Parse logical device tag. | ||
425 | */ | ||
426 | |||
427 | static struct pnp_dev * __init isapnp_parse_device(struct pnp_card *card, int size, int number) | ||
428 | { | ||
429 | unsigned char tmp[6]; | ||
430 | struct pnp_dev *dev; | ||
431 | |||
432 | isapnp_peek(tmp, size); | ||
433 | dev = isapnp_alloc(sizeof(struct pnp_dev)); | ||
434 | if (!dev) | ||
435 | return NULL; | ||
436 | dev->number = number; | ||
437 | isapnp_parse_id(dev, (tmp[1] << 8) | tmp[0], (tmp[3] << 8) | tmp[2]); | ||
438 | dev->regs = tmp[4]; | ||
439 | dev->card = card; | ||
440 | if (size > 5) | ||
441 | dev->regs |= tmp[5] << 8; | ||
442 | dev->protocol = &isapnp_protocol; | ||
443 | dev->capabilities |= PNP_CONFIGURABLE; | ||
444 | dev->capabilities |= PNP_READ; | ||
445 | dev->capabilities |= PNP_WRITE; | ||
446 | dev->capabilities |= PNP_DISABLE; | ||
447 | pnp_init_resource_table(&dev->res); | ||
448 | return dev; | ||
449 | } | ||
450 | |||
451 | |||
452 | /* | ||
453 | * Add IRQ resource to resources list. | ||
454 | */ | ||
455 | |||
456 | static void __init isapnp_parse_irq_resource(struct pnp_option *option, | ||
457 | int size) | ||
458 | { | ||
459 | unsigned char tmp[3]; | ||
460 | struct pnp_irq *irq; | ||
461 | unsigned long bits; | ||
462 | |||
463 | isapnp_peek(tmp, size); | ||
464 | irq = isapnp_alloc(sizeof(struct pnp_irq)); | ||
465 | if (!irq) | ||
466 | return; | ||
467 | bits = (tmp[1] << 8) | tmp[0]; | ||
468 | bitmap_copy(irq->map, &bits, 16); | ||
469 | if (size > 2) | ||
470 | irq->flags = tmp[2]; | ||
471 | else | ||
472 | irq->flags = IORESOURCE_IRQ_HIGHEDGE; | ||
473 | pnp_register_irq_resource(option, irq); | ||
474 | return; | ||
475 | } | ||
476 | |||
477 | /* | ||
478 | * Add DMA resource to resources list. | ||
479 | */ | ||
480 | |||
481 | static void __init isapnp_parse_dma_resource(struct pnp_option *option, | ||
482 | int size) | ||
483 | { | ||
484 | unsigned char tmp[2]; | ||
485 | struct pnp_dma *dma; | ||
486 | |||
487 | isapnp_peek(tmp, size); | ||
488 | dma = isapnp_alloc(sizeof(struct pnp_dma)); | ||
489 | if (!dma) | ||
490 | return; | ||
491 | dma->map = tmp[0]; | ||
492 | dma->flags = tmp[1]; | ||
493 | pnp_register_dma_resource(option, dma); | ||
494 | return; | ||
495 | } | ||
496 | |||
497 | /* | ||
498 | * Add port resource to resources list. | ||
499 | */ | ||
500 | |||
501 | static void __init isapnp_parse_port_resource(struct pnp_option *option, | ||
502 | int size) | ||
503 | { | ||
504 | unsigned char tmp[7]; | ||
505 | struct pnp_port *port; | ||
506 | |||
507 | isapnp_peek(tmp, size); | ||
508 | port = isapnp_alloc(sizeof(struct pnp_port)); | ||
509 | if (!port) | ||
510 | return; | ||
511 | port->min = (tmp[2] << 8) | tmp[1]; | ||
512 | port->max = (tmp[4] << 8) | tmp[3]; | ||
513 | port->align = tmp[5]; | ||
514 | port->size = tmp[6]; | ||
515 | port->flags = tmp[0] ? PNP_PORT_FLAG_16BITADDR : 0; | ||
516 | pnp_register_port_resource(option,port); | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | /* | ||
521 | * Add fixed port resource to resources list. | ||
522 | */ | ||
523 | |||
524 | static void __init isapnp_parse_fixed_port_resource(struct pnp_option *option, | ||
525 | int size) | ||
526 | { | ||
527 | unsigned char tmp[3]; | ||
528 | struct pnp_port *port; | ||
529 | |||
530 | isapnp_peek(tmp, size); | ||
531 | port = isapnp_alloc(sizeof(struct pnp_port)); | ||
532 | if (!port) | ||
533 | return; | ||
534 | port->min = port->max = (tmp[1] << 8) | tmp[0]; | ||
535 | port->size = tmp[2]; | ||
536 | port->align = 0; | ||
537 | port->flags = PNP_PORT_FLAG_FIXED; | ||
538 | pnp_register_port_resource(option,port); | ||
539 | return; | ||
540 | } | ||
541 | |||
542 | /* | ||
543 | * Add memory resource to resources list. | ||
544 | */ | ||
545 | |||
546 | static void __init isapnp_parse_mem_resource(struct pnp_option *option, | ||
547 | int size) | ||
548 | { | ||
549 | unsigned char tmp[9]; | ||
550 | struct pnp_mem *mem; | ||
551 | |||
552 | isapnp_peek(tmp, size); | ||
553 | mem = isapnp_alloc(sizeof(struct pnp_mem)); | ||
554 | if (!mem) | ||
555 | return; | ||
556 | mem->min = ((tmp[2] << 8) | tmp[1]) << 8; | ||
557 | mem->max = ((tmp[4] << 8) | tmp[3]) << 8; | ||
558 | mem->align = (tmp[6] << 8) | tmp[5]; | ||
559 | mem->size = ((tmp[8] << 8) | tmp[7]) << 8; | ||
560 | mem->flags = tmp[0]; | ||
561 | pnp_register_mem_resource(option,mem); | ||
562 | return; | ||
563 | } | ||
564 | |||
565 | /* | ||
566 | * Add 32-bit memory resource to resources list. | ||
567 | */ | ||
568 | |||
569 | static void __init isapnp_parse_mem32_resource(struct pnp_option *option, | ||
570 | int size) | ||
571 | { | ||
572 | unsigned char tmp[17]; | ||
573 | struct pnp_mem *mem; | ||
574 | |||
575 | isapnp_peek(tmp, size); | ||
576 | mem = isapnp_alloc(sizeof(struct pnp_mem)); | ||
577 | if (!mem) | ||
578 | return; | ||
579 | mem->min = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; | ||
580 | mem->max = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; | ||
581 | mem->align = (tmp[12] << 24) | (tmp[11] << 16) | (tmp[10] << 8) | tmp[9]; | ||
582 | mem->size = (tmp[16] << 24) | (tmp[15] << 16) | (tmp[14] << 8) | tmp[13]; | ||
583 | mem->flags = tmp[0]; | ||
584 | pnp_register_mem_resource(option,mem); | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * Add 32-bit fixed memory resource to resources list. | ||
589 | */ | ||
590 | |||
591 | static void __init isapnp_parse_fixed_mem32_resource(struct pnp_option *option, | ||
592 | int size) | ||
593 | { | ||
594 | unsigned char tmp[9]; | ||
595 | struct pnp_mem *mem; | ||
596 | |||
597 | isapnp_peek(tmp, size); | ||
598 | mem = isapnp_alloc(sizeof(struct pnp_mem)); | ||
599 | if (!mem) | ||
600 | return; | ||
601 | mem->min = mem->max = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; | ||
602 | mem->size = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; | ||
603 | mem->align = 0; | ||
604 | mem->flags = tmp[0]; | ||
605 | pnp_register_mem_resource(option,mem); | ||
606 | } | ||
607 | |||
608 | /* | ||
609 | * Parse card name for ISA PnP device. | ||
610 | */ | ||
611 | |||
612 | static void __init | ||
613 | isapnp_parse_name(char *name, unsigned int name_max, unsigned short *size) | ||
614 | { | ||
615 | if (name[0] == '\0') { | ||
616 | unsigned short size1 = *size >= name_max ? (name_max - 1) : *size; | ||
617 | isapnp_peek(name, size1); | ||
618 | name[size1] = '\0'; | ||
619 | *size -= size1; | ||
620 | |||
621 | /* clean whitespace from end of string */ | ||
622 | while (size1 > 0 && name[--size1] == ' ') | ||
623 | name[size1] = '\0'; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | /* | ||
628 | * Parse resource map for logical device. | ||
629 | */ | ||
630 | |||
631 | static int __init isapnp_create_device(struct pnp_card *card, | ||
632 | unsigned short size) | ||
633 | { | ||
634 | int number = 0, skip = 0, priority = 0, compat = 0; | ||
635 | unsigned char type, tmp[17]; | ||
636 | struct pnp_option *option; | ||
637 | struct pnp_dev *dev; | ||
638 | if ((dev = isapnp_parse_device(card, size, number++)) == NULL) | ||
639 | return 1; | ||
640 | option = pnp_register_independent_option(dev); | ||
641 | if (!option) { | ||
642 | kfree(dev); | ||
643 | return 1; | ||
644 | } | ||
645 | pnp_add_card_device(card,dev); | ||
646 | |||
647 | while (1) { | ||
648 | if (isapnp_read_tag(&type, &size)<0) | ||
649 | return 1; | ||
650 | if (skip && type != _STAG_LOGDEVID && type != _STAG_END) | ||
651 | goto __skip; | ||
652 | switch (type) { | ||
653 | case _STAG_LOGDEVID: | ||
654 | if (size >= 5 && size <= 6) { | ||
655 | if ((dev = isapnp_parse_device(card, size, number++)) == NULL) | ||
656 | return 1; | ||
657 | size = 0; | ||
658 | skip = 0; | ||
659 | option = pnp_register_independent_option(dev); | ||
660 | if (!option) | ||
661 | return 1; | ||
662 | pnp_add_card_device(card,dev); | ||
663 | } else { | ||
664 | skip = 1; | ||
665 | } | ||
666 | priority = 0; | ||
667 | compat = 0; | ||
668 | break; | ||
669 | case _STAG_COMPATDEVID: | ||
670 | if (size == 4 && compat < DEVICE_COUNT_COMPATIBLE) { | ||
671 | isapnp_peek(tmp, 4); | ||
672 | isapnp_parse_id(dev,(tmp[1] << 8) | tmp[0], (tmp[3] << 8) | tmp[2]); | ||
673 | compat++; | ||
674 | size = 0; | ||
675 | } | ||
676 | break; | ||
677 | case _STAG_IRQ: | ||
678 | if (size < 2 || size > 3) | ||
679 | goto __skip; | ||
680 | isapnp_parse_irq_resource(option, size); | ||
681 | size = 0; | ||
682 | break; | ||
683 | case _STAG_DMA: | ||
684 | if (size != 2) | ||
685 | goto __skip; | ||
686 | isapnp_parse_dma_resource(option, size); | ||
687 | size = 0; | ||
688 | break; | ||
689 | case _STAG_STARTDEP: | ||
690 | if (size > 1) | ||
691 | goto __skip; | ||
692 | priority = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE; | ||
693 | if (size > 0) { | ||
694 | isapnp_peek(tmp, size); | ||
695 | priority = 0x100 | tmp[0]; | ||
696 | size = 0; | ||
697 | } | ||
698 | option = pnp_register_dependent_option(dev,priority); | ||
699 | if (!option) | ||
700 | return 1; | ||
701 | break; | ||
702 | case _STAG_ENDDEP: | ||
703 | if (size != 0) | ||
704 | goto __skip; | ||
705 | priority = 0; | ||
706 | break; | ||
707 | case _STAG_IOPORT: | ||
708 | if (size != 7) | ||
709 | goto __skip; | ||
710 | isapnp_parse_port_resource(option, size); | ||
711 | size = 0; | ||
712 | break; | ||
713 | case _STAG_FIXEDIO: | ||
714 | if (size != 3) | ||
715 | goto __skip; | ||
716 | isapnp_parse_fixed_port_resource(option, size); | ||
717 | size = 0; | ||
718 | break; | ||
719 | case _STAG_VENDOR: | ||
720 | break; | ||
721 | case _LTAG_MEMRANGE: | ||
722 | if (size != 9) | ||
723 | goto __skip; | ||
724 | isapnp_parse_mem_resource(option, size); | ||
725 | size = 0; | ||
726 | break; | ||
727 | case _LTAG_ANSISTR: | ||
728 | isapnp_parse_name(dev->name, sizeof(dev->name), &size); | ||
729 | break; | ||
730 | case _LTAG_UNICODESTR: | ||
731 | /* silently ignore */ | ||
732 | /* who use unicode for hardware identification? */ | ||
733 | break; | ||
734 | case _LTAG_VENDOR: | ||
735 | break; | ||
736 | case _LTAG_MEM32RANGE: | ||
737 | if (size != 17) | ||
738 | goto __skip; | ||
739 | isapnp_parse_mem32_resource(option, size); | ||
740 | size = 0; | ||
741 | break; | ||
742 | case _LTAG_FIXEDMEM32RANGE: | ||
743 | if (size != 9) | ||
744 | goto __skip; | ||
745 | isapnp_parse_fixed_mem32_resource(option, size); | ||
746 | size = 0; | ||
747 | break; | ||
748 | case _STAG_END: | ||
749 | if (size > 0) | ||
750 | isapnp_skip_bytes(size); | ||
751 | return 1; | ||
752 | default: | ||
753 | printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", type, dev->number, card->number); | ||
754 | } | ||
755 | __skip: | ||
756 | if (size > 0) | ||
757 | isapnp_skip_bytes(size); | ||
758 | } | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | /* | ||
763 | * Parse resource map for ISA PnP card. | ||
764 | */ | ||
765 | |||
766 | static void __init isapnp_parse_resource_map(struct pnp_card *card) | ||
767 | { | ||
768 | unsigned char type, tmp[17]; | ||
769 | unsigned short size; | ||
770 | |||
771 | while (1) { | ||
772 | if (isapnp_read_tag(&type, &size)<0) | ||
773 | return; | ||
774 | switch (type) { | ||
775 | case _STAG_PNPVERNO: | ||
776 | if (size != 2) | ||
777 | goto __skip; | ||
778 | isapnp_peek(tmp, 2); | ||
779 | card->pnpver = tmp[0]; | ||
780 | card->productver = tmp[1]; | ||
781 | size = 0; | ||
782 | break; | ||
783 | case _STAG_LOGDEVID: | ||
784 | if (size >= 5 && size <= 6) { | ||
785 | if (isapnp_create_device(card, size)==1) | ||
786 | return; | ||
787 | size = 0; | ||
788 | } | ||
789 | break; | ||
790 | case _STAG_VENDOR: | ||
791 | break; | ||
792 | case _LTAG_ANSISTR: | ||
793 | isapnp_parse_name(card->name, sizeof(card->name), &size); | ||
794 | break; | ||
795 | case _LTAG_UNICODESTR: | ||
796 | /* silently ignore */ | ||
797 | /* who use unicode for hardware identification? */ | ||
798 | break; | ||
799 | case _LTAG_VENDOR: | ||
800 | break; | ||
801 | case _STAG_END: | ||
802 | if (size > 0) | ||
803 | isapnp_skip_bytes(size); | ||
804 | return; | ||
805 | default: | ||
806 | printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for device %i, ignored\n", type, card->number); | ||
807 | } | ||
808 | __skip: | ||
809 | if (size > 0) | ||
810 | isapnp_skip_bytes(size); | ||
811 | } | ||
812 | } | ||
813 | |||
814 | /* | ||
815 | * Compute ISA PnP checksum for first eight bytes. | ||
816 | */ | ||
817 | |||
818 | static unsigned char __init isapnp_checksum(unsigned char *data) | ||
819 | { | ||
820 | int i, j; | ||
821 | unsigned char checksum = 0x6a, bit, b; | ||
822 | |||
823 | for (i = 0; i < 8; i++) { | ||
824 | b = data[i]; | ||
825 | for (j = 0; j < 8; j++) { | ||
826 | bit = 0; | ||
827 | if (b & (1 << j)) | ||
828 | bit = 1; | ||
829 | checksum = ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) | (checksum >> 1); | ||
830 | } | ||
831 | } | ||
832 | return checksum; | ||
833 | } | ||
834 | |||
835 | /* | ||
836 | * Parse EISA id for ISA PnP card. | ||
837 | */ | ||
838 | |||
839 | static void isapnp_parse_card_id(struct pnp_card * card, unsigned short vendor, unsigned short device) | ||
840 | { | ||
841 | struct pnp_id * id = isapnp_alloc(sizeof(struct pnp_id)); | ||
842 | if (!id) | ||
843 | return; | ||
844 | sprintf(id->id, "%c%c%c%x%x%x%x", | ||
845 | 'A' + ((vendor >> 2) & 0x3f) - 1, | ||
846 | 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, | ||
847 | 'A' + ((vendor >> 8) & 0x1f) - 1, | ||
848 | (device >> 4) & 0x0f, | ||
849 | device & 0x0f, | ||
850 | (device >> 12) & 0x0f, | ||
851 | (device >> 8) & 0x0f); | ||
852 | pnp_add_card_id(id,card); | ||
853 | } | ||
854 | |||
855 | /* | ||
856 | * Build device list for all present ISA PnP devices. | ||
857 | */ | ||
858 | |||
859 | static int __init isapnp_build_device_list(void) | ||
860 | { | ||
861 | int csn; | ||
862 | unsigned char header[9], checksum; | ||
863 | struct pnp_card *card; | ||
864 | |||
865 | isapnp_wait(); | ||
866 | isapnp_key(); | ||
867 | for (csn = 1; csn <= isapnp_csn_count; csn++) { | ||
868 | isapnp_wake(csn); | ||
869 | isapnp_peek(header, 9); | ||
870 | checksum = isapnp_checksum(header); | ||
871 | #if 0 | ||
872 | printk(KERN_DEBUG "vendor: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", | ||
873 | header[0], header[1], header[2], header[3], | ||
874 | header[4], header[5], header[6], header[7], header[8]); | ||
875 | printk(KERN_DEBUG "checksum = 0x%x\n", checksum); | ||
876 | #endif | ||
877 | if ((card = isapnp_alloc(sizeof(struct pnp_card))) == NULL) | ||
878 | continue; | ||
879 | |||
880 | card->number = csn; | ||
881 | INIT_LIST_HEAD(&card->devices); | ||
882 | isapnp_parse_card_id(card, (header[1] << 8) | header[0], (header[3] << 8) | header[2]); | ||
883 | card->serial = (header[7] << 24) | (header[6] << 16) | (header[5] << 8) | header[4]; | ||
884 | isapnp_checksum_value = 0x00; | ||
885 | isapnp_parse_resource_map(card); | ||
886 | if (isapnp_checksum_value != 0x00) | ||
887 | printk(KERN_ERR "isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value); | ||
888 | card->checksum = isapnp_checksum_value; | ||
889 | card->protocol = &isapnp_protocol; | ||
890 | |||
891 | pnp_add_card(card); | ||
892 | } | ||
893 | isapnp_wait(); | ||
894 | return 0; | ||
895 | } | ||
896 | |||
897 | /* | ||
898 | * Basic configuration routines. | ||
899 | */ | ||
900 | |||
901 | int isapnp_present(void) | ||
902 | { | ||
903 | struct pnp_card *card; | ||
904 | pnp_for_each_card(card) { | ||
905 | if (card->protocol == &isapnp_protocol) | ||
906 | return 1; | ||
907 | } | ||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | int isapnp_cfg_begin(int csn, int logdev) | ||
912 | { | ||
913 | if (csn < 1 || csn > isapnp_csn_count || logdev > 10) | ||
914 | return -EINVAL; | ||
915 | down(&isapnp_cfg_mutex); | ||
916 | isapnp_wait(); | ||
917 | isapnp_key(); | ||
918 | isapnp_wake(csn); | ||
919 | #if 0 | ||
920 | /* to avoid malfunction when the isapnptools package is used */ | ||
921 | /* we must set RDP to our value again */ | ||
922 | /* it is possible to set RDP only in the isolation phase */ | ||
923 | /* Jens Thoms Toerring <Jens.Toerring@physik.fu-berlin.de> */ | ||
924 | isapnp_write_byte(0x02, 0x04); /* clear CSN of card */ | ||
925 | mdelay(2); /* is this necessary? */ | ||
926 | isapnp_wake(csn); /* bring card into sleep state */ | ||
927 | isapnp_wake(0); /* bring card into isolation state */ | ||
928 | isapnp_set_rdp(); /* reset the RDP port */ | ||
929 | udelay(1000); /* delay 1000us */ | ||
930 | isapnp_write_byte(0x06, csn); /* reset CSN to previous value */ | ||
931 | udelay(250); /* is this necessary? */ | ||
932 | #endif | ||
933 | if (logdev >= 0) | ||
934 | isapnp_device(logdev); | ||
935 | return 0; | ||
936 | } | ||
937 | |||
938 | int isapnp_cfg_end(void) | ||
939 | { | ||
940 | isapnp_wait(); | ||
941 | up(&isapnp_cfg_mutex); | ||
942 | return 0; | ||
943 | } | ||
944 | |||
945 | |||
946 | /* | ||
947 | * Inititialization. | ||
948 | */ | ||
949 | |||
950 | |||
951 | EXPORT_SYMBOL(isapnp_protocol); | ||
952 | EXPORT_SYMBOL(isapnp_present); | ||
953 | EXPORT_SYMBOL(isapnp_cfg_begin); | ||
954 | EXPORT_SYMBOL(isapnp_cfg_end); | ||
955 | EXPORT_SYMBOL(isapnp_read_byte); | ||
956 | EXPORT_SYMBOL(isapnp_write_byte); | ||
957 | |||
958 | static int isapnp_read_resources(struct pnp_dev *dev, struct pnp_resource_table *res) | ||
959 | { | ||
960 | int tmp, ret; | ||
961 | |||
962 | dev->active = isapnp_read_byte(ISAPNP_CFG_ACTIVATE); | ||
963 | if (dev->active) { | ||
964 | for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) { | ||
965 | ret = isapnp_read_word(ISAPNP_CFG_PORT + (tmp << 1)); | ||
966 | if (!ret) | ||
967 | continue; | ||
968 | res->port_resource[tmp].start = ret; | ||
969 | res->port_resource[tmp].flags = IORESOURCE_IO; | ||
970 | } | ||
971 | for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) { | ||
972 | ret = isapnp_read_word(ISAPNP_CFG_MEM + (tmp << 3)) << 8; | ||
973 | if (!ret) | ||
974 | continue; | ||
975 | res->mem_resource[tmp].start = ret; | ||
976 | res->mem_resource[tmp].flags = IORESOURCE_MEM; | ||
977 | } | ||
978 | for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) { | ||
979 | ret = (isapnp_read_word(ISAPNP_CFG_IRQ + (tmp << 1)) >> 8); | ||
980 | if (!ret) | ||
981 | continue; | ||
982 | res->irq_resource[tmp].start = res->irq_resource[tmp].end = ret; | ||
983 | res->irq_resource[tmp].flags = IORESOURCE_IRQ; | ||
984 | } | ||
985 | for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) { | ||
986 | ret = isapnp_read_byte(ISAPNP_CFG_DMA + tmp); | ||
987 | if (ret == 4) | ||
988 | continue; | ||
989 | res->dma_resource[tmp].start = res->dma_resource[tmp].end = ret; | ||
990 | res->dma_resource[tmp].flags = IORESOURCE_DMA; | ||
991 | } | ||
992 | } | ||
993 | return 0; | ||
994 | } | ||
995 | |||
996 | static int isapnp_get_resources(struct pnp_dev *dev, struct pnp_resource_table * res) | ||
997 | { | ||
998 | int ret; | ||
999 | pnp_init_resource_table(res); | ||
1000 | isapnp_cfg_begin(dev->card->number, dev->number); | ||
1001 | ret = isapnp_read_resources(dev, res); | ||
1002 | isapnp_cfg_end(); | ||
1003 | return ret; | ||
1004 | } | ||
1005 | |||
1006 | static int isapnp_set_resources(struct pnp_dev *dev, struct pnp_resource_table * res) | ||
1007 | { | ||
1008 | int tmp; | ||
1009 | |||
1010 | isapnp_cfg_begin(dev->card->number, dev->number); | ||
1011 | dev->active = 1; | ||
1012 | for (tmp = 0; tmp < PNP_MAX_PORT && (res->port_resource[tmp].flags & (IORESOURCE_IO | IORESOURCE_UNSET)) == IORESOURCE_IO; tmp++) | ||
1013 | isapnp_write_word(ISAPNP_CFG_PORT+(tmp<<1), res->port_resource[tmp].start); | ||
1014 | for (tmp = 0; tmp < PNP_MAX_IRQ && (res->irq_resource[tmp].flags & (IORESOURCE_IRQ | IORESOURCE_UNSET)) == IORESOURCE_IRQ; tmp++) { | ||
1015 | int irq = res->irq_resource[tmp].start; | ||
1016 | if (irq == 2) | ||
1017 | irq = 9; | ||
1018 | isapnp_write_byte(ISAPNP_CFG_IRQ+(tmp<<1), irq); | ||
1019 | } | ||
1020 | for (tmp = 0; tmp < PNP_MAX_DMA && (res->dma_resource[tmp].flags & (IORESOURCE_DMA | IORESOURCE_UNSET)) == IORESOURCE_DMA; tmp++) | ||
1021 | isapnp_write_byte(ISAPNP_CFG_DMA+tmp, res->dma_resource[tmp].start); | ||
1022 | for (tmp = 0; tmp < PNP_MAX_MEM && (res->mem_resource[tmp].flags & (IORESOURCE_MEM | IORESOURCE_UNSET)) == IORESOURCE_MEM; tmp++) | ||
1023 | isapnp_write_word(ISAPNP_CFG_MEM+(tmp<<3), (res->mem_resource[tmp].start >> 8) & 0xffff); | ||
1024 | /* FIXME: We aren't handling 32bit mems properly here */ | ||
1025 | isapnp_activate(dev->number); | ||
1026 | isapnp_cfg_end(); | ||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | static int isapnp_disable_resources(struct pnp_dev *dev) | ||
1031 | { | ||
1032 | if (!dev || !dev->active) | ||
1033 | return -EINVAL; | ||
1034 | isapnp_cfg_begin(dev->card->number, dev->number); | ||
1035 | isapnp_deactivate(dev->number); | ||
1036 | dev->active = 0; | ||
1037 | isapnp_cfg_end(); | ||
1038 | return 0; | ||
1039 | } | ||
1040 | |||
1041 | struct pnp_protocol isapnp_protocol = { | ||
1042 | .name = "ISA Plug and Play", | ||
1043 | .get = isapnp_get_resources, | ||
1044 | .set = isapnp_set_resources, | ||
1045 | .disable = isapnp_disable_resources, | ||
1046 | }; | ||
1047 | |||
1048 | static int __init isapnp_init(void) | ||
1049 | { | ||
1050 | int cards; | ||
1051 | struct pnp_card *card; | ||
1052 | struct pnp_dev *dev; | ||
1053 | |||
1054 | if (isapnp_disable) { | ||
1055 | isapnp_detected = 0; | ||
1056 | printk(KERN_INFO "isapnp: ISA Plug & Play support disabled\n"); | ||
1057 | return 0; | ||
1058 | } | ||
1059 | #ifdef ISAPNP_REGION_OK | ||
1060 | if (!request_region(_PIDXR, 1, "isapnp index")) { | ||
1061 | printk(KERN_ERR "isapnp: Index Register 0x%x already used\n", _PIDXR); | ||
1062 | return -EBUSY; | ||
1063 | } | ||
1064 | #endif | ||
1065 | if (!request_region(_PNPWRP, 1, "isapnp write")) { | ||
1066 | printk(KERN_ERR "isapnp: Write Data Register 0x%x already used\n", _PNPWRP); | ||
1067 | #ifdef ISAPNP_REGION_OK | ||
1068 | release_region(_PIDXR, 1); | ||
1069 | #endif | ||
1070 | return -EBUSY; | ||
1071 | } | ||
1072 | |||
1073 | if(pnp_register_protocol(&isapnp_protocol)<0) | ||
1074 | return -EBUSY; | ||
1075 | |||
1076 | /* | ||
1077 | * Print a message. The existing ISAPnP code is hanging machines | ||
1078 | * so let the user know where. | ||
1079 | */ | ||
1080 | |||
1081 | printk(KERN_INFO "isapnp: Scanning for PnP cards...\n"); | ||
1082 | if (isapnp_rdp >= 0x203 && isapnp_rdp <= 0x3ff) { | ||
1083 | isapnp_rdp |= 3; | ||
1084 | if (!request_region(isapnp_rdp, 1, "isapnp read")) { | ||
1085 | printk(KERN_ERR "isapnp: Read Data Register 0x%x already used\n", isapnp_rdp); | ||
1086 | #ifdef ISAPNP_REGION_OK | ||
1087 | release_region(_PIDXR, 1); | ||
1088 | #endif | ||
1089 | release_region(_PNPWRP, 1); | ||
1090 | return -EBUSY; | ||
1091 | } | ||
1092 | isapnp_set_rdp(); | ||
1093 | } | ||
1094 | isapnp_detected = 1; | ||
1095 | if (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff) { | ||
1096 | cards = isapnp_isolate(); | ||
1097 | if (cards < 0 || | ||
1098 | (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff)) { | ||
1099 | #ifdef ISAPNP_REGION_OK | ||
1100 | release_region(_PIDXR, 1); | ||
1101 | #endif | ||
1102 | release_region(_PNPWRP, 1); | ||
1103 | isapnp_detected = 0; | ||
1104 | printk(KERN_INFO "isapnp: No Plug & Play device found\n"); | ||
1105 | return 0; | ||
1106 | } | ||
1107 | request_region(isapnp_rdp, 1, "isapnp read"); | ||
1108 | } | ||
1109 | isapnp_build_device_list(); | ||
1110 | cards = 0; | ||
1111 | |||
1112 | protocol_for_each_card(&isapnp_protocol,card) { | ||
1113 | cards++; | ||
1114 | if (isapnp_verbose) { | ||
1115 | printk(KERN_INFO "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown"); | ||
1116 | if (isapnp_verbose < 2) | ||
1117 | continue; | ||
1118 | card_for_each_dev(card,dev) { | ||
1119 | printk(KERN_INFO "isapnp: Device '%s'\n", dev->name[0]?dev->name:"Unknown"); | ||
1120 | } | ||
1121 | } | ||
1122 | } | ||
1123 | if (cards) { | ||
1124 | printk(KERN_INFO "isapnp: %i Plug & Play card%s detected total\n", cards, cards>1?"s":""); | ||
1125 | } else { | ||
1126 | printk(KERN_INFO "isapnp: No Plug & Play card found\n"); | ||
1127 | } | ||
1128 | |||
1129 | isapnp_proc_init(); | ||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | device_initcall(isapnp_init); | ||
1134 | |||
1135 | /* format is: noisapnp */ | ||
1136 | |||
1137 | static int __init isapnp_setup_disable(char *str) | ||
1138 | { | ||
1139 | isapnp_disable = 1; | ||
1140 | return 1; | ||
1141 | } | ||
1142 | |||
1143 | __setup("noisapnp", isapnp_setup_disable); | ||
1144 | |||
1145 | /* format is: isapnp=rdp,reset,skip_pci_scan,verbose */ | ||
1146 | |||
1147 | static int __init isapnp_setup_isapnp(char *str) | ||
1148 | { | ||
1149 | (void)((get_option(&str,&isapnp_rdp) == 2) && | ||
1150 | (get_option(&str,&isapnp_reset) == 2) && | ||
1151 | (get_option(&str,&isapnp_verbose) == 2)); | ||
1152 | return 1; | ||
1153 | } | ||
1154 | |||
1155 | __setup("isapnp=", isapnp_setup_isapnp); | ||
1156 | |||
diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c new file mode 100644 index 000000000000..cf54b0a3628e --- /dev/null +++ b/drivers/pnp/isapnp/proc.c | |||
@@ -0,0 +1,171 @@ | |||
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 | #include <linux/config.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/isapnp.h> | ||
25 | #include <linux/proc_fs.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/smp_lock.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | |||
30 | extern struct pnp_protocol isapnp_protocol; | ||
31 | |||
32 | static struct proc_dir_entry *isapnp_proc_bus_dir = NULL; | ||
33 | |||
34 | static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence) | ||
35 | { | ||
36 | loff_t new = -1; | ||
37 | |||
38 | lock_kernel(); | ||
39 | switch (whence) { | ||
40 | case 0: | ||
41 | new = off; | ||
42 | break; | ||
43 | case 1: | ||
44 | new = file->f_pos + off; | ||
45 | break; | ||
46 | case 2: | ||
47 | new = 256 + off; | ||
48 | break; | ||
49 | } | ||
50 | if (new < 0 || new > 256) { | ||
51 | unlock_kernel(); | ||
52 | return -EINVAL; | ||
53 | } | ||
54 | unlock_kernel(); | ||
55 | return (file->f_pos = new); | ||
56 | } | ||
57 | |||
58 | static ssize_t isapnp_proc_bus_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) | ||
59 | { | ||
60 | struct inode *ino = file->f_dentry->d_inode; | ||
61 | struct proc_dir_entry *dp = PDE(ino); | ||
62 | struct pnp_dev *dev = dp->data; | ||
63 | int pos = *ppos; | ||
64 | int cnt, size = 256; | ||
65 | |||
66 | if (pos >= size) | ||
67 | return 0; | ||
68 | if (nbytes >= size) | ||
69 | nbytes = size; | ||
70 | if (pos + nbytes > size) | ||
71 | nbytes = size - pos; | ||
72 | cnt = nbytes; | ||
73 | |||
74 | if (!access_ok(VERIFY_WRITE, buf, cnt)) | ||
75 | return -EINVAL; | ||
76 | |||
77 | isapnp_cfg_begin(dev->card->number, dev->number); | ||
78 | for ( ; pos < 256 && cnt > 0; pos++, buf++, cnt--) { | ||
79 | unsigned char val; | ||
80 | val = isapnp_read_byte(pos); | ||
81 | __put_user(val, buf); | ||
82 | } | ||
83 | isapnp_cfg_end(); | ||
84 | |||
85 | *ppos = pos; | ||
86 | return nbytes; | ||
87 | } | ||
88 | |||
89 | static struct file_operations isapnp_proc_bus_file_operations = | ||
90 | { | ||
91 | .llseek = isapnp_proc_bus_lseek, | ||
92 | .read = isapnp_proc_bus_read, | ||
93 | }; | ||
94 | |||
95 | static int isapnp_proc_attach_device(struct pnp_dev *dev) | ||
96 | { | ||
97 | struct pnp_card *bus = dev->card; | ||
98 | struct proc_dir_entry *de, *e; | ||
99 | char name[16]; | ||
100 | |||
101 | if (!(de = bus->procdir)) { | ||
102 | sprintf(name, "%02x", bus->number); | ||
103 | de = bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir); | ||
104 | if (!de) | ||
105 | return -ENOMEM; | ||
106 | } | ||
107 | sprintf(name, "%02x", dev->number); | ||
108 | e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO, de); | ||
109 | if (!e) | ||
110 | return -ENOMEM; | ||
111 | e->proc_fops = &isapnp_proc_bus_file_operations; | ||
112 | e->owner = THIS_MODULE; | ||
113 | e->data = dev; | ||
114 | e->size = 256; | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | #ifdef MODULE | ||
119 | static int __exit isapnp_proc_detach_device(struct pnp_dev *dev) | ||
120 | { | ||
121 | struct pnp_card *bus = dev->card; | ||
122 | struct proc_dir_entry *de; | ||
123 | char name[16]; | ||
124 | |||
125 | if (!(de = bus->procdir)) | ||
126 | return -EINVAL; | ||
127 | sprintf(name, "%02x", dev->number); | ||
128 | remove_proc_entry(name, de); | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static int __exit isapnp_proc_detach_bus(struct pnp_card *bus) | ||
133 | { | ||
134 | struct proc_dir_entry *de; | ||
135 | char name[16]; | ||
136 | |||
137 | if (!(de = bus->procdir)) | ||
138 | return -EINVAL; | ||
139 | sprintf(name, "%02x", bus->number); | ||
140 | remove_proc_entry(name, isapnp_proc_bus_dir); | ||
141 | return 0; | ||
142 | } | ||
143 | #endif /* MODULE */ | ||
144 | |||
145 | int __init isapnp_proc_init(void) | ||
146 | { | ||
147 | struct pnp_dev *dev; | ||
148 | isapnp_proc_bus_dir = proc_mkdir("isapnp", proc_bus); | ||
149 | protocol_for_each_dev(&isapnp_protocol,dev) { | ||
150 | isapnp_proc_attach_device(dev); | ||
151 | } | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | #ifdef MODULE | ||
156 | int __exit isapnp_proc_done(void) | ||
157 | { | ||
158 | struct pnp_dev *dev; | ||
159 | struct pnp_bus *card; | ||
160 | |||
161 | isapnp_for_each_dev(dev) { | ||
162 | isapnp_proc_detach_device(dev); | ||
163 | } | ||
164 | isapnp_for_each_card(card) { | ||
165 | isapnp_proc_detach_bus(card); | ||
166 | } | ||
167 | if (isapnp_proc_bus_dir) | ||
168 | remove_proc_entry("isapnp", proc_bus); | ||
169 | return 0; | ||
170 | } | ||
171 | #endif /* MODULE */ | ||