isapnp.c

Go to the documentation of this file.
00001 /**************************************************************************
00002 *
00003 *    isapnp.c -- Etherboot isapnp support for the 3Com 3c515
00004 *    Written 2002-2003 by Timothy Legge <tlegge@rogers.com>
00005 *
00006 *    This program is free software; you can redistribute it and/or modify
00007 *    it under the terms of the GNU General Public License as published by
00008 *    the Free Software Foundation; either version 2 of the License, or
00009 *    (at your option) any later version.
00010 *
00011 *    This program is distributed in the hope that it will be useful,
00012 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 *    GNU General Public License for more details.
00015 *
00016 *    You should have received a copy of the GNU General Public License
00017 *    along with this program; if not, write to the Free Software
00018 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019 *
00020 *    Portions of this code:
00021 *       Copyright (C) 2001  P.J.H.Fox (fox@roestock.demon.co.uk)
00022 *
00023 *
00024 *    REVISION HISTORY:
00025 *    ================
00026 *    Version 0.1 April 26, 2002 TJL
00027 *    Version 0.2 01/08/2003     TJL Moved outside the 3c515.c driver file
00028 *    Version 0.3 Sept 23, 2003  timlegge Change delay to currticks
00029 *               
00030 *
00031 *    Generalised into an ISAPnP bus that can be used by more than just
00032 *    the 3c515 by Michael Brown <mbrown@fensystems.co.uk>
00033 *
00034 ***************************************************************************/
00035 
00036 /** @file
00037  *
00038  * ISAPnP bus support
00039  *
00040  * Etherboot orignally gained ISAPnP support in a very limited way for
00041  * the 3c515 NIC.  The current implementation is almost a complete
00042  * rewrite based on the ISAPnP specification, with passing reference
00043  * to the Linux ISAPnP code.
00044  *
00045  * There can be only one ISAPnP bus in a system.  Once the read port
00046  * is known and all cards have been allocated CSNs, there's nothing to
00047  * be gained by re-scanning for cards.
00048  *
00049  * External code (e.g. the ISAPnP ROM prefix) may already know the
00050  * read port address, in which case it can store it in
00051  * #isapnp_read_port.  Note that setting the read port address in this
00052  * way will prevent further isolation from taking place; you should
00053  * set the read port address only if you know that devices have
00054  * already been allocated CSNs.
00055  *
00056  */
00057 
00058 FILE_LICENCE ( GPL2_OR_LATER );
00059 
00060 #include <stdint.h>
00061 #include <stdlib.h>
00062 #include <string.h>
00063 #include <stdio.h>
00064 #include <errno.h>
00065 #include <gpxe/io.h>
00066 #include <unistd.h>
00067 #include <gpxe/isapnp.h>
00068 
00069 /**
00070  * ISAPnP Read Port address.
00071  *
00072  * ROM prefix may be able to set this address, which is why this is
00073  * non-static.
00074  */
00075 uint16_t isapnp_read_port;
00076 
00077 static void isapnpbus_remove ( struct root_device *rootdev );
00078 
00079 /*
00080  * ISAPnP utility functions
00081  *
00082  */
00083 
00084 #define ISAPNP_CARD_ID_FMT "ID %04x:%04x (\"%s\") serial %x"
00085 #define ISAPNP_CARD_ID_DATA(identifier)                                   \
00086         (identifier)->vendor_id, (identifier)->prod_id,                   \
00087         isa_id_string ( (identifier)->vendor_id, (identifier)->prod_id ), \
00088         (identifier)->serial
00089 #define ISAPNP_DEV_ID_FMT "ID %04x:%04x (\"%s\")"
00090 #define ISAPNP_DEV_ID_DATA(isapnp)                                        \
00091         (isapnp)->vendor_id, (isapnp)->prod_id,                           \
00092         isa_id_string ( (isapnp)->vendor_id, (isapnp)->prod_id )
00093 
00094 static inline void isapnp_write_address ( unsigned int address ) {
00095         outb ( address, ISAPNP_ADDRESS );
00096 }
00097 
00098 static inline void isapnp_write_data ( unsigned int data ) {
00099         outb ( data, ISAPNP_WRITE_DATA );
00100 }
00101 
00102 static inline unsigned int isapnp_read_data ( void ) {
00103         return inb ( isapnp_read_port );
00104 }
00105 
00106 static inline void isapnp_write_byte ( unsigned int address,
00107                                        unsigned int value ) {
00108         isapnp_write_address ( address );
00109         isapnp_write_data ( value );
00110 }
00111 
00112 static inline unsigned int isapnp_read_byte ( unsigned int address ) {
00113         isapnp_write_address ( address );
00114         return isapnp_read_data ();
00115 }
00116 
00117 static inline unsigned int isapnp_read_word ( unsigned int address ) {
00118         /* Yes, they're in big-endian order */
00119         return ( ( isapnp_read_byte ( address ) << 8 )
00120                  | isapnp_read_byte ( address + 1 ) );
00121 }
00122 
00123 /** Inform cards of a new read port address */
00124 static inline void isapnp_set_read_port ( void ) {
00125         isapnp_write_byte ( ISAPNP_READPORT, ( isapnp_read_port >> 2 ) );
00126 }
00127 
00128 /**
00129  * Enter the Isolation state.
00130  *
00131  * Only cards currently in the Sleep state will respond to this
00132  * command.
00133  */
00134 static inline void isapnp_serialisolation ( void ) {
00135         isapnp_write_address ( ISAPNP_SERIALISOLATION );
00136 }
00137 
00138 /**
00139  * Enter the Wait for Key state.
00140  *
00141  * All cards will respond to this command, regardless of their current
00142  * state.
00143  */
00144 static inline void isapnp_wait_for_key ( void ) {
00145         isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY );
00146 }
00147 
00148 /**
00149  * Reset (i.e. remove) Card Select Number.
00150  *
00151  * Only cards currently in the Sleep state will respond to this
00152  * command.
00153  */
00154 static inline void isapnp_reset_csn ( void ) {
00155         isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN );
00156 }
00157 
00158 /**
00159  * Place a specified card into the Config state.
00160  *
00161  * @v csn               Card Select Number
00162  * @ret None            -
00163  * @err None            -
00164  *
00165  * Only cards currently in the Sleep, Isolation, or Config states will
00166  * respond to this command.  The card that has the specified CSN will
00167  * enter the Config state, all other cards will enter the Sleep state.
00168  */
00169 static inline void isapnp_wake ( uint8_t csn ) {
00170         isapnp_write_byte ( ISAPNP_WAKE, csn );
00171 }
00172 
00173 static inline unsigned int isapnp_read_resourcedata ( void ) {
00174         return isapnp_read_byte ( ISAPNP_RESOURCEDATA );
00175 }
00176 
00177 static inline unsigned int isapnp_read_status ( void ) {
00178         return isapnp_read_byte ( ISAPNP_STATUS );
00179 }
00180 
00181 /**
00182  * Assign a Card Select Number to a card, and enter the Config state.
00183  *
00184  * @v csn               Card Select Number
00185  *
00186  * Only cards in the Isolation state will respond to this command.
00187  * The isolation protocol is designed so that only one card will
00188  * remain in the Isolation state by the time the isolation protocol
00189  * completes.
00190  */
00191 static inline void isapnp_write_csn ( unsigned int csn ) {
00192         isapnp_write_byte ( ISAPNP_CARDSELECTNUMBER, csn );
00193 }
00194 
00195 static inline void isapnp_logicaldevice ( unsigned int logdev ) {
00196         isapnp_write_byte ( ISAPNP_LOGICALDEVICENUMBER, logdev );
00197 }
00198 
00199 static inline void isapnp_activate ( unsigned int logdev ) {
00200         isapnp_logicaldevice ( logdev );
00201         isapnp_write_byte ( ISAPNP_ACTIVATE, 1 );
00202 }
00203 
00204 static inline void isapnp_deactivate ( unsigned int logdev ) {
00205         isapnp_logicaldevice ( logdev );
00206         isapnp_write_byte ( ISAPNP_ACTIVATE, 0 );
00207 }
00208 
00209 static inline unsigned int isapnp_read_iobase ( unsigned int index ) {
00210         return isapnp_read_word ( ISAPNP_IOBASE ( index ) );
00211 }
00212 
00213 static inline unsigned int isapnp_read_irqno ( unsigned int index ) {
00214         return isapnp_read_byte ( ISAPNP_IRQNO ( index ) );
00215 }
00216 
00217 static void isapnp_delay ( void ) {
00218         udelay ( 1000 );
00219 }
00220 
00221 /**
00222  * Linear feedback shift register.
00223  *
00224  * @v lfsr              Current value of the LFSR
00225  * @v input_bit         Current input bit to the LFSR
00226  * @ret lfsr            Next value of the LFSR
00227  *
00228  * This routine implements the linear feedback shift register as
00229  * described in Appendix B of the PnP ISA spec.  The hardware
00230  * implementation uses eight D-type latches and two XOR gates.  I
00231  * think this is probably the smallest possible implementation in
00232  * software.  Six instructions when input_bit is a constant 0 (for
00233  * isapnp_send_key).  :)
00234  */
00235 static inline unsigned int isapnp_lfsr_next ( unsigned int lfsr,
00236                                               unsigned int input_bit ) {
00237         register uint8_t lfsr_next;
00238 
00239         lfsr_next = lfsr >> 1;
00240         lfsr_next |= ( ( ( lfsr ^ lfsr_next ) ^ input_bit ) ) << 7;
00241         return lfsr_next;
00242 }
00243 
00244 /**
00245  * Send the ISAPnP initiation key.
00246  *
00247  * Sending the key causes all ISAPnP cards that are currently in the
00248  * Wait for Key state to transition into the Sleep state.
00249  */
00250 static void isapnp_send_key ( void ) {
00251         unsigned int i;
00252         unsigned int lfsr;
00253 
00254         isapnp_delay();
00255         isapnp_write_address ( 0x00 );
00256         isapnp_write_address ( 0x00 );
00257 
00258         lfsr = ISAPNP_LFSR_SEED;
00259         for ( i = 0 ; i < 32 ; i++ ) {
00260                 isapnp_write_address ( lfsr );
00261                 lfsr = isapnp_lfsr_next ( lfsr, 0 );
00262         }
00263 }
00264 
00265 /**
00266  * Compute ISAPnP identifier checksum
00267  *
00268  * @v identifier        ISAPnP identifier
00269  * @ret checksum        Expected checksum value
00270  */
00271 static unsigned int isapnp_checksum ( struct isapnp_identifier *identifier ) {
00272         unsigned int i, j;
00273         unsigned int lfsr;
00274         unsigned int byte;
00275 
00276         lfsr = ISAPNP_LFSR_SEED;
00277         for ( i = 0 ; i < 8 ; i++ ) {
00278                 byte = * ( ( ( uint8_t * ) identifier ) + i );
00279                 for ( j = 0 ; j < 8 ; j++ ) {
00280                         lfsr = isapnp_lfsr_next ( lfsr, byte );
00281                         byte >>= 1;
00282                 }
00283         }
00284         return lfsr;
00285 }
00286 
00287 /*
00288  * Read a byte of resource data from the current location
00289  *
00290  * @ret byte            Byte of resource data
00291  */
00292 static inline unsigned int isapnp_peek_byte ( void ) {
00293         unsigned int i;
00294 
00295         /* Wait for data to be ready */
00296         for ( i = 0 ; i < 20 ; i++ ) {
00297                 if ( isapnp_read_status() & 0x01 ) {
00298                         /* Byte ready - read it */
00299                         return isapnp_read_resourcedata();
00300                 }
00301                 isapnp_delay();
00302         }
00303         /* Data never became ready - return 0xff */
00304         return 0xff;
00305 }
00306 
00307 /**
00308  * Read resource data.
00309  *
00310  * @v buf               Buffer in which to store data, or NULL
00311  * @v bytes             Number of bytes to read
00312  *
00313  * Resource data is read from the current location.  If #buf is NULL,
00314  * the data is discarded.
00315  */
00316 static void isapnp_peek ( void *buf, size_t len ) {
00317         unsigned int i;
00318         unsigned int byte;
00319 
00320         for ( i = 0 ; i < len ; i++) {
00321                 byte = isapnp_peek_byte();
00322                 if ( buf )
00323                         * ( ( uint8_t * ) buf + i ) = byte;
00324         }
00325 }
00326 
00327 /**
00328  * Find a tag within the resource data.
00329  *
00330  * @v wanted_tag        The tag that we're looking for
00331  * @v buf               Buffer in which to store the tag's contents
00332  * @v len               Length of buffer
00333  * @ret rc              Return status code
00334  *
00335  * Scan through the resource data until we find a particular tag, and
00336  * read its contents into a buffer.
00337  */
00338 static int isapnp_find_tag ( unsigned int wanted_tag, void *buf, size_t len ) {
00339         unsigned int tag;
00340         unsigned int tag_len;
00341 
00342         DBG2 ( "ISAPnP read tag" );
00343         do {
00344                 tag = isapnp_peek_byte();
00345                 if ( ISAPNP_IS_SMALL_TAG ( tag ) ) {
00346                         tag_len = ISAPNP_SMALL_TAG_LEN ( tag );
00347                         tag = ISAPNP_SMALL_TAG_NAME ( tag );
00348                 } else {
00349                         tag_len = ( isapnp_peek_byte() +
00350                                     ( isapnp_peek_byte() << 8 ) );
00351                         tag = ISAPNP_LARGE_TAG_NAME ( tag );
00352                 }
00353                 DBG2 ( " %02x (%02x)", tag, tag_len );
00354                 if ( tag == wanted_tag ) {
00355                         if ( len > tag_len )
00356                                 len = tag_len;
00357                         isapnp_peek ( buf, len );
00358                         DBG2 ( "\n" );
00359                         return 0;
00360                 } else {
00361                         isapnp_peek ( NULL, tag_len );
00362                 }
00363         } while ( tag != ISAPNP_TAG_END );
00364         DBG2 ( "\n" );
00365         return -ENOENT;
00366 }
00367 
00368 /**
00369  * Find specified Logical Device ID tag
00370  *
00371  * @v logdev            Logical device ID
00372  * @v logdevid          Logical device ID structure to fill in
00373  * @ret rc              Return status code
00374  */
00375 static int isapnp_find_logdevid ( unsigned int logdev,
00376                                   struct isapnp_logdevid *logdevid ) {
00377         unsigned int i;
00378         int rc;
00379 
00380         for ( i = 0 ; i <= logdev ; i++ ) {
00381                 if ( ( rc = isapnp_find_tag ( ISAPNP_TAG_LOGDEVID, logdevid,
00382                                               sizeof ( *logdevid ) ) ) != 0 )
00383                         return rc;
00384         }
00385         return 0;
00386 }
00387 
00388 /**
00389  * Try isolating ISAPnP cards at the current read port.
00390  *
00391  * @ret >0              Number of ISAPnP cards found
00392  * @ret 0               There are no ISAPnP cards in the system
00393  * @ret <0              A conflict was detected; try a new read port
00394  * @err None            -
00395  *
00396  * The state diagram on page 18 (PDF page 24) of the PnP ISA spec
00397  * gives the best overview of what happens here.
00398  */
00399 static int isapnp_try_isolate ( void ) {
00400         struct isapnp_identifier identifier;
00401         unsigned int i, j;
00402         unsigned int seen_55aa, seen_life;
00403         unsigned int csn = 0;
00404         unsigned int data;
00405         unsigned int byte;
00406 
00407         DBG ( "ISAPnP attempting isolation at read port %04x\n",
00408               isapnp_read_port );
00409 
00410         /* Place all cards into the Sleep state, whatever state
00411          * they're currently in.
00412          */
00413         isapnp_wait_for_key();
00414         isapnp_send_key();
00415 
00416         /* Reset all assigned CSNs */
00417         isapnp_reset_csn();
00418         isapnp_delay();
00419         isapnp_delay();
00420         
00421         /* Place all cards into the Isolation state */
00422         isapnp_wait_for_key ();
00423         isapnp_send_key();
00424         isapnp_wake ( 0x00 );
00425         
00426         /* Set the read port */
00427         isapnp_set_read_port();
00428         isapnp_delay();
00429 
00430         while ( 1 ) {
00431 
00432                 /* All cards that do not have assigned CSNs are
00433                  * currently in the Isolation state, each time we go
00434                  * through this loop.
00435                  */
00436 
00437                 /* Initiate serial isolation */
00438                 isapnp_serialisolation();
00439                 isapnp_delay();
00440 
00441                 /* Read identifier serially via the ISAPnP read port. */
00442                 memset ( &identifier, 0, sizeof ( identifier ) );
00443                 seen_55aa = seen_life = 0;
00444                 for ( i = 0 ; i < 9 ; i++ ) {
00445                         byte = 0;
00446                         for ( j = 0 ; j < 8 ; j++ ) {
00447                                 data = isapnp_read_data();
00448                                 isapnp_delay();
00449                                 data = ( ( data << 8 ) | isapnp_read_data() );
00450                                 isapnp_delay();
00451                                 byte >>= 1;
00452                                 if (  data != 0xffff ) {
00453                                         seen_life++;
00454                                         if ( data == 0x55aa ) {
00455                                                 byte |= 0x80;
00456                                                 seen_55aa++;
00457                                         }
00458                                 }
00459                         }
00460                         *( ( ( uint8_t * ) &identifier ) + i ) = byte;
00461                 }
00462 
00463                 /* If we didn't see any 55aa patterns, stop here */
00464                 if ( ! seen_55aa ) {
00465                         if ( csn ) {
00466                                 DBG ( "ISAPnP found no more cards\n" );
00467                         } else {
00468                                 if ( seen_life ) {
00469                                         DBG ( "ISAPnP saw life but no cards, "
00470                                               "trying new read port\n" );
00471                                         csn = -1;
00472                                 } else {
00473                                         DBG ( "ISAPnP saw no signs of life, "
00474                                               "abandoning isolation\n" );
00475                                 }
00476                         }
00477                         break;
00478                 }
00479 
00480                 /* If the checksum was invalid stop here */
00481                 if ( identifier.checksum != isapnp_checksum ( &identifier) ) {
00482                         DBG ( "ISAPnP found malformed card "
00483                               ISAPNP_CARD_ID_FMT "\n  with checksum %02x "
00484                               "(should be %02x), trying new read port\n",
00485                               ISAPNP_CARD_ID_DATA ( &identifier ),
00486                               identifier.checksum,
00487                               isapnp_checksum ( &identifier) );
00488                         csn = -1;
00489                         break;
00490                 }
00491 
00492                 /* Give the device a CSN */
00493                 csn++;
00494                 DBG ( "ISAPnP found card " ISAPNP_CARD_ID_FMT
00495                       ", assigning CSN %02x\n",
00496                       ISAPNP_CARD_ID_DATA ( &identifier ), csn );
00497                 
00498                 isapnp_write_csn ( csn );
00499                 isapnp_delay();
00500 
00501                 /* Send this card back to Sleep and force all cards
00502                  * without a CSN into Isolation state
00503                  */
00504                 isapnp_wake ( 0x00 );
00505                 isapnp_delay();
00506         }
00507 
00508         /* Place all cards in Wait for Key state */
00509         isapnp_wait_for_key();
00510 
00511         /* Return number of cards found */
00512         if ( csn > 0 ) {
00513                 DBG ( "ISAPnP found %d cards at read port %04x\n",
00514                       csn, isapnp_read_port );
00515         }
00516         return csn;
00517 }
00518 
00519 /**
00520  * Find a valid read port and isolate all ISAPnP cards.
00521  *
00522  */
00523 static void isapnp_isolate ( void ) {
00524         for ( isapnp_read_port = ISAPNP_READ_PORT_START ;
00525               isapnp_read_port <= ISAPNP_READ_PORT_MAX ;
00526               isapnp_read_port += ISAPNP_READ_PORT_STEP ) {
00527                 /* Avoid problematic locations such as the NE2000
00528                  * probe space
00529                  */
00530                 if ( ( isapnp_read_port >= 0x280 ) &&
00531                      ( isapnp_read_port <= 0x380 ) )
00532                         continue;
00533                 
00534                 /* If we detect any ISAPnP cards at this location, stop */
00535                 if ( isapnp_try_isolate() >= 0 )
00536                         return;
00537         }
00538 }
00539 
00540 /**
00541  * Activate or deactivate an ISAPnP device.
00542  *
00543  * @v isapnp            ISAPnP device
00544  * @v activation        True to enable, False to disable the device
00545  * @ret None            -
00546  * @err None            -
00547  *
00548  * This routine simply activates the device in its current
00549  * configuration, or deactivates the device.  It does not attempt any
00550  * kind of resource arbitration.
00551  *
00552  */
00553 void isapnp_device_activation ( struct isapnp_device *isapnp,
00554                                 int activation ) {
00555         /* Wake the card and select the logical device */
00556         isapnp_wait_for_key ();
00557         isapnp_send_key ();
00558         isapnp_wake ( isapnp->csn );
00559         isapnp_logicaldevice ( isapnp->logdev );
00560 
00561         /* Activate/deactivate the logical device */
00562         isapnp_activate ( activation );
00563         isapnp_delay();
00564 
00565         /* Return all cards to Wait for Key state */
00566         isapnp_wait_for_key ();
00567 
00568         DBG ( "ISAPnP %s device %02x:%02x\n",
00569               ( activation ? "activated" : "deactivated" ),
00570               isapnp->csn, isapnp->logdev );
00571 }
00572 
00573 /**
00574  * Probe an ISAPnP device
00575  *
00576  * @v isapnp            ISAPnP device
00577  * @ret rc              Return status code
00578  *
00579  * Searches for a driver for the ISAPnP device.  If a driver is found,
00580  * its probe() routine is called.
00581  */
00582 static int isapnp_probe ( struct isapnp_device *isapnp ) {
00583         struct isapnp_driver *driver;
00584         struct isapnp_device_id *id;
00585         unsigned int i;
00586         int rc;
00587 
00588         DBG ( "Adding ISAPnP device %02x:%02x (%04x:%04x (\"%s\") "
00589               "io %x irq %d)\n", isapnp->csn, isapnp->logdev,
00590               isapnp->vendor_id, isapnp->prod_id,
00591               isa_id_string ( isapnp->vendor_id, isapnp->prod_id ),
00592               isapnp->ioaddr, isapnp->irqno );
00593 
00594         for_each_table_entry ( driver, ISAPNP_DRIVERS ) {
00595                 for ( i = 0 ; i < driver->id_count ; i++ ) {
00596                         id = &driver->ids[i];
00597                         if ( id->vendor_id != isapnp->vendor_id )
00598                                 continue;
00599                         if ( ISA_PROD_ID ( id->prod_id ) !=
00600                              ISA_PROD_ID ( isapnp->prod_id ) )
00601                                 continue;
00602                         isapnp->driver = driver;
00603                         isapnp->driver_name = id->name;
00604                         DBG ( "...using driver %s\n", isapnp->driver_name );
00605                         if ( ( rc = driver->probe ( isapnp, id ) ) != 0 ) {
00606                                 DBG ( "......probe failed\n" );
00607                                 continue;
00608                         }
00609                         return 0;
00610                 }
00611         }
00612 
00613         DBG ( "...no driver found\n" );
00614         return -ENOTTY;
00615 }
00616 
00617 /**
00618  * Remove an ISAPnP device
00619  *
00620  * @v isapnp            ISAPnP device
00621  */
00622 static void isapnp_remove ( struct isapnp_device *isapnp ) {
00623         isapnp->driver->remove ( isapnp );
00624         DBG ( "Removed ISAPnP device %02x:%02x\n",
00625               isapnp->csn, isapnp->logdev );
00626 }
00627 
00628 /**
00629  * Probe ISAPnP root bus
00630  *
00631  * @v rootdev           ISAPnP bus root device
00632  *
00633  * Scans the ISAPnP bus for devices and registers all devices it can
00634  * find.
00635  */
00636 static int isapnpbus_probe ( struct root_device *rootdev ) {
00637         struct isapnp_device *isapnp = NULL;
00638         struct isapnp_identifier identifier;
00639         struct isapnp_logdevid logdevid;
00640         unsigned int csn;
00641         unsigned int logdev;
00642         int rc;
00643 
00644         /* Perform isolation if it hasn't yet been done */
00645         if ( ! isapnp_read_port )
00646                 isapnp_isolate();
00647 
00648         for ( csn = 1 ; csn <= 0xff ; csn++ ) {
00649                 for ( logdev = 0 ; logdev <= 0xff ; logdev++ ) {
00650 
00651                         /* Allocate struct isapnp_device */
00652                         if ( ! isapnp )
00653                                 isapnp = malloc ( sizeof ( *isapnp ) );
00654                         if ( ! isapnp ) {
00655                                 rc = -ENOMEM;
00656                                 goto err;
00657                         }
00658                         memset ( isapnp, 0, sizeof ( *isapnp ) );
00659                         isapnp->csn = csn;
00660                         isapnp->logdev = logdev;
00661 
00662                         /* Wake the card */
00663                         isapnp_wait_for_key();
00664                         isapnp_send_key();
00665                         isapnp_wake ( csn );
00666 
00667                         /* Read the card identifier */
00668                         isapnp_peek ( &identifier, sizeof ( identifier ) );
00669                         
00670                         /* No card with this CSN; stop here */
00671                         if ( identifier.vendor_id & 0x80 )
00672                                 goto done;
00673 
00674                         /* Find the Logical Device ID tag */
00675                         if ( ( rc = isapnp_find_logdevid ( logdev,
00676                                                            &logdevid ) ) != 0){
00677                                 /* No more logical devices; go to next CSN */
00678                                 break;
00679                         }
00680                         
00681                         /* Select the logical device */
00682                         isapnp_logicaldevice ( logdev );
00683 
00684                         /* Populate struct isapnp_device */
00685                         isapnp->vendor_id = logdevid.vendor_id;
00686                         isapnp->prod_id = logdevid.prod_id;
00687                         isapnp->ioaddr = isapnp_read_iobase ( 0 );
00688                         isapnp->irqno = isapnp_read_irqno ( 0 );
00689 
00690                         /* Return all cards to Wait for Key state */
00691                         isapnp_wait_for_key();
00692 
00693                         /* Add to device hierarchy */
00694                         snprintf ( isapnp->dev.name,
00695                                    sizeof ( isapnp->dev.name ),
00696                                    "ISAPnP%02x:%02x", csn, logdev );
00697                         isapnp->dev.desc.bus_type = BUS_TYPE_ISAPNP;
00698                         isapnp->dev.desc.vendor = isapnp->vendor_id;
00699                         isapnp->dev.desc.device = isapnp->prod_id;
00700                         isapnp->dev.desc.ioaddr = isapnp->ioaddr;
00701                         isapnp->dev.desc.irq = isapnp->irqno;
00702                         isapnp->dev.parent = &rootdev->dev;
00703                         list_add ( &isapnp->dev.siblings,
00704                                    &rootdev->dev.children );
00705                         INIT_LIST_HEAD ( &isapnp->dev.children );
00706                         
00707                         /* Look for a driver */
00708                         if ( isapnp_probe ( isapnp ) == 0 ) {
00709                                 /* isapnpdev registered, we can drop our ref */
00710                                 isapnp = NULL;
00711                         } else {
00712                                 /* Not registered; re-use struct */
00713                                 list_del ( &isapnp->dev.siblings );
00714                         }
00715                 }
00716         }
00717 
00718  done:
00719         free ( isapnp );
00720         return 0;
00721 
00722  err:
00723         free ( isapnp );
00724         isapnpbus_remove ( rootdev );
00725         return rc;
00726 }
00727 
00728 /**
00729  * Remove ISAPnP root bus
00730  *
00731  * @v rootdev           ISAPnP bus root device
00732  */
00733 static void isapnpbus_remove ( struct root_device *rootdev ) {
00734         struct isapnp_device *isapnp;
00735         struct isapnp_device *tmp;
00736 
00737         list_for_each_entry_safe ( isapnp, tmp, &rootdev->dev.children,
00738                                    dev.siblings ) {
00739                 isapnp_remove ( isapnp );
00740                 list_del ( &isapnp->dev.siblings );
00741                 free ( isapnp );
00742         }
00743 }
00744 
00745 /** ISAPnP bus root device driver */
00746 static struct root_driver isapnp_root_driver = {
00747         .probe = isapnpbus_probe,
00748         .remove = isapnpbus_remove,
00749 };
00750 
00751 /** ISAPnP bus root device */
00752 struct root_device isapnp_root_device __root_device = {
00753         .dev = { .name = "ISAPnP" },
00754         .driver = &isapnp_root_driver,
00755 };

Generated on Tue Apr 6 20:00:52 2010 for gPXE by  doxygen 1.5.7.1