1/*
2 * Copyright (c) 1996, Sujal M. Patel
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/time.h>
28
29#include <err.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <fcntl.h>
34#include <string.h>
35
36#include <machine/cpufunc.h>
37
38#include <isa/pnpreg.h>
39
40#ifdef DEBUG
41#define	DEB(x) x
42#else
43#define DEB(x)
44#endif
45#define DDB(x) x
46
47void
48pnp_write(int d, u_char r)
49{
50    outb(_PNP_ADDRESS, d);
51    outb(_PNP_WRITE_DATA, r);
52}
53
54/* The READ_DATA port that we are using currently */
55static int rd_port;
56
57u_char
58pnp_read(int d)
59{
60    outb(_PNP_ADDRESS, d);
61    return inb( (rd_port << 2) + 3) & 0xff;
62}
63
64u_short
65pnp_readw(int d)
66{
67    int c = pnp_read(d) << 8 ;
68    c |= pnp_read(d+1);
69    return c;
70}
71
72int logdevs=0;
73
74void DELAY(int i);
75void send_Initiation_LFSR(void);
76int get_serial(u_char *data);
77int get_resource_info(u_char *buffer, int len);
78int handle_small_res(u_char *resinfo, int item, int len);
79void handle_large_res(u_char *resinfo, int item, int len);
80void dump_resdata(u_char *data, int csn);
81int isolation_protocol(void);
82
83
84/*
85 * DELAY does accurate delaying in user-space.
86 * This function busy-waits.
87 */
88void
89DELAY(int i)
90{
91    struct timeval t;
92    long start, stop;
93
94    i *= 4;
95
96    gettimeofday (&t, NULL);
97    start = t.tv_sec * 1000000 + t.tv_usec;
98    do {
99	gettimeofday (&t, NULL);
100	stop = t.tv_sec * 1000000 + t.tv_usec;
101    } while (start + i > stop);
102}
103
104
105/*
106 * Send Initiation LFSR as described in "Plug and Play ISA Specification,
107 * Intel May 94."
108 */
109void
110send_Initiation_LFSR(void)
111{
112    int cur, i;
113
114    pnp_write(PNP_CONFIG_CONTROL, 0x2);
115
116    /* Reset the LSFR */
117    outb(_PNP_ADDRESS, 0);
118    outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
119
120    cur = 0x6a;
121
122    for (i = 0; i < 32; i++) {
123	outb(_PNP_ADDRESS, cur);
124	cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
125    }
126}
127
128/*
129 * Get the device's serial number.  Returns 1 if the serial is valid.
130 */
131int
132get_serial(u_char *data)
133{
134    int i, bit, valid = 0, sum = 0x6a;
135
136    bzero(data, sizeof(char) * 9);
137
138    for (i = 0; i < 72; i++) {
139	bit = inb((rd_port << 2) | 0x3) == 0x55;
140	DELAY(250);	/* Delay 250 usec */
141
142	/* Can't Short Circuit the next evaluation, so 'and' is last */
143	bit = (inb((rd_port << 2) | 0x3) == 0xaa) && bit;
144	DELAY(250);	/* Delay 250 usec */
145
146	valid = valid || bit;
147
148	if (i < 64)
149	    sum = (sum >> 1) |
150		(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
151
152	data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
153    }
154
155    valid = valid && (data[8] == sum);
156
157    return valid;
158}
159
160
161/*
162 * Fill's the buffer with resource info from the device.
163 * Returns 0 if the device fails to report
164 */
165int
166get_resource_info(u_char *buffer, int len)
167{
168    int i, j;
169
170    for (i = 0; i < len; i++) {
171	outb(_PNP_ADDRESS, PNP_STATUS);
172	for (j = 0; j < 100; j++) {
173	    if ((inb((rd_port << 2) | 0x3)) & 0x1)
174		break;
175	    DELAY(1);
176	}
177	if (j == 100) {
178	    printf("PnP device failed to report resource data\n");
179	    return 0;
180	}
181	outb(_PNP_ADDRESS, PNP_RESOURCE_DATA);
182	buffer[i] = inb((rd_port << 2) | 0x3);
183	DEB(printf("--- get_resource_info: got 0x%02x\n",(unsigned)buffer[i]));
184    }
185    return 1;
186}
187
188void
189report_dma_info(int x)
190{
191    char *s1=NULL, *s2=NULL, *s3=NULL, *s4=NULL, *s5=NULL;
192
193    switch (x & 0x3) {
194    case 0:
195	s1="8-bit";
196	break;
197    case 1:
198	s1="8/16-bit";
199	break;
200    case 2:
201	s1="16-bit";
202	break;
203#ifdef DIAGNOSTIC
204    case 3:
205	s1="Reserved";
206	break;
207#endif
208    }
209
210    s2 = (x & 0x4) ? "bus master" : "not a bus master";
211
212    s3 = (x & 0x8) ? "count by byte" : "";
213
214    s4 = (x & 0x10) ? "count by word" : "";
215
216    switch ((x & 0x60) >> 5) {
217    case 0:
218	s5="Compatibility mode";
219	break;
220    case 1:
221	s5="Type A";
222	break;
223    case 2:
224	s5="Type B";
225	break;
226    case 3:
227	s5="Type F";
228	break;
229    }
230    printf("\t%s, %s, %s, %s, %s\n",s1,s2,s3,s4,s5);
231}
232
233
234void
235report_memory_info(int x)
236{
237    if (x & 0x1)
238	printf ("Memory Range: Writeable\n");
239    else
240	printf ("Memory Range: Not writeable (ROM)\n");
241
242    if (x & 0x2)
243	printf ("Memory Range: Read-cacheable, write-through\n");
244    else
245	printf ("Memory Range: Non-cacheable\n");
246
247    if (x & 0x4)
248	printf ("Memory Range: Decode supports high address\n");
249    else
250	printf ("Memory Range: Decode supports range length\n");
251
252    switch ((x & 0x18) >> 3) {
253    case 0:
254	printf ("Memory Range: 8-bit memory only\n");
255	break;
256    case 1:
257	printf ("Memory Range: 16-bit memory only\n");
258	break;
259    case 2:
260	printf ("Memory Range: 8-bit and 16-bit memory supported\n");
261	break;
262#ifdef DIAGNOSTIC
263    case 3:
264	printf ("Memory Range: Reserved\n");
265	break;
266#endif
267    }
268
269    if (x & 0x20)
270	printf ("Memory Range: Memory is shadowable\n");
271    else
272	printf ("Memory Range: Memory is not shadowable\n");
273
274    if (x & 0x40)
275	printf ("Memory Range: Memory is an expansion ROM\n");
276    else
277	printf ("Memory Range: Memory is not an expansion ROM\n");
278
279#ifdef DIAGNOSTIC
280    if (x & 0x80)
281	printf ("Memory Range: Reserved (Device is brain-damaged)\n");
282#endif
283}
284
285
286/*
287 *  Small Resource Tag Handler
288 *
289 *  Returns 1 if checksum was valid (and an END_TAG was received).
290 *  Returns -1 if checksum was invalid (and an END_TAG was received).
291 *  Returns 0 for other tags.
292 */
293int
294handle_small_res(u_char *resinfo, int item, int len)
295{
296    int i;
297
298    DEB(printf("*** ITEM 0x%04x len %d detected\n", item, len));
299
300    switch (item) {
301    default:
302	printf("*** ITEM 0x%02x detected\n", item);
303	break;
304    case PNP_TAG_VERSION:
305	printf("PnP Version %d.%d, Vendor Version %d\n",
306	    resinfo[0] >> 4, resinfo[0] & (0xf), resinfo[1]);
307	break;
308    case PNP_TAG_LOGICAL_DEVICE:
309	printf("\nLogical Device ID: %c%c%c%02x%02x 0x%08x #%d\n",
310		((resinfo[0] & 0x7c) >> 2) + 64,
311		(((resinfo[0] & 0x03) << 3) |
312		((resinfo[1] & 0xe0) >> 5)) + 64,
313		(resinfo[1] & 0x1f) + 64,
314		resinfo[2], resinfo[3], *(int *)(resinfo),
315		logdevs++);
316
317	if (resinfo[4] & 0x1)
318	    printf ("\tDevice powers up active\n"); /* XXX */
319	if (resinfo[4] & 0x2)
320	    printf ("\tDevice supports I/O Range Check\n");
321	if (resinfo[4] > 0x3)
322	    printf ("\tReserved register funcs %02x\n",
323		resinfo[4]);
324
325	if (len == 6)
326	    printf("\tVendor register funcs %02x\n", resinfo[5]);
327	break;
328    case PNP_TAG_COMPAT_DEVICE:
329	printf("Compatible Device ID: %c%c%c%02x%02x (%08x)\n",
330		((resinfo[0] & 0x7c) >> 2) + 64,
331		(((resinfo[0] & 0x03) << 3) |
332		((resinfo[1] & 0xe0) >> 5)) + 64,
333		(resinfo[1] & 0x1f) + 64,
334		resinfo[2], resinfo[3], *(int *)resinfo);
335	break;
336    case PNP_TAG_IRQ_FORMAT:
337	printf("    IRQ: ");
338
339	for (i = 0; i < 8; i++)
340	    if (resinfo[0] & (1<<i))
341		printf("%d ", i);
342	for (i = 0; i < 8; i++)
343	    if (resinfo[1] & (1<<i))
344		printf("%d ", i + 8);
345	if (len == 3) {
346	    if (resinfo[2] & 0x1)
347		printf("IRQ: High true edge sensitive\n");
348	    if (resinfo[2] & 0x2)
349		printf("IRQ: Low true edge sensitive\n");
350	    if (resinfo[2] & 0x4)
351		printf("IRQ: High true level sensitive\n");
352	    if (resinfo[2] & 0x8)
353		printf("IRQ: Low true level sensitive\n");
354	} else {
355	    printf(" - only one type (true/edge)\n");
356	}
357	break;
358    case PNP_TAG_DMA_FORMAT:
359	printf("    DMA: channel(s) ");
360	for (i = 0; i < 8; i++)
361	    if (resinfo[0] & (1<<i))
362		printf("%d ", i);
363	printf ("\n");
364	report_dma_info (resinfo[1]);
365	break;
366    case PNP_TAG_START_DEPENDANT:
367	printf("TAG Start DF\n");
368	if (len == 1) {
369	    switch (resinfo[0]) {
370	    case 0:
371		printf("Good Configuration\n");
372		break;
373	    case 1:
374		printf("Acceptable Configuration\n");
375		break;
376	    case 2:
377		printf("Sub-optimal Configuration\n");
378		break;
379	    }
380	}
381	break;
382    case PNP_TAG_END_DEPENDANT:
383	printf("TAG End DF\n");
384	break;
385    case PNP_TAG_IO_RANGE:
386	printf("    I/O Range 0x%x .. 0x%x, alignment 0x%x, len 0x%x\n",
387	    resinfo[1] + (resinfo[2] << 8),
388	    resinfo[3] + (resinfo[4] << 8),
389	    resinfo[5], resinfo[6] );
390	if (resinfo[0])
391	    printf("\t[16-bit addr]\n");
392	else
393	    printf("\t[not 16-bit addr]\n");
394	break;
395    case PNP_TAG_IO_FIXED:
396	printf ("    FIXED I/O base address 0x%x length 0x%x\n",
397	    resinfo[0] + ( (resinfo[1] & 3 ) << 8), /* XXX */
398	    resinfo[2]);
399	break;
400#ifdef DIAGNOSTIC
401    case PNP_TAG_RESERVED:
402	printf("Reserved Tag Detected\n");
403	break;
404#endif
405    case PNP_TAG_VENDOR:
406	printf("*** Small Vendor Tag Detected\n");
407	break;
408    case PNP_TAG_END:
409	printf("End Tag\n\n");
410	/* XXX Record and Verify Checksum */
411	return 1;
412	break;
413    }
414    return 0;
415}
416
417
418void
419handle_large_res(u_char *resinfo, int item, int len)
420{
421    int i;
422
423    DEB(printf("*** Large ITEM %d len %d found\n", item, len));
424    switch (item) {
425    case PNP_TAG_MEMORY_RANGE:
426	report_memory_info(resinfo[0]);
427	printf("Memory range minimum address: 0x%x\n",
428		(resinfo[1] << 8) + (resinfo[2] << 16));
429	printf("Memory range maximum address: 0x%x\n",
430		(resinfo[3] << 8) + (resinfo[4] << 16));
431	printf("Memory range base alignment: 0x%x\n",
432		(i = (resinfo[5] + (resinfo[6] << 8))) ? i : (1 << 16));
433	printf("Memory range length: 0x%x\n",
434		(resinfo[7] + (resinfo[8] << 8)) * 256);
435	break;
436    case PNP_TAG_ID_ANSI:
437	printf("Device Description: ");
438
439	for (i = 0; i < len; i++) {
440	    if (resinfo[i]) /* XXX */
441		printf("%c", resinfo[i]);
442	}
443	printf("\n");
444	break;
445    case PNP_TAG_ID_UNICODE:
446	printf("ID String Unicode Detected (Undefined)\n");
447	break;
448    case PNP_TAG_LARGE_VENDOR:
449	printf("Large Vendor Defined Detected\n");
450	break;
451    case PNP_TAG_MEMORY32_RANGE:
452	printf("32bit Memory Range Desc Unimplemented\n");
453	break;
454    case PNP_TAG_MEMORY32_FIXED:
455	printf("32bit Fixed Location Desc Unimplemented\n");
456	break;
457#ifdef DIAGNOSTIC
458    case PNP_TAG_LARGE_RESERVED:
459	printf("Large Reserved Tag Detected\n");
460	break;
461#endif
462    }
463}
464
465
466/*
467 * Dump all the information about configurations.
468 */
469void
470dump_resdata(u_char *data, int csn)
471{
472    int i, large_len;
473
474    u_char tag, *resinfo;
475
476    DDB(printf("\nCard assigned CSN #%d\n", csn));
477    printf("Vendor ID %c%c%c%02x%02x (0x%08x), Serial Number 0x%08x\n",
478	    ((data[0] & 0x7c) >> 2) + 64,
479	    (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
480	    (data[1] & 0x1f) + 64, data[2], data[3],
481	    *(int *)&(data[0]),
482	    *(int *)&(data[4]));
483
484    pnp_write(PNP_SET_CSN, csn); /* Move this out of this function XXX */
485    outb(_PNP_ADDRESS, PNP_STATUS);
486
487    /* Allows up to 1kb of Resource Info,  Should be plenty */
488    for (i = 0; i < 1024; i++) {
489	if (!get_resource_info(&tag, 1))
490	    break;
491
492	if (PNP_RES_TYPE(tag) == 0) {
493	    /* Handle small resouce data types */
494
495	    resinfo = malloc(PNP_SRES_LEN(tag));
496	    if (!get_resource_info(resinfo, PNP_SRES_LEN(tag)))
497		break;
498
499	    if (handle_small_res(resinfo, PNP_SRES_NUM(tag), PNP_SRES_LEN(tag)) == 1)
500		break;
501	    free(resinfo);
502	} else {
503	    /* Handle large resouce data types */
504	    u_char buf[2];
505	    if (!get_resource_info((char *)buf, 2))
506		break;
507	    large_len = (buf[1] << 8) + buf[0];
508
509	    resinfo = malloc(large_len);
510	    if (!get_resource_info(resinfo, large_len))
511		break;
512
513	    handle_large_res(resinfo, PNP_LRES_NUM(tag), large_len);
514	    free(resinfo);
515	}
516    }
517    printf("Successfully got %d resources, %d logical fdevs\n", i,
518	    logdevs);
519    printf("-- card select # 0x%04x\n", pnp_read(PNP_SET_CSN));
520    printf("\nCSN %c%c%c%02x%02x (0x%08x), Serial Number 0x%08x\n",
521	    ((data[0] & 0x7c) >> 2) + 64,
522	    (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64,
523	    (data[1] & 0x1f) + 64, data[2], data[3],
524	    *(int *)&(data[0]),
525	    *(int *)&(data[4]));
526
527    for (i=0; i<logdevs; i++) {
528	int j;
529
530	pnp_write(PNP_SET_LDN, i);
531
532	printf("\nLogical device #%d\n", pnp_read(PNP_SET_LDN) );
533	printf("IO: ");
534	for (j=0; j<8; j++)
535	    printf(" 0x%02x%02x", pnp_read(PNP_IO_BASE_HIGH(j)),
536		pnp_read(PNP_IO_BASE_LOW(j)));
537	printf("\nIRQ %d %d\n",
538	    pnp_read(PNP_IRQ_LEVEL(0)), pnp_read(PNP_IRQ_LEVEL(1)) );
539	printf("DMA %d %d\n",
540	    pnp_read(PNP_DMA_CHANNEL(0)), pnp_read(PNP_DMA_CHANNEL(1)) );
541	printf("IO range check 0x%02x activate 0x%02x\n",
542	    pnp_read(PNP_IO_RANGE_CHECK), pnp_read(PNP_ACTIVATE) );
543    }
544}
545
546
547/*
548 * Run the isolation protocol. Use rd_port as the READ_DATA port
549 * value (caller should try multiple READ_DATA locations before giving
550 * up). Upon exiting, all cards are aware that they should use rd_port
551 * as the READ_DATA port;
552 *
553 */
554int
555isolation_protocol(void)
556{
557    int csn;
558    u_char data[9];
559
560    send_Initiation_LFSR();
561
562    /* Reset CSN for All Cards */
563    pnp_write(PNP_CONFIG_CONTROL, 0x04);
564
565    for (csn = 1; (csn < PNP_MAX_CARDS); csn++) {
566	/* Wake up cards without a CSN */
567	logdevs = 0 ;
568	pnp_write(PNP_WAKE, 0);
569	pnp_write(PNP_SET_RD_DATA, rd_port);
570	outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
571	DELAY(1000);	/* Delay 1 msec */
572
573	if (get_serial(data))
574	    dump_resdata(data, csn);
575	else
576	    break;
577    }
578    return csn - 1;
579}
580
581
582int
583main(int argc, char **argv)
584{
585    int num_pnp_devs;
586
587#ifdef __i386__
588    /* Hey what about a i386_iopl() call :) */
589    if (open("/dev/io", O_RDONLY) < 0)
590	errx(1, "can't get I/O privilege");
591#endif
592
593    printf("Checking for Plug-n-Play devices...\n");
594
595    /* Try various READ_DATA ports from 0x203-0x3ff */
596    for (rd_port = 0x80; (rd_port < 0xff); rd_port += 0x10) {
597	DEB(printf("Trying Read_Port at %x...\n", (rd_port << 2) | 0x3) );
598	num_pnp_devs = isolation_protocol();
599	if (num_pnp_devs)
600	    break;
601    }
602    if (!num_pnp_devs) {
603	printf("No Plug-n-Play devices were found\n");
604	return (0);
605    }
606    return (0);
607}
608