|
|
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.