|
|
1.1 root 1: /**************************************************************************
2: *
3: * isapnp.c -- Etherboot isapnp support for the 3Com 3c515
4: * Written 2002-2003 by Timothy Legge <[email protected]>
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: * Portions of this code:
21: * Copyright (C) 2001 P.J.H.Fox ([email protected])
22: *
23: *
24: * REVISION HISTORY:
25: * ================
26: * Version 0.1 April 26, 2002 TJL
27: * Version 0.2 01/08/2003 TJL Moved outside the 3c515.c driver file
28: * Version 0.3 Sept 23, 2003 timlegge Change delay to currticks
29: *
30: *
31: * Generalised into an ISAPnP bus that can be used by more than just
32: * the 3c515 by Michael Brown <[email protected]>
33: *
34: ***************************************************************************/
35:
36: /** @file
37: *
38: * ISAPnP bus support
39: *
40: * Etherboot orignally gained ISAPnP support in a very limited way for
41: * the 3c515 NIC. The current implementation is almost a complete
42: * rewrite based on the ISAPnP specification, with passing reference
43: * to the Linux ISAPnP code.
44: *
45: * There can be only one ISAPnP bus in a system. Once the read port
46: * is known and all cards have been allocated CSNs, there's nothing to
47: * be gained by re-scanning for cards.
48: *
49: * External code (e.g. the ISAPnP ROM prefix) may already know the
50: * read port address, in which case it can store it in
51: * #isapnp_read_port. Note that setting the read port address in this
52: * way will prevent further isolation from taking place; you should
53: * set the read port address only if you know that devices have
54: * already been allocated CSNs.
55: *
56: */
57:
58: FILE_LICENCE ( GPL2_OR_LATER );
59:
60: #include <stdint.h>
61: #include <stdlib.h>
62: #include <string.h>
63: #include <stdio.h>
64: #include <errno.h>
65: #include <ipxe/io.h>
66: #include <unistd.h>
67: #include <ipxe/isapnp.h>
68:
69: /**
70: * ISAPnP Read Port address.
71: *
72: * ROM prefix may be able to set this address, which is why this is
73: * non-static.
74: */
75: uint16_t isapnp_read_port;
76:
77: static void isapnpbus_remove ( struct root_device *rootdev );
78:
79: /*
80: * ISAPnP utility functions
81: *
82: */
83:
84: #define ISAPNP_CARD_ID_FMT "ID %04x:%04x (\"%s\") serial %x"
85: #define ISAPNP_CARD_ID_DATA(identifier) \
86: (identifier)->vendor_id, (identifier)->prod_id, \
87: isa_id_string ( (identifier)->vendor_id, (identifier)->prod_id ), \
88: (identifier)->serial
89: #define ISAPNP_DEV_ID_FMT "ID %04x:%04x (\"%s\")"
90: #define ISAPNP_DEV_ID_DATA(isapnp) \
91: (isapnp)->vendor_id, (isapnp)->prod_id, \
92: isa_id_string ( (isapnp)->vendor_id, (isapnp)->prod_id )
93:
94: static inline void isapnp_write_address ( unsigned int address ) {
95: outb ( address, ISAPNP_ADDRESS );
96: }
97:
98: static inline void isapnp_write_data ( unsigned int data ) {
99: outb ( data, ISAPNP_WRITE_DATA );
100: }
101:
102: static inline unsigned int isapnp_read_data ( void ) {
103: return inb ( isapnp_read_port );
104: }
105:
106: static inline void isapnp_write_byte ( unsigned int address,
107: unsigned int value ) {
108: isapnp_write_address ( address );
109: isapnp_write_data ( value );
110: }
111:
112: static inline unsigned int isapnp_read_byte ( unsigned int address ) {
113: isapnp_write_address ( address );
114: return isapnp_read_data ();
115: }
116:
117: static inline unsigned int isapnp_read_word ( unsigned int address ) {
118: /* Yes, they're in big-endian order */
119: return ( ( isapnp_read_byte ( address ) << 8 )
120: | isapnp_read_byte ( address + 1 ) );
121: }
122:
123: /** Inform cards of a new read port address */
124: static inline void isapnp_set_read_port ( void ) {
125: isapnp_write_byte ( ISAPNP_READPORT, ( isapnp_read_port >> 2 ) );
126: }
127:
128: /**
129: * Enter the Isolation state.
130: *
131: * Only cards currently in the Sleep state will respond to this
132: * command.
133: */
134: static inline void isapnp_serialisolation ( void ) {
135: isapnp_write_address ( ISAPNP_SERIALISOLATION );
136: }
137:
138: /**
139: * Enter the Wait for Key state.
140: *
141: * All cards will respond to this command, regardless of their current
142: * state.
143: */
144: static inline void isapnp_wait_for_key ( void ) {
145: isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY );
146: }
147:
148: /**
149: * Reset (i.e. remove) Card Select Number.
150: *
151: * Only cards currently in the Sleep state will respond to this
152: * command.
153: */
154: static inline void isapnp_reset_csn ( void ) {
155: isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN );
156: }
157:
158: /**
159: * Place a specified card into the Config state.
160: *
161: * @v csn Card Select Number
162: * @ret None -
163: * @err None -
164: *
165: * Only cards currently in the Sleep, Isolation, or Config states will
166: * respond to this command. The card that has the specified CSN will
167: * enter the Config state, all other cards will enter the Sleep state.
168: */
169: static inline void isapnp_wake ( uint8_t csn ) {
170: isapnp_write_byte ( ISAPNP_WAKE, csn );
171: }
172:
173: static inline unsigned int isapnp_read_resourcedata ( void ) {
174: return isapnp_read_byte ( ISAPNP_RESOURCEDATA );
175: }
176:
177: static inline unsigned int isapnp_read_status ( void ) {
178: return isapnp_read_byte ( ISAPNP_STATUS );
179: }
180:
181: /**
182: * Assign a Card Select Number to a card, and enter the Config state.
183: *
184: * @v csn Card Select Number
185: *
186: * Only cards in the Isolation state will respond to this command.
187: * The isolation protocol is designed so that only one card will
188: * remain in the Isolation state by the time the isolation protocol
189: * completes.
190: */
191: static inline void isapnp_write_csn ( unsigned int csn ) {
192: isapnp_write_byte ( ISAPNP_CARDSELECTNUMBER, csn );
193: }
194:
195: static inline void isapnp_logicaldevice ( unsigned int logdev ) {
196: isapnp_write_byte ( ISAPNP_LOGICALDEVICENUMBER, logdev );
197: }
198:
199: static inline void isapnp_activate ( unsigned int logdev ) {
200: isapnp_logicaldevice ( logdev );
201: isapnp_write_byte ( ISAPNP_ACTIVATE, 1 );
202: }
203:
204: static inline void isapnp_deactivate ( unsigned int logdev ) {
205: isapnp_logicaldevice ( logdev );
206: isapnp_write_byte ( ISAPNP_ACTIVATE, 0 );
207: }
208:
209: static inline unsigned int isapnp_read_iobase ( unsigned int index ) {
210: return isapnp_read_word ( ISAPNP_IOBASE ( index ) );
211: }
212:
213: static inline unsigned int isapnp_read_irqno ( unsigned int index ) {
214: return isapnp_read_byte ( ISAPNP_IRQNO ( index ) );
215: }
216:
217: static void isapnp_delay ( void ) {
218: udelay ( 1000 );
219: }
220:
221: /**
222: * Linear feedback shift register.
223: *
224: * @v lfsr Current value of the LFSR
225: * @v input_bit Current input bit to the LFSR
226: * @ret lfsr Next value of the LFSR
227: *
228: * This routine implements the linear feedback shift register as
229: * described in Appendix B of the PnP ISA spec. The hardware
230: * implementation uses eight D-type latches and two XOR gates. I
231: * think this is probably the smallest possible implementation in
232: * software. Six instructions when input_bit is a constant 0 (for
233: * isapnp_send_key). :)
234: */
235: static inline unsigned int isapnp_lfsr_next ( unsigned int lfsr,
236: unsigned int input_bit ) {
237: register uint8_t lfsr_next;
238:
239: lfsr_next = lfsr >> 1;
240: lfsr_next |= ( ( ( lfsr ^ lfsr_next ) ^ input_bit ) ) << 7;
241: return lfsr_next;
242: }
243:
244: /**
245: * Send the ISAPnP initiation key.
246: *
247: * Sending the key causes all ISAPnP cards that are currently in the
248: * Wait for Key state to transition into the Sleep state.
249: */
250: static void isapnp_send_key ( void ) {
251: unsigned int i;
252: unsigned int lfsr;
253:
254: isapnp_delay();
255: isapnp_write_address ( 0x00 );
256: isapnp_write_address ( 0x00 );
257:
258: lfsr = ISAPNP_LFSR_SEED;
259: for ( i = 0 ; i < 32 ; i++ ) {
260: isapnp_write_address ( lfsr );
261: lfsr = isapnp_lfsr_next ( lfsr, 0 );
262: }
263: }
264:
265: /**
266: * Compute ISAPnP identifier checksum
267: *
268: * @v identifier ISAPnP identifier
269: * @ret checksum Expected checksum value
270: */
271: static unsigned int isapnp_checksum ( struct isapnp_identifier *identifier ) {
272: unsigned int i, j;
273: unsigned int lfsr;
274: unsigned int byte;
275:
276: lfsr = ISAPNP_LFSR_SEED;
277: for ( i = 0 ; i < 8 ; i++ ) {
278: byte = * ( ( ( uint8_t * ) identifier ) + i );
279: for ( j = 0 ; j < 8 ; j++ ) {
280: lfsr = isapnp_lfsr_next ( lfsr, byte );
281: byte >>= 1;
282: }
283: }
284: return lfsr;
285: }
286:
287: /*
288: * Read a byte of resource data from the current location
289: *
290: * @ret byte Byte of resource data
291: */
292: static inline unsigned int isapnp_peek_byte ( void ) {
293: unsigned int i;
294:
295: /* Wait for data to be ready */
296: for ( i = 0 ; i < 20 ; i++ ) {
297: if ( isapnp_read_status() & 0x01 ) {
298: /* Byte ready - read it */
299: return isapnp_read_resourcedata();
300: }
301: isapnp_delay();
302: }
303: /* Data never became ready - return 0xff */
304: return 0xff;
305: }
306:
307: /**
308: * Read resource data.
309: *
310: * @v buf Buffer in which to store data, or NULL
311: * @v bytes Number of bytes to read
312: *
313: * Resource data is read from the current location. If #buf is NULL,
314: * the data is discarded.
315: */
316: static void isapnp_peek ( void *buf, size_t len ) {
317: unsigned int i;
318: unsigned int byte;
319:
320: for ( i = 0 ; i < len ; i++) {
321: byte = isapnp_peek_byte();
322: if ( buf )
323: * ( ( uint8_t * ) buf + i ) = byte;
324: }
325: }
326:
327: /**
328: * Find a tag within the resource data.
329: *
330: * @v wanted_tag The tag that we're looking for
331: * @v buf Buffer in which to store the tag's contents
332: * @v len Length of buffer
333: * @ret rc Return status code
334: *
335: * Scan through the resource data until we find a particular tag, and
336: * read its contents into a buffer.
337: */
338: static int isapnp_find_tag ( unsigned int wanted_tag, void *buf, size_t len ) {
339: unsigned int tag;
340: unsigned int tag_len;
341:
342: DBG2 ( "ISAPnP read tag" );
343: do {
344: tag = isapnp_peek_byte();
345: if ( ISAPNP_IS_SMALL_TAG ( tag ) ) {
346: tag_len = ISAPNP_SMALL_TAG_LEN ( tag );
347: tag = ISAPNP_SMALL_TAG_NAME ( tag );
348: } else {
349: tag_len = ( isapnp_peek_byte() +
350: ( isapnp_peek_byte() << 8 ) );
351: tag = ISAPNP_LARGE_TAG_NAME ( tag );
352: }
353: DBG2 ( " %02x (%02x)", tag, tag_len );
354: if ( tag == wanted_tag ) {
355: if ( len > tag_len )
356: len = tag_len;
357: isapnp_peek ( buf, len );
358: DBG2 ( "\n" );
359: return 0;
360: } else {
361: isapnp_peek ( NULL, tag_len );
362: }
363: } while ( tag != ISAPNP_TAG_END );
364: DBG2 ( "\n" );
365: return -ENOENT;
366: }
367:
368: /**
369: * Find specified Logical Device ID tag
370: *
371: * @v logdev Logical device ID
372: * @v logdevid Logical device ID structure to fill in
373: * @ret rc Return status code
374: */
375: static int isapnp_find_logdevid ( unsigned int logdev,
376: struct isapnp_logdevid *logdevid ) {
377: unsigned int i;
378: int rc;
379:
380: for ( i = 0 ; i <= logdev ; i++ ) {
381: if ( ( rc = isapnp_find_tag ( ISAPNP_TAG_LOGDEVID, logdevid,
382: sizeof ( *logdevid ) ) ) != 0 )
383: return rc;
384: }
385: return 0;
386: }
387:
388: /**
389: * Try isolating ISAPnP cards at the current read port.
390: *
391: * @ret \>0 Number of ISAPnP cards found
392: * @ret 0 There are no ISAPnP cards in the system
393: * @ret \<0 A conflict was detected; try a new read port
394: * @err None -
395: *
396: * The state diagram on page 18 (PDF page 24) of the PnP ISA spec
397: * gives the best overview of what happens here.
398: */
399: static int isapnp_try_isolate ( void ) {
400: struct isapnp_identifier identifier;
401: unsigned int i, j;
402: unsigned int seen_55aa, seen_life;
403: unsigned int csn = 0;
404: unsigned int data;
405: unsigned int byte;
406:
407: DBG ( "ISAPnP attempting isolation at read port %04x\n",
408: isapnp_read_port );
409:
410: /* Place all cards into the Sleep state, whatever state
411: * they're currently in.
412: */
413: isapnp_wait_for_key();
414: isapnp_send_key();
415:
416: /* Reset all assigned CSNs */
417: isapnp_reset_csn();
418: isapnp_delay();
419: isapnp_delay();
420:
421: /* Place all cards into the Isolation state */
422: isapnp_wait_for_key ();
423: isapnp_send_key();
424: isapnp_wake ( 0x00 );
425:
426: /* Set the read port */
427: isapnp_set_read_port();
428: isapnp_delay();
429:
430: while ( 1 ) {
431:
432: /* All cards that do not have assigned CSNs are
433: * currently in the Isolation state, each time we go
434: * through this loop.
435: */
436:
437: /* Initiate serial isolation */
438: isapnp_serialisolation();
439: isapnp_delay();
440:
441: /* Read identifier serially via the ISAPnP read port. */
442: memset ( &identifier, 0, sizeof ( identifier ) );
443: seen_55aa = seen_life = 0;
444: for ( i = 0 ; i < 9 ; i++ ) {
445: byte = 0;
446: for ( j = 0 ; j < 8 ; j++ ) {
447: data = isapnp_read_data();
448: isapnp_delay();
449: data = ( ( data << 8 ) | isapnp_read_data() );
450: isapnp_delay();
451: byte >>= 1;
452: if ( data != 0xffff ) {
453: seen_life++;
454: if ( data == 0x55aa ) {
455: byte |= 0x80;
456: seen_55aa++;
457: }
458: }
459: }
460: *( ( ( uint8_t * ) &identifier ) + i ) = byte;
461: }
462:
463: /* If we didn't see any 55aa patterns, stop here */
464: if ( ! seen_55aa ) {
465: if ( csn ) {
466: DBG ( "ISAPnP found no more cards\n" );
467: } else {
468: if ( seen_life ) {
469: DBG ( "ISAPnP saw life but no cards, "
470: "trying new read port\n" );
471: csn = -1;
472: } else {
473: DBG ( "ISAPnP saw no signs of life, "
474: "abandoning isolation\n" );
475: }
476: }
477: break;
478: }
479:
480: /* If the checksum was invalid stop here */
481: if ( identifier.checksum != isapnp_checksum ( &identifier) ) {
482: DBG ( "ISAPnP found malformed card "
483: ISAPNP_CARD_ID_FMT "\n with checksum %02x "
484: "(should be %02x), trying new read port\n",
485: ISAPNP_CARD_ID_DATA ( &identifier ),
486: identifier.checksum,
487: isapnp_checksum ( &identifier) );
488: csn = -1;
489: break;
490: }
491:
492: /* Give the device a CSN */
493: csn++;
494: DBG ( "ISAPnP found card " ISAPNP_CARD_ID_FMT
495: ", assigning CSN %02x\n",
496: ISAPNP_CARD_ID_DATA ( &identifier ), csn );
497:
498: isapnp_write_csn ( csn );
499: isapnp_delay();
500:
501: /* Send this card back to Sleep and force all cards
502: * without a CSN into Isolation state
503: */
504: isapnp_wake ( 0x00 );
505: isapnp_delay();
506: }
507:
508: /* Place all cards in Wait for Key state */
509: isapnp_wait_for_key();
510:
511: /* Return number of cards found */
512: if ( csn > 0 ) {
513: DBG ( "ISAPnP found %d cards at read port %04x\n",
514: csn, isapnp_read_port );
515: }
516: return csn;
517: }
518:
519: /**
520: * Find a valid read port and isolate all ISAPnP cards.
521: *
522: */
523: static void isapnp_isolate ( void ) {
524: for ( isapnp_read_port = ISAPNP_READ_PORT_START ;
525: isapnp_read_port <= ISAPNP_READ_PORT_MAX ;
526: isapnp_read_port += ISAPNP_READ_PORT_STEP ) {
527: /* Avoid problematic locations such as the NE2000
528: * probe space
529: */
530: if ( ( isapnp_read_port >= 0x280 ) &&
531: ( isapnp_read_port <= 0x380 ) )
532: continue;
533:
534: /* If we detect any ISAPnP cards at this location, stop */
535: if ( isapnp_try_isolate() >= 0 )
536: return;
537: }
538: }
539:
540: /**
541: * Activate or deactivate an ISAPnP device.
542: *
543: * @v isapnp ISAPnP device
544: * @v activation True to enable, False to disable the device
545: * @ret None -
546: * @err None -
547: *
548: * This routine simply activates the device in its current
549: * configuration, or deactivates the device. It does not attempt any
550: * kind of resource arbitration.
551: *
552: */
553: void isapnp_device_activation ( struct isapnp_device *isapnp,
554: int activation ) {
555: /* Wake the card and select the logical device */
556: isapnp_wait_for_key ();
557: isapnp_send_key ();
558: isapnp_wake ( isapnp->csn );
559: isapnp_logicaldevice ( isapnp->logdev );
560:
561: /* Activate/deactivate the logical device */
562: isapnp_activate ( activation );
563: isapnp_delay();
564:
565: /* Return all cards to Wait for Key state */
566: isapnp_wait_for_key ();
567:
568: DBG ( "ISAPnP %s device %02x:%02x\n",
569: ( activation ? "activated" : "deactivated" ),
570: isapnp->csn, isapnp->logdev );
571: }
572:
573: /**
574: * Probe an ISAPnP device
575: *
576: * @v isapnp ISAPnP device
577: * @ret rc Return status code
578: *
579: * Searches for a driver for the ISAPnP device. If a driver is found,
580: * its probe() routine is called.
581: */
582: static int isapnp_probe ( struct isapnp_device *isapnp ) {
583: struct isapnp_driver *driver;
584: struct isapnp_device_id *id;
585: unsigned int i;
586: int rc;
587:
588: DBG ( "Adding ISAPnP device %02x:%02x (%04x:%04x (\"%s\") "
589: "io %x irq %d)\n", isapnp->csn, isapnp->logdev,
590: isapnp->vendor_id, isapnp->prod_id,
591: isa_id_string ( isapnp->vendor_id, isapnp->prod_id ),
592: isapnp->ioaddr, isapnp->irqno );
593:
594: for_each_table_entry ( driver, ISAPNP_DRIVERS ) {
595: for ( i = 0 ; i < driver->id_count ; i++ ) {
596: id = &driver->ids[i];
597: if ( id->vendor_id != isapnp->vendor_id )
598: continue;
599: if ( ISA_PROD_ID ( id->prod_id ) !=
600: ISA_PROD_ID ( isapnp->prod_id ) )
601: continue;
602: isapnp->driver = driver;
603: isapnp->dev.driver_name = id->name;
604: DBG ( "...using driver %s\n", isapnp->dev.driver_name );
605: if ( ( rc = driver->probe ( isapnp, id ) ) != 0 ) {
606: DBG ( "......probe failed\n" );
607: continue;
608: }
609: return 0;
610: }
611: }
612:
613: DBG ( "...no driver found\n" );
614: return -ENOTTY;
615: }
616:
617: /**
618: * Remove an ISAPnP device
619: *
620: * @v isapnp ISAPnP device
621: */
622: static void isapnp_remove ( struct isapnp_device *isapnp ) {
623: isapnp->driver->remove ( isapnp );
624: DBG ( "Removed ISAPnP device %02x:%02x\n",
625: isapnp->csn, isapnp->logdev );
626: }
627:
628: /**
629: * Probe ISAPnP root bus
630: *
631: * @v rootdev ISAPnP bus root device
632: *
633: * Scans the ISAPnP bus for devices and registers all devices it can
634: * find.
635: */
636: static int isapnpbus_probe ( struct root_device *rootdev ) {
637: struct isapnp_device *isapnp = NULL;
638: struct isapnp_identifier identifier;
639: struct isapnp_logdevid logdevid;
640: unsigned int csn;
641: unsigned int logdev;
642: int rc;
643:
644: /* Perform isolation if it hasn't yet been done */
645: if ( ! isapnp_read_port )
646: isapnp_isolate();
647:
648: for ( csn = 1 ; csn <= 0xff ; csn++ ) {
649: for ( logdev = 0 ; logdev <= 0xff ; logdev++ ) {
650:
651: /* Allocate struct isapnp_device */
652: if ( ! isapnp )
653: isapnp = malloc ( sizeof ( *isapnp ) );
654: if ( ! isapnp ) {
655: rc = -ENOMEM;
656: goto err;
657: }
658: memset ( isapnp, 0, sizeof ( *isapnp ) );
659: isapnp->csn = csn;
660: isapnp->logdev = logdev;
661:
662: /* Wake the card */
663: isapnp_wait_for_key();
664: isapnp_send_key();
665: isapnp_wake ( csn );
666:
667: /* Read the card identifier */
668: isapnp_peek ( &identifier, sizeof ( identifier ) );
669:
670: /* No card with this CSN; stop here */
671: if ( identifier.vendor_id & 0x80 )
672: goto done;
673:
674: /* Find the Logical Device ID tag */
675: if ( ( rc = isapnp_find_logdevid ( logdev,
676: &logdevid ) ) != 0){
677: /* No more logical devices; go to next CSN */
678: break;
679: }
680:
681: /* Select the logical device */
682: isapnp_logicaldevice ( logdev );
683:
684: /* Populate struct isapnp_device */
685: isapnp->vendor_id = logdevid.vendor_id;
686: isapnp->prod_id = logdevid.prod_id;
687: isapnp->ioaddr = isapnp_read_iobase ( 0 );
688: isapnp->irqno = isapnp_read_irqno ( 0 );
689:
690: /* Return all cards to Wait for Key state */
691: isapnp_wait_for_key();
692:
693: /* Add to device hierarchy */
694: snprintf ( isapnp->dev.name,
695: sizeof ( isapnp->dev.name ),
696: "ISAPnP%02x:%02x", csn, logdev );
697: isapnp->dev.desc.bus_type = BUS_TYPE_ISAPNP;
698: isapnp->dev.desc.vendor = isapnp->vendor_id;
699: isapnp->dev.desc.device = isapnp->prod_id;
700: isapnp->dev.desc.ioaddr = isapnp->ioaddr;
701: isapnp->dev.desc.irq = isapnp->irqno;
702: isapnp->dev.parent = &rootdev->dev;
703: list_add ( &isapnp->dev.siblings,
704: &rootdev->dev.children );
705: INIT_LIST_HEAD ( &isapnp->dev.children );
706:
707: /* Look for a driver */
708: if ( isapnp_probe ( isapnp ) == 0 ) {
709: /* isapnpdev registered, we can drop our ref */
710: isapnp = NULL;
711: } else {
712: /* Not registered; re-use struct */
713: list_del ( &isapnp->dev.siblings );
714: }
715: }
716: }
717:
718: done:
719: free ( isapnp );
720: return 0;
721:
722: err:
723: free ( isapnp );
724: isapnpbus_remove ( rootdev );
725: return rc;
726: }
727:
728: /**
729: * Remove ISAPnP root bus
730: *
731: * @v rootdev ISAPnP bus root device
732: */
733: static void isapnpbus_remove ( struct root_device *rootdev ) {
734: struct isapnp_device *isapnp;
735: struct isapnp_device *tmp;
736:
737: list_for_each_entry_safe ( isapnp, tmp, &rootdev->dev.children,
738: dev.siblings ) {
739: isapnp_remove ( isapnp );
740: list_del ( &isapnp->dev.siblings );
741: free ( isapnp );
742: }
743: }
744:
745: /** ISAPnP bus root device driver */
746: static struct root_driver isapnp_root_driver = {
747: .probe = isapnpbus_probe,
748: .remove = isapnpbus_remove,
749: };
750:
751: /** ISAPnP bus root device */
752: struct root_device isapnp_root_device __root_device = {
753: .dev = { .name = "ISAPnP" },
754: .driver = &isapnp_root_driver,
755: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.