isapnp.c revision 39444
1/*
2 * Copyright (c) 1998, Michael Smith
3 * Copyright (c) 1996, Sujal M. Patel
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *      $Id: pnp.c,v 1.5 1998/02/09 06:08:38 eivind Exp $
28 */
29
30/*
31 * Machine-independant ISA PnP enumerator implementing a subset of the
32 * ISA PnP specification.
33 */
34#include <stand.h>
35#include <string.h>
36#include <bootstrap.h>
37#include <isapnp.h>
38
39#define inb(x)		(archsw.arch_isainb((x)))
40#define outb(x,y)	(archsw.arch_isaoutb((x),(y)))
41
42static void	isapnp_write(int d, u_char r);
43static u_char	isapnp_read(int d);
44static void	isapnp_send_Initiation_LFSR();
45static int	isapnp_get_serial(u_int8_t *p);
46static int	isapnp_isolation_protocol(struct pnpinfo **pnplist);
47static void	isapnp_enumerate(struct pnpinfo **pnplist);
48
49/* PnP read data port */
50static int	pnp_rd_port;
51
52#define _PNP_ID_LEN	9
53
54struct pnphandler isapnphandler =
55{
56    "isapnp",
57    isapnp_enumerate
58};
59
60static void
61isapnp_write(int d, u_char r)
62{
63    outb (_PNP_ADDRESS, d);
64    outb (_PNP_WRITE_DATA, r);
65}
66
67static u_char
68isapnp_read(int d)
69{
70    outb (_PNP_ADDRESS, d);
71    return (inb(3 | (pnp_rd_port <<2)));
72}
73
74/*
75 * Send Initiation LFSR as described in "Plug and Play ISA Specification",
76 * Intel May 94.
77 */
78static void
79isapnp_send_Initiation_LFSR()
80{
81    int cur, i;
82
83    /* Reset the LSFR */
84    outb(_PNP_ADDRESS, 0);
85    outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
86
87    cur = 0x6a;
88    outb(_PNP_ADDRESS, cur);
89
90    for (i = 1; i < 32; i++) {
91	cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
92	outb(_PNP_ADDRESS, cur);
93    }
94}
95
96/*
97 * Get the device's serial number.  Returns 1 if the serial is valid.
98 */
99static int
100isapnp_get_serial(u_int8_t *data)
101{
102    int		i, bit, valid = 0, sum = 0x6a;
103
104    bzero(data, _PNP_ID_LEN);
105    outb(_PNP_ADDRESS, SERIAL_ISOLATION);
106    for (i = 0; i < 72; i++) {
107	bit = inb((pnp_rd_port << 2) | 0x3) == 0x55;
108	delay(250);	/* Delay 250 usec */
109
110	/* Can't Short Circuit the next evaluation, so 'and' is last */
111	bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit;
112	delay(250);	/* Delay 250 usec */
113
114	valid = valid || bit;
115
116	if (i < 64)
117	    sum = (sum >> 1) |
118		(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
119
120	data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
121    }
122
123    valid = valid && (data[8] == sum);
124
125    return valid;
126}
127
128/*
129 * Format a pnp id as a string in standard ISA PnP format, 0x<hex identifier>
130 */
131static char *
132isapnp_format(u_int8_t *data)
133{
134    static char	idbuf[16];
135    u_int32_t	i;
136
137    i = data[3];
138    i = (i << 8) + data[2];
139    i = (i << 8) + data[1];
140    i = (i << 8) + data[0];
141    sprintf(idbuf, "0x%08x", i);
142    return(idbuf);
143}
144
145/*
146 * Try to read a compatible device ID from the current device, return
147 * 1 if we found one.
148 */
149#define READ_RSC(c)	{while ((isapnp_read(STATUS) & 1) == 0); (c) = isapnp_read(RESOURCE_DATA);}
150static int
151isapnp_getid(u_int8_t *data)
152{
153    int		discard, pos, len;
154    u_int8_t	c, t;
155
156    discard = 0;
157    len = 0;
158    pos = 0;
159
160    for (;;) {
161	READ_RSC(c);
162	/* skipping junk? */
163	if (discard > 0) {
164	    discard--;
165	    continue;
166	}
167	/* copying data? */
168	if (len > 0) {
169	    data[pos++] = c;
170	    /* got all data? */
171	    if (pos >= len)
172		return(1);
173	}
174	/* resource type */
175	if (c & 0x80) {		/* large resource, throw it away */
176	    if (c == 0xff)
177		return(0);	/* end of resources */
178	    READ_RSC(c);
179	    discard = c;
180	    READ_RSC(c);
181	    discard += ((int)c << 8);
182	    continue;
183	}
184	/* small resource */
185	t = (c >> 3) & 0xf;
186	if (t == 0xf)
187	    return(0);		/* end of resources */
188	if ((t == LOG_DEVICE_ID) || (t == COMP_DEVICE_ID)) {
189	    len = c & 7;
190	    pos = 0;
191	    continue;
192	}
193	discard = c & 7;	/* unwanted small resource */
194    }
195
196}
197
198
199/*
200 * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port
201 * value (caller should try multiple READ_DATA locations before giving
202 * up). Upon exiting, all cards are aware that they should use
203 * pnp_rd_port as the READ_DATA port.
204 */
205static int
206isapnp_isolation_protocol(struct pnpinfo **pilist)
207{
208    int			csn;
209    struct pnpinfo	*pi;
210    u_int8_t		cardid[_PNP_ID_LEN];
211    int			ndevs;
212
213    isapnp_send_Initiation_LFSR();
214    ndevs = 0;
215
216    isapnp_write(CONFIG_CONTROL, 0x04);	/* Reset CSN for All Cards */
217
218    for (csn = 1; ; csn++) {
219	/* Wake up cards without a CSN (ie. all of them) */
220	isapnp_write(WAKE, 0);
221	isapnp_write(SET_RD_DATA, pnp_rd_port);
222	outb(_PNP_ADDRESS, SERIAL_ISOLATION);
223	delay(1000);	/* Delay 1 msec */
224
225	if (isapnp_get_serial(cardid)) {
226	    isapnp_write(SET_CSN, csn);
227	    pi = malloc(sizeof(struct pnpinfo));
228	    pi->pi_next = *pilist;
229	    *pilist = pi;
230	    ndevs++;
231	    /* scan the card obtaining all the identifiers it holds */
232	    while (isapnp_getid(cardid)) {
233		printf("   %s\n", isapnp_format(cardid));
234		pnp_addident(pi, isapnp_format(cardid));
235	    }
236	} else
237	    break;
238    }
239    /* Move all cards to wait-for-key state */
240    while (csn >= 0) {
241	isapnp_send_Initiation_LFSR();
242	isapnp_write(WAKE, csn);
243	isapnp_write(CONFIG_CONTROL, 0x02);
244	delay(1000); /* XXX is it really necessary ? */
245    }
246    return(ndevs);
247}
248
249/*
250 * Locate ISA-PnP devices and populate the supplied list.
251 */
252static void
253isapnp_enumerate(struct pnpinfo **pnplist)
254{
255    int			devs;
256
257    for (pnp_rd_port = 0x80; pnp_rd_port < 0xff; pnp_rd_port += 0x10) {
258
259	/* Look for something, quit when we find it */
260	if ((devs = isapnp_isolation_protocol(pnplist)) > 0)
261	    break;
262    }
263    printf("Found %d ISA PnP devices\n", devs);
264}
265
266
267