pnic.c

Go to the documentation of this file.
00001 /**************************************************************************
00002 Etherboot -  BOOTP/TFTP Bootstrap Program
00003 Bochs Pseudo NIC driver for Etherboot
00004 ***************************************************************************/
00005 
00006 /*
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 2, or (at
00010  * your option) any later version.
00011  *
00012  * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
00013  */
00014 
00015 FILE_LICENCE ( GPL2_OR_LATER );
00016 
00017 #include <stdint.h>
00018 #include <stdio.h>
00019 #include <gpxe/io.h>
00020 #include <errno.h>
00021 #include <gpxe/pci.h>
00022 #include <gpxe/if_ether.h>
00023 #include <gpxe/ethernet.h>
00024 #include <gpxe/iobuf.h>
00025 #include <gpxe/netdevice.h>
00026 
00027 #include "pnic_api.h"
00028 
00029 struct pnic {
00030         unsigned short ioaddr;
00031 };
00032 
00033 /* 
00034  * Utility functions: issue a PNIC command, retrieve result.  Use
00035  * pnic_command_quiet if you don't want failure codes to be
00036  * automatically printed.  Returns the PNIC status code.
00037  * 
00038  * Set output_length to NULL only if you expect to receive exactly
00039  * output_max_length bytes, otherwise it'll complain that you didn't
00040  * get enough data (on the assumption that if you not interested in
00041  * discovering the output length then you're expecting a fixed amount
00042  * of data).
00043  */
00044 
00045 static uint16_t pnic_command_quiet ( struct pnic *pnic, uint16_t command,
00046                                      const void *input, uint16_t input_length,
00047                                      void *output, uint16_t output_max_length,
00048                                      uint16_t *output_length ) {
00049         uint16_t status;
00050         uint16_t _output_length;
00051 
00052         if ( input != NULL ) {
00053                 /* Write input length */
00054                 outw ( input_length, pnic->ioaddr + PNIC_REG_LEN );
00055                 /* Write input data */
00056                 outsb ( pnic->ioaddr + PNIC_REG_DATA, input, input_length );
00057         }
00058         /* Write command */
00059         outw ( command, pnic->ioaddr + PNIC_REG_CMD );
00060         /* Retrieve status */
00061         status = inw ( pnic->ioaddr + PNIC_REG_STAT );
00062         /* Retrieve output length */
00063         _output_length = inw ( pnic->ioaddr + PNIC_REG_LEN );
00064         if ( output_length == NULL ) {
00065                 if ( _output_length != output_max_length ) {
00066                         printf ( "pnic_command %#hx: wrong data length "
00067                                  "returned (expected %d, got %d)\n", command,
00068                                  output_max_length, _output_length );
00069                 }
00070         } else {
00071                 *output_length = _output_length;
00072         }
00073         if ( output != NULL ) {
00074                 if ( _output_length > output_max_length ) {
00075                         printf ( "pnic_command %#hx: output buffer too small "
00076                                  "(have %d, need %d)\n", command,
00077                                  output_max_length, _output_length );
00078                         _output_length = output_max_length;
00079                 }
00080                 /* Retrieve output data */
00081                 insb ( pnic->ioaddr + PNIC_REG_DATA, output, _output_length );
00082         }
00083         return status;
00084 }
00085 
00086 static uint16_t pnic_command ( struct pnic *pnic, uint16_t command,
00087                                const void *input, uint16_t input_length,
00088                                void *output, uint16_t output_max_length,
00089                                uint16_t *output_length ) {
00090         uint16_t status = pnic_command_quiet ( pnic, command,
00091                                                input, input_length,
00092                                                output, output_max_length,
00093                                                output_length );
00094         if ( status == PNIC_STATUS_OK ) return status;
00095         printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
00096                  command, input_length, status );
00097         return status;
00098 }
00099 
00100 /* Check API version matches that of NIC */
00101 static int pnic_api_check ( uint16_t api_version ) {
00102         if ( api_version != PNIC_API_VERSION ) {
00103                 printf ( "Warning: API version mismatch! "
00104                          "(NIC's is %d.%d, ours is %d.%d)\n",
00105                          api_version >> 8, api_version & 0xff,
00106                          PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
00107         }
00108         if ( api_version < PNIC_API_VERSION ) {
00109                 printf ( "** You may need to update your copy of Bochs **\n" );
00110         }
00111         return ( api_version == PNIC_API_VERSION );
00112 }
00113 
00114 /**************************************************************************
00115 POLL - Wait for a frame
00116 ***************************************************************************/
00117 static void pnic_poll ( struct net_device *netdev ) {
00118         struct pnic *pnic = netdev->priv;
00119         struct io_buffer *iobuf;
00120         uint16_t length;
00121         uint16_t qlen;
00122 
00123         /* Fetch all available packets */
00124         while ( 1 ) {
00125                 if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0,
00126                                     &qlen, sizeof ( qlen ), NULL )
00127                      != PNIC_STATUS_OK )
00128                         return;
00129                 if ( qlen == 0 )
00130                         return;
00131                 iobuf = alloc_iob ( ETH_FRAME_LEN );
00132                 if ( ! iobuf ) {
00133                         DBG ( "could not allocate buffer\n" );
00134                         netdev_rx_err ( netdev, NULL, -ENOMEM );
00135                         return;
00136                 }
00137                 if ( pnic_command ( pnic, PNIC_CMD_RECV, NULL, 0,
00138                                     iobuf->data, ETH_FRAME_LEN, &length )
00139                      != PNIC_STATUS_OK ) {
00140                         netdev_rx_err ( netdev, iobuf, -EIO );
00141                         return;
00142                 }
00143                 iob_put ( iobuf, length );
00144                 netdev_rx ( netdev, iobuf );
00145         }
00146 }
00147 
00148 /**************************************************************************
00149 TRANSMIT - Transmit a frame
00150 ***************************************************************************/
00151 static int pnic_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
00152         struct pnic *pnic = netdev->priv;
00153 
00154         /* Pad the packet */
00155         iob_pad ( iobuf, ETH_ZLEN );
00156 
00157         /* Send packet */
00158         pnic_command ( pnic, PNIC_CMD_XMIT, iobuf->data, iob_len ( iobuf ),
00159                        NULL, 0, NULL );
00160 
00161         netdev_tx_complete ( netdev, iobuf );
00162         return 0;
00163 }
00164 
00165 /**************************************************************************
00166 OPEN - Open network device
00167 ***************************************************************************/
00168 static int pnic_open ( struct net_device *netdev __unused ) {
00169         /* Nothing to do */
00170         return 0;
00171 }
00172 
00173 /**************************************************************************
00174 CLOSE - Close network device
00175 ***************************************************************************/
00176 static void pnic_close ( struct net_device *netdev __unused ) {
00177         /* Nothing to do */
00178 }
00179 
00180 /**************************************************************************
00181 IRQ - Enable/disable interrupts
00182 ***************************************************************************/
00183 static void pnic_irq ( struct net_device *netdev, int enable ) {
00184         struct pnic *pnic = netdev->priv;
00185         uint8_t mask = ( enable ? 1 : 0 );
00186         
00187         pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &mask, sizeof ( mask ),
00188                        NULL, 0, NULL );
00189 }
00190 
00191 /**************************************************************************
00192 OPERATIONS TABLE
00193 ***************************************************************************/
00194 static struct net_device_operations pnic_operations = {
00195         .open           = pnic_open,
00196         .close          = pnic_close,
00197         .transmit       = pnic_transmit,
00198         .poll           = pnic_poll,
00199         .irq            = pnic_irq,
00200 };
00201 
00202 /**************************************************************************
00203 DISABLE - Turn off ethernet interface
00204 ***************************************************************************/
00205 static void pnic_remove ( struct pci_device *pci ) {
00206         struct net_device *netdev = pci_get_drvdata ( pci );
00207         struct pnic *pnic = netdev->priv;
00208 
00209         unregister_netdev ( netdev );
00210         pnic_command ( pnic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
00211         netdev_nullify ( netdev );
00212         netdev_put ( netdev );
00213 }
00214 
00215 /**************************************************************************
00216 PROBE - Look for an adapter, this routine's visible to the outside
00217 ***************************************************************************/
00218 static int pnic_probe ( struct pci_device *pci,
00219                         const struct pci_device_id *id __unused ) {
00220         struct net_device *netdev;
00221         struct pnic *pnic;
00222         uint16_t api_version;
00223         uint16_t status;
00224         int rc;
00225 
00226         /* Allocate net device */
00227         netdev = alloc_etherdev ( sizeof ( *pnic ) );
00228         if ( ! netdev )
00229                 return -ENOMEM;
00230         netdev_init ( netdev, &pnic_operations );
00231         pnic = netdev->priv;
00232         pci_set_drvdata ( pci, netdev );
00233         netdev->dev = &pci->dev;
00234         pnic->ioaddr = pci->ioaddr;
00235 
00236         /* Fix up PCI device */
00237         adjust_pci_device ( pci );
00238         
00239         /* API version check */
00240         status = pnic_command_quiet ( pnic, PNIC_CMD_API_VER, NULL, 0,
00241                                       &api_version,
00242                                       sizeof ( api_version ), NULL );
00243         if ( status != PNIC_STATUS_OK ) {
00244                 printf ( "PNIC failed installation check, code %#hx\n",
00245                          status );
00246                 rc = -EIO;
00247                 goto err;
00248         }
00249         pnic_api_check ( api_version );
00250 
00251         /* Get MAC address */
00252         status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0,
00253                                 netdev->hw_addr, ETH_ALEN, NULL );
00254 
00255         /* Mark as link up; PNIC has no concept of link state */
00256         netdev_link_up ( netdev );
00257 
00258         /* Register network device */
00259         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00260                 goto err;
00261 
00262         return 0;
00263 
00264  err:
00265         /* Free net device */
00266         netdev_nullify ( netdev );
00267         netdev_put ( netdev );
00268         return rc;
00269 }
00270 
00271 static struct pci_device_id pnic_nics[] = {
00272 /* genrules.pl doesn't let us use macros for PCI IDs...*/
00273 PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor", 0 ),
00274 };
00275 
00276 struct pci_driver pnic_driver __pci_driver = {
00277         .ids = pnic_nics,
00278         .id_count = ( sizeof ( pnic_nics ) / sizeof ( pnic_nics[0] ) ),
00279         .probe = pnic_probe,
00280         .remove = pnic_remove,
00281 };

Generated on Tue Apr 6 20:01:02 2010 for gPXE by  doxygen 1.5.7.1