iPXE
rndis.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * Remote Network Driver Interface Specification
00029  *
00030  */
00031 
00032 #include <unistd.h>
00033 #include <string.h>
00034 #include <errno.h>
00035 #include <byteswap.h>
00036 #include <ipxe/iobuf.h>
00037 #include <ipxe/netdevice.h>
00038 #include <ipxe/ethernet.h>
00039 #include <ipxe/device.h>
00040 #include <ipxe/rndis.h>
00041 
00042 /**
00043  * Allocate I/O buffer
00044  *
00045  * @v len               Length
00046  * @ret iobuf           I/O buffer, or NULL
00047  */
00048 static struct io_buffer * rndis_alloc_iob ( size_t len ) {
00049         struct rndis_header *header;
00050         struct io_buffer *iobuf;
00051 
00052         /* Allocate I/O buffer and reserve space */
00053         iobuf = alloc_iob ( sizeof ( *header ) + len );
00054         if ( iobuf )
00055                 iob_reserve ( iobuf, sizeof ( *header ) );
00056 
00057         return iobuf;
00058 }
00059 
00060 /**
00061  * Wait for completion
00062  *
00063  * @v rndis             RNDIS device
00064  * @v wait_id           Request ID
00065  * @ret rc              Return status code
00066  */
00067 static int rndis_wait ( struct rndis_device *rndis, unsigned int wait_id ) {
00068         unsigned int i;
00069 
00070         /* Record query ID */
00071         rndis->wait_id = wait_id;
00072 
00073         /* Wait for operation to complete */
00074         for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) {
00075 
00076                 /* Check for completion */
00077                 if ( ! rndis->wait_id )
00078                         return rndis->wait_rc;
00079 
00080                 /* Poll RNDIS device */
00081                 rndis->op->poll ( rndis );
00082 
00083                 /* Delay for 1ms */
00084                 mdelay ( 1 );
00085         }
00086 
00087         DBGC ( rndis, "RNDIS %s timed out waiting for ID %#08x\n",
00088                rndis->name, wait_id );
00089         return -ETIMEDOUT;
00090 }
00091 
00092 /**
00093  * Transmit message
00094  *
00095  * @v rndis             RNDIS device
00096  * @v iobuf             I/O buffer
00097  * @v type              Message type
00098  * @ret rc              Return status code
00099  */
00100 static int rndis_tx_message ( struct rndis_device *rndis,
00101                               struct io_buffer *iobuf, unsigned int type ) {
00102         struct rndis_header *header;
00103         int rc;
00104 
00105         /* Prepend RNDIS header */
00106         header = iob_push ( iobuf, sizeof ( *header ) );
00107         header->type = cpu_to_le32 ( type );
00108         header->len = cpu_to_le32 ( iob_len ( iobuf ) );
00109 
00110         /* Transmit message */
00111         if ( ( rc = rndis->op->transmit ( rndis, iobuf ) ) != 0 ) {
00112                 DBGC ( rndis, "RNDIS %s could not transmit: %s\n",
00113                        rndis->name, strerror ( rc ) );
00114                 return rc;
00115         }
00116 
00117         return 0;
00118 }
00119 
00120 /**
00121  * Complete message transmission
00122  *
00123  * @v rndis             RNDIS device
00124  * @v iobuf             I/O buffer
00125  * @v rc                Packet status code
00126  */
00127 void rndis_tx_complete_err ( struct rndis_device *rndis,
00128                              struct io_buffer *iobuf, int rc ) {
00129         struct net_device *netdev = rndis->netdev;
00130         struct rndis_header *header;
00131         size_t len = iob_len ( iobuf );
00132 
00133         /* Sanity check */
00134         if ( len < sizeof ( *header ) ) {
00135                 DBGC ( rndis, "RNDIS %s completed underlength transmission:\n",
00136                        rndis->name );
00137                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00138                 netdev_tx_err ( netdev, NULL, -EINVAL );
00139                 return;
00140         }
00141         header = iobuf->data;
00142 
00143         /* Complete buffer */
00144         if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) {
00145                 netdev_tx_complete_err ( netdev, iobuf, rc );
00146         } else {
00147                 free_iob ( iobuf );
00148         }
00149 }
00150 
00151 /**
00152  * Transmit data packet
00153  *
00154  * @v rndis             RNDIS device
00155  * @v iobuf             I/O buffer
00156  * @ret rc              Return status code
00157  */
00158 static int rndis_tx_data ( struct rndis_device *rndis,
00159                            struct io_buffer *iobuf ) {
00160         struct rndis_packet_message *msg;
00161         size_t len = iob_len ( iobuf );
00162         int rc;
00163 
00164         /* Prepend packet message header */
00165         msg = iob_push ( iobuf, sizeof ( *msg ) );
00166         memset ( msg, 0, sizeof ( *msg ) );
00167         msg->data.offset = cpu_to_le32 ( sizeof ( *msg ) );
00168         msg->data.len = cpu_to_le32 ( len );
00169 
00170         /* Transmit message */
00171         if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_PACKET_MSG ) ) != 0 )
00172                 return rc;
00173 
00174         return 0;
00175 }
00176 
00177 /**
00178  * Defer transmitted packet
00179  *
00180  * @v rndis             RNDIS device
00181  * @v iobuf             I/O buffer
00182  * @ret rc              Return status code
00183  *
00184  * As with netdev_tx_defer(), the caller must ensure that space in the
00185  * transmit descriptor ring is freed up before calling
00186  * rndis_tx_complete().
00187  *
00188  * Unlike netdev_tx_defer(), this call may fail.
00189  */
00190 int rndis_tx_defer ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
00191         struct net_device *netdev = rndis->netdev;
00192         struct rndis_header *header;
00193         struct rndis_packet_message *msg;
00194 
00195         /* Fail unless this was a packet message.  Only packet
00196          * messages correspond to I/O buffers in the network device's
00197          * TX queue; other messages cannot be deferred in this way.
00198          */
00199         assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
00200         header = iobuf->data;
00201         if ( header->type != cpu_to_le32 ( RNDIS_PACKET_MSG ) )
00202                 return -ENOTSUP;
00203 
00204         /* Strip RNDIS header and packet message header, to return
00205          * this packet to the state in which we received it.
00206          */
00207         iob_pull ( iobuf, ( sizeof ( *header ) + sizeof ( *msg ) ) );
00208 
00209         /* Defer packet */
00210         netdev_tx_defer ( netdev, iobuf );
00211 
00212         return 0;
00213 }
00214 
00215 /**
00216  * Receive data packet
00217  *
00218  * @v rndis             RNDIS device
00219  * @v iobuf             I/O buffer
00220  */
00221 static void rndis_rx_data ( struct rndis_device *rndis,
00222                             struct io_buffer *iobuf ) {
00223         struct net_device *netdev = rndis->netdev;
00224         struct rndis_packet_message *msg;
00225         size_t len = iob_len ( iobuf );
00226         size_t data_offset;
00227         size_t data_len;
00228         int rc;
00229 
00230         /* Sanity check */
00231         if ( len < sizeof ( *msg ) ) {
00232                 DBGC ( rndis, "RNDIS %s received underlength data packet:\n",
00233                        rndis->name );
00234                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00235                 rc = -EINVAL;
00236                 goto err_len;
00237         }
00238         msg = iobuf->data;
00239 
00240         /* Locate and sanity check data buffer */
00241         data_offset = le32_to_cpu ( msg->data.offset );
00242         data_len = le32_to_cpu ( msg->data.len );
00243         if ( ( data_offset > len ) || ( data_len > ( len - data_offset ) ) ) {
00244                 DBGC ( rndis, "RNDIS %s data packet data exceeds packet:\n",
00245                        rndis->name );
00246                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00247                 rc = -EINVAL;
00248                 goto err_data;
00249         }
00250 
00251         /* Strip non-data portions */
00252         iob_pull ( iobuf, data_offset );
00253         iob_unput ( iobuf, ( iob_len ( iobuf ) - data_len ) );
00254 
00255         /* Hand off to network stack */
00256         netdev_rx ( netdev, iob_disown ( iobuf ) );
00257 
00258         return;
00259 
00260  err_data:
00261  err_len:
00262         /* Report error to network stack */
00263         netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
00264 }
00265 
00266 /**
00267  * Transmit initialisation message
00268  *
00269  * @v rndis             RNDIS device
00270  * @v id                Request ID
00271  * @ret rc              Return status code
00272  */
00273 static int rndis_tx_initialise ( struct rndis_device *rndis, unsigned int id ) {
00274         struct io_buffer *iobuf;
00275         struct rndis_initialise_message *msg;
00276         int rc;
00277 
00278         /* Allocate I/O buffer */
00279         iobuf = rndis_alloc_iob ( sizeof ( *msg ) );
00280         if ( ! iobuf ) {
00281                 rc = -ENOMEM;
00282                 goto err_alloc;
00283         }
00284 
00285         /* Construct message */
00286         msg = iob_put ( iobuf, sizeof ( *msg ) );
00287         memset ( msg, 0, sizeof ( *msg ) );
00288         msg->id = id; /* Non-endian */
00289         msg->major = cpu_to_le32 ( RNDIS_VERSION_MAJOR );
00290         msg->minor = cpu_to_le32 ( RNDIS_VERSION_MINOR );
00291         msg->mtu = cpu_to_le32 ( RNDIS_MTU );
00292 
00293         /* Transmit message */
00294         if ( ( rc = rndis_tx_message ( rndis, iobuf,
00295                                        RNDIS_INITIALISE_MSG ) ) != 0 )
00296                 goto err_tx;
00297 
00298         return 0;
00299 
00300  err_tx:
00301         free_iob ( iobuf );
00302  err_alloc:
00303         return rc;
00304 }
00305 
00306 /**
00307  * Receive initialisation completion
00308  *
00309  * @v rndis             RNDIS device
00310  * @v iobuf             I/O buffer
00311  */
00312 static void rndis_rx_initialise ( struct rndis_device *rndis,
00313                                   struct io_buffer *iobuf ) {
00314         struct rndis_initialise_completion *cmplt;
00315         size_t len = iob_len ( iobuf );
00316         unsigned int id;
00317         int rc;
00318 
00319         /* Sanity check */
00320         if ( len < sizeof ( *cmplt ) ) {
00321                 DBGC ( rndis, "RNDIS %s received underlength initialisation "
00322                        "completion:\n", rndis->name );
00323                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00324                 rc = -EINVAL;
00325                 goto err_len;
00326         }
00327         cmplt = iobuf->data;
00328 
00329         /* Extract request ID */
00330         id = cmplt->id; /* Non-endian */
00331 
00332         /* Check status */
00333         if ( cmplt->status ) {
00334                 DBGC ( rndis, "RNDIS %s received initialisation completion "
00335                        "failure %#08x\n", rndis->name,
00336                        le32_to_cpu ( cmplt->status ) );
00337                 rc = -EIO;
00338                 goto err_status;
00339         }
00340 
00341         /* Success */
00342         rc = 0;
00343 
00344  err_status:
00345         /* Record completion result if applicable */
00346         if ( id == rndis->wait_id ) {
00347                 rndis->wait_id = 0;
00348                 rndis->wait_rc = rc;
00349         }
00350  err_len:
00351         free_iob ( iobuf );
00352 }
00353 
00354 /**
00355  * Initialise RNDIS
00356  *
00357  * @v rndis             RNDIS device
00358  * @ret rc              Return status code
00359  */
00360 static int rndis_initialise ( struct rndis_device *rndis ) {
00361         int rc;
00362 
00363         /* Transmit initialisation message */
00364         if ( ( rc = rndis_tx_initialise ( rndis, RNDIS_INIT_ID ) ) != 0 )
00365                 return rc;
00366 
00367         /* Wait for response */
00368         if ( ( rc = rndis_wait ( rndis, RNDIS_INIT_ID ) ) != 0 )
00369                 return rc;
00370 
00371         return 0;
00372 }
00373 
00374 /**
00375  * Transmit halt message
00376  *
00377  * @v rndis             RNDIS device
00378  * @ret rc              Return status code
00379  */
00380 static int rndis_tx_halt ( struct rndis_device *rndis ) {
00381         struct io_buffer *iobuf;
00382         struct rndis_halt_message *msg;
00383         int rc;
00384 
00385         /* Allocate I/O buffer */
00386         iobuf = rndis_alloc_iob ( sizeof ( *msg ) );
00387         if ( ! iobuf ) {
00388                 rc = -ENOMEM;
00389                 goto err_alloc;
00390         }
00391 
00392         /* Construct message */
00393         msg = iob_put ( iobuf, sizeof ( *msg ) );
00394         memset ( msg, 0, sizeof ( *msg ) );
00395 
00396         /* Transmit message */
00397         if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_HALT_MSG ) ) != 0 )
00398                 goto err_tx;
00399 
00400         return 0;
00401 
00402  err_tx:
00403         free_iob ( iobuf );
00404  err_alloc:
00405         return rc;
00406 }
00407 
00408 /**
00409  * Halt RNDIS
00410  *
00411  * @v rndis             RNDIS device
00412  * @ret rc              Return status code
00413  */
00414 static int rndis_halt ( struct rndis_device *rndis ) {
00415         int rc;
00416 
00417         /* Transmit halt message */
00418         if ( ( rc = rndis_tx_halt ( rndis ) ) != 0 )
00419                 return rc;
00420 
00421         return 0;
00422 }
00423 
00424 /**
00425  * Transmit OID message
00426  *
00427  * @v rndis             RNDIS device
00428  * @v oid               Object ID
00429  * @v data              New OID value (or NULL to query current value)
00430  * @v len               Length of new OID value
00431  * @ret rc              Return status code
00432  */
00433 static int rndis_tx_oid ( struct rndis_device *rndis, unsigned int oid,
00434                           const void *data, size_t len ) {
00435         struct io_buffer *iobuf;
00436         struct rndis_oid_message *msg;
00437         unsigned int type;
00438         int rc;
00439 
00440         /* Allocate I/O buffer */
00441         iobuf = rndis_alloc_iob ( sizeof ( *msg ) + len );
00442         if ( ! iobuf ) {
00443                 rc = -ENOMEM;
00444                 goto err_alloc;
00445         }
00446 
00447         /* Construct message.  We use the OID as the request ID. */
00448         msg = iob_put ( iobuf, sizeof ( *msg ) );
00449         memset ( msg, 0, sizeof ( *msg ) );
00450         msg->id = oid; /* Non-endian */
00451         msg->oid = cpu_to_le32 ( oid );
00452         msg->offset = cpu_to_le32 ( sizeof ( *msg ) );
00453         msg->len = cpu_to_le32 ( len );
00454         memcpy ( iob_put ( iobuf, len ), data, len );
00455 
00456         /* Transmit message */
00457         type = ( data ? RNDIS_SET_MSG : RNDIS_QUERY_MSG );
00458         if ( ( rc = rndis_tx_message ( rndis, iobuf, type ) ) != 0 )
00459                 goto err_tx;
00460 
00461         return 0;
00462 
00463  err_tx:
00464         free_iob ( iobuf );
00465  err_alloc:
00466         return rc;
00467 }
00468 
00469 /**
00470  * Receive query OID completion
00471  *
00472  * @v rndis             RNDIS device
00473  * @v iobuf             I/O buffer
00474  */
00475 static void rndis_rx_query_oid ( struct rndis_device *rndis,
00476                                  struct io_buffer *iobuf ) {
00477         struct net_device *netdev = rndis->netdev;
00478         struct rndis_query_completion *cmplt;
00479         size_t len = iob_len ( iobuf );
00480         size_t info_offset;
00481         size_t info_len;
00482         unsigned int id;
00483         void *info;
00484         uint32_t *link_status;
00485         int rc;
00486 
00487         /* Sanity check */
00488         if ( len < sizeof ( *cmplt ) ) {
00489                 DBGC ( rndis, "RNDIS %s received underlength query "
00490                        "completion:\n", rndis->name );
00491                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00492                 rc = -EINVAL;
00493                 goto err_len;
00494         }
00495         cmplt = iobuf->data;
00496 
00497         /* Extract request ID */
00498         id = cmplt->id; /* Non-endian */
00499 
00500         /* Check status */
00501         if ( cmplt->status ) {
00502                 DBGC ( rndis, "RNDIS %s received query completion failure "
00503                        "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
00504                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00505                 rc = -EIO;
00506                 goto err_status;
00507         }
00508 
00509         /* Locate and sanity check information buffer */
00510         info_offset = le32_to_cpu ( cmplt->offset );
00511         info_len = le32_to_cpu ( cmplt->len );
00512         if ( ( info_offset > len ) || ( info_len > ( len - info_offset ) ) ) {
00513                 DBGC ( rndis, "RNDIS %s query completion information exceeds "
00514                        "packet:\n", rndis->name );
00515                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00516                 rc = -EINVAL;
00517                 goto err_info;
00518         }
00519         info = ( ( ( void * ) cmplt ) + info_offset );
00520 
00521         /* Handle OID */
00522         switch ( id ) {
00523 
00524         case RNDIS_OID_802_3_PERMANENT_ADDRESS:
00525                 if ( info_len > sizeof ( netdev->hw_addr ) )
00526                         info_len = sizeof ( netdev->hw_addr );
00527                 memcpy ( netdev->hw_addr, info, info_len );
00528                 break;
00529 
00530         case RNDIS_OID_802_3_CURRENT_ADDRESS:
00531                 if ( info_len > sizeof ( netdev->ll_addr ) )
00532                         info_len = sizeof ( netdev->ll_addr );
00533                 memcpy ( netdev->ll_addr, info, info_len );
00534                 break;
00535 
00536         case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
00537                 if ( info_len != sizeof ( *link_status ) ) {
00538                         DBGC ( rndis, "RNDIS %s invalid link status:\n",
00539                                rndis->name );
00540                         DBGC_HDA ( rndis, 0, iobuf->data, len );
00541                         rc = -EPROTO;
00542                         goto err_link_status;
00543                 }
00544                 link_status = info;
00545                 if ( *link_status == 0 ) {
00546                         DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
00547                         netdev_link_up ( netdev );
00548                 } else {
00549                         DBGC ( rndis, "RNDIS %s link is down: %#08x\n",
00550                                rndis->name, le32_to_cpu ( *link_status ) );
00551                         netdev_link_down ( netdev );
00552                 }
00553                 break;
00554 
00555         default:
00556                 DBGC ( rndis, "RNDIS %s unexpected query completion ID %#08x\n",
00557                        rndis->name, id );
00558                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00559                 rc = -EPROTO;
00560                 goto err_id;
00561         }
00562 
00563         /* Success */
00564         rc = 0;
00565 
00566  err_id:
00567  err_link_status:
00568  err_info:
00569  err_status:
00570         /* Record completion result if applicable */
00571         if ( id == rndis->wait_id ) {
00572                 rndis->wait_id = 0;
00573                 rndis->wait_rc = rc;
00574         }
00575  err_len:
00576         /* Free I/O buffer */
00577         free_iob ( iobuf );
00578 }
00579 
00580 /**
00581  * Receive set OID completion
00582  *
00583  * @v rndis             RNDIS device
00584  * @v iobuf             I/O buffer
00585  */
00586 static void rndis_rx_set_oid ( struct rndis_device *rndis,
00587                                struct io_buffer *iobuf ) {
00588         struct rndis_set_completion *cmplt;
00589         size_t len = iob_len ( iobuf );
00590         unsigned int id;
00591         int rc;
00592 
00593         /* Sanity check */
00594         if ( len < sizeof ( *cmplt ) ) {
00595                 DBGC ( rndis, "RNDIS %s received underlength set completion:\n",
00596                        rndis->name );
00597                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00598                 rc = -EINVAL;
00599                 goto err_len;
00600         }
00601         cmplt = iobuf->data;
00602 
00603         /* Extract request ID */
00604         id = cmplt->id; /* Non-endian */
00605 
00606         /* Check status */
00607         if ( cmplt->status ) {
00608                 DBGC ( rndis, "RNDIS %s received set completion failure "
00609                        "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
00610                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00611                 rc = -EIO;
00612                 goto err_status;
00613         }
00614 
00615         /* Success */
00616         rc = 0;
00617 
00618  err_status:
00619         /* Record completion result if applicable */
00620         if ( id == rndis->wait_id ) {
00621                 rndis->wait_id = 0;
00622                 rndis->wait_rc = rc;
00623         }
00624  err_len:
00625         /* Free I/O buffer */
00626         free_iob ( iobuf );
00627 }
00628 
00629 /**
00630  * Query or set OID
00631  *
00632  * @v rndis             RNDIS device
00633  * @v oid               Object ID
00634  * @v data              New OID value (or NULL to query current value)
00635  * @v len               Length of new OID value
00636  * @ret rc              Return status code
00637  */
00638 static int rndis_oid ( struct rndis_device *rndis, unsigned int oid,
00639                        const void *data, size_t len ) {
00640         int rc;
00641 
00642         /* Transmit query */
00643         if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 )
00644                 return rc;
00645 
00646         /* Wait for response */
00647         if ( ( rc = rndis_wait ( rndis, oid ) ) != 0 )
00648                 return rc;
00649 
00650         return 0;
00651 }
00652 
00653 /**
00654  * Describe RNDIS device
00655  *
00656  * @v rndis             RNDIS device
00657  * @ret rc              Return status code
00658  */
00659 static int rndis_describe ( struct rndis_device *rndis ) {
00660         struct net_device *netdev = rndis->netdev;
00661         int rc;
00662 
00663         /* Assign device name (for debugging) */
00664         rndis->name = netdev->dev->name;
00665 
00666         /* Open RNDIS device to read MAC addresses */
00667         if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
00668                 DBGC ( rndis, "RNDIS %s could not open: %s\n",
00669                        rndis->name, strerror ( rc ) );
00670                 goto err_open;
00671         }
00672 
00673         /* Initialise RNDIS */
00674         if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
00675                 goto err_initialise;
00676 
00677         /* Query permanent MAC address */
00678         if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS,
00679                                 NULL, 0 ) ) != 0 )
00680                 goto err_query_permanent;
00681 
00682         /* Query current MAC address */
00683         if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS,
00684                                 NULL, 0 ) ) != 0 )
00685                 goto err_query_current;
00686 
00687         /* Get link status */
00688         if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
00689                                 NULL, 0 ) ) != 0 )
00690                 goto err_query_link;
00691 
00692         /* Halt RNDIS device */
00693         rndis_halt ( rndis );
00694 
00695         /* Close RNDIS device */
00696         rndis->op->close ( rndis );
00697 
00698         return 0;
00699 
00700  err_query_link:
00701  err_query_current:
00702  err_query_permanent:
00703         rndis_halt ( rndis );
00704  err_initialise:
00705         rndis->op->close ( rndis );
00706  err_open:
00707         return rc;
00708 }
00709 
00710 /**
00711  * Receive indicate status message
00712  *
00713  * @v rndis             RNDIS device
00714  * @v iobuf             I/O buffer
00715  */
00716 static void rndis_rx_status ( struct rndis_device *rndis,
00717                               struct io_buffer *iobuf ) {
00718         struct net_device *netdev = rndis->netdev;
00719         struct rndis_indicate_status_message *msg;
00720         size_t len = iob_len ( iobuf );
00721         unsigned int status;
00722         int rc;
00723 
00724         /* Sanity check */
00725         if ( len < sizeof ( *msg ) ) {
00726                 DBGC ( rndis, "RNDIS %s received underlength status message:\n",
00727                        rndis->name );
00728                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00729                 rc = -EINVAL;
00730                 goto err_len;
00731         }
00732         msg = iobuf->data;
00733 
00734         /* Extract status */
00735         status = le32_to_cpu ( msg->status );
00736 
00737         /* Handle status */
00738         switch ( msg->status ) {
00739 
00740         case RNDIS_STATUS_MEDIA_CONNECT:
00741                 DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
00742                 netdev_link_up ( netdev );
00743                 break;
00744 
00745         case RNDIS_STATUS_MEDIA_DISCONNECT:
00746                 DBGC ( rndis, "RNDIS %s link is down\n", rndis->name );
00747                 netdev_link_down ( netdev );
00748                 break;
00749 
00750         case RNDIS_STATUS_WTF_WORLD:
00751                 /* Ignore */
00752                 break;
00753 
00754         default:
00755                 DBGC ( rndis, "RNDIS %s unexpected status %#08x:\n",
00756                        rndis->name, status );
00757                 DBGC_HDA ( rndis, 0, iobuf->data, len );
00758                 rc = -ENOTSUP;
00759                 goto err_status;
00760         }
00761 
00762         /* Free I/O buffer */
00763         free_iob ( iobuf );
00764 
00765         return;
00766 
00767  err_status:
00768  err_len:
00769         /* Report error via network device statistics */
00770         netdev_rx_err ( netdev, iobuf, rc );
00771 }
00772 
00773 /**
00774  * Receive RNDIS message
00775  *
00776  * @v rndis             RNDIS device
00777  * @v iobuf             I/O buffer
00778  * @v type              Message type
00779  */
00780 static void rndis_rx_message ( struct rndis_device *rndis,
00781                                struct io_buffer *iobuf, unsigned int type ) {
00782         struct net_device *netdev = rndis->netdev;
00783         int rc;
00784 
00785         /* Handle packet */
00786         switch ( type ) {
00787 
00788         case RNDIS_PACKET_MSG:
00789                 rndis_rx_data ( rndis, iob_disown ( iobuf ) );
00790                 break;
00791 
00792         case RNDIS_INITIALISE_CMPLT:
00793                 rndis_rx_initialise ( rndis, iob_disown ( iobuf ) );
00794                 break;
00795 
00796         case RNDIS_QUERY_CMPLT:
00797                 rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) );
00798                 break;
00799 
00800         case RNDIS_SET_CMPLT:
00801                 rndis_rx_set_oid ( rndis, iob_disown ( iobuf ) );
00802                 break;
00803 
00804         case RNDIS_INDICATE_STATUS_MSG:
00805                 rndis_rx_status ( rndis, iob_disown ( iobuf ) );
00806                 break;
00807 
00808         default:
00809                 DBGC ( rndis, "RNDIS %s received unexpected type %#08x\n",
00810                        rndis->name, type );
00811                 DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
00812                 rc = -EPROTO;
00813                 goto err_type;
00814         }
00815 
00816         return;
00817 
00818  err_type:
00819         /* Report error via network device statistics */
00820         netdev_rx_err ( netdev, iobuf, rc );
00821 }
00822 
00823 /**
00824  * Receive packet from underlying transport layer
00825  *
00826  * @v rndis             RNDIS device
00827  * @v iobuf             I/O buffer
00828  */
00829 void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
00830         struct net_device *netdev = rndis->netdev;
00831         struct rndis_header *header;
00832         unsigned int type;
00833         int rc;
00834 
00835         /* Sanity check */
00836         if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
00837                 DBGC ( rndis, "RNDIS %s received underlength packet:\n",
00838                        rndis->name );
00839                 DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
00840                 rc = -EINVAL;
00841                 goto drop;
00842         }
00843         header = iobuf->data;
00844 
00845         /* Parse and strip header */
00846         type = le32_to_cpu ( header->type );
00847         iob_pull ( iobuf, sizeof ( *header ) );
00848 
00849         /* Handle message */
00850         rndis_rx_message ( rndis, iob_disown ( iobuf ), type );
00851 
00852         return;
00853 
00854  drop:
00855         /* Record error */
00856         netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
00857 }
00858 
00859 /**
00860  * Discard packet from underlying transport layer
00861  *
00862  * @v rndis             RNDIS device
00863  * @v iobuf             I/O buffer
00864  * @v rc                Packet status code
00865  */
00866 void rndis_rx_err ( struct rndis_device *rndis, struct io_buffer *iobuf,
00867                     int rc ) {
00868         struct net_device *netdev = rndis->netdev;
00869 
00870         /* Record error */
00871         netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
00872 }
00873 
00874 /**
00875  * Set receive filter
00876  *
00877  * @v rndis             RNDIS device
00878  * @v filter            Receive filter
00879  * @ret rc              Return status code
00880  */
00881 static int rndis_filter ( struct rndis_device *rndis, unsigned int filter ) {
00882         uint32_t value = cpu_to_le32 ( filter );
00883         int rc;
00884 
00885         /* Set receive filter */
00886         if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
00887                                 &value, sizeof ( value ) ) ) != 0 ) {
00888                 DBGC ( rndis, "RNDIS %s could not set receive filter to %#08x: "
00889                        "%s\n", rndis->name, filter, strerror ( rc ) );
00890                 return rc;
00891         }
00892 
00893         return 0;
00894 }
00895 
00896 /**
00897  * Open network device
00898  *
00899  * @v netdev            Network device
00900  * @ret rc              Return status code
00901  */
00902 static int rndis_open ( struct net_device *netdev ) {
00903         struct rndis_device *rndis = netdev->priv;
00904         int rc;
00905 
00906         /* Open RNDIS device */
00907         if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
00908                 DBGC ( rndis, "RNDIS %s could not open: %s\n",
00909                        rndis->name, strerror ( rc ) );
00910                 goto err_open;
00911         }
00912 
00913         /* Initialise RNDIS */
00914         if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
00915                 goto err_initialise;
00916 
00917         /* Set receive filter */
00918         if ( ( rc = rndis_filter ( rndis, ( RNDIS_FILTER_UNICAST |
00919                                             RNDIS_FILTER_MULTICAST |
00920                                             RNDIS_FILTER_ALL_MULTICAST |
00921                                             RNDIS_FILTER_BROADCAST |
00922                                             RNDIS_FILTER_PROMISCUOUS ) ) ) != 0)
00923                 goto err_set_filter;
00924 
00925         /* Update link status */
00926         if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
00927                                 NULL, 0 ) ) != 0 )
00928                 goto err_query_link;
00929 
00930         return 0;
00931 
00932  err_query_link:
00933  err_set_filter:
00934         rndis_halt ( rndis );
00935  err_initialise:
00936         rndis->op->close ( rndis );
00937  err_open:
00938         return rc;
00939 }
00940 
00941 /**
00942  * Close network device
00943  *
00944  * @v netdev            Network device
00945  */
00946 static void rndis_close ( struct net_device *netdev ) {
00947         struct rndis_device *rndis = netdev->priv;
00948 
00949         /* Clear receive filter */
00950         rndis_filter ( rndis, 0 );
00951 
00952         /* Halt RNDIS device */
00953         rndis_halt ( rndis );
00954 
00955         /* Close RNDIS device */
00956         rndis->op->close ( rndis );
00957 }
00958 
00959 /**
00960  * Transmit packet
00961  *
00962  * @v netdev            Network device
00963  * @v iobuf             I/O buffer
00964  * @ret rc              Return status code
00965  */
00966 static int rndis_transmit ( struct net_device *netdev,
00967                             struct io_buffer *iobuf ) {
00968         struct rndis_device *rndis = netdev->priv;
00969 
00970         /* Transmit data packet */
00971         return rndis_tx_data ( rndis, iobuf );
00972 }
00973 
00974 /**
00975  * Poll for completed and received packets
00976  *
00977  * @v netdev            Network device
00978  */
00979 static void rndis_poll ( struct net_device *netdev ) {
00980         struct rndis_device *rndis = netdev->priv;
00981 
00982         /* Poll RNDIS device */
00983         rndis->op->poll ( rndis );
00984 }
00985 
00986 /** Network device operations */
00987 static struct net_device_operations rndis_operations = {
00988         .open           = rndis_open,
00989         .close          = rndis_close,
00990         .transmit       = rndis_transmit,
00991         .poll           = rndis_poll,
00992 };
00993 
00994 /**
00995  * Allocate RNDIS device
00996  *
00997  * @v priv_len          Length of private data
00998  * @ret rndis           RNDIS device, or NULL on allocation failure
00999  */
01000 struct rndis_device * alloc_rndis ( size_t priv_len ) {
01001         struct net_device *netdev;
01002         struct rndis_device *rndis;
01003 
01004         /* Allocate and initialise structure */
01005         netdev = alloc_etherdev ( sizeof ( *rndis ) + priv_len );
01006         if ( ! netdev )
01007                 return NULL;
01008         netdev_init ( netdev, &rndis_operations );
01009         rndis = netdev->priv;
01010         rndis->netdev = netdev;
01011         rndis->priv = ( ( ( void * ) rndis ) + sizeof ( *rndis ) );
01012 
01013         return rndis;
01014 }
01015 
01016 /**
01017  * Register RNDIS device
01018  *
01019  * @v rndis             RNDIS device
01020  * @ret rc              Return status code
01021  *
01022  * Note that this routine will open and use the RNDIS device in order
01023  * to query the MAC address.  The device must be immediately ready for
01024  * use prior to registration.
01025  */
01026 int register_rndis ( struct rndis_device *rndis ) {
01027         struct net_device *netdev = rndis->netdev;
01028         int rc;
01029 
01030         /* Describe RNDIS device */
01031         if ( ( rc = rndis_describe ( rndis ) ) != 0 )
01032                 goto err_describe;
01033 
01034         /* Register network device */
01035         if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
01036                 DBGC ( rndis, "RNDIS %s could not register: %s\n",
01037                        rndis->name, strerror ( rc ) );
01038                 goto err_register;
01039         }
01040 
01041         return 0;
01042 
01043         unregister_netdev ( netdev );
01044  err_register:
01045  err_describe:
01046         return rc;
01047 }
01048 
01049 /**
01050  * Unregister RNDIS device
01051  *
01052  * @v rndis             RNDIS device
01053  */
01054 void unregister_rndis ( struct rndis_device *rndis ) {
01055         struct net_device *netdev = rndis->netdev;
01056 
01057         /* Unregister network device */
01058         unregister_netdev ( netdev );
01059 }
01060 
01061 /**
01062  * Free RNDIS device
01063  *
01064  * @v rndis             RNDIS device
01065  */
01066 void free_rndis ( struct rndis_device *rndis ) {
01067         struct net_device *netdev = rndis->netdev;
01068 
01069         /* Free network device */
01070         netdev_nullify ( netdev );
01071         netdev_put ( netdev );
01072 }