2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
28 * Remote Network Driver Interface Specification
36 #include <ipxe/iobuf.h>
37 #include <ipxe/netdevice.h>
38 #include <ipxe/ethernet.h>
39 #include <ipxe/device.h>
40 #include <ipxe/rndis.h>
46 * @ret iobuf I/O buffer, or NULL
48 static struct io_buffer * rndis_alloc_iob ( size_t len ) {
49 struct rndis_header *header;
50 struct io_buffer *iobuf;
52 /* Allocate I/O buffer and reserve space */
53 iobuf = alloc_iob ( sizeof ( *header ) + len );
55 iob_reserve ( iobuf, sizeof ( *header ) );
63 * @v rndis RNDIS device
64 * @v wait_id Request ID
65 * @ret rc Return status code
67 static int rndis_wait ( struct rndis_device *rndis, unsigned int wait_id ) {
71 rndis->wait_id = wait_id;
73 /* Wait for operation to complete */
74 for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) {
76 /* Check for completion */
77 if ( ! rndis->wait_id )
78 return rndis->wait_rc;
80 /* Poll RNDIS device */
81 rndis->op->poll ( rndis );
87 DBGC ( rndis, "RNDIS %s timed out waiting for ID %#08x\n",
88 rndis->name, wait_id );
95 * @v rndis RNDIS device
97 * @v type Message type
98 * @ret rc Return status code
100 static int rndis_tx_message ( struct rndis_device *rndis,
101 struct io_buffer *iobuf, unsigned int type ) {
102 struct rndis_header *header;
105 /* Prepend RNDIS header */
106 header = iob_push ( iobuf, sizeof ( *header ) );
107 header->type = cpu_to_le32 ( type );
108 header->len = cpu_to_le32 ( iob_len ( iobuf ) );
110 /* Transmit message */
111 if ( ( rc = rndis->op->transmit ( rndis, iobuf ) ) != 0 ) {
112 DBGC ( rndis, "RNDIS %s could not transmit: %s\n",
113 rndis->name, strerror ( rc ) );
121 * Complete message transmission
123 * @v rndis RNDIS device
124 * @v iobuf I/O buffer
125 * @v rc Packet status code
127 void rndis_tx_complete_err ( struct rndis_device *rndis,
128 struct io_buffer *iobuf, int rc ) {
129 struct net_device *netdev = rndis->netdev;
130 struct rndis_header *header;
131 size_t len = iob_len ( iobuf );
134 if ( len < sizeof ( *header ) ) {
135 DBGC ( rndis, "RNDIS %s completed underlength transmission:\n",
137 DBGC_HDA ( rndis, 0, iobuf->data, len );
138 netdev_tx_err ( netdev, NULL, -EINVAL );
141 header = iobuf->data;
143 /* Complete buffer */
144 if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) {
145 netdev_tx_complete_err ( netdev, iobuf, rc );
152 * Transmit data packet
154 * @v rndis RNDIS device
155 * @v iobuf I/O buffer
156 * @ret rc Return status code
158 static int rndis_tx_data ( struct rndis_device *rndis,
159 struct io_buffer *iobuf ) {
160 struct rndis_packet_message *msg;
161 size_t len = iob_len ( iobuf );
164 /* Prepend packet message header */
165 msg = iob_push ( iobuf, sizeof ( *msg ) );
166 memset ( msg, 0, sizeof ( *msg ) );
167 msg->data.offset = cpu_to_le32 ( sizeof ( *msg ) );
168 msg->data.len = cpu_to_le32 ( len );
170 /* Transmit message */
171 if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_PACKET_MSG ) ) != 0 )
178 * Defer transmitted packet
180 * @v rndis RNDIS device
181 * @v iobuf I/O buffer
182 * @ret rc Return status code
184 * As with netdev_tx_defer(), the caller must ensure that space in the
185 * transmit descriptor ring is freed up before calling
186 * rndis_tx_complete().
188 * Unlike netdev_tx_defer(), this call may fail.
190 int rndis_tx_defer ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
191 struct net_device *netdev = rndis->netdev;
192 struct rndis_header *header;
193 struct rndis_packet_message *msg;
195 /* Fail unless this was a packet message. Only packet
196 * messages correspond to I/O buffers in the network device's
197 * TX queue; other messages cannot be deferred in this way.
199 assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
200 header = iobuf->data;
201 if ( header->type != cpu_to_le32 ( RNDIS_PACKET_MSG ) )
204 /* Strip RNDIS header and packet message header, to return
205 * this packet to the state in which we received it.
207 iob_pull ( iobuf, ( sizeof ( *header ) + sizeof ( *msg ) ) );
210 netdev_tx_defer ( netdev, iobuf );
216 * Receive data packet
218 * @v rndis RNDIS device
219 * @v iobuf I/O buffer
221 static void rndis_rx_data ( struct rndis_device *rndis,
222 struct io_buffer *iobuf ) {
223 struct net_device *netdev = rndis->netdev;
224 struct rndis_packet_message *msg;
225 size_t len = iob_len ( iobuf );
231 if ( len < sizeof ( *msg ) ) {
232 DBGC ( rndis, "RNDIS %s received underlength data packet:\n",
234 DBGC_HDA ( rndis, 0, iobuf->data, len );
240 /* Locate and sanity check data buffer */
241 data_offset = le32_to_cpu ( msg->data.offset );
242 data_len = le32_to_cpu ( msg->data.len );
243 if ( ( data_offset > len ) || ( data_len > ( len - data_offset ) ) ) {
244 DBGC ( rndis, "RNDIS %s data packet data exceeds packet:\n",
246 DBGC_HDA ( rndis, 0, iobuf->data, len );
251 /* Strip non-data portions */
252 iob_pull ( iobuf, data_offset );
253 iob_unput ( iobuf, ( iob_len ( iobuf ) - data_len ) );
255 /* Hand off to network stack */
256 netdev_rx ( netdev, iob_disown ( iobuf ) );
262 /* Report error to network stack */
263 netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
267 * Transmit initialisation message
269 * @v rndis RNDIS device
271 * @ret rc Return status code
273 static int rndis_tx_initialise ( struct rndis_device *rndis, unsigned int id ) {
274 struct io_buffer *iobuf;
275 struct rndis_initialise_message *msg;
278 /* Allocate I/O buffer */
279 iobuf = rndis_alloc_iob ( sizeof ( *msg ) );
285 /* Construct message */
286 msg = iob_put ( iobuf, sizeof ( *msg ) );
287 memset ( msg, 0, sizeof ( *msg ) );
288 msg->id = id; /* Non-endian */
289 msg->major = cpu_to_le32 ( RNDIS_VERSION_MAJOR );
290 msg->minor = cpu_to_le32 ( RNDIS_VERSION_MINOR );
291 msg->mtu = cpu_to_le32 ( RNDIS_MTU );
293 /* Transmit message */
294 if ( ( rc = rndis_tx_message ( rndis, iobuf,
295 RNDIS_INITIALISE_MSG ) ) != 0 )
307 * Receive initialisation completion
309 * @v rndis RNDIS device
310 * @v iobuf I/O buffer
312 static void rndis_rx_initialise ( struct rndis_device *rndis,
313 struct io_buffer *iobuf ) {
314 struct rndis_initialise_completion *cmplt;
315 size_t len = iob_len ( iobuf );
320 if ( len < sizeof ( *cmplt ) ) {
321 DBGC ( rndis, "RNDIS %s received underlength initialisation "
322 "completion:\n", rndis->name );
323 DBGC_HDA ( rndis, 0, iobuf->data, len );
329 /* Extract request ID */
330 id = cmplt->id; /* Non-endian */
333 if ( cmplt->status ) {
334 DBGC ( rndis, "RNDIS %s received initialisation completion "
335 "failure %#08x\n", rndis->name,
336 le32_to_cpu ( cmplt->status ) );
345 /* Record completion result if applicable */
346 if ( id == rndis->wait_id ) {
357 * @v rndis RNDIS device
358 * @ret rc Return status code
360 static int rndis_initialise ( struct rndis_device *rndis ) {
363 /* Transmit initialisation message */
364 if ( ( rc = rndis_tx_initialise ( rndis, RNDIS_INIT_ID ) ) != 0 )
367 /* Wait for response */
368 if ( ( rc = rndis_wait ( rndis, RNDIS_INIT_ID ) ) != 0 )
375 * Transmit halt message
377 * @v rndis RNDIS device
378 * @ret rc Return status code
380 static int rndis_tx_halt ( struct rndis_device *rndis ) {
381 struct io_buffer *iobuf;
382 struct rndis_halt_message *msg;
385 /* Allocate I/O buffer */
386 iobuf = rndis_alloc_iob ( sizeof ( *msg ) );
392 /* Construct message */
393 msg = iob_put ( iobuf, sizeof ( *msg ) );
394 memset ( msg, 0, sizeof ( *msg ) );
396 /* Transmit message */
397 if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_HALT_MSG ) ) != 0 )
411 * @v rndis RNDIS device
412 * @ret rc Return status code
414 static int rndis_halt ( struct rndis_device *rndis ) {
417 /* Transmit halt message */
418 if ( ( rc = rndis_tx_halt ( rndis ) ) != 0 )
425 * Transmit OID message
427 * @v rndis RNDIS device
429 * @v data New OID value (or NULL to query current value)
430 * @v len Length of new OID value
431 * @ret rc Return status code
433 static int rndis_tx_oid ( struct rndis_device *rndis, unsigned int oid,
434 const void *data, size_t len ) {
435 struct io_buffer *iobuf;
436 struct rndis_oid_message *msg;
440 /* Allocate I/O buffer */
441 iobuf = rndis_alloc_iob ( sizeof ( *msg ) + len );
447 /* Construct message. We use the OID as the request ID. */
448 msg = iob_put ( iobuf, sizeof ( *msg ) );
449 memset ( msg, 0, sizeof ( *msg ) );
450 msg->id = oid; /* Non-endian */
451 msg->oid = cpu_to_le32 ( oid );
452 msg->offset = cpu_to_le32 ( sizeof ( *msg ) );
453 msg->len = cpu_to_le32 ( len );
454 memcpy ( iob_put ( iobuf, len ), data, len );
456 /* Transmit message */
457 type = ( data ? RNDIS_SET_MSG : RNDIS_QUERY_MSG );
458 if ( ( rc = rndis_tx_message ( rndis, iobuf, type ) ) != 0 )
470 * Receive query OID completion
472 * @v rndis RNDIS device
473 * @v iobuf I/O buffer
475 static void rndis_rx_query_oid ( struct rndis_device *rndis,
476 struct io_buffer *iobuf ) {
477 struct net_device *netdev = rndis->netdev;
478 struct rndis_query_completion *cmplt;
479 size_t len = iob_len ( iobuf );
484 uint32_t *link_status;
488 if ( len < sizeof ( *cmplt ) ) {
489 DBGC ( rndis, "RNDIS %s received underlength query "
490 "completion:\n", rndis->name );
491 DBGC_HDA ( rndis, 0, iobuf->data, len );
497 /* Extract request ID */
498 id = cmplt->id; /* Non-endian */
501 if ( cmplt->status ) {
502 DBGC ( rndis, "RNDIS %s received query completion failure "
503 "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
504 DBGC_HDA ( rndis, 0, iobuf->data, len );
509 /* Locate and sanity check information buffer */
510 info_offset = le32_to_cpu ( cmplt->offset );
511 info_len = le32_to_cpu ( cmplt->len );
512 if ( ( info_offset > len ) || ( info_len > ( len - info_offset ) ) ) {
513 DBGC ( rndis, "RNDIS %s query completion information exceeds "
514 "packet:\n", rndis->name );
515 DBGC_HDA ( rndis, 0, iobuf->data, len );
519 info = ( ( ( void * ) cmplt ) + info_offset );
524 case RNDIS_OID_802_3_PERMANENT_ADDRESS:
525 if ( info_len > sizeof ( netdev->hw_addr ) )
526 info_len = sizeof ( netdev->hw_addr );
527 memcpy ( netdev->hw_addr, info, info_len );
530 case RNDIS_OID_802_3_CURRENT_ADDRESS:
531 if ( info_len > sizeof ( netdev->ll_addr ) )
532 info_len = sizeof ( netdev->ll_addr );
533 memcpy ( netdev->ll_addr, info, info_len );
536 case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
537 if ( info_len != sizeof ( *link_status ) ) {
538 DBGC ( rndis, "RNDIS %s invalid link status:\n",
540 DBGC_HDA ( rndis, 0, iobuf->data, len );
542 goto err_link_status;
545 if ( *link_status == 0 ) {
546 DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
547 netdev_link_up ( netdev );
549 DBGC ( rndis, "RNDIS %s link is down: %#08x\n",
550 rndis->name, le32_to_cpu ( *link_status ) );
551 netdev_link_down ( netdev );
556 DBGC ( rndis, "RNDIS %s unexpected query completion ID %#08x\n",
558 DBGC_HDA ( rndis, 0, iobuf->data, len );
570 /* Record completion result if applicable */
571 if ( id == rndis->wait_id ) {
576 /* Free I/O buffer */
581 * Receive set OID completion
583 * @v rndis RNDIS device
584 * @v iobuf I/O buffer
586 static void rndis_rx_set_oid ( struct rndis_device *rndis,
587 struct io_buffer *iobuf ) {
588 struct rndis_set_completion *cmplt;
589 size_t len = iob_len ( iobuf );
594 if ( len < sizeof ( *cmplt ) ) {
595 DBGC ( rndis, "RNDIS %s received underlength set completion:\n",
597 DBGC_HDA ( rndis, 0, iobuf->data, len );
603 /* Extract request ID */
604 id = cmplt->id; /* Non-endian */
607 if ( cmplt->status ) {
608 DBGC ( rndis, "RNDIS %s received set completion failure "
609 "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
610 DBGC_HDA ( rndis, 0, iobuf->data, len );
619 /* Record completion result if applicable */
620 if ( id == rndis->wait_id ) {
625 /* Free I/O buffer */
632 * @v rndis RNDIS device
634 * @v data New OID value (or NULL to query current value)
635 * @v len Length of new OID value
636 * @ret rc Return status code
638 static int rndis_oid ( struct rndis_device *rndis, unsigned int oid,
639 const void *data, size_t len ) {
643 if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 )
646 /* Wait for response */
647 if ( ( rc = rndis_wait ( rndis, oid ) ) != 0 )
654 * Receive indicate status message
656 * @v rndis RNDIS device
657 * @v iobuf I/O buffer
659 static void rndis_rx_status ( struct rndis_device *rndis,
660 struct io_buffer *iobuf ) {
661 struct net_device *netdev = rndis->netdev;
662 struct rndis_indicate_status_message *msg;
663 size_t len = iob_len ( iobuf );
668 if ( len < sizeof ( *msg ) ) {
669 DBGC ( rndis, "RNDIS %s received underlength status message:\n",
671 DBGC_HDA ( rndis, 0, iobuf->data, len );
678 status = le32_to_cpu ( msg->status );
681 switch ( msg->status ) {
683 case RNDIS_STATUS_MEDIA_CONNECT:
684 DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
685 netdev_link_up ( netdev );
688 case RNDIS_STATUS_MEDIA_DISCONNECT:
689 DBGC ( rndis, "RNDIS %s link is down\n", rndis->name );
690 netdev_link_down ( netdev );
693 case RNDIS_STATUS_WTF_WORLD:
698 DBGC ( rndis, "RNDIS %s unexpected status %#08x:\n",
699 rndis->name, status );
700 DBGC_HDA ( rndis, 0, iobuf->data, len );
705 /* Free I/O buffer */
712 /* Report error via network device statistics */
713 netdev_rx_err ( netdev, iobuf, rc );
717 * Receive RNDIS message
719 * @v rndis RNDIS device
720 * @v iobuf I/O buffer
721 * @v type Message type
723 static void rndis_rx_message ( struct rndis_device *rndis,
724 struct io_buffer *iobuf, unsigned int type ) {
725 struct net_device *netdev = rndis->netdev;
731 case RNDIS_PACKET_MSG:
732 rndis_rx_data ( rndis, iob_disown ( iobuf ) );
735 case RNDIS_INITIALISE_CMPLT:
736 rndis_rx_initialise ( rndis, iob_disown ( iobuf ) );
739 case RNDIS_QUERY_CMPLT:
740 rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) );
743 case RNDIS_SET_CMPLT:
744 rndis_rx_set_oid ( rndis, iob_disown ( iobuf ) );
747 case RNDIS_INDICATE_STATUS_MSG:
748 rndis_rx_status ( rndis, iob_disown ( iobuf ) );
752 DBGC ( rndis, "RNDIS %s received unexpected type %#08x\n",
754 DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
762 /* Report error via network device statistics */
763 netdev_rx_err ( netdev, iobuf, rc );
767 * Receive packet from underlying transport layer
769 * @v rndis RNDIS device
770 * @v iobuf I/O buffer
772 void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
773 struct net_device *netdev = rndis->netdev;
774 struct rndis_header *header;
779 if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
780 DBGC ( rndis, "RNDIS %s received underlength packet:\n",
782 DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
786 header = iobuf->data;
788 /* Parse and strip header */
789 type = le32_to_cpu ( header->type );
790 iob_pull ( iobuf, sizeof ( *header ) );
793 rndis_rx_message ( rndis, iob_disown ( iobuf ), type );
799 netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
803 * Discard packet from underlying transport layer
805 * @v rndis RNDIS device
806 * @v iobuf I/O buffer
807 * @v rc Packet status code
809 void rndis_rx_err ( struct rndis_device *rndis, struct io_buffer *iobuf,
811 struct net_device *netdev = rndis->netdev;
814 netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
820 * @v rndis RNDIS device
821 * @v filter Receive filter
822 * @ret rc Return status code
824 static int rndis_filter ( struct rndis_device *rndis, unsigned int filter ) {
825 uint32_t value = cpu_to_le32 ( filter );
828 /* Set receive filter */
829 if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
830 &value, sizeof ( value ) ) ) != 0 ) {
831 DBGC ( rndis, "RNDIS %s could not set receive filter to %#08x: "
832 "%s\n", rndis->name, filter, strerror ( rc ) );
840 * Open network device
842 * @v netdev Network device
843 * @ret rc Return status code
845 static int rndis_open ( struct net_device *netdev ) {
846 struct rndis_device *rndis = netdev->priv;
849 /* Open RNDIS device */
850 if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
851 DBGC ( rndis, "RNDIS %s could not open: %s\n",
852 rndis->name, strerror ( rc ) );
856 /* Initialise RNDIS */
857 if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
860 /* Set receive filter */
861 if ( ( rc = rndis_filter ( rndis, ( RNDIS_FILTER_UNICAST |
862 RNDIS_FILTER_MULTICAST |
863 RNDIS_FILTER_ALL_MULTICAST |
864 RNDIS_FILTER_BROADCAST |
865 RNDIS_FILTER_PROMISCUOUS ) ) ) != 0)
868 /* Update link status */
869 if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
877 rndis_halt ( rndis );
879 rndis->op->close ( rndis );
885 * Close network device
887 * @v netdev Network device
889 static void rndis_close ( struct net_device *netdev ) {
890 struct rndis_device *rndis = netdev->priv;
892 /* Clear receive filter */
893 rndis_filter ( rndis, 0 );
895 /* Halt RNDIS device */
896 rndis_halt ( rndis );
898 /* Close RNDIS device */
899 rndis->op->close ( rndis );
905 * @v netdev Network device
906 * @v iobuf I/O buffer
907 * @ret rc Return status code
909 static int rndis_transmit ( struct net_device *netdev,
910 struct io_buffer *iobuf ) {
911 struct rndis_device *rndis = netdev->priv;
913 /* Transmit data packet */
914 return rndis_tx_data ( rndis, iobuf );
918 * Poll for completed and received packets
920 * @v netdev Network device
922 static void rndis_poll ( struct net_device *netdev ) {
923 struct rndis_device *rndis = netdev->priv;
925 /* Poll RNDIS device */
926 rndis->op->poll ( rndis );
929 /** Network device operations */
930 static struct net_device_operations rndis_operations = {
932 .close = rndis_close,
933 .transmit = rndis_transmit,
938 * Allocate RNDIS device
940 * @v priv_len Length of private data
941 * @ret rndis RNDIS device, or NULL on allocation failure
943 struct rndis_device * alloc_rndis ( size_t priv_len ) {
944 struct net_device *netdev;
945 struct rndis_device *rndis;
947 /* Allocate and initialise structure */
948 netdev = alloc_etherdev ( sizeof ( *rndis ) + priv_len );
951 netdev_init ( netdev, &rndis_operations );
952 rndis = netdev->priv;
953 rndis->netdev = netdev;
954 rndis->priv = ( ( ( void * ) rndis ) + sizeof ( *rndis ) );
960 * Register RNDIS device
962 * @v rndis RNDIS device
963 * @ret rc Return status code
965 * Note that this routine will open and use the RNDIS device in order
966 * to query the MAC address. The device must be immediately ready for
967 * use prior to registration.
969 int register_rndis ( struct rndis_device *rndis ) {
970 struct net_device *netdev = rndis->netdev;
973 /* Assign device name (for debugging) */
974 rndis->name = netdev->dev->name;
976 /* Register network device */
977 if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
978 DBGC ( rndis, "RNDIS %s could not register: %s\n",
979 rndis->name, strerror ( rc ) );
983 /* Open RNDIS device to read MAC addresses */
984 if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
985 DBGC ( rndis, "RNDIS %s could not open: %s\n",
986 rndis->name, strerror ( rc ) );
990 /* Initialise RNDIS */
991 if ( ( rc = rndis_initialise ( rndis ) ) != 0 )
994 /* Query permanent MAC address */
995 if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS,
997 goto err_query_permanent;
999 /* Query current MAC address */
1000 if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS,
1002 goto err_query_current;
1004 /* Get link status */
1005 if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
1007 goto err_query_link;
1009 /* Halt RNDIS device */
1010 rndis_halt ( rndis );
1012 /* Close RNDIS device */
1013 rndis->op->close ( rndis );
1019 err_query_permanent:
1020 rndis_halt ( rndis );
1022 rndis->op->close ( rndis );
1024 unregister_netdev ( netdev );
1030 * Unregister RNDIS device
1032 * @v rndis RNDIS device
1034 void unregister_rndis ( struct rndis_device *rndis ) {
1035 struct net_device *netdev = rndis->netdev;
1037 /* Unregister network device */
1038 unregister_netdev ( netdev );
1044 * @v rndis RNDIS device
1046 void free_rndis ( struct rndis_device *rndis ) {
1047 struct net_device *netdev = rndis->netdev;
1049 /* Free network device */
1050 netdev_nullify ( netdev );
1051 netdev_put ( netdev );