1 | /*
|
---|
2 | * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
|
---|
3 | *
|
---|
4 | * Permission to use, copy, modify, distribute, and sell this software and its
|
---|
5 | * documentation for any purpose is hereby granted without fee, provided that
|
---|
6 | * the above copyright notice appear in all copies and that both that
|
---|
7 | * copyright notice and this permission notice appear in supporting
|
---|
8 | * documentation, and that the name of Kazutaka YOKOTA not be used in
|
---|
9 | * advertising or publicity pertaining to distribution of the software without
|
---|
10 | * specific, written prior permission. Kazutaka YOKOTA makes no representations
|
---|
11 | * about the suitability of this software for any purpose. It is provided
|
---|
12 | * "as is" without express or implied warranty.
|
---|
13 | *
|
---|
14 | * KAZUTAKA YOKOTA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
---|
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
---|
16 | * EVENT SHALL KAZUTAKA YOKOTA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
---|
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
---|
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
---|
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
---|
20 | * PERFORMANCE OF THIS SOFTWARE.
|
---|
21 | */
|
---|
22 |
|
---|
23 | #define NEED_EVENTS
|
---|
24 | #include "X.h"
|
---|
25 | #include "Xproto.h"
|
---|
26 | #include "inputstr.h"
|
---|
27 | #include "scrnintstr.h"
|
---|
28 |
|
---|
29 | #include "xf86.h"
|
---|
30 | #include "xf86Priv.h"
|
---|
31 | #include "xf86Xinput.h"
|
---|
32 | #include "xf86_OSproc.h"
|
---|
33 | #include "xf86OSmouse.h"
|
---|
34 | #include "xf86_ansic.h"
|
---|
35 | #include "mouse.h"
|
---|
36 | #include "mousePriv.h"
|
---|
37 |
|
---|
38 | /* serial PnP ID string */
|
---|
39 | typedef struct {
|
---|
40 | int revision; /* PnP revision, 100 for 1.00 */
|
---|
41 | char *eisaid; /* EISA ID including mfr ID and product ID */
|
---|
42 | char *serial; /* serial No, optional */
|
---|
43 | char *class; /* device class, optional */
|
---|
44 | char *compat; /* list of compatible drivers, optional */
|
---|
45 | char *description; /* product description, optional */
|
---|
46 | int neisaid; /* length of the above fields... */
|
---|
47 | int nserial;
|
---|
48 | int nclass;
|
---|
49 | int ncompat;
|
---|
50 | int ndescription;
|
---|
51 | } pnpid_t;
|
---|
52 |
|
---|
53 | /* symbol table entry */
|
---|
54 | typedef struct {
|
---|
55 | char *name;
|
---|
56 | int val;
|
---|
57 | } symtab_t;
|
---|
58 |
|
---|
59 | /* PnP EISA/product IDs */
|
---|
60 | static symtab_t pnpprod[] = {
|
---|
61 | { "KML0001", PROT_THINKING }, /* Kensignton ThinkingMouse */
|
---|
62 | { "MSH0001", PROT_IMSERIAL }, /* MS IntelliMouse */
|
---|
63 | { "MSH0004", PROT_IMSERIAL }, /* MS IntelliMouse TrackBall */
|
---|
64 | { "KYEEZ00", PROT_MS }, /* Genius EZScroll */
|
---|
65 | { "KYE0001", PROT_MS }, /* Genius PnP Mouse */
|
---|
66 | { "KYE0002", PROT_MS }, /* MouseSystem (Genius?) SmartScroll */
|
---|
67 | { "KYE0003", PROT_IMSERIAL }, /* Genius NetMouse */
|
---|
68 | { "LGI800C", PROT_IMSERIAL }, /* Logitech MouseMan (4 button model) */
|
---|
69 | { "LGI8033", PROT_IMSERIAL }, /* Logitech Cordless MouseMan Wheel */
|
---|
70 | { "LGI8050", PROT_IMSERIAL }, /* Logitech MouseMan+ */
|
---|
71 | { "LGI8051", PROT_IMSERIAL }, /* Logitech FirstMouse+ */
|
---|
72 | { "LGI8001", PROT_LOGIMAN }, /* Logitech serial */
|
---|
73 | { "A4W0005", PROT_IMSERIAL }, /* A4 Tech 4D/4D+ Mouse */
|
---|
74 | { "PEC9802", PROT_IMSERIAL }, /* 8D Scroll Mouse */
|
---|
75 |
|
---|
76 | { "PNP0F00", PROT_BM }, /* MS bus */
|
---|
77 | { "PNP0F01", PROT_MS }, /* MS serial */
|
---|
78 | { "PNP0F02", PROT_BM }, /* MS InPort */
|
---|
79 | { "PNP0F03", PROT_PS2 }, /* MS PS/2 */
|
---|
80 | /*
|
---|
81 | * EzScroll returns PNP0F04 in the compatible device field; but it
|
---|
82 | * doesn't look compatible... XXX
|
---|
83 | */
|
---|
84 | { "PNP0F04", PROT_MSC }, /* MouseSystems */
|
---|
85 | { "PNP0F05", PROT_MSC }, /* MouseSystems */
|
---|
86 | #ifdef notyet
|
---|
87 | { "PNP0F06", PROT_??? }, /* Genius Mouse */
|
---|
88 | { "PNP0F07", PROT_??? }, /* Genius Mouse */
|
---|
89 | #endif
|
---|
90 | { "PNP0F08", PROT_LOGIMAN }, /* Logitech serial */
|
---|
91 | { "PNP0F09", PROT_MS }, /* MS BallPoint serial */
|
---|
92 | { "PNP0F0A", PROT_MS }, /* MS PnP serial */
|
---|
93 | { "PNP0F0B", PROT_MS }, /* MS PnP BallPoint serial */
|
---|
94 | { "PNP0F0C", PROT_MS }, /* MS serial comatible */
|
---|
95 | { "PNP0F0D", PROT_BM }, /* MS InPort comatible */
|
---|
96 | { "PNP0F0E", PROT_PS2 }, /* MS PS/2 comatible */
|
---|
97 | { "PNP0F0F", PROT_MS }, /* MS BallPoint comatible */
|
---|
98 | #ifdef notyet
|
---|
99 | { "PNP0F10", PROT_??? }, /* TI QuickPort */
|
---|
100 | #endif
|
---|
101 | { "PNP0F11", PROT_BM }, /* MS bus comatible */
|
---|
102 | { "PNP0F12", PROT_PS2 }, /* Logitech PS/2 */
|
---|
103 | { "PNP0F13", PROT_PS2 }, /* PS/2 */
|
---|
104 | #ifdef notyet
|
---|
105 | { "PNP0F14", PROT_??? }, /* MS Kids Mouse */
|
---|
106 | #endif
|
---|
107 | { "PNP0F15", PROT_BM }, /* Logitech bus */
|
---|
108 | #ifdef notyet
|
---|
109 | { "PNP0F16", PROT_??? }, /* Logitech SWIFT */
|
---|
110 | #endif
|
---|
111 | { "PNP0F17", PROT_LOGIMAN }, /* Logitech serial compat */
|
---|
112 | { "PNP0F18", PROT_BM }, /* Logitech bus compatible */
|
---|
113 | { "PNP0F19", PROT_PS2 }, /* Logitech PS/2 compatible */
|
---|
114 | #ifdef notyet
|
---|
115 | { "PNP0F1A", PROT_??? }, /* Logitech SWIFT compatible */
|
---|
116 | { "PNP0F1B", PROT_??? }, /* HP Omnibook */
|
---|
117 | { "PNP0F1C", PROT_??? }, /* Compaq LTE TrackBall PS/2 */
|
---|
118 | { "PNP0F1D", PROT_??? }, /* Compaq LTE TrackBall serial */
|
---|
119 | { "PNP0F1E", PROT_??? }, /* MS Kids Trackball */
|
---|
120 | #endif
|
---|
121 | { NULL, -1 },
|
---|
122 | };
|
---|
123 |
|
---|
124 | static const char *pnpSerial[] = {
|
---|
125 | "BaudRate", "1200",
|
---|
126 | "DataBits", "7",
|
---|
127 | "StopBits", "1",
|
---|
128 | "Parity", "None",
|
---|
129 | "FlowControl", "None",
|
---|
130 | "VTime", "0",
|
---|
131 | "VMin", "1",
|
---|
132 | NULL
|
---|
133 | };
|
---|
134 |
|
---|
135 | static int pnpgets(InputInfoPtr, char *);
|
---|
136 | static int pnpparse(InputInfoPtr, pnpid_t *, char *, int);
|
---|
137 | static symtab_t *pnpproto(pnpid_t *);
|
---|
138 | static symtab_t *gettoken(symtab_t *, char *, int);
|
---|
139 |
|
---|
140 | int
|
---|
141 | MouseGetPnpProtocol(InputInfoPtr pInfo)
|
---|
142 | {
|
---|
143 | char buf[256]; /* PnP ID string may be up to 256 bytes long */
|
---|
144 | pnpid_t pnpid;
|
---|
145 | symtab_t *t;
|
---|
146 | int len;
|
---|
147 |
|
---|
148 | if (((len = pnpgets(pInfo, buf)) <= 0) ||
|
---|
149 | !pnpparse(pInfo, &pnpid, buf, len))
|
---|
150 | return PROT_UNKNOWN;
|
---|
151 | if ((t = pnpproto(&pnpid)) == NULL)
|
---|
152 | return PROT_UNKNOWN;
|
---|
153 | xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n",
|
---|
154 | pInfo->name, t->val);
|
---|
155 | return (t->val);
|
---|
156 | }
|
---|
157 |
|
---|
158 | /*
|
---|
159 | * Try to elicit a PnP ID as described in
|
---|
160 | * Microsoft, Hayes: "Plug and Play External COM Device Specification,
|
---|
161 | * rev 1.00", 1995.
|
---|
162 | *
|
---|
163 | * The routine does not fully implement the COM Enumerator as per Section
|
---|
164 | * 2.1 of the document. In particular, we don't have idle state in which
|
---|
165 | * the driver software monitors the com port for dynamic connection or
|
---|
166 | * removal of a device at the port, because `moused' simply quits if no
|
---|
167 | * device is found.
|
---|
168 | *
|
---|
169 | * In addition, as PnP COM device enumeration procedure slightly has
|
---|
170 | * changed since its first publication, devices which follow earlier
|
---|
171 | * revisions of the above spec. may fail to respond if the rev 1.0
|
---|
172 | * procedure is used. XXX
|
---|
173 | */
|
---|
174 | static int
|
---|
175 | pnpgets(InputInfoPtr pInfo, char *buf)
|
---|
176 | {
|
---|
177 | int i;
|
---|
178 | char c;
|
---|
179 | pointer pnpOpts;
|
---|
180 |
|
---|
181 | #if 0
|
---|
182 | /*
|
---|
183 | * This is the procedure described in rev 1.0 of PnP COM device spec.
|
---|
184 | * Unfortunately, some devices which comform to earlier revisions of
|
---|
185 | * the spec gets confused and do not return the ID string...
|
---|
186 | */
|
---|
187 |
|
---|
188 | /* port initialization (2.1.2) */
|
---|
189 | if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
|
---|
190 | return 0;
|
---|
191 | i |= XF86_M_DTR; /* DTR = 1 */
|
---|
192 | i &= ~XF86_M_RTS; /* RTS = 0 */
|
---|
193 | if (xf86SetSerialModemState(pInfo->fd, i) == -1)
|
---|
194 | goto disconnect_idle;
|
---|
195 | usleep(200000);
|
---|
196 | if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 ||
|
---|
197 | (i & XF86_M_DSR) == 0)
|
---|
198 | goto disconnect_idle;
|
---|
199 |
|
---|
200 | /* port setup, 1st phase (2.1.3) */
|
---|
201 | pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
|
---|
202 | xf86SetSerial(pInfo->fd, pnpOpts);
|
---|
203 | i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
|
---|
204 | xf86SerialModemClearBits(pInfo->fd, i);
|
---|
205 | usleep(200000);
|
---|
206 | i = TIOCM_DTR; /* DTR = 1, RTS = 0 */
|
---|
207 | xf86SerialModemSetBits(pInfo->fd, i);
|
---|
208 | usleep(200000);
|
---|
209 |
|
---|
210 | /* wait for response, 1st phase (2.1.4) */
|
---|
211 | xf86FlushInput(pInfo->fd);
|
---|
212 | i = TIOCM_RTS; /* DTR = 1, RTS = 1 */
|
---|
213 | xf86SerialModemSetBits(pInfo->fd, i);
|
---|
214 |
|
---|
215 | /* try to read something */
|
---|
216 | if (xf86WaitForInput(pInfo->fd, 200000) <= 0) {
|
---|
217 |
|
---|
218 | /* port setup, 2nd phase (2.1.5) */
|
---|
219 | i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
|
---|
220 | xf86SerialModemClearBits(pInfo->fd, i);
|
---|
221 | usleep(200000);
|
---|
222 |
|
---|
223 | /* wait for respose, 2nd phase (2.1.6) */
|
---|
224 | xf86FlushInput(pInfo->fd);
|
---|
225 | i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
|
---|
226 | xf86SerialModemSetBits(pInfo->fd, i);
|
---|
227 |
|
---|
228 | /* try to read something */
|
---|
229 | if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
|
---|
230 | goto connect_idle;
|
---|
231 | }
|
---|
232 | #else
|
---|
233 | /*
|
---|
234 | * This is a simplified procedure; it simply toggles RTS.
|
---|
235 | */
|
---|
236 |
|
---|
237 | if ((i = xf86GetSerialModemState(pInfo->fd)) == -1)
|
---|
238 | return 0;
|
---|
239 | i |= XF86_M_DTR; /* DTR = 1 */
|
---|
240 | i &= ~XF86_M_RTS; /* RTS = 0 */
|
---|
241 | if (xf86SetSerialModemState(pInfo->fd, i) == -1)
|
---|
242 | goto disconnect_idle;
|
---|
243 | usleep(200000);
|
---|
244 |
|
---|
245 | pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1);
|
---|
246 | xf86SetSerial(pInfo->fd, pnpOpts);
|
---|
247 |
|
---|
248 | /* wait for respose */
|
---|
249 | xf86FlushInput(pInfo->fd);
|
---|
250 | i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */
|
---|
251 | xf86SerialModemSetBits(pInfo->fd, i);
|
---|
252 |
|
---|
253 | /* try to read something */
|
---|
254 | if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
|
---|
255 | goto connect_idle;
|
---|
256 | #endif
|
---|
257 |
|
---|
258 | /* collect PnP COM device ID (2.1.7) */
|
---|
259 | i = 0;
|
---|
260 | usleep(200000); /* the mouse must send `Begin ID' within 200msec */
|
---|
261 | while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) {
|
---|
262 | /* we may see "M", or "M3..." before `Begin ID' */
|
---|
263 | if ((c == 0x08) || (c == 0x28)) { /* Begin ID */
|
---|
264 | buf[i++] = c;
|
---|
265 | break;
|
---|
266 | }
|
---|
267 | if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
|
---|
268 | break;
|
---|
269 | }
|
---|
270 | if (i <= 0) {
|
---|
271 | /* we haven't seen `Begin ID' in time... */
|
---|
272 | goto connect_idle;
|
---|
273 | }
|
---|
274 |
|
---|
275 | ++c; /* make it `End ID' */
|
---|
276 | for (;;) {
|
---|
277 | if (xf86WaitForInput(pInfo->fd, 200000) <= 0)
|
---|
278 | break;
|
---|
279 |
|
---|
280 | xf86ReadSerial(pInfo->fd, &buf[i], 1);
|
---|
281 | if (buf[i++] == c) /* End ID */
|
---|
282 | break;
|
---|
283 | if (i >= 256)
|
---|
284 | break;
|
---|
285 | }
|
---|
286 | if (buf[i - 1] != c)
|
---|
287 | goto connect_idle;
|
---|
288 | return i;
|
---|
289 |
|
---|
290 | /*
|
---|
291 | * According to PnP spec, we should set DTR = 1 and RTS = 0 while
|
---|
292 | * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed,
|
---|
293 | * assuming there is something at the port even if it didn't
|
---|
294 | * respond to the PnP enumeration procedure.
|
---|
295 | */
|
---|
296 | disconnect_idle:
|
---|
297 | i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */
|
---|
298 | xf86SerialModemSetBits(pInfo->fd, i);
|
---|
299 | connect_idle:
|
---|
300 | return 0;
|
---|
301 | }
|
---|
302 |
|
---|
303 | static int
|
---|
304 | pnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len)
|
---|
305 | {
|
---|
306 | char s[3];
|
---|
307 | int offset;
|
---|
308 | int sum = 0;
|
---|
309 | int i, j;
|
---|
310 |
|
---|
311 | id->revision = 0;
|
---|
312 | id->eisaid = NULL;
|
---|
313 | id->serial = NULL;
|
---|
314 | id->class = NULL;
|
---|
315 | id->compat = NULL;
|
---|
316 | id->description = NULL;
|
---|
317 | id->neisaid = 0;
|
---|
318 | id->nserial = 0;
|
---|
319 | id->nclass = 0;
|
---|
320 | id->ncompat = 0;
|
---|
321 | id->ndescription = 0;
|
---|
322 |
|
---|
323 | offset = 0x28 - buf[0];
|
---|
324 |
|
---|
325 | /* calculate checksum */
|
---|
326 | for (i = 0; i < len - 3; ++i) {
|
---|
327 | sum += buf[i];
|
---|
328 | buf[i] += offset;
|
---|
329 | }
|
---|
330 | sum += buf[len - 1];
|
---|
331 | for (; i < len; ++i)
|
---|
332 | buf[i] += offset;
|
---|
333 | xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name,
|
---|
334 | len, len, buf);
|
---|
335 |
|
---|
336 | /* revision */
|
---|
337 | buf[1] -= offset;
|
---|
338 | buf[2] -= offset;
|
---|
339 | id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
|
---|
340 | xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name,
|
---|
341 | id->revision / 100, id->revision % 100);
|
---|
342 |
|
---|
343 | /* EISA vender and product ID */
|
---|
344 | id->eisaid = &buf[3];
|
---|
345 | id->neisaid = 7;
|
---|
346 |
|
---|
347 | /* option strings */
|
---|
348 | i = 10;
|
---|
349 | if (buf[i] == '\\') {
|
---|
350 | /* device serial # */
|
---|
351 | for (j = ++i; i < len; ++i) {
|
---|
352 | if (buf[i] == '\\')
|
---|
353 | break;
|
---|
354 | }
|
---|
355 | if (i >= len)
|
---|
356 | i -= 3;
|
---|
357 | if (i - j == 8) {
|
---|
358 | id->serial = &buf[j];
|
---|
359 | id->nserial = 8;
|
---|
360 | }
|
---|
361 | }
|
---|
362 | if (buf[i] == '\\') {
|
---|
363 | /* PnP class */
|
---|
364 | for (j = ++i; i < len; ++i) {
|
---|
365 | if (buf[i] == '\\')
|
---|
366 | break;
|
---|
367 | }
|
---|
368 | if (i >= len)
|
---|
369 | i -= 3;
|
---|
370 | if (i > j + 1) {
|
---|
371 | id->class = &buf[j];
|
---|
372 | id->nclass = i - j;
|
---|
373 | }
|
---|
374 | }
|
---|
375 | if (buf[i] == '\\') {
|
---|
376 | /* compatible driver */
|
---|
377 | for (j = ++i; i < len; ++i) {
|
---|
378 | if (buf[i] == '\\')
|
---|
379 | break;
|
---|
380 | }
|
---|
381 | /*
|
---|
382 | * PnP COM spec prior to v0.96 allowed '*' in this field,
|
---|
383 | * it's not allowed now; just ignore it.
|
---|
384 | */
|
---|
385 | if (buf[j] == '*')
|
---|
386 | ++j;
|
---|
387 | if (i >= len)
|
---|
388 | i -= 3;
|
---|
389 | if (i > j + 1) {
|
---|
390 | id->compat = &buf[j];
|
---|
391 | id->ncompat = i - j;
|
---|
392 | }
|
---|
393 | }
|
---|
394 | if (buf[i] == '\\') {
|
---|
395 | /* product description */
|
---|
396 | for (j = ++i; i < len; ++i) {
|
---|
397 | if (buf[i] == ';')
|
---|
398 | break;
|
---|
399 | }
|
---|
400 | if (i >= len)
|
---|
401 | i -= 3;
|
---|
402 | if (i > j + 1) {
|
---|
403 | id->description = &buf[j];
|
---|
404 | id->ndescription = i - j;
|
---|
405 | }
|
---|
406 | }
|
---|
407 |
|
---|
408 | /* checksum exists if there are any optional fields */
|
---|
409 | if ((id->nserial > 0) || (id->nclass > 0)
|
---|
410 | || (id->ncompat > 0) || (id->ndescription > 0)) {
|
---|
411 | xf86MsgVerb(X_INFO, 4, "PnP checksum: 0x%02X\n", pInfo->name, sum);
|
---|
412 | sprintf(s, "%02X", sum & 0x0ff);
|
---|
413 | if (strncmp(s, &buf[len - 3], 2) != 0) {
|
---|
414 | #if 0
|
---|
415 | /*
|
---|
416 | * Checksum error!!
|
---|
417 | * I found some mice do not comply with the PnP COM device
|
---|
418 | * spec regarding checksum... XXX
|
---|
419 | */
|
---|
420 | return FALSE;
|
---|
421 | #endif
|
---|
422 | }
|
---|
423 | }
|
---|
424 |
|
---|
425 | return TRUE;
|
---|
426 | }
|
---|
427 |
|
---|
428 | static symtab_t *
|
---|
429 | pnpproto(pnpid_t *id)
|
---|
430 | {
|
---|
431 | symtab_t *t;
|
---|
432 | int i, j;
|
---|
433 |
|
---|
434 | if (id->nclass > 0)
|
---|
435 | if (strncmp(id->class, "MOUSE", id->nclass) != 0)
|
---|
436 | /* this is not a mouse! */
|
---|
437 | return NULL;
|
---|
438 |
|
---|
439 | if (id->neisaid > 0) {
|
---|
440 | t = gettoken(pnpprod, id->eisaid, id->neisaid);
|
---|
441 | if (t->val != -1)
|
---|
442 | return t;
|
---|
443 | }
|
---|
444 |
|
---|
445 | /*
|
---|
446 | * The 'Compatible drivers' field may contain more than one
|
---|
447 | * ID separated by ','.
|
---|
448 | */
|
---|
449 | if (id->ncompat <= 0)
|
---|
450 | return NULL;
|
---|
451 | for (i = 0; i < id->ncompat; ++i) {
|
---|
452 | for (j = i; id->compat[i] != ','; ++i)
|
---|
453 | if (i >= id->ncompat)
|
---|
454 | break;
|
---|
455 | if (i > j) {
|
---|
456 | t = gettoken(pnpprod, id->compat + j, i - j);
|
---|
457 | if (t->val != -1)
|
---|
458 | return t;
|
---|
459 | }
|
---|
460 | }
|
---|
461 |
|
---|
462 | return NULL;
|
---|
463 | }
|
---|
464 |
|
---|
465 | /* name/val mapping */
|
---|
466 |
|
---|
467 | static symtab_t *
|
---|
468 | gettoken(tab, s, len)
|
---|
469 | symtab_t *tab;
|
---|
470 | char *s;
|
---|
471 | int len;
|
---|
472 | {
|
---|
473 | int i;
|
---|
474 |
|
---|
475 | for (i = 0; tab[i].name != NULL; ++i) {
|
---|
476 | if (strncmp(tab[i].name, s, len) == 0)
|
---|
477 | break;
|
---|
478 | }
|
---|
479 | return &tab[i];
|
---|
480 | }
|
---|