/* * Copyright (C) 2014 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * * Hyper-V network virtual service client * * The network virtual service client (NetVSC) connects to the network * virtual service provider (NetVSP) via the Hyper-V virtual machine * bus (VMBus). It provides a transport layer for RNDIS packets. */ #include #include #include #include #include #include #include "netvsc.h" /** * Send control message and wait for completion * * @v netvsc NetVSC device * @v xrid Relative transaction ID * @v data Data * @v len Length of data * @ret rc Return status code */ static int netvsc_control ( struct netvsc_device *netvsc, unsigned int xrid, const void *data, size_t len ) { uint64_t xid = ( NETVSC_BASE_XID + xrid ); unsigned int i; int rc; /* Send control message */ if ( ( rc = vmbus_send_control ( netvsc->vmdev, xid, data, len ) ) !=0){ DBGC ( netvsc, "NETVSC %s could not send control message: %s\n", netvsc->name, strerror ( rc ) ); return rc; } /* Record transaction ID */ netvsc->wait_xrid = xrid; /* Wait for operation to complete */ for ( i = 0 ; i < NETVSC_MAX_WAIT_MS ; i++ ) { /* Check for completion */ if ( ! netvsc->wait_xrid ) return netvsc->wait_rc; /* Poll VMBus device */ vmbus_poll ( netvsc->vmdev ); /* Delay for 1ms */ mdelay ( 1 ); } DBGC ( netvsc, "NETVSC %s timed out waiting for XRID %d\n", netvsc->name, xrid ); vmbus_dump_channel ( netvsc->vmdev ); return -ETIMEDOUT; } /** * Handle generic completion * * @v netvsc NetVSC device * @v data Data * @v len Length of data * @ret rc Return status code */ static int netvsc_completed ( struct netvsc_device *netvsc __unused, const void *data __unused, size_t len __unused ) { return 0; } /** * Initialise communication * * @v netvsc NetVSC device * @ret rc Return status code */ static int netvsc_initialise ( struct netvsc_device *netvsc ) { struct netvsc_init_message msg; int rc; /* Construct message */ memset ( &msg, 0, sizeof ( msg ) ); msg.header.type = cpu_to_le32 ( NETVSC_INIT_MSG ); msg.min = cpu_to_le32 ( NETVSC_VERSION_1 ); msg.max = cpu_to_le32 ( NETVSC_VERSION_1 ); /* Send message and wait for completion */ if ( ( rc = netvsc_control ( netvsc, NETVSC_INIT_XRID, &msg, sizeof ( msg ) ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not initialise: %s\n", netvsc->name, strerror ( rc ) ); return rc; } return 0; } /** * Handle initialisation completion * * @v netvsc NetVSC device * @v data Data * @v len Length of data * @ret rc Return status code */ static int netvsc_initialised ( struct netvsc_device *netvsc, const void *data, size_t len ) { const struct netvsc_init_completion *cmplt = data; /* Check completion */ if ( len < sizeof ( *cmplt ) ) { DBGC ( netvsc, "NETVSC %s underlength initialisation " "completion (%zd bytes)\n", netvsc->name, len ); return -EINVAL; } if ( cmplt->header.type != cpu_to_le32 ( NETVSC_INIT_CMPLT ) ) { DBGC ( netvsc, "NETVSC %s unexpected initialisation completion " "type %d\n", netvsc->name, le32_to_cpu ( cmplt->header.type ) ); return -EPROTO; } if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) { DBGC ( netvsc, "NETVSC %s initialisation failure status %d\n", netvsc->name, le32_to_cpu ( cmplt->status ) ); return -EPROTO; } return 0; } /** * Set NDIS version * * @v netvsc NetVSC device * @ret rc Return status code */ static int netvsc_ndis_version ( struct netvsc_device *netvsc ) { struct netvsc_ndis_version_message msg; int rc; /* Construct message */ memset ( &msg, 0, sizeof ( msg ) ); msg.header.type = cpu_to_le32 ( NETVSC_NDIS_VERSION_MSG ); msg.major = cpu_to_le32 ( NETVSC_NDIS_MAJOR ); msg.minor = cpu_to_le32 ( NETVSC_NDIS_MINOR ); /* Send message and wait for completion */ if ( ( rc = netvsc_control ( netvsc, NETVSC_NDIS_VERSION_XRID, &msg, sizeof ( msg ) ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not set NDIS version: %s\n", netvsc->name, strerror ( rc ) ); return rc; } return 0; } /** * Establish data buffer * * @v netvsc NetVSC device * @v buffer Data buffer * @ret rc Return status code */ static int netvsc_establish_buffer ( struct netvsc_device *netvsc, struct netvsc_buffer *buffer ) { struct netvsc_establish_buffer_message msg; int rc; /* Construct message */ memset ( &msg, 0, sizeof ( msg ) ); msg.header.type = cpu_to_le32 ( buffer->establish_type ); msg.gpadl = cpu_to_le32 ( buffer->gpadl ); msg.pageset = buffer->pages.pageset; /* Already protocol-endian */ /* Send message and wait for completion */ if ( ( rc = netvsc_control ( netvsc, buffer->establish_xrid, &msg, sizeof ( msg ) ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not establish buffer: %s\n", netvsc->name, strerror ( rc ) ); return rc; } return 0; } /** * Handle establish receive data buffer completion * * @v netvsc NetVSC device * @v data Data * @v len Length of data * @ret rc Return status code */ static int netvsc_rx_established_buffer ( struct netvsc_device *netvsc, const void *data, size_t len ) { const struct netvsc_rx_establish_buffer_completion *cmplt = data; /* Check completion */ if ( len < sizeof ( *cmplt ) ) { DBGC ( netvsc, "NETVSC %s underlength buffer completion (%zd " "bytes)\n", netvsc->name, len ); return -EINVAL; } if ( cmplt->header.type != cpu_to_le32 ( NETVSC_RX_ESTABLISH_CMPLT ) ) { DBGC ( netvsc, "NETVSC %s unexpected buffer completion type " "%d\n", netvsc->name, le32_to_cpu ( cmplt->header.type)); return -EPROTO; } if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) { DBGC ( netvsc, "NETVSC %s buffer failure status %d\n", netvsc->name, le32_to_cpu ( cmplt->status ) ); return -EPROTO; } return 0; } /** * Revoke data buffer * * @v netvsc NetVSC device * @v buffer Data buffer * @ret rc Return status code */ static int netvsc_revoke_buffer ( struct netvsc_device *netvsc, struct netvsc_buffer *buffer ) { struct netvsc_revoke_buffer_message msg; int rc; /* If the buffer's GPADL is obsolete (i.e. was created before * the most recent Hyper-V reset), then we will never receive * a response to the revoke message. Since the GPADL is * already destroyed as far as the hypervisor is concerned, no * further action is required. */ if ( netvsc_is_obsolete ( netvsc ) ) return 0; /* Construct message */ memset ( &msg, 0, sizeof ( msg ) ); msg.header.type = cpu_to_le32 ( buffer->revoke_type ); msg.pageset = buffer->pages.pageset; /* Already protocol-endian */ /* Send message and wait for completion */ if ( ( rc = netvsc_control ( netvsc, buffer->revoke_xrid, &msg, sizeof ( msg ) ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not revoke buffer: %s\n", netvsc->name, strerror ( rc ) ); return rc; } return 0; } /** * Handle received control packet * * @v vmdev VMBus device * @v xid Transaction ID * @v data Data * @v len Length of data * @ret rc Return status code */ static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len ) { struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); struct netvsc_device *netvsc = rndis->priv; DBGC ( netvsc, "NETVSC %s received unsupported control packet " "(%08llx):\n", netvsc->name, xid ); DBGC_HDA ( netvsc, 0, data, len ); return -ENOTSUP; } /** * Handle received data packet * * @v vmdev VMBus device * @v xid Transaction ID * @v data Data * @v len Length of data * @v list List of I/O buffers * @ret rc Return status code */ static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len, struct list_head *list ) { struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); struct netvsc_device *netvsc = rndis->priv; const struct netvsc_rndis_message *msg = data; struct io_buffer *iobuf; struct io_buffer *tmp; int rc; /* Sanity check */ if ( len < sizeof ( *msg ) ) { DBGC ( netvsc, "NETVSC %s received underlength RNDIS packet " "(%zd bytes)\n", netvsc->name, len ); rc = -EINVAL; goto err_sanity; } if ( msg->header.type != cpu_to_le32 ( NETVSC_RNDIS_MSG ) ) { DBGC ( netvsc, "NETVSC %s received unexpected RNDIS packet " "type %d\n", netvsc->name, le32_to_cpu ( msg->header.type ) ); rc = -EINVAL; goto err_sanity; } /* Send completion back to host */ if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not send completion: %s\n", netvsc->name, strerror ( rc ) ); goto err_completion; } /* Hand off to RNDIS */ list_for_each_entry_safe ( iobuf, tmp, list, list ) { list_del ( &iobuf->list ); rndis_rx ( rndis, iob_disown ( iobuf ) ); } return 0; err_completion: err_sanity: list_for_each_entry_safe ( iobuf, tmp, list, list ) { list_del ( &iobuf->list ); free_iob ( iobuf ); } return rc; } /** * Handle received completion packet * * @v vmdev VMBus device * @v xid Transaction ID * @v data Data * @v len Length of data * @ret rc Return status code */ static int netvsc_recv_completion ( struct vmbus_device *vmdev, uint64_t xid, const void *data, size_t len ) { struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); struct netvsc_device *netvsc = rndis->priv; struct io_buffer *iobuf; int ( * completion ) ( struct netvsc_device *netvsc, const void *data, size_t len ); unsigned int xrid = ( xid - NETVSC_BASE_XID ); unsigned int tx_id; int rc; /* Handle transmit completion, if applicable */ tx_id = ( xrid - NETVSC_TX_BASE_XRID ); if ( ( tx_id < NETVSC_TX_NUM_DESC ) && ( ( iobuf = netvsc->tx.iobufs[tx_id] ) != NULL ) ) { /* Free buffer ID */ netvsc->tx.iobufs[tx_id] = NULL; netvsc->tx.ids[ ( netvsc->tx.id_cons++ ) & ( netvsc->tx.count - 1 ) ] = tx_id; /* Hand back to RNDIS */ rndis_tx_complete ( rndis, iobuf ); return 0; } /* Otherwise determine completion handler */ if ( xrid == NETVSC_INIT_XRID ) { completion = netvsc_initialised; } else if ( xrid == NETVSC_RX_ESTABLISH_XRID ) { completion = netvsc_rx_established_buffer; } else if ( ( netvsc->wait_xrid != 0 ) && ( xrid == netvsc->wait_xrid ) ) { completion = netvsc_completed; } else { DBGC ( netvsc, "NETVSC %s received unexpected completion " "(%08llx)\n", netvsc->name, xid ); return -EPIPE; } /* Hand off to completion handler */ rc = completion ( netvsc, data, len ); /* Record completion handler result if applicable */ if ( xrid == netvsc->wait_xrid ) { netvsc->wait_xrid = 0; netvsc->wait_rc = rc; } return rc; } /** * Handle received cancellation packet * * @v vmdev VMBus device * @v xid Transaction ID * @ret rc Return status code */ static int netvsc_recv_cancellation ( struct vmbus_device *vmdev, uint64_t xid ) { struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); struct netvsc_device *netvsc = rndis->priv; DBGC ( netvsc, "NETVSC %s received unsupported cancellation packet " "(%08llx):\n", netvsc->name, xid ); return -ENOTSUP; } /** VMBus channel operations */ static struct vmbus_channel_operations netvsc_channel_operations = { .recv_control = netvsc_recv_control, .recv_data = netvsc_recv_data, .recv_completion = netvsc_recv_completion, .recv_cancellation = netvsc_recv_cancellation, }; /** * Poll for completed and received packets * * @v rndis RNDIS device */ static void netvsc_poll ( struct rndis_device *rndis ) { struct netvsc_device *netvsc = rndis->priv; struct vmbus_device *vmdev = netvsc->vmdev; /* Poll VMBus device */ while ( vmbus_has_data ( vmdev ) ) vmbus_poll ( vmdev ); } /** * Transmit packet * * @v rndis RNDIS device * @v iobuf I/O buffer * @ret rc Return status code * * If this method returns success then the RNDIS device must * eventually report completion via rndis_tx_complete(). */ static int netvsc_transmit ( struct rndis_device *rndis, struct io_buffer *iobuf ) { struct netvsc_device *netvsc = rndis->priv; struct rndis_header *header = iobuf->data; struct netvsc_rndis_message msg; unsigned int tx_id; unsigned int xrid; uint64_t xid; int rc; /* If the device is obsolete (i.e. was opened before the most * recent Hyper-V reset), then we will never receive transmit * completions. Fail transmissions immediately to minimise * the delay in closing and reopening the device. */ if ( netvsc_is_obsolete ( netvsc ) ) return -EPIPE; /* Sanity check */ assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); /* Check that we have space in the transmit ring */ if ( netvsc_ring_is_full ( &netvsc->tx ) ) return rndis_tx_defer ( rndis, iobuf ); /* Allocate buffer ID and calculate transaction ID */ tx_id = netvsc->tx.ids[ netvsc->tx.id_prod & ( netvsc->tx.count - 1 ) ]; assert ( netvsc->tx.iobufs[tx_id] == NULL ); xrid = ( NETVSC_TX_BASE_XRID + tx_id ); xid = ( NETVSC_BASE_XID + xrid ); /* Construct message */ memset ( &msg, 0, sizeof ( msg ) ); msg.header.type = cpu_to_le32 ( NETVSC_RNDIS_MSG ); msg.channel = ( ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) ? NETVSC_RNDIS_DATA : NETVSC_RNDIS_CONTROL ); msg.buffer = cpu_to_le32 ( NETVSC_RNDIS_NO_BUFFER ); /* Send message */ if ( ( rc = vmbus_send_data ( netvsc->vmdev, xid, &msg, sizeof ( msg ), iobuf ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not send RNDIS message: %s\n", netvsc->name, strerror ( rc ) ); return rc; } /* Store I/O buffer and consume buffer ID */ netvsc->tx.iobufs[tx_id] = iobuf; netvsc->tx.id_prod++; return 0; } /** * Cancel transmission * * @v netvsc NetVSC device * @v iobuf I/O buffer * @v tx_id Transmission ID */ static void netvsc_cancel_transmit ( struct netvsc_device *netvsc, struct io_buffer *iobuf, unsigned int tx_id ) { unsigned int xrid; uint64_t xid; /* Send cancellation */ xrid = ( NETVSC_TX_BASE_XRID + tx_id ); xid = ( NETVSC_BASE_XID + xrid ); DBGC ( netvsc, "NETVSC %s cancelling transmission %#x\n", netvsc->name, tx_id ); vmbus_send_cancellation ( netvsc->vmdev, xid ); /* Report back to RNDIS */ rndis_tx_complete_err ( netvsc->rndis, iobuf, -ECANCELED ); } /** * Create descriptor ring * * @v netvsc NetVSC device * @v ring Descriptor ring * @ret rc Return status code */ static int netvsc_create_ring ( struct netvsc_device *netvsc __unused, struct netvsc_ring *ring ) { unsigned int i; /* Initialise buffer ID ring */ for ( i = 0 ; i < ring->count ; i++ ) { ring->ids[i] = i; assert ( ring->iobufs[i] == NULL ); } ring->id_prod = 0; ring->id_cons = 0; return 0; } /** * Destroy descriptor ring * * @v netvsc NetVSC device * @v ring Descriptor ring * @v discard Method used to discard outstanding buffer, or NULL */ static void netvsc_destroy_ring ( struct netvsc_device *netvsc, struct netvsc_ring *ring, void ( * discard ) ( struct netvsc_device *, struct io_buffer *, unsigned int ) ) { struct io_buffer *iobuf; unsigned int i; /* Flush any outstanding buffers */ for ( i = 0 ; i < ring->count ; i++ ) { iobuf = ring->iobufs[i]; if ( ! iobuf ) continue; ring->iobufs[i] = NULL; ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = i; if ( discard ) discard ( netvsc, iobuf, i ); } /* Sanity check */ assert ( netvsc_ring_is_empty ( ring ) ); } /** * Copy data from data buffer * * @v pages Transfer page set * @v data Data buffer * @v offset Offset within page set * @v len Length within page set * @ret rc Return status code */ static int netvsc_buffer_copy ( struct vmbus_xfer_pages *pages, void *data, size_t offset, size_t len ) { struct netvsc_buffer *buffer = container_of ( pages, struct netvsc_buffer, pages ); /* Sanity check */ if ( ( offset > buffer->len ) || ( len > ( buffer->len - offset ) ) ) return -ERANGE; /* Copy data from buffer */ copy_from_user ( data, buffer->data, offset, len ); return 0; } /** Transfer page set operations */ static struct vmbus_xfer_pages_operations netvsc_xfer_pages_operations = { .copy = netvsc_buffer_copy, }; /** * Create data buffer * * @v netvsc NetVSC device * @v buffer Data buffer * @ret rc Return status code */ static int netvsc_create_buffer ( struct netvsc_device *netvsc, struct netvsc_buffer *buffer ) { struct vmbus_device *vmdev = netvsc->vmdev; int gpadl; int rc; /* Allocate receive buffer */ buffer->data = umalloc ( buffer->len ); if ( ! buffer->data ) { DBGC ( netvsc, "NETVSC %s could not allocate %zd-byte buffer\n", netvsc->name, buffer->len ); rc = -ENOMEM; goto err_alloc; } /* Establish GPA descriptor list */ gpadl = vmbus_establish_gpadl ( vmdev, buffer->data, buffer->len ); if ( gpadl < 0 ) { rc = gpadl; DBGC ( netvsc, "NETVSC %s could not establish GPADL: %s\n", netvsc->name, strerror ( rc ) ); goto err_establish_gpadl; } buffer->gpadl = gpadl; /* Register transfer page set */ if ( ( rc = vmbus_register_pages ( vmdev, &buffer->pages ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not register transfer pages: " "%s\n", netvsc->name, strerror ( rc ) ); goto err_register_pages; } return 0; vmbus_unregister_pages ( vmdev, &buffer->pages ); err_register_pages: vmbus_gpadl_teardown ( vmdev, gpadl ); err_establish_gpadl: ufree ( buffer->data ); err_alloc: return rc; } /** * Destroy data buffer * * @v netvsc NetVSC device * @v buffer Data buffer */ static void netvsc_destroy_buffer ( struct netvsc_device *netvsc, struct netvsc_buffer *buffer ) { struct vmbus_device *vmdev = netvsc->vmdev; int rc; /* Unregister transfer pages */ vmbus_unregister_pages ( vmdev, &buffer->pages ); /* Tear down GPA descriptor list */ if ( ( rc = vmbus_gpadl_teardown ( vmdev, buffer->gpadl ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not tear down GPADL: %s\n", netvsc->name, strerror ( rc ) ); /* Death is imminent. The host may well continue to * write to the data buffer. The best we can do is * leak memory for now and hope that the host doesn't * write to this region after we load an OS. */ return; } /* Free buffer */ ufree ( buffer->data ); } /** * Open device * * @v rndis RNDIS device * @ret rc Return status code */ static int netvsc_open ( struct rndis_device *rndis ) { struct netvsc_device *netvsc = rndis->priv; int rc; /* Initialise receive buffer */ if ( ( rc = netvsc_create_buffer ( netvsc, &netvsc->rx ) ) != 0 ) goto err_create_rx; /* Open channel */ if ( ( rc = vmbus_open ( netvsc->vmdev, &netvsc_channel_operations, PAGE_SIZE, PAGE_SIZE, NETVSC_MTU ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not open VMBus: %s\n", netvsc->name, strerror ( rc ) ); goto err_vmbus_open; } /* Initialise communication with NetVSP */ if ( ( rc = netvsc_initialise ( netvsc ) ) != 0 ) goto err_initialise; if ( ( rc = netvsc_ndis_version ( netvsc ) ) != 0 ) goto err_ndis_version; /* Initialise transmit ring */ if ( ( rc = netvsc_create_ring ( netvsc, &netvsc->tx ) ) != 0 ) goto err_create_tx; /* Establish receive buffer */ if ( ( rc = netvsc_establish_buffer ( netvsc, &netvsc->rx ) ) != 0 ) goto err_establish_rx; return 0; netvsc_revoke_buffer ( netvsc, &netvsc->rx ); err_establish_rx: netvsc_destroy_ring ( netvsc, &netvsc->tx, NULL ); err_create_tx: err_ndis_version: err_initialise: vmbus_close ( netvsc->vmdev ); err_vmbus_open: netvsc_destroy_buffer ( netvsc, &netvsc->rx ); err_create_rx: return rc; } /** * Close device * * @v rndis RNDIS device */ static void netvsc_close ( struct rndis_device *rndis ) { struct netvsc_device *netvsc = rndis->priv; /* Revoke receive buffer */ netvsc_revoke_buffer ( netvsc, &netvsc->rx ); /* Destroy transmit ring */ netvsc_destroy_ring ( netvsc, &netvsc->tx, netvsc_cancel_transmit ); /* Close channel */ vmbus_close ( netvsc->vmdev ); /* Destroy receive buffer */ netvsc_destroy_buffer ( netvsc, &netvsc->rx ); } /** RNDIS operations */ static struct rndis_operations netvsc_operations = { .open = netvsc_open, .close = netvsc_close, .transmit = netvsc_transmit, .poll = netvsc_poll, }; /** * Probe device * * @v vmdev VMBus device * @ret rc Return status code */ static int netvsc_probe ( struct vmbus_device *vmdev ) { struct netvsc_device *netvsc; struct rndis_device *rndis; int rc; /* Allocate and initialise structure */ rndis = alloc_rndis ( sizeof ( *netvsc ) ); if ( ! rndis ) { rc = -ENOMEM; goto err_alloc; } rndis_init ( rndis, &netvsc_operations ); rndis->netdev->dev = &vmdev->dev; netvsc = rndis->priv; netvsc->vmdev = vmdev; netvsc->rndis = rndis; netvsc->name = vmdev->dev.name; netvsc_init_ring ( &netvsc->tx, NETVSC_TX_NUM_DESC, netvsc->tx_iobufs, netvsc->tx_ids ); netvsc_init_buffer ( &netvsc->rx, NETVSC_RX_BUF_PAGESET, &netvsc_xfer_pages_operations, NETVSC_RX_ESTABLISH_MSG, NETVSC_RX_ESTABLISH_XRID, NETVSC_RX_REVOKE_MSG, NETVSC_RX_REVOKE_XRID, NETVSC_RX_BUF_LEN ); vmbus_set_drvdata ( vmdev, rndis ); /* Register RNDIS device */ if ( ( rc = register_rndis ( rndis ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not register: %s\n", netvsc->name, strerror ( rc ) ); goto err_register; } return 0; unregister_rndis ( rndis ); err_register: free_rndis ( rndis ); err_alloc: return rc; } /** * Reset device * * @v vmdev VMBus device * @ret rc Return status code */ static int netvsc_reset ( struct vmbus_device *vmdev ) { struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); struct netvsc_device *netvsc = rndis->priv; struct net_device *netdev = rndis->netdev; int rc; /* A closed device holds no NetVSC (or RNDIS) state, so there * is nothing to reset. */ if ( ! netdev_is_open ( netdev ) ) return 0; /* Close and reopen device to reset any stale state */ netdev_close ( netdev ); if ( ( rc = netdev_open ( netdev ) ) != 0 ) { DBGC ( netvsc, "NETVSC %s could not reopen: %s\n", netvsc->name, strerror ( rc ) ); return rc; } return 0; } /** * Remove device * * @v vmdev VMBus device */ static void netvsc_remove ( struct vmbus_device *vmdev ) { struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); /* Unregister RNDIS device */ unregister_rndis ( rndis ); /* Free RNDIS device */ free_rndis ( rndis ); } /** NetVSC driver */ struct vmbus_driver netvsc_driver __vmbus_driver = { .name = "netvsc", .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ), .probe = netvsc_probe, .reset = netvsc_reset, .remove = netvsc_remove, };